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.

Change-Id: I42f8f42995e9809e5bda2b29726f7244c052ca1c
Closes-Bug: #1570122
This commit is contained in:
John Davidge 2016-12-05 12:32:19 +00:00
parent ca8bc94331
commit cd38886d20
3 changed files with 58 additions and 1 deletions

View File

@ -18,6 +18,8 @@ from neutron_lib import constants as lib_constants
from neutron_lib.utils import helpers
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
@ -55,6 +57,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
@ -215,6 +218,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.
@ -530,6 +546,7 @@ class RouterInfo(object):
for subnet in p['subnets']:
if ipv6_utils.is_ipv6_pd_enabled(subnet):
self.agent.pd.disable_subnet(self.router_id, subnet['id'])
del self.pd_subnets[subnet['id']]
updated_cidrs = []
if updated_ports:
@ -558,6 +575,7 @@ class RouterInfo(object):
subnet['cidr'],
old_prefix,
updated_cidrs)
self.pd_subnets[subnet['id']] = subnet['cidr']
enable_ra = True
# Enable RA
@ -994,6 +1012,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()

View File

@ -2541,6 +2541,8 @@ class TestBasicRouterOperations(BasicRouterOperationsFramework):
agent = l3_agent.L3NATAgent(HOSTNAME, self.conf)
ri = l3router.RouterInfo(agent, router['id'],
router, **self.ri_kwargs)
ri.iptables_manager.ipv6['mangle'] = mock.MagicMock()
ri._process_pd_iptables_rules = mock.MagicMock()
agent.external_gateway_added = mock.Mock()
ri.process()
agent._router_added(router['id'], router)
@ -2668,9 +2670,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)
@ -2708,6 +2712,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)
@ -2729,6 +2735,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()
# No client should be started since there is no gateway port
@ -2739,7 +2746,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()
@ -2748,8 +2756,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)

View File

@ -123,6 +123,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(mock.Mock(), _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(mock.Mock(), _uuid(), {}, **self.ri_kwargs)
port = {