summaryrefslogtreecommitdiffstats
path: root/bgpd/bgp_route.c
diff options
context:
space:
mode:
Diffstat (limited to 'bgpd/bgp_route.c')
-rw-r--r--bgpd/bgp_route.c15995
1 files changed, 15995 insertions, 0 deletions
diff --git a/bgpd/bgp_route.c b/bgpd/bgp_route.c
new file mode 100644
index 0000000..3a850a4
--- /dev/null
+++ b/bgpd/bgp_route.c
@@ -0,0 +1,15995 @@
+// SPDX-License-Identifier: GPL-2.0-or-later
+/* BGP routing information
+ * Copyright (C) 1996, 97, 98, 99 Kunihiro Ishiguro
+ * Copyright (C) 2016 Job Snijders <job@instituut.net>
+ */
+
+#include <zebra.h>
+#include <math.h>
+
+#include "printfrr.h"
+#include "frrstr.h"
+#include "prefix.h"
+#include "linklist.h"
+#include "memory.h"
+#include "command.h"
+#include "stream.h"
+#include "filter.h"
+#include "log.h"
+#include "routemap.h"
+#include "buffer.h"
+#include "sockunion.h"
+#include "plist.h"
+#include "frrevent.h"
+#include "workqueue.h"
+#include "queue.h"
+#include "memory.h"
+#include "srv6.h"
+#include "lib/json.h"
+#include "lib_errors.h"
+#include "zclient.h"
+#include "bgpd/bgpd.h"
+#include "bgpd/bgp_table.h"
+#include "bgpd/bgp_route.h"
+#include "bgpd/bgp_attr.h"
+#include "bgpd/bgp_debug.h"
+#include "bgpd/bgp_errors.h"
+#include "bgpd/bgp_aspath.h"
+#include "bgpd/bgp_regex.h"
+#include "bgpd/bgp_community.h"
+#include "bgpd/bgp_community_alias.h"
+#include "bgpd/bgp_ecommunity.h"
+#include "bgpd/bgp_lcommunity.h"
+#include "bgpd/bgp_clist.h"
+#include "bgpd/bgp_packet.h"
+#include "bgpd/bgp_filter.h"
+#include "bgpd/bgp_fsm.h"
+#include "bgpd/bgp_mplsvpn.h"
+#include "bgpd/bgp_nexthop.h"
+#include "bgpd/bgp_damp.h"
+#include "bgpd/bgp_advertise.h"
+#include "bgpd/bgp_zebra.h"
+#include "bgpd/bgp_vty.h"
+#include "bgpd/bgp_mpath.h"
+#include "bgpd/bgp_nht.h"
+#include "bgpd/bgp_updgrp.h"
+#include "bgpd/bgp_label.h"
+#include "bgpd/bgp_addpath.h"
+#include "bgpd/bgp_mac.h"
+#include "bgpd/bgp_network.h"
+#include "bgpd/bgp_trace.h"
+#include "bgpd/bgp_rpki.h"
+
+#ifdef ENABLE_BGP_VNC
+#include "bgpd/rfapi/rfapi_backend.h"
+#include "bgpd/rfapi/vnc_import_bgp.h"
+#include "bgpd/rfapi/vnc_export_bgp.h"
+#endif
+#include "bgpd/bgp_encap_types.h"
+#include "bgpd/bgp_encap_tlv.h"
+#include "bgpd/bgp_evpn.h"
+#include "bgpd/bgp_evpn_mh.h"
+#include "bgpd/bgp_evpn_vty.h"
+#include "bgpd/bgp_flowspec.h"
+#include "bgpd/bgp_flowspec_util.h"
+#include "bgpd/bgp_pbr.h"
+
+#include "bgpd/bgp_route_clippy.c"
+
+DEFINE_HOOK(bgp_snmp_update_stats,
+ (struct bgp_dest *rn, struct bgp_path_info *pi, bool added),
+ (rn, pi, added));
+
+DEFINE_HOOK(bgp_rpki_prefix_status,
+ (struct peer *peer, struct attr *attr,
+ const struct prefix *prefix),
+ (peer, attr, prefix));
+
+/* Extern from bgp_dump.c */
+extern const char *bgp_origin_str[];
+extern const char *bgp_origin_long_str[];
+
+/* PMSI strings. */
+#define PMSI_TNLTYPE_STR_NO_INFO "No info"
+#define PMSI_TNLTYPE_STR_DEFAULT PMSI_TNLTYPE_STR_NO_INFO
+static const struct message bgp_pmsi_tnltype_str[] = {
+ {PMSI_TNLTYPE_NO_INFO, PMSI_TNLTYPE_STR_NO_INFO},
+ {PMSI_TNLTYPE_RSVP_TE_P2MP, "RSVP-TE P2MP"},
+ {PMSI_TNLTYPE_MLDP_P2MP, "mLDP P2MP"},
+ {PMSI_TNLTYPE_PIM_SSM, "PIM-SSM"},
+ {PMSI_TNLTYPE_PIM_SM, "PIM-SM"},
+ {PMSI_TNLTYPE_PIM_BIDIR, "PIM-BIDIR"},
+ {PMSI_TNLTYPE_INGR_REPL, "Ingress Replication"},
+ {PMSI_TNLTYPE_MLDP_MP2MP, "mLDP MP2MP"},
+ {0}
+};
+
+#define VRFID_NONE_STR "-"
+#define SOFT_RECONFIG_TASK_MAX_PREFIX 25000
+
+DEFINE_HOOK(bgp_process,
+ (struct bgp * bgp, afi_t afi, safi_t safi, struct bgp_dest *bn,
+ struct peer *peer, bool withdraw),
+ (bgp, afi, safi, bn, peer, withdraw));
+
+/** Test if path is suppressed. */
+static bool bgp_path_suppressed(struct bgp_path_info *pi)
+{
+ if (pi->extra == NULL || pi->extra->aggr_suppressors == NULL)
+ return false;
+
+ return listcount(pi->extra->aggr_suppressors) > 0;
+}
+
+struct bgp_dest *bgp_afi_node_get(struct bgp_table *table, afi_t afi,
+ safi_t safi, const struct prefix *p,
+ struct prefix_rd *prd)
+{
+ struct bgp_dest *dest;
+ struct bgp_dest *pdest = NULL;
+
+ assert(table);
+
+ if ((safi == SAFI_MPLS_VPN) || (safi == SAFI_ENCAP)
+ || (safi == SAFI_EVPN)) {
+ pdest = bgp_node_get(table, (struct prefix *)prd);
+
+ if (!bgp_dest_has_bgp_path_info_data(pdest))
+ bgp_dest_set_bgp_table_info(
+ pdest, bgp_table_init(table->bgp, afi, safi));
+ else
+ pdest = bgp_dest_unlock_node(pdest);
+
+ assert(pdest);
+ table = bgp_dest_get_bgp_table_info(pdest);
+ }
+
+ dest = bgp_node_get(table, p);
+
+ if ((safi == SAFI_MPLS_VPN) || (safi == SAFI_ENCAP)
+ || (safi == SAFI_EVPN))
+ dest->pdest = pdest;
+
+ return dest;
+}
+
+struct bgp_dest *bgp_safi_node_lookup(struct bgp_table *table, safi_t safi,
+ const struct prefix *p,
+ struct prefix_rd *prd)
+{
+ struct bgp_dest *dest;
+ struct bgp_dest *pdest = NULL;
+
+ if (!table)
+ return NULL;
+
+ if ((safi == SAFI_MPLS_VPN) || (safi == SAFI_ENCAP)
+ || (safi == SAFI_EVPN)) {
+ pdest = bgp_node_lookup(table, (struct prefix *)prd);
+ if (!pdest)
+ return NULL;
+
+ if (!bgp_dest_has_bgp_path_info_data(pdest)) {
+ bgp_dest_unlock_node(pdest);
+ return NULL;
+ }
+
+ table = bgp_dest_get_bgp_table_info(pdest);
+ }
+
+ dest = bgp_node_lookup(table, p);
+
+ return dest;
+}
+
+/* Allocate bgp_path_info_extra */
+static struct bgp_path_info_extra *bgp_path_info_extra_new(void)
+{
+ struct bgp_path_info_extra *new;
+ new = XCALLOC(MTYPE_BGP_ROUTE_EXTRA,
+ sizeof(struct bgp_path_info_extra));
+ new->label[0] = MPLS_INVALID_LABEL;
+ new->num_labels = 0;
+ new->flowspec = NULL;
+ return new;
+}
+
+void bgp_path_info_extra_free(struct bgp_path_info_extra **extra)
+{
+ struct bgp_path_info_extra *e;
+
+ if (!extra || !*extra)
+ return;
+
+ e = *extra;
+ if (e->damp_info)
+ bgp_damp_info_free(e->damp_info, 0, e->damp_info->afi,
+ e->damp_info->safi);
+
+ e->damp_info = NULL;
+ if (e->vrfleak && e->vrfleak->parent) {
+ struct bgp_path_info *bpi =
+ (struct bgp_path_info *)e->vrfleak->parent;
+
+ if (bpi->net) {
+ /* FIXME: since multiple e may have the same e->parent
+ * and e->parent->net is holding a refcount for each
+ * of them, we need to do some fudging here.
+ *
+ * WARNING: if bpi->net->lock drops to 0, bpi may be
+ * freed as well (because bpi->net was holding the
+ * last reference to bpi) => write after free!
+ */
+ unsigned refcount;
+
+ bpi = bgp_path_info_lock(bpi);
+ refcount = bgp_dest_get_lock_count(bpi->net) - 1;
+ bgp_dest_unlock_node((struct bgp_dest *)bpi->net);
+ if (!refcount)
+ bpi->net = NULL;
+ bgp_path_info_unlock(bpi);
+ }
+ bgp_path_info_unlock(e->vrfleak->parent);
+ e->vrfleak->parent = NULL;
+ }
+
+ if (e->vrfleak && e->vrfleak->bgp_orig)
+ bgp_unlock(e->vrfleak->bgp_orig);
+
+ if (e->vrfleak && e->vrfleak->peer_orig)
+ peer_unlock(e->vrfleak->peer_orig);
+
+ if (e->aggr_suppressors)
+ list_delete(&e->aggr_suppressors);
+
+ if (e->evpn && e->evpn->mh_info)
+ bgp_evpn_path_mh_info_free(e->evpn->mh_info);
+
+ if ((*extra)->flowspec && (*extra)->flowspec->bgp_fs_iprule)
+ list_delete(&((*extra)->flowspec->bgp_fs_iprule));
+ if ((*extra)->flowspec && (*extra)->flowspec->bgp_fs_pbr)
+ list_delete(&((*extra)->flowspec->bgp_fs_pbr));
+
+ if (e->evpn)
+ XFREE(MTYPE_BGP_ROUTE_EXTRA_EVPN, e->evpn);
+ if (e->flowspec)
+ XFREE(MTYPE_BGP_ROUTE_EXTRA_FS, e->flowspec);
+ if (e->vrfleak)
+ XFREE(MTYPE_BGP_ROUTE_EXTRA_VRFLEAK, e->vrfleak);
+
+ XFREE(MTYPE_BGP_ROUTE_EXTRA, *extra);
+}
+
+/* Get bgp_path_info extra information for the given bgp_path_info, lazy
+ * allocated if required.
+ */
+struct bgp_path_info_extra *bgp_path_info_extra_get(struct bgp_path_info *pi)
+{
+ if (!pi->extra)
+ pi->extra = bgp_path_info_extra_new();
+ if (!pi->extra->evpn && pi->net && pi->net->rn->p.family == AF_EVPN)
+ pi->extra->evpn =
+ XCALLOC(MTYPE_BGP_ROUTE_EXTRA_EVPN,
+ sizeof(struct bgp_path_info_extra_evpn));
+ return pi->extra;
+}
+
+/* Free bgp route information. */
+void bgp_path_info_free_with_caller(const char *name,
+ struct bgp_path_info *path)
+{
+ frrtrace(2, frr_bgp, bgp_path_info_free, path, name);
+ bgp_attr_unintern(&path->attr);
+
+ bgp_unlink_nexthop(path);
+ bgp_path_info_extra_free(&path->extra);
+ bgp_path_info_mpath_free(&path->mpath);
+ if (path->net)
+ bgp_addpath_free_info_data(&path->tx_addpath,
+ &path->net->tx_addpath);
+
+ peer_unlock(path->peer); /* bgp_path_info peer reference */
+
+ XFREE(MTYPE_BGP_ROUTE, path);
+}
+
+struct bgp_path_info *bgp_path_info_lock(struct bgp_path_info *path)
+{
+ path->lock++;
+ return path;
+}
+
+struct bgp_path_info *bgp_path_info_unlock(struct bgp_path_info *path)
+{
+ assert(path && path->lock > 0);
+ path->lock--;
+
+ if (path->lock == 0) {
+ bgp_path_info_free(path);
+ return NULL;
+ }
+
+ return path;
+}
+
+bool bgp_path_info_nexthop_changed(struct bgp_path_info *pi, struct peer *to,
+ afi_t afi)
+{
+ if (pi->peer->sort == BGP_PEER_IBGP && to->sort == BGP_PEER_IBGP &&
+ !CHECK_FLAG(to->af_flags[afi][SAFI_MPLS_VPN],
+ PEER_FLAG_FORCE_NEXTHOP_SELF))
+ /* IBGP RR with no nexthop self force configured */
+ return false;
+
+ if (to->sort == BGP_PEER_IBGP &&
+ !CHECK_FLAG(to->af_flags[afi][SAFI_MPLS_VPN],
+ PEER_FLAG_NEXTHOP_SELF))
+ /* IBGP RR with no nexthop self configured */
+ return false;
+
+ if (CHECK_FLAG(to->af_flags[afi][SAFI_MPLS_VPN],
+ PEER_FLAG_NEXTHOP_UNCHANGED))
+ /* IBGP or EBGP with nexthop attribute unchanged */
+ return false;
+
+ return true;
+}
+
+/* This function sets flag BGP_NODE_SELECT_DEFER based on condition */
+static int bgp_dest_set_defer_flag(struct bgp_dest *dest, bool delete)
+{
+ struct peer *peer;
+ struct bgp_path_info *old_pi, *nextpi;
+ bool set_flag = false;
+ struct bgp *bgp = NULL;
+ struct bgp_table *table = NULL;
+ afi_t afi = 0;
+ safi_t safi = 0;
+
+ /* If the flag BGP_NODE_SELECT_DEFER is set and new path is added
+ * then the route selection is deferred
+ */
+ if (CHECK_FLAG(dest->flags, BGP_NODE_SELECT_DEFER) && (!delete))
+ return 0;
+
+ if (CHECK_FLAG(dest->flags, BGP_NODE_PROCESS_SCHEDULED)) {
+ if (BGP_DEBUG(update, UPDATE_OUT)) {
+ table = bgp_dest_table(dest);
+ if (table)
+ bgp = table->bgp;
+
+ zlog_debug(
+ "Route %pBD(%s) is in workqueue and being processed, not deferred.",
+ dest, bgp ? bgp->name_pretty : "(Unknown)");
+ }
+
+ return 0;
+ }
+
+ table = bgp_dest_table(dest);
+ if (table) {
+ bgp = table->bgp;
+ afi = table->afi;
+ safi = table->safi;
+ }
+
+ for (old_pi = bgp_dest_get_bgp_path_info(dest);
+ (old_pi != NULL) && (nextpi = old_pi->next, 1); old_pi = nextpi) {
+ if (CHECK_FLAG(old_pi->flags, BGP_PATH_SELECTED))
+ continue;
+
+ /* Route selection is deferred if there is a stale path which
+ * which indicates peer is in restart mode
+ */
+ if (CHECK_FLAG(old_pi->flags, BGP_PATH_STALE)
+ && (old_pi->sub_type == BGP_ROUTE_NORMAL)) {
+ set_flag = true;
+ } else {
+ /* If the peer is graceful restart capable and peer is
+ * restarting mode, set the flag BGP_NODE_SELECT_DEFER
+ */
+ peer = old_pi->peer;
+ if (BGP_PEER_GRACEFUL_RESTART_CAPABLE(peer)
+ && BGP_PEER_RESTARTING_MODE(peer)
+ && (old_pi
+ && old_pi->sub_type == BGP_ROUTE_NORMAL)) {
+ set_flag = true;
+ }
+ }
+ if (set_flag)
+ break;
+ }
+
+ /* Set the flag BGP_NODE_SELECT_DEFER if route selection deferral timer
+ * is active
+ */
+ if (set_flag && table) {
+ if (bgp && (bgp->gr_info[afi][safi].t_select_deferral)) {
+ if (!CHECK_FLAG(dest->flags, BGP_NODE_SELECT_DEFER))
+ bgp->gr_info[afi][safi].gr_deferred++;
+ SET_FLAG(dest->flags, BGP_NODE_SELECT_DEFER);
+ if (BGP_DEBUG(update, UPDATE_OUT))
+ zlog_debug("DEFER route %pBD(%s), dest %p",
+ dest, bgp->name_pretty, dest);
+ return 0;
+ }
+ }
+ return -1;
+}
+
+void bgp_path_info_add_with_caller(const char *name, struct bgp_dest *dest,
+ struct bgp_path_info *pi)
+{
+ frrtrace(3, frr_bgp, bgp_path_info_add, dest, pi, name);
+ struct bgp_path_info *top;
+
+ top = bgp_dest_get_bgp_path_info(dest);
+
+ pi->next = top;
+ pi->prev = NULL;
+ if (top)
+ top->prev = pi;
+ bgp_dest_set_bgp_path_info(dest, pi);
+
+ bgp_path_info_lock(pi);
+ bgp_dest_lock_node(dest);
+ peer_lock(pi->peer); /* bgp_path_info peer reference */
+ bgp_dest_set_defer_flag(dest, false);
+ hook_call(bgp_snmp_update_stats, dest, pi, true);
+}
+
+/* Do the actual removal of info from RIB, for use by bgp_process
+ completion callback *only* */
+struct bgp_dest *bgp_path_info_reap(struct bgp_dest *dest,
+ struct bgp_path_info *pi)
+{
+ if (pi->next)
+ pi->next->prev = pi->prev;
+ if (pi->prev)
+ pi->prev->next = pi->next;
+ else
+ bgp_dest_set_bgp_path_info(dest, pi->next);
+
+ bgp_path_info_mpath_dequeue(pi);
+ bgp_path_info_unlock(pi);
+ hook_call(bgp_snmp_update_stats, dest, pi, false);
+
+ return bgp_dest_unlock_node(dest);
+}
+
+void bgp_path_info_delete(struct bgp_dest *dest, struct bgp_path_info *pi)
+{
+ bgp_path_info_set_flag(dest, pi, BGP_PATH_REMOVED);
+ /* set of previous already took care of pcount */
+ UNSET_FLAG(pi->flags, BGP_PATH_VALID);
+}
+
+/* undo the effects of a previous call to bgp_path_info_delete; typically
+ called when a route is deleted and then quickly re-added before the
+ deletion has been processed */
+void bgp_path_info_restore(struct bgp_dest *dest, struct bgp_path_info *pi)
+{
+ bgp_path_info_unset_flag(dest, pi, BGP_PATH_REMOVED);
+ /* unset of previous already took care of pcount */
+ SET_FLAG(pi->flags, BGP_PATH_VALID);
+}
+
+/* Adjust pcount as required */
+static void bgp_pcount_adjust(struct bgp_dest *dest, struct bgp_path_info *pi)
+{
+ struct bgp_table *table;
+
+ assert(dest && bgp_dest_table(dest));
+ assert(pi && pi->peer && pi->peer->bgp);
+
+ table = bgp_dest_table(dest);
+
+ if (pi->peer == pi->peer->bgp->peer_self)
+ return;
+
+ if (!BGP_PATH_COUNTABLE(pi)
+ && CHECK_FLAG(pi->flags, BGP_PATH_COUNTED)) {
+
+ UNSET_FLAG(pi->flags, BGP_PATH_COUNTED);
+
+ /* slight hack, but more robust against errors. */
+ if (pi->peer->pcount[table->afi][table->safi])
+ pi->peer->pcount[table->afi][table->safi]--;
+ else
+ flog_err(EC_LIB_DEVELOPMENT,
+ "Asked to decrement 0 prefix count for peer");
+ } else if (BGP_PATH_COUNTABLE(pi)
+ && !CHECK_FLAG(pi->flags, BGP_PATH_COUNTED)) {
+ SET_FLAG(pi->flags, BGP_PATH_COUNTED);
+ pi->peer->pcount[table->afi][table->safi]++;
+ }
+}
+
+static int bgp_label_index_differs(struct bgp_path_info *pi1,
+ struct bgp_path_info *pi2)
+{
+ return (!(pi1->attr->label_index == pi2->attr->label_index));
+}
+
+/* Set/unset bgp_path_info flags, adjusting any other state as needed.
+ * This is here primarily to keep prefix-count in check.
+ */
+void bgp_path_info_set_flag(struct bgp_dest *dest, struct bgp_path_info *pi,
+ uint32_t flag)
+{
+ SET_FLAG(pi->flags, flag);
+
+ /* early bath if we know it's not a flag that changes countability state
+ */
+ if (!CHECK_FLAG(flag,
+ BGP_PATH_VALID | BGP_PATH_HISTORY | BGP_PATH_REMOVED))
+ return;
+
+ bgp_pcount_adjust(dest, pi);
+}
+
+void bgp_path_info_unset_flag(struct bgp_dest *dest, struct bgp_path_info *pi,
+ uint32_t flag)
+{
+ UNSET_FLAG(pi->flags, flag);
+
+ /* early bath if we know it's not a flag that changes countability state
+ */
+ if (!CHECK_FLAG(flag,
+ BGP_PATH_VALID | BGP_PATH_HISTORY | BGP_PATH_REMOVED))
+ return;
+
+ bgp_pcount_adjust(dest, pi);
+}
+
+/* Get MED value. If MED value is missing and "bgp bestpath
+ missing-as-worst" is specified, treat it as the worst value. */
+static uint32_t bgp_med_value(struct attr *attr, struct bgp *bgp)
+{
+ if (attr->flag & ATTR_FLAG_BIT(BGP_ATTR_MULTI_EXIT_DISC))
+ return attr->med;
+ else {
+ if (CHECK_FLAG(bgp->flags, BGP_FLAG_MED_MISSING_AS_WORST))
+ return BGP_MED_MAX;
+ else
+ return 0;
+ }
+}
+
+void bgp_path_info_path_with_addpath_rx_str(struct bgp_path_info *pi, char *buf,
+ size_t buf_len)
+{
+ struct peer *peer;
+
+ if (pi->sub_type == BGP_ROUTE_IMPORTED &&
+ bgp_get_imported_bpi_ultimate(pi))
+ peer = bgp_get_imported_bpi_ultimate(pi)->peer;
+ else
+ peer = pi->peer;
+
+ if (pi->addpath_rx_id)
+ snprintf(buf, buf_len, "path %s (addpath rxid %d)", peer->host,
+ pi->addpath_rx_id);
+ else
+ snprintf(buf, buf_len, "path %s", peer->host);
+}
+
+
+/*
+ * Get the ultimate path info.
+ */
+struct bgp_path_info *bgp_get_imported_bpi_ultimate(struct bgp_path_info *info)
+{
+ struct bgp_path_info *bpi_ultimate;
+
+ if (info->sub_type != BGP_ROUTE_IMPORTED)
+ return info;
+
+ for (bpi_ultimate = info;
+ bpi_ultimate->extra && bpi_ultimate->extra->vrfleak &&
+ bpi_ultimate->extra->vrfleak->parent;
+ bpi_ultimate = bpi_ultimate->extra->vrfleak->parent)
+ ;
+
+ return bpi_ultimate;
+}
+
+/* Compare two bgp route entity. If 'new' is preferable over 'exist' return 1.
+ */
+int bgp_path_info_cmp(struct bgp *bgp, struct bgp_path_info *new,
+ struct bgp_path_info *exist, int *paths_eq,
+ struct bgp_maxpaths_cfg *mpath_cfg, bool debug,
+ char *pfx_buf, afi_t afi, safi_t safi,
+ enum bgp_path_selection_reason *reason)
+{
+ const struct prefix *new_p;
+ struct attr *newattr, *existattr;
+ enum bgp_peer_sort new_sort;
+ enum bgp_peer_sort exist_sort;
+ uint32_t new_pref;
+ uint32_t exist_pref;
+ uint32_t new_med;
+ uint32_t exist_med;
+ uint32_t new_weight;
+ uint32_t exist_weight;
+ uint32_t newm, existm;
+ struct in_addr new_id;
+ struct in_addr exist_id;
+ int new_cluster;
+ int exist_cluster;
+ int internal_as_route;
+ int confed_as_route;
+ int ret = 0;
+ int igp_metric_ret = 0;
+ int peer_sort_ret = -1;
+ char new_buf[PATH_ADDPATH_STR_BUFFER];
+ char exist_buf[PATH_ADDPATH_STR_BUFFER];
+ uint32_t new_mm_seq;
+ uint32_t exist_mm_seq;
+ int nh_cmp;
+ esi_t *exist_esi;
+ esi_t *new_esi;
+ bool same_esi;
+ bool old_proxy;
+ bool new_proxy;
+ bool new_origin, exist_origin;
+ struct bgp_path_info *bpi_ultimate;
+ struct peer *peer_new, *peer_exist;
+
+ *paths_eq = 0;
+
+ /* 0. Null check. */
+ if (new == NULL) {
+ *reason = bgp_path_selection_none;
+ if (debug)
+ zlog_debug("%s: new is NULL", pfx_buf);
+ return 0;
+ }
+
+ if (debug) {
+ bpi_ultimate = bgp_get_imported_bpi_ultimate(new);
+ bgp_path_info_path_with_addpath_rx_str(bpi_ultimate, new_buf,
+ sizeof(new_buf));
+ }
+
+ if (exist == NULL) {
+ *reason = bgp_path_selection_first;
+ if (debug)
+ zlog_debug("%s(%s): %s is the initial bestpath",
+ pfx_buf, bgp->name_pretty, new_buf);
+ return 1;
+ }
+
+ if (debug) {
+ bpi_ultimate = bgp_get_imported_bpi_ultimate(exist);
+ bgp_path_info_path_with_addpath_rx_str(bpi_ultimate, exist_buf,
+ sizeof(exist_buf));
+ zlog_debug("%s(%s): Comparing %s flags 0x%x with %s flags 0x%x",
+ pfx_buf, bgp->name_pretty, new_buf, new->flags,
+ exist_buf, exist->flags);
+ }
+
+ newattr = new->attr;
+ existattr = exist->attr;
+
+ /* A BGP speaker that has advertised the "Long-lived Graceful Restart
+ * Capability" to a neighbor MUST perform the following upon receiving
+ * a route from that neighbor with the "LLGR_STALE" community, or upon
+ * attaching the "LLGR_STALE" community itself per Section 4.2:
+ *
+ * Treat the route as the least-preferred in route selection (see
+ * below). See the Risks of Depreferencing Routes section (Section 5.2)
+ * for a discussion of potential risks inherent in doing this.
+ */
+ if (bgp_attr_get_community(newattr) &&
+ community_include(bgp_attr_get_community(newattr),
+ COMMUNITY_LLGR_STALE)) {
+ if (debug)
+ zlog_debug(
+ "%s: %s wins over %s due to LLGR_STALE community",
+ pfx_buf, new_buf, exist_buf);
+ return 0;
+ }
+
+ if (bgp_attr_get_community(existattr) &&
+ community_include(bgp_attr_get_community(existattr),
+ COMMUNITY_LLGR_STALE)) {
+ if (debug)
+ zlog_debug(
+ "%s: %s loses to %s due to LLGR_STALE community",
+ pfx_buf, new_buf, exist_buf);
+ return 1;
+ }
+
+ new_p = bgp_dest_get_prefix(new->net);
+
+ /* For EVPN routes, we cannot just go by local vs remote, we have to
+ * look at the MAC mobility sequence number, if present.
+ */
+ if ((safi == SAFI_EVPN)
+ && (new_p->u.prefix_evpn.route_type == BGP_EVPN_MAC_IP_ROUTE)) {
+ /* This is an error condition described in RFC 7432 Section
+ * 15.2. The RFC
+ * states that in this scenario "the PE MUST alert the operator"
+ * but it
+ * does not state what other action to take. In order to provide
+ * some
+ * consistency in this scenario we are going to prefer the path
+ * with the
+ * sticky flag.
+ */
+ if (newattr->sticky != existattr->sticky) {
+ if (newattr->sticky && !existattr->sticky) {
+ *reason = bgp_path_selection_evpn_sticky_mac;
+ if (debug)
+ zlog_debug(
+ "%s: %s wins over %s due to sticky MAC flag",
+ pfx_buf, new_buf, exist_buf);
+ return 1;
+ }
+
+ if (!newattr->sticky && existattr->sticky) {
+ *reason = bgp_path_selection_evpn_sticky_mac;
+ if (debug)
+ zlog_debug(
+ "%s: %s loses to %s due to sticky MAC flag",
+ pfx_buf, new_buf, exist_buf);
+ return 0;
+ }
+ }
+
+ new_esi = bgp_evpn_attr_get_esi(newattr);
+ exist_esi = bgp_evpn_attr_get_esi(existattr);
+ if (bgp_evpn_is_esi_valid(new_esi) &&
+ !memcmp(new_esi, exist_esi, sizeof(esi_t))) {
+ same_esi = true;
+ } else {
+ same_esi = false;
+ }
+
+ /* If both paths have the same non-zero ES and
+ * one path is local it wins.
+ * PS: Note the local path wins even if the remote
+ * has the higher MM seq. The local path's
+ * MM seq will be fixed up to match the highest
+ * rem seq, subsequently.
+ */
+ if (same_esi) {
+ char esi_buf[ESI_STR_LEN];
+
+ if (bgp_evpn_is_path_local(bgp, new)) {
+ *reason = bgp_path_selection_evpn_local_path;
+ if (debug)
+ zlog_debug(
+ "%s: %s wins over %s as ES %s is same and local",
+ pfx_buf, new_buf, exist_buf,
+ esi_to_str(new_esi, esi_buf,
+ sizeof(esi_buf)));
+ return 1;
+ }
+ if (bgp_evpn_is_path_local(bgp, exist)) {
+ *reason = bgp_path_selection_evpn_local_path;
+ if (debug)
+ zlog_debug(
+ "%s: %s loses to %s as ES %s is same and local",
+ pfx_buf, new_buf, exist_buf,
+ esi_to_str(new_esi, esi_buf,
+ sizeof(esi_buf)));
+ return 0;
+ }
+ }
+
+ new_mm_seq = mac_mobility_seqnum(newattr);
+ exist_mm_seq = mac_mobility_seqnum(existattr);
+
+ if (new_mm_seq > exist_mm_seq) {
+ *reason = bgp_path_selection_evpn_seq;
+ if (debug)
+ zlog_debug(
+ "%s: %s wins over %s due to MM seq %u > %u",
+ pfx_buf, new_buf, exist_buf, new_mm_seq,
+ exist_mm_seq);
+ return 1;
+ }
+
+ if (new_mm_seq < exist_mm_seq) {
+ *reason = bgp_path_selection_evpn_seq;
+ if (debug)
+ zlog_debug(
+ "%s: %s loses to %s due to MM seq %u < %u",
+ pfx_buf, new_buf, exist_buf, new_mm_seq,
+ exist_mm_seq);
+ return 0;
+ }
+
+ /* if the sequence numbers and ESI are the same and one path
+ * is non-proxy it wins (over proxy)
+ */
+ new_proxy = bgp_evpn_attr_is_proxy(newattr);
+ old_proxy = bgp_evpn_attr_is_proxy(existattr);
+ if (same_esi && bgp_evpn_attr_is_local_es(newattr) &&
+ old_proxy != new_proxy) {
+ if (!new_proxy) {
+ *reason = bgp_path_selection_evpn_non_proxy;
+ if (debug)
+ zlog_debug(
+ "%s: %s wins over %s, same seq/es and non-proxy",
+ pfx_buf, new_buf, exist_buf);
+ return 1;
+ }
+
+ *reason = bgp_path_selection_evpn_non_proxy;
+ if (debug)
+ zlog_debug(
+ "%s: %s loses to %s, same seq/es and non-proxy",
+ pfx_buf, new_buf, exist_buf);
+ return 0;
+ }
+
+ /*
+ * if sequence numbers are the same path with the lowest IP
+ * wins
+ */
+ nh_cmp = bgp_path_info_nexthop_cmp(new, exist);
+ if (nh_cmp < 0) {
+ *reason = bgp_path_selection_evpn_lower_ip;
+ if (debug)
+ zlog_debug(
+ "%s: %s wins over %s due to same MM seq %u and lower IP %pI4",
+ pfx_buf, new_buf, exist_buf, new_mm_seq,
+ &new->attr->nexthop);
+ return 1;
+ }
+ if (nh_cmp > 0) {
+ *reason = bgp_path_selection_evpn_lower_ip;
+ if (debug)
+ zlog_debug(
+ "%s: %s loses to %s due to same MM seq %u and higher IP %pI4",
+ pfx_buf, new_buf, exist_buf, new_mm_seq,
+ &new->attr->nexthop);
+ return 0;
+ }
+ }
+
+ /* 1. Weight check. */
+ new_weight = newattr->weight;
+ exist_weight = existattr->weight;
+
+ if (new_weight > exist_weight) {
+ *reason = bgp_path_selection_weight;
+ if (debug)
+ zlog_debug("%s: %s wins over %s due to weight %d > %d",
+ pfx_buf, new_buf, exist_buf, new_weight,
+ exist_weight);
+ return 1;
+ }
+
+ if (new_weight < exist_weight) {
+ *reason = bgp_path_selection_weight;
+ if (debug)
+ zlog_debug("%s: %s loses to %s due to weight %d < %d",
+ pfx_buf, new_buf, exist_buf, new_weight,
+ exist_weight);
+ return 0;
+ }
+
+ /* 2. Local preference check. */
+ new_pref = exist_pref = bgp->default_local_pref;
+
+ if (newattr->flag & ATTR_FLAG_BIT(BGP_ATTR_LOCAL_PREF))
+ new_pref = newattr->local_pref;
+ if (existattr->flag & ATTR_FLAG_BIT(BGP_ATTR_LOCAL_PREF))
+ exist_pref = existattr->local_pref;
+
+ if (new_pref > exist_pref) {
+ *reason = bgp_path_selection_local_pref;
+ if (debug)
+ zlog_debug(
+ "%s: %s wins over %s due to localpref %d > %d",
+ pfx_buf, new_buf, exist_buf, new_pref,
+ exist_pref);
+ return 1;
+ }
+
+ if (new_pref < exist_pref) {
+ *reason = bgp_path_selection_local_pref;
+ if (debug)
+ zlog_debug(
+ "%s: %s loses to %s due to localpref %d < %d",
+ pfx_buf, new_buf, exist_buf, new_pref,
+ exist_pref);
+ return 0;
+ }
+
+ /* If a BGP speaker supports ACCEPT_OWN and is configured for the
+ * extensions defined in this document, the following step is inserted
+ * after the LOCAL_PREF comparison step in the BGP decision process:
+ * When comparing a pair of routes for a BGP destination, the
+ * route with the ACCEPT_OWN community attached is preferred over
+ * the route that does not have the community.
+ * This extra step MUST only be invoked during the best path selection
+ * process of VPN-IP routes.
+ */
+ if (safi == SAFI_MPLS_VPN &&
+ (CHECK_FLAG(new->peer->af_flags[afi][safi], PEER_FLAG_ACCEPT_OWN) ||
+ CHECK_FLAG(exist->peer->af_flags[afi][safi],
+ PEER_FLAG_ACCEPT_OWN))) {
+ bool new_accept_own = false;
+ bool exist_accept_own = false;
+ uint32_t accept_own = COMMUNITY_ACCEPT_OWN;
+
+ if (newattr->flag & ATTR_FLAG_BIT(BGP_ATTR_COMMUNITIES))
+ new_accept_own = community_include(
+ bgp_attr_get_community(newattr), accept_own);
+ if (existattr->flag & ATTR_FLAG_BIT(BGP_ATTR_COMMUNITIES))
+ exist_accept_own = community_include(
+ bgp_attr_get_community(existattr), accept_own);
+
+ if (new_accept_own && !exist_accept_own) {
+ *reason = bgp_path_selection_accept_own;
+ if (debug)
+ zlog_debug(
+ "%s: %s wins over %s due to accept-own",
+ pfx_buf, new_buf, exist_buf);
+ return 1;
+ }
+
+ if (!new_accept_own && exist_accept_own) {
+ *reason = bgp_path_selection_accept_own;
+ if (debug)
+ zlog_debug(
+ "%s: %s loses to %s due to accept-own",
+ pfx_buf, new_buf, exist_buf);
+ return 0;
+ }
+ }
+
+ /* Tie-breaker - AIGP (Metric TLV) attribute */
+ if (CHECK_FLAG(newattr->flag, ATTR_FLAG_BIT(BGP_ATTR_AIGP)) &&
+ CHECK_FLAG(existattr->flag, ATTR_FLAG_BIT(BGP_ATTR_AIGP)) &&
+ CHECK_FLAG(bgp->flags, BGP_FLAG_COMPARE_AIGP)) {
+ uint64_t new_aigp = bgp_attr_get_aigp_metric(newattr);
+ uint64_t exist_aigp = bgp_attr_get_aigp_metric(existattr);
+
+ if (new_aigp < exist_aigp) {
+ *reason = bgp_path_selection_aigp;
+ if (debug)
+ zlog_debug(
+ "%s: %s wins over %s due to AIGP %" PRIu64
+ " < %" PRIu64,
+ pfx_buf, new_buf, exist_buf, new_aigp,
+ exist_aigp);
+ return 1;
+ }
+
+ if (new_aigp > exist_aigp) {
+ *reason = bgp_path_selection_aigp;
+ if (debug)
+ zlog_debug(
+ "%s: %s loses to %s due to AIGP %" PRIu64
+ " > %" PRIu64,
+ pfx_buf, new_buf, exist_buf, new_aigp,
+ exist_aigp);
+ return 0;
+ }
+ }
+
+ /* 3. Local route check. We prefer:
+ * - BGP_ROUTE_STATIC
+ * - BGP_ROUTE_AGGREGATE
+ * - BGP_ROUTE_REDISTRIBUTE
+ */
+ new_origin = !(new->sub_type == BGP_ROUTE_NORMAL ||
+ new->sub_type == BGP_ROUTE_IMPORTED);
+ exist_origin = !(exist->sub_type == BGP_ROUTE_NORMAL ||
+ exist->sub_type == BGP_ROUTE_IMPORTED);
+
+ if (new_origin && !exist_origin) {
+ *reason = bgp_path_selection_local_route;
+ if (debug)
+ zlog_debug(
+ "%s: %s wins over %s due to preferred BGP_ROUTE type",
+ pfx_buf, new_buf, exist_buf);
+ return 1;
+ }
+
+ if (!new_origin && exist_origin) {
+ *reason = bgp_path_selection_local_route;
+ if (debug)
+ zlog_debug(
+ "%s: %s loses to %s due to preferred BGP_ROUTE type",
+ pfx_buf, new_buf, exist_buf);
+ return 0;
+ }
+
+ /* Here if these are imported routes then get ultimate pi for
+ * path compare.
+ */
+ new = bgp_get_imported_bpi_ultimate(new);
+ exist = bgp_get_imported_bpi_ultimate(exist);
+ newattr = new->attr;
+ existattr = exist->attr;
+
+ /* 4. AS path length check. */
+ if (!CHECK_FLAG(bgp->flags, BGP_FLAG_ASPATH_IGNORE)) {
+ int exist_hops = aspath_count_hops(existattr->aspath);
+ int exist_confeds = aspath_count_confeds(existattr->aspath);
+
+ if (CHECK_FLAG(bgp->flags, BGP_FLAG_ASPATH_CONFED)) {
+ int aspath_hops;
+
+ aspath_hops = aspath_count_hops(newattr->aspath);
+ aspath_hops += aspath_count_confeds(newattr->aspath);
+
+ if (aspath_hops < (exist_hops + exist_confeds)) {
+ *reason = bgp_path_selection_confed_as_path;
+ if (debug)
+ zlog_debug(
+ "%s: %s wins over %s due to aspath (with confeds) hopcount %d < %d",
+ pfx_buf, new_buf, exist_buf,
+ aspath_hops,
+ (exist_hops + exist_confeds));
+ return 1;
+ }
+
+ if (aspath_hops > (exist_hops + exist_confeds)) {
+ *reason = bgp_path_selection_confed_as_path;
+ if (debug)
+ zlog_debug(
+ "%s: %s loses to %s due to aspath (with confeds) hopcount %d > %d",
+ pfx_buf, new_buf, exist_buf,
+ aspath_hops,
+ (exist_hops + exist_confeds));
+ return 0;
+ }
+ } else {
+ int newhops = aspath_count_hops(newattr->aspath);
+
+ if (newhops < exist_hops) {
+ *reason = bgp_path_selection_as_path;
+ if (debug)
+ zlog_debug(
+ "%s: %s wins over %s due to aspath hopcount %d < %d",
+ pfx_buf, new_buf, exist_buf,
+ newhops, exist_hops);
+ return 1;
+ }
+
+ if (newhops > exist_hops) {
+ *reason = bgp_path_selection_as_path;
+ if (debug)
+ zlog_debug(
+ "%s: %s loses to %s due to aspath hopcount %d > %d",
+ pfx_buf, new_buf, exist_buf,
+ newhops, exist_hops);
+ return 0;
+ }
+ }
+ }
+
+ /* 5. Origin check. */
+ if (newattr->origin < existattr->origin) {
+ *reason = bgp_path_selection_origin;
+ if (debug)
+ zlog_debug("%s: %s wins over %s due to ORIGIN %s < %s",
+ pfx_buf, new_buf, exist_buf,
+ bgp_origin_long_str[newattr->origin],
+ bgp_origin_long_str[existattr->origin]);
+ return 1;
+ }
+
+ if (newattr->origin > existattr->origin) {
+ *reason = bgp_path_selection_origin;
+ if (debug)
+ zlog_debug("%s: %s loses to %s due to ORIGIN %s > %s",
+ pfx_buf, new_buf, exist_buf,
+ bgp_origin_long_str[newattr->origin],
+ bgp_origin_long_str[existattr->origin]);
+ return 0;
+ }
+
+ /* 6. MED check. */
+ internal_as_route = (aspath_count_hops(newattr->aspath) == 0
+ && aspath_count_hops(existattr->aspath) == 0);
+ confed_as_route = (aspath_count_confeds(newattr->aspath) > 0
+ && aspath_count_confeds(existattr->aspath) > 0
+ && aspath_count_hops(newattr->aspath) == 0
+ && aspath_count_hops(existattr->aspath) == 0);
+
+ if (CHECK_FLAG(bgp->flags, BGP_FLAG_ALWAYS_COMPARE_MED)
+ || (CHECK_FLAG(bgp->flags, BGP_FLAG_MED_CONFED) && confed_as_route)
+ || aspath_cmp_left(newattr->aspath, existattr->aspath)
+ || aspath_cmp_left_confed(newattr->aspath, existattr->aspath)
+ || internal_as_route) {
+ new_med = bgp_med_value(new->attr, bgp);
+ exist_med = bgp_med_value(exist->attr, bgp);
+
+ if (new_med < exist_med) {
+ *reason = bgp_path_selection_med;
+ if (debug)
+ zlog_debug(
+ "%s: %s wins over %s due to MED %d < %d",
+ pfx_buf, new_buf, exist_buf, new_med,
+ exist_med);
+ return 1;
+ }
+
+ if (new_med > exist_med) {
+ *reason = bgp_path_selection_med;
+ if (debug)
+ zlog_debug(
+ "%s: %s loses to %s due to MED %d > %d",
+ pfx_buf, new_buf, exist_buf, new_med,
+ exist_med);
+ return 0;
+ }
+ }
+
+ if (exist->sub_type == BGP_ROUTE_IMPORTED) {
+ bpi_ultimate = bgp_get_imported_bpi_ultimate(exist);
+ peer_exist = bpi_ultimate->peer;
+ } else
+ peer_exist = exist->peer;
+
+ if (new->sub_type == BGP_ROUTE_IMPORTED) {
+ bpi_ultimate = bgp_get_imported_bpi_ultimate(new);
+ peer_new = bpi_ultimate->peer;
+ } else
+ peer_new = new->peer;
+
+ /* 7. Peer type check. */
+ new_sort = peer_new->sort;
+ exist_sort = peer_exist->sort;
+
+ if (new_sort == BGP_PEER_EBGP
+ && (exist_sort == BGP_PEER_IBGP || exist_sort == BGP_PEER_CONFED)) {
+ *reason = bgp_path_selection_peer;
+ if (debug)
+ zlog_debug(
+ "%s: %s wins over %s due to eBGP peer > iBGP peer",
+ pfx_buf, new_buf, exist_buf);
+ if (!CHECK_FLAG(bgp->flags, BGP_FLAG_PEERTYPE_MULTIPATH_RELAX))
+ return 1;
+ peer_sort_ret = 1;
+ }
+
+ if (exist_sort == BGP_PEER_EBGP
+ && (new_sort == BGP_PEER_IBGP || new_sort == BGP_PEER_CONFED)) {
+ *reason = bgp_path_selection_peer;
+ if (debug)
+ zlog_debug(
+ "%s: %s loses to %s due to iBGP peer < eBGP peer",
+ pfx_buf, new_buf, exist_buf);
+ if (!CHECK_FLAG(bgp->flags, BGP_FLAG_PEERTYPE_MULTIPATH_RELAX))
+ return 0;
+ peer_sort_ret = 0;
+ }
+
+ /* 8. IGP metric check. */
+ newm = existm = 0;
+
+ if (new->extra)
+ newm = new->extra->igpmetric;
+ if (exist->extra)
+ existm = exist->extra->igpmetric;
+
+ if (newm < existm) {
+ if (debug && peer_sort_ret < 0)
+ zlog_debug(
+ "%s: %s wins over %s due to IGP metric %u < %u",
+ pfx_buf, new_buf, exist_buf, newm, existm);
+ igp_metric_ret = 1;
+ }
+
+ if (newm > existm) {
+ if (debug && peer_sort_ret < 0)
+ zlog_debug(
+ "%s: %s loses to %s due to IGP metric %u > %u",
+ pfx_buf, new_buf, exist_buf, newm, existm);
+ igp_metric_ret = 0;
+ }
+
+ /* 9. Same IGP metric. Compare the cluster list length as
+ representative of IGP hops metric. Rewrite the metric value
+ pair (newm, existm) with the cluster list length. Prefer the
+ path with smaller cluster list length. */
+ if (newm == existm) {
+ if (peer_sort_lookup(peer_new) == BGP_PEER_IBGP &&
+ peer_sort_lookup(peer_exist) == BGP_PEER_IBGP &&
+ (mpath_cfg == NULL || mpath_cfg->same_clusterlen)) {
+ newm = BGP_CLUSTER_LIST_LENGTH(new->attr);
+ existm = BGP_CLUSTER_LIST_LENGTH(exist->attr);
+
+ if (newm < existm) {
+ if (debug && peer_sort_ret < 0)
+ zlog_debug(
+ "%s: %s wins over %s due to CLUSTER_LIST length %u < %u",
+ pfx_buf, new_buf, exist_buf,
+ newm, existm);
+ igp_metric_ret = 1;
+ }
+
+ if (newm > existm) {
+ if (debug && peer_sort_ret < 0)
+ zlog_debug(
+ "%s: %s loses to %s due to CLUSTER_LIST length %u > %u",
+ pfx_buf, new_buf, exist_buf,
+ newm, existm);
+ igp_metric_ret = 0;
+ }
+ }
+ }
+
+ /* 10. confed-external vs. confed-internal */
+ if (CHECK_FLAG(bgp->config, BGP_CONFIG_CONFEDERATION)) {
+ if (new_sort == BGP_PEER_CONFED
+ && exist_sort == BGP_PEER_IBGP) {
+ *reason = bgp_path_selection_confed;
+ if (debug)
+ zlog_debug(
+ "%s: %s wins over %s due to confed-external peer > confed-internal peer",
+ pfx_buf, new_buf, exist_buf);
+ if (!CHECK_FLAG(bgp->flags,
+ BGP_FLAG_PEERTYPE_MULTIPATH_RELAX))
+ return 1;
+ peer_sort_ret = 1;
+ }
+
+ if (exist_sort == BGP_PEER_CONFED
+ && new_sort == BGP_PEER_IBGP) {
+ *reason = bgp_path_selection_confed;
+ if (debug)
+ zlog_debug(
+ "%s: %s loses to %s due to confed-internal peer < confed-external peer",
+ pfx_buf, new_buf, exist_buf);
+ if (!CHECK_FLAG(bgp->flags,
+ BGP_FLAG_PEERTYPE_MULTIPATH_RELAX))
+ return 0;
+ peer_sort_ret = 0;
+ }
+ }
+
+ /* 11. Maximum path check. */
+ if (newm == existm) {
+ /* If one path has a label but the other does not, do not treat
+ * them as equals for multipath
+ */
+ int newl, existl;
+
+ newl = existl = 0;
+
+ if (new->extra)
+ newl = new->extra->num_labels;
+ if (exist->extra)
+ existl = exist->extra->num_labels;
+ if (((new->extra &&bgp_is_valid_label(&new->extra->label[0])) !=
+ (exist->extra &&
+ bgp_is_valid_label(&exist->extra->label[0]))) ||
+ (newl != existl)) {
+ if (debug)
+ zlog_debug(
+ "%s: %s and %s cannot be multipath, one has a label while the other does not",
+ pfx_buf, new_buf, exist_buf);
+ } else if (CHECK_FLAG(bgp->flags,
+ BGP_FLAG_ASPATH_MULTIPATH_RELAX)) {
+
+ /*
+ * For the two paths, all comparison steps till IGP
+ * metric
+ * have succeeded - including AS_PATH hop count. Since
+ * 'bgp
+ * bestpath as-path multipath-relax' knob is on, we
+ * don't need
+ * an exact match of AS_PATH. Thus, mark the paths are
+ * equal.
+ * That will trigger both these paths to get into the
+ * multipath
+ * array.
+ */
+ *paths_eq = 1;
+
+ if (debug)
+ zlog_debug(
+ "%s: %s and %s are equal via multipath-relax",
+ pfx_buf, new_buf, exist_buf);
+ } else if (peer_new->sort == BGP_PEER_IBGP) {
+ if (aspath_cmp(new->attr->aspath,
+ exist->attr->aspath)) {
+ *paths_eq = 1;
+
+ if (debug)
+ zlog_debug(
+ "%s: %s and %s are equal via matching aspaths",
+ pfx_buf, new_buf, exist_buf);
+ }
+ } else if (peer_new->as == peer_exist->as) {
+ *paths_eq = 1;
+
+ if (debug)
+ zlog_debug(
+ "%s: %s and %s are equal via same remote-as",
+ pfx_buf, new_buf, exist_buf);
+ }
+ } else {
+ /*
+ * TODO: If unequal cost ibgp multipath is enabled we can
+ * mark the paths as equal here instead of returning
+ */
+
+ /* Prior to the addition of BGP_FLAG_PEERTYPE_MULTIPATH_RELAX,
+ * if either step 7 or 10 (peer type checks) yielded a winner,
+ * that result was returned immediately. Returning from step 10
+ * ignored the return value computed in steps 8 and 9 (IGP
+ * metric checks). In order to preserve that behavior, if
+ * peer_sort_ret is set, return that rather than igp_metric_ret.
+ */
+ ret = peer_sort_ret;
+ if (peer_sort_ret < 0) {
+ ret = igp_metric_ret;
+ if (debug) {
+ if (ret == 1)
+ zlog_debug(
+ "%s: %s wins over %s after IGP metric comparison",
+ pfx_buf, new_buf, exist_buf);
+ else
+ zlog_debug(
+ "%s: %s loses to %s after IGP metric comparison",
+ pfx_buf, new_buf, exist_buf);
+ }
+ *reason = bgp_path_selection_igp_metric;
+ }
+ return ret;
+ }
+
+ /*
+ * At this point, the decision whether to set *paths_eq = 1 has been
+ * completed. If we deferred returning because of bestpath peer-type
+ * relax configuration, return now.
+ */
+ if (peer_sort_ret >= 0)
+ return peer_sort_ret;
+
+ /* 12. If both paths are external, prefer the path that was received
+ first (the oldest one). This step minimizes route-flap, since a
+ newer path won't displace an older one, even if it was the
+ preferred route based on the additional decision criteria below. */
+ if (!CHECK_FLAG(bgp->flags, BGP_FLAG_COMPARE_ROUTER_ID)
+ && new_sort == BGP_PEER_EBGP && exist_sort == BGP_PEER_EBGP) {
+ if (CHECK_FLAG(new->flags, BGP_PATH_SELECTED)) {
+ *reason = bgp_path_selection_older;
+ if (debug)
+ zlog_debug(
+ "%s: %s wins over %s due to oldest external",
+ pfx_buf, new_buf, exist_buf);
+ return 1;
+ }
+
+ if (CHECK_FLAG(exist->flags, BGP_PATH_SELECTED)) {
+ *reason = bgp_path_selection_older;
+ if (debug)
+ zlog_debug(
+ "%s: %s loses to %s due to oldest external",
+ pfx_buf, new_buf, exist_buf);
+ return 0;
+ }
+ }
+
+ /* 13. Router-ID comparison. */
+ /* If one of the paths is "stale", the corresponding peer router-id will
+ * be 0 and would always win over the other path. If originator id is
+ * used for the comparison, it will decide which path is better.
+ */
+ if (newattr->flag & ATTR_FLAG_BIT(BGP_ATTR_ORIGINATOR_ID))
+ new_id.s_addr = newattr->originator_id.s_addr;
+ else
+ new_id.s_addr = peer_new->remote_id.s_addr;
+ if (existattr->flag & ATTR_FLAG_BIT(BGP_ATTR_ORIGINATOR_ID))
+ exist_id.s_addr = existattr->originator_id.s_addr;
+ else
+ exist_id.s_addr = peer_exist->remote_id.s_addr;
+
+ if (ntohl(new_id.s_addr) < ntohl(exist_id.s_addr)) {
+ *reason = bgp_path_selection_router_id;
+ if (debug)
+ zlog_debug(
+ "%s: %s wins over %s due to Router-ID comparison",
+ pfx_buf, new_buf, exist_buf);
+ return 1;
+ }
+
+ if (ntohl(new_id.s_addr) > ntohl(exist_id.s_addr)) {
+ *reason = bgp_path_selection_router_id;
+ if (debug)
+ zlog_debug(
+ "%s: %s loses to %s due to Router-ID comparison",
+ pfx_buf, new_buf, exist_buf);
+ return 0;
+ }
+
+ /* 14. Cluster length comparison. */
+ new_cluster = BGP_CLUSTER_LIST_LENGTH(new->attr);
+ exist_cluster = BGP_CLUSTER_LIST_LENGTH(exist->attr);
+
+ if (new_cluster < exist_cluster) {
+ *reason = bgp_path_selection_cluster_length;
+ if (debug)
+ zlog_debug(
+ "%s: %s wins over %s due to CLUSTER_LIST length %d < %d",
+ pfx_buf, new_buf, exist_buf, new_cluster,
+ exist_cluster);
+ return 1;
+ }
+
+ if (new_cluster > exist_cluster) {
+ *reason = bgp_path_selection_cluster_length;
+ if (debug)
+ zlog_debug(
+ "%s: %s loses to %s due to CLUSTER_LIST length %d > %d",
+ pfx_buf, new_buf, exist_buf, new_cluster,
+ exist_cluster);
+ return 0;
+ }
+
+ /* 15. Neighbor address comparison. */
+ /* Do this only if neither path is "stale" as stale paths do not have
+ * valid peer information (as the connection may or may not be up).
+ */
+ if (CHECK_FLAG(exist->flags, BGP_PATH_STALE)) {
+ *reason = bgp_path_selection_stale;
+ if (debug)
+ zlog_debug(
+ "%s: %s wins over %s due to latter path being STALE",
+ pfx_buf, new_buf, exist_buf);
+ return 1;
+ }
+
+ if (CHECK_FLAG(new->flags, BGP_PATH_STALE)) {
+ *reason = bgp_path_selection_stale;
+ if (debug)
+ zlog_debug(
+ "%s: %s loses to %s due to former path being STALE",
+ pfx_buf, new_buf, exist_buf);
+ return 0;
+ }
+
+ /* locally configured routes to advertise do not have su_remote */
+ if (peer_new->su_remote == NULL) {
+ *reason = bgp_path_selection_local_configured;
+ return 0;
+ }
+
+ if (peer_exist->su_remote == NULL) {
+ *reason = bgp_path_selection_local_configured;
+ return 1;
+ }
+
+ ret = sockunion_cmp(peer_new->su_remote, peer_exist->su_remote);
+
+ if (ret == 1) {
+ *reason = bgp_path_selection_neighbor_ip;
+ if (debug)
+ zlog_debug(
+ "%s: %s loses to %s due to Neighor IP comparison",
+ pfx_buf, new_buf, exist_buf);
+ return 0;
+ }
+
+ if (ret == -1) {
+ *reason = bgp_path_selection_neighbor_ip;
+ if (debug)
+ zlog_debug(
+ "%s: %s wins over %s due to Neighor IP comparison",
+ pfx_buf, new_buf, exist_buf);
+ return 1;
+ }
+
+ *reason = bgp_path_selection_default;
+ if (debug)
+ zlog_debug("%s: %s wins over %s due to nothing left to compare",
+ pfx_buf, new_buf, exist_buf);
+
+ return 1;
+}
+
+
+int bgp_evpn_path_info_cmp(struct bgp *bgp, struct bgp_path_info *new,
+ struct bgp_path_info *exist, int *paths_eq,
+ bool debug)
+{
+ enum bgp_path_selection_reason reason;
+ char pfx_buf[PREFIX2STR_BUFFER] = {};
+
+ if (debug)
+ prefix2str(bgp_dest_get_prefix(new->net), pfx_buf,
+ sizeof(pfx_buf));
+
+ return bgp_path_info_cmp(bgp, new, exist, paths_eq, NULL, debug,
+ pfx_buf, AFI_L2VPN, SAFI_EVPN, &reason);
+}
+
+/* Compare two bgp route entity. Return -1 if new is preferred, 1 if exist
+ * is preferred, or 0 if they are the same (usually will only occur if
+ * multipath is enabled
+ * This version is compatible with */
+int bgp_path_info_cmp_compatible(struct bgp *bgp, struct bgp_path_info *new,
+ struct bgp_path_info *exist, char *pfx_buf,
+ afi_t afi, safi_t safi,
+ enum bgp_path_selection_reason *reason)
+{
+ int paths_eq;
+ int ret;
+ bool debug = false;
+
+ ret = bgp_path_info_cmp(bgp, new, exist, &paths_eq, NULL, debug,
+ pfx_buf, afi, safi, reason);
+
+ if (paths_eq)
+ ret = 0;
+ else {
+ if (ret == 1)
+ ret = -1;
+ else
+ ret = 1;
+ }
+ return ret;
+}
+
+static enum filter_type bgp_input_filter(struct peer *peer,
+ const struct prefix *p,
+ struct attr *attr, afi_t afi,
+ safi_t safi)
+{
+ struct bgp_filter *filter;
+ enum filter_type ret = FILTER_PERMIT;
+
+ filter = &peer->filter[afi][safi];
+
+#define FILTER_EXIST_WARN(F, f, filter) \
+ if (BGP_DEBUG(update, UPDATE_IN) && !(F##_IN(filter))) \
+ zlog_debug("%s: Could not find configured input %s-list %s!", \
+ peer->host, #f, F##_IN_NAME(filter));
+
+ if (DISTRIBUTE_IN_NAME(filter)) {
+ FILTER_EXIST_WARN(DISTRIBUTE, distribute, filter);
+
+ if (access_list_apply(DISTRIBUTE_IN(filter), p)
+ == FILTER_DENY) {
+ ret = FILTER_DENY;
+ goto done;
+ }
+ }
+
+ if (PREFIX_LIST_IN_NAME(filter)) {
+ FILTER_EXIST_WARN(PREFIX_LIST, prefix, filter);
+
+ if (prefix_list_apply(PREFIX_LIST_IN(filter), p)
+ == PREFIX_DENY) {
+ ret = FILTER_DENY;
+ goto done;
+ }
+ }
+
+ if (FILTER_LIST_IN_NAME(filter)) {
+ FILTER_EXIST_WARN(FILTER_LIST, as, filter);
+
+ if (as_list_apply(FILTER_LIST_IN(filter), attr->aspath)
+ == AS_FILTER_DENY) {
+ ret = FILTER_DENY;
+ goto done;
+ }
+ }
+
+done:
+ if (frrtrace_enabled(frr_bgp, input_filter)) {
+ char pfxprint[PREFIX2STR_BUFFER];
+
+ prefix2str(p, pfxprint, sizeof(pfxprint));
+ frrtrace(5, frr_bgp, input_filter, peer, pfxprint, afi, safi,
+ ret == FILTER_PERMIT ? "permit" : "deny");
+ }
+
+ return ret;
+#undef FILTER_EXIST_WARN
+}
+
+static enum filter_type bgp_output_filter(struct peer *peer,
+ const struct prefix *p,
+ struct attr *attr, afi_t afi,
+ safi_t safi)
+{
+ struct bgp_filter *filter;
+ enum filter_type ret = FILTER_PERMIT;
+
+ filter = &peer->filter[afi][safi];
+
+#define FILTER_EXIST_WARN(F, f, filter) \
+ if (BGP_DEBUG(update, UPDATE_OUT) && !(F##_OUT(filter))) \
+ zlog_debug("%s: Could not find configured output %s-list %s!", \
+ peer->host, #f, F##_OUT_NAME(filter));
+
+ if (DISTRIBUTE_OUT_NAME(filter)) {
+ FILTER_EXIST_WARN(DISTRIBUTE, distribute, filter);
+
+ if (access_list_apply(DISTRIBUTE_OUT(filter), p)
+ == FILTER_DENY) {
+ ret = FILTER_DENY;
+ goto done;
+ }
+ }
+
+ if (PREFIX_LIST_OUT_NAME(filter)) {
+ FILTER_EXIST_WARN(PREFIX_LIST, prefix, filter);
+
+ if (prefix_list_apply(PREFIX_LIST_OUT(filter), p)
+ == PREFIX_DENY) {
+ ret = FILTER_DENY;
+ goto done;
+ }
+ }
+
+ if (FILTER_LIST_OUT_NAME(filter)) {
+ FILTER_EXIST_WARN(FILTER_LIST, as, filter);
+
+ if (as_list_apply(FILTER_LIST_OUT(filter), attr->aspath)
+ == AS_FILTER_DENY) {
+ ret = FILTER_DENY;
+ goto done;
+ }
+ }
+
+ if (frrtrace_enabled(frr_bgp, output_filter)) {
+ char pfxprint[PREFIX2STR_BUFFER];
+
+ prefix2str(p, pfxprint, sizeof(pfxprint));
+ frrtrace(5, frr_bgp, output_filter, peer, pfxprint, afi, safi,
+ ret == FILTER_PERMIT ? "permit" : "deny");
+ }
+
+done:
+ return ret;
+#undef FILTER_EXIST_WARN
+}
+
+/* If community attribute includes no_export then return 1. */
+static bool bgp_community_filter(struct peer *peer, struct attr *attr)
+{
+ if (bgp_attr_get_community(attr)) {
+ /* NO_ADVERTISE check. */
+ if (community_include(bgp_attr_get_community(attr),
+ COMMUNITY_NO_ADVERTISE))
+ return true;
+
+ /* NO_EXPORT check. */
+ if (peer->sort == BGP_PEER_EBGP &&
+ community_include(bgp_attr_get_community(attr),
+ COMMUNITY_NO_EXPORT))
+ return true;
+
+ /* NO_EXPORT_SUBCONFED check. */
+ if (peer->sort == BGP_PEER_EBGP
+ || peer->sort == BGP_PEER_CONFED)
+ if (community_include(bgp_attr_get_community(attr),
+ COMMUNITY_NO_EXPORT_SUBCONFED))
+ return true;
+ }
+ return false;
+}
+
+/* Route reflection loop check. */
+static bool bgp_cluster_filter(struct peer *peer, struct attr *attr)
+{
+ struct in_addr cluster_id;
+ struct cluster_list *cluster = bgp_attr_get_cluster(attr);
+
+ if (cluster) {
+ if (peer->bgp->config & BGP_CONFIG_CLUSTER_ID)
+ cluster_id = peer->bgp->cluster_id;
+ else
+ cluster_id = peer->bgp->router_id;
+
+ if (cluster_loop_check(cluster, cluster_id))
+ return true;
+ }
+ return false;
+}
+
+static bool bgp_otc_filter(struct peer *peer, struct attr *attr)
+{
+ if (attr->flag & ATTR_FLAG_BIT(BGP_ATTR_OTC)) {
+ if (peer->local_role == ROLE_PROVIDER ||
+ peer->local_role == ROLE_RS_SERVER)
+ return true;
+ if (peer->local_role == ROLE_PEER && attr->otc != peer->as)
+ return true;
+ return false;
+ }
+ if (peer->local_role == ROLE_CUSTOMER ||
+ peer->local_role == ROLE_PEER ||
+ peer->local_role == ROLE_RS_CLIENT) {
+ attr->flag |= ATTR_FLAG_BIT(BGP_ATTR_OTC);
+ attr->otc = peer->as;
+ }
+ return false;
+}
+
+static bool bgp_otc_egress(struct peer *peer, struct attr *attr)
+{
+ if (attr->flag & ATTR_FLAG_BIT(BGP_ATTR_OTC)) {
+ if (peer->local_role == ROLE_CUSTOMER ||
+ peer->local_role == ROLE_RS_CLIENT ||
+ peer->local_role == ROLE_PEER)
+ return true;
+ return false;
+ }
+ if (peer->local_role == ROLE_PROVIDER ||
+ peer->local_role == ROLE_PEER ||
+ peer->local_role == ROLE_RS_SERVER) {
+ attr->flag |= ATTR_FLAG_BIT(BGP_ATTR_OTC);
+ attr->otc = peer->bgp->as;
+ }
+ return false;
+}
+
+static bool bgp_check_role_applicability(afi_t afi, safi_t safi)
+{
+ return ((afi == AFI_IP || afi == AFI_IP6) && safi == SAFI_UNICAST);
+}
+
+static int bgp_input_modifier(struct peer *peer, const struct prefix *p,
+ struct attr *attr, afi_t afi, safi_t safi,
+ const char *rmap_name, mpls_label_t *label,
+ uint32_t num_labels, struct bgp_dest *dest)
+{
+ struct bgp_filter *filter;
+ struct bgp_path_info rmap_path = { 0 };
+ struct bgp_path_info_extra extra = { 0 };
+ route_map_result_t ret;
+ struct route_map *rmap = NULL;
+
+ filter = &peer->filter[afi][safi];
+
+ /* Apply default weight value. */
+ if (peer->weight[afi][safi])
+ attr->weight = peer->weight[afi][safi];
+
+ if (rmap_name) {
+ rmap = route_map_lookup_by_name(rmap_name);
+
+ if (rmap == NULL)
+ return RMAP_DENY;
+ } else {
+ if (ROUTE_MAP_IN_NAME(filter)) {
+ rmap = ROUTE_MAP_IN(filter);
+
+ if (rmap == NULL)
+ return RMAP_DENY;
+ }
+ }
+
+ /* Route map apply. */
+ if (rmap) {
+ memset(&rmap_path, 0, sizeof(rmap_path));
+ /* Duplicate current value to new structure for modification. */
+ rmap_path.peer = peer;
+ rmap_path.attr = attr;
+ rmap_path.extra = &extra;
+ rmap_path.net = dest;
+
+ extra.num_labels = num_labels;
+ if (label && num_labels && num_labels <= BGP_MAX_LABELS)
+ memcpy(extra.label, label,
+ num_labels * sizeof(mpls_label_t));
+
+ SET_FLAG(peer->rmap_type, PEER_RMAP_TYPE_IN);
+
+ /* Apply BGP route map to the attribute. */
+ ret = route_map_apply(rmap, p, &rmap_path);
+
+ peer->rmap_type = 0;
+
+ if (ret == RMAP_DENYMATCH)
+ return RMAP_DENY;
+ }
+ return RMAP_PERMIT;
+}
+
+static int bgp_output_modifier(struct peer *peer, const struct prefix *p,
+ struct attr *attr, afi_t afi, safi_t safi,
+ const char *rmap_name)
+{
+ struct bgp_path_info rmap_path;
+ route_map_result_t ret;
+ struct route_map *rmap = NULL;
+ uint8_t rmap_type;
+
+ /*
+ * So if we get to this point and have no rmap_name
+ * we want to just show the output as it currently
+ * exists.
+ */
+ if (!rmap_name)
+ return RMAP_PERMIT;
+
+ /* Apply default weight value. */
+ if (peer->weight[afi][safi])
+ attr->weight = peer->weight[afi][safi];
+
+ rmap = route_map_lookup_by_name(rmap_name);
+
+ /*
+ * If we have a route map name and we do not find
+ * the routemap that means we have an implicit
+ * deny.
+ */
+ if (rmap == NULL)
+ return RMAP_DENY;
+
+ memset(&rmap_path, 0, sizeof(rmap_path));
+ /* Route map apply. */
+ /* Duplicate current value to new structure for modification. */
+ rmap_path.peer = peer;
+ rmap_path.attr = attr;
+
+ rmap_type = peer->rmap_type;
+ SET_FLAG(peer->rmap_type, PEER_RMAP_TYPE_OUT);
+
+ /* Apply BGP route map to the attribute. */
+ ret = route_map_apply(rmap, p, &rmap_path);
+
+ peer->rmap_type = rmap_type;
+
+ if (ret == RMAP_DENYMATCH)
+ /*
+ * caller has multiple error paths with bgp_attr_flush()
+ */
+ return RMAP_DENY;
+
+ return RMAP_PERMIT;
+}
+
+/* If this is an EBGP peer with remove-private-AS */
+static void bgp_peer_remove_private_as(struct bgp *bgp, afi_t afi, safi_t safi,
+ struct peer *peer, struct attr *attr)
+{
+ if (peer->sort == BGP_PEER_EBGP
+ && (peer_af_flag_check(peer, afi, safi,
+ PEER_FLAG_REMOVE_PRIVATE_AS_ALL_REPLACE)
+ || peer_af_flag_check(peer, afi, safi,
+ PEER_FLAG_REMOVE_PRIVATE_AS_REPLACE)
+ || peer_af_flag_check(peer, afi, safi,
+ PEER_FLAG_REMOVE_PRIVATE_AS_ALL)
+ || peer_af_flag_check(peer, afi, safi,
+ PEER_FLAG_REMOVE_PRIVATE_AS))) {
+ // Take action on the entire aspath
+ if (peer_af_flag_check(peer, afi, safi,
+ PEER_FLAG_REMOVE_PRIVATE_AS_ALL_REPLACE)
+ || peer_af_flag_check(peer, afi, safi,
+ PEER_FLAG_REMOVE_PRIVATE_AS_ALL)) {
+ if (peer_af_flag_check(
+ peer, afi, safi,
+ PEER_FLAG_REMOVE_PRIVATE_AS_ALL_REPLACE))
+ attr->aspath = aspath_replace_private_asns(
+ attr->aspath, bgp->as, peer->as);
+
+ /*
+ * Even if the aspath consists of just private ASNs we
+ * need to walk the AS-Path to maintain all instances
+ * of the peer's ASN to break possible loops.
+ */
+ else
+ attr->aspath = aspath_remove_private_asns(
+ attr->aspath, peer->as);
+ }
+
+ // 'all' was not specified so the entire aspath must be private
+ // ASNs
+ // for us to do anything
+ else if (aspath_private_as_check(attr->aspath)) {
+ if (peer_af_flag_check(
+ peer, afi, safi,
+ PEER_FLAG_REMOVE_PRIVATE_AS_REPLACE))
+ attr->aspath = aspath_replace_private_asns(
+ attr->aspath, bgp->as, peer->as);
+ else
+ /*
+ * Walk the aspath to retain any instances of
+ * the peer_asn
+ */
+ attr->aspath = aspath_remove_private_asns(
+ attr->aspath, peer->as);
+ }
+ }
+}
+
+/* If this is an EBGP peer with as-override */
+static void bgp_peer_as_override(struct bgp *bgp, afi_t afi, safi_t safi,
+ struct peer *peer, struct attr *attr)
+{
+ struct aspath *aspath;
+
+ if (peer->sort == BGP_PEER_EBGP &&
+ peer_af_flag_check(peer, afi, safi, PEER_FLAG_AS_OVERRIDE)) {
+ if (attr->aspath->refcnt)
+ aspath = aspath_dup(attr->aspath);
+ else
+ aspath = attr->aspath;
+
+ attr->aspath = aspath_intern(
+ aspath_replace_specific_asn(aspath, peer->as, bgp->as));
+
+ aspath_free(aspath);
+ }
+}
+
+void bgp_attr_add_llgr_community(struct attr *attr)
+{
+ struct community *old;
+ struct community *new;
+ struct community *merge;
+ struct community *llgr;
+
+ old = bgp_attr_get_community(attr);
+ llgr = community_str2com("llgr-stale");
+
+ assert(llgr);
+
+ if (old) {
+ merge = community_merge(community_dup(old), llgr);
+
+ if (old->refcnt == 0)
+ community_free(&old);
+
+ new = community_uniq_sort(merge);
+ community_free(&merge);
+ } else {
+ new = community_dup(llgr);
+ }
+
+ community_free(&llgr);
+
+ bgp_attr_set_community(attr, new);
+}
+
+void bgp_attr_add_gshut_community(struct attr *attr)
+{
+ struct community *old;
+ struct community *new;
+ struct community *merge;
+ struct community *gshut;
+
+ old = bgp_attr_get_community(attr);
+ gshut = community_str2com("graceful-shutdown");
+
+ assert(gshut);
+
+ if (old) {
+ merge = community_merge(community_dup(old), gshut);
+
+ if (old->refcnt == 0)
+ community_free(&old);
+
+ new = community_uniq_sort(merge);
+ community_free(&merge);
+ } else {
+ new = community_dup(gshut);
+ }
+
+ community_free(&gshut);
+ bgp_attr_set_community(attr, new);
+
+ /* When we add the graceful-shutdown community we must also
+ * lower the local-preference */
+ attr->flag |= ATTR_FLAG_BIT(BGP_ATTR_LOCAL_PREF);
+ attr->local_pref = BGP_GSHUT_LOCAL_PREF;
+}
+
+
+/* Notify BGP Conditional advertisement scanner process. */
+void bgp_notify_conditional_adv_scanner(struct update_subgroup *subgrp)
+{
+ struct peer *peer = SUBGRP_PEER(subgrp);
+ afi_t afi = SUBGRP_AFI(subgrp);
+ safi_t safi = SUBGRP_SAFI(subgrp);
+ struct bgp_filter *filter = &peer->filter[afi][safi];
+
+ if (!ADVERTISE_MAP_NAME(filter))
+ return;
+
+ if (!CHECK_FLAG(peer->flags, PEER_FLAG_CONFIG_NODE))
+ return;
+
+ peer->advmap_table_change = true;
+}
+
+
+void subgroup_announce_reset_nhop(uint8_t family, struct attr *attr)
+{
+ if (family == AF_INET) {
+ attr->nexthop.s_addr = INADDR_ANY;
+ attr->mp_nexthop_global_in.s_addr = INADDR_ANY;
+ }
+ if (family == AF_INET6)
+ memset(&attr->mp_nexthop_global, 0, IPV6_MAX_BYTELEN);
+ if (family == AF_EVPN)
+ memset(&attr->mp_nexthop_global_in, 0, BGP_ATTR_NHLEN_IPV4);
+}
+
+bool subgroup_announce_check(struct bgp_dest *dest, struct bgp_path_info *pi,
+ struct update_subgroup *subgrp,
+ const struct prefix *p, struct attr *attr,
+ struct attr *post_attr)
+{
+ struct bgp_filter *filter;
+ struct peer *from;
+ struct peer *peer;
+ struct peer *onlypeer;
+ struct bgp *bgp;
+ struct attr *piattr;
+ route_map_result_t ret;
+ int transparent;
+ int reflect;
+ afi_t afi;
+ safi_t safi;
+ int samepeer_safe = 0; /* for synthetic mplsvpns routes */
+ bool nh_reset = false;
+ uint64_t cum_bw;
+ mpls_label_t label;
+
+ if (DISABLE_BGP_ANNOUNCE)
+ return false;
+
+ afi = SUBGRP_AFI(subgrp);
+ safi = SUBGRP_SAFI(subgrp);
+ peer = SUBGRP_PEER(subgrp);
+ onlypeer = NULL;
+ if (CHECK_FLAG(peer->flags, PEER_FLAG_LONESOUL))
+ onlypeer = SUBGRP_PFIRST(subgrp)->peer;
+
+ from = pi->peer;
+ filter = &peer->filter[afi][safi];
+ bgp = SUBGRP_INST(subgrp);
+ piattr = bgp_path_info_mpath_count(pi) ? bgp_path_info_mpath_attr(pi)
+ : pi->attr;
+
+ if (CHECK_FLAG(peer->af_flags[afi][safi], PEER_FLAG_MAX_PREFIX_OUT) &&
+ peer->pmax_out[afi][safi] != 0 &&
+ subgrp->pscount >= peer->pmax_out[afi][safi]) {
+ if (BGP_DEBUG(update, UPDATE_OUT) ||
+ BGP_DEBUG(update, UPDATE_PREFIX)) {
+ zlog_debug("%s reached maximum prefix to be send (%u)",
+ peer->host, peer->pmax_out[afi][safi]);
+ }
+ return false;
+ }
+
+#ifdef ENABLE_BGP_VNC
+ if (((afi == AFI_IP) || (afi == AFI_IP6)) && (safi == SAFI_MPLS_VPN)
+ && ((pi->type == ZEBRA_ROUTE_BGP_DIRECT)
+ || (pi->type == ZEBRA_ROUTE_BGP_DIRECT_EXT))) {
+
+ /*
+ * direct and direct_ext type routes originate internally even
+ * though they can have peer pointers that reference other
+ * systems
+ */
+ zlog_debug("%s: pfx %pFX bgp_direct->vpn route peer safe",
+ __func__, p);
+ samepeer_safe = 1;
+ }
+#endif
+
+ if (((afi == AFI_IP) || (afi == AFI_IP6))
+ && ((safi == SAFI_MPLS_VPN) || (safi == SAFI_UNICAST))
+ && (pi->type == ZEBRA_ROUTE_BGP)
+ && (pi->sub_type == BGP_ROUTE_IMPORTED)) {
+
+ /* Applies to routes leaked vpn->vrf and vrf->vpn */
+
+ samepeer_safe = 1;
+ }
+
+ /* With addpath we may be asked to TX all kinds of paths so make sure
+ * pi is valid */
+ if (!CHECK_FLAG(pi->flags, BGP_PATH_VALID)
+ || CHECK_FLAG(pi->flags, BGP_PATH_HISTORY)
+ || CHECK_FLAG(pi->flags, BGP_PATH_REMOVED)) {
+ return false;
+ }
+
+ /* If this is not the bestpath then check to see if there is an enabled
+ * addpath
+ * feature that requires us to advertise it */
+ if (!CHECK_FLAG(pi->flags, BGP_PATH_SELECTED))
+ if (!bgp_addpath_capable(pi, peer, afi, safi))
+ return false;
+
+ /* Aggregate-address suppress check. */
+ if (bgp_path_suppressed(pi) && !UNSUPPRESS_MAP_NAME(filter))
+ return false;
+
+ /*
+ * If we are doing VRF 2 VRF leaking via the import
+ * statement, we want to prevent the route going
+ * off box as that the RT and RD created are localy
+ * significant and globaly useless.
+ */
+ if (safi == SAFI_MPLS_VPN && pi->extra && pi->extra->num_labels
+ && pi->extra->label[0] == BGP_PREVENT_VRF_2_VRF_LEAK)
+ return false;
+
+ /* If it's labeled safi, make sure the route has a valid label. */
+ if (safi == SAFI_LABELED_UNICAST) {
+ label = bgp_adv_label(dest, pi, peer, afi, safi);
+ if (!bgp_is_valid_label(&label)) {
+ if (bgp_debug_update(NULL, p, subgrp->update_group, 0))
+ zlog_debug("u%" PRIu64 ":s%" PRIu64
+ " %pFX is filtered - no label (%p)",
+ subgrp->update_group->id, subgrp->id,
+ p, &label);
+ return false;
+ }
+ } else if (safi == SAFI_MPLS_VPN &&
+ CHECK_FLAG(pi->flags, BGP_PATH_MPLSVPN_NH_LABEL_BIND) &&
+ pi->mplsvpn.bmnc.nh_label_bind_cache && peer &&
+ pi->peer != peer && pi->sub_type != BGP_ROUTE_IMPORTED &&
+ pi->sub_type != BGP_ROUTE_STATIC &&
+ bgp_mplsvpn_path_uses_valid_mpls_label(pi) &&
+ bgp_path_info_nexthop_changed(pi, peer, afi)) {
+ /* Redistributed mpls vpn route between distinct
+ * peers from 'pi->peer' to 'to',
+ * and an mpls label is used in this path,
+ * and there is a nh label bind entry,
+ * then get appropriate mpls local label
+ * and check its validity
+ */
+ label = bgp_mplsvpn_nh_label_bind_get_label(pi);
+ if (!bgp_is_valid_label(&label)) {
+ if (bgp_debug_update(NULL, p, subgrp->update_group, 0))
+ zlog_debug("u%" PRIu64 ":s%" PRIu64
+ " %pFX is filtered - no valid label",
+ subgrp->update_group->id, subgrp->id,
+ p);
+ return false;
+ }
+ }
+
+ /* Do not send back route to sender. */
+ if (onlypeer && from == onlypeer) {
+ return false;
+ }
+
+ /* Do not send the default route in the BGP table if the neighbor is
+ * configured for default-originate */
+ if (CHECK_FLAG(peer->af_flags[afi][safi],
+ PEER_FLAG_DEFAULT_ORIGINATE)) {
+ if (p->family == AF_INET && p->u.prefix4.s_addr == INADDR_ANY)
+ return false;
+ else if (p->family == AF_INET6 && p->prefixlen == 0)
+ return false;
+ }
+
+ /* Transparency check. */
+ if (CHECK_FLAG(peer->af_flags[afi][safi], PEER_FLAG_RSERVER_CLIENT)
+ && CHECK_FLAG(from->af_flags[afi][safi], PEER_FLAG_RSERVER_CLIENT))
+ transparent = 1;
+ else
+ transparent = 0;
+
+ /* If community is not disabled check the no-export and local. */
+ if (!transparent && bgp_community_filter(peer, piattr)) {
+ if (bgp_debug_update(NULL, p, subgrp->update_group, 0))
+ zlog_debug("%s: community filter check fail for %pFX",
+ __func__, p);
+ return false;
+ }
+
+ /* If the attribute has originator-id and it is same as remote
+ peer's id. */
+ if (onlypeer && piattr->flag & ATTR_FLAG_BIT(BGP_ATTR_ORIGINATOR_ID)
+ && (IPV4_ADDR_SAME(&onlypeer->remote_id, &piattr->originator_id))) {
+ if (bgp_debug_update(NULL, p, subgrp->update_group, 0))
+ zlog_debug(
+ "%pBP [Update:SEND] %pFX originator-id is same as remote router-id",
+ onlypeer, p);
+ return false;
+ }
+
+ /* ORF prefix-list filter check */
+ if (CHECK_FLAG(peer->af_cap[afi][safi], PEER_CAP_ORF_PREFIX_RM_ADV) &&
+ CHECK_FLAG(peer->af_cap[afi][safi], PEER_CAP_ORF_PREFIX_SM_RCV))
+ if (peer->orf_plist[afi][safi]) {
+ if (prefix_list_apply(peer->orf_plist[afi][safi], p)
+ == PREFIX_DENY) {
+ if (bgp_debug_update(NULL, p,
+ subgrp->update_group, 0))
+ zlog_debug(
+ "%pBP [Update:SEND] %pFX is filtered via ORF",
+ peer, p);
+ return false;
+ }
+ }
+
+ /* Output filter check. */
+ if (bgp_output_filter(peer, p, piattr, afi, safi) == FILTER_DENY) {
+ if (bgp_debug_update(NULL, p, subgrp->update_group, 0))
+ zlog_debug("%pBP [Update:SEND] %pFX is filtered", peer,
+ p);
+ return false;
+ }
+
+ /* AS path loop check. */
+ if (peer->as_path_loop_detection &&
+ aspath_loop_check(piattr->aspath, peer->as)) {
+ if (bgp_debug_update(NULL, p, subgrp->update_group, 0))
+ zlog_debug(
+ "%pBP [Update:SEND] suppress announcement to peer AS %u that is part of AS path.",
+ peer, peer->as);
+ return false;
+ }
+
+ /* If we're a CONFED we need to loop check the CONFED ID too */
+ if (CHECK_FLAG(bgp->config, BGP_CONFIG_CONFEDERATION)) {
+ if (aspath_loop_check_confed(piattr->aspath, bgp->confed_id)) {
+ if (bgp_debug_update(NULL, p, subgrp->update_group, 0))
+ zlog_debug(
+ "%pBP [Update:SEND] suppress announcement to peer AS %u is AS path.",
+ peer, bgp->confed_id);
+ return false;
+ }
+ }
+
+ /* Route-Reflect check. */
+ if (from->sort == BGP_PEER_IBGP && peer->sort == BGP_PEER_IBGP)
+ reflect = 1;
+ else
+ reflect = 0;
+
+ /* IBGP reflection check. */
+ if (reflect && !samepeer_safe) {
+ /* A route from a Client peer. */
+ if (CHECK_FLAG(from->af_flags[afi][safi],
+ PEER_FLAG_REFLECTOR_CLIENT)) {
+ /* Reflect to all the Non-Client peers and also to the
+ Client peers other than the originator. Originator
+ check
+ is already done. So there is noting to do. */
+ /* no bgp client-to-client reflection check. */
+ if (CHECK_FLAG(bgp->flags,
+ BGP_FLAG_NO_CLIENT_TO_CLIENT))
+ if (CHECK_FLAG(peer->af_flags[afi][safi],
+ PEER_FLAG_REFLECTOR_CLIENT))
+ return false;
+ } else {
+ /* A route from a Non-client peer. Reflect to all other
+ clients. */
+ if (!CHECK_FLAG(peer->af_flags[afi][safi],
+ PEER_FLAG_REFLECTOR_CLIENT))
+ return false;
+ }
+ }
+
+ /* For modify attribute, copy it to temporary structure.
+ * post_attr comes from BGP conditional advertisements, where
+ * attributes are already processed by advertise-map route-map,
+ * and this needs to be saved instead of overwriting from the
+ * path attributes.
+ */
+ if (post_attr)
+ *attr = *post_attr;
+ else
+ *attr = *piattr;
+
+ /* don't confuse inbound and outbound setting */
+ RESET_FLAG(attr->rmap_change_flags);
+
+ /* If local-preference is not set. */
+ if ((peer->sort == BGP_PEER_IBGP || peer->sort == BGP_PEER_CONFED)
+ && (!(attr->flag & ATTR_FLAG_BIT(BGP_ATTR_LOCAL_PREF)))) {
+ attr->flag |= ATTR_FLAG_BIT(BGP_ATTR_LOCAL_PREF);
+ attr->local_pref = bgp->default_local_pref;
+ }
+
+ /* If originator-id is not set and the route is to be reflected,
+ set the originator id */
+ if (reflect
+ && (!(attr->flag & ATTR_FLAG_BIT(BGP_ATTR_ORIGINATOR_ID)))) {
+ IPV4_ADDR_COPY(&(attr->originator_id), &(from->remote_id));
+ SET_FLAG(attr->flag, BGP_ATTR_ORIGINATOR_ID);
+ }
+
+ /* Remove MED if its an EBGP peer - will get overwritten by route-maps
+ */
+ if (peer->sort == BGP_PEER_EBGP
+ && attr->flag & ATTR_FLAG_BIT(BGP_ATTR_MULTI_EXIT_DISC)) {
+ if (from != bgp->peer_self && !transparent
+ && !CHECK_FLAG(peer->af_flags[afi][safi],
+ PEER_FLAG_MED_UNCHANGED))
+ attr->flag &=
+ ~(ATTR_FLAG_BIT(BGP_ATTR_MULTI_EXIT_DISC));
+ }
+
+ /* Since the nexthop attribute can vary per peer, it is not explicitly
+ * set
+ * in announce check, only certain flags and length (or number of
+ * nexthops
+ * -- for IPv6/MP_REACH) are set here in order to guide the update
+ * formation
+ * code in setting the nexthop(s) on a per peer basis in
+ * reformat_peer().
+ * Typically, the source nexthop in the attribute is preserved but in
+ * the
+ * scenarios where we know it will always be overwritten, we reset the
+ * nexthop to "0" in an attempt to achieve better Update packing. An
+ * example of this is when a prefix from each of 2 IBGP peers needs to
+ * be
+ * announced to an EBGP peer (and they have the same attributes barring
+ * their nexthop).
+ */
+ if (reflect)
+ SET_FLAG(attr->rmap_change_flags, BATTR_REFLECTED);
+
+#define NEXTHOP_IS_V6 \
+ ((safi != SAFI_ENCAP && safi != SAFI_MPLS_VPN \
+ && (p->family == AF_INET6 || peer_cap_enhe(peer, afi, safi))) \
+ || ((safi == SAFI_ENCAP || safi == SAFI_MPLS_VPN) \
+ && attr->mp_nexthop_len >= IPV6_MAX_BYTELEN))
+
+ /* IPv6/MP starts with 1 nexthop. The link-local address is passed only
+ * if
+ * the peer (group) is configured to receive link-local nexthop
+ * unchanged
+ * and it is available in the prefix OR we're not reflecting the route,
+ * link-local nexthop address is valid and
+ * the peer (group) to whom we're going to announce is on a shared
+ * network
+ * and this is either a self-originated route or the peer is EBGP.
+ * By checking if nexthop LL address is valid we are sure that
+ * we do not announce LL address as `::`.
+ */
+ if (NEXTHOP_IS_V6) {
+ attr->mp_nexthop_len = BGP_ATTR_NHLEN_IPV6_GLOBAL;
+ if ((CHECK_FLAG(peer->af_flags[afi][safi],
+ PEER_FLAG_NEXTHOP_LOCAL_UNCHANGED)
+ && IN6_IS_ADDR_LINKLOCAL(&attr->mp_nexthop_local))
+ || (!reflect && !transparent
+ && IN6_IS_ADDR_LINKLOCAL(&peer->nexthop.v6_local)
+ && peer->shared_network
+ && (from == bgp->peer_self
+ || peer->sort == BGP_PEER_EBGP))) {
+ if (safi == SAFI_MPLS_VPN)
+ attr->mp_nexthop_len =
+ BGP_ATTR_NHLEN_VPNV6_GLOBAL_AND_LL;
+ else
+ attr->mp_nexthop_len =
+ BGP_ATTR_NHLEN_IPV6_GLOBAL_AND_LL;
+ }
+
+ /* Clear off link-local nexthop in source, whenever it is not
+ * needed to
+ * ensure more prefixes share the same attribute for
+ * announcement.
+ */
+ if (!(CHECK_FLAG(peer->af_flags[afi][safi],
+ PEER_FLAG_NEXTHOP_LOCAL_UNCHANGED)))
+ memset(&attr->mp_nexthop_local, 0, IPV6_MAX_BYTELEN);
+ }
+
+ if (bgp_check_role_applicability(afi, safi) &&
+ bgp_otc_egress(peer, attr))
+ return false;
+
+ if (filter->advmap.update_type == UPDATE_TYPE_WITHDRAW &&
+ filter->advmap.aname &&
+ route_map_lookup_by_name(filter->advmap.aname)) {
+ struct bgp_path_info rmap_path = {0};
+ struct bgp_path_info_extra dummy_rmap_path_extra = {0};
+ struct attr dummy_attr = *attr;
+
+ /* Fill temp path_info */
+ prep_for_rmap_apply(&rmap_path, &dummy_rmap_path_extra, dest,
+ pi, peer, &dummy_attr);
+
+ struct route_map *amap =
+ route_map_lookup_by_name(filter->advmap.aname);
+
+ ret = route_map_apply(amap, p, &rmap_path);
+
+ bgp_attr_flush(&dummy_attr);
+
+ /*
+ * The conditional advertisement mode is Withdraw and this
+ * prefix is a conditional prefix. Don't advertise it
+ */
+ if (ret == RMAP_PERMITMATCH)
+ return false;
+ }
+
+ /* Route map & unsuppress-map apply. */
+ if (!post_attr &&
+ (ROUTE_MAP_OUT_NAME(filter) || bgp_path_suppressed(pi))) {
+ struct bgp_path_info rmap_path = {0};
+ struct bgp_path_info_extra dummy_rmap_path_extra = {0};
+ struct attr dummy_attr = {0};
+
+ /* Fill temp path_info */
+ prep_for_rmap_apply(&rmap_path, &dummy_rmap_path_extra, dest,
+ pi, peer, attr);
+ /*
+ * The route reflector is not allowed to modify the attributes
+ * of the reflected IBGP routes unless explicitly allowed.
+ */
+ if ((from->sort == BGP_PEER_IBGP && peer->sort == BGP_PEER_IBGP)
+ && !CHECK_FLAG(bgp->flags,
+ BGP_FLAG_RR_ALLOW_OUTBOUND_POLICY)) {
+ dummy_attr = *attr;
+ rmap_path.attr = &dummy_attr;
+ }
+
+ SET_FLAG(peer->rmap_type, PEER_RMAP_TYPE_OUT);
+
+ if (bgp_path_suppressed(pi))
+ ret = route_map_apply(UNSUPPRESS_MAP(filter), p,
+ &rmap_path);
+ else
+ ret = route_map_apply(ROUTE_MAP_OUT(filter), p,
+ &rmap_path);
+
+ bgp_attr_flush(&dummy_attr);
+ peer->rmap_type = 0;
+
+ if (ret == RMAP_DENYMATCH) {
+ if (bgp_debug_update(NULL, p, subgrp->update_group, 0))
+ zlog_debug(
+ "%pBP [Update:SEND] %pFX is filtered by route-map '%s'",
+ peer, p,
+ bgp_path_suppressed(pi)
+ ? UNSUPPRESS_MAP_NAME(filter)
+ : ROUTE_MAP_OUT_NAME(filter));
+ bgp_attr_flush(rmap_path.attr);
+ return false;
+ }
+ }
+
+ bgp_peer_remove_private_as(bgp, afi, safi, peer, attr);
+ bgp_peer_as_override(bgp, afi, safi, peer, attr);
+
+ /* RFC 8212 to prevent route leaks.
+ * This specification intends to improve this situation by requiring the
+ * explicit configuration of both BGP Import and Export Policies for any
+ * External BGP (EBGP) session such as customers, peers, or
+ * confederation boundaries for all enabled address families. Through
+ * codification of the aforementioned requirement, operators will
+ * benefit from consistent behavior across different BGP
+ * implementations.
+ */
+ if (CHECK_FLAG(bgp->flags, BGP_FLAG_EBGP_REQUIRES_POLICY))
+ if (!bgp_outbound_policy_exists(peer, filter)) {
+ if (monotime_since(&bgp->ebgprequirespolicywarning,
+ NULL) > FIFTEENMINUTE2USEC ||
+ bgp->ebgprequirespolicywarning.tv_sec == 0) {
+ zlog_warn(
+ "EBGP inbound/outbound policy not properly setup, please configure in order for your peering to work correctly");
+ monotime(&bgp->ebgprequirespolicywarning);
+ }
+ return false;
+ }
+
+ /* draft-ietf-idr-deprecate-as-set-confed-set
+ * Filter routes having AS_SET or AS_CONFED_SET in the path.
+ * Eventually, This document (if approved) updates RFC 4271
+ * and RFC 5065 by eliminating AS_SET and AS_CONFED_SET types,
+ * and obsoletes RFC 6472.
+ */
+ if (peer->bgp->reject_as_sets)
+ if (aspath_check_as_sets(attr->aspath))
+ return false;
+
+ /* If neighbor soo is configured, then check if the route has
+ * SoO extended community and validate against the configured
+ * one. If they match, do not announce, to prevent routing
+ * loops.
+ */
+ if ((attr->flag & ATTR_FLAG_BIT(BGP_ATTR_EXT_COMMUNITIES)) &&
+ peer->soo[afi][safi]) {
+ struct ecommunity *ecomm_soo = peer->soo[afi][safi];
+ struct ecommunity *ecomm = bgp_attr_get_ecommunity(attr);
+
+ if ((ecommunity_lookup(ecomm, ECOMMUNITY_ENCODE_AS,
+ ECOMMUNITY_SITE_ORIGIN) ||
+ ecommunity_lookup(ecomm, ECOMMUNITY_ENCODE_AS4,
+ ECOMMUNITY_SITE_ORIGIN) ||
+ ecommunity_lookup(ecomm, ECOMMUNITY_ENCODE_IP,
+ ECOMMUNITY_SITE_ORIGIN)) &&
+ ecommunity_include(ecomm, ecomm_soo)) {
+ if (bgp_debug_update(NULL, p, subgrp->update_group, 0))
+ zlog_debug(
+ "%pBP [Update:SEND] %pFX is filtered by SoO extcommunity '%s'",
+ peer, p, ecommunity_str(ecomm_soo));
+ return false;
+ }
+ }
+
+ /* Codification of AS 0 Processing */
+ if (aspath_check_as_zero(attr->aspath))
+ return false;
+
+ if (bgp_in_graceful_shutdown(bgp)) {
+ if (peer->sort == BGP_PEER_IBGP
+ || peer->sort == BGP_PEER_CONFED) {
+ attr->flag |= ATTR_FLAG_BIT(BGP_ATTR_LOCAL_PREF);
+ attr->local_pref = BGP_GSHUT_LOCAL_PREF;
+ } else {
+ bgp_attr_add_gshut_community(attr);
+ }
+ }
+
+ /* A BGP speaker that has advertised the "Long-lived Graceful Restart
+ * Capability" to a neighbor MUST perform the following upon receiving
+ * a route from that neighbor with the "LLGR_STALE" community, or upon
+ * attaching the "LLGR_STALE" community itself per Section 4.2:
+ *
+ * The route SHOULD NOT be advertised to any neighbor from which the
+ * Long-lived Graceful Restart Capability has not been received.
+ */
+ if (bgp_attr_get_community(attr) &&
+ community_include(bgp_attr_get_community(attr),
+ COMMUNITY_LLGR_STALE) &&
+ !CHECK_FLAG(peer->cap, PEER_CAP_LLGR_RCV) &&
+ !CHECK_FLAG(peer->cap, PEER_CAP_LLGR_ADV))
+ return false;
+
+ /* After route-map has been applied, we check to see if the nexthop to
+ * be carried in the attribute (that is used for the announcement) can
+ * be cleared off or not. We do this in all cases where we would be
+ * setting the nexthop to "ourselves". For IPv6, we only need to
+ * consider
+ * the global nexthop here; the link-local nexthop would have been
+ * cleared
+ * already, and if not, it is required by the update formation code.
+ * Also see earlier comments in this function.
+ */
+ /*
+ * If route-map has performed some operation on the nexthop or the peer
+ * configuration says to pass it unchanged, we cannot reset the nexthop
+ * here, so only attempt to do it if these aren't true. Note that the
+ * route-map handler itself might have cleared the nexthop, if for
+ * example,
+ * it is configured as 'peer-address'.
+ */
+ if (!bgp_rmap_nhop_changed(attr->rmap_change_flags,
+ piattr->rmap_change_flags)
+ && !transparent
+ && !CHECK_FLAG(peer->af_flags[afi][safi],
+ PEER_FLAG_NEXTHOP_UNCHANGED)) {
+ /* We can reset the nexthop, if setting (or forcing) it to
+ * 'self' */
+ if (CHECK_FLAG(peer->af_flags[afi][safi],
+ PEER_FLAG_NEXTHOP_SELF)
+ || CHECK_FLAG(peer->af_flags[afi][safi],
+ PEER_FLAG_FORCE_NEXTHOP_SELF)) {
+ if (!reflect
+ || CHECK_FLAG(peer->af_flags[afi][safi],
+ PEER_FLAG_FORCE_NEXTHOP_SELF)) {
+ subgroup_announce_reset_nhop(
+ (peer_cap_enhe(peer, afi, safi)
+ ? AF_INET6
+ : p->family),
+ attr);
+ nh_reset = true;
+ }
+ } else if (peer->sort == BGP_PEER_EBGP) {
+ /* Can also reset the nexthop if announcing to EBGP, but
+ * only if
+ * no peer in the subgroup is on a shared subnet.
+ * Note: 3rd party nexthop currently implemented for
+ * IPv4 only.
+ */
+ if ((p->family == AF_INET) &&
+ (!bgp_subgrp_multiaccess_check_v4(
+ piattr->nexthop,
+ subgrp, from))) {
+ subgroup_announce_reset_nhop(
+ (peer_cap_enhe(peer, afi, safi)
+ ? AF_INET6
+ : p->family),
+ attr);
+ nh_reset = true;
+ }
+
+ if ((p->family == AF_INET6) &&
+ (!bgp_subgrp_multiaccess_check_v6(
+ piattr->mp_nexthop_global,
+ subgrp, from))) {
+ subgroup_announce_reset_nhop(
+ (peer_cap_enhe(peer, afi, safi)
+ ? AF_INET6
+ : p->family),
+ attr);
+ nh_reset = true;
+ }
+
+
+
+ } else if (CHECK_FLAG(pi->flags, BGP_PATH_ANNC_NH_SELF)) {
+ /*
+ * This flag is used for leaked vpn-vrf routes
+ */
+ int family = p->family;
+
+ if (peer_cap_enhe(peer, afi, safi))
+ family = AF_INET6;
+
+ if (bgp_debug_update(NULL, p, subgrp->update_group, 0))
+ zlog_debug(
+ "%s: %pFX BGP_PATH_ANNC_NH_SELF, family=%s",
+ __func__, p, family2str(family));
+ subgroup_announce_reset_nhop(family, attr);
+ nh_reset = true;
+ }
+ }
+
+ /* If IPv6/MP and nexthop does not have any override and happens
+ * to
+ * be a link-local address, reset it so that we don't pass along
+ * the
+ * source's link-local IPv6 address to recipients who may not be
+ * on
+ * the same interface.
+ */
+ if (p->family == AF_INET6 || peer_cap_enhe(peer, afi, safi)) {
+ if (IN6_IS_ADDR_LINKLOCAL(&attr->mp_nexthop_global)) {
+ subgroup_announce_reset_nhop(AF_INET6, attr);
+ nh_reset = true;
+ }
+ }
+
+ /* If this is an iBGP, send Origin Validation State (OVS)
+ * extended community (rfc8097).
+ */
+ if (peer->sort == BGP_PEER_IBGP) {
+ enum rpki_states rpki_state = RPKI_NOT_BEING_USED;
+
+ rpki_state = hook_call(bgp_rpki_prefix_status, peer, attr, p);
+
+ if (rpki_state != RPKI_NOT_BEING_USED)
+ bgp_attr_set_ecommunity(
+ attr, ecommunity_add_origin_validation_state(
+ rpki_state,
+ bgp_attr_get_ecommunity(attr)));
+ }
+
+ /*
+ * When the next hop is set to ourselves, if all multipaths have
+ * link-bandwidth announce the cumulative bandwidth as that makes
+ * the most sense. However, don't modify if the link-bandwidth has
+ * been explicitly set by user policy.
+ */
+ if (nh_reset &&
+ bgp_path_info_mpath_chkwtd(bgp, pi) &&
+ (cum_bw = bgp_path_info_mpath_cumbw(pi)) != 0 &&
+ !CHECK_FLAG(attr->rmap_change_flags, BATTR_RMAP_LINK_BW_SET))
+ bgp_attr_set_ecommunity(
+ attr,
+ ecommunity_replace_linkbw(
+ bgp->as, bgp_attr_get_ecommunity(attr), cum_bw,
+ CHECK_FLAG(
+ peer->flags,
+ PEER_FLAG_DISABLE_LINK_BW_ENCODING_IEEE)));
+
+ return true;
+}
+
+static void bgp_route_select_timer_expire(struct event *thread)
+{
+ struct afi_safi_info *info;
+ afi_t afi;
+ safi_t safi;
+ struct bgp *bgp;
+
+ info = EVENT_ARG(thread);
+ afi = info->afi;
+ safi = info->safi;
+ bgp = info->bgp;
+
+ bgp->gr_info[afi][safi].t_route_select = NULL;
+ XFREE(MTYPE_TMP, info);
+
+ /* Best path selection */
+ bgp_best_path_select_defer(bgp, afi, safi);
+}
+
+void bgp_best_selection(struct bgp *bgp, struct bgp_dest *dest,
+ struct bgp_maxpaths_cfg *mpath_cfg,
+ struct bgp_path_info_pair *result, afi_t afi,
+ safi_t safi)
+{
+ struct bgp_path_info *new_select;
+ struct bgp_path_info *old_select;
+ struct bgp_path_info *pi;
+ struct bgp_path_info *pi1;
+ struct bgp_path_info *pi2;
+ struct bgp_path_info *nextpi = NULL;
+ int paths_eq, do_mpath;
+ bool debug;
+ struct list mp_list;
+ char pfx_buf[PREFIX2STR_BUFFER] = {};
+ char path_buf[PATH_ADDPATH_STR_BUFFER];
+
+ bgp_mp_list_init(&mp_list);
+ do_mpath =
+ (mpath_cfg->maxpaths_ebgp > 1 || mpath_cfg->maxpaths_ibgp > 1);
+
+ debug = bgp_debug_bestpath(dest);
+
+ if (debug)
+ prefix2str(bgp_dest_get_prefix(dest), pfx_buf, sizeof(pfx_buf));
+
+ dest->reason = bgp_path_selection_none;
+ /* bgp deterministic-med */
+ new_select = NULL;
+ if (CHECK_FLAG(bgp->flags, BGP_FLAG_DETERMINISTIC_MED)) {
+
+ /* Clear BGP_PATH_DMED_SELECTED for all paths */
+ for (pi1 = bgp_dest_get_bgp_path_info(dest); pi1;
+ pi1 = pi1->next)
+ bgp_path_info_unset_flag(dest, pi1,
+ BGP_PATH_DMED_SELECTED);
+
+ for (pi1 = bgp_dest_get_bgp_path_info(dest); pi1;
+ pi1 = pi1->next) {
+ if (CHECK_FLAG(pi1->flags, BGP_PATH_DMED_CHECK))
+ continue;
+ if (BGP_PATH_HOLDDOWN(pi1))
+ continue;
+ if (pi1->peer != bgp->peer_self &&
+ !CHECK_FLAG(pi1->peer->sflags,
+ PEER_STATUS_NSF_WAIT)) {
+ if (!peer_established(pi1->peer->connection))
+ continue;
+ }
+
+ new_select = pi1;
+ if (pi1->next) {
+ for (pi2 = pi1->next; pi2; pi2 = pi2->next) {
+ if (CHECK_FLAG(pi2->flags,
+ BGP_PATH_DMED_CHECK))
+ continue;
+ if (BGP_PATH_HOLDDOWN(pi2))
+ continue;
+ if (pi2->peer != bgp->peer_self &&
+ !CHECK_FLAG(pi2->peer->sflags,
+ PEER_STATUS_NSF_WAIT) &&
+ !peer_established(
+ pi2->peer->connection))
+ continue;
+
+ if (!aspath_cmp_left(pi1->attr->aspath,
+ pi2->attr->aspath)
+ && !aspath_cmp_left_confed(
+ pi1->attr->aspath,
+ pi2->attr->aspath))
+ continue;
+
+ if (bgp_path_info_cmp(
+ bgp, pi2, new_select,
+ &paths_eq, mpath_cfg, debug,
+ pfx_buf, afi, safi,
+ &dest->reason)) {
+ bgp_path_info_unset_flag(
+ dest, new_select,
+ BGP_PATH_DMED_SELECTED);
+ new_select = pi2;
+ }
+
+ bgp_path_info_set_flag(
+ dest, pi2, BGP_PATH_DMED_CHECK);
+ }
+ }
+ bgp_path_info_set_flag(dest, new_select,
+ BGP_PATH_DMED_CHECK);
+ bgp_path_info_set_flag(dest, new_select,
+ BGP_PATH_DMED_SELECTED);
+
+ if (debug) {
+ bgp_path_info_path_with_addpath_rx_str(
+ new_select, path_buf, sizeof(path_buf));
+ zlog_debug(
+ "%pBD(%s): %s is the bestpath from AS %u",
+ dest, bgp->name_pretty, path_buf,
+ aspath_get_first_as(
+ new_select->attr->aspath));
+ }
+ }
+ }
+
+ /* Check old selected route and new selected route. */
+ old_select = NULL;
+ new_select = NULL;
+ for (pi = bgp_dest_get_bgp_path_info(dest);
+ (pi != NULL) && (nextpi = pi->next, 1); pi = nextpi) {
+ enum bgp_path_selection_reason reason;
+
+ if (CHECK_FLAG(pi->flags, BGP_PATH_SELECTED))
+ old_select = pi;
+
+ if (BGP_PATH_HOLDDOWN(pi)) {
+ /* reap REMOVED routes, if needs be
+ * selected route must stay for a while longer though
+ */
+ if (CHECK_FLAG(pi->flags, BGP_PATH_REMOVED) &&
+ (pi != old_select)) {
+ dest = bgp_path_info_reap(dest, pi);
+ assert(dest);
+ }
+
+ if (debug)
+ zlog_debug(
+ "%s: %pBD(%s) pi from %s in holddown",
+ __func__, dest, bgp->name_pretty,
+ pi->peer->host);
+
+ continue;
+ }
+
+ if (pi->peer && pi->peer != bgp->peer_self
+ && !CHECK_FLAG(pi->peer->sflags, PEER_STATUS_NSF_WAIT))
+ if (!peer_established(pi->peer->connection)) {
+ if (debug)
+ zlog_debug(
+ "%s: %pBD(%s) non self peer %s not estab state",
+ __func__, dest,
+ bgp->name_pretty,
+ pi->peer->host);
+
+ continue;
+ }
+
+ if (CHECK_FLAG(bgp->flags, BGP_FLAG_DETERMINISTIC_MED)
+ && (!CHECK_FLAG(pi->flags, BGP_PATH_DMED_SELECTED))) {
+ bgp_path_info_unset_flag(dest, pi, BGP_PATH_DMED_CHECK);
+ if (debug)
+ zlog_debug("%s: %pBD(%s) pi %s dmed", __func__,
+ dest, bgp->name_pretty,
+ pi->peer->host);
+ continue;
+ }
+
+ bgp_path_info_unset_flag(dest, pi, BGP_PATH_DMED_CHECK);
+
+ reason = dest->reason;
+ if (bgp_path_info_cmp(bgp, pi, new_select, &paths_eq, mpath_cfg,
+ debug, pfx_buf, afi, safi,
+ &dest->reason)) {
+ if (new_select == NULL &&
+ reason != bgp_path_selection_none)
+ dest->reason = reason;
+ new_select = pi;
+ }
+ }
+
+ /* Now that we know which path is the bestpath see if any of the other
+ * paths
+ * qualify as multipaths
+ */
+ if (debug) {
+ if (new_select)
+ bgp_path_info_path_with_addpath_rx_str(
+ new_select, path_buf, sizeof(path_buf));
+ else
+ snprintf(path_buf, sizeof(path_buf), "NONE");
+ zlog_debug(
+ "%pBD(%s): After path selection, newbest is %s oldbest was %s",
+ dest, bgp->name_pretty, path_buf,
+ old_select ? old_select->peer->host : "NONE");
+ }
+
+ if (do_mpath && new_select) {
+ for (pi = bgp_dest_get_bgp_path_info(dest);
+ (pi != NULL) && (nextpi = pi->next, 1); pi = nextpi) {
+
+ if (debug)
+ bgp_path_info_path_with_addpath_rx_str(
+ pi, path_buf, sizeof(path_buf));
+
+ if (pi == new_select) {
+ if (debug)
+ zlog_debug(
+ "%pBD(%s): %s is the bestpath, add to the multipath list",
+ dest, bgp->name_pretty,
+ path_buf);
+ bgp_mp_list_add(&mp_list, pi);
+ continue;
+ }
+
+ if (BGP_PATH_HOLDDOWN(pi))
+ continue;
+
+ if (pi->peer && pi->peer != bgp->peer_self
+ && !CHECK_FLAG(pi->peer->sflags,
+ PEER_STATUS_NSF_WAIT))
+ if (!peer_established(pi->peer->connection))
+ continue;
+
+ if (!bgp_path_info_nexthop_cmp(pi, new_select)) {
+ if (debug)
+ zlog_debug(
+ "%pBD(%s): %s has the same nexthop as the bestpath, skip it",
+ dest, bgp->name_pretty,
+ path_buf);
+ continue;
+ }
+
+ bgp_path_info_cmp(bgp, pi, new_select, &paths_eq,
+ mpath_cfg, debug, pfx_buf, afi, safi,
+ &dest->reason);
+
+ if (paths_eq) {
+ if (debug)
+ zlog_debug(
+ "%pBD(%s): %s is equivalent to the bestpath, add to the multipath list",
+ dest, bgp->name_pretty,
+ path_buf);
+ bgp_mp_list_add(&mp_list, pi);
+ }
+ }
+ }
+
+ bgp_path_info_mpath_update(bgp, dest, new_select, old_select, &mp_list,
+ mpath_cfg);
+ bgp_path_info_mpath_aggregate_update(new_select, old_select);
+ bgp_mp_list_clear(&mp_list);
+
+ bgp_addpath_update_ids(bgp, dest, afi, safi);
+
+ result->old = old_select;
+ result->new = new_select;
+
+ return;
+}
+
+/*
+ * A new route/change in bestpath of an existing route. Evaluate the path
+ * for advertisement to the subgroup.
+ */
+void subgroup_process_announce_selected(struct update_subgroup *subgrp,
+ struct bgp_path_info *selected,
+ struct bgp_dest *dest, afi_t afi,
+ safi_t safi, uint32_t addpath_tx_id)
+{
+ const struct prefix *p;
+ struct peer *onlypeer;
+ struct attr attr;
+ struct bgp *bgp;
+ bool advertise;
+
+ p = bgp_dest_get_prefix(dest);
+ bgp = SUBGRP_INST(subgrp);
+ onlypeer = ((SUBGRP_PCOUNT(subgrp) == 1) ? (SUBGRP_PFIRST(subgrp))->peer
+ : NULL);
+
+ if (BGP_DEBUG(update, UPDATE_OUT))
+ zlog_debug("%s: p=%pFX, selected=%p", __func__, p, selected);
+
+ /* First update is deferred until ORF or ROUTE-REFRESH is received */
+ if (onlypeer && CHECK_FLAG(onlypeer->af_sflags[afi][safi],
+ PEER_STATUS_ORF_WAIT_REFRESH))
+ return;
+
+ memset(&attr, 0, sizeof(attr));
+ /* It's initialized in bgp_announce_check() */
+
+ /* Announcement to the subgroup. If the route is filtered withdraw it.
+ * If BGP_NODE_FIB_INSTALL_PENDING is set and data plane install status
+ * is pending (BGP_NODE_FIB_INSTALL_PENDING), do not advertise the
+ * route
+ */
+ advertise = bgp_check_advertise(bgp, dest, safi);
+
+ if (selected) {
+ if (subgroup_announce_check(dest, selected, subgrp, p, &attr,
+ NULL)) {
+ /* Route is selected, if the route is already installed
+ * in FIB, then it is advertised
+ */
+ if (advertise) {
+ if (!bgp_check_withdrawal(bgp, dest, safi)) {
+ struct attr *adv_attr =
+ bgp_attr_intern(&attr);
+
+ bgp_adj_out_set_subgroup(dest, subgrp,
+ adv_attr,
+ selected);
+ } else
+ bgp_adj_out_unset_subgroup(
+ dest, subgrp, 1, addpath_tx_id);
+ }
+ } else
+ bgp_adj_out_unset_subgroup(dest, subgrp, 1,
+ addpath_tx_id);
+ }
+
+ /* If selected is NULL we must withdraw the path using addpath_tx_id */
+ else {
+ bgp_adj_out_unset_subgroup(dest, subgrp, 1, addpath_tx_id);
+ }
+}
+
+/*
+ * Clear IGP changed flag and attribute changed flag for a route (all paths).
+ * This is called at the end of route processing.
+ */
+void bgp_zebra_clear_route_change_flags(struct bgp_dest *dest)
+{
+ struct bgp_path_info *pi;
+
+ for (pi = bgp_dest_get_bgp_path_info(dest); pi; pi = pi->next) {
+ if (BGP_PATH_HOLDDOWN(pi))
+ continue;
+ UNSET_FLAG(pi->flags, BGP_PATH_IGP_CHANGED);
+ UNSET_FLAG(pi->flags, BGP_PATH_ATTR_CHANGED);
+ }
+}
+
+/*
+ * Has the route changed from the RIB's perspective? This is invoked only
+ * if the route selection returns the same best route as earlier - to
+ * determine if we need to update zebra or not.
+ */
+bool bgp_zebra_has_route_changed(struct bgp_path_info *selected)
+{
+ struct bgp_path_info *mpinfo;
+
+ /* If this is multipath, check all selected paths for any nexthop
+ * change or attribute change. Some attribute changes (e.g., community)
+ * aren't of relevance to the RIB, but we'll update zebra to ensure
+ * we handle the case of BGP nexthop change. This is the behavior
+ * when the best path has an attribute change anyway.
+ */
+ if (CHECK_FLAG(selected->flags, BGP_PATH_IGP_CHANGED)
+ || CHECK_FLAG(selected->flags, BGP_PATH_MULTIPATH_CHG)
+ || CHECK_FLAG(selected->flags, BGP_PATH_LINK_BW_CHG))
+ return true;
+
+ /*
+ * If this is multipath, check all selected paths for any nexthop change
+ */
+ for (mpinfo = bgp_path_info_mpath_first(selected); mpinfo;
+ mpinfo = bgp_path_info_mpath_next(mpinfo)) {
+ if (CHECK_FLAG(mpinfo->flags, BGP_PATH_IGP_CHANGED)
+ || CHECK_FLAG(mpinfo->flags, BGP_PATH_ATTR_CHANGED))
+ return true;
+ }
+
+ /* Nothing has changed from the RIB's perspective. */
+ return false;
+}
+
+struct bgp_process_queue {
+ struct bgp *bgp;
+ STAILQ_HEAD(, bgp_dest) pqueue;
+#define BGP_PROCESS_QUEUE_EOIU_MARKER (1 << 0)
+ unsigned int flags;
+ unsigned int queued;
+};
+
+static void bgp_process_evpn_route_injection(struct bgp *bgp, afi_t afi,
+ safi_t safi, struct bgp_dest *dest,
+ struct bgp_path_info *new_select,
+ struct bgp_path_info *old_select)
+{
+ const struct prefix *p = bgp_dest_get_prefix(dest);
+
+ if ((afi != AFI_IP && afi != AFI_IP6) || (safi != SAFI_UNICAST))
+ return;
+
+ if (advertise_type5_routes(bgp, afi) && new_select
+ && is_route_injectable_into_evpn(new_select)) {
+
+ /* apply the route-map */
+ if (bgp->adv_cmd_rmap[afi][safi].map) {
+ route_map_result_t ret;
+ struct bgp_path_info rmap_path;
+ struct bgp_path_info_extra rmap_path_extra;
+ struct attr dummy_attr;
+
+ dummy_attr = *new_select->attr;
+
+ /* Fill temp path_info */
+ prep_for_rmap_apply(&rmap_path, &rmap_path_extra, dest,
+ new_select, new_select->peer,
+ &dummy_attr);
+
+ RESET_FLAG(dummy_attr.rmap_change_flags);
+
+ ret = route_map_apply(bgp->adv_cmd_rmap[afi][safi].map,
+ p, &rmap_path);
+
+ if (ret == RMAP_DENYMATCH) {
+ bgp_attr_flush(&dummy_attr);
+ bgp_evpn_withdraw_type5_route(bgp, p, afi,
+ safi);
+ } else
+ bgp_evpn_advertise_type5_route(
+ bgp, p, &dummy_attr, afi, safi);
+ } else {
+ bgp_evpn_advertise_type5_route(bgp, p, new_select->attr,
+ afi, safi);
+ }
+ } else if (advertise_type5_routes(bgp, afi) && old_select
+ && is_route_injectable_into_evpn(old_select))
+ bgp_evpn_withdraw_type5_route(bgp, p, afi, safi);
+}
+
+/*
+ * Utility to determine whether a particular path_info should use
+ * the IMPLICIT_NULL label. This is pretty specialized: it's only called
+ * in a path where we basically _know_ this is a BGP-LU route.
+ */
+static bool bgp_lu_need_null_label(struct bgp *bgp,
+ const struct bgp_path_info *new_select,
+ afi_t afi, mpls_label_t *label)
+{
+ /* Certain types get imp null; so do paths where the nexthop is
+ * not labeled.
+ */
+ if (new_select->sub_type == BGP_ROUTE_STATIC
+ || new_select->sub_type == BGP_ROUTE_AGGREGATE
+ || new_select->sub_type == BGP_ROUTE_REDISTRIBUTE)
+ goto need_null_label;
+ else if (new_select->extra &&
+ bgp_is_valid_label(&new_select->extra->label[0]))
+ return false;
+need_null_label:
+ if (label == NULL)
+ return true;
+ /* Disable PHP : explicit-null */
+ if (!!CHECK_FLAG(bgp->flags, BGP_FLAG_LU_IPV4_EXPLICIT_NULL) &&
+ afi == AFI_IP)
+ *label = MPLS_LABEL_IPV4_EXPLICIT_NULL;
+ else if (!!CHECK_FLAG(bgp->flags, BGP_FLAG_LU_IPV6_EXPLICIT_NULL) &&
+ afi == AFI_IP6)
+ *label = MPLS_LABEL_IPV6_EXPLICIT_NULL;
+ else
+ /* Enforced PHP popping: implicit-null */
+ *label = MPLS_LABEL_IMPLICIT_NULL;
+
+ return true;
+}
+
+/* Right now, since we only deal with per-prefix labels, it is not
+ * necessary to do this upon changes to best path. Exceptions:
+ * - label index has changed -> recalculate resulting label
+ * - path_info sub_type changed -> switch to/from null label value
+ * - no valid label (due to removed static label binding) -> get new one
+ */
+static void bgp_lu_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)
+{
+ mpls_label_t mpls_label_null;
+
+ if (bgp->allocate_mpls_labels[afi][SAFI_UNICAST]) {
+ if (new_select) {
+ if (!old_select ||
+ bgp_label_index_differs(new_select, old_select) ||
+ new_select->sub_type != old_select->sub_type ||
+ !bgp_is_valid_label(&dest->local_label)) {
+ /* control label imposition for local
+ * routes, aggregate and redistributed
+ * routes
+ */
+ mpls_label_null = MPLS_LABEL_IMPLICIT_NULL;
+ if (bgp_lu_need_null_label(bgp, new_select, afi,
+ &mpls_label_null)) {
+ if (CHECK_FLAG(
+ dest->flags,
+ BGP_NODE_REGISTERED_FOR_LABEL) ||
+ CHECK_FLAG(
+ dest->flags,
+ BGP_NODE_LABEL_REQUESTED))
+ bgp_unregister_for_label(dest);
+ dest->local_label = mpls_lse_encode(
+ mpls_label_null, 0, 0, 1);
+ bgp_set_valid_label(&dest->local_label);
+ } else
+ bgp_register_for_label(dest,
+ new_select);
+ }
+ } else if (CHECK_FLAG(dest->flags,
+ BGP_NODE_REGISTERED_FOR_LABEL) ||
+ CHECK_FLAG(dest->flags, BGP_NODE_LABEL_REQUESTED)) {
+ bgp_unregister_for_label(dest);
+ }
+ } else if (CHECK_FLAG(dest->flags, BGP_NODE_REGISTERED_FOR_LABEL) ||
+ CHECK_FLAG(dest->flags, BGP_NODE_LABEL_REQUESTED)) {
+ bgp_unregister_for_label(dest);
+ }
+}
+
+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
+ *
+ * if (!old_select && new_select)
+ * We are sending new information on.
+ *
+ * if (old_select && new_select) {
+ * if (new_select != old_select)
+ * We have a new best path send a change
+ * else
+ * We've received a update with new attributes that needs
+ * to be passed on.
+ * }
+ *
+ * if (old_select && !new_select)
+ * We have no eligible route that we can announce or the rn
+ * is being removed.
+ */
+static void bgp_process_main_one(struct bgp *bgp, struct bgp_dest *dest,
+ afi_t afi, safi_t safi)
+{
+ struct bgp_path_info *new_select;
+ struct bgp_path_info *old_select;
+ struct bgp_path_info_pair old_and_new;
+ int debug = 0;
+
+ if (CHECK_FLAG(bgp->flags, BGP_FLAG_DELETE_IN_PROGRESS)) {
+ if (dest)
+ debug = bgp_debug_bestpath(dest);
+ if (debug)
+ zlog_debug(
+ "%s: bgp delete in progress, ignoring event, p=%pBD(%s)",
+ __func__, dest, bgp->name_pretty);
+ return;
+ }
+ /* Is it end of initial update? (after startup) */
+ if (!dest) {
+ frr_timestamp(3, bgp->update_delay_zebra_resume_time,
+ sizeof(bgp->update_delay_zebra_resume_time));
+
+ bgp->main_zebra_update_hold = 0;
+ FOREACH_AFI_SAFI (afi, safi) {
+ if (bgp_fibupd_safi(safi))
+ bgp_zebra_announce_table(bgp, afi, safi);
+ }
+ bgp->main_peers_update_hold = 0;
+
+ bgp_start_routeadv(bgp);
+ return;
+ }
+
+ const struct prefix *p = bgp_dest_get_prefix(dest);
+
+ debug = bgp_debug_bestpath(dest);
+ if (debug)
+ zlog_debug("%s: p=%pBD(%s) afi=%s, safi=%s start", __func__,
+ dest, bgp->name_pretty, afi2str(afi),
+ safi2str(safi));
+
+ /* The best path calculation for the route is deferred if
+ * BGP_NODE_SELECT_DEFER is set
+ */
+ if (CHECK_FLAG(dest->flags, BGP_NODE_SELECT_DEFER)) {
+ if (BGP_DEBUG(update, UPDATE_OUT))
+ zlog_debug("SELECT_DEFER flag set for route %p(%s)",
+ dest, bgp->name_pretty);
+ return;
+ }
+
+ /* Best path selection. */
+ bgp_best_selection(bgp, dest, &bgp->maxpaths[afi][safi], &old_and_new,
+ afi, safi);
+ old_select = old_and_new.old;
+ new_select = old_and_new.new;
+
+ if (safi == SAFI_UNICAST || safi == SAFI_LABELED_UNICAST)
+ /* label unicast path :
+ * Do we need to allocate or free labels?
+ */
+ 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(
+ "%s: p=%pBD(%s) afi=%s, safi=%s, old_select=%p, new_select=%p",
+ __func__, dest, bgp->name_pretty, afi2str(afi),
+ safi2str(safi), old_select, new_select);
+
+ /* If best route remains the same and this is not due to user-initiated
+ * clear, see exactly what needs to be done.
+ */
+ if (old_select && old_select == new_select &&
+ !CHECK_FLAG(dest->flags, BGP_NODE_USER_CLEAR) &&
+ !CHECK_FLAG(dest->flags, BGP_NODE_PROCESS_CLEAR) &&
+ !CHECK_FLAG(old_select->flags, BGP_PATH_ATTR_CHANGED) &&
+ !bgp_addpath_is_addpath_used(&bgp->tx_addpath, afi, safi)) {
+ if (bgp_zebra_has_route_changed(old_select)) {
+#ifdef ENABLE_BGP_VNC
+ vnc_import_bgp_add_route(bgp, p, old_select);
+ vnc_import_bgp_exterior_add_route(bgp, p, old_select);
+#endif
+ if (bgp_fibupd_safi(safi)
+ && !bgp_option_check(BGP_OPT_NO_FIB)) {
+
+ if (new_select->type == ZEBRA_ROUTE_BGP
+ && (new_select->sub_type == BGP_ROUTE_NORMAL
+ || new_select->sub_type
+ == BGP_ROUTE_IMPORTED))
+
+ bgp_zebra_announce(dest, p, old_select,
+ bgp, afi, safi);
+ }
+ }
+
+ /* If there is a change of interest to peers, reannounce the
+ * route. */
+ if (CHECK_FLAG(old_select->flags, BGP_PATH_ATTR_CHANGED)
+ || CHECK_FLAG(old_select->flags, BGP_PATH_LINK_BW_CHG)
+ || CHECK_FLAG(dest->flags, BGP_NODE_LABEL_CHANGED)) {
+ group_announce_route(bgp, afi, safi, dest, new_select);
+
+ /* unicast routes must also be annouced to
+ * labeled-unicast update-groups */
+ if (safi == SAFI_UNICAST)
+ group_announce_route(bgp, afi,
+ SAFI_LABELED_UNICAST, dest,
+ new_select);
+
+ UNSET_FLAG(old_select->flags, BGP_PATH_ATTR_CHANGED);
+ UNSET_FLAG(dest->flags, BGP_NODE_LABEL_CHANGED);
+ }
+
+ /* advertise/withdraw type-5 routes */
+ if (CHECK_FLAG(old_select->flags, BGP_PATH_LINK_BW_CHG)
+ || CHECK_FLAG(old_select->flags, BGP_PATH_MULTIPATH_CHG))
+ bgp_process_evpn_route_injection(
+ bgp, afi, safi, dest, old_select, old_select);
+
+ UNSET_FLAG(old_select->flags, BGP_PATH_MULTIPATH_CHG);
+ UNSET_FLAG(old_select->flags, BGP_PATH_LINK_BW_CHG);
+ bgp_zebra_clear_route_change_flags(dest);
+ UNSET_FLAG(dest->flags, BGP_NODE_PROCESS_SCHEDULED);
+ return;
+ }
+
+ /* If the user did "clear ip bgp prefix x.x.x.x" this flag will be set
+ */
+ UNSET_FLAG(dest->flags, BGP_NODE_USER_CLEAR);
+
+ /* If the process wants to force deletion this flag will be set
+ */
+ UNSET_FLAG(dest->flags, BGP_NODE_PROCESS_CLEAR);
+
+ /* bestpath has changed; bump version */
+ if (old_select || new_select) {
+ bgp_bump_version(dest);
+
+ if (!bgp->t_rmap_def_originate_eval)
+ event_add_timer(
+ bm->master,
+ update_group_refresh_default_originate_route_map,
+ bgp, bgp->rmap_def_originate_eval_timer,
+ &bgp->t_rmap_def_originate_eval);
+ }
+
+ if (old_select)
+ bgp_path_info_unset_flag(dest, old_select, BGP_PATH_SELECTED);
+ if (new_select) {
+ if (debug)
+ zlog_debug("%s: setting SELECTED flag", __func__);
+ bgp_path_info_set_flag(dest, new_select, BGP_PATH_SELECTED);
+ bgp_path_info_unset_flag(dest, new_select,
+ BGP_PATH_ATTR_CHANGED);
+ UNSET_FLAG(new_select->flags, BGP_PATH_MULTIPATH_CHG);
+ UNSET_FLAG(new_select->flags, BGP_PATH_LINK_BW_CHG);
+ }
+
+#ifdef ENABLE_BGP_VNC
+ if ((afi == AFI_IP || afi == AFI_IP6) && (safi == SAFI_UNICAST)) {
+ if (old_select != new_select) {
+ if (old_select) {
+ vnc_import_bgp_exterior_del_route(bgp, p,
+ old_select);
+ vnc_import_bgp_del_route(bgp, p, old_select);
+ }
+ if (new_select) {
+ vnc_import_bgp_exterior_add_route(bgp, p,
+ new_select);
+ vnc_import_bgp_add_route(bgp, p, new_select);
+ }
+ }
+ }
+#endif
+
+ group_announce_route(bgp, afi, safi, dest, new_select);
+
+ /* unicast routes must also be annouced to labeled-unicast update-groups
+ */
+ if (safi == SAFI_UNICAST)
+ group_announce_route(bgp, afi, SAFI_LABELED_UNICAST, dest,
+ new_select);
+
+ /* FIB update. */
+ if (bgp_fibupd_safi(safi) && (bgp->inst_type != BGP_INSTANCE_TYPE_VIEW)
+ && !bgp_option_check(BGP_OPT_NO_FIB)) {
+
+ if (new_select && new_select->type == ZEBRA_ROUTE_BGP
+ && (new_select->sub_type == BGP_ROUTE_NORMAL
+ || new_select->sub_type == BGP_ROUTE_AGGREGATE
+ || new_select->sub_type == BGP_ROUTE_IMPORTED)) {
+
+ /* if this is an evpn imported type-5 prefix,
+ * we need to withdraw the route first to clear
+ * the nh neigh and the RMAC entry.
+ */
+ if (old_select &&
+ is_route_parent_evpn(old_select))
+ bgp_zebra_withdraw(p, old_select, bgp, safi);
+
+ bgp_zebra_announce(dest, p, new_select, bgp, afi, safi);
+ } else {
+ /* Withdraw the route from the kernel. */
+ if (old_select && old_select->type == ZEBRA_ROUTE_BGP
+ && (old_select->sub_type == BGP_ROUTE_NORMAL
+ || old_select->sub_type == BGP_ROUTE_AGGREGATE
+ || old_select->sub_type == BGP_ROUTE_IMPORTED))
+
+ bgp_zebra_withdraw(p, old_select, bgp, safi);
+ }
+ }
+
+ bgp_process_evpn_route_injection(bgp, afi, safi, dest, new_select,
+ old_select);
+
+ /* Clear any route change flags. */
+ bgp_zebra_clear_route_change_flags(dest);
+
+ UNSET_FLAG(dest->flags, BGP_NODE_PROCESS_SCHEDULED);
+
+ /* Reap old select bgp_path_info, if it has been removed */
+ if (old_select && CHECK_FLAG(old_select->flags, BGP_PATH_REMOVED))
+ bgp_path_info_reap(dest, old_select);
+
+ return;
+}
+
+/* Process the routes with the flag BGP_NODE_SELECT_DEFER set */
+void bgp_best_path_select_defer(struct bgp *bgp, afi_t afi, safi_t safi)
+{
+ struct bgp_dest *dest;
+ int cnt = 0;
+ struct afi_safi_info *thread_info;
+
+ if (bgp->gr_info[afi][safi].t_route_select) {
+ struct event *t = bgp->gr_info[afi][safi].t_route_select;
+
+ thread_info = EVENT_ARG(t);
+ XFREE(MTYPE_TMP, thread_info);
+ EVENT_OFF(bgp->gr_info[afi][safi].t_route_select);
+ }
+
+ if (BGP_DEBUG(update, UPDATE_OUT)) {
+ zlog_debug("%s: processing route for %s : cnt %d", __func__,
+ get_afi_safi_str(afi, safi, false),
+ bgp->gr_info[afi][safi].gr_deferred);
+ }
+
+ /* Process the route list */
+ for (dest = bgp_table_top(bgp->rib[afi][safi]);
+ dest && bgp->gr_info[afi][safi].gr_deferred != 0 &&
+ cnt < BGP_MAX_BEST_ROUTE_SELECT;
+ dest = bgp_route_next(dest)) {
+ if (!CHECK_FLAG(dest->flags, BGP_NODE_SELECT_DEFER))
+ continue;
+
+ UNSET_FLAG(dest->flags, BGP_NODE_SELECT_DEFER);
+ bgp->gr_info[afi][safi].gr_deferred--;
+ bgp_process_main_one(bgp, dest, afi, safi);
+ cnt++;
+ }
+ /* If iteration stopped before the entire table was traversed then the
+ * node needs to be unlocked.
+ */
+ if (dest) {
+ bgp_dest_unlock_node(dest);
+ dest = NULL;
+ }
+
+ /* Send EOR message when all routes are processed */
+ if (!bgp->gr_info[afi][safi].gr_deferred) {
+ bgp_send_delayed_eor(bgp);
+ /* Send route processing complete message to RIB */
+ bgp_zebra_update(bgp, afi, safi,
+ ZEBRA_CLIENT_ROUTE_UPDATE_COMPLETE);
+ return;
+ }
+
+ thread_info = XMALLOC(MTYPE_TMP, sizeof(struct afi_safi_info));
+
+ thread_info->afi = afi;
+ thread_info->safi = safi;
+ thread_info->bgp = bgp;
+
+ /* If there are more routes to be processed, start the
+ * selection timer
+ */
+ event_add_timer(bm->master, bgp_route_select_timer_expire, thread_info,
+ BGP_ROUTE_SELECT_DELAY,
+ &bgp->gr_info[afi][safi].t_route_select);
+}
+
+static wq_item_status bgp_process_wq(struct work_queue *wq, void *data)
+{
+ struct bgp_process_queue *pqnode = data;
+ struct bgp *bgp = pqnode->bgp;
+ struct bgp_table *table;
+ struct bgp_dest *dest;
+
+ /* eoiu marker */
+ if (CHECK_FLAG(pqnode->flags, BGP_PROCESS_QUEUE_EOIU_MARKER)) {
+ bgp_process_main_one(bgp, NULL, 0, 0);
+ /* should always have dedicated wq call */
+ assert(STAILQ_FIRST(&pqnode->pqueue) == NULL);
+ return WQ_SUCCESS;
+ }
+
+ while (!STAILQ_EMPTY(&pqnode->pqueue)) {
+ dest = STAILQ_FIRST(&pqnode->pqueue);
+ STAILQ_REMOVE_HEAD(&pqnode->pqueue, pq);
+ STAILQ_NEXT(dest, pq) = NULL; /* complete unlink */
+ table = bgp_dest_table(dest);
+ /* note, new DESTs may be added as part of processing */
+ bgp_process_main_one(bgp, dest, table->afi, table->safi);
+
+ bgp_dest_unlock_node(dest);
+ bgp_table_unlock(table);
+ }
+
+ return WQ_SUCCESS;
+}
+
+static void bgp_processq_del(struct work_queue *wq, void *data)
+{
+ struct bgp_process_queue *pqnode = data;
+
+ bgp_unlock(pqnode->bgp);
+
+ XFREE(MTYPE_BGP_PROCESS_QUEUE, pqnode);
+}
+
+void bgp_process_queue_init(struct bgp *bgp)
+{
+ if (!bgp->process_queue) {
+ char name[BUFSIZ];
+
+ snprintf(name, BUFSIZ, "process_queue %s", bgp->name_pretty);
+ bgp->process_queue = work_queue_new(bm->master, name);
+ }
+
+ bgp->process_queue->spec.workfunc = &bgp_process_wq;
+ bgp->process_queue->spec.del_item_data = &bgp_processq_del;
+ bgp->process_queue->spec.max_retries = 0;
+ bgp->process_queue->spec.hold = 50;
+ /* Use a higher yield value of 50ms for main queue processing */
+ bgp->process_queue->spec.yield = 50 * 1000L;
+}
+
+static struct bgp_process_queue *bgp_processq_alloc(struct bgp *bgp)
+{
+ struct bgp_process_queue *pqnode;
+
+ pqnode = XCALLOC(MTYPE_BGP_PROCESS_QUEUE,
+ sizeof(struct bgp_process_queue));
+
+ /* unlocked in bgp_processq_del */
+ pqnode->bgp = bgp_lock(bgp);
+ STAILQ_INIT(&pqnode->pqueue);
+
+ return pqnode;
+}
+
+void bgp_process(struct bgp *bgp, struct bgp_dest *dest, afi_t afi, safi_t safi)
+{
+#define ARBITRARY_PROCESS_QLEN 10000
+ struct work_queue *wq = bgp->process_queue;
+ struct bgp_process_queue *pqnode;
+ int pqnode_reuse = 0;
+
+ /* already scheduled for processing? */
+ if (CHECK_FLAG(dest->flags, BGP_NODE_PROCESS_SCHEDULED))
+ return;
+
+ /* If the flag BGP_NODE_SELECT_DEFER is set, do not add route to
+ * the workqueue
+ */
+ if (CHECK_FLAG(dest->flags, BGP_NODE_SELECT_DEFER)) {
+ if (BGP_DEBUG(update, UPDATE_OUT))
+ zlog_debug("BGP_NODE_SELECT_DEFER set for route %p",
+ dest);
+ return;
+ }
+
+ if (CHECK_FLAG(dest->flags, BGP_NODE_SOFT_RECONFIG)) {
+ if (BGP_DEBUG(update, UPDATE_OUT))
+ zlog_debug(
+ "Soft reconfigure table in progress for route %p",
+ dest);
+ return;
+ }
+
+ if (wq == NULL)
+ return;
+
+ /* Add route nodes to an existing work queue item until reaching the
+ limit only if is from the same BGP view and it's not an EOIU marker
+ */
+ if (work_queue_item_count(wq)) {
+ struct work_queue_item *item = work_queue_last_item(wq);
+ pqnode = item->data;
+
+ if (CHECK_FLAG(pqnode->flags, BGP_PROCESS_QUEUE_EOIU_MARKER)
+ || pqnode->bgp != bgp
+ || pqnode->queued >= ARBITRARY_PROCESS_QLEN)
+ pqnode = bgp_processq_alloc(bgp);
+ else
+ pqnode_reuse = 1;
+ } else
+ pqnode = bgp_processq_alloc(bgp);
+ /* all unlocked in bgp_process_wq */
+ bgp_table_lock(bgp_dest_table(dest));
+
+ SET_FLAG(dest->flags, BGP_NODE_PROCESS_SCHEDULED);
+ bgp_dest_lock_node(dest);
+
+ /* can't be enqueued twice */
+ assert(STAILQ_NEXT(dest, pq) == NULL);
+ STAILQ_INSERT_TAIL(&pqnode->pqueue, dest, pq);
+ pqnode->queued++;
+
+ if (!pqnode_reuse)
+ work_queue_add(wq, pqnode);
+
+ return;
+}
+
+void bgp_add_eoiu_mark(struct bgp *bgp)
+{
+ struct bgp_process_queue *pqnode;
+
+ if (bgp->process_queue == NULL)
+ return;
+
+ pqnode = bgp_processq_alloc(bgp);
+
+ SET_FLAG(pqnode->flags, BGP_PROCESS_QUEUE_EOIU_MARKER);
+ work_queue_add(bgp->process_queue, pqnode);
+}
+
+static void bgp_maximum_prefix_restart_timer(struct event *thread)
+{
+ struct peer_connection *connection = EVENT_ARG(thread);
+ struct peer *peer = connection->peer;
+
+ if (bgp_debug_neighbor_events(peer))
+ zlog_debug(
+ "%s Maximum-prefix restart timer expired, restore peering",
+ peer->host);
+
+ if ((peer_clear(peer, NULL) < 0) && bgp_debug_neighbor_events(peer))
+ zlog_debug("%s: %s peer_clear failed", __func__, peer->host);
+}
+
+static uint32_t bgp_filtered_routes_count(struct peer *peer, afi_t afi,
+ safi_t safi)
+{
+ uint32_t count = 0;
+ bool filtered = false;
+ struct bgp_dest *dest;
+ struct bgp_adj_in *ain;
+ struct attr attr = {};
+ struct bgp_table *table = peer->bgp->rib[afi][safi];
+
+ for (dest = bgp_table_top(table); dest; dest = bgp_route_next(dest)) {
+ for (ain = dest->adj_in; ain; ain = ain->next) {
+ const struct prefix *rn_p = bgp_dest_get_prefix(dest);
+
+ attr = *ain->attr;
+
+ if (bgp_input_filter(peer, rn_p, &attr, afi, safi)
+ == FILTER_DENY)
+ filtered = true;
+
+ if (bgp_input_modifier(
+ peer, rn_p, &attr, afi, safi,
+ ROUTE_MAP_IN_NAME(&peer->filter[afi][safi]),
+ NULL, 0, NULL)
+ == RMAP_DENY)
+ filtered = true;
+
+ if (filtered)
+ count++;
+
+ bgp_attr_flush(&attr);
+ }
+ }
+
+ return count;
+}
+
+bool bgp_maximum_prefix_overflow(struct peer *peer, afi_t afi, safi_t safi,
+ int always)
+{
+ iana_afi_t pkt_afi;
+ iana_safi_t pkt_safi;
+ uint32_t pcount = (CHECK_FLAG(peer->af_flags[afi][safi],
+ PEER_FLAG_MAX_PREFIX_FORCE))
+ ? bgp_filtered_routes_count(peer, afi, safi)
+ + peer->pcount[afi][safi]
+ : peer->pcount[afi][safi];
+ struct peer_connection *connection = peer->connection;
+
+ if (!CHECK_FLAG(peer->af_flags[afi][safi], PEER_FLAG_MAX_PREFIX))
+ return false;
+
+ if (pcount > peer->pmax[afi][safi]) {
+ if (CHECK_FLAG(peer->af_sflags[afi][safi],
+ PEER_STATUS_PREFIX_LIMIT)
+ && !always)
+ return false;
+
+ zlog_info(
+ "%%MAXPFXEXCEED: No. of %s prefix received from %pBP %u exceed, limit %u",
+ get_afi_safi_str(afi, safi, false), peer, pcount,
+ peer->pmax[afi][safi]);
+ SET_FLAG(peer->af_sflags[afi][safi], PEER_STATUS_PREFIX_LIMIT);
+
+ if (CHECK_FLAG(peer->af_flags[afi][safi],
+ PEER_FLAG_MAX_PREFIX_WARNING))
+ return false;
+
+ /* Convert AFI, SAFI to values for packet. */
+ pkt_afi = afi_int2iana(afi);
+ pkt_safi = safi_int2iana(safi);
+ {
+ uint8_t ndata[7];
+
+ ndata[0] = (pkt_afi >> 8);
+ ndata[1] = pkt_afi;
+ ndata[2] = pkt_safi;
+ ndata[3] = (peer->pmax[afi][safi] >> 24);
+ ndata[4] = (peer->pmax[afi][safi] >> 16);
+ ndata[5] = (peer->pmax[afi][safi] >> 8);
+ ndata[6] = (peer->pmax[afi][safi]);
+
+ SET_FLAG(peer->sflags, PEER_STATUS_PREFIX_OVERFLOW);
+ bgp_notify_send_with_data(connection, BGP_NOTIFY_CEASE,
+ BGP_NOTIFY_CEASE_MAX_PREFIX,
+ ndata, 7);
+ }
+
+ /* Dynamic peers will just close their connection. */
+ if (peer_dynamic_neighbor(peer))
+ return true;
+
+ /* restart timer start */
+ if (peer->pmax_restart[afi][safi]) {
+ peer->v_pmax_restart =
+ peer->pmax_restart[afi][safi] * 60;
+
+ if (bgp_debug_neighbor_events(peer))
+ zlog_debug(
+ "%pBP Maximum-prefix restart timer started for %d secs",
+ peer, peer->v_pmax_restart);
+
+ BGP_TIMER_ON(connection->t_pmax_restart,
+ bgp_maximum_prefix_restart_timer,
+ peer->v_pmax_restart);
+ }
+
+ return true;
+ } else
+ UNSET_FLAG(peer->af_sflags[afi][safi],
+ PEER_STATUS_PREFIX_LIMIT);
+
+ if (pcount
+ > (peer->pmax[afi][safi] * peer->pmax_threshold[afi][safi] / 100)) {
+ if (CHECK_FLAG(peer->af_sflags[afi][safi],
+ PEER_STATUS_PREFIX_THRESHOLD)
+ && !always)
+ return false;
+
+ zlog_info(
+ "%%MAXPFX: No. of %s prefix received from %pBP reaches %u, max %u",
+ get_afi_safi_str(afi, safi, false), peer, pcount,
+ peer->pmax[afi][safi]);
+ SET_FLAG(peer->af_sflags[afi][safi],
+ PEER_STATUS_PREFIX_THRESHOLD);
+ } else
+ UNSET_FLAG(peer->af_sflags[afi][safi],
+ PEER_STATUS_PREFIX_THRESHOLD);
+ return false;
+}
+
+/* Unconditionally remove the route from the RIB, without taking
+ * damping into consideration (eg, because the session went down)
+ */
+void bgp_rib_remove(struct bgp_dest *dest, struct bgp_path_info *pi,
+ struct peer *peer, afi_t afi, safi_t safi)
+{
+
+ struct bgp *bgp = NULL;
+ bool delete_route = false;
+
+ bgp_aggregate_decrement(peer->bgp, bgp_dest_get_prefix(dest), pi, afi,
+ safi);
+
+ if (!CHECK_FLAG(pi->flags, BGP_PATH_HISTORY)) {
+ bgp_path_info_delete(dest, pi); /* keep historical info */
+
+ /* If the selected path is removed, reset BGP_NODE_SELECT_DEFER
+ * flag
+ */
+ if (CHECK_FLAG(pi->flags, BGP_PATH_SELECTED))
+ delete_route = true;
+ else if (bgp_dest_set_defer_flag(dest, true) < 0)
+ delete_route = true;
+ if (delete_route) {
+ if (CHECK_FLAG(dest->flags, BGP_NODE_SELECT_DEFER)) {
+ UNSET_FLAG(dest->flags, BGP_NODE_SELECT_DEFER);
+ bgp = pi->peer->bgp;
+ bgp->gr_info[afi][safi].gr_deferred--;
+ }
+ }
+ }
+
+ hook_call(bgp_process, peer->bgp, afi, safi, dest, peer, true);
+ bgp_process(peer->bgp, dest, afi, safi);
+}
+
+static void bgp_rib_withdraw(struct bgp_dest *dest, struct bgp_path_info *pi,
+ struct peer *peer, afi_t afi, safi_t safi,
+ struct prefix_rd *prd)
+{
+ const struct prefix *p = bgp_dest_get_prefix(dest);
+
+ /* apply dampening, if result is suppressed, we'll be retaining
+ * the bgp_path_info in the RIB for historical reference.
+ */
+ if (CHECK_FLAG(peer->bgp->af_flags[afi][safi], BGP_CONFIG_DAMPENING)
+ && peer->sort == BGP_PEER_EBGP)
+ if ((bgp_damp_withdraw(pi, dest, afi, safi, 0))
+ == BGP_DAMP_SUPPRESSED) {
+ bgp_aggregate_decrement(peer->bgp, p, pi, afi,
+ safi);
+ return;
+ }
+
+#ifdef ENABLE_BGP_VNC
+ if (safi == SAFI_MPLS_VPN) {
+ struct bgp_dest *pdest = NULL;
+ struct bgp_table *table = NULL;
+
+ pdest = bgp_node_get(peer->bgp->rib[afi][safi],
+ (struct prefix *)prd);
+ if (bgp_dest_has_bgp_path_info_data(pdest)) {
+ table = bgp_dest_get_bgp_table_info(pdest);
+
+ vnc_import_bgp_del_vnc_host_route_mode_resolve_nve(
+ peer->bgp, prd, table, p, pi);
+ }
+ bgp_dest_unlock_node(pdest);
+ }
+ if ((afi == AFI_IP || afi == AFI_IP6) && (safi == SAFI_UNICAST)) {
+ if (CHECK_FLAG(pi->flags, BGP_PATH_SELECTED)) {
+
+ vnc_import_bgp_del_route(peer->bgp, p, pi);
+ vnc_import_bgp_exterior_del_route(peer->bgp, p, pi);
+ }
+ }
+#endif
+
+ /* If this is an EVPN route, process for un-import. */
+ if (safi == SAFI_EVPN)
+ bgp_evpn_unimport_route(peer->bgp, afi, safi, p, pi);
+
+ bgp_rib_remove(dest, pi, peer, afi, safi);
+}
+
+struct bgp_path_info *info_make(int type, int sub_type, unsigned short instance,
+ struct peer *peer, struct attr *attr,
+ struct bgp_dest *dest)
+{
+ struct bgp_path_info *new;
+
+ /* Make new BGP info. */
+ new = XCALLOC(MTYPE_BGP_ROUTE, sizeof(struct bgp_path_info));
+ new->type = type;
+ new->instance = instance;
+ new->sub_type = sub_type;
+ new->peer = peer;
+ new->attr = attr;
+ new->uptime = monotime(NULL);
+ new->net = dest;
+ return new;
+}
+
+/* Check if received nexthop is valid or not. */
+bool bgp_update_martian_nexthop(struct bgp *bgp, afi_t afi, safi_t safi,
+ uint8_t type, uint8_t stype, struct attr *attr,
+ struct bgp_dest *dest)
+{
+ bool ret = false;
+ bool is_bgp_static_route =
+ (type == ZEBRA_ROUTE_BGP && stype == BGP_ROUTE_STATIC) ? true
+ : false;
+
+ /* If `bgp allow-martian-nexthop` is turned on, return next-hop
+ * as good.
+ */
+ if (bgp->allow_martian)
+ return false;
+
+ /*
+ * Only validated for unicast and multicast currently.
+ * Also valid for EVPN where the nexthop is an IP address.
+ * If we are a bgp static route being checked then there is
+ * no need to check to see if the nexthop is martian as
+ * that it should be ok.
+ */
+ if (is_bgp_static_route ||
+ (safi != SAFI_UNICAST && safi != SAFI_MULTICAST && safi != SAFI_EVPN))
+ return false;
+
+ /* If NEXT_HOP is present, validate it. */
+ if (attr->flag & ATTR_FLAG_BIT(BGP_ATTR_NEXT_HOP)) {
+ if (attr->nexthop.s_addr == INADDR_ANY ||
+ !ipv4_unicast_valid(&attr->nexthop) ||
+ bgp_nexthop_self(bgp, afi, type, stype, attr, dest))
+ return true;
+ }
+
+ /* If MP_NEXTHOP is present, validate it. */
+ /* Note: For IPv6 nexthops, we only validate the global (1st) nexthop;
+ * there is code in bgp_attr.c to ignore the link-local (2nd) nexthop if
+ * it is not an IPv6 link-local address.
+ *
+ * If we receive an UPDATE with nexthop length set to 32 bytes
+ * we shouldn't discard an UPDATE if it's set to (::).
+ * The link-local (2st) is validated along the code path later.
+ */
+ if (attr->mp_nexthop_len) {
+ switch (attr->mp_nexthop_len) {
+ case BGP_ATTR_NHLEN_IPV4:
+ case BGP_ATTR_NHLEN_VPNV4:
+ ret = (attr->mp_nexthop_global_in.s_addr ==
+ INADDR_ANY ||
+ !ipv4_unicast_valid(
+ &attr->mp_nexthop_global_in) ||
+ bgp_nexthop_self(bgp, afi, type, stype, attr,
+ dest));
+ break;
+
+ case BGP_ATTR_NHLEN_IPV6_GLOBAL:
+ case BGP_ATTR_NHLEN_VPNV6_GLOBAL:
+ ret = (IN6_IS_ADDR_UNSPECIFIED(
+ &attr->mp_nexthop_global)
+ || IN6_IS_ADDR_LOOPBACK(&attr->mp_nexthop_global)
+ || IN6_IS_ADDR_MULTICAST(
+ &attr->mp_nexthop_global)
+ || bgp_nexthop_self(bgp, afi, type, stype, attr,
+ dest));
+ break;
+ case BGP_ATTR_NHLEN_IPV6_GLOBAL_AND_LL:
+ ret = (IN6_IS_ADDR_LOOPBACK(&attr->mp_nexthop_global)
+ || IN6_IS_ADDR_MULTICAST(
+ &attr->mp_nexthop_global)
+ || bgp_nexthop_self(bgp, afi, type, stype, attr,
+ dest));
+ break;
+
+ default:
+ ret = true;
+ break;
+ }
+ }
+
+ return ret;
+}
+
+static void bgp_attr_add_no_export_community(struct attr *attr)
+{
+ struct community *old;
+ struct community *new;
+ struct community *merge;
+ struct community *no_export;
+
+ old = bgp_attr_get_community(attr);
+ no_export = community_str2com("no-export");
+
+ assert(no_export);
+
+ if (old) {
+ merge = community_merge(community_dup(old), no_export);
+
+ if (!old->refcnt)
+ community_free(&old);
+
+ new = community_uniq_sort(merge);
+ community_free(&merge);
+ } else {
+ new = community_dup(no_export);
+ }
+
+ community_free(&no_export);
+
+ bgp_attr_set_community(attr, new);
+}
+
+static bool bgp_accept_own(struct peer *peer, afi_t afi, safi_t safi,
+ struct attr *attr, const struct prefix *prefix,
+ int *sub_type)
+{
+ struct listnode *node, *nnode;
+ struct bgp *bgp;
+ bool accept_own_found = false;
+
+ if (safi != SAFI_MPLS_VPN)
+ return false;
+
+ /* Processing of the ACCEPT_OWN community is enabled by configuration */
+ if (!CHECK_FLAG(peer->af_flags[afi][safi], PEER_FLAG_ACCEPT_OWN))
+ return false;
+
+ /* The route in question carries the ACCEPT_OWN community */
+ if (attr->flag & ATTR_FLAG_BIT(BGP_ATTR_COMMUNITIES)) {
+ struct community *comm = bgp_attr_get_community(attr);
+
+ if (community_include(comm, COMMUNITY_ACCEPT_OWN))
+ accept_own_found = true;
+ }
+
+ /* The route in question is targeted to one or more destination VRFs
+ * on the router (as determined by inspecting the Route Target(s)).
+ */
+ for (ALL_LIST_ELEMENTS(bm->bgp, node, nnode, bgp)) {
+ if (bgp->inst_type != BGP_INSTANCE_TYPE_VRF)
+ continue;
+
+ if (accept_own_found &&
+ ecommunity_include(
+ bgp->vpn_policy[afi]
+ .rtlist[BGP_VPN_POLICY_DIR_TOVPN],
+ bgp_attr_get_ecommunity(attr))) {
+ if (bgp_debug_update(peer, prefix, NULL, 1))
+ zlog_debug(
+ "%pBP prefix %pFX has ORIGINATOR_ID, but it's accepted due to ACCEPT_OWN",
+ peer, prefix);
+
+ /* Treat this route as imported, because it's leaked
+ * already from another VRF, and we got an updated
+ * version from route-reflector with ACCEPT_OWN
+ * community.
+ */
+ *sub_type = BGP_ROUTE_IMPORTED;
+
+ return true;
+ }
+ }
+
+ return false;
+}
+
+void bgp_update(struct peer *peer, const struct prefix *p, uint32_t addpath_id,
+ struct attr *attr, afi_t afi, safi_t safi, int type,
+ int sub_type, struct prefix_rd *prd, mpls_label_t *label,
+ uint32_t num_labels, int soft_reconfig,
+ struct bgp_route_evpn *evpn)
+{
+ int ret;
+ int aspath_loop_count = 0;
+ struct bgp_dest *dest;
+ struct bgp *bgp;
+ struct attr new_attr;
+ struct attr *attr_new;
+ struct bgp_path_info *pi;
+ struct bgp_path_info *new = NULL;
+ struct bgp_path_info_extra *extra;
+ const char *reason;
+ char pfx_buf[BGP_PRD_PATH_STRLEN];
+ int connected = 0;
+ int do_loop_check = 1;
+ int has_valid_label = 0;
+ afi_t nh_afi;
+ bool force_evpn_import = false;
+ safi_t orig_safi = safi;
+ int allowas_in = 0;
+
+ if (frrtrace_enabled(frr_bgp, process_update)) {
+ char pfxprint[PREFIX2STR_BUFFER];
+
+ prefix2str(p, pfxprint, sizeof(pfxprint));
+ frrtrace(6, frr_bgp, process_update, peer, pfxprint, addpath_id,
+ afi, safi, attr);
+ }
+
+#ifdef ENABLE_BGP_VNC
+ int vnc_implicit_withdraw = 0;
+#endif
+ int same_attr = 0;
+ const struct prefix *bgp_nht_param_prefix;
+
+ /* Special case for BGP-LU - map LU safi to ordinary unicast safi */
+ if (orig_safi == SAFI_LABELED_UNICAST)
+ safi = SAFI_UNICAST;
+
+ memset(&new_attr, 0, sizeof(new_attr));
+ new_attr.label_index = BGP_INVALID_LABEL_INDEX;
+ new_attr.label = MPLS_INVALID_LABEL;
+
+ bgp = peer->bgp;
+ dest = bgp_afi_node_get(bgp->rib[afi][safi], afi, safi, p, prd);
+ /* TODO: Check to see if we can get rid of "is_valid_label" */
+ if (afi == AFI_L2VPN && safi == SAFI_EVPN)
+ has_valid_label = (num_labels > 0) ? 1 : 0;
+ else
+ has_valid_label = bgp_is_valid_label(label);
+
+ if (has_valid_label)
+ assert(label != NULL);
+
+
+ /* When peer's soft reconfiguration enabled. Record input packet in
+ Adj-RIBs-In. */
+ if (!soft_reconfig &&
+ CHECK_FLAG(peer->af_flags[afi][safi], PEER_FLAG_SOFT_RECONFIG) &&
+ peer != bgp->peer_self) {
+ /*
+ * If the trigger is not from soft_reconfig and if
+ * PEER_FLAG_SOFT_RECONFIG is enabled for the peer, then attr
+ * will not be interned. In which case, it is ok to update the
+ * attr->evpn_overlay, so that, this can be stored in adj_in.
+ */
+ if ((afi == AFI_L2VPN) && evpn) {
+ memcpy(&attr->evpn_overlay, evpn,
+ sizeof(struct bgp_route_evpn));
+ }
+ bgp_adj_in_set(dest, peer, attr, addpath_id);
+ }
+
+ /* Update permitted loop count */
+ if (CHECK_FLAG(peer->af_flags[afi][safi], PEER_FLAG_ALLOWAS_IN))
+ allowas_in = peer->allowas_in[afi][safi];
+
+ /* Check previously received route. */
+ for (pi = bgp_dest_get_bgp_path_info(dest); pi; pi = pi->next)
+ if (pi->peer == peer && pi->type == type
+ && pi->sub_type == sub_type
+ && pi->addpath_rx_id == addpath_id)
+ break;
+
+ /* AS path local-as loop check. */
+ if (peer->change_local_as) {
+ if (allowas_in)
+ aspath_loop_count = allowas_in;
+ else if (!CHECK_FLAG(peer->flags,
+ PEER_FLAG_LOCAL_AS_NO_PREPEND))
+ aspath_loop_count = 1;
+
+ if (aspath_loop_check(attr->aspath, peer->change_local_as)
+ > aspath_loop_count) {
+ peer->stat_pfx_aspath_loop++;
+ reason = "as-path contains our own AS;";
+ goto filtered;
+ }
+ }
+
+ /* If the peer is configured for "allowas-in origin" and the last ASN in
+ * the
+ * as-path is our ASN then we do not need to call aspath_loop_check
+ */
+ if (CHECK_FLAG(peer->af_flags[afi][safi], PEER_FLAG_ALLOWAS_IN_ORIGIN))
+ if (aspath_get_last_as(attr->aspath) == bgp->as)
+ do_loop_check = 0;
+
+ if (CHECK_FLAG(peer->af_flags[afi][safi], PEER_FLAG_REFLECTOR_CLIENT))
+ bgp_nht_param_prefix = NULL;
+ else
+ bgp_nht_param_prefix = p;
+
+ /* AS path loop check. */
+ if (do_loop_check) {
+ if (aspath_loop_check(attr->aspath, bgp->as) >
+ peer->allowas_in[afi][safi]) {
+ peer->stat_pfx_aspath_loop++;
+ reason = "as-path contains our own AS;";
+ goto filtered;
+ }
+ }
+
+ /* If we're a CONFED we need to loop check the CONFED ID too */
+ if (CHECK_FLAG(bgp->config, BGP_CONFIG_CONFEDERATION) && do_loop_check)
+ if (aspath_loop_check_confed(attr->aspath, bgp->confed_id) >
+ peer->allowas_in[afi][safi]) {
+ peer->stat_pfx_aspath_loop++;
+ reason = "as-path contains our own confed AS;";
+ goto filtered;
+ }
+
+ /* Route reflector originator ID check. If ACCEPT_OWN mechanism is
+ * enabled, then take care of that too.
+ */
+ bool accept_own = false;
+
+ if (attr->flag & ATTR_FLAG_BIT(BGP_ATTR_ORIGINATOR_ID)
+ && IPV4_ADDR_SAME(&bgp->router_id, &attr->originator_id)) {
+ accept_own =
+ bgp_accept_own(peer, afi, safi, attr, p, &sub_type);
+ if (!accept_own) {
+ peer->stat_pfx_originator_loop++;
+ reason = "originator is us;";
+ goto filtered;
+ }
+ }
+
+ /* Route reflector cluster ID check. */
+ if (bgp_cluster_filter(peer, attr)) {
+ peer->stat_pfx_cluster_loop++;
+ reason = "reflected from the same cluster;";
+ goto filtered;
+ }
+
+ /* Apply incoming filter. */
+ if (bgp_input_filter(peer, p, attr, afi, orig_safi) == FILTER_DENY) {
+ peer->stat_pfx_filter++;
+ reason = "filter;";
+ goto filtered;
+ }
+
+ if ((afi == AFI_IP || afi == AFI_IP6) && safi == SAFI_MPLS_VPN &&
+ bgp->inst_type == BGP_INSTANCE_TYPE_DEFAULT &&
+ !CHECK_FLAG(bgp->af_flags[afi][safi],
+ BGP_VPNVX_RETAIN_ROUTE_TARGET_ALL) &&
+ vpn_leak_to_vrf_no_retain_filter_check(bgp, attr, afi)) {
+ reason =
+ "no import. Filtered by no bgp retain route-target all";
+ goto filtered;
+ }
+
+ /* If the route has Node Target Extended Communities, check
+ * if it's allowed to be installed locally.
+ */
+ if ((attr->flag & ATTR_FLAG_BIT(BGP_ATTR_EXT_COMMUNITIES))) {
+ struct ecommunity *ecomm = bgp_attr_get_ecommunity(attr);
+
+ if (ecommunity_lookup(ecomm, ECOMMUNITY_ENCODE_IP,
+ ECOMMUNITY_NODE_TARGET) &&
+ !ecommunity_node_target_match(ecomm, &peer->local_id)) {
+ reason =
+ "Node-Target Extended Communities do not contain own BGP Identifier;";
+ goto filtered;
+ }
+ }
+
+ /* RFC 8212 to prevent route leaks.
+ * This specification intends to improve this situation by requiring the
+ * explicit configuration of both BGP Import and Export Policies for any
+ * External BGP (EBGP) session such as customers, peers, or
+ * confederation boundaries for all enabled address families. Through
+ * codification of the aforementioned requirement, operators will
+ * benefit from consistent behavior across different BGP
+ * implementations.
+ */
+ if (CHECK_FLAG(bgp->flags, BGP_FLAG_EBGP_REQUIRES_POLICY))
+ if (!bgp_inbound_policy_exists(peer,
+ &peer->filter[afi][safi])) {
+ reason = "inbound policy missing";
+ if (monotime_since(&bgp->ebgprequirespolicywarning,
+ NULL) > FIFTEENMINUTE2USEC ||
+ bgp->ebgprequirespolicywarning.tv_sec == 0) {
+ zlog_warn(
+ "EBGP inbound/outbound policy not properly setup, please configure in order for your peering to work correctly");
+ monotime(&bgp->ebgprequirespolicywarning);
+ }
+ goto filtered;
+ }
+
+ /* draft-ietf-idr-deprecate-as-set-confed-set
+ * Filter routes having AS_SET or AS_CONFED_SET in the path.
+ * Eventually, This document (if approved) updates RFC 4271
+ * and RFC 5065 by eliminating AS_SET and AS_CONFED_SET types,
+ * and obsoletes RFC 6472.
+ */
+ if (peer->bgp->reject_as_sets)
+ if (aspath_check_as_sets(attr->aspath)) {
+ reason =
+ "as-path contains AS_SET or AS_CONFED_SET type;";
+ goto filtered;
+ }
+
+ new_attr = *attr;
+ /*
+ * If bgp_update is called with soft_reconfig set then
+ * attr is interned. In this case, do not overwrite the
+ * attr->evpn_overlay with evpn directly. Instead memcpy
+ * evpn to new_atr.evpn_overlay before it is interned.
+ */
+ if (soft_reconfig && (afi == AFI_L2VPN) && evpn)
+ memcpy(&new_attr.evpn_overlay, evpn,
+ sizeof(struct bgp_route_evpn));
+
+ /* Apply incoming route-map.
+ * NB: new_attr may now contain newly allocated values from route-map
+ * "set"
+ * commands, so we need bgp_attr_flush in the error paths, until we
+ * intern
+ * the attr (which takes over the memory references) */
+ if (bgp_input_modifier(peer, p, &new_attr, afi, orig_safi, NULL, label,
+ num_labels, dest)
+ == RMAP_DENY) {
+ peer->stat_pfx_filter++;
+ reason = "route-map;";
+ bgp_attr_flush(&new_attr);
+ goto filtered;
+ }
+
+ if (pi && pi->attr->rmap_table_id != new_attr.rmap_table_id) {
+ if (CHECK_FLAG(pi->flags, BGP_PATH_SELECTED))
+ /* remove from RIB previous entry */
+ bgp_zebra_withdraw(p, pi, bgp, safi);
+ }
+
+ if (peer->sort == BGP_PEER_EBGP) {
+
+ /* rfc7999:
+ * A BGP speaker receiving an announcement tagged with the
+ * BLACKHOLE community SHOULD add the NO_ADVERTISE or
+ * NO_EXPORT community as defined in RFC1997, or a
+ * similar community, to prevent propagation of the
+ * prefix outside the local AS. The community to prevent
+ * propagation SHOULD be chosen according to the operator's
+ * routing policy.
+ */
+ if (bgp_attr_get_community(&new_attr) &&
+ community_include(bgp_attr_get_community(&new_attr),
+ COMMUNITY_BLACKHOLE))
+ bgp_attr_add_no_export_community(&new_attr);
+
+ /* If we receive the graceful-shutdown community from an eBGP
+ * peer we must lower local-preference */
+ if (bgp_attr_get_community(&new_attr) &&
+ community_include(bgp_attr_get_community(&new_attr),
+ COMMUNITY_GSHUT)) {
+ new_attr.flag |= ATTR_FLAG_BIT(BGP_ATTR_LOCAL_PREF);
+ new_attr.local_pref = BGP_GSHUT_LOCAL_PREF;
+
+ /* If graceful-shutdown is configured globally or
+ * per neighbor, then add the GSHUT community to
+ * all paths received from eBGP peers. */
+ } else if (bgp_in_graceful_shutdown(peer->bgp) ||
+ CHECK_FLAG(peer->flags, PEER_FLAG_GRACEFUL_SHUTDOWN))
+ bgp_attr_add_gshut_community(&new_attr);
+ }
+
+ /* next hop check. */
+ if (!CHECK_FLAG(peer->flags, PEER_FLAG_IS_RFAPI_HD) &&
+ bgp_update_martian_nexthop(bgp, afi, safi, type, sub_type,
+ &new_attr, dest)) {
+ peer->stat_pfx_nh_invalid++;
+ reason = "martian or self next-hop;";
+ bgp_attr_flush(&new_attr);
+ goto filtered;
+ }
+
+ if (bgp_mac_entry_exists(p) || bgp_mac_exist(&attr->rmac)) {
+ peer->stat_pfx_nh_invalid++;
+ reason = "self mac;";
+ bgp_attr_flush(&new_attr);
+ goto filtered;
+ }
+
+ if (bgp_check_role_applicability(afi, safi) &&
+ bgp_otc_filter(peer, &new_attr)) {
+ reason = "failing otc validation";
+ bgp_attr_flush(&new_attr);
+ goto filtered;
+ }
+
+ /* If neighbor soo is configured, tag all incoming routes with
+ * this SoO tag and then filter out advertisements in
+ * subgroup_announce_check() if it matches the configured SoO
+ * on the other peer.
+ */
+ if (peer->soo[afi][safi]) {
+ struct ecommunity *old_ecomm =
+ bgp_attr_get_ecommunity(&new_attr);
+ struct ecommunity *ecomm_soo = peer->soo[afi][safi];
+ struct ecommunity *new_ecomm;
+
+ if (old_ecomm) {
+ new_ecomm = ecommunity_merge(ecommunity_dup(old_ecomm),
+ ecomm_soo);
+
+ if (!old_ecomm->refcnt)
+ ecommunity_free(&old_ecomm);
+ } else {
+ new_ecomm = ecommunity_dup(ecomm_soo);
+ }
+
+ bgp_attr_set_ecommunity(&new_attr, new_ecomm);
+ }
+
+ attr_new = bgp_attr_intern(&new_attr);
+
+ /* If the update is implicit withdraw. */
+ if (pi) {
+ pi->uptime = monotime(NULL);
+ same_attr = attrhash_cmp(pi->attr, attr_new);
+
+ hook_call(bgp_process, bgp, afi, safi, dest, peer, true);
+
+ /* Same attribute comes in. */
+ if (!CHECK_FLAG(pi->flags, BGP_PATH_REMOVED)
+ && same_attr
+ && (!has_valid_label
+ || memcmp(&(bgp_path_info_extra_get(pi))->label, label,
+ num_labels * sizeof(mpls_label_t))
+ == 0)) {
+ if (CHECK_FLAG(bgp->af_flags[afi][safi],
+ BGP_CONFIG_DAMPENING)
+ && peer->sort == BGP_PEER_EBGP
+ && CHECK_FLAG(pi->flags, BGP_PATH_HISTORY)) {
+ if (bgp_debug_update(peer, p, NULL, 1)) {
+ bgp_debug_rdpfxpath2str(
+ afi, safi, prd, p, label,
+ num_labels, addpath_id ? 1 : 0,
+ addpath_id, evpn, pfx_buf,
+ sizeof(pfx_buf));
+ zlog_debug("%pBP rcvd %s", peer,
+ pfx_buf);
+ }
+
+ if (bgp_damp_update(pi, dest, afi, safi)
+ != BGP_DAMP_SUPPRESSED) {
+ bgp_aggregate_increment(bgp, p, pi, afi,
+ safi);
+ bgp_process(bgp, dest, afi, safi);
+ }
+ } else /* Duplicate - odd */
+ {
+ if (bgp_debug_update(peer, p, NULL, 1)) {
+ if (!peer->rcvd_attr_printed) {
+ zlog_debug(
+ "%pBP rcvd UPDATE w/ attr: %s",
+ peer,
+ peer->rcvd_attr_str);
+ peer->rcvd_attr_printed = 1;
+ }
+
+ bgp_debug_rdpfxpath2str(
+ afi, safi, prd, p, label,
+ num_labels, addpath_id ? 1 : 0,
+ addpath_id, evpn, pfx_buf,
+ sizeof(pfx_buf));
+ zlog_debug(
+ "%pBP rcvd %s...duplicate ignored",
+ peer, pfx_buf);
+ }
+
+ /* graceful restart STALE flag unset. */
+ if (CHECK_FLAG(pi->flags, BGP_PATH_STALE)) {
+ bgp_path_info_unset_flag(
+ dest, pi, BGP_PATH_STALE);
+ bgp_dest_set_defer_flag(dest, false);
+ bgp_process(bgp, dest, afi, safi);
+ }
+ }
+
+ bgp_dest_unlock_node(dest);
+ bgp_attr_unintern(&attr_new);
+
+ return;
+ }
+
+ /* Withdraw/Announce before we fully processed the withdraw */
+ if (CHECK_FLAG(pi->flags, BGP_PATH_REMOVED)) {
+ if (bgp_debug_update(peer, p, NULL, 1)) {
+ bgp_debug_rdpfxpath2str(
+ afi, safi, prd, p, label, num_labels,
+ addpath_id ? 1 : 0, addpath_id, evpn,
+ pfx_buf, sizeof(pfx_buf));
+ zlog_debug(
+ "%pBP rcvd %s, flapped quicker than processing",
+ peer, pfx_buf);
+ }
+
+ bgp_path_info_restore(dest, pi);
+
+ /*
+ * If the BGP_PATH_REMOVED flag is set, then EVPN
+ * routes would have been unimported already when a
+ * prior BGP withdraw processing happened. Such routes
+ * need to be imported again, so flag accordingly.
+ */
+ force_evpn_import = true;
+ } else {
+ /* implicit withdraw, decrement aggregate and pcount
+ * here. only if update is accepted, they'll increment
+ * below.
+ */
+ bgp_aggregate_decrement(bgp, p, pi, afi, safi);
+ }
+
+ /* Received Logging. */
+ if (bgp_debug_update(peer, p, NULL, 1)) {
+ bgp_debug_rdpfxpath2str(afi, safi, prd, p, label,
+ num_labels, addpath_id ? 1 : 0,
+ addpath_id, evpn, pfx_buf,
+ sizeof(pfx_buf));
+ zlog_debug("%pBP rcvd %s", peer, pfx_buf);
+ }
+
+ /* graceful restart STALE flag unset. */
+ if (CHECK_FLAG(pi->flags, BGP_PATH_STALE)) {
+ bgp_path_info_unset_flag(dest, pi, BGP_PATH_STALE);
+ bgp_dest_set_defer_flag(dest, false);
+ }
+
+ /* The attribute is changed. */
+ bgp_path_info_set_flag(dest, pi, BGP_PATH_ATTR_CHANGED);
+
+ /* Update bgp route dampening information. */
+ if (CHECK_FLAG(bgp->af_flags[afi][safi], BGP_CONFIG_DAMPENING)
+ && peer->sort == BGP_PEER_EBGP) {
+ /* This is implicit withdraw so we should update
+ dampening
+ information. */
+ if (!CHECK_FLAG(pi->flags, BGP_PATH_HISTORY))
+ bgp_damp_withdraw(pi, dest, afi, safi, 1);
+ }
+#ifdef ENABLE_BGP_VNC
+ if (safi == SAFI_MPLS_VPN) {
+ struct bgp_dest *pdest = NULL;
+ struct bgp_table *table = NULL;
+
+ pdest = bgp_node_get(bgp->rib[afi][safi],
+ (struct prefix *)prd);
+ if (bgp_dest_has_bgp_path_info_data(pdest)) {
+ table = bgp_dest_get_bgp_table_info(pdest);
+
+ vnc_import_bgp_del_vnc_host_route_mode_resolve_nve(
+ bgp, prd, table, p, pi);
+ }
+ bgp_dest_unlock_node(pdest);
+ }
+ if ((afi == AFI_IP || afi == AFI_IP6)
+ && (safi == SAFI_UNICAST)) {
+ if (CHECK_FLAG(pi->flags, BGP_PATH_SELECTED)) {
+ /*
+ * Implicit withdraw case.
+ */
+ ++vnc_implicit_withdraw;
+ vnc_import_bgp_del_route(bgp, p, pi);
+ vnc_import_bgp_exterior_del_route(bgp, p, pi);
+ }
+ }
+#endif
+
+ /* Special handling for EVPN update of an existing route. If the
+ * extended community attribute has changed, we need to
+ * un-import
+ * the route using its existing extended community. It will be
+ * subsequently processed for import with the new extended
+ * community.
+ */
+ if (((safi == SAFI_EVPN) || (safi == SAFI_MPLS_VPN))
+ && !same_attr) {
+ if ((pi->attr->flag
+ & ATTR_FLAG_BIT(BGP_ATTR_EXT_COMMUNITIES))
+ && (attr_new->flag
+ & ATTR_FLAG_BIT(BGP_ATTR_EXT_COMMUNITIES))) {
+ int cmp;
+
+ cmp = ecommunity_cmp(
+ bgp_attr_get_ecommunity(pi->attr),
+ bgp_attr_get_ecommunity(attr_new));
+ if (!cmp) {
+ if (bgp_debug_update(peer, p, NULL, 1))
+ zlog_debug(
+ "Change in EXT-COMM, existing %s new %s",
+ ecommunity_str(
+ bgp_attr_get_ecommunity(
+ pi->attr)),
+ ecommunity_str(
+ bgp_attr_get_ecommunity(
+ attr_new)));
+ if (safi == SAFI_EVPN)
+ bgp_evpn_unimport_route(
+ bgp, afi, safi, p, pi);
+ else /* SAFI_MPLS_VPN */
+ vpn_leak_to_vrf_withdraw(pi);
+ }
+ }
+ }
+
+ /* Update to new attribute. */
+ bgp_attr_unintern(&pi->attr);
+ pi->attr = attr_new;
+
+ /* Update MPLS label */
+ if (has_valid_label) {
+ extra = bgp_path_info_extra_get(pi);
+ if (extra->label != label) {
+ memcpy(&extra->label, label,
+ num_labels * sizeof(mpls_label_t));
+ extra->num_labels = num_labels;
+ }
+ if (!(afi == AFI_L2VPN && safi == SAFI_EVPN))
+ bgp_set_valid_label(&extra->label[0]);
+ }
+
+#ifdef ENABLE_BGP_VNC
+ if ((afi == AFI_IP || afi == AFI_IP6)
+ && (safi == SAFI_UNICAST)) {
+ if (vnc_implicit_withdraw) {
+ /*
+ * Add back the route with its new attributes
+ * (e.g., nexthop).
+ * The route is still selected, until the route
+ * selection
+ * queued by bgp_process actually runs. We have
+ * to make this
+ * update to the VNC side immediately to avoid
+ * racing against
+ * configuration changes (e.g., route-map
+ * changes) which
+ * trigger re-importation of the entire RIB.
+ */
+ vnc_import_bgp_add_route(bgp, p, pi);
+ vnc_import_bgp_exterior_add_route(bgp, p, pi);
+ }
+ }
+#endif
+
+ /* Update bgp route dampening information. */
+ if (CHECK_FLAG(bgp->af_flags[afi][safi], BGP_CONFIG_DAMPENING)
+ && peer->sort == BGP_PEER_EBGP) {
+ /* Now we do normal update dampening. */
+ ret = bgp_damp_update(pi, dest, afi, safi);
+ if (ret == BGP_DAMP_SUPPRESSED) {
+ bgp_dest_unlock_node(dest);
+ return;
+ }
+ }
+
+ /* Nexthop reachability check - for unicast and
+ * labeled-unicast.. */
+ if (((afi == AFI_IP || afi == AFI_IP6) &&
+ (safi == SAFI_UNICAST || safi == SAFI_LABELED_UNICAST ||
+ (safi == SAFI_MPLS_VPN &&
+ pi->sub_type != BGP_ROUTE_IMPORTED))) ||
+ (safi == SAFI_EVPN &&
+ bgp_evpn_is_prefix_nht_supported(p))) {
+ if (safi != SAFI_EVPN && peer->sort == BGP_PEER_EBGP
+ && peer->ttl == BGP_DEFAULT_TTL
+ && !CHECK_FLAG(peer->flags,
+ PEER_FLAG_DISABLE_CONNECTED_CHECK)
+ && !CHECK_FLAG(bgp->flags,
+ BGP_FLAG_DISABLE_NH_CONNECTED_CHK))
+ connected = 1;
+ else
+ connected = 0;
+
+ struct bgp *bgp_nexthop = bgp;
+
+ if (pi->extra && pi->extra->vrfleak &&
+ pi->extra->vrfleak->bgp_orig)
+ bgp_nexthop = pi->extra->vrfleak->bgp_orig;
+
+ nh_afi = BGP_ATTR_NH_AFI(afi, pi->attr);
+
+ if (bgp_find_or_add_nexthop(bgp, bgp_nexthop, nh_afi,
+ safi, pi, NULL, connected,
+ bgp_nht_param_prefix) ||
+ CHECK_FLAG(peer->flags, PEER_FLAG_IS_RFAPI_HD)) {
+ if (accept_own)
+ bgp_path_info_set_flag(
+ dest, pi, BGP_PATH_ACCEPT_OWN);
+
+ bgp_path_info_set_flag(dest, pi,
+ BGP_PATH_VALID);
+ } else {
+ if (BGP_DEBUG(nht, NHT)) {
+ zlog_debug("%s(%pI4): NH unresolved",
+ __func__,
+ (in_addr_t *)&attr_new->nexthop);
+ }
+ bgp_path_info_unset_flag(dest, pi,
+ BGP_PATH_VALID);
+ }
+ } else {
+ /* case mpls-vpn routes with accept-own community
+ * (which have the BGP_ROUTE_IMPORTED subtype)
+ * case other afi/safi not supporting nexthop tracking
+ */
+ if (accept_own)
+ bgp_path_info_set_flag(dest, pi,
+ BGP_PATH_ACCEPT_OWN);
+ bgp_path_info_set_flag(dest, pi, BGP_PATH_VALID);
+ }
+
+#ifdef ENABLE_BGP_VNC
+ if (safi == SAFI_MPLS_VPN) {
+ struct bgp_dest *pdest = NULL;
+ struct bgp_table *table = NULL;
+
+ pdest = bgp_node_get(bgp->rib[afi][safi],
+ (struct prefix *)prd);
+ if (bgp_dest_has_bgp_path_info_data(pdest)) {
+ table = bgp_dest_get_bgp_table_info(pdest);
+
+ vnc_import_bgp_add_vnc_host_route_mode_resolve_nve(
+ bgp, prd, table, p, pi);
+ }
+ bgp_dest_unlock_node(pdest);
+ }
+#endif
+
+ /* If this is an EVPN route and some attribute has changed,
+ * or we are explicitly told to perform a route import, process
+ * route for import. If the extended community has changed, we
+ * would
+ * have done the un-import earlier and the import would result
+ * in the
+ * route getting injected into appropriate L2 VNIs. If it is
+ * just
+ * some other attribute change, the import will result in
+ * updating
+ * the attributes for the route in the VNI(s).
+ */
+ if (safi == SAFI_EVPN &&
+ (!same_attr || force_evpn_import) &&
+ CHECK_FLAG(pi->flags, BGP_PATH_VALID))
+ bgp_evpn_import_route(bgp, afi, safi, p, pi);
+
+ /* Process change. */
+ bgp_aggregate_increment(bgp, p, pi, afi, safi);
+
+ bgp_process(bgp, dest, afi, safi);
+ bgp_dest_unlock_node(dest);
+
+ if (SAFI_UNICAST == safi
+ && (bgp->inst_type == BGP_INSTANCE_TYPE_VRF
+ || bgp->inst_type == BGP_INSTANCE_TYPE_DEFAULT)) {
+
+ vpn_leak_from_vrf_update(bgp_get_default(), bgp, pi);
+ }
+ if ((SAFI_MPLS_VPN == safi)
+ && (bgp->inst_type == BGP_INSTANCE_TYPE_DEFAULT)) {
+ vpn_leak_to_vrf_update(bgp, pi, prd);
+ }
+
+#ifdef ENABLE_BGP_VNC
+ if (SAFI_MPLS_VPN == safi) {
+ mpls_label_t label_decoded = decode_label(label);
+
+ rfapiProcessUpdate(peer, NULL, p, prd, attr, afi, safi,
+ type, sub_type, &label_decoded);
+ }
+ if (SAFI_ENCAP == safi) {
+ rfapiProcessUpdate(peer, NULL, p, prd, attr, afi, safi,
+ type, sub_type, NULL);
+ }
+#endif
+ return;
+ } // End of implicit withdraw
+
+ /* Received Logging. */
+ if (bgp_debug_update(peer, p, NULL, 1)) {
+ if (!peer->rcvd_attr_printed) {
+ zlog_debug("%pBP rcvd UPDATE w/ attr: %s", peer,
+ peer->rcvd_attr_str);
+ peer->rcvd_attr_printed = 1;
+ }
+
+ bgp_debug_rdpfxpath2str(afi, safi, prd, p, label, num_labels,
+ addpath_id ? 1 : 0, addpath_id, evpn,
+ pfx_buf, sizeof(pfx_buf));
+ zlog_debug("%pBP rcvd %s", peer, pfx_buf);
+ }
+
+ /* Make new BGP info. */
+ new = info_make(type, sub_type, 0, peer, attr_new, dest);
+
+ /* Update MPLS label */
+ if (has_valid_label) {
+ extra = bgp_path_info_extra_get(new);
+ if (extra->label != label) {
+ memcpy(&extra->label, label,
+ num_labels * sizeof(mpls_label_t));
+ extra->num_labels = num_labels;
+ }
+ if (!(afi == AFI_L2VPN && safi == SAFI_EVPN))
+ bgp_set_valid_label(&extra->label[0]);
+ }
+
+ /* Nexthop reachability check. */
+ if (((afi == AFI_IP || afi == AFI_IP6) &&
+ (safi == SAFI_UNICAST || safi == SAFI_LABELED_UNICAST ||
+ (safi == SAFI_MPLS_VPN &&
+ new->sub_type != BGP_ROUTE_IMPORTED))) ||
+ (safi == SAFI_EVPN && bgp_evpn_is_prefix_nht_supported(p))) {
+ if (safi != SAFI_EVPN && peer->sort == BGP_PEER_EBGP
+ && peer->ttl == BGP_DEFAULT_TTL
+ && !CHECK_FLAG(peer->flags,
+ PEER_FLAG_DISABLE_CONNECTED_CHECK)
+ && !CHECK_FLAG(bgp->flags,
+ BGP_FLAG_DISABLE_NH_CONNECTED_CHK))
+ connected = 1;
+ else
+ connected = 0;
+
+ nh_afi = BGP_ATTR_NH_AFI(afi, new->attr);
+
+ if (bgp_find_or_add_nexthop(bgp, bgp, nh_afi, safi, new, NULL,
+ connected, bgp_nht_param_prefix) ||
+ CHECK_FLAG(peer->flags, PEER_FLAG_IS_RFAPI_HD)) {
+ if (accept_own)
+ bgp_path_info_set_flag(dest, new,
+ BGP_PATH_ACCEPT_OWN);
+
+ bgp_path_info_set_flag(dest, new, BGP_PATH_VALID);
+ } else {
+ if (BGP_DEBUG(nht, NHT))
+ zlog_debug("%s(%pI4): NH unresolved", __func__,
+ &attr_new->nexthop);
+ bgp_path_info_unset_flag(dest, new, BGP_PATH_VALID);
+ }
+ } else {
+ /* case mpls-vpn routes with accept-own community
+ * (which have the BGP_ROUTE_IMPORTED subtype)
+ * case other afi/safi not supporting nexthop tracking
+ */
+ if (accept_own)
+ bgp_path_info_set_flag(dest, new, BGP_PATH_ACCEPT_OWN);
+ bgp_path_info_set_flag(dest, new, BGP_PATH_VALID);
+ }
+
+ /* If maximum prefix count is configured and current prefix
+ * count exeed it.
+ */
+ if (bgp_maximum_prefix_overflow(peer, afi, safi, 0)) {
+ reason = "maximum-prefix overflow";
+ bgp_attr_flush(&new_attr);
+ goto filtered;
+ }
+
+ /* Addpath ID */
+ new->addpath_rx_id = addpath_id;
+
+ /* Increment prefix */
+ bgp_aggregate_increment(bgp, p, new, afi, safi);
+
+ /* Register new BGP information. */
+ bgp_path_info_add(dest, new);
+
+ /* route_node_get lock */
+ bgp_dest_unlock_node(dest);
+
+#ifdef ENABLE_BGP_VNC
+ if (safi == SAFI_MPLS_VPN) {
+ struct bgp_dest *pdest = NULL;
+ struct bgp_table *table = NULL;
+
+ pdest = bgp_node_get(bgp->rib[afi][safi], (struct prefix *)prd);
+ if (bgp_dest_has_bgp_path_info_data(pdest)) {
+ table = bgp_dest_get_bgp_table_info(pdest);
+
+ vnc_import_bgp_add_vnc_host_route_mode_resolve_nve(
+ bgp, prd, table, p, new);
+ }
+ bgp_dest_unlock_node(pdest);
+ }
+#endif
+
+ /* If this is an EVPN route, process for import. */
+ if (safi == SAFI_EVPN && CHECK_FLAG(new->flags, BGP_PATH_VALID))
+ bgp_evpn_import_route(bgp, afi, safi, p, new);
+
+ hook_call(bgp_process, bgp, afi, safi, dest, peer, false);
+
+ /* Process change. */
+ bgp_process(bgp, dest, afi, safi);
+
+ if (SAFI_UNICAST == safi
+ && (bgp->inst_type == BGP_INSTANCE_TYPE_VRF
+ || bgp->inst_type == BGP_INSTANCE_TYPE_DEFAULT)) {
+ vpn_leak_from_vrf_update(bgp_get_default(), bgp, new);
+ }
+ if ((SAFI_MPLS_VPN == safi)
+ && (bgp->inst_type == BGP_INSTANCE_TYPE_DEFAULT)) {
+ vpn_leak_to_vrf_update(bgp, new, prd);
+ }
+#ifdef ENABLE_BGP_VNC
+ if (SAFI_MPLS_VPN == safi) {
+ mpls_label_t label_decoded = decode_label(label);
+
+ rfapiProcessUpdate(peer, NULL, p, prd, attr, afi, safi, type,
+ sub_type, &label_decoded);
+ }
+ if (SAFI_ENCAP == safi) {
+ rfapiProcessUpdate(peer, NULL, p, prd, attr, afi, safi, type,
+ sub_type, NULL);
+ }
+#endif
+
+ return;
+
+/* This BGP update is filtered. Log the reason then update BGP
+ entry. */
+filtered:
+ if (new) {
+ bgp_unlink_nexthop(new);
+ bgp_path_info_delete(dest, new);
+ bgp_path_info_extra_free(&new->extra);
+ XFREE(MTYPE_BGP_ROUTE, new);
+ }
+
+ hook_call(bgp_process, bgp, afi, safi, dest, peer, true);
+
+ if (bgp_debug_update(peer, p, NULL, 1)) {
+ if (!peer->rcvd_attr_printed) {
+ zlog_debug("%pBP rcvd UPDATE w/ attr: %s", peer,
+ peer->rcvd_attr_str);
+ peer->rcvd_attr_printed = 1;
+ }
+
+ bgp_debug_rdpfxpath2str(afi, safi, prd, p, label, num_labels,
+ addpath_id ? 1 : 0, addpath_id, evpn,
+ pfx_buf, sizeof(pfx_buf));
+ zlog_debug("%pBP rcvd UPDATE about %s -- DENIED due to: %s",
+ peer, pfx_buf, reason);
+ }
+
+ if (pi) {
+ /* If this is an EVPN route, un-import it as it is now filtered.
+ */
+ if (safi == SAFI_EVPN)
+ bgp_evpn_unimport_route(bgp, afi, safi, p, pi);
+
+ if (SAFI_UNICAST == safi
+ && (bgp->inst_type == BGP_INSTANCE_TYPE_VRF
+ || bgp->inst_type == BGP_INSTANCE_TYPE_DEFAULT)) {
+
+ vpn_leak_from_vrf_withdraw(bgp_get_default(), bgp, pi);
+ }
+ if ((SAFI_MPLS_VPN == safi)
+ && (bgp->inst_type == BGP_INSTANCE_TYPE_DEFAULT)) {
+
+ vpn_leak_to_vrf_withdraw(pi);
+ }
+
+ bgp_rib_remove(dest, pi, peer, afi, safi);
+ }
+
+ bgp_dest_unlock_node(dest);
+
+#ifdef ENABLE_BGP_VNC
+ /*
+ * Filtered update is treated as an implicit withdrawal (see
+ * bgp_rib_remove()
+ * a few lines above)
+ */
+ if ((SAFI_MPLS_VPN == safi) || (SAFI_ENCAP == safi)) {
+ rfapiProcessWithdraw(peer, NULL, p, prd, NULL, afi, safi, type,
+ 0);
+ }
+#endif
+
+ return;
+}
+
+void bgp_withdraw(struct peer *peer, const struct prefix *p,
+ uint32_t addpath_id, afi_t afi, safi_t safi, int type,
+ int sub_type, struct prefix_rd *prd, mpls_label_t *label,
+ uint32_t num_labels, struct bgp_route_evpn *evpn)
+{
+ struct bgp *bgp;
+ char pfx_buf[BGP_PRD_PATH_STRLEN];
+ struct bgp_dest *dest;
+ struct bgp_path_info *pi;
+
+#ifdef ENABLE_BGP_VNC
+ if ((SAFI_MPLS_VPN == safi) || (SAFI_ENCAP == safi)) {
+ rfapiProcessWithdraw(peer, NULL, p, prd, NULL, afi, safi, type,
+ 0);
+ }
+#endif
+
+ bgp = peer->bgp;
+
+ /* Lookup node. */
+ dest = bgp_afi_node_get(bgp->rib[afi][safi], afi, safi, p, prd);
+
+ /* If peer is soft reconfiguration enabled. Record input packet for
+ * further calculation.
+ *
+ * Cisco IOS 12.4(24)T4 on session establishment sends withdraws for all
+ * routes that are filtered. This tanks out Quagga RS pretty badly due
+ * to
+ * the iteration over all RS clients.
+ * Since we need to remove the entry from adj_in anyway, do that first
+ * and
+ * if there was no entry, we don't need to do anything more.
+ */
+ if (CHECK_FLAG(peer->af_flags[afi][safi], PEER_FLAG_SOFT_RECONFIG)
+ && peer != bgp->peer_self)
+ if (!bgp_adj_in_unset(&dest, peer, addpath_id)) {
+ assert(dest);
+ peer->stat_pfx_dup_withdraw++;
+
+ if (bgp_debug_update(peer, p, NULL, 1)) {
+ bgp_debug_rdpfxpath2str(
+ afi, safi, prd, p, label, num_labels,
+ addpath_id ? 1 : 0, addpath_id, NULL,
+ pfx_buf, sizeof(pfx_buf));
+ zlog_debug(
+ "%s withdrawing route %s not in adj-in",
+ peer->host, pfx_buf);
+ }
+ bgp_dest_unlock_node(dest);
+ return;
+ }
+
+ /* Lookup withdrawn route. */
+ assert(dest);
+ for (pi = bgp_dest_get_bgp_path_info(dest); pi; pi = pi->next)
+ if (pi->peer == peer && pi->type == type
+ && pi->sub_type == sub_type
+ && pi->addpath_rx_id == addpath_id)
+ break;
+
+ /* Logging. */
+ if (bgp_debug_update(peer, p, NULL, 1)) {
+ bgp_debug_rdpfxpath2str(afi, safi, prd, p, label, num_labels,
+ addpath_id ? 1 : 0, addpath_id, NULL,
+ pfx_buf, sizeof(pfx_buf));
+ zlog_debug("%pBP rcvd UPDATE about %s -- withdrawn", peer,
+ pfx_buf);
+ }
+
+ /* Withdraw specified route from routing table. */
+ if (pi && !CHECK_FLAG(pi->flags, BGP_PATH_HISTORY)) {
+ bgp_rib_withdraw(dest, pi, peer, afi, safi, prd);
+ if (SAFI_UNICAST == safi
+ && (bgp->inst_type == BGP_INSTANCE_TYPE_VRF
+ || bgp->inst_type == BGP_INSTANCE_TYPE_DEFAULT)) {
+ vpn_leak_from_vrf_withdraw(bgp_get_default(), bgp, pi);
+ }
+ if ((SAFI_MPLS_VPN == safi)
+ && (bgp->inst_type == BGP_INSTANCE_TYPE_DEFAULT)) {
+
+ vpn_leak_to_vrf_withdraw(pi);
+ }
+ } else if (bgp_debug_update(peer, p, NULL, 1)) {
+ bgp_debug_rdpfxpath2str(afi, safi, prd, p, label, num_labels,
+ addpath_id ? 1 : 0, addpath_id, NULL,
+ pfx_buf, sizeof(pfx_buf));
+ zlog_debug("%s Can't find the route %s", peer->host, pfx_buf);
+ }
+
+ /* Unlock bgp_node_get() lock. */
+ bgp_dest_unlock_node(dest);
+
+ return;
+}
+
+void bgp_default_originate(struct peer *peer, afi_t afi, safi_t safi,
+ int withdraw)
+{
+ struct update_subgroup *subgrp;
+ subgrp = peer_subgroup(peer, afi, safi);
+ subgroup_default_originate(subgrp, withdraw);
+}
+
+
+/*
+ * bgp_stop_announce_route_timer
+ */
+void bgp_stop_announce_route_timer(struct peer_af *paf)
+{
+ if (!paf->t_announce_route)
+ return;
+
+ EVENT_OFF(paf->t_announce_route);
+}
+
+/*
+ * bgp_announce_route_timer_expired
+ *
+ * Callback that is invoked when the route announcement timer for a
+ * peer_af expires.
+ */
+static void bgp_announce_route_timer_expired(struct event *t)
+{
+ struct peer_af *paf;
+ struct peer *peer;
+
+ paf = EVENT_ARG(t);
+ peer = paf->peer;
+
+ if (!peer_established(peer->connection))
+ return;
+
+ if (!peer->afc_nego[paf->afi][paf->safi])
+ return;
+
+ peer_af_announce_route(paf, 1);
+
+ /* Notify BGP conditional advertisement scanner percess */
+ peer->advmap_config_change[paf->afi][paf->safi] = true;
+}
+
+/*
+ * bgp_announce_route
+ *
+ * *Triggers* announcement of routes of a given AFI/SAFI to a peer.
+ *
+ * if force is true we will force an update even if the update
+ * limiting code is attempted to kick in.
+ */
+void bgp_announce_route(struct peer *peer, afi_t afi, safi_t safi, bool force)
+{
+ struct peer_af *paf;
+ struct update_subgroup *subgrp;
+
+ paf = peer_af_find(peer, afi, safi);
+ if (!paf)
+ return;
+ subgrp = PAF_SUBGRP(paf);
+
+ /*
+ * Ignore if subgroup doesn't exist (implies AF is not negotiated)
+ * or a refresh has already been triggered.
+ */
+ if (!subgrp || paf->t_announce_route)
+ return;
+
+ if (force)
+ SET_FLAG(subgrp->sflags, SUBGRP_STATUS_FORCE_UPDATES);
+
+ /*
+ * Start a timer to stagger/delay the announce. This serves
+ * two purposes - announcement can potentially be combined for
+ * multiple peers and the announcement doesn't happen in the
+ * vty context.
+ */
+ event_add_timer_msec(bm->master, bgp_announce_route_timer_expired, paf,
+ (subgrp->peer_count == 1)
+ ? BGP_ANNOUNCE_ROUTE_SHORT_DELAY_MS
+ : BGP_ANNOUNCE_ROUTE_DELAY_MS,
+ &paf->t_announce_route);
+}
+
+/*
+ * Announce routes from all AF tables to a peer.
+ *
+ * This should ONLY be called when there is a need to refresh the
+ * routes to the peer based on a policy change for this peer alone
+ * or a route refresh request received from the peer.
+ * The operation will result in splitting the peer from its existing
+ * subgroups and putting it in new subgroups.
+ */
+void bgp_announce_route_all(struct peer *peer)
+{
+ afi_t afi;
+ safi_t safi;
+
+ FOREACH_AFI_SAFI (afi, safi)
+ bgp_announce_route(peer, afi, safi, false);
+}
+
+/* Flag or unflag bgp_dest to determine whether it should be treated by
+ * bgp_soft_reconfig_table_task.
+ * Flag if flag is true. Unflag if flag is false.
+ */
+static void bgp_soft_reconfig_table_flag(struct bgp_table *table, bool flag)
+{
+ struct bgp_dest *dest;
+ struct bgp_adj_in *ain;
+
+ if (!table)
+ return;
+
+ for (dest = bgp_table_top(table); dest; dest = bgp_route_next(dest)) {
+ for (ain = dest->adj_in; ain; ain = ain->next) {
+ if (ain->peer != NULL)
+ break;
+ }
+ if (flag && ain != NULL && ain->peer != NULL)
+ SET_FLAG(dest->flags, BGP_NODE_SOFT_RECONFIG);
+ else
+ UNSET_FLAG(dest->flags, BGP_NODE_SOFT_RECONFIG);
+ }
+}
+
+static void bgp_soft_reconfig_table_update(struct peer *peer,
+ struct bgp_dest *dest,
+ struct bgp_adj_in *ain, afi_t afi,
+ safi_t safi, struct prefix_rd *prd)
+{
+ struct bgp_path_info *pi;
+ uint32_t num_labels = 0;
+ mpls_label_t *label_pnt = NULL;
+ struct bgp_route_evpn evpn;
+
+ for (pi = bgp_dest_get_bgp_path_info(dest); pi; pi = pi->next)
+ if (pi->peer == peer)
+ break;
+
+ if (pi && pi->extra)
+ num_labels = pi->extra->num_labels;
+ if (num_labels)
+ label_pnt = &pi->extra->label[0];
+ if (pi)
+ memcpy(&evpn, bgp_attr_get_evpn_overlay(pi->attr),
+ sizeof(evpn));
+ else
+ memset(&evpn, 0, sizeof(evpn));
+
+ bgp_update(peer, bgp_dest_get_prefix(dest), ain->addpath_rx_id,
+ ain->attr, afi, safi, ZEBRA_ROUTE_BGP, BGP_ROUTE_NORMAL, prd,
+ label_pnt, num_labels, 1, &evpn);
+}
+
+static void bgp_soft_reconfig_table(struct peer *peer, afi_t afi, safi_t safi,
+ struct bgp_table *table,
+ struct prefix_rd *prd)
+{
+ struct bgp_dest *dest;
+ struct bgp_adj_in *ain;
+
+ if (!table)
+ table = peer->bgp->rib[afi][safi];
+
+ for (dest = bgp_table_top(table); dest; dest = bgp_route_next(dest))
+ for (ain = dest->adj_in; ain; ain = ain->next) {
+ if (ain->peer != peer)
+ continue;
+
+ bgp_soft_reconfig_table_update(peer, dest, ain, afi,
+ safi, prd);
+ }
+}
+
+/* Do soft reconfig table per bgp table.
+ * Walk on SOFT_RECONFIG_TASK_MAX_PREFIX bgp_dest,
+ * when BGP_NODE_SOFT_RECONFIG is set,
+ * reconfig bgp_dest for list of table->soft_reconfig_peers peers.
+ * Schedule a new thread to continue the job.
+ * Without splitting the full job into several part,
+ * vtysh waits for the job to finish before responding to a BGP command
+ */
+static void bgp_soft_reconfig_table_task(struct event *thread)
+{
+ uint32_t iter, max_iter;
+ struct bgp_dest *dest;
+ struct bgp_adj_in *ain;
+ struct peer *peer;
+ struct bgp_table *table;
+ struct prefix_rd *prd;
+ struct listnode *node, *nnode;
+
+ table = EVENT_ARG(thread);
+ prd = NULL;
+
+ max_iter = SOFT_RECONFIG_TASK_MAX_PREFIX;
+ if (table->soft_reconfig_init) {
+ /* first call of the function with a new srta structure.
+ * Don't do any treatment this time on nodes
+ * in order vtysh to respond quickly
+ */
+ max_iter = 0;
+ }
+
+ for (iter = 0, dest = bgp_table_top(table); (dest && iter < max_iter);
+ dest = bgp_route_next(dest)) {
+ if (!CHECK_FLAG(dest->flags, BGP_NODE_SOFT_RECONFIG))
+ continue;
+
+ UNSET_FLAG(dest->flags, BGP_NODE_SOFT_RECONFIG);
+
+ for (ain = dest->adj_in; ain; ain = ain->next) {
+ for (ALL_LIST_ELEMENTS(table->soft_reconfig_peers, node,
+ nnode, peer)) {
+ if (ain->peer != peer)
+ continue;
+
+ bgp_soft_reconfig_table_update(
+ peer, dest, ain, table->afi,
+ table->safi, prd);
+ iter++;
+ }
+ }
+ }
+
+ /* we're either starting the initial iteration,
+ * or we're going to continue an ongoing iteration
+ */
+ if (dest || table->soft_reconfig_init) {
+ table->soft_reconfig_init = false;
+ event_add_event(bm->master, bgp_soft_reconfig_table_task, table,
+ 0, &table->soft_reconfig_thread);
+ return;
+ }
+ /* we're done, clean up the background iteration context info and
+ schedule route annoucement
+ */
+ for (ALL_LIST_ELEMENTS(table->soft_reconfig_peers, node, nnode, peer)) {
+ listnode_delete(table->soft_reconfig_peers, peer);
+ bgp_announce_route(peer, table->afi, table->safi, false);
+ }
+
+ list_delete(&table->soft_reconfig_peers);
+}
+
+
+/* Cancel soft_reconfig_table task matching bgp instance, bgp_table
+ * and peer.
+ * - bgp cannot be NULL
+ * - if table and peer are NULL, cancel all threads within the bgp instance
+ * - if table is NULL and peer is not,
+ * remove peer in all threads within the bgp instance
+ * - if peer is NULL, cancel all threads matching table within the bgp instance
+ */
+void bgp_soft_reconfig_table_task_cancel(const struct bgp *bgp,
+ const struct bgp_table *table,
+ const struct peer *peer)
+{
+ struct peer *npeer;
+ struct listnode *node, *nnode;
+ int afi, safi;
+ struct bgp_table *ntable;
+
+ if (!bgp)
+ return;
+
+ FOREACH_AFI_SAFI (afi, safi) {
+ ntable = bgp->rib[afi][safi];
+ if (!ntable)
+ continue;
+ if (table && table != ntable)
+ continue;
+
+ for (ALL_LIST_ELEMENTS(ntable->soft_reconfig_peers, node, nnode,
+ npeer)) {
+ if (peer && peer != npeer)
+ continue;
+ listnode_delete(ntable->soft_reconfig_peers, npeer);
+ }
+
+ if (!ntable->soft_reconfig_peers
+ || !list_isempty(ntable->soft_reconfig_peers))
+ continue;
+
+ list_delete(&ntable->soft_reconfig_peers);
+ bgp_soft_reconfig_table_flag(ntable, false);
+ EVENT_OFF(ntable->soft_reconfig_thread);
+ }
+}
+
+/*
+ * Returns false if the peer is not configured for soft reconfig in
+ */
+bool bgp_soft_reconfig_in(struct peer *peer, afi_t afi, safi_t safi)
+{
+ struct bgp_dest *dest;
+ struct bgp_table *table;
+ struct listnode *node, *nnode;
+ struct peer *npeer;
+ struct peer_af *paf;
+
+ if (!CHECK_FLAG(peer->af_flags[afi][safi], PEER_FLAG_SOFT_RECONFIG))
+ return false;
+
+ if ((safi != SAFI_MPLS_VPN) && (safi != SAFI_ENCAP)
+ && (safi != SAFI_EVPN)) {
+ table = peer->bgp->rib[afi][safi];
+ if (!table)
+ return true;
+
+ table->soft_reconfig_init = true;
+
+ if (!table->soft_reconfig_peers)
+ table->soft_reconfig_peers = list_new();
+ npeer = NULL;
+ /* add peer to the table soft_reconfig_peers if not already
+ * there
+ */
+ for (ALL_LIST_ELEMENTS(table->soft_reconfig_peers, node, nnode,
+ npeer)) {
+ if (peer == npeer)
+ break;
+ }
+ if (peer != npeer)
+ listnode_add(table->soft_reconfig_peers, peer);
+
+ /* (re)flag all bgp_dest in table. Existing soft_reconfig_in job
+ * on table would start back at the beginning.
+ */
+ bgp_soft_reconfig_table_flag(table, true);
+
+ if (!table->soft_reconfig_thread)
+ event_add_event(bm->master,
+ bgp_soft_reconfig_table_task, table, 0,
+ &table->soft_reconfig_thread);
+ /* Cancel bgp_announce_route_timer_expired threads.
+ * bgp_announce_route_timer_expired threads have been scheduled
+ * to announce routes as soon as the soft_reconfigure process
+ * finishes.
+ * In this case, soft_reconfigure is also scheduled by using
+ * a thread but is planned after the
+ * bgp_announce_route_timer_expired threads. It means that,
+ * without cancelling the threads, the route announcement task
+ * would run before the soft reconfiguration one. That would
+ * useless and would block vtysh during several seconds. Route
+ * announcements are rescheduled as soon as the soft_reconfigure
+ * process finishes.
+ */
+ paf = peer_af_find(peer, afi, safi);
+ if (paf)
+ bgp_stop_announce_route_timer(paf);
+ } else
+ for (dest = bgp_table_top(peer->bgp->rib[afi][safi]); dest;
+ dest = bgp_route_next(dest)) {
+ table = bgp_dest_get_bgp_table_info(dest);
+
+ if (table == NULL)
+ continue;
+
+ const struct prefix *p = bgp_dest_get_prefix(dest);
+ struct prefix_rd prd;
+
+ prd.family = AF_UNSPEC;
+ prd.prefixlen = 64;
+ memcpy(&prd.val, p->u.val, 8);
+
+ bgp_soft_reconfig_table(peer, afi, safi, table, &prd);
+ }
+
+ return true;
+}
+
+
+struct bgp_clear_node_queue {
+ struct bgp_dest *dest;
+};
+
+static wq_item_status bgp_clear_route_node(struct work_queue *wq, void *data)
+{
+ struct bgp_clear_node_queue *cnq = data;
+ struct bgp_dest *dest = cnq->dest;
+ struct peer *peer = wq->spec.data;
+ struct bgp_path_info *pi;
+ struct bgp *bgp;
+ afi_t afi = bgp_dest_table(dest)->afi;
+ safi_t safi = bgp_dest_table(dest)->safi;
+
+ assert(dest && peer);
+ bgp = peer->bgp;
+
+ /* It is possible that we have multiple paths for a prefix from a peer
+ * if that peer is using AddPath.
+ */
+ for (pi = bgp_dest_get_bgp_path_info(dest); pi; pi = pi->next) {
+ if (pi->peer != peer)
+ continue;
+
+ /* graceful restart STALE flag set. */
+ if (((CHECK_FLAG(peer->sflags, PEER_STATUS_NSF_WAIT)
+ && peer->nsf[afi][safi])
+ || CHECK_FLAG(peer->af_sflags[afi][safi],
+ PEER_STATUS_ENHANCED_REFRESH))
+ && !CHECK_FLAG(pi->flags, BGP_PATH_STALE)
+ && !CHECK_FLAG(pi->flags, BGP_PATH_UNUSEABLE))
+ bgp_path_info_set_flag(dest, pi, BGP_PATH_STALE);
+ else {
+ /* If this is an EVPN route, process for
+ * un-import. */
+ if (safi == SAFI_EVPN)
+ bgp_evpn_unimport_route(
+ bgp, afi, safi,
+ bgp_dest_get_prefix(dest), pi);
+ /* Handle withdraw for VRF route-leaking and L3VPN */
+ if (SAFI_UNICAST == safi
+ && (bgp->inst_type == BGP_INSTANCE_TYPE_VRF ||
+ bgp->inst_type == BGP_INSTANCE_TYPE_DEFAULT)) {
+ vpn_leak_from_vrf_withdraw(bgp_get_default(),
+ bgp, pi);
+ }
+ if (SAFI_MPLS_VPN == safi &&
+ bgp->inst_type == BGP_INSTANCE_TYPE_DEFAULT) {
+ vpn_leak_to_vrf_withdraw(pi);
+ }
+
+ bgp_rib_remove(dest, pi, peer, afi, safi);
+ }
+ }
+ return WQ_SUCCESS;
+}
+
+static void bgp_clear_node_queue_del(struct work_queue *wq, void *data)
+{
+ struct bgp_clear_node_queue *cnq = data;
+ struct bgp_dest *dest = cnq->dest;
+ struct bgp_table *table = bgp_dest_table(dest);
+
+ bgp_dest_unlock_node(dest);
+ bgp_table_unlock(table);
+ XFREE(MTYPE_BGP_CLEAR_NODE_QUEUE, cnq);
+}
+
+static void bgp_clear_node_complete(struct work_queue *wq)
+{
+ struct peer *peer = wq->spec.data;
+
+ /* Tickle FSM to start moving again */
+ BGP_EVENT_ADD(peer->connection, Clearing_Completed);
+
+ peer_unlock(peer); /* bgp_clear_route */
+}
+
+static void bgp_clear_node_queue_init(struct peer *peer)
+{
+ char wname[sizeof("clear xxxx:xxxx:xxxx:xxxx:xxxx:xxxx:xxxx:xxxx")];
+
+ snprintf(wname, sizeof(wname), "clear %s", peer->host);
+#undef CLEAR_QUEUE_NAME_LEN
+
+ peer->clear_node_queue = work_queue_new(bm->master, wname);
+ peer->clear_node_queue->spec.hold = 10;
+ peer->clear_node_queue->spec.workfunc = &bgp_clear_route_node;
+ peer->clear_node_queue->spec.del_item_data = &bgp_clear_node_queue_del;
+ peer->clear_node_queue->spec.completion_func = &bgp_clear_node_complete;
+ peer->clear_node_queue->spec.max_retries = 0;
+
+ /* we only 'lock' this peer reference when the queue is actually active
+ */
+ peer->clear_node_queue->spec.data = peer;
+}
+
+static void bgp_clear_route_table(struct peer *peer, afi_t afi, safi_t safi,
+ struct bgp_table *table)
+{
+ struct bgp_dest *dest;
+ int force = peer->bgp->process_queue ? 0 : 1;
+
+ if (!table)
+ table = peer->bgp->rib[afi][safi];
+
+ /* If still no table => afi/safi isn't configured at all or smth. */
+ if (!table)
+ return;
+
+ for (dest = bgp_table_top(table); dest; dest = bgp_route_next(dest)) {
+ struct bgp_path_info *pi, *next;
+ struct bgp_adj_in *ain;
+ struct bgp_adj_in *ain_next;
+
+ /* XXX:TODO: This is suboptimal, every non-empty route_node is
+ * queued for every clearing peer, regardless of whether it is
+ * relevant to the peer at hand.
+ *
+ * Overview: There are 3 different indices which need to be
+ * scrubbed, potentially, when a peer is removed:
+ *
+ * 1 peer's routes visible via the RIB (ie accepted routes)
+ * 2 peer's routes visible by the (optional) peer's adj-in index
+ * 3 other routes visible by the peer's adj-out index
+ *
+ * 3 there is no hurry in scrubbing, once the struct peer is
+ * removed from bgp->peer, we could just GC such deleted peer's
+ * adj-outs at our leisure.
+ *
+ * 1 and 2 must be 'scrubbed' in some way, at least made
+ * invisible via RIB index before peer session is allowed to be
+ * brought back up. So one needs to know when such a 'search' is
+ * complete.
+ *
+ * Ideally:
+ *
+ * - there'd be a single global queue or a single RIB walker
+ * - rather than tracking which route_nodes still need to be
+ * examined on a peer basis, we'd track which peers still
+ * aren't cleared
+ *
+ * Given that our per-peer prefix-counts now should be reliable,
+ * this may actually be achievable. It doesn't seem to be a huge
+ * problem at this time,
+ *
+ * It is possible that we have multiple paths for a prefix from
+ * a peer
+ * if that peer is using AddPath.
+ */
+ ain = dest->adj_in;
+ while (ain) {
+ ain_next = ain->next;
+
+ if (ain->peer == peer)
+ bgp_adj_in_remove(&dest, ain);
+
+ ain = ain_next;
+
+ assert(dest);
+ }
+
+ for (pi = bgp_dest_get_bgp_path_info(dest); pi; pi = next) {
+ next = pi->next;
+ if (pi->peer != peer)
+ continue;
+
+ if (force) {
+ dest = bgp_path_info_reap(dest, pi);
+ assert(dest);
+ } else {
+ struct bgp_clear_node_queue *cnq;
+
+ /* both unlocked in bgp_clear_node_queue_del */
+ bgp_table_lock(bgp_dest_table(dest));
+ bgp_dest_lock_node(dest);
+ cnq = XCALLOC(
+ MTYPE_BGP_CLEAR_NODE_QUEUE,
+ sizeof(struct bgp_clear_node_queue));
+ cnq->dest = dest;
+ work_queue_add(peer->clear_node_queue, cnq);
+ break;
+ }
+ }
+ }
+ return;
+}
+
+void bgp_clear_route(struct peer *peer, afi_t afi, safi_t safi)
+{
+ struct bgp_dest *dest;
+ struct bgp_table *table;
+
+ if (peer->clear_node_queue == NULL)
+ bgp_clear_node_queue_init(peer);
+
+ /* bgp_fsm.c keeps sessions in state Clearing, not transitioning to
+ * Idle until it receives a Clearing_Completed event. This protects
+ * against peers which flap faster than we can we clear, which could
+ * lead to:
+ *
+ * a) race with routes from the new session being installed before
+ * clear_route_node visits the node (to delete the route of that
+ * peer)
+ * b) resource exhaustion, clear_route_node likely leads to an entry
+ * on the process_main queue. Fast-flapping could cause that queue
+ * to grow and grow.
+ */
+
+ /* lock peer in assumption that clear-node-queue will get nodes; if so,
+ * the unlock will happen upon work-queue completion; other wise, the
+ * unlock happens at the end of this function.
+ */
+ if (!peer->clear_node_queue->thread)
+ peer_lock(peer);
+
+ if (safi != SAFI_MPLS_VPN && safi != SAFI_ENCAP && safi != SAFI_EVPN)
+ bgp_clear_route_table(peer, afi, safi, NULL);
+ else
+ for (dest = bgp_table_top(peer->bgp->rib[afi][safi]); dest;
+ dest = bgp_route_next(dest)) {
+ table = bgp_dest_get_bgp_table_info(dest);
+ if (!table)
+ continue;
+
+ bgp_clear_route_table(peer, afi, safi, table);
+ }
+
+ /* unlock if no nodes got added to the clear-node-queue. */
+ if (!peer->clear_node_queue->thread)
+ peer_unlock(peer);
+}
+
+void bgp_clear_route_all(struct peer *peer)
+{
+ afi_t afi;
+ safi_t safi;
+
+ FOREACH_AFI_SAFI (afi, safi)
+ bgp_clear_route(peer, afi, safi);
+
+#ifdef ENABLE_BGP_VNC
+ rfapiProcessPeerDown(peer);
+#endif
+}
+
+void bgp_clear_adj_in(struct peer *peer, afi_t afi, safi_t safi)
+{
+ struct bgp_table *table;
+ struct bgp_dest *dest;
+ struct bgp_adj_in *ain;
+ struct bgp_adj_in *ain_next;
+
+ table = peer->bgp->rib[afi][safi];
+
+ /* It is possible that we have multiple paths for a prefix from a peer
+ * if that peer is using AddPath.
+ */
+ for (dest = bgp_table_top(table); dest; dest = bgp_route_next(dest)) {
+ ain = dest->adj_in;
+
+ while (ain) {
+ ain_next = ain->next;
+
+ if (ain->peer == peer)
+ bgp_adj_in_remove(&dest, ain);
+
+ ain = ain_next;
+
+ assert(dest);
+ }
+ }
+}
+
+/* If any of the routes from the peer have been marked with the NO_LLGR
+ * community, either as sent by the peer, or as the result of a configured
+ * policy, they MUST NOT be retained, but MUST be removed as per the normal
+ * operation of [RFC4271].
+ */
+void bgp_clear_stale_route(struct peer *peer, afi_t afi, safi_t safi)
+{
+ struct bgp_dest *dest;
+ struct bgp_path_info *pi;
+ struct bgp_table *table;
+
+ if (safi == SAFI_MPLS_VPN || safi == SAFI_ENCAP || safi == SAFI_EVPN) {
+ for (dest = bgp_table_top(peer->bgp->rib[afi][safi]); dest;
+ dest = bgp_route_next(dest)) {
+ struct bgp_dest *rm;
+
+ /* look for neighbor in tables */
+ table = bgp_dest_get_bgp_table_info(dest);
+ if (!table)
+ continue;
+
+ for (rm = bgp_table_top(table); rm;
+ rm = bgp_route_next(rm))
+ for (pi = bgp_dest_get_bgp_path_info(rm); pi;
+ pi = pi->next) {
+ if (pi->peer != peer)
+ continue;
+ if (CHECK_FLAG(
+ peer->af_sflags[afi][safi],
+ PEER_STATUS_LLGR_WAIT) &&
+ bgp_attr_get_community(pi->attr) &&
+ !community_include(
+ bgp_attr_get_community(
+ pi->attr),
+ COMMUNITY_NO_LLGR))
+ continue;
+ if (!CHECK_FLAG(pi->flags,
+ BGP_PATH_STALE))
+ continue;
+
+ /*
+ * If this is VRF leaked route
+ * process for withdraw.
+ */
+ if (pi->sub_type ==
+ BGP_ROUTE_IMPORTED &&
+ peer->bgp->inst_type ==
+ BGP_INSTANCE_TYPE_DEFAULT)
+ vpn_leak_to_vrf_withdraw(pi);
+
+ bgp_rib_remove(rm, pi, peer, afi, safi);
+ break;
+ }
+ }
+ } else {
+ for (dest = bgp_table_top(peer->bgp->rib[afi][safi]); dest;
+ dest = bgp_route_next(dest))
+ for (pi = bgp_dest_get_bgp_path_info(dest); pi;
+ pi = pi->next) {
+ if (pi->peer != peer)
+ continue;
+ if (CHECK_FLAG(peer->af_sflags[afi][safi],
+ PEER_STATUS_LLGR_WAIT) &&
+ bgp_attr_get_community(pi->attr) &&
+ !community_include(
+ bgp_attr_get_community(pi->attr),
+ COMMUNITY_NO_LLGR))
+ continue;
+ if (!CHECK_FLAG(pi->flags, BGP_PATH_STALE))
+ continue;
+ if (safi == SAFI_UNICAST &&
+ (peer->bgp->inst_type ==
+ BGP_INSTANCE_TYPE_VRF ||
+ peer->bgp->inst_type ==
+ BGP_INSTANCE_TYPE_DEFAULT))
+ vpn_leak_from_vrf_withdraw(
+ bgp_get_default(), peer->bgp,
+ pi);
+
+ bgp_rib_remove(dest, pi, peer, afi, safi);
+ break;
+ }
+ }
+}
+
+void bgp_set_stale_route(struct peer *peer, afi_t afi, safi_t safi)
+{
+ struct bgp_dest *dest, *ndest;
+ struct bgp_path_info *pi;
+ struct bgp_table *table;
+
+ if (safi == SAFI_MPLS_VPN || safi == SAFI_ENCAP || safi == SAFI_EVPN) {
+ for (dest = bgp_table_top(peer->bgp->rib[afi][safi]); dest;
+ dest = bgp_route_next(dest)) {
+ table = bgp_dest_get_bgp_table_info(dest);
+ if (!table)
+ continue;
+
+ for (ndest = bgp_table_top(table); ndest;
+ ndest = bgp_route_next(ndest)) {
+ for (pi = bgp_dest_get_bgp_path_info(ndest); pi;
+ pi = pi->next) {
+ if (pi->peer != peer)
+ continue;
+
+ if ((CHECK_FLAG(
+ peer->af_sflags[afi][safi],
+ PEER_STATUS_ENHANCED_REFRESH))
+ && !CHECK_FLAG(pi->flags,
+ BGP_PATH_STALE)
+ && !CHECK_FLAG(
+ pi->flags,
+ BGP_PATH_UNUSEABLE)) {
+ if (bgp_debug_neighbor_events(
+ peer))
+ zlog_debug(
+ "%pBP route-refresh for %s/%s, marking prefix %pFX as stale",
+ peer,
+ afi2str(afi),
+ safi2str(safi),
+ bgp_dest_get_prefix(
+ ndest));
+
+ bgp_path_info_set_flag(
+ ndest, pi,
+ BGP_PATH_STALE);
+ }
+ }
+ }
+ }
+ } else {
+ for (dest = bgp_table_top(peer->bgp->rib[afi][safi]); dest;
+ dest = bgp_route_next(dest)) {
+ for (pi = bgp_dest_get_bgp_path_info(dest); pi;
+ pi = pi->next) {
+ if (pi->peer != peer)
+ continue;
+
+ if ((CHECK_FLAG(peer->af_sflags[afi][safi],
+ PEER_STATUS_ENHANCED_REFRESH))
+ && !CHECK_FLAG(pi->flags, BGP_PATH_STALE)
+ && !CHECK_FLAG(pi->flags,
+ BGP_PATH_UNUSEABLE)) {
+ if (bgp_debug_neighbor_events(peer))
+ zlog_debug(
+ "%pBP route-refresh for %s/%s, marking prefix %pFX as stale",
+ peer, afi2str(afi),
+ safi2str(safi),
+ bgp_dest_get_prefix(
+ dest));
+
+ bgp_path_info_set_flag(dest, pi,
+ BGP_PATH_STALE);
+ }
+ }
+ }
+ }
+}
+
+bool bgp_outbound_policy_exists(struct peer *peer, struct bgp_filter *filter)
+{
+ if (peer->sort == BGP_PEER_IBGP)
+ return true;
+
+ if (peer->sort == BGP_PEER_EBGP
+ && (ROUTE_MAP_OUT_NAME(filter) || PREFIX_LIST_OUT_NAME(filter)
+ || FILTER_LIST_OUT_NAME(filter)
+ || DISTRIBUTE_OUT_NAME(filter)))
+ return true;
+ return false;
+}
+
+bool bgp_inbound_policy_exists(struct peer *peer, struct bgp_filter *filter)
+{
+ if (peer->sort == BGP_PEER_IBGP)
+ return true;
+
+ if (peer->sort == BGP_PEER_EBGP
+ && (ROUTE_MAP_IN_NAME(filter) || PREFIX_LIST_IN_NAME(filter)
+ || FILTER_LIST_IN_NAME(filter)
+ || DISTRIBUTE_IN_NAME(filter)))
+ return true;
+ return false;
+}
+
+static void bgp_cleanup_table(struct bgp *bgp, struct bgp_table *table,
+ safi_t safi)
+{
+ struct bgp_dest *dest;
+ struct bgp_path_info *pi;
+ struct bgp_path_info *next;
+
+ for (dest = bgp_table_top(table); dest; dest = bgp_route_next(dest))
+ for (pi = bgp_dest_get_bgp_path_info(dest); pi; pi = next) {
+ const struct prefix *p = bgp_dest_get_prefix(dest);
+
+ next = pi->next;
+
+ /* Unimport EVPN routes from VRFs */
+ if (safi == SAFI_EVPN)
+ bgp_evpn_unimport_route(bgp, AFI_L2VPN,
+ SAFI_EVPN, p, pi);
+
+ if (CHECK_FLAG(pi->flags, BGP_PATH_SELECTED)
+ && pi->type == ZEBRA_ROUTE_BGP
+ && (pi->sub_type == BGP_ROUTE_NORMAL
+ || pi->sub_type == BGP_ROUTE_AGGREGATE
+ || pi->sub_type == BGP_ROUTE_IMPORTED)) {
+
+ if (bgp_fibupd_safi(safi))
+ bgp_zebra_withdraw(p, pi, bgp, safi);
+ }
+
+ dest = bgp_path_info_reap(dest, pi);
+ assert(dest);
+ }
+}
+
+/* Delete all kernel routes. */
+void bgp_cleanup_routes(struct bgp *bgp)
+{
+ afi_t afi;
+ struct bgp_dest *dest;
+ struct bgp_table *table;
+
+ for (afi = AFI_IP; afi < AFI_MAX; ++afi) {
+ if (afi == AFI_L2VPN)
+ continue;
+ bgp_cleanup_table(bgp, bgp->rib[afi][SAFI_UNICAST],
+ SAFI_UNICAST);
+ /*
+ * VPN and ENCAP and EVPN tables are two-level (RD is top level)
+ */
+ if (afi != AFI_L2VPN) {
+ safi_t safi;
+ safi = SAFI_MPLS_VPN;
+ for (dest = bgp_table_top(bgp->rib[afi][safi]); dest;
+ dest = bgp_route_next(dest)) {
+ table = bgp_dest_get_bgp_table_info(dest);
+ if (table != NULL) {
+ bgp_cleanup_table(bgp, table, safi);
+ bgp_table_finish(&table);
+ bgp_dest_set_bgp_table_info(dest, NULL);
+ dest = bgp_dest_unlock_node(dest);
+
+ assert(dest);
+ }
+ }
+ safi = SAFI_ENCAP;
+ for (dest = bgp_table_top(bgp->rib[afi][safi]); dest;
+ dest = bgp_route_next(dest)) {
+ table = bgp_dest_get_bgp_table_info(dest);
+ if (table != NULL) {
+ bgp_cleanup_table(bgp, table, safi);
+ bgp_table_finish(&table);
+ bgp_dest_set_bgp_table_info(dest, NULL);
+ dest = bgp_dest_unlock_node(dest);
+
+ assert(dest);
+ }
+ }
+ }
+ }
+ for (dest = bgp_table_top(bgp->rib[AFI_L2VPN][SAFI_EVPN]); dest;
+ dest = bgp_route_next(dest)) {
+ table = bgp_dest_get_bgp_table_info(dest);
+ if (table != NULL) {
+ bgp_cleanup_table(bgp, table, SAFI_EVPN);
+ bgp_table_finish(&table);
+ bgp_dest_set_bgp_table_info(dest, NULL);
+ dest = bgp_dest_unlock_node(dest);
+
+ assert(dest);
+ }
+ }
+}
+
+void bgp_reset(void)
+{
+ vty_reset();
+ bgp_zclient_reset();
+ access_list_reset();
+ prefix_list_reset();
+}
+
+bool bgp_addpath_encode_rx(struct peer *peer, afi_t afi, safi_t safi)
+{
+ return (CHECK_FLAG(peer->af_cap[afi][safi], PEER_CAP_ADDPATH_AF_RX_ADV)
+ && CHECK_FLAG(peer->af_cap[afi][safi],
+ PEER_CAP_ADDPATH_AF_TX_RCV));
+}
+
+/* Parse NLRI stream. Withdraw NLRI is recognized by NULL attr
+ value. */
+int bgp_nlri_parse_ip(struct peer *peer, struct attr *attr,
+ struct bgp_nlri *packet)
+{
+ uint8_t *pnt;
+ uint8_t *lim;
+ struct prefix p;
+ int psize;
+ afi_t afi;
+ safi_t safi;
+ bool addpath_capable;
+ uint32_t addpath_id;
+
+ pnt = packet->nlri;
+ lim = pnt + packet->length;
+ afi = packet->afi;
+ safi = packet->safi;
+ addpath_id = 0;
+ addpath_capable = bgp_addpath_encode_rx(peer, afi, safi);
+
+ /* RFC4271 6.3 The NLRI field in the UPDATE message is checked for
+ syntactic validity. If the field is syntactically incorrect,
+ then the Error Subcode is set to Invalid Network Field. */
+ for (; pnt < lim; pnt += psize) {
+ /* Clear prefix structure. */
+ memset(&p, 0, sizeof(p));
+
+ if (addpath_capable) {
+
+ /* When packet overflow occurs return immediately. */
+ if (pnt + BGP_ADDPATH_ID_LEN >= lim)
+ return BGP_NLRI_PARSE_ERROR_PACKET_OVERFLOW;
+
+ memcpy(&addpath_id, pnt, BGP_ADDPATH_ID_LEN);
+ addpath_id = ntohl(addpath_id);
+ pnt += BGP_ADDPATH_ID_LEN;
+ }
+
+ /* Fetch prefix length. */
+ p.prefixlen = *pnt++;
+ /* afi/safi validity already verified by caller,
+ * bgp_update_receive */
+ p.family = afi2family(afi);
+
+ /* Prefix length check. */
+ if (p.prefixlen > prefix_blen(&p) * 8) {
+ flog_err(
+ EC_BGP_UPDATE_RCV,
+ "%s [Error] Update packet error (wrong prefix length %d for afi %u)",
+ peer->host, p.prefixlen, packet->afi);
+ return BGP_NLRI_PARSE_ERROR_PREFIX_LENGTH;
+ }
+
+ /* Packet size overflow check. */
+ psize = PSIZE(p.prefixlen);
+
+ /* When packet overflow occur return immediately. */
+ if (pnt + psize > lim) {
+ flog_err(
+ EC_BGP_UPDATE_RCV,
+ "%s [Error] Update packet error (prefix length %d overflows packet)",
+ peer->host, p.prefixlen);
+ return BGP_NLRI_PARSE_ERROR_PACKET_OVERFLOW;
+ }
+
+ /* Defensive coding, double-check the psize fits in a struct
+ * prefix for the v4 and v6 afi's and unicast/multicast */
+ if (psize > (ssize_t)sizeof(p.u.val)) {
+ flog_err(
+ EC_BGP_UPDATE_RCV,
+ "%s [Error] Update packet error (prefix length %d too large for prefix storage %zu)",
+ peer->host, p.prefixlen, sizeof(p.u.val));
+ return BGP_NLRI_PARSE_ERROR_PACKET_LENGTH;
+ }
+
+ /* Fetch prefix from NLRI packet. */
+ memcpy(p.u.val, pnt, psize);
+
+ /* Check address. */
+ if (afi == AFI_IP && safi == SAFI_UNICAST) {
+ if (IN_CLASSD(ntohl(p.u.prefix4.s_addr))) {
+ /* From RFC4271 Section 6.3:
+ *
+ * If a prefix in the NLRI field is semantically
+ * incorrect
+ * (e.g., an unexpected multicast IP address),
+ * an error SHOULD
+ * be logged locally, and the prefix SHOULD be
+ * ignored.
+ */
+ flog_err(
+ EC_BGP_UPDATE_RCV,
+ "%s: IPv4 unicast NLRI is multicast address %pI4, ignoring",
+ peer->host, &p.u.prefix4);
+ continue;
+ }
+ }
+
+ /* Check address. */
+ if (afi == AFI_IP6 && safi == SAFI_UNICAST) {
+ if (IN6_IS_ADDR_LINKLOCAL(&p.u.prefix6)) {
+ flog_err(
+ EC_BGP_UPDATE_RCV,
+ "%s: IPv6 unicast NLRI is link-local address %pI6, ignoring",
+ peer->host, &p.u.prefix6);
+
+ continue;
+ }
+ if (IN6_IS_ADDR_MULTICAST(&p.u.prefix6)) {
+ flog_err(
+ EC_BGP_UPDATE_RCV,
+ "%s: IPv6 unicast NLRI is multicast address %pI6, ignoring",
+ peer->host, &p.u.prefix6);
+
+ continue;
+ }
+ }
+
+ /* Normal process. */
+ if (attr)
+ bgp_update(peer, &p, addpath_id, attr, afi, safi,
+ ZEBRA_ROUTE_BGP, BGP_ROUTE_NORMAL, NULL,
+ NULL, 0, 0, NULL);
+ else
+ bgp_withdraw(peer, &p, addpath_id, afi, safi,
+ ZEBRA_ROUTE_BGP, BGP_ROUTE_NORMAL, NULL,
+ NULL, 0, NULL);
+
+ /* Do not send BGP notification twice when maximum-prefix count
+ * overflow. */
+ if (CHECK_FLAG(peer->sflags, PEER_STATUS_PREFIX_OVERFLOW))
+ return BGP_NLRI_PARSE_ERROR_PREFIX_OVERFLOW;
+ }
+
+ /* Packet length consistency check. */
+ if (pnt != lim) {
+ flog_err(
+ EC_BGP_UPDATE_RCV,
+ "%s [Error] Update packet error (prefix length mismatch with total length)",
+ peer->host);
+ return BGP_NLRI_PARSE_ERROR_PACKET_LENGTH;
+ }
+
+ return BGP_NLRI_PARSE_OK;
+}
+
+static void bgp_nexthop_reachability_check(afi_t afi, safi_t safi,
+ struct bgp_path_info *bpi,
+ const struct prefix *p,
+ struct bgp_dest *dest,
+ struct bgp *bgp)
+{
+ /* Nexthop reachability check. */
+ if (safi == SAFI_UNICAST || safi == SAFI_LABELED_UNICAST) {
+ if (CHECK_FLAG(bgp->flags, BGP_FLAG_IMPORT_CHECK)) {
+ if (bgp_find_or_add_nexthop(bgp, bgp, afi, safi, bpi,
+ NULL, 0, p))
+ bgp_path_info_set_flag(dest, bpi,
+ BGP_PATH_VALID);
+ else {
+ if (BGP_DEBUG(nht, NHT)) {
+ char buf1[INET6_ADDRSTRLEN];
+
+ inet_ntop(p->family, &p->u.prefix, buf1,
+ sizeof(buf1));
+ zlog_debug("%s(%s): Route not in table, not advertising",
+ __func__, buf1);
+ }
+ bgp_path_info_unset_flag(dest, bpi,
+ BGP_PATH_VALID);
+ }
+ } else {
+ /* Delete the NHT structure if any, if we're toggling between
+ * enabling/disabling import check. We deregister the route
+ * from NHT to avoid overloading NHT and the process interaction
+ */
+ bgp_unlink_nexthop(bpi);
+
+ bgp_path_info_set_flag(dest, bpi, BGP_PATH_VALID);
+ }
+ }
+}
+
+static struct bgp_static *bgp_static_new(void)
+{
+ return XCALLOC(MTYPE_BGP_STATIC, sizeof(struct bgp_static));
+}
+
+static void bgp_static_free(struct bgp_static *bgp_static)
+{
+ XFREE(MTYPE_ROUTE_MAP_NAME, bgp_static->rmap.name);
+ route_map_counter_decrement(bgp_static->rmap.map);
+
+ if (bgp_static->prd_pretty)
+ XFREE(MTYPE_BGP, bgp_static->prd_pretty);
+ XFREE(MTYPE_ATTR, bgp_static->eth_s_id);
+ XFREE(MTYPE_BGP_STATIC, bgp_static);
+}
+
+void bgp_static_update(struct bgp *bgp, const struct prefix *p,
+ struct bgp_static *bgp_static, afi_t afi, safi_t safi)
+{
+ struct bgp_dest *dest;
+ struct bgp_path_info *pi;
+ struct bgp_path_info *new;
+ struct bgp_path_info rmap_path;
+ struct attr attr;
+ struct attr *attr_new;
+ route_map_result_t ret;
+#ifdef ENABLE_BGP_VNC
+ int vnc_implicit_withdraw = 0;
+ mpls_label_t label = 0;
+#endif
+ uint32_t num_labels = 0;
+
+ assert(bgp_static);
+
+ if ((safi == SAFI_MPLS_VPN || safi == SAFI_ENCAP || safi == SAFI_EVPN) &&
+ bgp_static->label != MPLS_INVALID_LABEL)
+ num_labels = 1;
+
+ dest = bgp_afi_node_get(bgp->rib[afi][safi], afi, safi, p,
+ &bgp_static->prd);
+
+ bgp_attr_default_set(&attr, bgp, BGP_ORIGIN_IGP);
+
+ attr.nexthop = bgp_static->igpnexthop;
+ attr.med = bgp_static->igpmetric;
+ attr.flag |= ATTR_FLAG_BIT(BGP_ATTR_MULTI_EXIT_DISC);
+
+ if (afi == AFI_IP)
+ attr.mp_nexthop_len = BGP_ATTR_NHLEN_IPV4;
+
+ if (bgp_static->igpmetric)
+ bgp_attr_set_aigp_metric(&attr, bgp_static->igpmetric);
+
+ if (bgp_static->atomic)
+ attr.flag |= ATTR_FLAG_BIT(BGP_ATTR_ATOMIC_AGGREGATE);
+
+ /* Store label index, if required. */
+ if (bgp_static->label_index != BGP_INVALID_LABEL_INDEX) {
+ attr.label_index = bgp_static->label_index;
+ attr.flag |= ATTR_FLAG_BIT(BGP_ATTR_PREFIX_SID);
+ }
+
+ if (safi == SAFI_EVPN || safi == SAFI_MPLS_VPN || safi == SAFI_ENCAP) {
+ if (afi == AFI_IP) {
+ attr.mp_nexthop_global_in = bgp_static->igpnexthop;
+ attr.mp_nexthop_len = IPV4_MAX_BYTELEN;
+ }
+ }
+
+ if (afi == AFI_L2VPN) {
+ if (bgp_static->gatewayIp.family == AF_INET) {
+ SET_IPADDR_V4(&attr.evpn_overlay.gw_ip);
+ memcpy(&attr.evpn_overlay.gw_ip.ipaddr_v4,
+ &bgp_static->gatewayIp.u.prefix4,
+ IPV4_MAX_BYTELEN);
+ } else if (bgp_static->gatewayIp.family == AF_INET6) {
+ SET_IPADDR_V6(&attr.evpn_overlay.gw_ip);
+ memcpy(&attr.evpn_overlay.gw_ip.ipaddr_v6,
+ &bgp_static->gatewayIp.u.prefix6,
+ IPV6_MAX_BYTELEN);
+ }
+ memcpy(&attr.esi, bgp_static->eth_s_id, sizeof(esi_t));
+ if (bgp_static->encap_tunneltype == BGP_ENCAP_TYPE_VXLAN) {
+ struct bgp_encap_type_vxlan bet;
+ memset(&bet, 0, sizeof(bet));
+ bet.vnid = p->u.prefix_evpn.prefix_addr.eth_tag;
+ bgp_encap_type_vxlan_to_tlv(&bet, &attr);
+ }
+ if (bgp_static->router_mac) {
+ bgp_add_routermac_ecom(&attr, bgp_static->router_mac);
+ }
+ }
+
+ /* Apply route-map. */
+ if (bgp_static->rmap.name) {
+ struct attr attr_tmp = attr;
+
+ memset(&rmap_path, 0, sizeof(rmap_path));
+ rmap_path.peer = bgp->peer_self;
+ rmap_path.attr = &attr_tmp;
+
+ SET_FLAG(bgp->peer_self->rmap_type, PEER_RMAP_TYPE_NETWORK);
+
+ ret = route_map_apply(bgp_static->rmap.map, p, &rmap_path);
+
+ bgp->peer_self->rmap_type = 0;
+
+ if (ret == RMAP_DENYMATCH) {
+ /* Free uninterned attribute. */
+ bgp_attr_flush(&attr_tmp);
+
+ /* Unintern original. */
+ aspath_unintern(&attr.aspath);
+ bgp_static_withdraw(bgp, p, afi, safi, &bgp_static->prd);
+ bgp_dest_unlock_node(dest);
+ return;
+ }
+
+ if (bgp_in_graceful_shutdown(bgp))
+ bgp_attr_add_gshut_community(&attr_tmp);
+
+ attr_new = bgp_attr_intern(&attr_tmp);
+ } else {
+
+ if (bgp_in_graceful_shutdown(bgp))
+ bgp_attr_add_gshut_community(&attr);
+
+ attr_new = bgp_attr_intern(&attr);
+ }
+
+ for (pi = bgp_dest_get_bgp_path_info(dest); pi; pi = pi->next)
+ if (pi->peer == bgp->peer_self && pi->type == ZEBRA_ROUTE_BGP
+ && pi->sub_type == BGP_ROUTE_STATIC)
+ break;
+
+ if (pi) {
+ if (attrhash_cmp(pi->attr, attr_new)
+ && !CHECK_FLAG(pi->flags, BGP_PATH_REMOVED)
+ && !CHECK_FLAG(bgp->flags, BGP_FLAG_FORCE_STATIC_PROCESS)) {
+ bgp_dest_unlock_node(dest);
+ bgp_attr_unintern(&attr_new);
+ aspath_unintern(&attr.aspath);
+ return;
+ } else {
+ /* The attribute is changed. */
+ bgp_path_info_set_flag(dest, pi, BGP_PATH_ATTR_CHANGED);
+
+ /* Rewrite BGP route information. */
+ if (CHECK_FLAG(pi->flags, BGP_PATH_REMOVED))
+ bgp_path_info_restore(dest, pi);
+ else
+ bgp_aggregate_decrement(bgp, p, pi, afi, safi);
+#ifdef ENABLE_BGP_VNC
+ if ((afi == AFI_IP || afi == AFI_IP6) &&
+ safi == SAFI_UNICAST) {
+ if (CHECK_FLAG(pi->flags, BGP_PATH_SELECTED)) {
+ /*
+ * Implicit withdraw case.
+ * We have to do this before pi is
+ * changed
+ */
+ ++vnc_implicit_withdraw;
+ vnc_import_bgp_del_route(bgp, p, pi);
+ vnc_import_bgp_exterior_del_route(
+ bgp, p, pi);
+ }
+ }
+#endif
+ bgp_attr_unintern(&pi->attr);
+ pi->attr = attr_new;
+ pi->uptime = monotime(NULL);
+#ifdef ENABLE_BGP_VNC
+ if ((afi == AFI_IP || afi == AFI_IP6) &&
+ safi == SAFI_UNICAST) {
+ if (vnc_implicit_withdraw) {
+ vnc_import_bgp_add_route(bgp, p, pi);
+ vnc_import_bgp_exterior_add_route(
+ bgp, p, pi);
+ }
+ } else {
+ if (pi->extra)
+ label = decode_label(
+ &pi->extra->label[0]);
+ }
+#endif
+
+ bgp_nexthop_reachability_check(afi, safi, pi, p, dest,
+ bgp);
+
+ /* Process change. */
+ bgp_aggregate_increment(bgp, p, pi, afi, safi);
+ bgp_process(bgp, dest, afi, safi);
+
+ if (SAFI_MPLS_VPN == safi &&
+ bgp->inst_type == BGP_INSTANCE_TYPE_DEFAULT) {
+ vpn_leak_to_vrf_update(bgp, pi,
+ &bgp_static->prd);
+ }
+#ifdef ENABLE_BGP_VNC
+ if (safi == SAFI_MPLS_VPN || safi == SAFI_ENCAP ||
+ safi == SAFI_EVPN)
+ rfapiProcessUpdate(pi->peer, NULL, p,
+ &bgp_static->prd, pi->attr,
+ afi, safi, pi->type,
+ pi->sub_type, &label);
+#endif
+
+ if (SAFI_UNICAST == safi
+ && (bgp->inst_type == BGP_INSTANCE_TYPE_VRF
+ || bgp->inst_type
+ == BGP_INSTANCE_TYPE_DEFAULT)) {
+ vpn_leak_from_vrf_update(bgp_get_default(), bgp,
+ pi);
+ }
+
+ bgp_dest_unlock_node(dest);
+ aspath_unintern(&attr.aspath);
+ return;
+ }
+ }
+
+ /* Make new BGP info. */
+ new = info_make(ZEBRA_ROUTE_BGP, BGP_ROUTE_STATIC, 0, bgp->peer_self,
+ attr_new, dest);
+
+ if (safi == SAFI_MPLS_VPN || safi == SAFI_ENCAP || safi == SAFI_EVPN) {
+ SET_FLAG(new->flags, BGP_PATH_VALID);
+ bgp_path_info_extra_get(new);
+ if (num_labels) {
+ new->extra->label[0] = bgp_static->label;
+ new->extra->num_labels = num_labels;
+ }
+#ifdef ENABLE_BGP_VNC
+ label = decode_label(&bgp_static->label);
+#endif
+ }
+
+ bgp_nexthop_reachability_check(afi, safi, new, p, dest, bgp);
+
+ /* Aggregate address increment. */
+ bgp_aggregate_increment(bgp, p, new, afi, safi);
+
+ /* Register new BGP information. */
+ bgp_path_info_add(dest, new);
+
+ /* route_node_get lock */
+ bgp_dest_unlock_node(dest);
+
+ /* Process change. */
+ bgp_process(bgp, dest, afi, safi);
+
+ if (SAFI_UNICAST == safi &&
+ (bgp->inst_type == BGP_INSTANCE_TYPE_VRF ||
+ bgp->inst_type == BGP_INSTANCE_TYPE_DEFAULT)) {
+ vpn_leak_from_vrf_update(bgp_get_default(), bgp, new);
+ }
+
+ if (SAFI_MPLS_VPN == safi &&
+ bgp->inst_type == BGP_INSTANCE_TYPE_DEFAULT) {
+ vpn_leak_to_vrf_update(bgp, new, &bgp_static->prd);
+ }
+#ifdef ENABLE_BGP_VNC
+ if (safi == SAFI_MPLS_VPN || safi == SAFI_ENCAP || safi == SAFI_EVPN)
+ rfapiProcessUpdate(new->peer, NULL, p, &bgp_static->prd,
+ new->attr, afi, safi, new->type,
+ new->sub_type, &label);
+#endif
+
+ /* Unintern original. */
+ aspath_unintern(&attr.aspath);
+}
+
+void bgp_static_withdraw(struct bgp *bgp, const struct prefix *p, afi_t afi,
+ safi_t safi, struct prefix_rd *prd)
+{
+ struct bgp_dest *dest;
+ struct bgp_path_info *pi;
+
+ dest = bgp_afi_node_get(bgp->rib[afi][safi], afi, safi, p, prd);
+
+ /* Check selected route and self inserted route. */
+ for (pi = bgp_dest_get_bgp_path_info(dest); pi; pi = pi->next)
+ if (pi->peer == bgp->peer_self && pi->type == ZEBRA_ROUTE_BGP
+ && pi->sub_type == BGP_ROUTE_STATIC)
+ break;
+
+ /* Withdraw static BGP route from routing table. */
+ if (pi) {
+#ifdef ENABLE_BGP_VNC
+ if (safi == SAFI_MPLS_VPN || safi == SAFI_ENCAP)
+ rfapiProcessWithdraw(pi->peer, NULL, p, prd, pi->attr,
+ afi, safi, pi->type,
+ 1); /* Kill, since it is an administrative change */
+#endif
+ if (SAFI_UNICAST == safi &&
+ (bgp->inst_type == BGP_INSTANCE_TYPE_VRF ||
+ bgp->inst_type == BGP_INSTANCE_TYPE_DEFAULT)) {
+ vpn_leak_from_vrf_withdraw(bgp_get_default(), bgp, pi);
+ }
+ if (SAFI_MPLS_VPN == safi
+ && bgp->inst_type == BGP_INSTANCE_TYPE_DEFAULT) {
+ vpn_leak_to_vrf_withdraw(pi);
+ }
+ bgp_aggregate_decrement(bgp, p, pi, afi, safi);
+ bgp_unlink_nexthop(pi);
+ bgp_path_info_delete(dest, pi);
+ bgp_process(bgp, dest, afi, safi);
+ }
+
+ /* Unlock bgp_node_lookup. */
+ bgp_dest_unlock_node(dest);
+}
+
+/* Configure static BGP network. When user don't run zebra, static
+ route should be installed as valid. */
+int bgp_static_set(struct vty *vty, bool negate, const char *ip_str,
+ const char *rd_str, const char *label_str, afi_t afi,
+ safi_t safi, const char *rmap, int backdoor,
+ uint32_t label_index, int evpn_type, const char *esi,
+ const char *gwip, const char *ethtag, const char *routermac)
+{
+ VTY_DECLVAR_CONTEXT(bgp, bgp);
+ int ret;
+ struct prefix p;
+ struct bgp_static *bgp_static;
+ struct prefix_rd prd = {};
+ struct bgp_dest *pdest;
+ struct bgp_dest *dest;
+ struct bgp_table *table;
+ uint8_t need_update = 0;
+ mpls_label_t label = MPLS_INVALID_LABEL;
+ struct prefix gw_ip;
+
+ /* Convert IP prefix string to struct prefix. */
+ ret = str2prefix(ip_str, &p);
+ if (!ret) {
+ vty_out(vty, "%% Malformed prefix\n");
+ return CMD_WARNING_CONFIG_FAILED;
+ }
+ if (afi == AFI_IP6 && IN6_IS_ADDR_LINKLOCAL(&p.u.prefix6)) {
+ vty_out(vty, "%% Malformed prefix (link-local address)\n");
+ return CMD_WARNING_CONFIG_FAILED;
+ }
+
+ apply_mask(&p);
+
+ if (afi == AFI_L2VPN &&
+ (bgp_build_evpn_prefix(evpn_type, ethtag != NULL ? atol(ethtag) : 0,
+ &p))) {
+ vty_out(vty, "%% L2VPN prefix could not be forged\n");
+ return CMD_WARNING_CONFIG_FAILED;
+ }
+
+ if (safi == SAFI_MPLS_VPN || safi == SAFI_EVPN) {
+ ret = str2prefix_rd(rd_str, &prd);
+ if (!ret) {
+ vty_out(vty, "%% Malformed rd\n");
+ return CMD_WARNING_CONFIG_FAILED;
+ }
+
+ if (label_str) {
+ unsigned long label_val;
+
+ label_val = strtoul(label_str, NULL, 10);
+ encode_label(label_val, &label);
+ }
+ }
+
+ if (safi == SAFI_EVPN) {
+ if (esi && str2esi(esi, NULL) == 0) {
+ vty_out(vty, "%% Malformed ESI\n");
+ return CMD_WARNING_CONFIG_FAILED;
+ }
+ if (routermac && prefix_str2mac(routermac, NULL) == 0) {
+ vty_out(vty, "%% Malformed Router MAC\n");
+ return CMD_WARNING_CONFIG_FAILED;
+ }
+ if (gwip) {
+ memset(&gw_ip, 0, sizeof(gw_ip));
+ ret = str2prefix(gwip, &gw_ip);
+ if (!ret) {
+ vty_out(vty, "%% Malformed GatewayIp\n");
+ return CMD_WARNING_CONFIG_FAILED;
+ }
+ if ((gw_ip.family == AF_INET &&
+ is_evpn_prefix_ipaddr_v6((struct prefix_evpn *)&p)) ||
+ (gw_ip.family == AF_INET6 &&
+ is_evpn_prefix_ipaddr_v4(
+ (struct prefix_evpn *)&p))) {
+ vty_out(vty,
+ "%% GatewayIp family differs with IP prefix\n");
+ return CMD_WARNING_CONFIG_FAILED;
+ }
+ }
+ }
+
+ if (safi == SAFI_MPLS_VPN || safi == SAFI_EVPN) {
+ pdest = bgp_node_get(bgp->route[afi][safi],
+ (struct prefix *)&prd);
+ if (!bgp_dest_has_bgp_path_info_data(pdest))
+ bgp_dest_set_bgp_table_info(pdest,
+ bgp_table_init(bgp, afi,
+ safi));
+ table = bgp_dest_get_bgp_table_info(pdest);
+ } else {
+ table = bgp->route[afi][safi];
+ }
+
+ if (negate) {
+ /* Set BGP static route configuration. */
+ dest = bgp_node_lookup(bgp->route[afi][safi], &p);
+
+ if (!dest) {
+ vty_out(vty, "%% Can't find static route specified\n");
+ return CMD_WARNING_CONFIG_FAILED;
+ }
+
+ bgp_static = bgp_dest_get_bgp_static_info(dest);
+ if (bgp_static) {
+ if ((label_index != BGP_INVALID_LABEL_INDEX) &&
+ (label_index != bgp_static->label_index)) {
+ vty_out(vty,
+ "%% label-index doesn't match static route\n");
+ bgp_dest_unlock_node(dest);
+ return CMD_WARNING_CONFIG_FAILED;
+ }
+
+ if ((rmap && bgp_static->rmap.name) &&
+ strcmp(rmap, bgp_static->rmap.name)) {
+ vty_out(vty,
+ "%% route-map name doesn't match static route\n");
+ bgp_dest_unlock_node(dest);
+ return CMD_WARNING_CONFIG_FAILED;
+ }
+
+ /* Update BGP RIB. */
+ if (!bgp_static->backdoor)
+ bgp_static_withdraw(bgp, &p, afi, safi, NULL);
+
+ /* Clear configuration. */
+ bgp_static_free(bgp_static);
+ }
+
+ bgp_dest_set_bgp_static_info(dest, NULL);
+ dest = bgp_dest_unlock_node(dest);
+ assert(dest);
+ bgp_dest_unlock_node(dest);
+ } else {
+ dest = bgp_node_get(table, &p);
+
+ bgp_static = bgp_dest_get_bgp_static_info(dest);
+ if (bgp_static) {
+ /* Configuration change. */
+ /* Label index cannot be changed. */
+ if (bgp_static->label_index != label_index) {
+ vty_out(vty, "%% cannot change label-index\n");
+ bgp_dest_unlock_node(dest);
+ return CMD_WARNING_CONFIG_FAILED;
+ }
+
+ /* Check previous routes are installed into BGP. */
+ if (bgp_static->valid
+ && bgp_static->backdoor != backdoor)
+ need_update = 1;
+
+ bgp_static->backdoor = backdoor;
+
+ if (rmap) {
+ XFREE(MTYPE_ROUTE_MAP_NAME,
+ bgp_static->rmap.name);
+ route_map_counter_decrement(
+ bgp_static->rmap.map);
+ bgp_static->rmap.name =
+ XSTRDUP(MTYPE_ROUTE_MAP_NAME, rmap);
+ bgp_static->rmap.map =
+ route_map_lookup_by_name(rmap);
+ route_map_counter_increment(
+ bgp_static->rmap.map);
+ } else {
+ XFREE(MTYPE_ROUTE_MAP_NAME,
+ bgp_static->rmap.name);
+ route_map_counter_decrement(
+ bgp_static->rmap.map);
+ bgp_static->rmap.map = NULL;
+ bgp_static->valid = 0;
+ }
+ bgp_dest_unlock_node(dest);
+ } else {
+ /* New configuration. */
+ bgp_static = bgp_static_new();
+ bgp_static->backdoor = backdoor;
+ bgp_static->valid = 0;
+ bgp_static->igpmetric = 0;
+ bgp_static->igpnexthop.s_addr = INADDR_ANY;
+ bgp_static->label_index = label_index;
+ bgp_static->label = label;
+ bgp_static->prd = prd;
+
+ if (rmap) {
+ XFREE(MTYPE_ROUTE_MAP_NAME,
+ bgp_static->rmap.name);
+ route_map_counter_decrement(
+ bgp_static->rmap.map);
+ bgp_static->rmap.name =
+ XSTRDUP(MTYPE_ROUTE_MAP_NAME, rmap);
+ bgp_static->rmap.map =
+ route_map_lookup_by_name(rmap);
+ route_map_counter_increment(
+ bgp_static->rmap.map);
+ }
+
+ if (safi == SAFI_EVPN) {
+ if (esi) {
+ bgp_static->eth_s_id =
+ XCALLOC(MTYPE_ATTR,
+ sizeof(esi_t));
+ str2esi(esi, bgp_static->eth_s_id);
+ }
+ if (routermac) {
+ bgp_static->router_mac =
+ XCALLOC(MTYPE_ATTR,
+ ETH_ALEN + 1);
+ (void)prefix_str2mac(routermac,
+ bgp_static->router_mac);
+ }
+ if (gwip)
+ prefix_copy(&bgp_static->gatewayIp,
+ &gw_ip);
+ }
+
+ bgp_dest_set_bgp_static_info(dest, bgp_static);
+ }
+
+ bgp_static->valid = 1;
+ if (need_update)
+ bgp_static_withdraw(bgp, &p, afi, safi, NULL);
+
+ if (!bgp_static->backdoor)
+ bgp_static_update(bgp, &p, bgp_static, afi, safi);
+ }
+
+ return CMD_SUCCESS;
+}
+
+void bgp_static_add(struct bgp *bgp)
+{
+ afi_t afi;
+ safi_t safi;
+ struct bgp_dest *dest;
+ struct bgp_dest *rm;
+ struct bgp_table *table;
+ struct bgp_static *bgp_static;
+
+ SET_FLAG(bgp->flags, BGP_FLAG_FORCE_STATIC_PROCESS);
+ FOREACH_AFI_SAFI (afi, safi)
+ for (dest = bgp_table_top(bgp->route[afi][safi]); dest;
+ dest = bgp_route_next(dest)) {
+ if (!bgp_dest_has_bgp_path_info_data(dest))
+ continue;
+
+ if ((safi == SAFI_MPLS_VPN) || (safi == SAFI_ENCAP)
+ || (safi == SAFI_EVPN)) {
+ table = bgp_dest_get_bgp_table_info(dest);
+
+ for (rm = bgp_table_top(table); rm;
+ rm = bgp_route_next(rm)) {
+ bgp_static =
+ bgp_dest_get_bgp_static_info(
+ rm);
+ bgp_static_update(bgp,
+ bgp_dest_get_prefix(rm),
+ bgp_static, afi, safi);
+ }
+ } else {
+ bgp_static_update(
+ bgp, bgp_dest_get_prefix(dest),
+ bgp_dest_get_bgp_static_info(dest), afi,
+ safi);
+ }
+ }
+ UNSET_FLAG(bgp->flags, BGP_FLAG_FORCE_STATIC_PROCESS);
+}
+
+/* Called from bgp_delete(). Delete all static routes from the BGP
+ instance. */
+void bgp_static_delete(struct bgp *bgp)
+{
+ afi_t afi;
+ safi_t safi;
+ struct bgp_dest *dest;
+ struct bgp_dest *rm;
+ struct bgp_table *table;
+ struct bgp_static *bgp_static;
+
+ FOREACH_AFI_SAFI (afi, safi)
+ for (dest = bgp_table_top(bgp->route[afi][safi]); dest;
+ dest = bgp_route_next(dest)) {
+ if (!bgp_dest_has_bgp_path_info_data(dest))
+ continue;
+
+ if ((safi == SAFI_MPLS_VPN) || (safi == SAFI_ENCAP)
+ || (safi == SAFI_EVPN)) {
+ table = bgp_dest_get_bgp_table_info(dest);
+
+ for (rm = bgp_table_top(table); rm;
+ rm = bgp_route_next(rm)) {
+ bgp_static =
+ bgp_dest_get_bgp_static_info(
+ rm);
+ if (!bgp_static)
+ continue;
+
+ bgp_static_withdraw(bgp,
+ bgp_dest_get_prefix(
+ rm),
+ AFI_IP, safi,
+ (struct prefix_rd *)
+ bgp_dest_get_prefix(
+ dest));
+ bgp_static_free(bgp_static);
+ bgp_dest_set_bgp_static_info(rm,
+ NULL);
+ rm = bgp_dest_unlock_node(rm);
+ assert(rm);
+ }
+ } else {
+ bgp_static = bgp_dest_get_bgp_static_info(dest);
+ bgp_static_withdraw(bgp,
+ bgp_dest_get_prefix(dest),
+ afi, safi, NULL);
+ bgp_static_free(bgp_static);
+ bgp_dest_set_bgp_static_info(dest, NULL);
+ dest = bgp_dest_unlock_node(dest);
+ assert(dest);
+ }
+ }
+}
+
+void bgp_static_redo_import_check(struct bgp *bgp)
+{
+ afi_t afi;
+ safi_t safi;
+ struct bgp_dest *dest;
+ struct bgp_dest *rm;
+ struct bgp_table *table;
+ struct bgp_static *bgp_static;
+
+ /* Use this flag to force reprocessing of the route */
+ SET_FLAG(bgp->flags, BGP_FLAG_FORCE_STATIC_PROCESS);
+ FOREACH_AFI_SAFI (afi, safi) {
+ for (dest = bgp_table_top(bgp->route[afi][safi]); dest;
+ dest = bgp_route_next(dest)) {
+ if (!bgp_dest_has_bgp_path_info_data(dest))
+ continue;
+
+ if ((safi == SAFI_MPLS_VPN) || (safi == SAFI_ENCAP)
+ || (safi == SAFI_EVPN)) {
+ table = bgp_dest_get_bgp_table_info(dest);
+
+ for (rm = bgp_table_top(table); rm;
+ rm = bgp_route_next(rm)) {
+ bgp_static =
+ bgp_dest_get_bgp_static_info(
+ rm);
+ bgp_static_update(bgp,
+ bgp_dest_get_prefix(rm),
+ bgp_static, afi, safi);
+ }
+ } else {
+ bgp_static = bgp_dest_get_bgp_static_info(dest);
+ bgp_static_update(bgp,
+ bgp_dest_get_prefix(dest),
+ bgp_static, afi, safi);
+ }
+ }
+ }
+ UNSET_FLAG(bgp->flags, BGP_FLAG_FORCE_STATIC_PROCESS);
+}
+
+static void bgp_purge_af_static_redist_routes(struct bgp *bgp, afi_t afi,
+ safi_t safi)
+{
+ struct bgp_table *table;
+ struct bgp_dest *dest;
+ struct bgp_path_info *pi;
+
+ /* Do not install the aggregate route if BGP is in the
+ * process of termination.
+ */
+ if (CHECK_FLAG(bgp->flags, BGP_FLAG_DELETE_IN_PROGRESS)
+ || (bgp->peer_self == NULL))
+ return;
+
+ table = bgp->rib[afi][safi];
+ for (dest = bgp_table_top(table); dest; dest = bgp_route_next(dest)) {
+ for (pi = bgp_dest_get_bgp_path_info(dest); pi; pi = pi->next) {
+ if (pi->peer == bgp->peer_self
+ && ((pi->type == ZEBRA_ROUTE_BGP
+ && pi->sub_type == BGP_ROUTE_STATIC)
+ || (pi->type != ZEBRA_ROUTE_BGP
+ && pi->sub_type
+ == BGP_ROUTE_REDISTRIBUTE))) {
+ bgp_aggregate_decrement(
+ bgp, bgp_dest_get_prefix(dest), pi, afi,
+ safi);
+ bgp_unlink_nexthop(pi);
+ bgp_path_info_delete(dest, pi);
+ bgp_process(bgp, dest, afi, safi);
+ }
+ }
+ }
+}
+
+/*
+ * Purge all networks and redistributed routes from routing table.
+ * Invoked upon the instance going down.
+ */
+void bgp_purge_static_redist_routes(struct bgp *bgp)
+{
+ afi_t afi;
+ safi_t safi;
+
+ FOREACH_AFI_SAFI (afi, safi)
+ bgp_purge_af_static_redist_routes(bgp, afi, safi);
+}
+
+static int bgp_table_map_set(struct vty *vty, afi_t afi, safi_t safi,
+ const char *rmap_name)
+{
+ VTY_DECLVAR_CONTEXT(bgp, bgp);
+ struct bgp_rmap *rmap;
+
+ rmap = &bgp->table_map[afi][safi];
+ if (rmap_name) {
+ XFREE(MTYPE_ROUTE_MAP_NAME, rmap->name);
+ route_map_counter_decrement(rmap->map);
+ rmap->name = XSTRDUP(MTYPE_ROUTE_MAP_NAME, rmap_name);
+ rmap->map = route_map_lookup_by_name(rmap_name);
+ route_map_counter_increment(rmap->map);
+ } else {
+ XFREE(MTYPE_ROUTE_MAP_NAME, rmap->name);
+ route_map_counter_decrement(rmap->map);
+ rmap->map = NULL;
+ }
+
+ if (bgp_fibupd_safi(safi))
+ bgp_zebra_announce_table(bgp, afi, safi);
+
+ return CMD_SUCCESS;
+}
+
+static int bgp_table_map_unset(struct vty *vty, afi_t afi, safi_t safi,
+ const char *rmap_name)
+{
+ VTY_DECLVAR_CONTEXT(bgp, bgp);
+ struct bgp_rmap *rmap;
+
+ rmap = &bgp->table_map[afi][safi];
+ XFREE(MTYPE_ROUTE_MAP_NAME, rmap->name);
+ route_map_counter_decrement(rmap->map);
+ rmap->map = NULL;
+
+ if (bgp_fibupd_safi(safi))
+ bgp_zebra_announce_table(bgp, afi, safi);
+
+ return CMD_SUCCESS;
+}
+
+void bgp_config_write_table_map(struct vty *vty, struct bgp *bgp, afi_t afi,
+ safi_t safi)
+{
+ if (bgp->table_map[afi][safi].name) {
+ vty_out(vty, " table-map %s\n",
+ bgp->table_map[afi][safi].name);
+ }
+}
+
+DEFUN (bgp_table_map,
+ bgp_table_map_cmd,
+ "table-map WORD",
+ "BGP table to RIB route download filter\n"
+ "Name of the route map\n")
+{
+ int idx_word = 1;
+ return bgp_table_map_set(vty, bgp_node_afi(vty), bgp_node_safi(vty),
+ argv[idx_word]->arg);
+}
+DEFUN (no_bgp_table_map,
+ no_bgp_table_map_cmd,
+ "no table-map WORD",
+ NO_STR
+ "BGP table to RIB route download filter\n"
+ "Name of the route map\n")
+{
+ int idx_word = 2;
+ return bgp_table_map_unset(vty, bgp_node_afi(vty), bgp_node_safi(vty),
+ argv[idx_word]->arg);
+}
+
+DEFPY(bgp_network,
+ bgp_network_cmd,
+ "[no] network \
+ <A.B.C.D/M$prefix|A.B.C.D$address [mask A.B.C.D$netmask]> \
+ [{route-map RMAP_NAME$map_name|label-index (0-1048560)$label_index| \
+ backdoor$backdoor}]",
+ NO_STR
+ "Specify a network to announce via BGP\n"
+ "IPv4 prefix\n"
+ "Network number\n"
+ "Network mask\n"
+ "Network mask\n"
+ "Route-map to modify the attributes\n"
+ "Name of the route map\n"
+ "Label index to associate with the prefix\n"
+ "Label index value\n"
+ "Specify a BGP backdoor route\n")
+{
+ char addr_prefix_str[BUFSIZ];
+
+ if (address_str) {
+ int ret;
+
+ ret = netmask_str2prefix_str(address_str, netmask_str,
+ addr_prefix_str,
+ sizeof(addr_prefix_str));
+ if (!ret) {
+ vty_out(vty, "%% Inconsistent address and mask\n");
+ return CMD_WARNING_CONFIG_FAILED;
+ }
+ }
+
+ return bgp_static_set(vty, no,
+ address_str ? addr_prefix_str : prefix_str, NULL,
+ NULL, AFI_IP, bgp_node_safi(vty), map_name,
+ backdoor ? 1 : 0,
+ label_index ? (uint32_t)label_index
+ : BGP_INVALID_LABEL_INDEX,
+ 0, NULL, NULL, NULL, NULL);
+}
+
+DEFPY(ipv6_bgp_network,
+ ipv6_bgp_network_cmd,
+ "[no] network X:X::X:X/M$prefix \
+ [{route-map RMAP_NAME$map_name|label-index (0-1048560)$label_index}]",
+ NO_STR
+ "Specify a network to announce via BGP\n"
+ "IPv6 prefix\n"
+ "Route-map to modify the attributes\n"
+ "Name of the route map\n"
+ "Label index to associate with the prefix\n"
+ "Label index value\n")
+{
+ return bgp_static_set(vty, no, prefix_str, NULL, NULL, AFI_IP6,
+ bgp_node_safi(vty), map_name, 0,
+ label_index ? (uint32_t)label_index
+ : BGP_INVALID_LABEL_INDEX,
+ 0, NULL, NULL, NULL, NULL);
+}
+
+static struct bgp_aggregate *bgp_aggregate_new(void)
+{
+ return XCALLOC(MTYPE_BGP_AGGREGATE, sizeof(struct bgp_aggregate));
+}
+
+void bgp_aggregate_free(struct bgp_aggregate *aggregate)
+{
+ XFREE(MTYPE_ROUTE_MAP_NAME, aggregate->suppress_map_name);
+ route_map_counter_decrement(aggregate->suppress_map);
+ XFREE(MTYPE_ROUTE_MAP_NAME, aggregate->rmap.name);
+ route_map_counter_decrement(aggregate->rmap.map);
+ XFREE(MTYPE_BGP_AGGREGATE, aggregate);
+}
+
+/**
+ * Helper function to avoid repeated code: prepare variables for a
+ * `route_map_apply` call.
+ *
+ * \returns `true` on route map match, otherwise `false`.
+ */
+static bool aggr_suppress_map_test(struct bgp *bgp,
+ struct bgp_aggregate *aggregate,
+ struct bgp_path_info *pi)
+{
+ const struct prefix *p = bgp_dest_get_prefix(pi->net);
+ route_map_result_t rmr = RMAP_DENYMATCH;
+ struct bgp_path_info rmap_path = {};
+ struct attr attr = {};
+
+ /* No route map entries created, just don't match. */
+ if (aggregate->suppress_map == NULL)
+ return false;
+
+ /* Call route map matching and return result. */
+ attr.aspath = aspath_empty(bgp->asnotation);
+ rmap_path.peer = bgp->peer_self;
+ rmap_path.attr = &attr;
+
+ SET_FLAG(bgp->peer_self->rmap_type, PEER_RMAP_TYPE_AGGREGATE);
+ rmr = route_map_apply(aggregate->suppress_map, p, &rmap_path);
+ bgp->peer_self->rmap_type = 0;
+
+ bgp_attr_flush(&attr);
+ aspath_unintern(&attr.aspath);
+
+ return rmr == RMAP_PERMITMATCH;
+}
+
+/** Test whether the aggregation has suppressed this path or not. */
+static bool aggr_suppress_exists(struct bgp_aggregate *aggregate,
+ struct bgp_path_info *pi)
+{
+ if (pi->extra == NULL || pi->extra->aggr_suppressors == NULL)
+ return false;
+
+ return listnode_lookup(pi->extra->aggr_suppressors, aggregate) != NULL;
+}
+
+/**
+ * Suppress this path and keep the reference.
+ *
+ * \returns `true` if needs processing otherwise `false`.
+ */
+static bool aggr_suppress_path(struct bgp_aggregate *aggregate,
+ struct bgp_path_info *pi)
+{
+ struct bgp_path_info_extra *pie;
+
+ /* Path is already suppressed by this aggregation. */
+ if (aggr_suppress_exists(aggregate, pi))
+ return false;
+
+ pie = bgp_path_info_extra_get(pi);
+
+ /* This is the first suppression, allocate memory and list it. */
+ if (pie->aggr_suppressors == NULL)
+ pie->aggr_suppressors = list_new();
+
+ listnode_add(pie->aggr_suppressors, aggregate);
+
+ /* Only mark for processing if suppressed. */
+ if (listcount(pie->aggr_suppressors) == 1) {
+ if (BGP_DEBUG(update, UPDATE_OUT))
+ zlog_debug("aggregate-address suppressing: %pFX",
+ bgp_dest_get_prefix(pi->net));
+
+ bgp_path_info_set_flag(pi->net, pi, BGP_PATH_ATTR_CHANGED);
+ return true;
+ }
+
+ return false;
+}
+
+/**
+ * Unsuppress this path and remove the reference.
+ *
+ * \returns `true` if needs processing otherwise `false`.
+ */
+static bool aggr_unsuppress_path(struct bgp_aggregate *aggregate,
+ struct bgp_path_info *pi)
+{
+ /* Path wasn't suppressed. */
+ if (!aggr_suppress_exists(aggregate, pi))
+ return false;
+
+ listnode_delete(pi->extra->aggr_suppressors, aggregate);
+
+ /* Unsuppress and free extra memory if last item. */
+ if (listcount(pi->extra->aggr_suppressors) == 0) {
+ if (BGP_DEBUG(update, UPDATE_OUT))
+ zlog_debug("aggregate-address unsuppressing: %pFX",
+ bgp_dest_get_prefix(pi->net));
+
+ list_delete(&pi->extra->aggr_suppressors);
+ bgp_path_info_set_flag(pi->net, pi, BGP_PATH_ATTR_CHANGED);
+ return true;
+ }
+
+ return false;
+}
+
+static bool bgp_aggregate_info_same(struct bgp_path_info *pi, uint8_t origin,
+ struct aspath *aspath,
+ struct community *comm,
+ struct ecommunity *ecomm,
+ struct lcommunity *lcomm)
+{
+ static struct aspath *ae = NULL;
+ enum asnotation_mode asnotation;
+
+ asnotation = bgp_get_asnotation(NULL);
+
+ if (!aspath)
+ ae = aspath_empty(asnotation);
+
+ if (!pi)
+ return false;
+
+ if (origin != pi->attr->origin)
+ return false;
+
+ if (!aspath_cmp(pi->attr->aspath, (aspath) ? aspath : ae))
+ return false;
+
+ if (!community_cmp(bgp_attr_get_community(pi->attr), comm))
+ return false;
+
+ if (!ecommunity_cmp(bgp_attr_get_ecommunity(pi->attr), ecomm))
+ return false;
+
+ if (!lcommunity_cmp(bgp_attr_get_lcommunity(pi->attr), lcomm))
+ return false;
+
+ if (!CHECK_FLAG(pi->flags, BGP_PATH_VALID))
+ return false;
+
+ return true;
+}
+
+static void bgp_aggregate_install(
+ struct bgp *bgp, afi_t afi, safi_t safi, const struct prefix *p,
+ uint8_t origin, struct aspath *aspath, struct community *community,
+ struct ecommunity *ecommunity, struct lcommunity *lcommunity,
+ uint8_t atomic_aggregate, struct bgp_aggregate *aggregate)
+{
+ struct bgp_dest *dest;
+ struct bgp_table *table;
+ struct bgp_path_info *pi, *orig, *new;
+ struct attr *attr;
+
+ table = bgp->rib[afi][safi];
+
+ dest = bgp_node_get(table, p);
+
+ for (orig = pi = bgp_dest_get_bgp_path_info(dest); pi; pi = pi->next)
+ if (pi->peer == bgp->peer_self && pi->type == ZEBRA_ROUTE_BGP
+ && pi->sub_type == BGP_ROUTE_AGGREGATE)
+ break;
+
+ /*
+ * If we have paths with different MEDs, then don't install
+ * (or uninstall) the aggregate route.
+ */
+ if (aggregate->match_med && aggregate->med_mismatched)
+ goto uninstall_aggregate_route;
+
+ if (aggregate->count > 0) {
+ /*
+ * If the aggregate information has not changed
+ * no need to re-install it again.
+ */
+ if (pi && bgp_aggregate_info_same(pi, origin, aspath, community,
+ ecommunity, lcommunity)) {
+ bgp_dest_unlock_node(dest);
+
+ if (aspath)
+ aspath_free(aspath);
+ if (community)
+ community_free(&community);
+ if (ecommunity)
+ ecommunity_free(&ecommunity);
+ if (lcommunity)
+ lcommunity_free(&lcommunity);
+
+ return;
+ }
+
+ /*
+ * Mark the old as unusable
+ */
+ if (pi)
+ bgp_path_info_delete(dest, pi);
+
+ attr = bgp_attr_aggregate_intern(
+ bgp, origin, aspath, community, ecommunity, lcommunity,
+ aggregate, atomic_aggregate, p);
+
+ if (!attr) {
+ aspath_free(aspath);
+ community_free(&community);
+ ecommunity_free(&ecommunity);
+ lcommunity_free(&lcommunity);
+ bgp_dest_unlock_node(dest);
+ bgp_aggregate_delete(bgp, p, afi, safi, aggregate);
+ if (BGP_DEBUG(update_groups, UPDATE_GROUPS))
+ zlog_debug("%s: %pFX null attribute", __func__,
+ p);
+ return;
+ }
+
+ new = info_make(ZEBRA_ROUTE_BGP, BGP_ROUTE_AGGREGATE, 0,
+ bgp->peer_self, attr, dest);
+
+ SET_FLAG(new->flags, BGP_PATH_VALID);
+
+ bgp_path_info_add(dest, new);
+ bgp_process(bgp, dest, afi, safi);
+ } else {
+ uninstall_aggregate_route:
+ for (pi = orig; pi; pi = pi->next)
+ if (pi->peer == bgp->peer_self
+ && pi->type == ZEBRA_ROUTE_BGP
+ && pi->sub_type == BGP_ROUTE_AGGREGATE)
+ break;
+
+ /* Withdraw static BGP route from routing table. */
+ if (pi) {
+ bgp_path_info_delete(dest, pi);
+ bgp_process(bgp, dest, afi, safi);
+ }
+ }
+
+ bgp_dest_unlock_node(dest);
+}
+
+/**
+ * Check if the current path has different MED than other known paths.
+ *
+ * \returns `true` if the MED matched the others else `false`.
+ */
+static bool bgp_aggregate_med_match(struct bgp_aggregate *aggregate,
+ struct bgp *bgp, struct bgp_path_info *pi)
+{
+ uint32_t cur_med = bgp_med_value(pi->attr, bgp);
+
+ /* This is the first route being analyzed. */
+ if (!aggregate->med_initialized) {
+ aggregate->med_initialized = true;
+ aggregate->med_mismatched = false;
+ aggregate->med_matched_value = cur_med;
+ } else {
+ /* Check if routes with different MED showed up. */
+ if (cur_med != aggregate->med_matched_value)
+ aggregate->med_mismatched = true;
+ }
+
+ return !aggregate->med_mismatched;
+}
+
+/**
+ * Initializes and tests all routes in the aggregate address path for MED
+ * values.
+ *
+ * \returns `true` if all MEDs are the same otherwise `false`.
+ */
+static bool bgp_aggregate_test_all_med(struct bgp_aggregate *aggregate,
+ struct bgp *bgp, const struct prefix *p,
+ afi_t afi, safi_t safi)
+{
+ struct bgp_table *table = bgp->rib[afi][safi];
+ const struct prefix *dest_p;
+ struct bgp_dest *dest, *top;
+ struct bgp_path_info *pi;
+ bool med_matched = true;
+
+ aggregate->med_initialized = false;
+
+ top = bgp_node_get(table, p);
+ for (dest = bgp_node_get(table, p); dest;
+ dest = bgp_route_next_until(dest, top)) {
+ dest_p = bgp_dest_get_prefix(dest);
+ if (dest_p->prefixlen <= p->prefixlen)
+ continue;
+
+ for (pi = bgp_dest_get_bgp_path_info(dest); pi; pi = pi->next) {
+ if (BGP_PATH_HOLDDOWN(pi))
+ continue;
+ if (pi->sub_type == BGP_ROUTE_AGGREGATE)
+ continue;
+ if (!bgp_aggregate_med_match(aggregate, bgp, pi)) {
+ med_matched = false;
+ break;
+ }
+ }
+ if (!med_matched)
+ break;
+ }
+ bgp_dest_unlock_node(top);
+
+ return med_matched;
+}
+
+/**
+ * Toggles the route suppression status for this aggregate address
+ * configuration.
+ */
+void bgp_aggregate_toggle_suppressed(struct bgp_aggregate *aggregate,
+ struct bgp *bgp, const struct prefix *p,
+ afi_t afi, safi_t safi, bool suppress)
+{
+ struct bgp_table *table = bgp->rib[afi][safi];
+ const struct prefix *dest_p;
+ struct bgp_dest *dest, *top;
+ struct bgp_path_info *pi;
+ bool toggle_suppression;
+
+ /* We've found a different MED we must revert any suppressed routes. */
+ top = bgp_node_get(table, p);
+ for (dest = bgp_node_get(table, p); dest;
+ dest = bgp_route_next_until(dest, top)) {
+ dest_p = bgp_dest_get_prefix(dest);
+ if (dest_p->prefixlen <= p->prefixlen)
+ continue;
+
+ toggle_suppression = false;
+ for (pi = bgp_dest_get_bgp_path_info(dest); pi; pi = pi->next) {
+ if (BGP_PATH_HOLDDOWN(pi))
+ continue;
+ if (pi->sub_type == BGP_ROUTE_AGGREGATE)
+ continue;
+
+ /* We are toggling suppression back. */
+ if (suppress) {
+ /* Suppress route if not suppressed already. */
+ if (aggr_suppress_path(aggregate, pi))
+ toggle_suppression = true;
+ continue;
+ }
+
+ /* Install route if there is no more suppression. */
+ if (aggr_unsuppress_path(aggregate, pi))
+ toggle_suppression = true;
+ }
+
+ if (toggle_suppression)
+ bgp_process(bgp, dest, afi, safi);
+ }
+ bgp_dest_unlock_node(top);
+}
+
+/**
+ * Aggregate address MED matching incremental test: this function is called
+ * when the initial aggregation occurred and we are only testing a single
+ * new path.
+ *
+ * In addition to testing and setting the MED validity it also installs back
+ * suppressed routes (if summary is configured).
+ *
+ * Must not be called in `bgp_aggregate_route`.
+ */
+static void bgp_aggregate_med_update(struct bgp_aggregate *aggregate,
+ struct bgp *bgp, const struct prefix *p,
+ afi_t afi, safi_t safi,
+ struct bgp_path_info *pi)
+{
+ /* MED matching disabled. */
+ if (!aggregate->match_med)
+ return;
+
+ /* Aggregation with different MED, recheck if we have got equal MEDs
+ * now.
+ */
+ if (aggregate->med_mismatched &&
+ bgp_aggregate_test_all_med(aggregate, bgp, p, afi, safi) &&
+ aggregate->summary_only)
+ bgp_aggregate_toggle_suppressed(aggregate, bgp, p, afi, safi,
+ true);
+ else
+ bgp_aggregate_med_match(aggregate, bgp, pi);
+
+ /* No mismatches, just quit. */
+ if (!aggregate->med_mismatched)
+ return;
+
+ /* Route summarization is disabled. */
+ if (!aggregate->summary_only)
+ return;
+
+ bgp_aggregate_toggle_suppressed(aggregate, bgp, p, afi, safi, false);
+}
+
+/* Update an aggregate as routes are added/removed from the BGP table */
+bool bgp_aggregate_route(struct bgp *bgp, const struct prefix *p, afi_t afi,
+ safi_t safi, struct bgp_aggregate *aggregate)
+{
+ struct bgp_table *table;
+ struct bgp_dest *top;
+ struct bgp_dest *dest;
+ uint8_t origin;
+ struct aspath *aspath = NULL;
+ struct community *community = NULL;
+ struct ecommunity *ecommunity = NULL;
+ struct lcommunity *lcommunity = NULL;
+ struct bgp_path_info *pi;
+ unsigned long match = 0;
+ uint8_t atomic_aggregate = 0;
+
+ /* If the bgp instance is being deleted or self peer is deleted
+ * then do not create aggregate route
+ */
+ if (CHECK_FLAG(bgp->flags, BGP_FLAG_DELETE_IN_PROGRESS) ||
+ bgp->peer_self == NULL)
+ return false;
+
+ /* Initialize and test routes for MED difference. */
+ if (aggregate->match_med)
+ bgp_aggregate_test_all_med(aggregate, bgp, p, afi, safi);
+
+ /*
+ * Reset aggregate count: we might've been called from route map
+ * update so in that case we must retest all more specific routes.
+ *
+ * \see `bgp_route_map_process_update`.
+ */
+ aggregate->count = 0;
+ aggregate->incomplete_origin_count = 0;
+ aggregate->incomplete_origin_count = 0;
+ aggregate->egp_origin_count = 0;
+
+ /* ORIGIN attribute: If at least one route among routes that are
+ aggregated has ORIGIN with the value INCOMPLETE, then the
+ aggregated route must have the ORIGIN attribute with the value
+ INCOMPLETE. Otherwise, if at least one route among routes that
+ are aggregated has ORIGIN with the value EGP, then the aggregated
+ route must have the origin attribute with the value EGP. In all
+ other case the value of the ORIGIN attribute of the aggregated
+ route is INTERNAL. */
+ origin = BGP_ORIGIN_IGP;
+
+ table = bgp->rib[afi][safi];
+
+ top = bgp_node_get(table, p);
+ for (dest = bgp_node_get(table, p); dest;
+ dest = bgp_route_next_until(dest, top)) {
+ const struct prefix *dest_p = bgp_dest_get_prefix(dest);
+
+ if (dest_p->prefixlen <= p->prefixlen)
+ continue;
+
+ /* If suppress fib is enabled and route not installed
+ * in FIB, skip the route
+ */
+ if (!bgp_check_advertise(bgp, dest, safi))
+ continue;
+
+ match = 0;
+
+ for (pi = bgp_dest_get_bgp_path_info(dest); pi; pi = pi->next) {
+ if (BGP_PATH_HOLDDOWN(pi))
+ continue;
+
+ if (pi->attr->flag
+ & ATTR_FLAG_BIT(BGP_ATTR_ATOMIC_AGGREGATE))
+ atomic_aggregate = 1;
+
+ if (pi->sub_type == BGP_ROUTE_AGGREGATE)
+ continue;
+
+ /*
+ * summary-only aggregate route suppress
+ * aggregated route announcements.
+ *
+ * MED matching:
+ * Don't create summaries if MED didn't match
+ * otherwise neither the specific routes and the
+ * aggregation will be announced.
+ */
+ if (aggregate->summary_only
+ && AGGREGATE_MED_VALID(aggregate)) {
+ if (aggr_suppress_path(aggregate, pi))
+ match++;
+ }
+
+ /*
+ * Suppress more specific routes that match the route
+ * map results.
+ *
+ * MED matching:
+ * Don't suppress routes if MED matching is enabled and
+ * it mismatched otherwise we might end up with no
+ * routes for this path.
+ */
+ if (aggregate->suppress_map_name
+ && AGGREGATE_MED_VALID(aggregate)
+ && aggr_suppress_map_test(bgp, aggregate, pi)) {
+ if (aggr_suppress_path(aggregate, pi))
+ match++;
+ }
+
+ aggregate->count++;
+
+ /*
+ * If at least one route among routes that are
+ * aggregated has ORIGIN with the value INCOMPLETE,
+ * then the aggregated route MUST have the ORIGIN
+ * attribute with the value INCOMPLETE. Otherwise, if
+ * at least one route among routes that are aggregated
+ * has ORIGIN with the value EGP, then the aggregated
+ * route MUST have the ORIGIN attribute with the value
+ * EGP.
+ */
+ switch (pi->attr->origin) {
+ case BGP_ORIGIN_INCOMPLETE:
+ aggregate->incomplete_origin_count++;
+ break;
+ case BGP_ORIGIN_EGP:
+ aggregate->egp_origin_count++;
+ break;
+ default:
+ /*Do nothing.
+ */
+ break;
+ }
+
+ if (!aggregate->as_set)
+ continue;
+
+ /*
+ * as-set aggregate route generate origin, as path,
+ * and community aggregation.
+ */
+ /* Compute aggregate route's as-path.
+ */
+ bgp_compute_aggregate_aspath_hash(aggregate,
+ pi->attr->aspath);
+
+ /* Compute aggregate route's community.
+ */
+ if (bgp_attr_get_community(pi->attr))
+ bgp_compute_aggregate_community_hash(
+ aggregate,
+ bgp_attr_get_community(pi->attr));
+
+ /* Compute aggregate route's extended community.
+ */
+ if (bgp_attr_get_ecommunity(pi->attr))
+ bgp_compute_aggregate_ecommunity_hash(
+ aggregate,
+ bgp_attr_get_ecommunity(pi->attr));
+
+ /* Compute aggregate route's large community.
+ */
+ if (bgp_attr_get_lcommunity(pi->attr))
+ bgp_compute_aggregate_lcommunity_hash(
+ aggregate,
+ bgp_attr_get_lcommunity(pi->attr));
+ }
+ if (match)
+ bgp_process(bgp, dest, afi, safi);
+ }
+ if (aggregate->as_set) {
+ bgp_compute_aggregate_aspath_val(aggregate);
+ bgp_compute_aggregate_community_val(aggregate);
+ bgp_compute_aggregate_ecommunity_val(aggregate);
+ bgp_compute_aggregate_lcommunity_val(aggregate);
+ }
+
+
+ bgp_dest_unlock_node(top);
+
+
+ if (aggregate->incomplete_origin_count > 0)
+ origin = BGP_ORIGIN_INCOMPLETE;
+ else if (aggregate->egp_origin_count > 0)
+ origin = BGP_ORIGIN_EGP;
+
+ if (aggregate->origin != BGP_ORIGIN_UNSPECIFIED)
+ origin = aggregate->origin;
+
+ if (aggregate->as_set) {
+ if (aggregate->aspath)
+ /* Retrieve aggregate route's as-path.
+ */
+ aspath = aspath_dup(aggregate->aspath);
+
+ if (aggregate->community)
+ /* Retrieve aggregate route's community.
+ */
+ community = community_dup(aggregate->community);
+
+ if (aggregate->ecommunity)
+ /* Retrieve aggregate route's ecommunity.
+ */
+ ecommunity = ecommunity_dup(aggregate->ecommunity);
+
+ if (aggregate->lcommunity)
+ /* Retrieve aggregate route's lcommunity.
+ */
+ lcommunity = lcommunity_dup(aggregate->lcommunity);
+ }
+
+ bgp_aggregate_install(bgp, afi, safi, p, origin, aspath, community,
+ ecommunity, lcommunity, atomic_aggregate,
+ aggregate);
+
+ return true;
+}
+
+void bgp_aggregate_delete(struct bgp *bgp, const struct prefix *p, afi_t afi,
+ safi_t safi, struct bgp_aggregate *aggregate)
+{
+ struct bgp_table *table;
+ struct bgp_dest *top;
+ struct bgp_dest *dest;
+ struct bgp_path_info *pi;
+ unsigned long match;
+
+ table = bgp->rib[afi][safi];
+
+ /* If routes exists below this node, generate aggregate routes. */
+ top = bgp_node_get(table, p);
+ for (dest = bgp_node_get(table, p); dest;
+ dest = bgp_route_next_until(dest, top)) {
+ const struct prefix *dest_p = bgp_dest_get_prefix(dest);
+
+ if (dest_p->prefixlen <= p->prefixlen)
+ continue;
+ match = 0;
+
+ for (pi = bgp_dest_get_bgp_path_info(dest); pi; pi = pi->next) {
+ if (BGP_PATH_HOLDDOWN(pi))
+ continue;
+
+ if (pi->sub_type == BGP_ROUTE_AGGREGATE)
+ continue;
+
+ /*
+ * This route is suppressed: attempt to unsuppress it.
+ *
+ * `aggr_unsuppress_path` will fail if this particular
+ * aggregate route was not the suppressor.
+ */
+ if (pi->extra && pi->extra->aggr_suppressors &&
+ listcount(pi->extra->aggr_suppressors)) {
+ if (aggr_unsuppress_path(aggregate, pi))
+ match++;
+ }
+
+ aggregate->count--;
+
+ if (pi->attr->origin == BGP_ORIGIN_INCOMPLETE)
+ aggregate->incomplete_origin_count--;
+ else if (pi->attr->origin == BGP_ORIGIN_EGP)
+ aggregate->egp_origin_count--;
+
+ if (aggregate->as_set) {
+ /* Remove as-path from aggregate.
+ */
+ bgp_remove_aspath_from_aggregate_hash(
+ aggregate,
+ pi->attr->aspath);
+
+ if (bgp_attr_get_community(pi->attr))
+ /* Remove community from aggregate.
+ */
+ bgp_remove_comm_from_aggregate_hash(
+ aggregate,
+ bgp_attr_get_community(
+ pi->attr));
+
+ if (bgp_attr_get_ecommunity(pi->attr))
+ /* Remove ecommunity from aggregate.
+ */
+ bgp_remove_ecomm_from_aggregate_hash(
+ aggregate,
+ bgp_attr_get_ecommunity(
+ pi->attr));
+
+ if (bgp_attr_get_lcommunity(pi->attr))
+ /* Remove lcommunity from aggregate.
+ */
+ bgp_remove_lcomm_from_aggregate_hash(
+ aggregate,
+ bgp_attr_get_lcommunity(
+ pi->attr));
+ }
+ }
+
+ /* If this node was suppressed, process the change. */
+ if (match)
+ bgp_process(bgp, dest, afi, safi);
+ }
+ if (aggregate->as_set) {
+ aspath_free(aggregate->aspath);
+ aggregate->aspath = NULL;
+ if (aggregate->community)
+ community_free(&aggregate->community);
+ if (aggregate->ecommunity)
+ ecommunity_free(&aggregate->ecommunity);
+ if (aggregate->lcommunity)
+ lcommunity_free(&aggregate->lcommunity);
+ }
+
+ bgp_dest_unlock_node(top);
+}
+
+static void bgp_add_route_to_aggregate(struct bgp *bgp,
+ const struct prefix *aggr_p,
+ struct bgp_path_info *pinew, afi_t afi,
+ safi_t safi,
+ struct bgp_aggregate *aggregate)
+{
+ uint8_t origin;
+ struct aspath *aspath = NULL;
+ uint8_t atomic_aggregate = 0;
+ struct community *community = NULL;
+ struct ecommunity *ecommunity = NULL;
+ struct lcommunity *lcommunity = NULL;
+
+ /* If the bgp instance is being deleted or self peer is deleted
+ * then do not create aggregate route
+ */
+ if (CHECK_FLAG(bgp->flags, BGP_FLAG_DELETE_IN_PROGRESS)
+ || (bgp->peer_self == NULL))
+ return;
+
+ /* ORIGIN attribute: If at least one route among routes that are
+ * aggregated has ORIGIN with the value INCOMPLETE, then the
+ * aggregated route must have the ORIGIN attribute with the value
+ * INCOMPLETE. Otherwise, if at least one route among routes that
+ * are aggregated has ORIGIN with the value EGP, then the aggregated
+ * route must have the origin attribute with the value EGP. In all
+ * other case the value of the ORIGIN attribute of the aggregated
+ * route is INTERNAL.
+ */
+ origin = BGP_ORIGIN_IGP;
+
+ aggregate->count++;
+
+ /*
+ * This must be called before `summary` check to avoid
+ * "suppressing" twice.
+ */
+ if (aggregate->match_med)
+ bgp_aggregate_med_update(aggregate, bgp, aggr_p, afi, safi,
+ pinew);
+
+ if (aggregate->summary_only && AGGREGATE_MED_VALID(aggregate))
+ aggr_suppress_path(aggregate, pinew);
+
+ if (aggregate->suppress_map_name && AGGREGATE_MED_VALID(aggregate)
+ && aggr_suppress_map_test(bgp, aggregate, pinew))
+ aggr_suppress_path(aggregate, pinew);
+
+ switch (pinew->attr->origin) {
+ case BGP_ORIGIN_INCOMPLETE:
+ aggregate->incomplete_origin_count++;
+ break;
+ case BGP_ORIGIN_EGP:
+ aggregate->egp_origin_count++;
+ break;
+ default:
+ /* Do nothing.
+ */
+ break;
+ }
+
+ if (aggregate->incomplete_origin_count > 0)
+ origin = BGP_ORIGIN_INCOMPLETE;
+ else if (aggregate->egp_origin_count > 0)
+ origin = BGP_ORIGIN_EGP;
+
+ if (aggregate->origin != BGP_ORIGIN_UNSPECIFIED)
+ origin = aggregate->origin;
+
+ if (aggregate->as_set) {
+ /* Compute aggregate route's as-path.
+ */
+ bgp_compute_aggregate_aspath(aggregate,
+ pinew->attr->aspath);
+
+ /* Compute aggregate route's community.
+ */
+ if (bgp_attr_get_community(pinew->attr))
+ bgp_compute_aggregate_community(
+ aggregate, bgp_attr_get_community(pinew->attr));
+
+ /* Compute aggregate route's extended community.
+ */
+ if (bgp_attr_get_ecommunity(pinew->attr))
+ bgp_compute_aggregate_ecommunity(
+ aggregate,
+ bgp_attr_get_ecommunity(pinew->attr));
+
+ /* Compute aggregate route's large community.
+ */
+ if (bgp_attr_get_lcommunity(pinew->attr))
+ bgp_compute_aggregate_lcommunity(
+ aggregate,
+ bgp_attr_get_lcommunity(pinew->attr));
+
+ /* Retrieve aggregate route's as-path.
+ */
+ if (aggregate->aspath)
+ aspath = aspath_dup(aggregate->aspath);
+
+ /* Retrieve aggregate route's community.
+ */
+ if (aggregate->community)
+ community = community_dup(aggregate->community);
+
+ /* Retrieve aggregate route's ecommunity.
+ */
+ if (aggregate->ecommunity)
+ ecommunity = ecommunity_dup(aggregate->ecommunity);
+
+ /* Retrieve aggregate route's lcommunity.
+ */
+ if (aggregate->lcommunity)
+ lcommunity = lcommunity_dup(aggregate->lcommunity);
+ }
+
+ bgp_aggregate_install(bgp, afi, safi, aggr_p, origin,
+ aspath, community, ecommunity,
+ lcommunity, atomic_aggregate, aggregate);
+}
+
+static void bgp_remove_route_from_aggregate(struct bgp *bgp, afi_t afi,
+ safi_t safi,
+ struct bgp_path_info *pi,
+ struct bgp_aggregate *aggregate,
+ const struct prefix *aggr_p)
+{
+ uint8_t origin;
+ struct aspath *aspath = NULL;
+ uint8_t atomic_aggregate = 0;
+ struct community *community = NULL;
+ struct ecommunity *ecommunity = NULL;
+ struct lcommunity *lcommunity = NULL;
+ unsigned long match = 0;
+
+ /* If the bgp instance is being deleted or self peer is deleted
+ * then do not create aggregate route
+ */
+ if (CHECK_FLAG(bgp->flags, BGP_FLAG_DELETE_IN_PROGRESS)
+ || (bgp->peer_self == NULL))
+ return;
+
+ if (BGP_PATH_HOLDDOWN(pi))
+ return;
+
+ if (pi->sub_type == BGP_ROUTE_AGGREGATE)
+ return;
+
+ if (aggregate->summary_only && AGGREGATE_MED_VALID(aggregate))
+ if (aggr_unsuppress_path(aggregate, pi))
+ match++;
+
+ if (aggregate->suppress_map_name && AGGREGATE_MED_VALID(aggregate)
+ && aggr_suppress_map_test(bgp, aggregate, pi))
+ if (aggr_unsuppress_path(aggregate, pi))
+ match++;
+
+ /*
+ * This must be called after `summary`, `suppress-map` check to avoid
+ * "unsuppressing" twice.
+ */
+ if (aggregate->match_med)
+ bgp_aggregate_med_update(aggregate, bgp, aggr_p, afi, safi, pi);
+
+ if (aggregate->count > 0)
+ aggregate->count--;
+
+ if (pi->attr->origin == BGP_ORIGIN_INCOMPLETE)
+ aggregate->incomplete_origin_count--;
+ else if (pi->attr->origin == BGP_ORIGIN_EGP)
+ aggregate->egp_origin_count--;
+
+ if (aggregate->as_set) {
+ /* Remove as-path from aggregate.
+ */
+ bgp_remove_aspath_from_aggregate(aggregate,
+ pi->attr->aspath);
+
+ if (bgp_attr_get_community(pi->attr))
+ /* Remove community from aggregate.
+ */
+ bgp_remove_community_from_aggregate(
+ aggregate, bgp_attr_get_community(pi->attr));
+
+ if (bgp_attr_get_ecommunity(pi->attr))
+ /* Remove ecommunity from aggregate.
+ */
+ bgp_remove_ecommunity_from_aggregate(
+ aggregate, bgp_attr_get_ecommunity(pi->attr));
+
+ if (bgp_attr_get_lcommunity(pi->attr))
+ /* Remove lcommunity from aggregate.
+ */
+ bgp_remove_lcommunity_from_aggregate(
+ aggregate, bgp_attr_get_lcommunity(pi->attr));
+ }
+
+ /* If this node was suppressed, process the change. */
+ if (match)
+ bgp_process(bgp, pi->net, afi, safi);
+
+ origin = BGP_ORIGIN_IGP;
+ if (aggregate->incomplete_origin_count > 0)
+ origin = BGP_ORIGIN_INCOMPLETE;
+ else if (aggregate->egp_origin_count > 0)
+ origin = BGP_ORIGIN_EGP;
+
+ if (aggregate->origin != BGP_ORIGIN_UNSPECIFIED)
+ origin = aggregate->origin;
+
+ if (aggregate->as_set) {
+ /* Retrieve aggregate route's as-path.
+ */
+ if (aggregate->aspath)
+ aspath = aspath_dup(aggregate->aspath);
+
+ /* Retrieve aggregate route's community.
+ */
+ if (aggregate->community)
+ community = community_dup(aggregate->community);
+
+ /* Retrieve aggregate route's ecommunity.
+ */
+ if (aggregate->ecommunity)
+ ecommunity = ecommunity_dup(aggregate->ecommunity);
+
+ /* Retrieve aggregate route's lcommunity.
+ */
+ if (aggregate->lcommunity)
+ lcommunity = lcommunity_dup(aggregate->lcommunity);
+ }
+
+ bgp_aggregate_install(bgp, afi, safi, aggr_p, origin,
+ aspath, community, ecommunity,
+ lcommunity, atomic_aggregate, aggregate);
+}
+
+void bgp_aggregate_increment(struct bgp *bgp, const struct prefix *p,
+ struct bgp_path_info *pi, afi_t afi, safi_t safi)
+{
+ struct bgp_dest *child;
+ struct bgp_dest *dest;
+ struct bgp_aggregate *aggregate;
+ struct bgp_table *table;
+
+ table = bgp->aggregate[afi][safi];
+
+ /* No aggregates configured. */
+ if (bgp_table_top_nolock(table) == NULL)
+ return;
+
+ if (p->prefixlen == 0)
+ return;
+
+ if (BGP_PATH_HOLDDOWN(pi))
+ return;
+
+ /* If suppress fib is enabled and route not installed
+ * in FIB, do not update the aggregate route
+ */
+ if (!bgp_check_advertise(bgp, pi->net, safi))
+ return;
+
+ child = bgp_node_get(table, p);
+
+ /* Aggregate address configuration check. */
+ for (dest = child; dest; dest = bgp_dest_parent_nolock(dest)) {
+ const struct prefix *dest_p = bgp_dest_get_prefix(dest);
+
+ aggregate = bgp_dest_get_bgp_aggregate_info(dest);
+ if (aggregate != NULL && dest_p->prefixlen < p->prefixlen) {
+ bgp_add_route_to_aggregate(bgp, dest_p, pi, afi, safi,
+ aggregate);
+ }
+ }
+ bgp_dest_unlock_node(child);
+}
+
+void bgp_aggregate_decrement(struct bgp *bgp, const struct prefix *p,
+ struct bgp_path_info *del, afi_t afi, safi_t safi)
+{
+ struct bgp_dest *child;
+ struct bgp_dest *dest;
+ struct bgp_aggregate *aggregate;
+ struct bgp_table *table;
+
+ table = bgp->aggregate[afi][safi];
+
+ /* No aggregates configured. */
+ if (bgp_table_top_nolock(table) == NULL)
+ return;
+
+ if (p->prefixlen == 0)
+ return;
+
+ child = bgp_node_get(table, p);
+
+ /* Aggregate address configuration check. */
+ for (dest = child; dest; dest = bgp_dest_parent_nolock(dest)) {
+ const struct prefix *dest_p = bgp_dest_get_prefix(dest);
+
+ aggregate = bgp_dest_get_bgp_aggregate_info(dest);
+ if (aggregate != NULL && dest_p->prefixlen < p->prefixlen) {
+ bgp_remove_route_from_aggregate(bgp, afi, safi, del,
+ aggregate, dest_p);
+ }
+ }
+ bgp_dest_unlock_node(child);
+}
+
+/* Aggregate route attribute. */
+#define AGGREGATE_SUMMARY_ONLY 1
+#define AGGREGATE_AS_SET 1
+#define AGGREGATE_AS_UNSET 0
+
+static const char *bgp_origin2str(uint8_t origin)
+{
+ switch (origin) {
+ case BGP_ORIGIN_IGP:
+ return "igp";
+ case BGP_ORIGIN_EGP:
+ return "egp";
+ case BGP_ORIGIN_INCOMPLETE:
+ return "incomplete";
+ }
+ return "n/a";
+}
+
+static const char *bgp_rpki_validation2str(enum rpki_states v_state)
+{
+ switch (v_state) {
+ case RPKI_NOT_BEING_USED:
+ return "not used";
+ case RPKI_VALID:
+ return "valid";
+ case RPKI_NOTFOUND:
+ return "not found";
+ case RPKI_INVALID:
+ return "invalid";
+ }
+
+ assert(!"We should never get here this is a dev escape");
+ return "ERROR";
+}
+
+static int bgp_aggregate_unset(struct vty *vty, const char *prefix_str,
+ afi_t afi, safi_t safi)
+{
+ VTY_DECLVAR_CONTEXT(bgp, bgp);
+ int ret;
+ struct prefix p;
+ struct bgp_dest *dest;
+ struct bgp_aggregate *aggregate;
+
+ /* Convert string to prefix structure. */
+ ret = str2prefix(prefix_str, &p);
+ if (!ret) {
+ vty_out(vty, "Malformed prefix\n");
+ return CMD_WARNING_CONFIG_FAILED;
+ }
+ apply_mask(&p);
+
+ /* Old configuration check. */
+ dest = bgp_node_lookup(bgp->aggregate[afi][safi], &p);
+ if (!dest) {
+ vty_out(vty,
+ "%% There is no aggregate-address configuration.\n");
+ return CMD_WARNING_CONFIG_FAILED;
+ }
+
+ aggregate = bgp_dest_get_bgp_aggregate_info(dest);
+ bgp_aggregate_delete(bgp, &p, afi, safi, aggregate);
+ bgp_aggregate_install(bgp, afi, safi, &p, 0, NULL, NULL,
+ NULL, NULL, 0, aggregate);
+
+ /* Unlock aggregate address configuration. */
+ bgp_dest_set_bgp_aggregate_info(dest, NULL);
+
+ bgp_free_aggregate_info(aggregate);
+ dest = bgp_dest_unlock_node(dest);
+ assert(dest);
+ bgp_dest_unlock_node(dest);
+
+ return CMD_SUCCESS;
+}
+
+static int bgp_aggregate_set(struct vty *vty, const char *prefix_str, afi_t afi,
+ safi_t safi, const char *rmap,
+ uint8_t summary_only, uint8_t as_set,
+ uint8_t origin, bool match_med,
+ const char *suppress_map)
+{
+ VTY_DECLVAR_CONTEXT(bgp, bgp);
+ int ret;
+ struct prefix p;
+ struct bgp_dest *dest;
+ struct bgp_aggregate *aggregate;
+ uint8_t as_set_new = as_set;
+
+ if (suppress_map && summary_only) {
+ vty_out(vty,
+ "'summary-only' and 'suppress-map' can't be used at the same time\n");
+ return CMD_WARNING_CONFIG_FAILED;
+ }
+
+ /* Convert string to prefix structure. */
+ ret = str2prefix(prefix_str, &p);
+ if (!ret) {
+ vty_out(vty, "Malformed prefix\n");
+ return CMD_WARNING_CONFIG_FAILED;
+ }
+ apply_mask(&p);
+
+ if ((afi == AFI_IP && p.prefixlen == IPV4_MAX_BITLEN) ||
+ (afi == AFI_IP6 && p.prefixlen == IPV6_MAX_BITLEN)) {
+ vty_out(vty, "Specified prefix: %s will not result in any useful aggregation, disallowing\n",
+ prefix_str);
+ return CMD_WARNING_CONFIG_FAILED;
+ }
+
+ /* Old configuration check. */
+ dest = bgp_node_get(bgp->aggregate[afi][safi], &p);
+ aggregate = bgp_dest_get_bgp_aggregate_info(dest);
+
+ if (aggregate) {
+ vty_out(vty, "There is already same aggregate network.\n");
+ /* try to remove the old entry */
+ ret = bgp_aggregate_unset(vty, prefix_str, afi, safi);
+ if (ret) {
+ vty_out(vty, "Error deleting aggregate.\n");
+ bgp_dest_unlock_node(dest);
+ return CMD_WARNING_CONFIG_FAILED;
+ }
+ }
+
+ /* Make aggregate address structure. */
+ aggregate = bgp_aggregate_new();
+ aggregate->summary_only = summary_only;
+ aggregate->match_med = match_med;
+
+ /* Network operators MUST NOT locally generate any new
+ * announcements containing AS_SET or AS_CONFED_SET. If they have
+ * announced routes with AS_SET or AS_CONFED_SET in them, then they
+ * SHOULD withdraw those routes and re-announce routes for the
+ * aggregate or component prefixes (i.e., the more-specific routes
+ * subsumed by the previously aggregated route) without AS_SET
+ * or AS_CONFED_SET in the updates.
+ */
+ if (bgp->reject_as_sets) {
+ if (as_set == AGGREGATE_AS_SET) {
+ as_set_new = AGGREGATE_AS_UNSET;
+ zlog_warn(
+ "%s: Ignoring as-set because `bgp reject-as-sets` is enabled.",
+ __func__);
+ vty_out(vty,
+ "Ignoring as-set because `bgp reject-as-sets` is enabled.\n");
+ }
+ }
+
+ aggregate->as_set = as_set_new;
+ aggregate->safi = safi;
+ /* Override ORIGIN attribute if defined.
+ * E.g.: Cisco and Juniper set ORIGIN for aggregated address
+ * to IGP which is not what rfc4271 says.
+ * This enables the same behavior, optionally.
+ */
+ aggregate->origin = origin;
+
+ if (rmap) {
+ XFREE(MTYPE_ROUTE_MAP_NAME, aggregate->rmap.name);
+ route_map_counter_decrement(aggregate->rmap.map);
+ aggregate->rmap.name =
+ XSTRDUP(MTYPE_ROUTE_MAP_NAME, rmap);
+ aggregate->rmap.map = route_map_lookup_by_name(rmap);
+ route_map_counter_increment(aggregate->rmap.map);
+ }
+
+ if (suppress_map) {
+ XFREE(MTYPE_ROUTE_MAP_NAME, aggregate->suppress_map_name);
+ route_map_counter_decrement(aggregate->suppress_map);
+
+ aggregate->suppress_map_name =
+ XSTRDUP(MTYPE_ROUTE_MAP_NAME, suppress_map);
+ aggregate->suppress_map =
+ route_map_lookup_by_name(aggregate->suppress_map_name);
+ route_map_counter_increment(aggregate->suppress_map);
+ }
+
+ bgp_dest_set_bgp_aggregate_info(dest, aggregate);
+
+ /* Aggregate address insert into BGP routing table. */
+ if (!bgp_aggregate_route(bgp, &p, afi, safi, aggregate)) {
+ bgp_aggregate_free(aggregate);
+ bgp_dest_unlock_node(dest);
+ }
+
+ return CMD_SUCCESS;
+}
+
+DEFPY(aggregate_addressv4, aggregate_addressv4_cmd,
+ "[no] aggregate-address <A.B.C.D/M$prefix|A.B.C.D$addr A.B.C.D$mask> [{"
+ "as-set$as_set_s"
+ "|summary-only$summary_only"
+ "|route-map RMAP_NAME$rmap_name"
+ "|origin <egp|igp|incomplete>$origin_s"
+ "|matching-MED-only$match_med"
+ "|suppress-map RMAP_NAME$suppress_map"
+ "}]",
+ NO_STR
+ "Configure BGP aggregate entries\n"
+ "Aggregate prefix\n"
+ "Aggregate address\n"
+ "Aggregate mask\n"
+ "Generate AS set path information\n"
+ "Filter more specific routes from updates\n"
+ "Apply route map to aggregate network\n"
+ "Route map name\n"
+ "BGP origin code\n"
+ "Remote EGP\n"
+ "Local IGP\n"
+ "Unknown heritage\n"
+ "Only aggregate routes with matching MED\n"
+ "Suppress the selected more specific routes\n"
+ "Route map with the route selectors\n")
+{
+ const char *prefix_s = NULL;
+ safi_t safi = bgp_node_safi(vty);
+ uint8_t origin = BGP_ORIGIN_UNSPECIFIED;
+ int as_set = AGGREGATE_AS_UNSET;
+ char prefix_buf[PREFIX2STR_BUFFER];
+
+ if (addr_str) {
+ if (netmask_str2prefix_str(addr_str, mask_str, prefix_buf,
+ sizeof(prefix_buf))
+ == 0) {
+ vty_out(vty, "%% Inconsistent address and mask\n");
+ return CMD_WARNING_CONFIG_FAILED;
+ }
+ prefix_s = prefix_buf;
+ } else
+ prefix_s = prefix_str;
+
+ if (origin_s) {
+ if (strcmp(origin_s, "egp") == 0)
+ origin = BGP_ORIGIN_EGP;
+ else if (strcmp(origin_s, "igp") == 0)
+ origin = BGP_ORIGIN_IGP;
+ else if (strcmp(origin_s, "incomplete") == 0)
+ origin = BGP_ORIGIN_INCOMPLETE;
+ }
+
+ if (as_set_s)
+ as_set = AGGREGATE_AS_SET;
+
+ /* Handle configuration removal, otherwise installation. */
+ if (no)
+ return bgp_aggregate_unset(vty, prefix_s, AFI_IP, safi);
+
+ return bgp_aggregate_set(vty, prefix_s, AFI_IP, safi, rmap_name,
+ summary_only != NULL, as_set, origin,
+ match_med != NULL, suppress_map);
+}
+
+void bgp_free_aggregate_info(struct bgp_aggregate *aggregate)
+{
+ if (aggregate->community)
+ community_free(&aggregate->community);
+
+ hash_clean_and_free(&aggregate->community_hash,
+ bgp_aggr_community_remove);
+
+ if (aggregate->ecommunity)
+ ecommunity_free(&aggregate->ecommunity);
+
+ hash_clean_and_free(&aggregate->ecommunity_hash,
+ bgp_aggr_ecommunity_remove);
+
+ if (aggregate->lcommunity)
+ lcommunity_free(&aggregate->lcommunity);
+
+ hash_clean_and_free(&aggregate->lcommunity_hash,
+ bgp_aggr_lcommunity_remove);
+
+ if (aggregate->aspath)
+ aspath_free(aggregate->aspath);
+
+ hash_clean_and_free(&aggregate->aspath_hash, bgp_aggr_aspath_remove);
+
+ bgp_aggregate_free(aggregate);
+}
+
+DEFPY(aggregate_addressv6, aggregate_addressv6_cmd,
+ "[no] aggregate-address X:X::X:X/M$prefix [{"
+ "as-set$as_set_s"
+ "|summary-only$summary_only"
+ "|route-map RMAP_NAME$rmap_name"
+ "|origin <egp|igp|incomplete>$origin_s"
+ "|matching-MED-only$match_med"
+ "|suppress-map RMAP_NAME$suppress_map"
+ "}]",
+ NO_STR
+ "Configure BGP aggregate entries\n"
+ "Aggregate prefix\n"
+ "Generate AS set path information\n"
+ "Filter more specific routes from updates\n"
+ "Apply route map to aggregate network\n"
+ "Route map name\n"
+ "BGP origin code\n"
+ "Remote EGP\n"
+ "Local IGP\n"
+ "Unknown heritage\n"
+ "Only aggregate routes with matching MED\n"
+ "Suppress the selected more specific routes\n"
+ "Route map with the route selectors\n")
+{
+ uint8_t origin = BGP_ORIGIN_UNSPECIFIED;
+ int as_set = AGGREGATE_AS_UNSET;
+
+ if (origin_s) {
+ if (strcmp(origin_s, "egp") == 0)
+ origin = BGP_ORIGIN_EGP;
+ else if (strcmp(origin_s, "igp") == 0)
+ origin = BGP_ORIGIN_IGP;
+ else if (strcmp(origin_s, "incomplete") == 0)
+ origin = BGP_ORIGIN_INCOMPLETE;
+ }
+
+ if (as_set_s)
+ as_set = AGGREGATE_AS_SET;
+
+ /* Handle configuration removal, otherwise installation. */
+ if (no)
+ return bgp_aggregate_unset(vty, prefix_str, AFI_IP6,
+ SAFI_UNICAST);
+
+ return bgp_aggregate_set(vty, prefix_str, AFI_IP6, SAFI_UNICAST,
+ rmap_name, summary_only != NULL, as_set,
+ origin, match_med != NULL, suppress_map);
+}
+
+/* Redistribute route treatment. */
+void bgp_redistribute_add(struct bgp *bgp, struct prefix *p,
+ const union g_addr *nexthop, ifindex_t ifindex,
+ enum nexthop_types_t nhtype, uint8_t distance,
+ enum blackhole_type bhtype, uint32_t metric,
+ uint8_t type, unsigned short instance,
+ route_tag_t tag)
+{
+ struct bgp_path_info *new;
+ struct bgp_path_info *bpi;
+ struct bgp_path_info rmap_path;
+ struct bgp_dest *bn;
+ struct attr attr;
+ struct attr *new_attr;
+ afi_t afi;
+ route_map_result_t ret;
+ struct bgp_redist *red;
+
+ if (CHECK_FLAG(bgp->flags, BGP_FLAG_DELETE_IN_PROGRESS) ||
+ bgp->peer_self == NULL)
+ return;
+
+ /* Make default attribute. */
+ bgp_attr_default_set(&attr, bgp, BGP_ORIGIN_INCOMPLETE);
+ /*
+ * This must not be NULL to satisfy Coverity SA
+ */
+ assert(attr.aspath);
+
+ if (p->family == AF_INET6)
+ UNSET_FLAG(attr.flag, ATTR_FLAG_BIT(BGP_ATTR_NEXT_HOP));
+
+ switch (nhtype) {
+ case NEXTHOP_TYPE_IFINDEX:
+ switch (p->family) {
+ case AF_INET:
+ attr.nexthop.s_addr = INADDR_ANY;
+ attr.mp_nexthop_len = BGP_ATTR_NHLEN_IPV4;
+ attr.mp_nexthop_global_in.s_addr = INADDR_ANY;
+ break;
+ case AF_INET6:
+ memset(&attr.mp_nexthop_global, 0,
+ sizeof(attr.mp_nexthop_global));
+ attr.mp_nexthop_len = BGP_ATTR_NHLEN_IPV6_GLOBAL;
+ break;
+ }
+ break;
+ case NEXTHOP_TYPE_IPV4:
+ case NEXTHOP_TYPE_IPV4_IFINDEX:
+ attr.nexthop = nexthop->ipv4;
+ attr.mp_nexthop_len = BGP_ATTR_NHLEN_IPV4;
+ attr.mp_nexthop_global_in = nexthop->ipv4;
+ break;
+ case NEXTHOP_TYPE_IPV6:
+ case NEXTHOP_TYPE_IPV6_IFINDEX:
+ attr.mp_nexthop_global = nexthop->ipv6;
+ attr.mp_nexthop_len = BGP_ATTR_NHLEN_IPV6_GLOBAL;
+ break;
+ case NEXTHOP_TYPE_BLACKHOLE:
+ switch (p->family) {
+ case AF_INET:
+ attr.nexthop.s_addr = INADDR_ANY;
+ attr.mp_nexthop_len = BGP_ATTR_NHLEN_IPV4;
+ attr.mp_nexthop_global_in.s_addr = INADDR_ANY;
+ break;
+ case AF_INET6:
+ memset(&attr.mp_nexthop_global, 0,
+ sizeof(attr.mp_nexthop_global));
+ attr.mp_nexthop_len = BGP_ATTR_NHLEN_IPV6_GLOBAL;
+ break;
+ }
+ attr.bh_type = bhtype;
+ break;
+ }
+ attr.nh_type = nhtype;
+ attr.nh_ifindex = ifindex;
+
+ attr.med = metric;
+ attr.distance = distance;
+ attr.flag |= ATTR_FLAG_BIT(BGP_ATTR_MULTI_EXIT_DISC);
+ attr.tag = tag;
+
+ if (metric)
+ bgp_attr_set_aigp_metric(&attr, metric);
+
+ afi = family2afi(p->family);
+
+ red = bgp_redist_lookup(bgp, afi, type, instance);
+ if (red) {
+ struct attr attr_new;
+
+ /* Copy attribute for modification. */
+ attr_new = attr;
+
+ if (red->redist_metric_flag) {
+ attr_new.med = red->redist_metric;
+ bgp_attr_set_aigp_metric(&attr_new, red->redist_metric);
+ }
+
+ /* Apply route-map. */
+ if (red->rmap.name) {
+ memset(&rmap_path, 0, sizeof(rmap_path));
+ rmap_path.peer = bgp->peer_self;
+ rmap_path.attr = &attr_new;
+
+ SET_FLAG(bgp->peer_self->rmap_type,
+ PEER_RMAP_TYPE_REDISTRIBUTE);
+
+ ret = route_map_apply(red->rmap.map, p, &rmap_path);
+
+ bgp->peer_self->rmap_type = 0;
+
+ if (ret == RMAP_DENYMATCH) {
+ /* Free uninterned attribute. */
+ bgp_attr_flush(&attr_new);
+
+ /* Unintern original. */
+ aspath_unintern(&attr.aspath);
+ bgp_redistribute_delete(bgp, p, type, instance);
+ return;
+ }
+ }
+
+ if (bgp_in_graceful_shutdown(bgp))
+ bgp_attr_add_gshut_community(&attr_new);
+
+ bn = bgp_afi_node_get(bgp->rib[afi][SAFI_UNICAST], afi,
+ SAFI_UNICAST, p, NULL);
+
+ new_attr = bgp_attr_intern(&attr_new);
+
+ for (bpi = bgp_dest_get_bgp_path_info(bn); bpi; bpi = bpi->next)
+ if (bpi->peer == bgp->peer_self
+ && bpi->sub_type == BGP_ROUTE_REDISTRIBUTE)
+ break;
+
+ if (bpi) {
+ /* Ensure the (source route) type is updated. */
+ bpi->type = type;
+ if (attrhash_cmp(bpi->attr, new_attr)
+ && !CHECK_FLAG(bpi->flags, BGP_PATH_REMOVED)) {
+ bgp_attr_unintern(&new_attr);
+ aspath_unintern(&attr.aspath);
+ bgp_dest_unlock_node(bn);
+ return;
+ } else {
+ /* The attribute is changed. */
+ bgp_path_info_set_flag(bn, bpi,
+ BGP_PATH_ATTR_CHANGED);
+
+ /* Rewrite BGP route information. */
+ if (CHECK_FLAG(bpi->flags, BGP_PATH_REMOVED))
+ bgp_path_info_restore(bn, bpi);
+ else
+ bgp_aggregate_decrement(
+ bgp, p, bpi, afi, SAFI_UNICAST);
+ bgp_attr_unintern(&bpi->attr);
+ bpi->attr = new_attr;
+ bpi->uptime = monotime(NULL);
+
+ /* Process change. */
+ bgp_aggregate_increment(bgp, p, bpi, afi,
+ SAFI_UNICAST);
+ bgp_process(bgp, bn, afi, SAFI_UNICAST);
+ bgp_dest_unlock_node(bn);
+ aspath_unintern(&attr.aspath);
+
+ if ((bgp->inst_type == BGP_INSTANCE_TYPE_VRF)
+ || (bgp->inst_type
+ == BGP_INSTANCE_TYPE_DEFAULT)) {
+
+ vpn_leak_from_vrf_update(
+ bgp_get_default(), bgp, bpi);
+ }
+ return;
+ }
+ }
+
+ new = info_make(type, BGP_ROUTE_REDISTRIBUTE, instance,
+ bgp->peer_self, new_attr, bn);
+ SET_FLAG(new->flags, BGP_PATH_VALID);
+
+ bgp_aggregate_increment(bgp, p, new, afi, SAFI_UNICAST);
+ bgp_path_info_add(bn, new);
+ bgp_dest_unlock_node(bn);
+ SET_FLAG(bn->flags, BGP_NODE_FIB_INSTALLED);
+ bgp_process(bgp, bn, afi, SAFI_UNICAST);
+
+ if ((bgp->inst_type == BGP_INSTANCE_TYPE_VRF)
+ || (bgp->inst_type == BGP_INSTANCE_TYPE_DEFAULT)) {
+
+ vpn_leak_from_vrf_update(bgp_get_default(), bgp, new);
+ }
+ }
+
+ /* Unintern original. */
+ aspath_unintern(&attr.aspath);
+}
+
+void bgp_redistribute_delete(struct bgp *bgp, struct prefix *p, uint8_t type,
+ unsigned short instance)
+{
+ afi_t afi;
+ struct bgp_dest *dest;
+ struct bgp_path_info *pi;
+ struct bgp_redist *red;
+
+ afi = family2afi(p->family);
+
+ red = bgp_redist_lookup(bgp, afi, type, instance);
+ if (red) {
+ dest = bgp_afi_node_get(bgp->rib[afi][SAFI_UNICAST], afi,
+ SAFI_UNICAST, p, NULL);
+
+ for (pi = bgp_dest_get_bgp_path_info(dest); pi; pi = pi->next)
+ if (pi->peer == bgp->peer_self && pi->type == type)
+ break;
+
+ if (pi) {
+ if ((bgp->inst_type == BGP_INSTANCE_TYPE_VRF)
+ || (bgp->inst_type == BGP_INSTANCE_TYPE_DEFAULT)) {
+
+ vpn_leak_from_vrf_withdraw(bgp_get_default(),
+ bgp, pi);
+ }
+ bgp_aggregate_decrement(bgp, p, pi, afi, SAFI_UNICAST);
+ bgp_path_info_delete(dest, pi);
+ bgp_process(bgp, dest, afi, SAFI_UNICAST);
+ }
+ bgp_dest_unlock_node(dest);
+ }
+}
+
+/* Withdraw specified route type's route. */
+void bgp_redistribute_withdraw(struct bgp *bgp, afi_t afi, int type,
+ unsigned short instance)
+{
+ struct bgp_dest *dest;
+ struct bgp_path_info *pi;
+ struct bgp_table *table;
+
+ table = bgp->rib[afi][SAFI_UNICAST];
+
+ for (dest = bgp_table_top(table); dest; dest = bgp_route_next(dest)) {
+ for (pi = bgp_dest_get_bgp_path_info(dest); pi; pi = pi->next)
+ if (pi->peer == bgp->peer_self && pi->type == type
+ && pi->instance == instance)
+ break;
+
+ if (pi) {
+ if ((bgp->inst_type == BGP_INSTANCE_TYPE_VRF)
+ || (bgp->inst_type == BGP_INSTANCE_TYPE_DEFAULT)) {
+
+ vpn_leak_from_vrf_withdraw(bgp_get_default(),
+ bgp, pi);
+ }
+ bgp_aggregate_decrement(bgp, bgp_dest_get_prefix(dest),
+ pi, afi, SAFI_UNICAST);
+ bgp_path_info_delete(dest, pi);
+ if (!CHECK_FLAG(bgp->flags,
+ BGP_FLAG_DELETE_IN_PROGRESS))
+ bgp_process(bgp, dest, afi, SAFI_UNICAST);
+ else {
+ dest = bgp_path_info_reap(dest, pi);
+ assert(dest);
+ }
+ }
+ }
+}
+
+/* Static function to display route. */
+static void route_vty_out_route(struct bgp_dest *dest, const struct prefix *p,
+ struct vty *vty, json_object *json, bool wide)
+{
+ int len = 0;
+ char buf[INET6_ADDRSTRLEN];
+
+ if (p->family == AF_INET) {
+ if (!json) {
+ len = vty_out(vty, "%pFX", p);
+ } else {
+ json_object_string_add(json, "prefix",
+ inet_ntop(p->family,
+ &p->u.prefix, buf,
+ sizeof(buf)));
+ json_object_int_add(json, "prefixLen", p->prefixlen);
+ json_object_string_addf(json, "network", "%pFX", p);
+ json_object_int_add(json, "version", dest->version);
+ }
+ } else if (p->family == AF_ETHERNET) {
+ len = vty_out(vty, "%pFX", p);
+ } else if (p->family == AF_EVPN) {
+ if (!json)
+ len = vty_out(vty, "%pFX", (struct prefix_evpn *)p);
+ else
+ bgp_evpn_route2json((struct prefix_evpn *)p, json);
+ } else if (p->family == AF_FLOWSPEC) {
+ route_vty_out_flowspec(vty, p, NULL,
+ json ?
+ NLRI_STRING_FORMAT_JSON_SIMPLE :
+ NLRI_STRING_FORMAT_MIN, json);
+ } else {
+ if (!json)
+ len = vty_out(vty, "%pFX", p);
+ else {
+ json_object_string_add(json, "prefix",
+ inet_ntop(p->family,
+ &p->u.prefix, buf,
+ sizeof(buf)));
+ json_object_int_add(json, "prefixLen", p->prefixlen);
+ json_object_string_addf(json, "network", "%pFX", p);
+ json_object_int_add(json, "version", dest->version);
+ }
+ }
+
+ if (!json) {
+ len = wide ? (45 - len) : (17 - len);
+ if (len < 1)
+ vty_out(vty, "\n%*s", 20, " ");
+ else
+ vty_out(vty, "%*s", len, " ");
+ }
+}
+
+enum bgp_display_type {
+ normal_list,
+};
+
+const char *bgp_path_selection_reason2str(enum bgp_path_selection_reason reason)
+{
+ switch (reason) {
+ case bgp_path_selection_none:
+ return "Nothing to Select";
+ case bgp_path_selection_first:
+ return "First path received";
+ case bgp_path_selection_evpn_sticky_mac:
+ return "EVPN Sticky Mac";
+ case bgp_path_selection_evpn_seq:
+ return "EVPN sequence number";
+ case bgp_path_selection_evpn_lower_ip:
+ return "EVPN lower IP";
+ case bgp_path_selection_evpn_local_path:
+ return "EVPN local ES path";
+ case bgp_path_selection_evpn_non_proxy:
+ return "EVPN non proxy";
+ case bgp_path_selection_weight:
+ return "Weight";
+ case bgp_path_selection_local_pref:
+ return "Local Pref";
+ case bgp_path_selection_accept_own:
+ return "Accept Own";
+ case bgp_path_selection_local_route:
+ return "Local Route";
+ case bgp_path_selection_aigp:
+ return "AIGP";
+ case bgp_path_selection_confed_as_path:
+ return "Confederation based AS Path";
+ case bgp_path_selection_as_path:
+ return "AS Path";
+ case bgp_path_selection_origin:
+ return "Origin";
+ case bgp_path_selection_med:
+ return "MED";
+ case bgp_path_selection_peer:
+ return "Peer Type";
+ case bgp_path_selection_confed:
+ return "Confed Peer Type";
+ case bgp_path_selection_igp_metric:
+ return "IGP Metric";
+ case bgp_path_selection_older:
+ return "Older Path";
+ case bgp_path_selection_router_id:
+ return "Router ID";
+ case bgp_path_selection_cluster_length:
+ return "Cluster length";
+ case bgp_path_selection_stale:
+ return "Path Staleness";
+ case bgp_path_selection_local_configured:
+ return "Locally configured route";
+ case bgp_path_selection_neighbor_ip:
+ return "Neighbor IP";
+ case bgp_path_selection_default:
+ return "Nothing left to compare";
+ }
+ return "Invalid (internal error)";
+}
+
+/* Print the short form route status for a bgp_path_info */
+static void route_vty_short_status_out(struct vty *vty,
+ struct bgp_path_info *path,
+ const struct prefix *p,
+ json_object *json_path)
+{
+ enum rpki_states rpki_state = RPKI_NOT_BEING_USED;
+
+ if (json_path) {
+
+ /* Route status display. */
+ if (CHECK_FLAG(path->flags, BGP_PATH_REMOVED))
+ json_object_boolean_true_add(json_path, "removed");
+
+ if (CHECK_FLAG(path->flags, BGP_PATH_STALE))
+ json_object_boolean_true_add(json_path, "stale");
+
+ if (path->extra && bgp_path_suppressed(path))
+ json_object_boolean_true_add(json_path, "suppressed");
+
+ if (CHECK_FLAG(path->flags, BGP_PATH_VALID)
+ && !CHECK_FLAG(path->flags, BGP_PATH_HISTORY))
+ json_object_boolean_true_add(json_path, "valid");
+
+ /* Selected */
+ if (CHECK_FLAG(path->flags, BGP_PATH_HISTORY))
+ json_object_boolean_true_add(json_path, "history");
+
+ if (CHECK_FLAG(path->flags, BGP_PATH_DAMPED))
+ json_object_boolean_true_add(json_path, "damped");
+
+ if (CHECK_FLAG(path->flags, BGP_PATH_SELECTED)) {
+ json_object_boolean_true_add(json_path, "bestpath");
+ json_object_string_add(json_path, "selectionReason",
+ bgp_path_selection_reason2str(
+ path->net->reason));
+ }
+
+ if (CHECK_FLAG(path->flags, BGP_PATH_MULTIPATH))
+ json_object_boolean_true_add(json_path, "multipath");
+
+ /* Internal route. */
+ if ((path->peer->as)
+ && (path->peer->as == path->peer->local_as))
+ json_object_string_add(json_path, "pathFrom",
+ "internal");
+ else
+ json_object_string_add(json_path, "pathFrom",
+ "external");
+
+ return;
+ }
+
+ /* RPKI validation state */
+ rpki_state =
+ hook_call(bgp_rpki_prefix_status, path->peer, path->attr, p);
+
+ if (rpki_state == RPKI_VALID)
+ vty_out(vty, "V");
+ else if (rpki_state == RPKI_INVALID)
+ vty_out(vty, "I");
+ else if (rpki_state == RPKI_NOTFOUND)
+ vty_out(vty, "N");
+ else
+ vty_out(vty, " ");
+
+ /* Route status display. */
+ if (CHECK_FLAG(path->flags, BGP_PATH_REMOVED))
+ vty_out(vty, "R");
+ else if (CHECK_FLAG(path->flags, BGP_PATH_STALE))
+ vty_out(vty, "S");
+ else if (bgp_path_suppressed(path))
+ vty_out(vty, "s");
+ else if (CHECK_FLAG(path->flags, BGP_PATH_VALID)
+ && !CHECK_FLAG(path->flags, BGP_PATH_HISTORY))
+ vty_out(vty, "*");
+ else
+ vty_out(vty, " ");
+
+ /* Selected */
+ if (CHECK_FLAG(path->flags, BGP_PATH_HISTORY))
+ vty_out(vty, "h");
+ else if (CHECK_FLAG(path->flags, BGP_PATH_DAMPED))
+ vty_out(vty, "d");
+ else if (CHECK_FLAG(path->flags, BGP_PATH_SELECTED))
+ vty_out(vty, ">");
+ else if (CHECK_FLAG(path->flags, BGP_PATH_MULTIPATH))
+ vty_out(vty, "=");
+ else
+ vty_out(vty, " ");
+
+ /* Internal route. */
+ if (path->peer && (path->peer->as)
+ && (path->peer->as == path->peer->local_as))
+ vty_out(vty, "i");
+ else
+ vty_out(vty, " ");
+}
+
+static char *bgp_nexthop_hostname(struct peer *peer,
+ struct bgp_nexthop_cache *bnc)
+{
+ if (peer->hostname
+ && CHECK_FLAG(peer->bgp->flags, BGP_FLAG_SHOW_NEXTHOP_HOSTNAME))
+ return peer->hostname;
+ return NULL;
+}
+
+/* called from terminal list command */
+void route_vty_out(struct vty *vty, const struct prefix *p,
+ struct bgp_path_info *path, int display, safi_t safi,
+ json_object *json_paths, bool wide)
+{
+ int len;
+ struct attr *attr = path->attr;
+ json_object *json_path = NULL;
+ json_object *json_nexthops = NULL;
+ json_object *json_nexthop_global = NULL;
+ json_object *json_nexthop_ll = NULL;
+ json_object *json_ext_community = NULL;
+ char vrf_id_str[VRF_NAMSIZ] = {0};
+ bool nexthop_self =
+ CHECK_FLAG(path->flags, BGP_PATH_ANNC_NH_SELF) ? true : false;
+ bool nexthop_othervrf = false;
+ vrf_id_t nexthop_vrfid = VRF_DEFAULT;
+ const char *nexthop_vrfname = VRF_DEFAULT_NAME;
+ char *nexthop_hostname =
+ bgp_nexthop_hostname(path->peer, path->nexthop);
+ char esi_buf[ESI_STR_LEN];
+
+ if (json_paths)
+ json_path = json_object_new_object();
+
+ /* short status lead text */
+ route_vty_short_status_out(vty, path, p, json_path);
+
+ if (!json_paths) {
+ /* print prefix and mask */
+ if (!display)
+ route_vty_out_route(path->net, p, vty, json_path, wide);
+ else
+ vty_out(vty, "%*s", (wide ? 45 : 17), " ");
+ } else {
+ route_vty_out_route(path->net, p, vty, json_path, wide);
+ }
+
+ /*
+ * If vrf id of nexthop is different from that of prefix,
+ * set up printable string to append
+ */
+ if (path->extra && path->extra->vrfleak &&
+ path->extra->vrfleak->bgp_orig) {
+ const char *self = "";
+
+ if (nexthop_self)
+ self = "<";
+
+ nexthop_othervrf = true;
+ nexthop_vrfid = path->extra->vrfleak->bgp_orig->vrf_id;
+
+ if (path->extra->vrfleak->bgp_orig->vrf_id == VRF_UNKNOWN)
+ snprintf(vrf_id_str, sizeof(vrf_id_str),
+ "@%s%s", VRFID_NONE_STR, self);
+ else
+ snprintf(vrf_id_str, sizeof(vrf_id_str), "@%u%s",
+ path->extra->vrfleak->bgp_orig->vrf_id, self);
+
+ if (path->extra->vrfleak->bgp_orig->inst_type !=
+ BGP_INSTANCE_TYPE_DEFAULT)
+
+ nexthop_vrfname = path->extra->vrfleak->bgp_orig->name;
+ } else {
+ const char *self = "";
+
+ if (nexthop_self)
+ self = "<";
+
+ snprintf(vrf_id_str, sizeof(vrf_id_str), "%s", self);
+ }
+
+ /*
+ * For ENCAP and EVPN routes, nexthop address family is not
+ * neccessarily the same as the prefix address family.
+ * Both SAFI_MPLS_VPN and SAFI_ENCAP use the MP nexthop field
+ * EVPN routes are also exchanged with a MP nexthop. Currently,
+ * this
+ * is only IPv4, the value will be present in either
+ * attr->nexthop or
+ * attr->mp_nexthop_global_in
+ */
+ if ((safi == SAFI_ENCAP) || (safi == SAFI_MPLS_VPN)) {
+ char nexthop[128];
+ int af = NEXTHOP_FAMILY(attr->mp_nexthop_len);
+
+ switch (af) {
+ case AF_INET:
+ snprintfrr(nexthop, sizeof(nexthop), "%pI4",
+ &attr->mp_nexthop_global_in);
+ break;
+ case AF_INET6:
+ snprintfrr(nexthop, sizeof(nexthop), "%pI6",
+ &attr->mp_nexthop_global);
+ break;
+ default:
+ snprintf(nexthop, sizeof(nexthop), "?");
+ break;
+ }
+
+ if (json_paths) {
+ json_nexthop_global = json_object_new_object();
+
+ json_object_string_add(json_nexthop_global, "ip",
+ nexthop);
+
+ if (path->peer->hostname)
+ json_object_string_add(json_nexthop_global,
+ "hostname",
+ path->peer->hostname);
+
+ json_object_string_add(json_nexthop_global, "afi",
+ (af == AF_INET) ? "ipv4"
+ : "ipv6");
+ json_object_boolean_true_add(json_nexthop_global,
+ "used");
+ } else {
+ if (nexthop_hostname)
+ len = vty_out(vty, "%s(%s)%s", nexthop,
+ nexthop_hostname, vrf_id_str);
+ else
+ len = vty_out(vty, "%s%s", nexthop, vrf_id_str);
+
+ len = wide ? (41 - len) : (16 - len);
+ if (len < 1)
+ vty_out(vty, "\n%*s", 36, " ");
+ else
+ vty_out(vty, "%*s", len, " ");
+ }
+ } else if (safi == SAFI_EVPN) {
+ if (json_paths) {
+ json_nexthop_global = json_object_new_object();
+
+ json_object_string_addf(json_nexthop_global, "ip",
+ "%pI4",
+ &attr->mp_nexthop_global_in);
+
+ if (path->peer->hostname)
+ json_object_string_add(json_nexthop_global,
+ "hostname",
+ path->peer->hostname);
+
+ json_object_string_add(json_nexthop_global, "afi",
+ "ipv4");
+ json_object_boolean_true_add(json_nexthop_global,
+ "used");
+ } else {
+ if (nexthop_hostname)
+ len = vty_out(vty, "%pI4(%s)%s",
+ &attr->mp_nexthop_global_in,
+ nexthop_hostname, vrf_id_str);
+ else
+ len = vty_out(vty, "%pI4%s",
+ &attr->mp_nexthop_global_in,
+ vrf_id_str);
+
+ len = wide ? (41 - len) : (16 - len);
+ if (len < 1)
+ vty_out(vty, "\n%*s", 36, " ");
+ else
+ vty_out(vty, "%*s", len, " ");
+ }
+ } else if (safi == SAFI_FLOWSPEC) {
+ if (attr->nexthop.s_addr != INADDR_ANY) {
+ if (json_paths) {
+ json_nexthop_global = json_object_new_object();
+
+ json_object_string_add(json_nexthop_global,
+ "afi", "ipv4");
+ json_object_string_addf(json_nexthop_global,
+ "ip", "%pI4",
+ &attr->nexthop);
+
+ if (path->peer->hostname)
+ json_object_string_add(
+ json_nexthop_global, "hostname",
+ path->peer->hostname);
+
+ json_object_boolean_true_add(
+ json_nexthop_global,
+ "used");
+ } else {
+ if (nexthop_hostname)
+ len = vty_out(vty, "%pI4(%s)%s",
+ &attr->nexthop,
+ nexthop_hostname,
+ vrf_id_str);
+ else
+ len = vty_out(vty, "%pI4%s",
+ &attr->nexthop,
+ vrf_id_str);
+
+ len = wide ? (41 - len) : (16 - len);
+ if (len < 1)
+ vty_out(vty, "\n%*s", 36, " ");
+ else
+ vty_out(vty, "%*s", len, " ");
+ }
+ }
+ } else if (p->family == AF_INET && !BGP_ATTR_MP_NEXTHOP_LEN_IP6(attr)) {
+ if (json_paths) {
+ json_nexthop_global = json_object_new_object();
+
+ json_object_string_addf(json_nexthop_global, "ip",
+ "%pI4", &attr->nexthop);
+
+ if (path->peer->hostname)
+ json_object_string_add(json_nexthop_global,
+ "hostname",
+ path->peer->hostname);
+
+ json_object_string_add(json_nexthop_global, "afi",
+ "ipv4");
+ json_object_boolean_true_add(json_nexthop_global,
+ "used");
+ } else {
+ if (nexthop_hostname)
+ len = vty_out(vty, "%pI4(%s)%s", &attr->nexthop,
+ nexthop_hostname, vrf_id_str);
+ else
+ len = vty_out(vty, "%pI4%s", &attr->nexthop,
+ vrf_id_str);
+
+ len = wide ? (41 - len) : (16 - len);
+ if (len < 1)
+ vty_out(vty, "\n%*s", 36, " ");
+ else
+ vty_out(vty, "%*s", len, " ");
+ }
+ }
+
+ /* IPv6 Next Hop */
+ else if (p->family == AF_INET6 || BGP_ATTR_MP_NEXTHOP_LEN_IP6(attr)) {
+ if (json_paths) {
+ json_nexthop_global = json_object_new_object();
+ json_object_string_addf(json_nexthop_global, "ip",
+ "%pI6",
+ &attr->mp_nexthop_global);
+
+ if (path->peer->hostname)
+ json_object_string_add(json_nexthop_global,
+ "hostname",
+ path->peer->hostname);
+
+ json_object_string_add(json_nexthop_global, "afi",
+ "ipv6");
+ json_object_string_add(json_nexthop_global, "scope",
+ "global");
+
+ /* We display both LL & GL if both have been
+ * received */
+ if ((attr->mp_nexthop_len
+ == BGP_ATTR_NHLEN_IPV6_GLOBAL_AND_LL)
+ || (path->peer->conf_if)) {
+ json_nexthop_ll = json_object_new_object();
+ json_object_string_addf(
+ json_nexthop_ll, "ip", "%pI6",
+ &attr->mp_nexthop_local);
+
+ if (path->peer->hostname)
+ json_object_string_add(
+ json_nexthop_ll, "hostname",
+ path->peer->hostname);
+
+ json_object_string_add(json_nexthop_ll, "afi",
+ "ipv6");
+ json_object_string_add(json_nexthop_ll, "scope",
+ "link-local");
+
+ if ((IPV6_ADDR_CMP(&attr->mp_nexthop_global,
+ &attr->mp_nexthop_local)
+ != 0)
+ && !attr->mp_nexthop_prefer_global)
+ json_object_boolean_true_add(
+ json_nexthop_ll, "used");
+ else
+ json_object_boolean_true_add(
+ json_nexthop_global, "used");
+ } else
+ json_object_boolean_true_add(
+ json_nexthop_global, "used");
+ } else {
+ /* Display LL if LL/Global both in table unless
+ * prefer-global is set */
+ if (((attr->mp_nexthop_len
+ == BGP_ATTR_NHLEN_IPV6_GLOBAL_AND_LL)
+ && !attr->mp_nexthop_prefer_global)
+ || (path->peer->conf_if)) {
+ if (path->peer->conf_if) {
+ len = vty_out(vty, "%s",
+ path->peer->conf_if);
+ /* len of IPv6 addr + max len of def
+ * ifname */
+ len = wide ? (41 - len) : (16 - len);
+
+ if (len < 1)
+ vty_out(vty, "\n%*s", 36, " ");
+ else
+ vty_out(vty, "%*s", len, " ");
+ } else {
+ if (nexthop_hostname)
+ len = vty_out(
+ vty, "%pI6(%s)%s",
+ &attr->mp_nexthop_local,
+ nexthop_hostname,
+ vrf_id_str);
+ else
+ len = vty_out(
+ vty, "%pI6%s",
+ &attr->mp_nexthop_local,
+ vrf_id_str);
+
+ len = wide ? (41 - len) : (16 - len);
+
+ if (len < 1)
+ vty_out(vty, "\n%*s", 36, " ");
+ else
+ vty_out(vty, "%*s", len, " ");
+ }
+ } else {
+ if (nexthop_hostname)
+ len = vty_out(vty, "%pI6(%s)%s",
+ &attr->mp_nexthop_global,
+ nexthop_hostname,
+ vrf_id_str);
+ else
+ len = vty_out(vty, "%pI6%s",
+ &attr->mp_nexthop_global,
+ vrf_id_str);
+
+ len = wide ? (41 - len) : (16 - len);
+
+ if (len < 1)
+ vty_out(vty, "\n%*s", 36, " ");
+ else
+ vty_out(vty, "%*s", len, " ");
+ }
+ }
+ }
+
+ /* MED/Metric */
+ if (attr->flag & ATTR_FLAG_BIT(BGP_ATTR_MULTI_EXIT_DISC))
+ if (json_paths)
+ json_object_int_add(json_path, "metric", attr->med);
+ else if (wide)
+ vty_out(vty, "%7u", attr->med);
+ else
+ vty_out(vty, "%10u", attr->med);
+ else if (!json_paths) {
+ if (wide)
+ vty_out(vty, "%*s", 7, " ");
+ else
+ vty_out(vty, "%*s", 10, " ");
+ }
+
+ /* Local Pref */
+ if (attr->flag & ATTR_FLAG_BIT(BGP_ATTR_LOCAL_PREF))
+ if (json_paths)
+ json_object_int_add(json_path, "locPrf",
+ attr->local_pref);
+ else
+ vty_out(vty, "%7u", attr->local_pref);
+ else if (!json_paths)
+ vty_out(vty, " ");
+
+ if (json_paths)
+ json_object_int_add(json_path, "weight", attr->weight);
+ else
+ vty_out(vty, "%7u ", attr->weight);
+
+ if (json_paths)
+ json_object_string_addf(json_path, "peerId", "%pSU",
+ &path->peer->connection->su);
+
+ /* Print aspath */
+ if (attr->aspath) {
+ if (json_paths)
+ json_object_string_add(json_path, "path",
+ attr->aspath->str);
+ else
+ aspath_print_vty(vty, attr->aspath);
+ }
+
+ /* Print origin */
+ if (json_paths)
+ json_object_string_add(json_path, "origin",
+ bgp_origin_long_str[attr->origin]);
+ else
+ vty_out(vty, "%s", bgp_origin_str[attr->origin]);
+
+ if (json_paths) {
+ if (bgp_evpn_is_esi_valid(&attr->esi)) {
+ json_object_string_add(json_path, "esi",
+ esi_to_str(&attr->esi,
+ esi_buf, sizeof(esi_buf)));
+ }
+ if (safi == SAFI_EVPN &&
+ attr->flag & ATTR_FLAG_BIT(BGP_ATTR_EXT_COMMUNITIES)) {
+ json_ext_community = json_object_new_object();
+ json_object_string_add(
+ json_ext_community, "string",
+ bgp_attr_get_ecommunity(attr)->str);
+ json_object_object_add(json_path,
+ "extendedCommunity",
+ json_ext_community);
+ }
+
+ if (nexthop_self)
+ json_object_boolean_true_add(json_path,
+ "announceNexthopSelf");
+ if (nexthop_othervrf) {
+ json_object_string_add(json_path, "nhVrfName",
+ nexthop_vrfname);
+
+ json_object_int_add(json_path, "nhVrfId",
+ ((nexthop_vrfid == VRF_UNKNOWN)
+ ? -1
+ : (int)nexthop_vrfid));
+ }
+ }
+
+ if (json_paths) {
+ if (json_nexthop_global || json_nexthop_ll) {
+ json_nexthops = json_object_new_array();
+
+ if (json_nexthop_global)
+ json_object_array_add(json_nexthops,
+ json_nexthop_global);
+
+ if (json_nexthop_ll)
+ json_object_array_add(json_nexthops,
+ json_nexthop_ll);
+
+ json_object_object_add(json_path, "nexthops",
+ json_nexthops);
+ }
+
+ json_object_array_add(json_paths, json_path);
+ } else {
+ vty_out(vty, "\n");
+
+ if (safi == SAFI_EVPN) {
+ if (bgp_evpn_is_esi_valid(&attr->esi)) {
+ /* XXX - add these params to the json out */
+ vty_out(vty, "%*s", 20, " ");
+ vty_out(vty, "ESI:%s",
+ esi_to_str(&attr->esi, esi_buf,
+ sizeof(esi_buf)));
+
+ vty_out(vty, "\n");
+ }
+ if (attr->flag &
+ ATTR_FLAG_BIT(BGP_ATTR_EXT_COMMUNITIES)) {
+ vty_out(vty, "%*s", 20, " ");
+ vty_out(vty, "%s\n",
+ bgp_attr_get_ecommunity(attr)->str);
+ }
+ }
+
+#ifdef ENABLE_BGP_VNC
+ /* prints an additional line, indented, with VNC info, if
+ * present */
+ if ((safi == SAFI_MPLS_VPN) || (safi == SAFI_ENCAP))
+ rfapi_vty_out_vncinfo(vty, p, path, safi);
+#endif
+ }
+}
+
+/* called from terminal list command */
+void route_vty_out_tmp(struct vty *vty, struct bgp_dest *dest,
+ const struct prefix *p, struct attr *attr, safi_t safi,
+ bool use_json, json_object *json_ar, bool wide)
+{
+ json_object *json_status = NULL;
+ json_object *json_net = NULL;
+ int len;
+ char buff[BUFSIZ];
+
+ /* Route status display. */
+ if (use_json) {
+ json_status = json_object_new_object();
+ json_net = json_object_new_object();
+ } else {
+ vty_out(vty, " *");
+ vty_out(vty, ">");
+ vty_out(vty, " ");
+ }
+
+ /* print prefix and mask */
+ if (use_json) {
+ if (safi == SAFI_EVPN)
+ bgp_evpn_route2json((struct prefix_evpn *)p, json_net);
+ else if (p->family == AF_INET || p->family == AF_INET6) {
+ json_object_string_add(
+ json_net, "addrPrefix",
+ inet_ntop(p->family, &p->u.prefix, buff,
+ BUFSIZ));
+ json_object_int_add(json_net, "prefixLen",
+ p->prefixlen);
+ json_object_string_addf(json_net, "network", "%pFX", p);
+ }
+ } else
+ route_vty_out_route(dest, p, vty, NULL, wide);
+
+ /* Print attribute */
+ if (attr) {
+ if (use_json) {
+ if (p->family == AF_INET &&
+ (safi == SAFI_MPLS_VPN || safi == SAFI_ENCAP ||
+ !BGP_ATTR_MP_NEXTHOP_LEN_IP6(attr))) {
+ if (safi == SAFI_MPLS_VPN || safi == SAFI_ENCAP)
+ json_object_string_addf(
+ json_net, "nextHop", "%pI4",
+ &attr->mp_nexthop_global_in);
+ else
+ json_object_string_addf(
+ json_net, "nextHop", "%pI4",
+ &attr->nexthop);
+ } else if (p->family == AF_INET6 ||
+ BGP_ATTR_MP_NEXTHOP_LEN_IP6(attr)) {
+ json_object_string_addf(
+ json_net, "nextHopGlobal", "%pI6",
+ &attr->mp_nexthop_global);
+ } else if (p->family == AF_EVPN &&
+ !BGP_ATTR_NEXTHOP_AFI_IP6(attr)) {
+ json_object_string_addf(
+ json_net, "nextHop", "%pI4",
+ &attr->mp_nexthop_global_in);
+ }
+
+ if (attr->flag
+ & ATTR_FLAG_BIT(BGP_ATTR_MULTI_EXIT_DISC))
+ json_object_int_add(json_net, "metric",
+ attr->med);
+
+ if (attr->flag & ATTR_FLAG_BIT(BGP_ATTR_LOCAL_PREF))
+ json_object_int_add(json_net, "locPrf",
+ attr->local_pref);
+
+ json_object_int_add(json_net, "weight", attr->weight);
+
+ /* Print aspath */
+ if (attr->aspath)
+ json_object_string_add(json_net, "path",
+ attr->aspath->str);
+
+ /* Print origin */
+#if CONFDATE > 20231208
+CPP_NOTICE("Drop `bgpOriginCodes` from JSON outputs")
+#endif
+ json_object_string_add(json_net, "bgpOriginCode",
+ bgp_origin_str[attr->origin]);
+ json_object_string_add(
+ json_net, "origin",
+ bgp_origin_long_str[attr->origin]);
+ } else {
+ if (p->family == AF_INET &&
+ (safi == SAFI_MPLS_VPN || safi == SAFI_ENCAP ||
+ safi == SAFI_EVPN ||
+ !BGP_ATTR_MP_NEXTHOP_LEN_IP6(attr))) {
+ if (safi == SAFI_MPLS_VPN || safi == SAFI_ENCAP
+ || safi == SAFI_EVPN)
+ vty_out(vty, "%-16pI4",
+ &attr->mp_nexthop_global_in);
+ else if (wide)
+ vty_out(vty, "%-41pI4", &attr->nexthop);
+ else
+ vty_out(vty, "%-16pI4", &attr->nexthop);
+ } else if (p->family == AF_INET6 ||
+ BGP_ATTR_MP_NEXTHOP_LEN_IP6(attr)) {
+ len = vty_out(vty, "%pI6",
+ &attr->mp_nexthop_global);
+ len = wide ? (41 - len) : (16 - len);
+ if (len < 1)
+ vty_out(vty, "\n%*s", 36, " ");
+ else
+ vty_out(vty, "%*s", len, " ");
+ }
+ if (attr->flag
+ & ATTR_FLAG_BIT(BGP_ATTR_MULTI_EXIT_DISC))
+ if (wide)
+ vty_out(vty, "%7u", attr->med);
+ else
+ vty_out(vty, "%10u", attr->med);
+ else if (wide)
+ vty_out(vty, " ");
+ else
+ vty_out(vty, " ");
+
+ if (attr->flag & ATTR_FLAG_BIT(BGP_ATTR_LOCAL_PREF))
+ vty_out(vty, "%7u", attr->local_pref);
+ else
+ vty_out(vty, " ");
+
+ vty_out(vty, "%7u ", attr->weight);
+
+ /* Print aspath */
+ if (attr->aspath)
+ aspath_print_vty(vty, attr->aspath);
+
+ /* Print origin */
+ vty_out(vty, "%s", bgp_origin_str[attr->origin]);
+ }
+ }
+ if (use_json) {
+ struct bgp_path_info *bpi = bgp_dest_get_bgp_path_info(dest);
+
+#if CONFDATE > 20231208
+CPP_NOTICE("Drop `bgpStatusCodes` from JSON outputs")
+#endif
+ json_object_boolean_true_add(json_status, "*");
+ json_object_boolean_true_add(json_status, ">");
+ json_object_boolean_true_add(json_net, "valid");
+ json_object_boolean_true_add(json_net, "best");
+
+ if (bpi && CHECK_FLAG(bpi->flags, BGP_PATH_MULTIPATH)) {
+ json_object_boolean_true_add(json_status, "=");
+ json_object_boolean_true_add(json_net, "multipath");
+ }
+ json_object_object_add(json_net, "appliedStatusSymbols",
+ json_status);
+ json_object_object_addf(json_ar, json_net, "%pFX", p);
+ } else
+ vty_out(vty, "\n");
+}
+
+void route_vty_out_tag(struct vty *vty, const struct prefix *p,
+ struct bgp_path_info *path, int display, safi_t safi,
+ json_object *json)
+{
+ json_object *json_out = NULL;
+ struct attr *attr;
+ mpls_label_t label = MPLS_INVALID_LABEL;
+
+ if (!path->extra)
+ return;
+
+ if (json)
+ json_out = json_object_new_object();
+
+ /* short status lead text */
+ route_vty_short_status_out(vty, path, p, json_out);
+
+ /* print prefix and mask */
+ if (json == NULL) {
+ if (!display)
+ route_vty_out_route(path->net, p, vty, NULL, false);
+ else
+ vty_out(vty, "%*s", 17, " ");
+ }
+
+ /* Print attribute */
+ attr = path->attr;
+ if (((p->family == AF_INET) &&
+ ((safi == SAFI_MPLS_VPN || safi == SAFI_ENCAP))) ||
+ (safi == SAFI_EVPN && !BGP_ATTR_NEXTHOP_AFI_IP6(attr)) ||
+ (!BGP_ATTR_MP_NEXTHOP_LEN_IP6(attr))) {
+ if (safi == SAFI_MPLS_VPN || safi == SAFI_ENCAP
+ || safi == SAFI_EVPN) {
+ if (json)
+ json_object_string_addf(
+ json_out, "mpNexthopGlobalIn", "%pI4",
+ &attr->mp_nexthop_global_in);
+ else
+ vty_out(vty, "%-16pI4",
+ &attr->mp_nexthop_global_in);
+ } else {
+ if (json)
+ json_object_string_addf(json_out, "nexthop",
+ "%pI4", &attr->nexthop);
+ else
+ vty_out(vty, "%-16pI4", &attr->nexthop);
+ }
+ } else if (((p->family == AF_INET6) &&
+ ((safi == SAFI_MPLS_VPN || safi == SAFI_ENCAP))) ||
+ (safi == SAFI_EVPN && BGP_ATTR_MP_NEXTHOP_LEN_IP6(attr)) ||
+ (BGP_ATTR_MP_NEXTHOP_LEN_IP6(attr))) {
+ char buf_a[512];
+
+ if (attr->mp_nexthop_len == BGP_ATTR_NHLEN_IPV6_GLOBAL) {
+ if (json)
+ json_object_string_addf(
+ json_out, "mpNexthopGlobalIn", "%pI6",
+ &attr->mp_nexthop_global);
+ else
+ vty_out(vty, "%s",
+ inet_ntop(AF_INET6,
+ &attr->mp_nexthop_global,
+ buf_a, sizeof(buf_a)));
+ } else if (attr->mp_nexthop_len
+ == BGP_ATTR_NHLEN_IPV6_GLOBAL_AND_LL) {
+ snprintfrr(buf_a, sizeof(buf_a), "%pI6(%pI6)",
+ &attr->mp_nexthop_global,
+ &attr->mp_nexthop_local);
+ if (json)
+ json_object_string_add(json_out,
+ "mpNexthopGlobalLocal",
+ buf_a);
+ else
+ vty_out(vty, "%s", buf_a);
+ }
+ }
+
+ if (bgp_is_valid_label(&path->extra->label[0])) {
+ label = decode_label(&path->extra->label[0]);
+ if (json) {
+ json_object_int_add(json_out, "notag", label);
+ json_object_array_add(json, json_out);
+ } else {
+ vty_out(vty, "notag/%d", label);
+ vty_out(vty, "\n");
+ }
+ } else if (!json)
+ vty_out(vty, "\n");
+}
+
+void route_vty_out_overlay(struct vty *vty, const struct prefix *p,
+ struct bgp_path_info *path, int display,
+ json_object *json_paths)
+{
+ struct attr *attr;
+ json_object *json_path = NULL;
+ json_object *json_nexthop = NULL;
+ json_object *json_overlay = NULL;
+
+ if (!path->extra)
+ return;
+
+ if (json_paths) {
+ json_path = json_object_new_object();
+ json_overlay = json_object_new_object();
+ json_nexthop = json_object_new_object();
+ }
+
+ /* short status lead text */
+ route_vty_short_status_out(vty, path, p, json_path);
+
+ /* print prefix and mask */
+ if (!display)
+ route_vty_out_route(path->net, p, vty, json_path, false);
+ else
+ vty_out(vty, "%*s", 17, " ");
+
+ /* Print attribute */
+ attr = path->attr;
+ int af = NEXTHOP_FAMILY(attr->mp_nexthop_len);
+
+ switch (af) {
+ case AF_INET:
+ if (!json_path) {
+ vty_out(vty, "%-16pI4", &attr->mp_nexthop_global_in);
+ } else {
+ json_object_string_addf(json_nexthop, "ip", "%pI4",
+ &attr->mp_nexthop_global_in);
+
+ json_object_string_add(json_nexthop, "afi", "ipv4");
+
+ json_object_object_add(json_path, "nexthop",
+ json_nexthop);
+ }
+ break;
+ case AF_INET6:
+ if (!json_path) {
+ vty_out(vty, "%pI6(%pI6)", &attr->mp_nexthop_global,
+ &attr->mp_nexthop_local);
+ } else {
+ json_object_string_addf(json_nexthop, "ipv6Global",
+ "%pI6",
+ &attr->mp_nexthop_global);
+
+ json_object_string_addf(json_nexthop, "ipv6LinkLocal",
+ "%pI6",
+ &attr->mp_nexthop_local);
+
+ json_object_string_add(json_nexthop, "afi", "ipv6");
+
+ json_object_object_add(json_path, "nexthop",
+ json_nexthop);
+ }
+ break;
+ default:
+ if (!json_path) {
+ vty_out(vty, "?");
+ } else {
+ json_object_string_add(json_nexthop, "error",
+ "Unsupported address-family");
+ }
+ }
+
+ const struct bgp_route_evpn *eo = bgp_attr_get_evpn_overlay(attr);
+
+ if (!json_path)
+ vty_out(vty, "/%pIA", &eo->gw_ip);
+ else
+ json_object_string_addf(json_overlay, "gw", "%pIA", &eo->gw_ip);
+
+ if (bgp_attr_get_ecommunity(attr)) {
+ char *mac = NULL;
+ struct ecommunity_val *routermac = ecommunity_lookup(
+ bgp_attr_get_ecommunity(attr), ECOMMUNITY_ENCODE_EVPN,
+ ECOMMUNITY_EVPN_SUBTYPE_ROUTERMAC);
+
+ if (routermac)
+ mac = ecom_mac2str((char *)routermac->val);
+ if (mac) {
+ if (!json_path) {
+ vty_out(vty, "/%s", mac);
+ } else {
+ json_object_string_add(json_overlay, "rmac",
+ mac);
+ }
+ XFREE(MTYPE_TMP, mac);
+ }
+ }
+
+ if (!json_path) {
+ vty_out(vty, "\n");
+ } else {
+ json_object_object_add(json_path, "overlay", json_overlay);
+
+ json_object_array_add(json_paths, json_path);
+ }
+}
+
+/* dampening route */
+static void damp_route_vty_out(struct vty *vty, const struct prefix *p,
+ struct bgp_path_info *path, int display,
+ afi_t afi, safi_t safi, bool use_json,
+ json_object *json_paths)
+{
+ struct attr *attr = path->attr;
+ int len;
+ char timebuf[BGP_UPTIME_LEN] = {};
+ json_object *json_path = NULL;
+
+ if (use_json)
+ json_path = json_object_new_object();
+
+ /* short status lead text */
+ route_vty_short_status_out(vty, path, p, json_path);
+
+ /* print prefix and mask */
+ if (!use_json) {
+ if (!display)
+ route_vty_out_route(path->net, p, vty, NULL, false);
+ else
+ vty_out(vty, "%*s", 17, " ");
+
+ len = vty_out(vty, "%s", path->peer->host);
+ len = 17 - len;
+
+ if (len < 1)
+ vty_out(vty, "\n%*s", 34, " ");
+ else
+ vty_out(vty, "%*s", len, " ");
+
+ vty_out(vty, "%s ",
+ bgp_damp_reuse_time_vty(vty, path, timebuf,
+ BGP_UPTIME_LEN, afi, safi,
+ use_json, NULL));
+
+ if (attr->aspath)
+ aspath_print_vty(vty, attr->aspath);
+
+ vty_out(vty, "%s", bgp_origin_str[attr->origin]);
+
+ vty_out(vty, "\n");
+ } else {
+ bgp_damp_reuse_time_vty(vty, path, timebuf, BGP_UPTIME_LEN, afi,
+ safi, use_json, json_path);
+
+ if (attr->aspath)
+ json_object_string_add(json_path, "asPath",
+ attr->aspath->str);
+
+ json_object_string_add(json_path, "origin",
+ bgp_origin_str[attr->origin]);
+ json_object_string_add(json_path, "peerHost", path->peer->host);
+
+ json_object_array_add(json_paths, json_path);
+ }
+}
+
+/* flap route */
+static void flap_route_vty_out(struct vty *vty, const struct prefix *p,
+ struct bgp_path_info *path, int display,
+ afi_t afi, safi_t safi, bool use_json,
+ json_object *json_paths)
+{
+ struct attr *attr = path->attr;
+ struct bgp_damp_info *bdi;
+ char timebuf[BGP_UPTIME_LEN] = {};
+ int len;
+ json_object *json_path = NULL;
+
+ if (!path->extra)
+ return;
+
+ if (use_json)
+ json_path = json_object_new_object();
+
+ bdi = path->extra->damp_info;
+
+ /* short status lead text */
+ route_vty_short_status_out(vty, path, p, json_path);
+
+ if (!use_json) {
+ if (!display)
+ route_vty_out_route(path->net, p, vty, NULL, false);
+ else
+ vty_out(vty, "%*s", 17, " ");
+
+ len = vty_out(vty, "%s", path->peer->host);
+ len = 16 - len;
+ if (len < 1)
+ vty_out(vty, "\n%*s", 33, " ");
+ else
+ vty_out(vty, "%*s", len, " ");
+
+ len = vty_out(vty, "%d", bdi->flap);
+ len = 5 - len;
+ if (len < 1)
+ vty_out(vty, " ");
+ else
+ vty_out(vty, "%*s", len, " ");
+
+ vty_out(vty, "%s ", peer_uptime(bdi->start_time, timebuf,
+ BGP_UPTIME_LEN, 0, NULL));
+
+ if (CHECK_FLAG(path->flags, BGP_PATH_DAMPED)
+ && !CHECK_FLAG(path->flags, BGP_PATH_HISTORY))
+ vty_out(vty, "%s ",
+ bgp_damp_reuse_time_vty(vty, path, timebuf,
+ BGP_UPTIME_LEN, afi,
+ safi, use_json, NULL));
+ else
+ vty_out(vty, "%*s ", 8, " ");
+
+ if (attr->aspath)
+ aspath_print_vty(vty, attr->aspath);
+
+ vty_out(vty, "%s", bgp_origin_str[attr->origin]);
+
+ vty_out(vty, "\n");
+ } else {
+ json_object_string_add(json_path, "peerHost", path->peer->host);
+ json_object_int_add(json_path, "bdiFlap", bdi->flap);
+
+ peer_uptime(bdi->start_time, timebuf, BGP_UPTIME_LEN, use_json,
+ json_path);
+
+ if (CHECK_FLAG(path->flags, BGP_PATH_DAMPED)
+ && !CHECK_FLAG(path->flags, BGP_PATH_HISTORY))
+ bgp_damp_reuse_time_vty(vty, path, timebuf,
+ BGP_UPTIME_LEN, afi, safi,
+ use_json, json_path);
+
+ if (attr->aspath)
+ json_object_string_add(json_path, "asPath",
+ attr->aspath->str);
+
+ json_object_string_add(json_path, "origin",
+ bgp_origin_str[attr->origin]);
+
+ json_object_array_add(json_paths, json_path);
+ }
+}
+
+static void route_vty_out_advertised_to(struct vty *vty, struct peer *peer,
+ int *first, const char *header,
+ json_object *json_adv_to)
+{
+ json_object *json_peer = NULL;
+
+ if (json_adv_to) {
+ /* 'advertised-to' is a dictionary of peers we have advertised
+ * this
+ * prefix too. The key is the peer's IP or swpX, the value is
+ * the
+ * hostname if we know it and "" if not.
+ */
+ json_peer = json_object_new_object();
+
+ if (peer->hostname)
+ json_object_string_add(json_peer, "hostname",
+ peer->hostname);
+
+ if (peer->conf_if)
+ json_object_object_add(json_adv_to, peer->conf_if,
+ json_peer);
+ else
+ json_object_object_addf(json_adv_to, json_peer, "%pSU",
+ &peer->connection->su);
+ } else {
+ if (*first) {
+ vty_out(vty, "%s", header);
+ *first = 0;
+ }
+
+ if (peer->hostname
+ && CHECK_FLAG(peer->bgp->flags, BGP_FLAG_SHOW_HOSTNAME)) {
+ if (peer->conf_if)
+ vty_out(vty, " %s(%s)", peer->hostname,
+ peer->conf_if);
+ else
+ vty_out(vty, " %s(%pSU)", peer->hostname,
+ &peer->connection->su);
+ } else {
+ if (peer->conf_if)
+ vty_out(vty, " %s", peer->conf_if);
+ else
+ vty_out(vty, " %pSU", &peer->connection->su);
+ }
+ }
+}
+
+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");
+ }
+}
+
+static void route_vty_out_detail_es_info(struct vty *vty,
+ struct bgp_path_info *pi,
+ struct attr *attr,
+ json_object *json_path)
+{
+ char esi_buf[ESI_STR_LEN];
+ bool es_local = !!CHECK_FLAG(attr->es_flags, ATTR_ES_IS_LOCAL);
+ bool peer_router = !!CHECK_FLAG(attr->es_flags,
+ ATTR_ES_PEER_ROUTER);
+ bool peer_active = !!CHECK_FLAG(attr->es_flags,
+ ATTR_ES_PEER_ACTIVE);
+ bool peer_proxy = !!CHECK_FLAG(attr->es_flags,
+ ATTR_ES_PEER_PROXY);
+ esi_to_str(&attr->esi, esi_buf, sizeof(esi_buf));
+ if (json_path) {
+ json_object *json_es_info = NULL;
+
+ json_object_string_add(
+ json_path, "esi",
+ esi_buf);
+ if (es_local || bgp_evpn_attr_is_sync(attr)) {
+ json_es_info = json_object_new_object();
+ if (es_local)
+ json_object_boolean_true_add(
+ json_es_info, "localEs");
+ if (peer_active)
+ json_object_boolean_true_add(
+ json_es_info, "peerActive");
+ if (peer_proxy)
+ json_object_boolean_true_add(
+ json_es_info, "peerProxy");
+ if (peer_router)
+ json_object_boolean_true_add(
+ json_es_info, "peerRouter");
+ if (attr->mm_sync_seqnum)
+ json_object_int_add(
+ json_es_info, "peerSeq",
+ attr->mm_sync_seqnum);
+ json_object_object_add(
+ json_path, "es_info",
+ json_es_info);
+ }
+ } else {
+ if (bgp_evpn_attr_is_sync(attr))
+ vty_out(vty,
+ " ESI %s %s peer-info: (%s%s%sMM: %d)\n",
+ esi_buf,
+ es_local ? "local-es":"",
+ peer_proxy ? "proxy " : "",
+ peer_active ? "active ":"",
+ peer_router ? "router ":"",
+ attr->mm_sync_seqnum);
+ else
+ vty_out(vty, " ESI %s %s\n",
+ esi_buf,
+ es_local ? "local-es":"");
+ }
+}
+
+void route_vty_out_detail(struct vty *vty, struct bgp *bgp, struct bgp_dest *bn,
+ const struct prefix *p, struct bgp_path_info *path,
+ afi_t afi, safi_t safi,
+ enum rpki_states rpki_curr_state,
+ json_object *json_paths)
+{
+ char buf[INET6_ADDRSTRLEN];
+ char tag_buf[30];
+ struct attr *attr = path->attr;
+ time_t tbuf;
+ char timebuf[32];
+ json_object *json_bestpath = NULL;
+ json_object *json_cluster_list = NULL;
+ json_object *json_cluster_list_list = NULL;
+ json_object *json_ext_community = NULL;
+ json_object *json_last_update = NULL;
+ json_object *json_pmsi = NULL;
+ json_object *json_nexthop_global = NULL;
+ json_object *json_nexthop_ll = NULL;
+ json_object *json_nexthops = NULL;
+ json_object *json_path = NULL;
+ json_object *json_peer = NULL;
+ json_object *json_string = NULL;
+ json_object *json_adv_to = NULL;
+ int first = 0;
+ struct listnode *node, *nnode;
+ struct peer *peer;
+ bool addpath_capable;
+ int has_adj;
+ unsigned int first_as;
+ bool nexthop_self =
+ CHECK_FLAG(path->flags, BGP_PATH_ANNC_NH_SELF) ? true : false;
+ int i;
+ char *nexthop_hostname =
+ bgp_nexthop_hostname(path->peer, path->nexthop);
+ uint32_t ttl = 0;
+ uint32_t bos = 0;
+ uint32_t exp = 0;
+ mpls_label_t label = MPLS_INVALID_LABEL;
+ tag_buf[0] = '\0';
+ struct bgp_path_info *bpi_ultimate =
+ bgp_get_imported_bpi_ultimate(path);
+
+ if (json_paths) {
+ json_path = json_object_new_object();
+ json_peer = json_object_new_object();
+ json_nexthop_global = json_object_new_object();
+ }
+
+ if (safi == SAFI_EVPN) {
+ if (!json_paths)
+ vty_out(vty, " Route %pFX", p);
+ }
+
+ if (path->extra) {
+ if (path->extra && path->extra->num_labels) {
+ bgp_evpn_label2str(path->extra->label,
+ path->extra->num_labels, tag_buf,
+ sizeof(tag_buf));
+ }
+ if (safi == SAFI_EVPN) {
+ if (!json_paths) {
+ if (tag_buf[0] != '\0')
+ vty_out(vty, " VNI %s", tag_buf);
+ } else {
+ if (tag_buf[0])
+ json_object_string_add(json_path, "vni",
+ tag_buf);
+ }
+ }
+ }
+
+ if (safi == SAFI_EVPN
+ && attr->evpn_overlay.type == OVERLAY_INDEX_GATEWAY_IP) {
+ char gwip_buf[INET6_ADDRSTRLEN];
+
+ ipaddr2str(&attr->evpn_overlay.gw_ip, gwip_buf,
+ sizeof(gwip_buf));
+
+ if (json_paths)
+ json_object_string_add(json_path, "gatewayIP",
+ gwip_buf);
+ else
+ vty_out(vty, " Gateway IP %s", gwip_buf);
+ }
+
+ if (safi == SAFI_EVPN && !json_path)
+ vty_out(vty, "\n");
+
+
+ if (path->extra && path->extra->vrfleak &&
+ path->extra->vrfleak->parent && !json_paths) {
+ struct bgp_path_info *parent_ri;
+ struct bgp_dest *dest, *pdest;
+
+ parent_ri =
+ (struct bgp_path_info *)path->extra->vrfleak->parent;
+ dest = parent_ri->net;
+ if (dest && dest->pdest) {
+ pdest = dest->pdest;
+ if (is_pi_family_evpn(parent_ri)) {
+ vty_out(vty, " Imported from ");
+ vty_out(vty, BGP_RD_AS_FORMAT(bgp->asnotation),
+ (struct prefix_rd *)bgp_dest_get_prefix(
+ pdest));
+ vty_out(vty, ":%pFX, VNI %s",
+ (struct prefix_evpn *)
+ bgp_dest_get_prefix(dest),
+ tag_buf);
+ if (CHECK_FLAG(attr->es_flags, ATTR_ES_L3_NHG))
+ vty_out(vty, ", L3NHG %s",
+ CHECK_FLAG(
+ attr->es_flags,
+ ATTR_ES_L3_NHG_ACTIVE)
+ ? "active"
+ : "inactive");
+ vty_out(vty, "\n");
+
+ } else {
+ vty_out(vty, " Imported from ");
+ vty_out(vty, BGP_RD_AS_FORMAT(bgp->asnotation),
+ (struct prefix_rd *)bgp_dest_get_prefix(
+ pdest));
+ vty_out(vty, ":%pFX\n",
+ (struct prefix_evpn *)
+ bgp_dest_get_prefix(dest));
+ }
+ }
+ }
+
+ /* Line1 display AS-path, Aggregator */
+ if (attr->aspath) {
+ if (json_paths) {
+ if (!attr->aspath->json)
+ aspath_str_update(attr->aspath, true);
+ json_object_lock(attr->aspath->json);
+ json_object_object_add(json_path, "aspath",
+ attr->aspath->json);
+ } else {
+ if (attr->aspath->segments)
+ vty_out(vty, " %s", attr->aspath->str);
+ else
+ vty_out(vty, " Local");
+ }
+ }
+
+ if (CHECK_FLAG(path->flags, BGP_PATH_REMOVED)) {
+ if (json_paths)
+ json_object_boolean_true_add(json_path, "removed");
+ else
+ vty_out(vty, ", (removed)");
+ }
+
+ if (CHECK_FLAG(path->flags, BGP_PATH_STALE)) {
+ if (json_paths)
+ json_object_boolean_true_add(json_path, "stale");
+ else
+ vty_out(vty, ", (stale)");
+ }
+
+ if (CHECK_FLAG(attr->flag, ATTR_FLAG_BIT(BGP_ATTR_AGGREGATOR))) {
+ if (json_paths) {
+ json_object_int_add(json_path, "aggregatorAs",
+ attr->aggregator_as);
+ json_object_string_addf(json_path, "aggregatorId",
+ "%pI4", &attr->aggregator_addr);
+ } else {
+ vty_out(vty, ", (aggregated by %u %pI4)",
+ attr->aggregator_as, &attr->aggregator_addr);
+ }
+ }
+
+ if (CHECK_FLAG(path->peer->af_flags[afi][safi],
+ PEER_FLAG_REFLECTOR_CLIENT)) {
+ if (json_paths)
+ json_object_boolean_true_add(json_path,
+ "rxedFromRrClient");
+ else
+ vty_out(vty, ", (Received from a RR-client)");
+ }
+
+ if (CHECK_FLAG(path->peer->af_flags[afi][safi],
+ PEER_FLAG_RSERVER_CLIENT)) {
+ if (json_paths)
+ json_object_boolean_true_add(json_path,
+ "rxedFromRsClient");
+ else
+ vty_out(vty, ", (Received from a RS-client)");
+ }
+
+ if (CHECK_FLAG(path->flags, BGP_PATH_HISTORY)) {
+ if (json_paths)
+ json_object_boolean_true_add(json_path,
+ "dampeningHistoryEntry");
+ else
+ vty_out(vty, ", (history entry)");
+ } else if (CHECK_FLAG(path->flags, BGP_PATH_DAMPED)) {
+ if (json_paths)
+ json_object_boolean_true_add(json_path,
+ "dampeningSuppressed");
+ else
+ vty_out(vty, ", (suppressed due to dampening)");
+ }
+
+ if (!json_paths)
+ vty_out(vty, "\n");
+
+ /* Line2 display Next-hop, Neighbor, Router-id */
+ /* Display the nexthop */
+
+ if ((p->family == AF_INET || p->family == AF_ETHERNET ||
+ p->family == AF_EVPN) &&
+ (safi == SAFI_MPLS_VPN || safi == SAFI_ENCAP || safi == SAFI_EVPN ||
+ !BGP_ATTR_MP_NEXTHOP_LEN_IP6(attr))) {
+ if (safi == SAFI_MPLS_VPN || safi == SAFI_ENCAP
+ || safi == SAFI_EVPN) {
+ if (json_paths) {
+ json_object_string_addf(
+ json_nexthop_global, "ip", "%pI4",
+ &attr->mp_nexthop_global_in);
+
+ if (path->peer->hostname)
+ json_object_string_add(
+ json_nexthop_global, "hostname",
+ path->peer->hostname);
+ } else {
+ if (nexthop_hostname)
+ vty_out(vty, " %pI4(%s)",
+ &attr->mp_nexthop_global_in,
+ nexthop_hostname);
+ else
+ vty_out(vty, " %pI4",
+ &attr->mp_nexthop_global_in);
+ }
+ } else {
+ if (json_paths) {
+ json_object_string_addf(json_nexthop_global,
+ "ip", "%pI4",
+ &attr->nexthop);
+
+ if (path->peer->hostname)
+ json_object_string_add(
+ json_nexthop_global, "hostname",
+ path->peer->hostname);
+ } else {
+ if (nexthop_hostname)
+ vty_out(vty, " %pI4(%s)",
+ &attr->nexthop,
+ nexthop_hostname);
+ else
+ vty_out(vty, " %pI4",
+ &attr->nexthop);
+ }
+ }
+
+ if (json_paths)
+ json_object_string_add(json_nexthop_global, "afi",
+ "ipv4");
+ } else {
+ if (json_paths) {
+ json_object_string_addf(json_nexthop_global, "ip",
+ "%pI6",
+ &attr->mp_nexthop_global);
+
+ if (path->peer->hostname)
+ json_object_string_add(json_nexthop_global,
+ "hostname",
+ path->peer->hostname);
+
+ json_object_string_add(json_nexthop_global, "afi",
+ "ipv6");
+ json_object_string_add(json_nexthop_global, "scope",
+ "global");
+ } else {
+ if (nexthop_hostname)
+ vty_out(vty, " %pI6(%s)",
+ &attr->mp_nexthop_global,
+ nexthop_hostname);
+ else
+ vty_out(vty, " %pI6",
+ &attr->mp_nexthop_global);
+ }
+ }
+
+ /* Display the IGP cost or 'inaccessible' */
+ if (!CHECK_FLAG(bpi_ultimate->flags, BGP_PATH_VALID)) {
+ bool import = CHECK_FLAG(bgp->flags, BGP_FLAG_IMPORT_CHECK);
+
+ if (json_paths) {
+ json_object_boolean_false_add(json_nexthop_global,
+ "accessible");
+ json_object_boolean_add(json_nexthop_global,
+ "importCheckEnabled", import);
+ } else {
+ vty_out(vty, " (inaccessible%s)",
+ import ? ", import-check enabled" : "");
+ }
+ } else {
+ if (bpi_ultimate->extra && bpi_ultimate->extra->igpmetric) {
+ if (json_paths)
+ json_object_int_add(
+ json_nexthop_global, "metric",
+ bpi_ultimate->extra->igpmetric);
+ else
+ vty_out(vty, " (metric %u)",
+ bpi_ultimate->extra->igpmetric);
+ }
+
+ /* IGP cost is 0, display this only for json */
+ else {
+ if (json_paths)
+ json_object_int_add(json_nexthop_global,
+ "metric", 0);
+ }
+
+ if (json_paths)
+ json_object_boolean_true_add(json_nexthop_global,
+ "accessible");
+ }
+
+ /* Display peer "from" output */
+ /* This path was originated locally */
+ if (path->peer == bgp->peer_self) {
+
+ if (safi == SAFI_EVPN || (p->family == AF_INET &&
+ !BGP_ATTR_MP_NEXTHOP_LEN_IP6(attr))) {
+ if (json_paths)
+ json_object_string_add(json_peer, "peerId",
+ "0.0.0.0");
+ else
+ vty_out(vty, " from 0.0.0.0 ");
+ } else {
+ if (json_paths)
+ json_object_string_add(json_peer, "peerId",
+ "::");
+ else
+ vty_out(vty, " from :: ");
+ }
+
+ if (json_paths)
+ json_object_string_addf(json_peer, "routerId", "%pI4",
+ &bgp->router_id);
+ else
+ vty_out(vty, "(%pI4)", &bgp->router_id);
+ }
+
+ /* We RXed this path from one of our peers */
+ else {
+
+ if (json_paths) {
+ json_object_string_addf(json_peer, "peerId", "%pSU",
+ &path->peer->connection->su);
+ json_object_string_addf(json_peer, "routerId", "%pI4",
+ &path->peer->remote_id);
+
+ if (path->peer->hostname)
+ json_object_string_add(json_peer, "hostname",
+ path->peer->hostname);
+
+ if (path->peer->domainname)
+ json_object_string_add(json_peer, "domainname",
+ path->peer->domainname);
+
+ if (path->peer->conf_if)
+ json_object_string_add(json_peer, "interface",
+ path->peer->conf_if);
+ } else {
+ if (path->peer->conf_if) {
+ if (path->peer->hostname
+ && CHECK_FLAG(path->peer->bgp->flags,
+ BGP_FLAG_SHOW_HOSTNAME))
+ vty_out(vty, " from %s(%s)",
+ path->peer->hostname,
+ path->peer->conf_if);
+ else
+ vty_out(vty, " from %s",
+ path->peer->conf_if);
+ } else {
+ if (path->peer->hostname
+ && CHECK_FLAG(path->peer->bgp->flags,
+ BGP_FLAG_SHOW_HOSTNAME))
+ vty_out(vty, " from %s(%s)",
+ path->peer->hostname,
+ path->peer->host);
+ else
+ vty_out(vty, " from %pSU",
+ &path->peer->connection->su);
+ }
+
+ if (attr->flag & ATTR_FLAG_BIT(BGP_ATTR_ORIGINATOR_ID))
+ vty_out(vty, " (%pI4)", &attr->originator_id);
+ else
+ vty_out(vty, " (%pI4)", &path->peer->remote_id);
+ }
+ }
+
+ /*
+ * Note when vrfid of nexthop is different from that of prefix
+ */
+ if (path->extra && path->extra->vrfleak &&
+ path->extra->vrfleak->bgp_orig) {
+ vrf_id_t nexthop_vrfid = path->extra->vrfleak->bgp_orig->vrf_id;
+
+ if (json_paths) {
+ const char *vn;
+
+ if (path->extra->vrfleak->bgp_orig->inst_type ==
+ BGP_INSTANCE_TYPE_DEFAULT)
+ vn = VRF_DEFAULT_NAME;
+ else
+ vn = path->extra->vrfleak->bgp_orig->name;
+
+ json_object_string_add(json_path, "nhVrfName", vn);
+
+ if (nexthop_vrfid == VRF_UNKNOWN) {
+ json_object_int_add(json_path, "nhVrfId", -1);
+ } else {
+ json_object_int_add(json_path, "nhVrfId",
+ (int)nexthop_vrfid);
+ }
+ } else {
+ if (nexthop_vrfid == VRF_UNKNOWN)
+ vty_out(vty, " vrf ?");
+ else {
+ struct vrf *vrf;
+
+ vrf = vrf_lookup_by_id(nexthop_vrfid);
+ vty_out(vty, " vrf %s(%u)",
+ VRF_LOGNAME(vrf), nexthop_vrfid);
+ }
+ }
+ }
+
+ if (nexthop_self) {
+ if (json_paths) {
+ json_object_boolean_true_add(json_path,
+ "announceNexthopSelf");
+ } else {
+ vty_out(vty, " announce-nh-self");
+ }
+ }
+
+ if (!json_paths)
+ vty_out(vty, "\n");
+
+ /* display the link-local nexthop */
+ if (attr->mp_nexthop_len == BGP_ATTR_NHLEN_IPV6_GLOBAL_AND_LL) {
+ if (json_paths) {
+ json_nexthop_ll = json_object_new_object();
+ json_object_string_addf(json_nexthop_ll, "ip", "%pI6",
+ &attr->mp_nexthop_local);
+
+ if (path->peer->hostname)
+ json_object_string_add(json_nexthop_ll,
+ "hostname",
+ path->peer->hostname);
+
+ json_object_string_add(json_nexthop_ll, "afi", "ipv6");
+ json_object_string_add(json_nexthop_ll, "scope",
+ "link-local");
+
+ json_object_boolean_true_add(json_nexthop_ll,
+ "accessible");
+
+ if (!attr->mp_nexthop_prefer_global)
+ json_object_boolean_true_add(json_nexthop_ll,
+ "used");
+ else
+ json_object_boolean_true_add(
+ json_nexthop_global, "used");
+ } else {
+ vty_out(vty, " (%s) %s\n",
+ inet_ntop(AF_INET6, &attr->mp_nexthop_local,
+ buf, INET6_ADDRSTRLEN),
+ attr->mp_nexthop_prefer_global
+ ? "(prefer-global)"
+ : "(used)");
+ }
+ }
+ /* If we do not have a link-local nexthop then we must flag the
+ global as "used" */
+ else {
+ if (json_paths)
+ json_object_boolean_true_add(json_nexthop_global,
+ "used");
+ }
+
+ if (safi == SAFI_EVPN &&
+ bgp_evpn_is_esi_valid(&attr->esi)) {
+ route_vty_out_detail_es_info(vty, path, attr, json_path);
+ }
+
+ /* Line 3 display Origin, Med, Locpref, Weight, Tag, valid,
+ * Int/Ext/Local, Atomic, best */
+ if (json_paths)
+ json_object_string_add(json_path, "origin",
+ bgp_origin_long_str[attr->origin]);
+ else
+ vty_out(vty, " Origin %s",
+ bgp_origin_long_str[attr->origin]);
+
+ if (attr->flag & ATTR_FLAG_BIT(BGP_ATTR_MULTI_EXIT_DISC)) {
+ if (json_paths)
+ json_object_int_add(json_path, "metric", attr->med);
+ else
+ vty_out(vty, ", metric %u", attr->med);
+ }
+
+ if (attr->flag & ATTR_FLAG_BIT(BGP_ATTR_LOCAL_PREF)) {
+ if (json_paths)
+ json_object_int_add(json_path, "locPrf",
+ attr->local_pref);
+ else
+ vty_out(vty, ", localpref %u", attr->local_pref);
+ }
+
+ if (attr->flag & ATTR_FLAG_BIT(BGP_ATTR_AIGP)) {
+ if (json_paths)
+ json_object_int_add(json_path, "aigpMetric",
+ bgp_attr_get_aigp_metric(attr));
+ else
+ vty_out(vty, ", aigp-metric %" PRIu64,
+ bgp_attr_get_aigp_metric(attr));
+ }
+
+ if (attr->weight != 0) {
+ if (json_paths)
+ json_object_int_add(json_path, "weight", attr->weight);
+ else
+ vty_out(vty, ", weight %u", attr->weight);
+ }
+
+ if (attr->tag != 0) {
+ if (json_paths)
+ json_object_int_add(json_path, "tag", attr->tag);
+ else
+ vty_out(vty, ", tag %" ROUTE_TAG_PRI, attr->tag);
+ }
+
+ if (!CHECK_FLAG(path->flags, BGP_PATH_VALID)) {
+ if (json_paths)
+ json_object_boolean_false_add(json_path, "valid");
+ else
+ vty_out(vty, ", invalid");
+ } else if (!CHECK_FLAG(path->flags, BGP_PATH_HISTORY)) {
+ if (json_paths)
+ json_object_boolean_true_add(json_path, "valid");
+ else
+ vty_out(vty, ", valid");
+ }
+
+ if (json_paths)
+ json_object_int_add(json_path, "version", bn->version);
+
+ if (path->peer != bgp->peer_self) {
+ if (path->peer->as == path->peer->local_as) {
+ if (CHECK_FLAG(bgp->config, BGP_CONFIG_CONFEDERATION)) {
+ if (json_paths)
+ json_object_string_add(
+ json_peer, "type",
+ "confed-internal");
+ else
+ vty_out(vty, ", confed-internal");
+ } else {
+ if (json_paths)
+ json_object_string_add(
+ json_peer, "type", "internal");
+ else
+ vty_out(vty, ", internal");
+ }
+ } else {
+ if (bgp_confederation_peers_check(bgp,
+ path->peer->as)) {
+ if (json_paths)
+ json_object_string_add(
+ json_peer, "type",
+ "confed-external");
+ else
+ vty_out(vty, ", confed-external");
+ } else {
+ if (json_paths)
+ json_object_string_add(
+ json_peer, "type", "external");
+ else
+ vty_out(vty, ", external");
+ }
+ }
+ } else if (path->sub_type == BGP_ROUTE_AGGREGATE) {
+ if (json_paths) {
+ json_object_boolean_true_add(json_path, "aggregated");
+ json_object_boolean_true_add(json_path, "local");
+ } else {
+ vty_out(vty, ", aggregated, local");
+ }
+ } else if (path->type != ZEBRA_ROUTE_BGP) {
+ if (json_paths)
+ json_object_boolean_true_add(json_path, "sourced");
+ else
+ vty_out(vty, ", sourced");
+ } else {
+ if (json_paths) {
+ json_object_boolean_true_add(json_path, "sourced");
+ json_object_boolean_true_add(json_path, "local");
+ } else {
+ vty_out(vty, ", sourced, local");
+ }
+ }
+
+ if (attr->flag & ATTR_FLAG_BIT(BGP_ATTR_ATOMIC_AGGREGATE)) {
+ if (json_paths)
+ json_object_boolean_true_add(json_path,
+ "atomicAggregate");
+ else
+ vty_out(vty, ", atomic-aggregate");
+ }
+
+ if (attr->flag & ATTR_FLAG_BIT(BGP_ATTR_OTC)) {
+ if (json_paths)
+ json_object_int_add(json_path, "otc", attr->otc);
+ else
+ vty_out(vty, ", otc %u", attr->otc);
+ }
+
+ if (CHECK_FLAG(path->flags, BGP_PATH_MULTIPATH)
+ || (CHECK_FLAG(path->flags, BGP_PATH_SELECTED)
+ && bgp_path_info_mpath_count(path))) {
+ if (json_paths)
+ json_object_boolean_true_add(json_path, "multipath");
+ else
+ vty_out(vty, ", multipath");
+ }
+
+ // Mark the bestpath(s)
+ if (CHECK_FLAG(path->flags, BGP_PATH_DMED_SELECTED)) {
+ first_as = aspath_get_first_as(attr->aspath);
+
+ if (json_paths) {
+ if (!json_bestpath)
+ json_bestpath = json_object_new_object();
+ json_object_int_add(json_bestpath, "bestpathFromAs",
+ first_as);
+ } else {
+ if (first_as)
+ vty_out(vty, ", bestpath-from-AS %u", first_as);
+ else
+ vty_out(vty, ", bestpath-from-AS Local");
+ }
+ }
+
+ if (CHECK_FLAG(path->flags, BGP_PATH_SELECTED)) {
+ if (json_paths) {
+ if (!json_bestpath)
+ json_bestpath = json_object_new_object();
+ json_object_boolean_true_add(json_bestpath, "overall");
+ json_object_string_add(
+ json_bestpath, "selectionReason",
+ bgp_path_selection_reason2str(bn->reason));
+ } else {
+ vty_out(vty, ", best");
+ vty_out(vty, " (%s)",
+ bgp_path_selection_reason2str(bn->reason));
+ }
+ }
+
+ if (rpki_curr_state != RPKI_NOT_BEING_USED) {
+ if (json_paths)
+ json_object_string_add(
+ json_path, "rpkiValidationState",
+ bgp_rpki_validation2str(rpki_curr_state));
+ else
+ vty_out(vty, ", rpki validation-state: %s",
+ bgp_rpki_validation2str(rpki_curr_state));
+ }
+
+ if (json_bestpath)
+ json_object_object_add(json_path, "bestpath", json_bestpath);
+
+ if (!json_paths)
+ vty_out(vty, "\n");
+
+ /* Line 4 display Community */
+ if (attr->flag & ATTR_FLAG_BIT(BGP_ATTR_COMMUNITIES)) {
+ if (json_paths) {
+ if (!bgp_attr_get_community(attr)->json)
+ community_str(bgp_attr_get_community(attr),
+ true, true);
+ json_object_lock(bgp_attr_get_community(attr)->json);
+ json_object_object_add(
+ json_path, "community",
+ bgp_attr_get_community(attr)->json);
+ } else {
+ vty_out(vty, " Community: %s\n",
+ bgp_attr_get_community(attr)->str);
+ }
+ }
+
+ /* Line 5 display Extended-community */
+ if (attr->flag & ATTR_FLAG_BIT(BGP_ATTR_EXT_COMMUNITIES)) {
+ if (json_paths) {
+ json_ext_community = json_object_new_object();
+ json_object_string_add(
+ json_ext_community, "string",
+ bgp_attr_get_ecommunity(attr)->str);
+ json_object_object_add(json_path, "extendedCommunity",
+ json_ext_community);
+ } else {
+ vty_out(vty, " Extended Community: %s\n",
+ bgp_attr_get_ecommunity(attr)->str);
+ }
+ }
+
+ /* Line 6 display Large community */
+ if (attr->flag & ATTR_FLAG_BIT(BGP_ATTR_LARGE_COMMUNITIES)) {
+ if (json_paths) {
+ if (!bgp_attr_get_lcommunity(attr)->json)
+ lcommunity_str(bgp_attr_get_lcommunity(attr),
+ true, true);
+ json_object_lock(bgp_attr_get_lcommunity(attr)->json);
+ json_object_object_add(
+ json_path, "largeCommunity",
+ bgp_attr_get_lcommunity(attr)->json);
+ } else {
+ vty_out(vty, " Large Community: %s\n",
+ bgp_attr_get_lcommunity(attr)->str);
+ }
+ }
+
+ /* Line 7 display Originator, Cluster-id */
+ if ((attr->flag & ATTR_FLAG_BIT(BGP_ATTR_ORIGINATOR_ID))
+ || (attr->flag & ATTR_FLAG_BIT(BGP_ATTR_CLUSTER_LIST))) {
+ char buf[BUFSIZ] = {0};
+
+ if (attr->flag & ATTR_FLAG_BIT(BGP_ATTR_ORIGINATOR_ID)) {
+ if (json_paths)
+ json_object_string_addf(json_path,
+ "originatorId", "%pI4",
+ &attr->originator_id);
+ else
+ vty_out(vty, " Originator: %pI4",
+ &attr->originator_id);
+ }
+
+ if (attr->flag & ATTR_FLAG_BIT(BGP_ATTR_CLUSTER_LIST)) {
+ struct cluster_list *cluster =
+ bgp_attr_get_cluster(attr);
+ int i;
+
+ if (json_paths) {
+ json_cluster_list = json_object_new_object();
+ json_cluster_list_list =
+ json_object_new_array();
+
+ for (i = 0; i < cluster->length / 4; i++) {
+ json_string = json_object_new_string(
+ inet_ntop(AF_INET,
+ &cluster->list[i],
+ buf, sizeof(buf)));
+ json_object_array_add(
+ json_cluster_list_list,
+ json_string);
+ }
+
+ /*
+ * struct cluster_list does not have
+ * "str" variable like aspath and community
+ * do. Add this someday if someone asks
+ * for it.
+ * json_object_string_add(json_cluster_list,
+ * "string", cluster->str);
+ */
+ json_object_object_add(json_cluster_list,
+ "list",
+ json_cluster_list_list);
+ json_object_object_add(json_path, "clusterList",
+ json_cluster_list);
+ } else {
+ vty_out(vty, ", Cluster list: ");
+
+ for (i = 0; i < cluster->length / 4; i++) {
+ vty_out(vty, "%pI4 ",
+ &cluster->list[i]);
+ }
+ }
+ }
+
+ if (!json_paths)
+ vty_out(vty, "\n");
+ }
+
+ if (path->extra && path->extra->damp_info)
+ bgp_damp_info_vty(vty, path, afi, safi, json_path);
+
+ /* Remote Label */
+ if (path->extra && bgp_is_valid_label(&path->extra->label[0])
+ && (safi != SAFI_EVPN && !is_route_parent_evpn(path))) {
+ mpls_lse_decode(path->extra->label[0], &label, &ttl, &exp,
+ &bos);
+
+ if (json_paths)
+ json_object_int_add(json_path, "remoteLabel", label);
+ else
+ vty_out(vty, " Remote label: %d\n", label);
+ }
+
+ /* Remote SID */
+ if ((path->attr->srv6_l3vpn || path->attr->srv6_vpn) &&
+ safi != SAFI_EVPN) {
+ struct in6_addr *sid_tmp =
+ path->attr->srv6_l3vpn ? (&path->attr->srv6_l3vpn->sid)
+ : (&path->attr->srv6_vpn->sid);
+ if (json_paths)
+ json_object_string_addf(json_path, "remoteSid", "%pI6",
+ sid_tmp);
+ else
+ vty_out(vty, " Remote SID: %pI6\n", sid_tmp);
+ }
+
+ /* Label Index */
+ if (attr->label_index != BGP_INVALID_LABEL_INDEX) {
+ if (json_paths)
+ json_object_int_add(json_path, "labelIndex",
+ attr->label_index);
+ else
+ vty_out(vty, " Label Index: %d\n",
+ attr->label_index);
+ }
+
+ /* Line 8 display Addpath IDs */
+ 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);
+
+ /* 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, ",
+ 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_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,
+ bgp_addpath_id_for_peer(peer, afi, safi,
+ &path->tx_addpath));
+
+ if ((addpath_capable && has_adj)
+ || (!addpath_capable && has_adj
+ && CHECK_FLAG(path->flags,
+ BGP_PATH_SELECTED))) {
+ if (json_path && !json_adv_to)
+ json_adv_to = json_object_new_object();
+
+ route_vty_out_advertised_to(
+ vty, peer, &first,
+ " Advertised to:", json_adv_to);
+ }
+ }
+
+ if (json_path) {
+ if (json_adv_to) {
+ json_object_object_add(
+ json_path, "advertisedTo", json_adv_to);
+ }
+ } else {
+ if (!first) {
+ vty_out(vty, "\n");
+ }
+ }
+ }
+
+ /* Line 9 display Uptime */
+ tbuf = time(NULL) - (monotime(NULL) - path->uptime);
+ if (json_paths) {
+ json_last_update = json_object_new_object();
+ json_object_int_add(json_last_update, "epoch", tbuf);
+ json_object_string_add(json_last_update, "string",
+ ctime_r(&tbuf, timebuf));
+ json_object_object_add(json_path, "lastUpdate",
+ json_last_update);
+ } else
+ vty_out(vty, " Last update: %s", ctime_r(&tbuf, timebuf));
+
+ /* Line 10 display PMSI tunnel attribute, if present */
+ if (attr->flag & ATTR_FLAG_BIT(BGP_ATTR_PMSI_TUNNEL)) {
+ const char *str = lookup_msg(bgp_pmsi_tnltype_str,
+ bgp_attr_get_pmsi_tnl_type(attr),
+ PMSI_TNLTYPE_STR_DEFAULT);
+
+ if (json_paths) {
+ json_pmsi = json_object_new_object();
+ json_object_string_add(json_pmsi, "tunnelType", str);
+ json_object_int_add(json_pmsi, "label",
+ label2vni(&attr->label));
+ json_object_object_add(json_path, "pmsi", json_pmsi);
+ } else
+ vty_out(vty, " PMSI Tunnel Type: %s, label: %d\n",
+ str, label2vni(&attr->label));
+ }
+
+ if (path->peer->connection->t_gr_restart &&
+ CHECK_FLAG(path->flags, BGP_PATH_STALE)) {
+ unsigned long gr_remaining = event_timer_remain_second(
+ path->peer->connection->t_gr_restart);
+
+ if (json_paths) {
+ json_object_int_add(json_path,
+ "gracefulRestartSecondsRemaining",
+ gr_remaining);
+ } else
+ vty_out(vty,
+ " Time until Graceful Restart stale route deleted: %lu\n",
+ gr_remaining);
+ }
+
+ if (path->peer->t_llgr_stale[afi][safi] &&
+ bgp_attr_get_community(attr) &&
+ community_include(bgp_attr_get_community(attr),
+ COMMUNITY_LLGR_STALE)) {
+ unsigned long llgr_remaining = event_timer_remain_second(
+ path->peer->t_llgr_stale[afi][safi]);
+
+ if (json_paths) {
+ json_object_int_add(json_path, "llgrSecondsRemaining",
+ llgr_remaining);
+ } else
+ vty_out(vty,
+ " Time until Long-lived stale route deleted: %lu\n",
+ llgr_remaining);
+ }
+
+ /* Output some debug about internal state of the dest flags */
+ if (json_paths) {
+ if (CHECK_FLAG(bn->flags, BGP_NODE_PROCESS_SCHEDULED))
+ json_object_boolean_true_add(json_path, "processScheduled");
+ if (CHECK_FLAG(bn->flags, BGP_NODE_USER_CLEAR))
+ json_object_boolean_true_add(json_path, "userCleared");
+ if (CHECK_FLAG(bn->flags, BGP_NODE_LABEL_CHANGED))
+ json_object_boolean_true_add(json_path, "labelChanged");
+ if (CHECK_FLAG(bn->flags, BGP_NODE_REGISTERED_FOR_LABEL))
+ json_object_boolean_true_add(json_path, "registeredForLabel");
+ if (CHECK_FLAG(bn->flags, BGP_NODE_SELECT_DEFER))
+ json_object_boolean_true_add(json_path, "selectDefered");
+ if (CHECK_FLAG(bn->flags, BGP_NODE_FIB_INSTALLED))
+ json_object_boolean_true_add(json_path, "fibInstalled");
+ if (CHECK_FLAG(bn->flags, BGP_NODE_FIB_INSTALL_PENDING))
+ json_object_boolean_true_add(json_path, "fibPending");
+
+ if (json_nexthop_global || json_nexthop_ll) {
+ json_nexthops = json_object_new_array();
+
+ if (json_nexthop_global)
+ json_object_array_add(json_nexthops,
+ json_nexthop_global);
+
+ if (json_nexthop_ll)
+ json_object_array_add(json_nexthops,
+ json_nexthop_ll);
+
+ json_object_object_add(json_path, "nexthops",
+ json_nexthops);
+ }
+
+ json_object_object_add(json_path, "peer", json_peer);
+ json_object_array_add(json_paths, json_path);
+ }
+}
+
+#define BGP_SHOW_HEADER_CSV "Flags, Network, Next Hop, Metric, LocPrf, Weight, Path"
+#define BGP_SHOW_DAMP_HEADER " Network From Reuse Path\n"
+#define BGP_SHOW_FLAP_HEADER " Network From Flaps Duration Reuse Path\n"
+
+static int bgp_show_regexp(struct vty *vty, struct bgp *bgp, const char *regstr,
+ afi_t afi, safi_t safi, enum bgp_show_type type,
+ bool use_json);
+static int bgp_show_community(struct vty *vty, struct bgp *bgp,
+ const char *comstr, int exact, afi_t afi,
+ safi_t safi, uint16_t show_flags);
+
+static int bgp_show_table(struct vty *vty, struct bgp *bgp, afi_t afi, safi_t safi,
+ struct bgp_table *table, enum bgp_show_type type,
+ void *output_arg, const char *rd, int is_last,
+ unsigned long *output_cum, unsigned long *total_cum,
+ unsigned long *json_header_depth, uint16_t show_flags,
+ enum rpki_states rpki_target_state)
+{
+ struct bgp_path_info *pi;
+ struct bgp_dest *dest;
+ bool header = true;
+ bool json_detail_header = false;
+ int display;
+ unsigned long output_count = 0;
+ unsigned long total_count = 0;
+ struct prefix *p;
+ json_object *json_paths = NULL;
+ int first = 1;
+ bool use_json = CHECK_FLAG(show_flags, BGP_SHOW_OPT_JSON);
+ bool wide = CHECK_FLAG(show_flags, BGP_SHOW_OPT_WIDE);
+ bool all = CHECK_FLAG(show_flags, BGP_SHOW_OPT_AFI_ALL);
+ bool detail_json = CHECK_FLAG(show_flags, BGP_SHOW_OPT_JSON_DETAIL);
+ bool detail_routes = CHECK_FLAG(show_flags, BGP_SHOW_OPT_ROUTES_DETAIL);
+
+ if (output_cum && *output_cum != 0)
+ header = false;
+
+ if (use_json && !*json_header_depth) {
+ if (all)
+ *json_header_depth = 1;
+ else {
+ vty_out(vty, "{\n");
+ *json_header_depth = 2;
+ }
+ vty_out(vty,
+ " \"vrfId\": %d,\n \"vrfName\": \"%s\",\n \"tableVersion\": %" PRId64
+ ",\n \"routerId\": \"%pI4\",\n \"defaultLocPrf\": %u,\n"
+ " \"localAS\": ",
+ bgp->vrf_id == VRF_UNKNOWN ? -1 : (int)bgp->vrf_id,
+ bgp->inst_type == BGP_INSTANCE_TYPE_DEFAULT
+ ? VRF_DEFAULT_NAME
+ : bgp->name,
+ table->version, &bgp->router_id,
+ bgp->default_local_pref);
+ if ((bgp->asnotation == ASNOTATION_PLAIN) ||
+ ((bgp->asnotation == ASNOTATION_DOT) &&
+ (bgp->as < UINT16_MAX)))
+ vty_out(vty, "%u", bgp->as);
+ else {
+ vty_out(vty, "\"");
+ vty_out(vty, ASN_FORMAT(bgp->asnotation), &bgp->as);
+ vty_out(vty, "\"");
+ }
+ vty_out(vty, ",\n \"routes\": { ");
+ if (rd) {
+ vty_out(vty, " \"routeDistinguishers\" : {");
+ ++*json_header_depth;
+ }
+ }
+
+ if (use_json && rd) {
+ vty_out(vty, " \"%s\" : { ", rd);
+ }
+
+ /* Check for 'json detail', where we need header output once per dest */
+ if (use_json && detail_json && type != bgp_show_type_dampend_paths &&
+ type != bgp_show_type_damp_neighbor &&
+ type != bgp_show_type_flap_statistics &&
+ type != bgp_show_type_flap_neighbor)
+ json_detail_header = true;
+
+ /* Start processing of routes. */
+ for (dest = bgp_table_top(table); dest; dest = bgp_route_next(dest)) {
+ const struct prefix *dest_p = bgp_dest_get_prefix(dest);
+ enum rpki_states rpki_curr_state = RPKI_NOT_BEING_USED;
+ bool json_detail_header_used = false;
+
+ pi = bgp_dest_get_bgp_path_info(dest);
+ if (pi == NULL)
+ continue;
+
+ display = 0;
+ if (use_json)
+ json_paths = json_object_new_array();
+ else
+ json_paths = NULL;
+
+ for (; pi; pi = pi->next) {
+ struct community *picomm = NULL;
+
+ picomm = bgp_attr_get_community(pi->attr);
+
+ total_count++;
+
+ if (type == bgp_show_type_prefix_version) {
+ uint32_t version =
+ strtoul(output_arg, NULL, 10);
+ if (dest->version < version)
+ continue;
+ }
+
+ if (type == bgp_show_type_community_alias) {
+ char *alias = output_arg;
+ char **communities;
+ int num;
+ bool found = false;
+
+ if (picomm) {
+ frrstr_split(picomm->str, " ",
+ &communities, &num);
+ for (int i = 0; i < num; i++) {
+ const char *com2alias =
+ bgp_community2alias(
+ communities[i]);
+ if (!found
+ && strcmp(alias, com2alias)
+ == 0)
+ found = true;
+ XFREE(MTYPE_TMP,
+ communities[i]);
+ }
+ XFREE(MTYPE_TMP, communities);
+ }
+
+ if (!found &&
+ bgp_attr_get_lcommunity(pi->attr)) {
+ frrstr_split(bgp_attr_get_lcommunity(
+ pi->attr)
+ ->str,
+ " ", &communities, &num);
+ for (int i = 0; i < num; i++) {
+ const char *com2alias =
+ bgp_community2alias(
+ communities[i]);
+ if (!found
+ && strcmp(alias, com2alias)
+ == 0)
+ found = true;
+ XFREE(MTYPE_TMP,
+ communities[i]);
+ }
+ XFREE(MTYPE_TMP, communities);
+ }
+
+ if (!found)
+ continue;
+ }
+
+ if (type == bgp_show_type_rpki) {
+ if (dest_p->family == AF_INET
+ || dest_p->family == AF_INET6)
+ rpki_curr_state = hook_call(
+ bgp_rpki_prefix_status,
+ pi->peer, pi->attr, dest_p);
+ if (rpki_target_state != RPKI_NOT_BEING_USED
+ && rpki_curr_state != rpki_target_state)
+ continue;
+ }
+
+ if (type == bgp_show_type_flap_statistics
+ || type == bgp_show_type_flap_neighbor
+ || type == bgp_show_type_dampend_paths
+ || type == bgp_show_type_damp_neighbor) {
+ if (!(pi->extra && pi->extra->damp_info))
+ continue;
+ }
+ if (type == bgp_show_type_regexp) {
+ regex_t *regex = output_arg;
+
+ if (bgp_regexec(regex, pi->attr->aspath)
+ == REG_NOMATCH)
+ continue;
+ }
+ if (type == bgp_show_type_prefix_list) {
+ struct prefix_list *plist = output_arg;
+
+ if (prefix_list_apply(plist, dest_p)
+ != PREFIX_PERMIT)
+ continue;
+ }
+ if (type == bgp_show_type_access_list) {
+ struct access_list *alist = output_arg;
+
+ if (access_list_apply(alist, dest_p) !=
+ FILTER_PERMIT)
+ continue;
+ }
+ if (type == bgp_show_type_filter_list) {
+ struct as_list *as_list = output_arg;
+
+ if (as_list_apply(as_list, pi->attr->aspath)
+ != AS_FILTER_PERMIT)
+ continue;
+ }
+ if (type == bgp_show_type_route_map) {
+ struct route_map *rmap = output_arg;
+ struct bgp_path_info path;
+ struct bgp_path_info_extra extra;
+ struct attr dummy_attr = {};
+ route_map_result_t ret;
+
+ dummy_attr = *pi->attr;
+
+ prep_for_rmap_apply(&path, &extra, dest, pi,
+ pi->peer, &dummy_attr);
+
+ ret = route_map_apply(rmap, dest_p, &path);
+ bgp_attr_flush(&dummy_attr);
+ if (ret == RMAP_DENYMATCH)
+ continue;
+ }
+ if (type == bgp_show_type_neighbor
+ || type == bgp_show_type_flap_neighbor
+ || type == bgp_show_type_damp_neighbor) {
+ union sockunion *su = output_arg;
+
+ if (pi->peer == NULL
+ || pi->peer->su_remote == NULL
+ || !sockunion_same(pi->peer->su_remote, su))
+ continue;
+ }
+ if (type == bgp_show_type_cidr_only) {
+ uint32_t destination;
+
+ destination = ntohl(dest_p->u.prefix4.s_addr);
+ if (IN_CLASSC(destination)
+ && dest_p->prefixlen == 24)
+ continue;
+ if (IN_CLASSB(destination)
+ && dest_p->prefixlen == 16)
+ continue;
+ if (IN_CLASSA(destination)
+ && dest_p->prefixlen == 8)
+ continue;
+ }
+ if (type == bgp_show_type_prefix_longer) {
+ p = output_arg;
+ if (!prefix_match(p, dest_p))
+ continue;
+ }
+ if (type == bgp_show_type_community_all) {
+ if (!picomm)
+ continue;
+ }
+ if (type == bgp_show_type_community) {
+ struct community *com = output_arg;
+
+ if (!picomm || !community_match(picomm, com))
+ continue;
+ }
+ if (type == bgp_show_type_community_exact) {
+ struct community *com = output_arg;
+
+ if (!picomm || !community_cmp(picomm, com))
+ continue;
+ }
+ if (type == bgp_show_type_community_list) {
+ struct community_list *list = output_arg;
+
+ if (!community_list_match(picomm, list))
+ continue;
+ }
+ if (type == bgp_show_type_community_list_exact) {
+ struct community_list *list = output_arg;
+
+ if (!community_list_exact_match(picomm, list))
+ continue;
+ }
+ if (type == bgp_show_type_lcommunity) {
+ struct lcommunity *lcom = output_arg;
+
+ if (!bgp_attr_get_lcommunity(pi->attr) ||
+ !lcommunity_match(
+ bgp_attr_get_lcommunity(pi->attr),
+ lcom))
+ continue;
+ }
+
+ if (type == bgp_show_type_lcommunity_exact) {
+ struct lcommunity *lcom = output_arg;
+
+ if (!bgp_attr_get_lcommunity(pi->attr) ||
+ !lcommunity_cmp(
+ bgp_attr_get_lcommunity(pi->attr),
+ lcom))
+ continue;
+ }
+ if (type == bgp_show_type_lcommunity_list) {
+ struct community_list *list = output_arg;
+
+ if (!lcommunity_list_match(
+ bgp_attr_get_lcommunity(pi->attr),
+ list))
+ continue;
+ }
+ if (type
+ == bgp_show_type_lcommunity_list_exact) {
+ struct community_list *list = output_arg;
+
+ if (!lcommunity_list_exact_match(
+ bgp_attr_get_lcommunity(pi->attr),
+ list))
+ continue;
+ }
+ if (type == bgp_show_type_lcommunity_all) {
+ if (!bgp_attr_get_lcommunity(pi->attr))
+ continue;
+ }
+ if (type == bgp_show_type_dampend_paths
+ || type == bgp_show_type_damp_neighbor) {
+ if (!CHECK_FLAG(pi->flags, BGP_PATH_DAMPED)
+ || CHECK_FLAG(pi->flags, BGP_PATH_HISTORY))
+ continue;
+ }
+ if (type == bgp_show_type_self_originated) {
+ if (pi->peer != bgp->peer_self)
+ continue;
+ }
+
+ if (!use_json && header) {
+ vty_out(vty,
+ "BGP table version is %" PRIu64
+ ", local router ID is %pI4, vrf id ",
+ table->version, &bgp->router_id);
+ if (bgp->vrf_id == VRF_UNKNOWN)
+ vty_out(vty, "%s", VRFID_NONE_STR);
+ else
+ vty_out(vty, "%u", bgp->vrf_id);
+ vty_out(vty, "\n");
+ vty_out(vty, "Default local pref %u, ",
+ bgp->default_local_pref);
+ vty_out(vty, "local AS ");
+ vty_out(vty, ASN_FORMAT(bgp->asnotation),
+ &bgp->as);
+ vty_out(vty, "\n");
+ if (!detail_routes) {
+ vty_out(vty, BGP_SHOW_SCODE_HEADER);
+ vty_out(vty, BGP_SHOW_NCODE_HEADER);
+ vty_out(vty, BGP_SHOW_OCODE_HEADER);
+ vty_out(vty, BGP_SHOW_RPKI_HEADER);
+ }
+ if (type == bgp_show_type_dampend_paths
+ || type == bgp_show_type_damp_neighbor)
+ vty_out(vty, BGP_SHOW_DAMP_HEADER);
+ else if (type == bgp_show_type_flap_statistics
+ || type == bgp_show_type_flap_neighbor)
+ vty_out(vty, BGP_SHOW_FLAP_HEADER);
+ else if (!detail_routes)
+ vty_out(vty, (wide ? BGP_SHOW_HEADER_WIDE
+ : BGP_SHOW_HEADER));
+ header = false;
+
+ }
+ if (rd != NULL && !display && !output_count) {
+ if (!use_json)
+ vty_out(vty,
+ "Route Distinguisher: %s\n",
+ rd);
+ }
+ if (type == bgp_show_type_dampend_paths
+ || type == bgp_show_type_damp_neighbor)
+ damp_route_vty_out(vty, dest_p, pi, display,
+ afi, safi, use_json,
+ json_paths);
+ else if (type == bgp_show_type_flap_statistics
+ || type == bgp_show_type_flap_neighbor)
+ flap_route_vty_out(vty, dest_p, pi, display,
+ afi, safi, use_json,
+ json_paths);
+ else {
+ if (detail_routes || detail_json) {
+ const struct prefix_rd *prd = NULL;
+
+ if (dest->pdest)
+ prd = bgp_rd_from_dest(
+ dest->pdest, safi);
+
+ if (!use_json)
+ route_vty_out_detail_header(
+ vty, bgp, dest,
+ bgp_dest_get_prefix(
+ dest),
+ prd, table->afi, safi,
+ NULL, false);
+
+ route_vty_out_detail(
+ vty, bgp, dest, dest_p, pi,
+ family2afi(dest_p->family),
+ safi, RPKI_NOT_BEING_USED,
+ json_paths);
+ } else {
+ route_vty_out(vty, dest_p, pi, display,
+ safi, json_paths, wide);
+ }
+ }
+ display++;
+ }
+
+ if (display) {
+ output_count++;
+ if (!use_json)
+ continue;
+
+ /* encode prefix */
+ if (dest_p->family == AF_FLOWSPEC) {
+ char retstr[BGP_FLOWSPEC_STRING_DISPLAY_MAX];
+
+
+ bgp_fs_nlri_get_string(
+ (unsigned char *)
+ dest_p->u.prefix_flowspec.ptr,
+ dest_p->u.prefix_flowspec.prefixlen,
+ retstr, NLRI_STRING_FORMAT_MIN, NULL,
+ family2afi(dest_p->u
+ .prefix_flowspec.family));
+ if (first)
+ vty_out(vty, "\"%s/%d\": ", retstr,
+ dest_p->u.prefix_flowspec
+ .prefixlen);
+ else
+ vty_out(vty, ",\"%s/%d\": ", retstr,
+ dest_p->u.prefix_flowspec
+ .prefixlen);
+ } else {
+ if (first)
+ vty_out(vty, "\"%pFX\": ", dest_p);
+ else
+ vty_out(vty, ",\"%pFX\": ", dest_p);
+ }
+
+ /* This is used for 'json detail' vty keywords.
+ *
+ * In plain 'json' the per-prefix header is encoded
+ * as a standalone dictionary in the first json_paths
+ * array element:
+ * "<prefix>": [{header}, {path-1}, {path-N}]
+ * (which is confusing and borderline broken)
+ *
+ * For 'json detail' this changes the value
+ * of each prefix-key to be a dictionary where each
+ * header item has its own key, and json_paths is
+ * tucked under the "paths" key:
+ * "<prefix>": {
+ * "<header-key-1>": <header-val-1>,
+ * "<header-key-N>": <header-val-N>,
+ * "paths": [{path-1}, {path-N}]
+ * }
+ */
+ if (json_detail_header && json_paths != NULL) {
+ const struct prefix_rd *prd;
+
+ /* Start per-prefix dictionary */
+ vty_out(vty, "{\n");
+
+ prd = bgp_rd_from_dest(dest, safi);
+
+ route_vty_out_detail_header(
+ vty, bgp, dest,
+ bgp_dest_get_prefix(dest), prd,
+ table->afi, safi, json_paths, true);
+
+ vty_out(vty, "\"paths\": ");
+ json_detail_header_used = true;
+ }
+
+ /*
+ * We are using no_pretty here because under
+ * extremely high settings( say lots and lots of
+ * routes with lots and lots of ways to reach
+ * that route via different paths ) this can
+ * save several minutes of output when FRR
+ * is run on older cpu's or more underperforming
+ * routers out there
+ */
+ vty_json_no_pretty(vty, json_paths);
+
+ /* End per-prefix dictionary */
+ if (json_detail_header_used)
+ vty_out(vty, "} ");
+
+ json_paths = NULL;
+ first = 0;
+ } else
+ json_object_free(json_paths);
+ }
+
+ if (output_cum) {
+ output_count += *output_cum;
+ *output_cum = output_count;
+ }
+ if (total_cum) {
+ total_count += *total_cum;
+ *total_cum = total_count;
+ }
+ if (use_json) {
+ if (rd) {
+ vty_out(vty, " }%s ", (is_last ? "" : ","));
+ }
+ if (is_last) {
+ unsigned long i;
+ for (i = 0; i < *json_header_depth; ++i)
+ vty_out(vty, " } ");
+ if (!all)
+ vty_out(vty, "\n");
+ }
+ } else {
+ if (is_last) {
+ /* No route is displayed */
+ if (output_count == 0) {
+ if (type == bgp_show_type_normal)
+ vty_out(vty,
+ "No BGP prefixes displayed, %ld exist\n",
+ total_count);
+ } else
+ vty_out(vty,
+ "\nDisplayed %ld routes and %ld total paths\n",
+ output_count, total_count);
+ }
+ }
+
+ return CMD_SUCCESS;
+}
+
+int bgp_show_table_rd(struct vty *vty, struct bgp *bgp, afi_t afi, safi_t safi,
+ struct bgp_table *table, struct prefix_rd *prd_match,
+ enum bgp_show_type type, void *output_arg,
+ uint16_t show_flags)
+{
+ struct bgp_dest *dest, *next;
+ unsigned long output_cum = 0;
+ unsigned long total_cum = 0;
+ unsigned long json_header_depth = 0;
+ struct bgp_table *itable;
+ bool show_msg;
+ bool use_json = !!CHECK_FLAG(show_flags, BGP_SHOW_OPT_JSON);
+
+ show_msg = (!use_json && type == bgp_show_type_normal);
+
+ for (dest = bgp_table_top(table); dest; dest = next) {
+ const struct prefix *dest_p = bgp_dest_get_prefix(dest);
+
+ next = bgp_route_next(dest);
+ if (prd_match && memcmp(dest_p->u.val, prd_match->val, 8) != 0)
+ continue;
+
+ itable = bgp_dest_get_bgp_table_info(dest);
+ if (itable != NULL) {
+ struct prefix_rd prd;
+ char rd[RD_ADDRSTRLEN];
+
+ memcpy(&prd, dest_p, sizeof(struct prefix_rd));
+ prefix_rd2str(&prd, rd, sizeof(rd), bgp->asnotation);
+ bgp_show_table(vty, bgp, afi, safi, itable, type, output_arg,
+ rd, next == NULL, &output_cum,
+ &total_cum, &json_header_depth,
+ show_flags, RPKI_NOT_BEING_USED);
+ if (next == NULL)
+ show_msg = false;
+ }
+ }
+ if (show_msg) {
+ if (output_cum == 0)
+ vty_out(vty, "No BGP prefixes displayed, %ld exist\n",
+ total_cum);
+ else
+ vty_out(vty,
+ "\nDisplayed %ld routes and %ld total paths\n",
+ output_cum, total_cum);
+ } else {
+ if (use_json && output_cum == 0 && json_header_depth == 0)
+ vty_out(vty, "{}\n");
+ }
+ return CMD_SUCCESS;
+}
+
+static int bgp_show(struct vty *vty, struct bgp *bgp, afi_t afi, safi_t safi,
+ enum bgp_show_type type, void *output_arg,
+ uint16_t show_flags, enum rpki_states rpki_target_state)
+{
+ struct bgp_table *table;
+ unsigned long json_header_depth = 0;
+ bool use_json = CHECK_FLAG(show_flags, BGP_SHOW_OPT_JSON);
+
+ if (bgp == NULL) {
+ bgp = bgp_get_default();
+ }
+
+ if (bgp == NULL) {
+ if (!use_json)
+ vty_out(vty, "No BGP process is configured\n");
+ else
+ vty_out(vty, "{}\n");
+ return CMD_WARNING;
+ }
+
+ /* Labeled-unicast routes live in the unicast table. */
+ if (safi == SAFI_LABELED_UNICAST)
+ safi = SAFI_UNICAST;
+
+ table = bgp->rib[afi][safi];
+ /* use MPLS and ENCAP specific shows until they are merged */
+ if (safi == SAFI_MPLS_VPN) {
+ return bgp_show_table_rd(vty, bgp, afi, safi, table, NULL, type,
+ output_arg, show_flags);
+ }
+
+ if (safi == SAFI_FLOWSPEC && type == bgp_show_type_detail) {
+ return bgp_show_table_flowspec(vty, bgp, afi, table, type,
+ output_arg, use_json,
+ 1, NULL, NULL);
+ }
+
+ if (safi == SAFI_EVPN)
+ return bgp_evpn_show_all_routes(vty, bgp, type, use_json, 0);
+
+ return bgp_show_table(vty, bgp, afi, safi, table, type, output_arg, NULL, 1,
+ NULL, NULL, &json_header_depth, show_flags,
+ rpki_target_state);
+}
+
+static void bgp_show_all_instances_routes_vty(struct vty *vty, afi_t afi,
+ safi_t safi, uint16_t show_flags)
+{
+ struct listnode *node, *nnode;
+ struct bgp *bgp;
+ int is_first = 1;
+ bool route_output = false;
+ bool use_json = CHECK_FLAG(show_flags, BGP_SHOW_OPT_JSON);
+
+ if (use_json)
+ vty_out(vty, "{\n");
+
+ for (ALL_LIST_ELEMENTS(bm->bgp, node, nnode, bgp)) {
+ route_output = true;
+ if (use_json) {
+ if (!is_first)
+ vty_out(vty, ",\n");
+ else
+ is_first = 0;
+
+ vty_out(vty, "\"%s\":",
+ (bgp->inst_type == BGP_INSTANCE_TYPE_DEFAULT)
+ ? VRF_DEFAULT_NAME
+ : bgp->name);
+ } else {
+ vty_out(vty, "\nInstance %s:\n",
+ (bgp->inst_type == BGP_INSTANCE_TYPE_DEFAULT)
+ ? VRF_DEFAULT_NAME
+ : bgp->name);
+ }
+ bgp_show(vty, bgp, afi, safi, bgp_show_type_normal, NULL,
+ show_flags, RPKI_NOT_BEING_USED);
+ }
+
+ if (use_json)
+ vty_out(vty, "}\n");
+ else if (!route_output)
+ vty_out(vty, "%% BGP instance not found\n");
+}
+
+/* Header of detailed BGP route information */
+void route_vty_out_detail_header(struct vty *vty, struct bgp *bgp,
+ struct bgp_dest *dest, const struct prefix *p,
+ const struct prefix_rd *prd, afi_t afi,
+ safi_t safi, json_object *json,
+ bool incremental_print)
+{
+ struct bgp_path_info *pi;
+ struct peer *peer;
+ struct listnode *node, *nnode;
+ char buf1[RD_ADDRSTRLEN];
+ int count = 0;
+ int best = 0;
+ int suppress = 0;
+ int accept_own = 0;
+ int route_filter_translated_v4 = 0;
+ int route_filter_v4 = 0;
+ int route_filter_translated_v6 = 0;
+ int route_filter_v6 = 0;
+ int llgr_stale = 0;
+ int no_llgr = 0;
+ int accept_own_nexthop = 0;
+ int blackhole = 0;
+ int no_export = 0;
+ int no_advertise = 0;
+ int local_as = 0;
+ int no_peer = 0;
+ int first = 1;
+ int has_valid_label = 0;
+ mpls_label_t label = 0;
+ json_object *json_adv_to = NULL;
+ uint32_t ttl = 0;
+ uint32_t bos = 0;
+ uint32_t exp = 0;
+
+ mpls_lse_decode(dest->local_label, &label, &ttl, &exp, &bos);
+
+ has_valid_label = bgp_is_valid_label(&label);
+
+ if (safi == SAFI_EVPN) {
+ if (!json) {
+ vty_out(vty, "BGP routing table entry for %s%s%pFX\n",
+ prd ? prefix_rd2str(prd, buf1, sizeof(buf1),
+ bgp->asnotation)
+ : "",
+ prd ? ":" : "", (struct prefix_evpn *)p);
+ } else {
+ json_object_string_add(
+ json, "rd",
+ prd ? prefix_rd2str(prd, buf1, sizeof(buf1),
+ bgp->asnotation)
+ : "");
+ bgp_evpn_route2json((struct prefix_evpn *)p, json);
+ }
+ } else {
+ if (!json) {
+ vty_out(vty,
+ "BGP routing table entry for %s%s%pFX, version %" PRIu64
+ "\n",
+ (((safi == SAFI_MPLS_VPN ||
+ safi == SAFI_ENCAP) &&
+ prd)
+ ? prefix_rd2str(prd, buf1,
+ sizeof(buf1),
+ bgp->asnotation)
+ : ""),
+ safi == SAFI_MPLS_VPN && prd ? ":" : "", p,
+ dest->version);
+
+ } else {
+ if (incremental_print) {
+ vty_out(vty, "\"prefix\": \"%pFX\",\n", p);
+ vty_out(vty, "\"version\": \"%" PRIu64 "\",\n",
+ dest->version);
+ } else {
+ json_object_string_addf(json, "prefix", "%pFX",
+ p);
+ json_object_int_add(json, "version",
+ dest->version);
+ }
+ }
+ }
+
+ if (has_valid_label) {
+ if (json) {
+ if (incremental_print)
+ vty_out(vty, "\"localLabel\": \"%u\",\n",
+ label);
+ else
+ json_object_int_add(json, "localLabel", label);
+ } else
+ vty_out(vty, "Local label: %d\n", label);
+ }
+
+ if (!json)
+ if (bgp_labeled_safi(safi) && safi != SAFI_EVPN)
+ vty_out(vty, "not allocated\n");
+
+ for (pi = bgp_dest_get_bgp_path_info(dest); pi; pi = pi->next) {
+ struct community *picomm = NULL;
+
+ picomm = bgp_attr_get_community(pi->attr);
+
+ count++;
+ if (CHECK_FLAG(pi->flags, BGP_PATH_SELECTED)) {
+ best = count;
+ if (bgp_path_suppressed(pi))
+ suppress = 1;
+
+ if (!picomm)
+ continue;
+
+ no_advertise += community_include(
+ picomm, COMMUNITY_NO_ADVERTISE);
+ no_export +=
+ community_include(picomm, COMMUNITY_NO_EXPORT);
+ local_as +=
+ community_include(picomm, COMMUNITY_LOCAL_AS);
+ accept_own +=
+ community_include(picomm, COMMUNITY_ACCEPT_OWN);
+ route_filter_translated_v4 += community_include(
+ picomm, COMMUNITY_ROUTE_FILTER_TRANSLATED_v4);
+ route_filter_translated_v6 += community_include(
+ picomm, COMMUNITY_ROUTE_FILTER_TRANSLATED_v6);
+ route_filter_v4 += community_include(
+ picomm, COMMUNITY_ROUTE_FILTER_v4);
+ route_filter_v6 += community_include(
+ picomm, COMMUNITY_ROUTE_FILTER_v6);
+ llgr_stale +=
+ community_include(picomm, COMMUNITY_LLGR_STALE);
+ no_llgr += community_include(picomm, COMMUNITY_NO_LLGR);
+ accept_own_nexthop += community_include(
+ picomm, COMMUNITY_ACCEPT_OWN_NEXTHOP);
+ blackhole +=
+ community_include(picomm, COMMUNITY_BLACKHOLE);
+ no_peer += community_include(picomm, COMMUNITY_NO_PEER);
+ }
+ }
+
+ if (!json) {
+ vty_out(vty, "Paths: (%d available", count);
+ if (best) {
+ vty_out(vty, ", best #%d", best);
+ if (safi == SAFI_UNICAST) {
+ if (bgp->inst_type == BGP_INSTANCE_TYPE_DEFAULT)
+ vty_out(vty, ", table %s",
+ VRF_DEFAULT_NAME);
+ else
+ vty_out(vty, ", vrf %s",
+ bgp->name);
+ }
+ } else
+ vty_out(vty, ", no best path");
+
+ if (accept_own)
+ vty_out(vty,
+ ", accept own local route exported and imported in different VRF");
+ else if (route_filter_translated_v4)
+ vty_out(vty,
+ ", mark translated RTs for VPNv4 route filtering");
+ else if (route_filter_v4)
+ vty_out(vty,
+ ", attach RT as-is for VPNv4 route filtering");
+ else if (route_filter_translated_v6)
+ vty_out(vty,
+ ", mark translated RTs for VPNv6 route filtering");
+ else if (route_filter_v6)
+ vty_out(vty,
+ ", attach RT as-is for VPNv6 route filtering");
+ else if (llgr_stale)
+ vty_out(vty,
+ ", mark routes to be retained for a longer time. Requires support for Long-lived BGP Graceful Restart");
+ else if (no_llgr)
+ vty_out(vty,
+ ", mark routes to not be treated according to Long-lived BGP Graceful Restart operations");
+ else if (accept_own_nexthop)
+ vty_out(vty,
+ ", accept local nexthop");
+ else if (blackhole)
+ vty_out(vty, ", inform peer to blackhole prefix");
+ else if (no_export)
+ vty_out(vty, ", not advertised to EBGP peer");
+ else if (no_advertise)
+ vty_out(vty, ", not advertised to any peer");
+ else if (local_as)
+ vty_out(vty, ", not advertised outside local AS");
+ else if (no_peer)
+ vty_out(vty,
+ ", inform EBGP peer not to advertise to their EBGP peers");
+
+ if (suppress)
+ vty_out(vty,
+ ", Advertisements suppressed by an aggregate.");
+ vty_out(vty, ")\n");
+ }
+
+ /* If we are not using addpath then we can display Advertised to and
+ * that will
+ * 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_is_addpath_used(&bgp->tx_addpath, afi, safi)) {
+ for (ALL_LIST_ELEMENTS(bgp->peer, node, nnode, peer)) {
+ if (bgp_adj_out_lookup(peer, dest, 0)) {
+ if (json && !json_adv_to)
+ json_adv_to = json_object_new_object();
+
+ route_vty_out_advertised_to(
+ vty, peer, &first,
+ " Advertised to non peer-group peers:\n ",
+ json_adv_to);
+ }
+ }
+
+ if (json && json_adv_to) {
+ if (incremental_print) {
+ vty_out(vty, "\"advertisedTo\": ");
+ vty_json(vty, json_adv_to);
+ vty_out(vty, ",");
+ } else
+ json_object_object_add(json, "advertisedTo",
+ json_adv_to);
+ } else {
+ if (!json && first)
+ vty_out(vty, " Not advertised to any peer");
+ vty_out(vty, "\n");
+ }
+ }
+}
+
+static void bgp_show_path_info(const struct prefix_rd *pfx_rd,
+ struct bgp_dest *bgp_node, struct vty *vty,
+ struct bgp *bgp, afi_t afi, safi_t safi,
+ json_object *json, enum bgp_path_type pathtype,
+ int *display, enum rpki_states rpki_target_state)
+{
+ struct bgp_path_info *pi;
+ int header = 1;
+ json_object *json_header = NULL;
+ json_object *json_paths = NULL;
+ const struct prefix *p = bgp_dest_get_prefix(bgp_node);
+
+ for (pi = bgp_dest_get_bgp_path_info(bgp_node); pi; pi = pi->next) {
+ enum rpki_states rpki_curr_state = RPKI_NOT_BEING_USED;
+
+ if (p->family == AF_INET || p->family == AF_INET6)
+ rpki_curr_state = hook_call(bgp_rpki_prefix_status,
+ pi->peer, pi->attr, p);
+
+ if (rpki_target_state != RPKI_NOT_BEING_USED
+ && rpki_curr_state != rpki_target_state)
+ continue;
+
+ if (json && !json_paths) {
+ /* Instantiate json_paths only if path is valid */
+ json_paths = json_object_new_array();
+ if (pfx_rd)
+ json_header = json_object_new_object();
+ else
+ json_header = json;
+ }
+
+ if (header) {
+ route_vty_out_detail_header(
+ vty, bgp, bgp_node,
+ bgp_dest_get_prefix(bgp_node), pfx_rd, AFI_IP,
+ safi, json_header, false);
+ header = 0;
+ }
+ (*display)++;
+
+ if (pathtype == BGP_PATH_SHOW_ALL
+ || (pathtype == BGP_PATH_SHOW_BESTPATH
+ && CHECK_FLAG(pi->flags, BGP_PATH_SELECTED))
+ || (pathtype == BGP_PATH_SHOW_MULTIPATH
+ && (CHECK_FLAG(pi->flags, BGP_PATH_MULTIPATH)
+ || CHECK_FLAG(pi->flags, BGP_PATH_SELECTED))))
+ route_vty_out_detail(vty, bgp, bgp_node,
+ bgp_dest_get_prefix(bgp_node), pi,
+ afi, safi, rpki_curr_state,
+ json_paths);
+ }
+
+ if (json && json_paths) {
+ json_object_object_add(json_header, "paths", json_paths);
+
+ if (pfx_rd)
+ json_object_object_addf(
+ json, json_header,
+ BGP_RD_AS_FORMAT(bgp->asnotation), pfx_rd);
+ }
+}
+
+/*
+ * Return rd based on safi
+ */
+const struct prefix_rd *bgp_rd_from_dest(const struct bgp_dest *dest,
+ safi_t safi)
+{
+ switch (safi) {
+ case SAFI_MPLS_VPN:
+ case SAFI_ENCAP:
+ case SAFI_EVPN:
+ return (struct prefix_rd *)(bgp_dest_get_prefix(dest));
+ case SAFI_UNSPEC:
+ case SAFI_UNICAST:
+ case SAFI_MULTICAST:
+ case SAFI_LABELED_UNICAST:
+ case SAFI_FLOWSPEC:
+ case SAFI_MAX:
+ return NULL;
+ }
+
+ assert(!"Reached end of function when we were not expecting it");
+}
+
+/* Display specified route of BGP table. */
+static int bgp_show_route_in_table(struct vty *vty, struct bgp *bgp,
+ struct bgp_table *rib, const char *ip_str,
+ afi_t afi, safi_t safi,
+ enum rpki_states rpki_target_state,
+ struct prefix_rd *prd, int prefix_check,
+ enum bgp_path_type pathtype, bool use_json)
+{
+ int ret;
+ int display = 0;
+ struct prefix match;
+ struct bgp_dest *dest;
+ struct bgp_dest *rm;
+ struct bgp_table *table;
+ json_object *json = NULL;
+ json_object *json_paths = NULL;
+
+ /* Check IP address argument. */
+ ret = str2prefix(ip_str, &match);
+ if (!ret) {
+ vty_out(vty, "address is malformed\n");
+ return CMD_WARNING;
+ }
+
+ match.family = afi2family(afi);
+
+ if (use_json)
+ json = json_object_new_object();
+
+ if (safi == SAFI_MPLS_VPN || safi == SAFI_ENCAP) {
+ for (dest = bgp_table_top(rib); dest;
+ dest = bgp_route_next(dest)) {
+ const struct prefix *dest_p = bgp_dest_get_prefix(dest);
+
+ if (prd && memcmp(dest_p->u.val, prd->val, 8) != 0)
+ continue;
+ table = bgp_dest_get_bgp_table_info(dest);
+ if (!table)
+ continue;
+
+ rm = bgp_node_match(table, &match);
+ if (rm == NULL)
+ continue;
+
+ const struct prefix *rm_p = bgp_dest_get_prefix(rm);
+ if (prefix_check
+ && rm_p->prefixlen != match.prefixlen) {
+ bgp_dest_unlock_node(rm);
+ continue;
+ }
+
+ bgp_show_path_info((struct prefix_rd *)dest_p, rm, vty,
+ bgp, afi, safi, json, pathtype,
+ &display, rpki_target_state);
+
+ bgp_dest_unlock_node(rm);
+ }
+ } else if (safi == SAFI_EVPN) {
+ struct bgp_dest *longest_pfx;
+ bool is_exact_pfxlen_match = false;
+
+ for (dest = bgp_table_top(rib); dest;
+ dest = bgp_route_next(dest)) {
+ const struct prefix *dest_p = bgp_dest_get_prefix(dest);
+
+ if (prd && memcmp(&dest_p->u.val, prd->val, 8) != 0)
+ continue;
+ table = bgp_dest_get_bgp_table_info(dest);
+ if (!table)
+ continue;
+
+ longest_pfx = NULL;
+ is_exact_pfxlen_match = false;
+ /*
+ * Search through all the prefixes for a match. The
+ * pfx's are enumerated in ascending order of pfxlens.
+ * So, the last pfx match is the longest match. Set
+ * is_exact_pfxlen_match when we get exact pfxlen match
+ */
+ for (rm = bgp_table_top(table); rm;
+ rm = bgp_route_next(rm)) {
+ const struct prefix *rm_p =
+ bgp_dest_get_prefix(rm);
+ /*
+ * Get prefixlen of the ip-prefix within type5
+ * evpn route
+ */
+ if (evpn_type5_prefix_match(rm_p, &match)
+ && rm->info) {
+ longest_pfx = rm;
+ int type5_pfxlen =
+ bgp_evpn_get_type5_prefixlen(
+ rm_p);
+ if (type5_pfxlen == match.prefixlen) {
+ is_exact_pfxlen_match = true;
+ rm = bgp_dest_unlock_node(rm);
+
+ assert(rm);
+ break;
+ }
+ }
+ }
+
+ if (!longest_pfx)
+ continue;
+
+ if (prefix_check && !is_exact_pfxlen_match)
+ continue;
+
+ rm = longest_pfx;
+ bgp_dest_lock_node(rm);
+
+ bgp_show_path_info((struct prefix_rd *)dest_p, rm, vty,
+ bgp, afi, safi, json, pathtype,
+ &display, rpki_target_state);
+
+ bgp_dest_unlock_node(rm);
+ }
+ } else if (safi == SAFI_FLOWSPEC) {
+ if (use_json)
+ json_paths = json_object_new_array();
+
+ display = bgp_flowspec_display_match_per_ip(afi, rib,
+ &match, prefix_check,
+ vty,
+ use_json,
+ json_paths);
+ if (use_json) {
+ if (display)
+ json_object_object_add(json, "paths",
+ json_paths);
+ else
+ json_object_free(json_paths);
+ }
+ } else {
+ dest = bgp_node_match(rib, &match);
+ if (dest != NULL) {
+ const struct prefix *dest_p = bgp_dest_get_prefix(dest);
+ if (!prefix_check
+ || dest_p->prefixlen == match.prefixlen) {
+ bgp_show_path_info(NULL, dest, vty, bgp, afi,
+ safi, json, pathtype,
+ &display, rpki_target_state);
+ }
+
+ bgp_dest_unlock_node(dest);
+ }
+ }
+
+ if (use_json) {
+ vty_json(vty, json);
+ } else {
+ if (!display) {
+ vty_out(vty, "%% Network not in table\n");
+ return CMD_WARNING;
+ }
+ }
+
+ return CMD_SUCCESS;
+}
+
+/* Display specified route of Main RIB */
+static int bgp_show_route(struct vty *vty, struct bgp *bgp, const char *ip_str,
+ afi_t afi, safi_t safi, struct prefix_rd *prd,
+ int prefix_check, enum bgp_path_type pathtype,
+ enum rpki_states rpki_target_state, bool use_json)
+{
+ if (!bgp) {
+ bgp = bgp_get_default();
+ if (!bgp) {
+ if (!use_json)
+ vty_out(vty, "No BGP process is configured\n");
+ else
+ vty_out(vty, "{}\n");
+ return CMD_WARNING;
+ }
+ }
+
+ /* labeled-unicast routes live in the unicast table */
+ if (safi == SAFI_LABELED_UNICAST)
+ safi = SAFI_UNICAST;
+
+ return bgp_show_route_in_table(vty, bgp, bgp->rib[afi][safi], ip_str,
+ afi, safi, rpki_target_state, prd,
+ prefix_check, pathtype, use_json);
+}
+
+static int bgp_show_lcommunity(struct vty *vty, struct bgp *bgp, int argc,
+ struct cmd_token **argv, bool exact, afi_t afi,
+ safi_t safi, bool uj)
+{
+ struct lcommunity *lcom;
+ struct buffer *b;
+ int i;
+ char *str;
+ int first = 0;
+ uint16_t show_flags = 0;
+ int ret;
+
+ if (uj)
+ SET_FLAG(show_flags, BGP_SHOW_OPT_JSON);
+
+ b = buffer_new(1024);
+ for (i = 0; i < argc; i++) {
+ if (first)
+ buffer_putc(b, ' ');
+ else {
+ if (strmatch(argv[i]->text, "AA:BB:CC")) {
+ first = 1;
+ buffer_putstr(b, argv[i]->arg);
+ }
+ }
+ }
+ buffer_putc(b, '\0');
+
+ str = buffer_getstr(b);
+ buffer_free(b);
+
+ lcom = lcommunity_str2com(str);
+ XFREE(MTYPE_TMP, str);
+ if (!lcom) {
+ vty_out(vty, "%% Large-community malformed\n");
+ return CMD_WARNING;
+ }
+
+ ret = bgp_show(vty, bgp, afi, safi,
+ (exact ? bgp_show_type_lcommunity_exact
+ : bgp_show_type_lcommunity),
+ lcom, show_flags, RPKI_NOT_BEING_USED);
+
+ lcommunity_free(&lcom);
+ return ret;
+}
+
+static int bgp_show_lcommunity_list(struct vty *vty, struct bgp *bgp,
+ const char *lcom, bool exact, afi_t afi,
+ safi_t safi, bool uj)
+{
+ struct community_list *list;
+ uint16_t show_flags = 0;
+
+ if (uj)
+ SET_FLAG(show_flags, BGP_SHOW_OPT_JSON);
+
+
+ list = community_list_lookup(bgp_clist, lcom, 0,
+ LARGE_COMMUNITY_LIST_MASTER);
+ if (list == NULL) {
+ vty_out(vty, "%% %s is not a valid large-community-list name\n",
+ lcom);
+ return CMD_WARNING;
+ }
+
+ return bgp_show(vty, bgp, afi, safi,
+ (exact ? bgp_show_type_lcommunity_list_exact
+ : bgp_show_type_lcommunity_list),
+ list, show_flags, RPKI_NOT_BEING_USED);
+}
+
+DEFUN (show_ip_bgp_large_community_list,
+ show_ip_bgp_large_community_list_cmd,
+ "show [ip] bgp [<view|vrf> VIEWVRFNAME] ["BGP_AFI_CMD_STR" ["BGP_SAFI_WITH_LABEL_CMD_STR"]] large-community-list <(1-500)|LCOMMUNITY_LIST_NAME> [exact-match] [json]",
+ SHOW_STR
+ IP_STR
+ BGP_STR
+ BGP_INSTANCE_HELP_STR
+ BGP_AFI_HELP_STR
+ BGP_SAFI_WITH_LABEL_HELP_STR
+ "Display routes matching the large-community-list\n"
+ "large-community-list number\n"
+ "large-community-list name\n"
+ "Exact match of the large-communities\n"
+ JSON_STR)
+{
+ afi_t afi = AFI_IP6;
+ safi_t safi = SAFI_UNICAST;
+ int idx = 0;
+ bool exact_match = 0;
+ struct bgp *bgp = NULL;
+ bool uj = use_json(argc, argv);
+
+ if (uj)
+ argc--;
+
+ bgp_vty_find_and_parse_afi_safi_bgp(vty, argv, argc, &idx, &afi, &safi,
+ &bgp, uj);
+ if (!idx)
+ return CMD_WARNING;
+
+ argv_find(argv, argc, "large-community-list", &idx);
+
+ const char *clist_number_or_name = argv[++idx]->arg;
+
+ if (++idx < argc && strmatch(argv[idx]->text, "exact-match"))
+ exact_match = 1;
+
+ return bgp_show_lcommunity_list(vty, bgp, clist_number_or_name,
+ exact_match, afi, safi, uj);
+}
+DEFUN (show_ip_bgp_large_community,
+ show_ip_bgp_large_community_cmd,
+ "show [ip] bgp [<view|vrf> VIEWVRFNAME] ["BGP_AFI_CMD_STR" ["BGP_SAFI_WITH_LABEL_CMD_STR"]] large-community [<AA:BB:CC> [exact-match]] [json]",
+ SHOW_STR
+ IP_STR
+ BGP_STR
+ BGP_INSTANCE_HELP_STR
+ BGP_AFI_HELP_STR
+ BGP_SAFI_WITH_LABEL_HELP_STR
+ "Display routes matching the large-communities\n"
+ "List of large-community numbers\n"
+ "Exact match of the large-communities\n"
+ JSON_STR)
+{
+ afi_t afi = AFI_IP6;
+ safi_t safi = SAFI_UNICAST;
+ int idx = 0;
+ bool exact_match = 0;
+ struct bgp *bgp = NULL;
+ bool uj = use_json(argc, argv);
+ uint16_t show_flags = 0;
+
+ if (uj) {
+ argc--;
+ SET_FLAG(show_flags, BGP_SHOW_OPT_JSON);
+ }
+
+ bgp_vty_find_and_parse_afi_safi_bgp(vty, argv, argc, &idx, &afi, &safi,
+ &bgp, uj);
+ if (!idx)
+ return CMD_WARNING;
+
+ if (argv_find(argv, argc, "AA:BB:CC", &idx)) {
+ if (argv_find(argv, argc, "exact-match", &idx)) {
+ argc--;
+ exact_match = 1;
+ }
+ return bgp_show_lcommunity(vty, bgp, argc, argv,
+ exact_match, afi, safi, uj);
+ } else
+ return bgp_show(vty, bgp, afi, safi,
+ bgp_show_type_lcommunity_all, NULL, show_flags,
+ RPKI_NOT_BEING_USED);
+}
+
+static int bgp_table_stats_single(struct vty *vty, struct bgp *bgp, afi_t afi,
+ safi_t safi, struct json_object *json_array);
+static int bgp_table_stats(struct vty *vty, struct bgp *bgp, afi_t afi,
+ safi_t safi, struct json_object *json);
+
+
+DEFUN(show_ip_bgp_statistics_all, show_ip_bgp_statistics_all_cmd,
+ "show [ip] bgp [<view|vrf> VIEWVRFNAME] statistics-all [json]",
+ SHOW_STR IP_STR BGP_STR BGP_INSTANCE_HELP_STR
+ "Display number of prefixes for all afi/safi\n" JSON_STR)
+{
+ bool uj = use_json(argc, argv);
+ struct bgp *bgp = NULL;
+ safi_t safi = SAFI_UNICAST;
+ afi_t afi = AFI_IP6;
+ int idx = 0;
+ struct json_object *json_all = NULL;
+ struct json_object *json_afi_safi = NULL;
+
+ bgp_vty_find_and_parse_afi_safi_bgp(vty, argv, argc, &idx, &afi, &safi,
+ &bgp, false);
+ if (!idx)
+ return CMD_WARNING;
+
+ if (uj)
+ json_all = json_object_new_object();
+
+ FOREACH_AFI_SAFI (afi, safi) {
+ /*
+ * So limit output to those afi/safi pairs that
+ * actually have something interesting in them
+ */
+ if (strmatch(get_afi_safi_str(afi, safi, true),
+ "Unknown")) {
+ continue;
+ }
+ if (uj) {
+ json_afi_safi = json_object_new_array();
+ json_object_object_add(
+ json_all,
+ get_afi_safi_str(afi, safi, true),
+ json_afi_safi);
+ } else {
+ json_afi_safi = NULL;
+ }
+
+ bgp_table_stats(vty, bgp, afi, safi, json_afi_safi);
+ }
+
+ if (uj)
+ vty_json(vty, json_all);
+
+ return CMD_SUCCESS;
+}
+
+/* BGP route print out function without JSON */
+DEFUN (show_ip_bgp_l2vpn_evpn_statistics,
+ show_ip_bgp_l2vpn_evpn_statistics_cmd,
+ "show [ip] bgp [<view|vrf> VIEWVRFNAME] l2vpn evpn statistics [json]",
+ SHOW_STR
+ IP_STR
+ BGP_STR
+ BGP_INSTANCE_HELP_STR
+ L2VPN_HELP_STR
+ EVPN_HELP_STR
+ "BGP RIB advertisement statistics\n"
+ JSON_STR)
+{
+ afi_t afi = AFI_IP6;
+ safi_t safi = SAFI_UNICAST;
+ struct bgp *bgp = NULL;
+ int idx = 0, ret;
+ bool uj = use_json(argc, argv);
+ struct json_object *json_afi_safi = NULL, *json = NULL;
+
+ bgp_vty_find_and_parse_afi_safi_bgp(vty, argv, argc, &idx, &afi, &safi,
+ &bgp, false);
+ if (!idx)
+ return CMD_WARNING;
+
+ if (uj)
+ json_afi_safi = json_object_new_array();
+ else
+ json_afi_safi = NULL;
+
+ ret = bgp_table_stats(vty, bgp, afi, safi, json_afi_safi);
+
+ if (uj) {
+ json = json_object_new_object();
+ json_object_object_add(json, get_afi_safi_str(afi, safi, true),
+ json_afi_safi);
+ vty_json(vty, json);
+ }
+ return ret;
+}
+
+/* BGP route print out function without JSON */
+DEFUN(show_ip_bgp_afi_safi_statistics, show_ip_bgp_afi_safi_statistics_cmd,
+ "show [ip] bgp [<view|vrf> VIEWVRFNAME] [" BGP_AFI_CMD_STR
+ " [" BGP_SAFI_WITH_LABEL_CMD_STR
+ "]]\
+ statistics [json]",
+ SHOW_STR IP_STR BGP_STR BGP_INSTANCE_HELP_STR BGP_AFI_HELP_STR
+ BGP_SAFI_WITH_LABEL_HELP_STR
+ "BGP RIB advertisement statistics\n" JSON_STR)
+{
+ afi_t afi = AFI_IP6;
+ safi_t safi = SAFI_UNICAST;
+ struct bgp *bgp = NULL;
+ int idx = 0, ret;
+ bool uj = use_json(argc, argv);
+ struct json_object *json_afi_safi = NULL, *json = NULL;
+
+ bgp_vty_find_and_parse_afi_safi_bgp(vty, argv, argc, &idx, &afi, &safi,
+ &bgp, false);
+ if (!idx)
+ return CMD_WARNING;
+
+ if (uj)
+ json_afi_safi = json_object_new_array();
+ else
+ json_afi_safi = NULL;
+
+ ret = bgp_table_stats(vty, bgp, afi, safi, json_afi_safi);
+
+ if (uj) {
+ json = json_object_new_object();
+ json_object_object_add(json, get_afi_safi_str(afi, safi, true),
+ json_afi_safi);
+ vty_json(vty, json);
+ }
+ return ret;
+}
+
+DEFPY(show_ip_bgp_dampening_params, show_ip_bgp_dampening_params_cmd,
+ "show [ip] bgp [<view|vrf> VIEWVRFNAME] [" BGP_AFI_CMD_STR
+ " [" BGP_SAFI_WITH_LABEL_CMD_STR
+ "]] [all$all] dampening parameters [json]",
+ SHOW_STR IP_STR BGP_STR BGP_INSTANCE_HELP_STR BGP_AFI_HELP_STR
+ BGP_SAFI_WITH_LABEL_HELP_STR
+ "Display the entries for all address families\n"
+ "Display detailed information about dampening\n"
+ "Display detail of configured dampening parameters\n"
+ JSON_STR)
+{
+ afi_t afi = AFI_IP6;
+ safi_t safi = SAFI_UNICAST;
+ struct bgp *bgp = NULL;
+ int idx = 0;
+ uint16_t show_flags = 0;
+ bool uj = use_json(argc, argv);
+
+ if (uj) {
+ argc--;
+ SET_FLAG(show_flags, BGP_SHOW_OPT_JSON);
+ }
+
+ /* [<ipv4|ipv6> [all]] */
+ if (all) {
+ SET_FLAG(show_flags, BGP_SHOW_OPT_AFI_ALL);
+ if (argv_find(argv, argc, "ipv4", &idx))
+ SET_FLAG(show_flags, BGP_SHOW_OPT_AFI_IP);
+
+ if (argv_find(argv, argc, "ipv6", &idx))
+ SET_FLAG(show_flags, BGP_SHOW_OPT_AFI_IP6);
+ }
+
+ bgp_vty_find_and_parse_afi_safi_bgp(vty, argv, argc, &idx, &afi, &safi,
+ &bgp, false);
+ if (!idx)
+ return CMD_WARNING;
+
+ return bgp_show_dampening_parameters(vty, afi, safi, show_flags);
+}
+
+/* BGP route print out function */
+DEFPY(show_ip_bgp, show_ip_bgp_cmd,
+ "show [ip] bgp [<view|vrf> VIEWVRFNAME] [" BGP_AFI_CMD_STR
+ " [" BGP_SAFI_WITH_LABEL_CMD_STR
+ "]]\
+ [all$all]\
+ [cidr-only\
+ |dampening <flap-statistics|dampened-paths>\
+ |community [AA:NN|local-AS|no-advertise|no-export\
+ |graceful-shutdown|no-peer|blackhole|llgr-stale|no-llgr\
+ |accept-own|accept-own-nexthop|route-filter-v6\
+ |route-filter-v4|route-filter-translated-v6\
+ |route-filter-translated-v4] [exact-match]\
+ |community-list <(1-500)|COMMUNITY_LIST_NAME> [exact-match]\
+ |filter-list AS_PATH_FILTER_NAME\
+ |prefix-list WORD\
+ |access-list ACCESSLIST_NAME\
+ |route-map RMAP_NAME\
+ |rpki <invalid|valid|notfound>\
+ |version (1-4294967295)\
+ |alias ALIAS_NAME\
+ |A.B.C.D/M longer-prefixes\
+ |X:X::X:X/M longer-prefixes\
+ |"BGP_SELF_ORIG_CMD_STR"\
+ |detail-routes$detail_routes\
+ ] [json$uj [detail$detail_json] | wide$wide]",
+ SHOW_STR IP_STR BGP_STR BGP_INSTANCE_HELP_STR BGP_AFI_HELP_STR
+ BGP_SAFI_WITH_LABEL_HELP_STR
+ "Display the entries for all address families\n"
+ "Display only routes with non-natural netmasks\n"
+ "Display detailed information about dampening\n"
+ "Display flap statistics of routes\n"
+ "Display paths suppressed due to dampening\n"
+ "Display routes matching the communities\n" COMMUNITY_AANN_STR
+ "Do not send outside local AS (well-known community)\n"
+ "Do not advertise to any peer (well-known community)\n"
+ "Do not export to next AS (well-known community)\n"
+ "Graceful shutdown (well-known community)\n"
+ "Do not export to any peer (well-known community)\n"
+ "Inform EBGP peers to blackhole traffic to prefix (well-known community)\n"
+ "Staled Long-lived Graceful Restart VPN route (well-known community)\n"
+ "Removed because Long-lived Graceful Restart was not enabled for VPN route (well-known community)\n"
+ "Should accept local VPN route if exported and imported into different VRF (well-known community)\n"
+ "Should accept VPN route with local nexthop (well-known community)\n"
+ "RT VPNv6 route filtering (well-known community)\n"
+ "RT VPNv4 route filtering (well-known community)\n"
+ "RT translated VPNv6 route filtering (well-known community)\n"
+ "RT translated VPNv4 route filtering (well-known community)\n"
+ "Exact match of the communities\n"
+ "Community-list number\n"
+ "Community-list name\n"
+ "Display routes matching the community-list\n"
+ "Exact match of the communities\n"
+ "Display routes conforming to the filter-list\n"
+ "Regular expression access list name\n"
+ "Display routes conforming to the prefix-list\n"
+ "Prefix-list name\n"
+ "Display routes conforming to the access-list\n"
+ "Access-list name\n"
+ "Display routes matching the route-map\n"
+ "A route-map to match on\n"
+ "RPKI route types\n"
+ "A valid path as determined by rpki\n"
+ "A invalid path as determined by rpki\n"
+ "A path that has no rpki data\n"
+ "Display prefixes with matching version numbers\n"
+ "Version number and above\n"
+ "Display prefixes with matching BGP community alias\n"
+ "BGP community alias\n"
+ "IPv4 prefix\n"
+ "Display route and more specific routes\n"
+ "IPv6 prefix\n"
+ "Display route and more specific routes\n"
+ BGP_SELF_ORIG_HELP_STR
+ "Display detailed version of all routes\n"
+ JSON_STR
+ "Display detailed version of JSON output\n"
+ "Increase table width for longer prefixes\n")
+{
+ afi_t afi = AFI_IP6;
+ safi_t safi = SAFI_UNICAST;
+ enum bgp_show_type sh_type = bgp_show_type_normal;
+ void *output_arg = NULL;
+ struct bgp *bgp = NULL;
+ int idx = 0;
+ int exact_match = 0;
+ char *community = NULL;
+ bool first = true;
+ uint16_t show_flags = 0;
+ enum rpki_states rpki_target_state = RPKI_NOT_BEING_USED;
+ struct prefix p;
+
+ if (uj) {
+ argc--;
+ SET_FLAG(show_flags, BGP_SHOW_OPT_JSON);
+ }
+
+ if (detail_json)
+ SET_FLAG(show_flags, BGP_SHOW_OPT_JSON_DETAIL);
+
+ if (detail_routes)
+ SET_FLAG(show_flags, BGP_SHOW_OPT_ROUTES_DETAIL);
+
+ /* [<ipv4|ipv6> [all]] */
+ if (all) {
+ SET_FLAG(show_flags, BGP_SHOW_OPT_AFI_ALL);
+
+ if (argv_find(argv, argc, "ipv4", &idx))
+ SET_FLAG(show_flags, BGP_SHOW_OPT_AFI_IP);
+
+ if (argv_find(argv, argc, "ipv6", &idx))
+ SET_FLAG(show_flags, BGP_SHOW_OPT_AFI_IP6);
+ }
+
+ if (wide)
+ SET_FLAG(show_flags, BGP_SHOW_OPT_WIDE);
+
+ bgp_vty_find_and_parse_afi_safi_bgp(vty, argv, argc, &idx, &afi, &safi,
+ &bgp, uj);
+ if (!idx)
+ return CMD_WARNING;
+
+ if (argv_find(argv, argc, "cidr-only", &idx))
+ sh_type = bgp_show_type_cidr_only;
+
+ if (argv_find(argv, argc, "dampening", &idx)) {
+ if (argv_find(argv, argc, "dampened-paths", &idx))
+ sh_type = bgp_show_type_dampend_paths;
+ else if (argv_find(argv, argc, "flap-statistics", &idx))
+ sh_type = bgp_show_type_flap_statistics;
+ }
+
+ if (argv_find(argv, argc, "community", &idx)) {
+ char *maybecomm = NULL;
+
+ if (idx + 1 < argc) {
+ if (argv[idx + 1]->type == VARIABLE_TKN)
+ maybecomm = argv[idx + 1]->arg;
+ else
+ maybecomm = argv[idx + 1]->text;
+ }
+
+ if (maybecomm && !strmatch(maybecomm, "json")
+ && !strmatch(maybecomm, "exact-match"))
+ community = maybecomm;
+
+ if (argv_find(argv, argc, "exact-match", &idx))
+ exact_match = 1;
+
+ if (!community)
+ sh_type = bgp_show_type_community_all;
+ }
+
+ if (argv_find(argv, argc, "community-list", &idx)) {
+ const char *clist_number_or_name = argv[++idx]->arg;
+ struct community_list *list;
+
+ if (argv_find(argv, argc, "exact-match", &idx))
+ exact_match = 1;
+
+ list = community_list_lookup(bgp_clist, clist_number_or_name, 0,
+ COMMUNITY_LIST_MASTER);
+ if (list == NULL) {
+ vty_out(vty, "%% %s community-list not found\n",
+ clist_number_or_name);
+ return CMD_WARNING;
+ }
+
+ if (exact_match)
+ sh_type = bgp_show_type_community_list_exact;
+ else
+ sh_type = bgp_show_type_community_list;
+ output_arg = list;
+ }
+
+ if (argv_find(argv, argc, "filter-list", &idx)) {
+ const char *filter = argv[++idx]->arg;
+ struct as_list *as_list;
+
+ as_list = as_list_lookup(filter);
+ if (as_list == NULL) {
+ vty_out(vty, "%% %s AS-path access-list not found\n",
+ filter);
+ return CMD_WARNING;
+ }
+
+ sh_type = bgp_show_type_filter_list;
+ output_arg = as_list;
+ }
+
+ if (argv_find(argv, argc, "prefix-list", &idx)) {
+ const char *prefix_list_str = argv[++idx]->arg;
+ struct prefix_list *plist;
+
+ plist = prefix_list_lookup(afi, prefix_list_str);
+ if (plist == NULL) {
+ vty_out(vty, "%% %s prefix-list not found\n",
+ prefix_list_str);
+ return CMD_WARNING;
+ }
+
+ sh_type = bgp_show_type_prefix_list;
+ output_arg = plist;
+ }
+
+ if (argv_find(argv, argc, "access-list", &idx)) {
+ const char *access_list_str = argv[++idx]->arg;
+ struct access_list *alist;
+
+ alist = access_list_lookup(afi, access_list_str);
+ if (!alist) {
+ vty_out(vty, "%% %s access-list not found\n",
+ access_list_str);
+ return CMD_WARNING;
+ }
+
+ sh_type = bgp_show_type_access_list;
+ output_arg = alist;
+ }
+
+ if (argv_find(argv, argc, "route-map", &idx)) {
+ const char *rmap_str = argv[++idx]->arg;
+ struct route_map *rmap;
+
+ rmap = route_map_lookup_by_name(rmap_str);
+ if (!rmap) {
+ vty_out(vty, "%% %s route-map not found\n", rmap_str);
+ return CMD_WARNING;
+ }
+
+ sh_type = bgp_show_type_route_map;
+ output_arg = rmap;
+ }
+
+ if (argv_find(argv, argc, "rpki", &idx)) {
+ sh_type = bgp_show_type_rpki;
+ if (argv_find(argv, argc, "valid", &idx))
+ rpki_target_state = RPKI_VALID;
+ else if (argv_find(argv, argc, "invalid", &idx))
+ rpki_target_state = RPKI_INVALID;
+ else if (argv_find(argv, argc, "notfound", &idx))
+ rpki_target_state = RPKI_NOTFOUND;
+ }
+
+ /* Display prefixes with matching version numbers */
+ if (argv_find(argv, argc, "version", &idx)) {
+ sh_type = bgp_show_type_prefix_version;
+ output_arg = argv[idx + 1]->arg;
+ }
+
+ /* Display prefixes with matching BGP community alias */
+ if (argv_find(argv, argc, "alias", &idx)) {
+ sh_type = bgp_show_type_community_alias;
+ output_arg = argv[idx + 1]->arg;
+ }
+
+ /* prefix-longer */
+ if (argv_find(argv, argc, "A.B.C.D/M", &idx)
+ || argv_find(argv, argc, "X:X::X:X/M", &idx)) {
+ const char *prefix_str = argv[idx]->arg;
+
+ if (!str2prefix(prefix_str, &p)) {
+ vty_out(vty, "%% Malformed Prefix\n");
+ return CMD_WARNING;
+ }
+
+ sh_type = bgp_show_type_prefix_longer;
+ output_arg = &p;
+ }
+
+ /* self originated only */
+ if (argv_find(argv, argc, BGP_SELF_ORIG_CMD_STR, &idx))
+ sh_type = bgp_show_type_self_originated;
+
+ if (!all) {
+ /* show bgp: AFI_IP6, show ip bgp: AFI_IP */
+ if (community)
+ return bgp_show_community(vty, bgp, community,
+ exact_match, afi, safi,
+ show_flags);
+ else
+ return bgp_show(vty, bgp, afi, safi, sh_type,
+ output_arg, show_flags,
+ rpki_target_state);
+ } else {
+ struct listnode *node;
+ struct bgp *abgp;
+ /* show <ip> bgp ipv4 all: AFI_IP, show <ip> bgp ipv6 all:
+ * AFI_IP6 */
+
+ if (uj)
+ vty_out(vty, "{\n");
+
+ if (CHECK_FLAG(show_flags, BGP_SHOW_OPT_AFI_IP)
+ || CHECK_FLAG(show_flags, BGP_SHOW_OPT_AFI_IP6)) {
+ afi = CHECK_FLAG(show_flags, BGP_SHOW_OPT_AFI_IP)
+ ? AFI_IP
+ : AFI_IP6;
+ for (ALL_LIST_ELEMENTS_RO(bm->bgp, node, abgp)) {
+ FOREACH_SAFI (safi) {
+ if (!bgp_afi_safi_peer_exists(abgp, afi,
+ safi))
+ continue;
+
+ if (uj) {
+ if (first)
+ first = false;
+ else
+ vty_out(vty, ",\n");
+ vty_out(vty, "\"%s\":{\n",
+ get_afi_safi_str(afi,
+ safi,
+ true));
+ } else
+ vty_out(vty,
+ "\nFor address family: %s\n",
+ get_afi_safi_str(
+ afi, safi,
+ false));
+
+ if (community)
+ bgp_show_community(
+ vty, abgp, community,
+ exact_match, afi, safi,
+ show_flags);
+ else
+ bgp_show(vty, abgp, afi, safi,
+ sh_type, output_arg,
+ show_flags,
+ rpki_target_state);
+ if (uj)
+ vty_out(vty, "}\n");
+ }
+ }
+ } else {
+ /* show <ip> bgp all: for each AFI and SAFI*/
+ for (ALL_LIST_ELEMENTS_RO(bm->bgp, node, abgp)) {
+ FOREACH_AFI_SAFI (afi, safi) {
+ if (!bgp_afi_safi_peer_exists(abgp, afi,
+ safi))
+ continue;
+
+ if (uj) {
+ if (first)
+ first = false;
+ else
+ vty_out(vty, ",\n");
+
+ vty_out(vty, "\"%s\":{\n",
+ get_afi_safi_str(afi,
+ safi,
+ true));
+
+ /* Adding 'routes' key to make
+ * the json output format valid
+ * for evpn
+ */
+ if (safi == SAFI_EVPN)
+ vty_out(vty,
+ "\"routes\":");
+
+ } else
+ vty_out(vty,
+ "\nFor address family: %s\n",
+ get_afi_safi_str(
+ afi, safi,
+ false));
+
+ if (community)
+ bgp_show_community(
+ vty, abgp, community,
+ exact_match, afi, safi,
+ show_flags);
+ else
+ bgp_show(vty, abgp, afi, safi,
+ sh_type, output_arg,
+ show_flags,
+ rpki_target_state);
+ if (uj)
+ vty_out(vty, "}\n");
+ }
+ }
+ }
+ if (uj)
+ vty_out(vty, "}\n");
+ }
+ return CMD_SUCCESS;
+}
+
+DEFUN (show_ip_bgp_route,
+ show_ip_bgp_route_cmd,
+ "show [ip] bgp [<view|vrf> VIEWVRFNAME] ["BGP_AFI_CMD_STR" ["BGP_SAFI_WITH_LABEL_CMD_STR"]]<A.B.C.D|A.B.C.D/M|X:X::X:X|X:X::X:X/M> [<bestpath|multipath>] [rpki <valid|invalid|notfound>] [json]",
+ SHOW_STR
+ IP_STR
+ BGP_STR
+ BGP_INSTANCE_HELP_STR
+ BGP_AFI_HELP_STR
+ BGP_SAFI_WITH_LABEL_HELP_STR
+ "Network in the BGP routing table to display\n"
+ "IPv4 prefix\n"
+ "Network in the BGP routing table to display\n"
+ "IPv6 prefix\n"
+ "Display only the bestpath\n"
+ "Display only multipaths\n"
+ "Display only paths that match the specified rpki state\n"
+ "A valid path as determined by rpki\n"
+ "A invalid path as determined by rpki\n"
+ "A path that has no rpki data\n"
+ JSON_STR)
+{
+ int prefix_check = 0;
+
+ afi_t afi = AFI_IP6;
+ safi_t safi = SAFI_UNICAST;
+ char *prefix = NULL;
+ struct bgp *bgp = NULL;
+ enum bgp_path_type path_type;
+ bool uj = use_json(argc, argv);
+
+ int idx = 0;
+
+ bgp_vty_find_and_parse_afi_safi_bgp(vty, argv, argc, &idx, &afi, &safi,
+ &bgp, uj);
+ if (!idx)
+ return CMD_WARNING;
+
+ if (!bgp) {
+ vty_out(vty,
+ "Specified 'all' vrf's but this command currently only works per view/vrf\n");
+ return CMD_WARNING;
+ }
+
+ /* <A.B.C.D|A.B.C.D/M|X:X::X:X|X:X::X:X/M> */
+ if (argv_find(argv, argc, "A.B.C.D", &idx)
+ || argv_find(argv, argc, "X:X::X:X", &idx))
+ prefix_check = 0;
+ else if (argv_find(argv, argc, "A.B.C.D/M", &idx)
+ || argv_find(argv, argc, "X:X::X:X/M", &idx))
+ prefix_check = 1;
+
+ if ((argv[idx]->type == IPV6_TKN || argv[idx]->type == IPV6_PREFIX_TKN)
+ && afi != AFI_IP6) {
+ vty_out(vty,
+ "%% Cannot specify IPv6 address or prefix with IPv4 AFI\n");
+ return CMD_WARNING;
+ }
+ if ((argv[idx]->type == IPV4_TKN || argv[idx]->type == IPV4_PREFIX_TKN)
+ && afi != AFI_IP) {
+ vty_out(vty,
+ "%% Cannot specify IPv4 address or prefix with IPv6 AFI\n");
+ return CMD_WARNING;
+ }
+
+ prefix = argv[idx]->arg;
+
+ /* [<bestpath|multipath>] */
+ if (argv_find(argv, argc, "bestpath", &idx))
+ path_type = BGP_PATH_SHOW_BESTPATH;
+ else if (argv_find(argv, argc, "multipath", &idx))
+ path_type = BGP_PATH_SHOW_MULTIPATH;
+ else
+ path_type = BGP_PATH_SHOW_ALL;
+
+ return bgp_show_route(vty, bgp, prefix, afi, safi, NULL, prefix_check,
+ path_type, RPKI_NOT_BEING_USED, uj);
+}
+
+DEFUN (show_ip_bgp_regexp,
+ show_ip_bgp_regexp_cmd,
+ "show [ip] bgp [<view|vrf> VIEWVRFNAME] ["BGP_AFI_CMD_STR" ["BGP_SAFI_WITH_LABEL_CMD_STR"]] regexp REGEX [json]",
+ SHOW_STR
+ IP_STR
+ BGP_STR
+ BGP_INSTANCE_HELP_STR
+ BGP_AFI_HELP_STR
+ BGP_SAFI_WITH_LABEL_HELP_STR
+ "Display routes matching the AS path regular expression\n"
+ "A regular-expression (1234567890_^|[,{}() ]$*+.?-\\) to match the BGP AS paths\n"
+ JSON_STR)
+{
+ afi_t afi = AFI_IP6;
+ safi_t safi = SAFI_UNICAST;
+ struct bgp *bgp = NULL;
+ bool uj = use_json(argc, argv);
+ char *regstr = NULL;
+
+ int idx = 0;
+ bgp_vty_find_and_parse_afi_safi_bgp(vty, argv, argc, &idx, &afi, &safi,
+ &bgp, false);
+ if (!idx)
+ return CMD_WARNING;
+
+ // get index of regex
+ if (argv_find(argv, argc, "REGEX", &idx))
+ regstr = argv[idx]->arg;
+
+ assert(regstr);
+ return bgp_show_regexp(vty, bgp, (const char *)regstr, afi, safi,
+ bgp_show_type_regexp, uj);
+}
+
+DEFPY (show_ip_bgp_instance_all,
+ show_ip_bgp_instance_all_cmd,
+ "show [ip] bgp <view|vrf> all ["BGP_AFI_CMD_STR" ["BGP_SAFI_WITH_LABEL_CMD_STR"]] [json$uj | wide$wide]",
+ SHOW_STR
+ IP_STR
+ BGP_STR
+ BGP_INSTANCE_ALL_HELP_STR
+ BGP_AFI_HELP_STR
+ BGP_SAFI_WITH_LABEL_HELP_STR
+ JSON_STR
+ "Increase table width for longer prefixes\n")
+{
+ afi_t afi = AFI_IP6;
+ safi_t safi = SAFI_UNICAST;
+ struct bgp *bgp = NULL;
+ int idx = 0;
+ uint16_t show_flags = 0;
+
+ if (uj) {
+ argc--;
+ SET_FLAG(show_flags, BGP_SHOW_OPT_JSON);
+ }
+
+ if (wide)
+ SET_FLAG(show_flags, BGP_SHOW_OPT_WIDE);
+
+ bgp_vty_find_and_parse_afi_safi_bgp(vty, argv, argc, &idx, &afi, &safi,
+ &bgp, uj);
+ if (!idx)
+ return CMD_WARNING;
+
+ bgp_show_all_instances_routes_vty(vty, afi, safi, show_flags);
+ return CMD_SUCCESS;
+}
+
+static int bgp_show_regexp(struct vty *vty, struct bgp *bgp, const char *regstr,
+ afi_t afi, safi_t safi, enum bgp_show_type type,
+ bool use_json)
+{
+ regex_t *regex;
+ int rc;
+ uint16_t show_flags = 0;
+
+ if (use_json)
+ SET_FLAG(show_flags, BGP_SHOW_OPT_JSON);
+
+ if (!config_bgp_aspath_validate(regstr)) {
+ vty_out(vty, "Invalid character in REGEX %s\n",
+ regstr);
+ return CMD_WARNING_CONFIG_FAILED;
+ }
+
+ regex = bgp_regcomp(regstr);
+ if (!regex) {
+ vty_out(vty, "Can't compile regexp %s\n", regstr);
+ return CMD_WARNING;
+ }
+
+ rc = bgp_show(vty, bgp, afi, safi, type, regex, show_flags,
+ RPKI_NOT_BEING_USED);
+ bgp_regex_free(regex);
+ return rc;
+}
+
+static int bgp_show_community(struct vty *vty, struct bgp *bgp,
+ const char *comstr, int exact, afi_t afi,
+ safi_t safi, uint16_t show_flags)
+{
+ struct community *com;
+ int ret = 0;
+
+ com = community_str2com(comstr);
+ if (!com) {
+ vty_out(vty, "%% Community malformed: %s\n", comstr);
+ return CMD_WARNING;
+ }
+
+ ret = bgp_show(vty, bgp, afi, safi,
+ (exact ? bgp_show_type_community_exact
+ : bgp_show_type_community),
+ com, show_flags, RPKI_NOT_BEING_USED);
+ community_free(&com);
+
+ return ret;
+}
+
+enum bgp_stats {
+ BGP_STATS_MAXBITLEN = 0,
+ BGP_STATS_RIB,
+ BGP_STATS_PREFIXES,
+ BGP_STATS_TOTPLEN,
+ BGP_STATS_UNAGGREGATEABLE,
+ BGP_STATS_MAX_AGGREGATEABLE,
+ BGP_STATS_AGGREGATES,
+ BGP_STATS_SPACE,
+ BGP_STATS_ASPATH_COUNT,
+ BGP_STATS_ASPATH_MAXHOPS,
+ BGP_STATS_ASPATH_TOTHOPS,
+ BGP_STATS_ASPATH_MAXSIZE,
+ BGP_STATS_ASPATH_TOTSIZE,
+ BGP_STATS_ASN_HIGHEST,
+ BGP_STATS_MAX,
+};
+
+#define TABLE_STATS_IDX_VTY 0
+#define TABLE_STATS_IDX_JSON 1
+
+static const char *table_stats_strs[][2] = {
+ [BGP_STATS_PREFIXES] = {"Total Prefixes", "totalPrefixes"},
+ [BGP_STATS_TOTPLEN] = {"Average prefix length", "averagePrefixLength"},
+ [BGP_STATS_RIB] = {"Total Advertisements", "totalAdvertisements"},
+ [BGP_STATS_UNAGGREGATEABLE] = {"Unaggregateable prefixes",
+ "unaggregateablePrefixes"},
+ [BGP_STATS_MAX_AGGREGATEABLE] = {"Maximum aggregateable prefixes",
+ "maximumAggregateablePrefixes"},
+ [BGP_STATS_AGGREGATES] = {"BGP Aggregate advertisements",
+ "bgpAggregateAdvertisements"},
+ [BGP_STATS_SPACE] = {"Address space advertised",
+ "addressSpaceAdvertised"},
+ [BGP_STATS_ASPATH_COUNT] = {"Advertisements with paths",
+ "advertisementsWithPaths"},
+ [BGP_STATS_ASPATH_MAXHOPS] = {"Longest AS-Path (hops)",
+ "longestAsPath"},
+ [BGP_STATS_ASPATH_MAXSIZE] = {"Largest AS-Path (bytes)",
+ "largestAsPath"},
+ [BGP_STATS_ASPATH_TOTHOPS] = {"Average AS-Path length (hops)",
+ "averageAsPathLengthHops"},
+ [BGP_STATS_ASPATH_TOTSIZE] = {"Average AS-Path size (bytes)",
+ "averageAsPathSizeBytes"},
+ [BGP_STATS_ASN_HIGHEST] = {"Highest public ASN", "highestPublicAsn"},
+ [BGP_STATS_MAX] = {NULL, NULL}
+};
+
+struct bgp_table_stats {
+ struct bgp_table *table;
+ unsigned long long counts[BGP_STATS_MAX];
+
+ unsigned long long
+ prefix_len_count[MAX(EVPN_ROUTE_PREFIXLEN, IPV6_MAX_BITLEN) +
+ 1];
+
+ double total_space;
+};
+
+static void bgp_table_stats_rn(struct bgp_dest *dest, struct bgp_dest *top,
+ struct bgp_table_stats *ts, unsigned int space)
+{
+ struct bgp_dest *pdest = bgp_dest_parent_nolock(dest);
+ struct bgp_path_info *pi;
+ const struct prefix *rn_p;
+
+ if (!bgp_dest_has_bgp_path_info_data(dest))
+ return;
+
+ rn_p = bgp_dest_get_prefix(dest);
+ ts->counts[BGP_STATS_PREFIXES]++;
+ ts->counts[BGP_STATS_TOTPLEN] += rn_p->prefixlen;
+
+ ts->prefix_len_count[rn_p->prefixlen]++;
+ /* check if the prefix is included by any other announcements */
+ while (pdest && !bgp_dest_has_bgp_path_info_data(pdest))
+ pdest = bgp_dest_parent_nolock(pdest);
+
+ if (pdest == NULL || pdest == top) {
+ ts->counts[BGP_STATS_UNAGGREGATEABLE]++;
+ /* announced address space */
+ if (space)
+ ts->total_space += pow(2.0, space - rn_p->prefixlen);
+ } else if (bgp_dest_has_bgp_path_info_data(pdest))
+ ts->counts[BGP_STATS_MAX_AGGREGATEABLE]++;
+
+
+ for (pi = bgp_dest_get_bgp_path_info(dest); pi; pi = pi->next) {
+ ts->counts[BGP_STATS_RIB]++;
+
+ if (CHECK_FLAG(pi->attr->flag,
+ ATTR_FLAG_BIT(BGP_ATTR_ATOMIC_AGGREGATE)))
+ ts->counts[BGP_STATS_AGGREGATES]++;
+
+ /* as-path stats */
+ if (pi->attr->aspath) {
+ unsigned int hops = aspath_count_hops(pi->attr->aspath);
+ unsigned int size = aspath_size(pi->attr->aspath);
+ as_t highest = aspath_highest(pi->attr->aspath);
+
+ ts->counts[BGP_STATS_ASPATH_COUNT]++;
+
+ if (hops > ts->counts[BGP_STATS_ASPATH_MAXHOPS])
+ ts->counts[BGP_STATS_ASPATH_MAXHOPS] = hops;
+
+ if (size > ts->counts[BGP_STATS_ASPATH_MAXSIZE])
+ ts->counts[BGP_STATS_ASPATH_MAXSIZE] = size;
+
+ ts->counts[BGP_STATS_ASPATH_TOTHOPS] += hops;
+ ts->counts[BGP_STATS_ASPATH_TOTSIZE] += size;
+ if (highest > ts->counts[BGP_STATS_ASN_HIGHEST])
+ ts->counts[BGP_STATS_ASN_HIGHEST] = highest;
+ }
+ }
+}
+
+static void bgp_table_stats_walker(struct event *t)
+{
+ struct bgp_dest *dest, *ndest;
+ struct bgp_dest *top;
+ struct bgp_table_stats *ts = EVENT_ARG(t);
+ unsigned int space = 0;
+
+ if (!(top = bgp_table_top(ts->table)))
+ return;
+
+ switch (ts->table->afi) {
+ case AFI_IP:
+ space = IPV4_MAX_BITLEN;
+ break;
+ case AFI_IP6:
+ space = IPV6_MAX_BITLEN;
+ break;
+ case AFI_L2VPN:
+ space = EVPN_ROUTE_PREFIXLEN;
+ break;
+ case AFI_UNSPEC:
+ case AFI_MAX:
+ return;
+ }
+
+ ts->counts[BGP_STATS_MAXBITLEN] = space;
+
+ for (dest = top; dest; dest = bgp_route_next(dest)) {
+ if (ts->table->safi == SAFI_MPLS_VPN
+ || ts->table->safi == SAFI_ENCAP
+ || ts->table->safi == SAFI_EVPN) {
+ struct bgp_table *table;
+
+ table = bgp_dest_get_bgp_table_info(dest);
+ if (!table)
+ continue;
+
+ top = bgp_table_top(table);
+ for (ndest = bgp_table_top(table); ndest;
+ ndest = bgp_route_next(ndest))
+ bgp_table_stats_rn(ndest, top, ts, space);
+ } else {
+ bgp_table_stats_rn(dest, top, ts, space);
+ }
+ }
+}
+
+static void bgp_table_stats_all(struct vty *vty, afi_t afi, safi_t safi,
+ struct json_object *json_array)
+{
+ struct listnode *node, *nnode;
+ struct bgp *bgp;
+
+ for (ALL_LIST_ELEMENTS(bm->bgp, node, nnode, bgp))
+ bgp_table_stats_single(vty, bgp, afi, safi, json_array);
+}
+
+static int bgp_table_stats_single(struct vty *vty, struct bgp *bgp, afi_t afi,
+ safi_t safi, struct json_object *json_array)
+{
+ struct bgp_table_stats ts;
+ unsigned int i;
+ int ret = CMD_SUCCESS;
+ char temp_buf[20];
+ struct json_object *json = NULL;
+ uint32_t bitlen = 0;
+ struct json_object *json_bitlen;
+
+ if (json_array)
+ json = json_object_new_object();
+
+ if (!bgp->rib[afi][safi]) {
+ char warning_msg[50];
+
+ snprintf(warning_msg, sizeof(warning_msg),
+ "%% No RIB exist's for the AFI(%d)/SAFI(%d)", afi,
+ safi);
+
+ if (!json)
+ vty_out(vty, "%s\n", warning_msg);
+ else
+ json_object_string_add(json, "warning", warning_msg);
+
+ ret = CMD_WARNING;
+ goto end_table_stats;
+ }
+
+ if (!json)
+ vty_out(vty, "BGP %s RIB statistics (%s)\n",
+ get_afi_safi_str(afi, safi, false), bgp->name_pretty);
+ else
+ json_object_string_add(json, "instance", bgp->name_pretty);
+
+ /* labeled-unicast routes live in the unicast table */
+ if (safi == SAFI_LABELED_UNICAST)
+ safi = SAFI_UNICAST;
+
+ memset(&ts, 0, sizeof(ts));
+ ts.table = bgp->rib[afi][safi];
+ event_execute(bm->master, bgp_table_stats_walker, &ts, 0, NULL);
+
+ for (i = 0; i < BGP_STATS_MAX; i++) {
+ if ((!json && !table_stats_strs[i][TABLE_STATS_IDX_VTY])
+ || (json && !table_stats_strs[i][TABLE_STATS_IDX_JSON]))
+ continue;
+
+ switch (i) {
+ case BGP_STATS_ASPATH_TOTHOPS:
+ case BGP_STATS_ASPATH_TOTSIZE:
+ if (!json) {
+ snprintf(
+ temp_buf, sizeof(temp_buf), "%12.2f",
+ ts.counts[i]
+ ? (float)ts.counts[i]
+ / (float)ts.counts
+ [BGP_STATS_ASPATH_COUNT]
+ : 0);
+ vty_out(vty, "%-30s: %s",
+ table_stats_strs[i]
+ [TABLE_STATS_IDX_VTY],
+ temp_buf);
+ } else {
+ json_object_double_add(
+ json,
+ table_stats_strs[i]
+ [TABLE_STATS_IDX_JSON],
+ ts.counts[i]
+ ? (double)ts.counts[i]
+ / (double)ts.counts
+ [BGP_STATS_ASPATH_COUNT]
+ : 0);
+ }
+ break;
+ case BGP_STATS_TOTPLEN:
+ if (!json) {
+ snprintf(
+ temp_buf, sizeof(temp_buf), "%12.2f",
+ ts.counts[i]
+ ? (float)ts.counts[i]
+ / (float)ts.counts
+ [BGP_STATS_PREFIXES]
+ : 0);
+ vty_out(vty, "%-30s: %s",
+ table_stats_strs[i]
+ [TABLE_STATS_IDX_VTY],
+ temp_buf);
+ } else {
+ json_object_double_add(
+ json,
+ table_stats_strs[i]
+ [TABLE_STATS_IDX_JSON],
+ ts.counts[i]
+ ? (double)ts.counts[i]
+ / (double)ts.counts
+ [BGP_STATS_PREFIXES]
+ : 0);
+ }
+ break;
+ case BGP_STATS_SPACE:
+ if (!json) {
+ snprintf(temp_buf, sizeof(temp_buf), "%12g",
+ ts.total_space);
+ vty_out(vty, "%-30s: %s\n",
+ table_stats_strs[i]
+ [TABLE_STATS_IDX_VTY],
+ temp_buf);
+ } else {
+ json_object_double_add(
+ json,
+ table_stats_strs[i]
+ [TABLE_STATS_IDX_JSON],
+ (double)ts.total_space);
+ }
+ if (afi == AFI_IP6) {
+ if (!json) {
+ snprintf(temp_buf, sizeof(temp_buf),
+ "%12g",
+ ts.total_space
+ * pow(2.0, -128 + 32));
+ vty_out(vty, "%30s: %s\n",
+ "/32 equivalent %s\n",
+ temp_buf);
+ } else {
+ json_object_double_add(
+ json, "/32equivalent",
+ (double)(ts.total_space
+ * pow(2.0,
+ -128 + 32)));
+ }
+ if (!json) {
+ snprintf(temp_buf, sizeof(temp_buf),
+ "%12g",
+ ts.total_space
+ * pow(2.0, -128 + 48));
+ vty_out(vty, "%30s: %s\n",
+ "/48 equivalent %s\n",
+ temp_buf);
+ } else {
+ json_object_double_add(
+ json, "/48equivalent",
+ (double)(ts.total_space
+ * pow(2.0,
+ -128 + 48)));
+ }
+ } else {
+ if (!json) {
+ snprintf(temp_buf, sizeof(temp_buf),
+ "%12.2f",
+ ts.total_space * 100.
+ * pow(2.0, -32));
+ vty_out(vty, "%30s: %s\n",
+ "% announced ", temp_buf);
+ } else {
+ json_object_double_add(
+ json, "%announced",
+ (double)(ts.total_space * 100.
+ * pow(2.0, -32)));
+ }
+ if (!json) {
+ snprintf(temp_buf, sizeof(temp_buf),
+ "%12.2f",
+ ts.total_space
+ * pow(2.0, -32 + 8));
+ vty_out(vty, "%30s: %s\n",
+ "/8 equivalent ", temp_buf);
+ } else {
+ json_object_double_add(
+ json, "/8equivalent",
+ (double)(ts.total_space
+ * pow(2.0, -32 + 8)));
+ }
+ if (!json) {
+ snprintf(temp_buf, sizeof(temp_buf),
+ "%12.2f",
+ ts.total_space
+ * pow(2.0, -32 + 24));
+ vty_out(vty, "%30s: %s\n",
+ "/24 equivalent ", temp_buf);
+ } else {
+ json_object_double_add(
+ json, "/24equivalent",
+ (double)(ts.total_space
+ * pow(2.0, -32 + 24)));
+ }
+ }
+ break;
+ default:
+ if (!json) {
+ snprintf(temp_buf, sizeof(temp_buf), "%12llu",
+ ts.counts[i]);
+ vty_out(vty, "%-30s: %s",
+ table_stats_strs[i]
+ [TABLE_STATS_IDX_VTY],
+ temp_buf);
+ } else {
+ json_object_int_add(
+ json,
+ table_stats_strs[i]
+ [TABLE_STATS_IDX_JSON],
+ ts.counts[i]);
+ }
+ }
+ if (!json)
+ vty_out(vty, "\n");
+ }
+
+ switch (afi) {
+ case AFI_IP:
+ bitlen = IPV4_MAX_BITLEN;
+ break;
+ case AFI_IP6:
+ bitlen = IPV6_MAX_BITLEN;
+ break;
+ case AFI_L2VPN:
+ bitlen = EVPN_ROUTE_PREFIXLEN;
+ break;
+ case AFI_UNSPEC:
+ case AFI_MAX:
+ break;
+ }
+
+ if (json) {
+ json_bitlen = json_object_new_array();
+
+ for (i = 0; i <= bitlen; i++) {
+ if (!ts.prefix_len_count[i])
+ continue;
+
+ struct json_object *ind_bit = json_object_new_object();
+
+ snprintf(temp_buf, sizeof(temp_buf), "%u", i);
+ json_object_int_add(ind_bit, temp_buf,
+ ts.prefix_len_count[i]);
+ json_object_array_add(json_bitlen, ind_bit);
+ }
+ json_object_object_add(json, "prefixLength", json_bitlen);
+ }
+
+end_table_stats:
+ if (json)
+ json_object_array_add(json_array, json);
+ return ret;
+}
+
+static int bgp_table_stats(struct vty *vty, struct bgp *bgp, afi_t afi,
+ safi_t safi, struct json_object *json_array)
+{
+ if (!bgp) {
+ bgp_table_stats_all(vty, afi, safi, json_array);
+ return CMD_SUCCESS;
+ }
+
+ return bgp_table_stats_single(vty, bgp, afi, safi, json_array);
+}
+
+enum bgp_pcounts {
+ PCOUNT_ADJ_IN = 0,
+ PCOUNT_DAMPED,
+ PCOUNT_REMOVED,
+ PCOUNT_HISTORY,
+ PCOUNT_STALE,
+ PCOUNT_VALID,
+ PCOUNT_ALL,
+ PCOUNT_COUNTED,
+ PCOUNT_BPATH_SELECTED,
+ PCOUNT_PFCNT, /* the figure we display to users */
+ PCOUNT_MAX,
+};
+
+static const char *const pcount_strs[] = {
+ [PCOUNT_ADJ_IN] = "Adj-in",
+ [PCOUNT_DAMPED] = "Damped",
+ [PCOUNT_REMOVED] = "Removed",
+ [PCOUNT_HISTORY] = "History",
+ [PCOUNT_STALE] = "Stale",
+ [PCOUNT_VALID] = "Valid",
+ [PCOUNT_ALL] = "All RIB",
+ [PCOUNT_COUNTED] = "PfxCt counted",
+ [PCOUNT_BPATH_SELECTED] = "PfxCt Best Selected",
+ [PCOUNT_PFCNT] = "Useable",
+ [PCOUNT_MAX] = NULL,
+};
+
+struct peer_pcounts {
+ unsigned int count[PCOUNT_MAX];
+ const struct peer *peer;
+ const struct bgp_table *table;
+ safi_t safi;
+};
+
+static void bgp_peer_count_proc(struct bgp_dest *rn, struct peer_pcounts *pc)
+{
+ const struct bgp_adj_in *ain;
+ const struct bgp_path_info *pi;
+ const struct peer *peer = pc->peer;
+
+ for (ain = rn->adj_in; ain; ain = ain->next)
+ if (ain->peer == peer)
+ pc->count[PCOUNT_ADJ_IN]++;
+
+ for (pi = bgp_dest_get_bgp_path_info(rn); pi; pi = pi->next) {
+
+ if (pi->peer != peer)
+ continue;
+
+ pc->count[PCOUNT_ALL]++;
+
+ if (CHECK_FLAG(pi->flags, BGP_PATH_DAMPED))
+ pc->count[PCOUNT_DAMPED]++;
+ if (CHECK_FLAG(pi->flags, BGP_PATH_HISTORY))
+ pc->count[PCOUNT_HISTORY]++;
+ if (CHECK_FLAG(pi->flags, BGP_PATH_REMOVED))
+ pc->count[PCOUNT_REMOVED]++;
+ if (CHECK_FLAG(pi->flags, BGP_PATH_STALE))
+ pc->count[PCOUNT_STALE]++;
+ if (CHECK_FLAG(pi->flags, BGP_PATH_VALID))
+ pc->count[PCOUNT_VALID]++;
+ if (!CHECK_FLAG(pi->flags, BGP_PATH_UNUSEABLE))
+ pc->count[PCOUNT_PFCNT]++;
+ if (CHECK_FLAG(pi->flags, BGP_PATH_SELECTED))
+ pc->count[PCOUNT_BPATH_SELECTED]++;
+
+ if (CHECK_FLAG(pi->flags, BGP_PATH_COUNTED)) {
+ pc->count[PCOUNT_COUNTED]++;
+ if (CHECK_FLAG(pi->flags, BGP_PATH_UNUSEABLE))
+ flog_err(
+ EC_LIB_DEVELOPMENT,
+ "Attempting to count but flags say it is unusable");
+ } else {
+ if (!CHECK_FLAG(pi->flags, BGP_PATH_UNUSEABLE))
+ flog_err(
+ EC_LIB_DEVELOPMENT,
+ "Not counted but flags say we should");
+ }
+ }
+}
+
+static void bgp_peer_count_walker(struct event *t)
+{
+ struct bgp_dest *rn, *rm;
+ const struct bgp_table *table;
+ struct peer_pcounts *pc = EVENT_ARG(t);
+
+ if (pc->safi == SAFI_MPLS_VPN || pc->safi == SAFI_ENCAP
+ || pc->safi == SAFI_EVPN) {
+ /* Special handling for 2-level routing tables. */
+ for (rn = bgp_table_top(pc->table); rn;
+ rn = bgp_route_next(rn)) {
+ table = bgp_dest_get_bgp_table_info(rn);
+ if (table != NULL)
+ for (rm = bgp_table_top(table); rm;
+ rm = bgp_route_next(rm))
+ bgp_peer_count_proc(rm, pc);
+ }
+ } else
+ for (rn = bgp_table_top(pc->table); rn; rn = bgp_route_next(rn))
+ bgp_peer_count_proc(rn, pc);
+}
+
+static int bgp_peer_counts(struct vty *vty, struct peer *peer, afi_t afi,
+ safi_t safi, bool use_json)
+{
+ struct peer_pcounts pcounts = {.peer = peer};
+ unsigned int i;
+ json_object *json = NULL;
+ json_object *json_loop = NULL;
+
+ if (use_json) {
+ json = json_object_new_object();
+ json_loop = json_object_new_object();
+ }
+
+ if (!peer || !peer->bgp || !peer->afc[afi][safi]
+ || !peer->bgp->rib[afi][safi]) {
+ if (use_json) {
+ json_object_string_add(
+ json, "warning",
+ "No such neighbor or address family");
+ vty_out(vty, "%s\n", json_object_to_json_string(json));
+ json_object_free(json);
+ json_object_free(json_loop);
+ } else
+ vty_out(vty, "%% No such neighbor or address family\n");
+
+ return CMD_WARNING;
+ }
+
+ memset(&pcounts, 0, sizeof(pcounts));
+ pcounts.peer = peer;
+ pcounts.table = peer->bgp->rib[afi][safi];
+ pcounts.safi = safi;
+
+ /* in-place call via thread subsystem so as to record execution time
+ * stats for the thread-walk (i.e. ensure this can't be blamed on
+ * on just vty_read()).
+ */
+ event_execute(bm->master, bgp_peer_count_walker, &pcounts, 0, NULL);
+
+ if (use_json) {
+ json_object_string_add(json, "prefixCountsFor", peer->host);
+ json_object_string_add(json, "multiProtocol",
+ get_afi_safi_str(afi, safi, true));
+ json_object_int_add(json, "pfxCounter",
+ peer->pcount[afi][safi]);
+
+ for (i = 0; i < PCOUNT_MAX; i++)
+ json_object_int_add(json_loop, pcount_strs[i],
+ pcounts.count[i]);
+
+ json_object_object_add(json, "ribTableWalkCounters", json_loop);
+
+ if (pcounts.count[PCOUNT_PFCNT] != peer->pcount[afi][safi]) {
+ json_object_string_add(json, "pfxctDriftFor",
+ peer->host);
+ json_object_string_add(
+ json, "recommended",
+ "Please report this bug, with the above command output");
+ }
+ vty_json(vty, json);
+ } else {
+
+ if (peer->hostname
+ && CHECK_FLAG(peer->bgp->flags, BGP_FLAG_SHOW_HOSTNAME)) {
+ vty_out(vty, "Prefix counts for %s/%s, %s\n",
+ peer->hostname, peer->host,
+ get_afi_safi_str(afi, safi, false));
+ } else {
+ vty_out(vty, "Prefix counts for %s, %s\n", peer->host,
+ get_afi_safi_str(afi, safi, false));
+ }
+
+ vty_out(vty, "PfxCt: %u\n", peer->pcount[afi][safi]);
+ vty_out(vty, "\nCounts from RIB table walk:\n\n");
+
+ for (i = 0; i < PCOUNT_MAX; i++)
+ vty_out(vty, "%20s: %-10d\n", pcount_strs[i],
+ pcounts.count[i]);
+
+ if (pcounts.count[PCOUNT_PFCNT] != peer->pcount[afi][safi]) {
+ vty_out(vty, "%s [pcount] PfxCt drift!\n", peer->host);
+ vty_out(vty,
+ "Please report this bug, with the above command output\n");
+ }
+ }
+
+ return CMD_SUCCESS;
+}
+
+DEFUN (show_ip_bgp_instance_neighbor_prefix_counts,
+ show_ip_bgp_instance_neighbor_prefix_counts_cmd,
+ "show [ip] bgp [<view|vrf> VIEWVRFNAME] ["BGP_AFI_CMD_STR" ["BGP_SAFI_CMD_STR"]] neighbors <A.B.C.D|X:X::X:X|WORD> prefix-counts [json]",
+ SHOW_STR
+ IP_STR
+ BGP_STR
+ BGP_INSTANCE_HELP_STR
+ BGP_AFI_HELP_STR
+ BGP_SAFI_HELP_STR
+ "Detailed information on TCP and BGP neighbor connections\n"
+ "Neighbor to display information about\n"
+ "Neighbor to display information about\n"
+ "Neighbor on BGP configured interface\n"
+ "Display detailed prefix count information\n"
+ JSON_STR)
+{
+ afi_t afi = AFI_IP6;
+ safi_t safi = SAFI_UNICAST;
+ struct peer *peer;
+ int idx = 0;
+ struct bgp *bgp = NULL;
+ bool uj = use_json(argc, argv);
+
+ if (uj)
+ argc--;
+
+ bgp_vty_find_and_parse_afi_safi_bgp(vty, argv, argc, &idx, &afi, &safi,
+ &bgp, uj);
+ if (!idx)
+ return CMD_WARNING;
+
+ argv_find(argv, argc, "neighbors", &idx);
+ peer = peer_lookup_in_view(vty, bgp, argv[idx + 1]->arg, uj);
+ if (!peer)
+ return CMD_WARNING;
+
+ return bgp_peer_counts(vty, peer, afi, safi, uj);
+}
+
+#ifdef KEEP_OLD_VPN_COMMANDS
+DEFUN (show_ip_bgp_vpn_neighbor_prefix_counts,
+ show_ip_bgp_vpn_neighbor_prefix_counts_cmd,
+ "show [ip] bgp <vpnv4|vpnv6> all neighbors <A.B.C.D|X:X::X:X|WORD> prefix-counts [json]",
+ SHOW_STR
+ IP_STR
+ BGP_STR
+ BGP_VPNVX_HELP_STR
+ "Display information about all VPNv4 NLRIs\n"
+ "Detailed information on TCP and BGP neighbor connections\n"
+ "Neighbor to display information about\n"
+ "Neighbor to display information about\n"
+ "Neighbor on BGP configured interface\n"
+ "Display detailed prefix count information\n"
+ JSON_STR)
+{
+ int idx_peer = 6;
+ struct peer *peer;
+ bool uj = use_json(argc, argv);
+
+ peer = peer_lookup_in_view(vty, NULL, argv[idx_peer]->arg, uj);
+ if (!peer)
+ return CMD_WARNING;
+
+ return bgp_peer_counts(vty, peer, AFI_IP, SAFI_MPLS_VPN, uj);
+}
+
+DEFUN (show_ip_bgp_vpn_all_route_prefix,
+ show_ip_bgp_vpn_all_route_prefix_cmd,
+ "show [ip] bgp <vpnv4|vpnv6> all <A.B.C.D|A.B.C.D/M> [json]",
+ SHOW_STR
+ IP_STR
+ BGP_STR
+ BGP_VPNVX_HELP_STR
+ "Display information about all VPNv4 NLRIs\n"
+ "Network in the BGP routing table to display\n"
+ "Network in the BGP routing table to display\n"
+ JSON_STR)
+{
+ int idx = 0;
+ char *network = NULL;
+ struct bgp *bgp = bgp_get_default();
+ if (!bgp) {
+ vty_out(vty, "Can't find default instance\n");
+ return CMD_WARNING;
+ }
+
+ if (argv_find(argv, argc, "A.B.C.D", &idx))
+ network = argv[idx]->arg;
+ else if (argv_find(argv, argc, "A.B.C.D/M", &idx))
+ network = argv[idx]->arg;
+ else {
+ vty_out(vty, "Unable to figure out Network\n");
+ return CMD_WARNING;
+ }
+
+ return bgp_show_route(vty, bgp, network, AFI_IP, SAFI_MPLS_VPN, NULL, 0,
+ BGP_PATH_SHOW_ALL, RPKI_NOT_BEING_USED,
+ use_json(argc, argv));
+}
+#endif /* KEEP_OLD_VPN_COMMANDS */
+
+DEFUN (show_bgp_l2vpn_evpn_route_prefix,
+ show_bgp_l2vpn_evpn_route_prefix_cmd,
+ "show bgp l2vpn evpn <A.B.C.D|A.B.C.D/M|X:X::X:X|X:X::X:X/M> [json]",
+ SHOW_STR
+ BGP_STR
+ L2VPN_HELP_STR
+ EVPN_HELP_STR
+ "Network in the BGP routing table to display\n"
+ "Network in the BGP routing table to display\n"
+ "Network in the BGP routing table to display\n"
+ "Network in the BGP routing table to display\n"
+ JSON_STR)
+{
+ int idx = 0;
+ char *network = NULL;
+ int prefix_check = 0;
+
+ if (argv_find(argv, argc, "A.B.C.D", &idx) ||
+ argv_find(argv, argc, "X:X::X:X", &idx))
+ network = argv[idx]->arg;
+ else if (argv_find(argv, argc, "A.B.C.D/M", &idx) ||
+ argv_find(argv, argc, "X:X::X:X/M", &idx)) {
+ network = argv[idx]->arg;
+ prefix_check = 1;
+ } else {
+ vty_out(vty, "Unable to figure out Network\n");
+ return CMD_WARNING;
+ }
+ return bgp_show_route(vty, NULL, network, AFI_L2VPN, SAFI_EVPN, NULL,
+ prefix_check, BGP_PATH_SHOW_ALL,
+ RPKI_NOT_BEING_USED, use_json(argc, argv));
+}
+
+static void show_adj_route_header(struct vty *vty, struct peer *peer,
+ struct bgp_table *table, int *header1,
+ int *header2, json_object *json,
+ json_object *json_scode,
+ json_object *json_ocode, bool wide,
+ bool detail)
+{
+ uint64_t version = table ? table->version : 0;
+
+ if (*header1) {
+ if (json) {
+ json_object_int_add(json, "bgpTableVersion", version);
+ json_object_string_addf(json, "bgpLocalRouterId",
+ "%pI4", &peer->bgp->router_id);
+ json_object_int_add(json, "defaultLocPrf",
+ peer->bgp->default_local_pref);
+ json_object_int_add(json, "localAS",
+ peer->change_local_as
+ ? peer->change_local_as
+ : peer->local_as);
+ json_object_object_add(json, "bgpStatusCodes",
+ json_scode);
+ json_object_object_add(json, "bgpOriginCodes",
+ json_ocode);
+ } else {
+ vty_out(vty,
+ "BGP table version is %" PRIu64
+ ", local router ID is %pI4, vrf id ",
+ version, &peer->bgp->router_id);
+ if (peer->bgp->vrf_id == VRF_UNKNOWN)
+ vty_out(vty, "%s", VRFID_NONE_STR);
+ else
+ vty_out(vty, "%u", peer->bgp->vrf_id);
+ vty_out(vty, "\n");
+ vty_out(vty, "Default local pref %u, ",
+ peer->bgp->default_local_pref);
+ vty_out(vty, "local AS %u\n",
+ peer->change_local_as ? peer->change_local_as
+ : peer->local_as);
+ if (!detail) {
+ vty_out(vty, BGP_SHOW_SCODE_HEADER);
+ vty_out(vty, BGP_SHOW_NCODE_HEADER);
+ vty_out(vty, BGP_SHOW_OCODE_HEADER);
+ vty_out(vty, BGP_SHOW_RPKI_HEADER);
+ }
+ }
+ *header1 = 0;
+ }
+ if (*header2) {
+ if (!json && !detail)
+ vty_out(vty, (wide ? BGP_SHOW_HEADER_WIDE
+ : BGP_SHOW_HEADER));
+ *header2 = 0;
+ }
+}
+
+static void
+show_adj_route(struct vty *vty, struct peer *peer, struct bgp_table *table,
+ afi_t afi, safi_t safi, enum bgp_show_adj_route_type type,
+ const char *rmap_name, json_object *json, json_object *json_ar,
+ json_object *json_scode, json_object *json_ocode,
+ uint16_t show_flags, int *header1, int *header2, char *rd_str,
+ const struct prefix *match, unsigned long *output_count,
+ unsigned long *filtered_count)
+{
+ struct bgp_adj_in *ain = NULL;
+ struct bgp_adj_out *adj = NULL;
+ struct bgp_dest *dest;
+ struct bgp *bgp;
+ struct attr attr;
+ int ret;
+ struct update_subgroup *subgrp;
+ struct peer_af *paf = NULL;
+ bool route_filtered;
+ bool detail = CHECK_FLAG(show_flags, BGP_SHOW_OPT_ROUTES_DETAIL);
+ bool use_json = CHECK_FLAG(show_flags, BGP_SHOW_OPT_JSON);
+ bool wide = CHECK_FLAG(show_flags, BGP_SHOW_OPT_WIDE);
+ bool show_rd = ((safi == SAFI_MPLS_VPN) || (safi == SAFI_ENCAP)
+ || (safi == SAFI_EVPN))
+ ? true
+ : false;
+ int display = 0;
+ json_object *json_net = NULL;
+
+ bgp = peer->bgp;
+
+ /* If the user supplied a prefix, look for a matching route instead
+ * of walking the whole table.
+ */
+ if (match) {
+ dest = bgp_node_match(table, match);
+ if (!dest) {
+ if (!use_json)
+ vty_out(vty, "Network not in table\n");
+ return;
+ }
+
+ const struct prefix *rn_p = bgp_dest_get_prefix(dest);
+
+ if (rn_p->prefixlen != match->prefixlen) {
+ if (!use_json)
+ vty_out(vty, "Network not in table\n");
+ bgp_dest_unlock_node(dest);
+ return;
+ }
+
+ if (type == bgp_show_adj_route_received ||
+ type == bgp_show_adj_route_filtered) {
+ for (ain = dest->adj_in; ain; ain = ain->next) {
+ if (ain->peer == peer) {
+ attr = *ain->attr;
+ break;
+ }
+ }
+ /* bail out if if adj_out is empty, or
+ * if the prefix isn't in this peer's
+ * adj_in
+ */
+ if (!ain || ain->peer != peer) {
+ if (!use_json)
+ vty_out(vty, "Network not in table\n");
+ bgp_dest_unlock_node(dest);
+ return;
+ }
+ } else if (type == bgp_show_adj_route_advertised) {
+ bool peer_found = false;
+
+ RB_FOREACH (adj, bgp_adj_out_rb, &dest->adj_out) {
+ SUBGRP_FOREACH_PEER (adj->subgroup, paf) {
+ if (paf->peer == peer && adj->attr) {
+ attr = *adj->attr;
+ peer_found = true;
+ break;
+ }
+ }
+ if (peer_found)
+ break;
+ }
+ /* bail out if if adj_out is empty, or
+ * if the prefix isn't in this peer's
+ * adj_out
+ */
+ if (!paf || !peer_found) {
+ if (!use_json)
+ vty_out(vty, "Network not in table\n");
+ bgp_dest_unlock_node(dest);
+ return;
+ }
+ }
+
+ ret = bgp_output_modifier(peer, rn_p, &attr, afi, safi,
+ rmap_name);
+
+ if (ret != RMAP_DENY) {
+ show_adj_route_header(vty, peer, table, header1,
+ header2, json, json_scode,
+ json_ocode, wide, detail);
+
+ if (use_json)
+ json_net = json_object_new_object();
+
+ bgp_show_path_info(NULL /* prefix_rd */, dest, vty, bgp,
+ afi, safi, json_net,
+ BGP_PATH_SHOW_ALL, &display,
+ RPKI_NOT_BEING_USED);
+ if (use_json)
+ json_object_object_addf(json_ar, json_net,
+ "%pFX", rn_p);
+ (*output_count)++;
+ } else
+ (*filtered_count)++;
+
+ bgp_attr_flush(&attr);
+ bgp_dest_unlock_node(dest);
+ return;
+ }
+
+
+ subgrp = peer_subgroup(peer, afi, safi);
+
+ if (type == bgp_show_adj_route_advertised && subgrp
+ && CHECK_FLAG(subgrp->sflags, SUBGRP_STATUS_DEFAULT_ORIGINATE)) {
+ if (use_json) {
+ json_object_int_add(json, "bgpTableVersion",
+ table->version);
+ json_object_string_addf(json, "bgpLocalRouterId",
+ "%pI4", &bgp->router_id);
+ json_object_int_add(json, "defaultLocPrf",
+ bgp->default_local_pref);
+ json_object_int_add(json, "localAS",
+ peer->change_local_as
+ ? peer->change_local_as
+ : peer->local_as);
+ json_object_object_add(json, "bgpStatusCodes",
+ json_scode);
+ json_object_object_add(json, "bgpOriginCodes",
+ json_ocode);
+ json_object_string_add(
+ json, "bgpOriginatingDefaultNetwork",
+ (afi == AFI_IP) ? "0.0.0.0/0" : "::/0");
+ } else {
+ vty_out(vty,
+ "BGP table version is %" PRIu64
+ ", local router ID is %pI4, vrf id ",
+ table->version, &bgp->router_id);
+ if (bgp->vrf_id == VRF_UNKNOWN)
+ vty_out(vty, "%s", VRFID_NONE_STR);
+ else
+ vty_out(vty, "%u", bgp->vrf_id);
+ vty_out(vty, "\n");
+ vty_out(vty, "Default local pref %u, ",
+ bgp->default_local_pref);
+ vty_out(vty, "local AS %u\n",
+ peer->change_local_as ? peer->change_local_as
+ : peer->local_as);
+ if (!detail) {
+ vty_out(vty, BGP_SHOW_SCODE_HEADER);
+ vty_out(vty, BGP_SHOW_NCODE_HEADER);
+ vty_out(vty, BGP_SHOW_OCODE_HEADER);
+ vty_out(vty, BGP_SHOW_RPKI_HEADER);
+ }
+
+ vty_out(vty, "Originating default network %s\n\n",
+ (afi == AFI_IP) ? "0.0.0.0/0" : "::/0");
+ }
+ (*output_count)++;
+ *header1 = 0;
+ }
+
+ for (dest = bgp_table_top(table); dest; dest = bgp_route_next(dest)) {
+ if (type == bgp_show_adj_route_received
+ || type == bgp_show_adj_route_filtered) {
+ for (ain = dest->adj_in; ain; ain = ain->next) {
+ if (ain->peer != peer)
+ continue;
+ show_adj_route_header(vty, peer, table, header1,
+ header2, json, json_scode,
+ json_ocode, wide, detail);
+
+ if ((safi == SAFI_MPLS_VPN)
+ || (safi == SAFI_ENCAP)
+ || (safi == SAFI_EVPN)) {
+ if (use_json)
+ json_object_string_add(
+ json_ar, "rd", rd_str);
+ else if (show_rd && rd_str) {
+ vty_out(vty,
+ "Route Distinguisher: %s\n",
+ rd_str);
+ show_rd = false;
+ }
+ }
+
+ attr = *ain->attr;
+ route_filtered = false;
+
+ /* Filter prefix using distribute list,
+ * filter list or prefix list
+ */
+ const struct prefix *rn_p =
+ bgp_dest_get_prefix(dest);
+ if ((bgp_input_filter(peer, rn_p, &attr, afi,
+ safi))
+ == FILTER_DENY)
+ route_filtered = true;
+
+ /* Filter prefix using route-map */
+ ret = bgp_input_modifier(peer, rn_p, &attr, afi,
+ safi, rmap_name, NULL,
+ 0, NULL);
+
+ if (type == bgp_show_adj_route_filtered &&
+ !route_filtered && ret != RMAP_DENY) {
+ bgp_attr_flush(&attr);
+ continue;
+ }
+
+ if (type == bgp_show_adj_route_received
+ && (route_filtered || ret == RMAP_DENY))
+ (*filtered_count)++;
+
+ if (detail) {
+ if (use_json)
+ json_net =
+ json_object_new_object();
+
+ struct bgp_path_info bpi;
+ struct bgp_dest buildit = *dest;
+ struct bgp_dest *pass_in;
+
+ if (route_filtered ||
+ ret == RMAP_DENY) {
+ bpi.attr = &attr;
+ bpi.peer = peer;
+ buildit.info = &bpi;
+
+ pass_in = &buildit;
+ } else
+ pass_in = dest;
+ bgp_show_path_info(
+ NULL, pass_in, vty, bgp, afi,
+ safi, json_net,
+ BGP_PATH_SHOW_ALL, &display,
+ RPKI_NOT_BEING_USED);
+ if (use_json)
+ json_object_object_addf(
+ json_ar, json_net,
+ "%pFX", rn_p);
+ } else
+ route_vty_out_tmp(vty, dest, rn_p,
+ &attr, safi, use_json,
+ json_ar, wide);
+ bgp_attr_flush(&attr);
+ (*output_count)++;
+ }
+ } else if (type == bgp_show_adj_route_advertised) {
+ RB_FOREACH (adj, bgp_adj_out_rb, &dest->adj_out)
+ SUBGRP_FOREACH_PEER (adj->subgroup, paf) {
+ if (paf->peer != peer || !adj->attr)
+ continue;
+
+ show_adj_route_header(
+ vty, peer, table, header1,
+ header2, json, json_scode,
+ json_ocode, wide, detail);
+
+ const struct prefix *rn_p =
+ bgp_dest_get_prefix(dest);
+
+ attr = *adj->attr;
+ ret = bgp_output_modifier(
+ peer, rn_p, &attr, afi, safi,
+ rmap_name);
+
+ if (ret != RMAP_DENY) {
+ if ((safi == SAFI_MPLS_VPN)
+ || (safi == SAFI_ENCAP)
+ || (safi == SAFI_EVPN)) {
+ if (use_json)
+ json_object_string_add(
+ json_ar,
+ "rd",
+ rd_str);
+ else if (show_rd
+ && rd_str) {
+ vty_out(vty,
+ "Route Distinguisher: %s\n",
+ rd_str);
+ show_rd = false;
+ }
+ }
+ if (detail) {
+ if (use_json)
+ json_net =
+ json_object_new_object();
+ bgp_show_path_info(
+ NULL /* prefix_rd
+ */
+ ,
+ dest, vty, bgp,
+ afi, safi,
+ json_net,
+ BGP_PATH_SHOW_ALL,
+ &display,
+ RPKI_NOT_BEING_USED);
+ if (use_json)
+ json_object_object_addf(
+ json_ar,
+ json_net,
+ "%pFX",
+ rn_p);
+ } else
+ route_vty_out_tmp(
+ vty, dest, rn_p,
+ &attr, safi,
+ use_json,
+ json_ar, wide);
+ (*output_count)++;
+ } else {
+ (*filtered_count)++;
+ }
+
+ bgp_attr_flush(&attr);
+ }
+ } else if (type == bgp_show_adj_route_bestpath) {
+ struct bgp_path_info *pi;
+
+ show_adj_route_header(vty, peer, table, header1,
+ header2, json, json_scode,
+ json_ocode, wide, detail);
+
+ const struct prefix *rn_p = bgp_dest_get_prefix(dest);
+
+ for (pi = bgp_dest_get_bgp_path_info(dest); pi;
+ pi = pi->next) {
+ if (pi->peer != peer)
+ continue;
+
+ if (!CHECK_FLAG(pi->flags, BGP_PATH_SELECTED))
+ continue;
+
+ if (detail) {
+ if (use_json)
+ json_net =
+ json_object_new_object();
+ bgp_show_path_info(
+ NULL /* prefix_rd */, dest, vty,
+ bgp, afi, safi, json_net,
+ BGP_PATH_SHOW_BESTPATH,
+ &display, RPKI_NOT_BEING_USED);
+ if (use_json)
+ json_object_object_addf(
+ json_ar, json_net,
+ "%pFX", rn_p);
+ } else
+ route_vty_out_tmp(
+ vty, dest, rn_p, pi->attr, safi,
+ use_json, json_ar, wide);
+ (*output_count)++;
+ }
+ }
+ }
+}
+
+static int peer_adj_routes(struct vty *vty, struct peer *peer, afi_t afi,
+ safi_t safi, enum bgp_show_adj_route_type type,
+ const char *rmap_name, const struct prefix *match,
+ uint16_t show_flags)
+{
+ struct bgp *bgp;
+ struct bgp_table *table;
+ json_object *json = NULL;
+ json_object *json_scode = NULL;
+ json_object *json_ocode = NULL;
+ json_object *json_ar = NULL;
+ bool use_json = CHECK_FLAG(show_flags, BGP_SHOW_OPT_JSON);
+
+ /* Init BGP headers here so they're only displayed once
+ * even if 'table' is 2-tier (MPLS_VPN, ENCAP, EVPN).
+ */
+ int header1 = 1;
+ int header2 = 1;
+
+ /*
+ * Initialize variables for each RD
+ * All prefixes under an RD is aggregated within "json_routes"
+ */
+ char rd_str[BUFSIZ] = {0};
+ json_object *json_routes = NULL;
+
+
+ /* For 2-tier tables, prefix counts need to be
+ * maintained across multiple runs of show_adj_route()
+ */
+ unsigned long output_count_per_rd;
+ unsigned long filtered_count_per_rd;
+ unsigned long output_count = 0;
+ unsigned long filtered_count = 0;
+
+ if (use_json) {
+ json = json_object_new_object();
+ json_ar = json_object_new_object();
+ json_scode = json_object_new_object();
+ json_ocode = json_object_new_object();
+#if CONFDATE > 20231208
+CPP_NOTICE("Drop `bgpStatusCodes` from JSON outputs")
+#endif
+ json_object_string_add(json_scode, "suppressed", "s");
+ json_object_string_add(json_scode, "damped", "d");
+ json_object_string_add(json_scode, "history", "h");
+ json_object_string_add(json_scode, "valid", "*");
+ json_object_string_add(json_scode, "best", ">");
+ json_object_string_add(json_scode, "multipath", "=");
+ json_object_string_add(json_scode, "internal", "i");
+ json_object_string_add(json_scode, "ribFailure", "r");
+ json_object_string_add(json_scode, "stale", "S");
+ json_object_string_add(json_scode, "removed", "R");
+
+#if CONFDATE > 20231208
+CPP_NOTICE("Drop `bgpOriginCodes` from JSON outputs")
+#endif
+ json_object_string_add(json_ocode, "igp", "i");
+ json_object_string_add(json_ocode, "egp", "e");
+ json_object_string_add(json_ocode, "incomplete", "?");
+ }
+
+ if (!peer || !peer->afc[afi][safi]) {
+ if (use_json) {
+ json_object_string_add(
+ json, "warning",
+ "No such neighbor or address family");
+ vty_out(vty, "%s\n", json_object_to_json_string(json));
+ json_object_free(json);
+ json_object_free(json_ar);
+ json_object_free(json_scode);
+ json_object_free(json_ocode);
+ } else
+ vty_out(vty, "%% No such neighbor or address family\n");
+
+ return CMD_WARNING;
+ }
+
+ if ((type == bgp_show_adj_route_received
+ || type == bgp_show_adj_route_filtered)
+ && !CHECK_FLAG(peer->af_flags[afi][safi],
+ PEER_FLAG_SOFT_RECONFIG)) {
+ if (use_json) {
+ json_object_string_add(
+ json, "warning",
+ "Inbound soft reconfiguration not enabled");
+ vty_out(vty, "%s\n", json_object_to_json_string(json));
+ json_object_free(json);
+ json_object_free(json_ar);
+ json_object_free(json_scode);
+ json_object_free(json_ocode);
+ } else
+ vty_out(vty,
+ "%% Inbound soft reconfiguration not enabled\n");
+
+ return CMD_WARNING;
+ }
+
+ bgp = peer->bgp;
+
+ /* labeled-unicast routes live in the unicast table */
+ if (safi == SAFI_LABELED_UNICAST)
+ table = bgp->rib[afi][SAFI_UNICAST];
+ else
+ table = bgp->rib[afi][safi];
+
+ if ((safi == SAFI_MPLS_VPN) || (safi == SAFI_ENCAP)
+ || (safi == SAFI_EVPN)) {
+
+ struct bgp_dest *dest;
+
+ for (dest = bgp_table_top(table); dest;
+ dest = bgp_route_next(dest)) {
+ table = bgp_dest_get_bgp_table_info(dest);
+ if (!table)
+ continue;
+
+ output_count_per_rd = 0;
+ filtered_count_per_rd = 0;
+
+ if (use_json)
+ json_routes = json_object_new_object();
+
+ const struct prefix_rd *prd;
+ prd = (const struct prefix_rd *)bgp_dest_get_prefix(
+ dest);
+
+ prefix_rd2str(prd, rd_str, sizeof(rd_str),
+ bgp->asnotation);
+
+ show_adj_route(
+ vty, peer, table, afi, safi, type, rmap_name,
+ json, json_routes, json_scode, json_ocode,
+ show_flags, &header1, &header2, rd_str, match,
+ &output_count_per_rd, &filtered_count_per_rd);
+
+ /* Don't include an empty RD in the output! */
+ if (json_routes && (output_count_per_rd > 0))
+ json_object_object_add(json_ar, rd_str,
+ json_routes);
+
+ output_count += output_count_per_rd;
+ filtered_count += filtered_count_per_rd;
+ }
+ } else
+ show_adj_route(vty, peer, table, afi, safi, type, rmap_name,
+ json, json_ar, json_scode, json_ocode,
+ show_flags, &header1, &header2, rd_str, match,
+ &output_count, &filtered_count);
+
+ if (use_json) {
+ if (type == bgp_show_adj_route_advertised)
+ json_object_object_add(json, "advertisedRoutes",
+ json_ar);
+ else
+ json_object_object_add(json, "receivedRoutes", json_ar);
+ json_object_int_add(json, "totalPrefixCounter", output_count);
+ json_object_int_add(json, "filteredPrefixCounter",
+ filtered_count);
+
+ /*
+ * These fields only give up ownership to `json` when `header1`
+ * is used (set to zero). See code in `show_adj_route` and
+ * `show_adj_route_header`.
+ */
+ if (header1 == 1) {
+ json_object_free(json_scode);
+ json_object_free(json_ocode);
+ }
+
+ /*
+ * This is an extremely expensive operation at scale
+ * and non-pretty reduces memory footprint significantly.
+ */
+ vty_json_no_pretty(vty, json);
+ } else if (output_count > 0) {
+ if (!match && filtered_count > 0)
+ vty_out(vty,
+ "\nTotal number of prefixes %ld (%ld filtered)\n",
+ output_count, filtered_count);
+ else
+ vty_out(vty, "\nTotal number of prefixes %ld\n",
+ output_count);
+ }
+
+ return CMD_SUCCESS;
+}
+
+DEFPY (show_ip_bgp_instance_neighbor_bestpath_route,
+ show_ip_bgp_instance_neighbor_bestpath_route_cmd,
+ "show [ip] bgp [<view|vrf> VIEWVRFNAME] [" BGP_AFI_CMD_STR " [" BGP_SAFI_WITH_LABEL_CMD_STR "]] neighbors <A.B.C.D|X:X::X:X|WORD> bestpath-routes [detail$detail] [json$uj | wide$wide]",
+ SHOW_STR
+ IP_STR
+ BGP_STR
+ BGP_INSTANCE_HELP_STR
+ BGP_AFI_HELP_STR
+ BGP_SAFI_WITH_LABEL_HELP_STR
+ "Detailed information on TCP and BGP neighbor connections\n"
+ "Neighbor to display information about\n"
+ "Neighbor to display information about\n"
+ "Neighbor on BGP configured interface\n"
+ "Display the routes selected by best path\n"
+ "Display detailed version of routes\n"
+ JSON_STR
+ "Increase table width for longer prefixes\n")
+{
+ afi_t afi = AFI_IP6;
+ safi_t safi = SAFI_UNICAST;
+ char *rmap_name = NULL;
+ char *peerstr = NULL;
+ struct bgp *bgp = NULL;
+ struct peer *peer;
+ enum bgp_show_adj_route_type type = bgp_show_adj_route_bestpath;
+ int idx = 0;
+ uint16_t show_flags = 0;
+
+ if (detail)
+ SET_FLAG(show_flags, BGP_SHOW_OPT_ROUTES_DETAIL);
+
+ if (uj)
+ SET_FLAG(show_flags, BGP_SHOW_OPT_JSON);
+
+ if (wide)
+ SET_FLAG(show_flags, BGP_SHOW_OPT_WIDE);
+
+ bgp_vty_find_and_parse_afi_safi_bgp(vty, argv, argc, &idx, &afi, &safi,
+ &bgp, uj);
+
+ if (!idx)
+ return CMD_WARNING;
+
+ argv_find(argv, argc, "neighbors", &idx);
+ peerstr = argv[++idx]->arg;
+
+ peer = peer_lookup_in_view(vty, bgp, peerstr, uj);
+ if (!peer)
+ return CMD_WARNING;
+
+ return peer_adj_routes(vty, peer, afi, safi, type, rmap_name, NULL,
+ show_flags);
+}
+
+DEFPY(show_ip_bgp_instance_neighbor_advertised_route,
+ show_ip_bgp_instance_neighbor_advertised_route_cmd,
+ "show [ip] bgp [<view|vrf> VIEWVRFNAME] [" BGP_AFI_CMD_STR " [" BGP_SAFI_WITH_LABEL_CMD_STR "]] [all$all] neighbors <A.B.C.D|X:X::X:X|WORD> <advertised-routes|received-routes|filtered-routes> [route-map RMAP_NAME$route_map] [<A.B.C.D/M|X:X::X:X/M>$prefix | detail$detail] [json$uj | wide$wide]",
+ SHOW_STR
+ IP_STR
+ BGP_STR
+ BGP_INSTANCE_HELP_STR
+ BGP_AFI_HELP_STR
+ BGP_SAFI_WITH_LABEL_HELP_STR
+ "Display the entries for all address families\n"
+ "Detailed information on TCP and BGP neighbor connections\n"
+ "Neighbor to display information about\n"
+ "Neighbor to display information about\n"
+ "Neighbor on BGP configured interface\n"
+ "Display the routes advertised to a BGP neighbor\n"
+ "Display the received routes from neighbor\n"
+ "Display the filtered routes received from neighbor\n"
+ "Route-map to modify the attributes\n"
+ "Name of the route map\n"
+ "IPv4 prefix\n"
+ "IPv6 prefix\n"
+ "Display detailed version of routes\n"
+ JSON_STR
+ "Increase table width for longer prefixes\n")
+{
+ afi_t afi = AFI_IP6;
+ safi_t safi = SAFI_UNICAST;
+ char *peerstr = NULL;
+ struct bgp *bgp = NULL;
+ struct peer *peer;
+ enum bgp_show_adj_route_type type = bgp_show_adj_route_advertised;
+ int idx = 0;
+ bool first = true;
+ uint16_t show_flags = 0;
+ struct listnode *node;
+ struct bgp *abgp;
+
+ if (detail || prefix_str)
+ SET_FLAG(show_flags, BGP_SHOW_OPT_ROUTES_DETAIL);
+
+ if (uj) {
+ argc--;
+ SET_FLAG(show_flags, BGP_SHOW_OPT_JSON);
+ }
+
+ if (all) {
+ SET_FLAG(show_flags, BGP_SHOW_OPT_AFI_ALL);
+ if (argv_find(argv, argc, "ipv4", &idx))
+ SET_FLAG(show_flags, BGP_SHOW_OPT_AFI_IP);
+
+ if (argv_find(argv, argc, "ipv6", &idx))
+ SET_FLAG(show_flags, BGP_SHOW_OPT_AFI_IP6);
+ }
+
+ if (wide)
+ SET_FLAG(show_flags, BGP_SHOW_OPT_WIDE);
+
+ bgp_vty_find_and_parse_afi_safi_bgp(vty, argv, argc, &idx, &afi, &safi,
+ &bgp, uj);
+ if (!idx)
+ return CMD_WARNING;
+
+ /* neighbors <A.B.C.D|X:X::X:X|WORD> */
+ argv_find(argv, argc, "neighbors", &idx);
+ peerstr = argv[++idx]->arg;
+
+ peer = peer_lookup_in_view(vty, bgp, peerstr, uj);
+ if (!peer)
+ return CMD_WARNING;
+
+ if (argv_find(argv, argc, "advertised-routes", &idx))
+ type = bgp_show_adj_route_advertised;
+ else if (argv_find(argv, argc, "received-routes", &idx))
+ type = bgp_show_adj_route_received;
+ else if (argv_find(argv, argc, "filtered-routes", &idx))
+ type = bgp_show_adj_route_filtered;
+
+ if (!all)
+ return peer_adj_routes(vty, peer, afi, safi, type, route_map,
+ prefix_str ? prefix : NULL, show_flags);
+ if (uj)
+ vty_out(vty, "{\n");
+
+ if (CHECK_FLAG(show_flags, BGP_SHOW_OPT_AFI_IP)
+ || CHECK_FLAG(show_flags, BGP_SHOW_OPT_AFI_IP6)) {
+ afi = CHECK_FLAG(show_flags, BGP_SHOW_OPT_AFI_IP) ? AFI_IP
+ : AFI_IP6;
+ for (ALL_LIST_ELEMENTS_RO(bm->bgp, node, abgp)) {
+ FOREACH_SAFI (safi) {
+ if (!bgp_afi_safi_peer_exists(abgp, afi, safi))
+ continue;
+
+ if (uj) {
+ if (first)
+ first = false;
+ else
+ vty_out(vty, ",\n");
+ vty_out(vty, "\"%s\":",
+ get_afi_safi_str(afi, safi,
+ true));
+ } else
+ vty_out(vty,
+ "\nFor address family: %s\n",
+ get_afi_safi_str(afi, safi,
+ false));
+
+ peer_adj_routes(vty, peer, afi, safi, type,
+ route_map, prefix, show_flags);
+ }
+ }
+ } else {
+ for (ALL_LIST_ELEMENTS_RO(bm->bgp, node, abgp)) {
+ FOREACH_AFI_SAFI (afi, safi) {
+ if (!bgp_afi_safi_peer_exists(abgp, afi, safi))
+ continue;
+
+ if (uj) {
+ if (first)
+ first = false;
+ else
+ vty_out(vty, ",\n");
+ vty_out(vty, "\"%s\":",
+ get_afi_safi_str(afi, safi,
+ true));
+ } else
+ vty_out(vty,
+ "\nFor address family: %s\n",
+ get_afi_safi_str(afi, safi,
+ false));
+
+ peer_adj_routes(vty, peer, afi, safi, type,
+ route_map, prefix, show_flags);
+ }
+ }
+ }
+ if (uj)
+ vty_out(vty, "}\n");
+
+ return CMD_SUCCESS;
+}
+
+DEFUN (show_ip_bgp_neighbor_received_prefix_filter,
+ show_ip_bgp_neighbor_received_prefix_filter_cmd,
+ "show [ip] bgp [<view|vrf> VIEWVRFNAME] [<ipv4|ipv6> [unicast]] neighbors <A.B.C.D|X:X::X:X|WORD> received prefix-filter [json]",
+ SHOW_STR
+ IP_STR
+ BGP_STR
+ BGP_INSTANCE_HELP_STR
+ BGP_AF_STR
+ BGP_AF_STR
+ BGP_AF_MODIFIER_STR
+ "Detailed information on TCP and BGP neighbor connections\n"
+ "Neighbor to display information about\n"
+ "Neighbor to display information about\n"
+ "Neighbor on BGP configured interface\n"
+ "Display information received from a BGP neighbor\n"
+ "Display the prefixlist filter\n"
+ JSON_STR)
+{
+ afi_t afi = AFI_IP6;
+ safi_t safi = SAFI_UNICAST;
+ char *peerstr = NULL;
+ char name[BUFSIZ];
+ struct peer *peer;
+ int count;
+ int idx = 0;
+ struct bgp *bgp = NULL;
+ bool uj = use_json(argc, argv);
+
+ if (uj)
+ argc--;
+
+ bgp_vty_find_and_parse_afi_safi_bgp(vty, argv, argc, &idx, &afi, &safi,
+ &bgp, uj);
+ if (!idx)
+ return CMD_WARNING;
+
+ /* neighbors <A.B.C.D|X:X::X:X|WORD> */
+ argv_find(argv, argc, "neighbors", &idx);
+ peerstr = argv[++idx]->arg;
+
+ peer = peer_lookup_in_view(vty, bgp, peerstr, uj);
+ if (!peer)
+ return CMD_WARNING;
+
+ snprintf(name, sizeof(name), "%s.%d.%d", peer->host, afi, safi);
+ count = prefix_bgp_show_prefix_list(NULL, afi, name, uj);
+ if (count) {
+ if (!uj)
+ vty_out(vty, "Address Family: %s\n",
+ get_afi_safi_str(afi, safi, false));
+ prefix_bgp_show_prefix_list(vty, afi, name, uj);
+ } else {
+ if (uj)
+ vty_out(vty, "{}\n");
+ else
+ vty_out(vty, "No functional output\n");
+ }
+
+ return CMD_SUCCESS;
+}
+
+static int bgp_show_neighbor_route(struct vty *vty, struct peer *peer,
+ afi_t afi, safi_t safi,
+ enum bgp_show_type type, bool use_json)
+{
+ uint16_t show_flags = 0;
+
+ if (use_json)
+ SET_FLAG(show_flags, BGP_SHOW_OPT_JSON);
+
+ if (!peer || !peer->afc[afi][safi]) {
+ if (use_json) {
+ json_object *json_no = NULL;
+ json_no = json_object_new_object();
+ json_object_string_add(
+ json_no, "warning",
+ "No such neighbor or address family");
+ vty_out(vty, "%s\n",
+ json_object_to_json_string(json_no));
+ json_object_free(json_no);
+ } else
+ vty_out(vty, "%% No such neighbor or address family\n");
+ return CMD_WARNING;
+ }
+
+ /* labeled-unicast routes live in the unicast table */
+ if (safi == SAFI_LABELED_UNICAST)
+ safi = SAFI_UNICAST;
+
+ return bgp_show(vty, peer->bgp, afi, safi, type, &peer->connection->su,
+ show_flags, RPKI_NOT_BEING_USED);
+}
+
+/*
+ * Used for "detailed" output for cmds like show bgp <afi> <safi> (or)
+ * show bgp <vrf> (or) show bgp <vrf> <afi> <safi>
+ */
+DEFPY(show_ip_bgp_vrf_afi_safi_routes_detailed,
+ show_ip_bgp_vrf_afi_safi_routes_detailed_cmd,
+ "show [ip] bgp [<view|vrf> VIEWVRFNAME$vrf_name] ["BGP_AFI_CMD_STR" ["BGP_SAFI_WITH_LABEL_CMD_STR"]] detail [json$uj]",
+ SHOW_STR
+ IP_STR
+ BGP_STR
+ BGP_INSTANCE_HELP_STR
+ BGP_AFI_HELP_STR
+ BGP_SAFI_WITH_LABEL_HELP_STR
+ "Detailed information\n"
+ JSON_STR)
+{
+ afi_t afi = AFI_IP6;
+ safi_t safi = SAFI_UNICAST;
+ struct bgp *bgp = NULL;
+ int idx = 0;
+ uint16_t show_flags = BGP_SHOW_OPT_ROUTES_DETAIL;
+
+ if (uj)
+ SET_FLAG(show_flags, BGP_SHOW_OPT_JSON);
+
+ bgp_vty_find_and_parse_afi_safi_bgp(vty, argv, argc, &idx, &afi, &safi,
+ &bgp, uj);
+ if (!idx)
+ return CMD_WARNING;
+ /* 'vrf all' case to iterate all vrfs & show output per vrf instance */
+ if (vrf_name && strmatch(vrf_name, "all")) {
+ bgp_show_all_instances_routes_vty(vty, afi, safi, show_flags);
+ return CMD_SUCCESS;
+ }
+
+ /* All other cases except vrf all */
+ return bgp_show(vty, bgp, afi, safi, bgp_show_type_detail, NULL,
+ show_flags, RPKI_NOT_BEING_USED);
+}
+
+DEFUN (show_ip_bgp_neighbor_routes,
+ show_ip_bgp_neighbor_routes_cmd,
+ "show [ip] bgp [<view|vrf> VIEWVRFNAME] ["BGP_AFI_CMD_STR" ["BGP_SAFI_WITH_LABEL_CMD_STR"]] neighbors <A.B.C.D|X:X::X:X|WORD> <flap-statistics|dampened-routes|routes> [json]",
+ SHOW_STR
+ IP_STR
+ BGP_STR
+ BGP_INSTANCE_HELP_STR
+ BGP_AFI_HELP_STR
+ BGP_SAFI_WITH_LABEL_HELP_STR
+ "Detailed information on TCP and BGP neighbor connections\n"
+ "Neighbor to display information about\n"
+ "Neighbor to display information about\n"
+ "Neighbor on BGP configured interface\n"
+ "Display flap statistics of the routes learned from neighbor\n"
+ "Display the dampened routes received from neighbor\n"
+ "Display routes learned from neighbor\n"
+ JSON_STR)
+{
+ char *peerstr = NULL;
+ struct bgp *bgp = NULL;
+ afi_t afi = AFI_IP6;
+ safi_t safi = SAFI_UNICAST;
+ struct peer *peer;
+ enum bgp_show_type sh_type = bgp_show_type_neighbor;
+ int idx = 0;
+ bool uj = use_json(argc, argv);
+
+ if (uj)
+ argc--;
+
+ bgp_vty_find_and_parse_afi_safi_bgp(vty, argv, argc, &idx, &afi, &safi,
+ &bgp, uj);
+ if (!idx)
+ return CMD_WARNING;
+
+ /* neighbors <A.B.C.D|X:X::X:X|WORD> */
+ argv_find(argv, argc, "neighbors", &idx);
+ peerstr = argv[++idx]->arg;
+
+ peer = peer_lookup_in_view(vty, bgp, peerstr, uj);
+ if (!peer)
+ return CMD_WARNING;
+
+ if (argv_find(argv, argc, "flap-statistics", &idx))
+ sh_type = bgp_show_type_flap_neighbor;
+ else if (argv_find(argv, argc, "dampened-routes", &idx))
+ sh_type = bgp_show_type_damp_neighbor;
+ else if (argv_find(argv, argc, "routes", &idx))
+ sh_type = bgp_show_type_neighbor;
+
+ return bgp_show_neighbor_route(vty, peer, afi, safi, sh_type, uj);
+}
+
+struct bgp_table *bgp_distance_table[AFI_MAX][SAFI_MAX];
+
+struct bgp_distance {
+ /* Distance value for the IP source prefix. */
+ uint8_t distance;
+
+ /* Name of the access-list to be matched. */
+ char *access_list;
+};
+
+DEFUN (show_bgp_afi_vpn_rd_route,
+ show_bgp_afi_vpn_rd_route_cmd,
+ "show bgp "BGP_AFI_CMD_STR" vpn rd <ASN:NN_OR_IP-ADDRESS:NN|all> <A.B.C.D/M|X:X::X:X/M> [json]",
+ SHOW_STR
+ BGP_STR
+ BGP_AFI_HELP_STR
+ BGP_AF_MODIFIER_STR
+ "Display information for a route distinguisher\n"
+ "Route Distinguisher\n"
+ "All Route Distinguishers\n"
+ "Network in the BGP routing table to display\n"
+ "Network in the BGP routing table to display\n"
+ JSON_STR)
+{
+ int ret;
+ struct prefix_rd prd;
+ afi_t afi = AFI_MAX;
+ int idx = 0;
+
+ if (!argv_find_and_parse_afi(argv, argc, &idx, &afi)) {
+ vty_out(vty, "%% Malformed Address Family\n");
+ return CMD_WARNING;
+ }
+
+ if (!strcmp(argv[5]->arg, "all"))
+ return bgp_show_route(vty, NULL, argv[6]->arg, afi,
+ SAFI_MPLS_VPN, NULL, 0, BGP_PATH_SHOW_ALL,
+ RPKI_NOT_BEING_USED,
+ use_json(argc, argv));
+
+ ret = str2prefix_rd(argv[5]->arg, &prd);
+ if (!ret) {
+ vty_out(vty, "%% Malformed Route Distinguisher\n");
+ return CMD_WARNING;
+ }
+
+ return bgp_show_route(vty, NULL, argv[6]->arg, afi, SAFI_MPLS_VPN, &prd,
+ 0, BGP_PATH_SHOW_ALL, RPKI_NOT_BEING_USED,
+ use_json(argc, argv));
+}
+
+static struct bgp_distance *bgp_distance_new(void)
+{
+ return XCALLOC(MTYPE_BGP_DISTANCE, sizeof(struct bgp_distance));
+}
+
+static void bgp_distance_free(struct bgp_distance *bdistance)
+{
+ XFREE(MTYPE_BGP_DISTANCE, bdistance);
+}
+
+static int bgp_distance_set(struct vty *vty, const char *distance_str,
+ const char *ip_str, const char *access_list_str)
+{
+ int ret;
+ afi_t afi;
+ safi_t safi;
+ struct prefix p;
+ uint8_t distance;
+ struct bgp_dest *dest;
+ struct bgp_distance *bdistance;
+
+ afi = bgp_node_afi(vty);
+ safi = bgp_node_safi(vty);
+
+ ret = str2prefix(ip_str, &p);
+ if (ret == 0) {
+ vty_out(vty, "Malformed prefix\n");
+ return CMD_WARNING_CONFIG_FAILED;
+ }
+
+ distance = atoi(distance_str);
+
+ /* Get BGP distance node. */
+ dest = bgp_node_get(bgp_distance_table[afi][safi], &p);
+ bdistance = bgp_dest_get_bgp_distance_info(dest);
+ if (bdistance)
+ bgp_dest_unlock_node(dest);
+ else {
+ bdistance = bgp_distance_new();
+ bgp_dest_set_bgp_distance_info(dest, bdistance);
+ }
+
+ /* Set distance value. */
+ bdistance->distance = distance;
+
+ /* Reset access-list configuration. */
+ XFREE(MTYPE_AS_LIST, bdistance->access_list);
+ if (access_list_str)
+ bdistance->access_list =
+ XSTRDUP(MTYPE_AS_LIST, access_list_str);
+
+ return CMD_SUCCESS;
+}
+
+static int bgp_distance_unset(struct vty *vty, const char *distance_str,
+ const char *ip_str, const char *access_list_str)
+{
+ int ret;
+ afi_t afi;
+ safi_t safi;
+ struct prefix p;
+ int distance;
+ struct bgp_dest *dest;
+ struct bgp_distance *bdistance;
+
+ afi = bgp_node_afi(vty);
+ safi = bgp_node_safi(vty);
+
+ ret = str2prefix(ip_str, &p);
+ if (ret == 0) {
+ vty_out(vty, "Malformed prefix\n");
+ return CMD_WARNING_CONFIG_FAILED;
+ }
+
+ dest = bgp_node_lookup(bgp_distance_table[afi][safi], &p);
+ if (!dest) {
+ vty_out(vty, "Can't find specified prefix\n");
+ return CMD_WARNING_CONFIG_FAILED;
+ }
+
+ bdistance = bgp_dest_get_bgp_distance_info(dest);
+ distance = atoi(distance_str);
+
+ if (bdistance->distance != distance) {
+ vty_out(vty, "Distance does not match configured\n");
+ bgp_dest_unlock_node(dest);
+ return CMD_WARNING_CONFIG_FAILED;
+ }
+
+ XFREE(MTYPE_AS_LIST, bdistance->access_list);
+ bgp_distance_free(bdistance);
+
+ bgp_dest_set_bgp_path_info(dest, NULL);
+ dest = bgp_dest_unlock_node(dest);
+ assert(dest);
+ bgp_dest_unlock_node(dest);
+
+ return CMD_SUCCESS;
+}
+
+/* Apply BGP information to distance method. */
+uint8_t bgp_distance_apply(const struct prefix *p, struct bgp_path_info *pinfo,
+ afi_t afi, safi_t safi, struct bgp *bgp)
+{
+ struct bgp_dest *dest;
+ struct prefix q = {0};
+ struct peer *peer;
+ struct bgp_distance *bdistance;
+ struct access_list *alist;
+ struct bgp_static *bgp_static;
+ struct bgp_path_info *bpi_ultimate;
+
+ if (!bgp)
+ return 0;
+
+ peer = pinfo->peer;
+
+ if (pinfo->attr->distance)
+ return pinfo->attr->distance;
+
+ /* get peer origin to calculate appropriate distance */
+ if (pinfo->sub_type == BGP_ROUTE_IMPORTED) {
+ bpi_ultimate = bgp_get_imported_bpi_ultimate(pinfo);
+ peer = bpi_ultimate->peer;
+ }
+
+ /* Check source address.
+ * Note: for aggregate route, peer can have unspec af type.
+ */
+ if (pinfo->sub_type != BGP_ROUTE_AGGREGATE &&
+ !sockunion2hostprefix(&peer->connection->su, &q))
+ return 0;
+
+ dest = bgp_node_match(bgp_distance_table[afi][safi], &q);
+ if (dest) {
+ bdistance = bgp_dest_get_bgp_distance_info(dest);
+ bgp_dest_unlock_node(dest);
+
+ if (bdistance->access_list) {
+ alist = access_list_lookup(afi, bdistance->access_list);
+ if (alist
+ && access_list_apply(alist, p) == FILTER_PERMIT)
+ return bdistance->distance;
+ } else
+ return bdistance->distance;
+ }
+
+ /* Backdoor check. */
+ dest = bgp_node_lookup(bgp->route[afi][safi], p);
+ if (dest) {
+ bgp_static = bgp_dest_get_bgp_static_info(dest);
+ bgp_dest_unlock_node(dest);
+
+ if (bgp_static->backdoor) {
+ if (bgp->distance_local[afi][safi])
+ return bgp->distance_local[afi][safi];
+ else
+ return ZEBRA_IBGP_DISTANCE_DEFAULT;
+ }
+ }
+
+ if (peer->sort == BGP_PEER_EBGP) {
+ if (bgp->distance_ebgp[afi][safi])
+ return bgp->distance_ebgp[afi][safi];
+ return ZEBRA_EBGP_DISTANCE_DEFAULT;
+ } else if (peer->sort == BGP_PEER_IBGP) {
+ if (bgp->distance_ibgp[afi][safi])
+ return bgp->distance_ibgp[afi][safi];
+ return ZEBRA_IBGP_DISTANCE_DEFAULT;
+ } else {
+ if (bgp->distance_local[afi][safi])
+ return bgp->distance_local[afi][safi];
+ return ZEBRA_IBGP_DISTANCE_DEFAULT;
+ }
+}
+
+/* If we enter `distance bgp (1-255) (1-255) (1-255)`,
+ * we should tell ZEBRA update the routes for a specific
+ * AFI/SAFI to reflect changes in RIB.
+ */
+static void bgp_announce_routes_distance_update(struct bgp *bgp,
+ afi_t update_afi,
+ safi_t update_safi)
+{
+ afi_t afi;
+ safi_t safi;
+
+ FOREACH_AFI_SAFI (afi, safi) {
+ if (!bgp_fibupd_safi(safi))
+ continue;
+
+ if (afi != update_afi && safi != update_safi)
+ continue;
+
+ if (BGP_DEBUG(zebra, ZEBRA))
+ zlog_debug(
+ "%s: Announcing routes due to distance change afi/safi (%d/%d)",
+ __func__, afi, safi);
+ bgp_zebra_announce_table(bgp, afi, safi);
+ }
+}
+
+DEFUN (bgp_distance,
+ bgp_distance_cmd,
+ "distance bgp (1-255) (1-255) (1-255)",
+ "Define an administrative distance\n"
+ "BGP distance\n"
+ "Distance for routes external to the AS\n"
+ "Distance for routes internal to the AS\n"
+ "Distance for local routes\n")
+{
+ VTY_DECLVAR_CONTEXT(bgp, bgp);
+ int idx_number = 2;
+ int idx_number_2 = 3;
+ int idx_number_3 = 4;
+ int distance_ebgp = atoi(argv[idx_number]->arg);
+ int distance_ibgp = atoi(argv[idx_number_2]->arg);
+ int distance_local = atoi(argv[idx_number_3]->arg);
+ afi_t afi;
+ safi_t safi;
+
+ afi = bgp_node_afi(vty);
+ safi = bgp_node_safi(vty);
+
+ if (bgp->distance_ebgp[afi][safi] != distance_ebgp
+ || bgp->distance_ibgp[afi][safi] != distance_ibgp
+ || bgp->distance_local[afi][safi] != distance_local) {
+ bgp->distance_ebgp[afi][safi] = distance_ebgp;
+ bgp->distance_ibgp[afi][safi] = distance_ibgp;
+ bgp->distance_local[afi][safi] = distance_local;
+ bgp_announce_routes_distance_update(bgp, afi, safi);
+ }
+ return CMD_SUCCESS;
+}
+
+DEFUN (no_bgp_distance,
+ no_bgp_distance_cmd,
+ "no distance bgp [(1-255) (1-255) (1-255)]",
+ NO_STR
+ "Define an administrative distance\n"
+ "BGP distance\n"
+ "Distance for routes external to the AS\n"
+ "Distance for routes internal to the AS\n"
+ "Distance for local routes\n")
+{
+ VTY_DECLVAR_CONTEXT(bgp, bgp);
+ afi_t afi;
+ safi_t safi;
+
+ afi = bgp_node_afi(vty);
+ safi = bgp_node_safi(vty);
+
+ if (bgp->distance_ebgp[afi][safi] != 0
+ || bgp->distance_ibgp[afi][safi] != 0
+ || bgp->distance_local[afi][safi] != 0) {
+ bgp->distance_ebgp[afi][safi] = 0;
+ bgp->distance_ibgp[afi][safi] = 0;
+ bgp->distance_local[afi][safi] = 0;
+ bgp_announce_routes_distance_update(bgp, afi, safi);
+ }
+ return CMD_SUCCESS;
+}
+
+
+DEFUN (bgp_distance_source,
+ bgp_distance_source_cmd,
+ "distance (1-255) A.B.C.D/M",
+ "Define an administrative distance\n"
+ "Administrative distance\n"
+ "IP source prefix\n")
+{
+ int idx_number = 1;
+ int idx_ipv4_prefixlen = 2;
+ bgp_distance_set(vty, argv[idx_number]->arg,
+ argv[idx_ipv4_prefixlen]->arg, NULL);
+ return CMD_SUCCESS;
+}
+
+DEFUN (no_bgp_distance_source,
+ no_bgp_distance_source_cmd,
+ "no distance (1-255) A.B.C.D/M",
+ NO_STR
+ "Define an administrative distance\n"
+ "Administrative distance\n"
+ "IP source prefix\n")
+{
+ int idx_number = 2;
+ int idx_ipv4_prefixlen = 3;
+ bgp_distance_unset(vty, argv[idx_number]->arg,
+ argv[idx_ipv4_prefixlen]->arg, NULL);
+ return CMD_SUCCESS;
+}
+
+DEFUN (bgp_distance_source_access_list,
+ bgp_distance_source_access_list_cmd,
+ "distance (1-255) A.B.C.D/M WORD",
+ "Define an administrative distance\n"
+ "Administrative distance\n"
+ "IP source prefix\n"
+ "Access list name\n")
+{
+ int idx_number = 1;
+ int idx_ipv4_prefixlen = 2;
+ int idx_word = 3;
+ bgp_distance_set(vty, argv[idx_number]->arg,
+ argv[idx_ipv4_prefixlen]->arg, argv[idx_word]->arg);
+ return CMD_SUCCESS;
+}
+
+DEFUN (no_bgp_distance_source_access_list,
+ no_bgp_distance_source_access_list_cmd,
+ "no distance (1-255) A.B.C.D/M WORD",
+ NO_STR
+ "Define an administrative distance\n"
+ "Administrative distance\n"
+ "IP source prefix\n"
+ "Access list name\n")
+{
+ int idx_number = 2;
+ int idx_ipv4_prefixlen = 3;
+ int idx_word = 4;
+ bgp_distance_unset(vty, argv[idx_number]->arg,
+ argv[idx_ipv4_prefixlen]->arg, argv[idx_word]->arg);
+ return CMD_SUCCESS;
+}
+
+DEFUN (ipv6_bgp_distance_source,
+ ipv6_bgp_distance_source_cmd,
+ "distance (1-255) X:X::X:X/M",
+ "Define an administrative distance\n"
+ "Administrative distance\n"
+ "IP source prefix\n")
+{
+ bgp_distance_set(vty, argv[1]->arg, argv[2]->arg, NULL);
+ return CMD_SUCCESS;
+}
+
+DEFUN (no_ipv6_bgp_distance_source,
+ no_ipv6_bgp_distance_source_cmd,
+ "no distance (1-255) X:X::X:X/M",
+ NO_STR
+ "Define an administrative distance\n"
+ "Administrative distance\n"
+ "IP source prefix\n")
+{
+ bgp_distance_unset(vty, argv[2]->arg, argv[3]->arg, NULL);
+ return CMD_SUCCESS;
+}
+
+DEFUN (ipv6_bgp_distance_source_access_list,
+ ipv6_bgp_distance_source_access_list_cmd,
+ "distance (1-255) X:X::X:X/M WORD",
+ "Define an administrative distance\n"
+ "Administrative distance\n"
+ "IP source prefix\n"
+ "Access list name\n")
+{
+ bgp_distance_set(vty, argv[1]->arg, argv[2]->arg, argv[3]->arg);
+ return CMD_SUCCESS;
+}
+
+DEFUN (no_ipv6_bgp_distance_source_access_list,
+ no_ipv6_bgp_distance_source_access_list_cmd,
+ "no distance (1-255) X:X::X:X/M WORD",
+ NO_STR
+ "Define an administrative distance\n"
+ "Administrative distance\n"
+ "IP source prefix\n"
+ "Access list name\n")
+{
+ bgp_distance_unset(vty, argv[2]->arg, argv[3]->arg, argv[4]->arg);
+ return CMD_SUCCESS;
+}
+
+DEFUN (bgp_damp_set,
+ bgp_damp_set_cmd,
+ "bgp dampening [(1-45) [(1-20000) (1-50000) (1-255)]]",
+ "BGP Specific commands\n"
+ "Enable route-flap dampening\n"
+ "Half-life time for the penalty\n"
+ "Value to start reusing a route\n"
+ "Value to start suppressing a route\n"
+ "Maximum duration to suppress a stable route\n")
+{
+ VTY_DECLVAR_CONTEXT(bgp, bgp);
+ int idx_half_life = 2;
+ int idx_reuse = 3;
+ int idx_suppress = 4;
+ int idx_max_suppress = 5;
+ int half = DEFAULT_HALF_LIFE * 60;
+ int reuse = DEFAULT_REUSE;
+ int suppress = DEFAULT_SUPPRESS;
+ int max = 4 * half;
+
+ if (argc == 6) {
+ half = atoi(argv[idx_half_life]->arg) * 60;
+ reuse = atoi(argv[idx_reuse]->arg);
+ suppress = atoi(argv[idx_suppress]->arg);
+ max = atoi(argv[idx_max_suppress]->arg) * 60;
+ } else if (argc == 3) {
+ half = atoi(argv[idx_half_life]->arg) * 60;
+ max = 4 * half;
+ }
+
+ /*
+ * These can't be 0 but our SA doesn't understand the
+ * way our cli is constructed
+ */
+ assert(reuse);
+ assert(half);
+ if (suppress < reuse) {
+ vty_out(vty,
+ "Suppress value cannot be less than reuse value \n");
+ return 0;
+ }
+
+ return bgp_damp_enable(bgp, bgp_node_afi(vty), bgp_node_safi(vty), half,
+ reuse, suppress, max);
+}
+
+DEFUN (bgp_damp_unset,
+ bgp_damp_unset_cmd,
+ "no bgp dampening [(1-45) [(1-20000) (1-50000) (1-255)]]",
+ NO_STR
+ "BGP Specific commands\n"
+ "Enable route-flap dampening\n"
+ "Half-life time for the penalty\n"
+ "Value to start reusing a route\n"
+ "Value to start suppressing a route\n"
+ "Maximum duration to suppress a stable route\n")
+{
+ VTY_DECLVAR_CONTEXT(bgp, bgp);
+ return bgp_damp_disable(bgp, bgp_node_afi(vty), bgp_node_safi(vty));
+}
+
+/* Display specified route of BGP table. */
+static int bgp_clear_damp_route(struct vty *vty, const char *view_name,
+ const char *ip_str, afi_t afi, safi_t safi,
+ struct prefix_rd *prd, int prefix_check)
+{
+ int ret;
+ struct prefix match;
+ struct bgp_dest *dest;
+ struct bgp_dest *rm;
+ struct bgp_path_info *pi;
+ struct bgp_path_info *pi_temp;
+ struct bgp *bgp;
+ struct bgp_table *table;
+
+ /* BGP structure lookup. */
+ if (view_name) {
+ bgp = bgp_lookup_by_name(view_name);
+ if (bgp == NULL) {
+ vty_out(vty, "%% Can't find BGP instance %s\n",
+ view_name);
+ return CMD_WARNING;
+ }
+ } else {
+ bgp = bgp_get_default();
+ if (bgp == NULL) {
+ vty_out(vty, "%% No BGP process is configured\n");
+ return CMD_WARNING;
+ }
+ }
+
+ /* Check IP address argument. */
+ ret = str2prefix(ip_str, &match);
+ if (!ret) {
+ vty_out(vty, "%% address is malformed\n");
+ return CMD_WARNING;
+ }
+
+ match.family = afi2family(afi);
+
+ if ((safi == SAFI_MPLS_VPN) || (safi == SAFI_ENCAP)
+ || (safi == SAFI_EVPN)) {
+ for (dest = bgp_table_top(bgp->rib[AFI_IP][safi]); dest;
+ dest = bgp_route_next(dest)) {
+ const struct prefix *dest_p = bgp_dest_get_prefix(dest);
+
+ if (prd && memcmp(dest_p->u.val, prd->val, 8) != 0)
+ continue;
+ table = bgp_dest_get_bgp_table_info(dest);
+ if (!table)
+ continue;
+ rm = bgp_node_match(table, &match);
+ if (rm == NULL)
+ continue;
+
+ const struct prefix *rm_p = bgp_dest_get_prefix(dest);
+
+ if (!prefix_check
+ || rm_p->prefixlen == match.prefixlen) {
+ pi = bgp_dest_get_bgp_path_info(rm);
+ while (pi) {
+ if (pi->extra && pi->extra->damp_info) {
+ pi_temp = pi->next;
+ bgp_damp_info_free(
+ pi->extra->damp_info,
+ 1, afi, safi);
+ pi = pi_temp;
+ } else
+ pi = pi->next;
+ }
+ }
+
+ bgp_dest_unlock_node(rm);
+ }
+ } else {
+ dest = bgp_node_match(bgp->rib[afi][safi], &match);
+ if (dest != NULL) {
+ const struct prefix *dest_p = bgp_dest_get_prefix(dest);
+
+ if (!prefix_check
+ || dest_p->prefixlen == match.prefixlen) {
+ pi = bgp_dest_get_bgp_path_info(dest);
+ while (pi) {
+ if (pi->extra && pi->extra->damp_info) {
+ pi_temp = pi->next;
+ bgp_damp_info_free(
+ pi->extra->damp_info,
+ 1, afi, safi);
+ pi = pi_temp;
+ } else
+ pi = pi->next;
+ }
+ }
+
+ bgp_dest_unlock_node(dest);
+ }
+ }
+
+ return CMD_SUCCESS;
+}
+
+DEFUN (clear_ip_bgp_dampening,
+ clear_ip_bgp_dampening_cmd,
+ "clear ip bgp dampening",
+ CLEAR_STR
+ IP_STR
+ BGP_STR
+ "Clear route flap dampening information\n")
+{
+ bgp_damp_info_clean(AFI_IP, SAFI_UNICAST);
+ return CMD_SUCCESS;
+}
+
+DEFUN (clear_ip_bgp_dampening_prefix,
+ clear_ip_bgp_dampening_prefix_cmd,
+ "clear ip bgp dampening A.B.C.D/M",
+ CLEAR_STR
+ IP_STR
+ BGP_STR
+ "Clear route flap dampening information\n"
+ "IPv4 prefix\n")
+{
+ int idx_ipv4_prefixlen = 4;
+ return bgp_clear_damp_route(vty, NULL, argv[idx_ipv4_prefixlen]->arg,
+ AFI_IP, SAFI_UNICAST, NULL, 1);
+}
+
+DEFUN (clear_ip_bgp_dampening_address,
+ clear_ip_bgp_dampening_address_cmd,
+ "clear ip bgp dampening A.B.C.D",
+ CLEAR_STR
+ IP_STR
+ BGP_STR
+ "Clear route flap dampening information\n"
+ "Network to clear damping information\n")
+{
+ int idx_ipv4 = 4;
+ return bgp_clear_damp_route(vty, NULL, argv[idx_ipv4]->arg, AFI_IP,
+ SAFI_UNICAST, NULL, 0);
+}
+
+DEFUN (clear_ip_bgp_dampening_address_mask,
+ clear_ip_bgp_dampening_address_mask_cmd,
+ "clear ip bgp dampening A.B.C.D A.B.C.D",
+ CLEAR_STR
+ IP_STR
+ BGP_STR
+ "Clear route flap dampening information\n"
+ "Network to clear damping information\n"
+ "Network mask\n")
+{
+ int idx_ipv4 = 4;
+ int idx_ipv4_2 = 5;
+ int ret;
+ char prefix_str[BUFSIZ];
+
+ ret = netmask_str2prefix_str(argv[idx_ipv4]->arg, argv[idx_ipv4_2]->arg,
+ prefix_str, sizeof(prefix_str));
+ if (!ret) {
+ vty_out(vty, "%% Inconsistent address and mask\n");
+ return CMD_WARNING;
+ }
+
+ return bgp_clear_damp_route(vty, NULL, prefix_str, AFI_IP, SAFI_UNICAST,
+ NULL, 0);
+}
+
+static void show_bgp_peerhash_entry(struct hash_bucket *bucket, void *arg)
+{
+ struct vty *vty = arg;
+ struct peer *peer = bucket->data;
+
+ vty_out(vty, "\tPeer: %s %pSU\n", peer->host, &peer->connection->su);
+}
+
+DEFUN (show_bgp_listeners,
+ show_bgp_listeners_cmd,
+ "show bgp listeners",
+ SHOW_STR
+ BGP_STR
+ "Display Listen Sockets and who created them\n")
+{
+ bgp_dump_listener_info(vty);
+
+ return CMD_SUCCESS;
+}
+
+DEFUN (show_bgp_peerhash,
+ show_bgp_peerhash_cmd,
+ "show bgp peerhash",
+ SHOW_STR
+ BGP_STR
+ "Display information about the BGP peerhash\n")
+{
+ struct list *instances = bm->bgp;
+ struct listnode *node;
+ struct bgp *bgp;
+
+ for (ALL_LIST_ELEMENTS_RO(instances, node, bgp)) {
+ vty_out(vty, "BGP: %s\n", bgp->name_pretty);
+ hash_iterate(bgp->peerhash, show_bgp_peerhash_entry,
+ vty);
+ }
+
+ return CMD_SUCCESS;
+}
+
+/* also used for encap safi */
+static void bgp_config_write_network_vpn(struct vty *vty, struct bgp *bgp,
+ afi_t afi, safi_t safi)
+{
+ struct bgp_dest *pdest;
+ struct bgp_dest *dest;
+ struct bgp_table *table;
+ const struct prefix *p;
+ struct bgp_static *bgp_static;
+ mpls_label_t label;
+
+ /* Network configuration. */
+ for (pdest = bgp_table_top(bgp->route[afi][safi]); pdest;
+ pdest = bgp_route_next(pdest)) {
+ table = bgp_dest_get_bgp_table_info(pdest);
+ if (!table)
+ continue;
+
+ for (dest = bgp_table_top(table); dest;
+ dest = bgp_route_next(dest)) {
+ bgp_static = bgp_dest_get_bgp_static_info(dest);
+ if (bgp_static == NULL)
+ continue;
+
+ p = bgp_dest_get_prefix(dest);
+
+ /* "network" configuration display. */
+ label = decode_label(&bgp_static->label);
+
+ vty_out(vty, " network %pFX rd %s", p,
+ bgp_static->prd_pretty);
+ if (safi == SAFI_MPLS_VPN)
+ vty_out(vty, " label %u", label);
+
+ if (bgp_static->rmap.name)
+ vty_out(vty, " route-map %s",
+ bgp_static->rmap.name);
+
+ if (bgp_static->backdoor)
+ vty_out(vty, " backdoor");
+
+ vty_out(vty, "\n");
+ }
+ }
+}
+
+static void bgp_config_write_network_evpn(struct vty *vty, struct bgp *bgp,
+ afi_t afi, safi_t safi)
+{
+ struct bgp_dest *pdest;
+ struct bgp_dest *dest;
+ struct bgp_table *table;
+ const struct prefix *p;
+ struct bgp_static *bgp_static;
+ char buf[PREFIX_STRLEN * 2];
+ char buf2[SU_ADDRSTRLEN];
+ char esi_buf[ESI_STR_LEN];
+
+ /* Network configuration. */
+ for (pdest = bgp_table_top(bgp->route[afi][safi]); pdest;
+ pdest = bgp_route_next(pdest)) {
+ table = bgp_dest_get_bgp_table_info(pdest);
+ if (!table)
+ continue;
+
+ for (dest = bgp_table_top(table); dest;
+ dest = bgp_route_next(dest)) {
+ bgp_static = bgp_dest_get_bgp_static_info(dest);
+ if (bgp_static == NULL)
+ continue;
+
+ char *macrouter = NULL;
+
+ if (bgp_static->router_mac)
+ macrouter = prefix_mac2str(
+ bgp_static->router_mac, NULL, 0);
+ if (bgp_static->eth_s_id)
+ esi_to_str(bgp_static->eth_s_id,
+ esi_buf, sizeof(esi_buf));
+ p = bgp_dest_get_prefix(dest);
+
+ /* "network" configuration display. */
+ if (p->u.prefix_evpn.route_type == 5) {
+ char local_buf[PREFIX_STRLEN];
+
+ uint8_t family = is_evpn_prefix_ipaddr_v4((
+ struct prefix_evpn *)p)
+ ? AF_INET
+ : AF_INET6;
+ inet_ntop(family,
+ &p->u.prefix_evpn.prefix_addr.ip.ip
+ .addr,
+ local_buf, sizeof(local_buf));
+ snprintf(buf, sizeof(buf), "%s/%u", local_buf,
+ p->u.prefix_evpn.prefix_addr
+ .ip_prefix_length);
+ } else {
+ prefix2str(p, buf, sizeof(buf));
+ }
+
+ if (bgp_static->gatewayIp.family == AF_INET
+ || bgp_static->gatewayIp.family == AF_INET6)
+ inet_ntop(bgp_static->gatewayIp.family,
+ &bgp_static->gatewayIp.u.prefix, buf2,
+ sizeof(buf2));
+ vty_out(vty,
+ " network %s rd %s ethtag %u label %u esi %s gwip %s routermac %s\n",
+ buf, bgp_static->prd_pretty,
+ p->u.prefix_evpn.prefix_addr.eth_tag,
+ decode_label(&bgp_static->label), esi_buf, buf2,
+ macrouter);
+
+ XFREE(MTYPE_TMP, macrouter);
+ }
+ }
+}
+
+/* Configuration of static route announcement and aggregate
+ information. */
+void bgp_config_write_network(struct vty *vty, struct bgp *bgp, afi_t afi,
+ safi_t safi)
+{
+ struct bgp_dest *dest;
+ const struct prefix *p;
+ struct bgp_static *bgp_static;
+ struct bgp_aggregate *bgp_aggregate;
+
+ if ((safi == SAFI_MPLS_VPN) || (safi == SAFI_ENCAP)) {
+ bgp_config_write_network_vpn(vty, bgp, afi, safi);
+ return;
+ }
+
+ if (afi == AFI_L2VPN && safi == SAFI_EVPN) {
+ bgp_config_write_network_evpn(vty, bgp, afi, safi);
+ return;
+ }
+
+ /* Network configuration. */
+ for (dest = bgp_table_top(bgp->route[afi][safi]); dest;
+ dest = bgp_route_next(dest)) {
+ bgp_static = bgp_dest_get_bgp_static_info(dest);
+ if (bgp_static == NULL)
+ continue;
+
+ p = bgp_dest_get_prefix(dest);
+
+ vty_out(vty, " network %pFX", p);
+
+ if (bgp_static->label_index != BGP_INVALID_LABEL_INDEX)
+ vty_out(vty, " label-index %u",
+ bgp_static->label_index);
+
+ if (bgp_static->rmap.name)
+ vty_out(vty, " route-map %s", bgp_static->rmap.name);
+
+ if (bgp_static->backdoor)
+ vty_out(vty, " backdoor");
+
+ vty_out(vty, "\n");
+ }
+
+ /* Aggregate-address configuration. */
+ for (dest = bgp_table_top(bgp->aggregate[afi][safi]); dest;
+ dest = bgp_route_next(dest)) {
+ bgp_aggregate = bgp_dest_get_bgp_aggregate_info(dest);
+ if (bgp_aggregate == NULL)
+ continue;
+
+ p = bgp_dest_get_prefix(dest);
+
+ vty_out(vty, " aggregate-address %pFX", p);
+
+ if (bgp_aggregate->as_set)
+ vty_out(vty, " as-set");
+
+ if (bgp_aggregate->summary_only)
+ vty_out(vty, " summary-only");
+
+ if (bgp_aggregate->rmap.name)
+ vty_out(vty, " route-map %s", bgp_aggregate->rmap.name);
+
+ if (bgp_aggregate->origin != BGP_ORIGIN_UNSPECIFIED)
+ vty_out(vty, " origin %s",
+ bgp_origin2str(bgp_aggregate->origin));
+
+ if (bgp_aggregate->match_med)
+ vty_out(vty, " matching-MED-only");
+
+ if (bgp_aggregate->suppress_map_name)
+ vty_out(vty, " suppress-map %s",
+ bgp_aggregate->suppress_map_name);
+
+ vty_out(vty, "\n");
+ }
+}
+
+void bgp_config_write_distance(struct vty *vty, struct bgp *bgp, afi_t afi,
+ safi_t safi)
+{
+ struct bgp_dest *dest;
+ struct bgp_distance *bdistance;
+
+ /* Distance configuration. */
+ if (bgp->distance_ebgp[afi][safi] && bgp->distance_ibgp[afi][safi]
+ && bgp->distance_local[afi][safi]
+ && (bgp->distance_ebgp[afi][safi] != ZEBRA_EBGP_DISTANCE_DEFAULT
+ || bgp->distance_ibgp[afi][safi] != ZEBRA_IBGP_DISTANCE_DEFAULT
+ || bgp->distance_local[afi][safi]
+ != ZEBRA_IBGP_DISTANCE_DEFAULT)) {
+ vty_out(vty, " distance bgp %d %d %d\n",
+ bgp->distance_ebgp[afi][safi],
+ bgp->distance_ibgp[afi][safi],
+ bgp->distance_local[afi][safi]);
+ }
+
+ for (dest = bgp_table_top(bgp_distance_table[afi][safi]); dest;
+ dest = bgp_route_next(dest)) {
+ bdistance = bgp_dest_get_bgp_distance_info(dest);
+ if (bdistance != NULL)
+ vty_out(vty, " distance %d %pBD %s\n",
+ bdistance->distance, dest,
+ bdistance->access_list ? bdistance->access_list
+ : "");
+ }
+}
+
+/* Allocate routing table structure and install commands. */
+void bgp_route_init(void)
+{
+ afi_t afi;
+ safi_t safi;
+
+ /* Init BGP distance table. */
+ FOREACH_AFI_SAFI (afi, safi)
+ bgp_distance_table[afi][safi] = bgp_table_init(NULL, afi, safi);
+
+ /* IPv4 BGP commands. */
+ install_element(BGP_NODE, &bgp_table_map_cmd);
+ install_element(BGP_NODE, &bgp_network_cmd);
+ install_element(BGP_NODE, &no_bgp_table_map_cmd);
+
+ install_element(BGP_NODE, &aggregate_addressv4_cmd);
+
+ /* IPv4 unicast configuration. */
+ install_element(BGP_IPV4_NODE, &bgp_table_map_cmd);
+ install_element(BGP_IPV4_NODE, &bgp_network_cmd);
+ install_element(BGP_IPV4_NODE, &no_bgp_table_map_cmd);
+
+ install_element(BGP_IPV4_NODE, &aggregate_addressv4_cmd);
+
+ /* IPv4 multicast configuration. */
+ install_element(BGP_IPV4M_NODE, &bgp_table_map_cmd);
+ install_element(BGP_IPV4M_NODE, &bgp_network_cmd);
+ install_element(BGP_IPV4M_NODE, &no_bgp_table_map_cmd);
+ install_element(BGP_IPV4M_NODE, &aggregate_addressv4_cmd);
+
+ /* IPv4 labeled-unicast configuration. */
+ install_element(BGP_IPV4L_NODE, &bgp_network_cmd);
+ install_element(BGP_IPV4L_NODE, &aggregate_addressv4_cmd);
+
+ install_element(VIEW_NODE, &show_ip_bgp_instance_all_cmd);
+ install_element(VIEW_NODE, &show_ip_bgp_afi_safi_statistics_cmd);
+ install_element(VIEW_NODE, &show_ip_bgp_l2vpn_evpn_statistics_cmd);
+ install_element(VIEW_NODE, &show_ip_bgp_dampening_params_cmd);
+ install_element(VIEW_NODE, &show_ip_bgp_cmd);
+ install_element(VIEW_NODE, &show_ip_bgp_route_cmd);
+ install_element(VIEW_NODE, &show_ip_bgp_regexp_cmd);
+ install_element(VIEW_NODE, &show_ip_bgp_statistics_all_cmd);
+
+ install_element(VIEW_NODE,
+ &show_ip_bgp_instance_neighbor_advertised_route_cmd);
+ install_element(VIEW_NODE,
+ &show_ip_bgp_instance_neighbor_bestpath_route_cmd);
+ install_element(VIEW_NODE, &show_ip_bgp_neighbor_routes_cmd);
+ install_element(VIEW_NODE,
+ &show_ip_bgp_neighbor_received_prefix_filter_cmd);
+#ifdef KEEP_OLD_VPN_COMMANDS
+ install_element(VIEW_NODE, &show_ip_bgp_vpn_all_route_prefix_cmd);
+#endif /* KEEP_OLD_VPN_COMMANDS */
+ install_element(VIEW_NODE, &show_bgp_afi_vpn_rd_route_cmd);
+ install_element(VIEW_NODE,
+ &show_bgp_l2vpn_evpn_route_prefix_cmd);
+
+ /* BGP dampening clear commands */
+ install_element(ENABLE_NODE, &clear_ip_bgp_dampening_cmd);
+ install_element(ENABLE_NODE, &clear_ip_bgp_dampening_prefix_cmd);
+
+ install_element(ENABLE_NODE, &clear_ip_bgp_dampening_address_cmd);
+ install_element(ENABLE_NODE, &clear_ip_bgp_dampening_address_mask_cmd);
+
+ /* prefix count */
+ install_element(ENABLE_NODE,
+ &show_ip_bgp_instance_neighbor_prefix_counts_cmd);
+#ifdef KEEP_OLD_VPN_COMMANDS
+ install_element(ENABLE_NODE,
+ &show_ip_bgp_vpn_neighbor_prefix_counts_cmd);
+#endif /* KEEP_OLD_VPN_COMMANDS */
+
+ /* New config IPv6 BGP commands. */
+ install_element(BGP_IPV6_NODE, &bgp_table_map_cmd);
+ install_element(BGP_IPV6_NODE, &ipv6_bgp_network_cmd);
+ install_element(BGP_IPV6_NODE, &no_bgp_table_map_cmd);
+
+ install_element(BGP_IPV6_NODE, &aggregate_addressv6_cmd);
+
+ install_element(BGP_IPV6M_NODE, &ipv6_bgp_network_cmd);
+
+ /* IPv6 labeled unicast address family. */
+ install_element(BGP_IPV6L_NODE, &ipv6_bgp_network_cmd);
+ install_element(BGP_IPV6L_NODE, &aggregate_addressv6_cmd);
+
+ install_element(BGP_NODE, &bgp_distance_cmd);
+ install_element(BGP_NODE, &no_bgp_distance_cmd);
+ install_element(BGP_NODE, &bgp_distance_source_cmd);
+ install_element(BGP_NODE, &no_bgp_distance_source_cmd);
+ install_element(BGP_NODE, &bgp_distance_source_access_list_cmd);
+ install_element(BGP_NODE, &no_bgp_distance_source_access_list_cmd);
+ install_element(BGP_IPV4_NODE, &bgp_distance_cmd);
+ install_element(BGP_IPV4_NODE, &no_bgp_distance_cmd);
+ install_element(BGP_IPV4_NODE, &bgp_distance_source_cmd);
+ install_element(BGP_IPV4_NODE, &no_bgp_distance_source_cmd);
+ install_element(BGP_IPV4_NODE, &bgp_distance_source_access_list_cmd);
+ install_element(BGP_IPV4_NODE, &no_bgp_distance_source_access_list_cmd);
+ install_element(BGP_IPV4M_NODE, &bgp_distance_cmd);
+ install_element(BGP_IPV4M_NODE, &no_bgp_distance_cmd);
+ install_element(BGP_IPV4M_NODE, &bgp_distance_source_cmd);
+ install_element(BGP_IPV4M_NODE, &no_bgp_distance_source_cmd);
+ install_element(BGP_IPV4M_NODE, &bgp_distance_source_access_list_cmd);
+ install_element(BGP_IPV4M_NODE,
+ &no_bgp_distance_source_access_list_cmd);
+ install_element(BGP_IPV6_NODE, &bgp_distance_cmd);
+ install_element(BGP_IPV6_NODE, &no_bgp_distance_cmd);
+ install_element(BGP_IPV6_NODE, &ipv6_bgp_distance_source_cmd);
+ install_element(BGP_IPV6_NODE, &no_ipv6_bgp_distance_source_cmd);
+ install_element(BGP_IPV6_NODE,
+ &ipv6_bgp_distance_source_access_list_cmd);
+ install_element(BGP_IPV6_NODE,
+ &no_ipv6_bgp_distance_source_access_list_cmd);
+ install_element(BGP_IPV6M_NODE, &bgp_distance_cmd);
+ install_element(BGP_IPV6M_NODE, &no_bgp_distance_cmd);
+ install_element(BGP_IPV6M_NODE, &ipv6_bgp_distance_source_cmd);
+ install_element(BGP_IPV6M_NODE, &no_ipv6_bgp_distance_source_cmd);
+ install_element(BGP_IPV6M_NODE,
+ &ipv6_bgp_distance_source_access_list_cmd);
+ install_element(BGP_IPV6M_NODE,
+ &no_ipv6_bgp_distance_source_access_list_cmd);
+
+ /* BGP dampening */
+ install_element(BGP_NODE, &bgp_damp_set_cmd);
+ install_element(BGP_NODE, &bgp_damp_unset_cmd);
+ install_element(BGP_IPV4_NODE, &bgp_damp_set_cmd);
+ install_element(BGP_IPV4_NODE, &bgp_damp_unset_cmd);
+ install_element(BGP_IPV4M_NODE, &bgp_damp_set_cmd);
+ install_element(BGP_IPV4M_NODE, &bgp_damp_unset_cmd);
+ install_element(BGP_IPV4L_NODE, &bgp_damp_set_cmd);
+ install_element(BGP_IPV4L_NODE, &bgp_damp_unset_cmd);
+ install_element(BGP_IPV6_NODE, &bgp_damp_set_cmd);
+ install_element(BGP_IPV6_NODE, &bgp_damp_unset_cmd);
+ install_element(BGP_IPV6M_NODE, &bgp_damp_set_cmd);
+ install_element(BGP_IPV6M_NODE, &bgp_damp_unset_cmd);
+ install_element(BGP_IPV6L_NODE, &bgp_damp_set_cmd);
+ install_element(BGP_IPV6L_NODE, &bgp_damp_unset_cmd);
+
+ /* Large Communities */
+ install_element(VIEW_NODE, &show_ip_bgp_large_community_list_cmd);
+ install_element(VIEW_NODE, &show_ip_bgp_large_community_cmd);
+
+ /* show bgp vrf <afi> <safi> detailed */
+ install_element(VIEW_NODE,
+ &show_ip_bgp_vrf_afi_safi_routes_detailed_cmd);
+
+ install_element(VIEW_NODE, &show_bgp_listeners_cmd);
+ install_element(VIEW_NODE, &show_bgp_peerhash_cmd);
+}
+
+void bgp_route_finish(void)
+{
+ afi_t afi;
+ safi_t safi;
+
+ FOREACH_AFI_SAFI (afi, safi) {
+ bgp_table_unlock(bgp_distance_table[afi][safi]);
+ bgp_distance_table[afi][safi] = NULL;
+ }
+}