NanoPC-T2(Android 5.1)手动开启 设置WiFi,熟悉wpa_supplicant工作原理

NanoPC-T2(Android 5.1) 手动开启 设置WiFi,熟悉wpa_supplicant工作原理

系统版本: Android 5.1
内核版本:3.4

NanoPC-T2上的WiFi芯片是AP6212,在BoardConfig.mk中的配置如下:

# wifi
BOARD_WIFI_VENDOR := ap6212

# Wi-Fi related defines
ifeq ($(BOARD_WIFI_VENDOR),ap6212)
WPA_SUPPLICANT_VERSION      := VER_0_8_X
BOARD_WLAN_DEVICE           := bcmdhd
BOARD_WPA_SUPPLICANT_DRIVER := NL80211
BOARD_WPA_SUPPLICANT_PRIVATE_LIB := lib_driver_cmd_$(BOARD_WLAN_DEVICE)
BOARD_HOSTAPD_DRIVER        := NL80211
BOARD_HOSTAPD_PRIVATE_LIB   := lib_driver_cmd_$(BOARD_WLAN_DEVICE)
WIFI_DRIVER_MODULE_NAME     := "bcmdhd"
WIFI_DRIVER_MODULE_PATH     := "/system/lib/modules/bcmdhd.ko"
WIFI_DRIVER_FW_PATH_PARAM   := "/sys/module/bcmdhd/parameters/firmware_path"
WIFI_DRIVER_FW_PATH_STA     := "/vendor/firmware/fw_bcm43438a0.bin"
WIFI_DRIVER_FW_PATH_AP      := "/vendor/firmware/fw_bcm43438a0_apsta.bin"
endif

开启WiFi

wpa_supplicant启动参数分析

wpa_supplicant的启动参数较多,为了之后能手动启动之,需要把其所有的启动参数了解一下,比如正面就是一个完整的启动命令

service wpa_supplicant /system/bin/wpa_supplicant \
    -iwlan0 -Dnl80211 -c/data/misc/wifi/wpa_supplicant.conf \
    -I/system/etc/wifi/wpa_supplicant_overlay.conf \
    -O/data/misc/wifi/sockets \
    -e/data/misc/wifi/entropy.bin -g@android:wpa_wlan0
    #   we will start as root and wpa_supplicant will switch to user wifi
    #   after setting up the capabilities required for WEXT
    #   user wifi
    #   group wifi inet keystore
    class main
    socket wpa_wlan0 dgram 660 wifi wifi
    disabled
    oneshot

着重说几点-g@android:wpa_wlan0,它是位于/dev/socket/wpa_wlan0,用于控制通信。

手动启动wpa_supplicant

启动wlan0

insmod /system/lib/modules/bcmdhd.ko
busybox ifconfig wlan0 up

注:所有含有/data/system/wpa_supplicant的文章都是比较老的了,wpa_cli指定的-p参数也无效了,新的只是/dev/socket/wpa_wlan0

这样写更好理解一点,wpa_common中的代码写的容错机制比较好,不加-g参数和加了其实在Android中是一样的执行效果

wpa_cli -iwlan0 -g@android:wpa_wlan0

对上面一句话,如果你要证据的话,那还是贴一点代码出来:

chmod(ctrl->local.sun_path, S_IRUSR | S_IWUSR | S_IRGRP | S_IWGRP);
chown(ctrl->local.sun_path, AID_SYSTEM, AID_WIFI);
// 如果使用`-g`,直接打开对应的后面的参数`wpa_wlan0`
if (os_strncmp(ctrl_path, "@android:", 9) == 0) {
    if (socket_local_client_connect(
            ctrl->s, ctrl_path + 9,
            ANDROID_SOCKET_NAMESPACE_RESERVED,
            SOCK_DGRAM) < 0) {
        close(ctrl->s);
        unlink(ctrl->local.sun_path);
        os_free(ctrl);
        return NULL;
    }
    return ctrl;
}

/*
 * If the ctrl_path isn't an absolute pathname, assume that
 * it's the name of a socket in the Android reserved namespace.
 * Otherwise, it's a normal UNIX domain socket appearing in the
 * filesystem.
 */
// 如果不使用`-g`也会在这里强制转换成`wpa_wlan0`
if (*ctrl_path != '/') {
    char buf[21];
    os_snprintf(buf, sizeof(buf), "wpa_%s", ctrl_path);
    if (socket_local_client_connect(
            ctrl->s, buf,
            ANDROID_SOCKET_NAMESPACE_RESERVED,
            SOCK_DGRAM) < 0) {
        close(ctrl->s);
        unlink(ctrl->local.sun_path);
        os_free(ctrl);
        return NULL;
    }
    return ctrl;
}

所以,如果想要手动开启wpa_supplicantwpa_cli,那么首先要解决的就是/dev/socket/wpa_wlan0如何出现。

如何启动/dev/socket/wpa_wlan0

init有一定是有一个机制,在启动wpa_supplicant也自动创建了/dev/socket/wpa_wlan0,如果我直接手动启动wpa_supplicant似乎并不能自动创建/dev/socket/wpa_wlan0,这样就会导致后续的wpa_cli不能正常使用,找了半天也没有找到如何手动创建/dev/socket/wpa_wlan0,与其这样,不如借力打力。就使用start wpa_supplicant的方式来启动wpa_supplicant,通过改变init.xxx.rc中的参数值,来调试。

半手动启动wpa_supplicant步骤:
设置中先关闭WiFi,然后按照如下步骤执行

insmod /system/lib/modules/bcmdhd.ko
busybox ifconfig wlan0 up
start wpa_supplicant

这里就可以使用wpa_cli控制了:

wpa_cli -iwlan0 -g@android:wpa_wlan0
# 搜索SSID,如果不加IFNAME,则能使用的命令比如少
IFNAME=wlan0 SCAN
# 显示搜索热点结果
IFNAME=wlan0 SCAN_RESULTS

开启热点

热点是由hostapd来实现的,但是在init.xxx.rc中并没有找到配置,所以不能使用start hostapd来启动。

insmod /system/lib/modules/bcmdhd.ko
busybox ifconfig wlan0 192.168.43.1
busybox ifconfig wlan0 up
/system/bin/hostapd -e /data/misc/wifi/entropy.bin /data/misc/wifi/hostapd.conf

搜索不到设备,内核打印出一点错误,找找具体原因是什么

shell@nanopi2:/ # /system/bin/hostapd -dd -e /data/misc/wifi/entropy.bin /data>
[  666.036000] CFG80211-ERROR) check_dev_role_integrity : device role select failed
^C[  785.756000] CFG80211-ERROR) check_dev_role_integrity : device role select failed
shell@nanopi2:/ # 

搜索热点

根据A33上AP6210 ap和sta无法自由切换的问题的文章,发现是加载内核模块时忘记指定参数了,新的加载方式:

insmod /system/lib/modules/bcmdhd.ko op_mode=2
busybox ifconfig wlan0 192.168.43.1
busybox ifconfig wlan0 up
/system/bin/hostapd -e /data/misc/wifi/entropy.bin /data/misc/wifi/hostapd.conf

这时,可以搜索到热点了。

连接热点

上一步并不能达到连接热点,因为dhcp服务没有启动,则会导致获取不到IP地址。需要把dnsmasq启动,运行下面的命令就可以启动dnsmasq服务了。

/system/bin/dnsmasq --keep-in-foreground --no-resolv --no-poll --dhcp-authoritative --dhcp-option-force=43,ANDROID_METERED --pid-file --dhcp-range=192.168.42.2,192.168.42.254,1h --dhcp-range=192.168.43.2,192.168.43.254,1h --dhcp-range=192.168.44.2,192.168.44.254,1h --dhcp-range=192.168.45.2,192.168.45.254,1h --dhcp-range=192.168.46.2,192.168.46.254,1h --dhcp-range=192.168.47.2,192.168.47.254,1h --dhcp-range=192.168.48.2,192.168.48.254,1h --dhcp-range=192.168.49.2,192.168.49.254,1h

这时,可以正常连接上热点。

上网

连接上热点后,如果是Android手机连接上的会提示:

已连接,但是无法访问互联网

这里就要解决上网的问题。
由于板子另外一个能上网的是以太网,就把以太网开启,但是开启之后热点获取IP地址又不成功了。
正常

I/dnsmasq: DHCPDISCOVER(wlan0) 8c:3a:e3:18:94:ce 
    DHCPOFFER(wlan0) 192.168.43.68 8c:3a:e3:18:94:ce 
I/dnsmasq: DHCPREQUEST(wlan0) 192.168.43.68 8c:3a:e3:18:94:ce 
    DHCPACK(wlan0) 192.168.43.68 8c:3a:e3:18:94:ce android-2bc9472f786edcdc

异常

I/dnsmasq: DHCPDISCOVER(wlan0) 8c:3a:e3:18:94:ce 
    DHCPOFFER(wlan0) 192.168.43.68 8c:3a:e3:18:94:ce 
I/dnsmasq: DHCPDISCOVER(wlan0) 8c:3a:e3:18:94:ce 
    DHCPOFFER(wlan0) 192.168.43.68 8c:3a:e3:18:94:ce 

区别在于开启以太网后再连接热点时没有出现DHCPREQUEST,为什么没有呢?

今天再次仔细看LOG,看得出dnsmasq也是被热控制的,在启动后又被发送了一些事件。

05-13 00:33:37.514 119-381/? D/TetherController: Sending update msg to dnsmasq [update_ifaces:wlan0]
    Tethering services running
05-13 00:33:37.522 119-381/? D/TetherController: setDnsForwarders(0xf0064 0 = '192.168.1.1')
    Sending update msg to dnsmasq [update_dns:0xf0064:192.168.1.1]

第一个事件是:确定网络接口,即wlan0。如果不确定这个应该会导致DHCPOFFER下发不到位,则会一直处于DHCPDISCOVER状态;
第二个事件是:dns依托哪个IP的问题,这里依托的以太网连接的路由器IP。

系统中的dnsmasq是由netd启动的,再向下就得是自己写一个C程序控制,或者改由控制netd来达到控制dnsmasq的目的,这里有一个很好的文章Android 4.1 Netd详细分析(一)概述与应用实例。

基于netd控制热点

基于netd的控制就会简单一些

搜索并连接

insmod /system/lib/modules/bcmdhd.ko op_mode=2
busybox ifconfig wlan0 192.168.43.1
busybox ifconfig wlan0 up
ndc softap startap
# dns interface
ndc tether interface add wlan0
# 启动dnsmasq
ndc tether start 192.168.43.2 192.168.43.254

搜索这样就可以搜索到设备,并能连接上了,问题:不能上网;如果以太网开启了,则连接上获取不了IP地址。

连接以太网时获取IP

insmod /system/lib/modules/bcmdhd.ko op_mode=2
busybox ifconfig wlan0 192.168.43.1
busybox ifconfig wlan0 up
ndc softap startap
# dns interface
ndc tether interface add wlan0
# 启动dnsmasq
ndc tether start 192.168.43.2 192.168.43.254

# 启动IP转发

iptables -t nat -A natctrl_nat_POSTROUTING -o eth0 -j MASQUERADE
iptables -A natctrl_FORWARD -i eth0 -o wlan0 -m state --state ESTABLISHED,RELATED -g natctrl_tether_counters
iptables -A natctrl_FORWARD -i wlan0 -o eth0 -m state --state INVALID -j DROP
iptables -A natctrl_FORWARD -i wlan0 -o eth0 -g natctrl_tether_counters
iptables -A natctrl_tether_counters -i wlan0 -o eth0 -j RETURN
iptables -A natctrl_tether_counters -i eth0 -o wlan0 -j RETURN
iptables -D natctrl_FORWARD -j DROP
iptables -A natctrl_FORWARD -j DROP
ndc ipfwd enable

ndc tether dns set 100 192.168.1.1

搜索这样就可以搜索到设备,并能连接上,但是不能上网。

最终的手动开启热点脚本

下面是一个比较全面的,能上网的开启过程:

##NanoT2 手动启动热点

# 1. 加载驱动
# 方法1
insmod /system/lib/modules/bcmdhd.ko
ndc softap fwreload wlan0 AP
# 方法2
insmod /system/lib/modules/bcmdhd.ko op_mode=2

# 2. 启动hostapd
ndc softap startap

# 3. 启动wlan0
ndc interface setcfg wlan0 192.168.43.1 24 up

# 4. 添加
ndc tether interface add wlan0

# 5. 启动IP转发
ndc ipfwd enable

# 6. 启动dnsmasq用于管理dhcp服务器以及dns服务器转发
ndc tether start 192.168.43.10 192.168.43.99

# 7. 设置目标DNS地址,主要100是第一个被使能的网络netid,每次开关以太网数值为加1
#    最后能监听网络变化的广播,重新设置目标DNS;如果"100"这个数值不对,那么dnsmasq是无法转发到正确的DNS服务器上的
ndc tether dns set 100 192.168.1.1

# 8. 启动转发
ndc nat enable wlan0 eth0 0

# 9. 添加路由表以及规则
#    这里是通过观察系统开启热点时,所改变的路由表以及规则来做的操作;
ip route add 192.168.43.0/24 dev wlan0 proto static scope link table local_network
ip rule add from all oif wlan0 lookup local_network pref 14000

双WiFi配置
第二个WiFi的接口是eth1

# 2. 启动wlan0
ndc interface setcfg eth1 192.168.43.1 24 up

# 3. 启动hostapd
ndc softap startap

# 4. 添加
ndc tether interface add eth1

# 5. 启动IP转发
ndc ipfwd enable

# 6. 启动dnsmasq用于管理dhcp服务器以及dns服务器转发
ndc tether start 192.168.43.10 192.168.43.99

# 7. 设置目标DNS地址,主要100是第一个被使能的网络netid,每次开关以太网数值为加1
#    最后能监听网络变化的广播,重新设置目标DNS;如果"100"这个数值不对,那么dnsmasq是无法转发到正确的DNS服务器上的
ndc tether dns set 100 192.168.1.1

# 8. 启动转发
ndc nat enable eth1 wlan0 0

# 9. 添加路由表以及规则
#    这里是通过观察系统开启热点时,所改变的路由表以及规则来做的操作;
ip route add 192.168.43.0/24 dev eth1 proto static scope link table local_network
ip rule add from all oif eth1 lookup local_network pref 14000

后记

这个过程使用了近10天,前后了解了很多知识,在最后多网卡时,热点能开启不能获取IP的时候,还不知道有多个张路由表以及策略规则这回事,达成共识的还有个文章Wifi + Ethernet workable at the same time on Android。因为之前有了解过多张网卡在Android下使用busybox route看到的路由表已经正常了,但是似乎一直没有启使用一样,搜索到这个问答https://superuser.com/a/1179996/289070才知道Android上其实有多张路由表,顺着这个关键词搜索到了这个Android策略路由,这个文章详细了描述了他们之前的关系,我顺着这个思路,查看了系统开启热点和我手动开启热点的不同,对照着添加了路由表和策略,终于可以正常的获取IP了。随后了解了设置DNS的100的来历,现在已经可以正常手动开启热点并上网了。设置DNS中的netid,如果要灵活需要监听./base/services/core/java/com/android/server/connectivity/NetworkMonitor.java这里发出的广播,这里带有当前网络的netid。

之前遇到Android双网卡,不能同时ping通内外网的问题也有了思路,同样是添加路由表和策略就可以搞定了。那个就下一个文章写了。

你可能感兴趣的:(Android)