Ejemplo n.º 1
0
 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
Ejemplo n.º 2
0
    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
Ejemplo n.º 3
0
 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
Ejemplo n.º 4
0
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
Ejemplo n.º 5
0
    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'))
Ejemplo n.º 6
0
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()
Ejemplo n.º 7
0
 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
Ejemplo n.º 8
0
 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
Ejemplo n.º 9
0
    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)
Ejemplo n.º 10
0
    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()
Ejemplo n.º 11
0
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
Ejemplo n.º 12
0
    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()
Ejemplo n.º 13
0
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()
Ejemplo n.º 14
0
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)
Ejemplo n.º 15
0
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)
Ejemplo n.º 16
0
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)
Ejemplo n.º 17
0
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)
Ejemplo n.º 18
0
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)
Ejemplo n.º 19
0
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)