traceroute介绍:原理和实验

2020-03-30

traceroute是什么

在Linux操作系统中,系统管理员使用traceroute(现在已被替换为mtr)命令来打印从本机发送到目标主机的数据包(datagram)所途经的中间节点的IP地址和延迟.

举个例子,如果我们想知道,自己正在使用的这台计算机到cloudflare.com的一台主机之间都经过了哪些中间节点,可以运行:

sudo mtr --report -c 1 --no-dns cloudflare.com

其中--report选项表示打印报告,而不是一直刷新显示,-c 1选项表示只对链路上的每个节点发送一份数据包,而--no-dns选项将禁止mtr程序将ip地址反解析成主机名,接下来您将能够看到如下输出

mtr程序的输出

mtr程序的输出

这表示,一个数据包从当前计算机发送到cloudflare.com名下的一台主机104.17.176.85,至少要先后经过208.64.231.6206.72.211.63.其中Last字段是对应中间每台主机的往返延迟,也就是说,到104.17.176.85的往返需要1.3ms,而到208.64.231.6的往返延迟是1.1ms,你可能注意到到206.72.211.63`的往返延迟竟然比后面的还高,那是因为数据包前往和数据包返回走的未必会是同样的路径,并且前往时和返回时网路的拥堵情况也不一样,所以说可能是数据包返回时消耗了更长的时间.

traceroute能做什么

诊断连通性

简而言之,traceroute(或者mtr)能帮组我们诊断网络链路的连通性和拥堵情况,下面我们分别以一台京东云主机和一台阿里云主机到162.244.241.102这台服务器的traceroute为例来讲解

从京东云到162.244.241.102的traceroute

从京东云到162.244.241.102的traceroute

从上图看来,从京东云发出的数据包并没有送达目标主机162.244.241.102,因为162.244.241.102并没有出现在traceroute报告的最后一行.

从阿里云到162.244.241.102的traceroute

从阿里云到162.244.241.102的traceroute

从上图看来,从阿里云发出的数据包到达了目标主机162.244.241.102,因为162.244.241.102出现在了traceroute报告的最后一.我们还可以参考前后两个节点的延迟,猜测拥堵的位置,例如,到59.43.186.246的平均延迟为12.7ms,而到59.43.246.238的平均延迟为131.3ms,那么可能是这两个节点的链路发生的拥塞,但是不一定.

比较链路质量

下面我们在同样一台主机,先后mtrexploro.onebeyondstars.xyz这两个地址,得到下面两份traceroute报告.

mtr到exploro.one

mtr到exploro.one

mtr到beyondstars.xyz

mtr到beyondstars.xyz

对比以上两幅图,我们可以发现,从做测试的那一台主机到exploro.one的链路差一些:因为中间经过了更多的节点,而且到每一个节点的延迟都相对较高,而到beyondstars.xyz的链路好一些:因为中间节点更少,延迟也都比较.根据这个信息,可以考虑将exploro.one的CDN服务商更换为beyondstars.xyz的CDN服务商.

traceroute的原理

traceroute是依赖于互联网协议(Internet Protocol, IP)的,所谓互联网协议大概就是:互联网中的两台主机要互相通信要知道对方的IP地址,并且规定了具体的通信方.值得注意的一点是,根据IP协议,通信的发起方需要在要发送的消息的信封(IP分组头部)加入一个TTL(Time To Live,生存时间)字段,然后消息(IP分组)会由发送方到接收方的中间节点层层传递,每到达一个中间节点,中间节点将TTL的值减去1,如果TTL减去1后等于0,那就把该IP分组的接收方字段和发送方字段互换——退回来,如果TTL减去1后不等于0,那就将该IP分组传递给下一个节点,有点类似接力赛的样子.

traceroute工作的过程示意图

traceroute工作的过程示意图

假设traceroute是在$t_1$时刻开始工作,假设目的主机是receiver(如上图所示),那么traceroute会在$t_1$时刻向receiver发送一个TTL=1的分组(Packet),交由下一个接力点(hop1)转发,而接力点hop1收到TTL=1的分组之后,先将TTL减去1,于是TTL=0,于是hop1将该分组退回,sender在时刻$t_2$收到hop1退回的分组.

sender在$t_2$时刻收到hop1退回的分组之后,立刻,创建一个TTL=2的分组发向receiver,这次hop1给分组的TTL减去1之后还剩1,于是hop2收到分组,hop2收到分组再给TTL减去1发现TTL变成0,于是hop2把分组退回给sender.sender在$t_3$时刻收到退回的分组,并且立刻发送TTL=3`的分组.

就这样,从TTL=1开始,sender依次增加TTL的值并发送新的分组,直到sender检视收到的退回的分组看到是来自receiver为止,这样sender可以分别计算$\Delta t_i = t_{i+1} - t_i$来获知到第$i$个中间节点的往返延迟,并通过检视退回分组的头部,来查看中间节点的IP地址.

WireShark显示

WireShark显示

上图是在一次运行mtrWireShark捕获的ICMP分组:可以看到有很多"Time-to-live exceeded"的消息.

traceroute的简单实现

为了验证我们对traceroute的了解是正确的,下面我们尝试用Shell语言编写一个能打印到目的主机之间所有中间节点的IP地址的脚本程序,该程序运行在Linux操作系统上,依赖于最新版本的bashiputils.

#!/bin/bash

hostname=$1
maxhop=64

xpingfunc()
{
	IP_ADDR_REGEX="[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}"
	ping -c 1 -t $2 -W 4 -n $1 | 
	sed -n '2p' | 
	egrep --only-matching "$IP_ADDR_REGEX" | 
	head -n 1
}

targethostname=$(dig +short $hostname A | head -n 1)

for i in $(seq 1 $maxhop)
do
	replyfrom=$(xpingfunc $targethostname $i)
	echo $replyfrom
	if [ ! -z $replyfrom ] && [ $replyfrom = $targethostname ]
	then
		exit 0
	fi
done

其中xpingfunc函数的作用是ping一个主机,然后返回收到的响应的源IP地址,而下边的循环则是依次增加TTL的值,使得数据包可以到达越来越远的中间节点,并打印每一个中间节点的IP地址,同时判断最远的数据包是否已经到达目的主机,若是则结束程序,若否则继续增加TTL,直到TTL增加超过「最大跳」(maxhop)的值为止.

将以上这段脚本保存为后缀名为.sh的文件,例如xping.sh,然后以一个域名作为参数运行:

自制traceroute测试exploro.one

自制traceroute测试exploro.one

自制traceroute测试beyondstars.xyz

自制traceroute测试beyondstars.xyz

自制traceroute测试cloudflare.com

自制traceroute测试cloudflare.com

测试样例均输出了正确的结果.

总结

traceroute利用IP协议中规定的IP分组头部的TTL字段和ICMP协议规定的TTL耗尽时的自动返回机制展示从当前主机到目的主机之间的所有中间节点,是诊断和分析网络状况的好工具,是系统管理员和网络管理员的好朋友、好帮手.

traceroute的实现其实很简单,仅仅是不断地增加IP头部的TTL字段,直到当前TTL字段的值超过最大跳或者收到目标主机的回复为止,其实很好理解.

参考文献

[1] RFC 792 - Internet Control Message Protocol

[2] traceroute - Wikipedia

[3] RFC 791 - Internet Protocol

技术交流traceroutenetworklinux

小数在计算机中的表示和存储

引论:CDN的各种配置方式及配置原理