mirror of
https://github.com/xcat2/confluent.git
synced 2024-11-21 17:11:58 +00:00
Draft example of an alternative of setting filename in initial pass
In at least one ARM UEFI, the PXE support is non-compliant, and absolutely requires the filename field to be populated. That said, the same firmware fails to let iPXE function, so we have other issues with this firmware. Very preliminary testing indicates that PXE compliant firmware ignores the filename in the offer and proceeds to proxyDHCP. The non-compliant firmware ignores the PXEClient guidance and just consumes the filename. In theory, this just means that the compliant firmware inflicts a bit of redundant effort on our part, as we must sort out the filename to offer twice (once for DHCP, then again for proxyDHCP). It may aid non compliant firmware to proceed in more limited circumstances.
This commit is contained in:
parent
ec023831a5
commit
ce79c4cf11
@ -273,6 +273,29 @@ def opts_to_dict(rq, optidx, expectype=1):
|
||||
def ipfromint(numb):
|
||||
return socket.inet_ntoa(struct.pack('I', numb))
|
||||
|
||||
def get_pxe_bootfile(node, opts, disco, profile, cfg, myip):
|
||||
if opts.get(77, None) == b'iPXE':
|
||||
if not profile:
|
||||
profile = get_deployment_profile(node, cfg)
|
||||
if not profile:
|
||||
log.log({'info': 'No pending profile for {0}, skipping proxyDHCP reply'.format(node)})
|
||||
return None
|
||||
bootfile = 'http://{0}/confluent-public/os/{1}/boot.ipxe'.format(myip, profile).encode('utf8')
|
||||
elif disco['arch'] == 'uefi-x64':
|
||||
bootfile = b'confluent/x86_64/ipxe.efi'
|
||||
elif disco['arch'] == 'bios-x86':
|
||||
bootfile = b'confluent/x86_64/ipxe.kkpxe'
|
||||
elif disco['arch'] == 'uefi-aarch64':
|
||||
bootfile = b'confluent/aarch64/ipxe.efi'
|
||||
if len(bootfile) > 127:
|
||||
log.log(
|
||||
{'info': 'Boot offer cannot be made to {0} as the '
|
||||
'profile name "{1}" is {2} characters longer than is supported '
|
||||
'for this boot method.'.format(
|
||||
node, profile, len(bootfile) - 127)})
|
||||
return None
|
||||
return bootfile
|
||||
|
||||
def proxydhcp(handler, nodeguess):
|
||||
net4011 = socket.socket(socket.AF_INET, socket.SOCK_DGRAM)
|
||||
net4011.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1)
|
||||
@ -346,6 +369,7 @@ def proxydhcp(handler, nodeguess):
|
||||
profile = None
|
||||
if not myipn:
|
||||
myipn = socket.inet_aton(recv)
|
||||
myip = socket.inet_ntoa(myipn)
|
||||
profile = get_deployment_profile(node, cfg)
|
||||
if profile:
|
||||
log.log({
|
||||
@ -354,26 +378,9 @@ def proxydhcp(handler, nodeguess):
|
||||
if not skiplogging:
|
||||
log.log({'info': 'No pending profile for {0}, skipping proxyDHCP reply'.format(node)})
|
||||
continue
|
||||
if opts.get(77, None) == b'iPXE':
|
||||
if not profile:
|
||||
profile = get_deployment_profile(node, cfg)
|
||||
if not profile:
|
||||
log.log({'info': 'No pending profile for {0}, skipping proxyDHCP reply'.format(node)})
|
||||
continue
|
||||
myip = socket.inet_ntoa(myipn)
|
||||
bootfile = 'http://{0}/confluent-public/os/{1}/boot.ipxe'.format(myip, profile).encode('utf8')
|
||||
elif disco['arch'] == 'uefi-x64':
|
||||
bootfile = b'confluent/x86_64/ipxe.efi'
|
||||
elif disco['arch'] == 'bios-x86':
|
||||
bootfile = b'confluent/x86_64/ipxe.kkpxe'
|
||||
elif disco['arch'] == 'uefi-aarch64':
|
||||
bootfile = b'confluent/aarch64/ipxe.efi'
|
||||
if len(bootfile) > 127:
|
||||
log.log(
|
||||
{'info': 'Boot offer cannot be made to {0} as the '
|
||||
'profile name "{1}" is {2} characters longer than is supported '
|
||||
'for this boot method.'.format(
|
||||
node, profile, len(bootfile) - 127)})
|
||||
myip = socket.inet_ntoa(myipn)
|
||||
bootfile = get_pxe_bootfile(node, opts, disco, profile, cfg, myip)
|
||||
if not bootfile:
|
||||
continue
|
||||
rpv[:240] = rqv[:240].tobytes()
|
||||
rpv[0:1] = b'\x02'
|
||||
@ -398,6 +405,7 @@ def snoop(handler, protocol=None, nodeguess=None):
|
||||
#prominent
|
||||
#TODO(jjohnson2): enable unicast replies. This would suggest either
|
||||
# injection into the neigh table before OFFER or using SOCK_RAW.
|
||||
global tracelog
|
||||
start_proxydhcp(handler, nodeguess)
|
||||
tracelog = log.Logger('trace')
|
||||
global attribwatcher
|
||||
@ -499,7 +507,7 @@ def process_dhcp6req(handler, rqv, addr, net, cfg, nodeguess):
|
||||
if ignoredisco.get(mac, 0) + 90 < time.time():
|
||||
ignoredisco[mac] = time.time()
|
||||
handler(info)
|
||||
consider_discover(info, req, net, cfg, None, nodeguess, addr)
|
||||
consider_discover(info, req, net, cfg, None, nodeguess, disco, addr)
|
||||
|
||||
def process_dhcp4req(handler, nodeguess, cfg, net4, idx, recv, rqv):
|
||||
rq = bytearray(rqv)
|
||||
@ -539,7 +547,7 @@ def process_dhcp4req(handler, nodeguess, cfg, net4, idx, recv, rqv):
|
||||
and time.time() > ignoredisco.get(netaddr, 0) + 90):
|
||||
ignoredisco[netaddr] = time.time()
|
||||
handler(info)
|
||||
consider_discover(info, rqinfo, net4, cfg, rqv, nodeguess)
|
||||
consider_discover(info, rqinfo, net4, cfg, rqv, nodeguess, disco)
|
||||
|
||||
|
||||
|
||||
@ -594,7 +602,7 @@ def get_deployment_profile(node, cfg, cfd=None):
|
||||
|
||||
staticassigns = {}
|
||||
myipbypeer = {}
|
||||
def check_reply(node, info, packet, sock, cfg, reqview, addr):
|
||||
def check_reply(node, info, packet, sock, cfg, reqview, addr, disco):
|
||||
httpboot = info['architecture'] == 'uefi-httpboot'
|
||||
cfd = cfg.get_node_attributes(node, ('deployment.*', 'collective.managercandidates'))
|
||||
profile = get_deployment_profile(node, cfg, cfd)
|
||||
@ -611,7 +619,7 @@ def check_reply(node, info, packet, sock, cfg, reqview, addr):
|
||||
return
|
||||
return reply_dhcp6(node, addr, cfg, packet, cfd, profile, sock)
|
||||
else:
|
||||
return reply_dhcp4(node, info, packet, cfg, reqview, httpboot, cfd, profile)
|
||||
return reply_dhcp4(node, info, packet, cfg, reqview, httpboot, cfd, profile, disco)
|
||||
|
||||
def reply_dhcp6(node, addr, cfg, packet, cfd, profile, sock):
|
||||
myaddrs = netutil.get_my_addresses(addr[-1], socket.AF_INET6)
|
||||
@ -695,7 +703,7 @@ def get_my_duid():
|
||||
return _myuuid
|
||||
|
||||
|
||||
def reply_dhcp4(node, info, packet, cfg, reqview, httpboot, cfd, profile):
|
||||
def reply_dhcp4(node, info, packet, cfg, reqview, httpboot, cfd, profile, disco):
|
||||
replen = 275 # default is going to be 286
|
||||
# while myipn is describing presumed destination, it's really
|
||||
# vague in the face of aliases, need to convert to ifidx and evaluate
|
||||
@ -733,7 +741,7 @@ def reply_dhcp4(node, info, packet, cfg, reqview, httpboot, cfd, profile):
|
||||
log.log({'error': nicerr})
|
||||
if niccfg.get('ipv4_broken', False):
|
||||
# Received a request over a nic with no ipv4 configured, ignore it
|
||||
log.log({'error': 'Skipping boot reply to {0} due to no viable IPv4 configuration on deployment system'.format(node)})
|
||||
log.log({'error': 'Skipping boot reply to {0} due to no viable IPv4 configuration on deployment system, over nic "{}"'.format(node, info['netinfo']['ifidx'])})
|
||||
return
|
||||
clipn = None
|
||||
if niccfg['ipv4_method'] == 'firmwarenone':
|
||||
@ -786,6 +794,7 @@ def reply_dhcp4(node, info, packet, cfg, reqview, httpboot, cfd, profile):
|
||||
repview[249:255] = b'\x33\x04\x00\x00\x00\xf0' # fixed short lease time
|
||||
repview[255:257] = b'\x61\x11'
|
||||
repview[257:274] = packet[97]
|
||||
|
||||
# Note that sending PXEClient kicks off the proxyDHCP procedure, ignoring
|
||||
# boot filename and such in the DHCP packet
|
||||
# we will simply always do it to provide the boot payload in a consistent
|
||||
@ -794,6 +803,9 @@ def reply_dhcp4(node, info, packet, cfg, reqview, httpboot, cfd, profile):
|
||||
repview[replen - 1:replen + 11] = b'\x3c\x0aHTTPClient'
|
||||
replen += 12
|
||||
else:
|
||||
bootfile = get_pxe_bootfile(node, packet, disco, profile, cfg, myip)
|
||||
if bootfile:
|
||||
repview[108:108 + len(bootfile)] = bootfile
|
||||
repview[replen - 1:replen + 10] = b'\x3c\x09PXEClient'
|
||||
replen += 11
|
||||
hwlen = bytearray(reqview[2:3].tobytes())[0]
|
||||
@ -885,11 +897,11 @@ def ack_request(pkt, rq, info):
|
||||
repview[26:28] = struct.pack('!H', datasum)
|
||||
send_raw_packet(repview, len(rply), rq, info)
|
||||
|
||||
def consider_discover(info, packet, sock, cfg, reqview, nodeguess, addr=None):
|
||||
def consider_discover(info, packet, sock, cfg, reqview, nodeguess, disco, addr=None):
|
||||
if info.get('hwaddr', None) in macmap and info.get('uuid', None):
|
||||
check_reply(macmap[info['hwaddr']], info, packet, sock, cfg, reqview, addr)
|
||||
check_reply(macmap[info['hwaddr']], info, packet, sock, cfg, reqview, addr, disco)
|
||||
elif info.get('uuid', None) in uuidmap:
|
||||
check_reply(uuidmap[info['uuid']], info, packet, sock, cfg, reqview, addr)
|
||||
check_reply(uuidmap[info['uuid']], info, packet, sock, cfg, reqview, addr, disco)
|
||||
elif packet.get(53, None) == b'\x03':
|
||||
ack_request(packet, reqview, info)
|
||||
elif info.get('uuid', None) and info.get('hwaddr', None):
|
||||
|
Loading…
Reference in New Issue
Block a user