REFID Format

Related Topics: NTP-BUG 278, UpdatingTheRefidFormat, LeapSmearForNTPv4

The peers command of the ntpq utility (also available using the ntpq -p command) includes the Refid of each time source in the second column of the output.

In the packet of data exchanged between the ntpq process and the server, the Refid is represented as a four-byte data field. ntpq then translates this value into a string for display as follows:

  • If the associated time source has a stratum of 0, the bytes represent a Kiss Code.
  • If the associated time source has a stratum of 1, the bytes are converted to their ASCII representation, and the resulting up-to-four-character string is printed with leading and trailing period characters. Thus, the refid will indicate the type of reference clock, e.g. .ACTS. or .GPS.
  • If the associated time source has a stratum > 1, the four bytes are treated as an IPv4 address and printed in dotted-quad notation, e.g.

Note that if the time source has stratum > 1, the Refid it sends to the client will be related to the server to which that source itself is synchronized. The value is intended to be a unique identifier for a particular time server and is used to allow detection of timing loops.

Historically, the Refid sent was simply the (IPv4) address used to reach the synchronization server.

Recent versions of ntpd can instead use an IPv6 address to reach the synchronization server. Since IPv6 addresses don't fit into four bytes, ntpd "compresses" those address by using the first four bytes of the (binary) md5 digest of the IPv6 address as the Refid value.

Currently, ntpq has no way to know which type of Refid the server is sending and always displays the Refid value in dotted-quad format -- which means that any IPv6 Refids will be listed as if they were IPv4 addresses, even though they are not.

Starting with versions 4.2.8p3 and 4.3.41, ntpq has an apeers command which lists the Refid as an 8-character hexidecimal number, with no punctuation, instead of as a dotted quad. Other than the difference in representation, the two commands treat Refids in the same way.

Starting with 4.2.8.p3 and 4.3.47, if leap-smearing is underway, the ntpd server will generate a special Refid for itself, where the first byte will be "254", and the other three bytes will contain 24 encoded bits of the current smear value. Currently ntpq does not recognize this as being special, and simply reports the value as if it were an IPv4 number (but since the network is in a reserved range, such a value should never occur as an actual Refid). Once the leap smear period is over, the ntpd will return to using its usual Refid. For more information see the README.leapsmear file.

There is some ongoing discussion about updating the Refid display format in UpdatingTheRefidFormat .


The explanation of the Refid field values can be found in the RFCs:
  • The Common Variables section of RFC1305
  • The Packet Header Variables section of RFC5904

Example Code for Generating IPv6 Refids.

The following simple Python code can be used to generate Refids that match the ones NTP would use for IPv6 addresses (e.g. to confirm that the value displayed in an ntpq -p listing does indeed match a particular upstream server).

import socket
import md5
import re

# returns the refid string that NTP would use for the given IPv6 address
# (passed in string form)
#   * first, convert passed string to a packed binary representation of
#     the IPv6 addr
#   * the NTP refid is the first four bytes of the md5 digest (in binary
#     form) generated from the binary v6 address, expressed as an IPv4
#     dotted-quad IP number
def ipv6_to_ntp_refid(ipv6):
  return socket.inet_ntoa(d[0:4])

# reformats the input dotted-quad IP number (a string) into an 8-character
# hex representation of those four bytes (with no punctuation)
def ipv4_to_hex(ipv4):
  return 4*"%02x" % tuple(map(int, ipv4.split(".")))

# reformats a hex string into a dotted-quad-format string.
# (First, split the string into two-character chunks, then interpret each of those
# chunks as a base-16 integer, then convert back to a (base-10) string 
# representation, and finally join those strings with period between them.  
# [No input validation is done, so if the input isn't 8 hex characters the output
# will not actually be a four-byte dotted-quad address....])
def hex_to_ipv4(hex):
  return ".".join(map(lambda x:str(int(x,16)),re.findall("..",hex)))

# print the NTP refid that would be used for each IP address associated
# with the given hostname
def ntp_refids_for_host(hostname):
  print "NTP Refid        HexRefid   (addr type)"
  for a in addrinfo_list:
    # for each tuple "a", a[0] is the address family, and a[4] is a
    # sockaddr tuple, where a[4][0] is the string representation
    # of the ip address.
    if a[0] == socket.AF_INET6:
      print "%-16s" % refid, ipv4_to_hex(refid), "  (IPv6: " + ip + ")"
    if a[0] == socket.AF_INET:
      print "%-16s" % ip, ipv4_to_hex(ip), "  (IPv4)"

So, for example, after starting a Python interactive session and cutting-and-pasting the above lines into the command prompt, one can view the Refids would be used for both types of addresses assigned to the time server by running:

>>> ntp_refids_for_host("")
NTP Refid        HexRefid   (addr type)  9191ddfc   (IPv6: 2607:f248::45)   d8e4c045   (IPv4)

This topic: Support > OtherDocumentation > RefidFormat
Topic revision: 10 Oct 2022, DruLavigne
Copyright © by the contributing authors.Use of this website indicates your agreement with, and acceptance of, the PrivacyPolicy, the WikiDisclaimer, and the PrivateWebPolicy.