Commit 5006f0af authored by Xavier Thompson's avatar Xavier Thompson

slapformat: Allow customising IPv6 range sizes

parent 3006cb19
......@@ -1024,7 +1024,7 @@ class Tun(Tap):
class Interface(object):
"""Represent a network interface on the system"""
def __init__(self, logger, name, ipv4_local_network, ipv6_interface=None):
def __init__(self, logger, name, ipv4_local_network, ipv6_interface=None, ipv6_prefixshift=16):
"""
Attributes:
name: String, the name of the interface
......@@ -1036,6 +1036,8 @@ class Interface(object):
self.ipv6_interface = ipv6_interface or name
self._ipv6_ranges = set()
self.ipv6_prefixshift = ipv6_prefixshift
# XXX no __getinitargs__, as instances of this class are never deserialized.
def getIPv4LocalAddressList(self):
......@@ -1292,7 +1294,7 @@ class Interface(object):
# Try to use the IPv6 mapping based on partition index
address_dict['prefixlen'] = lenNetmaskIpv6(address_dict['netmask'])
if tap:
result_addr = getTapIpv6Range(address_dict, partition_index)
result_addr = getTapIpv6Range(address_dict, partition_index, self.ipv6_prefixshift)
# the netmask of the tap itself is always 128 bits
result_addr['netmask'] = netmaskFromLenIPv6(128)
else:
......@@ -1304,6 +1306,17 @@ class Interface(object):
self._reserveIpv6Range(result_addr['addr'], result_addr['prefixlen'])
return result_addr
if self._ipv6_prefixshift != 16:
self._logger.error(
"Address %s/%s for partition %s is already taken;"
" aborting because IPv6 prefixshift is %s != 16" % (
result_addr['addr'],
result_addr['prefixlen'],
'%s tap' % partition_index if tap else partition_index,
self._ipv6_prefixshift,
))
raise AddressGenerationError(addr)
self._logger.warning(
"Falling back to random address selection for partition %s"
" because %s/%s is already taken" % (
......@@ -1350,14 +1363,24 @@ class Interface(object):
address_dict = interface_addr_list[0]
address_dict['prefixlen'] = lenNetmaskIpv6(address_dict['netmask'])
if tun:
ipv6_range = getTunIpv6Range(address_dict, i)
ipv6_range = getTunIpv6Range(address_dict, i, self.ipv6_prefixshift)
else:
ipv6_range = getPartitionIpv6Range(address_dict, i)
ipv6_range = getPartitionIpv6Range(address_dict, i, self.ipv6_prefixshift)
ipv6_range['netmask'] = netmaskFromLenIPv6(ipv6_range['prefixlen'])
ipv6_range['network'] = '%(addr)s/%(prefixlen)d' % ipv6_range
if self._tryReserveIpv6Range(ipv6_range['addr'], ipv6_range['prefixlen']):
return ipv6_range
if self._ipv6_prefixshift != 16:
self._logger.error(
"Address % for partition %s is already taken;"
" aborting because IPv6 prefixshift is %s != 16" % (
ipv6_range['network'],
'%s tun' % i if tun else i,
self._ipv6_prefixshift,
))
raise AddressGenerationError(ipv6_range['addr'])
self._logger.warning(
"Falling back to random IPv6 range selection for partition %s"
" because %s is already taken" % (
......@@ -1406,7 +1429,8 @@ def parse_computer_definition(conf, definition_path):
interface = Interface(logger=conf.logger,
name=conf.interface_name,
ipv4_local_network=conf.ipv4_local_network,
ipv6_interface=conf.ipv6_interface)
ipv6_interface=conf.ipv6_interface,
ipv6_prefixshift=conf.ipv6_prefixshift)
computer = Computer(
reference=conf.computer_id,
interface=interface,
......@@ -1455,7 +1479,8 @@ def parse_computer_xml(conf, xml_path):
interface = Interface(logger=conf.logger,
name=conf.interface_name,
ipv4_local_network=conf.ipv4_local_network,
ipv6_interface=conf.ipv6_interface)
ipv6_interface=conf.ipv6_interface,
ipv6_prefixshift=conf.ipv6_prefixshift)
if os.path.exists(xml_path):
conf.logger.debug('Loading previous computer data from %r' % xml_path)
......@@ -1597,6 +1622,7 @@ class FormatConfig(object):
interface_name = None
ipv6_interface = None
partition_has_ipv6_range = True
ipv6_prefixshift = 16
create_tap = True
tap_base_name = None
tap_ipv6 = True
......@@ -1697,6 +1723,15 @@ class FormatConfig(object):
file_location)
sys.exit(1)
# Sanity check for prefixshift
self.ipv6_prefixshift = ipv6_prefixshift = int(self.ipv6_prefixshift)
if (1 << ipv6_prefixshift) < 4 * int(self.partition_amount):
# Each partition needs at least 4 IPv6, for IPv6 address, range, tap and tun
self.logger.fatal(
'ipv6_prefixshift %s is too small for partition_amount %s',
ipv6_prefixshift, self.partition_amount)
sys.exit(1)
self.logger.debug('Started.')
if self.dry_run:
self.logger.info("Dry-run mode enabled.")
......
......@@ -221,7 +221,11 @@ def getPartitionIpv6Addr(ipv6_range, partition_index):
return dict(addr=ipv6FromBin(prefix + bin(partition_index+2)[2:].zfill(128 - prefixlen)), prefixlen=prefixlen)
def getIpv6RangeFactory(k, s):
def getIpv6Range(ipv6_range, partition_index):
"""
k in (1, 2, 3)
s in ('0', '1')
"""
def getIpv6Range(ipv6_range, partition_index, prefixshift):
"""
from a IPv6 range in the form
{
......@@ -230,20 +234,23 @@ def getIpv6RangeFactory(k, s):
}
returns the IPv6 range
{
'addr' : addr:(k*(2^14) + partition_index+1)
'prefixlen' : CIDR+16
'addr' : addr:(k*(2^(prefixshift-2)) + partition_index+1)
'prefixlen' : CIDR+prefixshift
}
"""
addr = ipv6_range['addr']
prefixlen = ipv6_range['prefixlen']
prefix = binFromIpv6(addr)[:prefixlen]
n = prefixshift
# we generate a subnetwork for the partition
# the subnetwork has 16 bits more than our IPv6 range
# make sure we have at least 2 IPs in the subnetwork
prefixlen += 16
prefixlen += n
if prefixlen >= 128:
raise ValueError('The IPv6 range has prefixlen {} which is too big for generating IPv6 range for partitions.'.format(prefixlen))
return dict(addr=ipv6FromBin(prefix + bin((k << 14) + partition_index+1)[2:].zfill(16) + s * (128 - prefixlen)), prefixlen=prefixlen)
return dict(
addr=ipv6FromBin(prefix + bin((k << (n-2)) + partition_index+1)[2:].zfill(n) + s * (128 - prefixlen)),
prefixlen=prefixlen)
return getIpv6Range
getPartitionIpv6Range = getIpv6RangeFactory(1, '0')
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment