<?xml version="1.0" encoding="gb2312"?>
<rss version="2.0">
<channel>
<title><![CDATA[深度探索Linux内核]]></title>
        <image>
        <title>http://hi.baidu.com</title>
        <link>http://hi.baidu.com</link>
        <url>http://img.baidu.com/img/logo-hi.gif</url>
        </image>
<description><![CDATA[宠辱不惊，闲看庭前花开花落；去留无意，漫随天边云卷云舒……]]></description>
<link>http://hi.baidu.com/linux%5Fkernel</link>
<language>zh-cn</language>
<generator>www.baidu.com</generator>
<ttl>5</ttl>


<item>
        <title><![CDATA[相关书籍推荐(点击书名查看相关详细信息)]]></title>
        <link><![CDATA[http://hi.baidu.com/linux%5Fkernel/blog/item/4cb9b81237850bc9c2fd784e.html]]></link>
        <description><![CDATA[
		
		<p><a target="_blank" href="http://s.click.alimama.com/a/qXMULjPzJHE=-12478621">UNIX环境高级编程(英文版)</a></p>
<p><a target="_blank" href="http://s.click.alimama.com/a/qXMRFd+7K0E=-12478621">C语言经典入门第4版</a></p>
<p><a target="_blank" href="http://s.click.alimama.com/a/qXMREB0ER/w=-12478621"><font color="#000000">Linux经典入门★鸟哥的Linux私房菜基础学</font></a></p>
<p><a target="_blank" href="http://s.click.alimama.com/a/qXMVbzRuOkk=-12478621"><font color="#000000">TCP/IP 指南（卷2）： </font></a></p>
<p><a target="_blank" href="http://s.click.alimama.com/a/qXKeSh1y+pQ=-12478621"><font color="#000000">计算机网络（第四版）</font></a></p>
<p><a target="_blank" href="http://s.click.alimama.com/a/qXMVYAmTFe8=-12478621"><font color="#000000">计算机通信与网络技术(英文影印版) </font></a></p>
<p><a target="_blank" href="http://s.click.alimama.com/a/qve1E5yLBXg=-12478621"><font color="#000000">缓冲区溢出教程</font></a></p>
<p><a target="_blank" href="http://s.click.alimama.com/a/qXMcwViHoR0=-12478621"><font color="#000000">编程之美--微软技术面试心得</font></a></p>
<p><a target="_blank" href="http://s.click.alimama.com/a/qXKac1KmOHM=-12478621"><font color="#000000">.《网络安全原理与应用》电子教案</font></a></p>
<p><a target="_blank" href="http://s.click.alimama.com/a/qvYonrPFTkU=-12478621"><font color="#000000">C++Primer中文版 </font></a></p>
<p><a target="_blank" href="http://s.click.alimama.com/a/qvYskFXSQoA=-12478621"><font color="#000000">嵌入式开发详解 </font></a></p>
<p><a target="_blank" href="http://s.click.alimama.com/a/qXKac1KmMWo=-12478621"><font color="#000000">windows环境下32位汇编语言+精通破解工具</font></a></p>
<p><a target="_blank" href="http://s.click.alimama.com/a/qXKac1KmMoU=-12478621"><font color="#000000">Intel 汇编语言程序设计(第五版)</font></a></p> <a href="http://hi.baidu.com/linux%5Fkernel/blog/item/4cb9b81237850bc9c2fd784e.html">阅读全文</a>
		
		<br/><b>类别：</b><a href="http://hi.baidu.com/linux%5Fkernel/blog/category/%CA%E9%BC%AE%CD%C6%BC%F6">书籍推荐</a>&nbsp;<a href="http://hi.baidu.com/linux%5Fkernel/blog/item/4cb9b81237850bc9c2fd784e.html#comment">查看评论</a>]]></description>
        <pubDate>2009年02月03日 星期二  下午 02:48</pubDate>
        <category><![CDATA[书籍推荐]]></category>
        <author><![CDATA[LinqiangHe]]></author>
		<guid>http://hi.baidu.com/linux%5Fkernel/blog/item/4cb9b81237850bc9c2fd784e.html</guid>
</item>

<item>
        <title><![CDATA[关于博客暂停更新的声明]]></title>
        <link><![CDATA[http://hi.baidu.com/linux%5Fkernel/blog/item/3e83c75c7e26a242fbf2c0ad.html]]></link>
        <description><![CDATA[
		
		<p>&nbsp;&nbsp;&nbsp;&nbsp; 自今年年初起，由于一些个人原因，本博客暂停更新，目前还有UDP版、TCP版未发布，在一段时间后，会补充完整。具体时间未定。<br>
&nbsp;&nbsp;&nbsp;&nbsp; 如果现有的内容能够对你的工作，学习有所帮助，我会感到非常高兴。但由于技术的东西本身具有特殊性，我目前在暂时不关注这一块的情况下，很难一下子跟您进行深入的探讨，所以，所有关于技术咨询的来信，恕不回复，还望谅解。</p> 
		
		<br/><b>类别：</b><a href="http://hi.baidu.com/linux%5Fkernel/blog/category/%C4%AC%C8%CF%B7%D6%C0%E0">默认分类</a>&nbsp;<a href="http://hi.baidu.com/linux%5Fkernel/blog/item/3e83c75c7e26a242fbf2c0ad.html#comment">查看评论</a>]]></description>
        <pubDate>2007年06月11日 星期一  上午 09:10</pubDate>
        <category><![CDATA[默认分类]]></category>
        <author><![CDATA[LinqiangHe]]></author>
		<guid>http://hi.baidu.com/linux%5Fkernel/blog/item/3e83c75c7e26a242fbf2c0ad.html</guid>
</item>

<item>
        <title><![CDATA[发送一个UDP数据报]]></title>
        <link><![CDATA[http://hi.baidu.com/linux%5Fkernel/blog/item/dfae34fa638927889e51468a.html]]></link>
        <description><![CDATA[
		
		<p>&nbsp;&nbsp;&nbsp;&nbsp; 在Socket编程中，发送数据报文可供使用的API函数有send，sendto和sendmsg，下面是关于前两个系统调用的原型：<br>
 #include &lt;sys/socket.h&gt;<br>
 ssize_t send( int socket, const void *buffer, size_t length, int flags );<br>
&nbsp;&nbsp;&nbsp;&nbsp; 请注意它的返回值的类型ssize_t，其含义是signed size。从内核代码中，我们可以看到，在32位系统上，它是int，在64位系统上，它是long。它常用于表示在某一次操作后，缓冲区中可以被读或写的字节数量。相对应的，还有一个数据类型size_t，其含义是unsigned size。常用于表示对象本身的大小，操作sizeof的返回值就是该类型，malloc，memcpy等函数的参数中用该类型表示对象的大小，在32位系统上，它是unsigned int，在64位系统上，它是unsigned long。<br>
&nbsp;&nbsp;&nbsp;&nbsp; send执行成功，会返回被发送出去的数据报文的字节数，如果执行失败，则会返回-1(所以不能返回size_t类型)，并且可以从errno上查找到错误原因。<br>
 #include &lt;sys/socket.h&gt;<br>
 ssize_t sendto(int socket, const void *message, size_t length,<br>
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; int flags, const struct sockaddr *dest_addr,<br>
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; socklen_t dest_len);<br>
 在内核的实现中，send和sendto系统调用最终都会调用到内核函数：<br>
 asmlinkage long sys_sendto(int fd, void __user * buff, size_t len, unsigned flags,<br>
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; struct sockaddr __user *addr, int addr_len)<br>
&nbsp;&nbsp;&nbsp;&nbsp; 在send系统调用中，参数addr被置为NULL，addr_len为0。sys_sendto首先根据传入的描述符fd，找到对应的struct socket结构体。然后构建内核的消息结构struct msghdr:<br>
struct msghdr {<br>
&nbsp;&nbsp; void * msg_name;<br>
&nbsp;&nbsp; int&nbsp;&nbsp; msg_namelen;<br>
&nbsp;&nbsp; struct iovec * msg_iov;<br>
&nbsp;&nbsp; __kernel_size_t msg_iovlen;<br>
&nbsp;&nbsp; void&nbsp;&nbsp; * msg_control;<br>
&nbsp;&nbsp; __kernel_size_t msg_controllen;<br>
&nbsp;&nbsp; unsigned msg_flags;<br>
 };<br>
&nbsp;&nbsp;&nbsp;&nbsp; msg_name和msg_namelen就是数据报文要发向的对端的地址信息（即sendto系统调用中的addr和addr_len)。当使用send时，它们的值为NULL和0。msg_iov的定义如下：<br>
struct iovec<br>
{<br>
&nbsp;&nbsp; void __user *iov_base;<br>
&nbsp;&nbsp; __kernel_size_t iov_len;<br>
};<br>
 表示存放待发送数据的一个缓冲区，iov_base是缓冲区的起始地址，指向message, iov_len是缓冲区的长度，指向length。msg_iovlen是缓冲区的数量，对于sendto和send来讲，msg_iovlen都是1。msg_flags即为传入的参数flags，现在暂时不过多的关注flags的应用。msg_control和msg_controllen暂时不关注。<br>
&nbsp;&nbsp;&nbsp;&nbsp; sys_sendto构建完这些后，调用sock_sendmsg继续执行发送流程，传入参数为struct msghdr和数据的长度。忽略中间的一些不重要的细节，sock_sendmsg继续调用__sock_sendmsg，__sock_sendmsg最后调用struct socket-&gt;ops-&gt;sendmsg，即对应套接字类型的sendmsg函数，所有的套接字类型的sendmsg函数都是inet_sendmsg，该函数首先检查本地端口是否已绑定，无绑定则执行自动绑定，而后调用具体协议的sendmsg函数。<br>
&nbsp;&nbsp;&nbsp;&nbsp; 下面再来看sendmsg系统调用：<br>
 #include &lt;sys/socket.h&gt;<br>
 ssize_t sendmsg(int socket, const struct msghdr *message, int flags);<br>
&nbsp;&nbsp;&nbsp;&nbsp; 可以看到，它跟send和sendto的最大区别就是struc msghdr由用户来构建完成，对应的内核处理函数是sys_sendmsg。</p> <a href="http://hi.baidu.com/linux%5Fkernel/blog/item/dfae34fa638927889e51468a.html">阅读全文</a>
		
		<br/><b>类别：</b><a href="http://hi.baidu.com/linux%5Fkernel/blog/category/udp%D0%AD%D2%E9">udp协议</a>&nbsp;<a href="http://hi.baidu.com/linux%5Fkernel/blog/item/dfae34fa638927889e51468a.html#comment">查看评论</a>]]></description>
        <pubDate>2007年04月19日 星期四  下午 02:27</pubDate>
        <category><![CDATA[udp协议]]></category>
        <author><![CDATA[LinqiangHe]]></author>
		<guid>http://hi.baidu.com/linux%5Fkernel/blog/item/dfae34fa638927889e51468a.html</guid>
</item>

<item>
        <title><![CDATA[connect系统调用]]></title>
        <link><![CDATA[http://hi.baidu.com/linux%5Fkernel/blog/item/ba9c32fad482fe1fa8d311d7.html]]></link>
        <description><![CDATA[
		
		&nbsp;&nbsp;&nbsp;&nbsp; 下面是connect系统调用的函数原型：<br>
&nbsp;&nbsp;&nbsp;&nbsp; #include &lt;sys/types.h&gt;<br>
&nbsp;&nbsp;&nbsp;&nbsp; #include &lt;sys/socket.h&gt;<br>
&nbsp;&nbsp;&nbsp;&nbsp; int connect( int sockfd, const struct sockaddr *serv_addr, socklen_t addrlen );<br>
&nbsp;&nbsp;&nbsp;&nbsp; man手册里面这样描述该系统调用：connect()系统调用把由文件描述符sockfd所代表的套接字连接到serv_addr所指定的地址上，参数addrlen用于标明serv_addr的大小。如果sockfd是SOCK_DGRAM类型的套接字，那么serv_addr就是数据报文缺省传向的那个地址，同时，只有来自该地址的数据报文才能被<br>
该socket接收到。如果socket的类型是SOCK_STREAM或者SOCK_SEQPACKET，那么这个调用将试图同绑定在地址serv_addr的socket建立一个连接。一般来讲，面向连接协议的socket只可能一次成功调用connect，而无连接协议的socket可以多次使用connect改变它们的连接关系，同时，无连接协议的socket可以通过设置serv_addr的成员sa_family为AF_UNSPEC来解除已有的连接。<br>
&nbsp;&nbsp;&nbsp;&nbsp; 在TCP/IP的源代码实现中，SOCK_DGRAM套接字类型的connect函数是inet_dgram_connect。该函数首先检查connect系统调用的serv_addr参数，如果它的sa_family成员的值是AF_UNSPEC，则表示解除该socket已有的连接，直接调用具体协议的disconnect函数，UDP协议的disconnect函数是udp_disconnect，该函数所要做的工作主要是重置struct inet_sock(表示是INET域的socket)的几个成员，sk_state置TCP_CLOSE；daddr，dport置0，以清目的地址和端口；sk_bound_dev_if清零，表示清输入网络设备接口；然后选择性地清源地址和源端口。然后清目的入口(struct dst_entry)。否则，在判定本地端口已被绑定之后，执行协议的connect函数，udp协议的connect函数是ip4_datagram_connect。 <a href="http://hi.baidu.com/linux%5Fkernel/blog/item/ba9c32fad482fe1fa8d311d7.html">阅读全文</a>
		
		<br/><b>类别：</b><a href="http://hi.baidu.com/linux%5Fkernel/blog/category/udp%D0%AD%D2%E9">udp协议</a>&nbsp;<a href="http://hi.baidu.com/linux%5Fkernel/blog/item/ba9c32fad482fe1fa8d311d7.html#comment">查看评论</a>]]></description>
        <pubDate>2007年04月17日 星期二  下午 06:38</pubDate>
        <category><![CDATA[udp协议]]></category>
        <author><![CDATA[LinqiangHe]]></author>
		<guid>http://hi.baidu.com/linux%5Fkernel/blog/item/ba9c32fad482fe1fa8d311d7.html</guid>
</item>

<item>
        <title><![CDATA[自动分配UDP本地端口]]></title>
        <link><![CDATA[http://hi.baidu.com/linux%5Fkernel/blog/item/a0cd024f888bf337aec3ab42.html]]></link>
        <description><![CDATA[
		
		<p>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; 当建立一个UDP的socket用于网络通讯时，我们需要先为这个socket绑定一个本地端口号。因为端口在一台主机上是用于标识进程的，如果没有端口号，当收到来自对端主机的报文时，就不知道应该由哪一个进程来接收这个报文。但有时，我们建立UDP的sokcet以后，并不调用bind进行端口绑定，也能正常工作。这是因为协议栈对于没有进行端口绑定的socket进行了自动绑定。<br>
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; 在SOCK_DGRAM类型的套接字的操作函数集的sendmsg成员函数中，每次调用对应的第4层协议的sendmsg成员函数时，都会进行端口号的检查，如果没有绑定就调用协议的成员函数get_port进行自动绑定。代表INET域网络层套接字的结构体struct inet_sock有两个端口号相关的成员__u16 num和__u16 sport。它们都代表套接字的本地端口号。num是主机字节序，sport是网络字节序。当套接字类型为SOCK_RAW时，它们代表的是协议号(icmp,igmp等)。套接字层的sendmsg检查端口号绑定时，就是查看num是否为零。<br>
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; udp协议提供udp_v4_get_port函数用于自动获取本地端口号。端口号有一个固定的数值范围，自动获取必须在这个范围内进行。数组int sysctl_local_port_range[2]指定了本地端口号的范围。其默认值为1024到4999。对于高可用性系统，它的值应该是32768到61000(在TCP协议进行初始化时，会进行这项设置)。可通过修改文件/proc/sys/net/ipv4/ip_local_port_range的内容来修改这个范围。<br>
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; udp_hash是一个list数组，总共有128项，所有在协议栈中建立的udp socket全部以本地端口号为关键字被放入这个哈希数组中，全局变量udp_port_rover记录了最近一次被分配的端口号。寻找一个新的可用的端口，总是从udp_port_rover开始找，检查udp_hash[udp_port_rover &amp; (UDP_HTABLE_SIZE - 1)]的list是否为空，如果为空，则取udp_port_rover为新的端口，如果不为空，则记录下这个list的size，同时保存下该端口号，然后遍历整个数组，找到size最小的一个list，取对应的端口号为我们所要获得的端口。然后，检查这个新获得的端口号是否已经被使用（同样，通过检查udp_hash实现)。如果已在使用中，则把端口号加上UDP_HTABLE_SIZE(128)再检查。直至获得未使用的端口号。</p> <a href="http://hi.baidu.com/linux%5Fkernel/blog/item/a0cd024f888bf337aec3ab42.html">阅读全文</a>
		
		<br/><b>类别：</b><a href="http://hi.baidu.com/linux%5Fkernel/blog/category/udp%D0%AD%D2%E9">udp协议</a>&nbsp;<a href="http://hi.baidu.com/linux%5Fkernel/blog/item/a0cd024f888bf337aec3ab42.html#comment">查看评论</a>]]></description>
        <pubDate>2007年03月14日 星期三  上午 08:22</pubDate>
        <category><![CDATA[udp协议]]></category>
        <author><![CDATA[LinqiangHe]]></author>
		<guid>http://hi.baidu.com/linux%5Fkernel/blog/item/a0cd024f888bf337aec3ab42.html</guid>
</item>

<item>
        <title><![CDATA[回顾]]></title>
        <link><![CDATA[http://hi.baidu.com/linux%5Fkernel/blog/item/b108cc11bca1b110b9127bb0.html]]></link>
        <description><![CDATA[
		
		<p>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;首先以socket和send两个系统调用为例，来回顾一下协议栈是如何工作的，在这过程中可以找到如何在协议栈中增加对UDP协议的支持。socket系统调用的原型是<br>
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;int&nbsp;socket(int&nbsp;domain,&nbsp;int&nbsp;type,&nbsp;int&nbsp;protocol);<br>
domain是协议域，对于ipv4协议来说，其值是PF_INET（ipv4因特网协议），对于我们自己实现的ipv4协议模块，我们为其新增MY_PF_INET。所有的协议域在include/linux/socket.h被定义，如下：<br>
#define&nbsp;AF_UNSPEC&nbsp;0<br>
#define&nbsp;AF_UNIX&nbsp;&nbsp;1&nbsp;&nbsp;//&nbsp;Unix域的socket<br>
#define&nbsp;AF_LOCAL&nbsp;1&nbsp;&nbsp;//&nbsp;AF_UNIX的POSIX命名<br>
#define&nbsp;AF_INET&nbsp;&nbsp;2&nbsp;&nbsp;//&nbsp;因特网IP协议<br>
#define&nbsp;AF_AX25&nbsp;&nbsp;3&nbsp;&nbsp;//&nbsp;Amateur&nbsp;Radio&nbsp;AX.25<br>
#define&nbsp;AF_IPX&nbsp;&nbsp;4&nbsp;&nbsp;//&nbsp;Novell&nbsp;IPX<br>
#define&nbsp;AF_APPLETALK&nbsp;5&nbsp;//&nbsp;AppleTalk&nbsp;DDP<br>
#define&nbsp;AF_NETROM&nbsp;6&nbsp;&nbsp;&nbsp;//&nbsp;Amateur&nbsp;Radio&nbsp;NET/ROM<br>
#define&nbsp;AF_BRIDGE&nbsp;7&nbsp;&nbsp;&nbsp;//&nbsp;Multiprotocol&nbsp;bridge<br>
#define&nbsp;AF_ATMPVC&nbsp;8&nbsp;&nbsp;&nbsp;//&nbsp;ATM&nbsp;PVCs<br>
#define&nbsp;AF_X25&nbsp;&nbsp;9&nbsp;&nbsp;&nbsp;//&nbsp;Reserved&nbsp;for&nbsp;X.25&nbsp;project<br>
#define&nbsp;AF_INET6&nbsp;10&nbsp;&nbsp;//&nbsp;IP&nbsp;version&nbsp;6<br>
#define&nbsp;AF_ROSE&nbsp;&nbsp;11&nbsp;&nbsp;//&nbsp;Amateur&nbsp;Radio&nbsp;X.25&nbsp;PLP<br>
#define&nbsp;AF_DECnet&nbsp;12&nbsp;&nbsp;//&nbsp;Reserved&nbsp;for&nbsp;DECnet&nbsp;project<br>
#define&nbsp;AF_NETBEUI&nbsp;13&nbsp;//&nbsp;Reserved&nbsp;for&nbsp;802.2LLC&nbsp;project<br>
#define&nbsp;AF_SECURITY&nbsp;14&nbsp;//&nbsp;Security&nbsp;callback&nbsp;pseudo&nbsp;AF<br>
#define&nbsp;AF_KEY&nbsp;&nbsp;15&nbsp;&nbsp;//&nbsp;PF_KEY&nbsp;key&nbsp;management&nbsp;API<br>
#define&nbsp;AF_NETLINK&nbsp;16<br>
#define&nbsp;AF_ROUTE&nbsp;AF_NETLINK&nbsp;//&nbsp;Alias&nbsp;to&nbsp;emulate&nbsp;4.4BSD<br>
#define&nbsp;AF_PACKET&nbsp;17&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;//&nbsp;Packet&nbsp;family<br>
#define&nbsp;AF_ASH&nbsp;&nbsp;18&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;//&nbsp;Ash<br>
#define&nbsp;AF_ECONET&nbsp;19&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;//&nbsp;Acorn&nbsp;Econet<br>
#define&nbsp;AF_ATMSVC&nbsp;20&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;//&nbsp;ATM&nbsp;SVCs<br>
#define&nbsp;AF_SNA&nbsp;&nbsp;22&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;//&nbsp;Linux&nbsp;SNA&nbsp;Project&nbsp;(nutters!)<br>
#define&nbsp;AF_IRDA&nbsp;&nbsp;23&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;//&nbsp;IRDA&nbsp;sockets<br>
#define&nbsp;AF_PPPOX&nbsp;24&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;//&nbsp;PPPoX&nbsp;sockets<br>
#define&nbsp;AF_WANPIPE&nbsp;25&nbsp;&nbsp;&nbsp;&nbsp;//&nbsp;Wanpipe&nbsp;API&nbsp;Sockets<br>
#define&nbsp;AF_LLC&nbsp;&nbsp;26&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;//&nbsp;Linux&nbsp;LLC<br>
#define&nbsp;AF_BLUETOOTH&nbsp;31&nbsp;&nbsp;&nbsp;//&nbsp;Bluetooth&nbsp;sockets<br>
#define&nbsp;AF_MAX&nbsp;&nbsp;32&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;//&nbsp;For&nbsp;now..<br>
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;可以看到，当前，内核最多支持31个协议域（0为未指定，32为MAX）。而当前的定义中还有27,28,30为空，所以我们定义了MY_PF_INET为28。<br>
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;在内核中，结构体struct&nbsp;net_proto_family用于表示一个协议域，而全局数组变量static&nbsp;struct&nbsp;net_proto_family&nbsp;*net_families[NPROTO]是一个有32项的数组，用于保存当前内核中所有已注册的协议域，函数sock_register用于把一个协议域注册到内核中，即把一个协议域跟net_families数组<br>
中的某一项相关联。struct&nbsp;net_proto_family的完整定义如下：<br>
struct&nbsp;net_proto_family&nbsp;{<br>
&nbsp;int&nbsp;&nbsp;family;<br>
&nbsp;int&nbsp;&nbsp;(*create)(struct&nbsp;socket&nbsp;*sock,&nbsp;int&nbsp;protocol);<br>
&nbsp;short&nbsp;&nbsp;authentication;<br>
&nbsp;short&nbsp;&nbsp;encryption;<br>
&nbsp;short&nbsp;&nbsp;encrypt_net;<br>
&nbsp;struct&nbsp;module&nbsp;*owner;<br>
};<br>
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;其中，family为域编号，对于我们的模块即为MY_PF_INET。通过sock_register函数，使net_families[MY_PF_INET]指向需要注册的域。create是该域的socket的创建函数，我们的MY_PF_INET域定义如下：<br>
static&nbsp;struct&nbsp;net_proto_family&nbsp;myinet_family_ops&nbsp;=&nbsp;{<br>
&nbsp;&nbsp;&nbsp;&nbsp;.family&nbsp;=&nbsp;MY_PF_INET,<br>
&nbsp;&nbsp;&nbsp;&nbsp;.create&nbsp;=&nbsp;myinet_create,<br>
&nbsp;&nbsp;&nbsp;&nbsp;.owner&nbsp;&nbsp;=&nbsp;THIS_MODULE,<br>
};<br>
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;现在回到socket系统调用上来，内核实现socket系统调用的函数是sys_socket。该函数通过调用sock_create进行创建，sock_create调用__sock_create。__sock_create要创建一个struct&nbsp;socket，这是一个普通BSD&nbsp;socket的结构体，其定义如下：<br>
struct&nbsp;socket&nbsp;{<br>
&nbsp;socket_state&nbsp;&nbsp;state;<br>
&nbsp;unsigned&nbsp;long&nbsp;&nbsp;flags;<br>
&nbsp;struct&nbsp;proto_ops&nbsp;*ops;<br>
&nbsp;struct&nbsp;fasync_struct&nbsp;*fasync_list;<br>
&nbsp;struct&nbsp;file&nbsp;&nbsp;*file;<br>
&nbsp;struct&nbsp;sock&nbsp;&nbsp;*sk;<br>
&nbsp;wait_queue_head_t&nbsp;wait;<br>
&nbsp;short&nbsp;&nbsp;&nbsp;type;<br>
};<br>
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;__sock_create创建的时候，为其type赋上socket系统调用的第二个参数type，最后通过调用net_families[family]-&gt;create(sock,&nbsp;protocol)完成socket的创建。对于MY_PF_INET域来说，该create函数即myinet_create。MY_PF_INET域支持的网络层协议是IP协议，在该协议上支持的套接字接口有流套接字(SOCK_STREAM)，数据报套接字(SOCK_DGRAM)和原始套接字(SOCK_RAW)。在IP协议上注册一个套接字接口，也即创建一个套接字，需要知道该类型的套接字必需的一些相关信息。结构体struct&nbsp;inet_protosw就是用于在IP协议上注册套接字接口，其完整定义如下：<br>
struct&nbsp;inet_protosw&nbsp;{<br>
&nbsp;struct&nbsp;list_head&nbsp;list;<br>
&nbsp;unsigned&nbsp;short&nbsp;&nbsp;type;&nbsp;&nbsp;&nbsp;&nbsp;//套接字类型，即socket系统调用的第二个参数。<br>
&nbsp;int&nbsp;&nbsp;&nbsp;protocol;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;//第4层（传输层)协议号<br>
&nbsp;struct&nbsp;proto&nbsp;&nbsp;*prot;&nbsp;&nbsp;&nbsp;&nbsp;//第4层协议的操作函数集<br>
&nbsp;struct&nbsp;proto_ops&nbsp;*ops;&nbsp;&nbsp;&nbsp;//该类型的套接字的操作函数集<br>
&nbsp;int&nbsp;capability;<br>
&nbsp;char&nbsp;no_check;<br>
&nbsp;unsigned&nbsp;char&nbsp;&nbsp;flags;<br>
};<br>
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;myinet_create函数注册套接字的过程本质上就是为指定套接字类型和第4层协议号的一个socket找到对应的操作函数集，使这个socket随后能真正被操作。全局数组inetsw_array包含了系统当前支持的所有在IP协议上能够注册的套接字接口，在系统初始化的时候，这些结构体以type作为依据，被组织到<br>
static&nbsp;struct&nbsp;list_head&nbsp;inetsw[SOCK_MAX]中。当在inetsw数组中找到对应的socket类型和第4层协议号后，令struct&nbsp;socket-&gt;ops的值为struct&nbsp;inet_protosw-&gt;ops，即为该类型的套接字指定操作函数集。而struct&nbsp;socket-&gt;sk是网络层的套接字接口，其成员sk_prot的值为struct&nbsp;inet_protosw-&gt;prot，即为该类型的第4层协议指定操作函数集。套接字的创建工作大致如此。<br>
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;接下来，再来看send系统调用，它的原型如下：<br>
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;ssize_t&nbsp;send(int&nbsp;s,&nbsp;const&nbsp;void&nbsp;*buf,&nbsp;size_t&nbsp;len,&nbsp;int&nbsp;flags);<br>
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;s是文件描述符，在内核中跟一个struct&nbsp;socket结构体建立一一对应的映射关系。buf和len分别为待发送数据的内容和长度，flag是一些标志位。内核实现该系统调用的函数是sys_send。sys_send直接调用sys_sendto，把sys_sendto的最后两个参数addr和addr_len置空。sys_sendto根据文件描述符s找到对应的struct&nbsp;socket，然后建立一个结构体struct&nbsp;msghdr&nbsp;msg用于发送数据内容，该结构体的定义如下：<br>
struct&nbsp;msghdr&nbsp;{<br>
&nbsp;void&nbsp;*&nbsp;msg_name;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;/*&nbsp;Socket&nbsp;的名字&nbsp;*/<br>
&nbsp;int&nbsp;&nbsp;msg_namelen;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;/*&nbsp;名字的长度&nbsp;&nbsp;*/<br>
&nbsp;struct&nbsp;iovec&nbsp;*&nbsp;msg_iov;&nbsp;&nbsp;/*&nbsp;数据块&nbsp;&nbsp;&nbsp;*/<br>
&nbsp;__kernel_size_t&nbsp;msg_iovlen;&nbsp;/*&nbsp;数据块的数量&nbsp;&nbsp;*/<br>
&nbsp;void&nbsp;&nbsp;*&nbsp;msg_control;&nbsp;&nbsp;&nbsp;&nbsp;/*&nbsp;Per&nbsp;protocol&nbsp;magic&nbsp;(eg&nbsp;BSD&nbsp;file&nbsp;descriptor&nbsp;passing)&nbsp;*/<br>
&nbsp;__kernel_size_t&nbsp;msg_controllen;&nbsp;/*&nbsp;Length&nbsp;of&nbsp;cmsg&nbsp;list&nbsp;*/<br>
&nbsp;unsigned&nbsp;msg_flags;<br>
};<br>
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;然后，sys_sendto调用sock_sendmsg发送数据，sock_sendmsg调用__sock_sendmsg，__sock_sendmsg调用struct&nbsp;socket-&gt;ops-&gt;sendmsg，即调用特定套接字类型的操作函数集中的sendmsg成员函数。比如，SOCK_RAW类型的套接字的sendmsg成员函数的实现如下（实际上SOCK_DGRAM类型的套接字的sendmsg成员函数也是这个)：<br>
int&nbsp;inet_sendmsg(struct&nbsp;kiocb&nbsp;*iocb,&nbsp;struct&nbsp;socket&nbsp;*sock,&nbsp;<br>
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;struct&nbsp;msghdr&nbsp;*msg,&nbsp;size_t&nbsp;size)<br>
{<br>
&nbsp;&nbsp;&nbsp;&nbsp;struct&nbsp;sock&nbsp;*sk&nbsp;=&nbsp;sock-&gt;sk;<br>
&nbsp;&nbsp;&nbsp;&nbsp;if&nbsp;(!inet_sk(sk)-&gt;num&nbsp;&amp;&amp;&nbsp;inet_autobind(sk))<br>
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;return&nbsp;-EAGAIN;<br>
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;return&nbsp;sk-&gt;sk_prot-&gt;sendmsg(iocb,&nbsp;sk,&nbsp;msg,&nbsp;size);<br>
}<br>
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;可以看到，在该函数中，调用了具体的第4层协议的操作函数集中的sendmsg成员函数，而该函数真正实现了对应协议的数据报文发送工作。</p> <a href="http://hi.baidu.com/linux%5Fkernel/blog/item/b108cc11bca1b110b9127bb0.html">阅读全文</a>
		
		<br/><b>类别：</b><a href="http://hi.baidu.com/linux%5Fkernel/blog/category/socket%BC%B0%C6%E4%B1%E0%B3%CC">socket及其编程</a>&nbsp;<a href="http://hi.baidu.com/linux%5Fkernel/blog/item/b108cc11bca1b110b9127bb0.html#comment">查看评论</a>]]></description>
        <pubDate>2007年03月07日 星期三  上午 07:50</pubDate>
        <category><![CDATA[socket及其编程]]></category>
        <author><![CDATA[LinqiangHe]]></author>
		<guid>http://hi.baidu.com/linux%5Fkernel/blog/item/b108cc11bca1b110b9127bb0.html</guid>
</item>

<item>
        <title><![CDATA[TCP/IP module for linux 2.6 ip版]]></title>
        <link><![CDATA[http://hi.baidu.com/linux%5Fkernel/blog/item/dc836159e60f582a2834f0a8.html]]></link>
        <description><![CDATA[
		
		基本实现到网络层为止的所有功能，距上一版本后，主要增加对原始套接字(SOCK_RAW)的支持，IGMP和多播功能的支持，FIB也已经基本补充完整。还缺少一些不常用的功能，在后续的实现传输层协议的过程中，会根据需要加上。<br>
&nbsp;&nbsp;&nbsp; &nbsp;&nbsp;  <a href="http://www.linuxeden.com/forum/showthread.php?p=567196&amp;posted=1#post567196" target="_blank">点击跳转到下载页面</a> 
		
		<br/><b>类别：</b><a href="http://hi.baidu.com/linux%5Fkernel/blog/category/%D4%B4%B4%FA%C2%EB%B7%A2%B2%BC">源代码发布</a>&nbsp;<a href="http://hi.baidu.com/linux%5Fkernel/blog/item/dc836159e60f582a2834f0a8.html#comment">查看评论</a>]]></description>
        <pubDate>2007年01月06日 星期六  下午 05:42</pubDate>
        <category><![CDATA[源代码发布]]></category>
        <author><![CDATA[LinqiangHe]]></author>
		<guid>http://hi.baidu.com/linux%5Fkernel/blog/item/dc836159e60f582a2834f0a8.html</guid>
</item>

<item>
        <title><![CDATA[离开一个多播组(最简单的情况)]]></title>
        <link><![CDATA[http://hi.baidu.com/linux%5Fkernel/blog/item/9a26a1ecae3cd93d27979114.html]]></link>
        <description><![CDATA[
		
		&nbsp;&nbsp;&nbsp; 应用程序通过命令字IP_DROP_MEMBERSHIP使一个socket离开一个多播组，IP_DROP_MEMBERSHIP是一个IP层的命令字，其调用使用的参数是结构体struct ip_mreq。该结构体的两个成员分别用于指定所要离开的多播组的组IP地址，和所要离开组的那个本地接口的IP地址。<br>
&nbsp;&nbsp;&nbsp; ip_setsockopt实现了该命令字，它通过调用ip_mc_leave_group使socket离开一个多播组。<br>
&nbsp;&nbsp;&nbsp; 表示INET域socket的结构体struct inet_sock的成员mc_list是该socket所加入的所有多播组的一个链表，ip_mc_leave_group从该链表中去匹配所要离开的组(匹配条件是组地址相同，本地网络接口相同)，找到一个结构体struct ip_mc_socklist节点。<br>
&nbsp;&nbsp;&nbsp; 首先要从该节点中删去源列表，同时把该节点从socket中删除，因为我们现在所关注的IP_DROP_MEMBERSHIP所要离开的组是通过IP_ADD_MEMBERSHIP加入的，没有源过滤机制，只要把节点从socket中删除即可。<br>
&nbsp;&nbsp;&nbsp; 然后，还需要从网络接口struct in_device中删掉这个组，以使网络硬件设备以后可以过滤掉发往这个组的数据报。首先从struct in_device的成员mc_list链表中去匹配一个struct ip_mc_list结构体节点(匹配条件是组地址相同即可)。<br>
&nbsp;&nbsp;&nbsp; 如果节点的成员loaded不为0，表示硬件过滤机制正接收该组的数据报。则调用ip_mc_filter_del从网络设备接口的结构体struct net_device的成员mc_list链表中删去相应的节点struct dev_mc_list，并通过调用网络设备接口的成员函数set_multicast_list来更新设备的过滤机制。<br>
&nbsp;&nbsp;&nbsp; 如果所要离开的组不是IGMP_ALL_HOSTS(224.0.0.1)，则首先停止节点本身的定时器(用于IGMPv1和IGMPv2的主动报告)，然后把要删除的节点从struct in_device的mc_list链表移到mc_tomb链表上，表示这是将要删除的一个组，在稍后发送状态改变报告时有用。<br>
&nbsp;&nbsp;&nbsp; 最后启用定时器struct in_device-&gt;mr_ifc_timer(接口状态改变定时器)，发送主动成员报告。测试环境中要离开的多播组中是224.0.1.1，发出的IGMPv3报告如下：<br>
&nbsp;&nbsp;&nbsp; 数据&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; 含义<br>
&nbsp;&nbsp;&nbsp; 22&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; 第3版成员关系报告<br>
&nbsp;&nbsp;&nbsp; 00&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; 8bit保留，必须为0<br>
&nbsp;&nbsp;&nbsp; f9 fc&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; 校验和<br>
&nbsp;&nbsp;&nbsp; 00 00&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; 16bit保留，必须为0<br>
&nbsp;&nbsp;&nbsp; 00 01&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; 组记录的数量，为1<br>
&nbsp;&nbsp;&nbsp; 下面为一条组记录：<br>
&nbsp;&nbsp;&nbsp; 03&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; 组记录类型为CHANGE_TO_INCLUDE_MODE，改变到INCLUDE模式<br>
&nbsp;&nbsp;&nbsp; 00&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; 辅助数据长度<br>
&nbsp;&nbsp;&nbsp; 00 00&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; 源地址的数量<br>
&nbsp;&nbsp;&nbsp; e0 00 01 01&nbsp;&nbsp;&nbsp; 组地址224.0.1.1<br>
&nbsp;&nbsp;&nbsp; 即：组224.0.1.1的过滤模式从原来的EXCLUDE(过滤的源为空)变到现在的INCLUDE(包含的源为空)，也就是离开了多播组224.0.1.1。 <a href="http://hi.baidu.com/linux%5Fkernel/blog/item/9a26a1ecae3cd93d27979114.html">阅读全文</a>
		
		<br/><b>类别：</b><a href="http://hi.baidu.com/linux%5Fkernel/blog/category/%D7%E9%B2%A5%D3%EBigmp%D0%AD%D2%E9">组播与igmp协议</a>&nbsp;<a href="http://hi.baidu.com/linux%5Fkernel/blog/item/9a26a1ecae3cd93d27979114.html#comment">查看评论</a>]]></description>
        <pubDate>2007年01月03日 星期三  下午 10:37</pubDate>
        <category><![CDATA[组播与igmp协议]]></category>
        <author><![CDATA[LinqiangHe]]></author>
		<guid>http://hi.baidu.com/linux%5Fkernel/blog/item/9a26a1ecae3cd93d27979114.html</guid>
</item>

<item>
        <title><![CDATA[各位，新年快乐！祝大家前程似锦！]]></title>
        <link><![CDATA[http://hi.baidu.com/linux%5Fkernel/blog/item/f7aa93459e00503f86947355.html]]></link>
        <description><![CDATA[
		
		&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; 跌跌撞撞把TCP/IP协议栈的实现搞了一年，其间得到了很多朋友的帮助和支持，在这里衷心地感谢大家。12月由于家里有些事情，以至于未能在月底如期完成IP完整版，目前还差IGMP有部分未完成，看来只能等2007年了。<br>
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; 希望在新的一年里，我们能一如既往的互相帮助，互相支持，共同提高，谢谢大家。 
		
		<br/><b>类别：</b><a href="http://hi.baidu.com/linux%5Fkernel/blog/category/%C4%AC%C8%CF%B7%D6%C0%E0">默认分类</a>&nbsp;<a href="http://hi.baidu.com/linux%5Fkernel/blog/item/f7aa93459e00503f86947355.html#comment">查看评论</a>]]></description>
        <pubDate>2006年12月31日 星期日  下午 06:10</pubDate>
        <category><![CDATA[默认分类]]></category>
        <author><![CDATA[LinqiangHe]]></author>
		<guid>http://hi.baidu.com/linux%5Fkernel/blog/item/f7aa93459e00503f86947355.html</guid>
</item>

<item>
        <title><![CDATA[加入一个多播组(最简单的情况)]]></title>
        <link><![CDATA[http://hi.baidu.com/linux%5Fkernel/blog/item/fc9faec30a628e55b319a899.html]]></link>
        <description><![CDATA[
		
		&nbsp;&nbsp;&nbsp; 应用程序通过命令字IP_ADD_MEMBERSHIP把一个socket加入到一个多播组，IP_ADD_MEMBERSHIP是一个IP层的命令字，其调用使用的参数是结构体struct ip_mreq，其定义如下：<br>
&nbsp;&nbsp;&nbsp; struct ip_mreq<br>
&nbsp;&nbsp;&nbsp; {<br>
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; struct in_addr imr_multiaddr;<br>
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; struct in_addr imr_interface;<br>
&nbsp;&nbsp;&nbsp; };<br>
&nbsp;&nbsp;&nbsp; 该结构体的两个成员分别用于指定所加入的多播组的组IP地址，和所要加入组的那个本地接口的IP地址。该命令字没有源过滤的功能，它相当于实现IGMPv1的多播加入服务接口。<br>
&nbsp;&nbsp;&nbsp; ip_setsockopt实现了该命令字，它通过调用ip_mc_join_group把socket加入到多播组。<br>
&nbsp;&nbsp;&nbsp; 表示socket的结构体struct inet_sock有一个成员mc_list，它是一个结构体struct ip_mc_socklist的指针，实际上一个该结构体的链表，该结构体的定义如下：<br>
&nbsp;&nbsp;&nbsp; struct ip_mc_socklist<br>
&nbsp;&nbsp;&nbsp; {<br>
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; struct ip_mc_socklist&nbsp;&nbsp; *next;<br>
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; struct ip_mreqn&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; multi;<br>
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; unsigned int&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; sfmode;<br>
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; struct ip_sf_socklist&nbsp;&nbsp; *sflist;<br>
&nbsp;&nbsp;&nbsp; };<br>
&nbsp;&nbsp;&nbsp; next指向链表的下一个节点；multi表示组信息，即在哪一个本地接口上，加入到哪一个多播组；sfmode是过滤模式，取值为MCAST_INCLUDE或MCAST_EXCLUDE，分别表示只接收sflist所列出的那些源的多播数据报，和不接收sflist所列出的那些源的多播数据报；sflist是源列表，结构体struct ip_sf_socklist的定义如下：<br>
&nbsp;&nbsp;&nbsp; struct ip_sf_socklist<br>
&nbsp;&nbsp;&nbsp; {<br>
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; unsigned int&nbsp;&nbsp;&nbsp; sl_max;<br>
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; unsigned int&nbsp;&nbsp;&nbsp; sl_count;<br>
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; __u32&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; sl_addr[0];<br>
&nbsp;&nbsp;&nbsp; };<br>
&nbsp;&nbsp;&nbsp; sl_addr是源地址列表，sl_count应该是源地址列表中源地址的数量，sl_max应该是当前sl_addr数组的最大可容纳量(不确定)。对于通过调用IP_ADD_MEMBERSHIP加入的多播组，它会在struct inet_sock的mc_list的链表头添加如下一个节点：<br>
&nbsp;&nbsp;&nbsp; struct ip_mc_socklist{<br>
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; .next = 原来的链表头;<br>
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; .multi = 所加入的多播组，和接口信息;<br>
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; .sfmode = MCAST_EXCLUDE;<br>
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; .sflist = NULL;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; 即不排除任何源地址，也就是不存在源过滤。<br>
&nbsp;&nbsp;&nbsp; }<br>
&nbsp;&nbsp;&nbsp; 另外，一个socket所允许加入的多播组的最大数量也是有限制的，mc_list中节点的数量不允许超过sysctl_igmp_max_memberships(缺省为20)。<br>
&nbsp;&nbsp;&nbsp; ip_mc_join_group还需要通过ip_mreq.imr_interface的指定值找到要加入多播组的那个接口，并为接口设置状态(即该接口要加入哪个多播组，过滤哪些源，也就是为该接口增加一个组，如果要增加的组已存在，则增加该组的引用计数)。代表网络设备接口的结构体struct in_device有一个成员mc_list，这是一个结构体struct ip_mc_list的链表，该结构体的定义如下：<br>
&nbsp;&nbsp;&nbsp; struct ip_mc_list<br>
&nbsp;&nbsp;&nbsp; {<br>
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; struct in_device&nbsp;&nbsp;&nbsp; *interface;<br>
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; unsigned long&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; multiaddr;<br>
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; struct ip_sf_list&nbsp;&nbsp; *sources;<br>
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; struct ip_sf_list&nbsp;&nbsp; *tomb;<br>
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; unsigned int&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; sfmode;<br>
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; unsigned long&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; sfcount[2];<br>
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; struct ip_mc_list&nbsp;&nbsp; *next;<br>
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; struct timer_list&nbsp;&nbsp; timer;<br>
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; int&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; users;<br>
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; atomic_t&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; refcnt;<br>
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; spinlock_t&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; lock;<br>
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; char&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; tm_running;<br>
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; char&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; reporter;<br>
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; char&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; unsolicit_count;<br>
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; char&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; loaded;<br>
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; unsigned char&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; gsquery;<br>
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; unsigned char&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; crcount;<br>
&nbsp;&nbsp;&nbsp; };<br>
&nbsp;&nbsp;&nbsp; interface指向网络设备接口，multicast即为加入的组的多播地址，users记录当前有几个socket在该接口上加入了该多播组。sfcount是一个有两个元素的数组，分别记录在该接口上加入多播组的socket的过滤模式为EXCLUDE和INCLUDE的数量，sfmode为该接口本身的过滤模式。sources为源地址列表，该结构体具体内容稍后再分析。timer为主动报告定时器，当一个接口(注意：不是socket)新加入到一个多播组，需要向多播路由器发送一个igmp报告，以通知多播路由器需要向本地网络转发该组的数据报。tm_running是一个标志，如果timer当前正在运行，则置1，否则置0。reporter也是一个标志，如果当前正要开始发送igmp报告，则置该标志为1，否则为0。unsolicit_count是当一个接口新加入到一个多播组时，发送主动报告的次数，值赋为IGMP_Unsolicited_Report_Count(缺省值为2)。loaded也是一个标志，当该接口上的该多播组被加入时，需要通知硬件过滤器，通知完成即置该标志为1，否则为0。<br>
&nbsp;&nbsp;&nbsp; 该结构体比较复杂，先看通过IP_ADD_MEMBERSHIP命令字把一个socket加入到一个新的多播组，会使struct in_device的mc_list中增加一个什么样的节点。下面是生成的节点的情况：<br>
&nbsp;&nbsp;&nbsp; struct ip_mc_list{<br>
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; .interface = in_dev;<br>
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; .multiaddr = 多播组地址;<br>
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; .source = NULL;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; //源过滤列表为空。<br>
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; .tomb = NULL;<br>
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; .sfmode = MCAST_EXCLUDE;&nbsp;&nbsp;&nbsp; //EXCLUDE模式，即不过滤任何源。<br>
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; .sfcount[MCAST_EXCLUDE] = 1;<br>
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; .sfcount[MCAST_INCLUDE] = 0;//即该节点上该多播组有一个socket加入，过滤模式为EXCLUDE。<br>
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; .users = 1;&nbsp; //有一个用户。<br>
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; .refcnt = 1; //引用计数为1<br>
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; .tm_running = 0;<br>
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; .unsolicit_count = 2;<br>
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; ... ...<br>
&nbsp;&nbsp;&nbsp; }<br>
&nbsp;&nbsp;&nbsp; 新生成的节点加入到mc_list链表中后，要通知网络设备接口的硬件，以使它的过滤机制可以接收进该多播组的数据报，同时也要通知多播路由器。<br>
&nbsp;&nbsp;&nbsp; 首先要把多播地址映射成以太网地址，映射规则是把多播IP地址的低23位放到以太网多播地址01-00-5E-00-00-00(16进制)的低23位。因为一个IP组地址有28位有效位(除去高位的1110)，所以有可能出现多个组地址被映射成同一个以太网多播地址，具体实现见ip_eth_mc_map。然后把这个mac地址加到硬件的过滤机制中。<br>
&nbsp;&nbsp;&nbsp; 具体的实现在函数dev_mc_add中。代表网络设备接口的结构体struct net_device也有一个成员mc_list，它是一个结构体struct dev_mc_list的链表，该结构体的定义如下：<br>
&nbsp;&nbsp;&nbsp; struct dev_mc_list<br>
&nbsp;&nbsp;&nbsp; {<br>
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; struct dev_mc_list&nbsp; *next;<br>
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; __u8&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; dmi_addr[MAX_ADDR_LEN];<br>
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; unsigned char&nbsp;&nbsp; dmi_addrlen;<br>
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; int&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; dmi_users;<br>
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; int&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; dmi_gusers;<br>
&nbsp;&nbsp;&nbsp; };<br>
&nbsp;&nbsp;&nbsp; next指向链表下一个节点，dmi_addr是多播mac地址，dmi_addrlen为多播mac地址的长度，dmi_users是在节点被重复到加入到设备上的次数，struct net_device还有一个成员mc_count，用于记录链表中节点的数量。dev_mc_add创建一个新的struct dev_mc_list节点，加入到链表中，并通过调用网络设备接口的成员函数set_multicast_list来启用设备的过滤机制。<br>
&nbsp;&nbsp;&nbsp; 最后一步发送主动成员报告，这里，首先忽略IGMPv1和IGMPv2存在的情况。如果要加入的多播组是IGMP_ALL_HOSTS(224.0.0.1)，则不需要发送成员报告。否则启用定时器struct in_device-&gt;mr_ifc_timer(接口状态改变定时器)，该定时器在设备初始化的时候被建立，其超时处理函数是igmp_ifc_timer_expire，它发送一个IGMPv3的报告，然后再次启用定时器。也就是说，第一个主动成员报告立即发出，然后在一个0到IGMP_Unsolicited_Report_Interval(缺省为10秒)之间的一个时间后，发出第二个主动成员报告，连续发出IGMP_Unsolicited_Report_Count(缺省值为2)个。<br>
&nbsp;&nbsp;&nbsp; 测试环境中要加入的多播组是224.0.1.1，发出的IGMPv3报告如下：<br>
&nbsp;&nbsp;&nbsp; 数据&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; 含义<br>
&nbsp;&nbsp;&nbsp; 22&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; 第3版成员关系报告<br>
&nbsp;&nbsp;&nbsp; 00&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; 8bit保留，必须为0<br>
&nbsp;&nbsp;&nbsp; f8 fc&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; 校验和<br>
&nbsp;&nbsp;&nbsp; 00 00&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; 16bit保留，必须为0<br>
&nbsp;&nbsp;&nbsp; 00 01&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; 组记录的数量，为1<br>
&nbsp;&nbsp;&nbsp; 下面为一条组记录：<br>
&nbsp;&nbsp;&nbsp; 04&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; 类型为CHANGE_TO_EXCLUDE_MODE，改变到EXCLUDE过滤模式<br>
&nbsp;&nbsp;&nbsp; 00&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; 辅助数据长度<br>
&nbsp;&nbsp;&nbsp; 00 00&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; 源地址的数量<br>
&nbsp;&nbsp;&nbsp; e0 00 01 01&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; 组地址224.0.1.1 <a href="http://hi.baidu.com/linux%5Fkernel/blog/item/fc9faec30a628e55b319a899.html">阅读全文</a>
		
		<br/><b>类别：</b><a href="http://hi.baidu.com/linux%5Fkernel/blog/category/%D7%E9%B2%A5%D3%EBigmp%D0%AD%D2%E9">组播与igmp协议</a>&nbsp;<a href="http://hi.baidu.com/linux%5Fkernel/blog/item/fc9faec30a628e55b319a899.html#comment">查看评论</a>]]></description>
        <pubDate>2006年12月31日 星期日  下午 03:41</pubDate>
        <category><![CDATA[组播与igmp协议]]></category>
        <author><![CDATA[LinqiangHe]]></author>
		<guid>http://hi.baidu.com/linux%5Fkernel/blog/item/fc9faec30a628e55b319a899.html</guid>
</item>

<item>
        <title><![CDATA[RFC3376 因特网组管理协议 第3版续(译)]]></title>
        <link><![CDATA[http://hi.baidu.com/linux%5Fkernel/blog/item/d6fd574ebb1da809b3de058a.html]]></link>
        <description><![CDATA[
		
		5、组成员协议的描述<br>
&nbsp;&nbsp;&nbsp; IGMP是一个非对称的协议，分别指定组成员(即那些希望接收多播数据报的主机和路由器)和多播路由器的行为。本节主要描述IGMPv3运用于所有组成员 的那部分(需要注意的是，一个同时是组成员的多播路由器会同时执行IGMPv3的两部分，对来自自己的和邻居的IGMP消息，它都会接收和报告。 IGMPv3的多播路由器部分在第6节描述)。<br>
&nbsp;&nbsp;&nbsp; 一个系统在所有支持多播接收的接口上执行本节所描述的协议，即使有多个接口连接在同一网络上也是如此。<br>
&nbsp;&nbsp;&nbsp; 为了能跟运行旧版本IGMP协议的多播路由器互操作，系统在支持多播接收的每一个接口上维护了一个MulticastRouterVersion变量。本节描述在MulticastRouterVersion=3的接口上组成员系统的行为。关于确定MulticastRouterVersion变量的值的算法和非第3版的行为，在第7节描述。<br>
&nbsp;&nbsp;&nbsp; &ldquo;所有系统&rdquo;多播地址224.0.0.1作为一个特殊的情况处理。所有的系统(包括主机和路由器，多播路由器)接收发往&ldquo;所有系统&rdquo;多播地址的来自所有源的数据报。关于这一点，在所有支持多播的接口上都是永远被打开着的。<br>
&nbsp;&nbsp;&nbsp; 有两种类型的事件可以触发接口上的IGMPv3协议的行为：<br>
&nbsp;&nbsp;&nbsp; -由本地的IPMulticastListen调用引起的接口接收状态的修改。<br>
&nbsp;&nbsp;&nbsp; -收到一个查询。<br>
&nbsp;&nbsp;&nbsp; (除了要跟早期版本的IGMP进行互操作的请求，接收到的非查询类型的IGMP消息都会被忽略)。<br>
&nbsp;&nbsp;&nbsp; 接下来的章节描述这两种情况下所发生的行为。在这些描述中，定时器和计数器的名字出现在方括号中，这些定时器和计数器的缺省值将在第8节描述。<br>
5.1、接口状态改变时发生的行为<br>
&nbsp;&nbsp;&nbsp; 一个IPMulticastListen调用会引起接口上的多播接收状态的改变，改变规则见3.2节。每一个这样的改变只作用在单个接口上的单个多播地址。<br>
&nbsp;&nbsp;&nbsp; 接口状态的改变引起系统立即从该接口发送一个状态改变报告。该报告的组记录的类型和内容取决于针对该IP多播地址的改变前后，过滤模式和源列表的比较。如果改变前，对于该多播地址不存在接口状态(即这个改变是创建一个新的接口记录)，或者改变后，不存在接口状态(即该改变是删除一个接口记录)。那么就涉及 到一个&ldquo;不存在&rdquo;状态，该状态被看作拥有一个INCLUDE过滤模式和一个空的源列表。<br>
&nbsp;&nbsp;&nbsp; 旧的状态&nbsp;&nbsp;&nbsp; &nbsp;&nbsp;&nbsp; &nbsp;&nbsp;&nbsp;&nbsp; 新的状态&nbsp;&nbsp;&nbsp; &nbsp;&nbsp;&nbsp; &nbsp;&nbsp;&nbsp; &nbsp;&nbsp;&nbsp; 发送的状态改变记录<br>
&nbsp;&nbsp;&nbsp; INCLUDE(A)&nbsp;&nbsp;&nbsp; &nbsp;&nbsp;&nbsp; INCLUDE(B)&nbsp;&nbsp;&nbsp; &nbsp;&nbsp;&nbsp;&nbsp;&nbsp; ALLOW(B-A)&nbsp;&nbsp;&nbsp; BLOCK(A-B)<br>
&nbsp;&nbsp;&nbsp; EXLUCDE(A)&nbsp;&nbsp;&nbsp; &nbsp;&nbsp;&nbsp; EXCLUDE(B)&nbsp;&nbsp;&nbsp; &nbsp;&nbsp;&nbsp; ALLOW(A-B)&nbsp;&nbsp;&nbsp; BLOCK(B-A)<br>
&nbsp;&nbsp;&nbsp; INCLUDE(A)&nbsp;&nbsp;&nbsp; &nbsp;&nbsp;&nbsp; EXCLUDE(B)&nbsp;&nbsp;&nbsp; &nbsp;&nbsp;&nbsp;&nbsp; TO_EX(B)<br>
&nbsp;&nbsp;&nbsp; EXCLUDE(A)&nbsp;&nbsp;&nbsp; &nbsp;&nbsp;&nbsp; INCLUDE(B)&nbsp;&nbsp;&nbsp; &nbsp;&nbsp;&nbsp; TO_IN(B)<br>
&nbsp;&nbsp;&nbsp; 如果计算得到的源列表中，ALLOW状态改变记录为空，或者BLOCK为空，那么在报告消息中这个记录就被省略。<br>
&nbsp;&nbsp;&nbsp; 考虑到状态改变报告消息可能错过一个或多个路由器的可能性，它会被重传[健壮性变量]-1次，时间间隔是从范围(0，[主动报告间隔])中随机选取的一个值。<br>
&nbsp;&nbsp;&nbsp; 如果前一个改变的状态改变报告还没有被重传完，同一个接口上就发生了新的改变。那么这个新的改变会造成立即对新的状态改变报告进行重传。<br>
&nbsp;&nbsp;&nbsp; 新传送的报告的内容通过如下的方法计算得到：第一个报告完成后，相关的组在上一次改变前和改变后的接口状态被进行比较。然后根据上述的表格，报告的记录中表达了两者的不同。但是这些记录不在一条消息中进行传输，而是跟上一次未传送完的的报告的内容合在一起，产生一条新的状态改变报告。把状态改变报告和未传送完的报告进行合并，产生差异报告的规则描述如下。<br>
&nbsp;&nbsp;&nbsp; 合并的状态改变报告在被传输之前，同一个多播地址上的前一个状态改变报告会被中止，然后，新的状态改变报告进行[健壮性变量]次数的重传。<br>
&nbsp;&nbsp;&nbsp; 每一次被包含到差异报告中的一个源以上述的方法计算得到，对于该源的重传状态需要被维护到主机对状态改变报告已经传送了[健壮性变量]次数。这是为了保证接下来的一系例状态改变不会破坏协议的健壮性。<br>
&nbsp;&nbsp;&nbsp; 如果接口的接收状态改变，并触发了新的过滤模式改变的状态报告，那么，下一轮[健壮性变量]次数的状态改变报告会包含过滤模式改变记录。如果在此期间，有 任何数量的源列表改变发生，该规则都适用。主机必须为该组维护重传状态，直到有[健壮性变量]次数的状态改变报告被发送出去了。当在上一次过滤模式改变之 后，已经有[健壮性变量]次数的含有过滤模式改变记录的状态报告被发送出去了，并且接口接收的源列表改变已经启动了下一轮的报告，那么下一轮的状态改变报 告只含有源列表改变记录。<br>
&nbsp;&nbsp;&nbsp; 每一次被传送的状态改变报告，其内容以如下的方式确定：如果报告应当含有一个过滤模式改变记录，并且接口的当前过滤模式是INCLUDE，那么报告中就会含有一个TO_IN记录，否则就会含有一个TO_EX记录。或者，如果报告中应当含有源列表改变记录，那就会有一个ALLOW和一个BLOCK记录。这些记录的内容依据下列的表格建立：<br>
&nbsp;&nbsp;&nbsp; 记录&nbsp;&nbsp;&nbsp; &nbsp;&nbsp;&nbsp; &nbsp;&nbsp;&nbsp; 包含的源<br>
&nbsp;&nbsp;&nbsp; TO_IN&nbsp;&nbsp;&nbsp; &nbsp;&nbsp;&nbsp; &nbsp;&nbsp;&nbsp; 所有在当前接口状态中必须被转发的<br>
&nbsp;&nbsp;&nbsp; TO_EX&nbsp;&nbsp;&nbsp; &nbsp;&nbsp;&nbsp; &nbsp;&nbsp;&nbsp; 所有在当前接口状态中必须被阻止的<br>
&nbsp;&nbsp;&nbsp; ALLOW&nbsp;&nbsp;&nbsp; &nbsp;&nbsp;&nbsp; &nbsp;&nbsp;&nbsp; 所有在重传状态中必须被转发的<br>
&nbsp;&nbsp;&nbsp; BLOCK&nbsp;&nbsp;&nbsp; &nbsp;&nbsp;&nbsp; &nbsp;&nbsp;&nbsp; 所有在重传状态中必须被阻止的<br>
&nbsp;&nbsp;&nbsp; 如果计算出的ALLOW的源列表，或者BLOCK的源列表是空的，在状态报告中的记录就会被省略。<br>
&nbsp;&nbsp;&nbsp; 注意：当第一个状态改变报告被发出去，会合并进一个不存在的未完成的状态报告。可以被看作是一个拥有空的ALLOW或者BLOCK记录的源改变报告(这种重传状态下没有源)。<br>
5.2、接收到一个查询时的行为<br>
&nbsp;&nbsp;&nbsp; 当系统接收到一个查询，它不会立即响应。它会把这个响应延迟一个随机时间，这个时间就在接收到的查询消息的最大响应代码中计算得到的最大响应时间的范围 内。一个系统可能在不同的接口上接收到多个查询，并且是不同类型的(比如普通查询，指定组查询，指定组和源的查询)，每一个查询都需要相应的延迟响应。<br>
&nbsp;&nbsp;&nbsp; 在调度一个查询的响应之前，系统必须考虑到前一次调度的还未完成的响应，大多数情况下，会重新调度一个组合的响应。因此，系统必须能够维护下列的状态：<br>
&nbsp;&nbsp;&nbsp; -每一个接口上都有一个定时器，用于调度普通查询的响应。<br>
&nbsp;&nbsp;&nbsp; -每一个接口和特定组相关的定时器，用于调度对指定组查询和指定组和源的查询的响应。<br>
&nbsp;&nbsp;&nbsp; -每一个接口和特定组相关的源列表，被放置在指定组和源查询的响应中。<br>
&nbsp;&nbsp;&nbsp; 当一个带有路由器警告选项的查询到达接口上时，那么系统的状态就是要报告，报告的延迟时间从范围(0，[最大响应时间])中获取，这里，最大响应时间是从接收到的查询中的最大响应代码计算得到的。下面的规则用于确定一个报告是否需要被调度以及被调度的报告的类型。这些规则依次被考虑，当发现第一个匹配规则 时，即被应用：<br>
&nbsp;&nbsp;&nbsp; 1、如果还存在对前一个普通查询的未完成响应，它在被选择的延迟之前被调度，就不需要再调度额外的响应。<br>
&nbsp;&nbsp;&nbsp; 2、如果收到的查询是一个普通查询，接口的定时器在选定的延迟之后，必须调度一个普通查询的响应。<br>
&nbsp;&nbsp;&nbsp; 3、如果收到的查询是一个指定组查询，或者是一个指定组和源的查询，并且不存在该组的前一个查询的未完成的响应，那么组定时器就用于调度一个报告。如果收到的查询是一个指定组和源的查询，那么被查询的源列表必须被记录下来，用于生成响应。<br>
&nbsp;&nbsp;&nbsp; 4、如果对该组的前一个查询已经存在未完成的响应，并且新的查询是一个指定组的查询，或者已经被记录下来的该组的源列表是空的，那么这个组的源列表被清空，并使用组定时器调度单个的响应。新的响应在未完成报告的剩余时间和选定的延迟两个时间中的较早那个时间被发送出去。<br>
&nbsp;&nbsp;&nbsp; 5、如果收到的查询是一个指定组和源的查询，并且该组存在一个拥有非空源列表的未完成响应。那么组的源列表被增长以含有新的查询的源列表，并且使用组的定时器调度一个单个的响应。新的响应在未完成报告的剩余时间和选定的延迟两个时间中的较早的那个时间被发送出去。<br>
&nbsp;&nbsp;&nbsp; 当未完成报告相关的定时器超时，系统在相关的接口上传送一个或多个报告消息，消息中含有一个或多个当前状态记录，如下：<br>
&nbsp;&nbsp;&nbsp; 1、如果超时的定时器是接口定时器(即它是一个响应普通查询的报告)，那么为指定的拥有接收状态的接口上的每一个多播地址发送一个当前状态记录。当前状态记录含有该多播地址和相关的过滤模式(MODE_IS_INCLUDE或者MODE_IS_EXCLUDE)，以及源列表。多个当前状态记录被尽可能地打 到一个报告消息中。<br>
&nbsp;&nbsp;&nbsp; 当系统是大量的组的成员的时候，不成熟的算法会导致数据报的爆炸。不要使用单个的定时器，建议实现把这样的报告消息的传输分散到间隔(0，[最大响应时 间])中。需要注意的是，任何这样的实现，必须避免&ldquo;ask-implosion&rdquo;问题，即不能收到一个普通查询后立即发送报告。<br>
&nbsp;&nbsp;&nbsp; 2、如果超时的定时器是组定时器，并且该组的记录下来的源列表是空的(即它是一个未完成的指定组的查询的响应)，那么，当且仅当接口对该组地址有一个接收 状态时，为该地址发送一个当前状态记录。当前状态记录含有该多播地址，它相关的过滤模式(MODE_IS_INCLUDE或者 MODE_IS_EXCLUDE)以及源列表。<br>
&nbsp;&nbsp;&nbsp; 3、如果超时的定时器是组定时器，并且该组的记录下来的源列表是非空的(即它是一个未完成的指定组和源的查询的响应)，那么，当且仅当接口对该组地址有一个接收状态时，用于响应的当前状态记录的内容由接口的状态和未完成响应记录来决定，如下表所述：<br>
&nbsp;&nbsp;&nbsp; 接口状态&nbsp;&nbsp;&nbsp; &nbsp;&nbsp;&nbsp; 未完成响应记录中的源集&nbsp;&nbsp;&nbsp; &nbsp;&nbsp;&nbsp; 当前状态记录<br>
&nbsp;&nbsp;&nbsp; INCLUDE(A)&nbsp;&nbsp;&nbsp; &nbsp;&nbsp;&nbsp; B&nbsp;&nbsp;&nbsp; &nbsp;&nbsp;&nbsp; &nbsp;&nbsp;&nbsp; &nbsp;&nbsp;&nbsp; &nbsp;&nbsp;&nbsp; &nbsp;&nbsp;&nbsp; &nbsp;&nbsp;&nbsp; IS_IN(A*B)<br>
&nbsp;&nbsp;&nbsp; EXCLUDE(A)&nbsp;&nbsp;&nbsp; &nbsp;&nbsp;&nbsp; B&nbsp;&nbsp;&nbsp; &nbsp;&nbsp;&nbsp; &nbsp;&nbsp;&nbsp; &nbsp;&nbsp;&nbsp; &nbsp;&nbsp;&nbsp; &nbsp;&nbsp;&nbsp; &nbsp;&nbsp;&nbsp; IS_IN(B-A)<br>
&nbsp;&nbsp;&nbsp; 如果结果的当前状态记录有一个空的源地址集，就不会发送响应。<br>
&nbsp;&nbsp;&nbsp; 最后，在需要的报告消息被产生以后，跟所有被报告组相关的源列表都会被清除。<br>
&nbsp;&nbsp;&nbsp; <br>
6、多播路由器的协议描述<br>
&nbsp;&nbsp;&nbsp; 由于当前重点关注主机的实现，该部分略。<br>
<br>
7、跟旧版本的IGMP之间的互操作<br>
&nbsp;&nbsp;&nbsp; IGMPv3的主机和路由器跟未升级到IGMPv3的主机和路由器要能互操作。这种兼容性由主机和路由器根据同一网络中其它主机和路由器的IGMP版本采取相应的行为来完成。<br>
7.1、查询版本的差异<br>
&nbsp;&nbsp;&nbsp; 成员关系查询消息的IGMP版本以如下方式确定：<br>
&nbsp;&nbsp;&nbsp; IGMPv1查询：长度=8字节，最大响应代码字段为0。<br>
&nbsp;&nbsp;&nbsp; IGMPv2查询：长度=8字节，最大响应代码字段非0。<br>
&nbsp;&nbsp;&nbsp; IGMPv3查询：长度&gt;12字节。<br>
&nbsp;&nbsp;&nbsp; 不符合上述情况的查询消息(比如说一个10字节长的查询)必须被丢弃。<br>
7.2、组成员行为<br>
7.2.1、当有旧版本查询者存在的时候<br>
&nbsp;&nbsp;&nbsp; 为了跟旧版本的路由器兼容，IGMPv3的主机必须能够在第1版和第2版兼容模式下操作。IGMPv3主机必须为每一个本地接口维护一个跟相连网络的兼容 模式相关的状态。一台主机的兼容模式由主机的兼容模式变量来决定。兼容模式变量有三种可能的状态：IGMPv1，IGMPv2和IGMPv3，该变量在每 一个接口上都维护一个，其值取决于接口接收到的普通查询的版本，还有接口上的旧版本的查询者存在定时器。<br>
&nbsp;&nbsp;&nbsp; 为了能更优雅地在IGMP各版本间进行切换，主机在每一个接口上维护了一个IGMPv1查询者存在定时器和一个IGMPv2查询者存在定时器。任何时候， 当一个IGMPv1成员关系查询被收到时，IGMPv1查询者存在定时器被设置成旧版本查询者存在超时的时间。当一个IGMPv2普通查询被收到后， IGMPv2查询者存在定时器被设置成旧版本查询者存在超时的时间。<br>
&nbsp;&nbsp;&nbsp; 当一个更旧版本的查询被收到(比当前的兼容模式还旧)，或者特定的定时器条件发生时，主机某个接口上的兼容模式就会发生变化。当一个IGMPv1查询者存 在定时器超时，如果当前还有一个IGMPv2查询者存在定时器在运行，那主机就切换到IGMPv2的兼容模式。如果当前没有IGMPv2查询者存在定时器 在运行，主机就直接切换到IGMPv3兼容模式。当IGMPv2查询者存在定时器超时后，主机直接切换到IGMPv3兼容模式。<br>
&nbsp;&nbsp;&nbsp; 主机的兼容模式变量的取值取决于在前一个旧版本查询者存在超时时间内，是否收到旧版本的普通查询。主机的兼容模式的确定如下：<br>
&nbsp;&nbsp;&nbsp; 主机兼容模式&nbsp;&nbsp;&nbsp; &nbsp;&nbsp;&nbsp; &nbsp;&nbsp;&nbsp; 定时器状态<br>
&nbsp;&nbsp;&nbsp; IGMPv3(缺省)&nbsp;&nbsp;&nbsp; &nbsp;&nbsp;&nbsp; &nbsp;&nbsp;&nbsp; IGMPv2查询者存在没有在运行<br>
&nbsp;&nbsp;&nbsp; &nbsp;&nbsp;&nbsp; &nbsp;&nbsp;&nbsp; &nbsp;&nbsp;&nbsp; &nbsp;&nbsp;&nbsp; &nbsp;&nbsp;&nbsp; &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; IGMPv1查询者存在也没有在运行<br>
&nbsp;&nbsp;&nbsp; IGMPv2&nbsp;&nbsp;&nbsp; &nbsp;&nbsp;&nbsp; &nbsp;&nbsp;&nbsp; &nbsp;&nbsp;&nbsp; &nbsp;&nbsp;&nbsp; IGMPv2查询者存在正在运行<br>
&nbsp;&nbsp;&nbsp; &nbsp;&nbsp;&nbsp; &nbsp;&nbsp;&nbsp; &nbsp;&nbsp;&nbsp; &nbsp;&nbsp;&nbsp; &nbsp;&nbsp;&nbsp; &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; IGMPv1查询者存在不在运行<br>
&nbsp;&nbsp;&nbsp; IGMPv1&nbsp;&nbsp;&nbsp; &nbsp;&nbsp;&nbsp; &nbsp;&nbsp;&nbsp; &nbsp;&nbsp;&nbsp; &nbsp;&nbsp;&nbsp; IGMPv1查询者存在正在运行<br>
&nbsp;&nbsp;&nbsp; 当主机收到一个查询，该查询会造成它的查询者存在定时器被更新，并且需要相应地调整它的兼容模式，它应当立即调整兼容模式。<br>
&nbsp;&nbsp;&nbsp; 当主机的兼容模式是IGMPv3，主机就在该接口上使用IGMPv3协议。当主机的兼容模式是IGMPv2，主机就在该接口上使用IGMPv2协议。当主机的兼容模式是IGMPv1，主机就在该接口上使用IGMPv1协议。<br>
&nbsp;&nbsp;&nbsp; 一台IGMPv1路由器会发送最大响应代码为0的普通查询，这时，最大响应时间必须被解释为100(10秒)。<br>
&nbsp;&nbsp;&nbsp; 一台IGMPv2路由器发送的普通查询，其最大响应代码被解释为最大响应时间。即，该字段的全范围段是线性的，不存在指数表示法。<br>
&nbsp;&nbsp;&nbsp; 任何时候主机改变了它的兼容模式，会结束掉所有的未完成的响应和重传定时器。<br>
7.2.2、旧版本的组成员的存在<br>
&nbsp;&nbsp;&nbsp; 一台IGMPv3的主机可能被放置在某一个网络中，该网络中还有主机未升级到IGMPv3。一台主机的IGMPv3成员关系记录要可以被一个第1版成员报告或者第2版成员报告禁止。<br>
7.3、多播路由器的行为<br>
7.3.1、存在旧版本的查询者<br>
&nbsp;&nbsp;&nbsp; IGMPv3路由器可能被放置在一个网络中，该网络中至少还有一台路由器没有被升级到IGMPv3。这就产生了以下需求：<br>
&nbsp;&nbsp;&nbsp; -如果路由器上存在更旧版本的IGMP，查询者必须使用网络中最低版本的IGMP。这必须通过管理系统来确保；希望跟IGMPv1和IGMPv2兼容的路 由器必须有一个配置选项以运行在IGMPv1或IGMPv2兼容模式下。当在IGMPv1模式下，路由器必须周期性地出最大响应代码为0的查询，并且查询 会在组地址字段被截取掉(即8字节长度)，而且必须忽略离开组消息。它们应当在收到IGMPv2和IGMPv3查询时发出警告，但是这样的警告必须受到一 定的频率限制。如果运行在IGMPv2模式，路由器必须周期性地发出查询，该查询也要在组地址字段被截取(即8字节长度)，在收到IGMPv3查询时，也 要发出警告(这样的警告的频率必须受到限制)。它还要在最大响应代码字段填入最大响应时间。<br>
&nbsp;&nbsp;&nbsp; -如果路由器没有被显示地配置为使用IGMPv1或IGMPv2，并接收到了一个IGMPv1或IGMPv2的普通查询，它应当写下一个警告日志，这种警告必须受到频率限制。<br>
7.3.2、存在旧版本的组成员<br>
&nbsp;&nbsp;&nbsp; IGMPv3路由器可能被放置在一个网络中，该网络中还有主机没有被升级到IGMPv3。为了跟旧版本的主机兼容，IGMPv3路由器必须在IGMPv1 或IGMPv2兼容模式下运行。IGMPv3为每一个组记录维护一个兼容模式。组的兼容模式由组的兼容模式变量来决定，该变量可能是下列中个值中的一个： IGMPv1,IGMPv2和IGMPv3。每条组记录都有这么一个变量，它的值取决于该组所接收到的成员报告的版本，以及该组的旧版本主机存在定时器。<br>
&nbsp;&nbsp;&nbsp; 为了更优雅地在不同版本的IGMP间进行切换，路由器为每一个组维护了一个IGMPv1主机存在定时器和一个IGMPv2主机存在定时器。当收到一个 IGMPv1成员关系报告时，IGMPv1主机存在定时器会被设置为旧版本主机存在超时时间。当收到一个IGMPv2成员关系报告时，IGMPv2主机存 在定时器会被设置为旧版本主机存在超时时间。<br>
&nbsp;&nbsp;&nbsp; 当收到一个更旧版本的报告(比当前兼容模式的版本更旧),或者特定的定时器事件发生，组记录的组兼容模式就要发生改变。当IGMPv1主机存在定时器超 时，如果当前正在运行IGMPv2主机存在定时器就切换到IGMPv2的兼容模式。如果没有IGMPv2主机存在定时器在运行，就直接切换到IGMPv3 兼容模式。当一个IGMPv2主机存在定时器超时并且IGMPv1主机存在定时器不在运行，就切换到IGMPv3兼容模式。需要注意的是，当一个组切换回 IGMPv3模式，它需要一些时间去重新获取指定源的状态信息。指定源的信息通过下一个普通查询获取，但是应当被阻止的源还没有被阻止，直到下一个[组成 员关系间隔]后。<br>
&nbsp;&nbsp;&nbsp; 组兼容模式变量的值取决于在上一个旧版本主机存在超时时间内，是否收到一个更旧版本的报告。组兼容模式的设置依据下列规则：<br>
&nbsp;&nbsp;&nbsp; 组兼容模式&nbsp;&nbsp;&nbsp; &nbsp;&nbsp;&nbsp; &nbsp;&nbsp;&nbsp; 定时器状态<br>
&nbsp;&nbsp;&nbsp; IGMPv3(缺省)&nbsp;&nbsp;&nbsp; &nbsp;&nbsp;&nbsp; IGMPv2主机存在没有运行 <br>
&nbsp;&nbsp;&nbsp; &nbsp;&nbsp;&nbsp; &nbsp;&nbsp;&nbsp; &nbsp;&nbsp;&nbsp; &nbsp;&nbsp;&nbsp; &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; IGMPv1主机存在也没有运行<br>
&nbsp;&nbsp;&nbsp; IGMPv2&nbsp;&nbsp;&nbsp; &nbsp;&nbsp;&nbsp; &nbsp;&nbsp;&nbsp; &nbsp;&nbsp;&nbsp;&nbsp; IGMPv2主机存在正运行<br>
&nbsp;&nbsp;&nbsp; &nbsp;&nbsp;&nbsp; &nbsp;&nbsp;&nbsp; &nbsp;&nbsp;&nbsp; &nbsp;&nbsp;&nbsp; &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; IGMPv1主机存在没有运行 <br>
&nbsp;&nbsp;&nbsp; IGMPv1&nbsp;&nbsp;&nbsp; &nbsp;&nbsp;&nbsp; &nbsp;&nbsp;&nbsp; &nbsp;&nbsp;&nbsp; IGMPv1主机存在正运行<br>
&nbsp;&nbsp;&nbsp; 如果一台路由器收到一个报告，该报告造成该路由器的旧版本主机存在定时器被更新并且其兼容模式也要发生相应的变化，它应当立即切换其兼容模式。<br>
&nbsp;&nbsp;&nbsp; 当组兼容模式是IGMPv3，路由器为该组使用IGMPv3协议。当组兼容模式是IGMPv2，路由器在内部把下面的IGMPv2消息转化为它们等价的IGMPv3模式：<br>
&nbsp;&nbsp;&nbsp; IGMPv2消息&nbsp;&nbsp;&nbsp; &nbsp;&nbsp;&nbsp; &nbsp;&nbsp;&nbsp; IGMPv3等价形式<br>
&nbsp;&nbsp;&nbsp; 报告&nbsp;&nbsp;&nbsp; &nbsp;&nbsp;&nbsp; &nbsp;&nbsp;&nbsp; &nbsp;&nbsp;&nbsp; IS_EX({})<br>
&nbsp;&nbsp;&nbsp; 离开&nbsp;&nbsp;&nbsp; &nbsp;&nbsp;&nbsp; &nbsp;&nbsp;&nbsp; &nbsp;&nbsp;&nbsp; TO_IN({})<br>
&nbsp;&nbsp;&nbsp; IGMPv3的阻止消息被忽略，在TO_EX()消息中，源列表也被忽略，即任何TO_EX()消息被处理为TO_EX({})。<br>
&nbsp;&nbsp;&nbsp; 当组兼容模式是IGMPv1，路由器在内部把下面的该组的IGMPv1，IGMPv2消息转化为它们的等价的IGMPv3形式：<br>
&nbsp;&nbsp;&nbsp; IGMP消息&nbsp;&nbsp;&nbsp; &nbsp;&nbsp;&nbsp; IGMPv3等价形式<br>
&nbsp;&nbsp;&nbsp; v1报告&nbsp;&nbsp;&nbsp; &nbsp;&nbsp;&nbsp; &nbsp;&nbsp;&nbsp; IS_EX({})<br>
&nbsp;&nbsp;&nbsp; v2报告&nbsp;&nbsp;&nbsp; &nbsp;&nbsp;&nbsp; &nbsp;&nbsp;&nbsp; IS_EX({})<br>
&nbsp;&nbsp;&nbsp; 除了忽略IGMPv3阻止消息，和IGMPv2兼容模式下的TO_EX()消息的源列表，IGMPv2离开消息，IGMPv3的TO_IN消息也要被忽略。<br>
<br>
8、定时器，计数器列表和它们的缺省值<br>
&nbsp;&nbsp;&nbsp; 略。<br>
9、安全性方面的考虑<br>
&nbsp;&nbsp;&nbsp; 略。 <a href="http://hi.baidu.com/linux%5Fkernel/blog/item/d6fd574ebb1da809b3de058a.html">阅读全文</a>
		
		<br/><b>类别：</b><a href="http://hi.baidu.com/linux%5Fkernel/blog/category/%D7%E9%B2%A5%D3%EBigmp%D0%AD%D2%E9">组播与igmp协议</a>&nbsp;<a href="http://hi.baidu.com/linux%5Fkernel/blog/item/d6fd574ebb1da809b3de058a.html#comment">查看评论</a>]]></description>
        <pubDate>2006年12月21日 星期四  下午 06:38</pubDate>
        <category><![CDATA[组播与igmp协议]]></category>
        <author><![CDATA[LinqiangHe]]></author>
		<guid>http://hi.baidu.com/linux%5Fkernel/blog/item/d6fd574ebb1da809b3de058a.html</guid>
</item>

<item>
        <title><![CDATA[RFC3376 因特网组管理协议 第3版(译)]]></title>
        <link><![CDATA[http://hi.baidu.com/linux%5Fkernel/blog/item/b3a77aec51a24fd12f2e218a.html]]></link>
        <description><![CDATA[
		
		备忘录状态<br>
&nbsp;&nbsp;&nbsp; 略<br>
摘要<br>
&nbsp;&nbsp;&nbsp; 本文档说明了因特网组管理协议的第3版，IGMPv3。IGMP协议被IPv4系统用于向邻接的多播路由器报告它们的组成员关系。第3版的IGMP增加了对&ldquo;源过滤&rdquo;的支持，即系统能够报告它只对接收到的发往某一特定多播组的数据报中，某些来自特定源地址的数据感兴趣，或者是只对除了某些特定源地址之外的数据感兴趣。这个信息能够被多播路由协议用于避免把某些来自特定源地址的多播数据报发往对它不感兴趣的网络。<br>
<br>
1、简介<br>
&nbsp;&nbsp;&nbsp; IGMP协议被IPv4系统(主机或路由器)用于向邻接的多播路由器报告它们的组成员关系。需要注意的是IP多播路由器本身也可能是一个或多个多播组的成员。在这种情况下，它会既执行协议的&ldquo;多播路由器部分&rdquo;(为它的多播路由协议收集成员信息)，又执行协议的&ldquo;组成员部分&rdquo;(把自己的成员关系通知自己，其它主机，还有邻接的多播路由器)。<br>
&nbsp;&nbsp;&nbsp; IGMP协议还用于其它的IP多播管理功能，这通过使用组成员报告之外的其它的消息类型来实现。这份文档只描述组成员关系报告功能和消息。<br>
&nbsp;&nbsp;&nbsp; 这份文档说明IGMP第3版。第1版在RFC1112中说明，是第1个被广泛使用的版本，也是第1个成为因特网标准的版本。第2版在RFC2236中说明，增加了对&ldquo;低离开延迟&rdquo;的支持，即多播路由器获知相连的网络中的某一个组中已经没有组成员所花费的时间大大减少。而第3版增加了对&ldquo;源过滤&rdquo;的支持，即系统有能力报告对发往某个特定多播地址的数据报，只希望接收某些特定源的，以支持特定源多播[SSM]，或者只希望接收除了某些特定源的。第3版被设计为能够跟第1版，第2版互操作的。<br>
&nbsp;&nbsp;&nbsp; 多播侦听者发现(MLD)是IPv6系统采用的一种相似的方法，MLD第1版实现了IGMP第2版的功能，MLD第2版实现了IGMP第3版的功能。<br>
<br>
2、用于IP多播接收的服务接口<br>
&nbsp;&nbsp;&nbsp; 在一个IP系统内，有一个(至少概念上有)服务接口，被上层协议或者应用程序用于打开或者关闭IP层对发往某一特定IP多播地址的数据报的接收。为了充分利IGMPv3的能力，一个系统的IP服务接口必须支持以下操作：<br>
&nbsp;&nbsp;&nbsp; IPMulticastListen( socket, interface, multicast-address, filter-mode, source-list )<br>
&nbsp;&nbsp;&nbsp; 这里：<br>
&nbsp;&nbsp;&nbsp; socket是一个实现相关的参数，用于区别系统中不同的请求实体(程序或者进程)；BSD unix系统的socket参数就是一个例子。<br>
&nbsp;&nbsp;&nbsp; interface是网络接口的一个本地标识，是打开或关闭这个接口对特定多播地址的接收。接口必须是物理上的(比如说以太网接口)或者是虚拟的(比如侦中继虚拟电路的端点，或者IP-in-IP遂道的端点)。具体的实现必须允许向interface参数传递一个未指定值，在这种情况下，请求就会被作用于系统的主接口或缺省接口(可能是由系统配置建立的)。如果需要在多个接口上接收同一个多播地址，IPMulticastListen需要为每一个接口单独调用。<br>
&nbsp;&nbsp;&nbsp; multicast-address是该请求所属的那个IP多播地址，或者说是组。如果一个接口上需要接收多个组地址，IPMulticastListen需要为每一个组地址单独调用。<br>
&nbsp;&nbsp;&nbsp; filter-mode可以是INCLUDE或者是EXCLUDE。在INCLUDE模式下，只有来自source-list参数中列出的那些IP源地址的并发往指定多播地址的数据报才会被接收。在EXCLUDE模式下，只有来自除source-list参数中列出的那些IP源地址之外的源地址，并发往指定多播地址的数据才会被接收。<br>
&nbsp;&nbsp;&nbsp; source-list是0个或多个未排序的IP多播地址的列表。这些地址是希望接收的或者是不希望接收的，这具体取决于filter-mode参数。一个具体的实现可能会对源列表的大小强加一个限制，但是这个限制不能小于每个列表64个地址。当一个操作引起源列表大小的限制被超出，服务接口必须返回一个错误。<br>
&nbsp;&nbsp;&nbsp; 对于一个给定的socket，interface，和multicast address的组合，在任一时刻，只能有一个filter-mode和source-list有效。但是，filter-mode或者source-list，或者两者，可以被接下来的作用于同一socket，interface，和multicast address组合的IPMulticastListen操作修改。每一个接下来的请求都会完全替换上一个请求。<br>
&nbsp;&nbsp;&nbsp; 以前版本的IGMP不支持源过滤，只有一个简单的服务接口，通过加入和离开操作来打开和关闭给定接口上给定多播地址的接收。在新的服务接口上的一个等效的操作如下：<br>
&nbsp;&nbsp;&nbsp; 加入操作等效于：<br>
&nbsp;&nbsp;&nbsp; IPMulticastListen( socket，interface，multicast-address，EXCLUDE {} );<br>
&nbsp;&nbsp;&nbsp; 而离开操作等效于：<br>
&nbsp;&nbsp;&nbsp; IPMulticastListen( socket，interface，multicast-address，INCLUDE {} );<br>
&nbsp;&nbsp;&nbsp; 这里{}表示一个空的源列表。<br>
&nbsp;&nbsp;&nbsp; 一个关于在该服务接口上提供兼容性的API的例子在[FILTER-API]中。<br>
<br>
3、系统维护的多播接收状态<br>
3.1、Socket状态<br>
&nbsp;&nbsp;&nbsp; IPMulticastListen调用过的每一个Socket，系统为这个Socket记录期望的组播接收状态，这个状态概念上由如下形式的一组记录组成：<br>
&nbsp;&nbsp;&nbsp; (interface，multicast-address，filter-mode，source-list)<br>
&nbsp;&nbsp;&nbsp; 响应IPMulticastListen对该socket的每一次调用，socket的状态会变化。如下：<br>
&nbsp;&nbsp;&nbsp; 如果所请求的过滤模式是INCLUDE，并且所请求的源列表是空的。那么跟所请求的接口和多播地址相关的入口如果存在就会被删除，如果不存在这样的入口，则忽略请求。<br>
&nbsp;&nbsp;&nbsp; 如果所请求的过滤模式是EXCLUDE，并且所请求的源列表非空。那么跟请求的接口和多播地址相关的入口如果存在，就修改成含有所请求的过滤模式和源列表。如果这样的入口不存在，就需要使用参数指定的请求创建一个新的入口。<br>
3.2、Interface的状态<br>
&nbsp;&nbsp;&nbsp; 除了每一个socket的多播接收状态，系统同时也要为它的每一个接口维护或者计算一个多播接收状态。这个状态概念上由如下形式的一组记录组成：<br>
&nbsp;&nbsp;&nbsp; (multicast-address，filter-mode， source-list)。<br>
&nbsp;&nbsp;&nbsp; 对于一个给定的接口，每个多播地址至多存在一条记录。这个interface状态是从socket状态继承过来的。但是当不同的socket在同一个多播地址和接口上具有不同的mode和源列表时，两者可能就不同了。比如，假设一个应用程序或进程在socket s1上作如下调用：<br>
&nbsp;&nbsp;&nbsp; IPMulticastListen( s1，i，m，INCLUDE {a，b，c} )<br>
&nbsp;&nbsp;&nbsp; 请求接收来自源地址a,b和c的接口i上发往多播地址m的数据报。<br>
&nbsp;&nbsp;&nbsp; 假设同时有另一个程序或进程在socket s2上作了如下调用：<br>
&nbsp;&nbsp;&nbsp; IPMulticastListen( s2, i, m, INCLUDE {b, c, d} )<br>
&nbsp;&nbsp;&nbsp; 请求接收同一个接口i上发往同一个多播地址m的来自源地址b,c,d的数据报。为了同时满足两个socket的接收需求，必须让接口i接收来自源地址a,b,c,d的发往多播地址m的所有数据报。这样的话，在这个例子中，接口i对于多播地址m的接收状态就是mode为INCLUDE，源地址列表为{a,b,c,d}。<br>
&nbsp;&nbsp;&nbsp; 当IP层收到一个来自一个接口的IP多播数据报后，它接下来如何发往正在某个socket上侦听的程序或进程，取决于socket的多播接收状态[并且可能还跟其它条件相关，比如socket绑定在哪一个传输层端口上]。所以，在上面的例子中，如果数据报到达了接口i，并且它是来自源地址a发往多播地址m的，它会被传送给socket s1但不会给s2。需要注意的是IGMP查询和报告不属于源过滤的范围，必须总是被主机和路由器处理。<br>
&nbsp;&nbsp;&nbsp; 数据报的过滤基于socket的多播接收状态，这是该服务接口上的一个新特性。前一版本的服务接口[RFC1112]基于多播加入状态，没有过滤机制。而是，socket上的一个加入仅仅简单地使主机加入接口上的一个组，并且发往这个组的数据报可以被传送给所有的socket，不管它们有没有加入。<br>
&nbsp;&nbsp;&nbsp; 从socket状态继承接口状态的的基本规则如下：对于出现在socket状态中的每一个不同的(interface, multicast-address)对，为接口interface上的这个多播地址multicast-address创建一个interface记录，就像所有的socket记录都含有相同的(interface，multicast-address)对。<br>
&nbsp;&nbsp;&nbsp; -如果任何一个这样的socket记录含有EXCLUDE模式，那么interface记录的过滤模式就是EXCLUDE。并且interface记录的源列表是所有EXCLUDE模式的socket记录的源列表减去在INCLUDE模式的socket中出现的源列表后的交集。比如接口i上，多播地址m的socket记录如下：<br>
&nbsp;&nbsp;&nbsp; from socket s1:&nbsp;&nbsp;&nbsp; ( i, m, EXCLUDE, {a,b,c,d} )<br>
&nbsp;&nbsp;&nbsp; from socket s2:&nbsp;&nbsp;&nbsp; ( i, m, EXCLUDE, {b,c,d,e} )<br>
&nbsp;&nbsp;&nbsp; from socket s3: ( i, m, INCLUDE, {d,e,f} )<br>
&nbsp;&nbsp;&nbsp; 这样的话，相应的接口i上的interface记录就是：<br>
&nbsp;&nbsp;&nbsp; （m, EXCLUDE {a,b,c} )<br>
&nbsp;&nbsp;&nbsp; 如果第4个socket被加入，比如：<br>
&nbsp;&nbsp;&nbsp; from socket s4: ( i, m, EXCLUDE {} )<br>
&nbsp;&nbsp;&nbsp; 那么interface记录就变成：<br>
&nbsp;&nbsp;&nbsp; （m, EXCLUDE {} )<br>
&nbsp;&nbsp;&nbsp; -如果所有的这样的记录的过滤模式都为INCLUDE，那么interface记录的过滤模式就是INCLUDE。interface记录的源列表就是所有socket记录的源列表的合集。比如，如果接口i上多播地址m的socket记录如下：<br>
&nbsp;&nbsp;&nbsp; from socket s1：(i, m, INCLUDE {a,b,c} )<br>
&nbsp;&nbsp;&nbsp; from socket s2：(i, m, INCLUDE {b,c,d} )<br>
&nbsp;&nbsp;&nbsp; from socket s3：(i, m, INCLUDE {e,f} )<br>
&nbsp;&nbsp;&nbsp; 那么接口i上的相应的interface记录就是：<br>
&nbsp;&nbsp;&nbsp; (m, INCLUDE, {a,b,c,d,e,f})<br>
&nbsp;&nbsp;&nbsp; 如果一个组的所有的socket都在INCLUDE状态，那么具体的实现中，就不能把表示这个组的interface记录表示为EXCLUDE模式。如果在计算一个接口的interface记录时受到了系统资源的限制，那么应当立即向请求这个操作的应用程序返回一个错误。<br>
&nbsp;&nbsp;&nbsp; 当一个IPMulticastListen调用通过增，删或修改socket状态记录来修改socket状态时，上述的接收接口状态规则要马上被执行或重新执行。但是注意的是socket状态的修改不一定会导致interface状态的修改。<br>
<br>
4、消息格式<br>
&nbsp;&nbsp;&nbsp; IGMP消息用IPv4数据报进行封装，IP协议号是2。本文档所描述的每一个IGMP消息的IP生存时间都是1。因特网控制的IP优先组(比如服务类型0xC0)和IP路由器警告选项的负载会在它的IP首部中。IGMP消息的类型通过[RFC3228]描述的IANA[IANA-REG]来注册。<br>
&nbsp;&nbsp;&nbsp; 本文档所描述的IGMPv3协议关注两种类型的IGMP消息类型：<br>
&nbsp;&nbsp;&nbsp; 类型号(hex)&nbsp;&nbsp;&nbsp; &nbsp;&nbsp;&nbsp; 消息名称<br>
&nbsp;&nbsp;&nbsp; 0x11&nbsp;&nbsp;&nbsp; &nbsp;&nbsp;&nbsp; &nbsp;&nbsp;&nbsp; 成员关系查询<br>
&nbsp;&nbsp;&nbsp; 0x22&nbsp;&nbsp;&nbsp; &nbsp;&nbsp;&nbsp; &nbsp;&nbsp;&nbsp; 第3版成员关系报告<br>
&nbsp;&nbsp;&nbsp; IGMPv3的一个具体实现必须同时支持下列的三种消息类型，以能跟早期版本的IGMP互操作(见第7节)。<br>
&nbsp;&nbsp;&nbsp; 0x12&nbsp;&nbsp;&nbsp; &nbsp;&nbsp;&nbsp; &nbsp;&nbsp;&nbsp; 第一版成员关系报告&nbsp;&nbsp;&nbsp; RFC1112<br>
&nbsp;&nbsp;&nbsp; 0x16&nbsp;&nbsp;&nbsp; &nbsp;&nbsp;&nbsp; &nbsp;&nbsp;&nbsp; 第二版成员关系报告&nbsp;&nbsp;&nbsp; RFC2236<br>
&nbsp;&nbsp;&nbsp; 0x17&nbsp;&nbsp;&nbsp; &nbsp;&nbsp;&nbsp; &nbsp;&nbsp;&nbsp; 第二版离开组&nbsp;&nbsp;&nbsp; &nbsp;&nbsp;&nbsp; RFC2236<br>
&nbsp;&nbsp;&nbsp; 不能识别的消息类型必须被丢弃，其它的消息类型可能会出现在更新的IGMP版本中，或者IGMP的扩展中，或者多播路由协议，或者其它。<br>
&nbsp;&nbsp;&nbsp; 在本文档中，除非存在其它限制，关键词&ldquo;查询&rdquo;和&ldquo;报告&rdquo;分别指IGMP成员关系查询和IGMP第3版成员关系报告。<br>
4.1、成员关系查询消息<br>
&nbsp;&nbsp;&nbsp; 查员关系查询由IP多播路由器发出，用于查询邻接接口的多播接收状态，查询具有如下的格式：<br>
&nbsp;&nbsp;&nbsp; 8bit type=0x11|8bit Max Rsp Code|16bit checksum<br>
&nbsp;&nbsp;&nbsp; 32bit group address<br>
&nbsp;&nbsp;&nbsp; 4bit Resv|1bit S|3bit QRV|8bit QQIC|16bit Number of Sources(N)<br>
&nbsp;&nbsp;&nbsp; N个Source Address<br>
4.1.1、Max Rsp Code(最大响应代码)<br>
&nbsp;&nbsp;&nbsp; 最大响应代码字段指定在发送一个响应报告之前所允许的最大时间。实际允许的时间，被称为最大响应时间，其单位是1/10秒。它跟最大响应代码的换算如下：<br>
&nbsp;&nbsp;&nbsp; if Max Rsp Code &lt; 128 最大响应时间=Max Rsp Code<br>
&nbsp;&nbsp;&nbsp; if Max Rsp Code &gt;= 128 Max Rsp Code其实是表示如下的一个浮点值：<br>
&nbsp;&nbsp;&nbsp; 0 1 2 3 4 5 6 7<br>
&nbsp;&nbsp;&nbsp; 1|exp&nbsp; |mant&nbsp;&nbsp; |<br>
&nbsp;&nbsp;&nbsp; 最大响应时间= (mant | 0x10) &lt;&lt; (exp + 3)<br>
&nbsp;&nbsp;&nbsp; 最大响应时间的小值允许IGMPv3路由器调节&ldquo;离开延迟&rdquo;(最后一台主机离开组的那个时间点跟路由协议被通知到已经不存在成员的那个时间点，两者之间的时间差)。更大的值，尤其在指数范围内的值，可以调节网络中IGMP流量的爆炸。<br>
4.1.2、校验和<br>
&nbsp;&nbsp;&nbsp; 校验和是对整个IGMP数据报以16位为一段进行取反求和。为了计算校验和，校验和字段开始必须被设置成0。当收到一个数据，在处理之前必须先对校验和进行验证。<br>
4.1.3、组地址<br>
&nbsp;&nbsp;&nbsp; 当发送一个普通查询的时候，组地址字段必须被置0。当发送一个指定组查询或者发送一个指定组和源的查询(见4.1.9)时，必须被设置成要被查询的IP组地址。<br>
4.1.4、Resv(保留)<br>
&nbsp;&nbsp;&nbsp; Resv字段在传输时必须被置0，在接收时必须被忽略。<br>
4.1.5、S标志(禁止路由器处理)<br>
&nbsp;&nbsp;&nbsp; 当被设置成1的时候，S标志表示任何接收路由器禁止更新它们在收到查询时要更新的那些定时器。但它不禁止查询者选举或者普通的在路由器上执行的(当路由器作为一个组成员的时候)主机端的查询处理。<br>
4.1.6、QRV(查询者的健壮变量)<br>
&nbsp;&nbsp;&nbsp; 如果不为0，QRV中包含中一个被查询者使用的[健壮变量]的值，如果查询者的健壮变量的值超过7，即QRV字段的最大值，那么QRV被设成0。路由器取最近收到的查询中的QRV值作为它们自己的健壮性变量的值，除非最近收到的QRV是0，在这种情况下，接收者使用缺省的健壮性变量值，或者是一个静态配置的值。<br>
4.1.7、QQIC(查询者的查询间隔代码)<br>
&nbsp;&nbsp;&nbsp; 查询者的查询间隔代码字段指定查询者使用的[查询间隔]。实际的间隔，称为查询者的查询间隔(QQI)，以秒为单位表示，从查询者的查询间隔代码进行换算的方法如下：<br>
&nbsp;&nbsp;&nbsp; if QQIC &lt; 128&nbsp;&nbsp;&nbsp; QQI=QQIC<br>
&nbsp;&nbsp;&nbsp; if QQIC &gt;= 128&nbsp;&nbsp;&nbsp; QQI代表如下的一个浮点值：<br>
&nbsp;&nbsp;&nbsp; 0 1 2 3 4 5 6 7<br>
&nbsp;&nbsp;&nbsp; 1|exp&nbsp; |mant&nbsp;&nbsp; |<br>
&nbsp;&nbsp;&nbsp; QQI = (mant | 0x10 ) &lt;&lt; (exp + 3)<br>
&nbsp;&nbsp;&nbsp; 当前为非查询者的多播路由器从最近收到的查询中取QQI值作为自己的[查询间隔]值，除非最近收到QQI是0，在这种情况下，接收路由器使用缺省的[查询间隔]值。<br>
4.1.8、Number of Sources(N)<br>
&nbsp;&nbsp;&nbsp; 源数量(N)字段表明该查询中存在多少个源地址。在普通查询或指定组查询中这个值是0，在指定组和源的查询中，这个值为非0值。该数量受到查询所传输的网络上的MTU的限制。比如，在1500字节MTU的以太网上，含有路由器警告选项的IP首部占去24字节，除源数量之外的IGMP字段占去12字节，还有1464字节用于源地址，这就限制了源地址的数量最多只能有366(1464/4)。<br>
4.1.9、Source Address[i]<br>
&nbsp;&nbsp;&nbsp; Source Address[i]是n个IP单播地址的数组，n就是Number of Sources(N)字段的值。<br>
4.1.10、附加数据<br>
&nbsp;&nbsp;&nbsp; 如果收到的查询中的IP首部中数据报长度字段表明除了上述的字段之外，还有附加的数据存在，IGMPv3的实现在计算校验和的时候必须包含这些数据，但是在发送查询的时候，必须忽略这些数据，一个IGMPv3的实现在上述字段之外，不能再包含其它数据。<br>
4.1.11、查询的变体<br>
&nbsp;&nbsp;&nbsp; 查询消息有三种类型的变体：<br>
&nbsp;&nbsp;&nbsp; 1、&ldquo;普通查询&rdquo;由多播路由器发出，用于获知邻接接口(即查询所传输的网络中所相连的接口)的完整的多播接收状态。在一个普通查询中，组地址字段和源数量(N)字段都为0。<br>
&nbsp;&nbsp;&nbsp; 2、&ldquo;指定组查询&rdquo;由一台多播路由器发出，用于获知邻接接口中跟某一个IP地址相关的多播接收状态。在指定组查询中，&ldquo;组地址&rdquo;字段含有需要查询的那个组地址，源数量(N)字段为0。<br>
&nbsp;&nbsp;&nbsp; 3、&ldquo;指定组和源查询&rdquo;由一台多播路由器发出，用于获知邻接接口是否需要接收来自指定的这些源的，发往指定组的多播数据报。在一个指定组和源的查询中，组地址字段含有要查询的多播地址,源地址[i]字段含有相关的源地址。<br>
4.1.12、查询中的目的IP地址<br>
&nbsp;&nbsp;&nbsp; 在IGMPv3中，普通查询的目的IP地址是224.0.0.1，即&ldquo;所有主机&rdquo;多播地址。指定组和指定组和源的查询发往的目的IP地址就是要查询的那个多播地址。但是一个系统必须接收和处理目的IP地址是收到查询的那个接口的某一个地址(单播或多播)的查询。<br>
4.1、第3版成员关系报告<br>
&nbsp;&nbsp;&nbsp; 第3版成员关系报告由IP系统发出，用于向邻接路由器报告当前的多播接收状态，或者修改它们的接口的多播接收状态。报告具有以下的格式：<br>
&nbsp;&nbsp;&nbsp; 8bit type=0x22|8bit Reserved|16bit checksum<br>
&nbsp;&nbsp;&nbsp; 16bit Reserved&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; |16bit Number of Group Records(M)<br>
&nbsp;&nbsp;&nbsp; Group Records[M]<br>
&nbsp;&nbsp;&nbsp; 每一个Group Record的内部格式如下：<br>
&nbsp;&nbsp;&nbsp; 8bit Record Type |8bit Aux Data type|16bit Number of Sources(N)<br>
&nbsp;&nbsp;&nbsp; 32bit Multicast Address<br>
&nbsp;&nbsp;&nbsp; Source Address[N]<br>
&nbsp;&nbsp;&nbsp; Auxiliary Data<br>
4.2.1、Reserved(保留)<br>
&nbsp;&nbsp;&nbsp; 保留字段在传输时被设为0，接收时被忽略。<br>
4.2.2、校验和<br>
&nbsp;&nbsp;&nbsp; 校验和是对整个IGMP消息以16位为一段进行取反求和。为了计算校验和，校验和字段首先必须被置0。当收到一个数据，在处理之前，必须先对校验和进行验证。<br>
4.2.3、Number of Group Records(M)<br>
&nbsp;&nbsp;&nbsp; 组记录数量(M)字段标明在报告存在多个少组记录。<br>
4.2.4、Group Record<br>
&nbsp;&nbsp;&nbsp; 每一个组记录字段是一整块数据，其含有的信息是关于发送者在报告发送接口上的某一个多播组的成员关系。<br>
4.2.5、Record Type<br>
&nbsp;&nbsp;&nbsp; 见4.2.12。<br>
4.2.6、Aux Data Len<br>
&nbsp;&nbsp;&nbsp; 辅助数据长度含有在组记录中的辅助数据的实际长度，其单位是32bit字。它有可能是0，这就表示辅助数据不存在。<br>
4.2.7、Number of Sources(N)<br>
&nbsp;&nbsp;&nbsp; 源数量(N)字段标明在组记录中存在多少源地址。<br>
4.2.8、Multicast Address<br>
&nbsp;&nbsp;&nbsp; 多播地址字段标明该组记录从属的多播IP地址。<br>
4.2.9、Source Address[i]<br>
&nbsp;&nbsp;&nbsp; 源地址[i]字段是一个数组，含有n个单播地址。n就是该记录的源数量(N)字段的值。<br>
4.2.10、Auxiliary Data<br>
&nbsp;&nbsp;&nbsp; 辅助数据字段如果存在，它含有关于该组记录的一些附加信息。本文档所描述的协议IGMPv3，没有定义任何辅助数据。所以，IGMPv3的实现在任何传输的组记录中都不应该含有任何辅助数据(即必须把Aux Data Len字段置0)。并且在收到的所有组记录中，必须忽略辅助数据的存在。关于辅助数据的语法和内部编码会由将来版本的使用该字段的IGMP或其扩展定义。<br>
4.2.11、附加数据<br>
&nbsp;&nbsp;&nbsp; 如果收到的报告中的IP首部的数据报长度字段标明在最后一个组记录后面有附加的数据存在。IGMPv3的实现必须在计算和验证校验和的时候包含这些附加数据，但是同时必须忽略这些附加数据。当发送一个报告时，一个IGMPv3的实现在最后一个组记录后面不能包含附加数据。<br>
4.2.12、组记录类型<br>
&nbsp;&nbsp;&nbsp; 在一个报告消息中，有一定数量的不同类型的组记录：<br>
&nbsp;&nbsp;&nbsp; -&ldquo;当前状态记录&rdquo;由一个系统发出，用于响应在一个接口上收到的查询。它报告了接口跟某一个多播IP地址相关的当前的接收状态。当前状态记录的记录类型可以是下面两个值中的一个：<br>
&nbsp;&nbsp;&nbsp; 值&nbsp;&nbsp;&nbsp; 名字和含义<br>
&nbsp;&nbsp;&nbsp; 1&nbsp;&nbsp;&nbsp; MODE_IS_INCLUDE-标明接口相关于某一指定多播地址的过滤模式为INCLUDE。该组记录中的源地址[i]字段含有该接口的相关于该多播地址的源列表(如果非空的话)。<br>
&nbsp;&nbsp;&nbsp; 2&nbsp;&nbsp;&nbsp; MODE_IS_EXCLUDE-标明接口相关于某一指定多播地址的过滤模式为EXCLUDE。该组记录中的源地址[i]字段含有该接口的相关于该多播地址的源列表(如果非空的话)。<br>
&nbsp;&nbsp;&nbsp; -&ldquo;过滤模式改变记录&rdquo;是当本地的IPMulticastListen调用造成本地的接口层相关于某一特定多播IP地址的过滤模式的改变的时候(即从INCLUDE变到EXCLUDE，或者从EXCLUDE变到INCLUDE)，由系统发出。这个记录包含在一个报告中，而该报告是从发生改变的那个接口上发出来的。过滤模式改变记录的记录类型是以下两个值中的一个：<br>
&nbsp;&nbsp;&nbsp; 值&nbsp;&nbsp;&nbsp; 名字和含义<br>
&nbsp;&nbsp;&nbsp; 3&nbsp;&nbsp;&nbsp; CHANGE_TO_INCLUDE_MODE，标明接口相关于某一指定的多播地址的过滤模式改变到INCLUDE。该组记录中的源地址[i]字段含有该指定多播地址相关的新的源列表(如果非空的话)。<br>
&nbsp;&nbsp;&nbsp; 4&nbsp;&nbsp;&nbsp; CHANGE_TO_EXCLUDE_MODE，标明接口相关于某一指定的多播地址的过滤模式改变到EXCLUDE。该组记录中的源地址[i]字段含有该指定多播地址相关的新的源列表(如果非空的话)。<br>
&nbsp;&nbsp;&nbsp; -&ldquo;源列表改变记录&rdquo;是当本地的IPMulticastListen调用造成本地的接口层相关于某一特定多播IP地址的源列表发生改变，并且该改变不跟过滤模式的改变产生冲突时，由系统发出。该记录包含在一个报告中，而该报告是从发生改变的那个接口上发出来的。源列表改变记录的记录类型是以下两个值中的一个：<br>
&nbsp;&nbsp;&nbsp; 值&nbsp;&nbsp;&nbsp; 名字和含义<br>
&nbsp;&nbsp;&nbsp; 5&nbsp;&nbsp;&nbsp; ALLOW_NEW_SOURCE，标明组记录中的源地址[i]字段含有系统希望接收的发往某一多播地址的，新的源的列表。如果这是对一个INCLUDE列表的改变，那么这些地址会被添加到列表中，如果这是对一个EXCLUDE列表的改变，那么这些地址会被从列表中删除。<br>
&nbsp;&nbsp;&nbsp; 6&nbsp;&nbsp;&nbsp; BLOCK_OLD_SOURCE，标明组记录中的源地址[i]字段含有系统不希望再接收的发往某一多播地址的源的列表。如果这是对一个INCLUDE列表的改变，那么这些地址会被从列表中删除，如果这是对一个EXCLUDE列表的改变，那么这些地址会被添加到列表中。<br>
&nbsp;&nbsp;&nbsp; 如果源列表的改变是同时添加新的源和阻止旧的源，这两种组记录会同时发往一个多播地址，一个是ALLOW_NEW_SOURCE，另一个是BLOCK_OLD_SOURCE。<br>
&nbsp;&nbsp;&nbsp; 我们把过滤模式改变记录和源列表改变记录都统一称作状态改变记录。<br>
&nbsp;&nbsp;&nbsp; 不能识别的记录类型值必须被丢弃。<br>
4.2.13、报告的IP源地址<br>
&nbsp;&nbsp;&nbsp; 一个发往目的子网的IGMP报告应当有一个有效的IP源地址。当一个系统还没有获得一个IP地址时，它可以暂时使用0.0.0.0。需要注意的是在一个LAN中，源地址0.0.0.0可能同时被多台主机使用。路由器必须接收源地址为0.0.0.0的报告。<br>
4.2.14、报告的IP目的地址<br>
&nbsp;&nbsp;&nbsp; 第3版报告在发送时，目的IP地址为224.0.0.22，所有的IGMPv3路由器都在这个地址上侦听。以第1版和第2版兼容模式运行的系统向报告的组地址字段指定的组地址发送v1和v2报告。另外，一个系统必须接收和处理第1版和第2版的报告，它的目的IP地址字段是报告到达的接口上的某一个IP地址(单播或多播)。<br>
4.2.15、组记录的表示法<br>
&nbsp;&nbsp;&nbsp; 在文档的接下来部分，我们使用下面的表示法来描述属于某个特定多播组的组记录的内容：<br>
&nbsp;&nbsp;&nbsp; IS_IN(x)-类型INCLUDE，源地址x。<br>
&nbsp;&nbsp;&nbsp; IS_EX(x)-类型EXCLUDE，源地址x。<br>
&nbsp;&nbsp;&nbsp; TO_IN(x)-类型CHANGE_TO_INCLUDE_MODE，源地址x。<br>
&nbsp;&nbsp;&nbsp; TO_EX(x)-类型CHANGE_TO_EXCLUDE_MODE，源地址x。<br>
&nbsp;&nbsp;&nbsp; ALLOW(x)-类型ALLOW_NEW_SOURCE，源地址x。<br>
&nbsp;&nbsp;&nbsp; BLOCK(x)-类型BLOCK_OLD_SOURCE，源地址x。<br>
&nbsp;&nbsp;&nbsp; 这里x是：<br>
&nbsp;&nbsp;&nbsp; 一个大写的字母(如&ldquo;A&rdquo;)代表一组源地址，或者：<br>
&nbsp;&nbsp;&nbsp; 一个表达式(如A+B)，这里&ldquo;A+B&rdquo;表示A和B的合集，&ldquo;A*B&rdquo;表示A和B的交集，&ldquo;A-B&rdquo;表示从集合A中拿掉所有集合B的元素。<br>
4.2.16 成员关系报告的大小<br>
&nbsp;&nbsp;&nbsp; 如果包含在一个报告中的一组组记录，其大小超出了单个报告消息的大小限制(由它所发送的网络的MTU来决定)，组记录会被分散到多个报告中。<br>
&nbsp;&nbsp;&nbsp; 如果单个组记录含有太多的源地址，以致于它超出了报告消息的大小限制，如果它的类型不是MODE_IN_EXCLUDE或者CHANGE_TO_EXCLUDE_MODE，它会被拆分成多个组记录，每个都含有源地址的一个子集并在单个的报告消息中发送。如果它的类型是MODE_IN_EXCLUDE或者CHANGE_TO_EXCLUDE_MODE，只会发送单个组记录，组记录尽可能包含多的源地址，但会丢弃一部分，被丢弃的源地址不会再被报告，要被报告的源地址的选取也是任意的。它倾向于在接下来的每一个报告中，都报告同一组源，而不是每次都报告不同的源（待续)。 <a href="http://hi.baidu.com/linux%5Fkernel/blog/item/b3a77aec51a24fd12f2e218a.html">阅读全文</a>
		
		<br/><b>类别：</b><a href="http://hi.baidu.com/linux%5Fkernel/blog/category/%D7%E9%B2%A5%D3%EBigmp%D0%AD%D2%E9">组播与igmp协议</a>&nbsp;<a href="http://hi.baidu.com/linux%5Fkernel/blog/item/b3a77aec51a24fd12f2e218a.html#comment">查看评论</a>]]></description>
        <pubDate>2006年12月21日 星期四  下午 06:37</pubDate>
        <category><![CDATA[组播与igmp协议]]></category>
        <author><![CDATA[LinqiangHe]]></author>
		<guid>http://hi.baidu.com/linux%5Fkernel/blog/item/b3a77aec51a24fd12f2e218a.html</guid>
</item>

<item>
        <title><![CDATA[RFC2236: 因特网组管理协议，第2版(译)]]></title>
        <link><![CDATA[http://hi.baidu.com/linux%5Fkernel/blog/item/5a7380cb5a18c3fd53664f08.html]]></link>
        <description><![CDATA[
		
		1、定义<br>
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;略<br>
<br>
2、简介<br>
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;因特网组管理协议(IGMP)被IP主机用于向所有的直接相邻的多播路由器报告它们的多播组成员关系。本文档只描述在主机和路由器之间的确定组成员关系的IGMP应用。作为多播组成员的路由器应当还能表现为一台主机，甚至能对自己的查询作出响应。IGMP还可以应用在路由器之间，但这种应用不在这里描述。<br>
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;就像ICMP一样，IGMP作为整合在IP里面的一部分。所有希望接收IP组播的主机都应当实现IGMP。IGMP消息被封装在IP数据报中，IP协议号为2。本文档所描述的所有IGMP消息在发送时TTL都为1，并在它们的IP首部中含有一个路由器警告选项。主机所关心的所有IGMP消息都具有以下格式：<br>
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;8位类型+8位最大响应时间+16位校验和+32位组地址<br>
2.1、类型<br>
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;主机跟路由器交互时所关心的IGMP消息有三种类型：<br>
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;0x11=组成员关系查询<br>
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;组成员关系查询有两种子类型：<br>
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;-普通查询，用于获知一个相连的网络上有哪些组具有成员。<br>
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;-组特定的查询，用于获知在一个相连的网络上，某一个特定的组是否具有成员。<br>
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;这两种消息是通过组地址来区分的，具体描述见1.4节(译者注：好像没有1.4节)。成员关系查询消息以下简称为“查询”消息。<br>
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;0x16=第2版的成员关系报告<br>
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;0x17=离开一个组<br>
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;除此之外，还有一种类型的消息，只是为了对IGMPv1保持向下兼容：<br>
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;0x12=第一版的成员关系报告。<br>
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;这份文档把成员关系报告简称为“报告”。如果不指明版本，那么内容对两个版本均适用。<br>
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;未识别的消息类型会被忽略。在更新版本的IGMP中可能还会有新的消息类型，以被多播路由协议，或其它使用。<br>
2.2、最大响应时间<br>
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;最大响应时间只在成员关系查询消息中有意义，它以1/10秒为单位标明相应的报告发送所允许的最大延迟时间。在其它类型的消息中，发送者把该字段清0,接收者忽略这个字段。<br>
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;这个修改允许IGMPv2的路由器可以调节“离开延时”(最后一台主机离开组的时刻到路由协议被通过该组已经没有成员之间的时间间隔)，具体描述见7.8节。它也可以调节子网内IGMP流量的爆炸，具体描述见7.3节。<br>
2.3、校验和<br>
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;校验和是以16位为一段对整个IGMP消息(一个完整的IP负载)的取反求和。为计算校验和，校验和字段应当先被清0。当发送数据报时，必须计算校验和并放到该字段中。当收到一个数据报后，在处理之前，必须先验证校验和。<br>
2.4、组地址<br>
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;在一个成员关系查询消息中，在发送一个普通的查询之前，组地址字段必须被清0，在发送一个指定组的查询之前，把组地址设置为需要查询的那个组。<br>
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;在一个组成员关系报告或离开组的消息中，组地址字段应当是被报告的或要离开的那个组的IP地址。<br>
2.5、其它字段<br>
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;需要注意的是，IGMP消息是可能大于8个字节的，特别是将来的向下兼容的IGMP版本。只要类型字段是可以被识别的，IGMPv2的实现在处理数据报时必须忽略超过8个字节的内容。但是IGMP校验和始终是针对整个数据报的，而不仅仅是前面的8个字节。<br>
<br>
3、协议描述<br>
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;定时器的缺省值在本文档的稍后部分给出。定时器和计数器的名称在方括号中。<br>
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;名词“接口”在本文档中有时被用于表示“一个相连接的网络上的主接口”。如果一台路由器在单个网络中有多个物理接口，本协议只需要在其中的一个上面运行。但另一方面，主机需要在跟它的成员关系相关的所有网络接口上面执行相应的行为。<br>
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;多播路由器使用IGMP来了解与它相连的每一个物理网络中哪些组拥有成员。一个多播路由器为每一个相连的网络维护了一个多播组成员的列表。“多播组成员”表示在一个给定的相连网络中，一个多播组至少有一个成员，而不是所有成员的一个列表。对于每一个与它相连的网络，多播路由器可能是查询者，也可能是非查询者。通常情况下，每一个物理网络只有一个查询者。所有的多播路由器在启动的时候，在它的每一个相连的网络中都是查询者。如果一个多播路由器接收到另一台多播由器的一个查询消息，并且它的IP地址要比自己小，那它在该网络中要马上变成一个非查询者。如果一台路由器没有收到来自其它路由器的查询消息[其它查询者存在间隔]，它继续担任查询者的角色。路由器周期性地[查询间隔]向它担任查询者的相连网络发送普通查询请求，以得到成员信息。在启动的时候，路由器应当间隔较小[启动查询间隔]的发送多个[启动查询计数]普通查询,以快速并可靠地确定成员信息。一个普通查询应当被发往所有系统多播组(224.0.0.1)，其组播地址字段置0，并拥有一个最大响应时间[查询响应间隔]。<br>
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;当一台主机收到一个普通查询，它为收到查询的那个接口所在的每一个组(不包括“所有主机”组)设置延迟定时器。每个定时器都被设置成不同的随机值，该随机值采用主机所能达到的最高时间精度，值的范围是(0，最大响应时间]，最大响应时间已在查询数据报里标明。当主机收到一个指定组的查询时，如果收到该查询的接口属于该指定组，它也像上面一样，把延时定时器设置成(0，最大响应时间]中的一个随机值。如果该组的定时器已经在运行，只有当查询请求的最大响应时间比定时器当前的剩余时间还要小的时候，重置定时器为一个新的随机数。当一个组的定时器超时，主机向组内多播一个第2版的成员报告，TTL设1。如果主机在它的定时器运行的时候，收到来自另一台主机的报告(版本1或者版本2)，为了减少重复报告，它中止该组的定时器，不发送报告。<br>
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;当一台路由器接收到一个报告，它把所报告的组加入到发出报告的那个网络的多播组成员列表中，并把组成员定时器设置成[组成员间隔]。重复的报告会刷新这个定时器，如果关于某一特定的组在定时器超时前还没有报告收到。路由器就假设这个组没有本地成员，就不需要把来自远程的该组的数据报转发到这个网络中。<br>
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;当一台主机加入到一个多播组，它应当立即主动向组发送一个第2版组成员报告，以防它是这个组的第一个成员。考虑到最初的组成员报告可能会丢失或被破坏，建议在较短的时间间隔内[主动报告间隔]将该报告重复一到两次(实现这个的一个简单的办法是发送第一个第2版组成员报告后，就假设收到了一个关于该组的指定组查询，然后设置相应的定时器)。<br>
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;当一台主机离开一个多播组，如果它是响应该组的查询并发送报告的最后一台主机，它应当向“所有路由器”多播组(224.0.0.2)发送一个离开组消息。如果它不是最后一台响应查询的主机，它可以什么也不发，因为子网内肯定还有另一个成员在，这是减少网络流量的一种优化。如果主机没有足够的存储来记住它自己是否是最后一台响应的主机，在离开组时，它总是发送一个离开组消息。路由器应当接受发往要离开的那个组的离开组消息，以适应该标准的早期版本的实现。离开组消息被发往“所有路由器”组是因为其它的组成员不需要知道有主机离开这个组，但是发往这个组也是无害的。<br>
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;当查询者收到一个离开组消息，并且该组在接收接口上还有组成员。它每隔一定的时间[最后一个成员查询间隔]发送一个指定组查询，发送一定的数量[最后一个成员查询计数]。这些指定组查询的最大响应时间被设置为[最后一个成员查询间隔]。如果最后一个查询超时了，还是没有收到报告，路由器就假定这个组已经没有本地成员了。在这段时间内，任何的查询者和非查询者的转变都被忽略，由同一个路由器发送完这些指定组查询。<br>
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;非查询者必须忽略离开组消息，如果接收接口上没有该组的成员，查询者也应当忽略该离开组消息。<br>
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;当非查询者收到一个指定组的查询消息，如果它的已存在的组成员定时器比消息中指定的最大响应时间乘以[最后一个成员查询计数]还要大，它就把组成员定时器设置为该值。<br>
<br>
4、与IGMPv1路由器的兼容<br>
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;一台IGMPv2主机可能被放置在某一子网内，而该子网的路由器没有升级到IGMPv2。就产生了以下需求：<br>
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;IGMPv1路由器会发送普通查询，其最大响应时间为0，它必须被解释为100(10秒)。<br>
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;IGMPv1路由器发出查询后，期望收到第1版的成员报告，并不会关心第2版的成员报告。因此，每一个接口上必须维护一个状态变量，描述该接口上的多播查询者正在运行IGMPv1还是IGMPv2，这个变量的设置是根据在最后的几秒钟[第1版路由器存在超时]之内是否收到IGMPv1的查询来确定，不能根据最后收到的查询的类型来确定。这个变量用于决定在主动成员报告和响应查询的成员报告中发送哪一种类型的成员报告。<br>
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;在一个只有IGMPv1路由器的网络中，IGMPv2主机可能禁止发送离开组消息。<br>
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;一台IGMPv2的路由器可能被放置在某一子风内，而该子网内至少有一台路由器没有升级到IGMPv2。这就产生了以下需求：<br>
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;如果存在IGMPv1的路由器，查询者必须使用IGMPv1。IGMPv1的使用必须由管理系统配置，因为找不到一种可靠的方法来动态地确定网络内是否存在IGMPv1路由器。具体的实现可以提供一种方法让系统管理员在他们的路由器上启用IGMPv1；如果没有显式的配置，缺省值必须设为IGMPv2。在IGMPv1模式下，路由器必须周期性地发送最大响应时间为0的查询，并且必须忽离开组消息。它们还应当在收到IGMPv2查询时发出警告，但这种警告的频率必须受到限制。<br>
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;如果一台路由器没有被显式地配置成IGMPv1，但收到了一个IGMPv1查询，它应当记下一条警告日志，这些警告的频率必须受到限制。<br>
<br>
5、与IGMPv1主机的兼容<br>
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;一台IGMPv2主机可能被放置在某一个子网内，该子网内有几台主机还没有升级到IGMPv2，这就产生了以下需求：<br>
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;主机必须允许自己的成员报告被一个版本1的成员报告或者版本2的成员报告中止。<br>
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;一台IGMPv2的路由器可能被放置在某一个子网内，该子网内有几台主机还没有升级到IGMPv2，这就产生了以下需求：<br>
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;如果路由器收到一个版本1的成员报告，它必须设置定时器，以知道有版本1的主机存在，并且是它收到的报告的那个组的成员，这个定时器应当跟[组成员间隔]相同。<br>
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;如果一个特定的组内有版本1的主机存在，路由器必须忽略该组的离开组消息。<br>
<br>
6、主机状态图<br>
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;下面的状态转化图更为正式的说明了主机的行为。一台主机可能处于跟某一个网络接口上的某一个IP多播组相关的三种状态的其中之一：<br>
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;-“非成员”状态，当主机不属于接口上的这个组时即为这个状态。这是所有网络接口上所有成员关系的初始状态。它不需要主机的存储。<br>
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;-“延时成员”状态，当主机属于接口上的某一个组并且为该成员关系正在运行一个延时报告定时器时即为这个状态。<br>
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;-“空闲成员”状态，当主机属于接口上的某一个组，并且当前不在为该成员关系运行一个延时报告定时器时即为这个状态。<br>
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;有五类重要事件可以引起IGMP状态变化：<br>
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;-当主机决定加入接口上的某一个组时会发生“加入组”事件，该事件只在非成员状态时发生。<br>
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;-当主机决定离开接口上的某一个组时会发生“离开组”事件，该事件只在延时成员状态和空闲成员状态时发生。<br>
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;-当主机收到一个有效的普通成员查询信息或者一个有效的指定组成员查询信息时，会发生“收到查询”事件。为保证有效，查询消息必须至少是8字节长，并且有一个正确的IGMP校验和。IGMP首部中的组地址必须是0(普通查询)或者是一个有效的多播组地址(指定组查询)。一个普通查询作用于收到查询的接口上的所有组成员关系。一个指定组查询只作用于收到查询的接口上的单个组的成员关系。在非成员状态，对组成员查询忽略。<br>
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;-当主机收到一个有效的IGMP成员报告消息(版本1或者版本2),就会发生“收到报告”事件。为保证有效，报告消息至少必须有8字节长并且有一个正确的校验和。一个组成员报告只作用于收到该成员报告的接口上的，该报告自己所标明的那个组的成员。非成员状态和空闲成员状态忽略该报告。<br>
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;-当一个接口上的某个组的延时报告定时器超时时，就会发生“定时器超时”事件。它只在延时成员状态时发生。<br>
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;其它的事件，比如说接收到一个无效的IGMP消息，或者是查询和报告之外的IGMP消息，这三种状态都会忽略。<br>
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;应对上面的几个事件，会发生五种可能的行为：<br>
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;-在接口上为一个组“发送报告”。报告的类型由接口的状态决定，报告信息被发往要报告的那个组。<br>
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;-在接口上为一个组“发送离开消息”。如果这个接口的状态表明查询者正在运行IGMPv1，那就应当跳过这个行为不执行。如果那个表明本机是最后一个发送报告的标志已经被清除，那也可以跳过这个行为不执行。离开消息应当发往“所有路由器”组(224.0.0.2)。<br>
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;-如果本机是组内最后一个发送报告的成员，则“设置标志”。<br>
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;-如果本机已经不是组内最后一个发送报告的成员了，则“清除标志”。<br>
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;-为接口上的一个组“设置定时器”，延迟时间从(0，最大响应时间]中统一选取，最大响应时间在查询中会有标明。如果这是一个主动报告，定时器的时间从(0，[主动报告间隔]]中选取。<br>
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;-把接口上的某一个组的定时器“重置”为一个新的值。延迟时间跟“设置定时器”一样，从(0，最大响应时间]中统一选取。<br>
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;-为接口上的某个组“中止定时器”。<br>
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;(状态变换图略，参考RFC1112的状态变换图，内容略有扩展)<br>
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;另外，对于单个网络接口，一台主机可能处于以下两种可能的状态之一：<br>
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;-当主机在[第1版路由器存在超时]时段内没有收到IGMPv1类型的查询，则认为“不存在IGMPv1的路由器”，这是初始状态。<br>
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;-当主机在[第1版路由器存在超时]时段内收到了IGMPv1类型的查询，则认为“存在IGMPv1的路由器”。<br>
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;有两类事件可以引起这种状态变化：<br>
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;-当主机收到一个查询，它的最大响应时间字段的值为0，则“收到IGMPv1查询”。<br>
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;-当用于获知IGMPv1路由器存在的定时器超时，即“定时器超时”事件。<br>
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;还有一种可由事件触发的行为：<br>
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;-“设置定时器”把定时器设置成它的最大值[第1版路由器存在超时]，并启动(重启)它。<br>
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;_____________<br>
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;|不存在第&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;|<br>
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;|--&gt;|一版路由器&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;|------<br>
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;|&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;--------------------&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;|<br>
定时器&nbsp;|&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;|收到IGMPv1<br>
&nbsp;&nbsp;&nbsp;&nbsp;超时&nbsp;|&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;|查询(设置定时器)<br>
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;|&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;|<br>
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;|&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;|<br>
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;|&nbsp;&nbsp;&nbsp;&nbsp;_____________&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;|<br>
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;----|存在第一&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;|&lt;--<br>
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;---&gt;|版路由器&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;|-----------<br>
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;|&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;---------------------&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;|<br>
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;|_______________________|<br>
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;收到IGMPv1查询<br>
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;(设置定时器)<br>
&nbsp;&nbsp;&nbsp;&nbsp;<br>
7、路由器状态图<br>
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;通过下面的状态变换图，能更加正式的说明路由器的行为。<br>
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;相对于某一相连的单独网络，路由器可能处于以下两种可能的状态之一：<br>
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;-“查询者”，当路由器被分派在该网络中发送IGMP成员查询的时候，即为查询者。<br>
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;-“非查询者”，当该网络中，有另一台路由器被分派发送IGMP成员查询的时候，该路由器即为非查询者。<br>
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;下面的三类事件可以引起路由器的状态变化：<br>
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;-用于查询发送的定时器超时，就会发生“查询定时器”超时事件。<br>
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;-当收到同一网络中另一台拥有更小的IP地址值的路由器的IGMP成员查询时，会发生“收到更小IP值的路由器的查询”事件。<br>
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;-当用于获知同一网络中有更小IP地址值的另外路由器存在的定时器超时，会发生“其它路由器存在定时器超时”事件。<br>
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;对应于上述的事件，会发生三种可能的行为：<br>
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;-为相连的网络“启动普通查询定时器”。<br>
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;-为相连的网络“启动其它查询者存在定时器”，定时器时长为[其它查询者存在间隔]。<br>
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;-在相连的网络上“发送普通查询”。普通查询被发往“所有主机”组(224.0.0.1)，其最大响应时间为[查询响应间隔]。<br>
<br>
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;___________<br>
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;______|______&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;|普通查询定时器超时<br>
&nbsp;&nbsp;&nbsp;&nbsp;________&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;|查询者&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;|&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;|(发送普通查询，设<br>
&nbsp;&nbsp;&nbsp;&nbsp;|初始状态|---------------&gt;|&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;|&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;|置普通查询定时器)<br>
&nbsp;&nbsp;&nbsp;&nbsp;-----------&nbsp;(发送普通查询|&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;|&lt;-------<br>
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;设置普通查询&nbsp;|&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;|<br>
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;定时器)&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;|&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;|<br>
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;|&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;|<br>
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;__________&nbsp;|&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;|&lt;--------|&nbsp;&nbsp;&nbsp;&nbsp;<br>
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;|&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;|&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;|&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;|<br>
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;|&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;----------------------&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;|&nbsp;其它路由器存在定时器<br>
收到拥有更小|&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;|超时(发送普通查询，设<br>
IP地址值的路&nbsp;|&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;|置普通查询定时器)<br>
由器的查询&nbsp;&nbsp;&nbsp;&nbsp;|&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;_____________&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;|<br>
(设置其它路&nbsp;&nbsp;&nbsp;|&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;|&nbsp;&nbsp;&nbsp;&nbsp;非查询者&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;|&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;|<br>
&nbsp;由器存在定时|&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;|&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;|&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;|&nbsp;<br>
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;器)&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;|-------&gt;|&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;|___________|<br>
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;|&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;|<br>
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;|&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;|<br>
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;|&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;|&nbsp;<br>
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;|--------&gt;|&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;|_________<br>
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;|&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;|&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;|&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;|<br>
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;|&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;--------------------&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;|<br>
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;|&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;|<br>
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;---------------------------------------------|<br>
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;收到拥有更小IP地址值的路由器<br>
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;的查询(设置其它路由器存在定时器)<br>
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;一个路由器应当在所有的相连网络上以“初始状态”启动，并立即进入查询者状态。<br>
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;另外，为了随时跟踪哪些组拥有成员，一台路由器应当处于跟单个相连网络上的单个IP多播组相关的下列四种状态之一：<br>
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;-“不存在成员”状态。如果在某一网络中，没有主机为某一多播组发送报告，则处于该状态。这是路由器上所有组的初始状态。它不需要主机的存储。<br>
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;-“有成员存在”状态。如果在网络中，至少有一台主机为某一个多播组发送报告，则处于这种状态。<br>
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;-“第1版成员存在”状态。如果网络中有一台IGMPv1的主机，为该组发送了一个IGMPv1的成员报告，则处于该状态。<br>
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;-“确认成员”状态。如果路由器收到一个离开组的消息，之后到目前为止还未收到关于该组的成员报告，则处于该状态。<br>
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;有六类重要事件，可能引起路由器的状态变化：<br>
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;-当路由器收到接口上的某个组的第2版成员报告，会产生“收到第2版报告”事件。为保证有效，报告消息必须至少有8字节长度，并有一个正确的IGMP校验和。<br>
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;-当路由器收到接口上的某个组的第1版成员报告，会产生“收到第1版报告”事件。有效性要求同上。<br>
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;-当路由器收到接口上的某个组的离开组IGMP消息，会产生“收到离开消息”事件。为保证有效，消息必须至少有8字节长度，并有一个正确的IGMP校验和。<br>
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;-当组成员关系的定时器超时，会产生“定时器超时”事件。<br>
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;-当指定组成员查询重传定时器超时，会产生“重传定时器超时”事件。<br>
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;-当用于获知第1版成员主机存在的定时器超时，会产生“v1主机定时器超时”事件。<br>
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;对应于上述的事件，有六种可能的行为会发生：<br>
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;-为接口上的成员关系“启动定时器”，如果定时器已经在运行，把定时器重置为它的初始值[组成员关系间隔]。<br>
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;-为接口上的成员关系“启动定时器”，如果这台路由器是查询者的话，这次的行为把定时器设置为[最后一个成员查询间隔]*[最后一个成员查询计数]。如果这台路由器不是查询者，则把定时器设置为收到数据报里的[最大响应时间]*[最后一个成员查询计数]。<br>
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;-为接口上的成员关系“启动重传定时器”，时间间隔为[最后一个成员查询间隔]。<br>
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;-为接口上的成员关系“启动v1定时器”，同时，如果定时器已经在运行，把定时器设置为它的初始值[组成员关系间隔]。<br>
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;-为相连网络上的某个组“发送指定组查询”，指定组查询被发往所要查询的那个组。它的最大响应时间是[最后一个成员查询间隔]。<br>
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;-“通知路由+”事件通知路由协议相连网络上的某一个组内拥有成员。<br>
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;-“通知路由-”事件通知路由协议相连网络上的某一个组内已经没有成员。<br>
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;作为查询者的路由器的状态变化图如下：<br>
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;(由于图太大，请直接参阅RFC2236&nbsp;第7节)。<br>
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;作为非查询者的路由器的状态变化图类似，但是非查询者不发送任何类型的消息并只由消息接收来驱动，需要注意的非查询者不关心成员报告消息是第1版还是第2版。<br>
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;(图请直接参阅RFC2236&nbsp;第7节)。<br>
<br>
8、定时器缺省值列表<br>
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;这些定时器大多数是可配置的。如果不使用缺省设置，在单个连接上的所有路由器必须保持一致。<br>
8.1、健壮性变量<br>
&nbsp;&nbsp;&nbsp;&nbsp;略<br>
8.2、查询间隔<br>
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;查询间隔是指查询者发送普通查询之间的时间间隔。缺省值是125秒。<br>
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;管理员可能要修改查询间隔以调节子网内的IGMP消息的数量，值越大，IGMP查询的发送频率越低。<br>
8.3、查询报告间隔<br>
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;就是周期性普通查询数据报中的最大响应时间，缺省值是100(10秒)。<br>
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;管理员可以通过修改[查询报告间隔]来调节子网内的IGMP消息的并发机率；值越大，并发的可能性越低。因为主机的响应时刻会被分散在一个更大的间隔内，[查询响应间隔]的秒数必须比[查询间隔]的秒数少。<br>
8.4、组成员关系间隔<br>
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;组成员关系间隔是指一台多播路由器在确定某一个网络内的某一个组内没有成员之前，必须要经过的时长。这个值必须是((健壮性变量)*(查询间隔))+(一个查询响应间隔)。<br>
8.5、其它查询者存在间隔<br>
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;其它查询者存在间隔是指一台路由器在确定网络内没有其它作为查询者的路由器存在之前，必须要经过的时长。这个值必须是((健壮性变量)*(查询间隔)+(1.5倍的查询响应间隔)。<br>
8.6、启动查询间隔<br>
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;启动查询间隔是指在查询者启动的时候，发送普通查询之间的间隔。它的缺省值是1/4的查询间隔。<br>
8.7、启动查询计数<br>
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;启动查询计数是指在启动的时候，发出去的查询的数量，查询间的间隔是[启动查询间隔]，缺省值是健壮性变量。<br>
8.8、最后一个成员查询间隔<br>
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;最后一个成员查询间隔是指为响应离开组消息而发送的指定组查询中的最大响应时间，同时它也是指定组查询间的时间间隔。缺省值是10(1秒)。<br>
&nbsp;&nbsp;&nbsp;&nbsp;这个值可以通过修改网络中的“离开延迟”来调节。值越小，用于探测最后一个组成员离开花费的时间越少。<br>
8.9、最后一个成员查询计数<br>
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;最后一个成员查询计数是指路由器在确定某个组已经没有本地成员之前，需要发出去的指定组查询的数量。缺省值是健壮性变量。<br>
8.10、主动报告间隔<br>
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;主动报告间隔是指主机作为某一个组的成员的最初的报告之间的时间间隔，缺省值是10秒。<br>
8.11、第一版路由器存在超时<br>
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;第一版路由器存在超时是指主机在收到一个第一版的查询之后，必须等待的一个时间，之后它才可以发送IGMPv2的消息。值是400秒。<br>
<br>
9、消息的目的地址<br>
&nbsp;&nbsp;&nbsp;&nbsp;这些信息在本文档的其它地方也会提供，但为了方便起见，在这儿作一下总结：<br>
&nbsp;&nbsp;&nbsp;&nbsp;消息类型&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;目的组<br>
&nbsp;&nbsp;&nbsp;&nbsp;普通查询&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;所有主机组(224.0.0.1)<br>
&nbsp;&nbsp;&nbsp;&nbsp;指定组查询&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;被查询的那个组<br>
&nbsp;&nbsp;&nbsp;&nbsp;成员报告&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;要报告的那个组<br>
&nbsp;&nbsp;&nbsp;&nbsp;离开消息&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;所有路由器组(224.0.0.2)<br>
&nbsp;&nbsp;&nbsp;&nbsp;注意：IGMPv2的较老版本(非标准，现已废弃)中，主机向要离开的那个组发离开消息。所以，路由器应当接收发往要离开的组的那些离开消息，以跟这些较老版本的主机保持兼容性。但是，主机应当向“所有路由器”发送离开消息，以符合这份标准。<br>
<br>
10、安全性方面的考虑<br>
&nbsp;&nbsp;&nbsp;&nbsp;我们要考虑到每一种消息类型的伪造。<br>
&nbsp;&nbsp;&nbsp;&nbsp;查询消息：<br>
&nbsp;&nbsp;&nbsp;&nbsp;从一台具有更小IP地址值的机器发出的一个伪造查询消息会导致查询者的职责被伪造者获取。如果伪造者从此不再发送查询消息，其它路由器的“其它查询者存在定时器”会超时，并且有一台路由器会继续查询者的职责。在这段时间内，如果伪造者忽略了离开消息，那么没有成员的组的数据也会继续流向该网络直到[组成员关系间隔]时间之后。<br>
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;一个发向拥有成员的组的伪造的查询消息会使该组的成员主机报告它们的成员关系。这会在局域网内造成一定额外的流量，但是不会引起协议上的问题。<br>
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;报告消息：<br>
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;一个伪造的报告消息会使多播路由器错误地认为子网内某个没有成员的组拥有成员。来自本地子网内的伪造的报告消息没有任何意义，因为在一台主机上加入一个组不是一个特权操作，所以一个本地用户不需要伪造报告消息就可以轻易造成同样的效果。从外部源产生的伪造消息就会带来一些麻烦，对于外部产生的伪造消息有两种防卸措施：<br>
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;-如果你不能判定数据报的源地址属于接收到数据报的那个接口所在的子网，那就忽略这个报告。这个解决办法意味着在本地子网内由移动主机发出的不带有地址信息的报告都会被忽略。<br>
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;-不带有路由器警告选项[RFC2113]的报告都会被忽略，同时，要求路由器不能转发报告消息(这个要求并不是对转发路由的普通过滤提出要求，因为数据报已经带有路由警告选项)。这个解决方案跟某些不带路由器警告选项的该标准的早期实现不兼容。<br>
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;一个伪造的第1版报告消息可能会让路由器处于某一个组的“第1版成员存在”状态，这意味着路由器会忽略离开消息，这会造成没有成员的组的数据流量，直到[组成员关系间隔]时间之后。针对伪造的v1报告，有两种防卸措施：<br>
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;-针对外部源的v1报告，如果你不能判定数据的源地址属于接收到数据报的那个接口所在的子网，就忽略这个报告。这个解决方案意味着本地子网内由移动主机发出的不带有地址信息的v1报告都会被忽略。<br>
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;-给路由器提供一个配置开关，以完全忽略v1报告。这就跟v1的主机不能兼容了，所以只能用于那些对“快速离开”要求很严的场合。这个解决方案也能保护本地子网不受v1伪造报告的影响。<br>
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;离开消息<br>
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;一个伪造的离开消息会造成查询者向那个组发送指定组的查询消息。这会造成每一个路由器和组内每一个成员的额外处理，但是不会造成预期的数据的丢失。针对伪造的离开消息，有两种防卸措施：<br>
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;-如果你不能判定数据报的源地址属于收到这个数据的那个网络接口所在的子网，就丢弃这个离开消息。这个解决方案意味着子网内不带有地址的移动主机发出的离开消息都会被忽略。<br>
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;-忽略没有路由警告选项[RFC2113]的离开消息，并要求路由器不能转发离开消息。(因为数据报已经有了路由器警告选项，这个需求不会对路由转发的普通过滤产生新的需求)但是这个方案与早期的不带有路由警告选项的实现不兼容。<br>
<br>
11、致谢<br>
12、参考<br>
13、附录1-对IGMPv1的修改<br>
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;IGMPv1的“版本”和“类型”字段被合并成一个字段“类型”。<br>
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;第2版的IGMPv2成员报告中有了一个新的IGMP类型，这样，路由器就可以区分IGMPv1和IGMPv2的主机报告。<br>
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;为IGMPv2离开组消息创建了一个新的IGMP类型。<br>
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;成员查询消息被修改，先前未使用的一个字段现在有了新的值，即最大响应时间。<br>
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;IGMPv2标准提出了一个查询者选举机制。在IGMPv1中，查询者的选举是由多播路由协议来完成的，不同的协议使用不同的机制。这会造成同一个网络中有多个查询者的现象，所以选举机制在IGMPv2中被规范化。但是，这也意味着当一个IGMPv2路由器和一个使用早期的选举机制的IGMPv1路由器需要并存的时候，就要特别注意。即一个IGMPv2路由器在某一个网络内被配置后，要能够像一个IGMPv1路由器那样运行，包括：<br>
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;-在所有的查询消息中，把最大响应时间设置为0。<br>
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;-忽略离开组消息。<br>
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;IGMPv2标准减轻了成员查询和成员报告的有效性检查的负担。<br>
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;IGMPv2标准要求所有的数据报中都有IP路由器警告选项。 <a href="http://hi.baidu.com/linux%5Fkernel/blog/item/5a7380cb5a18c3fd53664f08.html">阅读全文</a>
		
		<br/><b>类别：</b><a href="http://hi.baidu.com/linux%5Fkernel/blog/category/%D7%E9%B2%A5%D3%EBigmp%D0%AD%D2%E9">组播与igmp协议</a>&nbsp;<a href="http://hi.baidu.com/linux%5Fkernel/blog/item/5a7380cb5a18c3fd53664f08.html#comment">查看评论</a>]]></description>
        <pubDate>2006年12月18日 星期一  下午 12:51</pubDate>
        <category><![CDATA[组播与igmp协议]]></category>
        <author><![CDATA[LinqiangHe]]></author>
		<guid>http://hi.baidu.com/linux%5Fkernel/blog/item/5a7380cb5a18c3fd53664f08.html</guid>
</item>

<item>
        <title><![CDATA[RFC1112,用于IP多播的主机扩展(译)]]></title>
        <link><![CDATA[http://hi.baidu.com/linux%5Fkernel/blog/item/99f1d009825fef82d0581b4e.html]]></link>
        <description><![CDATA[
		
		<p>1、备忘录状态<br>
&nbsp;略<br>
<br>
2、简介<br>
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;IP多播是用于把一个IP数据报传送给一个由多个主机组成的“组”，传送中，使用一个目的IP地址来标识由0个或多个主机组成的组。就像单播的IP数据一样，一个多播IP数据报被尽可能可靠地传送到它的目的组中的每一台主机。也就是说，数据报是不保证能够完整地到达目的组中的每一台主机，也不能保证跟其它数据报之间不会乱序。<br>
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;多播组的成员是动态的，也就是说，任何时候，任何一个成员都可以加入或离开组。同时，对于组中的成员的物理位置以及成员的数目也是没有限制的。一台主机同时可以是多个组的成员。如果一台主机仅仅是需要向一个组发送数据报，它不是必须要成为这个组的成员的。<br>
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;一个组，可以是永久性的，也可以是临时的。永久性组拥有一个熟知的，由管理机构分配的IP地址。当然，这个永久性指的是组IP地址的永久性，而不是说组成员是固定不变的，在任一时刻，一个永久组可能有一个或多个成员，甚至0个成员。除此之外，还有一些多播IP地址不是为固定组所保留的，它们可以被动态分配给临时性组，临时组只有当其组内有成员时才存在。<br>
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;因特网内，多播IP数据报的转发是通过“多播路由器”来处理的，它可以驻留在因特网网关上，也可以跟网关分离。主机以本地多播的方式传送IP多播数据报，该数据报只能到达目的组内与该主机直接相邻的那些成员上。如果这个数据报的IP&nbsp;TTL大于1，那么与本地网络直接相连的多播路由器会负责把它转发到其它有组成员的网络中。在那些TTL可以到达的网络里，也有一个直接相连的多播路由器，它会以本地多播的方式把数据报最终传送到本网内的组成员上，从而完成本次多播传送。<br>
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;这份文档主要说明了为实现对IP多播的支持，对主机IP实现所需要做的扩展。这里所说的主机包括因特网中的任何一台主机和除了多播路由器之外的所有网关。多播路由器上的和多播路由器之间的算法和协议对主机是透明的，会在专门的文档里进行描述。这份文档同时也不描述本地网络的多播是如何在不同类型的网络上实现的，尽管它说明了任意本地网络所需要的服务界面，并以一个以太网技术要求说明作为例子。其它类型的网络的技术说明将会在将来的文档中出现。<br>
<br>
3、一致性等级<br>
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;这份规范有3个一致性等级：<br>
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;0级：不支持IP多播。<br>
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;在这一等级中，不需要所有的IP实现支持IP多播。0级的主机一般不会受多播活动的影响。唯一的例外发生在某些类型的本地网络中，可能会出现1级或2级的主机错误地把IP多播数据报传递给0级主机。这类数据报的目的地址是一个D类IP地址，很容易区分出来，它们应当被不支持IP多播的那些主机丢弃。D类IP地址将在本文档的第4章描述。<br>
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;1级：支持发送，但不支持接收IP多播数据报。<br>
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;1级允许主机参与一些基于多播的服务，比如资源定位或状态报告，但是它不允许主机加入任何组播组。一个IP实现可以通过添加少量代码很容易地从0级升级到1级。本文档是只有4，5，6章是适用于1级的实现的。<br>
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;2级：完全支持IP多播<br>
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;2级允许主机加入或离开一个组，同时也允许向一个组发送IP数据报。它需要实现因特网组管理协议(IGMP)，同时要对主机的IP和本地网络服务界面进行扩展。接下来的所有章节都适用于二级的实现。<br>
<br>
4、组的地址<br>
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;组是通过一个D类的IP地址来标识的，也就是那些最高四位为“1110”的IP地址，最高四位为“1111”的是E类地址，是为将来使用的保留地址。<br>
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;用因特网标准的点分十进制表示法，组地址的范围是从224.0.0.0到239.255.255.255。地址224.0.0.0是不会被分配给任何组的，224.0.0.1被分配给一个永久性组，该组包括所有的IP主机(包括网关)，它被用于标识直接相连的网络中的所有多播主机。但是没有一个组地址(包括其它类型的IP地址)是可以用来标识整个因特网中的所有主机的。其它熟知的，永久性组的地址将在“已分本编号”文档中公布。<br>
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;附录2有一些与组地址相关的问题的背景讨论。<br>
<br>
5、主机IP实现模型<br>
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;对主机IP实现的多播扩展将在下面的模型层次图中加以说明。在这个模型中，ICMP和IGMP(2级主机)应该实现在IP模块中，IP地址到本地网络地址的映射由本地网络模块负责。这个模型仅仅是为了说明，而不应该认为它限制了实际的实现。<br>
&nbsp;&nbsp;|&nbsp;&nbsp;上层协议模块&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;|<br>
&nbsp;&nbsp;|_________________________|<br>
&nbsp;&nbsp;---------------IP服务界面-------------<br>
&nbsp;&nbsp;__________________________<br>
&nbsp;&nbsp;|&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;|&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;|&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;|<br>
&nbsp;&nbsp;|&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;|ICMP|IGMP|<br>
&nbsp;&nbsp;|IP模块&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;--------------|<br>
&nbsp;&nbsp;|_________________________|<br>
&nbsp;&nbsp;-----------本地网络服务界面--------<br>
&nbsp;&nbsp;__________________________<br>
&nbsp;&nbsp;|本地网络&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;|IP地址到|<br>
&nbsp;&nbsp;|模块(比如以太网)&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;|本地地址|<br>
&nbsp;&nbsp;|&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;|的映射(&nbsp;&nbsp;&nbsp;|<br>
&nbsp;&nbsp;|&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;|比如ARP)|<br>
&nbsp;&nbsp;|&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;-------------|<br>
&nbsp;&nbsp;|------------------------------------------|<br>
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;为了支持1级的多播，主机的IP实现必须支持多播IP数据报的传送。为了支持2级的多播，主机的IP实现还必须支持IP多播数据报的接收。这两种类型的服务会在下面的一个单独章节里描述。对每一种服务，都会描述对于IP服务界面，IP模块，本地网络服务界面和以太网本地网络模块的扩展。对非以太网类型的本地网络模块的扩展也会简单的提及，但不会很详细说明。<br>
<br>
6、发送多播IP数据报<br>
6.1、对IP服务界面的扩展<br>
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;多播IP数据报的发送是使用跟发送单播IP数据报同样的操作方式的，一个上层协议模块只需要指定一个IP组地址作为目的地址，而不是一个单个地址即可。虽然如此，但还是有一些扩展可能是必须的，或者是可取的。<br>
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;首先，服务接口必须提供一种方法，能够让上层协议模块指定发出的多播数据报的生存时间(TTL)。如果上层协议不指定生存时间，那么对所有的多播IP数据报，其生存时间缺省为1，这样的话，如果多播需要跨越一个网络，就必须指定生存时间。<br>
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;第二点，对于一台可能同时连接在多个网络中的主机，服务接口需要提供一种方法让上层协议指定某一个网络接口作为多播传送的接口。最初的传送只能使用一个接口，如果有需要，多播路由器会负责把数据报转发到其它网络中。如果上层协议不指定一个网络接口作为出口发，那么应该在系统管理的控制下选择一个缺省的网络接口。<br>
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;第三点(只适用于第2级的实现)，如果发送的主机自己也是发送数据报的目的组的成员之一，那么服务接口应当提供一种方法让上层协议禁止多播数据报的本地传送；缺省状态下，多播IP数据报会被复制一份从本地环回接口传回来。这对于那些限制组成员关系只能是一台主机一个进程的上层协议，或者在更高的层次上处理组通讯的环回(多播传输协议)的上层协议来讲，是一种性能优化。<br>
6.2、IP模块的扩展<br>
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;为了支持多播IP数据报的发送，IP模块必须被扩展，以在作发送数据报的路由时，可以识别IP组地址。绝大部分的IP实现具有以下的逻辑：<br>
&nbsp;&nbsp;&nbsp;&nbsp;if&nbsp;IP目的地址在同一个本地网络中<br>
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;直接发送数据报到目的地址<br>
&nbsp;&nbsp;&nbsp;&nbsp;else<br>
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;发送数据报到本地的网关(在到目的地址的路由上的网关)<br>
&nbsp;&nbsp;&nbsp;&nbsp;为了允许多播传送，这个逻辑必须被修改成这样：<br>
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;if&nbsp;&nbsp;IP目的地址在同一个本地网络中&nbsp;or&nbsp;IP目的地址是一个组地址<br>
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;直接发送数据报到目的地址<br>
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;else<br>
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;发送数据报到本地的网关(在到目的地址的路由上的网关)<br>
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;如果发送主机本身就是输出接口上的目的组的一个组成员，输出数据报的一个拷贝必须被环回用于本地接收，除非发送者已经禁止了它(只存在于第2级实现中)。<br>
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;输出数据报的源IP地址必须是输出接口上的一个单播IP地址。一个组地址不能被放在输出IP数据报的源地址字段，或源路由，或路由记录选项中的任何字段。<br>
6.3、本地网络服务接口的扩展<br>
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;为了支持发送多播IP数据报，本地网络服务接口不需要作任何修改。IP模块在调用已存在的本地发送操作时，仅仅需要指定一个IP组地址，而非IP单播地址作为目的地址即可。<br>
6.4、本地以太网络模块的扩展<br>
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;以太网通过允许以太网数据报中的目的地址字段填入多播地址来直接支持发送本地多播数据报。为了支持发送IP多播数据报，唯一需要的是把IP组地址映射成以太网多播地址的一个过程。<br>
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;一个IP组地址映射成以太网多播地址的方法是把IP地址的低23位放到以太网多播地址01-00-5E-00-00-00(16进制)的低23位。因为一个IP组地址有28位有效位(除去高位的1110)，所以有可能出现多个组地址被映射成同一个以太网多播地址。<br>
6.5、非以太网本地网络模块的扩展<br>
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;其它直接支持组播的网络，比如符合IEEE&nbsp;802.2标准的环网和总线网络，它们可以实现像以太网那样的为实现发送IP多播数据报所做的操作。对于一个只支持广播而不支持多播的网络，例如实验性以太网，所有的IP组地址可能被眏射成唯一的一个本地广播地址(以增加所有本地主机的费用为代价)。对于一个连接两台主机(或者一台主机和一台多播路由器)的点对点连接，多播应该完全像单播一样工作。对于像ARPANET这样的存储转发网络，或者公共的X.25网络，所有的IP组地址应当被映射成IP多播路由器的一个熟知的本地地址。在这样的网络中，路由器应当负责完成在网络内，和网络间的组播传送。<br>
<br>
7、接收组播IP数据报<br>
7.1、IP服务接口的扩展<br>
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;输入IP组播数据报是通过跟平常的单播数据报相同的IP接收操作被上层协议模块接收到的。上层协议类型的选择是基于IP首部中的协议字段，而不是目的IP地址。但是，在发送到特定的组的数据报能够被接收到之前，上层协议必须先让IP模块去加入这个组。这样，IP服务接口就必须被扩展以提供两个新的接口：<br>
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;JoinHostGroup(group-address,&nbsp;interface)<br>
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;LeaveHostGroup(group-address,&nbsp;interface)<br>
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;JoinHostGroup操作使本机成为给定网络接口interface上由IP地址group-address标识的组的成员，而LeaveHostGroup操作使本机退出给定网络接口interface上由IP地址group-address标识的组。对于只支持一个网络接口的主机，interface参数可以被忽略。对于那些可能连向多个网络的主机，上层协议也有可能不指定interface参数，在这种情况下，这个请求会作用于发送IP多播数据报的那个缺省接口上(见6.1节)。<br>
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;在多个接口上加入同一个组是允许的，在这种情况下，一台主机可能收到一个IP多播数据报的多份拷贝。同样，多个上层协议请求加入同一个组也是允许的。<br>
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;这两个操作都会立即返回(它们不是阻塞性操作)成功或失败。任何一个操作都有可能因为组地址或者接口标识的缘故失败。JoinHostGroup可能因为缺乏本地资源而失败。LeaveHostGroup可能因为主机根本就不属于组定接口上的指定组而失败。如果多个上层协议请求加入了同一个组，那么LeaveHostGroup可能成功了，但是主机还是那个组的成员。<br>
7.2、IP模块的扩展<br>
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;为了支持IP多播数据报的接收，IP模块必须被扩展，维护一个关于每个网络接口上的组成员关系的列表。属于这些组之一的输入数据报的处理方式跟目的IP地址属于该主机某一个单播IP地址的输入数据报的处理方式是相同的。<br>
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;对于那些不属于主机所在组的输入IP数据报，会被丢弃而不产生任何错误报告或日志。对于一个拥有多个网络接口的主机，如果输入数据报通过某一个接口到达，但是该数据报却是属于另一个接口上的某个组，这个数据报也会被丢弃(出现这种情况的唯一原因是本地网络模块的多播过滤功能不完整)。<br>
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;如果一个输入数据报的IP生存时间(TTL)为1，它不会被拒收(也就是说，对于非转发的输入数据报，生存时间(TTL)是不会被自动减1的)，如果一个输入数据报的IP首部的源地址字段是一个IP组地址，那该数据报会被丢弃。对于IP多播数据报，是不会产生ICMP差错报文的。<br>
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;组成员关系的列表通过上层协议调用JoinHostGroup和LeaveHostGroup请求来更新。每一个成员关系都应当有一个引用计数或相似的机制来处理多次加入和离开同一个组的情况。在第一次请求加入和最后一次请求离开给定接口上的某一个组时，该接口的本地网络模块会被通知到，它就可以更新它的组播接收过滤(见7.3节)。<br>
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;IP模块还需要被扩展，以实现附录1说明的IGMP协议。IGMP用于把某一特定本地网络中存在的组成员关系通过给邻接的多播路由器。为了支持IGMP协议，每个2级主机在初始化时就必须加入每一个网络接口上的“所有主机”组(地址224.0.0.1)，而且在主机活跃期间，必须一直是这个组的成员。<br>
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;多播路由器会把发往“所有主机”组的数据报作为一个特殊的例子处理，不管它的TTL是多少，从来不把它转发到另一个网络中。这样的话，“所有主机”组地址就不能作为整个因特网范围的广播地址。出于IGMP的目的，只有当主机属于其它的至少一个且的时候，才真正需要成为“所有主机”组的成员。但是文档又说明了主机在任何时候都必须保持为该组的成员，这是因为，第一，这样简单，第二，收到无用的IGMP查询不是很频繁，开销很低，可以忽略，第三，“所有主机”组地址可能服务于其于面向路由的一些应用，比如通知网关的存在或解析本地地址)。<br>
7.3、本地网络服务接口的扩展<br>
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;输入的本地网络多播数据报使用跟本地网络单播数据报同样的接收操作传给IP模块。为了能让IP模块告诉本地网络模块，哪些多播数据报是要接收的，本地网络服务接口必须进行扩展，提供以下两个接口。<br>
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;JoinLocalGroup(&nbsp;group-address&nbsp;)<br>
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;LeaveLocalGroup(&nbsp;group-address&nbsp;)<br>
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;这里，group-address是一个IP组地址。JoinLocalGroup操作请求本地网络模块接收并向上传属于给定IP组地址的那些数据报，而LeaveLocalGroup操作则请求本地网络模块停止向上传属于给定IP组地址的数据报。本地网络模块应该能够把IP组地址映射到本地网络地址，这是更新多播接收过滤所要求的。任何一个本地网络模块，如果它不能正确地过滤输入数据报的话，都可以忽略LeaveLocalGroup请求，并且可以向上传比JoinLocalGroup所请求的地址更多的IP数据报。<br>
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;本地网络模块不能向上传送任何由该模块发出的数据报，因为多播的环回是在IP层或更高的层做的。<br>
7.4、以太网本地网络模块的扩展&nbsp;<br>
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;为了支持多播IP数据报的接收，一个以太网模块必须能够接收由以太网多播地址标识的数据报，当然，该多播地址是跟主机的IP组地址对应的。充分利用以太网硬件接口所拥有的地址过滤功能是非常可取的，这样的话，主机就只会接收到属于它的数据报。<br>
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;不幸的是，现在相当多的以太网接口的硬件被配置成可识别的地址的数量有一些小小的限制。而且一个具体的实现必须能够侦听任意数量的以太网多播地址，这就意味着在地址数量超过过滤器的限制的时候，必须关闭地址过滤以接收所有的多播数据报。<br>
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;对于那些硬件地址过滤功能不强的接口，在以太网模块的软件中进行以太网地址过滤可能会更好些，但这不是强制的，因为IP模块会基于IP地址进行它自己的过滤。<br>
7.5、非以太网本地网络模块的扩展<br>
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;其它的多播网络，比如IEEE&nbsp;802.2网络，可以跟以太网一样地处理多播IP数据报的接收。对于纯粹的广播网络，比如实验性以太网，所有的输入广播数据报必须被接收并传给IP模块，由IP层过滤。在点对点或者存储转发网络中，多播IP数据报跟本地单播一样，所以，本地网络模块不需要作任何修改。<br>
<br>
附录1、因特网组管理协议(IGMP)<br>
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;因特网组管理协议(IGMP)被IP主机用于向与它们邻接的多播路由器报告它们的组成员关系。IGMP是一个不对称的协议，这里将从一台主机的角度，而非一个多播路由器的角度来来说明。(IGMP也可以在多播路由器之间，进行对称或非对称地使用，这种应用不在这里说明)。<br>
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;像ICMP一样，IGMP被整合为IP的一部分。符合2级IP多播标准的主机都必须实现它。IGMP信息被封装在IP数据报中，IP协议号是2。主机所关心的所有IGMP信息的格式如下：<br>
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;4位版本+4位类型+8位未使用+16位校验和+32位组地址<br>
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;版本：本文档说明的是IGMP的版本1，版本0在RFC-988中说明，已废弃。<br>
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;类型：主机关心的IGMP信息有两种类型：1-主机成员关系查询，2-主机成员关系报告。<br>
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;未使用：发送的时候置0，接收的时候忽略。<br>
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;校验和：校验和是对整个IGMP报文以16位为一段进行取反求和计算得到的，在计算校验和之前，校验和字段必须被清0。<br>
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;组地址：在组成员关系查询信息中，组地址字段在发送前被清零，接收时被忽略。在组成员关系报告信息中，组地址字段中存放的是被报告组的IP组地址。<br>
非正式的协议描述<br>
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;多播路由器发送主机组关系查询报文(以下称称查询)，以发现与它们直接相连接的网络中哪一个组已经拥有成员，查询发给“所有主机”组(地址224.0.0.1)，IP生存时间设置为1。<br>
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;主机对查询作出响应，产生一个主机组成员关系报告(以下简称报告)，报告收到查询的那个网络接口上所在的所有组，为了避免并发报告爆炸，减少被传送的报告的总数，使用了两种技术：<br>
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;1、当主机收到一个查询，它不会立即发送一个报告，它会在收到查询的那个网络接口上为每一个组成员关系都产生一个时间延迟，是在0和D秒之间的一个随机数，当定时器超时，那么相应的组的报告就会产生。这样的话，所有的报告会分散在一个D秒的时间间隔内，而不会同时并发。<br>
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;2、被发送的报告的目的IP地址等于它所要报告的组的组地址，IP生存时间设为1，这样的话，同一网络内，同一组的其它成员都可以侦听到该报告。如果一台主机收到一个同一网络内自己所在组的报告，这台主机就中止它自己的定时器，不再为这个组产生一个报告了，这样的话，正常情况下，同一网络内，每一个组只会产生一个报告，这个报告是由网络内第一台超时的主机产生的。需要注意的是，多播路由器接收所有的IP多播数据报，所以不需要明确地指明它的地址。还需要注意的是，路由器不需要知道哪些主机属于某个组，它只需要知道某一特定的网络内至少有一台主机属于某个组就可以了。<br>
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;对以上描述的行为，有两个例外。首先是如果一个查询到达的时候，这个组成员关系的报告延时定时器已经被启动了，那么这个定时器的超时值不会被重置为一个新的随时数，而是在它当前值的基础上继续运行。其次，对于所有组(224.0.0.1)的成员关系，没有延时报告定时器，也永远不会被报告。<br>
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;如果一台主机使用一个伪随时数产生器来计算报告延时，那么主机的某一个单播IP地址需要被作为随时种子的一部分，以减少多台主机产生同一个随时序列的机会。<br>
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;主机需要确认收到的报告的目的IP地址字段和它的IGMP组地址字段具有相同的IP地址，以确保主机自己的报告不会因为收到一个错误的报告而被中止。对于那些不是主机成员关系查询和主机成员关系报告的IGMP消息，主机应当把它丢弃。<br>
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;多播路由器周期性地发送查询以刷新它的关于某个特定网络内组成员关系的知识。如果在发送一定数量的查询之后，关于某个组的成员报告一直没有收到，那么路由器就假定该网络内已经没有该组的成员了，它就不必再把远程的该组内的多播数据报转发到这个网络内了。通常查询的发送不会很频繁(不多于1分种1次),以保证IGMP对主机和网络的开销都非常低。但是，当一个多播路由器启动的时候，它为了快速建立对于本地网络内的组成员关系的信息，会发几个时间间隔很小的请求。<br>
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;当一台主机加入一个新的组，有可能它会是这个网络内这个组的第一个成员，所以，以防万一，它应当立即发送一个关于这个组的报告，而不是等待请求。有可能最初的报告会被丢失或损坏，所以建议在一个短的时间间隔后，重复发送该报告一到两次(一个简单的实现方法是就像收到了关于这个组的查询一样，设置组的随时延时报告定时器，下面的状态变换图解释了这种方法)。<br>
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;需要注意的是，在一个不存在多播路由器的网络中，唯一的IGMP传输是当一台主机加入一个新的组时，一个或多个报告会被发出来。<br>
状态变换图<br>
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;通过下面的状态变换图，将更加正式地说明IGMP的特性。一台主机会处于三种可能的状态之一，这与某一个网络接口上的某一个IP组地址有关：<br>
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;-非成员状态。当主机不属于网络接口上的组时，这是所有网络接口上的所有成员的初始状态；它不需要主机的存储空间。<br>
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;-延迟成员状态。当一台主机属于网络接口上的某一个组，并且正在为这个组运行延迟报告定时器时，就处于这个状态。<br>
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;-空闲成员状态。当一台主机属于网络接口上的某一个组，并且已不在为这个组运行延迟报告定时器时，就处于这个状态。<br>
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;有五类重要的事件会造成IGMP状态的变化：<br>
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;当主机决定加入某一接口上的某个组时，会发生“加入组”事件。它只有在“非成员状态”时发生。<br>
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;当主机决定离开某个接口上的某个组时，会发生“离开组”事件。它只有在“延迟成员状态”和“空闲成员状态”时发生。<br>
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;当主机收到一个有效的IGMP组成员状态查询信息时，会发生“接收到查询”事件。这个查询信息必须至少有8个字节的长度，有一个正确的IGMP校验和和一个224.0.0.1的目的IP地址，才是有效有。单个查询会作用于收到查询的那个接口上的所有组成员关系，“非成员状态”和“延迟成员状态”的组成员关系会被忽略。<br>
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;当主机收到一个有效的IGMP组成员关系报告信息时，会发生“接收到报告”事件。这个报告信息必须至少有8个字节的长度，有一个正确的IGMP校验和，在它的目的IP地址字段和IGMP组地址字段必须有一个相同的组地址，它才是有效的。一个报告只作用于收到报告的那个网络接口上的，该报告所标识的那个组。“非成员状态”和“空闲成员状态”的组成员关系会被忽略。<br>
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;当某个接口上的一个组的报告延时定时器超时时，会发生“定时器超时”事件。它只会在“延迟成员状态”发生。<br>
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;所有的其它事件，比如接收到一个无效的IGMP信息，或者不是查询或请求的IGMP信息，在这三种状态中都会被忽略。<br>
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;应对上述的事件，有三种可能的行为会发生：<br>
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;-对接口上的组“发送报告”。<br>
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;-使用0到D秒之间的一个随时延迟，为接口上的组“启动定时器“。<br>
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;-为接口上的组中止定时器。<br>
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;在下面的图中，每个状态变换的箭头上都标识了引起这次状态变换的事件，在括号里标识了在这次状态变换中的行为。<br>
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;————————————<br>
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;|&nbsp;非成员&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;|<br>
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;-----&gt;|&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;|&lt;-----<br>
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;|&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;----------------------------------&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;|<br>
离开组&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;|&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;|加入组&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;|<br>
(关闭定时器)|&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;|(发送报告,&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;|离开组<br>
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;|&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;|&nbsp;启用定时器)&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;|<br>
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;_____|_____&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;|&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;_________|____<br>
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;|延迟成员&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;|&lt;------&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;|空闲成员&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;|<br>
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;|&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;|收到查询(&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;|&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;|<br>
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;|&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;|启用定时器)&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;|&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;|<br>
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;|&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;|&lt;-----------------------|&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;|<br>
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;|&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;|-----------------------&gt;|&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;|<br>
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;|&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;|收到报告(&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;|&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;|<br>
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;|&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;|中止定时器)&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;|&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;|<br>
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;|&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;|-----------------------&gt;|&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;|<br>
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;|&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;|定时器超时&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;|&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;|<br>
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;-----------------(发送报告)&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;----------------------<br>
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;“所有主机”组(地址224.0.0.1)作为一个特殊的例子处理。主机一开始就在所有的网络接口上处于空闲成员状态，从不转换到其它状态，也从不发送报告。<br>
协议参数<br>
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;最大报告延迟D是10秒。<br>
<br>
附录2：关于组地址<br>
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;本附录不是IP多播规范的一部分，但它提供几点跟IP组地址相关的背景讨论。<br>
组地址绑定<br>
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;将一个IP组地址绑定到一台物理主机的过程可以看作是IP单播地址绑定的一般化处理。一个IP单播地址被静态地绑定到单个IP网络中的单个本地网络接口上。一个IP多播地址被动态地绑定到一组IP网络中的一组本地网络接口上。<br>
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;很重要的一点是需要理解IP组地址不是被绑定到一组IP单播地址上，多播路由器不需要为每一个组维护一个成员的列表。比如连接到一个以太网的多播路由器，它只需要把单个的以太网组播地址跟一个有本地成员的组联系起来即可，而不需要一个成员的IP地址列表或以太网地址列表。<br>
分配临时组地址<br>
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;这份文档没有说明临时组地址是如何被分配的，可以肯定地一点是不同部分的临时IP组地址空间会使用不同的技术进行分配。比如有一定数量的服务器，可以申请一个新的临时组地址互相通讯。一些高层协议(比如RFC-1045的VMTP协议)可能会产生高层的临时“进程组”或“实体组”的地址。这些地址通过算法被映射成临时IP组地址的子集。跟IP组地址被映射成以太网多播地址的方法相似。IP组地址空间的一部分被保留，被应用程序进行随时分配，这些应用程序可能能够容忍组地址跟其它的多播用户冲突，或者另外寻找新的地址，直到找到合适的。<br>
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;一般来讲，主机不能假定发往任意组地址的数据报只会到达预期的主机，换句话说，就是作为一个临时组成员接收的数据报只到达它的接收者。所以，IP层以上必须使用高层的一些标识和认证手段，对错误的传输作检测。如果发送者不希望信息被非预期的接收者接听到，那么发往一个组的信息必须被加密或者通过路由控制来管理。</p> <a href="http://hi.baidu.com/linux%5Fkernel/blog/item/99f1d009825fef82d0581b4e.html">阅读全文</a>
		
		<br/><b>类别：</b><a href="http://hi.baidu.com/linux%5Fkernel/blog/category/%D7%E9%B2%A5%D3%EBigmp%D0%AD%D2%E9">组播与igmp协议</a>&nbsp;<a href="http://hi.baidu.com/linux%5Fkernel/blog/item/99f1d009825fef82d0581b4e.html#comment">查看评论</a>]]></description>
        <pubDate>2006年12月16日 星期六  上午 11:38</pubDate>
        <category><![CDATA[组播与igmp协议]]></category>
        <author><![CDATA[LinqiangHe]]></author>
		<guid>http://hi.baidu.com/linux%5Fkernel/blog/item/99f1d009825fef82d0581b4e.html</guid>
</item>

<item>
        <title><![CDATA[数据报接收过程中对IP选项的处理]]></title>
        <link><![CDATA[http://hi.baidu.com/linux%5Fkernel/blog/item/ee74e611894419c6a6ef3f30.html]]></link>
        <description><![CDATA[
		
		&nbsp;&nbsp;&nbsp; IP选项中目前还比较有用的就是源站选路选项(IPOPT_SSRR和IPOPT_LSRR)，和记录路由选项(IPOPT_RR)，ping程序的-R命令选项可以启用IPOPT_RR选项，以提供记录路由的功能。通过-R发送出去的回显请求包带有IPOPT_RR选项，共40字节，除去code, len, ptr三字节，另外发送数据报的源端也把自己的出口IP地址填入选项的IP清单中，这样，还有8个IP地址的空间可用，所以，用ping程序来确定数据报的路由，其功能是非常有限的。<br>
&nbsp;&nbsp;&nbsp; 当网络中的一台主机收到带有RR选项的回显请求包后，其网络层的接收函数ip_rcv_finish通过检查IP首部中的长度字段值是否超过20字节来判断该数据报是否含有IP选项，如果有，则调用ip_rcv_options来接收这些IP选项。ip_rcv_options会调用ip_options_compile来解析这些IP选项。<br>
&nbsp;&nbsp;&nbsp; ip_options_compile的根本任务是把skb的IP首部中的选项内容解析后存放在一个结构体strut ip_options中，并把该结构体保存在skb-&gt;cb中。cb成员是一个长度为48字节的缓冲区，供协议栈各层自由使用，在网络层，它被解释为结构体struct inet_skb_parm，其定义如下：<br>
&nbsp;&nbsp;&nbsp; struct inet_skb_parm<br>
&nbsp;&nbsp;&nbsp; {<br>
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; struct ip_options&nbsp;&nbsp; opt;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; /* Compiled IP options&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; */<br>
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; unsigned char flags;<br>
#define IPSKB_FORWARDED&nbsp;&nbsp;&nbsp;&nbsp; 1<br>
#define IPSKB_XFRM_TUNNEL_SIZE&nbsp; 2<br>
#define IPSKB_XFRM_TRANSFORMED&nbsp; 4<br>
#define IPSKB_FRAG_COMPLETE 8<br>
#define IPSKB_REROUTED&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; 16<br>
&nbsp;&nbsp;&nbsp; };<br>
&nbsp;&nbsp;&nbsp; 成员opt就是ip_options_compile最终存放数据的地方，它只在发送带IP选项的数据报的时候用于暂存IP选项内容到opt的成员__data中，在处理接收到的数据报中的IP选项的时候，直接在skb的IP首部里操作，opt的成员is_data值置0，表示__data成员中没有数据。<br>
&nbsp;&nbsp;&nbsp; 解析完struct ip_options后，在回应数据报的发送过程中，又可以通过ip_append_data函数像主动发送IP数据一样处理IP选项。 <a href="http://hi.baidu.com/linux%5Fkernel/blog/item/ee74e611894419c6a6ef3f30.html">阅读全文</a>
		
		<br/><b>类别：</b><a href="http://hi.baidu.com/linux%5Fkernel/blog/category/ip%D0%AD%D2%E9">ip协议</a>&nbsp;<a href="http://hi.baidu.com/linux%5Fkernel/blog/item/ee74e611894419c6a6ef3f30.html#comment">查看评论</a>]]></description>
        <pubDate>2006年12月12日 星期二  下午 06:45</pubDate>
        <category><![CDATA[ip协议]]></category>
        <author><![CDATA[LinqiangHe]]></author>
		<guid>http://hi.baidu.com/linux%5Fkernel/blog/item/ee74e611894419c6a6ef3f30.html</guid>
</item>


</channel>
</rss>