def addresses_match(addr1, addr2): """Check two network addresses for similarity Is it zero padded in one place, not zero padded in another? Is one place by name and another by IP?? Is one context getting a normal IPv4 address and another getting IPv4 in IPv6 notation? This function examines the two given names, performing the required changes to compare them for equivalency :param addr1: :param addr2: :return: True if the given addresses refer to the same thing """ for addrinfo in socket.getaddrinfo(addr1, 0, 0, socket.SOCK_STREAM): rootaddr1 = socket.inet_pton(addrinfo[0], addrinfo[4][0]) if addrinfo[ 0] == socket.AF_INET6 and rootaddr1[: 12] == b'\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\xff\xff': # normalize to standard IPv4 rootaddr1 = rootaddr1[-4:] for otherinfo in socket.getaddrinfo(addr2, 0, 0, socket.SOCK_STREAM): otheraddr = socket.inet_pton(otherinfo[0], otherinfo[4][0]) if otherinfo[ 0] == socket.AF_INET6 and otheraddr[: 12] == b'\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\xff\xff': otheraddr = otheraddr[-4:] if otheraddr == rootaddr1: return True return False
def mask_to_cidr(mask): maskn = socket.inet_pton(socket.AF_INET, mask) maskn = struct.unpack('!I', maskn)[0] cidr = 32 while maskn & 0b1 == 0 and cidr > 0: cidr -= 1 maskn >>= 1 return cidr
def rsync_ip(ip): """ Transform ip string to an rsync-compatible form Will return ipv4 addresses unchanged, but will nest ipv6 addresses inside square brackets. :param ip: an ip string (ipv4 or ipv6) :returns: a string ip address """ try: socket.inet_pton(socket.AF_INET6, ip) except socket.error: # it's IPv4 return ip else: return '[%s]' % ip
def rsync_ip(ip): """ Transform ip string to an rsync-compatible form Will return ipv4 addresses unchanged, but will nest ipv6 addresses inside square brackets. :param ip: an ip string (ipv4 or ipv6) :returns: a string ip address """ try: socket.inet_pton(socket.AF_INET6, ip) except socket.error: # it's IPv4 return ip else: return '[%s]' % ip
def ip_on_same_subnet(first, second, prefix): addrinf = socket.getaddrinfo(first, None, 0, socket.SOCK_STREAM)[0] fam = addrinf[0] ip = socket.inet_pton(fam, addrinf[-1][0]) ip = int(codecs.encode(bytes(ip), 'hex'), 16) addrinf = socket.getaddrinfo(second, None, 0, socket.SOCK_STREAM)[0] if fam != addrinf[0]: return False oip = socket.inet_pton(fam, addrinf[-1][0]) oip = int(codecs.encode(bytes(oip), 'hex'), 16) if fam == socket.AF_INET: addrlen = 32 elif fam == socket.AF_INET6: addrlen = 128 else: raise Exception("Unknown address family {0}".format(fam)) mask = 2**prefix - 1 << (addrlen - prefix) return ip & mask == oip & mask
def ip_on_same_subnet(first, second, prefix): if first.startswith('::ffff:') and '.' in first: first = first.replace('::ffff:', '') if second.startswith('::ffff:') and '.' in second: second = second.replace('::ffff:', '') addrinf = socket.getaddrinfo(first, None, 0, socket.SOCK_STREAM)[0] fam = addrinf[0] ip = socket.inet_pton(fam, addrinf[-1][0]) ip = int(codecs.encode(bytes(ip), 'hex'), 16) addrinf = socket.getaddrinfo(second, None, 0, socket.SOCK_STREAM)[0] if fam != addrinf[0]: return False txtaddr = addrinf[-1][0].split('%')[0] oip = socket.inet_pton(fam, txtaddr) oip = int(codecs.encode(bytes(oip), 'hex'), 16) if fam == socket.AF_INET: addrlen = 32 elif fam == socket.AF_INET6: addrlen = 128 else: raise Exception("Unknown address family {0}".format(fam)) mask = 2**prefix - 1 << (addrlen - prefix) return ip & mask == oip & mask
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)
import confluent.netutil as netutil import eventlet.green.select as select import eventlet.green.socket as socket import eventlet.greenpool as gp import time try: from eventlet.green.urllib.request import urlopen except (ImportError, AssertionError): from eventlet.green.urllib2 import urlopen import struct import traceback mcastv4addr = '239.255.255.250' mcastv6addr = 'ff02::c' ssdp6mcast = socket.inet_pton(socket.AF_INET6, mcastv6addr) smsg = ('M-SEARCH * HTTP/1.1\r\n' 'HOST: {0}:1900\r\n' 'MAN: "ssdp:discover"\r\n' 'ST: {1}\r\n' 'MX: 3\r\n\r\n') 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: continue if addr in known_peers:
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])