diff --git a/api/src/com/cloud/agent/api/routing/SetNetworkACLAnswer.java b/api/src/com/cloud/agent/api/routing/SetNetworkACLAnswer.java new file mode 100644 index 00000000000..b715a72a46a --- /dev/null +++ b/api/src/com/cloud/agent/api/routing/SetNetworkACLAnswer.java @@ -0,0 +1,21 @@ +// Copyright 2012 Citrix Systems, Inc. Licensed under the +package com.cloud.agent.api.routing; + +import com.cloud.agent.api.Answer; + +public class SetNetworkACLAnswer extends Answer { + String[] results; + + protected SetNetworkACLAnswer() { + } + + public SetNetworkACLAnswer(SetNetworkACLCommand cmd, boolean success, String[] results) { + super(cmd, success, null); + assert (cmd.getRules().length == results.length) : "ACLs and their results should be the same length"; + this.results = results; + } + + public String[] getResults() { + return results; + } +} \ No newline at end of file diff --git a/api/src/com/cloud/agent/api/routing/SetNetworkACLCommand.java b/api/src/com/cloud/agent/api/routing/SetNetworkACLCommand.java new file mode 100644 index 00000000000..f26e337f65b --- /dev/null +++ b/api/src/com/cloud/agent/api/routing/SetNetworkACLCommand.java @@ -0,0 +1,35 @@ +// Copyright 2012 Citrix Systems, Inc. Licensed under the +// Apache License, Version 2.0 (the "License"); you may not use this +// file except in compliance with the License. Citrix Systems, Inc. +// reserves all rights not expressly granted by the License. +// You may obtain a copy of the License at http://www.apache.org/licenses/LICENSE-2.0 +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. +// +// Automatically generated by addcopyright.py at 04/03/2012 +package com.cloud.agent.api.routing; + +import java.util.List; + +import com.cloud.agent.api.to.NetworkACLTO; + +/** + * @author Alena Prokharchyk + */ +public class SetNetworkACLCommand extends NetworkElementCommand{ + NetworkACLTO[] rules; + + protected SetNetworkACLCommand() { + } + + public SetNetworkACLCommand(List rules) { + this.rules = rules.toArray(new NetworkACLTO[rules.size()]); + } + + public NetworkACLTO[] getRules() { + return rules; + } +} diff --git a/api/src/com/cloud/agent/api/routing/SetPortForwardingRulesVpcCommand.java b/api/src/com/cloud/agent/api/routing/SetPortForwardingRulesVpcCommand.java new file mode 100644 index 00000000000..e6e91f52c26 --- /dev/null +++ b/api/src/com/cloud/agent/api/routing/SetPortForwardingRulesVpcCommand.java @@ -0,0 +1,29 @@ +// Copyright 2012 Citrix Systems, Inc. Licensed under the +// Apache License, Version 2.0 (the "License"); you may not use this +// file except in compliance with the License. Citrix Systems, Inc. +// reserves all rights not expressly granted by the License. +// You may obtain a copy of the License at http://www.apache.org/licenses/LICENSE-2.0 +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. +// +// Automatically generated by addcopyright.py at 04/03/2012 +package com.cloud.agent.api.routing; + +import java.util.List; + +import com.cloud.agent.api.to.PortForwardingRuleTO; + +/** + * @author Alena Prokharchyk + */ +public class SetPortForwardingRulesVpcCommand extends SetPortForwardingRulesCommand{ + protected SetPortForwardingRulesVpcCommand() { + } + + public SetPortForwardingRulesVpcCommand(List pfRules) { + super(pfRules); + } +} diff --git a/api/src/com/cloud/agent/api/to/NetworkACLTO.java b/api/src/com/cloud/agent/api/to/NetworkACLTO.java new file mode 100644 index 00000000000..2f725638d02 --- /dev/null +++ b/api/src/com/cloud/agent/api/to/NetworkACLTO.java @@ -0,0 +1,109 @@ +// Copyright 2012 Citrix Systems, Inc. Licensed under the +package com.cloud.agent.api.to; + +import java.util.ArrayList; +import java.util.List; + +import com.cloud.network.rules.FirewallRule; +import com.cloud.network.rules.FirewallRule.TrafficType; +import com.cloud.utils.net.NetUtils; + + +public class NetworkACLTO { + long id; + String vlanTag; + String protocol; + int[] portRange; + boolean revoked; + boolean alreadyAdded; + private List cidrList; + private Integer icmpType; + private Integer icmpCode; + private FirewallRule.TrafficType trafficType; + + + protected NetworkACLTO() { + } + + + public NetworkACLTO(long id,String vlanTag, String protocol, Integer portStart, Integer portEnd, boolean revoked, + boolean alreadyAdded, List cidrList, Integer icmpType,Integer icmpCode,TrafficType trafficType) { + this.vlanTag = vlanTag; + this.protocol = protocol; + + if (portStart != null) { + List range = new ArrayList(); + range.add(portStart); + if (portEnd != null) { + range.add(portEnd); + } + + portRange = new int[range.size()]; + int i = 0; + for (Integer port : range) { + portRange[i] = port.intValue(); + i ++; + } + } + + this.revoked = revoked; + this.alreadyAdded = alreadyAdded; + this.cidrList = cidrList; + this.icmpType = icmpType; + this.icmpCode = icmpCode; + this.trafficType = trafficType; + } + + public NetworkACLTO(FirewallRule rule, String vlanTag, FirewallRule.TrafficType trafficType ) { + this(rule.getId(), vlanTag, rule.getProtocol(), rule.getSourcePortStart(), rule.getSourcePortEnd(), + rule.getState() == FirewallRule.State.Revoke, rule.getState() == FirewallRule.State.Active, + rule.getSourceCidrList() ,rule.getIcmpType(), rule.getIcmpCode(),trafficType); + } + + public long getId() { + return id; + } + + public String getSrcVlanTag() { + return vlanTag; + } + + public String getProtocol() { + return protocol; + } + + public int[] getSrcPortRange() { + return portRange; + } + + public Integer getIcmpType(){ + return icmpType; + } + + public Integer getIcmpCode(){ + return icmpCode; + } + + public String getStringPortRange() { + if (portRange == null || portRange.length < 2) + return "0:0"; + else + return NetUtils.portRangeToString(portRange); + } + + public boolean revoked() { + return revoked; + } + + public List getSourceCidrList() { + return cidrList; + } + + public boolean isAlreadyAdded() { + return alreadyAdded; + } + + public FirewallRule.TrafficType getTrafficType() { + return trafficType; + } +} diff --git a/api/src/com/cloud/api/BaseCmd.java b/api/src/com/cloud/api/BaseCmd.java index 623cdec4939..294e2647369 100755 --- a/api/src/com/cloud/api/BaseCmd.java +++ b/api/src/com/cloud/api/BaseCmd.java @@ -41,6 +41,7 @@ import com.cloud.network.NetworkService; import com.cloud.network.StorageNetworkService; import com.cloud.network.VirtualNetworkApplianceService; import com.cloud.network.firewall.FirewallService; +import com.cloud.network.firewall.NetworkACLService; import com.cloud.network.lb.LoadBalancingRulesService; import com.cloud.network.rules.RulesService; import com.cloud.network.security.SecurityGroupService; @@ -59,7 +60,6 @@ import com.cloud.user.DomainService; import com.cloud.user.ResourceLimitService; import com.cloud.utils.Pair; import com.cloud.utils.component.ComponentLocator; -import com.cloud.utils.AnnotationHelper; import com.cloud.vm.BareMetalVmService; import com.cloud.vm.UserVmService; @@ -130,6 +130,7 @@ public abstract class BaseCmd { public static IdentityService _identityService; public static StorageNetworkService _storageNetworkService; public static VpcService _vpcService; + public static NetworkACLService _networkACLService; static void setComponents(ResponseGenerator generator) { ComponentLocator locator = ComponentLocator.getLocator(ManagementService.Name); @@ -158,6 +159,7 @@ public abstract class BaseCmd { _identityService = locator.getManager(IdentityService.class); _storageNetworkService = locator.getManager(StorageNetworkService.class); _vpcService = locator.getManager(VpcService.class); + _networkACLService = locator.getManager(NetworkACLService.class); } public abstract void execute() throws ResourceUnavailableException, InsufficientCapacityException, ServerApiException, ConcurrentOperationException, ResourceAllocationException, NetworkRuleConflictException; diff --git a/api/src/com/cloud/api/ResponseGenerator.java b/api/src/com/cloud/api/ResponseGenerator.java index 5886021eead..201883d1e84 100755 --- a/api/src/com/cloud/api/ResponseGenerator.java +++ b/api/src/com/cloud/api/ResponseGenerator.java @@ -23,6 +23,7 @@ import java.util.List; import com.cloud.api.ApiConstants.HostDetails; import com.cloud.api.ApiConstants.VMDetails; import com.cloud.api.commands.QueryAsyncJobResultCmd; +import com.cloud.api.response.NetworkACLResponse; import com.cloud.api.response.AccountResponse; import com.cloud.api.response.AsyncJobResponse; import com.cloud.api.response.CapacityResponse; @@ -103,6 +104,7 @@ import com.cloud.network.VpnUser; import com.cloud.network.router.VirtualRouter; import com.cloud.network.rules.FirewallRule; import com.cloud.network.rules.LoadBalancer; +import com.cloud.network.rules.NetworkACL; import com.cloud.network.rules.PortForwardingRule; import com.cloud.network.rules.StaticNatRule; import com.cloud.network.rules.StickinessPolicy; @@ -296,4 +298,10 @@ public interface ResponseGenerator { * @return */ VpcResponse createVpcResponse(Vpc vpc); + + /** + * @param networkACL + * @return + */ + NetworkACLResponse createNetworkACLResponse(NetworkACL networkACL); } diff --git a/api/src/com/cloud/api/commands/CreateFirewallRuleCmd.java b/api/src/com/cloud/api/commands/CreateFirewallRuleCmd.java index 800dac80e03..77caf19f9e1 100644 --- a/api/src/com/cloud/api/commands/CreateFirewallRuleCmd.java +++ b/api/src/com/cloud/api/commands/CreateFirewallRuleCmd.java @@ -159,7 +159,7 @@ public class CreateFirewallRuleCmd extends BaseAsyncCreateCmd implements Firewal } @Override - public long getSourceIpAddressId() { + public Long getSourceIpAddressId() { return ipAddressId; } diff --git a/api/src/com/cloud/api/commands/CreateIpForwardingRuleCmd.java b/api/src/com/cloud/api/commands/CreateIpForwardingRuleCmd.java index 53bd87de6f2..ba2992b5ebc 100644 --- a/api/src/com/cloud/api/commands/CreateIpForwardingRuleCmd.java +++ b/api/src/com/cloud/api/commands/CreateIpForwardingRuleCmd.java @@ -199,7 +199,7 @@ public class CreateIpForwardingRuleCmd extends BaseAsyncCreateCmd implements Sta } @Override - public long getSourceIpAddressId() { + public Long getSourceIpAddressId() { return ipAddressId; } diff --git a/api/src/com/cloud/api/commands/CreateNetworkACLCmd.java b/api/src/com/cloud/api/commands/CreateNetworkACLCmd.java new file mode 100644 index 00000000000..ccc092bb08f --- /dev/null +++ b/api/src/com/cloud/api/commands/CreateNetworkACLCmd.java @@ -0,0 +1,314 @@ +// Copyright 2012 Citrix Systems, Inc. Licensed under the +package com.cloud.api.commands; + +import java.util.ArrayList; +import java.util.List; + +import org.apache.log4j.Logger; + +import com.cloud.api.ApiConstants; +import com.cloud.api.BaseAsyncCmd; +import com.cloud.api.BaseAsyncCreateCmd; +import com.cloud.api.BaseCmd; +import com.cloud.api.IdentityMapper; +import com.cloud.api.Implementation; +import com.cloud.api.Parameter; +import com.cloud.api.ServerApiException; +import com.cloud.api.response.NetworkACLResponse; +import com.cloud.async.AsyncJob; +import com.cloud.event.EventTypes; +import com.cloud.exception.InvalidParameterValueException; +import com.cloud.exception.NetworkRuleConflictException; +import com.cloud.exception.ResourceUnavailableException; +import com.cloud.network.Network; +import com.cloud.network.rules.FirewallRule; +import com.cloud.network.rules.NetworkACL; +import com.cloud.network.vpc.Vpc; +import com.cloud.user.Account; +import com.cloud.user.UserContext; +import com.cloud.utils.net.NetUtils; + +@Implementation(description = "Creates a ACL rule the given network (the network has to belong to VPC)", +responseObject = NetworkACLResponse.class) +public class CreateNetworkACLCmd extends BaseAsyncCreateCmd implements NetworkACL { + public static final Logger s_logger = Logger.getLogger(CreateFirewallRuleCmd.class.getName()); + + private static final String s_name = "createnetworkaclresponse"; + + // /////////////////////////////////////////////////// + // ////////////// API parameters ///////////////////// + // /////////////////////////////////////////////////// + + @Parameter(name = ApiConstants.PROTOCOL, type = CommandType.STRING, required = true, description = + "the protocol for the ACL rule. Valid values are TCP/UDP/ICMP.") + private String protocol; + + @Parameter(name = ApiConstants.START_PORT, type = CommandType.INTEGER, required=true, description = "the starting port of ACL") + private Integer publicStartPort; + + @Parameter(name = ApiConstants.END_PORT, type = CommandType.INTEGER, description = "the ending port of ACL") + private Integer publicEndPort; + + @Parameter(name = ApiConstants.CIDR_LIST, type = CommandType.LIST, collectionType = CommandType.STRING, + description = "the cidr list to allow traffic from/to") + private List cidrlist; + + @Parameter(name = ApiConstants.ICMP_TYPE, type = CommandType.INTEGER, description = "type of the icmp message being sent") + private Integer icmpType; + + @Parameter(name = ApiConstants.ICMP_CODE, type = CommandType.INTEGER, description = "error code for this icmp message") + private Integer icmpCode; + + @IdentityMapper(entityTableName="networks") + @Parameter(name=ApiConstants.NETWORK_ID, type=CommandType.LONG, required=true, + description="The network of the vm the ACL will be created for") + private Long networkId; + + @Parameter(name=ApiConstants.TRAFFIC_TYPE, type=CommandType.LONG, description="the traffic type for the ACL," + + "can be Ingress or Egress, defaulted to Ingress if not specified") + private String trafficType; + + // /////////////////////////////////////////////////// + // ///////////////// Accessors /////////////////////// + // /////////////////////////////////////////////////// + + public String getEntityTable() { + return "firewall_rules"; + } + + public Long getIpAddressId() { + return null; + } + + @Override + public String getProtocol() { + return protocol.trim(); + } + + public List getSourceCidrList() { + if (cidrlist != null) { + return cidrlist; + } else { + List oneCidrList = new ArrayList(); + oneCidrList.add(NetUtils.ALL_CIDRS); + return oneCidrList; + } + } + + public long getVpcId() { + Network network = _networkService.getNetwork(getNetworkId()); + if (network == null) { + throw new InvalidParameterValueException("Invalid networkId is given"); + } + + Long vpcId = network.getVpcId(); + if (vpcId == null) { + throw new InvalidParameterValueException("Can create network ACL only for the network belonging to the VPC"); + } + + return vpcId; + } + + @Override + public FirewallRule.TrafficType getTrafficType() { + if (trafficType == null) { + return FirewallRule.TrafficType.Ingress; + } + for (FirewallRule.TrafficType type : FirewallRule.TrafficType.values()) { + if (type.toString().equalsIgnoreCase(trafficType)) { + return type; + } + } + throw new InvalidParameterValueException("Invalid traffic type " + trafficType); + } + + // /////////////////////////////////////////////////// + // ///////////// API Implementation/////////////////// + // /////////////////////////////////////////////////// + + @Override + 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 = false; + NetworkACL rule = _networkACLService.getNetworkACL(getEntityId()); + try { + UserContext.current().setEventDetails("Rule Id: " + getEntityId()); + success = _networkACLService.applyNetworkACLs(rule.getNetworkId(), callerContext.getCaller()); + + // State is different after the rule is applied, so get new object here + NetworkACLResponse aclResponse = new NetworkACLResponse(); + if (rule != null) { + aclResponse = _responseGenerator.createNetworkACLResponse(rule); + setResponseObject(aclResponse); + } + aclResponse.setResponseName(getCommandName()); + } finally { + if (!success || rule == null) { + _networkACLService.revokeNetworkACL(getEntityId(), true); + throw new ServerApiException(BaseCmd.INTERNAL_ERROR, "Failed to create network ACL"); + } + } + } + + @Override + public long getId() { + throw new UnsupportedOperationException("database id can only provided by VO objects"); + } + + @Override + public String getXid() { + // FIXME: We should allow for end user to specify Xid. + return null; + } + + @Override + public Long getSourceIpAddressId() { + return null; + } + + @Override + public Integer getSourcePortStart() { + if (publicStartPort != null) { + return publicStartPort.intValue(); + } + return null; + } + + @Override + public Integer getSourcePortEnd() { + if (publicEndPort == null) { + if (publicStartPort != null) { + return publicStartPort.intValue(); + } + } else { + return publicEndPort.intValue(); + } + + return null; + } + + @Override + public Purpose getPurpose() { + return Purpose.Firewall; + } + + @Override + public State getState() { + throw new UnsupportedOperationException("Should never call me to find the state"); + } + + @Override + public long getNetworkId() { + return networkId; + } + + @Override + public long getEntityOwnerId() { + Vpc vpc = _vpcService.getVpc(getVpcId()); + if (vpc == null) { + throw new InvalidParameterValueException("Invalid vpcId is given"); + } + + Account account = _accountService.getAccount(vpc.getAccountId()); + return account.getId(); + } + + @Override + public long getDomainId() { + Vpc vpc = _vpcService.getVpc(getVpcId()); + return vpc.getDomainId(); + } + + @Override + public void create() { + if (getSourceCidrList() != null) { + for (String cidr: getSourceCidrList()){ + if (!NetUtils.isValidCIDR(cidr)){ + throw new ServerApiException(BaseCmd.PARAM_ERROR, "Source cidrs formatting error " + cidr); + } + } + } + + try { + NetworkACL result = _networkACLService.createNetworkACL(this); + setEntityId(result.getId()); + } catch (NetworkRuleConflictException ex) { + s_logger.info("Network rule conflict: " + ex.getMessage()); + s_logger.trace("Network Rule Conflict: ", ex); + throw new ServerApiException(BaseCmd.NETWORK_RULE_CONFLICT_ERROR, ex.getMessage()); + } + } + + @Override + public String getEventType() { + return EventTypes.EVENT_FIREWALL_OPEN; + } + + @Override + public String getEventDescription() { + Network network = _networkService.getNetwork(networkId); + return ("Createing Network ACL for Netowrk: " + network + " for protocol:" + this.getProtocol()); + } + + @Override + public long getAccountId() { + Vpc vpc = _vpcService.getVpc(getVpcId()); + return vpc.getAccountId(); + } + + @Override + public String getSyncObjType() { + return BaseAsyncCmd.networkSyncObject; + } + + @Override + public Long getSyncObjId() { + return getNetworkId(); + } + + @Override + public Integer getIcmpCode() { + if (icmpCode != null) { + return icmpCode; + } else if (protocol.equalsIgnoreCase(NetUtils.ICMP_PROTO)) { + return -1; + } + return null; + } + + @Override + public Integer getIcmpType() { + if (icmpType != null) { + return icmpType; + } else if (protocol.equalsIgnoreCase(NetUtils.ICMP_PROTO)) { + return -1; + + } + return null; + } + + @Override + public Long getRelated() { + return null; + } + + @Override + public FirewallRuleType getType() { + return FirewallRuleType.User; + } + + @Override + public AsyncJob.Type getInstanceType() { + return AsyncJob.Type.FirewallRule; + } + +} diff --git a/api/src/com/cloud/api/commands/CreatePortForwardingRuleCmd.java b/api/src/com/cloud/api/commands/CreatePortForwardingRuleCmd.java index b17b6e1e57d..20132a6eca9 100644 --- a/api/src/com/cloud/api/commands/CreatePortForwardingRuleCmd.java +++ b/api/src/com/cloud/api/commands/CreatePortForwardingRuleCmd.java @@ -181,7 +181,7 @@ public class CreatePortForwardingRuleCmd extends BaseAsyncCreateCmd implements P } @Override - public long getSourceIpAddressId() { + public Long getSourceIpAddressId() { return ipAddressId; } diff --git a/api/src/com/cloud/api/response/FirewallResponse.java b/api/src/com/cloud/api/response/FirewallResponse.java index 0aa5989af8d..a6fd5970a20 100644 --- a/api/src/com/cloud/api/response/FirewallResponse.java +++ b/api/src/com/cloud/api/response/FirewallResponse.java @@ -17,10 +17,11 @@ package com.cloud.api.response; import com.cloud.api.ApiConstants; -import com.cloud.utils.IdentityProxy; import com.cloud.serializer.Param; +import com.cloud.utils.IdentityProxy; import com.google.gson.annotations.SerializedName; +@SuppressWarnings("unused") public class FirewallResponse extends BaseResponse { @SerializedName(ApiConstants.ID) @Param(description="the ID of the firewall rule") private IdentityProxy id = new IdentityProxy("firewall_rules"); @@ -34,10 +35,10 @@ public class FirewallResponse extends BaseResponse { @SerializedName(ApiConstants.END_PORT) @Param(description = "the ending port of firewall rule's port range") private String endPort; - @SerializedName(ApiConstants.IP_ADDRESS_ID) @Param(description="the public ip address id for the port forwarding rule") + @SerializedName(ApiConstants.IP_ADDRESS_ID) @Param(description="the public ip address id for the firewall rule") private Long publicIpAddressId; - @SerializedName(ApiConstants.IP_ADDRESS) @Param(description="the public ip address for the port forwarding rule") + @SerializedName(ApiConstants.IP_ADDRESS) @Param(description="the public ip address for the firewall rule") private String publicIpAddress; @SerializedName(ApiConstants.STATE) @Param(description="the state of the rule") @@ -91,7 +92,4 @@ public class FirewallResponse extends BaseResponse { public void setIcmpCode(Integer icmpCode) { this.icmpCode = icmpCode; } - - - } diff --git a/api/src/com/cloud/api/response/NetworkACLResponse.java b/api/src/com/cloud/api/response/NetworkACLResponse.java new file mode 100644 index 00000000000..1338d89f57a --- /dev/null +++ b/api/src/com/cloud/api/response/NetworkACLResponse.java @@ -0,0 +1,84 @@ +// Copyright 2012 Citrix Systems, Inc. Licensed under the +// Apache License, Version 2.0 (the "License"); you may not use this +// file except in compliance with the License. Citrix Systems, Inc. +// reserves all rights not expressly granted by the License. +// You may obtain a copy of the License at http://www.apache.org/licenses/LICENSE-2.0 +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. +// +// Automatically generated by addcopyright.py at 04/03/2012 +package com.cloud.api.response; + +import com.cloud.api.ApiConstants; +import com.cloud.serializer.Param; +import com.cloud.utils.IdentityProxy; +import com.google.gson.annotations.SerializedName; + +@SuppressWarnings("unused") +public class NetworkACLResponse extends BaseResponse { + @SerializedName(ApiConstants.ID) @Param(description="the ID of the ACL") + private IdentityProxy id = new IdentityProxy("firewall_rules"); + + @SerializedName(ApiConstants.PROTOCOL) @Param(description="the protocol of the ACL") + private String protocol; + + @SerializedName(ApiConstants.START_PORT) @Param(description="the starting port of ACL's port range") + private String startPort; + + @SerializedName(ApiConstants.END_PORT) @Param(description = "the ending port of ACL's port range") + private String endPort; + + @SerializedName(ApiConstants.TRAFFIC_TYPE) @Param(description="the traffic type for the ACL") + private String trafficType; + + @SerializedName(ApiConstants.STATE) @Param(description="the state of the rule") + private String state; + + @SerializedName(ApiConstants.CIDR_LIST) @Param(description="the cidr list to forward traffic from") + private String cidrList; + + @SerializedName(ApiConstants.ICMP_TYPE) @Param(description= "type of the icmp message being sent") + private Integer icmpType; + + @SerializedName(ApiConstants.ICMP_CODE) @Param(description = "error code for this icmp message") + private Integer icmpCode; + + public void setId(Long id) { + this.id.setValue(id); + } + + public void setProtocol(String protocol) { + this.protocol = protocol; + } + + public void setStartPort(String startPort) { + this.startPort = startPort; + } + + public void setEndPort(String endPort) { + this.endPort = endPort; + } + + public void setState(String state) { + this.state = state; + } + + public void setCidrList(String cidrList) { + this.cidrList = cidrList; + } + + public void setIcmpType(Integer icmpType) { + this.icmpType = icmpType; + } + + public void setIcmpCode(Integer icmpCode) { + this.icmpCode = icmpCode; + } + + public void setTrafficType(String trafficType) { + this.trafficType = trafficType; + } +} diff --git a/api/src/com/cloud/network/element/NetworkACLServiceProvider.java b/api/src/com/cloud/network/element/NetworkACLServiceProvider.java new file mode 100644 index 00000000000..92c4bb4eb78 --- /dev/null +++ b/api/src/com/cloud/network/element/NetworkACLServiceProvider.java @@ -0,0 +1,34 @@ +// Copyright 2012 Citrix Systems, Inc. Licensed under the +// Apache License, Version 2.0 (the "License"); you may not use this +// file except in compliance with the License. Citrix Systems, Inc. +// reserves all rights not expressly granted by the License. +// You may obtain a copy of the License at http://www.apache.org/licenses/LICENSE-2.0 +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. +// +// Automatically generated by addcopyright.py at 04/03/2012 +package com.cloud.network.element; + +import java.util.List; + +import com.cloud.exception.ResourceUnavailableException; +import com.cloud.network.Network; +import com.cloud.network.rules.FirewallRule; + +/** + * @author Alena Prokharchyk + */ +public interface NetworkACLServiceProvider extends NetworkElement{ + + /** + * @param config + * @param rules + * @return + * @throws ResourceUnavailableException + */ + boolean applyNetworkACLs(Network config, List rules) throws ResourceUnavailableException; + +} diff --git a/api/src/com/cloud/network/firewall/NetworkACLService.java b/api/src/com/cloud/network/firewall/NetworkACLService.java new file mode 100644 index 00000000000..19b3331e716 --- /dev/null +++ b/api/src/com/cloud/network/firewall/NetworkACLService.java @@ -0,0 +1,38 @@ +// Copyright 2012 Citrix Systems, Inc. Licensed under the +// Apache License, Version 2.0 (the "License"); you may not use this +// file except in compliance with the License. Citrix Systems, Inc. +// reserves all rights not expressly granted by the License. +// You may obtain a copy of the License at http://www.apache.org/licenses/LICENSE-2.0 +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. +// +// Automatically generated by addcopyright.py at 04/03/2012 +package com.cloud.network.firewall; + +import com.cloud.exception.NetworkRuleConflictException; +import com.cloud.exception.ResourceUnavailableException; +import com.cloud.network.rules.NetworkACL; +import com.cloud.user.Account; + +/** + * @author Alena Prokharchyk + */ +public interface NetworkACLService { + NetworkACL getNetworkACL(long ruleId); + boolean applyNetworkACLs(long networkId, Account caller) throws ResourceUnavailableException; + + /** + * @param createNetworkACLCmd + * @return + */ + NetworkACL createNetworkACL(NetworkACL acl) throws NetworkRuleConflictException; + /** + * @param ruleId + * @param apply + * @return + */ + boolean revokeNetworkACL(long ruleId, boolean apply); +} diff --git a/api/src/com/cloud/network/lb/LoadBalancingRule.java b/api/src/com/cloud/network/lb/LoadBalancingRule.java index c79ab18ea68..d49d0e9971a 100644 --- a/api/src/com/cloud/network/lb/LoadBalancingRule.java +++ b/api/src/com/cloud/network/lb/LoadBalancingRule.java @@ -78,7 +78,7 @@ public class LoadBalancingRule implements FirewallRule, LoadBalancer{ } @Override - public long getSourceIpAddressId() { + public Long getSourceIpAddressId() { return lb.getSourceIpAddressId(); } diff --git a/api/src/com/cloud/network/rules/FirewallRule.java b/api/src/com/cloud/network/rules/FirewallRule.java index 7c8fa663462..cb79e5eb18f 100644 --- a/api/src/com/cloud/network/rules/FirewallRule.java +++ b/api/src/com/cloud/network/rules/FirewallRule.java @@ -27,6 +27,7 @@ public interface FirewallRule extends ControlledEntity { LoadBalancing, Vpn, StaticNat, + NetworkACL, } enum FirewallRuleType { @@ -41,6 +42,11 @@ public interface FirewallRule extends ControlledEntity { Revoke // Revoke means this rule has been revoked. If this rule has been sent to the network elements, the rule will be deleted from database. } + enum TrafficType { + Ingress, + Egress + } + /** * @return database id. */ @@ -72,7 +78,7 @@ public interface FirewallRule extends ControlledEntity { long getNetworkId(); - long getSourceIpAddressId(); + Long getSourceIpAddressId(); Integer getIcmpCode(); diff --git a/api/src/com/cloud/network/rules/NetworkACL.java b/api/src/com/cloud/network/rules/NetworkACL.java new file mode 100644 index 00000000000..d0f7f21eca0 --- /dev/null +++ b/api/src/com/cloud/network/rules/NetworkACL.java @@ -0,0 +1,26 @@ +// Copyright 2012 Citrix Systems, Inc. Licensed under the +// Apache License, Version 2.0 (the "License"); you may not use this +// file except in compliance with the License. Citrix Systems, Inc. +// reserves all rights not expressly granted by the License. +// You may obtain a copy of the License at http://www.apache.org/licenses/LICENSE-2.0 +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. +// +// Automatically generated by addcopyright.py at 04/03/2012 +package com.cloud.network.rules; + + +/** + * @author Alena Prokharchyk + */ +public interface NetworkACL extends FirewallRule{ + + /** + * @return + */ + TrafficType getTrafficType(); + +} diff --git a/api/src/com/cloud/network/rules/StaticNatRule.java b/api/src/com/cloud/network/rules/StaticNatRule.java index 0cc67d6ec95..601cd6afb9e 100644 --- a/api/src/com/cloud/network/rules/StaticNatRule.java +++ b/api/src/com/cloud/network/rules/StaticNatRule.java @@ -40,7 +40,7 @@ public interface StaticNatRule extends ControlledEntity, FirewallRule { long getNetworkId(); - long getSourceIpAddressId(); + Long getSourceIpAddressId(); String getDestIpAddress(); } diff --git a/client/tomcatconf/commands.properties.in b/client/tomcatconf/commands.properties.in index b0154f2877b..d36329da658 100755 --- a/client/tomcatconf/commands.properties.in +++ b/client/tomcatconf/commands.properties.in @@ -358,4 +358,8 @@ listVPCOfferings=com.cloud.api.commands.ListVPCOfferingsCmd;15 #### Private network command createPrivateNetwork=com.cloud.api.commands.CreatePrivateNetworkCmd;1 - + +#### +createNetworkACL=com.cloud.api.commands.CreateNetworkACLCmd;15 +#deleteNetworkACL=com.cloud.api.commands.DeleteNetworkACLCmd;15 +#listNetworkACLs=com.cloud.api.commands.ListNetworkACLsCmd;15 diff --git a/core/src/com/cloud/hypervisor/xen/resource/CitrixResourceBase.java b/core/src/com/cloud/hypervisor/xen/resource/CitrixResourceBase.java index 2c8666dfc5e..7153dc4607d 100644 --- a/core/src/com/cloud/hypervisor/xen/resource/CitrixResourceBase.java +++ b/core/src/com/cloud/hypervisor/xen/resource/CitrixResourceBase.java @@ -148,8 +148,11 @@ import com.cloud.agent.api.routing.RemoteAccessVpnCfgCommand; 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.SetNetworkACLAnswer; +import com.cloud.agent.api.routing.SetNetworkACLCommand; import com.cloud.agent.api.routing.SetPortForwardingRulesAnswer; import com.cloud.agent.api.routing.SetPortForwardingRulesCommand; +import com.cloud.agent.api.routing.SetPortForwardingRulesVpcCommand; import com.cloud.agent.api.routing.SetSourceNatCommand; import com.cloud.agent.api.routing.SetStaticNatRulesAnswer; import com.cloud.agent.api.routing.SetStaticNatRulesCommand; @@ -535,11 +538,16 @@ public abstract class CitrixResourceBase implements ServerResource, HypervisorRe return execute((IpAssocVpcCommand) cmd); } else if (clazz == SetSourceNatCommand.class) { return execute((SetSourceNatCommand) cmd); + } else if (clazz == SetNetworkACLCommand.class) { + return execute((SetNetworkACLCommand) cmd); + } else if (clazz == SetPortForwardingRulesVpcCommand.class) { + return execute((SetPortForwardingRulesVpcCommand) cmd); } else { return Answer.createUnsupportedCommandAnswer(cmd); } } + protected XsLocalNetwork getNativeNetworkForTraffic(Connection conn, TrafficType type, String name) throws XenAPIException, XmlRpcException { if (name != null) { if (s_logger.isDebugEnabled()) { @@ -7108,5 +7116,13 @@ public abstract class CitrixResourceBase implements ServerResource, HypervisorRe return null; } + private SetNetworkACLAnswer execute(SetNetworkACLCommand cmd) { + // TODO - add implementation logic here + return null; + } + protected SetPortForwardingRulesAnswer execute(SetPortForwardingRulesVpcCommand cmd) { + //TODO - add implementation + return null; + } } diff --git a/server/src/com/cloud/api/ApiResponseHelper.java b/server/src/com/cloud/api/ApiResponseHelper.java index 634f5d13d4f..f39a8b2fe7b 100755 --- a/server/src/com/cloud/api/ApiResponseHelper.java +++ b/server/src/com/cloud/api/ApiResponseHelper.java @@ -32,6 +32,7 @@ import com.cloud.acl.ControlledEntity.ACLType; import com.cloud.api.ApiConstants.HostDetails; import com.cloud.api.ApiConstants.VMDetails; import com.cloud.api.commands.QueryAsyncJobResultCmd; +import com.cloud.api.response.NetworkACLResponse; import com.cloud.api.response.AccountResponse; import com.cloud.api.response.ApiResponseSerializer; import com.cloud.api.response.AsyncJobResponse; @@ -137,6 +138,7 @@ import com.cloud.network.VpnUser; import com.cloud.network.router.VirtualRouter; import com.cloud.network.rules.FirewallRule; import com.cloud.network.rules.LoadBalancer; +import com.cloud.network.rules.NetworkACL; import com.cloud.network.rules.PortForwardingRule; import com.cloud.network.rules.StaticNatRule; import com.cloud.network.rules.StickinessPolicy; @@ -2959,6 +2961,39 @@ public class ApiResponseHelper implements ResponseGenerator { response.setObjectName("firewallrule"); return response; } + + @Override + public NetworkACLResponse createNetworkACLResponse(NetworkACL networkACL) { + NetworkACLResponse response = new NetworkACLResponse(); + + response.setId(networkACL.getId()); + response.setProtocol(networkACL.getProtocol()); + if (networkACL.getSourcePortStart() != null) { + response.setStartPort(Integer.toString(networkACL.getSourcePortStart())); + } + + if (networkACL.getSourcePortEnd() != null) { + response.setEndPort(Integer.toString(networkACL.getSourcePortEnd())); + } + + List cidrs = ApiDBUtils.findFirewallSourceCidrs(networkACL.getId()); + response.setCidrList(StringUtils.join(cidrs, ",")); + + response.setTrafficType(networkACL.getTrafficType().toString()); + + FirewallRule.State state = networkACL.getState(); + String stateToSet = state.toString(); + if (state.equals(FirewallRule.State.Revoke)) { + stateToSet = "Deleting"; + } + + response.setIcmpCode(networkACL.getIcmpCode()); + response.setIcmpType(networkACL.getIcmpType()); + + response.setState(stateToSet); + response.setObjectName("networkacl"); + return response; + } public UserVmData newUserVmData(UserVm userVm) { UserVmData userVmData = new UserVmData(); diff --git a/server/src/com/cloud/configuration/DefaultComponentLibrary.java b/server/src/com/cloud/configuration/DefaultComponentLibrary.java index 4841a259ce4..cf0e61a25a8 100755 --- a/server/src/com/cloud/configuration/DefaultComponentLibrary.java +++ b/server/src/com/cloud/configuration/DefaultComponentLibrary.java @@ -106,6 +106,7 @@ import com.cloud.network.element.NetscalerLoadBalancerElementService; import com.cloud.network.element.VirtualRouterElement; import com.cloud.network.element.VirtualRouterElementService; import com.cloud.network.firewall.FirewallManagerImpl; +import com.cloud.network.firewall.NetworkACLManagerImpl; import com.cloud.network.lb.ElasticLoadBalancerManagerImpl; import com.cloud.network.lb.LoadBalancingRulesManagerImpl; import com.cloud.network.lb.dao.ElasticLbVmMapDaoImpl; @@ -397,6 +398,7 @@ public class DefaultComponentLibrary extends ComponentLibraryBase implements Com addManager("HA Manager", HighAvailabilityManagerImpl.class); addManager("VPC Manager", VpcManagerImpl.class); addManager("VpcVirtualRouterManager", VpcVirtualNetworkApplianceManagerImpl.class); + addManager("NetworkACLManager", NetworkACLManagerImpl.class); } @Override diff --git a/server/src/com/cloud/network/ExternalLoadBalancerDeviceManagerImpl.java b/server/src/com/cloud/network/ExternalLoadBalancerDeviceManagerImpl.java index 1acba8ba7b6..c91f4caff3a 100644 --- a/server/src/com/cloud/network/ExternalLoadBalancerDeviceManagerImpl.java +++ b/server/src/com/cloud/network/ExternalLoadBalancerDeviceManagerImpl.java @@ -732,7 +732,7 @@ public abstract class ExternalLoadBalancerDeviceManagerImpl extends AdapterBase List staticNatRules = new ArrayList(); IPAddressVO ipVO = _ipAddressDao.listByDcIdIpAddress(zone.getId(), publicIp).get(0); VlanVO vlan = _vlanDao.findById(ipVO.getVlanId()); - FirewallRuleVO fwRule = new FirewallRuleVO(null, ipVO.getId(), -1, -1, "any", network.getId(), network.getAccountId(), network.getDomainId(), Purpose.StaticNat, null, null, null, null); + FirewallRuleVO fwRule = new FirewallRuleVO(null, ipVO.getId(), -1, -1, "any", network.getId(), network.getAccountId(), network.getDomainId(), Purpose.StaticNat, null, null, null, null, null); FirewallRule.State state = !revoked ? FirewallRule.State.Add : FirewallRule.State.Revoke; fwRule.setState(state); StaticNatRule rule = new StaticNatRuleImpl(fwRule, privateIp); diff --git a/server/src/com/cloud/network/NetworkManagerImpl.java b/server/src/com/cloud/network/NetworkManagerImpl.java index 09bbf2204bc..c891a3eeda8 100755 --- a/server/src/com/cloud/network/NetworkManagerImpl.java +++ b/server/src/com/cloud/network/NetworkManagerImpl.java @@ -125,6 +125,7 @@ import com.cloud.network.element.DhcpServiceProvider; import com.cloud.network.element.FirewallServiceProvider; import com.cloud.network.element.IpDeployer; import com.cloud.network.element.LoadBalancingServiceProvider; +import com.cloud.network.element.NetworkACLServiceProvider; import com.cloud.network.element.NetworkElement; import com.cloud.network.element.PortForwardingServiceProvider; import com.cloud.network.element.RemoteAccessVPNServiceProvider; @@ -172,7 +173,6 @@ import com.cloud.user.User; import com.cloud.user.UserContext; import com.cloud.user.dao.AccountDao; import com.cloud.user.dao.UserStatisticsDao; -import com.cloud.utils.AnnotationHelper; import com.cloud.utils.NumbersUtil; import com.cloud.utils.Pair; import com.cloud.utils.component.Adapters; @@ -3679,6 +3679,13 @@ public class NetworkManagerImpl implements NetworkManager, NetworkService, Manag } handled = ((FirewallServiceProvider) ne).applyFWRules(network, rules); break; + case NetworkACL: + boolean isNetworkACLProvider = isProviderSupportServiceInNetwork(network.getId(), Service.Firewall, provider); + if (!(ne instanceof NetworkACLServiceProvider && isNetworkACLProvider)) { + continue; + } + handled = ((NetworkACLServiceProvider) ne).applyNetworkACLs(network, rules); + break; default: s_logger.debug("Unable to handle network rules for purpose: " + purpose.toString()); handled = false; diff --git a/server/src/com/cloud/network/dao/FirewallRulesDao.java b/server/src/com/cloud/network/dao/FirewallRulesDao.java index dc9ab00e15c..48aec350b28 100644 --- a/server/src/com/cloud/network/dao/FirewallRulesDao.java +++ b/server/src/com/cloud/network/dao/FirewallRulesDao.java @@ -50,5 +50,8 @@ public interface FirewallRulesDao extends GenericDao { List listByIpAndNotRevoked(long ipAddressId); long countRulesByIpId(long sourceIpId); + + List listByNetworkPurposeTrafficTypeAndNotRevoked(long networkId, FirewallRule.Purpose purpose, FirewallRule.TrafficType trafficType); + } diff --git a/server/src/com/cloud/network/dao/FirewallRulesDaoImpl.java b/server/src/com/cloud/network/dao/FirewallRulesDaoImpl.java index 66cf887d0f7..5423ad91c90 100644 --- a/server/src/com/cloud/network/dao/FirewallRulesDaoImpl.java +++ b/server/src/com/cloud/network/dao/FirewallRulesDaoImpl.java @@ -21,6 +21,7 @@ import com.cloud.network.rules.FirewallRule; import com.cloud.network.rules.FirewallRule.FirewallRuleType; import com.cloud.network.rules.FirewallRule.Purpose; import com.cloud.network.rules.FirewallRule.State; +import com.cloud.network.rules.FirewallRule.TrafficType; import com.cloud.network.rules.FirewallRuleVO; import com.cloud.utils.component.ComponentLocator; import com.cloud.utils.db.DB; @@ -69,6 +70,7 @@ public class FirewallRulesDaoImpl extends GenericDaoBase i NotRevokedSearch.and("sourcePortStart", NotRevokedSearch.entity().getSourcePortStart(), Op.EQ); NotRevokedSearch.and("sourcePortEnd", NotRevokedSearch.entity().getSourcePortEnd(), Op.EQ); NotRevokedSearch.and("networkId", NotRevokedSearch.entity().getNetworkId(), Op.EQ); + NotRevokedSearch.and("trafficType", NotRevokedSearch.entity().getTrafficType(), Op.EQ); NotRevokedSearch.done(); ReleaseSearch = createSearchBuilder(); @@ -265,4 +267,19 @@ public class FirewallRulesDaoImpl extends GenericDaoBase i return customSearch(sc, null).get(0); } + @Override + public List listByNetworkPurposeTrafficTypeAndNotRevoked(long networkId, Purpose purpose, TrafficType trafficType) { + SearchCriteria sc = NotRevokedSearch.create(); + sc.setParameters("networkId", networkId); + sc.setParameters("state", State.Revoke); + + if (purpose != null) { + sc.setParameters("purpose", purpose); + } + + sc.setParameters("trafficType", trafficType); + + return listBy(sc); + } + } diff --git a/server/src/com/cloud/network/element/VpcVirtualRouterElement.java b/server/src/com/cloud/network/element/VpcVirtualRouterElement.java index b4462db8f42..321e1c0be59 100644 --- a/server/src/com/cloud/network/element/VpcVirtualRouterElement.java +++ b/server/src/com/cloud/network/element/VpcVirtualRouterElement.java @@ -34,7 +34,10 @@ import com.cloud.network.Network.Service; import com.cloud.network.NetworkService; import com.cloud.network.PublicIpAddress; import com.cloud.network.router.VirtualRouter; +import com.cloud.network.router.VirtualRouter.Role; import com.cloud.network.router.VpcVirtualNetworkApplianceManager; +import com.cloud.network.rules.FirewallRule; +import com.cloud.network.rules.NetworkACL; import com.cloud.network.vpc.Vpc; import com.cloud.network.vpc.VpcManager; import com.cloud.offering.NetworkOffering; @@ -51,7 +54,7 @@ import com.cloud.vm.VirtualMachineProfile; * @author Alena Prokharchyk */ @Local(value = NetworkElement.class) -public class VpcVirtualRouterElement extends VirtualRouterElement implements VpcProvider{ +public class VpcVirtualRouterElement extends VirtualRouterElement implements VpcProvider, NetworkACLServiceProvider{ private static final Logger s_logger = Logger.getLogger(VpcVirtualRouterElement.class); @Inject NetworkService _ntwkService; @@ -341,4 +344,24 @@ public class VpcVirtualRouterElement extends VirtualRouterElement implements Vpc return false; } } + + @Override + public boolean applyNetworkACLs(Network config, List rules) throws ResourceUnavailableException { + if (canHandle(config, Service.Firewall)) { + List routers = _routerDao.listByNetworkAndRole(config.getId(), Role.VIRTUAL_ROUTER); + if (routers == null || routers.isEmpty()) { + s_logger.debug("Virtual router elemnt doesn't need to apply firewall rules on the backend; virtual " + + "router doesn't exist in the network " + config.getId()); + return true; + } + + if (!_vpcRouterMgr.applyNetworkACLs(config, (List)rules, routers)) { + throw new CloudRuntimeException("Failed to apply firewall rules in network " + config.getId()); + } else { + return true; + } + } else { + return true; + } + } } diff --git a/server/src/com/cloud/network/firewall/FirewallManagerImpl.java b/server/src/com/cloud/network/firewall/FirewallManagerImpl.java index 506b615bd9d..ea8aec749ea 100644 --- a/server/src/com/cloud/network/firewall/FirewallManagerImpl.java +++ b/server/src/com/cloud/network/firewall/FirewallManagerImpl.java @@ -33,7 +33,6 @@ import com.cloud.event.EventTypes; import com.cloud.event.UsageEventVO; import com.cloud.event.dao.EventDao; import com.cloud.event.dao.UsageEventDao; -import com.cloud.exception.ConcurrentOperationException; import com.cloud.exception.InvalidParameterValueException; import com.cloud.exception.NetworkRuleConflictException; import com.cloud.exception.ResourceUnavailableException; @@ -73,7 +72,7 @@ import com.cloud.utils.net.NetUtils; import com.cloud.vm.UserVmVO; import com.cloud.vm.dao.UserVmDao; -@Local(value = { FirewallService.class, FirewallManager.class }) +@Local(value = { FirewallService.class, FirewallManager.class}) public class FirewallManagerImpl implements FirewallService, FirewallManager, Manager { private static final Logger s_logger = Logger.getLogger(FirewallManagerImpl.class); String _name; @@ -147,22 +146,10 @@ public class FirewallManagerImpl implements FirewallService, FirewallManager, Ma IPAddressVO ipAddress = _ipAddressDao.findById(ipAddrId); // Validate ip address if (ipAddress == null && type == FirewallRule.FirewallRuleType.User) { - throw new InvalidParameterValueException("Unable to create port forwarding rule; ip id=" + ipAddrId + + throw new InvalidParameterValueException("Unable to create firewall rule; ip id=" + ipAddrId + " doesn't exist in the system"); } - //associate ip address to network (if needed) - if (ipAddress.getAssociatedWithNetworkId() == null) { - s_logger.debug("The ip is not associated with the network id="+ networkId + " so assigning"); - try { - _networkMgr.associateIPToGuestNetwork(ipAddrId, networkId); - } catch (Exception ex) { - s_logger.warn("Failed to associate ip id=" + ipAddrId + " to network id=" + networkId + " as " + - "a part of firewall rule creation"); - return null; - } - } - _networkMgr.checkIpForService(ipAddress, Service.Firewall); validateFirewallRule(caller, ipAddress, portStart, portEnd, protocol, Purpose.Firewall, type); @@ -184,16 +171,16 @@ public class FirewallManagerImpl implements FirewallService, FirewallManager, Ma domainId = ipAddress.getAllocatedInDomainId(); } - Transaction txn = Transaction.currentTxn(); txn.start(); - FirewallRuleVO newRule = new FirewallRuleVO(xId, ipAddrId, portStart, portEnd, protocol.toLowerCase(), networkId, accountId, domainId, Purpose.Firewall, sourceCidrList, icmpCode, icmpType, relatedRuleId); + FirewallRuleVO newRule = new FirewallRuleVO(xId, ipAddrId, portStart, portEnd, protocol.toLowerCase(), networkId, + accountId, domainId, Purpose.Firewall, sourceCidrList, icmpCode, icmpType, relatedRuleId, null); newRule.setType(type); newRule = _firewallDao.persist(newRule); if (type == FirewallRuleType.User) - detectRulesConflict(newRule, ipAddress); + detectRulesConflict(newRule); if (!_firewallDao.setStateToAdd(newRule)) { throw new CloudRuntimeException("Unable to update the state to add for " + newRule); @@ -252,18 +239,19 @@ public class FirewallManagerImpl implements FirewallService, FirewallManager, Ma } @Override - public void detectRulesConflict(FirewallRule newRule, IpAddress ipAddress) throws NetworkRuleConflictException { - assert newRule.getSourceIpAddressId() == ipAddress.getId() : "You passed in an ip address that doesn't match the address in the new rule"; + public void detectRulesConflict(FirewallRule newRule) throws NetworkRuleConflictException { 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."; + 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 oneOfRulesIsFirewall = ((rule.getPurpose() == Purpose.Firewall || newRule.getPurpose() == Purpose.Firewall) && ((newRule.getPurpose() != rule.getPurpose()) || (!newRule.getProtocol() + boolean oneOfRulesIsFirewall = ((rule.getPurpose() == Purpose.Firewall || newRule.getPurpose() == Purpose.Firewall) + && ((newRule.getPurpose() != rule.getPurpose()) || (!newRule.getProtocol() .equalsIgnoreCase(rule.getProtocol())))); // if both rules are firewall and their cidrs are different, we can skip port ranges verification @@ -288,24 +276,29 @@ public class FirewallManagerImpl implements FirewallService, FirewallManager, Ma if (!oneOfRulesIsFirewall) { 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()); + 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()); + 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()); + throw new NetworkRuleConflictException("New rule is for a different network than what's specified in rule " + + rule.getXid()); } 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() + if (newRule.getIcmpCode().longValue() == rule.getIcmpCode().longValue() + && newRule.getIcmpType().longValue() == rule.getIcmpType().longValue() && newRule.getProtocol().equalsIgnoreCase(rule.getProtocol()) && duplicatedCidrs) { throw new InvalidParameterValueException("New rule conflicts with existing rule id=" + rule.getId()); } } - boolean notNullPorts = (newRule.getSourcePortStart() != null && newRule.getSourcePortEnd() != null && rule.getSourcePortStart() != null && rule.getSourcePortEnd() != null); + boolean notNullPorts = (newRule.getSourcePortStart() != null && newRule.getSourcePortEnd() != null && + rule.getSourcePortStart() != null && rule.getSourcePortEnd() != null); if (!notNullPorts) { continue; } else if (!oneOfRulesIsFirewall && !(bothRulesFirewall && !duplicatedCidrs) @@ -331,7 +324,8 @@ public class FirewallManagerImpl implements FirewallService, FirewallManager, Ma } @Override - public void validateFirewallRule(Account caller, IPAddressVO ipAddress, Integer portStart, Integer portEnd, String proto, Purpose purpose, FirewallRuleType type) { + public void validateFirewallRule(Account caller, IPAddressVO ipAddress, Integer portStart, Integer portEnd, + String proto, Purpose purpose, FirewallRuleType type) { if (portStart != null && !NetUtils.isValidPort(portStart)) { throw new InvalidParameterValueException("publicPort is an invalid value: " + portStart); } @@ -351,10 +345,13 @@ public class FirewallManagerImpl implements FirewallService, FirewallManager, Ma // Validate ip address _accountMgr.checkAccess(caller, null, true, ipAddress); - Long networkId = ipAddress.getAssociatedWithNetworkId(); - if (networkId == null) { - throw new InvalidParameterValueException("Unable to create port forwarding rule ; ip id=" + ipAddress.getId() + " is not associated with any network"); + Long networkId = null; + if (ipAddress.getAssociatedWithNetworkId() == null) { + throw new InvalidParameterValueException("Unable to create port forwarding rule ; ip id=" + + ipAddress.getId() + " is not associated with any network"); + } else { + networkId = ipAddress.getAssociatedWithNetworkId(); } Network network = _networkMgr.getNetwork(networkId); @@ -441,7 +438,7 @@ public class FirewallManagerImpl implements FirewallService, FirewallManager, Ma public boolean applyFirewallRules(List rules, boolean continueOnError, Account caller) { if (rules.size() == 0) { - s_logger.debug("There are no firewall rules to apply for ip id=" + rules); + s_logger.debug("There are no firewall rules to apply"); return true; } @@ -493,7 +490,6 @@ public class FirewallManagerImpl implements FirewallService, FirewallManager, Ma } return success; - } @Override diff --git a/server/src/com/cloud/network/firewall/NetworkACLManagerImpl.java b/server/src/com/cloud/network/firewall/NetworkACLManagerImpl.java new file mode 100644 index 00000000000..366f6bf92e5 --- /dev/null +++ b/server/src/com/cloud/network/firewall/NetworkACLManagerImpl.java @@ -0,0 +1,301 @@ +// Copyright 2012 Citrix Systems, Inc. Licensed under the +// Apache License, Version 2.0 (the "License"); you may not use this +// file except in compliance with the License. Citrix Systems, Inc. +// reserves all rights not expressly granted by the License. +// You may obtain a copy of the License at http://www.apache.org/licenses/LICENSE-2.0 +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. +// +// Automatically generated by addcopyright.py at 04/03/2012 +package com.cloud.network.firewall; + +import java.util.Collection; +import java.util.HashSet; +import java.util.List; +import java.util.Map; + +import javax.ejb.Local; +import javax.naming.ConfigurationException; + +import org.apache.log4j.Logger; + +import com.cloud.acl.SecurityChecker.AccessType; +import com.cloud.event.ActionEvent; +import com.cloud.event.EventTypes; +import com.cloud.exception.InvalidParameterValueException; +import com.cloud.exception.NetworkRuleConflictException; +import com.cloud.exception.ResourceUnavailableException; +import com.cloud.network.Network; +import com.cloud.network.Network.Capability; +import com.cloud.network.Network.Service; +import com.cloud.network.NetworkManager; +import com.cloud.network.Networks; +import com.cloud.network.dao.FirewallRulesDao; +import com.cloud.network.rules.FirewallManager; +import com.cloud.network.rules.FirewallRule; +import com.cloud.network.rules.FirewallRule.Purpose; +import com.cloud.network.rules.FirewallRule.TrafficType; +import com.cloud.network.rules.FirewallRuleVO; +import com.cloud.network.rules.NetworkACL; +import com.cloud.network.vpc.Vpc; +import com.cloud.network.vpc.VpcManager; +import com.cloud.user.Account; +import com.cloud.user.AccountManager; +import com.cloud.user.UserContext; +import com.cloud.utils.component.Inject; +import com.cloud.utils.component.Manager; +import com.cloud.utils.db.DB; +import com.cloud.utils.db.Transaction; +import com.cloud.utils.exception.CloudRuntimeException; +import com.cloud.utils.net.NetUtils; +import com.cloud.vm.Nic.State; + +/** + * @author Alena Prokharchyk + */ + +@Local(value = { NetworkACLService.class}) +public class NetworkACLManagerImpl implements Manager,NetworkACLService{ + String _name; + private static final Logger s_logger = Logger.getLogger(NetworkACLManagerImpl.class); + + + @Inject + AccountManager _accountMgr; + @Inject + FirewallManager _firewallMgr; + @Inject + FirewallRulesDao _firewallDao; + @Inject + NetworkManager _networkMgr; + @Inject + VpcManager _vpcMgr; + + + @Override + public boolean configure(String name, Map params) throws ConfigurationException { + _name = name; + return true; + } + + @Override + public boolean start() { + return true; + } + + + @Override + public boolean stop() { + return true; + } + + + @Override + public String getName() { + return _name; + } + + @Override + public NetworkACL getNetworkACL(long ruleId) { + return _firewallDao.findById(ruleId); + } + + @Override + public boolean applyNetworkACLs(long networkId, Account caller) throws ResourceUnavailableException { + List rules = _firewallDao.listByNetworkAndPurpose(networkId, Purpose.NetworkACL); + return _firewallMgr.applyFirewallRules(rules, false, caller); + } + + @Override + public NetworkACL createNetworkACL(NetworkACL acl) throws NetworkRuleConflictException { + return createNetworkACL(UserContext.current().getCaller(), acl.getXid(), acl.getSourcePortStart(), + acl.getSourcePortEnd(), acl.getProtocol(), acl.getSourceCidrList(), acl.getIcmpCode(), + acl.getIcmpType(), null, acl.getType(), acl.getNetworkId(), acl.getTrafficType()); + } + + @DB + @ActionEvent(eventType = EventTypes.EVENT_FIREWALL_OPEN, eventDescription = "creating firewall rule", create = true) + protected NetworkACL createNetworkACL(Account caller, String xId, Integer portStart, + Integer portEnd, String protocol, List sourceCidrList, Integer icmpCode, Integer icmpType, + Long relatedRuleId, FirewallRule.FirewallRuleType type, long networkId, TrafficType trafficType) throws NetworkRuleConflictException { + + Network network = _networkMgr.getNetwork(networkId); + if (network == null) { + throw new InvalidParameterValueException("Can't find network by id"); + } + + if (network.getVpcId() == null) { + throw new UnsupportedOperationException("Network ACL rules are supported just for VPC networks"); + } + + Vpc vpc = _vpcMgr.getVpc(network.getVpcId()); + Account aclOwner = _accountMgr.getAccount(vpc.getAccountId()); + + _accountMgr.checkAccess(caller, AccessType.UseNetwork, false, network); + + + if (!_networkMgr.areServicesSupportedInNetwork(networkId, Service.Firewall)) { + throw new InvalidParameterValueException("Service " + Service.Firewall + " is not supported in network " + network); + } + + // 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"); + } + + validateNetworkACL(caller, network, portStart, portEnd, protocol); + + + Transaction txn = Transaction.currentTxn(); + txn.start(); + + FirewallRuleVO newRule = new FirewallRuleVO(xId, null, portStart, portEnd, protocol.toLowerCase(), networkId, + aclOwner.getAccountId(), aclOwner.getDomainId(), Purpose.NetworkACL, sourceCidrList, icmpCode, icmpType, + relatedRuleId, trafficType); + newRule.setType(type); + newRule = _firewallDao.persist(newRule); + + if (type == FirewallRule.FirewallRuleType.User) { + detectNetworkACLConflict(newRule); + } + + if (!_firewallDao.setStateToAdd(newRule)) { + throw new CloudRuntimeException("Unable to update the state to add for " + newRule); + } + UserContext.current().setEventDetails("Rule Id: " + newRule.getId()); + + txn.commit(); + + return newRule; + } + + + protected void validateNetworkACL(Account caller, Network network, Integer portStart, Integer portEnd, + String proto) { + + if (portStart != null && !NetUtils.isValidPort(portStart)) { + throw new InvalidParameterValueException("publicPort is an invalid value: " + portStart); + } + if (portEnd != null && !NetUtils.isValidPort(portEnd)) { + throw new InvalidParameterValueException("Public port range is an invalid value: " + portEnd); + } + + // start port can't be bigger than end port + if (portStart != null && portEnd != null && portStart > portEnd) { + throw new InvalidParameterValueException("Start port can't be bigger than end port"); + } + + if (network.getTrafficType() != Networks.TrafficType.Guest) { + throw new InvalidParameterValueException("Network ACL can be created just for networks of type " + Networks.TrafficType.Guest); + } + + // Verify that the network guru supports the protocol specified + Map protocolCapabilities = _networkMgr.getNetworkServiceCapabilities(network.getId(), 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()); + } + } + } + + protected void detectNetworkACLConflict(NetworkACL newRule) throws NetworkRuleConflictException { + + List rules = _firewallDao.listByNetworkPurposeTrafficTypeAndNotRevoked(newRule.getNetworkId(), Purpose.NetworkACL, newRule.getTrafficType()); + assert (rules.size() >= 1) : "For network ACLs, 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. + } + + // if rules cidrs are different, we can skip port ranges verification + boolean duplicatedCidrs = false; + // Verify that the rules have different cidrs + List ruleCidrList = rule.getSourceCidrList(); + List newRuleCidrList = newRule.getSourceCidrList(); + + if (ruleCidrList == null || newRuleCidrList == null) { + continue; + } + + Collection similar = new HashSet(ruleCidrList); + similar.retainAll(newRuleCidrList); + + if (similar.size() > 0) { + duplicatedCidrs = true; + } + + + 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()) && duplicatedCidrs) { + throw new InvalidParameterValueException("New network ACL conflicts with existing network ACL id=" + rule.getId()); + } + } + + boolean notNullPorts = (newRule.getSourcePortStart() != null && newRule.getSourcePortEnd() != null && + rule.getSourcePortStart() != null && rule.getSourcePortEnd() != null); + if (!notNullPorts) { + continue; + } else if (duplicatedCidrs + && ((rule.getSourcePortStart().intValue() <= newRule.getSourcePortStart().intValue() && rule.getSourcePortEnd().intValue() >= newRule.getSourcePortStart().intValue()) + || (rule.getSourcePortStart().intValue() <= newRule.getSourcePortEnd().intValue() && rule.getSourcePortEnd().intValue() >= newRule.getSourcePortEnd().intValue()) + || (newRule.getSourcePortStart().intValue() <= rule.getSourcePortStart().intValue() && newRule.getSourcePortEnd().intValue() >= rule.getSourcePortStart().intValue()) + || (newRule.getSourcePortStart().intValue() <= rule.getSourcePortEnd().intValue() && newRule.getSourcePortEnd().intValue() >= rule.getSourcePortEnd().intValue()))) { + + throw new NetworkRuleConflictException("The range specified, " + newRule.getSourcePortStart() + "-" + + newRule.getSourcePortEnd() + ", conflicts with rule " + rule.getId() + + " which has " + rule.getSourcePortStart() + "-" + rule.getSourcePortEnd()); + + } + } + + if (s_logger.isDebugEnabled()) { + s_logger.debug("No network rule conflicts detected for " + newRule + " against " + (rules.size() - 1) + " existing network ACLs"); + } + } + + @Override + public boolean revokeNetworkACL(long ruleId, boolean apply) { + Account caller = UserContext.current().getCaller(); + long userId = UserContext.current().getCallerUserId(); + return revokeNetworkACL(ruleId, apply, caller, userId); + } + + @ActionEvent(eventType = EventTypes.EVENT_FIREWALL_CLOSE, eventDescription = "revoking firewall rule", async = true) + protected boolean revokeNetworkACL(long ruleId, boolean apply, Account caller, long userId) { + + FirewallRuleVO rule = _firewallDao.findById(ruleId); + if (rule == null || rule.getPurpose() != Purpose.NetworkACL) { + throw new InvalidParameterValueException("Unable to find " + ruleId + " having purpose " + Purpose.NetworkACL); + } + + _accountMgr.checkAccess(caller, null, true, rule); + + _firewallMgr.revokeRule(rule, caller, userId, false); + + boolean success = false; + + if (apply) { + List rules = _firewallDao.listByNetworkAndPurpose(rule.getNetworkId(), Purpose.NetworkACL); + return _firewallMgr.applyFirewallRules(rules, false, caller); + } else { + success = true; + } + + return success; + } + +} diff --git a/server/src/com/cloud/network/lb/LoadBalancingRulesManagerImpl.java b/server/src/com/cloud/network/lb/LoadBalancingRulesManagerImpl.java index 805657bf10f..84221c4e78f 100755 --- a/server/src/com/cloud/network/lb/LoadBalancingRulesManagerImpl.java +++ b/server/src/com/cloud/network/lb/LoadBalancingRulesManagerImpl.java @@ -819,7 +819,7 @@ public class LoadBalancingRulesManagerImpl implements LoadBalancingRulesMa boolean success = true; try { - _firewallMgr.detectRulesConflict(newRule, ipAddr); + _firewallMgr.detectRulesConflict(newRule); if (!_firewallDao.setStateToAdd(newRule)) { throw new CloudRuntimeException("Unable to update the state to add for " + newRule); } diff --git a/server/src/com/cloud/network/router/VirtualNetworkApplianceManagerImpl.java b/server/src/com/cloud/network/router/VirtualNetworkApplianceManagerImpl.java index ee1371895a9..be5d4b6766b 100755 --- a/server/src/com/cloud/network/router/VirtualNetworkApplianceManagerImpl.java +++ b/server/src/com/cloud/network/router/VirtualNetworkApplianceManagerImpl.java @@ -61,6 +61,7 @@ import com.cloud.agent.api.routing.RemoteAccessVpnCfgCommand; import com.cloud.agent.api.routing.SavePasswordCommand; import com.cloud.agent.api.routing.SetFirewallRulesCommand; import com.cloud.agent.api.routing.SetPortForwardingRulesCommand; +import com.cloud.agent.api.routing.SetPortForwardingRulesVpcCommand; import com.cloud.agent.api.routing.SetStaticNatRulesCommand; import com.cloud.agent.api.routing.VmDataCommand; import com.cloud.agent.api.routing.VpnUsersCfgCommand; @@ -2533,7 +2534,14 @@ public class VirtualNetworkApplianceManagerImpl implements VirtualNetworkApplian } } - SetPortForwardingRulesCommand cmd = new SetPortForwardingRulesCommand(rulesTO); + SetPortForwardingRulesCommand cmd = null; + + if (router.getVpcId() != null) { + cmd = new SetPortForwardingRulesVpcCommand(rulesTO); + } else { + cmd = new SetPortForwardingRulesCommand(rulesTO); + } + cmd.setAccessDetail(NetworkElementCommand.ROUTER_IP, getRouterControlIp(router.getId())); cmd.setAccessDetail(NetworkElementCommand.ROUTER_GUEST_IP, getRouterIpInNetwork(guestNetworkId, router.getId())); cmd.setAccessDetail(NetworkElementCommand.ROUTER_NAME, router.getInstanceName()); diff --git a/server/src/com/cloud/network/router/VpcVirtualNetworkApplianceManager.java b/server/src/com/cloud/network/router/VpcVirtualNetworkApplianceManager.java index 68164138d11..3d756bfde8c 100644 --- a/server/src/com/cloud/network/router/VpcVirtualNetworkApplianceManager.java +++ b/server/src/com/cloud/network/router/VpcVirtualNetworkApplianceManager.java @@ -19,7 +19,9 @@ import com.cloud.deploy.DeployDestination; import com.cloud.exception.ConcurrentOperationException; import com.cloud.exception.InsufficientCapacityException; import com.cloud.exception.ResourceUnavailableException; +import com.cloud.network.Network; import com.cloud.network.VpcVirtualNetworkApplianceService; +import com.cloud.network.rules.NetworkACL; import com.cloud.network.vpc.Vpc; import com.cloud.user.Account; import com.cloud.vm.DomainRouterVO; @@ -44,4 +46,14 @@ public interface VpcVirtualNetworkApplianceManager extends VirtualNetworkApplian throws InsufficientCapacityException, ConcurrentOperationException, ResourceUnavailableException; + /** + * @param network + * @param rules + * @param routers + * @return + * @throws ResourceUnavailableException + */ + boolean applyNetworkACLs(Network network, List rules, List routers) + throws ResourceUnavailableException; + } diff --git a/server/src/com/cloud/network/router/VpcVirtualNetworkApplianceManagerImpl.java b/server/src/com/cloud/network/router/VpcVirtualNetworkApplianceManagerImpl.java index 899faf78704..872d1a65725 100644 --- a/server/src/com/cloud/network/router/VpcVirtualNetworkApplianceManagerImpl.java +++ b/server/src/com/cloud/network/router/VpcVirtualNetworkApplianceManagerImpl.java @@ -32,8 +32,10 @@ import com.cloud.agent.api.UnPlugNicAnswer; import com.cloud.agent.api.UnPlugNicCommand; import com.cloud.agent.api.routing.IpAssocVpcCommand; import com.cloud.agent.api.routing.NetworkElementCommand; +import com.cloud.agent.api.routing.SetNetworkACLCommand; import com.cloud.agent.api.routing.SetSourceNatCommand; import com.cloud.agent.api.to.IpAddressTO; +import com.cloud.agent.api.to.NetworkACLTO; import com.cloud.agent.api.to.NicTO; import com.cloud.agent.api.to.VirtualMachineTO; import com.cloud.agent.manager.Commands; @@ -68,6 +70,7 @@ import com.cloud.network.VpcVirtualNetworkApplianceService; import com.cloud.network.addr.PublicIp; import com.cloud.network.dao.PhysicalNetworkDao; import com.cloud.network.router.VirtualRouter.Role; +import com.cloud.network.rules.NetworkACL; import com.cloud.network.vpc.Vpc; import com.cloud.network.vpc.Dao.VpcDao; import com.cloud.network.vpc.Dao.VpcOfferingDao; @@ -704,4 +707,55 @@ public class VpcVirtualNetworkApplianceManagerImpl extends VirtualNetworkApplian return super.finalizeVirtualMachineProfile(profile, dest, context); } + + @Override + public boolean applyNetworkACLs(Network network, final List rules, List routers) + throws ResourceUnavailableException { + if (rules == null || rules.isEmpty()) { + s_logger.debug("No network ACLs to be applied for network " + network.getId()); + return true; + } + return applyRules(network, routers, "network acls", false, null, false, new RuleApplier() { + @Override + public boolean execute(Network network, VirtualRouter router) throws ResourceUnavailableException { + return sendNetworkACLs(router, (List)rules, network.getId()); + } + }); + } + + + protected boolean sendNetworkACLs(VirtualRouter router, List rules, long guestNetworkId) throws ResourceUnavailableException { + Commands cmds = new Commands(OnError.Continue); + createNetworkACLsCommands(rules, router, cmds, guestNetworkId); + return sendCommandsToRouter(router, cmds); + + } + + private void createNetworkACLsCommands(List rules, VirtualRouter router, Commands cmds, long guestNetworkId) { + List rulesTO = null; + String guestVlan = null; + Network guestNtwk = _networkDao.findById(guestNetworkId); + URI uri = guestNtwk.getBroadcastUri(); + if (uri != null) { + guestVlan = guestNtwk.getBroadcastUri().getHost(); + } + + if (rules != null) { + rulesTO = new ArrayList(); + + for (NetworkACL rule : rules) { + NetworkACLTO ruleTO = new NetworkACLTO(rule, guestVlan, rule.getTrafficType()); + rulesTO.add(ruleTO); + } + } + + SetNetworkACLCommand cmd = new SetNetworkACLCommand(rulesTO); + cmd.setAccessDetail(NetworkElementCommand.ROUTER_IP, getRouterControlIp(router.getId())); + cmd.setAccessDetail(NetworkElementCommand.ROUTER_GUEST_IP, getRouterIpInNetwork(guestNetworkId, router.getId())); + cmd.setAccessDetail(NetworkElementCommand.GUEST_VLAN_TAG, guestVlan); + 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 58e8750500b..abf4403b3c6 100644 --- a/server/src/com/cloud/network/rules/FirewallManager.java +++ b/server/src/com/cloud/network/rules/FirewallManager.java @@ -17,7 +17,6 @@ import java.util.List; import com.cloud.exception.NetworkRuleConflictException; 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.FirewallRuleType; import com.cloud.network.rules.FirewallRule.Purpose; @@ -37,13 +36,12 @@ public interface FirewallManager extends FirewallService { * * @param newRule * the new rule created. - * @param ipAddress - * ip address that back up the new rule. * @throws NetworkRuleConflictException */ - void detectRulesConflict(FirewallRule newRule, IpAddress ipAddress) throws NetworkRuleConflictException; + void detectRulesConflict(FirewallRule newRule) throws NetworkRuleConflictException; - void validateFirewallRule(Account caller, IPAddressVO ipAddress, Integer portStart, Integer portEnd, String proto, Purpose purpose, FirewallRuleType type); + void validateFirewallRule(Account caller, IPAddressVO ipAddress, Integer portStart, Integer portEnd, String proto, + Purpose purpose, FirewallRuleType type); boolean applyRules(List rules, boolean continueOnError, boolean updateRulesInDB) throws ResourceUnavailableException; diff --git a/server/src/com/cloud/network/rules/FirewallRuleVO.java b/server/src/com/cloud/network/rules/FirewallRuleVO.java index 7917463008a..a9834ec3371 100644 --- a/server/src/com/cloud/network/rules/FirewallRuleVO.java +++ b/server/src/com/cloud/network/rules/FirewallRuleVO.java @@ -32,6 +32,7 @@ import javax.persistence.Transient; import com.cloud.api.Identity; import com.cloud.network.dao.FirewallRulesCidrsDaoImpl; +import com.cloud.network.rules.FirewallRule.TrafficType; import com.cloud.utils.component.ComponentLocator; import com.cloud.utils.db.GenericDao; import com.cloud.utils.net.NetUtils; @@ -40,7 +41,7 @@ import com.cloud.utils.net.NetUtils; @Table(name="firewall_rules") @Inheritance(strategy=InheritanceType.JOINED) @DiscriminatorColumn(name="purpose", discriminatorType=DiscriminatorType.STRING, length=32) -public class FirewallRuleVO implements FirewallRule, Identity { +public class FirewallRuleVO implements Identity, NetworkACL { protected final FirewallRulesCidrsDaoImpl _firewallRulesCidrsDao = ComponentLocator.inject(FirewallRulesCidrsDaoImpl.class); @Id @@ -59,7 +60,7 @@ public class FirewallRuleVO implements FirewallRule, Identity { long accountId; @Column(name="ip_address_id", updatable=false) - long sourceIpAddressId; + Long sourceIpAddressId; @Column(name="start_port", updatable=false) Integer sourcePortStart; @@ -98,6 +99,10 @@ public class FirewallRuleVO implements FirewallRule, Identity { @Enumerated(value=EnumType.STRING) FirewallRuleType type; + @Column(name="traffic_type") + @Enumerated(value=EnumType.STRING) + TrafficType trafficType; + // This is a delayed load value. If the value is null, // then this field has not been loaded yet. @@ -114,7 +119,7 @@ public class FirewallRuleVO implements FirewallRule, Identity { @Override public List getSourceCidrList() { - if (sourceCidrs == null && purpose == Purpose.Firewall) { + if (sourceCidrs == null && (purpose == Purpose.Firewall || purpose == Purpose.NetworkACL)) { return _firewallRulesCidrsDao.getSourceCidrs(id); } return sourceCidrs; @@ -141,7 +146,7 @@ public class FirewallRuleVO implements FirewallRule, Identity { } @Override - public long getSourceIpAddressId() { + public Long getSourceIpAddressId() { return sourceIpAddressId; } @@ -191,13 +196,20 @@ public class FirewallRuleVO implements FirewallRule, Identity { this.uuid = UUID.randomUUID().toString(); } - public FirewallRuleVO(String xId, long ipAddressId, Integer portStart, Integer portEnd, String protocol, long networkId, long accountId, long domainId, Purpose purpose, List sourceCidrs, Integer icmpCode, Integer icmpType, Long related) { + public FirewallRuleVO(String xId, Long ipAddressId, Integer portStart, Integer portEnd, String protocol, + long networkId, long accountId, long domainId, Purpose purpose, List sourceCidrs, Integer icmpCode, + Integer icmpType, Long related, TrafficType trafficType) { this.xId = xId; if (xId == null) { this.xId = UUID.randomUUID().toString(); } this.accountId = accountId; this.domainId = domainId; + + if (sourceIpAddressId == null) { + assert (purpose == Purpose.NetworkACL) : "ipAddressId can be null for " + Purpose.NetworkACL + " only"; + } + this.sourceIpAddressId = ipAddressId; this.sourcePortStart = portStart; this.sourcePortEnd = portEnd; @@ -216,10 +228,13 @@ public class FirewallRuleVO implements FirewallRule, Identity { this.related = related; this.uuid = UUID.randomUUID().toString(); this.type = FirewallRuleType.User; + this.trafficType = trafficType; } - public FirewallRuleVO(String xId, long ipAddressId, int port, String protocol, long networkId, long accountId, long domainId, Purpose purpose, List sourceCidrs, Integer icmpCode, Integer icmpType, Long related) { - this(xId, ipAddressId, port, port, protocol, networkId, accountId, domainId, purpose, sourceCidrs, icmpCode, icmpType, related); + + public FirewallRuleVO(String xId, long ipAddressId, int port, String protocol, long networkId, long accountId, + long domainId, Purpose purpose, List sourceCidrs, Integer icmpCode, Integer icmpType, Long related) { + this(xId, ipAddressId, port, port, protocol, networkId, accountId, domainId, purpose, sourceCidrs, icmpCode, icmpType, related, null); } @Override @@ -254,4 +269,9 @@ public class FirewallRuleVO implements FirewallRule, Identity { public void setType(FirewallRuleType type) { this.type = type; } + + @Override + public TrafficType getTrafficType() { + return trafficType; + } } diff --git a/server/src/com/cloud/network/rules/PortForwardingRuleVO.java b/server/src/com/cloud/network/rules/PortForwardingRuleVO.java index 848177e3eba..6b76294fdf4 100644 --- a/server/src/com/cloud/network/rules/PortForwardingRuleVO.java +++ b/server/src/com/cloud/network/rules/PortForwardingRuleVO.java @@ -47,7 +47,7 @@ public class PortForwardingRuleVO extends FirewallRuleVO implements PortForwardi } public PortForwardingRuleVO(String xId, long srcIpId, int srcPortStart, int srcPortEnd, Ip dstIp, int dstPortStart, int dstPortEnd, String protocol, long networkId, long accountId, long domainId, long instanceId) { - super(xId, srcIpId, srcPortStart, srcPortEnd, protocol, networkId, accountId, domainId, Purpose.PortForwarding, null, null, null, null); + super(xId, srcIpId, srcPortStart, srcPortEnd, protocol, networkId, accountId, domainId, Purpose.PortForwarding, null, null, null, null, null); this.destinationIpAddress = dstIp; this.virtualMachineId = instanceId; this.destinationPortStart = dstPortStart; diff --git a/server/src/com/cloud/network/rules/RulesManagerImpl.java b/server/src/com/cloud/network/rules/RulesManagerImpl.java index d000ac4e432..74d6940da3e 100755 --- a/server/src/com/cloud/network/rules/RulesManagerImpl.java +++ b/server/src/com/cloud/network/rules/RulesManagerImpl.java @@ -236,7 +236,7 @@ public class RulesManagerImpl implements RulesManager, RulesService, Manager { } try { - _firewallMgr.detectRulesConflict(newRule, ipAddress); + _firewallMgr.detectRulesConflict(newRule); if (!_firewallDao.setStateToAdd(newRule)) { throw new CloudRuntimeException("Unable to update the state to add for " + newRule); } @@ -299,7 +299,7 @@ public class RulesManagerImpl implements RulesManager, RulesService, Manager { txn.start(); FirewallRuleVO newRule = new FirewallRuleVO(rule.getXid(), rule.getSourceIpAddressId(), rule.getSourcePortStart(), rule.getSourcePortEnd(), rule.getProtocol().toLowerCase(), - networkId, accountId, domainId, rule.getPurpose(), null, null, null, null); + networkId, accountId, domainId, rule.getPurpose(), null, null, null, null, null); newRule = _firewallDao.persist(newRule); @@ -309,7 +309,7 @@ public class RulesManagerImpl implements RulesManager, RulesService, Manager { } try { - _firewallMgr.detectRulesConflict(newRule, ipAddress); + _firewallMgr.detectRulesConflict(newRule); if (!_firewallDao.setStateToAdd(newRule)) { throw new CloudRuntimeException("Unable to update the state to add for " + newRule); } @@ -1009,7 +1009,7 @@ public class RulesManagerImpl implements RulesManager, RulesService, Manager { boolean success = false; try { for (FirewallRuleVO newRule : rules) { - _firewallMgr.detectRulesConflict(newRule, ip); + _firewallMgr.detectRulesConflict(newRule); } success = true; return rules; diff --git a/server/src/com/cloud/network/rules/StaticNatRuleImpl.java b/server/src/com/cloud/network/rules/StaticNatRuleImpl.java index 8cbf2b3dad7..cf6783926c4 100644 --- a/server/src/com/cloud/network/rules/StaticNatRuleImpl.java +++ b/server/src/com/cloud/network/rules/StaticNatRuleImpl.java @@ -90,7 +90,7 @@ public class StaticNatRuleImpl implements StaticNatRule{ } @Override - public long getSourceIpAddressId() { + public Long getSourceIpAddressId() { return sourceIpAddressId; } diff --git a/setup/db/create-schema.sql b/setup/db/create-schema.sql index f2b47360a06..83c76c8ef63 100755 --- a/setup/db/create-schema.sql +++ b/setup/db/create-schema.sql @@ -647,7 +647,7 @@ CREATE TABLE `cloud`.`op_dc_vnet_alloc` ( CREATE TABLE `cloud`.`firewall_rules` ( `id` bigint unsigned NOT NULL auto_increment COMMENT 'id', `uuid` varchar(40), - `ip_address_id` bigint unsigned NOT NULL COMMENT 'id of the corresponding ip address', + `ip_address_id` bigint unsigned COMMENT 'id of the corresponding ip address', `start_port` int(10) COMMENT 'starting port of a port range', `end_port` int(10) COMMENT 'end port of a port range', `state` char(32) NOT NULL COMMENT 'current state of this rule', @@ -662,12 +662,15 @@ CREATE TABLE `cloud`.`firewall_rules` ( `icmp_type` int(10) COMMENT 'The ICMP type (if protocol=ICMP). A value of -1 means all types.', `related` bigint unsigned COMMENT 'related to what other firewall rule', `type` varchar(10) NOT NULL DEFAULT 'USER', + `vpc_id` bigint unsigned COMMENT 'vpc the firewall rule is associated with', + `traffic_type` char(32) COMMENT 'the traffic type of the rule, can be Ingress or Egress', PRIMARY KEY (`id`), CONSTRAINT `fk_firewall_rules__ip_address_id` FOREIGN KEY(`ip_address_id`) REFERENCES `user_ip_address`(`id`), CONSTRAINT `fk_firewall_rules__network_id` FOREIGN KEY(`network_id`) REFERENCES `networks`(`id`) ON DELETE CASCADE, CONSTRAINT `fk_firewall_rules__account_id` FOREIGN KEY(`account_id`) REFERENCES `account`(`id`) ON DELETE CASCADE, CONSTRAINT `fk_firewall_rules__domain_id` FOREIGN KEY(`domain_id`) REFERENCES `domain`(`id`) ON DELETE CASCADE, CONSTRAINT `fk_firewall_rules__related` FOREIGN KEY(`related`) REFERENCES `firewall_rules`(`id`) ON DELETE CASCADE, + CONSTRAINT `fk_firewall_rules__vpc_id` FOREIGN KEY (`vpc_id`) REFERENCES `vpc`(`id`) ON DELETE CASCADE, INDEX `i_firewall_rules__purpose`(`purpose`), CONSTRAINT `uc_firewall_rules__uuid` UNIQUE (`uuid`) ) ENGINE=InnoDB DEFAULT CHARSET=utf8;