NSX-V3 Add NO NAT rules for router interfaces

For each router interface subnets, we need to add NO DNAT rule whenever
NAT rules are added, or else internal traffic will be blocked.

Change-Id: I34d72b12289d6f6527bc114a32dac88281dd2cc4
This commit is contained in:
Adit Sarfaty 2018-02-22 14:40:22 +02:00
parent 36bb519b93
commit e7111bd5a7
3 changed files with 82 additions and 5 deletions

View File

@ -191,7 +191,8 @@ class NsxPluginBase(db_base_plugin_v2.NeutronDbPluginV2,
subnet = subnet_qry.filter_by(id=ip.subnet_id).one()
subnets.append({'id': subnet.id, 'cidr': subnet.cidr,
'subnetpool_id': subnet.subnetpool_id,
'ip_version': subnet.ip_version})
'ip_version': subnet.ip_version,
'network_id': subnet.network_id})
return subnets
def _find_router_gw_subnets(self, context, router):

View File

@ -117,6 +117,7 @@ from vmware_nsx.services.trunk.nsx_v3 import driver as trunk_driver
from vmware_nsxlib.v3 import core_resources as nsx_resources
from vmware_nsxlib.v3 import exceptions as nsx_lib_exc
from vmware_nsxlib.v3 import nsx_constants as nsxlib_consts
from vmware_nsxlib.v3 import router as nsxlib_router
from vmware_nsxlib.v3 import security
from vmware_nsxlib.v3 import utils as nsxlib_utils
@ -3207,6 +3208,9 @@ class NsxV3Plugin(agentschedulers_db.AZDhcpAgentSchedulerDbMixin,
(newaddr != orgaddr or
not new_enable_snat))
# Remove No-DNAT rules if GW was removed
remove_no_dnat_rules = (orgaddr and not newaddr)
# Revocate bgp announce for nonat subnets if tier0 router link is
# changed or enable_snat is updated from False to True
revocate_bgp_announce = (not org_enable_snat and org_tier0_uuid and
@ -3225,6 +3229,9 @@ class NsxV3Plugin(agentschedulers_db.AZDhcpAgentSchedulerDbMixin,
(newaddr != orgaddr or
not org_enable_snat))
# Add No-DNAT rules if GW was added
add_no_dnat_rules = (newaddr and not orgaddr)
# Bgp announce for nonat subnets if tier0 router link is changed or
# enable_snat is updated from True to False
bgp_announce = (not new_enable_snat and new_tier0_uuid and
@ -3236,11 +3243,18 @@ class NsxV3Plugin(agentschedulers_db.AZDhcpAgentSchedulerDbMixin,
advertise_route_nat_flag = True if new_enable_snat else False
advertise_route_connected_flag = True if not new_enable_snat else False
if add_no_dnat_rules or remove_no_dnat_rules or add_snat_rules:
subnets = self._find_router_subnets(context.elevated(),
router_id)
if revocate_bgp_announce:
# TODO(berlin): revocate bgp announce on org tier0 router
pass
if remove_snat_rules:
self.nsxlib.router.delete_gw_snat_rules(nsx_router_id, orgaddr)
if remove_no_dnat_rules:
for subnet in subnets:
self._del_subnet_no_dnat_rule(context, nsx_router_id, subnet)
if remove_router_link_port:
self.nsxlib.router.remove_router_link_port(
nsx_router_id, org_tier0_uuid)
@ -3260,11 +3274,13 @@ class NsxV3Plugin(agentschedulers_db.AZDhcpAgentSchedulerDbMixin,
# than the gw
gw_address_scope = self._get_network_address_scope(
context, router.gw_port.network_id)
subnets = self._find_router_subnets(context.elevated(),
router_id)
for subnet in subnets:
self._add_subnet_snat_rule(context, router_id, nsx_router_id,
subnet, gw_address_scope, newaddr)
if add_no_dnat_rules:
for subnet in subnets:
self._add_subnet_no_dnat_rule(context, nsx_router_id, subnet)
if bgp_announce:
# TODO(berlin): bgp announce on new tier0 router
pass
@ -3293,6 +3309,21 @@ class NsxV3Plugin(agentschedulers_db.AZDhcpAgentSchedulerDbMixin,
source_net=subnet['cidr'],
bypass_firewall=False)
def _add_subnet_no_dnat_rule(self, context, nsx_router_id, subnet):
# Add NO-DNAT rule to allow internal traffic between VMs, even if
# they have floating ips
self.nsxlib.logical_router.add_nat_rule(
nsx_router_id, "NO_DNAT", None,
dest_net=subnet['cidr'],
rule_priority=nsxlib_router.GW_NAT_PRI)
def _del_subnet_no_dnat_rule(self, context, nsx_router_id, subnet):
# Delete the previously created NO-DNAT rules
self.nsxlib.logical_router.delete_nat_rule_by_values(
nsx_router_id,
action="NO_DNAT",
match_destination_network=subnet['cidr'])
def _process_extra_attr_router_create(self, context, router_db, r):
for extra_attr in l3_attrs_db.get_attr_info().keys():
if (extra_attr in r and
@ -3851,7 +3882,7 @@ class NsxV3Plugin(agentschedulers_db.AZDhcpAgentSchedulerDbMixin,
nsx_rpc.handle_router_metadata_access(self, context, router_id,
interface=info)
# add the SNAT rule for this interface
# add the SNAT/NO_DNAT rules for this interface
if (router_db.enable_snat and gw_network_id and
router_db.gw_port.get('fixed_ips')):
gw_ip = router_db.gw_port['fixed_ips'][0]['ip_address']
@ -3859,6 +3890,8 @@ class NsxV3Plugin(agentschedulers_db.AZDhcpAgentSchedulerDbMixin,
context, gw_network_id)
self._add_subnet_snat_rule(context, router_id, nsx_router_id,
subnet, gw_address_scope, gw_ip)
if gw_network_id:
self._add_subnet_no_dnat_rule(context, nsx_router_id, subnet)
# update firewall rules
self.update_router_firewall(context, router_id)
except Exception:
@ -3931,13 +3964,15 @@ class NsxV3Plugin(agentschedulers_db.AZDhcpAgentSchedulerDbMixin,
else:
self.nsxlib.logical_router_port.delete_by_lswitch_id(
nsx_net_id)
# try to delete the SNAT rule of this subnet
# try to delete the SNAT/NO_DNAT rules of this subnet
if (router_db.gw_port and router_db.enable_snat and
router_db.gw_port.get('fixed_ips')):
gw_ip = router_db.gw_port['fixed_ips'][0]['ip_address']
self.nsxlib.router.delete_gw_snat_rule_by_source(
nsx_router_id, gw_ip, subnet['cidr'],
skip_not_found=True)
if router_db.gw_port:
self._del_subnet_no_dnat_rule(context, nsx_router_id, subnet)
except nsx_lib_exc.ResourceNotFound:
LOG.error("router port on router %(router_id)s for net "

View File

@ -1266,6 +1266,47 @@ class TestL3NatTestCase(L3NatTest,
gw_info = body['router']['external_gateway_info']
self.assertIsNone(gw_info)
def test_router_on_vlan_net(self):
providernet_args = {pnet.NETWORK_TYPE: 'vlan',
pnet.SEGMENTATION_ID: 10}
with mock.patch('vmware_nsxlib.v3.core_resources.NsxLibTransportZone.'
'get_transport_type', return_value='VLAN'):
result = self._create_network(fmt='json', name='badvlan_net',
admin_state_up=True,
providernet_args=providernet_args,
arg_list=(
pnet.NETWORK_TYPE,
pnet.SEGMENTATION_ID))
vlan_network = self.deserialize('json', result)
with self.router() as r1,\
self.subnet() as ext_subnet,\
self.subnet(cidr='11.0.0.0/24', network=vlan_network) as s1:
self._set_net_external(ext_subnet['subnet']['network_id'])
# adding a vlan interface with no GW should fail
self._router_interface_action(
'add', r1['router']['id'],
s1['subnet']['id'], None,
expected_code=400)
# adding GW
self._add_external_gateway_to_router(
r1['router']['id'],
ext_subnet['subnet']['network_id'])
# adding the vlan interface
self._router_interface_action(
'add', r1['router']['id'],
s1['subnet']['id'], None)
# adding a floating ip
with self.port(subnet=s1) as p:
fip_res = self._create_floatingip(
self.fmt,
ext_subnet['subnet']['network_id'],
subnet_id=ext_subnet['subnet']['id'],
port_id=p['port']['id'])
fip = self.deserialize(self.fmt, fip_res)
self.assertEqual(p['port']['id'],
fip['floatingip']['port_id'])
def test_create_router_gateway_fails(self):
self.skipTest('not supported')