D类IP地址(224.0.0.0到239.255.255.255)不识别互联网内的单个接口,但识别接口组,被称为多播组。
单个网络上的组成员利用IGMP协议在系统之间通信。多播路由器用多播选录协议,如DVMRP(distance vector multicast
routing protocol,距离向量多播路由选择协议)传播成员信息。
在Net/3中,如果某个接口支持多播,那么在接口ifnet结构中的if_flags的标识IFF_MULTICAST比特就被打开。
RFC 1112描述了多播对主机的要求,分三个级别:
0级:主机不能发送或接受IP。
折中主机应该自动丢弃它收到的具有D类目的地址的分组。
1级:主机能发送但不能接受IP多播。
在向某个IP多播组发送数据报之前,并不要求主机加入该组。多播数据报的发送方式与单播一样,除了多播数据报的目的地址
是IP多播组之外。网络驱动器必须能够识别出这个地址。
2级:主机能发送和接收IP多播
为了接收IP多播,主机必须能够加入或离开多播组,而且必须支持IGMP,能够在至少一个接口上交换组成员信息。多接口主机
必须支持在它的接口的一个子网上的多播,Net/3符合2级主机要求。可以完成多播路由器的工作。
和UDP、TCP的端口号一样,互联网授权机构IANA(Internet Assigned Numbers Authority)维护着一个注册的IP多播组表。下图
只给出了一些知名的多播组。
全256个组(224.0.0.0到224.0.0.255)是为实现IP单播和多播选路机制的协议预留的。不管发给其中任意一个组的数据报内IP
首部的TTL值如何变化,多播路由器都不会把它转发出本地网络。
对于符合2级的系统,要求其在系统初始化时,在所有的多播接口上加入224.0.0.1组,并且保持该组成员,直到系统关闭。
单播和多播路由可能会加入224.0.0.2组进行互相通信。ICMP路由器请求报文和路由器通告可能会被分别发往224.0.0.2(所有
路由器)和224.0.0.1(所有主机),而不是受限的广播地址(255.255.255.255).
224.0.0.4组支持在实现DVMRP的多播路由器之间的通信。
1.以太网多播地址
IP多播的高效实现要求IP充分利用硬件级多播,因为没有硬件级多播,就不得不在网络上广播每个多播IP数据报,而每台主机
也不得不检查每个数据报,把哪些不是给它的丢掉。硬件在数据报到达IP层之前,就把没有用的过滤掉了。
为了保证硬件过滤器能正常工作,网络接口必须把IP多播目的地址转换成网络硬件识别的链路级多播地址。在以太网,需要有
一个明确地完成映射地址的函数,以太网的标准映射适用于任何使用802.3寻址方式的网络。
因为以太网支持多种协议,所以要采取措施分配多播地址,避免冲突。IEEE管理以太网多播地址分配。IEEE把一块以太网多播
地址分给IANA以支持IP多播。块的地址都是以01:00:5e开头。以00:00:5e开头的以太网单播也被分配给IANA,但为将来
使用预留。下图显示了从一个D类IP地址构造出一个以太网多播地址。
2.ether_multi结构
Net/3为每个以太网接口维护一个该硬件接收的以太网多播地址范围表。这个表定义了该设备要实现的多播过滤。因为大多数
以太网设备能选择地接收的地址是有限的,所以IP层要准备丢弃那些通过了硬件过滤的数据报,地址范围保存在ether_multi
结构中。
enm_addrlo和enm_addrhi指定需要被接收的以太网多播地址的范围。当enm_addrlo和enm_addrhi相同时,就指定一个以太网
地址。ether_multi的完整列表附在每个以太网接口的arpcom结构中,下图表示有三个ether_multi结构的LANCE接口。
接口已经加入了三个组,可能是224.0.0.1(所有主机)、224.0.0.2(所有路由器)和224.0.1.2(SGI-dogfight)。因为以太网
到IP地址的映射是一对多的,所以无法确定确定的IP地址,接口也可能加入了225.0.0.1,225.0.0.2和225.0.1.2组。
3.以太网多播接收
在Net/3中,也有可能把系统配置成接收所有以太网分组,虽然对IP协议族没有用,但内核的其他协议族可能准备接收这些多播
分组。可以发出下图的ioctl命令,就可以明确地进行多播配置。
这两个命令直接被传给了接口的设备驱动程序(ifp->if_ioctl)
4.in_multi结构
以太网多播数据结构并不专用于IP,它们必须支持所有内核的任意协议族的多播活动。在网络级,IP维护者一个与接口相关
的IP多播组表。
为了实现方便,把这个IP多播表附在与该接口有关的in_ifaddr结构中。这个结构中包含了该接口的单播地址。除了它们都与
同一个接口相关以外,这个单播地址与所附的多播组表之间没有任何关系。
下图的in_multi结构描述了每个IP多播{接口,组}对。
下图用接口实例le_softc[0]显示了接口,即它的单播地址和它的IP多播表之前的关系。
图中省略了ether_multi结构。
5.ip_moptions结构
传输层通过ip_moptions结构包含的多播选项控制多播输出处理。对每个输出的数据报,都可以给ip_output传一个ip_moptions
结构。ip_moptions结构如下图所示:
imo_multicast_ifp指向的接口对输出的多播数据报进行选路,如果imo_multicast_ifp为空,就通过目的站多播组的默认接口。
imo_multicast_ttl为外出的多播数据报指定初始的IP TTL。默认为1.
如果imo_multicast_loop为0,就不回送数据报,也不把数据报提交给正在发送的接口,即使该接口是多播组的成员,如果
为1,并且如果正在发送的接口是多播组的成员,就把多播数据报回送给该接口。
最后,整数imo_num_memberships和数组imo_membership维护与该结构相关的{接口,组}对。所有对该表的改变都转告
给IP,由IP在所连到的本地网络上宣布成员的变化。imo_membership数组的每个入口都是指向一个in_multi结构的指针,
该in_multi结构附在适当接口的in_ifaddr结构上。
6.多播的插口选项
下图显示了几个IP级的插口选项,提供对ip_moptions结构的进程及访问。
所有的多播选项都由ip_setmoptions和ip_getmoptions函数处理,ip_setmoption主体是一个switch case,对每个选项
分别调用函数进行处理。
7.多播的TTL值
多播的TTL有两个作用,基本功能是如IP分组一样,限制分组在互联网内的生存期,避免在网络内部无线地循环。第二个
作用是,把分组限制在管理边界所指定的互联网的区域内。
多播路由器还给每个接口关联一个TTL阈值,限制在该接口上的多播传输。一个要在该接口上传输的分组必须具有大于或
等于该接口阈值的TTL。所以多播分组可能会在它的TTL到0之前就被丢弃了。
阈值时管理员在配置多播路由器时分配的,这些值确定了多播分组的辖域。下图显示了多种应用程序的推荐TTL值和推荐
的阈值。
8.加入一个IP多播组
除了内核自动加入的IP所有主机外,其他组成员是由进程明确发出请求产生的。加入(或离开)多播组选项比其他选项更多
使用。必须修改接口的in_multi表以及其他链路层多播结构,如ether_multi。
当optname是IP_ADDMEMBERSHIP时,mbuf中的数据如下图所示的ip_mreq:
ip_mreq结构指定{接口,组}对表示成员的变化。
下图显示了加入与离开我们的以太网接口例子相关的多播组时,所调用的函数。
对于IP_ADD_MEMBERSHIP选项,ip_setmoptions的大致处理流程如下:
1.验证。对传入的参数进行检查。
↓
2.找到接口。如果mreq中的imr_interface是INADDR_ANY,则必须找到指定组的默认接口,由rtalloc函数为多播组找到一个
路由器;如果imr_interface不是INADDR_ANY,则请求一个明确的接口,根据提供的地址搜索接口。
↓
3.已经是成员了?检查imo_membership数组,看看所选接口是否已经是请求组的成员。如果找到一个匹配,或者成员已满,
则终止对这个选项的处理。
↓
4.加入多播组。调用in_addrmulti函数加入多播组。
8.1.in_addrmulti函数
in_addrmulti和in_delmulti函数维护接口已加入多播组的表。加入请求或者在接口表中增加一个新的in_multi结构,或者增加
对某个已有结构的引用次数。(该函数是在协议层面上进行加入多播组的操作)
函数的大概处理流程如下:
1.如果已经是一个成员,则查找到该成员对应的in_multi结构,更新引用计数后返回。
↓
2.如果接口还不是成员,则in_addrmulti分配并初始化一个新的in_multi结构,并将该结构插到接口的in_ifaddr结构中ia_multiaddrs
表的前端。
↓
3.更新接口,通告变化。如果接口驱动程序已经定义了一个if_ioctl函数,则调用该函数。最后,in_addrmulti调用igmp_joingroup,
把成员变化信息传播给其他主机和路由器。
8.2.leioctl函数:SIOCADDMULTI和SIOCDELMULTI
对于LANCE以太网接口,if_ioctl函数指针指向leioctl函数。在leioctl函数中,对于SIOCADDMULTI和SIOCDELMULTI选项,
分别调用ether_addmulti函数和ether_delmulti函数。
8.3.ether_addmulti函数
ether_addmulti函数是在接口层对加入多播组进行操作。这个函数把IP D类地址映射到合适的以太网地址上,并更新ether_multi
表。函数的大概处理流程如下:
1.初始化地址范围。ether_addmulti初始化addrlo和addrhi中的多播地址范围。如果所请求的地址来自AF_UNSPEC族,
ether_addmulti假定该地址时一个明确的以太网多播地址,并把它复制到addrlo和addrhi中。如果地址属于AF_INET族,并且
是INADDR_ANY,ether_addmulti把addrlo初始化成ehter_ipmulticast_min,把addrhi初始化成ether_ipmulticast_max。
这两个以太地址常量定义为:
↓
2.已经在接收。ether_addmulti检查高地址和低地址的多播比特位,保证它们是真正的以太网多播地址。然后确定硬件是否已经
对指定的地址开始监听。如果是,则增加匹配ether_multi结构中的引用计数(enm_refcount)。
↓
3.更新ether_multi表。如果是一个新的地址范围,则分配并初始化一个新的ether_multi结构,把它链到接口arpcom结构中的
ac_multiaddrs表上,最后函数返回,根据返回值,设备驱动程序知道多播表被改变了,必须更新硬件接收过滤器。
下图显示了LANCE以太网接口加入主机组后,ip_moptions、in_multi和ether_multi结构之间的关系。
9.离开一个IP多播组
通常情况下,离开一个多播组的步骤时加入一个多播组的步骤的反序。更新ip_moptions结构中的成员表、IP接口的in_multi表
和设备的ether_multi表。
我们回到ip_setmoptions中的IP_DROP_MEMBERSHIP情况语句。语句中用请求的{接口,组}对组成员表中寻找一个in_multi
结构。如果找到,则调用in_delmulti更新in_multi表,然后调用igmp_leavegroup(不做任何操作),最后,调用if_ioctl函数指针指向
的ether_delmulti函数,删除找到的ether_multi结构。
《TCP/IP详解卷2:实现》笔记--IP多播,布布扣,bubuko.com