Beispiel #1
0
def three_tier_ibgp_corner_cases(rtrs):
    """Calculate edges for iBGP l3 clusters that don't contain a HRR.
    Connects l1 to l3 directly"""
    up_links = []
    down_links = []
    over_links = []
    for l3_cluster, l3d in ank_utils.groupby("ibgp_l3_cluster", rtrs):
        for l2_cluster, l2d in ank_utils.groupby("ibgp_l2_cluster", l3d):
            l2d = list(l2d)
            if any(r.ibgp_level == 2 for r in l2d):
                log.debug("Cluster (%s, %s) has l2 devices, not "
                          "adding extra links" % (l3_cluster, l2_cluster))
            elif all(r.ibgp_level == 1 for r in l2d):
                # No l2 or l3 routers -> full-mesh of l1 routers
                over_links += [(s, t) for s in l2d for t in l2d if s != t]
                log.debug("Cluster (%s, %s) has no level 2 or 3 iBGP routers."
                          "Connecting l1 routers (%s) in full-mesh" %
                          (l3_cluster, l2_cluster, l2d))
            else:
                l1_rtrs = [r for r in l2d if r.ibgp_level == 1]
                l3_rtrs = [r for r in l2d if r.ibgp_level == 3]
                if not (len(l1_rtrs) and len(l3_rtrs)):
                    break  # no routers to connect
                log.debug("Cluster (%s, %s) has no level 2 iBGP routers."
                          "Connecting l1 routers (%s) to l3 routers (%s)" %
                          (l3_cluster, l2_cluster, l1_rtrs, l3_rtrs))

                l1_l3_up_links = [(s, t) for s in l1_rtrs for t in l3_rtrs]
                up_links += l1_l3_up_links
                down_links += [(t, s) for (s, t) in l1_l3_up_links]

    return up_links, down_links, over_links
Beispiel #2
0
def three_tier_ibgp_corner_cases(rtrs):
    """Calculate edges for iBGP l3 clusters that don't contain a HRR.
    Connects l1 to l3 directly"""
    up_links = []
    down_links = []
    over_links = []
    for l3_cluster, l3d in ank_utils.groupby("ibgp_l3_cluster", rtrs):
        for l2_cluster, l2d in ank_utils.groupby("ibgp_l2_cluster", l3d):
            l2d = list(l2d)
            if any(r.ibgp_level == 2 for r in l2d):
                log.debug("Cluster (%s, %s) has l2 devices, not "
                          "adding extra links" % (l3_cluster, l2_cluster))
            elif all(r.ibgp_level == 1 for r in l2d):
                # No l2 or l3 routers -> full-mesh of l1 routers
                over_links += [(s, t) for s in l2d for t in l2d if s != t]
                log.debug("Cluster (%s, %s) has no level 2 or 3 iBGP routers."
                        "Connecting l1 routers (%s) in full-mesh"
                        % (l3_cluster, l2_cluster, l2d))
            else:
                l1_rtrs = [r for r in l2d if r.ibgp_level == 1]
                l3_rtrs = [r for r in l2d if r.ibgp_level == 3]
                if not(len(l1_rtrs) and len(l3_rtrs)):
                    break  # no routers to connect
                log.debug("Cluster (%s, %s) has no level 2 iBGP routers."
                          "Connecting l1 routers (%s) to l3 routers (%s)"
                          % (l3_cluster, l2_cluster, l1_rtrs, l3_rtrs))

                l1_l3_up_links = [(s, t) for s in l1_rtrs for t in l3_rtrs]
                up_links += l1_l3_up_links
                down_links += [(t, s) for (s, t) in l1_l3_up_links]

    return up_links, down_links, over_links
Beispiel #3
0
def build_vrf(anm):
    """Build VRF Overlay"""
    g_in = anm['input']
    g_vrf = anm.add_overlay("vrf")
    g_vrf.add_nodes_from(g_in.nodes("is_router"), retain=["vrf_role", "vrf"])

    allocate_vrf_roles(g_vrf)

    vrf_pre_process(anm)

    def is_pe_ce_edge(edge):
        src_vrf_role = g_vrf.node(edge.src).vrf_role
        dst_vrf_role = g_vrf.node(edge.dst).vrf_role
        return (src_vrf_role, dst_vrf_role) in (("PE", "CE"), ("CE", "PE"))

    vrf_add_edges = (e for e in g_in.edges() if is_pe_ce_edge(e))
    #TODO: should mark as being towards PE or CE
    g_vrf.add_edges_from(vrf_add_edges, retain=['edge_id'])

    def is_pe_p_edge(edge):
        src_vrf_role = g_vrf.node(edge.src).vrf_role
        dst_vrf_role = g_vrf.node(edge.dst).vrf_role
        return (src_vrf_role, dst_vrf_role) in (("PE", "P"), ("P", "PE"))

    vrf_add_edges = (e for e in g_in.edges() if is_pe_p_edge(e))
    g_vrf.add_edges_from(vrf_add_edges, retain=['edge_id'])

    build_mpls_ldp(anm)
    # add PE to P edges

    add_vrf_loopbacks(g_vrf)
    # allocate route-targets per AS
    # This could later look at connected components for each ASN
    route_targets = {}
    for asn, devices in ank_utils.groupby("asn", g_vrf.nodes(vrf_role="PE")):
        asn_vrfs = [d.node_vrf_names for d in devices]
        # flatten list to unique set
        asn_vrfs = set(itertools.chain.from_iterable(asn_vrfs))
        route_targets[asn] = {
            vrf: "%s:%s" % (asn, index)
            for index, vrf in enumerate(sorted(asn_vrfs), 1)
        }

    g_vrf.data.route_targets = route_targets

    for node in g_vrf:
        vrf_loopbacks = node.interfaces("is_loopback", "vrf_name")
        for index, interface in enumerate(vrf_loopbacks, start=101):
            interface.index = index

    for edge in g_vrf.edges():
        # Set the vrf of the edge to be that of the CE device (either src or dst)
        edge.vrf = edge.src.vrf if edge.src.vrf_role is "CE" else edge.dst.vrf

    # map attributes to interfaces
    for edge in g_vrf.edges():
        for interface in edge.interfaces():
            interface.vrf_name = edge.vrf
Beispiel #4
0
def build_vrf(anm):
    """Build VRF Overlay"""
    g_in = anm['input']
    g_vrf = anm.add_overlay("vrf")
    g_vrf.add_nodes_from(g_in.nodes("is_router"), retain=["vrf_role", "vrf"])

    allocate_vrf_roles(g_vrf)

    vrf_pre_process(anm)

    def is_pe_ce_edge(edge):
        src_vrf_role = g_vrf.node(edge.src).vrf_role
        dst_vrf_role = g_vrf.node(edge.dst).vrf_role
        return (src_vrf_role, dst_vrf_role) in (("PE", "CE"), ("CE", "PE"))

    vrf_add_edges = (e for e in g_in.edges()
           if is_pe_ce_edge(e))
    #TODO: should mark as being towards PE or CE
    g_vrf.add_edges_from(vrf_add_edges, retain=['edge_id'])

    def is_pe_p_edge(edge):
        src_vrf_role = g_vrf.node(edge.src).vrf_role
        dst_vrf_role = g_vrf.node(edge.dst).vrf_role
        return (src_vrf_role, dst_vrf_role) in (("PE", "P"), ("P", "PE"))
    vrf_add_edges = (e for e in g_in.edges()
            if is_pe_p_edge(e))
    g_vrf.add_edges_from(vrf_add_edges, retain=['edge_id'])

    build_mpls_ldp(anm)
    # add PE to P edges

    add_vrf_loopbacks(g_vrf)
    # allocate route-targets per AS
    # This could later look at connected components for each ASN
    route_targets = {}
    for asn, devices in ank_utils.groupby("asn", g_vrf.nodes(vrf_role = "PE")):
        asn_vrfs = [d.node_vrf_names for d in devices]
        # flatten list to unique set
        asn_vrfs = set(itertools.chain.from_iterable(asn_vrfs)) 
        route_targets[asn] = {vrf: "%s:%s" % (asn, index)
                for index, vrf in enumerate(sorted(asn_vrfs), 1)}

    g_vrf.data.route_targets = route_targets

    for node in g_vrf:
        vrf_loopbacks = node.interfaces("is_loopback", "vrf_name")
        for index, interface in enumerate(vrf_loopbacks, start = 101):
            interface.index = index 

    for edge in g_vrf.edges():
        # Set the vrf of the edge to be that of the CE device (either src or dst)
        edge.vrf = edge.src.vrf if edge.src.vrf_role is "CE" else edge.dst.vrf

    # map attributes to interfaces
    for edge in g_vrf.edges():
        for interface in edge.interfaces():
            interface.vrf_name = edge.vrf
Beispiel #5
0
def validate_ibgp(anm):
    import networkx as nx
    #TODO: repeat for ibgp v6
    #TODO: test if overlay is present, if not then warn
    if not anm.has_overlay("ibgp_v4"):
        return # no ibgp v4  - eg if ip addressing disabled

    g_ibgp_v4 = anm['ibgp_v4']

    for asn, devices in ank_utils.groupby("asn", g_ibgp_v4):
        asn_subgraph = g_ibgp_v4.subgraph(devices)
        graph = asn_subgraph._graph
        # get subgraph
        if not nx.is_strongly_connected(graph):
            g_ibgp_v4.log.warning("iBGP v4 topology for ASN%s is disconnected" % asn)
            #TODO: list connected components - but not the primary?
        else:
            g_ibgp_v4.log.debug("iBGP v4 topology for ASN%s is connected" % asn)
Beispiel #6
0
def validate_igp(anm):
    import networkx as nx
    # TODO: test if overlay is present, if not then warn
    if not anm.has_overlay("igp"):
        return  # no ibgp v4  - eg if ip addressing disabled

    g_igp = anm['igp']

    for asn, devices in ank_utils.groupby("asn", g_igp):
        if asn is None:
            continue
        asn_subgraph = g_igp.subgraph(devices)
        graph = asn_subgraph._graph
        # get subgraph
        if not nx.is_connected(graph):
            g_igp.log.warning("IGP topology for ASN%s is disconnected" % asn)
            # TODO: list connected components - but not the primary?
        else:
            g_igp.log.debug("IGP topology for ASN%s is connected" % asn)
Beispiel #7
0
def validate_igp(anm):
    import networkx as nx
    # TODO: test if overlay is present, if not then warn
    if not anm.has_overlay("igp"):
        return  # no ibgp v4  - eg if ip addressing disabled

    g_igp = anm['igp']

    for asn, devices in ank_utils.groupby("asn", g_igp):
        if asn is None:
            continue
        asn_subgraph = g_igp.subgraph(devices)
        graph = asn_subgraph._graph
        # get subgraph
        if not nx.is_connected(graph):
            g_igp.log.warning("IGP topology for ASN%s is disconnected" % asn)
            # TODO: list connected components - but not the primary?
        else:
            g_igp.log.debug("IGP topology for ASN%s is connected" % asn)
Beispiel #8
0
def build_ibgp(anm):
    g_in = anm['input']
    g_bgp = anm['bgp']

    # TODO: build direct to ibgp graph - can construct combined bgp for vis
    #TODO: normalise input property

    ank_utils.copy_attr_from(g_in, g_bgp, "ibgp_role")
    ank_utils.copy_attr_from(
        g_in, g_bgp, "ibgp_l2_cluster", "hrr_cluster", default=None)
    ank_utils.copy_attr_from(
        g_in, g_bgp, "ibgp_l3_cluster", "rr_cluster", default=None)

    # TODO: add more detailed logging

    for n in g_bgp:
        # Tag with label to make logic clearer
        if n.ibgp_role is None:
            n.ibgp_role = "Peer"

            # TODO: if top-level, then don't mark as RRC

    ibgp_nodes = [n for n in g_bgp if not n.ibgp_role is "Disabled"]

    # Notify user of non-ibgp nodes
    non_ibgp_nodes = [n for n in g_bgp if n.ibgp_role is "Disabled"]
    if 0 < len(non_ibgp_nodes) < 10:
        log.info("Skipping iBGP for iBGP disabled nodes: %s", non_ibgp_nodes)
    elif len(non_ibgp_nodes) >= 10:
        log.info("Skipping iBGP for more than 10 iBGP disabled nodes:",
                 "refer to visualization for resulting topology.")

    # warn for any nodes that have RR set but no rr_cluster, or HRR set and no
    # hrr_cluster
    rr_mismatch = [
        n for n in ibgp_nodes if n.ibgp_role == "RR" and n.rr_cluster is None]
    if len(rr_mismatch):
        log.warning("Some routers are set as RR but have no rr_cluster: %s. Please specify an rr_cluster for peering."
                    % ", ".join(str(n) for n in rr_mismatch))

    hrr_mismatch = [
        n for n in ibgp_nodes if n.ibgp_role == "HRR" and n.hrr_cluster is None]
    if len(hrr_mismatch):
        log.warning("Some routers are set as HRR but have no hrr_cluster: %s. Please specify an hrr_cluster for peering."
                    % ", ".join(str(n) for n in hrr_mismatch))

    for _, asn_devices in ank_utils.groupby("asn", ibgp_nodes):
        asn_devices = list(asn_devices)

        # iBGP peer peers with
        peers = [n for n in asn_devices if n.ibgp_role == "Peer"]
        rrs = [n for n in asn_devices if n.ibgp_role == "RR"]
        hrrs = [n for n in asn_devices if n.ibgp_role == "HRR"]
        rrcs = [n for n in asn_devices if n.ibgp_role == "RRC"]

        over_links = []
        up_links = []
        down_links = []

        # 0. RRCs can only belong to either an rr_cluster or a hrr_cluster
        invalid_rrcs = [r for r in rrcs if r.rr_cluster is not None
                        and r.hrr_cluster is not None]
        if len(invalid_rrcs):
            message = ", ".join(str(r) for r in invalid_rrcs)
            log.warning("RRCs can only have either a rr_cluster or hrr_cluster set. "
                        "The following have both set, and only the rr_cluster will be used: %s", message)

        # TODO: do we also want to warn for RRCs with no cluster set? Do we also exclude these?
        # TODO: do we also want to warn for HRRs and RRs with no cluster set?
        # Do we also exclude these?

        # 1. Peers:
        # 1a. Peers connect over to peers
        over_links += [(s, t) for s in peers for t in peers]
        # 1b. Peers connect over to RRs
        over_links += [(s, t) for s in peers for t in rrs]

        # 2. RRs:
        # 2a. RRs connect over to Peers
        over_links += [(s, t) for s in rrs for t in peers]
        # 2b. RRs connect over to RRs
        over_links += [(s, t) for s in rrs for t in rrs]
        # 2c. RRs connect down to RRCs in same rr_cluster
        down_links += [(s, t) for s in rrs for t in rrcs
                       if s.rr_cluster == t.rr_cluster != None]
        # 2d. RRs connect down to HRRs in the same rr_cluster
        down_links += [(s, t) for s in rrs for t in hrrs
                       if s.rr_cluster == t.rr_cluster != None]

        # 3. HRRs
        # 3a. HRRs connect up to RRs in the same rr_cluster
        up_links += [(s, t) for s in hrrs for t in rrs
                     if s.rr_cluster == t.rr_cluster != None]
        # 3b. HRRs connect down to RRCs in same hrr_cluster (providing RRC has
        # no rr_cluster set)
        down_links += [(s, t) for s in hrrs for t in rrcs
                       if s.hrr_cluster == t.hrr_cluster != None
                       and t.rr_cluster is None]

        # 4. RRCs
        # 4a. RRCs connect up to RRs in the same rr_cluster (regardless if RRC
        # has hrr_cluster set)
        up_links += [(s, t) for s in rrcs for t in rrs
                     if s.rr_cluster == t.rr_cluster != None]
        # 3b. RRCs connect up to HRRs in same hrr_cluster (providing RRC has no
        # rr_cluster set)
        up_links += [(s, t) for s in rrcs for t in hrrs
                     if s.hrr_cluster == t.hrr_cluster != None
                     and s.rr_cluster is None]

        # Remove self-links
        over_links = [(s, t) for s, t in over_links if s != t]
        up_links = [(s, t) for s, t in up_links if s != t]
        down_links = [(s, t) for s, t in down_links if s != t]

        g_bgp.add_edges_from(over_links, type='ibgp', direction='over')
        g_bgp.add_edges_from(up_links, type='ibgp', direction='up')
        g_bgp.add_edges_from(down_links, type='ibgp', direction='down')
Beispiel #9
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 #10
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 #11
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())
Beispiel #12
0
def build_ibgp(anm):
    g_in = anm['input']
    g_bgp = anm['bgp']

    # TODO: build direct to ibgp graph - can construct combined bgp for vis
    #TODO: normalise input property

    ank_utils.copy_attr_from(g_in, g_bgp, "ibgp_role")
    ank_utils.copy_attr_from(g_in,
                             g_bgp,
                             "ibgp_l2_cluster",
                             "hrr_cluster",
                             default=None)
    ank_utils.copy_attr_from(g_in,
                             g_bgp,
                             "ibgp_l3_cluster",
                             "rr_cluster",
                             default=None)

    # TODO: add more detailed logging

    for n in g_bgp:
        # Tag with label to make logic clearer
        if n.ibgp_role is None:
            n.ibgp_role = "Peer"

            # TODO: if top-level, then don't mark as RRC

    ibgp_nodes = [n for n in g_bgp if not n.ibgp_role is "Disabled"]

    # Notify user of non-ibgp nodes
    non_ibgp_nodes = [n for n in g_bgp if n.ibgp_role is "Disabled"]
    if 0 < len(non_ibgp_nodes) < 10:
        log.info("Skipping iBGP for iBGP disabled nodes: %s", non_ibgp_nodes)
    elif len(non_ibgp_nodes) >= 10:
        log.info("Skipping iBGP for more than 10 iBGP disabled nodes:"
                 "refer to visualization for resulting topology.")

    # warn for any nodes that have RR set but no rr_cluster, or HRR set and no
    # hrr_cluster
    rr_mismatch = [
        n for n in ibgp_nodes if n.ibgp_role == "RR" and n.rr_cluster is None
    ]
    if len(rr_mismatch):
        log.warning(
            "Some routers are set as RR but have no rr_cluster: %s. Please specify an rr_cluster for peering."
            % ", ".join(str(n) for n in rr_mismatch))

    hrr_mismatch = [
        n for n in ibgp_nodes if n.ibgp_role == "HRR" and n.hrr_cluster is None
    ]
    if len(hrr_mismatch):
        log.warning(
            "Some routers are set as HRR but have no hrr_cluster: %s. Please specify an hrr_cluster for peering."
            % ", ".join(str(n) for n in hrr_mismatch))

    for _, asn_devices in ank_utils.groupby("asn", ibgp_nodes):
        asn_devices = list(asn_devices)

        # iBGP peer peers with
        peers = [n for n in asn_devices if n.ibgp_role == "Peer"]
        rrs = [n for n in asn_devices if n.ibgp_role == "RR"]
        hrrs = [n for n in asn_devices if n.ibgp_role == "HRR"]
        rrcs = [n for n in asn_devices if n.ibgp_role == "RRC"]

        over_links = []
        up_links = []
        down_links = []

        # 0. RRCs can only belong to either an rr_cluster or a hrr_cluster
        invalid_rrcs = [
            r for r in rrcs
            if r.rr_cluster is not None and r.hrr_cluster is not None
        ]
        if len(invalid_rrcs):
            message = ", ".join(str(r) for r in invalid_rrcs)
            log.warning(
                "RRCs can only have either a rr_cluster or hrr_cluster set. "
                "The following have both set, and only the rr_cluster will be used: %s",
                message)

        # TODO: do we also want to warn for RRCs with no cluster set? Do we also exclude these?
        # TODO: do we also want to warn for HRRs and RRs with no cluster set?
        # Do we also exclude these?

        # 1. Peers:
        # 1a. Peers connect over to peers
        over_links += [(s, t) for s in peers for t in peers]
        # 1b. Peers connect over to RRs
        over_links += [(s, t) for s in peers for t in rrs]

        # 2. RRs:
        # 2a. RRs connect over to Peers
        over_links += [(s, t) for s in rrs for t in peers]
        # 2b. RRs connect over to RRs
        over_links += [(s, t) for s in rrs for t in rrs]
        # 2c. RRs connect down to RRCs in same rr_cluster
        down_links += [(s, t) for s in rrs for t in rrcs
                       if s.rr_cluster == t.rr_cluster != None]
        # 2d. RRs connect down to HRRs in the same rr_cluster
        down_links += [(s, t) for s in rrs for t in hrrs
                       if s.rr_cluster == t.rr_cluster != None]

        # 3. HRRs
        # 3a. HRRs connect up to RRs in the same rr_cluster
        up_links += [(s, t) for s in hrrs for t in rrs
                     if s.rr_cluster == t.rr_cluster != None]
        # 3b. HRRs connect down to RRCs in same hrr_cluster (providing RRC has
        # no rr_cluster set)
        down_links += [
            (s, t) for s in hrrs for t in rrcs
            if s.hrr_cluster == t.hrr_cluster != None and t.rr_cluster is None
        ]

        # 4. RRCs
        # 4a. RRCs connect up to RRs in the same rr_cluster (regardless if RRC
        # has hrr_cluster set)
        up_links += [(s, t) for s in rrcs for t in rrs
                     if s.rr_cluster == t.rr_cluster != None]
        # 3b. RRCs connect up to HRRs in same hrr_cluster (providing RRC has no
        # rr_cluster set)
        up_links += [
            (s, t) for s in rrcs for t in hrrs
            if s.hrr_cluster == t.hrr_cluster != None and s.rr_cluster is None
        ]

        # Remove self-links
        over_links = [(s, t) for s, t in over_links if s != t]
        up_links = [(s, t) for s, t in up_links if s != t]
        down_links = [(s, t) for s, t in down_links if s != t]

        g_bgp.add_edges_from(over_links, type='ibgp', direction='over')
        g_bgp.add_edges_from(up_links, type='ibgp', direction='up')
        g_bgp.add_edges_from(down_links, type='ibgp', direction='down')
Beispiel #13
0
def build_bgp(anm):
    """Build iBGP end eBGP overlays"""
    # eBGP
    g_in = anm['input']
    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')

# 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

        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 _, 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
        all_pairs = [(s, t) for s in routers for t in routers if s != t]
        if max_level == 3:
            up_links, down_links, over_links = three_tier_ibgp_edges(routers)

        if max_level == 2:
            up_links, down_links, over_links = build_two_tier_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]

        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')

# 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 edge in g_bgp.edges(type='ibgp'):
        # TODO: need interface querying/selection. rather than hard-coded ids
        edge.bind_interface(edge.src, 0)