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对这个问题已作了修改。
异常的输出
下面的例子是作者观察到的,我们把它作为第9章讨论ICMP间接报文的起点。我们
在子网140.252.1子网上ping主机aix(在主机sun上通过拔号SLIP连接可以访问),并
带有记录路由选项。在slip主机上运行有如下输出结果:
(见原书p.94的①)
我们已经在主机bsdi上运行过这个例子。现在我们选择slip来运行它,观察RR清单
中所有的9个IP地址。
在输出中令人感到疑惑的是,为什么传出的数据报(ICMP回显请求)直接从netb传
到aix,而返回的数据报(ICMP回显回答)却从aix开始经路由器gateway再到netb?这
里看到的正是下面我们将要描述的IP路由选择的一个特点。数据报经过的路由如图7.6
所示。
图7.6 运行带有记录路由选项的ping程序,显示IP路由选择的特点
问题是aix不知道要把目的地为子网140.252.13的IP数据报发到主机netb上。相反,
aix在它的路由表中有一个默认项,它指明当没有明确某个目的主机的路由时,就把所
有的数据报发往默认项指定的路由器gateway。路由器gateway比子网140.252.1上的任
何主机都具备更强的路由选择能力。(在这个以太网上有超过150台主机,每台主机的
路由表中都有一个默认项指向路由器gateway,这样就不用在每台主机上都运行一个路
由选择守护程序。)
这里没有回答的一个问题是为什么gateway不直接发送ICMP报文改变路由到aix
(9.5节),以更新它的路由表?由于某种原因(很可能是由于数据报产生的改变路由
是一份ICMP回显请求报文),改变路由并没有产生。但是如果我们用Telnet登录到aix
上的daytime服务器,ICMP就会产生改变路由,因而它在aix上的路由表也随之更新。如
果我们接着执行ping程序并带有记录路由选项,其路由显示表明数据报从netb到aix,
然后返回netb,而不再经过路由器gateway。在9.5节中我们将更详细地讨论ICMP改变路
由的问题。
7.4 IP时间戳选项
IP时间戳选项与记录路由选项类似。IP时间戳选项的格式如图7.7所示(请与图7.3
进行比较)。
图7.7 IP首部中时间戳选项的一般格式
时间戳选项的代码为0x44。其它两个字段len和ptr与记录路由选项相同:选项的总
长度(一般为36或40)和指向下一可用空间的指针(5,9,13,等)。
接下来的两个字段是4 bit的值:OF表示溢出字段,FL表示标志字段。时间戳选项的操
作根据标志字段来进行,如图7.8所示。
(下面是图7.8的译文)
标志
描述
0
只记录时间戳,正如我们在图7.7看到的那样。
1
每台路由器都记录它的IP地址和时间戳。在选项列表中只有存放四对地址和时间戳的空
间。
3
发送端对选项列表进行初始化,存放了4个IP地址和四个取值为0的时间戳值。只有当列
表中的下一个IP地址与当前路由器地址相匹配时,才记录它的时间戳。
图7.8 时间戳选项不同标志字段值的意义
如果路由器由于没有空间而不能增加时间戳选项,那么它将增加溢出字段的值。
时间戳的取值一般为自午夜开始计的毫秒数,UTC,与ICMP时间戳请求和回答相类
似。如果路由器不使用这种格式,它就可以插入任何它使用的时间表示格式,但是必须
打开时间戳中的高位以表明为非标准值。
与我们遇到的记录路由选项所受到的限制相比,时间戳选项遇到情况要更坏一些。
如果我们要同时记录IP地址和时间戳(标志位为1),那么就可以同时存入其中的四对
值。只记录时间戳是没有用处的,因为我们没有标明时间戳与路由器之间的对应关系
(除非我们有一个永远不变的拓扑结构)。标志值取3会更好一些,因为我们可以插入
时间戳的路由器。一个更为基本的问题是,你很可能无法控制任何给定路由器上时间戳
的正确性。这使得试图用IP选项来计算路由器之间的跳站数是徒劳的。我们将看到(第
8章)traceroute程序可以提供一种更好的方法来计算路由器之间的跳数。
7.5 小结
ping程序是对两个TCP/IP系统连通性进行测试的基本工具。它只利用ICMP回显请求
和回显回答报文,而不用经过传输层(TCP/UDP)。Ping服务器一般在内核中实现ICMP
的功能。
我们分析了在LAN,WAN以及SLIP链路(拔号和线路)上运行ping程序的输出结果,
并对串行线路上的SLIP链路吞吐量进行了计算。我们还讨论并使用了ping程序的IP记录
路由选项。利用该IP选项,我们可以看到它是如何经常使用默认路由的。在第9章我们
将再次回到这个讨论主题。另外,我们还讨论了IP 时间戳选项,但它在实际使用时有
所限制。
习题
7.1 请画出7.2节中ping输出的时间线。
7.2 若把bsdi和slip主机之间的SLIP链路设置为9600 b/s,请计算这时的RTT。假定默
认的数据是56字节。
7.3 当前BSD版中的ping程序允许我们为ICMP报文的数据部分指定一种模式。(数据部
分的前8个字节不用来存放模式,因为它要存放发送报文的时间。)如果我们指定的模
式为0xc0,请重新计算上一题中的答案。(提示:阅读2.4节。)
7.4 使用压缩SLIP(CSLIP,见2.5节)是否会影响我们在7.2节中看到的ping输出中的
时间值?
7.5 在图2.4中,ping环回地址与ping主机以太网地址会出现什么不同?
7-7