目标系统是一个 Linux 路由器系统,网桥默认开启 IGMP snooping,并且硬件 offload 路径能够:
- 广播 IGMP General Query;
- 在 Linux bridge/MDB 中生成 multicast router port;
- 从网桥从属接口接收 IGMP Report/Join/Leave;
- 将这些 IGMP 报文上送到网桥层处理。
当前缺口是:这些来自成员端口的 IGMP Report/Join/Leave 没有按协议转发到网桥的 router port,导致上游组播路由器无法完整感知下游主机成员关系变化。
本补丁在应用层实现一个 C 守护进程,用用户态 raw socket 补齐该转发行为。
程序接受以下选项:
igmp+ [-v] -b <bridge-name>选项说明:
-b <bridge-name>:指定网桥名称(必需)-v:verbose 模式,前台运行并打印调试信息
运行模式:
- 带
-v:前台运行,输出调试信息到终端 - 不带
-v:自动以 daemon 模式在后台运行
示例:
# 前台运行,带调试输出
igmp+ -v -b br-lan
# daemon 模式运行(无输出)
igmp+ -b br-lan
# 停止 daemon
pkill igmp+除网桥名称外,所有信息必须从系统 API 获取,不依赖 ip、bridge、brctl、ifconfig 等系统可执行命令。
- 参数中的网桥名称必须通过
if_nametoindex()解析为接口索引。 - 通过 rtnetlink
RTM_GETLINK获取网桥从属端口列表:- 读取每个 link 的
IFLA_MASTER; IFLA_MASTER == bridge ifindex的接口视为网桥从属端口。
- 读取每个 link 的
- 通过 rtnetlink
RTM_GETMDB获取网桥 router port 列表:- 解析
MDBA_ROUTER/MDBA_ROUTER_PORT; - 仅保留当前网桥的 router port;
- router port 必须同时是网桥从属端口。
- 解析
- 使用
AF_PACKETraw socket 绑定到网桥从属端口,接收 IPv4 IGMP 报文。 - 支持 IGMPv2 与 IGMPv3 成员关系报文,至少包括:
- Membership Query (
0x11):默认不转发,避免回灌查询报文; - IGMPv1/v2 Membership Report (
0x12,0x16); - IGMPv2 Leave Group (
0x17); - IGMPv3 Membership Report (
0x22)。
- Membership Query (
- 对从非 router port 收到的 IGMP Report/Leave,转发到所有 router port。
- 不将报文转发回接收端口。
- 从 router port 收到的 IGMP 报文不再转发,避免环路或重复上送。
- 保留原始以太网帧内容转发,不重写 IP/IGMP 负载。
- 能够处理 VLAN-tagged 以太网帧(802.1Q/802.1ad)中的 IPv4 IGMP。
- 定期刷新网桥端口和 router port 状态,以适配拓扑/MDB 变化。
- 支持
SIGINT/SIGTERM优雅退出。
- 实现语言:C。
- 不依赖外部命令;只使用 Linux 系统 API(netlink、socket、ioctl/if_nametoindex 等)。
- 尽量减少第三方依赖,使用 libc 与 Linux UAPI 头文件即可。
- 需要 root 或具备
CAP_NET_RAW、CAP_NET_ADMIN的运行权限。 - 目标运行平台为 arm64 Linux;代码适合作为嵌入式路由器固件组件交叉编译。
程序创建一个 AF_PACKET/SOCK_RAW socket,协议为 ETH_P_ALL,接收从网桥从属端口进入的二层帧。
收到报文后:
- 使用
recvmsg()获取PACKET_AUXDATA,优先从辅助信息读取入接口索引;缺省使用sockaddr_ll.sll_ifindex。 - 判断入接口是否为网桥从属端口;否则丢弃。
- 解析以太网头与可选 VLAN 标签,确认内层协议为 IPv4。
- 解析 IPv4 头,确认
protocol == IPPROTO_IGMP且头长合法。 - 解析 IGMP type:
0x12、0x16、0x17、0x22转发;0x11和其他类型不转发。
- 如果入接口是 router port,丢弃。
- 遍历当前 router port 列表,将原始帧通过
sendto(AF_PACKET)发送到每个 router port,但跳过入接口。
程序周期性使用 rtnetlink 刷新状态:
RTM_GETLINK:获取所有IFLA_MASTER == bridge_ifindex的接口作为 bridge ports;RTM_GETMDB:获取当前 bridge 的 multicast router ports;- router ports 与 bridge ports 取交集后用于转发。
刷新周期默认为 5 秒,后续如需可增加编译期宏或配置接口。
- 仅处理 IPv4 IGMP;MLD/IPv6 不在本补丁范围内。
- 不主动生成 IGMP 报文;只转发系统已经上送的 IGMP Report/Leave。
- 不维护组成员数据库;成员关系仍由 Linux bridge snooping 维护。
- 不处理 bridge VLAN filtering 下的 per-VLAN router port 语义;当前版本按端口级 router port 转发,保留 VLAN tag 原样发送。
make
sudo make install PREFIX=/usr/localarm64 交叉编译示例:
make CROSS_COMPILE=aarch64-linux-gnu-也可以显式指定编译器和 sysroot:
make CC=aarch64-linux-gnu-gcc CFLAGS="--sysroot=/path/to/sysroot -O2 -Wall -Wextra -std=c11"运行:
# 前台运行(带调试输出)
sudo /usr/local/sbin/igmp+ -v -b br-lan
# daemon 模式运行(无输出)
sudo /usr/local/sbin/igmp+ -b br-lan
# 停止 daemon
pkill igmp+- 在带 IGMP snooping 的网桥上启动程序。
- 下游主机加入/离开组播组。
- 在 router port 对端抓包,确认 IGMPv2/v3 Report/Leave 被转发。
- 确认 General Query 不被应用回灌。
- 动态切换 multicast router port,确认刷新周期后转发目标变化。