树莓派bond问题排查与分析

shixudong@163.com

一年前给pi3B(32位)配置了bond(使用active-backup模式,将eth0和wlan0加入bond0,bond-primary选择eth0),连到一台双频无线路由器,插上网线流量走eth0,拔掉网线走wlan0,插拔网线前后ping包一个也不丢,非常完美。同一时间对另一个pi4B(64位)进行了同样配置后,发现无法连接其他笔记本电脑(通过802.11ac联网),当时以为pi4B的64位OS刚发布,与bond不兼容,没有深究。最近偶尔发现笔记本电脑无法访问已开启bond的pi3B,经过多次测试,发现笔记本电脑只要使用2.4G wifi连入双频无线路由器,就无法访问开启了bond的pi3B,此时如拔掉pi3B的网线,则笔记本电脑就能顺利访问pi3B;在笔记本电脑使用5G WiFi联网时就不存在上述现象,一切都正常。


鉴于疫情期间本人在研究Linux bridge参数multicast_to_unicast和isolated的作用时(详见组播优化、multicast_query_use_ifaddr、accept_local及其他),发现hostapd参数ap_isolate=0时,同一AP下无线终端之间的通信压根不经过bridge。联想到pi3B开启bond后,虽然流量只走eth0,但wlan0仍然连在AP上待命,而pi3B的板载无线网卡只支持2.4G wifi网络,与使用2.4G wifi连入双频无线路由器的笔记本电脑恰巧位于同一AP,难道是基于同样的原因使得无线双频路由器在这种情况下将本应发到pi3B上eth0的流量发到了pi3B的wlan0,从而导致网络不通?通过wireshark分别在pi3B和笔记本电脑抓包分析,确实符合上述判断。为了进一步证实上述推断,本人用另一个pi4B搭建了Linux AP+bridge来模拟硬件无线路由器的桥接行为。


Linux AP+bridge需配置eth0、eth1和wlan1(外置2.4G无线网卡),加入同一个桥br0,其中eth0连本地网络,eth1用于直连pi3B,wlan1配合hostapd用作AP。笔记本电脑使用2.4G wifi连到Linux的AP,Pi3B用bond0连到Linux的eth1和AP(wlan1),由于bond使用active-backup模式,Pi3B的bond0、eth0和wlan0都使用eth0的MAC地址,在Linux上使用bridge fdb查看MAC与port的对应关系时,虽然pi3B上bond0的MAC地址能正确关联到bridge的eth1口而非wlan1口,但因笔记本电脑和pi3B之间的通讯走不到bridge,这个fdb以及bridge压根起不到作用,所以,如同硬件无线路由器一样,笔记本电脑和pi3B之间不能互相访问。此时如设置linux上hostapd参数ap_isolate=1,强制AP将无线终端之间的通信包引导到bridge处理,linux的bridge和fdb紧密协作,笔记本电脑和pi3B之间就能互相访问了。


分析问题要层层递进,先是合理推论,接下来实际验证,最后还是要通过源码分析找到根本原因,Hostapd虽然具有参数ap_isolate用于控制AP隔离,但Hostapd本身并不实现这一功能,只是将该参数通过nl80211传递给mac80211,由mac80211来实现AP隔离功能。经对mac80211相关源码进行分析,ap_isolate的实质是控制无线AP收到包后,是通过调用dev_queue_xmit将包通过无线网卡直接发送出去(ap_isolate=0)还是通过调用netif_receive_skb将包交由本机上层网络栈处理(ap_isolate=1)。显然ap_isolate=0时,无线AP对于无线终端之间的通信,是通过无线网卡直接发送出去,而无需交给bridge处理的,Linux AP发现pi3B的wlan0在线,就不会往bridge转交数据,笔记本电脑和pi3B之间的通信永远到不了pi3B的eth0口,除非拔掉pi3B的网线(流量走wlan0)、或者开启ap_isolate=1,笔记本电脑和pi3B之间方能正常通信。这也完美解释了一年前本人在pi4B配置bond后无法与笔记本电脑通信的故障(pi4B和笔记本电脑都连到同一个5G AP)。


前述验证过程中,还有一个意外发现,pi4B的板载无线网卡(wlan0)不使用mac80211,而是采用了fullmac驱动,虽然其fullmac不支持Hostpad的ap_isolate参数,但其作为AP在处理无线终端之间的通信包时,不仅通过无线网卡直接发送,而且同时还向上层网络栈(此处指bridge)复制一份,所以上述实验如用pi4B板载无线网卡wlan0配合hostapd实现AP时,笔记本电脑和pi3B之间都能正常通信,根本无法重现硬件无线路由器下的异常现象。


目前硬件无线路由器固件大都基于linux制作,并且为其提供AP功能的无线网卡大都采用softmac驱动,和mac80211配合使用,所以通过分析Linux AP+bridge的桥接行为,基本可以确认同一AP下使用active-backup模式bond的终端无法和其他纯无线终端通信是硬件无线路由器的通病(其实质是mac80211的fastpath机制所致)。


要解决这个问题,可以分别在两处用力,一是在硬件无线路由器上启用AP隔离,不足之处是AP隔离后其他无线终端之间就无法互相通信了。对于基于openwrt之类固件的路由器,这个两难问题可以轻松解决,即同时开启hostapd参数ap_isolate=1和bridge参数hairpin_mode=1,这也是最新版Openwrt实现无线AP的标配,让无线终端之间通信包交由bridge处理后通过hairpin原路返回AP层,从而仍然允许无线终端之间互相通信。


另一个发力点是在bond终端上解决,根据网上资料,active-backup模式bond可以和另一个bond参数all_slaves_active配合使用,all_slaves_active默认为0,以pi3B为例,表示备用口wlan0上收到的重复帧将被删除。如将all_slaves_active设置为1,则备用口wlan0上收到的重复帧将交由bond0处理,从而保证bond0能在流量走eth0的同时仍可从wlan0接收其他无线终端发出的通信包(回复包仍然通过eth0出去),同样可以解决active-backup模式下bond终端无法和其他纯无线终端通信的问题。网上有人担心该功能可能引发网络环路,明白了原理其实完全不必顾虑,该功能仅允许将收到的重复帧交由bond处理,而bond不具备bridge的功能,不会再次转发已处理的重复帧。


其实,active-backup模式还有另一个参数fail_over_mac可以解决上述问题,前面说过,Pi3B的bond0、eth0和wlan0都使用eth0的MAC地址,这正是fail_over_mac=0时active-backup模式的默认行为。当fail_over_mac=1时,eth0和wlan0分别拥有各自的MAC,bond0的MAC则不固定,跟随Currently Active Slave设备变化,并和其MAC保持一致。Pi3B插上网线时,Currently Active Slave设备是eth0,bond0此时对应的MAC是eth0的MAC,纯无线终端访问pi3B时,因其目标MAC指向eth0而非wlan0,流量可以顺利到达bond0的eth0口;同理,网线拔掉的情况下,纯无线终端访问pi3B的流量可以顺利到达bond0的wlan0口。


如果pi3B采用静态分配IP,前述问题可以完美解决,但如果pi3B采用dhcp分配IP,使用fail_over_mac=1将带来一些后遗症,除非一直不拔网线。譬如在拔掉网线的情形下启动pi3B,因为pi3B已将eth0设为第一个slave设备(bond-slaves eth0 wlan0),即使拔掉网线,bond启动时总选择第一个slave设备eth0的MAC作为bond0的MAC,紧接着dhclient马上启动,并一直与该MAC绑定。然后bond发现eth0不在线,选择wlan0作为Currently Active Slave设备,同时将bond0的MAC改为wlan0的MAC,于是后续发出的dhcp请求包将从wlan0发出,然而该dhcp请求包的源MAC仍为先前dhclient启动时绑定的MAC(eth0拥有),由于AP的安全限制,导致这些dhcp请求包永远也发不到DHCP服务器。幸运的是,只要插上网线,最多等待5分钟左右(dhclient请求不成功的睡眠时间)即可获取IP。


本人曾试着将wlan0设为第一个slave设备(bond-slaves wlan0 eth0),虽然解决了拔掉网线情况下pi3B的启动问题,然而在插上网线的情况下启动pi3B,却无法获取IP了。如前所述,bond0启动时选择wlan0的MAC作为自己的MAC,然后dhclient与该MAC绑定,后来bond0又选择主设备eth0(bond-primary eth0)的MAC作为自己的MAC,后续dhcp请求包(其源MAC始终是先前dhclient绑定的MAC,为wlan0所拥有)从eth0发出,因dhclient发出dhcp请求包的bootp flags为unicast,后续dhcp offer包采用单播回复,该单播包到达pi3B后,因目标MAC与eth0不符,直接被DROP,导致Pi3B始终无法获取IP(如pi3B启动时打开eth0的promisc模式,则可避免这一情形)。如果dhclient能配置bootp flags为broadcast或dhcp服务器能够配置以广播方式发送dhcp应答包,那么,Pi3B在使用dhcp获取IP时可设置fail_over_mac=1,并通过bond-slaves wlan0 eth0这种非主流方式也可解决最初的问题。


当fail_over_mac=2时,bond0的MAC始终保持不变,为第一个slave设备的MAC(pi3B为eth0),但eth0和wlan0的MAC并不相同,由bond根据主从设备切换情况随时更改,其更改原则为,Currently Active Slave设备和bond0的MAC(即eth0的MAC)保持一致,另一个slave设备则使用另一个MAC,确保eth0和wlan0的MAC始终不同。从理论上来说,无论插拔网线,bond0的MAC都不会变化,通过改变Currently Active Slave设备的MAC,使其和bond0的MAC保持一致,避免了fail_over_mac=1时的后遗症(其实质是dhclient启动时绑定了bond0的MAC,并始终将其作为dhcp请求包的源MAC,但和实际发送DHCP请求包的网卡MAC可能不一致);eth0和wlan0的MAC不相同,又避免了fail_over_mac=0时还需要额外配置all_slaves_active=1的不足。然而遗憾的是,Pi3B的eth0不支持运行状态下更改MAC,一旦拔掉其网线,wlan0当选为Currently Active Slave设备,其MAC改为和bond0一致(即eth0的MAC),而eth0的MAC却无法更改(如可以的话,将和wlan0对调MAC)。此后bond0、eth0和wlan0三者的MAC一致,和fail_over_mac=0时的状态无异,又只能通过all_slaves_active=1来解决最初的问题了。


测试中本人还发现,在fail_over_mac=2时,如果不是通过拔网线而是执行sudo ifconfig eth0 down强制pi3B的bond0切换到wlan0线路,虽然能成功切换,但也存在着严重的后遗症。这是因为执行上述命令后,eth0变为down状态,wlan0的MAC同样改为和bond0一致,和拔网线不太一样的是,在这种情况下eth0的MAC可以成功对调为wlan0的MAC,此后再执行sudo ifconfig eth0 up时,eth0的MAC又无法更改了(因其状态为up),只能继续使用wlan0的MAC,但不影响eth0变成主用设备,此时因其MAC和bond0的不一致,导致pi3B后续无法与外部通信。此时如在pi3B用-p参数(non-promiscuous mode)执行tcpdump,发现eth0上看不到外部发给bond0的包(因其目标MAC为bond0对应的MAC,与eth0不相符直接被DROP),如直接执行tcpdump(默认启用promisc模式),虽然eth0上能看到外部发给bond0的包了,但由于eth0给这些目标MAC与自己不相符的包打上了PACKET_OTHERHOST标志,依然不能被上层协议栈处理,导致无法与pi3B通信(在前面类似情形中,dhcp应答包采用了Linux Packet Filter技术,故不受PACKET_OTHERHOST标志影响)。

显然,对于pi3B来说,由于其eth0不具备热更改MAC的特性,fail_over_mac=2不仅无助于解决最初问题,若使用不当反而会引发彻底无法访问pi3B的问题。

鉴于树莓派板载网络的性质,有线和无线speed不对等,而且无法通过ethtool获取wlan0的speed,故树莓派天然适用active-backup模式(bond-mode 1)。在active-backup模式下,为解决最初的问题,如pi3B(pi4B)使用静态IP,最佳的方案是设置bond参数fail_over_mac=1;如欲采用dhcp获取IP,就只能同时使用fail_over_mac=0(默认)以及all_slaves_active=1了。另外,由上述排查分析过程可知,对于其他也采用了ethernet+wifi实现bond(active-backup模式)的Linux终端,同样存在无法访问同一AP下其他纯无线终端的问题,可参考本文并结合Linux终端的网卡特性以及特定软件所能支持的配置加以解决,至少能完全适用pi3B的解决方案。

最后,再提供一种与bond完全无关、同样可以完美解决Linux终端实现eth0+wlan0互备的方案,Linux终端安装NetworkManager,并利用其dispatcher功能实现eth0和wlan0的互斥使用。eth0在线,就关掉wlan0,否则开启wlan0,NetworkManager在网络状态发生改变时实时触发上述检测机制,详细的脚本可查看nmcli-examples的man page。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值