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:
parent
ca8bc94331
commit
cd38886d20
|
@ -18,6 +18,8 @@ from neutron_lib import constants as lib_constants
|
||||||
from neutron_lib.utils import helpers
|
from neutron_lib.utils import helpers
|
||||||
from oslo_log import log as logging
|
from oslo_log import log as logging
|
||||||
|
|
||||||
|
import six
|
||||||
|
|
||||||
from neutron._i18n import _, _LE, _LW
|
from neutron._i18n import _, _LE, _LW
|
||||||
from neutron.agent.l3 import namespaces
|
from neutron.agent.l3 import namespaces
|
||||||
from neutron.agent.linux import ip_lib
|
from neutron.agent.linux import ip_lib
|
||||||
|
@ -55,6 +57,7 @@ class RouterInfo(object):
|
||||||
self._snat_enabled = None
|
self._snat_enabled = None
|
||||||
self.fip_map = {}
|
self.fip_map = {}
|
||||||
self.internal_ports = []
|
self.internal_ports = []
|
||||||
|
self.pd_subnets = {}
|
||||||
self.floating_ips = set()
|
self.floating_ips = set()
|
||||||
# Invoke the setter for establishing initial SNAT action
|
# Invoke the setter for establishing initial SNAT action
|
||||||
self.router = router
|
self.router = router
|
||||||
|
@ -215,6 +218,19 @@ class RouterInfo(object):
|
||||||
|
|
||||||
self.iptables_manager.apply()
|
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):
|
def process_floating_ip_address_scope_rules(self):
|
||||||
"""Configure address scope related iptables rules for the router's
|
"""Configure address scope related iptables rules for the router's
|
||||||
floating IPs.
|
floating IPs.
|
||||||
|
@ -530,6 +546,7 @@ class RouterInfo(object):
|
||||||
for subnet in p['subnets']:
|
for subnet in p['subnets']:
|
||||||
if ipv6_utils.is_ipv6_pd_enabled(subnet):
|
if ipv6_utils.is_ipv6_pd_enabled(subnet):
|
||||||
self.agent.pd.disable_subnet(self.router_id, subnet['id'])
|
self.agent.pd.disable_subnet(self.router_id, subnet['id'])
|
||||||
|
del self.pd_subnets[subnet['id']]
|
||||||
|
|
||||||
updated_cidrs = []
|
updated_cidrs = []
|
||||||
if updated_ports:
|
if updated_ports:
|
||||||
|
@ -558,6 +575,7 @@ class RouterInfo(object):
|
||||||
subnet['cidr'],
|
subnet['cidr'],
|
||||||
old_prefix,
|
old_prefix,
|
||||||
updated_cidrs)
|
updated_cidrs)
|
||||||
|
self.pd_subnets[subnet['id']] = subnet['cidr']
|
||||||
enable_ra = True
|
enable_ra = True
|
||||||
|
|
||||||
# Enable RA
|
# Enable RA
|
||||||
|
@ -994,6 +1012,9 @@ class RouterInfo(object):
|
||||||
iptables['filter'].add_rule(
|
iptables['filter'].add_rule(
|
||||||
'scope',
|
'scope',
|
||||||
self.address_scope_filter_rule(device_name, mark))
|
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):
|
def process_ports_address_scope_iptables(self):
|
||||||
ports_scopemark = self._get_address_scope_mark()
|
ports_scopemark = self._get_address_scope_mark()
|
||||||
|
|
|
@ -2541,6 +2541,8 @@ class TestBasicRouterOperations(BasicRouterOperationsFramework):
|
||||||
agent = l3_agent.L3NATAgent(HOSTNAME, self.conf)
|
agent = l3_agent.L3NATAgent(HOSTNAME, self.conf)
|
||||||
ri = l3router.RouterInfo(agent, router['id'],
|
ri = l3router.RouterInfo(agent, router['id'],
|
||||||
router, **self.ri_kwargs)
|
router, **self.ri_kwargs)
|
||||||
|
ri.iptables_manager.ipv6['mangle'] = mock.MagicMock()
|
||||||
|
ri._process_pd_iptables_rules = mock.MagicMock()
|
||||||
agent.external_gateway_added = mock.Mock()
|
agent.external_gateway_added = mock.Mock()
|
||||||
ri.process()
|
ri.process()
|
||||||
agent._router_added(router['id'], router)
|
agent._router_added(router['id'], router)
|
||||||
|
@ -2668,9 +2670,11 @@ class TestBasicRouterOperations(BasicRouterOperationsFramework):
|
||||||
prefixes = {}
|
prefixes = {}
|
||||||
expected_pd_update = {}
|
expected_pd_update = {}
|
||||||
expected_calls = []
|
expected_calls = []
|
||||||
|
last_prefix = ''
|
||||||
for ifno, intf in enumerate(existing_intfs + new_intfs):
|
for ifno, intf in enumerate(existing_intfs + new_intfs):
|
||||||
requestor_id = self._pd_get_requestor_id(intf, router, ri)
|
requestor_id = self._pd_get_requestor_id(intf, router, ri)
|
||||||
prefixes[requestor_id] = "2001:cafe:cafe:%d::/64" % ifno
|
prefixes[requestor_id] = "2001:cafe:cafe:%d::/64" % ifno
|
||||||
|
last_prefix = prefixes[requestor_id]
|
||||||
if intf in new_intfs:
|
if intf in new_intfs:
|
||||||
subnet_id = (intf['subnets'][0]['id'] if intf['subnets']
|
subnet_id = (intf['subnets'][0]['id'] if intf['subnets']
|
||||||
else None)
|
else None)
|
||||||
|
@ -2708,6 +2712,8 @@ class TestBasicRouterOperations(BasicRouterOperationsFramework):
|
||||||
self.external_process.mock_calls[-len(expected_calls):])
|
self.external_process.mock_calls[-len(expected_calls):])
|
||||||
self.assertEqual(expected_pd_update, self.pd_update)
|
self.assertEqual(expected_pd_update, self.pd_update)
|
||||||
|
|
||||||
|
return last_prefix
|
||||||
|
|
||||||
def _pd_add_gw_interface(self, agent, router, ri):
|
def _pd_add_gw_interface(self, agent, router, ri):
|
||||||
gw_ifname = ri.get_external_device_name(router['gw_port']['id'])
|
gw_ifname = ri.get_external_device_name(router['gw_port']['id'])
|
||||||
agent.pd.add_gw_interface(router['id'], gw_ifname)
|
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
|
# Create one pd-enabled subnet and add router interface
|
||||||
intfs = l3_test_common.router_append_pd_enabled_subnet(router)
|
intfs = l3_test_common.router_append_pd_enabled_subnet(router)
|
||||||
|
subnet_id = intfs[0]['subnets'][0]['id']
|
||||||
ri.process()
|
ri.process()
|
||||||
|
|
||||||
# No client should be started since there is no gateway port
|
# 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)
|
self._pd_add_gw_interface(agent, router, ri)
|
||||||
|
|
||||||
# Get one prefix
|
# 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
|
# Update the router with the new prefix
|
||||||
ri.process()
|
ri.process()
|
||||||
|
@ -2748,8 +2756,14 @@ class TestBasicRouterOperations(BasicRouterOperationsFramework):
|
||||||
# with the new prefix
|
# with the new prefix
|
||||||
self._pd_assert_radvd_calls(ri)
|
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
|
# Now remove the interface
|
||||||
self._pd_remove_interfaces(intfs, agent, router, ri)
|
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.PDDibbler, 'get_prefix', autospec=True)
|
||||||
@mock.patch.object(dibbler.os, 'getpid', return_value=1234)
|
@mock.patch.object(dibbler.os, 'getpid', return_value=1234)
|
||||||
|
|
|
@ -123,6 +123,28 @@ class TestRouterInfo(base.BaseTestCase):
|
||||||
'via', '10.100.10.30']]
|
'via', '10.100.10.30']]
|
||||||
self._check_agent_method_called(expected)
|
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):
|
def test_add_ports_address_scope_iptables(self):
|
||||||
ri = router_info.RouterInfo(mock.Mock(), _uuid(), {}, **self.ri_kwargs)
|
ri = router_info.RouterInfo(mock.Mock(), _uuid(), {}, **self.ri_kwargs)
|
||||||
port = {
|
port = {
|
||||||
|
|
Loading…
Reference in New Issue