def get_waypoint_policies(self, policies, forwarding_graphs, dominator_graphs, node_local_reachability=False): start_time = time.time() for subnet, graph in dominator_graphs.items(): all_destinations, destinations, dst_routers = self.get_destination_interfaces_for_subnet( subnet, forwarding_graphs[subnet]) # TODO how to deal with Loadbalancing to same prefix, but last hop is different router?! # only use router specific subnets (e.g., subnets that are connected at a single interface, such as loopback # and host subnets). This removes all subnet that just exist between two routers (usually /31) if len(all_destinations) > 1: continue for waypoint in self.waypoints: if waypoint in graph: candidates = [waypoint] sources = [PolicySource(waypoint)] while candidates: current_node = candidates.pop() if current_node not in graph: self.logger.error( "Node {node} is not in the dominator graph for subnet {subnet}." .format(node=current_node, subnet=subnet)) break predecessors = list(graph.predecessors(current_node)) candidates.extend(predecessors) for predecessor in predecessors: # prevent policies within a single router (e.g., r1 can reach r1:loopback0) if not (not node_local_reachability and predecessor in dst_routers): sources.append(PolicySource(predecessor)) for source in sources: if source.router not in dst_routers: for policy_destination in destinations[ source.router]: policies.append( (PolicyType.Waypoint, policy_destination, waypoint, source)) self.logger.debug( "Getting the waypoint policies from dominator and forwarding graph took {time:.4f}s." .format(time=time.time() - start_time, )) return policies
def get_reachability_policies(self, policies, forwarding_graphs, dominator_graphs, node_local_reachability=False): all_nodes = set(self.network.nodes()) # iterate over every forwarding graph and check if it is connected start_time = time.time() for subnet, graph in dominator_graphs.items(): all_destinations, destinations, dst_routers = self.get_destination_interfaces_for_subnet( subnet, forwarding_graphs[subnet]) # only use router specific subnets (e.g., subnets that are connected at a single interface, such as loopback # and host subnets). This removes all subnet that just exist between two routers (usually /31) if len(all_destinations) > 1 or len(all_destinations) == 0: continue # get all routers that can reach the destination and all routers that are isolated from it reachable_sources = set(graph.nodes()) isolated_sources = all_nodes.difference(reachable_sources) if 'sink' in reachable_sources: reachable_sources.remove("sink") # prevent policies within a single router (e.g., r1 can reach r1:loopback0) if not node_local_reachability: for dst_router in dst_routers: reachable_sources.remove(dst_router) # add all reachability policies for source in reachable_sources: policy_source = PolicySource(source) policy_destinations = destinations[source] for policy_destination in policy_destinations: policies.append((PolicyType.Reachability, policy_destination, 0, policy_source)) # add all isolation policies for source in isolated_sources: policy_source = PolicySource(source) for policy_destination in all_destinations: policies.append((PolicyType.Isolation, policy_destination, 0, policy_source)) self.logger.debug( "Getting the reachability policies from the forwarding graphs took {time:.4f}s." .format(time=time.time() - start_time)) return policies
def get_two_interfaces_network(self, local=False): subnet = IPv4Network("10.0.0.0/24") # normal forwarding graph fwd_graph = nx.DiGraph() fwd_graph.add_edge("r1", "sink") fwd_graph.add_edge("r2", "sink") fwd_graph.add_edge("r3", "r1") fwd_graph.add_edge("r4", "r1") fwd_graph.add_edge("r4", "r2") fwd_graph.add_edge("r5", "r2") fwd_graph.add_edge("r6", "r2") fwd_graph.add_edge("r6", "r5") # according to the definition in https://en.wikipedia.org/wiki/Dominator_(graph_theory) dom_graph = nx.DiGraph() dom_graph.add_edge("r1", "sink") dom_graph.add_edge("r2", "sink") dom_graph.add_edge("r3", "r1") dom_graph.add_edge("r4", "sink") dom_graph.add_edge("r5", "r2") dom_graph.add_edge("r6", "r2") fwd_graphs = {subnet: fwd_graph} dom_graphs = {subnet: dom_graph} # dummy network nodes = ["r1", "r2", "r3", "r4", "r5", "r6", ] dst_router1 = "r1" dst_router2 = "r2" dst_interface = Interface("FastEthernet0/0") dst_interface.set_ip_address(next(subnet.hosts()), subnet) interfaces = {subnet: [(dst_router1, dst_interface), (dst_router2, dst_interface), ]} network = DummyNetwork(nodes, interfaces) policy_destination1 = PolicyDestination(dst_router1, dst_interface, subnet) policy_destination2 = PolicyDestination(dst_router2, dst_interface, subnet) reachability_policies = [ (PolicyType.Reachability, policy_destination1, 0, PolicySource("r3")), (PolicyType.Reachability, policy_destination1, 0, PolicySource("r4")), (PolicyType.Reachability, policy_destination2, 0, PolicySource("r4")), (PolicyType.Reachability, policy_destination2, 0, PolicySource("r5")), (PolicyType.Reachability, policy_destination2, 0, PolicySource("r6")), ] if local: reachability_policies.append((PolicyType.Reachability, policy_destination1, 0, PolicySource("r1"))) reachability_policies.append((PolicyType.Reachability, policy_destination2, 0, PolicySource("r2"))) return network, fwd_graphs, dom_graphs, reachability_policies
def test_get_waypoint_policies(self): network, fwd_graphs, dom_graphs, policy_destination, reachability_policies = self.get_disconnected_network() correct_policies = [ (PolicyType.Waypoint, policy_destination, "r2", PolicySource("r2")), (PolicyType.Waypoint, policy_destination, "r2", PolicySource("r3")), ] test_policies = list() pg = PolicyGuesser(network, waypoints=["r2", "r6"]) pg.get_waypoint_policies(test_policies, fwd_graphs, dom_graphs, node_local_reachability=False) self.assertCountEqual(test_policies, correct_policies)
def test_get_waypoint_policies_simple(self): network, fwd_graphs, dom_graphs, policy_destination, _ = self.get_simple_network() correct_policies = [ (PolicyType.Reachability, policy_destination, 0, PolicySource("r2")), (PolicyType.Reachability, policy_destination, 0, PolicySource("r3")), (PolicyType.Reachability, policy_destination, 0, PolicySource("r4")), (PolicyType.Reachability, policy_destination, 0, PolicySource("r5")), (PolicyType.Reachability, policy_destination, 0, PolicySource("r6")), ] test_policies = list() pg = PolicyGuesser(network) pg.get_reachability_policies(test_policies, fwd_graphs, dom_graphs, node_local_reachability=False) self.assertCountEqual(test_policies, correct_policies)
def get_query(sources): sources = [PolicySource(src_router) for src_router in sources] destinations = PolicyDestination("r12", "FastEthernet1/1", "11.0.12.0/24") num_paths = 3 links = [Link("l{id}".format(id=i), edge, LinkState.UP) for i, edge in enumerate(ResponseTest.get_all_edges())] netenv = NetworkEnvironment(links) return Query(PolicyType.LoadBalancingSimple, sources, destinations, num_paths, netenv, negate=False)
def test_get_loadbalancing_policies_disconnected(self): network, fwd_graphs, dom_graphs, policy_destination, _ = self.get_disconnected_network() correct_policies = [ (PolicyType.LoadBalancingSimple, policy_destination, 2, PolicySource("r4")), ] test_policies = list() pg = PolicyGuesser(network) pg.get_loadbalancing_policies(test_policies, fwd_graphs, dom_graphs, simple=True, node_local_reachability=False) self.assertCountEqual(test_policies, correct_policies)
# restart backend ms_manager.restart(force=True) policy_db = get_policy_db(network, waypoints=waypoints, debug=debug) # add all the policies start_time = time.time() destinations = list() for subnet, interfaces in network.subnets.items(): if len(interfaces) == 1: dst_router, dst_interface = interfaces[0] destinations.append(PolicyDestination(dst_router, dst_interface, subnet)) sources = list() for router in network.nodes(): sources.append(PolicySource(router)) policies = list() for source in sources: for destination in destinations: policies.append((PolicyType.Reachability, destination, 0, source)) policies.append((PolicyType.Isolation, destination, 0, source)) for waypoint in waypoints: policies.append((PolicyType.Waypoint, destination, waypoint, source)) policies.append((PolicyType.LoadBalancingSimple, destination, 2, source)) policy_db.update_policies2(policies, 99) policy_time = time.time() - start_time verifier_baseline = VerificationOnly(policy_db, netenv, ms_manager)
def get_loadbalancing_policies(self, policies, forwarding_graphs, dominator_graphs, node_local_reachability=False, simple=True, edge_disjoint=False, node_disjoint=False): start_time = time.time() for subnet, graph in dominator_graphs.items(): all_destinations, destinations, dst_routers = self.get_destination_interfaces_for_subnet( subnet, forwarding_graphs[subnet]) # TODO how to deal with Loadbalancing to same prefix, but last hop is different router?! # only use router specific subnets (e.g., subnets that are connected at a single interface, such as loopback # and host subnets). This removes all subnet that just exist between two routers (usually /31) if len(all_destinations) > 1: continue for node in graph.nodes(): # prevent policies within the same router if node == "sink" or (not node_local_reachability and node in dst_routers): continue if len(destinations[node]) == 1: policy_destination = destinations[node][0] dst_router = policy_destination.router else: self.logger.error( "There should only be a single destination for {node} this subnet: {subnet}." .format(node=node, subnet=subnet)) continue policy_source = PolicySource(node) all_paths = list( nx.all_simple_paths(forwarding_graphs[subnet], node, "sink")) # only continue if there is more than one path available if len(all_paths) > 1: # simple loadbalancing, more than a single path if simple: policies.append((PolicyType.LoadBalancingSimple, policy_destination, len(all_paths), policy_source)) # edge disjoint loadbalancing if edge_disjoint: edge_disjoint_paths = list() edge_union = set() for path in all_paths: edges = set([ Link.get_name(path[i], path[i + 1]) for i in range(len(path) - 1) ]) if not edge_union.intersection(edges): edge_union.union(edges) edge_disjoint_paths.append(path) if len(edge_disjoint_paths) > 1: self.logger.debug( "There are {num_paths} edge-disjoint paths from {src} to {dst}." .format(num_paths=len(edge_disjoint_paths), src=node, dst=dst_router)) # TODO add policies # node disjoint loadbalancing if node_disjoint: node_disjoint_paths = list() node_union = set() for path in all_paths: # need to remove the source and destination as all the paths will have them in common nodes = set(path[1:-1]) if not node_union.intersection(nodes): node_union.union(nodes) node_disjoint_paths.append(path) if len(node_disjoint_paths) > 1: self.logger.debug( "There are {num_paths} node-disjoint paths from {src} to {dst}." .format(num_paths=len(node_disjoint_paths), src=node, dst=dst_router)) # TODO add policies self.logger.debug( "Getting the loadbalancing policies from the forwarding graphs took {time:.4f}s." .format(time=time.time() - start_time, )) return policies