Skip to content

Latest commit

 

History

History
178 lines (129 loc) · 6.41 KB

File metadata and controls

178 lines (129 loc) · 6.41 KB

IGMP Snooping Router-Port Forwarding Patch

背景

目标系统是一个 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 获取,不依赖 ipbridgebrctlifconfig 等系统可执行命令。

功能需求

  1. 参数中的网桥名称必须通过 if_nametoindex() 解析为接口索引。
  2. 通过 rtnetlink RTM_GETLINK 获取网桥从属端口列表:
    • 读取每个 link 的 IFLA_MASTER
    • IFLA_MASTER == bridge ifindex 的接口视为网桥从属端口。
  3. 通过 rtnetlink RTM_GETMDB 获取网桥 router port 列表:
    • 解析 MDBA_ROUTER / MDBA_ROUTER_PORT
    • 仅保留当前网桥的 router port;
    • router port 必须同时是网桥从属端口。
  4. 使用 AF_PACKET raw socket 绑定到网桥从属端口,接收 IPv4 IGMP 报文。
  5. 支持 IGMPv2 与 IGMPv3 成员关系报文,至少包括:
    • Membership Query (0x11):默认不转发,避免回灌查询报文;
    • IGMPv1/v2 Membership Report (0x12, 0x16);
    • IGMPv2 Leave Group (0x17);
    • IGMPv3 Membership Report (0x22)。
  6. 对从非 router port 收到的 IGMP Report/Leave,转发到所有 router port。
  7. 不将报文转发回接收端口。
  8. 从 router port 收到的 IGMP 报文不再转发,避免环路或重复上送。
  9. 保留原始以太网帧内容转发,不重写 IP/IGMP 负载。
  10. 能够处理 VLAN-tagged 以太网帧(802.1Q/802.1ad)中的 IPv4 IGMP。
  11. 定期刷新网桥端口和 router port 状态,以适配拓扑/MDB 变化。
  12. 支持 SIGINT / SIGTERM 优雅退出。

非功能需求

  • 实现语言:C。
  • 不依赖外部命令;只使用 Linux 系统 API(netlink、socket、ioctl/if_nametoindex 等)。
  • 尽量减少第三方依赖,使用 libc 与 Linux UAPI 头文件即可。
  • 需要 root 或具备 CAP_NET_RAWCAP_NET_ADMIN 的运行权限。
  • 目标运行平台为 arm64 Linux;代码适合作为嵌入式路由器固件组件交叉编译。

设计

数据面

程序创建一个 AF_PACKET/SOCK_RAW socket,协议为 ETH_P_ALL,接收从网桥从属端口进入的二层帧。

收到报文后:

  1. 使用 recvmsg() 获取 PACKET_AUXDATA,优先从辅助信息读取入接口索引;缺省使用 sockaddr_ll.sll_ifindex
  2. 判断入接口是否为网桥从属端口;否则丢弃。
  3. 解析以太网头与可选 VLAN 标签,确认内层协议为 IPv4。
  4. 解析 IPv4 头,确认 protocol == IPPROTO_IGMP 且头长合法。
  5. 解析 IGMP type:
    • 0x120x160x170x22 转发;
    • 0x11 和其他类型不转发。
  6. 如果入接口是 router port,丢弃。
  7. 遍历当前 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/local

arm64 交叉编译示例:

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+

验证建议

  1. 在带 IGMP snooping 的网桥上启动程序。
  2. 下游主机加入/离开组播组。
  3. 在 router port 对端抓包,确认 IGMPv2/v3 Report/Leave 被转发。
  4. 确认 General Query 不被应用回灌。
  5. 动态切换 multicast router port,确认刷新周期后转发目标变化。