Skip to content

Instantly share code, notes, and snippets.

@svagionitis
Last active August 29, 2015 14:14
Show Gist options
  • Save svagionitis/fa87048e7627c1fb6f69 to your computer and use it in GitHub Desktop.
Save svagionitis/fa87048e7627c1fb6f69 to your computer and use it in GitHub Desktop.
Receive the NTP response
#!/usr/bin/env python
# Based on http://blog.mattcrampton.com/post/88291892461/query-an-ntp-server-from-python
# but added to interprete the message received from the NTP server. The structure of the
# response message described https://www.eecis.udel.edu/~mills/database/rfc/rfc1305/rfc1305c.pdf
# Also check http://www.ntp.org/ntpfaq/NTP-s-algo.htm for information of the NTP algorithm.
# Check the RFC for NTP https://www.ietf.org/rfc/rfc5905.txt
from socket import AF_INET, SOCK_DGRAM
import sys
import socket
import struct, time
import math
def getNTPTime(host = "0.pool.ntp.org"):
port = 123
buf = 1024
address = (host,port)
msg = '\x1b' + 47 * '\0'
# reference time (in seconds since 1900-01-01 00:00:00)
TIME1970 = 2208988800L # 1970-01-01 00:00:00
# connect to server
client = socket.socket( AF_INET, SOCK_DGRAM)
client.sendto(msg, address)
msg, address = client.recvfrom( buf )
resp = struct.unpack( "!1B1B1b1b3I4Q", msg )
print resp
LI_VN_M = resp[0]
# Leap Indicator (leap): 2-bit integer warning of an impending leap
# second to be inserted or deleted in the last minute of the current month
# Next following the values as described in the https://www.ietf.org/rfc/rfc5905.txt
# figure 9
LeapIndicator = {0:'no warning',
1:'last minute of the day has 61 seconds',
2:'last minute of the day has 59 seconds',
3:'unknown (clock unsynchronized)'}
LI = (LI_VN_M & 0xC0) >> 0x06
print "Leap Indicator: %d (%s)" % (LI, LeapIndicator[LI])
# Version Number (version): 3-bit integer representing the NTP
# version number
VN = (LI_VN_M & 0x38) >> 0x03
print "Version Number: %d" % VN
# Mode (mode): 3-bit integer representing the mode
# Next following the values as described in the https://www.ietf.org/rfc/rfc5905.txt
# figure 10
Mode = {0:'reserved',
1:'symmetric active',
2:'symmetric passive',
3:'client',
4:'server',
5:'broadcast',
6:'NTP control message',
7:'reserved for private use'}
M = (LI_VN_M & 0x07)
print "Mode: %d (%s)" % (M, Mode[M])
# Stratum (stratum): 8-bit integer representing the stratum
# Next following the values as described in the https://www.ietf.org/rfc/rfc5905.txt
# figure 11
# TODO How to use multiple keys (or range keys) for a single value
Stratum = {0:'unspecified or invalid',
1:'primary server (e.g., equipped with a GPS receiver)',
#range(2,15):'secondary server (via NTP)',
2:'secondary server (via NTP)',
16:'unsynchronized',
#range(17,255):'reserved'}
17:'reserved'}
stratum = resp[1]
print "Stratum: %d (%s)" % (stratum, Stratum[stratum])
# Poll: 8-bit signed integer representing the maximum interval between
# successive messages, in log2 seconds. Suggested default limits for
# minimum and maximum poll intervals are 6 and 10, respectively.
minpoll = 6
maxpoll = 10
poll = resp[2]
# See http://www.ntp.org/ntpfaq/NTP-s-algo.htm#Q-ALGO-POLL-BEST
if poll < minpoll or poll > maxpoll:
poll = 10
print "Poll: %d (%d secs)" % (poll, math.pow(2,poll))
# Precision: 8-bit signed integer representing the precision of the
# system clock, in log2 seconds. For instance, a value of -18
# corresponds to a precision of about one microsecond. The precision
# can be determined when the service first starts up as the minimum
# time of several iterations to read the system clock.
precision = resp[3]
print "Precision: %d (%e sec)" % (precision, 1/math.pow(2,abs(precision)))
# Root Delay (rootdelay): Total round-trip delay to the reference
# clock, in NTP short format.
root_delay = resp[4]
print "Root delay: %d (%f seconds)" % (root_delay, root_delay / 65536.0)
# Root Dispersion (rootdisp): Total dispersion to the reference clock,
# in NTP short format.
root_dispersion = resp[5]
print "Root dispersion: %d (%f seconds)" % (root_dispersion, root_dispersion / 65536.0 )
ref_identifier = resp[6]
ref_timestamp = resp[7]
orig_timestamp = resp[8]
recv_timestamp = resp[9]
tran_timestamp = resp[10]
t = struct.unpack( "!12I", msg )[10]
t -= TIME1970
return time.ctime(t).replace(" "," ")
if __name__ == "__main__":
if len(sys.argv) != 2:
print "Usage %s <NTP server>" % sys.argv[0]
sys.exit(-1)
print getNTPTime(sys.argv[1])
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment