From 5288593fafe6636fc14b8873465866d20de26935 Mon Sep 17 00:00:00 2001 From: Damian Dabrowski Date: Thu, 28 Apr 2022 02:54:25 +0200 Subject: [PATCH] [L3-HA] Disable automatic link-local address assignment for HA routers In order to get both [1] and [2] fixed, we set `net.ipv6.conf.all.addr_gen_mode=1` in HA router namespace to prevent auto-assigning link-local address(lla) to the interfaces. We don't need lla auto-assignment as keepalived manages them. With this change, we will have link-local addresses only on active router, which will prevent 'dadfailed' and MLD packets will not be sent from standby router. Previously we also reverted [3] to always keep qg-* interface up on both active&standby router's instance, no matter if keepalived is started or not. Without link-local address assigned, backup router's instance won't send any packets, so I see no reason to keep qg-* interface down. [1] https://bugs.launchpad.net/neutron/+bug/1952907 [2] https://bugs.launchpad.net/neutron/+bug/1859832 [3] https://review.opendev.org/c/openstack/neutron/+/834162 Closes-Bug: #1952907 Related-Bug: #1859832 Depends-On: https://review.opendev.org/c/openstack/neutron/+/834162 Change-Id: I306f14aa6b7e8bb69a81f441be337bc1a584d3b2 --- neutron/agent/l3/ha_router.py | 5 +++ .../tests/functional/agent/l3/framework.py | 13 ++++++++ .../functional/agent/l3/test_ha_router.py | 31 +++++++++++++++++++ 3 files changed, 49 insertions(+) diff --git a/neutron/agent/l3/ha_router.py b/neutron/agent/l3/ha_router.py index c204f5f9b64..222e1d1df36 100644 --- a/neutron/agent/l3/ha_router.py +++ b/neutron/agent/l3/ha_router.py @@ -63,6 +63,11 @@ class HaRouterNamespace(namespaces.RouterNamespace): super(HaRouterNamespace, self).create(ipv6_forwarding=False) # HA router namespaces should not have ip_nonlocal_bind enabled ip_lib.set_ip_nonlocal_bind_for_namespace(self.name, 0) + # Linux should not automatically assign link-local addr for HA routers + # They are managed by keepalived + ip_wrapper = ip_lib.IPWrapper(namespace=self.name) + cmd = ['sysctl', '-w', 'net.ipv6.conf.all.addr_gen_mode=1'] + ip_wrapper.netns.execute(cmd, privsep_exec=True) class HaRouter(router.RouterInfo): diff --git a/neutron/tests/functional/agent/l3/framework.py b/neutron/tests/functional/agent/l3/framework.py index af476b7085a..a25d52ecf37 100644 --- a/neutron/tests/functional/agent/l3/framework.py +++ b/neutron/tests/functional/agent/l3/framework.py @@ -276,6 +276,19 @@ class L3AgentTestFramework(base.BaseSudoTestCase): def _assert_external_device(self, router): self.assertTrue(self._check_external_device(router)) + def _wait_until_addr_gen_mode_has_state( + self, ns_name, state): + ip_wrapper = ip_lib.IPWrapper(namespace=ns_name) + + def _addr_gen_mode_state(): + addr_gen_mode_state = ip_wrapper.netns.execute( + ['sysctl', '-b', 'net.ipv6.conf.all.addr_gen_mode'], + privsep_exec=True) + return ( + state == int(addr_gen_mode_state)) + + common_utils.wait_until_true(_addr_gen_mode_state) + def _wait_until_ipv6_accept_ra_has_state( self, ns_name, device_name, enabled): ip_wrapper = ip_lib.IPWrapper(namespace=ns_name) diff --git a/neutron/tests/functional/agent/l3/test_ha_router.py b/neutron/tests/functional/agent/l3/test_ha_router.py index 23e519c4e2f..710025c7f2c 100644 --- a/neutron/tests/functional/agent/l3/test_ha_router.py +++ b/neutron/tests/functional/agent/l3/test_ha_router.py @@ -352,6 +352,37 @@ class L3HATestCase(framework.L3AgentTestFramework): raise self.assertEqual(0, ip_nonlocal_bind_value) + @testtools.skipUnless(netutils.is_ipv6_enabled(), "IPv6 is not enabled") + def test_ha_router_addr_gen_mode(self): + router_info = self.generate_router_info(enable_ha=True) + router_info[constants.HA_INTERFACE_KEY]['status'] = ( + constants.PORT_STATUS_DOWN) + router = self.manage_router(self.agent, router_info) + external_port = router.get_ex_gw_port() + external_device_name = router.get_external_device_name( + external_port['id']) + + def check_gw_lla_status(expected): + lladdr = ip_lib.get_ipv6_lladdr( + external_port['mac_address']) + exists = ip_lib.device_exists_with_ips_and_mac( + external_device_name, [lladdr], + external_port['mac_address'], router.ns_name) + self.assertEqual(expected, exists) + + self.wait_until_ha_router_has_state(router, 'backup') + self._wait_until_addr_gen_mode_has_state( + router.ns_name, 1) + check_gw_lla_status(False) + + router.router[constants.HA_INTERFACE_KEY]['status'] = ( + constants.PORT_STATUS_ACTIVE) + self.agent._process_updated_router(router.router) + self.wait_until_ha_router_has_state(router, 'primary') + self._wait_until_addr_gen_mode_has_state( + router.ns_name, 1) + check_gw_lla_status(True) + @testtools.skipUnless(netutils.is_ipv6_enabled(), "IPv6 is not enabled") def test_ha_router_namespace_has_ipv6_forwarding_disabled(self): router_info = self.generate_router_info(enable_ha=True)