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; }