Ping 的“往返时间”存储在 IP 标头中的何处?
往返时间实际上并未存储在任何地方。发送主机使用 ICMP 的 16 位标识符和序列字段记住它发送每个 ICMP Echo Request 消息的时间。当它得到ICMP Echo Reply时,记下当前时间,找到它发送该回复标识的匹配Request报文的时间,计算差值,并上报。
通常 ping 使用 ICMP 的标识字段来区分多个同时进行的 ping,并使用序列字段来区分单个数据包。
由实现决定将给定数据包的传出时间存储在何处:而不是将其存储在主机上的表中,它通常在传出请求中发送它,并在回复中使用副本来计算时间。(感谢评论者指出这一点。)它以任何便于实施的方式发送,当然必须信任远端和任何介入设备,以正确复制数据。已知一些系统以 16 字节的微秒分辨率表示时间,有些系统以毫秒的分辨率表示 8 字节。
data
IP 数据包部分内部的格式是 ICMP Echo Request/Reply 消息,从RFC 792 “Internet 控制消息格式”(p14)复制到此处。
Type
请求为 8,回复为 0;Code
是 0。
0 1 2 3
0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
| Type | Code | Checksum |
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
| Identifier | Sequence Number |
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
| Data ...
+-+-+-+-+-
附注。需要说明的是,IP 报头的标识字段通常设置为任意值,每个传出数据包都不同,用于重组任何碎片,并且与 ICMP 正文中的任何值都不相同。
此外,虽然定义了一种将时间戳作为选项放入 IP 报头的机制,但这不是 ping 的正常机制,因为很多路由器被配置为不传递某些 IP 选项。请参阅Internet 协议时间戳选项的RFC 781规范。
最后,尽管这里的所有内容都是从 IPv4 的角度编写的,但根据原始问题;但是 IPv6 中的 ping 非常相似,请参阅 ICMPv6 RFC 4443。
至少ping
对于 Linux上的通用实用程序,发送数据包的时间存储在回显请求数据包的数据部分中,即在 IP 和 ICMP 标头之后。当接收方以回声回复回复时,数据部分保持完整,因此发送方可以计算往返时间。
该实用程序的手册页中ping
对此进行了描述(在“ICMP PACKET DETAILS”下):
如果数据空间至少是
struct timeval
ping的大小,则使用该空间的开始字节来包括它在计算往返时间时使用的时间戳。如果数据空间较短,则不会给出往返时间。
在我的机器上sizeof(struct timeval)
是 16,因此将数据包数据大小设置为 15 可以防止ping
显示往返时间:
$ ping -s 15 8.8.8.8
PING 8.8.8.8 (8.8.8.8) 15(43) bytes of data.
23 bytes from 8.8.8.8: icmp_seq=1 ttl=121
当然,正如@jonathanjo 的回答所描述的那样,将发送时间戳存储在实用程序中也是一种可能的实现。甚至 Linux 实用程序也需要一些内部簿记,因为它会检测重复的数据包。