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
from collections import defaultdict, deque
from datetime import datetime, timedelta
from itertools import cycle
import logging
from multiprocessing import Process, Queue
import queue
import sys
from typing import List
from .lib import *
import graphviz
from icmplib import Hop, ping, traceroute
from icmplib.utils import *
import requests
@ -27,24 +28,12 @@ parser = argparse.ArgumentParser()
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):
LOCALHOST = Hop("127.0.0.1", 1, [0.0], 0)
def __init__(self):
self._graph = defaultdict(set) # Dict[address, List[address]]
self._nodes = {self.LOCALHOST.address: self.LOCALHOST} # Dict[address, Host]
self._graph = defaultdict(set)
self._nodes = {self.LOCALHOST.address: self.LOCALHOST}
def next_hops(self, address: str) -> List[str]:
return list(self._graph.get(address))
@ -60,9 +49,11 @@ class Topology(object):
e0 = self._nodes[e.address]
e0._packets_sent += e._packets_sent
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)
i2 = iter(hosts)
next(i2)
@ -78,8 +69,10 @@ class Topology(object):
g.edge(n.address, next)
# Lol. Lmao.
return requests.post("https://dot-to-ascii.ggerganov.com/dot-to-ascii.php", params={"boxart":1, "src": g.source}).text
# return g.source
return requests.post(
"https://dot-to-ascii.ggerganov.com/dot-to-ascii.php",
params={"boxart": 1, "src": g.source},
).text
def __iter__(self):
return iter(sorted(self._nodes.values(), key=lambda n: n.distance))
@ -88,6 +81,7 @@ class Topology(object):
del self._graph[key]
del self._nodes[key]
def compute_topology(hostlist, topology=None):
"""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
def cycle(iter):
while True:
for e in iter:
yield e
def pinger(host, id, queue):
# Mokney patch the RTT tracking
host._rtts = deque(host._rtts, maxlen=100)
@ -143,6 +131,7 @@ if __name__ == "__main__":
spinner = cycle("|/-\\")
with open("incidents.txt", "a") as fp:
fp.write("RESTART\n")
while True:
now = datetime.now()
@ -157,7 +146,7 @@ if __name__ == "__main__":
log.info("Graph -\n" + topology.render())
for h in topology:
if h.distance < 1 or h.distance > 6:
if h.distance == 0:
continue
if h.address in workers:
@ -176,7 +165,11 @@ if __name__ == "__main__":
elif res.is_alive:
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
elif not res.is_alive:
@ -184,7 +177,9 @@ if __name__ == "__main__":
workers[h.address].terminate()
del workers[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:
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