summaryrefslogtreecommitdiff
path: root/cloudinit
diff options
context:
space:
mode:
authorHarald <hjensas@redhat.com>2019-11-20 18:55:27 +0100
committerRyan Harper <ryan.harper@canonical.com>2019-11-20 11:55:26 -0600
commit62bbc262c3c7f633eac1d09ec78c055eef05166a (patch)
tree4b372f78ee1ed1dbd7ede5ff09836e7ce396d457 /cloudinit
parent87bb30c7a282a2006f64205b570da25258f221f1 (diff)
downloadvyos-cloud-init-62bbc262c3c7f633eac1d09ec78c055eef05166a.tar.gz
vyos-cloud-init-62bbc262c3c7f633eac1d09ec78c055eef05166a.zip
net: IPv6, accept_ra, slaac, stateless (#51)
Router advertisements are required for the default route to be set up, thus accept_ra should be enabled for dhcpv6-stateful. sysconf: IPV6_FORCE_ACCEPT_RA controls accept_ra sysctl. eni: mode static and mode dhcp 'accept_ra' controls sysctl. Add 'accept-ra: true|false' parameter to config v1 and v2. When True: accept_ra is set to '1'. When False: accept_ra is set to '0'. When not defined in config the value is left to the operating system default. This change also extend the IPv6 support to distinguish between slaac and dhcpv6-stateless. SLAAC is autoconfig without any options from DHCP, while stateless auto-configures the address and the uses DHCP for other options. LP: #1806014 LP: #1808647
Diffstat (limited to 'cloudinit')
-rw-r--r--cloudinit/net/eni.py15
-rw-r--r--cloudinit/net/netplan.py9
-rw-r--r--cloudinit/net/network_state.py21
-rw-r--r--cloudinit/net/sysconfig.py34
-rw-r--r--cloudinit/sources/helpers/openstack.py21
5 files changed, 83 insertions, 17 deletions
diff --git a/cloudinit/net/eni.py b/cloudinit/net/eni.py
index a9a80c95..70771060 100644
--- a/cloudinit/net/eni.py
+++ b/cloudinit/net/eni.py
@@ -399,6 +399,7 @@ class Renderer(renderer.Renderer):
def _render_iface(self, iface, render_hwaddress=False):
sections = []
subnets = iface.get('subnets', {})
+ accept_ra = iface.pop('accept-ra', None)
if subnets:
for index, subnet in enumerate(subnets):
ipv4_subnet_mtu = None
@@ -415,9 +416,23 @@ class Renderer(renderer.Renderer):
subnet['type'] == 'ipv6_dhcpv6-stateful'):
# Configure network settings using DHCP or DHCPv6
iface['mode'] = 'dhcp'
+ if accept_ra is not None:
+ # Accept router advertisements (0=off, 1=on)
+ iface['accept_ra'] = '1' if accept_ra else '0'
elif subnet['type'] == 'ipv6_dhcpv6-stateless':
# Configure network settings using SLAAC from RAs
iface['mode'] = 'auto'
+ # Use stateless DHCPv6 (0=off, 1=on)
+ iface['dhcp'] = '1'
+ elif subnet['type'] == 'ipv6_slaac':
+ # Configure network settings using SLAAC from RAs
+ iface['mode'] = 'auto'
+ # Use stateless DHCPv6 (0=off, 1=on)
+ iface['dhcp'] = '0'
+ elif subnet_is_ipv6(subnet) and subnet['type'] == 'static':
+ if accept_ra is not None:
+ # Accept router advertisements (0=off, 1=on)
+ iface['accept_ra'] = '1' if accept_ra else '0'
# do not emit multiple 'auto $IFACE' lines as older (precise)
# ifupdown complains
diff --git a/cloudinit/net/netplan.py b/cloudinit/net/netplan.py
index 749d46f8..14d3999f 100644
--- a/cloudinit/net/netplan.py
+++ b/cloudinit/net/netplan.py
@@ -4,7 +4,7 @@ import copy
import os
from . import renderer
-from .network_state import subnet_is_ipv6, NET_CONFIG_TO_V2
+from .network_state import subnet_is_ipv6, NET_CONFIG_TO_V2, IPV6_DYNAMIC_TYPES
from cloudinit import log as logging
from cloudinit import util
@@ -52,7 +52,8 @@ def _extract_addresses(config, entry, ifname, features=None):
'mtu': 1480,
'netmask': 64,
'type': 'static'}],
- 'type: physical'
+ 'type: physical',
+ 'accept-ra': 'true'
}
An entry dictionary looks like:
@@ -95,6 +96,8 @@ def _extract_addresses(config, entry, ifname, features=None):
if sn_type == 'dhcp':
sn_type += '4'
entry.update({sn_type: True})
+ elif sn_type in IPV6_DYNAMIC_TYPES:
+ entry.update({'dhcp6': True})
elif sn_type in ['static']:
addr = "%s" % subnet.get('address')
if 'prefix' in subnet:
@@ -147,6 +150,8 @@ def _extract_addresses(config, entry, ifname, features=None):
ns = entry.get('nameservers', {})
ns.update({'search': searchdomains})
entry.update({'nameservers': ns})
+ if 'accept-ra' in config and config['accept-ra'] is not None:
+ entry.update({'accept-ra': util.is_true(config.get('accept-ra'))})
def _extract_bond_slaves_by_name(interfaces, entry, bond_master):
diff --git a/cloudinit/net/network_state.py b/cloudinit/net/network_state.py
index 20b7716b..7d206a1a 100644
--- a/cloudinit/net/network_state.py
+++ b/cloudinit/net/network_state.py
@@ -18,13 +18,17 @@ from cloudinit import util
LOG = logging.getLogger(__name__)
NETWORK_STATE_VERSION = 1
+IPV6_DYNAMIC_TYPES = ['dhcp6',
+ 'ipv6_slaac',
+ 'ipv6_dhcpv6-stateless',
+ 'ipv6_dhcpv6-stateful']
NETWORK_STATE_REQUIRED_KEYS = {
1: ['version', 'config', 'network_state'],
}
NETWORK_V2_KEY_FILTER = [
'addresses', 'dhcp4', 'dhcp4-overrides', 'dhcp6', 'dhcp6-overrides',
'gateway4', 'gateway6', 'interfaces', 'match', 'mtu', 'nameservers',
- 'renderer', 'set-name', 'wakeonlan'
+ 'renderer', 'set-name', 'wakeonlan', 'accept-ra'
]
NET_CONFIG_TO_V2 = {
@@ -342,7 +346,8 @@ class NetworkStateInterpreter(object):
'name': 'eth0',
'subnets': [
{'type': 'dhcp4'}
- ]
+ ],
+ 'accept-ra': 'true'
}
'''
@@ -362,6 +367,9 @@ class NetworkStateInterpreter(object):
self.use_ipv6 = True
break
+ accept_ra = command.get('accept-ra', None)
+ if accept_ra is not None:
+ accept_ra = util.is_true(accept_ra)
iface.update({
'name': command.get('name'),
'type': command.get('type'),
@@ -372,6 +380,7 @@ class NetworkStateInterpreter(object):
'address': None,
'gateway': None,
'subnets': subnets,
+ 'accept-ra': accept_ra
})
self._network_state['interfaces'].update({command.get('name'): iface})
self.dump_network_state()
@@ -615,6 +624,7 @@ class NetworkStateInterpreter(object):
driver: ixgbe
set-name: lom1
dhcp6: true
+ accept-ra: true
switchports:
match:
name: enp2*
@@ -643,7 +653,7 @@ class NetworkStateInterpreter(object):
driver = match.get('driver', None)
if driver:
phy_cmd['params'] = {'driver': driver}
- for key in ['mtu', 'match', 'wakeonlan']:
+ for key in ['mtu', 'match', 'wakeonlan', 'accept-ra']:
if key in cfg:
phy_cmd[key] = cfg[key]
@@ -928,8 +938,9 @@ def is_ipv6_addr(address):
def subnet_is_ipv6(subnet):
"""Common helper for checking network_state subnets for ipv6."""
- # 'static6', 'dhcp6', 'ipv6_dhcpv6-stateful' or 'ipv6_dhcpv6-stateless'
- if subnet['type'].endswith('6') or subnet['type'].startswith('ipv6'):
+ # 'static6', 'dhcp6', 'ipv6_dhcpv6-stateful', 'ipv6_dhcpv6-stateless' or
+ # 'ipv6_slaac'
+ if subnet['type'].endswith('6') or subnet['type'] in IPV6_DYNAMIC_TYPES:
# This is a request for DHCPv6.
return True
elif subnet['type'] == 'static' and is_ipv6_addr(subnet.get('address')):
diff --git a/cloudinit/net/sysconfig.py b/cloudinit/net/sysconfig.py
index fe0c67ca..310cdf01 100644
--- a/cloudinit/net/sysconfig.py
+++ b/cloudinit/net/sysconfig.py
@@ -14,7 +14,7 @@ from configobj import ConfigObj
from . import renderer
from .network_state import (
- is_ipv6_addr, net_prefix_to_ipv4_mask, subnet_is_ipv6)
+ is_ipv6_addr, net_prefix_to_ipv4_mask, subnet_is_ipv6, IPV6_DYNAMIC_TYPES)
LOG = logging.getLogger(__name__)
NM_CFG_FILE = "/etc/NetworkManager/NetworkManager.conf"
@@ -335,6 +335,9 @@ class Renderer(renderer.Renderer):
continue
iface_cfg[new_key] = old_value
+ if iface['accept-ra'] is not None:
+ iface_cfg['IPV6_FORCE_ACCEPT_RA'] = iface['accept-ra']
+
@classmethod
def _render_subnets(cls, iface_cfg, subnets, has_default_route):
# setting base values
@@ -351,6 +354,15 @@ class Renderer(renderer.Renderer):
iface_cfg['DHCPV6C'] = True
elif subnet_type == 'ipv6_dhcpv6-stateless':
iface_cfg['IPV6INIT'] = True
+ # Configure network settings using SLAAC from RAs and optional
+ # info from dhcp server using DHCPv6
+ iface_cfg['IPV6_AUTOCONF'] = True
+ iface_cfg['DHCPV6C'] = True
+ # Use Information-request to get only stateless configuration
+ # parameters (i.e., without address).
+ iface_cfg['DHCPV6C_OPTIONS'] = '-S'
+ elif subnet_type == 'ipv6_slaac':
+ iface_cfg['IPV6INIT'] = True
# Configure network settings using SLAAC from RAs
iface_cfg['IPV6_AUTOCONF'] = True
elif subnet_type in ['dhcp4', 'dhcp']:
@@ -398,10 +410,15 @@ class Renderer(renderer.Renderer):
# metric may apply to both dhcp and static config
if 'metric' in subnet:
iface_cfg['METRIC'] = subnet['metric']
+ # TODO(hjensas): Including dhcp6 here is likely incorrect. DHCPv6
+ # does not ever provide a default gateway, the default gateway
+ # come from RA's. (https://github.com/openSUSE/wicked/issues/570)
if subnet_type in ['dhcp', 'dhcp4', 'dhcp6']:
if has_default_route and iface_cfg['BOOTPROTO'] != 'none':
iface_cfg['DHCLIENT_SET_DEFAULT_ROUTE'] = False
continue
+ elif subnet_type in IPV6_DYNAMIC_TYPES:
+ continue
elif subnet_type == 'static':
if subnet_is_ipv6(subnet):
ipv6_index = ipv6_index + 1
@@ -444,10 +461,14 @@ class Renderer(renderer.Renderer):
@classmethod
def _render_subnet_routes(cls, iface_cfg, route_cfg, subnets):
for _, subnet in enumerate(subnets, start=len(iface_cfg.children)):
+ subnet_type = subnet.get('type')
for route in subnet.get('routes', []):
is_ipv6 = subnet.get('ipv6') or is_ipv6_addr(route['gateway'])
- if _is_default_route(route):
+ # Any dynamic configuration method, slaac, dhcpv6-stateful/
+ # stateless should get router information from router RA's.
+ if (_is_default_route(route) and subnet_type not in
+ IPV6_DYNAMIC_TYPES):
if (
(subnet.get('ipv4') and
route_cfg.has_set_default_ipv4) or
@@ -466,10 +487,17 @@ class Renderer(renderer.Renderer):
# TODO(harlowja): add validation that no other iface has
# also provided the default route?
iface_cfg['DEFROUTE'] = True
+ # TODO(hjensas): Including dhcp6 here is likely incorrect.
+ # DHCPv6 does not ever provide a default gateway, the
+ # default gateway come from RA's.
+ # (https://github.com/openSUSE/wicked/issues/570)
if iface_cfg['BOOTPROTO'] in ('dhcp', 'dhcp4', 'dhcp6'):
+ # NOTE(hjensas): DHCLIENT_SET_DEFAULT_ROUTE is SuSE
+ # only. RHEL, CentOS, Fedora does not implement this
+ # option.
iface_cfg['DHCLIENT_SET_DEFAULT_ROUTE'] = True
if 'gateway' in route:
- if is_ipv6 or is_ipv6_addr(route['gateway']):
+ if is_ipv6:
iface_cfg['IPV6_DEFAULTGW'] = route['gateway']
route_cfg.has_set_default_ipv6 = True
else:
diff --git a/cloudinit/sources/helpers/openstack.py b/cloudinit/sources/helpers/openstack.py
index d1c4601a..0778f45a 100644
--- a/cloudinit/sources/helpers/openstack.py
+++ b/cloudinit/sources/helpers/openstack.py
@@ -584,17 +584,24 @@ def convert_net_json(network_json=None, known_macs=None):
if n['link'] == link['id']]:
subnet = dict((k, v) for k, v in network.items()
if k in valid_keys['subnet'])
- if 'dhcp' in network['type']:
- t = (network['type'] if network['type'].startswith('ipv6')
- else 'dhcp4')
- subnet.update({
- 'type': t,
- })
- else:
+
+ if network['type'] == 'ipv4_dhcp':
+ subnet.update({'type': 'dhcp4'})
+ elif network['type'] == 'ipv6_dhcp':
+ subnet.update({'type': 'dhcp6'})
+ elif network['type'] in ['ipv6_slaac', 'ipv6_dhcpv6-stateless',
+ 'ipv6_dhcpv6-stateful']:
+ subnet.update({'type': network['type']})
+ elif network['type'] in ['ipv4', 'ipv6']:
subnet.update({
'type': 'static',
'address': network.get('ip_address'),
})
+
+ # Enable accept_ra for stateful and legacy ipv6_dhcp types
+ if network['type'] in ['ipv6_dhcpv6-stateful', 'ipv6_dhcp']:
+ cfg.update({'accept-ra': True})
+
if network['type'] == 'ipv4':
subnet['ipv4'] = True
if network['type'] == 'ipv6':