diff --git a/neutron/agent/l3/dvr_edge_ha_router.py b/neutron/agent/l3/dvr_edge_ha_router.py index 377d51e4c44..da356682719 100644 --- a/neutron/agent/l3/dvr_edge_ha_router.py +++ b/neutron/agent/l3/dvr_edge_ha_router.py @@ -117,4 +117,5 @@ class DvrEdgeHaRouter(DvrEdgeRouter, HaRouter): self.driver.plug(port['network_id'], port['id'], interface_name, port['mac_address'], namespace=self.snat_namespace.name, - prefix=dvr_snat_ns.SNAT_INT_DEV_PREFIX) + prefix=dvr_snat_ns.SNAT_INT_DEV_PREFIX, + mtu=port.get('mtu')) diff --git a/neutron/agent/l3/dvr_edge_router.py b/neutron/agent/l3/dvr_edge_router.py index 078d3bc0b62..48c75213677 100644 --- a/neutron/agent/l3/dvr_edge_router.py +++ b/neutron/agent/l3/dvr_edge_router.py @@ -104,7 +104,8 @@ class DvrEdgeRouter(dvr_local_router.DvrLocalRouter): sn_port['fixed_ips'], sn_port['mac_address'], interface_name, - dvr_snat_ns.SNAT_INT_DEV_PREFIX) + dvr_snat_ns.SNAT_INT_DEV_PREFIX, + mtu=sn_port.get('mtu')) def _dvr_internal_network_removed(self, port): super(DvrEdgeRouter, self)._dvr_internal_network_removed(port) @@ -132,7 +133,8 @@ class DvrEdgeRouter(dvr_local_router.DvrLocalRouter): self.snat_namespace.name, port['network_id'], port['id'], port['fixed_ips'], port['mac_address'], interface_name, - dvr_snat_ns.SNAT_INT_DEV_PREFIX) + dvr_snat_ns.SNAT_INT_DEV_PREFIX, + mtu=port.get('mtu')) def _create_dvr_gateway(self, ex_gw_port, gw_interface_name): """Create SNAT namespace.""" diff --git a/neutron/agent/l3/dvr_fip_ns.py b/neutron/agent/l3/dvr_fip_ns.py index c5660386aca..30be9a2c3d5 100644 --- a/neutron/agent/l3/dvr_fip_ns.py +++ b/neutron/agent/l3/dvr_fip_ns.py @@ -106,7 +106,8 @@ class FipNamespace(namespaces.Namespace): ex_gw_port['mac_address'], bridge=self.agent_conf.external_network_bridge, namespace=ns_name, - prefix=FIP_EXT_DEV_PREFIX) + prefix=FIP_EXT_DEV_PREFIX, + mtu=ex_gw_port.get('mtu')) ip_cidrs = common_utils.fixed_ip_cidrs(ex_gw_port['fixed_ips']) self.driver.init_l3(interface_name, ip_cidrs, namespace=ns_name, @@ -232,9 +233,11 @@ class FipNamespace(namespaces.Namespace): self._internal_ns_interface_added(str(fip_2_rtr), fip_2_rtr_name, fip_ns_name) - if self.agent_conf.network_device_mtu: - int_dev[0].link.set_mtu(self.agent_conf.network_device_mtu) - int_dev[1].link.set_mtu(self.agent_conf.network_device_mtu) + mtu = (self.agent_conf.network_device_mtu or + ri.get_ex_gw_port().get('mtu')) + if mtu: + int_dev[0].link.set_mtu(mtu) + int_dev[1].link.set_mtu(mtu) int_dev[0].link.set_up() int_dev[1].link.set_up() diff --git a/neutron/agent/l3/ha_router.py b/neutron/agent/l3/ha_router.py index f1a248963de..b7d7031fd7c 100644 --- a/neutron/agent/l3/ha_router.py +++ b/neutron/agent/l3/ha_router.py @@ -144,7 +144,8 @@ class HaRouter(router.RouterInfo): interface_name, self.ha_port['mac_address'], namespace=self.ha_namespace, - prefix=HA_DEV_PREFIX) + prefix=HA_DEV_PREFIX, + mtu=self.ha_port.get('mtu')) ip_cidrs = common_utils.fixed_ip_cidrs(self.ha_port['fixed_ips']) self.driver.init_l3(interface_name, ip_cidrs, namespace=self.ha_namespace, @@ -268,13 +269,13 @@ class HaRouter(router.RouterInfo): def _plug_ha_router_port(self, port, name_getter, prefix): port_id = port['id'] interface_name = name_getter(port_id) - self.driver.plug(port['network_id'], port_id, interface_name, port['mac_address'], namespace=self.ha_namespace, - prefix=prefix) + prefix=prefix, + mtu=port.get('mtu')) self._disable_ipv6_addressing_on_interface(interface_name) self._add_vips(port, interface_name) diff --git a/neutron/agent/l3/router_info.py b/neutron/agent/l3/router_info.py index b4b7eb45930..c442aac4d3a 100644 --- a/neutron/agent/l3/router_info.py +++ b/neutron/agent/l3/router_info.py @@ -361,12 +361,12 @@ class RouterInfo(object): def _internal_network_added(self, ns_name, network_id, port_id, fixed_ips, mac_address, - interface_name, prefix): + interface_name, prefix, mtu=None): LOG.debug("adding internal network: prefix(%s), port(%s)", prefix, port_id) self.driver.plug(network_id, port_id, interface_name, mac_address, namespace=ns_name, - prefix=prefix) + prefix=prefix, mtu=mtu) ip_cidrs = common_utils.fixed_ip_cidrs(fixed_ips) self.driver.init_router_port( @@ -391,7 +391,8 @@ class RouterInfo(object): fixed_ips, mac_address, interface_name, - INTERNAL_DEV_PREFIX) + INTERNAL_DEV_PREFIX, + mtu=port.get('mtu')) def internal_network_removed(self, port): interface_name = self.get_internal_device_name(port['id']) @@ -557,7 +558,8 @@ class RouterInfo(object): ex_gw_port['mac_address'], bridge=self.agent_conf.external_network_bridge, namespace=ns_name, - prefix=EXTERNAL_DEV_PREFIX) + prefix=EXTERNAL_DEV_PREFIX, + mtu=ex_gw_port.get('mtu')) def _get_external_gw_ips(self, ex_gw_port): gateway_ips = [] diff --git a/neutron/agent/linux/dhcp.py b/neutron/agent/linux/dhcp.py index b4d04256ea6..e08a1265489 100644 --- a/neutron/agent/linux/dhcp.py +++ b/neutron/agent/linux/dhcp.py @@ -1230,7 +1230,8 @@ class DeviceManager(object): port.id, interface_name, port.mac_address, - namespace=network.namespace) + namespace=network.namespace, + mtu=network.get('mtu')) except Exception: with excutils.save_and_reraise_exception(): LOG.exception(_LE('Unable to plug DHCP port for ' diff --git a/neutron/agent/linux/interface.py b/neutron/agent/linux/interface.py index 29c1b352895..280385cd240 100644 --- a/neutron/agent/linux/interface.py +++ b/neutron/agent/linux/interface.py @@ -20,7 +20,7 @@ from oslo_config import cfg from oslo_log import log as logging import six -from neutron._i18n import _, _LE, _LI +from neutron._i18n import _, _LE, _LI, _LW from neutron.agent.common import ovs_lib from neutron.agent.linux import ip_lib from neutron.agent.linux import utils @@ -233,15 +233,15 @@ class LinuxInterfaceDriver(object): @abc.abstractmethod def plug_new(self, network_id, port_id, device_name, mac_address, - bridge=None, namespace=None, prefix=None): + bridge=None, namespace=None, prefix=None, mtu=None): """Plug in the interface only for new devices that don't exist yet.""" def plug(self, network_id, port_id, device_name, mac_address, - bridge=None, namespace=None, prefix=None): + bridge=None, namespace=None, prefix=None, mtu=None): if not ip_lib.device_exists(device_name, namespace=namespace): self.plug_new(network_id, port_id, device_name, mac_address, - bridge, namespace, prefix) + bridge, namespace, prefix, mtu) else: LOG.info(_LI("Device %s already exists"), device_name) @@ -269,7 +269,7 @@ class LinuxInterfaceDriver(object): class NullDriver(LinuxInterfaceDriver): def plug_new(self, network_id, port_id, device_name, mac_address, - bridge=None, namespace=None, prefix=None): + bridge=None, namespace=None, prefix=None, mtu=None): pass def unplug(self, device_name, bridge=None, namespace=None, prefix=None): @@ -304,7 +304,7 @@ class OVSInterfaceDriver(LinuxInterfaceDriver): ovs.replace_port(device_name, *attrs) def plug_new(self, network_id, port_id, device_name, mac_address, - bridge=None, namespace=None, prefix=None): + bridge=None, namespace=None, prefix=None, mtu=None): """Plug in the interface.""" if not bridge: bridge = self.conf.ovs_integration_bridge @@ -328,11 +328,13 @@ class OVSInterfaceDriver(LinuxInterfaceDriver): ns_dev.link.set_address(mac_address) - if self.conf.network_device_mtu: - ns_dev.link.set_mtu(self.conf.network_device_mtu) + mtu = self.conf.network_device_mtu or mtu + if mtu: + ns_dev.link.set_mtu(mtu) if self.conf.ovs_use_veth: - root_dev.link.set_mtu(self.conf.network_device_mtu) - + root_dev.link.set_mtu(mtu) + else: + LOG.warning(_LW("No MTU configured for port %s"), port_id) # Add an interface created by ovs to the namespace. if not self.conf.ovs_use_veth and namespace: namespace_obj = ip.ensure_namespace(namespace) @@ -381,7 +383,7 @@ class IVSInterfaceDriver(LinuxInterfaceDriver): utils.execute(cmd, run_as_root=True) def plug_new(self, network_id, port_id, device_name, mac_address, - bridge=None, namespace=None, prefix=None): + bridge=None, namespace=None, prefix=None, mtu=None): """Plug in the interface.""" ip = ip_lib.IPWrapper() tap_name = self._get_tap_name(device_name, prefix) @@ -393,9 +395,12 @@ class IVSInterfaceDriver(LinuxInterfaceDriver): ns_dev = ip.device(device_name) ns_dev.link.set_address(mac_address) - if self.conf.network_device_mtu: - ns_dev.link.set_mtu(self.conf.network_device_mtu) - root_dev.link.set_mtu(self.conf.network_device_mtu) + mtu = self.conf.network_device_mtu or mtu + if mtu: + ns_dev.link.set_mtu(mtu) + root_dev.link.set_mtu(mtu) + else: + LOG.warning(_LW("No MTU configured for port %s"), port_id) if namespace: namespace_obj = ip.ensure_namespace(namespace) @@ -424,7 +429,7 @@ class BridgeInterfaceDriver(LinuxInterfaceDriver): DEV_NAME_PREFIX = 'ns-' def plug_new(self, network_id, port_id, device_name, mac_address, - bridge=None, namespace=None, prefix=None): + bridge=None, namespace=None, prefix=None, mtu=None): """Plugin the interface.""" ip = ip_lib.IPWrapper() @@ -436,9 +441,12 @@ class BridgeInterfaceDriver(LinuxInterfaceDriver): namespace2=namespace) ns_veth.link.set_address(mac_address) - if self.conf.network_device_mtu: - root_veth.link.set_mtu(self.conf.network_device_mtu) - ns_veth.link.set_mtu(self.conf.network_device_mtu) + mtu = self.conf.network_device_mtu or mtu + if mtu: + root_veth.link.set_mtu(mtu) + ns_veth.link.set_mtu(mtu) + else: + LOG.warning(_LW("No MTU configured for port %s"), port_id) root_veth.link.set_up() ns_veth.link.set_up() diff --git a/neutron/tests/functional/agent/l3/test_dvr_router.py b/neutron/tests/functional/agent/l3/test_dvr_router.py index f6308e98ad1..4283f8736f7 100644 --- a/neutron/tests/functional/agent/l3/test_dvr_router.py +++ b/neutron/tests/functional/agent/l3/test_dvr_router.py @@ -51,6 +51,10 @@ class TestDvrRouter(framework.L3AgentTestFramework): self.agent.conf.agent_mode = 'dvr' self._test_update_floatingip_statuses(self.generate_dvr_router_info()) + def test_dvr_router_lifecycle_ha_with_snat_with_fips_nmtu(self): + self._dvr_router_lifecycle(enable_ha=True, enable_snat=True, + use_port_mtu=True) + def test_dvr_router_lifecycle_without_ha_without_snat_with_fips(self): self._dvr_router_lifecycle(enable_ha=False, enable_snat=False) @@ -101,7 +105,7 @@ class TestDvrRouter(framework.L3AgentTestFramework): self._validate_fips_for_external_network(router2, fip2_ns) def _dvr_router_lifecycle(self, enable_ha=False, enable_snat=False, - custom_mtu=2000, + custom_mtu=2000, use_port_mtu=False, ip_version=4, dual_stack=False): '''Test dvr router lifecycle @@ -115,11 +119,19 @@ class TestDvrRouter(framework.L3AgentTestFramework): # Since by definition this is a dvr (distributed = true) # only dvr and dvr_snat are applicable self.agent.conf.agent_mode = 'dvr_snat' if enable_snat else 'dvr' - self.agent.conf.network_device_mtu = custom_mtu # We get the router info particular to a dvr router router_info = self.generate_dvr_router_info( enable_ha, enable_snat, extra_routes=True) + if use_port_mtu: + for key in ('_interfaces', '_snat_router_interfaces', + '_floatingip_agent_interfaces'): + for port in router_info[key]: + port['mtu'] = custom_mtu + router_info['gw_port']['mtu'] = custom_mtu + router_info['_ha_interface']['mtu'] = custom_mtu + else: + self.agent.conf.network_device_mtu = custom_mtu # We need to mock the get_agent_gateway_port return value # because the whole L3PluginApi is mocked and we need the port @@ -156,6 +168,9 @@ class TestDvrRouter(framework.L3AgentTestFramework): router.get_internal_device_name, router.ns_name) utils.wait_until_true(device_exists) + name = router.get_internal_device_name(device['id']) + self.assertEqual(custom_mtu, + ip_lib.IPDevice(name, router.ns_name).link.mtu) ext_gateway_port = router_info['gw_port'] self.assertTrue(self._namespace_exists(router.ns_name)) diff --git a/neutron/tests/functional/agent/test_dhcp_agent.py b/neutron/tests/functional/agent/test_dhcp_agent.py index db86e1b89e8..041367c6c5b 100644 --- a/neutron/tests/functional/agent/test_dhcp_agent.py +++ b/neutron/tests/functional/agent/test_dhcp_agent.py @@ -253,6 +253,15 @@ class DHCPAgentOVSTestCase(DHCPAgentOVSTestFramework): dhcp_enabled=dhcp_enabled) self.assert_dhcp_resources(network, dhcp_enabled) + def test_agent_mtu_set_on_interface_driver(self): + network = self.network_dict_for_dhcp() + network["mtu"] = 789 + self.configure_dhcp_for_network(network=network) + port = network.ports[0] + iface_name = self.get_interface_name(network, port) + dev = ip_lib.IPDevice(iface_name, network.namespace) + self.assertEqual(789, dev.link.mtu) + def test_good_address_allocation(self): network, port = self._get_network_port_for_allocation_test() network.ports.append(port) diff --git a/neutron/tests/unit/agent/dhcp/test_agent.py b/neutron/tests/unit/agent/dhcp/test_agent.py index 6c8645fa20f..c3046c12136 100644 --- a/neutron/tests/unit/agent/dhcp/test_agent.py +++ b/neutron/tests/unit/agent/dhcp/test_agent.py @@ -1309,7 +1309,8 @@ class TestDeviceManager(base.BaseTestCase): port.id, 'tap12345678-12', 'aa:bb:cc:dd:ee:ff', - namespace=net.namespace)) + namespace=net.namespace, + mtu=None)) self.mock_driver.assert_has_calls(expected) dh._set_default_route.assert_called_once_with(net, 'tap12345678-12') diff --git a/neutron/tests/unit/agent/l3/test_agent.py b/neutron/tests/unit/agent/l3/test_agent.py index 48de8a90082..2ee744c8057 100644 --- a/neutron/tests/unit/agent/l3/test_agent.py +++ b/neutron/tests/unit/agent/l3/test_agent.py @@ -390,7 +390,8 @@ class TestBasicRouterOperations(BasicRouterOperationsFramework): sn_port['fixed_ips'], sn_port['mac_address'], ri._get_snat_int_device_name(sn_port['id']), - dvr_snat_ns.SNAT_INT_DEV_PREFIX) + dvr_snat_ns.SNAT_INT_DEV_PREFIX, + mtu=None) elif action == 'remove': self.device_exists.return_value = False ri.get_snat_port_for_internal_port = mock.Mock( diff --git a/releasenotes/notes/end-to-end-mtu-00345fc4282cb8fb.yaml b/releasenotes/notes/end-to-end-mtu-00345fc4282cb8fb.yaml new file mode 100644 index 00000000000..e5164c2dca8 --- /dev/null +++ b/releasenotes/notes/end-to-end-mtu-00345fc4282cb8fb.yaml @@ -0,0 +1,20 @@ +--- +features: + - Use the value of the network 'mtu' attribute for the MTU + of virtual network interfaces such as veth pairs, patch + ports, and tap devices involving a particular network. + - Enable end-to-end support for arbitrary MTUs including + jumbo frames between instances and provider networks by + moving MTU disparities between flat or VLAN networks and + overlay networks from layer-2 devices to layer-3 devices + that support path MTU discovery (PMTUD). +upgrade: + - Does not change MTU for existing virtual network interfaces. + - Actions that create virtual network interfaces on an existing + network with the 'mtu' attribute containing a value greater + than zero could cause issues for network traffic traversing + existing and new virtual network interfaces. +fixes: + - Explicitly configure MTU of virtual network interfaces + rather than using default values or incorrect values that + do not account for overlay protocol overhead.