bigywater.tech
网络协议 · 33

IGMP:成员关系查询与报告

你的电脑加入一个组播组时,是怎么告诉路由器和交换机“请把这个组的流量给我”的?IGMP 的报文长什么样?本机又是怎么回应查询的?这篇在 en0 上真实地加入和离开一个组,逐字节解码 IGMP 报文,并被动听 180 秒看局域网里别的设备的 IGMP 行为。

8 字节
IGMPv2 报文大小:类型、最大响应时间、校验和、组地址
79 个 / 27 个
180 秒内别的设备发的 IGMPv2 报告个数与源地址个数,全部是 224.0.0.251 这个组
0 个查询
这 180 秒里听到的 IGMP 成员查询个数(累计有 3497 个)

报文

实测 · 本机执行输出
  net.inet.igmp.gsrdelay: 10
  net.inet.igmp.default_version: 3
  net.inet.igmp.legacysupp: 0
  net.inet.igmp.v2enable: 1
  net.inet.igmp.v1enable: 1
  net.inet.igmp.sendlocal: 1
  net.inet.igmp.sendra: 1
  netstat -s -p igmp 累计:收到 IGMP 报文 84112 个,其中 V1/V2 成员查询 3497 个,成员报告 80613 个(大多是别的设备发的),本机发出的成员报告 1713 个
  default_version = 3:本机默认说 IGMPv3;但收到 V1/V2 查询时会按对方的版本降级回应(见第三节抓到的报告类型)

IGMPv2 只有 8 字节,IP 头带 4 字节的 Router Alert 选项,TTL 1。报告发给组地址本身,离开发给 224.0.0.2。

加入与离开

实测 · 本机执行输出
  加入后本机发出 IGMP 报文 1 个(只算与本节操作相关的、源地址是本机的)
    +      0 ms  IGMPv2 报告(0x16),IP 目的 239.255.42.99,以太网目的 01:00:5e:7f:2a:63,TTL 1,IP 头 24 字节,IGMP 部分 8 字节,组地址 = 239.255.42.99,校验和正确
  离开后本机发出 IGMP 报文 1 个(只算与本节操作相关的、源地址是本机的)
    +      0 ms  IGMPv2 离开(0x17),IP 目的 224.0.0.2,以太网目的 01:00:5e:00:00:02,TTL 1,IP 头 24 字节,IGMP 部分 8 字节,组地址 = 239.255.42.99,校验和正确
  IGMPv2 报文固定 8 字节:类型 1 + 最大响应时间 1(单位 0.1 秒)+ 校验和 2 + 组地址 4;校验和是对这 8 字节做的互联网校验和(第 17 篇)

加入时本机发 1 个成员报告,离开时发 1 个 Leave(没有看到重复发送)。成员关系用于让交换机 / 路由器只把这个组的流量转给需要的端口。

查询

实测 · 本机执行输出
  180 秒共抓到 79 个 IGMP 报文:IGMPv2 报告 79
  本机发出 0 个,别的设备发出 79 个(只统计个数)
  180 秒里没有抓到任何成员查询——这个网段上可能没有 IGMP 查询者,或者间隔比 180 秒长
  netstat 累计的变化(180 秒):收到 IGMP 报文 +79,V1/V2 成员查询 +0,成员报告 +79,本机发出的成员报告 +0

查询者定期发 General Query,主机在最大响应时间内随机延迟回应。本次 180 秒没有听到查询;累计计数说明以前有。

局域网里的报告

实测 · 本机执行输出
  79 个 IGMPv2 报告来自 27 个不同的源地址,涉及 1 个不同的组;TTL 取值 [1];校验和正确的 79 / 79;IP 头长度取值 [24] 字节(24 = 带 Router Alert 选项);最大响应时间字段(报告里恒为 0)取值 [0]
  按组统计:224.0.0.251(mDNS) 79
  IGMPv2 报告的 IP 目的地址就是被报告的那个组(79 / 79 个符合);所以同一个组里的其它成员也能收到它——这就是“报告抑制”的基础:听到别人已经报告了同一个组,自己就不再发

别的设备在没有查询的情况下也持续发 mDNS 组的报告,27 个设备 180 秒发了 79 个,没有看到“抑制”的效果。

分步演示

IGMP
本步的对象

    本文用到的工具与边界

    • 本机 macOS 27 arm64(Apple 芯片)/ Python 3.14.8 / tcpdump(libpcap)/ scapy 2.8 / dpkt / dnspython / h2 / aioquic / OpenSSL / curl 8.7.1;抓包在 lo0(环回)上做,少数实验用真实网卡 en0(这台机器上 en0 是 Wi-Fi,tcpdump 看到的是系统转成以太网格式的帧,所以文中的“以太网头”都指这个 14 字节的头)或访问公网(会在文章里注明);需要丢包 / 延迟 / 限速时,用一个只作用于 lo0 端口 9400-9499 的 dummynet 辅助脚本(netlab);所有服务端都是本机自己起的进程;时间戳取自 tcpdump 的 -tt,数字每次运行会有小幅抖动。第二节加入的组 239.255.42.99 是私有管理范围里的测试地址;第三、四节来自一次 180 秒的被动抓包,别的设备的报告只统计个数,不写任何地址;本机默认 IGMPv3,但这个网段上只看到 v2 报文
    • 没有验证:IGMPv3 的报告与查询(见下一篇);IGMP 查询触发下的回应延迟分布(这段时间没有查询);交换机 / 接入点的 IGMP snooping;离开时是否会触发“特定组查询”;Router Alert 选项在网络设备上的实际处理;别的设备为什么在没有查询时持续报告
    京ICP备2026067502号-1京公网安备11010502063487号