Beispiel #1
0
def build_ebgp(anm):
    g_in = anm['input']
    g_phy = anm['phy']
    g_ebgp = anm.add_overlay("ebgp", directed=True)
    g_ebgp.add_nodes_from(g_in.nodes("is_router"))
    ebgp_edges = [e for e in g_in.edges() if not e.attr_equal("asn")]
    g_ebgp.add_edges_from(ebgp_edges, bidirectional=True, type='ebgp')

    ebgp_switches = [
        n for n in g_in.nodes("is_switch")
        if not ank_utils.neigh_equal(g_phy, n, "asn")
    ]
    g_ebgp.add_nodes_from(ebgp_switches, retain=['asn'])
    log.debug("eBGP switches are %s" % ebgp_switches)
    g_ebgp.add_edges_from(
        (e for e in g_in.edges()
         if e.src in ebgp_switches or e.dst in ebgp_switches),
        bidirectional=True,
        type='ebgp')
    ank_utils.aggregate_nodes(g_ebgp, ebgp_switches, retain="edge_id")
    ebgp_switches = list(g_ebgp.nodes(
        "is_switch"))  # need to recalculate as may have aggregated
    log.debug("aggregated eBGP switches are %s" % ebgp_switches)
    exploded_edges = ank_utils.explode_nodes(g_ebgp,
                                             ebgp_switches,
                                             retain="edge_id")
    for edge in exploded_edges:
        edge.multipoint = True
Beispiel #2
0
def build_ebgp(anm):
    g_in = anm['input']
    g_phy = anm['phy']
    g_ebgp = anm.add_overlay("ebgp", directed=True)
    g_ebgp.add_nodes_from(g_in.nodes("is_router"))
    ebgp_edges = [e for e in g_in.edges() if not e.attr_equal("asn")]
    g_ebgp.add_edges_from(ebgp_edges, bidirectional=True, type='ebgp')

    ebgp_switches = [n for n in g_in.nodes("is_switch")
            if not ank_utils.neigh_equal(g_phy, n, "asn")]
    g_ebgp.add_nodes_from(ebgp_switches, retain=['asn'])
    log.debug("eBGP switches are %s" % ebgp_switches)
    g_ebgp.add_edges_from((e for e in g_in.edges()
            if e.src in ebgp_switches or e.dst in ebgp_switches),
    bidirectional=True, type='ebgp')
    ank_utils.aggregate_nodes(g_ebgp, ebgp_switches, retain="edge_id")
    # need to recalculate as may have aggregated
    ebgp_switches = list(g_ebgp.nodes("is_switch"))
    log.debug("aggregated eBGP switches are %s" % ebgp_switches)
    exploded_edges = ank_utils.explode_nodes(g_ebgp, ebgp_switches,
            retain="edge_id")
    same_asn_edges = []
    for edge in exploded_edges:
        if edge.src.asn == edge.dst.asn:
            same_asn_edges.append(edge)
        else:
            edge.multipoint = True
    """TODO: remove up to here once compiler updated"""

    g_ebgp.remove_edges_from(same_asn_edges)
Beispiel #3
0
def build_ebgp(anm):
    g_in = anm['input']
    g_phy = anm['phy']
    g_ebgp = anm.add_overlay("ebgp", directed=True)
    g_ebgp.add_nodes_from(g_in.routers())
    ebgp_edges = [e for e in g_in.edges() if not e.attr_equal("asn")]
    g_ebgp.add_edges_from(ebgp_edges, bidirectional=True, type='ebgp')

    ebgp_switches = [n for n in g_in.switches()
            if not ank_utils.neigh_equal(g_phy, n, "asn")]
    g_ebgp.add_nodes_from(ebgp_switches, retain=['asn'])
    g_ebgp.log.debug("eBGP switches are %s" % ebgp_switches)
    g_ebgp.add_edges_from((e for e in g_in.edges()
            if e.src in ebgp_switches or e.dst in ebgp_switches),
    bidirectional=True, type='ebgp')
    ank_utils.aggregate_nodes(g_ebgp, ebgp_switches)
    # need to recalculate as may have aggregated
    ebgp_switches = list(g_ebgp.switches())
    g_ebgp.log.debug("aggregated eBGP switches are %s" % ebgp_switches)
    exploded_edges = ank_utils.explode_nodes(g_ebgp, ebgp_switches)
    same_asn_edges = []
    for edge in exploded_edges:
        if edge.src.asn == edge.dst.asn:
            same_asn_edges.append(edge)
        else:
            edge.multipoint = True
    """TODO: remove up to here once compiler updated"""

    g_ebgp.remove_edges_from(same_asn_edges)
Beispiel #4
0
def build_ipv4(anm, infrastructure=True):
    """Builds IPv4 graph"""
    g_ipv4 = anm.add_overlay("ipv4")
    g_in = anm['input']
    g_graphics = anm['graphics']
    g_phy = anm['phy']

    g_ipv4.add_nodes_from(g_in)
    g_ipv4.add_edges_from(g_in.edges(type="physical"))

    ank_utils.aggregate_nodes(g_ipv4, g_ipv4.nodes("is_switch"),
                              retain="edge_id")

    edges_to_split = [edge for edge in g_ipv4.edges() if edge.attr_both(
        "is_l3device")]
    split_created_nodes = list(
        ank_utils.split(g_ipv4, edges_to_split, retain='edge_id'))
    for node in split_created_nodes:
        node['graphics'].x = ank_utils.neigh_average(g_ipv4, node, "x",
                                                     g_graphics) + 0.1 # temporary fix for gh-90
        node['graphics'].y = ank_utils.neigh_average(g_ipv4, node, "y",
                                                     g_graphics) + 0.1 # temporary fix for gh-90
        asn = ank_utils.neigh_most_frequent(
            g_ipv4, node, "asn", g_phy)  # arbitrary choice
        node['graphics'].asn = asn
        node.asn = asn # need to use asn in IP overlay for aggregating subnets

    switch_nodes = g_ipv4.nodes("is_switch")  # regenerate due to aggregated
    g_ipv4.update(switch_nodes, collision_domain=True)
                 # switches are part of collision domain
    g_ipv4.update(split_created_nodes, collision_domain=True)
# Assign collision domain to a host if all neighbours from same host
    for node in split_created_nodes:
        if ank_utils.neigh_equal(g_ipv4, node, "host", g_phy):
            node.host = ank_utils.neigh_attr(
                g_ipv4, node, "host", g_phy).next()  # first attribute

# set collision domain IPs
    for node in g_ipv4.nodes("collision_domain"):
        graphics_node = g_graphics.node(node)
        graphics_node.device_type = "collision_domain"
        if not node.is_switch:
            label = "_".join(
                sorted(ank_utils.neigh_attr(g_ipv4, node, "label", g_phy)))
            cd_label = "cd_%s" % label  # switches keep their names
            node.label = cd_label
            node.cd_id = cd_label
            graphics_node.label = cd_label

    #TODO: need to set allocate_ipv4 by default in the readers
    if g_in.data.allocate_ipv4 is False:
        manual_ipv4_allocation(anm)
    else:
        import autonetkit.plugins.ipv4 as ipv4
        ipv4.allocate_ips(g_ipv4, infrastructure)
        ank_utils.save(g_ipv4)
Beispiel #5
0
def build_ip(anm):
    g_ip = anm.add_overlay("ip")
    g_in = anm['input']
    g_graphics = anm['graphics']
    g_phy = anm['phy']

    g_ip.add_nodes_from(g_in)
    g_ip.add_edges_from(g_in.edges(type="physical"))

    ank_utils.aggregate_nodes(g_ip, g_ip.nodes("is_switch"), retain="edge_id")

    edges_to_split = [
        edge for edge in g_ip.edges() if edge.attr_both("is_l3device")
    ]
    for edge in edges_to_split:
        edge.split = True  # mark as split for use in building nidb
    split_created_nodes = list(
        ank_utils.split(g_ip,
                        edges_to_split,
                        retain=['edge_id', 'split'],
                        id_prepend="cd"))
    for node in split_created_nodes:
        node['graphics'].x = ank_utils.neigh_average(
            g_ip, node, "x", g_graphics) + 0.1  # temporary fix for gh-90
        node['graphics'].y = ank_utils.neigh_average(
            g_ip, node, "y", g_graphics) + 0.1  # temporary fix for gh-90
        asn = ank_utils.neigh_most_frequent(g_ip, node, "asn",
                                            g_phy)  # arbitrary choice
        node['graphics'].asn = asn
        node.asn = asn  # need to use asn in IP overlay for aggregating subnets

    switch_nodes = g_ip.nodes("is_switch")  # regenerate due to aggregated
    g_ip.update(switch_nodes, collision_domain=True)
    # switches are part of collision domain
    g_ip.update(split_created_nodes, collision_domain=True)
    # Assign collision domain to a host if all neighbours from same host
    for node in split_created_nodes:
        if ank_utils.neigh_equal(g_ip, node, "host", g_phy):
            node.host = ank_utils.neigh_attr(g_ip, node, "host",
                                             g_phy).next()  # first attribute


# set collision domain IPs
    for node in g_ip.nodes("collision_domain"):
        graphics_node = g_graphics.node(node)
        graphics_node.device_type = "collision_domain"
        if not node.is_switch:
            # use node sorting, as accomodates for numeric/string names
            neighbors = sorted(neigh for neigh in node.neighbors())
            label = "_".join(neigh.label for neigh in neighbors)
            cd_label = "cd_%s" % label  # switches keep their names
            node.label = cd_label
            node.cd_id = cd_label
            graphics_node.label = cd_label
Beispiel #6
0
def build_ip(anm):
    g_ip = anm.add_overlay("ip")
    g_in = anm['input']
    g_graphics = anm['graphics']
    g_phy = anm['phy']

    g_ip.add_nodes_from(g_in)
    g_ip.add_edges_from(g_in.edges(type="physical"))

    ank_utils.aggregate_nodes(g_ip, g_ip.nodes("is_switch"),
                              retain="edge_id")

    edges_to_split = [edge for edge in g_ip.edges() if edge.attr_both(
        "is_l3device")]
    for edge in edges_to_split:
        edge.split = True # mark as split for use in building nidb

    split_created_nodes = list(
        ank_utils.split(g_ip, edges_to_split,
            retain=['edge_id', 'split'], id_prepend = "cd"))
    for node in split_created_nodes:
        node['graphics'].x = ank_utils.neigh_average(g_ip, node, "x",
           g_graphics) + 0.1
         # temporary fix for gh-90
        node['graphics'].y = ank_utils.neigh_average(g_ip, node, "y",
             g_graphics) + 0.1
            # temporary fix for gh-90
        asn = ank_utils.neigh_most_frequent(
            g_ip, node, "asn", g_phy)  # arbitrary choice
        node['graphics'].asn = asn
        node.asn = asn # need to use asn in IP overlay for aggregating subnets

    switch_nodes = g_ip.nodes("is_switch")  # regenerate due to aggregated
    g_ip.update(switch_nodes, collision_domain=True)
                 # switches are part of collision domain
    g_ip.update(split_created_nodes, collision_domain=True)
# Assign collision domain to a host if all neighbours from same host
    for node in split_created_nodes:
        if ank_utils.neigh_equal(g_ip, node, "host", g_phy):
            node.host = ank_utils.neigh_attr(
                g_ip, node, "host", g_phy).next()  # first attribute

# set collision domain IPs
    for node in g_ip.nodes("collision_domain"):
        graphics_node = g_graphics.node(node)
        graphics_node.device_type = "collision_domain"
        if not node.is_switch:
            # use node sorting, as accomodates for numeric/string names
            neighbors = sorted(neigh for neigh in node.neighbors())
            label = "_".join(neigh.label for neigh in neighbors)
            cd_label = "cd_%s" % label  # switches keep their names
            node.label = cd_label
            node.cd_id = cd_label
            graphics_node.label = cd_label
Beispiel #7
0
def build_ebgp(anm):
    g_in = anm['input']
    g_phy = anm['phy']
    g_ebgp = anm.add_overlay("ebgp", directed=True)
    g_ebgp.add_nodes_from(g_in.nodes("is_router"))
    ebgp_edges = [e for e in g_in.edges() if not e.attr_equal("asn")]
    g_ebgp.add_edges_from(ebgp_edges, bidirectional=True, type='ebgp')

    ebgp_switches = [n for n in g_in.nodes("is_switch")
            if not ank_utils.neigh_equal(g_phy, n, "asn")]
    g_ebgp.add_nodes_from(ebgp_switches, retain=['asn'])
    log.debug("eBGP switches are %s" % ebgp_switches)
    g_ebgp.add_edges_from((e for e in g_in.edges()
            if e.src in ebgp_switches or e.dst in ebgp_switches), bidirectional=True, type='ebgp')
    ank_utils.aggregate_nodes(g_ebgp, ebgp_switches, retain="edge_id")
    ebgp_switches = list(g_ebgp.nodes("is_switch")) # need to recalculate as may have aggregated
    log.debug("aggregated eBGP switches are %s" % ebgp_switches)
    exploded_edges = ank_utils.explode_nodes(g_ebgp, ebgp_switches,
            retain="edge_id")
    for edge in exploded_edges:
        edge.multipoint = True
Beispiel #8
0
def build_bgp(anm):
    """Build iBGP end eBGP overlays"""
    # eBGP
    g_in = anm['input']
    g_phy = anm['phy']

    build_ebgp(anm)
    build_ebgp_v4(anm)
    build_ebgp_v6(anm)
    """TODO: remove from here once compiler updated"""
    g_bgp = anm.add_overlay("bgp", directed=True)
    g_bgp.add_nodes_from(g_in.nodes("is_router"))
    ebgp_edges = [edge for edge in g_in.edges() if not edge.attr_equal("asn")]
    g_bgp.add_edges_from(ebgp_edges, bidirectional=True, type='ebgp')
    #TODO: why don't we include edge_id here

    ebgp_switches = [
        n for n in g_in.nodes("is_switch")
        if not ank_utils.neigh_equal(g_phy, n, "asn")
    ]
    g_bgp.add_nodes_from(ebgp_switches, retain=['asn'])
    log.debug("eBGP switches are %s" % ebgp_switches)
    g_bgp.add_edges_from((e for e in g_in.edges()
                          if e.src in ebgp_switches or e.dst in ebgp_switches),
                         bidirectional=True,
                         type='ebgp')
    ank_utils.aggregate_nodes(g_bgp, ebgp_switches, retain="edge_id")
    ebgp_switches = list(
        g_bgp.nodes("is_switch"))  # need to recalculate as may have aggregated
    log.debug("aggregated eBGP switches are %s" % ebgp_switches)
    exploded_edges = ank_utils.explode_nodes(g_bgp,
                                             ebgp_switches,
                                             retain="edge_id")
    for edge in exploded_edges:
        edge.multipoint = True
    """TODO: remove up to here once compiler updated"""

    # now iBGP
    ank_utils.copy_attr_from(g_in, g_bgp, "ibgp_level")
    ank_utils.copy_attr_from(g_in, g_bgp, "ibgp_l2_cluster")
    ank_utils.copy_attr_from(g_in, g_bgp, "ibgp_l3_cluster")
    for node in g_bgp:
        # set defaults
        if node.ibgp_level is None:
            node.ibgp_level = 1

        if node.ibgp_level == "None":  # if unicode string from yEd
            node.ibgp_level = 1

#TODO CHECK FOR IBGP NONE

        node.ibgp_level = int(node.ibgp_level)  # ensure is numeric

        if not node.ibgp_l2_cluster or node.ibgp_l2_cluster == "None":
            # ibgp_l2_cluster defaults to region
            node.ibgp_l2_cluster = node.region or "default_l2_cluster"
        if not node.ibgp_l3_cluster or node.ibgp_l3_cluster == "None":
            # ibgp_l3_cluster defaults to ASN
            node.ibgp_l3_cluster = node.asn

    for asn, devices in ank_utils.groupby("asn", g_bgp):
        # group by nodes in phy graph
        routers = list(g_bgp.node(n) for n in devices if n.is_router)
        # list of nodes from bgp graph
        ibgp_levels = {int(r.ibgp_level) for r in routers}
        max_level = max(ibgp_levels)
        # all possible edge src/dst pairs
        ibgp_routers = [r for r in routers if r.ibgp_level > 0]
        all_pairs = [(s, t) for s in ibgp_routers for t in ibgp_routers
                     if s != t]
        if max_level == 3:
            up_links, down_links, over_links = three_tier_ibgp_edges(
                ibgp_routers)

        elif max_level == 2:
            up_links, down_links, over_links = build_two_tier_ibgp(
                ibgp_routers)

        elif max_level == 1:
            up_links = []
            down_links = []
            over_links = [(s, t) for (s, t) in all_pairs
                          if s.ibgp_l3_cluster == t.ibgp_l3_cluster
                          and s.ibgp_l2_cluster == t.ibgp_l2_cluster]
        else:
            # no iBGP
            up_links = []
            down_links = []
            over_links = []

        if max_level > 0:
            g_bgp.add_edges_from(up_links, type='ibgp', direction='up')
            g_bgp.add_edges_from(down_links, type='ibgp', direction='down')
            g_bgp.add_edges_from(over_links, type='ibgp', direction='over')

        else:
            log.debug("No iBGP routers in %s" % asn)


# and set label back
    ibgp_label_to_level = {
        0: "None",  # Explicitly set role to "None" -> Not in iBGP
        3: "RR",
        1: "RRC",
        2: "HRR",
    }
    for node in g_bgp:
        node.ibgp_role = ibgp_label_to_level[node.ibgp_level]

    ebgp_nodes = [
        d for d in g_bgp if any(edge.type == 'ebgp' for edge in d.edges())
    ]
    g_bgp.update(ebgp_nodes, ebgp=True)

    for ebgp_edge in g_bgp.edges(type="ebgp"):
        for interface in ebgp_edge.interfaces():
            interface.ebgp = True

    for edge in g_bgp.edges(type='ibgp'):
        # TODO: need interface querying/selection. rather than hard-coded ids
        edge.bind_interface(edge.src, 0)

    #TODO: need to initialise interface zero to be a loopback rather than physical type
    for node in g_bgp:
        for interface in node.interfaces():
            interface.multipoint = any(e.multipoint for e in interface.edges())

    build_ibgp_v4(anm)
    build_ibgp_v6(anm)
Beispiel #9
0
def build_ip(anm):
    #TODO: use the l3_conn graph
    g_ip = anm.add_overlay('ip')
    g_in = anm['input']
    g_graphics = anm['graphics']
    g_phy = anm['phy']

    #TODO: add these from layer2 graph - and scrap g_ip: clone g_layer2 to g_ipv4 and g_ipv6
    g_ip.add_nodes_from(g_phy)
    g_ip.add_edges_from(g_phy.edges())

    ank_utils.aggregate_nodes(g_ip, g_ip.switches())

    edges_to_split = [
        edge for edge in g_ip.edges()
        if edge.src.is_l3device() and edge.dst.is_l3device()
    ]
    for edge in edges_to_split:
        edge.split = True  # mark as split for use in building nidb

    split_created_nodes = list(
        ank_utils.split(g_ip,
                        edges_to_split,
                        retain=['split'],
                        id_prepend='cd'))
    for node in split_created_nodes:
        node['graphics'].x = ank_utils.neigh_average(g_ip, node, 'x',
                                                     g_graphics) + 0.1

        # temporary fix for gh-90

        node['graphics'].y = ank_utils.neigh_average(g_ip, node, 'y',
                                                     g_graphics) + 0.1

        # temporary fix for gh-90

        asn = ank_utils.neigh_most_frequent(g_ip, node, 'asn',
                                            g_phy)  # arbitrary choice
        node['graphics'].asn = asn
        node.asn = asn  # need to use asn in IP overlay for aggregating subnets

    switch_nodes = g_ip.switches()  # regenerate due to aggregated
    g_ip.update(switch_nodes, broadcast_domain=True)

    # switches are part of collision domain

    g_ip.update(split_created_nodes, broadcast_domain=True)

    # Assign collision domain to a host if all neighbours from same host

    for node in split_created_nodes:
        if ank_utils.neigh_equal(g_ip, node, 'host', g_phy):
            node.host = ank_utils.neigh_attr(g_ip, node, 'host',
                                             g_phy).next()  # first attribute

    # set collision domain IPs
    #TODO; work out why this throws a json exception
    #autonetkit.ank.set_node_default(g_ip,  broadcast_domain=False)

    for node in g_ip.nodes('broadcast_domain'):
        graphics_node = g_graphics.node(node)
        #graphics_node.device_type = 'broadcast_domain'
        if node.is_switch():
            node['phy'].broadcast_domain = True
        if not node.is_switch():
            # use node sorting, as accomodates for numeric/string names
            graphics_node.device_type = 'broadcast_domain'
            neighbors = sorted(neigh for neigh in node.neighbors())
            label = '_'.join(neigh.label for neigh in neighbors)
            cd_label = 'cd_%s' % label  # switches keep their names
            node.label = cd_label
            node.cd_id = cd_label
            graphics_node.label = cd_label
            node.device_type = "broadcast_domain"
Beispiel #10
0
def build_ip(anm):
    import autonetkit.plugins.ip as ip
    G_ip = anm.add_overlay("ip")
    G_in = anm['input']
    G_graphics = anm['graphics']
    G_phy = anm['phy']

    G_ip.add_nodes_from(G_in)
    G_ip.add_edges_from(G_in.edges(type="physical"))

#TODO: need to look at if allocate_v6 is specified: ie manually set

    ank.aggregate_nodes(G_ip, G_ip.nodes("is_switch"), retain = "edge_id")
#TODO: add function to update edge properties: can overload node update?

    edges_to_split = [edge for edge in G_ip.edges() if edge.attr_both("is_l3device")]
    split_created_nodes = list(ank.split(G_ip, edges_to_split, retain='edge_id'))
    for node in split_created_nodes:
        node['graphics'].x = ank.neigh_average(G_ip, node, "x", G_graphics)
        node['graphics'].y = ank.neigh_average(G_ip, node, "y", G_graphics)
        asn = ank.neigh_most_frequent(G_ip, node, "asn", G_phy) # arbitrary choice
        node['graphics'].asn = asn
# need to use asn in IP overlay for aggregating subnets
        node.asn = asn
#TODO: could choose largest ASN if tie break
#TODO: see if need G_phy - should auto fall through to phy for ASN

    switch_nodes = G_ip.nodes("is_switch")# regenerate due to aggregated
    G_ip.update(switch_nodes, collision_domain=True) # switches are part of collision domain
    G_ip.update(split_created_nodes, collision_domain=True)
# Assign collision domain to a host if all neighbours from same host
    for node in split_created_nodes:
        if ank.neigh_equal(G_ip, node, "host", G_phy):
            node.host = ank.neigh_attr(G_ip, node, "host", G_phy).next() # first attribute

    #TODO: Need to allocate interfaces or appropriate bypass for collision domain nodes

# set collision domain IPs
#TODO: trim next line
    collision_domain_id = itertools.count(0) # TODO: remove this, as isn't needed as set id to be based on neighbors
    for node in G_ip.nodes("collision_domain"):
        graphics_node = G_graphics.node(node)
        graphics_node.device_type = "collision_domain"
        cd_id = collision_domain_id.next()
        node.cd_id = cd_id
#TODO: Use this label
        if not node.is_switch:
            label = "_".join(sorted(ank.neigh_attr(G_ip, node, "label", G_phy)))
            cd_label = "cd_%s" % label # switches keep their names
            node.label = cd_label 
            node.cd_id = cd_label
            graphics_node.label = cd_label

    if G_in.data.allocate_ipv4 == False:
        import netaddr
        G_in_directed = anm['input_directed']

        for l3_device in G_ip.nodes("is_l3device"):
            directed_node = G_in_directed.node(l3_device)
            l3_device.loopback = directed_node.ipv4loopback
            for edge in l3_device.edges():
                # find edge in G_in_directed
                directed_edge = G_in_directed.edge(edge)
                edge.ip_address = netaddr.IPAddress(directed_edge.ipv4)

                # set subnet onto collision domain (can come from either direction) 
                collision_domain = edge.dst
                if not collision_domain.subnet:
                    #TODO: see if direct method in netaddr to deduce network
                    prefixlen = directed_edge.netPrefixLenV4
                    cidr_string = "%s/%s" % (edge.ip_address, prefixlen)
                    intermediate_subnet = netaddr.IPNetwork(cidr_string)
                    cidr_string = "%s/%s" % (intermediate_subnet.network, prefixlen)
                    subnet = netaddr.IPNetwork(cidr_string)
                    collision_domain.subnet = subnet

        # also need to form aggregated IP blocks (used for e.g. routing prefix advertisement)
        loopback_blocks = {}
        infra_blocks = {}
        for asn, devices in G_ip.groupby("asn").items():
            routers = [d for d in devices if d.is_router]
            loopbacks = [r.loopback for r in routers]
            loopback_blocks[asn] = netaddr.cidr_merge(loopbacks)

            collision_domains = [d for d in devices if d.collision_domain]
            subnets = [cd.subnet for cd in collision_domains]
            infra_blocks[asn] = netaddr.cidr_merge(subnets)

        G_ip.data.loopback_blocks = loopback_blocks
        G_ip.data.infra_blocks = infra_blocks

    else:
        ip.allocate_ips(G_ip)
        ank.save(G_ip)
Beispiel #11
0
def build_bgp(anm):
    """Build iBGP end eBGP overlays"""
    # eBGP
    g_in = anm['input']
    g_phy = anm['phy']

    if not anm['phy'].data.enable_routing:
        log.info("Routing disabled, not configuring BGP")
        return

    build_ebgp(anm)
    build_ebgp_v4(anm)
    build_ebgp_v6(anm)

    """TODO: remove from here once compiler updated"""
    g_bgp = anm.add_overlay("bgp", directed=True)
    g_bgp.add_nodes_from(g_in.routers())
    ebgp_edges = [edge for edge in g_in.edges()
        if edge.src.asn != edge.dst.asn]
    g_bgp.add_edges_from(ebgp_edges, bidirectional=True, type='ebgp')

    ebgp_switches = [n for n in g_in.switches()
            if not ank_utils.neigh_equal(g_phy, n, "asn")]
    g_bgp.add_nodes_from(ebgp_switches, retain=['asn'])
    log.debug("eBGP switches are %s" % ebgp_switches)
    g_bgp.add_edges_from((e for e in g_in.edges()
            if e.src in ebgp_switches or e.dst in ebgp_switches), bidirectional=True, type='ebgp')
    ank_utils.aggregate_nodes(g_bgp, ebgp_switches)
    ebgp_switches = list(g_bgp.switches()) # need to recalculate as may have aggregated
    log.debug("aggregated eBGP switches are %s" % ebgp_switches)
    exploded_edges = ank_utils.explode_nodes(g_bgp, ebgp_switches)

    same_asn_edges = []
    for edge in exploded_edges:
        if edge.src.asn == edge.dst.asn:
            same_asn_edges.append(edge)
        else:
            edge.multipoint = True
    """TODO: remove up to here once compiler updated"""
    ank_utils.copy_attr_from(g_in, g_bgp, "custom_config_bgp", dst_attr="custom_config")

    g_bgp.remove_edges_from(same_asn_edges)


    build_ibgp(anm)

    ebgp_nodes = [d for d in g_bgp if any(
        edge.type == 'ebgp' for edge in d.edges())]
    g_bgp.update(ebgp_nodes, ebgp=True)

    for ebgp_edge in g_bgp.edges(type = "ebgp"):
        for interface in ebgp_edge.interfaces():
            interface.ebgp = True

    for edge in g_bgp.edges(type='ibgp'):
        # TODO: need interface querying/selection. rather than hard-coded ids
        edge.bind_interface(edge.src, 0)

    #TODO: need to initialise interface zero to be a loopback rather than physical type
    for node in g_bgp:
        for interface in node.interfaces():
            interface.multipoint = any(e.multipoint for e in interface.edges())

    build_ibgp_v4(anm)
    build_ibgp_v6(anm)
Beispiel #12
0
def build_layer2_broadcast(anm):
        g_l2 = anm['layer2']
        g_phy = anm['phy']
        g_graphics = anm['graphics']
        g_l2_bc = anm.add_overlay('layer2_bc')
        g_l2_bc.add_nodes_from(g_l2)
        g_l2_bc.add_edges_from(g_l2.edges())

        edges_to_split = [edge for edge in g_l2_bc.edges()
            if edge.src.is_l3device() and edge.dst.is_l3device()]
        for edge in edges_to_split:
            edge.split = True  # mark as split for use in building nidb

        split_created_nodes = list(ank_utils.split(g_l2_bc, edges_to_split,
                                   retain=['split'],
                                   id_prepend='cd_'))
        for node in split_created_nodes:
            node['graphics'].x = ank_utils.neigh_average(g_l2_bc, node, 'x',
                    g_graphics) + 0.1

             # temporary fix for gh-90

            node['graphics'].y = ank_utils.neigh_average(g_l2_bc, node, 'y',
                    g_graphics) + 0.1

                # temporary fix for gh-90

            asn = ank_utils.neigh_most_frequent(g_l2_bc, node, 'asn', g_phy)  # arbitrary choice
            node['graphics'].asn = asn
            node.asn = asn  # need to use asn in IP overlay for aggregating subnets

        switch_nodes = g_l2_bc.switches()  # regenerate due to aggregated
        g_l2_bc.update(switch_nodes, broadcast_domain=True)

                     # switches are part of collision domain

        g_l2_bc.update(split_created_nodes, broadcast_domain=True)

    # Assign collision domain to a host if all neighbours from same host

        for node in split_created_nodes:
            if ank_utils.neigh_equal(g_l2_bc, node, 'host', g_phy):
                node.host = ank_utils.neigh_attr(g_l2_bc, node, 'host',
                        g_phy).next()  # first attribute

        # set collision domain IPs
        #TODO; work out why this throws a json exception
        #autonetkit.ank.set_node_default(g_l2_bc,  broadcast_domain=False)

        for node in g_l2_bc.nodes('broadcast_domain'):
            graphics_node = g_graphics.node(node)
            #graphics_node.device_type = 'broadcast_domain'
            if node.is_switch():
                node['phy'].broadcast_domain = True
            if not node.is_switch():
                # use node sorting, as accomodates for numeric/string names
                graphics_node.device_type = 'broadcast_domain'
                neighbors = sorted(neigh for neigh in node.neighbors())
                label = '_'.join(neigh.label for neigh in neighbors)
                cd_label = 'cd_%s' % label  # switches keep their names
                node.label = cd_label
                graphics_node.label = cd_label
                node.device_type = "broadcast_domain"
Beispiel #13
0
def build_layer2_broadcast(anm):
    g_l2 = anm['layer2']
    g_phy = anm['phy']
    g_graphics = anm['graphics']
    g_l2_bc = anm.add_overlay('layer2_bc')
    g_l2_bc.add_nodes_from(g_l2.l3devices())
    g_l2_bc.add_nodes_from(g_l2.switches())
    g_l2_bc.add_edges_from(g_l2.edges())

    # remove external connectors

    edges_to_split = [
        edge for edge in g_l2_bc.edges()
        if edge.src.is_l3device() and edge.dst.is_l3device()
    ]
    #TODO: debug the edges to split
    #print "edges to split", edges_to_split
    for edge in edges_to_split:
        edge.split = True  # mark as split for use in building nidb

    split_created_nodes = list(
        ank_utils.split(g_l2_bc,
                        edges_to_split,
                        retain=['split'],
                        id_prepend='cd_'))

    #TODO: if parallel nodes, offset
    #TODO: remove graphics, assign directly
    for node in split_created_nodes:
        node['graphics'].x = ank_utils.neigh_average(g_l2_bc, node, 'x',
                                                     g_graphics) + 0.1

        # temporary fix for gh-90

        node['graphics'].y = ank_utils.neigh_average(g_l2_bc, node, 'y',
                                                     g_graphics) + 0.1

        # temporary fix for gh-90

        asn = ank_utils.neigh_most_frequent(g_l2_bc, node, 'asn',
                                            g_phy)  # arbitrary choice
        node['graphics'].asn = asn
        node.asn = asn  # need to use asn in IP overlay for aggregating subnets

    from collections import defaultdict
    coincident_nodes = defaultdict(list)
    for node in split_created_nodes:
        coincident_nodes[(node['graphics'].x, node['graphics'].y)].append(node)

    coincident_nodes = {
        k: v
        for k, v in coincident_nodes.items() if len(v) > 1
    }  # trim out single node co-ordinates
    import math
    for key, val in coincident_nodes.items():
        for index, item in enumerate(val):
            index = index + 1
            x_offset = 25 * math.floor(index / 2) * math.pow(-1, index)
            y_offset = -1 * 25 * math.floor(index / 2) * math.pow(-1, index)
            item['graphics'].x = item['graphics'].x + x_offset
            item['graphics'].y = item['graphics'].y + y_offset

    switch_nodes = g_l2_bc.switches()  # regenerate due to aggregated
    g_l2_bc.update(switch_nodes, broadcast_domain=True)

    # switches are part of collision domain
    g_l2_bc.update(split_created_nodes, broadcast_domain=True)

    # Assign collision domain to a host if all neighbours from same host

    for node in split_created_nodes:
        if ank_utils.neigh_equal(g_l2_bc, node, 'host', g_phy):
            node.host = ank_utils.neigh_attr(g_l2_bc, node, 'host',
                                             g_phy).next()  # first attribute

    # set collision domain IPs
    #TODO; work out why this throws a json exception
    #autonetkit.ank.set_node_default(g_l2_bc,  broadcast_domain=False)

    for node in g_l2_bc.nodes('broadcast_domain'):
        graphics_node = g_graphics.node(node)
        #graphics_node.device_type = 'broadcast_domain'
        if node.is_switch():
            node['phy'].broadcast_domain = True
        if not node.is_switch():
            # use node sorting, as accomodates for numeric/string names
            graphics_node.device_type = 'broadcast_domain'
            neighbors = sorted(neigh for neigh in node.neighbors())
            label = '_'.join(neigh.label for neigh in neighbors)
            cd_label = 'cd_%s' % label  # switches keep their names
            node.label = cd_label
            graphics_node.label = cd_label
            node.device_type = "broadcast_domain"
            node.label = node.id
            graphics_node.label = node.id
Beispiel #14
0
def build_bgp(anm):
    """Build iBGP end eBGP overlays"""
    # eBGP
    g_in = anm['input']
    g_phy = anm['phy']

    if not anm['phy'].data.enable_routing:
        log.info("Routing disabled, not configuring BGP")
        return

    build_ebgp(anm)
    build_ebgp_v4(anm)
    build_ebgp_v6(anm)

    """TODO: remove from here once compiler updated"""
    g_bgp = anm.add_overlay("bgp", directed=True)
    g_bgp.add_nodes_from(g_in.routers())
    ebgp_edges = [edge for edge in g_in.edges() if not edge.attr_equal("asn")]
    g_bgp.add_edges_from(ebgp_edges, bidirectional=True, type='ebgp')

    ebgp_switches = [n for n in g_in.switches()
            if not ank_utils.neigh_equal(g_phy, n, "asn")]
    g_bgp.add_nodes_from(ebgp_switches, retain=['asn'])
    log.debug("eBGP switches are %s" % ebgp_switches)
    g_bgp.add_edges_from((e for e in g_in.edges()
            if e.src in ebgp_switches or e.dst in ebgp_switches), bidirectional=True, type='ebgp')
    ank_utils.aggregate_nodes(g_bgp, ebgp_switches)
    ebgp_switches = list(g_bgp.switches()) # need to recalculate as may have aggregated
    log.debug("aggregated eBGP switches are %s" % ebgp_switches)
    exploded_edges = ank_utils.explode_nodes(g_bgp, ebgp_switches)

    same_asn_edges = []
    for edge in exploded_edges:
        if edge.src.asn == edge.dst.asn:
            same_asn_edges.append(edge)
        else:
            edge.multipoint = True
    """TODO: remove up to here once compiler updated"""

    g_bgp.remove_edges_from(same_asn_edges)


    build_ibgp(anm)

    ebgp_nodes = [d for d in g_bgp if any(
        edge.type == 'ebgp' for edge in d.edges())]
    g_bgp.update(ebgp_nodes, ebgp=True)

    for ebgp_edge in g_bgp.edges(type = "ebgp"):
        for interface in ebgp_edge.interfaces():
            interface.ebgp = True

    for edge in g_bgp.edges(type='ibgp'):
        # TODO: need interface querying/selection. rather than hard-coded ids
        edge.bind_interface(edge.src, 0)

    #TODO: need to initialise interface zero to be a loopback rather than physical type
    for node in g_bgp:
        for interface in node.interfaces():
            interface.multipoint = any(e.multipoint for e in interface.edges())

    build_ibgp_v4(anm)
    build_ibgp_v6(anm)
Beispiel #15
0
def build_bgp(anm):
    """Build iBGP end eBGP overlays"""
    # eBGP
    g_in = anm['input']
    g_phy = anm['phy']

    if not anm['phy'].data.enable_routing:
        log.info("Routing disabled, not configuring BGP")
        return

    build_ebgp(anm)
    build_ebgp_v4(anm)
    build_ebgp_v6(anm)

    """TODO: remove from here once compiler updated"""
    g_bgp = anm.add_overlay("bgp", directed=True)
    g_bgp.add_nodes_from(g_in.nodes("is_router"))
    ebgp_edges = [edge for edge in g_in.edges() if not edge.attr_equal("asn")]
    g_bgp.add_edges_from(ebgp_edges, bidirectional=True, type='ebgp')
#TODO: why don't we include edge_id here

    ebgp_switches = [n for n in g_in.nodes("is_switch")
            if not ank_utils.neigh_equal(g_phy, n, "asn")]
    g_bgp.add_nodes_from(ebgp_switches, retain=['asn'])
    log.debug("eBGP switches are %s" % ebgp_switches)
    g_bgp.add_edges_from((e for e in g_in.edges()
            if e.src in ebgp_switches or e.dst in ebgp_switches), bidirectional=True, type='ebgp')
    ank_utils.aggregate_nodes(g_bgp, ebgp_switches, retain="edge_id")
    ebgp_switches = list(g_bgp.nodes("is_switch")) # need to recalculate as may have aggregated
    log.debug("aggregated eBGP switches are %s" % ebgp_switches)
    exploded_edges = ank_utils.explode_nodes(g_bgp, ebgp_switches,
            retain="edge_id")

    same_asn_edges = []
    for edge in exploded_edges:
        if edge.src.asn == edge.dst.asn:
            same_asn_edges.append(edge)
        else:
            edge.multipoint = True
    """TODO: remove up to here once compiler updated"""

    g_bgp.remove_edges_from(same_asn_edges)

# now iBGP
    ank_utils.copy_attr_from(g_in, g_bgp, "ibgp_level")
    ank_utils.copy_attr_from(g_in, g_bgp, "ibgp_l2_cluster")
    ank_utils.copy_attr_from(g_in, g_bgp, "ibgp_l3_cluster")
    for node in g_bgp:
        # set defaults
        if node.ibgp_level is None:
            node.ibgp_level = 1

        if node.ibgp_level == "None":  # if unicode string from yEd
            node.ibgp_level = 1

#TODO CHECK FOR IBGP NONE

        node.ibgp_level = int(node.ibgp_level)  # ensure is numeric

        if not node.ibgp_l2_cluster or node.ibgp_l2_cluster == "None":
            # ibgp_l2_cluster defaults to region
            node.ibgp_l2_cluster = node.region or "default_l2_cluster"
        if not node.ibgp_l3_cluster or node.ibgp_l3_cluster == "None":
            # ibgp_l3_cluster defaults to ASN
            node.ibgp_l3_cluster = node.asn

    for asn, devices in ank_utils.groupby("asn", g_bgp):
        # group by nodes in phy graph
        routers = list(g_bgp.node(n) for n in devices if n.is_router)
        # list of nodes from bgp graph
        ibgp_levels = {int(r.ibgp_level) for r in routers}
        max_level = max(ibgp_levels)
        # all possible edge src/dst pairs
        ibgp_routers = [r for r in routers if r.ibgp_level > 0]
        all_pairs = [(s, t) for s in ibgp_routers for t in ibgp_routers if s != t]
        if max_level == 3:
            up_links, down_links, over_links = three_tier_ibgp_edges(ibgp_routers)

        elif max_level == 2:
            #TODO: check when this is reached - as RR is not HRR.... due to naming/levels mapping
            up_links, down_links, over_links = build_two_tier_ibgp(ibgp_routers)

        elif max_level == 1:
            up_links = []
            down_links = []
            over_links = [(s, t) for (s, t) in all_pairs
                             if s.ibgp_l3_cluster == t.ibgp_l3_cluster
                             and s.ibgp_l2_cluster == t.ibgp_l2_cluster
                             ]
        else:
            # no iBGP
            up_links = []
            down_links = []
            over_links = []

        if max_level > 0:
            g_bgp.add_edges_from(up_links, type='ibgp', direction='up')
            g_bgp.add_edges_from(down_links, type='ibgp', direction='down')
            g_bgp.add_edges_from(over_links, type='ibgp', direction='over')

        else:
            log.debug("No iBGP routers in %s" % asn)

# and set label back
    ibgp_label_to_level = {
        0: "None",  # Explicitly set role to "None" -> Not in iBGP
        3: "RR",
        1: "RRC",
        2: "HRR",
    }
    for node in g_bgp:
        node.ibgp_role = ibgp_label_to_level[node.ibgp_level]

    ebgp_nodes = [d for d in g_bgp if any(
        edge.type == 'ebgp' for edge in d.edges())]
    g_bgp.update(ebgp_nodes, ebgp=True)

    for ebgp_edge in g_bgp.edges(type = "ebgp"):
        for interface in ebgp_edge.interfaces():
            interface.ebgp = True

    for edge in g_bgp.edges(type='ibgp'):
        # TODO: need interface querying/selection. rather than hard-coded ids
        edge.bind_interface(edge.src, 0)

    #TODO: need to initialise interface zero to be a loopback rather than physical type
    for node in g_bgp:
        for interface in node.interfaces():
            interface.multipoint = any(e.multipoint for e in interface.edges())

    build_ibgp_v4(anm)
    build_ibgp_v6(anm)
Beispiel #16
0
def build_layer2_broadcast(anm):
    g_l2 = anm['layer2']
    g_phy = anm['phy']
    g_graphics = anm['graphics']
    g_l2_bc = anm.add_overlay('layer2_bc')
    g_l2_bc.add_nodes_from(g_l2.l3devices())
    g_l2_bc.add_nodes_from(g_l2.switches())
    g_l2_bc.add_edges_from(g_l2.edges(), retain=['link_type'])

    # remove external connectors

    edges_to_split = [edge for edge in g_l2_bc.edges()
                      if edge.src.is_l3device() and edge.dst.is_l3device()]
    # TODO: debug the edges to split
    for edge in edges_to_split:
        edge.split = True  # mark as split for use in building nidb

    split_created_nodes = list(ank_utils.split(g_l2_bc, edges_to_split,
                                               retain=['split'],
                                               id_prepend='cd_'))

    # TODO: if parallel nodes, offset
    # TODO: remove graphics, assign directly
    if len(g_graphics):
        co_ords_overlay = g_graphics  # source from graphics overlay
    else:
        co_ords_overlay = g_phy  # source from phy overlay

    for node in split_created_nodes:
        node['graphics'].x = ank_utils.neigh_average(g_l2_bc, node, 'x',
                                                     co_ords_overlay) + 0.1

        # temporary fix for gh-90

        node['graphics'].y = ank_utils.neigh_average(g_l2_bc, node, 'y',
                                                     co_ords_overlay) + 0.1

        # temporary fix for gh-90

        asn = ank_utils.neigh_most_frequent(
            g_l2_bc, node, 'asn', g_phy)  # arbitrary choice
        node['graphics'].asn = asn
        node.asn = asn  # need to use asn in IP overlay for aggregating subnets

    # also allocate an ASN for virtual switches
    vswitches = [n for n in g_l2_bc.nodes()
                 if n['layer2'].device_type == "switch"
                 and n['layer2'].device_subtype == "virtual"]
    for node in vswitches:
        # TODO: refactor neigh_most_frequent to allow fallthrough attributes
        # asn = ank_utils.neigh_most_frequent(g_l2_bc, node, 'asn', g_l2)  #
        # arbitrary choice
        asns = [n['layer2'].asn for n in node.neighbors()]
        asns = [x for x in asns if x is not None]
        asn = ank_utils.most_frequent(asns)
        node.asn = asn  # need to use asn in IP overlay for aggregating subnets
        # also mark as broadcast domain

    from collections import defaultdict
    coincident_nodes = defaultdict(list)
    for node in split_created_nodes:
        coincident_nodes[(node['graphics'].x, node['graphics'].y)].append(node)

    coincident_nodes = {k: v for k, v in coincident_nodes.items()
                        if len(v) > 1}  # trim out single node co-ordinates
    import math
    for _, val in coincident_nodes.items():
        for index, item in enumerate(val):
            index = index + 1
            x_offset = 25 * math.floor(index / 2) * math.pow(-1, index)
            y_offset = -1 * 25 * math.floor(index / 2) * math.pow(-1, index)
            item['graphics'].x = item['graphics'].x + x_offset
            item['graphics'].y = item['graphics'].y + y_offset

    switch_nodes = g_l2_bc.switches()  # regenerate due to aggregated
    g_l2_bc.update(switch_nodes, broadcast_domain=True)

    # switches are part of collision domain
    g_l2_bc.update(split_created_nodes, broadcast_domain=True)

    # Assign collision domain to a host if all neighbours from same host

    for node in split_created_nodes:
        if ank_utils.neigh_equal(g_l2_bc, node, 'host', g_phy):
            node.host = ank_utils.neigh_attr(g_l2_bc, node, 'host',
                                             g_phy).next()  # first attribute

    # set collision domain IPs
    # TODO; work out why this throws a json exception
    #autonetkit.ank.set_node_default(g_l2_bc,  broadcast_domain=False)

    for node in g_l2_bc.nodes('broadcast_domain'):
        graphics_node = g_graphics.node(node)
        #graphics_node.device_type = 'broadcast_domain'
        if node.is_switch():
            # TODO: check not virtual
            node['phy'].broadcast_domain = True
        if not node.is_switch():
            # use node sorting, as accomodates for numeric/string names
            graphics_node.device_type = 'broadcast_domain'
            neighbors = sorted(neigh for neigh in node.neighbors())
            label = '_'.join(neigh.label for neigh in neighbors)
            cd_label = 'cd_%s' % label  # switches keep their names
            node.label = cd_label
            graphics_node.label = cd_label
            node.device_type = "broadcast_domain"
            node.label = node.id
            graphics_node.label = node.id

    for node in vswitches:
        node.broadcast_domain = True
Beispiel #17
0
def build_bgp(anm):
    """Build iBGP end eBGP overlays"""
    # eBGP
    g_in = anm["input"]
    g_phy = anm["phy"]
    g_bgp = anm.add_overlay("bgp", directed=True)
    g_bgp.add_nodes_from(g_in.nodes("is_router"))
    ebgp_edges = [edge for edge in g_in.edges() if not edge.attr_equal("asn")]
    g_bgp.add_edges_from(ebgp_edges, bidirectional=True, type="ebgp")
    # TODO: why don't we include edge_id here

    ebgp_switches = [n for n in g_in.nodes("is_switch") if not ank_utils.neigh_equal(g_phy, n, "asn")]
    g_bgp.add_nodes_from(ebgp_switches, retain=["asn"])
    log.debug("eBGP switches are %s" % ebgp_switches)
    g_bgp.add_edges_from(
        (e for e in g_in.edges() if e.src in ebgp_switches or e.dst in ebgp_switches), bidirectional=True, type="ebgp"
    )
    ank_utils.aggregate_nodes(g_bgp, ebgp_switches, retain="edge_id")
    ebgp_switches = list(g_bgp.nodes("is_switch"))  # need to recalculate as may have aggregated
    log.debug("aggregated eBGP switches are %s" % ebgp_switches)
    exploded_edges = ank_utils.explode_nodes(g_bgp, ebgp_switches, retain="edge_id")
    for edge in exploded_edges:
        edge.multipoint = True

    # now iBGP
    ank_utils.copy_attr_from(g_in, g_bgp, "ibgp_level")
    ank_utils.copy_attr_from(g_in, g_bgp, "ibgp_l2_cluster")
    ank_utils.copy_attr_from(g_in, g_bgp, "ibgp_l3_cluster")
    for node in g_bgp:
        # set defaults
        if node.ibgp_level is None:
            node.ibgp_level = 1

        if node.ibgp_level == "None":  # if unicode string from yEd
            node.ibgp_level = 1

        # TODO CHECK FOR IBGP NONE

        node.ibgp_level = int(node.ibgp_level)  # ensure is numeric

        if not node.ibgp_l2_cluster or node.ibgp_l2_cluster == "None":
            # ibgp_l2_cluster defaults to region
            node.ibgp_l2_cluster = node.region or "default_l2_cluster"
        if not node.ibgp_l3_cluster or node.ibgp_l3_cluster == "None":
            # ibgp_l3_cluster defaults to ASN
            node.ibgp_l3_cluster = node.asn

    for asn, devices in ank_utils.groupby("asn", g_bgp):
        # group by nodes in phy graph
        routers = list(g_bgp.node(n) for n in devices if n.is_router)
        # list of nodes from bgp graph
        ibgp_levels = {int(r.ibgp_level) for r in routers}
        max_level = max(ibgp_levels)
        # all possible edge src/dst pairs
        ibgp_routers = [r for r in routers if r.ibgp_level > 0]
        all_pairs = [(s, t) for s in ibgp_routers for t in ibgp_routers if s != t]
        if max_level == 3:
            up_links, down_links, over_links = three_tier_ibgp_edges(ibgp_routers)

        elif max_level == 2:
            up_links, down_links, over_links = build_two_tier_ibgp(ibgp_routers)

        elif max_level == 1:
            up_links = []
            down_links = []
            over_links = [
                (s, t)
                for (s, t) in all_pairs
                if s.ibgp_l3_cluster == t.ibgp_l3_cluster and s.ibgp_l2_cluster == t.ibgp_l2_cluster
            ]
        else:
            # no iBGP
            up_links = []
            down_links = []
            over_links = []

        if max_level > 0:
            g_bgp.add_edges_from(up_links, type="ibgp", direction="up")
            g_bgp.add_edges_from(down_links, type="ibgp", direction="down")
            g_bgp.add_edges_from(over_links, type="ibgp", direction="over")

        else:
            log.debug("No iBGP routers in %s" % asn)

    # and set label back
    ibgp_label_to_level = {0: "None", 3: "RR", 1: "RRC", 2: "HRR"}  # Explicitly set role to "None" -> Not in iBGP
    for node in g_bgp:
        node.ibgp_role = ibgp_label_to_level[node.ibgp_level]

    ebgp_nodes = [d for d in g_bgp if any(edge.type == "ebgp" for edge in d.edges())]
    g_bgp.update(ebgp_nodes, ebgp=True)

    for ebgp_edge in g_bgp.edges(type="ebgp"):
        for interface in ebgp_edge.interfaces():
            interface.ebgp = True

    for edge in g_bgp.edges(type="ibgp"):
        # TODO: need interface querying/selection. rather than hard-coded ids
        edge.bind_interface(edge.src, 0)

    # TODO: need to initialise interface zero to be a loopback rather than physical type
    for node in g_bgp:
        for interface in node.interfaces():
            interface.multipoint = any(e.multipoint for e in interface.edges())