def _parse_slp_packet(packet, peer, rsps, xidmap): parsed = _parse_slp_header(packet) if not parsed: return 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 else: identifier = addr if (identifier, parsed['xid']) in rsps: # avoid obviously duplicate entries parsed = rsps[(identifier, parsed['xid'])] else: rsps[(identifier, parsed['xid'])] = parsed if mac and 'hwaddr' not in parsed: parsed['hwaddr'] = mac if parsed['xid'] in xidmap: parsed['services'] = [xidmap[parsed['xid']]] if 'addresses' in parsed: if peer not in parsed['addresses']: parsed['addresses'].append(peer) else: parsed['addresses'] = [peer] if parsed['function'] == 2: # A service reply _parse_SrvRply(parsed)
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 known_peers.add(addr) else: scanned['protocol'] = protocol handler(scanned)
def snoop(handler, protocol=None): """Watch for SLP activity handler will be called with a dictionary of relevant attributes :param handler: :return: """ tracelog = log.Logger('trace') try: active_scan(handler, protocol) except Exception as e: tracelog.log(traceback.format_exc(), ltype=log.DataTypes.event, event=log.Events.stacktrace) net = socket.socket(socket.AF_INET6, socket.SOCK_DGRAM) net.setsockopt(IPPROTO_IPV6, socket.IPV6_V6ONLY, 1) slpg = socket.inet_pton(socket.AF_INET6, 'ff01::123') slpg2 = socket.inet_pton(socket.AF_INET6, 'ff02::123') for i6idx in util.list_interface_indexes(): mreq = slpg + struct.pack('=I', i6idx) net.setsockopt(IPPROTO_IPV6, socket.IPV6_JOIN_GROUP, mreq) mreq = slpg2 + struct.pack('=I', i6idx) net.setsockopt(IPPROTO_IPV6, socket.IPV6_JOIN_GROUP, mreq) net4 = socket.socket(socket.AF_INET, socket.SOCK_DGRAM) net.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1) net4.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1) for i4 in util.list_ips(): if 'broadcast' not in i4: continue slpmcast = socket.inet_aton('239.255.255.253') + \ socket.inet_aton(i4['addr']) try: net4.setsockopt(socket.IPPROTO_IP, socket.IP_ADD_MEMBERSHIP, slpmcast) except socket.error as e: if e.errno != 98: raise # socket in use can occur when aliased ipv4 are encountered net.bind(('', 427)) net4.bind(('', 427)) while True: try: newmacs = set([]) r, _, _ = select.select((net, net4), (), (), 60) # clear known_peers and peerbymacaddress # to avoid stale info getting in... # rely upon the select(0.2) to catch rapid fire and aggregate ip # addresses that come close together # calling code needs to understand deeper context, as snoop # will now yield dupe info over time known_peers = set([]) peerbymacaddress = {} while r: for s in r: (rsp, peer) = s.recvfrom(9000) 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: continue known_peers.add(peer) mac = neighutil.neightable[ip] if mac in peerbymacaddress: peerbymacaddress[mac]['addresses'].append(peer) else: q = query_srvtypes(peer) if not q or not q[0]: # SLP might have started and not ready yet # ignore for now known_peers.discard(peer) continue # we want to prioritize the very well known services svcs = [] for svc in q: if svc in _slp_services: svcs.insert(0, svc) else: svcs.append(svc) peerbymacaddress[mac] = { 'services': svcs, 'addresses': [peer], } newmacs.add(mac) r, _, _ = select.select((net, net4), (), (), 0.2) for mac in newmacs: peerbymacaddress[mac]['xid'] = 1 _add_attributes(peerbymacaddress[mac]) peerbymacaddress[mac]['hwaddr'] = mac peerbymacaddress[mac]['protocol'] = protocol for srvurl in peerbymacaddress[mac].get('urls', ()): if len(srvurl) > 4: srvurl = srvurl[:-3] if srvurl.endswith('://Athena:'): continue if 'service:ipmi' in peerbymacaddress[mac]['services']: continue if 'service:lightttpd' in peerbymacaddress[mac]['services']: currinf = peerbymacaddress[mac] curratt = currinf.get('attributes', {}) if curratt.get('System-Manufacturing', [None])[0] == 'Lenovo' and curratt.get( 'type', [None])[0] == 'LenovoThinkServer': peerbymacaddress[mac]['services'] = [ 'service:lenovo-tsm' ] else: continue handler(peerbymacaddress[mac]) except Exception as e: tracelog.log(traceback.format_exc(), ltype=log.DataTypes.event, event=log.Events.stacktrace)
def snoop(handler, byehandler=None, protocol=None, uuidlookup=None): """Watch for SSDP notify messages The handler shall be called on any service coming online. byehandler is called whenever a system advertises that it is departing. If no byehandler is specified, byebye messages are ignored. The handler is given (as possible), the mac address, a list of viable sockaddrs to reference the peer, and the notification type (e.g. 'urn:dmtf-org:service:redfish-rest:1' :param handler: A handler for online notifications from network :param byehandler: Optional handler for devices going off the network """ # Normally, I like using v6/v4 agnostic socket. However, since we are # dabbling in multicast wizardry here, such sockets can cause big problems, # so we will have two distinct sockets tracelog = log.Logger('trace') known_peers = set([]) net6 = socket.socket(socket.AF_INET6, socket.SOCK_DGRAM) net6.setsockopt(socket.IPPROTO_IPV6, socket.IPV6_V6ONLY, 1) for ifidx in util.list_interface_indexes(): v6grp = ssdp6mcast + struct.pack('=I', ifidx) net6.setsockopt(socket.IPPROTO_IPV6, socket.IPV6_JOIN_GROUP, v6grp) net6.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1) net4 = socket.socket(socket.AF_INET, socket.SOCK_DGRAM) for i4 in util.list_ips(): ssdp4mcast = socket.inet_pton(socket.AF_INET, mcastv4addr) + \ socket.inet_aton(i4['addr']) try: net4.setsockopt(socket.IPPROTO_IP, socket.IP_ADD_MEMBERSHIP, ssdp4mcast) except socket.error as e: if e.errno != 98: # errno 98 can happen if aliased, skip for now raise net4.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1) net4.bind(('', 1900)) net6.bind(('', 1900)) peerbymacaddress = {} while True: try: newmacs = set([]) machandlers = {} r, _, _ = select.select((net4, net6), (), (), 60) while r: for s in r: (rsp, peer) = s.recvfrom(9000) if rsp[:4] == b'PING': continue rsp = rsp.split(b'\r\n') method, _, _ = rsp[0].split(b' ', 2) if method == b'NOTIFY': 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: continue mac = neighutil.neightable[ip] known_peers.add(peer) newmacs.add(mac) if mac in peerbymacaddress: peerbymacaddress[mac]['addresses'].append(peer) else: peerbymacaddress[mac] = { 'hwaddr': mac, 'addresses': [peer], } peerdata = peerbymacaddress[mac] for headline in rsp[1:]: if not headline: continue headline = util.stringify(headline) header, _, value = headline.partition(':') header = header.strip() value = value.strip() if header == 'NT': peerdata['service'] = value elif header == 'NTS': if value == 'ssdp:byebye': machandlers[mac] = byehandler elif value == 'ssdp:alive': machandlers[mac] = None # handler elif method == b'M-SEARCH': if not uuidlookup: continue #ip = peer[0].partition('%')[0] for headline in rsp[1:]: if not headline: continue headline = util.stringify(headline) headline = headline.partition(':') if len(headline) < 3: continue if headline[0] == 'ST' and headline[-1].startswith( ' urn:xcat.org:service:confluent:'): try: cfm.check_quorum() except Exception: continue for query in headline[-1].split('/'): if query.startswith('uuid='): curruuid = query.split('=', 1)[1].lower() node = uuidlookup(curruuid) if not node: break # Do not bother replying to a node that # we have no deployment activity # planned for cfg = cfm.ConfigManager(None) cfd = cfg.get_node_attributes( node, [ 'deployment.pendingprofile', 'collective.managercandidates' ]) if not cfd.get(node, {}).get( 'deployment.pendingprofile', {}).get('value', None): break candmgrs = cfd.get(node, {}).get( 'collective.managercandidates', {}).get('value', None) if candmgrs: candmgrs = noderange.NodeRange( candmgrs, cfg).nodes if collective.get_myname( ) not in candmgrs: break currtime = time.time() seconds = int(currtime) msecs = int(currtime * 1000 % 1000) reply = 'HTTP/1.1 200 OK\r\nNODENAME: {0}\r\nCURRTIME: {1}\r\nCURRMSECS: {2}\r\n'.format( node, seconds, msecs) if '%' in peer[0]: iface = peer[0].split('%', 1)[1] reply += 'MGTIFACE: {0}\r\n'.format( peer[0].split('%', 1)[1]) ncfg = netutil.get_nic_config( cfg, node, ifidx=iface) if ncfg.get( 'matchesnodename', None): reply += 'DEFAULTNET: 1\r\n' elif not netutil.address_is_local( peer[0]): continue if not isinstance(reply, bytes): reply = reply.encode('utf8') s.sendto(reply, peer) r, _, _ = select.select((net4, net6), (), (), 0.2) for mac in newmacs: thehandler = machandlers.get(mac, None) if thehandler: thehandler(peerbymacaddress[mac]) except Exception: tracelog.log(traceback.format_exc(), ltype=log.DataTypes.event, event=log.Events.stacktrace)
def snoop(handler, byehandler=None): """Watch for SSDP notify messages The handler shall be called on any service coming online. byehandler is called whenever a system advertises that it is departing. If no byehandler is specified, byebye messages are ignored. The handler is given (as possible), the mac address, a list of viable sockaddrs to reference the peer, and the notification type (e.g. 'urn:dmtf-org:service:redfish-rest:1' :param handler: A handler for online notifications from network :param byehandler: Optional handler for devices going off the network """ # Normally, I like using v6/v4 agnostic socket. However, since we are # dabbling in multicast wizardry here, such sockets can cause big problems, # so we will have two distinct sockets known_peers = set([]) net6 = socket.socket(socket.AF_INET6, socket.SOCK_DGRAM) net6.setsockopt(socket.IPPROTO_IPV6, socket.IPV6_V6ONLY, 1) for ifidx in util.list_interface_indexes(): v6grp = ssdp6mcast + struct.pack('=I', ifidx) net6.setsockopt(socket.IPPROTO_IPV6, socket.IPV6_JOIN_GROUP, v6grp) net6.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1) net4 = socket.socket(socket.AF_INET, socket.SOCK_DGRAM) for i4 in util.list_ips(): ssdp4mcast = socket.inet_pton(socket.AF_INET, mcastv4addr) + \ socket.inet_aton(i4['addr']) net4.setsockopt(socket.IPPROTO_IP, socket.IP_ADD_MEMBERSHIP, ssdp4mcast) net4.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1) net4.bind(('', 1900)) net6.bind(('', 1900)) peerbymacaddress = {} while True: newmacs = set([]) machandlers = {} r, _, _ = select.select((net4, net6), (), (), 60) neighutil.update_neigh() while r: for s in r: (rsp, peer) = s.recvfrom(9000) rsp = rsp.split('\r\n') method, _, _ = rsp[0].split(' ', 2) if method == 'NOTIFY': ip = peer[0].partition('%')[0] if ip not in neighutil.neightable: continue if peer in known_peers: continue mac = neighutil.neightable[ip] known_peers.add(peer) newmacs.add(mac) if mac in peerbymacaddress: peerbymacaddress[mac]['peers'].append(peer) else: peerbymacaddress[mac] = { 'hwaddr': mac, 'peers': [peer], } peerdata = peerbymacaddress[mac] for headline in rsp[1:]: if not headline: continue header, _, value = headline.partition(':') header = header.strip() value = value.strip() if header == 'NT': peerdata['service'] = value elif header == 'NTS': if value == 'ssdp:byebye': machandlers[mac] = byehandler elif value == 'ssdp:alive': machandlers[mac] = handler r, _, _ = select.select((net4, net6), (), (), 0.1) for mac in newmacs: thehandler = machandlers.get(mac, None) if thehandler: thehandler(peerbymacaddress[mac])
def snoop(handler): """Watch for SLP activity handler will be called with a dictionary of relevant attributes :param handler: :return: """ active_scan(handler) net = socket.socket(socket.AF_INET6, socket.SOCK_DGRAM) net.setsockopt(IPPROTO_IPV6, socket.IPV6_V6ONLY, 1) slpg = socket.inet_pton(socket.AF_INET6, 'ff01::123') slpg2 = socket.inet_pton(socket.AF_INET6, 'ff02::123') for i6idx in util.list_interface_indexes(): mreq = slpg + struct.pack('=I', i6idx) net.setsockopt(IPPROTO_IPV6, socket.IPV6_JOIN_GROUP, mreq) mreq = slpg2 + struct.pack('=I', i6idx) net.setsockopt(IPPROTO_IPV6, socket.IPV6_JOIN_GROUP, mreq) net4 = socket.socket(socket.AF_INET, socket.SOCK_DGRAM) net.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1) net4.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1) for i4 in util.list_ips(): if 'broadcast' not in i4: continue slpmcast = socket.inet_aton('239.255.255.253') + \ socket.inet_aton(i4['addr']) try: net4.setsockopt(socket.IPPROTO_IP, socket.IP_ADD_MEMBERSHIP, slpmcast) except socket.error as e: if e.errno != 98: raise # socket in use can occur when aliased ipv4 are encountered net.bind(('', 427)) net4.bind(('', 427)) while True: newmacs = set([]) r, _, _ = select.select((net, net4), (), (), 60) # clear known_peers and peerbymacaddress # to avoid stale info getting in... # rely upon the select(0.2) to catch rapid fire and aggregate ip # addresses that come close together # calling code needs to understand deeper context, as snoop # will now yield dupe info over time known_peers = set([]) peerbymacaddress = {} neighutil.update_neigh() while r: for s in r: (rsp, peer) = s.recvfrom(9000) ip = peer[0].partition('%')[0] if ip not in neighutil.neightable: continue if peer in known_peers: continue known_peers.add(peer) mac = neighutil.neightable[ip] if mac in peerbymacaddress: peerbymacaddress[mac]['addresses'].append(peer) else: q = query_srvtypes(peer) if not q or not q[0]: # SLP might have started and not ready yet # ignore for now known_peers.discard(peer) continue # we want to prioritize the very well known services svcs = [] for svc in q: if svc in _slp_services: svcs.insert(0, svc) else: svcs.append(svc) peerbymacaddress[mac] = { 'services': svcs, 'addresses': [peer], } newmacs.add(mac) r, _, _ = select.select((net, net4), (), (), 0.2) for mac in newmacs: peerbymacaddress[mac]['xid'] = 1 _add_attributes(peerbymacaddress[mac]) peerbymacaddress[mac]['hwaddr'] = mac handler(peerbymacaddress[mac])