From 0d8911115e1b722da2f1e92f444e53b22223ee32 Mon Sep 17 00:00:00 2001 From: Eugene Nikanorov Date: Mon, 25 Aug 2014 00:59:02 +0400 Subject: [PATCH] Raise exception if ipv6 prefix is inappropriate for address mode Address prefix to use with slaac and stateless ipv6 address modes should be equal to 64 in order to work properly. The patch adds corresponding validation and fixes unit tests accordingly. Change-Id: I6c344b21a69f85f2885a72377171f70309b26775 Closes-Bug: #1357084 --- neutron/db/db_base_plugin_v2.py | 15 ++++++++++++++ neutron/tests/unit/test_db_plugin.py | 30 +++++++++++++++++++--------- 2 files changed, 36 insertions(+), 9 deletions(-) diff --git a/neutron/db/db_base_plugin_v2.py b/neutron/db/db_base_plugin_v2.py index c7e82d5bc..155b9f399 100644 --- a/neutron/db/db_base_plugin_v2.py +++ b/neutron/db/db_base_plugin_v2.py @@ -758,6 +758,21 @@ class NeutronDbPluginV2(neutron_plugin_base_v2.NeutronPluginBaseV2, if ra_mode_set and address_mode_set: self._validate_ipv6_combination(subnet['ipv6_ra_mode'], subnet['ipv6_address_mode']) + if address_mode_set: + self._validate_eui64_applicable(subnet) + + def _validate_eui64_applicable(self, subnet): + # Per RFC 4862, section 5.5.3, prefix length and interface + # id together should be equal to 128. Currently neutron supports + # EUI64 interface id only, thus limiting the prefix + # length to be 64 only. + if self._check_if_subnet_uses_eui64(subnet): + if netaddr.IPNetwork(subnet['cidr']).prefixlen != 64: + msg = _('Invalid CIDR %s for IPv6 address mode. ' + 'OpenStack uses the EUI-64 address format, ' + 'which requires the prefix to be /64.') + raise n_exc.InvalidInput( + error_message=(msg % subnet['cidr'])) def _validate_ipv6_combination(self, ra_mode, address_mode): if ra_mode != address_mode: diff --git a/neutron/tests/unit/test_db_plugin.py b/neutron/tests/unit/test_db_plugin.py index 7754cc2b1..2db224339 100644 --- a/neutron/tests/unit/test_db_plugin.py +++ b/neutron/tests/unit/test_db_plugin.py @@ -1390,13 +1390,13 @@ fixed_ips=ip_address%%3D%s&fixed_ips=ip_address%%3D%s&fixed_ips=subnet_id%%3D%s self._delete('ports', port3['port']['id']) self._delete('ports', port4['port']['id']) - def test_ip_allocation_for_ipv6_subnet_slaac_adddress_mode(self): + def test_ip_allocation_for_ipv6_subnet_slaac_address_mode(self): res = self._create_network(fmt=self.fmt, name='net', admin_state_up=True) network = self.deserialize(self.fmt, res) v6_subnet = self._make_subnet(self.fmt, network, gateway='fe80::1', - cidr='fe80::/80', + cidr='fe80::/64', ip_version=6, ipv6_ra_mode=None, ipv6_address_mode=constants.IPV6_SLAAC) @@ -2359,6 +2359,18 @@ class TestSubnetsV2(NeutronDbPluginV2TestCase): res = subnet_req.get_response(self.api) self.assertEqual(res.status_int, webob.exc.HTTPClientError.code) + def test_create_subnet_V6_slaac_big_prefix(self): + with self.network() as network: + data = {'subnet': {'network_id': network['network']['id'], + 'cidr': '2014::/65', + 'ip_version': '6', + 'tenant_id': network['network']['tenant_id'], + 'gateway_ip': 'fe80::1', + 'ipv6_address_mode': 'slaac'}} + subnet_req = self.new_create_request('subnets', data) + res = subnet_req.get_response(self.api) + self.assertEqual(webob.exc.HTTPClientError.code, res.status_int) + def test_create_2_subnets_overlapping_cidr_allowed_returns_200(self): cidr_1 = '10.0.0.0/23' cidr_2 = '10.0.0.0/24' @@ -3026,7 +3038,7 @@ class TestSubnetsV2(NeutronDbPluginV2TestCase): def test_create_subnet_ipv6_attributes(self): gateway_ip = 'fe80::1' - cidr = 'fe80::/80' + cidr = 'fe80::/64' for mode in constants.IPV6_MODES: self._test_create_subnet(gateway_ip=gateway_ip, @@ -3058,7 +3070,7 @@ class TestSubnetsV2(NeutronDbPluginV2TestCase): def test_create_subnet_ipv6_attributes_no_dhcp_enabled(self): gateway_ip = 'fe80::1' - cidr = 'fe80::/80' + cidr = 'fe80::/64' with testlib_api.ExpectedException( webob.exc.HTTPClientError) as ctx_manager: for mode in constants.IPV6_MODES: @@ -3108,7 +3120,7 @@ class TestSubnetsV2(NeutronDbPluginV2TestCase): def test_create_subnet_ipv6_single_attribute_set(self): gateway_ip = 'fe80::1' - cidr = 'fe80::/80' + cidr = 'fe80::/64' for mode in constants.IPV6_MODES: self._test_create_subnet(gateway_ip=gateway_ip, cidr=cidr, ip_version=6, @@ -3299,7 +3311,7 @@ class TestSubnetsV2(NeutronDbPluginV2TestCase): webob.exc.HTTPConflict.code) def test_update_subnet_ipv6_attributes(self): - with self.subnet(ip_version=6, cidr='fe80::/80', + with self.subnet(ip_version=6, cidr='fe80::/64', ipv6_ra_mode=constants.IPV6_SLAAC, ipv6_address_mode=constants.IPV6_SLAAC) as subnet: data = {'subnet': {'ipv6_ra_mode': constants.DHCPV6_STATEFUL, @@ -3313,7 +3325,7 @@ class TestSubnetsV2(NeutronDbPluginV2TestCase): data['subnet']['ipv6_address_mode']) def test_update_subnet_ipv6_inconsistent_ra_attribute(self): - with self.subnet(ip_version=6, cidr='fe80::/80', + with self.subnet(ip_version=6, cidr='fe80::/64', ipv6_ra_mode=constants.IPV6_SLAAC, ipv6_address_mode=constants.IPV6_SLAAC) as subnet: data = {'subnet': {'ipv6_ra_mode': constants.DHCPV6_STATEFUL}} @@ -3324,7 +3336,7 @@ class TestSubnetsV2(NeutronDbPluginV2TestCase): webob.exc.HTTPClientError.code) def test_update_subnet_ipv6_inconsistent_address_attribute(self): - with self.subnet(ip_version=6, cidr='fe80::/80', + with self.subnet(ip_version=6, cidr='fe80::/64', ipv6_ra_mode=constants.IPV6_SLAAC, ipv6_address_mode=constants.IPV6_SLAAC) as subnet: data = {'subnet': {'ipv6_address_mode': constants.DHCPV6_STATEFUL}} @@ -3335,7 +3347,7 @@ class TestSubnetsV2(NeutronDbPluginV2TestCase): webob.exc.HTTPClientError.code) def test_update_subnet_ipv6_inconsistent_enable_dhcp(self): - with self.subnet(ip_version=6, cidr='fe80::/80', + with self.subnet(ip_version=6, cidr='fe80::/64', ipv6_ra_mode=constants.IPV6_SLAAC, ipv6_address_mode=constants.IPV6_SLAAC) as subnet: data = {'subnet': {'enable_dhcp': False}} -- 2.45.2