diff --git a/api/src/com/cloud/agent/api/to/FirewallRuleTO.java b/api/src/com/cloud/agent/api/to/FirewallRuleTO.java index 01860204c00..8ce3def11d6 100644 --- a/api/src/com/cloud/agent/api/to/FirewallRuleTO.java +++ b/api/src/com/cloud/agent/api/to/FirewallRuleTO.java @@ -58,6 +58,7 @@ public class FirewallRuleTO { this(id,null,srcIp,protocol,srcPortStart,srcPortEnd,revoked,alreadyAdded,purpose,sourceCidr,icmpType,icmpCode); } public FirewallRuleTO(long id,String srcVlanTag, String srcIp, String protocol, Integer srcPortStart, Integer srcPortEnd, boolean revoked, boolean alreadyAdded, FirewallRule.Purpose purpose, List sourceCidr,Integer icmpType,Integer icmpCode) { + this.id = id; this.srcVlanTag = srcVlanTag; this.srcIp = srcIp; this.protocol = protocol; diff --git a/api/src/com/cloud/agent/api/to/StaticNatRuleTO.java b/api/src/com/cloud/agent/api/to/StaticNatRuleTO.java index fd77dd69578..6f600149237 100644 --- a/api/src/com/cloud/agent/api/to/StaticNatRuleTO.java +++ b/api/src/com/cloud/agent/api/to/StaticNatRuleTO.java @@ -47,6 +47,11 @@ public class StaticNatRuleTO extends FirewallRuleTO{ super(id, srcIp, protocol, srcPortStart, srcPortEnd, revoked, alreadyAdded, FirewallRule.Purpose.StaticNat, null,0,0); this.dstIp = dstIp; } + + public StaticNatRuleTO(long id,String srcVlanTag, String srcIp, Integer srcPortStart, Integer srcPortEnd, String dstIp, Integer dstPortStart, Integer dstPortEnd, String protocol, boolean revoked, boolean alreadyAdded) { + super(id, srcVlanTag, srcIp, protocol, srcPortStart, srcPortEnd, revoked, alreadyAdded, FirewallRule.Purpose.StaticNat, null,0,0); + this.dstIp = dstIp; + } public String getDstIp() { return dstIp; diff --git a/plugins/network-elements/juniper-srx/src/com/cloud/network/element/JuniperSRXExternalFirewallElement.java b/plugins/network-elements/juniper-srx/src/com/cloud/network/element/JuniperSRXExternalFirewallElement.java index 95bc031726a..50c088c2447 100644 --- a/plugins/network-elements/juniper-srx/src/com/cloud/network/element/JuniperSRXExternalFirewallElement.java +++ b/plugins/network-elements/juniper-srx/src/com/cloud/network/element/JuniperSRXExternalFirewallElement.java @@ -268,7 +268,7 @@ public class JuniperSRXExternalFirewallElement extends ExternalFirewallDeviceMan // Set capabilities for Firewall service Map firewallCapabilities = new HashMap(); - firewallCapabilities.put(Capability.SupportedProtocols, "tcp,udp"); + firewallCapabilities.put(Capability.SupportedProtocols, "tcp,udp,icmp"); firewallCapabilities.put(Capability.MultipleIps, "true"); firewallCapabilities.put(Capability.TrafficStatistics, "per public ip"); capabilities.put(Service.Firewall, firewallCapabilities); diff --git a/plugins/network-elements/juniper-srx/src/com/cloud/network/resource/JuniperSrxResource.java b/plugins/network-elements/juniper-srx/src/com/cloud/network/resource/JuniperSrxResource.java index ebb3de6c850..f823ab8af4f 100644 --- a/plugins/network-elements/juniper-srx/src/com/cloud/network/resource/JuniperSrxResource.java +++ b/plugins/network-elements/juniper-srx/src/com/cloud/network/resource/JuniperSrxResource.java @@ -55,6 +55,7 @@ import com.cloud.agent.api.routing.IpAssocAnswer; import com.cloud.agent.api.routing.IpAssocCommand; import com.cloud.agent.api.routing.NetworkElementCommand; import com.cloud.agent.api.routing.RemoteAccessVpnCfgCommand; +import com.cloud.agent.api.routing.SetFirewallRulesCommand; import com.cloud.agent.api.routing.SetPortForwardingRulesCommand; import com.cloud.agent.api.routing.SetStaticNatRulesCommand; import com.cloud.agent.api.routing.VpnUsersCfgCommand; @@ -64,6 +65,7 @@ import com.cloud.agent.api.to.IpAddressTO; import com.cloud.agent.api.to.PortForwardingRuleTO; import com.cloud.agent.api.to.StaticNatRuleTO; import com.cloud.host.Host; +import com.cloud.network.rules.FirewallRule; import com.cloud.network.rules.FirewallRule.Purpose; import com.cloud.resource.ServerResource; import com.cloud.utils.NumbersUtil; @@ -75,7 +77,6 @@ public class JuniperSrxResource implements ServerResource { private String _name; private String _zoneId; - private String _physicalNetworkId; private String _ip; private String _username; private String _password; @@ -89,6 +90,7 @@ public class JuniperSrxResource implements ServerResource { private Integer _timeoutInSeconds; private String _publicZone; private String _privateZone; + private String _publicZoneInputFilterName; private String _publicInterface; private String _usageInterface; private String _privateInterface; @@ -159,6 +161,9 @@ public class JuniperSrxResource implements ServerResource { ACCESS_PROFILE_ADD("access-profile-add.xml"), ACCESS_PROFILE_GETONE("access-profile-getone.xml"), ACCESS_PROFILE_GETALL("access-profile-getall.xml"), + FIREWALL_FILTER_TERM_ADD("firewall-filter-term-add.xml"), + FIREWALL_FILTER_TERM_GETONE("firewall-filter-term-getone.xml"), + TEMPLATE_ENTRY("template-entry.xml"), OPEN_CONFIGURATION("open-configuration.xml"), CLOSE_CONFIGURATION("close-configuration.xml"), COMMIT("commit.xml"), @@ -231,12 +236,74 @@ public class JuniperSrxResource implements ServerResource { } } + public class FirewallFilterTerm { + private String name; + private List sourceCidrs; + private String destIp; + private String portRange; + private String protocol; + private String icmpType; + private String icmpCode; + private String countName; + + private FirewallFilterTerm(String name, List sourceCidrs, String destIp, String protocol, Integer startPort, Integer endPort, + Integer icmpType, Integer icmpCode, String countName) { + this.name = name; + this.sourceCidrs = sourceCidrs; + this.destIp = destIp; + this.protocol = protocol; + + if (protocol.equals("tcp") || protocol.equals("udp")) { + this.portRange = String.valueOf(startPort) + "-" + String.valueOf(endPort); + } else if (protocol.equals("icmp")) { + this.icmpType = String.valueOf(icmpType); + this.icmpCode = String.valueOf(icmpCode); + } else { + assert protocol.equals("any"); + } + this.countName = countName; + + } + + public String getName() { + return name; + } + + public List getSourceCidrs() { + return sourceCidrs; + } + + public String getDestIp() { + return destIp; + } + + public String getPortRange() { + return portRange; + } + + public String getProtocol() { + return protocol; + } + + public String getIcmpType() { + return icmpType; + } + + public String getIcmpCode() { + return icmpCode; + } + + public String getCountName() { + return countName; + } + } + private enum SrxCommand { LOGIN, OPEN_CONFIGURATION, CLOSE_CONFIGURATION, COMMIT, ROLLBACK, CHECK_IF_EXISTS, CHECK_IF_IN_USE, ADD, DELETE, GET_ALL; } private enum Protocol { - tcp, udp, any; + tcp, udp, icmp, any; } private enum RuleMatchCondition { @@ -277,6 +344,8 @@ public class JuniperSrxResource implements ServerResource { return execute((SetStaticNatRulesCommand) cmd); } else if (cmd instanceof SetPortForwardingRulesCommand) { return execute((SetPortForwardingRulesCommand) cmd); + } else if (cmd instanceof SetFirewallRulesCommand) { + return execute((SetFirewallRulesCommand) cmd); } else if (cmd instanceof ExternalNetworkResourceUsageCommand) { return execute((ExternalNetworkResourceUsageCommand) cmd); } else if (cmd instanceof RemoteAccessVpnCfgCommand) { @@ -300,11 +369,6 @@ public class JuniperSrxResource implements ServerResource { throw new ConfigurationException("Unable to find zone"); } - _physicalNetworkId = (String) params.get("physicalNetworkId"); - if (_physicalNetworkId == null) { - throw new ConfigurationException("Unable to find physical network id in the configuration parameters"); - } - _ip = (String) params.get("ip"); if (_ip == null) { throw new ConfigurationException("Unable to find IP"); @@ -325,8 +389,6 @@ public class JuniperSrxResource implements ServerResource { throw new ConfigurationException("Unable to find public interface."); } - _usageInterface = (String) params.get("usageinterface"); - _privateInterface = (String) params.get("privateinterface"); if (_privateInterface == null) { throw new ConfigurationException("Unable to find private interface."); @@ -364,6 +426,8 @@ public class JuniperSrxResource implements ServerResource { throw new ConfigurationException("Unable to open a connection to the SRX."); } + _publicZoneInputFilterName = _publicZone; + _usageFilterVlanInput = new UsageFilter("vlan-input", null, "vlan-input"); _usageFilterVlanOutput = new UsageFilter("vlan-output", null, "vlan-output"); _usageFilterIPInput = new UsageFilter(_publicZone, "destination-address", "-i"); @@ -712,6 +776,50 @@ public class JuniperSrxResource implements ServerResource { s_logger.debug(msg); } + + /* security policies */ + private synchronized Answer execute(SetFirewallRulesCommand cmd) { + refreshSrxConnection(); + return execute(cmd, _numRetries); + } + + private Answer execute(SetFirewallRulesCommand cmd, int numRetries) { + FirewallRuleTO[] rules = cmd.getRules(); + try { + openConfiguration(); + + for (FirewallRuleTO rule : rules) { + int startPort = 0, endPort = 0; + if (rule.getSrcPortRange() != null) { + startPort = rule.getSrcPortRange()[0]; + endPort = rule.getSrcPortRange()[1]; + } + FirewallFilterTerm term = new FirewallFilterTerm(genIpIdentifier(rule.getSrcIp()) + "-" + String.valueOf(rule.getId()), rule.getSourceCidrList(), + rule.getSrcIp(), rule.getProtocol(), startPort, endPort, + rule.getIcmpType(), rule.getIcmpCode(), genIpIdentifier(rule.getSrcIp()) + _usageFilterIPInput.getCounterIdentifier()); + if (!rule.revoked()) { + manageFirewallFilter(SrxCommand.ADD, term, _publicZoneInputFilterName); + } else { + manageFirewallFilter(SrxCommand.DELETE, term, _publicZoneInputFilterName); + } + } + + commitConfiguration(); + return new Answer(cmd); + } catch (ExecutionException e) { + s_logger.error(e); + closeConfiguration(); + + if (numRetries > 0 && refreshSrxConnection()) { + int numRetriesRemaining = numRetries - 1; + s_logger.debug("Retrying SetFirewallRulesCommand. Number of retries remaining: " + numRetriesRemaining); + return execute(cmd, numRetriesRemaining); + } else { + return new Answer(cmd, e); + } + } + } + /* * Static NAT */ @@ -766,7 +874,6 @@ public class JuniperSrxResource implements ServerResource { private void addStaticNatRule(Long publicVlanTag, String publicIp, String privateIp, List rules) throws ExecutionException { manageProxyArp(SrxCommand.ADD, publicVlanTag, publicIp); manageStaticNatRule(SrxCommand.ADD, publicIp, privateIp); - manageUsageFilter(SrxCommand.ADD, _usageFilterIPInput, publicIp, null, genIpFilterTermName(publicIp)); manageAddressBookEntry(SrxCommand.ADD, _privateZone, privateIp, null); // Add a new security policy with the current set of applications @@ -778,7 +885,6 @@ public class JuniperSrxResource implements ServerResource { private void removeStaticNatRule(Long publicVlanTag, String publicIp, String privateIp) throws ExecutionException { manageStaticNatRule(SrxCommand.DELETE, publicIp, privateIp); manageProxyArp(SrxCommand.DELETE, publicVlanTag, publicIp); - manageUsageFilter(SrxCommand.DELETE, _usageFilterIPInput, publicIp, null, genIpFilterTermName(publicIp)); // Remove any existing security policy and clean up applications removeSecurityPolicyAndApplications(SecurityPolicyType.STATIC_NAT, privateIp); @@ -2781,6 +2887,108 @@ public class JuniperSrxResource implements ServerResource { } } + private String genNameValueEntry(String name, String value) { + String xml = SrxXml.TEMPLATE_ENTRY.getXml(); + xml = replaceXmlValue(xml, "name", name); + xml = replaceXmlValue(xml, "value", value); + return xml; + } + + private String genMultipleEntries(String name, List values) { + String result = ""; + for (String value : values) { + result = result + genNameValueEntry(name, value); + } + return result; + } + + private String genPortRangeEntry(String protocol, String portRange) { + String result = ""; + result = result + genNameValueEntry("protocol", protocol); + result = result + genNameValueEntry("destination-port", portRange); + return result; + } + + private String genIcmpEntries(String icmpType, String icmpCode) { + String result = ""; + result = result + genNameValueEntry("protocol", "icmp"); + if (icmpType.equals("-1")) { + result = result + genNameValueEntry("icmp-type", "0-255"); + } else { + result = result + genNameValueEntry("icmp-type", icmpType); + } + if (icmpCode.equals("-1")) { + result = result + genNameValueEntry("icmp-code", "0-255"); + } else { + result = result + genNameValueEntry("icmp-code", icmpCode); + } + return result; + } + + private boolean manageFirewallFilter(SrxCommand command, FirewallFilterTerm term, String filterName) throws ExecutionException { + String xml; + + switch(command) { + + case CHECK_IF_EXISTS: + xml = SrxXml.FIREWALL_FILTER_TERM_GETONE.getXml(); + xml = setDelete(xml, false); + xml = replaceXmlValue(xml, "filter-name", filterName); + xml = replaceXmlValue(xml, "term-name", term.getName()); + return sendRequestAndCheckResponse(command, xml, "name", term.getName()); + + case ADD: + if (manageFirewallFilter(SrxCommand.CHECK_IF_EXISTS, term, filterName)) { + return true; + } + + xml = SrxXml.FIREWALL_FILTER_TERM_ADD.getXml(); + + xml = replaceXmlValue(xml, "filter-name", filterName); + xml = replaceXmlValue(xml, "term-name", term.getName()); + xml = replaceXmlValue(xml, "source-address-entries", genMultipleEntries("source-address", term.getSourceCidrs())); + xml = replaceXmlValue(xml, "dest-ip-address", term.getDestIp()); + + String protocol = term.getProtocol(); + if (protocol.equals("tcp") || protocol.equals("udp")) { + xml = replaceXmlValue(xml, "protocol-options", genPortRangeEntry(protocol, term.getPortRange())); + } else if (protocol.equals("icmp")) { + xml = replaceXmlValue(xml, "protocol-options", genIcmpEntries(term.getIcmpType(), term.getIcmpCode())); + } else { + assert protocol.equals("any"); + xml = replaceXmlValue(xml, "protocol-options", ""); + } + xml = replaceXmlValue(xml, "count-name", term.getCountName()); + + if (!sendRequestAndCheckResponse(command, xml)) { + throw new ExecutionException("Failed to add firewall filter: " + term.getName()); + } else { + return true; + } + + case DELETE: + if (!manageFirewallFilter(SrxCommand.CHECK_IF_EXISTS, term, filterName)) { + return true; + } + + xml = SrxXml.FIREWALL_FILTER_TERM_GETONE.getXml(); + xml = setDelete(xml, true); + xml = replaceXmlValue(xml, "filter-name", filterName); + xml = replaceXmlValue(xml, "term-name", term.getName()); + + if (!sendRequestAndCheckResponse(command, xml)) { + throw new ExecutionException("Failed to delete firewall filter: " + term.getName()); + } else { + return true; + } + + default: + s_logger.debug("Unrecognized command."); + return false; + + } + } + /* * Usage */ @@ -2913,6 +3121,10 @@ public class JuniperSrxResource implements ServerResource { } UsageFilter filter = getUsageFilter(counterName); + if (filter == null) { + s_logger.debug("Failed to parse counter name in usage answer: " + counterName); + return; + } String usageAnswerKey = getUsageAnswerKey(filter, counterName); Map bytesMap = getBytesMap(answer, filter, usageAnswerKey); updateBytesMap(bytesMap, filter, usageAnswerKey, byteCount); @@ -2981,6 +3193,11 @@ public class JuniperSrxResource implements ServerResource { } private boolean checkResponse(String xmlResponse, boolean errorKeyAndValue, String key, String value) { + if (xmlResponse == null) { + s_logger.error("Failed to communicate with SRX!"); + return false; + } + if (!xmlResponse.contains("authentication-response")) { s_logger.debug("Checking response: " + xmlResponse); } else { diff --git a/scripts/network/juniper/firewall-filter-term-add.xml b/scripts/network/juniper/firewall-filter-term-add.xml new file mode 100644 index 00000000000..4f96ed4d16e --- /dev/null +++ b/scripts/network/juniper/firewall-filter-term-add.xml @@ -0,0 +1,25 @@ + + + + + +%filter-name% + +%term-name% + +%source-address-entries% + +%dest-ip-address% + +%protocol-options% + + +%count-name% + + + + + + + + diff --git a/scripts/network/juniper/firewall-filter-term-getone.xml b/scripts/network/juniper/firewall-filter-term-getone.xml new file mode 100644 index 00000000000..782290992c1 --- /dev/null +++ b/scripts/network/juniper/firewall-filter-term-getone.xml @@ -0,0 +1,14 @@ + + + + + +%filter-name% + +%term-name% + + + + + + diff --git a/scripts/network/juniper/template-entry.xml b/scripts/network/juniper/template-entry.xml new file mode 100644 index 00000000000..cf0cb6c37e3 --- /dev/null +++ b/scripts/network/juniper/template-entry.xml @@ -0,0 +1,3 @@ +<%name%> +%value% + diff --git a/server/src/com/cloud/network/ExternalFirewallDeviceManager.java b/server/src/com/cloud/network/ExternalFirewallDeviceManager.java index f6a39d96cea..f758ca1d341 100644 --- a/server/src/com/cloud/network/ExternalFirewallDeviceManager.java +++ b/server/src/com/cloud/network/ExternalFirewallDeviceManager.java @@ -22,6 +22,7 @@ import com.cloud.exception.InsufficientCapacityException; import com.cloud.exception.ResourceUnavailableException; import com.cloud.host.Host; import com.cloud.network.rules.FirewallRule; +import com.cloud.network.rules.PortForwardingRule; import com.cloud.resource.ServerResource; import com.cloud.utils.component.Manager; @@ -94,4 +95,12 @@ public interface ExternalFirewallDeviceManager extends Manager { */ public boolean manageGuestNetworkWithExternalFirewall(boolean add, Network guestConfig) throws ResourceUnavailableException, InsufficientCapacityException; + /** + * applies port forwarding rules + * @param network guest network if + * @param rules load balancer rules + * @return true if successfully applied rules + * @throws ResourceUnavailableException + */ + public boolean applyPortForwardingRules(Network network, List rules) throws ResourceUnavailableException; } diff --git a/server/src/com/cloud/network/ExternalFirewallDeviceManagerImpl.java b/server/src/com/cloud/network/ExternalFirewallDeviceManagerImpl.java index 1629dd5e5f3..4b67840db3a 100644 --- a/server/src/com/cloud/network/ExternalFirewallDeviceManagerImpl.java +++ b/server/src/com/cloud/network/ExternalFirewallDeviceManagerImpl.java @@ -34,9 +34,11 @@ import com.cloud.agent.api.StartupExternalLoadBalancerCommand; import com.cloud.agent.api.routing.IpAssocCommand; import com.cloud.agent.api.routing.NetworkElementCommand; import com.cloud.agent.api.routing.RemoteAccessVpnCfgCommand; +import com.cloud.agent.api.routing.SetFirewallRulesCommand; import com.cloud.agent.api.routing.SetPortForwardingRulesCommand; import com.cloud.agent.api.routing.SetStaticNatRulesCommand; import com.cloud.agent.api.routing.VpnUsersCfgCommand; +import com.cloud.agent.api.to.FirewallRuleTO; import com.cloud.agent.api.to.IpAddressTO; import com.cloud.agent.api.to.PortForwardingRuleTO; import com.cloud.agent.api.to.StaticNatRuleTO; @@ -65,6 +67,7 @@ import com.cloud.network.Network.Capability; import com.cloud.network.Network.Service; import com.cloud.network.Networks.TrafficType; import com.cloud.network.dao.ExternalFirewallDeviceDao; +import com.cloud.network.dao.FirewallRulesDao; import com.cloud.network.dao.IPAddressDao; import com.cloud.network.dao.InlineLoadBalancerNicMapDao; import com.cloud.network.dao.LoadBalancerDao; @@ -78,14 +81,17 @@ import com.cloud.network.dao.VpnUserDao; import com.cloud.network.rules.FirewallRule; import com.cloud.network.rules.FirewallRule.Purpose; import com.cloud.network.rules.PortForwardingRule; +import com.cloud.network.rules.StaticNat; import com.cloud.network.rules.StaticNatRule; import com.cloud.network.rules.dao.PortForwardingRulesDao; import com.cloud.offering.NetworkOffering; import com.cloud.offerings.dao.NetworkOfferingDao; import com.cloud.resource.ResourceManager; +import com.cloud.resource.ResourceState; import com.cloud.resource.ResourceStateAdapter; import com.cloud.resource.ServerResource; import com.cloud.resource.UnableDeleteHostException; +import com.cloud.resource.ResourceStateAdapter.DeleteHostAnswer; import com.cloud.server.api.response.ExternalFirewallResponse; import com.cloud.user.Account; import com.cloud.user.AccountManager; @@ -98,6 +104,7 @@ import com.cloud.utils.db.DB; import com.cloud.utils.db.GlobalLock; import com.cloud.utils.db.Transaction; import com.cloud.utils.exception.CloudRuntimeException; +import com.cloud.utils.net.Ip; import com.cloud.utils.net.NetUtils; import com.cloud.utils.net.UrlUtil; import com.cloud.vm.Nic.ReservationStrategy; @@ -133,6 +140,7 @@ public abstract class ExternalFirewallDeviceManagerImpl extends AdapterBase impl @Inject NetworkExternalFirewallDao _networkExternalFirewallDao; @Inject VpnUserDao _vpnUsersDao; @Inject HostDetailsDao _hostDetailDao; + @Inject FirewallRulesDao _fwRulesDao; private static final org.apache.log4j.Logger s_logger = Logger.getLogger(ExternalFirewallDeviceManagerImpl.class); private long _defaultFwCapacity; @@ -236,14 +244,26 @@ public abstract class ExternalFirewallDeviceManagerImpl extends AdapterBase impl throw new InvalidParameterValueException("Could not find an external firewall with ID: " + hostId); } + DetailVO fwHostDetails = _hostDetailDao.findDetail(hostId, ApiConstants.FIREWALL_DEVICE_ID); + long fwDeviceId = Long.parseLong(fwHostDetails.getValue()); + + // check if any networks are using this balancer device + List networks = _networkExternalFirewallDao.listByFirewallDeviceId(fwDeviceId); + if ((networks != null) && !networks.isEmpty()) { + throw new CloudRuntimeException("Delete can not be done as there are networks using the firewall device "); + } + try { - if (_resourceMgr.maintain(hostId) && _resourceMgr.deleteHost(hostId, false, false)) { - return true; - } else { - return false; - } - } catch (AgentUnavailableException e) { - s_logger.debug(e); + // put the host in maintenance state in order for it to be deleted + externalFirewall.setResourceState(ResourceState.Maintenance); + _hostDao.update(hostId, externalFirewall); + _resourceMgr.deleteHost(hostId, false, false); + + // delete the external load balancer entry + _externalFirewallDeviceDao.remove(fwDeviceId); + return true; + } catch (Exception e) { + s_logger.debug("Failed to delete external firewall device due to " + e.getMessage()); return false; } } @@ -316,6 +336,32 @@ public abstract class ExternalFirewallDeviceManagerImpl extends AdapterBase impl " to implement the network", DataCenter.class, network.getDataCenterId()); } + @DB + protected boolean freeFirewallForNetwork(Network network) { + Transaction txn = Transaction.currentTxn(); + GlobalLock deviceMapLock = GlobalLock.getInternLock("NetworkFirewallDeviceMap"); + try { + if (deviceMapLock.lock(120)) { + try { + NetworkExternalFirewallVO fwDeviceForNetwork = _networkExternalFirewallDao.findByNetworkId(network.getId()); + if (fwDeviceForNetwork != null) { + _networkExternalFirewallDao.remove(fwDeviceForNetwork.getId()); + } + } catch (Exception exception) { + txn.rollback(); + s_logger.error("Failed to release firewall device for the network" + network.getId() + " due to " + exception.getMessage()); + return false; + } finally { + deviceMapLock.unlock(); + } + } + } finally { + deviceMapLock.releaseRef(); + } + txn.commit(); + return true; + } + public String getExternalNetworkResourceGuid(long physicalNetworkId, String deviceName, String ip) { return physicalNetworkId + "-" + deviceName + "-" + ip; } @@ -422,15 +468,21 @@ public abstract class ExternalFirewallDeviceManagerImpl extends AdapterBase impl cmd.setAccessDetail(NetworkElementCommand.GUEST_VLAN_TAG, String.valueOf(guestVlanTag)); Answer answer = _agentMgr.easySend(externalFirewall.getId(), cmd); + List reservedIpAddressesForGuestNetwork = _nicDao.listIpAddressInNetwork(network.getId()); + if (answer == null || !answer.getResult()) { String action = add ? "implement" : "shutdown"; String answerDetails = (answer != null) ? answer.getDetails() : "answer was null"; String msg = "External firewall was unable to " + action + " the guest network on the external firewall in zone " + zone.getName() + " due to " + answerDetails; s_logger.error(msg); + if (!add && (!reservedIpAddressesForGuestNetwork.contains(network.getGateway()))) { + // If we failed the implementation as well, then just return, no complain + s_logger.error("Skip the shutdown of guest network on SRX because it seems we didn't implement it as well"); + return true; + } throw new ResourceUnavailableException(msg, DataCenter.class, zoneId); } - List reservedIpAddressesForGuestNetwork = _nicDao.listIpAddressInNetwork(network.getId()); if (add && (!reservedIpAddressesForGuestNetwork.contains(network.getGateway()))) { // Insert a new NIC for this guest network to reserve the gateway address savePlaceholderNic(network, network.getGateway()); @@ -447,6 +499,19 @@ public abstract class ExternalFirewallDeviceManagerImpl extends AdapterBase impl } } + // on network shutdown, delete placeHolder nics used for the firewall device + if (!add) { + List guestIps = _nicDao.listByNetworkId(network.getId()); + for (NicVO guestIp : guestIps) { + // only external firewall and external load balancer will create NicVO with PlaceHolder reservation strategy + if (guestIp.getReservationStrategy().equals(ReservationStrategy.PlaceHolder) && guestIp.getIp4Address().equals(network.getGateway())) { + _nicDao.remove(guestIp.getId()); + } + } + + freeFirewallForNetwork(network); + } + String action = add ? "implemented" : "shut down"; s_logger.debug("External firewall has " + action + " the guest network for account " + account.getAccountName() + "(id = " + account.getAccountId() + ") with VLAN tag " + guestVlanTag); @@ -456,6 +521,37 @@ public abstract class ExternalFirewallDeviceManagerImpl extends AdapterBase impl @Override public boolean applyFirewallRules(Network network, List rules) throws ResourceUnavailableException { // Find the external firewall in this zone + long zoneId = network.getDataCenterId(); + DataCenterVO zone = _dcDao.findById(zoneId); + ExternalFirewallDeviceVO fwDeviceVO = getExternalFirewallForNetwork(network); + // During destroy, device reference may already been clean up, then we just return true + if (fwDeviceVO == null) { + return true; + } + HostVO externalFirewall = _hostDao.findById(fwDeviceVO.getHostId()); + + assert(externalFirewall != null); + + if (network.getState() == Network.State.Allocated) { + s_logger.debug("External firewall was asked to apply firewall rules for network with ID " + network.getId() + "; this network is not implemented. Skipping backend commands."); + return true; + } + + List rulesTO = new ArrayList(); + + for (FirewallRule rule : rules) { + IpAddress sourceIp = _networkMgr.getIp(rule.getSourceIpAddressId()); + FirewallRuleTO ruleTO = new FirewallRuleTO(rule, null, sourceIp.getAddress().addr()); + rulesTO.add(ruleTO); + } + + //Firewall rules configured for staticNAT/PF + sendFirewallRules(rulesTO, zone, externalFirewall.getId()); + + return true; + } + + public boolean applyStaticNatRules(Network network, List rules) throws ResourceUnavailableException { long zoneId = network.getDataCenterId(); DataCenterVO zone = _dcDao.findById(zoneId); ExternalFirewallDeviceVO fwDeviceVO = getExternalFirewallForNetwork(network); @@ -469,32 +565,34 @@ public abstract class ExternalFirewallDeviceManagerImpl extends AdapterBase impl } List staticNatRules = new ArrayList(); - List portForwardingRules = new ArrayList(); - - for (FirewallRule rule : rules) { + + for (StaticNat rule : rules) { IpAddress sourceIp = _networkMgr.getIp(rule.getSourceIpAddressId()); Vlan vlan = _vlanDao.findById(sourceIp.getVlanId()); - if (rule.getPurpose() == Purpose.StaticNat) { - StaticNatRule staticNatRule = (StaticNatRule) rule; - StaticNatRuleTO ruleTO = new StaticNatRuleTO(staticNatRule, vlan.getVlanTag(), sourceIp.getAddress().addr(), staticNatRule.getDestIpAddress()); - staticNatRules.add(ruleTO); - } else if (rule.getPurpose() == Purpose.PortForwarding) { - PortForwardingRuleTO ruleTO = new PortForwardingRuleTO((PortForwardingRule) rule, vlan.getVlanTag(), sourceIp.getAddress().addr()); - portForwardingRules.add(ruleTO); - } + StaticNatRuleTO ruleTO = new StaticNatRuleTO(0,vlan.getVlanTag(), sourceIp.getAddress().addr(), -1, -1, rule.getDestIpAddress(), -1, -1, "any", rule.isForRevoke(), false); + staticNatRules.add(ruleTO); } - - // Apply static nat rules - applyStaticNatRules(staticNatRules, zone, externalFirewall.getId()); - - // apply port forwarding rules - applyPortForwardingRules(portForwardingRules, zone, externalFirewall.getId()); - + + sendStaticNatRules(staticNatRules, zone, externalFirewall.getId()); + return true; } + + protected void sendFirewallRules(List firewallRules, DataCenter zone, long externalFirewallId) throws ResourceUnavailableException { + if (!firewallRules.isEmpty()) { + SetFirewallRulesCommand cmd = new SetFirewallRulesCommand(firewallRules); + Answer answer = _agentMgr.easySend(externalFirewallId, cmd); + if (answer == null || !answer.getResult()) { + String details = (answer != null) ? answer.getDetails() : "details unavailable"; + String msg = "External firewall was unable to apply static nat rules to the SRX appliance in zone " + zone.getName() + " due to: " + details + "."; + s_logger.error(msg); + throw new ResourceUnavailableException(msg, DataCenter.class, zone.getId()); + } + } + } - protected void applyStaticNatRules(List staticNatRules, DataCenter zone, long externalFirewallId) throws ResourceUnavailableException { + protected void sendStaticNatRules(List staticNatRules, DataCenter zone, long externalFirewallId) throws ResourceUnavailableException { if (!staticNatRules.isEmpty()) { SetStaticNatRulesCommand cmd = new SetStaticNatRulesCommand(staticNatRules, null); Answer answer = _agentMgr.easySend(externalFirewallId, cmd); @@ -507,7 +605,7 @@ public abstract class ExternalFirewallDeviceManagerImpl extends AdapterBase impl } } - protected void applyPortForwardingRules(List portForwardingRules, DataCenter zone, long externalFirewallId) throws ResourceUnavailableException { + protected void sendPortForwardingRules(List portForwardingRules, DataCenter zone, long externalFirewallId) throws ResourceUnavailableException { if (!portForwardingRules.isEmpty()) { SetPortForwardingRulesCommand cmd = new SetPortForwardingRulesCommand(portForwardingRules); Answer answer = _agentMgr.easySend(externalFirewallId, cmd); @@ -648,7 +746,38 @@ public abstract class ExternalFirewallDeviceManagerImpl extends AdapterBase impl @Override public DeleteHostAnswer deleteHost(HostVO host, boolean isForced, boolean isForceDeleteStorage) throws UnableDeleteHostException { - // TODO Auto-generated method stub - return null; + if (host.getType() != com.cloud.host.Host.Type.ExternalFirewall) { + return null; + } + return new DeleteHostAnswer(true); + } + + @Override + public boolean applyPortForwardingRules(Network network, List rules) throws ResourceUnavailableException { + // Find the external firewall in this zone + long zoneId = network.getDataCenterId(); + DataCenterVO zone = _dcDao.findById(zoneId); + ExternalFirewallDeviceVO fwDeviceVO = getExternalFirewallForNetwork(network); + HostVO externalFirewall = _hostDao.findById(fwDeviceVO.getHostId()); + + assert(externalFirewall != null); + + if (network.getState() == Network.State.Allocated) { + s_logger.debug("External firewall was asked to apply firewall rules for network with ID " + network.getId() + "; this network is not implemented. Skipping backend commands."); + return true; + } + + List pfRules = new ArrayList(); + + for (PortForwardingRule rule : rules) { + IpAddress sourceIp = _networkMgr.getIp(rule.getSourceIpAddressId()); + Vlan vlan = _vlanDao.findById(sourceIp.getVlanId()); + + PortForwardingRuleTO ruleTO = new PortForwardingRuleTO(rule, vlan.getVlanTag(), sourceIp.getAddress().addr()); + pfRules.add(ruleTO); + } + + sendPortForwardingRules(pfRules, zone, externalFirewall.getId()); + return true; } }