背景
在linux服务器上traceroute某一IP,当到了防火墙后就不回显了,显示*号。防火墙策略已经是放通ICMP协议,并且在服务器上可以ping通该地址。
分析
对服务器tracerote发出的数据包抓包分析,发现服务器发出的包为UDP包,端口号大于30000
traceroute原理
- IP协议
IP协议是TCP/IP协议族中最核心的部分,它的作用是在两台主机之间传输数据,所有上层协议的数据(HTTP、TCP、UDP等)都会被封装在一个个的IP数据包中被发送到网络上。
- ICMP
ICMP全称为互联网控制报文协议,它常用于传递错误信息,ICMP协议是IP层的一部分,它的报文也是通过IP数据包来传输的。 - TTL
TTL(time-to-live)是IP数据包中的一个字段,它指定了数据包最多能经过几次路由器。从我们源主机发出去的数据包在到达目的主机的路上要经过许多个路由器的转发,在发送数据包的时候源主机会设置一个TTL的值,每经过一个路由器TTL就会被减去一,当TTL为0的时候该数据包会被直接丢弃(不再继续转发),并发送一个超时ICMP报文给源主机。
具体到traceroute的实现细节上,有两种不同的方案:
基于UDP实现
在基于UDP的实现中,客户端发送的数据包是通过UDP协议来传输的,使用了一个大于30000的端口号,服务器在收到这个数据包的时候会返回一个端口不可达的ICMP错误信息,客户端通过判断收到的错误信息是TTL超时还是端口不可达来判断数据包是否到达目标主机,具体的流程如图:
- 客户端发送一个TTL为1,端口号大于30000的UDP数据包,到达第一站路由器之后TTL被减去1,返回了一个超时的ICMP数据包,客户端得到第一跳路由器的地址。
- 客户端发送一个TTL为2的数据包,在第二跳的路由器节点处超时,得到第二跳路由器的地址。
- 客户端发送一个TTL为3的数据包,数据包成功到达目标主机,返回一个端口不可达错误,traceroute结束。
目前在网上找到许多开源iOS traceroute实现大多都是基于UDP的方案,实际用起来并不能达到想要的效果,所以我们需要采用另一种方案来实现。
基于ICMP实现
上述方案失败的原因是由于服务器对于UDP数据包的处理,所以在这一种实现中我们不使用UDP协议,而是直接发送一个ICMP回显请求(echo request)数据包,服务器在收到回显请求的时候会向客户端发送一个ICMP回显应答(echo reply)数据包,在这之后的流程还是跟第一种方案一样。这样就避免了我们的traceroute数据包被服务器的防火墙策略墙掉。
采用这种方案的实现流程如下:
- 客户端发送一个TTL为1的ICMP请求回显数据包,在第一跳的时候超时并返回一个ICMP超时数据包,得到第一跳的地址。
- 客户端发送一个TTL为2的ICMP请求回显数据包,得到第二跳的地址。
- 客户端发送一个TTL为3的ICMP请求回显数据包,到达目标主机,目标主机返回一个ICMP回显应答,traceroute结束。
值得一提的是在Windows系统中也有traceroute程序,它的名字叫做tracert,tracert就是用采用这种方法来实现的。
验证
防火墙策略只开放ICMP协议,并没有开放UDP协议,所以traceroute数据包到达防火墙后被直接丢弃。
1、使用windows系统tracert测试,抓包分析,显示数据包协议为ICMP,并且测试路径回显正常。
2、在linux服务器上traceroute 带“-I”参数,即使用ICMP协议发送数据包,测试路径回显正常。
总结
linux系统traceroute默认使用UDP协议发送数据包,日常运维过程需要测试可能会忽略这一点,导致防火墙策略已经开放ICMP协议但是traceroute回显不正常。