2
0
mirror of https://github.com/xcat2/confluent.git synced 2025-08-29 06:18:15 +00:00

Further the IPv6 support

This commit is contained in:
Jarrod Johnson
2021-08-26 17:44:30 -04:00
parent f2fcbc887d
commit a9044238ba
2 changed files with 157 additions and 88 deletions

View File

@@ -461,6 +461,26 @@ node = {
'case for other net attributes, net.eth0.ipv4_gateway '
'and similar is accepted.'
},
'net.ipv6_address': {
'description': 'When configuring static, use this address. If '
'unspecified, it will check if the node name resolves '
'to an IP address. Additionally, the subnet prefix '
'may be specified with a suffix, e.g. "/64". If not '
'specified, it will attempt to autodetect based on '
'current network configuration.'
},
'net.ipv6_method': {
'description': 'Whether to use static or dhcp when configuring this '
'interface for IPv6. "firmwaredhcp" means to defer to '
'external DHCP server during firmware execution, but '
'use static for OS',
'validvalues': ('dhcp', 'static', 'firmwaredhcp', 'none')
},
'net.ipv6_gateway': {
'description': 'The IPv6 gateway to use if applicable. As is the '
'case for other net attributes, net.eth0.ipv6_gateway '
'and similar is accepted.'
},
'net.hwaddr': {
'description': 'The hardware address, aka MAC address of the interface indicated, generally populated by the '
'PXE discovery mechanism'

View File

@@ -39,6 +39,24 @@ def cidr_to_mask(cidr):
return socket.inet_ntop(
socket.AF_INET, struct.pack('!I', (2**32 - 1) ^ (2**(32 - cidr) - 1)))
def ipn_on_same_subnet(fam, first, second, prefix):
if fam == socket.AF_INET6:
if prefix > 64:
firstmask = 0xffffffffffffffff
secondmask = (2**64-1) ^ (2**(128 - prefix) - 1)
else:
firstmask = (2**64-1) ^ (2**(64 - prefix) - 1)
secondmask = 0
first = struct.unpack('!QQ', first)
second = struct.unpack('!QQ', second)
return ((first[0] & firstmask == second[0] & firstmask)
and (first[1] & secondmask == second[1] & secondmask))
else:
mask = (2**32 - 1) ^ (2**(32 - prefix) - 1)
first = struct.unpack('!I', first)[0]
second = struct.unpack('!I', second)[0]
return (first & mask == second & mask)
def ip_on_same_subnet(first, second, prefix):
if first.startswith('::ffff:') and '.' in first:
first = first.replace('::ffff:', '')
@@ -172,8 +190,6 @@ def get_nic_config(configmanager, node, ip=None, mac=None, ifidx=None,
None)
cfgdata = {
'ipv4_gateway': None,
'ipv4_broken': True,
'ipv6_broken': True,
'ipv4_address': None,
'ipv4_method': None,
'prefix': None,
@@ -185,14 +201,17 @@ def get_nic_config(configmanager, node, ip=None, mac=None, ifidx=None,
if ifidx is not None:
dhcprequested = False
myaddrs = get_my_addresses(ifidx)
v4broken = True
v6broken = True
for addr in myaddrs:
try:
if addr[0] == socket.AF_INET:
del cfgdata['ipv4_broken']
elif addr[1] == socket.AF_INET6:
del cfgdata['ipv6_broken']
except KeyError:
pass
if addr[0] == socket.AF_INET:
v4broken = False
elif addr[0] == socket.AF_INET6:
v6broken = False
if v4broken:
cfgdata['ipv4_broken'] = True
if v6broken:
cfgdata['ipv6_broken'] = True
if serverip is not None:
if '.' in serverip:
fam = socket.AF_INET
@@ -202,87 +221,136 @@ def get_nic_config(configmanager, node, ip=None, mac=None, ifidx=None,
raise ValueError('"{0}" is not a valid ip argument')
ipbytes = socket.inet_pton(fam, serverip)
dhcprequested = False
myips = [x for x in get_my_addresses() if x[1] == ipbytes]
print(repr(myips))
nets = list(myiptonets(serverip))
myaddrs = [x for x in get_my_addresses() if x[1] == ipbytes]
genericmethod = 'static'
ipbynodename = None
ip6bynodename = None
try:
ipbynodename = socket.getaddrinfo(
node, 0, socket.AF_INET, socket.SOCK_DGRAM)[0][-1][0]
except Exception:
ipbynodename = None
if nets:
for addr in socket.getaddrinfo(node, 0, 0, socket.SOCK_DGRAM):
if addr[0] == socket.AF_INET:
ipbynodename = addr[-1][0]
elif addr[0] == socket.AF_INET6:
ip6bynodename = addr[-1][0]
except socket.gaierror:
pass
if myaddrs:
candgws = []
candsrvs = []
for net in nets:
net, prefix, svrip = net
candsrvs.append(svrip)
cfgdata['deploy_server'] = svrip
for myaddr in myaddrs:
fam, svrip, prefix = myaddr[:3]
candsrvs.append((fam, svrip, prefix))
if fam == socket.AF_INET:
cfgdata['deploy_server'] = socket.inet_ntop(socket.AF_INET, svrip)
nver = '4'
elif fam == socket.AF_INET6:
cfgdata['deploy_server_v6'] = socket.inet_ntop(socket.AF_INET6, svrip)
nver = '6'
for candidate in cfgbyname:
ipmethod = cfgbyname[candidate].get('ipv4_method', 'static')
ipmethod = cfgbyname[candidate].get('ipv{}_method'.format(nver), 'static')
if ipmethod == 'dhcp':
dhcprequested = True
continue
if ipmethod == 'firmwaredhcp':
genericmethod = ipmethod
candip = cfgbyname[candidate].get('ipv4_address', None)
candip = cfgbyname[candidate].get('ipv{}_address'.format(nver), None)
if candip and '/' in candip:
candip, candprefix = candip.split('/')
if int(candprefix) != prefix:
continue
candgw = cfgbyname[candidate].get('ipv4_gateway', None)
candgw = cfgbyname[candidate].get('ipv{}_gateway'.format(nver), None)
if candip:
try:
if ip_on_same_subnet(net, candip, prefix):
cfgdata['ipv4_address'] = candip
cfgdata['ipv4_method'] = ipmethod
cfgdata['ipv4_gateway'] = cfgbyname[candidate].get(
'ipv4_gateway', None)
cfgdata['prefix'] = prefix
if ipbynodename and ipbynodename == candip:
for inf in socket.getaddrinfo(candip, 0, fam, socket.SOCK_STREAM):
candipn = socket.inet_pton(fam, inf[-1][0])
if ipn_on_same_subnet(fam, svrip, candipn, prefix):
cfgdata['ipv{}_address'.format(nver)] = candip
cfgdata['ipv{}_method'.format(nver)] = ipmethod
cfgdata['ipv{}_gateway'.format(nver)] = cfgbyname[candidate].get(
'ipv{}_gateway'.format(nver), None)
if nver == '4':
cfgdata['prefix'] = prefix
else:
cfgdata['prefix_v{}'.format(nver)] = prefix
if candip in (ipbynodename, ip6bynodename):
cfgdata['matchesnodename'] = True
return cfgdata
except Exception as e:
cfgdata['error_msg'] = "Error trying to evaluate net.*ipv4_address attribute value '{0}' on {1}: {2}".format(candip, node, str(e))
elif candgw:
if ip_on_same_subnet(net, candgw, prefix):
candgws.append(candgw)
for inf in socket.getaddrinfo(candgw, 0, fam, socket.SOCK_STREAM):
candgwn = socket.inet_pton(fam, inf[-1][0])
if ipn_on_same_subnet(fam, svrip, candgwn, prefix):
candgws.append((fam, candgwn, prefix))
if dhcprequested:
if not cfgdata.get('ipv4_method', None):
cfgdata['ipv4_method'] = 'dhcp'
if not cfgdata.get('ipv6_method', None):
cfgdata['ipv6_method'] = 'dhcp'
return cfgdata
if ipbynodename == None:
if ipbynodename == None and ip6bynodename == None:
return cfgdata
for net in nets:
net, prefix, svrip = net
if ip_on_same_subnet(net, ipbynodename, prefix):
for myaddr in myaddrs:
fam, svrip, prefix = myaddr[:3]
if fam == socket.AF_INET:
bynodename = ipbynodename
nver = '4'
else:
bynodename = ip6bynodename
nver = '6'
ipbynodenamn = socket.inet_pton(fam, bynodename)
if ipn_on_same_subnet(fam, svrip, ipbynodenamn, prefix):
cfgdata['matchesnodename'] = True
cfgdata['ipv4_address'] = ipbynodename
cfgdata['ipv4_method'] = genericmethod
cfgdata['prefix'] = prefix
break
cfgdata['ipv{}_address'.format(nver)] = bynodename
cfgdata['ipv{}_method'.format(nver)] = genericmethod
if nver == '4':
cfgdata['prefix'] = prefix
else:
cfgdata['prefix_v6'] = prefix
for svr in candsrvs:
if ip_on_same_subnet(svr, ipbynodename, prefix):
cfgdata['deploy_server'] = svr
break
fam, svr, prefix = svr
if fam == socket.AF_INET:
bynodename = ipbynodename
elif fam == socket.AF_INET6:
bynodename = ip6bynodename
bynodenamn = socket.inet_pton(fam, bynodename)
if ipn_on_same_subnet(fam, svr, bynodenamn, prefix):
svrname = socket.inet_ntop(fam, svr)
if fam == socket.AF_INET:
cfgdata['deploy_server'] = svrname
else:
cfgdata['deploy_server_v6'] = svrname
for gw in candgws:
if ip_on_same_subnet(gw, ipbynodename, prefix):
cfgdata['ipv4_gateway'] = gw
break
fam, candgwn, prefix = gw
if fam == socket.AF_INET:
nver = '4'
bynodename = ipbynodename
elif fam == socket.AF_INET6:
nver = '6'
bynodename = ip6bynodename
bynodenamn = socket.inet_pton(fam, bynodename)
if ipn_on_same_subnet(fam, candgwn, bynodenamn, prefix):
cfgdata['ipv{}_gateway'.format(nver)] = socket.inet_ntop(fam, candgwn)
return cfgdata
if ip is not None:
prefixlen = get_prefix_len_for_ip(ip)
cfgdata['prefix'] = prefixlen
for setting in nodenetattribs:
if 'ipv4_gateway' not in setting:
continue
gw = nodenetattribs[setting].get('value', None)
if gw is None or not gw:
continue
if ip_on_same_subnet(ip, gw, prefixlen):
cfgdata['ipv4_gateway'] = gw
break
for prefixinfo in get_prefix_len_for_ip(ip):
fam, prefix = prefixinfo
if fam == socket.AF_INET:
cfgdata['prefix'] = prefix
nver = '4'
else:
cfgdata['prefix_v6'] = prefix
nver = '6'
for setting in nodenetattribs:
if 'ipv{}_gateway'.format(nver) not in setting:
continue
gw = nodenetattribs[setting].get('value', None)
if gw is None or not gw:
continue
gwn = socket.inet_pton(fam, gw)
ipn = socket.inet_pton(fam, ip)
if ipn_on_same_subnet(fam, ipn, gwn, prefix):
cfgdata['ipv{}_gateway'.format(nver)] = gw
break
return cfgdata
nlhdrsz = struct.calcsize('IHHII')
@@ -321,36 +389,17 @@ def get_my_addresses(idx=0, family=0):
def get_prefix_len_for_ip(ip):
# for now, we'll use the system route table
# later may provide for configuration lookup to override the route
# table
ip = getaddrinfo(ip, 0, socket.AF_INET)[0][-1][0]
try:
ipn = socket.inet_aton(ip)
except socket.error: # For now, assume 64 for ipv6
return 64
# It comes out big endian, regardless of host arch
ipn = struct.unpack('>I', ipn)[0]
rf = open('/proc/net/route')
ri = rf.read()
rf.close()
ri = ri.split('\n')[1:]
for rl in ri:
if not rl:
continue
rd = rl.split('\t')
if rd[1] == '00000000': # default gateway, not useful for this
continue
# don't have big endian to look at, assume that it is host endian
maskn = struct.unpack('I', struct.pack('>I', int(rd[7], 16)))[0]
netn = struct.unpack('I', struct.pack('>I', int(rd[1], 16)))[0]
if ipn & maskn == netn:
nbits = 0
while maskn:
nbits += 1
maskn = maskn << 1 & 0xffffffff
return nbits
raise exc.NotImplementedException("Non local addresses not supported")
myaddrs = get_my_addresses()
found = False
for inf in socket.getaddrinfo(ip, 0, 0, socket.SOCK_DGRAM):
for myaddr in myaddrs:
if inf[0] != myaddr[0]:
continue
if ipn_on_same_subnet(myaddr[0], myaddr[1], socket.inet_pton(myaddr[0], inf[-1][0]), myaddr[2]):
found = True
yield (myaddr[0], myaddr[2])
if not found:
raise exc.NotImplementedException("Non local addresses not supported")
def addresses_match(addr1, addr2):
"""Check two network addresses for similarity