Example #1
0
    def test_case2(self):
        # Shortest-path interdiction from PyomoGallery.
        ans = [5.0, 17.0, 100.0]

        fname = os.path.join("tests", "test_case2.xlsx")
        topology = NetworkTopology(fname)

        solver = "glpk"
        attack = 0

        model = SPInterdiction(topology, attack, solver)
        primal, idual = model.solve()

        self.assertTrue(np.allclose(primal.OBJ(), ans[0], atol=0.001))
        self.assertTrue(np.allclose(idual.OBJ(), ans[0], atol=0.001))

        model.set_attacks(1)
        primal, idual = model.solve()

        self.assertTrue(np.allclose(primal.OBJ(), ans[1], atol=0.001))
        self.assertTrue(np.allclose(idual.OBJ(), ans[1], atol=0.001))

        model.set_attacks(2)
        primal, idual = model.solve()

        self.assertTrue(np.allclose(primal.OBJ(), ans[2], atol=0.001))
        self.assertTrue(np.allclose(idual.OBJ(), ans[2], atol=0.001))
Example #2
0
    def test_case3(self):
        # Min-cost-flow interdiction from PyomoGallery.
        ans = [700.0, 7300.0, 21000.0]

        fname = os.path.join("tests", "test_case3.xlsx")
        topology = NetworkTopology(fname)

        solver = "glpk"
        attack = 0

        model = MinCostFlowInterdiction(topology, attack, solver)
        primal, idual = model.solve()

        self.assertTrue(np.allclose(primal.OBJ(), ans[0], atol=0.001))
        self.assertTrue(np.allclose(idual.OBJ(), ans[0], atol=0.001))

        model.set_attacks(1)
        primal, idual = model.solve()

        self.assertTrue(np.allclose(primal.OBJ(), ans[1], atol=0.001))
        self.assertTrue(np.allclose(idual.OBJ(), ans[1], atol=0.001))

        model.set_attacks(2)
        primal, idual = model.solve()

        self.assertTrue(np.allclose(primal.OBJ(), ans[2], atol=0.001))
        self.assertTrue(np.allclose(idual.OBJ(), ans[2], atol=0.001))
Example #3
0
    def __init__(self, topology):
        self.topology = None
        if isinstance(topology, NetworkTopology):
            self.topology = topology
        elif isinstance(topology, str):  # filename is provided
            self.topology = NetworkTopology(topology)
        else:
            raise AttributeError("unknown topology provided")

        if "threat" not in self.topology.node_data:
            self.topology.node_data["threat"] = self._compute_node_threat()
        if "risk" not in self.topology.node_data:
            self.topology.node_data["risk"] = self._compute_node_risk()
        if "threat" not in self.topology.link_data:
            self.topology.link_data["threat"] = self._compute_link_threat()
        if "risk" not in self.topology.link_data:
            self.topology.link_data["risk"] = self._compute_link_risk()
Example #4
0
    def __init__(self, topology, attacks=0, solver="cplex", tee=False):
        self._topology = None
        if isinstance(topology, NetworkTopology):
            self._topology = topology
        elif isinstance(topology, str):  # filename is provided
            self._topology = NetworkTopology(topology)
        else:
            raise AttributeError("unknown topology provided")

        self._attacks = attacks
        self._solver = solver
        self._tee = tee

        self._primal = self._create_primal()
        self._idual = self._create_interdict_dual()
Example #5
0
    def __init__(self, topology, attacks=0, solver="cplex", tee=False):
        self._topology = None
        if isinstance(topology, NetworkTopology):
            self._topology = topology
        elif isinstance(topology, str):  # filename is provided
            self._topology = NetworkTopology(topology)
        else:
            raise AttributeError("unknown topology provided")

        self._attacks = attacks
        self._solver = solver
        self._tee = tee

        # Compute nCmax
        self._nCmax = len(self._topology.node_set) \
            * self._topology.link_data["risk"].max()

        self._primal = self._create_primal()
        self._idual = self._create_interdict_dual()
Example #6
0
def driver(xlsx_file, **kwargs):
    """Driver for SNRAM."""
    # Set input arguments:
    png_file = kwargs.get("png_file", None)
    save_xlsx = kwargs.get("save_xlsx", None)
    run_type = kwargs.get("run_type", "stackelberg")
    budget = int(kwargs.get("budget", 1))
    interdict = kwargs.get("interdict", "max-flow")
    attacks = int(kwargs.get("attacks", 0))
    solver = kwargs.get("solver", "cplex")
    max_iter = int(kwargs.get("max_iter", 10))
    tee = kwargs.get("tee", False)

    _print_header()

    # Initialise network topology:
    topology = NetworkTopology(xlsx_file)
    if png_file:
        topology.plot(png_file)

    # Conduct network risk assessment:
    network_risk = NetworkRisk(topology)
    network_risk.risk_assessment()

    # Identify critical assets:
    network_risk.critical_assets()

    if run_type == "stackelberg":
        topology = stackelberg(network_risk, budget, max_iter)
    elif run_type == "prepare":
        defender = Defender(network_risk, budget)
        topology = defender.prepare()
    elif run_type == "mitigate":
        defender = Defender(network_risk, budget)
        topology = defender.mitigate()
    elif run_type == "threat":
        attacker = Attacker(network_risk, budget)
        topology = attacker.threat()
    elif run_type == "interdict":
        interdiction(topology, interdict, attacks, solver, tee)

    if save_xlsx:
        topology.to_excel(save_xlsx)
Example #7
0
class NetworkRisk:
    """Class for handling network risks."""

    def __init__(self, topology):
        self.topology = None
        if isinstance(topology, NetworkTopology):
            self.topology = topology
        elif isinstance(topology, str):  # filename is provided
            self.topology = NetworkTopology(topology)
        else:
            raise AttributeError("unknown topology provided")

        if "threat" not in self.topology.node_data:
            self.topology.node_data["threat"] = self._compute_node_threat()
        if "risk" not in self.topology.node_data:
            self.topology.node_data["risk"] = self._compute_node_risk()
        if "threat" not in self.topology.link_data:
            self.topology.link_data["threat"] = self._compute_link_threat()
        if "risk" not in self.topology.link_data:
            self.topology.link_data["risk"] = self._compute_link_risk()

    def _compute_node_threat(self):
        """Compute threat index from the degree centrality of the node."""
        degree = self.topology.node_degree_centrality()
        return [int(round(di * THREAT_MAX)) for di in degree]

    def _compute_link_threat(self):
        """Compute threat index from the edge betweenness centrality."""
        betweenness = self.topology.link_betweenness_centrality()
        return [int(round(bi * THREAT_MAX)) for bi in betweenness]

    def _compute_node_risk(self):
        """Compute node risk = threat * vulnerability * consequence."""
        if "threat" in self.topology.node_data:
            threat = self.topology.node_data["threat"]
        else:
            threat = [THREAT_MIN] * len(self.topology.node_data)
        if "vulnerability" in self.topology.node_data:
            vuln = self.topology.node_data["vulnerability"]
        else:
            vuln = [VULN_MIN] * len(self.topology.node_data)
        if "consequence" in self.topology.node_data:
            cons = self.topology.node_data["consequence"]
        else:
            cons = [CONS_MIN] * len(self.topology.node_data)
        risk = self.compute_risk(threat, vuln, cons)
        self.topology.node_data["risk"] = risk
        return risk

    def _compute_link_risk(self):
        """Compute link risk = threat * vulnerability * consequence."""
        if "threat" in self.topology.link_data:
            threat = self.topology.link_data["threat"]
        else:
            threat = [THREAT_MIN] * len(self.topology.link_data)
        if "vulnerability" in self.topology.link_data:
            vuln = self.topology.link_data["vulnerability"]
        else:
            vuln = [VULN_MIN] * len(self.topology.link_data)
        if "consequence" in self.topology.link_data:
            cons = self.topology.link_data["consequence"]
        else:
            cons = [CONS_MIN] * len(self.topology.link_data)
        risk = self.compute_risk(threat, vuln, cons)
        self.topology.link_data["risk"] = risk
        return risk

    def get_threat(self, asset):
        """Get threat vector for given asset."""
        if asset == "nodes":
            return self.topology.node_data["threat"]
        elif asset == "links":
            return self.topology.link_data["threat"]

    def set_threat(self, asset, threat):
        """Set threat vector for given asset and update risk vector."""
        if asset == "nodes":
            assert len(self.topology.node_data["threat"]) == len(threat)
            self.topology.node_data["threat"] = threat
            self._compute_node_risk()
        elif asset == "links":
            assert len(self.topology.link_data["threat"]) == len(threat)
            self.topology.link_data["threat"] = threat
            self._compute_link_risk()

    def get_vulnerability(self, asset):
        """Get vulnerability vector for given asset."""
        if asset == "nodes":
            return self.topology.node_data["vulnerability"]
        elif asset == "links":
            return self.topology.link_data["vulnerability"]

    def set_vulnerability(self, asset, vuln):
        """Set vulnerability vector for given asset and update risk vector."""
        if asset == "nodes":
            assert len(self.topology.node_data["vulnerability"]) == len(vuln)
            self.topology.node_data["vulnerability"] = vuln
            self._compute_node_risk()
        elif asset == "links":
            assert len(self.topology.link_data["vulnerability"]) == len(vuln)
            self.topology.link_data["vulnerability"] = vuln
            self._compute_link_risk()

    def get_consequence(self, asset):
        """Get consequence vector for given asset."""
        if asset == "nodes":
            return self.topology.node_data["consequence"]
        elif asset == "links":
            return self.topology.link_data["consequence"]

    def set_consequence(self, asset, vuln):
        """Set consequence vector for given asset and update risk vector."""
        if asset == "nodes":
            assert len(self.topology.node_data["consequence"]) == len(vuln)
            self.topology.node_data["consequence"] = vuln
            self._compute_node_risk()
        elif asset == "links":
            assert len(self.topology.link_data["consequence"]) == len(vuln)
            self.topology.link_data["consequence"] = vuln
            self._compute_link_risk()

    def get_risk(self, asset):
        """Get risk vector for given asset."""
        if asset == "nodes":
            return self.topology.node_data["risk"]
        elif asset == "links":
            return self.topology.link_data["risk"]

    def compute_risk(self, threat, vuln, cons):
        """Compute risk from threat, vulnerability and consequence vectors."""
        return [t * v * c for t, v, c in zip(threat, vuln, cons)]

    def find_critical_asset(self, asset, attribute):
        """Find index and maximum value for the given asset attribute."""
        val = asset.loc[asset["attackable"] == asset["attackable"].max()]
        # Asset with largest risk is most critical:
        val = val.loc[val[attribute] == val[attribute].max()]
        val = val.loc[val["risk"] == val["risk"].max()]
        idx = val.index.values[0]
        val = val[attribute].values[0]
        return (idx, val)

    def risk_assessment(self):
        """Conduct network risk assessment."""
        print("Network Risk Assessment:")
        print("%s" % ("-" * 70))
        print("Node\t\tT\tV\tC\tR")
        print("%s" % ("-" * 70))
        for node, threat, vuln, cons, risk in zip(self.topology.node_set,
                                                  self.topology.node_data["threat"],
                                                  self.topology.node_data["vulnerability"],
                                                  self.topology.node_data["consequence"],
                                                  self.topology.node_data["risk"]):
            print("%-12s\t%d\t%d\t%d\t%d" % (node, threat, vuln, cons, risk))
        print("%s" % ("-" * 70))

        print("%s" % ("-" * 70))
        print("Link\t\tT\tV\tC\tR")
        print("%s" % ("-" * 70))
        for link, threat, vuln, cons, risk in zip(self.topology.link_set,
                                                  self.topology.link_data["threat"],
                                                  self.topology.link_data["vulnerability"],
                                                  self.topology.link_data["consequence"],
                                                  self.topology.link_data["risk"]):
            link_ij = "(" + str(link[0]) + ", " + str(link[1]) + ")"
            print("%-12s\t%d\t%d\t%d\t%d" %
                  (link_ij, threat, vuln, cons, risk))
        print("%s" % ("-" * 70))
        print("T = Threat (1-5)")
        print("V = Vulnerability (1-5)")
        print("C = Consequence (1-5)")
        print("R = Risk (T x V x C)")

    def critical_assets(self):
        """Identify critical assets."""
        print("\nCritical Assets:")
        print("%s" % ("-" * 70))
        print("                                 Index\t\tValue")
        print("%s" % ("-" * 70))

        # Analyse nodes:
        node_data = self.topology.node_data
        idx, val = self.find_critical_asset(node_data, "threat")
        print("Node with largest threat:        %s\t\t%d" % (idx, val))

        idx, val = self.find_critical_asset(node_data, "vulnerability")
        print("Node with largest vulnerability: %s\t\t%d" % (idx, val))

        idx, val = self.find_critical_asset(node_data, "consequence")
        print("Node with largest consequence:   %s\t\t%d" % (idx, val))

        idx, val = self.find_critical_asset(node_data, "risk")
        print("Node with largest risk:          %s\t\t%d" % (idx, val))
        print()

        # Analyse links:
        link_data = self.topology.link_data
        idx, val = self.find_critical_asset(link_data, "threat")
        sij = "(" + str(idx[0]) + ", " + str(idx[1]) + ")"
        print("Link with largest threat:        %-12s\t%d" % (sij, val))

        idx, val = self.find_critical_asset(link_data, "vulnerability")
        sij = "(" + str(idx[0]) + ", " + str(idx[1]) + ")"
        print("Link with largest vulnerability: %-12s\t%d" % (sij, val))

        idx, val = self.find_critical_asset(link_data, "consequence")
        sij = "(" + str(idx[0]) + ", " + str(idx[1]) + ")"
        print("Link with largest consequence:   %-12s\t%d" % (sij, val))

        idx, val = self.find_critical_asset(link_data, "risk")
        sij = "(" + str(idx[0]) + ", " + str(idx[1]) + ")"
        print("Link with largest risk:          %-12s\t%d" % (sij, val))
        print("%s" % ("-" * 70))

        art_pts = self.topology.articulation_points()
        print("Articulation points: ", end="")
        if len(art_pts) == 0 or art_pts is None:
            print("None")
        else:
            for node in art_pts:
                print("%s, " % node, end="")
            print()
        print()