summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--bgpd/bgp_addpath.c422
-rw-r--r--bgpd/bgp_addpath.h72
-rw-r--r--bgpd/bgp_addpath_types.h55
-rw-r--r--bgpd/bgp_evpn.c5
-rw-r--r--bgpd/bgp_open.c5
-rw-r--r--bgpd/bgp_route.c69
-rw-r--r--bgpd/bgp_route.h3
-rw-r--r--bgpd/bgp_table.c10
-rw-r--r--bgpd/bgp_table.h3
-rw-r--r--bgpd/bgp_updgrp.c24
-rw-r--r--bgpd/bgp_updgrp.h5
-rw-r--r--bgpd/bgp_updgrp_adv.c97
-rw-r--r--bgpd/bgp_updgrp_packet.c1
-rw-r--r--bgpd/bgp_vty.c85
-rw-r--r--bgpd/bgpd.c123
-rw-r--r--bgpd/bgpd.h18
-rw-r--r--bgpd/subdir.am3
-rw-r--r--lib/id_alloc.c406
-rw-r--r--lib/id_alloc.h90
-rw-r--r--lib/lib_errors.h2
-rw-r--r--lib/subdir.am2
-rw-r--r--tests/.gitignore7
-rw-r--r--tests/bgpd/test_bgp_table.c2
-rw-r--r--tests/bgpd/test_peer_attr.c13
-rw-r--r--tests/bgpd/test_peer_attr.py12
-rw-r--r--tests/lib/test_idalloc.c193
-rw-r--r--tests/lib/test_idalloc.py6
-rw-r--r--tests/subdir.am4
28 files changed, 1546 insertions, 191 deletions
diff --git a/bgpd/bgp_addpath.c b/bgpd/bgp_addpath.c
new file mode 100644
index 0000000000..22401f0017
--- /dev/null
+++ b/bgpd/bgp_addpath.c
@@ -0,0 +1,422 @@
+/*
+ * Addpath TX ID selection, and related utilities
+ * Copyright (C) 2018 Amazon.com, Inc. or its affiliates
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License as published by the Free
+ * Software Foundation; either version 2 of the License, or (at your option)
+ * any later version.
+ *
+ * This program is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
+ * more details.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program; see the file COPYING; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
+ */
+
+#include "bgp_addpath.h"
+#include "bgp_route.h"
+
+static 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"
+ }
+};
+
+static 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.
+ */
+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.
+ */
+int 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;
+
+ for (afi = AFI_IP; afi < AFI_MAX; afi++) {
+ for (safi = SAFI_UNICAST; safi < SAFI_MAX; 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 (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.
+ */
+int 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 1;
+
+ return 0;
+}
+
+/*
+ * 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.
+ */
+int 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
+ */
+int bgp_addpath_tx_path(enum bgp_addpath_strat strat,
+ struct bgp_path_info *pi)
+{
+ switch (strat) {
+ case BGP_ADDPATH_NONE:
+ return 0;
+ case BGP_ADDPATH_ALL:
+ return 1;
+ case BGP_ADDPATH_BEST_PER_AS:
+ if (CHECK_FLAG(pi->flags, BGP_PATH_DMED_SELECTED))
+ return 1;
+ else
+ return 0;
+ default:
+ return 0;
+ }
+}
+
+/*
+ * 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_node *rn;
+ struct bgp_path_info *pi;
+
+ for (rn = bgp_table_top(bgp->rib[afi][safi]); rn;
+ rn = bgp_route_next(rn)) {
+ idalloc_drain_pool(
+ bgp->tx_addpath.id_allocators[afi][safi][addpath_type],
+ &(rn->tx_addpath.free_ids[addpath_type]));
+ for (pi = rn->info; 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;
+ }
+ }
+ }
+
+ 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.
+ */
+static void bgp_addpath_populate_type(struct bgp *bgp, afi_t afi, safi_t safi,
+ enum bgp_addpath_strat addpath_type)
+{
+ struct bgp_node *rn;
+ struct bgp_path_info *bi;
+ char buf[200];
+ struct id_alloc *allocator;
+
+ 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 (rn = bgp_table_top(bgp->rib[afi][safi]); rn;
+ rn = bgp_route_next(rn))
+ for (bi = rn->info; 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;
+ }
+ }
+
+ 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;
+ }
+ }
+ }
+
+ 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)
+{
+ struct bgp *bgp = peer->bgp;
+ enum bgp_addpath_strat old_type = peer->addpath_type[afi][safi];
+ struct listnode *node, *nnode;
+ struct peer *tmp_peer;
+ struct peer_group *group;
+
+ 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 (!bgp_flag_check(bgp, BGP_FLAG_DETERMINISTIC_MED)) {
+ zlog_warn(
+ "%s: enabling bgp deterministic-med, this is required for addpath-tx-bestpath-per-AS",
+ peer->host);
+ bgp_flag_set(bgp, BGP_FLAG_DETERMINISTIC_MED);
+ bgp_recalculate_all_bestpaths(bgp);
+ }
+ }
+ }
+
+ zlog_info("Resetting peer %s%s due to change in addpath config\n",
+ CHECK_FLAG(peer->sflags, PEER_STATUS_GROUP) ? "group " : "",
+ peer->host);
+
+ 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);
+ }
+ }
+ }
+ } 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_node *bn, afi_t afi,
+ safi_t safi)
+{
+ int i;
+ struct bgp_path_info *pi;
+ struct id_alloc_pool **pool_ptr;
+
+ 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 = bn->info; 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 = bn->info; 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);
+ }
+}
diff --git a/bgpd/bgp_addpath.h b/bgpd/bgp_addpath.h
new file mode 100644
index 0000000000..c0c182791b
--- /dev/null
+++ b/bgpd/bgp_addpath.h
@@ -0,0 +1,72 @@
+/*
+ * Addpath TX ID selection, and related utilities
+ * Copyright (C) 2018 Amazon.com, Inc. or its affiliates
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License as published by the Free
+ * Software Foundation; either version 2 of the License, or (at your option)
+ * any later version.
+ *
+ * This program is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
+ * more details.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program; see the file COPYING; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
+ */
+
+#ifndef _QUAGGA_BGPD_TX_ADDPATH_H
+#define _QUAGGA_BGPD_TX_ADDPATH_H
+
+#include <stdint.h>
+#include <zebra.h>
+
+#include "bgpd/bgp_addpath_types.h"
+#include "bgpd/bgp_route.h"
+#include "bgpd/bgp_table.h"
+#include "lib/json.h"
+
+#define BGP_ADDPATH_TX_ID_FOR_DEFAULT_ORIGINATE 1
+
+void bgp_addpath_init_bgp_data(struct bgp_addpath_bgp_data *d);
+
+int bgp_addpath_is_addpath_used(struct bgp_addpath_bgp_data *d, afi_t afi,
+ safi_t safi);
+
+void bgp_addpath_free_node_data(struct bgp_addpath_bgp_data *bd,
+ struct bgp_addpath_node_data *nd,
+ afi_t afi, safi_t safi);
+
+void bgp_addpath_free_info_data(struct bgp_addpath_info_data *d,
+ struct bgp_addpath_node_data *nd);
+
+
+int bgp_addpath_info_has_ids(struct bgp_addpath_info_data *d);
+
+uint32_t bgp_addpath_id_for_peer(struct peer *peer, afi_t afi, safi_t safi,
+ struct bgp_addpath_info_data *d);
+
+struct bgp_addpath_strategy_names *
+bgp_addpath_names(enum bgp_addpath_strat strat);
+
+int bgp_addpath_dmed_required(int strategy);
+
+/*
+ * Return true if this is a path we should advertise due to a configured
+ * addpath-tx knob
+ */
+int bgp_addpath_tx_path(enum bgp_addpath_strat strat,
+ struct bgp_path_info *pi);
+/*
+ * Change the type of addpath used for a peer.
+ */
+void bgp_addpath_set_peer_type(struct peer *peer, afi_t afi, safi_t safi,
+ enum bgp_addpath_strat addpath_type);
+
+void bgp_addpath_update_ids(struct bgp *bgp, struct bgp_node *bn, afi_t afi,
+ safi_t safi);
+
+void bgp_addpath_type_changed(struct bgp *bgp);
+#endif
diff --git a/bgpd/bgp_addpath_types.h b/bgpd/bgp_addpath_types.h
new file mode 100644
index 0000000000..b0b3302979
--- /dev/null
+++ b/bgpd/bgp_addpath_types.h
@@ -0,0 +1,55 @@
+/*
+ * Addpath TX ID selection, and related utilities
+ * Copyright (C) 2018 Amazon.com, Inc. or its affiliates
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License as published by the Free
+ * Software Foundation; either version 2 of the License, or (at your option)
+ * any later version.
+ *
+ * This program is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
+ * more details.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program; see the file COPYING; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
+ */
+
+#ifndef _QUAGGA_BGPD_TX_ADDPATH_DATA_H
+#define _QUAGGA_BGPD_TX_ADDPATH_DATA_H
+#include "lib/id_alloc.h"
+#include <stdint.h>
+
+enum bgp_addpath_strat {
+ BGP_ADDPATH_ALL = 0,
+ BGP_ADDPATH_BEST_PER_AS,
+ BGP_ADDPATH_MAX,
+ BGP_ADDPATH_NONE,
+};
+
+/* TX Addpath structures */
+struct bgp_addpath_bgp_data {
+ unsigned int peercount[AFI_MAX][SAFI_MAX][BGP_ADDPATH_MAX];
+ unsigned int total_peercount[AFI_MAX][SAFI_MAX];
+ struct id_alloc *id_allocators[AFI_MAX][SAFI_MAX][BGP_ADDPATH_MAX];
+};
+
+struct bgp_addpath_node_data {
+ struct id_alloc_pool *free_ids[BGP_ADDPATH_MAX];
+};
+
+struct bgp_addpath_info_data {
+ uint32_t addpath_tx_id[BGP_ADDPATH_MAX];
+};
+
+struct bgp_addpath_strategy_names {
+ const char *config_name;
+ const char *human_name; /* path detail non-json */
+ const char *human_description; /* non-json peer descriptions */
+ const char *type_json_name; /* json peer listings */
+ const char *id_json_name; /* path json output for tx ID# */
+};
+
+#endif
diff --git a/bgpd/bgp_evpn.c b/bgpd/bgp_evpn.c
index cff050a9ef..fc3ac28723 100644
--- a/bgpd/bgp_evpn.c
+++ b/bgpd/bgp_evpn.c
@@ -47,6 +47,7 @@
#include "bgpd/bgp_aspath.h"
#include "bgpd/bgp_zebra.h"
#include "bgpd/bgp_nexthop.h"
+#include "bgpd/bgp_addpath.h"
/*
* Definitions and external declarations.
@@ -1059,7 +1060,7 @@ static int evpn_es_route_select_install(struct bgp *bgp,
&& old_select->sub_type == BGP_ROUTE_IMPORTED
&& !CHECK_FLAG(rn->flags, BGP_NODE_USER_CLEAR)
&& !CHECK_FLAG(old_select->flags, BGP_PATH_ATTR_CHANGED)
- && !bgp->addpath_tx_used[afi][safi]) {
+ && !bgp_addpath_is_addpath_used(&bgp->tx_addpath, afi, safi)) {
if (bgp_zebra_has_route_changed(rn, old_select)) {
ret = evpn_es_install_vtep(bgp, es,
(struct prefix_evpn *)&rn->p,
@@ -1142,7 +1143,7 @@ static int evpn_route_select_install(struct bgp *bgp, struct bgpevpn *vpn,
&& old_select->sub_type == BGP_ROUTE_IMPORTED
&& !CHECK_FLAG(rn->flags, BGP_NODE_USER_CLEAR)
&& !CHECK_FLAG(old_select->flags, BGP_PATH_ATTR_CHANGED)
- && !bgp->addpath_tx_used[afi][safi]) {
+ && !bgp_addpath_is_addpath_used(&bgp->tx_addpath, afi, safi)) {
if (bgp_zebra_has_route_changed(rn, old_select)) {
if (old_select->attr->sticky)
SET_FLAG(flags, ZEBRA_MACIP_TYPE_STICKY);
diff --git a/bgpd/bgp_open.c b/bgpd/bgp_open.c
index cf5901df5a..3018124f45 100644
--- a/bgpd/bgp_open.c
+++ b/bgpd/bgp_open.c
@@ -1387,10 +1387,7 @@ void bgp_open_capability(struct stream *s, struct peer *peer)
/* Only advertise addpath TX if a feature that
* will use it is
* configured */
- if (CHECK_FLAG(peer->af_flags[afi][safi],
- PEER_FLAG_ADDPATH_TX_ALL_PATHS)
- || CHECK_FLAG(peer->af_flags[afi][safi],
- PEER_FLAG_ADDPATH_TX_BESTPATH_PER_AS))
+ if (peer->addpath_type[afi][safi] != BGP_ADDPATH_NONE)
adv_addpath_tx = 1;
}
}
diff --git a/bgpd/bgp_route.c b/bgpd/bgp_route.c
index 8ae74a008c..bb8c5d8f6e 100644
--- a/bgpd/bgp_route.c
+++ b/bgpd/bgp_route.c
@@ -65,6 +65,7 @@
#include "bgpd/bgp_nht.h"
#include "bgpd/bgp_updgrp.h"
#include "bgpd/bgp_label.h"
+#include "bgpd/bgp_addpath.h"
#if ENABLE_BGP_VNC
#include "bgpd/rfapi/rfapi_backend.h"
@@ -247,6 +248,8 @@ static void bgp_path_info_free(struct bgp_path_info *path)
bgp_unlink_nexthop(path);
bgp_path_info_extra_free(&path->extra);
bgp_path_info_mpath_free(&path->mpath);
+ bgp_addpath_free_info_data(&path->tx_addpath,
+ path->net ? &path->net->tx_addpath : NULL);
peer_unlock(path->peer); /* bgp_path_info peer reference */
@@ -1472,7 +1475,7 @@ int subgroup_announce_check(struct bgp_node *rn, struct bgp_path_info *pi,
* addpath
* feature that requires us to advertise it */
if (!CHECK_FLAG(pi->flags, BGP_PATH_SELECTED)) {
- if (!bgp_addpath_tx_path(peer, afi, safi, pi)) {
+ if (!bgp_addpath_tx_path(peer->addpath_type[afi][safi], pi)) {
return 0;
}
}
@@ -2078,6 +2081,8 @@ void bgp_best_selection(struct bgp *bgp, struct bgp_node *rn,
bgp_path_info_mpath_aggregate_update(new_select, old_select);
bgp_mp_list_clear(&mp_list);
+ bgp_addpath_update_ids(bgp, rn, afi, safi);
+
result->old = old_select;
result->new = new_select;
@@ -2127,7 +2132,7 @@ int subgroup_process_announce_selected(struct update_subgroup *subgrp,
bgp_adj_out_set_subgroup(rn, subgrp, &attr, selected);
else
bgp_adj_out_unset_subgroup(rn, subgrp, 1,
- selected->addpath_tx_id);
+ addpath_tx_id);
}
/* If selected is NULL we must withdraw the path using addpath_tx_id */
@@ -2303,7 +2308,7 @@ static void bgp_process_main_one(struct bgp *bgp, struct bgp_node *rn,
if (old_select && old_select == new_select
&& !CHECK_FLAG(rn->flags, BGP_NODE_USER_CLEAR)
&& !CHECK_FLAG(old_select->flags, BGP_PATH_ATTR_CHANGED)
- && !bgp->addpath_tx_used[afi][safi]) {
+ && !bgp_addpath_is_addpath_used(&bgp->tx_addpath, afi, safi)) {
if (bgp_zebra_has_route_changed(rn, old_select)) {
#if ENABLE_BGP_VNC
vnc_import_bgp_add_route(bgp, p, old_select);
@@ -2776,7 +2781,6 @@ struct bgp_path_info *info_make(int type, int sub_type, unsigned short instance,
new->attr = attr;
new->uptime = bgp_clock();
new->net = rn;
- new->addpath_tx_id = ++peer->bgp->addpath_tx_id;
return new;
}
@@ -7486,6 +7490,18 @@ static void route_vty_out_advertised_to(struct vty *vty, struct peer *peer,
}
}
+static void route_vty_out_tx_ids(struct vty *vty,
+ struct bgp_addpath_info_data *d)
+{
+ int i;
+
+ for (i = 0; i < BGP_ADDPATH_MAX; i++) {
+ vty_out(vty, "TX-%s %u%s", bgp_addpath_names(i)->human_name,
+ d->addpath_tx_id[i],
+ i < BGP_ADDPATH_MAX - 1 ? " " : "\n");
+ }
+}
+
void route_vty_out_detail(struct vty *vty, struct bgp *bgp, struct prefix *p,
struct bgp_path_info *path, afi_t afi, safi_t safi,
json_object *json_paths)
@@ -7517,6 +7533,7 @@ void route_vty_out_detail(struct vty *vty, struct bgp *bgp, struct prefix *p,
unsigned int first_as;
bool nexthop_self =
CHECK_FLAG(path->flags, BGP_PATH_ANNC_NH_SELF) ? true : false;
+ int i;
if (json_paths) {
json_path = json_object_new_object();
@@ -8228,29 +8245,53 @@ void route_vty_out_detail(struct vty *vty, struct bgp *bgp, struct prefix *p,
}
/* Line 8 display Addpath IDs */
- if (path->addpath_rx_id || path->addpath_tx_id) {
+ if (path->addpath_rx_id
+ || bgp_addpath_info_has_ids(&path->tx_addpath)) {
if (json_paths) {
json_object_int_add(json_path, "addpathRxId",
path->addpath_rx_id);
- json_object_int_add(json_path, "addpathTxId",
- path->addpath_tx_id);
+
+ /* Keep backwards compatibility with the old API
+ * by putting TX All's ID in the old field
+ */
+ json_object_int_add(
+ json_path, "addpathTxId",
+ path->tx_addpath.addpath_tx_id
+ [BGP_ADDPATH_ALL]);
+
+ /* ... but create a specific field for each
+ * strategy
+ */
+ for (i = 0; i < BGP_ADDPATH_MAX; i++) {
+ json_object_int_add(
+ json_path,
+ bgp_addpath_names(i)
+ ->id_json_name,
+ path->tx_addpath
+ .addpath_tx_id[i]);
+ }
} else {
- vty_out(vty, " AddPath ID: RX %u, TX %u\n",
- path->addpath_rx_id,
- path->addpath_tx_id);
+ vty_out(vty, " AddPath ID: RX %u, ",
+ path->addpath_rx_id);
+
+ route_vty_out_tx_ids(vty, &path->tx_addpath);
}
}
/* If we used addpath to TX a non-bestpath we need to display
- * "Advertised to" on a path-by-path basis */
- if (bgp->addpath_tx_used[afi][safi]) {
+ * "Advertised to" on a path-by-path basis
+ */
+ if (bgp_addpath_is_addpath_used(&bgp->tx_addpath, afi, safi)) {
first = 1;
for (ALL_LIST_ELEMENTS(bgp->peer, node, nnode, peer)) {
addpath_capable =
bgp_addpath_encode_tx(peer, afi, safi);
has_adj = bgp_adj_out_lookup(
- peer, path->net, path->addpath_tx_id);
+ peer, path->net,
+ bgp_addpath_id_for_peer(
+ peer, afi, safi,
+ &path->tx_addpath));
if ((addpath_capable && has_adj)
|| (!addpath_capable && has_adj
@@ -8958,7 +8999,7 @@ void route_vty_out_detail_header(struct vty *vty, struct bgp *bgp,
* show what peers we advertised the bestpath to. If we are using
* addpath
* though then we must display Advertised to on a path-by-path basis. */
- if (!bgp->addpath_tx_used[afi][safi]) {
+ if (!bgp_addpath_is_addpath_used(&bgp->tx_addpath, afi, safi)) {
for (ALL_LIST_ELEMENTS(bgp->peer, node, nnode, peer)) {
if (bgp_adj_out_lookup(peer, rn, 0)) {
if (json && !json_adv_to)
diff --git a/bgpd/bgp_route.h b/bgpd/bgp_route.h
index f0edc8d49a..466d52ed64 100644
--- a/bgpd/bgp_route.h
+++ b/bgpd/bgp_route.h
@@ -24,6 +24,7 @@
#include "queue.h"
#include "nexthop.h"
#include "bgp_table.h"
+#include "bgp_addpath_types.h"
struct bgp_nexthop_cache;
struct bgp_route_evpn;
@@ -220,7 +221,7 @@ struct bgp_path_info {
/* Addpath identifiers */
uint32_t addpath_rx_id;
- uint32_t addpath_tx_id;
+ struct bgp_addpath_info_data tx_addpath;
};
/* Structure used in BGP path selection */
diff --git a/bgpd/bgp_table.c b/bgpd/bgp_table.c
index 231f326cef..728eeaa3a9 100644
--- a/bgpd/bgp_table.c
+++ b/bgpd/bgp_table.c
@@ -29,6 +29,7 @@
#include "bgpd/bgpd.h"
#include "bgpd/bgp_table.h"
+#include "bgp_addpath.h"
void bgp_table_lock(struct bgp_table *rt)
{
@@ -76,7 +77,16 @@ static void bgp_node_destroy(route_table_delegate_t *delegate,
struct route_table *table, struct route_node *node)
{
struct bgp_node *bgp_node;
+ struct bgp_table *rt;
bgp_node = bgp_node_from_rnode(node);
+ rt = table->info;
+
+ if (rt->bgp) {
+ bgp_addpath_free_node_data(&rt->bgp->tx_addpath,
+ &bgp_node->tx_addpath,
+ rt->afi, rt->safi);
+ }
+
XFREE(MTYPE_BGP_NODE, bgp_node);
}
diff --git a/bgpd/bgp_table.h b/bgpd/bgp_table.h
index 60a26b76cc..c267b4fe8a 100644
--- a/bgpd/bgp_table.h
+++ b/bgpd/bgp_table.h
@@ -25,6 +25,7 @@
#include "table.h"
#include "queue.h"
#include "linklist.h"
+#include "bgpd.h"
struct bgp_table {
/* table belongs to this instance */
@@ -67,6 +68,8 @@ struct bgp_node {
#define BGP_NODE_USER_CLEAR (1 << 1)
#define BGP_NODE_LABEL_CHANGED (1 << 2)
#define BGP_NODE_REGISTERED_FOR_LABEL (1 << 3)
+
+ struct bgp_addpath_node_data tx_addpath;
};
/*
diff --git a/bgpd/bgp_updgrp.c b/bgpd/bgp_updgrp.c
index 393586dbec..2e2ad88314 100644
--- a/bgpd/bgp_updgrp.c
+++ b/bgpd/bgp_updgrp.c
@@ -152,6 +152,7 @@ static void conf_copy(struct peer *dst, struct peer *src, afi_t afi,
dst->af_cap[afi][safi] = src->af_cap[afi][safi];
dst->afc_nego[afi][safi] = src->afc_nego[afi][safi];
dst->orf_plist[afi][safi] = src->orf_plist[afi][safi];
+ dst->addpath_type[afi][safi] = src->addpath_type[afi][safi];
dst->local_as = src->local_as;
dst->change_local_as = src->change_local_as;
dst->shared_network = src->shared_network;
@@ -322,6 +323,7 @@ static unsigned int updgrp_hash_key_make(void *p)
key = jhash_1word(peer->sort, key); /* EBGP or IBGP */
key = jhash_1word((peer->flags & PEER_UPDGRP_FLAGS), key);
key = jhash_1word((flags & PEER_UPDGRP_AF_FLAGS), key);
+ key = jhash_1word((uint32_t)peer->addpath_type[afi][safi], key);
key = jhash_1word((peer->cap & PEER_UPDGRP_CAP_FLAGS), key);
key = jhash_1word((peer->af_cap[afi][safi] & PEER_UPDGRP_AF_CAP_FLAGS),
key);
@@ -437,6 +439,9 @@ static bool updgrp_hash_cmp(const void *p1, const void *p2)
if ((flags1 & PEER_UPDGRP_AF_FLAGS) != (flags2 & PEER_UPDGRP_AF_FLAGS))
return false;
+ if (pe1->addpath_type[afi][safi] != pe2->addpath_type[afi][safi])
+ return 0;
+
if ((pe1->cap & PEER_UPDGRP_CAP_FLAGS)
!= (pe2->cap & PEER_UPDGRP_CAP_FLAGS))
return false;
@@ -1900,22 +1905,3 @@ int bgp_addpath_encode_tx(struct peer *peer, afi_t afi, safi_t safi)
&& CHECK_FLAG(peer->af_cap[afi][safi],
PEER_CAP_ADDPATH_AF_RX_RCV));
}
-
-/*
- * Return true if this is a path we should advertise due to a
- * configured addpath-tx knob
- */
-int bgp_addpath_tx_path(struct peer *peer, afi_t afi, safi_t safi,
- struct bgp_path_info *pi)
-{
- if (CHECK_FLAG(peer->af_flags[afi][safi],
- PEER_FLAG_ADDPATH_TX_ALL_PATHS))
- return 1;
-
- if (CHECK_FLAG(peer->af_flags[afi][safi],
- PEER_FLAG_ADDPATH_TX_BESTPATH_PER_AS)
- && CHECK_FLAG(pi->flags, BGP_PATH_DMED_SELECTED))
- return 1;
-
- return 0;
-}
diff --git a/bgpd/bgp_updgrp.h b/bgpd/bgp_updgrp.h
index b1d852d384..6b3bf9d1f7 100644
--- a/bgpd/bgp_updgrp.h
+++ b/bgpd/bgp_updgrp.h
@@ -64,8 +64,7 @@
| PEER_FLAG_REMOVE_PRIVATE_AS_ALL \
| PEER_FLAG_REMOVE_PRIVATE_AS_REPLACE \
| PEER_FLAG_REMOVE_PRIVATE_AS_ALL_REPLACE \
- | PEER_FLAG_ADDPATH_TX_ALL_PATHS \
- | PEER_FLAG_ADDPATH_TX_BESTPATH_PER_AS | PEER_FLAG_AS_OVERRIDE)
+ | PEER_FLAG_AS_OVERRIDE)
#define PEER_UPDGRP_CAP_FLAGS (PEER_CAP_AS4_RCV)
@@ -469,8 +468,6 @@ extern int update_group_clear_update_dbg(struct update_group *updgrp,
extern void update_bgp_group_free(struct bgp *bgp);
extern int bgp_addpath_encode_tx(struct peer *peer, afi_t afi, safi_t safi);
-extern int bgp_addpath_tx_path(struct peer *peer, afi_t afi, safi_t safi,
- struct bgp_path_info *pi);
/*
* Inline functions
diff --git a/bgpd/bgp_updgrp_adv.c b/bgpd/bgp_updgrp_adv.c
index 81b782c2b0..7196bbbf12 100644
--- a/bgpd/bgp_updgrp_adv.c
+++ b/bgpd/bgp_updgrp_adv.c
@@ -49,6 +49,7 @@
#include "bgpd/bgp_mplsvpn.h"
#include "bgpd/bgp_updgrp.h"
#include "bgpd/bgp_advertise.h"
+#include "bgpd/bgp_addpath.h"
/********************
@@ -97,6 +98,40 @@ static void adj_free(struct bgp_adj_out *adj)
XFREE(MTYPE_BGP_ADJ_OUT, adj);
}
+static void subgrp_withdraw_stale_addpath(struct updwalk_context *ctx,
+ struct update_subgroup *subgrp)
+{
+ struct bgp_adj_out *adj, *adj_next;
+ uint32_t id;
+ struct bgp_path_info *pi;
+ afi_t afi = SUBGRP_AFI(subgrp);
+ safi_t safi = SUBGRP_SAFI(subgrp);
+ struct peer *peer = SUBGRP_PEER(subgrp);
+
+ /* Look through all of the paths we have advertised for this rn and send
+ * a withdraw for the ones that are no longer present */
+ for (adj = ctx->rn->adj_out; adj; adj = adj_next) {
+ adj_next = adj->next;
+
+ if (adj->subgroup == subgrp) {
+ for (pi = ctx->rn->info; pi; pi = pi->next) {
+ id = bgp_addpath_id_for_peer(peer, afi, safi,
+ &pi->tx_addpath);
+
+ if (id == adj->addpath_tx_id) {
+ break;
+ }
+ }
+
+ if (!pi) {
+ subgroup_process_announce_selected(
+ subgrp, NULL, ctx->rn,
+ adj->addpath_tx_id);
+ }
+ }
+ }
+}
+
static int group_announce_route_walkcb(struct update_group *updgrp, void *arg)
{
struct updwalk_context *ctx = arg;
@@ -131,31 +166,7 @@ static int group_announce_route_walkcb(struct update_group *updgrp, void *arg)
if (!subgrp->t_coalesce) {
/* An update-group that uses addpath */
if (addpath_capable) {
- /* Look through all of the paths we have
- * advertised for this rn and
- * send a withdraw for the ones that are no
- * longer present */
- for (adj = ctx->rn->adj_out; adj;
- adj = adj_next) {
- adj_next = adj->next;
-
- if (adj->subgroup == subgrp) {
- for (pi = ctx->rn->info; pi;
- pi = pi->next) {
- if (pi->addpath_tx_id
- == adj->addpath_tx_id) {
- break;
- }
- }
-
- if (!pi) {
- subgroup_process_announce_selected(
- subgrp, NULL,
- ctx->rn,
- adj->addpath_tx_id);
- }
- }
- }
+ subgrp_withdraw_stale_addpath(ctx, subgrp);
for (pi = ctx->rn->info; pi; pi = pi->next) {
/* Skip the bestpath for now */
@@ -164,7 +175,9 @@ static int group_announce_route_walkcb(struct update_group *updgrp, void *arg)
subgroup_process_announce_selected(
subgrp, pi, ctx->rn,
- pi->addpath_tx_id);
+ bgp_addpath_id_for_peer(
+ peer, afi, safi,
+ &pi->tx_addpath));
}
/* Process the bestpath last so the "show [ip]
@@ -174,7 +187,9 @@ static int group_announce_route_walkcb(struct update_group *updgrp, void *arg)
if (ctx->pi)
subgroup_process_announce_selected(
subgrp, ctx->pi, ctx->rn,
- ctx->pi->addpath_tx_id);
+ bgp_addpath_id_for_peer(
+ peer, afi, safi,
+ &ctx->pi->tx_addpath));
}
/* An update-group that does not use addpath */
@@ -182,7 +197,9 @@ static int group_announce_route_walkcb(struct update_group *updgrp, void *arg)
if (ctx->pi) {
subgroup_process_announce_selected(
subgrp, ctx->pi, ctx->rn,
- ctx->pi->addpath_tx_id);
+ bgp_addpath_id_for_peer(
+ peer, afi, safi,
+ &ctx->pi->tx_addpath));
} else {
/* Find the addpath_tx_id of the path we
* had advertised and
@@ -433,15 +450,27 @@ void bgp_adj_out_set_subgroup(struct bgp_node *rn,
{
struct bgp_adj_out *adj = NULL;
struct bgp_advertise *adv;
+ struct peer *peer;
+ afi_t afi;
+ safi_t safi;
+
+ peer = SUBGRP_PEER(subgrp);
+ afi = SUBGRP_AFI(subgrp);
+ safi = SUBGRP_SAFI(subgrp);
if (DISABLE_BGP_ANNOUNCE)
return;
/* Look for adjacency information. */
- adj = adj_lookup(rn, subgrp, path->addpath_tx_id);
+ adj = adj_lookup(
+ rn, subgrp,
+ bgp_addpath_id_for_peer(peer, afi, safi, &path->tx_addpath));
if (!adj) {
- adj = bgp_adj_out_alloc(subgrp, rn, path->addpath_tx_id);
+ adj = bgp_adj_out_alloc(
+ subgrp, rn,
+ bgp_addpath_id_for_peer(peer, afi, safi,
+ &path->tx_addpath));
if (!adj)
return;
}
@@ -597,7 +626,9 @@ void subgroup_announce_table(struct update_subgroup *subgrp,
if (CHECK_FLAG(ri->flags, BGP_PATH_SELECTED)
|| (addpath_capable
- && bgp_addpath_tx_path(peer, afi, safi, ri))) {
+ && bgp_addpath_tx_path(
+ peer->addpath_type[afi][safi],
+ ri))) {
if (subgroup_announce_check(rn, ri, subgrp,
&rn->p, &attr))
bgp_adj_out_set_subgroup(rn, subgrp,
@@ -605,7 +636,9 @@ void subgroup_announce_table(struct update_subgroup *subgrp,
else
bgp_adj_out_unset_subgroup(
rn, subgrp, 1,
- ri->addpath_tx_id);
+ bgp_addpath_id_for_peer(
+ peer, afi, safi,
+ &ri->tx_addpath));
}
/*
diff --git a/bgpd/bgp_updgrp_packet.c b/bgpd/bgp_updgrp_packet.c
index c4fcea948f..ca6bb5ab82 100644
--- a/bgpd/bgp_updgrp_packet.c
+++ b/bgpd/bgp_updgrp_packet.c
@@ -56,6 +56,7 @@
#include "bgpd/bgp_nht.h"
#include "bgpd/bgp_mplsvpn.h"
#include "bgpd/bgp_label.h"
+#include "bgpd/bgp_addpath.h"
/********************
* PRIVATE FUNCTIONS
diff --git a/bgpd/bgp_vty.c b/bgpd/bgp_vty.c
index c57cd38151..4dd148bb41 100644
--- a/bgpd/bgp_vty.c
+++ b/bgpd/bgp_vty.c
@@ -61,6 +61,7 @@
#include "bgpd/bgp_bfd.h"
#include "bgpd/bgp_io.h"
#include "bgpd/bgp_evpn.h"
+#include "bgpd/bgp_addpath.h"
static struct peer_group *listen_range_exists(struct bgp *bgp,
struct prefix *range, int exact);
@@ -1888,9 +1889,8 @@ DEFUN (no_bgp_deterministic_med,
for (ALL_LIST_ELEMENTS(bgp->peer, node, nnode, peer)) {
FOREACH_AFI_SAFI (afi, safi)
- if (CHECK_FLAG(
- peer->af_flags[afi][safi],
- PEER_FLAG_ADDPATH_TX_BESTPATH_PER_AS)) {
+ if (bgp_addpath_dmed_required(
+ peer->addpath_type[afi][safi])) {
bestpath_per_as_used = 1;
break;
}
@@ -6234,9 +6234,9 @@ DEFUN (neighbor_addpath_tx_all_paths,
if (!peer)
return CMD_WARNING_CONFIG_FAILED;
- return peer_af_flag_set_vty(vty, argv[idx_peer]->arg, bgp_node_afi(vty),
- bgp_node_safi(vty),
- PEER_FLAG_ADDPATH_TX_ALL_PATHS);
+ bgp_addpath_set_peer_type(peer, bgp_node_afi(vty), bgp_node_safi(vty),
+ BGP_ADDPATH_ALL);
+ return CMD_SUCCESS;
}
ALIAS_HIDDEN(neighbor_addpath_tx_all_paths,
@@ -6254,9 +6254,23 @@ DEFUN (no_neighbor_addpath_tx_all_paths,
"Use addpath to advertise all paths to a neighbor\n")
{
int idx_peer = 2;
- return peer_af_flag_unset_vty(vty, argv[idx_peer]->arg,
- bgp_node_afi(vty), bgp_node_safi(vty),
- PEER_FLAG_ADDPATH_TX_ALL_PATHS);
+ struct peer *peer;
+
+ peer = peer_and_group_lookup_vty(vty, argv[idx_peer]->arg);
+ if (!peer)
+ return CMD_WARNING_CONFIG_FAILED;
+
+ if (peer->addpath_type[bgp_node_afi(vty)][bgp_node_safi(vty)]
+ != BGP_ADDPATH_ALL) {
+ vty_out(vty,
+ "%% Peer not currently configured to transmit all paths.");
+ return CMD_WARNING_CONFIG_FAILED;
+ }
+
+ bgp_addpath_set_peer_type(peer, bgp_node_afi(vty), bgp_node_safi(vty),
+ BGP_ADDPATH_NONE);
+
+ return CMD_SUCCESS;
}
ALIAS_HIDDEN(no_neighbor_addpath_tx_all_paths,
@@ -6279,9 +6293,10 @@ DEFUN (neighbor_addpath_tx_bestpath_per_as,
if (!peer)
return CMD_WARNING_CONFIG_FAILED;
- return peer_af_flag_set_vty(vty, argv[idx_peer]->arg, bgp_node_afi(vty),
- bgp_node_safi(vty),
- PEER_FLAG_ADDPATH_TX_BESTPATH_PER_AS);
+ bgp_addpath_set_peer_type(peer, bgp_node_afi(vty), bgp_node_safi(vty),
+ BGP_ADDPATH_BEST_PER_AS);
+
+ return CMD_SUCCESS;
}
ALIAS_HIDDEN(neighbor_addpath_tx_bestpath_per_as,
@@ -6299,9 +6314,23 @@ DEFUN (no_neighbor_addpath_tx_bestpath_per_as,
"Use addpath to advertise the bestpath per each neighboring AS\n")
{
int idx_peer = 2;
- return peer_af_flag_unset_vty(vty, argv[idx_peer]->arg,
- bgp_node_afi(vty), bgp_node_safi(vty),
- PEER_FLAG_ADDPATH_TX_BESTPATH_PER_AS);
+ struct peer *peer;
+
+ peer = peer_and_group_lookup_vty(vty, argv[idx_peer]->arg);
+ if (!peer)
+ return CMD_WARNING_CONFIG_FAILED;
+
+ if (peer->addpath_type[bgp_node_afi(vty)][bgp_node_safi(vty)]
+ != BGP_ADDPATH_BEST_PER_AS) {
+ vty_out(vty,
+ "%% Peer not currently configured to transmit all best path per as.");
+ return CMD_WARNING_CONFIG_FAILED;
+ }
+
+ bgp_addpath_set_peer_type(peer, bgp_node_afi(vty), bgp_node_safi(vty),
+ BGP_ADDPATH_NONE);
+
+ return CMD_SUCCESS;
}
ALIAS_HIDDEN(no_neighbor_addpath_tx_bestpath_per_as,
@@ -8656,15 +8685,11 @@ static void bgp_show_peer_afi(struct vty *vty, struct peer *p, afi_t afi,
json_addr,
"privateAsNumsRemovedInUpdatesToNbr");
- if (CHECK_FLAG(p->af_flags[afi][safi],
- PEER_FLAG_ADDPATH_TX_ALL_PATHS))
- json_object_boolean_true_add(json_addr,
- "addpathTxAllPaths");
-
- if (CHECK_FLAG(p->af_flags[afi][safi],
- PEER_FLAG_ADDPATH_TX_BESTPATH_PER_AS))
- json_object_boolean_true_add(json_addr,
- "addpathTxBestpathPerAS");
+ if (p->addpath_type[afi][safi] != BGP_ADDPATH_NONE)
+ json_object_boolean_true_add(
+ json_addr,
+ bgp_addpath_names(p->addpath_type[afi][safi])
+ ->type_json_name);
if (CHECK_FLAG(p->af_flags[afi][safi], PEER_FLAG_AS_OVERRIDE))
json_object_string_add(json_addr,
@@ -8930,14 +8955,10 @@ static void bgp_show_peer_afi(struct vty *vty, struct peer *p, afi_t afi,
vty_out(vty,
" Private AS numbers removed in updates to this neighbor\n");
- if (CHECK_FLAG(p->af_flags[afi][safi],
- PEER_FLAG_ADDPATH_TX_ALL_PATHS))
- vty_out(vty, " Advertise all paths via addpath\n");
-
- if (CHECK_FLAG(p->af_flags[afi][safi],
- PEER_FLAG_ADDPATH_TX_BESTPATH_PER_AS))
- vty_out(vty,
- " Advertise bestpath per AS via addpath\n");
+ if (p->addpath_type[afi][safi] != BGP_ADDPATH_NONE)
+ vty_out(vty, " %s\n",
+ bgp_addpath_names(p->addpath_type[afi][safi])
+ ->human_description);
if (CHECK_FLAG(p->af_flags[afi][safi], PEER_FLAG_AS_OVERRIDE))
vty_out(vty,
diff --git a/bgpd/bgpd.c b/bgpd/bgpd.c
index 0c26fa6720..32bbb78ef3 100644
--- a/bgpd/bgpd.c
+++ b/bgpd/bgpd.c
@@ -85,6 +85,7 @@
#include "bgpd/bgp_flowspec.h"
#include "bgpd/bgp_labelpool.h"
#include "bgpd/bgp_pbr.h"
+#include "bgpd/bgp_addpath.h"
DEFINE_MTYPE_STATIC(BGPD, PEER_TX_SHUTDOWN_MSG, "Peer shutdown message (TX)");
DEFINE_QOBJ_TYPE(bgp_master)
@@ -883,6 +884,31 @@ static bool peergroup_filter_check(struct peer *peer, afi_t afi, safi_t safi,
}
}
+/* Return true if the addpath type is set for peer and different from
+ * peer-group.
+ */
+static int peergroup_af_addpath_check(struct peer *peer, afi_t afi, safi_t safi)
+{
+ enum bgp_addpath_strat type, g_type;
+
+ type = peer->addpath_type[afi][safi];
+
+ if (type != BGP_ADDPATH_NONE) {
+ if (peer_group_active(peer)) {
+ g_type = peer->group->conf->addpath_type[afi][safi];
+
+ if (type != g_type)
+ return 1;
+ else
+ return 0;
+ }
+
+ return 1;
+ }
+
+ return 0;
+}
+
/* Check peer's AS number and determines if this peer is IBGP or EBGP */
static inline bgp_peer_sort_t peer_calc_sort(struct peer *peer)
{
@@ -960,6 +986,9 @@ bgp_peer_sort_t peer_sort(struct peer *peer)
static void peer_free(struct peer *peer)
{
+ afi_t afi;
+ safi_t safi;
+
assert(peer->status == Deleted);
QOBJ_UNREG(peer);
@@ -1032,6 +1061,13 @@ static void peer_free(struct peer *peer)
bfd_info_free(&(peer->bfd_info));
+ for (afi = AFI_IP; afi < AFI_MAX; afi++) {
+ for (safi = SAFI_UNICAST; safi < SAFI_MAX; safi++) {
+ bgp_addpath_set_peer_type(peer, afi, safi,
+ BGP_ADDPATH_NONE);
+ }
+ }
+
bgp_unlock(peer->bgp);
memset(peer, 0, sizeof(struct peer));
@@ -1118,6 +1154,7 @@ struct peer *peer_new(struct bgp *bgp)
SET_FLAG(peer->af_flags_invert[afi][safi],
PEER_FLAG_SEND_LARGE_COMMUNITY);
}
+ peer->addpath_type[afi][safi] = BGP_ADDPATH_NONE;
}
/* set nexthop-unchanged for l2vpn evpn by default */
@@ -1210,6 +1247,8 @@ void peer_xfer_config(struct peer *peer_dst, struct peer *peer_src)
peer_dst->allowas_in[afi][safi] =
peer_src->allowas_in[afi][safi];
peer_dst->weight[afi][safi] = peer_src->weight[afi][safi];
+ peer_dst->addpath_type[afi][safi] =
+ peer_src->addpath_type[afi][safi];
}
for (afidx = BGP_AF_START; afidx < BGP_AF_MAX; afidx++) {
@@ -1808,6 +1847,11 @@ static void peer_group2peer_config_copy_af(struct peer_group *group,
MTYPE_BGP_FILTER_NAME);
PEER_ATTR_INHERIT(peer, group, filter[afi][safi].usmap.map);
}
+
+ if (peer->addpath_type[afi][safi] == BGP_ADDPATH_NONE) {
+ peer->addpath_type[afi][safi] = conf->addpath_type[afi][safi];
+ bgp_addpath_type_changed(conf->bgp);
+ }
}
static int peer_activate_af(struct peer *peer, afi_t afi, safi_t safi)
@@ -2836,7 +2880,7 @@ static struct bgp *bgp_create(as_t *as, const char *name,
#if DFLT_BGP_DETERMINISTIC_MED
bgp_flag_set(bgp, BGP_FLAG_DETERMINISTIC_MED);
#endif
- bgp->addpath_tx_id = BGP_ADDPATH_TX_ID_FOR_DEFAULT_ORIGINATE;
+ bgp_addpath_init_bgp_data(&bgp->tx_addpath);
bgp->as = *as;
@@ -3627,15 +3671,7 @@ int peer_active_nego(struct peer *peer)
return 0;
}
-/* peer_flag_change_type. */
-enum peer_change_type {
- peer_change_none,
- peer_change_reset,
- peer_change_reset_in,
- peer_change_reset_out,
-};
-
-static void peer_change_action(struct peer *peer, afi_t afi, safi_t safi,
+void peer_change_action(struct peer *peer, afi_t afi, safi_t safi,
enum peer_change_type type)
{
if (CHECK_FLAG(peer->sflags, PEER_STATUS_GROUP))
@@ -3731,8 +3767,6 @@ static const struct peer_flag_action peer_af_flag_action_list[] = {
{PEER_FLAG_REMOVE_PRIVATE_AS_REPLACE, 1, peer_change_reset_out},
{PEER_FLAG_AS_OVERRIDE, 1, peer_change_reset_out},
{PEER_FLAG_REMOVE_PRIVATE_AS_ALL_REPLACE, 1, peer_change_reset_out},
- {PEER_FLAG_ADDPATH_TX_ALL_PATHS, 1, peer_change_reset},
- {PEER_FLAG_ADDPATH_TX_BESTPATH_PER_AS, 1, peer_change_reset},
{PEER_FLAG_WEIGHT, 0, peer_change_reset_in},
{0, 0, 0}};
@@ -3957,9 +3991,7 @@ static int peer_af_flag_modify(struct peer *peer, afi_t afi, safi_t safi,
{
int found;
int size;
- int addpath_tx_used;
bool invert, member_invert;
- struct bgp *bgp;
struct peer *member;
struct listnode *node, *nnode;
struct peer_flag_action action;
@@ -4122,45 +4154,6 @@ static int peer_af_flag_modify(struct peer *peer, afi_t afi, safi_t safi,
}
}
- /* Track if addpath TX is in use */
- if (flag & (PEER_FLAG_ADDPATH_TX_ALL_PATHS
- | PEER_FLAG_ADDPATH_TX_BESTPATH_PER_AS)) {
- bgp = peer->bgp;
- addpath_tx_used = 0;
-
- if (set) {
- addpath_tx_used = 1;
-
- if (flag & PEER_FLAG_ADDPATH_TX_BESTPATH_PER_AS) {
- if (!bgp_flag_check(
- bgp, BGP_FLAG_DETERMINISTIC_MED)) {
- zlog_info(
- "%s: enabling bgp deterministic-med, this is required"
- " for addpath-tx-bestpath-per-AS",
- peer->host);
- bgp_flag_set(
- bgp,
- BGP_FLAG_DETERMINISTIC_MED);
- bgp_recalculate_all_bestpaths(bgp);
- }
- }
- } else {
- for (ALL_LIST_ELEMENTS(bgp->peer, node, nnode,
- member)) {
- if (CHECK_FLAG(member->af_flags[afi][safi],
- PEER_FLAG_ADDPATH_TX_ALL_PATHS)
- || CHECK_FLAG(
- member->af_flags[afi][safi],
- PEER_FLAG_ADDPATH_TX_BESTPATH_PER_AS)) {
- addpath_tx_used = 1;
- break;
- }
- }
- }
-
- bgp->addpath_tx_used[afi][safi] = addpath_tx_used;
- }
-
return 0;
}
@@ -7065,15 +7058,21 @@ static void bgp_config_write_peer_af(struct vty *vty, struct bgp *bgp,
}
/* addpath TX knobs */
- if (peergroup_af_flag_check(peer, afi, safi,
- PEER_FLAG_ADDPATH_TX_ALL_PATHS)) {
- vty_out(vty, " neighbor %s addpath-tx-all-paths\n", addr);
- }
-
- if (peergroup_af_flag_check(peer, afi, safi,
- PEER_FLAG_ADDPATH_TX_BESTPATH_PER_AS)) {
- vty_out(vty, " neighbor %s addpath-tx-bestpath-per-AS\n",
- addr);
+ if (peergroup_af_addpath_check(peer, afi, safi)) {
+ switch (peer->addpath_type[afi][safi]) {
+ case BGP_ADDPATH_ALL:
+ vty_out(vty, " neighbor %s addpath-tx-all-paths\n",
+ addr);
+ break;
+ case BGP_ADDPATH_BEST_PER_AS:
+ vty_out(vty,
+ " neighbor %s addpath-tx-bestpath-per-AS\n",
+ addr);
+ break;
+ case BGP_ADDPATH_MAX:
+ case BGP_ADDPATH_NONE:
+ break;
+ }
}
/* ORF capability. */
diff --git a/bgpd/bgpd.h b/bgpd/bgpd.h
index e14b0f39e0..5ee54e3c70 100644
--- a/bgpd/bgpd.h
+++ b/bgpd/bgpd.h
@@ -39,6 +39,7 @@
#include "bitfield.h"
#include "vxlan.h"
#include "bgp_labelpool.h"
+#include "bgp_addpath_types.h"
#define BGP_MAX_HOSTNAME 64 /* Linux max, is larger than most other sys */
#define BGP_PEER_MAX_HASH_SIZE 16384
@@ -462,8 +463,7 @@ struct bgp {
/* Auto-shutdown new peers */
bool autoshutdown;
- uint32_t addpath_tx_id;
- int addpath_tx_used[AFI_MAX][SAFI_MAX];
+ struct bgp_addpath_bgp_data tx_addpath;
#if ENABLE_BGP_VNC
struct rfapi_cfg *rfapi_cfg;
@@ -938,12 +938,12 @@ struct peer {
#define PEER_FLAG_REMOVE_PRIVATE_AS_REPLACE (1 << 19) /* remove-private-as replace-as */
#define PEER_FLAG_AS_OVERRIDE (1 << 20) /* as-override */
#define PEER_FLAG_REMOVE_PRIVATE_AS_ALL_REPLACE (1 << 21) /* remove-private-as all replace-as */
-#define PEER_FLAG_ADDPATH_TX_ALL_PATHS (1 << 22) /* addpath-tx-all-paths */
-#define PEER_FLAG_ADDPATH_TX_BESTPATH_PER_AS (1 << 23) /* addpath-tx-bestpath-per-AS */
#define PEER_FLAG_WEIGHT (1 << 24) /* weight */
#define PEER_FLAG_ALLOWAS_IN_ORIGIN (1 << 25) /* allowas-in origin */
#define PEER_FLAG_SEND_LARGE_COMMUNITY (1 << 26) /* Send large Communities */
+ enum bgp_addpath_strat addpath_type[AFI_MAX][SAFI_MAX];
+
/* MD5 password */
char *password;
@@ -1466,6 +1466,14 @@ typedef enum {
BGP_POLICY_DISTRIBUTE_LIST,
} bgp_policy_type_e;
+/* peer_flag_change_type. */
+enum peer_change_type {
+ peer_change_none,
+ peer_change_reset,
+ peer_change_reset_in,
+ peer_change_reset_out,
+};
+
extern struct bgp_master *bm;
extern unsigned int multipath_num;
@@ -1597,6 +1605,8 @@ extern int peer_af_flag_unset(struct peer *, afi_t, safi_t, uint32_t);
extern int peer_af_flag_check(struct peer *, afi_t, safi_t, uint32_t);
extern void peer_af_flag_inherit(struct peer *peer, afi_t afi, safi_t safi,
uint32_t flag);
+extern void peer_change_action(struct peer *peer, afi_t afi, safi_t safi,
+ enum peer_change_type type);
extern int peer_ebgp_multihop_set(struct peer *, int);
extern int peer_ebgp_multihop_unset(struct peer *);
diff --git a/bgpd/subdir.am b/bgpd/subdir.am
index 83f55939ce..a13f058f74 100644
--- a/bgpd/subdir.am
+++ b/bgpd/subdir.am
@@ -46,6 +46,7 @@ man8 += $(MANBUILD)/bgpd.8
endif
bgpd_libbgp_a_SOURCES = \
+ bgpd/bgp_addpath.c \
bgpd/bgp_advertise.c \
bgpd/bgp_aspath.c \
bgpd/bgp_attr.c \
@@ -115,6 +116,8 @@ bgpd_libbgp_a_SOURCES += \
endif
noinst_HEADERS += \
+ bgpd/bgp_addpath.h \
+ bgpd/bgp_addpath_types.h \
bgpd/bgp_advertise.h \
bgpd/bgp_aspath.h \
bgpd/bgp_attr.h \
diff --git a/lib/id_alloc.c b/lib/id_alloc.c
new file mode 100644
index 0000000000..b4d37dbdcf
--- /dev/null
+++ b/lib/id_alloc.c
@@ -0,0 +1,406 @@
+/*
+ * FRR ID Number Allocator
+ * Copyright (C) 2018 Amazon.com, Inc. or its affiliates
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License as published by the Free
+ * Software Foundation; either version 2 of the License, or (at your option)
+ * any later version.
+ *
+ * This program is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
+ * more details.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program; see the file COPYING; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
+ */
+
+#include "id_alloc.h"
+
+#include "log.h"
+#include "lib_errors.h"
+#include "memory.h"
+
+#include <inttypes.h>
+
+DEFINE_MTYPE_STATIC(LIB, IDALLOC_ALLOCATOR, "ID Number Allocator")
+DEFINE_MTYPE_STATIC(LIB, IDALLOC_ALLOCATOR_NAME, "ID Number Allocator Name")
+DEFINE_MTYPE_STATIC(LIB, IDALLOC_DIRECTORY, "ID Number Allocator Directory")
+DEFINE_MTYPE_STATIC(LIB, IDALLOC_SUBDIRECTORY,
+ "ID Number Allocator Subdirectory")
+DEFINE_MTYPE_STATIC(LIB, IDALLOC_PAGE, "ID Number Allocator Page")
+DEFINE_MTYPE_STATIC(LIB, IDALLOC_POOL, "ID Number temporary holding pool entry")
+
+#if UINT_MAX >= UINT32_MAX
+#define FFS32(x) ffs(x)
+#else
+/* ints less than 32 bits? Yikes. */
+#define FFS32(x) ffsl(x)
+#endif
+
+#define DIR_MASK ((1<<IDALLOC_DIR_BITS)-1)
+#define SUBDIR_MASK ((1<<IDALLOC_SUBDIR_BITS)-1)
+#define PAGE_MASK ((1<<IDALLOC_PAGE_BITS)-1)
+#define WORD_MASK ((1<<IDALLOC_WORD_BITS)-1)
+#define OFFSET_MASK ((1<<IDALLOC_OFFSET_BITS)-1)
+
+#define DIR_SHIFT (IDALLOC_OFFSET_BITS + IDALLOC_WORD_BITS + \
+ IDALLOC_PAGE_BITS + IDALLOC_SUBDIR_BITS)
+#define SUBDIR_SHIFT (IDALLOC_OFFSET_BITS + IDALLOC_WORD_BITS + \
+ IDALLOC_PAGE_BITS)
+#define PAGE_SHIFT (IDALLOC_OFFSET_BITS + IDALLOC_WORD_BITS)
+#define WORD_SHIFT (IDALLOC_OFFSET_BITS)
+#define OFFSET_SHIFT (0)
+
+#define ID_DIR(id) ((id >> DIR_SHIFT) & DIR_MASK)
+#define ID_SUBDIR(id) ((id >> SUBDIR_SHIFT) & SUBDIR_MASK)
+#define ID_PAGE(id) ((id >> PAGE_SHIFT) & PAGE_MASK)
+#define ID_WORD(id) ((id >> WORD_SHIFT) & WORD_MASK)
+#define ID_OFFSET(id) ((id >> OFFSET_SHIFT) & OFFSET_MASK)
+
+/*
+ * Find the page that an ID number belongs to in an allocator.
+ * Optionally create the page if it doesn't exist.
+ */
+static struct id_alloc_page *find_or_create_page(struct id_alloc *alloc,
+ uint32_t id, int create)
+{
+ struct id_alloc_dir *dir = NULL;
+ struct id_alloc_subdir *subdir = NULL;
+ struct id_alloc_page *page = NULL;
+
+ dir = alloc->sublevels[ID_DIR(id)];
+ if (dir == NULL) {
+ if (create) {
+ dir = XCALLOC(MTYPE_IDALLOC_DIRECTORY, sizeof(*dir));
+ alloc->sublevels[ID_DIR(id)] = dir;
+ } else {
+ return NULL;
+ }
+ }
+
+ subdir = dir->sublevels[ID_SUBDIR(id)];
+ if (subdir == NULL) {
+ if (create) {
+ subdir = XCALLOC(MTYPE_IDALLOC_SUBDIRECTORY,
+ sizeof(*subdir));
+ dir->sublevels[ID_SUBDIR(id)] = subdir;
+ } else {
+ return NULL;
+ }
+ }
+
+ page = subdir->sublevels[ID_PAGE(id)];
+ if (page == NULL && create) {
+ page = XCALLOC(MTYPE_IDALLOC_PAGE, sizeof(*page));
+ page->base_value = id;
+ subdir->sublevels[ID_PAGE(id)] = page;
+
+ alloc->capacity += 1 << PAGE_SHIFT;
+ page->next_has_free = alloc->has_free;
+ alloc->has_free = page;
+ } else if (page != NULL && create) {
+ flog_err(
+ EC_LIB_ID_CONSISTENCY,
+ "ID Allocator %s attempt to re-create page at %" PRIu32,
+ alloc->name, id);
+ }
+
+ return page;
+}
+
+/*
+ * Return an ID number back to the allocator.
+ * While this ID can be re-assigned through idalloc_allocate, the underlying
+ * memory will not be freed. If this is the first free ID in the page, the page
+ * will be added to the allocator's list of pages with free IDs.
+ */
+void idalloc_free(struct id_alloc *alloc, uint32_t id)
+{
+ struct id_alloc_page *page = NULL;
+
+ int word, offset;
+ uint32_t old_word, old_word_mask;
+
+ page = find_or_create_page(alloc, id, 0);
+ if (!page) {
+ flog_err(EC_LIB_ID_CONSISTENCY,
+ "ID Allocator %s cannot free #%" PRIu32
+ ". ID Block does not exist.",
+ alloc->name, id);
+ return;
+ }
+
+ word = ID_WORD(id);
+ offset = ID_OFFSET(id);
+
+ if ((page->allocated_mask[word] & (1 << offset)) == 0) {
+ flog_err(EC_LIB_ID_CONSISTENCY,
+ "ID Allocator %s cannot free #%" PRIu32
+ ". ID was not allocated at the time of free.",
+ alloc->name, id);
+ return;
+ }
+
+ old_word = page->allocated_mask[word];
+ page->allocated_mask[word] &= ~(((uint32_t)1) << offset);
+ alloc->allocated -= 1;
+
+ if (old_word == UINT32_MAX) {
+ /* first bit in this block of 32 to be freed.*/
+
+ old_word_mask = page->full_word_mask;
+ page->full_word_mask &= ~(((uint32_t)1) << word);
+
+ if (old_word_mask == UINT32_MAX) {
+ /* first bit in page freed, add this to the allocator's
+ * list of pages with free space
+ */
+ page->next_has_free = alloc->has_free;
+ alloc->has_free = page;
+ }
+ }
+}
+
+/*
+ * Add a allocation page to the end of the allocator's current range.
+ * Returns null if the allocator has had all possible pages allocated already.
+ */
+static struct id_alloc_page *create_next_page(struct id_alloc *alloc)
+{
+ if (alloc->capacity == 0 && alloc->sublevels[0])
+ return NULL; /* All IDs allocated and the capacity looped. */
+
+ return find_or_create_page(alloc, alloc->capacity, 1);
+}
+
+/*
+ * Marks an ID within an allocator page as in use.
+ * If the ID was the last free ID in the page, the page is removed from the
+ * allocator's list of free IDs. In the typical allocation case, this page is
+ * the first page in the list, and removing the page is fast. If instead an ID
+ * is being reserved by number, this may end up scanning the whole single linked
+ * list of pages in order to remove it.
+ */
+static void reserve_bit(struct id_alloc *alloc, struct id_alloc_page *page,
+ int word, int offset)
+{
+ struct id_alloc_page *itr;
+
+ page->allocated_mask[word] |= ((uint32_t)1) << offset;
+ alloc->allocated += 1;
+
+ if (page->allocated_mask[word] == UINT32_MAX) {
+ page->full_word_mask |= ((uint32_t)1) << word;
+ if (page->full_word_mask == UINT32_MAX) {
+ if (alloc->has_free == page) {
+ /* allocate always pulls from alloc->has_free */
+ alloc->has_free = page->next_has_free;
+ } else {
+ /* reserve could pull from any page with free
+ * bits
+ */
+ itr = alloc->has_free;
+ while (itr) {
+ if (itr->next_has_free == page) {
+ itr->next_has_free =
+ page->next_has_free;
+ return;
+ }
+
+ itr = itr->next_has_free;
+ }
+ }
+ }
+ }
+}
+
+/*
+ * Reserve an ID number from the allocator. Returns IDALLOC_INVALID (0) if the
+ * allocator has no more IDs available.
+ */
+uint32_t idalloc_allocate(struct id_alloc *alloc)
+{
+ struct id_alloc_page *page;
+ int word, offset;
+ uint32_t return_value;
+
+ if (alloc->has_free == NULL)
+ create_next_page(alloc);
+
+ if (alloc->has_free == NULL) {
+ flog_err(EC_LIB_ID_EXHAUST,
+ "ID Allocator %s has run out of IDs.", alloc->name);
+ return IDALLOC_INVALID;
+ }
+
+ page = alloc->has_free;
+ word = FFS32(~(page->full_word_mask)) - 1;
+
+ if (word < 0 || word >= 32) {
+ flog_err(EC_LIB_ID_CONSISTENCY,
+ "ID Allocator %s internal error. Page starting at %d is inconsistent.",
+ alloc->name, page->base_value);
+ return IDALLOC_INVALID;
+ }
+
+ offset = FFS32(~(page->allocated_mask[word])) - 1;
+ if (offset < 0 || offset >= 32) {
+ flog_err(EC_LIB_ID_CONSISTENCY,
+ "ID Allocator %s internal error. Page starting at %d is inconsistent on word %d",
+ alloc->name, page->base_value, word);
+ return IDALLOC_INVALID;
+ }
+ return_value = page->base_value + word * 32 + offset;
+
+ reserve_bit(alloc, page, word, offset);
+
+ return return_value;
+}
+
+/*
+ * Tries to allocate a specific ID from the allocator. Returns IDALLOC_INVALID
+ * when the ID being "reserved" has allready been assigned/reserved. This should
+ * only be done with low numbered IDs, as the allocator needs to reserve bit-map
+ * pages in order
+ */
+uint32_t idalloc_reserve(struct id_alloc *alloc, uint32_t id)
+{
+ struct id_alloc_page *page;
+ int word, offset;
+
+ while (alloc->capacity <= id)
+ create_next_page(alloc);
+
+ word = ID_WORD(id);
+ offset = ID_OFFSET(id);
+ page = find_or_create_page(alloc, id, 0);
+ /* page can't be null because the loop above ensured it was created. */
+
+ if (page->allocated_mask[word] & (((uint32_t)1) << offset)) {
+ flog_err(EC_LIB_ID_CONSISTENCY,
+ "ID Allocator %s could not reserve %" PRIu32
+ " because it is already allocated.",
+ alloc->name, id);
+ return IDALLOC_INVALID;
+ }
+
+ reserve_bit(alloc, page, word, offset);
+ return id;
+}
+
+/*
+ * Set up an empty ID allocator, with IDALLOC_INVALID pre-reserved.
+ */
+struct id_alloc *idalloc_new(const char *name)
+{
+ struct id_alloc *ret;
+
+ ret = XCALLOC(MTYPE_IDALLOC_ALLOCATOR, sizeof(*ret));
+ ret->name = XSTRDUP(MTYPE_IDALLOC_ALLOCATOR_NAME, name);
+
+ idalloc_reserve(ret, IDALLOC_INVALID);
+
+ return ret;
+}
+
+/*
+ * Free a subdir, and all pages below it.
+ */
+static void idalloc_destroy_subdir(struct id_alloc_subdir *subdir)
+{
+ int i;
+
+ for (i = 0; i < IDALLOC_PAGE_COUNT; i++) {
+ if (subdir->sublevels[i])
+ XFREE(MTYPE_IDALLOC_PAGE, subdir->sublevels[i]);
+ else
+ break;
+ }
+ XFREE(MTYPE_IDALLOC_SUBDIRECTORY, subdir);
+}
+
+/*
+ * Free a dir, and all subdirs/pages below it.
+ */
+static void idalloc_destroy_dir(struct id_alloc_dir *dir)
+{
+ int i;
+
+ for (i = 0; i < IDALLOC_SUBDIR_COUNT; i++) {
+ if (dir->sublevels[i])
+ idalloc_destroy_subdir(dir->sublevels[i]);
+ else
+ break;
+ }
+ XFREE(MTYPE_IDALLOC_DIRECTORY, dir);
+}
+
+/*
+ * Free all memory associated with an ID allocator.
+ */
+void idalloc_destroy(struct id_alloc *alloc)
+{
+ int i;
+
+ for (i = 0; i < IDALLOC_DIR_COUNT; i++) {
+ if (alloc->sublevels[i])
+ idalloc_destroy_dir(alloc->sublevels[i]);
+ else
+ break;
+ }
+
+ XFREE(MTYPE_IDALLOC_ALLOCATOR_NAME, alloc->name);
+ XFREE(MTYPE_IDALLOC_ALLOCATOR, alloc);
+}
+
+/*
+ * Give an ID number to temporary holding pool.
+ */
+void idalloc_free_to_pool(struct id_alloc_pool **pool_ptr, uint32_t id)
+{
+ struct id_alloc_pool *new_pool;
+
+ new_pool = XMALLOC(MTYPE_IDALLOC_POOL, sizeof(*new_pool));
+ new_pool->id = id;
+ new_pool->next = *pool_ptr;
+ *pool_ptr = new_pool;
+}
+
+/*
+ * Free all ID numbers held in a holding pool back to the main allocator.
+ */
+void idalloc_drain_pool(struct id_alloc *alloc, struct id_alloc_pool **pool_ptr)
+{
+ struct id_alloc_pool *current, *next;
+
+ while (*pool_ptr) {
+ current = *pool_ptr;
+ next = current->next;
+ idalloc_free(alloc, current->id);
+ XFREE(MTYPE_IDALLOC_POOL, current);
+ *pool_ptr = next;
+ }
+}
+
+/*
+ * Allocate an ID from either a holding pool, or the main allocator. IDs will
+ * only be pulled form the main allocator when the pool is empty.
+ */
+uint32_t idalloc_allocate_prefer_pool(struct id_alloc *alloc,
+ struct id_alloc_pool **pool_ptr)
+{
+ uint32_t ret;
+ struct id_alloc_pool *pool_head = *pool_ptr;
+
+ if (pool_head) {
+ ret = pool_head->id;
+ *pool_ptr = pool_head->next;
+ XFREE(MTYPE_IDALLOC_POOL, pool_head);
+ return ret;
+ } else {
+ return idalloc_allocate(alloc);
+ }
+}
diff --git a/lib/id_alloc.h b/lib/id_alloc.h
new file mode 100644
index 0000000000..efe355658a
--- /dev/null
+++ b/lib/id_alloc.h
@@ -0,0 +1,90 @@
+/*
+ * FRR ID Number Allocator
+ * Copyright (C) 2018 Amazon.com, Inc. or its affiliates
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License as published by the Free
+ * Software Foundation; either version 2 of the License, or (at your option)
+ * any later version.
+ *
+ * This program is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
+ * more details.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program; see the file COPYING; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
+ */
+
+#ifndef _ZEBRA_ID_ALLOC_H
+#define _ZEBRA_ID_ALLOC_H
+
+#include <strings.h>
+#include <limits.h>
+#include <stdint.h>
+
+#define IDALLOC_INVALID 0
+
+#define IDALLOC_DIR_BITS 8
+#define IDALLOC_SUBDIR_BITS 7
+#define IDALLOC_PAGE_BITS 7
+#define IDALLOC_WORD_BITS 5
+#define IDALLOC_OFFSET_BITS 5
+
+#define IDALLOC_DIR_COUNT (1 << IDALLOC_DIR_BITS)
+#define IDALLOC_SUBDIR_COUNT (1 << IDALLOC_SUBDIR_BITS)
+#define IDALLOC_PAGE_COUNT (1 << IDALLOC_PAGE_BITS)
+#define IDALLOC_WORD_COUNT (1 << IDALLOC_WORD_BITS)
+
+struct id_alloc_page {
+ /* Bitmask of allocations. 1s indicates the ID is already allocated. */
+ uint32_t allocated_mask[IDALLOC_WORD_COUNT];
+
+ /* Bitmask for free space in allocated_mask. 1s indicate whole 32 bit
+ * section is full.
+ */
+ uint32_t full_word_mask;
+
+ /* The ID that bit 0 in allocated_mask corresponds to. */
+ uint32_t base_value;
+
+ struct id_alloc_page
+ *next_has_free; /* Next page with at least one bit open */
+};
+
+struct id_alloc_subdir {
+ struct id_alloc_page *sublevels[IDALLOC_PAGE_COUNT];
+};
+
+struct id_alloc_dir {
+ struct id_alloc_subdir *sublevels[IDALLOC_SUBDIR_COUNT];
+};
+
+struct id_alloc {
+ struct id_alloc_dir *sublevels[IDALLOC_DIR_COUNT];
+
+ struct id_alloc_page *has_free;
+
+ char *name;
+
+ uint32_t allocated, capacity;
+};
+
+struct id_alloc_pool {
+ struct id_alloc_pool *next;
+ uint32_t id;
+};
+
+void idalloc_free(struct id_alloc *alloc, uint32_t id);
+void idalloc_free_to_pool(struct id_alloc_pool **pool_ptr, uint32_t id);
+void idalloc_drain_pool(struct id_alloc *alloc,
+ struct id_alloc_pool **pool_ptr);
+uint32_t idalloc_allocate(struct id_alloc *alloc);
+uint32_t idalloc_allocate_prefer_pool(struct id_alloc *alloc,
+ struct id_alloc_pool **pool_ptr);
+uint32_t idalloc_reserve(struct id_alloc *alloc, uint32_t id);
+struct id_alloc *idalloc_new(const char *name);
+void idalloc_destroy(struct id_alloc *alloc);
+
+#endif
diff --git a/lib/lib_errors.h b/lib/lib_errors.h
index 3831116dd3..38c75f913e 100644
--- a/lib/lib_errors.h
+++ b/lib/lib_errors.h
@@ -72,6 +72,8 @@ enum lib_log_refs {
EC_LIB_SYSREPO_INIT,
EC_LIB_SYSREPO_DATA_CONVERT,
EC_LIB_LIBSYSREPO,
+ EC_LIB_ID_CONSISTENCY,
+ EC_LIB_ID_EXHAUST,
};
extern void lib_error_init(void);
diff --git a/lib/subdir.am b/lib/subdir.am
index 356796fbb1..c144c2c2e1 100644
--- a/lib/subdir.am
+++ b/lib/subdir.am
@@ -29,6 +29,7 @@ lib_libfrr_la_SOURCES = \
lib/graph.c \
lib/hash.c \
lib/hook.c \
+ lib/id_alloc.c \
lib/if.c \
lib/if_rmap.c \
lib/imsg-buffer.c \
@@ -147,6 +148,7 @@ pkginclude_HEADERS += \
lib/graph.h \
lib/hash.h \
lib/hook.h \
+ lib/id_alloc.h \
lib/if.h \
lib/if_rmap.h \
lib/imsg.h \
diff --git a/tests/.gitignore b/tests/.gitignore
index 49a4b6e470..a6202786be 100644
--- a/tests/.gitignore
+++ b/tests/.gitignore
@@ -20,23 +20,24 @@
/lib/cli/test_commands_defun.c
/lib/test_buffer
/lib/test_checksum
+/lib/test_graph
/lib/test_heavy
/lib/test_heavy_thread
/lib/test_heavy_wq
+/lib/test_idalloc
/lib/test_memory
/lib/test_nexthop_iter
/lib/test_privs
/lib/test_ringbuf
-/lib/test_srcdest_table
/lib/test_segv
/lib/test_sig
+/lib/test_srcdest_table
/lib/test_stream
/lib/test_table
/lib/test_timer_correctness
/lib/test_timer_performance
/lib/test_ttable
-/lib/test_zmq
/lib/test_zlog
-/lib/test_graph
+/lib/test_zmq
/ospf6d/test_lsdb
/ospf6d/test_lsdb_clippy.c
diff --git a/tests/bgpd/test_bgp_table.c b/tests/bgpd/test_bgp_table.c
index 171b1aa22f..73243dcacf 100644
--- a/tests/bgpd/test_bgp_table.c
+++ b/tests/bgpd/test_bgp_table.c
@@ -28,6 +28,8 @@
#include "bgpd/bgp_table.h"
#include "linklist.h"
+/* Satisfy link requirements from including bgpd.h */
+struct zebra_privs_t bgpd_privs = {0};
/*
* test_node_t
*
diff --git a/tests/bgpd/test_peer_attr.c b/tests/bgpd/test_peer_attr.c
index 2f464d7217..2fbc686e1e 100644
--- a/tests/bgpd/test_peer_attr.c
+++ b/tests/bgpd/test_peer_attr.c
@@ -254,6 +254,8 @@ TEST_STR_ATTR_HANDLER_DECL(password, password, "FRR-Peer", "FRR-Group");
TEST_ATTR_HANDLER_DECL(local_as, change_local_as, 1, 2);
TEST_ATTR_HANDLER_DECL(timers_1, keepalive, 10, 20);
TEST_ATTR_HANDLER_DECL(timers_2, holdtime, 30, 60);
+TEST_ATTR_HANDLER_DECL(addpath_types, addpath_type[pa->afi][pa->safi],
+ BGP_ADDPATH_ALL, BGP_ADDPATH_BEST_PER_AS);
TEST_SU_ATTR_HANDLER_DECL(update_source_su, update_source, "255.255.255.1",
"255.255.255.2");
TEST_STR_ATTR_HANDLER_DECL(update_source_if, update_if, "IF-PEER", "IF-GROUP");
@@ -414,12 +416,11 @@ static struct test_peer_attr test_peer_attrs[] = {
/* Address Family Attributes */
{
- .cmd = "addpath-tx-all-paths",
- .u.flag = PEER_FLAG_ADDPATH_TX_ALL_PATHS,
- },
- {
- .cmd = "addpath-tx-bestpath-per-AS",
- .u.flag = PEER_FLAG_ADDPATH_TX_BESTPATH_PER_AS,
+ .cmd = "addpath",
+ .peer_cmd = "addpath-tx-all-paths",
+ .group_cmd = "addpath-tx-bestpath-per-AS",
+ .type = PEER_AT_AF_CUSTOM,
+ .handlers[0] = TEST_HANDLER(addpath_types),
},
{
.cmd = "allowas-in",
diff --git a/tests/bgpd/test_peer_attr.py b/tests/bgpd/test_peer_attr.py
index 7a4dd2b04a..bafd131b78 100644
--- a/tests/bgpd/test_peer_attr.py
+++ b/tests/bgpd/test_peer_attr.py
@@ -26,14 +26,10 @@ TestFlag.okfail('peer\\timers')
TestFlag.okfail('peer\\timers connect')
TestFlag.okfail('peer\\update-source')
TestFlag.okfail('peer\\update-source')
-TestFlag.okfail('peer\\ipv4-unicast\\addpath-tx-all-paths')
-TestFlag.okfail('peer\\ipv4-multicast\\addpath-tx-all-paths')
-TestFlag.okfail('peer\\ipv6-unicast\\addpath-tx-all-paths')
-TestFlag.okfail('peer\\ipv6-multicast\\addpath-tx-all-paths')
-TestFlag.okfail('peer\\ipv4-unicast\\addpath-tx-bestpath-per-AS')
-TestFlag.okfail('peer\\ipv4-multicast\\addpath-tx-bestpath-per-AS')
-TestFlag.okfail('peer\\ipv6-unicast\\addpath-tx-bestpath-per-AS')
-TestFlag.okfail('peer\\ipv6-multicast\\addpath-tx-bestpath-per-AS')
+TestFlag.okfail('peer\\ipv4-unicast\\addpath')
+TestFlag.okfail('peer\\ipv4-multicast\\addpath')
+TestFlag.okfail('peer\\ipv6-unicast\\addpath')
+TestFlag.okfail('peer\\ipv6-multicast\\addpath')
TestFlag.okfail('peer\\ipv4-unicast\\allowas-in')
TestFlag.okfail('peer\\ipv4-multicast\\allowas-in')
TestFlag.okfail('peer\\ipv6-unicast\\allowas-in')
diff --git a/tests/lib/test_idalloc.c b/tests/lib/test_idalloc.c
new file mode 100644
index 0000000000..3053c1c074
--- /dev/null
+++ b/tests/lib/test_idalloc.c
@@ -0,0 +1,193 @@
+#include "id_alloc.h"
+
+#include <inttypes.h>
+#include <string.h>
+#include <assert.h>
+#include <stdio.h>
+
+#define IDS_PER_PAGE (1<<(IDALLOC_OFFSET_BITS + IDALLOC_WORD_BITS))
+char allocated_markers[IDS_PER_PAGE*3];
+
+int main(int argc, char **argv)
+{
+ int i, val;
+ uint32_t pg;
+ struct id_alloc *a;
+
+ /* 1. Rattle test, shake it a little and make sure it doesn't make any
+ * noise :)
+ */
+ a = idalloc_new("Rattle test");
+ for (i = 0; i < 1000000; i++)
+ assert(idalloc_allocate(a) != 0);
+
+ idalloc_destroy(a);
+
+ /* 2. Reserve a few low IDs, make sure they are skipped by normal
+ * allocation.
+ */
+ a = idalloc_new("Low Reservations");
+ assert(idalloc_reserve(a, 1) == 1);
+ assert(idalloc_reserve(a, 3) == 3);
+ assert(idalloc_reserve(a, 5) == 5);
+ for (i = 0; i < 100; i++) {
+ val = idalloc_allocate(a);
+ assert(val != 1 && val != 3 && val != 5);
+ }
+ idalloc_destroy(a);
+
+ /* 3. Single page testing. Check that IDs are kept unique, and all IDs
+ * in the existing page are allocated before a new page is added.
+ */
+ memset(allocated_markers, 0, sizeof(allocated_markers));
+ allocated_markers[IDALLOC_INVALID] = 1;
+
+ a = idalloc_new("Single Page");
+
+ /* reserve the rest of the first page */
+ for (i = 0; i < IDS_PER_PAGE - 1; i++) {
+ val = idalloc_allocate(a);
+ assert(val < IDS_PER_PAGE);
+ assert(allocated_markers[val] == 0);
+ assert(a->capacity == IDS_PER_PAGE);
+ allocated_markers[val] = 1;
+ }
+ /* Check that the count is right */
+ assert(a->allocated == IDS_PER_PAGE);
+
+ /* Free some IDs out of the middle. */
+ idalloc_free(a, 300);
+ allocated_markers[300] = 0;
+ idalloc_free(a, 400);
+ allocated_markers[400] = 0;
+ idalloc_free(a, 500);
+ allocated_markers[500] = 0;
+
+ assert(a->allocated == IDS_PER_PAGE-3);
+
+ /* Allocate the three IDs back and make sure they are pulled from the
+ * set just freed
+ */
+ for (i = 0; i < 3; i++) {
+ val = idalloc_allocate(a);
+ assert(val < IDS_PER_PAGE);
+ assert(allocated_markers[val] == 0);
+ assert(a->capacity == IDS_PER_PAGE);
+ allocated_markers[val] = 1;
+ }
+ idalloc_destroy(a);
+
+ /* 4. Multi-page testing. */
+ memset(allocated_markers, 0, sizeof(allocated_markers));
+ allocated_markers[IDALLOC_INVALID] = 1;
+
+ a = idalloc_new("Multi-page");
+
+ /* reserve the rest of the first page and all of the second and third */
+ for (i = 0; i < 3 * IDS_PER_PAGE - 1; i++) {
+ val = idalloc_allocate(a);
+ assert(val < 3*IDS_PER_PAGE);
+ assert(allocated_markers[val] == 0);
+ allocated_markers[val] = 1;
+ }
+ assert(a->capacity == 3*IDS_PER_PAGE);
+ assert(a->allocated == 3*IDS_PER_PAGE);
+
+ /* Free two IDs from each page. */
+ for (i = 0; i < 3; i++) {
+ idalloc_free(a, 7 + i*IDS_PER_PAGE);
+ allocated_markers[7 + i*IDS_PER_PAGE] = 0;
+
+ idalloc_free(a, 4 + i*IDS_PER_PAGE);
+ allocated_markers[4 + i*IDS_PER_PAGE] = 0;
+ }
+
+ assert(a->allocated == 3*IDS_PER_PAGE - 6);
+
+ /* Allocate the six IDs back and make sure they are pulled from the set
+ * just freed.
+ */
+ for (i = 0; i < 6; i++) {
+ val = idalloc_allocate(a);
+ assert(val < 3*IDS_PER_PAGE);
+ assert(allocated_markers[val] == 0);
+ assert(a->capacity == 3*IDS_PER_PAGE);
+ allocated_markers[val] = 1;
+ }
+
+ assert(a->capacity == 3*IDS_PER_PAGE);
+ assert(a->allocated == 3*IDS_PER_PAGE);
+
+ /* Walk each allocated ID. Free it, then re-allocate it back. */
+ for (i = 1; i < 3 * IDS_PER_PAGE - 1; i++) {
+ idalloc_free(a, i);
+ val = idalloc_allocate(a);
+ assert(val == i);
+ assert(a->capacity == 3*IDS_PER_PAGE);
+ assert(a->allocated == 3*IDS_PER_PAGE);
+ }
+ idalloc_destroy(a);
+
+ /* 5. Weird Reservations
+ * idalloc_reserve exists primarily to black out low numbered IDs that
+ * are reserved for special cases. However, we will test it for more
+ * complex use cases to avoid unpleasant surprises.
+ */
+
+ memset(allocated_markers, 0, sizeof(allocated_markers));
+ allocated_markers[IDALLOC_INVALID] = 1;
+
+ a = idalloc_new("Weird Reservations");
+
+ /* Start with 3 pages fully allocated. */
+ for (i = 0; i < 3 * IDS_PER_PAGE - 1; i++) {
+ val = idalloc_allocate(a);
+ assert(val < 3*IDS_PER_PAGE);
+ assert(allocated_markers[val] == 0);
+ allocated_markers[val] = 1;
+ }
+ assert(a->capacity == 3*IDS_PER_PAGE);
+ assert(a->allocated == 3*IDS_PER_PAGE);
+
+ /* Free a bit out of each of the three pages. Then reserve one of the
+ * three freed IDs. Finally, allocate the other two freed IDs. Do this
+ * each of three ways. (Reserve out of the first, seconds then third
+ * page.)
+ * The intent here is to exercise the rare cases on reserve_bit's
+ * linked-list removal in the case that it is not removing the first
+ * page with a free bit in its list of pages with free bits.
+ */
+
+ for (pg = 0; pg < 3; pg++) {
+ /* free a bit out of each of the three pages */
+ for (i = 0; i < 3; i++) {
+ idalloc_free(a, i*IDS_PER_PAGE + 17);
+ allocated_markers[i*IDS_PER_PAGE + 17] = 0;
+ }
+
+ assert(a->capacity == 3*IDS_PER_PAGE);
+ assert(a->allocated == 3*IDS_PER_PAGE-3);
+
+ /* Reserve one of the freed IDs */
+ assert(idalloc_reserve(a, pg*IDS_PER_PAGE + 17) ==
+ pg*IDS_PER_PAGE + 17);
+ allocated_markers[pg*IDS_PER_PAGE + 17] = 1;
+
+ assert(a->capacity == 3*IDS_PER_PAGE);
+ assert(a->allocated == 3*IDS_PER_PAGE-2);
+
+ /* Allocate the other two back */
+ for (i = 0; i < 2; i++) {
+ val = idalloc_allocate(a);
+ assert(val < 3*IDS_PER_PAGE);
+ assert(allocated_markers[val] == 0);
+ allocated_markers[val] = 1;
+ }
+ assert(a->capacity == 3*IDS_PER_PAGE);
+ assert(a->allocated == 3*IDS_PER_PAGE);
+ }
+ idalloc_destroy(a);
+
+ puts("ID Allocator test successful.\n");
+ return 0;
+}
diff --git a/tests/lib/test_idalloc.py b/tests/lib/test_idalloc.py
new file mode 100644
index 0000000000..22de082be4
--- /dev/null
+++ b/tests/lib/test_idalloc.py
@@ -0,0 +1,6 @@
+import frrtest
+
+class TestIDAlloc(frrtest.TestMultiOut):
+ program = './test_idalloc'
+
+TestIDAlloc.onesimple('ID Allocator test successful.')
diff --git a/tests/subdir.am b/tests/subdir.am
index 7a693ac0f7..6b52c90bc0 100644
--- a/tests/subdir.am
+++ b/tests/subdir.am
@@ -51,6 +51,7 @@ check_PROGRAMS = \
tests/lib/test_heavy_thread \
tests/lib/test_heavy_wq \
tests/lib/test_heavy \
+ tests/lib/test_idalloc \
tests/lib/test_memory \
tests/lib/test_nexthop_iter \
tests/lib/test_privs \
@@ -198,6 +199,9 @@ tests_lib_test_heavy_wq_CFLAGS = $(TESTS_CFLAGS)
tests_lib_test_heavy_wq_CPPFLAGS = $(TESTS_CPPFLAGS)
tests_lib_test_heavy_wq_LDADD = $(ALL_TESTS_LDADD) -lm
tests_lib_test_heavy_wq_SOURCES = tests/lib/test_heavy_wq.c tests/helpers/c/main.c
+tests_lib_test_idalloc_CFLAGS = $(TESTS_CFLAGS)
+tests_lib_test_idalloc_LDADD = $(ALL_TESTS_LDADD)
+tests_lib_test_idalloc_SOURCES = tests/lib/test_idalloc.c
tests_lib_test_memory_CFLAGS = $(TESTS_CFLAGS)
tests_lib_test_memory_CPPFLAGS = $(TESTS_CPPFLAGS)
tests_lib_test_memory_LDADD = $(ALL_TESTS_LDADD)