You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@cloudstack.apache.org by ya...@apache.org on 2013/11/06 19:08:42 UTC
[3/5] Squashed commit of the Palo Alto Networks firewall integration
plugin.
http://git-wip-us.apache.org/repos/asf/cloudstack/blob/8f8ad3f3/plugins/network-elements/palo-alto/src/com/cloud/network/resource/PaloAltoResource.java
----------------------------------------------------------------------
diff --git a/plugins/network-elements/palo-alto/src/com/cloud/network/resource/PaloAltoResource.java b/plugins/network-elements/palo-alto/src/com/cloud/network/resource/PaloAltoResource.java
new file mode 100644
index 0000000..2251ce0
--- /dev/null
+++ b/plugins/network-elements/palo-alto/src/com/cloud/network/resource/PaloAltoResource.java
@@ -0,0 +1,2030 @@
+// Licensed to the Apache Software Foundation (ASF) under one
+// or more contributor license agreements. See the NOTICE file
+// distributed with this work for additional information
+// regarding copyright ownership. The ASF licenses this file
+// to you under the Apache License, Version 2.0 (the
+// "License"); you may not use this file except in compliance
+// with 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.
+package com.cloud.network.resource;
+
+import java.io.BufferedReader;
+import java.io.FileReader;
+import java.io.IOException;
+import java.io.StringReader;
+import java.util.ArrayList;
+import java.util.HashMap;
+import java.util.List;
+import java.util.Map;
+import org.apache.log4j.Logger;
+import org.w3c.dom.Document;
+import org.w3c.dom.Node;
+import org.w3c.dom.NodeList;
+import org.xml.sax.InputSource;
+import javax.naming.ConfigurationException;
+import javax.xml.parsers.DocumentBuilderFactory;
+
+import com.cloud.agent.IAgentControl;
+import com.cloud.agent.api.Answer;
+import com.cloud.agent.api.Command;
+import com.cloud.agent.api.ExternalNetworkResourceUsageAnswer;
+import com.cloud.agent.api.ExternalNetworkResourceUsageCommand;
+import com.cloud.agent.api.MaintainAnswer;
+import com.cloud.agent.api.MaintainCommand;
+import com.cloud.agent.api.PingCommand;
+import com.cloud.agent.api.ReadyAnswer;
+import com.cloud.agent.api.ReadyCommand;
+import com.cloud.agent.api.StartupCommand;
+import com.cloud.agent.api.StartupExternalFirewallCommand;
+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.SetFirewallRulesCommand;
+import com.cloud.agent.api.routing.SetPortForwardingRulesCommand;
+import com.cloud.agent.api.routing.SetStaticNatRulesCommand;
+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;
+import com.cloud.host.Host;
+import com.cloud.network.rules.FirewallRule;
+import com.cloud.network.rules.FirewallRule.TrafficType;
+import com.cloud.network.rules.FirewallRule.Purpose;
+import com.cloud.resource.ServerResource;
+import com.cloud.utils.NumbersUtil;
+import com.cloud.utils.exception.ExecutionException;
+import com.cloud.utils.net.NetUtils;
+import com.cloud.utils.script.Script;
+
+// http client handling
+import org.apache.http.client.ResponseHandler;
+import org.apache.http.client.HttpClient;
+import org.apache.http.client.methods.HttpGet;
+import org.apache.http.client.methods.HttpPost;
+import org.apache.http.impl.client.BasicResponseHandler;
+import org.apache.http.impl.client.DefaultHttpClient;
+import org.apache.http.NameValuePair;
+import org.apache.http.message.BasicNameValuePair;
+import org.apache.http.client.entity.UrlEncodedFormEntity;
+import org.apache.http.protocol.HTTP;
+import java.io.UnsupportedEncodingException;
+import java.net.URLEncoder;
+import java.net.URLDecoder;
+import javax.xml.xpath.XPathFactory;
+import javax.xml.xpath.XPath;
+import javax.xml.xpath.XPathExpression;
+import javax.xml.xpath.XPathConstants;
+import javax.xml.xpath.XPathExpressionException;
+import com.cloud.network.utils.HttpClientWrapper;
+
+// for prettyFormat()
+import javax.xml.transform.stream.StreamSource;
+import javax.xml.transform.stream.StreamResult;
+import javax.xml.transform.TransformerFactory;
+import javax.xml.transform.Transformer;
+import javax.xml.transform.OutputKeys;
+import javax.xml.transform.Source;
+import java.io.StringWriter;
+
+public class PaloAltoResource implements ServerResource {
+
+ private String _name;
+ private String _zoneId;
+ private String _ip;
+ private String _username;
+ private String _password;
+ private String _guid;
+ private String _key;
+ private Integer _numRetries;
+ private Integer _timeoutInSeconds;
+ private String _publicZone;
+ private String _privateZone;
+ private String _publicInterface;
+ private String _privateInterface;
+ private String _publicInterfaceType;
+ private String _privateInterfaceType;
+ private String _virtualRouter;
+ private String _threatProfile;
+ private String _logProfile;
+ private String _pingManagementProfile;
+ private final Logger s_logger = Logger.getLogger(PaloAltoResource.class);
+
+ private static String _apiUri = "/api";
+ private static HttpClient _httpclient;
+
+ protected enum PaloAltoMethod {
+ GET, POST;
+ }
+
+ private enum PaloAltoPrimative {
+ CHECK_IF_EXISTS, ADD, DELETE;
+ }
+
+ private enum InterfaceType {
+ AGGREGATE("aggregate-ethernet"),
+ ETHERNET("ethernet");
+
+ private String type;
+
+ private InterfaceType(String type) {
+ this.type = type;
+ }
+ public String toString() {
+ return type;
+ }
+ }
+
+ private enum Protocol {
+ TCP("tcp"),
+ UDP("udp"),
+ ICMP("icmp"),
+ ALL("all");
+
+ private String protocol;
+
+ private Protocol(String protocol) {
+ this.protocol = protocol;
+ }
+ public String toString() {
+ return protocol;
+ }
+ }
+
+ private enum GuestNetworkType {
+ SOURCE_NAT,
+ INTERFACE_NAT;
+ }
+
+ public Answer executeRequest(Command cmd) {
+ if (cmd instanceof ReadyCommand) {
+ return execute((ReadyCommand) cmd);
+ } else if (cmd instanceof MaintainCommand) {
+ return execute((MaintainCommand) cmd);
+ } else if (cmd instanceof IpAssocCommand) {
+ return execute((IpAssocCommand) cmd);
+ } else if (cmd instanceof SetStaticNatRulesCommand) {
+ 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 {
+ return Answer.createUnsupportedCommandAnswer(cmd);
+ }
+ }
+
+ public boolean configure(String name, Map<String, Object> params) throws ConfigurationException {
+ try {
+ _name = (String) params.get("name");
+ if (_name == null) {
+ throw new ConfigurationException("Unable to find name");
+ }
+
+ _zoneId = (String) params.get("zoneId");
+ if (_zoneId == null) {
+ throw new ConfigurationException("Unable to find zone");
+ }
+
+ _ip = (String) params.get("ip");
+ if (_ip == null) {
+ throw new ConfigurationException("Unable to find IP");
+ }
+
+ _username = (String) params.get("username");
+ if (_username == null) {
+ throw new ConfigurationException("Unable to find username");
+ }
+
+ _password = (String) params.get("password");
+ if (_password == null) {
+ throw new ConfigurationException("Unable to find password");
+ }
+
+ _publicInterface = (String) params.get("publicinterface");
+ if (_publicInterface == null) {
+ throw new ConfigurationException("Unable to find public interface.");
+ }
+
+ _privateInterface = (String) params.get("privateinterface");
+ if (_privateInterface == null) {
+ throw new ConfigurationException("Unable to find private interface.");
+ }
+
+ _publicZone = (String) params.get("publicnetwork");
+ if (_publicZone == null) {
+ throw new ConfigurationException("Unable to find public zone");
+ }
+
+ _privateZone = (String) params.get("privatenetwork");
+ if (_privateZone == null) {
+ throw new ConfigurationException("Unable to find private zone");
+ }
+
+ _virtualRouter = (String) params.get("pavr");
+ if (_virtualRouter == null) {
+ throw new ConfigurationException("Unable to find virtual router");
+ }
+
+ _threatProfile = (String) params.get("patp");
+ _logProfile = (String) params.get("palp");
+
+ _guid = (String) params.get("guid");
+ if (_guid == null) {
+ throw new ConfigurationException("Unable to find the guid");
+ }
+
+ _numRetries = NumbersUtil.parseInt((String) params.get("numretries"), 1);
+ _timeoutInSeconds = NumbersUtil.parseInt((String) params.get("timeout"), 300);
+
+ // Open a socket and login
+ if (!refreshPaloAltoConnection()) {
+ throw new ConfigurationException("Unable to open a connection to the Palo Alto.");
+ }
+
+ // check that the threat profile exists if one was specified
+ if (_threatProfile != null) {
+ try {
+ boolean has_profile = getThreatProfile(_threatProfile);
+ if (!has_profile) {
+ throw new ConfigurationException("The specified threat profile group does not exist.");
+ }
+ } catch (ExecutionException e) {
+ throw new ConfigurationException(e.getMessage());
+ }
+ }
+
+ // check that the log profile exists if one was specified
+ if (_logProfile != null) {
+ try {
+ boolean has_profile = getLogProfile(_logProfile);
+ if (!has_profile) {
+ throw new ConfigurationException("The specified log profile does not exist.");
+ }
+ } catch (ExecutionException e) {
+ throw new ConfigurationException(e.getMessage());
+ }
+ }
+
+ // get public interface type
+ try {
+ _publicInterfaceType = getInterfaceType(_publicInterface);
+ if (_publicInterfaceType.equals("")) {
+ throw new ConfigurationException("The specified public interface is not configured on the Palo Alto.");
+ }
+ } catch (ExecutionException e) {
+ throw new ConfigurationException(e.getMessage());
+ }
+
+ // get private interface type
+ try {
+ _privateInterfaceType = getInterfaceType(_privateInterface);
+ if (_privateInterfaceType.equals("")) {
+ throw new ConfigurationException("The specified private interface is not configured on the Palo Alto.");
+ }
+ } catch (ExecutionException e) {
+ throw new ConfigurationException(e.getMessage());
+ }
+
+ _pingManagementProfile = "Ping";
+ try {
+ ArrayList<IPaloAltoCommand> cmdList = new ArrayList<IPaloAltoCommand>();
+ managePingProfile(cmdList, PaloAltoPrimative.ADD);
+ boolean status = requestWithCommit(cmdList);
+ } catch (ExecutionException e) {
+ throw new ConfigurationException(e.getMessage());
+ }
+
+ return true;
+ } catch (Exception e) {
+ throw new ConfigurationException(e.getMessage());
+ }
+
+ }
+
+ public StartupCommand[] initialize() {
+ StartupExternalFirewallCommand cmd = new StartupExternalFirewallCommand();
+ cmd.setName(_name);
+ cmd.setDataCenter(_zoneId);
+ cmd.setPod("");
+ cmd.setPrivateIpAddress(_ip);
+ cmd.setStorageIpAddress("");
+ cmd.setVersion(PaloAltoResource.class.getPackage().getImplementationVersion());
+ cmd.setGuid(_guid);
+ return new StartupCommand[]{cmd};
+ }
+
+ public Host.Type getType() {
+ return Host.Type.ExternalFirewall;
+ }
+
+ @Override
+ public String getName() {
+ return _name;
+ }
+
+ @Override
+ public boolean start() {
+ return true;
+ }
+
+ @Override
+ public boolean stop() {
+ return true;
+ }
+
+ @Override
+ public PingCommand getCurrentStatus(final long id) {
+ return new PingCommand(Host.Type.ExternalFirewall, id);
+ }
+
+ @Override
+ public void disconnected() {
+ // nothing for now...
+ }
+
+ public IAgentControl getAgentControl() {
+ return null;
+ }
+
+ public void setAgentControl(IAgentControl agentControl) {
+ return;
+ }
+
+ /*
+ * Login
+ */
+ private void openHttpConnection(){
+ _httpclient = new DefaultHttpClient();
+
+ // Allows you to connect via SSL using unverified certs
+ _httpclient = HttpClientWrapper.wrapClient(_httpclient);
+ }
+
+ private boolean refreshPaloAltoConnection() {
+ if (_httpclient == null) {
+ openHttpConnection();
+ }
+
+ try {
+ return login(_username, _password);
+ } catch (ExecutionException e) {
+ s_logger.error("Failed to login due to " + e.getMessage());
+ return false;
+ }
+ }
+
+ private boolean login(String username, String password) throws ExecutionException {
+ Map<String, String> params = new HashMap<String, String>();
+ params.put("type", "keygen");
+ params.put("user", username);
+ params.put("password", password);
+
+ String keygenBody;
+ try {
+ keygenBody = request(PaloAltoMethod.GET, params);
+ } catch (ExecutionException e) {
+ return false;
+ }
+ Document keygen_doc = getDocument(keygenBody);
+ XPath xpath = XPathFactory.newInstance().newXPath();
+ try {
+ XPathExpression expr = xpath.compile("/response[@status='success']/result/key/text()");
+ _key = (String) expr.evaluate(keygen_doc, XPathConstants.STRING);
+ } catch (XPathExpressionException e) {
+ throw new ExecutionException(e.getCause().getMessage());
+ }
+ if (_key != null) {
+ return true;
+ }
+ return false;
+ }
+
+
+ // ENTRY POINTS...
+
+
+ private Answer execute(ReadyCommand cmd) {
+ return new ReadyAnswer(cmd);
+ }
+
+ private Answer execute(MaintainCommand cmd) {
+ return new MaintainAnswer(cmd);
+ }
+
+ private ExternalNetworkResourceUsageAnswer execute(ExternalNetworkResourceUsageCommand cmd) {
+ return new ExternalNetworkResourceUsageAnswer(cmd);
+ }
+
+
+ /*
+ * Guest networks
+ */
+
+ private synchronized Answer execute(IpAssocCommand cmd) {
+ refreshPaloAltoConnection();
+ return execute(cmd, _numRetries);
+ }
+
+ private Answer execute(IpAssocCommand cmd, int numRetries) {
+ String[] results = new String[cmd.getIpAddresses().length];
+ int i = 0;
+ try {
+ IpAddressTO ip;
+ if (cmd.getIpAddresses().length != 1) {
+ throw new ExecutionException("Received an invalid number of guest IPs to associate.");
+ } else {
+ ip = cmd.getIpAddresses()[0];
+ }
+
+ String sourceNatIpAddress = null;
+ GuestNetworkType type = GuestNetworkType.INTERFACE_NAT;
+
+ if (ip.isSourceNat()) {
+ type = GuestNetworkType.SOURCE_NAT;
+
+ if (ip.getPublicIp() == null) {
+ throw new ExecutionException("Source NAT IP address must not be null.");
+ } else {
+ sourceNatIpAddress = ip.getPublicIp();
+ }
+ }
+
+ long guestVlanTag = Long.parseLong(cmd.getAccessDetail(NetworkElementCommand.GUEST_VLAN_TAG));
+ String guestVlanGateway = cmd.getAccessDetail(NetworkElementCommand.GUEST_NETWORK_GATEWAY);
+ String cidr = cmd.getAccessDetail(NetworkElementCommand.GUEST_NETWORK_CIDR);
+ long cidrSize = NetUtils.cidrToLong(cidr)[1];
+ String guestVlanSubnet = NetUtils.getCidrSubNet(guestVlanGateway, cidrSize);
+
+ Long publicVlanTag = null;
+ if (ip.getBroadcastUri() != null && !ip.getBroadcastUri().equals("untagged")) {
+ try {
+ publicVlanTag = Long.parseLong(ip.getBroadcastUri());
+ } catch (Exception e) {
+ throw new ExecutionException("Could not parse public VLAN tag: " + ip.getBroadcastUri());
+ }
+ }
+
+ ArrayList<IPaloAltoCommand> commandList = new ArrayList<IPaloAltoCommand>();
+
+ if (ip.isAdd()) {
+ // Implement the guest network for this VLAN
+ implementGuestNetwork(commandList, type, publicVlanTag, sourceNatIpAddress, guestVlanTag, guestVlanGateway, guestVlanSubnet, cidrSize);
+ } else {
+ // Remove the guest network:
+ shutdownGuestNetwork(commandList, type, publicVlanTag, sourceNatIpAddress, guestVlanTag, guestVlanGateway, guestVlanSubnet, cidrSize);
+ }
+
+ boolean status = requestWithCommit(commandList);
+
+ results[i++] = ip.getPublicIp() + " - success";
+ } catch (ExecutionException e) {
+ s_logger.error(e);
+
+ if (numRetries > 0 && refreshPaloAltoConnection()) {
+ int numRetriesRemaining = numRetries - 1;
+ s_logger.debug("Retrying IPAssocCommand. Number of retries remaining: " + numRetriesRemaining);
+ return execute(cmd, numRetriesRemaining);
+ } else {
+ results[i++] = IpAssocAnswer.errorResult;
+ }
+ }
+
+ return new IpAssocAnswer(cmd, results);
+ }
+
+ private void implementGuestNetwork(ArrayList<IPaloAltoCommand> cmdList, GuestNetworkType type, Long publicVlanTag, String publicIp, long privateVlanTag, String privateGateway, String privateSubnet, long privateCidrNumber) throws ExecutionException {
+ privateSubnet = privateSubnet+"/"+privateCidrNumber;
+
+ managePrivateInterface(cmdList, PaloAltoPrimative.ADD, privateVlanTag, privateGateway+"/"+privateCidrNumber);
+
+ if (type.equals(GuestNetworkType.SOURCE_NAT)) {
+ managePublicInterface(cmdList, PaloAltoPrimative.ADD, publicVlanTag, publicIp+"/32", privateVlanTag);
+ manageSrcNatRule(cmdList, PaloAltoPrimative.ADD, type, publicVlanTag, publicIp+"/32", privateVlanTag, privateGateway+"/"+privateCidrNumber);
+ manageNetworkIsolation(cmdList, PaloAltoPrimative.ADD, privateVlanTag, privateSubnet, privateGateway);
+ }
+
+ String msg = "Implemented guest network with type " + type + ". Guest VLAN tag: " + privateVlanTag + ", guest gateway: " + privateGateway+"/"+privateCidrNumber;
+ msg += type.equals(GuestNetworkType.SOURCE_NAT) ? ", source NAT IP: " + publicIp : "";
+ s_logger.debug(msg);
+ }
+
+ private void shutdownGuestNetwork(ArrayList<IPaloAltoCommand> cmdList, GuestNetworkType type, Long publicVlanTag, String sourceNatIpAddress, long privateVlanTag, String privateGateway, String privateSubnet, long privateCidrSize) throws ExecutionException {
+ privateSubnet = privateSubnet+"/"+privateCidrSize;
+
+ if (type.equals(GuestNetworkType.SOURCE_NAT)) {
+ manageNetworkIsolation(cmdList, PaloAltoPrimative.DELETE, privateVlanTag, privateSubnet, privateGateway);
+ manageSrcNatRule(cmdList, PaloAltoPrimative.DELETE, type, publicVlanTag, sourceNatIpAddress+"/32", privateVlanTag, privateGateway+"/"+privateCidrSize);
+ managePublicInterface(cmdList, PaloAltoPrimative.DELETE, publicVlanTag, sourceNatIpAddress+"/32", privateVlanTag);
+ }
+
+ managePrivateInterface(cmdList, PaloAltoPrimative.DELETE, privateVlanTag, privateGateway+"/"+privateCidrSize);
+
+ String msg = "Shut down guest network with type " + type +". Guest VLAN tag: " + privateVlanTag + ", guest gateway: " + privateGateway+"/"+privateCidrSize;
+ msg += type.equals(GuestNetworkType.SOURCE_NAT) ? ", source NAT IP: " + sourceNatIpAddress : "";
+ s_logger.debug(msg);
+ }
+
+
+
+ /*
+ * Firewall rule entry point
+ */
+ private synchronized Answer execute(SetFirewallRulesCommand cmd) {
+ refreshPaloAltoConnection();
+ return execute(cmd, _numRetries);
+ }
+
+ private Answer execute(SetFirewallRulesCommand cmd, int numRetries) {
+ FirewallRuleTO[] rules = cmd.getRules();
+ try {
+ ArrayList<IPaloAltoCommand> commandList = new ArrayList<IPaloAltoCommand>();
+
+ for (FirewallRuleTO rule : rules) {
+ if (!rule.revoked()) {
+ manageFirewallRule(commandList, PaloAltoPrimative.ADD, rule);
+ } else {
+ manageFirewallRule(commandList, PaloAltoPrimative.DELETE, rule);
+ }
+ }
+
+ boolean status = requestWithCommit(commandList);
+
+ return new Answer(cmd);
+ } catch (ExecutionException e) {
+ s_logger.error(e);
+
+ if (numRetries > 0 && refreshPaloAltoConnection()) {
+ 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 rule entry point
+ */
+
+ private synchronized Answer execute(SetStaticNatRulesCommand cmd) {
+ refreshPaloAltoConnection();
+ return execute(cmd, _numRetries);
+ }
+
+ private Answer execute(SetStaticNatRulesCommand cmd, int numRetries) {
+ StaticNatRuleTO[] rules = cmd.getRules();
+
+ try {
+ ArrayList<IPaloAltoCommand> commandList = new ArrayList<IPaloAltoCommand>();
+
+ for (StaticNatRuleTO rule : rules) {
+ if (!rule.revoked()) {
+ manageStcNatRule(commandList, PaloAltoPrimative.ADD, rule);
+ } else {
+ manageStcNatRule(commandList, PaloAltoPrimative.DELETE, rule);
+ }
+ }
+
+ boolean status = requestWithCommit(commandList);
+
+ return new Answer(cmd);
+ } catch (ExecutionException e) {
+ s_logger.error(e);
+
+ if (numRetries > 0 && refreshPaloAltoConnection()) {
+ int numRetriesRemaining = numRetries - 1;
+ s_logger.debug("Retrying SetStaticNatRulesCommand. Number of retries remaining: " + numRetriesRemaining);
+ return execute(cmd, numRetriesRemaining);
+ } else {
+ return new Answer(cmd, e);
+ }
+ }
+ }
+
+
+ /*
+ * Destination NAT (Port Forwarding) entry point
+ */
+ private synchronized Answer execute (SetPortForwardingRulesCommand cmd) {
+ refreshPaloAltoConnection();
+ return execute(cmd, _numRetries);
+ }
+
+ private Answer execute(SetPortForwardingRulesCommand cmd, int numRetries) {
+ PortForwardingRuleTO[] rules = cmd.getRules();
+
+ try {
+ ArrayList<IPaloAltoCommand> commandList = new ArrayList<IPaloAltoCommand>();
+
+ for (PortForwardingRuleTO rule : rules) {
+ if (!rule.revoked()) {
+ manageDstNatRule(commandList, PaloAltoPrimative.ADD, rule);
+ } else {
+ manageDstNatRule(commandList, PaloAltoPrimative.DELETE, rule);
+ }
+ }
+
+ boolean status = requestWithCommit(commandList);
+
+ return new Answer(cmd);
+ } catch (ExecutionException e) {
+ s_logger.error(e);
+
+ if (numRetries > 0 && refreshPaloAltoConnection()) {
+ int numRetriesRemaining = numRetries - 1;
+ s_logger.debug("Retrying SetPortForwardingRulesCommand. Number of retries remaining: " + numRetriesRemaining);
+ return execute(cmd, numRetriesRemaining);
+ } else {
+ return new Answer(cmd, e);
+ }
+ }
+ }
+
+
+ // IMPLEMENTATIONS...
+
+
+ /*
+ * Private interface implementation
+ */
+
+ private String genPrivateInterfaceName(long vlanTag) {
+ return _privateInterface+"."+Long.toString(vlanTag);
+ }
+
+ public boolean managePrivateInterface(ArrayList<IPaloAltoCommand> cmdList, PaloAltoPrimative prim, long privateVlanTag, String privateGateway) throws ExecutionException {
+ String interfaceName = genPrivateInterfaceName(privateVlanTag);
+
+ switch (prim) {
+
+ case CHECK_IF_EXISTS:
+ // check if one exists already
+ Map<String, String> params = new HashMap<String, String>();
+ params.put("type", "config");
+ params.put("action", "get");
+ params.put("xpath", "/config/devices/entry/network/interface/"+_privateInterfaceType+"/entry[@name='"+_privateInterface+"']/layer3/units/entry[@name='"+interfaceName+"']");
+ String response = request(PaloAltoMethod.GET, params);
+ boolean result = (validResponse(response) && responseNotEmpty(response));
+ s_logger.debug("Private sub-interface exists: "+interfaceName+", "+result);
+ return result;
+
+ case ADD:
+ if (managePrivateInterface(cmdList, PaloAltoPrimative.CHECK_IF_EXISTS, privateVlanTag, privateGateway)) {
+ return true;
+ }
+
+ // add cmds
+ // add sub-interface
+ Map<String, String> a_sub_params = new HashMap<String, String>();
+ a_sub_params.put("type", "config");
+ a_sub_params.put("action", "set");
+ a_sub_params.put("xpath", "/config/devices/entry/network/interface/"+_privateInterfaceType+"/entry[@name='"+_privateInterface+"']/layer3/units/entry[@name='"+interfaceName+"']");
+ a_sub_params.put("element", "<tag>"+privateVlanTag+"</tag><ip><entry name='"+privateGateway+"'/></ip><interface-management-profile>"+_pingManagementProfile+"</interface-management-profile>");
+ cmdList.add(new DefaultPaloAltoCommand(PaloAltoMethod.GET, a_sub_params));
+
+ // add sub-interface to VR...
+ Map<String, String> a_vr_params = new HashMap<String, String>();
+ a_vr_params.put("type", "config");
+ a_vr_params.put("action", "set");
+ a_vr_params.put("xpath", "/config/devices/entry/network/virtual-router/entry[@name='"+_virtualRouter+"']/interface");
+ a_vr_params.put("element", "<member>"+interfaceName+"</member>");
+ cmdList.add(new DefaultPaloAltoCommand(PaloAltoMethod.GET, a_vr_params));
+
+ // add sub-interface to vsys...
+ Map<String, String> a_vsys_params = new HashMap<String, String>();
+ a_vsys_params.put("type", "config");
+ a_vsys_params.put("action", "set");
+ a_vsys_params.put("xpath", "/config/devices/entry/vsys/entry[@name='vsys1']/import/network/interface");
+ a_vsys_params.put("element", "<member>"+interfaceName+"</member>");
+ cmdList.add(new DefaultPaloAltoCommand(PaloAltoMethod.GET, a_vsys_params));
+
+ // add sub-interface to zone...
+ Map<String, String> a_zone_params = new HashMap<String, String>();
+ a_zone_params.put("type", "config");
+ a_zone_params.put("action", "set");
+ a_zone_params.put("xpath", "/config/devices/entry/vsys/entry[@name='vsys1']/zone/entry[@name='"+_privateZone+"']/network/layer3");
+ a_zone_params.put("element", "<member>"+interfaceName+"</member>");
+ cmdList.add(new DefaultPaloAltoCommand(PaloAltoMethod.GET, a_zone_params));
+
+ return true;
+
+ case DELETE:
+ if (!managePrivateInterface(cmdList, PaloAltoPrimative.CHECK_IF_EXISTS, privateVlanTag, privateGateway)) {
+ return true;
+ }
+
+ // add cmds to the list
+ // delete sub-interface from zone...
+ Map<String, String> d_zone_params = new HashMap<String, String>();
+ d_zone_params.put("type", "config");
+ d_zone_params.put("action", "delete");
+ d_zone_params.put("xpath", "/config/devices/entry/vsys/entry[@name='vsys1']/zone/entry[@name='"+_privateZone+"']/network/layer3/member[text()='"+interfaceName+"']");
+ cmdList.add(new DefaultPaloAltoCommand(PaloAltoMethod.GET, d_zone_params));
+
+ // delete sub-interface from vsys...
+ Map<String, String> d_vsys_params = new HashMap<String, String>();
+ d_vsys_params.put("type", "config");
+ d_vsys_params.put("action", "delete");
+ d_vsys_params.put("xpath", "/config/devices/entry/vsys/entry[@name='vsys1']/import/network/interface/member[text()='"+interfaceName+"']");
+ cmdList.add(new DefaultPaloAltoCommand(PaloAltoMethod.GET, d_vsys_params));
+
+ // delete sub-interface from VR...
+ Map<String, String> d_vr_params = new HashMap<String, String>();
+ d_vr_params.put("type", "config");
+ d_vr_params.put("action", "delete");
+ d_vr_params.put("xpath", "/config/devices/entry/network/virtual-router/entry[@name='"+_virtualRouter+"']/interface/member[text()='"+interfaceName+"']");
+ cmdList.add(new DefaultPaloAltoCommand(PaloAltoMethod.GET, d_vr_params));
+
+ // delete sub-interface...
+ Map<String, String> d_sub_params = new HashMap<String, String>();
+ d_sub_params.put("type", "config");
+ d_sub_params.put("action", "delete");
+ d_sub_params.put("xpath", "/config/devices/entry/network/interface/"+_privateInterfaceType+"/entry[@name='"+_privateInterface+"']/layer3/units/entry[@name='"+interfaceName+"']");
+ cmdList.add(new DefaultPaloAltoCommand(PaloAltoMethod.GET, d_sub_params));
+
+ return true;
+
+ default:
+ s_logger.debug("Unrecognized command.");
+ return false;
+ }
+ }
+
+
+ /*
+ * Public Interface implementation
+ */
+
+ private String genPublicInterfaceName(Long id) {
+ return _publicInterface+"."+Long.toString(id);
+ }
+
+ public boolean managePublicInterface(ArrayList<IPaloAltoCommand> cmdList, PaloAltoPrimative prim, Long publicVlanTag, String publicIp, long privateVlanTag) throws ExecutionException {
+ String interfaceName;
+ if (publicVlanTag == null) {
+ interfaceName = genPublicInterfaceName(new Long("9999"));
+ } else {
+ interfaceName = genPublicInterfaceName(publicVlanTag);
+ }
+
+ switch (prim) {
+
+ case CHECK_IF_EXISTS:
+ // check if one exists already
+ Map<String, String> params = new HashMap<String, String>();
+ params.put("type", "config");
+ params.put("action", "get");
+ params.put("xpath", "/config/devices/entry/network/interface/"+_publicInterfaceType+"/entry[@name='"+_publicInterface+"']/layer3/units/entry[@name='"+interfaceName+"']/ip/entry[@name='"+publicIp+"']");
+ String response = request(PaloAltoMethod.GET, params);
+ boolean result = (validResponse(response) && responseNotEmpty(response));
+ s_logger.debug("Public sub-interface & IP exists: "+interfaceName+" : "+publicIp+", "+result);
+ return result;
+
+ case ADD:
+ if (managePublicInterface(cmdList, PaloAltoPrimative.CHECK_IF_EXISTS, publicVlanTag, publicIp, privateVlanTag)) {
+ return true;
+ }
+
+ // add IP to the sub-interface
+ Map<String, String> a_sub_params = new HashMap<String, String>();
+ a_sub_params.put("type", "config");
+ a_sub_params.put("action", "set");
+ a_sub_params.put("xpath", "/config/devices/entry/network/interface/"+_publicInterfaceType+"/entry[@name='"+_publicInterface+"']/layer3/units/entry[@name='"+interfaceName+"']/ip");
+ a_sub_params.put("element", "<entry name='"+publicIp+"'/>");
+ cmdList.add(new DefaultPaloAltoCommand(PaloAltoMethod.GET, a_sub_params));
+
+ // add sub-interface to VR (does nothing if already done)...
+ Map<String, String> a_vr_params = new HashMap<String, String>();
+ a_vr_params.put("type", "config");
+ a_vr_params.put("action", "set");
+ a_vr_params.put("xpath", "/config/devices/entry/network/virtual-router/entry[@name='"+_virtualRouter+"']/interface");
+ a_vr_params.put("element", "<member>"+interfaceName+"</member>");
+ cmdList.add(new DefaultPaloAltoCommand(PaloAltoMethod.GET, a_vr_params));
+
+ // add sub-interface to vsys (does nothing if already done)...
+ Map<String, String> a_vsys_params = new HashMap<String, String>();
+ a_vsys_params.put("type", "config");
+ a_vsys_params.put("action", "set");
+ a_vsys_params.put("xpath", "/config/devices/entry/vsys/entry[@name='vsys1']/import/network/interface");
+ a_vsys_params.put("element", "<member>"+interfaceName+"</member>");
+ cmdList.add(new DefaultPaloAltoCommand(PaloAltoMethod.GET, a_vsys_params));
+
+ // add sub-interface to zone (does nothing if already done)...
+ Map<String, String> a_zone_params = new HashMap<String, String>();
+ a_zone_params.put("type", "config");
+ a_zone_params.put("action", "set");
+ a_zone_params.put("xpath", "/config/devices/entry/vsys/entry[@name='vsys1']/zone/entry[@name='"+_publicZone+"']/network/layer3");
+ a_zone_params.put("element", "<member>"+interfaceName+"</member>");
+ cmdList.add(new DefaultPaloAltoCommand(PaloAltoMethod.GET, a_zone_params));
+
+ return true;
+
+ case DELETE:
+ if (!managePublicInterface(cmdList, PaloAltoPrimative.CHECK_IF_EXISTS, publicVlanTag, publicIp, privateVlanTag)) {
+ return true;
+ }
+
+ // delete IP from sub-interface...
+ Map<String, String> d_sub_params = new HashMap<String, String>();
+ d_sub_params.put("type", "config");
+ d_sub_params.put("action", "delete");
+ d_sub_params.put("xpath", "/config/devices/entry/network/interface/"+_publicInterfaceType+"/entry[@name='"+_publicInterface+"']/layer3/units/entry[@name='"+interfaceName+"']/ip/entry[@name='"+publicIp+"']");
+ cmdList.add(new DefaultPaloAltoCommand(PaloAltoMethod.GET, d_sub_params));
+
+ return true;
+
+ default:
+ s_logger.debug("Unrecognized command.");
+ return false;
+ }
+ }
+
+
+ /*
+ * Source NAT rule implementation
+ */
+
+ private String genSrcNatRuleName(Long privateVlanTag) {
+ return "src_nat."+Long.toString(privateVlanTag);
+ }
+
+ public boolean manageSrcNatRule(ArrayList<IPaloAltoCommand> cmdList, PaloAltoPrimative prim, GuestNetworkType type, Long publicVlanTag, String publicIp, long privateVlanTag, String privateGateway) throws ExecutionException {
+ String publicInterfaceName;
+ if (publicVlanTag == null) {
+ publicInterfaceName = genPublicInterfaceName(new Long("9999"));
+ } else {
+ publicInterfaceName = genPublicInterfaceName(publicVlanTag);
+ }
+ String srcNatName = genSrcNatRuleName(privateVlanTag);
+
+ switch (prim) {
+
+ case CHECK_IF_EXISTS:
+ // check if one exists already
+ Map<String, String> params = new HashMap<String, String>();
+ params.put("type", "config");
+ params.put("action", "get");
+ params.put("xpath", "/config/devices/entry/vsys/entry[@name='vsys1']/rulebase/nat/rules/entry[@name='"+srcNatName+"']");
+ String response = request(PaloAltoMethod.GET, params);
+ boolean result = (validResponse(response) && responseNotEmpty(response));
+ s_logger.debug("Source NAT exists: "+srcNatName+", "+result);
+ return result;
+
+ case ADD:
+ if (manageSrcNatRule(cmdList, PaloAltoPrimative.CHECK_IF_EXISTS, type, publicVlanTag, publicIp, privateVlanTag, privateGateway)) {
+ return true;
+ }
+
+ String xml = "";
+ xml += "<from><member>"+_privateZone+"</member></from>";
+ xml += "<to><member>"+_publicZone+"</member></to>";
+ xml += "<source><member>"+privateGateway+"</member></source>";
+ xml += "<destination><member>any</member></destination>";
+ xml += "<service>any</service>";
+ xml += "<nat-type>ipv4</nat-type>";
+ xml += "<to-interface>"+publicInterfaceName+"</to-interface>";
+ xml += "<source-translation><dynamic-ip-and-port><interface-address>";
+ xml += "<ip>"+publicIp+"</ip>";
+ xml += "<interface>"+publicInterfaceName+"</interface>";
+ xml += "</interface-address></dynamic-ip-and-port></source-translation>";
+
+ Map<String, String> a_params = new HashMap<String, String>();
+ a_params.put("type", "config");
+ a_params.put("action", "set");
+ a_params.put("xpath", "/config/devices/entry/vsys/entry[@name='vsys1']/rulebase/nat/rules/entry[@name='"+srcNatName+"']");
+ a_params.put("element", xml);
+ cmdList.add(new DefaultPaloAltoCommand(PaloAltoMethod.POST, a_params));
+
+ return true;
+
+ case DELETE:
+ if (!manageSrcNatRule(cmdList, PaloAltoPrimative.CHECK_IF_EXISTS, type, publicVlanTag, publicIp, privateVlanTag, privateGateway)) {
+ return true;
+ }
+
+ Map<String, String> d_params = new HashMap<String, String>();
+ d_params.put("type", "config");
+ d_params.put("action", "delete");
+ d_params.put("xpath", "/config/devices/entry/vsys/entry[@name='vsys1']/rulebase/nat/rules/entry[@name='"+srcNatName+"']");
+ cmdList.add(new DefaultPaloAltoCommand(PaloAltoMethod.POST, d_params));
+
+ return true;
+
+ default:
+ s_logger.debug("Unrecognized command.");
+ return false;
+ }
+ }
+
+
+ /*
+ * Destination NAT rules (Port Forwarding) implementation
+ */
+ private String genDstNatRuleName(String publicIp, long id) {
+ return "dst_nat."+genIpIdentifier(publicIp)+"_"+Long.toString(id);
+ }
+
+ public boolean manageDstNatRule(ArrayList<IPaloAltoCommand> cmdList, PaloAltoPrimative prim, PortForwardingRuleTO rule) throws ExecutionException {
+ String publicIp = rule.getSrcIp();
+ String dstNatName = genDstNatRuleName(publicIp, rule.getId());
+
+ String publicInterfaceName;
+ String publicVlanTag = rule.getSrcVlanTag();
+ if (publicVlanTag == null || publicVlanTag.equals("untagged")) {
+ publicInterfaceName = genPublicInterfaceName(new Long("9999"));
+ } else {
+ publicInterfaceName = genPublicInterfaceName(new Long(publicVlanTag));
+ }
+
+ switch (prim) {
+
+ case CHECK_IF_EXISTS:
+ // check if one exists already
+ Map<String, String> params = new HashMap<String, String>();
+ params.put("type", "config");
+ params.put("action", "get");
+ params.put("xpath", "/config/devices/entry/vsys/entry[@name='vsys1']/rulebase/nat/rules/entry[@name='"+dstNatName+"']");
+ String response = request(PaloAltoMethod.GET, params);
+ boolean result = (validResponse(response) && responseNotEmpty(response));
+ s_logger.debug("Destination NAT exists: "+dstNatName+", "+result);
+ return result;
+
+ case ADD:
+ if (manageDstNatRule(cmdList, PaloAltoPrimative.CHECK_IF_EXISTS, rule)) {
+ return true;
+ }
+
+ // build source service xml
+ String srcService;
+ String protocol = rule.getProtocol();
+ int[] srcPortRange = rule.getSrcPortRange();
+ if (srcPortRange != null) {
+ String portRange;
+ if (srcPortRange.length == 1 || srcPortRange[0] == srcPortRange[1]) {
+ portRange = String.valueOf(srcPortRange[0]);
+ } else {
+ portRange = String.valueOf(srcPortRange[0])+"-"+String.valueOf(srcPortRange[1]);
+ }
+ manageService(cmdList, PaloAltoPrimative.ADD, protocol, portRange, null);
+ srcService = genServiceName(protocol, portRange, null);
+ } else {
+ // no equivalent config in PA, so allow all traffic...
+ srcService = "any";
+ }
+
+ // build destination port xml (single port limit in PA)
+ String dstPortXML = "";
+ int[] dstPortRange = rule.getDstPortRange();
+ if (dstPortRange != null) {
+ dstPortXML = "<translated-port>"+dstPortRange[0]+"</translated-port>";
+ }
+
+ // add public IP to the sub-interface
+ Map<String, String> a_sub_params = new HashMap<String, String>();
+ a_sub_params.put("type", "config");
+ a_sub_params.put("action", "set");
+ a_sub_params.put("xpath", "/config/devices/entry/network/interface/"+_publicInterfaceType+"/entry[@name='"+_publicInterface+"']/layer3/units/entry[@name='"+publicInterfaceName+"']/ip");
+ a_sub_params.put("element", "<entry name='"+publicIp+"/32'/>");
+ cmdList.add(new DefaultPaloAltoCommand(PaloAltoMethod.GET, a_sub_params));
+
+ // add the destination nat rule for the public IP
+ String xml = "";
+ xml += "<from><member>"+_publicZone+"</member></from>";
+ xml += "<to><member>"+_publicZone+"</member></to>";
+ xml += "<source><member>any</member></source>";
+ xml += "<destination><member>"+publicIp+"</member></destination>";
+ xml += "<service>"+srcService+"</service>";
+ xml += "<nat-type>ipv4</nat-type>";
+ xml += "<to-interface>"+publicInterfaceName+"</to-interface>";
+ xml += "<destination-translation><translated-address>"+rule.getDstIp()+"</translated-address>"+dstPortXML+"</destination-translation>";
+
+ Map<String, String> a_params = new HashMap<String, String>();
+ a_params.put("type", "config");
+ a_params.put("action", "set");
+ a_params.put("xpath", "/config/devices/entry/vsys/entry[@name='vsys1']/rulebase/nat/rules/entry[@name='"+dstNatName+"']");
+ a_params.put("element", xml);
+ cmdList.add(new DefaultPaloAltoCommand(PaloAltoMethod.POST, a_params));
+
+ return true;
+
+ case DELETE:
+ if (!manageDstNatRule(cmdList, PaloAltoPrimative.CHECK_IF_EXISTS, rule)) {
+ return true;
+ }
+
+ // determine if we need to delete the ip from the interface as well...
+ Map<String, String> c_params = new HashMap<String, String>();
+ c_params.put("type", "config");
+ c_params.put("action", "get");
+ c_params.put("xpath", "/config/devices/entry/vsys/entry[@name='vsys1']/rulebase/nat/rules/entry[destination/member[text()='"+publicIp+"']]");
+ String c_response = request(PaloAltoMethod.GET, c_params);
+
+ String count = "";
+ NodeList response_body;
+ Document doc = getDocument(c_response);
+ XPath xpath = XPathFactory.newInstance().newXPath();
+ try {
+ XPathExpression expr = xpath.compile("/response[@status='success']/result");
+ response_body = (NodeList) expr.evaluate(doc, XPathConstants.NODESET);
+ } catch (XPathExpressionException e) {
+ throw new ExecutionException(e.getCause().getMessage());
+ }
+ if (response_body.getLength() > 0 && response_body.item(0).getAttributes().getLength() > 0) {
+ count = response_body.item(0).getAttributes().getNamedItem("count").getTextContent();
+ }
+
+ // delete the dst nat rule
+ Map<String, String> d_params = new HashMap<String, String>();
+ d_params.put("type", "config");
+ d_params.put("action", "delete");
+ d_params.put("xpath", "/config/devices/entry/vsys/entry[@name='vsys1']/rulebase/nat/rules/entry[@name='"+dstNatName+"']");
+ cmdList.add(new DefaultPaloAltoCommand(PaloAltoMethod.POST, d_params));
+
+ if (!count.equals("") && Integer.parseInt(count) == 1) { // this dst nat rule is the last, so remove the ip...
+ // delete IP from sub-interface...
+ Map<String, String> d_sub_params = new HashMap<String, String>();
+ d_sub_params.put("type", "config");
+ d_sub_params.put("action", "delete");
+ d_sub_params.put("xpath", "/config/devices/entry/network/interface/"+_publicInterfaceType+"/entry[@name='"+_publicInterface+"']/layer3/units/entry[@name='"+publicInterfaceName+"']/ip/entry[@name='"+publicIp+"/32']");
+ cmdList.add(new DefaultPaloAltoCommand(PaloAltoMethod.GET, d_sub_params));
+ }
+
+ return true;
+
+ default:
+ s_logger.debug("Unrecognized command.");
+ return false;
+ }
+ }
+
+
+
+ /*
+ * Static NAT rule implementation
+ */
+ private String genStcNatRuleName(String publicIp, long id) {
+ return "stc_nat."+genIpIdentifier(publicIp)+"_"+Long.toString(id);
+ }
+
+ public boolean manageStcNatRule(ArrayList<IPaloAltoCommand> cmdList, PaloAltoPrimative prim, StaticNatRuleTO rule) throws ExecutionException {
+ String publicIp = rule.getSrcIp();
+ String stcNatName = genStcNatRuleName(publicIp, rule.getId());
+
+ String publicInterfaceName;
+ String publicVlanTag = rule.getSrcVlanTag();
+ if (publicVlanTag == null || publicVlanTag.equals("untagged")) {
+ publicInterfaceName = genPublicInterfaceName(new Long("9999"));
+ } else {
+ publicInterfaceName = genPublicInterfaceName(new Long(publicVlanTag));
+ }
+
+ switch (prim) {
+
+ case CHECK_IF_EXISTS:
+ // check if one exists already
+ Map<String, String> params = new HashMap<String, String>();
+ params.put("type", "config");
+ params.put("action", "get");
+ params.put("xpath", "/config/devices/entry/vsys/entry[@name='vsys1']/rulebase/nat/rules/entry[@name='"+stcNatName+"']");
+ String response = request(PaloAltoMethod.GET, params);
+ boolean result = (validResponse(response) && responseNotEmpty(response));
+ s_logger.debug("Static NAT exists: "+stcNatName+", "+result);
+ return result;
+
+ case ADD:
+ if (manageStcNatRule(cmdList, PaloAltoPrimative.CHECK_IF_EXISTS, rule)) {
+ return true;
+ }
+
+ // add public IP to the sub-interface
+ Map<String, String> a_sub_params = new HashMap<String, String>();
+ a_sub_params.put("type", "config");
+ a_sub_params.put("action", "set");
+ a_sub_params.put("xpath", "/config/devices/entry/network/interface/"+_publicInterfaceType+"/entry[@name='"+_publicInterface+"']/layer3/units/entry[@name='"+publicInterfaceName+"']/ip");
+ a_sub_params.put("element", "<entry name='"+publicIp+"/32'/>");
+ cmdList.add(new DefaultPaloAltoCommand(PaloAltoMethod.GET, a_sub_params));
+
+ // add the static nat rule for the public IP
+ String xml = "";
+ xml += "<from><member>"+_publicZone+"</member></from>";
+ xml += "<to><member>"+_publicZone+"</member></to>";
+ xml += "<source><member>any</member></source>";
+ xml += "<destination><member>"+publicIp+"</member></destination>";
+ xml += "<service>any</service>";
+ xml += "<nat-type>ipv4</nat-type>";
+ xml += "<to-interface>"+publicInterfaceName+"</to-interface>";
+ xml += "<destination-translation><translated-address>"+rule.getDstIp()+"</translated-address></destination-translation>";
+
+
+ Map<String, String> a_params = new HashMap<String, String>();
+ a_params.put("type", "config");
+ a_params.put("action", "set");
+ a_params.put("xpath", "/config/devices/entry/vsys/entry[@name='vsys1']/rulebase/nat/rules/entry[@name='"+stcNatName+"']");
+ a_params.put("element", xml);
+ cmdList.add(new DefaultPaloAltoCommand(PaloAltoMethod.POST, a_params));
+
+ return true;
+
+ case DELETE:
+ if (!manageStcNatRule(cmdList, PaloAltoPrimative.CHECK_IF_EXISTS, rule)) {
+ return true;
+ }
+
+ // delete the static nat rule
+ Map<String, String> d_params = new HashMap<String, String>();
+ d_params.put("type", "config");
+ d_params.put("action", "delete");
+ d_params.put("xpath", "/config/devices/entry/vsys/entry[@name='vsys1']/rulebase/nat/rules/entry[@name='"+stcNatName+"']");
+ cmdList.add(new DefaultPaloAltoCommand(PaloAltoMethod.POST, d_params));
+
+ // delete IP from sub-interface...
+ Map<String, String> d_sub_params = new HashMap<String, String>();
+ d_sub_params.put("type", "config");
+ d_sub_params.put("action", "delete");
+ d_sub_params.put("xpath", "/config/devices/entry/network/interface/"+_publicInterfaceType+"/entry[@name='"+_publicInterface+"']/layer3/units/entry[@name='"+publicInterfaceName+"']/ip/entry[@name='"+publicIp+"/32']");
+ cmdList.add(new DefaultPaloAltoCommand(PaloAltoMethod.GET, d_sub_params));
+
+ return true;
+
+ default:
+ s_logger.debug("Unrecognized command.");
+ return false;
+ }
+ }
+
+
+ /*
+ * Firewall rule implementation
+ */
+
+ private String genFirewallRuleName(long id) {
+ return "policy_"+Long.toString(id);
+ }
+
+ public boolean manageFirewallRule(ArrayList<IPaloAltoCommand> cmdList, PaloAltoPrimative prim, FirewallRuleTO rule) throws ExecutionException {
+ String ruleName = genFirewallRuleName(rule.getId());
+
+ switch (prim) {
+
+ case CHECK_IF_EXISTS:
+ // check if one exists already
+ Map<String, String> params = new HashMap<String, String>();
+ params.put("type", "config");
+ params.put("action", "get");
+ params.put("xpath", "/config/devices/entry/vsys/entry[@name='vsys1']/rulebase/security/rules/entry[@name='"+ruleName+"']");
+ String response = request(PaloAltoMethod.GET, params);
+ boolean result = (validResponse(response) && responseNotEmpty(response));
+ s_logger.debug("Firewall policy exists: "+ruleName+", "+result);
+ return result;
+
+ case ADD:
+ if (manageFirewallRule(cmdList, PaloAltoPrimative.CHECK_IF_EXISTS, rule)) {
+ return true;
+ }
+
+ String srcZone;
+ String dstZone;
+ String dstAddressXML;
+ String appXML;
+ String serviceXML;
+
+ String protocol = rule.getProtocol();
+
+ // Only ICMP will use an Application, so others will be any.
+ if (protocol.equals(Protocol.ICMP.toString())) {
+ appXML = "<member>icmp</member><member>ping</member><member>traceroute</member>"; // use the default icmp applications...
+ } else {
+ appXML = "<member>any</member>";
+ }
+
+ // Only TCP and UDP will use a Service, others will use any.
+ if (protocol.equals(Protocol.TCP.toString()) || protocol.equals(Protocol.UDP.toString())) {
+ String portRange;
+ if (rule.getSrcPortRange() != null) {
+ int startPort = rule.getSrcPortRange()[0];
+ int endPort = rule.getSrcPortRange()[1];
+ if (startPort == endPort) {
+ portRange = String.valueOf(startPort);
+ } else {
+ portRange = String.valueOf(startPort)+"-"+String.valueOf(endPort);
+ }
+ manageService(cmdList, PaloAltoPrimative.ADD, protocol, portRange, null);
+ serviceXML = "<member>"+genServiceName(protocol, portRange, null)+"</member>";
+ } else {
+ // no equivalent config in PA, so allow all traffic...
+ serviceXML = "<member>any</member>";
+ }
+ } else {
+ serviceXML = "<member>any</member>";
+ }
+
+ if (rule.getTrafficType() == FirewallRule.TrafficType.Egress) { // Network egress rule
+ srcZone = _privateZone;
+ dstZone = _publicZone;
+ dstAddressXML = "<member>any</member>";
+ } else {
+ srcZone = _publicZone;
+ dstZone = _privateZone;
+ dstAddressXML = "<member>"+rule.getSrcIp()+"</member>";
+ }
+
+ // build the source cidr xml
+ String srcCidrXML = "";
+ List<String> ruleSrcCidrList = rule.getSourceCidrList();
+ if (ruleSrcCidrList.size() > 0) { // a cidr was entered, modify as needed...
+ for (int i = 0; i < ruleSrcCidrList.size(); i++) {
+ if (ruleSrcCidrList.get(i).trim().equals("0.0.0.0/0")) { // allow any
+ if (rule.getTrafficType() == FirewallRule.TrafficType.Egress) {
+ srcCidrXML += "<member>"+getPrivateSubnet(rule.getSrcVlanTag())+"</member>";
+ } else {
+ srcCidrXML += "<member>any</member>";
+ }
+ } else {
+ srcCidrXML += "<member>"+ruleSrcCidrList.get(i).trim()+"</member>";
+ }
+ }
+ } else { // no cidr was entered, so allow ALL according to firewall rule type
+ if (rule.getTrafficType() == FirewallRule.TrafficType.Egress) {
+ srcCidrXML = "<member>"+getPrivateSubnet(rule.getSrcVlanTag())+"</member>";
+ } else {
+ srcCidrXML = "<member>any</member>";
+ }
+ }
+
+ String xml = "";
+ xml += "<from><member>"+srcZone+"</member></from>";
+ xml += "<to><member>"+dstZone+"</member></to>";
+ xml += "<source>"+srcCidrXML+"</source>";
+ xml += "<destination>"+dstAddressXML+"</destination>";
+ xml += "<application>"+appXML+"</application>";
+ xml += "<service>"+serviceXML+"</service>";
+ xml += "<action>allow</action>";
+ xml += "<negate-source>no</negate-source>";
+ xml += "<negate-destination>no</negate-destination>";
+ if (_threatProfile != null) { // add the threat profile if it exists
+ xml += "<profile-setting><group><member>"+_threatProfile+"</member></group></profile-setting>";
+ }
+ if (_logProfile != null) { // add the log profile if it exists
+ xml += "<log-setting>"+_logProfile+"</log-setting>";
+ }
+
+ Map<String, String> a_params = new HashMap<String, String>();
+ a_params.put("type", "config");
+ a_params.put("action", "set");
+ a_params.put("xpath", "/config/devices/entry/vsys/entry[@name='vsys1']/rulebase/security/rules/entry[@name='"+ruleName+"']");
+ a_params.put("element", xml);
+ cmdList.add(new DefaultPaloAltoCommand(PaloAltoMethod.POST, a_params));
+
+ return true;
+
+ case DELETE:
+ if (!manageFirewallRule(cmdList, PaloAltoPrimative.CHECK_IF_EXISTS, rule)) {
+ return true;
+ }
+
+ Map<String, String> d_params = new HashMap<String, String>();
+ d_params.put("type", "config");
+ d_params.put("action", "delete");
+ d_params.put("xpath", "/config/devices/entry/vsys/entry[@name='vsys1']/rulebase/security/rules/entry[@name='"+ruleName+"']");
+ cmdList.add(new DefaultPaloAltoCommand(PaloAltoMethod.POST, d_params));
+
+ return true;
+
+ default:
+ s_logger.debug("Unrecognized command.");
+ return false;
+ }
+ }
+
+
+
+ /*
+ * Usage
+ */
+
+
+
+ /*
+ * Helper config functions
+ */
+
+ // ensure guest network isolation
+ private String genNetworkIsolationName(long privateVlanTag) {
+ return "isolate_"+Long.toString(privateVlanTag);
+ }
+
+ public boolean manageNetworkIsolation(ArrayList<IPaloAltoCommand> cmdList, PaloAltoPrimative prim, long privateVlanTag, String privateSubnet, String privateGateway) throws ExecutionException {
+ String ruleName = genNetworkIsolationName(privateVlanTag);
+
+ switch (prim) {
+
+ case CHECK_IF_EXISTS:
+ // check if one exists already
+ Map<String, String> params = new HashMap<String, String>();
+ params.put("type", "config");
+ params.put("action", "get");
+ params.put("xpath", "/config/devices/entry/vsys/entry[@name='vsys1']/rulebase/security/rules/entry[@name='"+ruleName+"']");
+ String response = request(PaloAltoMethod.GET, params);
+ boolean result = (validResponse(response) && responseNotEmpty(response));
+ s_logger.debug("Firewall policy exists: "+ruleName+", "+result);
+ return result;
+
+ case ADD:
+ if (manageNetworkIsolation(cmdList, PaloAltoPrimative.CHECK_IF_EXISTS, privateVlanTag, privateSubnet, privateGateway)) {
+ return true;
+ }
+
+ String xml = "";
+ xml += "<from><member>"+_privateZone+"</member></from>";
+ xml += "<to><member>"+_privateZone+"</member></to>";
+ xml += "<source><member>"+privateSubnet+"</member></source>";
+ xml += "<destination><member>"+privateGateway+"</member></destination>";
+ xml += "<application><member>any</member></application>";
+ xml += "<service><member>any</member></service>";
+ xml += "<action>deny</action>";
+ xml += "<negate-source>no</negate-source>";
+ xml += "<negate-destination>yes</negate-destination>";
+
+ Map<String, String> a_params = new HashMap<String, String>();
+ a_params.put("type", "config");
+ a_params.put("action", "set");
+ a_params.put("xpath", "/config/devices/entry/vsys/entry[@name='vsys1']/rulebase/security/rules/entry[@name='"+ruleName+"']");
+ a_params.put("element", xml);
+ cmdList.add(new DefaultPaloAltoCommand(PaloAltoMethod.POST, a_params));
+
+ return true;
+
+ case DELETE:
+ if (!manageNetworkIsolation(cmdList, PaloAltoPrimative.CHECK_IF_EXISTS, privateVlanTag, privateSubnet, privateGateway)) {
+ return true;
+ }
+
+ Map<String, String> d_params = new HashMap<String, String>();
+ d_params.put("type", "config");
+ d_params.put("action", "delete");
+ d_params.put("xpath", "/config/devices/entry/vsys/entry[@name='vsys1']/rulebase/security/rules/entry[@name='"+ruleName+"']");
+ cmdList.add(new DefaultPaloAltoCommand(PaloAltoMethod.POST, d_params));
+
+ return true;
+
+ default:
+ s_logger.debug("Unrecognized command.");
+ return false;
+ }
+ }
+
+
+ // make the interfaces pingable for basic network troubleshooting
+ public boolean managePingProfile(ArrayList<IPaloAltoCommand> cmdList, PaloAltoPrimative prim) throws ExecutionException {
+ switch (prim) {
+
+ case CHECK_IF_EXISTS:
+ // check if one exists already
+ Map<String, String> params = new HashMap<String, String>();
+ params.put("type", "config");
+ params.put("action", "get");
+ params.put("xpath", "/config/devices/entry/network/profiles/interface-management-profile/entry[@name='"+_pingManagementProfile+"']");
+ String response = request(PaloAltoMethod.GET, params);
+ boolean result = (validResponse(response) && responseNotEmpty(response));
+ s_logger.debug("Management profile exists: "+_pingManagementProfile+", "+result);
+ return result;
+
+ case ADD:
+ if (managePingProfile(cmdList, PaloAltoPrimative.CHECK_IF_EXISTS)) {
+ return true;
+ }
+
+ // add ping profile...
+ Map<String, String> a_params = new HashMap<String, String>();
+ a_params.put("type", "config");
+ a_params.put("action", "set");
+ a_params.put("xpath", "/config/devices/entry/network/profiles/interface-management-profile/entry[@name='"+_pingManagementProfile+"']");
+ a_params.put("element", "<ping>yes</ping>");
+ cmdList.add(new DefaultPaloAltoCommand(PaloAltoMethod.GET, a_params));
+
+ return true;
+
+ case DELETE:
+ if (!managePingProfile(cmdList, PaloAltoPrimative.CHECK_IF_EXISTS)) {
+ return true;
+ }
+
+ // delete ping profile...
+ Map<String, String> d_params = new HashMap<String, String>();
+ d_params.put("type", "config");
+ d_params.put("action", "delete");
+ d_params.put("xpath", "/config/devices/entry/network/profiles/interface-management-profile/entry[@name='"+_pingManagementProfile+"']");
+ cmdList.add(new DefaultPaloAltoCommand(PaloAltoMethod.GET, d_params));
+
+ return true;
+
+ default:
+ s_logger.debug("Unrecognized command.");
+ return false;
+ }
+ }
+
+
+ private String genServiceName(String protocol, String dstPorts, String srcPorts) {
+ String name;
+ if (srcPorts == null) {
+ name = "cs_"+protocol.toLowerCase()+"_"+dstPorts.replace(',', '.');
+ } else {
+ name = "cs_"+protocol.toLowerCase()+"_"+dstPorts.replace(',', '.')+"_"+srcPorts.replace(',', '.');
+ }
+ return name;
+ }
+
+ public boolean manageService(ArrayList<IPaloAltoCommand> cmdList, PaloAltoPrimative prim, String protocol, String dstPorts, String srcPorts) throws ExecutionException {
+ String serviceName = genServiceName(protocol, dstPorts, srcPorts);
+
+ switch (prim) {
+
+ case CHECK_IF_EXISTS:
+ // check if one exists already
+ Map<String, String> params = new HashMap<String, String>();
+ params.put("type", "config");
+ params.put("action", "get");
+ params.put("xpath", "/config/devices/entry/vsys/entry[@name='vsys1']/service/entry[@name='"+serviceName+"']");
+ String response = request(PaloAltoMethod.GET, params);
+ boolean result = (validResponse(response) && responseNotEmpty(response));
+ s_logger.debug("Service exists: "+serviceName+", "+result);
+ return result;
+
+ case ADD:
+ if (manageService(cmdList, PaloAltoPrimative.CHECK_IF_EXISTS, protocol, dstPorts, srcPorts)) {
+ return true;
+ }
+
+ String dstPortXML = "<port>"+dstPorts+"</port>";
+ String srcPortXML = "";
+ if (srcPorts != null) {
+ srcPortXML = "<source-port>"+srcPorts+"</source-port>";
+ }
+
+ // add ping profile...
+ Map<String, String> a_params = new HashMap<String, String>();
+ a_params.put("type", "config");
+ a_params.put("action", "set");
+ a_params.put("xpath", "/config/devices/entry/vsys/entry[@name='vsys1']/service/entry[@name='"+serviceName+"']");
+ a_params.put("element", "<protocol><"+protocol.toLowerCase()+">"+dstPortXML+srcPortXML+"</"+protocol.toLowerCase()+"></protocol>");
+ cmdList.add(new DefaultPaloAltoCommand(PaloAltoMethod.GET, a_params));
+
+ return true;
+
+ case DELETE:
+ if (!manageService(cmdList, PaloAltoPrimative.CHECK_IF_EXISTS, protocol, dstPorts, srcPorts)) {
+ return true;
+ }
+
+ // delete ping profile...
+ Map<String, String> d_params = new HashMap<String, String>();
+ d_params.put("type", "config");
+ d_params.put("action", "delete");
+ d_params.put("xpath", "/config/devices/entry/vsys/entry[@name='vsys1']/service/entry[@name='"+serviceName+"']");
+ cmdList.add(new DefaultPaloAltoCommand(PaloAltoMethod.GET, d_params));
+
+ return true;
+
+ default:
+ s_logger.debug("Unrecognized command.");
+ return false;
+ }
+ }
+
+ private String getPrivateSubnet(String vlan) throws ExecutionException {
+ String _interfaceName = genPrivateInterfaceName(Long.valueOf(vlan).longValue());
+ Map<String, String> params = new HashMap<String, String>();
+ params.put("type", "config");
+ params.put("action", "get");
+ params.put("xpath", "/config/devices/entry/network/interface/"+_privateInterfaceType+"/entry[@name='"+_privateInterface+"']/layer3/units/entry[@name='"+_interfaceName+"']/ip/entry");
+ String response = request(PaloAltoMethod.GET, params);
+ if (validResponse(response) && responseNotEmpty(response)) {
+ NodeList response_body;
+ Document doc = getDocument(response);
+ XPath xpath = XPathFactory.newInstance().newXPath();
+ try {
+ XPathExpression expr = xpath.compile("/response[@status='success']/result/entry");
+ response_body = (NodeList) expr.evaluate(doc, XPathConstants.NODESET);
+ } catch (XPathExpressionException e) {
+ throw new ExecutionException(e.getCause().getMessage());
+ }
+ if (response_body.getLength() > 0) {
+ return response_body.item(0).getAttributes().getNamedItem("name").getTextContent();
+ }
+ }
+ return null;
+ }
+
+
+ /*
+ * XML API commands
+ */
+
+ /* Function to make calls to the Palo Alto API. */
+ /* All API calls will end up going through this function. */
+ protected String request(PaloAltoMethod method, Map<String, String> params) throws ExecutionException {
+ if (method != PaloAltoMethod.GET && method != PaloAltoMethod.POST) {
+ throw new ExecutionException("Invalid http method used to access the Palo Alto API.");
+ }
+
+ String responseBody = "";
+ String debug_msg = "Palo Alto Request\n";
+
+ // a GET method...
+ if (method == PaloAltoMethod.GET) {
+ String queryString = "?";
+ for (String key : params.keySet()) {
+ if (!queryString.equals("?")) {
+ queryString = queryString + "&";
+ }
+ try {
+ queryString = queryString + key+"="+URLEncoder.encode(params.get(key), "UTF-8");
+ } catch (UnsupportedEncodingException e) {
+ throw new ExecutionException(e.getMessage());
+ }
+ }
+ if (_key != null) {
+ queryString = queryString + "&key="+_key;
+ }
+
+ try {
+ debug_msg = debug_msg + "GET request: https://" + _ip + _apiUri + URLDecoder.decode(queryString, "UTF-8") + "\n";
+ } catch (UnsupportedEncodingException e) {
+ debug_msg = debug_msg + "GET request: https://" + _ip + _apiUri + queryString + "\n";
+ }
+
+
+ HttpGet get_request = new HttpGet("https://" + _ip + _apiUri + queryString);
+ ResponseHandler<String> responseHandler = new BasicResponseHandler();
+ try {
+ responseBody = _httpclient.execute(get_request, responseHandler);
+ } catch (IOException e) {
+ throw new ExecutionException(e.getMessage());
+ }
+ }
+
+ // a POST method...
+ if (method == PaloAltoMethod.POST) {
+ List <NameValuePair> nvps = new ArrayList <NameValuePair>();
+ for (String key : params.keySet()) {
+ nvps.add(new BasicNameValuePair(key, params.get(key)));
+ }
+ if (_key != null) {
+ nvps.add(new BasicNameValuePair("key", _key));
+ }
+
+ debug_msg = debug_msg + "POST request: https://" + _ip + _apiUri + "\n";
+ for (NameValuePair nvp : nvps) {
+ debug_msg = debug_msg + "param: "+nvp.getName()+", "+nvp.getValue() + "\n";
+ }
+
+ HttpPost post_request = new HttpPost("https://" + _ip + _apiUri);
+ try {
+ post_request.setEntity(new UrlEncodedFormEntity(nvps, HTTP.UTF_8));
+ } catch (UnsupportedEncodingException e) {
+ throw new ExecutionException(e.getMessage());
+ }
+ ResponseHandler<String> responseHandler = new BasicResponseHandler();
+ try {
+ responseBody = _httpclient.execute(post_request, responseHandler);
+ } catch (IOException e) {
+ throw new ExecutionException(e.getMessage());
+ }
+ }
+
+ debug_msg = debug_msg + prettyFormat(responseBody);
+ debug_msg = debug_msg + "\n" + responseBody.replace("\"", "\\\"") + "\n\n"; // test cases
+ //s_logger.debug(debug_msg); // this can be commented if we don't want to show each request in the log.
+
+ return responseBody;
+ }
+
+ /* Used for requests that require polling to get a result (eg: commit) */
+ private String requestWithPolling(PaloAltoMethod method, Map<String, String> params) throws ExecutionException {
+ String job_id;
+ String job_response = request(method, params);
+ Document doc = getDocument(job_response);
+ XPath xpath = XPathFactory.newInstance().newXPath();
+ try {
+ XPathExpression expr = xpath.compile("/response[@status='success']/result/job/text()");
+ job_id = (String) expr.evaluate(doc, XPathConstants.STRING);
+ } catch (XPathExpressionException e) {
+ throw new ExecutionException(e.getCause().getMessage());
+ }
+ if (job_id.length() > 0) {
+ boolean finished = false;
+ Map<String, String> job_params = new HashMap<String, String>();
+ job_params.put("type", "op");
+ job_params.put("cmd", "<show><jobs><id>"+job_id+"</id></jobs></show>");
+
+ while (!finished) {
+ String job_status;
+ String response = request(PaloAltoMethod.GET, job_params);
+ Document job_doc = getDocument(response);
+ XPath job_xpath = XPathFactory.newInstance().newXPath();
+ try {
+ XPathExpression expr = job_xpath.compile("/response[@status='success']/result/job/status/text()");
+ job_status = (String) expr.evaluate(job_doc, XPathConstants.STRING);
+ } catch (XPathExpressionException e) {
+ throw new ExecutionException(e.getCause().getMessage());
+ }
+ if (job_status.equals("FIN")) {
+ finished = true;
+ String job_result;
+ try {
+ XPathExpression expr = job_xpath.compile("/response[@status='success']/result/job/result/text()");
+ job_result = (String) expr.evaluate(job_doc, XPathConstants.STRING);
+ } catch (XPathExpressionException e) {
+ throw new ExecutionException(e.getCause().getMessage());
+ }
+ if (!job_result.equals("OK")) {
+ NodeList job_details;
+ try {
+ XPathExpression expr = job_xpath.compile("/response[@status='success']/result/job/details/line");
+ job_details = (NodeList) expr.evaluate(job_doc, XPathConstants.NODESET);
+ } catch (XPathExpressionException e) {
+ throw new ExecutionException(e.getCause().getMessage());
+ }
+ String error = "";
+ for (int i = 0; i < job_details.getLength(); i++) {
+ error = error + job_details.item(i).getTextContent() + "\n";
+ }
+ throw new ExecutionException(error);
+ }
+ return response;
+ } else {
+ try {
+ Thread.sleep(2000); // poll periodically for the status of the async job...
+ } catch (InterruptedException e) { /* do nothing */ }
+ }
+ }
+ } else {
+ return job_response;
+ }
+ return null;
+ }
+
+ /* Runs a sequence of commands and attempts to commit at the end. */
+ /* Uses the Command pattern to enable overriding of the response handling if needed. */
+ private synchronized boolean requestWithCommit(ArrayList<IPaloAltoCommand> commandList) throws ExecutionException {
+ boolean result = true;
+
+ if (commandList.size() > 0) {
+ // CHECK IF THERE IS PENDING CHANGES THAT HAVE NOT BEEN COMMITTED...
+ String pending_changes;
+ Map<String, String> check_params = new HashMap<String, String>();
+ check_params.put("type", "op");
+ check_params.put("cmd", "<check><pending-changes></pending-changes></check>");
+ String check_response = request(PaloAltoMethod.GET, check_params);
+ Document check_doc = getDocument(check_response);
+ XPath check_xpath = XPathFactory.newInstance().newXPath();
+ try {
+ XPathExpression expr = check_xpath.compile("/response[@status='success']/result/text()");
+ pending_changes = (String) expr.evaluate(check_doc, XPathConstants.STRING);
+ } catch (XPathExpressionException e) {
+ throw new ExecutionException(e.getCause().getMessage());
+ }
+ if (pending_changes.equals("yes")) {
+ throw new ExecutionException("The Palo Alto has uncommited changes, so no changes can be made. Try again later or contact your administrator.");
+ } else {
+ // ADD A CONFIG LOCK TO CAPTURE THE PALO ALTO RESOURCE
+ String add_lock_status;
+ Map<String, String> add_lock_params = new HashMap<String, String>();
+ add_lock_params.put("type", "op");
+ add_lock_params.put("cmd", "<request><config-lock><add></add></config-lock></request>");
+ String add_lock_response = request(PaloAltoMethod.GET, add_lock_params);
+ Document add_lock_doc = getDocument(add_lock_response);
+ XPath add_lock_xpath = XPathFactory.newInstance().newXPath();
+ try {
+ XPathExpression expr = add_lock_xpath.compile("/response[@status='success']/result/text()");
+ add_lock_status = (String) expr.evaluate(add_lock_doc, XPathConstants.STRING);
+ } catch (XPathExpressionException e) {
+ throw new ExecutionException(e.getCause().getMessage());
+ }
+ if (add_lock_status.length() == 0) {
+ throw new ExecutionException("The Palo Alto is locked, no changes can be made at this time.");
+ }
+
+ try {
+ // RUN THE SEQUENCE OF COMMANDS
+ for (IPaloAltoCommand command : commandList) {
+ result = (result && command.execute()); // run commands and modify result boolean
+ }
+
+ // COMMIT THE CHANGES (ALSO REMOVES CONFIG LOCK)
+ String commit_job_id;
+ Map<String, String> commit_params = new HashMap<String, String>();
+ commit_params.put("type", "commit");
+ commit_params.put("cmd", "<commit></commit>");
+ String commit_response = requestWithPolling(PaloAltoMethod.GET, commit_params);
+ Document commit_doc = getDocument(commit_response);
+ XPath commit_xpath = XPathFactory.newInstance().newXPath();
+ try {
+ XPathExpression expr = commit_xpath.compile("/response[@status='success']/result/job/id/text()");
+ commit_job_id = (String) expr.evaluate(commit_doc, XPathConstants.STRING);
+ } catch (XPathExpressionException e) {
+ throw new ExecutionException(e.getCause().getMessage());
+ }
+ if (commit_job_id.length() == 0) { // no commit was done, so release the lock...
+ // REMOVE THE CONFIG LOCK TO RELEASE THE PALO ALTO RESOURCE
+ String remove_lock_status;
+ Map<String, String> remove_lock_params = new HashMap<String, String>();
+ remove_lock_params.put("type", "op");
+ remove_lock_params.put("cmd", "<request><config-lock><remove></remove></config-lock></request>");
+ String remove_lock_response = request(PaloAltoMethod.GET, remove_lock_params);
+ Document remove_lock_doc = getDocument(remove_lock_response);
+ XPath remove_lock_xpath = XPathFactory.newInstance().newXPath();
+ try {
+ XPathExpression expr = remove_lock_xpath.compile("/response[@status='success']/result/text()");
+ remove_lock_status = (String) expr.evaluate(remove_lock_doc, XPathConstants.STRING);
+ } catch (XPathExpressionException e) {
+ throw new ExecutionException(e.getCause().getMessage());
+ }
+ if (remove_lock_status.length() == 0) {
+ throw new ExecutionException("Could not release the Palo Alto device. Please notify an administrator!");
+ }
+ }
+
+ } catch (ExecutionException ex) {
+ // REVERT TO RUNNING
+ String revert_job_id;
+ Map<String, String> revert_params = new HashMap<String, String>();
+ revert_params.put("type", "op");
+ revert_params.put("cmd", "<load><config><from>running-config.xml</from></config></load>");
+ requestWithPolling(PaloAltoMethod.GET, revert_params);
+
+ // REMOVE THE CONFIG LOCK TO RELEASE THE PALO ALTO RESOURCE
+ String remove_lock_status;
+ Map<String, String> remove_lock_params = new HashMap<String, String>();
+ remove_lock_params.put("type", "op");
+ remove_lock_params.put("cmd", "<request><config-lock><remove></remove></config-lock></request>");
+ String remove_lock_response = request(PaloAltoMethod.GET, remove_lock_params);
+ Document remove_lock_doc = getDocument(remove_lock_response);
+ XPath remove_lock_xpath = XPathFactory.newInstance().newXPath();
+ try {
+ XPathExpression expr = remove_lock_xpath.compile("/response[@status='success']/result/text()");
+ remove_lock_status = (String) expr.evaluate(remove_lock_doc, XPathConstants.STRING);
+ } catch (XPathExpressionException e) {
+ throw new ExecutionException(e.getCause().getMessage());
+ }
+ if (remove_lock_status.length() == 0) {
+ throw new ExecutionException("Could not release the Palo Alto device. Please notify an administrator!");
+ }
+
+ throw ex; // Bubble up the reason we reverted...
+ }
+
+ return result;
+ }
+ } else {
+ return true; // nothing to do
+ }
+ }
+
+ /* A default response handler to validate that the request was successful. */
+ public boolean validResponse(String response) throws ExecutionException {
+ NodeList response_body;
+ Document doc = getDocument(response);
+ XPath xpath = XPathFactory.newInstance().newXPath();
+ try {
+ XPathExpression expr = xpath.compile("/response[@status='success']");
+ response_body = (NodeList) expr.evaluate(doc, XPathConstants.NODESET);
+ } catch (XPathExpressionException e) {
+ throw new ExecutionException(e.getCause().getMessage());
+ }
+
+ if (response_body.getLength() > 0) {
+ return true;
+ } else {
+ NodeList error_details;
+ try {
+ XPathExpression expr = xpath.compile("/response/msg/line/line");
+ error_details = (NodeList) expr.evaluate(doc, XPathConstants.NODESET);
+ } catch (XPathExpressionException e) {
+ throw new ExecutionException(e.getCause().getMessage());
+ }
+ if (error_details.getLength() == 0) {
+ try {
+ XPathExpression expr = xpath.compile("/response/msg/line");
+ error_details = (NodeList) expr.evaluate(doc, XPathConstants.NODESET);
+ } catch (XPathExpressionException e) {
+ throw new ExecutionException(e.getCause().getMessage());
+ }
+
+ if (error_details.getLength() == 0) {
+ try {
+ XPathExpression expr = xpath.compile("/response/result/msg");
+ error_details = (NodeList) expr.evaluate(doc, XPathConstants.NODESET);
+ } catch (XPathExpressionException e) {
+ throw new ExecutionException(e.getCause().getMessage());
+ }
+ }
+ }
+ String error = "";
+ for (int i = 0; i < error_details.getLength(); i++) {
+ error = error + error_details.item(i).getTextContent() + "\n";
+ }
+ throw new ExecutionException(error);
+ }
+ }
+
+ /* Validate that the response is not empty. */
+ public boolean responseNotEmpty(String response) throws ExecutionException {
+ NodeList response_body;
+ Document doc = getDocument(response);
+ XPath xpath = XPathFactory.newInstance().newXPath();
+ try {
+ XPathExpression expr = xpath.compile("/response[@status='success']");
+ response_body = (NodeList) expr.evaluate(doc, XPathConstants.NODESET);
+ } catch (XPathExpressionException e) {
+ throw new ExecutionException(e.getCause().getMessage());
+ }
+
+ if (response_body.getLength() > 0 &&
+ (!response_body.item(0).getTextContent().equals("") ||
+ (response_body.item(0).hasChildNodes() && response_body.item(0).getFirstChild().hasChildNodes()))) {
+ return true;
+ } else {
+ return false;
+ }
+ }
+
+ /* Get the type of interface from the PA device. */
+ private String getInterfaceType(String interface_name) throws ExecutionException {
+ String[] types = { InterfaceType.ETHERNET.toString(), InterfaceType.AGGREGATE.toString() };
+ for (String type : types) {
+ Map<String, String> params = new HashMap<String, String>();
+ params.put("type", "config");
+ params.put("action", "get");
+ params.put("xpath", "/config/devices/entry/network/interface/"+type+"/entry[@name='"+interface_name+"']");
+ String ethernet_response = request(PaloAltoMethod.GET, params);
+ if (validResponse(ethernet_response) && responseNotEmpty(ethernet_response)) {
+ return type;
+ }
+ }
+ return "";
+ }
+
+ /* Get the threat profile from the server if it exists. */
+ private boolean getThreatProfile(String profile) throws ExecutionException {
+ Map<String, String> params = new HashMap<String, String>();
+ params.put("type", "config");
+ params.put("action", "get");
+ params.put("xpath", "/config/devices/entry/vsys/entry[@name='vsys1']/profile-group/entry[@name='"+profile+"']");
+ String response = request(PaloAltoMethod.GET, params);
+ return (validResponse(response) && responseNotEmpty(response));
+ }
+
+ /* Get the log profile from the server if it exists. */
+ private boolean getLogProfile(String profile) throws ExecutionException {
+ Map<String, String> params = new HashMap<String, String>();
+ params.put("type", "config");
+ params.put("action", "get");
+ params.put("xpath", "/config/devices/entry/vsys/entry[@name='vsys1']/log-settings/profiles/entry[@name='"+profile+"']");
+ String response = request(PaloAltoMethod.GET, params);
+ return (validResponse(response) && responseNotEmpty(response));
+ }
+
+ /* Command Interface */
+ public interface IPaloAltoCommand {
+ public boolean execute() throws ExecutionException;
+ }
+
+ /* Command Abstract */
+ private abstract class AbstractPaloAltoCommand implements IPaloAltoCommand {
+ PaloAltoMethod method;
+ Map<String, String> params;
+
+ public AbstractPaloAltoCommand() {}
+
+ public AbstractPaloAltoCommand(PaloAltoMethod method, Map<String, String> params) {
+ this.method = method;
+ this.params = params;
+ }
+
+ public boolean execute() throws ExecutionException {
+ String response = request(method, params);
+ return validResponse(response);
+ }
+ }
+
+ /* Implement the default functionality */
+ private class DefaultPaloAltoCommand extends AbstractPaloAltoCommand {
+ public DefaultPaloAltoCommand(PaloAltoMethod method, Map<String, String> params) {
+ super(method, params);
+ }
+ }
+
+
+ /*
+ * Misc
+ */
+
+ private String genIpIdentifier(String ip) {
+ return ip.r
<TRUNCATED>