Change tunnel MTU calculation to support IPv6
The IPv6 header is twice the size of the IPv4 header, 40 vs 20 bytes, but the tunnel overhead constants are static, only accounting for an IPv4 header in all cases. In order to be correct it needs to treat the tunnel overhead different from the IP overhead at L3. This required removing the 20 byte IP overhead from the tunnel type overhead constants and creating a new option, ml2.overlay_ip_version, in order for the server to know which version will be used, since it calculates the MTU for the network. A version mis-match will now cause a tunnel sync to fail on the server. Moved all MTU tests to a common location to remove duplication. DocImpact Change-Id: Ia2546c4c71ff48b9fe2817fbad22b1fbf85f325b Closes-bug: #1584940
This commit is contained in:
parent
de0456db7c
commit
51a697817d
@ -91,7 +91,13 @@ MIN_VXLAN_VNI = 1
|
||||
MAX_VXLAN_VNI = 2 ** 24 - 1
|
||||
VXLAN_UDP_PORT = 4789
|
||||
|
||||
# Network Type MTU overhead
|
||||
GENEVE_ENCAP_MIN_OVERHEAD = 50
|
||||
GRE_ENCAP_OVERHEAD = 42
|
||||
VXLAN_ENCAP_OVERHEAD = 50
|
||||
# Overlay (tunnel) protocol overhead
|
||||
GENEVE_ENCAP_MIN_OVERHEAD = 30
|
||||
GRE_ENCAP_OVERHEAD = 22
|
||||
VXLAN_ENCAP_OVERHEAD = 30
|
||||
|
||||
# IP header length
|
||||
IP_HEADER_LENGTH = {
|
||||
4: 20,
|
||||
6: 40,
|
||||
}
|
||||
|
@ -61,7 +61,11 @@ ml2_opts = [
|
||||
"will have the same type as tenant networks. Allowed "
|
||||
"values for external_network_type config option depend "
|
||||
"on the network type values configured in type_drivers "
|
||||
"config option."))
|
||||
"config option.")),
|
||||
cfg.IntOpt('overlay_ip_version',
|
||||
default=4,
|
||||
help=_("IP version of all overlay (tunnel) network endpoints. "
|
||||
"Use a value of 4 for IPv4 or 6 for IPv6."))
|
||||
]
|
||||
|
||||
|
||||
|
@ -40,7 +40,13 @@ vxlan_opts = [
|
||||
"To reserve a unique group for each possible "
|
||||
"(24-bit) VNI, use a /8 such as 239.0.0.0/8. This "
|
||||
"setting must be the same on all the agents.")),
|
||||
cfg.IPOpt('local_ip', help=_("Local IP address of the VXLAN endpoints.")),
|
||||
cfg.IPOpt('local_ip',
|
||||
help=_("IP address of local overlay (tunnel) network endpoint. "
|
||||
"Use either an IPv4 or IPv6 address that resides on one "
|
||||
"of the host network interfaces. The IP version of this "
|
||||
"value must match the value of the 'overlay_ip_version' "
|
||||
"option in the ML2 plug-in configuration file on the "
|
||||
"neutron server node(s).")),
|
||||
cfg.BoolOpt('l2_population', default=False,
|
||||
help=_("Extension to use alongside ml2 plugin's l2population "
|
||||
"mechanism driver. It enables the plugin to populate "
|
||||
|
@ -43,8 +43,12 @@ ovs_opts = [
|
||||
help=_("Peer patch port in tunnel bridge for integration "
|
||||
"bridge.")),
|
||||
cfg.IPOpt('local_ip',
|
||||
help=_("Local IP address of tunnel endpoint. Can be either "
|
||||
"an IPv4 or IPv6 address.")),
|
||||
help=_("IP address of local overlay (tunnel) network endpoint. "
|
||||
"Use either an IPv4 or IPv6 address that resides on one "
|
||||
"of the host network interfaces. The IP version of this "
|
||||
"value must match the value of the 'overlay_ip_version' "
|
||||
"option in the ML2 plug-in configuration file on the "
|
||||
"neutron server node(s).")),
|
||||
cfg.ListOpt('bridge_mappings',
|
||||
default=DEFAULT_BRIDGE_MAPPINGS,
|
||||
help=_("Comma-separated list of <physical_network>:<bridge> "
|
||||
|
@ -16,6 +16,7 @@ import abc
|
||||
import itertools
|
||||
import operator
|
||||
|
||||
import netaddr
|
||||
from neutron_lib import exceptions as exc
|
||||
from oslo_config import cfg
|
||||
from oslo_db import api as oslo_db_api
|
||||
@ -28,6 +29,7 @@ from sqlalchemy import or_
|
||||
from neutron._i18n import _, _LI, _LW
|
||||
from neutron.common import topics
|
||||
from neutron.db import api as db_api
|
||||
from neutron.plugins.common import constants as p_const
|
||||
from neutron.plugins.common import utils as plugin_utils
|
||||
from neutron.plugins.ml2 import driver_api as api
|
||||
from neutron.plugins.ml2.drivers import helpers
|
||||
@ -251,7 +253,9 @@ class TunnelTypeDriver(helpers.SegmentTypeDriver):
|
||||
mtu.append(seg_mtu)
|
||||
if cfg.CONF.ml2.path_mtu > 0:
|
||||
mtu.append(cfg.CONF.ml2.path_mtu)
|
||||
return min(mtu) if mtu else 0
|
||||
version = cfg.CONF.ml2.overlay_ip_version
|
||||
ip_header_length = p_const.IP_HEADER_LENGTH[version]
|
||||
return min(mtu) - ip_header_length if mtu else 0
|
||||
|
||||
|
||||
class EndpointTunnelTypeDriver(TunnelTypeDriver):
|
||||
@ -324,12 +328,19 @@ class TunnelRpcCallbackMixin(object):
|
||||
msg = _("Tunnel IP value needed by the ML2 plugin")
|
||||
raise exc.InvalidInput(error_message=msg)
|
||||
|
||||
host = kwargs.get('host')
|
||||
version = netaddr.IPAddress(tunnel_ip).version
|
||||
if version != cfg.CONF.ml2.overlay_ip_version:
|
||||
msg = (_("Tunnel IP version does not match ML2 "
|
||||
"overlay_ip_version, host: %(host)s, tunnel_ip: %(ip)s"),
|
||||
{'host': host, 'ip': tunnel_ip})
|
||||
raise exc.InvalidInput(error_message=msg)
|
||||
|
||||
tunnel_type = kwargs.get('tunnel_type')
|
||||
if not tunnel_type:
|
||||
msg = _("Network type value needed by the ML2 plugin")
|
||||
raise exc.InvalidInput(error_message=msg)
|
||||
|
||||
host = kwargs.get('host')
|
||||
driver = self._type_manager.drivers.get(tunnel_type)
|
||||
if driver:
|
||||
# The given conditional statements will verify the following
|
||||
|
@ -20,11 +20,14 @@ import testtools
|
||||
from testtools import matchers
|
||||
|
||||
from neutron.db import api as db
|
||||
from neutron.plugins.common import constants as p_const
|
||||
from neutron.plugins.ml2 import config
|
||||
from neutron.plugins.ml2 import driver_api as api
|
||||
from neutron.plugins.ml2.drivers import type_tunnel
|
||||
|
||||
TUNNEL_IP_ONE = "10.10.10.10"
|
||||
TUNNEL_IP_TWO = "10.10.10.20"
|
||||
TUNNEL_IPV6_ONE = "2001:db8:1::10"
|
||||
HOST_ONE = 'fake_host_one'
|
||||
HOST_TWO = 'fake_host_two'
|
||||
TUN_MIN = 100
|
||||
@ -355,6 +358,12 @@ class TunnelRpcCallbackTestMixin(object):
|
||||
'host': HOST_ONE}
|
||||
self._test_tunnel_sync(kwargs)
|
||||
|
||||
def test_tunnel_sync_called_with_host_passed_ipv6(self):
|
||||
config.cfg.CONF.set_override('overlay_ip_version', 6, group='ml2')
|
||||
kwargs = {'tunnel_ip': TUNNEL_IPV6_ONE, 'tunnel_type': self.TYPE,
|
||||
'host': HOST_ONE}
|
||||
self._test_tunnel_sync(kwargs)
|
||||
|
||||
def test_tunnel_sync_called_for_existing_endpoint(self):
|
||||
self.driver.add_endpoint(TUNNEL_IP_ONE, HOST_ONE)
|
||||
|
||||
@ -391,3 +400,60 @@ class TunnelRpcCallbackTestMixin(object):
|
||||
def test_tunnel_sync_called_without_tunnel_type(self):
|
||||
kwargs = {'tunnel_ip': TUNNEL_IP_ONE, 'host': None}
|
||||
self._test_tunnel_sync_raises(kwargs)
|
||||
|
||||
def test_tunnel_sync_called_with_tunnel_overlay_mismatch(self):
|
||||
config.cfg.CONF.set_override('overlay_ip_version', 6, group='ml2')
|
||||
kwargs = {'tunnel_ip': TUNNEL_IP_ONE, 'tunnel_type': self.TYPE,
|
||||
'host': HOST_ONE}
|
||||
self._test_tunnel_sync_raises(kwargs)
|
||||
|
||||
def test_tunnel_sync_called_with_tunnel_overlay_mismatch_ipv6(self):
|
||||
config.cfg.CONF.set_override('overlay_ip_version', 4, group='ml2')
|
||||
kwargs = {'tunnel_ip': TUNNEL_IPV6_ONE, 'tunnel_type': self.TYPE,
|
||||
'host': HOST_ONE}
|
||||
self._test_tunnel_sync_raises(kwargs)
|
||||
|
||||
|
||||
class TunnelTypeMTUTestMixin(object):
|
||||
|
||||
DRIVER_CLASS = None
|
||||
TYPE = None
|
||||
ENCAP_OVERHEAD = 0
|
||||
|
||||
def setUp(self):
|
||||
super(TunnelTypeMTUTestMixin, self).setUp()
|
||||
self.driver = self.DRIVER_CLASS()
|
||||
|
||||
def _test_get_mtu(self, ip_version):
|
||||
config.cfg.CONF.set_override('overlay_ip_version', ip_version,
|
||||
group='ml2')
|
||||
ip_header_length = p_const.IP_HEADER_LENGTH[ip_version]
|
||||
|
||||
config.cfg.CONF.set_override('global_physnet_mtu', 1500)
|
||||
config.cfg.CONF.set_override('path_mtu', 1475, group='ml2')
|
||||
self.driver.physnet_mtus = {'physnet1': 1450, 'physnet2': 1400}
|
||||
self.assertEqual(1475 - self.ENCAP_OVERHEAD - ip_header_length,
|
||||
self.driver.get_mtu('physnet1'))
|
||||
|
||||
config.cfg.CONF.set_override('global_physnet_mtu', 1450)
|
||||
config.cfg.CONF.set_override('path_mtu', 1475, group='ml2')
|
||||
self.driver.physnet_mtus = {'physnet1': 1400, 'physnet2': 1425}
|
||||
self.assertEqual(1450 - self.ENCAP_OVERHEAD - ip_header_length,
|
||||
self.driver.get_mtu('physnet1'))
|
||||
|
||||
config.cfg.CONF.set_override('global_physnet_mtu', 0)
|
||||
config.cfg.CONF.set_override('path_mtu', 1450, group='ml2')
|
||||
self.driver.physnet_mtus = {'physnet1': 1425, 'physnet2': 1400}
|
||||
self.assertEqual(1450 - self.ENCAP_OVERHEAD - ip_header_length,
|
||||
self.driver.get_mtu('physnet1'))
|
||||
|
||||
config.cfg.CONF.set_override('global_physnet_mtu', 0)
|
||||
config.cfg.CONF.set_override('path_mtu', 0, group='ml2')
|
||||
self.driver.physnet_mtus = {}
|
||||
self.assertEqual(0, self.driver.get_mtu('physnet1'))
|
||||
|
||||
def test_get_mtu_ipv4(self):
|
||||
self._test_get_mtu(4)
|
||||
|
||||
def test_get_mtu_ipv6(self):
|
||||
self._test_get_mtu(6)
|
||||
|
@ -53,3 +53,10 @@ class GeneveTypeRpcCallbackTest(base_type_tunnel.TunnelRpcCallbackTestMixin,
|
||||
testlib_api.SqlTestCase):
|
||||
DRIVER_CLASS = type_geneve.GeneveTypeDriver
|
||||
TYPE = p_const.TYPE_GENEVE
|
||||
|
||||
|
||||
class GeneveTypeTunnelMTUTest(base_type_tunnel.TunnelTypeMTUTestMixin,
|
||||
testlib_api.SqlTestCase):
|
||||
DRIVER_CLASS = type_geneve.GeneveTypeDriver
|
||||
TYPE = p_const.TYPE_GENEVE
|
||||
ENCAP_OVERHEAD = p_const.GENEVE_ENCAP_MIN_OVERHEAD
|
||||
|
@ -14,7 +14,6 @@
|
||||
# under the License.
|
||||
|
||||
from neutron.plugins.common import constants as p_const
|
||||
from neutron.plugins.ml2 import config
|
||||
from neutron.plugins.ml2.drivers import type_gre
|
||||
from neutron.tests.unit.plugins.ml2.drivers import base_type_tunnel
|
||||
from neutron.tests.unit.plugins.ml2 import test_rpc
|
||||
@ -55,30 +54,6 @@ class GreTypeTest(base_type_tunnel.TunnelTypeTestMixin,
|
||||
elif endpoint['ip_address'] == base_type_tunnel.TUNNEL_IP_TWO:
|
||||
self.assertEqual(base_type_tunnel.HOST_TWO, endpoint['host'])
|
||||
|
||||
def test_get_mtu(self):
|
||||
config.cfg.CONF.set_override('global_physnet_mtu', 1500)
|
||||
config.cfg.CONF.set_override('path_mtu', 1475, group='ml2')
|
||||
self.driver.physnet_mtus = {'physnet1': 1450, 'physnet2': 1400}
|
||||
self.assertEqual(1475 - p_const.GRE_ENCAP_OVERHEAD,
|
||||
self.driver.get_mtu('physnet1'))
|
||||
|
||||
config.cfg.CONF.set_override('global_physnet_mtu', 1425)
|
||||
config.cfg.CONF.set_override('path_mtu', 1475, group='ml2')
|
||||
self.driver.physnet_mtus = {'physnet1': 1400, 'physnet2': 1400}
|
||||
self.assertEqual(1425 - p_const.GRE_ENCAP_OVERHEAD,
|
||||
self.driver.get_mtu('physnet1'))
|
||||
|
||||
config.cfg.CONF.set_override('global_physnet_mtu', 0)
|
||||
config.cfg.CONF.set_override('path_mtu', 1475, group='ml2')
|
||||
self.driver.physnet_mtus = {'physnet1': 1450, 'physnet2': 1425}
|
||||
self.assertEqual(1475 - p_const.GRE_ENCAP_OVERHEAD,
|
||||
self.driver.get_mtu('physnet2'))
|
||||
|
||||
config.cfg.CONF.set_override('global_physnet_mtu', 0)
|
||||
config.cfg.CONF.set_override('path_mtu', 0, group='ml2')
|
||||
self.driver.physnet_mtus = {}
|
||||
self.assertEqual(0, self.driver.get_mtu('physnet1'))
|
||||
|
||||
|
||||
class GreTypeMultiRangeTest(base_type_tunnel.TunnelTypeMultiRangeTestMixin,
|
||||
testlib_api.SqlTestCase):
|
||||
@ -90,3 +65,10 @@ class GreTypeRpcCallbackTest(base_type_tunnel.TunnelRpcCallbackTestMixin,
|
||||
testlib_api.SqlTestCase):
|
||||
DRIVER_CLASS = type_gre.GreTypeDriver
|
||||
TYPE = p_const.TYPE_GRE
|
||||
|
||||
|
||||
class GreTypeTunnelMTUTest(base_type_tunnel.TunnelTypeMTUTestMixin,
|
||||
testlib_api.SqlTestCase):
|
||||
DRIVER_CLASS = type_gre.GreTypeDriver
|
||||
TYPE = p_const.TYPE_GRE
|
||||
ENCAP_OVERHEAD = p_const.GRE_ENCAP_OVERHEAD
|
||||
|
@ -14,7 +14,6 @@
|
||||
# under the License.
|
||||
|
||||
from neutron.plugins.common import constants as p_const
|
||||
from neutron.plugins.ml2 import config
|
||||
from neutron.plugins.ml2.drivers import type_vxlan
|
||||
from neutron.tests.unit.plugins.ml2.drivers import base_type_tunnel
|
||||
from neutron.tests.unit.plugins.ml2 import test_rpc
|
||||
@ -65,30 +64,6 @@ class VxlanTypeTest(base_type_tunnel.TunnelTypeTestMixin,
|
||||
self.assertEqual(VXLAN_UDP_PORT_TWO, endpoint['udp_port'])
|
||||
self.assertEqual(base_type_tunnel.HOST_TWO, endpoint['host'])
|
||||
|
||||
def test_get_mtu(self):
|
||||
config.cfg.CONF.set_override('global_physnet_mtu', 1500)
|
||||
config.cfg.CONF.set_override('path_mtu', 1475, group='ml2')
|
||||
self.driver.physnet_mtus = {'physnet1': 1450, 'physnet2': 1400}
|
||||
self.assertEqual(1475 - p_const.VXLAN_ENCAP_OVERHEAD,
|
||||
self.driver.get_mtu('physnet1'))
|
||||
|
||||
config.cfg.CONF.set_override('global_physnet_mtu', 1450)
|
||||
config.cfg.CONF.set_override('path_mtu', 1475, group='ml2')
|
||||
self.driver.physnet_mtus = {'physnet1': 1400, 'physnet2': 1425}
|
||||
self.assertEqual(1450 - p_const.VXLAN_ENCAP_OVERHEAD,
|
||||
self.driver.get_mtu('physnet1'))
|
||||
|
||||
config.cfg.CONF.set_override('global_physnet_mtu', 0)
|
||||
config.cfg.CONF.set_override('path_mtu', 1450, group='ml2')
|
||||
self.driver.physnet_mtus = {'physnet1': 1425, 'physnet2': 1400}
|
||||
self.assertEqual(1450 - p_const.VXLAN_ENCAP_OVERHEAD,
|
||||
self.driver.get_mtu('physnet1'))
|
||||
|
||||
config.cfg.CONF.set_override('global_physnet_mtu', 0)
|
||||
config.cfg.CONF.set_override('path_mtu', 0, group='ml2')
|
||||
self.driver.physnet_mtus = {}
|
||||
self.assertEqual(0, self.driver.get_mtu('physnet1'))
|
||||
|
||||
|
||||
class VxlanTypeMultiRangeTest(base_type_tunnel.TunnelTypeMultiRangeTestMixin,
|
||||
testlib_api.SqlTestCase):
|
||||
@ -100,3 +75,10 @@ class VxlanTypeRpcCallbackTest(base_type_tunnel.TunnelRpcCallbackTestMixin,
|
||||
testlib_api.SqlTestCase):
|
||||
DRIVER_CLASS = type_vxlan.VxlanTypeDriver
|
||||
TYPE = p_const.TYPE_VXLAN
|
||||
|
||||
|
||||
class VxlanTypeTunnelMTUTest(base_type_tunnel.TunnelTypeMTUTestMixin,
|
||||
testlib_api.SqlTestCase):
|
||||
DRIVER_CLASS = type_vxlan.VxlanTypeDriver
|
||||
TYPE = p_const.TYPE_VXLAN
|
||||
ENCAP_OVERHEAD = p_const.VXLAN_ENCAP_OVERHEAD
|
||||
|
@ -0,0 +1,17 @@
|
||||
---
|
||||
prelude: >
|
||||
Properly calculate overlay (tunnel) protocol overhead for
|
||||
environments using IPv4 or IPv6 endpoints. The ML2 plug-in
|
||||
configuration file contains a new configuration option,
|
||||
'overlay_ip_version', in the '[ml2]' section that indicates
|
||||
the IP version of all overlay network endpoints. Use '4' for
|
||||
IPv4 and '6' for IPv6. Defaults to '4'. Additionally, all
|
||||
layer-2 agents must use the same IP version for endpoints.
|
||||
upgrade:
|
||||
- Define the 'overlay_ip_version' option and value
|
||||
appropriate for the environment. Only required if not
|
||||
using the Default of '4'.
|
||||
other:
|
||||
- The value of the 'overlay_ip_version' option adds either
|
||||
20 bytes for IPv4 or 40 bytes for IPv6 to determine the total
|
||||
tunnel overhead amount.
|
Loading…
Reference in New Issue
Block a user