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(
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 */
+}
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
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,
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);
}
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;
}
}
+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
*/
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(
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;
#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;
union {
struct bgp_mplsvpn_label_nh blnc;
+ struct bgp_mplsvpn_nh_label_bind bmnc;
} mplsvpn;
};
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])
}
/* 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) {
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])
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;
}