[Git][NTPsec/ntpsec][master] 4 commits: Simpler duplicate elimination.
Eric S. Raymond
gitlab at mg.gitlab.com
Wed Nov 2 19:03:56 UTC 2016
Eric S. Raymond pushed to branch master at NTPsec / ntpsec
Commits:
72bfcc86 by Eric S. Raymond at 2016-11-02T12:14:00-04:00
Simpler duplicate elimination.
- - - - -
643ef2f1 by Eric S. Raymond at 2016-11-02T13:12:28-04:00
In the MRU list getter, implement sort choice.
- - - - -
89bfdb69 by Eric S. Raymond at 2016-11-02T14:42:39-04:00
In pyntpq, first successful MRU list.
- - - - -
3c0e3201 by Eric S. Raymond at 2016-11-02T15:03:24-04:00
pyntpq bug fixes.
- - - - -
4 changed files:
- include/ntp.h
- ntpq/pyntpq
- pylib/packet.py
- pylib/util.py
Changes:
=====================================
include/ntp.h
=====================================
--- a/include/ntp.h
+++ b/include/ntp.h
@@ -511,9 +511,9 @@ struct pkt {
/*
* Stuff for extracting things from li_vn_mode
*/
-#define PKT_MODE(li_vn_mode) ((uint8_t)((li_vn_mode) & 0x7))
-#define PKT_VERSION(li_vn_mode) ((uint8_t)(((li_vn_mode) >> 3) & 0x7))
-#define PKT_LEAP(li_vn_mode) ((uint8_t)(((li_vn_mode) >> 6) & 0x3))
+#define PKT_MODE(li_vn_mode) ((li_vn_mode) & 0x7)
+#define PKT_VERSION(li_vn_mode) (((li_vn_mode) >> 3) & 0x7)
+#define PKT_LEAP(li_vn_mode) (((li_vn_mode) >> 6) & 0x3)
/*
* Stuff for putting things back into li_vn_mode in packets and vn_mode
@@ -752,6 +752,8 @@ struct restrict_u_tag {
#define V6_SIZEOF_RESTRICT_U (offsetof(restrict_u, u) \
+ sizeof(res_addr6))
+/* pythonize-header: stop ignoring */
+
/*
* Access flags
*/
@@ -779,6 +781,8 @@ struct restrict_u_tag {
RES_MSSNTP | RES_FLAKE | \
RES_NOMRULIST)
+/* pythonize-header: start ignoring */
+
/*
* Match flags
*/
=====================================
ntpq/pyntpq
=====================================
--- a/ntpq/pyntpq
+++ b/ntpq/pyntpq
@@ -357,7 +357,7 @@ usage: help [ command ]
return ()
lo = self.__assoc_valid(tokens[0])
hi = self.__assoc_valid(tokens[1])
- if (hi < lo):
+ if (lo >= 0 and hi >= 0 and hi < lo):
return ()
return (lo, hi)
@@ -889,8 +889,9 @@ usage: showvars
def do_readlist(self, line):
"read the system or peer variables included in the variable list"
associd = self.__assoc_valid(line)
- qtype = TYPE_SYS if associd == 0 else TYPE_PEER
- self.__dolist(self.uservars.keys(), associd, CTL_OP_READVAR, qtype)
+ if associd >= 0:
+ qtype = TYPE_SYS if associd == 0 else TYPE_PEER
+ self.__dolist(self.uservars.keys(), associd, CTL_OP_READVAR, qtype)
def help_readlist(self):
self.say("""\
@@ -921,8 +922,9 @@ usage: writelist [ assocID ]
def do_readvar(self, line):
"read system or peer variables"
associd = self.__assoc_valid(line)
- qtype = TYPE_SYS if associd == 0 else TYPE_PEER
- self.__dolist(line.split()[1:], associd, CTL_OP_READVAR, qtype)
+ if associd >= 0:
+ qtype = TYPE_SYS if associd == 0 else TYPE_PEER
+ self.__dolist(line.split()[1:], associd, CTL_OP_READVAR, qtype)
def help_readvar(self):
self.say("""\
@@ -1021,8 +1023,10 @@ usage: mrv assocIDlow assocIDhigh [ name=value[,...] ]
def do_clocklist(self, line):
"read the clock variables included in the variable list"
- self.__dolist(self.uservars.keys(),
- self.__assoc_valid(line), CTL_OP_READVAR, TYPE_CLOCK)
+ assoc = self.__assoc_valid(line)
+ if assoc >= 0:
+ self.__dolist(self.uservars.keys(),
+ assoc, CTL_OP_READVAR, TYPE_CLOCK)
def help_clocklist(self):
self.say("""\
@@ -1042,8 +1046,9 @@ usage: cl [ assocID ]
def do_clockvar(self, line):
"read clock variables"
- self.__dolist(line.split()[1:], self.__assoc_valid(line),
- CTL_OP_READCLOCK, TYPE_CLOCK)
+ assoc = self.__assoc_valid(line)
+ if assoc >= 0:
+ self.__dolist(line.split()[1:], assoc, CTL_OP_READCLOCK, TYPE_CLOCK)
def help_clockvar(self):
self.say("""\
@@ -1198,7 +1203,13 @@ usage: config_from_file <configuration filename>
else:
mruhook = None
try:
- self.session.mrulist(mruhook)
+ span = self.session.mrulist(mruhook)
+ if not self.rawmode:
+ oracle = MRUSummary(interpreter.showhostnames)
+ self.say(MRUSummary.header + "\n")
+ self.say(("=" * MRUSummary.width) + "\n")
+ for entry in span.entries:
+ self.say(oracle.summary(entry) + "\n")
except Mode6Exception as e:
# Giving up after 8 restarts from the beginning.
# With high-traffic NTP servers, this can occur if the
=====================================
pylib/packet.py
=====================================
--- a/pylib/packet.py
+++ b/pylib/packet.py
@@ -13,6 +13,7 @@
from __future__ import print_function, division
import sys, socket, select, struct, curses.ascii, collections
import getpass, hashlib, time
+from ntpc import lfptofloat
# General notes on Python 2/3 compatibility:
#
@@ -109,7 +110,7 @@ from ntp_control import *
# ensure forward progress is made and loss isn't triggered too quickly
# afterward. While the lossless case gains only marginally with
# MAXFRAGS == 96, the lossy case is a lot slower due to the repeated
-# timeouts. Empirally, MAXFRAGS == 32 avoids most of the routine loss
+# timeouts. Empirically, MAXFRAGS == 32 avoids most of the routine loss
# on both the WiFi and UDP v6 tunnel tests and seems a good compromise.
# This suggests some device in the path has a limit of 32 ~512 byte UDP
# packets in queue.
@@ -253,6 +254,7 @@ SERR_NOCRED = "***No credentials"
SERR_SERVER = "***Server error code"
SERR_STALL = "***No response, probably high-traffic server with low MRU limit"
SERR_BADTAG = "***Bad MRU tag %s"
+SERR_BADSORT = "***Sort order %s is not implemented"
def dump_hex_printable(xdata):
"Dump a packet in hex, in a familiar hex format"
@@ -286,28 +288,21 @@ class MRUEntry:
self.ct = 0 # count of packets received
self.mv = None # mode and version
self.rs = None # restriction mask (RES_* bits)
- def matches(self, other):
- return self.last == other.last and self.addr == other.addr
+ def avgint(self):
+ return (e.last - e.first) / e.ct
def __repr__(self):
- return "<MRUentry: " + repr(self.__dict__) + ">"
+ return "<MRUentry: " + repr(self.__dict__)[1:-1] + ">"
-class MRUSpan:
+class MRUList:
"A sequence of address-timespan pairs returned by ntpd in one response."
def __init__(self):
- self.older = MRUEntry() # If not None, an MRUEntry object
self.entries = [] # A list of MRUEntry objects
self.now = None # server timestamp marking end of operation
- self.last_newest = None # timestamp same as last.# of final entry
- def breadcrumb(self, i):
- e = self.entries[i]
- return ", addr.%d=%s, last.%d=%s" % (i, e.addr, i, e.last)
-
def is_complete(self):
"Is the server done shipping entries for this span?"
- return self.last_newest is not None
+ return self.now is not None
def __repr__(self):
- return "<MRUSpan: older=%s entries=%s now=%s last_newest=%s>" \
- % (self.older, self.entries, self.now, self.last_newest)
+ return "<MRUList: entries=%s now=%s>" % (self.entries, self.now)
class Mode6Exception(BaseException):
def __init__(self, message, errorcode=0):
@@ -755,19 +750,41 @@ class Mode6Session:
restarted_count = 0
cap_frags = True
warn = sys.stderr.write
+ sorter = None
if variables:
+ if "sort" in variables:
+ sorter = variables["sort"]
+ del variables["sort"]
+ # FIXME: implement sorting by address, in case anyone cares
+ sortdict = {
+ "lstint" : lambda e: e.last, # lstint ascending
+ "-lstint" : lambda e: -e.last, # lstint descending
+ "avgint" : lambda e: e.avgint(), # avgint ascending
+ "-avgint" : lambda e: -e.avgint(), # avgint descending
+ "addr" : None, # IPv4 asc. then IPv6 asc.
+ "-addr" : None, # IPv6 desc. then IPv4 desc.
+ "count" : lambda e: e.ct, # hit count ascending
+ "-count": lambda e: -e.ct, # hit count descending
+ }
+ if sorter == "listint":
+ sorter = None
+ if sorter is not None:
+ sorter = sortdict.getkey(key)
+ if sorter == None:
+ raise Mode6Exception(BAD_SORT % key)
for k in list(variables.keys()):
if k in ("mincount", "resall", "resany",
"maxlstint", "laddr", "sort"):
continue
else:
raise Mode6Exception(SERR_BADPARAM % k)
+
# FIXME: Do the reslist parameter mappings from the C version
nonce = self.fetch_nonce()
- mrulist_interrupted = False
+ span = MRUList()
try:
# Form the initial request
#next_report = time.time() + MRU_REPORT_SECS
@@ -828,6 +845,9 @@ class Mode6Session:
elif e.errorcode:
raise e
+ # Parse the response
+ variables = self.__parse_varlist()
+
# Comment from the C code:
# This is a cheap cop-out implementation of rawmode
# output for mrulist. A better approach would be to
@@ -836,31 +856,20 @@ class Mode6Session:
# cheap approach has indexes resetting to zero for
# each query/response, and duplicates are not
# coalesced.
- variables = self.__parse_varlist()
if rawhook:
rawhook(variables)
- continue
- # Deserialize the contents of one response
- span = MRUSpan()
+ # Analyze the contents of this response into a span structure
+ last_older = None
+ addr_older = None
+ highwater = len(span.entries)
for (tag, val) in variables.items():
- if tag =="addr.older":
- if span.older.last is None:
- if self.debug:
- warn("addr.older %s before last.older\n" % val)
- return False
- span.older.addr = val
- continue
- elif tag =="last.older":
- span.older.addr = val
+ if tag =="now":
+ span.now = lfptofloat(val)
continue
- elif tag =="now":
- span.now = val
+ elif tag == "last.newest":
continue
- elif tag =="last.newest":
- span.last_newest = val
- continue
- for prefix in ("addr", "last", "ct", "mv", "rs"):
+ for prefix in ("addr", "last", "first", "ct", "mv", "rs"):
if tag.startswith(prefix + "."):
(member, idx) = tag.split(".")
try:
@@ -869,12 +878,10 @@ class Mode6Session:
raise Mode6Exception(SERR_BADTAG % tag)
if idx >= len(span.entries):
span.entries.append(MRUEntry())
+ if type(val) != type(0) and val.startswith("0x"):
+ val = lfptofloat(val)
setattr(span.entries[-1], prefix, val)
- # Now try to glue it to the history
- # FIXME: The following enables an eyeball check of the parse
- print(repr(span))
-
# If we've seen the end sentinel on the span, break out
if span.is_complete():
break
@@ -907,14 +914,40 @@ class Mode6Session:
nonce_uses += 1
if nonce_uses >= 4:
nonce = fetch_nonce()
- nonce_uses = 0
+ nonce_uses = 0
for i in range(len(span.entries)):
- incr = span.breadcrumb(i)
+ e = self.entries[len(span.entries) - i - 1]
+ incr += ", addr.%d=%s, last.%d=%s" % (i, e.addr, i, e.last)
if len(req_buf) + len(incr) >= CTL_MAX_DATA_LEN:
break
else:
req_buf += incr
except KeyboardInterrupt:
- mrulist_interrupted = True
+ pass # We can test for interruption with is_complete()
+
+ # C ntpq's code for stitching together spans was absurdly
+ # overelaborate - all that dancing with last.older and
+ # addr.older was, as far as I can tell, just pointless.
+ # Much simpler to just run through the final list throwing
+ # out every entry with an IP address that is duplicated
+ # with a later most-recent-transmission time.
+ addrdict = {}
+ deletia = []
+ for (i, entry) in enumerate(span.entries):
+ if entry.addr not in addrdict:
+ addrdict[entry.addr] = []
+ addrdict[entry.addr].append((i, entry.last))
+ for addr in addrdict:
+ deletia += sorted(addrdict[addr], key=lambda x: x[1])[:-1]
+ deletia = [x[0] for x in deletia]
+ deletia.sort(reverse=True)
+ for i in deletia:
+ span.entries.pop(i)
+
+ # Sort for presentation
+ if sorter:
+ span.entries.sort(key=sorter)
+
+ return span
# end
=====================================
pylib/util.py
=====================================
--- a/pylib/util.py
+++ b/pylib/util.py
@@ -10,7 +10,7 @@ import ntp.ntpc
from ntp.packet import *
-def canonicalize_dns(hostname):
+def portsplit(hostname):
portsuffix = ""
if hostname.count(":") == 1: # IPv4 with appended port
(hostname, portsuffix) = hostname.split(":")
@@ -22,6 +22,10 @@ def canonicalize_dns(hostname):
portsuffix = hostname[portsep:]
hostname = hostname[:portsep]
hostname = hostname[1:-1] # Strip brackets
+ return (hostname, portsuffix)
+
+def canonicalize_dns(hostname):
+ (hostname, portsuffix) = portsplit(hostname)
try:
ai = socket.getaddrinfo(hostname, None, 0, 0, 0, socket.AI_CANONNAME)
except socket.gaierror as (s, _e):
@@ -247,4 +251,38 @@ class PeerSummary:
jd))
return line
+class MRUSummary:
+ "Reusable class for MRU entry summary generation."
+ def __init__(self, showhostnames):
+ self.showhostnames = showhostnames # If false, display numeric IPs
+ self.now = time.time()
+
+ header = "lstint avgint rstr r m v count rport remote address"
+
+ width = 78
+
+ def summary(self, entry):
+ lstint = int(self.now - entry.last + 0.5)
+ active = int(entry.last - entry.first + 0.5)
+ favgint = active / entry.ct
+ avgint = int(favgint + 0.5)
+ stats = "%6d" % lstint
+ if 5 < avgint or 1 == entry.ct:
+ stats += " %6d" % avgint
+ else:
+ stats += " %6.2f" % favgint
+ if entry.rs & RES_KOD:
+ rscode = 'K'
+ elif entry.rs & RES_LIMITED:
+ rscode = 'L'
+ else:
+ rscode = '.'
+ (dns, port) = portsplit(entry.addr)
+ if self.showhostnames:
+ dns = canonicalize_dns(dns)
+ stats += " %4hx %c %d %d %6d %5s %s" % \
+ (entry.rs, rscode, PKT_MODE(entry.mv), PKT_VERSION(entry.mv),
+ entry.ct, port[1:], dns)
+ return stats
+
# end
View it on GitLab: https://gitlab.com/NTPsec/ntpsec/compare/209dbc185c8b62ad018c67ec6e912d2eb634f702...3c0e3201633e87d8b9c86eb3de323cf6e4f979d8
-------------- next part --------------
An HTML attachment was scrubbed...
URL: <http://lists.ntpsec.org/pipermail/vc/attachments/20161102/41de5184/attachment.html>
More information about the vc
mailing list