From db0433458221d66d9a3a1a8399405e1b09b38c90 Mon Sep 17 00:00:00 2001 From: Nate Johnston Date: Wed, 17 Jul 2019 14:08:27 -0400 Subject: [PATCH] Add ethertype validator for custom ethertype validation Since the restriction on ethertypes is being raised so that ethertypes other than IPv4 and IPv6 can be specified in security group rules, add a validator to ensure that ethertypes will be a valid two octet value. The ethertype handling is configurable so make sure the validator handles both possible configuration alternatives: the original style where only IPv4 and IPv6 are valid, and the new style where any valid two octet number (or it's string equivalent) works. Related-Bug: #1832758 Needed-By: https://review.opendev.org/670203 Change-Id: I900f370f1de20b5137f138e54c5eec2102f77cce --- neutron_lib/api/validators/__init__.py | 34 ++++++++++++++ neutron_lib/constants.py | 3 ++ .../unit/api/validators/test_validators.py | 44 +++++++++++++++++++ .../ethertype_validator-2d608a46c237e214.yaml | 5 +++ 4 files changed, 86 insertions(+) create mode 100644 releasenotes/notes/ethertype_validator-2d608a46c237e214.yaml diff --git a/neutron_lib/api/validators/__init__.py b/neutron_lib/api/validators/__init__.py index 0da528643..b86df2e98 100644 --- a/neutron_lib/api/validators/__init__.py +++ b/neutron_lib/api/validators/__init__.py @@ -16,6 +16,7 @@ import inspect import re import netaddr +from oslo_config import cfg from oslo_log import log as logging from oslo_utils import netutils from oslo_utils import strutils @@ -1122,11 +1123,44 @@ def validate_subnet_service_types(service_types, valid_values=None): raise n_exc.InvalidSubnetServiceType(service_type=service_type) +def validate_ethertype(ethertype, valid_values=None): + """Validates ethertype is a valid ethertype. + + :param ethertype: Ethertype to validate + :returns: None if data is a valid ethertype. Otherwise a human-readable + message indicating that the data is not a valid ethertype. + """ + if cfg.CONF.sg_filter_ethertypes: + try: + if isinstance(ethertype, six.string_types): + ethertype = int(ethertype, 16) + if (isinstance(ethertype, six.integer_types) and + constants.ETHERTYPE_MIN <= ethertype and + ethertype <= constants.ETHERTYPE_MAX): + return None + except ValueError: + pass + msg = _("Ethertype %s is not a two octet hexadecimal " + "number.") % ethertype + LOG.debug(msg) + return msg + else: + if ethertype in constants.VALID_ETHERTYPES: + return None + valids = ','.join(map(str, constants.VALID_ETHERTYPES)) + msg = _("Ethertype %(ethertype)s is not a valid ethertype, must be " + "one of %(valid_ethertypes)s.") % {'ethertype': ethertype, + 'valid_ethertypes': valids} + LOG.debug(msg) + return msg + + # Dictionary that maintains a list of validation functions validators = {'type:dict': validate_dict, 'type:dict_or_none': validate_dict_or_none, 'type:dict_or_empty': validate_dict_or_empty, 'type:dict_or_nodata': validate_dict_or_nodata, + 'type:ethertype': validate_ethertype, 'type:fixed_ips': validate_fixed_ips, 'type:hostroutes': validate_hostroutes, 'type:ip_address': validate_ip_address, diff --git a/neutron_lib/constants.py b/neutron_lib/constants.py index 4d388444c..45ee329df 100644 --- a/neutron_lib/constants.py +++ b/neutron_lib/constants.py @@ -563,6 +563,9 @@ IP_ALLOWED_VERSIONS = [IP_VERSION_4, IP_VERSION_6] PORT_RANGE_MIN = 1 PORT_RANGE_MAX = 65535 +ETHERTYPE_MIN = 0 +ETHERTYPE_MAX = 65535 + DHCPV6_CLIENT_PORT = 546 # Configuration values for accept_ra sysctl, copied from linux kernel diff --git a/neutron_lib/tests/unit/api/validators/test_validators.py b/neutron_lib/tests/unit/api/validators/test_validators.py index d3afc02dd..069e62d63 100644 --- a/neutron_lib/tests/unit/api/validators/test_validators.py +++ b/neutron_lib/tests/unit/api/validators/test_validators.py @@ -1107,6 +1107,50 @@ class TestAttributeValidation(base.BaseTestCase): ] self.assertIsNone(validators.validate_subports(body)) + @mock.patch('oslo_config.cfg.CONF') + def test_validate_ethertype_valid_string(self, CONF): + CONF.sg_filter_ethertypes = False + self.assertIsNone(validators.validate_ethertype('IPv4')) + self.assertIsNone(validators.validate_ethertype('IPv6')) + + @mock.patch('oslo_config.cfg.CONF') + def test_validate_ethertype_invalid_string(self, CONF): + CONF.sg_filter_ethertypes = False + self.assertEqual(('Ethertype 0x4008 is not a valid ethertype, must be ' + 'one of IPv4,IPv6.'), + validators.validate_ethertype('0x4008')) + + @mock.patch('oslo_config.cfg.CONF') + def test_validate_ethertype_extended_valid_string(self, CONF): + CONF.sg_filter_ethertypes = True + self.assertIsNone(validators.validate_ethertype('0x4008')) + + @mock.patch('oslo_config.cfg.CONF') + def test_validate_ethertype_extended_valid_hexint(self, CONF): + CONF.sg_filter_ethertypes = True + self.assertIsNone(validators.validate_ethertype(0x4008)) + + @mock.patch('oslo_config.cfg.CONF') + def test_validate_ethertype_extended_invalid_negative(self, CONF): + CONF.sg_filter_ethertypes = True + self.assertEqual(("Ethertype -16392 is not a two octet " + "hexadecimal number."), + validators.validate_ethertype('-0x4008')) + + @mock.patch('oslo_config.cfg.CONF') + def test_validate_ethertype_extended_invalid_nonhex(self, CONF): + CONF.sg_filter_ethertypes = True + self.assertEqual(("Ethertype invalid is not a two octet " + "hexadecimal number."), + validators.validate_ethertype('invalid')) + + @mock.patch('oslo_config.cfg.CONF') + def test_validate_ethertype_extended_invalid_toobig(self, CONF): + CONF.sg_filter_ethertypes = True + self.assertEqual(("Ethertype 3735928559 is not a two octet " + "hexadecimal number."), + validators.validate_ethertype('0xdeadbeef')) + class TestValidateIPSubnetNone(base.BaseTestCase): diff --git a/releasenotes/notes/ethertype_validator-2d608a46c237e214.yaml b/releasenotes/notes/ethertype_validator-2d608a46c237e214.yaml new file mode 100644 index 000000000..32b1b0732 --- /dev/null +++ b/releasenotes/notes/ethertype_validator-2d608a46c237e214.yaml @@ -0,0 +1,5 @@ +--- +features: + - A new API validation type ``type:ethertype`` has been added and validates + ethertypes either as a valid two byte octet or as 'IPv4' and 'IPv6' based on + the sg_filter_ethertypes configuration setting.