今天给大家聊聊 Ping 与 Traceroute 的实现原理,ICMP 协议。

ICMP 协议

ICMP 全称 Internet Control Message Protocol,就是互联网控制报文协议。ICMP 报文是封装在 IP 包里面的,因为传输指令的时候,肯定需要源地址和目标地址。

ICMP 报文有很多的类型,主要分为两类,查询报文类型和差错报文类型。

查询报文类型简单来说就是查询方主动发送侦查协议,等待对方的应答。具体例子,见下面 ping 的使用

差错报文类型会使用 ICMP 的规则,故意制造一些能够产生错误的场景,从而达到网络诊断的目的。具体例子,见下面 traceroute 的使用。

Ping 的使用

ping 的发送和接收过程,如下图所示:

假定主机 A 的 IP 地址是 192.168.1.1,主机 B 的 IP 地址是 192.168.1.2,它们都在同一个子网。那当你在主机 A 上运行 “ping 192.168.1.2” 后,会发生什么呢?

ping 命令执行的时候,源主机首先会构建一个 ICMP 请求数据包,ICMP 数据包内包含多个字段。最重要的是两个,第一个是类型字段,对于请求数据包而言该字段为 8;另外一个是顺序号,主要 用于区分连续 ping 的时候发出的多个数据包。每发出一个请求数据包,顺序号会自动加 1。为了能够计算往返时间 RTT,它会在报文的数据部分插入发送时间。

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

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

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

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

在规定的时候间内,源主机如果没有接到 ICMP 的应答包,则说明目标主机不可达;如果接收到了 ICMP 应答包,则说明目标主机可达。此时,源主机会检查,用当前时刻减去该数据包最初从源主机上发出 的时刻,就是 ICMP 数据包的时间延迟。

当然这只是最简单的,同一个局域网里面的情况。如果跨网段的话,还会涉及网关的转发、路由器的转发等等。但是对于 ICMP 的头来讲,是没什么影响的。会影响的是根据目标 IP 地址,选择路由的下一 跳,还有每经过一个路由器到达一个新的局域网,需要换 MAC 头里面的 MAC 地址。这个过程后面几节会详细描述,这里暂时不多说。

如果在自己的可控范围之内,当遇到网络不通的问题的时候,除了直接 ping 目标的 IP 地址之外,还应该有一个清晰的网络拓扑图。并且从理论上来讲,应该要清楚地知道一个网络包从源地址到目标地址 都需要经过哪些设备,然后逐个 ping 中间的这些设备或者机器。如果可能的话,在这些关键点,通过 tcpdump -i eth0 icmp,查看包有没有到达某个点,回复的包到达了哪个点,可以更加容易推断出错的 位置。

经常会遇到一个问题,如果不在我们的控制范围内,很多中间设备都是禁止 ping 的,但是 ping 不通不代表网络不通。这个时候就要使用 telnet,通过其他协议来测试网络是否通,这个就不在本篇的讲述范围了。

说了这么多,我们可以看出 ping 这个程序是使用了 ICMP 里面的 ECHO REQUEST 和 ECHO REPLY 类型的。

Traceroute 使用

前面我们提到,差错报文类型会使用 ICMP 规则故意制造一些能够产生错误的场景。

常见的错误场景有以下几个:

  1. 终点不可达:网络不可达、主机不可达、协议不可达、端口不可达、需要进行分片但设置了不分片位
  2. 源站抑制
  3. 时间超时
  4. 路由重定向

Traceroute 故意制造上面常见错误中的一些场景,以达到诊断网络的目的。

Traceroute 故意设置特殊的 TTL,来追踪去往目的地时沿途经过的路由器。Traceroute 的参数指向某个目的 IP 地址,它会发送一个 UDP 的数据包。将 TTL 设置成 1,也就是说一旦遇到一个路由器或者一个关卡,就表示它“牺牲”了,于是,返回一个 ICMP 包,也就是网络差错包,类型是时间超时。接下来将将 TTL 设置为 2,第一关过了第二关又“牺牲了”,依次类推,直到到达目的主机。这样,Traceroute 就 拿到了所有的路由器 IP。当然,有的路由器压根不会回这个 ICMP。这也是 Traceroute 一个公网的地址,看不到中间路由的原因。

那又是怎么知道 UDP 有没有到达目的主机呢?Traceroute 程序会发送一份 UDP 数据报给目的主机,但它会选择一个不可能的值作为 UDP 端口号(大于 30000 )。当该数据报到达时,将使目的主机的 UDP 模块产生一份“端口不可达”错误 ICMP 报文。如果数据报没有到达,则可能是超时。

小结

Ping 与 Traceroute 是大家平时最常用的命令,这篇文章较为详细的解释了它们的工作原理,并引出了 ICMP 协议。有些公司在面试的时候可能会问的比较深,希望这篇文章能帮到大家。