def gen_grid(self, n, m, nets): grid = gen_grid_topology(n, m, nets) for node in grid.local_routers_iter(): grid.enable_ospf(node, 100) conn_syn = ConnectedSyn([], grid, full=True) conn_syn.synthesize() return grid
def get_g(): """ Get a simple graph of 4 mesh connected graph :return: NetworkGraph """ # Start with some initial inputs # This input only define routers, interfaces, and networks g_phy = NetworkGraph() g_phy.add_router('R1') g_phy.add_router('R2') g_phy.add_router('R3') g_phy.add_router('R4') g_phy.enable_ospf('R1', 100) g_phy.enable_ospf('R2', 100) g_phy.enable_ospf('R3', 100) g_phy.enable_ospf('R4', 100) g_phy.add_router_edge('R1', 'R2') g_phy.add_router_edge('R1', 'R3') g_phy.add_router_edge('R1', 'R4') g_phy.add_router_edge('R2', 'R1') g_phy.add_router_edge('R2', 'R3') g_phy.add_router_edge('R2', 'R4') g_phy.add_router_edge('R3', 'R1') g_phy.add_router_edge('R3', 'R2') g_phy.add_router_edge('R3', 'R4') g_phy.add_router_edge('R4', 'R1') g_phy.add_router_edge('R4', 'R2') g_phy.add_router_edge('R4', 'R3') conn_syn = ConnectedSyn([], g_phy, full=True) conn_syn.synthesize() return g_phy
def get_triangles(self, fanout): topo = get_fanout_topology(fanout) for node in topo.local_routers_iter(): topo.enable_ospf(node, 100) conn_syn = ConnectedSyn([], topo, full=True) conn_syn.synthesize() return topo
def create_context(reqs, g, announcements, create_as_paths=False): connected = ConnectedSyn([], g, full=True) connected.synthesize() next_hops_map = compute_next_hop_map(g) next_hops = extract_all_next_hops(next_hops_map) peers = [node for node in g.routers_iter() if g.is_bgp_enabled(node)] ctx = SolverContext.create_context(announcements, peer_list=peers, next_hop_list=next_hops, create_as_paths=create_as_paths) return ctx
def test_one_extra(self): g = self.get_two_nodes() g.add_router('R3') g.add_router_edge('R1', 'R3') g.add_router_edge('R2', 'R3') g.add_router_edge('R3', 'R1') g.add_router_edge('R3', 'R2') reqs = [PathReq(Protocols.BGP, 'Prefix', ['R1', 'R2'], False)] # Set Iface for R1 to R3 iface1 = 'Fa0/0' addr1 = ip_interface(u"192.168.0.1/24") g.add_iface('R1', iface1, is_shutdown=False) g.set_iface_addr('R1', iface1, addr1) g.set_edge_iface('R1', 'R3', iface1) g.set_iface_description('R1', iface1, '' "To {}" ''.format('R3')) # Set Iface for R3 to R1 iface2 = 'Fa0/0' addr2 = ip_interface(u"192.168.0.2/24") g.add_iface('R3', iface2, is_shutdown=False) g.set_iface_addr('R3', iface2, addr2) g.set_edge_iface('R3', 'R1', iface2) g.set_iface_description('R3', iface2, '' "To {}" ''.format('R1')) # Set Iface for R2 to R3 iface3 = 'Fa0/0' addr3 = ip_interface(u"192.168.1.1/24") g.add_iface('R2', iface3, is_shutdown=True) g.set_iface_addr('R2', iface3, addr3) g.set_edge_iface('R2', 'R3', iface3) g.set_iface_description('R2', iface3, '' "To {}" ''.format('R3')) # Set Iface for R3 to R2 iface4 = 'Fa0/1' addr4 = ip_interface(u"192.168.2.2/24") g.add_iface('R3', iface4, is_shutdown=True) g.set_iface_addr('R3', iface4, addr4) g.set_edge_iface('R3', 'R2', iface4) g.set_iface_description('R3', iface4, '' "To {}" ''.format('R1')) p = ConnectedSyn(reqs, g) p.synthesize() self.assertTrue(g.has_edge('R1', 'R2')) self.assertTrue(g.has_edge('R1', 'R3')) self.assertTrue(g.has_edge('R2', 'R1')) self.assertFalse(g.has_edge('R2', 'R3')) self.assertTrue(g.has_edge('R3', 'R1')) self.assertFalse(g.has_edge('R3', 'R2'))
def static(n, nreqs=10): req_file = './topos/cav/gridrand%d-static-%d-req.logic' % (n, nreqs) topo = gen_grid_topology(n, n, 0) static_reqs, ospf_reqs, bgp_reqs = read_reqs(req_file) for node in topo.nodes(): topo.enable_ospf(node, 100) # Initially all costs are empty topo.set_static_routes_empty(node) for src, dst in topo.edges(): topo.set_edge_ospf_cost(src, dst, VALUENOTSET) conn = ConnectedSyn([], topo, full=True) conn.synthesize() static_syn = StaticSyn(static_reqs, topo) static_syn.synthesize()
def create_context(self, reqs, g): connected = ConnectedSyn(reqs, g, full=True) connected.synthesize() next_hops_map = compute_next_hop_map(g) next_hops = extract_all_next_hops(next_hops_map) peers = [node for node in g.routers_iter() if g.is_bgp_enabled(node)] anns = self.get_anns() for ann in anns: g.add_bgp_advertise(node=ann.peer, announcement=ann, loopback='lo0') ctx = SolverContext.create_context(anns, next_hop_list=next_hops, peer_list=peers, create_as_paths=False) return ctx
def __init__(self, reqs, topo, external_announcements, netcompplete_config=None): self.log = logging.getLogger( '%s.%s' % (self.__module__, self.__class__.__name__)) assert not reqs or isinstance(reqs, Iterable) assert isinstance(topo, NetworkGraph) assert not external_announcements or isinstance( external_announcements, Iterable) if not netcompplete_config: netcompplete_config = NetCompleteConfigs() assert isinstance(netcompplete_config, NetCompleteConfigs) self.reqs = reqs self.topo = topo self.connected_syn = ConnectedSyn([], self.topo, full=True) self.external_announcements = external_announcements self.configs = netcompplete_config self._bgp_ctx = None self._bgp_synthesizer = None self._bgp_solver = None
def test_one_side_concrete(self): g = self.get_two_nodes() reqs = [PathReq(Protocols.BGP, 'Prefix', ['R1', 'R2'], False)] addr1 = ip_interface(u"192.168.0.1/24") # Set Iface for R1 to R2 iface = 'Fa0/0' g.add_iface('R1', iface, is_shutdown=False) g.set_iface_addr('R1', iface, addr1) g.set_edge_iface('R1', 'R2', iface) g.set_iface_description('R1', iface, '' "To {}" ''.format('R2')) # Set Iface for R2 to R1 iface = 'Fa0/0' g.add_iface('R2', iface, is_shutdown=False) g.set_iface_addr('R2', iface, VALUENOTSET) g.set_edge_iface('R2', 'R1', iface) g.set_iface_description('R2', iface, '' "To {}" ''.format('R1')) p = ConnectedSyn(reqs, g) p.synthesize() self.assertNotEqual(g.get_iface_addr('R2', iface), VALUENOTSET)
def test_grid_one_peer(self): anns = self.get_announcements(1, 1) g = gen_mesh(4, 100) self.get_add_one_peer(g, ['R2'], anns.values()) ann = anns.values()[0] reqs = [ PathReq(Protocols.BGP, ann.prefix, ['ATT', 'R2'], False), PathReq(Protocols.BGP, ann.prefix, ['ATT', 'R2', 'R1'], False), PathReq(Protocols.BGP, ann.prefix, ['ATT', 'R2', 'R3'], False), PathReq(Protocols.BGP, ann.prefix, ['ATT', 'R2', 'R4'], False), ] # Set Iface for R2 to ATT iface = 'Fa0/0' g.add_iface('R2', iface, is_shutdown=False) g.set_iface_addr('R2', iface, VALUENOTSET) g.set_edge_iface('R2', 'ATT', iface) g.set_iface_description('R2', iface, '' "To {}" ''.format('ATT')) p = ConnectedSyn(reqs, g) p.synthesize()
def ospf(n, nreqs=10): req_file = './topos/cav/gridrand%d-ospf-%d-req.logic' % (n, nreqs) topo = gen_grid_topology(n, n, 0) static_reqs, ospf_reqs, bgp_reqs = read_reqs(req_file) seed = 159734782 path_gen = 200 ospfRand = random.Random(seed) for node in topo.nodes(): topo.enable_ospf(node, 100) # Initially all costs are empty topo.set_static_routes_empty(node) for src, dst in topo.edges(): topo.set_edge_ospf_cost(src, dst, VALUENOTSET) conn = ConnectedSyn([], topo, full=True) conn.synthesize() static_syn = StaticSyn(static_reqs, topo) static_syn.synthesize() ospf = OSPFCEGIS(topo, gen_paths=path_gen, random_obj=ospfRand) for req in ospf_reqs: ospf.add_req(req) assert ospf.synthesize(allow_ecmp=True) assert not ospf.removed_reqs
def test_wrong_subnets(self): g = self.get_two_nodes() reqs = [PathReq(Protocols.BGP, 'Prefix', ['R1', 'R2'], False)] addr1 = ip_interface(u"192.168.0.1/24") addr2 = ip_interface(u"192.168.0.2/25") # Set Iface for R1 to R2 iface = 'Fa0/0' g.add_iface('R1', iface, is_shutdown=False) g.set_iface_addr('R1', iface, addr1) g.set_edge_iface('R1', 'R2', iface) g.set_iface_description('R1', iface, '' "To {}" ''.format('R2')) # Set Iface for R2 to R1 iface = 'Fa0/0' g.add_iface('R2', iface, is_shutdown=False) g.set_iface_addr('R2', iface, addr2) g.set_edge_iface('R2', 'R1', iface) g.set_iface_description('R2', iface, '' "To {}" ''.format('R1')) p = ConnectedSyn(reqs, g) with self.assertRaises(NotValidSubnetsError): p.synthesize()
def bgp(n, nreqs=10): req_file = './topos/cav/gridrand%d-bgp-%d-req.logic' % (n, nreqs) topo = gen_grid_topology(n, n, 0) static_reqs, ospf_reqs, bgp_reqs = read_reqs(req_file) seed = 159734782 path_gen = 200 ospfRand = random.Random(seed) for node in topo.routers_iter(): topo.enable_ospf(node, 100) topo.set_bgp_asnum(node, 100) # Initially all costs are empty topo.set_static_routes_empty(node) for src, dst in topo.edges(): topo.set_edge_ospf_cost(src, dst, VALUENOTSET) peer = 'ATT' egresses = set([p.path[-2] for p in bgp_reqs]) topo.add_peer(peer) topo.set_bgp_asnum(peer, 5000) for req in bgp_reqs: req.path.append(peer) for egress in egresses: topo.add_peer_edge(peer, egress) topo.add_peer_edge(egress, peer) topo.add_bgp_neighbor(peer, egress, VALUENOTSET, VALUENOTSET) for src in topo.local_routers_iter(): for dst in topo.local_routers_iter(): if src == dst or dst in topo.get_bgp_neighbors(src): continue topo.add_bgp_neighbor(src, dst, VALUENOTSET, VALUENOTSET) prefix = 'GOOGLE' communities = [Community("100:%d" % i) for i in range(5)] ann = Announcement(prefix=prefix, peer=peer, origin=BGP_ATTRS_ORIGIN.EBGP, as_path=[1, 2, 5000], as_path_len=3, next_hop='%sHop' % peer, local_pref=100, med=10, communities=dict([(c, False) for c in communities]), permitted=True) topo.add_bgp_advertise(peer, ann) conn = ConnectedSyn([], topo, full=True) conn.synthesize() static_syn = StaticSyn(static_reqs, topo) static_syn.synthesize() ospf = OSPFCEGIS(topo, gen_paths=path_gen, random_obj=ospfRand) for req in ospf_reqs: ospf.add_req(req) assert ospf.synthesize(allow_ecmp=True) assert not ospf.removed_reqs for router in topo.local_routers_iter(): count = itertools.count(1) for neighbor in topo.get_bgp_neighbors(router): if router == neighbor: continue comm_list = CommunityList( list_id=count.next(), access=Access.permit, communities=[VALUENOTSET, VALUENOTSET, VALUENOTSET]) topo.add_bgp_community_list(router, comm_list) match_comm = MatchCommunitiesList(comm_list) iplist = IpPrefixList(name='ip%s' % count.next(), access=Access.permit, networks=[VALUENOTSET]) topo.add_ip_prefix_list(router, iplist) match_ip = MatchIpPrefixListList(iplist) match_next_hop = MatchNextHop(VALUENOTSET) match_sel = MatchSelectOne([match_comm, match_next_hop, match_ip]) actions = [ ActionSetLocalPref(VALUENOTSET), ActionSetCommunity([VALUENOTSET], True) ] rline = RouteMapLine([match_sel], actions, VALUENOTSET, 10) dline = RouteMapLine(None, None, Access.deny, 100) rmap = RouteMap("Rimp_%s_from_%s" % (router, neighbor), lines=[rline, dline]) topo.add_route_map(router, rmap) topo.add_bgp_import_route_map(router, neighbor, rmap.name) ctx = create_context(bgp_reqs, topo, [ann]) p = EBGPPropagation(bgp_reqs, topo, ctx) p.compute_dags() p.synthesize() solver = z3.Solver() ret = ctx.check(solver) assert ret == z3.sat, solver.unsat_core() p.update_network_graph()
def two_ebgp_nodes(export_path): """ Two routers connected via eBGP Very simple once router announces a single prefix and the other selects it """ graph = NetworkGraph() r1, r2 = 'R1', 'R2' graph.add_router(r1) graph.add_router(r2) graph.add_router_edge(r1, r2) graph.add_router_edge(r2, r1) # BGP configs graph.set_bgp_asnum(r1, 100) graph.set_bgp_asnum(r2, 200) # Establish peering # The actual network interfaces used for peering will be synthesized graph.add_bgp_neighbor(r1, r2, router_a_iface=VALUENOTSET, router_b_iface=VALUENOTSET) # Some internal network net = ip_network(u'128.0.0.0/24') prefix = '128_0_0_0' prefix_map = {prefix: net} lo0 = 'lo0' graph.set_loopback_addr( r1, lo0, ip_interface("%s/%d" % (net.hosts().next(), net.prefixlen))) # Announce the internal network graph.add_bgp_announces(r1, lo0) # The communities recognized by us comms = [Community("100:10"), Community("100:20")] # The announcement that will be propagated by R1 ann = Announcement(prefix=prefix, peer=r1, origin=BGP_ATTRS_ORIGIN.EBGP, next_hop='R1Hop', as_path=[100], as_path_len=1, local_pref=100, med=100, communities=dict([(c, False) for c in comms]), permitted=True) path = PathReq(Protocols.BGP, prefix, ['R2', 'R1'], False) reqs = [path] # Get SMT Context ctx = create_context(reqs, graph, [ann]) propagation = EBGPPropagation(reqs, graph, ctx) propagation.compute_dags() propagation.synthesize() # Synthesize all the interfaces and link configurations connecte_syn = ConnectedSyn([], graph, full=True) connecte_syn.synthesize() # SMT Solving solver = z3.Solver(ctx=ctx.z3_ctx) assert ctx.check(solver) == z3.sat, solver.unsat_core() # Update graph with the concrete values after solver propagation.update_network_graph() gns3 = GNS3Topo(graph=graph, prefix_map=prefix_map) gns3.write_configs('%s/ibgp-simple' % export_path)
def main(): setup_logging() parser = argparse.ArgumentParser(description='Process some integers.') parser.add_argument('-f', type=str, default='', help='read topology zoo graphml file') parser.add_argument('-s', type=int, default=5, help='grid size') parser.add_argument('-r', type=int, default=20, help='number of generated random paths as reqs') parser.add_argument( '-p', type=int, default=1000, help='number of generated random paths for each round of synthesis') parser.add_argument('-u', type=int, default=0, help='number of unsatisfiable requirements,' 'it is added to the total number of requirements') parser.add_argument('--seed', type=int, default=0, help='The seed of the random generator') parser.add_argument( '--fixed', type=float, default=0, help='The percentage of fixed edge costs of the total edges (0 to 1)') # Parse command line args args = parser.parse_args() gsize = args.s reqsize = args.r pathsize = args.p seed = args.seed unsatisfiable_reqs = args.u topology_file = args.f fixed = args.fixed # Generate new random number seed if need if not seed: seed = random.randint(0, sys.maxint) print "Generated new seed", seed # This random generator MUST be used everywhere!!!! ospfRand = random.Random(seed) # If zoo topology file is specified, then read it # Otherwise generate a grid topo if topology_file: g = read_topology_zoo_netgraph(topology_file) results_name = os.path.basename(topology_file)[:-len('.graphml')] else: g = gen_grid_topology(gsize, gsize, 0) results_name = "grid%x%s" % (gsize, gsize) # Enable OSPF on all local routers for node in g.local_routers_iter(): g.enable_ospf(node, 100) # Initially assume all costs are empty for src, dst in g.edges(): if not g.is_ospf_enabled(src): continue if not g.is_ospf_enabled(dst): continue g.set_edge_ospf_cost(src, dst, VALUENOTSET) conn_syn = ConnectedSyn([], g, full=True) conn_syn.synthesize() if not topology_file: print "Grid size %dx%d" % (gsize, gsize) else: print "Topology file:", topology_file print "Number of nodes:", len(list(g.nodes())) print "Number of edges:", len(list(g.edges())) print "Percentage of fixed edge costs:", fixed print "Number of requirements: %d" % reqsize print "Number of paths per iteration %d" % pathsize print "Random Seed", seed paths = [] tmp_weight_name = 'tmp-weight' print "Generating random paths for requirements" for i in range(0, reqsize): src, dst = ospfRand.sample(list(g.nodes()), 2) assert src != dst path = random_requirement_path(g, src, dst, ospfRand, tmp_weight_name) paths.append(path) print "Done generating random paths for requirements" if fixed > 0: weights = [] for src, dst in g.edges(): weights.append((src, dst, g[src][dst][tmp_weight_name])) population = int(round(len(weights) * fixed)) sampled = ospfRand.sample(weights, population) for src, dst, w in sampled: g.set_edge_ospf_cost(src, dst, w) cl = nx.DiGraph() for n in g.nodes(): cl.add_node(n) for s, d in g.edges(): cl.add_edge(s, d) if unsatisfiable_reqs: print "Generating counter paths" chosen = [] for i in range(unsatisfiable_reqs): candidate = ospfRand.choice(paths) counter_path = None while counter_path is None: while candidate in chosen: candidate = ospfRand.choice(paths) counter_path = generate_second_path(g, candidate) chosen.append(candidate) print "Generating counter path for path", candidate paths.append(counter_path) unsatisfiable_reqs = len(chosen) ospf = OSPFSyn(g, gen_paths=pathsize) for path in paths: req = PathReq(Protocols.OSPF, path[-1], path, False) ospf.add_req(req) ospf.synthesize(retries_before_rest=10) ospf.update_network_graph() print "OSPF Edge cost" for src, dst in g.edges(): print src, dst, g.get_edge_ospf_cost(src, dst)
def two_ebgp_nodes_route_map(export_path): """ Two routers connected via eBGP with route maps Very simple one router announces a single prefix and the other selects it """ graph = NetworkGraph() r1, r2 = 'R1', 'R2' graph.add_router(r1) graph.add_router(r2) graph.add_router_edge(r1, r2) graph.add_router_edge(r2, r1) # BGP configs graph.set_bgp_asnum(r1, 100) graph.set_bgp_asnum(r2, 200) # Establish peering # The actual network interfaces used for peering will be synthesized graph.add_bgp_neighbor(r1, r2, router_a_iface=VALUENOTSET, router_b_iface=VALUENOTSET) # Some internal network net = ip_network(u'128.0.0.0/24') prefix = '128_0_0_0' prefix_map = {prefix: net} lo0 = 'lo0' graph.set_loopback_addr( r1, lo0, ip_interface("%s/%d" % (net.hosts().next(), net.prefixlen))) # Announce the internal network graph.add_bgp_announces(r1, lo0) # The communities recognized by us comms = [Community("100:10"), Community("100:20")] # The announcement that will be propagated by R1 ann = Announcement(prefix=prefix, peer=r1, origin=BGP_ATTRS_ORIGIN.EBGP, next_hop='R1Hop', as_path=[100], as_path_len=1, local_pref=100, med=100, communities=dict([(c, False) for c in comms]), permitted=True) path = PathReq(Protocols.BGP, prefix, ['R2', 'R1'], False) reqs = [path] # Create a route map to export from R1 to R2 iplist = IpPrefixList(name='IpList1', access=Access.permit, networks=[prefix]) graph.add_ip_prefix_list(r1, iplist) ip_match = MatchIpPrefixListList(iplist) set_community = ActionSetCommunity([comms[0]]) rline = RouteMapLine(matches=[ip_match], actions=[set_community], access=Access.permit, lineno=10) export_map = RouteMap(name="Export_R1_to_R2", lines=[rline]) # Register the route map graph.add_route_map(r1, export_map) # Set the route map as an export route map graph.add_bgp_export_route_map(r1, r2, export_map.name) # Create a route map to import at R2 to from R1 comm_list = CommunityList(list_id=1, access=Access.permit, communities=[comms[0]]) graph.add_bgp_community_list(r2, comm_list) comm_match = MatchCommunitiesList(comm_list) set_local_pref = ActionSetLocalPref(200) rline = RouteMapLine(matches=[MatchNextHop(VALUENOTSET)], actions=[set_local_pref], access=Access.permit, lineno=10) import_map = RouteMap(name="Import_R2_from_R1", lines=[rline]) # Register the route map graph.add_route_map(r2, import_map) # Set the route map as an import route map graph.add_bgp_import_route_map(r2, r1, import_map.name) # Get SMT Context ctx = create_context(reqs, graph, [ann]) propagation = EBGPPropagation(reqs, graph, ctx) propagation.compute_dags() propagation.synthesize() # Synthesize all the interfaces and link configurations connecte_syn = ConnectedSyn([], graph, full=True) connecte_syn.synthesize() # SMT Solving solver = z3.Solver(ctx=ctx.z3_ctx) assert ctx.check(solver) == z3.sat, solver.unsat_core() # Update graph with the concrete values after solver propagation.update_network_graph() gns3 = GNS3Topo(graph=graph, prefix_map=prefix_map) gns3.write_configs('%s/ebgp-route-map' % export_path) graph.write_graphml('%s/ebgp-route-map/topology.graphml' % export_path)
def main(): setup_logging() parser = argparse.ArgumentParser(description='Run OSPF experiment.') parser.add_argument('--topo', required=True, type=str, help='read topology zoo graphml file') parser.add_argument('--values', required=True, type=str, help='python file with reqs vals') parser.add_argument('--type', required=True, type=str, choices=['simple', 'ecmp', 'kconnected', 'order'], help='simple, ecmp, kconnected, ordered') parser.add_argument('--reqsize', type=int, required=True, help='Number of reqs to be used') parser.add_argument('--syn', required=True, type=str, choices=['cegis', 'concrete'], help='simple, ecmp, kconnected, ordered') parser.add_argument( '-k', type=int, default=2, help='Number of paths used per requirement (ecmp, ordered, etc..)') parser.add_argument( '-p', type=int, default=100, help='number of generated random paths for each round of synthesis') parser.add_argument('--seed', type=int, default=0, help='The seed of the random generator') parser.add_argument( '--fixed', type=float, default=0, help='The percentage of fixed edge costs of the total edges (0 to 1)') # Parse command line args args = parser.parse_args() topo_file = args.topo reqs_file = args.values req_type = args.type reqsize = args.reqsize k = args.k path_gen = args.p seed = args.seed fixed = args.fixed syn = args.syn print "Syntype", syn assert 0 <= fixed <= 1.0 # Generate new random number seed if need if not seed: seed = random.randint(0, sys.maxint) print "Generated new seed", seed # This random generator MUST be used everywhere!!!! ospfRand = random.Random(seed) topo = read_topology_zoo_netgraph(topo_file) with open(reqs_file, 'r') as file: exec(file.read()) if req_type == 'simple': reqs = eval('reqs_simple_%d' % reqsize) vals = eval('edges_cost_simple_%d' % reqsize) elif req_type == 'ecmp': reqs = eval('reqs_ecmp_%d_%d' % (reqsize, k)) vals = eval('edges_cost_ecmp_%d_%d' % (reqsize, k)) elif req_type == 'kconnected': reqs = eval('reqs_kconnected_%d_%d' % (reqsize, k)) vals = eval('edges_cost_kconnected_%d_%d' % (reqsize, k)) elif req_type == 'order': reqs = eval('reqs_order_%d_%d' % (reqsize, k)) vals = eval('edges_cost_order_%d_%d' % (reqsize, k)) else: raise ValueError("Unknow req type %s", req_type) for node in topo.nodes(): topo.enable_ospf(node, 100) # Initially all costs are empty for src, dst in topo.edges(): topo.set_edge_ospf_cost(src, dst, VALUENOTSET) # how many is fixed fixed_edges = ospfRand.sample(vals, int(round(len(vals) * fixed))) for src, dst, cost in fixed_edges: #print "Fixing", src, dst, cost topo.set_edge_ospf_cost(src, dst, cost) # Establish basic connectivity conn = ConnectedSyn([], topo, full=True) conn.synthesize() t1 = timer() if syn == 'cegis': print "Syn CEGIS" ospf = OSPFCEGIS(topo, gen_paths=path_gen, random_obj=ospfRand) for req in reqs: ospf.add_req(req) assert ospf.synthesize() assert not ospf.removed_reqs elif syn == "concrete": print "Syn Concrete" ospf = OSPFConcrete(topo) for req in reqs: ospf.add_req(req) assert ospf.solve() else: raise ValueError("Unknow syn type %s" % syn) t2 = timer() print "TOTAL SYN TIME:", t2 - t1 if fixed == 1.0: t1 = timer() print "Updating network graph, to assert full values" ospf.update_network_graph() for src, dst, cost in vals: new_cost = topo.get_edge_ospf_cost(src, dst) assert cost == new_cost, "Diff (%s, %s) old=%s new=%s" % ( src, dst, cost, new_cost) t2 = timer() print "Update Network graph TIME:", t2 - t1 from tekton.gns3 import GNS3Topo ospf.update_network_graph() gns3 = GNS3Topo(topo) basename = os.path.basename(topo_file).strip('.graphml') out_name = "%s_%s_%s_%s" % (basename, fixed, req_type, reqsize) out_dir = 'out-configs/%s_%d' % (out_name, ospfRand.randint(0, 1000)) print "Writing configs to:", out_dir gns3.write_configs(out_dir)
def main(): parser = argparse.ArgumentParser( description='Generate OSPF Requiremetns for a given topology.') parser.add_argument('-f', type=str, required=True, default=None, help='read topology zoo graphml file') parser.add_argument('--seed', type=int, default=0, help='The seed of the random generator') args = parser.parse_args() topology_file = args.f seed = args.seed assert topology_file print "Y" * 50 print "Generating reqs for:", topology_file print "Y" * 50 if not seed: seed = random.randint(0, 2**32 - 1) print "Generated new seed", seed print "Using SEED:", seed out_file = """ from synet.utils.common import PathReq from synet.utils.common import ECMPPathsReq from synet.utils.common import PathOrderReq from synet.utils.common import Protocols from synet.utils.common import KConnectedPathsReq\n """ out_file += "topology_file = '%s'\n" % topology_file out_file += "seed = %d\n" % seed rand = random.Random(seed) numpy.random.seed(seed) topo = read_topology_zoo_netgraph(topology_file) # Synthesize Connected conn_syn = ConnectedSyn([], topo, full=True) conn_syn.synthesize() # All routers are OSPF Enabled for node in topo.local_routers_iter(): topo.enable_ospf(node, 100) simple_reqs = [] simple_vals = [] ecmp_reqs = [] ecmp_vals = [] kconnected_reqs = [] kconnected_vals = [] order_reqs = [] order_vals = [] for reqsize in [1, 2, 4, 8, 16]: s_out, s_reqs, s_req_name, s_vals_name = get_simple_reqs( topo, reqsize, rand) out_file += s_out simple_reqs.append(s_req_name) simple_vals.append(s_vals_name) for ecmp in [2]: e_out, e_reqs, e_req_name, e_vals_name = get_ecmp_reqs( topo, reqsize, ecmp, rand) out_file += e_out ecmp_reqs.append(e_req_name) ecmp_vals.append(e_vals_name) # We can use the ECMP to generate kconnected print "ECMP reqs", e_reqs k_out, k_reqs, k_req_name, k_vals_name = get_kconnected( topo, e_reqs, reqsize, ecmp, rand) out_file += k_out print "APPENDING", k_req_name kconnected_reqs.append(k_req_name) kconnected_vals.append(k_vals_name) for pathorder in [2]: o_out, o_reqs, o_req_name, o_vals_name = get_path_order( topo, reqsize, pathorder, rand) out_file += o_out order_reqs.append(o_req_name) order_vals.append(o_vals_name) out_file += "#" * 20 out_file += "\n\n" out_file += "reqs_simple = [%s]\n\n" % ",".join(simple_reqs) out_file += "reqs_simple_vals = [%s]\n\n" % ",".join(simple_vals) out_file += "#" * 20 out_file += "\n\n" out_file += "reqs_ecmp = [%s]\n\n" % ",".join(ecmp_reqs) out_file += "reqs_ecmp_vals = [%s]\n\n" % ",".join(ecmp_vals) out_file += "#" * 20 out_file += "\n\n" out_file += "reqs_kconnected = [%s]\n\n" % ",".join(kconnected_reqs) out_file += "reqs_kconnected_vals = [%s]\n\n" % ",".join(kconnected_vals) out_file += "#" * 20 out_file += "\n\n" out_file += "reqs_order = [%s]\n\n" % ", ".join(order_reqs) out_file += "reqs_order_vals = [%s]\n\n" % ", ".join(order_vals) out_file += "#" * 20 out_file += "\n\n" dirname = os.path.dirname(topology_file) topo_name = os.path.basename(topology_file).split('.graphml')[0] out_name = "%s_ospf_reqs.py" % topo_name out_path = os.path.join(dirname, out_name) with open(out_path, 'w') as file: print "Writing to", out_path file.write(out_file)
class NetComplete(object): def __init__(self, reqs, topo, external_announcements, netcompplete_config=None): self.log = logging.getLogger( '%s.%s' % (self.__module__, self.__class__.__name__)) assert not reqs or isinstance(reqs, Iterable) assert isinstance(topo, NetworkGraph) assert not external_announcements or isinstance( external_announcements, Iterable) if not netcompplete_config: netcompplete_config = NetCompleteConfigs() assert isinstance(netcompplete_config, NetCompleteConfigs) self.reqs = reqs self.topo = topo self.connected_syn = ConnectedSyn([], self.topo, full=True) self.external_announcements = external_announcements self.configs = netcompplete_config self._bgp_ctx = None self._bgp_synthesizer = None self._bgp_solver = None @property def bgp_ctx(self): """The Announcement context used for the BGP solver""" return self._bgp_ctx @property def bgp_synthesizer(self): """The BGP synthesizer""" return self._bgp_synthesizer @property def bgp_solver(self): """The SMT Solver used for BGP""" return self._bgp_solver @property def bgp_reqs(self): """All BGP requirements""" return [req for req in self.reqs if req.protocol == Protocols.BGP] @property def ospf_reqs(self): """All OSPF requirements""" return [req for req in self.reqs if req.protocol == Protocols.OSPF] @property def static_reqs(self): """All Static requirements""" return [req for req in self.reqs if req.protocol == Protocols.Static] @property def announcements(self): return self.external_announcements def _create_context(self, create_as_paths=False): """ Create the context to hold symbolic variables used in BGP synthesis :param create_as_paths: :return: SolverContext """ next_hops_map = compute_next_hop_map(self.topo) next_hops = extract_all_next_hops(next_hops_map) peers = [ node for node in self.topo.routers_iter() if self.topo.is_bgp_enabled(node) ] print "PEER", peers print "NEXTHOPS", next_hops ctx = SolverContext.create_context(self.announcements, peer_list=peers, next_hop_list=next_hops, create_as_paths=create_as_paths) return ctx def synthesize_connected(self): if not self.connected_syn.synthesize(): msg = "Couldn't establish basic connectivity" raise UnImplementableRequirements(msg) return True def synthesize_bgp(self): self._bgp_ctx = self._create_context(create_as_paths=False) self._bgp_synthesizer = EBGPPropagation(self.bgp_reqs, self.topo, self._bgp_ctx) # Compute BGP Propagation unmatching_order = self.bgp_synthesizer.compute_dags() if unmatching_order: msg = "Unimplementable BGP requirements; " \ "the following BGP selection order cannot be met: " \ "{}".format(unmatching_order) raise UnImplementableRequirements(msg) self.bgp_synthesizer.synthesize() #SMT Solving self._bgp_solver = z3.Solver(ctx=self._bgp_ctx.z3_ctx) if self.bgp_ctx.check(self.bgp_solver, track=True, out_smt=self.configs.bgp_smt) != z3.sat: msg = "Unimplementable BGP requirements;" \ "Possibly change the requirements or loosen the sketch." \ "The following constraints couldn't be satisfied:" \ "{}".format(self.bgp_solver.unsat_core()) raise UnImplementableRequirements(msg) self.bgp_synthesizer.update_network_graph() return True def _check_ospf_path(self, req): """ Checks if the OSPF path synthesizable :return: """ not_enabled_nodes = [] errors = [] for node in req.path: if not self.topo.is_local_router(node): # OSPF is enabled only on local routers, not external peers msg = "Node '{}' is not configured to be a local router." \ "From OSPF requirement {}".format(node, req) errors.append(msg) elif not self.topo.is_ospf_enabled(node): if self.configs.auto_enable_ospf_process: # OSPF process is not enabled on the router # But NetComplete is allowed to enable it self.topo.enable_ospf(node, self.configs.default_ospf_process_id) else: # NetComplete is not allowed to enable it not_enabled_nodes.append(node) if not_enabled_nodes: msg = "Nodes are on OSPF path but OSPF process" \ " is not enabled on them: {}".format(not_enabled_nodes) errors.append(msg) if errors: return False, errors return True, None def _check_req(self, req): errors = [] for node in req.path: if not self.topo.has_node(node): msg = "Node doesn't exist in the network topology." \ "Node: {} from requirement {}".format(node, req) errors.append(msg) elif not self.topo.is_local_router(node): msg = "Node '{}' is not configured to be a local router." \ "From requirement {}".format(node, req) errors.append(msg) if req.protocol == Protocols.OSPF: check, msg = self._check_ospf_path(req) if not check: errors.append(check) elif req.protocol == Protocols.BGP: pass elif req.protocol == Protocols.Static: pass else: raise ValueError( "Unknown protocol value {} for requirement {}".format( req.protocol, req)) if not self._check_announce_prefix(req.path[-1], req.dst_net, req.protocol): errors.append("Node {} doesn't announce prefix {} " "for requirement {}".format(node, req.dst_net, req)) if errors: return False, errors return True, None def _check_announce_prefix(self, node, prefix, protocol): if protocol == Protocols.OSPF: if is_empty(prefix): self.log.warn("Using unknown prefix to node %s", node) return True if not self.topo.is_ospf_enabled(node): return False if prefix not in self.topo.get_ospf_networks(node): return False else: raise ValueError("Unknown protocol value {}".format(protocol)) return True def _check_reqs(self): results = [] for req in self.ospf_reqs: if isinstance(req, PathReq): results.append(self._check_req(req)) all_good = all([check for check, _ in results]) if all_good: return True, None else: return False, [msg for _, msg in results] def _check_ospf_announced(self, router, iface): """Return True if the address is announced over OSPF""" addr = self.topo.get_interface_loop_addr(router, iface) assert not is_empty(addr) routers = [(router, iface)] # Maybe the neighbor is announcing it for neighbor in self.topo.neighbors(router): if not self.topo.is_router(neighbor): continue if iface != self.topo.get_edge_iface(router, neighbor): continue routers.append( (neighbor, self.topo.get_edge_iface(neighbor, router))) for router, iface in routers: if not self.topo.is_ospf_enabled(router): continue for network in self.topo.get_ospf_networks(router): if not isinstance(network, (IPv4Network, IPv6Network)): if iface == network: return True elif addr in network: return True return False def _check_static_local(self, router, iface): return False def _check_next_hops(self): not_announced = [] for node, attrs in self.bgp_synthesizer.ibgp_propagation.nodes( data=True): for ann in attrs['box'].selected_sham: if not ann.permitted.get_value(): # Announcement has been dropped continue next_hop = ann.next_hop if not next_hop.is_concrete: continue next_hop = desanitize_smt_name(next_hop.get_value()) if next_hop == desanitize_smt_name( self.bgp_ctx.origin_next_hop): continue next_router, next_iface = next_hop.split("-")[0], '/'.join( next_hop.split("-")[1:]) path = [ k.path for k, v in attrs['box'].anns_map.iteritems() if v == ann ][0] pretty = "{}:{}".format(next_router, next_iface) print "XXXXX NEXT HOP at {} is {}, Path {}".format( node, pretty, path) if node == next_router: # Next hop is is one the same router continue elif self.topo.has_edge( node, next_router ) and next_iface == self.topo.get_edge_iface( next_router, node): # Or Next is directly connected continue else: if not (self._check_static_local(next_router, next_iface) or self._check_ospf_announced( next_router, next_iface)): not_announced.append((node, next_router, next_iface)) if not_announced: return False, not_announced return True, [] def _check_bgp_peer_connected(self): not_announced = [] for node in self.topo.routers_iter(): if not self.topo.is_bgp_enabled(node): continue for neighbor in self.topo.get_bgp_neighbors(node): remote_iface = self.topo.get_bgp_neighbor_iface(node, neighbor) if self.topo.has_edge(node, neighbor): phys_iface = self.topo.get_edge_iface(neighbor, node) if phys_iface != remote_iface and \ not (self._check_static_local(neighbor, remote_iface) or self._check_ospf_announced(neighbor, remote_iface)): not_announced.append((node, neighbor, remote_iface)) if not_announced: return False, not_announced return True, [] def synthesize_ospf(self): check, msg = self._check_reqs() if not check: raise SketchError(msg) seed = 0 ospfRand = random.Random(seed) path_gen = 100 ospf = OSPFCEGIS(network_graph=self.topo, gen_paths=path_gen, random_obj=ospfRand) for req in self.ospf_reqs: ospf.add_req(req) ospf.synthesize() ospf.update_network_graph() def synthesize(self): self.synthesize_connected() if self.bgp_reqs: self.synthesize_bgp() self.synthesize_ospf() self.synthesize_connected() if self.bgp_reqs: ret1, not_ann1 = self._check_next_hops() if not_ann1: tmp = [ "{}->{}:{}-{}".format( s, x, y, self.topo.get_interface_loop_addr(x, y)) for s, x, y in not_ann1 ] err = "The following next hop IP addresses" \ " are not announced via IGP protocol, " \ "Hence the BGP requirements cannot be satisfied " \ "(consider announcing them in OSPF or static routes)" \ ": {}".format(tmp) raise SketchError(err) ret1, not_ann2 = self._check_bgp_peer_connected() if not_ann2: tmp = [ "{}->{}:{}-{}".format( s, x, y, self.topo.get_interface_loop_addr(x, y)) for s, x, y in not_ann1 ] err = "The following peering IP addresses" \ " are not announced via IGP protocol, " \ "Hence the BGP requirements cannot be satisfied " \ "(consider announcing them in OSPF or static routes)" \ ": {}".format(tmp) raise SketchError(err) return True def write_configs(self, output_dir, prefix_map=None, gns3_config=None): writer = GNS3Topo(graph=self.topo, prefix_map=prefix_map, gns3_config=gns3_config) writer.write_configs(out_folder=output_dir)