diff --git a/vmware_nsx/plugins/common/plugin.py b/vmware_nsx/plugins/common/plugin.py index 9d3422dd2b..81a9c072ac 100644 --- a/vmware_nsx/plugins/common/plugin.py +++ b/vmware_nsx/plugins/common/plugin.py @@ -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): diff --git a/vmware_nsx/plugins/nsx_v3/plugin.py b/vmware_nsx/plugins/nsx_v3/plugin.py index eddeccbeb1..71e367047c 100644 --- a/vmware_nsx/plugins/nsx_v3/plugin.py +++ b/vmware_nsx/plugins/nsx_v3/plugin.py @@ -118,6 +118,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 @@ -3230,6 +3231,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 @@ -3248,6 +3252,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 @@ -3259,11 +3266,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) @@ -3283,11 +3297,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 @@ -3316,6 +3332,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 @@ -3881,7 +3912,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'] @@ -3889,6 +3920,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: @@ -3961,13 +3994,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 " diff --git a/vmware_nsx/tests/unit/nsx_v3/test_plugin.py b/vmware_nsx/tests/unit/nsx_v3/test_plugin.py index 1e667f8f7b..9b33e3aadd 100644 --- a/vmware_nsx/tests/unit/nsx_v3/test_plugin.py +++ b/vmware_nsx/tests/unit/nsx_v3/test_plugin.py @@ -1319,6 +1319,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')