ABSTRACT:
网络连接问题在后台开发中最常见的问题之一。本文总结了定位网络连接问题的思路及常用工具 ping和traceroute(traceroute是Linux命令,在windows中是tracert),并从ICMP网络协议的角度来解释 ping和traceroute的实现原理。假如学有余力的读者,还可以看看扩展部分,一起来钻一下牛角尖:“机器通不通”这个描述到底准不准确?
阅读本文大概需要15min。
0. 问题描述
在一个比较大的网络中,如果两台机器不通,你知道应该用什么方式调试吗?
比如,两台机器均以开机,并且都连接上了网络,但是一台机器调用另一台机器的服务时,发现出现了较大的时延, 我们第一反应是,机器A与机器B不通?那么,我们应该如何排查问题呢?
1. 背景知识:ICMP协议
ICMP全称Internet Control Message Protocol,就是互联网控制协议。
ICMP是封装在IP包里面的。
ICMP报文有很多类型,不同的类型有不同的Message type。最常用的类型如下:
2. 排查思路
为方便演示,我先采用处于不同局域网的两台机器为例进行讲解。
假设local host的IP地址为219.223.192.155,remote host的IP地址为219.223.220.20。
目前,两台机器均已经开机并且连上网。
2.1 ping
「ping」是用来探测两台机器是否可达的命令,是确认网络连接情况最常用的命令。
现在,local host来ping remote host:
可以看到,local host能够ping通remote host,说明两台机器是可达的。
那么,ping不通的话,local host会收不到任何response:
图中虽然显示了google的“IP地址”,但是这个IP地址可能是国内ISP DNS解析出来的一个虚拟IP地址。(GFW拦截原理概析)
ping不通可能是由以下原因导致的:
- 网络本身存在物理因素故障。比如,本地DNS故障,路由器故障。
- 该ip地址/域名可能不正确或无法使用。比如,GFW会为一些网站解析成一个虚拟的IP地址
- 路由表有问题
- ping请求被防火墙阻止。比如,通过iptables drop掉所有的ICMP request
所以,ping不通remote host,但是remote host仍然有可能正常工作。
ping实现原理
ping 是基于 ICMP 协议工作的,利用了ICMP中的查询报文,即,主动请求Echo request(Message Type = 8),主动请求的应答Echo reply(Message Type = 0)
那么,ping的过程如下:
- ping 命令执行的时候,源主机首先会构建 ICMP 请求数据包,ICMP 数据包内包含多个字段。最重要的是两个,第一个是类型字段(Message type),对于请求数据包而言该字段为 8;另外一个是顺序号(Seq.num),主要用于区分连续 ping 的时候发出的多个数据包。每发出一个请求数据包,顺序号会自动加 1。为了能够计算往返时间 RTT,它会在报文的数据部分插入发送时间。
- ICMP协议把这个数据包交给IP层构建一个IP数据包,MAC再把这个IP数据包加入MAC头构建一个数据帧,传送给目的主机。
- 当目的主机收到了这个数据帧,会构建一个ICMP应答包,应答数据包的类型字段为0,顺序号为接收到的请求数据包中的顺序号,然后再发送出去给主机 A。
- 在规定的时候间内,源主机如果没有接到 ICMP 的应答包,则说明目标主机不可达;如果接收到了 ICMP 应答包,则说明目标主机可达。此时,源主机会检查,用当前时刻减去该数据包最初从源主机上发出的时刻,就是 ICMP 数据包的时间延迟。
了解了ping的实现原理,那么想要阻止ping请求,只需要让源主机没有收到ICMP应答包即可。 可以通过添加很简单的 [iptables规则](https://wangchujiang.com/linux-command/c/iptables.html):
iptables -I INPUT -p ICMP --icmp-type 8 -j DROP
收到主动请求的ICMP包就Drop掉iptables -I OUTPUT -p ICMP --icmp-type 0 -j DROP
将所有要发送出去的主动请求的应答包Drop掉
以上两条规则有一即可。
2.2 traceroute
「traceroute」能够提供两台主机的网络连接的详细信息,能够比ping提供更多的信息。traceroute能够显示有关数据包从local host到remote host的每一“跳”的信息。因此,traceroute命令常常用于定位网络连接问题和查明网络瓶颈。
现在local host开始ping remote host:
记录按序列号从1开始,每个纪录就是一跳 ,每跳表示一个网关,我们看到每行有三个时间,单位是ms,其实就是-q的默认参数。探测数据包向每个网关发送三个数据包后,网关响应后返回的时间;如果用traceroute -q 4 www.58.com,表示向每个网关发送4个数据包,此时每行就会显示4个时间。
其中 !X指明网络不可达的原因“communication administratively prohibited”,意指该机器由于内部设定的转发规则,无法转发该数据包。
「traceroute」命令默认使用UDP协议的,当然也可以使用ICMP协议(-I)和TCP协议(-T)
星号“*”表示收不到任何的返回数据,也就没有办法计算响应返回时间。此外,每次数据包由某一同样的出发点(source)到达某一同样的目的地(destination)走的路径可能会不一样,但基本上来说大部分时候所走的路由是相同的。
所以,traceroute通过追踪数据包所经过的网关,定位问题是主机问题还是网关问题。
traceroute的实现
traceroute命令默认使用UDP协议的,并且利用了ICMP的差错报文(Message Type=3,目的地不可达;Message Type=11,TTL超时)。在基于UDP的实现中,客户端发送的数据包是通过UDP协议来传输的,使用了一个大于30000的端口号,服务器在收到这个数据包的时候会返回一个端口不可达的ICMP错误信息(Message Type=3),客户端通过判断收到的错误信息是TTL超时还是端口不可达来判断数据包是否到达目标主机,具体的流程如图:
- 客户端发送一个TTL为1,端口号大于30000的UDP数据包,到达第一站路由器之后TTL被减去1,返回了一个超时的ICMP数据包,客户端得到第一跳路由器的地址。
- 客户端发送一个TTL为2的数据包,在第二跳的路由器节点处超时,得到第二跳路由器的地址。
- 客户端发送一个TTL为3的数据包,数据包成功到达目标主机,返回一个端口不可达错误,traceroute结束。
那么,想要阻止remote host阻止基于UDP的traceroute,只要让源没有收到ICMP的端口不可达的错误类型报文即可:
iptables -I OUTPUT -p ICMP --icmp-type 3 -j DROP
traceroute命令当然也可以使用ICMP协议。此时,源主机会直接向目的主机发送ICMP echo request数据包,服务器收到这个请求之后会返回一个echo reply数据包。路由器/网关返回给源主机依然是TTL超时的ICMP差错报文(Message Type=11)。
那么想要阻止基于ICMP的traceroute请求,只需要让源主机没有收到ICMP应答包即可。 可以通过添加很简单的 iptables规则:
iptables -I INPUT -p ICMP --icmp-type 8 -j DROP
收到主动请求的ICMP包就Drop掉iptables -I OUTPUT -p ICMP --icmp-type 0 -j DROP
将所有要发送出去的主动请求的应答包Drop掉即可。
以上两条规则有一即可。traceroute使用ICMP协议时的原理跟ping相同,故通过iptables阻止基于ICMP协议的traceroute命令的过滤规则也是相同的。使用了这类型规则后,也会让目的主机ping不通。
3. 扩展
ping/traceroute不通,是不是就能够说明两台机器之间不可达了呢?其实不一定。
通过第2章的实践,可以发现,目的主机通过iptables设定专门禁止ping/trace的过滤规则,使其他机器ping/traceroute不通这台主机。假如iptables没有禁止ssh服务,那么,你依然可以通过ssh连接上这台机器。
这时候,直接得出目的主机“不通”的判断就太简单粗暴了。
一次网络连接,往往需要关心这样的一个5元组:<local_ip,local_port, remote_ip,remote_port, protocol>,五元组里任何一个元素变了,这个连接就无法建立,无法通信。这5个元素也是我们研究网络层/传输层的最关心的元素。
所以,只要找到目的主机对外开放的端口和协议,并且源主机IP地址在目的主机开放的白名单了,那么源主机是可以跟目的主机通信的。
然而,我们常常说的两台机器通不通并没有关注地那么细。 假如机器A通过ping/traceroute均不可到达机器B,那么可以断定从机器A到机器B的通讯路径中的某个节点设置了一定的访问限制,也就是我们常说的“不通”。
参考文献
【ICMP与ping,traceroute相关的协议知识】
【traceroute用法】
【traceroute的原理解析】