Anatomy of the NTP packet#

Why#

The Chrony chapter explains how an offset is computed from four timestamps, but never what the packet carrying them actually looks like on the wire. Understanding this packet’s structure, a tiny 48 bytes in its simplest form, makes it possible to read a network capture, understand the mode this project uses (client-server) as opposed to the other modes NTP offers, and discover a protocol-level mechanism this guide hasn’t shown yet: the “Kiss-o’-Death”.

How: the header fields#

 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
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
|LI | VN  |Mode |    Stratum    |     Poll      |   Precision   |
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
|                           Root Delay                          |
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
|                        Root Dispersion                        |
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
|                          Reference ID                         |
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
|                                                               |
+                    Reference Timestamp (64)                   +
|                                                               |
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
|                     Origin Timestamp (64)  [T1]               |
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
|                     Receive Timestamp (64) [T2]               |
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
|                  Transmit Timestamp (64) [T3]                 |
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
|           Extensions (NTS, MAC...), variable length           |
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+

The fourth timestamp (T4, reception) never appears in the packet itself: it’s recorded locally by the recipient at the moment of reception, never transmitted over the wire.

FieldSizeRole
LI (Leap Indicator)2 bitsAnnounces an imminent leap second (see dedicated section below)
VN (Version)3 bitsProtocol version, 4 today
Mode3 bitsThe sender’s role in the exchange (see Modes section)
Stratum8 bitsHierarchy level (Overview chapter), 0 has a special meaning, see Kiss-o’-Death
Poll8 bitsPolling interval, in log₂ seconds
Precision8 bitsDeclared precision of the local clock, in log₂ seconds
Root Delay / Root Dispersion32 bits eachCumulative delay and uncertainty up to the stratum 0 reference
Reference ID32 bitsSource identifier (ASCII string on stratum 0/1, e.g. PPS0; IP address of the upstream server on stratum ≥ 2)
Origin/Receive/Transmit Timestamps64 bits eachT1, T2, T3, see The four timestamps section below

Reference ID: the catalog of stratum 1 sources#

On a stratum 1 server, the Reference ID is a 4-character ASCII string announcing the type of physical reference disciplining the clock. RFC 5905 (§ 7.3) assigns a conventional list; here are the main codes:

CodePhysical source
GPSGPS constellation (United States), received via a satellite receiver
GALGalileo constellation (Europe)
PPSGeneric pulse-per-second, supplied by any calibrated external equipment
DCFDCF77 longwave transmitter (Mainflingen, Germany, 77.5 kHz), Europe’s historic radio clock, still received by millions of alarm clocks and station clocks
MSFMSF longwave transmitter (Anthorn, United Kingdom, 60 kHz)
WWVBWWVB longwave transmitter (Fort Collins, Colorado, 60 kHz)
JJYJapanese longwave transmitters (Fukushima 40 kHz, Saga 60 kHz)
TDFAllouis transmitter (France, 162 kHz), the former France Inter carrier, still broadcasting a time code driven by atomic clocks
CHU / WWV / WWVHHF time-signal radio stations (Ottawa, Colorado, Hawaii), the shortwave “spoken time”
IRIGIRIG wired signal (Inter-Range Instrumentation Group), time distribution in industrial and military environments
NIST / ACTS / USNO / PTBTelephone-modem time services of national institutes (the American NIST and USNO, the German PTB)
GOESGOES geostationary weather satellites (United States), a historic time service
LORCLORAN-C radionavigation stations (100 kHz), a network now largely shut down

These codes tell the history of time references: national longwave transmitters (DCF, MSF, WWVB, JJY, TDF) and telephone-modem services (NIST, ACTS) predate GNSS, which has supplanted almost all of them; PPS designates not a source per se but the electrical alignment pulse that any source, whatever it is, feeds to the machine.

The exact code remains declarative: the server’s configuration chooses it, and nothing lets a client verify it. On this server, the operator chose PPS0 (Reference ID : 50505330 (PPS0), i.e. the ASCII codes of P, P, S, 0). From stratum 2 upward, the same field changes nature and carries the IPv4 address of the upstream source (see Pitfalls to avoid).

The four timestamps and the offset computation#

The client-server exchange relies on four instants, measured alternately by the client’s clock and by the server’s:

      Client                             Server
        │                                   │
   T1   ●─────────── request ─────────────► ●   T2
        │                                   │  (processing)
   T4   ● ◄────────── response ─────────────●   T3
        │                                   │
  • T1 (Origin Timestamp): request departure, read on the client’s clock;
  • T2 (Receive Timestamp): request arrival, read on the server’s clock;
  • T3 (Transmit Timestamp): response departure, server’s clock;
  • T4: response arrival, client’s clock. This fourth instant is never transmitted in the packet: each machine records it locally on reception (see Pitfalls to avoid).

Each difference mixes two unknowns: the real offset between the two clocks and the network travel time. T2 - T1 contains the offset plus the outbound trip; T3 - T4 contains the offset minus the return trip. Averaging the two cancels the trips out, provided they are equal:

offset = ((T2 - T1) + (T3 - T4)) / 2
delay  = (T4 - T1) - (T3 - T2)

The round-trip delay, for its part, subtracts from the total time seen by the client (T4 - T1) the server’s processing time (T3 - T2), which never crosses the network.

Worked example: a server 100 ms ahead of the client, 50 ms of travel in each direction, 20 ms of processing (in bold, the four actually measured values):

InstantClient clockServer clock (+100 ms)
T1 (request departure)0 ms100 ms
T2 (server arrival)50 ms150 ms
T3 (response departure)70 ms170 ms
T4 (client return)120 ms220 ms
offset = ((150 - 0) + (170 - 120)) / 2 = (150 + 50) / 2 = 100 ms   (the real lead)
delay  = (120 - 0) - (170 - 150)       = 120 - 20       = 100 ms   (2 × 50 ms of travel)

The implicit assumption, a network symmetric in both directions, is the protocol’s fundamental limit: any asymmetry introduces a bias equal to half of it, undetectable from the four timestamps alone (Chrony chapter, Going further). The step-by-step walkthrough of this computation is also presented, with another example, in How does NTP work? (sookocheff.com), one of the founding reads of this chapter (see Sources and acknowledgements in the Overview chapter).

NTP modes#

The Mode field (3 bits) indicates the role played by the packet. This guide only uses one, but the protocol defines several:

ModeUsageUsed in this guide?
Client (3) / Server (4)The client queries, the server replies, the mode used for every request to this Stratum 1 server and to its upstream sourcesYes
Symmetric active/passive (1/2)Two servers query each other peer-to-peer, each able to discipline the other, typical between same-tier servers monitoring one anotherNo
Broadcast/Multicast (5)A server broadcasts time without individual requests, open loop, less precise (no network delay computation possible without a two-way exchange)No

Client-server mode is the right choice for this project: a public server answering millions of anonymous clients has no business maintaining symmetric associations with each of them, and broadcast would sacrifice precision for a bandwidth gain that’s irrelevant here.

Kiss-o’-Death: rate control built into the protocol#

The nftables chapter shows how to rate-limit at the network level (dropping packets past a threshold). NTP additionally has its own retaliation mechanism, at the application level: when Stratum = 0, the Reference ID field no longer holds a source identifier, but a 4-character ASCII Kiss code, hence the name “Kiss-o’-Death” (KoD).

CodeMeaning
RATEPolling frequency too high, the poll interval must be increased
DENY / RSTRAccess denied by server policy
INITThe server isn’t synchronized yet

This is a cooperative signal: a well-behaved client (like chrony) actually reduces its request rate upon receiving a RATE. A malicious client can ignore it, which is why the network-level rate-limiting from the nftables chapter remains necessary as a complement: KoD regulates well-behaved clients, the firewall contains the ones that aren’t.

The Leap Indicator field#

The Leap seconds chapter explains the leap-seconds.list file that tells chrony in advance that a leap second is scheduled. The 2-bit LI field is the mechanism by which this information then gets relayed in real time to every client, in every packet, during the hours leading up to the event:

LI valueMeaning
00No warning
01The last minute of the day will have 61 seconds
10The last minute of the day will have 59 seconds
11Clock unsynchronized (alarm)

Pitfalls to avoid#

  • Confusing the Reference ID of a stratum 1 with that of a higher stratum. On this project (Reference ID: 50505330 (PPS0), Chrony chapter), it’s a 4-character ASCII string. On a client querying this server, the Reference ID it sees in replies from its own upstream stratum ≥ 2 sources will instead be an IPv4 address, the same field changes meaning depending on the sender’s stratum.
  • Forgetting that T4 is never in the packet. A network capture of a single NTP packet only contains 3 of the 4 timestamps needed for the offset calculation, the fourth is local to each machine, never observable from outside.

Verify#

# Capture an NTP exchange to observe the actual fields
tcpdump -i any -n -vv udp port 123 -c 4

# With an NTP client able to print packet details
ntpdate -d pool.ntp.org 2>&1 | head -30

📚 Going further

  • RFC 5905 defines the complete NTPv4 packet structure, every field and its exact encoding.
  • The Kiss-o’-Death code format is detailed in RFC 5905 §7.4.
  • The historical Autokey authentication mechanism (RFC 5906), NTS’s predecessor, is mentioned in the Chrony chapter.