From 0f076cfc2d59605bdb5e66241a98ed5fa1fdddeb Mon Sep 17 00:00:00 2001 From: Sean Redmond Date: Mon, 29 May 2017 10:50:27 +0100 Subject: [PATCH] Do not respond to ARP on IPv6-only interfaces When using dual stack, the IPv6 router interface responds to ARP requests that only the IPv4 interface should. This results in ARP flux and can cause a guest to address packets to the wrong layer-2 address when sending traffic to the IPv4 gateway. Change arp_ignore and arp_announce sysctl options on interfaces in the router namespace to be more strict in how we respond. Closes-bug: 1692007 Change-Id: Ic3c2370995abb027a3412b473ce6bc63790c1105 --- neutron/agent/l3/namespaces.py | 10 ++++++++++ neutron/tests/unit/agent/l3/test_agent.py | 17 +++++++++++++++++ neutron/tests/unit/agent/l3/test_dvr_fip_ns.py | 6 +++--- 3 files changed, 30 insertions(+), 3 deletions(-) diff --git a/neutron/agent/l3/namespaces.py b/neutron/agent/l3/namespaces.py index 6db124e805b..9be4efc6689 100644 --- a/neutron/agent/l3/namespaces.py +++ b/neutron/agent/l3/namespaces.py @@ -90,9 +90,19 @@ class Namespace(object): self.use_ipv6 = use_ipv6 def create(self): + # See networking (netdev) tree, file + # Documentation/networking/ip-sysctl.txt for an explanation of + # these sysctl values. ip_wrapper = self.ip_wrapper_root.ensure_namespace(self.name) cmd = ['sysctl', '-w', 'net.ipv4.ip_forward=1'] ip_wrapper.netns.execute(cmd) + # 1. Reply only if the target IP address is local address configured + # on the incoming interface; and + # 2. Always use the best local address + cmd = ['sysctl', '-w', 'net.ipv4.conf.all.arp_ignore=1'] + ip_wrapper.netns.execute(cmd) + cmd = ['sysctl', '-w', 'net.ipv4.conf.all.arp_announce=2'] + ip_wrapper.netns.execute(cmd) if self.use_ipv6: cmd = ['sysctl', '-w', 'net.ipv6.conf.all.forwarding=1'] ip_wrapper.netns.execute(cmd) diff --git a/neutron/tests/unit/agent/l3/test_agent.py b/neutron/tests/unit/agent/l3/test_agent.py index 2d016d2b9aa..b70f37e3252 100644 --- a/neutron/tests/unit/agent/l3/test_agent.py +++ b/neutron/tests/unit/agent/l3/test_agent.py @@ -2105,6 +2105,23 @@ class TestBasicRouterOperations(BasicRouterOperationsFramework): agent.router_added_to_agent(None, [FAKE_ID]) self.assertEqual(1, agent._queue.add.call_count) + def test_create_router_namespace(self): + self.mock_ip.ensure_namespace.return_value = self.mock_ip + agent = l3_agent.L3NATAgent(HOSTNAME, self.conf) + ns = namespaces.Namespace( + 'qrouter-bar', self.conf, agent.driver, agent.use_ipv6) + ns.create() + + calls = [mock.call(['sysctl', '-w', 'net.ipv4.ip_forward=1']), + mock.call(['sysctl', '-w', 'net.ipv4.conf.all.arp_ignore=1']), + mock.call( + ['sysctl', '-w', 'net.ipv4.conf.all.arp_announce=2'])] + if agent.use_ipv6: + calls.append(mock.call( + ['sysctl', '-w', 'net.ipv6.conf.all.forwarding=1'])) + + self.mock_ip.netns.execute.assert_has_calls(calls) + def test_destroy_namespace(self): namespace = 'qrouter-bar' diff --git a/neutron/tests/unit/agent/l3/test_dvr_fip_ns.py b/neutron/tests/unit/agent/l3/test_dvr_fip_ns.py index 2feebd75058..d5017c044c4 100644 --- a/neutron/tests/unit/agent/l3/test_dvr_fip_ns.py +++ b/neutron/tests/unit/agent/l3/test_dvr_fip_ns.py @@ -191,9 +191,9 @@ class TestDvrFipNs(base.BaseTestCase): @mock.patch.object(ip_lib.IpNetnsCommand, 'exists') def _test_create(self, old_kernel, exists, execute, IPTables): exists.return_value = True - # There are up to four sysctl calls - two to enable forwarding, - # and two for ip_nonlocal_bind - execute.side_effect = [None, None, + # There are up to six sysctl calls - two to enable forwarding, + # two for arp_ignore and arp_announce, and two for ip_nonlocal_bind + execute.side_effect = [None, None, None, None, RuntimeError if old_kernel else None, None] self.fip_ns._iptables_manager = IPTables()