shixudong@163.com
疫情期间,对树莓派的组播功能进行了一些优化,优化过程中碰到了一些问题,通过网上搜索资料,这些问题均通过Linux的相关参数调整得到了妥善解决,原本一些模糊的概念也得以厘清,特此记录,以免遗忘。
一、Bridge multicast_to_unicast与hostapd multicast_to_unicast参数的区别
无线组播具有先天缺陷,常规优化方法就是启用multicast_to_unicast功能,Bridge和hostapd都有multicast_to_unicast参数,然而他们之间究竟有什么区别呢?
根据man bridge资料,并结合源码分析和实际验证,要启用bridge物理端口的multicast_to_unicast功能(默认关闭),必须同时启用multicast_snooping功能(默认启用)和multicast_querier功能(默认关闭),才能使bridge物理端口的multicast_to_unicast功能真正生效。
Bridge的multicast_to_unicast功能不仅对eth和wlan之间的组播有效,对不同eth之间的组播依然有效,然而对于同一AP下station之间的组播则无效。虽然station之间相互通信仍然必须通过AP,但这些通信全部在无线网卡驱动层处理,其行为更像Hub而非Switch,压根到不了bridge层面。为使station之间组播也能受益于bridge的multicast_to_unicast功能,需要额外再启用两个参数,首先启用hostapd的ap_isolate功能,使得station之间通信交由bridge处理;其次启用bridge下wlan端口的hairpin_mode功能,使得从同一wlan口进来的组播包经过multicast_snooping处理后允许原路返回。
综上,Bridge的multicast_to_unicast功能在bridge发送环节实现,通用性强,适用于所有无线网卡和有线网卡。不足是由于bridge的内在机制(RFC规范要求),multicast_to_unicast对224.0.0.X多播网段不起作用。此外无线station之间的multicast_to_unicast功能还取决于bridge是否采用SoftMAC无线网卡,大部分FullMAC无线网卡还没有实现ap_isolate功能。
Hostapd的multicast_to_unicast功能,在无线网卡发送环节实现,其优势在于不依赖bridge,无需配置multicast_snooping,可作用于任何多播网段;ap_isolate=0时,无线station之间通信也能实现multicast_to_unicast功能。其不足仍然是大部分FullMAC无线网卡还没有实现multicast_to_unicast功能。
Hostapd和Bridge配套使用时,两者的multicast_to_unicast功能可以同时启用,按照网络层对包发送的处理顺序,Bridge的multicast_to_unicast先发挥作用,漏网的组播包,由hostapd的multicast_to_unicast再次处理,理论上可实现multicast_to_unicast的全覆盖。然而对于FullMAC无线网卡来说,依然美中不足,譬如树莓派4的板载无线网卡既不支持multicast_to_unicast,也不支持ap_isolate,所以树莓派4用作无线AP时,其下连station之间组播要想充分受益于multicast_to_unicast功能,只能通过外置无线网卡实现(外置无线网卡采用SoftMAC驱动,使用mac80211框架,multicast_to_unicast和ap_isolate在mac80211实现,不依赖SoftMAC驱动)。
二、Bridge isolated与hostapd ap_isolate参数的区别
根据源码分析,前者隔离bridge物理端口之间的通信(必须进出端口都启用方生效),后者隔离AP station之间的通信。Hostapd不启用ap_isolate时,station之间的通信不经过桥,此时bridge isolated对station之间通信起不到隔离作用。
Hostapd启用ap_isolate后,station之间无法通信,此时启用bridge下wlan端口的hairpin_mode功能,station之间通信就可通过桥转发得以恢复,如再启用bridge下wlan端口的isolated功能,便再次隔离station之间通过桥转发的通信(虽然进出都是同一个wlan口,但仍然符合进出端口都启用isolated这一约束)。
当Hostapd参数ap_isolate=1主要用于控制引导station之间通信经由桥处理时,不宜再通过改变ap_isolate参数来控制station之间通信隔离与否,此时可联合使用bridge无线端口的isolated和hairpin_mode参数实现控制:(1)isolated=1,始终开启AP隔离;isolated=0,通过hairpin_mode控制AP隔离开启与否。(2)hairpin_mode=0,始终开启AP隔离;hairpin_mode=1,再通过isolated控制AP隔离开启与否。就源码分析来看,在上述场景下,isolated=1(开启隔离)和hairpin_mode=0(不允许同一端口之间转发,相当于开启隔离)所起的作用是完全等效的。
三、multicast_querier的源IP问题
如上文所述,要使bridge的multicast_to_unicast功能生效,网络上必须存在igmp querier定期维护组播转发信息。家庭网络可启用linux bridge自带的multicast_querier,定期向bridge本身和下挂的网卡发送igmp查询报文,默认使用0.0.0.0作为查询报文的源IP。根据资料,源IP为0.0.0.0的igmp querier不参与querier选举,为此可启用multicast_query_use_ifaddr参数使用桥IP作为查询报文的源IP。
在树莓派上启用multicast_querier后,一切运作正常。但一旦开启multicast_query_use_ifaddr功能,其他设备就无法找到树莓派本身提供的MiniDLNA服务(239.255.255.250)或其他multicast接收端服务(非224.0.0.X网段,对于224.0.0.X网段,Bridge不做任何限制)。在其他设备上抓包分析,开启multicast_query_use_ifaddr前后igmp查询报文唯一的区别就是源IP不同。设法在树莓派上运行igmp querier专用软件(采用桥IP作为查询报文的源IP),其他设备能正常找到树莓派的MiniDLNA服务,此时在其他设备抓包,专用软件发出的查询报文包和启用multicast_query_use_ifaddr后树莓派bridge的multicast_querier发出的包格式和内容完全一致,都采用树莓派的桥IP作为源IP。
经在树莓派bridge上抓包,分析树莓派开启multicast_query_use_ifaddr前后的区别,开启前能收到本机发出的igmp report包,而开启后则无法收到本机发出的igmp report包,后者估计是树莓派没有收到自身bridge发出的查询报文包,从而无法发出后续igmp report包。结合源码分析和实际验证,可启用accept_local参数,允许从非loopback网卡进来数据包的源IP可以是本机IP,树莓派就能收到自身bridge发出的查询报文包,并向bridge的multicast_querier发回igmp report包,报告MiniDLNA服务所加入的多播地址,此时其他设备就能正常找到树莓派的MiniDLNA服务。
四、Linux本机组播包自发自收问题
鉴于从其他设备看到树莓派上igmp querier专用软件和bridge的multicast_querier(启用multicast_query_use_ifaddr后)发出的igmp查询报文完全一致,但树莓派本机只能收到前者报文,收不到后者报文(启用accept_local参数后可正常接收)。于是对这两者(都是组播包,理论上都能自发自收)进行了对比分析,意外发现,在本机接收multicast_querier发出的组播包时竟然能被iptables nat表的PREROUTING处理,而根据网上资料,接收自己发送的组播(含广播)和单播都不应被本机iptables nat表PREROUTING跟踪和处理。
经对源代码进行分析,通常三层软件发送组播包时,如启用IP_MULTICAST_LOOP选项(默认启用),将会发给自己一份(广播包无此选项,必须发给自己一份)。在三层发组播包给自己时,直接调用dev_loopback_xmit,在此函数中通过skb_dst_force强制设置路由后调用netif_rx收包,本机接收时,调用skb_valid_dst,发现数据包路由有效,从而跳过了需要进行源IP验证的环节,所以无需启用accept_local参数便能收到组播包。而multicast_querier在二层发组播包给自己时,直接调用netif_rx收包,本机接收时,调用skb_valid_dst,发现数据包路由不存在,后续需要进入源IP验证环节,如不启用accept_local参数,就DROP掉源IP为本机IP的数据包。
在三层发组播包给自己时,因为是通过netif_rx收包,不涉及物理网卡,在调用dev_loopback_xmit前通过rt_dst_clone将发送设备修改为loopback_dev,而multicast_querier在二层发组播包给自己时,没有修改发送设备这一环节。本机接收时,nat表的conntrack机制判断如是从loopback_dev收到的包(包括单播),不做跟踪处理,所以正常情况下,igmp querier专用软件发给自己的组播包不能被nat表的PREROUTING处理,而multicast_querier发给自己的组播包能被nat表的PREROUTING处理(如果只启用multicast_query_use_ifaddr而不启用accept_local,因为组播包后续无法通过源IP验证而被DROP,能频繁看到LOG)。通过对raw和mangle表的PREROUTING链进行LOG,证明igmp querier专用软件发给自己的组播包确实经过这两个表的PREROUTING链,后续自然也会经过nat表的PREROUTING链,只是不进行跟踪处理而已。
五、Bridge的multicast_router
根据资料,Bridge物理端口上的multicast_router默认为1,表示该端口如持续收到外部igmp query包的话,该端口将成为临时router ports;如multicast_router设置为2,表示该端口将成为永久router ports。一旦物理端口成为router ports,该端口将不受multicast_snooping限制,总是能向外发送所有的multicast traffic,使用bridge -s -d mdb命令可以查看物理端口是否为router ports。近期一系列测试过程中,发现不仅bridge物理端口有multicast_router参数,Bridge本身也有multicast_router参数,Bridge自己的multicast_router又在什么情况下起作用呢?
在前面igmp querier专用软件和bridge的multicast_querier(启用multicast_query_use_ifaddr后)发送igmp查询报文的对比测试中,已经得到结论,如后者不启用accept_local参数,就无法接收发给自己的igmp查询报文,导致收不到本机后续发出的igmp report包,其他设备就找不到树莓派本身提供的MiniDLNA服务。测试过程中,偶尔通过INPUT规则DROP掉前者发给自己的igmp查询报文,此时在bridge上抓包,确实发现也无法收到本机后续发出的igmp report包,但其他设备仍然能找到树莓派本身提供的MiniDLNA服务,根据前面的测试和推论,完全无法给出合理解释。
再次验证分析,通常情况下,Bridge对igmp查询报文的处理既可在bridge发出该报文时处理,也可在bridge接收该报文时处理。igmp querier专用软件发给自己的igmp查询报文在三层直接调用dev_loopback_xmit,dev_loopback_xmit又直接调用netif_rx收包,此时关联设备为bridge本身,故组播包不会进入bridge,而是直接进入上层IP协议栈。因此发给自己的igmp查询报文无论是发送还是接收,都不会触发bridge对igmp查询报文的处理。但igmp querier专用软件发往外部的igmp查询报文从三层发出,需要经过bridge,此时会调用bridge的发送函数br_dev_xmit对igmp查询报文进行处理。简单来说,就是bridge持续收到上层发来的igmp查询报文后,Bridge本身也将成为临时router ports,与bridge物理端口不同的是,该临时router ports仅仅面向上层协议,意味着bridge收到的multicast traffic将不受multicast_snooping限制,全部发往上层协议。因此即使INPUT规则DROP掉了发给本机的igmp查询报文,导致bridge收不到本机发出的igmp report包,也丝毫不影响其他设备的组播包通过bridge发往本机上层协议,从而顺利发现树莓派本身提供的MiniDLNA服务。
Bridge的multicast_querier(启用multicast_query_use_ifaddr后)的igmp查询报文从二层发出,无论是发给自己还是发往下挂网卡,都无需经过br_dev_xmit处理(该函数仅面向上层提供发送服务),发给自己的查询报文直接调用netif_rx收包,此时关联设备同样为bridge本身,组播包将直接进入上层IP协议栈,而不会进入bridge。该情形下bridge收不到上层发出的igmp query包,不可能成为面向上层的临时router ports。如本机没有启用accept_local参数,上层IP协议最终也将DROP掉发给本机的igmp查询报文,导致bridge收不到本机发出的igmp report包,其他设备的组播包到达bridge后,不会发往本机上层协议,也就无法发现树莓派本身提供的MiniDLNA服务。
根据上述分析,为本文第三部分存在的问题提供了另外一种解决思路,即无需启用accept_local参数,直接将bridge自身的multicast_router设置为2即可。经测试,此时即使bridge始终收不到本机发出的igmp report包,也同样不影响其他设备顺利找到树莓派本身提供的MiniDLNA服务。
值得一提的是,无论bridge本身成为临时router ports,还是将bridge自己的multicast_router设置为2,都无法通过bridge -s -d mdb命令验证这一事实。
本文详细探讨了树莓派在疫情期间针对组播功能的优化过程,涉及Linux Bridge和Hostapd的multicast_to_unicast参数区别、桥接隔离参数的影响以及multicast_querier的源IP问题。作者通过源码分析和实际测试,揭示了Bridge的multicast_to_unicast功能的工作原理,以及如何解决组播路由器和组播查询报文自收自发的问题。此外,还讨论了如何避免使用accept_local参数,通过设置Bridge的multicast_router参数来实现相同效果。

8838

被折叠的 条评论
为什么被折叠?



