diff --git a/api/src/main/java/com/cloud/network/Site2SiteCustomerGateway.java b/api/src/main/java/com/cloud/network/Site2SiteCustomerGateway.java index f9a88bdd845..de83fddf813 100644 --- a/api/src/main/java/com/cloud/network/Site2SiteCustomerGateway.java +++ b/api/src/main/java/com/cloud/network/Site2SiteCustomerGateway.java @@ -43,5 +43,9 @@ public interface Site2SiteCustomerGateway extends ControlledEntity, Identity, In public Date getRemoved(); + public Boolean getSplitConnections(); + + public String getIkeVersion(); + String getName(); } diff --git a/api/src/main/java/org/apache/cloudstack/api/ApiConstants.java b/api/src/main/java/org/apache/cloudstack/api/ApiConstants.java index 93f6e5ebd49..605a6c006eb 100644 --- a/api/src/main/java/org/apache/cloudstack/api/ApiConstants.java +++ b/api/src/main/java/org/apache/cloudstack/api/ApiConstants.java @@ -630,12 +630,14 @@ public class ApiConstants { public static final String GUEST_IP = "guestip"; public static final String REMOVED = "removed"; public static final String COMPLETED = "completed"; + public static final String IKE_VERSION = "ikeversion"; public static final String IKE_POLICY = "ikepolicy"; public static final String ESP_POLICY = "esppolicy"; public static final String IKE_LIFETIME = "ikelifetime"; public static final String ESP_LIFETIME = "esplifetime"; public static final String DPD = "dpd"; public static final String FORCE_ENCAP = "forceencap"; + public static final String SPLIT_CONNECTIONS = "splitconnections"; public static final String FOR_VPC = "forvpc"; public static final String SHRINK_OK = "shrinkok"; public static final String NICIRA_NVP_DEVICE_ID = "nvpdeviceid"; diff --git a/api/src/main/java/org/apache/cloudstack/api/command/user/vpn/CreateVpnCustomerGatewayCmd.java b/api/src/main/java/org/apache/cloudstack/api/command/user/vpn/CreateVpnCustomerGatewayCmd.java index 6f591756d2e..df7295b9dbf 100644 --- a/api/src/main/java/org/apache/cloudstack/api/command/user/vpn/CreateVpnCustomerGatewayCmd.java +++ b/api/src/main/java/org/apache/cloudstack/api/command/user/vpn/CreateVpnCustomerGatewayCmd.java @@ -92,6 +92,13 @@ public class CreateVpnCustomerGatewayCmd extends BaseAsyncCmd { description = "create site-to-site VPN customer gateway for the project", since = "4.6") private Long projectId; + @Parameter(name = ApiConstants.SPLIT_CONNECTIONS, type = CommandType.BOOLEAN, required = false, description = "For IKEv2, whether to split multiple right subnet cidrs into multiple connection statements.") + private Boolean splitConnections; + + @Parameter(name = ApiConstants.IKE_VERSION, type = CommandType.STRING, required = false, description = "Which IKE Version to use, one of ike (autoselect), ikev1, or ikev2. " + + "Connections marked with 'ike' will use 'ikev2' when initiating, but accept any protocol version when responding. Defaults to ike") + private String ikeVersion; + ///////////////////////////////////////////////////// /////////////////// Accessors /////////////////////// ///////////////////////////////////////////////////// @@ -146,6 +153,14 @@ public class CreateVpnCustomerGatewayCmd extends BaseAsyncCmd { return projectId; } + public Boolean getSplitConnections() { + return splitConnections; + } + + public String getIkeVersion() { + return ikeVersion; + } + ///////////////////////////////////////////////////// /////////////// API Implementation/////////////////// ///////////////////////////////////////////////////// diff --git a/api/src/main/java/org/apache/cloudstack/api/command/user/vpn/UpdateVpnCustomerGatewayCmd.java b/api/src/main/java/org/apache/cloudstack/api/command/user/vpn/UpdateVpnCustomerGatewayCmd.java index d7bf5c4b557..f2fcc9170b7 100644 --- a/api/src/main/java/org/apache/cloudstack/api/command/user/vpn/UpdateVpnCustomerGatewayCmd.java +++ b/api/src/main/java/org/apache/cloudstack/api/command/user/vpn/UpdateVpnCustomerGatewayCmd.java @@ -94,6 +94,13 @@ public class UpdateVpnCustomerGatewayCmd extends BaseAsyncCmd { + "gateway associated with the account for the specified domain.") private Long domainId; + @Parameter(name = ApiConstants.SPLIT_CONNECTIONS, type = CommandType.BOOLEAN, required = false, description = "For IKEv2, whether to split multiple right subnet cidrs into multiple connection statements.") + private Boolean splitConnections; + + @Parameter(name = ApiConstants.IKE_VERSION, type = CommandType.STRING, required = false, description = "Which IKE Version to use, one of ike (autoselect), ikev1, or ikev2." + + "Connections marked with 'ike' will use 'ikev2' when initiating, but accept any protocol version when responding. Defaults to ike") + private String ikeVersion; + ///////////////////////////////////////////////////// /////////////////// Accessors /////////////////////// ///////////////////////////////////////////////////// @@ -140,6 +147,14 @@ public class UpdateVpnCustomerGatewayCmd extends BaseAsyncCmd { public Boolean getEncap() { return encap; } + public boolean getSplitConnections() { + return null == splitConnections ? false : splitConnections; + } + + public String getIkeVersion() { + return ikeVersion; + } + ///////////////////////////////////////////////////// /////////////// API Implementation/////////////////// ///////////////////////////////////////////////////// diff --git a/api/src/main/java/org/apache/cloudstack/api/response/Site2SiteCustomerGatewayResponse.java b/api/src/main/java/org/apache/cloudstack/api/response/Site2SiteCustomerGatewayResponse.java index 8128405cdae..88e0e1600e5 100644 --- a/api/src/main/java/org/apache/cloudstack/api/response/Site2SiteCustomerGatewayResponse.java +++ b/api/src/main/java/org/apache/cloudstack/api/response/Site2SiteCustomerGatewayResponse.java @@ -102,6 +102,14 @@ public class Site2SiteCustomerGatewayResponse extends BaseResponse implements Co @Param(description = "the date and time the host was removed") private Date removed; + @SerializedName(ApiConstants.SPLIT_CONNECTIONS) + @Param(description = "For IKEv2, whether to split multiple right subnet cidrs into multiple connection statements.") + private Boolean splitConnections; + + @SerializedName(ApiConstants.IKE_VERSION) + @Param(description = "Which IKE Version to use, one of ike (autoselect), ikev1, or ikev2. Defaults to ike") + private String ikeVersion; + public void setId(String id) { this.id = id; } @@ -148,6 +156,14 @@ public class Site2SiteCustomerGatewayResponse extends BaseResponse implements Co public void setEncap(Boolean encap) { this.encap = encap; } + public void setSplitConnections(Boolean splitConnections) { + this.splitConnections = splitConnections; + } + + public void setIkeVersion(String ikeVersion) { + this.ikeVersion = ikeVersion; + } + public void setRemoved(Date removed) { this.removed = removed; } diff --git a/api/src/main/java/org/apache/cloudstack/api/response/Site2SiteVpnConnectionResponse.java b/api/src/main/java/org/apache/cloudstack/api/response/Site2SiteVpnConnectionResponse.java index edaa1b262a7..1f7509239d1 100644 --- a/api/src/main/java/org/apache/cloudstack/api/response/Site2SiteVpnConnectionResponse.java +++ b/api/src/main/java/org/apache/cloudstack/api/response/Site2SiteVpnConnectionResponse.java @@ -132,6 +132,14 @@ public class Site2SiteVpnConnectionResponse extends BaseResponse implements Cont @Param(description = "is connection for display to the regular user", since = "4.4", authorized = {RoleType.Admin}) private Boolean forDisplay; + @SerializedName(ApiConstants.SPLIT_CONNECTIONS) + @Param(description = "Split multiple remote networks into multiple phase 2 SAs. Often used with Cisco some products.") + private Boolean splitConnections; + + @SerializedName(ApiConstants.IKE_VERSION) + @Param(description = "Which IKE Version to use, one of ike (autoselect), ikev1, or ikev2. Defaults to ike") + private String ikeVersion; + public void setId(String id) { this.id = id; } @@ -200,6 +208,14 @@ public class Site2SiteVpnConnectionResponse extends BaseResponse implements Cont this.removed = removed; } + public void setSplitConnections(Boolean splitConnections) { + this.splitConnections = splitConnections; + } + + public void setIkeVersion(String ikeVersion) { + this.ikeVersion = ikeVersion; + } + @Override public void setAccountName(String accountName) { this.accountName = accountName; diff --git a/core/src/main/java/com/cloud/agent/api/routing/Site2SiteVpnCfgCommand.java b/core/src/main/java/com/cloud/agent/api/routing/Site2SiteVpnCfgCommand.java index 685cf4049c7..f679b75d155 100644 --- a/core/src/main/java/com/cloud/agent/api/routing/Site2SiteVpnCfgCommand.java +++ b/core/src/main/java/com/cloud/agent/api/routing/Site2SiteVpnCfgCommand.java @@ -35,6 +35,8 @@ public class Site2SiteVpnCfgCommand extends NetworkElementCommand { private boolean dpd; private boolean passive; private boolean encap; + private boolean splitConnections; + private String ikeVersion; @Override public boolean executeInSequence() { @@ -46,7 +48,8 @@ public class Site2SiteVpnCfgCommand extends NetworkElementCommand { } public Site2SiteVpnCfgCommand(boolean create, String localPublicIp, String localPublicGateway, String localGuestCidr, String peerGatewayIp, String peerGuestCidrList, - String ikePolicy, String espPolicy, String ipsecPsk, Long ikeLifetime, Long espLifetime, Boolean dpd, boolean passive, boolean encap) { + String ikePolicy, String espPolicy, String ipsecPsk, Long ikeLifetime, Long espLifetime, Boolean dpd, boolean passive, boolean encap, + boolean splitConnections, String ikeVersion) { this.create = create; this.setLocalPublicIp(localPublicIp); this.setLocalPublicGateway(localPublicGateway); @@ -61,6 +64,8 @@ public class Site2SiteVpnCfgCommand extends NetworkElementCommand { this.dpd = dpd; this.passive = passive; this.encap = encap; + this.splitConnections = splitConnections; + this.ikeVersion = ikeVersion; } public boolean isCreate() { @@ -174,4 +179,20 @@ public class Site2SiteVpnCfgCommand extends NetworkElementCommand { public void setPassive(boolean passive) { this.passive = passive; } + + public boolean getSplitConnections() { + return splitConnections; + } + + public void setSplitConnections(boolean splitConnections) { + this.splitConnections = splitConnections; + } + + public String getIkeVersion() { + return ikeVersion; + } + + public void setIkeVersion(String ikeVersion) { + this.ikeVersion = ikeVersion; + } } diff --git a/core/src/main/java/com/cloud/agent/resource/virtualnetwork/facade/Site2SiteVpnConfigItem.java b/core/src/main/java/com/cloud/agent/resource/virtualnetwork/facade/Site2SiteVpnConfigItem.java index 5bb466c5935..badd0d376d1 100644 --- a/core/src/main/java/com/cloud/agent/resource/virtualnetwork/facade/Site2SiteVpnConfigItem.java +++ b/core/src/main/java/com/cloud/agent/resource/virtualnetwork/facade/Site2SiteVpnConfigItem.java @@ -36,7 +36,7 @@ public class Site2SiteVpnConfigItem extends AbstractConfigItemFacade { final Site2SiteVpn site2siteVpn = new Site2SiteVpn(command.getLocalPublicIp(), command.getLocalGuestCidr(), command.getLocalPublicGateway(), command.getPeerGatewayIp(), command.getPeerGuestCidrList(), command.getEspPolicy(), command.getIkePolicy(), command.getIpsecPsk(), command.getIkeLifetime(), command.getEspLifetime(), command.isCreate(), command.getDpd(), - command.isPassive(), command.getEncap()); + command.isPassive(), command.getEncap(), command.getSplitConnections(), command.getIkeVersion()); return generateConfigItems(site2siteVpn); } diff --git a/core/src/main/java/com/cloud/agent/resource/virtualnetwork/model/Site2SiteVpn.java b/core/src/main/java/com/cloud/agent/resource/virtualnetwork/model/Site2SiteVpn.java index 232e99f099a..9057bf4f293 100644 --- a/core/src/main/java/com/cloud/agent/resource/virtualnetwork/model/Site2SiteVpn.java +++ b/core/src/main/java/com/cloud/agent/resource/virtualnetwork/model/Site2SiteVpn.java @@ -21,9 +21,9 @@ package com.cloud.agent.resource.virtualnetwork.model; public class Site2SiteVpn extends ConfigBase { - private String localPublicIp, localGuestCidr, localPublicGateway, peerGatewayIp, peerGuestCidrList, espPolicy, ikePolicy, ipsecPsk; + private String localPublicIp, localGuestCidr, localPublicGateway, peerGatewayIp, peerGuestCidrList, espPolicy, ikePolicy, ipsecPsk, ikeVersion; private Long ikeLifetime, espLifetime; - private boolean create, dpd, passive, encap; + private boolean create, dpd, passive, encap, splitConnections; public Site2SiteVpn() { super(ConfigBase.SITE2SITEVPN); @@ -31,7 +31,7 @@ public class Site2SiteVpn extends ConfigBase { public Site2SiteVpn(String localPublicIp, String localGuestCidr, String localPublicGateway, String peerGatewayIp, String peerGuestCidrList, String espPolicy, String ikePolicy, - String ipsecPsk, Long ikeLifetime, Long espLifetime, boolean create, Boolean dpd, boolean passive, boolean encap) { + String ipsecPsk, Long ikeLifetime, Long espLifetime, boolean create, Boolean dpd, boolean passive, boolean encap, boolean splitConnections, String ikeVersion) { super(ConfigBase.SITE2SITEVPN); this.localPublicIp = localPublicIp; this.localGuestCidr = localGuestCidr; @@ -47,6 +47,8 @@ public class Site2SiteVpn extends ConfigBase { this.dpd = dpd; this.passive = passive; this.encap = encap; + this.splitConnections = splitConnections; + this.ikeVersion = ikeVersion; } public String getLocalPublicIp() { @@ -161,4 +163,20 @@ public class Site2SiteVpn extends ConfigBase { this.encap = encap; } + public boolean getSplitConnections() { + return splitConnections; + } + + public void setSplitConnections(boolean splitConnections) { + this.splitConnections = splitConnections; + } + + public String getIkeVersion() { + return ikeVersion; + } + + public void setIkeVersion(String ikeVersion) { + this.ikeVersion = ikeVersion; + } + } diff --git a/core/src/test/java/com/cloud/agent/resource/virtualnetwork/VirtualRoutingResourceTest.java b/core/src/test/java/com/cloud/agent/resource/virtualnetwork/VirtualRoutingResourceTest.java index 200f266b925..6eb30aeeed9 100644 --- a/core/src/test/java/com/cloud/agent/resource/virtualnetwork/VirtualRoutingResourceTest.java +++ b/core/src/test/java/com/cloud/agent/resource/virtualnetwork/VirtualRoutingResourceTest.java @@ -495,17 +495,17 @@ public class VirtualRoutingResourceTest implements VirtualRouterDeployer { public void testSite2SiteVpnCfgCommand() { _count = 0; - Site2SiteVpnCfgCommand cmd = new Site2SiteVpnCfgCommand(true, "64.10.1.10", "64.10.1.1", "192.168.1.1/16", "124.10.1.10", "192.168.100.1/24", "3des-sha1,aes128-sha1;modp1536", "3des-sha1,aes128-md5", "psk", Long.valueOf(1800), Long.valueOf(1800), true, false, false); + Site2SiteVpnCfgCommand cmd = new Site2SiteVpnCfgCommand(true, "64.10.1.10", "64.10.1.1", "192.168.1.1/16", "124.10.1.10", "192.168.100.1/24", "3des-sha1,aes128-sha1;modp1536", "3des-sha1,aes128-md5", "psk", Long.valueOf(1800), Long.valueOf(1800), true, false, false, false, "ike"); cmd.setAccessDetail(NetworkElementCommand.ROUTER_NAME, ROUTERNAME); Answer answer = _resource.executeRequest(cmd); assertTrue(answer.getResult()); - cmd = new Site2SiteVpnCfgCommand(true, "64.10.1.10", "64.10.1.1", "192.168.1.1/16", "124.10.1.10", "192.168.100.1/24", "3des-sha1,aes128-sha1;modp1536", "3des-sha1,aes128-md5", "psk", Long.valueOf(1800), Long.valueOf(1800), false, true, false); + cmd = new Site2SiteVpnCfgCommand(true, "64.10.1.10", "64.10.1.1", "192.168.1.1/16", "124.10.1.10", "192.168.100.1/24", "3des-sha1,aes128-sha1;modp1536", "3des-sha1,aes128-md5", "psk", Long.valueOf(1800), Long.valueOf(1800), false, true, false, false, "ike"); cmd.setAccessDetail(NetworkElementCommand.ROUTER_NAME, ROUTERNAME); answer = _resource.executeRequest(cmd); assertTrue(answer.getResult()); - cmd = new Site2SiteVpnCfgCommand(false, "64.10.1.10", "64.10.1.1", "192.168.1.1/16", "124.10.1.10", "192.168.100.1/24", "3des-sha1,aes128-sha1;modp1536", "3des-sha1,aes128-md5", "psk", Long.valueOf(1800), Long.valueOf(1800), false, true, false); + cmd = new Site2SiteVpnCfgCommand(false, "64.10.1.10", "64.10.1.1", "192.168.1.1/16", "124.10.1.10", "192.168.100.1/24", "3des-sha1,aes128-sha1;modp1536", "3des-sha1,aes128-md5", "psk", Long.valueOf(1800), Long.valueOf(1800), false, true, false, false, "ike"); cmd.setAccessDetail(NetworkElementCommand.ROUTER_NAME, ROUTERNAME); answer = _resource.executeRequest(cmd); assertTrue(answer.getResult()); diff --git a/engine/schema/src/main/java/com/cloud/network/dao/Site2SiteCustomerGatewayVO.java b/engine/schema/src/main/java/com/cloud/network/dao/Site2SiteCustomerGatewayVO.java index f1d3ef32712..c8241518d63 100644 --- a/engine/schema/src/main/java/com/cloud/network/dao/Site2SiteCustomerGatewayVO.java +++ b/engine/schema/src/main/java/com/cloud/network/dao/Site2SiteCustomerGatewayVO.java @@ -79,6 +79,12 @@ public class Site2SiteCustomerGatewayVO implements Site2SiteCustomerGateway { @Column(name = "account_id") private Long accountId; + @Column(name = "split_connections") + private boolean splitConnections; + + @Column(name = "ike_version") + private String ikeVersion; + @Column(name = GenericDao.REMOVED_COLUMN) private Date removed; @@ -86,7 +92,7 @@ public class Site2SiteCustomerGatewayVO implements Site2SiteCustomerGateway { } public Site2SiteCustomerGatewayVO(String name, long accountId, long domainId, String gatewayIp, String guestCidrList, String ipsecPsk, String ikePolicy, - String espPolicy, long ikeLifetime, long espLifetime, boolean dpd, boolean encap) { + String espPolicy, long ikeLifetime, long espLifetime, boolean dpd, boolean encap, boolean splitConnections, String ikeVersion) { this.name = name; this.gatewayIp = gatewayIp; this.guestCidrList = guestCidrList; @@ -100,6 +106,8 @@ public class Site2SiteCustomerGatewayVO implements Site2SiteCustomerGateway { uuid = UUID.randomUUID().toString(); this.accountId = accountId; this.domainId = domainId; + this.splitConnections = splitConnections; + this.ikeVersion = ikeVersion; } @Override @@ -221,6 +229,24 @@ public class Site2SiteCustomerGatewayVO implements Site2SiteCustomerGateway { return accountId; } + @Override + public Boolean getSplitConnections() { + return splitConnections; + } + + public void setSplitConnections(Boolean splitConnections) { + this.splitConnections = splitConnections; + } + + @Override + public String getIkeVersion() { + return ikeVersion; + } + + public void setIkeVersion(String ikeVersion) { + this.ikeVersion = ikeVersion; + } + @Override public Class getEntityType() { return Site2SiteCustomerGateway.class; diff --git a/engine/schema/src/main/resources/META-INF/db/schema-41500to41510.sql b/engine/schema/src/main/resources/META-INF/db/schema-41500to41510.sql index 859bbd00776..07f832bf77a 100644 --- a/engine/schema/src/main/resources/META-INF/db/schema-41500to41510.sql +++ b/engine/schema/src/main/resources/META-INF/db/schema-41500to41510.sql @@ -56,7 +56,8 @@ INSERT IGNORE INTO `cloud`.`guest_os_hypervisor` (uuid,hypervisor_type, hypervis INSERT INTO `cloud`.`guest_os` (id, uuid, category_id, display_name, created) VALUES (335, UUID(), 10, 'Ubuntu 20.04 LTS', now()); INSERT INTO `cloud`.`guest_os_hypervisor` (uuid,hypervisor_type, hypervisor_version, guest_os_name, guest_os_id, created, is_user_defined) VALUES (UUID(),'Xenserver', '8.2.0', 'Ubuntu Focal Fossa 20.04', 330, now(), 0); -------------------------------------------------------------------------------------------------------------- +ALTER TABLE `cloud`.`s2s_customer_gateway` ADD COLUMN `ike_version` varchar(5) NOT NULL DEFAULT 'ike' COMMENT 'one of ike, ikev1, ikev2'; +ALTER TABLE `cloud`.`s2s_customer_gateway` ADD COLUMN `split_connections` int(1) NOT NULL DEFAULT 0; -- Add support for VMware 7.0 INSERT IGNORE INTO `cloud`.`hypervisor_capabilities` (uuid, hypervisor_type, hypervisor_version, max_guests_limit, security_group_enabled, max_data_volumes_limit, max_hosts_per_cluster, storage_motion_supported, vm_snapshot_enabled) values (UUID(), 'VMware', '7.0', 1024, 0, 59, 64, 1, 1); @@ -131,4 +132,3 @@ INSERT INTO `cloud`.`guest_os_hypervisor` (uuid,hypervisor_type, hypervisor_vers -- Add support for windows2019srvNext_64Guest from VMware 7.0.1.0 INSERT INTO `cloud`.`guest_os_hypervisor` (uuid,hypervisor_type, hypervisor_version, guest_os_name, guest_os_id, created, is_user_defined) VALUES (UUID(),'VMware', '7.0.1.0', 'windows2019srvNext_64Guest', 276, now(), 0); - diff --git a/server/src/main/java/com/cloud/api/ApiResponseHelper.java b/server/src/main/java/com/cloud/api/ApiResponseHelper.java index ad8672bbd08..9510e28b7e2 100644 --- a/server/src/main/java/com/cloud/api/ApiResponseHelper.java +++ b/server/src/main/java/com/cloud/api/ApiResponseHelper.java @@ -3257,6 +3257,8 @@ public class ApiResponseHelper implements ResponseGenerator { response.setDpd(result.getDpd()); response.setEncap(result.getEncap()); response.setRemoved(result.getRemoved()); + response.setIkeVersion(result.getIkeVersion()); + response.setSplitConnections(result.getSplitConnections()); response.setObjectName("vpncustomergateway"); populateAccount(response, result.getAccountId()); @@ -3296,6 +3298,8 @@ public class ApiResponseHelper implements ResponseGenerator { response.setEspLifetime(customerGateway.getEspLifetime()); response.setDpd(customerGateway.getDpd()); response.setEncap(customerGateway.getEncap()); + response.setIkeVersion(customerGateway.getIkeVersion()); + response.setSplitConnections(customerGateway.getSplitConnections()); } } diff --git a/server/src/main/java/com/cloud/network/router/CommandSetupHelper.java b/server/src/main/java/com/cloud/network/router/CommandSetupHelper.java index e344b462b50..e73a83393f4 100644 --- a/server/src/main/java/com/cloud/network/router/CommandSetupHelper.java +++ b/server/src/main/java/com/cloud/network/router/CommandSetupHelper.java @@ -944,9 +944,11 @@ public class CommandSetupHelper { final Long espLifetime = gw.getEspLifetime(); final Boolean dpd = gw.getDpd(); final Boolean encap = gw.getEncap(); + final Boolean splitConnections = gw.getSplitConnections(); + final String ikeVersion = gw.getIkeVersion(); final Site2SiteVpnCfgCommand cmd = new Site2SiteVpnCfgCommand(isCreate, localPublicIp, localPublicGateway, localGuestCidr, peerGatewayIp, peerGuestCidrList, ikePolicy, - espPolicy, ipsecPsk, ikeLifetime, espLifetime, dpd, conn.isPassive(), encap); + espPolicy, ipsecPsk, ikeLifetime, espLifetime, dpd, conn.isPassive(), encap, splitConnections, ikeVersion); cmd.setAccessDetail(NetworkElementCommand.ROUTER_IP, _routerControlHelper.getRouterControlIp(router.getId())); cmd.setAccessDetail(NetworkElementCommand.ROUTER_IP, _routerControlHelper.getRouterControlIp(router.getId())); cmd.setAccessDetail(NetworkElementCommand.ROUTER_NAME, router.getInstanceName()); diff --git a/server/src/main/java/com/cloud/network/vpn/Site2SiteVpnManagerImpl.java b/server/src/main/java/com/cloud/network/vpn/Site2SiteVpnManagerImpl.java index 7fd3473a85f..63fcacc6ccd 100644 --- a/server/src/main/java/com/cloud/network/vpn/Site2SiteVpnManagerImpl.java +++ b/server/src/main/java/com/cloud/network/vpn/Site2SiteVpnManagerImpl.java @@ -45,6 +45,7 @@ import com.cloud.event.ActionEvent; import com.cloud.event.EventTypes; import com.cloud.exception.InvalidParameterValueException; import com.cloud.exception.NetworkRuleConflictException; +import com.cloud.exception.PermissionDeniedException; import com.cloud.exception.ResourceUnavailableException; import com.cloud.network.Site2SiteCustomerGateway; import com.cloud.network.Site2SiteVpnConnection; @@ -229,10 +230,20 @@ public class Site2SiteVpnManagerImpl extends ManagerBase implements Site2SiteVpn throw new InvalidParameterValueException("The customer gateway with name " + name + " already existed!"); } + Boolean splitConnections = cmd.getSplitConnections(); + if (splitConnections == null) { + splitConnections = false; + } + + String ikeVersion = cmd.getIkeVersion(); + if (ikeVersion == null) { + ikeVersion = "ike"; + } + checkCustomerGatewayCidrList(peerCidrList); Site2SiteCustomerGatewayVO gw = - new Site2SiteCustomerGatewayVO(name, accountId, owner.getDomainId(), gatewayIp, peerCidrList, ipsecPsk, ikePolicy, espPolicy, ikeLifetime, espLifetime, dpd, encap); + new Site2SiteCustomerGatewayVO(name, accountId, owner.getDomainId(), gatewayIp, peerCidrList, ipsecPsk, ikePolicy, espPolicy, ikeLifetime, espLifetime, dpd, encap, splitConnections, ikeVersion); _customerGatewayDao.persist(gw); return gw; } @@ -419,14 +430,6 @@ public class Site2SiteVpnManagerImpl extends ManagerBase implements Site2SiteVpn } _accountMgr.checkAccess(caller, null, false, gw); - List conns = _vpnConnectionDao.listByCustomerGatewayId(id); - if (conns != null) { - for (Site2SiteVpnConnection conn : conns) { - if (conn.getState() != State.Error) { - throw new InvalidParameterValueException("Unable to update customer gateway with connections in non-Error state!"); - } - } - } String name = cmd.getName(); String gatewayIp = cmd.getGatewayIp(); @@ -476,6 +479,10 @@ public class Site2SiteVpnManagerImpl extends ManagerBase implements Site2SiteVpn encap = false; } + Boolean splitConnections = cmd.getSplitConnections(); + + String ikeVersion = cmd.getIkeVersion(); + checkCustomerGatewayCidrList(guestCidrList); long accountId = gw.getAccountId(); @@ -494,10 +501,47 @@ public class Site2SiteVpnManagerImpl extends ManagerBase implements Site2SiteVpn gw.setEspLifetime(espLifetime); gw.setDpd(dpd); gw.setEncap(encap); + gw.setSplitConnections(splitConnections); + if (ikeVersion != null) { + gw.setIkeVersion(ikeVersion); + } _customerGatewayDao.persist(gw); + + setupVpnConnection(caller, id); + return gw; } + private void setupVpnConnection(Account caller, Long vpnCustomerGwIp) { + List conns = _vpnConnectionDao.listByCustomerGatewayId(vpnCustomerGwIp); + if (conns != null) { + for (Site2SiteVpnConnection conn : conns) { + try { + _accountMgr.checkAccess(caller, null, false, conn); + } catch (PermissionDeniedException e) { + // Just don't restart this connection, as the user has no rights to it + // Maybe should issue a notification to the system? + s_logger.info("Site2SiteVpnManager:updateCustomerGateway() Not resetting VPN connection " + conn.getId() + " as user lacks permission"); + continue; + } + + if (conn.getState() == State.Pending) { + // Vpn connection cannot be reset when the state is Pending + continue; + } + try { + if (conn.getState() == State.Connected || conn.getState() == State.Error) { + stopVpnConnection(conn.getId()); + } + startVpnConnection(conn.getId()); + } catch (ResourceUnavailableException e) { + // Should never get here, as we are looping on the actual connections, but we must handle it regardless + s_logger.warn("Failed to update VPN connection"); + } + } + } + } + @Override @ActionEvent(eventType = EventTypes.EVENT_S2S_VPN_CONNECTION_DELETE, eventDescription = "deleting s2s vpn connection", create = true) public boolean deleteVpnConnection(DeleteVpnConnectionCmd cmd) throws ResourceUnavailableException { diff --git a/server/src/test/java/com/cloud/network/router/VirtualNetworkApplianceManagerImplTest.java b/server/src/test/java/com/cloud/network/router/VirtualNetworkApplianceManagerImplTest.java index 0ddbd845a8c..aed4769ab96 100644 --- a/server/src/test/java/com/cloud/network/router/VirtualNetworkApplianceManagerImplTest.java +++ b/server/src/test/java/com/cloud/network/router/VirtualNetworkApplianceManagerImplTest.java @@ -278,7 +278,7 @@ public class VirtualNetworkApplianceManagerImplTest { conns.add(conn); conns.add(conn1); - Site2SiteCustomerGatewayVO gw = new Site2SiteCustomerGatewayVO("Testing gateway", 1L, 1L, "192.168.50.15", "Guest List", "ipsecPsk", "ikePolicy", "espPolicy", 1L, 1L, true, true); + Site2SiteCustomerGatewayVO gw = new Site2SiteCustomerGatewayVO("Testing gateway", 1L, 1L, "192.168.50.15", "Guest List", "ipsecPsk", "ikePolicy", "espPolicy", 1L, 1L, true, true, false, "ike"); HostVO hostVo = new HostVO(1L, "Testing host", Host.Type.Routing, "192.168.50.15", "privateNetmask", "privateMacAddress", "publicIpAddress", "publicNetmask", "publicMacAddress", "storageIpAddress", "storageNetmask", "storageMacAddress", "deuxStorageIpAddress", "duxStorageNetmask", "deuxStorageMacAddress", "guid", Status.Up, "version", "iqn", new Date() , 1L, 1L, 1L, 1L, "parent", 20L, Storage.StoragePoolType.Gluster); hostVo.setManagementServerId(ManagementServerNode.getManagementServerId()); diff --git a/systemvm/debian/opt/cloud/bin/configure.py b/systemvm/debian/opt/cloud/bin/configure.py index be67f403c8b..d0a83abd855 100755 --- a/systemvm/debian/opt/cloud/bin/configure.py +++ b/systemvm/debian/opt/cloud/bin/configure.py @@ -556,10 +556,18 @@ class CsSite2SiteVpn(CsDataBag): vpnsecretsfile = "%s/ipsec.vpn-%s.secrets" % (self.VPNCONFDIR, rightpeer) ikepolicy = obj['ike_policy'].replace(';', '-') esppolicy = obj['esp_policy'].replace(';', '-') + splitconnections = obj['split_connections'] if 'split_connections' in obj else False + ikeversion = obj['ike_version'] if 'ike_version' in obj and obj['ike_version'].lower() in ('ike', 'ikev1', 'ikev2') else 'ike' + + peerlistarr = peerlist.split(',') + if splitconnections: + logging.debug('Splitting rightsubnets %s' % peerlistarr) + peerlist = peerlistarr[0] if rightpeer in self.confips: self.confips.remove(rightpeer) file = CsFile(vpnconffile) + file.repopulate() # This avoids issues when switching off split_connections or removing subnets with split_connections == true file.add("#conn for vpn-%s" % rightpeer, 0) file.search("conn ", "conn vpn-%s" % rightpeer) file.addeq(" left=%s" % leftpeer) @@ -568,7 +576,7 @@ class CsSite2SiteVpn(CsDataBag): file.addeq(" rightsubnet=%s" % peerlist) file.addeq(" type=tunnel") file.addeq(" authby=secret") - file.addeq(" keyexchange=ike") + file.addeq(" keyexchange=%s" % ikeversion) file.addeq(" ike=%s" % ikepolicy) file.addeq(" ikelifetime=%s" % self.convert_sec_to_h(obj['ike_lifetime'])) file.addeq(" esp=%s" % esppolicy) @@ -582,6 +590,14 @@ class CsSite2SiteVpn(CsDataBag): file.addeq(" dpddelay=30") file.addeq(" dpdtimeout=120") file.addeq(" dpdaction=restart") + if splitconnections and peerlistarr.count > 1: + logging.debug('Splitting connections for rightsubnets %s' % peerlistarr) + for peeridx in range(1, len(peerlistarr)): + logging.debug('Adding split connection -%d for subnet %s' % (peeridx + 1, peerlistarr[peeridx])) + file.append('') + file.search('conn vpn-.*-%d' % (peeridx + 1), "conn vpn-%s-%d" % (rightpeer, peeridx + 1)) + file.append(' also=vpn-%s' % rightpeer) + file.append(' rightsubnet=%s' % peerlistarr[peeridx]) secret = CsFile(vpnsecretsfile) secret.search("%s " % leftpeer, "%s %s : PSK \"%s\"" % (leftpeer, rightpeer, obj['ipsec_psk'])) if secret.is_changed() or file.is_changed(): @@ -595,14 +611,25 @@ class CsSite2SiteVpn(CsDataBag): os.chmod(vpnsecretsfile, 0400) for i in xrange(3): - result = CsHelper.execute('ipsec status vpn-%s | grep "%s"' % (rightpeer, peerlist.split(",", 1)[0])) - if len(result) > 0: + done = True + for peeridx in range(0, len(peerlistarr)): + # Check for the proper connection and subnet + conn = rightpeer if not splitconnections else rightpeer if peeridx == 0 else '%s-%d' % (rightpeer, peeridx + 1) + result = CsHelper.execute('ipsec status vpn-%s | grep "%s"' % (conn, peerlistarr[peeridx])) + # If any of the peers hasn't yet finished, continue the outer loop + if len(result) == 0: + done = False + if done: break time.sleep(1) # With 'auto=route', connections are established on an attempt to # communicate over the S2S VPN. This uses ping to initialize the connection. - CsHelper.execute("timeout 5 ping -c 3 %s" % (peerlist.split("/", 1)[0].replace(".0", ".1"))) + for peer in peerlistarr: + octets = peer.split('/', 1)[0].split('.') + octets[3] = str((int(octets[3]) + 1)) + ipinsubnet = '.'.join(octets) + CsHelper.execute("timeout 5 ping -c 3 %s" % ipinsubnet) def convert_sec_to_h(self, val): hrs = int(val) / 3600 diff --git a/systemvm/debian/opt/cloud/bin/ipsectunnel.sh b/systemvm/debian/opt/cloud/bin/ipsectunnel.sh index c42650f8d1d..ad12efddbdb 100755 --- a/systemvm/debian/opt/cloud/bin/ipsectunnel.sh +++ b/systemvm/debian/opt/cloud/bin/ipsectunnel.sh @@ -23,7 +23,7 @@ vpnoutmark="0x525" vpninmark="0x524" usage() { - printf "Usage: %s: (-A|-D) -l -n -g -r -N -e -i -t -T -s -d [ -p -c -S ]\n" $(basename $0) >&2 + printf "Usage: %s: (-A|-D) -l -n -g -r -N -e -i -t -T -s -d [ -p -c -S -C -v ]\n" $(basename $0) >&2 } #set -x @@ -139,14 +139,21 @@ ipsec_tunnel_add() { check_and_enable_iptables + rsubnets=" rightsubnets={$rightnets}" + if [ $splitconnections -eq 1 ] + then + rsubnetarr=(${rightnets}) + rsubnets=" rightsubnet=${rsubnetarr[0]}" + fi + sudo echo "conn vpn-$rightpeer" > $vpnconffile && sudo echo " left=$leftpeer" >> $vpnconffile && sudo echo " leftsubnet=$leftnet" >> $vpnconffile && sudo echo " right=$rightpeer" >> $vpnconffile && - sudo echo " rightsubnets={$rightnets}" >> $vpnconffile && + sudo echo $rsubnets >> $vpnconffile && sudo echo " type=tunnel" >> $vpnconffile && sudo echo " authby=secret" >> $vpnconffile && - sudo echo " keyexchange=ike" >> $vpnconffile && + sudo echo " keyexchange=${ikeversion:-ike}" >> $vpnconffile && sudo echo " ike=$ikepolicy" >> $vpnconffile && sudo echo " ikelifetime=${ikelifetime}s" >> $vpnconffile && sudo echo " esp=$esppolicy" >> $vpnconffile && @@ -163,6 +170,20 @@ ipsec_tunnel_add() { sudo echo " dpdaction=restart" >> $vpnconffile fi + if [ $splitconnections -eq 1 ] + then + # Split out all but the first right subnet into their own statements + subnetidx=2 + for rsubnet in ${rsubnetarr[@]:1}; do + sudo echo "" >> $vpnconffile && + sudo echo "conn vpn-$rightpeer-$subnetidx" >> $vpnconffile && + sudo echo " also=vpn-$rightpeer" >> $vpnconffile && + sudo echo " auto=route" >> $vpnconffile && + sudo echo " rightsubnet=$rsubnet" >> $vpnconffile + ((++subnetidx)) + done + fi + enable_iptables_subnets sudo ipsec auto --rereadall @@ -215,8 +236,10 @@ passive=0 op="" checkup=0 secure=1 +ikeversion="ike" +splitconnections=0 -while getopts 'ADSpcl:n:g:r:N:e:i:t:T:s:d:' OPTION +while getopts 'ACDSpcl:n:g:r:N:e:i:t:T:s:d:v:' OPTION do case $OPTION in A) opflag=1 @@ -243,6 +266,8 @@ do e) eflag=1 esppolicy="$OPTARG" ;; + v) ikeversion="$OPTARG" + ;; i) iflag=1 ikepolicy="$OPTARG" ;; @@ -264,6 +289,8 @@ do ;; S) secure=0 ;; + C) splitconnections=1 + ;; ?) usage exit 2 ;; diff --git a/ui/public/locales/ca.json b/ui/public/locales/ca.json index 743d82f1e81..1e312e9965b 100644 --- a/ui/public/locales/ca.json +++ b/ui/public/locales/ca.json @@ -828,6 +828,7 @@ "label.icmpcode": "ICMP Code", "label.icmptype": "ICMP Type", "label.id": "ID", +"label.ike.version": "IKE Version", "label.ikedh": "IKE DH", "label.ikeencryption": "IKE Encryption", "label.ikehash": "IKE Hash", @@ -897,6 +898,7 @@ "label.ipaddress2": "IP Address", "label.iplimit": "Public IP Limits", "label.ips": "IPs", +"label.ipsec.splitconnections": "Split Connections", "label.ipsecpsk": "IPsec Preshared-Key", "label.iptotal": "Total of IP Addresses", "label.ipv4.cidr": "IPv4 CIDR", diff --git a/ui/public/locales/en.json b/ui/public/locales/en.json index 25a2e7e6e3f..8e430dd7d87 100644 --- a/ui/public/locales/en.json +++ b/ui/public/locales/en.json @@ -1029,11 +1029,13 @@ "label.icmptype.start.port": "ICMP Type / Start Port", "label.id": "ID", "label.identity.and.access": "Identity and Access", +"label.ike.version": "IKE Version", "label.ikedh": "IKE DH", "label.ikeencryption": "IKE Encryption", "label.ikehash": "IKE Hash", "label.ikelifetime": "IKE lifetime (second)", "label.ikepolicy": "IKE policy", +"label.ikeversion": "IKE Version", "label.images": "Images", "label.import.backup.offering": "Import Backup Offering", "label.import.offering": "Import Offering", @@ -1109,6 +1111,7 @@ "label.ipaddress2": "IP Address", "label.iplimit": "Public IP Limits", "label.ips": "IPs", +"label.ipsec.splitconnections": "Split Connections", "label.ipsecpsk": "IPsec Preshared-Key", "label.iptotal": "Total of IP Addresses", "label.ipv4.cidr": "IPv4 CIDR", @@ -1983,6 +1986,7 @@ "label.specify.vxlan": "Specify VXLAN", "label.specifyipranges": "Specify IP ranges", "label.specifyvlan": "Specify VLAN", +"label.splitconnections": "Split Connections", "label.sr.name": "SR Name-Label", "label.srx": "SRX", "label.srx.details": "SRX details", diff --git a/ui/public/locales/it_IT.json b/ui/public/locales/it_IT.json index 9a6be067678..a8308a0bc05 100644 --- a/ui/public/locales/it_IT.json +++ b/ui/public/locales/it_IT.json @@ -828,6 +828,7 @@ "label.icmpcode": "Codice ICMP", "label.icmptype": "Tipo ICMP", "label.id": "ID", +"label.ike.version": "Versione di IKE", "label.ikedh": "DH di IKE", "label.ikeencryption": "Encryption di IKE", "label.ikehash": "Hash di IKE", @@ -897,6 +898,7 @@ "label.ipaddress2": "Indirizzo IP", "label.iplimit": "Public IP Limits", "label.ips": "Indirizzi IP", +"label.ipsec.splitconnections": "Connessioni Divise", "label.ipsecpsk": "Preshared-Key di IPsec", "label.iptotal": "Total of IP Addresses", "label.ipv4.cidr": "IPv4 CIDR", diff --git a/ui/public/locales/nb_NO.json b/ui/public/locales/nb_NO.json index 722529319bd..81cbbbe3668 100644 --- a/ui/public/locales/nb_NO.json +++ b/ui/public/locales/nb_NO.json @@ -828,6 +828,7 @@ "label.icmpcode": "ICMP-kode", "label.icmptype": "ICMP-type", "label.id": "ID", +"label.ike.version": "IKE versjon", "label.ikedh": "IKE DH", "label.ikeencryption": "IKE kryptering", "label.ikehash": "IKE Hash", @@ -897,6 +898,7 @@ "label.ipaddress2": "IP-adresse", "label.iplimit": "Offentlig IP-addresse Grenser", "label.ips": "IPer", +"label.ipsec.splitconnections": "delte forbindelser", "label.ipsecpsk": "IPSec Delt N\u00f8kkel", "label.iptotal": "Totalt IP-adresser", "label.ipv4.cidr": "IPv4 CIDR", diff --git a/ui/public/locales/nl_NL.json b/ui/public/locales/nl_NL.json index 4a5e606f073..07aae3063b0 100644 --- a/ui/public/locales/nl_NL.json +++ b/ui/public/locales/nl_NL.json @@ -828,6 +828,7 @@ "label.icmpcode": "ICMP Code", "label.icmptype": "ICMP Type", "label.id": "ID", +"label.ike.version": "IKE Versie", "label.ikedh": "IKE DH", "label.ikeencryption": "IKE Encryptie", "label.ikehash": "IKE Hash", @@ -897,6 +898,7 @@ "label.ipaddress2": "IP Adres", "label.iplimit": "Publieke IP Limieten", "label.ips": "IPs", +"label.ipsec.splitconnections": "Gesplitste Verbindingen", "label.ipsecpsk": "IPsec Preshared-Key", "label.iptotal": "totaal aantal IP adressen", "label.ipv4.cidr": "IPv4 CIDR", diff --git a/ui/public/locales/pl.json b/ui/public/locales/pl.json index 29c1c0617d0..163f1a2a727 100644 --- a/ui/public/locales/pl.json +++ b/ui/public/locales/pl.json @@ -828,6 +828,7 @@ "label.icmpcode": "ICMP Code", "label.icmptype": "ICMP Type", "label.id": "ID", +"label.ike.version": "IKE Version", "label.ikedh": "IKE DH", "label.ikeencryption": "IKE Encryption", "label.ikehash": "IKE Hash", @@ -897,6 +898,7 @@ "label.ipaddress2": "IP Address", "label.iplimit": "Public IP Limits", "label.ips": "IP", +"label.ipsec.splitconnections": "Split Connections", "label.ipsecpsk": "IPsec Preshared-Key", "label.iptotal": "Total of IP Addresses", "label.ipv4.cidr": "IPv4 CIDR", diff --git a/ui/src/config/section/network.js b/ui/src/config/section/network.js index f274fd51d73..4076509ad21 100644 --- a/ui/src/config/section/network.js +++ b/ui/src/config/section/network.js @@ -407,7 +407,7 @@ export default { hidden: true, permission: ['listVpnConnections'], columns: ['publicip', 'state', 'gateway', 'ipsecpsk', 'ikepolicy', 'esppolicy'], - details: ['publicip', 'gateway', 'passive', 'cidrlist', 'ipsecpsk', 'ikepolicy', 'esppolicy', 'ikelifetime', 'esplifetime', 'dpd', 'forceencap', 'created'], + details: ['publicip', 'gateway', 'passive', 'cidrlist', 'ipsecpsk', 'ikepolicy', 'esppolicy', 'ikelifetime', 'ikeversion', 'esplifetime', 'dpd', 'splitconnections', 'forceencap', 'created'], actions: [ { api: 'createVpnConnection', @@ -593,7 +593,7 @@ export default { icon: 'lock', permission: ['listVpnCustomerGateways'], columns: ['name', 'gateway', 'cidrlist', 'ipsecpsk', 'account'], - details: ['name', 'id', 'gateway', 'cidrlist', 'ipsecpsk', 'ikepolicy', 'ikelifetime', 'esppolicy', 'esplifetime', 'dpd', 'forceencap', 'account', 'domain'], + details: ['name', 'id', 'gateway', 'cidrlist', 'ipsecpsk', 'ikepolicy', 'ikelifetime', 'ikeversion', 'esppolicy', 'esplifetime', 'dpd', 'splitconnections', 'forceencap', 'account', 'domain'], searchFilters: ['keyword', 'domainid', 'account'], actions: [ { @@ -611,7 +611,12 @@ export default { label: 'label.edit', docHelp: 'adminguide/networking_and_traffic.html#updating-and-removing-a-vpn-customer-gateway', dataView: true, - args: ['name', 'gateway', 'cidrlist', 'ipsecpsk', 'ikepolicy', 'ikelifetime', 'esppolicy', 'esplifetime', 'dpd', 'forceencap'] + args: ['name', 'gateway', 'cidrlist', 'ipsecpsk', 'ikepolicy', 'ikelifetime', 'ikeversion', 'esppolicy', 'esplifetime', 'dpd', 'splitconnections', 'forceencap'], + mapping: { + ikeversion: { + options: ['ike', 'ikev1', 'ikev2'] + } + } }, { api: 'deleteVpnCustomerGateway', diff --git a/ui/src/views/network/CreateVpnCustomerGateway.vue b/ui/src/views/network/CreateVpnCustomerGateway.vue index 99ff4cd0ec5..666bece867b 100644 --- a/ui/src/views/network/CreateVpnCustomerGateway.vue +++ b/ui/src/views/network/CreateVpnCustomerGateway.vue @@ -108,6 +108,26 @@ + + + {{ $t('label.ikeversion') }} + + + + + + + {{ vers }} + + + + + + {{ $t('label.splitconnections') }} + + + + + + {{ $t('label.forceencap') }} @@ -270,6 +305,11 @@ export default { 'sha512', 'md5' ], + ikeVersions: [ + 'ike', + 'ikev1', + 'ikev2' + ], DHGroups: { '': 'None', 'Group 2': 'modp1024', @@ -280,7 +320,8 @@ export default { 'Group 17': 'modp6144', 'Group 18': 'modp8192' }, - ikeDhGroupInitialValue: 'Group 5(modp1536)' + ikeDhGroupInitialValue: 'Group 5(modp1536)', + ikeversion: 'ike' } }, beforeCreate () { @@ -317,7 +358,9 @@ export default { dpd: values.dpd, forceencap: values.forceencap, ikepolicy: ikepolicy, - esppolicy: esppolicy + esppolicy: esppolicy, + splitconnections: values.splitconnections, + ikeversion: values.ikeversion }).then(response => { this.$store.dispatch('AddAsyncJob', { title: this.$t('message.add.vpn.customer.gateway'),