blueprint ipv6-router (ChangeID:Iaefa95f788053ded9fc9c7ff6845c3030c6fd6df),
supports an IPv6 Router where the router gateway port has no subnet.
The BP implements the following. If an external network (without any subnet)
is attached to the Neutron router, it reads the ipv6_gateway config parameter
(LLA of upstream router) from l3_agent.ini file and adds a default route that
points to this LLA. If the ipv6_gateway config value is not configured, it
would configure the gateway interface to accept router advts from upstream
router to build the default route. For an HA router, we would have to
configure keepalived to perform this operation.
This patch is a bug fix for the broken feature in kilo.
Conflicts:
neutron/agent/l3/ha_router.py
neutron/tests/unit/agent/l3/test_agent.py
Implements: blueprint ipv6-router
Change-Id: I26dc5ce9e46c74423358aa8a9559bc6c7cbdf85e
(cherry picked from commit
89489d2720c80c3465e36dad566aa835215fb92e)
self.routes = new_routes
def _add_default_gw_virtual_route(self, ex_gw_port, interface_name):
- subnets = ex_gw_port.get('subnets', [])
- for subnet in subnets:
- gw_ip = subnet['gateway_ip']
- if gw_ip:
+ gateway_ips, enable_ra_on_gw = self._get_external_gw_ips(ex_gw_port)
+ for gw_ip in gateway_ips:
# TODO(Carl) This is repeated everywhere. A method would
# be nice.
default_gw = (n_consts.IPv4_ANY if
keepalived.KeepalivedVirtualRoute(
default_gw, gw_ip, interface_name))
+ if enable_ra_on_gw:
+ self.driver.configure_ipv6_ra(self.ns_name, interface_name)
+
def _should_delete_ipv6_lladdr(self, ipv6_lladdr):
"""Only the master should have any IP addresses configured.
Let keepalived manage IPv6 link local addresses, the same way we let
namespace=ns_name,
prefix=EXTERNAL_DEV_PREFIX)
- def _external_gateway_added(self, ex_gw_port, interface_name,
- ns_name, preserve_ips):
- self._plug_external_gateway(ex_gw_port, interface_name, ns_name)
-
- # Build up the interface and gateway IP addresses that
- # will be added to the interface.
- ip_cidrs = common_utils.fixed_ip_cidrs(ex_gw_port['fixed_ips'])
+ def _get_external_gw_ips(self, ex_gw_port):
gateway_ips = []
enable_ra_on_gw = False
if 'subnets' in ex_gw_port:
# ipv6_gateway is also not configured.
# Use RA for default route.
enable_ra_on_gw = True
+ return gateway_ips, enable_ra_on_gw
+
+ def _external_gateway_added(self, ex_gw_port, interface_name,
+ ns_name, preserve_ips):
+ self._plug_external_gateway(ex_gw_port, interface_name, ns_name)
+
+ # Build up the interface and gateway IP addresses that
+ # will be added to the interface.
+ ip_cidrs = common_utils.fixed_ip_cidrs(ex_gw_port['fixed_ips'])
+
+ gateway_ips, enable_ra_on_gw = self._get_external_gw_ips(ex_gw_port)
self.driver.init_l3(interface_name,
ip_cidrs,
namespace=ns_name,
device.route.add_gateway(gateway_ip)
if enable_ra_on_gw:
- self._configure_ipv6_ra(namespace, device_name)
+ self.configure_ipv6_ra(namespace, device_name)
new_onlink_routes = set(s['cidr'] for s in extra_subnets)
existing_onlink_routes = set(
return (self.DEV_NAME_PREFIX + port.id)[:self.DEV_NAME_LEN]
@staticmethod
- def _configure_ipv6_ra(namespace, dev_name):
+ def configure_ipv6_ra(namespace, dev_name):
"""Configure acceptance of IPv6 route advertisements on an intf."""
# Learn the default router's IP address via RAs
ip_lib.IPWrapper(namespace=namespace).netns.execute(
enable_fip = False
extra_routes = False
- if not v6_ext_gw_with_sub:
- self.agent.conf.set_override('ipv6_gateway',
- 'fe80::f816:3eff:fe2e:1')
return test_l3_agent.prepare_router_data(ip_version=ip_version,
enable_snat=enable_snat,
enable_floating_ip=enable_fip,
self._router_lifecycle(enable_ha=False, dual_stack=True)
def test_legacy_router_lifecycle_with_no_gateway_subnet(self):
+ self.agent.conf.set_override('ipv6_gateway',
+ 'fe80::f816:3eff:fe2e:1')
self._router_lifecycle(enable_ha=False, dual_stack=True,
v6_ext_gw_with_sub=False)
def test_ipv6_ha_router_lifecycle(self):
self._router_lifecycle(enable_ha=True, ip_version=6)
+ def test_ipv6_ha_router_lifecycle_with_no_gw_subnet(self):
+ self.agent.conf.set_override('ipv6_gateway',
+ 'fe80::f816:3eff:fe2e:1')
+ self._router_lifecycle(enable_ha=True, ip_version=6,
+ v6_ext_gw_with_sub=False)
+
+ def test_ipv6_ha_router_lifecycle_with_no_gw_subnet_for_router_advts(self):
+ # Verify that router gw interface is configured to receive Router
+ # Advts from upstream router when no external gateway is configured.
+ self._router_lifecycle(enable_ha=True, dual_stack=True,
+ v6_ext_gw_with_sub=False)
+
def test_keepalived_configuration(self):
router_info = self.generate_router_info(enable_ha=True)
router = self.manage_router(self.agent, router_info)
self._assert_extra_routes(router)
self._assert_metadata_chains(router)
+ # Verify router gateway interface is configured to receive Router Advts
+ # when IPv6 is enabled and no IPv6 gateway is configured.
+ if router.use_ipv6 and not v6_ext_gw_with_sub:
+ if not self.agent.conf.ipv6_gateway:
+ external_port = router.get_ex_gw_port()
+ external_device_name = router.get_external_device_name(
+ external_port['id'])
+ ip_wrapper = ip_lib.IPWrapper(namespace=router.ns_name)
+ ra_state = ip_wrapper.netns.execute(['sysctl', '-b',
+ 'net.ipv6.conf.%s.accept_ra' % external_device_name])
+ self.assertEqual('2', ra_state)
+
if enable_ha:
self._assert_ha_device(router)
self.assertTrue(router.keepalived_manager.get_process().active)
subnets.append({'id': subnet_id,
'cidr': subnet_cidr,
'gateway_ip': gateway_ip})
- if not fixed_ips:
+ if not fixed_ips and v6_ext_gw_with_sub:
raise ValueError("Invalid ip_version: %s" % ip_version)
router_id = _uuid()