mirror of
https://github.com/xcat2/confluent.git
synced 2025-02-16 18:49:04 +00:00
When implicitly working by lookup, both v4 and v6 were provided, but first match only on explicit specification. Correct this discrepency.
618 lines
25 KiB
Python
618 lines
25 KiB
Python
# vim: tabstop=4 shiftwidth=4 softtabstop=4
|
|
|
|
# Copyright 2017 Lenovo
|
|
#
|
|
# Licensed under the Apache License, Version 2.0 (the "License");
|
|
# you may not use this file except in compliance with the License.
|
|
# You may obtain a copy of the License at
|
|
#
|
|
# http://www.apache.org/licenses/LICENSE-2.0
|
|
#
|
|
# Unless required by applicable law or agreed to in writing, software
|
|
# distributed under the License is distributed on an "AS IS" BASIS,
|
|
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
# See the License for the specific language governing permissions and
|
|
# limitations under the License.
|
|
# this will implement noderange grammar
|
|
|
|
|
|
import confluent.exceptions as exc
|
|
import codecs
|
|
import netifaces
|
|
import struct
|
|
import eventlet.green.socket as socket
|
|
import eventlet.support.greendns
|
|
import os
|
|
getaddrinfo = eventlet.support.greendns.getaddrinfo
|
|
|
|
|
|
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 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:', '')
|
|
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 address_is_local(address):
|
|
for iface in netifaces.interfaces():
|
|
for i4 in netifaces.ifaddresses(iface).get(2, []):
|
|
cidr = mask_to_cidr(i4['netmask'])
|
|
if ip_on_same_subnet(i4['addr'], address, cidr):
|
|
return True
|
|
for i6 in netifaces.ifaddresses(iface).get(10, []):
|
|
cidr = int(i6['netmask'].split('/')[1])
|
|
laddr = i6['addr'].split('%')[0]
|
|
if ip_on_same_subnet(laddr, address, cidr):
|
|
return True
|
|
return False
|
|
|
|
|
|
_idxtoifnamemap = {}
|
|
def _rebuildidxmap():
|
|
_idxtoifnamemap.clear()
|
|
for iname in os.listdir('/sys/class/net'):
|
|
try:
|
|
ci = int(open('/sys/class/net/{0}/ifindex'.format(iname)).read())
|
|
_idxtoifnamemap[ci] = iname
|
|
except Exception: # there may be non interface in /sys/class/net
|
|
pass
|
|
|
|
|
|
def myiptonets(svrip):
|
|
fam = netifaces.AF_INET
|
|
if ':' in svrip:
|
|
fam = netifaces.AF_INET6
|
|
relevantnic = None
|
|
for iface in netifaces.interfaces():
|
|
for addr in netifaces.ifaddresses(iface).get(fam, []):
|
|
addr = addr.get('addr', '')
|
|
addr = addr.split('%')[0]
|
|
if addresses_match(addr, svrip):
|
|
relevantnic = iface
|
|
break
|
|
else:
|
|
continue
|
|
break
|
|
return inametonets(relevantnic)
|
|
|
|
|
|
def _iftonets(ifidx):
|
|
if isinstance(ifidx, int):
|
|
_rebuildidxmap()
|
|
ifidx = _idxtoifnamemap.get(ifidx, None)
|
|
return inametonets(ifidx)
|
|
|
|
def inametonets(iname):
|
|
addrs = netifaces.ifaddresses(iname)
|
|
try:
|
|
addrs = addrs[netifaces.AF_INET]
|
|
except KeyError:
|
|
return
|
|
for addr in addrs:
|
|
ip = struct.unpack('!I', socket.inet_aton(addr['addr']))[0]
|
|
mask = struct.unpack('!I', socket.inet_aton(addr['netmask']))[0]
|
|
net = ip & mask
|
|
net = socket.inet_ntoa(struct.pack('!I', net))
|
|
yield (net, mask_to_cidr(addr['netmask']), addr['addr'])
|
|
|
|
|
|
class NetManager(object):
|
|
def __init__(self, myaddrs, node, configmanager):
|
|
self.myaddrs = myaddrs
|
|
self._allmyaddrs = None
|
|
self.cfm = configmanager
|
|
self.node = node
|
|
self.myattribs = {}
|
|
self.consumednames4 = set([])
|
|
self.consumednames6 = set([])
|
|
|
|
@property
|
|
def allmyaddrs(self):
|
|
if not self._allmyaddrs:
|
|
self._allmyaddrs = get_my_addresses()
|
|
return self._allmyaddrs
|
|
|
|
def process_attribs(self, netname, attribs):
|
|
self.myattribs[netname] = {}
|
|
ipv4addr = None
|
|
ipv6addr = None
|
|
myattribs = self.myattribs[netname]
|
|
hwaddr = attribs.get('hwaddr', None)
|
|
if hwaddr:
|
|
myattribs['hwaddr'] = hwaddr
|
|
conname = attribs.get('connection_name', None)
|
|
if conname:
|
|
myattribs['connection_name'] = conname
|
|
iname = attribs.get('interface_names', None)
|
|
if iname:
|
|
myattribs['interface_names'] = iname
|
|
teammod = attribs.get('team_mode', None)
|
|
if teammod:
|
|
myattribs['team_mode'] = teammod
|
|
method = attribs.get('ipv4_method', None)
|
|
if method != 'dhcp':
|
|
ipv4addr = attribs.get('ipv4_address', None)
|
|
if ipv4addr:
|
|
try:
|
|
for ai in socket.getaddrinfo(ipv4addr, 0, socket.AF_INET, socket.SOCK_STREAM):
|
|
ipv4addr = ai[-1][0]
|
|
except socket.gaierror:
|
|
pass
|
|
else:
|
|
currname = attribs.get('hostname', self.node).split()[0]
|
|
if currname and currname not in self.consumednames4:
|
|
try:
|
|
for ai in socket.getaddrinfo(currname, 0, socket.AF_INET, socket.SOCK_STREAM):
|
|
ipv4addr = ai[-1][0]
|
|
self.consumednames4.add(currname)
|
|
except socket.gaierror:
|
|
pass
|
|
if ipv4addr:
|
|
myattribs['ipv4_method'] = 'static'
|
|
myattribs['ipv4_address'] = ipv4addr
|
|
else:
|
|
myattribs['ipv4_method'] = 'dhcp'
|
|
if attribs.get('ipv4_gateway', None) and 'ipv4_method' in myattribs:
|
|
myattribs['ipv4_gateway'] = attribs['ipv4_gateway']
|
|
method = attribs.get('ipv6_method', None)
|
|
if method != 'dhcp':
|
|
ipv6addr = attribs.get('ipv6_address', None)
|
|
if ipv6addr:
|
|
try:
|
|
for ai in socket.getaddrinfo(ipv6addr, 0, socket.AF_INET6, socket.SOCK_STREAM):
|
|
ipv6addr = ai[-1][0]
|
|
except socket.gaierror:
|
|
pass
|
|
else:
|
|
currname = attribs.get('hostname', self.node).split()[0]
|
|
if currname and currname not in self.consumednames6:
|
|
try:
|
|
for ai in socket.getaddrinfo(currname, 0, socket.AF_INET6, socket.SOCK_STREAM):
|
|
ipv6addr = ai[-1][0]
|
|
self.consumednames6.add(currname)
|
|
except socket.gaierror:
|
|
pass
|
|
if ipv6addr:
|
|
myattribs['ipv6_method'] = 'static'
|
|
myattribs['ipv6_address'] = ipv6addr
|
|
if attribs.get('ipv6_gateway', None) and 'ipv6_method' in myattribs:
|
|
myattribs['ipv6_gateway'] = attribs['ipv6_gateway']
|
|
if 'ipv4_method' not in myattribs and 'ipv6_method' not in myattribs:
|
|
del self.myattribs[netname]
|
|
return
|
|
if ipv4addr:
|
|
prefixlen = None
|
|
if '/' in ipv4addr:
|
|
ipv4addr, _ = ipv4addr.split('/', 1)
|
|
ipv4bytes = socket.inet_pton(socket.AF_INET, ipv4addr.split('/')[0])
|
|
for addr in self.myaddrs:
|
|
if addr[0] != socket.AF_INET:
|
|
continue
|
|
if ipn_on_same_subnet(addr[0], addr[1], ipv4bytes, addr[2]):
|
|
myattribs['current_nic'] = True
|
|
myattribs['ipv4_address'] = '{0}/{1}'.format(ipv4addr, addr[2])
|
|
if not myattribs.get('current_nic', False) and ipv6addr:
|
|
if '/' in ipv6addr:
|
|
ipv6addr, _ = ipv6addr.split('/', 1)
|
|
ipv6bytes = socket.inet_pton(socket.AF_INET6, ipv6addr)
|
|
for addr in self.myaddrs:
|
|
if addr[0] != socket.AF_INET6:
|
|
continue
|
|
if ipn_on_same_subnet(addr[0], addr[1], ipv6bytes, addr[2]):
|
|
myattribs['current_nic'] = True
|
|
myattribs['ipv6_address'] = '{0}/{1}'.format(ipv6addr, addr[2])
|
|
if '/' not in myattribs.get('ipv6_address', '/'):
|
|
ipn = socket.inet_pton(socket.AF_INET6, myattribs['ipv6_address'])
|
|
plen = 64
|
|
for addr in self.allmyaddrs:
|
|
if addr[0] != socket.AF_INET6:
|
|
continue
|
|
if ipn_on_same_subnet(addr[0], ipn, addr[1], addr[2]):
|
|
plen = addr[2]
|
|
myattribs['ipv6_address'] += '/{0}'.format(plen)
|
|
if '/' not in myattribs.get('ipv4_address', '/'):
|
|
ipn = socket.inet_pton(socket.AF_INET, myattribs['ipv4_address'])
|
|
plen = 16
|
|
for addr in self.allmyaddrs:
|
|
if addr[0] != socket.AF_INET:
|
|
continue
|
|
if ipn_on_same_subnet(addr[0], ipn, addr[1], addr[2]):
|
|
plen = addr[2]
|
|
myattribs['ipv4_address'] += '/{0}'.format(plen)
|
|
if 'current_nic' not in myattribs:
|
|
myattribs['current_nic'] = False
|
|
|
|
|
|
def get_full_net_config(configmanager, node, serverip=None):
|
|
cfd = configmanager.get_node_attributes(node, ['net.*'])
|
|
cfd = cfd.get(node, {})
|
|
attribs = {}
|
|
for attrib in cfd:
|
|
val = cfd[attrib].get('value', None)
|
|
if val is None:
|
|
continue
|
|
if attrib.startswith('net.'):
|
|
attrib = attrib.replace('net.', '').rsplit('.', 1)
|
|
if len(attrib) == 1:
|
|
iface = None
|
|
attrib = attrib[0]
|
|
else:
|
|
iface, attrib = attrib
|
|
if attrib in ('switch', 'switchport', 'bootable'):
|
|
continue
|
|
if iface not in attribs:
|
|
attribs[iface] = {}
|
|
attribs[iface][attrib] = val
|
|
myaddrs = None
|
|
if serverip:
|
|
myaddrs = get_addresses_by_serverip(serverip)
|
|
nm = NetManager(myaddrs, node, configmanager)
|
|
if None in attribs:
|
|
nm.process_attribs(None, attribs[None])
|
|
del attribs[None]
|
|
for netname in sorted(attribs):
|
|
nm.process_attribs(netname, attribs[netname])
|
|
retattrs = {}
|
|
if None in nm.myattribs:
|
|
retattrs['default'] = nm.myattribs[None]
|
|
del nm.myattribs[None]
|
|
retattrs['extranets'] = nm.myattribs
|
|
return retattrs
|
|
|
|
|
|
|
|
# TODO(jjohnson2): have a method to arbitrate setting methods, to aid
|
|
# in correct matching of net.* based on parameters, mainly for pxe
|
|
# The scheme for pxe:
|
|
# For one: the candidate net.* should have pxe set to true, to help
|
|
# disambiguate from interfaces meant for bmc access
|
|
# bmc relies upon hardwaremanagement.manager, plus we don't collect
|
|
# that mac address
|
|
# the ip as reported by recvmsg to match the subnet of that net.* interface
|
|
# if switch and port available, that should match.
|
|
def get_nic_config(configmanager, node, ip=None, mac=None, ifidx=None,
|
|
serverip=None):
|
|
"""Fetch network configuration parameters for a nic
|
|
|
|
For a given node and interface, find and retrieve the pertinent network
|
|
configuration data. The desired configuration can be searched
|
|
either by ip or by mac.
|
|
|
|
:param configmanager: The relevant confluent.config.ConfigManager
|
|
instance.
|
|
:param node: The name of the node
|
|
:param ip: An IP address on the intended subnet
|
|
:param mac: The mac address of the interface
|
|
:param ifidx: The local index relevant to the network.
|
|
|
|
:returns: A dict of parameters, 'ipv4_gateway', ....
|
|
"""
|
|
# ip parameter *could* be the result of recvmsg with cmsg to tell
|
|
# pxe *our* ip address, or it could be the desired ip address
|
|
#TODO(jjohnson2): ip address, prefix length, mac address,
|
|
# join a bond/bridge, vlan configs, etc.
|
|
# also other nic criteria, physical location, driver and index...
|
|
nodenetattribs = configmanager.get_node_attributes(
|
|
node, 'net*').get(node, {})
|
|
cfgbyname = {}
|
|
for attrib in nodenetattribs:
|
|
segs = attrib.split('.')
|
|
if len(segs) == 2:
|
|
name = None
|
|
else:
|
|
name = segs[1]
|
|
if name not in cfgbyname:
|
|
cfgbyname[name] = {}
|
|
cfgbyname[name][segs[-1]] = nodenetattribs[attrib].get('value',
|
|
None)
|
|
cfgdata = {
|
|
'ipv4_gateway': None,
|
|
'ipv4_address': None,
|
|
'ipv4_method': None,
|
|
'prefix': None,
|
|
'ipv6_prefix': None,
|
|
'ipv6_address': None,
|
|
'ipv6_method': None,
|
|
}
|
|
myaddrs = []
|
|
if ifidx is not None:
|
|
dhcprequested = False
|
|
myaddrs = get_my_addresses(ifidx)
|
|
v4broken = True
|
|
v6broken = True
|
|
for addr in myaddrs:
|
|
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:
|
|
dhcprequested = False
|
|
myaddrs = get_addresses_by_serverip(serverip)
|
|
genericmethod = 'static'
|
|
ipbynodename = None
|
|
ip6bynodename = None
|
|
try:
|
|
for addr in socket.getaddrinfo(node, 0, socket.AF_INET, socket.SOCK_DGRAM):
|
|
ipbynodename = addr[-1][0]
|
|
except socket.gaierror:
|
|
pass
|
|
try:
|
|
for addr in socket.getaddrinfo(node, 0, socket.AF_INET6, socket.SOCK_DGRAM):
|
|
ip6bynodename = addr[-1][0]
|
|
except socket.gaierror:
|
|
pass
|
|
if myaddrs:
|
|
foundaddr = False
|
|
candgws = []
|
|
candsrvs = []
|
|
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('ipv{}_method'.format(nver), 'static')
|
|
if ipmethod == 'dhcp':
|
|
dhcprequested = True
|
|
continue
|
|
if ipmethod == 'firmwaredhcp':
|
|
genericmethod = ipmethod
|
|
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('ipv{}_gateway'.format(nver), None)
|
|
if candip:
|
|
try:
|
|
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['ipv{}_prefix'.format(nver)] = prefix
|
|
if candip in (ipbynodename, ip6bynodename):
|
|
cfgdata['matchesnodename'] = True
|
|
foundaddr = True
|
|
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:
|
|
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 foundaddr:
|
|
return cfgdata
|
|
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 and ip6bynodename == None:
|
|
return cfgdata
|
|
for myaddr in myaddrs:
|
|
fam, svrip, prefix = myaddr[:3]
|
|
if fam == socket.AF_INET:
|
|
bynodename = ipbynodename
|
|
nver = '4'
|
|
else:
|
|
bynodename = ip6bynodename
|
|
nver = '6'
|
|
if not bynodename: # node is missing either ipv6 or ipv4, ignore
|
|
continue
|
|
ipbynodenamn = socket.inet_pton(fam, bynodename)
|
|
if ipn_on_same_subnet(fam, svrip, ipbynodenamn, prefix):
|
|
cfgdata['matchesnodename'] = True
|
|
cfgdata['ipv{}_address'.format(nver)] = bynodename
|
|
cfgdata['ipv{}_method'.format(nver)] = genericmethod
|
|
if nver == '4':
|
|
cfgdata['prefix'] = prefix
|
|
else:
|
|
cfgdata['ipv{}_prefix'.format(nver)] = prefix
|
|
for svr in candsrvs:
|
|
fam, svr, prefix = svr
|
|
if fam == socket.AF_INET:
|
|
bynodename = ipbynodename
|
|
elif fam == socket.AF_INET6:
|
|
bynodename = ip6bynodename
|
|
if not bynodename:
|
|
continue # ignore undefined family
|
|
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:
|
|
fam, candgwn, prefix = gw
|
|
if fam == socket.AF_INET:
|
|
nver = '4'
|
|
bynodename = ipbynodename
|
|
elif fam == socket.AF_INET6:
|
|
nver = '6'
|
|
bynodename = ip6bynodename
|
|
if bynodename:
|
|
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:
|
|
for prefixinfo in get_prefix_len_for_ip(ip):
|
|
fam, prefix = prefixinfo
|
|
if fam == socket.AF_INET:
|
|
cfgdata['prefix'] = prefix
|
|
nver = '4'
|
|
else:
|
|
nver = '6'
|
|
cfgdata['ipv{}_prefix'.format(nver)] = prefix
|
|
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
|
|
|
|
def get_addresses_by_serverip(serverip):
|
|
if '.' in serverip:
|
|
fam = socket.AF_INET
|
|
elif ':' in serverip:
|
|
fam = socket.AF_INET6
|
|
else:
|
|
raise ValueError('"{0}" is not a valid ip argument')
|
|
ipbytes = socket.inet_pton(fam, serverip)
|
|
if ipbytes[:8] == b'\xfe\x80\x00\x00\x00\x00\x00\x00':
|
|
myaddrs = get_my_addresses(matchlla=ipbytes)
|
|
else:
|
|
myaddrs = [x for x in get_my_addresses() if x[1] == ipbytes]
|
|
return myaddrs
|
|
|
|
nlhdrsz = struct.calcsize('IHHII')
|
|
ifaddrsz = struct.calcsize('BBBBI')
|
|
|
|
def get_my_addresses(idx=0, family=0, matchlla=None):
|
|
# RTM_GETADDR = 22
|
|
# nlmsghdr struct: u32 len, u16 type, u16 flags, u32 seq, u32 pid
|
|
nlhdr = struct.pack('IHHII', nlhdrsz + ifaddrsz, 22, 0x301, 0, 0)
|
|
# ifaddrmsg struct: u8 family, u8 prefixlen, u8 flags, u8 scope, u32 index
|
|
ifaddrmsg = struct.pack('BBBBI', family, 0, 0, 0, idx)
|
|
s = socket.socket(socket.AF_NETLINK, socket.SOCK_RAW, socket.NETLINK_ROUTE)
|
|
s.bind((0, 0))
|
|
s.sendall(nlhdr + ifaddrmsg)
|
|
addrs = []
|
|
while True:
|
|
pdata = s.recv(65536)
|
|
v = memoryview(pdata)
|
|
if struct.unpack('H', v[4:6])[0] == 3: # netlink done message
|
|
break
|
|
while len(v):
|
|
length, typ = struct.unpack('IH', v[:6])
|
|
if typ == 20:
|
|
fam, plen, _, scope, ridx = struct.unpack('BBBBI', v[nlhdrsz:nlhdrsz+ifaddrsz])
|
|
if matchlla:
|
|
if scope == 253:
|
|
rta = v[nlhdrsz+ifaddrsz:length]
|
|
while len(rta):
|
|
rtalen, rtatyp = struct.unpack('HH', rta[:4])
|
|
if rta[4:rtalen].tobytes() == matchlla:
|
|
return get_my_addresses(idx=ridx)
|
|
rta = rta[rtalen:]
|
|
elif (ridx == idx or not idx) and scope == 0:
|
|
rta = v[nlhdrsz+ifaddrsz:length]
|
|
while len(rta):
|
|
rtalen, rtatyp = struct.unpack('HH', rta[:4])
|
|
if rtatyp == 1:
|
|
addrs.append((fam, rta[4:rtalen].tobytes(), plen, ridx))
|
|
if not rtalen:
|
|
break
|
|
rta = rta[rtalen:]
|
|
v = v[length:]
|
|
return addrs
|
|
|
|
|
|
def get_prefix_len_for_ip(ip):
|
|
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
|
|
|
|
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
|