Last active
August 29, 2015 14:14
-
-
Save svagionitis/fa87048e7627c1fb6f69 to your computer and use it in GitHub Desktop.
Receive the NTP response
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
#!/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