From 1069425868d108bd6e582f6b67a78a1d9ac4bd85 Mon Sep 17 00:00:00 2001 From: Philippe Guibert Date: Thu, 11 May 2023 13:56:16 +0200 Subject: [PATCH] bgpd: allocate label bound to received mpls vpn routes Current implementation does not offer a new label to bind to a received VPN route entry to redistribute with that new label. This commit allocates a label for VPN entries that have a valid label, and a reachable next-hop interface that is configured as follows: > interface eth0 > mpls bgp l3vpn-multi-domain-switching > exit An mplsvpn next-hop label binding entry is created in an mpls vpn nexthop label bind hash table of the current BGP instance. That mpls vpn next-hop label entry is indexed by the (next-hop, orig_label) values provided by the incoming updates, and shared with other updates having the same (next-hop, orig_label) values. A new 'LP_TYPE_BGP_L3VPN_BIND' label value is picked up from the zebra mpls label pool, and assigned to the new_label attribute. The 'bgp_path_info' appends a 'bgp_mplsvpn_nh_label_bind' structure to the 'mplsvpn' union structure. Both structures in the union are not used at the same, as the paths are either VRF updates to export, or MPLS VPN updates. Using an union gives a 24 bytes memory gain compared to if the structures had not been in an union (24 bytes compared to 48 bytes). Signed-off-by: Philippe Guibert --- bgpd/bgp_mplsvpn.c | 123 +++++++++++++++++++++++++++++++++++++++++++++ bgpd/bgp_mplsvpn.h | 6 +++ bgpd/bgp_nexthop.c | 1 + bgpd/bgp_nht.c | 1 + bgpd/bgp_route.c | 60 ++++++++++++++++++++++ bgpd/bgp_route.h | 10 ++++ bgpd/bgpd.c | 43 ++++++++++------ 7 files changed, 229 insertions(+), 15 deletions(-) diff --git a/bgpd/bgp_mplsvpn.c b/bgpd/bgp_mplsvpn.c index 4b8aa1ee70..cbb52c5f7f 100644 --- a/bgpd/bgp_mplsvpn.c +++ b/bgpd/bgp_mplsvpn.c @@ -3888,6 +3888,12 @@ int bgp_mplsvpn_nh_label_bind_cmp( void bgp_mplsvpn_nh_label_bind_free( struct bgp_mplsvpn_nh_label_bind_cache *bmnc) { + if (bmnc->allocation_in_progress) { + bmnc->allocation_in_progress = false; + bgp_mplsvpn_nh_label_bind_cache_del( + &bmnc->bgp_vpn->mplsvpn_nh_label_bind, bmnc); + return; + } if (bmnc->new_label != MPLS_INVALID_LABEL) bgp_lp_release(LP_TYPE_BGP_L3VPN_BIND, bmnc, bmnc->new_label); bgp_mplsvpn_nh_label_bind_cache_del( @@ -3926,3 +3932,120 @@ struct bgp_mplsvpn_nh_label_bind_cache *bgp_mplsvpn_nh_label_bind_find( return bgp_mplsvpn_nh_label_bind_cache_find(tree, &bmnc); } + +/* Called to check if the incoming l3vpn path entry + * has mpls label information + */ +bool bgp_mplsvpn_path_uses_valid_mpls_label(struct bgp_path_info *pi) +{ + if (pi->attr && pi->attr->srv6_l3vpn) + /* srv6 sid */ + return false; + + if (pi->attr && + CHECK_FLAG(pi->attr->flag, ATTR_FLAG_BIT(BGP_ATTR_PREFIX_SID)) && + pi->attr->label_index != BGP_INVALID_LABEL_INDEX) + /* prefix_sid attribute */ + return false; + + if (!pi->extra || !bgp_is_valid_label(&pi->extra->label[0])) + /* invalid MPLS label */ + return false; + return true; +} + +/* Called upon reception of a ZAPI Message from zebra, about + * a new available label. + */ +static int bgp_mplsvpn_nh_label_bind_get_local_label_cb(mpls_label_t label, + void *context, + bool allocated) +{ + struct bgp_mplsvpn_nh_label_bind_cache *bmnc = context; + + if (BGP_DEBUG(labelpool, LABELPOOL)) + zlog_debug("%s: label=%u, allocated=%d, nexthop=%pFX, label %u", + __func__, label, allocated, &bmnc->nexthop, + bmnc->orig_label); + if (allocated) + /* update the entry with the new label */ + bmnc->new_label = label; + else + /* + * previously-allocated label is now invalid + * eg: zebra deallocated the labels and notifies it + */ + bmnc->new_label = MPLS_INVALID_LABEL; + + if (!bmnc->allocation_in_progress) { + bgp_mplsvpn_nh_label_bind_free(bmnc); + return 0; + } + bmnc->allocation_in_progress = false; + + /* Create MPLS entry with new_label */ + /* Trigger BGP process to re-advertise updates */ + return 0; +} + +void bgp_mplsvpn_path_nh_label_bind_unlink(struct bgp_path_info *pi) +{ + struct bgp_mplsvpn_nh_label_bind_cache *bmnc; + + if (!pi) + return; + + if (!CHECK_FLAG(pi->flags, BGP_PATH_MPLSVPN_NH_LABEL_BIND)) + return; + + bmnc = pi->mplsvpn.bmnc.nh_label_bind_cache; + + if (!bmnc) + return; + + LIST_REMOVE(pi, mplsvpn.bmnc.nh_label_bind_thread); + pi->mplsvpn.bmnc.nh_label_bind_cache->path_count--; + pi->mplsvpn.bmnc.nh_label_bind_cache = NULL; + SET_FLAG(pi->flags, BGP_PATH_MPLSVPN_NH_LABEL_BIND); + + if (LIST_EMPTY(&(bmnc->paths))) + bgp_mplsvpn_nh_label_bind_free(bmnc); +} + +void bgp_mplsvpn_nh_label_bind_register_local_label(struct bgp *bgp, + struct bgp_dest *dest, + struct bgp_path_info *pi) +{ + struct bgp_mplsvpn_nh_label_bind_cache *bmnc; + struct bgp_mplsvpn_nh_label_bind_cache_head *tree; + + tree = &bgp->mplsvpn_nh_label_bind; + bmnc = bgp_mplsvpn_nh_label_bind_find( + tree, &pi->nexthop->prefix, decode_label(&pi->extra->label[0])); + if (!bmnc) { + bmnc = bgp_mplsvpn_nh_label_bind_new( + tree, &pi->nexthop->prefix, + decode_label(&pi->extra->label[0])); + bmnc->bgp_vpn = bgp; + bmnc->allocation_in_progress = true; + bgp_lp_get(LP_TYPE_BGP_L3VPN_BIND, bmnc, + bgp_mplsvpn_nh_label_bind_get_local_label_cb); + } + + if (pi->mplsvpn.bmnc.nh_label_bind_cache == bmnc) + /* no change */ + return; + + bgp_mplsvpn_path_nh_label_bind_unlink(pi); + if (bmnc) { + /* updates NHT pi list reference */ + LIST_INSERT_HEAD(&(bmnc->paths), pi, + mplsvpn.bmnc.nh_label_bind_thread); + pi->mplsvpn.bmnc.nh_label_bind_cache = bmnc; + pi->mplsvpn.bmnc.nh_label_bind_cache->path_count++; + SET_FLAG(pi->flags, BGP_PATH_MPLSVPN_NH_LABEL_BIND); + bmnc->last_update = monotime(NULL); + } + + /* Add or update the selected nexthop */ +} diff --git a/bgpd/bgp_mplsvpn.h b/bgpd/bgp_mplsvpn.h index 5fe64ade48..132e96f0f2 100644 --- a/bgpd/bgp_mplsvpn.h +++ b/bgpd/bgp_mplsvpn.h @@ -325,9 +325,13 @@ extern void vpn_handle_router_id_update(struct bgp *bgp, bool withdraw, extern void bgp_vpn_leak_unimport(struct bgp *from_bgp); extern void bgp_vpn_leak_export(struct bgp *from_bgp); +extern bool bgp_mplsvpn_path_uses_valid_mpls_label(struct bgp_path_info *pi); extern int bgp_mplsvpn_nh_label_bind_cmp(const struct bgp_mplsvpn_nh_label_bind_cache *a, const struct bgp_mplsvpn_nh_label_bind_cache *b); +extern void bgp_mplsvpn_path_nh_label_bind_unlink(struct bgp_path_info *pi); +extern void bgp_mplsvpn_nh_label_bind_register_local_label( + struct bgp *bgp, struct bgp_dest *dest, struct bgp_path_info *pi); /* used to bind a local label to the (label, nexthop) values * from an incoming BGP mplsvpn update @@ -365,6 +369,8 @@ struct bgp_mplsvpn_nh_label_bind_cache { LIST_HEAD(mplsvpn_nh_label_bind_path_lists, bgp_path_info) paths; time_t last_update; + + bool allocation_in_progress; }; DECLARE_RBTREE_UNIQ(bgp_mplsvpn_nh_label_bind_cache, diff --git a/bgpd/bgp_nexthop.c b/bgpd/bgp_nexthop.c index c878512389..a854ca0fe4 100644 --- a/bgpd/bgp_nexthop.c +++ b/bgpd/bgp_nexthop.c @@ -121,6 +121,7 @@ static void bgp_nexthop_cache_reset(struct bgp_nexthop_cache_head *tree) struct bgp_path_info *path = LIST_FIRST(&(bnc->paths)); bgp_mplsvpn_path_nh_label_unlink(path); + bgp_mplsvpn_path_nh_label_bind_unlink(path); path_nh_map(path, bnc, false); } diff --git a/bgpd/bgp_nht.c b/bgpd/bgp_nht.c index df59e31d4a..4770434b62 100644 --- a/bgpd/bgp_nht.c +++ b/bgpd/bgp_nht.c @@ -151,6 +151,7 @@ void bgp_unlink_nexthop(struct bgp_path_info *path) struct bgp_nexthop_cache *bnc = path->nexthop; bgp_mplsvpn_path_nh_label_unlink(path); + bgp_mplsvpn_path_nh_label_bind_unlink(path); if (!bnc) return; diff --git a/bgpd/bgp_route.c b/bgpd/bgp_route.c index 72db9975fd..223bcfde54 100644 --- a/bgpd/bgp_route.c +++ b/bgpd/bgp_route.c @@ -3149,6 +3149,60 @@ static void bgp_lu_handle_label_allocation(struct bgp *bgp, } } +static struct interface * +bgp_label_get_resolved_nh_iface(const struct bgp_path_info *pi) +{ + struct nexthop *nh; + + if (pi->nexthop == NULL || pi->nexthop->nexthop == NULL || + !CHECK_FLAG(pi->nexthop->flags, BGP_NEXTHOP_VALID)) + /* next-hop is not valid */ + return NULL; + + nh = pi->nexthop->nexthop; + if (nh->ifindex == IFINDEX_INTERNAL && + nh->type != NEXTHOP_TYPE_IPV4_IFINDEX && + nh->type != NEXTHOP_TYPE_IPV6_IFINDEX) + /* next-hop does not contain valid interface */ + return NULL; + + return if_lookup_by_index(nh->ifindex, nh->vrf_id); +} + +static void +bgp_mplsvpn_handle_label_allocation(struct bgp *bgp, struct bgp_dest *dest, + struct bgp_path_info *new_select, + struct bgp_path_info *old_select, afi_t afi) +{ + struct interface *ifp; + struct bgp_interface *bgp_ifp; + + if (bgp->allocate_mpls_labels[afi][SAFI_MPLS_VPN] && new_select) { + ifp = bgp_label_get_resolved_nh_iface(new_select); + if (ifp) + bgp_ifp = (struct bgp_interface *)(ifp->info); + else + bgp_ifp = NULL; + if (bgp_ifp && + CHECK_FLAG(bgp_ifp->flags, + BGP_INTERFACE_MPLS_L3VPN_SWITCHING) && + bgp_mplsvpn_path_uses_valid_mpls_label(new_select) && + new_select->sub_type != BGP_ROUTE_IMPORTED && + new_select->sub_type != BGP_ROUTE_STATIC) + bgp_mplsvpn_nh_label_bind_register_local_label( + bgp, dest, new_select); + else + bgp_mplsvpn_path_nh_label_bind_unlink(new_select); + } else { + if (new_select) + /* no mpls vpn allocation */ + bgp_mplsvpn_path_nh_label_bind_unlink(new_select); + else if (old_select) + /* unlink old selection if any */ + bgp_mplsvpn_path_nh_label_bind_unlink(old_select); + } +} + /* * old_select = The old best path * new_select = the new best path @@ -3231,6 +3285,12 @@ static void bgp_process_main_one(struct bgp *bgp, struct bgp_dest *dest, */ bgp_lu_handle_label_allocation(bgp, dest, new_select, old_select, afi); + else if (safi == SAFI_MPLS_VPN) + /* mpls vpn path: + * Do we need to allocate or free labels? + */ + bgp_mplsvpn_handle_label_allocation(bgp, dest, new_select, + old_select, afi); if (debug) zlog_debug( diff --git a/bgpd/bgp_route.h b/bgpd/bgp_route.h index 96aa98c277..a67145a3ab 100644 --- a/bgpd/bgp_route.h +++ b/bgpd/bgp_route.h @@ -254,6 +254,14 @@ struct bgp_mplsvpn_label_nh { struct bgp_label_per_nexthop_cache *label_nexthop_cache; }; +struct bgp_mplsvpn_nh_label_bind { + /* For mplsvpn nexthop label bind linked list */ + LIST_ENTRY(bgp_path_info) nh_label_bind_thread; + + /* Back pointer to the bgp mplsvpn nexthop label bind structure */ + struct bgp_mplsvpn_nh_label_bind_cache *nh_label_bind_cache; +}; + struct bgp_path_info { /* For linked list. */ struct bgp_path_info *next; @@ -307,6 +315,7 @@ struct bgp_path_info { #define BGP_PATH_LINK_BW_CHG (1 << 15) #define BGP_PATH_ACCEPT_OWN (1 << 16) #define BGP_PATH_MPLSVPN_LABEL_NH (1 << 17) +#define BGP_PATH_MPLSVPN_NH_LABEL_BIND (1 << 18) /* BGP route type. This can be static, RIP, OSPF, BGP etc. */ uint8_t type; @@ -331,6 +340,7 @@ struct bgp_path_info { union { struct bgp_mplsvpn_label_nh blnc; + struct bgp_mplsvpn_nh_label_bind bmnc; } mplsvpn; }; diff --git a/bgpd/bgpd.c b/bgpd/bgpd.c index 22f48114d4..d4f42128e6 100644 --- a/bgpd/bgpd.c +++ b/bgpd/bgpd.c @@ -2319,6 +2319,7 @@ int peer_activate(struct peer *peer, afi_t afi, safi_t safi) struct listnode *node, *nnode; struct peer *tmp_peer; struct bgp *bgp; + safi_t safi_check; /* Nothing to do if we've already activated this peer */ if (peer->afc[afi][safi]) @@ -2349,16 +2350,22 @@ int peer_activate(struct peer *peer, afi_t afi, safi_t safi) } /* If this is the first peer to be activated for this - * afi/labeled-unicast recalc bestpaths to trigger label allocation */ - if (ret != BGP_ERR_PEER_SAFI_CONFLICT && safi == SAFI_LABELED_UNICAST - && !bgp->allocate_mpls_labels[afi][SAFI_UNICAST]) { + * afi/labeled-unicast or afi/mpls-vpn, recalc bestpaths to trigger + * label allocation */ + if (safi == SAFI_LABELED_UNICAST) + safi_check = SAFI_UNICAST; + else + safi_check = safi; + if (ret != BGP_ERR_PEER_SAFI_CONFLICT && + (safi == SAFI_LABELED_UNICAST || safi == SAFI_MPLS_VPN) && + !bgp->allocate_mpls_labels[afi][safi_check]) { if (BGP_DEBUG(zebra, ZEBRA)) zlog_debug( - "peer(s) are now active for labeled-unicast, allocate MPLS labels"); - - bgp->allocate_mpls_labels[afi][SAFI_UNICAST] = 1; - bgp_recalculate_afi_safi_bestpaths(bgp, afi, SAFI_UNICAST); + "peer(s) are now active for %s, allocate MPLS labels", + safi2str(safi)); + bgp->allocate_mpls_labels[afi][safi_check] = 1; + bgp_recalculate_afi_safi_bestpaths(bgp, afi, safi_check); } if (safi == SAFI_FLOWSPEC) { @@ -2424,6 +2431,7 @@ int peer_deactivate(struct peer *peer, afi_t afi, safi_t safi) struct peer *tmp_peer; struct listnode *node, *nnode; struct bgp *bgp; + safi_t safi_check; /* Nothing to do if we've already de-activated this peer */ if (!peer->afc[afi][safi]) @@ -2445,17 +2453,22 @@ int peer_deactivate(struct peer *peer, afi_t afi, safi_t safi) bgp = peer->bgp; /* If this is the last peer to be deactivated for this - * afi/labeled-unicast recalc bestpaths to trigger label deallocation */ - if (safi == SAFI_LABELED_UNICAST - && bgp->allocate_mpls_labels[afi][SAFI_UNICAST] - && !bgp_afi_safi_peer_exists(bgp, afi, safi)) { + * afi/labeled-unicast or afi/mpls-vpn, recalc bestpaths to trigger + * label deallocation */ + if (safi == SAFI_LABELED_UNICAST) + safi_check = SAFI_UNICAST; + else + safi_check = safi; + if ((safi == SAFI_LABELED_UNICAST || safi == SAFI_MPLS_VPN) && + bgp->allocate_mpls_labels[afi][safi_check] && + !bgp_afi_safi_peer_exists(bgp, afi, safi)) { if (BGP_DEBUG(zebra, ZEBRA)) zlog_debug( - "peer(s) are no longer active for labeled-unicast, deallocate MPLS labels"); - - bgp->allocate_mpls_labels[afi][SAFI_UNICAST] = 0; - bgp_recalculate_afi_safi_bestpaths(bgp, afi, SAFI_UNICAST); + "peer(s) are no longer active for %s, deallocate MPLS labels", + safi2str(safi)); + bgp->allocate_mpls_labels[afi][safi_check] = 0; + bgp_recalculate_afi_safi_bestpaths(bgp, afi, safi_check); } return ret; } -- 2.39.5