Using 31-Bit and 32-Bit prefixes for IPv4 reasonably

When needing to create a point to point connection via a subnet,
generally and /31 is the recommended cidr. Neutron supports /31
disabling dhcp and gateway on a subnet. /32 is also supported in
openstack.

Closes-Bug: #1580927
Change-Id: I3bfa3efb9fb8076656b16c89d2f35d74efde12b7
This commit is contained in:
Nurmatov Mamatisa 2021-02-25 21:19:17 +03:00 committed by Mamatisa Nurmatov
parent 535a13be9c
commit 437a311eca
7 changed files with 101 additions and 21 deletions

View File

@ -310,9 +310,17 @@ class IpamBackendMixin(db_base_plugin_common.DbBasePluginCommon):
Finally, verify that each range fall within the subnet's CIDR. Finally, verify that each range fall within the subnet's CIDR.
""" """
subnet = netaddr.IPNetwork(subnet_cidr) subnet = netaddr.IPNetwork(subnet_cidr)
subnet_first_ip = netaddr.IPAddress(subnet.first + 1) if subnet.version == const.IP_VERSION_4:
# last address is broadcast in v4 if subnet.prefixlen <= 30:
subnet_last_ip = netaddr.IPAddress(subnet.last - (subnet.version == 4)) subnet_first_ip = netaddr.IPAddress(subnet.first + 1)
# last address is broadcast in v4
subnet_last_ip = netaddr.IPAddress(subnet.last - 1)
else:
subnet_first_ip = netaddr.IPAddress(subnet.first)
subnet_last_ip = netaddr.IPAddress(subnet.last)
else: # IPv6 case
subnet_first_ip = netaddr.IPAddress(subnet.first + 1)
subnet_last_ip = netaddr.IPAddress(subnet.last)
LOG.debug("Performing IP validity checks on allocation pools") LOG.debug("Performing IP validity checks on allocation pools")
ip_sets = [] ip_sets = []

View File

@ -62,12 +62,17 @@ def generate_pools(cidr, gateway_ip):
if first == last: if first == last:
# handle single address subnet case # handle single address subnet case
return [netaddr.IPRange(first, last)] return [netaddr.IPRange(first, last)]
first_ip = first + 1 if ip_version == constants.IP_VERSION_4:
# last address is broadcast in v4 if net.prefixlen <= 30:
last_ip = last - (ip_version == 4) first_ip = first + 1
if first_ip >= last_ip: # last address is broadcast in v4
# /31 lands here last_ip = last - (ip_version == 4)
return [] else:
first_ip = first
last_ip = last
else: # IPv6 case
first_ip = first + 1
last_ip = last
ipset = netaddr.IPSet(netaddr.IPRange(first_ip, last_ip)) ipset = netaddr.IPSet(netaddr.IPRange(first_ip, last_ip))
if gateway_ip: if gateway_ip:
ipset.remove(netaddr.IPAddress(gateway_ip, ip_version)) ipset.remove(netaddr.IPAddress(gateway_ip, ip_version))

View File

@ -129,7 +129,10 @@ class FakeFullstackMachine(machine_fixtures.FakeMachineBase):
def _configure_static_ipaddress(self): def _configure_static_ipaddress(self):
self.port.addr.add(self.ip_cidr) self.port.addr.add(self.ip_cidr)
if self.gateway_ip: if self.gateway_ip:
net_helpers.set_namespace_gateway(self.port, self.gateway_ip) net = netaddr.IPNetwork(self.ip_cidr)
gateway_ip = netaddr.IPAddress(self.gateway_ip)
if gateway_ip in net:
net_helpers.set_namespace_gateway(self.port, self.gateway_ip)
def _configure_ipaddress_via_dhcp(self): def _configure_ipaddress_via_dhcp(self):
self._start_async_dhclient() self._start_async_dhclient()

View File

@ -20,6 +20,7 @@ from oslo_utils import uuidutils
from neutron.tests.common.exclusive_resources import ip_network from neutron.tests.common.exclusive_resources import ip_network
from neutron.tests.fullstack import base from neutron.tests.fullstack import base
from neutron.tests.fullstack.resources import environment from neutron.tests.fullstack.resources import environment
from neutron.tests.fullstack.resources import machine
class TestSubnet(base.BaseFullStackTestCase): class TestSubnet(base.BaseFullStackTestCase):
@ -132,3 +133,32 @@ class TestSubnet(base.BaseFullStackTestCase):
subnet = self._show_subnet(subnet['id']) subnet = self._show_subnet(subnet['id'])
self.assertEqual(subnet['subnet']['cidr'], str(subnets[2].cidr)) self.assertEqual(subnet['subnet']['cidr'], str(subnets[2].cidr))
self.assertEqual(subnet['subnet']['gateway_ip'], str(gateway_ip)) self.assertEqual(subnet['subnet']['gateway_ip'], str(gateway_ip))
def test_subnet_with_prefixlen_31_connectivity(self):
network = self._create_network(self._project_id)
self.safe_client.create_subnet(
self._project_id, network['id'],
cidr='10.14.0.20/31',
gateway_ip='10.14.0.19',
name='subnet-test',
enable_dhcp=False)
vms = self._prepare_vms_in_net(self._project_id, network, False)
vms.ping_all()
def test_subnet_with_prefixlen_32_vm_spawn(self):
network = self._create_network(self._project_id)
self.safe_client.create_subnet(
self._project_id, network['id'],
cidr='10.14.0.20/32',
gateway_ip='10.14.0.19',
name='subnet-test',
enable_dhcp=False)
vm = self.useFixture(
machine.FakeFullstackMachine(
self.environment.hosts[0],
network['id'],
self._project_id,
self.safe_client))
vm.block_until_boot()

View File

@ -1693,16 +1693,6 @@ fixed_ips=ip_address%%3D%s&fixed_ips=ip_address%%3D%s&fixed_ips=subnet_id%%3D%s
self.assertEqual(data['port']['fixed_ips'], self.assertEqual(data['port']['fixed_ips'],
res['port']['fixed_ips']) res['port']['fixed_ips'])
def test_no_more_port_exception(self):
with self.subnet(cidr='10.0.0.0/31', enable_dhcp=False,
gateway_ip=None) as subnet:
id = subnet['subnet']['network_id']
res = self._create_port(self.fmt, id)
data = self.deserialize(self.fmt, res)
msg = str(lib_exc.IpAddressGenerationFailure(net_id=id))
self.assertEqual(data['NeutronError']['message'], msg)
self.assertEqual(webob.exc.HTTPConflict.code, res.status_int)
def test_create_ports_native_quotas(self): def test_create_ports_native_quotas(self):
quota = 1 quota = 1
cfg.CONF.set_override('quota_port', quota, group='QUOTAS') cfg.CONF.set_override('quota_port', quota, group='QUOTAS')
@ -5091,6 +5081,43 @@ class TestSubnetsV2(NeutronDbPluginV2TestCase):
res = req.get_response(self.api) res = req.get_response(self.api)
self.assertEqual(webob.exc.HTTPConflict.code, res.status_int) self.assertEqual(webob.exc.HTTPConflict.code, res.status_int)
def test_create_subnet_allocation_pools_with_prefixlen_31(self):
with self.network() as network:
with self.subnet(network=network,
enable_dhcp=False,
gateway_ip='10.14.0.19',
cidr='10.14.0.20/31'):
res = self._create_port(self.fmt, network['network']['id'])
self.assertEqual(webob.exc.HTTPCreated.code, res.status_int)
res = self._create_port(self.fmt, network['network']['id'])
self.assertEqual(webob.exc.HTTPCreated.code, res.status_int)
def test_update_subnet_allocation_pools_with_prefixlen_31(self):
with self.network() as network:
with self.subnet(network=network,
enable_dhcp=False,
gateway_ip='10.14.0.19',
cidr='10.14.0.20/31') as subnet:
data = {'subnet': {'allocation_pools': [
{'start': '10.14.0.20', 'end': '10.14.0.21'}]}}
req = self.new_update_request('subnets', data,
subnet['subnet']['id'])
res = req.get_response(self.api)
self.assertEqual(webob.exc.HTTPOk.code, res.status_int)
res = self._create_port(self.fmt, network['network']['id'])
self.assertEqual(webob.exc.HTTPCreated.code, res.status_int)
res = self._create_port(self.fmt, network['network']['id'])
self.assertEqual(webob.exc.HTTPCreated.code, res.status_int)
def test_create_subnet_allocation_pools_with_prefixlen_32(self):
with self.network() as network:
with self.subnet(network=network,
enable_dhcp=False,
gateway_ip='10.14.0.19',
cidr='10.14.0.20/32'):
res = self._create_port(self.fmt, network['network']['id'])
self.assertEqual(webob.exc.HTTPCreated.code, res.status_int)
def test_create_subnets_native_quotas(self): def test_create_subnets_native_quotas(self):
quota = 1 quota = 1
cfg.CONF.set_override('quota_subnet', quota, group='QUOTAS') cfg.CONF.set_override('quota_subnet', quota, group='QUOTAS')

View File

@ -89,7 +89,7 @@ class TestIpamUtils(base.BaseTestCase):
def test_generate_pools_v4_31(self): def test_generate_pools_v4_31(self):
cidr = '192.168.0.0/31' cidr = '192.168.0.0/31'
expected = [] expected = [netaddr.IPRange('192.168.0.0', '192.168.0.1')]
self.assertEqual(expected, utils.generate_pools(cidr, None)) self.assertEqual(expected, utils.generate_pools(cidr, None))
def test_generate_pools_v4_gateway_middle(self): def test_generate_pools_v4_gateway_middle(self):

View File

@ -0,0 +1,7 @@
---
features:
- |
Neutron supports creating IPv4 subnet with prefixlen /31 and /32,
via disabling dhcp on a subnet.
For more information, see bug
`1580927 <https://bugs.launchpad.net/neutron/+bug/1580927>`_.