为什么80%的码农都做不了架构师?>>>
TP-LINK 841n路由更新openwrt系统到Barrier Breaker r41988,“网络”-->“无线”-->“搜索”这个功能就不能用了,trunk版的也有这个问题。其实今年4、5月份玩HG255D路由就有这个问题,但当时的源码编译给TP-LINK 841n却没有这个问题。对这样的问题,确实比较头大,花了两天时间终于找到bug所在(只解决wr841n的,我的hg255d出问题了)。
无线搜索bug截图(注意device参数radio0):
一番搜索后发现此页面的源码在系统的/usr/lib/lua/luci/view/admin_network/wifi_join.htm文件里,对应openwrt的源码路径为./feeds/luci/modules/admin-full/luasrc/view/admin_network/wifi_join.htm。终于要代码如下:
<%-
local sys = require "luci.sys"
local utl = require "luci.util"
function guess_wifi_signal(info)
local scale = (100 / (info.quality_max or 100) * (info.quality or 0))
local icon
if not info.bssid or info.bssid == "00:00:00:00:00:00" then
icon = resource .. "/icons/signal-none.png"
elseif scale < 15 then
icon = resource .. "/icons/signal-0.png"
elseif scale < 35 then
icon = resource .. "/icons/signal-0-25.png"
elseif scale < 55 then
icon = resource .. "/icons/signal-25-50.png"
elseif scale < 75 then
icon = resource .. "/icons/signal-50-75.png"
else
icon = resource .. "/icons/signal-75-100.png"
end
return icon
end
function percent_wifi_signal(info)
local qc = info.quality or 0
local qm = info.quality_max or 0
if info.bssid and qc > 0 and qm > 0 then
return math.floor((100 / qm) * qc)
else
return 0
end
end
function format_wifi_encryption(info)
if info.wep == true then
return "WEP"
elseif info.wpa > 0 then
return translatef("%s - %s",
table.concat(info.pair_ciphers, ", "),
table.concat(info.group_ciphers, ", "),
(info.wpa == 3) and translate("mixed WPA/WPA2")
or (info.wpa == 2 and "WPA2" or "WPA"),
table.concat(info.auth_suites, ", ")
)
elseif info.enabled then
return "%s" % translate("unknown")
else
return "%s" % translate("open")
end
end
local dev = luci.http.formvalue("device")
local iw = luci.sys.wifi.getiwinfo(dev)
if not iw then
luci.http.redirect(luci.dispatcher.build_url("admin/network/wireless"))
return
end
function scanlist(times)
local i, k, v
local l = { }
local s = { }
for i = 1, times do
for k, v in ipairs(iw.scanlist or { }) do
if not s[v.bssid] then
l[#l+1] = v
s[v.bssid] = true
end
end
end
return l
end
-%>
<%+header%>
<%:Join Network: Wireless Scan%>
进一步的测试发现,在调用function scanlist(times)异常导致生成的网页不完整,浏览器就会看到那段错误信息。而此函数又是在调用iw.scanlist发生异常。由local iw = luci.sys.wifi.getiwinfo(dev)找到sys.lua模块,路径/usr/lib/lua/luci/sys.lua,对应openwrt源码位置./feeds/luci/modules/base/luasrc/sys.lua。主要源码:
--- Get wireless information for given interface.
-- @param ifname String containing the interface name
-- @return A wrapped iwinfo object instance
function wifi.getiwinfo(ifname)
local stat, iwinfo = pcall(require, "iwinfo")
if ifname then
local c = 0
local u = uci.cursor_state()
local d, n = ifname:match("^(%w+)%.network(%d+)")
if d and n then
ifname = d
n = tonumber(n)
u:foreach("wireless", "wifi-iface",
function(s)
if s.device == d then
c = c + 1
if c == n then
ifname = s.ifname or s.device
return false
end
end
end)
elseif u:get("wireless", ifname) == "wifi-device" then
u:foreach("wireless", "wifi-iface",
function(s)
if s.device == ifname and s.ifname then
ifname = s.ifname
return false
end
end)
end
local t = stat and iwinfo.type(ifname)
local x = t and iwinfo[t] or { }
return setmetatable({}, {
__index = function(t, k)
if k == "ifname" then
return ifname
elseif x[k] then
return x[k](ifname)
end
end
})
end
end
原来是调用iwinfo命令完成的无线搜索。在openwrt下运行iwinfo命令:
root@OpenWrt:~# iwinfo --help
Usage:
iwinfo info
iwinfo scan
iwinfo txpowerlist
iwinfo freqlist
iwinfo assoclist
iwinfo countrylist
root@OpenWrt:~# iwinfo radio0 scan
Cell 01 - Address: 00:00:00:00:00:00
ESSID: unknown
Mode: Unknown Channel: unknown
Signal: -256 dBm Quality: 0/0
Encryption: none
Cell 02 - Address: 00:00:00:00:00:00
ESSID: unknown
Mode: Unknown Channel: unknown
Signal: -256 dBm Quality: 0/0
Encryption: none
Cell 03 - Address: 00:00:00:00:00:00
ESSID: unknown
Mode: Unknown Channel: unknown
Signal: -256 dBm Quality: 0/0
Encryption: none
Cell 04 - Address: 00:00:00:00:00:00
ESSID: unknown
Mode: Unknown Channel: unknown
Signal: -256 dBm Quality: 0/0
Encryption: none
Cell 05 - Address: 00:00:00:00:00:00
ESSID: unknown
Mode: Unknown Channel: unknown
Signal: -256 dBm Quality: 0/0
Encryption: none
Cell 06 - Address: 00:00:00:00:00:00
ESSID: unknown
Mode: Unknown Channel: unknown
Signal: -256 dBm Quality: 0/0
Encryption: none
Cell 07 - Address: 00:00:00:00:00:00
ESSID: unknown
Mode: Unknown Channel: unknown
Signal: -256 dBm Quality: 0/0
Encryption: none
Cell 08 - Address: 00:00:00:00:00:00
ESSID: unknown
Mode: Unknown Channel: unknown
Signal: -256 dBm Quality: 0/0
Encryption: none
Cell 09 - Address: 00:00:00:00:00:00
ESSID: unknown
Mode: Unknown Channel: unknown
Signal: -256 dBm Quality: 0/0
Encryption: none
Cell 10 - Address: 00:00:00:00:00:00
ESSID: unknown
Mode: Unknown Channel: unknown
Signal: -256 dBm Quality: 0/0
Encryption: none
Cell 11 - Address: 00:00:00:00:00:00
ESSID: unknown
Mode: Unknown Channel: unknown
Signal: -256 dBm Quality: 0/0
Encryption: none
Cell 12 - Address: 00:00:00:00:00:00
ESSID: unknown
Mode: Unknown Channel: unknown
Signal: -256 dBm Quality: 0/0
Encryption: none
Cell 13 - Address: 00:00:00:00:00:00
ESSID: unknown
Mode: Unknown Channel: unknown
Signal: -256 dBm Quality: 0/0
Encryption: none
Cell 14 - Address: 00:00:00:00:00:00
ESSID: unknown
Mode: Unknown Channel: unknown
Signal: -256 dBm Quality: 0/0
Encryption: none
Cell 15 - Address: 00:00:00:00:00:00
ESSID: unknown
Mode: Unknown Channel: unknown
Signal: -256 dBm Quality: 0/0
Encryption: none
Cell 16 - Address: 00:00:00:00:00:00
ESSID: unknown
Mode: Unknown Channel: unknown
Signal: -256 dBm Quality: 0/0
Encryption: none
Cell 17 - Address: 00:00:00:00:00:00
ESSID: unknown
Mode: Unknown Channel: unknown
Signal: -256 dBm Quality: 0/0
Encryption: none
Cell 18 - Address: 00:00:00:00:00:00
ESSID: unknown
Mode: Unknown Channel: unknown
Signal: -256 dBm Quality: 0/0
Encryption: none
Cell 19 - Address: 00:00:00:00:00:00
ESSID: unknown
Mode: Unknown Channel: unknown
Signal: -256 dBm Quality: 0/0
Encryption: none
Cell 20 - Address: 00:00:00:00:00:00
ESSID: unknown
Mode: Unknown Channel: unknown
Signal: -256 dBm Quality: 0/0
Encryption: none
看看Address、ESSID。。 说明iwinfo根本就无法扫描到无线接入点。而在4、5月份编译的版本运行此命令没问题,可以搜索到接入点。iwinfo涉及到两个文件,在openwrt系统下一个是可执行文件/usr/bin/iwinfo,另一个是动态链接库文件/usr/lib/libiwinfo.so。于是拷贝之前系统(可以用搜索功能)的iwinfo、libiwinfo.so文件到有问题的路由的/tmp目录下,shell切换到/tmp目录,执行export LD_LIBRARY_PATH=.
然后再执行./iwinfo radio0 scan,发现搜索无线接入点正常,看来出来就出在iwinfo、libiwinfo.so上。直接删除/tmp目录下的libiwinfo.so后再./iwinfo radio0 scan,问题又出现了。由此推断出问题应该在libiwinfo.so里。iwinfo工具源码在openwrt源码的./package/network/utils/iwinfo下,注意Makefile文件有如下代码:
IWINFO_BACKENDS := \
$(if $(CONFIG_PACKAGE_kmod-brcm-wl),wl) \
$(if $(CONFIG_PACKAGE_kmod-brcm-wl-mini),wl) \
$(if $(CONFIG_PACKAGE_kmod-brcm-wl-mimo),wl) \
$(if $(CONFIG_PACKAGE_kmod-madwifi),madwifi) \
$(if $(CONFIG_PACKAGE_kmod-mac80211),nl80211)
在src目录下的Makefile文件有如下代码:
ifneq ($(filter wl,$(IWINFO_BACKENDS)),)
IWINFO_CFLAGS += -DUSE_WL
IWINFO_LIB_OBJ += iwinfo_wl.o
endif
ifneq ($(filter madwifi,$(IWINFO_BACKENDS)),)
IWINFO_CFLAGS += -DUSE_MADWIFI
IWINFO_LIB_OBJ += iwinfo_madwifi.o
endif
ifneq ($(filter nl80211,$(IWINFO_BACKENDS)),)
IWINFO_CFLAGS += -DUSE_NL80211
IWINFO_CLI_LDFLAGS += -lnl-tiny
IWINFO_LIB_LDFLAGS += -lnl-tiny
IWINFO_LIB_OBJ += iwinfo_nl80211.o
endif
无线搜索有3中实现,刚开始我也不知道841n下是用哪种,索性在makefile文件里写些调试输出语句。然后make package/network/utils/iwinfo/compile V=s重新编译iwinfo,发现用的是nl80211,也就是说要改的代码在./src/iwinfo_nl80211.c。经过一系列的调试最终发现bug所在。解决方案如下:
// iwinfo_cli.c文件
static void print_scanlist(const struct iwinfo_ops *iw, const char *ifname)
{
int i, x, len=0; //这里len必须初始化为0
char buf[IWINFO_BUFSIZE];
struct iwinfo_scanlist_entry *e;
if (iw->scanlist(ifname, buf, &len))
{
printf("Scanning not possible\n\n");
return;
}
else if (len <= 0)
{
printf("No scan results\n\n");
return;
}
// iwinfo_nl80211.c文件nl80211_phy2ifname函数修改如下
static char * nl80211_phy2ifname(const char *ifname)
{
int fd, ifidx = -1, cifidx = -1, phyidx = -1;
char buffer[64];
static char nif[IFNAMSIZ] = { 0 };//注意静态变量
DIR *d;
struct dirent *e;
char *ret = NULL; //添加
if (!ifname)
return NULL;
else if (!strncmp(ifname, "phy", 3))
phyidx = atoi(&ifname[3]);
else if (!strncmp(ifname, "radio", 5))
phyidx = atoi(&ifname[5]);
//当传进来的ifname=wlan0时,phyidx=-1,后面的memset可能会把ifname清空。
//memset(nif, 0, sizeof(nif)); //注释掉,因为ifname可能指向nif
if (phyidx > -1)
{
if ((d = opendir("/sys/class/net")) != NULL)
{
while ((e = readdir(d)) != NULL)
{
snprintf(buffer, sizeof(buffer),
"/sys/class/net/%s/phy80211/index", e->d_name);
if (nl80211_readint(buffer) == phyidx)
{
snprintf(buffer, sizeof(buffer),
"/sys/class/net/%s/ifindex", e->d_name);
if ((cifidx = nl80211_readint(buffer)) >= 0 &&
((ifidx < 0) || (cifidx < ifidx)))
{
ifidx = cifidx;
strncpy(nif, e->d_name, sizeof(nif));
ret = nif ; //添加
break ; //添加
}
}
}
closedir(d);
}
}
return ret ; //修改
}
make package/network/utils/iwinfo/compile V=s 重新编译iwinfo,传到路由上测试,iwinfo radio0 scan已经没问题,可以正常搜索无线了。其实我不喜欢nl80211_phy2ifname函数用静态变量方式返回,而更倾向于用函数参数的方式,例如声明成static char * nl80211_phy2ifname(const char *ifname, char *out);
已经向官方提交bug链接。