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