From 4f4d02d68f960868cca8557c6953f8d1eea49415 Mon Sep 17 00:00:00 2001 From: Jarrod Johnson Date: Tue, 24 Aug 2021 16:19:41 -0400 Subject: [PATCH] Refactor neighutil Stick to bytes in the expensive part, only convert to and from presentation form on lookup need. Convert all external calls to a unified lookup logic. --- .../confluent/discovery/protocols/slp.py | 32 ++++++--------- .../confluent/discovery/protocols/ssdp.py | 24 +++++------ confluent_server/confluent/neighutil.py | 40 +++++++++++++------ 3 files changed, 50 insertions(+), 46 deletions(-) diff --git a/confluent_server/confluent/discovery/protocols/slp.py b/confluent_server/confluent/discovery/protocols/slp.py index 8f95d9c3..1fc92558 100644 --- a/confluent_server/confluent/discovery/protocols/slp.py +++ b/confluent_server/confluent/discovery/protocols/slp.py @@ -106,12 +106,9 @@ def _parse_slp_packet(packet, peer, rsps, xidmap): addr = peer[0] if '%' in addr: addr = addr[:addr.index('%')] - mac = None - if addr not in neighutil.neightable: - neighutil.update_neigh() - if addr in neighutil.neightable: - identifier = neighutil.neightable[addr] - mac = identifier + mac = neighutil.get_hwaddr(addr) + if mac: + identifier = mac else: identifier = addr if (identifier, parsed['xid']) in rsps: @@ -254,7 +251,6 @@ def _grab_rsps(socks, rsps, interval, xidmap): while r: for s in r: (rsp, peer) = s.recvfrom(9000) - neighutil.refresh_neigh() _parse_slp_packet(rsp, peer, rsps, xidmap) res = select.select(socks, (), (), interval) if not res: @@ -415,11 +411,12 @@ def rescan(handler): known_peers = set([]) for scanned in scan(): for addr in scanned['addresses']: - ip = addr[0].partition('%')[0] # discard scope if present - if ip not in neighutil.neightable: - continue if addr in known_peers: break + ip = addr[0].partition('%')[0] # discard scope if present + macaddr = neighutil.get_hwaddr(ip) + if not macaddr: + continue known_peers.add(addr) else: handler(scanned) @@ -484,12 +481,10 @@ def snoop(handler, protocol=None): ip = peer[0].partition('%')[0] if peer in known_peers: continue - if ip not in neighutil.neightable: - neighutil.update_neigh() - if ip not in neighutil.neightable: + mac = neighutil.get_hwaddr(ip) + if not mac: continue known_peers.add(peer) - mac = neighutil.neightable[ip] if mac in peerbymacaddress: peerbymacaddress[mac]['addresses'].append(peer) else: @@ -541,13 +536,12 @@ def active_scan(handler, protocol=None): known_peers = set([]) for scanned in scan(): for addr in scanned['addresses']: - ip = addr[0].partition('%')[0] # discard scope if present - if ip not in neighutil.neightable: - neighutil.update_neigh() - if ip not in neighutil.neightable: - continue if addr in known_peers: break + ip = addr[0].partition('%')[0] # discard scope if present + macaddr = neighutil.get_hwaddr(ip) + if not macaddr: + continue known_peers.add(addr) else: scanned['protocol'] = protocol diff --git a/confluent_server/confluent/discovery/protocols/ssdp.py b/confluent_server/confluent/discovery/protocols/ssdp.py index 43a48936..b257e75a 100644 --- a/confluent_server/confluent/discovery/protocols/ssdp.py +++ b/confluent_server/confluent/discovery/protocols/ssdp.py @@ -61,13 +61,12 @@ def active_scan(handler, protocol=None): known_peers = set([]) for scanned in scan(['urn:dmtf-org:service:redfish-rest:1']): for addr in scanned['addresses']: - ip = addr[0].partition('%')[0] # discard scope if present - if ip not in neighutil.neightable: - neighutil.update_neigh() - if ip not in neighutil.neightable: - continue if addr in known_peers: break + ip = addr[0].partition('%')[0] # discard scope if present + hwaddr = neighutil.get_hwaddr(ip) + if not hwaddr: + continue known_peers.add(addr) else: scanned['protocol'] = protocol @@ -134,11 +133,9 @@ def snoop(handler, byehandler=None, protocol=None, uuidlookup=None): ip = peer[0].partition('%')[0] if peer in known_peers: continue - if ip not in neighutil.neightable: - neighutil.update_neigh() - if ip not in neighutil.neightable: + mac = neighutil.get_hwaddr(ip) + if not mac: continue - mac = neighutil.neightable[ip] known_peers.add(peer) newmacs.add(mac) if mac in peerbymacaddress: @@ -284,7 +281,6 @@ def _find_service(service, target): while r: for s in r: (rsp, peer) = s.recvfrom(9000) - neighutil.refresh_neigh() _parse_ssdp(peer, rsp, peerdata) timeout = deadline - util.monotonic_time() if timeout < 0: @@ -316,11 +312,9 @@ def _parse_ssdp(peer, rsp, peerdata): ip = peer[0].partition('%')[0] nid = ip mac = None - if ip not in neighutil.neightable: - neighutil.update_neigh() - if ip in neighutil.neightable: - nid = neighutil.neightable[ip] - mac = nid + mac = neighutil.get_hwaddr(ip) + if mac: + nid = mac headlines = rsp.split(b'\r\n') try: _, code, _ = headlines[0].split(b' ', 2) diff --git a/confluent_server/confluent/neighutil.py b/confluent_server/confluent/neighutil.py index 9b9dc7c6..93291ac5 100644 --- a/confluent_server/confluent/neighutil.py +++ b/confluent_server/confluent/neighutil.py @@ -20,6 +20,7 @@ import confluent.util as util import os +import eventlet.semaphore as semaphore import eventlet.green.socket as socket import struct @@ -28,10 +29,9 @@ neightime = 0 import re -_validmac = re.compile('..:..:..:..:..:..') +neighlock = semaphore.Semaphore() - -def update_neigh(): +def _update_neigh(): global neightable global neightime neightime = os.times()[4] @@ -41,6 +41,7 @@ def update_neigh(): nlhdr = b'\x1c\x00\x00\x00\x1e\x00\x01\x03\x00\x00\x00\x00\x00\x00\x00\x00' ndmsg= b'\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00' s.sendall(nlhdr + ndmsg) + neightable = {} while True: pdata = s.recv(65536) v = memoryview(pdata) @@ -50,7 +51,7 @@ def update_neigh(): length, typ = struct.unpack('IH', v[:6]) if typ == 28: hlen = struct.calcsize('BIHBB') - fam, idx, state, flags, typ = struct.unpack('BIHBB', v[16:16+hlen]) + _, idx, state, flags, typ = struct.unpack('BIHBB', v[16:16+hlen]) if typ == 1: # only handle unicast entries curraddr = None currip = None @@ -58,10 +59,11 @@ def update_neigh(): while len(rta): rtalen, rtatyp = struct.unpack('HH', rta[:4]) if rtatyp == 2: # hwaddr - hwlen = rtalen - 4 - curraddr = ':'.join(['{:02x}'.format(x) for x in bytearray(rta[4:rtalen].tobytes())]) + curraddr = rta[4:rtalen].tobytes() # ':'.join(['{:02x}'.format(x) for x in bytearray(rta[4:rtalen].tobytes())]) + if len(curraddr) == 20: + curraddr = curraddr[12:] elif rtatyp == 1: # ip address - currip = socket.inet_ntop(fam, rta[4:rtalen].tobytes()) + currip = rta[4:rtalen].tobytes() # socket.inet_ntop(fam, rta[4:rtalen].tobytes()) rta = rta[rtalen:] if not rtalen: if curraddr and currip: @@ -70,9 +72,23 @@ def update_neigh(): v = v[length:] -def refresh_neigh(): - global neightime +def get_hwaddr(ipaddr): + hwaddr = None if os.name == 'nt': - return - if os.times()[4] > (neightime + 30): - update_neigh() + return hwaddr + if ':' in ipaddr: + ipaddr = socket.inet_pton(socket.AF_INET6, ipaddr) + elif '.' in ipaddr: + ipaddr = socket.inet_pton(socket.AF_INET, ipaddr) + with neighlock: + updated = False + if os.times()[4] > (neightime + 30): + _update_neigh() + updated = True + hwaddr = neightable.get(ipaddr, None) + if not hwaddr and not updated: + _update_neigh() + hwaddr = neightable.get(ipaddr, None) + if hwaddr: + hwaddr = ':'.join(['{:02x}'.format(x) for x in bytearray(hwaddr)]) + return hwaddr