Commit c000b130 authored by Thomas Gambier's avatar Thomas Gambier 🚴🏼

Make IPv6 on tap optionnal. Also put only 1 IPv6 address on the tap itself.

If we put the whole /80 or /96 network on the tap, we can have a dadfailed (dad = "Duplicate Address Detection") inside the VM.
parent 812d00c4
Changes
=======
1.4.16 (???)
-------------------
* add option "tap_ipv6" in cfg file. By default it is true meaning that the taps will have an IPv6 address and an IPv6 subnetwork will be routed to this tap. Put it to false to have only IPv4 on taps. Of course, this option has no purpose if create_tap is False.
1.4.15 (2018-12-11)
-------------------
......
......@@ -20,6 +20,8 @@ log_file = /opt/slapos/log/slapos-node-format.log
partition_base_name = slappart
user_base_name = slapuser
tap_base_name = slaptap
# Change "tap_ipv6" into "false" if you don't want the tap to have IPv6 addresses
tap_ipv6 = true
# You can choose any other local network which does not conflict with your
# current machine configuration
ipv4_local_network = 10.0.0.0/16
......
......@@ -246,7 +246,7 @@ class Computer(object):
def __init__(self, reference, interface=None, addr=None, netmask=None,
ipv6_interface=None, software_user='slapsoft',
tap_gateway_interface=None,
tap_gateway_interface=None, tap_ipv6=None,
instance_root=None, software_root=None, instance_storage_home=None,
partition_list=None, config=None):
"""
......@@ -264,6 +264,7 @@ class Computer(object):
self.ipv6_interface = ipv6_interface
self.software_user = software_user
self.tap_gateway_interface = tap_gateway_interface
self.tap_ipv6 = tap_ipv6
# Used to be static attributes of the class object - didn't make sense (Marco again)
assert instance_root is not None and software_root is not None, \
......@@ -402,7 +403,7 @@ class Computer(object):
@classmethod
def load(cls, path_to_xml, reference, ipv6_interface, tap_gateway_interface,
instance_root=None, software_root=None, config=None):
tap_ipv6, instance_root=None, software_root=None, config=None):
"""
Create a computer object from a valid xml file.
......@@ -424,6 +425,7 @@ class Computer(object):
ipv6_interface=ipv6_interface,
software_user=dumped_dict.get('software_user', 'slapsoft'),
tap_gateway_interface=tap_gateway_interface,
tap_ipv6=tap_ipv6,
software_root=dumped_dict.get('software_root', software_root),
instance_root=dumped_dict.get('instance_root', instance_root),
config=config,
......@@ -612,24 +614,32 @@ class Computer(object):
partition.tap.ipv4_gateway = gateway_addr_dict['addr']
partition.tap.ipv4_network = gateway_addr_dict['network']
if not partition.tap.ipv6_addr:
# create a new IPv6 randomly for the tap
ipv6_dict = self.interface.addIPv6Address(tap=partition.tap)
partition.tap.ipv6_addr = ipv6_dict['addr']
partition.tap.ipv6_netmask = ipv6_dict['netmask']
if self.tap_ipv6:
if not partition.tap.ipv6_addr:
# create a new IPv6 randomly for the tap
ipv6_dict = self.interface.addIPv6Address(tap=partition.tap)
partition.tap.ipv6_addr = ipv6_dict['addr']
partition.tap.ipv6_netmask = ipv6_dict['netmask']
else:
# make sure the tap has its IPv6
self.interface.addIPv6Address(
addr=partition.tap.ipv6_addr,
netmask=partition.tap.ipv6_netmask,
tap=partition.tap)
# construct ipv6_network (16 bit more than the computer network)
netmask_len = lenNetmaskIpv6(self.interface.getGlobalScopeAddressList()[0]['netmask']) + 16
prefix = binFromIpv6(partition.tap.ipv6_addr)[:netmask_len]
network_addr = ipv6FromBin(prefix)
partition.tap.ipv6_gateway = partition.tap.ipv6_addr
partition.tap.ipv6_network = "{}/{}".format(network_addr, netmask_len)
else:
# make sure the tap has its IPv6
self.interface.addIPv6Address(
addr=partition.tap.ipv6_addr,
netmask=partition.tap.ipv6_netmask,
tap=partition.tap)
# construct ipv6_network and create routes
netmask_len = lenNetmaskIpv6(partition.tap.ipv6_netmask)
prefix = binFromIpv6(partition.tap.ipv6_addr)[:netmask_len]
network_addr = ipv6FromBin(prefix)
partition.tap.ipv6_gateway = partition.tap.ipv6_addr
partition.tap.ipv6_network = "{}/{}".format(network_addr, netmask_len)
partition.tap.ipv6_addr = ''
partition.tap.ipv6_netmask = ''
partition.tap.ipv6_gateway = ''
partition.tap.ipv6_network = ''
# create IPv4 and IPv6 routes
partition.tap.createRoutes()
if partition.tun is not None:
......@@ -878,19 +888,25 @@ class Tap(object):
def createRoutes(self):
"""
Configure ipv4 route to reach this interface from local network
Configure ipv4 and ipv6 routes
"""
if self.ipv4_addr:
# Check if this route exits
code, result = callAndRead(['ip', 'route', 'show', self.ipv4_addr],
raise_on_error=False)
if code == 0 and self.ipv4_addr in result and self.name in result:
return
callAndRead(['ip', 'route', 'add', self.ipv4_addr, 'dev', self.name])
if code != 0 or self.ipv4_addr not in result or self.name not in result:
callAndRead(['ip', 'route', 'add', self.ipv4_addr, 'dev', self.name])
else:
raise ValueError("%s should not be empty. No ipv4 address assigned to %s" %
(self.ipv4_addr, self.name))
if self.ipv6_network:
# Check if this route exits
code, result = callAndRead(['ip', '-6', 'route', 'show', self.ipv6_network],
raise_on_error=False)
if code != 0 or self.ipv6_network not in result or self.name not in result:
callAndRead(['ip', '-6', 'route', 'add', self.ipv6_network, 'dev', self.name])
class Tun(Tap):
"""Represent TUN interface which might be many per user."""
......@@ -1146,7 +1162,7 @@ class Interface(object):
# confirmed to be configured
return dict_addr_netmask
if netmask == address_dict['netmask'] or \
(tap and lenNetmaskIpv6(netmask) == lenNetmaskIpv6(address_dict['netmask']) + 16):
(tap and lenNetmaskIpv6(netmask) == 128):
# same netmask, so there is a chance to add good one
interface_network = netaddr.ip.IPNetwork('%s/%s' % (address_dict['addr'],
netmaskToPrefixIPv6(address_dict['netmask'])))
......@@ -1175,7 +1191,7 @@ class Interface(object):
if netmask_len >= 128:
self._logger.error('Interface %s has netmask %s which is too big for generating IPv6 on taps.' % (interface_name, netmask))
raise AddressGenerationError(addr)
netmask = ipv6FromBin('1'*netmask_len)
netmask = ipv6FromBin('1'*128) # the netmask of the tap itself is always 128 bits
while try_num > 0:
if tap:
......@@ -1221,6 +1237,7 @@ def parse_computer_definition(conf, definition_path):
ipv6_interface=conf.ipv6_interface,
software_user=computer_definition.get('computer', 'software_user'),
tap_gateway_interface=conf.tap_gateway_interface,
tap_ipv6=conf.tap_ipv6,
software_root=conf.software_root,
instance_root=conf.instance_root
)
......@@ -1259,6 +1276,7 @@ def parse_computer_xml(conf, xml_path):
reference=conf.computer_id,
ipv6_interface=conf.ipv6_interface,
tap_gateway_interface=conf.tap_gateway_interface,
tap_ipv6=conf.tap_ipv6,
software_root=conf.software_root,
instance_root=conf.instance_root,
config=conf)
......@@ -1277,6 +1295,7 @@ def parse_computer_xml(conf, xml_path):
ipv6_interface=conf.ipv6_interface,
software_user=conf.software_user,
tap_gateway_interface=conf.tap_gateway_interface,
tap_ipv6=conf.tap_ipv6,
config=conf,
)
......@@ -1389,8 +1408,9 @@ class FormatConfig(object):
create_tap = True
create_tun = False
tap_base_name = None
ipv4_local_network = None
tap_ipv6 = True
tap_gateway_interface = ''
ipv4_local_network = None
use_unique_local_address_block = False
# User options
......@@ -1450,7 +1470,7 @@ class FormatConfig(object):
raise UsageError(message)
# Convert strings to booleans
for option in ['alter_network', 'alter_user', 'create_tap', 'create_tun', 'use_unique_local_address_block']:
for option in ['alter_network', 'alter_user', 'create_tap', 'create_tun', 'use_unique_local_address_block', 'tap_ipv6']:
attr = getattr(self, option)
if isinstance(attr, str):
if attr.lower() == 'true':
......@@ -1489,7 +1509,11 @@ class FormatConfig(object):
if self.dry_run:
self.logger.info("Dry-run mode enabled.")
if self.create_tap:
self.logger.info("Tap creation mode enabled.")
if self.tap_ipv6:
with_ipv6 = "with"
else:
with_ipv6 = "without"
self.logger.info("Tap creation mode enabled (%s IPv6)." % with_ipv6)
# Calculate path once
self.computer_xml = os.path.abspath(self.computer_xml)
......
......@@ -121,6 +121,11 @@ class FakeCallAndRead:
retval = 0, str(INTERFACE_DICT)
elif argument_list[:3] == ['ip', 'route', 'show']:
retval = 0, 'OK'
elif argument_list[:3] == ['ip', '-6', 'route']:
retval = 0, 'OK'
ip = argument_list[4]
netmask = int(ip.split('/')[1])
argument_list[4] = 'ip/%s' % netmask
elif argument_list[:3] == ['route', 'add', '-host']:
retval = 0, 'OK'
self.external_command_list.append(' '.join(argument_list))
......@@ -448,6 +453,7 @@ class TestComputer(SlapformatMixin):
computer = slapos.format.Computer('computer',
instance_root='/instance_root',
software_root='/software_root',
tap_ipv6=True,
interface=slapos.format.Interface(
logger=self.logger, name='myinterface', ipv4_local_network='127.0.0.1/16'),
partition_list=[
......@@ -479,10 +485,12 @@ class TestComputer(SlapformatMixin):
self.assertEqual([
'ip tuntap add dev tap mode tap user testuser',
'ip link set tap up',
'ip addr add ip/ffff:ffff:ffff:ffff:ffff:: dev tap',
'ip addr add ip/ffff:ffff:ffff:ffff:ffff:ffff:ffff:ffff dev tap',
'ip -6 addr list tap',
'ip route show 10.0.0.2',
'ip route add 10.0.0.2 dev tap',
'ip -6 route show ip/80',
'ip -6 route add ip/80 dev tap',
'ip addr add ip/255.255.255.255 dev myinterface',
# 'ip addr list myinterface',
'ip addr add ip/ffff:ffff:ffff:ffff:: dev myinterface',
......@@ -495,6 +503,7 @@ class TestComputer(SlapformatMixin):
instance_root='/instance_root',
software_root='/software_root',
tap_gateway_interface='eth1',
tap_ipv6=True,
interface=slapos.format.Interface(
logger=self.logger, name='iface', ipv4_local_network='127.0.0.1/16'),
partition_list=[
......@@ -508,7 +517,7 @@ class TestComputer(SlapformatMixin):
INTERFACE_DICT['iface'] = {
socket.AF_INET: [{'addr': '192.168.242.77', 'broadcast': '127.0.0.1',
'netmask': '255.255.255.0'}],
socket.AF_INET6: [{'addr': '2a01:e35:2e27:3456::e59c', 'netmask': 'ffff:ffff:ffff:ffff:ffff::'}]
socket.AF_INET6: [{'addr': '2a01:e35:2e27:3456:1357::e59c', 'netmask': 'ffff:ffff:ffff:ffff:ffff::'}]
}
INTERFACE_DICT['eth1'] = {
socket.AF_INET: [{'addr': '10.8.0.1', 'broadcast': '10.8.0.254',
......@@ -516,7 +525,7 @@ class TestComputer(SlapformatMixin):
}
INTERFACE_DICT['tap'] = {
socket.AF_INET6: [{'addr': '2a01:e35:2e27:3456::e59c', 'netmask': 'ffff:ffff:ffff:ffff:ffff::'}]
socket.AF_INET6: [{'addr': '2a01:e35:2e27:3456:1357:7890:ffff:ffff', 'netmask': 'ffff:ffff:ffff:ffff:ffff:ffff:ffff'}]
}
computer.format(alter_user=False)
......@@ -531,10 +540,12 @@ class TestComputer(SlapformatMixin):
self.assertEqual([
'ip tuntap add dev tap mode tap user testuser',
'ip link set tap up',
'ip addr add ip/ffff:ffff:ffff:ffff:ffff:ffff:: dev tap',
'ip addr add ip/ffff:ffff:ffff:ffff:ffff:ffff:ffff:ffff dev tap',
'ip -6 addr list tap',
'ip route show 10.8.0.2',
'ip route add 10.8.0.2 dev tap',
'ip -6 route show ip/96',
'ip -6 route add ip/96 dev tap',
'ip addr add ip/255.255.255.255 dev iface',
'ip addr add ip/ffff:ffff:ffff:ffff:ffff:: dev iface',
'ip -6 addr list iface'
......
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