[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