三十天学不会TCP,UDP/IP网络编程-TraceRoute的哲学
新年快乐,继续来部分粘贴复制我的这一系列文章啦,如果对和程序员有关的计算机网络知识,和对计算机网络方面的编程有兴趣,欢迎去gitbook(https://rogerzhu.gitbooks.io/-tcp-udp-ip/content/)star我的这一系列文章,虽然说现在这种“看不见”的东西真正能在实用中遇到的机会不多,但是我始终觉得无论计算机的语言,热点方向怎么变化,作为一个程序员,很多基本的知识都应该有所了解。而当时在网上搜索资料的时候,这方面的资料真的是少的可怜,所以,我有幸前两年接触了这方面的知识,我觉得我应该把我知道的记录下来,虽然写的不一定很好,但是希望能给需要帮助的人多个参考。我的计划是用半年时间来写完这一系列文章,这个标题也是我对太多速成文章的一种态度,好了,废话不再多扯了,下面是其中的一节内容,更多内容可以去gitbook上找到。
TraceRoute 是另外一个利用ICMP的程序,目的为了找到一个端点到另一个端点的路线,其设计理念我个人是觉得十分的巧妙。而且这个程序在windows和linux上还使用了稍微有点点不同的方法,但是却可以看到有异曲同工之妙的地方。
TraceRoute的起源
TraceRout这个网络工具主要是由一个叫Van Jacobson的大牛完成的,这位大牛应该算是互联网名人堂级人物,他最出名的应该是提出了TCP里面的Jacobson算法用以解决拥塞问题,而这个算法在今天百分之九十的TCP终端上面都在运用。 1987年,他在另外一位大牛Steve Deering的建议下写了这样一个探测网络路径的工具。同样,在第二大部分,程序的时候,我会用raw socket写一个玩具版的traceroute程序,来阐述其基本原理。
TTL和ICMP在windows上的结合
在介绍IP头格式的时候,里面有一个字段叫TTL,Time to live,这个字段起初是规定一个IP数据包在网络中的生存周期的,防止一个数据包无限制占用网络资源,毕竟要想一切有秩序,一个上限是不可避免要设置的,所谓的红线就是这个道理。而随着网络基础设施的升级和网络规模的增长,连接每个网络的路由器处理一个数据包的时间基本都远远小于1秒钟,而数据包在路由器之间的网线的上的传输时间更是可以忽略不计,所以这个字段就演化成了经过多少个路由器的计数器。每经过一个路由器,TTL的值就会减1,当这个值为0的时候,也就是这个IP数据包消亡的时候。
比如举个例子,如果某个IP包中TTL设置为1,那么这个数据包就只能在这个一个子网内传输,因为一旦经过了一个路由器,TTL就会变成0,该数据包就会消失了。而当这个数据包消失的时候,一个ICMP消息就会被产生,在上个部分里表里,ICMP头里的Type值为11的时候表示超时,而这个就是当TTL为0的时候被回复的ICMP消息。Windows 的TraceRoute程序就是利用了这个原理,先还是来看一下windows上运行traceroute是啥样的,不过windows的traceroute程序真名叫tracert,其运行结果如下:
我寻找的是从我这里到局域网的某一个路由器的地址,可以看到,从我的主机到这个路由器要经过三个路由器,所以有三行输出,而每行有三个往返时间,这是因为traceroute会发三次ICMP的echo消息,而这个输出值得我们逐行分析一下。
第一行是到我的局域网路由器(热点),往返时间是6-8ms之间,这个时候TTL被设置成1,从下面抓到的数据包中可以看到其具体的数据:
从截图中可以看出,第一个数据包TTL是1,虽然dest IP是100.97.2.9,但实际上在192.168.8.1这个路由器中这个数据包就已经消失了,因为这个时候该IP数据包中的TTL的值已经减为0,192.168.8.1随即回复一个ICMP消息报告超时。上一节中介绍了ICMP echo的格式,非常简单,而ICMP超时的数据包的格式比Echo稍微多一点点信息,但是依然是并不复杂,其格式如下:
其中前4个字节和echo是一样的,只不过这里type变成了11(十进制)标识超时错误消息,code为0表示ttl为0发生了超时。接下来4个字节作为保留字节根本没有用处,目的是和所有ICMP报文格式统一并兼容。而接着后面的数据部分就和echo不同了,而这些被选择中的数据也是这个数据头格式的精妙之所在。ICMP的超时消息会把发送方的IP数据头和IP数据包中头64个bit作为数据部分传回给发送方。其作用是可以让发送方的更上层的进程在如有需要的时候筛选出自己需要的信息和数据包。比如说,有了这个部分,里面就有了IP头中的TTL信息,发送端就知道是第几个TTL到期,数据包消失的。而接着的64个bit的数据在特定情况下更是有妙用,特别是在后面要介绍的端口不可达的ICMP消息之中。
第二行就是TTL为2的回复,而这一行全是星号,这是因为有的网关/路由器不会返回ICMP超时报文,甚至为了安全考虑,很多带有防火墙的路由器不返回各种ICMP报文,所以你会看到上面输出的星号,而这种暂时的不返回并不妨碍继续向更深的一层探测,所以在三次没有返回后,traceroute会将TTL递增1,继续发送ICMP的echo报文。
第三行就是TTL为3的结果,而这个时候数据包已经到达目标网络,由于探测的时候发的是ICMP echo request的消息,所以这个时候发送端可以得到ICMP echo reply消息,具体请参见上一节(你到底在ping什么)。所以发送端就能知道他的数据包已经到达了目的地,也就不会再继续增加TTL,探测活动停止,这个从抓获的数据包中也能得到体现。
TTL和ICMP在linux上的结合
前面说过traceroute这个程序在linux上和windows上采用一个有一点点不一样的设计,但是都是利用了ICMP消息和TTL。
Linux上traceroute程序就是使用traceroute这个命令,看起来像句废话,而linux上追踪数据包路径的方法是采用的UDP,而不是ICMP的echo消息。这样的一个不同点就是,使用UDP的时候,如果对方的端口是没有的,那么ICMP就会返回端口不可达消息,type是3。而在linux上会在这个UDP消息中使用一个30000以上端口号,这样当时数据包到达目的主机的时候,由于对方主机并没有打开这个UDP端口,所以ICMP会返回一个端口不可达的消息,这样发送端收到端口不可达的时候就知道不需要继续探测le。其格式和超时的格式是一模一样的,不同点只是type的数值是3,code的数值3标识端口不可达。
其余中间的探测过程和windows一样,都是使用ICMP的超时消息。
这里我想要稍微更多的介绍一下端口不可达的ICMP消息之中的data部分了,这个部分包含IP数据头和IP数据包的前8个字节,那么如果发送端发送的是UDP消息的话,回一下前面介绍的UDP头的格式,前8个字节正好UDP的头部,其中含有发送端的Port的到达端的Port,而UDP是IP的data部分,所以说发送端就可以从这个ICMP的错误消息中得到更多的信息。从下面这个抓包中可以看出这样一个data的返回(忽略port号,哈哈):
传统的traceroute利用ICMP消息有很多弊端,其中之一就是前面说的有很多网关或者路由器由于安全的设置不会返回任何ICMP错误消息,所以现在也有很多探测方法是利用TCP,有兴趣可以去搜索一下。