diff --git a/api/src/main/java/com/cloud/network/Site2SiteCustomerGateway.java b/api/src/main/java/com/cloud/network/Site2SiteCustomerGateway.java index f9a88bdd8453..de83fddf8132 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 5c3050c85ae5..4d7accbc75d2 100644 --- a/api/src/main/java/org/apache/cloudstack/api/ApiConstants.java +++ b/api/src/main/java/org/apache/cloudstack/api/ApiConstants.java @@ -628,12 +628,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 6f591756d2e9..f99ee54c695f 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,12 @@ 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. Defaults to ike") + private String ikeVersion; + ///////////////////////////////////////////////////// /////////////////// Accessors /////////////////////// ///////////////////////////////////////////////////// @@ -146,6 +152,14 @@ public Long getProjectId() { 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 d7bf5c4b557e..cb8fb9ceae42 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,12 @@ 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. Defaults to ike") + private String ikeVersion; + ///////////////////////////////////////////////////// /////////////////// Accessors /////////////////////// ///////////////////////////////////////////////////// @@ -140,6 +146,14 @@ public Boolean getDpd() { public Boolean getEncap() { return encap; } + public boolean getSplitConnections() { + return 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 8128405cdaee..88e0e1600e5b 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 void setDpd(Boolean dpd) { 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 edaa1b262a74..1f7509239d1a 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 void setRemoved(Date removed) { 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 685cf4049c79..f679b75d155d 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 Site2SiteVpnCfgCommand() { } 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 Site2SiteVpnCfgCommand(boolean create, String localPublicIp, String local this.dpd = dpd; this.passive = passive; this.encap = encap; + this.splitConnections = splitConnections; + this.ikeVersion = ikeVersion; } public boolean isCreate() { @@ -174,4 +179,20 @@ public boolean isPassive() { 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 5bb466c59352..badd0d376d12 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 List generateConfig(final NetworkElementCommand cmd) { 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 232e99f099ac..9057bf4f2936 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 @@ 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 Site2SiteVpn() { 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 Site2SiteVpn(String localPublicIp, String localGuestCidr, String localPub this.dpd = dpd; this.passive = passive; this.encap = encap; + this.splitConnections = splitConnections; + this.ikeVersion = ikeVersion; } public String getLocalPublicIp() { @@ -161,4 +163,20 @@ public void setEncap(boolean encap) { 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 200f266b9251..6eb30aeeed9e 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 @@ private void verifyArgs(final SetMonitorServiceCommand cmd, final String script, 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 f1d3ef32712d..c8241518d63e 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 Site2SiteCustomerGatewayVO() { } 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 Site2SiteCustomerGatewayVO(String name, long accountId, long domainId, St uuid = UUID.randomUUID().toString(); this.accountId = accountId; this.domainId = domainId; + this.splitConnections = splitConnections; + this.ikeVersion = ikeVersion; } @Override @@ -221,6 +229,24 @@ public long getAccountId() { 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-41510to41600.sql b/engine/schema/src/main/resources/META-INF/db/schema-41510to41600.sql index 7168f883db67..058daa54ca93 100644 --- a/engine/schema/src/main/resources/META-INF/db/schema-41510to41600.sql +++ b/engine/schema/src/main/resources/META-INF/db/schema-41510to41600.sql @@ -301,3 +301,7 @@ from left join `cloud`.`resource_count` secondary_storage_count ON domain.id = secondary_storage_count.domain_id and secondary_storage_count.type = 'secondary_storage'; + +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; + diff --git a/server/src/main/java/com/cloud/api/ApiResponseHelper.java b/server/src/main/java/com/cloud/api/ApiResponseHelper.java index edbd649c9431..486b0f55e2ca 100644 --- a/server/src/main/java/com/cloud/api/ApiResponseHelper.java +++ b/server/src/main/java/com/cloud/api/ApiResponseHelper.java @@ -3208,6 +3208,8 @@ public Site2SiteCustomerGatewayResponse createSite2SiteCustomerGatewayResponse(S 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()); @@ -3247,6 +3249,8 @@ public Site2SiteVpnConnectionResponse createSite2SiteVpnConnectionResponse(Site2 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 262fa4b4ff14..96c487731dc3 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 void createSite2SiteVpnCfgCommands(final Site2SiteVpnConnection conn, fin 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 7fd3473a85f4..668eb114cb66 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.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 Site2SiteCustomerGateway createCustomerGateway(CreateVpnCustomerGatewayCm 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 Site2SiteCustomerGateway updateCustomerGateway(UpdateVpnCustomerGatewayCm } _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,16 @@ public Site2SiteCustomerGateway updateCustomerGateway(UpdateVpnCustomerGatewayCm encap = false; } + Boolean splitConnections = cmd.getSplitConnections(); + if (splitConnections == null) { + splitConnections = false; + } + + String ikeVersion = cmd.getIkeVersion(); + if (ikeVersion == null) { + ikeVersion = "ike"; + } + checkCustomerGatewayCidrList(guestCidrList); long accountId = gw.getAccountId(); @@ -494,7 +507,38 @@ public Site2SiteCustomerGateway updateCustomerGateway(UpdateVpnCustomerGatewayCm gw.setEspLifetime(espLifetime); gw.setDpd(dpd); gw.setEncap(encap); + gw.setSplitConnections(splitConnections); + gw.setIkeVersion(ikeVersion); _customerGatewayDao.persist(gw); + + List conns = _vpnConnectionDao.listByCustomerGatewayId(id); + 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 + continue; + } + } + } + return gw; } 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 0ddbd845a8c7..aed4769ab964 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 void testUpdateSite2SiteVpnConnectionState() throws Exception{ 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 be67f403c8be..d0a83abd8551 100755 --- a/systemvm/debian/opt/cloud/bin/configure.py +++ b/systemvm/debian/opt/cloud/bin/configure.py @@ -556,10 +556,18 @@ def configure_ipsec(self, obj): 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 @@ def configure_ipsec(self, obj): 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 @@ def configure_ipsec(self, obj): 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 @@ def configure_ipsec(self, obj): 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 c42650f8d1de..ad12efddbdb6 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 743d82f1e81c..1e312e9965b2 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 3241c82309d8..4689e39a5efd 100644 --- a/ui/public/locales/en.json +++ b/ui/public/locales/en.json @@ -1025,11 +1025,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", @@ -1105,6 +1107,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", @@ -1973,6 +1976,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 9a6be067678c..a8308a0bc05e 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 722529319bd7..81cbbbe3668c 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 4a5e606f0737..07aae3063b00 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 29c1c0617d0a..163f1a2a7274 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 29b57e2f3f5d..030142361f26 100644 --- a/ui/src/config/section/network.js +++ b/ui/src/config/section/network.js @@ -402,7 +402,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', @@ -588,7 +588,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: [ { @@ -606,7 +606,7 @@ 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'] }, { api: 'deleteVpnCustomerGateway', diff --git a/ui/src/views/network/CreateVpnCustomerGateway.vue b/ui/src/views/network/CreateVpnCustomerGateway.vue index ebbb7be12167..7654a0bd78a4 100644 --- a/ui/src/views/network/CreateVpnCustomerGateway.vue +++ b/ui/src/views/network/CreateVpnCustomerGateway.vue @@ -107,6 +107,19 @@ + + + + {{ vers }} + + + + + + {{ $t('label.splitconnections') }} + + + + + + {{ $t('label.forceencap') }} @@ -269,6 +297,11 @@ export default { 'sha512', 'md5' ], + ikeVersions: [ + 'ike', + 'ikev1', + 'ikev2' + ], DHGroups: { '': 'None', 'Group 2': 'modp1024', @@ -316,7 +349,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'),