ICMP 时间戳请求允许系统向另一个系统查询当前的时间。
1. ICMP 时间戳
1.1 首部格式
图1 ICMP 时间戳请求与应答报文
它的 type 字段为 17(请求)或 18(应答),code 字段为 0.
- 发起时间戳:发送者的发送时间,由发送者填写
- 接收时间戳:接收者的接收时间,由接收者填写
- 发送时间戳:接收者的发送时间,由接收者填写
通常,接收时间戳等于发送时间戳。可以理解为接收者接收到 ICMP 时间戳请求报文后立即发送应答报文回去。
ICMP 时间戳请求与应答报文提供了毫秒级的分辨率,并且返回的时间是从午夜 00:00 开始到当前时间的毫秒数。这是它的一个缺陷。
1.2 ICMP 时间戳结构体
// icmp 时间戳头部 struct icmp_time { uint8_t icmp_type; uint8_t icmp_code; uint16_t icmp_cksum; uint16_t icmp_id; uint16_t icmp_seq; uint32_t icmp_origtime; uint32_t icmp_recvtime; uint32_t icmp_sendtime; };
2. 程序设计
2.1 程序路径
本文使用的程序托管在 gitos 上:http://git.oschina.net/ivan_allen/unp
如果你已经 clone 过这个代码了,请使用 Git pull 更新一下。本节程序所使用的程序路径是 unp/program/icmp/icmptime
.
2.2 注意
程序的编写方法其实和前面的 ping 命令基本上差不多,但是在这里需要注意的几个地方:
- 发送的时候,时间戳需要填写午夜到当前的毫秒数,计算公式如下:
// 绝对时间 cur = now() / 1000; // 午夜到当前的毫秒数 origtime = cur % (24*60*60*1000);
- 接收到的时间,都是网络字节序,打印它或者使用它的时候,注意要转换成本机字节序。我们自己在填写发起时间戳的时候,可以直接填写本机字节序,也可以填写网络字节序,因为接收者不会修改发起时间戳的任何内容。
3. 实验
图1 发送时间戳请求给 mars 主机并得到回应
要注意的是,并不是所有主机都会回应 ICMP 时间戳请求。很多主机都将此功能关闭了,因此你在运行的时候,可能会出现 no reply 字样,如图 2 所示。大量的 ICMP 请求会造成服务器的负担,为了防止被人利用,应该把服务器上的这些功能关掉。
图2 有些主机并不会产生 ICMP 时间戳应答报文
使用下面的命令可以把 ICMP 时间戳请求与应答报文关闭:
[mars]$ sudo iptables -A INPUT -p icmp --icmp-type 13 -j DROP [mars]$ sudo iptables -A OUTPUT -p icmp --icmp-type 14 -j DROP
图3 将 mars 上的 ICMP 时间戳请求与应答报文丢弃
4. 总结
- 掌握 ICMP 时间戳请求与应答