org.eclipse.kura.linux.net.iptables.LinuxFirewall.java Source code

Java tutorial

Introduction

Here is the source code for org.eclipse.kura.linux.net.iptables.LinuxFirewall.java

Source

/**
 * Copyright (c) 2011, 2014 Eurotech and/or its affiliates
 *
 *  All rights reserved. This program and the accompanying materials
 *  are made available under the terms of the Eclipse Public License v1.0
 *  which accompanies this distribution, and is available at
 *  http://www.eclipse.org/legal/epl-v10.html
 *
 * Contributors:
 *   Eurotech
 */
/*
* Copyright (c) 2013 Eurotech Inc. All rights reserved.
*/

package org.eclipse.kura.linux.net.iptables;

import java.io.BufferedReader;
import java.io.DataInputStream;
import java.io.File;
import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.FileOutputStream;
import java.io.FileReader;
import java.io.IOException;
import java.io.InputStreamReader;
import java.io.PrintWriter;
import java.util.ArrayList;
import java.util.Iterator;
import java.util.LinkedHashSet;
import java.util.Set;
import java.util.StringTokenizer;

import org.apache.commons.io.FileUtils;
import org.eclipse.kura.KuraErrorCode;
import org.eclipse.kura.KuraException;
import org.eclipse.kura.core.util.ProcessUtil;
import org.eclipse.kura.core.util.SafeProcess;
import org.eclipse.kura.linux.net.util.KuraConstants;
import org.eclipse.kura.net.IPAddress;
import org.eclipse.kura.net.NetworkPair;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

/**
 * Implementation class for the ILinuxFirewallService
 * 
 * @author eurotech
 */
public class LinuxFirewall {
    private static final Logger s_logger = LoggerFactory.getLogger(LinuxFirewall.class);

    private static final String[] HEADER = { "#!/bin/sh", "# IPTables Firewall script", "" };

    private static final String OS_VERSION = System.getProperty("kura.os.version");

    private static final String[] DEFAULT_POLICY = HEADER;

    private static final String[] CLEAR_ALL_CHAINS = { "", "#Clear all Built-in Chains", "iptables -F INPUT",
            "iptables -F OUTPUT", "iptables -F FORWARD", "iptables -t nat -F", "" };

    private static final String[] BLOCK_POLICY = { "", "#Block all ports for input traffic",
            "iptables -P INPUT DROP", "#block Output Traffic", "iptables -P OUTPUT ACCEPT",
            "#block forward Traffic", "iptables -P FORWARD DROP", "",
            "#Allow all traffic to the loop back interface", "iptables -A INPUT -i lo -j ACCEPT", "",
            "#Allow Only incoming connection related to Outgoing connection",
            "iptables -A INPUT -m state --state ESTABLISHED,RELATED -j ACCEPT", "" };

    /*
    private static final String UNBLOCK_POLICY = "\n" + "#unBlock all ports for input traffic\n" + "iptables -P INPUT ACCEPT\n" + "#Accept Output Traffic\n" + "iptables -P OUTPUT ACCEPT\n"
              + "#unblock forward Traffic\n" + "iptables -P FORWARD ACCEPT\n" + "\n"
              + "#Allow all traffic to loop back interface\n" + "iptables -A INPUT -i lo -j ACCEPT\n" + "\n"
              + "#Allow Only incoming connection related to Outgoing connection\n" + "iptables -A INPUT -m state --state ESTABLISHED,RELATED -j ACCEPT\n" + "\n";
     */

    private static final String[] ALLOW_ICMP = { "#allow inbound ICMP requests",
            "iptables -A INPUT -p icmp --icmp-type 8 -m state --state NEW,ESTABLISHED,RELATED -j ACCEPT",
            "iptables -A OUTPUT -p icmp --icmp-type 0 -m state --state ESTABLISHED,RELATED -j ACCEPT", "" };

    private static final String[] DO_NOT_ALLOW_ICMP = { "#Do not allow inbound ICMP requests",
            "iptables -A INPUT -p icmp --icmp-type 8 -m state --state NEW,ESTABLISHED,RELATED -j DROP",
            "iptables -A OUTPUT -p icmp --icmp-type 0 -m state --state ESTABLISHED,RELATED -j DROP", "" };

    private static final String[] ALLOW_FORWARDING = { "#allow fowarding if any masquerade is defined",
            "echo 1 > /proc/sys/net/ipv4/ip_forward" };

    private static final String[] DO_NOT_ALLOW_FORWARDING = {
            "#do not allow fowarding unless masquerade is defined", "echo 0 > /proc/sys/net/ipv4/ip_forward" };

    private static final String[] FOOTER = { "#source a custom firewall script",
            "source /etc/init.d/firewall_cust 2> /dev/null" };

    private static LinuxFirewall s_linuxFirewall;

    private static Object s_lock = new Object();

    private static final String FIREWALL_SCRIPT_NAME = "/etc/init.d/firewall";
    private static final String FIREWALL_TMP_SCRIPT_NAME = "/etc/init.d/firewall.tmp";

    private LinkedHashSet<LocalRule> m_localRules;
    private LinkedHashSet<PortForwardRule> m_portForwardRules;
    private LinkedHashSet<NATRule> m_autoNatRules;
    private LinkedHashSet<NATRule> m_natRules;
    private LinkedHashSet<String> m_customRules;
    private boolean m_allowIcmp;
    private boolean m_allowForwarding;

    private LinuxFirewall() {
        try {
            try {
                File oscfile = new File(FIREWALL_SCRIPT_NAME);
                if (oscfile.exists() == false) {
                    FileOutputStream fos = new FileOutputStream(oscfile);
                    s_logger.debug(oscfile + " new file created");
                    PrintWriter pw = new PrintWriter(fos);
                    for (String line : DEFAULT_POLICY) {
                        pw.println(line);
                    }
                    pw.println();
                    pw.close();
                } else {
                    s_logger.debug(oscfile + " file already exists");
                }
            } catch (IOException e) {
                s_logger.error("cannot create or read file");// File did not exist and was created
            }
            initialize();
        } catch (Exception e) {
            e.printStackTrace();
            s_logger.error("failed to initialize LinuxFirewall");
        }
    }

    public static LinuxFirewall getInstance() {
        if (s_linuxFirewall == null) {
            s_linuxFirewall = new LinuxFirewall();
        }

        return s_linuxFirewall;
    }

    public ArrayList<String> readFileLinebyLine(String sourceFile) {
        ArrayList<String> destination = new ArrayList<String>();
        DataInputStream in = null;
        BufferedReader br = null;

        try {
            // Open the file that is the first command line parameter
            FileInputStream fstream = new FileInputStream(sourceFile);

            // Get the object of DataInputStream
            in = new DataInputStream(fstream);
            br = new BufferedReader(new InputStreamReader(in));
            String strLine;
            int i = 0;

            // Read File Line By Line
            while ((strLine = br.readLine()) != null) {
                // Print the content on the console
                destination.add(i, strLine);
                i = i + 1;
            }

            // Close the input stream
            in.close();
        } catch (FileNotFoundException e) {// Catch exception if any
            s_logger.error("the file: " + sourceFile + " does not exist", e);
        } catch (IOException ioe) {
            s_logger.error("IOException while trying to open: " + sourceFile, ioe);
        } finally {
            if (in != null) {
                try {
                    in.close();
                } catch (IOException ex) {
                    s_logger.error("I/O Exception while closing DataInputStream!", ex);
                }
            }
            if (br != null) {
                try {
                    br.close();
                } catch (IOException ex) {
                    s_logger.error("I/O Exception while closing BufferedReader!", ex);
                }
            }
        }

        s_logger.trace("size of destination is" + destination.size());
        return destination;
    }

    public void createFile(String file1) throws KuraException {
        File file = new File(file1);
        if (file.exists() == true) {
            s_logger.debug(file + " already exists in getdefaultroute method");

        } else {
            s_logger.debug(file + " does not exist in getdefaultroute method");
            try {
                file.createNewFile();
            } catch (IOException e) {
                s_logger.error(file + ", ERROR creating new file");
                throw new KuraException(KuraErrorCode.INTERNAL_ERROR, e);
            }
            s_logger.debug("new File: " + file + " created");
        }
    }

    public void initialize() throws KuraException {

        s_logger.debug("initializing firewall ...");
        m_localRules = new LinkedHashSet<LocalRule>();
        m_portForwardRules = new LinkedHashSet<PortForwardRule>();
        m_autoNatRules = new LinkedHashSet<NATRule>();
        m_natRules = new LinkedHashSet<NATRule>();
        m_customRules = new LinkedHashSet<String>();
        m_allowIcmp = true;
        m_allowForwarding = false;

        s_logger.debug("initialize() :: Parsing current firewall configuraion");

        File tmpFirewallFile = new File(FIREWALL_TMP_SCRIPT_NAME);
        if (tmpFirewallFile.exists()) {
            tmpFirewallFile.delete();
        }

        BufferedReader br = null;

        try {
            br = new BufferedReader(new FileReader(FIREWALL_SCRIPT_NAME));
            String line = null;

            lineloop: while ((line = br.readLine()) != null) {
                //skip any predefined lines or comment lines
                if (line.trim().equals("")) {
                    continue;
                }
                if (line.trim().startsWith("#")) {
                    continue;
                }
                for (String headerLine : HEADER) {
                    if (line.equals(headerLine)) {
                        continue lineloop;
                    }
                }
                for (String clearAllChains : CLEAR_ALL_CHAINS) {
                    if (line.equals(clearAllChains)) {
                        continue lineloop;
                    }
                }
                for (String blockPolicy : BLOCK_POLICY) {
                    if (line.equals(blockPolicy)) {
                        continue lineloop;
                    }
                }
                for (String allowIcmp : ALLOW_ICMP) {
                    if (line.equals(allowIcmp)) {
                        m_allowIcmp = true;
                        continue lineloop;
                    }
                }
                for (String doNotAllowIcmp : DO_NOT_ALLOW_ICMP) {
                    if (line.equals(doNotAllowIcmp)) {
                        m_allowIcmp = false;
                        continue lineloop;
                    }
                }
                for (String allowForwarding : ALLOW_FORWARDING) {
                    if (line.equals(allowForwarding)) {
                        m_allowForwarding = true;
                        continue lineloop;
                    }
                }
                for (String doNotAllowForwarding : DO_NOT_ALLOW_FORWARDING) {
                    if (line.equals(doNotAllowForwarding)) {
                        m_allowForwarding = false;
                        continue lineloop;
                    }
                }
                for (String footer : FOOTER) {
                    if (line.equals(footer)) {
                        continue lineloop;
                    }
                }

                if (line.startsWith("iptables -I INPUT -p")) {
                    s_logger.debug("Found local rule");

                    //Local Rule
                    int dport = -1;
                    String sport = null;
                    String permittedMac = null;
                    String permittedNetwork = null;
                    String permittedInterfaceName = null;
                    String unpermittedInterfaceName = null;
                    int permittedNetworkMask = -1;
                    String protocol = null;

                    StringTokenizer st = new StringTokenizer(line);
                    while (st.hasMoreTokens()) {
                        String token = st.nextToken();
                        if (token.equals("iptables")) {
                            st.nextToken(); //skip -I
                            st.nextToken(); //skip INPUT
                        } else if (token.equals("-p")) {
                            protocol = st.nextToken();
                        } else if (token.equals("--dport")) {
                            dport = Integer.parseInt(st.nextToken());
                        } else if (token.equals("--sport")) {
                            sport = st.nextToken();
                        } else if (token.equals("--mac-source")) {
                            permittedMac = st.nextToken();
                        } else if (token.equals("-m")) {
                            st.nextToken(); //skip mac
                        } else if (token.equals("-s")) {
                            String[] permitted = st.nextToken().split("/");
                            permittedNetwork = permitted[0];
                            permittedNetworkMask = Integer.parseInt(permitted[1]);
                        } else if (token.equals("!")) {
                            st.nextToken(); //skip -i
                            unpermittedInterfaceName = st.nextToken();
                        } else if (token.equals("-i")) {
                            permittedInterfaceName = st.nextToken();
                        } else if (token.equals("-j")) {
                            //got to the end of the line...
                            break;
                        } else {
                            throw new KuraException(KuraErrorCode.CONFIGURATION_ERROR,
                                    "Error parsing LocalRule: " + line);
                        }
                    }

                    LocalRule localRule = new LocalRule(dport, protocol,
                            new NetworkPair(IPAddress.parseHostAddress(permittedNetwork),
                                    (short) permittedNetworkMask),
                            permittedInterfaceName, unpermittedInterfaceName, permittedMac, sport);
                    s_logger.debug("Adding local rule: " + localRule.toString());
                    m_localRules.add(localRule);
                } else if (line.startsWith("iptables -t nat -A PREROUTING")) {
                    s_logger.debug("Found port forward rule");

                    //Port Forward Rule
                    String inboundIfaceName = null;
                    String outboundIfaceName = null;
                    String protocol = null;
                    int inPort = -1;
                    int outPort = -1;
                    boolean masquerade = false;
                    String sport = null;
                    String permittedMac = null;
                    String permittedNetwork = null;
                    int permittedNetworkMask = -1;
                    String address = null;

                    StringTokenizer st = new StringTokenizer(line, "; \t\n\r\f");
                    while (st.hasMoreTokens()) {
                        String token = st.nextToken();
                        if (token.equals("iptables")) {
                            String tok1 = st.nextToken(); //skip -t
                            String tok2 = st.nextToken(); //skip nat
                            if (tok1.equals("-t") && tok2.equals("nat")) {
                                st.nextToken(); //skip -A
                                String tok3 = st.nextToken(); //skip PREROUTING/POSTROUTING
                                if (tok3.equals("POSTROUTING")) {
                                    // this is masquerading rule, set out-bound interface, masquerade flag and skip the rest
                                    st.nextToken(); //skip -o 
                                    outboundIfaceName = st.nextToken();
                                    masquerade = true;
                                    break;
                                }
                            } else if (tok1.equals("-A") && tok2.equals("FORWARD")) {
                                // this is a forwarding rule, skip it
                                break;
                            } else {
                                throw new KuraException(KuraErrorCode.CONFIGURATION_ERROR,
                                        "Error parsing LocalRule: " + line);
                            }
                        } else if (token.equals("-i")) {
                            inboundIfaceName = st.nextToken();
                        } else if (token.equals("-p")) {
                            protocol = st.nextToken();
                        } else if (token.equals("--dport")) {
                            inPort = Integer.parseInt(st.nextToken());
                        } else if (token.equals("--sport")) {
                            sport = st.nextToken();
                        } else if (token.equals("--mac-source")) {
                            permittedMac = st.nextToken();
                        } else if (token.equals("-m")) {
                            st.nextToken(); //skip mac
                        } else if (token.equals("-j")) {
                            st.nextToken(); //skip DNAT
                        } else if (token.equals("--to")) {
                            String[] to = st.nextToken().split(":");
                            address = to[0];
                            outPort = Integer.parseInt(to[1]);
                        } else if (token.equals("-s")) {
                            String[] permitted = st.nextToken().split("/");
                            permittedNetwork = permitted[0];
                            permittedNetworkMask = Integer.parseInt(permitted[1]);
                        } else {
                            throw new KuraException(KuraErrorCode.CONFIGURATION_ERROR,
                                    "Error parsing LocalRule: " + line);
                        }
                    }

                    PortForwardRule portForwardRule = new PortForwardRule(inboundIfaceName, outboundIfaceName,
                            address, protocol, inPort, outPort, masquerade, permittedNetwork, permittedNetworkMask,
                            permittedMac, sport);

                    s_logger.debug("Adding port forward rule: " + portForwardRule.toString());
                    m_portForwardRules.add(portForwardRule);
                } else if (line.startsWith("iptables -t nat -A POSTROUTING")) {
                    s_logger.debug("Found NAT rule");

                    //NAT Rule
                    String destinationInterface = null;
                    String sourceInterface = null;
                    boolean masquerade = false;

                    String protocol = null;
                    String source = null;
                    String destination = null;

                    //just do this one by one
                    StringTokenizer st = new StringTokenizer(line);
                    st.nextToken(); //skip iptables
                    String token = st.nextToken(); //get -t or -A (depending on whether or not masquerade is enabled)
                    if (token.equals("-t")) {
                        masquerade = true;
                        st.nextToken(); //skip nat
                        st.nextToken(); //skip -A
                        st.nextToken(); //skip POSTROUTING
                        String tok = st.nextToken(); //skip -o or -p
                        if (tok.equals("-p")) {
                            protocol = st.nextToken();
                            st.nextToken(); // skip -s
                            source = st.nextToken();
                            st.nextToken(); // skip -d
                            destination = st.nextToken();
                            st.nextToken(); // skip -o
                            destinationInterface = st.nextToken();
                        } else {
                            destinationInterface = st.nextToken();
                        }
                        st.nextToken(); //skip -j
                        st.nextToken(); //skip MASQUERADE
                        st.nextToken(); //skip iptables
                        st.nextToken(); //skip -A
                    }

                    //get the rest (or continue on if no MASQ)
                    st.nextToken(); //skip FORWARD
                    String tok = st.nextToken(); //skip -i or -p
                    if (tok.equals("-p")) {
                        st.nextToken(); // skip protocol
                        st.nextToken(); // skip -s
                        st.nextToken(); // skip source
                        st.nextToken(); // skip -d
                        st.nextToken(); // skip destination
                        st.nextToken(); // skip -i
                        st.nextToken(); // skip destination interface
                        st.nextToken(); //skip -o
                        sourceInterface = st.nextToken();
                    } else {
                        destinationInterface = st.nextToken();
                        st.nextToken(); //skip -o
                        sourceInterface = st.nextToken();
                    }

                    if (protocol == null) {
                        // used to be s_logger.debug
                        s_logger.debug("Parsed auto NAT rule with" + "   sourceInterface: " + sourceInterface
                                + "   destinationInterface: " + destinationInterface + "   masquerade: "
                                + masquerade);

                        NATRule natRule = new NATRule(sourceInterface, destinationInterface, masquerade);
                        s_logger.debug("Adding auto NAT rule " + natRule.toString());
                        m_autoNatRules.add(natRule);
                    } else {
                        s_logger.debug("Parsed NAT rule with" + "   sourceInterface: " + sourceInterface
                                + "   destinationInterface: " + destinationInterface + "   masquerade: "
                                + masquerade + "   protocol: " + protocol + "   source network/host: " + source
                                + "   destination network/host + destination");
                        NATRule natRule = new NATRule(sourceInterface, destinationInterface, protocol, source,
                                destination, masquerade);
                        s_logger.warn("Adding NAT rule " + natRule.toString());
                        m_natRules.add(natRule);
                    }
                } else if (line.startsWith("iptables -A FORWARD")) {
                    s_logger.debug("Found FORWARD rule");

                    //just do this one by one
                    StringTokenizer st = new StringTokenizer(line);
                    st.nextToken(); //skip iptables
                    st.nextToken(); //skip -A
                    st.nextToken(); //skip FORWARD
                    st.nextToken(); //skip -p
                    String protocol = st.nextToken();
                    st.nextToken(); // skip -s
                    String source = st.nextToken();
                    st.nextToken(); // skip -d
                    String destination = st.nextToken();
                    st.nextToken(); // skip -i
                    String destinationInterface = st.nextToken();
                    st.nextToken(); // skip -o
                    String sourceInterface = st.nextToken();

                    NATRule natRule = new NATRule(sourceInterface, destinationInterface, protocol, source,
                            destination, false);
                    s_logger.warn("Adding NAT rule (no MASQUERADING)" + natRule.toString());
                    m_natRules.add(natRule);
                } else {
                    throw new KuraException(KuraErrorCode.INTERNAL_ERROR,
                            "invalid line in /etc/init.d/firewall: " + line);
                }
            }
        } catch (Exception e) {
            throw new KuraException(KuraErrorCode.INTERNAL_ERROR, e);
        } finally {
            //close
            if (br != null) {
                try {
                    br.close();
                } catch (IOException e) {
                    throw new KuraException(KuraErrorCode.INTERNAL_ERROR, e);
                }
                br = null;
            }
        }
    }

    private boolean writeFile() throws KuraException {
        s_logger.trace("writing to file:  " + FIREWALL_TMP_SCRIPT_NAME);
        PrintWriter pw = null;
        try {
            pw = new PrintWriter(new FileOutputStream(FIREWALL_TMP_SCRIPT_NAME));
            for (String line : HEADER) {
                pw.println(line);
            }
            for (String line : CLEAR_ALL_CHAINS) {
                pw.println(line);
            }
            for (String line : BLOCK_POLICY) {
                pw.println(line);
            }

            if (m_allowIcmp) {
                for (String line : ALLOW_ICMP) {
                    pw.println(line);
                }
            } else {
                for (String line : DO_NOT_ALLOW_ICMP) {
                    pw.println(line);
                }
            }

            pw.println("#custom local service rules");
            Iterator<LocalRule> itLocalRules = m_localRules.iterator();
            while (itLocalRules.hasNext()) {
                pw.println(itLocalRules.next());
            }
            pw.println();
            pw.println("#custom port forward service rules");
            Iterator<PortForwardRule> itPortForwardRules = m_portForwardRules.iterator();
            while (itPortForwardRules.hasNext()) {
                pw.println(itPortForwardRules.next());
            }
            pw.println();
            pw.println("#custom automatic NAT service rules (if NAT option is enabled for LAN interface)");
            Iterator<NATRule> itAutoNatRules = m_autoNatRules.iterator();
            while (itAutoNatRules.hasNext()) {
                pw.println(itAutoNatRules.next());
            }
            pw.println();
            pw.println("#custom NAT service rules");
            Iterator<NATRule> itNatRules = m_natRules.iterator();
            while (itNatRules.hasNext()) {
                pw.println(itNatRules.next());
            }
            pw.println();
            pw.println("#custom rules");
            Iterator<String> itCustomRules = m_customRules.iterator();
            while (itCustomRules.hasNext()) {
                pw.println(itCustomRules.next());
            }
            pw.println();
            if (m_allowForwarding) {
                for (String line : ALLOW_FORWARDING) {
                    pw.println(line);
                }
            } else {
                for (String line : DO_NOT_ALLOW_FORWARDING) {
                    pw.println(line);
                }
            }

            pw.println();
            for (String line : FOOTER) {
                pw.println(line);
            }
            pw.close();

            SafeProcess proc = null;
            try {
                proc = ProcessUtil.exec("chmod 755 " + FIREWALL_TMP_SCRIPT_NAME);
                proc.waitFor();
            } finally {
                if (proc != null)
                    ProcessUtil.destroy(proc);
            }

            //move the file if we made it this far
            File tmpFirewallFile = new File(FIREWALL_TMP_SCRIPT_NAME);
            File firewallFile = new File(FIREWALL_SCRIPT_NAME);
            if (!FileUtils.contentEquals(tmpFirewallFile, firewallFile)) {
                if (tmpFirewallFile.renameTo(firewallFile)) {
                    s_logger.info("writeFile() :: Successfully wrote firewall file");
                    return true;
                } else {
                    s_logger.error("writeFile() :: Failed to write firewall file");
                    throw new KuraException(KuraErrorCode.CONFIGURATION_ERROR,
                            "error while building up new configuration file for firewall");
                }
            } else {
                s_logger.info("writeFile() :: Not rewriting firewall file because it is the same");
                return false;
            }
        } catch (Exception e) {
            throw new KuraException(KuraErrorCode.INTERNAL_ERROR, e);
        } finally {
            if (pw != null) {
                pw.close();
            }
        }
    }

    public void addCustomRule(String rule) throws KuraException {
        try {
            s_logger.info("adding custom local rule to  firewall configuration");
            m_customRules.add(rule);
            this.update();
        } catch (Exception e) {
            throw new KuraException(KuraErrorCode.INTERNAL_ERROR, e);
        }
    }

    public void addLocalRule(int port, String protocol, String permittedNetwork, String permittedNetworkPrefix,
            String permittedInterfaceName, String unpermittedInterfaceName, String permittedMAC,
            String sourcePortRange) throws KuraException {
        try {
            LocalRule newLocalRule = null;
            if (permittedNetwork != null && permittedNetworkPrefix != null) {
                s_logger.debug("permittedNetwork: " + permittedNetwork);
                s_logger.debug("permittedNetworkPrefix: " + permittedNetworkPrefix);

                newLocalRule = new LocalRule(port, protocol,
                        new NetworkPair(IPAddress.parseHostAddress(permittedNetwork),
                                Short.parseShort(permittedNetworkPrefix)),
                        permittedInterfaceName, unpermittedInterfaceName, permittedMAC, sourcePortRange);
            } else {
                newLocalRule = new LocalRule(port, protocol,
                        new NetworkPair(IPAddress.parseHostAddress("0.0.0.0"), (short) 0), permittedInterfaceName,
                        permittedInterfaceName, permittedMAC, sourcePortRange);
            }

            //make sure it is not already present
            for (LocalRule localRule : m_localRules) {
                if (newLocalRule.equals(localRule)) {
                    s_logger.warn("Not adding local rule that is already present: " + localRule);
                    return;
                }
            }

            s_logger.info("adding local rule to firewall configuration: " + newLocalRule.toString());
            m_localRules.add(newLocalRule);
            this.update();
        } catch (Exception e) {
            throw new KuraException(KuraErrorCode.INTERNAL_ERROR, e);
        }
    }

    public void addPortForwardRule(String inboundIface, String outboundIface, String address, String protocol,
            int inPort, int outPort, boolean masquerade, String permittedNetwork, String permittedNetworkPrefix,
            String permittedMAC, String sourcePortRange) throws KuraException {
        try {
            PortForwardRule newPortForwardRule = null;
            if (permittedNetworkPrefix != null) {
                newPortForwardRule = new PortForwardRule(inboundIface, outboundIface, address, protocol, inPort,
                        outPort, masquerade, permittedNetwork, Short.parseShort(permittedNetworkPrefix),
                        permittedMAC, sourcePortRange);
            } else {
                newPortForwardRule = new PortForwardRule(inboundIface, outboundIface, address, protocol, inPort,
                        outPort, masquerade, permittedNetwork, -1, permittedMAC, sourcePortRange);
            }

            //make sure it is not already present
            for (PortForwardRule portForwardRule : m_portForwardRules) {
                if (newPortForwardRule.equals(portForwardRule)) {
                    s_logger.warn("Not adding port forward rule that is already present: " + portForwardRule);
                    return;
                }
            }

            s_logger.info("adding port forward rule to firewall configuration: " + newPortForwardRule.toString());
            m_portForwardRules.add(newPortForwardRule);

            m_allowForwarding = true;
            this.update();
        } catch (Exception e) {
            throw new KuraException(KuraErrorCode.INTERNAL_ERROR, e);
        }
    }

    /**
     * Adds automatic NAT rule
     * 
     * @param sourceInterface
     * @param destinationInterface
     * @param masquerade
     * @throws EsfException
     */
    public void addNatRule(String sourceInterface, String destinationInterface, boolean masquerade)
            throws KuraException {

        try {
            if (sourceInterface == null || sourceInterface.isEmpty()) {
                s_logger.warn("Can't add auto NAT rule - source interface not specified");
                return;
            } else if (destinationInterface == null || destinationInterface.isEmpty()) {
                s_logger.warn("Can't add auto NAT rule - destination interface not specified");
                return;
            }

            NATRule newNatRule = new NATRule(sourceInterface, destinationInterface, masquerade);

            //make sure it is not already present
            for (NATRule natRule : m_autoNatRules) {
                if (newNatRule.equals(natRule)) {
                    s_logger.warn("Not adding auto nat rule that is already present: " + natRule);
                    return;
                }
            }

            s_logger.info("adding auto NAT rule to firewall configuration: " + newNatRule.toString());
            m_autoNatRules.add(newNatRule);
            m_allowForwarding = true;
            this.update();
        } catch (Exception e) {
            throw new KuraException(KuraErrorCode.INTERNAL_ERROR, e);
        }
    }

    /**
     * Adds NAT Rule
     * 
     * @param sourceInterface
     * @param destinationInterface
     * @param protocol
     * @param source
     * @param destination
     * @param masquerade
     * @throws EsfException
     */
    public void addNatRule(String sourceInterface, String destinationInterface, String protocol, String source,
            String destination, boolean masquerade) throws KuraException {

        try {
            if (sourceInterface == null || sourceInterface.isEmpty()) {
                s_logger.warn("Can't add NAT rule - source interface not specified");
                return;
            } else if (destinationInterface == null || destinationInterface.isEmpty()) {
                s_logger.warn("Can't add NAT rule - destination interface not specified");
                return;
            }

            NATRule newNatRule = new NATRule(sourceInterface, destinationInterface, protocol, source, destination,
                    masquerade);
            // TODO need to add comparison
            s_logger.info("adding NAT rule to firewall configuration: {}", newNatRule.toString());
            m_natRules.add(newNatRule);
            m_allowForwarding = true;
            this.update();
        } catch (Exception e) {
            throw new KuraException(KuraErrorCode.INTERNAL_ERROR, e);
        }
    }

    public Set<LocalRule> getLocalRules() throws KuraException {
        try {
            return m_localRules;
        } catch (Exception e) {
            throw new KuraException(KuraErrorCode.INTERNAL_ERROR, e);
        }
    }

    public Set<PortForwardRule> getPortForwardRules() throws KuraException {
        try {
            return m_portForwardRules;
        } catch (Exception e) {
            throw new KuraException(KuraErrorCode.INTERNAL_ERROR, e);
        }
    }

    public Set<NATRule> getAutoNatRules() throws KuraException {
        try {
            return m_autoNatRules;
        } catch (Exception e) {
            throw new KuraException(KuraErrorCode.INTERNAL_ERROR, e);
        }
    }

    public Set<NATRule> getNatRules() throws KuraException {
        try {
            return m_natRules;
        } catch (Exception e) {
            throw new KuraException(KuraErrorCode.INTERNAL_ERROR, e);
        }
    }

    public void deleteLocalRule(LocalRule rule) throws KuraException {
        try {
            m_localRules.remove(rule);
            this.update();
        } catch (Exception e) {
            throw new KuraException(KuraErrorCode.INTERNAL_ERROR, e);
        }
    }

    public void deletePortForwardRule(PortForwardRule rule) throws KuraException {
        try {
            m_portForwardRules.remove(rule);
            if (((m_autoNatRules != null) && (m_autoNatRules.size() < 1))
                    && ((m_natRules != null) && (m_natRules.size() < 1))
                    && ((m_portForwardRules != null) && (m_portForwardRules.size() < 1))) {

                m_allowForwarding = false;
            }
            this.update();
        } catch (Exception e) {
            throw new KuraException(KuraErrorCode.INTERNAL_ERROR, e);
        }
    }

    public void deleteAutoNatRule(NATRule rule) throws KuraException {
        try {
            m_autoNatRules.remove(rule);
            if (((m_autoNatRules != null) && (m_autoNatRules.size() < 1))
                    && ((m_natRules != null) && (m_natRules.size() < 1))
                    && ((m_portForwardRules != null) && (m_portForwardRules.size() < 1))) {

                m_allowForwarding = false;
            }
            update();
        } catch (Exception e) {
            throw new KuraException(KuraErrorCode.INTERNAL_ERROR, e);
        }
    }

    public void deleteAllLocalRules() throws KuraException {
        try {
            this.m_localRules.clear();
            this.update();
        } catch (Exception e) {
            throw new KuraException(KuraErrorCode.INTERNAL_ERROR, e);
        }
    }

    public void deleteAllPortForwardRules() throws KuraException {
        try {
            m_portForwardRules.clear();
            if (((m_autoNatRules != null) && (m_autoNatRules.size() < 1))
                    && ((m_natRules != null) && (m_natRules.size() < 1))) {

                m_allowForwarding = false;
            }
            this.update();
        } catch (Exception e) {
            throw new KuraException(KuraErrorCode.INTERNAL_ERROR, e);
        }
    }

    public void replaceAllNatRules(LinkedHashSet<NATRule> newNatRules) throws KuraException {
        try {
            m_autoNatRules = newNatRules;
            if (((m_autoNatRules != null) && (m_autoNatRules.size() > 0))
                    || ((m_natRules != null) && (m_natRules.size() > 0))
                    || ((m_portForwardRules != null) && (m_portForwardRules.size() > 0))) {

                m_allowForwarding = true;
            } else {
                m_allowForwarding = false;
            }
            this.update();
        } catch (Exception e) {
            throw new KuraException(KuraErrorCode.INTERNAL_ERROR, e);
        }
    }

    public void deleteAllAutoNatRules() throws KuraException {
        try {
            m_autoNatRules.clear();
            if ((m_natRules != null) && (m_natRules.size() < 1)
                    && ((m_portForwardRules != null) && (m_portForwardRules.size() < 1))) {

                m_allowForwarding = false;
            }
            this.update();
        } catch (Exception e) {
            throw new KuraException(KuraErrorCode.INTERNAL_ERROR, e);
        }
    }

    public void deleteAllNatRules() throws KuraException {
        try {
            m_natRules.clear();
            if (((m_autoNatRules != null) && (m_autoNatRules.size() < 1))
                    && ((m_portForwardRules != null) && (m_portForwardRules.size() < 1))) {
                m_allowForwarding = false;
            }
            this.update();
        } catch (KuraException e) {
            throw new KuraException(KuraErrorCode.INTERNAL_ERROR, e);
        }
    }

    public void blockAllPorts() throws KuraException {
        deleteAllLocalRules();
        deleteAllPortForwardRules();
        deleteAllAutoNatRules();
        this.update();
    }

    public void unblockAllPorts() throws KuraException {
        deleteAllLocalRules();
        deleteAllPortForwardRules();
        deleteAllAutoNatRules();
        this.update();
    }

    private void runScript() throws KuraException {
        SafeProcess proc = null;
        try {
            File file = new File(FIREWALL_SCRIPT_NAME);
            if (!file.exists()) {
                throw new KuraException(KuraErrorCode.INTERNAL_ERROR,
                        "Firewall configuration file: " + FIREWALL_SCRIPT_NAME + " does not exist.");
            }
            s_logger.info("Running the firewall script");
            proc = ProcessUtil.exec("sh " + FIREWALL_SCRIPT_NAME);
            proc.waitFor();
        } catch (Exception e) {
            throw new KuraException(KuraErrorCode.INTERNAL_ERROR, e);
        } finally {
            if (proc != null)
                ProcessUtil.destroy(proc);
        }
    }

    /*
     * Saves the current iptables config into /etc/sysconfig/iptables
     */
    private void iptablesSave() throws KuraException {
        SafeProcess proc = null;
        try {
            if (OS_VERSION.equals(
                    KuraConstants.Mini_Gateway.getImageName() + "_" + KuraConstants.Mini_Gateway.getImageVersion())
                    || (OS_VERSION.equals(KuraConstants.Intel_Edison.getImageName() + "_"
                            + KuraConstants.Intel_Edison.getImageVersion() + "_"
                            + KuraConstants.Intel_Edison.getTargetName()))) {
                proc = ProcessUtil.exec("iptables-save > /opt/eurotech/firewall_rules.fw");
                proc.waitFor();
            } else {
                proc = ProcessUtil.exec("service iptables save");
                proc.waitFor();
            }
        } catch (Exception e) {
            throw new KuraException(KuraErrorCode.INTERNAL_ERROR, e);
        } finally {
            if (proc != null)
                ProcessUtil.destroy(proc);
        }
    }

    public void enable() throws KuraException {
        this.update();
        this.iptablesSave();
    }

    public void disable() throws KuraException {
        this.iptablesSave();

        s_logger.trace("writing to file:  " + FIREWALL_TMP_SCRIPT_NAME);
        try {
            PrintWriter pw = new PrintWriter(new FileOutputStream(FIREWALL_TMP_SCRIPT_NAME));
            for (String line : HEADER) {
                pw.println(line);
            }
            for (String line : CLEAR_ALL_CHAINS) {
                pw.println(line);
            }
            pw.flush();
            pw.close();

            //move the file if we made it this far
            File tmpFirewallFile = new File(FIREWALL_TMP_SCRIPT_NAME);
            File firewallFile = new File(FIREWALL_SCRIPT_NAME);
            if (!FileUtils.contentEquals(tmpFirewallFile, firewallFile)) {
                if (tmpFirewallFile.renameTo(firewallFile)) {
                    s_logger.info("disable() :: Successfully wrote firewall file");
                    runScript();
                } else {
                    s_logger.error("disable() :: Failed to write firewall file");
                    throw new KuraException(KuraErrorCode.CONFIGURATION_ERROR,
                            "error while building up new configuration file for firewall");
                }
            } else {
                s_logger.info("disable() :: Not rewriting firewall file because it is the same");
            }
        } catch (Exception e) {
            throw new KuraException(KuraErrorCode.INTERNAL_ERROR, e);
        }
    }

    public void allowIcmp() {
        m_allowIcmp = true;
    }

    public void disableIcmp() {
        m_allowIcmp = false;
    }

    public void enableForwarding() {
        m_allowForwarding = true;
    }

    public void disableForwarding() {
        m_allowForwarding = false;
    }

    private void update() throws KuraException {
        synchronized (s_lock) {
            if (writeFile()) {
                runScript();
            }
        }
    }
}