概说《TCP/IP详解 卷2》第10章 IP的分片和重装

原文链接:https://mp.weixin.qq.com/s/GlZs42qnahajM0MJqlJl4Q

本文要点

  • 引言

  • 分片

  • ip_optcopy函数

  • 重装

  • ip_reass函数

  • ip_slowtimo函数

  • 小结

 

引言

    本文将详细讨论在概说《TCP/IP详解 卷2》第8章 IP:网际协议中省略的IP分片与重装处理的部分。

    IP具有一种重要功能,就是当分组过大而不适合在所选硬件接口上发送时,能够对分组进行分片。过大的分组被分成两个或者多个大小合适的在所选定网络上发送的IP分片。而在去目的主机的路途中,分片还可能被中间的路由器继续分片。因此,在目的主机上,一个IP数据报可能放在一个IP分组内,或者如果被分片,就放在多个IP分组内。因为各个分片可能以不同的路径到达目的主机,所有只有目的主机才能看到所有分片。因此,也只有目的主机才能把所有分片重装成一个完整的数据报,提交给相应的运输层协议。

    IP首部内有三个字段实现分片和重装:标识字段(ip_id)、标志字段(ip_off的3个高位比特)和偏移字段(ip_off的13个低位比特)。标志字段由三个1bit标志组成。比特0是保留的,必须为0;比特1是“不分片”标志;比特2是“更多分片”标志。在Net/3中,标志和偏移字段结合起来,由ip_off访问,如图1所示。

图1 ip_off控制IP分组的分片

    Net/3通过使用IP_DF和IP_MF掩去ip_off来访问DF和MF。ip_off的其它13bit指出在原始数据报内分片的位置,以8字节为单元计算。因而,除最后一个分片外,其它每个分片都希望是一个8字节倍数的数据,从而使后面的分片从8字节边界开始。图2显示了在原始数据报内的字节偏移关系,以及在分片的IP首部内分片的偏移(ip_off的低位13bit)。

    图2显示了把一个最大的IP数据报分成8190个分片,除最后一个分片包含3个字节外,其它每个分片都包含8个字节;而且除最后一个分片外,其余分片都设置了MF比特。

图2 65535字节的数据报分片

    原始数据报上面的数字是该数据部分在数据报内的字节偏移。分片偏移(ip_off)是从数据报的数据部分开始计算的。分片不可能含有偏移超过65514的字节,因为如果这样的话,重装的数据报会大于65535字节,这是ip_len字段的最大值。这就限制了ip_off的最大值为8189(8189x8=65512),只为最后一个分片留下3字节空间。如果有IP选项,则偏移还要小些。

    因为IP互联网是无连接的,所以,在目的主机上,来自一个数据报的分片必然会与来自其它数据报的分片交错。ip_id是唯一地标识某个特定数据报的分片。源系统用相同的源地址(ip_src)、目的地址(ip_dst)和协议(ip_p)值,作为数据报在互联网上生命期的值,把每个数据报的ip_id设置成一个唯一的值。

    总而言之,ip_id标识了特定数据报的分片,ip_off确定了分片在原始数据报内的位置,除最后一个分片外,MF标识每个分片。

 

分片

    我们现在回到ip_output,分析分片代码。在概说《TCP/IP详解 卷2》第8章 IP:网际协议图20中,如果分组的大小不超过选定出接口的MTU,就在一个链路级帧中发送它。否则,必须对分组分片,并在多个帧中将其发送。分组可以是一个完整的数据报或者它自己也是前面系统创建的分片。我们分三个部分讨论分片代码:

  • 确定分片大小(图3)

  • 构造分片(图4)

  • 构造第一个分片并发送分片(图5)

图3 函数ip_output:确定分片大小

253~261 分片算法很简单,但由于对mbuf结构链的操作使实现很复杂。如果DF比特禁止分片,则ip_output丢弃该分组,并返回EMSGSIZE。如果数据报是在本地生成的,则运输协议把错误传回该进程;如果分组是被转发的,则ip_forward生成一个ICMP目的地不可达差错报文,并指出不分片就无法转发该分组。

262~266 每个分片中的数据字节数len的计算是用接口的MTU减去分组首部的长度后,舍去低位的3个比特(&~7),成为8字节倍数。如果MTU太小,使每个分片中无法含有8字节的数据,则ip_output返回EMSGSIZE。

    每个新的分片中都包含:一个IP首部、某些原始分组中的选项以及最多len长度的数据。

    图4中的代码,以一个C的复合语句开始,构造从第2个分片开始的分片表。在分片表生成后(图5),原来的分组被转换成第一个分片。

图4 函数ip_output:构造分片

图5 函数ip_output:发送分片

267~269 外部块允许在函数中离使用点更近一点的地方定义mhlen、firstlen和mnext。这些变量的范围一直到块的末尾,它们隐藏其它在块外定义的有相同名字的变量。

270~276 因为原来的缓存链现在成了第一个分片,所以for循环从第2个分片的依稀开始:hlen+len。对于每个分片,ip_output采取以下动作:

  • 277~284 分配一个新的分组缓存,调整m_data指针,为一个16字节链路层首部(max_linkhdr)腾出空间。如果ip_output不这么做,则网络接口驱动器就必须再分配一个mbuf来存储链路层首部或者移动数据。两种工作都耗时,在这里容易处理从而避免问题。

  • 285~290 从原来的分组中把IP首部和IP选项复制到新的分组中。前者复制在一个结构中;ip_optcopy只复制那些将被复制到每个分片中的选项。

  • 291~297 设置分片包括MF比特的偏移字段(ip_off)。如果原来分组中已设置MF比特,则把所有分片中都把MF置位;如果原来分组中没有设置MF比特,则除了最后一个分片外,其它所有分片中的MF都置位。

  • 298 为分片设置长度,解决首部小一些(ip_optcopy可能没有复制所有选项),以及最后一个分片的数据区小一些的问题。以网络字节序列存储长度。

  • 299~305 从原始分组中把数据复制到该分片中。如果必要,m_copy会再分配一个mbuf。如果m_copy失败,则发出ENOBUFS。sendorfree丢弃所有已分配的缓存。

  • 306~314 调整新创建的分片的mbuf分组首部,使其具有正确的全长。把新分片的接口指针清零,把ip_off转换成网络字节序列,计算新分片的检验和。通过m_nextpkt把该分片与前面的分片链接起来。

    在图5中,ip_output构造了第一个分片,并把每个分片传递到接口层。

315~325 把末尾多余的数据截断后,原来的分组就转换成第一个分片,同时设置MF比特,把ip_len和ip_off转换成网络字节序列,计算新的检验和。在这个分片中,保留所有的IP选项。在目的主机重装时,只保留数据中的第一个分片的IP选项(图25)。某些选项,如源路由选项,必须被复制到每个分片中,即使在重装时都被丢弃了。

326~338 此时,ip_output可能有一个完整的分片表,或者已经产生了错误,都必须把生成的那部分的分片表丢弃。for循环遍历该表,发送分片或者由于error而丢弃该分片。

 

ip_optcopy函数

    在分片过程中,ip_optcopy(图6)复制到达分组(如果分组是被转发的)或者原始数据报中(如果该数据报是本地生成的)中的选项到外出的分片中。

图6 函数ip_optcopy

395~422 ip_optcopy的参数是:ip,一个指向原始分组的IP首部指针;jp,一个指向新生成的分片的IP首部的指针;ip_optcopy初始化cp和dp指向每个分组的第一个选项,并在处理每个选项时把cp和dp向前移动。for循环每次复制一个选项,当它遇到EOL选项或者检查完所有选项时。NOP选项被复制,用来维持后续选项的对齐限制。

    如果IPOPT_COPIED指示的copied比特被置位,则ip_optcopy把选项复制到新片中。如果某个选项的长度太大,就被截断;ip_dooptions应该已经发现这种错误了。

423~426 第2个for循环把选项表填充到4字节的边界。由于分组首部长度(ip_hlen)是以4字节为单位计算的,所以需要这个操作。这也保证后面跟着的运输层首部与4字节边界对齐。这样会提高性能,因为在许多运输层协议的设计中,如果运输层首部从一个32bit边界开始,那么32bit首部将按照32bit边界对齐。

    图7显示了ip_optcopy的运行。

图7 在分片中并不复制所有选项

    在图7中,我们看到ip_optcopy不复制时间戳选项(它的copied比特为0),但却复制LSRR选项(它的copied比特为1)。为了把新选项与4字节边界对齐,ip_optcopy也增加了一个EOL选项。

 

重装

    到目前为止,我们已经讨论了数据报的分片,现在再回到ipintr讨论重装过程。ipintr将分片重装成一个完整的数据报,然后整个交给运输层处理。ipintr接收的分片被传给ip_reass,由它尝试把分片重装成一个完整的数据报。图8显示了ipintr的代码。

图8 函数ipintr:分片处理

271~279 我们知道ip_off包含DF比特、MF比特和分片偏移。如果MF比特或者分片偏移非零,则DF就被掩盖掉了,分组就是一个必须被重装的分片。如果两者都为零,则分组就是一个完整的数据报。跳过重装代码,执行图8中最后的else语句,它从全部数据报长度中排除了首部长度。

280~286 m_pullup把位于外部簇上的数据移动到mbuf的数据区。在mtod宏开始工作之前,m_pullup必须把IP首部从外部簇移到mbuf的数据区中去。

287~297 Net/3在一个全局双向链表ipq上记录不完整的数据报。这个名字可能容易产生误解,因为这个数据结构并不是一个队列。也就是说,可能在表的任何地方插入和删除,并不限制一定要在末尾。

    ipintr对表进行线性搜索,为当前分片找到合适的数据报。记住分片是由4元组{ip_id、ip_src、ip_dst和ip_p}唯一标识。ipq的每个入口是一个分片表,如果ipintr找到一个匹配,则fp指向匹配的表。

298~303 在found语句,ipintr为方便重装,修改了分组:

  • 304 ipintr修改了ip_len,从中减去标准IP首部和任何选项长度。

  • 305~307 ipintr把MF标志复制到ipf_mff的低位,把ip_top覆盖掉(&=~1只清除低位)。注意,在ipf_mff成为一个有效成员之前,必须把ip指向一个ipasfrag结构(图11)。此时,可以把ip_off作为一个16bit的偏移,而不是3个标志比特和一个13bit偏移来访问了。

  • 308 用8乘以if_off,把它从以8字节为单元转换成以1字节为单元。ipf_mff和ip_off决定ipintr是否应该重装。图9描述了不同的情况以及相应的动作,其中fp指向的是系统以前为该数据报接收的分片表。许多工作是由ip_reass做的。

图9 ipintr和ip_reass中的IP分片处理

309~322 如果ip_reass通过把当前分片与以前收到的分片组合在一起,能重装成一个完整的数据报,它就返回指向该重装好的数据报的指针。如果没有重装好,则ip_reass保存该分片,ipintr跳到next去处理下一个分片。

323~324 当到达一个完整的数据报时,就选择这个else分支,并按照前面的一样修改ip_hlen。大部分执行流程会到达else分支,因为收到的数据报大多数不是分片。

    如果重装处理产生一个完整的数据报,ipintr就把这个完成的数据报上传给相应的运输层协议:

    (*inetsw[ip_protox[ip->ip_p]].pr_input)(m, hlen)

 

ip_reass函数

    ipintr把一个要处理的分片和一个指针传给ip_reass,其中指针指向是的ipq中匹配的重装首部。ip_reass可能重装成功并返回一个完整的数据报,可能把该分片链接到数据报的重装链表上,等待其它分片到达后重装。每个重装链表的表头是一个ipq结构,如图10所求。

图10 ipq结构

52~60 用来标识一个数据报分片的四个字段,ip_id、ip_src、ip_dst和ip_p,被保存在每个重装链表静养的ipq结构中。Net/3用next和prev构造数据报链表,用ipq_next和ipq_pre构造分片的链表。

    到达分组的IP首部在被放在重装链表之前,首先被转换成一个ipasfrag结构,如图11所示。

 图11 ipasfrag结构

66~86 ip_reass在一个由ipf_next和ipf_prev链接起来的双向循环链表上,收集某个数据报的分片。这些指针覆盖了IP首部的源地址和目的地址。ipf_mff成员覆盖ip结构中的ip_tos。其它成员是相同的。

    图12显示了分片首部链表(ipq)和分片(ipasfrag)之间的关系。

图12 分片首部链表ipq和分片

    图12的左下部是重装首部的链表。表中第一个节点是全局ipq结构,ipq。它永远不会有自己的相关分片表。ipq表是一个双向链表,用于支持快速插入和删除。next和prev指针指向前一个和后一个ipq结构。

    图12仍然没有显示重装结构的所有复杂性。重装代码很难跟踪,因为它完全依靠指针指向底层mbuf上的三个不同的结构。

    图13显示了mubf、ipq结构、ipasfrag结构和ip结构之间的关系,包含了大量信息:

  • 所有结构都放在一个mbuf的数据区内。

  • ipq链表由next和prev链接起来的ipq结构组成。每个ipq结构保存了唯一标识一个IP数据报的四个字段(图13中阴影部分)。

  • 当作为分片链表的头访问时,每个ipq结构被看成一个ipasfrag结构。这些分片由ipf_next和ipf_prev链接起来,分别覆盖了ipq结构的ipq_next和ipq_prev成员。

  • 每个ipasfrag结构都覆盖了到达分片的ip结构,与分片一起到达的数据在缓存中跟在该结构之后。ipasfrag结构的阴影部分是成员和含义与其在ip结构中不太相同。

图13 可通过多种结构访问的一段内存区

    图12显示了这些重装结构之间的物理连接,图13显示了ip_reass使用的覆盖技术。图14从逻辑的观点说明重装结构:该图显示了三个数据报的重装,以及ipq链表和ipasfrag结构之间的关系。

图14 三个IP数据报的重装

    每个重装链表的表头包含原始数据报的标识符、协议、源和目的地址。图14中只显示了ip_id字段。分片表通过偏移字段排序,如果MF比特被置位,则用MF标志分片,缺少的分片出现在阴影里。每个分片的数字显示了该分片的开始和结束字节相对于原始数据报数据区的相对偏移,而不是相对于原始数据报的IP首部。

    这个例子用来说明三个没有IP选项的UDP数据报,其中每个数据报都有1024字节的数据。每个数据报的全长是1052(20+8+1024)字节,正好适合1500字节的以太网MTU。在到目的主机的途中,这些数据报会遇到一个SLIP链路,该链路上的路由器对数据报分片,使其大小适于放在典型的296字节的SLIP MTU中。每个数据报分4个分片到达。第1个分片中包含一个标准的20字节IP首部,8字节UDP首部和264字节数据。第2个和第3个分片中包含一个20字节的IP首部和272字节的数据。最后一个分片中在一个20字节首部和216字节数据(1032=272x3+216)。

    在图14中,数据报5缺少一个包含272~543字节的分片。数据报6缺少第一个分片0~271字节,以及最后一个从偏移816开始的分片。数据报7缺少前三个分片0~815。

    图15列出了ip_reass。前面讲到,当目的地是本机的某个IP分片到达时,在处理完所有选项后,ipintr会调用ip_reass。

图15 函数ip_reass:数据报重装

343~358 当调用ip_reass时,ip指向分片,fp指向匹配的ipq结构或者为空。因为重装只涉及每个分片的数据部分,所以ip_reass调整含有该分片的mbuf的m_data和m_len,减去每个分片的IP首部。

465~469 在重装过程中,如果产生错误,该函数就跳到dropfrag。dropfrag增加ips_fragdropped,丢弃该分片,并返回一个空指针。

    在运输层丢弃分片通常会严重降低性能,因为必须重传整个数据报。TCP谨慎地避免分片,但是UDP应用程序必须采取步骤以避免对自己分片。

    所有IP实现必须能够重装最多576字节的数据报。没有通用的方法来确定远程主机能重装的最大数据报大小。我们将在27章看到TCP提供一个机制,可以确定远程主机所能处理的最大数据报大小。UDP没有这样的机制,所以许多基于UDP的协议,都限制在576字节左右。

    我们将分7个部分显示重装代码,从图16开始。

图16 函数ip_reass:创建重装表

    a. 创建重装表

359~366 当fp为空时,ip_reass用新的数据报的第一个分片创建一个重装表。它分配一个mbuf来存放新表的头(一个ipq结构),并调用insque,把该结构插入到重装表的链表中。

    图17列出了操作数据报和分片链表的函数。

图17 ip_reass采用的队列函数

    b. 重装超时

367 ipq_ttl字段用于限制等待分片以重装成一个完整数据报的时间。这与IP首部的TTL字段是不同的,IP首部的TTL字段是为了限制分组在互联网中最大的跳数。

    在Net/3中,重装超时的初始值设为60(IPFRAGTTL)。因为每次内核调用ip_slowtimo时,ipq_ttl就减去1,而内核每500ms调用ip_slowtimo一次。那么,如果系统在第一次接收到数据报的任一分片30秒后,还没有组装好一个完整的IP数据报,就丢弃该IP重装链表。重装定时器在链表被创建后的第一次调用ip_slowtimo时开始计时。

    重装时间一般推荐为60~120秒,并且当收到数据报的第一个分片且定时器超时后,向源主机发出一个ICMP超时差错报文。重装后,总是丢弃其它分片的首部和选项,并且在ICMP差错报文中必须包含出错数据报的前64bit数据。所以,如果内核还没收到分片0,它就不能发ICMP报文。

    c. 数据报标识符

368~375 ip_reass在分配给该数据报的ipq结构中保存ip_p、ip_id、ip_src和ip_dst,让ipq_next和ipq_pre指针指向该ipq结构,让q指向这个结构,并跳到insert(图22),把第一个分片ip插入到新的重装表中去。

    ip_reass的下一个部分(图18),此时fp已不为空了,然后为新的分片找到正确的插入位置。

图18 函数ip_reass:在重装链表中找位置

376~381 因为fp不为空,所以for循环搜索数据报的分片链表,找到一个偏移大于ip_off的分片。

    在目的主机上,分片包含的字节范围可能会相互覆盖。发生这种情况的原因是,当一个运输层协议重传某个数据报时,采用与原来数据报不同的路由;而且,分片的模式也可能不同,这就导致在目的主机上的相互覆盖;同时传输协议必须能强制IP使用原来的ID字段,这样才能使分片到达目的主机原先已存在(相同四元组)的重装列队中,才能识别该数据报可能是重传的。

    Net/3并不为运输层协议提供机制保证在重传的数据报中重用IP ID字段。在准备新数据时,在概说《TCP/IP详解 卷2》第8章 IP:网际协议图17中,ip_output通过增加全局整数ip_id来赋一个新值,尽管如此,Net/3系统也能从让运输层用相同IP字段重传的IP数据报的系统上接收重叠分片。

    图19说明分片可能以不同的方式与已到达的分片重叠。分片是按照它们到达目的主机的顺序编号的。重装的分片在图19底部显示,分片的阴影部分是被丢弃的多余字节。

图19 可能会在目的主机重叠的分片的字节范围

    图20中代码截断或者丢弃到达的分片。

图20 函数ip_reass:截断到达分组

382~396 ip_reass把新片中与早到分片末尾重叠的字节丢弃,截断重复的部分(图20中分片5的前部),或者如果新分片的所有字节已经在早先的分片中(分片4)出现,就丢弃整个新分片(分片6)。

    图21中的代码截断或者丢弃已有的分片。

图21 函数ip_reass:截断已有分组

397~412 如果当前分片部分地与早到分片的前端部分重叠,就把早到分片中重复的数据截掉(图19中分片2的前部)。丢弃所有与当前分片完全重叠的早到分片(分片3)。

    图22中,到达分片被插入到重装链表。

图22 函数ip_reass:插入分组

413~426 在截断后,ip_enq把该分片插入链表,并扫描链表,确定是否所有分片全部到达。如果还缺少分片,或者链表最后一个分片的ipf_mff被置位,ip_reass就返回0,并等待更多的分片。

    当目前的分片完成一个数据报后,整个链表被图23所示的代码转换成一个mbuf链。

图23 函数ip_reass:重装数据报

427~440 如果某个数据报的所有分片都被接收下来,while循环用m_cat把分片重新构造成数据报。

    图24显示了一个有三个分片的数据报的mbuf和ipq结构之间的关系。

图24 m_cat重装缓存内的分片

    图24中最暗的区域是分组的数据部分,稍淡的阴影部分是mbuf中未用的部分。有三个分片,每个分片都被存放在一个含有两个mbuf的链上:一个分组首部和一个簇。每个分片的第一个缓存上的m_data指针指向分组数据,而不是分组的首部。因此,由m_cat构造的缓存只包含分片的数据部分。

    当一个分片含有多于208字节时,情况通常是这样。缓存的“frag”部分是分片的IP首部。由于图15中代码中m_data指针后移了hlen,所以各缓存链第一个缓存的m_data指针指向“opt”之后。

    图25显示了用所有分片的缓存重装的数据报。注意,分片2和3的IP部分和选项不在重装的数据报里。

图25 重装的数据报

    第一个分片的首部仍然被用作ipasfrag结构。它被图26中的代码恢复成一个有效的IP数据报首部。

图26 函数ip_reass:数据报重装

    d. 重建数据报首部

441~456 ip_reass把ip指向链表的第一个分片,将ipasfrag结构恢复成ip结构:把数据报长度恢复成ip_len,源站地址恢复成ip_src,目的地址恢复成ip_dst;并把ipf_mff的低位清零。

    ip_reass用remque所整个分组从重装链表中移走,丢弃链表表头ipq结构,调整第一个缓存中的m_len和m_data,把前端被隐藏起来的第一个分片的首部和选项包含进来。

    e. 计算分组长度

457~464 此处的代码总是被执行,因为数据报的第一个缓存总是一个分组首部。for循环计算缓存链中的数据的字节数,并把值保存在m_pkthdr.len中。

    在选项类型字段中,copied比特的意义现在应该很明白了。因为目的主机只保留那些出现在第一个分片的中的选项,而且只有那些在分组去往目的主机的途中控制分组处理的选项才被复制下来。不复制那些在传送过程中收集信息的选项,因为当分组在目的主机上重装时,所有收集的信息都被丢弃了。

 

ip_slowtimo函数

    Net/3的各项协议可能指定每500ms调用一个函数。对IP而言,这个函数是ip_slowtimo,如图27所示,为重装链表上的分片计时。

图27 函数ip_slowtimo

515~534 ip_slowtimo遍历部分数据报的链表,减少重装TTL字段。当该字段减为0时,就调用ip_freef,把与该数据报相关的分片都丢弃。在splnet外运行ip_slowtimo,避免到达分组修改链表。

    ip_freef显示如图28所示。

图28 函数ip_freef

474~486 ip_freef移走并释放链表上的fp指向的各分片,然后释放链表本身。

    在第7章中,我们讲到IP把ip_drain定义成一个当内核需要更多内存时才调用的函数。这种情况通常发生在分配缓存时内存不够时。ip_drain显示如图29所示。

图29 函数ip_drain

538~545 IP释放内存的最简单办法就是丢弃重装链表上的所有IP分片。对属于某个TCP报文段的分片,TCP最终会重传该数据。属于UDP数据报的IP分片就丢失了,基于UDP的协议必须在应用程序层处理这种情况。

 

小结

    本文介绍了当一个外出数据报过大而不适合在选定的网络上发送时,ip_output如何对数据报分片。由于分片在向目的地传送的途中可能继续被分片,也有可能走不同的路径,所以只有目的主机才能组装原来的数据报。

    ip_reass接收到达的分片,并试图重装数据报。如果重装成功则传回ipintr,然后提交给相应的运输层。所有IP实现必须能够重装最多576字节的数据报。如果在一段合理时间内,分片未能重装成一个完整的数据报,ip_slowtimo就丢弃不完整的数据报。

 

更多最新文章尽在公众号:大白爱爬山,欢迎关注!

TCPIP协议详解2:实现 pdf版,有目录,完美阅读体验。 中文书名:TCP/IP详解 2:实现 英文书名:TCP/IP Illustrated, Volume 2: The Implementation 作者:(美) Gary R. Wright ,W. Richard Stevens 译者:陆雪莹、蒋慧 等译;谢希仁 校 ISBN:7-111-7567-6 16开,924页,78元 内容简介: 本书完整而详细地介绍了TCP/IP协议是如何实现的。书中给出了约500个图例,15 000行实际操作的C代码,采用举例教学的方法帮助你掌握TCP/IP实现。本书不仅说明了插口API协议族的关系以及主机实现与路由器实现的差别。还介绍了4.4BSD-Lite版的新的特点,如多播、长肥管道支持、窗口缩放、时间戳选项以及其他主题等等。读者阅读本书时,应当具备1中阐述的关于TCP/IP的基本知识。 本书针对任何希望理解TCP/IP协议是如何实现的读者设计;无论是编写网络应用的程序员,负责利用TCP/TP维护计算机系统网络的系统管理员,还是任何有兴趣理解大块非凡代码的普通读者;本书都是一本优秀的教科书。 作者简介: W.Richard Stevens(1951-1999),国际知名的UNIX网络专家,受人尊敬的作家。他的著作有《UNIX网络编程》(两本),《UNIX环境高级编程》,《TCP/IP详解》(三本)等,同时他还是广受欢迎的教师顾问。Stevens先生1951年生于赞比亚,早年,他就读于美国弗吉尼亚州的费什本军事学校,后获得密歇根大学学士、亚利桑那大学系统工程硕士博士学位。他曾就职于基特峰国家天文台,从事计算机编程。Stevens先生不幸病逝于1999年9月1日,他的离去是计算机界的巨大损失。 译、校者简介: 谢希仁,中国人民解放军理工大学(南京)计算机系教授,全军网络技术研究中心主任,博士研究生导师,1952年毕业于清华大学电机系电信专业。所编写的《计算机网络》于1992年获全国优秀教材奖。1999年再版的《计算机网络》第2版为普通高等教育“九五”国家级重点教材。近来还主持翻译了Comer写的《TCP/IP网际互联》计算机网络经典教材一套三本(电子工业出版社1998年出版),Harnedy写的《简单网络管理协议教程》(电子工业出版社1999年出版)。 陆雪莹,女,1973年1月出生。1994年7月毕业于南京通信工程学院无线通信专业,获工学学士学位。1997年2月于南京通信工程学院计算机软件专业毕业,并获硕士学位。1997年9月至今,任南京通信工程学院计算机教研室教员,同时于解放军理工大学攻读军事通信学博士学位,讲师职称,主要研究方向:智能化网络管理,计算机网络分布式处理。曾参加国家“863”项目,并参加编写专业著作2本,翻译专业著作3本,在各级学术刊物上发表论文5篇。 蒋慧,女,1973年2月出生。1995年毕业于南京通信工程学院计算机系,获计算机应用专业工学学士学位。1998年于南京通信工程学院计算机软件专业毕业,并获硕士学位。1998年9月至今,于解放军理工大学攻读博士学位。自1995年以来,在国内外重要学术刊物会议上发表8篇论文,其中2篇论文被IEEE国际会议录用。已出版3本有关网络的译作。目前从事软件需求工程、网络协议验证形式化方法以及函数式语言等方面的研究。 译者序: 我们愿意向广大的读者推荐W. Richard Stevens关于TCP/IP的经典著作(共3)的中译本。本书是其中的第2:《TCP/IP详解 2:实现》。 大家知道,TCP/IP已成为计算机网络的事实上的标准。在关于TCP/IP的论著中,最有影响的就是两部著作。一部是Douglas E. Comer写的《用TCP/IP进行网际互连》,一套共3(中译本已由电子工业出版社于1998年出版),而另一部就是Stevens写的这3书。这两套巨著都很有名,各有其特点。无论是从事计算机网络教学的教师还是进行科研的技术人员,这两套书都应当是必读的。 本书的特点是内容丰富,概念清楚且准确,讲解详细,例子很多。作者在书中举出的所有例子均在作者安装的计算机网络上通过实际验证。各都留有一定数量的习题。在附录A作者对部分习题给出了解答。在本书的最后,作者给出了许多经典的参考文献,并一一写出了评论。 第2是第1的继续深入。读者在学习这一时,应当先具备第1所阐述的关于TCP/IP的基本知识。本的特点是使用大量的源代码来讲述TCP/IP协议族中的各协议是怎样实现的。这些内容对于编写TCP/IP网络应用程序的程序员负责维护基于TCP/IP协议的计算机网络的系统管理员来说,应当是必读的。 参加本书翻译的有:谢钧(序言第1~第7),蒋慧(第8~第14,第22~第23),吴礼发(第15~第17),端义峰(第18~第19),胥光辉(第20~第21)陆雪莹(第24~第32以及全部附录)。全书由谢希仁教授审校。 限于水平,翻译中不妥或错误之处在所难免,敬请广大读者批评指正。 目录: 前言 第1 概述 1 1.1 引言 1 1.2 源代码表示 1 1.2.1 将拥塞窗口设置为1 1 1.2.2 印刷约定 2 1.3 历史 2 1.4 应用编程接口 3 1.5 程序示例 4 1.6 系统调用库函数 6 1.7 网络实现概述 6 1.8 描述符 7 1.9 mbuf与输出处理 11 1.9.1 包含插口地址结构的mbuf 11 1.9.2 包含数据的mbuf 12 1.9.3 添加IPUDP首部 13 1.9.4 IP输出 14 1.9.5 以太网输出 14 1.9.6 UDP输出小结 14 1.10 输入处理 15 1.10.1 以太网输入 15 1.10.2 IP输入 15 1.10.3 UDP输入 16 1.10.4 进程输入 17 1.11 网络实现概述(续) 17 1.12 中断级别与并发 18 1.13 源代码组织 20 1.14 测试网络 21 1.15 小结 222 mbuf:存储器缓存 24 2.1 引言 24 2.2 代码介绍 27 2.2.1 全局变量 27 2.2.2 统计 28 2.2.3 内核统计 28 2.3 mbuf的定义 29 2.4 mbuf结构 29 2.5 简单的mbuf宏函数 31 2.5.1 m_get函数 32 2.5.2 MGET宏 32 2.5.3 m_retry函数 33 2.5.4 mbuf锁 34 2.6 m_devgetm_pullup函数 34 2.6.1 m_devget函数 34 2.6.2 mtoddtom宏 36 2.6.3 m_pullup函数连续的协议首部 36 2.6.4 m_pullupIP分片与重组 37 2.6.5 TCP重组避免调用m_pullup 39 2.6.6 m_pullup使用总结 40 2.7 mbuf宏函数的小结 40 2.8 Net/3联网数据结构小结 42 2.9 m_copy簇引用计数 43 2.10 其他选择 47 2.11 小结 47 第3 接口层 49 3.1 引言 49 3.2 代码介绍 49 3.2.1 全局变量 49 3.2.2 SNMP变量 50 3.3 ifnet结构 51 3.4 ifaddr结构 57 3.5 sockaddr结构 58 3.6 ifnet与ifaddr的专用化 59 3.7 网络初始化概述 60 3.8 以太网初始化 61 3.9 SLIP初始化 64 3.10 环回初始化 65 3.11 if_attach函数 66 3.12 ifinit函数 72 3.13 小结 73 第4 接口:以太网 74 4.1 引言 74 4.2 代码介绍 75 4.2.1 全局变量 75 4.2.2 统计量 75 4.2.3 SNMP变量 76 4.3 以太网接口 77 4.3.1 leintr函数 79 4.3.2 leread函数 79 4.3.3 ether_input函数 81 4.3.4 ether_output函数 84 4.3.5 lestart函数 87 4.4 ioctl系统调用 89 4.4.1 ifioctl函数 90 4.4.2 ifconf函数 91 4.4.3 举例 94 4.4.4 通用接口ioctl命令 95 4.4.5 if_downif_up函数 96 4.4.6 以太网、SLIP环回 97 4.5 小结 98 第5 接口:SLIP环回 100 5.1 引言 100 5.2 代码介绍 100 5.2.1 全局变量 100 5.2.2 统计量 101 5.3 SLIP接口 101 5.3.1 SLIP线路规程:SLIPDISC 101 5.3.2 SLIP初始化:slopenslinit 103 5.3.3 SLIP输入处理:slinput 105 5.3.4 SLIP输出处理:sloutput 109 5.3.5 slstart函数 111 5.3.6 SLIP分组丢失 116 5.3.7 SLIP性能考虑 117 5.3.8 slclose函数 117 5.3.9 sltioctl函数 118 5.4 环回接口 119 5.5 小结 121 第6 IP编址 123 6.1 引言 123 6.1.1 IP地址 123 6.1.2 IP地址的印刷规定 123 6.1.3 主机路由器 124 6.2 代码介绍 125 6.3 接口地址小结 125 6.4 sockaddr_in结构 126 6.5 in_ifaddr结构 127 6.6 地址指派 128 6.6.1 ifioctl函数 130 6.6.2 in_control函数 130 6.6.3 前提条件:SIOCSIFADDR、 SIOCSIFNETMASK SIOCSIFDSTADDR 132 6.6.4 地址指派:SIOCSIFADDR 133 6.6.5 in_ifinit函数 133 6.6.6 网络掩码指派:SIOCSIFNETMASK 136 6.6.7 目的地址指派:SIOCSIFDSTADDR 137 6.6.8 获取接口信息 137 6.6.9 每个接口多个IP地址 138 6.6.10 附加IP地址:SIOCAIFADDR 139 6.6.11 删除IP地址:SIOCDIFADDR 140 6.7 接口ioctl处理 141 6.7.1 leioctl函数 141 6.7.2 slioctl函数 142 6.7.3 loioctl函数 143 6.8 Internet实用函数 144 6.9 ifnet实用函数 144 6.10 小结 145 第7协议 146 7.1 引言 146 7.2 代码介绍 146 7.2.1 全局变量 147 7.2.2 统计量 147 7.3 domain结构 147 7.4 protosw结构 148 7.5 IP 的domainprotosw结构 150 7.6 pffindprotopffindtype函数 155 7.7 pfctlinput函数 157 7.8 IP初始化 157 7.8.1 Internet传输分用 157 7.8.2 ip_init函数 158 7.9 sysctl系统调用 159 7.10 小结 161 第8 IP:网际协议 162 8.1 引言 162 8.2 代码介绍 163 8.2.1 全局变量 163 8.2.2 统计量 163 8.2.3 SNMP变量 164 8.3 IP分组 165 8.4 输入处理:ipintr函数 167 8.4.1 ipintr概观 167 8.4.2 验证 168 8.4.3 转发或不转发 171 8.4.4 重装分用 173 8.5 转发:ip_forward函数 174 8.6 输出处理:ip_output函数 180 8.6.1 首部初始化 181 8.6.2 路由选择 182 8.6.3 源地址选择分片 184 8.7 Internet检验:in_cksum函数 186 8.8 setsockoptgetsockopt系统调用 190 8.8.1 PRCO_SETOPT的处理 192 8.8.2 PRCO_GETOPT的处理 193 8.9 ip_sysctl函数 193 8.10 小结 194 第9 IP选项处理 196 9.1 引言 196 9.2 代码介绍 196 9.2.1 全局变量 196 9.2.2 统计量 197 9.3 选项格式 197 9.4 ip_dooptions函数 198 9.5 记录路由选项 200 9.6 源站记录路由选项 202 9.6.1 save_rte函数 205 9.6.2 ip_srcroute函数 206 9.7 时间戳选项 207 9.8 ip_insertoptions函数 210 9.9 ip_pcbopts函数 214 9.10 一些限制 217 9.11 小结 217 第10 IP分片重装 218 10.1 引言 218 10.2 代码介绍 219 10.2.1 全局变量 220 10.2.2 统计量 220 10.3 分片 220 10.4 ip_optcopy函数 223 10.5 重装 224 10.6 ip_reass函数 227 10.7 ip_slowtimo函数 237 10.8 小结 238 第11 ICMP:Internet控制报文协议 239 11.1 引言 239 11.2 代码介绍 242 11.2.1 全局变量 242 11.2.2 统计量 242 11.2.3 SNMP变量 243 11.3 icmp结构 244 11.4 ICMP 的protosw结构 245 11.5 输入处理:icmp_input函数 246 11.6 差错处理 249 11.7 请求处理 251 11.7.1 回显询问:ICMP_ECHO ICMP_ECHOREPLY 252 11.7.2 时间戳询问:ICMP_TSTAMP ICMP_TSTAMPREPLY 253 11.7.3 地址掩码询问:ICMP_MASKREQ ICMP_MASKREPLY 253 11.7.4 信息询问:ICMP_IREQICMP_ IREQREPLY 255 11.7.5 路由器发现:ICMP_ROUTERADVERT ICMP_ROUTERSOLICIT 255 11.8 重定向处理 255 11.9 回答处理 257 11.10 输出处理 257 11.11 icmp_error函数 258 11.12 icmp_reflect函数 261 11.13 icmp_send函数 265 11.14 icmp_sysctl函数 266 11.15 小结 266 第12 IP多播 268 12.1 引言 268 12.2 代码介绍 269 12.2.1 全局变量 270 12.2.2 统计量 270 12.3 以太网多播地址 270 12.4 ether_multi结构 271 12.5 以太网多播接收 273 12.6 in_multi结构 273 12.7 ip_moptions结构 275 12.8 多播的插口选项 276 12.9 多播的TTL值 277 12.9.1 MBONE 278 12.9.2 扩展环搜索 278 12.10 ip_setmoptions函数 278 12.10.1 选择一个明确的多播接口:IP_ MULTICAST_IF 280 12.10.2 选择明确的多播TTL: IP_ MULTICAST_TTL 281 12.10.3 选择多播环回:IP_MULTICAST_ LOOP 281 12.11 加入一个IP多播组 282 12.11.1 in_addmulti函数 285 12.11.2 slioctlloioctl函数:SIOCADDMULTISIOCDELMULTI 287 12.11.3 leioctl函数:SIOCADDMULTI SIOCDELMULTI 288 12.11.4 ether_addmulti函数 288 12.12 离开一个IP多播组 291 12.12.1 in_delmulti函数 292 12.12.2 ether_delmulti函数 293 12.13 ip_getmoptions函数 295 12.14 多播输入处理:ipintr函数 296 12.15 多播输出处理:ip_output函数 298 12.16 性能的考虑 301 12.17 小结 301 第13 IGMP:Internet组管理协议 303 13.1 引言 303 13.2 代码介绍 304 13.2.1 全局变量 304 13.2.2 统计量 304 13.2.3 SNMP变量 305 13.3 igmp结构 305 13.4 IGMP的protosw的结构 306 13.5 加入一个组:igmp_joingroup函数 306 13.6 igmp_fasttimo函数 308 13.7 输入处理:igmp_input函数 311 13.7.1 成员关系查询:IGMP_HOST_ MEMBERSHIP_QUERY 312 13.7.2 成员关系报告:IGMP_HOST_ MEMBERSHIP_REPORT 313 13.8 离开一个组:igmp_leavegroup函数 314 13.9 小结 315 第14 IP多播选路 316 14.1 引言 316 14.2 代码介绍 316 14.2.1 全局变量 316 14.2.2 统计量 317 14.2.3 SNMP变量 317 14.3 多播输出处理(续) 317 14.4 mrouted守护程序 318 14.5 虚拟接口 321 14.5.1 虚拟接口表 322 14.5.2 add_vif函数 324 14.5.3 del_vif函数 326 14.6 IGMP(续) 327 14.6.1 add_lgrp函数 328 14.6.2 del_lgrp函数 329 14.6.3 grplst_member函数 330 14.7 多播选路 331 14.7.1 多播选路表 334 14.7.2 del_mrt函数 335 14.7.3 add_mrt函数 336 14.7.4 mrtfind函数 337 14.8 多播转发:ip_mforward函数 338 14.8.1 phyint_send函数 343 14.8.2 tunnel_send函数 344 14.9 清理:ip_mrouter_done函数 345 14.10 小结 346 第15 插口层 348 15.1 引言 348 15.2 代码介绍 349 15.3 socket结构 349 15.4 系统调用 354 15.4.1 举例 355 15.4.2 系统调用小结 355 15.5 进程、描述符插口 357 15.6 socket系统调用 358 15.6.1 socreate函数 359 15.6.2 超级用户特权 361 15.7 getsocksockargs函数 361 15.8 bind系统调用 363 15.9 listen系统调用 364 15.10 tsleepwakeup函数 365 15.11 accept系统调用 366 15.12 sonewconnsoisconnected 函数 369 15.13 connect系统调用 372 15.13.1 soconnect函数 374 15.13.2 切断无连接插口外部地址的 关联 375 15.14 shutdown系统调用 375 15.15 close系统调用 377 15.15.1 soo_close函数 377 15.15.2 soclose函数 378 15.16 小结 380 第16 插口I/O 381 16.1 引言 381 16.2 代码介绍 381 16.3 插口缓存 381 16.4 write、writev、sendtosendmsg 系统调用 384 16.5 sendmsg系统调用 387 16.6 sendit函数 388 16.6.1 uiomove函数 389 16.6.2 举例 390 16.6.3 sendit代码 391 16.7 sosend函数 392 16.7.1 可靠的协议缓存 393 16.7.2 不可靠的协议缓存 393 16.7.3 sosend函数小结 401 16.7.4 性能问题 401 16.8 read、readv、recvfromrecvmsg 系统调用 401 16.9 recvmsg系统调用 402 16.10 recvit函数 403 16.11 soreceive函数 405 16.11.1 带外数据 406 16.11.2 举例 406 16.11.3 其他的接收操作选项 407 16.11.4 接收缓存的组织:报文边界 407 16.11.5 接收缓存的组织:没有报文边界 408 16.11.6 控制信息带外数据 409 16.12 soreceive代码 410 16.13 select系统调用 421 16.13.1 selscan函数 425 16.13.2 soo_select函数 425 16.13.3 selrecord函数 427 16.13.4 selwakeup函数 428 16.14 小结 429 第17 插口选项 431 17.1 引言 431 17.2 代码介绍 431 17.3 setsockopt系统调用 432 17.4 getsockopt系统调用 437 17.5 fcntlioctl系统调用 440 17.5.1 fcntl代码 441 17.5.2 ioctl代码 443 17.6 getsockname系统调用 444 17.7 getpeername系统调用 445 17.8 小结 447 第18 Radix树路由表 448 18.1 引言 448 18.2 路由表结构 448 18.3 选路插口 456 18.4 代码介绍 456 18.4.1 全局变量 458 18.4.2 统计量 458 18.4.3 SNMP变量 459 18.5 Radix结点数据结构 460 18.6 选路结构 463 18.7 初始化:route_initrtable_init 函数 465 18.8 初始化:rn_initrn_inithead 函数 468 18.9 重复键掩码列表 471 18.10 rn_match函数 473 18.11 rn_search函数 480 18.12 小结 481 第19 选路请求选路消息 482 19.1 引言 482 19.2 rtallocrtalloc1函数 482 19.3 宏RTFREErtfree函数 484 19.4 rtrequest函数 486 19.5 rt_setgate函数 491 19.6 rtinit函数 493 19.7 rtredirect函数 495 19.8 选路消息的结构 498 19.9 rt_missmsg函数 501 19.10 rt_ifmsg函数 503 19.11 rt_newaddrmsg函数 504 19.12 rt_msg1函数 505 19.13 rt_msg2函数 507 19.14 sysctl_rtable函数 510 19.15 sysctl_dumpentry函数 514 19.16 sysctl_iflist函数 515 19.17 小结 517 第20 选路插口 518 20.1 引言 518 20.2 routedomainprotosw结构 518 20.3 选路控制块 519 20.4 raw_init函数 520 20.5 route_output函数 520 20.6 rt_xaddrs函数 530 20.7 rt_setmetrics函数 531 20.8 raw_input函数 532 20.9 route_usrreq函数 534 20.10 raw_usrreq函数 535 20.11 raw_attach、raw_detachraw_disconnect函数 539 20.12 小结 540 第21 ARP:地址解析协议 542 21.1 介绍 542 21.2 ARP路由表 542 21.3 代码介绍 544 21.3.1 全局变量 544 21.3.2 统计量 544 21.3.3 SNMP变量 546 21.4 ARP结构 546 21.5 arpwhohas函数 548 21.6 arprequest函数 548 21.7 arpintr函数 551 21.8 in_arpinput函数 552 21.9 ARP定时器函数 557 21.9.1 arptimer函数 557 21.9.2 arptfree函数 557 21.10 arpresolve函数 558 21.11 arplookup函数 562 21.12 代理ARP 563 21.13 arp_rtrequest函数 564 21.14 ARP多播 569 21.15 小结 570 第22 协议控制块 572 22.1 引言 572 22.2 代码介绍 573 22.2.1 全局变量 574 22.2.2 统计量 574 22.3 inpcb的结构 574 22.4 in_pcballocin_pcbdetach函数 575 22.5 绑定、连接分用 577 22.6 in_pcblookup函数 581 22.7 in_pcbbind函数 584 22.8 in_pcbconnect函数 589 22.9 in_pcbdisconnect函数 594 22.10 in_setsockaddrin_setpeeraddr 函数 595 22.11 in_pcbnotify、in_rtchangein_losing函数 595 22.11.1 in_rtchange函数 598 22.11.2 重定向原始插口 599 22.11.3 ICMP差错UDP插口 600 22.11.4 in_losing函数 601 22.12 实现求精 602 22.13 小结 60223 UDP:用户数据报协议 605 23.1 引言 605 23.2 代码介绍 605 23.2.1 全局变量 606 23.2.2 统计量 606 23.2.3 SNMP变量 607 23.3 UDP 的protosw结构 607 23.4 UDP的首部 608 23.5 udp_init函数 609 23.6 udp_output函数 609 23.6.1 在前面加上IP/UDP首部mbuf簇 612 23.6.2 UDP检验计算伪首部 612 23.7 udp_input函数 616 23.7.1 对收到的UDP数据报的一般确认 616 23.7.2 分用单播数据报 619 23.7.3 分用多播广播数据报 622 23.7.4 连接上的UDP插口多接口主机 625 23.8 udp_saveopt函数 625 23.9 udp_ctlinput函数 627 23.10 udp_usrreq函数 628 23.11 udp_sysctl函数 633 23.12 实现求精 633 23.12.1 UDP PCB高速缓存 633 23.12.2 UDP检验 634 23.13 小结 635 第24 TCP:传输控制协议 636 24.1 引言 636 24.2 代码介绍 636 24.2.1 全局变量 636 24.2.2 统计量 637 24.2.3 SNMP变量 640 24.3 TCP 的protosw结构 641 24.4 TCP的首部 641 24.5 TCP的控制块 643 24.6 TCP的状态变迁图 645 24.7 TCP的序号 646 24.8 tcp_init函数 650 24.9 小结 65225 TCP的定时器 654 25.1 引言 654 25.2 代码介绍 655 25.3 tcp_canceltimers函数 657 25.4 tcp_fasttimo函数 657 25.5 tcp_slowtimo函数 658 25.6 tcp_timers函数 659 25.6.1 FIN_WAIT_22MSL定时器 660 25.6.2 持续定时器 662 25.6.3 连接建立定时器保活定时器 662 25.7 重传定时器的计算 665 25.8 tcp_newtcpcb算法 666 25.9 tcp_setpersist函数 668 25.10 tcp_xmit_timer函数 669 25.11 重传超时:tcp_timers函数 673 25.11.1 慢起动避免拥塞 675 25.11.2 精确性 677 25.12 一个RTT的例子 677 25.13 小结 679 第26 TCP输出 680 26.1 引言 680 26.2 tcp_output概述 680 26.3 决定是否应发送一个报文段 682 26.4 TCP选项 691 26.5 窗口大小选项 692 26.6 时间戳选项 692 26.6.1 哪个时间戳需要回显,RFC1323 算法 694 26.6.2 哪个时间戳需要回显,正确的 算法 695 26.6.3 时间戳与延迟ACK 695 26.7 发送一个报文段 696 26.8 tcp_template函数 707 26.9 tcp_respond函数 708 26.10 小结 71027 TCP的函数 712 27.1 引言 712 27.2 tcp_drain函数 712 27.3 tcp_drop函数 712 27.4 tcp_close函数 713 27.4.1 路由特性 713 27.4.2 资源释放 716 27.5 tcp_mss函数 717 27.6 tcp_ctlinput函数 722 27.7 tcp_notify函数 723 27.8 tcp_quench函数 724 27.9 TCP_REASS宏tcp_reass函数 724 27.9.1 TCP_REASS宏 725 27.9.2 tcp_reass函数 727 27.10 tcp_trace函数 732 27.11 小结 736 第28 TCP的输入 737 28.1 引言 737 28.2 预处理 739 28.3 tcp_dooptions函数 745 28.4 首部预测 747 28.5 TCP输入:缓慢的执行路径 752 28.6 完成被动打开或主动打开 752 28.6.1 完成被动打开 753 28.6.2 完成主动打开 756 28.7 PAWS:防止序号回绕 760 28.8 裁剪报文段使数据在窗口内 762 28.9 自连接同时打开 768 28.10 记录时间戳 770 28.11 RST处理 770 28.12 小结 77229 TCP的输入(续) 773 29.1 引言 773 29.2 ACK处理概述 773 29.3 完成被动打开同时打开 774 29.4 快速重传快速恢复的算法 775 29.5 ACK处理 778 29.6 更新窗口信息 784 29.7 紧急方式处理 786 29.8 tcp_pulloutofband函数 788 29.9 处理已接收的数据 789 29.10 FIN处理 791 29.11 最后的处理 793 29.12 实现求精 795 29.13 首部压缩 795 29.13.1 引言 796 29.13.2 首部字段的压缩 799 29.13.3 特殊情况 801 29.13.4 实例 802 29.13.5 配置 803 29.14 小结 803 第30 TCP的用户需求 805 30.1 引言 805 30.2 tcp_usrreq函数 805 30.3 tcp_attach函数 814 30.4 tcp_disconnect函数 815 30.5 tcp_usrclosed函数 816 30.6 tcp_ctloutput函数 817 30.7 小结 820 第31 BPF:BSD 分组过滤程序 821 31.1 引言 821 31.2 代码介绍 821 31.2.1 全局变量 821 31.2.2 统计量 822 31.3 bpf_if结构 822 31.4 bpf_d结构 825 31.4.1 bpfopen函数 826 31.4.2 bpfioctl函数 827 31.4.3 bpf_setif函数 830 31.4.4 bpf_attachd函数 831 31.5 BPF的输入 832 31.5.1 bpf_tap函数 832 31.5.2 catchpacket函数 833 31.5.3 bpfread函数 835 31.6 BPF的输出 837 31.7 小结 838 第32 原始IP 839 32.1 引言 839 32.2 代码介绍 839 32.2.1 全局变量 839 32.2.2 统计量 840 32.3 原始 IP的protosw结构 840 32.4 rip_init函数 842 32.5 rip_input函数 842 32.6 rip_output函数 844 32.7 rip_usrreq函数 846 32.8 rip_ctloutput函数 850 32.9 小结 852 结束语 853 附录A 部分习题的解答 854 附录B 源代码的获取 872 附录C RFC 1122 的有关内容 874 参考文献 895
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值