组播优化、mcast_to_unicast、multicast_querier和multicast_router

发布时间:2026/8/19 20:54:08
组播优化、mcast_to_unicast、multicast_querier和multicast_router shixudong163.com疫情期间对树莓派的组播功能进行了一些优化优化过程中碰到了一些问题通过网上搜索资料这些问题均通过Linux的相关参数调整得到了妥善解决原本一些模糊的概念也得以厘清特此记录以免遗忘。一、Bridge的mcast_to_unicast与hostapd的mcast_to_unicast区别无线组播具有先天缺陷并且还类似HUB不支持IGMP Snooping。v4.11内核为bridge增加了multicast to unicast选项使得无线组播也能变相实现IGMP Snooping功能。然而令人好奇的是Linux上充当无线AP的hostapd也有mcast_to_unicast选项这两者之间究竟有什么区别呢根据man bridge资料并结合源码分析和实际验证Bridge的mcast_to_unicast功能要生效必须同时满足以下三个条件缺一不可1、Bridge启用IGMP Snoopingmulticast_snooping已默认启用2、网络上要有IGMP查询器Linux Bridge自带multicast_querier但默认关闭3、Bridge下挂的发送网卡需启用mcast_to_unicast默认关闭。Bridge的mcast_to_unicast功能不仅对eth和wlan之间的组播有效对不同eth之间的组播也有效然而对于hostapd下连station之间的组播则无效。这是因为station之间相互通信全部在无线网卡驱动层处理压根到不了Bridge详见《Linux无线AP隔离功能分析》。为使station之间组播也能受益于Bridge的mcast_to_unicast功能还需要额外再启用两个选项首先启用hostapd的ap_isolate使得station之间通信必须上交Bridge处理其次启用Bridge下wlan端口的hairpin_mode使得从同一wlan口进来的组播包经过multicast_snooping处理后允许原路返回。此外无线station也会因为IGMPv2的report suppression机制导致启用mcast_to_unicast后收不到上游发来的组播数据包这也需要hostapd启用ap_isolate解决。综上Bridge的mcast_to_unicast功能在Bridge发送环节实现通用性强适用于所有无线网卡和有线网卡。不足是Bridge严格按照RFC要求实现IGMP Snooping224.0.0.0/24多播IP不受IGMP Snooping影响因此mcast_to_unicast对224.0.0.0/24网段也不起作用仍使用组播地址发送。此外ap_isolate表面上是hostapd选项但最终还需要交给无线网卡驱动实现AP隔离功能。对于SoftMAC无线网卡实际上是由内核mac80211驱动在收包时实现对于FullMAC无线网卡则由网卡固件实现然而大部分FullMAC网卡固件事实上还没有实现ap_isolate功能。Hostapd的mcast_to_unicast功能和ap_isolate一样最终也是通过无线网卡驱动实现的。对于SoftMAC无线网卡同样由mac80211驱动在发包时实现其优势在于不依赖BridgeNAT模式也能使用不涉及multicast_snooping可作用于任何多播网段。此外即使AP没有隔离即ap_isolate0无线station之间组播通信也能实现mcast_to_unicast功能。但其不足也和ap_isolate一样同样是大部分FullMAC无线网卡固件还没有实现mcast_to_unicast功能。Hostapd和Bridge配套使用时两者的mcast_to_unicast功能可以同时启用按照网络层对包发送的处理顺序Bridge的mcast_to_unicast先发挥作用漏网的组播包由hostapd的mcast_to_unicast再次处理理论上可实现mcast_to_unicast的全覆盖。然而对于大部分FullMAC无线网卡来说现实很骨感譬如树莓派4的板载无线网卡既不支持mcast_to_unicast也不支持ap_isolate所以树莓派4用作无线AP时其下连station之间组播要想受益于mcast_to_unicast功能只能通过外置无线网卡实现外置无线网卡一般采用SoftMAC架构使用mac80211框架mcast_to_unicast和ap_isolate都由内核mac80211驱动实现。二、Bridge的isolated与hostapd的ap_isolate区别根据源码分析前者隔离Bridge物理端口之间的通信必须进出端口都启用方生效后者隔离AP station之间的通信。Hostapd不启用ap_isolate时station之间的通信根本不经过Bridge此时Bridge的isolated对station之间通信起不到隔离作用。Hostapd启用ap_isolate后station之间无法通信此时启用Bridge下wlan端口的hairpin_modestation之间通信就可通过桥转发得以恢复。如再启用Bridge下wlan端口的isolated功能又能再次隔离station之间通过桥转发的通信虽然进出都是同一个wlan口但仍然符合进出端口都启用isolated的约束。当Hostapd选项ap_isolate1主要用于控制引导station之间通信经由桥处理时不宜再通过改变ap_isolate来控制station之间通信隔离与否此时可联合使用Bridge无线端口的isolated和hairpin_mode实现控制1isolated1始终开启AP隔离isolated0通过hairpin_mode控制AP隔离开启与否。2hairpin_mode0始终开启AP隔离hairpin_mode1通过isolated控制AP隔离开启与否。就源码分析来看在上述场景下isolated1开启隔离和hairpin_mode0不允许同一端口之间转发相当于开启隔离所起的作用是完全等效的。三、multicast_querier的源IP问题根据内核源码要使Bridge的IGMP Snooping功能生效网络上必须存在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或其他组播服务非224.0.0.0/24网段对于224.0.0.0/24网段Bridge不做任何限制。在其他设备上抓包分析开启multicast_query_use_ifaddr前后igmp查询报文的唯一区别就是源IP不同。如在树莓派上运行igmp querier脚本采用桥IP作为查询报文的源IP其他设备就能正常找到树莓派的MiniDLNA服务。此时在其他设备抓包igmp querier脚本发出的查询报文和启用multicast_query_use_ifaddr后multicast_querier发出的包完全一致且都采用树莓派的桥IP作为源IP。经在树莓派Bridge上抓包分析树莓派开启multicast_query_use_ifaddr前后的区别开启前能抓到本机发出的igmp report包。开启后就无法抓到本机发出的igmp report包估计是树莓派因为没有收到自身Bridge发出的查询报文从而就无法发出后续的igmp report包。这将导致Bridge不再维护MiniDLNA服务对应的组播地址239.255.255.250最终结果就是其他设备无法找到树莓派提供的MiniDLNA服务。结合源码分析和实际验证可启用accept_local参数允许从非lo网卡进来数据包的源IP可以是本机IP。此时树莓派就能收到自身Bridge发出的查询报文并向Bridge发出igmp report包报告MiniDLNA服务对应的组播IP仍然有效此时其他设备就能正常找到树莓派的MiniDLNA服务。四、Linux本机组播包自发自收问题鉴于从其他设备看到树莓派上igmp querier脚本和启用multicast_query_use_ifaddr 后multicast_querier发出的igmp查询报文完全一致但树莓派本机能收到前者报文却收不到后者报文启用accept_local参数后可正常接收。于是对这两者按理都能自发自收进行了对比分析意外发现本机接收multicast_querier发出的组播包竟然能匹配nat表PREROUTING链LOG规则。而根据网上资料接收自己发送的组播含广播和单播都不应该被本机nat表PREROUTING链跟踪和处理。经对源代码进行分析igmp querier脚本发送查询报文时如启用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的查询报文。在三层发组播包给自己属于标准的自发自收必须通过ip_local_out,自然也经由nf_conntrack_in在此处留了痕并在POSTROUTING链经由nf_conntrack_confirm加以确认。该包被本机自收并到达nat表PREROUTING链再次经由nf_conntrack_in时被识别为loopback or untracked故直接退出不会去匹配nat表PREROUTING链LOG规则导致永远无LOG产生。Bridge自带multicast_querier在二层发组播包给自己直接调用netif_rx收包无需经由ip_local_out的nf_conntrack_in也就是说并没有留下conntrack记录。后续到达nat表PREROUTING链时相当于外来包而非自发自收包因为无conntrack记录属于首次进入nf_conntrack_in故必然去匹配nat表PREROUTING链LOG规则首次匹配产生LOG后续适用快速匹配机制不再产生LOG。如只启用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限制总是能向外发送所有的组播包使用bridge -s -d mdb命令可以查看物理端口是否为router ports。近期一系列测试过程中发现不仅bridge物理端口有multicast_router选项Bridge本身也有multicast_router选项Bridge自己的multicast_router又在什么情况下起作用呢在igmp querier脚本和启用multicast_query_use_ifaddr后multicast_querier发送的查询报文对比测试中已经得到结论。如后者不启用accept_local参数树莓派就无法接收发给自己的查询报文导致本机后续无法发出igmp report包其他设备也就找不到树莓派提供的MiniDLNA服务。测试过程中突发奇想如果使用INPUT规则DROP掉igmp querier脚本发给自己的查询报文结果又会如何此时在Bridge上抓包确实也抓不到本机后续发出的igmp report包但其他设备仍然能准确无误找到树莓派提供的MiniDLNA服务根据前面的测试和推论完全无法给出合理解释。再次验证分析通常情况下Bridge对igmp查询报文的处理br_multicast_rcv既可在Bridge发出该报文br_dev_xmit时调用也可在Bridge接收该报文br_handle_frame_finish时调用。igmp querier脚本和multicast_querier发给自己的查询报文都是直接进入上层协议栈并不会触发Bridge对igmp查询报文的处理。igmp querier脚本从三层同时发往外部的igmp查询报文需要经过Bridge此时会调用Bridge的发送函数br_dev_xmit对igmp查询报文进行处理。简单来说就是Bridge持续收到上层协议栈发来的igmp查询报文后Bridge本身也将成为临时router ports与bridge物理端口不同的是该临时router ports仅仅面向上层协议栈意味着Bridge收到的组播包将不受multicast_snooping限制全部发往上层协议栈。因此即使使用INPUT规则显式DROP掉发给本机的igmp查询报文导致Bridge收不到本机发出的igmp report包也丝毫不影响其他设备的组播包通过Bridge发往本机上层协议栈从而顺利发现树莓派本身提供的MiniDLNA服务。启用multicast_query_use_ifaddr 后multicast_querier定期发往外部的igmp查询报文直接通过下挂网卡发出根本无需经过br_dev_xmit该函数仅面向上层提供发送服务。该情形下Bridge显然收不到来自上层协议栈的igmp query包无法成为面向上层协议栈的临时router ports。如本机没有启用accept_local参数上层协议栈将隐式DROP掉发给本机的igmp查询报文而Bridge则因为收不到本机发出的igmp report包不再维护对应的组播239.255.255.250导致其他设备的组播包到达Bridge后不再发往本机上层协议栈从而无法发现树莓派提供的MiniDLNA服务。根据上述分析为本文第三部分multicast_querier的源IP问题提供了另外一种解决思路即无需启用accept_local参数直接将Bridge自身的multicast_router设置为2即可。经测试此时即使Bridge仍然收不到本机发出的igmp report包也同样不影响其他设备顺利找到树莓派本身提供的MiniDLNA服务。值得一提的是无论Bridge本身成为临时router ports还是将Bridge自己的multicast_router设置为2都无法通过bridge -s -d mdb命令验证这一事实。