And buffing

This commit is contained in:
Reid 'arrdem' McKenzie 2021-11-20 22:37:57 -07:00
parent a0766fc42e
commit a9eecc6998
2 changed files with 24 additions and 120 deletions

View file

@ -9,15 +9,16 @@ when packet delivery latencies radically degrade and maintain a report file.
import argparse import argparse
from collections import defaultdict, deque from collections import defaultdict, deque
from datetime import datetime, timedelta from datetime import datetime, timedelta
from itertools import cycle
import logging import logging
from multiprocessing import Process, Queue from multiprocessing import Process, Queue
import queue import queue
import sys import sys
from typing import List from typing import List
from .lib import *
import graphviz import graphviz
from icmplib import Hop, ping, traceroute
from icmplib.utils import *
import requests import requests
@ -27,24 +28,12 @@ parser = argparse.ArgumentParser()
parser.add_argument("hosts", nargs="+") parser.add_argument("hosts", nargs="+")
def distinct(iter):
s = set()
l = []
for e in iter:
if e in s:
continue
else:
l.append(e)
s.add(e)
return l
class Topology(object): class Topology(object):
LOCALHOST = Hop("127.0.0.1", 1, [0.0], 0) LOCALHOST = Hop("127.0.0.1", 1, [0.0], 0)
def __init__(self): def __init__(self):
self._graph = defaultdict(set) # Dict[address, List[address]] self._graph = defaultdict(set)
self._nodes = {self.LOCALHOST.address: self.LOCALHOST} # Dict[address, Host] self._nodes = {self.LOCALHOST.address: self.LOCALHOST}
def next_hops(self, address: str) -> List[str]: def next_hops(self, address: str) -> List[str]:
return list(self._graph.get(address)) return list(self._graph.get(address))
@ -60,9 +49,11 @@ class Topology(object):
e0 = self._nodes[e.address] e0 = self._nodes[e.address]
e0._packets_sent += e._packets_sent e0._packets_sent += e._packets_sent
e0._rtts.extend(e.rtts) e0._rtts.extend(e.rtts)
e0._distance = max(e.distance, e0.distance) e0._distance = min(e.distance, e0.distance)
hosts = [(self.LOCALHOST.address, self.LOCALHOST.distance)] + [(e.address, e.distance) for e in trace] hosts = [(self.LOCALHOST.address, self.LOCALHOST.distance)] + [
(e.address, e.distance) for e in trace
]
i1 = iter(hosts) i1 = iter(hosts)
i2 = iter(hosts) i2 = iter(hosts)
next(i2) next(i2)
@ -78,8 +69,10 @@ class Topology(object):
g.edge(n.address, next) g.edge(n.address, next)
# Lol. Lmao. # Lol. Lmao.
return requests.post("https://dot-to-ascii.ggerganov.com/dot-to-ascii.php", params={"boxart":1, "src": g.source}).text return requests.post(
# return g.source "https://dot-to-ascii.ggerganov.com/dot-to-ascii.php",
params={"boxart": 1, "src": g.source},
).text
def __iter__(self): def __iter__(self):
return iter(sorted(self._nodes.values(), key=lambda n: n.distance)) return iter(sorted(self._nodes.values(), key=lambda n: n.distance))
@ -88,6 +81,7 @@ class Topology(object):
del self._graph[key] del self._graph[key]
del self._nodes[key] del self._nodes[key]
def compute_topology(hostlist, topology=None): def compute_topology(hostlist, topology=None):
"""Walk a series of traceroute tuples, computing a 'worst expected latency' topology from them.""" """Walk a series of traceroute tuples, computing a 'worst expected latency' topology from them."""
@ -101,12 +95,6 @@ def compute_topology(hostlist, topology=None):
return topology return topology
def cycle(iter):
while True:
for e in iter:
yield e
def pinger(host, id, queue): def pinger(host, id, queue):
# Mokney patch the RTT tracking # Mokney patch the RTT tracking
host._rtts = deque(host._rtts, maxlen=100) host._rtts = deque(host._rtts, maxlen=100)
@ -143,6 +131,7 @@ if __name__ == "__main__":
spinner = cycle("|/-\\") spinner = cycle("|/-\\")
with open("incidents.txt", "a") as fp: with open("incidents.txt", "a") as fp:
fp.write("RESTART\n")
while True: while True:
now = datetime.now() now = datetime.now()
@ -157,7 +146,7 @@ if __name__ == "__main__":
log.info("Graph -\n" + topology.render()) log.info("Graph -\n" + topology.render())
for h in topology: for h in topology:
if h.distance < 1 or h.distance > 6: if h.distance == 0:
continue continue
if h.address in workers: if h.address in workers:
@ -176,7 +165,11 @@ if __name__ == "__main__":
elif res.is_alive: elif res.is_alive:
if last and (delta := timestamp - last) > recovered_duration: if last and (delta := timestamp - last) > recovered_duration:
fp.write(f"RECOVERED\t{res.address}\t{timestamp.isoformat()}\t{delta.total_seconds()}\n") fp.write(
f"RECOVERED\t{res.address}\t{timestamp.isoformat()}\t{delta.total_seconds()}\n"
)
elif not last:
fp.write(f"UP\t{res.address}\t{timestamp.isoformat()}\n")
last_seen[res.address] = timestamp last_seen[res.address] = timestamp
elif not res.is_alive: elif not res.is_alive:
@ -184,7 +177,9 @@ if __name__ == "__main__":
workers[h.address].terminate() workers[h.address].terminate()
del workers[h.address] del workers[h.address]
del topology[h.address] del topology[h.address]
fp.write(f"DEAD\t{res.address}\t{timestamp.isoformat()}\t{delta.total_seconds()}\n") fp.write(
f"DEAD\t{res.address}\t{timestamp.isoformat()}\t{delta.total_seconds()}\n"
)
else: else:
fp.write(f"DOWN\t{res.address}\t{timestamp.isoformat()}\n") fp.write(f"DOWN\t{res.address}\t{timestamp.isoformat()}\n")

View file

@ -1,91 +0,0 @@
#!/usr/bin/env python3
import logging
from time import sleep
from icmplib import Hop
from icmplib.exceptions import (
ICMPLibError,
TimeExceeded,
)
from icmplib.models import Hop, ICMPRequest
from icmplib.sockets import (
ICMPv4Socket,
ICMPv6Socket,
)
from icmplib.utils import *
log = logging.getLogger(__name__)
def urping(address: str, hops: int, via: str, family=None, count=3, fudge=4, interval=0.05, timeout=2, source=None, **kwargs) -> Hop:
"""Ur-ping by (ab)using ICMP TTLs.
Craft an ICMP message which would go one (or more) hops FARTHER than the `address` host, routed towards `via`.
Send `count * fudge` packets, looking for responses from `address`.
Responses from `address` are considered; and a `Hop` is built from those results.
Other responses from other hosts are discarded.
"""
if is_hostname(via):
via = resolve(via, family)[0]
if is_hostname(address):
address = resolve(address, falmiy)[0]
if is_ipv6_address(via):
_Socket = ICMPv6Socket
else:
_Socket = ICMPv4Socket
ttl = hops
hop = Hop(address, 0, [])
packets_sent = 0
with _Socket(source) as sock:
for _ in range(count * fudge):
request = ICMPRequest(
destination=via,
# Note that we act like this is a new stream with a new ID and sequence each time to try and fool multipathing.
id=unique_identifier(),
sequence=0,
ttl=ttl,
**kwargs)
try:
sock.send(request)
packets_sent += 1
reply = None
reply = sock.receive(request, timeout)
rtt = (reply.time - request.time) * 1000
reply.raise_for_status()
except TimeExceeded:
sleep(interval)
except ICMPLibError as e:
log.exception(e)
break
if not reply:
log.debug(f"got no reply from {address}")
else:
# Because the default repr() is crap
reply_str = "ICMPReply({})".format(", ".join(slot + "=" + str(getattr(reply, slot)) for slot in reply.__slots__))
log.debug(f"Pinging {address} (distance {hops}) via {via} got reply {reply_str}")
if reply and reply.source != address:
continue
elif reply:
hop._packets_sent += 1
hop._rtts.append(rtt)
if hop and hop._packets_sent >= count:
break
return hop