TCP/IP详解(二) 

JavaGG 发布于 2009/05/05 15:00
阅读 1K+
收藏 3

TCP/IP详解(二)  

4.5 ARP举例 
在本小节中,我们用tcpdump命令来看一看运行像Telnet这样的普通TCP工具软件时ARP会做些什么。附录A包含tcpdump命令的其它细节。 

普通例子 
为了看清楚ARP的运作过程,我们执行telnet命令与无效的服务器连接。 

(见原书p.57的①) 

当我们在另一个系统上(sun)运行带有-e参数的tcpdump命令时,显示的是硬件地址(在我们的例子中是48 bit的以太网地址。) 

图4.4 TCP连接请求产生的ARP请求和回答 

图4.4中的tcpdump的原始输出如图附录A中的A.3所示。由于这是本书第一个tcpdump输出例子,你应该去查看附录中的原始输出,看看我们作了哪些修改。 
我们删除了tcpdump命令输出的最后四行,因为它们是结束连接的信息(我们将在第18章进行讨论),与这里讨论的内容不相关。 
在第1行中,源端主机(bsdi)的硬件地址是0:0:c0:6f:2d:40。目的端主机的硬件地址是ff:ff:ff:ff:ff:ff,这是一个以太网广播地址。电缆上的每个以太网接口都要接收这个数据帧并对它进行处理,如图4.2所示。 
第1行中紧接着的一个输出字段是arp,表明帧类型字段的值是0x0806,说明此数据帧是一个ARP请求或回答。 
在每行中,单词arp或ip后面的值60指的是以太网数据帧的长度。由于ARP请求或回答的数据帧长都是42字节(28字节的ARP数据,14字节的以太网帧头),因此每一帧都必须加入填充字符以达到以太网的最小长度要求:60字节。 
请参见图1.7,这个最小长度60字节包含14字节的以太网帧头,但是不包括4个字节的以太网帧尾。有一些书把最小长度定为64字节,它包括以太网的帧尾。我们在图1.7中把最小长度定为46字节,是有意不包括14字节的帧首部,因为对应的最大长度(1500字节)指的是MTU――最大传输单元(图2.5)。我们使用MTU经常是因为它对IP数据报的长度进行限制,但一般与最小长度无关。大多数的设备驱动程序或接口卡自动地用填充字符把以太网数据帧充满到最小长度。第3,4和5行中的IP数据报(包含TCP段)的长度都比最小长度小,因此都必须进行填充到60字节。 
第1行中的下一个输出字段arp who-has表示作为ARP请求的这个数据帧中,目的IP地址是svr4的地址,发送端的IP地址是bsdi的地址。tcpdump打印出主机名对应的默认IP地址。(在4.7节中,我们将用-n参数来查看ARP请求中真正的IP地址。) 
从第2行中我们可看到,尽管ARP请求是广播的,但是ARP回答的目的地址却是bsdi(0:0:c0:6f:2d:40)。ARP回答是直接送到请求端主机的,而是广播的。 
tcpdump打印出arp reply的字样,同时打印出响应者的主机名和硬件地址。 
第3行是第一个请求建立连接的TCP段。它的目的硬件地址是目的主机(svr4)。我们将在第18章讨论这个段的细节内容。 
在每一行中,行号后面的数字表示tcpdump收到分组的时间(以秒为单位)。除第1行外,其它每行在括号中还包含了与上一行的时间差异(以秒为单位)。我们从这个图可以看出,发送ARP请求与收到ARP回答之间的时延是2.2 ms。而在0.7 ms之后发出第一段TCP报文。在本例中,用ARP进行动态地址解析的时间小于3 ms。 
最后需要指出的一点,在tcpdump命令输出中,我们没有看到svr4在发出第一段TCP报文(第4行)之前发出的ARP请求。这是因为可能在svr4的ARP高速缓存中已经有bsdi的表项。一般情况下,当系统收到ARP请求或发送ARP回答时,都要把请求端的硬件地址和IP地址存入ARP高速缓存。在逻辑上可以假设,如果请求端要发送IP数据报,那么数据报的接收端将很可能会发送一个回答。 

对不存在主机的ARP请求 
如果查询的主机已关机或不存在会发生什么情况呢?为此我们指定一个并不存在的Internet地址――根据网络号和子网号所对应的网络确实存在,但是并不存在所指定的主机号。从图3.10我们可以看出,主机号从36到62的主机并不存在(主机号为63是广播地址)。这里,我们用主机号36来举例子。 

(见原书p.59的①) 

tcpdump命令的输出如图4.5所示。 

图4.5 对不存在主机的ARP请求 

这一次,我们没有用-e选项,因为我们已经知道ARP请求是在网上广播的。 
令人感兴趣的是看到多次进行ARP请求:第一次请求发生后5.5秒进行第二次请求,在24秒之后又进行第三次请求。(在第21章我们将看到TCP的超时和重发算法的细节。)tcpdump命令输出的超时限制为29.5秒。但是,在telnet命令使用前后分别用date命令检查时间,可以发现Telnet客户端的连接请求似乎在大约75秒后才放弃。事实上,我们在后面将看到,大多数的BSD实现把完成TCP连接请求的时间限制设置为75秒。 
在第18章中,当我们看到建立连接的TCP报文段序列时,会发现ARP请求对应于TCP试图发送的初始TCP SYN(同步)段。 
注意,在线路上我们始终看不到TCP的报文段。我们能看到的是ARP请求。直到ARP回答返回时,TCP报文段才可以被发送,因为硬件地址到这时才可能知道。如果我们用过滤模式运行tcpdump命令,只查看TCP数据,那么将没有任何输出。 

ARP 高速缓存超时设置 
在ARP高速缓存中的表项一般都要设置超时值。(在4.8小节中,我们将看到管理员可以用arp命令把地址放入高速缓存中而不设置超时值。)从伯克利系统演变而来的系统一般对完整的表项设置超时值为20分钟,而对不完整的表项设置超时值为3分钟。(在前面的例子中我们已见过一个不完整的表项,即在以太网上对一个不存在的主机发出ARP请求。)当这些表项再次使用时,这些实现一般都把超时值重新设为20分钟。 

(下面是原书p.60①的译文) 
在RFC中说,在表项正在使用时,超时值就应该启动,但是大多数的从伯克利系统演变而来的系统没有这样做――它们每次都是在访问表项进重设超时值。 

4.6 ARP代理 
如果ARP请求是从一个网络的主机发往另一个网络上的主机,那么连接这两个网络的路由器就可以回答该请求,这个过程称作委托ARP或ARP代理(Proxy ARP)。这样可以欺骗发起ARP请求的发送端,使它误以为路由器就是目的主机,而事实上目的主机是在路由器的“另一边”。路由器的功能相当于目的主机的代理,把分组从其它主机转发给它。 
举例是说明ARP代理的最好方法。如图3.10所示,系统sun与两个以太网相连。但是,我们也指出过,事实上并不是这样,请把它与封二中的图进行比较。在sun和子网140.252.1之间实际存在一个路由器,就是这个具有ARP代理功能的路由器使得sun就好像在子网140.252.1上一样。具体安置如图4.6所示,路由器Telebit NetBlazer,取名为netb,在子网和主机sun之间。 

图4.6 ARP代理的例子 

当子网140.252.1(称作gemini)上的其它主机有一份IP数据报要传给地址为140.252.1.29的sun,gemini比较网络号(140.252)和子网号(1),因为它们都是相同的,因而在图4.6上面的以太网中发送IP地址140.252.1.29的ARP请求。路由器netb识别出该IP地址属于它的一个拔号主机,于是把它的以太网接口地址140.252.1作为硬件地址来回答。主机gemini通过以太网发送IP数据报到netb,netb通过拔号SLIP链路把数据报转发到sun。这个过程对于所有140.252.1子网上的主机来说都是透明的,主机sun实际上是在路由器netb后面进行配置的。 
如果我们在主机gemini上执行arp命令,经过与主机sun通信以后,我们发现在同一个子网140.252.1上的netb和sun的IP地址映射的硬件地址是相同的。这通常是使用委托ARP的线索。 

gemini % arp -a 
这里是子网140.252.1上其他主机的输出行 
netb (140.252.1.183) at 0:80🇦🇩3:6a:80 
sun (140.252.1.29) at 0:80🇦🇩3:6a:80 

图4.6中的另一个需要解释的细节是在路由器netb的下方(SLIP链路)显然缺少一个IP地址。为什么在拔号SLIP链路的两端只拥有一个IP地址,而在bsdi和slip之间的两端却分别有一个IP地址?在3.8小节我们已经指出,用ifconfig命令可以显示拔号SLIP链路的目的地址,它是140.252.1.183。NetBlazer不需要知道拔号SLIP链路每一端的IP地址。(这样做会用更多的IP地址。)相反,它通过分组到达的串行线路接口来确定发送分组的拔号主机,因此对于连接到路由器的每个拔号主机不需要用唯一的IP地址。所有的拔号主机使用同一个IP地址140.252.1.183作为SLIP链路的目的地址。 
ARP代理可以把数据报传送到路由器sun上,但是子网140.252.13上的其它主机是如何处理的呢?路由选择必须使数据报能到达其它主机。这里需要特殊处理,路由选择表中的表项必须在网络140.252的某个地方制定,使所有数据报的目的端要么是子网140.252.13,要么是子网上的某个主机,这样都指向路由器netb。而路由器netb知道如何把数据报传到最终的目的端,即通过路由器sun。 
ARP代理也称作混合ARP(promiscuous ARP)或ARP 出租(ARP hack)。这些名字来自于ARP代理的其它用途:通过两个物理网络之间的路由器可以互相隐藏物理网络。在这种情况下,两个物理网络可以使用相同的网络号,只要把中间的路由器设置成一个ARP代理,以响应一个网络到另一个网络主机的ARP请求。这种技术在过去用来隐藏一组在不同物理电缆上运行旧版TCP/IP的主机。分开这些旧主机有两个共同的理由,其一是它们不能处理子网划分,其二是它们使用旧的广播地址(所有比特值为0的主机号,而不是目前使用的所有比特值为1的主机号)。 

4.7 免费ARP 
我们可以看到的另一个ARP特性称作免费ARP (gratuitous ARP)。它是指主机发送ARP查找自己的IP地址。通常,它发生在系统引导期间进行接口配置的时候。 
在我们的互联网中,如果我们引导主机bsdi并在主机sun上运行tcpdump命令,我们可以看到如图4.7所示的分组。 

图4.7 免费ARP的例子 

(我们用-n选项运行tcpdump命令,打印出点分十进制的地址,而不是主机名。)对于ARP请求中的各字段来说,发送端的协议地址和目的端的协议地址是一致的:即主机bsdi的地址140.252.13.35。另外,以太网报头中的源地址0:0:c0:6f:2d:40,正如tcpdump命令显示的那样,等于发送端的硬件地址(见图4.4)。 
免费ARP可以有两个方面的作用。 
1. 一个主机可以通过它来确定另一个主机是否设置了相同的IP地址。主机bsdi并不希望对此请求有一个回答。但是,如果收到一个回答,那么就会在终端日志上产生一个错误消息“以太网地址:a🅱c:d:e:f发送来重复的IP地址”。这样就可以警告系统管理员,某个系统有不正确的设置。 
2. 如果发送免费ARP的主机正好改变了硬件地址(很可能是主机关机了,并换了一块接口卡,然后重新启动),那么这个分组就可以使其它主机高速缓存中旧的硬件地址进行相应的更新。一个比较著名的ARP协议事实[Plummer 1982]是,如果主机收到某个IP地址的ARP请求,而且它已经在接收者的高速缓存中,那么就要用ARP请求中的发送端硬件地址(如以太网地址)对高速缓存中相应的内容进行更新。主机接收到任何ARP请求都要完成这个操作。(ARP请求是在网上广播的,因此每次发送ARP请求时网络上的所有主机都要这样做。) 
文献[Bhide, Elnozahy, and Morgan 1991]中有一个应用例子,通过发送含有备份硬件地址和故障服务器的IP地址的免费ARP请求,使得备份文件服务器可以顺利地接替故障服务器进行工作。这使得所有目的地为故障服务器的报文都被送到备份服务器那里,客户程序不用关心原来的服务器是否出了故障。 

(以下是原书p.63①的译文) 
不幸的是,作者却反对这个做法,因为这取决于所有不同类型的客户端都要有正确的ARP协议实现。它们显然碰到过客户端的ARP协议实现与规范不一致的情况。 
通过检查作者所在子网上的所有系统可以发现,SunOS 4.1.3和4.4BSD在引导时都发送免费ARP,但是SVR4却没有这样做。 

4.8 arp命令 
我们已经用这个命令及参数-a来显示ARP高速缓存中的所有内容。这里介绍其它参数的功能。 
超级用户可以用参数-d来删除ARP高速缓存中的某一项内容。(这个命令格式可以在运行一些例子之前使用,以让我们看清楚ARP的交换过程。) 
另外,可以通过参数-s来增加高速缓存中的内容。这个参数需要主机名和以太网地址:对应于主机名的IP地址和以太网地址被增加到高速缓存中。新增加的内容是永久性的(比如,它没有超时值),除非在命令行的末尾附上关键字temp。 
位于命令行末尾的关键字pub和-s参数一起,可以使系统起着主机ARP代理的作用。系统将回答与主机名对应的IP地址的ARP请求,并以指定的以太网地址作为回答。如果广播的地址是系统本身,那么系统就为指定的主机名起着委托ARP代理的作用。 

4.9 小结 
在大多数的TCP/IP实现中,ARP是一个基础协议,但是它的运行对于应用程序或系统管理员来说一般是透明的。ARP高速缓存在它的运行过程中非常关键,我们可以用arp命令对高速缓存进行检查和操作。高速缓存中的每一项内容都有一个定时器,根据它来删除不完整和完整的表项。arp命令可以显示和修改ARP高速缓存中的内容。 
我们介绍了ARP的一般操作,同时也介绍了一些特殊的功能:委托ARP(当路由器对来自于另一个路由器接口的ARP请求进行回答时)和免费ARP(发送自己IP地址的ARP请求,一般发生在引导过程中)。 

习题 
4.1 当我们输入命令以生成类似图4.4那样的输出时,发现本地ARP快速缓存为空以后,输入命令 
bsdi % rsh svr4 arp -a 
如果发现目的主机上的ARP快速缓存也是空的,那将发生什么情况?(该命令将在svr4主机上运行arp -a命令。) 
4.2 请描述如何判断一个给定主机是否能正确处理接收到的非必要的ARP请求的方法。 
4.3 由于发送一个数据包后ARP将等待响应,因此4.2节所描述的步骤7可能会持续一段时间。你认为ARP将如何处理在这期间收到相同目的IP地址发来的多个数据包? 
4.4 在4.5节的最后,我们指出Host Requirements RFC和伯克利派生系统在处理活动ARP表目的超时时存在差异。那么如果我们在一个由伯克利派生系统的客户端上,试图与一个正在更换以太网卡而处于关机状态的服务器主机联系,这时会发生什么情况?如果服务器在引导过程中广播一份免费(gratuitous)ARP,这种情况是否会发生变化? 











4-1 
5 RARP:逆地址解析协议 

5.1 引言 
具有本地磁盘的系统引导时,一般是从磁盘上的配置文件中读取IP地址。但是无盘机,如X终端或无盘工作站,则需要采用其他方法来获得IP地址。 
网络上的每个系统都具有唯一的硬件地址,它是由网络接口生产厂家配置的。无盘系统的RARP实现过程是从接口卡上读取唯一的硬件地址,然后发送一份RARP请求(一帧在网络上广播的数据),请求某个主机响应该无盘系统的IP地址(在RARP回答中)。 
在概念上这个过程是很简单的,但是实现起来常常比ARP要困难,其原因在本章后面介绍。RARP的正式规范是RFC 903 [Finlayson et al. 1984]。 

5.2 RARP的分组格式 
RARP分组的格式与ARP分组基本一致(图4.3)。它们之间主要的差别是RARP请求或回答的帧类型代码为0x8035,而且RARP请求的操作代码为3,回答操作代码为4。 
对应于ARP,RARP请求以广播方式传送,而RARP回答一般是单播(unicast)传送的。 

5.3 RARP举例 
在我们的互连网中,我们可以强制sun主机从网络上引导,而不是从本地磁盘引导。如果我们在主机bsdi上运行RARP服务程序和tcpdump命令,那么可以得到如图5.1那样的输出。我们用-e参数使得tcpdump命令打印出硬件地址: 

图5.1 RARP请求和回答 

RARP请求是广播方式(第1行),而第2行的RARP回答是单播方式。第2行的输出中at sun表示RARP回答包含主机sun的IP地址(140.252.13.33)。 
在第3行中,我们可以看到,一旦sun收到RARP回答,它就发送一个TFTP读请求(RRQ)给文件8CFC0D21.SUN4C。(TFTP表示简单文件传输协议。我们将在第15章详细介绍它。)文件名中的8个十六进制数字表求主机sun的IP地址140.252.13.33。这个IP地址在RARP回答中返回。文件名的后缀SUN4C表示被引导系统的类型。 
tcpdump在第3行中指出IP数据报的长度是65个字节,而不是一个UDP数据报(实际上是一个UDP数据报),因为我们运行tcpdump命令时带有-e参数,以查看硬件层的地址。在图5.1中需要指出的另一点是,第2行中的以太网数据帧长度比最小长度还要小(在4.5节中我们说过应该是60字节。)其原因是我们在发送该以太网数据帧的系统(bisdi)上运行tcpdump命令的。应用程序rarpd写42字节到BSD分组过滤设备上(其中14字节为以太网数据帧的报头,剩下的28字节是RARP回答),这就是tcpdump收到的副本。但是以太网设备驱动程序要把这一短帧填充空白字符以达到最小传输长度(60)。如果我们在另一个系统上运行tcpdump命令,其长度将会是60。 
我们从这个例子可以看出,当无盘系统从RARP回答中收到它的IP地址后,它将发送TFTP请求来读取引导映象。在这一点上我们将不再进一步详细讨论无盘系统是如何引导的。(第16章将描述无盘X终端利用RARP,BOOTP以及TFTP进行引导的过程。) 
当网络上没有RARP服务器时,其结果如图5.2所示。每个分组的目的地址都是以太网广播地址。在who-后面的以太网地址是目的硬件地址,跟在tell后面的以太网地址是发送端的硬件地址。 
请注意重发的频度。第一次重发是在6.55秒以后,然后增加到42.80秒,然后又减到5.34秒和6.55秒,然后又回到42.79秒。这种不确定的情况一直继续下去。如果计算一下两次重发之间的时间间隔,我们发现存在一种双倍的关系:从5.34到6.55是1.21秒,从 6.55到8.97是2.42秒,从8.97到13.80是4.83秒,一直这样继续下去。当时间间隔达到某个阈值时(大于42.80秒),它又重新置为5.34秒。 

图5.2 网络中没有RARP服务器的RARP请求 

超时间隔采用这样的递增方法比每次都采用相同值的方法要好。在图6.8中,我们将看到一种错误的超时重发方法,以及在第21章中将看到TCP的超时重发机制。 

5.4 RARP服务器的设计 
虽然RARP在概念上很简单,但是设计一个RARP服务器与系统相关而且比较复杂。相反,提供一个ARP服务器很简单,通常是TCP/IP在内核中实现的一部分。由于内核知道IP地址和硬件地址,因此当它收到一个询问IP地址的ARP请求时,只需用相应的硬件地址来提供回答就可以了。 

作为用户进程的RARP服务器 
RARP服务器的复杂性在于,服务器一般要为多个主机(网络上所有的无盘系统)提供硬件地址到IP地址的映射。该映射包含在一个磁盘文件中(在Unix系统中一般位于/etc/ethers目录中)。由于内核一般不读取和分析磁盘文件,因此RARP服务器的功能就由用户进程来提供,而不是作为内核的TCP/IP实现的一部分。 
更为复杂的是,RARP请求是作为一个特殊类型的以太网数据帧来传送的(帧类型字段值为0x8035,如图2.1所示)。这说明RARP服务器必须能够发送和接收这种类型的以太网数据帧。在附录A中,我们描述了BSD分组过滤器,Sun的网络接口栓,以及SVR4数据链路提供者接口都可用来接收这些数据帧。由于发送和接收这些数据帧与系统有关,因此RARP服务器的实现与系统是捆绑在一起的。 

每个网络有多个RARP服务器 
RARP服务器实现的一个复杂因素是RARP请求是在硬件层上进行广播的,如图5.2所示。这意味着它们不经过路由器进行转发。为了让无盘系统在RARP服务器关机的状态下也能引导,通常在一个网络上(例如一根电缆)要提供多个RARP服务器。 
当服务器的数目增加时(以提供冗余备份),网络流量也随之增加,因为每个服务器对每个RARP请求都要发送RARP回答。发送RARP请求的无盘系统一般采用最先收到的RARP回答。(对于ARP我们从来没有遇到这种情况,因为只有一台主机发送ARP回答。)另外,还有一种可能发生的情况是每个RARP服务器同时回答,这样会增加以太网发生冲突的概率。 

5.5 小结 
RARP协议是许多无盘系统在引导时用来获取IP地址。RARP分组格式基本上与ARP分组一致。一个RARP请求在网络上进行广播,它在分组中标明发送端的硬件地址,以请求相应IP地址的响应。回答通常是单播传送的。 
RARP带来的问题包括使用链路层广播,这样就阻止大多数路由器转发RARP请求,只返回很少信息:只是系统的IP地址。在第16章中,我们将看到BOOTP在无盘系统引导时会返回更多的信息:IP地址,引导主机的名字等等。 
虽然RARP在概念上很简单,但是RARP服务器的实现却与系统相关。因此,并不是所有的TCP/IP实现都提供RARP服务器。 

习题 
5.1 RARP需要不同的帧类型字段吗?ARP和RARP都使用相同的值0x0806吗? 
5.2 在一个有多个RARP服务器的网络上,如何防止它们的响应发生冲突? 
5-3 
6 ICMP:Internet控制报文协议 

6.1 引言 
ICMP经常被认为是IP层的一个组成部分。它传递差错信息以及其它需要注意的信息。ICMP报文通常被IP层或更高层协议(TCP或UDP)使用。一些ICMP报文把差错信息返回给用户进程。 
ICMP信息是在IP数据报内部被传输的,如6.1所示。 

图6.1 ICMP封装在IP数据报内部 

ICMP 的正式规范参见RFC 792 [Posterl 1981b]。 
ICMP报文的格式如图6.2所示。所有报文的前4个字节都是一样的,但是剩下的其它字节则互不相同。下面我们将逐个介绍各种报文格式。 
类型字段可以有15个不同的值,以描述特定类型的ICMP报文。某些ICMP报文还使用代码字段的值来进一步描述不同的条件。 
检验和字段覆盖整个ICMP报文。使用的算法与我们在3.2节中介绍的IP首部检验和算法相同。ICMP的检验和是必需的。 

图6.2 ICMP报文 

在本章中,我们将粗浅地讨论ICMP报文,并对其中一部分作详细介绍:地址掩码请求和回答,时间戳请求和回答,以及不可答端口。我们将详细介绍第27章Ping程序所使用的回应请求和回答报文和第9章处理IP路由的ICMP报文。 

6.2 ICMP报文的类型 
各种类型的ICMP报文如图6.3所示,不同类型由报文中的类型字段和代码字段来共同决定。 
图中的最后两列表明ICMP报文是一份查询报文还是一份差错报文。因为对ICMP差错报文有时需要作特殊处理,因此我们需要对它们进行区分。例如,在对ICMP差错报文进行响应时,永远不会生成另一份ICMP差错报文。(如果没有这个限制规则,我们可能会遇到一个差错产生另一个差错的情况,而差错再产生差错,这样无休止地循环下去。) 
当发送一份ICMP差错报文时,报文始终包含IP的首部和产生ICMP差错报文的IP数据报的前8个字节。这样,接收ICMP差错报文的模块就会把它与某个特定的协议(根据IP数据报首部中的协议字段来判断)和用户进程(根据包含在IP数据报前8个字节中的TCP或UDP报文首部中的TCP或UDP端口号来判断)联系起来。在6.5节我们将举例来说明一点。 
下面各种情况都不会导致产生ICMP差错报文: 
1.ICMP差错报文。(但是,ICMP查询报文可能会产生ICMP差错报文。) 
2.目的地址是广播地址(图3.9)或多播地址(D类地址,图1.5)的IP数据报。 
3.作为链路层广播的数据报。 
4.不是IP分片的第一片。(我们将在11.5节介绍分片。) 
5.源地址不是单个主机的数据报。这就是说,源地址不能为零地址、环回地址、广播地址或多播地址。 

(下面是图6.3的译文) 
类型 
代码 
描述 
查询 
差错 


回答回显(Ping回答,第7章) 
• 



目的不可到达: 




网络不可到达(9.3节) 

• 


主机不可到达(9.3节) 

• 


协议不可到达 

• 


端口不可到达(6.5节) 

• 


需要进行分片但设置了不分片比特(11.6节) 

• 


源站路由选择失败(8.5节) 

• 


目的网络不认识 

• 


目的主机不认识 

• 


源主机被隔离(作废不用) 

• 


目的网络被强制禁止 

• 

10 
目的主机被强制禁止 

• 

11 
由于服务类型TOS网络不可到达(9.3节) 

• 

12 
由于服务类型TOS主机不可到达(9.3节) 

• 

13 
由于过滤通信被强制禁止 

• 

14 
主机越权 

• 

15 
优先权中止生效 

• 


源端被关闭(基本流控制,11.11节) 

• 


改变路由(9.5节): 

• 


对网络改变路由 

• 


对主机改变路由 

• 


对服务类型和网络改变路由 

• 


对服务类型和主机改变路由 

• 


请求回显(Ping请求,第7章) 
• 



路由器通告(9.6节) 
• 

10 

路由器请求(9.6节) 
• 

11 

超时: 




传输期间生存时间为0(Traceroute, 第8章) 

• 


在数据报组装期间生存时间为0(11.5节) 

• 
12 

参数问题: 




坏的IP首部(包括各种差错) 

• 


缺少必需的选项 

• 
13 

时间戳请求(6.4节) 
• 

14 

时间戳回答(6.4节) 
• 

15 

信息回答(作废不用) 
• 

16 

信息回答(作废不用) 
• 

17 

地址掩码请求(6.3节) 
• 

18 

地址掩码回答(6.3节) 
• 

图6.3 ICMP报文类型 

这些规则是为了防止过去允许ICMP差错报文对广播分组响应所带来的广播风暴。 

6.3 ICMP地址掩码请求与回答 
ICMP地址掩码请求用于无盘系统在引导过程中获取自己的子网掩码(3.5节)。系统广播它的ICMP请求报文。(这一过程与无盘系统在引导过程中用RARP获取IP地址是类似的。)无盘系统获取子网掩码的另一个方法是BOOTP协议,我们将在第16章中介绍。ICMP地址掩码请求和回答报文的格式如图6.4所示。 

图6.4 ICMP地址掩码请求和回答报文 

ICMP报文中的标识符和序列号字段由发送端任意选择设定,这些值在回答中将被返回。这样,发送端就可以把回答与请求进行匹配。 
我们可以写一个简单的程序(取名为icmpaddrmask),它发送一份ICMP地址掩码请求报文,然后打印出所有的回答。由于一般是把请求报文发往广播地址,因此这里我们也这样做。目的地址(140.252.13.63)是子网140.252.13.32的广播地址(图3.12)。 

sun % icmpaddrmask 140.252.13.33 
received mask = ffffffe0, from 140.252.13.33 来自本机 
received mask = ffffffe0, from 140.252.13.35 来自bsdi 
received mask = ffff0000, from 140.252.13.34 来自svr4 

在输出中我们首先注意到的是,从svr4返回的子网掩码是差错的。显然,尽管svr4接口已经设置了正确的子网掩码,但是SVR4还是返回了一个普通的B类地址掩码,就好像子网并不存在一样。 

svr4 % ifconfig emd0 
emd0: flags=23 
inet 140.252.13.34 netmask ffffffe0 broadcast 140.252.13.63 

SVR4处理ICMP地址掩码请求过程存在差错。 
我们用tcpdump命令来查看主机bsdi上的情况,输出如图6.5所示。我们用-e参数来查看硬件地址。 

图6.5 发到广播地址的ICMP地址掩码请求 

注意,尽管在线路上什么也看不见,但是发送主机sun也能接收到ICMP回答(带有from ourself的输出行)。这是广播的一般特性:发送主机也能通过某种内部环回机制收到一份广播报文拷贝。由于术语“广播”的定义是指局域网上的所有主机,因此它必须包括发送主机在内。(参见图2.4,当以太网驱动程序识别出目的地址是广播地址后,它就把分组送到网络上,同时传一份拷贝到环回接口。) 
接下来,bsdi广播回答,而svr4却只把回答传给请求主机。通常,回答地址必须是单播地址,除非请求端的源IP地址是0.0.0.0,本例不属于这种情况。因此,把回答发送到广播地址是BSD/386的一个内部差错。 

(下面是原书p.73①的译文) 
RFC规定,除非系统是地址掩码的授权代理,否则它不能发送地址掩码回答。(为了成为授权代理,它必须进行特殊配置,以发送这些回答。参见附录E。)但是,正如我们从本例中看到的那样,大多数主机在收到请求时都发送一个回答,甚至有一些主机还发送差错的回答。 

最后一点可以通过下面的例子来说明。我们向本机IP地址和环回地址分别发送地址掩码请求: 

sun % icmpaddrmask sun 
received mask= ff000000, from 140.252.13.33 

sun % icmpaddrmask localhost 
received mask= ff000000, from 127.0.0.1 


上述两种情况下返回的地址掩码对应的都是环回地址,即A类地址127.0.0.1。还有,我们从图2.4可以看到,发送给本机IP地址的数据报(140.252.12.33)实际上是送到环回接口。ICMP地址掩码回答必须是收到请求接口的子网掩码(这是因为多接口主机每个接口有不同的子网掩码),因此两种情况下地址掩码接求都来自于环回接口。 

6.4 ICMP时间戳请求与回答 
ICMP时间戳请求允许系统向另一个系统查询当前的时间。返回的建议值是自午夜开始计算的毫秒数,协调的统一时间(Coordinated Universal Time, UTC)。(早期的参考手册认为UTC是格林尼治时间。)这种ICMP报文的好处是它提供了毫秒级的分辨率,而利用其它方法从别的主机获取的时间(如某些Unix系统提供的rdate命令)只能提供秒级的分辨率。由于返回的时间是从午夜开始计算的,因此调用者必须通过其它方法获知当时的日期,这是它的一个缺陷。 
ICMP时间戳请求和回答报文格式如图6.6所示。 

图6.6 ICMP时间戳请求和回答报文 

请求端填写发起时间戳,然后发送报文。回答系统收到请求报文时填写接收时间戳,在发送回答时填写发送时间戳。但是,实际上,大多数的实现把后面两个字段都设成相同的值。(提供三个字段的原因是可以让发送方分别计算发送请求的时间和发送回答的时间。) 

例子 
我们可以写一个简单程序(取名为icmptime),给某个主机发送ICMP时间戳请求,并打印出返回的回答。它在我们的小互连网上运行结果如下: 

(见原书p.74的①) 

程序打印出ICMP报文中的三个时间戳:发起时间戳(orig),接收时间戳(recv),以及发送时间戳(xmit)。正如我们在这个例子以及下面的例子中所看到的那样,所有的主机把接收时间戳和发送时间戳都设成相同的值。 
我们还能计算出往返时间(rtt),它的值是收到回答时的时间值减去发送请求时的时间值。difference的值是接收时间戳值减去发起时间戳值。这些值之间的关系如图6 7所示。 
如果我们相信RTT的值,并且相信RTT的一半用于请求报文的传输,另一半用于回答报文的传输,那么为了使本机时钟与查询主机的时钟一致,本机时钟需要进行调整,调整值是difference减去RTT的一半。在前面的例子中,bsdi的时钟比sun的时钟要慢7 ms和8 ms。 
由于时间戳的值是自午夜开始计算的毫秒数,即UTC,因此它们的值始终小于86,400,000 (24×60×60×1000)。这些例子都是在下午4:00以前运行的,并且在一个比UTC慢7个小时的时区,因此它们的值比82,800,000(2300小时)要大是有道理的。 
如果我们对主机bsdi重复运行该程序数次,我们发现接收时间戳和发送时间戳的最后一位数总是0。这是因为该版本的软件(0.9.4版)只能提供10毫秒的时间分辨率。(说明参见附录B。) 
如果我们对主机svr4运行该程序两次,我们发现SVR4时间戳的最后三位数始终为0: 

(见原书p.75的①) 

由于某种原因,SVR4在ICMP时间戳中不提供毫秒级的分辨率。这样,对秒以下的时间差调整将不起任何作用。 
如果我们对子网140.252.1上的其它主机运行该程序,结果表明其中一台主机的时钟与sun相差3.7秒,而另一个主机时钟相差近75秒: 

(见原书p.75的②) 

另一个令人感兴趣的例子是路由器gateway(一个Cisco路由器)。这表明,当系统返回一个非标准时间戳值时(不是自午夜开始计算的毫秒数,UTC),它就用32 bit时间戳中的高位来表示。我们的程序证明了一点,在尖括号中打印出了接收和发送的时间戳值(在关闭高位之后)。另外,我们不能计算发起时间戳和接收时间戳之间的时间差,因为它们的单位不一致。 

(见原书p.76的①) 

如果我们在这台主机上运行该程序数次,会发现时间戳值显然具有毫秒级的分辨率,而且是从某个起始点开始计算的毫秒数,但是起始点并不是午夜UTC。(例如,可能是从路由器引导时开始计数的毫秒数。) 
作为最后一个例子,我们来比较sun主机和另一个已知是准确的系统时钟----一个NTP stratum 1服务器。(下面我们会更多地讨论NTP,网络时间协议。) 

(见原书p.76的②) 

如果我们把difference的值减去RTT的一半,结果表明sun主机上的时钟要快38.5到51.5 ms。 

另一种方法 
还可以用另一种方法来获得时间和日期。 
1. 我们在1.12节中描述了日期时间服务程序和时间服务程序。前者是以人们可读的格式返回当前的时间和日期,是一行ASCII字符。我们可以用telnet命令来验证这个服务: 

(见原书p.76的③) 

另一方面,时间服务程序返回的是一个32 bit的二制进数值,表示自UTC,1900年1月1日午夜起算的秒数。这个程序是以秒为单位提供的日期和时间。(前面我们提过的rdate命令使用的是TCP时间服务程序。) 
2. 严格的计时器使用网络时间协议(NTP),该协议在RFC 1305中给出了描述[Mills 1992]。这个协议采用先进的技术来保证LAN或WAN上的一组系统的时钟误差在毫秒级以内。对计算机精确时间感兴趣的读者应该阅读这份RFC文档。 
3. 开放软件基金会(OSF)的分布式计算环境(DCE)定义了分布式时间服务(DTS),它也提供计算机之间的时钟同步。文献[Rosenberg, Kenney and Fisher 1992]提供了该服务的其它细节描述。 
4. 伯克利大学的Unix系统提供守护程序timed(8),来同步局域网上的系统时钟。不像NTP和DTS,timed不在广域网范围内工作。 

6.5 ICMP端口不可达差错 
最后两小节我们来讨论ICMP查询报文----地址掩码和时间戳查询及回答。我们现在来分析一种ICMP差错报文,即端口不可到达报文,它是ICMP目的不可到达报文中的一种,以此来看一看ICMP差错报文中所附加的信息。我们使用UDP(见第11章)来查看它。 
UDP的规则之一是,如果收到一份UDP数据报而目的端口与某个正在使用的进程不相符,那么UDP返回一个ICMP不可到达报文。我们可以用TFTP来强制生成一个端口不可到达报文。(TFTP将在第15章描述。) 
对于TFTP服务器来说,UDP的公共端口号是69。但是大多数的TFTP客户程序允许我们用connect命令来指定一个不同的端口号。这里,我们就用为它指定为8888: 

(见原书p.77的①) 

connect命令首先指定要连接的主机名及其端口号,接着用get命令来取文件。敲入get命令后,一份UDP数据报就发送到主机svr4上的8888端口。tcpdump命令引起的报文交换结果如图6.8所示。 
在UDP数据报送到svr4之前,要先发送一份ARP请求来确定它的硬件地址(第1行)。接着返回ARP回答(第2行),然后才发送UDP数据报(第3行)。(我们在tcpdump的输出中保留ARP请求和回答是为了提醒我们,这些报文交换可能在第一个IP数据报从一个主机发送到的另一个主机之前是必需的。在本书以后的章节中,如果这些报文与讨论的题目不相关,那么我们将省略它们。) 

图6.8 由TFTP产生的ICMP端口不可到达差错 

一个ICMP端口不可到达差错是立刻返回的(第4行)。但是,TFTP客户程序看上去似乎忽略了这个ICMP报文,而在5秒钟之后又发送了另一份UDP数据报(第5行)。在客户程序放弃之前重发了三次。 
注意,ICMP报文是在主机之间交换的,而不用目的端口号,而每个20字节的UDP数据报则是从一个特定端口(2924)发送到另一个特定端口(8888)。 
跟在每个UDP后面的数字20指的是UDP数据报中的数据长度。在这个例子中,20字节包括TFTP的2个字节的操作代码,9个字节以空字符结束的文件名temp.foo,以及9个字节以空字符结束的字符串netascii。(TFTP报文的详细格式参见图15.1。) 
如果用-e参数运行同样的例子,我们可以看到每个返回的ICMP端口不可到达报文的完整长度。这里的长度为70字节,各字段分配如图6.9所示。 

图6.9 “UDP端口不可到达”例子中返回的ICMP报文 

ICMP的一个规则是,ICMP差错报文(参见图6.3的最后一列)必须包括生成该差错报文的数据报IP首部(包含任何选项),还必须至少包括跟在该IP首部后面的前8个字节。在我们的例子中,跟在IP首部后面的前8个字节包含UDP的首部(图11.2)。 
一个重要的事实是包含在UDP首部中内容是源端口号和目的端口号。就是由于目的端口号(8888)才导致产生了ICMP端口不可到达的差错报文。接收ICMP的系统可以根据源端口号(2924)来把差错报文与某个特定的用户进程相关联(在本例中是TFTP客户程序)。 
导致差错的数据报中的IP首部要被送回的原因是因为IP首部中包含了协议字段,使得ICMP可以知道如何解释后面的8个字节(在本例中是UDP首部)。如果我们来查看TCP首部(图17.2),可以发现源端口和目的端口被包含在TCP首部的前8个字节中。 
ICMP不可到达报文的一般格式如图6.10所示。 

图6.10 ICMP不可到达报文 

在图6.3中,我们注意到有16种不同类型的ICMP不可到达报文,代码分别从0到15。ICMP端口不可到达差错代码是3。另外,尽管图6.10指出了在ICMP报文中的第二个32 bit字必须为0,但是当代码为4时(“需要分片但设置了不分片比特”),路径MTU发现机制(2.9节)却允许路由器把外出接口的MTU填在这个32 bit字的低16 bit中。我们在11.6节中给出了一个这种差错的例子。 

(下面是原书p.79①的译文) 
尽管ICMP规则允许系统返回多于8个字节的产生差错的IP数据报中的数据,但是大多数从伯克利派生出来的系统只返回8个字节。Solaris 2.2的ip_icmp_return_data_bytes选项默认条件下返回前64个字节(E.4节)。 

tcpdump时间系列 
在本书的后面章节中,我们还要以时间系列的格式给出tcpdump命令的输出,如图6.11所示。 

图6.11 发送到无效端口的TFTP请求的时间系列 

时间随着向下而递增,在图左边的时间标记与tcpdump命令的输出是相同的(图6.8).位于图顶部的标记是通信双方的主机名和端口号。需要指出的是,随着页面向下的y坐标轴与真正的时间值不是成比例的。当出现一个有意义的时间段时,在本例中是每5秒之间的重发,我们就在时间系列的两侧作上标记。当UDP或TCP数据正在被传送时,我们用粗线的行来表示。 
当ICMP报文返回时,为什么TFTP客户程序还要继续重发请求呢?这是由于网络编程中的一个因素,即BSD系统不把从插口(socket)接收到的ICMP报文中的UDP数据通知用户进程,除非该进程已经发送了一个connect命令给该插口。标准的BSD TFTP客户程序并不发送connect命令,因此它永远也不会收到ICMP差错报文的通知。 
这里需要注意的另一点是TFTP客户程序所采用的不太好的超时重发算法。它只是假定5秒是足够的,因此每隔5少就重传一次,总共需要25秒钟的时间。在后面我们将看到TCP有一个较好的超时重发算法。 

(下面是原书p.81的①的译文) 
TFTP客户程序所采用的超时重传算法已被RFC所禁用。不过,在作者所在子网上的三个系统以及Solaris 2.2仍然在使用它。AIX 3.2.2采用一种指数退避方法来设置超时值,分别在0,5,15和35秒时重发报文,这正是所推荐的方法。我们将在第21章更详细地讨论超时问题。 
最后需要指出的是,ICMP报文是在发送UDP数据报3.5 ms后返回的,这与第7章我们所看到的Ping回答的往返时间差不多。 






原文链接:http://bbs.chinaunix.net/viewthread.php?tid=16061

转载请注明作者名及原文出处



加载中
返回顶部
顶部