]> git.puffer.fish Git - mirror/frr.git/commitdiff
bgpd: allocate label bound to received mpls vpn routes
authorPhilippe Guibert <philippe.guibert@6wind.com>
Thu, 11 May 2023 11:56:16 +0000 (13:56 +0200)
committerPhilippe Guibert <philippe.guibert@6wind.com>
Fri, 16 Jun 2023 08:54:58 +0000 (10:54 +0200)
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 <philippe.guibert@6wind.com>
bgpd/bgp_mplsvpn.c
bgpd/bgp_mplsvpn.h
bgpd/bgp_nexthop.c
bgpd/bgp_nht.c
bgpd/bgp_route.c
bgpd/bgp_route.h
bgpd/bgpd.c

index 4b8aa1ee702bd28ff6bb546835d8674406568288..cbb52c5f7f0749716c59baad5b9981a50016690e 100644 (file)
@@ -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 */
+}
index 5fe64ade486eb9d5a38a3722b042bf3258c64d48..132e96f0f26712c339dc6a34467fe0846237203e 100644 (file)
@@ -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,
index c878512389d7daadef849ca27c82ab0ba8f92ffb..a854ca0fe43b31b20f81db00f013a27d1cbfb43a 100644 (file)
@@ -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);
                }
index df59e31d4afce9495ac2b09cce97e3ae39fd2a5e..4770434b62fe69db394ebe3b6837394d1752fc7a 100644 (file)
@@ -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;
index 72db9975fdde9a86ddf59a3c585a3a26e5ecb52b..223bcfde541b1774ee38167c3f8a637ed5aaf5bb 100644 (file)
@@ -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(
index 96aa98c277fe7262f9b77b7a3ed18a7ad8f0c17c..a67145a3ab245f733de82420f32b7a9e77bcf470 100644 (file)
@@ -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;
 };
 
index 22f48114d42ffaa91efdf6756f0ed2144359b267..d4f42128e6a69fe0712f744028f5f1d57f4e629c 100644 (file)
@@ -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;
 }