Make sure that gateway is in CIDR range by default

Git commit c3706fa2 introduced the force_gateway_on_subnet
option that verified that the defined gateway is in the CIDR
range of a newly created or updated subnet. However, the default
value was False for backwards compatability reasons. The default
will change to True and the option will be marked as deprecated.

For IPv6, the gateway must be in the CIDR only if the gateway
is not a link local address.

DocImpact
Change-Id: I04fd1caec6da5dceee3f736b3f91f2468150ba2a
Closes-Bug: #1304181
This commit is contained in:
Assaf Muller 2014-05-07 18:05:42 +03:00
parent 4e1ff7bba2
commit 0e44b7b541
8 changed files with 72 additions and 38 deletions

View File

@ -97,8 +97,10 @@ lock_path = $state_path/lock
# Attention: the following parameter MUST be set to False if Neutron is
# being used in conjunction with nova security groups
# allow_overlapping_ips = False
# Ensure that configured gateway is on subnet
# force_gateway_on_subnet = False
# Ensure that configured gateway is on subnet. For IPv6, validate only if
# gateway is not a link local address. Deprecated, to be removed during the
# K release, at which point the check will be mandatory.
# force_gateway_on_subnet = True
# Default maximum number of items returned in a single response,
# value == infinite and value < 0 means no max limit, and value must

View File

@ -80,8 +80,12 @@ core_opts = [
help=_("Allow overlapping IP support in Neutron")),
cfg.StrOpt('host', default=utils.get_hostname(),
help=_("The hostname Neutron is running on")),
cfg.BoolOpt('force_gateway_on_subnet', default=False,
help=_("Ensure that configured gateway is on subnet")),
cfg.BoolOpt('force_gateway_on_subnet', default=True,
help=_("Ensure that configured gateway is on subnet. "
"For IPv6, validate only if gateway is not a link "
"local address. Deprecated, to be removed during the "
"K release, at which point the check will be "
"mandatory.")),
cfg.BoolOpt('notify_nova_on_port_status_changes', default=True,
help=_("Send notification to nova when port status changes")),
cfg.BoolOpt('notify_nova_on_port_data_changes', default=True,

View File

@ -491,8 +491,16 @@ class NeutronDbPluginV2(neutron_plugin_base_v2.NeutronPluginBaseV2,
return True
return False
@staticmethod
def _check_subnet_ip(cidr, ip_address):
@classmethod
def _check_gateway_in_subnet(cls, cidr, gateway):
"""Validate that the gateway is on the subnet."""
ip = netaddr.IPAddress(gateway)
if ip.version == 4 or (ip.version == 6 and not ip.is_link_local()):
return cls._check_subnet_ip(cidr, gateway)
return True
@classmethod
def _check_subnet_ip(cls, cidr, ip_address):
"""Validate that the IP address is on the subnet."""
ip = netaddr.IPAddress(ip_address)
net = netaddr.IPNetwork(cidr)
@ -549,8 +557,8 @@ class NeutronDbPluginV2(neutron_plugin_base_v2.NeutronPluginBaseV2,
filter = {'network_id': [network_id]}
subnets = self.get_subnets(context, filters=filter)
for subnet in subnets:
if NeutronDbPluginV2._check_subnet_ip(subnet['cidr'],
fixed['ip_address']):
if self._check_subnet_ip(subnet['cidr'],
fixed['ip_address']):
found = True
subnet_id = subnet['id']
break
@ -579,8 +587,8 @@ class NeutronDbPluginV2(neutron_plugin_base_v2.NeutronPluginBaseV2,
# Ensure that the IP is valid on the subnet
if (not found and
not NeutronDbPluginV2._check_subnet_ip(
subnet['cidr'], fixed['ip_address'])):
not self._check_subnet_ip(subnet['cidr'],
fixed['ip_address'])):
msg = _('IP address %s is not a valid IP for the defined '
'subnet') % fixed['ip_address']
raise n_exc.InvalidInput(error_message=msg)
@ -1099,8 +1107,8 @@ class NeutronDbPluginV2(neutron_plugin_base_v2.NeutronPluginBaseV2,
if attributes.is_attr_set(s.get('gateway_ip')):
self._validate_ip_version(ip_ver, s['gateway_ip'], 'gateway_ip')
if (cfg.CONF.force_gateway_on_subnet and
not NeutronDbPluginV2._check_subnet_ip(s['cidr'],
s['gateway_ip'])):
not self._check_gateway_in_subnet(
s['cidr'], s['gateway_ip'])):
error_message = _("Gateway is not valid on subnet")
raise n_exc.InvalidInput(error_message=error_message)
# Ensure the gateway IP is not assigned to any port

View File

@ -461,7 +461,7 @@ class TestFaultyMechansimDriver(Ml2PluginV2FaultyDriverTestCase):
'name': 'subnet1',
'tenant_id':
network['network']['tenant_id'],
'gateway_ip': '10.0.2.1'}}
'gateway_ip': '10.0.20.1'}}
req = self.new_create_request('subnets', data)
res = req.get_response(self.api)
self.assertEqual(500, res.status_int)
@ -488,7 +488,7 @@ class TestFaultyMechansimDriver(Ml2PluginV2FaultyDriverTestCase):
'name': 'subnet1',
'tenant_id':
network['network']['tenant_id'],
'gateway_ip': '10.0.2.1'}}
'gateway_ip': '10.0.20.1'}}
subnet_req = self.new_create_request('subnets', data)
subnet_res = subnet_req.get_response(self.api)
self.assertEqual(201, subnet_res.status_int)
@ -519,7 +519,7 @@ class TestFaultyMechansimDriver(Ml2PluginV2FaultyDriverTestCase):
'name': 'subnet1',
'tenant_id':
network['network']['tenant_id'],
'gateway_ip': '10.0.2.1'}}
'gateway_ip': '10.0.20.1'}}
subnet_req = self.new_create_request('subnets', data)
subnet_res = subnet_req.get_response(self.api)
self.assertEqual(201, subnet_res.status_int)

View File

@ -216,6 +216,10 @@ class TestNuageSubnetsV2(NuagePluginV2TestCase,
self.skipTest("Plugin does not support "
"Neutron Subnet no-gateway option")
def test_create_subnet_nonzero_cidr(self):
self.skipTest("Plugin does not support "
"Neutron Subnet no-gateway option")
def test_create_subnet_with_none_gateway_fully_allocated(self):
self.skipTest("Plugin does not support Neutron "
"Subnet no-gateway option")
@ -235,7 +239,9 @@ class TestNuagePluginPortBinding(NuagePluginV2TestCase,
class TestNuagePortsV2(NuagePluginV2TestCase,
test_db_plugin.TestPortsV2):
pass
def test_no_more_port_exception(self):
self.skipTest("Plugin does not support "
"Neutron Subnet no-gateway option")
class TestNuageL3NatTestCase(NuagePluginV2TestCase,

View File

@ -87,6 +87,12 @@ class TestOneConvergencePluginSubnetsV2(test_plugin.TestSubnetsV2,
def test_update_subnet_ipv6_inconsistent_address_attribute(self):
self.skipTest("NVSD Plugin does not support IPV6.")
def test_create_subnet_ipv6_out_of_cidr_global(self):
self.skipTest("NVSD Plugin does not support IPV6.")
def test_create_subnet_ipv6_out_of_cidr_lla(self):
self.skipTest("NVSD Plugin does not support IPV6.")
class TestOneConvergencePluginPortsV2(test_plugin.TestPortsV2,
test_bindings.PortBindingsTestCase,

View File

@ -1144,7 +1144,7 @@ fixed_ips=ip_address%%3D%s&fixed_ips=ip_address%%3D%s&fixed_ips=subnet_id%%3D%s
data['port']['fixed_ips'])
def test_no_more_port_exception(self):
with self.subnet(cidr='10.0.0.0/32') as subnet:
with self.subnet(cidr='10.0.0.0/32', gateway_ip=None) as subnet:
id = subnet['subnet']['network_id']
res = self._create_port(self.fmt, id)
data = self.deserialize(self.fmt, res)
@ -2583,7 +2583,7 @@ class TestSubnetsV2(NeutronDbPluginV2TestCase):
self.subnet(cidr='14.129.122.5/22'),
self.subnet(cidr='15.129.122.5/24'),
self.subnet(cidr='16.129.122.5/28'),
self.subnet(cidr='17.129.122.5/32')
self.subnet(cidr='17.129.122.5/32', gateway_ip=None)
) as subs:
# the API should accept and correct these for users
self.assertEqual(subs[0]['subnet']['cidr'], '10.0.0.0/8')
@ -2724,15 +2724,7 @@ class TestSubnetsV2(NeutronDbPluginV2TestCase):
allocation_pools)
def test_create_subnet_gw_values(self):
# Gateway not in subnet
gateway = '100.0.0.1'
cidr = '10.0.0.0/24'
allocation_pools = [{'start': '10.0.0.1',
'end': '10.0.0.254'}]
expected = {'gateway_ip': gateway,
'cidr': cidr,
'allocation_pools': allocation_pools}
self._test_create_subnet(expected=expected, gateway_ip=gateway)
# Gateway is last IP in range
gateway = '10.0.0.254'
allocation_pools = [{'start': '10.0.0.1',
@ -2751,8 +2743,7 @@ class TestSubnetsV2(NeutronDbPluginV2TestCase):
self._test_create_subnet(expected=expected,
gateway_ip=gateway)
def test_create_subnet_gw_outside_cidr_force_on_returns_400(self):
cfg.CONF.set_override('force_gateway_on_subnet', True)
def test_create_subnet_gw_outside_cidr_returns_400(self):
with self.network() as network:
self._create_subnet(self.fmt,
network['network']['id'],
@ -2760,8 +2751,7 @@ class TestSubnetsV2(NeutronDbPluginV2TestCase):
webob.exc.HTTPClientError.code,
gateway_ip='100.0.0.1')
def test_create_subnet_gw_of_network_force_on_returns_400(self):
cfg.CONF.set_override('force_gateway_on_subnet', True)
def test_create_subnet_gw_of_network_returns_400(self):
with self.network() as network:
self._create_subnet(self.fmt,
network['network']['id'],
@ -2769,8 +2759,7 @@ class TestSubnetsV2(NeutronDbPluginV2TestCase):
webob.exc.HTTPClientError.code,
gateway_ip='10.0.0.0')
def test_create_subnet_gw_bcast_force_on_returns_400(self):
cfg.CONF.set_override('force_gateway_on_subnet', True)
def test_create_subnet_gw_bcast_returns_400(self):
with self.network() as network:
self._create_subnet(self.fmt,
network['network']['id'],
@ -3038,6 +3027,28 @@ class TestSubnetsV2(NeutronDbPluginV2TestCase):
ipv6_ra_mode=mode,
ipv6_address_mode=mode)
def test_create_subnet_ipv6_out_of_cidr_global(self):
gateway_ip = '2000::1'
cidr = '2001::/64'
with testlib_api.ExpectedException(
webob.exc.HTTPClientError) as ctx_manager:
self._test_create_subnet(
gateway_ip=gateway_ip, cidr=cidr, ip_version=6,
ipv6_ra_mode=constants.DHCPV6_STATEFUL,
ipv6_address_mode=constants.DHCPV6_STATEFUL)
self.assertEqual(ctx_manager.exception.code,
webob.exc.HTTPClientError.code)
def test_create_subnet_ipv6_out_of_cidr_lla(self):
gateway_ip = 'fe80::1'
cidr = '2001::/64'
self._test_create_subnet(
gateway_ip=gateway_ip, cidr=cidr, ip_version=6,
ipv6_ra_mode=constants.IPV6_SLAAC,
ipv6_address_mode=constants.IPV6_SLAAC)
def test_create_subnet_ipv6_attributes_no_dhcp_enabled(self):
gateway_ip = 'fe80::1'
cidr = 'fe80::/80'
@ -3122,7 +3133,7 @@ class TestSubnetsV2(NeutronDbPluginV2TestCase):
def test_update_subnet_no_gateway(self):
with self.subnet() as subnet:
data = {'subnet': {'gateway_ip': '11.0.0.1'}}
data = {'subnet': {'gateway_ip': '10.0.0.1'}}
req = self.new_update_request('subnets', data,
subnet['subnet']['id'])
res = self.deserialize(self.fmt, req.get_response(self.api))
@ -3136,7 +3147,7 @@ class TestSubnetsV2(NeutronDbPluginV2TestCase):
def test_update_subnet(self):
with self.subnet() as subnet:
data = {'subnet': {'gateway_ip': '11.0.0.1'}}
data = {'subnet': {'gateway_ip': '10.0.0.1'}}
req = self.new_update_request('subnets', data,
subnet['subnet']['id'])
res = self.deserialize(self.fmt, req.get_response(self.api))
@ -3182,8 +3193,7 @@ class TestSubnetsV2(NeutronDbPluginV2TestCase):
self.assertEqual(res.status_int,
webob.exc.HTTPClientError.code)
def test_update_subnet_gw_outside_cidr_force_on_returns_400(self):
cfg.CONF.set_override('force_gateway_on_subnet', True)
def test_update_subnet_gw_outside_cidr_returns_400(self):
with self.network() as network:
with self.subnet(network=network) as subnet:
data = {'subnet': {'gateway_ip': '100.0.0.1'}}

View File

@ -880,8 +880,6 @@ class L3NatTestCaseBase(L3NatTestCaseMixin):
try_overlapped_cidr('10.0.1.0/24')
# another subnet with overlapped cidr including s1
try_overlapped_cidr('10.0.0.0/16')
# another subnet with overlapped cidr included by s1
try_overlapped_cidr('10.0.1.1/32')
# clean-up
self._router_interface_action('remove',
r['router']['id'],