diff --git a/neutron/agent/l3/router_info.py b/neutron/agent/l3/router_info.py index 2706db046de..09a82db93b2 100644 --- a/neutron/agent/l3/router_info.py +++ b/neutron/agent/l3/router_info.py @@ -17,6 +17,8 @@ import netaddr from neutron_lib import constants as lib_constants from oslo_log import log as logging +import six + from neutron._i18n import _, _LE, _LW from neutron.agent.l3 import namespaces from neutron.agent.linux import ip_lib @@ -52,6 +54,7 @@ class RouterInfo(object): self._snat_enabled = None self.fip_map = {} self.internal_ports = [] + self.pd_subnets = {} self.floating_ips = set() # Invoke the setter for establishing initial SNAT action self.router = router @@ -212,6 +215,19 @@ class RouterInfo(object): self.iptables_manager.apply() + def _process_pd_iptables_rules(self, prefix, subnet_id): + """Configure iptables rules for prefix delegated subnets""" + ext_scope = self._get_external_address_scope() + ext_scope_mark = self.get_address_scope_mark_mask(ext_scope) + ex_gw_device = self.get_external_device_name( + self.get_ex_gw_port()['id']) + scope_rule = self.address_scope_mangle_rule(ex_gw_device, + ext_scope_mark) + self.iptables_manager.ipv6['mangle'].add_rule( + 'scope', + '-d %s ' % prefix + scope_rule, + tag=('prefix_delegation_%s' % subnet_id)) + def process_floating_ip_address_scope_rules(self): """Configure address scope related iptables rules for the router's floating IPs. @@ -527,6 +543,7 @@ class RouterInfo(object): for subnet in p['subnets']: if ipv6_utils.is_ipv6_pd_enabled(subnet): pd.disable_subnet(self.router_id, subnet['id']) + del self.pd_subnets[subnet['id']] updated_cidrs = [] if updated_ports: @@ -554,6 +571,7 @@ class RouterInfo(object): subnet['cidr'], old_prefix, updated_cidrs) + self.pd_subnets[subnet['id']] = subnet['cidr'] enable_ra = True # Enable RA @@ -986,6 +1004,9 @@ class RouterInfo(object): iptables['filter'].add_rule( 'scope', self.address_scope_filter_rule(device_name, mark)) + for subnet_id, prefix in six.iteritems(self.pd_subnets): + if prefix != n_const.PROVISIONAL_IPV6_PD_PREFIX: + self._process_pd_iptables_rules(prefix, subnet_id) def process_ports_address_scope_iptables(self): ports_scopemark = self._get_address_scope_mark() diff --git a/neutron/tests/unit/agent/l3/test_agent.py b/neutron/tests/unit/agent/l3/test_agent.py index 5fe7b579427..47177e59be5 100644 --- a/neutron/tests/unit/agent/l3/test_agent.py +++ b/neutron/tests/unit/agent/l3/test_agent.py @@ -2570,6 +2570,8 @@ class TestBasicRouterOperations(BasicRouterOperationsFramework): router = l3_test_common.prepare_router_data() ri = l3router.RouterInfo(router['id'], router, **self.ri_kwargs) agent = l3_agent.L3NATAgent(HOSTNAME, self.conf) + ri.iptables_manager.ipv6['mangle'] = mock.MagicMock() + ri._process_pd_iptables_rules = mock.MagicMock() agent.external_gateway_added = mock.Mock() ri.process(agent) agent._router_added(router['id'], router) @@ -2697,9 +2699,11 @@ class TestBasicRouterOperations(BasicRouterOperationsFramework): prefixes = {} expected_pd_update = {} expected_calls = [] + last_prefix = '' for ifno, intf in enumerate(existing_intfs + new_intfs): requestor_id = self._pd_get_requestor_id(intf, router, ri) prefixes[requestor_id] = "2001:cafe:cafe:%d::/64" % ifno + last_prefix = prefixes[requestor_id] if intf in new_intfs: subnet_id = (intf['subnets'][0]['id'] if intf['subnets'] else None) @@ -2737,6 +2741,8 @@ class TestBasicRouterOperations(BasicRouterOperationsFramework): self.external_process.mock_calls[-len(expected_calls):]) self.assertEqual(expected_pd_update, self.pd_update) + return last_prefix + def _pd_add_gw_interface(self, agent, router, ri): gw_ifname = ri.get_external_device_name(router['gw_port']['id']) agent.pd.add_gw_interface(router['id'], gw_ifname) @@ -2758,6 +2764,7 @@ class TestBasicRouterOperations(BasicRouterOperationsFramework): # Create one pd-enabled subnet and add router interface intfs = l3_test_common.router_append_pd_enabled_subnet(router) + subnet_id = intfs[0]['subnets'][0]['id'] ri.process(agent) # No client should be started since there is no gateway port @@ -2768,7 +2775,8 @@ class TestBasicRouterOperations(BasicRouterOperationsFramework): self._pd_add_gw_interface(agent, router, ri) # Get one prefix - self._pd_get_prefixes(agent, router, ri, [], intfs, mock_get_prefix) + prefix = self._pd_get_prefixes(agent, router, ri, [], + intfs, mock_get_prefix) # Update the router with the new prefix ri.process(agent) @@ -2777,8 +2785,14 @@ class TestBasicRouterOperations(BasicRouterOperationsFramework): # with the new prefix self._pd_assert_radvd_calls(ri) + # Check that _process_pd_iptables_rules() is called correctly + self.assertEqual({subnet_id: prefix}, ri.pd_subnets) + ri._process_pd_iptables_rules.assert_called_once_with(prefix, + subnet_id) + # Now remove the interface self._pd_remove_interfaces(intfs, agent, router, ri) + self.assertEqual({}, ri.pd_subnets) @mock.patch.object(dibbler.PDDibbler, 'get_prefix', autospec=True) @mock.patch.object(dibbler.os, 'getpid', return_value=1234) diff --git a/neutron/tests/unit/agent/l3/test_router_info.py b/neutron/tests/unit/agent/l3/test_router_info.py index ee05f2d7cda..62f84282fde 100644 --- a/neutron/tests/unit/agent/l3/test_router_info.py +++ b/neutron/tests/unit/agent/l3/test_router_info.py @@ -122,6 +122,28 @@ class TestRouterInfo(base.BaseTestCase): 'via', '10.100.10.30']] self._check_agent_method_called(expected) + def test__process_pd_iptables_rules(self): + subnet_id = _uuid() + ex_gw_port = {'id': _uuid()} + prefix = '2001:db8:cafe::/64' + + ri = router_info.RouterInfo(_uuid(), {}, **self.ri_kwargs) + + ipv6_mangle = ri.iptables_manager.ipv6['mangle'] = mock.MagicMock() + ri.get_ex_gw_port = mock.Mock(return_value=ex_gw_port) + ri.get_external_device_name = mock.Mock(return_value='fake_device') + ri.get_address_scope_mark_mask = mock.Mock(return_value='fake_mark') + + ri._process_pd_iptables_rules(prefix, subnet_id) + + mangle_rule = '-d %s ' % prefix + mangle_rule += ri.address_scope_mangle_rule('fake_device', 'fake_mark') + + ipv6_mangle.add_rule.assert_called_once_with( + 'scope', + mangle_rule, + tag='prefix_delegation_%s' % subnet_id) + def test_add_ports_address_scope_iptables(self): ri = router_info.RouterInfo(_uuid(), {}, **self.ri_kwargs) port = {