概说《TCP/IP详解 卷2》第13章 IGMP:Internet组管理协议

1,845 阅读11分钟

本文要点

  • 引言

  • IGMP结构

  • IGMP的protosw结构

  • igmp_joingroup函数

  • igmp_fasttimo函数

  • igmp_input函数

    • 成员关系查询

    • 成员关系报告

  • igmp_leavegroup函数

  • 小结

引言

    IGMP在本地网络上的主机和路由器之间传达组成员信息。路由器定时向“所有主机组”多播IGMP查询。主机多播IGMP报告报文 以响应查询。

    从体系结构的观点来看,IGMP是位于IP上面的运输层协议。它的协议号为2,它的报文是由IP数据报运载的,和ICMP一样。而且进程通常不直接访问IGMP,但进程可以通过IGMP插口发送或者接收IGMP报文。这个特性使得能够把多播选路守护程序作为用户级进程实现。

    图1显示了Net/3中的IGMP协议的整体结构。

图1 IGMP处理概要

    IGMP处理的关键是一组在图1中心显示的in_multi结构。到达的IGMP查询使igmp_input为每个in_multi结构初始化一个递减定时器。该定时器由igmp_fasttimo更新,当每个定时器超时时,igmp_fasttimo调用igmp_sendreport。

    在第12章看到,当创建一个新的in_multi结构时,ip_setmoptions调用igmp_joingroup。igmp_joingroup调用igmp_sendreport来发布新的组成员信息,使组的定时器能够在短时间内安排第二次通告。igmp_sendreport完成对IGMP报文的格式化,并把它传给ip_output。

    在图1的左边和右边,我们看到一个原始插口可以直接发送和接收IGMP报文。

IGMP结构

    IGMP报文只有8字节长。图2显示了Net/3使用的igmp结构。

图2 igmp结构

    igmp_type包括一个4bit的版本码和一个4bit的类型码。图3显示了标准值。

图3 IGMP报文类型

43~44 Net/3只使用版本1的报文。多播路由器发送1类报文向本地网络上所有主机请求成员关系报告。对1类IGMP报文响应是主机的一个2类报文,报告它们的多播成员信息。3类报文是在路由器之间传输多播选路信息(原著第14章)。主机不处理3类报文。

45~46 在IGMP版本1中没有使用igmp_code。igmp_cksum与ICMP类似,计算IGMP报文的所有8个字节。

47~48 对查询,igmp_group是0。对回答,它包括报告的多播组。

    图4是相对于IP数据报的IGMP的报文结构。

图4 IGMP报文

IGMP的protosw结构

    图5是IGMP的protosw结构。

图5 IGMP protosw的结构

    尽管进程有可能通过IGMP protosw入口发送原始IP分组,但在本文我们只考虑内核如何处理IGMP报文。

    三种事件触发IGMP处理:

  • 一个本地接口加入一个新的多播组

  • 某个IGMP定时器超时

  • 收到一个IGMP查询

    还有两种事件也触发本地IGMP处理,但结果不发送任何报文:

  • 收到一个IGMP报告

  • 某个本地接口离开一个多播组

加入一个组:igmp_joingroup函数

    在概说《TCP/IP详解 卷2》第12章 IP多播中我们看到,当一个新的in_multi结构被创建时,in_addmulti函数会调用igmp_joingroup。后面加入同一多播组的请求只增加in_multi结构的引用计数,而不调用igmp_joingroup。igmp_joingroup如图6所示。

图6 igmp_joingroup函数

164~178 inm指向新的组的in_multi结构。如果新的组是“所有主机组”,或者成员关系请求是环回接口,则inm_timer被禁止,igmp_joingroup返回。不报告“所有主机组”的成员关系,因为假定每个多播主机都是该组成员。没必要向环回接口发送组成员报告,因为本地主机是在环回网络上唯一系统,它已经知道它的成员状态了。

    在其它情况下,新组的报告被立即发送,并根据组的情况为组定时器选择一个随机的值。全局标志位igmp_timers_are_running被设置,表明至少使能一个定时器。igmp_fasttimo检查这个变量,避免不必要的处理。

59~73 当新组的定时器超时,就发布第2次成员关系报告。复制报告是无害的,当第1次报告丢失或者被破坏时,有了它就保险了。IGMP_RANDOM_DELAY(图7)计算报告时延。

图7 IGMP_RANDOM_DELAY函数

    报告定时器设成0到10之间的随机秒数(IGMP_MAX_HOST_REPORT_DELAY)。因为IGMP定时器被减去PR_FASTHZ,即5,所以IGMP_RANDOM_DELAY必须选择一个在1~50之间的随机数。如果r是把接到的所有IP分组数、主机的原始地址和多播组相加后得到的随机数,则

0 <= ( r mod 50 ) <= 49

1 <= ( r mod 50 ) + 1 <= 50

    要避免为0,因为这会禁止定时器,并且不发送任何报告。

igmp_fasttimo函数

    在讨论igmp_fasttimo之前,我们需要描述一下遍历in_multi结构的机制。在遍历过程中,in_multistep结构(图8)记录位置。

图8 in_multistep结构

123~126 i_ia指向下一个in_ifaddr接口结构,i_inm指向当前接口的in_multi结构。

    IN_FIRST_MULTI和IN_NEXT_MULTI宏(图9)遍历该表。

图9 IN_FIRST_MILTI和IN_NEXT_MULTI结构

154~169 如果in_multi表有多个入口,i_inm就前进到下一个入口。当IN_NEXT_MULTI到达多播表的最后时,i_ia就指向下一个接口,i_inm指向与该接口相关的第一个in_multi结构。如果该接口没有多播结构,while循环继续遍历整个接口表,走到搜索完所有接口。

170~177 in_multistep数组初始化时,指向in_ifaddr表的第一个in_ifaddr结构,i_inm设成空。IN_NEXT_MULTI找到第一个in_multi结构。

    从图5中可以看出,igmp_fasttimo是IGMP的快速超时函数,每秒被执行5次。igmp_fasttimo(图10)递减多播报告定时器,并在定时器超时时发送一个报告。

图10 igmp_fasttimo函数

187~198 如果igmp_timers_are_running为假,igmp_fasttimo立即返回,不再浪费时间检查各个定时器。

199~213 igmp_fasttimo重新设置运行标志位,用IN_FIRST_MULTI初始化step和inm。igmp_fasttimo函数用while循环找到各个in_multi结构和IN_NEXT_NULTI宏。对每个结构:

  • 如果定时器为0,则什么都不做。

  • 如果定时器不为0,则将其递减。如果到达0,则发送一个IGMP组成员关系报告。

  • 如果定时器还不为0,则至少还有一个定时器在运行,所以把igmp_timers_are_runnig设成1。

    igmp_sendreport函数(图11)为一个多播组构造和发送IGMP报告报文。

图11 igmp_sendreport函数

214~232 唯一的inm指向被报告组的in_multi结构。igmp_sendreport分配一个新的mbuf,准备存放一个IGMP报文。igmp_sendreport为链路层首部留下空间,把mbuf的长度和分组长度设成IGMP报文的长度。

233~245 每次构造IP首部和IGMP报文的一个字段。数据报的源地址设成INADDR_ANY,目的地址是被报告的多播组。ip_output用输出接口的单播地址替换INADDR_ANY。每个组成员和所有多播路由器都接收报告。

246~260 最后,igmp_sendreport构造一个ipmoptions结构,并把它与报文一起传给ip_output。与in_multi结构相关的接口被选做输出接口;TTL被设成1,使报告只能在本地网络上传播;如果本地系统被配置成路由器,则允许这个请求的多播环回。

输入处理:igmp_input函数

    在概说《TCP/IP详解 卷2》第12章 IP多播中,我们描述了ipintr的多播处理部分。我们看到,多播路由器接收所有的IGMP报文,但多播主机只接受那些到达接口是目的多播组成员的IGMP报文,即那些接收它们的接口是组成员的查询和成员关系报告。

    标准协议分用机制把接受的报文传给igmp_input。igmp_input的开始和结束如图12所示。下面将描述每种IGMP报文类型。

图12 igmp_input函数

    a. 验证IGMP报文

52~96 函数ipintr传递一个指向接收分组的指针m,和数据报IP首部的大小iphlen。

    数据报的长度必须足够容纳一个IGMP报文(IGMP_MIN_LEN),并能被放在一个标准的mbuf首部中(m_pullup),而且还必须有正确的IGMP检验和。如果发现有任何错误,统计错误个数,并自动丢弃该数据报,igmp_input返回。

    igmp_input进程体根据igmp_type内的代码处理无效报文。switch语句基于igmp_type中两个值的结合进行相应处理。

    b. 把IGMP报文传给原始IP

157~163 switch语句没有default情况。所有有效报文被传给rip_input,在rip_input里被提交给所有监听IGMP报文的进程。监听里程可以自己处理或者丢弃那些内核不识别的版本或者类型的IGMP报文。

1. 成员关系查询报文:IGMP_HOST_MEMBERSHIP_QUERY

    多播路由器第120秒至少发布一次IGMP成员关系查询。把查询发到224.0.0.1组(所有主机组)。图13显示了主机如何处理报文。

图13 IGMP查询报文的输入处理

97~122 到达环回接口上查询报文被自动丢弃。查询报文被定义成发给“所有主机组”,到达其它地址的查询报文由igps_rcv_badqueries统计数据,并被丢弃。

    接收查询报文并不会立即引起IGMP成员报告。相反,igmp_input为与接收查询接口相关的各个组定时器设置一个随机值IGMP_RANDOM_DELAY。当某组的定时器超时,则igmp_fasttimo发送一个成员关系报告,与此同时,其它所有收到查询的主机也进行同样动作。一旦某个主机上的某个特定组的随机定时器超时,就该组多播一个报告。这个报告将取消其它主机上的定时器,保证只有一个报告在网络上多播。路由器与其它组员一样,接收该报告。

    这个情况的一个例外就是“所有主机组”,这个组不设定定时器,也不发送报告。

2. 成员关系报告报文:IGMP_HOST_MEMBERSHIP_REPORT

    成员关系报告报文的效果限于接收它的接口本地。图14显示了报文处理。

图14 IGMP报告报文的输入处理

123~146 丢弃发送到环回接口上的报告。同时,报文的目的地址必须是IGMP报文内标识的组。

    不完整的初始化的主机源地址中可能没有网络号或主机号(或两个都没有)。igmp_report查看地址的A类网络部分,如果地址的网络或者子网部分是0,这部分一定为0。如果是这种情况 ,则把源地址设成子网地址,其中包含正在接收接口的网络标识符和子网标识符。这样做的唯一原因是为了通知子网号所标识正在接收接口上的某个进程组守护进程。

    如果接收接口属于被报告的组,就把相关的报告定时器设成0。从而使用发给该组的第一个报告能够制止其它主机发布报告。路由器只需要知道网络上至少有一个接口是组的成员,就无需维护一个明确的组成员表或者计数器。

离开一个组:igmp_leavegroup函数

    在概说《TCP/IP详解 卷2》第12章 IP多播中看到,当in_multi结构中的引用计数器减少到0时,in_delmulti函数会调用igmp_leavegroup,如图15所示。

图15 igmp_leavegroup函数

179~186 当一个接口离开一个组时,IGMP没有采取任何动作。不发明确的通知,等下一次多播路由器发布IGMP查询时,接口不为该组生成IGMP报告。如果没有为某个组生成报告,则多播路由器就假定所有接口已经离开该组,并停止把该组的分组在网络上多播。

小结

    本文介绍了IGMP,IGMP在一个网络上的主机和路由器之间传递IP多播成员信息。当一个接口加入一个组或按照多播路由器发布的IGMP报告查询报文的要求时,生成IGMP成员关系报告。

    设计IGMP使交换成员信息所需要的报文数最少:

  • 当主机加入一个组时,宣布它们的成员关系;

  • 对成员关系查询的响应被推迟一个随机时间,而且第一个响应抵制了其它响应;

  • 当主机离开一个组时,不发通知报文;

  • 每分钟发的成员查询不超过一次。

    对于多播路由器与其它路由器共享自己收集的IGMP信息(原著第14章),以便把多播数据报传给多播目的组的远程成员。