traceroute的实现方式

最近读《TCP/IP详解卷一》,想了解一下这个世界上最大最复杂的系统是怎么设计的,其背后又有哪些思想是指的我们学习的,读到traceroute这一段时,感觉挺有意思,这里跟大家分享一下。

traceroute简介

traceroute是一个计算机网络的诊断工具,用来显示网络路由和每一段路径的耗时,是大家最常用的工具之一,效果如下面所示:

![599f73cfb84b44d79bb939f7e354310b-image.png](http://oss.lanjingdejia.com/file/2018/9/599f73cfb84b44d79bb939f7e354310b-image.png)
我们知道,互联网上数据的传输是通过各个路由器从数据源传递到目的地的,直观的感觉是这样的:

e589633bfada40e99e0d4bacbf0de413-image.png

traceroute则是把我们看不到的路径用文字的形式展示出来了,让我们对数据传输的路线有了一个大致的了解。
这里不打算讲traceroute工具打印出来的数据每一行是什么意思,也不会讲traceroute怎么用,命令行参数都有啥,这些东西随便一搜能出来好多。
在这里,我们来看看traceroute工具是怎么实现的,在看下面一节之前,不妨先想一下,如果你是traceroute的作者,你会怎么设计实现这个工具呢?

traceroute实现

最容易想到的实现方式

最容易想到的一种实现方式是,在报文里维护一个地址列表,每经过一个路由器时,将改路由器的地址添加进这个地址列表中,当报文最终到达目的地时,也就得到了一个完整的路径。
traceroute之所以没有使用这种方式,按照《TCP/IP》的作者来说,是受限于两个条件:
一个是ICMP协议报文的长度有限,当路径比较长时,无法存储这么多数据。
另一个原因是当时许多路由器/主机在实现ICMP协议时,并不支持这种地址列表的存储。
ICMP协议是TCP/IP协议族的一个子协议,属于网络层协议,主要用于在主机与路由器之间传递控制信息,traceroute就是基于ICMP协议实现的。
ICMP协议中有一个TTL(Time to Live)的概念,但是这里TTL表示的不是报文的生存时间,而是报文最大所能经过的节点数。举个例子,一个ICMP报文的TTL设置为5,那么在每经过一跳(路由器)时,路由器会将TTL的值减一,当TTL的值到达0时,如果此时该路由器(主机)不是报文最终的目的地,那么会返回发送方一个“TTL time exceeded”异常。
traceroute基本上就是基于这个规则实现的。

traceroute实现方式

用举例的方式来讲会简单一些
比如我们traceroute一个4.4.4.4的地址,我们(1.1.1.1)和4.4.4.4之间隔着2.2.2.2和3.3.3.3两个路由器,那么traceroute的过程是这样的。

  1. 发送TTL为1的报文,报文到达2.2.2.2后TTL被减小为0后返回,我们得到了1.1.1.1的下一跳地址2.2.2.2
  2. 发送TTL为2的报文,报文到达3.3.3.3后TTL被减小为0后返回,我们得到了1.1.1.1的下一跳的下一跳的地址3.3.3.3
  3. 发送TTL为3的报文,报文到达4.4.4.4后到达目的地。

从上面的步骤来看,整个traceroute就是通过不断增加TTL然后触发”TTL time exceeded“异常,通过记录异常报文返回时携带的地址来实现的。
现在还整最后一个问题:最终到达目的地时,属于报文正常到达,是不会触发“time exceeded”异常的,这个时候traceroute的发起方收不到任何反馈,从而无法判断报文是真的到了目的地还是因为网络原因被丢弃了。
对于这个问题,traceroute是这样的解决的,traceroute发送UDP数据包到目的地主机的一个很大的端口号上(30000以上,确保不会有应用用来对外提供服务),这时,目的主机收到这个报文后会产生一个"port unreachable"异常,traceroute命令发起方收到这个异常时,就知道了报文最终到达了目的地。
总结一下,traceroute做的事情其实很简单:向目的地的发UDP数据包,收到"TTL time exceeeded"异常后,打印一场中携带的ip地址,将TTL加1,直到收到“port unreachable”异常后结束。

traceroute的局限性

  1. traceroute是interface级别的,而不是router级别的,所以如下图所示,一个router可以有多个interface,那么从0.0.0.0 traceroute 5.5.5.5 得到的结果是0.0.0.0 -> 1.1.1.1 -> 3.3.3.3 -> 5.5.5.5。但是反过来,同样的路径从5.5.5.5 traceroute 0.0.0.0,你得到的结果可能是 5.5.5.5 -> 4.4.4.4 -> 2.2.2.2 -> 0.0.0.0。 看起来似乎不是一条路径对不?然而明明是一条路径啊啊啊啊。解决办法就是DNS把IP转换成域名,就可以解决这种疑惑了。

8fd23e89e1b5462fb14a5c68ece861a2-image.png

2.可能是受限于当时的种种制约因素,traceroute的实现方式说真的,真是别扭,尤其是最后一步靠触发“port unreachable”来判断是否最终到达了目的地的做法。

2.可能是受限于当时的种种制约因素,traceroute的实现方式说真的,真是别扭,尤其是最后一步靠触发“port unreachable”来判断是否最终到达了目的地的做法。


因僧问我西来意,我话山居不记年。
草履只栽三个耳,麻衣曾补两番肩。
东庵每见西庵雪,下涧常流上涧泉 。
半夜白云消散后,一轮明月到床前。