From 399f1c1b65b5aad5a810d9986da35d07216fa707 Mon Sep 17 00:00:00 2001 From: Bence Romsics Date: Fri, 11 Jan 2019 16:08:53 +0100 Subject: [PATCH] Always fill UDP checksums in DHCPv6 replies Bug #1244589 re-appeared for IPv6. This change adds an ip6tables rule to fix the checksum of DHCPv6 response packets. Those checksums were left unfilled by virtio (as a hypervisor internal optimization), but some picky dhcp clients (AFAIU particularly ISC dhclient) try verifying the checksums, so they fail to acquire an address if the checksums are left incorrect. Change-Id: I4a045e0dcfcbd3c7959a78f1460d5bf7da0252ff Closes-Bug: #1811639 Related-Bug: #1244589 (cherry picked from commit 26eb2509fea632e67ffabcc15195cc13ee02bf68) --- neutron/agent/linux/dhcp.py | 5 ++++- neutron/common/constants.py | 3 +++ neutron/tests/unit/agent/dhcp/test_agent.py | 17 +++++++++++++---- 3 files changed, 20 insertions(+), 5 deletions(-) diff --git a/neutron/agent/linux/dhcp.py b/neutron/agent/linux/dhcp.py index 88e99d89645..b409d5b9c50 100644 --- a/neutron/agent/linux/dhcp.py +++ b/neutron/agent/linux/dhcp.py @@ -1534,9 +1534,12 @@ class DeviceManager(object): def fill_dhcp_udp_checksums(self, namespace): """Ensure DHCP reply packets always have correct UDP checksums.""" - iptables_mgr = iptables_manager.IptablesManager(use_ipv6=False, + iptables_mgr = iptables_manager.IptablesManager(use_ipv6=True, namespace=namespace) ipv4_rule = ('-p udp -m udp --dport %d -j CHECKSUM --checksum-fill' % constants.DHCP_RESPONSE_PORT) + ipv6_rule = ('-p udp -m udp --dport %d -j CHECKSUM --checksum-fill' + % n_const.DHCPV6_CLIENT_PORT) iptables_mgr.ipv4['mangle'].add_rule('POSTROUTING', ipv4_rule) + iptables_mgr.ipv6['mangle'].add_rule('POSTROUTING', ipv6_rule) iptables_mgr.apply() diff --git a/neutron/common/constants.py b/neutron/common/constants.py index ed17553c7f6..106c8e0f7b1 100644 --- a/neutron/common/constants.py +++ b/neutron/common/constants.py @@ -199,6 +199,9 @@ IP_ALLOWED_VERSIONS = [lib_constants.IP_VERSION_4, lib_constants.IP_VERSION_6] PORT_RANGE_MIN = 1 PORT_RANGE_MAX = 65535 +# TODO(bence romsics): move this to neutron_lib.constants +DHCPV6_CLIENT_PORT = 546 + # Configuration values for accept_ra sysctl, copied from linux kernel # networking (netdev) tree, file Documentation/networking/ip-sysctl.txt # diff --git a/neutron/tests/unit/agent/dhcp/test_agent.py b/neutron/tests/unit/agent/dhcp/test_agent.py index 41f9fcc60ff..24e08ba16e7 100644 --- a/neutron/tests/unit/agent/dhcp/test_agent.py +++ b/neutron/tests/unit/agent/dhcp/test_agent.py @@ -1570,8 +1570,10 @@ class TestDeviceManager(base.BaseTestCase): iptables_cls = iptables_cls_p.start() self.iptables_inst = mock.Mock() iptables_cls.return_value = self.iptables_inst - self.mangle_inst = mock.Mock() - self.iptables_inst.ipv4 = {'mangle': self.mangle_inst} + self.mangle_inst_v4 = mock.Mock() + self.iptables_inst.ipv4 = {'mangle': self.mangle_inst_v4} + self.mangle_inst_v6 = mock.Mock() + self.iptables_inst.ipv6 = {'mangle': self.mangle_inst_v6} self.mock_ip_wrapper_p = mock.patch("neutron.agent.linux.ip_lib." "IPWrapper") @@ -1631,12 +1633,19 @@ class TestDeviceManager(base.BaseTestCase): cfg.CONF.set_override('enable_metadata_network', True) self._test_setup_helper(False) - def test_setup_calls_fill_dhcp_udp_checksums(self): + def test_setup_calls_fill_dhcp_udp_checksums_v4(self): self._test_setup_helper(False) rule = ('-p udp -m udp --dport %d -j CHECKSUM --checksum-fill' % const.DHCP_RESPONSE_PORT) expected = [mock.call.add_rule('POSTROUTING', rule)] - self.mangle_inst.assert_has_calls(expected) + self.mangle_inst_v4.assert_has_calls(expected) + + def test_setup_calls_fill_dhcp_udp_checksums_v6(self): + self._test_setup_helper(False) + rule = ('-p udp -m udp --dport %d -j CHECKSUM --checksum-fill' + % n_const.DHCPV6_CLIENT_PORT) + expected = [mock.call.add_rule('POSTROUTING', rule)] + self.mangle_inst_v6.assert_has_calls(expected) def test_setup_dhcp_port_doesnt_orphan_devices(self): with mock.patch.object(dhcp.ip_lib, 'IPDevice') as mock_IPDevice: