2
0
mirror of https://github.com/xcat2/confluent.git synced 2026-09-22 09:32:28 +00:00

Implement lease_time opt in for non-boot dhcp offers

This commit is contained in:
Jarrod Johnson
2026-09-18 15:31:12 -04:00
parent 3dad193926
commit 043ede7464
2 changed files with 36 additions and 18 deletions
@@ -591,34 +591,42 @@ myipbypeer = {}
async def check_reply(node, info, packet, sock, cfg, reqview, addr, requestor):
if not requestor:
requestor = ('0.0.0.0', None)
isboot = True
if requestor[0] == '0.0.0.0' and not info.get('uuid', None):
return # ignore DHCP from local non-PXE segment
httpboot = info.get('architecture', None) == 'uefi-httpboot'
isboot = False
if addr:
niccfg = await netutil.get_nic_config(cfg, node, ifidx=addr[-1], onlyfamily=socket.AF_INET6)
else:
niccfg = await netutil.get_nic_config(cfg, node, ifidx=info['netinfo']['ifidx'], onlyfamily=socket.AF_INET)
if not niccfg.get('lease_time'):
return # ignore DHCP from local non-PXE segment
cfd = cfg.get_node_attributes(node, ('deployment.*', 'collective.managercandidates'))
profile, stgprofile = get_deployment_profile(node, cfg, cfd)
if ((not profile)
and (requestor[0] == '0.0.0.0' or not stgprofile)):
if time.time() > ignoremacs.get(info['hwaddr'], 0) + 90:
ignoremacs[info['hwaddr']] = time.time()
log.log({'info': 'Ignoring boot attempt by {0} no deployment profile specified (uuid {1}, hwaddr {2})'.format(
node, info.get('uuid', 'NA'), info['hwaddr']
)})
return
if isboot:
if ((not profile)
and (requestor[0] == '0.0.0.0' or not stgprofile)):
if time.time() > ignoremacs.get(info['hwaddr'], 0) + 90:
ignoremacs[info['hwaddr']] = time.time()
log.log({'info': 'Ignoring boot attempt by {0} no deployment profile specified (uuid {1}, hwaddr {2})'.format(
node, info.get('uuid', 'NA'), info['hwaddr']
)})
return
if addr:
if packet['vci'] and packet['vci'].startswith('PXEClient'):
log.log({'info': 'IPv6 PXE boot attempt by {0}, but IPv6 PXE is not supported, try IPv6 HTTP boot or IPv4 boot'.format(node)})
return
return await reply_dhcp6(node, addr, cfg, packet, cfd, profile, sock)
else:
return await reply_dhcp4(node, info, packet, cfg, reqview, httpboot, cfd, profile, sock, requestor)
httpboot = info.get('architecture', None) == 'uefi-httpboot'
return await reply_dhcp4(node, info, packet, cfg, reqview, httpboot, cfd, profile, sock, requestor, niccfg=niccfg)
async def reply_dhcp6(node, addr, cfg, packet, cfd, profile, sock):
async def reply_dhcp6(node, addr, cfg, packet, cfd, profile, sock, niccfg=None):
myaddrs = await netutil.get_my_addresses(addr[-1], socket.AF_INET6)
if not myaddrs:
log.log({'info': 'Unable to provide IPv6 boot services to {0}, no viable IPv6 configuration on interface index "{1}" to respond through.'.format(node, addr[-1])})
return
niccfg = await netutil.get_nic_config(cfg, node, ifidx=addr[-1], onlyfamily=socket.AF_INET6)
if not niccfg:
niccfg = await netutil.get_nic_config(cfg, node, ifidx=addr[-1], onlyfamily=socket.AF_INET6)
ipv6addr = niccfg.get('ipv6_address', None)
ipv6prefix = niccfg.get('ipv6_prefix', None)
ipv6method = niccfg.get('ipv6_method', 'static')
@@ -698,7 +706,7 @@ def get_my_duid():
_recent_txids = {}
async def reply_dhcp4(node, info, packet, cfg, reqview, httpboot, cfd, profile, sock=None, requestor=None):
async def reply_dhcp4(node, info, packet, cfg, reqview, httpboot, cfd, profile, sock=None, requestor=None, niccfg=None):
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
@@ -737,8 +745,8 @@ async def reply_dhcp4(node, info, packet, cfg, reqview, httpboot, cfd, profile,
repview[10:11] = b'\x80' # always set broadcast
repview[28:44] = reqview[28:44] # copy chaddr field
relayip = reqview[24:28].tobytes()
if (not isboot) and relayip == b'\x00\x00\x00\x00':
# Ignore local DHCP packets if it isn't a firmware request
if (not isboot) and relayip == b'\x00\x00\x00\x00' and not (niccfg and niccfg.get('lease_time')):
# Ignore local DHCP packets if it isn't a firmware request and operator didn't opt into lease_time
return
relayipa = None
if relayip != b'\x00\x00\x00\x00':
@@ -823,7 +831,11 @@ async def reply_dhcp4(node, info, packet, cfg, reqview, httpboot, cfd, profile,
repview[242:243] = b'\x05'
repview[243:245] = b'\x36\x04' # DHCP server identifier
repview[245:249] = myipn
repview[249:255] = b'\x33\x04\x00\x00\x00\xf0' # fixed short lease time
if niccfg.get('lease_time'):
leasetime = int(niccfg['lease_time'])
repview[249:255] = b'\x33\x04' + struct.pack('!I', leasetime)
else:
repview[249:255] = b'\x33\x04\x00\x00\x00\xf0' # fixed short lease time
repview[255:257] = b'\x61\x11'
if packet.get(97, None) is not None:
repview[257:274] = packet[97]
@@ -941,6 +953,8 @@ def ack_request(pkt, rq, info, sock=None, requestor=None):
hwaddr = rq[28:28+hwlen].tobytes()
relayip = rq[24:28].tobytes()
myipn = myipbypeer.get(hwaddr, None)
#TODO(jjohnson2): This only works for the discover/offer/request/ack sequence, the renewal is broken
# Will need to refactor the reply_dhcp logic to generate a new reply for other request
if not myipn or pkt.get(54, None) != myipn:
return
assigninfo = staticassigns.get(hwaddr, None)
+4
View File
@@ -777,6 +777,7 @@ async def get_nic_config(configmanager, node, ip=None, mac=None, ifidx=None,
'ipv6_prefix': None,
'ipv6_address': None,
'ipv6_method': None,
'lease_time': None,
}
myaddrs = []
if ifidx is not None:
@@ -846,6 +847,9 @@ async def get_nic_config(configmanager, node, ip=None, mac=None, ifidx=None,
cfgdata[srvkey] = socket.inet_ntop(fam, svrip)
for candidate in cfgbyname:
ipmethod = cfgbyname[candidate].get('ipv{}_method'.format(nver), 'static')
leasetime = cfgbyname[candidate].get('lease_time'.format(nver), None)
if leasetime:
cfgdata['lease_time'] = leasetime
if not ipmethod:
ipmethod = 'static'
if ipmethod == 'dhcp':