0%

学习TCPIP(5)-网际协议 IPv4-差错与控制报文(ICMP)

学习TCPIP(5)-网际协议 IPv4-差错与控制报文(ICMP)

引言

当互联网通信的过程中在某一结点发生错误时,IP会通过差错与控制报文(ICMP)最初源站发送一个ICMP包从而向源站上的网际协议软件报告发生了差错。

ICMP协议是由ICMP软件负责处理的;也就是说,当一个ICMP报文到达后,ICMP软件模块会对它进行处理。当ICMP确定是由某个高层协议或应用程序引起的问题,它会通知相应的模块。

值得注意的是,ICMP报告错误总是报告给最初源站。也就是说,即使这个错误是由传输过程中的负责转发的某个路由器造成的,ICMP也只会报告给最初发送这个数据包的路由器/主机。

ICMP报文的封装

ICMP报文需要两级封装。也即,ICMP先封装在IP数据报的数据部分,然后IP数据报再封装在帧里面,最后通过物理网络传送。此外,携带ICMP报文的数据报完全就像携带用户信息的数据报一样转发,没有而外的可靠性或优先级。也就是说,差错报文本身也可能丢失或被丢弃,或者当一个网络处于拥堵状态时,在此网络上传送ICMP报文也可能造成而外的拥堵。

其次,如果携带了ICMP报文的IP数据报发生了差错,在差错处理过程中会产生一个异常而不会为了差错控制报文的差错再生成一个差错控制报文。

再次,即使ICMP报文是用IP数据报封装和发送的,但是由于ICMP是IP的一个必要组成部分,所以它不是一个高层协议。之所以ICMP是用IP传送的,而不直接封装到帧里面,是因为它们可能需要经过记过物理网络才能到达最终目的地。它们不能单独通过物理网络进行传送。

ICMP报文的格式

首先需要说明的是,ICMP的Protocol Number(也就是IP数据报首部的协议PROTOCOL字段)是1.

各个ICMP报文都有自己的格式。但是,它们开头的32位都是相同的。这32位的内容格式如下所示:

TYPE 占8位 用于标识这个ICMP报文具体是属于哪一种类型。 CODE 占8位 提供了有关报文类型的更多信息。 CHECKSUM 占16位 校验和字段。ICMP使用与IP相同的校验和算法,但是ICMP校验和的计算只包括ICMP报文。

大多数ICMP报文在ICMP数据区域会附加一些从产生问题的数据报中提取出来的首部和一些额外的字节。ICMP会返回一些额外的字节的原因是,为了让接收方更加准确的判断是哪个(些)协议或应用程序负责该数据报。

TYPE和CODE的各种组合用于标识各异的ICMP报文类型,有关所有ICMP报文类型请参见 Wikipedia

回送请求 & 回送回答

回送请求(Echo Request) & 回送回答(Echo Reply)ICMP报文是TCP/IP协议提供给网络管理员或用户识别网络的工具。著名的ping工具就是基于此类型的ICMP报文实现的。

冷知识:ping其实是Dave Mills(David Mills)提出的Package InterNet Groper(分组互联网探索者)的首字母缩略词

回送请求(Echo Request)的类型字段(TYPE,在ICMP首部内)是8,回送回答(Echo Reply)的类型字段(TYPE,同样在ICMP首部内)是0.

因为请求和回答都是在IP软件中完成的,所以若目标站点成功响应回送请求报文就说明本站和目标站及与之连接的传输系统大体上是正常运行的。

因为目标站点要想回复回送请求报文至少需要满足下述四个条件:

1. 源站计算机上的IP软件必须转发数据报;
2. 在源站和目标站之间的中间路由器必须是在运行状态;
3. 目的站机器必须正常运行:它至少应该能够响应中断
4. 在返回路径的所有路由器路由表中,必须含有形成可行路径的所需信息。

回送请求与回送回答的ICMP报文格式

ECHO REQUEST 和 ECHO REPLY报文的格式如下图所示:

image-20210113114945079
image-20210113115018752
image-20210113115043798

注:该图来自于 Wikipedia of Ping#ICMP_packetWikipedia of Ping#Echo_requestWikipedia of Ping#Echo_reply

其中,ICMP Header的Type of Message(类型)为8或0(分别标志着ICMP回送请求和ICMP回送回答报文),Code(代码)为0.

发送方通过指定标识符(Identifier)和序号(Sequence Number)来匹配echo请求和回答报文。通过TYPE字段是8还是0,可以识别出这是一个回送请求还是回送回答。

目的站不可达报告

当路由器无法转发或交付一个数据报时,它会向最初源站发送一个目的站不可达(Destination unreachable)报文。

目的站不可达的报文类型(TYPE)字段是3,代码字段(CODE)是0~15之间的一个整数,用于标识具体的错误类型:

image-20210113115107844

这个类型的报文格式如下所示:

1
2
3
4
5
6
7
8
9
 0                   1                   2                   3
0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
| Type | Code | Checksum |
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
| unused(Must be 0) |
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
| Internet Header + 64 bits of Original Data Datagram |
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+

虽然IP尽最大努力转发数据报,但是如果IP数据报本身传输的过程中发生了错误,那么IP会丢弃掉这个数据报(记住,IP数据报只能够转发未发生错误的数据报,并提供了数据报正确性校验机制,但是对于数据报本身已经发生了错误的情况下无能为力)。

通常情况下,ICMP报文会携带发生问题的数据报的短前缀,以便源站能够准确地识别出是哪个地址不可达。

但是,ICMP报文不应被视为解决网络问题的"万能神丹"。因为ICMP不能够检测出所有的差错:例如路由硬件故障(导致不能发送ICMP差错报文回源站)、或者发送方指定了一个并不存在的目的站地址(这种情况下目的站网段路由器会正常转发数据报,只是收不到目的站的响应,自然目的站网段路由器不会检测到故障)。

一些会触发ICMP报文的典型例子: 1.数据报包含一个路由不正确的源路由选项-->激发源路由(source route failed)故障报文(属于目的站不可达报文的一种)。 2.路由器检测到数据报太大而需要分片但是IP数据报里设置了DNF(Do Not Fragment,不分片)选项-->激发需要分片(Fragmentation needed)报文。

拥塞与数据报流量控制

因为IP是无连接的协议,路由器或主机在收到数据报之前不能为其预留内存或通信资源,因此当发送方数据报发送速率过快使得接收方来不及接受所有数据报时就会发生所谓的拥塞现象:发送方数据报发送速率超过了接收方最大能承受的数据报接受速率。

为了解决这个问题,接收方在因为发送方速率过快而无法接收进而丢弃了数据报时,会向源站发送一个ICMP源站抑制(source quench)报文。当路由器或主机接收到这种报文时,会无条件降低报文发送速度直到没有接收到新的ICMP源站抑制报文为止。(通常每收到一个ICMP源站抑制报文时就会降低一次)

另一种与ICMP源站抑制不同的拥塞管理机制叫做RED(Random Early Discard)。这种机制使路由器在缓存空间快要用完时不采用尾部丢弃的方法来管理缓存。见维基百科

通常,大多数TCP/IP实现都不会是用这个源站抑制,而是依靠于更高层的协议(如TCP)来对拥塞做出反应。

ICMP源站抑制(source quench)的报文格式见Wikipedia.

注:由于研究表明“源站抑制是一种无效的(和不公平的)补救措施“,所以路由的源站抑制报文已在1995年被RFC 1812弃用。此外,(路由)转发和回应任何形式的源站抑制报文已在2012年被RFC 6633弃用。

路由重定向

我们假定路由器总是知道正确的路由(至少存在一条默认路由)。主机在开机之时,只有很少量的路由信息,之后主机从路由器那了解路由信息。

初始状态下的主机可能只知道最少可能的路由信息。因此,主机需要依靠路由器来更新它的路由信息。当路由器检测到主机使用了一条非优化的路由时,它会向主机放送一条重定向(Redirect)报文,以请求主机改变它的路由。

ICMP路由重定向机制的优点在于它允许主机在开机时只知道本地网络上的一个路由器地址(默认路由),而主机就会把发往主机外部的报文发给这台路由器。当路由器知道这个报文有另外一个更佳的路由路径时,它就会向主机发送上述的ICMP重定向路由以告诉主机这条报文有更好的路由路径,并且路由器也会把收到的报文发送给目的站。

但是,重定向路由仅限于直连到同一网络上的路由器和主机之间的交互,不能跨网段交互。

路由重定向的报文类型(TYPE 字段)为5,代码(CODE)为0~3,其数据报格式如下图(你可以在Wikipedia找到)所示,每个重定向报文都包含一个32位的路由器的互联网地址(ROUTER INTERNET ADDRESS)字段,该字段指定该数据包应该被重定向到的路由器地址。此外,除了路由器的互联网地址之外,每个重定向报文还包含一个互联网首部(INTERNET HEADER)字段,该字段包含激发重定向报文的数据报的IP首部,再加上首部之后的紧接着的64位数据,该字段主要用于收到重定向报文的主机确定该数据报的目的站地址。

image-20210112152911758

其中Code(代码)字段的含义如下表所示:

代码值 含义
0 针对网络的重定向报文
1 针对主机的重定向报文
2 针对网络和服务类型的重定向报文
3 针对主机和服务类型的重定向报文

值得注意的是,路由器仅向主机发送重定向报文,而不对路由器发送重定向报文。在之后的文章中,将介绍路由器之间交换路由信息的机制。另外,RFC 1122指出,该类型报文应当由路由器发出,而不应该从主机发出。

路由旋转循环

由于路由器都使用其自身的路由表来计算下一跳,所以有可能出现数据报被两个及以上的路由器循环转发的现象,这种现象称之为路由选择循环(routing cycle)。正如前面几篇文章提到的,为了避免数据报在TCP/IP互联网中无休止地循环,每个IP数据报都有一个名为生存时间(Time to Live, TTL)的计数器(或者称为跳计数器(Hop count)),当该计数器达到0时就会丢弃该数据报。

一旦路由器检测到了数据报的生存时间计数器递减到0,或者等待数据报分片的过程中出现了超时,路由器就会丢弃该数据报,并向源站发送一个ICMP超时(ICMP Time Exceeded)的报文。报文的格式如下图所示。

image-20210112202029223

其中代码(Code)字段要么为0要么为1,用于说明超时的类别:

Code 解释
0 生存时间计数超时
1 分片重装超时

ICMP时间戳

由于互联网上的机器都是独立运行的,因此很有可能会出现时钟不同步的情况;如果各个机器间的时钟差别很大,那一些依赖于互联网时钟的协议(如https)可能就会无法正常运行。 为了同步各个机器之间的时钟,机器需要向其他机器发送一个ICMP时间戳请求(Timestamp Request)报文,请求它返回当前时间。通常情况下,接收到ICMP时间戳请求报文的机器会返回一个ICMP时间戳回答(Timestamp Reply)报文,以格林尼治标准时间的午夜开始的毫秒数来告诉询问方当前时间。

该报文的格式如下:

1
2
3
4
5
6
7
8
9
10
11
12
13
 0                   1                   2                   3
0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
| Type | Code | Checksum |
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
| Identifier | Sequence Number |
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
| Originate Timestamp |
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
| Receive Timestamp |
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
| Transmit Timestamp |
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+

其中类型(Type)字段用于标识该报文的类型:ICMP时间戳请求(Type = 13)或ICMP时间戳回答(Type = 14),代码(Code)字段固定为0,标识(Identifier)和序号(Sequence Number)用于在请求报文和回答报文之间建立关联;

起始时间戳(Originate Timestamp,下文简称OT)字段是在最初源站发送此报文前填写的;

接收时间戳(Receive Timestamp,下文简称RT)是接收方接收到此报文后填写的;

传输时间戳(Transmit Timestamp,下文简称TT)是传输回答时填写的。

当一个主机A需要得知与其他主机的时钟差值时,在t1时刻向主机B发送一个ICMP时间戳请求报文,T2时刻主机B接收到了该报文,并在T3时刻返回了ICMP时间戳回答报文,随后,主机A在t4时刻接收到了该回答报文。

计算两主机间的时钟差Delay的过程如下图所示:

how-calculate-delay

值得注意的是,t1和t4时刻的时间是针对于主机A而言的,T2和T3时刻的时间是针对于主机B而言的。

如果这个公式存在推导错误,请在此博文下方留言,欢迎指正。