您的位置:农牧网资源中心 >> 电脑学堂 >> 互联网 >> 阅读资讯:TCP/IP详解(二)

TCP/IP详解(二)

[ 来源:本站整理 | 作者:佚名 | 时间:2008-7-10 9:57:23 | 收藏本文 ]

 

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回答的往返时间差不多。

>> 相关资讯:

上一篇文章:TCP/IP详解(一)   下一篇文章:TCP/IP详解(三)
最新五条评论
查看全部评论
您的评论

分 值:100分 85分 70分 55分 40分 25分 10分 0分

用户名: 新注册 验证码: 验证码,看不清楚?请点击刷新验证码
·用户发表意见仅代表其个人意见,并且承担一切因发表内容引起的纠纷和责任
·本站管理人员有权在不通知用户的情况下删除不符合规定的评论信息或留做证据
·请客观的评价您所看到的资讯,提倡就事论事,杜绝漫骂和人身攻击等不文明行为
内容搜索
最新更新文章
本类推荐文章