Fix iptables rules for Prefix Delegated subnets
Make sure the correct iptables rule is added when the router gets
an interface on a PD-enabled subnet. This will allow traffic on PD
subnets to reach the external network.
Includes a unit test for the new function, and modifies an
existing test to verify the adding and removal of the rule.
Conflicts:
neutron/agent/l3/router_info.py
neutron/tests/unit/agent/l3/test_agent.py
Change-Id: I42f8f42995e9809e5bda2b29726f7244c052ca1c
Closes-Bug: #1570122
(cherry picked from commit cd38886d20
)
This commit is contained in:
parent
941ec3f4d1
commit
bebedc42e5
@ -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()
|
||||
|
@ -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)
|
||||
|
@ -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 = {
|
||||
|
Loading…
Reference in New Issue
Block a user