From 30a8b2176f200886c58dd3f0457c6e234f3a99c5 Mon Sep 17 00:00:00 2001 From: Adit Sarfaty Date: Mon, 29 Jan 2018 11:48:25 +0200 Subject: [PATCH] NSX-v3: VPNaaS supports only No-SNAT routers Prevent the creation of a vpn service for a rotuer with SNAT enabled, and prevent updating the SNAT to enabled for a router with a vpn service. Change-Id: Ib6bfd9e019b2161245ba4951ef48e84314e0b923 --- vmware_nsx/plugins/nsx_v3/plugin.py | 8 +++++++ .../services/vpnaas/nsxv3/ipsec_driver.py | 20 ++++++++++++++++ .../services/vpnaas/nsxv3/ipsec_validator.py | 5 ++++ .../unit/services/vpnaas/test_nsxv3_vpnaas.py | 24 +++++++++++++++---- 4 files changed, 53 insertions(+), 4 deletions(-) diff --git a/vmware_nsx/plugins/nsx_v3/plugin.py b/vmware_nsx/plugins/nsx_v3/plugin.py index 40ee815a16..cae758d5c0 100644 --- a/vmware_nsx/plugins/nsx_v3/plugin.py +++ b/vmware_nsx/plugins/nsx_v3/plugin.py @@ -3444,6 +3444,14 @@ class NsxV3Plugin(agentschedulers_db.AZDhcpAgentSchedulerDbMixin, context.elevated(), router_id, gw_info['network_id'], fip['subnet_id']) + # VPNaaS need to be notified on router GW changes (there is currently + # no matching upstream registration for this) + if validators.is_attr_set(gw_info): + vpn_plugin = directory.get_plugin(plugin_const.VPN) + if vpn_plugin: + vpn_driver = vpn_plugin.drivers[vpn_plugin.default_provider] + vpn_driver.validate_router_gw_info(context, router_id, gw_info) + nsx_router_id = None routes_added = [] routes_removed = [] diff --git a/vmware_nsx/services/vpnaas/nsxv3/ipsec_driver.py b/vmware_nsx/services/vpnaas/nsxv3/ipsec_driver.py index a21a8a3eeb..6a5f07936a 100644 --- a/vmware_nsx/services/vpnaas/nsxv3/ipsec_driver.py +++ b/vmware_nsx/services/vpnaas/nsxv3/ipsec_driver.py @@ -14,6 +14,7 @@ # under the License. import netaddr +from oslo_config import cfg from oslo_log import log as logging from oslo_utils import excutils @@ -21,6 +22,7 @@ from neutron_lib.callbacks import events from neutron_lib.callbacks import registry from neutron_lib.callbacks import resources from neutron_lib import constants +from neutron_lib import exceptions as nexception from neutron_lib.plugins import directory from neutron_vpnaas.services.vpn import service_drivers @@ -37,6 +39,11 @@ LOG = logging.getLogger(__name__) IPSEC = 'ipsec' +class RouterWithSNAT(nexception.BadRequest): + message = _("Router %(router_id)s has a VPN service and cannot enable " + "SNAT") + + class NSXv3IPsecVpnDriver(service_drivers.VpnDriver): def __init__(self, service_plugin): @@ -354,6 +361,19 @@ class NSXv3IPsecVpnDriver(service_drivers.VpnDriver): if local_ep_id: self._nsx_vpn.local_endpoint.delete(local_ep_id) + def validate_router_gw_info(self, context, router_id, gw_info): + """Upon router gw update - verify no-snat""" + # ckeck if this router has a vpn service + filters = {'router_id': [router_id], + 'status': [constants.ACTIVE]} + services = self.vpn_plugin.get_vpnservices( + context.elevated(), filters=filters) + if services: + # do not allow enable-snat + if (gw_info and + gw_info.get('enable_snat', cfg.CONF.enable_snat_by_default)): + raise RouterWithSNAT(router_id=router_id) + def _get_session_rules(self, context, connection, vpnservice): # TODO(asarfaty): support vpn-endpoint-groups too peer_cidrs = connection['peer_cidrs'] diff --git a/vmware_nsx/services/vpnaas/nsxv3/ipsec_validator.py b/vmware_nsx/services/vpnaas/nsxv3/ipsec_validator.py index 7d0640d308..f1d5cd319d 100644 --- a/vmware_nsx/services/vpnaas/nsxv3/ipsec_validator.py +++ b/vmware_nsx/services/vpnaas/nsxv3/ipsec_validator.py @@ -323,6 +323,11 @@ class IPsecV3Validator(vpn_validator.VpnReferenceValidator): "with ACTIVE_STANDBY HA mode") raise nsx_exc.NsxVpnValidationError(details=msg) + # Verify that this is a no-snat router + if router_db.enable_snat: + msg = _("VPN is supported only for routers with disabled SNAT") + raise nsx_exc.NsxVpnValidationError(details=msg) + def validate_vpnservice(self, context, vpnservice): """Called upon create/update of a service""" diff --git a/vmware_nsx/tests/unit/services/vpnaas/test_nsxv3_vpnaas.py b/vmware_nsx/tests/unit/services/vpnaas/test_nsxv3_vpnaas.py index bc3ab7a6be..2fa26dec1c 100644 --- a/vmware_nsx/tests/unit/services/vpnaas/test_nsxv3_vpnaas.py +++ b/vmware_nsx/tests/unit/services/vpnaas/test_nsxv3_vpnaas.py @@ -14,6 +14,7 @@ # under the License. import mock +from neutron.db.models import l3 as l3_models from neutron_lib import context as n_ctx from neutron_vpnaas.tests import base @@ -156,16 +157,31 @@ class TestDriverValidation(base.BaseTestCase): self.validator.validate_ipsec_policy(self.context, policy_info) def test_vpn_service_validation_router(self): - router = {'high_availability_mode': 'ACITVE_ACTIVE'} + db_router = l3_models.Router() + nsx_router = {'high_availability_mode': 'ACITVE_ACTIVE'} + db_router.enable_snat = False with mock.patch.object(self.validator.nsxlib.logical_router, 'get', - return_value=router): + return_value=nsx_router): self.assertRaises(nsx_exc.NsxVpnValidationError, self.validator.validate_vpnservice, self.context, self.vpn_service) - router = {'high_availability_mode': 'ACTIVE_STANDBY'} + nsx_router = {'high_availability_mode': 'ACTIVE_STANDBY'} + db_router.enable_snat = True with mock.patch.object(self.validator.nsxlib.logical_router, 'get', - return_value=router): + return_value=nsx_router),\ + mock.patch.object(self.validator._core_plugin, '_get_router', + return_value=db_router): + self.assertRaises(nsx_exc.NsxVpnValidationError, + self.validator.validate_vpnservice, + self.context, self.vpn_service) + + nsx_router = {'high_availability_mode': 'ACTIVE_STANDBY'} + db_router.enable_snat = False + with mock.patch.object(self.validator.nsxlib.logical_router, 'get', + return_value=nsx_router),\ + mock.patch.object(self.validator._core_plugin, '_get_router', + return_value=db_router): self.validator.validate_vpnservice(self.context, self.vpn_service) def _test_conn_validation(self, conn_params=None, success=True,