概说《TCP/IP详解 卷2》第8章 IP:网际协议

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

本文要点

  • 引言

  • IP分组

  • 输入处理:ipintr函数

    • ipintr概述

    • 验证

    • 转发或不转发

    • 重装和分用

  • 转发:ip_forward函数

  • 转出处理:ip_output函数

    • 首部初始化

    • 路由选择

    • 源地址选择和分片

  • Internet检验和:in_cksum函数

  • setsockopt和getsockopt系统调用

  • ip_sysctl函数

 

引言

    本文主要介绍IP分组的结构和基本的IP处理过程,包括输入、转发和输出。图1显示了IP层常见的组织形式。

图1 IP层的处理

    在第4章中,我们已经介绍网络接口如何把到达的IP分组放到IP输入队列ipintrq中,并如何调用一个软件中断。因为硬件中断的优先级比软件中断的要高,所以在发生一次软件中断之前,有的分组可能会被放到队列中。在软件中断处理中,ipintr函数不断从ipintrq中移走和处理分组,直到队列为空。在最终的目的地,IP把分组重装为数据报,并通过函数调用把该数据报传给适当的运输层协议。如果分组没有到达最后的目的地,并且如果主机被配置成一个路由器,则IP把分组传给ip_forward。传输协议和ip_forward把要输出的分组传给ip_output,由ip_output完成IP首部、选择输出接口以及在必要时对分组分片。最终的分组被传给合适的网络接口输出函数。

    当产生差错时,IP丢弃分组,并在某些条件下向分组的源站发出一个差错报文。这些报文是ICMP的一部分。Net/3通过调用icmp_error发出ICMP差错报文,icmp_error接收一个mbuf,其中包括差错分组、发现的差错类型以及一个选项码,提供依赖于差错类型的附加信息。

 

IP分组

    为了更准确的讨论Internet协议处理,首先必须理解一些名词定义,图2显示了在不同Internet层之间传递数据时用来描述数据的名词。

图2 帧、分组、分片、数据报和报文

    我们把传输层协议交给IP网络层的数据称为报文。典型的报文包含一个运输层首部和应用程序数据。图2所示的传输协议是UDP。IP在报文的首部前加上它自己的首部形成一个数据报。如果在选定的网络中,数据报的长度太大,IP就把数据报分裂成几个分片,每个分片中含有它自己的IP首部和一段原来数据报的数据。图2显示了一个数据报被分成三个分片。

    当提交给数据链路层进行传送时,一个IP分片或者一个很小的无需分片的IP数据报称为分组。数据链路层在分组前面加上它自己的首部,并发送得到的帧。

    IP只考虑它自己加上IP首部,对报文本身既不检查也不修改(除非进行分片)。图3显示了IP首部的结构。

图3 IP数据报

    图3包括ip结构(图4)中各成员的名字,Net/3通过该结构访问IP首部。

图4 ip结构

47~67 因为在存储器中,比特字段的物理顺序根据机器和编译器的不同而不同,所以由#ifs保证编译器按照IP标准排列结构成员。从而,当Net/3把一个ip结构覆盖到存储器中的一个IP分组上时,结构成员能够访问到分组中正确的比特。

    IP分组的格式由版本ip_v指定,通常为4;首部长度ip_hl,通常以4字节单元度量;分组长度ip_len以字节为单位度量;传输协议ip_p生成分组内数据;ip_sum是检验和,检测在发送中首部的变化。

    标准的IP首部长度是20字节,所以ip_hl必须大于或者等于5。大于5表示IP选项紧跟在标准首部后。ip_hl的最大值为15(2的4次方减1),允许最多40个字节的选项(20+40=60)。IP数据报的最大长度为65535(2的16次方减1),因为ip_len是一个16bit的字段。图5显示了一个完整的IP分组构成。

图5 有选项的IP分组构成

    因为ip_hl是以4字节为单元计算的,所以IP选项必须常常被填充成4字节的倍数。

 

输入处理:ipintr函数

    当接口把分组放到ipintrq上排队后,通过schednetisr调用一个软中断。当该软中断发生时,如果IP处理过程已经由schednetisr调度,则内核调用ipintr。在调用ipintr之前,CPU的优先级被改变成splnet。

1. ipintr概述

ipintr是一个比较大的函数,我们将分4个部分讨论:a. 对到达分组验证;b. 选项处理及转发;c. 分组重装;d. 分用。在ipintr中发生分组的重装,比较复杂,将在后续章节讨论。图6显示了ipintr的整体结构。

图6 ipintr函数整体结构

100~117 标号next标识主要的分组处理循环开始。ipintr从ipintrq中移走分组,并对之加以处理直到整个队列为空。如果到函数最后控制失败,goto把控制权传回给next中最上面的函数。ipintr把分组阻塞在splimp内,避免当它访问队列时,运行网络的中断程序(例如slinput和ether_input)。

332~336 标号bad标识由于释放相关mbuf并且返回到next中处理循环开始而自动丢弃分组。在整个ipintr中,都是跳到bad来处理差错。

2. 验证

    从图7开始,把分组从ipintrq中取出,验证它们的内容,损坏和有差错的分组自动被丢弃。

图7 ipintr函数:验证

    a. IP版本

118~134 如果in_ifaddr表为空,则该网络接口没有指派IP地址,ipintr必须丢弃所有的IP分组;没有地址,ipintr就无法决定该分组是否要到该系统。

    在ipintr访问任何IP首部之前,它必须证实ip_v是4。当m的数据长度小于结构ip的长度时,调用m_pullup函数,将ip首部放在同一个mbuf中,即一段连续的存储区。

135~146 下面的步骤保证IP首部(包括选项)位于一段连续的存储器缓存区上:

  • 如果在第一个mbuf中的数据小于一个标准的IP首部(20字节),m_pullup会重新把标准商务部放到一个连续的存储器缓存区上去。链路层不太可能把最大的(60字节)IP首部分在两个mbuf中从而使用上面的m_pullup。

  • ip_hl通过乘以4得到首部字节长度,并将其保存在hlen中。

  • 如果IP分组首部的字节数长度hlen小于标准首部(20字节),将是无效分组并被丢弃。

  • 如果整个首部仍不在第一个mbuf中(也就是说,分组包含了IP选项),则由m_pullup完成其任务。同样,这不一定是必须的。

    检验和计算是所有Internet协议的重要组成。所有的协议均使用相同的算法(由函数in_cksum完成),但应用于分组的不同部分。对于IP来说,检验和只保证IP首部(以及选项)。对于传输协议,如UDP或TCP,检验和覆盖了分组的数据部分和运输层首部。

    b. IP检验和

147~150 ipintr把由in_cksum计算出来的检验和保存首部的ip_sum字段中。一个未被破坏的首部应该具有0检验和。如果结构非0,则该分组被丢弃。

    c. 字节顺序

151~160 Internet标准在指定协议首部中多字节整数值的字节顺序时非常小心。NTOHS把IP首部中所有16bit的值从网络字节序列转换主机字节序列:分组长度(ip_len),数据报标识(ip_id)和分片偏移(ip_off)。如果两种格式相同,则NTOHS是一个空的宏。在这里就转换成主机字节序列,以避免Net/3每次检验该字段时必须进行一次转换。

    d. 分组长度

161~177 如果分组的逻辑长度(ip_len)比储存在mbuf中的数据量(m_pkthdr.len)大,说明有些字节被丢失了,此时丢弃该分组。如果mbuf比分组大,则去掉多余的字节。

    现在,有了完整的IP首部,分组的逻辑长度和物理长度相同,检验和表明分组的首部无损地到达。

3. 转发或不转发

    图8显示了ipintr的下一部分,调用ip_dooptions来处理IP选项,然后决定分组是否到达它最后的目的地。如果分组没有到达最后的目的地,Net/3会尝试转发该分组(如果系统被配置成路由器)。如果分组到达最后目的地,就被交付给合适的运输层协议。

图8 ipintr函数:是否转发

    a. 选项处理

178~186 通过对ip_nhops清零,丢掉前一个分组的原路由。如果分组首部大于默认首部长度,则它必然包含由ip_dooptions处理的选项。如果ip_dooptions返回0,ipintr将继续处理该分组;否则,ip_dooptions通过转发或丢弃分组完成对该分组的处理,ipintr可以处理输入队列中的下一个分组。我们将在第9章进一步讨论选项处理。

    处理完选项后,ipintr通过把IP首部内的ip_dst与配置的所有本接口的IP地址比较,以决定分组是否已到达最终目的地。ipintr必须考虑与接口相关的几个广播地址、一个或多个单播地址以及做生意个多播地址。

    b. 最终目的地

187~261 ipintr通过遍历in_ifaddr(概说《TCP/IP详解 卷2》第6章 IP编址图3所示),由配置好的Internet地址表,来决定是否与分组的目的地址匹配。对in_ifaddr列表中的每个in_ifaddr结构进行一系列的比较。要考虑4种常见的情况:

  • 与某个接口地址完全匹配(图9第一行)

  • 与某个接口相关的广播地址匹配(图9中间四行)

  • 与某个接口相关的多播组之一的匹配

  • 与两个受限的广播地址之一匹配(图9最后两行)

    图9显示的是当分组到达我们的示例网络里的主机sun上的以太网接口时要测试的地址(ip多播地址除外)。

图9 为判断分组是否到达最终目的地进行的比较

    c. 转发

262~271 如果ip_dst与所有地址都不匹配,分组还没到达最终目的地。如果还没有设置ipforwarding,就丢弃分组。否则,ip_forward尝试把分组路由到它的最终目的地。

    当分组到达的某个地址不是目的地址指定的接口时,主机会丢掉该分组。在这种情况下,Net/3将搜索整个in_ifaddr列表;只考虑那些分配给接收接口地地址,这种方式称为强端系统模型。与之相对的是称为弱端系统模型。

4. 重装和分用

    最后,ipintr的最后一部分代码如图10所示,在这里进行重装和分用。我们略去了重装代码,推迟到第10章讨论。当无法重装完全的数据报时,略去的代码将指针ip设为空;否则ip指向一个已经到达目的地的完整数据报。

图10 ipintr函数

325~332 数据报中指定的协议被ip_p用ip_protox数组映射到inetsw数组的下标。ipintr调用选定的protosw结构中的pr_input函数来处理数据报包含的运输报文。当pr_input返回时,ipintr继续处理ipintrq中的下一个分组。

    注意,运输层对分组的处理发生在ipintr处理循环内部。在IP和传输协议之间没有到达分组的排队。

 

转发:ip_forward函数

    到达非最终目的地的系统的分组需要被转发。只有当ipforwarding非零或者当分组中包含源路由(第9章介绍)时,ipintr才调用实现转发算法的ip_forward函数。当分组中包含源路由时,ip_dooptions调用ip_forward,并且第2个参数srcrt设为1.

    ip_forward通过图11显示了route结构与路由表接口。

图11 route结构

46~49 route结构有两个成员:ro_rt,指向rtentry结构的指针;ro_dst,一个socketaddr结构,指定与ro_rt所指的路由项相关的目的地。目的地是在内核的路由表中用来查找路由信息的关键字。第18章对rtentry结构和路由表进行详细讨论。

    我们分两个部分讨论ip_forward。第一部分确定允许系统转发分组,修改IP首部,并为分组选择路由;第二部分处理ICMP重定向报文,并把分组交给ip_output进行发送。代码如图12所示。

 

图12 ip_forward函数:路由选择

    a. 分组适合转发吗

867~871 ip_forward的第1个参数是指向一个mbuf链的指针,该mubf中包含了要被转发的分组。如果第2个参数非0,则分组由于源路由选项正在被转发。

879~884 if语句识别并丢弃以下分组

  • 链路层广播

    任何支持广播的网络接口驱动器必须为收到的广播分组把M_BCASH标志置位。如果分组寻址到以太网广播地址,则ether_input就把M_CAST置位。不转发链路层的广播分组。

  • 环回分组

    对寻址到环回网络的分组,in_canforward返回0。这些分组将被ipintr提交给ip_forward,因为没有正确配置反馈接口。

  • 网络0和E类地址

    对于这些分组,in_canforward返回0。这些目的地址是无效的,而且因为没有主机接收这些分组,所以它们不应该继续在网络中流动。

  • D类地址

    寻址到D类地址的分组应该由多播函数ip_mforward而不是由ip_forward处理。in_canforward拒绝D类(多播)地址。

    处理分组的所有系统都必须把生存时间(TTL)字段至少减去1,即使TTL是以秒计算的。由于这个要求,TTL通常被认为是对IP分组在被丢掉之前能经过的跳的个数。

    b. 减小TTL

885~890 由于转发时不再需要分组的标识符,所以标识符又被转换回网络字节序列。但是当ip_forward发送包含无效IP首部的ICMP差错报文时,分组的标识符又应该是正确的顺序。

    如果ip_ttl达到1(IPTTLDEC),则向发送发返回一个ICMP超时报文,并丢掉该分组。否则,ip_forward把ip_ttl减去IPTTLDEC(先判断后减1)。

    c. 定位下一跳

891~907 IP转发算法把最近的路由缓存在全局route结构的ipforward_rt中,有可能时应用于当前分组。研究表明连续分组趋向于同一目的地址,所以这种向后一个的缓存使得路由查询的次数减少。如果缓存为空(ipforward_rt)或者当前分组的目的地不是ipforward_rt中的路由,就取消前面的路由,ro_dst被初始化成新的目的地,rtalloc为当前的目的地找一个新路由。如果找不到路由,则返回一个ICMP主机不可达差错,并丢弃该分组。

908~914 由于在产生差错时,ip_output要丢掉分组,所以m_copy复制分组的前64个字节,以便ip_forward发送ICMP差错报文。如果调用m_copy失败,ip_forward并不终止。在这个情况下不发送差错报文。ip_ifmatrix记录在接口之间进行路由选择的分组的个数。具有接收和发送接口索引的计数器是递增的。

 

    当主机错误地选择某个路由器作为分组的第一跳路由器时,该路由器向源主机返回一个ICMP重定向报文。IP网络互连模型假定主机相对地并不知道整个互联网的拓扑结构,把维护正确路由选择的责任交给路由器。路由器发出重定向报文是向主机表明它为分组选择了一个不正确的路由。图13说明重定向报文。

图13 路由器R1重定向主机HS使用路由器R2到达HD

    通常,管理员对主机的配置是:把远程网络的分组发送到某个默认路由器上。在图13中,主机HS上R1被配置成它的默认路由器。当HS首次向HD发送分组时,它不知道R2是合适的选择,所以把分组转发给R1。R1识别出差错,就把分组转发给R2,并向HS发回一个重定向报文。接收到重定向报文后,HS更新它的路由表,下一次发往HD的分组就直接发给R2。

    在图14中,ip_forward决定是否发送重定向报文。

图14 ipforward函数:是否发送重定向报文

    a. 在接收接口上离开吗

915~929 路由器识别重定向情况的规则很复杂。首先,只有在同一接口(rt_ifp和rcvif)上接收或重发分组时,才能应用重定向。其次,被选择的路由本身必须没有被ICMP重定向报文创建或者修改过(RTF_DYNAMIC|RTF_MODIFIED),而且该路由也不能是默认目的地(0.0.0.0)。

    全局整数ipsendredirects指定系统是否被授权发送重定向,它的默认值为1。当传给ip_forward的参数srcrt指明系统是对分组路由选择的源时,禁止系统重定向,因为假定源主机要覆盖中间路由器的选择。

    b. 发送重定向吗

930~931 这个测试决定分组是否产生于本地子网。如果源地址的子网掩码位和输出接口的相同,则这两个地址位于同一IP网络中。如果源接口和输出的接口位于同一网络中,则该系统就不应该接收这个分组,因为源站可能已经把分组发给正确的第一跳路由器了。ICMP重定向报文告诉主机正确的第一跳目的地。如果分组产生于其它子网,则前一系统是个路由器,这个系统就不应该发送重定向报文;差错路由由选择协议纠正。

    c. 选择合适的路由器

932~940 ICMP重定向报文中包含下一个系统的地址,如果目的主机不在直接相连的网络上,该地址是一个路由器地址;当目的主机在直接相连的网络中时,该地址就是主机地址。

图15 ip_forward

    d. 转发分组

941~954 现在,ip_forward有一个路由,并决定是否需要ICMP重定向报文。ip_output把分组发送到路由ipforward_rt所指定的下一跳。IP_ALLOWBROADCAST标志位允许被转发分组是到某个局域网的广播。如果ip_output成功,并且不需要发送任何重定向报文,则丢掉分组的前64字节,if_forward返回。

    e. 发送ICMP差错报文?

955~983 ip_forward可能会由于ip_output失败或者重定向而发送ICMP报文。如果没有原始分组的复制(可能当时复制时,缓存不足而复制失败),则无法发送重定向报文,ip_forward返回。如果有重定向,type和code以前被置位,但是如果ip_output失败,switch语句基于从ip_output返回的值重新设置新的ICMP类型和错误码。icmp_error发送该报文。来自失败的ip_outputICMP报文将覆盖任何重定向报文。

    处理来自ip_output的差错的switch语句非常重要。它把本地差错翻译成适当的ICMP差错报文,并返回给分组的源站,如果是ICMP重定向报文,则dest则是告诉源站对于当前报文,下次应该跳转的目的地。对于图16对差错作了总结。

图16 来自ip_output的差错

 

输出处理:ip_output函数

    IP输出代码从两处接收分组:ip_forward和运输协议(图1)。让inetsw[0].pr_output能访问到IP输出操作似乎很有道理,但事情并非如此。标准Internet传输协议(ICMP、IGMP、UDP和TCP)直接调用ip_output,而不查询inetsw表。对标准Internet传输协议而言,protosw结构不必具有一般性,因为调用函数并不是在与协议无关的情况下接入IP的。对于协议无关的路由选择插口可以调用pr_output接入IP。

    下面分三个部分描述ip_output:

  • 首部初始化

  • 路由选择

  • 源地址选择和分片

1. 首部初始化

    图17显示了ip_output的第一部分,把选项与外出的分组合并,完成传输协议提交(不是ip_forward提交的)的分组首部。

图17 函数ip_output

44~59 传给ip_output的参数包括:m0,要发送的分组;opt,包含的IP选项;ro,缓存的到目的地的路由;flags,见图18;imo,指向多播选项的指针。

图18 ip_output:flag值

    IP_FORWARDING被 ip_forward和ip_mforward(多播分组转发)设置,并禁止ip_output重新设置任何IP首部字段。

    a. 构造IP首部

60~73 如果调用程序提供任何IP选项,它们将被ip_insertoptions与分组合并,并把返回新的首部长度。

    进程可以设置IP_OPTIONS插口选项来为一个插口指定IP选项。插口的运输层(TCP或UDP)总是把这些选项提交给ip_output。

    被转发的分组(IP_FORWARDING)或有预构首部(IP_RAWOUTPUT)分组的IP首部不能被ip_output修改。任何其它分组需要有几个IP首部字段被初始化。ip_output把ip_v设置成4,把DF位需要的ip_off清零,并设置成调用程序提供的值,给来自全局整数的ip->ip_id赋一个唯一的标识符,把ip_id加1。ip_id是在协议初始化时由系统时钟设置。ip_hl被设置成用32bit字节度量的首部长度。

    IP首部的其它字段长度、偏移、TTL、TOS和目的地址已经被传输协议初始化了。源地址可能没被设置,因为是确定了到目的地的路由后选择的(图20)。

    b. 分组已经包括首部

74~76 对一个已转发的分组(或一个有首部的原始IP分组),首部长度被保存在hlen中,留给将来分片算法使用。

2. 路由选择

    在完成IP首部后,ip_output的下一个任务就是确定一条到目的地的路由。如图19所示。

图19 ip_output:选择路由

    a. 验证调整缓存中的路由

77~99 ip_output可能把一条高速缓存中的路由作为ro参数来提供。如果没有路由,则ip_output把ro设置成临时route结构iproute。

    如果高速缓存中的目的地不是去当前分组的目的地,就把该路由丢掉,新的目的地址放在dst中。

    b. 旁路路由选择

100~114 调用方可以通过设置IP_ROUTETOI标志禁止对分组进行路由选择。ip_output必须找到一个与分组中指定的目的地网络直接相连的接口。ifa_ifwithdstaddr搜索点到点接口,而in_ifwithnet搜索其它接口。如果任一函数均未找到与目的网络相连的接口,就返回ENETUNREACH;否则,ifp指向选定接口。

    c. 本地路由

115~122 如果分组正被路由选择,并且没有其他缓存的路由,则rtalloc找到一条到dst指定的地址的路由。如果rtalloc没找到路由,则ip_output返回EHOSTUNREACH。如果ip_forward调用ip_output,就把EHOSTUNREACH转换成ICMP差错。如果某个传输协议调用ip_output,就把差错传回给进程。

123~122 ia被设置成指向选定接口的地址(ifaddr结构),而ifp指向接口的ifnet结构。如果下一跳不是分组的最终目的地,则把dst改成下一跳路由器地址,而不再是分组最终的目的地址。IP首部内部的目的地址不变,但接口层必须把分组提交给dst,即下一跳路由器。

3. 源地址选择和分片

    ip_output的最后一部分如图20所示,保证IP首部有一个有效源地址,然后把分组提交给与路由相关的接口。如果分组比接口的MTU大,就必须对分组分片,然后一片一片地发送。关于分片的代码,将在第10章讨论。

图20 ip_output函数:源地址选择和分片

    a. 选择源地址

212~239 如果没有指定ip_src,则ip_output选择输出接口的IP地址ia作为源地址。这不能在早期填充其它IP首部字段时一起初始化,因为那里还没选定路由。转发分组通常都有一个源地址,但是,如果发送进程没有明确指定源地址,产生于本地主机的分组可能没有源地址。

    如果目的IP地址是一个广播地址,则接口必须支持广播(IFF_BROADCAST),调用方必须明确能广播(IP_ALLOWBROADCAST),而且分组必须足够小,无需分片。

    如果这些条件都不满足,就扔掉该分组,把相应错误码返回给调用方。否则,设置输出分组的M_BCAST,告诉接口输出函数把该分组作为链路级广播发送。

    如果目的地址不是广播地址,则ip_output把M_BCAST清零。

    b. 发送分组

240~252 如果分组对所选择的接口足够小,ip_len和ip_off被转换成网络字节序列,IP检验和与in_cksum一起计算,把分组提交给所选接口的if_output函数。

    c. 分片分组

253~338 大分组在被发送之前必须分片。这里暂时不予讨论,将在第10章讨论。

    d. 清零

339~346 对第一路由入口都有一个引用计数。如果参数ro为空,ip_output可能会使用一个临时的route结构(iproute)。如果需要,RTFREE发布iproute内的路由入口,并把引用计数减1。Bad处的代码在返回前扔掉当前分组。

 

Internet检验和:in_cksum函数

    检验和的目的是检验数据是否正确,没有被修改。对于检验和函数的设计和实现如下所示:

  1. 把被检验的相邻字节成对配成16bit整数,然后进行二进制反码求和。

  2. 为生成检验和,把检验和字段本身清零,把这个16bit的和的二进制反码放到检验和字段。

  3. 为了检验检验和,依然按16bit的方式进行二进制反码求和。如果结果为全1(在二进制反码运算中为0),则检验成功。

    二进制反码运算:当对用二进制反码表示的整数进行加法运算时,把两个整数相加后再加上最高位的进位从而得到加法的结果。在二进制反码运算中,只要把每一位求补就得到一个数的反;所以在二进制反码运算中,0的表示方法有两种:全0和全1。

    检验和算法在发送分组之前计算出要放在IP首部检验和字段的值。为了计算这个值,先把首部的检验和字段设为0,然后把首部作为一个16bit的整数数组来处理,计算整个首部(包括选项)的二进制反码的和。暂且把这个计算结果称为a,因为检验和字段被明确设为0,所以a是除了检验和字段外所有IP首部字段的各。a的二进制反码,用-a表示,被放在检验和字段中,发送该分组。

    如果在传输过程中没有比特位被改变,则在目的地计算的检验和应该等于(a+-a)的二进制反码。在二进制反码运算中(a+-a)的和是-0(全1),而它的二进制反码应该等于0(全0)。所以在目的地,一个没有损坏分组计算出来的检验和应该总是为0,正如图7中看到的检验和判断部分。

    图21是这个算法的一种原始的实现:

图21 IP检验和计算的一种原始的实现

1~16 这里唯一提高性能之处在于累计sum高16bit的进位。当循环结束时,累计的进位被加在低16bit上,直到没有其它进位发生。

    图22显示的是Net/3的可移植C版本。它使用了延迟进位技术,作用于存储在一个mbuf链中的分组。

图22 IP检验和计算的一个优化

42~140 我们的新检验和实现假定所有的被检验字节存储在一个连续缓存而不是mbuf中。这个版本的检验和计算采用相同的底层算法来正确地处理mbuf:用32bit整数的延迟进位对16bit字节作加法。对奇数个字节的mbuf,多出来的一个字节保存起来,并与下一个mbuf的第一个字节配对。因为大多数体系结构中,对16bit字的不对齐访问是无效的,甚至会产生严重差错,所以不对齐字节被保存,in_cksum继续加上下一个对齐的字。当这种情况发生时,in_cksum总是很小心地交换字节,保证位于奇数和偶数位置的字节被放在单独的和字节中,以满足检验和算法的要求。

93~115 函数中的三个while循环在每次迭代中分别在和中加上16个字、4个字和1个字。展开的循环减小了循环的耗费,在某些体系结构中可能比一个直接循环要快很多,但代价是代码长度和复杂性增加。

 

setsockopt和getsockopt系统调用

    Net/3提供setsockopt和getsockopt两个系统调用来访问一些网络互连的性质。这两个系统调用支持一个动态接口,进程可用该动态接口访问某种网络互连协议的一些性质,而标准的系统调用通常不支持该协议。这两个调用的原型是:

    int setsockopt(int s, int level, int optname, 

                             void *optval, int opt len);

    int getsockopt(int s, int level, int optname, 

                        const void * optval, int optlen)

    大多数插口选项只影响它们在其上发布的插口。与sysctl参数相比,后者影响整个系统。

    setsockopt和getsockopt设置和获取通信栈所有层上的选项。Net/3按照与s相关的协议和由level指定的标识处理选项。图23列出了在我们讨论的协议中level可能取得的值。

图23 sosetopt和sogetopt参数

    图24显示了所有插口选项的总结。该图显示了IPPROTO_IP级选项。选项出现在第1列,optval指向变量的数据类型出现在第2列,第3列显示的是处理该选项的函数。

图24 IPPROTO_IP级的插口选项

    图25显示了用于处理大部分IPPROT_IP选项的ip_ctloutput函数的整个结构。

图25 ip_ctloutput函数整体结构

431~447 ip_ctloutput的第一个参数op,可以是PRCO_SETOPT或者PRCO_GETOPT。第二个参数so,指向向其发布请求的插口。levlel必须是IPROTO_IP。optname是要改变或者检索的选项,mp间接指向一个含有与该选项相关数据的mbuf,m被初始化为指向由*mp引用的mbuf。

448~500 如果在调用setsockopt时指定了一个无法识别的选项,ip_ctloutput释放掉所有调用方传来的缓存,并返回EINVAL。

501~553 getsockopt传来的无法识别的选项导致ip_ctloutput返回ENOPROTOOPT,调用方释放mbuf。

1. PRCO_SETOPT的处理

    对于PRCO_SETOPT的处理如图26所示。

图26 ip_ctloutput函数:处理PRCO_SETOPT

450~451 IP_OPTIONS是由ip_pcbopts处理的(第9章讨论)。

452~484 IP_TOS、IP_TTL、IP_RECVOPTS、IP_RECVERTOPTS以及IP_RECVDSTADDR选项都需要在由m指向的mbuf中有一个整数。该整数存储在optval中,用来改变与插口有关的ip_tos和ip_ttl的值,或者用来设置或复位与插口相关的INP_RECVOPTS、INT_RECVERTOPTS和INP_RECVDSTADDR标志位。如果optval是非0,则宏OPTSET设置或复位指定的比特。

2. PRCO_GETOPT的处理

    图27显示了对PRCO_SETOPT的处理。

图27 ip_ctloutput函数:处理PRCO_GETOPT

503~538 对IP_OPTIONS,ip_ctloutput返回一个缓存,该缓存中包含了与该插口相关的选项的备份。对其他选项,ip_ctloutput返回ip_tos和ip_ttl的值,或与该选项相关的标志的状态。返回的值放在由m指向的mbuf中。如果在inp_flags中的bit是打开的,则宏OPTBIT返回1,否则返回0。

 

ip_sysctl函数

    概说《TCP/IP详解 卷2》第7章 域和协议图24中显示,在调用sysctl中,当协议和协议族的标识符是0时,就调用ip_sysctl函数。图28显示了ip_sysctl支持的三个参数。

图28 sysctl参数

    图29显示了ip_sysctl函数。

图29 ip_sysctl函数

    因为ip_sysctl并不把sysctl请求转发给其他函数,所以在name中只能有一个成员,否则返回ENOTDIR。

    switch语句选择的调用sysctl_int,它访问或修改ipforwarding、ipsendredirects或者ip_defttl。对无法识别的选项返回EOPNOTSUPP。

 

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

TCP/IP详解·2:实现》完整而详细地介绍了TCP/IP协议是如何实现的。书中给出了约500个图例,15000行实际操作的C代码,采用举例教学的方法帮助你掌握TCP/IP实现。《TCP/IP详解·2:实现》不仅说明了插口API和协议族的关系以及主机实现与路由器实现的差别。还介绍了4.4BSD-Lite版的新的特点。《TCP/IP详解·2:实现》适用于希望理解TCP/IP协议如何实现的人,包括编写网络应用程序的程序员以及利用TCP/IP维护计算机网络的系统管理员。 目录 · · · · · · 第一 概述 1.1 引言 1.2 源代码表示 1.3 历史 1.4 应用编程接口 1.5 程序示例 1.6 系统调用和库函数 1.7 描述符 1.8 网络实现概述 1.9 mbuf与输出处理 1.10 输入处理 1.11 网络实现概述 1.12 中断级别与并发 1.13 源代码组织 1.14 测试网络 1.15 小结 第二 mduf:存储器缓存 2.1 引言 2.2 代码介绍 2.3 mduf的定义 2.4 mduf结构 2.5 简单的mduf宏和函数 2.6 m_devget和m_pullup函数 2.7 mduf宏和函数的小结 2.8 Net/3联网数据结构小结 2.9 m_copy和簇引用记数 2.10 其他选择 2.11 小结 第三 接口层 3.1 引言 3.2 代码介绍 3.3 ifnet结构 3.4 ifaddr结构 3.5 sockaddr结构 3.6 ifnet与ifaddr的专用化 3.7 网络初始化概述 3.8 以太网初始化 3.9 SLIP初始化 3.10 环回初始化 3.11 if_attach函数 3.12 ifinit函数 3.13 小结 第四 接口:以太网 4.1 引言 4.2 代码介绍 4.3 以太网接口 4.4 ioctl系统调用 4.5 小结 第五 接口:SLIP和环回 5.1 引言 5.2 代码介绍 5.3 SLIP接口 5.4 环回接口 5.5 小结 第六 IP编址 6.1 引言 6.2 代码介绍 6.3 接口和地址小结 6.4 sockaddr_in结构 6.5 in_ifaddr结构 6.6 地址指派 6.7 接口ioctl处理 6.8 internet实用函数 6.9 ifnet实用函数 6.10 小结 第七 域和协议 7.1 引言 7.2 代码介绍 7.3 domain结构 7.4 protosw结构 7.5 IP的domain和protosw结构 7.6 pffindproto和pffindtype函数 7.7 pfctlinput函数 7.8 IP初始化 7.9 sysctl系统调用 7.10 小结 第八 IP网际协议 8.1 引言 8.2 代码介绍 8.3 IP分组 8.4 输入处理:ipintr函数 8.5 转发:ip_forward函数 8.6 输出处理:ip_output函数 8.7 Internet检验和:in_cksum函数 8.8 setsockopt和getsockopt系统调用 8.9 ip_sysctl函数 8.10 小结 第九 IP选项处理 9.1 引言 9.2 代码介绍 9.3 选项格式 9.4 ip_dooptions函数 9.5 记录路由选项 9.6 源站和记录路由选项 9.7 时间戳选项 9.8 ip_insertoptions函数 9.9 ip_pcbopts函数 9.10 一些限制 9.11 小结 第十 IP的分片与重装 10.1 引言 10.2 代码介绍 10.3 分片 10.4 ip_optcopy函数 10.5 重装 10.6 ip_optcopy函数 10.7 ip_slowtimo函数 10.8 小结 第十一 ICMP:Internet控制报文协议 第十二 IP多播 第十三 IGMP:Internet组管理协议 第十四 IP多播选路 第十五 插口层 第十六 插口I/O 第十七 插口选项 第十八 Radix树路由表 第十九 选路请求和选路消息 第二十 选路接口 第二十一 ARP:地址解析协议 第二十二 协议控制块 第二十三 UDP:用户数据报协议 第二十四 TCP:传输控制协议 第二十五 TCP的定时器 第二十六 TCP输出 第二十七 TCP的函数 第二十八 TCP的输入 第二十九 TCP的输入(续) 第三十 TCP的用户需求 第三十一 BPF:BSD分组过滤程序 第三十二 原始IP 结束语 附录A 部分习题的解答 附录B 源代码的获取 附录C RFC 1122的有关内容 参考文献
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 添加IP和UDP首部 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 小结 22 第2 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_devget和m_pullup函数 34 2.6.1 m_devget函数 34 2.6.2 mtod和dtom宏 36 2.6.3 m_pullup函数和连续的协议首部 36 2.6.4 m_pullup和IP的分片与重组 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_down和if_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初始化:slopen和slinit 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 的domain和protosw结构 150 7.6 pffindproto和pffindtype函数 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 setsockopt和getsockopt系统调用 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_IREQ和ICMP_ 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 slioctl和loioctl函数:SIOCADDMULTI和SIOCDELMULTI 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 getsock和sockargs函数 361 15.8 bind系统调用 363 15.9 listen系统调用 364 15.10 tsleep和wakeup函数 365 15.11 accept系统调用 366 15.12 sonewconn和soisconnected 函数 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、sendto和sendmsg 系统调用 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、recvfrom和recvmsg 系统调用 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 fcntl和ioctl系统调用 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_init和rtable_init 函数 465 18.8 初始化:rn_init和rn_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 rtalloc和rtalloc1函数 482 19.3 宏RTFREE和rtfree函数 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 routedomain和protosw结构 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_detach和raw_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_pcballoc和in_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_setsockaddr和in_setpeeraddr 函数 595 22.11 in_pcbnotify、in_rtchange和in_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 小结 602 第23 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 小结 652 第25 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_2和2MSL定时器 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 小结 710 第27 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 小结 772 第29 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、付费专栏及课程。

余额充值