diff --git a/neutron/ipam/subnet_alloc.py b/neutron/ipam/subnet_alloc.py index dc38f43752a..d1a6f681f7c 100644 --- a/neutron/ipam/subnet_alloc.py +++ b/neutron/ipam/subnet_alloc.py @@ -126,13 +126,19 @@ class SubnetAllocator(driver.Pool): self._check_subnetpool_tenant_quota(request.tenant_id, request.prefixlen) cidr = request.subnet_cidr + gateway = request.gateway_ip + if gateway and not ipam_utils.check_subnet_ip(cidr, gateway): + msg = _("Cannot allocate requested subnet due to bad gateway " + "address") + raise n_exc.SubnetAllocationError(reason=msg) + available = self._get_available_prefix_list() matched = netaddr.all_matching_cidrs(cidr, available) if len(matched) is 1 and matched[0].prefixlen <= cidr.prefixlen: return IpamSubnet(request.tenant_id, request.subnet_id, cidr, - gateway_ip=request.gateway_ip, + gateway_ip=gateway, allocation_pools=request.allocation_pools) msg = _("Cannot allocate requested subnet from the available " "set of prefixes") diff --git a/neutron/ipam/utils.py b/neutron/ipam/utils.py index 74927769ad7..3f0bda173b7 100644 --- a/neutron/ipam/utils.py +++ b/neutron/ipam/utils.py @@ -21,8 +21,9 @@ def check_subnet_ip(cidr, ip_address): ip = netaddr.IPAddress(ip_address) net = netaddr.IPNetwork(cidr) # Check that the IP is valid on subnet. This cannot be the - # network or the broadcast address - return (ip != net.network and ip != net.broadcast + # network or the broadcast address (which exists only in IPv4) + return (ip != net.network + and (net.version == 6 or ip != net.broadcast) and net.netmask & ip == net.network) diff --git a/neutron/tests/unit/ipam/test_subnet_alloc.py b/neutron/tests/unit/ipam/test_subnet_alloc.py index 25021af2fdc..8923ef8de6a 100644 --- a/neutron/tests/unit/ipam/test_subnet_alloc.py +++ b/neutron/tests/unit/ipam/test_subnet_alloc.py @@ -146,6 +146,37 @@ class TestSubnetAllocation(testlib_api.SqlTestCase): self.assertEqual(detail.gateway_ip, netaddr.IPAddress('10.1.2.254')) + def test_allocate_specific_ipv6_subnet_specific_gateway(self): + # Same scenario as described in bug #1466322 + sp = self._create_subnet_pool(self.plugin, self.ctx, 'test-sp', + ['2210::/64'], + 64, 6) + sp = self.plugin._get_subnetpool(self.ctx, sp['id']) + with self.ctx.session.begin(subtransactions=True): + sa = subnet_alloc.SubnetAllocator(sp, self.ctx) + req = ipam.SpecificSubnetRequest(self._tenant_id, + uuidutils.generate_uuid(), + '2210::/64', + '2210::ffff:ffff:ffff:ffff') + res = sa.allocate_subnet(req) + detail = res.get_details() + self.assertEqual(detail.gateway_ip, + netaddr.IPAddress('2210::ffff:ffff:ffff:ffff')) + + def test_allocate_specific_ipv4_subnet_specific_broadcast_gateway(self): + # Valid failure in subnet creation due to gateway==broadcast ip + sp = self._create_subnet_pool(self.plugin, self.ctx, 'test-sp', + ['10.1.0.0/24'], + 24, 4) + sa = subnet_alloc.SubnetAllocator(sp, self.ctx) + req = ipam.SpecificSubnetRequest(self._tenant_id, + uuidutils.generate_uuid(), + '10.1.0.0/24', + gateway_ip='10.1.0.255') + self.assertRaises(n_exc.SubnetAllocationError, + sa.allocate_subnet, + req) + def test__allocation_value_for_tenant_no_allocations(self): sp = self._create_subnet_pool(self.plugin, self.ctx, 'test-sp', ['10.1.0.0/16', '192.168.1.0/24'],