ICMP协议

ICMP协议概述

ICMP,即Internet Control Message Protocol网络控制报文协议,它属于网络层。

在详细介绍ICMP之前,我们先看几个问题:

问题1:http协议,当client请求的网页不存在时,server如何做?

同学们会说,这是出错的情况,当然要发送特定的Error Code,告诉client出现了什么错误, 以便让client认识到当前的情况,及时调整自己的行,或让用户来进行干预。如果没有这种错误消息通知机制 ,一旦出错,server什么也不做,只是把无法处理的请求一丢了事。简单是简单了,问题是client被闷在鼓里,一直在那里 转圈圈 等,直到用户很不满意地关闭软件,并伴随一声 靠,什么破软件!很显然,http有自己的 错误消息通知机制,由于http属于应用层,姑且称之为 应用层的错误消息通知机制。

问题2:TCP协议一旦接到无法处理的报文,如何处理?

熟悉TCP协议的同学略加思索会说,使用RST(Reset)消息,告诉发送方,当前遭遇无法处理的异常情况,且无法修复,只有Reset连接才是最好的选择。接收方接到Reset消息,只要这个Reset消息是合法的,那么接收方会无条件的Reset当前的连接。RST消息,属于TCP协议的错误消息通知机制,由于TCP协议属于OSI传输层,称之为 传输层的错误消息通知机制。

但TCP的RST消息,实在是很粗糙,没有任何 Error Code,让接收方只知道发生了错误,但是具体是哪里出错了并不知道,这样就给排错设置了一个很大的障碍,于是千千万万的网工、码农一旦遭遇TCP连接被Reset,就会陷入深深地思索:娘西皮,到底是咋回事呢?如果TCP提供 Error Code ,方便排错的同时,将使TCP协议变得臃肿不堪。 个人推测,当初设计这些基础协议时,还是希望流线型 简洁型,尽量减少协议头尺寸,留更多的空间传输用户的数据。

问题3:当主机在处理IP报文时,发现目的端口没有程序在监听,如何处理?

熟悉ICMP协议的同学会不假思索脱口而出,使用ICMP消息发送出错消息 Port Unreachable ,告诉源主机目前的现状 是的,由于当前没有程序在目的端口侦听(Listen),无法使用 传输层的错误消息通知机制 来通知源主机当前的错误状况,那只好退而求其次,让传输层的下级单位 网络层来处理。 网络层的代表人物“IP协议”,终于闪亮登场。

但是熟悉IP协议的同学会知道,,P协议20字节标准头部 以及40字节的扩展头部(option)并没有处理出错的字段。 如上文所言,如果IP协议本身增添了出错处理字段,协议变得不再流线,将大大挤占用户数据的空间。 但是,错误的情况没有“错误消息通知机制”来通知源主机,是不可接受的。 所以,协议设计者发明了ICMP协议,用于协助IP层来处理“错误消息通知机制 ”。由于该层位于网络层,称之为“网络层的错误消息通知机制”。

ICMP里面有个关键词——控制,如何控制的呢?

网络包在复杂的网络传输环境里,常常会遇到各种问题。当遇到问题的时候,总不能死的不明不白,没头没脑的作风不是计算机网络的风格。所以需要传出消息,报告遇到了什么问题,这样才可以调整传输策略,以此来控制整个局面。

ICMP 主要的功能包括:

  • 确认 IP 包是否成功送达目标地址
  • 报告发送过程中 IP 包被废弃的原因和改善网络设置等

IP 通信中如果某个 IP 包因为某种原因未能达到目标地址,那么这个具体的原因将由 ICMP 负责通知

如上图例子,主机 A 向主机 B 发送了数据包,由于某种原因,途中的路由器 2 未能发现主机 B 的存在,这时,路由器 2 就会向主机 A 发送一个 ICMP 目标不可达数据包,说明发往主机 B 的包未能成功。

ICMP 的这种通知消息会使用 IP 进行发送

因此,从路由器 2 返回的 ICMP 包会按照往常的路由控制先经过路由器 1 再转发给主机 A 。收到该 ICMP 包的主机 A 则分解 ICMP 的首部和数据域以后得知具体发生问题的原因。

ICMP报文

ICMP 报文是封装在 IP 包里面,它工作在网络层,是 IP 协议的助手。

ICMP 包头的类型字段,大致可以分为两大类:

  • 一类是用于诊断的查询消息,也就是「查询报文类型
  • 另一类是通知出错原因的错误消息,也就是「差错报文类型

查询报文类型

查询报文类型包括:回送应答和回送请求,代码分别为0和8。

回送消息用于进行通信的主机或路由器之间,判断所发送的数据包是否已经成功到达对端的一种消息,ping 命令就是利用这个消息实现的。

一台主机可以向对端主机发送回送请求的消息(ICMP Echo Request Message,类型 8),也可以接收对端主机发回来的回送应答消息(ICMP Echo Reply Message,类型 0)。 其报文格式如下:

相比原生的 ICMP,这里多了两个字段:

  • 标识符:用以区分是哪个应用程序发 ICMP 包,比如用进程 PID 作为标识符;
  • 序号:序列号从 0 开始,每发送一次新的回送请求就会加 1, 可以用来确认网络包是否有丢失。

选项数据中,ping 还会存放发送请求的时间值,来计算往返时间,说明路程的长短。

差错报文类型

这里介绍几个常用的差错报文类型:目标不可达、原点抑制、重定向和超时。

  1. 目标不可达

    IP 路由器无法将 IP 数据包发送给目标地址时,会给发送端主机返回一个目标不可达的 ICMP 消息,并在这个消息中显示不可达的具体原因,原因记录在 ICMP 包头的代码字段。

    由此,根据 ICMP 不可达的具体消息,发送端主机也就可以了解此次发送不可达的具体原因

    下图总结了6 种常见的目标不可达类型的代码

  • 网络不可达: IP 地址是分为网络号和主机号,当路由器表匹配不到接收方 IP 的网络号时, 就通过 ICMP 协议以网络不可达Network Unreachable)的原因告知主机。
    • 主机不可达:路由表中没有该主机信息,或者该主机没有接入到网络
    • 协议不可达:比如目标主机防火墙禁用了TCP协议
    • 端口不可达:可能目标主机没有对该端口进行监听
  1. 原点抑制

    在使用低速广域线路的情况下,连接 WAN 的路由器可能会遇到网络拥堵的问题。

    ICMP 原点抑制消息的目的就是为了缓和这种拥堵情况

    当路由器向低速线路发送数据时,其发送队列的缓存变为零而无法发送出去时,可以向 IP 包的源地址发送一个 ICMP 原点抑制消息

    收到这个消息的主机借此了解在整个线路的某一处发生了拥堵的情况,从而增大 IP 包的传输间隔,减少网络拥堵的情况。

    然而,由于这种 ICMP 可能会引起不公平的网络通信,一般不被使用。

  2. 重定向

    如果路由器发现发送端主机使用了「不是最优」的路径发送数据,那么它会返回一个 ICMP 重定向消息给这个主机。

    在这个消息中包含了最合适的路由信息和源数据。这主要发生在路由器持有更好的路由信息的情况下。路由器会通过这样的 ICMP 消息告知发送端,让它下次发给另外一个路由器。

  3. 超时消息

    IP 包中有一个字段叫做 TTLTime To Live,生存周期),它的值随着每经过一次路由器就会减 1,直到减到 0 时该 IP 包会被丢弃。

    此时,路由器将会发送一个 ICMP 超时消息给发送端主机,并通知该包已被丢弃。

    设置 IP 包生存周期的主要目的,是为了在路由控制遇到问题发生循环状况时,避免 IP 包无休止地在网络上被转发。

    此外,有时可以用 TTL 控制包的到达范围,例如设置一个较小的 TTL 值

Ping命令

接下来,我们重点来看 ping发送和接收过程

同个子网下的主机 A 和 主机 B,主机 A 执行ping 主机 B 后,我们来看看其间发送了什么?

  1. ping 命令执行的时候,源主机首先会构建一个 ICMP 回送请求消息数据包。
    ICMP 数据包内包含多个字段,最重要的是两个:
  • 第一个是类型,对于回送请求消息而言该字段为 8

  • 另外一个是序号,主要用于区分连续 ping 的时候发出的多个数据包。
    每发出一个请求数据包,序号会自动加 1。为了能够计算往返时间 RTT,它会在报文的数据部分插入发送时间。

  1. 然后,由 ICMP 协议将这个数据包连同地址 192.168.1.2 一起交给 IP 层。IP 层将以 192.168.1.2 作为目的地址,本机 IP 地址作为源地址协议字段设置为 1 表示是 ICMP 协议,再加上一些其他控制信息,构建一个 IP 数据包。

  2. 接下来,需要加入 MAC 头。如果在本地 ARP 映射表中查找出 IP 地址 192.168.1.2 所对应的 MAC 地址,则可以直接使用;如果没有,则需要发送 ARP 协议查询 MAC 地址,获得 MAC 地址后,由数据链路层构建一个数据帧,目的地址是 IP 层传过来的 MAC 地址,源地址则是本机的 MAC 地址;还要附加上一些控制信息,依据以太网的介质访问规则,将它们传送出去。

  3. 主机 B 收到这个数据帧后,先检查它的目的 MAC 地址,并和本机的 MAC 地址对比,如符合,则接收,否则就丢弃。
    接收后检查该数据帧,将 IP 数据包从帧中提取出来,交给本机的 IP 层。同样,IP 层检查后,将有用的信息提取后交给 ICMP 协议。

  4. 主机 B 会构建一个 ICMP 回送响应消息数据包,回送响应数据包的类型字段为 0序号为接收到的请求数据包中的序号,然后再发送出去给主机 A。

在规定的时候间内,源主机如果没有接到 ICMP 的应答包,则说明目标主机不可达;如果接收到了 ICMP 回送响应消息,则说明目标主机可达。

此时,源主机会检查,用当前时刻减去该数据包最初从源主机上发出的时刻,就是 ICMP 数据包的时间延迟。

针对上面发送的事情,总结成了如下图:

当然这只是最简单的,同一个局域网里面的情况。如果跨网段的话,还会涉及网关的转发、路由器的转发等等。

但是对于 ICMP 的头来讲,是没什么影响的。会影响的是根据目标 IP 地址,选择路由的下一跳,还有每经过一个路由器到达一个新的局域网,需要换 MAC 头里面的 MAC 地址

说了这么多,可以看出 ping 这个程序是使用了 ICMP 里面的 ECHO REQUEST(类型为 8 ) 和 ECHO REPLY (类型为 0)

Traceroute命令

有一款充分利用 ICMP 差错报文类型的应用叫做 traceroute(在UNIX、MacOS中是这个命令,而在Windows中对等的命令叫做 tracert )。

1. traceroute 作用一

traceroute 的第一个作用就是故意设置特殊的 TTL,来追踪去往目的地时沿途经过的路由器。

traceroute 的参数指向某个目的 IP 地址

1
2
traceroute 192.168.1.100
1

这个作用是如何工作的呢?

它的原理就是利用 IP 包的生存期限1 开始按照顺序递增的同时发送 UDP 包,强制接收 ICMP 超时消息的一种方法。

比如,将 TTL 设置 为 1,则遇到第一个路由器,就牺牲了,接着返回 ICMP 差错报文网络包,类型是时间超时

接下来将 TTL 设置为 2,第一个路由器过了,遇到第二个路由器也牺牲了,也同时返回了 ICMP 差错报文数据包,如此往复,直到到达目的主机。

这样的过程,traceroute 就可以拿到了所有的路由器 IP。

当然有的路由器根本就不会返回这个 ICMP,所以对于有的公网地址,是看不到中间经过的路由的。

发送方如何知道发出的 UDP 包是否到达了目的主机呢?

traceroute 在发送 UDP 包时,会填入一个不可能的端口号值作为 UDP 目标端口号(大于 3000 )。当目的主机,收到 UDP 包后,会返回 ICMP 差错报文消息,但这个差错报文消息的类型是「端口不可达」。

traceroute 还有一个作用是故意设置不分片,从而确定路径的 MTU

这么做是为了什么?

这样做的目的是为了路径MTU发现

因为有的时候我们并不知道路由器的 MTU 大小,以太网的数据链路上的 MTU 通常是 1500 字节,但是非以外网的 MTU 值就不一样了,所以我们要知道 MTU 的大小,从而控制发送的包大小。

它的工作原理如下:

首先在发送端主机发送 IP 数据报时,将 IP 包首部的分片禁止标志位设置为 1。根据这个标志位,途中的路由器不会对大数据包进行分片,而是将包丢弃。

随后,通过一个 ICMP 的不可达消息将数据链路上 MTU 的值一起给发送主机,不可达消息的类型为「需要进行分片但设置了不分片位」。

发送主机端每次收到 ICMP 差错报文时就减少包的大小,以此来定位一个合适的 MTU 值,以便能到达目标主机。

参考文献

  1. https://www.zhihu.com/question/31002474
  2. https://blog.csdn.net/qq_32523711/article/details/108724419
打赏
  • 版权声明: 本博客所有文章除特别声明外,著作权归作者所有。转载请注明出处!
  • Copyrights © 2021-2022 Yin Peng
  • 引擎: Hexo   |  主题:修改自 Ayer
  • 访问人数: | 浏览次数:

请我喝杯咖啡吧~

支付宝
微信