diff --git a/etc/neutron/plugins/ml2/openvswitch_agent.ini b/etc/neutron/plugins/ml2/openvswitch_agent.ini index 323ed3d740a..664aa0a1f31 100644 --- a/etc/neutron/plugins/ml2/openvswitch_agent.ini +++ b/etc/neutron/plugins/ml2/openvswitch_agent.ini @@ -163,6 +163,12 @@ # # extensions = +# (BoolOpt) Set or un-set the checksum on outgoing IP packet +# carrying GRE/VXLAN tunnel. The default value is False. +# +# tunnel_csum = False + + [securitygroup] # Firewall driver for realizing neutron security group function. # firewall_driver = neutron.agent.firewall.NoopFirewallDriver diff --git a/neutron/agent/common/ovs_lib.py b/neutron/agent/common/ovs_lib.py index 76f0b4478d9..a7d66097635 100644 --- a/neutron/agent/common/ovs_lib.py +++ b/neutron/agent/common/ovs_lib.py @@ -299,7 +299,8 @@ class OVSBridge(BaseOVS): def add_tunnel_port(self, port_name, remote_ip, local_ip, tunnel_type=p_const.TYPE_GRE, vxlan_udp_port=p_const.VXLAN_UDP_PORT, - dont_fragment=True): + dont_fragment=True, + tunnel_csum=False): attrs = [('type', tunnel_type)] # TODO(twilson) This is an OrderedDict solely to make a test happy options = collections.OrderedDict() @@ -314,6 +315,8 @@ class OVSBridge(BaseOVS): options['local_ip'] = local_ip options['in_key'] = 'flow' options['out_key'] = 'flow' + if tunnel_csum: + options['csum'] = str(tunnel_csum).lower() attrs.append(('options', options)) return self.add_port(port_name, *attrs) diff --git a/neutron/plugins/ml2/drivers/openvswitch/agent/common/config.py b/neutron/plugins/ml2/drivers/openvswitch/agent/common/config.py index 34685c53a2e..300c1a83695 100644 --- a/neutron/plugins/ml2/drivers/openvswitch/agent/common/config.py +++ b/neutron/plugins/ml2/drivers/openvswitch/agent/common/config.py @@ -119,7 +119,11 @@ agent_opts = [ "timeout won't be changed")), cfg.BoolOpt('drop_flows_on_start', default=False, help=_("Reset flow table on start. Setting this to True will " - "cause brief traffic interruption.")) + "cause brief traffic interruption.")), + cfg.BoolOpt('tunnel_csum', default=False, + help=_("Set or un-set the tunnel header checksum on " + "outgoing IP packet carrying GRE/VXLAN tunnel.")) + ] diff --git a/neutron/plugins/ml2/drivers/openvswitch/agent/ovs_neutron_agent.py b/neutron/plugins/ml2/drivers/openvswitch/agent/ovs_neutron_agent.py index 26771f449bd..8d37423f797 100644 --- a/neutron/plugins/ml2/drivers/openvswitch/agent/ovs_neutron_agent.py +++ b/neutron/plugins/ml2/drivers/openvswitch/agent/ovs_neutron_agent.py @@ -254,6 +254,7 @@ class OVSNeutronAgent(sg_rpc.SecurityGroupAgentRpcCallbackMixin, self.tunnel_count = 0 self.vxlan_udp_port = self.conf.AGENT.vxlan_udp_port self.dont_fragment = self.conf.AGENT.dont_fragment + self.tunnel_csum = cfg.CONF.AGENT.tunnel_csum self.tun_br = None self.patch_int_ofport = constants.OFPORT_INVALID self.patch_tun_ofport = constants.OFPORT_INVALID @@ -1243,7 +1244,8 @@ class OVSNeutronAgent(sg_rpc.SecurityGroupAgentRpcCallbackMixin, self.local_ip, tunnel_type, self.vxlan_udp_port, - self.dont_fragment) + self.dont_fragment, + self.tunnel_csum) if ofport == ovs_lib.INVALID_OFPORT: LOG.error(_LE("Failed to set-up %(type)s tunnel port to %(ip)s"), {'type': tunnel_type, 'ip': remote_ip}) diff --git a/neutron/tests/unit/agent/common/test_ovs_lib.py b/neutron/tests/unit/agent/common/test_ovs_lib.py index a599dc6bb2d..8741dff79d2 100644 --- a/neutron/tests/unit/agent/common/test_ovs_lib.py +++ b/neutron/tests/unit/agent/common/test_ovs_lib.py @@ -400,6 +400,40 @@ class OVS_Lib_Test(base.BaseTestCase): tools.verify_mock_calls(self.execute, expected_calls_and_values) + def test_add_vxlan_csum_tunnel_port(self): + pname = "tap99" + local_ip = "1.1.1.1" + remote_ip = "9.9.9.9" + ofport = 6 + vxlan_udp_port = "9999" + dont_fragment = True + tunnel_csum = True + command = ["--may-exist", "add-port", self.BR_NAME, pname] + command.extend(["--", "set", "Interface", pname]) + command.extend(["type=" + constants.TYPE_VXLAN, + "options:dst_port=" + vxlan_udp_port, + "options:df_default=true", + "options:remote_ip=" + remote_ip, + "options:local_ip=" + local_ip, + "options:in_key=flow", + "options:out_key=flow", + "options:csum=true"]) + # Each element is a tuple of (expected mock call, return_value) + expected_calls_and_values = [ + (self._vsctl_mock(*command), None), + (self._vsctl_mock("--columns=ofport", "list", "Interface", pname), + self._encode_ovs_json(['ofport'], [[ofport]])), + ] + tools.setup_mock_calls(self.execute, expected_calls_and_values) + + self.assertEqual( + self.br.add_tunnel_port(pname, remote_ip, local_ip, + constants.TYPE_VXLAN, vxlan_udp_port, + dont_fragment, tunnel_csum), + ofport) + + tools.verify_mock_calls(self.execute, expected_calls_and_values) + def _test_get_vif_ports(self, is_xen=False): pname = "tap99" ofport = 6 diff --git a/neutron/tests/unit/plugins/ml2/drivers/openvswitch/agent/test_ovs_neutron_agent.py b/neutron/tests/unit/plugins/ml2/drivers/openvswitch/agent/test_ovs_neutron_agent.py index df6c8780df7..c566001d2e6 100644 --- a/neutron/tests/unit/plugins/ml2/drivers/openvswitch/agent/test_ovs_neutron_agent.py +++ b/neutron/tests/unit/plugins/ml2/drivers/openvswitch/agent/test_ovs_neutron_agent.py @@ -1169,7 +1169,8 @@ class TestOvsNeutronAgent(object): self.agent.tun_br, 'gre-1', 'remote_ip', p_const.TYPE_GRE) add_tunnel_port_fn.assert_called_once_with( 'gre-1', 'remote_ip', self.agent.local_ip, p_const.TYPE_GRE, - self.agent.vxlan_udp_port, self.agent.dont_fragment) + self.agent.vxlan_udp_port, self.agent.dont_fragment, + self.agent.tunnel_csum) log_error_fn.assert_called_once_with( _("Failed to set-up %(type)s tunnel port to %(ip)s"), {'type': p_const.TYPE_GRE, 'ip': 'remote_ip'}) @@ -1182,11 +1183,32 @@ class TestOvsNeutronAgent(object): return_value=ovs_lib.INVALID_OFPORT) as add_tunnel_port_fn,\ mock.patch.object(self.mod_agent.LOG, 'error') as log_error_fn: self.agent.dont_fragment = False + self.agent.tunnel_csum = False ofport = self.agent._setup_tunnel_port( self.agent.tun_br, 'gre-1', 'remote_ip', p_const.TYPE_GRE) add_tunnel_port_fn.assert_called_once_with( 'gre-1', 'remote_ip', self.agent.local_ip, p_const.TYPE_GRE, - self.agent.vxlan_udp_port, self.agent.dont_fragment) + self.agent.vxlan_udp_port, self.agent.dont_fragment, + self.agent.tunnel_csum) + log_error_fn.assert_called_once_with( + _("Failed to set-up %(type)s tunnel port to %(ip)s"), + {'type': p_const.TYPE_GRE, 'ip': 'remote_ip'}) + self.assertEqual(ofport, 0) + + def test_setup_tunnel_port_error_negative_tunnel_csum(self): + with mock.patch.object( + self.agent.tun_br, + 'add_tunnel_port', + return_value=ovs_lib.INVALID_OFPORT) as add_tunnel_port_fn,\ + mock.patch.object(self.mod_agent.LOG, 'error') as log_error_fn: + self.agent.dont_fragment = True + self.agent.tunnel_csum = True + ofport = self.agent._setup_tunnel_port( + self.agent.tun_br, 'gre-1', 'remote_ip', p_const.TYPE_GRE) + add_tunnel_port_fn.assert_called_once_with( + 'gre-1', 'remote_ip', self.agent.local_ip, p_const.TYPE_GRE, + self.agent.vxlan_udp_port, self.agent.dont_fragment, + self.agent.tunnel_csum) log_error_fn.assert_called_once_with( _("Failed to set-up %(type)s tunnel port to %(ip)s"), {'type': p_const.TYPE_GRE, 'ip': 'remote_ip'}) diff --git a/neutron/tests/unit/plugins/ml2/drivers/openvswitch/agent/test_ovs_tunnel.py b/neutron/tests/unit/plugins/ml2/drivers/openvswitch/agent/test_ovs_tunnel.py index d2d1920a216..3fef3f17de3 100644 --- a/neutron/tests/unit/plugins/ml2/drivers/openvswitch/agent/test_ovs_tunnel.py +++ b/neutron/tests/unit/plugins/ml2/drivers/openvswitch/agent/test_ovs_tunnel.py @@ -484,7 +484,7 @@ class TunnelTest(object): self.mock_tun_bridge.add_tunnel_port.return_value = tunnel_port self.mock_tun_bridge_expected += [ mock.call.add_tunnel_port('gre-0a000a01', '10.0.10.1', '10.0.0.1', - 'gre', 4789, True), + 'gre', 4789, True, False), mock.call.setup_tunnel_port('gre', tunnel_port), ]