网络协议

详解网络分层和网络协议工作原理
帅旋
关注
充电
IT宅站长,技术博主,架构师,全网id:arthinking。

ICMP协议 | ping,traceroute

发布于 2020-07-26 | 更新于 2024-05-16

IP协议本身不支持发现发往目的地地址失败的IP数据包,也没有提供直接的方式获取诊断信息,比如在发送途中,经过了哪些路由器,以及往返时间。

为此,就有了ICMP协议Internet Control Message ProtocolICMP)专门来负责这些事情。

ICMP并不为IP网络提供可靠性,它只是用于反馈各种故障和配置信息。丢包不会触发ICMP。

ICMP是RFC 792中定义的Internet协议套件的一部分。ICMP消息通常用于诊断网络或探测网络目的,或者是为了响应IP操作中的错误而生成(如RFC 1122中所指定),ICMP错误响应给原始数据包的源IP地址。

但是黑客经常用ICMP来做坏事,于是网络管理员可能会用防火墙阻止掉ICMP报文,这样的话,很多ping、traceroute之类的诊断程序就无法正常工作了。

1、格式

ICMP报文是在IP数据报内部传输的,格式如下:

image-20211023115713368

而ICMP报文的格式如下:

image-20211023115738283

其中:

  • 类型有15个不同的值,描述特定类型的ICMP报文;
  • 某些ICMP报文还是用代码字段的值来进一步描述不同的条件;
  • 校验和字段用于ICMP报文的差错检查。

以下是常见的差错报文类型:

类型 代码 描述 查询 差错
0 0 回显应答(ping应答)
3 目标不可达
0 网络不可达
1 主机不可达
2 协议不可达
3 端口不可达
4 需要进行分片但设置了不分片比特
5 源站选路失败
6 目的网络不认识
7 目的主机不认识
8 源主机被隔离(作废不用)
9 目的网络被强制禁止
10 目的主机被强制禁止
11 由于服务类型 T O S , 网 络 不 可 达
12 由于服务类型 T O S , 主 机 不 可 达
13 由于过滤,通信被强制禁止
14 主机越权
15 优先权中止生效
4 0 源端抑制
5 重定向
0 对网络重定向
1 对网络重定向
2 对服务类型和网络重定向
3 对服务类型和主机重定向
8 0 回显请求(ping)
9 0 路由器通告
10 0 路由器请求
11 超时
0 传输期间生存时间为0(Traceroute)
1 在数据报组装期间生存时间为0
12 参数问题
0 坏的 I P 首部(包括各种差错)
1 缺少必需的选项
13 0 时间戳请求
14 0 时间戳应答
15 0 信息请求
16 0 信息应答
17 0 地址掩码请求
18 0 地址掩码应答

其中,最常用的类型是8:回显请求(ping),以及0:回显应答(ping应答)。

2、查询报文

查询报文是有关信息采集和配置的ICMP报文。

我们经常用到的ping程序就用到了ICMP查询报文。

ping程序

ping程序会发送一份ICMP回显请求给主机,并等待返回ICMP回显应答。

ping程序ping不通了,就不能访问对应的主机了吗?

我们知道,网络管理员可能会用防火墙阻止掉ICMP报文的,这样我们可能就ping不通了,但是主机的可达性不能只取决于IP层是否可达,还与端口号和协议有关,而ping是运行在网络层的,用于测试网络连接状态和信息包发送接收状况,即使ping不通,我们也可能用telnet远程登录到主机的其他端口,如25号端口。

ping程序用到了回显请求和回显应答报文,报文格式如下:

image-20211023115812619

Unix系统实现ping程序时,把ICMP报文的标识符设置为进程ID,在进程内,序号从0开始,每发送一次新的 回显请求就加1,这样就可以同时运行多个ping进程了。

ping程序的端口号是什么?

端口号是传输层的东西,ping程序是使用ICMP协议,直接跳过了传输层,所以呢,ping程序是没有所谓的端口号的。

我们发送一个ping请求,数据在协议栈中的处理流程如下:

image-20211023120029998

  1. A主机的ping应用程序向服务器发起回显请求,说了一句:hi
  2. 直接传输到网络层的ICMP协议,进行ICMP数据封装:
    1. image-20211023120224620
    2. 8表示回显请求,112是发起请求的进程号,1表示请求序号
  3. IP协议拿到数据后进一步加上IP头,加上自己的IP和目标IP,传输给数据链路层;
  4. 数据链路层拿到IP数据包,准备封装成帧,这个时候会去寻找目标IP的MAC地址,如果在A主机的ARP映射表找到了IP的MAC地址,那就直接拿来用了,否则会发起一个ARP广播请求,获取到MAC地址。至于跨网关这种ping,会多了转发的功能更,后面会进行介绍。最终数据链路层封装成数据帧,从网络接口发出去;
  5. 服务器拿到数据帧之后,拿到MAC头,判断MAC地址是自己的,就基于拿到Frame data,按首部协议传给对应的模块,即IP模块;
  6. IP模块拿到数据,判断到IP跟自己对上了,于是继续拿到IP data,传输给ICMP协议,ICMP协议收到消息,准备应答:
    1. image-20211023120243196
    2. 0表示回显应答
  7. 然后按照同样的流程,把数据包发送回A主机。

可以发现,ping程序是直接用到了网络层的ICMP协议,不经过传输层

是什么原因导致ping失败了?

ping失败的原因有很多:

  • 可能是输错了IP;
  • 可能是网络配置不正确,如错误的子网掩码;
  • 可能有防火墙软件组织了ping;
  • 可能是硬件故障,如损坏了的以太网适配器,电缆,路由器,集线器等。

如果叫你自己实现一个ping程序,你会怎么做呢?

提示:为了能处理ICMP网络报文,我们需要用到原始套接字(SOCK_RAW),而不是SOCK_STREAM或者SOCK_DGRAM套接字。

更多提示:Homework 6: A raw socket ping tool[1],思路都在这里了,大家动手做一做,然后就可以有直接操作网络层的工作经验了。😏

3、差错报文

差错报文是有关IP数据报传递的ICMP报文。要是发送IP数据报中途产生了异常,那么就会响应ICMP差错报文。

但是不是所有情况都会响应ICMP差错报文,如以下场景:

  • ICMP差错报文不会产生另一个ICMP差错报文;
  • 目的地址是广播地址或者多播地址的IP数据报不会产生差错报文;
  • 作为链路层广播的数据报不会产生差错报文;
  • 源地址不是单个主机(源地址为零地址、环回地址、广播地址或者多波地址)的数据报不会产生差错报文;

为什么要这些规则呢?假如允许ICMP差错报文对广播分组响应,那么就会导致广播风暴了。

下面我们举一个ICMP差错报文的例子来说明下。

目标不可达

上面的表格我们了解到,如果类型为三则表示目标不可达,而根据具体的代码可以进一步划分:

类型 代码 描述 查询 差错
3 目标不可达
0 网络不可达
1 主机不可达
2 协议不可达
3 端口不可达
4 需要进行分片但设置了不分片比特

下面我们看一个端口不可达的例子来演示下ICMP差错报文附加的信息。

ICMP端口不可达案例

这里我们演示通过tftp访问一个不存在的端口号,查看其返回的ICMP响应差错报文。tftp应用在传输层是通过UDP来进行传输数据的

下面我们tftp请求之前先开启tcpdump抓包:

1
sudo tcpdump -i en0 -nn host 我的IP and 目标IP

然后执行tftp命令:

1
2
3
4
tftp
tftp> connect 目标IP 8090
tftp> get test.foo
Transfer timed out.

可以发现,在等待了大约25秒之后,最终输出:Transfer timed out.

观察tcpdump抓包日志:

1
2
3
4
5
6
7
8
9
10
16:51:16.739632 IP 我的IP.64517 > 目标IP.8090: UDP, length 20
16:51:16.768590 IP 目标IP > 我的IP: ICMP host 193.112.43.165 udp port 8090 unreachable, length 56
16:51:21.743056 IP 我的IP.64517 > 目标IP.8090: UDP, length 20
16:51:21.751958 IP 目标IP > 我的IP: ICMP host 193.112.43.165 udp port 8090 unreachable, length 56
16:51:26.748387 IP 我的IP.64517 > 目标IP.8090: UDP, length 20
16:51:26.757631 IP 目标IP > 我的IP: ICMP host 193.112.43.165 udp port 8090 unreachable, length 56
16:51:31.752851 IP 我的IP.64517 > 目标IP.8090: UDP, length 20
16:51:31.794217 IP 目标IP > 我的IP: ICMP host 193.112.43.165 udp port 8090 unreachable, length 56
16:51:36.755172 IP 我的IP.64517 > 目标IP.8090: UDP, length 20
16:51:36.774400 IP 目标IP > 我的IP: ICMP host 193.112.43.165 udp port 8090 unreachable, length 56

可以发现这里执行了五次UDP请求,每次请求都响应了一个ICMP包,为udp port 8090 unreachable端口不可达,产生了ICMP不可达报文,该报文一般格式如下:

image-20211023120412814

为什么需要返回IP首部:因为IP首部包含了协议字段,使得ICMP知道如何解释后面的8个字节;

为什么需要原始IP数据报中数据的前8个字节:因为这里面包含了源端口和目的端口。

不过看起来我的电脑好像忽略了ICMP报文,还是继续重试了4次。

注意:ICMP报文是在主机之间交换的,网络层的协议,不需要端口号,而以上20个字节的UDP数据报是包含了源端口号和目标端口号信息的。

为什么TFTP客户程序会继续重发呢?

因为网络编程中,BSD系统不把从socket接收到的ICMP报文中的UDP数据通知用户进程,除非该进程以及发送了一个connect命令给该接口。标准的BSDTFTP客户程序并不发送connect命令,所以它永远也不会受到ICMP差错报文的通知。

traceroute程序

traceroute工具用于确定从发送者到目的地路径上的路由器。

traceroute主要是通过故意设置特殊的TTL,来达到追踪目的地路径上的路由器的功能。

TTL运行原理

TTL:是 Time To Live的缩写,该字段指定IP包被路由器丢弃之前允许通过的最大网段数量。每经过一个路由器,TTL就会减一,然后再把IP包转发出去,如果TTL减到0了,路由器就会丢弃收到的TTL=0的IP包,并向IP包的发送者发送一个ICMP差错报文,类型为11,代码为0:传输期间生存时间为0。

第一轮,traceroute设置TTL值为1,那么遇到第一个路由就返回ICMP容错报文了,下一轮,TTL设置为2…这样依次增加。最终就把整个链路的路由器都试出来了。

当然,有点路由器不会回整个ICMP,这也是为什么你去traceroute一个公网地址,看不到中间路由的原因。

除此之外,traceroute也可以通过不设置分片,来确定传输链路的MTU(Maximum Transmission Unit, 最大传输单元):首先发送一个分组的长度正好与出口MTU相等,如果中间遇到窄点的关口,就被卡主了,这个时候会接收到一个ICMP差错报文,然后调小分组长度重试…

References


  1. Homework 6: A raw socket ping tool ↩︎

本文作者: 帅旋

本文链接: https://www.itzhai.com/columns/network/network-layer/icmp.html

版权声明: 版权归作者所有,未经许可不得转载,侵权必究!联系作者请加公众号。

×
IT宅

关注公众号及时获取网站内容更新。

请帅旋喝一杯咖啡

咖啡=电量,给帅旋充杯咖啡,他会满电写代码!

IT宅

关注公众号及时获取网站内容更新。