目录
前文
在ipv4包结构中,已经提到了ip头中有个数据是存活时间 TTL(Time to Live):
指一个数据包在经过一个路由器时,可传递的最长距离(跃点数)。每当数据包经过一个路由器时,其存活次数就会被减一。当其存活次数为0时,路由器便会取消该数据包转发,IP网络的话,会向原数据包的发出者发送一个ICMP TTL数据包以告知跃点数超限。其设计目的是防止数据包因不正确的路由表等原因造成的无限循环而无法送达及耗尽网络资源。(来自wiki)
tracetroute 则是利用了 TTL 来实现的,通过递增的 TLL 发送 ICMP 包,每经过一个路由转发,TLL就会减1,如果至0还未到达我们的目标地址,则会返回 一个 ICMP 超时报文,具体可见ipv4 ICMP 报文>type=11 超时消息。
php tarceroute
搜录了一天,网上的资料少之又少,大部分还是exec traceroute,而我希望找到socket版本的。最主要的问题是,php的官方文档并没有提及怎么修改IP包的TTL,可能这并不属于php问题,而是要更深入去学习socket吧。
大概能找到的资料
- jaytaph/traceroute
用的是发UDP包,收ICMP包。不过呢,我在本地的win运行失败,服务器的Ubunut却运行成功。关于win,该项目的issues有人提到,回复是本地防火墙问题,不过暂时每搞明白,有懂得可以告知我一声。
- witersen/traceroute
ICMP 实现的,没测试,翻了下代码,应该没问题。
- PHP Ping Traceroute and DNS
需要注册账号,把代码下载后学习一波,写得真不错。
设置 TLL
这可以说是困扰我整个晚上的问题,无论是php官网文档,还是google,并没有过多提及,可能就是没人问吧。
过程
- 在第一个项目中,我已经知道了,php官网文档中的可用参数和变量是不全的,特别是 socket_set_option。这点在之前学习 curl_setopt就已经了解到了(在curl的官方文档不在php官方文档中的里面找个配置填进去,是能用的)。而我获取到的信息是:
define ("SOL_IP", 0);
define ("IP_TTL", 4); // On OSX, use '4' instead of '2'.
....
// Set TTL to current lifetime
socket_set_option ($send_socket, SOL_IP, IP_TTL, $ttl);
问题就变成了 : liunx 用 2 ,OSX 用4 ,所以windows 呢?手动测试过 2、3、4,但由于基于UDP,我本地并不能跑起来,无法排除是防火墙问题还是参数配置问题。
2. 在第二个项目中,直接找到设置为2,并没有测试,并且readme中就提示是 centos,也就没去深究。
3. 第三个项目中,我获得了神的救赎,这个注释太棒了。
define('SOL_IP', getprotobyname('ip'));
...
define('IP_TTL_l', 2);
define('IP_TTL_w', 4);
...
if($os == OS_LINUX) {
socket_set_option($send, SOL_IP, IP_TTL_l, $TTL);
} if($os == OS_WINDOWS) {
socket_set_option($send, SOL_IP, IP_TTL_w, $TTL);
}
立马测试
因为刚好有在写[测试]socket_read和socket_recvfrom 多次ping,就继续沿用代码
- 在 socket_read-ping 中
很遗憾,收不到回包。我的理解是,socket_read是需要已连接的socket,低TLL的Ip包发送后是到达不了目标地址的,返回超时回包,并没有建立连接。当然,TLL超过阈值后就能收到ping成功的返回数据。
- 在 socket_recvfrom-ping 中
成功运行,也给个超时回包报文,一起学习下
69 192 0 88 // 192 表示头部长度 88 指ip包全长
178 61 0 0
64 1 65 127 // 64 表示回包TTL, 1 表示 ICMP
192 168 2 1 // 网关ip
192 168 2 215 // 本地ip
11 0 244 255 // 11 表示超时报文
0 0 0 0
69 0 0 60 // Internet Header + 64 bits of Original Data Datagram
114 31 0 0
1 1 128 114 // 1 表示发包TTL, 1 表示 ICMP
192 168 2 215 // 发包ip
220 181 38 251 // 目标ip
8 0 135 57 // 8 表示查询
112 198 0 0 // 112 198 表示 Identifier,0 0 表示 Sequence Number
0 0 0 0
0 0 0 0
0 0 0 0
0 0 0 0
0 0 0 0
0 0 0 0
0 0 0 0
0 0 0 0
ftraceroute
执行代码
(xftraceroute::init())
->setDebug(0)
->setShuffleIpList(true)
->setCallback(function ($ret,$t,$host_list){
echo 'ret '.PHP_EOL;
foreach ($ret as $ip=>$r){
echo '===== '.$host_list[$ip] .' tracerout ret ====='.PHP_EOL;
echo xftracerouteformet($r).PHP_EOL;
}
echo 'total time_ms '.$t.PHP_EOL;
echo 'memory_get_peak_usage:'.memory_get_peak_usage().PHP_EOL;
echo 'memory_get_usage:'.memory_get_usage();
})->doit('baidu.com',64,200,10);
效果
ret
===== baidu.com tracerout ret =====
0 192.168.2.215 0.242 ms *
1 192.168.2.1 2.149 ms *
2 192.168.1.1 15.193 ms *
3 100.64.0.1 30.094 ms *
4 119.142.241.145 30.254 ms *
5 59.37.175.25 29.812 ms *
6 * timeout *
7 * timeout *
8 * timeout *
9 * timeout *
10 220.181.182.34 63.446 ms *
11 * timeout *
12 * timeout *
13 * timeout *
14 * timeout *
15 220.181.38.251 61.426 ms *
16 220.181.38.251 81.099 ms *
17 220.181.38.251 67.581 ms *
18 220.181.38.251 72.723 ms *
19 220.181.38.251 64.894 ms *
total time_ms 942.1010017395
memory_get_peak_usage:600552
memory_get_usage:454776
源码地址
xftraceroute.php
与xfping 不同
- 不能用 socket_read
- 需要处理imcp类型为11超时的数据包
我的思路:
- 用Sequence Number 去对应 ttl,由于确定回包顺序
- 超时包会携带原始数据包,具体是28字节后的数据
- socket_recvfrom 的 host ,是具体网关的ip,回包的归属需要到原始数据包中提取。
- 因为超时包携带的原始数据包,类型为8,所以目标ip是第17-20字节。
- 停止标志不同,ping可以用次数,而traceroute 则以到达目标ip为停止。
收获
- 认识了个新网站www.phpclasses.org
- 写好注释很求他人一头黑发
- 认识了ICMP超时回包报文。
- 类似socket的学习,不应该局限在php中,它只是用来学习和加深理解的,不然连个配置都写不明白
后续
对第一个项目在win中先发UDP包,再接收ICMP包的测试:
-
系统确实收到了ICMP包,包括 服务器不可达或者端口不可达
以下为wireshark抓包截图
-
php ICMP监听收不到内容
首先这应该不是防火墙问题,二这可能是系统的问题,大概是关于未设置IP_RECVERR,系统层面拦截了这个信息,没有往上传。这块超了我目前认知水平,具体可参考:
Windows Vista Receiving ICMP Destination Unreachable - Port Unreachable Packets
systemd-resolved doesn’t set IP_RECVERR, and thus misses out on ICMP error feedback
-
如果把发UDP包改成发ICMP包,则可以收到 主机不可达的回包
69 0 0 56 0 32 0 0 64 1 0 0 192 168 2 215 192 168 2 215 3 1 7 238 0 0 0 0 69 0 0 28 233 199 0 0 64 1 0 0 192 168 2 215 192 168 2 3 8 0
247 255 0 0 0 0
参考
存活时间