diff --git a/api/src/com/cloud/agent/api/routing/LoadBalancerConfigCommand.java b/api/src/com/cloud/agent/api/routing/LoadBalancerConfigCommand.java index fe0c146b85d..7cbfa0d7cf2 100644 --- a/api/src/com/cloud/agent/api/routing/LoadBalancerConfigCommand.java +++ b/api/src/com/cloud/agent/api/routing/LoadBalancerConfigCommand.java @@ -25,7 +25,7 @@ import com.cloud.agent.api.to.LoadBalancerTO; */ public class LoadBalancerConfigCommand extends NetworkElementCommand { LoadBalancerTO[] loadBalancers; - public String lbStatsVisibility; + public String lbStatsVisibility = "guest-network"; public String lbStatsIp; /* load balancer listen on this ip for stats */ public String lbStatsPort = "8081"; /*load balancer listen on this port for stats */ public String lbStatsSrcCidrs = "0/0" ; /* TODO : currently there is no filtering based on the source ip */ diff --git a/api/src/com/cloud/agent/api/routing/SetFirewallRulesAnswer.java b/api/src/com/cloud/agent/api/routing/SetFirewallRulesAnswer.java index 701b5f5aeb8..99fbe7f055f 100644 --- a/api/src/com/cloud/agent/api/routing/SetFirewallRulesAnswer.java +++ b/api/src/com/cloud/agent/api/routing/SetFirewallRulesAnswer.java @@ -25,9 +25,8 @@ public class SetFirewallRulesAnswer extends Answer { protected SetFirewallRulesAnswer() { } - public SetFirewallRulesAnswer(SetFirewallRulesCommand cmd, String[] results) { - super(cmd, true, null); - + public SetFirewallRulesAnswer(SetFirewallRulesCommand cmd, Boolean success, String[] results) { + super(cmd, success, null); assert (cmd.getRules().length == results.length) : "rules and their results should be the same length don't you think?"; this.results = results; } diff --git a/api/src/com/cloud/agent/api/routing/SetFirewallRulesCommand.java b/api/src/com/cloud/agent/api/routing/SetFirewallRulesCommand.java index 3a48e3ffd15..f1c584c978b 100644 --- a/api/src/com/cloud/agent/api/routing/SetFirewallRulesCommand.java +++ b/api/src/com/cloud/agent/api/routing/SetFirewallRulesCommand.java @@ -17,9 +17,13 @@ */ package com.cloud.agent.api.routing; +import java.util.HashSet; import java.util.List; +import java.util.Set; import com.cloud.agent.api.to.FirewallRuleTO; +import com.cloud.agent.api.to.LoadBalancerTO; +import com.cloud.utils.StringUtils; /** * SetFirewallRulesCommand is the transport for firewall rules. @@ -40,4 +44,59 @@ public class SetFirewallRulesCommand extends NetworkElementCommand { public FirewallRuleTO[] getRules() { return rules; } + + public String[][] generateFwRules() { + String [][] result = new String [2][]; + Set toAdd = new HashSet(); + + + for (FirewallRuleTO fwTO: rules) { + /* example : 172.16.92.44:tcp:80:80:0.0.0.0/0:,200.16.92.44:tcp:220:220:0.0.0.0/0:, + * each entry format :protocol:srcport:destport:scidr: + * reverted entry format :reverted:0:0:0: + */ + if (fwTO.revoked() == true) + { + StringBuilder sb = new StringBuilder(); + /* This entry is added just to make sure atleast there will one entry in the list to get the ipaddress */ + sb.append(fwTO.getSrcIp()).append(":reverted:0:0:0:"); + String fwRuleEntry = sb.toString(); + toAdd.add(fwRuleEntry); + continue; + } + + List cidr; + StringBuilder sb = new StringBuilder(); + sb.append(fwTO.getSrcIp()).append(":").append(fwTO.getProtocol()).append(":"); + if ("icmp".compareTo(fwTO.getProtocol()) == 0) + { + sb.append(fwTO.getIcmpType()).append(":").append(fwTO.getIcmpCode()).append(":"); + + }else if (fwTO.getStringSrcPortRange() == null) + sb.append("0:0").append(":"); + else + sb.append(fwTO.getStringSrcPortRange()).append(":"); + + cidr = fwTO.getSourceCidrList(); + if (cidr == null || cidr.isEmpty()) + { + sb.append("0.0.0.0/0"); + }else{ + Boolean firstEntry = true; + for (String tag : cidr) { + if (!firstEntry) sb.append("-"); + sb.append(tag); + firstEntry = false; + } + } + sb.append(":"); + String fwRuleEntry = sb.toString(); + + toAdd.add(fwRuleEntry); + + } + result[0] = toAdd.toArray(new String[toAdd.size()]); + + return result; + } } diff --git a/api/src/com/cloud/agent/api/routing/SetStaticNatRulesAnswer.java b/api/src/com/cloud/agent/api/routing/SetStaticNatRulesAnswer.java index d7942e0cf7a..f393e8d31de 100644 --- a/api/src/com/cloud/agent/api/routing/SetStaticNatRulesAnswer.java +++ b/api/src/com/cloud/agent/api/routing/SetStaticNatRulesAnswer.java @@ -25,8 +25,8 @@ public class SetStaticNatRulesAnswer extends Answer { super(); } - public SetStaticNatRulesAnswer(SetStaticNatRulesCommand cmd, String[] results) { - super(cmd, true, null); + public SetStaticNatRulesAnswer(SetStaticNatRulesCommand cmd, String[] results, Boolean success) { + super(cmd, success, null); assert(cmd.getRules().length == results.length) : "Shouldn't the results match the commands?"; this.results = results; diff --git a/api/src/com/cloud/agent/api/to/FirewallRuleTO.java b/api/src/com/cloud/agent/api/to/FirewallRuleTO.java index 236f0562524..96e02d0afdb 100644 --- a/api/src/com/cloud/agent/api/to/FirewallRuleTO.java +++ b/api/src/com/cloud/agent/api/to/FirewallRuleTO.java @@ -51,12 +51,16 @@ public class FirewallRuleTO { int[] srcPortRange; boolean revoked; boolean alreadyAdded; + private List sourceCidrList; FirewallRule.Purpose purpose; + private Integer icmpType; + private Integer icmpCode; + protected FirewallRuleTO() { } - public FirewallRuleTO(long id, String srcIp, String protocol, Integer srcPortStart, Integer srcPortEnd, boolean revoked, boolean alreadyAdded, FirewallRule.Purpose purpose) { + public FirewallRuleTO(long id, String srcIp, String protocol, Integer srcPortStart, Integer srcPortEnd, boolean revoked, boolean alreadyAdded, FirewallRule.Purpose purpose, List sourceCidr,Integer icmpType,Integer icmpCode) { this.srcIp = srcIp; this.protocol = protocol; @@ -78,10 +82,13 @@ public class FirewallRuleTO { this.revoked = revoked; this.alreadyAdded = alreadyAdded; this.purpose = purpose; + this.sourceCidrList = sourceCidr; + this.icmpType = icmpType; + this.icmpCode = icmpCode; } public FirewallRuleTO(FirewallRule rule, String srcIp) { - this(rule.getId(), srcIp, rule.getProtocol(), rule.getSourcePortStart(), rule.getSourcePortEnd(), rule.getState()==State.Revoke, rule.getState()==State.Active, rule.getPurpose()); + this(rule.getId(), srcIp, rule.getProtocol(), rule.getSourcePortStart(), rule.getSourcePortEnd(), rule.getState()==State.Revoke, rule.getState()==State.Active, rule.getPurpose(),rule.getSourceCidrList(),rule.getIcmpType(),rule.getIcmpCode()); } public long getId() { @@ -100,14 +107,29 @@ public class FirewallRuleTO { return srcPortRange; } + public Integer getIcmpType(){ + return icmpType; + } + + public Integer getIcmpCode(){ + return icmpCode; + } + public String getStringSrcPortRange() { - return NetUtils.portRangeToString(srcPortRange); + if (srcPortRange == null || srcPortRange.length < 2) + return "0:0"; + else + return NetUtils.portRangeToString(srcPortRange); } public boolean revoked() { return revoked; } + public List getSourceCidrList() { + return sourceCidrList; + } + public boolean isAlreadyAdded() { return alreadyAdded; } diff --git a/api/src/com/cloud/agent/api/to/PortForwardingRuleTO.java b/api/src/com/cloud/agent/api/to/PortForwardingRuleTO.java index 9818ebb523a..4b6342b0c59 100644 --- a/api/src/com/cloud/agent/api/to/PortForwardingRuleTO.java +++ b/api/src/com/cloud/agent/api/to/PortForwardingRuleTO.java @@ -45,7 +45,7 @@ public class PortForwardingRuleTO extends FirewallRuleTO { } protected PortForwardingRuleTO(long id, String srcIp, int srcPortStart, int srcPortEnd, String dstIp, int dstPortStart, int dstPortEnd, String protocol, boolean revoked, boolean brandNew) { - super(id, srcIp, protocol, srcPortStart, srcPortEnd, revoked, brandNew, FirewallRule.Purpose.PortForwarding); + super(id, srcIp, protocol, srcPortStart, srcPortEnd, revoked, brandNew, FirewallRule.Purpose.PortForwarding, null,0,0); this.dstIp = dstIp; this.dstPortRange = new int[] { dstPortStart, dstPortEnd }; } diff --git a/api/src/com/cloud/agent/api/to/StaticNatRuleTO.java b/api/src/com/cloud/agent/api/to/StaticNatRuleTO.java index 2c2f0dfdd1b..a4845989d7f 100644 --- a/api/src/com/cloud/agent/api/to/StaticNatRuleTO.java +++ b/api/src/com/cloud/agent/api/to/StaticNatRuleTO.java @@ -36,13 +36,13 @@ public class StaticNatRuleTO extends FirewallRuleTO{ } public StaticNatRuleTO(StaticNatRule rule, String scrIp, String dstIp) { - super(rule.getId(), scrIp, rule.getProtocol(), rule.getSourcePortStart(), rule.getSourcePortEnd(),rule.getState()==State.Revoke, rule.getState()==State.Active, rule.getPurpose()); + super(rule.getId(), scrIp, rule.getProtocol(), rule.getSourcePortStart(), rule.getSourcePortEnd(),rule.getState()==State.Revoke, rule.getState()==State.Active, rule.getPurpose(), null,0,0); this.dstIp = dstIp; } - protected StaticNatRuleTO(long id, String srcIp, int srcPortStart, int srcPortEnd, String dstIp, int dstPortStart, int dstPortEnd, String protocol, boolean revoked, boolean brandNew) { - super(id, srcIp, protocol, srcPortStart, srcPortEnd, revoked, brandNew, FirewallRule.Purpose.StaticNat); + public StaticNatRuleTO(long id, String srcIp, Integer srcPortStart, Integer srcPortEnd, String dstIp, Integer dstPortStart, Integer dstPortEnd, String protocol, boolean revoked, boolean alreadyAdded) { + super(id, srcIp, protocol, srcPortStart, srcPortEnd, revoked, alreadyAdded, FirewallRule.Purpose.StaticNat, null,0,0); this.dstIp = dstIp; } diff --git a/api/src/com/cloud/api/commands/CreateIpForwardingRuleCmd.java b/api/src/com/cloud/api/commands/CreateIpForwardingRuleCmd.java index 6d2ddc2ddd4..5011ee4f76c 100644 --- a/api/src/com/cloud/api/commands/CreateIpForwardingRuleCmd.java +++ b/api/src/com/cloud/api/commands/CreateIpForwardingRuleCmd.java @@ -66,6 +66,9 @@ public class CreateIpForwardingRuleCmd extends BaseAsyncCreateCmd implements Sta @Parameter(name = ApiConstants.OPEN_FIREWALL, type = CommandType.BOOLEAN, description = "if true, firewall rule for source/end pubic port is automatically created; if false - firewall rule has to be created explicitely. Has value true by default") private Boolean openFirewall; + @Parameter(name = ApiConstants.CIDR_LIST, type = CommandType.LIST, collectionType = CommandType.STRING, description = "the cidr list to forward traffic from") + private List cidrlist; + ///////////////////////////////////////////////////// /////////////////// Accessors /////////////////////// @@ -102,13 +105,14 @@ public class CreateIpForwardingRuleCmd extends BaseAsyncCreateCmd implements Sta @Override public void execute() throws ResourceUnavailableException{ + boolean result = true; FirewallRule rule = null; try { UserContext.current().setEventDetails("Rule Id: "+ getEntityId()); if (getOpenFirewall()) { - result = result && _firewallService.applyFirewallRules(rule.getSourceIpAddressId(), UserContext.current().getCaller()); + result = result && _firewallService.applyFirewallRules(ipAddressId, UserContext.current().getCaller()); } result = result && _rulesService.applyStaticNatRules(ipAddressId, UserContext.current().getCaller()); @@ -127,15 +131,19 @@ public class CreateIpForwardingRuleCmd extends BaseAsyncCreateCmd implements Sta @Override public void create() { - StaticNatRule rule; + + //cidr list parameter is deprecated + if (cidrlist != null) { + throw new InvalidParameterValueException("Parameter cidrList is deprecated; if you need to open firewall rule for the specific cidr, please refer to createFirewallRule command"); + } + try { - rule = _rulesService.createStaticNatRule(this, getOpenFirewall()); + StaticNatRule rule = _rulesService.createStaticNatRule(this, getOpenFirewall()); + this.setEntityId(rule.getId()); } catch (NetworkRuleConflictException e) { - s_logger.info("Unable to create Static Nat Rule due to " + e.getMessage()); + s_logger.info("Unable to create Static Nat Rule due to ", e); throw new ServerApiException(BaseCmd.NETWORK_RULE_CONFLICT_ERROR, e.getMessage()); } - - this.setEntityId(rule.getId()); } @Override @@ -269,4 +277,8 @@ public class CreateIpForwardingRuleCmd extends BaseAsyncCreateCmd implements Sta return null; } + public List getSourceCidrList() { + return null; + } + } diff --git a/api/src/com/cloud/api/commands/CreateLoadBalancerRuleCmd.java b/api/src/com/cloud/api/commands/CreateLoadBalancerRuleCmd.java index 0e639c1fc0b..334bad5edca 100644 --- a/api/src/com/cloud/api/commands/CreateLoadBalancerRuleCmd.java +++ b/api/src/com/cloud/api/commands/CreateLoadBalancerRuleCmd.java @@ -81,6 +81,9 @@ public class CreateLoadBalancerRuleCmd extends BaseAsyncCmd /*implements LoadBa @Parameter(name=ApiConstants.DOMAIN_ID, type=CommandType.LONG, description="the domain ID associated with the load balancer") private Long domainId; + @Parameter(name = ApiConstants.CIDR_LIST, type = CommandType.LIST, collectionType = CommandType.STRING, description = "the cidr list to forward traffic from") + private List cidrlist; + ///////////////////////////////////////////////////// /////////////////// Accessors /////////////////////// ///////////////////////////////////////////////////// @@ -125,6 +128,14 @@ public class CreateLoadBalancerRuleCmd extends BaseAsyncCmd /*implements LoadBa return true; } } + + public List getSourceCidrList() { + if (cidrlist != null) { + throw new InvalidParameterValueException("Parameter cidrList is deprecated; if you need to open firewall rule for the specific cidr, please refer to createFirewallRule command"); + } + return null; + } + ///////////////////////////////////////////////////// /////////////// API Implementation/////////////////// @@ -137,9 +148,17 @@ public class CreateLoadBalancerRuleCmd extends BaseAsyncCmd /*implements LoadBa @Override public void execute() throws ResourceAllocationException, ResourceUnavailableException { - LoadBalancer result = null; + + //cidr list parameter is deprecated + if (cidrlist != null) { + throw new InvalidParameterValueException("Parameter cidrList is deprecated; if you need to open firewall rule for the specific cidr, please refer to createFirewallRule command"); + } + try { - result = _lbService.createLoadBalancerRule(this, getOpenFirewall()); + LoadBalancer result = _lbService.createLoadBalancerRule(this, getOpenFirewall()); + LoadBalancerResponse response = _responseGenerator.createLoadBalancerResponse(result); + response.setResponseName(getCommandName()); + this.setResponseObject(response); } catch (NetworkRuleConflictException e) { s_logger.warn("Exception: ", e); throw new ServerApiException(BaseCmd.NETWORK_RULE_CONFLICT_ERROR, e.getMessage()); @@ -147,9 +166,6 @@ public class CreateLoadBalancerRuleCmd extends BaseAsyncCmd /*implements LoadBa s_logger.warn("Exception: ", e); throw new ServerApiException(BaseCmd.INSUFFICIENT_CAPACITY_ERROR, e.getMessage()); } - LoadBalancerResponse response = _responseGenerator.createLoadBalancerResponse(result); - response.setResponseName(getCommandName()); - this.setResponseObject(response); } diff --git a/api/src/com/cloud/api/commands/CreatePortForwardingRuleCmd.java b/api/src/com/cloud/api/commands/CreatePortForwardingRuleCmd.java index 42f9c93ca36..f0d390ad2f4 100644 --- a/api/src/com/cloud/api/commands/CreatePortForwardingRuleCmd.java +++ b/api/src/com/cloud/api/commands/CreatePortForwardingRuleCmd.java @@ -98,7 +98,10 @@ public class CreatePortForwardingRuleCmd extends BaseAsyncCreateCmd implements P } public List getSourceCidrList() { - return cidrlist; + if (cidrlist != null) { + throw new InvalidParameterValueException("Parameter cidrList is deprecated; if you need to open firewall rule for the specific cidr, please refer to createFirewallRule command"); + } + return null; } public Boolean getOpenFirewall() { @@ -117,24 +120,20 @@ public class CreatePortForwardingRuleCmd extends BaseAsyncCreateCmd implements P public String getCommandName() { return s_name; } - - public void setSourceCidrList(List cidrs){ - cidrlist = cidrs; - } @Override public void execute() throws ResourceUnavailableException { UserContext callerContext = UserContext.current(); boolean success = true; - PortForwardingRule rule = _entityMgr.findById(PortForwardingRule.class, getEntityId()); + PortForwardingRule rule = null; try { UserContext.current().setEventDetails("Rule Id: " + getEntityId()); if (getOpenFirewall()) { - success = success && _firewallService.applyFirewallRules(rule.getSourceIpAddressId(), callerContext.getCaller()); + success = success && _firewallService.applyFirewallRules(ipAddressId, callerContext.getCaller()); } - success = success && _rulesService.applyPortForwardingRules(rule.getSourceIpAddressId(), callerContext.getCaller()); + success = success && _rulesService.applyPortForwardingRules(ipAddressId, callerContext.getCaller()); // State is different after the rule is applied, so get new object here rule = _entityMgr.findById(PortForwardingRule.class, getEntityId()); @@ -227,17 +226,17 @@ public class CreatePortForwardingRuleCmd extends BaseAsyncCreateCmd implements P @Override public void create() { - if (cidrlist != null) - for (String cidr: cidrlist){ - if (!NetUtils.isValidCIDR(cidr)){ - throw new ServerApiException(BaseCmd.PARAM_ERROR, "Source cidrs formatting error " + cidr); - } - } + + //cidr list parameter is deprecated + if (cidrlist != null) { + throw new InvalidParameterValueException("Parameter cidrList is deprecated; if you need to open firewall rule for the specific cidr, please refer to createFirewallRule command"); + } + try { PortForwardingRule result = _rulesService.createPortForwardingRule(this, virtualMachineId, getOpenFirewall()); setEntityId(result.getId()); } catch (NetworkRuleConflictException ex) { - s_logger.info("Network rule conflict: " + ex.getMessage()); + s_logger.info("Network rule conflict: " , ex); s_logger.trace("Network Rule Conflict: ", ex); throw new ServerApiException(BaseCmd.NETWORK_RULE_CONFLICT_ERROR, ex.getMessage()); } diff --git a/api/src/com/cloud/api/commands/DisableStaticNatCmd.java b/api/src/com/cloud/api/commands/DisableStaticNatCmd.java index 258cbd6b021..afc68cd7d66 100644 --- a/api/src/com/cloud/api/commands/DisableStaticNatCmd.java +++ b/api/src/com/cloud/api/commands/DisableStaticNatCmd.java @@ -76,7 +76,7 @@ public class DisableStaticNatCmd extends BaseAsyncCmd { @Override public void execute() throws ResourceUnavailableException { - boolean result = _rulesService.disableOneToOneNat(ipAddressId); + boolean result = _rulesService.disableStaticNat(ipAddressId); if (result) { SuccessResponse response = new SuccessResponse(getCommandName()); diff --git a/api/src/com/cloud/api/commands/EnableStaticNatCmd.java b/api/src/com/cloud/api/commands/EnableStaticNatCmd.java index 3b6262b8259..e8f1879e7ab 100644 --- a/api/src/com/cloud/api/commands/EnableStaticNatCmd.java +++ b/api/src/com/cloud/api/commands/EnableStaticNatCmd.java @@ -80,7 +80,7 @@ public class EnableStaticNatCmd extends BaseCmd{ @Override public void execute(){ try { - boolean result = _rulesService.enableOneToOneNat(ipAddressId, virtualMachineId); + boolean result = _rulesService.enableStaticNat(ipAddressId, virtualMachineId); if (result) { SuccessResponse response = new SuccessResponse(getCommandName()); this.setResponseObject(response); diff --git a/api/src/com/cloud/api/response/CapabilitiesResponse.java b/api/src/com/cloud/api/response/CapabilitiesResponse.java index c4cc3539c79..ba4fbcef978 100644 --- a/api/src/com/cloud/api/response/CapabilitiesResponse.java +++ b/api/src/com/cloud/api/response/CapabilitiesResponse.java @@ -38,6 +38,9 @@ public class CapabilitiesResponse extends BaseResponse { @SerializedName("supportELB") @Param(description="true if region supports elastic load balancer on basic zones") private String supportELB; + @SerializedName("firewallRuleUiEnabled") @Param(description="true if the firewall rule UI is enabled") + private boolean firewallRuleUiEnabled; + public boolean getSecurityGroupsEnabled() { return securityGroupsEnabled; } diff --git a/api/src/com/cloud/network/element/NetworkElement.java b/api/src/com/cloud/network/element/NetworkElement.java index c14b06db9b6..a4f42dffd9d 100644 --- a/api/src/com/cloud/network/element/NetworkElement.java +++ b/api/src/com/cloud/network/element/NetworkElement.java @@ -35,6 +35,7 @@ import com.cloud.network.Network.Provider; import com.cloud.network.Network.Service; import com.cloud.network.PublicIpAddress; import com.cloud.network.rules.FirewallRule; +import com.cloud.network.rules.StaticNat; import com.cloud.offering.NetworkOffering; import com.cloud.utils.component.Adapter; import com.cloud.vm.NicProfile; @@ -132,5 +133,14 @@ public interface NetworkElement extends Adapter { * @throws ResourceUnavailableException */ boolean applyRules(Network network, List rules) throws ResourceUnavailableException; + + /** + * Creates static nat rule (public IP to private IP mapping) on the network element + * @param config + * @param rules + * @return + * @throws ResourceUnavailableException + */ + boolean applyStaticNats(Network config, List rules) throws ResourceUnavailableException; } diff --git a/api/src/com/cloud/network/rules/RulesService.java b/api/src/com/cloud/network/rules/RulesService.java index 7af02a291fd..61e912ae5e0 100644 --- a/api/src/com/cloud/network/rules/RulesService.java +++ b/api/src/com/cloud/network/rules/RulesService.java @@ -54,9 +54,9 @@ public interface RulesService { boolean applyPortForwardingRules(long ipAdddressId, Account caller) throws ResourceUnavailableException; - boolean enableOneToOneNat(long ipAddressId, long vmId) throws NetworkRuleConflictException; + boolean enableStaticNat(long ipAddressId, long vmId) throws NetworkRuleConflictException; - boolean disableOneToOneNat(long ipAddressId) throws ResourceUnavailableException; + boolean disableStaticNat(long ipAddressId) throws ResourceUnavailableException; PortForwardingRule getPortForwardigRule(long ruleId); FirewallRule getFirewallRule(long ruleId); diff --git a/api/src/com/cloud/network/rules/StaticNat.java b/api/src/com/cloud/network/rules/StaticNat.java new file mode 100644 index 00000000000..430eaf42094 --- /dev/null +++ b/api/src/com/cloud/network/rules/StaticNat.java @@ -0,0 +1,35 @@ +/** + * Copyright (C) 2010 Cloud.com, Inc. All rights reserved. + * + * This software is licensed under the GNU General Public License v3 or later. + * + * It is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or any later version. + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + * + */ + +package com.cloud.network.rules; + + +public interface StaticNat{ + + long getAccountId(); + + long getDomainId(); + + long getNetworkId(); + + long getSourceIpAddressId(); + + String getDestIpAddress(); + + boolean isForRevoke(); +} diff --git a/core/src/com/cloud/agent/resource/virtualnetwork/VirtualRoutingResource.java b/core/src/com/cloud/agent/resource/virtualnetwork/VirtualRoutingResource.java index 79cc808be7d..d51ca001c1e 100755 --- a/core/src/com/cloud/agent/resource/virtualnetwork/VirtualRoutingResource.java +++ b/core/src/com/cloud/agent/resource/virtualnetwork/VirtualRoutingResource.java @@ -52,6 +52,8 @@ import com.cloud.agent.api.routing.IpAssocAnswer; import com.cloud.agent.api.routing.LoadBalancerConfigCommand; import com.cloud.agent.api.routing.NetworkElementCommand; import com.cloud.agent.api.routing.SavePasswordCommand; +import com.cloud.agent.api.routing.SetFirewallRulesAnswer; +import com.cloud.agent.api.routing.SetFirewallRulesCommand; import com.cloud.agent.api.routing.SetPortForwardingRulesAnswer; import com.cloud.agent.api.routing.SetPortForwardingRulesCommand; import com.cloud.agent.api.routing.SetStaticNatRulesAnswer; @@ -121,6 +123,8 @@ public class VirtualRoutingResource implements Manager { return execute ((VmDataCommand)cmd); } else if (cmd instanceof CheckRouterCommand) { return execute ((CheckRouterCommand)cmd); + } else if (cmd instanceof SetFirewallRulesCommand) { + return execute((SetFirewallRulesCommand)cmd); } else { return Answer.createUnsupportedCommandAnswer(cmd); } @@ -129,6 +133,40 @@ public class VirtualRoutingResource implements Manager { } } + private Answer execute(SetFirewallRulesCommand cmd) { + String[] results = new String[cmd.getRules().length]; + for (int i =0; i < cmd.getRules().length; i++) { + results[i] = "Failed"; + } + String routerIp = cmd.getAccessDetail(NetworkElementCommand.ROUTER_IP); + + if (routerIp == null) { + return new SetFirewallRulesAnswer(cmd, false, results); + } + + String[][] rules = cmd.generateFwRules(); + final Script command = new Script(_firewallPath, _timeout, s_logger); + command.add(routerIp); + command.add("-F"); + + StringBuilder sb = new StringBuilder(); + String[] fwRules = rules[0]; + if (fwRules.length > 0) { + for (int i = 0; i < fwRules.length; i++) { + sb.append(fwRules[i]).append(','); + } + command.add("-a", sb.toString()); + } + + String result = command.execute(); + if (result != null) { + return new SetFirewallRulesAnswer(cmd, false, results); + } + return new SetFirewallRulesAnswer(cmd, true, null); + + + } + private Answer execute(SetPortForwardingRulesCommand cmd) { String routerIp = cmd.getAccessDetail(NetworkElementCommand.ROUTER_IP); String[] results = new String[cmd.getRules().length]; @@ -155,6 +193,7 @@ public class VirtualRoutingResource implements Manager { String routerIp = cmd.getAccessDetail(NetworkElementCommand.ROUTER_IP); String[] results = new String[cmd.getRules().length]; int i = 0; + boolean endResult = true; for (StaticNatRuleTO rule : cmd.getRules()) { String result = null; final Script command = new Script(_firewallPath, _timeout, s_logger); @@ -169,10 +208,15 @@ public class VirtualRoutingResource implements Manager { command.add(" -G ") ; result = command.execute(); - results[i++] = (!(result == null || result.isEmpty())) ? "Failed" : null; + if (result == null || result.isEmpty()) { + results[i++] = "Failed"; + endResult = false; + } else { + results[i++] = null; + } } - return new SetStaticNatRulesAnswer(cmd, results); + return new SetStaticNatRulesAnswer(cmd, results, endResult); } diff --git a/core/src/com/cloud/hypervisor/xen/resource/CitrixResourceBase.java b/core/src/com/cloud/hypervisor/xen/resource/CitrixResourceBase.java index 48221a511f3..cb59e9df8c8 100644 --- a/core/src/com/cloud/hypervisor/xen/resource/CitrixResourceBase.java +++ b/core/src/com/cloud/hypervisor/xen/resource/CitrixResourceBase.java @@ -156,7 +156,6 @@ import com.cloud.agent.api.storage.CreatePrivateTemplateAnswer; import com.cloud.agent.api.storage.DestroyCommand; import com.cloud.agent.api.storage.PrimaryStorageDownloadAnswer; import com.cloud.agent.api.storage.PrimaryStorageDownloadCommand; -import com.cloud.agent.api.to.FirewallRuleTO; import com.cloud.agent.api.to.IpAddressTO; import com.cloud.agent.api.to.NicTO; import com.cloud.agent.api.to.PortForwardingRuleTO; @@ -1270,6 +1269,7 @@ public abstract class CitrixResourceBase implements ServerResource, HypervisorRe //String args = routerIp; String[] results = new String[cmd.getRules().length]; int i = 0; + boolean endResult = true; for (StaticNatRuleTO rule : cmd.getRules()) { //1:1 NAT needs instanceip;publicip;domrip;op StringBuilder args = new StringBuilder(); @@ -1277,15 +1277,23 @@ public abstract class CitrixResourceBase implements ServerResource, HypervisorRe args.append(rule.revoked() ? " -D " : " -A "); args.append(" -l ").append(rule.getSrcIp()); args.append(" -r ").append(rule.getDstIp()); + if (rule.getProtocol() != null) { args.append(" -P ").append(rule.getProtocol().toLowerCase()); + } + args.append(" -d ").append(rule.getStringSrcPortRange()); args.append(" -G "); String result = callHostPlugin(conn, "vmops", "setFirewallRule", "args", args.toString()); - results[i++] = (result == null || result.isEmpty()) ? "Failed" : null; + if (result == null || result.isEmpty()) { + results[i++] = "Failed"; + endResult = false; + } else { + results[i++] = null; + } } - return new SetStaticNatRulesAnswer(cmd, results); + return new SetStaticNatRulesAnswer(cmd, results, endResult); } protected Answer execute(final LoadBalancerConfigCommand cmd) { @@ -6456,16 +6464,36 @@ public abstract class CitrixResourceBase implements ServerResource, HypervisorRe } protected SetFirewallRulesAnswer execute(SetFirewallRulesCommand cmd) { + String[] results = new String[cmd.getRules().length]; + String callResult; Connection conn = getConnection(); - String routerIp = cmd.getAccessDetail(NetworkElementCommand.ROUTER_IP); - String[] results = new String[cmd.getRules().length]; - int i = 0; - for (FirewallRuleTO rule : cmd.getRules()) { - //FIXME - Jana, add implementation here + + if (routerIp == null) { + return new SetFirewallRulesAnswer(cmd, false, results); } - return new SetFirewallRulesAnswer(cmd, results); + String[][] rules = cmd.generateFwRules(); + String args = ""; + args += routerIp + " -F "; + StringBuilder sb = new StringBuilder(); + String[] fwRules = rules[0]; + if (fwRules.length > 0) { + for (int i = 0; i < fwRules.length; i++) { + sb.append(fwRules[i]).append(','); + } + args += " -a " + sb.toString(); } + callResult = callHostPlugin(conn, "vmops", "setFirewallRule", "args", args); + + if (callResult == null || callResult.isEmpty()) { + //FIXME - in the future we have to process each rule separately; now we temporarily set every rule to be false if single rule fails + for (int i=0; i < results.length; i++) { + results[i] = "Failed"; + } + return new SetFirewallRulesAnswer(cmd, false, results); + } + return new SetFirewallRulesAnswer(cmd, true, results); + } } diff --git a/patches/systemvm/debian/config/root/firewall.sh b/patches/systemvm/debian/config/root/firewall.sh index 84cbab75d5f..a4b60b55378 100755 --- a/patches/systemvm/debian/config/root/firewall.sh +++ b/patches/systemvm/debian/config/root/firewall.sh @@ -138,6 +138,56 @@ one_to_one_fw_entry() { return $result } +fw_chain_for_ip() { + local pubIp=$1 + if iptables -t mangle -N FIREWALL_$pubIp &> /dev/null + then + logger -t cloud "created a fw chain for $pubIp to DROP by default" + (sudo iptables -t mangle -A FIREWALL_$pubIp -j DROP) && + (sudo iptables -t mangle -I FIREWALL_$pubIp -m state --state RELATED,ESTABLISHED -j ACCEPT ) && + (sudo iptables -t mangle -I PREROUTING -d $pubIp -j FIREWALL_$pubIp) + return $? + fi + logger -t cloud "fw chain for $pubIp already exists" + return 0 +} + +static_nat() { + local publicIp=$1 + local instIp=$2 + local op=$3 + local op2="-D" + local rulenum= + logger -t cloud "$(basename $0): static nat: public ip=$publicIp \ + instance ip=$instIp op=$op" + + #TODO check error below + fw_chain_for_ip $publicIp + + #if adding, this might be a duplicate, so delete the old one first + [ "$op" == "-A" ] && static_nat $publicIp $instIp "-D" + # the delete operation may have errored out but the only possible reason is + # that the rules didn't exist in the first place + [ "$op" == "-A" ] && rulenum=1 + [ "$op" == "-A" ] && op2="-I" + + local dev=$(ip_to_dev $publicIp) + [ $? -ne 0 ] && echo "Could not find device associated with $publicIp" && return 1 + + # shortcircuit the process if error and it is an append operation + # continue if it is delete + (sudo iptables -t nat $op PREROUTING -i $dev -d $publicIp -j DNAT \ + --to-destination $instIp &>> $OUTFILE || [ "$op" == "-D" ]) && + (sudo iptables $op FORWARD -i $dev -o eth0 -d $instIp -m state \ + --state NEW -j ACCEPT &>> $OUTFILE || [ "$op" == "-D" ]) && + (sudo iptables -t nat $op2 POSTROUTING $rulenum -s $instIp -j SNAT \ + --to-source $publicIp &>> $OUTFILE || [ "$op" == "-D" ]) + + result=$? + logger -t cloud "$(basename $0): done static nat entry public ip=$publicIp op=$op result=$result" + return $result +} + rflag= @@ -192,7 +242,12 @@ OUTFILE=$(mktemp) #Firewall ports for one-to-one/static NAT if [ "$Gflag" == "1" ] then - one_to_one_fw_entry $publicIp $instanceIp $protocol $dport $op + if [ "$protocol" == "" ] + then + static_nat $publicIp $instanceIp $op + else + one_to_one_fw_entry $publicIp $instanceIp $protocol $dport $op + fi result=$? [ "$result" -ne 0 ] && cat $OUTFILE >&2 rm -f $OUTFILE diff --git a/patches/systemvm/debian/config/root/firewall_rule.sh b/patches/systemvm/debian/config/root/firewall_rule.sh new file mode 100755 index 00000000000..f0f8c0c1e2d --- /dev/null +++ b/patches/systemvm/debian/config/root/firewall_rule.sh @@ -0,0 +1,186 @@ +#!/usr/bin/env bash +# +# Copyright (C) 2010 Cloud.com, Inc. All rights reserved. +# +# This software is licensed under the GNU General Public License v3 or later. +# +# It is free software: you can redistribute it and/or modify +# it under the terms of the GNU General Public License as published by +# the Free Software Foundation, either version 3 of the License, or any later version. +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with this program. If not, see . +# +# firewall_rule.sh -- allow some ports / protocols to vm instances +# +# +# @VERSION@ + +usage() { + printf "Usage: %s: -a \n" $(basename $0) >&2 + printf "sourcecidrs format: cidr1-cidr2-cidr3-...\n" +} +#set -x +#FIXME: eating up the error code during execution of iptables +fw_remove_backup() { + local pubIp=$1 + sudo iptables -t mangle -F _FIREWALL_$pubIp 2> /dev/null + sudo iptables -t mangle -D PREROUTING -j _FIREWALL_$pubIp -d $pubIp 2> /dev/null + sudo iptables -t mangle -X _FIREWALL_$pubIp 2> /dev/null +} + +fw_restore() { + local pubIp=$1 + sudo iptables -t mangle -F FIREWALL_$pubIp 2> /dev/null + sudo iptables -t mangle -D PREROUTING -j FIREWALL_$pubIp -d $pubIp 2> /dev/null + sudo iptables -t mangle -X FIREWALL_$pubIp 2> /dev/null + sudo iptables -t mangle -E _FIREWALL_$pubIp FIREWALL_$pubIp 2> /dev/null +} +fw_chain_for_ip () { + local pubIp=$1 + fw_remove_backup $1 + sudo iptables -t mangle -E FIREWALL_$pubIp _FIREWALL_$pubIp 2> /dev/null + sudo iptables -t mangle -N FIREWALL_$pubIp 2> /dev/null + # drop if no rules match (this will be the last rule in the chain) + sudo iptables -t mangle -A FIREWALL_$pubIp -j DROP> /dev/null + # ensure outgoing connections are maintained (first rule in chain) + sudo iptables -t mangle -I FIREWALL_$pubIp -m state --state RELATED,ESTABLISHED -j ACCEPT> /dev/null + sudo iptables -t mangle -I PREROUTING -d $pubIp -j FIREWALL_$pubIp +} + +fw_entry_for_public_ip() { + local rules=$1 + + local pubIp=$(echo $rules | cut -d: -f1) + local prot=$(echo $rules | cut -d: -f2) + local sport=$(echo $rules | cut -d: -f3) + local eport=$(echo $rules | cut -d: -f4) + local scidrs=$(echo $rules | cut -d: -f5 | sed 's/-/ /g') + + logger -t cloud "$(basename $0): enter apply firewall rules for public ip $pubIp:$prot:$sport:$eport:$scidrs" + + + # note that rules are inserted after the RELATED,ESTABLISHED rule + # but before the DROP rule + for src in $scidrs + do + [ "$prot" == "reverted" ] && continue; + if [ "$prot" == "icmp" ] + then + typecode="$sport/$eport" + [ "$eport" == "-1" ] && typecode="$sport" + [ "$sport" == "-1" ] && typecode="any" + sudo iptables -t mangle -I FIREWALL_$pubIp 2 -s $src -p $prot \ + --icmp-type $typecode -j RETURN + else + sudo iptables -t mangle -I FIREWALL_$pubIp 2 -s $src -p $prot \ + --dport $sport:$eport -j RETURN + fi + result=$? + [ $result -gt 0 ] && + logger -t cloud "Error adding iptables entry for $pubIp:$prot:$sport:$eport:$src" && + break + done + + logger -t cloud "$(basename $0): exit apply firewall rules for public ip $pubIp" + return $result +} + +get_vif_list() { + local vif_list="" + for i in /sys/class/net/eth*; do + vif=$(basename $i); + if [ "$vif" != "eth0" ] && [ "$vif" != "eth1" ] + then + vif_list="$vif_list $vif"; + fi + done + if [ "$vif_list" == "" ] + then + vif_list="eth0" + fi + + logger -t cloud "FirewallRule public interfaces = $vif_list" + echo $vif_list +} + +shift +rules= +while getopts 'a:' OPTION +do + case $OPTION in + a) aflag=1 + rules="$OPTARG" + ;; + ?) usage + exit 2 + ;; + esac +done + +VIF_LIST=$(get_vif_list) + +if [ "$rules" == "" ] +then + rules="none" +fi + +#-a 172.16.92.44:tcp:80:80:0.0.0.0/0:,172.16.92.44:tcp:220:220:0.0.0.0/0:,172.16.92.44:tcp:222:222:192.168.10.0/24-75.57.23.0/22-88.100.33.1/32 +# if any entry is reverted , entry will be in the format :reverted:0:0:0 +# example : 172.16.92.44:tcp:80:80:0.0.0.0/0:,172.16.92.44:tcp:220:220:0.0.0.0/0:,200.1.1.2:reverted:0:0:0 +# The reverted entries will fix the following partially +#FIXME: rule leak: when there are multiple ip address, there will chance that entry will be left over if the ipadress does not appear in the current execution when compare to old one +# example : In the below first transaction have 2 ip's whereas in second transaction it having one ip, so after the second trasaction 200.1.2.3 ip will have rules in mangle table. +# 1) -a 172.16.92.44:tcp:80:80:0.0.0.0/0:,200.16.92.44:tcp:220:220:0.0.0.0/0:, +# 2) -a 172.16.92.44:tcp:80:80:0.0.0.0/0:,172.16.92.44:tcp:220:220:0.0.0.0/0:, + + +success=0 +publicIps= +rules_list=$(echo $rules | cut -d, -f1- --output-delimiter=" ") +for r in $rules_list +do + pubIp=$(echo $r | cut -d: -f1) + publicIps="$pubIp $publicIps" +done + +unique_ips=$(echo $publicIps| tr " " "\n" | sort | uniq | tr "\n" " ") + +for u in $unique_ips +do + fw_chain_for_ip $u +done + +for r in $rules_list +do + pubIp=$(echo $r | cut -d: -f1) + fw_entry_for_public_ip $r + success=$? + if [ $success -gt 0 ] + then + logger -t cloud "$(basename $0): failure to apply fw rules for ip $pubIp" + break + else + logger -t cloud "$(basename $0): successful in applying fw rules for ip $pubIp" + fi +done + +if [ $success -gt 0 ] +then + for p in $unique_ips + do + logger -t cloud "$(basename $0): restoring from backup for ip: $p" + fw_restore $p + done +fi +for p in $unique_ips +do + logger -t cloud "$(basename $0): deleting backup for ip: $p" + fw_remove_backup $p +done +exit $success + diff --git a/scripts/network/domr/call_firewall.sh b/scripts/network/domr/call_firewall.sh index 29765e657d9..1e4dc939eaf 100755 --- a/scripts/network/domr/call_firewall.sh +++ b/scripts/network/domr/call_firewall.sh @@ -24,12 +24,12 @@ # firewall.sh -- allow some ports / protocols to vm instances # # - usage() { - printf "Usage: %s: (-A|-D) -i -r -P protocol (-p port_range | -t icmp_type_code) -l -d [-f -u -y -z ] \n" $(basename $0) >&2 + printf "Usage for Firewall rule : %s: -F " $(basename $0) >&2 + printf "Usage for other purposes : %s: (-A|-D) -i -r -P protocol (-p port_range | -t icmp_type_code) -l -d [-f -u -y -z ] \n" $(basename $0) >&2 } -# set -x +#set -x # check if gateway domain is up and running check_gw() { @@ -52,9 +52,22 @@ if [ $? -gt 0 ] then exit 1 fi +fflag= +while getopts ':F' OPTION +do + case $OPTION in + F) fflag=1 + ;; + \?) ;; + esac +done - -ssh -p 3922 -q -o StrictHostKeyChecking=no -i $cert root@$domRIp "/root/firewall.sh $*" +if [ -n "$fflag" ] +then + ssh -p 3922 -q -o StrictHostKeyChecking=no -i $cert root@$domRIp "/root/firewall_rule.sh $*" +else + ssh -p 3922 -q -o StrictHostKeyChecking=no -i $cert root@$domRIp "/root/firewall.sh $*" +fi exit $? diff --git a/server/src/com/cloud/network/NetworkManager.java b/server/src/com/cloud/network/NetworkManager.java index c46f2b6728e..09fa13e6c56 100644 --- a/server/src/com/cloud/network/NetworkManager.java +++ b/server/src/com/cloud/network/NetworkManager.java @@ -35,6 +35,7 @@ import com.cloud.network.Networks.TrafficType; import com.cloud.network.addr.PublicIp; import com.cloud.network.guru.NetworkGuru; import com.cloud.network.rules.FirewallRule; +import com.cloud.network.rules.StaticNat; import com.cloud.network.vpn.PasswordResetElement; import com.cloud.network.vpn.RemoteAccessVpnElement; import com.cloud.offerings.NetworkOfferingVO; @@ -210,4 +211,8 @@ public interface NetworkManager extends NetworkService { String getGlobalGuestDomainSuffix(); String getStartIpAddress(long networkId); + + boolean applyStaticNats(List staticNats, boolean continueOnError) throws ResourceUnavailableException; + + String getIpInNetwork(long vmId, long networkId); } diff --git a/server/src/com/cloud/network/NetworkManagerImpl.java b/server/src/com/cloud/network/NetworkManagerImpl.java index e30225f3a69..019bf065c6a 100755 --- a/server/src/com/cloud/network/NetworkManagerImpl.java +++ b/server/src/com/cloud/network/NetworkManagerImpl.java @@ -102,6 +102,7 @@ import com.cloud.network.guru.NetworkGuru; import com.cloud.network.lb.LoadBalancingRulesManager; import com.cloud.network.rules.FirewallManager; import com.cloud.network.rules.FirewallRule; +import com.cloud.network.rules.StaticNat; import com.cloud.network.rules.FirewallRule.Purpose; import com.cloud.network.rules.FirewallRuleVO; import com.cloud.network.rules.RulesManager; @@ -2431,6 +2432,12 @@ public class NetworkManagerImpl implements NetworkManager, NetworkService, Manag success = false; } + // apply static nat + if (!_rulesMgr.applyStaticNatsForNetwork(networkId, false, caller)) { + s_logger.warn("Failed to apply static nats a part of network id" + networkId + " restart"); + success = false; + } + // apply firewall rules List firewallRulesToApply = _firewallDao.listByNetworkAndPurpose(networkId, Purpose.Firewall); if (!_firewallMgr.applyFirewallRules(firewallRulesToApply, false, caller)) { @@ -2631,6 +2638,13 @@ public class NetworkManagerImpl implements NetworkManager, NetworkService, Manag return _nicDao.findByInstanceIdAndNetworkId(networkId, vmId); } + @Override + public String getIpInNetwork(long vmId, long networkId) { + Nic guestNic = getNicInNetwork(vmId, networkId); + assert (guestNic != null && guestNic.getIp4Address() != null) : "Vm doesn't belong to network associated with ipAddress or ip4 address is null...how is it possible?"; + return guestNic.getIp4Address(); + } + @Override public Nic getNicInNetworkIncludingRemoved(long vmId, long networkId) { return _nicDao.findByInstanceIdAndNetworkIdIncludingRemoved(networkId, vmId); @@ -3190,4 +3204,29 @@ public class NetworkManagerImpl implements NetworkManager, NetworkService, Manag return startIP; } + + @Override + public boolean applyStaticNats(List staticNats, boolean continueOnError) throws ResourceUnavailableException { + if (staticNats == null || staticNats.size() == 0) { + s_logger.debug("There are no static nat rules for the network elements"); + return true; + } + + boolean success = true; + Network network = _networksDao.findById(staticNats.get(0).getNetworkId()); + for (NetworkElement ne : _networkElements) { + try { + boolean handled = ne.applyStaticNats(network, staticNats); + s_logger.debug("Static Nat for network " + network.getId() + " were " + (handled ? "" : " not") + " handled by " + ne.getName()); + } catch (ResourceUnavailableException e) { + if (!continueOnError) { + throw e; + } + s_logger.warn("Problems with " + ne.getName() + " but pushing on", e); + success = false; + } + } + + return success; + } } diff --git a/server/src/com/cloud/network/element/DhcpElement.java b/server/src/com/cloud/network/element/DhcpElement.java index a164b0f54d5..42efc838cad 100644 --- a/server/src/com/cloud/network/element/DhcpElement.java +++ b/server/src/com/cloud/network/element/DhcpElement.java @@ -45,6 +45,7 @@ import com.cloud.network.dao.NetworkDao; import com.cloud.network.router.VirtualNetworkApplianceManager; import com.cloud.network.router.VirtualRouter; import com.cloud.network.rules.FirewallRule; +import com.cloud.network.rules.StaticNat; import com.cloud.network.vpn.PasswordResetElement; import com.cloud.offering.NetworkOffering; import com.cloud.user.AccountManager; @@ -120,7 +121,17 @@ public class DhcpElement extends AdapterBase implements NetworkElement, Password VirtualMachineProfile uservm = (VirtualMachineProfile)vm; Map params = new HashMap(1); params.put(VirtualMachineProfile.Param.RestartNetwork, true); - List routers = _routerMgr.deployDhcp(network, dest, _accountMgr.getAccount(network.getAccountId()), uservm.getParameters()); + List routers = _routerMgr.deployDhcp(network, dest, _accountMgr.getAccount(network.getAccountId()), uservm.getParameters()); + + //for Basic zone, add all Running routers - we have to send Dhcp/vmData/password info to them when network.dns.basiczone.updates is set to "all" + Long podId = dest.getPod().getId(); + DataCenter dc = dest.getDataCenter(); + boolean isPodBased = (dc.getNetworkType() == NetworkType.Basic || network.isSecurityGroupEnabled()) && network.getTrafficType() == TrafficType.Guest; + if (isPodBased && _routerMgr.getDnsBasicZoneUpdate().equalsIgnoreCase("all")) { + List allRunningRoutersOutsideThePod = _routerDao.findByNetworkOutsideThePod(network.getId(), podId, State.Running); + routers.addAll(allRunningRoutersOutsideThePod); + } + List rets = _routerMgr.addVirtualMachineIntoNetwork(network, nic, uservm, dest, context, routers); return (rets != null) && (!rets.isEmpty()); } else { @@ -235,4 +246,9 @@ public class DhcpElement extends AdapterBase implements NetworkElement, Password return _routerMgr.savePasswordToRouter(network, nic, uservm); } + + @Override + public boolean applyStaticNats(Network config, List rules) throws ResourceUnavailableException { + return false; + } } diff --git a/server/src/com/cloud/network/element/ElasticLoadBalancerElement.java b/server/src/com/cloud/network/element/ElasticLoadBalancerElement.java index 63824448330..c096325814d 100644 --- a/server/src/com/cloud/network/element/ElasticLoadBalancerElement.java +++ b/server/src/com/cloud/network/element/ElasticLoadBalancerElement.java @@ -42,6 +42,7 @@ import com.cloud.network.PublicIpAddress; import com.cloud.network.dao.NetworkDao; import com.cloud.network.lb.ElasticLoadBalancerManager; import com.cloud.network.rules.FirewallRule; +import com.cloud.network.rules.StaticNat; import com.cloud.offering.NetworkOffering; import com.cloud.offerings.dao.NetworkOfferingDao; import com.cloud.utils.component.AdapterBase; @@ -166,4 +167,9 @@ public class ElasticLoadBalancerElement extends AdapterBase implements NetworkEl } return true; } + + @Override + public boolean applyStaticNats(Network config, List rules) throws ResourceUnavailableException { + return false; + } } diff --git a/server/src/com/cloud/network/element/OvsElement.java b/server/src/com/cloud/network/element/OvsElement.java index ed51c0291bc..4cfe1ef50e2 100644 --- a/server/src/com/cloud/network/element/OvsElement.java +++ b/server/src/com/cloud/network/element/OvsElement.java @@ -38,6 +38,7 @@ import com.cloud.network.element.NetworkElement; import com.cloud.network.ovs.OvsNetworkManager; import com.cloud.network.ovs.OvsTunnelManager; import com.cloud.network.rules.FirewallRule; +import com.cloud.network.rules.StaticNat; import com.cloud.offering.NetworkOffering; import com.cloud.utils.component.AdapterBase; import com.cloud.utils.component.Inject; @@ -142,5 +143,10 @@ public class OvsElement extends AdapterBase implements NetworkElement { throws ConcurrentOperationException, ResourceUnavailableException { return true; } + + @Override + public boolean applyStaticNats(Network config, List rules) throws ResourceUnavailableException { + return false; + } } diff --git a/server/src/com/cloud/network/element/VirtualRouterElement.java b/server/src/com/cloud/network/element/VirtualRouterElement.java index d4bfa290c99..f7fe4354b53 100644 --- a/server/src/com/cloud/network/element/VirtualRouterElement.java +++ b/server/src/com/cloud/network/element/VirtualRouterElement.java @@ -49,6 +49,7 @@ import com.cloud.network.router.VirtualNetworkApplianceManager; import com.cloud.network.router.VirtualRouter; import com.cloud.network.rules.FirewallRule; import com.cloud.network.rules.RulesManager; +import com.cloud.network.rules.StaticNat; import com.cloud.network.vpn.RemoteAccessVpnElement; import com.cloud.offering.NetworkOffering; import com.cloud.offerings.dao.NetworkOfferingDao; @@ -284,4 +285,21 @@ public class VirtualRouterElement extends DhcpElement implements NetworkElement, return capabilities; } + + @Override + public boolean applyStaticNats(Network config, List rules) throws ResourceUnavailableException { + DataCenter dc = _configMgr.getZone(config.getDataCenterId()); + if (canHandle(config.getGuestType(),dc)) { + long networkId = config.getId(); + List routers = _routerDao.findByNetwork(networkId); + if (routers == null || routers.isEmpty()) { + s_logger.debug("Virtual router elemnt doesn't need to apply static nat on the backend; virtual router doesn't exist in the network " + config.getId()); + return true; + } + + return _routerMgr.applyStaticNats(config, rules); + } else { + return true; + } + } } diff --git a/server/src/com/cloud/network/firewall/FirewallManagerImpl.java b/server/src/com/cloud/network/firewall/FirewallManagerImpl.java index f02a29c73ca..db58171f0d3 100644 --- a/server/src/com/cloud/network/firewall/FirewallManagerImpl.java +++ b/server/src/com/cloud/network/firewall/FirewallManagerImpl.java @@ -10,6 +10,8 @@ import javax.naming.ConfigurationException; import org.apache.log4j.Logger; import com.cloud.api.commands.ListFirewallRulesCmd; +import com.cloud.configuration.Config; +import com.cloud.configuration.dao.ConfigurationDao; import com.cloud.domain.Domain; import com.cloud.domain.DomainVO; import com.cloud.domain.dao.DomainDao; @@ -72,6 +74,10 @@ public class FirewallManagerImpl implements FirewallService, FirewallManager, Ma NetworkManager _networkMgr; @Inject UsageEventDao _usageEventDao; + @Inject + ConfigurationDao _configDao; + + private boolean _elbEnabled=false; @Override @@ -92,6 +98,8 @@ public class FirewallManagerImpl implements FirewallService, FirewallManager, Ma @Override public boolean configure(String name, Map params) throws ConfigurationException { _name = name; + String elbEnabledString = _configDao.getValue(Config.ElasticLoadBalancerEnabled.key()); + _elbEnabled = Boolean.parseBoolean(elbEnabledString); return true; } @@ -111,16 +119,19 @@ public class FirewallManagerImpl implements FirewallService, FirewallManager, Ma // Validate ip address if (ipAddress == null) { throw new InvalidParameterValueException("Unable to create port forwarding rule; ip id=" + ipAddrId + " doesn't exist in the system"); - } else if (ipAddress.isOneToOneNat()) { - throw new InvalidParameterValueException("Unable to create port forwarding rule; ip id=" + ipAddrId + " has static nat enabled"); } - validateFirewallRule(caller, ipAddress, portStart, portEnd, protocol); + validateFirewallRule(caller, ipAddress, portStart, portEnd, protocol, Purpose.Firewall); + //icmp code and icmp type can't be passed in for any other protocol rather than icmp if (!protocol.equalsIgnoreCase(NetUtils.ICMP_PROTO) && (icmpCode != null || icmpType != null)) { throw new InvalidParameterValueException("Can specify icmpCode and icmpType for ICMP protocol only"); } + if (protocol.equalsIgnoreCase(NetUtils.ICMP_PROTO) && (portStart != null || portEnd != null)) { + throw new InvalidParameterValueException("Can't specify start/end port when protocol is ICMP"); + } + Long networkId = ipAddress.getAssociatedWithNetworkId(); Long accountId = ipAddress.getAccountId(); Long domainId = ipAddress.getDomainId(); @@ -215,25 +226,26 @@ public class FirewallManagerImpl implements FirewallService, FirewallManager, Ma List rules = _firewallDao.listByIpAndPurposeAndNotRevoked(newRule.getSourceIpAddressId(), null); assert (rules.size() >= 1) : "For network rules, we now always first persist the rule and then check for network conflicts so we should at least have one rule at this point."; - - for (FirewallRuleVO rule : rules) { if (rule.getId() == newRule.getId()) { continue; // Skips my own rule. } + + boolean allowFirewall = ((rule.getPurpose() == Purpose.Firewall || newRule.getPurpose() == Purpose.Firewall) && newRule.getPurpose() != rule.getPurpose()); + + if (!allowFirewall) { if (rule.getPurpose() == Purpose.StaticNat && newRule.getPurpose() != Purpose.StaticNat) { throw new NetworkRuleConflictException("There is 1 to 1 Nat rule specified for the ip address id=" + newRule.getSourceIpAddressId()); } else if (rule.getPurpose() != Purpose.StaticNat && newRule.getPurpose() == Purpose.StaticNat) { throw new NetworkRuleConflictException("There is already firewall rule specified for the ip address id=" + newRule.getSourceIpAddressId()); } + } if (rule.getNetworkId() != newRule.getNetworkId() && rule.getState() != State.Revoke) { throw new NetworkRuleConflictException("New rule is for a different network than what's specified in rule " + rule.getXid()); } - boolean allowFirewall = ((rule.getPurpose() == Purpose.Firewall || newRule.getPurpose() == Purpose.Firewall) && newRule.getPurpose() != rule.getPurpose()); - boolean notNullPorts = (newRule.getSourcePortStart() != null && newRule.getSourcePortEnd() != null && rule.getSourcePortStart() != null && rule.getSourcePortEnd() != null); if (!allowFirewall && notNullPorts && ((rule.getSourcePortStart() <= newRule.getSourcePortStart() && rule.getSourcePortEnd() >= newRule.getSourcePortStart()) || (rule.getSourcePortStart() <= newRule.getSourcePortEnd() && rule.getSourcePortEnd() >= newRule.getSourcePortEnd()) @@ -251,15 +263,12 @@ public class FirewallManagerImpl implements FirewallService, FirewallManager, Ma } if (newRule.getProtocol().equalsIgnoreCase(NetUtils.ICMP_PROTO) && newRule.getProtocol().equalsIgnoreCase(rule.getProtocol())) { - if (newRule.getIcmpCode().longValue() == rule.getIcmpCode().longValue() || newRule.getIcmpType().longValue() == rule.getIcmpType().longValue() || newRule.getProtocol().equalsIgnoreCase(rule.getProtocol())) { + if (newRule.getIcmpCode().longValue() == rule.getIcmpCode().longValue() && newRule.getIcmpType().longValue() == rule.getIcmpType().longValue() && newRule.getProtocol().equalsIgnoreCase(rule.getProtocol())) { throw new InvalidParameterValueException("New rule conflicts with existing rule id=" + rule.getId()); } - } } - - if (s_logger.isDebugEnabled()) { s_logger.debug("No network rule conflicts detected for " + newRule + " against " + (rules.size() - 1) + " existing rules"); } @@ -267,7 +276,7 @@ public class FirewallManagerImpl implements FirewallService, FirewallManager, Ma @Override - public void validateFirewallRule(Account caller, IPAddressVO ipAddress, Integer portStart, Integer portEnd, String proto) { + public void validateFirewallRule(Account caller, IPAddressVO ipAddress, Integer portStart, Integer portEnd, String proto, Purpose purpose) { // Validate ip address _accountMgr.checkAccess(caller, ipAddress); @@ -293,10 +302,23 @@ public class FirewallManagerImpl implements FirewallService, FirewallManager, Ma } // Verify that the network guru supports the protocol specified - Map firewallCapabilities = _networkMgr.getServiceCapabilities(network.getDataCenterId(), network.getNetworkOfferingId(), Service.Firewall); - String supportedProtocols = firewallCapabilities.get(Capability.SupportedProtocols).toLowerCase(); + Map protocolCapabilities = null; + + if (purpose == Purpose.LoadBalancing) { + if (!_elbEnabled) { + protocolCapabilities = _networkMgr.getServiceCapabilities(network.getDataCenterId(), network.getNetworkOfferingId(), Service.Lb); + } + } else { + protocolCapabilities = _networkMgr.getServiceCapabilities(network.getDataCenterId(), network.getNetworkOfferingId(), Service.Firewall); + } + + if (protocolCapabilities != null) { + String supportedProtocols = protocolCapabilities.get(Capability.SupportedProtocols).toLowerCase(); if (!supportedProtocols.contains(proto.toLowerCase())) { throw new InvalidParameterValueException("Protocol " + proto + " is not supported in zone " + network.getDataCenterId()); + } else if (proto.equalsIgnoreCase(NetUtils.ICMP_PROTO) && purpose != Purpose.Firewall) { + throw new InvalidParameterValueException("Protocol " + proto + " is currently supported only for rules with purpose " + Purpose.Firewall); + } } } @@ -496,4 +518,5 @@ public class FirewallManagerImpl implements FirewallService, FirewallManager, Ma return rules.size() == 0; } + } diff --git a/server/src/com/cloud/network/lb/ElasticLoadBalancerManagerImpl.java b/server/src/com/cloud/network/lb/ElasticLoadBalancerManagerImpl.java index e97dbfaec55..61b01f3d2a3 100644 --- a/server/src/com/cloud/network/lb/ElasticLoadBalancerManagerImpl.java +++ b/server/src/com/cloud/network/lb/ElasticLoadBalancerManagerImpl.java @@ -293,6 +293,13 @@ public class ElasticLoadBalancerManagerImpl implements elbVm.getPrivateIpAddress()); cmd.setAccessDetail(NetworkElementCommand.ROUTER_NAME, elbVm.getInstanceName()); + //FIXME: why are we setting attributes directly? Ick!! There should be accessors and + //the constructor should set defaults. + cmd.lbStatsVisibility = _configDao.getValue(Config.NetworkLBHaproxyStatsVisbility.key()); + cmd.lbStatsUri = _configDao.getValue(Config.NetworkLBHaproxyStatsUri.key()); + cmd.lbStatsAuth = _configDao.getValue(Config.NetworkLBHaproxyStatsAuth.key()); + cmd.lbStatsPort = _configDao.getValue(Config.NetworkLBHaproxyStatsPort.key()); + cmd.lbStatsIp = elbVm.getGuestIpAddress(); cmds.addCommand(cmd); } diff --git a/server/src/com/cloud/network/lb/LoadBalancingRulesManagerImpl.java b/server/src/com/cloud/network/lb/LoadBalancingRulesManagerImpl.java index 41bce78d820..aeeaf3ef8a8 100755 --- a/server/src/com/cloud/network/lb/LoadBalancingRulesManagerImpl.java +++ b/server/src/com/cloud/network/lb/LoadBalancingRulesManagerImpl.java @@ -396,7 +396,7 @@ public class LoadBalancingRulesManagerImpl implements LoadBalancingRulesManager, throw new InvalidParameterValueException("Unable to create load balancer rule, invalid IP address id" + ipId); } - _firewallMgr.validateFirewallRule(caller.getCaller(), ipAddr, srcPortStart, srcPortEnd, lb.getProtocol()); + _firewallMgr.validateFirewallRule(caller.getCaller(), ipAddr, srcPortStart, srcPortEnd, lb.getProtocol(), Purpose.LoadBalancing); networkId = ipAddr.getAssociatedWithNetworkId(); diff --git a/server/src/com/cloud/network/router/VirtualNetworkApplianceManager.java b/server/src/com/cloud/network/router/VirtualNetworkApplianceManager.java index c4d41d70cec..dbae1f1e4a3 100644 --- a/server/src/com/cloud/network/router/VirtualNetworkApplianceManager.java +++ b/server/src/com/cloud/network/router/VirtualNetworkApplianceManager.java @@ -30,6 +30,7 @@ import com.cloud.network.RemoteAccessVpn; import com.cloud.network.VirtualNetworkApplianceService; import com.cloud.network.VpnUser; import com.cloud.network.rules.FirewallRule; +import com.cloud.network.rules.StaticNat; import com.cloud.user.Account; import com.cloud.user.User; import com.cloud.uservm.UserVm; @@ -72,7 +73,6 @@ public interface VirtualNetworkApplianceManager extends Manager, VirtualNetworkA List deployDhcp(Network guestNetwork, DeployDestination dest, Account owner, Map params) throws InsufficientCapacityException, ResourceUnavailableException, ConcurrentOperationException; - List addVirtualMachineIntoNetwork(Network config, NicProfile nic, VirtualMachineProfile vm, DeployDestination dest, ReservationContext context, List routers) throws ConcurrentOperationException, InsufficientCapacityException, ResourceUnavailableException; boolean startRemoteAccessVpn(Network network, RemoteAccessVpn vpn) throws ResourceUnavailableException; @@ -88,4 +88,9 @@ public interface VirtualNetworkApplianceManager extends Manager, VirtualNetworkA String[] applyVpnUsers(Network network, List users) throws ResourceUnavailableException; VirtualRouter stop(VirtualRouter router, boolean forced, User callingUser, Account callingAccount) throws ConcurrentOperationException, ResourceUnavailableException; + + String getDnsBasicZoneUpdate(); + + boolean applyStaticNats(Network network, List rules) throws ResourceUnavailableException; + } diff --git a/server/src/com/cloud/network/router/VirtualNetworkApplianceManagerImpl.java b/server/src/com/cloud/network/router/VirtualNetworkApplianceManagerImpl.java index 03447e4667b..c4098b121f7 100755 --- a/server/src/com/cloud/network/router/VirtualNetworkApplianceManagerImpl.java +++ b/server/src/com/cloud/network/router/VirtualNetworkApplianceManagerImpl.java @@ -78,7 +78,6 @@ import com.cloud.dc.DataCenterVO; import com.cloud.dc.HostPodVO; import com.cloud.dc.dao.AccountVlanMapDao; import com.cloud.dc.dao.DataCenterDao; -import com.cloud.dc.dao.DcDetailsDaoImpl; import com.cloud.dc.dao.HostPodDao; import com.cloud.dc.dao.VlanDao; import com.cloud.deploy.DataCenterDeployment; @@ -136,6 +135,8 @@ import com.cloud.network.rules.FirewallRule; import com.cloud.network.rules.FirewallRule.Purpose; import com.cloud.network.rules.PortForwardingRule; import com.cloud.network.rules.RulesManager; +import com.cloud.network.rules.StaticNat; +import com.cloud.network.rules.StaticNatImpl; import com.cloud.network.rules.StaticNatRule; import com.cloud.network.rules.dao.PortForwardingRulesDao; import com.cloud.offering.NetworkOffering; @@ -1076,11 +1077,6 @@ public class VirtualNetworkApplianceManagerImpl implements VirtualNetworkApplian } if (!routers.isEmpty()) { - //for Basic zone, add all Running routers - we have to send Dhcp/vmData/password info to them when network.dns.basiczone.updates is set to "all" - if (isPodBased) { - List allRunningRoutersOutsideThePod = _routerDao.findByNetworkOutsideThePod(guestNetwork.getId(), podId, State.Running); - routers.addAll(allRunningRoutersOutsideThePod); - } return routers; } @@ -1136,6 +1132,7 @@ public class VirtualNetworkApplianceManagerImpl implements VirtualNetworkApplian _networkDao.releaseFromLockTable(network.getId()); } } + return routers; } @@ -1397,7 +1394,9 @@ public class VirtualNetworkApplianceManagerImpl implements VirtualNetworkApplian List vpns = new ArrayList(); List pfRules = new ArrayList(); List staticNatFirewallRules = new ArrayList(); + List staticNats = new ArrayList(); + //Get information about all the rules (StaticNats and StaticNatRules; PFVPN to reapply on domR start) for (PublicIpAddress ip : publicIps) { pfRules.addAll(_pfRulesDao.listForApplication(ip.getId())); staticNatFirewallRules.addAll(_rulesDao.listByIpAndPurpose(ip.getId(), Purpose.StaticNat)); @@ -1406,6 +1405,18 @@ public class VirtualNetworkApplianceManagerImpl implements VirtualNetworkApplian if (vpn != null) { vpns.add(vpn); } + + if (ip.isOneToOneNat()) { + String dstIp = _networkMgr.getIpInNetwork(ip.getAssociatedWithVmId(), networkId); + StaticNatImpl staticNat = new StaticNatImpl(ip.getAccountId(), ip.getDomainId(), networkId, ip.getId(), dstIp, false); + staticNats.add(staticNat); + } + } + + //Re-apply static nats + s_logger.debug("Found " + staticNats.size() + " static nat(s) to apply as a part of domR " + router + " start."); + if (!staticNats.isEmpty()) { + createApplyStaticNatCommands(staticNats, router, cmds); } // Re-apply port forwarding rules @@ -1596,20 +1607,26 @@ public class VirtualNetworkApplianceManagerImpl implements VirtualNetworkApplian List rets = new ArrayList(routers.size()); + boolean sendPasswordAndVmData = true; + boolean sendDnsDhcpData = true; + _userVmDao.loadDetails((UserVmVO) profile.getVirtualMachine()); + + DataCenter dc = dest.getDataCenter(); + String serviceOffering = _serviceOfferingDao.findByIdIncludingRemoved(profile.getServiceOfferingId()).getDisplayText(); + String zoneName = _dcDao.findById(network.getDataCenterId()).getName(); + boolean isZoneBasic = (dc.getNetworkType() == NetworkType.Basic); + for (DomainRouterVO router : routers) { if (router.getState() != State.Running) { s_logger.warn("Unable to add virtual machine " + profile.getVirtualMachine() + " to the router " + router + " as the router is not in Running state"); continue; } - boolean sendPasswordAndVmData = true; - boolean sendDnsDhcpData = true; - _userVmDao.loadDetails((UserVmVO) profile.getVirtualMachine()); //for basic zone: //1) send vm data/password information only to the dhcp in the same pod //2) send dhcp/dns information to all routers in the cloudstack only when _dnsBasicZoneUpdates is set to "all" value - DataCenter dc = dest.getDataCenter(); - if (dc.getNetworkType() == NetworkType.Basic) { + + if (isZoneBasic) { Long podId = dest.getPod().getId(); if (router.getPodIdToDeployIn().longValue() != podId.longValue()) { sendPasswordAndVmData = false; @@ -1658,8 +1675,7 @@ public class VirtualNetworkApplianceManagerImpl implements VirtualNetworkApplian cmds.addCommand("password", cmd); } - String serviceOffering = _serviceOfferingDao.findByIdIncludingRemoved(profile.getServiceOfferingId()).getDisplayText(); - String zoneName = _dcDao.findById(network.getDataCenterId()).getName(); + cmds.addCommand( "vmdata", @@ -2074,6 +2090,12 @@ public class VirtualNetworkApplianceManagerImpl implements VirtualNetworkApplian s_logger.warn("Unable to associate ip addresses, virtual router is not in the right state " + router.getState()); throw new ResourceUnavailableException("Unable to assign ip addresses, domR is not in right state " + router.getState(), DataCenter.class, network.getDataCenterId()); } + + //If rules fail to apply on one domR, no need to proceed with the rest + if (!result) { + throw new ResourceUnavailableException("Unable to apply firewall rules on router ", VirtualRouter.class, router.getId()); + } + } return result; } @@ -2082,8 +2104,8 @@ public class VirtualNetworkApplianceManagerImpl implements VirtualNetworkApplian public boolean applyFirewallRules(Network network, List rules) throws ResourceUnavailableException { List routers = _routerDao.findByNetwork(network.getId()); if (routers == null || routers.isEmpty()) { - s_logger.warn("Unable to apply lb rules, virtual router doesn't exist in the network " + network.getId()); - throw new ResourceUnavailableException("Unable to apply lb rules", DataCenter.class, network.getDataCenterId()); + s_logger.warn("Unable to apply firewall rules, virtual router doesn't exist in the network " + network.getId()); + throw new ResourceUnavailableException("Unable to apply firewall rules", DataCenter.class, network.getDataCenterId()); } boolean result = true; @@ -2111,6 +2133,12 @@ public class VirtualNetworkApplianceManagerImpl implements VirtualNetworkApplian result = false; } } + + //If rules fail to apply on one domR, no need to proceed with the rest + if (!result) { + throw new ResourceUnavailableException("Unable to apply firewall rules on router ", VirtualRouter.class, router.getId()); + } + } else if (router.getState() == State.Stopped || router.getState() == State.Stopping) { s_logger.debug("Router is in " + router.getState() + ", so not sending apply firewall rules commands to the backend"); } else { @@ -2118,6 +2146,7 @@ public class VirtualNetworkApplianceManagerImpl implements VirtualNetworkApplian throw new ResourceUnavailableException("Unable to apply firewall rules, virtual router is not in the right state", VirtualRouter.class, router.getId()); } } + return result; } @@ -2179,4 +2208,68 @@ public class VirtualNetworkApplianceManagerImpl implements VirtualNetworkApplian // Send commands to router return sendCommandsToRouter(router, cmds); } + + @Override + public String getDnsBasicZoneUpdate() { + return _dnsBasicZoneUpdates; + } + + + @Override + public boolean applyStaticNats(Network network, List rules) throws ResourceUnavailableException { + List routers = _routerDao.findByNetwork(network.getId()); + if (routers == null || routers.isEmpty()) { + s_logger.warn("Unable to create static nat, virtual router doesn't exist in the network " + network.getId()); + throw new ResourceUnavailableException("Unable to create static nat", DataCenter.class, network.getDataCenterId()); + } + + boolean result = true; + for (DomainRouterVO router : routers) { + if (router.getState() == State.Running) { + s_logger.debug("Applying " + rules.size() + " static nat in network " + network); + result = applyStaticNat(router, rules); + + //If rules fail to apply on one domR, no need to proceed with the rest + if (!result) { + throw new ResourceUnavailableException("Unable to apply static nat on router ", VirtualRouter.class, router.getId()); + } + + } else if (router.getState() == State.Stopped || router.getState() == State.Stopping) { + s_logger.debug("Router is in " + router.getState() + ", so not sending apply firewall rules commands to the backend"); + } else { + s_logger.warn("Unable to apply static nat, virtual router is not in the right state " + router.getState()); + throw new ResourceUnavailableException("Unable to apply static nat, virtual router is not in the right state", VirtualRouter.class, router.getId()); + } + } + + return result; + } + + + protected boolean applyStaticNat(DomainRouterVO router, List rules) throws ResourceUnavailableException { + Commands cmds = new Commands(OnError.Continue); + createApplyStaticNatCommands(rules, router, cmds); + // Send commands to router + return sendCommandsToRouter(router, cmds); + } + + private void createApplyStaticNatCommands(List rules, DomainRouterVO router, Commands cmds) { + List rulesTO = null; + if (rules != null) { + rulesTO = new ArrayList(); + for (StaticNat rule : rules) { + IpAddress sourceIp = _networkMgr.getIp(rule.getSourceIpAddressId()); + StaticNatRuleTO ruleTO = new StaticNatRuleTO(0, sourceIp.getAddress().addr(), null, null, rule.getDestIpAddress(), null, null, null, rule.isForRevoke(), false); + rulesTO.add(ruleTO); + } + } + + SetStaticNatRulesCommand cmd = new SetStaticNatRulesCommand(rulesTO); + cmd.setAccessDetail(NetworkElementCommand.ROUTER_IP, router.getPrivateIpAddress()); + cmd.setAccessDetail(NetworkElementCommand.ROUTER_GUEST_IP, router.getGuestIpAddress()); + cmd.setAccessDetail(NetworkElementCommand.ROUTER_NAME, router.getInstanceName()); + DataCenterVO dcVo = _dcDao.findById(router.getDataCenterIdToDeployIn()); + cmd.setAccessDetail(NetworkElementCommand.ZONE_NETWORK_TYPE, dcVo.getNetworkType().toString()); + cmds.addCommand(cmd); + } } diff --git a/server/src/com/cloud/network/rules/FirewallManager.java b/server/src/com/cloud/network/rules/FirewallManager.java index c7faf66b6ac..6367c482c29 100644 --- a/server/src/com/cloud/network/rules/FirewallManager.java +++ b/server/src/com/cloud/network/rules/FirewallManager.java @@ -7,6 +7,7 @@ import com.cloud.exception.ResourceUnavailableException; import com.cloud.network.IPAddressVO; import com.cloud.network.IpAddress; import com.cloud.network.firewall.FirewallService; +import com.cloud.network.rules.FirewallRule.Purpose; import com.cloud.user.Account; public interface FirewallManager extends FirewallService{ @@ -29,7 +30,7 @@ public interface FirewallManager extends FirewallService{ */ void detectRulesConflict(FirewallRule newRule, IpAddress ipAddress) throws NetworkRuleConflictException; - void validateFirewallRule(Account caller, IPAddressVO ipAddress, Integer portStart, Integer portEnd, String proto); + void validateFirewallRule(Account caller, IPAddressVO ipAddress, Integer portStart, Integer portEnd, String proto, Purpose purpose); boolean applyRules(List rules, boolean continueOnError) throws ResourceUnavailableException; diff --git a/server/src/com/cloud/network/rules/RulesManager.java b/server/src/com/cloud/network/rules/RulesManager.java index 8872d2c1b95..04ea5829a75 100644 --- a/server/src/com/cloud/network/rules/RulesManager.java +++ b/server/src/com/cloud/network/rules/RulesManager.java @@ -66,6 +66,9 @@ public interface RulesManager extends RulesService { boolean releasePorts(long ipId, String protocol, FirewallRule.Purpose purpose, int... ports); List listByNetworkId(long networkId); - - + + boolean applyStaticNatForIp(long sourceIpId, boolean continueOnError, Account caller, boolean forRevoke); + + boolean applyStaticNatsForNetwork(long networkId, boolean continueOnError, Account caller); + } diff --git a/server/src/com/cloud/network/rules/RulesManagerImpl.java b/server/src/com/cloud/network/rules/RulesManagerImpl.java index 4afe37eb0e3..f64b8d6c252 100755 --- a/server/src/com/cloud/network/rules/RulesManagerImpl.java +++ b/server/src/com/cloud/network/rules/RulesManagerImpl.java @@ -165,7 +165,7 @@ public class RulesManagerImpl implements RulesManager, RulesService, Manager { throw new InvalidParameterValueException("Unable to create port forwarding rule; ip id=" + ipAddrId + " has static nat enabled"); } - _firewallMgr.validateFirewallRule(caller, ipAddress, rule.getSourcePortStart(), rule.getSourcePortEnd(), rule.getProtocol()); + _firewallMgr.validateFirewallRule(caller, ipAddress, rule.getSourcePortStart(), rule.getSourcePortEnd(), rule.getProtocol(), Purpose.PortForwarding); Long networkId = ipAddress.getAssociatedWithNetworkId(); Long accountId = ipAddress.getAccountId(); @@ -225,7 +225,7 @@ public class RulesManagerImpl implements RulesManager, RulesService, Manager { if (e instanceof NetworkRuleConflictException) { throw (NetworkRuleConflictException) e; } - throw new CloudRuntimeException("Unable to add rule for the ip id=" + newRule.getSourceIpAddressId(), e); + throw new CloudRuntimeException("Unable to add rule for the ip id=" + ipAddrId, e); } } @@ -245,16 +245,13 @@ public class RulesManagerImpl implements RulesManager, RulesService, Manager { throw new NetworkRuleConflictException("Can't do static nat on ip address: " + ipAddress.getAddress()); } - _firewallMgr.validateFirewallRule(caller, ipAddress, rule.getSourcePortStart(), rule.getSourcePortEnd(), rule.getProtocol()); + _firewallMgr.validateFirewallRule(caller, ipAddress, rule.getSourcePortStart(), rule.getSourcePortEnd(), rule.getProtocol(), Purpose.StaticNat); Long networkId = ipAddress.getAssociatedWithNetworkId(); Long accountId = ipAddress.getAccountId(); Long domainId = ipAddress.getDomainId(); - // Get nic IP4 address - Nic guestNic = _networkMgr.getNicInNetwork(ipAddress.getAssociatedWithVmId(), networkId); - assert (guestNic != null && guestNic.getIp4Address() != null) : "Vm doesn't belong to network associated with ipAddress or ip4 address is null...how is it possible?"; - String dstIp = guestNic.getIp4Address(); + String dstIp = _networkMgr.getIpInNetwork(ipAddress.getAssociatedWithVmId(), networkId); Transaction txn = Transaction.currentTxn(); txn.start(); @@ -291,7 +288,7 @@ public class RulesManagerImpl implements RulesManager, RulesService, Manager { } @Override - public boolean enableOneToOneNat(long ipId, long vmId) throws NetworkRuleConflictException { + public boolean enableStaticNat(long ipId, long vmId) throws NetworkRuleConflictException { Account caller = UserContext.current().getCaller(); @@ -351,7 +348,22 @@ public class RulesManagerImpl implements RulesManager, RulesService, Manager { ipAddress.setOneToOneNat(true); ipAddress.setAssociatedWithVmId(vmId); - return _ipAddressDao.update(ipAddress.getId(), ipAddress); + if (_ipAddressDao.update(ipAddress.getId(), ipAddress)) { + //enable static nat on the backend + s_logger.trace("Enabling static nat for ip address " + ipAddress + " and vm id=" + vmId + " on the backend"); + if (applyStaticNatForIp(ipId, false, caller, false)) { + return true; + } else { + ipAddress.setOneToOneNat(false); + ipAddress.setAssociatedWithVmId(null); + _ipAddressDao.update(ipAddress.getId(), ipAddress); + s_logger.warn("Failed to enable static nat rule for ip address " + ipId + " on the backend"); + return false; + } + } else { + s_logger.warn("Failed to update ip address " + ipAddress + " in the DB as a part of enableStaticNat"); + return false; + } } @@ -692,6 +704,38 @@ public class RulesManagerImpl implements RulesManager, RulesService, Manager { return true; } + @Override + public boolean applyStaticNatsForNetwork(long networkId, boolean continueOnError, Account caller) { + List ips = _ipAddressDao.listStaticNatPublicIps(networkId); + if (ips.isEmpty()) { + s_logger.debug("There are no static nat to apply for network id=" + networkId); + return true; + } + + if (caller != null) { + _accountMgr.checkAccess(caller, ips.toArray(new IPAddressVO[ips.size()])); + } + + List staticNats = new ArrayList(); + for (IPAddressVO ip : ips) { + // Get nic IP4 address + String dstIp = _networkMgr.getIpInNetwork(ip.getAssociatedWithVmId(), networkId); + StaticNatImpl staticNat = new StaticNatImpl(ip.getAccountId(), ip.getDomainId(), networkId, ip.getId(), dstIp, false); + staticNats.add(staticNat); + } + + try { + if (!_networkMgr.applyStaticNats(staticNats, continueOnError)) { + return false; + } + } catch (ResourceUnavailableException ex) { + s_logger.warn("Failed to create static nat rule due to ", ex); + return false; + } + + return true; + } + @Override public List searchStaticNatRules(Long ipId, Long id, Long vmId, Long start, Long size, String accountName, Long domainId) { Account caller = UserContext.current().getCaller(); @@ -797,11 +841,15 @@ public class RulesManagerImpl implements RulesManager, RulesService, Manager { s_logger.debug("Releasing " + staticNatRules.size() + " static nat rules for ip id=" + ipId); } + for (FirewallRuleVO rule : staticNatRules) { // Mark all static nat rules as Revoke, but don't revoke them yet revokeStaticNatRuleInternal(rule.getId(), caller, userId, false); } + //revoke static nat for the ip address + boolean staticNatRevoked = applyStaticNatForIp(ipId, false, caller, true); + // revoke all port forwarding rules applyPortForwardingRules(ipId, true, caller); @@ -816,7 +864,7 @@ public class RulesManagerImpl implements RulesManager, RulesService, Manager { s_logger.debug("Successfully released rules for ip id=" + ipId + " and # of rules now = " + rules.size()); } - return rules.size() == 0; + return (rules.size() == 0 && staticNatRevoked); } @Override @@ -955,7 +1003,7 @@ public class RulesManagerImpl implements RulesManager, RulesService, Manager { } @Override - public boolean disableOneToOneNat(long ipId) throws ResourceUnavailableException { + public boolean disableStaticNat(long ipId) throws ResourceUnavailableException { boolean success = true; Account caller = UserContext.current().getCaller(); @@ -1002,9 +1050,53 @@ public class RulesManagerImpl implements RulesManager, RulesService, Manager { throw new InvalidParameterValueException("Source ip address of the rule id=" + rule.getId() + " is not static nat enabled"); } - Nic guestNic = _networkMgr.getNicInNetwork(ip.getAssociatedWithVmId(), rule.getNetworkId()); + String dstIp = _networkMgr.getIpInNetwork(ip.getAssociatedWithVmId(), rule.getNetworkId()); - return new StaticNatRuleImpl(ruleVO, guestNic.getIp4Address()); + return new StaticNatRuleImpl(ruleVO, dstIp); + } + + @Override + public boolean applyStaticNatForIp(long sourceIpId, boolean continueOnError, Account caller, boolean forRevoke) { + + List staticNats = new ArrayList(); + IpAddress sourceIp = _ipAddressDao.findById(sourceIpId); + + if (!sourceIp.isOneToOneNat()) { + s_logger.debug("Source ip id=" + sourceIpId + " is not one to one nat"); + return true; + } + + Long networkId = sourceIp.getAssociatedWithNetworkId(); + if (networkId == null) { + throw new CloudRuntimeException("Ip address is not associated with any network"); + } + + UserVmVO vm = _vmDao.findById(sourceIp.getAssociatedWithVmId()); + Network network = _networkMgr.getNetwork(networkId); + if (network == null) { + throw new CloudRuntimeException("Unable to find ip address to map to in vm id=" + vm.getId()); + } + + if (caller != null) { + _accountMgr.checkAccess(caller, sourceIp); + } + + //create new static nat rule + // Get nic IP4 address + String dstIp = _networkMgr.getIpInNetwork(sourceIp.getAssociatedWithVmId(), networkId); + StaticNatImpl staticNat = new StaticNatImpl(sourceIp.getAccountId(), sourceIp.getDomainId(), networkId, sourceIpId, dstIp, forRevoke); + staticNats.add(staticNat); + + try { + if (!_networkMgr.applyStaticNats(staticNats, continueOnError)) { + return false; + } + } catch (ResourceUnavailableException ex) { + s_logger.warn("Failed to create static nat rule due to ", ex); + return false; + } + + return true; } } diff --git a/server/src/com/cloud/network/rules/StaticNatImpl.java b/server/src/com/cloud/network/rules/StaticNatImpl.java new file mode 100644 index 00000000000..b8ebf2a002e --- /dev/null +++ b/server/src/com/cloud/network/rules/StaticNatImpl.java @@ -0,0 +1,71 @@ +/** + * Copyright (C) 2010 Cloud.com, Inc. All rights reserved. + * + * This software is licensed under the GNU General Public License v3 or later. + * + * It is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or any later version. + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + * + */ + +package com.cloud.network.rules; + +public class StaticNatImpl implements StaticNat{ + long accountId; + long domainId; + long networkId; + long sourceIpAddressId; + String destIpAddress; + boolean forRevoke; + + + + public StaticNatImpl(long accountId, long domainId, long networkId, long sourceIpAddressId, String destIpAddress, boolean forRevoke) { + super(); + this.accountId = accountId; + this.domainId = domainId; + this.networkId = networkId; + this.sourceIpAddressId = sourceIpAddressId; + this.destIpAddress = destIpAddress; + this.forRevoke = forRevoke; + } + + @Override + public long getAccountId() { + return accountId; + } + + @Override + public long getDomainId() { + return domainId; + } + + @Override + public long getNetworkId() { + return networkId; + } + + @Override + public long getSourceIpAddressId() { + return sourceIpAddressId; + } + + @Override + public String getDestIpAddress() { + return destIpAddress; + } + + @Override + public boolean isForRevoke() { + return forRevoke; + } + +} diff --git a/server/src/com/cloud/vm/UserVmManagerImpl.java b/server/src/com/cloud/vm/UserVmManagerImpl.java index a9cd7c16841..9866e252e80 100755 --- a/server/src/com/cloud/vm/UserVmManagerImpl.java +++ b/server/src/com/cloud/vm/UserVmManagerImpl.java @@ -1254,7 +1254,7 @@ public class UserVmManagerImpl implements UserVmManager, UserVmService, Manager IPAddressVO ip = _ipAddressDao.findByAssociatedVmId(vmId); try { if (ip != null) { - if (_rulesMgr.disableOneToOneNat(ip.getId())) { + if (_rulesMgr.disableStaticNat(ip.getId())) { s_logger.debug("Disabled 1-1 nat for ip address " + ip + " as a part of vm id=" + vmId + " expunge"); } else { s_logger.warn("Failed to disable static nat for ip address " + ip + " as a part of vm id=" + vmId + " expunge"); diff --git a/server/src/com/cloud/vm/VirtualMachineManagerImpl.java b/server/src/com/cloud/vm/VirtualMachineManagerImpl.java index 63ec27da1d3..11c02b2cc44 100755 --- a/server/src/com/cloud/vm/VirtualMachineManagerImpl.java +++ b/server/src/com/cloud/vm/VirtualMachineManagerImpl.java @@ -1557,6 +1557,13 @@ public class VirtualMachineManagerImpl implements VirtualMachineManager, Listene } if(trackExternalChange) { + if(serverState == State.Starting) { + if(vm.getHostId() != null && vm.getHostId() != hostId) { + s_logger.info("CloudStack is starting VM on host " + vm.getHostId() + ", but status report comes from a different host " + hostId + ", skip status sync for vm: " + vm.getInstanceName()); + return null; + } + } + if(vm.getHostId() == null || hostId != vm.getHostId()) { try { stateTransitTo(vm, VirtualMachine.Event.AgentReportMigrated, hostId);