七叶笔记 » golang编程 » 带你深入熟悉你所不知道的ICMP协议

带你深入熟悉你所不知道的ICMP协议


简介

Internet Control Messages Protocol 因特网控制 报文 协议,经常被认为是IP层的一个组成部分。它传递差错报文以及其他需要注意的信息。 ICMP 报文通常被IP层或更高层协议(TCP或 UDP )使用。一些ICMP报文把差错报文返回给用户。

ICMP是在IP数据报文中,直接传输。
不同类型由报文中的类型字段和代码字段来共同决定 ,比如 type 为8 Code 为0,那么这个为ICMP的Echo Request 如果Type 为0 Code为0 那么就为ICMP的 reply 还包含很重要的Identifier 2个字节:用于区分不同进程的应用。比如, Ping 测试,开启了两个窗口,它用于这个标识这个Request 与Reply
到低 属于第一个窗口,还是第二个窗口。

Sequence Number:2个字节:用于对应请求与响应 :比如,我们都知道一个Ping ,在cisco路由器上会发送5个,那么第一个Request Sequence number为1 ,如果收到了关于第一个Request的回应,那么这个reply的Sequence number也为1,如果丢失了,那么就没有响应。

R1——————–R2, 测试 R1发送两次 ping包,得到的 抓包 结果

可以从 id=0x0001,Seq=1/256 发现Requset 与reply都为1,第二个Requset为2,reply也为2 那么展开看看Identifier的标识,都显示为1, 开始发送第二个Ping包。

抓包结果,Id=0x0003,下面一个Identifier也为3,

Ping通过Sequence Number与ID标识,正确的回显了Request与reply的信息,并且它们的数据中通常Requset与reply是不同的,它们依靠这个计算出源到达目往返使用的时间。
下面是ICMP的详细类型与代码信息。这才是
正真 有用的东西。

CMP 另外一个重大的作用是,当一个应用程序出现某些错误或者不可达的信息时候,会返回一个ICMP差错报文,用于告诉源这个错误的信息,如果在开发程序的时 候,调用了这个机制,那么当收到这个错误信息,提交给应用程序的时候,这个程序会自动断开服务,并且报错,比如 我们在路由器上常用的telnet ,如果被ACL拒绝了,会显示一个ICMP错误信息,提示 Destination unreachable; gateway or host down,这对故障排错是很有作用的。

以太网 | IP首部 | ICMP首部 | IP 首部 | 8字节差错数据包
当 发送一份ICMP 差错报文时,报文始终包含IP的首部和产生ICMP差错报文的IP数据包的前8个字节。这8个字节标识的是是哪种协议产生的差错(比如TCP UDP,会标识它的源目端口号 TCP还会有序列号) 由于告诉源的应用程序 然后由这个应用程序发送差错提示

R1———–R2————R3 R3上写了ACL决绝R1的流量,R1 Telnet R3,返回了一个ICMP错误消息

上面已经提到过,一个差错报文始终包含IP的首部和IP数据包的前8个字节,也就是说不包含IP首部在内 ,就是TCP的源目端口与序列号。
仔细看这个抓包的结果,有两个IP头部,那么表达的意思是什么。

首先第一个IP首部,标识产生这个ICMP差错报文的源是谁(这里是R3拒绝掉流量,所以为R3) 标识的类型为3,code为13,通过上面的对应表 可以看出是由于过滤,通信不可达。

第二个IP首部,标识了这个Telnet的发起端为谁,(这里为R1) 并且标识了因为什么协议产生的差错
这 样做,是为了保证源端在运行许多应用程序的情况下,能清楚
知道是因为什么程序而出错,然后通过这个错误回显给应用程序,如果编程人员在设计代码的时候, 调用了这个机制,那么就能立即停止服务 并且报错,如果,没有这个机制,那么就会一直发送请求。比如 TFTP ,它是不会识别这种差错报文,所以 我们通常在做TFTP升级IOS 或者配置的时候,由于没打开TFTP软件,服务器发送了差错报文,但是TFTP识别不了,就会一直在请求当中。 这对我们排错也非常有用,能有效 了解到流量是在哪被过滤或者出错的。 建议在内部网络中开启ICMP的应答,在边界网络关闭,因为对于外部的恶意流量,没必要回答这个消息,浪费路由器资源,通过 no ip unreachables
也可以通过debug ip icmp,会得到一个提示。

Tracerouter的实现:Tracerouter是为了解决IP提供的可选字段中的路径记录功能不足而开发的,因为路径记录只能记录9个地址的存在,并且包括来回,并不适应现网络的环境,而Tracerouter利用 TTL ,来达到路径记录的特效。

Tracerouter的原理:在Tracerouter一个目的的时候,源端会发送3个大于30000端口的UDP报文,TTL=1,当第一个路由器收 到以后,会把TTL置0,而丢弃这个数据包,并返回一个ICMP TTL exceeded的消息,当源收到这个错误消息后,再次发送3个大于30000端口UDP报文,TTL=2,当传达到第二个路由器的时候,把TTL置0, 丢弃这个数据,并返回ICMP TTL exceeded消息,当源收到以后,继续发送UDP报文,TTL=3,这样直到到达目的地,当目的主机收到以后,发现是给自己的数据包,发现自己并没有 打开关于这个UDP端口号的服务,
(大于30000端口,就是为了确保目的端没有使用这个端口)从而回复一个“端口不可达”错误的ICMP消息,从而实现路径记录的功能,因为回显错误信息中,都包含了它们的地址。

R1———R2—————-R3, R1上Tracerouter R3,经过了2跳,所以看到有关于6个UDP的数据包,发现前面的都是 TTL exceeded,而最后一个是 Destination Unreachalbe

Tracerouter的功能,在应用中还是比较适用的,可以探测经过了哪些路由器,和路径测试,并且如果能从DNS解析到它们的名字,那么它们就会显示出来经过路由器的名字。 有些OS的实现可能不同,比如微软基于TTL值来测试的

最后一个关于分片:分片在网络中,经常出现,但是在现代的网络中 因该合理的控制 MTU ,避免不必要的分片,特别是UDP的数据,最容易造成分片,TCP由于有流量控制与 MSS 机制。 分片最容易出现在VPN的网络中,因为VPN在加密的情况下,使用ESP 、tunnel Mode、GRE、等协议,还会产生新的头部,往往会超过IP MTU 1500, 至于为什么是1500,后面会解释,先介绍下特点
IP分片:1、IP把MTU与数据长度进行比较
2、如果需要则进行分片。分片可以发生在原始发送端主机上,也可以发生在中间路由器上
3、把一个IP数据包分片以后,只有到达目的地才进行重新组装。(FR fragment)
4、重新组装由目的端的IP层来完成,其目的是使分片和重新组装的过程对运输层(TCP和UDP)是透明的。
5、已经分片的数据包有可能再次进行分片(可能不只一次)
6、当IP数据包被分片以后,每一片都成为一个分组,具有自己的IP首部,并且选择路由的时候与其他分片独立,这样,当数据包到达目的地的时候,可能不是同一路径,会失序,但是IP首部中有足够的信息让
接收端能正确的组装这些分片 比如是ICMP、TCP与UDP的流量话,那么只有第一个分片有ICMP、TCP或UDP的首部信息,其余的都没有。 而且这些头部信息,也算在数据部分里面。
7、尽管IP分片在过程中看起来是透明的,但是如果中途丢失了其中一个分片的话,那么整个数据包重新发送。
8、IP层本身没有超时重传机制————它必须依靠高层来提供可靠性(TCP有超时和重传机制,但UDP没有。一些UDP 应用程序本身提供超时和重传),当来自TCP报文段的某一片丢失后,TCP在超时后会重新发送整个
TCP报文段,不能只重传某一片数据报。
比如一个TCP的数据大小为2000,分成两个分片,但是其中一个丢失了,那么TCP会发送重传机制,但是重传的是整个数据包,因为TCP是需要对整个数据包最确认,而不是分片,而且分片对于TCP和UDP是透明的。
9、如果对数据报分片的是中间路由器,而不是起始端,那么起始端系统无法知道数据报如何被分片。所以,经常要避免分片的存在。
由 于以太网EthernetII最大的数据帧是1518Bytes,除去以太网帧的帧头(DMAC目的MAC地址 48bit=6Bytes+SMAC源MAC地址48bit=6Bytes+Type域2bytes)14Bytes和帧尾CRC校验部分4Bytes (这个
部份 有时候大家也把它叫做FCS),那么剩下承载上层协议的地方也就是Data域最大就只能有1500Bytes,这个值我们就把它称之为MTU, 也就是指IP MTU。
这个MTU就是网络层协议非常关心的地方,因为网络层协议比如IP协议会根据这个值来决定是否把上层传下来的数据进行分 片。就好比一个盒子没法装下一大块面包,我们需要把面包切成片,装在多个盒子里面一样的道理。当两台远程PC互联的时候,它们的数据需要穿过很多的路由器 和各种各样的网络媒介才能到达对端,网络中不同媒介的MTU各不相同,就好比一长段的水管,由不同粗细的水管组成(MTU不同 )通过这段水管最大水量就要由中间最细的水管决定。


比如一个 数据包为1501,如下图

分成了两个分片,其中第一个分片包含UDP的首部,而第二个分片没有标识,只有IP的首部。 这样,如果中间经过了PAT的设备或者防火墙,很有可能造成丢失,而分片的丢失,代表整个数据包全部丢弃,UDP没有重传机制。
R1———R2————R3 ,这里我把R2的出接口MTU改为200, 我用R1 Ping 大小为1000,只发送一个Request
这里得到了5个分片包,这是软件的问题,通过Sniffer 之类的抓取的话,会有6个分片,不过可以通过计算算出来,

这是第一个分片,但是不知道为什么 这个软件也没有在第一个分片上显示ICMP的头部信息,其他软件都会显示。
首先,先看下我发送了一个1000字节的数据包,这1000字节包括了 20字节的IP首部 ,和ICMP 8字节的首部,那么数据部分因该是972,先看下未分片之前的抓包。 Data 为972

为什么分片的数据部分只有176呢 ,加上20字节的首部也并没有达到接口的200,那是分片中有一个规定 在分片的时候,除最后一片外,其他每一片中的数据部分(除IP首部外的最后一个分片)必须是8字节的整数倍。 所以这里为176,而不是180.
这里注意的是,第一个分片中,通常会包含协议的头部信息,比如TCP UDP、这里因该是ICMP的头部,但这个软件并没有显示,直接显示数据大小为176,实际因该是168+ICMP 8字节头部。

之 间说因该有6个分片,而这里只显示了5个分片,可能是软件的一种优化,但是,挺不习惯的。 这里5个分片数据部分都为176,所以176*5=880, 而实际数据部分是 972+ICMP 8字节 980,所以最后一个分片的数据部分是 980-880=100
关于MTU的探测:我们通常探测MTU,因该要探测路径MUT,所谓路径MTU,是指这个源到目的中出口接口最小的MTU,因为MTU属于单方向的,而返回的MTU可能与去的MTU不一致。

在一般的路由器上都有这样的功能。路径MTU发现机制,这对我们实施VPN和修改接口MTU是很有帮助的。
环境还是之前 R1——–R2———R3 R2出接口MTU为200, R1ping的时候,把DF位置位,不允许这个数据包分片,那么当接口小的MTU丢弃这个数据包的时候,会返回一个分片不可达的差错报文 。

MTU of next hop:200 ,这对于一个VPN的网络是很有帮助的,通常我遇到的朋友在实施VPN的时候,说流量大的时候性能非常不好,速度非常慢,而且会有时候丢包,这大部分原因 是因为分片造成的,VPN的加解密本来就很消耗路由器的资源,加上对分片的处理,VPN情况下,分片也会加密,而且对于PAT的穿越性 很不好, 所以造成性能下降。
这个是通过debug显示不出来的,但是cisco 路由器上可以通过扩展Ping 来实现,而且更直观
R1——-R2———R3 这里我把R2的接口MTU改为500,方便测试,在R1是使用扩展Ping
这里的 注意是DF位置位, 并且使用路径记录, 设置包大小的范围,我这里从100开始 到700,间隔100,也是每次发100大小的包测试。

没超过MTU之前 都有路径记录,并且数据包通过了,当在600的时候,由于超出了MTU的范围,需要分片,而我又把DF置位了,它就会提示Maximum MTU 500 ,就是表示到达目的的MTU最大为500.
所以,特别在做VPN实施的时候, 不要认为隧道建立了,流量能通过了 就代表完善了,一定要注意MTU的范围,这个可以通过IP头部+其他头部信息 计算可以得出来。

特别说明

1 IP MTU
对 于网络层的上层协议而言(我们以TCP/IP协议族为例),网络层IP协议会检查每个从上层协议下来的数据包的大小,并根据本机MTU的大小决定是否作 “分片”处理。分片最大的坏处就是降低了传输性能,本来一次可以搞定的事情,分成多次搞定,所以在网络层更高一层(就是传输层)的实现中往往会对此加以注 意!有些高层因为某些原因就会要求我这个面包不能切片,我要完整
面包,所以会在IP数据包包头里面加上一个标签:DF(Donot Fragment)。这样当这个IP数据包在一大段网络(水管里面)传输的时候,如果遇到MTU小于IP数据包的情况,转发设备就会根据要求丢弃这个数据 包,然后返回一个错误信息给发送者。这样往往会造成某些通讯上的问题,不过幸运的是大部分网络链路MTU都是等于1500或者大于1500。
对于 UDP协议而言,这个协议本身是无连接的协议,对数据包的到达顺序以及是否正确到达不甚关心,所以一般UDP应用对分片没有特殊要求。对于 TCP协议 而言 就不一样了,这个协议是面向连接的协议,对于TCP协议而言它非常在意数据包的到达顺序以及是否传输中有错误发生。所以有些TCP应用对分片有要求— 不能分片(DF)。
2、 MSS
MSS是最大传输大小的缩写,它是TCP协议里面的一个概念。如下图1-1所示:

图1-1 TCP头部
注:URG等参数指的是 ACK URG PSH SIN FIN RST等参数
在 TCP报文中 MSS的位置就在选项的位置,根据RFC1323和RFC793规定,选项中内容有很多种,MSS是其中的一种,用kind=2表示;kind=1表示无 操作,kind=4、5、6、7称为选择ACK及回显选项,但是由于回显选项已经被时间戳选项取代,同时,目前定义的选择ACK选项仍未定论,也没有包括 在RFC1323中,所以具体代表什么含义还无定论。在实际网络数据传输,要求MSS+20TCP包头 +20 IP包头不大于MTU。MSS在TCP报文中是可选项,不是必选项,换句话说,MSS是可协商项,而且在协商过后,该选项内容可以改变,也可以没有;在协
MSS时,一般是建立TCP连结的两端发送Syn标志报文时互相通报,然后选取最小MSS作为双方的约定,如果双方都不通报或有一方不通报。
MSS 就是TCP数据包每次能够传输的最大数据分段。为了达到最佳的传输效能,TCP协议在建立连接的时候通常要协商双方的MSS值,这个值TCP协议在实现的 时候往往用MTU值代替(需要减去IP数据包包头的大小20Bytes和TCP数据段的包头20Bytes),所以往往MSS为1460。通讯双方会根据 双方提供的MSS值
最小值确定为这次连接的最大MSS值。

3. 区别及联系
由前面的叙述可知:MTU是一个二层的概念,以太网 最大的mtu就是1500(它是不包含二层头部的,加上头部应该为1518 bytes),当然这里说的是很常规的情况,也有些server,比如server 2008,出来的就是jumbo frame了,我们在这里讨论常规情况。IP MTU是一个三层概念,它包含了三层头部及所有载荷,根据下层为上层服务的,上层基于下层才能做进一步的扩展的原则,尽管IP MTU的变化范围很大(68-65535),但也不得不照顾以太网MTU的限制,说白了就是ip对以太网的妥协。MSS是TCP里面的一个概念,它是 TCP数据包每次能够传输的最大数据分段,不包含包头部分,它与IP MTU满足如下关系:IP MTU=MSS+20bytes(IP包头)+20bytes(TCP包头)。当然,如果传输的时候还承载有其他协议,还要加些包头在前面,简言 之,mtu就是总的最后发出去的报文大小,MSS就是需要发出去的数据大小,比如 PPPoE ,就是在以太网上承载PPP协议(点到点连接协议),它包括 6bytes的PPPoE头部和2bytes的PPP协议ID号,此时,由于以太网的MTU值为1500,所以上层PPP负载数据不能超过1492字节, 也就是相当于在PPPOE环境下的MTU是1492字节,MSS是1452字节。

4. MTU问题解决方法
通常情况下,MTU不匹配会表现为两种故障情况:
ping大包时不通无法访问某些站点
在这种情况下,通常有两种解决方法:
修改用户端MTU值(不推荐使用)修改传输路由所有设备MTU值,确保路径MTU值大于用户发送的IP报文的长度,以保证用户报文不会因为超过设备的MTU值被丢弃。主要要考虑下面几种情况:
· 对于纯IP网络,要保证:路径MTU值>最大用户报文长度
· 对于纯MPLS网络(没有VPN业务),要保证路径MTU值>最大用户报文+一层标签长度(4)
· 对于三层VPN业务,要保证:路径MTU值>最大用户报文+两层标签长度(8);
· 对于二层VPN业务,要保证:路由MTU值>最大用户报文长度+两处标签长度(8)+二层帧头长度(18)。
值得注意的是:fastethernet接口不能调整MTU,所以说在有些设备中,使用MTU命令不能解决问题的。此外,更改MTU后,如果IGP是OSPF的话,不同的MTU可能会造成OSPF 停留在INIT状态,此时需要将两端的MTU调整一致。

如果大家有任何疑问或者文中有错误跟疏忽的地方,欢迎大家留言指出,博主看到后会第一时间修改,谢谢大家的支持,更多技术文章尽在网络之路Blog,版权归网络之路Blog所有, 原创不易,侵权必究,觉得有帮助的,关注转发一波谢谢。

上一篇回顾

下一篇学习

相关文章