From cc70191abcb928d2ec057b7aa04683c2ce8491e1 Mon Sep 17 00:00:00 2001 From: Ludovic Pouzenc Date: Mon, 16 May 2016 01:19:53 +0200 Subject: Implement parsing of brmacs.cgi and status.cgi (only for local interfaces) --- haircontrol/data.py | 11 +++++++++-- haircontrol/discovery.py | 30 +++++++++++++++++++++++++++--- haircontrol/inspectors.py | 40 +++++++++++++++++++++++++++++++++++----- 3 files changed, 71 insertions(+), 10 deletions(-) diff --git a/haircontrol/data.py b/haircontrol/data.py index 6f5733d..8adf75f 100644 --- a/haircontrol/data.py +++ b/haircontrol/data.py @@ -37,6 +37,13 @@ class Equipment: def __repr__(self): return repr( (self.name, self.mgmtip, list(self.ifaces.values())) ) + def add_iface(self, ifname, mac): + iface = self.ifaces.get(ifname) + if iface: + iface.mac = mac + else: + self.ifaces[ifname] = Interface(ifname, mac) + def add_seen_mac(self, ifname, mac): iface = self.ifaces.get(ifname) if not iface: @@ -52,6 +59,6 @@ class Interface: self.mac_seen = [] def __repr__(self): - return repr( ( self.mac, self.name, self.mac_seen ) ) - #return repr( ( self.mac, self.name, '[ %i mac_seen ]'%len(self.mac_seen) ) ) + #return repr( ( self.mac, self.name, self.mac_seen ) ) + return repr( ( self.mac, self.name, '[ %i mac_seen ]'%len(self.mac_seen) ) ) diff --git a/haircontrol/discovery.py b/haircontrol/discovery.py index 243e3b6..5f63349 100644 --- a/haircontrol/discovery.py +++ b/haircontrol/discovery.py @@ -5,6 +5,7 @@ from haircontrol.inspectors import * class Discovery: def __init__(self): #, inspector): + # XXX Use only one Inspector self.linux_inspector = LinuxInspector() self.ubnt_inspector = UbntInspector() self.net = EtherDomain() @@ -15,16 +16,19 @@ class Discovery: # Learn local interfaces of e_lldp result = self.linux_inspector.command('ip-link') - for (_, ifname, mac) in result: - e_lldp.ifaces[ifname] = Interface(ifname, mac) + for (ifname, mac) in result: + if ifname not in [ 'lo' ]: # XXX configurable filter + e_lldp.add_iface(ifname, mac) # Create equipments and ifaces from LLDP neighbour discovery result = self.linux_inspector.command('lldpctl') for (local_ifname, local_mac, remote_name, remote_ipmgmt, ports) in result: e = Equipment(remote_name, remote_ipmgmt) self.net.add_equipment(e) + # lldp returns logical port, not physicial port for remote bridges for remote_ifname in ports: - e.add_seen_mac(remote_ifname, local_mac) + if remote_ifname not in [ 'br0' ]: # XXX configurable filter + e.add_seen_mac(remote_ifname, local_mac) self.linux_inspector.disconnect() @@ -32,6 +36,7 @@ class Discovery: self.net.add_equipment(e_root) self.linux_inspector.connect(e_root) + # Learn root neighbours result = self.linux_inspector.command('ip-neigh') for (ip, ifname, mac) in result: self.net.index_mac_ip(mac, ip) @@ -39,3 +44,22 @@ class Discovery: self.linux_inspector.disconnect() + # Inspect antennas bridge tables + for ip in self.net.equipments: + if ip.startswith('172.16.1'): # XXX Use neighbours, filter with OUI + e = self.net.equipments[ip] + self.ubnt_inspector.connect(e) + + # Learn local interfaces + result = self.ubnt_inspector.command('status.cgi') + for (ifname, mac) in result: + if ifname not in [ 'lo', 'wifi0', 'br0' ]: # XXX configurable filter + e.add_iface(ifname, mac) + + # Learn bridge tables + result = self.ubnt_inspector.command('brmacs.cgi') + for (ifname, mac) in result: + e.add_seen_mac(ifname, mac) + + self.ubnt_inspector.disconnect() + diff --git a/haircontrol/inspectors.py b/haircontrol/inspectors.py index 33bf360..f59287a 100644 --- a/haircontrol/inspectors.py +++ b/haircontrol/inspectors.py @@ -1,4 +1,5 @@ import re +import json import xml.etree.ElementTree class Inspector(): @@ -68,7 +69,7 @@ class LinuxInspector(Inspector): 'ip-link': { 'cmd': 'ip -o link', 'kind': 'text', - 're': re.compile("(?P\d+):\s+(?P[^:]*):.+\s+(?:link/ether\s+(?P[a-f0-9:]*)\s+brd|link/none)") + 're': re.compile("\d+:\s+(?P[^:]*):.+\s+(?:link/ether\s+(?P[a-f0-9:]*)\s+brd|link/none)") # 1: lo: mtu 65536 qdisc noqueue state UNKNOWN mode DEFAULT group default \ link/loopback 00:00:00:00:00:00 brd 00:00:00:00:00:00 # 2: eth0: mtu 1500 qdisc pfifo_fast state UP mode DEFAULT group default qlen 1000\ link/ether 8c:89:a5:c7:be:88 brd ff:ff:ff:ff:ff:ff }, @@ -103,21 +104,50 @@ class LinuxInspector(Inspector): class UbntInspector(Inspector): + def _parse_cgi_json(self, fd): + for cgi_headers in fd: + if cgi_headers == '\n': + break + js = {} + try: + js = json.load(fd) + except ValueError: + print("Warn : unparsable json") + fd.close() + return js + def parse_status_json(self, fd): - return 'parse_status_json' + result = [] + interfaces = self._parse_cgi_json(fd).get('interfaces') + # XXX dup parse_brmacs_json(fd) and many other cool info to get + if interfaces: + for line in interfaces: + if isinstance(line, dict): + ifname = line.get('ifname') + mac = line.get('hwaddr') + result.append( (ifname, mac) ) + return result def parse_brmacs_json(self, fd): - return 'parse_brmacs_json' + result = [] + brmacs = self._parse_cgi_json(fd).get('brmacs') + if brmacs: + for line in brmacs: + if isinstance(line, dict): + ifname = line.get('port') + mac = line.get('hwaddr') + result.append( (ifname, mac) ) + return result + cmds = { 'status.cgi': { 'cmd':'..', - 'kind': 'cgi-json', 'func': parse_status_json }, 'brmacs.cgi': { 'cmd':'..', - 'kind': 'cgi-json', 'func': parse_brmacs_json } } + -- cgit v1.1