[Linuxbridge] Handle properly too big VXLAN MTU values

In case when MTU value configured for VXLAN network is
higher than physical device's MTU - VXLAN overhead
then Linuxbridge agent will not be able to create properly
vxlan interface with desired MTU value.
In patch [1] there was introduced validation if MTU of
physical device is big enough to create MTU with required
MTU value but it was working only for IPv4 tunnels as
for IPv6 overhead is bigger.

This patch changes this validation a bit and now it will
works properly for both IPv4 and IPv6  VXLAN tunnels.

[1] https://review.openstack.org/#/c/546291/

Change-Id: Ib707312adb45dae7295884aba4ece6538d330d56
Related-Bug: #1744101
This commit is contained in:
Sławek Kapłoński 2018-04-23 22:25:09 +02:00
parent 4d40e6e40e
commit 60d62d3103
4 changed files with 48 additions and 24 deletions

View File

@ -13,6 +13,7 @@
# License for the specific language governing permissions and limitations
# under the License.
import errno
import os
import re
import time
@ -25,6 +26,7 @@ from oslo_config import cfg
from oslo_log import log as logging
from oslo_utils import excutils
from pyroute2.netlink.rtnl import ifinfmsg
from pyroute2 import NetlinkError
from pyroute2 import netns
import six
@ -65,6 +67,11 @@ class AddressNotReady(exceptions.NeutronException):
"become ready: %(reason)s")
class InvalidArgument(exceptions.NeutronException):
message = _("Invalid value %(value)s for parameter %(parameter)s "
"provided.")
class SubProcessBase(object):
def __init__(self, namespace=None,
log_fail_as_error=True):
@ -508,8 +515,13 @@ class IpLinkCommand(IpDeviceCommandBase):
self.name, self._parent.namespace, ifinfmsg.IFF_ALLMULTI)
def set_mtu(self, mtu_size):
privileged.set_link_attribute(
self.name, self._parent.namespace, mtu=mtu_size)
try:
privileged.set_link_attribute(
self.name, self._parent.namespace, mtu=mtu_size)
except NetlinkError as e:
if e.code == errno.EINVAL:
raise InvalidArgument(parameter="MTU", value=mtu_size)
raise
def set_up(self):
privileged.set_link_attribute(

View File

@ -347,25 +347,8 @@ class LinuxBridgeManager(amb.CommonAgentManagerBase):
args['proxy'] = cfg.CONF.VXLAN.arp_responder
try:
if mtu:
phys_dev_mtu = ip_lib.get_device_mtu(self.local_int)
max_mtu = phys_dev_mtu - constants.VXLAN_ENCAP_OVERHEAD
if mtu > max_mtu:
LOG.error("Provided MTU value %(mtu)s for VNI "
"%(segmentation_id)s is too high. "
"According to physical device %(dev)s "
"MTU=%(phys_mtu)s maximum available "
"MTU is %(max_mtu)s",
{'mtu': mtu,
'segmentation_id': segmentation_id,
'dev': self.local_int,
'phys_mtu': phys_dev_mtu,
'max_mtu': max_mtu})
return None
int_vxlan = self.ip.add_vxlan(interface, segmentation_id,
**args)
if mtu:
int_vxlan.link.set_mtu(mtu)
except RuntimeError:
with excutils.save_and_reraise_exception() as ctxt:
# perform this check after an attempt rather than before
@ -376,6 +359,20 @@ class LinuxBridgeManager(amb.CommonAgentManagerBase):
"VNI %s because it is in use by another "
"interface.", segmentation_id)
return None
if mtu:
try:
int_vxlan.link.set_mtu(mtu)
except ip_lib.InvalidArgument:
phys_dev_mtu = ip_lib.get_device_mtu(self.local_int)
LOG.error("Provided MTU value %(mtu)s for VNI "
"%(segmentation_id)s is too high according "
"to physical device %(dev)s MTU=%(phys_mtu)s.",
{'mtu': mtu,
'segmentation_id': segmentation_id,
'dev': self.local_int,
'phys_mtu': phys_dev_mtu})
int_vxlan.link.delete()
return None
int_vxlan.disable_ipv6()
int_vxlan.link.set_up()
LOG.debug("Done creating vxlan interface %s", interface)

View File

@ -480,6 +480,10 @@ class IpLibTestCase(IpLibTestFramework):
self.assertEqual(1450, device.link.mtu)
# Check if proper exception will be raised when wrong MTU value is
# provided
self.assertRaises(ip_lib.InvalidArgument, device.link.set_mtu, 1)
def test_set_link_allmulticast_on(self):
attr = self.generate_device_details()
device = self.manage_device(attr)

View File

@ -442,22 +442,33 @@ class TestLinuxBridgeManager(base.BaseTestCase):
def test_ensure_vxlan_mtu_too_big(self):
seg_id = "12345678"
physical_mtu = 1500
# Any mtu value which will be higher than physical_mtu - 50 should
# be too big
# Any mtu value which will be higher than
# physical_mtu - VXLAN_ENCAP_OVERHEAD should raise NetlinkError
mtu = 1490
self.lbm.local_int = 'eth0'
self.lbm.vxlan_mode = lconst.VXLAN_MCAST
with mock.patch.object(ip_lib, 'device_exists', return_value=False):
vxlan_dev = FakeIpDevice()
vxlan_dev = mock.Mock()
with mock.patch.object(vxlan_dev, 'disable_ipv6') as dv6_fn,\
mock.patch.object(self.lbm.ip, 'add_vxlan',
return_value=vxlan_dev) as add_vxlan_fn,\
mock.patch.object(
vxlan_dev.link, 'set_mtu',
side_effect=ip_lib.InvalidArgument(
parameter="MTU", value=mtu)),\
mock.patch.object(ip_lib, 'get_device_mtu',
return_value=physical_mtu):
return_value=physical_mtu),\
mock.patch.object(vxlan_dev.link, 'delete') as delete_dev:
self.assertFalse(
self.lbm.ensure_vxlan(seg_id, mtu=mtu))
add_vxlan_fn.assert_not_called()
add_vxlan_fn.assert_called_with("vxlan-" + seg_id, seg_id,
group="224.0.0.1",
srcport=(0, 0),
dstport=None,
ttl=None,
dev=self.lbm.local_int)
delete_dev.assert_called_once_with()
dv6_fn.assert_not_called()
def test__update_interface_ip_details(self):