mirror of
				https://github.com/xcat2/confluent.git
				synced 2025-10-30 19:02:29 +00:00 
			
		
		
		
	Begin work on ipv6 netboot support
This commit is contained in:
		| @@ -27,6 +27,7 @@ import confluent.collective.manager as collective | ||||
| import confluent.noderange as noderange | ||||
| import confluent.log as log | ||||
| import confluent.netutil as netutil | ||||
| import confluent.util as util | ||||
| import ctypes | ||||
| import ctypes.util | ||||
| import eventlet | ||||
| @@ -45,6 +46,9 @@ udphdr = b'\x00\x43\x00\x44\x00\x00\x00\x00' | ||||
| ignoremacs = {} | ||||
| ignoredisco = {} | ||||
|  | ||||
| mcastv6addr = 'ff02::1:2' | ||||
|  | ||||
|  | ||||
| def _ipsum(data): | ||||
|     currsum = 0 | ||||
|     if len(data) % 2: | ||||
| @@ -189,6 +193,26 @@ def _decode_ocp_vivso(rq, idx, size): | ||||
|         idx += rq[idx + 1] + 2 | ||||
|     return '', None, vivso | ||||
|  | ||||
| def v6opts_to_dict(rq): | ||||
|     optidx = 0 | ||||
|     reqdict = {} | ||||
|     disco = {'uuid':None, 'arch': None, 'vivso': None} | ||||
|     try: | ||||
|         while optidx < len(rq): | ||||
|             optnum, optlen = struct.unpack('!HH', rq[optidx:optidx+4]) | ||||
|             reqdict[optnum] = rq[optidx + 4:optidx + 4 + optlen] | ||||
|             optidx += optlen + 4 | ||||
|     except IndexError: | ||||
|         pass | ||||
|     if 1 in reqdict: | ||||
|         duid = reqdict[1] | ||||
|         if struct.unpack('!H', duid[:2])[0] == 4: | ||||
|             disco['uuid'] = decode_uuid(duid[2:]) | ||||
|     if 61 in reqdict: | ||||
|         arch = bytes(rq[optidx+4:optidx+4+optlen]) | ||||
|         disco['arch'] = pxearchs.get(bytes(reqdict[61]), None) | ||||
|     return reqdict, disco | ||||
|  | ||||
| def opts_to_dict(rq, optidx, expectype=1): | ||||
|     reqdict = {} | ||||
|     disco = {'uuid':None, 'arch': None, 'vivso': None} | ||||
| @@ -317,95 +341,117 @@ def snoop(handler, protocol=None, nodeguess=None): | ||||
|     net4.setsockopt(socket.SOL_SOCKET, socket.SO_BROADCAST, 1) | ||||
|     net4.setsockopt(socket.IPPROTO_IP, IP_PKTINFO, 1) | ||||
|     net4.bind(('', 67)) | ||||
|     v6addr = socket.inet_pton(socket.AF_INET6, mcastv6addr) | ||||
|     net6 = socket.socket(socket.AF_INET6, socket.SOCK_DGRAM) | ||||
|     net6.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1) | ||||
|     for ifidx in util.list_interface_indexes(): | ||||
|         v6grp = v6addr + struct.pack('=I', ifidx) | ||||
|         net6.setsockopt(socket.IPPROTO_IPV6, socket.IPV6_JOIN_GROUP, v6grp) | ||||
|     net6.bind(('', 547)) | ||||
|     clientaddr = sockaddr_in() | ||||
|     rawbuffer = bytearray(2048) | ||||
|     data = pkttype.from_buffer(rawbuffer) | ||||
|     msg = msghdr() | ||||
|     cmsgarr = bytearray(cmsgsize) | ||||
|     cmsg = cmsgtype.from_buffer(cmsgarr) | ||||
|     iov = iovec() | ||||
|     iov.iov_base = ctypes.addressof(data) | ||||
|     iov.iov_len = 2048 | ||||
|     msg.msg_iov = ctypes.pointer(iov) | ||||
|     msg.msg_iovlen = 1 | ||||
|     msg.msg_control = ctypes.addressof(cmsg) | ||||
|     msg.msg_controllen = ctypes.sizeof(cmsg) | ||||
|     msg.msg_name = ctypes.addressof(clientaddr) | ||||
|     msg.msg_namelen = ctypes.sizeof(clientaddr) | ||||
|     # We'll leave name and namelen blank for now | ||||
|     while True: | ||||
|         try: | ||||
|             # Just need some delay, picked a prime number so that overlap with other | ||||
|             # timers might be reduced, though it really is probably nothing | ||||
|             ready = select.select([net4], [], [], None) | ||||
|             ready = select.select([net4, net6], [], [], None) | ||||
|             if not ready or not ready[0]: | ||||
|                 continue | ||||
|             clientaddr = sockaddr_in() | ||||
|             rawbuffer = bytearray(2048) | ||||
|             data = pkttype.from_buffer(rawbuffer) | ||||
|             msg = msghdr() | ||||
|             cmsgarr = bytearray(cmsgsize) | ||||
|             cmsg = cmsgtype.from_buffer(cmsgarr) | ||||
|             iov = iovec() | ||||
|             iov.iov_base = ctypes.addressof(data) | ||||
|             iov.iov_len = 2048 | ||||
|             msg.msg_iov = ctypes.pointer(iov) | ||||
|             msg.msg_iovlen = 1 | ||||
|             msg.msg_control = ctypes.addressof(cmsg) | ||||
|             msg.msg_controllen = ctypes.sizeof(cmsg) | ||||
|             msg.msg_name = ctypes.addressof(clientaddr) | ||||
|             msg.msg_namelen = ctypes.sizeof(clientaddr) | ||||
|             # We'll leave name and namelen blank for now | ||||
|             i = recvmsg(net4.fileno(), ctypes.pointer(msg), 0) | ||||
|             # if we have a small packet, just skip, it can't possible hold enough | ||||
|             # data and avoids some downstream IndexErrors that would be messy | ||||
|             # with try/except | ||||
|             if i < 64: | ||||
|                 continue | ||||
|             #peer = ipfromint(clientaddr.sin_addr.s_addr) | ||||
|             # We don't need peer yet, generally it's 0.0.0.0 | ||||
|             _, level, typ = struct.unpack('QII', cmsgarr[:16]) | ||||
|             if level == socket.IPPROTO_IP and typ == IP_PKTINFO: | ||||
|                 idx, recv, targ = struct.unpack('III', cmsgarr[16:28]) | ||||
|                 recv = ipfromint(recv) | ||||
|                 targ = ipfromint(targ) | ||||
|             # peer is the source ip (in dhcpdiscover, 0.0.0.0) | ||||
|             # recv is the 'ip' that recevied the packet, regardless of target | ||||
|             # targ is the ip in the destination ip of the header. | ||||
|             # idx is the ip link number of the receiving nic | ||||
|             # For example, a DHCPDISCOVER will probably have: | ||||
|             # peer of 0.0.0.0 | ||||
|             # targ of 255.255.255.255 | ||||
|             # recv of <actual ip address that could reply> | ||||
|             # idx correlated to the nic | ||||
|             rqv = memoryview(rawbuffer) | ||||
|             rq = bytearray(rqv[:i]) | ||||
|             if rq[0] == 1:  # Boot request | ||||
|                 addrlen = rq[2] | ||||
|                 if addrlen > 16 or addrlen == 0: | ||||
|                     continue | ||||
|                 rawnetaddr = rq[28:28+addrlen] | ||||
|                 netaddr = ':'.join(['{0:02x}'.format(x) for x in rawnetaddr]) | ||||
|                 optidx = 0 | ||||
|                 try: | ||||
|                     optidx = rq.index(b'\x63\x82\x53\x63') + 4 | ||||
|                 except ValueError: | ||||
|                     continue | ||||
|                 txid = rq[4:8] # struct.unpack('!I', rq[4:8])[0] | ||||
|                 rqinfo, disco = opts_to_dict(rq, optidx) | ||||
|                 vivso = disco.get('vivso', None) | ||||
|                 if vivso: | ||||
|                     # info['modelnumber'] = info['attributes']['enclosure-machinetype-model'][0] | ||||
|                     info = {'hwaddr': netaddr, 'uuid': disco['uuid'], | ||||
|                             'architecture': vivso.get('arch', ''), | ||||
|                             'services': (vivso['service-type'],), | ||||
|                             'netinfo': {'ifidx': idx, 'recvip': recv, 'txid': txid}, | ||||
|                             'attributes': {'enclosure-machinetype-model': [vivso.get('machine', '')]}} | ||||
|                     if time.time() > ignoredisco.get(netaddr, 0) + 90: | ||||
|                         ignoredisco[netaddr] = time.time() | ||||
|                         handler(info) | ||||
|                     #consider_discover(info, rqinfo, net4, cfg, rqv) | ||||
|                     continue | ||||
|                 # We will fill out service to have something to byte into, | ||||
|                 # but the nature of the beast is that we do not have peers, | ||||
|                 # so that will not be present for a pxe snoop | ||||
|                 info = {'hwaddr': netaddr, 'uuid': disco['uuid'], | ||||
|                         'architecture': disco['arch'], | ||||
|                         'netinfo': {'ifidx': idx, 'recvip': recv, 'txid': txid}, | ||||
|                         'services': ('pxe-client',)} | ||||
|                 if (disco['uuid'] | ||||
|                         and time.time() > ignoredisco.get(netaddr, 0) + 90): | ||||
|                     ignoredisco[netaddr] = time.time() | ||||
|                     handler(info) | ||||
|                 consider_discover(info, rqinfo, net4, cfg, rqv, nodeguess) | ||||
|             for netc in ready[0]: | ||||
|                 idx = None | ||||
|                 if netc == net4: | ||||
|                     i = recvmsg(netc.fileno(), ctypes.pointer(msg), 0) | ||||
|                     # if we have a small packet, just skip, it can't possible hold enough | ||||
|                     # data and avoids some downstream IndexErrors that would be messy | ||||
|                     # with try/except | ||||
|                     if i < 64: | ||||
|                         continue | ||||
|                     _, level, typ = struct.unpack('QII', cmsgarr[:16]) | ||||
|                     if level == socket.IPPROTO_IP and typ == IP_PKTINFO: | ||||
|                         idx, recv = struct.unpack('II', cmsgarr[16:24]) | ||||
|                         recv = ipfromint(recv) | ||||
|                     rqv = memoryview(rawbuffer) | ||||
|                     if rawbuffer[0] == 1:  # Boot request | ||||
|                         process_dhcp4req(handler, nodeguess, cfg, net4, idx, recv, rqv) | ||||
|                 elif netc == net6: | ||||
|                     recv = 'ff02::1:2' | ||||
|                     pkt, addr = netc.recvfrom(2048) | ||||
|                     idx = addr[-1] | ||||
|                     i = len(pkt) | ||||
|                     if i < 64: | ||||
|                         continue | ||||
|                     rqv = memoryview(pkt) | ||||
|                     rq = bytearray(rqv[:2]) | ||||
|                     if rq[0] == 1: # dhcpv6 solicit | ||||
|                         process_dhcp6req(rqv, addr[0]) | ||||
|  | ||||
|         except Exception as e: | ||||
|             tracelog.log(traceback.format_exc(), ltype=log.DataTypes.event, | ||||
|                             event=log.Events.stacktrace) | ||||
|  | ||||
| def process_dhcp6req(rqv, addr): | ||||
|     txid = rqv[1:4] | ||||
|     req, disco = v6opts_to_dict(bytearray(rqv[4:])) | ||||
|     if 'uuid' not in disco: | ||||
|         return | ||||
|     print(addr) | ||||
|     print(repr(disco)) | ||||
|  | ||||
|  | ||||
| def process_dhcp4req(handler, nodeguess, cfg, net4, idx, recv, rqv): | ||||
|     rq = bytearray(rqv) | ||||
|     addrlen = rq[2] | ||||
|     if addrlen > 16 or addrlen == 0: | ||||
|         return | ||||
|     rawnetaddr = rq[28:28+addrlen] | ||||
|     netaddr = ':'.join(['{0:02x}'.format(x) for x in rawnetaddr]) | ||||
|     optidx = 0 | ||||
|     try: | ||||
|         optidx = rq.index(b'\x63\x82\x53\x63') + 4 | ||||
|     except ValueError: | ||||
|         return | ||||
|     txid = rq[4:8] # struct.unpack('!I', rq[4:8])[0] | ||||
|     rqinfo, disco = opts_to_dict(rq, optidx) | ||||
|     vivso = disco.get('vivso', None) | ||||
|     if vivso: | ||||
|                         # info['modelnumber'] = info['attributes']['enclosure-machinetype-model'][0] | ||||
|         info = {'hwaddr': netaddr, 'uuid': disco['uuid'], | ||||
|                                 'architecture': vivso.get('arch', ''), | ||||
|                                 'services': (vivso['service-type'],), | ||||
|                                 'netinfo': {'ifidx': idx, 'recvip': recv, 'txid': txid}, | ||||
|                                 'attributes': {'enclosure-machinetype-model': [vivso.get('machine', '')]}} | ||||
|         if time.time() > ignoredisco.get(netaddr, 0) + 90: | ||||
|             ignoredisco[netaddr] = time.time() | ||||
|             handler(info) | ||||
|                         #consider_discover(info, rqinfo, net4, cfg, rqv) | ||||
|         return | ||||
|                     # We will fill out service to have something to byte into, | ||||
|                     # but the nature of the beast is that we do not have peers, | ||||
|                     # so that will not be present for a pxe snoop | ||||
|     info = {'hwaddr': netaddr, 'uuid': disco['uuid'], | ||||
|                             'architecture': disco['arch'], | ||||
|                             'netinfo': {'ifidx': idx, 'recvip': recv, 'txid': txid}, | ||||
|                             'services': ('pxe-client',)} | ||||
|     if (disco['uuid'] | ||||
|                             and time.time() > ignoredisco.get(netaddr, 0) + 90): | ||||
|         ignoredisco[netaddr] = time.time() | ||||
|         handler(info) | ||||
|     consider_discover(info, rqinfo, net4, cfg, rqv, nodeguess) | ||||
|  | ||||
|  | ||||
|  | ||||
| def clear_nodes(nodes): | ||||
|   | ||||
		Reference in New Issue
	
	Block a user