小米路由器端口转发 Internal Server Error

今天想给家里路由器 (XiaoMi R3D) 做个端口映射给同事演示做的东西,但是发现通过网页设置路由器端口映射直接返回 500 错误,查了下日志也看到什么错误,然后查看了一下相关代码,并通过 lua 交互式客户端调试了一下

> do
local pf = require("xiaoqiang.module.XQPortForward")
pf.setPortForward('test', '192.168.231.204', 6800, 8000, "tcp")
end>> >> >> >> >> >>
/usr/lib/lua/xiaoqiang/module/XQPortForward.lua:93: attempt to compare nil with number
stack traceback:
[C]: in function 'foreach'
/usr/lib/lua/xiaoqiang/module/XQPortForward.lua:104: in function '_portConflictCheck'
/usr/lib/lua/xiaoqiang/module/XQPortForward.lua:181: in function 'setPortForward'
stdin:6: in main chunk
[C]: ?

发现错误发生在校验指定的目标端口是否已经使用的代码中,代码如下

function _portRangeOverlap(port1, port2)
local LuciUtil = require("luci.util")
if port1 and port2 then
port1 = tostring(port1)
port2 = tostring(port2)
if port1 == port2 then
return false
end
local range1 = {}
local range2 = {}
if port1:match("-") then
local sp = LuciUtil.split(port1, "-")
range1["f"] = tonumber(sp[1])
range1["t"] = tonumber(sp[2])
else
range1["f"] = tonumber(port1)
range1["t"] = tonumber(port1)
end
if port2:match("-") then
local sp = LuciUtil.split(port2, "-")
range2["f"] = tonumber(sp[1])
range2["t"] = tonumber(sp[2])
else
range2["f"] = tonumber(port2)
range2["t"] = tonumber(port2)
end
if (range1.f >= range2.f and range1.f <= range2.t) or
(range1.t >= range2.f and range1.t <= range2.t) then
return true
end
end
return false
end

错误出现在这一行代码中,错误的原因是 range1.f 的值为 nilrange2.f 的数据类型为 number 不能进行比较

if (range1.f >= range2.f and range1.f <= range2.t)

为什么 range1.f 会是 nil 呢,笔者看了一下前面的代码

uci:foreach("firewall", "redirect",
function(s)
if _portRangeOverlap(port, s.src_dport) then
result = true
end
end
)

是对 uci (lua 统一配置接口) 中的 firewall.redirect 这部分内容进行迭代,与指定的目标端口进行对比,那么原因就出现在笔者环境中的 firewall.redirect 里面了,通过命令查看配置内容

$ uci show firewall.@redirect[1]
firewall.cfg333837=redirect
firewall.cfg333837.name=wan samba
firewall.cfg333837.src=wan
firewall.cfg333837.src_dport=137 139 445
firewall.cfg333837.dest=lan
firewall.cfg333837.proto=tcp udp

可以看到这个配置里面的 dport 不是标准的格式,那么问题就是出现在这里了,通过以下命令删除对应的配置就搞定了

$ uci delete firewall.@redirect[1]

为什么会出现这个问题,笔者认为应该是之前配置 smb 外网访问导致的