Skip to content

ntpstats.sntp

Minimal, dependency-free SNTP (RFC 5905 / RFC 4330) client for measurements.

Improvements over the ntplib 0.1.9 copy used in 2012:

  • Client data minimisation (draft-ietf-ntp-data-minimization): the transmit timestamp is a random nonce, not the local clock, so requests do not leak the client's time; the true T1 is kept locally. The server's echoed origin timestamp must match the nonce (anti-spoofing / stale reply check).
  • NTP era handling: 32-bit NTP seconds roll over in 2036; timestamps are mapped to the era nearest the local clock.
  • Kiss-o'-Death (stratum 0: RATE, DENY, RSTR...) and unsynchronised (LI=3) replies are reported instead of being treated as time.
  • High-resolution local timestamps (time.time_ns) taken as close to the socket calls as Python allows.

  • Interleaved mode (RFC 9769, :func:query_interleaved): the server returns the precise transmit time of its previous response, so software timestamping on the server's send path no longer adds error.

  • Experimental NTPv5 (draft-ietf-ntp-ntpv5-09): 48-octet v5 header with client/server cookies, timescale, era and flags, the draft-identification extension field, and the NTPv4 -> v5 upgrade probe (reference timestamp NTP5DRFT). Interoperable with ntpd-rs' experimental v5 support.

This is a measurement client. It never sets the clock. Authenticated time (NTS, RFC 8915) is not implemented here; use chrony/NTPsec for that and analyse their logs with :mod:ntpstats.parsers.

query(server, port=123, timeout=2.0, version=4, family=0)

Send one client-mode request and return the measured offset/delay.

version=5 uses the experimental NTPv5 format (see :func:query_v5).

query_v5(server, port=123, timeout=2.0, family=0, tai_utc=TAI_UTC)

Experimental NTPv5 query (draft-ietf-ntp-ntpv5-09).

Header: LI|VN|Mode, Stratum, Poll, Precision, Root Delay and Root Dispersion (unsigned 4.28 fixed point, seconds), Timescale, Era, Flags, Server Cookie, Client Cookie, Receive and Transmit Timestamps. The random client cookie replaces the v4 origin timestamp. Timestamps are mapped with the era field; a TAI response is converted to UTC with tai_utc.

supports_v5(server, port=123, timeout=2.0, family=0)

Probe for NTPv5 support: an NTPv4 request whose reference timestamp is NTP5DRFT; a v5-capable server echoes it in its v4 response.

query_interleaved(server, port=123, timeout=2.0, family=0, spacing=1.0)

Two exchanges using the RFC 9769 interleaved client/server mode.

The first exchange is basic. The second request carries the server's receive timestamp as origin and our receive time as its receive timestamp; a server supporting the mode answers with origin equal to that receive timestamp and, as transmit time, the precise transmit time of its first response. The first exchange is then evaluated with it (RFC 9769 section 2, first timestamp set) and interleaved is True. Otherwise the second, basic, measurement is returned.

best_of(server, count=4, spacing=2.0, **kw)

Take count samples and return the minimum-delay one (the classic NTP clock-filter heuristic: least delay = least queueing asymmetry).