Skip to content

ntpstats.pcap

NTP and PTP exchanges from packet captures (pcap and pcapng), standard library only.

NTP. Client requests (mode 3) are matched with server responses (mode 4) by the echoed origin timestamp (NTPv3/v4) or client cookie (NTPv5), and RFC 9769 interleaved responses by the request's receive timestamp. Offsets are computed from the server timestamps T2/T3 in the response and the capture timestamps of the two packets as T1/T4::

offset = ((T2 - c1) + (T3 - c4)) / 2      delay = (c4 - c1) - (T3 - T2)

so the result is the offset of the server relative to the capture host's clock, independent of what the client software believes, and it keeps working when the client randomises its transmit timestamp (RFC 9109 / data-minimisation). With hardware timestamps in the capture (pcapng if_tsresol nanosecond resolution, tcpdump -j adapter_unsynced) this measures the path without software timestamping noise.

NTP over PTP (RFC 10030, chrony 4.9): NTP messages carried in an organization-specific TLV of a unicast PTP event message on UDP port 319 are matched the same way. The PTP correction field of the response and the Network Correction extension field (0x010A) the server returns for the request give the transparent-clock corrections; when both are present they are applied as RFC 10030 section 3 specifies::

offset_c = offset + (nc_rs - nc_rq) / 2
delay_c  = delay - (nc_rs + nc_rq - dur_rs - dur_rq) * (1 - 100 ppm)

where nc_rs is the response's correction plus its receive duration (frame length over link_speed, 1 Gb/s by default) and dur_rq is taken equal to dur_rs. Corrections that are negative or give a negative delay are not applied (the RFC forbids using them). The uncorrected values and both corrections are kept as extra columns.

PTP (IEEE 1588) messages are handled by :mod:ntpstats.ptp.

Capture timestamps are kept as integer nanoseconds: a float of POSIX seconds only resolves about 240 ns today, too coarse for hardware-timestamped data.

Supported link types: Ethernet (with 802.1Q/802.1ad tags), Linux cooked v1/v2, raw IPv4/IPv6, BSD loopback. IPv4 and IPv6 (without extension headers) over UDP. IP fragments are ignored.

packets(data)

(timestamp_ns, linktype, frame) for every packet of a pcap/pcapng file.

Link-layer header: EtherType, payload offset and MAC addresses.

udp_payload(frame, lk)

Return (src, dst, udp_sport, udp_dport, payload) or None.

ntp_over_ptp(payload)

(ntp_message, ptp_correction_s, domain) if payload is a PTP event message with an RFC 10030 NTP TLV.

ntp_exchanges(data, port=123, link_speed=1000000000.0)

Matched client/server exchanges per (client, server) pair.

Rows are (time, offset, delay, stratum, version, interleaved, over_ptp, nc_request, nc_response, offset_uncorrected, delay_uncorrected); the last four are NaN for plain UDP. With RFC 9769 interleaved mode, a response whose origin equals the request's receive timestamp carries the precise transmit time of the previous response; that exchange's row is then recomputed with it and marked interleaved = 1. NTP over PTP (RFC 10030) is recognised on the PTP event port; link_speed (bit/s) gives the receive duration used in its corrections.

parse_capture(data, name='capture')

NTP and PTP time series found in a capture (one per client/server or slave/master).