NSX|V3+P: Ensure router GW & interfaces do not overlap

The NSX backend will fail to add NAT rules in case the GW network
and the interface networks overlap.
This patch will ensure that the GW and interfaces do not overlap

Change-Id: I6a6c6be865dc05a1f73f17f47e182c7087cb8a21
This commit is contained in:
Adit Sarfaty 2019-03-27 11:05:42 +02:00
parent 8b48578f69
commit 1a607f7941
6 changed files with 150 additions and 26 deletions

View File

@ -1189,6 +1189,26 @@ class NsxPluginV3Base(agentschedulers_db.AZDhcpAgentSchedulerDbMixin,
self._validate_router_tz(context, new_tier0_uuid,
router_subnets)
def _validate_gw_overlap_interfaces(self, context, gateway_net,
interfaces_networks):
# Ensure that interface subnets cannot overlap with the GW subnet
gw_subnets = self._get_subnets_by_network(
context.elevated(), gateway_net)
gw_cidrs = [subnet['cidr'] for subnet in gw_subnets]
gw_ip_set = netaddr.IPSet(gw_cidrs)
if_subnets = []
for net in interfaces_networks:
if_subnets.extend(self._get_subnets_by_network(
context.elevated(), net))
if_cidrs = [subnet['cidr'] for subnet in if_subnets]
if_ip_set = netaddr.IPSet(if_cidrs)
if gw_ip_set & if_ip_set:
msg = _("Interface network cannot overlap with router GW network")
LOG.error(msg)
raise n_exc.InvalidInput(error_message=msg)
def _get_update_router_gw_actions(
self,
org_tier0_uuid, orgaddr, org_enable_snat,

View File

@ -1364,6 +1364,11 @@ class NsxPolicyPlugin(nsx_plugin_common.NsxPluginV3Base):
context.elevated(), router_id)
self._validate_router_gw_and_tz(context, router_id, info,
org_enable_snat, router_subnets)
# Interface subnets cannot overlap with the GW external subnet
if info and info.get('network_id'):
self._validate_gw_overlap_interfaces(
context, info['network_id'],
[sub['network_id'] for sub in router_subnets])
# First update the neutron DB
super(NsxPolicyPlugin, self)._update_router_gw_info(
@ -1612,6 +1617,10 @@ class NsxPolicyPlugin(nsx_plugin_common.NsxPluginV3Base):
"must have an external network assigned")
raise n_exc.InvalidInput(error_message=msg)
# Interface subnets cannot overlap with the GW external subnet
self._validate_gw_overlap_interfaces(context, gw_network_id,
[network_id])
# Update the interface of the neutron router
info = super(NsxPolicyPlugin, self).add_router_interface(
context, router_id, interface_info)

View File

@ -2144,6 +2144,11 @@ class NsxV3Plugin(nsx_plugin_common.NsxPluginV3Base,
context.elevated(), router_id)
self._validate_router_gw_and_tz(context, router_id, info,
org_enable_snat, router_subnets)
# Interface subnets cannot overlap with the GW external subnet
if info and info.get('network_id'):
self._validate_gw_overlap_interfaces(
context, info['network_id'],
[sub['network_id'] for sub in router_subnets])
# TODO(berlin): For nonat use case, we actually don't need a gw port
# which consumes one external ip. But after looking at the DB logic
@ -2688,6 +2693,10 @@ class NsxV3Plugin(nsx_plugin_common.NsxPluginV3Base,
"must have an external network assigned")
raise n_exc.InvalidInput(error_message=msg)
# Interface subnets cannot overlap with the GW external subnet
self._validate_gw_overlap_interfaces(context, gw_network_id,
[network_id])
# Update the interface of the neutron router
info = self._add_router_interface_wrapper(context, router_id,
interface_info)

View File

@ -1679,8 +1679,13 @@ class NsxPTestL3NatTestCase(NsxPTestL3NatTest,
@common_v3.with_external_subnet_once
def test_router_update_gateway_with_existed_floatingip(self):
super(NsxPTestL3NatTestCase,
self).test_router_update_gateway_with_existed_floatingip()
with self.subnet(cidr='20.0.0.0/24') as subnet:
self._set_net_external(subnet['subnet']['network_id'])
with self.floatingip_with_assoc() as fip:
self._add_external_gateway_to_router(
fip['floatingip']['router_id'],
subnet['subnet']['network_id'],
expected_code=exc.HTTPConflict.code)
@common_v3.with_external_network
def test_router_update_gateway_add_multiple_prefixes_ipv6(self):
@ -1692,11 +1697,6 @@ class NsxPTestL3NatTestCase(NsxPTestL3NatTest,
super(NsxPTestL3NatTestCase,
self).test_router_concurrent_delete_upon_subnet_create()
@common_v3.with_external_subnet_second_time
def test_router_add_interface_cidr_overlapped_with_gateway(self):
super(NsxPTestL3NatTestCase,
self).test_router_add_interface_cidr_overlapped_with_gateway()
@common_v3.with_external_subnet
def test_router_add_gateway_dup_subnet2_returns_400(self):
super(NsxPTestL3NatTestCase,
@ -1737,11 +1737,6 @@ class NsxPTestL3NatTestCase(NsxPTestL3NatTest,
super(NsxPTestL3NatTestCase,
self).test_router_add_and_remove_gateway_tenant_ctx()
@common_v3.with_external_subnet_second_time
def test_router_add_interface_by_port_cidr_overlapped_with_gateway(self):
super(NsxPTestL3NatTestCase, self).\
test_router_add_interface_by_port_cidr_overlapped_with_gateway()
@common_v3.with_external_subnet
def test_router_add_and_remove_gateway(self):
super(NsxPTestL3NatTestCase,
@ -1950,3 +1945,51 @@ class NsxPTestL3NatTestCase(NsxPTestL3NatTest,
s['subnet']['network_id'])
add_srv_router.assert_called_once_with(
mock.ANY, '%s%s' % (path_prefix, edge_cluster))
def test_router_add_interface_cidr_overlapped_with_gateway(self):
with self.router() as r,\
self._create_l3_ext_network() as ext_net,\
self.subnet(cidr='10.0.1.0/24') as s1,\
self.subnet(network=ext_net, cidr='10.0.0.0/16',
enable_dhcp=False) as s2:
self._add_external_gateway_to_router(
r['router']['id'],
s2['subnet']['network_id'])
res = self._router_interface_action(
'add', r['router']['id'],
s1['subnet']['id'], None,
expected_code=exc.HTTPBadRequest.code)
self.assertIn('NeutronError', res)
def test_router_add_gateway_overlapped_with_interface_cidr(self):
with self.router() as r,\
self._create_l3_ext_network() as ext_net,\
self.subnet(cidr='10.0.1.0/24') as s1,\
self.subnet(network=ext_net, cidr='10.0.0.0/16',
enable_dhcp=False) as s2:
self._router_interface_action(
'add', r['router']['id'],
s1['subnet']['id'], None)
res = self._add_external_gateway_to_router(
r['router']['id'],
s2['subnet']['network_id'],
expected_code=exc.HTTPBadRequest.code)
self.assertIn('NeutronError', res)
def test_router_add_interface_by_port_cidr_overlapped_with_gateway(self):
with self.router() as r,\
self._create_l3_ext_network() as ext_net,\
self.subnet(cidr='10.0.1.0/24') as s1,\
self.subnet(network=ext_net, cidr='10.0.0.0/16',
enable_dhcp=False) as s2,\
self.port(subnet=s1) as p:
self._add_external_gateway_to_router(
r['router']['id'],
s2['subnet']['network_id'])
res = self._router_interface_action(
'add', r['router']['id'],
None,
p['port']['id'],
expected_code=exc.HTTPBadRequest.code)
self.assertIn('NeutronError', res)

View File

@ -2038,8 +2038,13 @@ class TestL3NatTestCase(L3NatTest,
@common_v3.with_external_subnet_once
def test_router_update_gateway_with_existed_floatingip(self):
super(TestL3NatTestCase,
self).test_router_update_gateway_with_existed_floatingip()
with self.subnet(cidr='20.0.0.0/24') as subnet:
self._set_net_external(subnet['subnet']['network_id'])
with self.floatingip_with_assoc() as fip:
self._add_external_gateway_to_router(
fip['floatingip']['router_id'],
subnet['subnet']['network_id'],
expected_code=exc.HTTPConflict.code)
@common_v3.with_external_network
def test_router_update_gateway_add_multiple_prefixes_ipv6(self):
@ -2056,11 +2061,6 @@ class TestL3NatTestCase(L3NatTest,
super(TestL3NatTestCase,
self).test_router_update_gateway_upon_subnet_create_ipv6()
@common_v3.with_external_subnet_second_time
def test_router_add_interface_cidr_overlapped_with_gateway(self):
super(TestL3NatTestCase,
self).test_router_add_interface_cidr_overlapped_with_gateway()
@common_v3.with_external_subnet
def test_router_add_gateway_dup_subnet2_returns_400(self):
super(TestL3NatTestCase,
@ -2101,11 +2101,6 @@ class TestL3NatTestCase(L3NatTest,
super(TestL3NatTestCase,
self).test_router_add_and_remove_gateway_tenant_ctx()
@common_v3.with_external_subnet_second_time
def test_router_add_interface_by_port_cidr_overlapped_with_gateway(self):
super(TestL3NatTestCase, self).\
test_router_add_interface_by_port_cidr_overlapped_with_gateway()
@common_v3.with_external_subnet
def test_router_add_and_remove_gateway(self):
super(TestL3NatTestCase,
@ -3058,6 +3053,54 @@ class TestL3NatTestCase(L3NatTest,
enable_standby_relocation=True)
self.mock_get_edge_cluster.start()
def test_router_add_interface_cidr_overlapped_with_gateway(self):
with self.router() as r,\
self._create_l3_ext_network() as ext_net,\
self.subnet(cidr='10.0.1.0/24') as s1,\
self.subnet(network=ext_net, cidr='10.0.0.0/16',
enable_dhcp=False) as s2:
self._add_external_gateway_to_router(
r['router']['id'],
s2['subnet']['network_id'])
res = self._router_interface_action(
'add', r['router']['id'],
s1['subnet']['id'], None,
expected_code=exc.HTTPBadRequest.code)
self.assertIn('NeutronError', res)
def test_router_add_gateway_overlapped_with_interface_cidr(self):
with self.router() as r,\
self._create_l3_ext_network() as ext_net,\
self.subnet(cidr='10.0.1.0/24') as s1,\
self.subnet(network=ext_net, cidr='10.0.0.0/16',
enable_dhcp=False) as s2:
self._router_interface_action(
'add', r['router']['id'],
s1['subnet']['id'], None)
res = self._add_external_gateway_to_router(
r['router']['id'],
s2['subnet']['network_id'],
expected_code=exc.HTTPBadRequest.code)
self.assertIn('NeutronError', res)
def test_router_add_interface_by_port_cidr_overlapped_with_gateway(self):
with self.router() as r,\
self._create_l3_ext_network() as ext_net,\
self.subnet(cidr='10.0.1.0/24') as s1,\
self.subnet(network=ext_net, cidr='10.0.0.0/16',
enable_dhcp=False) as s2,\
self.port(subnet=s1) as p:
self._add_external_gateway_to_router(
r['router']['id'],
s2['subnet']['network_id'])
res = self._router_interface_action(
'add', r['router']['id'],
None,
p['port']['id'],
expected_code=exc.HTTPBadRequest.code)
self.assertIn('NeutronError', res)
class ExtGwModeTestCase(test_ext_gw_mode.ExtGwModeIntTestCase,
L3NatTest):

View File

@ -613,7 +613,7 @@ class TestVpnaasDriver(test_plugin.NsxV3PluginTestCaseMixin):
return_value=tier0_uuid),\
self.router(external_gateway_info={'network_id':
ext_net['network']['id']}) as router,\
self.subnet() as sub:
self.subnet(cidr='1.1.0.0/24') as sub:
# add an interface to the router
self.l3plugin.add_router_interface(
self.context,
@ -665,7 +665,7 @@ class TestVpnaasDriver(test_plugin.NsxV3PluginTestCaseMixin):
return_value=tier0_rtr_id),\
self.router(external_gateway_info={'network_id':
ext_net['network']['id']}) as router,\
self.subnet() as sub:
self.subnet(cidr='1.1.0.0/24') as sub:
# add an interface to the router
self.l3plugin.add_router_interface(
self.context,