diff --git a/requirements.txt b/requirements.txt index 2804b73..8fb62df 100644 --- a/requirements.txt +++ b/requirements.txt @@ -1,2 +1,3 @@ pyserial ipaddress +dhcp-leases diff --git a/router.py b/router.py index 26fa7cd..41c6af1 100644 --- a/router.py +++ b/router.py @@ -18,6 +18,7 @@ iptables -A INPUT -i lo -j ACCEPT sim = False + def unix_cmd(command): if sim: print('> %r' % command) @@ -29,10 +30,15 @@ def unix_cmd(command): class IoHandler: client = None handler = None + port = None def __init__(self, client, handler): self.handler = handler self.client = client + self.sentchunks = 0 + self.sentbytes = 0 + self.rcvdchunks = 0 + self.rcvdbytes = 0 def request(self): try: @@ -40,6 +46,8 @@ class IoHandler: if data: # print('<', data) self.write(data) + self.sentbytes += len(data) + self.sentchunks += 1 return except Exception as e: print('ERROR in request: %s' % e) @@ -51,6 +59,8 @@ class IoHandler: data = self.read() # print('>', data) self.client.sendall(data) + self.rcvdbytes += len(data) + self.rcvdchunks += 1 return except ConnectionResetError: pass @@ -99,6 +109,44 @@ class SerialHandler(IoHandler): self.serial.close() +class InfoHandler(IoHandler): + clients = {} + + def __init__(self, client, handler): + super().__init__(client, handler) + info = [f'{k} -> {v}' for k, v in AcceptHandler.routes.items()] + if AcceptHandler.handlers: + info.append('\nactive routings, statistics bytes/chunks') + info.append('fno port sent received') + for fno, h in AcceptHandler.handlers.items(): + info.append(f'{fno} {h.port} {h.sentbytes:d}/{h.sentchunks:d} {h.rcvdbytes:d}/{h.rcvdchunks:d}') + info.append('') + self.client.sendall('\n'.join(info).encode('utf-8')) + self.clients[client.fileno()] = self + self.fno = None + + def read(self): + return b'' + + def write(self, data): + pass + + def close(self): + self.clients.pop(self.client.fileno()) + + @classmethod + def log(cls, line): + if cls.clients: + for c in cls.clients.values(): + try: + c.client.sendall(line.encode('utf-8')) + c.client.sendall(b'\n') + except TimeoutError: + pass + else: + print(line) + + class AcceptHandler: """handler for routing @@ -112,6 +160,8 @@ class AcceptHandler: reused: in this case maxcount has to be increased ... """ readers = {} + handlers = {} + routes = {} def __init__(self, port, addr, iocls, maxcount=1): s = socket.socket(socket.AF_INET, socket.SOCK_STREAM) @@ -125,16 +175,18 @@ class AcceptHandler: self.port = port self.available = maxcount self.pending = 0 - print('listening at port %d for %s(%r)' % (port, iocls.__name__, addr) ) def close_client(self, iohandler): self.readers.pop(iohandler.fno, None) + client = iohandler.client + fno = client.fileno() try: - client = iohandler.client - self.readers.pop(client.fileno()) + self.readers.pop(fno) client.close() + InfoHandler.log(f'closed connection from port {self.port} fno {fno}') except Exception as e: - print('ERROR in close_client: %s' % e) + InfoHandler.log(f'{e!r} in close_client') + self.handlers.pop(fno, None) iohandler.client = None iohandler.fno = None self.available += 1 @@ -148,18 +200,23 @@ class AcceptHandler: return try: client, addr = self.socket.accept() - print('accepted', addr, 'on', self.port) + InfoHandler.log(f'accepted {addr} on {self.port} fno {client.fileno()}') handler = self.iocls(client, self) except Exception as e: - print('ERROR creating %s(%r)' % (self.iocls.__name__, self.addr)) + InfoHandler.log(f'{e!r} creating {self.iocls.__name__}({self.addr})') client.close() return self.readers[client.fileno()] = handler.request - self.readers[handler.fno] = handler.reply + if handler.fno is not None: + self.readers[handler.fno] = handler.reply + # statistics: number of chunks sent / received + handler.port = self.port + self.handlers[client.fileno()] = handler self.available -= 1 @classmethod def run(cls, routes, restrict=None): + cls.routes = dict(routes) if restrict is not None: lines = BASIC % dict(accept='DROP' if restrict else 'ACCEPT') unix_cmd('iptables -F') @@ -169,6 +226,7 @@ class AcceptHandler: if restrict: unix_cmd(FILTER % 22) + AcceptHandler(1111, None, InfoHandler, 5) for port, dest in routes.items(): port=int(port) if restrict: @@ -196,7 +254,6 @@ class AcceptHandler: cls.readers[fno]() - if __name__ == '__main__': parser = ConfigParser() cfgfiles = glob('/root/aputools/servercfg/%s_*.cfg' % socket.gethostname())