Tunnel ID range validation for VXLAN/GRE networks
Currently, there is no check which validates the values of tunnel range for VXLAN/GRE networks. The VXLAN VNI is 24 bit which have range between 1 to 2^24 - 1. Similarly, GRE key field is 32 bit which have range between 1 to 2^32 - 1. Closes-Bug: #1306488 Co-Authored-By: romilg romilg@hp.com Change-Id: Idb3d0f41166df589a1e90394d9319901b5f9b439
This commit is contained in:
parent
5ba47e8ca3
commit
22bec67395
|
@ -61,7 +61,15 @@ DHCP_RESPONSE_PORT = 68
|
|||
|
||||
MIN_VLAN_TAG = 1
|
||||
MAX_VLAN_TAG = 4094
|
||||
MAX_VXLAN_VNI = 16777215
|
||||
|
||||
# For GRE Tunnel
|
||||
MIN_GRE_ID = 1
|
||||
MAX_GRE_ID = 2 ** 32 - 1
|
||||
|
||||
# For VXLAN Tunnel
|
||||
MIN_VXLAN_VNI = 1
|
||||
MAX_VXLAN_VNI = 2 ** 24 - 1
|
||||
|
||||
FLOODING_ENTRY = ['00:00:00:00:00:00', '0.0.0.0']
|
||||
|
||||
EXT_NS_COMP = '_backward_comp_e_ns'
|
||||
|
|
|
@ -305,6 +305,17 @@ class NetworkVlanRangeError(NeutronException):
|
|||
super(NetworkVlanRangeError, self).__init__(**kwargs)
|
||||
|
||||
|
||||
class NetworkTunnelRangeError(NeutronException):
|
||||
message = _("Invalid network Tunnel range: "
|
||||
"'%(tunnel_range)s' - %(error)s")
|
||||
|
||||
def __init__(self, **kwargs):
|
||||
# Convert tunnel_range tuple to 'start:end' format for display
|
||||
if isinstance(kwargs['tunnel_range'], tuple):
|
||||
kwargs['tunnel_range'] = "%d:%d" % kwargs['tunnel_range']
|
||||
super(NetworkTunnelRangeError, self).__init__(**kwargs)
|
||||
|
||||
|
||||
class NetworkVxlanPortRangeError(NeutronException):
|
||||
message = _("Invalid network VXLAN port range: '%(vxlan_range)s'")
|
||||
|
||||
|
|
|
@ -272,6 +272,14 @@ def is_valid_vlan_tag(vlan):
|
|||
return q_const.MIN_VLAN_TAG <= vlan <= q_const.MAX_VLAN_TAG
|
||||
|
||||
|
||||
def is_valid_gre_id(gre_id):
|
||||
return q_const.MIN_GRE_ID <= gre_id <= q_const.MAX_GRE_ID
|
||||
|
||||
|
||||
def is_valid_vxlan_vni(vni):
|
||||
return q_const.MIN_VXLAN_VNI <= vni <= q_const.MAX_VXLAN_VNI
|
||||
|
||||
|
||||
def get_random_mac(base_mac):
|
||||
mac = [int(base_mac[0], 16), int(base_mac[1], 16),
|
||||
int(base_mac[2], 16), random.randint(0x00, 0xff),
|
||||
|
|
|
@ -18,7 +18,25 @@ Common utilities and helper functions for Openstack Networking Plugins.
|
|||
|
||||
from neutron.common import exceptions as n_exc
|
||||
from neutron.common import utils
|
||||
from neutron.plugins.common import constants
|
||||
from neutron.plugins.common import constants as p_const
|
||||
|
||||
|
||||
def verify_tunnel_range(tunnel_range, tunnel_type):
|
||||
"""Raise an exception for invalid tunnel range or malformed range."""
|
||||
mappings = {p_const.TYPE_GRE: utils.is_valid_gre_id,
|
||||
p_const.TYPE_VXLAN: utils.is_valid_vxlan_vni}
|
||||
if tunnel_type in mappings:
|
||||
for ident in tunnel_range:
|
||||
if not mappings[tunnel_type](ident):
|
||||
raise n_exc.NetworkTunnelRangeError(
|
||||
tunnel_range=tunnel_range,
|
||||
error=_("%(id)s is not a valid %(type)s identifier") %
|
||||
{'id': ident, 'type': tunnel_type})
|
||||
if tunnel_range[1] < tunnel_range[0]:
|
||||
raise n_exc.NetworkTunnelRangeError(
|
||||
tunnel_range=tunnel_range,
|
||||
error=_("End of tunnel range is less "
|
||||
"than start of tunnel range"))
|
||||
|
||||
|
||||
def verify_vlan_range(vlan_range):
|
||||
|
@ -62,6 +80,6 @@ def parse_network_vlan_ranges(network_vlan_ranges_cfg_entries):
|
|||
|
||||
|
||||
def in_pending_status(status):
|
||||
return status in (constants.PENDING_CREATE,
|
||||
constants.PENDING_UPDATE,
|
||||
constants.PENDING_DELETE)
|
||||
return status in (p_const.PENDING_CREATE,
|
||||
p_const.PENDING_UPDATE,
|
||||
p_const.PENDING_DELETE)
|
||||
|
|
|
@ -19,6 +19,7 @@ from six import moves
|
|||
import sqlalchemy as sa
|
||||
from sqlalchemy import sql
|
||||
|
||||
from neutron.common import exceptions as exc
|
||||
from neutron.db import api as db_api
|
||||
from neutron.db import model_base
|
||||
from neutron.openstack.common.gettextutils import _LE
|
||||
|
@ -68,7 +69,12 @@ class GreTypeDriver(type_tunnel.TunnelTypeDriver):
|
|||
return p_const.TYPE_GRE
|
||||
|
||||
def initialize(self):
|
||||
self._initialize(cfg.CONF.ml2_type_gre.tunnel_id_ranges)
|
||||
try:
|
||||
self._initialize(cfg.CONF.ml2_type_gre.tunnel_id_ranges)
|
||||
except exc.NetworkTunnelRangeError:
|
||||
LOG.exception(_("Failed to parse tunnel_id_ranges. "
|
||||
"Service terminated!"))
|
||||
raise SystemExit()
|
||||
|
||||
def sync_allocations(self):
|
||||
|
||||
|
|
|
@ -16,8 +16,10 @@ import abc
|
|||
|
||||
from neutron.common import exceptions as exc
|
||||
from neutron.common import topics
|
||||
from neutron.openstack.common.gettextutils import _LI
|
||||
from neutron.openstack.common.gettextutils import _LW
|
||||
from neutron.openstack.common import log
|
||||
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
|
||||
|
||||
|
@ -59,25 +61,23 @@ class TunnelTypeDriver(helpers.TypeDriverHelper):
|
|||
|
||||
def _initialize(self, raw_tunnel_ranges):
|
||||
self.tunnel_ranges = []
|
||||
self._parse_tunnel_ranges(raw_tunnel_ranges,
|
||||
self.tunnel_ranges,
|
||||
self.get_type())
|
||||
self._parse_tunnel_ranges(raw_tunnel_ranges, self.tunnel_ranges)
|
||||
self.sync_allocations()
|
||||
|
||||
def _parse_tunnel_ranges(self, tunnel_ranges, current_range, tunnel_type):
|
||||
def _parse_tunnel_ranges(self, tunnel_ranges, current_range):
|
||||
for entry in tunnel_ranges:
|
||||
entry = entry.strip()
|
||||
try:
|
||||
tun_min, tun_max = entry.split(':')
|
||||
tun_min = tun_min.strip()
|
||||
tun_max = tun_max.strip()
|
||||
current_range.append((int(tun_min), int(tun_max)))
|
||||
tunnel_range = int(tun_min), int(tun_max)
|
||||
except ValueError as ex:
|
||||
LOG.error(_("Invalid tunnel ID range: '%(range)s' - %(e)s. "
|
||||
"Agent terminated!"),
|
||||
{'range': tunnel_ranges, 'e': ex})
|
||||
LOG.info(_("%(type)s ID ranges: %(range)s"),
|
||||
{'type': tunnel_type, 'range': current_range})
|
||||
raise exc.NetworkTunnelRangeError(tunnel_range=entry, error=ex)
|
||||
plugin_utils.verify_tunnel_range(tunnel_range, self.get_type())
|
||||
current_range.append(tunnel_range)
|
||||
LOG.info(_LI("%(type)s ID ranges: %(range)s"),
|
||||
{'type': self.get_type(), 'range': current_range})
|
||||
|
||||
def is_partial_segment(self, segment):
|
||||
return segment.get(api.SEGMENTATION_ID) is None
|
||||
|
|
|
@ -20,6 +20,7 @@ from six import moves
|
|||
import sqlalchemy as sa
|
||||
from sqlalchemy import sql
|
||||
|
||||
from neutron.common import exceptions as exc
|
||||
from neutron.db import api as db_api
|
||||
from neutron.db import model_base
|
||||
from neutron.openstack.common.gettextutils import _LE
|
||||
|
@ -76,7 +77,12 @@ class VxlanTypeDriver(type_tunnel.TunnelTypeDriver):
|
|||
return p_const.TYPE_VXLAN
|
||||
|
||||
def initialize(self):
|
||||
self._initialize(cfg.CONF.ml2_type_vxlan.vni_ranges)
|
||||
try:
|
||||
self._initialize(cfg.CONF.ml2_type_vxlan.vni_ranges)
|
||||
except exc.NetworkTunnelRangeError:
|
||||
LOG.exception(_("Failed to parse vni_ranges. "
|
||||
"Service terminated!"))
|
||||
raise SystemExit()
|
||||
|
||||
def sync_allocations(self):
|
||||
|
||||
|
|
|
@ -16,8 +16,10 @@ import eventlet
|
|||
import mock
|
||||
import testtools
|
||||
|
||||
from neutron.common import constants
|
||||
from neutron.common import exceptions as n_exc
|
||||
from neutron.common import utils
|
||||
from neutron.plugins.common import constants as p_const
|
||||
from neutron.plugins.common import utils as plugin_utils
|
||||
from neutron.tests import base
|
||||
|
||||
|
@ -65,6 +67,68 @@ class TestParseMappings(base.BaseTestCase):
|
|||
self.assertEqual(self.parse(['']), {})
|
||||
|
||||
|
||||
class TestParseTunnelRangesMixin(object):
|
||||
TUN_MIN = None
|
||||
TUN_MAX = None
|
||||
TYPE = None
|
||||
_err_prefix = "Invalid network Tunnel range: '%d:%d' - "
|
||||
_err_suffix = "%s is not a valid %s identifier"
|
||||
_err_range = "End of tunnel range is less than start of tunnel range"
|
||||
|
||||
def _build_invalid_tunnel_range_msg(self, t_range_tuple, n):
|
||||
bad_id = t_range_tuple[n - 1]
|
||||
return (self._err_prefix % t_range_tuple) + (self._err_suffix
|
||||
% (bad_id, self.TYPE))
|
||||
|
||||
def _build_range_reversed_msg(self, t_range_tuple):
|
||||
return (self._err_prefix % t_range_tuple) + self._err_range
|
||||
|
||||
def _verify_range(self, tunnel_range):
|
||||
return plugin_utils.verify_tunnel_range(tunnel_range, self.TYPE)
|
||||
|
||||
def _check_range_valid_ranges(self, tunnel_range):
|
||||
self.assertIsNone(self._verify_range(tunnel_range))
|
||||
|
||||
def _check_range_invalid_ranges(self, bad_range, which):
|
||||
expected_msg = self._build_invalid_tunnel_range_msg(bad_range, which)
|
||||
err = self.assertRaises(n_exc.NetworkTunnelRangeError,
|
||||
self._verify_range, bad_range)
|
||||
self.assertEqual(expected_msg, str(err))
|
||||
|
||||
def _check_range_reversed(self, bad_range):
|
||||
err = self.assertRaises(n_exc.NetworkTunnelRangeError,
|
||||
self._verify_range, bad_range)
|
||||
expected_msg = self._build_range_reversed_msg(bad_range)
|
||||
self.assertEqual(expected_msg, str(err))
|
||||
|
||||
def test_range_tunnel_id_valid(self):
|
||||
self._check_range_valid_ranges((self.TUN_MIN, self.TUN_MAX))
|
||||
|
||||
def test_range_tunnel_id_invalid(self):
|
||||
self._check_range_invalid_ranges((-1, self.TUN_MAX), 1)
|
||||
self._check_range_invalid_ranges((self.TUN_MIN,
|
||||
self.TUN_MAX + 1), 2)
|
||||
self._check_range_invalid_ranges((self.TUN_MIN - 1,
|
||||
self.TUN_MAX + 1), 1)
|
||||
|
||||
def test_range_tunnel_id_reversed(self):
|
||||
self._check_range_reversed((self.TUN_MAX, self.TUN_MIN))
|
||||
|
||||
|
||||
class TestGreTunnelRangeVerifyValid(TestParseTunnelRangesMixin,
|
||||
base.BaseTestCase):
|
||||
TUN_MIN = constants.MIN_GRE_ID
|
||||
TUN_MAX = constants.MAX_GRE_ID
|
||||
TYPE = p_const.TYPE_GRE
|
||||
|
||||
|
||||
class TestVxlanTunnelRangeVerifyValid(TestParseTunnelRangesMixin,
|
||||
base.BaseTestCase):
|
||||
TUN_MIN = constants.MIN_VXLAN_VNI
|
||||
TUN_MAX = constants.MAX_VXLAN_VNI
|
||||
TYPE = p_const.TYPE_VXLAN
|
||||
|
||||
|
||||
class UtilTestParseVlanRanges(base.BaseTestCase):
|
||||
_err_prefix = "Invalid network VLAN range: '"
|
||||
_err_too_few = "' - 'need more than 2 values to unpack'"
|
||||
|
|
Loading…
Reference in New Issue