If we use ICMP's
ping, we know the TTL and
round-trip time are stored in the IP header. In the below IP header map we know TTL's location, but where is the round-trip time?
Is it stored in
The round trip time is not actually stored anywhere. The sending host remembers the time it sends each ICMP Echo Request message, using ICMP's 16-bit identifier and sequence fields. When it gets the ICMP Echo Reply, it notes the current time, finds the time it sent the matching Request packet identified by the reply, calculates the difference, and reports it.
Typically ping uses ICMP's identification field to differentiate multiple simultaneous pings, and the sequence field to differentiate individual packets.
It is up to the implementation to decide where to store the outgoing time for a given packet: instead of storing it on the host in a table, it typically sends it in the outgoing request and uses the copy in the reply to calculate the time. (Thanks commenters for pointing this out.) It's sent in whatever way is convenient for the implementation, and of course has to trust the far end, and any intervening equipment, to properly copy the data. Some systems are known to represent the time in 16 bytes with resolution of microseconds, some as 8 bytes with resolution of milliseconds.
The format inside the
data portion of the IP packet is the ICMP Echo Request/Reply message, copied here from RFC 792 "Internet Control Message Format" (p14).
Type is 8 for Request, 0 for Reply;
Code is 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 ... +-+-+-+-+-
PS. Just to be clear, the identification field of the IP header is normally set to an arbitrary value, different for each outgoing packet, used for reassembly of any fragmentation, and doesn't have the same value as anything in the ICMP body.
Also, although there is a mechanism defined for putting timestamps into the IP header as an option, this is not the normal mechanism for ping because very many routers are configured not to pass certain IP options. See RFC 781 Specification of the Internet Protocol Timestamp Option.
Finally, although everything here was written from an IPv4 perspective, per the original question; but ping in IPv6 is extremely similar, see ICMPv6 RFC 4443.
At least with the common
ping utility on Linux, the time when the packet was sent is stored in the data part of the echo request packet, i.e. after the IP and ICMP headers. The data part is kept intact when the receiver replies with an echo reply, so the sender can calculate the round-trip time.
This is described in the man page for the
ping utility (under "ICMP PACKET DETAILS"):
If the data space is at least of size of
struct timevalping uses the beginning bytes of this space to include a timestamp which it uses in the computation of round trip times. If the data space is shorter, no round trip times are given.
On my machine
sizeof(struct timeval) is 16, so setting the packet data size to 15 prevents
ping from showing the round-trip times:
$ ping -s 15 18.104.22.168 PING 22.214.171.124 (126.96.36.199) 15(43) bytes of data. 23 bytes from 188.8.131.52: icmp_seq=1 ttl=121
Of course, storing the send timestamp within the utility, as @jonathanjo's answer describes, would also be a possible implementation. Even the Linux utility needs some internal bookkeeping, since it detects duplicate packets.