def _find_srvtype(net, net4, srvtype, addresses, xid): """Internal function to find a single service type Helper to do singleton requests to srvtype :param net: Socket active :param srvtype: Service type to do now :param addresses: Pass through of addresses argument from find_targets :return: """ if addresses is not None: for addr in addresses: for saddr in socket.getaddrinfo(addr, 427): if saddr[0] == socket.AF_INET: net4.sendto(data, saddr[4]) elif saddr[0] == socket.AF_INET6: net.sendto(data, saddr[4]) else: data = _generate_request_payload(srvtype, True, xid) net4.setsockopt(socket.SOL_SOCKET, socket.SO_BROADCAST, 1) v6addrs = [] v6hash = _v6mcasthash(srvtype) # do 'interface local' and 'link local' # it shouldn't make sense, but some configurations work with interface # local that do not work with link local v6addrs.append(('ff01::1:' + v6hash, 427, 0, 0)) v6addrs.append(('ff02::1:' + v6hash, 427, 0, 0)) for idx in util.list_interface_indexes(): # IPv6 multicast is by index, so lead with that net.setsockopt(IPPROTO_IPV6, socket.IPV6_MULTICAST_IF, idx) for sa in v6addrs: try: net.sendto(data, sa) except socket.error: # if we hit an interface without ipv6 multicast, # this can cause an error, skip such an interface # case in point, 'lo' pass for i4 in util.list_ips(): if 'broadcast' not in i4: continue addr = i4['addr'] bcast = i4['broadcast'] net4.setsockopt(socket.IPPROTO_IP, socket.IP_MULTICAST_IF, socket.inet_aton(addr)) try: net4.sendto(data, ('239.255.255.253', 427)) except socket.error as se: # On occasion, multicasting may be disabled # tolerate this scenario and move on if se.errno != 101: raise net4.sendto(data, (bcast, 427))
def _find_service(service, target): net4 = socket.socket(socket.AF_INET, socket.SOCK_DGRAM) net6 = socket.socket(socket.AF_INET6, socket.SOCK_DGRAM) net6.setsockopt(socket.IPPROTO_IPV6, socket.IPV6_V6ONLY, 1) if target: addrs = socket.getaddrinfo(target, 1900, 0, socket.SOCK_DGRAM) for addr in addrs: host = addr[4][0] if addr[0] == socket.AF_INET: net4.sendto(smsg.format(host, service), addr[4]) elif addr[0] == socket.AF_INET6: host = '[{0}]'.format(host) net6.sendto(smsg.format(host, service), addr[4]) else: net4.setsockopt(socket.SOL_SOCKET, socket.SO_BROADCAST, 1) for idx in util.list_interface_indexes(): net6.setsockopt(socket.IPPROTO_IPV6, socket.IPV6_MULTICAST_IF, idx) try: net6.sendto(smsg.format('[{0}]'.format(mcastv6addr), service), (mcastv6addr, 1900, 0, 0)) except socket.error: # ignore interfaces without ipv6 multicast causing error pass for i4 in util.list_ips(): if 'broadcast' not in i4: continue addr = i4['addr'] bcast = i4['broadcast'] net4.setsockopt(socket.IPPROTO_IP, socket.IP_MULTICAST_IF, socket.inet_aton(addr)) net4.sendto(smsg.format(mcastv4addr, service), (mcastv4addr, 1900)) net4.sendto(smsg.format(bcast, service), (bcast, 1900)) # SSDP by spec encourages responses to spread out over a 3 second interval # hence we must be a bit more patient deadline = util.monotonic_time() + 4 r, _, _ = select.select((net4, net6), (), (), 4) peerdata = {} 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: timeout = 0 r, _, _ = select.select((net4, net6), (), (), timeout) for nid in peerdata: yield peerdata[nid]
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 _find_service(service, target): net4 = socket.socket(socket.AF_INET, socket.SOCK_DGRAM) net6 = socket.socket(socket.AF_INET6, socket.SOCK_DGRAM) net6.setsockopt(socket.IPPROTO_IPV6, socket.IPV6_V6ONLY, 1) if target: addrs = socket.getaddrinfo(target, 1900, 0, socket.SOCK_DGRAM) for addr in addrs: host = addr[4][0] if addr[0] == socket.AF_INET: msg = smsg.format(host, service) if not isinstance(msg, bytes): msg = msg.encode('utf8') net4.sendto(msg, addr[4]) elif addr[0] == socket.AF_INET6: host = '[{0}]'.format(host) msg = smsg.format(host, service) if not isinstance(msg, bytes): msg = msg.encode('utf8') net6.sendto(msg, addr[4]) else: net4.setsockopt(socket.SOL_SOCKET, socket.SO_BROADCAST, 1) for idx in util.list_interface_indexes(): net6.setsockopt(socket.IPPROTO_IPV6, socket.IPV6_MULTICAST_IF, idx) try: msg = smsg.format('[{0}]'.format(mcastv6addr), service) if not isinstance(msg, bytes): msg = msg.encode('utf8') net6.sendto(msg, (mcastv6addr, 1900, 0, 0)) except socket.error: # ignore interfaces without ipv6 multicast causing error pass for i4 in util.list_ips(): if 'broadcast' not in i4: continue addr = i4['addr'] bcast = i4['broadcast'] net4.setsockopt(socket.IPPROTO_IP, socket.IP_MULTICAST_IF, socket.inet_aton(addr)) msg = smsg.format(mcastv4addr, service) if not isinstance(msg, bytes): msg = msg.encode('utf8') net4.sendto(msg, (mcastv4addr, 1900)) msg = smsg.format(bcast, service) if not isinstance(msg, bytes): msg = msg.encode('utf8') net4.sendto(msg, (bcast, 1900)) # SSDP by spec encourages responses to spread out over a 3 second interval # hence we must be a bit more patient deadline = util.monotonic_time() + 4 r, _, _ = select.select((net4, net6), (), (), 4) peerdata = {} 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: timeout = 0 r, _, _ = select.select((net4, net6), (), (), timeout) querypool = gp.GreenPool() pooltargs = [] for nid in peerdata: for url in peerdata[nid].get('urls', ()): if url.endswith('/desc.tmpl'): pooltargs.append((url, peerdata[nid])) for pi in querypool.imap(check_cpstorage, pooltargs): if pi is not None: yield pi
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])