收藏本站 收藏本站
积木网首页 - 软件测试 - 常用手册 - 站长工具 - 技术社区
积木学院 > 黑客技术 > 菜鸟入门 > 正文

陈十三哥推荐TCP/IP详解(4)

来源:互联摘选 日期:2006-12-17 18:48
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的译文)
类型
代码
描述
查询
差错
0
0
回答回显(Ping回答,第7章)
·

3

目的不可到达:



0
    网络不可到达(9.3节)

·

1
    主机不可到达(9.3节)

·

2
    协议不可到达

·

3
    端口不可到达(6.5节)

·

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

·

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

·

6
    目的网络不认识

·

7
    目的主机不认识

·

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

·

9
    目的网络被强制禁止

·

10
    目的主机被强制禁止

·

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

·

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

·

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

·

14
    主机越权

·

15
    优先权中止生效

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

·
5

改变路由(9.5节):

·

0
    对网络改变路由

·

1
    对主机改变路由

·

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

·

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

·
8
0
请求回显(Ping请求,第7章)
·

9
0
路由器通告(9.6节)
·

10
0
路由器请求(9.6节)
·

11

超时:



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

·

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

·
12

参数问题:



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

·

1
    缺少必需的选项

·
13
0
时间戳请求(6.4节)
·

14
0
时间戳回答(6.4节)
·

15
0
信息回答(作废不用)
·

16
0
信息回答(作废不用)
·

17
0
地址掩码请求(6.3节)
·

18
0
地址掩码回答(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回答的往返时间差不多。

6.6  ICMP报文的4.4BSD处理
    由于ICMP覆盖的范围很广泛,从致命差错到信息差错,因此即使在一个给定的系统
实现中,对每个ICMP报文的处理都是不相同的。图6.12的内容与图6.3相同,它显示的
是4.4BSD系统对每个可能的ICMP报文的处理方法。
    如果最后一列标明是“内核”,那么ICMP就由内核来处理。如果最后一列指明是
“用户进程”,那么报文就被传送到所有在内核中登记的用户进程,以读取收到的ICMP
报文。如果不存在任何这样的用户进程,那么报文就悄悄地被丢弃。(这些用户进程还
会收到所有其他类型的ICMP报文的拷贝,虽然它们应该由内核来处理,当然用户进程只
有在内核处理以后才能收到这些报文。)有一些报文完全被忽略。最后,如果最后一列
标明的是引号内的一串字符,那么它就是对应的Unix差错。其中一些差错,如TCP对发
送端关闭的处理等,我们将在以后的章节中对它们进行讨论。

(下面是图6.12的译文)
类型
代码
描述
处理方法
0
0
回显回答
用户进程
3

目的不可到达:


0
    网络不可到达
“无路由到达主机”

1
    主机不可到达
“无路由到达主机”

2
    协议不可到达
“连接被拒绝”

3
    端口不可到达
“连接被拒绝”

4
    需要进行分片但设置了不分片比特DF
“报文太长”

5
    源站路由选择失败
“无路由到达主机”

6
    目的网络不认识
“无路由到达主机”

7
    目的主机不认识
“无路由到达主机”

8
    源主机被隔离(作废不用)
“无路由到达主机”

9
    目的网络被强制禁止
“无路由到达主机”

10
    目的主机被强制禁止
“无路由到达主机”

11
    由于服务类型TOS网络不可到达
“无路由到达主机”

12
    由于服务类型TOS主机不可到达
“无路由到达主机”

13
    由于过滤通信被强制禁止
(忽略)

14
    主机越权
(忽略)

15
    优先权中止生效
(忽略)
4
0
源站被抑制(quench)
TCP由内核处理,UDP则忽略
5

改变路由


0
    对网络改变路由
内核更新路由表

1
    对主机改变路由
内核更新路由表

2
    对服务类型和网络改变路由
内核更新路由表

3
    对服务类型和主机改变路由
内核更新路由表
8
0
回显请求

9
0
路由器通告
用户进程
10
0
路由器请求
用户进程
11

超时:


0
    传输期间生存时间为0
用户进程

1
    在数据报组装期间生存时间为0
用户进程
12

参数问题:


0
    坏的IP首部(包括各种差错)
“协议不可用”

1
    缺少必需的选项
“协议不可用”
13
0
时间戳请求
内核产生回答
14
0
时间戳回答
用户进程
15
0
信息请求(作废不用)
(忽略)
16
0
信息回答(作废不用)
用户进程
17
0
地址掩码请求
内核产生回答
18
0
地址掩码回答
用户进程
图6.12 4.4BSD系统对ICMP报文的处理

6.7 小结
    本章对每个系统都必须包括的Internet控制报文协议进行了讨论。图6.3列出了所
有的ICMP报文类型,其中大多数我们都将在以后的章节中加以讨论。
    我们详细讨论了ICMP地址掩码请求和回答以及时间戳请求和回答。这些是典型的请
求-回答报文。二者在ICMP报文中都标识符和序号。发送端应用程序在标识字段内存入
一个唯一的数值,以区别于其它进程的回答。序号字段使得客户程序可以在回答和请求
之间进行匹配。
    我们还讨论了ICMP端口不可到达差错,一种常见的ICMP差错。我们对返回的ICMP差
错信息进行了分析:导致差错的IP数据报的首部及后序8个字节。这个信息对于ICMP差
错的接收方来说是必要的,可以更多地了解导致差错的原因。这是因为TCP和UDP都在它
们的首部前8个字节中存入源端口号和目的端口号。
    最后,我们第一次给出了按时间先后的tcpdump输出,这种表现方式的输出在本书
后面的章节中会经常用到。

习题
6.1 在6.2节的末尾我们列出了5种不发送ICMP差错报文的特殊条件。如果这些条件不满
足而我们又在局域网上向一个似乎不存在的端口号发送一份广播UDP数据报,这时会发
生什么样的情况?
6.2 阅读RFC [Braden 1989a],注意生成一个ICMP端口不可到达差错是否为“必须”,
“应该”或者“可能”。这些信息所在的页码和章节是多少?
6.3 阅读RFC 1349 [Almquist 1992],看看IP的服务类型字段(图3.2)是如何被ICMP
设置的?
6.4 如果你的系统提供netstat命令,请用它来查看接收和发送的ICMP报文类型。

6-9
7  Ping程序

7.1 引言
    “ping”这个名字来自于声纳定位操作。Ping程序由Mike Muuss编写,目的是为了
测试另一台主机是否可达。该程序发送一份ICMP回显请求报文给主机,并等待返回ICMP
回显回答。(图6.3列出了所有的ICMP报文类型。)
    一般来说,如果你不能Ping到某台主机,那么你就不能Telnet或者FTP到那台主机。
反过来,如果你不能Telnet到某台主机,那么通常可以用Ping程序来确定问题出在哪里。
Ping程序还能测出到这台主机的往返时间,以表明该主机离我们有“多远”。
    在本章中,我们将使用Ping程序作为诊断工具来深入剖析ICMP。Ping还给我们提供
了检测IP记录路由和时间戳选项的机会。文献[Stevens 1990]的第11章提供了Ping程序
的源代码。

(下面是原书p.85①的译文)
    几年前我们还可以作出这样没有限定的断言,如果我们不能Ping到某台主机,那么
就不能Telnet或FTP那台主机。随着Internet安全意识的增强,出现了提供访问控制清
单的路由器和防火墙,那么像这样没有限定的断言不再成立了。一台主机的可达性可能
不只取决于IP层是否可达,还取决于使用何种协议以及端口号。Ping程序的运行结果可
能显示某台主机不可达,但我们可以用Telnet远程登录到该台主机的25号端口(邮件服
务器)。

7.2  Ping程序
    我们称发送回显请求的ping程序为客户,而称被ping的主机为服务器。大多数的
TCP/IP实现都在内核中直接支持Ping服务器——这种服务器不是一个用户进程。(我们
在第6章中描述的两种ICMP查询服务,地址掩码和时间戳请求,也都是直接在内核中进
行处理的。)
    ICMP回显请求和回显回答报文如图7.1所示。

图7.1 ICMP回显请求和回显回答报文格式

    对于其他类型的ICMP查询报文,服务器必须响应标识符和序号字段。另外,客户发
送的选项数据必须回显,假设客户对这些信息都会感兴趣。
    Unix系统在实现ping程序时是把ICMP报文中的标识符字段置成发送进程的ID号。这
样即使在同一台主机上同时运行了多个ping程序实例,ping程序也可以识别出返回的信
息。
    序号从0开始,每发送一次新的回显请求就加1。ping程序打印出返回的每个分组的
序号,允许我们查看是否有分组丢失,失序或重复。IP是一种最好的数据报传递服务,
因此这三个条件都有可能发生。
    旧版本的ping程序曾经以这种模式运行,即每秒发送一个回显请求,并打印出返回
的每个回显回答。但是,新版本的实现需要加上-s参数才能以这种模式运行。默认情况
下,新版本的ping程序只发送一个回显请求,如果收到回显回答则输出“host is
alive”,否则在20秒内没有收到回答就输出“no answer”(没有回答)。

LAN输出
    在局域网LAN上运行ping程序的结果输出一般有如下格式:

(见原书p.86的①)

    当返回ICMP回显回答时,要打印出序号和TTL,并计算往返时间。(TTL位于IP首部
中的生存时间字段。当前的BSD系统中的ping程序每次收到回显回答时都打印出收到的
TTL----有些系统并不这样做。我们将在第8章中通过traceroute程序来介绍TTL的用
法。)
    我们从上面的输出中可以看出,回显回答是以发送的次序返回的(0,1,2等等)。
    ping程序通过在ICMP报文数据中存放发送请求的时间值来计算往返时间。当回答返
回时,用当前时间减去存放在ICMP报文中的时间值,即是往返时间。注意,在发送端
bsdi上,往返时间的计算结果都为0 ms。这是因为程序使用的计时器分辨率低的原因。
BSD/386版本0.9.4系统只能提供10 ms级的计时器。(我们在附录B中有更详细的介绍。)
在后面的章节中,当我们在具有较高分辨率计时器的系统上(Sun)查看tcpdump输出时
会发现,ICMP回显请求和回显回答的时间差在4 ms以下。
    输出的第一行包括目的主机的IP地址,尽管我们指定的是它的名字(svr4)。这说
明名字已经经过解析器被转换成IP地址了。我们将在第14章介绍解析器和DNS。现在,
我们发现,如果敲入ping命令,几秒钟过后会在第一行打印出IP地址,DNS就是利用这
段时间来确定主机名所对应的IP地址。
    本例中的tcpdump输出如图7.2所示。

图7.2 在LAN上运行ping程序的结果

    从发送回显请求到收到回显回答,时间间隔始终为3.7 ms。我们还可以看到,回显
请求大约每隔1秒钟发送一次。
    通常,第一个往返时间值要比其他的大。这是由于目的端的硬件地址不在ARP高速
缓存中的缘故。正如我们在第4章中看到的那样,在发送第一个回显请求之前要发送一
个ARP请求并接收ARP回答,这需要花费几毫秒的时间。下面的例子说明了这一点:

(见原书p.88的①)

    第一个RTT中多的3 ms很可能就是因为发送ARP请求和接收ARP回答所花费的时间。
    这个例子运行在sun主机上,它提供的是具有微秒级分辨率的计时器,但是ping程
序只能打印出毫秒级的往返时间。在前面运行于BSD/386 0.9.4版上的例子中,打印出
来的往返时间值为0 ms,这是因为计时器只能提供10 ms的误差。下面的例子是BSD/386
1.0版的输出,它提供的计时器也具有微秒级的分辨率,因此ping程序的输出结果也具
有较高的分辨率。

(见原书p.88的②)

WAN输出
    在一个广域网WAN上,结果会有很大的不同。下面的例子是在某个工作日的下午即
Internet具有正常通信量时的运行结果:

(见原书p.88的③)

    这里,序号为1,2,3,4,6,10,11,12和13的回显请求或回显回答在某个地方
丢失了。另外,我们注意到往返时间发生了很大的变化。(像52%这样高的分组丢失率
是不正常的。即使是在工作日的下午,对于Internet来说也是不正常的,。)
    通过广域网还有可能看到重复的分组(即相同序号的分组被打印两次或更多次),
失序的分组(序号为N + 1的分组在序号为N的分组之前被打印。)

线路SLIP链接
    让我们再来看看SLIP链路上的往返时间,因为它们经常运行于低速的异步方式,如
9600 b/s或更低。回想我们在2.10节计算的串行线路吞吐量。针对这个例子,我们把主
机bsdi和slip之间的SLIP链路传输速率设置为1200 b/s。
    下面我们可以来估计往返时间。首先,我们从前面的Ping程序输出例子中可以注意
到,默认情况下发送的ICMP报文有56个字节。再加上20个字节的IP首部和8个字节的
ICMP首部,IP数据报的总长度为84字节。(我们可以运行tcpdump -e命令查看以太网数
据帧来验证这一点。)另外,从2.4节我们可以知道,至少要增加两个额外的字节:在
数据报的开始和结尾加上END字符。此外,SLIP帧还有可能再增加一些字节,但这取决
于数据报中每个字节的值。对于1200 b/s这个速率来说,由于每个字节含有8 bit数据,
1 bit起始位和1 bit结束位,因此传输速率是每秒120个字节,或者说每个字节8.33 ms。
所以我们可以估计需要1433(86×8.33×2) ms 。(乘2是因为我们计算的是往返时
间。)
    下面的输出证实了我们的计算:

(见原书p.89的①)

    (对于SVR4来说,如果每秒钟发送一次请求则必须带-s参数。)往返时间大约是
1.5秒,但是程序仍然每间隔1秒钟发送一次ICMP回显请求。这说明在第一个回显回答返
回之前(1.480秒时刻)就已经发送了两次回显请求(分别在0秒和1秒时刻)。这就是
为什么总结行指出丢失了一个分组。实际上分组并未丢失,很可能仍然在返回的途中。
    我们在第8章讨论traceroute程序时将回头再讨论这种低速的SLIP链路。

拔号SLIP链路
    对于拔号SLIP链路来说,情况有些变化,因为在链路的两端增加了调制解调器。用
在sun和netb系统之间的调制解调器提供的是V.32调制方式(9600 b/s),V.42错误控
制方式(也称作LAP-M),以及V.42bis数据压缩方式。这表明我们针对线路链路参数进
行的简单计算不再准确了。
    很多因素都有可能影响。调制解调器带来了时延。随着数据的压缩,分组长度可能
会减小,但是由于使用了错误控制协议,分组长度又可能会增加。另外,接收端的调制
解调器只能在验证了循环检验字符(检验和)后才能释放收到的数据。最后,我们还要
处理每一端的计算机异步串行接口,许多操作系统只能在固定的时间间隔内,或者收到
若干字符后者才去读这些接口。
    作为一个例子,我们在sun主机上ping主机gemini,输出结果如下:

(见原书p.90的①)

    注意,第一个RTT不是10 ms的整数倍,但是其它行都是10 ms的整数倍。如果我们
运行该程序若干次,发现每次结果都是这样。(这并不是由sun主机上的时钟分辨率造
成的结果,因为根据附录B中的测试结果可以知道它的时钟能提供毫秒级的分辨率。)
    另外还要注意,第一个RTT要比其他的大,而且依次递减,然后徘徊在280至300 ms
之间。我们让它运行一分钟到两分钟,RTT一直处于这个范围,不会低于260 ms。如果
我们以9600 b/s的速率计算RTT(习题7.2),那么我们观察到的值应该大约是估计值的
1.5倍。
    如果运行ping程序60秒钟并计算观察到的RTT的平均值,我们发现在V.42和V.42bis
模式下平均值为277 ms。(这要比上个例子打印出来的平均值要好,因为运行时间较长,
这样就把开始较长的时间平摊了。)如果我们关闭V.42bis数据压缩方式,平均值为330
ms。如果我们关闭V.42错误控制方式(它同时也关闭了V.42bis数据压缩方式),平均
值为300 ms。这些调制解调器的参数对RTT的影响很大,使用错误控制和数据压缩方式
似乎效果最好。

7.3  IP记录路由选项
    ping程序为我们提供了查看IP记录路由(RR)选项的机会。大多数不同版本的ping
程序都提供-R参数,以提供记录路由的功能。它使得ping程序在发送出去的IP数据报中
设置IP RR选项(该IP数据报包含ICMP回显请求报文)。这样,每个处理该数据报的路
由器都把它的IP地址放入选项字段中。当数据报到达目的端时,IP地址清单应该复制到
ICMP回显回答中,这样返回途中所经过的路由器地址也被加入清单中。当ping程序收到
回显回答时,它就打印出这份IP地址清单。
    这个过程听起来简单,但存在一些缺陷。源端主机生成RR选项,中间路由器对RR选
项的处理,以及把ICMP回显请求中的RR清单复制到ICMP回显回答中,所有这些都是选项
功能。幸运的是,现在的大多数系统都支持这些选项功能,只是有一些系统不把ICMP请
求中的IP清单复制到ICMP回答中。
    但是,最大的问题是IP首部中只有有限的空间来存放IP地址。我们从图3.1可以看
到,IP首部中的首部长度字段只有4 bit,因此整个IP首部最长只能包括15个32 bit长
的字(即60个字节)。由于IP首部固定长度为20字节,RR选项用去3个字节(下面我们
再讨论),这样只剩下37个字节(60 - 20 - 3)来存放IP地址清单,也就是说只能存
放9个IP地址。对于早期的ARPANET来说,9个IP地址似乎是很多了,但是现在看来是非
常有限的。(在第8章中,我们将用Traceroute工具来确定数据报的路由。)除了这些
缺点,记录路由选项工作得很好,为详细查看如何处理IP选项提供了一个机会。
    IP数据报中的RR选项的一般格式如图7.3所示。

图7.3 IP首部中的记录路由选项的一般格式

    code是一个字节,指明IP选项的类型。对于RR选项来说,它的值为7。len是RR选项
总字节长度,在这种情况下为39。(尽管可以为RR选项设置比最大长度小的长度,但是
ping程序总是提供39字节的选项字段,最多可以记录9个IP地址。由于IP首部中留给选
项的空间有限,它一般情况都设置成最大长度。)
    ptr称作指针字段。它是一个基于1的指针,指向存放下一个IP地址的位置。它的最
小值为4,指向存放第一个IP地址的位置。随着每个IP地址存入清单,ptr的值分别为8,
12,16,最大到36。当记录下9个IP地址后,ptr的值为40,表示清单已满。
    当路由器(根据定义应该是多穴的)在清单中记录IP地址时,它应该记录哪个地址
呢?是入口地址还是出口地址?为此,RFC 791 [Postel 1981a]指定路由器记录出口IP
地址。我们在后面将看到,当原始主机(运行ping程序的主机)收到带有RR选项的ICMP
回显回答时,它也要把它的入口IP地址放入清单中。

正常的例子
    我们举一个用RR选项运行ping程序的例子。我们在主机svr4上运行ping程序到主机
slip。一个中间路由器(bsdi)将处理这个数据报。下面是svr4的输出结果:

(见原书p.92的①)

    分组所经过的四站如图7.4所示(每个方向各有两站),每一站都把自己的IP地址
加入RR清单。

图7.4 带有记录路由选项的ping程序

    路由器bsdi在不同方向上分别加入了不同的IP地址。它始终是把出口的IP地址加入
清单。我们还可以看到,当ICMP回显回答到达原始系统(svr4)时,它把自己的入口IP
地址也加入清单中。
    我们还可以通过运行带有-v参数的tcpdump命令来查看主机sun上进行的分组交换
(参见IP选项)。输出如图7.5所示。

图7.5 记录路由选项的tcpdump输出

    输出中optlen=40表示在IP首部中有40个字节的选项空间。(IP首部长度必须为4字
节的整数倍。)RR{39}的意思是记录路由选项已被设置,它的长度字段是39。然后是9
个IP地址,符号“#”用来标记RR选项中的ptr字段所指向的IP地址。由于我们是在主机
sun上观察这些分组(参见图7.4),因此我们所能看到ICMP回显请求中的IP地址清单是
空的,而ICMP回显回答中有3个IP地址。我们省略了tcpdump输出中的其它行,因为它们
与图7.5基本一致。
    位于路由信息末尾的标记EOL表示IP选项“end of list”(清单结束)的值。EOL
选项的值可以为0。这时表示39个字节的RR数据位于IP首部中的40字节空间中。由于在
数据报发送之前空间选项被设置为0,因此跟在39个字节的RR数据之后的0字符就被解释
为EOL。这正是我们所希望的结果。如果在IP首部中的选项字段中有多个选项,在开始
下个选项之前必须填入空白字符,另外还可以用另一个值为1的特殊字符NOP(“no
operation”)。

(下面是原书p.93①的译文)
    在图7.5中,SVR4把回显请求中的TTL字段设为32,BSD/386设为255。(它打印出的
值为254是因为路由器bsdi已经将其减去1。)新的系统都把ICMP报文中的TTL设为最大
值(255)。
    在作者使用的三个TCP/IP系统中,BSD/386和SVR4都支持记录路由选项。这就是说,
当转发数据报时,它们都能正确地更新RR清单,而且能正确地把接收到的ICMP回显请求
中的RR清单复制到出口ICMP回显回答中。虽然SunOS 4.1.3在转发一个数据报时能正确
更新RR清单,但是不能复制RR清单。Solaris 2.x对这个问题已作了修改。

推荐阅读

 

热点信息

 
强悍的草根IT技术社区,这里应该有您想要的! 友情链接:b2b电子商务
Copyright © 2010 Gimoo.Net. All Rights Rreserved  京ICP备05050695号