ca.uqac.dim.net.verify.NetworkChecker.java Source code

Java tutorial

Introduction

Here is the source code for ca.uqac.dim.net.verify.NetworkChecker.java

Source

/*-------------------------------------------------------------------------
Distributed Firewall Anomaly Detector
Copyright (C) 2012  Sylvain Hall
    
This program is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.
    
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
GNU General Public License for more details.
    
You should have received a copy of the GNU General Public License
along with this program.  If not, see <http://www.gnu.org/licenses/>.
 -------------------------------------------------------------------------*/
package ca.uqac.dim.net.verify;

import java.io.*;

import ca.uqac.logic.vl.Utilities;
import org.apache.commons.cli.*;

/**
 * Main executable class to discover anomalies in a network of
 * distributed firewalls.
 * @author sylvain
 *
 */
public class NetworkChecker {
    /**
     * Full path to the NuSMV executable
     */
    private static String NUSMV_EXEC = "NuSMV";

    /**
     * Main loop. Processes command line arguments, loads the network, builds and
     * sends a file to NuSMV and handles its response.
     * @param args Command-line arguments
     */
    public static void main(String[] args) {
        // Configuration variables
        boolean opt_show_file = false, opt_show_messages = true, opt_show_stats = false;

        // Create and parse command line options
        Option opt = null;
        Options options = new Options();
        options.addOption("f", "file", false, "Output NuSMV file to stdout");
        options.addOption("s", "time", false, "Display statistics");
        options.addOption("h", "help", false, "Show usage information");
        options.addOption("q", "quiet", false, "Do not display any message or explanation");
        opt = new Option("i", "file", true, "Input directory");
        opt.setRequired(true);
        options.addOption(opt);
        CommandLineParser parser = new PosixParser();
        CommandLine cmd = null;
        try {
            cmd = parser.parse(options, args);
        } catch (ParseException e) {
            System.err.println("Error parsing command line arguments");
            showUsage(options);
            System.exit(1);
        }
        if (cmd == null) {
            System.err.println("Error parsing command line arguments");
            showUsage(options);
            System.exit(1);
        }

        // Get options
        assert cmd != null;
        opt_show_file = cmd.hasOption("f");
        opt_show_messages = !cmd.hasOption("q");
        opt_show_stats = cmd.hasOption("s");

        if (opt_show_messages)
            System.err.println(
                    "Distributed anomaly verifier\n(C) 2012 Sylvain Hall, Universit du Qubec  Chicoutimi\n");

        // Get input directory and blob
        assert cmd.hasOption("i");
        String slash = System.getProperty("file.separator");
        String input_dir = cmd.getOptionValue("i");
        int loc = input_dir.lastIndexOf(slash);
        String dir_name = "", blob = "";
        if (loc == -1) {
            dir_name = "";
            blob = input_dir;
        } else {
            dir_name = input_dir.substring(0, loc);
            blob = input_dir.substring(loc + 1);
        }

        // Load network
        Network net = null;
        try {
            net = loadNetworkFromDirectory(dir_name, blob, slash);
        } catch (FileNotFoundException e) {
            System.err.print("Error reading ");
            System.err.println(e.getMessage());
            System.exit(1);
        } catch (IOException e) {
            System.err.println(e);
            System.exit(1);
        }

        // Build NuSMV file
        assert net != null;
        StringBuilder sb = new StringBuilder();
        if (opt_show_file) {
            sb.append("-- File auto-generated by DistributedChecker\n");
            sb.append("-- (C) 2012  Sylvain Hall, Universit du Qubec  Chicoutimi\n\n");
        }
        sb.append(net.toSmv(opt_show_file));
        // Simple shadowing
        String f_shadowing = "G (frozen -> !(interval_l <= rule_interval_l & interval_r >= rule_interval_r & decision != rule_decision))";
        sb.append("\n\nLTLSPEC\n").append(f_shadowing);

        // Run NuSMV and parse its return value
        if (opt_show_file) {
            System.out.println(sb);
            System.exit(0);
        }
        RuntimeInfos ri = null;
        try {
            ri = runNuSMV(sb.toString());
        } catch (IOException e) {
            System.err.println(e.getMessage());
            System.exit(1);
        } catch (InterruptedException e) {
            System.err.println(e.getMessage());
            System.exit(1);
        }
        assert ri != null;

        // If requested, compute and show an explanation from NuSMV's returned contents
        if (opt_show_messages) {
            ExplanationTrace t = null;
            try {
                t = net.explain(ri.m_return_contents);
            } catch (NuSmvParseException e) {
                System.err.println("Error reading NuSMV's answer");
                System.exit(1);
            }
            if (t == null) {
                // No explanation => no anomaly found
                System.out.println("No anomaly found");
            } else
                System.out.println(t);
        }

        // If requested, show runtime statistics
        if (opt_show_stats) {
            StringBuilder out = new StringBuilder();
            out.append(net.getNodeSize()).append(",");
            out.append(net.getFirewallRuleSize()).append(",");
            out.append(net.getRoutingTableSize()).append(",");
            out.append(ri.m_runtime);
            System.out.println(out);
        }
        System.exit(0);
    }

    /**
     * Simple class used to contain info resulting from calling NuSMV
     * @author sylvain
     *
     */
    private static class RuntimeInfos {
        String m_return_contents;
        long m_runtime;

        public RuntimeInfos(String return_contents, long runtime) {
            m_return_contents = return_contents;
            m_runtime = runtime;
        }
    }

    /**
     * Show command line usage
     * @param options
     */
    private static void showUsage(Options options) {
        int screen_width = 80;
        String header = "", footer = "";
        int padding_l = 1, padding_r = 1;
        HelpFormatter hf = new HelpFormatter();
        PrintWriter pw = new PrintWriter(System.err);
        hf.printHelp(pw, screen_width, "verify [-f] [-h] -i <arg> [-s]", header, options, padding_l, padding_r,
                footer);
        pw.close();
    }

    /**
     * Runs the NuSMV program as a spawned command-line process, and passes the
     * file to process through that process' standard input
     * @param file_contents A String containing the NuSMV model to process
     */
    private static RuntimeInfos runNuSMV(String file_contents) throws IOException, InterruptedException {
        StringBuilder sb = new StringBuilder();
        Runtime rt = Runtime.getRuntime();
        long time_start = 0, time_end = 0;
        // Start NuSMV, and feed the model through its standard input
        time_start = System.currentTimeMillis();
        Process p = rt.exec(NUSMV_EXEC);
        OutputStream o = p.getOutputStream();
        InputStream i = p.getInputStream();
        InputStream e = p.getErrorStream();
        Writer w = new PrintWriter(o);
        w.write(file_contents);
        w.close(); // Close stdin so NuSMV can start processing it
        // Wait for NuSMV to be done, then collect its standard output
        int exitVal = p.waitFor();
        if (exitVal != 0)
            throw new IOException("NuSMV's return value indicates an error in processing its input");
        BufferedReader br = new BufferedReader(new InputStreamReader(i));
        String line;
        while ((line = br.readLine()) != null) {
            sb.append(line).append("\n");
        }
        time_end = System.currentTimeMillis();
        i.close(); // Close stdout
        e.close(); // Close stderr
        return new RuntimeInfos(sb.toString(), time_end - time_start);
    }

    /**
     * Simple implementation of a FileFilter that takes as input
     * a file pattern in blob syntax (e.g. <tt>*.txt</tt> or
     * <tt>abc-*.ex?</tt>. Internally the BlobFilter converts the
     * blob into an equivalent regular expression and uses regex
     * matching to filter files.
     * @author sylvain
     *
     */
    private static class BlobFilter implements FilenameFilter {
        /**
         * Holds the regex equivalent to the blob passed to the constructor
         */
        private String m_blob;

        /**
         * Instantiates the filter.
         * @param blob A file pattern in blob syntax
         */
        public BlobFilter(String blob) {
            super();
            blob = blob.replace(".", "\\.");
            blob = blob.replace("*", ".*");
            blob = blob.replace("?", ".");
            m_blob = "^" + blob + "$";
        }

        @Override
        public boolean accept(File dir, String name) {
            return name.matches(m_blob);
        }

    }

    private static Network loadNetworkFromDirectory(String dirname, String blob, String slash) throws IOException {
        Network net = new Network();
        File dir = new File(dirname);
        String[] children = dir.list(new NetworkChecker.BlobFilter(blob));
        for (String filename : children) {
            String s = Utilities.readFileAsString(dirname + slash + filename);
            NetworkNode nn = new NetworkNode(s);
            net.add(nn);
        }
        return net;
    }

    @SuppressWarnings("unused")
    private static Network loadNetworkFromFile(String filename) {
        Network net = new Network();
        try {
            String file_contents = Utilities.readFileAsString(filename);
            String[] lines = file_contents.split("[\\r|\\n]");
            for (String li : lines) {
                li = li.trim();
                if (li.isEmpty())
                    continue;
                if (li.startsWith("#"))
                    continue;
                String s = Utilities.readFileAsString(li);
                NetworkNode nn = new NetworkNode(s);
                net.add(nn);
            }
        } catch (IOException e) {
            e.printStackTrace();
        }

        return net;
    }
}