summaryrefslogtreecommitdiffstats
path: root/bgpd/bgp_addpath.c
diff options
context:
space:
mode:
Diffstat (limited to 'bgpd/bgp_addpath.c')
-rw-r--r--bgpd/bgp_addpath.c486
1 files changed, 486 insertions, 0 deletions
diff --git a/bgpd/bgp_addpath.c b/bgpd/bgp_addpath.c
new file mode 100644
index 0000000..de4b4a4
--- /dev/null
+++ b/bgpd/bgp_addpath.c
@@ -0,0 +1,486 @@
+// SPDX-License-Identifier: GPL-2.0-or-later
+/*
+ * Addpath TX ID selection, and related utilities
+ * Copyright (C) 2018 Amazon.com, Inc. or its affiliates
+ */
+
+#ifdef HAVE_CONFIG_H
+#include "config.h"
+#endif
+
+#include "bgp_addpath.h"
+#include "bgp_route.h"
+
+static const struct bgp_addpath_strategy_names strat_names[BGP_ADDPATH_MAX] = {
+ {
+ .config_name = "addpath-tx-all-paths",
+ .human_name = "All",
+ .human_description = "Advertise all paths via addpath",
+ .type_json_name = "addpathTxAllPaths",
+ .id_json_name = "addpathTxIdAll"
+ },
+ {
+ .config_name = "addpath-tx-bestpath-per-AS",
+ .human_name = "Best-Per-AS",
+ .human_description = "Advertise bestpath per AS via addpath",
+ .type_json_name = "addpathTxBestpathPerAS",
+ .id_json_name = "addpathTxIdBestPerAS"
+ },
+ {
+ .config_name = "addpath-tx-best-selected",
+ .human_name = "Best-Selected",
+ .human_description = "Advertise best N selected paths via addpath",
+ .type_json_name = "addpathTxBestSelectedPaths",
+ .id_json_name = "addpathTxIdBestSelected"
+ },
+};
+
+static const struct bgp_addpath_strategy_names unknown_names = {
+ .config_name = "addpath-tx-unknown",
+ .human_name = "Unknown-Addpath-Strategy",
+ .human_description = "Unknown Addpath Strategy",
+ .type_json_name = "addpathTxUnknown",
+ .id_json_name = "addpathTxIdUnknown"
+};
+
+/*
+ * Returns a structure full of strings associated with an addpath type. Will
+ * never return null.
+ */
+const struct bgp_addpath_strategy_names *
+bgp_addpath_names(enum bgp_addpath_strat strat)
+{
+ if (strat < BGP_ADDPATH_MAX)
+ return &(strat_names[strat]);
+ else
+ return &unknown_names;
+};
+
+/*
+ * Returns if any peer is transmitting addpaths for a given afi/safi.
+ */
+bool bgp_addpath_is_addpath_used(struct bgp_addpath_bgp_data *d, afi_t afi,
+ safi_t safi)
+{
+ return d->total_peercount[afi][safi] > 0;
+}
+
+/*
+ * Initialize the BGP instance level data for addpath.
+ */
+void bgp_addpath_init_bgp_data(struct bgp_addpath_bgp_data *d)
+{
+ safi_t safi;
+ afi_t afi;
+ int i;
+
+ FOREACH_AFI_SAFI (afi, safi) {
+ for (i = 0; i < BGP_ADDPATH_MAX; i++) {
+ d->id_allocators[afi][safi][i] = NULL;
+ d->peercount[afi][safi][i] = 0;
+ }
+ d->total_peercount[afi][safi] = 0;
+ }
+}
+
+/*
+ * Free up resources associated with BGP route info structures.
+ */
+void bgp_addpath_free_info_data(struct bgp_addpath_info_data *d,
+ struct bgp_addpath_node_data *nd)
+{
+ int i;
+
+ for (i = 0; i < BGP_ADDPATH_MAX; i++) {
+ if (d->addpath_tx_id[i] != IDALLOC_INVALID)
+ idalloc_free_to_pool(&nd->free_ids[i],
+ d->addpath_tx_id[i]);
+ }
+}
+
+/*
+ * Return the addpath ID used to send a particular route, to a particular peer,
+ * in a particular AFI/SAFI.
+ */
+uint32_t bgp_addpath_id_for_peer(struct peer *peer, afi_t afi, safi_t safi,
+ struct bgp_addpath_info_data *d)
+{
+ if (safi == SAFI_LABELED_UNICAST)
+ safi = SAFI_UNICAST;
+
+ if (peer->addpath_type[afi][safi] < BGP_ADDPATH_MAX)
+ return d->addpath_tx_id[peer->addpath_type[afi][safi]];
+ else
+ return IDALLOC_INVALID;
+}
+
+/*
+ * Returns true if the path has an assigned addpath ID for any of the addpath
+ * strategies.
+ */
+bool bgp_addpath_info_has_ids(struct bgp_addpath_info_data *d)
+{
+ int i;
+
+ for (i = 0; i < BGP_ADDPATH_MAX; i++)
+ if (d->addpath_tx_id[i] != 0)
+ return true;
+
+ return false;
+}
+
+/*
+ * Releases any ID's associated with the BGP prefix.
+ */
+void bgp_addpath_free_node_data(struct bgp_addpath_bgp_data *bd,
+ struct bgp_addpath_node_data *nd, afi_t afi,
+ safi_t safi)
+{
+ int i;
+
+ for (i = 0; i < BGP_ADDPATH_MAX; i++) {
+ idalloc_drain_pool(bd->id_allocators[afi][safi][i],
+ &(nd->free_ids[i]));
+ }
+}
+
+/*
+ * Check to see if the addpath strategy requires DMED to be configured to work.
+ */
+bool bgp_addpath_dmed_required(int strategy)
+{
+ return strategy == BGP_ADDPATH_BEST_PER_AS;
+}
+
+/*
+ * Return true if this is a path we should advertise due to a
+ * configured addpath-tx knob
+ */
+bool bgp_addpath_tx_path(enum bgp_addpath_strat strat, struct bgp_path_info *pi)
+{
+ switch (strat) {
+ case BGP_ADDPATH_NONE:
+ return false;
+ case BGP_ADDPATH_ALL:
+ return true;
+ case BGP_ADDPATH_BEST_PER_AS:
+ if (CHECK_FLAG(pi->flags, BGP_PATH_DMED_SELECTED))
+ return true;
+ else
+ return false;
+ case BGP_ADDPATH_BEST_SELECTED:
+ return true;
+ case BGP_ADDPATH_MAX:
+ return false;
+ }
+
+ assert(!"Reached end of function we should never hit");
+}
+
+static void bgp_addpath_flush_type_rn(struct bgp *bgp, afi_t afi, safi_t safi,
+ enum bgp_addpath_strat addpath_type,
+ struct bgp_dest *dest)
+{
+ struct bgp_path_info *pi;
+
+ if (safi == SAFI_LABELED_UNICAST)
+ safi = SAFI_UNICAST;
+
+ idalloc_drain_pool(
+ bgp->tx_addpath.id_allocators[afi][safi][addpath_type],
+ &(dest->tx_addpath.free_ids[addpath_type]));
+ for (pi = bgp_dest_get_bgp_path_info(dest); pi; pi = pi->next) {
+ if (pi->tx_addpath.addpath_tx_id[addpath_type]
+ != IDALLOC_INVALID) {
+ idalloc_free(
+ bgp->tx_addpath
+ .id_allocators[afi][safi][addpath_type],
+ pi->tx_addpath.addpath_tx_id[addpath_type]);
+ pi->tx_addpath.addpath_tx_id[addpath_type] =
+ IDALLOC_INVALID;
+ }
+ }
+}
+
+/*
+ * Purge all addpath ID's on a BGP instance associated with the addpath
+ * strategy, and afi/safi combination. This lets us let go of all memory held to
+ * track ID numbers associated with an addpath type not in use. Since
+ * post-bestpath ID processing is skipped for types not used, this is the only
+ * chance to free this data.
+ */
+static void bgp_addpath_flush_type(struct bgp *bgp, afi_t afi, safi_t safi,
+ enum bgp_addpath_strat addpath_type)
+{
+ struct bgp_dest *dest, *ndest;
+
+ if (safi == SAFI_LABELED_UNICAST)
+ safi = SAFI_UNICAST;
+
+ for (dest = bgp_table_top(bgp->rib[afi][safi]); dest;
+ dest = bgp_route_next(dest)) {
+ if (safi == SAFI_MPLS_VPN) {
+ struct bgp_table *table;
+
+ table = bgp_dest_get_bgp_table_info(dest);
+ if (!table)
+ continue;
+
+ for (ndest = bgp_table_top(table); ndest;
+ ndest = bgp_route_next(ndest))
+ bgp_addpath_flush_type_rn(bgp, afi, safi,
+ addpath_type, ndest);
+ } else {
+ bgp_addpath_flush_type_rn(bgp, afi, safi, addpath_type,
+ dest);
+ }
+ }
+
+ idalloc_destroy(bgp->tx_addpath.id_allocators[afi][safi][addpath_type]);
+ bgp->tx_addpath.id_allocators[afi][safi][addpath_type] = NULL;
+}
+
+/*
+ * Allocate an Addpath ID for the given type on a path, if necessary.
+ */
+static void bgp_addpath_populate_path(struct id_alloc *allocator,
+ struct bgp_path_info *path,
+ enum bgp_addpath_strat addpath_type)
+{
+ if (bgp_addpath_tx_path(addpath_type, path)) {
+ path->tx_addpath.addpath_tx_id[addpath_type] =
+ idalloc_allocate(allocator);
+ }
+}
+
+/*
+ * Compute addpath ID's on a BGP instance associated with the addpath strategy,
+ * and afi/safi combination. Since we won't waste the time computing addpath IDs
+ * for unused strategies, the first time a peer is configured to use a strategy,
+ * we have to backfill the data.
+ * In labeled-unicast, addpath allocations SHOULD be done in unicast SAFI.
+ */
+static void bgp_addpath_populate_type(struct bgp *bgp, afi_t afi, safi_t safi,
+ enum bgp_addpath_strat addpath_type)
+{
+ struct bgp_dest *dest, *ndest;
+ char buf[200];
+ struct id_alloc *allocator;
+
+ if (safi == SAFI_LABELED_UNICAST)
+ safi = SAFI_UNICAST;
+
+ snprintf(buf, sizeof(buf), "Addpath ID Allocator %s:%d/%d",
+ bgp_addpath_names(addpath_type)->config_name, (int)afi,
+ (int)safi);
+ buf[sizeof(buf) - 1] = '\0';
+ zlog_info("Computing addpath IDs for addpath type %s",
+ bgp_addpath_names(addpath_type)->human_name);
+
+ bgp->tx_addpath.id_allocators[afi][safi][addpath_type] =
+ idalloc_new(buf);
+
+ idalloc_reserve(bgp->tx_addpath.id_allocators[afi][safi][addpath_type],
+ BGP_ADDPATH_TX_ID_FOR_DEFAULT_ORIGINATE);
+
+ allocator = bgp->tx_addpath.id_allocators[afi][safi][addpath_type];
+
+ for (dest = bgp_table_top(bgp->rib[afi][safi]); dest;
+ dest = bgp_route_next(dest)) {
+ struct bgp_path_info *bi;
+
+ if (safi == SAFI_MPLS_VPN) {
+ struct bgp_table *table;
+
+ table = bgp_dest_get_bgp_table_info(dest);
+ if (!table)
+ continue;
+
+ for (ndest = bgp_table_top(table); ndest;
+ ndest = bgp_route_next(ndest))
+ for (bi = bgp_dest_get_bgp_path_info(ndest); bi;
+ bi = bi->next)
+ bgp_addpath_populate_path(allocator, bi,
+ addpath_type);
+ } else {
+ for (bi = bgp_dest_get_bgp_path_info(dest); bi;
+ bi = bi->next)
+ bgp_addpath_populate_path(allocator, bi,
+ addpath_type);
+ }
+ }
+}
+
+/*
+ * Handle updates to a peer or group's addpath strategy. If after adjusting
+ * counts a addpath strategy is in use for the first time, or no longer in use,
+ * the IDs for that strategy will be populated or flushed.
+ */
+void bgp_addpath_type_changed(struct bgp *bgp)
+{
+ afi_t afi;
+ safi_t safi;
+ struct listnode *node, *nnode;
+ struct peer *peer;
+ int peer_count[AFI_MAX][SAFI_MAX][BGP_ADDPATH_MAX];
+ enum bgp_addpath_strat type;
+
+ FOREACH_AFI_SAFI(afi, safi) {
+ for (type=0; type<BGP_ADDPATH_MAX; type++) {
+ peer_count[afi][safi][type] = 0;
+ }
+ bgp->tx_addpath.total_peercount[afi][safi] = 0;
+ }
+
+ for (ALL_LIST_ELEMENTS(bgp->peer, node, nnode, peer)) {
+ FOREACH_AFI_SAFI(afi, safi) {
+ type = peer->addpath_type[afi][safi];
+ if (type != BGP_ADDPATH_NONE) {
+ peer_count[afi][safi][type] += 1;
+ bgp->tx_addpath.total_peercount[afi][safi] += 1;
+ }
+ }
+ }
+
+ FOREACH_AFI_SAFI(afi, safi) {
+ for (type=0; type<BGP_ADDPATH_MAX; type++) {
+ int old = bgp->tx_addpath.peercount[afi][safi][type];
+ int new = peer_count[afi][safi][type];
+
+ bgp->tx_addpath.peercount[afi][safi][type] = new;
+
+ if (old == 0 && new != 0) {
+ bgp_addpath_populate_type(bgp, afi, safi,
+ type);
+ } else if (old != 0 && new == 0) {
+ bgp_addpath_flush_type(bgp, afi, safi, type);
+ }
+ }
+ }
+}
+
+/*
+ * Change the addpath type assigned to a peer, or peer group. In addition to
+ * adjusting the counts, peer sessions will be reset as needed to make the
+ * change take effect.
+ */
+void bgp_addpath_set_peer_type(struct peer *peer, afi_t afi, safi_t safi,
+ enum bgp_addpath_strat addpath_type,
+ uint8_t paths)
+{
+ struct bgp *bgp = peer->bgp;
+ enum bgp_addpath_strat old_type;
+ struct listnode *node, *nnode;
+ struct peer *tmp_peer;
+ struct peer_group *group;
+
+ if (safi == SAFI_LABELED_UNICAST)
+ safi = SAFI_UNICAST;
+
+ peer->addpath_best_selected[afi][safi] = paths;
+
+ old_type = peer->addpath_type[afi][safi];
+ if (addpath_type == old_type)
+ return;
+
+ if (addpath_type == BGP_ADDPATH_NONE && peer->group &&
+ !CHECK_FLAG(peer->sflags, PEER_STATUS_GROUP)) {
+ /* A "no" config on a group member inherits group */
+ addpath_type = peer->group->conf->addpath_type[afi][safi];
+ }
+
+ peer->addpath_type[afi][safi] = addpath_type;
+
+ bgp_addpath_type_changed(bgp);
+
+ if (addpath_type != BGP_ADDPATH_NONE) {
+ if (bgp_addpath_dmed_required(addpath_type)) {
+ if (!CHECK_FLAG(bgp->flags,
+ BGP_FLAG_DETERMINISTIC_MED)) {
+ zlog_warn(
+ "%s: enabling bgp deterministic-med, this is required for addpath-tx-bestpath-per-AS",
+ peer->host);
+ SET_FLAG(bgp->flags,
+ BGP_FLAG_DETERMINISTIC_MED);
+ bgp_recalculate_all_bestpaths(bgp);
+ }
+ }
+ }
+
+ zlog_info("Resetting peer %s%pBP due to change in addpath config",
+ CHECK_FLAG(peer->sflags, PEER_STATUS_GROUP) ? "group " : "",
+ peer);
+
+ if (CHECK_FLAG(peer->sflags, PEER_STATUS_GROUP)) {
+ group = peer->group;
+
+ /* group will be null as peer_group_delete calls peer_delete on
+ * group->conf. That peer_delete will eventuallly end up here
+ * if the group was configured to tx addpaths.
+ */
+ if (group != NULL) {
+ for (ALL_LIST_ELEMENTS(group->peer, node, nnode,
+ tmp_peer)) {
+ if (tmp_peer->addpath_type[afi][safi] ==
+ old_type) {
+ bgp_addpath_set_peer_type(
+ tmp_peer, afi, safi,
+ addpath_type, paths);
+ }
+ }
+ }
+ } else {
+ peer_change_action(peer, afi, safi, peer_change_reset);
+ }
+
+}
+
+/*
+ * Intended to run after bestpath. This function will take TX IDs from paths
+ * that no longer need them, and give them to paths that do. This prevents
+ * best-per-as updates from needing to do a separate withdraw and update just to
+ * swap out which path is sent.
+ */
+void bgp_addpath_update_ids(struct bgp *bgp, struct bgp_dest *bn, afi_t afi,
+ safi_t safi)
+{
+ int i;
+ struct bgp_path_info *pi;
+ struct id_alloc_pool **pool_ptr;
+
+ if (safi == SAFI_LABELED_UNICAST)
+ safi = SAFI_UNICAST;
+
+ for (i = 0; i < BGP_ADDPATH_MAX; i++) {
+ struct id_alloc *alloc =
+ bgp->tx_addpath.id_allocators[afi][safi][i];
+ pool_ptr = &(bn->tx_addpath.free_ids[i]);
+
+ if (bgp->tx_addpath.peercount[afi][safi][i] == 0)
+ continue;
+
+ /* Free Unused IDs back to the pool.*/
+ for (pi = bgp_dest_get_bgp_path_info(bn); pi; pi = pi->next) {
+ if (pi->tx_addpath.addpath_tx_id[i] != IDALLOC_INVALID
+ && !bgp_addpath_tx_path(i, pi)) {
+ idalloc_free_to_pool(pool_ptr,
+ pi->tx_addpath.addpath_tx_id[i]);
+ pi->tx_addpath.addpath_tx_id[i] =
+ IDALLOC_INVALID;
+ }
+ }
+
+ /* Give IDs to paths that need them (pulling from the pool) */
+ for (pi = bgp_dest_get_bgp_path_info(bn); pi; pi = pi->next) {
+ if (pi->tx_addpath.addpath_tx_id[i] == IDALLOC_INVALID
+ && bgp_addpath_tx_path(i, pi)) {
+ pi->tx_addpath.addpath_tx_id[i] =
+ idalloc_allocate_prefer_pool(
+ alloc, pool_ptr);
+ }
+ }
+
+ /* Free any IDs left in the pool to the main allocator */
+ idalloc_drain_pool(alloc, pool_ptr);
+ }
+}