summaryrefslogtreecommitdiffstats
path: root/bgpd
diff options
context:
space:
mode:
authorDaniel Baumann <daniel.baumann@progress-linux.org>2024-04-28 09:53:30 +0000
committerDaniel Baumann <daniel.baumann@progress-linux.org>2024-04-28 09:53:30 +0000
commit2c7cac91ed6e7db0f6937923d2b57f97dbdbc337 (patch)
treec05dc0f8e6aa3accc84e3e5cffc933ed94941383 /bgpd
parentInitial commit. (diff)
downloadfrr-2c7cac91ed6e7db0f6937923d2b57f97dbdbc337.tar.xz
frr-2c7cac91ed6e7db0f6937923d2b57f97dbdbc337.zip
Adding upstream version 8.4.4.upstream/8.4.4upstream
Signed-off-by: Daniel Baumann <daniel.baumann@progress-linux.org>
Diffstat (limited to 'bgpd')
-rw-r--r--bgpd/.gitignore3
-rw-r--r--bgpd/Makefile10
-rw-r--r--bgpd/bgp_addpath.c466
-rw-r--r--bgpd/bgp_addpath.h72
-rw-r--r--bgpd/bgp_addpath_types.h55
-rw-r--r--bgpd/bgp_advertise.c262
-rw-r--r--bgpd/bgp_advertise.h170
-rw-r--r--bgpd/bgp_aspath.c2276
-rw-r--r--bgpd/bgp_aspath.h158
-rw-r--r--bgpd/bgp_attr.c4713
-rw-r--r--bgpd/bgp_attr.h638
-rw-r--r--bgpd/bgp_attr_evpn.c317
-rw-r--r--bgpd/bgp_attr_evpn.h65
-rw-r--r--bgpd/bgp_bfd.c644
-rw-r--r--bgpd/bgp_bfd.h80
-rw-r--r--bgpd/bgp_bmp.c2587
-rw-r--r--bgpd/bgp_bmp.h316
-rw-r--r--bgpd/bgp_btoa.c288
-rw-r--r--bgpd/bgp_clist.c1447
-rw-r--r--bgpd/bgp_clist.h192
-rw-r--r--bgpd/bgp_community.c1079
-rw-r--r--bgpd/bgp_community.h112
-rw-r--r--bgpd/bgp_community_alias.c232
-rw-r--r--bgpd/bgp_community_alias.h49
-rw-r--r--bgpd/bgp_conditional_adv.c376
-rw-r--r--bgpd/bgp_conditional_adv.h47
-rw-r--r--bgpd/bgp_damp.c741
-rw-r--r--bgpd/bgp_damp.h159
-rw-r--r--bgpd/bgp_debug.c2741
-rw-r--r--bgpd/bgp_debug.h192
-rw-r--r--bgpd/bgp_dump.c880
-rw-r--r--bgpd/bgp_dump.h64
-rw-r--r--bgpd/bgp_ecommunity.c1716
-rw-r--r--bgpd/bgp_ecommunity.h356
-rw-r--r--bgpd/bgp_encap_tlv.c1006
-rw-r--r--bgpd/bgp_encap_tlv.h134
-rw-r--r--bgpd/bgp_encap_types.h231
-rw-r--r--bgpd/bgp_errors.c505
-rw-r--r--bgpd/bgp_errors.h111
-rw-r--r--bgpd/bgp_evpn.c6518
-rw-r--r--bgpd/bgp_evpn.h233
-rw-r--r--bgpd/bgp_evpn_mh.c4979
-rw-r--r--bgpd/bgp_evpn_mh.h477
-rw-r--r--bgpd/bgp_evpn_private.h656
-rw-r--r--bgpd/bgp_evpn_vty.c6578
-rw-r--r--bgpd/bgp_evpn_vty.h39
-rw-r--r--bgpd/bgp_filter.c793
-rw-r--r--bgpd/bgp_filter.h38
-rw-r--r--bgpd/bgp_flowspec.c224
-rw-r--r--bgpd/bgp_flowspec.h59
-rw-r--r--bgpd/bgp_flowspec_private.h46
-rw-r--r--bgpd/bgp_flowspec_util.c689
-rw-r--r--bgpd/bgp_flowspec_util.h62
-rw-r--r--bgpd/bgp_flowspec_vty.c633
-rw-r--r--bgpd/bgp_fsm.c3130
-rw-r--r--bgpd/bgp_fsm.h178
-rw-r--r--bgpd/bgp_io.c583
-rw-r--r--bgpd/bgp_io.h103
-rw-r--r--bgpd/bgp_keepalives.c323
-rw-r--r--bgpd/bgp_keepalives.h93
-rw-r--r--bgpd/bgp_label.c476
-rw-r--r--bgpd/bgp_label.h108
-rw-r--r--bgpd/bgp_labelpool.c1559
-rw-r--r--bgpd/bgp_labelpool.h57
-rw-r--r--bgpd/bgp_lcommunity.c690
-rw-r--r--bgpd/bgp_lcommunity.h95
-rw-r--r--bgpd/bgp_mac.c423
-rw-r--r--bgpd/bgp_mac.h42
-rw-r--r--bgpd/bgp_main.c524
-rw-r--r--bgpd/bgp_memory.c141
-rw-r--r--bgpd/bgp_memory.h141
-rw-r--r--bgpd/bgp_mpath.c931
-rw-r--r--bgpd/bgp_mpath.h95
-rw-r--r--bgpd/bgp_mplsvpn.c3159
-rw-r--r--bgpd/bgp_mplsvpn.h311
-rw-r--r--bgpd/bgp_mplsvpn_snmp.c1693
-rw-r--r--bgpd/bgp_mplsvpn_snmp.h31
-rw-r--r--bgpd/bgp_network.c977
-rw-r--r--bgpd/bgp_network.h51
-rw-r--r--bgpd/bgp_nexthop.c1086
-rw-r--r--bgpd/bgp_nexthop.h175
-rw-r--r--bgpd/bgp_nht.c1455
-rw-r--r--bgpd/bgp_nht.h106
-rw-r--r--bgpd/bgp_open.c1936
-rw-r--r--bgpd/bgp_open.h114
-rw-r--r--bgpd/bgp_packet.c3047
-rw-r--r--bgpd/bgp_packet.h101
-rw-r--r--bgpd/bgp_pbr.c2960
-rw-r--r--bgpd/bgp_pbr.h317
-rw-r--r--bgpd/bgp_rd.c227
-rw-r--r--bgpd/bgp_rd.h74
-rw-r--r--bgpd/bgp_regex.c89
-rw-r--r--bgpd/bgp_regex.h36
-rw-r--r--bgpd/bgp_route.c15633
-rw-r--r--bgpd/bgp_route.h872
-rw-r--r--bgpd/bgp_routemap.c7166
-rw-r--r--bgpd/bgp_routemap_nb.c427
-rw-r--r--bgpd/bgp_routemap_nb.h166
-rw-r--r--bgpd/bgp_routemap_nb_config.c3031
-rw-r--r--bgpd/bgp_rpki.c1747
-rw-r--r--bgpd/bgp_rpki.h33
-rw-r--r--bgpd/bgp_script.c196
-rw-r--r--bgpd/bgp_script.h45
-rw-r--r--bgpd/bgp_snmp.c916
-rw-r--r--bgpd/bgp_table.c254
-rw-r--r--bgpd/bgp_table.h401
-rw-r--r--bgpd/bgp_trace.c6
-rw-r--r--bgpd/bgp_trace.h484
-rw-r--r--bgpd/bgp_updgrp.c2052
-rw-r--r--bgpd/bgp_updgrp.h606
-rw-r--r--bgpd/bgp_updgrp_adv.c1066
-rw-r--r--bgpd/bgp_updgrp_packet.c1315
-rw-r--r--bgpd/bgp_vnc_types.h29
-rw-r--r--bgpd/bgp_vpn.c247
-rw-r--r--bgpd/bgp_vpn.h30
-rw-r--r--bgpd/bgp_vty.c20867
-rw-r--r--bgpd/bgp_vty.h189
-rw-r--r--bgpd/bgp_zebra.c3838
-rw-r--r--bgpd/bgp_zebra.h136
-rw-r--r--bgpd/bgpd.c8393
-rw-r--r--bgpd/bgpd.h2586
-rw-r--r--bgpd/rfapi/bgp_rfapi_cfg.c4637
-rw-r--r--bgpd/rfapi/bgp_rfapi_cfg.h315
-rw-r--r--bgpd/rfapi/rfapi.c4062
-rw-r--r--bgpd/rfapi/rfapi.h914
-rw-r--r--bgpd/rfapi/rfapi_ap.c555
-rw-r--r--bgpd/rfapi/rfapi_ap.h85
-rw-r--r--bgpd/rfapi/rfapi_backend.h73
-rw-r--r--bgpd/rfapi/rfapi_descriptor_rfp_utils.c123
-rw-r--r--bgpd/rfapi/rfapi_descriptor_rfp_utils.h38
-rw-r--r--bgpd/rfapi/rfapi_encap_tlv.c743
-rw-r--r--bgpd/rfapi/rfapi_encap_tlv.h35
-rw-r--r--bgpd/rfapi/rfapi_import.c4810
-rw-r--r--bgpd/rfapi/rfapi_import.h243
-rw-r--r--bgpd/rfapi/rfapi_monitor.c1558
-rw-r--r--bgpd/rfapi/rfapi_monitor.h190
-rw-r--r--bgpd/rfapi/rfapi_nve_addr.c158
-rw-r--r--bgpd/rfapi/rfapi_nve_addr.h38
-rw-r--r--bgpd/rfapi/rfapi_private.h413
-rw-r--r--bgpd/rfapi/rfapi_rib.c2433
-rw-r--r--bgpd/rfapi/rfapi_rib.h154
-rw-r--r--bgpd/rfapi/rfapi_vty.c5033
-rw-r--r--bgpd/rfapi/rfapi_vty.h174
-rw-r--r--bgpd/rfapi/vnc_debug.c196
-rw-r--r--bgpd/rfapi/vnc_debug.h51
-rw-r--r--bgpd/rfapi/vnc_export_bgp.c2113
-rw-r--r--bgpd/rfapi/vnc_export_bgp.h41
-rw-r--r--bgpd/rfapi/vnc_export_bgp_p.h74
-rw-r--r--bgpd/rfapi/vnc_export_table.c192
-rw-r--r--bgpd/rfapi/vnc_export_table.h66
-rw-r--r--bgpd/rfapi/vnc_import_bgp.c2907
-rw-r--r--bgpd/rfapi/vnc_import_bgp.h75
-rw-r--r--bgpd/rfapi/vnc_import_bgp_p.h44
-rw-r--r--bgpd/rfapi/vnc_zebra.c921
-rw-r--r--bgpd/rfapi/vnc_zebra.h56
-rw-r--r--bgpd/rfp-example/librfp/Makefile10
-rw-r--r--bgpd/rfp-example/librfp/rfp.h30
-rw-r--r--bgpd/rfp-example/librfp/rfp_example.c344
-rw-r--r--bgpd/rfp-example/librfp/rfp_internal.h28
-rw-r--r--bgpd/rfp-example/librfp/subdir.am17
-rw-r--r--bgpd/rfp-example/rfptest/.gitignore1
-rw-r--r--bgpd/rfp-example/rfptest/Makefile10
-rw-r--r--bgpd/rfp-example/rfptest/rfptest.c33
-rw-r--r--bgpd/rfp-example/rfptest/rfptest.h25
-rw-r--r--bgpd/rfp-example/rfptest/subdir.am23
-rw-r--r--bgpd/subdir.am255
166 files changed, 182178 insertions, 0 deletions
diff --git a/bgpd/.gitignore b/bgpd/.gitignore
new file mode 100644
index 0000000..2e77195
--- /dev/null
+++ b/bgpd/.gitignore
@@ -0,0 +1,3 @@
+bgpd
+bgp_btoa
+bgpd.conf
diff --git a/bgpd/Makefile b/bgpd/Makefile
new file mode 100644
index 0000000..b8664a8
--- /dev/null
+++ b/bgpd/Makefile
@@ -0,0 +1,10 @@
+all: ALWAYS
+ @$(MAKE) -s -C .. bgpd/bgpd
+%: ALWAYS
+ @$(MAKE) -s -C .. bgpd/$@
+
+Makefile:
+ #nothing
+ALWAYS:
+.PHONY: ALWAYS makefiles
+.SUFFIXES:
diff --git a/bgpd/bgp_addpath.c b/bgpd/bgp_addpath.c
new file mode 100644
index 0000000..d822f6e
--- /dev/null
+++ b/bgpd/bgp_addpath.c
@@ -0,0 +1,466 @@
+/*
+ * Addpath TX ID selection, and related utilities
+ * Copyright (C) 2018 Amazon.com, Inc. or its affiliates
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License as published by the Free
+ * Software Foundation; either version 2 of the License, or (at your option)
+ * any later version.
+ *
+ * This program is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
+ * more details.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program; see the file COPYING; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
+ */
+
+#ifdef HAVE_CONFIG_H
+#include "config.h"
+#endif
+
+#include "bgp_addpath.h"
+#include "bgp_route.h"
+
+static const struct bgp_addpath_strategy_names strat_names[BGP_ADDPATH_MAX] = {
+ {
+ .config_name = "addpath-tx-all-paths",
+ .human_name = "All",
+ .human_description = "Advertise all paths via addpath",
+ .type_json_name = "addpathTxAllPaths",
+ .id_json_name = "addpathTxIdAll"
+ },
+ {
+ .config_name = "addpath-tx-bestpath-per-AS",
+ .human_name = "Best-Per-AS",
+ .human_description = "Advertise bestpath per AS via addpath",
+ .type_json_name = "addpathTxBestpathPerAS",
+ .id_json_name = "addpathTxIdBestPerAS"
+ }
+};
+
+static const struct bgp_addpath_strategy_names unknown_names = {
+ .config_name = "addpath-tx-unknown",
+ .human_name = "Unknown-Addpath-Strategy",
+ .human_description = "Unknown Addpath Strategy",
+ .type_json_name = "addpathTxUnknown",
+ .id_json_name = "addpathTxIdUnknown"
+};
+
+/*
+ * Returns a structure full of strings associated with an addpath type. Will
+ * never return null.
+ */
+const struct bgp_addpath_strategy_names *
+bgp_addpath_names(enum bgp_addpath_strat strat)
+{
+ if (strat < BGP_ADDPATH_MAX)
+ return &(strat_names[strat]);
+ else
+ return &unknown_names;
+};
+
+/*
+ * Returns if any peer is transmitting addpaths for a given afi/safi.
+ */
+bool bgp_addpath_is_addpath_used(struct bgp_addpath_bgp_data *d, afi_t afi,
+ safi_t safi)
+{
+ return d->total_peercount[afi][safi] > 0;
+}
+
+/*
+ * Initialize the BGP instance level data for addpath.
+ */
+void bgp_addpath_init_bgp_data(struct bgp_addpath_bgp_data *d)
+{
+ safi_t safi;
+ afi_t afi;
+ int i;
+
+ FOREACH_AFI_SAFI (afi, safi) {
+ for (i = 0; i < BGP_ADDPATH_MAX; i++) {
+ d->id_allocators[afi][safi][i] = NULL;
+ d->peercount[afi][safi][i] = 0;
+ }
+ d->total_peercount[afi][safi] = 0;
+ }
+}
+
+/*
+ * Free up resources associated with BGP route info structures.
+ */
+void bgp_addpath_free_info_data(struct bgp_addpath_info_data *d,
+ struct bgp_addpath_node_data *nd)
+{
+ int i;
+
+ for (i = 0; i < BGP_ADDPATH_MAX; i++) {
+ if (d->addpath_tx_id[i] != IDALLOC_INVALID)
+ idalloc_free_to_pool(&nd->free_ids[i],
+ d->addpath_tx_id[i]);
+ }
+}
+
+/*
+ * Return the addpath ID used to send a particular route, to a particular peer,
+ * in a particular AFI/SAFI.
+ */
+uint32_t bgp_addpath_id_for_peer(struct peer *peer, afi_t afi, safi_t safi,
+ struct bgp_addpath_info_data *d)
+{
+ if (peer->addpath_type[afi][safi] < BGP_ADDPATH_MAX)
+ return d->addpath_tx_id[peer->addpath_type[afi][safi]];
+ else
+ return IDALLOC_INVALID;
+}
+
+/*
+ * Returns true if the path has an assigned addpath ID for any of the addpath
+ * strategies.
+ */
+bool bgp_addpath_info_has_ids(struct bgp_addpath_info_data *d)
+{
+ int i;
+
+ for (i = 0; i < BGP_ADDPATH_MAX; i++)
+ if (d->addpath_tx_id[i] != 0)
+ return true;
+
+ return false;
+}
+
+/*
+ * Releases any ID's associated with the BGP prefix.
+ */
+void bgp_addpath_free_node_data(struct bgp_addpath_bgp_data *bd,
+ struct bgp_addpath_node_data *nd, afi_t afi,
+ safi_t safi)
+{
+ int i;
+
+ for (i = 0; i < BGP_ADDPATH_MAX; i++) {
+ idalloc_drain_pool(bd->id_allocators[afi][safi][i],
+ &(nd->free_ids[i]));
+ }
+}
+
+/*
+ * Check to see if the addpath strategy requires DMED to be configured to work.
+ */
+bool bgp_addpath_dmed_required(int strategy)
+{
+ return strategy == BGP_ADDPATH_BEST_PER_AS;
+}
+
+/*
+ * Return true if this is a path we should advertise due to a
+ * configured addpath-tx knob
+ */
+bool bgp_addpath_tx_path(enum bgp_addpath_strat strat, struct bgp_path_info *pi)
+{
+ switch (strat) {
+ case BGP_ADDPATH_NONE:
+ return false;
+ case BGP_ADDPATH_ALL:
+ return true;
+ case BGP_ADDPATH_BEST_PER_AS:
+ if (CHECK_FLAG(pi->flags, BGP_PATH_DMED_SELECTED))
+ return true;
+ else
+ return false;
+ default:
+ return false;
+ }
+}
+
+static void bgp_addpath_flush_type_rn(struct bgp *bgp, afi_t afi, safi_t safi,
+ enum bgp_addpath_strat addpath_type,
+ struct bgp_dest *dest)
+{
+ struct bgp_path_info *pi;
+
+ idalloc_drain_pool(
+ bgp->tx_addpath.id_allocators[afi][safi][addpath_type],
+ &(dest->tx_addpath.free_ids[addpath_type]));
+ for (pi = bgp_dest_get_bgp_path_info(dest); pi; pi = pi->next) {
+ if (pi->tx_addpath.addpath_tx_id[addpath_type]
+ != IDALLOC_INVALID) {
+ idalloc_free(
+ bgp->tx_addpath
+ .id_allocators[afi][safi][addpath_type],
+ pi->tx_addpath.addpath_tx_id[addpath_type]);
+ pi->tx_addpath.addpath_tx_id[addpath_type] =
+ IDALLOC_INVALID;
+ }
+ }
+}
+
+/*
+ * Purge all addpath ID's on a BGP instance associated with the addpath
+ * strategy, and afi/safi combination. This lets us let go of all memory held to
+ * track ID numbers associated with an addpath type not in use. Since
+ * post-bestpath ID processing is skipped for types not used, this is the only
+ * chance to free this data.
+ */
+static void bgp_addpath_flush_type(struct bgp *bgp, afi_t afi, safi_t safi,
+ enum bgp_addpath_strat addpath_type)
+{
+ struct bgp_dest *dest, *ndest;
+
+ for (dest = bgp_table_top(bgp->rib[afi][safi]); dest;
+ dest = bgp_route_next(dest)) {
+ if (safi == SAFI_MPLS_VPN) {
+ struct bgp_table *table;
+
+ table = bgp_dest_get_bgp_table_info(dest);
+ if (!table)
+ continue;
+
+ for (ndest = bgp_table_top(table); ndest;
+ ndest = bgp_route_next(ndest))
+ bgp_addpath_flush_type_rn(bgp, afi, safi,
+ addpath_type, ndest);
+ } else {
+ bgp_addpath_flush_type_rn(bgp, afi, safi, addpath_type,
+ dest);
+ }
+ }
+
+ idalloc_destroy(bgp->tx_addpath.id_allocators[afi][safi][addpath_type]);
+ bgp->tx_addpath.id_allocators[afi][safi][addpath_type] = NULL;
+}
+
+/*
+ * Allocate an Addpath ID for the given type on a path, if necessary.
+ */
+static void bgp_addpath_populate_path(struct id_alloc *allocator,
+ struct bgp_path_info *path,
+ enum bgp_addpath_strat addpath_type)
+{
+ if (bgp_addpath_tx_path(addpath_type, path)) {
+ path->tx_addpath.addpath_tx_id[addpath_type] =
+ idalloc_allocate(allocator);
+ }
+}
+
+/*
+ * Compute addpath ID's on a BGP instance associated with the addpath strategy,
+ * and afi/safi combination. Since we won't waste the time computing addpath IDs
+ * for unused strategies, the first time a peer is configured to use a strategy,
+ * we have to backfill the data.
+ */
+static void bgp_addpath_populate_type(struct bgp *bgp, afi_t afi, safi_t safi,
+ enum bgp_addpath_strat addpath_type)
+{
+ struct bgp_dest *dest, *ndest;
+ char buf[200];
+ struct id_alloc *allocator;
+
+ snprintf(buf, sizeof(buf), "Addpath ID Allocator %s:%d/%d",
+ bgp_addpath_names(addpath_type)->config_name, (int)afi,
+ (int)safi);
+ buf[sizeof(buf) - 1] = '\0';
+ zlog_info("Computing addpath IDs for addpath type %s",
+ bgp_addpath_names(addpath_type)->human_name);
+
+ bgp->tx_addpath.id_allocators[afi][safi][addpath_type] =
+ idalloc_new(buf);
+
+ idalloc_reserve(bgp->tx_addpath.id_allocators[afi][safi][addpath_type],
+ BGP_ADDPATH_TX_ID_FOR_DEFAULT_ORIGINATE);
+
+ allocator = bgp->tx_addpath.id_allocators[afi][safi][addpath_type];
+
+ for (dest = bgp_table_top(bgp->rib[afi][safi]); dest;
+ dest = bgp_route_next(dest)) {
+ struct bgp_path_info *bi;
+
+ if (safi == SAFI_MPLS_VPN) {
+ struct bgp_table *table;
+
+ table = bgp_dest_get_bgp_table_info(dest);
+ if (!table)
+ continue;
+
+ for (ndest = bgp_table_top(table); ndest;
+ ndest = bgp_route_next(ndest))
+ for (bi = bgp_dest_get_bgp_path_info(ndest); bi;
+ bi = bi->next)
+ bgp_addpath_populate_path(allocator, bi,
+ addpath_type);
+ } else {
+ for (bi = bgp_dest_get_bgp_path_info(dest); bi;
+ bi = bi->next)
+ bgp_addpath_populate_path(allocator, bi,
+ addpath_type);
+ }
+ }
+}
+
+/*
+ * Handle updates to a peer or group's addpath strategy. If after adjusting
+ * counts a addpath strategy is in use for the first time, or no longer in use,
+ * the IDs for that strategy will be populated or flushed.
+ */
+void bgp_addpath_type_changed(struct bgp *bgp)
+{
+ afi_t afi;
+ safi_t safi;
+ struct listnode *node, *nnode;
+ struct peer *peer;
+ int peer_count[AFI_MAX][SAFI_MAX][BGP_ADDPATH_MAX];
+ enum bgp_addpath_strat type;
+
+ FOREACH_AFI_SAFI(afi, safi) {
+ for (type=0; type<BGP_ADDPATH_MAX; type++) {
+ peer_count[afi][safi][type] = 0;
+ }
+ bgp->tx_addpath.total_peercount[afi][safi] = 0;
+ }
+
+ for (ALL_LIST_ELEMENTS(bgp->peer, node, nnode, peer)) {
+ FOREACH_AFI_SAFI(afi, safi) {
+ type = peer->addpath_type[afi][safi];
+ if (type != BGP_ADDPATH_NONE) {
+ peer_count[afi][safi][type] += 1;
+ bgp->tx_addpath.total_peercount[afi][safi] += 1;
+ }
+ }
+ }
+
+ FOREACH_AFI_SAFI(afi, safi) {
+ for (type=0; type<BGP_ADDPATH_MAX; type++) {
+ int old = bgp->tx_addpath.peercount[afi][safi][type];
+ int new = peer_count[afi][safi][type];
+
+ bgp->tx_addpath.peercount[afi][safi][type] = new;
+
+ if (old == 0 && new != 0) {
+ bgp_addpath_populate_type(bgp, afi, safi,
+ type);
+ } else if (old != 0 && new == 0) {
+ bgp_addpath_flush_type(bgp, afi, safi, type);
+ }
+ }
+ }
+}
+
+/*
+ * Change the addpath type assigned to a peer, or peer group. In addition to
+ * adjusting the counts, peer sessions will be reset as needed to make the
+ * change take effect.
+ */
+void bgp_addpath_set_peer_type(struct peer *peer, afi_t afi, safi_t safi,
+ enum bgp_addpath_strat addpath_type)
+{
+ struct bgp *bgp = peer->bgp;
+ enum bgp_addpath_strat old_type = peer->addpath_type[afi][safi];
+ struct listnode *node, *nnode;
+ struct peer *tmp_peer;
+ struct peer_group *group;
+
+ if (addpath_type == old_type)
+ return;
+
+ if (addpath_type == BGP_ADDPATH_NONE && peer->group &&
+ !CHECK_FLAG(peer->sflags, PEER_STATUS_GROUP)) {
+ /* A "no" config on a group member inherits group */
+ addpath_type = peer->group->conf->addpath_type[afi][safi];
+ }
+
+ peer->addpath_type[afi][safi] = addpath_type;
+
+ bgp_addpath_type_changed(bgp);
+
+ if (addpath_type != BGP_ADDPATH_NONE) {
+ if (bgp_addpath_dmed_required(addpath_type)) {
+ if (!CHECK_FLAG(bgp->flags,
+ BGP_FLAG_DETERMINISTIC_MED)) {
+ zlog_warn(
+ "%s: enabling bgp deterministic-med, this is required for addpath-tx-bestpath-per-AS",
+ peer->host);
+ SET_FLAG(bgp->flags,
+ BGP_FLAG_DETERMINISTIC_MED);
+ bgp_recalculate_all_bestpaths(bgp);
+ }
+ }
+ }
+
+ zlog_info("Resetting peer %s%s due to change in addpath config",
+ CHECK_FLAG(peer->sflags, PEER_STATUS_GROUP) ? "group " : "",
+ peer->host);
+
+ if (CHECK_FLAG(peer->sflags, PEER_STATUS_GROUP)) {
+ group = peer->group;
+
+ /* group will be null as peer_group_delete calls peer_delete on
+ * group->conf. That peer_delete will eventuallly end up here
+ * if the group was configured to tx addpaths.
+ */
+ if (group != NULL) {
+ for (ALL_LIST_ELEMENTS(group->peer, node, nnode,
+ tmp_peer)) {
+ if (tmp_peer->addpath_type[afi][safi] ==
+ old_type) {
+ bgp_addpath_set_peer_type(tmp_peer,
+ afi,
+ safi,
+ addpath_type);
+ }
+ }
+ }
+ } else {
+ peer_change_action(peer, afi, safi, peer_change_reset);
+ }
+
+}
+
+/*
+ * Intended to run after bestpath. This function will take TX IDs from paths
+ * that no longer need them, and give them to paths that do. This prevents
+ * best-per-as updates from needing to do a separate withdraw and update just to
+ * swap out which path is sent.
+ */
+void bgp_addpath_update_ids(struct bgp *bgp, struct bgp_dest *bn, afi_t afi,
+ safi_t safi)
+{
+ int i;
+ struct bgp_path_info *pi;
+ struct id_alloc_pool **pool_ptr;
+
+ for (i = 0; i < BGP_ADDPATH_MAX; i++) {
+ struct id_alloc *alloc =
+ bgp->tx_addpath.id_allocators[afi][safi][i];
+ pool_ptr = &(bn->tx_addpath.free_ids[i]);
+
+ if (bgp->tx_addpath.peercount[afi][safi][i] == 0)
+ continue;
+
+ /* Free Unused IDs back to the pool.*/
+ for (pi = bgp_dest_get_bgp_path_info(bn); pi; pi = pi->next) {
+ if (pi->tx_addpath.addpath_tx_id[i] != IDALLOC_INVALID
+ && !bgp_addpath_tx_path(i, pi)) {
+ idalloc_free_to_pool(pool_ptr,
+ pi->tx_addpath.addpath_tx_id[i]);
+ pi->tx_addpath.addpath_tx_id[i] =
+ IDALLOC_INVALID;
+ }
+ }
+
+ /* Give IDs to paths that need them (pulling from the pool) */
+ for (pi = bgp_dest_get_bgp_path_info(bn); pi; pi = pi->next) {
+ if (pi->tx_addpath.addpath_tx_id[i] == IDALLOC_INVALID
+ && bgp_addpath_tx_path(i, pi)) {
+ pi->tx_addpath.addpath_tx_id[i] =
+ idalloc_allocate_prefer_pool(
+ alloc, pool_ptr);
+ }
+ }
+
+ /* Free any IDs left in the pool to the main allocator */
+ idalloc_drain_pool(alloc, pool_ptr);
+ }
+}
diff --git a/bgpd/bgp_addpath.h b/bgpd/bgp_addpath.h
new file mode 100644
index 0000000..0350ee8
--- /dev/null
+++ b/bgpd/bgp_addpath.h
@@ -0,0 +1,72 @@
+/*
+ * Addpath TX ID selection, and related utilities
+ * Copyright (C) 2018 Amazon.com, Inc. or its affiliates
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License as published by the Free
+ * Software Foundation; either version 2 of the License, or (at your option)
+ * any later version.
+ *
+ * This program is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
+ * more details.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program; see the file COPYING; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
+ */
+
+#ifndef _QUAGGA_BGPD_TX_ADDPATH_H
+#define _QUAGGA_BGPD_TX_ADDPATH_H
+
+#include <stdint.h>
+#include <zebra.h>
+
+#include "bgpd/bgp_addpath_types.h"
+#include "bgpd/bgp_route.h"
+#include "bgpd/bgp_table.h"
+#include "lib/json.h"
+
+#define BGP_ADDPATH_TX_ID_FOR_DEFAULT_ORIGINATE 1
+
+void bgp_addpath_init_bgp_data(struct bgp_addpath_bgp_data *d);
+
+bool bgp_addpath_is_addpath_used(struct bgp_addpath_bgp_data *d, afi_t afi,
+ safi_t safi);
+
+void bgp_addpath_free_node_data(struct bgp_addpath_bgp_data *bd,
+ struct bgp_addpath_node_data *nd,
+ afi_t afi, safi_t safi);
+
+void bgp_addpath_free_info_data(struct bgp_addpath_info_data *d,
+ struct bgp_addpath_node_data *nd);
+
+
+bool bgp_addpath_info_has_ids(struct bgp_addpath_info_data *d);
+
+uint32_t bgp_addpath_id_for_peer(struct peer *peer, afi_t afi, safi_t safi,
+ struct bgp_addpath_info_data *d);
+
+const struct bgp_addpath_strategy_names *
+bgp_addpath_names(enum bgp_addpath_strat strat);
+
+bool bgp_addpath_dmed_required(int strategy);
+
+/*
+ * Return true if this is a path we should advertise due to a configured
+ * addpath-tx knob
+ */
+bool bgp_addpath_tx_path(enum bgp_addpath_strat strat,
+ struct bgp_path_info *pi);
+/*
+ * Change the type of addpath used for a peer.
+ */
+void bgp_addpath_set_peer_type(struct peer *peer, afi_t afi, safi_t safi,
+ enum bgp_addpath_strat addpath_type);
+
+void bgp_addpath_update_ids(struct bgp *bgp, struct bgp_dest *dest, afi_t afi,
+ safi_t safi);
+
+void bgp_addpath_type_changed(struct bgp *bgp);
+#endif
diff --git a/bgpd/bgp_addpath_types.h b/bgpd/bgp_addpath_types.h
new file mode 100644
index 0000000..b0b3302
--- /dev/null
+++ b/bgpd/bgp_addpath_types.h
@@ -0,0 +1,55 @@
+/*
+ * Addpath TX ID selection, and related utilities
+ * Copyright (C) 2018 Amazon.com, Inc. or its affiliates
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License as published by the Free
+ * Software Foundation; either version 2 of the License, or (at your option)
+ * any later version.
+ *
+ * This program is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
+ * more details.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program; see the file COPYING; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
+ */
+
+#ifndef _QUAGGA_BGPD_TX_ADDPATH_DATA_H
+#define _QUAGGA_BGPD_TX_ADDPATH_DATA_H
+#include "lib/id_alloc.h"
+#include <stdint.h>
+
+enum bgp_addpath_strat {
+ BGP_ADDPATH_ALL = 0,
+ BGP_ADDPATH_BEST_PER_AS,
+ BGP_ADDPATH_MAX,
+ BGP_ADDPATH_NONE,
+};
+
+/* TX Addpath structures */
+struct bgp_addpath_bgp_data {
+ unsigned int peercount[AFI_MAX][SAFI_MAX][BGP_ADDPATH_MAX];
+ unsigned int total_peercount[AFI_MAX][SAFI_MAX];
+ struct id_alloc *id_allocators[AFI_MAX][SAFI_MAX][BGP_ADDPATH_MAX];
+};
+
+struct bgp_addpath_node_data {
+ struct id_alloc_pool *free_ids[BGP_ADDPATH_MAX];
+};
+
+struct bgp_addpath_info_data {
+ uint32_t addpath_tx_id[BGP_ADDPATH_MAX];
+};
+
+struct bgp_addpath_strategy_names {
+ const char *config_name;
+ const char *human_name; /* path detail non-json */
+ const char *human_description; /* non-json peer descriptions */
+ const char *type_json_name; /* json peer listings */
+ const char *id_json_name; /* path json output for tx ID# */
+};
+
+#endif
diff --git a/bgpd/bgp_advertise.c b/bgpd/bgp_advertise.c
new file mode 100644
index 0000000..f62a54b
--- /dev/null
+++ b/bgpd/bgp_advertise.c
@@ -0,0 +1,262 @@
+/* BGP advertisement and adjacency
+ * Copyright (C) 1996, 97, 98, 99, 2000 Kunihiro Ishiguro
+ *
+ * This file is part of GNU Zebra.
+ *
+ * GNU Zebra is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License as published by the
+ * Free Software Foundation; either version 2, or (at your option) any
+ * later version.
+ *
+ * GNU Zebra is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program; see the file COPYING; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
+ */
+
+#include <zebra.h>
+
+#include "command.h"
+#include "memory.h"
+#include "prefix.h"
+#include "hash.h"
+#include "thread.h"
+#include "queue.h"
+#include "filter.h"
+
+#include "bgpd/bgpd.h"
+#include "bgpd/bgp_table.h"
+#include "bgpd/bgp_route.h"
+#include "bgpd/bgp_advertise.h"
+#include "bgpd/bgp_attr.h"
+#include "bgpd/bgp_debug.h"
+#include "bgpd/bgp_aspath.h"
+#include "bgpd/bgp_packet.h"
+#include "bgpd/bgp_fsm.h"
+#include "bgpd/bgp_mplsvpn.h"
+#include "bgpd/bgp_updgrp.h"
+
+/* BGP advertise attribute is used for pack same attribute update into
+ one packet. To do that we maintain attribute hash in struct
+ peer. */
+struct bgp_advertise_attr *bgp_advertise_attr_new(void)
+{
+ return XCALLOC(MTYPE_BGP_ADVERTISE_ATTR,
+ sizeof(struct bgp_advertise_attr));
+}
+
+void bgp_advertise_attr_free(struct bgp_advertise_attr *baa)
+{
+ XFREE(MTYPE_BGP_ADVERTISE_ATTR, baa);
+}
+
+static void *bgp_advertise_attr_hash_alloc(void *p)
+{
+ struct bgp_advertise_attr *ref = (struct bgp_advertise_attr *)p;
+ struct bgp_advertise_attr *baa;
+
+ baa = bgp_advertise_attr_new();
+ baa->attr = ref->attr;
+ return baa;
+}
+
+unsigned int bgp_advertise_attr_hash_key(const void *p)
+{
+ const struct bgp_advertise_attr *baa = p;
+
+ return attrhash_key_make(baa->attr);
+}
+
+bool bgp_advertise_attr_hash_cmp(const void *p1, const void *p2)
+{
+ const struct bgp_advertise_attr *baa1 = p1;
+ const struct bgp_advertise_attr *baa2 = p2;
+
+ return attrhash_cmp(baa1->attr, baa2->attr);
+}
+
+/* BGP update and withdraw information is stored in BGP advertise
+ structure. This structure is referred from BGP adjacency
+ information. */
+struct bgp_advertise *bgp_advertise_new(void)
+{
+ return XCALLOC(MTYPE_BGP_ADVERTISE, sizeof(struct bgp_advertise));
+}
+
+void bgp_advertise_free(struct bgp_advertise *adv)
+{
+ if (adv->pathi)
+ /* bgp_advertise bgp_path_info reference */
+ bgp_path_info_unlock(adv->pathi);
+ XFREE(MTYPE_BGP_ADVERTISE, adv);
+}
+
+void bgp_advertise_add(struct bgp_advertise_attr *baa,
+ struct bgp_advertise *adv)
+{
+ adv->next = baa->adv;
+ if (baa->adv)
+ baa->adv->prev = adv;
+ baa->adv = adv;
+}
+
+void bgp_advertise_delete(struct bgp_advertise_attr *baa,
+ struct bgp_advertise *adv)
+{
+ if (adv->next)
+ adv->next->prev = adv->prev;
+ if (adv->prev)
+ adv->prev->next = adv->next;
+ else
+ baa->adv = adv->next;
+}
+
+struct bgp_advertise_attr *bgp_advertise_attr_intern(struct hash *hash,
+ struct attr *attr)
+{
+ struct bgp_advertise_attr ref;
+ struct bgp_advertise_attr *baa;
+
+ ref.attr = bgp_attr_intern(attr);
+ baa = (struct bgp_advertise_attr *)hash_get(
+ hash, &ref, bgp_advertise_attr_hash_alloc);
+ baa->refcnt++;
+
+ return baa;
+}
+
+void bgp_advertise_attr_unintern(struct hash *hash,
+ struct bgp_advertise_attr *baa)
+{
+ if (baa->refcnt)
+ baa->refcnt--;
+
+ if (baa->refcnt && baa->attr)
+ bgp_attr_unintern(&baa->attr);
+ else {
+ if (baa->attr) {
+ hash_release(hash, baa);
+ bgp_attr_unintern(&baa->attr);
+ }
+ bgp_advertise_attr_free(baa);
+ }
+}
+
+bool bgp_adj_out_lookup(struct peer *peer, struct bgp_dest *dest,
+ uint32_t addpath_tx_id)
+{
+ struct bgp_adj_out *adj;
+ struct peer_af *paf;
+ afi_t afi;
+ safi_t safi;
+ bool addpath_capable;
+
+ RB_FOREACH (adj, bgp_adj_out_rb, &dest->adj_out)
+ SUBGRP_FOREACH_PEER (adj->subgroup, paf)
+ if (paf->peer == peer) {
+ afi = SUBGRP_AFI(adj->subgroup);
+ safi = SUBGRP_SAFI(adj->subgroup);
+ addpath_capable =
+ bgp_addpath_encode_tx(peer, afi, safi);
+
+ /* Match on a specific addpath_tx_id if we are
+ * using addpath for
+ * this
+ * peer and if an addpath_tx_id was specified */
+ if (addpath_capable && addpath_tx_id
+ && adj->addpath_tx_id != addpath_tx_id)
+ continue;
+
+ return (adj->adv
+ ? (adj->adv->baa ? true : false)
+ : (adj->attr ? true : false));
+ }
+
+ return false;
+}
+
+
+void bgp_adj_in_set(struct bgp_dest *dest, struct peer *peer, struct attr *attr,
+ uint32_t addpath_id)
+{
+ struct bgp_adj_in *adj;
+
+ for (adj = dest->adj_in; adj; adj = adj->next) {
+ if (adj->peer == peer && adj->addpath_rx_id == addpath_id) {
+ if (adj->attr != attr) {
+ bgp_attr_unintern(&adj->attr);
+ adj->attr = bgp_attr_intern(attr);
+ }
+ return;
+ }
+ }
+ adj = XCALLOC(MTYPE_BGP_ADJ_IN, sizeof(struct bgp_adj_in));
+ adj->peer = peer_lock(peer); /* adj_in peer reference */
+ adj->attr = bgp_attr_intern(attr);
+ adj->uptime = monotime(NULL);
+ adj->addpath_rx_id = addpath_id;
+ BGP_ADJ_IN_ADD(dest, adj);
+ bgp_dest_lock_node(dest);
+}
+
+void bgp_adj_in_remove(struct bgp_dest *dest, struct bgp_adj_in *bai)
+{
+ bgp_attr_unintern(&bai->attr);
+ BGP_ADJ_IN_DEL(dest, bai);
+ bgp_dest_unlock_node(dest);
+ peer_unlock(bai->peer); /* adj_in peer reference */
+ XFREE(MTYPE_BGP_ADJ_IN, bai);
+}
+
+bool bgp_adj_in_unset(struct bgp_dest *dest, struct peer *peer,
+ uint32_t addpath_id)
+{
+ struct bgp_adj_in *adj;
+ struct bgp_adj_in *adj_next;
+
+ adj = dest->adj_in;
+
+ if (!adj)
+ return false;
+
+ while (adj) {
+ adj_next = adj->next;
+
+ if (adj->peer == peer && adj->addpath_rx_id == addpath_id)
+ bgp_adj_in_remove(dest, adj);
+
+ adj = adj_next;
+ }
+
+ return true;
+}
+
+void bgp_sync_init(struct peer *peer)
+{
+ afi_t afi;
+ safi_t safi;
+ struct bgp_synchronize *sync;
+
+ FOREACH_AFI_SAFI (afi, safi) {
+ sync = XCALLOC(MTYPE_BGP_SYNCHRONISE,
+ sizeof(struct bgp_synchronize));
+ bgp_adv_fifo_init(&sync->update);
+ bgp_adv_fifo_init(&sync->withdraw);
+ bgp_adv_fifo_init(&sync->withdraw_low);
+ peer->sync[afi][safi] = sync;
+ }
+}
+
+void bgp_sync_delete(struct peer *peer)
+{
+ afi_t afi;
+ safi_t safi;
+
+ FOREACH_AFI_SAFI (afi, safi) {
+ XFREE(MTYPE_BGP_SYNCHRONISE, peer->sync[afi][safi]);
+ }
+}
diff --git a/bgpd/bgp_advertise.h b/bgpd/bgp_advertise.h
new file mode 100644
index 0000000..70294c2
--- /dev/null
+++ b/bgpd/bgp_advertise.h
@@ -0,0 +1,170 @@
+/* BGP advertisement and adjacency
+ * Copyright (C) 1996, 97, 98, 99, 2000 Kunihiro Ishiguro
+ *
+ * This file is part of GNU Zebra.
+ *
+ * GNU Zebra is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License as published by the
+ * Free Software Foundation; either version 2, or (at your option) any
+ * later version.
+ *
+ * GNU Zebra is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program; see the file COPYING; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
+ */
+
+#ifndef _QUAGGA_BGP_ADVERTISE_H
+#define _QUAGGA_BGP_ADVERTISE_H
+
+#include "lib/typesafe.h"
+
+PREDECL_DLIST(bgp_adv_fifo);
+
+struct update_subgroup;
+
+/* BGP advertise attribute. */
+struct bgp_advertise_attr {
+ /* Head of advertisement pointer. */
+ struct bgp_advertise *adv;
+
+ /* Reference counter. */
+ unsigned long refcnt;
+
+ /* Attribute pointer to be announced. */
+ struct attr *attr;
+};
+
+struct bgp_advertise {
+ /* FIFO for advertisement. */
+ struct bgp_adv_fifo_item fifo;
+
+ /* Link list for same attribute advertise. */
+ struct bgp_advertise *next;
+ struct bgp_advertise *prev;
+
+ /* Prefix information. */
+ struct bgp_dest *dest;
+
+ /* Reference pointer. */
+ struct bgp_adj_out *adj;
+
+ /* Advertisement attribute. */
+ struct bgp_advertise_attr *baa;
+
+ /* BGP info. */
+ struct bgp_path_info *pathi;
+};
+
+DECLARE_DLIST(bgp_adv_fifo, struct bgp_advertise, fifo);
+
+/* BGP adjacency out. */
+struct bgp_adj_out {
+ /* RB Tree of adjacency entries */
+ RB_ENTRY(bgp_adj_out) adj_entry;
+
+ /* Advertised subgroup. */
+ struct update_subgroup *subgroup;
+
+ /* Threading that makes the adj part of subgroup's adj queue */
+ TAILQ_ENTRY(bgp_adj_out) subgrp_adj_train;
+
+ /* Prefix information. */
+ struct bgp_dest *dest;
+
+ uint32_t addpath_tx_id;
+
+ /* Advertised attribute. */
+ struct attr *attr;
+
+ /* Advertisement information. */
+ struct bgp_advertise *adv;
+
+ /* Attribute hash */
+ uint32_t attr_hash;
+};
+
+RB_HEAD(bgp_adj_out_rb, bgp_adj_out);
+RB_PROTOTYPE(bgp_adj_out_rb, bgp_adj_out, adj_entry,
+ bgp_adj_out_compare);
+
+/* BGP adjacency in. */
+struct bgp_adj_in {
+ /* Linked list pointer. */
+ struct bgp_adj_in *next;
+ struct bgp_adj_in *prev;
+
+ /* Received peer. */
+ struct peer *peer;
+
+ /* Received attribute. */
+ struct attr *attr;
+
+ /* timestamp (monotime) */
+ time_t uptime;
+
+ /* Addpath identifier */
+ uint32_t addpath_rx_id;
+};
+
+/* BGP advertisement list. */
+struct bgp_synchronize {
+ struct bgp_adv_fifo_head update;
+ struct bgp_adv_fifo_head withdraw;
+ struct bgp_adv_fifo_head withdraw_low;
+};
+
+/* BGP adjacency linked list. */
+#define BGP_PATH_INFO_ADD(N, A, TYPE) \
+ do { \
+ (A)->prev = NULL; \
+ (A)->next = (N)->TYPE; \
+ if ((N)->TYPE) \
+ (N)->TYPE->prev = (A); \
+ (N)->TYPE = (A); \
+ } while (0)
+
+#define BGP_PATH_INFO_DEL(N, A, TYPE) \
+ do { \
+ if ((A)->next) \
+ (A)->next->prev = (A)->prev; \
+ if ((A)->prev) \
+ (A)->prev->next = (A)->next; \
+ else \
+ (N)->TYPE = (A)->next; \
+ } while (0)
+
+#define BGP_ADJ_IN_ADD(N, A) BGP_PATH_INFO_ADD(N, A, adj_in)
+#define BGP_ADJ_IN_DEL(N, A) BGP_PATH_INFO_DEL(N, A, adj_in)
+
+/* Prototypes. */
+extern bool bgp_adj_out_lookup(struct peer *peer, struct bgp_dest *dest,
+ uint32_t addpath_tx_id);
+extern void bgp_adj_in_set(struct bgp_dest *dest, struct peer *peer,
+ struct attr *attr, uint32_t addpath_id);
+extern bool bgp_adj_in_unset(struct bgp_dest *dest, struct peer *peer,
+ uint32_t addpath_id);
+extern void bgp_adj_in_remove(struct bgp_dest *dest, struct bgp_adj_in *bai);
+
+extern void bgp_sync_init(struct peer *peer);
+extern void bgp_sync_delete(struct peer *peer);
+extern unsigned int bgp_advertise_attr_hash_key(const void *p);
+extern bool bgp_advertise_attr_hash_cmp(const void *p1, const void *p2);
+extern void bgp_advertise_add(struct bgp_advertise_attr *baa,
+ struct bgp_advertise *adv);
+extern struct bgp_advertise *bgp_advertise_new(void);
+extern void bgp_advertise_free(struct bgp_advertise *adv);
+extern struct bgp_advertise_attr *bgp_advertise_attr_intern(struct hash *hash,
+ struct attr *attr);
+extern struct bgp_advertise_attr *bgp_advertise_attr_new(void);
+extern void bgp_advertise_delete(struct bgp_advertise_attr *baa,
+ struct bgp_advertise *adv);
+extern void bgp_advertise_attr_unintern(struct hash *hash,
+ struct bgp_advertise_attr *baa);
+extern void bgp_advertise_attr_free(struct bgp_advertise_attr *baa);
+
+#endif /* _QUAGGA_BGP_ADVERTISE_H */
diff --git a/bgpd/bgp_aspath.c b/bgpd/bgp_aspath.c
new file mode 100644
index 0000000..06f6073
--- /dev/null
+++ b/bgpd/bgp_aspath.c
@@ -0,0 +1,2276 @@
+/* AS path management routines.
+ * Copyright (C) 1996, 97, 98, 99 Kunihiro Ishiguro
+ * Copyright (C) 2005 Sun Microsystems, Inc.
+ *
+ * This file is part of GNU Zebra.
+ *
+ * GNU Zebra is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License as published by the
+ * Free Software Foundation; either version 2, or (at your option) any
+ * later version.
+ *
+ * GNU Zebra is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program; see the file COPYING; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
+ */
+
+#include <zebra.h>
+
+#include "hash.h"
+#include "memory.h"
+#include "vector.h"
+#include "log.h"
+#include "stream.h"
+#include "command.h"
+#include "jhash.h"
+#include "queue.h"
+#include "filter.h"
+
+#include "bgpd/bgpd.h"
+#include "bgpd/bgp_aspath.h"
+#include "bgpd/bgp_debug.h"
+#include "bgpd/bgp_attr.h"
+#include "bgpd/bgp_errors.h"
+
+/* Attr. Flags and Attr. Type Code. */
+#define AS_HEADER_SIZE 2
+
+/* Now FOUR octets are used for AS value. */
+#define AS_VALUE_SIZE sizeof(as_t)
+/* This is the old one */
+#define AS16_VALUE_SIZE sizeof(as16_t)
+
+/* Maximum protocol segment length value */
+#define AS_SEGMENT_MAX 255
+
+/* The following length and size macros relate specifically to Quagga's
+ * internal representation of AS-Segments, not per se to the on-wire
+ * sizes and lengths. At present (200508) they sort of match, however
+ * the ONLY functions which should now about the on-wire syntax are
+ * aspath_put, assegment_put and assegment_parse.
+ *
+ * aspath_put returns bytes written, the only definitive record of
+ * size of wire-format attribute..
+ */
+
+/* Calculated size in bytes of ASN segment data to hold N ASN's */
+#define ASSEGMENT_DATA_SIZE(N, S) \
+ ((N) * ((S) ? AS_VALUE_SIZE : AS16_VALUE_SIZE))
+
+/* Calculated size of segment struct to hold N ASN's */
+#define ASSEGMENT_SIZE(N,S) (AS_HEADER_SIZE + ASSEGMENT_DATA_SIZE (N,S))
+
+/* AS segment octet length. */
+#define ASSEGMENT_LEN(X,S) ASSEGMENT_SIZE((X)->length,S)
+
+/* AS_SEQUENCE segments can be packed together */
+/* Can the types of X and Y be considered for packing? */
+#define ASSEGMENT_TYPES_PACKABLE(X, Y) \
+ (((X)->type == (Y)->type) && ((X)->type == AS_SEQUENCE))
+/* Types and length of X,Y suitable for packing? */
+#define ASSEGMENTS_PACKABLE(X, Y) \
+ (ASSEGMENT_TYPES_PACKABLE((X), (Y)) \
+ && (((X)->length + (Y)->length) <= AS_SEGMENT_MAX))
+
+/* As segment header - the on-wire representation
+ * NOT the internal representation!
+ */
+struct assegment_header {
+ uint8_t type;
+ uint8_t length;
+};
+
+/* Hash for aspath. This is the top level structure of AS path. */
+static struct hash *ashash;
+
+/* Stream for SNMP. See aspath_snmp_pathseg */
+static struct stream *snmp_stream;
+
+/* Callers are required to initialize the memory */
+static as_t *assegment_data_new(int num)
+{
+ return (XMALLOC(MTYPE_AS_SEG_DATA, ASSEGMENT_DATA_SIZE(num, 1)));
+}
+
+static void assegment_data_free(as_t *asdata)
+{
+ XFREE(MTYPE_AS_SEG_DATA, asdata);
+}
+
+const char *const aspath_segment_type_str[] = {
+ "as-invalid", "as-set", "as-sequence", "as-confed-sequence",
+ "as-confed-set"
+};
+
+/* Get a new segment. Note that 0 is an allowed length,
+ * and will result in a segment with no allocated data segment.
+ * the caller should immediately assign data to the segment, as the segment
+ * otherwise is not generally valid
+ */
+static struct assegment *assegment_new(uint8_t type, unsigned short length)
+{
+ struct assegment *new;
+
+ new = XCALLOC(MTYPE_AS_SEG, sizeof(struct assegment));
+
+ if (length)
+ new->as = assegment_data_new(length);
+
+ new->length = length;
+ new->type = type;
+
+ return new;
+}
+
+static void assegment_free(struct assegment *seg)
+{
+ if (!seg)
+ return;
+
+ assegment_data_free(seg->as);
+ memset(seg, 0xfe, sizeof(struct assegment));
+ XFREE(MTYPE_AS_SEG, seg);
+
+ return;
+}
+
+/* free entire chain of segments */
+static void assegment_free_all(struct assegment *seg)
+{
+ struct assegment *prev;
+
+ while (seg) {
+ prev = seg;
+ seg = seg->next;
+ assegment_free(prev);
+ }
+}
+
+/* Duplicate just the given assegment and its data */
+static struct assegment *assegment_dup(struct assegment *seg)
+{
+ struct assegment *new;
+
+ new = assegment_new(seg->type, seg->length);
+ memcpy(new->as, seg->as, ASSEGMENT_DATA_SIZE(new->length, 1));
+
+ return new;
+}
+
+/* Duplicate entire chain of assegments, return the head */
+static struct assegment *assegment_dup_all(struct assegment *seg)
+{
+ struct assegment *new = NULL;
+ struct assegment *head = NULL;
+
+ while (seg) {
+ if (head) {
+ new->next = assegment_dup(seg);
+ new = new->next;
+ } else
+ head = new = assegment_dup(seg);
+
+ seg = seg->next;
+ }
+ return head;
+}
+
+/* prepend the as number to given segment, given num of times */
+static struct assegment *assegment_prepend_asns(struct assegment *seg,
+ as_t asnum, int num)
+{
+ as_t *newas;
+ int i;
+
+ if (!num)
+ return seg;
+
+ if (num >= AS_SEGMENT_MAX)
+ return seg; /* we don't do huge prepends */
+
+ newas = assegment_data_new(seg->length + num);
+ if (newas == NULL)
+ return seg;
+
+ for (i = 0; i < num; i++)
+ newas[i] = asnum;
+
+ memcpy(newas + num, seg->as, ASSEGMENT_DATA_SIZE(seg->length, 1));
+ assegment_data_free(seg->as);
+ seg->as = newas;
+ seg->length += num;
+
+ return seg;
+}
+
+/* append given array of as numbers to the segment */
+static struct assegment *assegment_append_asns(struct assegment *seg,
+ as_t *asnos, int num)
+{
+ as_t *newas;
+
+ if (!seg)
+ return seg;
+
+ newas = XREALLOC(MTYPE_AS_SEG_DATA, seg->as,
+ ASSEGMENT_DATA_SIZE(seg->length + num, 1));
+
+ seg->as = newas;
+ memcpy(seg->as + seg->length, asnos,
+ ASSEGMENT_DATA_SIZE(num, 1));
+ seg->length += num;
+ return seg;
+}
+
+static int int_cmp(const void *p1, const void *p2)
+{
+ const as_t *as1 = p1;
+ const as_t *as2 = p2;
+
+ return (*as1 == *as2) ? 0 : ((*as1 > *as2) ? 1 : -1);
+}
+
+/* normalise the segment.
+ * In particular, merge runs of AS_SEQUENCEs into one segment
+ * Internally, we do not care about the wire segment length limit, and
+ * we want each distinct AS_PATHs to have the exact same internal
+ * representation - eg, so that our hashing actually works..
+ */
+static struct assegment *assegment_normalise(struct assegment *head)
+{
+ struct assegment *seg = head, *pin;
+ struct assegment *tmp;
+
+ if (!head)
+ return head;
+
+ while (seg) {
+ pin = seg;
+
+ /* Sort values SET segments, for determinism in paths to aid
+ * creation of hash values / path comparisons
+ * and because it helps other lesser implementations ;)
+ */
+ if (seg->type == AS_SET || seg->type == AS_CONFED_SET) {
+ int tail = 0;
+ int i;
+
+ qsort(seg->as, seg->length, sizeof(as_t), int_cmp);
+
+ /* weed out dupes */
+ for (i = 1; i < seg->length; i++) {
+ if (seg->as[tail] == seg->as[i])
+ continue;
+
+ tail++;
+ if (tail < i)
+ seg->as[tail] = seg->as[i];
+ }
+ /* seg->length can be 0.. */
+ if (seg->length)
+ seg->length = tail + 1;
+ }
+
+ /* read ahead from the current, pinned segment while the
+ * segments
+ * are packable/mergeable. Append all following packable
+ * segments
+ * to the segment we have pinned and remove these appended
+ * segments.
+ */
+ while (pin->next && ASSEGMENT_TYPES_PACKABLE(pin, pin->next)) {
+ tmp = pin->next;
+ seg = pin->next;
+
+ /* append the next sequence to the pinned sequence */
+ pin = assegment_append_asns(pin, seg->as, seg->length);
+
+ /* bypass the next sequence */
+ pin->next = seg->next;
+
+ /* get rid of the now referenceless segment */
+ assegment_free(tmp);
+ }
+
+ seg = pin->next;
+ }
+ return head;
+}
+
+static struct aspath *aspath_new(void)
+{
+ return XCALLOC(MTYPE_AS_PATH, sizeof(struct aspath));
+}
+
+/* Free AS path structure. */
+void aspath_free(struct aspath *aspath)
+{
+ if (!aspath)
+ return;
+ if (aspath->segments)
+ assegment_free_all(aspath->segments);
+ XFREE(MTYPE_AS_STR, aspath->str);
+
+ if (aspath->json) {
+ json_object_free(aspath->json);
+ aspath->json = NULL;
+ }
+
+ XFREE(MTYPE_AS_PATH, aspath);
+}
+
+/* Unintern aspath from AS path bucket. */
+void aspath_unintern(struct aspath **aspath)
+{
+ struct aspath *ret;
+ struct aspath *asp;
+
+ if (!*aspath)
+ return;
+
+ asp = *aspath;
+
+ if (asp->refcnt)
+ asp->refcnt--;
+
+ if (asp->refcnt == 0) {
+ /* This aspath must exist in aspath hash table. */
+ ret = hash_release(ashash, asp);
+ assert(ret != NULL);
+ aspath_free(asp);
+ *aspath = NULL;
+ }
+}
+
+/* Return the start or end delimiters for a particular Segment type */
+#define AS_SEG_START 0
+#define AS_SEG_END 1
+static char aspath_delimiter_char(uint8_t type, uint8_t which)
+{
+ int i;
+ struct {
+ int type;
+ char start;
+ char end;
+ } aspath_delim_char[] = {{AS_SET, '{', '}'},
+ {AS_CONFED_SET, '[', ']'},
+ {AS_CONFED_SEQUENCE, '(', ')'},
+ {0}};
+
+ for (i = 0; aspath_delim_char[i].type != 0; i++) {
+ if (aspath_delim_char[i].type == type) {
+ if (which == AS_SEG_START)
+ return aspath_delim_char[i].start;
+ else if (which == AS_SEG_END)
+ return aspath_delim_char[i].end;
+ }
+ }
+ return ' ';
+}
+
+/* countup asns from this segment and index onward */
+static int assegment_count_asns(struct assegment *seg, int from)
+{
+ int count = 0;
+ while (seg) {
+ if (!from)
+ count += seg->length;
+ else {
+ count += (seg->length - from);
+ from = 0;
+ }
+ seg = seg->next;
+ }
+ return count;
+}
+
+unsigned int aspath_count_confeds(struct aspath *aspath)
+{
+ int count = 0;
+ struct assegment *seg = aspath->segments;
+
+ while (seg) {
+ if (seg->type == AS_CONFED_SEQUENCE)
+ count += seg->length;
+ else if (seg->type == AS_CONFED_SET)
+ count++;
+
+ seg = seg->next;
+ }
+ return count;
+}
+
+unsigned int aspath_count_hops(const struct aspath *aspath)
+{
+ int count = 0;
+ struct assegment *seg = aspath->segments;
+
+ while (seg) {
+ if (seg->type == AS_SEQUENCE)
+ count += seg->length;
+ else if (seg->type == AS_SET)
+ count++;
+
+ seg = seg->next;
+ }
+ return count;
+}
+
+/* Check if aspath has AS_SET or AS_CONFED_SET */
+bool aspath_check_as_sets(struct aspath *aspath)
+{
+ struct assegment *seg = aspath->segments;
+
+ while (seg) {
+ if (seg->type == AS_SET || seg->type == AS_CONFED_SET)
+ return true;
+ seg = seg->next;
+ }
+ return false;
+}
+
+/* Check if aspath has BGP_AS_ZERO */
+bool aspath_check_as_zero(struct aspath *aspath)
+{
+ struct assegment *seg = aspath->segments;
+ unsigned int i;
+
+ while (seg) {
+ for (i = 0; i < seg->length; i++)
+ if (seg->as[i] == BGP_AS_ZERO)
+ return true;
+ seg = seg->next;
+ }
+
+ return false;
+}
+
+/* Estimate size aspath /might/ take if encoded into an
+ * ASPATH attribute.
+ *
+ * This is a quick estimate, not definitive! aspath_put()
+ * may return a different number!!
+ */
+unsigned int aspath_size(struct aspath *aspath)
+{
+ int size = 0;
+ struct assegment *seg = aspath->segments;
+
+ while (seg) {
+ size += ASSEGMENT_SIZE(seg->length, 1);
+ seg = seg->next;
+ }
+ return size;
+}
+
+/* Return highest public ASN in path */
+as_t aspath_highest(struct aspath *aspath)
+{
+ struct assegment *seg = aspath->segments;
+ as_t highest = 0;
+ unsigned int i;
+
+ while (seg) {
+ for (i = 0; i < seg->length; i++)
+ if (seg->as[i] > highest
+ && !BGP_AS_IS_PRIVATE(seg->as[i]))
+ highest = seg->as[i];
+ seg = seg->next;
+ }
+ return highest;
+}
+
+/* Return the left-most ASN in path */
+as_t aspath_leftmost(struct aspath *aspath)
+{
+ struct assegment *seg = aspath->segments;
+ as_t leftmost = 0;
+
+ if (seg && seg->length && seg->type == AS_SEQUENCE)
+ leftmost = seg->as[0];
+
+ return leftmost;
+}
+
+/* Return 1 if there are any 4-byte ASes in the path */
+bool aspath_has_as4(struct aspath *aspath)
+{
+ struct assegment *seg = aspath->segments;
+ unsigned int i;
+
+ while (seg) {
+ for (i = 0; i < seg->length; i++)
+ if (seg->as[i] > BGP_AS_MAX)
+ return true;
+ seg = seg->next;
+ }
+ return false;
+}
+
+/* Convert aspath structure to string expression. */
+static void aspath_make_str_count(struct aspath *as, bool make_json)
+{
+ struct assegment *seg;
+ int str_size;
+ int len = 0;
+ char *str_buf;
+ json_object *jaspath_segments = NULL;
+ json_object *jseg = NULL;
+ json_object *jseg_list = NULL;
+
+ if (make_json) {
+ as->json = json_object_new_object();
+ jaspath_segments = json_object_new_array();
+ }
+
+ /* Empty aspath. */
+ if (!as->segments) {
+ if (make_json) {
+ json_object_string_add(as->json, "string", "Local");
+ json_object_object_add(as->json, "segments",
+ jaspath_segments);
+ json_object_int_add(as->json, "length", 0);
+ }
+ as->str = XMALLOC(MTYPE_AS_STR, 1);
+ as->str[0] = '\0';
+ as->str_len = 0;
+ return;
+ }
+
+ seg = as->segments;
+
+/* ASN takes 5 to 10 chars plus separator, see below.
+ * If there is one differing segment type, we need an additional
+ * 2 chars for segment delimiters, and the final '\0'.
+ * Hopefully this is large enough to avoid hitting the realloc
+ * code below for most common sequences.
+ *
+ * This was changed to 10 after the well-known BGP assertion, which
+ * had hit some parts of the Internet in May of 2009.
+ */
+#define ASN_STR_LEN (10 + 1)
+ str_size = MAX(assegment_count_asns(seg, 0) * ASN_STR_LEN + 2 + 1,
+ ASPATH_STR_DEFAULT_LEN);
+ str_buf = XMALLOC(MTYPE_AS_STR, str_size);
+
+ while (seg) {
+ int i;
+ char separator;
+
+ /* Check AS type validity. Set separator for segment */
+ switch (seg->type) {
+ case AS_SET:
+ case AS_CONFED_SET:
+ separator = ',';
+ break;
+ case AS_SEQUENCE:
+ case AS_CONFED_SEQUENCE:
+ separator = ' ';
+ break;
+ default:
+ XFREE(MTYPE_AS_STR, str_buf);
+ as->str = NULL;
+ as->str_len = 0;
+ json_object_free(as->json);
+ as->json = NULL;
+
+ return;
+ }
+
+/* We might need to increase str_buf, particularly if path has
+ * differing segments types, our initial guesstimate above will
+ * have been wrong. Need 10 chars for ASN, a separator each and
+ * potentially two segment delimiters, plus a space between each
+ * segment and trailing zero.
+ *
+ * This definitely didn't work with the value of 5 bytes and
+ * 32-bit ASNs.
+ */
+#define SEGMENT_STR_LEN(X) (((X)->length * ASN_STR_LEN) + 2 + 1 + 1)
+ if ((len + SEGMENT_STR_LEN(seg)) > str_size) {
+ str_size = len + SEGMENT_STR_LEN(seg);
+ str_buf = XREALLOC(MTYPE_AS_STR, str_buf, str_size);
+ }
+#undef ASN_STR_LEN
+#undef SEGMENT_STR_LEN
+
+ if (seg->type != AS_SEQUENCE)
+ len += snprintf(
+ str_buf + len, str_size - len, "%c",
+ aspath_delimiter_char(seg->type, AS_SEG_START));
+
+ if (make_json)
+ jseg_list = json_object_new_array();
+
+ /* write out the ASNs, with their separators, bar the last one*/
+ for (i = 0; i < seg->length; i++) {
+ if (make_json)
+ json_object_array_add(
+ jseg_list,
+ json_object_new_int64(seg->as[i]));
+
+ len += snprintf(str_buf + len, str_size - len, "%u",
+ seg->as[i]);
+
+ if (i < (seg->length - 1))
+ len += snprintf(str_buf + len, str_size - len,
+ "%c", separator);
+ }
+
+ if (make_json) {
+ jseg = json_object_new_object();
+ json_object_string_add(
+ jseg, "type",
+ aspath_segment_type_str[seg->type]);
+ json_object_object_add(jseg, "list", jseg_list);
+ json_object_array_add(jaspath_segments, jseg);
+ }
+
+ if (seg->type != AS_SEQUENCE)
+ len += snprintf(
+ str_buf + len, str_size - len, "%c",
+ aspath_delimiter_char(seg->type, AS_SEG_END));
+ if (seg->next)
+ len += snprintf(str_buf + len, str_size - len, " ");
+
+ seg = seg->next;
+ }
+
+ assert(len < str_size);
+
+ str_buf[len] = '\0';
+ as->str = str_buf;
+ as->str_len = len;
+
+ if (make_json) {
+ json_object_string_add(as->json, "string", str_buf);
+ json_object_object_add(as->json, "segments", jaspath_segments);
+ json_object_int_add(as->json, "length", aspath_count_hops(as));
+ }
+
+ return;
+}
+
+void aspath_str_update(struct aspath *as, bool make_json)
+{
+ XFREE(MTYPE_AS_STR, as->str);
+
+ if (as->json) {
+ json_object_free(as->json);
+ as->json = NULL;
+ }
+
+ aspath_make_str_count(as, make_json);
+}
+
+/* Intern allocated AS path. */
+struct aspath *aspath_intern(struct aspath *aspath)
+{
+ struct aspath *find;
+
+ /* Assert this AS path structure is not interned and has the string
+ representation built. */
+ assert(aspath->refcnt == 0);
+ assert(aspath->str);
+
+ /* Check AS path hash. */
+ find = hash_get(ashash, aspath, hash_alloc_intern);
+ if (find != aspath)
+ aspath_free(aspath);
+
+ find->refcnt++;
+
+ return find;
+}
+
+/* Duplicate aspath structure. Created same aspath structure but
+ reference count and AS path string is cleared. */
+struct aspath *aspath_dup(struct aspath *aspath)
+{
+ unsigned short buflen = aspath->str_len + 1;
+ struct aspath *new;
+
+ new = XCALLOC(MTYPE_AS_PATH, sizeof(struct aspath));
+ new->json = NULL;
+
+ if (aspath->segments)
+ new->segments = assegment_dup_all(aspath->segments);
+
+ if (!aspath->str)
+ return new;
+
+ new->str = XMALLOC(MTYPE_AS_STR, buflen);
+ new->str_len = aspath->str_len;
+
+ /* copy the string data */
+ if (aspath->str_len > 0)
+ memcpy(new->str, aspath->str, buflen);
+ else
+ new->str[0] = '\0';
+
+ return new;
+}
+
+static void *aspath_hash_alloc(void *arg)
+{
+ const struct aspath *aspath = arg;
+ struct aspath *new;
+
+ /* Malformed AS path value. */
+ assert(aspath->str);
+
+ /* New aspath structure is needed. */
+ new = XMALLOC(MTYPE_AS_PATH, sizeof(struct aspath));
+
+ /* Reuse segments and string representation */
+ new->refcnt = 0;
+ new->segments = aspath->segments;
+ new->str = aspath->str;
+ new->str_len = aspath->str_len;
+ new->json = aspath->json;
+
+ return new;
+}
+
+/* parse as-segment byte stream in struct assegment */
+static int assegments_parse(struct stream *s, size_t length,
+ struct assegment **result, int use32bit)
+{
+ struct assegment_header segh;
+ struct assegment *seg, *prev = NULL, *head = NULL;
+ size_t bytes = 0;
+
+ /* empty aspath (ie iBGP or somesuch) */
+ if (length == 0)
+ return 0;
+
+ if (BGP_DEBUG(as4, AS4_SEGMENT))
+ zlog_debug(
+ "[AS4SEG] Parse aspath segment: got total byte length %lu",
+ (unsigned long)length);
+ /* basic checks */
+ if ((STREAM_READABLE(s) < length)
+ || (STREAM_READABLE(s) < AS_HEADER_SIZE)
+ || (length % AS16_VALUE_SIZE))
+ return -1;
+
+ while (bytes < length) {
+ int i;
+ size_t seg_size;
+
+ if ((length - bytes) <= AS_HEADER_SIZE) {
+ if (head)
+ assegment_free_all(head);
+ return -1;
+ }
+
+ /* softly softly, get the header first on its own */
+ segh.type = stream_getc(s);
+ segh.length = stream_getc(s);
+
+ seg_size = ASSEGMENT_SIZE(segh.length, use32bit);
+
+ if (BGP_DEBUG(as4, AS4_SEGMENT))
+ zlog_debug(
+ "[AS4SEG] Parse aspath segment: got type %d, length %d",
+ segh.type, segh.length);
+
+ /* check it.. */
+ if (((bytes + seg_size) > length)
+ /* 1771bis 4.3b: seg length contains one or more */
+ || (segh.length == 0)
+ /* Paranoia in case someone changes type of segment length.
+ * Shift both values by 0x10 to make the comparison operate
+ * on more, than 8 bits (otherwise it's a warning, bug
+ * #564).
+ */
+ || ((sizeof(segh.length) > 1)
+ && (0x10 + segh.length > 0x10 + AS_SEGMENT_MAX))) {
+ if (head)
+ assegment_free_all(head);
+ return -1;
+ }
+
+ switch (segh.type) {
+ case AS_SEQUENCE:
+ case AS_SET:
+ case AS_CONFED_SEQUENCE:
+ case AS_CONFED_SET:
+ break;
+ default:
+ if (head)
+ assegment_free_all(head);
+ return -1;
+ }
+
+ /* now its safe to trust lengths */
+ seg = assegment_new(segh.type, segh.length);
+
+ if (head)
+ prev->next = seg;
+ else /* it's the first segment */
+ head = seg;
+
+ for (i = 0; i < segh.length; i++)
+ seg->as[i] =
+ (use32bit) ? stream_getl(s) : stream_getw(s);
+
+ bytes += seg_size;
+
+ if (BGP_DEBUG(as4, AS4_SEGMENT))
+ zlog_debug(
+ "[AS4SEG] Parse aspath segment: Bytes now: %lu",
+ (unsigned long)bytes);
+
+ prev = seg;
+ }
+
+ *result = assegment_normalise(head);
+ return 0;
+}
+
+/* AS path parse function. pnt is a pointer to byte stream and length
+ is length of byte stream. If there is same AS path in the the AS
+ path hash then return it else make new AS path structure.
+
+ On error NULL is returned.
+ */
+struct aspath *aspath_parse(struct stream *s, size_t length, int use32bit)
+{
+ struct aspath as;
+ struct aspath *find;
+
+ /* If length is odd it's malformed AS path. */
+ /* Nit-picking: if (use32bit == 0) it is malformed if odd,
+ * otherwise its malformed when length is larger than 2 and (length-2)
+ * is not dividable by 4.
+ * But... this time we're lazy
+ */
+ if (length % AS16_VALUE_SIZE)
+ return NULL;
+
+ memset(&as, 0, sizeof(as));
+ if (assegments_parse(s, length, &as.segments, use32bit) < 0)
+ return NULL;
+
+ /* If already same aspath exist then return it. */
+ find = hash_get(ashash, &as, aspath_hash_alloc);
+
+ /* if the aspath was already hashed free temporary memory. */
+ if (find->refcnt) {
+ assegment_free_all(as.segments);
+ /* aspath_key_make() always updates the string */
+ XFREE(MTYPE_AS_STR, as.str);
+ if (as.json) {
+ json_object_free(as.json);
+ as.json = NULL;
+ }
+ }
+
+ find->refcnt++;
+
+ return find;
+}
+
+static void assegment_data_put(struct stream *s, as_t *as, int num,
+ int use32bit)
+{
+ int i;
+ assert(num <= AS_SEGMENT_MAX);
+
+ for (i = 0; i < num; i++)
+ if (use32bit)
+ stream_putl(s, as[i]);
+ else {
+ if (as[i] <= BGP_AS_MAX)
+ stream_putw(s, as[i]);
+ else
+ stream_putw(s, BGP_AS_TRANS);
+ }
+}
+
+static size_t assegment_header_put(struct stream *s, uint8_t type, int length)
+{
+ size_t lenp;
+ assert(length <= AS_SEGMENT_MAX);
+ stream_putc(s, type);
+ lenp = stream_get_endp(s);
+ stream_putc(s, length);
+ return lenp;
+}
+
+/* write aspath data to stream */
+size_t aspath_put(struct stream *s, struct aspath *as, int use32bit)
+{
+ struct assegment *seg = as->segments;
+ size_t bytes = 0;
+
+ if (!seg || seg->length == 0)
+ return 0;
+
+ /*
+ * Hey, what do we do when we have > STREAM_WRITABLE(s) here?
+ * At the moment, we would write out a partial aspath, and our
+ * peer
+ * will complain and drop the session :-/
+ *
+ * The general assumption here is that many things tested will
+ * never happen. And, in real live, up to now, they have not.
+ */
+ while (seg && (ASSEGMENT_LEN(seg, use32bit) <= STREAM_WRITEABLE(s))) {
+ struct assegment *next = seg->next;
+ int written = 0;
+ int asns_packed = 0;
+ size_t lenp;
+
+ /* Overlength segments have to be split up */
+ while ((seg->length - written) > AS_SEGMENT_MAX) {
+ assegment_header_put(s, seg->type, AS_SEGMENT_MAX);
+ assegment_data_put(s, (seg->as + written),
+ AS_SEGMENT_MAX, use32bit);
+ written += AS_SEGMENT_MAX;
+ bytes += ASSEGMENT_SIZE(AS_SEGMENT_MAX, use32bit);
+ }
+
+ /* write the final segment, probably is also the first
+ */
+ lenp = assegment_header_put(s, seg->type,
+ seg->length - written);
+ assegment_data_put(s, (seg->as + written),
+ seg->length - written, use32bit);
+
+ /* Sequence-type segments can be 'packed' together
+ * Case of a segment which was overlength and split up
+ * will be missed here, but that doesn't matter.
+ */
+ while (next && ASSEGMENTS_PACKABLE(seg, next)) {
+ /* NB: We should never normally get here given
+ * we
+ * normalise aspath data when parse them.
+ * However, better
+ * safe than sorry. We potentially could call
+ * assegment_normalise here instead, but it's
+ * cheaper and
+ * easier to do it on the fly here rather than
+ * go through
+ * the segment list twice every time we write
+ * out
+ * aspath's.
+ */
+
+ /* Next segment's data can fit in this one */
+ assegment_data_put(s, next->as, next->length, use32bit);
+
+ /* update the length of the segment header */
+ stream_putc_at(s, lenp,
+ seg->length - written + next->length);
+ asns_packed += next->length;
+
+ next = next->next;
+ }
+
+ bytes += ASSEGMENT_SIZE(seg->length - written + asns_packed,
+ use32bit);
+ seg = next;
+ }
+ return bytes;
+}
+
+/* This is for SNMP BGP4PATHATTRASPATHSEGMENT
+ * We have no way to manage the storage, so we use a static stream
+ * wrapper around aspath_put.
+ */
+uint8_t *aspath_snmp_pathseg(struct aspath *as, size_t *varlen)
+{
+#define SNMP_PATHSEG_MAX 1024
+
+ if (!snmp_stream)
+ snmp_stream = stream_new(SNMP_PATHSEG_MAX);
+ else
+ stream_reset(snmp_stream);
+
+ if (!as) {
+ *varlen = 0;
+ return NULL;
+ }
+ aspath_put(snmp_stream, as, 0); /* use 16 bit for now here */
+
+ *varlen = stream_get_endp(snmp_stream);
+ return stream_pnt(snmp_stream);
+}
+
+static struct assegment *aspath_aggregate_as_set_add(struct aspath *aspath,
+ struct assegment *asset,
+ as_t as)
+{
+ int i;
+
+ /* If this is first AS set member, create new as-set segment. */
+ if (asset == NULL) {
+ asset = assegment_new(AS_SET, 1);
+ if (!aspath->segments)
+ aspath->segments = asset;
+ else {
+ struct assegment *seg = aspath->segments;
+ while (seg->next)
+ seg = seg->next;
+ seg->next = asset;
+ }
+ asset->type = AS_SET;
+ asset->length = 1;
+ asset->as[0] = as;
+ } else {
+ /* Check this AS value already exists or not. */
+ for (i = 0; i < asset->length; i++)
+ if (asset->as[i] == as)
+ return asset;
+
+ asset->length++;
+ asset->as = XREALLOC(MTYPE_AS_SEG_DATA, asset->as,
+ asset->length * AS_VALUE_SIZE);
+ asset->as[asset->length - 1] = as;
+ }
+
+
+ return asset;
+}
+
+/* Modify as1 using as2 for aggregation. */
+struct aspath *aspath_aggregate(struct aspath *as1, struct aspath *as2)
+{
+ int i;
+ int minlen = 0;
+ int match = 0;
+ int from;
+ struct assegment *seg1 = as1->segments;
+ struct assegment *seg2 = as2->segments;
+ struct aspath *aspath = NULL;
+ struct assegment *asset = NULL;
+ struct assegment *prevseg = NULL;
+
+ /* First of all check common leading sequence. */
+ while (seg1 && seg2) {
+ /* Check segment type. */
+ if (seg1->type != seg2->type)
+ break;
+
+ /* Minimum segment length. */
+ minlen = MIN(seg1->length, seg2->length);
+
+ for (match = 0; match < minlen; match++)
+ if (seg1->as[match] != seg2->as[match])
+ break;
+
+ if (match) {
+ struct assegment *seg = assegment_new(seg1->type, 0);
+
+ seg = assegment_append_asns(seg, seg1->as, match);
+
+ if (!aspath) {
+ aspath = aspath_new();
+ aspath->segments = seg;
+ } else
+ prevseg->next = seg;
+
+ prevseg = seg;
+ }
+
+ if (match != minlen || match != seg1->length
+ || seg1->length != seg2->length)
+ break;
+ /* We are moving on to the next segment to reset match */
+ else
+ match = 0;
+
+ seg1 = seg1->next;
+ seg2 = seg2->next;
+ }
+
+ if (!aspath)
+ aspath = aspath_new();
+
+ /* Make as-set using rest of all information. */
+ from = match;
+ while (seg1) {
+ for (i = from; i < seg1->length; i++)
+ asset = aspath_aggregate_as_set_add(aspath, asset,
+ seg1->as[i]);
+
+ from = 0;
+ seg1 = seg1->next;
+ }
+
+ from = match;
+ while (seg2) {
+ for (i = from; i < seg2->length; i++)
+ asset = aspath_aggregate_as_set_add(aspath, asset,
+ seg2->as[i]);
+
+ from = 0;
+ seg2 = seg2->next;
+ }
+
+ assegment_normalise(aspath->segments);
+ aspath_str_update(aspath, false);
+ return aspath;
+}
+
+/* When a BGP router receives an UPDATE with an MP_REACH_NLRI
+ attribute, check the leftmost AS number in the AS_PATH attribute is
+ or not the peer's AS number. */
+bool aspath_firstas_check(struct aspath *aspath, as_t asno)
+{
+ if ((aspath == NULL) || (aspath->segments == NULL))
+ return false;
+
+ if (aspath->segments && (aspath->segments->type == AS_SEQUENCE)
+ && (aspath->segments->as[0] == asno))
+ return true;
+
+ return false;
+}
+
+unsigned int aspath_get_first_as(struct aspath *aspath)
+{
+ if (aspath == NULL || aspath->segments == NULL)
+ return 0;
+
+ return aspath->segments->as[0];
+}
+
+unsigned int aspath_get_last_as(struct aspath *aspath)
+{
+ int i;
+ unsigned int last_as = 0;
+ const struct assegment *seg;
+
+ if (aspath == NULL || aspath->segments == NULL)
+ return last_as;
+
+ seg = aspath->segments;
+
+ while (seg) {
+ if (seg->type == AS_SEQUENCE || seg->type == AS_CONFED_SEQUENCE)
+ for (i = 0; i < seg->length; i++)
+ last_as = seg->as[i];
+ seg = seg->next;
+ }
+
+ return last_as;
+}
+
+/* AS path loop check. If aspath contains asno then return >= 1. */
+int aspath_loop_check(struct aspath *aspath, as_t asno)
+{
+ struct assegment *seg;
+ int count = 0;
+
+ if ((aspath == NULL) || (aspath->segments == NULL))
+ return 0;
+
+ seg = aspath->segments;
+
+ while (seg) {
+ int i;
+
+ for (i = 0; i < seg->length; i++)
+ if (seg->as[i] == asno)
+ count++;
+
+ seg = seg->next;
+ }
+ return count;
+}
+
+/* When all of AS path is private AS return 1. */
+bool aspath_private_as_check(struct aspath *aspath)
+{
+ struct assegment *seg;
+
+ if (!(aspath && aspath->segments))
+ return false;
+
+ seg = aspath->segments;
+
+ while (seg) {
+ int i;
+
+ for (i = 0; i < seg->length; i++) {
+ if (!BGP_AS_IS_PRIVATE(seg->as[i]))
+ return false;
+ }
+ seg = seg->next;
+ }
+ return true;
+}
+
+/* Replace all instances of the target ASN with our own ASN */
+struct aspath *aspath_replace_specific_asn(struct aspath *aspath,
+ as_t target_asn, as_t our_asn)
+{
+ struct aspath *new;
+ struct assegment *seg;
+
+ new = aspath_dup(aspath);
+ seg = new->segments;
+
+ while (seg) {
+ int i;
+
+ for (i = 0; i < seg->length; i++) {
+ if (seg->as[i] == target_asn)
+ seg->as[i] = our_asn;
+ }
+ seg = seg->next;
+ }
+
+ aspath_str_update(new, false);
+ return new;
+}
+
+/* Replace all ASNs with our own ASN */
+struct aspath *aspath_replace_all_asn(struct aspath *aspath, as_t our_asn)
+{
+ struct aspath *new;
+ struct assegment *seg;
+
+ new = aspath_dup(aspath);
+ seg = new->segments;
+
+ while (seg) {
+ int i;
+
+ for (i = 0; i < seg->length; i++)
+ seg->as[i] = our_asn;
+
+ seg = seg->next;
+ }
+
+ aspath_str_update(new, false);
+ return new;
+}
+
+/* Replace all private ASNs with our own ASN */
+struct aspath *aspath_replace_private_asns(struct aspath *aspath, as_t asn,
+ as_t peer_asn)
+{
+ struct aspath *new;
+ struct assegment *seg;
+
+ new = aspath_dup(aspath);
+ seg = new->segments;
+
+ while (seg) {
+ int i;
+
+ for (i = 0; i < seg->length; i++) {
+ /* Don't replace if public ASN or peer's ASN */
+ if (BGP_AS_IS_PRIVATE(seg->as[i])
+ && (seg->as[i] != peer_asn))
+ seg->as[i] = asn;
+ }
+ seg = seg->next;
+ }
+
+ aspath_str_update(new, false);
+ return new;
+}
+
+/* Remove all private ASNs */
+struct aspath *aspath_remove_private_asns(struct aspath *aspath, as_t peer_asn)
+{
+ struct aspath *new;
+ struct assegment *seg;
+ struct assegment *new_seg;
+ struct assegment *last_new_seg;
+ int i;
+ int j;
+ int public = 0;
+ int peer = 0;
+
+ new = XCALLOC(MTYPE_AS_PATH, sizeof(struct aspath));
+
+ new->json = NULL;
+ new_seg = NULL;
+ last_new_seg = NULL;
+ seg = aspath->segments;
+ while (seg) {
+ public = 0;
+ peer = 0;
+ for (i = 0; i < seg->length; i++) {
+ // ASN is public
+ if (!BGP_AS_IS_PRIVATE(seg->as[i]))
+ public++;
+ /* ASN matches peer's.
+ * Don't double-count if peer_asn is public.
+ */
+ else if (seg->as[i] == peer_asn)
+ peer++;
+ }
+
+ // The entire segment is public so copy it
+ if (public == seg->length)
+ new_seg = assegment_dup(seg);
+
+ // The segment is a mix of public and private ASNs. Copy as many
+ // spots as
+ // there are public ASNs then come back and fill in only the
+ // public ASNs.
+ else {
+ /* length needs to account for all retained ASNs
+ * (public or peer_asn), not just public
+ */
+ new_seg = assegment_new(seg->type, (public + peer));
+ j = 0;
+ for (i = 0; i < seg->length; i++) {
+ // keep ASN if public or matches peer's ASN
+ if (!BGP_AS_IS_PRIVATE(seg->as[i])
+ || (seg->as[i] == peer_asn)) {
+ new_seg->as[j] = seg->as[i];
+ j++;
+ }
+ }
+ }
+
+ // This is the first segment so set the aspath segments pointer
+ // to this one
+ if (!last_new_seg)
+ new->segments = new_seg;
+ else
+ last_new_seg->next = new_seg;
+
+ last_new_seg = new_seg;
+ seg = seg->next;
+ }
+
+ aspath_str_update(new, false);
+ return new;
+}
+
+/* AS path confed check. If aspath contains confed set or sequence then return
+ * 1. */
+bool aspath_confed_check(struct aspath *aspath)
+{
+ struct assegment *seg;
+
+ if (!(aspath && aspath->segments))
+ return false;
+
+ seg = aspath->segments;
+
+ while (seg) {
+ if (seg->type == AS_CONFED_SET
+ || seg->type == AS_CONFED_SEQUENCE)
+ return true;
+ seg = seg->next;
+ }
+ return false;
+}
+
+/* Leftmost AS path segment confed check. If leftmost AS segment is of type
+ AS_CONFED_SEQUENCE or AS_CONFED_SET then return 1. */
+bool aspath_left_confed_check(struct aspath *aspath)
+{
+
+ if (!(aspath && aspath->segments))
+ return false;
+
+ if ((aspath->segments->type == AS_CONFED_SEQUENCE)
+ || (aspath->segments->type == AS_CONFED_SET))
+ return true;
+
+ return false;
+}
+
+/* Merge as1 to as2. as2 should be uninterned aspath. */
+static struct aspath *aspath_merge(struct aspath *as1, struct aspath *as2)
+{
+ struct assegment *last, *new;
+
+ if (!as1 || !as2)
+ return NULL;
+
+ last = new = assegment_dup_all(as1->segments);
+
+ /* find the last valid segment */
+ while (last && last->next)
+ last = last->next;
+
+ if (last)
+ last->next = as2->segments;
+ as2->segments = new;
+ aspath_str_update(as2, false);
+ return as2;
+}
+
+/* Prepend as1 to as2. as2 should be uninterned aspath. */
+struct aspath *aspath_prepend(struct aspath *as1, struct aspath *as2)
+{
+ struct assegment *as1segtail;
+ struct assegment *as2segtail;
+ struct assegment *as2seghead;
+
+ if (!as1 || !as2)
+ return NULL;
+
+ /* If as2 is empty, only need to dupe as1's chain onto as2 */
+ if (as2->segments == NULL) {
+ as2->segments = assegment_dup_all(as1->segments);
+ aspath_str_update(as2, false);
+ return as2;
+ }
+
+ /* If as1 is empty AS, no prepending to do. */
+ if (as1->segments == NULL)
+ return as2;
+
+ /* find the tail as1's segment chain. */
+ as1segtail = as1->segments;
+ while (as1segtail && as1segtail->next)
+ as1segtail = as1segtail->next;
+
+ /* Delete any AS_CONFED_SEQUENCE segment from as2. */
+ if (as1segtail->type == AS_SEQUENCE
+ && as2->segments->type == AS_CONFED_SEQUENCE)
+ as2 = aspath_delete_confed_seq(as2);
+
+ if (!as2->segments) {
+ as2->segments = assegment_dup_all(as1->segments);
+ aspath_str_update(as2, false);
+ return as2;
+ }
+
+ /* Compare last segment type of as1 and first segment type of as2. */
+ if (as1segtail->type != as2->segments->type)
+ return aspath_merge(as1, as2);
+
+ if (as1segtail->type == AS_SEQUENCE) {
+ /* We have two chains of segments, as1->segments and seg2,
+ * and we have to attach them together, merging the attaching
+ * segments together into one.
+ *
+ * 1. dupe as1->segments onto head of as2
+ * 2. merge seg2's asns onto last segment of this new chain
+ * 3. attach chain after seg2
+ */
+
+ /* save as2 head */
+ as2seghead = as2->segments;
+
+ /* dupe as1 onto as2's head */
+ as2segtail = as2->segments = assegment_dup_all(as1->segments);
+
+ /* refind the tail of as2 */
+ while (as2segtail && as2segtail->next)
+ as2segtail = as2segtail->next;
+
+ /* merge the old head, seg2, into tail, seg1 */
+ assegment_append_asns(as2segtail, as2seghead->as,
+ as2seghead->length);
+
+ /*
+ * bypass the merged seg2, and attach any chain after it
+ * to chain descending from as2's head
+ */
+ if (as2segtail)
+ as2segtail->next = as2seghead->next;
+
+ /* as2->segments is now referenceless and useless */
+ assegment_free(as2seghead);
+
+ /* we've now prepended as1's segment chain to as2, merging
+ * the inbetween AS_SEQUENCE of seg2 in the process
+ */
+ aspath_str_update(as2, false);
+ return as2;
+ } else {
+ /* AS_SET merge code is needed at here. */
+ return aspath_merge(as1, as2);
+ }
+ /* XXX: Ermmm, what if as1 has multiple segments?? */
+
+ /* Not reached */
+}
+
+/* Iterate over AS_PATH segments and wipe all occurrences of the
+ * listed AS numbers. Hence some segments may lose some or even
+ * all data on the way, the operation is implemented as a smarter
+ * version of aspath_dup(), which allocates memory to hold the new
+ * data, not the original. The new AS path is returned.
+ */
+struct aspath *aspath_filter_exclude(struct aspath *source,
+ struct aspath *exclude_list)
+{
+ struct assegment *srcseg, *exclseg, *lastseg;
+ struct aspath *newpath;
+
+ newpath = aspath_new();
+ lastseg = NULL;
+
+ for (srcseg = source->segments; srcseg; srcseg = srcseg->next) {
+ unsigned i, y, newlen = 0, done = 0, skip_as;
+ struct assegment *newseg;
+
+ /* Find out, how much ASns are we going to pick from this
+ * segment.
+ * We can't perform filtering right inline, because the size of
+ * the new segment isn't known at the moment yet.
+ */
+ for (i = 0; i < srcseg->length; i++) {
+ skip_as = 0;
+ for (exclseg = exclude_list->segments;
+ exclseg && !skip_as; exclseg = exclseg->next)
+ for (y = 0; y < exclseg->length; y++)
+ if (srcseg->as[i] == exclseg->as[y]) {
+ skip_as = 1;
+ // There's no sense in testing
+ // the rest of exclusion list,
+ // bail out.
+ break;
+ }
+ if (!skip_as)
+ newlen++;
+ }
+ /* newlen is now the number of ASns to copy */
+ if (!newlen)
+ continue;
+
+ /* Actual copying. Allocate memory and iterate once more,
+ * performing filtering. */
+ newseg = assegment_new(srcseg->type, newlen);
+ for (i = 0; i < srcseg->length; i++) {
+ skip_as = 0;
+ for (exclseg = exclude_list->segments;
+ exclseg && !skip_as; exclseg = exclseg->next)
+ for (y = 0; y < exclseg->length; y++)
+ if (srcseg->as[i] == exclseg->as[y]) {
+ skip_as = 1;
+ break;
+ }
+ if (skip_as)
+ continue;
+ newseg->as[done++] = srcseg->as[i];
+ }
+ /* At his point newlen must be equal to done, and both must be
+ * positive. Append
+ * the filtered segment to the gross result. */
+ if (!lastseg)
+ newpath->segments = newseg;
+ else
+ lastseg->next = newseg;
+ lastseg = newseg;
+ }
+ aspath_str_update(newpath, false);
+ /* We are happy returning even an empty AS_PATH, because the
+ * administrator
+ * might expect this very behaviour. There's a mean to avoid this, if
+ * necessary,
+ * by having a match rule against certain AS_PATH regexps in the
+ * route-map index.
+ */
+ aspath_free(source);
+ return newpath;
+}
+
+/* Add specified AS to the leftmost of aspath. */
+static struct aspath *aspath_add_asns(struct aspath *aspath, as_t asno,
+ uint8_t type, unsigned num)
+{
+ struct assegment *assegment = aspath->segments;
+ unsigned i;
+
+ if (assegment && assegment->type == type) {
+ /* extend existing segment */
+ aspath->segments =
+ assegment_prepend_asns(aspath->segments, asno, num);
+ } else {
+ /* prepend with new segment */
+ struct assegment *newsegment = assegment_new(type, num);
+ for (i = 0; i < num; i++)
+ newsegment->as[i] = asno;
+
+ /* insert potentially replacing empty segment */
+ if (assegment && assegment->length == 0) {
+ newsegment->next = assegment->next;
+ assegment_free(assegment);
+ } else
+ newsegment->next = assegment;
+ aspath->segments = newsegment;
+ }
+
+ aspath_str_update(aspath, false);
+ return aspath;
+}
+
+/* Add specified AS to the leftmost of aspath num times. */
+struct aspath *aspath_add_seq_n(struct aspath *aspath, as_t asno, unsigned num)
+{
+ return aspath_add_asns(aspath, asno, AS_SEQUENCE, num);
+}
+
+/* Add specified AS to the leftmost of aspath. */
+struct aspath *aspath_add_seq(struct aspath *aspath, as_t asno)
+{
+ return aspath_add_asns(aspath, asno, AS_SEQUENCE, 1);
+}
+
+/* Compare leftmost AS value for MED check. If as1's leftmost AS and
+ as2's leftmost AS is same return 1. */
+bool aspath_cmp_left(const struct aspath *aspath1, const struct aspath *aspath2)
+{
+ const struct assegment *seg1;
+ const struct assegment *seg2;
+
+ if (!(aspath1 && aspath2))
+ return false;
+
+ seg1 = aspath1->segments;
+ seg2 = aspath2->segments;
+
+ /* If both paths are originated in this AS then we do want to compare
+ * MED */
+ if (!seg1 && !seg2)
+ return true;
+
+ /* find first non-confed segments for each */
+ while (seg1 && ((seg1->type == AS_CONFED_SEQUENCE)
+ || (seg1->type == AS_CONFED_SET)))
+ seg1 = seg1->next;
+
+ while (seg2 && ((seg2->type == AS_CONFED_SEQUENCE)
+ || (seg2->type == AS_CONFED_SET)))
+ seg2 = seg2->next;
+
+ /* Check as1's */
+ if (!(seg1 && seg2 && (seg1->type == AS_SEQUENCE)
+ && (seg2->type == AS_SEQUENCE)))
+ return false;
+
+ if (seg1->as[0] == seg2->as[0])
+ return true;
+
+ return false;
+}
+
+/* Truncate an aspath after a number of hops, and put the hops remaining
+ * at the front of another aspath. Needed for AS4 compat.
+ *
+ * Returned aspath is a /new/ aspath, which should either by free'd or
+ * interned by the caller, as desired.
+ */
+struct aspath *aspath_reconcile_as4(struct aspath *aspath,
+ struct aspath *as4path)
+{
+ struct assegment *seg, *newseg, *prevseg = NULL;
+ struct aspath *newpath = NULL, *mergedpath;
+ int hops, cpasns = 0;
+
+ if (!aspath || !as4path)
+ return NULL;
+
+ seg = aspath->segments;
+
+ /* CONFEDs should get reconciled too.. */
+ hops = (aspath_count_hops(aspath) + aspath_count_confeds(aspath))
+ - aspath_count_hops(as4path);
+
+ if (hops < 0) {
+ if (BGP_DEBUG(as4, AS4))
+ flog_warn(
+ EC_BGP_ASPATH_FEWER_HOPS,
+ "[AS4] Fewer hops in AS_PATH than NEW_AS_PATH");
+ /* Something's gone wrong. The RFC says we should now ignore
+ * AS4_PATH,
+ * which is daft behaviour - it contains vital loop-detection
+ * information which must have been removed from AS_PATH.
+ */
+ hops = aspath_count_hops(aspath);
+ }
+
+ if (!hops) {
+ newpath = aspath_dup(as4path);
+ aspath_str_update(newpath, false);
+ return newpath;
+ }
+
+ if (BGP_DEBUG(as4, AS4))
+ zlog_debug(
+ "[AS4] got AS_PATH %s and AS4_PATH %s synthesizing now",
+ aspath->str, as4path->str);
+
+ while (seg && hops > 0) {
+ switch (seg->type) {
+ case AS_SET:
+ case AS_CONFED_SET:
+ hops--;
+ cpasns = seg->length;
+ break;
+ case AS_CONFED_SEQUENCE:
+ /* Should never split a confed-sequence, if hop-count
+ * suggests we must then something's gone wrong
+ * somewhere.
+ *
+ * Most important goal is to preserve AS_PATHs prime
+ * function
+ * as loop-detector, so we fudge the numbers so that the
+ * entire
+ * confed-sequence is merged in.
+ */
+ if (hops < seg->length) {
+ if (BGP_DEBUG(as4, AS4))
+ zlog_debug(
+ "[AS4] AS4PATHmangle: AS_CONFED_SEQUENCE falls across 2/4 ASN boundary somewhere, broken..");
+ hops = seg->length;
+ }
+ /* fallthru */
+ case AS_SEQUENCE:
+ cpasns = MIN(seg->length, hops);
+ hops -= seg->length;
+ }
+
+ assert(cpasns <= seg->length);
+
+ newseg = assegment_new(seg->type, 0);
+ newseg = assegment_append_asns(newseg, seg->as, cpasns);
+
+ if (!newpath) {
+ newpath = aspath_new();
+ newpath->segments = newseg;
+ } else
+ prevseg->next = newseg;
+
+ prevseg = newseg;
+ seg = seg->next;
+ }
+
+ /* We may be able to join some segments here, and we must
+ * do this because... we want normalised aspaths in out hash
+ * and we do not want to stumble in aspath_put.
+ */
+ mergedpath = aspath_merge(newpath, aspath_dup(as4path));
+ aspath_free(newpath);
+ mergedpath->segments = assegment_normalise(mergedpath->segments);
+ aspath_str_update(mergedpath, false);
+
+ if (BGP_DEBUG(as4, AS4))
+ zlog_debug("[AS4] result of synthesizing is %s",
+ mergedpath->str);
+
+ return mergedpath;
+}
+
+/* Compare leftmost AS value for MED check. If as1's leftmost AS and
+ as2's leftmost AS is same return 1. (confederation as-path
+ only). */
+bool aspath_cmp_left_confed(const struct aspath *aspath1,
+ const struct aspath *aspath2)
+{
+ if (!(aspath1 && aspath2))
+ return false;
+
+ if (!(aspath1->segments && aspath2->segments))
+ return false;
+
+ if ((aspath1->segments->type != AS_CONFED_SEQUENCE)
+ || (aspath2->segments->type != AS_CONFED_SEQUENCE))
+ return false;
+
+ if (aspath1->segments->as[0] == aspath2->segments->as[0])
+ return true;
+
+ return false;
+}
+
+/* Delete all AS_CONFED_SEQUENCE/SET segments from aspath.
+ * RFC 5065 section 4.1.c.1
+ *
+ * 1) if any path segments of the AS_PATH are of the type
+ * AS_CONFED_SEQUENCE or AS_CONFED_SET, those segments MUST be
+ * removed from the AS_PATH attribute, leaving the sanitized
+ * AS_PATH attribute to be operated on by steps 2, 3 or 4.
+ */
+struct aspath *aspath_delete_confed_seq(struct aspath *aspath)
+{
+ struct assegment *seg, *prev, *next;
+ char removed_confed_segment;
+
+ if (!(aspath && aspath->segments))
+ return aspath;
+
+ seg = aspath->segments;
+ removed_confed_segment = 0;
+ next = NULL;
+ prev = NULL;
+
+ while (seg) {
+ next = seg->next;
+
+ if (seg->type == AS_CONFED_SEQUENCE
+ || seg->type == AS_CONFED_SET) {
+ /* This is the first segment in the aspath */
+ if (aspath->segments == seg)
+ aspath->segments = seg->next;
+ else
+ prev->next = seg->next;
+
+ assegment_free(seg);
+ removed_confed_segment = 1;
+ } else
+ prev = seg;
+
+ seg = next;
+ }
+
+ if (removed_confed_segment)
+ aspath_str_update(aspath, false);
+
+ return aspath;
+}
+
+/* Add new AS number to the leftmost part of the aspath as
+ AS_CONFED_SEQUENCE. */
+struct aspath *aspath_add_confed_seq(struct aspath *aspath, as_t asno)
+{
+ return aspath_add_asns(aspath, asno, AS_CONFED_SEQUENCE, 1);
+}
+
+/* Add new as value to as path structure. */
+static void aspath_as_add(struct aspath *as, as_t asno)
+{
+ struct assegment *seg = as->segments;
+
+ if (!seg)
+ return;
+
+ /* Last segment search procedure. */
+ while (seg->next)
+ seg = seg->next;
+
+ assegment_append_asns(seg, &asno, 1);
+}
+
+/* Add new as segment to the as path. */
+static void aspath_segment_add(struct aspath *as, int type)
+{
+ struct assegment *seg = as->segments;
+ struct assegment *new = assegment_new(type, 0);
+
+ if (seg) {
+ while (seg->next)
+ seg = seg->next;
+ seg->next = new;
+ } else
+ as->segments = new;
+}
+
+struct aspath *aspath_empty(void)
+{
+ return aspath_parse(NULL, 0, 1); /* 32Bit ;-) */
+}
+
+struct aspath *aspath_empty_get(void)
+{
+ struct aspath *aspath;
+
+ aspath = aspath_new();
+ aspath_make_str_count(aspath, false);
+ return aspath;
+}
+
+unsigned long aspath_count(void)
+{
+ return ashash->count;
+}
+
+/*
+ Theoretically, one as path can have:
+
+ One BGP packet size should be less than 4096.
+ One BGP attribute size should be less than 4096 - BGP header size.
+ One BGP aspath size should be less than 4096 - BGP header size -
+ BGP mandantry attribute size.
+*/
+
+/* AS path string lexical token enum. */
+enum as_token {
+ as_token_asval,
+ as_token_set_start,
+ as_token_set_end,
+ as_token_confed_seq_start,
+ as_token_confed_seq_end,
+ as_token_confed_set_start,
+ as_token_confed_set_end,
+ as_token_unknown
+};
+
+/* Return next token and point for string parse. */
+static const char *aspath_gettoken(const char *buf, enum as_token *token,
+ unsigned long *asno)
+{
+ const char *p = buf;
+
+ /* Skip separators (space for sequences, ',' for sets). */
+ while (isspace((unsigned char)*p) || *p == ',')
+ p++;
+
+ /* Check the end of the string and type specify characters
+ (e.g. {}()). */
+ switch (*p) {
+ case '\0':
+ return NULL;
+ case '{':
+ *token = as_token_set_start;
+ p++;
+ return p;
+ case '}':
+ *token = as_token_set_end;
+ p++;
+ return p;
+ case '(':
+ *token = as_token_confed_seq_start;
+ p++;
+ return p;
+ case ')':
+ *token = as_token_confed_seq_end;
+ p++;
+ return p;
+ case '[':
+ *token = as_token_confed_set_start;
+ p++;
+ return p;
+ case ']':
+ *token = as_token_confed_set_end;
+ p++;
+ return p;
+ }
+
+ /* Check actual AS value. */
+ if (isdigit((unsigned char)*p)) {
+ as_t asval;
+
+ *token = as_token_asval;
+ asval = (*p - '0');
+ p++;
+
+ while (isdigit((unsigned char)*p)) {
+ asval *= 10;
+ asval += (*p - '0');
+ p++;
+ }
+ *asno = asval;
+ return p;
+ }
+
+ /* There is no match then return unknown token. */
+ *token = as_token_unknown;
+ p++;
+ return p;
+}
+
+struct aspath *aspath_str2aspath(const char *str)
+{
+ enum as_token token = as_token_unknown;
+ unsigned short as_type;
+ unsigned long asno = 0;
+ struct aspath *aspath;
+ int needtype;
+
+ aspath = aspath_new();
+
+ /* We start default type as AS_SEQUENCE. */
+ as_type = AS_SEQUENCE;
+ needtype = 1;
+
+ while ((str = aspath_gettoken(str, &token, &asno)) != NULL) {
+ switch (token) {
+ case as_token_asval:
+ if (needtype) {
+ aspath_segment_add(aspath, as_type);
+ needtype = 0;
+ }
+ aspath_as_add(aspath, asno);
+ break;
+ case as_token_set_start:
+ as_type = AS_SET;
+ aspath_segment_add(aspath, as_type);
+ needtype = 0;
+ break;
+ case as_token_set_end:
+ as_type = AS_SEQUENCE;
+ needtype = 1;
+ break;
+ case as_token_confed_seq_start:
+ as_type = AS_CONFED_SEQUENCE;
+ aspath_segment_add(aspath, as_type);
+ needtype = 0;
+ break;
+ case as_token_confed_seq_end:
+ as_type = AS_SEQUENCE;
+ needtype = 1;
+ break;
+ case as_token_confed_set_start:
+ as_type = AS_CONFED_SET;
+ aspath_segment_add(aspath, as_type);
+ needtype = 0;
+ break;
+ case as_token_confed_set_end:
+ as_type = AS_SEQUENCE;
+ needtype = 1;
+ break;
+ case as_token_unknown:
+ default:
+ aspath_free(aspath);
+ return NULL;
+ }
+ }
+
+ aspath_make_str_count(aspath, false);
+
+ return aspath;
+}
+
+/* Make hash value by raw aspath data. */
+unsigned int aspath_key_make(const void *p)
+{
+ const struct aspath *aspath = p;
+ unsigned int key = 0;
+
+ if (!aspath->str)
+ aspath_str_update((struct aspath *)aspath, false);
+
+ key = jhash(aspath->str, aspath->str_len, 2334325);
+
+ return key;
+}
+
+/* If two aspath have same value then return 1 else return 0 */
+bool aspath_cmp(const void *arg1, const void *arg2)
+{
+ const struct assegment *seg1 = ((const struct aspath *)arg1)->segments;
+ const struct assegment *seg2 = ((const struct aspath *)arg2)->segments;
+
+ while (seg1 || seg2) {
+ int i;
+ if ((!seg1 && seg2) || (seg1 && !seg2))
+ return false;
+ if (seg1->type != seg2->type)
+ return false;
+ if (seg1->length != seg2->length)
+ return false;
+ for (i = 0; i < seg1->length; i++)
+ if (seg1->as[i] != seg2->as[i])
+ return false;
+ seg1 = seg1->next;
+ seg2 = seg2->next;
+ }
+ return true;
+}
+
+/* AS path hash initialize. */
+void aspath_init(void)
+{
+ ashash = hash_create_size(32768, aspath_key_make, aspath_cmp,
+ "BGP AS Path");
+}
+
+void aspath_finish(void)
+{
+ hash_clean(ashash, (void (*)(void *))aspath_free);
+ hash_free(ashash);
+ ashash = NULL;
+
+ if (snmp_stream)
+ stream_free(snmp_stream);
+}
+
+/* return and as path value */
+const char *aspath_print(struct aspath *as)
+{
+ return (as ? as->str : NULL);
+}
+
+/* Printing functions */
+/* Feed the AS_PATH to the vty; the suffix string follows it only in case
+ * AS_PATH wasn't empty.
+ */
+void aspath_print_vty(struct vty *vty, const char *format, struct aspath *as,
+ const char *suffix)
+{
+ assert(format);
+ vty_out(vty, format, as->str);
+ if (as->str_len && strlen(suffix))
+ vty_out(vty, "%s", suffix);
+}
+
+static void aspath_show_all_iterator(struct hash_bucket *bucket,
+ struct vty *vty)
+{
+ struct aspath *as;
+
+ as = (struct aspath *)bucket->data;
+
+ vty_out(vty, "[%p:%u] (%ld) ", (void *)bucket, bucket->key, as->refcnt);
+ vty_out(vty, "%s\n", as->str);
+}
+
+/* Print all aspath and hash information. This function is used from
+ `show [ip] bgp paths' command. */
+void aspath_print_all_vty(struct vty *vty)
+{
+ hash_iterate(ashash, (void (*)(struct hash_bucket *,
+ void *))aspath_show_all_iterator,
+ vty);
+}
+
+static struct aspath *bgp_aggr_aspath_lookup(struct bgp_aggregate *aggregate,
+ struct aspath *aspath)
+{
+ return hash_lookup(aggregate->aspath_hash, aspath);
+}
+
+static void *bgp_aggr_aspath_hash_alloc(void *p)
+{
+ struct aspath *ref = (struct aspath *)p;
+ struct aspath *aspath = NULL;
+
+ aspath = aspath_dup(ref);
+ return aspath;
+}
+
+static void bgp_aggr_aspath_prepare(struct hash_bucket *hb, void *arg)
+{
+ struct aspath *hb_aspath = hb->data;
+ struct aspath **aggr_aspath = arg;
+
+ if (*aggr_aspath)
+ *aggr_aspath = aspath_aggregate(*aggr_aspath, hb_aspath);
+ else
+ *aggr_aspath = aspath_dup(hb_aspath);
+}
+
+void bgp_aggr_aspath_remove(void *arg)
+{
+ struct aspath *aspath = arg;
+
+ aspath_free(aspath);
+}
+
+void bgp_compute_aggregate_aspath(struct bgp_aggregate *aggregate,
+ struct aspath *aspath)
+{
+ bgp_compute_aggregate_aspath_hash(aggregate, aspath);
+
+ bgp_compute_aggregate_aspath_val(aggregate);
+
+}
+
+void bgp_compute_aggregate_aspath_hash(struct bgp_aggregate *aggregate,
+ struct aspath *aspath)
+{
+ struct aspath *aggr_aspath = NULL;
+
+ if ((aggregate == NULL) || (aspath == NULL))
+ return;
+
+ /* Create hash if not already created.
+ */
+ if (aggregate->aspath_hash == NULL)
+ aggregate->aspath_hash = hash_create(
+ aspath_key_make, aspath_cmp,
+ "BGP Aggregator as-path hash");
+
+ aggr_aspath = bgp_aggr_aspath_lookup(aggregate, aspath);
+ if (aggr_aspath == NULL) {
+ /* Insert as-path into hash.
+ */
+ aggr_aspath = hash_get(aggregate->aspath_hash, aspath,
+ bgp_aggr_aspath_hash_alloc);
+ }
+
+ /* Increment reference counter.
+ */
+ aggr_aspath->refcnt++;
+}
+
+void bgp_compute_aggregate_aspath_val(struct bgp_aggregate *aggregate)
+{
+ if (aggregate == NULL)
+ return;
+ /* Re-compute aggregate's as-path.
+ */
+ if (aggregate->aspath) {
+ aspath_free(aggregate->aspath);
+ aggregate->aspath = NULL;
+ }
+ if (aggregate->aspath_hash
+ && aggregate->aspath_hash->count) {
+ hash_iterate(aggregate->aspath_hash,
+ bgp_aggr_aspath_prepare,
+ &aggregate->aspath);
+ }
+}
+
+void bgp_remove_aspath_from_aggregate(struct bgp_aggregate *aggregate,
+ struct aspath *aspath)
+{
+ struct aspath *aggr_aspath = NULL;
+ struct aspath *ret_aspath = NULL;
+
+ if ((!aggregate)
+ || (!aggregate->aspath_hash)
+ || (!aspath))
+ return;
+
+ /* Look-up the aspath in the hash.
+ */
+ aggr_aspath = bgp_aggr_aspath_lookup(aggregate, aspath);
+ if (aggr_aspath) {
+ aggr_aspath->refcnt--;
+
+ if (aggr_aspath->refcnt == 0) {
+ ret_aspath = hash_release(aggregate->aspath_hash,
+ aggr_aspath);
+ aspath_free(ret_aspath);
+ ret_aspath = NULL;
+
+ /* Remove aggregate's old as-path.
+ */
+ aspath_free(aggregate->aspath);
+ aggregate->aspath = NULL;
+
+ bgp_compute_aggregate_aspath_val(aggregate);
+ }
+ }
+}
+
+void bgp_remove_aspath_from_aggregate_hash(struct bgp_aggregate *aggregate,
+ struct aspath *aspath)
+{
+ struct aspath *aggr_aspath = NULL;
+ struct aspath *ret_aspath = NULL;
+
+ if ((!aggregate)
+ || (!aggregate->aspath_hash)
+ || (!aspath))
+ return;
+
+ /* Look-up the aspath in the hash.
+ */
+ aggr_aspath = bgp_aggr_aspath_lookup(aggregate, aspath);
+ if (aggr_aspath) {
+ aggr_aspath->refcnt--;
+
+ if (aggr_aspath->refcnt == 0) {
+ ret_aspath = hash_release(aggregate->aspath_hash,
+ aggr_aspath);
+ aspath_free(ret_aspath);
+ ret_aspath = NULL;
+ }
+ }
+}
+
diff --git a/bgpd/bgp_aspath.h b/bgpd/bgp_aspath.h
new file mode 100644
index 0000000..0b58e1a
--- /dev/null
+++ b/bgpd/bgp_aspath.h
@@ -0,0 +1,158 @@
+/* AS path related definitions.
+ * Copyright (C) 1997, 98, 99 Kunihiro Ishiguro
+ *
+ * This file is part of GNU Zebra.
+ *
+ * GNU Zebra is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License as published by the
+ * Free Software Foundation; either version 2, or (at your option) any
+ * later version.
+ *
+ * GNU Zebra is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program; see the file COPYING; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
+ */
+
+#ifndef _QUAGGA_BGP_ASPATH_H
+#define _QUAGGA_BGP_ASPATH_H
+
+#include "lib/json.h"
+#include "bgpd/bgp_route.h"
+
+/* AS path segment type. */
+#define AS_SET 1
+#define AS_SEQUENCE 2
+#define AS_CONFED_SEQUENCE 3
+#define AS_CONFED_SET 4
+
+/* Private AS range defined in RFC2270. */
+#define BGP_PRIVATE_AS_MIN 64512U
+#define BGP_PRIVATE_AS_MAX UINT16_MAX
+
+/* Private 4 byte AS range defined in RFC6996. */
+#define BGP_PRIVATE_AS4_MIN 4200000000U
+#define BGP_PRIVATE_AS4_MAX 4294967294U
+
+/* we leave BGP_AS_MAX as the 16bit AS MAX number. */
+#define BGP_AS_ZERO 0
+#define BGP_AS_MAX UINT16_MAX
+#define BGP_AS4_MAX 4294967295U
+/* Transition 16Bit AS as defined by IANA */
+#define BGP_AS_TRANS 23456U
+
+#define BGP_AS_IS_PRIVATE(ASN) \
+ (((ASN) >= BGP_PRIVATE_AS_MIN && (ASN) <= BGP_PRIVATE_AS_MAX) \
+ || ((ASN) >= BGP_PRIVATE_AS4_MIN && (ASN) <= BGP_PRIVATE_AS4_MAX))
+
+/* AS_PATH segment data in abstracted form, no limit is placed on length */
+struct assegment {
+ struct assegment *next;
+ as_t *as;
+ unsigned short length;
+ uint8_t type;
+};
+
+/* AS path may be include some AsSegments. */
+struct aspath {
+ /* Reference count to this aspath. */
+ unsigned long refcnt;
+
+ /* segment data */
+ struct assegment *segments;
+
+ /* AS path as a json object */
+ json_object *json;
+
+ /* String expression of AS path. This string is used by vty output
+ and AS path regular expression match. */
+ char *str;
+ unsigned short str_len;
+};
+
+#define ASPATH_STR_DEFAULT_LEN 32
+
+/* Prototypes. */
+extern void aspath_init(void);
+extern void aspath_finish(void);
+extern struct aspath *aspath_parse(struct stream *s, size_t length,
+ int use32bit);
+extern struct aspath *aspath_dup(struct aspath *aspath);
+extern struct aspath *aspath_aggregate(struct aspath *as1, struct aspath *as2);
+extern struct aspath *aspath_prepend(struct aspath *as1, struct aspath *as2);
+extern struct aspath *aspath_filter_exclude(struct aspath *source,
+ struct aspath *exclude_list);
+extern struct aspath *aspath_add_seq_n(struct aspath *aspath, as_t asno,
+ unsigned num);
+extern struct aspath *aspath_add_seq(struct aspath *aspath, as_t asno);
+extern struct aspath *aspath_add_confed_seq(struct aspath *aspath, as_t asno);
+extern bool aspath_cmp(const void *as1, const void *as2);
+extern bool aspath_cmp_left(const struct aspath *aspath1,
+ const struct aspath *aspath2);
+extern bool aspath_cmp_left_confed(const struct aspath *as1,
+ const struct aspath *as2);
+extern struct aspath *aspath_delete_confed_seq(struct aspath *aspath);
+extern struct aspath *aspath_empty(void);
+extern struct aspath *aspath_empty_get(void);
+extern struct aspath *aspath_str2aspath(const char *str);
+extern void aspath_str_update(struct aspath *as, bool make_json);
+extern void aspath_free(struct aspath *aspath);
+extern struct aspath *aspath_intern(struct aspath *aspath);
+extern void aspath_unintern(struct aspath **aspath);
+extern const char *aspath_print(struct aspath *aspath);
+extern void aspath_print_vty(struct vty *vty, const char *format,
+ struct aspath *aspath, const char *suffix);
+extern void aspath_print_all_vty(struct vty *vty);
+extern unsigned int aspath_key_make(const void *p);
+extern unsigned int aspath_get_first_as(struct aspath *aspath);
+extern unsigned int aspath_get_last_as(struct aspath *aspath);
+extern int aspath_loop_check(struct aspath *aspath, as_t asno);
+extern bool aspath_private_as_check(struct aspath *aspath);
+extern struct aspath *aspath_replace_specific_asn(struct aspath *aspath,
+ as_t target_asn,
+ as_t our_asn);
+extern struct aspath *aspath_replace_all_asn(struct aspath *aspath,
+ as_t our_asn);
+extern struct aspath *aspath_replace_private_asns(struct aspath *aspath,
+ as_t asn, as_t peer_asn);
+extern struct aspath *aspath_remove_private_asns(struct aspath *aspath,
+ as_t peer_asn);
+extern bool aspath_firstas_check(struct aspath *aspath, as_t asno);
+extern bool aspath_confed_check(struct aspath *aspath);
+extern bool aspath_left_confed_check(struct aspath *aspath);
+extern unsigned long aspath_count(void);
+extern unsigned int aspath_count_hops(const struct aspath *aspath);
+extern bool aspath_check_as_sets(struct aspath *aspath);
+extern bool aspath_check_as_zero(struct aspath *aspath);
+extern unsigned int aspath_count_confeds(struct aspath *aspath);
+extern unsigned int aspath_size(struct aspath *aspath);
+extern as_t aspath_highest(struct aspath *aspath);
+extern as_t aspath_leftmost(struct aspath *aspath);
+extern size_t aspath_put(struct stream *s, struct aspath *aspath, int use32bit);
+
+extern struct aspath *aspath_reconcile_as4(struct aspath *aspath,
+ struct aspath *as4path);
+extern bool aspath_has_as4(struct aspath *aspath);
+
+/* For SNMP BGP4PATHATTRASPATHSEGMENT, might be useful for debug */
+extern uint8_t *aspath_snmp_pathseg(struct aspath *aspath, size_t *varlen);
+
+extern void bgp_compute_aggregate_aspath(struct bgp_aggregate *aggregate,
+ struct aspath *aspath);
+
+extern void bgp_compute_aggregate_aspath_hash(struct bgp_aggregate *aggregate,
+ struct aspath *aspath);
+extern void bgp_compute_aggregate_aspath_val(struct bgp_aggregate *aggregate);
+extern void bgp_remove_aspath_from_aggregate(struct bgp_aggregate *aggregate,
+ struct aspath *aspath);
+extern void bgp_remove_aspath_from_aggregate_hash(
+ struct bgp_aggregate *aggregate,
+ struct aspath *aspath);
+
+extern void bgp_aggr_aspath_remove(void *arg);
+
+#endif /* _QUAGGA_BGP_ASPATH_H */
diff --git a/bgpd/bgp_attr.c b/bgpd/bgp_attr.c
new file mode 100644
index 0000000..250dca7
--- /dev/null
+++ b/bgpd/bgp_attr.c
@@ -0,0 +1,4713 @@
+/* BGP attributes management routines.
+ * Copyright (C) 1996, 97, 98, 1999 Kunihiro Ishiguro
+ *
+ * This file is part of GNU Zebra.
+ *
+ * GNU Zebra is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License as published by the
+ * Free Software Foundation; either version 2, or (at your option) any
+ * later version.
+ *
+ * GNU Zebra is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program; see the file COPYING; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
+ */
+
+#include <zebra.h>
+
+#include "linklist.h"
+#include "prefix.h"
+#include "memory.h"
+#include "vector.h"
+#include "stream.h"
+#include "log.h"
+#include "hash.h"
+#include "jhash.h"
+#include "queue.h"
+#include "table.h"
+#include "filter.h"
+#include "command.h"
+#include "srv6.h"
+
+#include "bgpd/bgpd.h"
+#include "bgpd/bgp_attr.h"
+#include "bgpd/bgp_route.h"
+#include "bgpd/bgp_aspath.h"
+#include "bgpd/bgp_community.h"
+#include "bgpd/bgp_debug.h"
+#include "bgpd/bgp_errors.h"
+#include "bgpd/bgp_label.h"
+#include "bgpd/bgp_packet.h"
+#include "bgpd/bgp_ecommunity.h"
+#include "bgpd/bgp_lcommunity.h"
+#include "bgpd/bgp_updgrp.h"
+#include "bgpd/bgp_encap_types.h"
+#ifdef ENABLE_BGP_VNC
+#include "bgpd/rfapi/bgp_rfapi_cfg.h"
+#include "bgp_encap_types.h"
+#include "bgp_vnc_types.h"
+#endif
+#include "bgp_evpn.h"
+#include "bgp_flowspec_private.h"
+#include "bgp_mac.h"
+
+/* Attribute strings for logging. */
+static const struct message attr_str[] = {
+ {BGP_ATTR_ORIGIN, "ORIGIN"},
+ {BGP_ATTR_AS_PATH, "AS_PATH"},
+ {BGP_ATTR_NEXT_HOP, "NEXT_HOP"},
+ {BGP_ATTR_MULTI_EXIT_DISC, "MULTI_EXIT_DISC"},
+ {BGP_ATTR_LOCAL_PREF, "LOCAL_PREF"},
+ {BGP_ATTR_ATOMIC_AGGREGATE, "ATOMIC_AGGREGATE"},
+ {BGP_ATTR_AGGREGATOR, "AGGREGATOR"},
+ {BGP_ATTR_COMMUNITIES, "COMMUNITY"},
+ {BGP_ATTR_ORIGINATOR_ID, "ORIGINATOR_ID"},
+ {BGP_ATTR_CLUSTER_LIST, "CLUSTER_LIST"},
+ {BGP_ATTR_MP_REACH_NLRI, "MP_REACH_NLRI"},
+ {BGP_ATTR_MP_UNREACH_NLRI, "MP_UNREACH_NLRI"},
+ {BGP_ATTR_EXT_COMMUNITIES, "EXT_COMMUNITIES"},
+ {BGP_ATTR_AS4_PATH, "AS4_PATH"},
+ {BGP_ATTR_AS4_AGGREGATOR, "AS4_AGGREGATOR"},
+ {BGP_ATTR_AS_PATHLIMIT, "AS_PATHLIMIT"},
+ {BGP_ATTR_PMSI_TUNNEL, "PMSI_TUNNEL_ATTRIBUTE"},
+ {BGP_ATTR_ENCAP, "ENCAP"},
+ {BGP_ATTR_OTC, "OTC"},
+#ifdef ENABLE_BGP_VNC_ATTR
+ {BGP_ATTR_VNC, "VNC"},
+#endif
+ {BGP_ATTR_LARGE_COMMUNITIES, "LARGE_COMMUNITY"},
+ {BGP_ATTR_PREFIX_SID, "PREFIX_SID"},
+ {BGP_ATTR_IPV6_EXT_COMMUNITIES, "IPV6_EXT_COMMUNITIES"},
+ {0}};
+
+static const struct message attr_flag_str[] = {
+ {BGP_ATTR_FLAG_OPTIONAL, "Optional"},
+ {BGP_ATTR_FLAG_TRANS, "Transitive"},
+ {BGP_ATTR_FLAG_PARTIAL, "Partial"},
+ /* bgp_attr_flags_diagnose() relies on this bit being last in
+ this list */
+ {BGP_ATTR_FLAG_EXTLEN, "Extended Length"},
+ {0}};
+
+static struct hash *cluster_hash;
+
+static void *cluster_hash_alloc(void *p)
+{
+ const struct cluster_list *val = (const struct cluster_list *)p;
+ struct cluster_list *cluster;
+
+ cluster = XMALLOC(MTYPE_CLUSTER, sizeof(struct cluster_list));
+ cluster->length = val->length;
+
+ if (cluster->length) {
+ cluster->list = XMALLOC(MTYPE_CLUSTER_VAL, val->length);
+ memcpy(cluster->list, val->list, val->length);
+ } else
+ cluster->list = NULL;
+
+ cluster->refcnt = 0;
+
+ return cluster;
+}
+
+/* Cluster list related functions. */
+static struct cluster_list *cluster_parse(struct in_addr *pnt, int length)
+{
+ struct cluster_list tmp = {};
+ struct cluster_list *cluster;
+
+ tmp.length = length;
+ tmp.list = length == 0 ? NULL : pnt;
+
+ cluster = hash_get(cluster_hash, &tmp, cluster_hash_alloc);
+ cluster->refcnt++;
+ return cluster;
+}
+
+bool cluster_loop_check(struct cluster_list *cluster, struct in_addr originator)
+{
+ int i;
+
+ for (i = 0; i < cluster->length / 4; i++)
+ if (cluster->list[i].s_addr == originator.s_addr)
+ return true;
+ return false;
+}
+
+static unsigned int cluster_hash_key_make(const void *p)
+{
+ const struct cluster_list *cluster = p;
+
+ return jhash(cluster->list, cluster->length, 0);
+}
+
+static bool cluster_hash_cmp(const void *p1, const void *p2)
+{
+ const struct cluster_list *cluster1 = p1;
+ const struct cluster_list *cluster2 = p2;
+
+ if (cluster1->list == cluster2->list)
+ return true;
+
+ if (!cluster1->list || !cluster2->list)
+ return false;
+
+ if (cluster1->length != cluster2->length)
+ return false;
+
+ return (memcmp(cluster1->list, cluster2->list, cluster1->length) == 0);
+}
+
+static void cluster_free(struct cluster_list *cluster)
+{
+ XFREE(MTYPE_CLUSTER_VAL, cluster->list);
+ XFREE(MTYPE_CLUSTER, cluster);
+}
+
+static struct cluster_list *cluster_intern(struct cluster_list *cluster)
+{
+ struct cluster_list *find;
+
+ find = hash_get(cluster_hash, cluster, cluster_hash_alloc);
+ find->refcnt++;
+
+ return find;
+}
+
+static void cluster_unintern(struct cluster_list **cluster)
+{
+ if ((*cluster)->refcnt)
+ (*cluster)->refcnt--;
+
+ if ((*cluster)->refcnt == 0) {
+ void *p = hash_release(cluster_hash, *cluster);
+ assert(p == *cluster);
+ cluster_free(*cluster);
+ *cluster = NULL;
+ }
+}
+
+static void cluster_init(void)
+{
+ cluster_hash = hash_create(cluster_hash_key_make, cluster_hash_cmp,
+ "BGP Cluster");
+}
+
+static void cluster_finish(void)
+{
+ hash_clean(cluster_hash, (void (*)(void *))cluster_free);
+ hash_free(cluster_hash);
+ cluster_hash = NULL;
+}
+
+static struct hash *encap_hash = NULL;
+#ifdef ENABLE_BGP_VNC
+static struct hash *vnc_hash = NULL;
+#endif
+static struct hash *srv6_l3vpn_hash;
+static struct hash *srv6_vpn_hash;
+
+struct bgp_attr_encap_subtlv *encap_tlv_dup(struct bgp_attr_encap_subtlv *orig)
+{
+ struct bgp_attr_encap_subtlv *new;
+ struct bgp_attr_encap_subtlv *tail;
+ struct bgp_attr_encap_subtlv *p;
+
+ for (p = orig, tail = new = NULL; p; p = p->next) {
+ int size = sizeof(struct bgp_attr_encap_subtlv) + p->length;
+ if (tail) {
+ tail->next = XCALLOC(MTYPE_ENCAP_TLV, size);
+ tail = tail->next;
+ } else {
+ tail = new = XCALLOC(MTYPE_ENCAP_TLV, size);
+ }
+ assert(tail);
+ memcpy(tail, p, size);
+ tail->next = NULL;
+ }
+
+ return new;
+}
+
+static void encap_free(struct bgp_attr_encap_subtlv *p)
+{
+ struct bgp_attr_encap_subtlv *next;
+ while (p) {
+ next = p->next;
+ p->next = NULL;
+ XFREE(MTYPE_ENCAP_TLV, p);
+ p = next;
+ }
+}
+
+void bgp_attr_flush_encap(struct attr *attr)
+{
+ if (!attr)
+ return;
+
+ if (attr->encap_subtlvs) {
+ encap_free(attr->encap_subtlvs);
+ attr->encap_subtlvs = NULL;
+ }
+#ifdef ENABLE_BGP_VNC
+ struct bgp_attr_encap_subtlv *vnc_subtlvs =
+ bgp_attr_get_vnc_subtlvs(attr);
+
+ if (vnc_subtlvs) {
+ encap_free(vnc_subtlvs);
+ bgp_attr_set_vnc_subtlvs(attr, NULL);
+ }
+#endif
+}
+
+/*
+ * Compare encap sub-tlv chains
+ *
+ * 1 = equivalent
+ * 0 = not equivalent
+ *
+ * This algorithm could be made faster if needed
+ */
+static bool encap_same(const struct bgp_attr_encap_subtlv *h1,
+ const struct bgp_attr_encap_subtlv *h2)
+{
+ const struct bgp_attr_encap_subtlv *p;
+ const struct bgp_attr_encap_subtlv *q;
+
+ if (h1 == h2)
+ return true;
+ if (h1 == NULL || h2 == NULL)
+ return false;
+
+ for (p = h1; p; p = p->next) {
+ for (q = h2; q; q = q->next) {
+ if ((p->type == q->type) && (p->length == q->length)
+ && !memcmp(p->value, q->value, p->length)) {
+
+ break;
+ }
+ }
+ if (!q)
+ return false;
+ }
+
+ for (p = h2; p; p = p->next) {
+ for (q = h1; q; q = q->next) {
+ if ((p->type == q->type) && (p->length == q->length)
+ && !memcmp(p->value, q->value, p->length)) {
+
+ break;
+ }
+ }
+ if (!q)
+ return false;
+ }
+
+ return true;
+}
+
+static void *encap_hash_alloc(void *p)
+{
+ /* Encap structure is already allocated. */
+ return p;
+}
+
+typedef enum {
+ ENCAP_SUBTLV_TYPE,
+#ifdef ENABLE_BGP_VNC
+ VNC_SUBTLV_TYPE
+#endif
+} encap_subtlv_type;
+
+static struct bgp_attr_encap_subtlv *
+encap_intern(struct bgp_attr_encap_subtlv *encap, encap_subtlv_type type)
+{
+ struct bgp_attr_encap_subtlv *find;
+ struct hash *hash = encap_hash;
+#ifdef ENABLE_BGP_VNC
+ if (type == VNC_SUBTLV_TYPE)
+ hash = vnc_hash;
+#endif
+
+ find = hash_get(hash, encap, encap_hash_alloc);
+ if (find != encap)
+ encap_free(encap);
+ find->refcnt++;
+
+ return find;
+}
+
+static void encap_unintern(struct bgp_attr_encap_subtlv **encapp,
+ encap_subtlv_type type)
+{
+ struct bgp_attr_encap_subtlv *encap = *encapp;
+ if (encap->refcnt)
+ encap->refcnt--;
+
+ if (encap->refcnt == 0) {
+ struct hash *hash = encap_hash;
+#ifdef ENABLE_BGP_VNC
+ if (type == VNC_SUBTLV_TYPE)
+ hash = vnc_hash;
+#endif
+ hash_release(hash, encap);
+ encap_free(encap);
+ *encapp = NULL;
+ }
+}
+
+static unsigned int encap_hash_key_make(const void *p)
+{
+ const struct bgp_attr_encap_subtlv *encap = p;
+
+ return jhash(encap->value, encap->length, 0);
+}
+
+static bool encap_hash_cmp(const void *p1, const void *p2)
+{
+ return encap_same((const struct bgp_attr_encap_subtlv *)p1,
+ (const struct bgp_attr_encap_subtlv *)p2);
+}
+
+static void encap_init(void)
+{
+ encap_hash = hash_create(encap_hash_key_make, encap_hash_cmp,
+ "BGP Encap Hash");
+#ifdef ENABLE_BGP_VNC
+ vnc_hash = hash_create(encap_hash_key_make, encap_hash_cmp,
+ "BGP VNC Hash");
+#endif
+}
+
+static void encap_finish(void)
+{
+ hash_clean(encap_hash, (void (*)(void *))encap_free);
+ hash_free(encap_hash);
+ encap_hash = NULL;
+#ifdef ENABLE_BGP_VNC
+ hash_clean(vnc_hash, (void (*)(void *))encap_free);
+ hash_free(vnc_hash);
+ vnc_hash = NULL;
+#endif
+}
+
+static bool overlay_index_same(const struct attr *a1, const struct attr *a2)
+{
+ if (!a1 && a2)
+ return false;
+ if (!a2 && a1)
+ return false;
+ if (!a1 && !a2)
+ return true;
+
+ return bgp_route_evpn_same(bgp_attr_get_evpn_overlay(a1),
+ bgp_attr_get_evpn_overlay(a2));
+}
+
+/* Unknown transit attribute. */
+static struct hash *transit_hash;
+
+static void transit_free(struct transit *transit)
+{
+ XFREE(MTYPE_TRANSIT_VAL, transit->val);
+ XFREE(MTYPE_TRANSIT, transit);
+}
+
+static void *transit_hash_alloc(void *p)
+{
+ /* Transit structure is already allocated. */
+ return p;
+}
+
+static struct transit *transit_intern(struct transit *transit)
+{
+ struct transit *find;
+
+ find = hash_get(transit_hash, transit, transit_hash_alloc);
+ if (find != transit)
+ transit_free(transit);
+ find->refcnt++;
+
+ return find;
+}
+
+static void transit_unintern(struct transit **transit)
+{
+ if ((*transit)->refcnt)
+ (*transit)->refcnt--;
+
+ if ((*transit)->refcnt == 0) {
+ hash_release(transit_hash, *transit);
+ transit_free(*transit);
+ *transit = NULL;
+ }
+}
+
+static void *srv6_l3vpn_hash_alloc(void *p)
+{
+ return p;
+}
+
+static void srv6_l3vpn_free(struct bgp_attr_srv6_l3vpn *l3vpn)
+{
+ XFREE(MTYPE_BGP_SRV6_L3VPN, l3vpn);
+}
+
+static struct bgp_attr_srv6_l3vpn *
+srv6_l3vpn_intern(struct bgp_attr_srv6_l3vpn *l3vpn)
+{
+ struct bgp_attr_srv6_l3vpn *find;
+
+ find = hash_get(srv6_l3vpn_hash, l3vpn, srv6_l3vpn_hash_alloc);
+ if (find != l3vpn)
+ srv6_l3vpn_free(l3vpn);
+ find->refcnt++;
+ return find;
+}
+
+static void srv6_l3vpn_unintern(struct bgp_attr_srv6_l3vpn **l3vpnp)
+{
+ struct bgp_attr_srv6_l3vpn *l3vpn = *l3vpnp;
+
+ if (l3vpn->refcnt)
+ l3vpn->refcnt--;
+
+ if (l3vpn->refcnt == 0) {
+ hash_release(srv6_l3vpn_hash, l3vpn);
+ srv6_l3vpn_free(l3vpn);
+ *l3vpnp = NULL;
+ }
+}
+
+static void *srv6_vpn_hash_alloc(void *p)
+{
+ return p;
+}
+
+static void srv6_vpn_free(struct bgp_attr_srv6_vpn *vpn)
+{
+ XFREE(MTYPE_BGP_SRV6_VPN, vpn);
+}
+
+static struct bgp_attr_srv6_vpn *srv6_vpn_intern(struct bgp_attr_srv6_vpn *vpn)
+{
+ struct bgp_attr_srv6_vpn *find;
+
+ find = hash_get(srv6_vpn_hash, vpn, srv6_vpn_hash_alloc);
+ if (find != vpn)
+ srv6_vpn_free(vpn);
+ find->refcnt++;
+ return find;
+}
+
+static void srv6_vpn_unintern(struct bgp_attr_srv6_vpn **vpnp)
+{
+ struct bgp_attr_srv6_vpn *vpn = *vpnp;
+
+ if (vpn->refcnt)
+ vpn->refcnt--;
+
+ if (vpn->refcnt == 0) {
+ hash_release(srv6_vpn_hash, vpn);
+ srv6_vpn_free(vpn);
+ *vpnp = NULL;
+ }
+}
+
+static uint32_t srv6_l3vpn_hash_key_make(const void *p)
+{
+ const struct bgp_attr_srv6_l3vpn *l3vpn = p;
+ uint32_t key = 0;
+
+ key = jhash(&l3vpn->sid, 16, key);
+ key = jhash_1word(l3vpn->sid_flags, key);
+ key = jhash_1word(l3vpn->endpoint_behavior, key);
+ key = jhash_1word(l3vpn->loc_block_len, key);
+ key = jhash_1word(l3vpn->loc_node_len, key);
+ key = jhash_1word(l3vpn->func_len, key);
+ key = jhash_1word(l3vpn->arg_len, key);
+ key = jhash_1word(l3vpn->transposition_len, key);
+ key = jhash_1word(l3vpn->transposition_offset, key);
+ return key;
+}
+
+static bool srv6_l3vpn_hash_cmp(const void *p1, const void *p2)
+{
+ const struct bgp_attr_srv6_l3vpn *l3vpn1 = p1;
+ const struct bgp_attr_srv6_l3vpn *l3vpn2 = p2;
+
+ return sid_same(&l3vpn1->sid, &l3vpn2->sid)
+ && l3vpn1->sid_flags == l3vpn2->sid_flags
+ && l3vpn1->endpoint_behavior == l3vpn2->endpoint_behavior
+ && l3vpn1->loc_block_len == l3vpn2->loc_block_len
+ && l3vpn1->loc_node_len == l3vpn2->loc_node_len
+ && l3vpn1->func_len == l3vpn2->func_len
+ && l3vpn1->arg_len == l3vpn2->arg_len
+ && l3vpn1->transposition_len == l3vpn2->transposition_len
+ && l3vpn1->transposition_offset == l3vpn2->transposition_offset;
+}
+
+static bool srv6_l3vpn_same(const struct bgp_attr_srv6_l3vpn *h1,
+ const struct bgp_attr_srv6_l3vpn *h2)
+{
+ if (h1 == h2)
+ return true;
+ else if (h1 == NULL || h2 == NULL)
+ return false;
+ else
+ return srv6_l3vpn_hash_cmp((const void *)h1, (const void *)h2);
+}
+
+static unsigned int srv6_vpn_hash_key_make(const void *p)
+{
+ const struct bgp_attr_srv6_vpn *vpn = p;
+ uint32_t key = 0;
+
+ key = jhash(&vpn->sid, 16, key);
+ key = jhash_1word(vpn->sid_flags, key);
+ return key;
+}
+
+static bool srv6_vpn_hash_cmp(const void *p1, const void *p2)
+{
+ const struct bgp_attr_srv6_vpn *vpn1 = p1;
+ const struct bgp_attr_srv6_vpn *vpn2 = p2;
+
+ return sid_same(&vpn1->sid, &vpn2->sid)
+ && vpn1->sid_flags == vpn2->sid_flags;
+}
+
+static bool srv6_vpn_same(const struct bgp_attr_srv6_vpn *h1,
+ const struct bgp_attr_srv6_vpn *h2)
+{
+ if (h1 == h2)
+ return true;
+ else if (h1 == NULL || h2 == NULL)
+ return false;
+ else
+ return srv6_vpn_hash_cmp((const void *)h1, (const void *)h2);
+}
+
+static void srv6_init(void)
+{
+ srv6_l3vpn_hash =
+ hash_create(srv6_l3vpn_hash_key_make, srv6_l3vpn_hash_cmp,
+ "BGP Prefix-SID SRv6-L3VPN-Service-TLV");
+ srv6_vpn_hash = hash_create(srv6_vpn_hash_key_make, srv6_vpn_hash_cmp,
+ "BGP Prefix-SID SRv6-VPN-Service-TLV");
+}
+
+static void srv6_finish(void)
+{
+ hash_clean(srv6_l3vpn_hash, (void (*)(void *))srv6_l3vpn_free);
+ hash_free(srv6_l3vpn_hash);
+ srv6_l3vpn_hash = NULL;
+ hash_clean(srv6_vpn_hash, (void (*)(void *))srv6_vpn_free);
+ hash_free(srv6_vpn_hash);
+ srv6_vpn_hash = NULL;
+}
+
+static unsigned int transit_hash_key_make(const void *p)
+{
+ const struct transit *transit = p;
+
+ return jhash(transit->val, transit->length, 0);
+}
+
+static bool transit_hash_cmp(const void *p1, const void *p2)
+{
+ const struct transit *transit1 = p1;
+ const struct transit *transit2 = p2;
+
+ return (transit1->length == transit2->length
+ && memcmp(transit1->val, transit2->val, transit1->length) == 0);
+}
+
+static void transit_init(void)
+{
+ transit_hash = hash_create(transit_hash_key_make, transit_hash_cmp,
+ "BGP Transit Hash");
+}
+
+static void transit_finish(void)
+{
+ hash_clean(transit_hash, (void (*)(void *))transit_free);
+ hash_free(transit_hash);
+ transit_hash = NULL;
+}
+
+/* Attribute hash routines. */
+static struct hash *attrhash;
+
+unsigned long int attr_count(void)
+{
+ return attrhash->count;
+}
+
+unsigned long int attr_unknown_count(void)
+{
+ return transit_hash->count;
+}
+
+unsigned int attrhash_key_make(const void *p)
+{
+ const struct attr *attr = (struct attr *)p;
+ uint32_t key = 0;
+#define MIX(val) key = jhash_1word(val, key)
+#define MIX3(a, b, c) key = jhash_3words((a), (b), (c), key)
+
+ MIX3(attr->origin, attr->nexthop.s_addr, attr->med);
+ MIX3(attr->local_pref, attr->aggregator_as,
+ attr->aggregator_addr.s_addr);
+ MIX3(attr->weight, attr->mp_nexthop_global_in.s_addr,
+ attr->originator_id.s_addr);
+ MIX3(attr->tag, attr->label, attr->label_index);
+
+ if (attr->aspath)
+ MIX(aspath_key_make(attr->aspath));
+ if (bgp_attr_get_community(attr))
+ MIX(community_hash_make(bgp_attr_get_community(attr)));
+ if (bgp_attr_get_lcommunity(attr))
+ MIX(lcommunity_hash_make(bgp_attr_get_lcommunity(attr)));
+ if (bgp_attr_get_ecommunity(attr))
+ MIX(ecommunity_hash_make(bgp_attr_get_ecommunity(attr)));
+ if (bgp_attr_get_ipv6_ecommunity(attr))
+ MIX(ecommunity_hash_make(bgp_attr_get_ipv6_ecommunity(attr)));
+ if (bgp_attr_get_cluster(attr))
+ MIX(cluster_hash_key_make(bgp_attr_get_cluster(attr)));
+ if (bgp_attr_get_transit(attr))
+ MIX(transit_hash_key_make(bgp_attr_get_transit(attr)));
+ if (attr->encap_subtlvs)
+ MIX(encap_hash_key_make(attr->encap_subtlvs));
+ if (attr->srv6_l3vpn)
+ MIX(srv6_l3vpn_hash_key_make(attr->srv6_l3vpn));
+ if (attr->srv6_vpn)
+ MIX(srv6_vpn_hash_key_make(attr->srv6_vpn));
+#ifdef ENABLE_BGP_VNC
+ struct bgp_attr_encap_subtlv *vnc_subtlvs =
+ bgp_attr_get_vnc_subtlvs(attr);
+ if (vnc_subtlvs)
+ MIX(encap_hash_key_make(vnc_subtlvs));
+#endif
+ MIX(attr->mp_nexthop_len);
+ key = jhash(attr->mp_nexthop_global.s6_addr, IPV6_MAX_BYTELEN, key);
+ key = jhash(attr->mp_nexthop_local.s6_addr, IPV6_MAX_BYTELEN, key);
+ MIX3(attr->nh_ifindex, attr->nh_lla_ifindex, attr->distance);
+ MIX(attr->rmap_table_id);
+ MIX(attr->nh_type);
+ MIX(attr->bh_type);
+ MIX(attr->otc);
+
+ return key;
+}
+
+bool attrhash_cmp(const void *p1, const void *p2)
+{
+ const struct attr *attr1 = p1;
+ const struct attr *attr2 = p2;
+
+ if (attr1->flag == attr2->flag && attr1->origin == attr2->origin
+ && attr1->nexthop.s_addr == attr2->nexthop.s_addr
+ && attr1->aspath == attr2->aspath
+ && bgp_attr_get_community(attr1)
+ == bgp_attr_get_community(attr2)
+ && attr1->med == attr2->med
+ && attr1->local_pref == attr2->local_pref
+ && attr1->rmap_change_flags == attr2->rmap_change_flags) {
+ if (attr1->aggregator_as == attr2->aggregator_as
+ && attr1->aggregator_addr.s_addr
+ == attr2->aggregator_addr.s_addr
+ && attr1->weight == attr2->weight
+ && attr1->tag == attr2->tag
+ && attr1->label_index == attr2->label_index
+ && attr1->mp_nexthop_len == attr2->mp_nexthop_len
+ && bgp_attr_get_ecommunity(attr1)
+ == bgp_attr_get_ecommunity(attr2)
+ && bgp_attr_get_ipv6_ecommunity(attr1)
+ == bgp_attr_get_ipv6_ecommunity(attr2)
+ && bgp_attr_get_lcommunity(attr1)
+ == bgp_attr_get_lcommunity(attr2)
+ && bgp_attr_get_cluster(attr1)
+ == bgp_attr_get_cluster(attr2)
+ && bgp_attr_get_transit(attr1)
+ == bgp_attr_get_transit(attr2)
+ && attr1->rmap_table_id == attr2->rmap_table_id
+ && (attr1->encap_tunneltype == attr2->encap_tunneltype)
+ && encap_same(attr1->encap_subtlvs, attr2->encap_subtlvs)
+#ifdef ENABLE_BGP_VNC
+ && encap_same(bgp_attr_get_vnc_subtlvs(attr1),
+ bgp_attr_get_vnc_subtlvs(attr2))
+#endif
+ && IPV6_ADDR_SAME(&attr1->mp_nexthop_global,
+ &attr2->mp_nexthop_global)
+ && IPV6_ADDR_SAME(&attr1->mp_nexthop_local,
+ &attr2->mp_nexthop_local)
+ && IPV4_ADDR_SAME(&attr1->mp_nexthop_global_in,
+ &attr2->mp_nexthop_global_in)
+ && IPV4_ADDR_SAME(&attr1->originator_id,
+ &attr2->originator_id)
+ && overlay_index_same(attr1, attr2)
+ && !memcmp(&attr1->esi, &attr2->esi, sizeof(esi_t))
+ && attr1->es_flags == attr2->es_flags
+ && attr1->mm_sync_seqnum == attr2->mm_sync_seqnum
+ && attr1->df_pref == attr2->df_pref
+ && attr1->df_alg == attr2->df_alg
+ && attr1->nh_ifindex == attr2->nh_ifindex
+ && attr1->nh_lla_ifindex == attr2->nh_lla_ifindex
+ && attr1->distance == attr2->distance
+ && srv6_l3vpn_same(attr1->srv6_l3vpn, attr2->srv6_l3vpn)
+ && srv6_vpn_same(attr1->srv6_vpn, attr2->srv6_vpn)
+ && attr1->srte_color == attr2->srte_color
+ && attr1->nh_type == attr2->nh_type
+ && attr1->bh_type == attr2->bh_type
+ && attr1->otc == attr2->otc)
+ return true;
+ }
+
+ return false;
+}
+
+static void attrhash_init(void)
+{
+ attrhash =
+ hash_create(attrhash_key_make, attrhash_cmp, "BGP Attributes");
+}
+
+/*
+ * special for hash_clean below
+ */
+static void attr_vfree(void *attr)
+{
+ XFREE(MTYPE_ATTR, attr);
+}
+
+static void attrhash_finish(void)
+{
+ hash_clean(attrhash, attr_vfree);
+ hash_free(attrhash);
+ attrhash = NULL;
+}
+
+static void attr_show_all_iterator(struct hash_bucket *bucket, struct vty *vty)
+{
+ struct attr *attr = bucket->data;
+ char sid_str[BUFSIZ];
+
+ vty_out(vty, "attr[%ld] nexthop %pI4\n", attr->refcnt, &attr->nexthop);
+
+ sid_str[0] = '\0';
+ if (attr->srv6_l3vpn)
+ inet_ntop(AF_INET6, &attr->srv6_l3vpn->sid, sid_str, BUFSIZ);
+ else if (attr->srv6_vpn)
+ inet_ntop(AF_INET6, &attr->srv6_vpn->sid, sid_str, BUFSIZ);
+
+ vty_out(vty,
+ "\tflags: %" PRIu64" distance: %u med: %u local_pref: %u origin: %u weight: %u label: %u sid: %s\n",
+ attr->flag, attr->distance, attr->med, attr->local_pref,
+ attr->origin, attr->weight, attr->label, sid_str);
+}
+
+void attr_show_all(struct vty *vty)
+{
+ hash_iterate(attrhash, (void (*)(struct hash_bucket *,
+ void *))attr_show_all_iterator,
+ vty);
+}
+
+static void *bgp_attr_hash_alloc(void *p)
+{
+ struct attr *val = (struct attr *)p;
+ struct attr *attr;
+
+ attr = XMALLOC(MTYPE_ATTR, sizeof(struct attr));
+ *attr = *val;
+ if (val->encap_subtlvs) {
+ val->encap_subtlvs = NULL;
+ }
+#ifdef ENABLE_BGP_VNC
+ struct bgp_attr_encap_subtlv *vnc_subtlvs =
+ bgp_attr_get_vnc_subtlvs(val);
+
+ if (vnc_subtlvs)
+ bgp_attr_set_vnc_subtlvs(val, NULL);
+#endif
+
+ attr->refcnt = 0;
+ return attr;
+}
+
+/* Internet argument attribute. */
+struct attr *bgp_attr_intern(struct attr *attr)
+{
+ struct attr *find;
+ struct ecommunity *ecomm = NULL;
+ struct ecommunity *ipv6_ecomm = NULL;
+ struct lcommunity *lcomm = NULL;
+ struct community *comm = NULL;
+
+ /* Intern referenced structure. */
+ if (attr->aspath) {
+ if (!attr->aspath->refcnt)
+ attr->aspath = aspath_intern(attr->aspath);
+ else
+ attr->aspath->refcnt++;
+ }
+
+ comm = bgp_attr_get_community(attr);
+ if (comm) {
+ if (!comm->refcnt)
+ bgp_attr_set_community(attr, community_intern(comm));
+ else
+ comm->refcnt++;
+ }
+
+ ecomm = bgp_attr_get_ecommunity(attr);
+ if (ecomm) {
+ if (!ecomm->refcnt)
+ bgp_attr_set_ecommunity(attr, ecommunity_intern(ecomm));
+ else
+ ecomm->refcnt++;
+ }
+
+ ipv6_ecomm = bgp_attr_get_ipv6_ecommunity(attr);
+ if (ipv6_ecomm) {
+ if (!ipv6_ecomm->refcnt)
+ bgp_attr_set_ipv6_ecommunity(
+ attr, ecommunity_intern(ipv6_ecomm));
+ else
+ ipv6_ecomm->refcnt++;
+ }
+
+ lcomm = bgp_attr_get_lcommunity(attr);
+ if (lcomm) {
+ if (!lcomm->refcnt)
+ bgp_attr_set_lcommunity(attr, lcommunity_intern(lcomm));
+ else
+ lcomm->refcnt++;
+ }
+
+ struct cluster_list *cluster = bgp_attr_get_cluster(attr);
+
+ if (cluster) {
+ if (!cluster->refcnt)
+ bgp_attr_set_cluster(attr, cluster_intern(cluster));
+ else
+ cluster->refcnt++;
+ }
+
+ struct transit *transit = bgp_attr_get_transit(attr);
+
+ if (transit) {
+ if (!transit->refcnt)
+ bgp_attr_set_transit(attr, transit_intern(transit));
+ else
+ transit->refcnt++;
+ }
+ if (attr->encap_subtlvs) {
+ if (!attr->encap_subtlvs->refcnt)
+ attr->encap_subtlvs = encap_intern(attr->encap_subtlvs,
+ ENCAP_SUBTLV_TYPE);
+ else
+ attr->encap_subtlvs->refcnt++;
+ }
+ if (attr->srv6_l3vpn) {
+ if (!attr->srv6_l3vpn->refcnt)
+ attr->srv6_l3vpn = srv6_l3vpn_intern(attr->srv6_l3vpn);
+ else
+ attr->srv6_l3vpn->refcnt++;
+ }
+ if (attr->srv6_vpn) {
+ if (!attr->srv6_vpn->refcnt)
+ attr->srv6_vpn = srv6_vpn_intern(attr->srv6_vpn);
+ else
+ attr->srv6_vpn->refcnt++;
+ }
+#ifdef ENABLE_BGP_VNC
+ struct bgp_attr_encap_subtlv *vnc_subtlvs =
+ bgp_attr_get_vnc_subtlvs(attr);
+
+ if (vnc_subtlvs) {
+ if (!vnc_subtlvs->refcnt)
+ bgp_attr_set_vnc_subtlvs(
+ attr,
+ encap_intern(vnc_subtlvs, VNC_SUBTLV_TYPE));
+ else
+ vnc_subtlvs->refcnt++;
+ }
+#endif
+
+ /* At this point, attr only contains intern'd pointers. that means
+ * if we find it in attrhash, it has all the same pointers and we
+ * correctly updated the refcounts on these.
+ * If we don't find it, we need to allocate a one because in all
+ * cases this returns a new reference to a hashed attr, but the input
+ * wasn't on hash. */
+ find = (struct attr *)hash_get(attrhash, attr, bgp_attr_hash_alloc);
+ find->refcnt++;
+
+ return find;
+}
+
+/* Make network statement's attribute. */
+struct attr *bgp_attr_default_set(struct attr *attr, struct bgp *bgp,
+ uint8_t origin)
+{
+ memset(attr, 0, sizeof(struct attr));
+
+ attr->origin = origin;
+ attr->flag |= ATTR_FLAG_BIT(BGP_ATTR_ORIGIN);
+ attr->aspath = aspath_empty();
+ attr->flag |= ATTR_FLAG_BIT(BGP_ATTR_AS_PATH);
+ attr->weight = BGP_ATTR_DEFAULT_WEIGHT;
+ attr->tag = 0;
+ attr->label_index = BGP_INVALID_LABEL_INDEX;
+ attr->label = MPLS_INVALID_LABEL;
+ attr->flag |= ATTR_FLAG_BIT(BGP_ATTR_NEXT_HOP);
+ attr->mp_nexthop_len = IPV6_MAX_BYTELEN;
+ attr->local_pref = bgp->default_local_pref;
+
+ return attr;
+}
+
+/* Create the attributes for an aggregate */
+struct attr *bgp_attr_aggregate_intern(
+ struct bgp *bgp, uint8_t origin, struct aspath *aspath,
+ struct community *community, struct ecommunity *ecommunity,
+ struct lcommunity *lcommunity, struct bgp_aggregate *aggregate,
+ uint8_t atomic_aggregate, const struct prefix *p)
+{
+ struct attr attr;
+ struct attr *new;
+ route_map_result_t ret;
+
+ memset(&attr, 0, sizeof(attr));
+
+ /* Origin attribute. */
+ attr.origin = origin;
+ attr.flag |= ATTR_FLAG_BIT(BGP_ATTR_ORIGIN);
+
+ /* MED */
+ attr.med = 0;
+ attr.flag |= ATTR_FLAG_BIT(BGP_ATTR_MULTI_EXIT_DISC);
+
+ /* AS path attribute. */
+ if (aspath)
+ attr.aspath = aspath_intern(aspath);
+ else
+ attr.aspath = aspath_empty();
+ attr.flag |= ATTR_FLAG_BIT(BGP_ATTR_AS_PATH);
+
+ /* Next hop attribute. */
+ attr.flag |= ATTR_FLAG_BIT(BGP_ATTR_NEXT_HOP);
+
+ if (community) {
+ uint32_t gshut = COMMUNITY_GSHUT;
+
+ /* If we are not shutting down ourselves and we are
+ * aggregating a route that contains the GSHUT community we
+ * need to remove that community when creating the aggregate */
+ if (!bgp_in_graceful_shutdown(bgp)
+ && community_include(community, gshut)) {
+ community_del_val(community, &gshut);
+ }
+
+ bgp_attr_set_community(&attr, community);
+ }
+
+ if (ecommunity)
+ bgp_attr_set_ecommunity(&attr, ecommunity);
+
+ if (lcommunity)
+ bgp_attr_set_lcommunity(&attr, lcommunity);
+
+ if (bgp_in_graceful_shutdown(bgp))
+ bgp_attr_add_gshut_community(&attr);
+
+ attr.label_index = BGP_INVALID_LABEL_INDEX;
+ attr.label = MPLS_INVALID_LABEL;
+ attr.weight = BGP_ATTR_DEFAULT_WEIGHT;
+ attr.mp_nexthop_len = IPV6_MAX_BYTELEN;
+ if (!aggregate->as_set || atomic_aggregate)
+ attr.flag |= ATTR_FLAG_BIT(BGP_ATTR_ATOMIC_AGGREGATE);
+ attr.flag |= ATTR_FLAG_BIT(BGP_ATTR_AGGREGATOR);
+ if (CHECK_FLAG(bgp->config, BGP_CONFIG_CONFEDERATION))
+ attr.aggregator_as = bgp->confed_id;
+ else
+ attr.aggregator_as = bgp->as;
+ attr.aggregator_addr = bgp->router_id;
+
+ /* Apply route-map */
+ if (aggregate->rmap.name) {
+ struct attr attr_tmp = attr;
+ struct bgp_path_info rmap_path;
+
+ 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_AGGREGATE);
+
+ ret = route_map_apply(aggregate->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);
+ return NULL;
+ }
+
+ if (bgp_in_graceful_shutdown(bgp))
+ bgp_attr_add_gshut_community(&attr_tmp);
+
+ new = bgp_attr_intern(&attr_tmp);
+ } else {
+
+ if (bgp_in_graceful_shutdown(bgp))
+ bgp_attr_add_gshut_community(&attr);
+
+ new = bgp_attr_intern(&attr);
+ }
+
+ /* Always release the 'intern()'ed AS Path. */
+ aspath_unintern(&attr.aspath);
+
+ return new;
+}
+
+/* Unintern just the sub-components of the attr, but not the attr */
+void bgp_attr_unintern_sub(struct attr *attr)
+{
+ struct ecommunity *ecomm = NULL;
+ struct ecommunity *ipv6_ecomm = NULL;
+ struct cluster_list *cluster;
+ struct lcommunity *lcomm = NULL;
+ struct community *comm = NULL;
+
+ /* aspath refcount shoud be decrement. */
+ aspath_unintern(&attr->aspath);
+ UNSET_FLAG(attr->flag, ATTR_FLAG_BIT(BGP_ATTR_AS_PATH));
+
+ comm = bgp_attr_get_community(attr);
+ community_unintern(&comm);
+ bgp_attr_set_community(attr, NULL);
+
+ ecomm = bgp_attr_get_ecommunity(attr);
+ ecommunity_unintern(&ecomm);
+ bgp_attr_set_ecommunity(attr, NULL);
+
+ ipv6_ecomm = bgp_attr_get_ipv6_ecommunity(attr);
+ ecommunity_unintern(&ipv6_ecomm);
+ bgp_attr_set_ipv6_ecommunity(attr, NULL);
+
+ lcomm = bgp_attr_get_lcommunity(attr);
+ lcommunity_unintern(&lcomm);
+ bgp_attr_set_lcommunity(attr, NULL);
+
+ cluster = bgp_attr_get_cluster(attr);
+ if (cluster) {
+ cluster_unintern(&cluster);
+ bgp_attr_set_cluster(attr, cluster);
+ }
+ UNSET_FLAG(attr->flag, ATTR_FLAG_BIT(BGP_ATTR_CLUSTER_LIST));
+
+ struct transit *transit = bgp_attr_get_transit(attr);
+
+ if (transit) {
+ transit_unintern(&transit);
+ bgp_attr_set_transit(attr, transit);
+ }
+
+ if (attr->encap_subtlvs)
+ encap_unintern(&attr->encap_subtlvs, ENCAP_SUBTLV_TYPE);
+
+#ifdef ENABLE_BGP_VNC
+ struct bgp_attr_encap_subtlv *vnc_subtlvs =
+ bgp_attr_get_vnc_subtlvs(attr);
+
+ if (vnc_subtlvs) {
+ encap_unintern(&vnc_subtlvs, VNC_SUBTLV_TYPE);
+ bgp_attr_set_vnc_subtlvs(attr, vnc_subtlvs);
+ }
+#endif
+
+ if (attr->srv6_l3vpn)
+ srv6_l3vpn_unintern(&attr->srv6_l3vpn);
+
+ if (attr->srv6_vpn)
+ srv6_vpn_unintern(&attr->srv6_vpn);
+}
+
+/* Free bgp attribute and aspath. */
+void bgp_attr_unintern(struct attr **pattr)
+{
+ struct attr *attr = *pattr;
+ struct attr *ret;
+ struct attr tmp;
+
+ /* Decrement attribute reference. */
+ attr->refcnt--;
+
+ tmp = *attr;
+
+ /* If reference becomes zero then free attribute object. */
+ if (attr->refcnt == 0) {
+ ret = hash_release(attrhash, attr);
+ assert(ret != NULL);
+ XFREE(MTYPE_ATTR, attr);
+ *pattr = NULL;
+ }
+
+ bgp_attr_unintern_sub(&tmp);
+}
+
+void bgp_attr_flush(struct attr *attr)
+{
+ struct ecommunity *ecomm;
+ struct ecommunity *ipv6_ecomm;
+ struct cluster_list *cluster;
+ struct lcommunity *lcomm;
+ struct community *comm;
+
+ if (attr->aspath && !attr->aspath->refcnt) {
+ aspath_free(attr->aspath);
+ attr->aspath = NULL;
+ }
+ comm = bgp_attr_get_community(attr);
+ if (comm && !comm->refcnt)
+ community_free(&comm);
+ bgp_attr_set_community(attr, NULL);
+
+ ecomm = bgp_attr_get_ecommunity(attr);
+ if (ecomm && !ecomm->refcnt)
+ ecommunity_free(&ecomm);
+ bgp_attr_set_ecommunity(attr, NULL);
+
+ ipv6_ecomm = bgp_attr_get_ipv6_ecommunity(attr);
+ if (ipv6_ecomm && !ipv6_ecomm->refcnt)
+ ecommunity_free(&ipv6_ecomm);
+ bgp_attr_set_ipv6_ecommunity(attr, NULL);
+
+ lcomm = bgp_attr_get_lcommunity(attr);
+ if (lcomm && !lcomm->refcnt)
+ lcommunity_free(&lcomm);
+ bgp_attr_set_lcommunity(attr, NULL);
+
+ cluster = bgp_attr_get_cluster(attr);
+ if (cluster && !cluster->refcnt) {
+ cluster_free(cluster);
+ bgp_attr_set_cluster(attr, NULL);
+ }
+
+ struct transit *transit = bgp_attr_get_transit(attr);
+
+ if (transit && !transit->refcnt) {
+ transit_free(transit);
+ bgp_attr_set_transit(attr, NULL);
+ }
+ if (attr->encap_subtlvs && !attr->encap_subtlvs->refcnt) {
+ encap_free(attr->encap_subtlvs);
+ attr->encap_subtlvs = NULL;
+ }
+ if (attr->srv6_l3vpn && !attr->srv6_l3vpn->refcnt) {
+ srv6_l3vpn_free(attr->srv6_l3vpn);
+ attr->srv6_l3vpn = NULL;
+ }
+ if (attr->srv6_vpn && !attr->srv6_vpn->refcnt) {
+ srv6_vpn_free(attr->srv6_vpn);
+ attr->srv6_vpn = NULL;
+ }
+#ifdef ENABLE_BGP_VNC
+ struct bgp_attr_encap_subtlv *vnc_subtlvs =
+ bgp_attr_get_vnc_subtlvs(attr);
+
+ if (vnc_subtlvs && !vnc_subtlvs->refcnt) {
+ encap_free(vnc_subtlvs);
+ bgp_attr_set_vnc_subtlvs(attr, NULL);
+ }
+#endif
+}
+
+/* Implement draft-scudder-idr-optional-transitive behaviour and
+ * avoid resetting sessions for malformed attributes which are
+ * are partial/optional and hence where the error likely was not
+ * introduced by the sending neighbour.
+ */
+static enum bgp_attr_parse_ret
+bgp_attr_malformed(struct bgp_attr_parser_args *args, uint8_t subcode,
+ bgp_size_t length)
+{
+ struct peer *const peer = args->peer;
+ struct attr *const attr = args->attr;
+ const uint8_t flags = args->flags;
+ /* startp and length must be special-cased, as whether or not to
+ * send the attribute data with the NOTIFY depends on the error,
+ * the caller therefore signals this with the seperate length argument
+ */
+ uint8_t *notify_datap = (length > 0 ? args->startp : NULL);
+
+ if (bgp_debug_update(peer, NULL, NULL, 1)) {
+ char attr_str[BUFSIZ] = {0};
+
+ bgp_dump_attr(attr, attr_str, sizeof(attr_str));
+
+ zlog_debug("%s: attributes: %s", __func__, attr_str);
+ }
+
+ /* Only relax error handling for eBGP peers */
+ if (peer->sort != BGP_PEER_EBGP) {
+ bgp_notify_send_with_data(peer, BGP_NOTIFY_UPDATE_ERR, subcode,
+ notify_datap, length);
+ return BGP_ATTR_PARSE_ERROR;
+ }
+
+ /* Adjust the stream getp to the end of the attribute, in case we can
+ * still proceed but the caller hasn't read all the attribute.
+ */
+ stream_set_getp(BGP_INPUT(peer),
+ (args->startp - STREAM_DATA(BGP_INPUT(peer)))
+ + args->total);
+
+ switch (args->type) {
+ /* where an attribute is relatively inconsequential, e.g. it does not
+ * affect route selection, and can be safely ignored, then any such
+ * attributes which are malformed should just be ignored and the route
+ * processed as normal.
+ */
+ case BGP_ATTR_AS4_AGGREGATOR:
+ case BGP_ATTR_AGGREGATOR:
+ case BGP_ATTR_ATOMIC_AGGREGATE:
+ return BGP_ATTR_PARSE_PROCEED;
+
+ /* Core attributes, particularly ones which may influence route
+ * selection, should be treat-as-withdraw.
+ */
+ case BGP_ATTR_ORIGIN:
+ case BGP_ATTR_AS_PATH:
+ case BGP_ATTR_NEXT_HOP:
+ case BGP_ATTR_MULTI_EXIT_DISC:
+ case BGP_ATTR_LOCAL_PREF:
+ case BGP_ATTR_COMMUNITIES:
+ case BGP_ATTR_EXT_COMMUNITIES:
+ case BGP_ATTR_IPV6_EXT_COMMUNITIES:
+ case BGP_ATTR_LARGE_COMMUNITIES:
+ case BGP_ATTR_ORIGINATOR_ID:
+ case BGP_ATTR_CLUSTER_LIST:
+ case BGP_ATTR_OTC:
+ return BGP_ATTR_PARSE_WITHDRAW;
+ case BGP_ATTR_MP_REACH_NLRI:
+ case BGP_ATTR_MP_UNREACH_NLRI:
+ bgp_notify_send_with_data(peer, BGP_NOTIFY_UPDATE_ERR, subcode,
+ notify_datap, length);
+ return BGP_ATTR_PARSE_ERROR;
+ }
+
+ /* Partial optional attributes that are malformed should not cause
+ * the whole session to be reset. Instead treat it as a withdrawal
+ * of the routes, if possible.
+ */
+ if (CHECK_FLAG(flags, BGP_ATTR_FLAG_TRANS)
+ && CHECK_FLAG(flags, BGP_ATTR_FLAG_OPTIONAL)
+ && CHECK_FLAG(flags, BGP_ATTR_FLAG_PARTIAL))
+ return BGP_ATTR_PARSE_WITHDRAW;
+
+ /* default to reset */
+ return BGP_ATTR_PARSE_ERROR_NOTIFYPLS;
+}
+
+/* Find out what is wrong with the path attribute flag bits and log the error.
+ "Flag bits" here stand for Optional, Transitive and Partial, but not for
+ Extended Length. Checking O/T/P bits at once implies, that the attribute
+ being diagnosed is defined by RFC as either a "well-known" or an "optional,
+ non-transitive" attribute. */
+static void
+bgp_attr_flags_diagnose(struct bgp_attr_parser_args *args,
+ uint8_t desired_flags /* how RFC says it must be */
+)
+{
+ uint8_t seen = 0, i;
+ uint8_t real_flags = args->flags;
+ const uint8_t attr_code = args->type;
+
+ desired_flags &= ~BGP_ATTR_FLAG_EXTLEN;
+ real_flags &= ~BGP_ATTR_FLAG_EXTLEN;
+ for (i = 0; i <= 2; i++) /* O,T,P, but not E */
+ if (CHECK_FLAG(desired_flags, attr_flag_str[i].key)
+ != CHECK_FLAG(real_flags, attr_flag_str[i].key)) {
+ flog_err(EC_BGP_ATTR_FLAG,
+ "%s attribute must%s be flagged as \"%s\"",
+ lookup_msg(attr_str, attr_code, NULL),
+ CHECK_FLAG(desired_flags, attr_flag_str[i].key)
+ ? ""
+ : " not",
+ attr_flag_str[i].str);
+ seen = 1;
+ }
+ if (!seen) {
+ zlog_debug(
+ "Strange, %s called for attr %s, but no problem found with flags (real flags 0x%x, desired 0x%x)",
+ __func__, lookup_msg(attr_str, attr_code, NULL),
+ real_flags, desired_flags);
+ }
+}
+
+/* Required flags for attributes. EXTLEN will be masked off when testing,
+ * as will PARTIAL for optional+transitive attributes.
+ */
+const uint8_t attr_flags_values[] = {
+ [BGP_ATTR_ORIGIN] = BGP_ATTR_FLAG_TRANS,
+ [BGP_ATTR_AS_PATH] = BGP_ATTR_FLAG_TRANS,
+ [BGP_ATTR_NEXT_HOP] = BGP_ATTR_FLAG_TRANS,
+ [BGP_ATTR_MULTI_EXIT_DISC] = BGP_ATTR_FLAG_OPTIONAL,
+ [BGP_ATTR_LOCAL_PREF] = BGP_ATTR_FLAG_TRANS,
+ [BGP_ATTR_ATOMIC_AGGREGATE] = BGP_ATTR_FLAG_TRANS,
+ [BGP_ATTR_AGGREGATOR] = BGP_ATTR_FLAG_TRANS | BGP_ATTR_FLAG_OPTIONAL,
+ [BGP_ATTR_COMMUNITIES] = BGP_ATTR_FLAG_TRANS | BGP_ATTR_FLAG_OPTIONAL,
+ [BGP_ATTR_ORIGINATOR_ID] = BGP_ATTR_FLAG_OPTIONAL,
+ [BGP_ATTR_CLUSTER_LIST] = BGP_ATTR_FLAG_OPTIONAL,
+ [BGP_ATTR_MP_REACH_NLRI] = BGP_ATTR_FLAG_OPTIONAL,
+ [BGP_ATTR_MP_UNREACH_NLRI] = BGP_ATTR_FLAG_OPTIONAL,
+ [BGP_ATTR_EXT_COMMUNITIES] =
+ BGP_ATTR_FLAG_OPTIONAL | BGP_ATTR_FLAG_TRANS,
+ [BGP_ATTR_AS4_PATH] = BGP_ATTR_FLAG_OPTIONAL | BGP_ATTR_FLAG_TRANS,
+ [BGP_ATTR_AS4_AGGREGATOR] =
+ BGP_ATTR_FLAG_OPTIONAL | BGP_ATTR_FLAG_TRANS,
+ [BGP_ATTR_PMSI_TUNNEL] = BGP_ATTR_FLAG_OPTIONAL | BGP_ATTR_FLAG_TRANS,
+ [BGP_ATTR_LARGE_COMMUNITIES] =
+ BGP_ATTR_FLAG_OPTIONAL | BGP_ATTR_FLAG_TRANS,
+ [BGP_ATTR_OTC] = BGP_ATTR_FLAG_OPTIONAL | BGP_ATTR_FLAG_TRANS,
+ [BGP_ATTR_PREFIX_SID] = BGP_ATTR_FLAG_OPTIONAL | BGP_ATTR_FLAG_TRANS,
+ [BGP_ATTR_IPV6_EXT_COMMUNITIES] =
+ BGP_ATTR_FLAG_OPTIONAL | BGP_ATTR_FLAG_TRANS,
+};
+static const size_t attr_flags_values_max = array_size(attr_flags_values) - 1;
+
+static bool bgp_attr_flag_invalid(struct bgp_attr_parser_args *args)
+{
+ uint8_t mask = BGP_ATTR_FLAG_EXTLEN;
+ const uint8_t flags = args->flags;
+ const uint8_t attr_code = args->type;
+
+ /* there may be attributes we don't know about */
+ if (attr_code > attr_flags_values_max)
+ return false;
+ if (attr_flags_values[attr_code] == 0)
+ return false;
+
+ /* RFC4271, "For well-known attributes, the Transitive bit MUST be set
+ * to
+ * 1."
+ */
+ if (!CHECK_FLAG(BGP_ATTR_FLAG_OPTIONAL, flags)
+ && !CHECK_FLAG(BGP_ATTR_FLAG_TRANS, flags)) {
+ flog_err(
+ EC_BGP_ATTR_FLAG,
+ "%s well-known attributes must have transitive flag set (%x)",
+ lookup_msg(attr_str, attr_code, NULL), flags);
+ return true;
+ }
+
+ /* "For well-known attributes and for optional non-transitive
+ * attributes,
+ * the Partial bit MUST be set to 0."
+ */
+ if (CHECK_FLAG(flags, BGP_ATTR_FLAG_PARTIAL)) {
+ if (!CHECK_FLAG(flags, BGP_ATTR_FLAG_OPTIONAL)) {
+ flog_err(EC_BGP_ATTR_FLAG,
+ "%s well-known attribute must NOT have the partial flag set (%x)",
+ lookup_msg(attr_str, attr_code, NULL), flags);
+ return true;
+ }
+ if (CHECK_FLAG(flags, BGP_ATTR_FLAG_OPTIONAL)
+ && !CHECK_FLAG(flags, BGP_ATTR_FLAG_TRANS)) {
+ flog_err(EC_BGP_ATTR_FLAG,
+ "%s optional + transitive attribute must NOT have the partial flag set (%x)",
+ lookup_msg(attr_str, attr_code, NULL), flags);
+ return true;
+ }
+ }
+
+ /* Optional transitive attributes may go through speakers that don't
+ * reocgnise them and set the Partial bit.
+ */
+ if (CHECK_FLAG(flags, BGP_ATTR_FLAG_OPTIONAL)
+ && CHECK_FLAG(flags, BGP_ATTR_FLAG_TRANS))
+ SET_FLAG(mask, BGP_ATTR_FLAG_PARTIAL);
+
+ if ((flags & ~mask) == attr_flags_values[attr_code])
+ return false;
+
+ bgp_attr_flags_diagnose(args, attr_flags_values[attr_code]);
+ return true;
+}
+
+/* Get origin attribute of the update message. */
+static enum bgp_attr_parse_ret
+bgp_attr_origin(struct bgp_attr_parser_args *args)
+{
+ struct peer *const peer = args->peer;
+ struct attr *const attr = args->attr;
+ const bgp_size_t length = args->length;
+
+ /* If any recognized attribute has Attribute Length that conflicts
+ with the expected length (based on the attribute type code), then
+ the Error Subcode is set to Attribute Length Error. The Data
+ field contains the erroneous attribute (type, length and
+ value). */
+ if (length != 1) {
+ flog_err(EC_BGP_ATTR_LEN,
+ "Origin attribute length is not one %d", length);
+ return bgp_attr_malformed(args, BGP_NOTIFY_UPDATE_ATTR_LENG_ERR,
+ args->total);
+ }
+
+ /* Fetch origin attribute. */
+ attr->origin = stream_getc(BGP_INPUT(peer));
+
+ /* If the ORIGIN attribute has an undefined value, then the Error
+ Subcode is set to Invalid Origin Attribute. The Data field
+ contains the unrecognized attribute (type, length and value). */
+ if ((attr->origin != BGP_ORIGIN_IGP) && (attr->origin != BGP_ORIGIN_EGP)
+ && (attr->origin != BGP_ORIGIN_INCOMPLETE)) {
+ flog_err(EC_BGP_ATTR_ORIGIN,
+ "Origin attribute value is invalid %d", attr->origin);
+ return bgp_attr_malformed(args, BGP_NOTIFY_UPDATE_INVAL_ORIGIN,
+ args->total);
+ }
+
+ /* Set oring attribute flag. */
+ attr->flag |= ATTR_FLAG_BIT(BGP_ATTR_ORIGIN);
+
+ return 0;
+}
+
+/* Parse AS path information. This function is wrapper of
+ aspath_parse. */
+static int bgp_attr_aspath(struct bgp_attr_parser_args *args)
+{
+ struct attr *const attr = args->attr;
+ struct peer *const peer = args->peer;
+ const bgp_size_t length = args->length;
+
+ /*
+ * peer with AS4 => will get 4Byte ASnums
+ * otherwise, will get 16 Bit
+ */
+ attr->aspath = aspath_parse(
+ peer->curr, length,
+ CHECK_FLAG(peer->cap, PEER_CAP_AS4_RCV)
+ && CHECK_FLAG(peer->cap, PEER_CAP_AS4_ADV));
+
+ /* In case of IBGP, length will be zero. */
+ if (!attr->aspath) {
+ flog_err(EC_BGP_ATTR_MAL_AS_PATH,
+ "Malformed AS path from %s, length is %d", peer->host,
+ length);
+ return bgp_attr_malformed(args, BGP_NOTIFY_UPDATE_MAL_AS_PATH,
+ 0);
+ }
+
+ /* Conformant BGP speakers SHOULD NOT send BGP
+ * UPDATE messages containing AS_SET or AS_CONFED_SET. Upon receipt of
+ * such messages, conformant BGP speakers SHOULD use the "Treat-as-
+ * withdraw" error handling behavior as per [RFC7606].
+ */
+ if (peer->bgp->reject_as_sets && aspath_check_as_sets(attr->aspath)) {
+ flog_err(EC_BGP_ATTR_MAL_AS_PATH,
+ "AS_SET and AS_CONFED_SET are deprecated from %pBP",
+ peer);
+ return bgp_attr_malformed(args, BGP_NOTIFY_UPDATE_MAL_AS_PATH,
+ 0);
+ }
+
+ /* Set aspath attribute flag. */
+ attr->flag |= ATTR_FLAG_BIT(BGP_ATTR_AS_PATH);
+
+ return BGP_ATTR_PARSE_PROCEED;
+}
+
+static enum bgp_attr_parse_ret bgp_attr_aspath_check(struct peer *const peer,
+ struct attr *const attr)
+{
+ /* These checks were part of bgp_attr_aspath, but with
+ * as4 we should to check aspath things when
+ * aspath synthesizing with as4_path has already taken place.
+ * Otherwise we check ASPATH and use the synthesized thing, and that is
+ * not right.
+ * So do the checks later, i.e. here
+ */
+ struct aspath *aspath;
+
+ /* Refresh peer's type. If we set e.g.: AS_EXTERNAL/AS_INTERNAL,
+ * then peer->sort remains BGP_PEER_EBGP/IBGP, hence we need to
+ * have an actual type before checking.
+ * This is especially a case for BGP confederation peers, to avoid
+ * receiving and treating AS_PATH as malformed.
+ */
+ (void)peer_sort(peer);
+
+ /* Confederation sanity check. */
+ if ((peer->sort == BGP_PEER_CONFED
+ && !aspath_left_confed_check(attr->aspath))
+ || (peer->sort == BGP_PEER_EBGP
+ && aspath_confed_check(attr->aspath))) {
+ flog_err(EC_BGP_ATTR_MAL_AS_PATH, "Malformed AS path from %s",
+ peer->host);
+ return BGP_ATTR_PARSE_WITHDRAW;
+ }
+
+ /* First AS check for EBGP. */
+ if (CHECK_FLAG(peer->flags, PEER_FLAG_ENFORCE_FIRST_AS)) {
+ if (peer->sort == BGP_PEER_EBGP
+ && !aspath_firstas_check(attr->aspath, peer->as)) {
+ flog_err(EC_BGP_ATTR_FIRST_AS,
+ "%s incorrect first AS (must be %u)",
+ peer->host, peer->as);
+ return BGP_ATTR_PARSE_WITHDRAW;
+ }
+ }
+
+ /* Codification of AS 0 Processing */
+ if (peer->sort == BGP_PEER_EBGP && aspath_check_as_zero(attr->aspath)) {
+ flog_err(
+ EC_BGP_ATTR_MAL_AS_PATH,
+ "Malformed AS path, AS number is 0 in the path from %s",
+ peer->host);
+ return BGP_ATTR_PARSE_WITHDRAW;
+ }
+
+ /* local-as prepend */
+ if (peer->change_local_as
+ && !CHECK_FLAG(peer->flags, PEER_FLAG_LOCAL_AS_NO_PREPEND)) {
+ aspath = aspath_dup(attr->aspath);
+ aspath = aspath_add_seq(aspath, peer->change_local_as);
+ aspath_unintern(&attr->aspath);
+ attr->aspath = aspath_intern(aspath);
+ }
+
+ return BGP_ATTR_PARSE_PROCEED;
+}
+
+/* Parse AS4 path information. This function is another wrapper of
+ aspath_parse. */
+static int bgp_attr_as4_path(struct bgp_attr_parser_args *args,
+ struct aspath **as4_path)
+{
+ struct peer *const peer = args->peer;
+ struct attr *const attr = args->attr;
+ const bgp_size_t length = args->length;
+
+ *as4_path = aspath_parse(peer->curr, length, 1);
+
+ /* In case of IBGP, length will be zero. */
+ if (!*as4_path) {
+ flog_err(EC_BGP_ATTR_MAL_AS_PATH,
+ "Malformed AS4 path from %s, length is %d", peer->host,
+ length);
+ return bgp_attr_malformed(args, BGP_NOTIFY_UPDATE_MAL_AS_PATH,
+ 0);
+ }
+
+ /* Conformant BGP speakers SHOULD NOT send BGP
+ * UPDATE messages containing AS_SET or AS_CONFED_SET. Upon receipt of
+ * such messages, conformant BGP speakers SHOULD use the "Treat-as-
+ * withdraw" error handling behavior as per [RFC7606].
+ */
+ if (peer->bgp->reject_as_sets && aspath_check_as_sets(attr->aspath)) {
+ flog_err(EC_BGP_ATTR_MAL_AS_PATH,
+ "AS_SET and AS_CONFED_SET are deprecated from %pBP",
+ peer);
+ return bgp_attr_malformed(args, BGP_NOTIFY_UPDATE_MAL_AS_PATH,
+ 0);
+ }
+
+ /* Set aspath attribute flag. */
+ attr->flag |= ATTR_FLAG_BIT(BGP_ATTR_AS4_PATH);
+
+ return BGP_ATTR_PARSE_PROCEED;
+}
+
+/*
+ * Check that the nexthop attribute is valid.
+ */
+enum bgp_attr_parse_ret bgp_attr_nexthop_valid(struct peer *peer,
+ struct attr *attr)
+{
+ struct bgp *bgp = peer->bgp;
+
+ if (ipv4_martian(&attr->nexthop) && !bgp->allow_martian) {
+ uint8_t data[7]; /* type(2) + length(1) + nhop(4) */
+ char buf[INET_ADDRSTRLEN];
+
+ inet_ntop(AF_INET, &attr->nexthop.s_addr, buf,
+ INET_ADDRSTRLEN);
+ flog_err(EC_BGP_ATTR_MARTIAN_NH, "Martian nexthop %s",
+ buf);
+ data[0] = BGP_ATTR_FLAG_TRANS;
+ data[1] = BGP_ATTR_NEXT_HOP;
+ data[2] = BGP_ATTR_NHLEN_IPV4;
+ memcpy(&data[3], &attr->nexthop.s_addr, BGP_ATTR_NHLEN_IPV4);
+ bgp_notify_send_with_data(peer, BGP_NOTIFY_UPDATE_ERR,
+ BGP_NOTIFY_UPDATE_INVAL_NEXT_HOP,
+ data, 7);
+ return BGP_ATTR_PARSE_ERROR;
+ }
+
+ return BGP_ATTR_PARSE_PROCEED;
+}
+
+/* Nexthop attribute. */
+static enum bgp_attr_parse_ret
+bgp_attr_nexthop(struct bgp_attr_parser_args *args)
+{
+ struct peer *const peer = args->peer;
+ struct attr *const attr = args->attr;
+ const bgp_size_t length = args->length;
+
+ /* Check nexthop attribute length. */
+ if (length != 4) {
+ flog_err(EC_BGP_ATTR_LEN,
+ "Nexthop attribute length isn't four [%d]", length);
+
+ return bgp_attr_malformed(args, BGP_NOTIFY_UPDATE_ATTR_LENG_ERR,
+ args->total);
+ }
+
+ attr->nexthop.s_addr = stream_get_ipv4(peer->curr);
+ attr->flag |= ATTR_FLAG_BIT(BGP_ATTR_NEXT_HOP);
+
+ return BGP_ATTR_PARSE_PROCEED;
+}
+
+/* MED atrribute. */
+static enum bgp_attr_parse_ret bgp_attr_med(struct bgp_attr_parser_args *args)
+{
+ struct peer *const peer = args->peer;
+ struct attr *const attr = args->attr;
+ const bgp_size_t length = args->length;
+
+ /* Length check. */
+ if (length != 4) {
+ flog_err(EC_BGP_ATTR_LEN,
+ "MED attribute length isn't four [%d]", length);
+
+ return bgp_attr_malformed(args, BGP_NOTIFY_UPDATE_ATTR_LENG_ERR,
+ args->total);
+ }
+
+ attr->med = stream_getl(peer->curr);
+
+ attr->flag |= ATTR_FLAG_BIT(BGP_ATTR_MULTI_EXIT_DISC);
+
+ return BGP_ATTR_PARSE_PROCEED;
+}
+
+/* Local preference attribute. */
+static enum bgp_attr_parse_ret
+bgp_attr_local_pref(struct bgp_attr_parser_args *args)
+{
+ struct peer *const peer = args->peer;
+ struct attr *const attr = args->attr;
+ const bgp_size_t length = args->length;
+
+ /* if received from an internal neighbor, it SHALL be considered
+ * malformed if its length is not equal to 4. If malformed, the
+ * UPDATE message SHALL be handled using the approach of "treat-as-
+ * withdraw".
+ */
+ if (peer->sort == BGP_PEER_IBGP && length != 4) {
+ flog_err(EC_BGP_ATTR_LEN,
+ "LOCAL_PREF attribute length isn't 4 [%u]", length);
+ return bgp_attr_malformed(args, BGP_NOTIFY_UPDATE_ATTR_LENG_ERR,
+ args->total);
+ }
+
+ /* If it is contained in an UPDATE message that is received from an
+ external peer, then this attribute MUST be ignored by the
+ receiving speaker. */
+ if (peer->sort == BGP_PEER_EBGP) {
+ STREAM_FORWARD_GETP(peer->curr, length);
+ return BGP_ATTR_PARSE_PROCEED;
+ }
+
+ STREAM_GETL(peer->curr, attr->local_pref);
+
+ /* Set the local-pref flag. */
+ attr->flag |= ATTR_FLAG_BIT(BGP_ATTR_LOCAL_PREF);
+
+ return BGP_ATTR_PARSE_PROCEED;
+
+stream_failure:
+ return bgp_attr_malformed(args, BGP_NOTIFY_UPDATE_ATTR_LENG_ERR,
+ args->total);
+}
+
+/* Atomic aggregate. */
+static int bgp_attr_atomic(struct bgp_attr_parser_args *args)
+{
+ struct attr *const attr = args->attr;
+ const bgp_size_t length = args->length;
+
+ /* Length check. */
+ if (length != 0) {
+ flog_err(EC_BGP_ATTR_LEN,
+ "ATOMIC_AGGREGATE attribute length isn't 0 [%u]",
+ length);
+ return bgp_attr_malformed(args, BGP_NOTIFY_UPDATE_ATTR_LENG_ERR,
+ args->total);
+ }
+
+ /* Set atomic aggregate flag. */
+ attr->flag |= ATTR_FLAG_BIT(BGP_ATTR_ATOMIC_AGGREGATE);
+
+ return BGP_ATTR_PARSE_PROCEED;
+}
+
+/* Aggregator attribute */
+static int bgp_attr_aggregator(struct bgp_attr_parser_args *args)
+{
+ struct peer *const peer = args->peer;
+ struct attr *const attr = args->attr;
+ const bgp_size_t length = args->length;
+ as_t aggregator_as;
+
+ int wantedlen = 6;
+
+ /* peer with AS4 will send 4 Byte AS, peer without will send 2 Byte */
+ if (CHECK_FLAG(peer->cap, PEER_CAP_AS4_RCV)
+ && CHECK_FLAG(peer->cap, PEER_CAP_AS4_ADV))
+ wantedlen = 8;
+
+ if (length != wantedlen) {
+ flog_err(EC_BGP_ATTR_LEN,
+ "AGGREGATOR attribute length isn't %u [%u]", wantedlen,
+ length);
+ return bgp_attr_malformed(args, BGP_NOTIFY_UPDATE_ATTR_LENG_ERR,
+ args->total);
+ }
+
+ if (CHECK_FLAG(peer->cap, PEER_CAP_AS4_RCV))
+ aggregator_as = stream_getl(peer->curr);
+ else
+ aggregator_as = stream_getw(peer->curr);
+
+ attr->aggregator_as = aggregator_as;
+ attr->aggregator_addr.s_addr = stream_get_ipv4(peer->curr);
+
+ /* Codification of AS 0 Processing */
+ if (aggregator_as == BGP_AS_ZERO) {
+ flog_err(EC_BGP_ATTR_LEN,
+ "%s: AGGREGATOR AS number is 0 for aspath: %s",
+ peer->host, aspath_print(attr->aspath));
+
+ if (bgp_debug_update(peer, NULL, NULL, 1)) {
+ char attr_str[BUFSIZ] = {0};
+
+ bgp_dump_attr(attr, attr_str, sizeof(attr_str));
+
+ zlog_debug("%s: attributes: %s", __func__, attr_str);
+ }
+ } else {
+ attr->flag |= ATTR_FLAG_BIT(BGP_ATTR_AGGREGATOR);
+ }
+
+ return BGP_ATTR_PARSE_PROCEED;
+}
+
+/* New Aggregator attribute */
+static enum bgp_attr_parse_ret
+bgp_attr_as4_aggregator(struct bgp_attr_parser_args *args,
+ as_t *as4_aggregator_as,
+ struct in_addr *as4_aggregator_addr)
+{
+ struct peer *const peer = args->peer;
+ struct attr *const attr = args->attr;
+ const bgp_size_t length = args->length;
+ as_t aggregator_as;
+
+ if (length != 8) {
+ flog_err(EC_BGP_ATTR_LEN, "New Aggregator length is not 8 [%d]",
+ length);
+ return bgp_attr_malformed(args, BGP_NOTIFY_UPDATE_ATTR_LENG_ERR,
+ 0);
+ }
+
+ aggregator_as = stream_getl(peer->curr);
+
+ *as4_aggregator_as = aggregator_as;
+ as4_aggregator_addr->s_addr = stream_get_ipv4(peer->curr);
+
+ /* Codification of AS 0 Processing */
+ if (aggregator_as == BGP_AS_ZERO) {
+ flog_err(EC_BGP_ATTR_LEN,
+ "%s: AS4_AGGREGATOR AS number is 0 for aspath: %s",
+ peer->host, aspath_print(attr->aspath));
+
+ if (bgp_debug_update(peer, NULL, NULL, 1)) {
+ char attr_str[BUFSIZ] = {0};
+
+ bgp_dump_attr(attr, attr_str, sizeof(attr_str));
+
+ zlog_debug("%s: attributes: %s", __func__, attr_str);
+ }
+ } else {
+ attr->flag |= ATTR_FLAG_BIT(BGP_ATTR_AS4_AGGREGATOR);
+ }
+
+ return BGP_ATTR_PARSE_PROCEED;
+}
+
+/* Munge Aggregator and New-Aggregator, AS_PATH and NEW_AS_PATH.
+ */
+static enum bgp_attr_parse_ret
+bgp_attr_munge_as4_attrs(struct peer *const peer, struct attr *const attr,
+ struct aspath *as4_path, as_t as4_aggregator,
+ struct in_addr *as4_aggregator_addr)
+{
+ int ignore_as4_path = 0;
+ struct aspath *newpath;
+
+ if (!attr->aspath) {
+ /* NULL aspath shouldn't be possible as bgp_attr_parse should
+ * have
+ * checked that all well-known, mandatory attributes were
+ * present.
+ *
+ * Can only be a problem with peer itself - hard error
+ */
+ return BGP_ATTR_PARSE_ERROR;
+ }
+
+ if (CHECK_FLAG(peer->cap, PEER_CAP_AS4_RCV)) {
+ /* peer can do AS4, so we ignore AS4_PATH and AS4_AGGREGATOR
+ * if given.
+ * It is worth a warning though, because the peer really
+ * should not send them
+ */
+ if (BGP_DEBUG(as4, AS4)) {
+ if (attr->flag & (ATTR_FLAG_BIT(BGP_ATTR_AS4_PATH)))
+ zlog_debug("[AS4] %s %s AS4_PATH", peer->host,
+ "AS4 capable peer, yet it sent");
+
+ if (attr->flag
+ & (ATTR_FLAG_BIT(BGP_ATTR_AS4_AGGREGATOR)))
+ zlog_debug("[AS4] %s %s AS4_AGGREGATOR",
+ peer->host,
+ "AS4 capable peer, yet it sent");
+ }
+
+ return BGP_ATTR_PARSE_PROCEED;
+ }
+
+ /* We have a asn16 peer. First, look for AS4_AGGREGATOR
+ * because that may override AS4_PATH
+ */
+ if (attr->flag & (ATTR_FLAG_BIT(BGP_ATTR_AS4_AGGREGATOR))) {
+ if (attr->flag & (ATTR_FLAG_BIT(BGP_ATTR_AGGREGATOR))) {
+ /* received both.
+ * if the as_number in aggregator is not AS_TRANS,
+ * then AS4_AGGREGATOR and AS4_PATH shall be ignored
+ * and the Aggregator shall be taken as
+ * info on the aggregating node, and the AS_PATH
+ * shall be taken as the AS_PATH
+ * otherwise
+ * the Aggregator shall be ignored and the
+ * AS4_AGGREGATOR shall be taken as the
+ * Aggregating node and the AS_PATH is to be
+ * constructed "as in all other cases"
+ */
+ if (attr->aggregator_as != BGP_AS_TRANS) {
+ /* ignore */
+ if (BGP_DEBUG(as4, AS4))
+ zlog_debug(
+ "[AS4] %s BGP not AS4 capable peer send AGGREGATOR != AS_TRANS and AS4_AGGREGATOR, so ignore AS4_AGGREGATOR and AS4_PATH",
+ peer->host);
+ ignore_as4_path = 1;
+ } else {
+ /* "New_aggregator shall be taken as aggregator"
+ */
+ attr->aggregator_as = as4_aggregator;
+ attr->aggregator_addr.s_addr =
+ as4_aggregator_addr->s_addr;
+ }
+ } else {
+ /* We received a AS4_AGGREGATOR but no AGGREGATOR.
+ * That is bogus - but reading the conditions
+ * we have to handle AS4_AGGREGATOR as if it were
+ * AGGREGATOR in that case
+ */
+ if (BGP_DEBUG(as4, AS4))
+ zlog_debug(
+ "[AS4] %s BGP not AS4 capable peer send AS4_AGGREGATOR but no AGGREGATOR, will take it as if AGGREGATOR with AS_TRANS had been there",
+ peer->host);
+ attr->aggregator_as = as4_aggregator;
+ /* sweep it under the carpet and simulate a "good"
+ * AGGREGATOR */
+ attr->flag |= (ATTR_FLAG_BIT(BGP_ATTR_AGGREGATOR));
+ }
+ }
+
+ /* need to reconcile NEW_AS_PATH and AS_PATH */
+ if (!ignore_as4_path
+ && (attr->flag & (ATTR_FLAG_BIT(BGP_ATTR_AS4_PATH)))) {
+ newpath = aspath_reconcile_as4(attr->aspath, as4_path);
+ if (!newpath)
+ return BGP_ATTR_PARSE_ERROR;
+
+ aspath_unintern(&attr->aspath);
+ attr->aspath = aspath_intern(newpath);
+ }
+ return BGP_ATTR_PARSE_PROCEED;
+}
+
+/* Community attribute. */
+static enum bgp_attr_parse_ret
+bgp_attr_community(struct bgp_attr_parser_args *args)
+{
+ struct peer *const peer = args->peer;
+ struct attr *const attr = args->attr;
+ const bgp_size_t length = args->length;
+
+ if (length == 0) {
+ bgp_attr_set_community(attr, NULL);
+ return bgp_attr_malformed(args, BGP_NOTIFY_UPDATE_OPT_ATTR_ERR,
+ args->total);
+ }
+
+ bgp_attr_set_community(
+ attr,
+ community_parse((uint32_t *)stream_pnt(peer->curr), length));
+
+ /* XXX: fix community_parse to use stream API and remove this */
+ stream_forward_getp(peer->curr, length);
+
+ /* The Community attribute SHALL be considered malformed if its
+ * length is not a non-zero multiple of 4.
+ */
+ if (!bgp_attr_get_community(attr))
+ return bgp_attr_malformed(args, BGP_NOTIFY_UPDATE_OPT_ATTR_ERR,
+ args->total);
+
+ return BGP_ATTR_PARSE_PROCEED;
+}
+
+/* Originator ID attribute. */
+static enum bgp_attr_parse_ret
+bgp_attr_originator_id(struct bgp_attr_parser_args *args)
+{
+ struct peer *const peer = args->peer;
+ struct attr *const attr = args->attr;
+ const bgp_size_t length = args->length;
+
+ /* if received from an internal neighbor, it SHALL be considered
+ * malformed if its length is not equal to 4. If malformed, the
+ * UPDATE message SHALL be handled using the approach of "treat-as-
+ * withdraw".
+ */
+ if (length != 4) {
+ flog_err(EC_BGP_ATTR_LEN, "Bad originator ID length %d",
+ length);
+
+ return bgp_attr_malformed(args, BGP_NOTIFY_UPDATE_ATTR_LENG_ERR,
+ args->total);
+ }
+
+ attr->originator_id.s_addr = stream_get_ipv4(peer->curr);
+
+ attr->flag |= ATTR_FLAG_BIT(BGP_ATTR_ORIGINATOR_ID);
+
+ return BGP_ATTR_PARSE_PROCEED;
+}
+
+/* Cluster list attribute. */
+static enum bgp_attr_parse_ret
+bgp_attr_cluster_list(struct bgp_attr_parser_args *args)
+{
+ struct peer *const peer = args->peer;
+ struct attr *const attr = args->attr;
+ const bgp_size_t length = args->length;
+
+ /* if received from an internal neighbor, it SHALL be considered
+ * malformed if its length is not a non-zero multiple of 4. If
+ * malformed, the UPDATE message SHALL be handled using the approach
+ * of "treat-as-withdraw".
+ */
+ if (length == 0 || length % 4) {
+ flog_err(EC_BGP_ATTR_LEN, "Bad cluster list length %d", length);
+
+ return bgp_attr_malformed(args, BGP_NOTIFY_UPDATE_ATTR_LENG_ERR,
+ args->total);
+ }
+
+ bgp_attr_set_cluster(
+ attr, cluster_parse((struct in_addr *)stream_pnt(peer->curr),
+ length));
+
+ /* XXX: Fix cluster_parse to use stream API and then remove this */
+ stream_forward_getp(peer->curr, length);
+
+ attr->flag |= ATTR_FLAG_BIT(BGP_ATTR_CLUSTER_LIST);
+
+ return BGP_ATTR_PARSE_PROCEED;
+}
+
+/* Multiprotocol reachability information parse. */
+int bgp_mp_reach_parse(struct bgp_attr_parser_args *args,
+ struct bgp_nlri *mp_update)
+{
+ iana_afi_t pkt_afi;
+ afi_t afi;
+ iana_safi_t pkt_safi;
+ safi_t safi;
+ bgp_size_t nlri_len;
+ size_t start;
+ struct stream *s;
+ struct peer *const peer = args->peer;
+ struct attr *const attr = args->attr;
+ const bgp_size_t length = args->length;
+
+ /* Set end of packet. */
+ s = BGP_INPUT(peer);
+ start = stream_get_getp(s);
+
+/* safe to read statically sized header? */
+#define BGP_MP_REACH_MIN_SIZE 5
+#define LEN_LEFT (length - (stream_get_getp(s) - start))
+ if ((length > STREAM_READABLE(s)) || (length < BGP_MP_REACH_MIN_SIZE)) {
+ zlog_info("%s: %s sent invalid length, %lu, of MP_REACH_NLRI",
+ __func__, peer->host, (unsigned long)length);
+ return BGP_ATTR_PARSE_ERROR_NOTIFYPLS;
+ }
+
+ /* Load AFI, SAFI. */
+ pkt_afi = stream_getw(s);
+ pkt_safi = stream_getc(s);
+
+ /* Convert AFI, SAFI to internal values, check. */
+ if (bgp_map_afi_safi_iana2int(pkt_afi, pkt_safi, &afi, &safi)) {
+ /* Log if AFI or SAFI is unrecognized. This is not an error
+ * unless
+ * the attribute is otherwise malformed.
+ */
+ if (bgp_debug_update(peer, NULL, NULL, 0))
+ zlog_debug(
+ "%s sent unrecognizable AFI, %s or, SAFI, %s, of MP_REACH_NLRI",
+ peer->host, iana_afi2str(pkt_afi),
+ iana_safi2str(pkt_safi));
+ return BGP_ATTR_PARSE_ERROR;
+ }
+
+ /* Get nexthop length. */
+ attr->mp_nexthop_len = stream_getc(s);
+
+ if (LEN_LEFT < attr->mp_nexthop_len) {
+ zlog_info(
+ "%s: %s sent next-hop length, %u, in MP_REACH_NLRI which goes past the end of attribute",
+ __func__, peer->host, attr->mp_nexthop_len);
+ return BGP_ATTR_PARSE_ERROR_NOTIFYPLS;
+ }
+
+ /* Nexthop length check. */
+ switch (attr->mp_nexthop_len) {
+ case 0:
+ if (safi != SAFI_FLOWSPEC) {
+ zlog_info("%s: %s sent wrong next-hop length, %d, in MP_REACH_NLRI",
+ __func__, peer->host, attr->mp_nexthop_len);
+ return BGP_ATTR_PARSE_ERROR_NOTIFYPLS;
+ }
+ break;
+ case BGP_ATTR_NHLEN_VPNV4:
+ stream_getl(s); /* RD high */
+ stream_getl(s); /* RD low */
+ /*
+ * NOTE: intentional fall through
+ * - for consistency in rx processing
+ *
+ * The following comment is to signal GCC this intention
+ * and suppress the warning
+ */
+ /* FALLTHRU */
+ case BGP_ATTR_NHLEN_IPV4:
+ stream_get(&attr->mp_nexthop_global_in, s, IPV4_MAX_BYTELEN);
+ /* Probably needed for RFC 2283 */
+ if (attr->nexthop.s_addr == INADDR_ANY)
+ memcpy(&attr->nexthop.s_addr,
+ &attr->mp_nexthop_global_in, IPV4_MAX_BYTELEN);
+ break;
+ case BGP_ATTR_NHLEN_IPV6_GLOBAL:
+ case BGP_ATTR_NHLEN_VPNV6_GLOBAL:
+ if (attr->mp_nexthop_len == BGP_ATTR_NHLEN_VPNV6_GLOBAL) {
+ stream_getl(s); /* RD high */
+ stream_getl(s); /* RD low */
+ }
+ stream_get(&attr->mp_nexthop_global, s, IPV6_MAX_BYTELEN);
+ if (IN6_IS_ADDR_LINKLOCAL(&attr->mp_nexthop_global)) {
+ if (!peer->nexthop.ifp) {
+ zlog_warn("%s sent a v6 global attribute but address is a V6 LL and there's no peer interface information. Hence, withdrawing",
+ peer->host);
+ return BGP_ATTR_PARSE_WITHDRAW;
+ }
+ attr->nh_ifindex = peer->nexthop.ifp->ifindex;
+ }
+ break;
+ case BGP_ATTR_NHLEN_IPV6_GLOBAL_AND_LL:
+ case BGP_ATTR_NHLEN_VPNV6_GLOBAL_AND_LL:
+ if (attr->mp_nexthop_len
+ == BGP_ATTR_NHLEN_VPNV6_GLOBAL_AND_LL) {
+ stream_getl(s); /* RD high */
+ stream_getl(s); /* RD low */
+ }
+ stream_get(&attr->mp_nexthop_global, s, IPV6_MAX_BYTELEN);
+ if (IN6_IS_ADDR_LINKLOCAL(&attr->mp_nexthop_global)) {
+ if (!peer->nexthop.ifp) {
+ zlog_warn("%s sent a v6 global and LL attribute but global address is a V6 LL and there's no peer interface information. Hence, withdrawing",
+ peer->host);
+ return BGP_ATTR_PARSE_WITHDRAW;
+ }
+ attr->nh_ifindex = peer->nexthop.ifp->ifindex;
+ }
+ if (attr->mp_nexthop_len
+ == BGP_ATTR_NHLEN_VPNV6_GLOBAL_AND_LL) {
+ stream_getl(s); /* RD high */
+ stream_getl(s); /* RD low */
+ }
+ stream_get(&attr->mp_nexthop_local, s, IPV6_MAX_BYTELEN);
+ if (!IN6_IS_ADDR_LINKLOCAL(&attr->mp_nexthop_local)) {
+ if (bgp_debug_update(peer, NULL, NULL, 1))
+ zlog_debug(
+ "%s sent next-hops %pI6 and %pI6. Ignoring non-LL value",
+ peer->host, &attr->mp_nexthop_global,
+ &attr->mp_nexthop_local);
+
+ attr->mp_nexthop_len = IPV6_MAX_BYTELEN;
+ }
+ if (!peer->nexthop.ifp) {
+ zlog_warn("%s sent a v6 LL next-hop and there's no peer interface information. Hence, withdrawing",
+ peer->host);
+ return BGP_ATTR_PARSE_WITHDRAW;
+ }
+ attr->nh_lla_ifindex = peer->nexthop.ifp->ifindex;
+ break;
+ default:
+ zlog_info("%s: %s sent wrong next-hop length, %d, in MP_REACH_NLRI",
+ __func__, peer->host, attr->mp_nexthop_len);
+ return BGP_ATTR_PARSE_ERROR_NOTIFYPLS;
+ }
+
+ if (!LEN_LEFT) {
+ zlog_info("%s: %s sent SNPA which couldn't be read",
+ __func__, peer->host);
+ return BGP_ATTR_PARSE_ERROR_NOTIFYPLS;
+ }
+
+ {
+ uint8_t val;
+ if ((val = stream_getc(s)))
+ flog_warn(
+ EC_BGP_DEFUNCT_SNPA_LEN,
+ "%s sent non-zero value, %u, for defunct SNPA-length field",
+ peer->host, val);
+ }
+
+ /* must have nrli_len, what is left of the attribute */
+ nlri_len = LEN_LEFT;
+ if (nlri_len > STREAM_READABLE(s)) {
+ zlog_info("%s: %s sent MP_REACH_NLRI which couldn't be read",
+ __func__, peer->host);
+ return BGP_ATTR_PARSE_ERROR_NOTIFYPLS;
+ }
+
+ if (!nlri_len) {
+ zlog_info("%s: %s sent a zero-length NLRI. Hence, treating as a EOR marker",
+ __func__, peer->host);
+
+ mp_update->afi = afi;
+ mp_update->safi = safi;
+ return BGP_ATTR_PARSE_EOR;
+ }
+
+ mp_update->afi = afi;
+ mp_update->safi = safi;
+ mp_update->nlri = stream_pnt(s);
+ mp_update->length = nlri_len;
+
+ stream_forward_getp(s, nlri_len);
+
+ attr->flag |= ATTR_FLAG_BIT(BGP_ATTR_MP_REACH_NLRI);
+
+ return BGP_ATTR_PARSE_PROCEED;
+#undef LEN_LEFT
+}
+
+/* Multiprotocol unreachable parse */
+int bgp_mp_unreach_parse(struct bgp_attr_parser_args *args,
+ struct bgp_nlri *mp_withdraw)
+{
+ struct stream *s;
+ iana_afi_t pkt_afi;
+ afi_t afi;
+ iana_safi_t pkt_safi;
+ safi_t safi;
+ uint16_t withdraw_len;
+ struct peer *const peer = args->peer;
+ struct attr *const attr = args->attr;
+ const bgp_size_t length = args->length;
+
+ s = peer->curr;
+
+#define BGP_MP_UNREACH_MIN_SIZE 3
+ if ((length > STREAM_READABLE(s)) || (length < BGP_MP_UNREACH_MIN_SIZE))
+ return BGP_ATTR_PARSE_ERROR_NOTIFYPLS;
+
+ pkt_afi = stream_getw(s);
+ pkt_safi = stream_getc(s);
+
+ /* Convert AFI, SAFI to internal values, check. */
+ if (bgp_map_afi_safi_iana2int(pkt_afi, pkt_safi, &afi, &safi)) {
+ /* Log if AFI or SAFI is unrecognized. This is not an error
+ * unless
+ * the attribute is otherwise malformed.
+ */
+ if (bgp_debug_update(peer, NULL, NULL, 0))
+ zlog_debug(
+ "%s: MP_UNREACH received AFI %s or SAFI %s is unrecognized",
+ peer->host, iana_afi2str(pkt_afi),
+ iana_safi2str(pkt_safi));
+ return BGP_ATTR_PARSE_ERROR;
+ }
+
+ withdraw_len = length - BGP_MP_UNREACH_MIN_SIZE;
+
+ mp_withdraw->afi = afi;
+ mp_withdraw->safi = safi;
+ mp_withdraw->nlri = stream_pnt(s);
+ mp_withdraw->length = withdraw_len;
+
+ stream_forward_getp(s, withdraw_len);
+
+ attr->flag |= ATTR_FLAG_BIT(BGP_ATTR_MP_UNREACH_NLRI);
+
+ return BGP_ATTR_PARSE_PROCEED;
+}
+
+/* Large Community attribute. */
+static enum bgp_attr_parse_ret
+bgp_attr_large_community(struct bgp_attr_parser_args *args)
+{
+ struct peer *const peer = args->peer;
+ struct attr *const attr = args->attr;
+ const bgp_size_t length = args->length;
+
+ /*
+ * Large community follows new attribute format.
+ */
+ if (length == 0) {
+ bgp_attr_set_lcommunity(attr, NULL);
+ /* Empty extcomm doesn't seem to be invalid per se */
+ return bgp_attr_malformed(args, BGP_NOTIFY_UPDATE_OPT_ATTR_ERR,
+ args->total);
+ }
+
+ bgp_attr_set_lcommunity(
+ attr, lcommunity_parse(stream_pnt(peer->curr), length));
+ /* XXX: fix ecommunity_parse to use stream API */
+ stream_forward_getp(peer->curr, length);
+
+ if (!bgp_attr_get_lcommunity(attr))
+ return bgp_attr_malformed(args, BGP_NOTIFY_UPDATE_OPT_ATTR_ERR,
+ args->total);
+
+ return BGP_ATTR_PARSE_PROCEED;
+}
+
+/* Extended Community attribute. */
+static enum bgp_attr_parse_ret
+bgp_attr_ext_communities(struct bgp_attr_parser_args *args)
+{
+ struct peer *const peer = args->peer;
+ struct attr *const attr = args->attr;
+ const bgp_size_t length = args->length;
+ uint8_t sticky = 0;
+ bool proxy = false;
+ struct ecommunity *ecomm;
+
+ if (length == 0) {
+ bgp_attr_set_ecommunity(attr, NULL);
+ /* Empty extcomm doesn't seem to be invalid per se */
+ return bgp_attr_malformed(args, BGP_NOTIFY_UPDATE_OPT_ATTR_ERR,
+ args->total);
+ }
+
+ ecomm = ecommunity_parse(
+ stream_pnt(peer->curr), length,
+ CHECK_FLAG(peer->flags,
+ PEER_FLAG_DISABLE_LINK_BW_ENCODING_IEEE));
+ bgp_attr_set_ecommunity(attr, ecomm);
+ /* XXX: fix ecommunity_parse to use stream API */
+ stream_forward_getp(peer->curr, length);
+
+ /* The Extended Community attribute SHALL be considered malformed if
+ * its length is not a non-zero multiple of 8.
+ */
+ if (!bgp_attr_get_ecommunity(attr))
+ return bgp_attr_malformed(args, BGP_NOTIFY_UPDATE_OPT_ATTR_ERR,
+ args->total);
+
+ /* Extract DF election preference and mobility sequence number */
+ attr->df_pref = bgp_attr_df_pref_from_ec(attr, &attr->df_alg);
+
+ /* Extract MAC mobility sequence number, if any. */
+ attr->mm_seqnum = bgp_attr_mac_mobility_seqnum(attr, &sticky);
+ attr->sticky = sticky;
+
+ /* Check if this is a Gateway MAC-IP advertisement */
+ attr->default_gw = bgp_attr_default_gw(attr);
+
+ /* Handle scenario where router flag ecommunity is not
+ * set but default gw ext community is present.
+ * Use default gateway, set and propogate R-bit.
+ */
+ if (attr->default_gw)
+ attr->router_flag = 1;
+
+ /* Check EVPN Neighbor advertisement flags, R-bit */
+ bgp_attr_evpn_na_flag(attr, &attr->router_flag, &proxy);
+ if (proxy)
+ attr->es_flags |= ATTR_ES_PROXY_ADVERT;
+
+ /* Extract the Rmac, if any */
+ if (bgp_attr_rmac(attr, &attr->rmac)) {
+ if (bgp_debug_update(peer, NULL, NULL, 1)
+ && bgp_mac_exist(&attr->rmac))
+ zlog_debug("%s: router mac %pEA is self mac", __func__,
+ &attr->rmac);
+ }
+
+ /* Get the tunnel type from encap extended community */
+ bgp_attr_extcom_tunnel_type(attr,
+ (bgp_encap_types *)&attr->encap_tunneltype);
+
+ /* Extract link bandwidth, if any. */
+ (void)ecommunity_linkbw_present(bgp_attr_get_ecommunity(attr),
+ &attr->link_bw);
+
+ return BGP_ATTR_PARSE_PROCEED;
+}
+
+/* IPv6 Extended Community attribute. */
+static enum bgp_attr_parse_ret
+bgp_attr_ipv6_ext_communities(struct bgp_attr_parser_args *args)
+{
+ struct peer *const peer = args->peer;
+ struct attr *const attr = args->attr;
+ const bgp_size_t length = args->length;
+ struct ecommunity *ipv6_ecomm = NULL;
+
+ if (length == 0) {
+ bgp_attr_set_ipv6_ecommunity(attr, ipv6_ecomm);
+ return bgp_attr_malformed(args, BGP_NOTIFY_UPDATE_OPT_ATTR_ERR,
+ args->total);
+ }
+
+ ipv6_ecomm = ecommunity_parse_ipv6(
+ stream_pnt(peer->curr), length,
+ CHECK_FLAG(peer->flags,
+ PEER_FLAG_DISABLE_LINK_BW_ENCODING_IEEE));
+ bgp_attr_set_ipv6_ecommunity(attr, ipv6_ecomm);
+
+ /* XXX: fix ecommunity_parse to use stream API */
+ stream_forward_getp(peer->curr, length);
+
+ if (!ipv6_ecomm)
+ return bgp_attr_malformed(args, BGP_NOTIFY_UPDATE_OPT_ATTR_ERR,
+ args->total);
+
+ return BGP_ATTR_PARSE_PROCEED;
+}
+
+/* Parse Tunnel Encap attribute in an UPDATE */
+static int bgp_attr_encap(uint8_t type, struct peer *peer, /* IN */
+ bgp_size_t length, /* IN: attr's length field */
+ struct attr *attr, /* IN: caller already allocated */
+ uint8_t flag, /* IN: attr's flags field */
+ uint8_t *startp)
+{
+ bgp_size_t total;
+ uint16_t tunneltype = 0;
+
+ total = length + (CHECK_FLAG(flag, BGP_ATTR_FLAG_EXTLEN) ? 4 : 3);
+
+ if (!CHECK_FLAG(flag, BGP_ATTR_FLAG_TRANS)
+ || !CHECK_FLAG(flag, BGP_ATTR_FLAG_OPTIONAL)) {
+ zlog_info(
+ "Tunnel Encap attribute flag isn't optional and transitive %d",
+ flag);
+ bgp_notify_send_with_data(peer, BGP_NOTIFY_UPDATE_ERR,
+ BGP_NOTIFY_UPDATE_ATTR_FLAG_ERR,
+ startp, total);
+ return -1;
+ }
+
+ if (BGP_ATTR_ENCAP == type) {
+ /* read outer TLV type and length */
+ uint16_t tlv_length;
+
+ if (length < 4) {
+ zlog_info(
+ "Tunnel Encap attribute not long enough to contain outer T,L");
+ bgp_notify_send_with_data(
+ peer, BGP_NOTIFY_UPDATE_ERR,
+ BGP_NOTIFY_UPDATE_OPT_ATTR_ERR, startp, total);
+ return -1;
+ }
+ tunneltype = stream_getw(BGP_INPUT(peer));
+ tlv_length = stream_getw(BGP_INPUT(peer));
+ length -= 4;
+
+ if (tlv_length != length) {
+ zlog_info("%s: tlv_length(%d) != length(%d)",
+ __func__, tlv_length, length);
+ }
+ }
+
+ while (length >= 4) {
+ uint16_t subtype = 0;
+ uint16_t sublength = 0;
+ struct bgp_attr_encap_subtlv *tlv;
+
+ if (BGP_ATTR_ENCAP == type) {
+ subtype = stream_getc(BGP_INPUT(peer));
+ sublength = stream_getc(BGP_INPUT(peer));
+ length -= 2;
+#ifdef ENABLE_BGP_VNC
+ } else {
+ subtype = stream_getw(BGP_INPUT(peer));
+ sublength = stream_getw(BGP_INPUT(peer));
+ length -= 4;
+#endif
+ }
+
+ if (sublength > length) {
+ zlog_info(
+ "Tunnel Encap attribute sub-tlv length %d exceeds remaining length %d",
+ sublength, length);
+ bgp_notify_send_with_data(
+ peer, BGP_NOTIFY_UPDATE_ERR,
+ BGP_NOTIFY_UPDATE_OPT_ATTR_ERR, startp, total);
+ return -1;
+ }
+
+ /* alloc and copy sub-tlv */
+ /* TBD make sure these are freed when attributes are released */
+ tlv = XCALLOC(MTYPE_ENCAP_TLV,
+ sizeof(struct bgp_attr_encap_subtlv) + sublength);
+ tlv->type = subtype;
+ tlv->length = sublength;
+ stream_get(tlv->value, peer->curr, sublength);
+ length -= sublength;
+
+ /* attach tlv to encap chain */
+ if (BGP_ATTR_ENCAP == type) {
+ struct bgp_attr_encap_subtlv *stlv_last;
+ for (stlv_last = attr->encap_subtlvs;
+ stlv_last && stlv_last->next;
+ stlv_last = stlv_last->next)
+ ;
+ if (stlv_last) {
+ stlv_last->next = tlv;
+ } else {
+ attr->encap_subtlvs = tlv;
+ }
+#ifdef ENABLE_BGP_VNC
+ } else {
+ struct bgp_attr_encap_subtlv *stlv_last;
+ struct bgp_attr_encap_subtlv *vnc_subtlvs =
+ bgp_attr_get_vnc_subtlvs(attr);
+
+ for (stlv_last = vnc_subtlvs;
+ stlv_last && stlv_last->next;
+ stlv_last = stlv_last->next)
+ ;
+ if (stlv_last)
+ stlv_last->next = tlv;
+ else
+ bgp_attr_set_vnc_subtlvs(attr, tlv);
+#endif
+ }
+ }
+
+ if (BGP_ATTR_ENCAP == type) {
+ attr->encap_tunneltype = tunneltype;
+ }
+
+ if (length) {
+ /* spurious leftover data */
+ zlog_info(
+ "Tunnel Encap attribute length is bad: %d leftover octets",
+ length);
+ bgp_notify_send_with_data(peer, BGP_NOTIFY_UPDATE_ERR,
+ BGP_NOTIFY_UPDATE_OPT_ATTR_ERR,
+ startp, total);
+ return -1;
+ }
+
+ return 0;
+}
+
+
+/* SRv6 Service Data Sub-Sub-TLV attribute
+ * draft-ietf-bess-srv6-services-07
+ */
+static enum bgp_attr_parse_ret
+bgp_attr_srv6_service_data(struct bgp_attr_parser_args *args)
+{
+ struct peer *const peer = args->peer;
+ struct attr *const attr = args->attr;
+ uint8_t type, loc_block_len, loc_node_len, func_len, arg_len,
+ transposition_len, transposition_offset;
+ uint16_t length;
+ size_t headersz = sizeof(type) + sizeof(length);
+
+ if (STREAM_READABLE(peer->curr) < headersz) {
+ flog_err(
+ EC_BGP_ATTR_LEN,
+ "Malformed SRv6 Service Data Sub-Sub-TLV attribute - insufficent data (need %zu for attribute header, have %zu remaining in UPDATE)",
+ headersz, STREAM_READABLE(peer->curr));
+ return bgp_attr_malformed(args, BGP_NOTIFY_UPDATE_ATTR_LENG_ERR,
+ args->total);
+ }
+
+ type = stream_getc(peer->curr);
+ length = stream_getw(peer->curr);
+
+ if (STREAM_READABLE(peer->curr) < length) {
+ flog_err(
+ EC_BGP_ATTR_LEN,
+ "Malformed SRv6 Service Data Sub-Sub-TLV attribute - insufficent data (need %hu for attribute data, have %zu remaining in UPDATE)",
+ length, STREAM_READABLE(peer->curr));
+ return bgp_attr_malformed(args, BGP_NOTIFY_UPDATE_ATTR_LENG_ERR,
+ args->total);
+ }
+
+ if (length < BGP_PREFIX_SID_SRV6_L3_SERVICE_SID_STRUCTURE_LENGTH) {
+ flog_err(
+ EC_BGP_ATTR_LEN,
+ "Malformed SRv6 Service Data Sub-Sub-TLV attribute - insufficient data (need %u, have %hu remaining in UPDATE)",
+ BGP_PREFIX_SID_SRV6_L3_SERVICE_SID_STRUCTURE_LENGTH,
+ length);
+ return bgp_attr_malformed(args, BGP_NOTIFY_UPDATE_ATTR_LENG_ERR,
+ args->total);
+ }
+
+ if (type == BGP_PREFIX_SID_SRV6_L3_SERVICE_SID_STRUCTURE) {
+ loc_block_len = stream_getc(peer->curr);
+ loc_node_len = stream_getc(peer->curr);
+ func_len = stream_getc(peer->curr);
+ arg_len = stream_getc(peer->curr);
+ transposition_len = stream_getc(peer->curr);
+ transposition_offset = stream_getc(peer->curr);
+
+ /* Log SRv6 Service Data Sub-Sub-TLV */
+ if (BGP_DEBUG(vpn, VPN_LEAK_LABEL)) {
+ zlog_debug(
+ "%s: srv6-l3-srv-data loc-block-len=%u, loc-node-len=%u func-len=%u, arg-len=%u, transposition-len=%u, transposition-offset=%u",
+ __func__, loc_block_len, loc_node_len, func_len,
+ arg_len, transposition_len,
+ transposition_offset);
+ }
+
+ attr->srv6_l3vpn->loc_block_len = loc_block_len;
+ attr->srv6_l3vpn->loc_node_len = loc_node_len;
+ attr->srv6_l3vpn->func_len = func_len;
+ attr->srv6_l3vpn->arg_len = arg_len;
+ attr->srv6_l3vpn->transposition_len = transposition_len;
+ attr->srv6_l3vpn->transposition_offset = transposition_offset;
+ }
+
+ else {
+ if (bgp_debug_update(peer, NULL, NULL, 1))
+ zlog_debug(
+ "%s attr SRv6 Service Data Sub-Sub-TLV sub-sub-type=%u is not supported, skipped",
+ peer->host, type);
+
+ stream_forward_getp(peer->curr, length);
+ }
+
+ return BGP_ATTR_PARSE_PROCEED;
+}
+
+/* SRv6 Service Sub-TLV attribute
+ * draft-ietf-bess-srv6-services-07
+ */
+static enum bgp_attr_parse_ret
+bgp_attr_srv6_service(struct bgp_attr_parser_args *args)
+{
+ struct peer *const peer = args->peer;
+ struct attr *const attr = args->attr;
+ struct in6_addr ipv6_sid;
+ uint8_t type, sid_flags;
+ uint16_t length, endpoint_behavior;
+ size_t headersz = sizeof(type) + sizeof(length);
+ enum bgp_attr_parse_ret err;
+ char buf[BUFSIZ];
+
+ if (STREAM_READABLE(peer->curr) < headersz) {
+ flog_err(
+ EC_BGP_ATTR_LEN,
+ "Malformed SRv6 Service Sub-TLV attribute - insufficent data (need %zu for attribute header, have %zu remaining in UPDATE)",
+ headersz, STREAM_READABLE(peer->curr));
+ return bgp_attr_malformed(args, BGP_NOTIFY_UPDATE_ATTR_LENG_ERR,
+ args->total);
+ }
+
+ type = stream_getc(peer->curr);
+ length = stream_getw(peer->curr);
+
+ if (STREAM_READABLE(peer->curr) < length) {
+ flog_err(
+ EC_BGP_ATTR_LEN,
+ "Malformed SRv6 Service Sub-TLV attribute - insufficent data (need %hu for attribute data, have %zu remaining in UPDATE)",
+ length, STREAM_READABLE(peer->curr));
+ return bgp_attr_malformed(args, BGP_NOTIFY_UPDATE_ATTR_LENG_ERR,
+ args->total);
+ }
+
+ if (type == BGP_PREFIX_SID_SRV6_L3_SERVICE_SID_INFO) {
+ stream_getc(peer->curr);
+ stream_get(&ipv6_sid, peer->curr, sizeof(ipv6_sid));
+ sid_flags = stream_getc(peer->curr);
+ endpoint_behavior = stream_getw(peer->curr);
+ stream_getc(peer->curr);
+
+ /* Log SRv6 Service Sub-TLV */
+ if (BGP_DEBUG(vpn, VPN_LEAK_LABEL)) {
+ inet_ntop(AF_INET6, &ipv6_sid, buf, sizeof(buf));
+ zlog_debug(
+ "%s: srv6-l3-srv sid %s, sid-flags 0x%02x, end-behaviour 0x%04x",
+ __func__, buf, sid_flags, endpoint_behavior);
+ }
+
+ /* Configure from Info */
+ if (attr->srv6_l3vpn) {
+ flog_err(EC_BGP_ATTRIBUTE_REPEATED,
+ "Prefix SID SRv6 L3VPN field repeated");
+ return bgp_attr_malformed(
+ args, BGP_NOTIFY_UPDATE_MAL_ATTR, args->total);
+ }
+ attr->srv6_l3vpn = XCALLOC(MTYPE_BGP_SRV6_L3VPN,
+ sizeof(struct bgp_attr_srv6_l3vpn));
+ sid_copy(&attr->srv6_l3vpn->sid, &ipv6_sid);
+ attr->srv6_l3vpn->sid_flags = sid_flags;
+ attr->srv6_l3vpn->endpoint_behavior = endpoint_behavior;
+ attr->srv6_l3vpn->loc_block_len = 0;
+ attr->srv6_l3vpn->loc_node_len = 0;
+ attr->srv6_l3vpn->func_len = 0;
+ attr->srv6_l3vpn->arg_len = 0;
+ attr->srv6_l3vpn->transposition_len = 0;
+ attr->srv6_l3vpn->transposition_offset = 0;
+
+ // Sub-Sub-TLV found
+ if (length > BGP_PREFIX_SID_SRV6_L3_SERVICE_SID_INFO_LENGTH) {
+ err = bgp_attr_srv6_service_data(args);
+
+ if (err != BGP_ATTR_PARSE_PROCEED)
+ return err;
+ }
+
+ attr->srv6_l3vpn = srv6_l3vpn_intern(attr->srv6_l3vpn);
+ }
+
+ /* Placeholder code for unsupported type */
+ else {
+ if (bgp_debug_update(peer, NULL, NULL, 1))
+ zlog_debug(
+ "%s attr SRv6 Service Sub-TLV sub-type=%u is not supported, skipped",
+ peer->host, type);
+
+ stream_forward_getp(peer->curr, length);
+ }
+
+ return BGP_ATTR_PARSE_PROCEED;
+}
+
+/*
+ * Read an individual SID value returning how much data we have read
+ * Returns 0 if there was an error that needs to be passed up the stack
+ */
+static enum bgp_attr_parse_ret
+bgp_attr_psid_sub(uint8_t type, uint16_t length,
+ struct bgp_attr_parser_args *args)
+{
+ struct peer *const peer = args->peer;
+ struct attr *const attr = args->attr;
+ uint32_t label_index;
+ struct in6_addr ipv6_sid;
+ uint32_t srgb_base;
+ uint32_t srgb_range;
+ int srgb_count;
+ uint8_t sid_type, sid_flags;
+ char buf[BUFSIZ];
+
+ /*
+ * Check that we actually have at least as much data as
+ * specified by the length field
+ */
+ if (STREAM_READABLE(peer->curr) < length) {
+ flog_err(
+ EC_BGP_ATTR_LEN,
+ "Prefix SID specifies length %hu, but only %zu bytes remain",
+ length, STREAM_READABLE(peer->curr));
+ return bgp_attr_malformed(args, BGP_NOTIFY_UPDATE_ATTR_LENG_ERR,
+ args->total);
+ }
+
+ if (type == BGP_PREFIX_SID_LABEL_INDEX) {
+ if (length != BGP_PREFIX_SID_LABEL_INDEX_LENGTH) {
+ flog_err(EC_BGP_ATTR_LEN,
+ "Prefix SID label index length is %hu instead of %u",
+ length, BGP_PREFIX_SID_LABEL_INDEX_LENGTH);
+ return bgp_attr_malformed(args,
+ BGP_NOTIFY_UPDATE_ATTR_LENG_ERR,
+ args->total);
+ }
+
+ /* Ignore flags and reserved */
+ stream_getc(peer->curr);
+ stream_getw(peer->curr);
+
+ /* Fetch the label index and see if it is valid. */
+ label_index = stream_getl(peer->curr);
+ if (label_index == BGP_INVALID_LABEL_INDEX)
+ return bgp_attr_malformed(args, BGP_NOTIFY_UPDATE_OPT_ATTR_ERR,
+ args->total);
+
+ /* Store label index; subsequently, we'll check on
+ * address-family */
+ attr->label_index = label_index;
+ } else if (type == BGP_PREFIX_SID_IPV6) {
+ if (length != BGP_PREFIX_SID_IPV6_LENGTH) {
+ flog_err(EC_BGP_ATTR_LEN,
+ "Prefix SID IPv6 length is %hu instead of %u",
+ length, BGP_PREFIX_SID_IPV6_LENGTH);
+ return bgp_attr_malformed(args,
+ BGP_NOTIFY_UPDATE_ATTR_LENG_ERR,
+ args->total);
+ }
+
+ /* Ignore reserved */
+ stream_getc(peer->curr);
+ stream_getw(peer->curr);
+
+ stream_get(&ipv6_sid, peer->curr, 16);
+ } else if (type == BGP_PREFIX_SID_ORIGINATOR_SRGB) {
+ /*
+ * ietf-idr-bgp-prefix-sid-05:
+ * Length is the total length of the value portion of the
+ * TLV: 2 + multiple of 6.
+ *
+ * peer->curr stream readp should be at the beginning of the 16
+ * bit flag field at this point in the code.
+ */
+
+ /*
+ * Check that the TLV length field is sane: at least 2 bytes of
+ * flag, and at least 1 SRGB (these are 6 bytes each)
+ */
+ if (length < (2 + BGP_PREFIX_SID_ORIGINATOR_SRGB_LENGTH)) {
+ flog_err(
+ EC_BGP_ATTR_LEN,
+ "Prefix SID Originator SRGB length field claims length of %hu bytes, but the minimum for this TLV type is %u",
+ length,
+ 2 + BGP_PREFIX_SID_ORIGINATOR_SRGB_LENGTH);
+ return bgp_attr_malformed(
+ args, BGP_NOTIFY_UPDATE_ATTR_LENG_ERR,
+ args->total);
+ }
+
+ /*
+ * Check that the portion of the TLV containing the sequence of
+ * SRGBs corresponds to a multiple of the SRGB size; to get
+ * that length, we skip the 16 bit flags field
+ */
+ stream_getw(peer->curr);
+ length -= 2;
+ if (length % BGP_PREFIX_SID_ORIGINATOR_SRGB_LENGTH) {
+ flog_err(
+ EC_BGP_ATTR_LEN,
+ "Prefix SID Originator SRGB length field claims attribute SRGB sequence section is %hubytes, but it must be a multiple of %u",
+ length, BGP_PREFIX_SID_ORIGINATOR_SRGB_LENGTH);
+ return bgp_attr_malformed(
+ args, BGP_NOTIFY_UPDATE_ATTR_LENG_ERR,
+ args->total);
+ }
+
+ srgb_count = length / BGP_PREFIX_SID_ORIGINATOR_SRGB_LENGTH;
+
+ for (int i = 0; i < srgb_count; i++) {
+ stream_get(&srgb_base, peer->curr, 3);
+ stream_get(&srgb_range, peer->curr, 3);
+ }
+ } else if (type == BGP_PREFIX_SID_VPN_SID) {
+ if (length != BGP_PREFIX_SID_VPN_SID_LENGTH) {
+ flog_err(EC_BGP_ATTR_LEN,
+ "Prefix SID VPN SID length is %hu instead of %u",
+ length, BGP_PREFIX_SID_VPN_SID_LENGTH);
+ return bgp_attr_malformed(args,
+ BGP_NOTIFY_UPDATE_ATTR_LENG_ERR,
+ args->total);
+ }
+
+ /* Parse VPN-SID Sub-TLV */
+ stream_getc(peer->curr); /* reserved */
+ sid_type = stream_getc(peer->curr); /* sid_type */
+ sid_flags = stream_getc(peer->curr); /* sid_flags */
+ stream_get(&ipv6_sid, peer->curr,
+ sizeof(ipv6_sid)); /* sid_value */
+
+ /* Log VPN-SID Sub-TLV */
+ if (BGP_DEBUG(vpn, VPN_LEAK_LABEL)) {
+ inet_ntop(AF_INET6, &ipv6_sid, buf, sizeof(buf));
+ zlog_debug(
+ "%s: vpn-sid: sid %s, sid-type 0x%02x sid-flags 0x%02x",
+ __func__, buf, sid_type, sid_flags);
+ }
+
+ /* Configure from Info */
+ if (attr->srv6_vpn) {
+ flog_err(EC_BGP_ATTRIBUTE_REPEATED,
+ "Prefix SID SRv6 VPN field repeated");
+ return bgp_attr_malformed(
+ args, BGP_NOTIFY_UPDATE_MAL_ATTR, args->total);
+ }
+ attr->srv6_vpn = XCALLOC(MTYPE_BGP_SRV6_VPN,
+ sizeof(struct bgp_attr_srv6_vpn));
+ attr->srv6_vpn->sid_flags = sid_flags;
+ sid_copy(&attr->srv6_vpn->sid, &ipv6_sid);
+ attr->srv6_vpn = srv6_vpn_intern(attr->srv6_vpn);
+ } else if (type == BGP_PREFIX_SID_SRV6_L3_SERVICE) {
+ if (STREAM_READABLE(peer->curr) < 1) {
+ flog_err(
+ EC_BGP_ATTR_LEN,
+ "Prefix SID SRV6 L3 Service not enough data left, it must be at least 1 byte");
+ return bgp_attr_malformed(
+ args, BGP_NOTIFY_UPDATE_ATTR_LENG_ERR,
+ args->total);
+ }
+ /* ignore reserved */
+ stream_getc(peer->curr);
+
+ return bgp_attr_srv6_service(args);
+ }
+ /* Placeholder code for Unsupported TLV */
+ else {
+ if (bgp_debug_update(peer, NULL, NULL, 1))
+ zlog_debug(
+ "%s attr Prefix-SID sub-type=%u is not supported, skipped",
+ peer->host, type);
+
+ stream_forward_getp(peer->curr, length);
+ }
+
+ return BGP_ATTR_PARSE_PROCEED;
+}
+
+/* Prefix SID attribute
+ * draft-ietf-idr-bgp-prefix-sid-05
+ */
+enum bgp_attr_parse_ret bgp_attr_prefix_sid(struct bgp_attr_parser_args *args)
+{
+ struct peer *const peer = args->peer;
+ struct attr *const attr = args->attr;
+ enum bgp_attr_parse_ret ret;
+
+ attr->flag |= ATTR_FLAG_BIT(BGP_ATTR_PREFIX_SID);
+
+ uint8_t type;
+ uint16_t length;
+ size_t headersz = sizeof(type) + sizeof(length);
+ size_t psid_parsed_length = 0;
+
+ while (STREAM_READABLE(peer->curr) > 0
+ && psid_parsed_length < args->length) {
+
+ if (STREAM_READABLE(peer->curr) < headersz) {
+ flog_err(
+ EC_BGP_ATTR_LEN,
+ "Malformed Prefix SID attribute - insufficent data (need %zu for attribute header, have %zu remaining in UPDATE)",
+ headersz, STREAM_READABLE(peer->curr));
+ return bgp_attr_malformed(
+ args, BGP_NOTIFY_UPDATE_ATTR_LENG_ERR,
+ args->total);
+ }
+
+ type = stream_getc(peer->curr);
+ length = stream_getw(peer->curr);
+
+ if (STREAM_READABLE(peer->curr) < length) {
+ flog_err(
+ EC_BGP_ATTR_LEN,
+ "Malformed Prefix SID attribute - insufficient data (need %hu for attribute body, have %zu remaining in UPDATE)",
+ length, STREAM_READABLE(peer->curr));
+ return bgp_attr_malformed(args,
+ BGP_NOTIFY_UPDATE_ATTR_LENG_ERR,
+ args->total);
+ }
+
+ ret = bgp_attr_psid_sub(type, length, args);
+
+ if (ret != BGP_ATTR_PARSE_PROCEED)
+ return ret;
+
+ psid_parsed_length += length + headersz;
+
+ if (psid_parsed_length > args->length) {
+ flog_err(
+ EC_BGP_ATTR_LEN,
+ "Malformed Prefix SID attribute - TLV overflow by attribute (need %zu for TLV length, have %zu overflowed in UPDATE)",
+ length + headersz, psid_parsed_length - (length + headersz));
+ return bgp_attr_malformed(
+ args, BGP_NOTIFY_UPDATE_ATTR_LENG_ERR,
+ args->total);
+ }
+ }
+
+ return BGP_ATTR_PARSE_PROCEED;
+}
+
+/* PMSI tunnel attribute (RFC 6514)
+ * Basic validation checks done here.
+ */
+static enum bgp_attr_parse_ret
+bgp_attr_pmsi_tunnel(struct bgp_attr_parser_args *args)
+{
+ struct peer *const peer = args->peer;
+ struct attr *const attr = args->attr;
+ const bgp_size_t length = args->length;
+ uint8_t tnl_type;
+ int attr_parse_len = 2 + BGP_LABEL_BYTES;
+
+ /* Verify that the receiver is expecting "ingress replication" as we
+ * can only support that.
+ */
+ if (length < attr_parse_len) {
+ flog_err(EC_BGP_ATTR_LEN, "Bad PMSI tunnel attribute length %d",
+ length);
+ return bgp_attr_malformed(args, BGP_NOTIFY_UPDATE_ATTR_LENG_ERR,
+ args->total);
+ }
+ stream_getc(peer->curr); /* Flags */
+ tnl_type = stream_getc(peer->curr);
+ if (tnl_type > PMSI_TNLTYPE_MAX) {
+ flog_err(EC_BGP_ATTR_PMSI_TYPE,
+ "Invalid PMSI tunnel attribute type %d", tnl_type);
+ return bgp_attr_malformed(args, BGP_NOTIFY_UPDATE_OPT_ATTR_ERR,
+ args->total);
+ }
+ if (tnl_type == PMSI_TNLTYPE_INGR_REPL) {
+ if (length != 9) {
+ flog_err(EC_BGP_ATTR_PMSI_LEN,
+ "Bad PMSI tunnel attribute length %d for IR",
+ length);
+ return bgp_attr_malformed(
+ args, BGP_NOTIFY_UPDATE_ATTR_LENG_ERR,
+ args->total);
+ }
+ }
+
+ attr->flag |= ATTR_FLAG_BIT(BGP_ATTR_PMSI_TUNNEL);
+ bgp_attr_set_pmsi_tnl_type(attr, tnl_type);
+ stream_get(&attr->label, peer->curr, BGP_LABEL_BYTES);
+
+ /* Forward read pointer of input stream. */
+ stream_forward_getp(peer->curr, length - attr_parse_len);
+
+ return BGP_ATTR_PARSE_PROCEED;
+}
+
+/* OTC attribute. */
+static enum bgp_attr_parse_ret bgp_attr_otc(struct bgp_attr_parser_args *args)
+{
+ struct peer *const peer = args->peer;
+ struct attr *const attr = args->attr;
+ const bgp_size_t length = args->length;
+
+ /* Length check. */
+ if (length != 4) {
+ flog_err(EC_BGP_ATTR_LEN, "OTC attribute length isn't 4 [%u]",
+ length);
+ return bgp_attr_malformed(args, BGP_NOTIFY_UPDATE_ATTR_LENG_ERR,
+ args->total);
+ }
+
+ attr->otc = stream_getl(peer->curr);
+ if (!attr->otc) {
+ flog_err(EC_BGP_ATTR_MAL_AS_PATH, "OTC attribute value is 0");
+ return bgp_attr_malformed(args, BGP_NOTIFY_UPDATE_MAL_AS_PATH,
+ args->total);
+ }
+
+ attr->flag |= ATTR_FLAG_BIT(BGP_ATTR_OTC);
+
+ return BGP_ATTR_PARSE_PROCEED;
+}
+
+/* BGP unknown attribute treatment. */
+static enum bgp_attr_parse_ret
+bgp_attr_unknown(struct bgp_attr_parser_args *args)
+{
+ bgp_size_t total = args->total;
+ struct transit *transit;
+ struct peer *const peer = args->peer;
+ struct attr *const attr = args->attr;
+ uint8_t *const startp = args->startp;
+ const uint8_t type = args->type;
+ const uint8_t flag = args->flags;
+ const bgp_size_t length = args->length;
+
+ if (bgp_debug_update(peer, NULL, NULL, 1))
+ zlog_debug(
+ "%s Unknown attribute is received (type %d, length %d)",
+ peer->host, type, length);
+
+ /* Forward read pointer of input stream. */
+ stream_forward_getp(peer->curr, length);
+
+ /* If any of the mandatory well-known attributes are not recognized,
+ then the Error Subcode is set to Unrecognized Well-known
+ Attribute. The Data field contains the unrecognized attribute
+ (type, length and value). */
+ if (!CHECK_FLAG(flag, BGP_ATTR_FLAG_OPTIONAL)) {
+ return bgp_attr_malformed(args, BGP_NOTIFY_UPDATE_UNREC_ATTR,
+ args->total);
+ }
+
+ /* Unrecognized non-transitive optional attributes must be quietly
+ ignored and not passed along to other BGP peers. */
+ if (!CHECK_FLAG(flag, BGP_ATTR_FLAG_TRANS))
+ return BGP_ATTR_PARSE_PROCEED;
+
+ /* If a path with recognized transitive optional attribute is
+ accepted and passed along to other BGP peers and the Partial bit
+ in the Attribute Flags octet is set to 1 by some previous AS, it
+ is not set back to 0 by the current AS. */
+ SET_FLAG(*startp, BGP_ATTR_FLAG_PARTIAL);
+
+ /* Store transitive attribute to the end of attr->transit. */
+ transit = bgp_attr_get_transit(attr);
+ if (!transit)
+ transit = XCALLOC(MTYPE_TRANSIT, sizeof(struct transit));
+
+ transit->val = XREALLOC(MTYPE_TRANSIT_VAL, transit->val,
+ transit->length + total);
+
+ memcpy(transit->val + transit->length, startp, total);
+ transit->length += total;
+ bgp_attr_set_transit(attr, transit);
+
+ return BGP_ATTR_PARSE_PROCEED;
+}
+
+/* Well-known attribute check. */
+static int bgp_attr_check(struct peer *peer, struct attr *attr)
+{
+ uint8_t type = 0;
+
+ /* BGP Graceful-Restart End-of-RIB for IPv4 unicast is signaled as an
+ * empty UPDATE. */
+ if (CHECK_FLAG(peer->cap, PEER_CAP_RESTART_RCV) && !attr->flag)
+ return BGP_ATTR_PARSE_PROCEED;
+
+ /* "An UPDATE message that contains the MP_UNREACH_NLRI is not required
+ to carry any other path attributes.", though if MP_REACH_NLRI or NLRI
+ are present, it should. Check for any other attribute being present
+ instead.
+ */
+ if ((!CHECK_FLAG(attr->flag, ATTR_FLAG_BIT(BGP_ATTR_MP_REACH_NLRI)) &&
+ CHECK_FLAG(attr->flag, ATTR_FLAG_BIT(BGP_ATTR_MP_UNREACH_NLRI))))
+ return BGP_ATTR_PARSE_PROCEED;
+
+ if (!CHECK_FLAG(attr->flag, ATTR_FLAG_BIT(BGP_ATTR_ORIGIN)))
+ type = BGP_ATTR_ORIGIN;
+
+ if (!CHECK_FLAG(attr->flag, ATTR_FLAG_BIT(BGP_ATTR_AS_PATH)))
+ type = BGP_ATTR_AS_PATH;
+
+ /* RFC 2858 makes Next-Hop optional/ignored, if MP_REACH_NLRI is present
+ * and
+ * NLRI is empty. We can't easily check NLRI empty here though.
+ */
+ if (!CHECK_FLAG(attr->flag, ATTR_FLAG_BIT(BGP_ATTR_NEXT_HOP))
+ && !CHECK_FLAG(attr->flag, ATTR_FLAG_BIT(BGP_ATTR_MP_REACH_NLRI)))
+ type = BGP_ATTR_NEXT_HOP;
+
+ if (peer->sort == BGP_PEER_IBGP
+ && !CHECK_FLAG(attr->flag, ATTR_FLAG_BIT(BGP_ATTR_LOCAL_PREF)))
+ type = BGP_ATTR_LOCAL_PREF;
+
+ /* If any of the well-known mandatory attributes are not present
+ * in an UPDATE message, then "treat-as-withdraw" MUST be used.
+ */
+ if (type) {
+ flog_warn(EC_BGP_MISSING_ATTRIBUTE,
+ "%s Missing well-known attribute %s.", peer->host,
+ lookup_msg(attr_str, type, NULL));
+ return BGP_ATTR_PARSE_WITHDRAW;
+ }
+ return BGP_ATTR_PARSE_PROCEED;
+}
+
+/* Read attribute of update packet. This function is called from
+ bgp_update_receive() in bgp_packet.c. */
+enum bgp_attr_parse_ret bgp_attr_parse(struct peer *peer, struct attr *attr,
+ bgp_size_t size,
+ struct bgp_nlri *mp_update,
+ struct bgp_nlri *mp_withdraw)
+{
+ enum bgp_attr_parse_ret ret;
+ uint8_t flag = 0;
+ uint8_t type = 0;
+ bgp_size_t length;
+ uint8_t *startp, *endp;
+ uint8_t *attr_endp;
+ uint8_t seen[BGP_ATTR_BITMAP_SIZE];
+ /* we need the as4_path only until we have synthesized the as_path with
+ * it */
+ /* same goes for as4_aggregator */
+ struct aspath *as4_path = NULL;
+ as_t as4_aggregator = 0;
+ struct in_addr as4_aggregator_addr = {.s_addr = 0};
+ struct transit *transit;
+
+ /* Initialize bitmap. */
+ memset(seen, 0, BGP_ATTR_BITMAP_SIZE);
+
+ /* End pointer of BGP attribute. */
+ endp = BGP_INPUT_PNT(peer) + size;
+
+ /* Get attributes to the end of attribute length. */
+ while (BGP_INPUT_PNT(peer) < endp) {
+ /* Check remaining length check.*/
+ if (endp - BGP_INPUT_PNT(peer) < BGP_ATTR_MIN_LEN) {
+ /* XXX warning: long int format, int arg (arg 5) */
+ flog_warn(
+ EC_BGP_ATTRIBUTE_TOO_SMALL,
+ "%s: error BGP attribute length %lu is smaller than min len",
+ peer->host,
+ (unsigned long)(endp
+ - stream_pnt(BGP_INPUT(peer))));
+
+ bgp_notify_send(peer, BGP_NOTIFY_UPDATE_ERR,
+ BGP_NOTIFY_UPDATE_ATTR_LENG_ERR);
+ ret = BGP_ATTR_PARSE_ERROR;
+ goto done;
+ }
+
+ /* Fetch attribute flag and type. */
+ startp = BGP_INPUT_PNT(peer);
+ /* "The lower-order four bits of the Attribute Flags octet are
+ unused. They MUST be zero when sent and MUST be ignored when
+ received." */
+ flag = 0xF0 & stream_getc(BGP_INPUT(peer));
+ type = stream_getc(BGP_INPUT(peer));
+
+ /* Check whether Extended-Length applies and is in bounds */
+ if (CHECK_FLAG(flag, BGP_ATTR_FLAG_EXTLEN)
+ && ((endp - startp) < (BGP_ATTR_MIN_LEN + 1))) {
+ flog_warn(
+ EC_BGP_EXT_ATTRIBUTE_TOO_SMALL,
+ "%s: Extended length set, but just %lu bytes of attr header",
+ peer->host,
+ (unsigned long)(endp
+ - stream_pnt(BGP_INPUT(peer))));
+
+ bgp_notify_send(peer, BGP_NOTIFY_UPDATE_ERR,
+ BGP_NOTIFY_UPDATE_ATTR_LENG_ERR);
+ ret = BGP_ATTR_PARSE_ERROR;
+ goto done;
+ }
+
+ /* Check extended attribue length bit. */
+ if (CHECK_FLAG(flag, BGP_ATTR_FLAG_EXTLEN))
+ length = stream_getw(BGP_INPUT(peer));
+ else
+ length = stream_getc(BGP_INPUT(peer));
+
+ /* If any attribute appears more than once in the UPDATE
+ message, then the Error Subcode is set to Malformed Attribute
+ List. */
+
+ if (CHECK_BITMAP(seen, type)) {
+ flog_warn(
+ EC_BGP_ATTRIBUTE_REPEATED,
+ "%s: error BGP attribute type %d appears twice in a message",
+ peer->host, type);
+
+ bgp_notify_send(peer, BGP_NOTIFY_UPDATE_ERR,
+ BGP_NOTIFY_UPDATE_MAL_ATTR);
+ ret = BGP_ATTR_PARSE_ERROR;
+ goto done;
+ }
+
+ /* Set type to bitmap to check duplicate attribute. `type' is
+ unsigned char so it never overflow bitmap range. */
+
+ SET_BITMAP(seen, type);
+
+ /* Overflow check. */
+ attr_endp = BGP_INPUT_PNT(peer) + length;
+
+ if (attr_endp > endp) {
+ flog_warn(
+ EC_BGP_ATTRIBUTE_TOO_LARGE,
+ "%s: BGP type %d length %d is too large, attribute total length is %d. attr_endp is %p. endp is %p",
+ peer->host, type, length, size, attr_endp,
+ endp);
+ /*
+ * RFC 4271 6.3
+ * If any recognized attribute has an Attribute
+ * Length that conflicts with the expected length
+ * (based on the attribute type code), then the
+ * Error Subcode MUST be set to Attribute Length
+ * Error. The Data field MUST contain the erroneous
+ * attribute (type, length, and value).
+ * ----------
+ * We do not currently have a good way to determine the
+ * length of the attribute independent of the length
+ * received in the message. Instead we send the
+ * minimum between the amount of data we have and the
+ * amount specified by the attribute length field.
+ *
+ * Instead of directly passing in the packet buffer and
+ * offset we use the stream_get* functions to read into
+ * a stack buffer, since they perform bounds checking
+ * and we are working with untrusted data.
+ */
+ unsigned char ndata[peer->max_packet_size];
+ memset(ndata, 0x00, sizeof(ndata));
+ size_t lfl =
+ CHECK_FLAG(flag, BGP_ATTR_FLAG_EXTLEN) ? 2 : 1;
+ /* Rewind to end of flag field */
+ stream_rewind_getp(BGP_INPUT(peer), (1 + lfl));
+ /* Type */
+ stream_get(&ndata[0], BGP_INPUT(peer), 1);
+ /* Length */
+ stream_get(&ndata[1], BGP_INPUT(peer), lfl);
+ /* Value */
+ size_t atl = attr_endp - startp;
+ size_t ndl = MIN(atl, STREAM_READABLE(BGP_INPUT(peer)));
+ stream_get(&ndata[lfl + 1], BGP_INPUT(peer), ndl);
+
+ bgp_notify_send_with_data(
+ peer, BGP_NOTIFY_UPDATE_ERR,
+ BGP_NOTIFY_UPDATE_ATTR_LENG_ERR, ndata,
+ ndl + lfl + 1);
+
+ ret = BGP_ATTR_PARSE_ERROR;
+ goto done;
+ }
+
+ struct bgp_attr_parser_args attr_args = {
+ .peer = peer,
+ .length = length,
+ .attr = attr,
+ .type = type,
+ .flags = flag,
+ .startp = startp,
+ .total = attr_endp - startp,
+ };
+
+
+ /* If any recognized attribute has Attribute Flags that conflict
+ with the Attribute Type Code, then the Error Subcode is set
+ to
+ Attribute Flags Error. The Data field contains the erroneous
+ attribute (type, length and value). */
+ if (bgp_attr_flag_invalid(&attr_args)) {
+ ret = bgp_attr_malformed(
+ &attr_args, BGP_NOTIFY_UPDATE_ATTR_FLAG_ERR,
+ attr_args.total);
+ if (ret == BGP_ATTR_PARSE_PROCEED)
+ continue;
+ goto done;
+ }
+
+ /* OK check attribute and store it's value. */
+ switch (type) {
+ case BGP_ATTR_ORIGIN:
+ ret = bgp_attr_origin(&attr_args);
+ break;
+ case BGP_ATTR_AS_PATH:
+ ret = bgp_attr_aspath(&attr_args);
+ break;
+ case BGP_ATTR_AS4_PATH:
+ ret = bgp_attr_as4_path(&attr_args, &as4_path);
+ break;
+ case BGP_ATTR_NEXT_HOP:
+ ret = bgp_attr_nexthop(&attr_args);
+ break;
+ case BGP_ATTR_MULTI_EXIT_DISC:
+ ret = bgp_attr_med(&attr_args);
+ break;
+ case BGP_ATTR_LOCAL_PREF:
+ ret = bgp_attr_local_pref(&attr_args);
+ break;
+ case BGP_ATTR_ATOMIC_AGGREGATE:
+ ret = bgp_attr_atomic(&attr_args);
+ break;
+ case BGP_ATTR_AGGREGATOR:
+ ret = bgp_attr_aggregator(&attr_args);
+ break;
+ case BGP_ATTR_AS4_AGGREGATOR:
+ ret = bgp_attr_as4_aggregator(&attr_args,
+ &as4_aggregator,
+ &as4_aggregator_addr);
+ break;
+ case BGP_ATTR_COMMUNITIES:
+ ret = bgp_attr_community(&attr_args);
+ break;
+ case BGP_ATTR_LARGE_COMMUNITIES:
+ ret = bgp_attr_large_community(&attr_args);
+ break;
+ case BGP_ATTR_ORIGINATOR_ID:
+ ret = bgp_attr_originator_id(&attr_args);
+ break;
+ case BGP_ATTR_CLUSTER_LIST:
+ ret = bgp_attr_cluster_list(&attr_args);
+ break;
+ case BGP_ATTR_MP_REACH_NLRI:
+ ret = bgp_mp_reach_parse(&attr_args, mp_update);
+ break;
+ case BGP_ATTR_MP_UNREACH_NLRI:
+ ret = bgp_mp_unreach_parse(&attr_args, mp_withdraw);
+ break;
+ case BGP_ATTR_EXT_COMMUNITIES:
+ ret = bgp_attr_ext_communities(&attr_args);
+ break;
+#ifdef ENABLE_BGP_VNC_ATTR
+ case BGP_ATTR_VNC:
+#endif
+ case BGP_ATTR_ENCAP:
+ ret = bgp_attr_encap(type, peer, length, attr, flag,
+ startp);
+ break;
+ case BGP_ATTR_PREFIX_SID:
+ ret = bgp_attr_prefix_sid(&attr_args);
+ break;
+ case BGP_ATTR_PMSI_TUNNEL:
+ ret = bgp_attr_pmsi_tunnel(&attr_args);
+ break;
+ case BGP_ATTR_IPV6_EXT_COMMUNITIES:
+ ret = bgp_attr_ipv6_ext_communities(&attr_args);
+ break;
+ case BGP_ATTR_OTC:
+ ret = bgp_attr_otc(&attr_args);
+ break;
+ default:
+ ret = bgp_attr_unknown(&attr_args);
+ break;
+ }
+
+ if (ret == BGP_ATTR_PARSE_ERROR_NOTIFYPLS) {
+ bgp_notify_send(peer, BGP_NOTIFY_UPDATE_ERR,
+ BGP_NOTIFY_UPDATE_MAL_ATTR);
+ ret = BGP_ATTR_PARSE_ERROR;
+ goto done;
+ }
+
+ if (ret == BGP_ATTR_PARSE_EOR) {
+ goto done;
+ }
+
+ if (ret == BGP_ATTR_PARSE_ERROR) {
+ flog_warn(EC_BGP_ATTRIBUTE_PARSE_ERROR,
+ "%s: Attribute %s, parse error", peer->host,
+ lookup_msg(attr_str, type, NULL));
+ goto done;
+ }
+ if (ret == BGP_ATTR_PARSE_WITHDRAW) {
+ flog_warn(
+ EC_BGP_ATTRIBUTE_PARSE_WITHDRAW,
+ "%s: Attribute %s, parse error - treating as withdrawal",
+ peer->host, lookup_msg(attr_str, type, NULL));
+ goto done;
+ }
+
+ /* Check the fetched length. */
+ if (BGP_INPUT_PNT(peer) != attr_endp) {
+ flog_warn(EC_BGP_ATTRIBUTE_FETCH_ERROR,
+ "%s: BGP attribute %s, fetch error",
+ peer->host, lookup_msg(attr_str, type, NULL));
+ bgp_notify_send(peer, BGP_NOTIFY_UPDATE_ERR,
+ BGP_NOTIFY_UPDATE_ATTR_LENG_ERR);
+ ret = BGP_ATTR_PARSE_ERROR;
+ goto done;
+ }
+ }
+
+ /*
+ * draft-ietf-idr-bgp-prefix-sid-27#section-3:
+ * About Prefix-SID path attribute,
+ * Label-Index TLV(type1) and The Originator SRGB TLV(type-3)
+ * may only appear in a BGP Prefix-SID attribute attached to
+ * IPv4/IPv6 Labeled Unicast prefixes ([RFC8277]).
+ * It MUST be ignored when received for other BGP AFI/SAFI combinations.
+ */
+ if (!attr->mp_nexthop_len || mp_update->safi != SAFI_LABELED_UNICAST)
+ attr->label_index = BGP_INVALID_LABEL_INDEX;
+
+ /* Check final read pointer is same as end pointer. */
+ if (BGP_INPUT_PNT(peer) != endp) {
+ flog_warn(EC_BGP_ATTRIBUTES_MISMATCH,
+ "%s: BGP attribute %s, length mismatch", peer->host,
+ lookup_msg(attr_str, type, NULL));
+ bgp_notify_send(peer, BGP_NOTIFY_UPDATE_ERR,
+ BGP_NOTIFY_UPDATE_ATTR_LENG_ERR);
+
+ ret = BGP_ATTR_PARSE_ERROR;
+ goto done;
+ }
+
+ /*
+ * RFC4271: If the NEXT_HOP attribute field is syntactically incorrect,
+ * then the Error Subcode MUST be set to Invalid NEXT_HOP Attribute.
+ * This is implemented below and will result in a NOTIFICATION. If the
+ * NEXT_HOP attribute is semantically incorrect, the error SHOULD be
+ * logged, and the route SHOULD be ignored. In this case, a NOTIFICATION
+ * message SHOULD NOT be sent. This is implemented elsewhere.
+ *
+ * RFC4760: An UPDATE message that carries no NLRI, other than the one
+ * encoded in the MP_REACH_NLRI attribute, SHOULD NOT carry the NEXT_HOP
+ * attribute. If such a message contains the NEXT_HOP attribute, the BGP
+ * speaker that receives the message SHOULD ignore this attribute.
+ */
+ if (CHECK_FLAG(attr->flag, ATTR_FLAG_BIT(BGP_ATTR_NEXT_HOP))
+ && !CHECK_FLAG(attr->flag, ATTR_FLAG_BIT(BGP_ATTR_MP_REACH_NLRI))) {
+ if (bgp_attr_nexthop_valid(peer, attr) < 0) {
+ ret = BGP_ATTR_PARSE_ERROR;
+ goto done;
+ }
+ }
+
+ /* Check all mandatory well-known attributes are present */
+ ret = bgp_attr_check(peer, attr);
+ if (ret < 0)
+ goto done;
+
+ /*
+ * At this place we can see whether we got AS4_PATH and/or
+ * AS4_AGGREGATOR from a 16Bit peer and act accordingly.
+ * We can not do this before we've read all attributes because
+ * the as4 handling does not say whether AS4_PATH has to be sent
+ * after AS_PATH or not - and when AS4_AGGREGATOR will be send
+ * in relationship to AGGREGATOR.
+ * So, to be defensive, we are not relying on any order and read
+ * all attributes first, including these 32bit ones, and now,
+ * afterwards, we look what and if something is to be done for as4.
+ *
+ * It is possible to not have AS_PATH, e.g. GR EoR and sole
+ * MP_UNREACH_NLRI.
+ */
+ /* actually... this doesn't ever return failure currently, but
+ * better safe than sorry */
+ if (CHECK_FLAG(attr->flag, ATTR_FLAG_BIT(BGP_ATTR_AS_PATH))
+ && bgp_attr_munge_as4_attrs(peer, attr, as4_path, as4_aggregator,
+ &as4_aggregator_addr)) {
+ bgp_notify_send(peer, BGP_NOTIFY_UPDATE_ERR,
+ BGP_NOTIFY_UPDATE_MAL_ATTR);
+ ret = BGP_ATTR_PARSE_ERROR;
+ goto done;
+ }
+
+ /*
+ * Finally do the checks on the aspath we did not do yet
+ * because we waited for a potentially synthesized aspath.
+ */
+ if (attr->flag & (ATTR_FLAG_BIT(BGP_ATTR_AS_PATH))) {
+ ret = bgp_attr_aspath_check(peer, attr);
+ if (ret != BGP_ATTR_PARSE_PROCEED)
+ goto done;
+ }
+
+ ret = BGP_ATTR_PARSE_PROCEED;
+done:
+
+ /*
+ * At this stage, we have done all fiddling with as4, and the
+ * resulting info is in attr->aggregator resp. attr->aspath so
+ * we can chuck as4_aggregator and as4_path alltogether in order
+ * to save memory
+ */
+ /*
+ * unintern - it is in the hash
+ * The flag that we got this is still there, but that
+ * does not do any trouble
+ */
+ aspath_unintern(&as4_path);
+
+ transit = bgp_attr_get_transit(attr);
+ if (ret != BGP_ATTR_PARSE_ERROR) {
+ /* Finally intern unknown attribute. */
+ if (transit)
+ bgp_attr_set_transit(attr, transit_intern(transit));
+ if (attr->encap_subtlvs)
+ attr->encap_subtlvs = encap_intern(attr->encap_subtlvs,
+ ENCAP_SUBTLV_TYPE);
+#ifdef ENABLE_BGP_VNC
+ struct bgp_attr_encap_subtlv *vnc_subtlvs =
+ bgp_attr_get_vnc_subtlvs(attr);
+
+ if (vnc_subtlvs)
+ bgp_attr_set_vnc_subtlvs(
+ attr,
+ encap_intern(vnc_subtlvs, VNC_SUBTLV_TYPE));
+#endif
+ } else {
+ if (transit) {
+ transit_free(transit);
+ bgp_attr_set_transit(attr, NULL);
+ }
+
+ bgp_attr_flush_encap(attr);
+ };
+
+ /* Sanity checks */
+ transit = bgp_attr_get_transit(attr);
+ if (transit)
+ assert(transit->refcnt > 0);
+ if (attr->encap_subtlvs)
+ assert(attr->encap_subtlvs->refcnt > 0);
+#ifdef ENABLE_BGP_VNC
+ struct bgp_attr_encap_subtlv *vnc_subtlvs =
+ bgp_attr_get_vnc_subtlvs(attr);
+
+ if (vnc_subtlvs)
+ assert(vnc_subtlvs->refcnt > 0);
+#endif
+
+ return ret;
+}
+
+/*
+ * Extract the tunnel type from extended community
+ */
+void bgp_attr_extcom_tunnel_type(struct attr *attr,
+ bgp_encap_types *tunnel_type)
+{
+ struct ecommunity *ecom;
+ uint32_t i;
+
+ if (!attr)
+ return;
+
+ ecom = bgp_attr_get_ecommunity(attr);
+ if (!ecom || !ecom->size)
+ return;
+
+ for (i = 0; i < ecom->size; i++) {
+ uint8_t *pnt;
+ uint8_t type, sub_type;
+
+ pnt = (ecom->val + (i * ECOMMUNITY_SIZE));
+ type = pnt[0];
+ sub_type = pnt[1];
+ if (!(type == ECOMMUNITY_ENCODE_OPAQUE &&
+ sub_type == ECOMMUNITY_OPAQUE_SUBTYPE_ENCAP))
+ continue;
+ *tunnel_type = ((pnt[6] << 8) | pnt[7]);
+ return;
+ }
+
+ return;
+}
+
+size_t bgp_packet_mpattr_start(struct stream *s, struct peer *peer, afi_t afi,
+ safi_t safi, struct bpacket_attr_vec_arr *vecarr,
+ struct attr *attr)
+{
+ size_t sizep;
+ iana_afi_t pkt_afi = IANA_AFI_IPV4;
+ iana_safi_t pkt_safi = IANA_SAFI_UNICAST;
+ afi_t nh_afi;
+
+ /* Set extended bit always to encode the attribute length as 2 bytes */
+ stream_putc(s, BGP_ATTR_FLAG_OPTIONAL | BGP_ATTR_FLAG_EXTLEN);
+ stream_putc(s, BGP_ATTR_MP_REACH_NLRI);
+ sizep = stream_get_endp(s);
+ stream_putw(s, 0); /* Marker: Attribute length. */
+
+
+ /* Convert AFI, SAFI to values for packet. */
+ bgp_map_afi_safi_int2iana(afi, safi, &pkt_afi, &pkt_safi);
+
+ stream_putw(s, pkt_afi); /* AFI */
+ stream_putc(s, pkt_safi); /* SAFI */
+
+ /* Nexthop AFI */
+ if (afi == AFI_IP
+ && (safi == SAFI_UNICAST || safi == SAFI_LABELED_UNICAST
+ || safi == SAFI_MPLS_VPN || safi == SAFI_MULTICAST))
+ nh_afi = peer_cap_enhe(peer, afi, safi) ? AFI_IP6 : AFI_IP;
+ else if (safi == SAFI_FLOWSPEC)
+ nh_afi = afi;
+ else
+ nh_afi = BGP_NEXTHOP_AFI_FROM_NHLEN(attr->mp_nexthop_len);
+
+ /* Nexthop */
+ bpacket_attr_vec_arr_set_vec(vecarr, BGP_ATTR_VEC_NH, s, attr);
+ switch (nh_afi) {
+ case AFI_IP:
+ switch (safi) {
+ case SAFI_UNICAST:
+ case SAFI_MULTICAST:
+ case SAFI_LABELED_UNICAST:
+ stream_putc(s, 4);
+ stream_put_ipv4(s, attr->nexthop.s_addr);
+ break;
+ case SAFI_MPLS_VPN:
+ stream_putc(s, 12);
+ stream_putl(s, 0); /* RD = 0, per RFC */
+ stream_putl(s, 0);
+ stream_put(s, &attr->mp_nexthop_global_in, 4);
+ break;
+ case SAFI_ENCAP:
+ case SAFI_EVPN:
+ stream_putc(s, 4);
+ stream_put(s, &attr->mp_nexthop_global_in, 4);
+ break;
+ case SAFI_FLOWSPEC:
+ if (attr->mp_nexthop_len == 0)
+ stream_putc(s, 0); /* no nexthop for flowspec */
+ else {
+ stream_putc(s, attr->mp_nexthop_len);
+ stream_put_ipv4(s, attr->nexthop.s_addr);
+ }
+ default:
+ break;
+ }
+ break;
+ case AFI_IP6:
+ switch (safi) {
+ case SAFI_UNICAST:
+ case SAFI_MULTICAST:
+ case SAFI_LABELED_UNICAST:
+ case SAFI_EVPN: {
+ if (attr->mp_nexthop_len
+ == BGP_ATTR_NHLEN_IPV6_GLOBAL_AND_LL) {
+ stream_putc(s,
+ BGP_ATTR_NHLEN_IPV6_GLOBAL_AND_LL);
+ stream_put(s, &attr->mp_nexthop_global,
+ IPV6_MAX_BYTELEN);
+ stream_put(s, &attr->mp_nexthop_local,
+ IPV6_MAX_BYTELEN);
+ } else {
+ stream_putc(s, IPV6_MAX_BYTELEN);
+ stream_put(s, &attr->mp_nexthop_global,
+ IPV6_MAX_BYTELEN);
+ }
+ } break;
+ case SAFI_MPLS_VPN: {
+ if (attr->mp_nexthop_len
+ == BGP_ATTR_NHLEN_IPV6_GLOBAL_AND_LL) {
+ stream_putc(s, 48);
+ stream_putl(s, 0); /* RD = 0, per RFC */
+ stream_putl(s, 0);
+ stream_put(s, &attr->mp_nexthop_global,
+ IPV6_MAX_BYTELEN);
+ stream_putl(s, 0); /* RD = 0, per RFC */
+ stream_putl(s, 0);
+ stream_put(s, &attr->mp_nexthop_local,
+ IPV6_MAX_BYTELEN);
+ } else {
+ stream_putc(s, 24);
+ stream_putl(s, 0); /* RD = 0, per RFC */
+ stream_putl(s, 0);
+ stream_put(s, &attr->mp_nexthop_global,
+ IPV6_MAX_BYTELEN);
+ }
+ } break;
+ case SAFI_ENCAP:
+ stream_putc(s, IPV6_MAX_BYTELEN);
+ stream_put(s, &attr->mp_nexthop_global,
+ IPV6_MAX_BYTELEN);
+ break;
+ case SAFI_FLOWSPEC:
+ stream_putc(s, 0); /* no nexthop for flowspec */
+ default:
+ break;
+ }
+ break;
+ default:
+ if (safi != SAFI_FLOWSPEC)
+ flog_err(
+ EC_BGP_ATTR_NH_SEND_LEN,
+ "Bad nexthop when sending to %s, AFI %u SAFI %u nhlen %d",
+ peer->host, afi, safi, attr->mp_nexthop_len);
+ break;
+ }
+
+ /* SNPA */
+ stream_putc(s, 0);
+ return sizep;
+}
+
+void bgp_packet_mpattr_prefix(struct stream *s, afi_t afi, safi_t safi,
+ const struct prefix *p,
+ const struct prefix_rd *prd, mpls_label_t *label,
+ uint32_t num_labels, bool addpath_capable,
+ uint32_t addpath_tx_id, struct attr *attr)
+{
+ if (safi == SAFI_MPLS_VPN) {
+ if (addpath_capable)
+ stream_putl(s, addpath_tx_id);
+ /* Label, RD, Prefix write. */
+ stream_putc(s, p->prefixlen + 88);
+ stream_put(s, label, BGP_LABEL_BYTES);
+ stream_put(s, prd->val, 8);
+ stream_put(s, &p->u.prefix, PSIZE(p->prefixlen));
+ } else if (afi == AFI_L2VPN && safi == SAFI_EVPN) {
+ /* EVPN prefix - contents depend on type */
+ bgp_evpn_encode_prefix(s, p, prd, label, num_labels, attr,
+ addpath_capable, addpath_tx_id);
+ } else if (safi == SAFI_LABELED_UNICAST) {
+ /* Prefix write with label. */
+ stream_put_labeled_prefix(s, p, label, addpath_capable,
+ addpath_tx_id);
+ } else if (safi == SAFI_FLOWSPEC) {
+ stream_putc(s, p->u.prefix_flowspec.prefixlen);
+ stream_put(s, (const void *)p->u.prefix_flowspec.ptr,
+ p->u.prefix_flowspec.prefixlen);
+ } else
+ stream_put_prefix_addpath(s, p, addpath_capable, addpath_tx_id);
+}
+
+size_t bgp_packet_mpattr_prefix_size(afi_t afi, safi_t safi,
+ const struct prefix *p)
+{
+ int size = PSIZE(p->prefixlen);
+ if (safi == SAFI_MPLS_VPN)
+ size += 88;
+ else if (safi == SAFI_LABELED_UNICAST)
+ size += BGP_LABEL_BYTES;
+ else if (afi == AFI_L2VPN && safi == SAFI_EVPN)
+ size += 232; // TODO: Maximum possible for type-2, type-3 and
+ // type-5
+ return size;
+}
+
+/*
+ * Encodes the tunnel encapsulation attribute,
+ * and with ENABLE_BGP_VNC the VNC attribute which uses
+ * almost the same TLV format
+ */
+static void bgp_packet_mpattr_tea(struct bgp *bgp, struct peer *peer,
+ struct stream *s, struct attr *attr,
+ uint8_t attrtype)
+{
+ unsigned int attrlenfield = 0;
+ unsigned int attrhdrlen = 0;
+ struct bgp_attr_encap_subtlv *subtlvs;
+ struct bgp_attr_encap_subtlv *st;
+ const char *attrname;
+
+ if (!attr || (attrtype == BGP_ATTR_ENCAP
+ && (!attr->encap_tunneltype
+ || attr->encap_tunneltype == BGP_ENCAP_TYPE_MPLS)))
+ return;
+
+ switch (attrtype) {
+ case BGP_ATTR_ENCAP:
+ attrname = "Tunnel Encap";
+ subtlvs = attr->encap_subtlvs;
+ if (subtlvs == NULL) /* nothing to do */
+ return;
+ /*
+ * The tunnel encap attr has an "outer" tlv.
+ * T = tunneltype,
+ * L = total length of subtlvs,
+ * V = concatenated subtlvs.
+ */
+ attrlenfield = 2 + 2; /* T + L */
+ attrhdrlen = 1 + 1; /* subTLV T + L */
+ break;
+
+#ifdef ENABLE_BGP_VNC_ATTR
+ case BGP_ATTR_VNC:
+ attrname = "VNC";
+ subtlvs = bgp_attr_get_vnc_subtlvs(attr);
+ if (subtlvs == NULL) /* nothing to do */
+ return;
+ attrlenfield = 0; /* no outer T + L */
+ attrhdrlen = 2 + 2; /* subTLV T + L */
+ break;
+#endif
+
+ default:
+ assert(0);
+ }
+
+ /* compute attr length */
+ for (st = subtlvs; st; st = st->next) {
+ attrlenfield += (attrhdrlen + st->length);
+ }
+
+ if (attrlenfield > 0xffff) {
+ zlog_info("%s attribute is too long (length=%d), can't send it",
+ attrname, attrlenfield);
+ return;
+ }
+
+ if (attrlenfield > 0xff) {
+ /* 2-octet length field */
+ stream_putc(s,
+ BGP_ATTR_FLAG_TRANS | BGP_ATTR_FLAG_OPTIONAL
+ | BGP_ATTR_FLAG_EXTLEN);
+ stream_putc(s, attrtype);
+ stream_putw(s, attrlenfield & 0xffff);
+ } else {
+ /* 1-octet length field */
+ stream_putc(s, BGP_ATTR_FLAG_TRANS | BGP_ATTR_FLAG_OPTIONAL);
+ stream_putc(s, attrtype);
+ stream_putc(s, attrlenfield & 0xff);
+ }
+
+ if (attrtype == BGP_ATTR_ENCAP) {
+ /* write outer T+L */
+ stream_putw(s, attr->encap_tunneltype);
+ stream_putw(s, attrlenfield - 4);
+ }
+
+ /* write each sub-tlv */
+ for (st = subtlvs; st; st = st->next) {
+ if (attrtype == BGP_ATTR_ENCAP) {
+ stream_putc(s, st->type);
+ stream_putc(s, st->length);
+#ifdef ENABLE_BGP_VNC
+ } else {
+ stream_putw(s, st->type);
+ stream_putw(s, st->length);
+#endif
+ }
+ stream_put(s, st->value, st->length);
+ }
+}
+
+void bgp_packet_mpattr_end(struct stream *s, size_t sizep)
+{
+ /* Set MP attribute length. Don't count the (2) bytes used to encode
+ the attr length */
+ stream_putw_at(s, sizep, (stream_get_endp(s) - sizep) - 2);
+}
+
+static bool bgp_append_local_as(struct peer *peer, afi_t afi, safi_t safi)
+{
+ if (!BGP_AS_IS_PRIVATE(peer->local_as)
+ || (BGP_AS_IS_PRIVATE(peer->local_as)
+ && !CHECK_FLAG(peer->af_flags[afi][safi],
+ PEER_FLAG_REMOVE_PRIVATE_AS)
+ && !CHECK_FLAG(peer->af_flags[afi][safi],
+ PEER_FLAG_REMOVE_PRIVATE_AS_ALL)
+ && !CHECK_FLAG(peer->af_flags[afi][safi],
+ PEER_FLAG_REMOVE_PRIVATE_AS_REPLACE)
+ && !CHECK_FLAG(peer->af_flags[afi][safi],
+ PEER_FLAG_REMOVE_PRIVATE_AS_ALL_REPLACE)))
+ return true;
+ return false;
+}
+
+/* Make attribute packet. */
+bgp_size_t bgp_packet_attribute(struct bgp *bgp, struct peer *peer,
+ struct stream *s, struct attr *attr,
+ struct bpacket_attr_vec_arr *vecarr,
+ struct prefix *p, afi_t afi, safi_t safi,
+ struct peer *from, struct prefix_rd *prd,
+ mpls_label_t *label, uint32_t num_labels,
+ bool addpath_capable, uint32_t addpath_tx_id)
+{
+ size_t cp;
+ size_t aspath_sizep;
+ struct aspath *aspath;
+ int send_as4_path = 0;
+ int send_as4_aggregator = 0;
+ bool use32bit = CHECK_FLAG(peer->cap, PEER_CAP_AS4_RCV)
+ && CHECK_FLAG(peer->cap, PEER_CAP_AS4_ADV);
+
+ if (!bgp)
+ bgp = peer->bgp;
+
+ /* Remember current pointer. */
+ cp = stream_get_endp(s);
+
+ if (p
+ && !((afi == AFI_IP && safi == SAFI_UNICAST)
+ && !peer_cap_enhe(peer, afi, safi))) {
+ size_t mpattrlen_pos = 0;
+
+ mpattrlen_pos = bgp_packet_mpattr_start(s, peer, afi, safi,
+ vecarr, attr);
+ bgp_packet_mpattr_prefix(s, afi, safi, p, prd, label,
+ num_labels, addpath_capable,
+ addpath_tx_id, attr);
+ bgp_packet_mpattr_end(s, mpattrlen_pos);
+ }
+
+ /* Origin attribute. */
+ stream_putc(s, BGP_ATTR_FLAG_TRANS);
+ stream_putc(s, BGP_ATTR_ORIGIN);
+ stream_putc(s, 1);
+ stream_putc(s, attr->origin);
+
+ /* AS path attribute. */
+
+ /* If remote-peer is EBGP */
+ if (peer->sort == BGP_PEER_EBGP
+ && (!CHECK_FLAG(peer->af_flags[afi][safi],
+ PEER_FLAG_AS_PATH_UNCHANGED)
+ || attr->aspath->segments == NULL)
+ && (!CHECK_FLAG(peer->af_flags[afi][safi],
+ PEER_FLAG_RSERVER_CLIENT))) {
+ aspath = aspath_dup(attr->aspath);
+
+ /* Even though we may not be configured for confederations we
+ * may have
+ * RXed an AS_PATH with AS_CONFED_SEQUENCE or AS_CONFED_SET */
+ aspath = aspath_delete_confed_seq(aspath);
+
+ if (CHECK_FLAG(bgp->config, BGP_CONFIG_CONFEDERATION)) {
+ /* A confed member, so we need to do the
+ * AS_CONFED_SEQUENCE thing if it's outside a common
+ * administration.
+ * Configured confederation peers MUST be validated
+ * under BGP_PEER_CONFED, but if we have configured
+ * remote-as as AS_EXTERNAL, we need to check again
+ * if the peer belongs to us.
+ */
+ if (bgp_confederation_peers_check(bgp, peer->as)) {
+ aspath = aspath_add_confed_seq(aspath,
+ peer->local_as);
+ } else {
+ /* Stuff our path CONFED_ID on the front */
+ aspath = aspath_add_seq(aspath, bgp->confed_id);
+ }
+ } else {
+ if (peer->change_local_as) {
+ /* If replace-as is specified, we only use the
+ change_local_as when
+ advertising routes. */
+ if (!CHECK_FLAG(peer->flags,
+ PEER_FLAG_LOCAL_AS_REPLACE_AS))
+ if (bgp_append_local_as(peer, afi,
+ safi))
+ aspath = aspath_add_seq(
+ aspath, peer->local_as);
+ aspath = aspath_add_seq(aspath,
+ peer->change_local_as);
+ } else {
+ aspath = aspath_add_seq(aspath, peer->local_as);
+ }
+ }
+ } else if (peer->sort == BGP_PEER_CONFED) {
+ /* A confed member, so we need to do the AS_CONFED_SEQUENCE
+ * thing */
+ aspath = aspath_dup(attr->aspath);
+ aspath = aspath_add_confed_seq(aspath, peer->local_as);
+ } else
+ aspath = attr->aspath;
+
+ /* If peer is not AS4 capable, then:
+ * - send the created AS_PATH out as AS4_PATH (optional, transitive),
+ * but ensure that no AS_CONFED_SEQUENCE and AS_CONFED_SET path
+ * segment
+ * types are in it (i.e. exclude them if they are there)
+ * AND do this only if there is at least one asnum > 65535 in the
+ * path!
+ * - send an AS_PATH out, but put 16Bit ASnums in it, not 32bit, and
+ * change
+ * all ASnums > 65535 to BGP_AS_TRANS
+ */
+
+ stream_putc(s, BGP_ATTR_FLAG_TRANS | BGP_ATTR_FLAG_EXTLEN);
+ stream_putc(s, BGP_ATTR_AS_PATH);
+ aspath_sizep = stream_get_endp(s);
+ stream_putw(s, 0);
+ stream_putw_at(s, aspath_sizep, aspath_put(s, aspath, use32bit));
+
+ /* OLD session may need NEW_AS_PATH sent, if there are 4-byte ASNs
+ * in the path
+ */
+ if (!use32bit && aspath_has_as4(aspath))
+ send_as4_path =
+ 1; /* we'll do this later, at the correct place */
+
+ /* Nexthop attribute. */
+ if (afi == AFI_IP && safi == SAFI_UNICAST
+ && !peer_cap_enhe(peer, afi, safi)) {
+ afi_t nh_afi = BGP_NEXTHOP_AFI_FROM_NHLEN(attr->mp_nexthop_len);
+
+ if (attr->flag & ATTR_FLAG_BIT(BGP_ATTR_NEXT_HOP)) {
+ stream_putc(s, BGP_ATTR_FLAG_TRANS);
+ stream_putc(s, BGP_ATTR_NEXT_HOP);
+ bpacket_attr_vec_arr_set_vec(vecarr, BGP_ATTR_VEC_NH, s,
+ attr);
+ stream_putc(s, 4);
+ stream_put_ipv4(s, attr->nexthop.s_addr);
+ } else if (peer_cap_enhe(from, afi, safi)
+ || (nh_afi == AFI_IP6)) {
+ /*
+ * Likely this is the case when an IPv4 prefix was
+ * received with Extended Next-hop capability in this
+ * or another vrf and is now being advertised to
+ * non-ENHE peers. Since peer_cap_enhe only checks
+ * peers in this vrf, also check the nh_afi to catch
+ * the case where the originator was in another vrf.
+ * Setting the mandatory (ipv4) next-hop attribute here
+ * to enable implicit next-hop self with correct A-F
+ * (ipv4 address family).
+ */
+ stream_putc(s, BGP_ATTR_FLAG_TRANS);
+ stream_putc(s, BGP_ATTR_NEXT_HOP);
+ bpacket_attr_vec_arr_set_vec(vecarr, BGP_ATTR_VEC_NH, s,
+ NULL);
+ stream_putc(s, 4);
+ stream_put_ipv4(s, 0);
+ }
+ }
+
+ /* MED attribute. */
+ if (attr->flag & ATTR_FLAG_BIT(BGP_ATTR_MULTI_EXIT_DISC)
+ || bgp->maxmed_active) {
+ stream_putc(s, BGP_ATTR_FLAG_OPTIONAL);
+ stream_putc(s, BGP_ATTR_MULTI_EXIT_DISC);
+ stream_putc(s, 4);
+ stream_putl(s, (bgp->maxmed_active ? bgp->maxmed_value
+ : attr->med));
+ }
+
+ /* Local preference. */
+ if (peer->sort == BGP_PEER_IBGP || peer->sort == BGP_PEER_CONFED) {
+ stream_putc(s, BGP_ATTR_FLAG_TRANS);
+ stream_putc(s, BGP_ATTR_LOCAL_PREF);
+ stream_putc(s, 4);
+ stream_putl(s, attr->local_pref);
+ }
+
+ /* Atomic aggregate. */
+ if (attr->flag & ATTR_FLAG_BIT(BGP_ATTR_ATOMIC_AGGREGATE)) {
+ stream_putc(s, BGP_ATTR_FLAG_TRANS);
+ stream_putc(s, BGP_ATTR_ATOMIC_AGGREGATE);
+ stream_putc(s, 0);
+ }
+
+ /* Aggregator. */
+ if (attr->flag & ATTR_FLAG_BIT(BGP_ATTR_AGGREGATOR)) {
+ /* Common to BGP_ATTR_AGGREGATOR, regardless of ASN size */
+ stream_putc(s, BGP_ATTR_FLAG_OPTIONAL | BGP_ATTR_FLAG_TRANS);
+ stream_putc(s, BGP_ATTR_AGGREGATOR);
+
+ if (use32bit) {
+ /* AS4 capable peer */
+ stream_putc(s, 8);
+ stream_putl(s, attr->aggregator_as);
+ } else {
+ /* 2-byte AS peer */
+ stream_putc(s, 6);
+
+ /* Is ASN representable in 2-bytes? Or must AS_TRANS be
+ * used? */
+ if (attr->aggregator_as > UINT16_MAX) {
+ stream_putw(s, BGP_AS_TRANS);
+
+ /* we have to send AS4_AGGREGATOR, too.
+ * we'll do that later in order to send
+ * attributes in ascending
+ * order.
+ */
+ send_as4_aggregator = 1;
+ } else
+ stream_putw(s, (uint16_t)attr->aggregator_as);
+ }
+ stream_put_ipv4(s, attr->aggregator_addr.s_addr);
+ }
+
+ /* Community attribute. */
+ if (CHECK_FLAG(peer->af_flags[afi][safi], PEER_FLAG_SEND_COMMUNITY)
+ && (attr->flag & ATTR_FLAG_BIT(BGP_ATTR_COMMUNITIES))) {
+ struct community *comm = NULL;
+
+ comm = bgp_attr_get_community(attr);
+ if (comm->size * 4 > 255) {
+ stream_putc(s,
+ BGP_ATTR_FLAG_OPTIONAL | BGP_ATTR_FLAG_TRANS
+ | BGP_ATTR_FLAG_EXTLEN);
+ stream_putc(s, BGP_ATTR_COMMUNITIES);
+ stream_putw(s, comm->size * 4);
+ } else {
+ stream_putc(s,
+ BGP_ATTR_FLAG_OPTIONAL
+ | BGP_ATTR_FLAG_TRANS);
+ stream_putc(s, BGP_ATTR_COMMUNITIES);
+ stream_putc(s, comm->size * 4);
+ }
+ stream_put(s, comm->val, comm->size * 4);
+ }
+
+ /*
+ * Large Community attribute.
+ */
+ if (CHECK_FLAG(peer->af_flags[afi][safi],
+ PEER_FLAG_SEND_LARGE_COMMUNITY)
+ && (attr->flag & ATTR_FLAG_BIT(BGP_ATTR_LARGE_COMMUNITIES))) {
+ if (lcom_length(bgp_attr_get_lcommunity(attr)) > 255) {
+ stream_putc(s,
+ BGP_ATTR_FLAG_OPTIONAL | BGP_ATTR_FLAG_TRANS
+ | BGP_ATTR_FLAG_EXTLEN);
+ stream_putc(s, BGP_ATTR_LARGE_COMMUNITIES);
+ stream_putw(s,
+ lcom_length(bgp_attr_get_lcommunity(attr)));
+ } else {
+ stream_putc(s,
+ BGP_ATTR_FLAG_OPTIONAL
+ | BGP_ATTR_FLAG_TRANS);
+ stream_putc(s, BGP_ATTR_LARGE_COMMUNITIES);
+ stream_putc(s,
+ lcom_length(bgp_attr_get_lcommunity(attr)));
+ }
+ stream_put(s, bgp_attr_get_lcommunity(attr)->val,
+ lcom_length(bgp_attr_get_lcommunity(attr)));
+ }
+
+ /* Route Reflector. */
+ if (peer->sort == BGP_PEER_IBGP && from
+ && from->sort == BGP_PEER_IBGP) {
+ struct cluster_list *cluster = bgp_attr_get_cluster(attr);
+
+ /* Originator ID. */
+ stream_putc(s, BGP_ATTR_FLAG_OPTIONAL);
+ stream_putc(s, BGP_ATTR_ORIGINATOR_ID);
+ stream_putc(s, 4);
+
+ if (attr->flag & ATTR_FLAG_BIT(BGP_ATTR_ORIGINATOR_ID))
+ stream_put_in_addr(s, &attr->originator_id);
+ else
+ stream_put_in_addr(s, &from->remote_id);
+
+ /* Cluster list. */
+ stream_putc(s, BGP_ATTR_FLAG_OPTIONAL);
+ stream_putc(s, BGP_ATTR_CLUSTER_LIST);
+
+ if (cluster) {
+ stream_putc(s, cluster->length + 4);
+ /* If this peer configuration's parent BGP has
+ * cluster_id. */
+ if (bgp->config & BGP_CONFIG_CLUSTER_ID)
+ stream_put_in_addr(s, &bgp->cluster_id);
+ else
+ stream_put_in_addr(s, &bgp->router_id);
+ stream_put(s, cluster->list, cluster->length);
+ } else {
+ stream_putc(s, 4);
+ /* If this peer configuration's parent BGP has
+ * cluster_id. */
+ if (bgp->config & BGP_CONFIG_CLUSTER_ID)
+ stream_put_in_addr(s, &bgp->cluster_id);
+ else
+ stream_put_in_addr(s, &bgp->router_id);
+ }
+ }
+
+ /* Extended Communities attribute. */
+ if (CHECK_FLAG(peer->af_flags[afi][safi], PEER_FLAG_SEND_EXT_COMMUNITY)
+ && (attr->flag & ATTR_FLAG_BIT(BGP_ATTR_EXT_COMMUNITIES))) {
+ struct ecommunity *ecomm = bgp_attr_get_ecommunity(attr);
+ bool transparent = CHECK_FLAG(peer->af_flags[afi][safi],
+ PEER_FLAG_RSERVER_CLIENT) &&
+ from &&
+ CHECK_FLAG(from->af_flags[afi][safi],
+ PEER_FLAG_RSERVER_CLIENT);
+
+ if (peer->sort == BGP_PEER_IBGP ||
+ peer->sort == BGP_PEER_CONFED || transparent) {
+ if (ecomm->size * 8 > 255) {
+ stream_putc(s,
+ BGP_ATTR_FLAG_OPTIONAL
+ | BGP_ATTR_FLAG_TRANS
+ | BGP_ATTR_FLAG_EXTLEN);
+ stream_putc(s, BGP_ATTR_EXT_COMMUNITIES);
+ stream_putw(s, ecomm->size * 8);
+ } else {
+ stream_putc(s,
+ BGP_ATTR_FLAG_OPTIONAL
+ | BGP_ATTR_FLAG_TRANS);
+ stream_putc(s, BGP_ATTR_EXT_COMMUNITIES);
+ stream_putc(s, ecomm->size * 8);
+ }
+ stream_put(s, ecomm->val, ecomm->size * 8);
+ } else {
+ uint8_t *pnt;
+ int tbit;
+ int ecom_tr_size = 0;
+ uint32_t i;
+
+ for (i = 0; i < ecomm->size; i++) {
+ pnt = ecomm->val + (i * 8);
+ tbit = *pnt;
+
+ if (CHECK_FLAG(tbit,
+ ECOMMUNITY_FLAG_NON_TRANSITIVE))
+ continue;
+
+ ecom_tr_size++;
+ }
+
+ if (ecom_tr_size) {
+ if (ecom_tr_size * 8 > 255) {
+ stream_putc(
+ s,
+ BGP_ATTR_FLAG_OPTIONAL
+ | BGP_ATTR_FLAG_TRANS
+ | BGP_ATTR_FLAG_EXTLEN);
+ stream_putc(s,
+ BGP_ATTR_EXT_COMMUNITIES);
+ stream_putw(s, ecom_tr_size * 8);
+ } else {
+ stream_putc(
+ s,
+ BGP_ATTR_FLAG_OPTIONAL
+ | BGP_ATTR_FLAG_TRANS);
+ stream_putc(s,
+ BGP_ATTR_EXT_COMMUNITIES);
+ stream_putc(s, ecom_tr_size * 8);
+ }
+
+ for (i = 0; i < ecomm->size; i++) {
+ pnt = ecomm->val + (i * 8);
+ tbit = *pnt;
+
+ if (CHECK_FLAG(
+ tbit,
+ ECOMMUNITY_FLAG_NON_TRANSITIVE))
+ continue;
+
+ stream_put(s, pnt, 8);
+ }
+ }
+ }
+ }
+
+ /* Label index attribute. */
+ if (safi == SAFI_LABELED_UNICAST) {
+ if (attr->flag & ATTR_FLAG_BIT(BGP_ATTR_PREFIX_SID)) {
+ uint32_t label_index;
+
+ label_index = attr->label_index;
+
+ if (label_index != BGP_INVALID_LABEL_INDEX) {
+ stream_putc(s,
+ BGP_ATTR_FLAG_OPTIONAL
+ | BGP_ATTR_FLAG_TRANS);
+ stream_putc(s, BGP_ATTR_PREFIX_SID);
+ stream_putc(s, 10);
+ stream_putc(s, BGP_PREFIX_SID_LABEL_INDEX);
+ stream_putw(s,
+ BGP_PREFIX_SID_LABEL_INDEX_LENGTH);
+ stream_putc(s, 0); // reserved
+ stream_putw(s, 0); // flags
+ stream_putl(s, label_index);
+ }
+ }
+ }
+
+ /* SRv6 Service Information Attribute. */
+ if ((afi == AFI_IP || afi == AFI_IP6) && safi == SAFI_MPLS_VPN) {
+ if (attr->srv6_l3vpn) {
+ uint8_t subtlv_len =
+ BGP_PREFIX_SID_SRV6_L3_SERVICE_SID_STRUCTURE_LENGTH
+ + BGP_ATTR_MIN_LEN
+ + BGP_PREFIX_SID_SRV6_L3_SERVICE_SID_INFO_LENGTH;
+ uint8_t tlv_len = subtlv_len + BGP_ATTR_MIN_LEN + 1;
+ uint8_t attr_len = tlv_len + BGP_ATTR_MIN_LEN;
+ stream_putc(s, BGP_ATTR_FLAG_OPTIONAL
+ | BGP_ATTR_FLAG_TRANS);
+ stream_putc(s, BGP_ATTR_PREFIX_SID);
+ stream_putc(s, attr_len);
+ stream_putc(s, BGP_PREFIX_SID_SRV6_L3_SERVICE);
+ stream_putw(s, tlv_len);
+ stream_putc(s, 0); /* reserved */
+ stream_putc(s, BGP_PREFIX_SID_SRV6_L3_SERVICE_SID_INFO);
+ stream_putw(s, subtlv_len);
+ stream_putc(s, 0); /* reserved */
+ stream_put(s, &attr->srv6_l3vpn->sid,
+ sizeof(attr->srv6_l3vpn->sid)); /* sid */
+ stream_putc(s, 0); /* sid_flags */
+ stream_putw(s, 0xffff); /* endpoint */
+ stream_putc(s, 0); /* reserved */
+ stream_putc(
+ s,
+ BGP_PREFIX_SID_SRV6_L3_SERVICE_SID_STRUCTURE);
+ stream_putw(
+ s,
+ BGP_PREFIX_SID_SRV6_L3_SERVICE_SID_STRUCTURE_LENGTH);
+ stream_putc(s, attr->srv6_l3vpn->loc_block_len);
+ stream_putc(s, attr->srv6_l3vpn->loc_node_len);
+ stream_putc(s, attr->srv6_l3vpn->func_len);
+ stream_putc(s, attr->srv6_l3vpn->arg_len);
+ stream_putc(s, attr->srv6_l3vpn->transposition_len);
+ stream_putc(s, attr->srv6_l3vpn->transposition_offset);
+ } else if (attr->srv6_vpn) {
+ stream_putc(s, BGP_ATTR_FLAG_OPTIONAL
+ | BGP_ATTR_FLAG_TRANS);
+ stream_putc(s, BGP_ATTR_PREFIX_SID);
+ stream_putc(s, 22); /* tlv len */
+ stream_putc(s, BGP_PREFIX_SID_VPN_SID);
+ stream_putw(s, 0x13); /* tlv len */
+ stream_putc(s, 0x00); /* reserved */
+ stream_putc(s, 0x01); /* sid_type */
+ stream_putc(s, 0x00); /* sif_flags */
+ stream_put(s, &attr->srv6_vpn->sid,
+ sizeof(attr->srv6_vpn->sid)); /* sid */
+ }
+ }
+
+ if (send_as4_path) {
+ /* If the peer is NOT As4 capable, AND */
+ /* there are ASnums > 65535 in path THEN
+ * give out AS4_PATH */
+
+ /* Get rid of all AS_CONFED_SEQUENCE and AS_CONFED_SET
+ * path segments!
+ * Hm, I wonder... confederation things *should* only be at
+ * the beginning of an aspath, right? Then we should use
+ * aspath_delete_confed_seq for this, because it is already
+ * there! (JK)
+ * Folks, talk to me: what is reasonable here!?
+ */
+
+ /* Make sure dup aspath before the modification */
+ if (aspath == attr->aspath)
+ aspath = aspath_dup(attr->aspath);
+ aspath = aspath_delete_confed_seq(aspath);
+
+ stream_putc(s,
+ BGP_ATTR_FLAG_TRANS | BGP_ATTR_FLAG_OPTIONAL
+ | BGP_ATTR_FLAG_EXTLEN);
+ stream_putc(s, BGP_ATTR_AS4_PATH);
+ aspath_sizep = stream_get_endp(s);
+ stream_putw(s, 0);
+ stream_putw_at(s, aspath_sizep, aspath_put(s, aspath, 1));
+ }
+
+ if (aspath != attr->aspath)
+ aspath_free(aspath);
+
+ if (send_as4_aggregator) {
+ /* send AS4_AGGREGATOR, at this place */
+ /* this section of code moved here in order to ensure the
+ * correct
+ * *ascending* order of attributes
+ */
+ stream_putc(s, BGP_ATTR_FLAG_OPTIONAL | BGP_ATTR_FLAG_TRANS);
+ stream_putc(s, BGP_ATTR_AS4_AGGREGATOR);
+ stream_putc(s, 8);
+ stream_putl(s, attr->aggregator_as);
+ stream_put_ipv4(s, attr->aggregator_addr.s_addr);
+ }
+
+ if (((afi == AFI_IP || afi == AFI_IP6)
+ && (safi == SAFI_ENCAP || safi == SAFI_MPLS_VPN))
+ || (afi == AFI_L2VPN && safi == SAFI_EVPN)) {
+ /* Tunnel Encap attribute */
+ bgp_packet_mpattr_tea(bgp, peer, s, attr, BGP_ATTR_ENCAP);
+
+#ifdef ENABLE_BGP_VNC_ATTR
+ /* VNC attribute */
+ bgp_packet_mpattr_tea(bgp, peer, s, attr, BGP_ATTR_VNC);
+#endif
+ }
+
+ /* PMSI Tunnel */
+ if (attr->flag & ATTR_FLAG_BIT(BGP_ATTR_PMSI_TUNNEL)) {
+ stream_putc(s, BGP_ATTR_FLAG_OPTIONAL | BGP_ATTR_FLAG_TRANS);
+ stream_putc(s, BGP_ATTR_PMSI_TUNNEL);
+ stream_putc(s, 9); // Length
+ stream_putc(s, 0); // Flags
+ stream_putc(s, bgp_attr_get_pmsi_tnl_type(attr));
+ stream_put(s, &(attr->label),
+ BGP_LABEL_BYTES); // MPLS Label / VXLAN VNI
+ stream_put_ipv4(s, attr->nexthop.s_addr);
+ // Unicast tunnel endpoint IP address
+ }
+
+ /* OTC */
+ if (attr->flag & ATTR_FLAG_BIT(BGP_ATTR_OTC)) {
+ stream_putc(s, BGP_ATTR_FLAG_OPTIONAL | BGP_ATTR_FLAG_TRANS);
+ stream_putc(s, BGP_ATTR_OTC);
+ stream_putc(s, 4);
+ stream_putl(s, attr->otc);
+ }
+
+ /* Unknown transit attribute. */
+ struct transit *transit = bgp_attr_get_transit(attr);
+
+ if (transit)
+ stream_put(s, transit->val, transit->length);
+
+ /* Return total size of attribute. */
+ return stream_get_endp(s) - cp;
+}
+
+size_t bgp_packet_mpunreach_start(struct stream *s, afi_t afi, safi_t safi)
+{
+ unsigned long attrlen_pnt;
+ iana_afi_t pkt_afi = IANA_AFI_IPV4;
+ iana_safi_t pkt_safi = IANA_SAFI_UNICAST;
+
+ /* Set extended bit always to encode the attribute length as 2 bytes */
+ stream_putc(s, BGP_ATTR_FLAG_OPTIONAL | BGP_ATTR_FLAG_EXTLEN);
+ stream_putc(s, BGP_ATTR_MP_UNREACH_NLRI);
+
+ attrlen_pnt = stream_get_endp(s);
+ stream_putw(s, 0); /* Length of this attribute. */
+
+ /* Convert AFI, SAFI to values for packet. */
+ bgp_map_afi_safi_int2iana(afi, safi, &pkt_afi, &pkt_safi);
+
+ stream_putw(s, pkt_afi);
+ stream_putc(s, pkt_safi);
+
+ return attrlen_pnt;
+}
+
+void bgp_packet_mpunreach_prefix(struct stream *s, const struct prefix *p,
+ afi_t afi, safi_t safi,
+ const struct prefix_rd *prd,
+ mpls_label_t *label, uint32_t num_labels,
+ bool addpath_capable, uint32_t addpath_tx_id,
+ struct attr *attr)
+{
+ uint8_t wlabel[4] = {0x80, 0x00, 0x00};
+
+ if (safi == SAFI_LABELED_UNICAST) {
+ label = (mpls_label_t *)wlabel;
+ num_labels = 1;
+ }
+
+ bgp_packet_mpattr_prefix(s, afi, safi, p, prd, label, num_labels,
+ addpath_capable, addpath_tx_id, attr);
+}
+
+void bgp_packet_mpunreach_end(struct stream *s, size_t attrlen_pnt)
+{
+ bgp_packet_mpattr_end(s, attrlen_pnt);
+}
+
+/* Initialization of attribute. */
+void bgp_attr_init(void)
+{
+ aspath_init();
+ attrhash_init();
+ community_init();
+ ecommunity_init();
+ lcommunity_init();
+ cluster_init();
+ transit_init();
+ encap_init();
+ srv6_init();
+}
+
+void bgp_attr_finish(void)
+{
+ aspath_finish();
+ attrhash_finish();
+ community_finish();
+ ecommunity_finish();
+ lcommunity_finish();
+ cluster_finish();
+ transit_finish();
+ encap_finish();
+ srv6_finish();
+}
+
+/* Make attribute packet. */
+void bgp_dump_routes_attr(struct stream *s, struct attr *attr,
+ const struct prefix *prefix)
+{
+ unsigned long cp;
+ unsigned long len;
+ size_t aspath_lenp;
+ struct aspath *aspath;
+ bool addpath_capable = false;
+ uint32_t addpath_tx_id = 0;
+
+ /* Remember current pointer. */
+ cp = stream_get_endp(s);
+
+ /* Place holder of length. */
+ stream_putw(s, 0);
+
+ /* Origin attribute. */
+ stream_putc(s, BGP_ATTR_FLAG_TRANS);
+ stream_putc(s, BGP_ATTR_ORIGIN);
+ stream_putc(s, 1);
+ stream_putc(s, attr->origin);
+
+ aspath = attr->aspath;
+
+ stream_putc(s, BGP_ATTR_FLAG_TRANS | BGP_ATTR_FLAG_EXTLEN);
+ stream_putc(s, BGP_ATTR_AS_PATH);
+ aspath_lenp = stream_get_endp(s);
+ stream_putw(s, 0);
+
+ stream_putw_at(s, aspath_lenp, aspath_put(s, aspath, 1));
+
+ /* Nexthop attribute. */
+ /* If it's an IPv6 prefix, don't dump the IPv4 nexthop to save space */
+ if (prefix != NULL && prefix->family != AF_INET6) {
+ stream_putc(s, BGP_ATTR_FLAG_TRANS);
+ stream_putc(s, BGP_ATTR_NEXT_HOP);
+ stream_putc(s, 4);
+ stream_put_ipv4(s, attr->nexthop.s_addr);
+ }
+
+ /* MED attribute. */
+ if (attr->flag & ATTR_FLAG_BIT(BGP_ATTR_MULTI_EXIT_DISC)) {
+ stream_putc(s, BGP_ATTR_FLAG_OPTIONAL);
+ stream_putc(s, BGP_ATTR_MULTI_EXIT_DISC);
+ stream_putc(s, 4);
+ stream_putl(s, attr->med);
+ }
+
+ /* Local preference. */
+ if (attr->flag & ATTR_FLAG_BIT(BGP_ATTR_LOCAL_PREF)) {
+ stream_putc(s, BGP_ATTR_FLAG_TRANS);
+ stream_putc(s, BGP_ATTR_LOCAL_PREF);
+ stream_putc(s, 4);
+ stream_putl(s, attr->local_pref);
+ }
+
+ /* Atomic aggregate. */
+ if (attr->flag & ATTR_FLAG_BIT(BGP_ATTR_ATOMIC_AGGREGATE)) {
+ stream_putc(s, BGP_ATTR_FLAG_TRANS);
+ stream_putc(s, BGP_ATTR_ATOMIC_AGGREGATE);
+ stream_putc(s, 0);
+ }
+
+ /* Aggregator. */
+ if (attr->flag & ATTR_FLAG_BIT(BGP_ATTR_AGGREGATOR)) {
+ stream_putc(s, BGP_ATTR_FLAG_OPTIONAL | BGP_ATTR_FLAG_TRANS);
+ stream_putc(s, BGP_ATTR_AGGREGATOR);
+ stream_putc(s, 8);
+ stream_putl(s, attr->aggregator_as);
+ stream_put_ipv4(s, attr->aggregator_addr.s_addr);
+ }
+
+ /* Community attribute. */
+ if (attr->flag & ATTR_FLAG_BIT(BGP_ATTR_COMMUNITIES)) {
+ struct community *comm = NULL;
+
+ comm = bgp_attr_get_community(attr);
+ if (comm->size * 4 > 255) {
+ stream_putc(s,
+ BGP_ATTR_FLAG_OPTIONAL | BGP_ATTR_FLAG_TRANS
+ | BGP_ATTR_FLAG_EXTLEN);
+ stream_putc(s, BGP_ATTR_COMMUNITIES);
+ stream_putw(s, comm->size * 4);
+ } else {
+ stream_putc(s,
+ BGP_ATTR_FLAG_OPTIONAL
+ | BGP_ATTR_FLAG_TRANS);
+ stream_putc(s, BGP_ATTR_COMMUNITIES);
+ stream_putc(s, comm->size * 4);
+ }
+ stream_put(s, comm->val, comm->size * 4);
+ }
+
+ /* Large Community attribute. */
+ if (attr->flag & ATTR_FLAG_BIT(BGP_ATTR_LARGE_COMMUNITIES)) {
+ if (lcom_length(bgp_attr_get_lcommunity(attr)) > 255) {
+ stream_putc(s,
+ BGP_ATTR_FLAG_OPTIONAL | BGP_ATTR_FLAG_TRANS
+ | BGP_ATTR_FLAG_EXTLEN);
+ stream_putc(s, BGP_ATTR_LARGE_COMMUNITIES);
+ stream_putw(s,
+ lcom_length(bgp_attr_get_lcommunity(attr)));
+ } else {
+ stream_putc(s,
+ BGP_ATTR_FLAG_OPTIONAL
+ | BGP_ATTR_FLAG_TRANS);
+ stream_putc(s, BGP_ATTR_LARGE_COMMUNITIES);
+ stream_putc(s,
+ lcom_length(bgp_attr_get_lcommunity(attr)));
+ }
+
+ stream_put(s, bgp_attr_get_lcommunity(attr)->val,
+ lcom_length(bgp_attr_get_lcommunity(attr)));
+ }
+
+ /* Add a MP_NLRI attribute to dump the IPv6 next hop */
+ if (prefix != NULL && prefix->family == AF_INET6
+ && (attr->mp_nexthop_len == BGP_ATTR_NHLEN_IPV6_GLOBAL
+ || attr->mp_nexthop_len == BGP_ATTR_NHLEN_IPV6_GLOBAL_AND_LL)) {
+ int sizep;
+
+ stream_putc(s, BGP_ATTR_FLAG_OPTIONAL);
+ stream_putc(s, BGP_ATTR_MP_REACH_NLRI);
+ sizep = stream_get_endp(s);
+
+ /* MP header */
+ stream_putc(s, 0); /* Marker: Attribute length. */
+ stream_putw(s, AFI_IP6); /* AFI */
+ stream_putc(s, SAFI_UNICAST); /* SAFI */
+
+ /* Next hop */
+ stream_putc(s, attr->mp_nexthop_len);
+ stream_put(s, &attr->mp_nexthop_global, IPV6_MAX_BYTELEN);
+ if (attr->mp_nexthop_len == BGP_ATTR_NHLEN_IPV6_GLOBAL_AND_LL)
+ stream_put(s, &attr->mp_nexthop_local,
+ IPV6_MAX_BYTELEN);
+
+ /* SNPA */
+ stream_putc(s, 0);
+
+ /* Prefix */
+ stream_put_prefix_addpath(s, prefix, addpath_capable,
+ addpath_tx_id);
+
+ /* Set MP attribute length. */
+ stream_putc_at(s, sizep, (stream_get_endp(s) - sizep) - 1);
+ }
+
+ /* Prefix SID */
+ if (attr->flag & ATTR_FLAG_BIT(BGP_ATTR_PREFIX_SID)) {
+ if (attr->label_index != BGP_INVALID_LABEL_INDEX) {
+ stream_putc(s,
+ BGP_ATTR_FLAG_OPTIONAL
+ | BGP_ATTR_FLAG_TRANS);
+ stream_putc(s, BGP_ATTR_PREFIX_SID);
+ stream_putc(s, 10);
+ stream_putc(s, BGP_PREFIX_SID_LABEL_INDEX);
+ stream_putc(s, BGP_PREFIX_SID_LABEL_INDEX_LENGTH);
+ stream_putc(s, 0); // reserved
+ stream_putw(s, 0); // flags
+ stream_putl(s, attr->label_index);
+ }
+ }
+
+ /* OTC */
+ if (attr->flag & ATTR_FLAG_BIT(BGP_ATTR_OTC)) {
+ stream_putc(s, BGP_ATTR_FLAG_OPTIONAL | BGP_ATTR_FLAG_TRANS);
+ stream_putc(s, BGP_ATTR_OTC);
+ stream_putc(s, 4);
+ stream_putl(s, attr->otc);
+ }
+
+ /* Return total size of attribute. */
+ len = stream_get_endp(s) - cp - 2;
+ stream_putw_at(s, cp, len);
+}
diff --git a/bgpd/bgp_attr.h b/bgpd/bgp_attr.h
new file mode 100644
index 0000000..4963ea6
--- /dev/null
+++ b/bgpd/bgp_attr.h
@@ -0,0 +1,638 @@
+/* BGP attributes.
+ * Copyright (C) 1996, 97, 98 Kunihiro Ishiguro
+ *
+ * This file is part of GNU Zebra.
+ *
+ * GNU Zebra is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License as published by the
+ * Free Software Foundation; either version 2, or (at your option) any
+ * later version.
+ *
+ * GNU Zebra is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program; see the file COPYING; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
+ */
+
+#ifndef _QUAGGA_BGP_ATTR_H
+#define _QUAGGA_BGP_ATTR_H
+
+#include "mpls.h"
+#include "bgp_attr_evpn.h"
+#include "bgpd/bgp_encap_types.h"
+#include "srte.h"
+
+/* Simple bit mapping. */
+#define BITMAP_NBBY 8
+
+#define SET_BITMAP(MAP, NUM) \
+ SET_FLAG(MAP[(NUM) / BITMAP_NBBY], 1 << ((NUM) % BITMAP_NBBY))
+
+#define CHECK_BITMAP(MAP, NUM) \
+ CHECK_FLAG(MAP[(NUM) / BITMAP_NBBY], 1 << ((NUM) % BITMAP_NBBY))
+
+#define BGP_MED_MAX UINT32_MAX
+
+/* BGP Attribute type range. */
+#define BGP_ATTR_TYPE_RANGE 256
+#define BGP_ATTR_BITMAP_SIZE (BGP_ATTR_TYPE_RANGE / BITMAP_NBBY)
+
+/* BGP Attribute flags. */
+#define BGP_ATTR_FLAG_OPTIONAL 0x80 /* Attribute is optional. */
+#define BGP_ATTR_FLAG_TRANS 0x40 /* Attribute is transitive. */
+#define BGP_ATTR_FLAG_PARTIAL 0x20 /* Attribute is partial. */
+#define BGP_ATTR_FLAG_EXTLEN 0x10 /* Extended length flag. */
+
+/* BGP attribute header must bigger than 2. */
+#define BGP_ATTR_MIN_LEN 3 /* Attribute flag, type length. */
+#define BGP_ATTR_DEFAULT_WEIGHT 32768
+
+/* Valid lengths for mp_nexthop_len */
+#define BGP_ATTR_NHLEN_IPV4 IPV4_MAX_BYTELEN
+#define BGP_ATTR_NHLEN_VPNV4 8+IPV4_MAX_BYTELEN
+#define BGP_ATTR_NHLEN_IPV6_GLOBAL IPV6_MAX_BYTELEN
+#define BGP_ATTR_NHLEN_IPV6_GLOBAL_AND_LL (IPV6_MAX_BYTELEN * 2)
+#define BGP_ATTR_NHLEN_VPNV6_GLOBAL 8+IPV6_MAX_BYTELEN
+#define BGP_ATTR_NHLEN_VPNV6_GLOBAL_AND_LL ((8+IPV6_MAX_BYTELEN) * 2)
+
+/* Prefix SID types */
+#define BGP_PREFIX_SID_LABEL_INDEX 1
+#define BGP_PREFIX_SID_IPV6 2
+#define BGP_PREFIX_SID_ORIGINATOR_SRGB 3
+#define BGP_PREFIX_SID_VPN_SID 4
+#define BGP_PREFIX_SID_SRV6_L3_SERVICE 5
+#define BGP_PREFIX_SID_SRV6_L2_SERVICE 6
+
+#define BGP_PREFIX_SID_LABEL_INDEX_LENGTH 7
+#define BGP_PREFIX_SID_IPV6_LENGTH 19
+#define BGP_PREFIX_SID_ORIGINATOR_SRGB_LENGTH 6
+#define BGP_PREFIX_SID_VPN_SID_LENGTH 19
+
+/* SRv6 Service Sub-TLV types */
+#define BGP_PREFIX_SID_SRV6_L3_SERVICE_SID_INFO 1
+#define BGP_PREFIX_SID_SRV6_L3_SERVICE_SID_INFO_LENGTH 21
+
+/* SRv6 Service Data Sub-Sub-TLV types */
+#define BGP_PREFIX_SID_SRV6_L3_SERVICE_SID_STRUCTURE 1
+#define BGP_PREFIX_SID_SRV6_L3_SERVICE_SID_STRUCTURE_LENGTH 6
+
+/* SRv6 SID Structure default values */
+#define BGP_PREFIX_SID_SRV6_LOCATOR_BLOCK_LENGTH 40
+#define BGP_PREFIX_SID_SRV6_LOCATOR_NODE_LENGTH 24
+#define BGP_PREFIX_SID_SRV6_FUNCTION_LENGTH 16
+#define BGP_PREFIX_SID_SRV6_ARGUMENT_LENGTH 0
+#define BGP_PREFIX_SID_SRV6_TRANSPOSITION_LENGTH 16
+#define BGP_PREFIX_SID_SRV6_TRANSPOSITION_OFFSET 64
+
+#define BGP_ATTR_NH_AFI(afi, attr) \
+ ((afi != AFI_L2VPN) ? afi : \
+ ((attr->mp_nexthop_len == BGP_ATTR_NHLEN_IPV4) ? AFI_IP : AFI_IP6))
+
+/* PMSI tunnel types (RFC 6514) */
+
+struct bgp_attr_encap_subtlv {
+ struct bgp_attr_encap_subtlv *next; /* for chaining */
+ /* Reference count of this attribute. */
+ unsigned long refcnt;
+ uint16_t type;
+ uint16_t length;
+ uint8_t value[0]; /* will be extended */
+};
+
+#ifdef ENABLE_BGP_VNC
+/*
+ * old rfp<->rfapi representation
+ */
+struct bgp_tea_options {
+ struct bgp_tea_options *next;
+ uint8_t options_count;
+ uint16_t options_length; /* each TLV may be 256 in length */
+ uint8_t type;
+ uint8_t length;
+ void *value; /* pointer to data */
+};
+
+#endif
+
+enum pta_type {
+ PMSI_TNLTYPE_NO_INFO = 0,
+ PMSI_TNLTYPE_RSVP_TE_P2MP,
+ PMSI_TNLTYPE_MLDP_P2MP,
+ PMSI_TNLTYPE_PIM_SSM,
+ PMSI_TNLTYPE_PIM_SM,
+ PMSI_TNLTYPE_PIM_BIDIR,
+ PMSI_TNLTYPE_INGR_REPL,
+ PMSI_TNLTYPE_MLDP_MP2MP,
+ PMSI_TNLTYPE_MAX = PMSI_TNLTYPE_MLDP_MP2MP
+};
+
+/*
+ * Prefix-SID type-4
+ * SRv6-VPN-SID-TLV
+ * draft-dawra-idr-srv6-vpn-04
+ */
+struct bgp_attr_srv6_vpn {
+ unsigned long refcnt;
+ uint8_t sid_flags;
+ struct in6_addr sid;
+};
+
+/*
+ * Prefix-SID type-5
+ * SRv6-L3VPN-Service-TLV
+ * draft-dawra-idr-srv6-vpn-05
+ */
+struct bgp_attr_srv6_l3vpn {
+ unsigned long refcnt;
+ uint8_t sid_flags;
+ uint16_t endpoint_behavior;
+ struct in6_addr sid;
+ uint8_t loc_block_len;
+ uint8_t loc_node_len;
+ uint8_t func_len;
+ uint8_t arg_len;
+ uint8_t transposition_len;
+ uint8_t transposition_offset;
+};
+
+/* BGP core attribute structure. */
+struct attr {
+ /* AS Path structure */
+ struct aspath *aspath;
+
+ /* Community structure */
+ struct community *community;
+
+ /* Reference count of this attribute. */
+ unsigned long refcnt;
+
+ /* Flag of attribute is set or not. */
+ uint64_t flag;
+
+ /* Apart from in6_addr, the remaining static attributes */
+ struct in_addr nexthop;
+ uint32_t med;
+ uint32_t local_pref;
+ ifindex_t nh_ifindex;
+
+ /* Path origin attribute */
+ uint8_t origin;
+
+ /* PMSI tunnel type (RFC 6514). */
+ enum pta_type pmsi_tnl_type;
+
+ /* has the route-map changed any attribute?
+ Used on the peer outbound side. */
+ uint32_t rmap_change_flags;
+
+ /* Multi-Protocol Nexthop, AFI IPv6 */
+ struct in6_addr mp_nexthop_global;
+ struct in6_addr mp_nexthop_local;
+
+ /* ifIndex corresponding to mp_nexthop_local. */
+ ifindex_t nh_lla_ifindex;
+
+ /* Extended Communities attribute. */
+ struct ecommunity *ecommunity;
+
+ /* Extended Communities attribute. */
+ struct ecommunity *ipv6_ecommunity;
+
+ /* Large Communities attribute. */
+ struct lcommunity *lcommunity;
+
+ /* Route-Reflector Cluster attribute */
+ struct cluster_list *cluster1;
+
+ /* Unknown transitive attribute. */
+ struct transit *transit;
+
+ struct in_addr mp_nexthop_global_in;
+
+ /* Aggregator Router ID attribute */
+ struct in_addr aggregator_addr;
+
+ /* Route Reflector Originator attribute */
+ struct in_addr originator_id;
+
+ /* Local weight, not actually an attribute */
+ uint32_t weight;
+
+ /* Aggregator ASN */
+ as_t aggregator_as;
+
+ /* MP Nexthop length */
+ uint8_t mp_nexthop_len;
+
+ /* MP Nexthop preference */
+ uint8_t mp_nexthop_prefer_global;
+
+ /* Static MAC for EVPN */
+ uint8_t sticky;
+
+ /* Flag for default gateway extended community in EVPN */
+ uint8_t default_gw;
+
+ /* NA router flag (R-bit) support in EVPN */
+ uint8_t router_flag;
+
+ /* ES info */
+ uint8_t es_flags;
+ /* Path is not "locally-active" on the advertising VTEP. This is
+ * translated into an ARP-ND ECOM.
+ */
+#define ATTR_ES_PROXY_ADVERT (1 << 0)
+ /* Destination ES is present locally. This flag is set on local
+ * paths and sync paths
+ */
+#define ATTR_ES_IS_LOCAL (1 << 1)
+ /* There are one or more non-best paths from ES peers. Note that
+ * this flag is only set on the local MAC-IP paths in the VNI
+ * route table (not set in the global routing table). And only
+ * non-proxy advertisements from an ES peer can result in this
+ * flag being set.
+ */
+#define ATTR_ES_PEER_ACTIVE (1 << 2)
+ /* There are one or more non-best proxy paths from ES peers */
+#define ATTR_ES_PEER_PROXY (1 << 3)
+ /* An ES peer has router bit set - only applicable if
+ * ATTR_ES_PEER_ACTIVE is set
+ */
+#define ATTR_ES_PEER_ROUTER (1 << 4)
+
+ /* These two flags are only set on L3 routes installed in a
+ * VRF as a result of EVPN MAC-IP route
+ * XXX - while splitting up per-family attrs these need to be
+ * classified as non-EVPN
+ */
+#define ATTR_ES_L3_NHG_USE (1 << 5)
+#define ATTR_ES_L3_NHG_ACTIVE (1 << 6)
+#define ATTR_ES_L3_NHG (ATTR_ES_L3_NHG_USE | ATTR_ES_L3_NHG_ACTIVE)
+
+ /* route tag */
+ route_tag_t tag;
+
+ /* Label index */
+ uint32_t label_index;
+
+ /* MPLS label */
+ mpls_label_t label;
+
+ /* SRv6 VPN SID */
+ struct bgp_attr_srv6_vpn *srv6_vpn;
+
+ /* SRv6 L3VPN SID */
+ struct bgp_attr_srv6_l3vpn *srv6_l3vpn;
+
+ uint16_t encap_tunneltype; /* grr */
+ struct bgp_attr_encap_subtlv *encap_subtlvs; /* rfc5512 */
+
+#ifdef ENABLE_BGP_VNC
+ struct bgp_attr_encap_subtlv *vnc_subtlvs; /* VNC-specific */
+#endif
+ /* EVPN */
+ struct bgp_route_evpn evpn_overlay;
+
+ /* EVPN MAC Mobility sequence number, if any. */
+ uint32_t mm_seqnum;
+ /* highest MM sequence number rxed in a MAC-IP route from an
+ * ES peer (this includes both proxy and non-proxy MAC-IP
+ * advertisements from ES peers).
+ * This is only applicable to local paths in the VNI routing
+ * table and derived from other imported/non-best paths.
+ */
+ uint32_t mm_sync_seqnum;
+
+ /* EVPN local router-mac */
+ struct ethaddr rmac;
+
+ /* Distance as applied by Route map */
+ uint8_t distance;
+
+ /* rmap set table */
+ uint32_t rmap_table_id;
+
+ /* Link bandwidth value, if any. */
+ uint32_t link_bw;
+
+ /* EVPN ES */
+ esi_t esi;
+
+ /* SR-TE Color */
+ uint32_t srte_color;
+
+ /* EVPN DF preference and algorithm for DF election on local ESs */
+ uint16_t df_pref;
+ uint8_t df_alg;
+
+ /* Nexthop type */
+ enum nexthop_types_t nh_type;
+
+ /* If NEXTHOP_TYPE_BLACKHOLE, then blackhole type */
+ enum blackhole_type bh_type;
+
+ /* OTC value if set */
+ uint32_t otc;
+};
+
+/* rmap_change_flags definition */
+#define BATTR_RMAP_IPV4_NHOP_CHANGED (1 << 0)
+#define BATTR_RMAP_NEXTHOP_PEER_ADDRESS (1 << 1)
+#define BATTR_REFLECTED (1 << 2)
+#define BATTR_RMAP_NEXTHOP_UNCHANGED (1 << 3)
+#define BATTR_RMAP_IPV6_GLOBAL_NHOP_CHANGED (1 << 4)
+#define BATTR_RMAP_IPV6_LL_NHOP_CHANGED (1 << 5)
+#define BATTR_RMAP_IPV6_PREFER_GLOBAL_CHANGED (1 << 6)
+#define BATTR_RMAP_LINK_BW_SET (1 << 7)
+#define BATTR_RMAP_L3VPN_ACCEPT_GRE (1 << 8)
+
+/* Router Reflector related structure. */
+struct cluster_list {
+ unsigned long refcnt;
+ int length;
+ struct in_addr *list;
+};
+
+/* Unknown transit attribute. */
+struct transit {
+ unsigned long refcnt;
+ int length;
+ uint8_t *val;
+};
+
+/* "(void) 0" will generate a compiler error. this is a safety check to
+ * ensure we're not using a value that exceeds the bit size of attr->flag. */
+#define ATTR_FLAG_BIT(X) \
+ __builtin_choose_expr((X) >= 1 && (X) <= 64, 1ULL << ((X)-1), (void)0)
+
+#define BGP_CLUSTER_LIST_LENGTH(attr) \
+ (((attr)->flag & ATTR_FLAG_BIT(BGP_ATTR_CLUSTER_LIST)) \
+ ? bgp_attr_get_cluster((attr))->length \
+ : 0)
+
+enum bgp_attr_parse_ret {
+ BGP_ATTR_PARSE_PROCEED = 0,
+ BGP_ATTR_PARSE_ERROR = -1,
+ BGP_ATTR_PARSE_WITHDRAW = -2,
+
+ /* only used internally, send notify + convert to BGP_ATTR_PARSE_ERROR
+ */
+ BGP_ATTR_PARSE_ERROR_NOTIFYPLS = -3,
+ BGP_ATTR_PARSE_EOR = -4,
+};
+
+struct bpacket_attr_vec_arr;
+
+/* Prototypes. */
+extern void bgp_attr_init(void);
+extern void bgp_attr_finish(void);
+extern enum bgp_attr_parse_ret
+bgp_attr_parse(struct peer *peer, struct attr *attr, bgp_size_t size,
+ struct bgp_nlri *mp_update, struct bgp_nlri *mp_withdraw);
+extern struct attr *bgp_attr_intern(struct attr *attr);
+extern void bgp_attr_unintern_sub(struct attr *attr);
+extern void bgp_attr_unintern(struct attr **pattr);
+extern void bgp_attr_flush(struct attr *attr);
+extern struct attr *bgp_attr_default_set(struct attr *attr, struct bgp *bgp,
+ uint8_t origin);
+extern struct attr *bgp_attr_aggregate_intern(
+ struct bgp *bgp, uint8_t origin, struct aspath *aspath,
+ struct community *community, struct ecommunity *ecommunity,
+ struct lcommunity *lcommunity, struct bgp_aggregate *aggregate,
+ uint8_t atomic_aggregate, const struct prefix *p);
+extern bgp_size_t bgp_packet_attribute(struct bgp *bgp, struct peer *peer,
+ struct stream *s, struct attr *attr,
+ struct bpacket_attr_vec_arr *vecarr,
+ struct prefix *p, afi_t afi, safi_t safi,
+ struct peer *from, struct prefix_rd *prd,
+ mpls_label_t *label, uint32_t num_labels,
+ bool addpath_capable,
+ uint32_t addpath_tx_id);
+extern void bgp_dump_routes_attr(struct stream *s, struct attr *attr,
+ const struct prefix *p);
+extern bool attrhash_cmp(const void *arg1, const void *arg2);
+extern unsigned int attrhash_key_make(const void *p);
+extern void attr_show_all(struct vty *vty);
+extern unsigned long int attr_count(void);
+extern unsigned long int attr_unknown_count(void);
+
+/* Cluster list prototypes. */
+extern bool cluster_loop_check(struct cluster_list *cluster,
+ struct in_addr originator);
+
+/* Below exported for unit-test purposes only */
+struct bgp_attr_parser_args {
+ struct peer *peer;
+ bgp_size_t length; /* attribute data length; */
+ bgp_size_t total; /* total length, inc header */
+ struct attr *attr;
+ uint8_t type;
+ uint8_t flags;
+ uint8_t *startp;
+};
+extern int bgp_mp_reach_parse(struct bgp_attr_parser_args *args,
+ struct bgp_nlri *mp_update);
+extern int bgp_mp_unreach_parse(struct bgp_attr_parser_args *args,
+ struct bgp_nlri *mp_withdraw);
+extern enum bgp_attr_parse_ret
+bgp_attr_prefix_sid(struct bgp_attr_parser_args *args);
+
+extern struct bgp_attr_encap_subtlv *
+encap_tlv_dup(struct bgp_attr_encap_subtlv *orig);
+
+extern void bgp_attr_flush_encap(struct attr *attr);
+
+extern void bgp_attr_extcom_tunnel_type(struct attr *attr,
+ bgp_encap_types *tunnel_type);
+
+/**
+ * Set of functions to encode MP_REACH_NLRI and MP_UNREACH_NLRI attributes.
+ * Typical call sequence is to call _start(), followed by multiple _prefix(),
+ * one for each NLRI that needs to be encoded into the UPDATE message, and
+ * finally the _end() function.
+ */
+extern size_t bgp_packet_mpattr_start(struct stream *s, struct peer *peer,
+ afi_t afi, safi_t safi,
+ struct bpacket_attr_vec_arr *vecarr,
+ struct attr *attr);
+extern void bgp_packet_mpattr_prefix(struct stream *s, afi_t afi, safi_t safi,
+ const struct prefix *p,
+ const struct prefix_rd *prd,
+ mpls_label_t *label, uint32_t num_labels,
+ bool addpath_capable,
+ uint32_t addpath_tx_id, struct attr *);
+extern size_t bgp_packet_mpattr_prefix_size(afi_t afi, safi_t safi,
+ const struct prefix *p);
+extern void bgp_packet_mpattr_end(struct stream *s, size_t sizep);
+
+extern size_t bgp_packet_mpunreach_start(struct stream *s, afi_t afi,
+ safi_t safi);
+extern void bgp_packet_mpunreach_prefix(
+ struct stream *s, const struct prefix *p, afi_t afi, safi_t safi,
+ const struct prefix_rd *prd, mpls_label_t *label, uint32_t num_labels,
+ bool addpath_capable, uint32_t addpath_tx_id, struct attr *attr);
+extern void bgp_packet_mpunreach_end(struct stream *s, size_t attrlen_pnt);
+
+extern enum bgp_attr_parse_ret bgp_attr_nexthop_valid(struct peer *peer,
+ struct attr *attr);
+
+static inline int bgp_rmap_nhop_changed(uint32_t out_rmap_flags,
+ uint32_t in_rmap_flags)
+{
+ return ((CHECK_FLAG(out_rmap_flags, BATTR_RMAP_NEXTHOP_PEER_ADDRESS)
+ || CHECK_FLAG(out_rmap_flags, BATTR_RMAP_NEXTHOP_UNCHANGED)
+ || CHECK_FLAG(out_rmap_flags, BATTR_RMAP_IPV4_NHOP_CHANGED)
+ || CHECK_FLAG(out_rmap_flags,
+ BATTR_RMAP_IPV6_GLOBAL_NHOP_CHANGED)
+ || CHECK_FLAG(out_rmap_flags,
+ BATTR_RMAP_IPV6_PREFER_GLOBAL_CHANGED)
+ || CHECK_FLAG(out_rmap_flags, BATTR_RMAP_IPV6_LL_NHOP_CHANGED)
+ || CHECK_FLAG(in_rmap_flags, BATTR_RMAP_NEXTHOP_UNCHANGED))
+ ? 1
+ : 0);
+}
+
+static inline uint32_t mac_mobility_seqnum(struct attr *attr)
+{
+ return (attr) ? attr->mm_seqnum : 0;
+}
+
+static inline enum pta_type bgp_attr_get_pmsi_tnl_type(struct attr *attr)
+{
+ return attr->pmsi_tnl_type;
+}
+
+static inline void bgp_attr_set_pmsi_tnl_type(struct attr *attr,
+ enum pta_type pmsi_tnl_type)
+{
+ attr->pmsi_tnl_type = pmsi_tnl_type;
+}
+
+static inline struct ecommunity *
+bgp_attr_get_ecommunity(const struct attr *attr)
+{
+ return attr->ecommunity;
+}
+
+static inline void bgp_attr_set_ecommunity(struct attr *attr,
+ struct ecommunity *ecomm)
+{
+ attr->ecommunity = ecomm;
+
+ if (ecomm)
+ SET_FLAG(attr->flag, ATTR_FLAG_BIT(BGP_ATTR_EXT_COMMUNITIES));
+ else
+ UNSET_FLAG(attr->flag, ATTR_FLAG_BIT(BGP_ATTR_EXT_COMMUNITIES));
+}
+
+static inline struct lcommunity *
+bgp_attr_get_lcommunity(const struct attr *attr)
+{
+ return attr->lcommunity;
+}
+
+static inline void bgp_attr_set_lcommunity(struct attr *attr,
+ struct lcommunity *lcomm)
+{
+ attr->lcommunity = lcomm;
+
+ if (lcomm)
+ SET_FLAG(attr->flag, ATTR_FLAG_BIT(BGP_ATTR_LARGE_COMMUNITIES));
+ else
+ UNSET_FLAG(attr->flag,
+ ATTR_FLAG_BIT(BGP_ATTR_LARGE_COMMUNITIES));
+}
+
+static inline struct community *bgp_attr_get_community(const struct attr *attr)
+{
+ return attr->community;
+}
+
+static inline void bgp_attr_set_community(struct attr *attr,
+ struct community *comm)
+{
+ attr->community = comm;
+
+ if (comm)
+ SET_FLAG(attr->flag, ATTR_FLAG_BIT(BGP_ATTR_COMMUNITIES));
+ else
+ UNSET_FLAG(attr->flag, ATTR_FLAG_BIT(BGP_ATTR_COMMUNITIES));
+}
+
+static inline struct ecommunity *
+bgp_attr_get_ipv6_ecommunity(const struct attr *attr)
+{
+ return attr->ipv6_ecommunity;
+}
+
+static inline void bgp_attr_set_ipv6_ecommunity(struct attr *attr,
+ struct ecommunity *ipv6_ecomm)
+{
+ attr->ipv6_ecommunity = ipv6_ecomm;
+
+ if (ipv6_ecomm)
+ SET_FLAG(attr->flag,
+ ATTR_FLAG_BIT(BGP_ATTR_IPV6_EXT_COMMUNITIES));
+ else
+ UNSET_FLAG(attr->flag,
+ ATTR_FLAG_BIT(BGP_ATTR_IPV6_EXT_COMMUNITIES));
+}
+
+static inline struct transit *bgp_attr_get_transit(const struct attr *attr)
+{
+ return attr->transit;
+}
+
+static inline void bgp_attr_set_transit(struct attr *attr,
+ struct transit *transit)
+{
+ attr->transit = transit;
+}
+
+static inline struct cluster_list *bgp_attr_get_cluster(const struct attr *attr)
+{
+ return attr->cluster1;
+}
+
+static inline void bgp_attr_set_cluster(struct attr *attr,
+ struct cluster_list *cl)
+{
+ attr->cluster1 = cl;
+}
+
+static inline const struct bgp_route_evpn *
+bgp_attr_get_evpn_overlay(const struct attr *attr)
+{
+ return &attr->evpn_overlay;
+}
+
+static inline void bgp_attr_set_evpn_overlay(struct attr *attr,
+ struct bgp_route_evpn *eo)
+{
+ memcpy(&attr->evpn_overlay, eo, sizeof(struct bgp_route_evpn));
+}
+
+static inline struct bgp_attr_encap_subtlv *
+bgp_attr_get_vnc_subtlvs(const struct attr *attr)
+{
+#ifdef ENABLE_BGP_VNC
+ return attr->vnc_subtlvs;
+#else
+ return NULL;
+#endif
+}
+
+static inline void
+bgp_attr_set_vnc_subtlvs(struct attr *attr,
+ struct bgp_attr_encap_subtlv *vnc_subtlvs)
+{
+#ifdef ENABLE_BGP_VNC
+ attr->vnc_subtlvs = vnc_subtlvs;
+#endif
+}
+
+#endif /* _QUAGGA_BGP_ATTR_H */
diff --git a/bgpd/bgp_attr_evpn.c b/bgpd/bgp_attr_evpn.c
new file mode 100644
index 0000000..d379f40
--- /dev/null
+++ b/bgpd/bgp_attr_evpn.c
@@ -0,0 +1,317 @@
+/* Ethernet-VPN Attribute handling file
+ * Copyright (C) 2016 6WIND
+ *
+ * This file is part of FRRouting.
+ *
+ * FRRouting is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License as published by the
+ * Free Software Foundation; either version 2, or (at your option) any
+ * later version.
+ *
+ * FRRouting is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program; see the file COPYING; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
+ */
+
+#include <zebra.h>
+
+#include "command.h"
+#include "filter.h"
+#include "prefix.h"
+#include "log.h"
+#include "memory.h"
+#include "stream.h"
+#include "vxlan.h"
+
+#include "bgpd/bgpd.h"
+#include "bgpd/bgp_attr.h"
+#include "bgpd/bgp_route.h"
+#include "bgpd/bgp_attr_evpn.h"
+#include "bgpd/bgp_ecommunity.h"
+#include "bgpd/bgp_evpn.h"
+#include "bgpd/bgp_evpn_private.h"
+
+bool bgp_route_evpn_same(const struct bgp_route_evpn *e1,
+ const struct bgp_route_evpn *e2)
+{
+ return (e1->type == e2->type &&
+ !memcmp(&(e1->eth_s_id), &(e2->eth_s_id), sizeof(esi_t)) &&
+ !ipaddr_cmp(&(e1->gw_ip), &(e2->gw_ip)));
+}
+
+void bgp_add_routermac_ecom(struct attr *attr, struct ethaddr *routermac)
+{
+ struct ecommunity_val routermac_ecom;
+ struct ecommunity *ecomm = bgp_attr_get_ecommunity(attr);
+
+ memset(&routermac_ecom, 0, sizeof(routermac_ecom));
+ routermac_ecom.val[0] = ECOMMUNITY_ENCODE_EVPN;
+ routermac_ecom.val[1] = ECOMMUNITY_EVPN_SUBTYPE_ROUTERMAC;
+ memcpy(&routermac_ecom.val[2], routermac->octet, ETH_ALEN);
+ if (!ecomm) {
+ bgp_attr_set_ecommunity(attr, ecommunity_new());
+ ecomm = bgp_attr_get_ecommunity(attr);
+ }
+ ecommunity_add_val(ecomm, &routermac_ecom, false, false);
+ ecommunity_str(ecomm);
+}
+
+/* converts to an esi
+ * returns 1 on success, 0 otherwise
+ * format accepted: AA:BB:CC:DD:EE:FF:GG:HH:II:JJ
+ * if id is null, check only is done
+ */
+bool str2esi(const char *str, esi_t *id)
+{
+ unsigned int a[ESI_BYTES];
+ int i;
+
+ if (!str)
+ return false;
+ if (sscanf(str, "%2x:%2x:%2x:%2x:%2x:%2x:%2x:%2x:%2x:%2x", a + 0, a + 1,
+ a + 2, a + 3, a + 4, a + 5, a + 6, a + 7, a + 8, a + 9)
+ != ESI_BYTES) {
+ /* error in incoming str length */
+ return false;
+ }
+ /* valid mac address */
+ if (!id)
+ return true;
+ for (i = 0; i < ESI_BYTES; ++i)
+ id->val[i] = a[i] & 0xff;
+ return true;
+}
+
+char *ecom_mac2str(char *ecom_mac)
+{
+ char *en;
+
+ en = ecom_mac;
+ en += 2;
+
+ return prefix_mac2str((struct ethaddr *)en, NULL, 0);
+}
+
+/* Fetch router-mac from extended community */
+bool bgp_attr_rmac(struct attr *attr, struct ethaddr *rmac)
+{
+ uint32_t i = 0;
+ struct ecommunity *ecom;
+
+ ecom = bgp_attr_get_ecommunity(attr);
+ if (!ecom || !ecom->size)
+ return false;
+
+ /* If there is a router mac extended community, set RMAC in attr */
+ for (i = 0; i < ecom->size; i++) {
+ uint8_t *pnt = NULL;
+ uint8_t type = 0;
+ uint8_t sub_type = 0;
+
+ pnt = (ecom->val + (i * ECOMMUNITY_SIZE));
+ type = *pnt++;
+ sub_type = *pnt++;
+
+ if (!(type == ECOMMUNITY_ENCODE_EVPN
+ && sub_type == ECOMMUNITY_EVPN_SUBTYPE_ROUTERMAC))
+ continue;
+
+ memcpy(rmac, pnt, ETH_ALEN);
+ return true;
+ }
+ return false;
+}
+
+/*
+ * return true if attr contains default gw extended community
+ */
+uint8_t bgp_attr_default_gw(struct attr *attr)
+{
+ struct ecommunity *ecom;
+ uint32_t i;
+
+ ecom = bgp_attr_get_ecommunity(attr);
+ if (!ecom || !ecom->size)
+ return 0;
+
+ /* If there is a default gw extendd community return true otherwise
+ * return 0 */
+ for (i = 0; i < ecom->size; i++) {
+ uint8_t *pnt;
+ uint8_t type, sub_type;
+
+ pnt = (ecom->val + (i * ECOMMUNITY_SIZE));
+ type = *pnt++;
+ sub_type = *pnt++;
+
+ if ((type == ECOMMUNITY_ENCODE_OPAQUE
+ && sub_type == ECOMMUNITY_EVPN_SUBTYPE_DEF_GW))
+ return 1;
+ }
+
+ return 0;
+}
+
+/*
+ * Fetch and return the DF preference and algorithm from
+ * DF election extended community, if present, else 0.
+ */
+uint16_t bgp_attr_df_pref_from_ec(struct attr *attr, uint8_t *alg)
+{
+ struct ecommunity *ecom;
+ uint32_t i;
+ uint16_t df_pref = 0;
+
+ *alg = EVPN_MH_DF_ALG_SERVICE_CARVING;
+ ecom = bgp_attr_get_ecommunity(attr);
+ if (!ecom || !ecom->size)
+ return 0;
+
+ for (i = 0; i < ecom->size; i++) {
+ uint8_t *pnt;
+ uint8_t type, sub_type;
+
+ pnt = (ecom->val + (i * ECOMMUNITY_SIZE));
+ type = *pnt++;
+ sub_type = *pnt++;
+ if (!(type == ECOMMUNITY_ENCODE_EVPN
+ && sub_type == ECOMMUNITY_EVPN_SUBTYPE_DF_ELECTION))
+ continue;
+
+ *alg = (*pnt++) & ECOMMUNITY_EVPN_SUBTYPE_DF_ALG_BITS;
+
+ pnt += 3;
+ pnt = ptr_get_be16(pnt, &df_pref);
+ (void)pnt; /* consume value */
+ break;
+ }
+
+ return df_pref;
+}
+
+/*
+ * Fetch and return the sequence number from MAC Mobility extended
+ * community, if present, else 0.
+ */
+uint32_t bgp_attr_mac_mobility_seqnum(struct attr *attr, uint8_t *sticky)
+{
+ struct ecommunity *ecom;
+ uint32_t i;
+ uint8_t flags = 0;
+
+ ecom = bgp_attr_get_ecommunity(attr);
+ if (!ecom || !ecom->size)
+ return 0;
+
+ /* If there is a MAC Mobility extended community, return its
+ * sequence number.
+ * TODO: RFC is silent on handling of multiple MAC mobility extended
+ * communities for the same route. We will bail out upon the first
+ * one.
+ */
+ for (i = 0; i < ecom->size; i++) {
+ const uint8_t *pnt;
+ uint8_t type, sub_type;
+ uint32_t seq_num;
+
+ pnt = (ecom->val + (i * ECOMMUNITY_SIZE));
+ type = *pnt++;
+ sub_type = *pnt++;
+ if (!(type == ECOMMUNITY_ENCODE_EVPN
+ && sub_type == ECOMMUNITY_EVPN_SUBTYPE_MACMOBILITY))
+ continue;
+ flags = *pnt++;
+
+ if (flags & ECOMMUNITY_EVPN_SUBTYPE_MACMOBILITY_FLAG_STICKY)
+ *sticky = 1;
+ else
+ *sticky = 0;
+
+ pnt++;
+ pnt = ptr_get_be32(pnt, &seq_num);
+ (void)pnt; /* consume value */
+ return seq_num;
+ }
+
+ return 0;
+}
+
+/*
+ * return true if attr contains router flag extended community
+ */
+void bgp_attr_evpn_na_flag(struct attr *attr,
+ uint8_t *router_flag, bool *proxy)
+{
+ struct ecommunity *ecom;
+ uint32_t i;
+ uint8_t val;
+
+ ecom = bgp_attr_get_ecommunity(attr);
+ if (!ecom || !ecom->size)
+ return;
+
+ /* If there is a evpn na extendd community set router_flag */
+ for (i = 0; i < ecom->size; i++) {
+ uint8_t *pnt;
+ uint8_t type, sub_type;
+
+ pnt = (ecom->val + (i * ECOMMUNITY_SIZE));
+ type = *pnt++;
+ sub_type = *pnt++;
+
+ if (type == ECOMMUNITY_ENCODE_EVPN &&
+ sub_type == ECOMMUNITY_EVPN_SUBTYPE_ND) {
+ val = *pnt++;
+
+ if (val & ECOMMUNITY_EVPN_SUBTYPE_ND_ROUTER_FLAG)
+ *router_flag = 1;
+
+ if (val & ECOMMUNITY_EVPN_SUBTYPE_PROXY_FLAG)
+ *proxy = true;
+
+ break;
+ }
+ }
+}
+
+/* dst prefix must be AF_INET or AF_INET6 prefix, to forge EVPN prefix */
+extern int bgp_build_evpn_prefix(int evpn_type, uint32_t eth_tag,
+ struct prefix *dst)
+{
+ struct evpn_addr *p_evpn_p;
+ struct prefix p2;
+ struct prefix *src = &p2;
+
+ if (!dst || dst->family == 0)
+ return -1;
+ /* store initial prefix in src */
+ prefix_copy(src, dst);
+ memset(dst, 0, sizeof(struct prefix));
+ p_evpn_p = &(dst->u.prefix_evpn);
+ dst->family = AF_EVPN;
+ p_evpn_p->route_type = evpn_type;
+ if (evpn_type == BGP_EVPN_IP_PREFIX_ROUTE) {
+ p_evpn_p->prefix_addr.eth_tag = eth_tag;
+ p_evpn_p->prefix_addr.ip_prefix_length = p2.prefixlen;
+ if (src->family == AF_INET) {
+ SET_IPADDR_V4(&p_evpn_p->prefix_addr.ip);
+ memcpy(&p_evpn_p->prefix_addr.ip.ipaddr_v4,
+ &src->u.prefix4,
+ sizeof(struct in_addr));
+ dst->prefixlen = (uint16_t)PREFIX_LEN_ROUTE_TYPE_5_IPV4;
+ } else {
+ SET_IPADDR_V6(&p_evpn_p->prefix_addr.ip);
+ memcpy(&p_evpn_p->prefix_addr.ip.ipaddr_v6,
+ &src->u.prefix6,
+ sizeof(struct in6_addr));
+ dst->prefixlen = (uint16_t)PREFIX_LEN_ROUTE_TYPE_5_IPV6;
+ }
+ } else
+ return -1;
+ return 0;
+}
diff --git a/bgpd/bgp_attr_evpn.h b/bgpd/bgp_attr_evpn.h
new file mode 100644
index 0000000..bb4a816
--- /dev/null
+++ b/bgpd/bgp_attr_evpn.h
@@ -0,0 +1,65 @@
+/* E-VPN attribute handling structure file
+ * Copyright (C) 2016 6WIND
+ *
+ * This file is part of FRRouting.
+ *
+ * FRRouting is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License as published by the
+ * Free Software Foundation; either version 2, or (at your option) any
+ * later version.
+ *
+ * FRRouting is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program; see the file COPYING; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
+ */
+
+#ifndef _QUAGGA_BGP_ATTR_EVPN_H
+#define _QUAGGA_BGP_ATTR_EVPN_H
+
+#define MAX_ET 0xffffffff
+
+struct attr;
+
+enum overlay_index_type {
+ OVERLAY_INDEX_TYPE_NONE,
+ OVERLAY_INDEX_GATEWAY_IP,
+ OVERLAY_INDEX_ESI,
+ OVERLAY_INDEX_MAC,
+};
+
+/*
+ * Structure to store ovrelay index for EVPN type-5 route
+ * This structure stores ESI and Gateway IP overlay index.
+ * MAC overlay index is stored in the RMAC attribute.
+ */
+struct bgp_route_evpn {
+ enum overlay_index_type type;
+ esi_t eth_s_id;
+ struct ipaddr gw_ip;
+};
+
+extern bool str2esi(const char *str, esi_t *id);
+extern char *ecom_mac2str(char *ecom_mac);
+
+extern void bgp_add_routermac_ecom(struct attr *attr,
+ struct ethaddr *routermac);
+extern int bgp_build_evpn_prefix(int type, uint32_t eth_tag,
+ struct prefix *dst);
+extern bool bgp_attr_rmac(struct attr *attr, struct ethaddr *rmac);
+extern uint32_t bgp_attr_mac_mobility_seqnum(struct attr *attr,
+ uint8_t *sticky);
+extern uint8_t bgp_attr_default_gw(struct attr *attr);
+
+extern void bgp_attr_evpn_na_flag(struct attr *attr, uint8_t *router_flag,
+ bool *proxy);
+extern uint16_t bgp_attr_df_pref_from_ec(struct attr *attr, uint8_t *alg);
+
+
+extern bool bgp_route_evpn_same(const struct bgp_route_evpn *e1,
+ const struct bgp_route_evpn *e2);
+#endif /* _QUAGGA_BGP_ATTR_EVPN_H */
diff --git a/bgpd/bgp_bfd.c b/bgpd/bgp_bfd.c
new file mode 100644
index 0000000..d66b916
--- /dev/null
+++ b/bgpd/bgp_bfd.c
@@ -0,0 +1,644 @@
+/**
+ * bgp_bfd.c: BGP BFD handling routines
+ *
+ * @copyright Copyright (C) 2015 Cumulus Networks, Inc.
+ *
+ * This file is part of GNU Zebra.
+ *
+ * GNU Zebra is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License as published by the
+ * Free Software Foundation; either version 2, or (at your option) any
+ * later version.
+ *
+ * GNU Zebra is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program; see the file COPYING; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
+ */
+
+#include <zebra.h>
+
+#include "command.h"
+#include "linklist.h"
+#include "memory.h"
+#include "prefix.h"
+#include "thread.h"
+#include "buffer.h"
+#include "stream.h"
+#include "vrf.h"
+#include "zclient.h"
+#include "bfd.h"
+#include "lib/json.h"
+#include "filter.h"
+
+#include "bgpd/bgpd.h"
+#include "bgp_fsm.h"
+#include "bgpd/bgp_bfd.h"
+#include "bgpd/bgp_debug.h"
+#include "bgpd/bgp_vty.h"
+#include "bgpd/bgp_packet.h"
+
+DEFINE_MTYPE_STATIC(BGPD, BFD_CONFIG, "BFD configuration data");
+
+extern struct zclient *zclient;
+
+static void bfd_session_status_update(struct bfd_session_params *bsp,
+ const struct bfd_session_status *bss,
+ void *arg)
+{
+ struct peer *peer = arg;
+
+ if (BGP_DEBUG(bfd, BFD_LIB))
+ zlog_debug("%s: neighbor %s vrf %s(%u) bfd state %s -> %s",
+ __func__, peer->conf_if ? peer->conf_if : peer->host,
+ bfd_sess_vrf(bsp), bfd_sess_vrf_id(bsp),
+ bfd_get_status_str(bss->previous_state),
+ bfd_get_status_str(bss->state));
+
+ if (bss->state == BSS_DOWN && bss->previous_state == BSS_UP) {
+ if (CHECK_FLAG(peer->sflags, PEER_STATUS_NSF_MODE)
+ && bfd_sess_cbit(bsp) && !bss->remote_cbit) {
+ if (BGP_DEBUG(bfd, BFD_LIB))
+ zlog_debug(
+ "%s BFD DOWN message ignored in the process of graceful restart when C bit is cleared",
+ peer->host);
+ return;
+ }
+ peer->last_reset = PEER_DOWN_BFD_DOWN;
+
+ /* draft-ietf-idr-bfd-subcode */
+ if (BGP_IS_VALID_STATE_FOR_NOTIF(peer->status))
+ bgp_notify_send(peer, BGP_NOTIFY_CEASE,
+ BGP_NOTIFY_CEASE_BFD_DOWN);
+
+ BGP_EVENT_ADD(peer, BGP_Stop);
+ }
+
+ if (bss->state == BSS_UP && bss->previous_state != BSS_UP
+ && !peer_established(peer)) {
+ if (!BGP_PEER_START_SUPPRESSED(peer)) {
+ bgp_fsm_nht_update(peer, true);
+ BGP_EVENT_ADD(peer, BGP_Start);
+ }
+ }
+}
+
+void bgp_peer_config_apply(struct peer *p, struct peer_group *pg)
+{
+ struct listnode *n;
+ struct peer *pn;
+ struct peer *gconfig;
+
+ /* When called on a group, apply to all peers. */
+ if (CHECK_FLAG(p->sflags, PEER_STATUS_GROUP)) {
+ for (ALL_LIST_ELEMENTS_RO(p->group->peer, n, pn))
+ bgp_peer_config_apply(pn, pg);
+ return;
+ }
+
+ /* No group, just use current configuration. */
+ if (pg == NULL || pg->conf->bfd_config == NULL) {
+ bfd_sess_set_timers(p->bfd_config->session,
+ p->bfd_config->detection_multiplier,
+ p->bfd_config->min_rx,
+ p->bfd_config->min_tx);
+ bfd_sess_set_cbit(p->bfd_config->session, p->bfd_config->cbit);
+ bfd_sess_set_profile(p->bfd_config->session,
+ p->bfd_config->profile);
+ bfd_sess_install(p->bfd_config->session);
+ return;
+ }
+
+ /*
+ * Check if the group configuration was overwritten or apply group
+ * configuration.
+ */
+ gconfig = pg->conf;
+
+ /*
+ * If using default control plane independent configuration,
+ * then prefer group's (e.g. it means it wasn't manually configured).
+ */
+ if (!p->bfd_config->cbit)
+ bfd_sess_set_cbit(p->bfd_config->session,
+ gconfig->bfd_config->cbit);
+ else
+ bfd_sess_set_cbit(p->bfd_config->session, p->bfd_config->cbit);
+
+ /* If no profile was specified in peer, then use the group profile. */
+ if (p->bfd_config->profile[0] == 0)
+ bfd_sess_set_profile(p->bfd_config->session,
+ gconfig->bfd_config->profile);
+ else
+ bfd_sess_set_profile(p->bfd_config->session,
+ p->bfd_config->profile);
+
+ /* If no specific timers were configured, then use the group timers. */
+ if (p->bfd_config->detection_multiplier == BFD_DEF_DETECT_MULT
+ || p->bfd_config->min_rx == BFD_DEF_MIN_RX
+ || p->bfd_config->min_tx == BFD_DEF_MIN_TX)
+ bfd_sess_set_timers(p->bfd_config->session,
+ gconfig->bfd_config->detection_multiplier,
+ gconfig->bfd_config->min_rx,
+ gconfig->bfd_config->min_tx);
+ else
+ bfd_sess_set_timers(p->bfd_config->session,
+ p->bfd_config->detection_multiplier,
+ p->bfd_config->min_rx,
+ p->bfd_config->min_tx);
+
+ bfd_sess_install(p->bfd_config->session);
+}
+
+void bgp_peer_bfd_update_source(struct peer *p)
+{
+ struct bfd_session_params *session = p->bfd_config->session;
+ const union sockunion *source;
+ bool changed = false;
+ int family;
+ union {
+ struct in_addr v4;
+ struct in6_addr v6;
+ } src, dst;
+
+ /* Nothing to do for groups. */
+ if (CHECK_FLAG(p->sflags, PEER_STATUS_GROUP))
+ return;
+
+ /* Figure out the correct source to use. */
+ if (CHECK_FLAG(p->flags, PEER_FLAG_UPDATE_SOURCE) && p->update_source)
+ source = p->update_source;
+ else
+ source = p->su_local;
+
+ /* Update peer's source/destination addresses. */
+ bfd_sess_addresses(session, &family, &src.v6, &dst.v6);
+ if (family == AF_INET) {
+ if ((source && source->sin.sin_addr.s_addr != src.v4.s_addr)
+ || p->su.sin.sin_addr.s_addr != dst.v4.s_addr) {
+ if (BGP_DEBUG(bfd, BFD_LIB))
+ zlog_debug(
+ "%s: address [%pI4->%pI4] to [%pI4->%pI4]",
+ __func__, &src.v4, &dst.v4,
+ source ? &source->sin.sin_addr
+ : &src.v4,
+ &p->su.sin.sin_addr);
+
+ bfd_sess_set_ipv4_addrs(
+ session, source ? &source->sin.sin_addr : NULL,
+ &p->su.sin.sin_addr);
+ changed = true;
+ }
+ } else {
+ if ((source && memcmp(&source->sin6, &src.v6, sizeof(src.v6)))
+ || memcmp(&p->su.sin6, &dst.v6, sizeof(dst.v6))) {
+ if (BGP_DEBUG(bfd, BFD_LIB))
+ zlog_debug(
+ "%s: address [%pI6->%pI6] to [%pI6->%pI6]",
+ __func__, &src.v6, &dst.v6,
+ source ? &source->sin6.sin6_addr
+ : &src.v6,
+ &p->su.sin6.sin6_addr);
+
+ bfd_sess_set_ipv6_addrs(session,
+ source ? &source->sin6.sin6_addr
+ : NULL,
+ &p->su.sin6.sin6_addr);
+ changed = true;
+ }
+ }
+
+ /* Update interface. */
+ if (p->nexthop.ifp && bfd_sess_interface(session) == NULL) {
+ if (BGP_DEBUG(bfd, BFD_LIB))
+ zlog_debug("%s: interface none to %s", __func__,
+ p->nexthop.ifp->name);
+
+ bfd_sess_set_interface(session, p->nexthop.ifp->name);
+ changed = true;
+ }
+
+ /*
+ * Update TTL.
+ *
+ * Two cases:
+ * - We detected that the peer is a hop away from us (remove multi hop).
+ * (this happens when `p->shared_network` is set to `true`)
+ * - eBGP multi hop / TTL security changed.
+ */
+ if (!PEER_IS_MULTIHOP(p) && bfd_sess_hop_count(session) > 1) {
+ if (BGP_DEBUG(bfd, BFD_LIB))
+ zlog_debug("%s: TTL %d to 1", __func__,
+ bfd_sess_hop_count(session));
+
+ bfd_sess_set_hop_count(session, 1);
+ changed = true;
+ }
+ if (PEER_IS_MULTIHOP(p) && p->ttl != bfd_sess_hop_count(session)) {
+ if (BGP_DEBUG(bfd, BFD_LIB))
+ zlog_debug("%s: TTL %d to %d", __func__,
+ bfd_sess_hop_count(session), p->ttl);
+
+ bfd_sess_set_hop_count(session, p->ttl);
+ changed = true;
+ }
+
+ /* Update VRF. */
+ if (bfd_sess_vrf_id(session) != p->bgp->vrf_id) {
+ if (BGP_DEBUG(bfd, BFD_LIB))
+ zlog_debug(
+ "%s: VRF %s(%d) to %s(%d)", __func__,
+ bfd_sess_vrf(session), bfd_sess_vrf_id(session),
+ vrf_id_to_name(p->bgp->vrf_id), p->bgp->vrf_id);
+
+ bfd_sess_set_vrf(session, p->bgp->vrf_id);
+ changed = true;
+ }
+
+ if (changed)
+ bfd_sess_install(session);
+}
+
+/**
+ * Reset BFD configuration data structure to its defaults settings.
+ */
+static void bgp_peer_bfd_reset(struct peer *p)
+{
+ /* Set defaults. */
+ p->bfd_config->detection_multiplier = BFD_DEF_DETECT_MULT;
+ p->bfd_config->min_rx = BFD_DEF_MIN_RX;
+ p->bfd_config->min_tx = BFD_DEF_MIN_TX;
+ p->bfd_config->cbit = false;
+ p->bfd_config->profile[0] = 0;
+}
+
+void bgp_peer_configure_bfd(struct peer *p, bool manual)
+{
+ /* Groups should not call this. */
+ assert(!CHECK_FLAG(p->sflags, PEER_STATUS_GROUP));
+
+ /* Already configured, skip it. */
+ if (p->bfd_config) {
+ /* If manually active update flag. */
+ if (!p->bfd_config->manual)
+ p->bfd_config->manual = manual;
+
+ return;
+ }
+
+ /* Allocate memory for configuration overrides. */
+ p->bfd_config = XCALLOC(MTYPE_BFD_CONFIG, sizeof(*p->bfd_config));
+ p->bfd_config->manual = manual;
+
+ /* Create new session and assign callback. */
+ p->bfd_config->session = bfd_sess_new(bfd_session_status_update, p);
+ bgp_peer_bfd_reset(p);
+
+ /* Configure session with basic BGP peer data. */
+ if (p->su.sa.sa_family == AF_INET)
+ bfd_sess_set_ipv4_addrs(p->bfd_config->session,
+ p->su_local ? &p->su_local->sin.sin_addr
+ : NULL,
+ &p->su.sin.sin_addr);
+ else
+ bfd_sess_set_ipv6_addrs(
+ p->bfd_config->session,
+ p->su_local ? &p->su_local->sin6.sin6_addr : NULL,
+ &p->su.sin6.sin6_addr);
+
+ bfd_sess_set_vrf(p->bfd_config->session, p->bgp->vrf_id);
+ bfd_sess_set_hop_count(p->bfd_config->session,
+ PEER_IS_MULTIHOP(p) ? p->ttl : 1);
+
+ if (p->nexthop.ifp)
+ bfd_sess_set_interface(p->bfd_config->session,
+ p->nexthop.ifp->name);
+}
+
+static void bgp_peer_remove_bfd(struct peer *p)
+{
+ /* Groups should not call this. */
+ assert(!CHECK_FLAG(p->sflags, PEER_STATUS_GROUP));
+
+ /*
+ * Peer configuration was removed, however we must check if there
+ * is still a group configuration to keep this running.
+ */
+ if (p->group && p->group->conf->bfd_config) {
+ p->bfd_config->manual = false;
+ bgp_peer_bfd_reset(p);
+ bgp_peer_config_apply(p, p->group);
+ return;
+ }
+
+ if (p->bfd_config)
+ bfd_sess_free(&p->bfd_config->session);
+
+ XFREE(MTYPE_BFD_CONFIG, p->bfd_config);
+}
+
+static void bgp_group_configure_bfd(struct peer *p)
+{
+ struct listnode *n;
+ struct peer *pn;
+
+ /* Peers should not call this. */
+ assert(CHECK_FLAG(p->sflags, PEER_STATUS_GROUP));
+
+ /* Already allocated: do nothing. */
+ if (p->bfd_config)
+ return;
+
+ p->bfd_config = XCALLOC(MTYPE_BFD_CONFIG, sizeof(*p->bfd_config));
+
+ /* Set defaults. */
+ p->bfd_config->detection_multiplier = BFD_DEF_DETECT_MULT;
+ p->bfd_config->min_rx = BFD_DEF_MIN_RX;
+ p->bfd_config->min_tx = BFD_DEF_MIN_TX;
+
+ for (ALL_LIST_ELEMENTS_RO(p->group->peer, n, pn))
+ bgp_peer_configure_bfd(pn, false);
+}
+
+static void bgp_group_remove_bfd(struct peer *p)
+{
+ struct listnode *n;
+ struct peer *pn;
+
+ /* Peers should not call this. */
+ assert(CHECK_FLAG(p->sflags, PEER_STATUS_GROUP));
+
+ /* Already freed: do nothing. */
+ if (p->bfd_config == NULL)
+ return;
+
+ /* Free configuration and point to `NULL`. */
+ XFREE(MTYPE_BFD_CONFIG, p->bfd_config);
+
+ /* Now that it is `NULL` recalculate configuration for all peers. */
+ for (ALL_LIST_ELEMENTS_RO(p->group->peer, n, pn)) {
+ if (pn->bfd_config->manual)
+ bgp_peer_config_apply(pn, NULL);
+ else
+ bgp_peer_remove_bfd(pn);
+ }
+}
+
+void bgp_peer_remove_bfd_config(struct peer *p)
+{
+ if (CHECK_FLAG(p->sflags, PEER_STATUS_GROUP))
+ bgp_group_remove_bfd(p);
+ else
+ bgp_peer_remove_bfd(p);
+}
+
+/*
+ * bgp_bfd_peer_config_write - Write the peer BFD configuration.
+ */
+void bgp_bfd_peer_config_write(struct vty *vty, const struct peer *peer,
+ const char *addr)
+{
+ /*
+ * Always show group BFD configuration, but peer only when explicitly
+ * configured.
+ */
+ if ((!CHECK_FLAG(peer->sflags, PEER_STATUS_GROUP)
+ && peer->bfd_config->manual)
+ || CHECK_FLAG(peer->sflags, PEER_STATUS_GROUP)) {
+#if HAVE_BFDD > 0
+ vty_out(vty, " neighbor %s bfd\n", addr);
+#else
+ vty_out(vty, " neighbor %s bfd %d %d %d\n", addr,
+ peer->bfd_config->detection_multiplier,
+ peer->bfd_config->min_rx, peer->bfd_config->min_tx);
+#endif /* HAVE_BFDD */
+ }
+
+ if (peer->bfd_config->profile[0])
+ vty_out(vty, " neighbor %s bfd profile %s\n", addr,
+ peer->bfd_config->profile);
+
+ if (peer->bfd_config->cbit)
+ vty_out(vty, " neighbor %s bfd check-control-plane-failure\n",
+ addr);
+}
+
+/*
+ * bgp_bfd_show_info - Show the peer BFD information.
+ */
+void bgp_bfd_show_info(struct vty *vty, const struct peer *peer,
+ json_object *json_neigh)
+{
+ bfd_sess_show(vty, json_neigh, peer->bfd_config->session);
+}
+
+DEFUN (neighbor_bfd,
+ neighbor_bfd_cmd,
+ "neighbor <A.B.C.D|X:X::X:X|WORD> bfd",
+ NEIGHBOR_STR
+ NEIGHBOR_ADDR_STR2
+ "Enables BFD support\n")
+{
+ int idx_peer = 1;
+ struct peer *peer;
+
+ peer = peer_and_group_lookup_vty(vty, argv[idx_peer]->arg);
+ if (!peer)
+ return CMD_WARNING_CONFIG_FAILED;
+
+ if (CHECK_FLAG(peer->sflags, PEER_STATUS_GROUP))
+ bgp_group_configure_bfd(peer);
+ else
+ bgp_peer_configure_bfd(peer, true);
+
+ bgp_peer_config_apply(peer, peer->group);
+
+ return CMD_SUCCESS;
+}
+
+#if HAVE_BFDD > 0
+DEFUN_HIDDEN(
+#else
+DEFUN(
+#endif /* HAVE_BFDD */
+ neighbor_bfd_param,
+ neighbor_bfd_param_cmd,
+ "neighbor <A.B.C.D|X:X::X:X|WORD> bfd (2-255) (50-60000) (50-60000)",
+ NEIGHBOR_STR
+ NEIGHBOR_ADDR_STR2
+ "Enables BFD support\n"
+ "Detect Multiplier\n"
+ "Required min receive interval\n"
+ "Desired min transmit interval\n")
+{
+ int idx_peer = 1;
+ int idx_number_1 = 3;
+ int idx_number_2 = 4;
+ int idx_number_3 = 5;
+ long detection_multiplier, min_rx, min_tx;
+ struct peer *peer;
+
+ peer = peer_and_group_lookup_vty(vty, argv[idx_peer]->arg);
+ if (!peer)
+ return CMD_WARNING_CONFIG_FAILED;
+
+ detection_multiplier = strtol(argv[idx_number_1]->arg, NULL, 10);
+ min_rx = strtol(argv[idx_number_2]->arg, NULL, 10);
+ min_tx = strtol(argv[idx_number_3]->arg, NULL, 10);
+
+ if (CHECK_FLAG(peer->sflags, PEER_STATUS_GROUP))
+ bgp_group_configure_bfd(peer);
+ else
+ bgp_peer_configure_bfd(peer, true);
+
+ peer->bfd_config->detection_multiplier = detection_multiplier;
+ peer->bfd_config->min_rx = min_rx;
+ peer->bfd_config->min_tx = min_tx;
+ bgp_peer_config_apply(peer, peer->group);
+
+ return CMD_SUCCESS;
+}
+
+DEFUN (neighbor_bfd_check_controlplane_failure,
+ neighbor_bfd_check_controlplane_failure_cmd,
+ "[no] neighbor <A.B.C.D|X:X::X:X|WORD> bfd check-control-plane-failure",
+ NO_STR
+ NEIGHBOR_STR
+ NEIGHBOR_ADDR_STR2
+ "BFD support\n"
+ "Link dataplane status with BGP controlplane\n")
+{
+ const char *no = strmatch(argv[0]->text, "no") ? "no" : NULL;
+ int idx_peer = 0;
+ struct peer *peer;
+
+ if (no)
+ idx_peer = 2;
+ else
+ idx_peer = 1;
+ peer = peer_and_group_lookup_vty(vty, argv[idx_peer]->arg);
+ if (!peer) {
+ vty_out(vty, "%% Specify remote-as or peer-group commands first\n");
+ return CMD_WARNING_CONFIG_FAILED;
+ }
+
+ if (CHECK_FLAG(peer->sflags, PEER_STATUS_GROUP))
+ bgp_group_configure_bfd(peer);
+ else
+ bgp_peer_configure_bfd(peer, true);
+
+ peer->bfd_config->cbit = no == NULL;
+ bgp_peer_config_apply(peer, peer->group);
+
+ return CMD_SUCCESS;
+ }
+
+DEFUN (no_neighbor_bfd,
+ no_neighbor_bfd_cmd,
+#if HAVE_BFDD > 0
+ "no neighbor <A.B.C.D|X:X::X:X|WORD> bfd",
+#else
+ "no neighbor <A.B.C.D|X:X::X:X|WORD> bfd [(2-255) (50-60000) (50-60000)]",
+#endif /* HAVE_BFDD */
+ NO_STR
+ NEIGHBOR_STR
+ NEIGHBOR_ADDR_STR2
+ "Disables BFD support\n"
+#if HAVE_BFDD == 0
+ "Detect Multiplier\n"
+ "Required min receive interval\n"
+ "Desired min transmit interval\n"
+#endif /* !HAVE_BFDD */
+)
+{
+ int idx_peer = 2;
+ struct peer *peer;
+
+ peer = peer_and_group_lookup_vty(vty, argv[idx_peer]->arg);
+ if (!peer)
+ return CMD_WARNING_CONFIG_FAILED;
+
+ if (CHECK_FLAG(peer->sflags, PEER_STATUS_GROUP))
+ bgp_group_remove_bfd(peer);
+ else
+ bgp_peer_remove_bfd(peer);
+
+ return CMD_SUCCESS;
+}
+
+#if HAVE_BFDD > 0
+DEFUN(neighbor_bfd_profile, neighbor_bfd_profile_cmd,
+ "neighbor <A.B.C.D|X:X::X:X|WORD> bfd profile BFDPROF",
+ NEIGHBOR_STR
+ NEIGHBOR_ADDR_STR2
+ "BFD integration\n"
+ BFD_PROFILE_STR
+ BFD_PROFILE_NAME_STR)
+{
+ int idx_peer = 1, idx_prof = 4;
+ struct peer *peer;
+
+ peer = peer_and_group_lookup_vty(vty, argv[idx_peer]->arg);
+ if (!peer)
+ return CMD_WARNING_CONFIG_FAILED;
+
+ if (CHECK_FLAG(peer->sflags, PEER_STATUS_GROUP))
+ bgp_group_configure_bfd(peer);
+ else
+ bgp_peer_configure_bfd(peer, true);
+
+ strlcpy(peer->bfd_config->profile, argv[idx_prof]->arg,
+ sizeof(peer->bfd_config->profile));
+ bgp_peer_config_apply(peer, peer->group);
+
+ return CMD_SUCCESS;
+}
+
+DEFUN(no_neighbor_bfd_profile, no_neighbor_bfd_profile_cmd,
+ "no neighbor <A.B.C.D|X:X::X:X|WORD> bfd profile [BFDPROF]",
+ NO_STR
+ NEIGHBOR_STR
+ NEIGHBOR_ADDR_STR2
+ "BFD integration\n"
+ BFD_PROFILE_STR
+ BFD_PROFILE_NAME_STR)
+{
+ int idx_peer = 2;
+ struct peer *peer;
+
+ peer = peer_and_group_lookup_vty(vty, argv[idx_peer]->arg);
+ if (!peer)
+ return CMD_WARNING_CONFIG_FAILED;
+
+ if (CHECK_FLAG(peer->sflags, PEER_STATUS_GROUP))
+ bgp_group_configure_bfd(peer);
+ else
+ bgp_peer_configure_bfd(peer, true);
+
+ peer->bfd_config->profile[0] = 0;
+ bgp_peer_config_apply(peer, peer->group);
+
+ return CMD_SUCCESS;
+}
+#endif /* HAVE_BFDD */
+
+void bgp_bfd_init(struct thread_master *tm)
+{
+ /* Initialize BFD client functions */
+ bfd_protocol_integration_init(zclient, tm);
+
+ /* "neighbor bfd" commands. */
+ install_element(BGP_NODE, &neighbor_bfd_cmd);
+ install_element(BGP_NODE, &neighbor_bfd_param_cmd);
+ install_element(BGP_NODE, &neighbor_bfd_check_controlplane_failure_cmd);
+ install_element(BGP_NODE, &no_neighbor_bfd_cmd);
+
+#if HAVE_BFDD > 0
+ install_element(BGP_NODE, &neighbor_bfd_profile_cmd);
+ install_element(BGP_NODE, &no_neighbor_bfd_profile_cmd);
+#endif /* HAVE_BFDD */
+}
diff --git a/bgpd/bgp_bfd.h b/bgpd/bgp_bfd.h
new file mode 100644
index 0000000..9dca48a
--- /dev/null
+++ b/bgpd/bgp_bfd.h
@@ -0,0 +1,80 @@
+/**
+ * bgp_bfd.h: BGP BFD definitions and structures
+ *
+ * @copyright Copyright (C) 2015 Cumulus Networks, Inc.
+ *
+ * This file is part of GNU Zebra.
+ *
+ * GNU Zebra is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License as published by the
+ * Free Software Foundation; either version 2, or (at your option) any
+ * later version.
+ *
+ * GNU Zebra is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program; see the file COPYING; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
+ */
+
+#ifndef _QUAGGA_BGP_BFD_H
+#define _QUAGGA_BGP_BFD_H
+
+#define PEER_IS_MULTIHOP(peer) \
+ ((((peer)->sort == BGP_PEER_IBGP) && !(peer)->shared_network) \
+ || is_ebgp_multihop_configured((peer)))
+
+extern void bgp_bfd_init(struct thread_master *tm);
+
+extern void bgp_bfd_peer_config_write(struct vty *vty, const struct peer *peer,
+ const char *addr);
+
+/**
+ * Show BFD information helper.
+ *
+ * \param vty the VTY pointer.
+ * \param peer the BGP configuration pointer.
+ * \param use_json unused.
+ * \param json_neigh JSON object when called as JSON command.
+ */
+extern void bgp_bfd_show_info(struct vty *vty, const struct peer *peer,
+ json_object *json_neigh);
+
+/**
+ * When called on a group it applies configuration to all peers in that group,
+ * otherwise just applies the configuration to a single peer.
+ *
+ * This function should be called when configuration changes either on group
+ * or peer.
+ *
+ * \param p the BGP peer pointer.
+ * \param pg the BGP group to copy configuration from (it is usually
+ * `p->group` exception when copying new group configuration
+ * see `peer_group2peer_config_copy` function case).
+ */
+extern void bgp_peer_config_apply(struct peer *p, struct peer_group *pg);
+
+/**
+ * Allocates and configure BFD session for peer. If it is already configured,
+ * then it does nothing.
+ *
+ * Always call `bgp_peer_config_apply` afterwards if you need the changes
+ * immediately applied.
+ */
+extern void bgp_peer_configure_bfd(struct peer *p, bool manual);
+
+/**
+ * Removes BFD configuration from either peer or peer group.
+ */
+extern void bgp_peer_remove_bfd_config(struct peer *p);
+
+/**
+ * Special function to handle the case of changing source address. This
+ * happens when the peer/group is configured with `neigbor X update-source Y`.
+ */
+extern void bgp_peer_bfd_update_source(struct peer *p);
+
+#endif /* _QUAGGA_BGP_BFD_H */
diff --git a/bgpd/bgp_bmp.c b/bgpd/bgp_bmp.c
new file mode 100644
index 0000000..05c6a14
--- /dev/null
+++ b/bgpd/bgp_bmp.c
@@ -0,0 +1,2587 @@
+/* BMP support.
+ * Copyright (C) 2018 Yasuhiro Ohara
+ * Copyright (C) 2019 David Lamparter for NetDEF, Inc.
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License as published by the Free
+ * Software Foundation; either version 2 of the License, or (at your option)
+ * any later version.
+ *
+ * This program is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
+ * more details.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program; see the file COPYING; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
+ */
+
+#include <zebra.h>
+
+#include "log.h"
+#include "stream.h"
+#include "sockunion.h"
+#include "command.h"
+#include "prefix.h"
+#include "thread.h"
+#include "linklist.h"
+#include "queue.h"
+#include "pullwr.h"
+#include "memory.h"
+#include "network.h"
+#include "filter.h"
+#include "lib_errors.h"
+#include "stream.h"
+#include "libfrr.h"
+#include "lib/version.h"
+#include "jhash.h"
+#include "termtable.h"
+
+#include "bgpd/bgp_table.h"
+#include "bgpd/bgpd.h"
+#include "bgpd/bgp_route.h"
+#include "bgpd/bgp_attr.h"
+#include "bgpd/bgp_dump.h"
+#include "bgpd/bgp_errors.h"
+#include "bgpd/bgp_packet.h"
+#include "bgpd/bgp_bmp.h"
+#include "bgpd/bgp_fsm.h"
+#include "bgpd/bgp_updgrp.h"
+#include "bgpd/bgp_vty.h"
+#include "bgpd/bgp_trace.h"
+#include "bgpd/bgp_network.h"
+
+static void bmp_close(struct bmp *bmp);
+static struct bmp_bgp *bmp_bgp_find(struct bgp *bgp);
+static void bmp_targets_put(struct bmp_targets *bt);
+static struct bmp_bgp_peer *bmp_bgp_peer_find(uint64_t peerid);
+static struct bmp_bgp_peer *bmp_bgp_peer_get(struct peer *peer);
+static void bmp_active_disconnected(struct bmp_active *ba);
+static void bmp_active_put(struct bmp_active *ba);
+
+DEFINE_MGROUP(BMP, "BMP (BGP Monitoring Protocol)");
+
+DEFINE_MTYPE_STATIC(BMP, BMP_CONN, "BMP connection state");
+DEFINE_MTYPE_STATIC(BMP, BMP_TARGETS, "BMP targets");
+DEFINE_MTYPE_STATIC(BMP, BMP_TARGETSNAME, "BMP targets name");
+DEFINE_MTYPE_STATIC(BMP, BMP_LISTENER, "BMP listener");
+DEFINE_MTYPE_STATIC(BMP, BMP_ACTIVE, "BMP active connection config");
+DEFINE_MTYPE_STATIC(BMP, BMP_ACLNAME, "BMP access-list name");
+DEFINE_MTYPE_STATIC(BMP, BMP_QUEUE, "BMP update queue item");
+DEFINE_MTYPE_STATIC(BMP, BMP, "BMP instance state");
+DEFINE_MTYPE_STATIC(BMP, BMP_MIRRORQ, "BMP route mirroring buffer");
+DEFINE_MTYPE_STATIC(BMP, BMP_PEER, "BMP per BGP peer data");
+DEFINE_MTYPE_STATIC(BMP, BMP_OPEN, "BMP stored BGP OPEN message");
+
+DEFINE_QOBJ_TYPE(bmp_targets);
+
+static int bmp_bgp_cmp(const struct bmp_bgp *a, const struct bmp_bgp *b)
+{
+ if (a->bgp < b->bgp)
+ return -1;
+ if (a->bgp > b->bgp)
+ return 1;
+ return 0;
+}
+
+static uint32_t bmp_bgp_hash(const struct bmp_bgp *e)
+{
+ return jhash(&e->bgp, sizeof(e->bgp), 0x55aa5a5a);
+}
+
+DECLARE_HASH(bmp_bgph, struct bmp_bgp, bbi, bmp_bgp_cmp, bmp_bgp_hash);
+
+struct bmp_bgph_head bmp_bgph;
+
+static int bmp_bgp_peer_cmp(const struct bmp_bgp_peer *a,
+ const struct bmp_bgp_peer *b)
+{
+ if (a->peerid < b->peerid)
+ return -1;
+ if (a->peerid > b->peerid)
+ return 1;
+ return 0;
+}
+
+static uint32_t bmp_bgp_peer_hash(const struct bmp_bgp_peer *e)
+{
+ return e->peerid;
+}
+
+DECLARE_HASH(bmp_peerh, struct bmp_bgp_peer, bpi,
+ bmp_bgp_peer_cmp, bmp_bgp_peer_hash);
+
+struct bmp_peerh_head bmp_peerh;
+
+DECLARE_LIST(bmp_mirrorq, struct bmp_mirrorq, bmi);
+
+/* listener management */
+
+static int bmp_listener_cmp(const struct bmp_listener *a,
+ const struct bmp_listener *b)
+{
+ int c;
+
+ c = sockunion_cmp(&a->addr, &b->addr);
+ if (c)
+ return c;
+ if (a->port < b->port)
+ return -1;
+ if (a->port > b->port)
+ return 1;
+ return 0;
+}
+
+DECLARE_SORTLIST_UNIQ(bmp_listeners, struct bmp_listener, bli,
+ bmp_listener_cmp);
+
+static void bmp_listener_put(struct bmp_listener *bl)
+{
+ bmp_listeners_del(&bl->targets->listeners, bl);
+ XFREE(MTYPE_BMP_LISTENER, bl);
+}
+
+static int bmp_targets_cmp(const struct bmp_targets *a,
+ const struct bmp_targets *b)
+{
+ return strcmp(a->name, b->name);
+}
+
+DECLARE_SORTLIST_UNIQ(bmp_targets, struct bmp_targets, bti, bmp_targets_cmp);
+
+DECLARE_LIST(bmp_session, struct bmp, bsi);
+
+DECLARE_DLIST(bmp_qlist, struct bmp_queue_entry, bli);
+
+static int bmp_qhash_cmp(const struct bmp_queue_entry *a,
+ const struct bmp_queue_entry *b)
+{
+ int ret;
+ if (a->afi == AFI_L2VPN && a->safi == SAFI_EVPN && b->afi == AFI_L2VPN
+ && b->safi == SAFI_EVPN) {
+ ret = prefix_cmp(&a->rd, &b->rd);
+ if (ret)
+ return ret;
+ } else if (a->afi == AFI_L2VPN && a->safi == SAFI_EVPN)
+ return 1;
+ else if (b->afi == AFI_L2VPN && b->safi == SAFI_EVPN)
+ return -1;
+
+ if (a->afi == b->afi && a->safi == SAFI_MPLS_VPN &&
+ b->safi == SAFI_MPLS_VPN) {
+ ret = prefix_cmp(&a->rd, &b->rd);
+ if (ret)
+ return ret;
+ } else if (a->safi == SAFI_MPLS_VPN)
+ return 1;
+ else if (b->safi == SAFI_MPLS_VPN)
+ return -1;
+
+ ret = prefix_cmp(&a->p, &b->p);
+ if (ret)
+ return ret;
+ ret = memcmp(&a->peerid, &b->peerid,
+ offsetof(struct bmp_queue_entry, refcount) -
+ offsetof(struct bmp_queue_entry, peerid));
+ return ret;
+}
+
+static uint32_t bmp_qhash_hkey(const struct bmp_queue_entry *e)
+{
+ uint32_t key;
+
+ key = prefix_hash_key((void *)&e->p);
+ key = jhash(&e->peerid,
+ offsetof(struct bmp_queue_entry, refcount)
+ - offsetof(struct bmp_queue_entry, peerid),
+ key);
+ if ((e->afi == AFI_L2VPN && e->safi == SAFI_EVPN) ||
+ (e->safi == SAFI_MPLS_VPN))
+ key = jhash(&e->rd,
+ offsetof(struct bmp_queue_entry, rd)
+ - offsetof(struct bmp_queue_entry, refcount)
+ + PSIZE(e->rd.prefixlen),
+ key);
+
+ return key;
+}
+
+DECLARE_HASH(bmp_qhash, struct bmp_queue_entry, bhi,
+ bmp_qhash_cmp, bmp_qhash_hkey);
+
+static int bmp_active_cmp(const struct bmp_active *a,
+ const struct bmp_active *b)
+{
+ int c;
+
+ c = strcmp(a->hostname, b->hostname);
+ if (c)
+ return c;
+ if (a->port < b->port)
+ return -1;
+ if (a->port > b->port)
+ return 1;
+ return 0;
+}
+
+DECLARE_SORTLIST_UNIQ(bmp_actives, struct bmp_active, bai, bmp_active_cmp);
+
+static struct bmp *bmp_new(struct bmp_targets *bt, int bmp_sock)
+{
+ struct bmp *new = XCALLOC(MTYPE_BMP_CONN, sizeof(struct bmp));
+ afi_t afi;
+ safi_t safi;
+
+ monotime(&new->t_up);
+ new->targets = bt;
+ new->socket = bmp_sock;
+ new->syncafi = AFI_MAX;
+
+ FOREACH_AFI_SAFI (afi, safi) {
+ new->afistate[afi][safi] = bt->afimon[afi][safi]
+ ? BMP_AFI_NEEDSYNC : BMP_AFI_INACTIVE;
+ }
+
+ bmp_session_add_tail(&bt->sessions, new);
+ return new;
+}
+
+static void bmp_free(struct bmp *bmp)
+{
+ bmp_session_del(&bmp->targets->sessions, bmp);
+ XFREE(MTYPE_BMP_CONN, bmp);
+}
+
+static void bmp_common_hdr(struct stream *s, uint8_t ver, uint8_t type)
+{
+ stream_putc(s, ver);
+ stream_putl(s, 0); //dummy message length. will be set later.
+ stream_putc(s, type);
+}
+
+static void bmp_per_peer_hdr(struct stream *s, struct peer *peer,
+ uint8_t flags, const struct timeval *tv)
+{
+ char peer_distinguisher[8];
+
+#define BMP_PEER_TYPE_GLOBAL_INSTANCE 0
+#define BMP_PEER_TYPE_RD_INSTANCE 1
+#define BMP_PEER_TYPE_LOCAL_INSTANCE 2
+
+#define BMP_PEER_FLAG_V (1 << 7)
+#define BMP_PEER_FLAG_L (1 << 6)
+#define BMP_PEER_FLAG_A (1 << 5)
+
+ /* Peer Type */
+ stream_putc(s, BMP_PEER_TYPE_GLOBAL_INSTANCE);
+
+ /* Peer Flags */
+ if (peer->su.sa.sa_family == AF_INET6)
+ SET_FLAG(flags, BMP_PEER_FLAG_V);
+ else
+ UNSET_FLAG(flags, BMP_PEER_FLAG_V);
+ stream_putc(s, flags);
+
+ /* Peer Distinguisher */
+ memset (&peer_distinguisher[0], 0, 8);
+ stream_put(s, &peer_distinguisher[0], 8);
+
+ /* Peer Address */
+ if (peer->su.sa.sa_family == AF_INET6)
+ stream_put(s, &peer->su.sin6.sin6_addr, 16);
+ else if (peer->su.sa.sa_family == AF_INET) {
+ stream_putl(s, 0);
+ stream_putl(s, 0);
+ stream_putl(s, 0);
+ stream_put_in_addr(s, &peer->su.sin.sin_addr);
+ } else {
+ stream_putl(s, 0);
+ stream_putl(s, 0);
+ stream_putl(s, 0);
+ stream_putl(s, 0);
+ }
+
+ /* Peer AS */
+ stream_putl(s, peer->as);
+
+ /* Peer BGP ID */
+ stream_put_in_addr(s, &peer->remote_id);
+
+ /* Timestamp */
+ if (tv) {
+ stream_putl(s, tv->tv_sec);
+ stream_putl(s, tv->tv_usec);
+ } else {
+ stream_putl(s, 0);
+ stream_putl(s, 0);
+ }
+}
+
+static void bmp_put_info_tlv(struct stream *s, uint16_t type,
+ const char *string)
+{
+ int len = strlen (string);
+ stream_putw(s, type);
+ stream_putw(s, len);
+ stream_put(s, string, len);
+}
+
+static int bmp_send_initiation(struct bmp *bmp)
+{
+ int len;
+ struct stream *s;
+ s = stream_new(BGP_MAX_PACKET_SIZE);
+ bmp_common_hdr(s, BMP_VERSION_3, BMP_TYPE_INITIATION);
+
+#define BMP_INFO_TYPE_SYSDESCR 1
+#define BMP_INFO_TYPE_SYSNAME 2
+ bmp_put_info_tlv(s, BMP_INFO_TYPE_SYSDESCR,
+ FRR_FULL_NAME " " FRR_VER_SHORT);
+ bmp_put_info_tlv(s, BMP_INFO_TYPE_SYSNAME, cmd_hostname_get());
+
+ len = stream_get_endp(s);
+ stream_putl_at(s, BMP_LENGTH_POS, len); //message length is set.
+
+ pullwr_write_stream(bmp->pullwr, s);
+ stream_free(s);
+ return 0;
+}
+
+static void bmp_notify_put(struct stream *s, struct bgp_notify *nfy)
+{
+ size_t len_pos;
+ uint8_t marker[16] = {
+ 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff,
+ };
+
+ stream_put(s, marker, sizeof(marker));
+ len_pos = stream_get_endp(s);
+ stream_putw(s, 0);
+ stream_putc(s, BGP_MSG_NOTIFY);
+ stream_putc(s, nfy->code);
+ stream_putc(s, nfy->subcode);
+ stream_put(s, nfy->data, nfy->length);
+
+ stream_putw_at(s, len_pos, stream_get_endp(s) - len_pos
+ + sizeof(marker));
+}
+
+static struct stream *bmp_peerstate(struct peer *peer, bool down)
+{
+ struct stream *s;
+ size_t len;
+ struct timeval uptime, uptime_real;
+
+ uptime.tv_sec = peer->uptime;
+ uptime.tv_usec = 0;
+ monotime_to_realtime(&uptime, &uptime_real);
+
+#define BGP_BMP_MAX_PACKET_SIZE 1024
+ s = stream_new(BGP_MAX_PACKET_SIZE);
+
+ if (peer_established(peer) && !down) {
+ struct bmp_bgp_peer *bbpeer;
+
+ bmp_common_hdr(s, BMP_VERSION_3,
+ BMP_TYPE_PEER_UP_NOTIFICATION);
+ bmp_per_peer_hdr(s, peer, 0, &uptime_real);
+
+ /* Local Address (16 bytes) */
+ if (peer->su_local->sa.sa_family == AF_INET6)
+ stream_put(s, &peer->su_local->sin6.sin6_addr, 16);
+ else if (peer->su_local->sa.sa_family == AF_INET) {
+ stream_putl(s, 0);
+ stream_putl(s, 0);
+ stream_putl(s, 0);
+ stream_put_in_addr(s, &peer->su_local->sin.sin_addr);
+ }
+
+ /* Local Port, Remote Port */
+ if (peer->su_local->sa.sa_family == AF_INET6)
+ stream_putw(s, htons(peer->su_local->sin6.sin6_port));
+ else if (peer->su_local->sa.sa_family == AF_INET)
+ stream_putw(s, htons(peer->su_local->sin.sin_port));
+ if (peer->su_remote->sa.sa_family == AF_INET6)
+ stream_putw(s, htons(peer->su_remote->sin6.sin6_port));
+ else if (peer->su_remote->sa.sa_family == AF_INET)
+ stream_putw(s, htons(peer->su_remote->sin.sin_port));
+
+ static const uint8_t dummy_open[] = {
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0x00, 0x13, 0x01,
+ };
+
+ bbpeer = bmp_bgp_peer_find(peer->qobj_node.nid);
+
+ if (bbpeer && bbpeer->open_tx)
+ stream_put(s, bbpeer->open_tx, bbpeer->open_tx_len);
+ else {
+ stream_put(s, dummy_open, sizeof(dummy_open));
+ zlog_warn("bmp: missing TX OPEN message for peer %s",
+ peer->host);
+ }
+ if (bbpeer && bbpeer->open_rx)
+ stream_put(s, bbpeer->open_rx, bbpeer->open_rx_len);
+ else {
+ stream_put(s, dummy_open, sizeof(dummy_open));
+ zlog_warn("bmp: missing RX OPEN message for peer %s",
+ peer->host);
+ }
+
+ if (peer->desc)
+ bmp_put_info_tlv(s, 0, peer->desc);
+ } else {
+ uint8_t type;
+ size_t type_pos;
+
+ bmp_common_hdr(s, BMP_VERSION_3,
+ BMP_TYPE_PEER_DOWN_NOTIFICATION);
+ bmp_per_peer_hdr(s, peer, 0, &uptime_real);
+
+ type_pos = stream_get_endp(s);
+ stream_putc(s, 0); /* placeholder for down reason */
+
+ switch (peer->last_reset) {
+ case PEER_DOWN_NOTIFY_RECEIVED:
+ type = BMP_PEERDOWN_REMOTE_NOTIFY;
+ bmp_notify_put(s, &peer->notify);
+ break;
+ case PEER_DOWN_CLOSE_SESSION:
+ type = BMP_PEERDOWN_REMOTE_CLOSE;
+ break;
+ case PEER_DOWN_WAITING_NHT:
+ type = BMP_PEERDOWN_LOCAL_FSM;
+ stream_putw(s, BGP_FSM_TcpConnectionFails);
+ break;
+ /*
+ * TODO: Map remaining PEER_DOWN_* reasons to RFC event codes.
+ * TODO: Implement BMP_PEERDOWN_LOCAL_NOTIFY.
+ *
+ * See RFC7854 ss. 4.9
+ */
+ default:
+ type = BMP_PEERDOWN_LOCAL_FSM;
+ stream_putw(s, BMP_PEER_DOWN_NO_RELEVANT_EVENT_CODE);
+ break;
+ }
+ stream_putc_at(s, type_pos, type);
+ }
+
+ len = stream_get_endp(s);
+ stream_putl_at(s, BMP_LENGTH_POS, len); //message length is set.
+ return s;
+}
+
+
+static int bmp_send_peerup(struct bmp *bmp)
+{
+ struct peer *peer;
+ struct listnode *node;
+ struct stream *s;
+
+ /* Walk down all peers */
+ for (ALL_LIST_ELEMENTS_RO(bmp->targets->bgp->peer, node, peer)) {
+ s = bmp_peerstate(peer, false);
+ pullwr_write_stream(bmp->pullwr, s);
+ stream_free(s);
+ }
+
+ return 0;
+}
+
+/* XXX: kludge - filling the pullwr's buffer */
+static void bmp_send_all(struct bmp_bgp *bmpbgp, struct stream *s)
+{
+ struct bmp_targets *bt;
+ struct bmp *bmp;
+
+ frr_each(bmp_targets, &bmpbgp->targets, bt)
+ frr_each(bmp_session, &bt->sessions, bmp)
+ pullwr_write_stream(bmp->pullwr, s);
+ stream_free(s);
+}
+
+/*
+ * Route Mirroring
+ */
+
+#define BMP_MIRROR_TLV_TYPE_BGP_MESSAGE 0
+#define BMP_MIRROR_TLV_TYPE_INFO 1
+
+#define BMP_MIRROR_INFO_CODE_ERRORPDU 0
+#define BMP_MIRROR_INFO_CODE_LOSTMSGS 1
+
+static struct bmp_mirrorq *bmp_pull_mirror(struct bmp *bmp)
+{
+ struct bmp_mirrorq *bmq;
+
+ bmq = bmp->mirrorpos;
+ if (!bmq)
+ return NULL;
+
+ bmp->mirrorpos = bmp_mirrorq_next(&bmp->targets->bmpbgp->mirrorq, bmq);
+
+ bmq->refcount--;
+ if (!bmq->refcount) {
+ bmp->targets->bmpbgp->mirror_qsize -= sizeof(*bmq) + bmq->len;
+ bmp_mirrorq_del(&bmp->targets->bmpbgp->mirrorq, bmq);
+ }
+ return bmq;
+}
+
+static void bmp_mirror_cull(struct bmp_bgp *bmpbgp)
+{
+ while (bmpbgp->mirror_qsize > bmpbgp->mirror_qsizelimit) {
+ struct bmp_mirrorq *bmq, *inner;
+ struct bmp_targets *bt;
+ struct bmp *bmp;
+
+ bmq = bmp_mirrorq_first(&bmpbgp->mirrorq);
+
+ frr_each(bmp_targets, &bmpbgp->targets, bt) {
+ if (!bt->mirror)
+ continue;
+ frr_each(bmp_session, &bt->sessions, bmp) {
+ if (bmp->mirrorpos != bmq)
+ continue;
+
+ while ((inner = bmp_pull_mirror(bmp))) {
+ if (!inner->refcount)
+ XFREE(MTYPE_BMP_MIRRORQ,
+ inner);
+ }
+
+ zlog_warn("bmp[%s] lost mirror messages due to buffer size limit",
+ bmp->remote);
+ bmp->mirror_lost = true;
+ pullwr_bump(bmp->pullwr);
+ }
+ }
+ }
+}
+
+static int bmp_mirror_packet(struct peer *peer, uint8_t type, bgp_size_t size,
+ struct stream *packet)
+{
+ struct bmp_bgp *bmpbgp = bmp_bgp_find(peer->bgp);
+ struct timeval tv;
+ struct bmp_mirrorq *qitem;
+ struct bmp_targets *bt;
+ struct bmp *bmp;
+
+ frrtrace(3, frr_bgp, bmp_mirror_packet, peer, type, packet);
+
+ gettimeofday(&tv, NULL);
+
+ if (type == BGP_MSG_OPEN) {
+ struct bmp_bgp_peer *bbpeer = bmp_bgp_peer_get(peer);
+
+ XFREE(MTYPE_BMP_OPEN, bbpeer->open_rx);
+
+ bbpeer->open_rx_len = size;
+ bbpeer->open_rx = XMALLOC(MTYPE_BMP_OPEN, size);
+ memcpy(bbpeer->open_rx, packet->data, size);
+ }
+
+ if (!bmpbgp)
+ return 0;
+
+ qitem = XCALLOC(MTYPE_BMP_MIRRORQ, sizeof(*qitem) + size);
+ qitem->peerid = peer->qobj_node.nid;
+ qitem->tv = tv;
+ qitem->len = size;
+ memcpy(qitem->data, packet->data, size);
+
+ frr_each(bmp_targets, &bmpbgp->targets, bt) {
+ if (!bt->mirror)
+ continue;
+ frr_each(bmp_session, &bt->sessions, bmp) {
+ qitem->refcount++;
+ if (!bmp->mirrorpos)
+ bmp->mirrorpos = qitem;
+ pullwr_bump(bmp->pullwr);
+ }
+ }
+ if (qitem->refcount == 0)
+ XFREE(MTYPE_BMP_MIRRORQ, qitem);
+ else {
+ bmpbgp->mirror_qsize += sizeof(*qitem) + size;
+ bmp_mirrorq_add_tail(&bmpbgp->mirrorq, qitem);
+
+ bmp_mirror_cull(bmpbgp);
+
+ bmpbgp->mirror_qsizemax = MAX(bmpbgp->mirror_qsizemax,
+ bmpbgp->mirror_qsize);
+ }
+ return 0;
+}
+
+static void bmp_wrmirror_lost(struct bmp *bmp, struct pullwr *pullwr)
+{
+ struct stream *s;
+ struct timeval tv;
+
+ gettimeofday(&tv, NULL);
+
+ s = stream_new(BGP_MAX_PACKET_SIZE);
+
+ bmp_common_hdr(s, BMP_VERSION_3, BMP_TYPE_ROUTE_MIRRORING);
+ bmp_per_peer_hdr(s, bmp->targets->bgp->peer_self, 0, &tv);
+
+ stream_putw(s, BMP_MIRROR_TLV_TYPE_INFO);
+ stream_putw(s, 2);
+ stream_putw(s, BMP_MIRROR_INFO_CODE_LOSTMSGS);
+ stream_putl_at(s, BMP_LENGTH_POS, stream_get_endp(s));
+
+ bmp->cnt_mirror_overruns++;
+ pullwr_write_stream(bmp->pullwr, s);
+ stream_free(s);
+}
+
+static bool bmp_wrmirror(struct bmp *bmp, struct pullwr *pullwr)
+{
+ struct bmp_mirrorq *bmq;
+ struct peer *peer;
+ bool written = false;
+
+ if (bmp->mirror_lost) {
+ bmp_wrmirror_lost(bmp, pullwr);
+ bmp->mirror_lost = false;
+ return true;
+ }
+
+ bmq = bmp_pull_mirror(bmp);
+ if (!bmq)
+ return false;
+
+ peer = QOBJ_GET_TYPESAFE(bmq->peerid, peer);
+ if (!peer) {
+ zlog_info("bmp: skipping mirror message for deleted peer");
+ goto out;
+ }
+
+ struct stream *s;
+ s = stream_new(BGP_MAX_PACKET_SIZE);
+
+ bmp_common_hdr(s, BMP_VERSION_3, BMP_TYPE_ROUTE_MIRRORING);
+ bmp_per_peer_hdr(s, peer, 0, &bmq->tv);
+
+ /* BMP Mirror TLV. */
+ stream_putw(s, BMP_MIRROR_TLV_TYPE_BGP_MESSAGE);
+ stream_putw(s, bmq->len);
+ stream_putl_at(s, BMP_LENGTH_POS, stream_get_endp(s) + bmq->len);
+
+ bmp->cnt_mirror++;
+ pullwr_write_stream(bmp->pullwr, s);
+ pullwr_write(bmp->pullwr, bmq->data, bmq->len);
+
+ stream_free(s);
+ written = true;
+
+out:
+ if (!bmq->refcount)
+ XFREE(MTYPE_BMP_MIRRORQ, bmq);
+ return written;
+}
+
+static int bmp_outgoing_packet(struct peer *peer, uint8_t type, bgp_size_t size,
+ struct stream *packet)
+{
+ if (type == BGP_MSG_OPEN) {
+ frrtrace(2, frr_bgp, bmp_update_saved_open, peer, packet);
+
+ struct bmp_bgp_peer *bbpeer = bmp_bgp_peer_get(peer);
+
+ XFREE(MTYPE_BMP_OPEN, bbpeer->open_tx);
+
+ bbpeer->open_tx_len = size;
+ bbpeer->open_tx = XMALLOC(MTYPE_BMP_OPEN, size);
+ memcpy(bbpeer->open_tx, packet->data, size);
+ }
+ return 0;
+}
+
+static int bmp_peer_status_changed(struct peer *peer)
+{
+ struct bmp_bgp *bmpbgp = bmp_bgp_find(peer->bgp);
+ struct bmp_bgp_peer *bbpeer, *bbdopp;
+
+ frrtrace(1, frr_bgp, bmp_peer_status_changed, peer);
+
+ if (!bmpbgp)
+ return 0;
+
+ if (peer->status == Deleted) {
+ bbpeer = bmp_bgp_peer_find(peer->qobj_node.nid);
+ if (bbpeer) {
+ XFREE(MTYPE_BMP_OPEN, bbpeer->open_rx);
+ XFREE(MTYPE_BMP_OPEN, bbpeer->open_tx);
+ bmp_peerh_del(&bmp_peerh, bbpeer);
+ XFREE(MTYPE_BMP_PEER, bbpeer);
+ }
+ return 0;
+ }
+
+ /* Check if this peer just went to Established */
+ if ((peer->ostatus != OpenConfirm) || !(peer_established(peer)))
+ return 0;
+
+ if (peer->doppelganger && (peer->doppelganger->status != Deleted)) {
+ bbpeer = bmp_bgp_peer_get(peer);
+ bbdopp = bmp_bgp_peer_find(peer->doppelganger->qobj_node.nid);
+ if (bbdopp) {
+ XFREE(MTYPE_BMP_OPEN, bbpeer->open_tx);
+ XFREE(MTYPE_BMP_OPEN, bbpeer->open_rx);
+
+ bbpeer->open_tx = bbdopp->open_tx;
+ bbpeer->open_tx_len = bbdopp->open_tx_len;
+ bbpeer->open_rx = bbdopp->open_rx;
+ bbpeer->open_rx_len = bbdopp->open_rx_len;
+
+ bmp_peerh_del(&bmp_peerh, bbdopp);
+ XFREE(MTYPE_BMP_PEER, bbdopp);
+ }
+ }
+
+ bmp_send_all(bmpbgp, bmp_peerstate(peer, false));
+ return 0;
+}
+
+static int bmp_peer_backward(struct peer *peer)
+{
+ struct bmp_bgp *bmpbgp = bmp_bgp_find(peer->bgp);
+ struct bmp_bgp_peer *bbpeer;
+
+ frrtrace(1, frr_bgp, bmp_peer_backward_transition, peer);
+
+ if (!bmpbgp)
+ return 0;
+
+ bbpeer = bmp_bgp_peer_find(peer->qobj_node.nid);
+ if (bbpeer) {
+ XFREE(MTYPE_BMP_OPEN, bbpeer->open_tx);
+ bbpeer->open_tx_len = 0;
+ XFREE(MTYPE_BMP_OPEN, bbpeer->open_rx);
+ bbpeer->open_rx_len = 0;
+ }
+
+ bmp_send_all(bmpbgp, bmp_peerstate(peer, true));
+ return 0;
+}
+
+static void bmp_eor(struct bmp *bmp, afi_t afi, safi_t safi, uint8_t flags)
+{
+ struct peer *peer;
+ struct listnode *node;
+ struct stream *s, *s2;
+ iana_afi_t pkt_afi = IANA_AFI_IPV4;
+ iana_safi_t pkt_safi = IANA_SAFI_UNICAST;
+
+ frrtrace(3, frr_bgp, bmp_eor, afi, safi, flags);
+
+ s = stream_new(BGP_MAX_PACKET_SIZE);
+
+ /* Make BGP update packet. */
+ bgp_packet_set_marker(s, BGP_MSG_UPDATE);
+
+ /* Unfeasible Routes Length */
+ stream_putw(s, 0);
+
+ if (afi == AFI_IP && safi == SAFI_UNICAST) {
+ /* Total Path Attribute Length */
+ stream_putw(s, 0);
+ } else {
+ /* Convert AFI, SAFI to values for packet. */
+ bgp_map_afi_safi_int2iana(afi, safi, &pkt_afi, &pkt_safi);
+
+ /* Total Path Attribute Length */
+ stream_putw(s, 6);
+ stream_putc(s, BGP_ATTR_FLAG_OPTIONAL);
+ stream_putc(s, BGP_ATTR_MP_UNREACH_NLRI);
+ stream_putc(s, 3);
+ stream_putw(s, pkt_afi);
+ stream_putc(s, pkt_safi);
+ }
+
+ bgp_packet_set_size(s);
+
+ for (ALL_LIST_ELEMENTS_RO(bmp->targets->bgp->peer, node, peer)) {
+ if (!peer->afc_nego[afi][safi])
+ continue;
+
+ s2 = stream_new(BGP_MAX_PACKET_SIZE);
+
+ bmp_common_hdr(s2, BMP_VERSION_3,
+ BMP_TYPE_ROUTE_MONITORING);
+ bmp_per_peer_hdr(s2, peer, flags, NULL);
+
+ stream_putl_at(s2, BMP_LENGTH_POS,
+ stream_get_endp(s) + stream_get_endp(s2));
+
+ bmp->cnt_update++;
+ pullwr_write_stream(bmp->pullwr, s2);
+ pullwr_write_stream(bmp->pullwr, s);
+ stream_free(s2);
+ }
+ stream_free(s);
+}
+
+static struct stream *bmp_update(const struct prefix *p, struct prefix_rd *prd,
+ struct peer *peer, struct attr *attr,
+ afi_t afi, safi_t safi)
+{
+ struct bpacket_attr_vec_arr vecarr;
+ struct stream *s;
+ size_t attrlen_pos = 0, mpattrlen_pos = 0;
+ bgp_size_t total_attr_len = 0;
+
+ bpacket_attr_vec_arr_reset(&vecarr);
+
+ s = stream_new(BGP_MAX_PACKET_SIZE);
+ bgp_packet_set_marker(s, BGP_MSG_UPDATE);
+
+ /* 2: withdrawn routes length */
+ stream_putw(s, 0);
+
+ /* 3: total attributes length - attrlen_pos stores the position */
+ attrlen_pos = stream_get_endp(s);
+ stream_putw(s, 0);
+
+ /* 5: Encode all the attributes, except MP_REACH_NLRI attr. */
+ total_attr_len = bgp_packet_attribute(NULL, peer, s, attr,
+ &vecarr, NULL, afi, safi, peer, NULL, NULL, 0, 0, 0);
+
+ /* space check? */
+
+ /* peer_cap_enhe & add-path removed */
+ if (afi == AFI_IP && safi == SAFI_UNICAST)
+ stream_put_prefix(s, p);
+ else {
+ size_t p1 = stream_get_endp(s);
+
+ /* MPLS removed for now */
+
+ mpattrlen_pos = bgp_packet_mpattr_start(s, peer, afi, safi,
+ &vecarr, attr);
+ bgp_packet_mpattr_prefix(s, afi, safi, p, prd, NULL, 0, 0, 0,
+ attr);
+ bgp_packet_mpattr_end(s, mpattrlen_pos);
+ total_attr_len += stream_get_endp(s) - p1;
+ }
+
+ /* set the total attribute length correctly */
+ stream_putw_at(s, attrlen_pos, total_attr_len);
+ bgp_packet_set_size(s);
+ return s;
+}
+
+static struct stream *bmp_withdraw(const struct prefix *p,
+ struct prefix_rd *prd, afi_t afi,
+ safi_t safi)
+{
+ struct stream *s;
+ size_t attrlen_pos = 0, mp_start, mplen_pos;
+ bgp_size_t total_attr_len = 0;
+ bgp_size_t unfeasible_len;
+
+ s = stream_new(BGP_MAX_PACKET_SIZE);
+
+ bgp_packet_set_marker(s, BGP_MSG_UPDATE);
+ stream_putw(s, 0);
+
+ if (afi == AFI_IP && safi == SAFI_UNICAST) {
+ stream_put_prefix(s, p);
+ unfeasible_len = stream_get_endp(s) - BGP_HEADER_SIZE
+ - BGP_UNFEASIBLE_LEN;
+ stream_putw_at(s, BGP_HEADER_SIZE, unfeasible_len);
+ stream_putw(s, 0);
+ } else {
+ attrlen_pos = stream_get_endp(s);
+ /* total attr length = 0 for now. reevaluate later */
+ stream_putw(s, 0);
+ mp_start = stream_get_endp(s);
+ mplen_pos = bgp_packet_mpunreach_start(s, afi, safi);
+
+ bgp_packet_mpunreach_prefix(s, p, afi, safi, prd, NULL, 0, 0, 0,
+ NULL);
+ /* Set the mp_unreach attr's length */
+ bgp_packet_mpunreach_end(s, mplen_pos);
+
+ /* Set total path attribute length. */
+ total_attr_len = stream_get_endp(s) - mp_start;
+ stream_putw_at(s, attrlen_pos, total_attr_len);
+ }
+
+ bgp_packet_set_size(s);
+ return s;
+}
+
+static void bmp_monitor(struct bmp *bmp, struct peer *peer, uint8_t flags,
+ const struct prefix *p, struct prefix_rd *prd,
+ struct attr *attr, afi_t afi, safi_t safi,
+ time_t uptime)
+{
+ struct stream *hdr, *msg;
+ struct timeval tv = { .tv_sec = uptime, .tv_usec = 0 };
+ struct timeval uptime_real;
+
+ monotime_to_realtime(&tv, &uptime_real);
+ if (attr)
+ msg = bmp_update(p, prd, peer, attr, afi, safi);
+ else
+ msg = bmp_withdraw(p, prd, afi, safi);
+
+ hdr = stream_new(BGP_MAX_PACKET_SIZE);
+ bmp_common_hdr(hdr, BMP_VERSION_3, BMP_TYPE_ROUTE_MONITORING);
+ bmp_per_peer_hdr(hdr, peer, flags, &uptime_real);
+
+ stream_putl_at(hdr, BMP_LENGTH_POS,
+ stream_get_endp(hdr) + stream_get_endp(msg));
+
+ bmp->cnt_update++;
+ pullwr_write_stream(bmp->pullwr, hdr);
+ pullwr_write_stream(bmp->pullwr, msg);
+ stream_free(hdr);
+ stream_free(msg);
+}
+
+static bool bmp_wrsync(struct bmp *bmp, struct pullwr *pullwr)
+{
+ afi_t afi;
+ safi_t safi;
+
+ if (bmp->syncafi == AFI_MAX) {
+ FOREACH_AFI_SAFI (afi, safi) {
+ if (bmp->afistate[afi][safi] != BMP_AFI_NEEDSYNC)
+ continue;
+
+ bmp->afistate[afi][safi] = BMP_AFI_SYNC;
+
+ bmp->syncafi = afi;
+ bmp->syncsafi = safi;
+ bmp->syncpeerid = 0;
+ memset(&bmp->syncpos, 0, sizeof(bmp->syncpos));
+ bmp->syncpos.family = afi2family(afi);
+ bmp->syncrdpos = NULL;
+ zlog_info("bmp[%s] %s %s sending table",
+ bmp->remote,
+ afi2str(bmp->syncafi),
+ safi2str(bmp->syncsafi));
+ /* break does not work here, 2 loops... */
+ goto afibreak;
+ }
+ if (bmp->syncafi == AFI_MAX)
+ return false;
+ }
+
+afibreak:
+ afi = bmp->syncafi;
+ safi = bmp->syncsafi;
+
+ if (!bmp->targets->afimon[afi][safi]) {
+ /* shouldn't happen */
+ bmp->afistate[afi][safi] = BMP_AFI_INACTIVE;
+ bmp->syncafi = AFI_MAX;
+ bmp->syncsafi = SAFI_MAX;
+ return true;
+ }
+
+ struct bgp_table *table = bmp->targets->bgp->rib[afi][safi];
+ struct bgp_dest *bn = NULL;
+ struct bgp_path_info *bpi = NULL, *bpiter;
+ struct bgp_adj_in *adjin = NULL, *adjiter;
+
+ if ((afi == AFI_L2VPN && safi == SAFI_EVPN) ||
+ (safi == SAFI_MPLS_VPN)) {
+ /* initialize syncrdpos to the first
+ * mid-layer table entry
+ */
+ if (!bmp->syncrdpos) {
+ bmp->syncrdpos = bgp_table_top(table);
+ if (!bmp->syncrdpos)
+ goto eor;
+ }
+
+ /* look for a valid mid-layer table */
+ do {
+ table = bgp_dest_get_bgp_table_info(bmp->syncrdpos);
+ if (table) {
+ break;
+ }
+ bmp->syncrdpos = bgp_route_next(bmp->syncrdpos);
+ } while (bmp->syncrdpos);
+
+ /* mid-layer table completed */
+ if (!bmp->syncrdpos)
+ goto eor;
+ }
+
+ bn = bgp_node_lookup(table, &bmp->syncpos);
+ do {
+ if (!bn) {
+ bn = bgp_table_get_next(table, &bmp->syncpos);
+ if (!bn) {
+ if ((afi == AFI_L2VPN && safi == SAFI_EVPN) ||
+ (safi == SAFI_MPLS_VPN)) {
+ /* reset bottom-layer pointer */
+ memset(&bmp->syncpos, 0,
+ sizeof(bmp->syncpos));
+ bmp->syncpos.family = afi2family(afi);
+ /* check whethere there is a valid
+ * next mid-layer table, otherwise
+ * declare table completed (eor)
+ */
+ for (bmp->syncrdpos = bgp_route_next(
+ bmp->syncrdpos);
+ bmp->syncrdpos;
+ bmp->syncrdpos = bgp_route_next(
+ bmp->syncrdpos))
+ if (bgp_dest_get_bgp_table_info(
+ bmp->syncrdpos))
+ return true;
+ }
+ eor:
+ zlog_info("bmp[%s] %s %s table completed (EoR)",
+ bmp->remote, afi2str(afi),
+ safi2str(safi));
+ bmp_eor(bmp, afi, safi, BMP_PEER_FLAG_L);
+ bmp_eor(bmp, afi, safi, 0);
+
+ bmp->afistate[afi][safi] = BMP_AFI_LIVE;
+ bmp->syncafi = AFI_MAX;
+ bmp->syncsafi = SAFI_MAX;
+ return true;
+ }
+ bmp->syncpeerid = 0;
+ prefix_copy(&bmp->syncpos, bgp_dest_get_prefix(bn));
+ }
+
+ if (bmp->targets->afimon[afi][safi] & BMP_MON_POSTPOLICY) {
+ for (bpiter = bgp_dest_get_bgp_path_info(bn); bpiter;
+ bpiter = bpiter->next) {
+ if (!CHECK_FLAG(bpiter->flags, BGP_PATH_VALID))
+ continue;
+ if (bpiter->peer->qobj_node.nid
+ <= bmp->syncpeerid)
+ continue;
+ if (bpi && bpiter->peer->qobj_node.nid
+ > bpi->peer->qobj_node.nid)
+ continue;
+ bpi = bpiter;
+ }
+ }
+ if (bmp->targets->afimon[afi][safi] & BMP_MON_PREPOLICY) {
+ for (adjiter = bn->adj_in; adjiter;
+ adjiter = adjiter->next) {
+ if (adjiter->peer->qobj_node.nid
+ <= bmp->syncpeerid)
+ continue;
+ if (adjin && adjiter->peer->qobj_node.nid
+ > adjin->peer->qobj_node.nid)
+ continue;
+ adjin = adjiter;
+ }
+ }
+ if (bpi || adjin)
+ break;
+
+ bn = NULL;
+ } while (1);
+
+ if (adjin && bpi
+ && adjin->peer->qobj_node.nid < bpi->peer->qobj_node.nid) {
+ bpi = NULL;
+ bmp->syncpeerid = adjin->peer->qobj_node.nid;
+ } else if (adjin && bpi
+ && adjin->peer->qobj_node.nid > bpi->peer->qobj_node.nid) {
+ adjin = NULL;
+ bmp->syncpeerid = bpi->peer->qobj_node.nid;
+ } else if (bpi) {
+ bmp->syncpeerid = bpi->peer->qobj_node.nid;
+ } else if (adjin) {
+ bmp->syncpeerid = adjin->peer->qobj_node.nid;
+ }
+
+ const struct prefix *bn_p = bgp_dest_get_prefix(bn);
+ struct prefix_rd *prd = NULL;
+ if (((afi == AFI_L2VPN) && (safi == SAFI_EVPN)) ||
+ (safi == SAFI_MPLS_VPN))
+ prd = (struct prefix_rd *)bgp_dest_get_prefix(bmp->syncrdpos);
+
+ if (bpi)
+ bmp_monitor(bmp, bpi->peer, BMP_PEER_FLAG_L, bn_p, prd,
+ bpi->attr, afi, safi, bpi->uptime);
+ if (adjin)
+ bmp_monitor(bmp, adjin->peer, 0, bn_p, prd, adjin->attr, afi,
+ safi, adjin->uptime);
+
+ if (bn)
+ bgp_dest_unlock_node(bn);
+
+ return true;
+}
+
+static struct bmp_queue_entry *bmp_pull(struct bmp *bmp)
+{
+ struct bmp_queue_entry *bqe;
+
+ bqe = bmp->queuepos;
+ if (!bqe)
+ return NULL;
+
+ bmp->queuepos = bmp_qlist_next(&bmp->targets->updlist, bqe);
+
+ bqe->refcount--;
+ if (!bqe->refcount) {
+ bmp_qhash_del(&bmp->targets->updhash, bqe);
+ bmp_qlist_del(&bmp->targets->updlist, bqe);
+ }
+ return bqe;
+}
+
+static bool bmp_wrqueue(struct bmp *bmp, struct pullwr *pullwr)
+{
+ struct bmp_queue_entry *bqe;
+ struct peer *peer;
+ struct bgp_dest *bn = NULL;
+ bool written = false;
+
+ bqe = bmp_pull(bmp);
+ if (!bqe)
+ return false;
+
+ afi_t afi = bqe->afi;
+ safi_t safi = bqe->safi;
+
+ switch (bmp->afistate[afi][safi]) {
+ case BMP_AFI_INACTIVE:
+ case BMP_AFI_NEEDSYNC:
+ goto out;
+ case BMP_AFI_SYNC:
+ if (prefix_cmp(&bqe->p, &bmp->syncpos) <= 0)
+ /* currently syncing but have already passed this
+ * prefix => send it. */
+ break;
+
+ /* currently syncing & haven't reached this prefix yet
+ * => it'll be sent as part of the table sync, no need here */
+ goto out;
+ case BMP_AFI_LIVE:
+ break;
+ }
+
+ peer = QOBJ_GET_TYPESAFE(bqe->peerid, peer);
+ if (!peer) {
+ zlog_info("bmp: skipping queued item for deleted peer");
+ goto out;
+ }
+ if (!peer_established(peer))
+ goto out;
+
+ bool is_vpn = (bqe->afi == AFI_L2VPN && bqe->safi == SAFI_EVPN) ||
+ (bqe->safi == SAFI_MPLS_VPN);
+
+ struct prefix_rd *prd = is_vpn ? &bqe->rd : NULL;
+ bn = bgp_afi_node_lookup(bmp->targets->bgp->rib[afi][safi], afi, safi,
+ &bqe->p, prd);
+
+
+ if (bmp->targets->afimon[afi][safi] & BMP_MON_POSTPOLICY) {
+ struct bgp_path_info *bpi;
+
+ for (bpi = bn ? bgp_dest_get_bgp_path_info(bn) : NULL; bpi;
+ bpi = bpi->next) {
+ if (!CHECK_FLAG(bpi->flags, BGP_PATH_VALID))
+ continue;
+ if (bpi->peer == peer)
+ break;
+ }
+
+ bmp_monitor(bmp, peer, BMP_PEER_FLAG_L, &bqe->p, prd,
+ bpi ? bpi->attr : NULL, afi, safi,
+ bpi ? bpi->uptime : monotime(NULL));
+ written = true;
+ }
+
+ if (bmp->targets->afimon[afi][safi] & BMP_MON_PREPOLICY) {
+ struct bgp_adj_in *adjin;
+
+ for (adjin = bn ? bn->adj_in : NULL; adjin;
+ adjin = adjin->next) {
+ if (adjin->peer == peer)
+ break;
+ }
+ bmp_monitor(bmp, peer, 0, &bqe->p, prd,
+ adjin ? adjin->attr : NULL, afi, safi,
+ adjin ? adjin->uptime : monotime(NULL));
+ written = true;
+ }
+
+out:
+ if (!bqe->refcount)
+ XFREE(MTYPE_BMP_QUEUE, bqe);
+
+ if (bn)
+ bgp_dest_unlock_node(bn);
+
+ return written;
+}
+
+static void bmp_wrfill(struct bmp *bmp, struct pullwr *pullwr)
+{
+ switch(bmp->state) {
+ case BMP_PeerUp:
+ bmp_send_peerup(bmp);
+ bmp->state = BMP_Run;
+ break;
+
+ case BMP_Run:
+ if (bmp_wrmirror(bmp, pullwr))
+ break;
+ if (bmp_wrqueue(bmp, pullwr))
+ break;
+ if (bmp_wrsync(bmp, pullwr))
+ break;
+ break;
+ }
+}
+
+static void bmp_wrerr(struct bmp *bmp, struct pullwr *pullwr, bool eof)
+{
+ if (eof)
+ zlog_info("bmp[%s] disconnected", bmp->remote);
+ else
+ flog_warn(EC_LIB_SYSTEM_CALL, "bmp[%s] connection error: %s",
+ bmp->remote, strerror(errno));
+
+ bmp_close(bmp);
+ bmp_free(bmp);
+}
+
+static void bmp_process_one(struct bmp_targets *bt, struct bgp *bgp, afi_t afi,
+ safi_t safi, struct bgp_dest *bn, struct peer *peer)
+{
+ struct bmp *bmp;
+ struct bmp_queue_entry *bqe, bqeref;
+ size_t refcount;
+
+ refcount = bmp_session_count(&bt->sessions);
+ if (refcount == 0)
+ return;
+
+ memset(&bqeref, 0, sizeof(bqeref));
+ prefix_copy(&bqeref.p, bgp_dest_get_prefix(bn));
+ bqeref.peerid = peer->qobj_node.nid;
+ bqeref.afi = afi;
+ bqeref.safi = safi;
+
+ if ((afi == AFI_L2VPN && safi == SAFI_EVPN && bn->pdest) ||
+ (safi == SAFI_MPLS_VPN))
+ prefix_copy(&bqeref.rd,
+ (struct prefix_rd *)bgp_dest_get_prefix(bn->pdest));
+
+ bqe = bmp_qhash_find(&bt->updhash, &bqeref);
+ if (bqe) {
+ if (bqe->refcount >= refcount)
+ /* nothing to do here */
+ return;
+
+ bmp_qlist_del(&bt->updlist, bqe);
+ } else {
+ bqe = XMALLOC(MTYPE_BMP_QUEUE, sizeof(*bqe));
+ memcpy(bqe, &bqeref, sizeof(*bqe));
+
+ bmp_qhash_add(&bt->updhash, bqe);
+ }
+
+ bqe->refcount = refcount;
+ bmp_qlist_add_tail(&bt->updlist, bqe);
+
+ frr_each (bmp_session, &bt->sessions, bmp)
+ if (!bmp->queuepos)
+ bmp->queuepos = bqe;
+}
+
+static int bmp_process(struct bgp *bgp, afi_t afi, safi_t safi,
+ struct bgp_dest *bn, struct peer *peer, bool withdraw)
+{
+ struct bmp_bgp *bmpbgp = bmp_bgp_find(peer->bgp);
+ struct bmp_targets *bt;
+ struct bmp *bmp;
+
+ if (frrtrace_enabled(frr_bgp, bmp_process)) {
+ char pfxprint[PREFIX2STR_BUFFER];
+
+ prefix2str(&bn->p, pfxprint, sizeof(pfxprint));
+ frrtrace(5, frr_bgp, bmp_process, peer, pfxprint, afi, safi,
+ withdraw);
+ }
+
+ if (!bmpbgp)
+ return 0;
+
+ frr_each(bmp_targets, &bmpbgp->targets, bt) {
+ if (!bt->afimon[afi][safi])
+ continue;
+
+ bmp_process_one(bt, bgp, afi, safi, bn, peer);
+
+ frr_each(bmp_session, &bt->sessions, bmp) {
+ pullwr_bump(bmp->pullwr);
+ }
+ }
+ return 0;
+}
+
+static void bmp_stat_put_u32(struct stream *s, size_t *cnt, uint16_t type,
+ uint32_t value)
+{
+ stream_putw(s, type);
+ stream_putw(s, 4);
+ stream_putl(s, value);
+ (*cnt)++;
+}
+
+static void bmp_stats(struct thread *thread)
+{
+ struct bmp_targets *bt = THREAD_ARG(thread);
+ struct stream *s;
+ struct peer *peer;
+ struct listnode *node;
+ struct timeval tv;
+
+ if (bt->stat_msec)
+ thread_add_timer_msec(bm->master, bmp_stats, bt, bt->stat_msec,
+ &bt->t_stats);
+
+ gettimeofday(&tv, NULL);
+
+ /* Walk down all peers */
+ for (ALL_LIST_ELEMENTS_RO(bt->bgp->peer, node, peer)) {
+ size_t count = 0, count_pos, len;
+
+ if (!peer_established(peer))
+ continue;
+
+ s = stream_new(BGP_MAX_PACKET_SIZE);
+ bmp_common_hdr(s, BMP_VERSION_3, BMP_TYPE_STATISTICS_REPORT);
+ bmp_per_peer_hdr(s, peer, 0, &tv);
+
+ count_pos = stream_get_endp(s);
+ stream_putl(s, 0);
+
+ bmp_stat_put_u32(s, &count, BMP_STATS_PFX_REJECTED,
+ peer->stat_pfx_filter);
+ bmp_stat_put_u32(s, &count, BMP_STATS_UPD_LOOP_ASPATH,
+ peer->stat_pfx_aspath_loop);
+ bmp_stat_put_u32(s, &count, BMP_STATS_UPD_LOOP_ORIGINATOR,
+ peer->stat_pfx_originator_loop);
+ bmp_stat_put_u32(s, &count, BMP_STATS_UPD_LOOP_CLUSTER,
+ peer->stat_pfx_cluster_loop);
+ bmp_stat_put_u32(s, &count, BMP_STATS_PFX_DUP_WITHDRAW,
+ peer->stat_pfx_dup_withdraw);
+ bmp_stat_put_u32(s, &count, BMP_STATS_UPD_7606_WITHDRAW,
+ peer->stat_upd_7606);
+ bmp_stat_put_u32(s, &count, BMP_STATS_FRR_NH_INVALID,
+ peer->stat_pfx_nh_invalid);
+
+ stream_putl_at(s, count_pos, count);
+
+ len = stream_get_endp(s);
+ stream_putl_at(s, BMP_LENGTH_POS, len);
+
+ bmp_send_all(bt->bmpbgp, s);
+ }
+}
+
+/* read from the BMP socket to detect session termination */
+static void bmp_read(struct thread *t)
+{
+ struct bmp *bmp = THREAD_ARG(t);
+ char buf[1024];
+ ssize_t n;
+
+ bmp->t_read = NULL;
+
+ n = read(bmp->socket, buf, sizeof(buf));
+ if (n >= 1) {
+ zlog_info("bmp[%s]: unexpectedly received %zu bytes", bmp->remote, n);
+ } else if (n == 0) {
+ /* the TCP session was terminated by the far end */
+ bmp_wrerr(bmp, NULL, true);
+ return;
+ } else if (!(errno == EAGAIN || errno == EWOULDBLOCK || errno == EINTR)) {
+ /* the TCP session experienced a fatal error, likely a timeout */
+ bmp_wrerr(bmp, NULL, false);
+ return;
+ }
+
+ thread_add_read(bm->master, bmp_read, bmp, bmp->socket, &bmp->t_read);
+}
+
+static struct bmp *bmp_open(struct bmp_targets *bt, int bmp_sock)
+{
+ union sockunion su, *sumem;
+ struct prefix p;
+ int on = 1;
+ struct access_list *acl = NULL;
+ enum filter_type ret;
+ char buf[SU_ADDRSTRLEN];
+ struct bmp *bmp;
+
+ sumem = sockunion_getpeername(bmp_sock);
+ if (!sumem) {
+ close(bmp_sock);
+ return NULL;
+ }
+ memcpy(&su, sumem, sizeof(su));
+ sockunion_free(sumem);
+
+ set_nonblocking(bmp_sock);
+ set_cloexec(bmp_sock);
+
+ if (!sockunion2hostprefix(&su, &p)) {
+ close(bmp_sock);
+ return NULL;
+ }
+
+ acl = NULL;
+ switch (p.family) {
+ case AF_INET:
+ acl = access_list_lookup(AFI_IP, bt->acl_name);
+ break;
+ case AF_INET6:
+ acl = access_list_lookup(AFI_IP6, bt->acl6_name);
+ break;
+ default:
+ break;
+ }
+
+ ret = FILTER_PERMIT;
+ if (acl) {
+ ret = access_list_apply(acl, &p);
+ }
+
+ sockunion2str(&su, buf, SU_ADDRSTRLEN);
+ snprintf(buf + strlen(buf), sizeof(buf) - strlen(buf), ":%u",
+ su.sa.sa_family == AF_INET
+ ? ntohs(su.sin.sin_port)
+ : ntohs(su.sin6.sin6_port));
+
+ if (ret == FILTER_DENY) {
+ bt->cnt_aclrefused++;
+ zlog_info("bmp[%s] connection refused by access-list", buf);
+ close(bmp_sock);
+ return NULL;
+ }
+ bt->cnt_accept++;
+
+ if (setsockopt(bmp_sock, SOL_SOCKET, SO_KEEPALIVE, &on, sizeof(on)) < 0)
+ flog_err(EC_LIB_SOCKET, "bmp: %d can't setsockopt SO_KEEPALIVE: %s(%d)",
+ bmp_sock, safe_strerror(errno), errno);
+ if (setsockopt(bmp_sock, IPPROTO_TCP, TCP_NODELAY, &on, sizeof(on)) < 0)
+ flog_err(EC_LIB_SOCKET, "bmp: %d can't setsockopt TCP_NODELAY: %s(%d)",
+ bmp_sock, safe_strerror(errno), errno);
+
+ zlog_info("bmp[%s] connection established", buf);
+
+ /* Allocate new BMP structure and set up default values. */
+ bmp = bmp_new(bt, bmp_sock);
+ strlcpy(bmp->remote, buf, sizeof(bmp->remote));
+
+ bmp->state = BMP_PeerUp;
+ bmp->pullwr = pullwr_new(bm->master, bmp_sock, bmp, bmp_wrfill,
+ bmp_wrerr);
+ thread_add_read(bm->master, bmp_read, bmp, bmp_sock, &bmp->t_read);
+ bmp_send_initiation(bmp);
+
+ return bmp;
+}
+
+/* Accept BMP connection. */
+static void bmp_accept(struct thread *thread)
+{
+ union sockunion su;
+ struct bmp_listener *bl = THREAD_ARG(thread);
+ int bmp_sock;
+
+ /* We continue hearing BMP socket. */
+ thread_add_read(bm->master, bmp_accept, bl, bl->sock, &bl->t_accept);
+
+ memset(&su, 0, sizeof(union sockunion));
+
+ /* We can handle IPv4 or IPv6 socket. */
+ bmp_sock = sockunion_accept(bl->sock, &su);
+ if (bmp_sock < 0) {
+ zlog_info("bmp: accept_sock failed: %s", safe_strerror(errno));
+ return;
+ }
+ bmp_open(bl->targets, bmp_sock);
+}
+
+static void bmp_close(struct bmp *bmp)
+{
+ struct bmp_queue_entry *bqe;
+ struct bmp_mirrorq *bmq;
+
+ THREAD_OFF(bmp->t_read);
+
+ if (bmp->active)
+ bmp_active_disconnected(bmp->active);
+
+ while ((bmq = bmp_pull_mirror(bmp)))
+ if (!bmq->refcount)
+ XFREE(MTYPE_BMP_MIRRORQ, bmq);
+ while ((bqe = bmp_pull(bmp)))
+ if (!bqe->refcount)
+ XFREE(MTYPE_BMP_QUEUE, bqe);
+
+ THREAD_OFF(bmp->t_read);
+ pullwr_del(bmp->pullwr);
+ close(bmp->socket);
+}
+
+static struct bmp_bgp *bmp_bgp_find(struct bgp *bgp)
+{
+ struct bmp_bgp dummy = { .bgp = bgp };
+ return bmp_bgph_find(&bmp_bgph, &dummy);
+}
+
+static struct bmp_bgp *bmp_bgp_get(struct bgp *bgp)
+{
+ struct bmp_bgp *bmpbgp;
+
+ bmpbgp = bmp_bgp_find(bgp);
+ if (bmpbgp)
+ return bmpbgp;
+
+ bmpbgp = XCALLOC(MTYPE_BMP, sizeof(*bmpbgp));
+ bmpbgp->bgp = bgp;
+ bmpbgp->mirror_qsizelimit = ~0UL;
+ bmp_mirrorq_init(&bmpbgp->mirrorq);
+ bmp_bgph_add(&bmp_bgph, bmpbgp);
+
+ return bmpbgp;
+}
+
+static void bmp_bgp_put(struct bmp_bgp *bmpbgp)
+{
+ struct bmp_targets *bt;
+ struct bmp_listener *bl;
+
+ bmp_bgph_del(&bmp_bgph, bmpbgp);
+
+ frr_each_safe (bmp_targets, &bmpbgp->targets, bt) {
+ frr_each_safe (bmp_listeners, &bt->listeners, bl)
+ bmp_listener_put(bl);
+
+ bmp_targets_put(bt);
+ }
+
+ bmp_mirrorq_fini(&bmpbgp->mirrorq);
+ XFREE(MTYPE_BMP, bmpbgp);
+}
+
+static int bmp_bgp_del(struct bgp *bgp)
+{
+ struct bmp_bgp *bmpbgp = bmp_bgp_find(bgp);
+
+ if (bmpbgp)
+ bmp_bgp_put(bmpbgp);
+ return 0;
+}
+
+static struct bmp_bgp_peer *bmp_bgp_peer_find(uint64_t peerid)
+{
+ struct bmp_bgp_peer dummy = { .peerid = peerid };
+ return bmp_peerh_find(&bmp_peerh, &dummy);
+}
+
+static struct bmp_bgp_peer *bmp_bgp_peer_get(struct peer *peer)
+{
+ struct bmp_bgp_peer *bbpeer;
+
+ bbpeer = bmp_bgp_peer_find(peer->qobj_node.nid);
+ if (bbpeer)
+ return bbpeer;
+
+ bbpeer = XCALLOC(MTYPE_BMP_PEER, sizeof(*bbpeer));
+ bbpeer->peerid = peer->qobj_node.nid;
+ bmp_peerh_add(&bmp_peerh, bbpeer);
+
+ return bbpeer;
+}
+
+static struct bmp_targets *bmp_targets_find1(struct bgp *bgp, const char *name)
+{
+ struct bmp_bgp *bmpbgp = bmp_bgp_find(bgp);
+ struct bmp_targets dummy;
+
+ if (!bmpbgp)
+ return NULL;
+ dummy.name = (char *)name;
+ return bmp_targets_find(&bmpbgp->targets, &dummy);
+}
+
+static struct bmp_targets *bmp_targets_get(struct bgp *bgp, const char *name)
+{
+ struct bmp_targets *bt;
+
+ bt = bmp_targets_find1(bgp, name);
+ if (bt)
+ return bt;
+
+ bt = XCALLOC(MTYPE_BMP_TARGETS, sizeof(*bt));
+ bt->name = XSTRDUP(MTYPE_BMP_TARGETSNAME, name);
+ bt->bgp = bgp;
+ bt->bmpbgp = bmp_bgp_get(bgp);
+ bmp_session_init(&bt->sessions);
+ bmp_qhash_init(&bt->updhash);
+ bmp_qlist_init(&bt->updlist);
+ bmp_actives_init(&bt->actives);
+ bmp_listeners_init(&bt->listeners);
+
+ QOBJ_REG(bt, bmp_targets);
+ bmp_targets_add(&bt->bmpbgp->targets, bt);
+ return bt;
+}
+
+static void bmp_targets_put(struct bmp_targets *bt)
+{
+ struct bmp *bmp;
+ struct bmp_active *ba;
+
+ THREAD_OFF(bt->t_stats);
+
+ frr_each_safe (bmp_actives, &bt->actives, ba)
+ bmp_active_put(ba);
+
+ frr_each_safe(bmp_session, &bt->sessions, bmp) {
+ bmp_close(bmp);
+ bmp_free(bmp);
+ }
+
+ bmp_targets_del(&bt->bmpbgp->targets, bt);
+ QOBJ_UNREG(bt);
+
+ bmp_listeners_fini(&bt->listeners);
+ bmp_actives_fini(&bt->actives);
+ bmp_qhash_fini(&bt->updhash);
+ bmp_qlist_fini(&bt->updlist);
+
+ XFREE(MTYPE_BMP_ACLNAME, bt->acl_name);
+ XFREE(MTYPE_BMP_ACLNAME, bt->acl6_name);
+ bmp_session_fini(&bt->sessions);
+
+ XFREE(MTYPE_BMP_TARGETSNAME, bt->name);
+ XFREE(MTYPE_BMP_TARGETS, bt);
+}
+
+static struct bmp_listener *bmp_listener_find(struct bmp_targets *bt,
+ const union sockunion *su,
+ int port)
+{
+ struct bmp_listener dummy;
+ dummy.addr = *su;
+ dummy.port = port;
+ return bmp_listeners_find(&bt->listeners, &dummy);
+}
+
+static struct bmp_listener *bmp_listener_get(struct bmp_targets *bt,
+ const union sockunion *su,
+ int port)
+{
+ struct bmp_listener *bl = bmp_listener_find(bt, su, port);
+
+ if (bl)
+ return bl;
+
+ bl = XCALLOC(MTYPE_BMP_LISTENER, sizeof(*bl));
+ bl->targets = bt;
+ bl->addr = *su;
+ bl->port = port;
+ bl->sock = -1;
+
+ bmp_listeners_add(&bt->listeners, bl);
+ return bl;
+}
+
+static void bmp_listener_start(struct bmp_listener *bl)
+{
+ int sock, ret;
+
+ sock = socket(bl->addr.sa.sa_family, SOCK_STREAM, 0);
+ if (sock < 0)
+ return;
+
+ sockopt_reuseaddr(sock);
+ sockopt_reuseport(sock);
+ sockopt_v6only(bl->addr.sa.sa_family, sock);
+ set_cloexec(sock);
+
+ ret = sockunion_bind(sock, &bl->addr, bl->port, &bl->addr);
+ if (ret < 0)
+ goto out_sock;
+
+ ret = listen(sock, 3);
+ if (ret < 0)
+ goto out_sock;
+
+ bl->sock = sock;
+ thread_add_read(bm->master, bmp_accept, bl, sock, &bl->t_accept);
+ return;
+out_sock:
+ close(sock);
+}
+
+static void bmp_listener_stop(struct bmp_listener *bl)
+{
+ THREAD_OFF(bl->t_accept);
+
+ if (bl->sock != -1)
+ close(bl->sock);
+ bl->sock = -1;
+}
+
+static struct bmp_active *bmp_active_find(struct bmp_targets *bt,
+ const char *hostname, int port)
+{
+ struct bmp_active dummy;
+ dummy.hostname = (char *)hostname;
+ dummy.port = port;
+ return bmp_actives_find(&bt->actives, &dummy);
+}
+
+static struct bmp_active *bmp_active_get(struct bmp_targets *bt,
+ const char *hostname, int port)
+{
+ struct bmp_active *ba;
+
+ ba = bmp_active_find(bt, hostname, port);
+ if (ba)
+ return ba;
+
+ ba = XCALLOC(MTYPE_BMP_ACTIVE, sizeof(*ba));
+ ba->targets = bt;
+ ba->hostname = XSTRDUP(MTYPE_TMP, hostname);
+ ba->port = port;
+ ba->minretry = BMP_DFLT_MINRETRY;
+ ba->maxretry = BMP_DFLT_MAXRETRY;
+ ba->socket = -1;
+
+ bmp_actives_add(&bt->actives, ba);
+ return ba;
+}
+
+static void bmp_active_put(struct bmp_active *ba)
+{
+ THREAD_OFF(ba->t_timer);
+ THREAD_OFF(ba->t_read);
+ THREAD_OFF(ba->t_write);
+
+ bmp_actives_del(&ba->targets->actives, ba);
+
+ if (ba->bmp) {
+ ba->bmp->active = NULL;
+ bmp_close(ba->bmp);
+ bmp_free(ba->bmp);
+ }
+ if (ba->socket != -1)
+ close(ba->socket);
+
+ XFREE(MTYPE_TMP, ba->ifsrc);
+ XFREE(MTYPE_TMP, ba->hostname);
+ XFREE(MTYPE_BMP_ACTIVE, ba);
+}
+
+static void bmp_active_setup(struct bmp_active *ba);
+
+static void bmp_active_connect(struct bmp_active *ba)
+{
+ enum connect_result res;
+ struct interface *ifp;
+ vrf_id_t vrf_id = VRF_DEFAULT;
+ int res_bind;
+
+ for (; ba->addrpos < ba->addrtotal; ba->addrpos++) {
+ if (ba->ifsrc) {
+ if (ba->targets && ba->targets->bgp)
+ vrf_id = ba->targets->bgp->vrf_id;
+
+ /* find interface and related */
+ /* address with same family */
+ ifp = if_lookup_by_name(ba->ifsrc, vrf_id);
+ if (!ifp) {
+ zlog_warn("bmp[%s]: failed to find interface",
+ ba->ifsrc);
+ continue;
+ }
+
+ if (bgp_update_address(ifp, &ba->addrs[ba->addrpos],
+ &ba->addrsrc)){
+ zlog_warn("bmp[%s]: failed to find matching address",
+ ba->ifsrc);
+ continue;
+ }
+ zlog_info("bmp[%s]: selected source address : %pSU",
+ ba->ifsrc, &ba->addrsrc);
+ }
+
+ ba->socket = sockunion_socket(&ba->addrs[ba->addrpos]);
+ if (ba->socket < 0) {
+ zlog_warn("bmp[%s]: failed to create socket",
+ ba->hostname);
+ continue;
+ }
+
+ set_nonblocking(ba->socket);
+
+ if (!sockunion_is_null(&ba->addrsrc)) {
+ res_bind = sockunion_bind(ba->socket, &ba->addrsrc, 0,
+ &ba->addrsrc);
+ if (res_bind < 0) {
+ zlog_warn(
+ "bmp[%s]: no bind currently to source address %pSU:%d",
+ ba->hostname, &ba->addrsrc, ba->port);
+ close(ba->socket);
+ ba->socket = -1;
+ sockunion_init(&ba->addrsrc);
+ continue;
+ }
+ }
+
+
+ res = sockunion_connect(ba->socket, &ba->addrs[ba->addrpos],
+ htons(ba->port), 0);
+ switch (res) {
+ case connect_error:
+ zlog_warn("bmp[%s]: failed to connect to %pSU:%d",
+ ba->hostname, &ba->addrs[ba->addrpos],
+ ba->port);
+ close(ba->socket);
+ ba->socket = -1;
+ sockunion_init(&ba->addrsrc);
+ continue;
+ case connect_success:
+ zlog_info("bmp[%s]: connected to %pSU:%d",
+ ba->hostname, &ba->addrs[ba->addrpos],
+ ba->port);
+ break;
+ case connect_in_progress:
+ zlog_warn("bmp[%s]: connect in progress %pSU:%d",
+ ba->hostname, &ba->addrs[ba->addrpos],
+ ba->port);
+ bmp_active_setup(ba);
+ return;
+ }
+ }
+
+ /* exhausted all addresses */
+ ba->curretry += ba->curretry / 2;
+ bmp_active_setup(ba);
+}
+
+static void bmp_active_resolved(struct resolver_query *resq, const char *errstr,
+ int numaddrs, union sockunion *addr)
+{
+ struct bmp_active *ba = container_of(resq, struct bmp_active, resq);
+ unsigned i;
+
+ if (numaddrs <= 0) {
+ zlog_warn("bmp[%s]: hostname resolution failed: %s",
+ ba->hostname, errstr);
+ ba->last_err = errstr;
+ ba->curretry += ba->curretry / 2;
+ ba->addrpos = 0;
+ ba->addrtotal = 0;
+ bmp_active_setup(ba);
+ return;
+ }
+
+ if (numaddrs > (int)array_size(ba->addrs))
+ numaddrs = array_size(ba->addrs);
+
+ ba->addrpos = 0;
+ ba->addrtotal = numaddrs;
+ for (i = 0; i < ba->addrtotal; i++)
+ memcpy(&ba->addrs[i], &addr[i], sizeof(ba->addrs[0]));
+
+ bmp_active_connect(ba);
+}
+
+static void bmp_active_thread(struct thread *t)
+{
+ struct bmp_active *ba = THREAD_ARG(t);
+ socklen_t slen;
+ int status, ret;
+ vrf_id_t vrf_id;
+
+ /* all 3 end up here, though only timer or read+write are active
+ * at a time */
+ THREAD_OFF(ba->t_timer);
+ THREAD_OFF(ba->t_read);
+ THREAD_OFF(ba->t_write);
+
+ ba->last_err = NULL;
+
+ if (ba->socket == -1) {
+ /* get vrf_id */
+ if (!ba->targets || !ba->targets->bgp)
+ vrf_id = VRF_DEFAULT;
+ else
+ vrf_id = ba->targets->bgp->vrf_id;
+ resolver_resolve(&ba->resq, AF_UNSPEC, vrf_id, ba->hostname,
+ bmp_active_resolved);
+ return;
+ }
+
+ slen = sizeof(status);
+ ret = getsockopt(ba->socket, SOL_SOCKET, SO_ERROR, (void *)&status,
+ &slen);
+
+ if (ret < 0 || status != 0) {
+ ba->last_err = strerror(status);
+ zlog_warn("bmp[%s]: failed to connect to %pSU:%d: %s",
+ ba->hostname, &ba->addrs[ba->addrpos], ba->port,
+ ba->last_err);
+ goto out_next;
+ }
+
+ zlog_warn("bmp[%s]: outbound connection to %pSU:%d", ba->hostname,
+ &ba->addrs[ba->addrpos], ba->port);
+
+ ba->bmp = bmp_open(ba->targets, ba->socket);
+ if (!ba->bmp)
+ goto out_next;
+
+ ba->bmp->active = ba;
+ ba->socket = -1;
+ ba->curretry = ba->minretry;
+ return;
+
+out_next:
+ close(ba->socket);
+ ba->socket = -1;
+ ba->addrpos++;
+ bmp_active_connect(ba);
+}
+
+static void bmp_active_disconnected(struct bmp_active *ba)
+{
+ ba->bmp = NULL;
+ bmp_active_setup(ba);
+}
+
+static void bmp_active_setup(struct bmp_active *ba)
+{
+ THREAD_OFF(ba->t_timer);
+ THREAD_OFF(ba->t_read);
+ THREAD_OFF(ba->t_write);
+
+ if (ba->bmp)
+ return;
+ if (ba->resq.callback)
+ return;
+
+ if (ba->curretry > ba->maxretry)
+ ba->curretry = ba->maxretry;
+
+ if (ba->socket == -1)
+ thread_add_timer_msec(bm->master, bmp_active_thread, ba,
+ ba->curretry, &ba->t_timer);
+ else {
+ thread_add_read(bm->master, bmp_active_thread, ba, ba->socket,
+ &ba->t_read);
+ thread_add_write(bm->master, bmp_active_thread, ba, ba->socket,
+ &ba->t_write);
+ }
+}
+
+static struct cmd_node bmp_node = {
+ .name = "bmp",
+ .node = BMP_NODE,
+ .parent_node = BGP_NODE,
+ .prompt = "%s(config-bgp-bmp)# "
+};
+
+static void bmp_targets_autocomplete(vector comps, struct cmd_token *token)
+{
+ struct bgp *bgp;
+ struct bmp_targets *target;
+ struct listnode *node;
+
+ for (ALL_LIST_ELEMENTS_RO(bm->bgp, node, bgp)) {
+ struct bmp_bgp *bmpbgp = bmp_bgp_find(bgp);
+
+ if (!bmpbgp)
+ continue;
+
+ frr_each_safe (bmp_targets, &bmpbgp->targets, target)
+ vector_set(comps,
+ XSTRDUP(MTYPE_COMPLETION, target->name));
+ }
+}
+
+static const struct cmd_variable_handler bmp_targets_var_handlers[] = {
+ {.tokenname = "BMPTARGETS", .completions = bmp_targets_autocomplete},
+ {.completions = NULL}};
+
+#define BMP_STR "BGP Monitoring Protocol\n"
+
+#ifndef VTYSH_EXTRACT_PL
+#include "bgpd/bgp_bmp_clippy.c"
+#endif
+
+DEFPY_NOSH(bmp_targets_main,
+ bmp_targets_cmd,
+ "bmp targets BMPTARGETS",
+ BMP_STR
+ "Create BMP target group\n"
+ "Name of the BMP target group\n")
+{
+ VTY_DECLVAR_CONTEXT(bgp, bgp);
+ struct bmp_targets *bt;
+
+ bt = bmp_targets_get(bgp, bmptargets);
+
+ VTY_PUSH_CONTEXT_SUB(BMP_NODE, bt);
+ return CMD_SUCCESS;
+}
+
+DEFPY(no_bmp_targets_main,
+ no_bmp_targets_cmd,
+ "no bmp targets BMPTARGETS",
+ NO_STR
+ BMP_STR
+ "Delete BMP target group\n"
+ "Name of the BMP target group\n")
+{
+ VTY_DECLVAR_CONTEXT(bgp, bgp);
+ struct bmp_targets *bt;
+
+ bt = bmp_targets_find1(bgp, bmptargets);
+ if (!bt) {
+ vty_out(vty, "%% BMP target group not found\n");
+ return CMD_WARNING;
+ }
+ bmp_targets_put(bt);
+ return CMD_SUCCESS;
+}
+
+DEFPY(bmp_listener_main,
+ bmp_listener_cmd,
+ "bmp listener <X:X::X:X|A.B.C.D> port (1-65535)",
+ BMP_STR
+ "Listen for inbound BMP connections\n"
+ "IPv6 address to listen on\n"
+ "IPv4 address to listen on\n"
+ "TCP Port number\n"
+ "TCP Port number\n")
+{
+ VTY_DECLVAR_CONTEXT_SUB(bmp_targets, bt);
+ struct bmp_listener *bl;
+
+ bl = bmp_listener_get(bt, listener, port);
+ if (bl->sock == -1)
+ bmp_listener_start(bl);
+
+ return CMD_SUCCESS;
+}
+
+DEFPY(no_bmp_listener_main,
+ no_bmp_listener_cmd,
+ "no bmp listener <X:X::X:X|A.B.C.D> port (1-65535)",
+ NO_STR
+ BMP_STR
+ "Create BMP listener\n"
+ "IPv6 address to listen on\n"
+ "IPv4 address to listen on\n"
+ "TCP Port number\n"
+ "TCP Port number\n")
+{
+ VTY_DECLVAR_CONTEXT_SUB(bmp_targets, bt);
+ struct bmp_listener *bl;
+
+ bl = bmp_listener_find(bt, listener, port);
+ if (!bl) {
+ vty_out(vty, "%% BMP listener not found\n");
+ return CMD_WARNING;
+ }
+ bmp_listener_stop(bl);
+ bmp_listener_put(bl);
+ return CMD_SUCCESS;
+}
+
+DEFPY(bmp_connect,
+ bmp_connect_cmd,
+ "[no] bmp connect HOSTNAME port (1-65535) {min-retry (100-86400000)|max-retry (100-86400000)} [source-interface <WORD$srcif>]",
+ NO_STR
+ BMP_STR
+ "Actively establish connection to monitoring station\n"
+ "Monitoring station hostname or address\n"
+ "TCP port\n"
+ "TCP port\n"
+ "Minimum connection retry interval\n"
+ "Minimum connection retry interval (milliseconds)\n"
+ "Maximum connection retry interval\n"
+ "Maximum connection retry interval (milliseconds)\n"
+ "Source interface to use\n"
+ "Define an interface\n")
+{
+ VTY_DECLVAR_CONTEXT_SUB(bmp_targets, bt);
+ struct bmp_active *ba;
+
+ if (no) {
+ ba = bmp_active_find(bt, hostname, port);
+ if (!ba) {
+ vty_out(vty, "%% No such active connection found\n");
+ return CMD_WARNING;
+ }
+ /* connection deletion need same hostname port and interface */
+ if (ba->ifsrc || srcif)
+ if ((!ba->ifsrc) || (!srcif) ||
+ !strcmp(ba->ifsrc, srcif)) {
+ vty_out(vty,
+ "%% No such active connection found\n");
+ return CMD_WARNING;
+ }
+ bmp_active_put(ba);
+ return CMD_SUCCESS;
+ }
+
+ ba = bmp_active_get(bt, hostname, port);
+ if (srcif)
+ ba->ifsrc = XSTRDUP(MTYPE_TMP, srcif);
+ if (min_retry_str)
+ ba->minretry = min_retry;
+ if (max_retry_str)
+ ba->maxretry = max_retry;
+ ba->curretry = ba->minretry;
+ bmp_active_setup(ba);
+
+ return CMD_SUCCESS;
+}
+
+DEFPY(bmp_acl,
+ bmp_acl_cmd,
+ "[no] <ip|ipv6>$af access-list ACCESSLIST_NAME$access_list",
+ NO_STR
+ IP_STR
+ IPV6_STR
+ "Access list to restrict BMP sessions\n"
+ "Access list name\n")
+{
+ VTY_DECLVAR_CONTEXT_SUB(bmp_targets, bt);
+ char **what;
+
+ if (no)
+ access_list = NULL;
+ if (!strcmp(af, "ipv6"))
+ what = &bt->acl6_name;
+ else
+ what = &bt->acl_name;
+
+ XFREE(MTYPE_BMP_ACLNAME, *what);
+ if (access_list)
+ *what = XSTRDUP(MTYPE_BMP_ACLNAME, access_list);
+
+ return CMD_SUCCESS;
+}
+
+DEFPY(bmp_stats_cfg,
+ bmp_stats_cmd,
+ "[no] bmp stats [interval (100-86400000)]",
+ NO_STR
+ BMP_STR
+ "Send BMP statistics messages\n"
+ "Specify BMP stats interval\n"
+ "Interval (milliseconds) to send BMP Stats in\n")
+{
+ VTY_DECLVAR_CONTEXT_SUB(bmp_targets, bt);
+
+ THREAD_OFF(bt->t_stats);
+ if (no)
+ bt->stat_msec = 0;
+ else if (interval_str)
+ bt->stat_msec = interval;
+ else
+ bt->stat_msec = BMP_STAT_DEFAULT_TIMER;
+
+ if (bt->stat_msec)
+ thread_add_timer_msec(bm->master, bmp_stats, bt, bt->stat_msec,
+ &bt->t_stats);
+ return CMD_SUCCESS;
+}
+
+DEFPY(bmp_monitor_cfg,
+ bmp_monitor_cmd,
+ "[no] bmp monitor <ipv4|ipv6|l2vpn> <unicast|multicast|evpn|vpn> <pre-policy|post-policy>$policy",
+ NO_STR
+ BMP_STR
+ "Send BMP route monitoring messages\n"
+ BGP_AF_STR
+ BGP_AF_STR
+ BGP_AF_STR
+ BGP_AF_STR
+ BGP_AF_STR
+ BGP_AF_STR
+ BGP_AF_STR
+ "Send state before policy and filter processing\n"
+ "Send state with policy and filters applied\n")
+{
+ int index = 0;
+ uint8_t flag, prev;
+ afi_t afi;
+ safi_t safi;
+
+ VTY_DECLVAR_CONTEXT_SUB(bmp_targets, bt);
+ struct bmp *bmp;
+
+ argv_find_and_parse_afi(argv, argc, &index, &afi);
+ argv_find_and_parse_safi(argv, argc, &index, &safi);
+
+ if (policy[1] == 'r')
+ flag = BMP_MON_PREPOLICY;
+ else
+ flag = BMP_MON_POSTPOLICY;
+
+ prev = bt->afimon[afi][safi];
+ if (no)
+ bt->afimon[afi][safi] &= ~flag;
+ else
+ bt->afimon[afi][safi] |= flag;
+
+ if (prev == bt->afimon[afi][safi])
+ return CMD_SUCCESS;
+
+ frr_each (bmp_session, &bt->sessions, bmp) {
+ if (bmp->syncafi == afi && bmp->syncsafi == safi) {
+ bmp->syncafi = AFI_MAX;
+ bmp->syncsafi = SAFI_MAX;
+ }
+
+ if (!bt->afimon[afi][safi]) {
+ bmp->afistate[afi][safi] = BMP_AFI_INACTIVE;
+ continue;
+ }
+
+ bmp->afistate[afi][safi] = BMP_AFI_NEEDSYNC;
+ }
+
+ return CMD_SUCCESS;
+}
+
+DEFPY(bmp_mirror_cfg,
+ bmp_mirror_cmd,
+ "[no] bmp mirror",
+ NO_STR
+ BMP_STR
+ "Send BMP route mirroring messages\n")
+{
+ VTY_DECLVAR_CONTEXT_SUB(bmp_targets, bt);
+ struct bmp *bmp;
+
+ if (bt->mirror == !no)
+ return CMD_SUCCESS;
+
+ bt->mirror = !no;
+ if (bt->mirror)
+ return CMD_SUCCESS;
+
+ frr_each (bmp_session, &bt->sessions, bmp) {
+ struct bmp_mirrorq *bmq;
+
+ while ((bmq = bmp_pull_mirror(bmp)))
+ if (!bmq->refcount)
+ XFREE(MTYPE_BMP_MIRRORQ, bmq);
+ }
+ return CMD_SUCCESS;
+}
+
+DEFPY(bmp_mirror_limit_cfg,
+ bmp_mirror_limit_cmd,
+ "bmp mirror buffer-limit (0-4294967294)",
+ BMP_STR
+ "Route Mirroring settings\n"
+ "Configure maximum memory used for buffered mirroring messages\n"
+ "Limit in bytes\n")
+{
+ VTY_DECLVAR_CONTEXT(bgp, bgp);
+ struct bmp_bgp *bmpbgp;
+
+ bmpbgp = bmp_bgp_get(bgp);
+ bmpbgp->mirror_qsizelimit = buffer_limit;
+
+ return CMD_SUCCESS;
+}
+
+DEFPY(no_bmp_mirror_limit_cfg,
+ no_bmp_mirror_limit_cmd,
+ "no bmp mirror buffer-limit [(0-4294967294)]",
+ NO_STR
+ BMP_STR
+ "Route Mirroring settings\n"
+ "Configure maximum memory used for buffered mirroring messages\n"
+ "Limit in bytes\n")
+{
+ VTY_DECLVAR_CONTEXT(bgp, bgp);
+ struct bmp_bgp *bmpbgp;
+
+ bmpbgp = bmp_bgp_get(bgp);
+ bmpbgp->mirror_qsizelimit = ~0UL;
+
+ return CMD_SUCCESS;
+}
+
+
+DEFPY(show_bmp,
+ show_bmp_cmd,
+ "show bmp",
+ SHOW_STR
+ BMP_STR)
+{
+ struct bmp_bgp *bmpbgp;
+ struct bmp_targets *bt;
+ struct bmp_listener *bl;
+ struct bmp_active *ba;
+ struct bmp *bmp;
+ struct ttable *tt;
+ char uptime[BGP_UPTIME_LEN];
+ char *out;
+
+ frr_each(bmp_bgph, &bmp_bgph, bmpbgp) {
+ vty_out(vty, "BMP state for BGP %s:\n\n",
+ bmpbgp->bgp->name_pretty);
+ vty_out(vty, " Route Mirroring %9zu bytes (%zu messages) pending\n",
+ bmpbgp->mirror_qsize,
+ bmp_mirrorq_count(&bmpbgp->mirrorq));
+ vty_out(vty, " %9zu bytes maximum buffer used\n",
+ bmpbgp->mirror_qsizemax);
+ if (bmpbgp->mirror_qsizelimit != ~0UL)
+ vty_out(vty, " %9zu bytes buffer size limit\n",
+ bmpbgp->mirror_qsizelimit);
+ vty_out(vty, "\n");
+
+ frr_each(bmp_targets, &bmpbgp->targets, bt) {
+ vty_out(vty, " Targets \"%s\":\n", bt->name);
+ vty_out(vty, " Route Mirroring %sabled\n",
+ bt->mirror ? "en" : "dis");
+
+ afi_t afi;
+ safi_t safi;
+
+ FOREACH_AFI_SAFI (afi, safi) {
+ const char *str = NULL;
+
+ switch (bt->afimon[afi][safi]) {
+ case BMP_MON_PREPOLICY:
+ str = "pre-policy";
+ break;
+ case BMP_MON_POSTPOLICY:
+ str = "post-policy";
+ break;
+ case BMP_MON_PREPOLICY | BMP_MON_POSTPOLICY:
+ str = "pre-policy and post-policy";
+ break;
+ }
+ if (!str)
+ continue;
+ vty_out(vty, " Route Monitoring %s %s %s\n",
+ afi2str(afi), safi2str(safi), str);
+ }
+
+ vty_out(vty, " Listeners:\n");
+ frr_each (bmp_listeners, &bt->listeners, bl)
+ vty_out(vty, " %pSU:%d\n", &bl->addr,
+ bl->port);
+
+ vty_out(vty, "\n Outbound connections:\n");
+ tt = ttable_new(&ttable_styles[TTSTYLE_BLANK]);
+ ttable_add_row(tt, "remote|state||timer|local");
+ ttable_rowseps(tt, 0, BOTTOM, true, '-');
+ frr_each (bmp_actives, &bt->actives, ba) {
+ const char *state_str = "?";
+
+ if (ba->bmp) {
+ peer_uptime(ba->bmp->t_up.tv_sec,
+ uptime, sizeof(uptime),
+ false, NULL);
+ ttable_add_row(tt,
+ "%s:%d|Up|%s|%s|%pSU",
+ ba->hostname, ba->port,
+ ba->bmp->remote, uptime,
+ &ba->addrsrc);
+ continue;
+ }
+
+ uptime[0] = '\0';
+
+ if (ba->t_timer) {
+ long trem = thread_timer_remain_second(
+ ba->t_timer);
+
+ peer_uptime(monotime(NULL) - trem,
+ uptime, sizeof(uptime),
+ false, NULL);
+ state_str = "RetryWait";
+ } else if (ba->t_read) {
+ state_str = "Connecting";
+ } else if (ba->resq.callback) {
+ state_str = "Resolving";
+ }
+
+ ttable_add_row(tt, "%s:%d|%s|%s|%s|%pSU",
+ ba->hostname, ba->port,
+ state_str,
+ ba->last_err ? ba->last_err : "",
+ uptime, &ba->addrsrc);
+ continue;
+ }
+ out = ttable_dump(tt, "\n");
+ vty_out(vty, "%s", out);
+ XFREE(MTYPE_TMP, out);
+ ttable_del(tt);
+
+ vty_out(vty, "\n %zu connected clients:\n",
+ bmp_session_count(&bt->sessions));
+ tt = ttable_new(&ttable_styles[TTSTYLE_BLANK]);
+ ttable_add_row(tt, "remote|uptime|MonSent|MirrSent|MirrLost|ByteSent|ByteQ|ByteQKernel");
+ ttable_rowseps(tt, 0, BOTTOM, true, '-');
+
+ frr_each (bmp_session, &bt->sessions, bmp) {
+ uint64_t total;
+ size_t q, kq;
+
+ pullwr_stats(bmp->pullwr, &total, &q, &kq);
+
+ peer_uptime(bmp->t_up.tv_sec, uptime,
+ sizeof(uptime), false, NULL);
+
+ ttable_add_row(tt, "%s|%s|%Lu|%Lu|%Lu|%Lu|%zu|%zu",
+ bmp->remote, uptime,
+ bmp->cnt_update,
+ bmp->cnt_mirror,
+ bmp->cnt_mirror_overruns,
+ total, q, kq);
+ }
+ out = ttable_dump(tt, "\n");
+ vty_out(vty, "%s", out);
+ XFREE(MTYPE_TMP, out);
+ ttable_del(tt);
+ vty_out(vty, "\n");
+ }
+ }
+
+ return CMD_SUCCESS;
+}
+
+static int bmp_config_write(struct bgp *bgp, struct vty *vty)
+{
+ struct bmp_bgp *bmpbgp = bmp_bgp_find(bgp);
+ struct bmp_targets *bt;
+ struct bmp_listener *bl;
+ struct bmp_active *ba;
+ afi_t afi;
+ safi_t safi;
+
+ if (!bmpbgp)
+ return 0;
+
+ if (bmpbgp->mirror_qsizelimit != ~0UL)
+ vty_out(vty, " !\n bmp mirror buffer-limit %zu\n",
+ bmpbgp->mirror_qsizelimit);
+
+ frr_each(bmp_targets, &bmpbgp->targets, bt) {
+ vty_out(vty, " !\n bmp targets %s\n", bt->name);
+
+ if (bt->acl6_name)
+ vty_out(vty, " ipv6 access-list %s\n", bt->acl6_name);
+ if (bt->acl_name)
+ vty_out(vty, " ip access-list %s\n", bt->acl_name);
+
+ if (bt->stat_msec)
+ vty_out(vty, " bmp stats interval %d\n",
+ bt->stat_msec);
+
+ if (bt->mirror)
+ vty_out(vty, " bmp mirror\n");
+
+ FOREACH_AFI_SAFI (afi, safi) {
+ const char *afi_str = (afi == AFI_IP) ? "ipv4" : "ipv6";
+
+ if (bt->afimon[afi][safi] & BMP_MON_PREPOLICY)
+ vty_out(vty, " bmp monitor %s %s pre-policy\n",
+ afi_str, safi2str(safi));
+ if (bt->afimon[afi][safi] & BMP_MON_POSTPOLICY)
+ vty_out(vty, " bmp monitor %s %s post-policy\n",
+ afi_str, safi2str(safi));
+ }
+ frr_each (bmp_listeners, &bt->listeners, bl)
+ vty_out(vty, " \n bmp listener %pSU port %d\n",
+ &bl->addr, bl->port);
+
+ frr_each (bmp_actives, &bt->actives, ba) {
+ vty_out(vty, " bmp connect %s port %u min-retry %u max-retry %u",
+ ba->hostname, ba->port,
+ ba->minretry, ba->maxretry);
+
+ if (ba->ifsrc)
+ vty_out(vty, " source-interface %s\n", ba->ifsrc);
+ else
+ vty_out(vty, "\n");
+ }
+ vty_out(vty, " exit\n");
+ }
+
+ return 0;
+}
+
+static int bgp_bmp_init(struct thread_master *tm)
+{
+ install_node(&bmp_node);
+ install_default(BMP_NODE);
+
+ cmd_variable_handler_register(bmp_targets_var_handlers);
+
+ install_element(BGP_NODE, &bmp_targets_cmd);
+ install_element(BGP_NODE, &no_bmp_targets_cmd);
+
+ install_element(BMP_NODE, &bmp_listener_cmd);
+ install_element(BMP_NODE, &no_bmp_listener_cmd);
+ install_element(BMP_NODE, &bmp_connect_cmd);
+ install_element(BMP_NODE, &bmp_acl_cmd);
+ install_element(BMP_NODE, &bmp_stats_cmd);
+ install_element(BMP_NODE, &bmp_monitor_cmd);
+ install_element(BMP_NODE, &bmp_mirror_cmd);
+
+ install_element(BGP_NODE, &bmp_mirror_limit_cmd);
+ install_element(BGP_NODE, &no_bmp_mirror_limit_cmd);
+
+ install_element(VIEW_NODE, &show_bmp_cmd);
+
+ resolver_init(tm);
+ return 0;
+}
+
+static int bgp_bmp_module_init(void)
+{
+ hook_register(bgp_packet_dump, bmp_mirror_packet);
+ hook_register(bgp_packet_send, bmp_outgoing_packet);
+ hook_register(peer_status_changed, bmp_peer_status_changed);
+ hook_register(peer_backward_transition, bmp_peer_backward);
+ hook_register(bgp_process, bmp_process);
+ hook_register(bgp_inst_config_write, bmp_config_write);
+ hook_register(bgp_inst_delete, bmp_bgp_del);
+ hook_register(frr_late_init, bgp_bmp_init);
+ return 0;
+}
+
+FRR_MODULE_SETUP(.name = "bgpd_bmp", .version = FRR_VERSION,
+ .description = "bgpd BMP module",
+ .init = bgp_bmp_module_init,
+);
diff --git a/bgpd/bgp_bmp.h b/bgpd/bgp_bmp.h
new file mode 100644
index 0000000..1693b44
--- /dev/null
+++ b/bgpd/bgp_bmp.h
@@ -0,0 +1,316 @@
+/* BMP support.
+ * Copyright (C) 2018 Yasuhiro Ohara
+ * Copyright (C) 2019 David Lamparter for NetDEF, Inc.
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License as published by the Free
+ * Software Foundation; either version 2 of the License, or (at your option)
+ * any later version.
+ *
+ * This program is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
+ * more details.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program; see the file COPYING; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
+ */
+
+#ifndef _BGP_BMP_H_
+#define _BGP_BMP_H_
+
+#include "zebra.h"
+#include "typesafe.h"
+#include "pullwr.h"
+#include "qobj.h"
+#include "resolver.h"
+
+#define BMP_VERSION_3 3
+
+#define BMP_LENGTH_POS 1
+
+/* BMP message types */
+#define BMP_TYPE_ROUTE_MONITORING 0
+#define BMP_TYPE_STATISTICS_REPORT 1
+#define BMP_TYPE_PEER_DOWN_NOTIFICATION 2
+#define BMP_TYPE_PEER_UP_NOTIFICATION 3
+#define BMP_TYPE_INITIATION 4
+#define BMP_TYPE_TERMINATION 5
+#define BMP_TYPE_ROUTE_MIRRORING 6
+
+#define BMP_READ_BUFSIZ 1024
+
+/* bmp->state */
+#define BMP_None 0
+#define BMP_PeerUp 2
+#define BMP_Run 3
+
+/* This one is for BMP Route Monitoring messages, i.e. delivering updates
+ * in somewhat processed (as opposed to fully raw, see mirroring below) form.
+ * RFC explicitly says that we can skip old updates if we haven't sent them out
+ * yet and another newer update for the same prefix arrives.
+ *
+ * So, at most one of these can exist for each (bgp, afi, safi, prefix, peerid)
+ * tuple; if some prefix is "re-added" to the queue, the existing entry is
+ * instead moved to the end of the queue. This ensures that the queue size is
+ * bounded by the BGP table size.
+ *
+ * bmp_qlist is the queue itself while bmp_qhash is used to efficiently check
+ * whether a tuple is already on the list. The queue is maintained per
+ * bmp_target.
+ *
+ * refcount = number of "struct bmp *" whose queue position is before this
+ * entry, i.e. number of BMP sessions where we still want to send this out.
+ * Decremented on send so we know when we're done with an entry (i.e. this
+ * always happens from the front of the queue.)
+ */
+
+PREDECL_DLIST(bmp_qlist);
+PREDECL_HASH(bmp_qhash);
+
+struct bmp_queue_entry {
+ struct bmp_qlist_item bli;
+ struct bmp_qhash_item bhi;
+
+ struct prefix p;
+ uint64_t peerid;
+ afi_t afi;
+ safi_t safi;
+
+ size_t refcount;
+
+ /* initialized only for L2VPN/EVPN (S)AFIs */
+ struct prefix_rd rd;
+};
+
+/* This is for BMP Route Mirroring, which feeds fully raw BGP PDUs out to BMP
+ * receivers. So, this goes directly off packet RX/TX handling instead of
+ * grabbing bits from tables.
+ *
+ * There is *one* queue for each "struct bgp *" where we throw everything on,
+ * with a size limit. Refcount works the same as for monitoring above.
+ */
+
+PREDECL_LIST(bmp_mirrorq);
+
+struct bmp_mirrorq {
+ struct bmp_mirrorq_item bmi;
+
+ size_t refcount;
+ uint64_t peerid;
+ struct timeval tv;
+
+ size_t len;
+ uint8_t data[0];
+};
+
+enum {
+ BMP_AFI_INACTIVE = 0,
+ BMP_AFI_NEEDSYNC,
+ BMP_AFI_SYNC,
+ BMP_AFI_LIVE,
+};
+
+PREDECL_LIST(bmp_session);
+
+struct bmp_active;
+struct bmp_targets;
+
+/* an established BMP session to a peer */
+struct bmp {
+ struct bmp_session_item bsi;
+ struct bmp_targets *targets;
+ struct bmp_active *active;
+
+ int socket;
+ char remote[SU_ADDRSTRLEN + 6];
+ struct thread *t_read;
+
+ struct pullwr *pullwr;
+
+ int state;
+
+ /* queue positions must remain synced with refcounts in the items.
+ * Whenever appending a queue item, we need to know the correct number
+ * of "struct bmp *" that want it, and when moving these positions
+ * ahead we need to make sure that refcount is decremented. Also, on
+ * disconnects we need to walk the queue and drop our reference.
+ */
+ struct bmp_queue_entry *queuepos;
+ struct bmp_mirrorq *mirrorpos;
+ bool mirror_lost;
+
+ /* enum BMP_AFI_* */
+ uint8_t afistate[AFI_MAX][SAFI_MAX];
+
+ /* counters for the various BMP packet types */
+ uint64_t cnt_update, cnt_mirror;
+ /* number of times this peer wasn't fast enough in consuming the
+ * mirror queue
+ */
+ uint64_t cnt_mirror_overruns;
+ struct timeval t_up;
+
+ /* synchronization / startup works by repeatedly finding the next
+ * table entry, the sync* fields note down what we sent last
+ */
+ struct prefix syncpos;
+ struct bgp_dest *syncrdpos;
+ uint64_t syncpeerid;
+ afi_t syncafi;
+ safi_t syncsafi;
+};
+
+/* config & state for an active outbound connection. When the connection
+ * succeeds, "bmp" is set up.
+ */
+
+PREDECL_SORTLIST_UNIQ(bmp_actives);
+
+#define BMP_DFLT_MINRETRY 30000
+#define BMP_DFLT_MAXRETRY 720000
+
+struct bmp_active {
+ struct bmp_actives_item bai;
+ struct bmp_targets *targets;
+ struct bmp *bmp;
+
+ char *hostname;
+ int port;
+ unsigned minretry, maxretry;
+ char *ifsrc;
+ union sockunion addrsrc;
+
+ struct resolver_query resq;
+
+ unsigned curretry;
+ unsigned addrpos, addrtotal;
+ union sockunion addrs[8];
+ int socket;
+ const char *last_err;
+ struct thread *t_timer, *t_read, *t_write;
+};
+
+/* config & state for passive / listening sockets */
+PREDECL_SORTLIST_UNIQ(bmp_listeners);
+
+struct bmp_listener {
+ struct bmp_listeners_item bli;
+
+ struct bmp_targets *targets;
+
+ union sockunion addr;
+ int port;
+
+ struct thread *t_accept;
+ int sock;
+};
+
+/* bmp_targets - plural since it may contain multiple bmp_listener &
+ * bmp_active items. If they have the same config, BMP session should be
+ * put in the same targets since that's a bit more effective.
+ */
+PREDECL_SORTLIST_UNIQ(bmp_targets);
+
+struct bmp_targets {
+ struct bmp_targets_item bti;
+
+ struct bmp_bgp *bmpbgp;
+ struct bgp *bgp;
+ char *name;
+
+ struct bmp_listeners_head listeners;
+
+ char *acl_name;
+ char *acl6_name;
+#define BMP_STAT_DEFAULT_TIMER 60000
+ int stat_msec;
+
+ /* only supporting:
+ * - IPv4 / unicast & multicast
+ * - IPv6 / unicast & multicast
+ * - L2VPN / EVPN
+ */
+#define BMP_MON_PREPOLICY (1 << 0)
+#define BMP_MON_POSTPOLICY (1 << 1)
+ uint8_t afimon[AFI_MAX][SAFI_MAX];
+ bool mirror;
+
+ struct bmp_actives_head actives;
+
+ struct thread *t_stats;
+ struct bmp_session_head sessions;
+
+ struct bmp_qhash_head updhash;
+ struct bmp_qlist_head updlist;
+
+ uint64_t cnt_accept, cnt_aclrefused;
+
+ QOBJ_FIELDS;
+};
+DECLARE_QOBJ_TYPE(bmp_targets);
+
+/* per struct peer * data. Lookup by peer->qobj_node.nid, created on demand,
+ * deleted in peer_backward hook. */
+PREDECL_HASH(bmp_peerh);
+
+struct bmp_bgp_peer {
+ struct bmp_peerh_item bpi;
+
+ uint64_t peerid;
+ /* struct peer *peer; */
+
+ uint8_t *open_rx;
+ size_t open_rx_len;
+
+ uint8_t *open_tx;
+ size_t open_tx_len;
+};
+
+/* per struct bgp * data */
+PREDECL_HASH(bmp_bgph);
+
+#define BMP_PEER_DOWN_NO_RELEVANT_EVENT_CODE 0x00
+
+struct bmp_bgp {
+ struct bmp_bgph_item bbi;
+
+ struct bgp *bgp;
+ struct bmp_targets_head targets;
+
+ struct bmp_mirrorq_head mirrorq;
+ size_t mirror_qsize, mirror_qsizemax;
+
+ size_t mirror_qsizelimit;
+};
+
+enum {
+ BMP_PEERDOWN_LOCAL_NOTIFY = 1,
+ BMP_PEERDOWN_LOCAL_FSM = 2,
+ BMP_PEERDOWN_REMOTE_NOTIFY = 3,
+ BMP_PEERDOWN_REMOTE_CLOSE = 4,
+ BMP_PEERDOWN_ENDMONITOR = 5,
+};
+
+enum {
+ BMP_STATS_PFX_REJECTED = 0,
+ BMP_STATS_PFX_DUP_ADV = 1,
+ BMP_STATS_PFX_DUP_WITHDRAW = 2,
+ BMP_STATS_UPD_LOOP_CLUSTER = 3,
+ BMP_STATS_UPD_LOOP_ASPATH = 4,
+ BMP_STATS_UPD_LOOP_ORIGINATOR = 5,
+ BMP_STATS_UPD_LOOP_CONFED = 6,
+ BMP_STATS_SIZE_ADJ_RIB_IN = 7,
+ BMP_STATS_SIZE_LOC_RIB = 8,
+ BMP_STATS_SIZE_ADJ_RIB_IN_SAFI = 9,
+ BMP_STATS_SIZE_LOC_RIB_IN_SAFI = 10,
+ BMP_STATS_UPD_7606_WITHDRAW = 11,
+ BMP_STATS_PFX_7606_WITHDRAW = 12,
+ BMP_STATS_UPD_DUP = 13,
+ BMP_STATS_FRR_NH_INVALID = 65531,
+};
+
+DECLARE_MGROUP(BMP);
+
+#endif /*_BGP_BMP_H_*/
diff --git a/bgpd/bgp_btoa.c b/bgpd/bgp_btoa.c
new file mode 100644
index 0000000..aa14d99
--- /dev/null
+++ b/bgpd/bgp_btoa.c
@@ -0,0 +1,288 @@
+/* BGP dump to ascii converter
+ * Copyright (C) 1999 Kunihiro Ishiguro
+ *
+ * This file is part of GNU Zebra.
+ *
+ * GNU Zebra is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License as published by the
+ * Free Software Foundation; either version 2, or (at your option) any
+ * later version.
+ *
+ * GNU Zebra is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program; see the file COPYING; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
+ */
+
+#include <zebra.h>
+
+#include "zebra.h"
+#include "stream.h"
+#include "log.h"
+#include "prefix.h"
+#include "command.h"
+#include "memory.h"
+#include "privs.h"
+#include "filter.h"
+
+#include "bgpd/bgpd.h"
+#include "bgpd/bgp_dump.h"
+#include "bgpd/bgp_attr.h"
+#include "bgpd/bgp_aspath.h"
+
+/* privileges */
+static zebra_capabilities_t _caps_p[] = {
+ ZCAP_BIND, ZCAP_NET_RAW, ZCAP_NET_ADMIN,
+};
+
+struct zebra_privs_t bgpd_privs = {
+#if defined(FRR_USER) && defined(FRR_GROUP)
+ .user = FRR_USER,
+ .group = FRR_GROUP,
+#endif
+#ifdef VTY_GROUP
+ .vty_group = VTY_GROUP,
+#endif
+ .caps_p = _caps_p,
+ .cap_num_p = array_size(_caps_p),
+ .cap_num_i = 0,
+};
+
+enum MRT_MSG_TYPES {
+ MSG_NULL,
+ MSG_START, /* sender is starting up */
+ MSG_DIE, /* receiver should shut down */
+ MSG_I_AM_DEAD, /* sender is shutting down */
+ MSG_PEER_DOWN, /* sender's peer is down */
+ MSG_PROTOCOL_BGP, /* msg is a BGP packet */
+ MSG_PROTOCOL_RIP, /* msg is a RIP packet */
+ MSG_PROTOCOL_IDRP, /* msg is an IDRP packet */
+ MSG_PROTOCOL_RIPNG, /* msg is a RIPNG packet */
+ MSG_PROTOCOL_BGP4PLUS, /* msg is a BGP4+ packet */
+ MSG_PROTOCOL_BGP4PLUS_01, /* msg is a BGP4+ (draft 01) packet */
+ MSG_PROTOCOL_OSPF, /* msg is an OSPF packet */
+ MSG_TABLE_DUMP /* routing table dump */
+};
+
+static void attr_parse(struct stream *s, uint16_t len)
+{
+ unsigned int flag;
+ unsigned int type;
+ uint16_t length;
+ uint16_t lim;
+
+ lim = s->getp + len;
+
+ printf("%s s->getp %zd, len %d, lim %d\n", __func__, s->getp, len, lim);
+
+ while (s->getp < lim) {
+ flag = stream_getc(s);
+ type = stream_getc(s);
+
+ if (flag & BGP_ATTR_FLAG_EXTLEN)
+ length = stream_getw(s);
+ else
+ length = stream_getc(s);
+
+ printf("FLAG: %d\n", flag);
+ printf("TYPE: %d\n", type);
+ printf("Len: %d\n", length);
+
+ switch (type) {
+ case BGP_ATTR_ORIGIN: {
+ uint8_t origin;
+ origin = stream_getc(s);
+ printf("ORIGIN: %d\n", origin);
+ } break;
+ case BGP_ATTR_AS_PATH: {
+ struct aspath *aspath;
+
+ aspath = aspath_parse(s, length, 1);
+ printf("ASPATH: %s\n", aspath->str);
+ aspath_free(aspath);
+ } break;
+ case BGP_ATTR_NEXT_HOP: {
+ struct in_addr nexthop;
+ nexthop.s_addr = stream_get_ipv4(s);
+ printf("NEXTHOP: %pI4\n", &nexthop);
+ } break;
+ default:
+ stream_getw_from(s, length);
+ break;
+ }
+ }
+}
+
+int main(int argc, char **argv)
+{
+ int ret;
+ int fd;
+ struct stream *s;
+ time_t now;
+ int type;
+ int subtype;
+ size_t len;
+ int source_as;
+ int dest_as;
+ ifindex_t ifindex;
+ int family;
+ struct in_addr sip;
+ struct in_addr dip;
+ uint16_t viewno, seq_num;
+ struct prefix_ipv4 p;
+
+ s = stream_new(10000);
+
+ if (argc != 2) {
+ fprintf(stderr, "Usage: %s FILENAME\n", argv[0]);
+ exit(1);
+ }
+ fd = open(argv[1], O_RDONLY);
+ if (fd < 0) {
+ fprintf(stdout,
+ "%% Can't open configuration file %s due to '%s'.\n",
+ argv[1], safe_strerror(errno));
+ exit(1);
+ }
+
+ while (1) {
+ stream_reset(s);
+
+ ret = stream_read(s, fd, 12);
+ if (ret != 12) {
+ if (!ret)
+ printf("END OF FILE\n");
+ else if (ret < 0)
+ printf("ERROR OF READ\n");
+ else
+ printf("UNDERFLOW\n");
+ break;
+ }
+
+ /* Extract header. */
+ now = stream_getl(s);
+ type = stream_getw(s);
+ subtype = stream_getw(s);
+ len = stream_getl(s);
+
+ printf("TIME: %s", ctime(&now));
+
+ /* printf ("TYPE: %d/%d\n", type, subtype); */
+
+ if (type == MSG_PROTOCOL_BGP4MP)
+ printf("TYPE: BGP4MP");
+ else if (type == MSG_PROTOCOL_BGP4MP_ET)
+ printf("TYPE: BGP4MP_ET");
+ else if (type == MSG_TABLE_DUMP)
+ printf("TYPE: MSG_TABLE_DUMP");
+ else
+ printf("TYPE: Unknown %d", type);
+
+ if (type == MSG_TABLE_DUMP)
+ switch (subtype) {
+ case AFI_IP:
+ printf("/AFI_IP\n");
+ break;
+ case AFI_IP6:
+ printf("/AFI_IP6\n");
+ break;
+ default:
+ printf("/UNKNOWN %d", subtype);
+ break;
+ }
+ else {
+ switch (subtype) {
+ case BGP4MP_STATE_CHANGE:
+ printf("/CHANGE\n");
+ break;
+ case BGP4MP_MESSAGE:
+ printf("/MESSAGE\n");
+ break;
+ case BGP4MP_ENTRY:
+ printf("/ENTRY\n");
+ break;
+ case BGP4MP_SNAPSHOT:
+ printf("/SNAPSHOT\n");
+ break;
+ default:
+ printf("/UNKNOWN %d", subtype);
+ break;
+ }
+ }
+
+ printf("len: %zd\n", len);
+
+ ret = stream_read(s, fd, len);
+ if (ret != (int)len) {
+ if (!ret)
+ printf("END OF FILE 2\n");
+ else if (ret < 0)
+ printf("ERROR OF READ 2\n");
+ else
+ printf("UNDERFLOW 2\n");
+ break;
+ }
+
+ /* printf ("now read %d\n", len); */
+
+ if (type == MSG_TABLE_DUMP) {
+ uint8_t status;
+ time_t originated;
+ struct in_addr peer;
+ uint16_t attrlen;
+
+ viewno = stream_getw(s);
+ seq_num = stream_getw(s);
+ printf("VIEW: %d\n", viewno);
+ printf("SEQUENCE: %d\n", seq_num);
+
+ /* start */
+ while (s->getp < len - 16) {
+ p.prefix.s_addr = stream_get_ipv4(s);
+ p.prefixlen = stream_getc(s);
+ printf("PREFIX: %pI4/%d\n", &p.prefix,
+ p.prefixlen);
+
+ status = stream_getc(s);
+ originated = stream_getl(s);
+ peer.s_addr = stream_get_ipv4(s);
+ source_as = stream_getw(s);
+
+ printf("FROM: %pI4 AS%d\n", &peer, source_as);
+ printf("ORIGINATED: %s", ctime(&originated));
+
+ attrlen = stream_getw(s);
+ printf("ATTRLEN: %d\n", attrlen);
+
+ attr_parse(s, attrlen);
+
+ printf("STATUS: 0x%x\n", status);
+ }
+ } else {
+ source_as = stream_getw(s);
+ dest_as = stream_getw(s);
+ printf("source_as: %d\n", source_as);
+ printf("dest_as: %d\n", dest_as);
+
+ ifindex = stream_getw(s);
+ family = stream_getw(s);
+
+ printf("ifindex: %d\n", ifindex);
+ printf("family: %d\n", family);
+
+ sip.s_addr = stream_get_ipv4(s);
+ dip.s_addr = stream_get_ipv4(s);
+
+ printf("saddr: %pI4\n", &sip);
+ printf("daddr: %pI4\n", &dip);
+
+ printf("\n");
+ }
+ }
+ close(fd);
+ return 0;
+}
diff --git a/bgpd/bgp_clist.c b/bgpd/bgp_clist.c
new file mode 100644
index 0000000..34d4be8
--- /dev/null
+++ b/bgpd/bgp_clist.c
@@ -0,0 +1,1447 @@
+/* BGP community-list and extcommunity-list.
+ * Copyright (C) 1999 Kunihiro Ishiguro
+ *
+ * This file is part of GNU Zebra.
+ *
+ * GNU Zebra is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License as published by the
+ * Free Software Foundation; either version 2, or (at your option) any
+ * later version.
+ *
+ * GNU Zebra is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program; see the file COPYING; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
+ */
+
+#include <zebra.h>
+
+#include "command.h"
+#include "prefix.h"
+#include "memory.h"
+#include "queue.h"
+#include "filter.h"
+#include "stream.h"
+#include "jhash.h"
+#include "frrstr.h"
+
+#include "bgpd/bgpd.h"
+#include "bgpd/bgp_community.h"
+#include "bgpd/bgp_ecommunity.h"
+#include "bgpd/bgp_lcommunity.h"
+#include "bgpd/bgp_community_alias.h"
+#include "bgpd/bgp_aspath.h"
+#include "bgpd/bgp_regex.h"
+#include "bgpd/bgp_clist.h"
+
+/* Calculate new sequential number. */
+static int64_t bgp_clist_new_seq_get(struct community_list *list)
+{
+ int64_t maxseq;
+ int64_t newseq;
+ struct community_entry *entry;
+
+ maxseq = 0;
+
+ for (entry = list->head; entry; entry = entry->next) {
+ if (maxseq < entry->seq)
+ maxseq = entry->seq;
+ }
+
+ newseq = ((maxseq / 5) * 5) + 5;
+
+ return (newseq > UINT_MAX) ? UINT_MAX : newseq;
+}
+
+/* Return community-list entry which has same seq number. */
+static struct community_entry *bgp_clist_seq_check(struct community_list *list,
+ int64_t seq)
+{
+ struct community_entry *entry;
+
+ for (entry = list->head; entry; entry = entry->next)
+ if (entry->seq == seq)
+ return entry;
+ return NULL;
+}
+
+static uint32_t bgp_clist_hash_key_community_list(const void *data)
+{
+ struct community_list *cl = (struct community_list *) data;
+
+ if (cl->name_hash)
+ return cl->name_hash;
+
+ cl->name_hash = bgp_clist_hash_key(cl->name);
+ return cl->name_hash;
+}
+
+static bool bgp_clist_hash_cmp_community_list(const void *a1, const void *a2)
+{
+ const struct community_list *cl1 = a1;
+ const struct community_list *cl2 = a2;
+
+ if (cl1->name_hash != cl2->name_hash)
+ return false;
+
+ if (strcmp(cl1->name, cl2->name) == 0)
+ return true;
+
+ return false;
+}
+
+/* Lookup master structure for community-list or
+ extcommunity-list. */
+struct community_list_master *
+community_list_master_lookup(struct community_list_handler *ch, int master)
+{
+ if (ch)
+ switch (master) {
+ case COMMUNITY_LIST_MASTER:
+ return &ch->community_list;
+ case EXTCOMMUNITY_LIST_MASTER:
+ return &ch->extcommunity_list;
+ case LARGE_COMMUNITY_LIST_MASTER:
+ return &ch->lcommunity_list;
+ }
+ return NULL;
+}
+
+/* Allocate a new community list entry. */
+static struct community_entry *community_entry_new(void)
+{
+ return XCALLOC(MTYPE_COMMUNITY_LIST_ENTRY,
+ sizeof(struct community_entry));
+}
+
+/* Free community list entry. */
+static void community_entry_free(struct community_entry *entry)
+{
+ switch (entry->style) {
+ case COMMUNITY_LIST_STANDARD:
+ if (entry->u.com)
+ community_free(&entry->u.com);
+ break;
+ case LARGE_COMMUNITY_LIST_STANDARD:
+ if (entry->u.lcom)
+ lcommunity_free(&entry->u.lcom);
+ break;
+ case EXTCOMMUNITY_LIST_STANDARD:
+ /* In case of standard extcommunity-list, configuration string
+ is made by ecommunity_ecom2str(). */
+ XFREE(MTYPE_ECOMMUNITY_STR, entry->config);
+ if (entry->u.ecom)
+ ecommunity_free(&entry->u.ecom);
+ break;
+ case COMMUNITY_LIST_EXPANDED:
+ case EXTCOMMUNITY_LIST_EXPANDED:
+ case LARGE_COMMUNITY_LIST_EXPANDED:
+ XFREE(MTYPE_COMMUNITY_LIST_CONFIG, entry->config);
+ if (entry->reg)
+ bgp_regex_free(entry->reg);
+ default:
+ break;
+ }
+ XFREE(MTYPE_COMMUNITY_LIST_ENTRY, entry);
+}
+
+/* Allocate a new community-list. */
+static struct community_list *community_list_new(void)
+{
+ return XCALLOC(MTYPE_COMMUNITY_LIST, sizeof(struct community_list));
+}
+
+/* Free community-list. */
+static void community_list_free(struct community_list *list)
+{
+ XFREE(MTYPE_COMMUNITY_LIST_NAME, list->name);
+ XFREE(MTYPE_COMMUNITY_LIST, list);
+}
+
+static struct community_list *
+community_list_insert(struct community_list_handler *ch, const char *name,
+ int master)
+{
+ size_t i;
+ long number;
+ struct community_list *new;
+ struct community_list *point;
+ struct community_list_list *list;
+ struct community_list_master *cm;
+
+ /* Lookup community-list master. */
+ cm = community_list_master_lookup(ch, master);
+ if (!cm)
+ return NULL;
+
+ /* Allocate new community_list and copy given name. */
+ new = community_list_new();
+ new->name = XSTRDUP(MTYPE_COMMUNITY_LIST_NAME, name);
+ new->name_hash = bgp_clist_hash_key_community_list(new);
+
+ /* Save for later */
+ (void)hash_get(cm->hash, new, hash_alloc_intern);
+
+ /* If name is made by all digit character. We treat it as
+ number. */
+ for (number = 0, i = 0; i < strlen(name); i++) {
+ if (isdigit((unsigned char)name[i]))
+ number = (number * 10) + (name[i] - '0');
+ else
+ break;
+ }
+
+ /* In case of name is all digit character */
+ if (i == strlen(name)) {
+ new->sort = COMMUNITY_LIST_NUMBER;
+
+ /* Set access_list to number list. */
+ list = &cm->num;
+
+ for (point = list->head; point; point = point->next)
+ if (atol(point->name) >= number)
+ break;
+ } else {
+ new->sort = COMMUNITY_LIST_STRING;
+
+ /* Set access_list to string list. */
+ list = &cm->str;
+
+ /* Set point to insertion point. */
+ for (point = list->head; point; point = point->next)
+ if (strcmp(point->name, name) >= 0)
+ break;
+ }
+
+ /* Link to upper list. */
+ new->parent = list;
+
+ /* In case of this is the first element of master. */
+ if (list->head == NULL) {
+ list->head = list->tail = new;
+ return new;
+ }
+
+ /* In case of insertion is made at the tail of access_list. */
+ if (point == NULL) {
+ new->prev = list->tail;
+ list->tail->next = new;
+ list->tail = new;
+ return new;
+ }
+
+ /* In case of insertion is made at the head of access_list. */
+ if (point == list->head) {
+ new->next = list->head;
+ list->head->prev = new;
+ list->head = new;
+ return new;
+ }
+
+ /* Insertion is made at middle of the access_list. */
+ new->next = point;
+ new->prev = point->prev;
+
+ if (point->prev)
+ point->prev->next = new;
+ point->prev = new;
+
+ return new;
+}
+
+struct community_list *community_list_lookup(struct community_list_handler *ch,
+ const char *name,
+ uint32_t name_hash,
+ int master)
+{
+ struct community_list lookup;
+ struct community_list_master *cm;
+
+ if (!name)
+ return NULL;
+
+ cm = community_list_master_lookup(ch, master);
+ if (!cm)
+ return NULL;
+
+ lookup.name = (char *)name;
+ lookup.name_hash = name_hash;
+ return hash_get(cm->hash, &lookup, NULL);
+}
+
+static struct community_list *
+community_list_get(struct community_list_handler *ch, const char *name,
+ int master)
+{
+ struct community_list *list;
+
+ list = community_list_lookup(ch, name, 0, master);
+ if (!list)
+ list = community_list_insert(ch, name, master);
+ return list;
+}
+
+static void community_list_delete(struct community_list_master *cm,
+ struct community_list *list)
+{
+ struct community_list_list *clist;
+ struct community_entry *entry, *next;
+
+ for (entry = list->head; entry; entry = next) {
+ next = entry->next;
+ community_entry_free(entry);
+ }
+
+ clist = list->parent;
+
+ if (list->next)
+ list->next->prev = list->prev;
+ else
+ clist->tail = list->prev;
+
+ if (list->prev)
+ list->prev->next = list->next;
+ else
+ clist->head = list->next;
+
+ hash_release(cm->hash, list);
+ community_list_free(list);
+}
+
+static bool community_list_empty_p(struct community_list *list)
+{
+ return list->head == NULL && list->tail == NULL;
+}
+
+/* Delete community-list entry from the list. */
+static void community_list_entry_delete(struct community_list_master *cm,
+ struct community_list *list,
+ struct community_entry *entry)
+{
+ if (entry->next)
+ entry->next->prev = entry->prev;
+ else
+ list->tail = entry->prev;
+
+ if (entry->prev)
+ entry->prev->next = entry->next;
+ else
+ list->head = entry->next;
+
+ community_entry_free(entry);
+
+ if (community_list_empty_p(list))
+ community_list_delete(cm, list);
+}
+
+/*
+ * Replace community-list entry in the list. Note that entry is the new one
+ * and replace is one one being replaced.
+ */
+static void community_list_entry_replace(struct community_list *list,
+ struct community_entry *replace,
+ struct community_entry *entry)
+{
+ if (replace->next) {
+ entry->next = replace->next;
+ replace->next->prev = entry;
+ } else {
+ entry->next = NULL;
+ list->tail = entry;
+ }
+
+ if (replace->prev) {
+ entry->prev = replace->prev;
+ replace->prev->next = entry;
+ } else {
+ entry->prev = NULL;
+ list->head = entry;
+ }
+
+ community_entry_free(replace);
+}
+
+/* Add community-list entry to the list. */
+static void community_list_entry_add(struct community_list *list,
+ struct community_entry *entry,
+ struct community_list_handler *ch,
+ int master)
+{
+ struct community_entry *replace;
+ struct community_entry *point;
+
+ /* Automatic assignment of seq no. */
+ if (entry->seq == COMMUNITY_SEQ_NUMBER_AUTO)
+ entry->seq = bgp_clist_new_seq_get(list);
+
+ if (list->tail && entry->seq > list->tail->seq)
+ point = NULL;
+ else {
+ replace = bgp_clist_seq_check(list, entry->seq);
+ if (replace) {
+ community_list_entry_replace(list, replace, entry);
+ return;
+ }
+
+ /* Check insert point. */
+ for (point = list->head; point; point = point->next)
+ if (point->seq >= entry->seq)
+ break;
+ }
+
+ /* In case of this is the first element of the list. */
+ entry->next = point;
+
+ if (point) {
+ if (point->prev)
+ point->prev->next = entry;
+ else
+ list->head = entry;
+
+ entry->prev = point->prev;
+ point->prev = entry;
+ } else {
+ if (list->tail)
+ list->tail->next = entry;
+ else
+ list->head = entry;
+
+ entry->prev = list->tail;
+ list->tail = entry;
+ }
+}
+
+/* Lookup community-list entry from the list. */
+static struct community_entry *
+community_list_entry_lookup(struct community_list *list, const void *arg,
+ int direct)
+{
+ struct community_entry *entry;
+
+ for (entry = list->head; entry; entry = entry->next) {
+ switch (entry->style) {
+ case COMMUNITY_LIST_STANDARD:
+ if (entry->direct == direct
+ && community_cmp(entry->u.com, arg))
+ return entry;
+ break;
+ case EXTCOMMUNITY_LIST_STANDARD:
+ if (entry->direct == direct
+ && ecommunity_cmp(entry->u.ecom, arg))
+ return entry;
+ break;
+ case LARGE_COMMUNITY_LIST_STANDARD:
+ if (entry->direct == direct
+ && lcommunity_cmp(entry->u.lcom, arg))
+ return entry;
+ break;
+ case COMMUNITY_LIST_EXPANDED:
+ case EXTCOMMUNITY_LIST_EXPANDED:
+ case LARGE_COMMUNITY_LIST_EXPANDED:
+ if (entry->direct == direct
+ && strcmp(entry->config, arg) == 0)
+ return entry;
+ break;
+ default:
+ break;
+ }
+ }
+ return NULL;
+}
+
+static char *community_str_get(struct community *com, int i)
+{
+ uint32_t comval;
+ uint16_t as;
+ uint16_t val;
+ char *str;
+
+ memcpy(&comval, com_nthval(com, i), sizeof(uint32_t));
+ comval = ntohl(comval);
+
+ switch (comval) {
+ case COMMUNITY_INTERNET:
+ str = XSTRDUP(MTYPE_COMMUNITY_STR, "internet");
+ break;
+ case COMMUNITY_GSHUT:
+ str = XSTRDUP(MTYPE_COMMUNITY_STR, "graceful-shutdown");
+ break;
+ case COMMUNITY_ACCEPT_OWN:
+ str = XSTRDUP(MTYPE_COMMUNITY_STR, "accept-own");
+ break;
+ case COMMUNITY_ROUTE_FILTER_TRANSLATED_v4:
+ str = XSTRDUP(MTYPE_COMMUNITY_STR,
+ "route-filter-translated-v4");
+ break;
+ case COMMUNITY_ROUTE_FILTER_v4:
+ str = XSTRDUP(MTYPE_COMMUNITY_STR, "route-filter-v4");
+ break;
+ case COMMUNITY_ROUTE_FILTER_TRANSLATED_v6:
+ str = XSTRDUP(MTYPE_COMMUNITY_STR,
+ "route-filter-translated-v6");
+ break;
+ case COMMUNITY_ROUTE_FILTER_v6:
+ str = XSTRDUP(MTYPE_COMMUNITY_STR, "route-filter-v6");
+ break;
+ case COMMUNITY_LLGR_STALE:
+ str = XSTRDUP(MTYPE_COMMUNITY_STR, "llgr-stale");
+ break;
+ case COMMUNITY_NO_LLGR:
+ str = XSTRDUP(MTYPE_COMMUNITY_STR, "no-llgr");
+ break;
+ case COMMUNITY_ACCEPT_OWN_NEXTHOP:
+ str = XSTRDUP(MTYPE_COMMUNITY_STR, "accept-own-nexthop");
+ break;
+ case COMMUNITY_BLACKHOLE:
+ str = XSTRDUP(MTYPE_COMMUNITY_STR, "blackhole");
+ break;
+ case COMMUNITY_NO_EXPORT:
+ str = XSTRDUP(MTYPE_COMMUNITY_STR, "no-export");
+ break;
+ case COMMUNITY_NO_ADVERTISE:
+ str = XSTRDUP(MTYPE_COMMUNITY_STR, "no-advertise");
+ break;
+ case COMMUNITY_LOCAL_AS:
+ str = XSTRDUP(MTYPE_COMMUNITY_STR, "local-AS");
+ break;
+ case COMMUNITY_NO_PEER:
+ str = XSTRDUP(MTYPE_COMMUNITY_STR, "no-peer");
+ break;
+ default:
+ str = XSTRDUP(MTYPE_COMMUNITY_STR, "65536:65535");
+ as = (comval >> 16) & 0xFFFF;
+ val = comval & 0xFFFF;
+ snprintf(str, strlen(str), "%u:%d", as, val);
+ break;
+ }
+
+ return str;
+}
+
+/* Internal function to perform regular expression match for
+ * a single community. */
+static bool community_regexp_include(regex_t *reg, struct community *com, int i)
+{
+ char *str;
+ int rv;
+
+ /* When there is no communities attribute it is treated as empty string.
+ */
+ if (com == NULL || com->size == 0)
+ str = XSTRDUP(MTYPE_COMMUNITY_STR, "");
+ else
+ str = community_str_get(com, i);
+
+ /* Regular expression match. */
+ rv = regexec(reg, str, 0, NULL, 0);
+
+ XFREE(MTYPE_COMMUNITY_STR, str);
+
+ return rv == 0;
+}
+
+/* Internal function to perform regular expression match for community
+ attribute. */
+static bool community_regexp_match(struct community *com, regex_t *reg)
+{
+ const char *str;
+ char *regstr;
+ int rv;
+
+ /* When there is no communities attribute it is treated as empty
+ string. */
+ if (com == NULL || com->size == 0)
+ str = "";
+ else
+ str = community_str(com, false, true);
+
+ regstr = bgp_alias2community_str(str);
+
+ /* Regular expression match. */
+ rv = regexec(reg, regstr, 0, NULL, 0);
+
+ XFREE(MTYPE_TMP, regstr);
+
+ return rv == 0;
+}
+
+static char *lcommunity_str_get(struct lcommunity *lcom, int i)
+{
+ struct lcommunity_val lcomval;
+ uint32_t globaladmin;
+ uint32_t localdata1;
+ uint32_t localdata2;
+ char *str;
+ const uint8_t *ptr;
+
+ ptr = lcom->val + (i * LCOMMUNITY_SIZE);
+
+ memcpy(&lcomval, ptr, LCOMMUNITY_SIZE);
+
+ /* Allocate memory. 48 bytes taken off bgp_lcommunity.c */
+ ptr = (uint8_t *)lcomval.val;
+ ptr = ptr_get_be32(ptr, &globaladmin);
+ ptr = ptr_get_be32(ptr, &localdata1);
+ ptr = ptr_get_be32(ptr, &localdata2);
+ (void)ptr; /* consume value */
+
+ str = XMALLOC(MTYPE_LCOMMUNITY_STR, 48);
+ snprintf(str, 48, "%u:%u:%u", globaladmin, localdata1, localdata2);
+
+ return str;
+}
+
+/* Internal function to perform regular expression match for
+ * a single community. */
+static bool lcommunity_regexp_include(regex_t *reg, struct lcommunity *lcom,
+ int i)
+{
+ char *str;
+
+ /* When there is no communities attribute it is treated as empty string.
+ */
+ if (lcom == NULL || lcom->size == 0)
+ str = XSTRDUP(MTYPE_LCOMMUNITY_STR, "");
+ else
+ str = lcommunity_str_get(lcom, i);
+
+ /* Regular expression match. */
+ if (regexec(reg, str, 0, NULL, 0) == 0) {
+ XFREE(MTYPE_LCOMMUNITY_STR, str);
+ return true;
+ }
+
+ XFREE(MTYPE_LCOMMUNITY_STR, str);
+ /* No match. */
+ return false;
+}
+
+static bool lcommunity_regexp_match(struct lcommunity *com, regex_t *reg)
+{
+ const char *str;
+ char *regstr;
+ int rv;
+
+ /* When there is no communities attribute it is treated as empty
+ string. */
+ if (com == NULL || com->size == 0)
+ str = "";
+ else
+ str = lcommunity_str(com, false, true);
+
+ regstr = bgp_alias2community_str(str);
+
+ /* Regular expression match. */
+ rv = regexec(reg, regstr, 0, NULL, 0);
+
+ XFREE(MTYPE_TMP, regstr);
+
+ return rv == 0;
+}
+
+
+static bool ecommunity_regexp_match(struct ecommunity *ecom, regex_t *reg)
+{
+ const char *str;
+
+ /* When there is no communities attribute it is treated as empty
+ string. */
+ if (ecom == NULL || ecom->size == 0)
+ str = "";
+ else
+ str = ecommunity_str(ecom);
+
+ /* Regular expression match. */
+ if (regexec(reg, str, 0, NULL, 0) == 0)
+ return true;
+
+ /* No match. */
+ return false;
+}
+
+/* When given community attribute matches to the community-list return
+ 1 else return 0. */
+bool community_list_match(struct community *com, struct community_list *list)
+{
+ struct community_entry *entry;
+
+ for (entry = list->head; entry; entry = entry->next) {
+ if (entry->any)
+ return entry->direct == COMMUNITY_PERMIT;
+
+ if (entry->style == COMMUNITY_LIST_STANDARD) {
+ if (community_include(entry->u.com, COMMUNITY_INTERNET))
+ return entry->direct == COMMUNITY_PERMIT;
+
+ if (community_match(com, entry->u.com))
+ return entry->direct == COMMUNITY_PERMIT;
+ } else if (entry->style == COMMUNITY_LIST_EXPANDED) {
+ if (community_regexp_match(com, entry->reg))
+ return entry->direct == COMMUNITY_PERMIT;
+ }
+ }
+ return false;
+}
+
+bool lcommunity_list_match(struct lcommunity *lcom, struct community_list *list)
+{
+ struct community_entry *entry;
+
+ for (entry = list->head; entry; entry = entry->next) {
+ if (entry->any)
+ return entry->direct == COMMUNITY_PERMIT;
+
+ if (entry->style == LARGE_COMMUNITY_LIST_STANDARD) {
+ if (lcommunity_match(lcom, entry->u.lcom))
+ return entry->direct == COMMUNITY_PERMIT;
+ } else if (entry->style == LARGE_COMMUNITY_LIST_EXPANDED) {
+ if (lcommunity_regexp_match(lcom, entry->reg))
+ return entry->direct == COMMUNITY_PERMIT;
+ }
+ }
+ return false;
+}
+
+
+/* Perform exact matching. In case of expanded large-community-list, do
+ * same thing as lcommunity_list_match().
+ */
+bool lcommunity_list_exact_match(struct lcommunity *lcom,
+ struct community_list *list)
+{
+ struct community_entry *entry;
+
+ for (entry = list->head; entry; entry = entry->next) {
+ if (entry->any)
+ return entry->direct == COMMUNITY_PERMIT;
+
+ if (entry->style == LARGE_COMMUNITY_LIST_STANDARD) {
+ if (lcommunity_cmp(lcom, entry->u.lcom))
+ return entry->direct == COMMUNITY_PERMIT;
+ } else if (entry->style == LARGE_COMMUNITY_LIST_EXPANDED) {
+ if (lcommunity_regexp_match(lcom, entry->reg))
+ return entry->direct == COMMUNITY_PERMIT;
+ }
+ }
+ return false;
+}
+
+bool ecommunity_list_match(struct ecommunity *ecom, struct community_list *list)
+{
+ struct community_entry *entry;
+
+ for (entry = list->head; entry; entry = entry->next) {
+ if (entry->any)
+ return entry->direct == COMMUNITY_PERMIT;
+
+ if (entry->style == EXTCOMMUNITY_LIST_STANDARD) {
+ if (ecommunity_match(ecom, entry->u.ecom))
+ return entry->direct == COMMUNITY_PERMIT;
+ } else if (entry->style == EXTCOMMUNITY_LIST_EXPANDED) {
+ if (ecommunity_regexp_match(ecom, entry->reg))
+ return entry->direct == COMMUNITY_PERMIT;
+ }
+ }
+ return false;
+}
+
+/* Perform exact matching. In case of expanded community-list, do
+ same thing as community_list_match(). */
+bool community_list_exact_match(struct community *com,
+ struct community_list *list)
+{
+ struct community_entry *entry;
+
+ for (entry = list->head; entry; entry = entry->next) {
+ if (entry->any)
+ return entry->direct == COMMUNITY_PERMIT;
+
+ if (entry->style == COMMUNITY_LIST_STANDARD) {
+ if (community_include(entry->u.com, COMMUNITY_INTERNET))
+ return entry->direct == COMMUNITY_PERMIT;
+
+ if (community_cmp(com, entry->u.com))
+ return entry->direct == COMMUNITY_PERMIT;
+ } else if (entry->style == COMMUNITY_LIST_EXPANDED) {
+ if (community_regexp_match(com, entry->reg))
+ return entry->direct == COMMUNITY_PERMIT;
+ }
+ }
+ return false;
+}
+
+/* Delete all permitted communities in the list from com. */
+struct community *community_list_match_delete(struct community *com,
+ struct community_list *list)
+{
+ struct community_entry *entry;
+ uint32_t val;
+ uint32_t com_index_to_delete[com->size];
+ int delete_index = 0;
+ int i;
+
+ /* Loop over each community value and evaluate each against the
+ * community-list. If we need to delete a community value add its index
+ * to com_index_to_delete.
+ */
+ for (i = 0; i < com->size; i++) {
+ val = community_val_get(com, i);
+
+ for (entry = list->head; entry; entry = entry->next) {
+ if (entry->any) {
+ if (entry->direct == COMMUNITY_PERMIT) {
+ com_index_to_delete[delete_index] = i;
+ delete_index++;
+ }
+ break;
+ }
+
+ else if ((entry->style == COMMUNITY_LIST_STANDARD)
+ && (community_include(entry->u.com,
+ COMMUNITY_INTERNET)
+ || community_include(entry->u.com, val))) {
+ if (entry->direct == COMMUNITY_PERMIT) {
+ com_index_to_delete[delete_index] = i;
+ delete_index++;
+ }
+ break;
+ }
+
+ else if ((entry->style == COMMUNITY_LIST_EXPANDED)
+ && community_regexp_include(entry->reg, com,
+ i)) {
+ if (entry->direct == COMMUNITY_PERMIT) {
+ com_index_to_delete[delete_index] = i;
+ delete_index++;
+ }
+ break;
+ }
+ }
+ }
+
+ /* Delete all of the communities we flagged for deletion */
+ for (i = delete_index - 1; i >= 0; i--) {
+ val = community_val_get(com, com_index_to_delete[i]);
+ val = htonl(val);
+ community_del_val(com, &val);
+ }
+
+ return com;
+}
+
+/* To avoid duplicated entry in the community-list, this function
+ compares specified entry to existing entry. */
+static bool community_list_dup_check(struct community_list *list,
+ struct community_entry *new)
+{
+ struct community_entry *entry;
+
+ for (entry = list->head; entry; entry = entry->next) {
+ if (entry->style != new->style)
+ continue;
+
+ if (entry->direct != new->direct)
+ continue;
+
+ if (entry->any != new->any)
+ continue;
+
+ if (entry->any)
+ return true;
+
+ switch (entry->style) {
+ case COMMUNITY_LIST_STANDARD:
+ if (community_cmp(entry->u.com, new->u.com))
+ return true;
+ break;
+ case LARGE_COMMUNITY_LIST_STANDARD:
+ if (lcommunity_cmp(entry->u.lcom, new->u.lcom))
+ return true;
+ break;
+ case EXTCOMMUNITY_LIST_STANDARD:
+ if (ecommunity_cmp(entry->u.ecom, new->u.ecom))
+ return true;
+ break;
+ case COMMUNITY_LIST_EXPANDED:
+ case EXTCOMMUNITY_LIST_EXPANDED:
+ case LARGE_COMMUNITY_LIST_EXPANDED:
+ if (strcmp(entry->config, new->config) == 0)
+ return true;
+ break;
+ default:
+ break;
+ }
+ }
+ return false;
+}
+
+/* Set community-list. */
+int community_list_set(struct community_list_handler *ch, const char *name,
+ const char *str, const char *seq, int direct, int style)
+{
+ struct community_entry *entry = NULL;
+ struct community_list *list;
+ struct community *com = NULL;
+ regex_t *regex = NULL;
+ int64_t seqnum = COMMUNITY_SEQ_NUMBER_AUTO;
+
+ if (seq)
+ seqnum = (int64_t)atol(seq);
+
+ /* Get community list. */
+ list = community_list_get(ch, name, COMMUNITY_LIST_MASTER);
+
+ /* When community-list already has entry, new entry should have same
+ style. If you want to have mixed style community-list, you can
+ comment out this check. */
+ if (!community_list_empty_p(list)) {
+ struct community_entry *first;
+
+ first = list->head;
+
+ if (style != first->style) {
+ return (first->style == COMMUNITY_LIST_STANDARD
+ ? COMMUNITY_LIST_ERR_STANDARD_CONFLICT
+ : COMMUNITY_LIST_ERR_EXPANDED_CONFLICT);
+ }
+ }
+
+ if (str) {
+ if (style == COMMUNITY_LIST_STANDARD)
+ com = community_str2com(str);
+ else
+ regex = bgp_regcomp(str);
+
+ if (!com && !regex)
+ return COMMUNITY_LIST_ERR_MALFORMED_VAL;
+ }
+
+ entry = community_entry_new();
+ entry->direct = direct;
+ entry->style = style;
+ entry->any = (str ? false : true);
+ entry->u.com = com;
+ entry->reg = regex;
+ entry->seq = seqnum;
+ entry->config =
+ (regex ? XSTRDUP(MTYPE_COMMUNITY_LIST_CONFIG, str) : NULL);
+
+ /* Do not put duplicated community entry. */
+ if (community_list_dup_check(list, entry))
+ community_entry_free(entry);
+ else {
+ community_list_entry_add(list, entry, ch,
+ COMMUNITY_LIST_MASTER);
+ route_map_notify_dependencies(name, RMAP_EVENT_CLIST_ADDED);
+ }
+
+ return 0;
+}
+
+/* Unset community-list */
+int community_list_unset(struct community_list_handler *ch, const char *name,
+ const char *str, const char *seq, int direct,
+ int style)
+{
+ struct community_list_master *cm = NULL;
+ struct community_entry *entry = NULL;
+ struct community_list *list;
+ struct community *com = NULL;
+
+ /* Lookup community list. */
+ list = community_list_lookup(ch, name, 0, COMMUNITY_LIST_MASTER);
+ if (list == NULL)
+ return COMMUNITY_LIST_ERR_CANT_FIND_LIST;
+
+ cm = community_list_master_lookup(ch, COMMUNITY_LIST_MASTER);
+ /* Delete all of entry belongs to this community-list. */
+ if (!str) {
+ community_list_delete(cm, list);
+ route_map_notify_dependencies(name, RMAP_EVENT_CLIST_DELETED);
+ return 0;
+ }
+
+ if (style == COMMUNITY_LIST_STANDARD)
+ com = community_str2com(str);
+
+ if (com) {
+ entry = community_list_entry_lookup(list, com, direct);
+ community_free(&com);
+ } else
+ entry = community_list_entry_lookup(list, str, direct);
+
+ if (!entry)
+ return COMMUNITY_LIST_ERR_CANT_FIND_LIST;
+
+ community_list_entry_delete(cm, list, entry);
+ route_map_notify_dependencies(name, RMAP_EVENT_CLIST_DELETED);
+
+ return 0;
+}
+
+/* Delete all permitted large communities in the list from com. */
+struct lcommunity *lcommunity_list_match_delete(struct lcommunity *lcom,
+ struct community_list *list)
+{
+ struct community_entry *entry;
+ uint32_t com_index_to_delete[lcom->size];
+ uint8_t *ptr;
+ int delete_index = 0;
+ int i;
+
+ /* Loop over each lcommunity value and evaluate each against the
+ * community-list. If we need to delete a community value add its index
+ * to com_index_to_delete.
+ */
+ for (i = 0; i < lcom->size; i++) {
+ ptr = lcom->val + (i * LCOMMUNITY_SIZE);
+ for (entry = list->head; entry; entry = entry->next) {
+ if (entry->any) {
+ if (entry->direct == COMMUNITY_PERMIT) {
+ com_index_to_delete[delete_index] = i;
+ delete_index++;
+ }
+ break;
+ }
+
+ else if ((entry->style == LARGE_COMMUNITY_LIST_STANDARD)
+ && lcommunity_include(entry->u.lcom, ptr)) {
+ if (entry->direct == COMMUNITY_PERMIT) {
+ com_index_to_delete[delete_index] = i;
+ delete_index++;
+ }
+ break;
+ }
+
+ else if ((entry->style == LARGE_COMMUNITY_LIST_EXPANDED)
+ && lcommunity_regexp_include(entry->reg, lcom,
+ i)) {
+ if (entry->direct == COMMUNITY_PERMIT) {
+ com_index_to_delete[delete_index] = i;
+ delete_index++;
+ }
+ break;
+ }
+ }
+ }
+
+ /* Delete all of the communities we flagged for deletion */
+ for (i = delete_index - 1; i >= 0; i--) {
+ ptr = lcom->val + (com_index_to_delete[i] * LCOMMUNITY_SIZE);
+ lcommunity_del_val(lcom, ptr);
+ }
+
+ return lcom;
+}
+
+/* Helper to check if every octet do not exceed UINT_MAX */
+bool lcommunity_list_valid(const char *community, int style)
+{
+ int octets;
+ char **splits, **communities;
+ char *endptr;
+ int num, num_communities;
+ regex_t *regres;
+ int invalid = 0;
+
+ frrstr_split(community, " ", &communities, &num_communities);
+
+ for (int j = 0; j < num_communities; j++) {
+ octets = 0;
+ frrstr_split(communities[j], ":", &splits, &num);
+
+ for (int i = 0; i < num; i++) {
+ if (strlen(splits[i]) == 0)
+ /* There is no digit to check */
+ invalid++;
+
+ if (style == LARGE_COMMUNITY_LIST_STANDARD) {
+ if (*splits[i] == '-')
+ /* Must not be negative */
+ invalid++;
+ else if (strtoul(splits[i], &endptr, 10)
+ > UINT_MAX)
+ /* Larger than 4 octets */
+ invalid++;
+ else if (*endptr)
+ /* Not all characters were digits */
+ invalid++;
+ } else {
+ regres = bgp_regcomp(communities[j]);
+ if (!regres)
+ /* malformed regex */
+ invalid++;
+ else
+ bgp_regex_free(regres);
+ }
+
+ octets++;
+ XFREE(MTYPE_TMP, splits[i]);
+ }
+ XFREE(MTYPE_TMP, splits);
+
+ if (octets != 3)
+ invalid++;
+
+ XFREE(MTYPE_TMP, communities[j]);
+ }
+ XFREE(MTYPE_TMP, communities);
+
+ return (invalid > 0) ? false : true;
+}
+
+/* Set lcommunity-list. */
+int lcommunity_list_set(struct community_list_handler *ch, const char *name,
+ const char *str, const char *seq, int direct, int style)
+{
+ struct community_entry *entry = NULL;
+ struct community_list *list;
+ struct lcommunity *lcom = NULL;
+ regex_t *regex = NULL;
+ int64_t seqnum = COMMUNITY_SEQ_NUMBER_AUTO;
+
+ if (seq)
+ seqnum = (int64_t)atol(seq);
+
+ /* Get community list. */
+ list = community_list_get(ch, name, LARGE_COMMUNITY_LIST_MASTER);
+
+ /* When community-list already has entry, new entry should have same
+ style. If you want to have mixed style community-list, you can
+ comment out this check. */
+ if (!community_list_empty_p(list)) {
+ struct community_entry *first;
+
+ first = list->head;
+
+ if (style != first->style) {
+ return (first->style == COMMUNITY_LIST_STANDARD
+ ? COMMUNITY_LIST_ERR_STANDARD_CONFLICT
+ : COMMUNITY_LIST_ERR_EXPANDED_CONFLICT);
+ }
+ }
+
+ if (str) {
+ if (style == LARGE_COMMUNITY_LIST_STANDARD)
+ lcom = lcommunity_str2com(str);
+ else
+ regex = bgp_regcomp(str);
+
+ if (!lcom && !regex)
+ return COMMUNITY_LIST_ERR_MALFORMED_VAL;
+ }
+
+ entry = community_entry_new();
+ entry->direct = direct;
+ entry->style = style;
+ entry->any = (str ? false : true);
+ entry->u.lcom = lcom;
+ entry->reg = regex;
+ entry->seq = seqnum;
+ entry->config =
+ (regex ? XSTRDUP(MTYPE_COMMUNITY_LIST_CONFIG, str) : NULL);
+
+ /* Do not put duplicated community entry. */
+ if (community_list_dup_check(list, entry))
+ community_entry_free(entry);
+ else {
+ community_list_entry_add(list, entry, ch,
+ LARGE_COMMUNITY_LIST_MASTER);
+ route_map_notify_dependencies(name, RMAP_EVENT_LLIST_ADDED);
+ }
+
+ return 0;
+}
+
+/* Unset community-list. When str is NULL, delete all of
+ community-list entry belongs to the specified name. */
+int lcommunity_list_unset(struct community_list_handler *ch, const char *name,
+ const char *str, const char *seq, int direct,
+ int style)
+{
+ struct community_list_master *cm = NULL;
+ struct community_entry *entry = NULL;
+ struct community_list *list;
+ struct lcommunity *lcom = NULL;
+ regex_t *regex = NULL;
+
+ /* Lookup community list. */
+ list = community_list_lookup(ch, name, 0, LARGE_COMMUNITY_LIST_MASTER);
+ if (list == NULL)
+ return COMMUNITY_LIST_ERR_CANT_FIND_LIST;
+
+ cm = community_list_master_lookup(ch, LARGE_COMMUNITY_LIST_MASTER);
+ /* Delete all of entry belongs to this community-list. */
+ if (!str) {
+ community_list_delete(cm, list);
+ route_map_notify_dependencies(name, RMAP_EVENT_LLIST_DELETED);
+ return 0;
+ }
+
+ if (style == LARGE_COMMUNITY_LIST_STANDARD)
+ lcom = lcommunity_str2com(str);
+ else
+ regex = bgp_regcomp(str);
+
+ if (!lcom && !regex)
+ return COMMUNITY_LIST_ERR_MALFORMED_VAL;
+
+ if (lcom)
+ entry = community_list_entry_lookup(list, lcom, direct);
+ else
+ entry = community_list_entry_lookup(list, str, direct);
+
+ if (lcom)
+ lcommunity_free(&lcom);
+ if (regex)
+ bgp_regex_free(regex);
+
+ if (!entry)
+ return COMMUNITY_LIST_ERR_CANT_FIND_LIST;
+
+ community_list_entry_delete(cm, list, entry);
+ route_map_notify_dependencies(name, RMAP_EVENT_LLIST_DELETED);
+
+ return 0;
+}
+
+/* Set extcommunity-list. */
+int extcommunity_list_set(struct community_list_handler *ch, const char *name,
+ const char *str, const char *seq, int direct,
+ int style)
+{
+ struct community_entry *entry = NULL;
+ struct community_list *list;
+ struct ecommunity *ecom = NULL;
+ regex_t *regex = NULL;
+ int64_t seqnum = COMMUNITY_SEQ_NUMBER_AUTO;
+
+ if (seq)
+ seqnum = (int64_t)atol(seq);
+
+ if (str == NULL)
+ return COMMUNITY_LIST_ERR_MALFORMED_VAL;
+
+ /* Get community list. */
+ list = community_list_get(ch, name, EXTCOMMUNITY_LIST_MASTER);
+
+ /* When community-list already has entry, new entry should have same
+ style. If you want to have mixed style community-list, you can
+ comment out this check. */
+ if (!community_list_empty_p(list)) {
+ struct community_entry *first;
+
+ first = list->head;
+
+ if (style != first->style) {
+ return (first->style == EXTCOMMUNITY_LIST_STANDARD
+ ? COMMUNITY_LIST_ERR_STANDARD_CONFLICT
+ : COMMUNITY_LIST_ERR_EXPANDED_CONFLICT);
+ }
+ }
+
+ if (style == EXTCOMMUNITY_LIST_STANDARD)
+ ecom = ecommunity_str2com(str, 0, 1);
+ else
+ regex = bgp_regcomp(str);
+
+ if (!ecom && !regex)
+ return COMMUNITY_LIST_ERR_MALFORMED_VAL;
+
+ if (ecom)
+ ecom->str =
+ ecommunity_ecom2str(ecom, ECOMMUNITY_FORMAT_DISPLAY, 0);
+
+ entry = community_entry_new();
+ entry->direct = direct;
+ entry->style = style;
+ entry->any = false;
+ if (ecom)
+ entry->config = ecommunity_ecom2str(
+ ecom, ECOMMUNITY_FORMAT_COMMUNITY_LIST, 0);
+ else if (regex)
+ entry->config = XSTRDUP(MTYPE_COMMUNITY_LIST_CONFIG, str);
+
+ entry->u.ecom = ecom;
+ entry->reg = regex;
+ entry->seq = seqnum;
+
+ /* Do not put duplicated community entry. */
+ if (community_list_dup_check(list, entry))
+ community_entry_free(entry);
+ else {
+ community_list_entry_add(list, entry, ch,
+ EXTCOMMUNITY_LIST_MASTER);
+ route_map_notify_dependencies(name, RMAP_EVENT_ECLIST_ADDED);
+ }
+
+ return 0;
+}
+
+/* Unset extcommunity-list.
+ *
+ * When str is NULL, delete all extcommunity-list entries belonging to the
+ * specified name.
+ */
+int extcommunity_list_unset(struct community_list_handler *ch, const char *name,
+ const char *str, const char *seq, int direct,
+ int style)
+{
+ struct community_list_master *cm = NULL;
+ struct community_entry *entry = NULL;
+ struct community_list *list;
+ struct ecommunity *ecom = NULL;
+
+ /* Lookup extcommunity list. */
+ list = community_list_lookup(ch, name, 0, EXTCOMMUNITY_LIST_MASTER);
+ if (list == NULL)
+ return COMMUNITY_LIST_ERR_CANT_FIND_LIST;
+
+ cm = community_list_master_lookup(ch, EXTCOMMUNITY_LIST_MASTER);
+ /* Delete all of entry belongs to this extcommunity-list. */
+ if (!str) {
+ community_list_delete(cm, list);
+ route_map_notify_dependencies(name, RMAP_EVENT_ECLIST_DELETED);
+ return 0;
+ }
+
+ if (style == EXTCOMMUNITY_LIST_STANDARD)
+ ecom = ecommunity_str2com(str, 0, 1);
+
+ if (ecom) {
+ entry = community_list_entry_lookup(list, ecom, direct);
+ ecommunity_free(&ecom);
+ } else
+ entry = community_list_entry_lookup(list, str, direct);
+
+ if (!entry)
+ return COMMUNITY_LIST_ERR_CANT_FIND_LIST;
+
+ community_list_entry_delete(cm, list, entry);
+ route_map_notify_dependencies(name, RMAP_EVENT_ECLIST_DELETED);
+
+ return 0;
+}
+
+/* Initializa community-list. Return community-list handler. */
+struct community_list_handler *community_list_init(void)
+{
+ struct community_list_handler *ch;
+ ch = XCALLOC(MTYPE_COMMUNITY_LIST_HANDLER,
+ sizeof(struct community_list_handler));
+
+ ch->community_list.hash =
+ hash_create_size(4, bgp_clist_hash_key_community_list,
+ bgp_clist_hash_cmp_community_list,
+ "Community List Number Quick Lookup");
+
+ ch->extcommunity_list.hash =
+ hash_create_size(4, bgp_clist_hash_key_community_list,
+ bgp_clist_hash_cmp_community_list,
+ "Extended Community List Quick Lookup");
+
+ ch->lcommunity_list.hash =
+ hash_create_size(4, bgp_clist_hash_key_community_list,
+ bgp_clist_hash_cmp_community_list,
+ "Large Community List Quick Lookup");
+
+ return ch;
+}
+
+/* Terminate community-list. */
+void community_list_terminate(struct community_list_handler *ch)
+{
+ struct community_list_master *cm;
+ struct community_list *list;
+
+ cm = &ch->community_list;
+ while ((list = cm->num.head) != NULL)
+ community_list_delete(cm, list);
+ while ((list = cm->str.head) != NULL)
+ community_list_delete(cm, list);
+ hash_free(cm->hash);
+
+ cm = &ch->lcommunity_list;
+ while ((list = cm->num.head) != NULL)
+ community_list_delete(cm, list);
+ while ((list = cm->str.head) != NULL)
+ community_list_delete(cm, list);
+ hash_free(cm->hash);
+
+ cm = &ch->extcommunity_list;
+ while ((list = cm->num.head) != NULL)
+ community_list_delete(cm, list);
+ while ((list = cm->str.head) != NULL)
+ community_list_delete(cm, list);
+ hash_free(cm->hash);
+
+ XFREE(MTYPE_COMMUNITY_LIST_HANDLER, ch);
+}
+
+static int bgp_community_list_vector_walker(struct hash_bucket *bucket,
+ void *data)
+{
+ vector *comps = data;
+ struct community_list *list = bucket->data;
+
+ vector_set(*comps, XSTRDUP(MTYPE_COMPLETION, list->name));
+
+ return 1;
+}
+
+static void bgp_community_list_cmd_completion(vector comps,
+ struct cmd_token *token)
+{
+ struct community_list_master *cm;
+
+ cm = community_list_master_lookup(bgp_clist, COMMUNITY_LIST_MASTER);
+
+ hash_walk(cm->hash, bgp_community_list_vector_walker, &comps);
+}
+
+static void bgp_lcommunity_list_cmd_completion(vector comps,
+ struct cmd_token *token)
+{
+ struct community_list_master *cm;
+
+ cm = community_list_master_lookup(bgp_clist,
+ LARGE_COMMUNITY_LIST_MASTER);
+
+ hash_walk(cm->hash, bgp_community_list_vector_walker, &comps);
+}
+
+static void bgp_extcommunity_list_cmd_completion(vector comps,
+ struct cmd_token *token)
+{
+ struct community_list_master *cm;
+
+ cm = community_list_master_lookup(bgp_clist, EXTCOMMUNITY_LIST_MASTER);
+
+ hash_walk(cm->hash, bgp_community_list_vector_walker, &comps);
+}
+
+static const struct cmd_variable_handler community_list_handlers[] = {
+ {.tokenname = "COMMUNITY_LIST_NAME",
+ .completions = bgp_community_list_cmd_completion},
+ {.completions = NULL}};
+
+static const struct cmd_variable_handler lcommunity_list_handlers[] = {
+ {.tokenname = "LCOMMUNITY_LIST_NAME",
+ .completions = bgp_lcommunity_list_cmd_completion},
+ {.completions = NULL}};
+
+static const struct cmd_variable_handler extcommunity_list_handlers[] = {
+ {.tokenname = "EXTCOMMUNITY_LIST_NAME",
+ .completions = bgp_extcommunity_list_cmd_completion},
+ {.completions = NULL}};
+
+void bgp_community_list_command_completion_setup(void)
+{
+ cmd_variable_handler_register(community_list_handlers);
+ cmd_variable_handler_register(lcommunity_list_handlers);
+ cmd_variable_handler_register(extcommunity_list_handlers);
+}
diff --git a/bgpd/bgp_clist.h b/bgpd/bgp_clist.h
new file mode 100644
index 0000000..fb80787
--- /dev/null
+++ b/bgpd/bgp_clist.h
@@ -0,0 +1,192 @@
+/* BGP Community list.
+ * Copyright (C) 1999 Kunihiro Ishiguro
+ *
+ * This file is part of GNU Zebra.
+ *
+ * GNU Zebra is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License as published by the
+ * Free Software Foundation; either version 2, or (at your option) any
+ * later version.
+ *
+ * GNU Zebra is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program; see the file COPYING; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
+ */
+
+#ifndef _QUAGGA_BGP_CLIST_H
+#define _QUAGGA_BGP_CLIST_H
+
+#include "jhash.h"
+
+/* Master Community-list. */
+#define COMMUNITY_LIST_MASTER 0
+#define EXTCOMMUNITY_LIST_MASTER 1
+#define LARGE_COMMUNITY_LIST_MASTER 2
+
+/* Community-list deny and permit. */
+#define COMMUNITY_DENY 0
+#define COMMUNITY_PERMIT 1
+
+/* Number and string based community-list name. */
+#define COMMUNITY_LIST_STRING 0
+#define COMMUNITY_LIST_NUMBER 1
+
+#define COMMUNITY_SEQ_NUMBER_AUTO -1
+
+/* Community-list entry types. */
+#define COMMUNITY_LIST_STANDARD 0 /* Standard community-list. */
+#define COMMUNITY_LIST_EXPANDED 1 /* Expanded community-list. */
+#define EXTCOMMUNITY_LIST_STANDARD 2 /* Standard extcommunity-list. */
+#define EXTCOMMUNITY_LIST_EXPANDED 3 /* Expanded extcommunity-list. */
+#define LARGE_COMMUNITY_LIST_STANDARD 4 /* Standard Large community-list. */
+#define LARGE_COMMUNITY_LIST_EXPANDED 5 /* Expanded Large community-list. */
+
+/* Community-list. */
+struct community_list {
+ /* Name of the community-list. */
+ char *name;
+
+ /* Stored hash value of name, to further speed up hash operations */
+ uint32_t name_hash;
+
+ /* String or number. */
+ int sort;
+
+ /* Link to upper list. */
+ struct community_list_list *parent;
+
+ /* Linked list for other community-list. */
+ struct community_list *next;
+ struct community_list *prev;
+
+ /* Community-list entry in this community-list. */
+ struct community_entry *head;
+ struct community_entry *tail;
+};
+
+/* Each entry in community-list. */
+struct community_entry {
+ struct community_entry *next;
+ struct community_entry *prev;
+
+ /* Permit or deny. */
+ uint8_t direct;
+
+ /* Standard or expanded. */
+ uint8_t style;
+
+ /* Any match. */
+ bool any;
+
+ /* Sequence number. */
+ int64_t seq;
+
+ /* Community structure. */
+ union {
+ struct community *com;
+ struct ecommunity *ecom;
+ struct lcommunity *lcom;
+ } u;
+
+ /* Configuration string. */
+ char *config;
+
+ /* Expanded community-list regular expression. */
+ regex_t *reg;
+};
+
+/* Linked list of community-list. */
+struct community_list_list {
+ struct community_list *head;
+ struct community_list *tail;
+};
+
+/* Master structure of community-list and extcommunity-list. */
+struct community_list_master {
+ struct community_list_list num;
+ struct community_list_list str;
+ struct hash *hash;
+};
+
+/* Community-list handler. community_list_init() returns this
+ structure as handler. */
+struct community_list_handler {
+ /* Community-list. */
+ struct community_list_master community_list;
+
+ /* Exteded community-list. */
+ struct community_list_master extcommunity_list;
+
+ /* Large community-list. */
+ struct community_list_master lcommunity_list;
+};
+
+/* Error code of community-list. */
+#define COMMUNITY_LIST_ERR_CANT_FIND_LIST -1
+#define COMMUNITY_LIST_ERR_MALFORMED_VAL -2
+#define COMMUNITY_LIST_ERR_STANDARD_CONFLICT -3
+#define COMMUNITY_LIST_ERR_EXPANDED_CONFLICT -4
+
+/* Handler. */
+extern struct community_list_handler *bgp_clist;
+
+/* Prototypes. */
+extern struct community_list_handler *community_list_init(void);
+extern void community_list_terminate(struct community_list_handler *ch);
+
+extern int community_list_set(struct community_list_handler *ch,
+ const char *name, const char *str,
+ const char *seq, int direct, int style);
+extern int community_list_unset(struct community_list_handler *ch,
+ const char *name, const char *str,
+ const char *seq, int direct, int style);
+extern int extcommunity_list_set(struct community_list_handler *ch,
+ const char *name, const char *str,
+ const char *seq, int direct, int style);
+extern int extcommunity_list_unset(struct community_list_handler *ch,
+ const char *name, const char *str,
+ const char *seq, int direct, int style);
+extern int lcommunity_list_set(struct community_list_handler *ch,
+ const char *name, const char *str,
+ const char *seq, int direct, int style);
+extern bool lcommunity_list_valid(const char *community, int style);
+extern int lcommunity_list_unset(struct community_list_handler *ch,
+ const char *name, const char *str,
+ const char *seq, int direct, int style);
+
+extern struct community_list_master *
+community_list_master_lookup(struct community_list_handler *ch, int master);
+
+extern struct community_list *
+community_list_lookup(struct community_list_handler *c, const char *name,
+ uint32_t name_hash, int master);
+
+extern bool community_list_match(struct community *com,
+ struct community_list *list);
+extern bool ecommunity_list_match(struct ecommunity *ecom,
+ struct community_list *list);
+extern bool lcommunity_list_match(struct lcommunity *lcom,
+ struct community_list *list);
+extern bool community_list_exact_match(struct community *com,
+ struct community_list *list);
+extern bool lcommunity_list_exact_match(struct lcommunity *lcom,
+ struct community_list *list);
+extern struct community *
+community_list_match_delete(struct community *com, struct community_list *list);
+extern struct lcommunity *
+lcommunity_list_match_delete(struct lcommunity *lcom,
+ struct community_list *list);
+
+static inline uint32_t bgp_clist_hash_key(char *name)
+{
+ return jhash(name, strlen(name), 0xdeadbeaf);
+}
+
+extern void bgp_community_list_command_completion_setup(void);
+
+#endif /* _QUAGGA_BGP_CLIST_H */
diff --git a/bgpd/bgp_community.c b/bgpd/bgp_community.c
new file mode 100644
index 0000000..9f6f337
--- /dev/null
+++ b/bgpd/bgp_community.c
@@ -0,0 +1,1079 @@
+/* Community attribute related functions.
+ * Copyright (C) 1998, 2001 Kunihiro Ishiguro
+ *
+ * This file is part of GNU Zebra.
+ *
+ * GNU Zebra is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License as published by the
+ * Free Software Foundation; either version 2, or (at your option) any
+ * later version.
+ *
+ * GNU Zebra is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program; see the file COPYING; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
+ */
+
+#include <zebra.h>
+
+#include "command.h"
+#include "hash.h"
+#include "memory.h"
+#include "jhash.h"
+#include "frrstr.h"
+
+#include "bgpd/bgp_memory.h"
+#include "bgpd/bgp_community.h"
+#include "bgpd/bgp_community_alias.h"
+
+/* Hash of community attribute. */
+static struct hash *comhash;
+
+/* Allocate a new communities value. */
+static struct community *community_new(void)
+{
+ return XCALLOC(MTYPE_COMMUNITY, sizeof(struct community));
+}
+
+/* Free communities value. */
+void community_free(struct community **com)
+{
+ if (!(*com))
+ return;
+
+ XFREE(MTYPE_COMMUNITY_VAL, (*com)->val);
+ XFREE(MTYPE_COMMUNITY_STR, (*com)->str);
+
+ if ((*com)->json) {
+ json_object_free((*com)->json);
+ (*com)->json = NULL;
+ }
+
+ XFREE(MTYPE_COMMUNITY, (*com));
+}
+
+/* Add one community value to the community. */
+void community_add_val(struct community *com, uint32_t val)
+{
+ com->size++;
+ com->val = XREALLOC(MTYPE_COMMUNITY_VAL, com->val, com_length(com));
+
+ val = htonl(val);
+ memcpy(com_lastval(com), &val, sizeof(uint32_t));
+}
+
+/* Delete one community. */
+void community_del_val(struct community *com, uint32_t *val)
+{
+ int i = 0;
+ int c = 0;
+
+ if (!com->val)
+ return;
+
+ while (i < com->size) {
+ if (memcmp(com->val + i, val, sizeof(uint32_t)) == 0) {
+ c = com->size - i - 1;
+
+ if (c > 0)
+ memmove(com->val + i, com->val + (i + 1),
+ c * sizeof(*val));
+
+ com->size--;
+
+ if (com->size > 0)
+ com->val = XREALLOC(MTYPE_COMMUNITY_VAL,
+ com->val, com_length(com));
+ else {
+ XFREE(MTYPE_COMMUNITY_VAL, com->val);
+ }
+ return;
+ }
+ i++;
+ }
+}
+
+/* Delete all communities listed in com2 from com1 */
+struct community *community_delete(struct community *com1,
+ struct community *com2)
+{
+ int i = 0;
+
+ while (i < com2->size) {
+ community_del_val(com1, com2->val + i);
+ i++;
+ }
+
+ return com1;
+}
+
+/* Callback function from qsort(). */
+static int community_compare(const void *a1, const void *a2)
+{
+ uint32_t v1;
+ uint32_t v2;
+
+ memcpy(&v1, a1, sizeof(uint32_t));
+ memcpy(&v2, a2, sizeof(uint32_t));
+ v1 = ntohl(v1);
+ v2 = ntohl(v2);
+
+ if (v1 < v2)
+ return -1;
+ if (v1 > v2)
+ return 1;
+ return 0;
+}
+
+bool community_include(struct community *com, uint32_t val)
+{
+ int i;
+
+ val = htonl(val);
+
+ for (i = 0; i < com->size; i++)
+ if (memcmp(&val, com_nthval(com, i), sizeof(uint32_t)) == 0)
+ return true;
+ return false;
+}
+
+uint32_t community_val_get(struct community *com, int i)
+{
+ uint8_t *p;
+ uint32_t val;
+
+ p = (uint8_t *)com->val;
+ p += (i * COMMUNITY_SIZE);
+
+ memcpy(&val, p, sizeof(uint32_t));
+
+ return ntohl(val);
+}
+
+/* Sort and uniq given community. */
+struct community *community_uniq_sort(struct community *com)
+{
+ int i;
+ struct community *new;
+ uint32_t val;
+
+ if (!com)
+ return NULL;
+
+ new = community_new();
+ new->json = NULL;
+
+ for (i = 0; i < com->size; i++) {
+ val = community_val_get(com, i);
+
+ if (!community_include(new, val))
+ community_add_val(new, val);
+ }
+
+ qsort(new->val, new->size, sizeof(uint32_t), community_compare);
+
+ return new;
+}
+
+/* Convert communities attribute to string.
+
+ For Well-known communities value, below keyword is used.
+
+ 0x0 "internet"
+ 0xFFFF0000 "graceful-shutdown"
+ 0xFFFF0001 "accept-own"
+ 0xFFFF0002 "route-filter-translated-v4"
+ 0xFFFF0003 "route-filter-v4"
+ 0xFFFF0004 "route-filter-translated-v6"
+ 0xFFFF0005 "route-filter-v6"
+ 0xFFFF0006 "llgr-stale"
+ 0xFFFF0007 "no-llgr"
+ 0xFFFF0008 "accept-own-nexthop"
+ 0xFFFF029A "blackhole"
+ 0xFFFFFF01 "no-export"
+ 0xFFFFFF02 "no-advertise"
+ 0xFFFFFF03 "local-AS"
+ 0xFFFFFF04 "no-peer"
+
+ For other values, "AS:VAL" format is used. */
+static void set_community_string(struct community *com, bool make_json,
+ bool translate_alias)
+{
+ int i;
+ char *str;
+ int len;
+ int first;
+ uint32_t comval;
+ uint16_t as;
+ uint16_t val;
+ json_object *json_community_list = NULL;
+ json_object *json_string = NULL;
+
+ if (!com)
+ return;
+
+ if (make_json) {
+ com->json = json_object_new_object();
+ json_community_list = json_object_new_array();
+ }
+
+ /* When communities attribute is empty. */
+ if (com->size == 0) {
+ str = XMALLOC(MTYPE_COMMUNITY_STR, 1);
+ str[0] = '\0';
+
+ if (make_json) {
+ json_object_string_add(com->json, "string", "");
+ json_object_object_add(com->json, "list",
+ json_community_list);
+ }
+ com->str = str;
+ return;
+ }
+
+ /* Memory allocation is time consuming work. So we calculate
+ required string length first. */
+ len = 0;
+
+ for (i = 0; i < com->size; i++) {
+ memcpy(&comval, com_nthval(com, i), sizeof(uint32_t));
+ comval = ntohl(comval);
+
+ switch (comval) {
+ case COMMUNITY_INTERNET:
+ len += strlen(" internet");
+ break;
+ case COMMUNITY_GSHUT:
+ len += strlen(" graceful-shutdown");
+ break;
+ case COMMUNITY_ACCEPT_OWN:
+ len += strlen(" accept-own");
+ break;
+ case COMMUNITY_ROUTE_FILTER_TRANSLATED_v4:
+ len += strlen(" route-filter-translated-v4");
+ break;
+ case COMMUNITY_ROUTE_FILTER_v4:
+ len += strlen(" route-filter-v4");
+ break;
+ case COMMUNITY_ROUTE_FILTER_TRANSLATED_v6:
+ len += strlen(" route-filter-translated-v6");
+ break;
+ case COMMUNITY_ROUTE_FILTER_v6:
+ len += strlen(" route-filter-v6");
+ break;
+ case COMMUNITY_LLGR_STALE:
+ len += strlen(" llgr-stale");
+ break;
+ case COMMUNITY_NO_LLGR:
+ len += strlen(" no-llgr");
+ break;
+ case COMMUNITY_ACCEPT_OWN_NEXTHOP:
+ len += strlen(" accept-own-nexthop");
+ break;
+ case COMMUNITY_BLACKHOLE:
+ len += strlen(" blackhole");
+ break;
+ case COMMUNITY_NO_EXPORT:
+ len += strlen(" no-export");
+ break;
+ case COMMUNITY_NO_ADVERTISE:
+ len += strlen(" no-advertise");
+ break;
+ case COMMUNITY_LOCAL_AS:
+ len += strlen(" local-AS");
+ break;
+ case COMMUNITY_NO_PEER:
+ len += strlen(" no-peer");
+ break;
+ default:
+ len = BUFSIZ;
+ break;
+ }
+ }
+
+ /* Allocate memory. */
+ str = XCALLOC(MTYPE_COMMUNITY_STR, len);
+ first = 1;
+
+ /* Fill in string. */
+ for (i = 0; i < com->size; i++) {
+ memcpy(&comval, com_nthval(com, i), sizeof(uint32_t));
+ comval = ntohl(comval);
+
+ if (first)
+ first = 0;
+ else
+ strlcat(str, " ", len);
+
+ switch (comval) {
+ case COMMUNITY_INTERNET:
+ strlcat(str, "internet", len);
+ if (make_json) {
+ json_string =
+ json_object_new_string("internet");
+ json_object_array_add(json_community_list,
+ json_string);
+ }
+ break;
+ case COMMUNITY_GSHUT:
+ strlcat(str, "graceful-shutdown", len);
+ if (make_json) {
+ json_string = json_object_new_string(
+ "gracefulShutdown");
+ json_object_array_add(json_community_list,
+ json_string);
+ }
+ break;
+ case COMMUNITY_ACCEPT_OWN:
+ strlcat(str, "accept-own", len);
+ if (make_json) {
+ json_string = json_object_new_string(
+ "acceptown");
+ json_object_array_add(json_community_list,
+ json_string);
+ }
+ break;
+ case COMMUNITY_ROUTE_FILTER_TRANSLATED_v4:
+ strlcat(str, "route-filter-translated-v4", len);
+ if (make_json) {
+ json_string = json_object_new_string(
+ "routeFilterTranslatedV4");
+ json_object_array_add(json_community_list,
+ json_string);
+ }
+ break;
+ case COMMUNITY_ROUTE_FILTER_v4:
+ strlcat(str, "route-filter-v4", len);
+ if (make_json) {
+ json_string = json_object_new_string(
+ "routeFilterV4");
+ json_object_array_add(json_community_list,
+ json_string);
+ }
+ break;
+ case COMMUNITY_ROUTE_FILTER_TRANSLATED_v6:
+ strlcat(str, "route-filter-translated-v6", len);
+ if (make_json) {
+ json_string = json_object_new_string(
+ "routeFilterTranslatedV6");
+ json_object_array_add(json_community_list,
+ json_string);
+ }
+ break;
+ case COMMUNITY_ROUTE_FILTER_v6:
+ strlcat(str, "route-filter-v6", len);
+ if (make_json) {
+ json_string = json_object_new_string(
+ "routeFilterV6");
+ json_object_array_add(json_community_list,
+ json_string);
+ }
+ break;
+ case COMMUNITY_LLGR_STALE:
+ strlcat(str, "llgr-stale", len);
+ if (make_json) {
+ json_string = json_object_new_string(
+ "llgrStale");
+ json_object_array_add(json_community_list,
+ json_string);
+ }
+ break;
+ case COMMUNITY_NO_LLGR:
+ strlcat(str, "no-llgr", len);
+ if (make_json) {
+ json_string = json_object_new_string(
+ "noLlgr");
+ json_object_array_add(json_community_list,
+ json_string);
+ }
+ break;
+ case COMMUNITY_ACCEPT_OWN_NEXTHOP:
+ strlcat(str, "accept-own-nexthop", len);
+ if (make_json) {
+ json_string = json_object_new_string(
+ "acceptownnexthop");
+ json_object_array_add(json_community_list,
+ json_string);
+ }
+ break;
+ case COMMUNITY_BLACKHOLE:
+ strlcat(str, "blackhole", len);
+ if (make_json) {
+ json_string = json_object_new_string(
+ "blackhole");
+ json_object_array_add(json_community_list,
+ json_string);
+ }
+ break;
+ case COMMUNITY_NO_EXPORT:
+ strlcat(str, "no-export", len);
+ if (make_json) {
+ json_string =
+ json_object_new_string("noExport");
+ json_object_array_add(json_community_list,
+ json_string);
+ }
+ break;
+ case COMMUNITY_NO_ADVERTISE:
+ strlcat(str, "no-advertise", len);
+ if (make_json) {
+ json_string =
+ json_object_new_string("noAdvertise");
+ json_object_array_add(json_community_list,
+ json_string);
+ }
+ break;
+ case COMMUNITY_LOCAL_AS:
+ strlcat(str, "local-AS", len);
+ if (make_json) {
+ json_string = json_object_new_string("localAs");
+ json_object_array_add(json_community_list,
+ json_string);
+ }
+ break;
+ case COMMUNITY_NO_PEER:
+ strlcat(str, "no-peer", len);
+ if (make_json) {
+ json_string = json_object_new_string("noPeer");
+ json_object_array_add(json_community_list,
+ json_string);
+ }
+ break;
+ default:
+ as = (comval >> 16) & 0xFFFF;
+ val = comval & 0xFFFF;
+ char buf[32];
+ snprintf(buf, sizeof(buf), "%u:%d", as, val);
+ const char *com2alias =
+ translate_alias ? bgp_community2alias(buf)
+ : buf;
+
+ strlcat(str, com2alias, len);
+ if (make_json) {
+ json_string = json_object_new_string(com2alias);
+ json_object_array_add(json_community_list,
+ json_string);
+ }
+ break;
+ }
+ }
+
+ if (make_json) {
+ json_object_string_add(com->json, "string", str);
+ json_object_object_add(com->json, "list", json_community_list);
+ }
+ com->str = str;
+}
+
+/* Intern communities attribute. */
+struct community *community_intern(struct community *com)
+{
+ struct community *find;
+
+ /* Assert this community structure is not interned. */
+ assert(com->refcnt == 0);
+
+ /* Lookup community hash. */
+ find = (struct community *)hash_get(comhash, com, hash_alloc_intern);
+
+ /* Arguemnt com is allocated temporary. So when it is not used in
+ hash, it should be freed. */
+ if (find != com)
+ community_free(&com);
+
+ /* Increment refrence counter. */
+ find->refcnt++;
+
+ /* Make string. */
+ if (!find->str)
+ set_community_string(find, false, true);
+
+ return find;
+}
+
+/* Free community attribute. */
+void community_unintern(struct community **com)
+{
+ struct community *ret;
+
+ if (!*com)
+ return;
+
+ if ((*com)->refcnt)
+ (*com)->refcnt--;
+
+ /* Pull off from hash. */
+ if ((*com)->refcnt == 0) {
+ /* Community value com must exist in hash. */
+ ret = (struct community *)hash_release(comhash, *com);
+ assert(ret != NULL);
+
+ community_free(com);
+ }
+}
+
+/* Create new community attribute. */
+struct community *community_parse(uint32_t *pnt, unsigned short length)
+{
+ struct community tmp;
+ struct community *new;
+
+ /* If length is malformed return NULL. */
+ if (length % COMMUNITY_SIZE)
+ return NULL;
+
+ /* Make temporary community for hash look up. */
+ tmp.size = length / COMMUNITY_SIZE;
+ tmp.val = pnt;
+
+ new = community_uniq_sort(&tmp);
+
+ return community_intern(new);
+}
+
+struct community *community_dup(struct community *com)
+{
+ struct community *new;
+
+ new = XCALLOC(MTYPE_COMMUNITY, sizeof(struct community));
+ new->size = com->size;
+ if (new->size) {
+ new->val = XMALLOC(MTYPE_COMMUNITY_VAL,
+ com->size * COMMUNITY_SIZE);
+ memcpy(new->val, com->val, com->size * COMMUNITY_SIZE);
+ } else
+ new->val = NULL;
+ return new;
+}
+
+/* Return string representation of communities attribute. */
+char *community_str(struct community *com, bool make_json, bool translate_alias)
+{
+ if (!com)
+ return NULL;
+
+ if (make_json && !com->json && com->str)
+ XFREE(MTYPE_COMMUNITY_STR, com->str);
+
+ if (!com->str)
+ set_community_string(com, make_json, translate_alias);
+ return com->str;
+}
+
+/* Make hash value of community attribute. This function is used by
+ hash package.*/
+unsigned int community_hash_make(const struct community *com)
+{
+ uint32_t *pnt = com->val;
+
+ return jhash2(pnt, com->size, 0x43ea96c1);
+}
+
+bool community_match(const struct community *com1, const struct community *com2)
+{
+ int i = 0;
+ int j = 0;
+
+ if (com1 == NULL && com2 == NULL)
+ return true;
+
+ if (com1 == NULL || com2 == NULL)
+ return false;
+
+ if (com1->size < com2->size)
+ return false;
+
+ /* Every community on com2 needs to be on com1 for this to match */
+ while (i < com1->size && j < com2->size) {
+ if (memcmp(com1->val + i, com2->val + j, sizeof(uint32_t)) == 0)
+ j++;
+ i++;
+ }
+
+ if (j == com2->size)
+ return true;
+ else
+ return false;
+}
+
+bool community_cmp(const struct community *com1, const struct community *com2)
+{
+ if (com1 == NULL && com2 == NULL)
+ return true;
+ if (com1 == NULL || com2 == NULL)
+ return false;
+
+ if (com1->size == com2->size)
+ if (memcmp(com1->val, com2->val, com1->size * COMMUNITY_SIZE)
+ == 0)
+ return true;
+ return false;
+}
+
+/* Add com2 to the end of com1. */
+struct community *community_merge(struct community *com1,
+ struct community *com2)
+{
+ com1->val = XREALLOC(MTYPE_COMMUNITY_VAL, com1->val,
+ (com1->size + com2->size) * COMMUNITY_SIZE);
+
+ memcpy(com1->val + com1->size, com2->val, com2->size * COMMUNITY_SIZE);
+ com1->size += com2->size;
+
+ return com1;
+}
+
+/* Community token enum. */
+enum community_token {
+ community_token_val,
+ community_token_gshut,
+ community_token_accept_own,
+ community_token_route_filter_translated_v4,
+ community_token_route_filter_v4,
+ community_token_route_filter_translated_v6,
+ community_token_route_filter_v6,
+ community_token_llgr_stale,
+ community_token_no_llgr,
+ community_token_accept_own_nexthop,
+ community_token_blackhole,
+ community_token_no_export,
+ community_token_no_advertise,
+ community_token_local_as,
+ community_token_no_peer,
+ community_token_unknown
+};
+
+/* Helper to check if a given community is valid */
+static bool community_valid(const char *community)
+{
+ int octets = 0;
+ char **splits;
+ int num;
+ int invalid = 0;
+
+ frrstr_split(community, ":", &splits, &num);
+
+ for (int i = 0; i < num; i++) {
+ if (strtoul(splits[i], NULL, 10) > UINT16_MAX)
+ invalid++;
+
+ if (strlen(splits[i]) == 0)
+ invalid++;
+
+ octets++;
+ XFREE(MTYPE_TMP, splits[i]);
+ }
+ XFREE(MTYPE_TMP, splits);
+
+ return (octets < 2 || invalid) ? false : true;
+}
+
+/* Get next community token from string. */
+static const char *
+community_gettoken(const char *buf, enum community_token *token, uint32_t *val)
+{
+ const char *p = buf;
+
+ /* Skip white space. */
+ while (isspace((unsigned char)*p))
+ p++;
+
+ /* Check the end of the line. */
+ if (*p == '\0')
+ return NULL;
+
+ /* Well known community string check. */
+ if (isalpha((unsigned char)*p)) {
+ if (strncmp(p, "internet", strlen("internet")) == 0) {
+ *val = COMMUNITY_INTERNET;
+ *token = community_token_no_export;
+ p += strlen("internet");
+ return p;
+ }
+ if (strncmp(p, "graceful-shutdown", strlen("graceful-shutdown"))
+ == 0) {
+ *val = COMMUNITY_GSHUT;
+ *token = community_token_gshut;
+ p += strlen("graceful-shutdown");
+ return p;
+ }
+ if (strncmp(p, "accept-own-nexthop",
+ strlen("accept-own-nexthop"))
+ == 0) {
+ *val = COMMUNITY_ACCEPT_OWN_NEXTHOP;
+ *token = community_token_accept_own_nexthop;
+ p += strlen("accept-own-nexthop");
+ return p;
+ }
+ if (strncmp(p, "accept-own", strlen("accept-own"))
+ == 0) {
+ *val = COMMUNITY_ACCEPT_OWN;
+ *token = community_token_accept_own;
+ p += strlen("accept-own");
+ return p;
+ }
+ if (strncmp(p, "route-filter-translated-v4",
+ strlen("route-filter-translated-v4"))
+ == 0) {
+ *val = COMMUNITY_ROUTE_FILTER_TRANSLATED_v4;
+ *token = community_token_route_filter_translated_v4;
+ p += strlen("route-filter-translated-v4");
+ return p;
+ }
+ if (strncmp(p, "route-filter-v4", strlen("route-filter-v4"))
+ == 0) {
+ *val = COMMUNITY_ROUTE_FILTER_v4;
+ *token = community_token_route_filter_v4;
+ p += strlen("route-filter-v4");
+ return p;
+ }
+ if (strncmp(p, "route-filter-translated-v6",
+ strlen("route-filter-translated-v6"))
+ == 0) {
+ *val = COMMUNITY_ROUTE_FILTER_TRANSLATED_v6;
+ *token = community_token_route_filter_translated_v6;
+ p += strlen("route-filter-translated-v6");
+ return p;
+ }
+ if (strncmp(p, "route-filter-v6", strlen("route-filter-v6"))
+ == 0) {
+ *val = COMMUNITY_ROUTE_FILTER_v6;
+ *token = community_token_route_filter_v6;
+ p += strlen("route-filter-v6");
+ return p;
+ }
+ if (strncmp(p, "llgr-stale", strlen("llgr-stale"))
+ == 0) {
+ *val = COMMUNITY_LLGR_STALE;
+ *token = community_token_llgr_stale;
+ p += strlen("llgr-stale");
+ return p;
+ }
+ if (strncmp(p, "no-llgr", strlen("no-llgr"))
+ == 0) {
+ *val = COMMUNITY_NO_LLGR;
+ *token = community_token_no_llgr;
+ p += strlen("no-llgr");
+ return p;
+ }
+ if (strncmp(p, "blackhole", strlen("blackhole"))
+ == 0) {
+ *val = COMMUNITY_BLACKHOLE;
+ *token = community_token_blackhole;
+ p += strlen("blackhole");
+ return p;
+ }
+ if (strncmp(p, "no-export", strlen("no-export")) == 0) {
+ *val = COMMUNITY_NO_EXPORT;
+ *token = community_token_no_export;
+ p += strlen("no-export");
+ return p;
+ }
+ if (strncmp(p, "no-advertise", strlen("no-advertise")) == 0) {
+ *val = COMMUNITY_NO_ADVERTISE;
+ *token = community_token_no_advertise;
+ p += strlen("no-advertise");
+ return p;
+ }
+ if (strncmp(p, "local-AS", strlen("local-AS")) == 0) {
+ *val = COMMUNITY_LOCAL_AS;
+ *token = community_token_local_as;
+ p += strlen("local-AS");
+ return p;
+ }
+ if (strncmp(p, "no-peer", strlen("no-peer")) == 0) {
+ *val = COMMUNITY_NO_PEER;
+ *token = community_token_no_peer;
+ p += strlen("no-peer");
+ return p;
+ }
+
+ /* Unknown string. */
+ *token = community_token_unknown;
+ return NULL;
+ }
+
+ /* Community value. */
+ if (isdigit((unsigned char)*p)) {
+ int separator = 0;
+ int digit = 0;
+ uint32_t community_low = 0;
+ uint32_t community_high = 0;
+
+ if (!community_valid(p)) {
+ *token = community_token_unknown;
+ return NULL;
+ }
+
+ while (isdigit((unsigned char)*p) || *p == ':') {
+ if (*p == ':') {
+ if (separator) {
+ *token = community_token_unknown;
+ return NULL;
+ } else {
+ separator = 1;
+ digit = 0;
+
+ if (community_low > UINT16_MAX) {
+ *token =
+ community_token_unknown;
+ return NULL;
+ }
+
+ community_high = community_low << 16;
+ community_low = 0;
+ }
+ } else {
+ digit = 1;
+ community_low *= 10;
+ community_low += (*p - '0');
+ }
+ p++;
+ }
+ if (!digit) {
+ *token = community_token_unknown;
+ return NULL;
+ }
+
+ *val = community_high + community_low;
+ *token = community_token_val;
+ return p;
+ }
+ *token = community_token_unknown;
+ return NULL;
+}
+
+/* convert string to community structure */
+struct community *community_str2com(const char *str)
+{
+ struct community *com = NULL;
+ struct community *com_sort = NULL;
+ uint32_t val = 0;
+ enum community_token token = community_token_unknown;
+
+ do {
+ str = community_gettoken(str, &token, &val);
+
+ switch (token) {
+ case community_token_val:
+ case community_token_gshut:
+ case community_token_accept_own:
+ case community_token_route_filter_translated_v4:
+ case community_token_route_filter_v4:
+ case community_token_route_filter_translated_v6:
+ case community_token_route_filter_v6:
+ case community_token_llgr_stale:
+ case community_token_no_llgr:
+ case community_token_accept_own_nexthop:
+ case community_token_blackhole:
+ case community_token_no_export:
+ case community_token_no_advertise:
+ case community_token_local_as:
+ case community_token_no_peer:
+ if (com == NULL) {
+ com = community_new();
+ com->json = NULL;
+ }
+ community_add_val(com, val);
+ break;
+ case community_token_unknown:
+ if (com)
+ community_free(&com);
+ return NULL;
+ }
+ } while (str);
+
+ com_sort = community_uniq_sort(com);
+ community_free(&com);
+
+ return com_sort;
+}
+
+/* Return communities hash entry count. */
+unsigned long community_count(void)
+{
+ return comhash->count;
+}
+
+/* Return communities hash. */
+struct hash *community_hash(void)
+{
+ return comhash;
+}
+
+/* Initialize comminity related hash. */
+void community_init(void)
+{
+ comhash =
+ hash_create((unsigned int (*)(const void *))community_hash_make,
+ (bool (*)(const void *, const void *))community_cmp,
+ "BGP Community Hash");
+}
+
+static void community_hash_free(void *data)
+{
+ struct community *com = data;
+
+ community_free(&com);
+}
+
+void community_finish(void)
+{
+ hash_clean(comhash, community_hash_free);
+ hash_free(comhash);
+ comhash = NULL;
+}
+
+static struct community *bgp_aggr_community_lookup(
+ struct bgp_aggregate *aggregate,
+ struct community *community)
+{
+ return hash_lookup(aggregate->community_hash, community);
+}
+
+static void *bgp_aggr_community_hash_alloc(void *p)
+{
+ struct community *ref = (struct community *)p;
+ struct community *community = NULL;
+
+ community = community_dup(ref);
+ return community;
+}
+
+static void bgp_aggr_community_prepare(struct hash_bucket *hb, void *arg)
+{
+ struct community *hb_community = hb->data;
+ struct community **aggr_community = arg;
+
+ if (*aggr_community)
+ *aggr_community = community_merge(*aggr_community,
+ hb_community);
+ else
+ *aggr_community = community_dup(hb_community);
+}
+
+void bgp_aggr_community_remove(void *arg)
+{
+ struct community *community = arg;
+
+ community_free(&community);
+}
+
+void bgp_compute_aggregate_community(struct bgp_aggregate *aggregate,
+ struct community *community)
+{
+ bgp_compute_aggregate_community_hash(aggregate, community);
+ bgp_compute_aggregate_community_val(aggregate);
+}
+
+
+void bgp_compute_aggregate_community_hash(struct bgp_aggregate *aggregate,
+ struct community *community)
+{
+ struct community *aggr_community = NULL;
+
+ if ((aggregate == NULL) || (community == NULL))
+ return;
+
+ /* Create hash if not already created.
+ */
+ if (aggregate->community_hash == NULL)
+ aggregate->community_hash = hash_create(
+ (unsigned int (*)(const void *))community_hash_make,
+ (bool (*)(const void *, const void *))community_cmp,
+ "BGP Aggregator community hash");
+
+ aggr_community = bgp_aggr_community_lookup(aggregate, community);
+ if (aggr_community == NULL) {
+ /* Insert community into hash.
+ */
+ aggr_community = hash_get(aggregate->community_hash, community,
+ bgp_aggr_community_hash_alloc);
+ }
+
+ /* Increment reference counter.
+ */
+ aggr_community->refcnt++;
+}
+
+void bgp_compute_aggregate_community_val(struct bgp_aggregate *aggregate)
+{
+ struct community *commerge = NULL;
+
+ if (aggregate == NULL)
+ return;
+
+ /* Re-compute aggregate's community.
+ */
+ if (aggregate->community)
+ community_free(&aggregate->community);
+ if (aggregate->community_hash &&
+ aggregate->community_hash->count) {
+ hash_iterate(aggregate->community_hash,
+ bgp_aggr_community_prepare,
+ &aggregate->community);
+ commerge = aggregate->community;
+ aggregate->community = community_uniq_sort(commerge);
+ if (commerge)
+ community_free(&commerge);
+ }
+}
+
+
+
+void bgp_remove_community_from_aggregate(struct bgp_aggregate *aggregate,
+ struct community *community)
+{
+ struct community *aggr_community = NULL;
+ struct community *ret_comm = NULL;
+
+ if ((!aggregate)
+ || (!aggregate->community_hash)
+ || (!community))
+ return;
+
+ /* Look-up the community in the hash.
+ */
+ aggr_community = bgp_aggr_community_lookup(aggregate, community);
+ if (aggr_community) {
+ aggr_community->refcnt--;
+
+ if (aggr_community->refcnt == 0) {
+ ret_comm = hash_release(aggregate->community_hash,
+ aggr_community);
+ community_free(&ret_comm);
+
+ bgp_compute_aggregate_community_val(aggregate);
+ }
+ }
+}
+
+void bgp_remove_comm_from_aggregate_hash(struct bgp_aggregate *aggregate,
+ struct community *community)
+{
+
+ struct community *aggr_community = NULL;
+ struct community *ret_comm = NULL;
+
+ if ((!aggregate)
+ || (!aggregate->community_hash)
+ || (!community))
+ return;
+
+ /* Look-up the community in the hash.
+ */
+ aggr_community = bgp_aggr_community_lookup(aggregate, community);
+ if (aggr_community) {
+ aggr_community->refcnt--;
+
+ if (aggr_community->refcnt == 0) {
+ ret_comm = hash_release(aggregate->community_hash,
+ aggr_community);
+ community_free(&ret_comm);
+ }
+ }
+}
diff --git a/bgpd/bgp_community.h b/bgpd/bgp_community.h
new file mode 100644
index 0000000..616ddb4
--- /dev/null
+++ b/bgpd/bgp_community.h
@@ -0,0 +1,112 @@
+/* Community attribute related functions.
+ * Copyright (C) 1998 Kunihiro Ishiguro
+ *
+ * This file is part of GNU Zebra.
+ *
+ * GNU Zebra is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License as published by the
+ * Free Software Foundation; either version 2, or (at your option) any
+ * later version.
+ *
+ * GNU Zebra is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program; see the file COPYING; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
+ */
+
+#ifndef _QUAGGA_BGP_COMMUNITY_H
+#define _QUAGGA_BGP_COMMUNITY_H
+
+#include "lib/json.h"
+#include "bgpd/bgp_route.h"
+
+/* Communities attribute. */
+struct community {
+ /* Reference count of communities value. */
+ unsigned long refcnt;
+
+ /* Communities value size. */
+ int size;
+
+ /* Communities value. */
+ uint32_t *val;
+
+ /* Communities as a json object */
+ json_object *json;
+
+ /* String of community attribute. This sring is used by vty output
+ and expanded community-list for regular expression match. */
+ char *str;
+};
+
+/* Well-known communities value. */
+#define COMMUNITY_INTERNET 0x0
+#define COMMUNITY_GSHUT 0xFFFF0000
+#define COMMUNITY_ACCEPT_OWN 0xFFFF0001
+#define COMMUNITY_ROUTE_FILTER_TRANSLATED_v4 0xFFFF0002
+#define COMMUNITY_ROUTE_FILTER_v4 0xFFFF0003
+#define COMMUNITY_ROUTE_FILTER_TRANSLATED_v6 0xFFFF0004
+#define COMMUNITY_ROUTE_FILTER_v6 0xFFFF0005
+#define COMMUNITY_LLGR_STALE 0xFFFF0006
+#define COMMUNITY_NO_LLGR 0xFFFF0007
+#define COMMUNITY_ACCEPT_OWN_NEXTHOP 0xFFFF0008
+#define COMMUNITY_BLACKHOLE 0xFFFF029A
+#define COMMUNITY_NO_EXPORT 0xFFFFFF01
+#define COMMUNITY_NO_ADVERTISE 0xFFFFFF02
+#define COMMUNITY_NO_EXPORT_SUBCONFED 0xFFFFFF03
+#define COMMUNITY_LOCAL_AS 0xFFFFFF03
+#define COMMUNITY_NO_PEER 0xFFFFFF04
+
+#define COMMUNITY_SIZE 4
+
+/* Macros of community attribute. */
+#define com_length(X) ((X)->size * COMMUNITY_SIZE)
+#define com_lastval(X) ((X)->val + (X)->size - 1)
+#define com_nthval(X,n) ((X)->val + (n))
+
+/* Prototypes of communities attribute functions. */
+extern void community_init(void);
+extern void community_finish(void);
+extern void community_free(struct community **comm);
+extern struct community *community_uniq_sort(struct community *com);
+extern struct community *community_parse(uint32_t *pnt, unsigned short length);
+extern struct community *community_intern(struct community *com);
+extern void community_unintern(struct community **com);
+extern char *community_str(struct community *com, bool make_json,
+ bool translate_alias);
+extern unsigned int community_hash_make(const struct community *com);
+extern struct community *community_str2com(const char *str);
+extern bool community_match(const struct community *com1,
+ const struct community *com2);
+extern bool community_cmp(const struct community *c1,
+ const struct community *c2);
+extern struct community *community_merge(struct community *com1,
+ struct community *com2);
+extern struct community *community_delete(struct community *com1,
+ struct community *com2);
+extern struct community *community_dup(struct community *com);
+extern bool community_include(struct community *com, uint32_t val);
+extern void community_add_val(struct community *com, uint32_t val);
+extern void community_del_val(struct community *com, uint32_t *val);
+extern unsigned long community_count(void);
+extern struct hash *community_hash(void);
+extern uint32_t community_val_get(struct community *com, int i);
+extern void bgp_compute_aggregate_community(struct bgp_aggregate *aggregate,
+ struct community *community);
+
+extern void bgp_compute_aggregate_community_val(
+ struct bgp_aggregate *aggregate);
+extern void bgp_compute_aggregate_community_hash(
+ struct bgp_aggregate *aggregate,
+ struct community *community);
+extern void bgp_remove_community_from_aggregate(struct bgp_aggregate *aggregate,
+ struct community *community);
+extern void bgp_remove_comm_from_aggregate_hash(struct bgp_aggregate *aggregate,
+ struct community *community);
+extern void bgp_aggr_community_remove(void *arg);
+
+#endif /* _QUAGGA_BGP_COMMUNITY_H */
diff --git a/bgpd/bgp_community_alias.c b/bgpd/bgp_community_alias.c
new file mode 100644
index 0000000..3750a24
--- /dev/null
+++ b/bgpd/bgp_community_alias.c
@@ -0,0 +1,232 @@
+/* BGP community, large-community aliasing.
+ *
+ * Copyright (C) 2021 Donatas Abraitis <donatas.abraitis@gmail.com>
+ *
+ * This file is part of FRRouting (FRR).
+ *
+ * FRR is free software; you can redistribute it and/or modify it under the
+ * terms of the GNU General Public License as published by the Free Software
+ * Foundation; either version 2, or (at your option) any later version.
+ *
+ * FRR is distributed in the hope that it will be useful, but WITHOUT ANY
+ * WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
+ * FOR A PARTICULAR PURPOSE. See the GNU General Public License for more
+ * details.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program; see the file COPYING; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
+ */
+
+#include "zebra.h"
+
+#include "memory.h"
+#include "lib/jhash.h"
+#include "frrstr.h"
+
+#include "bgpd/bgpd.h"
+#include "bgpd/bgp_community_alias.h"
+
+static struct hash *bgp_ca_alias_hash;
+static struct hash *bgp_ca_community_hash;
+
+static unsigned int bgp_ca_community_hash_key(const void *p)
+{
+ const struct community_alias *ca = p;
+
+ return jhash(ca->community, sizeof(ca->community), 0);
+}
+
+static bool bgp_ca_community_hash_cmp(const void *p1, const void *p2)
+{
+ const struct community_alias *ca1 = p1;
+ const struct community_alias *ca2 = p2;
+
+ return (strcmp(ca1->community, ca2->community) == 0);
+}
+
+static unsigned int bgp_ca_alias_hash_key(const void *p)
+{
+ const struct community_alias *ca = p;
+
+ return jhash(ca->alias, sizeof(ca->alias), 0);
+}
+
+static bool bgp_ca_alias_hash_cmp(const void *p1, const void *p2)
+{
+ const struct community_alias *ca1 = p1;
+ const struct community_alias *ca2 = p2;
+
+ return (strcmp(ca1->alias, ca2->alias) == 0);
+}
+
+static void *bgp_community_alias_alloc(void *p)
+{
+ const struct community_alias *ca = p;
+ struct communtiy_alias *new;
+
+ new = XCALLOC(MTYPE_COMMUNITY_ALIAS, sizeof(struct community_alias));
+ memcpy(new, ca, sizeof(struct community_alias));
+
+ return new;
+}
+
+void bgp_community_alias_init(void)
+{
+ bgp_ca_community_hash = hash_create(bgp_ca_community_hash_key,
+ bgp_ca_community_hash_cmp,
+ "BGP community alias (community)");
+ bgp_ca_alias_hash =
+ hash_create(bgp_ca_alias_hash_key, bgp_ca_alias_hash_cmp,
+ "BGP community alias (alias)");
+}
+
+static void bgp_ca_free(void *ca)
+{
+ XFREE(MTYPE_COMMUNITY_ALIAS, ca);
+}
+
+void bgp_community_alias_finish(void)
+{
+ hash_clean(bgp_ca_community_hash, bgp_ca_free);
+ hash_free(bgp_ca_community_hash);
+ hash_clean(bgp_ca_alias_hash, bgp_ca_free);
+ hash_free(bgp_ca_alias_hash);
+}
+
+static void bgp_community_alias_show_iterator(struct hash_bucket *hb,
+ struct vty *vty)
+{
+ struct community_alias *ca = hb->data;
+
+ vty_out(vty, "bgp community alias %s %s\n", ca->community, ca->alias);
+}
+
+int bgp_community_alias_write(struct vty *vty)
+{
+ hash_iterate(bgp_ca_community_hash,
+ (void (*)(struct hash_bucket *,
+ void *))bgp_community_alias_show_iterator,
+ vty);
+ return 1;
+}
+
+void bgp_ca_community_insert(struct community_alias *ca)
+{
+ (void)hash_get(bgp_ca_community_hash, ca, bgp_community_alias_alloc);
+}
+
+void bgp_ca_alias_insert(struct community_alias *ca)
+{
+ (void)hash_get(bgp_ca_alias_hash, ca, bgp_community_alias_alloc);
+}
+
+void bgp_ca_community_delete(struct community_alias *ca)
+{
+ struct community_alias *data = hash_release(bgp_ca_community_hash, ca);
+
+ XFREE(MTYPE_COMMUNITY_ALIAS, data);
+}
+
+void bgp_ca_alias_delete(struct community_alias *ca)
+{
+ struct community_alias *data = hash_release(bgp_ca_alias_hash, ca);
+
+ XFREE(MTYPE_COMMUNITY_ALIAS, data);
+}
+
+struct community_alias *bgp_ca_community_lookup(struct community_alias *ca)
+{
+ return hash_lookup(bgp_ca_community_hash, ca);
+}
+
+struct community_alias *bgp_ca_alias_lookup(struct community_alias *ca)
+{
+ return hash_lookup(bgp_ca_alias_hash, ca);
+}
+
+const char *bgp_community2alias(char *community)
+{
+ struct community_alias ca;
+ struct community_alias *find;
+
+ memset(&ca, 0, sizeof(ca));
+ strlcpy(ca.community, community, sizeof(ca.community));
+
+ find = bgp_ca_community_lookup(&ca);
+ if (find)
+ return find->alias;
+
+ return community;
+}
+
+const char *bgp_alias2community(char *alias)
+{
+ struct community_alias ca;
+ struct community_alias *find;
+
+ memset(&ca, 0, sizeof(ca));
+ strlcpy(ca.alias, alias, sizeof(ca.alias));
+
+ find = bgp_ca_alias_lookup(&ca);
+ if (find)
+ return find->community;
+
+ return alias;
+}
+
+/* Communities structs have `->str` which is used
+ * for vty outputs and extended BGP community lists
+ * with regexp.
+ * This is a helper to convert already aliased version
+ * of communities into numerical-only format.
+ */
+char *bgp_alias2community_str(const char *str)
+{
+ char **aliases;
+ char *comstr;
+ int num, i;
+
+ frrstr_split(str, " ", &aliases, &num);
+ const char *communities[num];
+
+ for (i = 0; i < num; i++)
+ communities[i] = bgp_alias2community(aliases[i]);
+
+ comstr = frrstr_join(communities, num, " ");
+
+ for (i = 0; i < num; i++)
+ XFREE(MTYPE_TMP, aliases[i]);
+ XFREE(MTYPE_TMP, aliases);
+
+ return comstr;
+}
+
+static int bgp_community_alias_vector_walker(struct hash_bucket *bucket,
+ void *data)
+{
+ vector *comps = data;
+ struct community_alias *alias = bucket->data;
+
+ vector_set(*comps, XSTRDUP(MTYPE_COMPLETION, alias->alias));
+
+ return 1;
+}
+
+static void bgp_community_alias_cmd_completion(vector comps,
+ struct cmd_token *token)
+{
+ hash_walk(bgp_ca_alias_hash, bgp_community_alias_vector_walker, &comps);
+}
+
+static const struct cmd_variable_handler community_alias_handlers[] = {
+ {.varname = "alias_name",
+ .completions = bgp_community_alias_cmd_completion},
+ {.tokenname = "ALIAS_NAME",
+ .completions = bgp_community_alias_cmd_completion},
+ {.completions = NULL}};
+
+void bgp_community_alias_command_completion_setup(void)
+{
+ cmd_variable_handler_register(community_alias_handlers);
+}
diff --git a/bgpd/bgp_community_alias.h b/bgpd/bgp_community_alias.h
new file mode 100644
index 0000000..57cde0a
--- /dev/null
+++ b/bgpd/bgp_community_alias.h
@@ -0,0 +1,49 @@
+/* BGP community, large-community aliasing.
+ *
+ * Copyright (C) 2021 Donatas Abraitis <donatas.abraitis@gmail.com>
+ *
+ * This file is part of FRRouting (FRR).
+ *
+ * FRR is free software; you can redistribute it and/or modify it under the
+ * terms of the GNU General Public License as published by the Free Software
+ * Foundation; either version 2, or (at your option) any later version.
+ *
+ * FRR is distributed in the hope that it will be useful, but WITHOUT ANY
+ * WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
+ * FOR A PARTICULAR PURPOSE. See the GNU General Public License for more
+ * details.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program; see the file COPYING; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
+ */
+
+#include "bgpd/bgp_lcommunity.h"
+
+#ifndef FRR_BGP_COMMUNITY_ALIAS_H
+#define FRR_BGP_COMMUNITY_ALIAS_H
+
+struct community_alias {
+ /* Human readable community string */
+ char community[LCOMMUNITY_SIZE * 3];
+
+ /* Human readable community alias */
+ char alias[BUFSIZ];
+};
+
+extern void bgp_community_alias_init(void);
+extern void bgp_community_alias_finish(void);
+extern struct community_alias *bgp_ca_alias_lookup(struct community_alias *ca);
+extern struct community_alias *
+bgp_ca_community_lookup(struct community_alias *ca);
+extern void bgp_ca_community_insert(struct community_alias *ca);
+extern void bgp_ca_alias_insert(struct community_alias *ca);
+extern void bgp_ca_community_delete(struct community_alias *ca);
+extern void bgp_ca_alias_delete(struct community_alias *ca);
+extern int bgp_community_alias_write(struct vty *vty);
+extern const char *bgp_community2alias(char *community);
+extern const char *bgp_alias2community(char *alias);
+extern char *bgp_alias2community_str(const char *str);
+extern void bgp_community_alias_command_completion_setup(void);
+
+#endif /* FRR_BGP_COMMUNITY_ALIAS_H */
diff --git a/bgpd/bgp_conditional_adv.c b/bgpd/bgp_conditional_adv.c
new file mode 100644
index 0000000..04a2cfc
--- /dev/null
+++ b/bgpd/bgp_conditional_adv.c
@@ -0,0 +1,376 @@
+/*
+ * BGP Conditional advertisement
+ * Copyright (C) 2020 Samsung R&D Institute India - Bangalore.
+ * Madhurilatha Kuruganti
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License as published by the Free
+ * Software Foundation; either version 2 of the License, or (at your option)
+ * any later version.
+ *
+ * This program is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
+ * more details.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program; see the file COPYING; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
+ */
+
+#include <zebra.h>
+
+#include "bgpd/bgp_conditional_adv.h"
+#include "bgpd/bgp_vty.h"
+
+static route_map_result_t
+bgp_check_rmap_prefixes_in_bgp_table(struct bgp_table *table,
+ struct route_map *rmap)
+{
+ struct attr dummy_attr = {0};
+ struct bgp_dest *dest;
+ struct bgp_path_info *pi;
+ struct bgp_path_info path = {0};
+ struct bgp_path_info_extra path_extra = {0};
+ const struct prefix *dest_p;
+ route_map_result_t ret = RMAP_DENYMATCH;
+
+ for (dest = bgp_table_top(table); dest; dest = bgp_route_next(dest)) {
+ dest_p = bgp_dest_get_prefix(dest);
+ assert(dest_p);
+
+ for (pi = bgp_dest_get_bgp_path_info(dest); pi; pi = pi->next) {
+ dummy_attr = *pi->attr;
+
+ /* Fill temp path_info */
+ prep_for_rmap_apply(&path, &path_extra, dest, pi,
+ pi->peer, &dummy_attr);
+
+ RESET_FLAG(dummy_attr.rmap_change_flags);
+
+ ret = route_map_apply(rmap, dest_p, &path);
+ bgp_attr_flush(&dummy_attr);
+
+ if (ret == RMAP_PERMITMATCH) {
+ bgp_dest_unlock_node(dest);
+ if (BGP_DEBUG(update, UPDATE_OUT))
+ zlog_debug(
+ "%s: Condition map routes present in BGP table",
+ __func__);
+
+ return ret;
+ }
+ }
+ }
+
+ if (BGP_DEBUG(update, UPDATE_OUT))
+ zlog_debug("%s: Condition map routes not present in BGP table",
+ __func__);
+
+ return ret;
+}
+
+static void bgp_conditional_adv_routes(struct peer *peer, afi_t afi,
+ safi_t safi, struct bgp_table *table,
+ struct route_map *rmap,
+ enum update_type update_type)
+{
+ bool addpath_capable;
+ struct bgp_dest *dest;
+ struct bgp_path_info *pi;
+ struct bgp_path_info path;
+ struct peer_af *paf;
+ const struct prefix *dest_p;
+ struct update_subgroup *subgrp;
+ struct attr advmap_attr = {0}, attr = {0};
+ struct bgp_path_info_extra path_extra = {0};
+ route_map_result_t ret;
+
+ 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) */
+ if (!subgrp)
+ return;
+
+ subgrp->pscount = 0;
+ SET_FLAG(subgrp->sflags, SUBGRP_STATUS_TABLE_REPARSING);
+
+ if (BGP_DEBUG(update, UPDATE_OUT))
+ zlog_debug("%s: %s routes to/from %s for %s", __func__,
+ update_type == UPDATE_TYPE_ADVERTISE ? "Advertise"
+ : "Withdraw",
+ peer->host, get_afi_safi_str(afi, safi, false));
+
+ addpath_capable = bgp_addpath_encode_tx(peer, afi, safi);
+
+ for (dest = bgp_table_top(table); dest; dest = bgp_route_next(dest)) {
+ dest_p = bgp_dest_get_prefix(dest);
+ assert(dest_p);
+
+ for (pi = bgp_dest_get_bgp_path_info(dest); pi; pi = pi->next) {
+ advmap_attr = *pi->attr;
+
+ /* Fill temp path_info */
+ prep_for_rmap_apply(&path, &path_extra, dest, pi,
+ pi->peer, &advmap_attr);
+
+ RESET_FLAG(advmap_attr.rmap_change_flags);
+
+ ret = route_map_apply(rmap, dest_p, &path);
+ if (ret != RMAP_PERMITMATCH ||
+ !bgp_check_selected(pi, peer, addpath_capable, afi,
+ safi)) {
+ bgp_attr_flush(&advmap_attr);
+ continue;
+ }
+
+ /* Skip route-map checks in
+ * subgroup_announce_check while executing from
+ * the conditional advertise scanner process.
+ * otherwise when route-map is also configured
+ * on same peer, routes in advertise-map may not
+ * be advertised as expected.
+ */
+ if (update_type == UPDATE_TYPE_ADVERTISE &&
+ subgroup_announce_check(dest, pi, subgrp, dest_p,
+ &attr, &advmap_attr)) {
+ bgp_adj_out_set_subgroup(dest, subgrp, &attr,
+ pi);
+ } else {
+ /* If default originate is enabled for
+ * the peer, do not send explicit
+ * withdraw. This will prevent deletion
+ * of default route advertised through
+ * default originate.
+ */
+ if (CHECK_FLAG(peer->af_flags[afi][safi],
+ PEER_FLAG_DEFAULT_ORIGINATE) &&
+ is_default_prefix(dest_p))
+ break;
+
+ bgp_adj_out_unset_subgroup(
+ dest, subgrp, 1,
+ bgp_addpath_id_for_peer(
+ peer, afi, safi,
+ &pi->tx_addpath));
+ }
+ bgp_attr_flush(&advmap_attr);
+ }
+ }
+ UNSET_FLAG(subgrp->sflags, SUBGRP_STATUS_TABLE_REPARSING);
+}
+
+/* Handler of conditional advertisement timer event.
+ * Each route in the condition-map is evaluated.
+ */
+static void bgp_conditional_adv_timer(struct thread *t)
+{
+ afi_t afi;
+ safi_t safi;
+ int pfx_rcd_safi;
+ struct bgp *bgp = NULL;
+ struct peer *peer = NULL;
+ struct peer_af *paf = NULL;
+ struct bgp_table *table = NULL;
+ struct bgp_filter *filter = NULL;
+ struct listnode *node, *nnode = NULL;
+ struct update_subgroup *subgrp = NULL;
+ route_map_result_t ret;
+ bool advmap_table_changed = false;
+
+ bgp = THREAD_ARG(t);
+ assert(bgp);
+
+ thread_add_timer(bm->master, bgp_conditional_adv_timer, bgp,
+ bgp->condition_check_period, &bgp->t_condition_check);
+
+ /* loop through each peer and check if we have peers with
+ * advmap_table_change attribute set, to make sure we send
+ * conditional advertisements properly below.
+ * peer->advmap_table_change is added on incoming BGP UPDATES,
+ * but here it's used for outgoing UPDATES, hence we need to
+ * check if at least one peer got advmap_table_change.
+ */
+ for (ALL_LIST_ELEMENTS_RO(bgp->peer, node, peer)) {
+ if (peer->advmap_table_change) {
+ advmap_table_changed = true;
+ break;
+ }
+ }
+
+ /* loop through each peer and advertise or withdraw routes if
+ * advertise-map is configured and prefix(es) in condition-map
+ * does exist(exist-map)/not exist(non-exist-map) in BGP table
+ * based on condition(exist-map or non-exist map)
+ */
+ for (ALL_LIST_ELEMENTS(bgp->peer, node, nnode, peer)) {
+ if (!CHECK_FLAG(peer->flags, PEER_FLAG_CONFIG_NODE))
+ continue;
+
+ if (!peer_established(peer))
+ continue;
+
+ FOREACH_AFI_SAFI (afi, safi) {
+ if (!peer->afc_nego[afi][safi])
+ continue;
+
+ /* labeled-unicast routes are installed in the unicast
+ * table so in order to display the correct PfxRcd value
+ * we must look at SAFI_UNICAST
+ */
+ pfx_rcd_safi = (safi == SAFI_LABELED_UNICAST)
+ ? SAFI_UNICAST
+ : safi;
+
+ table = bgp->rib[afi][pfx_rcd_safi];
+ if (!table)
+ continue;
+
+ filter = &peer->filter[afi][safi];
+
+ if (!filter->advmap.aname || !filter->advmap.cname
+ || !filter->advmap.amap || !filter->advmap.cmap)
+ continue;
+
+ if (!peer->advmap_config_change[afi][safi] &&
+ !advmap_table_changed)
+ continue;
+
+ if (BGP_DEBUG(update, UPDATE_OUT)) {
+ if (peer->advmap_table_change)
+ zlog_debug(
+ "%s: %s - routes changed in BGP table.",
+ __func__, peer->host);
+ if (peer->advmap_config_change[afi][safi])
+ zlog_debug(
+ "%s: %s for %s - advertise/condition map configuration is changed.",
+ __func__, peer->host,
+ get_afi_safi_str(afi, safi,
+ false));
+ }
+
+ /* cmap (route-map attached to exist-map or
+ * non-exist-map) map validation
+ */
+ ret = bgp_check_rmap_prefixes_in_bgp_table(
+ table, filter->advmap.cmap);
+
+ /* Derive conditional advertisement status from
+ * condition and return value of condition-map
+ * validation.
+ */
+ if (filter->advmap.condition == CONDITION_EXIST)
+ filter->advmap.update_type =
+ (ret == RMAP_PERMITMATCH)
+ ? UPDATE_TYPE_ADVERTISE
+ : UPDATE_TYPE_WITHDRAW;
+ else
+ filter->advmap.update_type =
+ (ret == RMAP_PERMITMATCH)
+ ? UPDATE_TYPE_WITHDRAW
+ : UPDATE_TYPE_ADVERTISE;
+
+ /*
+ * Update condadv update type so
+ * subgroup_announce_check() can properly apply
+ * outbound policy according to advertisement state
+ */
+ paf = peer_af_find(peer, afi, safi);
+ if (paf && (SUBGRP_PEER(PAF_SUBGRP(paf))
+ ->filter[afi][safi]
+ .advmap.update_type !=
+ filter->advmap.update_type)) {
+ /* Handle change to peer advmap */
+ if (BGP_DEBUG(update, UPDATE_OUT))
+ zlog_debug(
+ "%s: advmap.update_type changed for peer %s, adjusting update_group.",
+ __func__, peer->host);
+
+ update_group_adjust_peer(paf);
+ }
+
+ /* Send regular update as per the existing policy.
+ * There is a change in route-map, match-rule, ACLs,
+ * or route-map filter configuration on the same peer.
+ */
+ if (peer->advmap_config_change[afi][safi]) {
+
+ if (BGP_DEBUG(update, UPDATE_OUT))
+ zlog_debug(
+ "%s: Configuration is changed on peer %s for %s, send the normal update first.",
+ __func__, peer->host,
+ get_afi_safi_str(afi, safi,
+ false));
+ if (paf) {
+ update_subgroup_split_peer(paf, NULL);
+ subgrp = paf->subgroup;
+
+ if (subgrp && subgrp->update_group)
+ subgroup_announce_table(
+ paf->subgroup, NULL);
+ }
+ peer->advmap_config_change[afi][safi] = false;
+ }
+
+ /* Send update as per the conditional advertisement */
+ bgp_conditional_adv_routes(peer, afi, safi, table,
+ filter->advmap.amap,
+ filter->advmap.update_type);
+ }
+ peer->advmap_table_change = false;
+ }
+}
+
+void bgp_conditional_adv_enable(struct peer *peer, afi_t afi, safi_t safi)
+{
+ struct bgp *bgp = peer->bgp;
+
+ assert(bgp);
+
+ /* This flag is used to monitor conditional routes status in BGP table,
+ * and advertise/withdraw routes only when there is a change in BGP
+ * table w.r.t conditional routes
+ */
+ peer->advmap_config_change[afi][safi] = true;
+
+ /* advertise-map is already configured on at least one of its
+ * neighbors (AFI/SAFI). So just increment the counter.
+ */
+ if (++bgp->condition_filter_count > 1) {
+ if (BGP_DEBUG(update, UPDATE_OUT))
+ zlog_debug("%s: condition_filter_count %d", __func__,
+ bgp->condition_filter_count);
+
+ return;
+ }
+
+ /* Register for conditional routes polling timer */
+ if (!thread_is_scheduled(bgp->t_condition_check))
+ thread_add_timer(bm->master, bgp_conditional_adv_timer, bgp, 0,
+ &bgp->t_condition_check);
+}
+
+void bgp_conditional_adv_disable(struct peer *peer, afi_t afi, safi_t safi)
+{
+ struct bgp *bgp = peer->bgp;
+
+ assert(bgp);
+
+ /* advertise-map is not configured on any of its neighbors or
+ * it is configured on more than one neighbor(AFI/SAFI).
+ * So there's nothing to do except decrementing the counter.
+ */
+ if (--bgp->condition_filter_count != 0) {
+ if (BGP_DEBUG(update, UPDATE_OUT))
+ zlog_debug("%s: condition_filter_count %d", __func__,
+ bgp->condition_filter_count);
+
+ return;
+ }
+
+ /* Last filter removed. So cancel conditional routes polling thread. */
+ THREAD_OFF(bgp->t_condition_check);
+}
diff --git a/bgpd/bgp_conditional_adv.h b/bgpd/bgp_conditional_adv.h
new file mode 100644
index 0000000..371ae85
--- /dev/null
+++ b/bgpd/bgp_conditional_adv.h
@@ -0,0 +1,47 @@
+/*
+ * BGP Conditional advertisement
+ * Copyright (C) 2020 Samsung R&D Institute India - Bangalore.
+ * Madhurilatha Kuruganti
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License as published by the Free
+ * Software Foundation; either version 2 of the License, or (at your option)
+ * any later version.
+ *
+ * This program is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
+ * more details.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program; see the file COPYING; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
+ */
+
+#ifndef _FRR_BGP_CONDITION_ADV_H
+#define _FRR_BGP_CONDITION_ADV_H
+#include <zebra.h>
+#include "prefix.h"
+#include "bgpd/bgp_addpath.h"
+#include "bgpd/bgp_attr.h"
+#include "bgpd/bgpd.h"
+#include "bgpd/bgp_debug.h"
+#include "bgpd/bgp_route.h"
+#include "bgpd/bgp_updgrp.h"
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+/* Polling time for monitoring condition-map routes in route table */
+#define DEFAULT_CONDITIONAL_ROUTES_POLL_TIME 60
+
+extern void bgp_conditional_adv_enable(struct peer *peer, afi_t afi,
+ safi_t safi);
+extern void bgp_conditional_adv_disable(struct peer *peer, afi_t afi,
+ safi_t safi);
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* _FRR_BGP_CONDITION_ADV_H */
diff --git a/bgpd/bgp_damp.c b/bgpd/bgp_damp.c
new file mode 100644
index 0000000..6646190
--- /dev/null
+++ b/bgpd/bgp_damp.c
@@ -0,0 +1,741 @@
+/* BGP flap dampening
+ * Copyright (C) 2001 IP Infusion Inc.
+ *
+ * This file is part of GNU Zebra.
+ *
+ * GNU Zebra is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License as published by the
+ * Free Software Foundation; either version 2, or (at your option) any
+ * later version.
+ *
+ * GNU Zebra is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program; see the file COPYING; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
+ */
+
+#include <zebra.h>
+#include <math.h>
+
+#include "prefix.h"
+#include "memory.h"
+#include "command.h"
+#include "log.h"
+#include "thread.h"
+#include "queue.h"
+#include "filter.h"
+
+#include "bgpd/bgpd.h"
+#include "bgpd/bgp_damp.h"
+#include "bgpd/bgp_table.h"
+#include "bgpd/bgp_route.h"
+#include "bgpd/bgp_attr.h"
+#include "bgpd/bgp_advertise.h"
+#include "bgpd/bgp_vty.h"
+
+/* Global variable to access damping configuration */
+static struct bgp_damp_config damp[AFI_MAX][SAFI_MAX];
+
+/* Utility macro to add and delete BGP dampening information to no
+ used list. */
+#define BGP_DAMP_LIST_ADD(N, A) BGP_PATH_INFO_ADD(N, A, no_reuse_list)
+#define BGP_DAMP_LIST_DEL(N, A) BGP_PATH_INFO_DEL(N, A, no_reuse_list)
+
+/* Calculate reuse list index by penalty value. */
+static int bgp_reuse_index(int penalty, struct bgp_damp_config *bdc)
+{
+ unsigned int i;
+ int index;
+
+ /*
+ * reuse_limit can't be zero, this is for Coverity
+ * to bypass division by zero test.
+ */
+ assert(bdc->reuse_limit);
+
+ i = (int)(((double)penalty / bdc->reuse_limit - 1.0)
+ * bdc->scale_factor);
+
+ if (i >= bdc->reuse_index_size)
+ i = bdc->reuse_index_size - 1;
+
+ index = bdc->reuse_index[i] - bdc->reuse_index[0];
+
+ return (bdc->reuse_offset + index) % bdc->reuse_list_size;
+}
+
+/* Add BGP dampening information to reuse list. */
+static void bgp_reuse_list_add(struct bgp_damp_info *bdi,
+ struct bgp_damp_config *bdc)
+{
+ int index;
+
+ index = bdi->index = bgp_reuse_index(bdi->penalty, bdc);
+
+ bdi->prev = NULL;
+ bdi->next = bdc->reuse_list[index];
+ if (bdc->reuse_list[index])
+ bdc->reuse_list[index]->prev = bdi;
+ bdc->reuse_list[index] = bdi;
+}
+
+/* Delete BGP dampening information from reuse list. */
+static void bgp_reuse_list_delete(struct bgp_damp_info *bdi,
+ struct bgp_damp_config *bdc)
+{
+ if (bdi->next)
+ bdi->next->prev = bdi->prev;
+ if (bdi->prev)
+ bdi->prev->next = bdi->next;
+ else
+ bdc->reuse_list[bdi->index] = bdi->next;
+}
+
+/* Return decayed penalty value. */
+int bgp_damp_decay(time_t tdiff, int penalty, struct bgp_damp_config *bdc)
+{
+ unsigned int i;
+
+ i = (int)((double)tdiff / DELTA_T);
+
+ if (i == 0)
+ return penalty;
+
+ if (i >= bdc->decay_array_size)
+ return 0;
+
+ return (int)(penalty * bdc->decay_array[i]);
+}
+
+/* Handler of reuse timer event. Each route in the current reuse-list
+ is evaluated. RFC2439 Section 4.8.7. */
+static void bgp_reuse_timer(struct thread *t)
+{
+ struct bgp_damp_info *bdi;
+ struct bgp_damp_info *next;
+ time_t t_now, t_diff;
+
+ struct bgp_damp_config *bdc = THREAD_ARG(t);
+
+ bdc->t_reuse = NULL;
+ thread_add_timer(bm->master, bgp_reuse_timer, bdc, DELTA_REUSE,
+ &bdc->t_reuse);
+
+ t_now = monotime(NULL);
+
+ /* 1. save a pointer to the current zeroth queue head and zero the
+ list head entry. */
+ bdi = bdc->reuse_list[bdc->reuse_offset];
+ bdc->reuse_list[bdc->reuse_offset] = NULL;
+
+ /* 2. set offset = modulo reuse-list-size ( offset + 1 ), thereby
+ rotating the circular queue of list-heads. */
+ bdc->reuse_offset = (bdc->reuse_offset + 1) % bdc->reuse_list_size;
+
+ /* 3. if ( the saved list head pointer is non-empty ) */
+ for (; bdi; bdi = next) {
+ struct bgp *bgp = bdi->path->peer->bgp;
+
+ next = bdi->next;
+
+ /* Set t-diff = t-now - t-updated. */
+ t_diff = t_now - bdi->t_updated;
+
+ /* Set figure-of-merit = figure-of-merit * decay-array-ok
+ * [t-diff] */
+ bdi->penalty = bgp_damp_decay(t_diff, bdi->penalty, bdc);
+
+ /* Set t-updated = t-now. */
+ bdi->t_updated = t_now;
+
+ /* if (figure-of-merit < reuse). */
+ if (bdi->penalty < bdc->reuse_limit) {
+ /* Reuse the route. */
+ bgp_path_info_unset_flag(bdi->dest, bdi->path,
+ BGP_PATH_DAMPED);
+ bdi->suppress_time = 0;
+
+ if (bdi->lastrecord == BGP_RECORD_UPDATE) {
+ bgp_path_info_unset_flag(bdi->dest, bdi->path,
+ BGP_PATH_HISTORY);
+ bgp_aggregate_increment(
+ bgp, bgp_dest_get_prefix(bdi->dest),
+ bdi->path, bdi->afi, bdi->safi);
+ bgp_process(bgp, bdi->dest, bdi->afi,
+ bdi->safi);
+ }
+
+ if (bdi->penalty <= bdc->reuse_limit / 2.0)
+ bgp_damp_info_free(bdi, 1, bdc->afi, bdc->safi);
+ else
+ BGP_DAMP_LIST_ADD(bdc, bdi);
+ } else
+ /* Re-insert into another list (See RFC2439 Section
+ * 4.8.6). */
+ bgp_reuse_list_add(bdi, bdc);
+ }
+}
+
+/* A route becomes unreachable (RFC2439 Section 4.8.2). */
+int bgp_damp_withdraw(struct bgp_path_info *path, struct bgp_dest *dest,
+ afi_t afi, safi_t safi, int attr_change)
+{
+ time_t t_now;
+ struct bgp_damp_info *bdi = NULL;
+ unsigned int last_penalty = 0;
+ struct bgp_damp_config *bdc = &damp[afi][safi];
+
+ t_now = monotime(NULL);
+
+ /* Processing Unreachable Messages. */
+ if (path->extra)
+ bdi = path->extra->damp_info;
+
+ if (bdi == NULL) {
+ /* If there is no previous stability history. */
+
+ /* RFC2439 said:
+ 1. allocate a damping structure.
+ 2. set figure-of-merit = 1.
+ 3. withdraw the route. */
+
+ bdi = XCALLOC(MTYPE_BGP_DAMP_INFO,
+ sizeof(struct bgp_damp_info));
+ bdi->path = path;
+ bdi->dest = dest;
+ bdi->penalty =
+ (attr_change ? DEFAULT_PENALTY / 2 : DEFAULT_PENALTY);
+ bdi->flap = 1;
+ bdi->start_time = t_now;
+ bdi->suppress_time = 0;
+ bdi->index = -1;
+ bdi->afi = afi;
+ bdi->safi = safi;
+ (bgp_path_info_extra_get(path))->damp_info = bdi;
+ BGP_DAMP_LIST_ADD(bdc, bdi);
+ } else {
+ last_penalty = bdi->penalty;
+
+ /* 1. Set t-diff = t-now - t-updated. */
+ bdi->penalty = (bgp_damp_decay(t_now - bdi->t_updated,
+ bdi->penalty, bdc)
+ + (attr_change ? DEFAULT_PENALTY / 2
+ : DEFAULT_PENALTY));
+
+ if (bdi->penalty > bdc->ceiling)
+ bdi->penalty = bdc->ceiling;
+
+ bdi->flap++;
+ }
+
+ assert((dest == bdi->dest) && (path == bdi->path));
+
+ bdi->lastrecord = BGP_RECORD_WITHDRAW;
+ bdi->t_updated = t_now;
+
+ /* Make this route as historical status. */
+ bgp_path_info_set_flag(dest, path, BGP_PATH_HISTORY);
+
+ /* Remove the route from a reuse list if it is on one. */
+ if (CHECK_FLAG(bdi->path->flags, BGP_PATH_DAMPED)) {
+ /* If decay rate isn't equal to 0, reinsert brn. */
+ if (bdi->penalty != last_penalty && bdi->index >= 0) {
+ bgp_reuse_list_delete(bdi, bdc);
+ bgp_reuse_list_add(bdi, bdc);
+ }
+ return BGP_DAMP_SUPPRESSED;
+ }
+
+ /* If not suppressed before, do annonunce this withdraw and
+ insert into reuse_list. */
+ if (bdi->penalty >= bdc->suppress_value) {
+ bgp_path_info_set_flag(dest, path, BGP_PATH_DAMPED);
+ bdi->suppress_time = t_now;
+ BGP_DAMP_LIST_DEL(bdc, bdi);
+ bgp_reuse_list_add(bdi, bdc);
+ }
+
+ return BGP_DAMP_USED;
+}
+
+int bgp_damp_update(struct bgp_path_info *path, struct bgp_dest *dest,
+ afi_t afi, safi_t safi)
+{
+ time_t t_now;
+ struct bgp_damp_info *bdi;
+ int status;
+ struct bgp_damp_config *bdc = &damp[afi][safi];
+
+ if (!path->extra || !((bdi = path->extra->damp_info)))
+ return BGP_DAMP_USED;
+
+ t_now = monotime(NULL);
+ bgp_path_info_unset_flag(dest, path, BGP_PATH_HISTORY);
+
+ bdi->lastrecord = BGP_RECORD_UPDATE;
+ bdi->penalty =
+ bgp_damp_decay(t_now - bdi->t_updated, bdi->penalty, bdc);
+
+ if (!CHECK_FLAG(bdi->path->flags, BGP_PATH_DAMPED)
+ && (bdi->penalty < bdc->suppress_value))
+ status = BGP_DAMP_USED;
+ else if (CHECK_FLAG(bdi->path->flags, BGP_PATH_DAMPED)
+ && (bdi->penalty < bdc->reuse_limit)) {
+ bgp_path_info_unset_flag(dest, path, BGP_PATH_DAMPED);
+ bgp_reuse_list_delete(bdi, bdc);
+ BGP_DAMP_LIST_ADD(bdc, bdi);
+ bdi->suppress_time = 0;
+ status = BGP_DAMP_USED;
+ } else
+ status = BGP_DAMP_SUPPRESSED;
+
+ if (bdi->penalty > bdc->reuse_limit / 2.0)
+ bdi->t_updated = t_now;
+ else
+ bgp_damp_info_free(bdi, 0, afi, safi);
+
+ return status;
+}
+
+void bgp_damp_info_free(struct bgp_damp_info *bdi, int withdraw, afi_t afi,
+ safi_t safi)
+{
+ struct bgp_path_info *path;
+ struct bgp_damp_config *bdc = &damp[afi][safi];
+
+ if (!bdi)
+ return;
+
+ path = bdi->path;
+ path->extra->damp_info = NULL;
+
+ if (CHECK_FLAG(path->flags, BGP_PATH_DAMPED))
+ bgp_reuse_list_delete(bdi, bdc);
+ else
+ BGP_DAMP_LIST_DEL(bdc, bdi);
+
+ bgp_path_info_unset_flag(bdi->dest, path,
+ BGP_PATH_HISTORY | BGP_PATH_DAMPED);
+
+ if (bdi->lastrecord == BGP_RECORD_WITHDRAW && withdraw)
+ bgp_path_info_delete(bdi->dest, path);
+
+ XFREE(MTYPE_BGP_DAMP_INFO, bdi);
+}
+
+static void bgp_damp_parameter_set(int hlife, int reuse, int sup, int maxsup,
+ struct bgp_damp_config *bdc)
+{
+ double reuse_max_ratio;
+ unsigned int i;
+ double j;
+
+ bdc->suppress_value = sup;
+ bdc->half_life = hlife;
+ bdc->reuse_limit = reuse;
+ bdc->max_suppress_time = maxsup;
+
+ /* Initialize params per bgp_damp_config. */
+ bdc->reuse_index_size = REUSE_ARRAY_SIZE;
+
+ bdc->ceiling = (int)(bdc->reuse_limit
+ * (pow(2, (double)bdc->max_suppress_time
+ / bdc->half_life)));
+
+ /* Decay-array computations */
+ bdc->decay_array_size = ceil((double)bdc->max_suppress_time / DELTA_T);
+ bdc->decay_array = XMALLOC(MTYPE_BGP_DAMP_ARRAY,
+ sizeof(double) * (bdc->decay_array_size));
+ bdc->decay_array[0] = 1.0;
+ bdc->decay_array[1] =
+ exp((1.0 / ((double)bdc->half_life / DELTA_T)) * log(0.5));
+
+ /* Calculate decay values for all possible times */
+ for (i = 2; i < bdc->decay_array_size; i++)
+ bdc->decay_array[i] =
+ bdc->decay_array[i - 1] * bdc->decay_array[1];
+
+ /* Reuse-list computations */
+ i = ceil((double)bdc->max_suppress_time / DELTA_REUSE) + 1;
+ if (i > REUSE_LIST_SIZE || i == 0)
+ i = REUSE_LIST_SIZE;
+ bdc->reuse_list_size = i;
+
+ bdc->reuse_list =
+ XCALLOC(MTYPE_BGP_DAMP_ARRAY,
+ bdc->reuse_list_size * sizeof(struct bgp_reuse_node *));
+
+ /* Reuse-array computations */
+ bdc->reuse_index = XCALLOC(MTYPE_BGP_DAMP_ARRAY,
+ sizeof(int) * bdc->reuse_index_size);
+
+ reuse_max_ratio = (double)bdc->ceiling / bdc->reuse_limit;
+ j = (exp((double)bdc->max_suppress_time / bdc->half_life) * log10(2.0));
+ if (reuse_max_ratio > j && j != 0)
+ reuse_max_ratio = j;
+
+ bdc->scale_factor =
+ (double)bdc->reuse_index_size / (reuse_max_ratio - 1);
+
+ for (i = 0; i < bdc->reuse_index_size; i++) {
+ bdc->reuse_index[i] =
+ (int)(((double)bdc->half_life / DELTA_REUSE)
+ * log10(1.0
+ / (bdc->reuse_limit
+ * (1.0
+ + ((double)i / bdc->scale_factor))))
+ / log10(0.5));
+ }
+}
+
+int bgp_damp_enable(struct bgp *bgp, afi_t afi, safi_t safi, time_t half,
+ unsigned int reuse, unsigned int suppress, time_t max)
+{
+ struct bgp_damp_config *bdc = &damp[afi][safi];
+
+ if (CHECK_FLAG(bgp->af_flags[afi][safi], BGP_CONFIG_DAMPENING)) {
+ if (bdc->half_life == half && bdc->reuse_limit == reuse
+ && bdc->suppress_value == suppress
+ && bdc->max_suppress_time == max)
+ return 0;
+ bgp_damp_disable(bgp, afi, safi);
+ }
+
+ SET_FLAG(bgp->af_flags[afi][safi], BGP_CONFIG_DAMPENING);
+ bgp_damp_parameter_set(half, reuse, suppress, max, bdc);
+
+ /* Register reuse timer. */
+ thread_add_timer(bm->master, bgp_reuse_timer, bdc, DELTA_REUSE,
+ &bdc->t_reuse);
+
+ return 0;
+}
+
+static void bgp_damp_config_clean(struct bgp_damp_config *bdc)
+{
+ /* Free decay array */
+ XFREE(MTYPE_BGP_DAMP_ARRAY, bdc->decay_array);
+ bdc->decay_array_size = 0;
+
+ /* Free reuse index array */
+ XFREE(MTYPE_BGP_DAMP_ARRAY, bdc->reuse_index);
+ bdc->reuse_index_size = 0;
+
+ /* Free reuse list array. */
+ XFREE(MTYPE_BGP_DAMP_ARRAY, bdc->reuse_list);
+ bdc->reuse_list_size = 0;
+}
+
+/* Clean all the bgp_damp_info stored in reuse_list. */
+void bgp_damp_info_clean(afi_t afi, safi_t safi)
+{
+ unsigned int i;
+ struct bgp_damp_info *bdi, *next;
+ struct bgp_damp_config *bdc = &damp[afi][safi];
+
+ bdc->reuse_offset = 0;
+
+ for (i = 0; i < bdc->reuse_list_size; i++) {
+ if (!bdc->reuse_list[i])
+ continue;
+
+ for (bdi = bdc->reuse_list[i]; bdi; bdi = next) {
+ next = bdi->next;
+ bgp_damp_info_free(bdi, 1, afi, safi);
+ }
+ bdc->reuse_list[i] = NULL;
+ }
+
+ for (bdi = bdc->no_reuse_list; bdi; bdi = next) {
+ next = bdi->next;
+ bgp_damp_info_free(bdi, 1, afi, safi);
+ }
+ bdc->no_reuse_list = NULL;
+}
+
+int bgp_damp_disable(struct bgp *bgp, afi_t afi, safi_t safi)
+{
+ struct bgp_damp_config *bdc = &damp[afi][safi];
+ /* If it wasn't enabled, there's nothing to do. */
+ if (!CHECK_FLAG(bgp->af_flags[afi][safi], BGP_CONFIG_DAMPENING))
+ return 0;
+
+ /* Cancel reuse event. */
+ THREAD_OFF(bdc->t_reuse);
+
+ /* Clean BGP dampening information. */
+ bgp_damp_info_clean(afi, safi);
+
+ /* Clear configuration */
+ bgp_damp_config_clean(bdc);
+
+ UNSET_FLAG(bgp->af_flags[afi][safi], BGP_CONFIG_DAMPENING);
+ return 0;
+}
+
+void bgp_config_write_damp(struct vty *vty, afi_t afi, safi_t safi)
+{
+ if (damp[afi][safi].half_life == DEFAULT_HALF_LIFE * 60
+ && damp[afi][safi].reuse_limit == DEFAULT_REUSE
+ && damp[afi][safi].suppress_value == DEFAULT_SUPPRESS
+ && damp[afi][safi].max_suppress_time
+ == damp[afi][safi].half_life * 4)
+ vty_out(vty, " bgp dampening\n");
+ else if (damp[afi][safi].half_life != DEFAULT_HALF_LIFE * 60
+ && damp[afi][safi].reuse_limit == DEFAULT_REUSE
+ && damp[afi][safi].suppress_value == DEFAULT_SUPPRESS
+ && damp[afi][safi].max_suppress_time
+ == damp[afi][safi].half_life * 4)
+ vty_out(vty, " bgp dampening %lld\n",
+ damp[afi][safi].half_life / 60LL);
+ else
+ vty_out(vty, " bgp dampening %lld %d %d %lld\n",
+ damp[afi][safi].half_life / 60LL,
+ damp[afi][safi].reuse_limit,
+ damp[afi][safi].suppress_value,
+ damp[afi][safi].max_suppress_time / 60LL);
+}
+
+static const char *bgp_get_reuse_time(unsigned int penalty, char *buf,
+ size_t len, afi_t afi, safi_t safi,
+ bool use_json, json_object *json)
+{
+ time_t reuse_time = 0;
+ struct tm tm;
+ int time_store = 0;
+
+ if (penalty > damp[afi][safi].reuse_limit) {
+ reuse_time = (int)(DELTA_T
+ * ((log((double)damp[afi][safi].reuse_limit
+ / penalty))
+ / (log(damp[afi][safi].decay_array[1]))));
+
+ if (reuse_time > damp[afi][safi].max_suppress_time)
+ reuse_time = damp[afi][safi].max_suppress_time;
+
+ gmtime_r(&reuse_time, &tm);
+ } else
+ reuse_time = 0;
+
+ /* Making formatted timer strings. */
+ if (reuse_time == 0) {
+ if (use_json)
+ json_object_int_add(json, "reuseTimerMsecs", 0);
+ else
+ snprintf(buf, len, "00:00:00");
+ } else if (reuse_time < ONE_DAY_SECOND) {
+ if (use_json) {
+ time_store = (3600000 * tm.tm_hour)
+ + (60000 * tm.tm_min)
+ + (1000 * tm.tm_sec);
+ json_object_int_add(json, "reuseTimerMsecs",
+ time_store);
+ } else
+ snprintf(buf, len, "%02d:%02d:%02d", tm.tm_hour,
+ tm.tm_min, tm.tm_sec);
+ } else if (reuse_time < ONE_WEEK_SECOND) {
+ if (use_json) {
+ time_store = (86400000 * tm.tm_yday)
+ + (3600000 * tm.tm_hour)
+ + (60000 * tm.tm_min)
+ + (1000 * tm.tm_sec);
+ json_object_int_add(json, "reuseTimerMsecs",
+ time_store);
+ } else
+ snprintf(buf, len, "%dd%02dh%02dm", tm.tm_yday,
+ tm.tm_hour, tm.tm_min);
+ } else {
+ if (use_json) {
+ time_store =
+ (604800000 * tm.tm_yday / 7)
+ + (86400000
+ * (tm.tm_yday - ((tm.tm_yday / 7) * 7)))
+ + (3600000 * tm.tm_hour) + (60000 * tm.tm_min)
+ + (1000 * tm.tm_sec);
+ json_object_int_add(json, "reuseTimerMsecs",
+ time_store);
+ } else
+ snprintf(buf, len, "%02dw%dd%02dh", tm.tm_yday / 7,
+ tm.tm_yday - ((tm.tm_yday / 7) * 7),
+ tm.tm_hour);
+ }
+
+ return buf;
+}
+
+void bgp_damp_info_vty(struct vty *vty, struct bgp_path_info *path, afi_t afi,
+ safi_t safi, json_object *json_path)
+{
+ struct bgp_damp_info *bdi;
+ time_t t_now, t_diff;
+ char timebuf[BGP_UPTIME_LEN];
+ int penalty;
+ struct bgp_damp_config *bdc = &damp[afi][safi];
+
+ if (!path->extra)
+ return;
+
+ /* BGP dampening information. */
+ bdi = path->extra->damp_info;
+
+ /* If dampening is not enabled or there is no dampening information,
+ return immediately. */
+ if (!bdc || !bdi)
+ return;
+
+ /* Calculate new penalty. */
+ t_now = monotime(NULL);
+ t_diff = t_now - bdi->t_updated;
+ penalty = bgp_damp_decay(t_diff, bdi->penalty, bdc);
+
+ if (json_path) {
+ json_object_int_add(json_path, "dampeningPenalty", penalty);
+ json_object_int_add(json_path, "dampeningFlapCount", bdi->flap);
+ peer_uptime(bdi->start_time, timebuf, BGP_UPTIME_LEN, 1,
+ json_path);
+
+ if (CHECK_FLAG(path->flags, BGP_PATH_DAMPED)
+ && !CHECK_FLAG(path->flags, BGP_PATH_HISTORY))
+ bgp_get_reuse_time(penalty, timebuf, BGP_UPTIME_LEN,
+ afi, safi, 1, json_path);
+ } else {
+ vty_out(vty,
+ " Dampinfo: penalty %d, flapped %d times in %s",
+ penalty, bdi->flap,
+ peer_uptime(bdi->start_time, timebuf, BGP_UPTIME_LEN, 0,
+ json_path));
+
+ if (CHECK_FLAG(path->flags, BGP_PATH_DAMPED)
+ && !CHECK_FLAG(path->flags, BGP_PATH_HISTORY))
+ vty_out(vty, ", reuse in %s",
+ bgp_get_reuse_time(penalty, timebuf,
+ BGP_UPTIME_LEN, afi, safi, 0,
+ json_path));
+
+ vty_out(vty, "\n");
+ }
+}
+
+const char *bgp_damp_reuse_time_vty(struct vty *vty, struct bgp_path_info *path,
+ char *timebuf, size_t len, afi_t afi,
+ safi_t safi, bool use_json,
+ json_object *json)
+{
+ struct bgp_damp_info *bdi;
+ time_t t_now, t_diff;
+ int penalty;
+ struct bgp_damp_config *bdc = &damp[afi][safi];
+
+ if (!path->extra)
+ return NULL;
+
+ /* BGP dampening information. */
+ bdi = path->extra->damp_info;
+
+ /* If dampening is not enabled or there is no dampening information,
+ return immediately. */
+ if (!bdc || !bdi)
+ return NULL;
+
+ /* Calculate new penalty. */
+ t_now = monotime(NULL);
+ t_diff = t_now - bdi->t_updated;
+ penalty = bgp_damp_decay(t_diff, bdi->penalty, bdc);
+
+ return bgp_get_reuse_time(penalty, timebuf, len, afi, safi, use_json,
+ json);
+}
+
+static int bgp_print_dampening_parameters(struct bgp *bgp, struct vty *vty,
+ afi_t afi, safi_t safi, bool use_json)
+{
+ if (CHECK_FLAG(bgp->af_flags[afi][safi], BGP_CONFIG_DAMPENING)) {
+ struct bgp_damp_config *bdc = &damp[afi][safi];
+
+ if (use_json) {
+ json_object *json = json_object_new_object();
+
+ json_object_int_add(json, "halfLifeSecs",
+ bdc->half_life);
+ json_object_int_add(json, "reusePenalty",
+ bdc->reuse_limit);
+ json_object_int_add(json, "suppressPenalty",
+ bdc->suppress_value);
+ json_object_int_add(json, "maxSuppressTimeSecs",
+ bdc->max_suppress_time);
+ json_object_int_add(json, "maxSuppressPenalty",
+ bdc->ceiling);
+
+ vty_json(vty, json);
+ } else {
+ vty_out(vty, "Half-life time: %lld min\n",
+ (long long)bdc->half_life / 60);
+ vty_out(vty, "Reuse penalty: %d\n", bdc->reuse_limit);
+ vty_out(vty, "Suppress penalty: %d\n",
+ bdc->suppress_value);
+ vty_out(vty, "Max suppress time: %lld min\n",
+ (long long)bdc->max_suppress_time / 60);
+ vty_out(vty, "Max suppress penalty: %u\n",
+ bdc->ceiling);
+ vty_out(vty, "\n");
+ }
+ } else if (!use_json)
+ vty_out(vty, "dampening not enabled for %s\n",
+ get_afi_safi_str(afi, safi, false));
+
+ return CMD_SUCCESS;
+}
+
+int bgp_show_dampening_parameters(struct vty *vty, afi_t afi, safi_t safi,
+ uint16_t show_flags)
+{
+ struct bgp *bgp;
+ bool use_json = CHECK_FLAG(show_flags, BGP_SHOW_OPT_JSON);
+
+ bgp = bgp_get_default();
+
+ if (bgp == NULL) {
+ vty_out(vty, "No BGP process is configured\n");
+ return CMD_WARNING;
+ }
+
+ if (!CHECK_FLAG(show_flags, BGP_SHOW_OPT_AFI_ALL))
+ return bgp_print_dampening_parameters(bgp, vty, afi, safi,
+ use_json);
+
+ 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;
+ FOREACH_SAFI (safi) {
+ if (strmatch(get_afi_safi_str(afi, safi, true),
+ "Unknown"))
+ continue;
+
+ if (!use_json)
+ vty_out(vty, "\nFor address family: %s\n\n",
+ get_afi_safi_str(afi, safi, false));
+
+ bgp_print_dampening_parameters(bgp, vty, afi, safi,
+ use_json);
+ }
+ } else {
+ FOREACH_AFI_SAFI (afi, safi) {
+ if (strmatch(get_afi_safi_str(afi, safi, true),
+ "Unknown"))
+ continue;
+
+ if (!use_json)
+ vty_out(vty, "\nFor address family: %s\n",
+ get_afi_safi_str(afi, safi, false));
+
+ bgp_print_dampening_parameters(bgp, vty, afi, safi,
+ use_json);
+ }
+ }
+ return CMD_SUCCESS;
+}
diff --git a/bgpd/bgp_damp.h b/bgpd/bgp_damp.h
new file mode 100644
index 0000000..f7deea5
--- /dev/null
+++ b/bgpd/bgp_damp.h
@@ -0,0 +1,159 @@
+/* BGP flap dampening
+ * Copyright (C) 2001 IP Infusion Inc.
+ *
+ * This file is part of GNU Zebra.
+ *
+ * GNU Zebra is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License as published by the
+ * Free Software Foundation; either version 2, or (at your option) any
+ * later version.
+ *
+ * GNU Zebra is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program; see the file COPYING; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
+ */
+
+#ifndef _QUAGGA_BGP_DAMP_H
+#define _QUAGGA_BGP_DAMP_H
+
+#include "bgpd/bgp_table.h"
+
+/* Structure maintained on a per-route basis. */
+struct bgp_damp_info {
+ /* Doubly linked list. This information must be linked to
+ reuse_list or no_reuse_list. */
+ struct bgp_damp_info *next;
+ struct bgp_damp_info *prev;
+
+ /* Figure-of-merit. */
+ unsigned int penalty;
+
+ /* Number of flapping. */
+ unsigned int flap;
+
+ /* First flap time */
+ time_t start_time;
+
+ /* Last time penalty was updated. */
+ time_t t_updated;
+
+ /* Time of route start to be suppressed. */
+ time_t suppress_time;
+
+ /* Back reference to bgp_path_info. */
+ struct bgp_path_info *path;
+
+ /* Back reference to bgp_node. */
+ struct bgp_dest *dest;
+
+ /* Current index in the reuse_list. */
+ int index;
+
+ /* Last time message type. */
+ uint8_t lastrecord;
+#define BGP_RECORD_UPDATE 1U
+#define BGP_RECORD_WITHDRAW 2U
+
+ afi_t afi;
+ safi_t safi;
+};
+
+/* Specified parameter set configuration. */
+struct bgp_damp_config {
+ /* Value over which routes suppressed. */
+ unsigned int suppress_value;
+
+ /* Value below which suppressed routes reused. */
+ unsigned int reuse_limit;
+
+ /* Max time a route can be suppressed. */
+ time_t max_suppress_time;
+
+ /* Time during which accumulated penalty reduces by half. */
+ time_t half_life;
+
+ /* Non-configurable parameters but fixed at implementation time.
+ * To change this values, init_bgp_damp() should be modified.
+ */
+ time_t tmax; /* Max time previous instability retained */
+ unsigned int reuse_list_size; /* Number of reuse lists */
+ unsigned int reuse_index_size; /* Size of reuse index array */
+
+ /* Non-configurable parameters. Most of these are calculated from
+ * the configurable parameters above.
+ */
+ unsigned int ceiling; /* Max value a penalty can attain */
+ unsigned int decay_rate_per_tick; /* Calculated from half-life */
+ unsigned int decay_array_size; /* Calculated using config parameters */
+ double scale_factor;
+ unsigned int reuse_scale_factor;
+
+ /* Decay array per-set based. */
+ double *decay_array;
+
+ /* Reuse index array per-set based. */
+ int *reuse_index;
+
+ /* Reuse list array per-set based. */
+ struct bgp_damp_info **reuse_list;
+ int reuse_offset;
+
+ /* All dampening information which is not on reuse list. */
+ struct bgp_damp_info *no_reuse_list;
+
+ /* Reuse timer thread per-set base. */
+ struct thread *t_reuse;
+
+ afi_t afi;
+ safi_t safi;
+};
+
+#define BGP_DAMP_NONE 0
+#define BGP_DAMP_USED 1
+#define BGP_DAMP_SUPPRESSED 2
+
+/* Time granularity for reuse lists */
+#define DELTA_REUSE 10
+
+/* Time granularity for decay arrays */
+#define DELTA_T 5
+
+#define DEFAULT_PENALTY 1000
+
+#define DEFAULT_HALF_LIFE 15
+#define DEFAULT_REUSE 750
+#define DEFAULT_SUPPRESS 2000
+
+#define REUSE_LIST_SIZE 256
+#define REUSE_ARRAY_SIZE 1024
+
+extern int bgp_damp_enable(struct bgp *bgp, afi_t afi, safi_t safi, time_t half,
+ unsigned int reuse, unsigned int suppress,
+ time_t max);
+extern int bgp_damp_disable(struct bgp *bgp, afi_t afi, safi_t safi);
+extern int bgp_damp_withdraw(struct bgp_path_info *path, struct bgp_dest *dest,
+ afi_t afi, safi_t safi, int attr_change);
+extern int bgp_damp_update(struct bgp_path_info *path, struct bgp_dest *dest,
+ afi_t afi, safi_t saff);
+extern void bgp_damp_info_free(struct bgp_damp_info *path, int withdraw,
+ afi_t afi, safi_t safi);
+extern void bgp_damp_info_clean(afi_t afi, safi_t safi);
+extern int bgp_damp_decay(time_t tdiff, int penalty,
+ struct bgp_damp_config *damp);
+extern void bgp_config_write_damp(struct vty *vty, afi_t afi, safi_t safi);
+extern void bgp_damp_info_vty(struct vty *vty, struct bgp_path_info *path,
+ afi_t afi, safi_t safi, json_object *json_path);
+extern const char *bgp_damp_reuse_time_vty(struct vty *vty,
+ struct bgp_path_info *path,
+ char *timebuf, size_t len, afi_t afi,
+ safi_t safi, bool use_json,
+ json_object *json);
+extern int bgp_show_dampening_parameters(struct vty *vty, afi_t afi,
+ safi_t safi, uint16_t show_flags);
+
+#endif /* _QUAGGA_BGP_DAMP_H */
diff --git a/bgpd/bgp_debug.c b/bgpd/bgp_debug.c
new file mode 100644
index 0000000..864cdb5
--- /dev/null
+++ b/bgpd/bgp_debug.c
@@ -0,0 +1,2741 @@
+/* BGP-4, BGP-4+ packet debug routine
+ * Copyright (C) 1996, 97, 99 Kunihiro Ishiguro
+ *
+ * This file is part of GNU Zebra.
+ *
+ * GNU Zebra is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License as published by the
+ * Free Software Foundation; either version 2, or (at your option) any
+ * later version.
+ *
+ * GNU Zebra is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program; see the file COPYING; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
+ */
+
+#include <zebra.h>
+
+#include <lib/version.h>
+#include "lib/bfd.h"
+#include "lib/printfrr.h"
+#include "prefix.h"
+#include "linklist.h"
+#include "stream.h"
+#include "command.h"
+#include "log.h"
+#include "sockunion.h"
+#include "memory.h"
+#include "queue.h"
+#include "filter.h"
+
+#include "bgpd/bgpd.h"
+#include "bgpd/bgp_aspath.h"
+#include "bgpd/bgp_route.h"
+#include "bgpd/bgp_attr.h"
+#include "bgpd/bgp_debug.h"
+#include "bgpd/bgp_community.h"
+#include "bgpd/bgp_lcommunity.h"
+#include "bgpd/bgp_updgrp.h"
+#include "bgpd/bgp_mplsvpn.h"
+#include "bgpd/bgp_ecommunity.h"
+#include "bgpd/bgp_label.h"
+#include "bgpd/bgp_evpn.h"
+#include "bgpd/bgp_evpn_private.h"
+#include "bgpd/bgp_evpn_vty.h"
+#include "bgpd/bgp_vty.h"
+#include "bgpd/bgp_flowspec.h"
+#include "bgpd/bgp_packet.h"
+
+unsigned long conf_bgp_debug_as4;
+unsigned long conf_bgp_debug_neighbor_events;
+unsigned long conf_bgp_debug_events;
+unsigned long conf_bgp_debug_packet;
+unsigned long conf_bgp_debug_filter;
+unsigned long conf_bgp_debug_keepalive;
+unsigned long conf_bgp_debug_update;
+unsigned long conf_bgp_debug_bestpath;
+unsigned long conf_bgp_debug_zebra;
+unsigned long conf_bgp_debug_allow_martians;
+unsigned long conf_bgp_debug_nht;
+unsigned long conf_bgp_debug_update_groups;
+unsigned long conf_bgp_debug_vpn;
+unsigned long conf_bgp_debug_flowspec;
+unsigned long conf_bgp_debug_labelpool;
+unsigned long conf_bgp_debug_pbr;
+unsigned long conf_bgp_debug_graceful_restart;
+unsigned long conf_bgp_debug_evpn_mh;
+unsigned long conf_bgp_debug_bfd;
+
+unsigned long term_bgp_debug_as4;
+unsigned long term_bgp_debug_neighbor_events;
+unsigned long term_bgp_debug_events;
+unsigned long term_bgp_debug_packet;
+unsigned long term_bgp_debug_filter;
+unsigned long term_bgp_debug_keepalive;
+unsigned long term_bgp_debug_update;
+unsigned long term_bgp_debug_bestpath;
+unsigned long term_bgp_debug_zebra;
+unsigned long term_bgp_debug_allow_martians;
+unsigned long term_bgp_debug_nht;
+unsigned long term_bgp_debug_update_groups;
+unsigned long term_bgp_debug_vpn;
+unsigned long term_bgp_debug_flowspec;
+unsigned long term_bgp_debug_labelpool;
+unsigned long term_bgp_debug_pbr;
+unsigned long term_bgp_debug_graceful_restart;
+unsigned long term_bgp_debug_evpn_mh;
+unsigned long term_bgp_debug_bfd;
+
+struct list *bgp_debug_neighbor_events_peers = NULL;
+struct list *bgp_debug_keepalive_peers = NULL;
+struct list *bgp_debug_update_out_peers = NULL;
+struct list *bgp_debug_update_in_peers = NULL;
+struct list *bgp_debug_update_prefixes = NULL;
+struct list *bgp_debug_bestpath_prefixes = NULL;
+struct list *bgp_debug_zebra_prefixes = NULL;
+
+/* messages for BGP-4 status */
+const struct message bgp_status_msg[] = {{Idle, "Idle"},
+ {Connect, "Connect"},
+ {Active, "Active"},
+ {OpenSent, "OpenSent"},
+ {OpenConfirm, "OpenConfirm"},
+ {Established, "Established"},
+ {Clearing, "Clearing"},
+ {Deleted, "Deleted"},
+ {0}};
+
+/* BGP message type string. */
+const char *const bgp_type_str[] = {NULL, "OPEN", "UPDATE",
+ "NOTIFICATION", "KEEPALIVE", "ROUTE-REFRESH",
+ "CAPABILITY"};
+
+/* message for BGP-4 Notify */
+static const struct message bgp_notify_msg[] = {
+ {BGP_NOTIFY_HEADER_ERR, "Message Header Error"},
+ {BGP_NOTIFY_OPEN_ERR, "OPEN Message Error"},
+ {BGP_NOTIFY_UPDATE_ERR, "UPDATE Message Error"},
+ {BGP_NOTIFY_HOLD_ERR, "Hold Timer Expired"},
+ {BGP_NOTIFY_FSM_ERR, "Neighbor Events Error"},
+ {BGP_NOTIFY_CEASE, "Cease"},
+ {BGP_NOTIFY_ROUTE_REFRESH_ERR, "ROUTE-REFRESH Message Error"},
+ {0}};
+
+static const struct message bgp_notify_head_msg[] = {
+ {BGP_NOTIFY_HEADER_NOT_SYNC, "/Connection Not Synchronized"},
+ {BGP_NOTIFY_HEADER_BAD_MESLEN, "/Bad Message Length"},
+ {BGP_NOTIFY_HEADER_BAD_MESTYPE, "/Bad Message Type"},
+ {0}};
+
+static const struct message bgp_notify_open_msg[] = {
+ {BGP_NOTIFY_SUBCODE_UNSPECIFIC, "/Unspecific"},
+ {BGP_NOTIFY_OPEN_UNSUP_VERSION, "/Unsupported Version Number"},
+ {BGP_NOTIFY_OPEN_BAD_PEER_AS, "/Bad Peer AS"},
+ {BGP_NOTIFY_OPEN_BAD_BGP_IDENT, "/Bad BGP Identifier"},
+ {BGP_NOTIFY_OPEN_UNSUP_PARAM, "/Unsupported Optional Parameter"},
+ {BGP_NOTIFY_OPEN_AUTH_FAILURE, "/Authentication Failure"},
+ {BGP_NOTIFY_OPEN_UNACEP_HOLDTIME, "/Unacceptable Hold Time"},
+ {BGP_NOTIFY_OPEN_UNSUP_CAPBL, "/Unsupported Capability"},
+ {BGP_NOTIFY_OPEN_ROLE_MISMATCH, "/Role Mismatch"},
+ {0}};
+
+static const struct message bgp_notify_update_msg[] = {
+ {BGP_NOTIFY_SUBCODE_UNSPECIFIC, "/Unspecific"},
+ {BGP_NOTIFY_UPDATE_MAL_ATTR, "/Malformed Attribute List"},
+ {BGP_NOTIFY_UPDATE_UNREC_ATTR, "/Unrecognized Well-known Attribute"},
+ {BGP_NOTIFY_UPDATE_MISS_ATTR, "/Missing Well-known Attribute"},
+ {BGP_NOTIFY_UPDATE_ATTR_FLAG_ERR, "/Attribute Flags Error"},
+ {BGP_NOTIFY_UPDATE_ATTR_LENG_ERR, "/Attribute Length Error"},
+ {BGP_NOTIFY_UPDATE_INVAL_ORIGIN, "/Invalid ORIGIN Attribute"},
+ {BGP_NOTIFY_UPDATE_AS_ROUTE_LOOP, "/AS Routing Loop"},
+ {BGP_NOTIFY_UPDATE_INVAL_NEXT_HOP, "/Invalid NEXT_HOP Attribute"},
+ {BGP_NOTIFY_UPDATE_OPT_ATTR_ERR, "/Optional Attribute Error"},
+ {BGP_NOTIFY_UPDATE_INVAL_NETWORK, "/Invalid Network Field"},
+ {BGP_NOTIFY_UPDATE_MAL_AS_PATH, "/Malformed AS_PATH"},
+ {0}};
+
+static const struct message bgp_notify_cease_msg[] = {
+ {BGP_NOTIFY_SUBCODE_UNSPECIFIC, "/Unspecific"},
+ {BGP_NOTIFY_CEASE_MAX_PREFIX, "/Maximum Number of Prefixes Reached"},
+ {BGP_NOTIFY_CEASE_ADMIN_SHUTDOWN, "/Administrative Shutdown"},
+ {BGP_NOTIFY_CEASE_PEER_UNCONFIG, "/Peer De-configured"},
+ {BGP_NOTIFY_CEASE_ADMIN_RESET, "/Administrative Reset"},
+ {BGP_NOTIFY_CEASE_CONNECT_REJECT, "/Connection Rejected"},
+ {BGP_NOTIFY_CEASE_CONFIG_CHANGE, "/Other Configuration Change"},
+ {BGP_NOTIFY_CEASE_COLLISION_RESOLUTION,
+ "/Connection Collision Resolution"},
+ {BGP_NOTIFY_CEASE_OUT_OF_RESOURCE, "/Out of Resources"},
+ {BGP_NOTIFY_CEASE_HARD_RESET, "/Hard Reset"},
+ {BGP_NOTIFY_CEASE_BFD_DOWN, "/BFD Down"},
+ {0}};
+
+static const struct message bgp_notify_route_refresh_msg[] = {
+ {BGP_NOTIFY_SUBCODE_UNSPECIFIC, "/Unspecific"},
+ {BGP_NOTIFY_ROUTE_REFRESH_INVALID_MSG_LEN, "/Invalid Message Length"},
+ {0}};
+
+static const struct message bgp_notify_fsm_msg[] = {
+ {BGP_NOTIFY_FSM_ERR_SUBCODE_UNSPECIFIC, "/Unspecific"},
+ {BGP_NOTIFY_FSM_ERR_SUBCODE_OPENSENT,
+ "/Receive Unexpected Message in OpenSent State"},
+ {BGP_NOTIFY_FSM_ERR_SUBCODE_OPENCONFIRM,
+ "/Receive Unexpected Message in OpenConfirm State"},
+ {BGP_NOTIFY_FSM_ERR_SUBCODE_ESTABLISHED,
+ "/Receive Unexpected Message in Established State"},
+ {0}};
+
+/* Origin strings. */
+const char *const bgp_origin_str[] = {"i", "e", "?"};
+const char *const bgp_origin_long_str[] = {"IGP", "EGP", "incomplete"};
+
+static void bgp_debug_print_evpn_prefix(struct vty *vty, const char *desc,
+ struct prefix *p);
+/* Given a string return a pointer the corresponding peer structure */
+static struct peer *bgp_find_peer(struct vty *vty, const char *peer_str)
+{
+ struct bgp *bgp = VTY_GET_CONTEXT(bgp);
+ int ret;
+ union sockunion su;
+ struct peer *peer;
+
+ if (!bgp) {
+ return NULL;
+ }
+ ret = str2sockunion(peer_str, &su);
+
+ /* 'swpX' string */
+ if (ret < 0) {
+ peer = peer_lookup_by_conf_if(bgp, peer_str);
+
+ if (!peer)
+ peer = peer_lookup_by_hostname(bgp, peer_str);
+
+ return peer;
+ } else
+ return peer_lookup(bgp, &su);
+}
+
+static void bgp_debug_list_free(struct list *list)
+{
+ struct bgp_debug_filter *filter;
+ struct listnode *node, *nnode;
+
+ if (list)
+ for (ALL_LIST_ELEMENTS(list, node, nnode, filter)) {
+ listnode_delete(list, filter);
+ prefix_free(&filter->p);
+ XFREE(MTYPE_BGP_DEBUG_STR, filter->host);
+ XFREE(MTYPE_BGP_DEBUG_FILTER, filter);
+ }
+}
+
+/*
+ * Print the desc along with a list of peers/prefixes this debug is
+ * enabled for
+ */
+static void bgp_debug_list_print(struct vty *vty, const char *desc,
+ struct list *list)
+{
+ struct bgp_debug_filter *filter;
+ struct listnode *node, *nnode;
+
+ vty_out(vty, "%s", desc);
+
+ if (list && !list_isempty(list)) {
+ vty_out(vty, " for");
+ for (ALL_LIST_ELEMENTS(list, node, nnode, filter)) {
+ if (filter->host)
+ vty_out(vty, " %s", filter->host);
+
+ if (filter->p && filter->p->family == AF_EVPN)
+ bgp_debug_print_evpn_prefix(vty, "", filter->p);
+ else if (filter->p)
+ vty_out(vty, " %pFX", filter->p);
+ }
+ }
+
+ vty_out(vty, "\n");
+}
+
+/*
+ * Print the command to enable the debug for each peer/prefix this debug is
+ * enabled for
+ */
+static int bgp_debug_list_conf_print(struct vty *vty, const char *desc,
+ struct list *list)
+{
+ struct bgp_debug_filter *filter;
+ struct listnode *node, *nnode;
+ int write = 0;
+
+ if (list && !list_isempty(list)) {
+ for (ALL_LIST_ELEMENTS(list, node, nnode, filter)) {
+ if (filter->host) {
+ vty_out(vty, "%s %s\n", desc, filter->host);
+ write++;
+ }
+
+ if (filter->p && filter->p->family == AF_EVPN) {
+ bgp_debug_print_evpn_prefix(vty, desc,
+ filter->p);
+ write++;
+ } else if (filter->p) {
+ vty_out(vty, "%s %pFX\n", desc, filter->p);
+ write++;
+ }
+ }
+ }
+
+ if (!write) {
+ vty_out(vty, "%s\n", desc);
+ write++;
+ }
+
+ return write;
+}
+
+static void bgp_debug_list_add_entry(struct list *list, const char *host,
+ const struct prefix *p)
+{
+ struct bgp_debug_filter *filter;
+
+ filter = XCALLOC(MTYPE_BGP_DEBUG_FILTER,
+ sizeof(struct bgp_debug_filter));
+
+ if (host) {
+ filter->host = XSTRDUP(MTYPE_BGP_DEBUG_STR, host);
+ filter->p = NULL;
+ } else if (p) {
+ filter->host = NULL;
+ filter->p = prefix_new();
+ prefix_copy(filter->p, p);
+ }
+
+ listnode_add(list, filter);
+}
+
+static bool bgp_debug_list_remove_entry(struct list *list, const char *host,
+ struct prefix *p)
+{
+ struct bgp_debug_filter *filter;
+ struct listnode *node, *nnode;
+
+ for (ALL_LIST_ELEMENTS(list, node, nnode, filter)) {
+ if (host && strcmp(filter->host, host) == 0) {
+ listnode_delete(list, filter);
+ XFREE(MTYPE_BGP_DEBUG_STR, filter->host);
+ XFREE(MTYPE_BGP_DEBUG_FILTER, filter);
+ return true;
+ } else if (p && filter->p->prefixlen == p->prefixlen
+ && prefix_match(filter->p, p)) {
+ listnode_delete(list, filter);
+ prefix_free(&filter->p);
+ XFREE(MTYPE_BGP_DEBUG_FILTER, filter);
+ return true;
+ }
+ }
+
+ return false;
+}
+
+static bool bgp_debug_list_has_entry(struct list *list, const char *host,
+ const struct prefix *p)
+{
+ struct bgp_debug_filter *filter;
+ struct listnode *node, *nnode;
+
+ for (ALL_LIST_ELEMENTS(list, node, nnode, filter)) {
+ if (host) {
+ if (strcmp(filter->host, host) == 0) {
+ return true;
+ }
+ } else if (p) {
+ if (filter->p->prefixlen == p->prefixlen
+ && prefix_match(filter->p, p)) {
+ return true;
+ }
+ }
+ }
+
+ return false;
+}
+
+bool bgp_debug_peer_updout_enabled(char *host)
+{
+ return (bgp_debug_list_has_entry(bgp_debug_update_out_peers, host,
+ NULL));
+}
+
+/* Dump attribute. */
+bool bgp_dump_attr(struct attr *attr, char *buf, size_t size)
+{
+ char addrbuf[BUFSIZ];
+
+ if (!attr)
+ return false;
+
+ buf[0] = '\0';
+
+ if (CHECK_FLAG(attr->flag, ATTR_FLAG_BIT(BGP_ATTR_NEXT_HOP)))
+ snprintfrr(buf, size, "nexthop %pI4", &attr->nexthop);
+
+ if (CHECK_FLAG(attr->flag, ATTR_FLAG_BIT(BGP_ATTR_ORIGIN)))
+ snprintf(buf + strlen(buf), size - strlen(buf), ", origin %s",
+ bgp_origin_str[attr->origin]);
+
+ /* Add MP case. */
+ if (attr->mp_nexthop_len == BGP_ATTR_NHLEN_IPV6_GLOBAL
+ || attr->mp_nexthop_len == BGP_ATTR_NHLEN_IPV6_GLOBAL_AND_LL)
+ snprintf(buf + strlen(buf), size - strlen(buf),
+ ", mp_nexthop %s",
+ inet_ntop(AF_INET6, &attr->mp_nexthop_global, addrbuf,
+ BUFSIZ));
+
+ if (attr->mp_nexthop_len == BGP_ATTR_NHLEN_IPV6_GLOBAL_AND_LL)
+ snprintf(buf + strlen(buf), size - strlen(buf), "(%s)",
+ inet_ntop(AF_INET6, &attr->mp_nexthop_local, addrbuf,
+ BUFSIZ));
+
+ if (attr->mp_nexthop_len == BGP_ATTR_NHLEN_IPV4)
+ snprintfrr(buf, size, "nexthop %pI4", &attr->nexthop);
+
+ if (CHECK_FLAG(attr->flag, ATTR_FLAG_BIT(BGP_ATTR_LOCAL_PREF)))
+ snprintf(buf + strlen(buf), size - strlen(buf),
+ ", localpref %u", attr->local_pref);
+
+ if (CHECK_FLAG(attr->flag, ATTR_FLAG_BIT(BGP_ATTR_MULTI_EXIT_DISC)))
+ snprintf(buf + strlen(buf), size - strlen(buf), ", metric %u",
+ attr->med);
+
+ if (CHECK_FLAG(attr->flag, ATTR_FLAG_BIT(BGP_ATTR_COMMUNITIES)))
+ snprintf(buf + strlen(buf), size - strlen(buf),
+ ", community %s",
+ community_str(bgp_attr_get_community(attr), false,
+ true));
+
+ if (CHECK_FLAG(attr->flag, ATTR_FLAG_BIT(BGP_ATTR_LARGE_COMMUNITIES)))
+ snprintf(buf + strlen(buf), size - strlen(buf),
+ ", large-community %s",
+ lcommunity_str(bgp_attr_get_lcommunity(attr), false,
+ true));
+
+ if (CHECK_FLAG(attr->flag, ATTR_FLAG_BIT(BGP_ATTR_EXT_COMMUNITIES)))
+ snprintf(buf + strlen(buf), size - strlen(buf),
+ ", extcommunity %s",
+ ecommunity_str(bgp_attr_get_ecommunity(attr)));
+
+ if (CHECK_FLAG(attr->flag, ATTR_FLAG_BIT(BGP_ATTR_ATOMIC_AGGREGATE)))
+ snprintf(buf + strlen(buf), size - strlen(buf),
+ ", atomic-aggregate");
+
+ if (CHECK_FLAG(attr->flag, ATTR_FLAG_BIT(BGP_ATTR_AGGREGATOR)))
+ snprintfrr(buf + strlen(buf), size - strlen(buf),
+ ", aggregated by %u %pI4", attr->aggregator_as,
+ &attr->aggregator_addr);
+
+ if (CHECK_FLAG(attr->flag, ATTR_FLAG_BIT(BGP_ATTR_ORIGINATOR_ID)))
+ snprintfrr(buf + strlen(buf), size - strlen(buf),
+ ", originator %pI4", &attr->originator_id);
+
+ if (CHECK_FLAG(attr->flag, ATTR_FLAG_BIT(BGP_ATTR_CLUSTER_LIST))) {
+ struct cluster_list *cluster;
+ int i;
+
+ snprintf(buf + strlen(buf), size - strlen(buf),
+ ", clusterlist");
+
+ cluster = bgp_attr_get_cluster(attr);
+ for (i = 0; i < cluster->length / 4; i++)
+ snprintfrr(buf + strlen(buf), size - strlen(buf),
+ " %pI4", &cluster->list[i]);
+ }
+
+ if (CHECK_FLAG(attr->flag, ATTR_FLAG_BIT(BGP_ATTR_PMSI_TUNNEL)))
+ snprintf(buf + strlen(buf), size - strlen(buf),
+ ", pmsi tnltype %u", bgp_attr_get_pmsi_tnl_type(attr));
+
+ if (CHECK_FLAG(attr->flag, ATTR_FLAG_BIT(BGP_ATTR_AS_PATH)))
+ snprintf(buf + strlen(buf), size - strlen(buf), ", path %s",
+ aspath_print(attr->aspath));
+
+ if (CHECK_FLAG(attr->flag, ATTR_FLAG_BIT(BGP_ATTR_PREFIX_SID))) {
+ if (attr->label_index != BGP_INVALID_LABEL_INDEX)
+ snprintf(buf + strlen(buf), size - strlen(buf),
+ ", label-index %u", attr->label_index);
+ }
+
+ if (strlen(buf) > 1)
+ return true;
+ else
+ return false;
+}
+
+const char *bgp_notify_code_str(char code)
+{
+ return lookup_msg(bgp_notify_msg, code, "Unrecognized Error Code");
+}
+
+const char *bgp_notify_subcode_str(char code, char subcode)
+{
+
+ switch (code) {
+ case BGP_NOTIFY_HEADER_ERR:
+ return lookup_msg(bgp_notify_head_msg, subcode,
+ "Unrecognized Error Subcode");
+ case BGP_NOTIFY_OPEN_ERR:
+ return lookup_msg(bgp_notify_open_msg, subcode,
+ "Unrecognized Error Subcode");
+ case BGP_NOTIFY_UPDATE_ERR:
+ return lookup_msg(bgp_notify_update_msg, subcode,
+ "Unrecognized Error Subcode");
+ case BGP_NOTIFY_HOLD_ERR:
+ break;
+ case BGP_NOTIFY_FSM_ERR:
+ return lookup_msg(bgp_notify_fsm_msg, subcode,
+ "Unrecognized Error Subcode");
+ case BGP_NOTIFY_CEASE:
+ return lookup_msg(bgp_notify_cease_msg, subcode,
+ "Unrecognized Error Subcode");
+ case BGP_NOTIFY_ROUTE_REFRESH_ERR:
+ return lookup_msg(bgp_notify_route_refresh_msg, subcode,
+ "Unrecognized Error Subcode");
+ }
+ return "";
+}
+
+/* extract notify admin reason if correctly present */
+const char *bgp_notify_admin_message(char *buf, size_t bufsz, uint8_t *data,
+ size_t datalen)
+{
+ if (!data || datalen < 1)
+ return NULL;
+
+ uint8_t len = data[0];
+ if (!len || len > datalen - 1)
+ return NULL;
+
+ return zlog_sanitize(buf, bufsz, data + 1, len);
+}
+
+/* dump notify packet */
+void bgp_notify_print(struct peer *peer, struct bgp_notify *bgp_notify,
+ const char *direct, bool hard_reset)
+{
+ const char *subcode_str;
+ const char *code_str;
+ const char *msg_str = NULL;
+ char msg_buf[1024];
+
+ if (BGP_DEBUG(neighbor_events, NEIGHBOR_EVENTS)
+ || CHECK_FLAG(peer->bgp->flags, BGP_FLAG_LOG_NEIGHBOR_CHANGES)) {
+ code_str = bgp_notify_code_str(bgp_notify->code);
+ subcode_str = bgp_notify_subcode_str(bgp_notify->code,
+ bgp_notify->subcode);
+
+ if (bgp_notify->code == BGP_NOTIFY_CEASE
+ && (bgp_notify->subcode == BGP_NOTIFY_CEASE_ADMIN_SHUTDOWN
+ || bgp_notify->subcode
+ == BGP_NOTIFY_CEASE_ADMIN_RESET)) {
+ msg_str = bgp_notify_admin_message(
+ msg_buf, sizeof(msg_buf), bgp_notify->raw_data,
+ bgp_notify->length);
+ }
+
+ if (msg_str) {
+ zlog_info(
+ "%%NOTIFICATION%s: %s neighbor %s %d/%d (%s%s) \"%s\"",
+ hard_reset ? "(Hard Reset)" : "",
+ strcmp(direct, "received") == 0
+ ? "received from"
+ : "sent to",
+ peer->host, bgp_notify->code,
+ bgp_notify->subcode, code_str, subcode_str,
+ msg_str);
+ } else {
+ msg_str = bgp_notify->data ? bgp_notify->data : "";
+ zlog_info(
+ "%%NOTIFICATION%s: %s neighbor %s %d/%d (%s%s) %d bytes %s",
+ hard_reset ? "(Hard Reset)" : "",
+ strcmp(direct, "received") == 0
+ ? "received from"
+ : "sent to",
+ peer->host, bgp_notify->code,
+ bgp_notify->subcode, code_str, subcode_str,
+ bgp_notify->length, msg_str);
+ }
+ }
+}
+
+static void bgp_debug_clear_updgrp_update_dbg(struct bgp *bgp)
+{
+ if (!bgp)
+ bgp = bgp_get_default();
+ update_group_walk(bgp, update_group_clear_update_dbg, NULL);
+}
+
+static void bgp_debug_print_evpn_prefix(struct vty *vty, const char *desc,
+ struct prefix *p)
+{
+ char evpn_desc[PREFIX2STR_BUFFER + INET_ADDRSTRLEN];
+ char buf[PREFIX2STR_BUFFER];
+ char buf2[ETHER_ADDR_STRLEN];
+
+ if (p->u.prefix_evpn.route_type == BGP_EVPN_MAC_IP_ROUTE) {
+ if (is_evpn_prefix_ipaddr_none((struct prefix_evpn *)p)) {
+ snprintf(
+ evpn_desc, sizeof(evpn_desc),
+ "l2vpn evpn type macip mac %s",
+ prefix_mac2str(&p->u.prefix_evpn.macip_addr.mac,
+ buf2, sizeof(buf2)));
+ } else {
+ uint8_t family = is_evpn_prefix_ipaddr_v4(
+ (struct prefix_evpn *)p) ?
+ AF_INET : AF_INET6;
+ snprintf(
+ evpn_desc, sizeof(evpn_desc),
+ "l2vpn evpn type macip mac %s ip %s",
+ prefix_mac2str(&p->u.prefix_evpn.macip_addr.mac,
+ buf2, sizeof(buf2)),
+ inet_ntop(
+ family,
+ &p->u.prefix_evpn.macip_addr.ip.ip.addr,
+ buf, PREFIX2STR_BUFFER));
+ }
+ } else if (p->u.prefix_evpn.route_type == BGP_EVPN_IMET_ROUTE) {
+ snprintfrr(evpn_desc, sizeof(evpn_desc),
+ "l2vpn evpn type multicast ip %pI4",
+ &p->u.prefix_evpn.imet_addr.ip.ipaddr_v4);
+ } else if (p->u.prefix_evpn.route_type == BGP_EVPN_IP_PREFIX_ROUTE) {
+ uint8_t family = is_evpn_prefix_ipaddr_v4(
+ (struct prefix_evpn *)p) ? AF_INET
+ : AF_INET6;
+ snprintf(evpn_desc, sizeof(evpn_desc),
+ "l2vpn evpn type prefix ip %s/%d",
+ inet_ntop(family,
+ &p->u.prefix_evpn.prefix_addr.ip.ip.addr,
+ buf, PREFIX2STR_BUFFER),
+ p->u.prefix_evpn.prefix_addr.ip_prefix_length);
+ }
+
+ vty_out(vty, "%s %s\n", desc, evpn_desc);
+}
+
+static int bgp_debug_parse_evpn_prefix(struct vty *vty, struct cmd_token **argv,
+ int argc, struct prefix **argv_pp)
+{
+ struct prefix *argv_p;
+ struct ethaddr mac = {};
+ struct ipaddr ip = {};
+ int evpn_type = 0;
+ int mac_idx = 0;
+ int ip_idx = 0;
+
+ argv_p = *argv_pp;
+
+ if (bgp_evpn_cli_parse_type(&evpn_type, argv, argc) < 0)
+ return CMD_WARNING;
+
+ if (evpn_type == BGP_EVPN_MAC_IP_ROUTE) {
+ memset(&ip, 0, sizeof(ip));
+
+ if (argv_find(argv, argc, "mac", &mac_idx))
+ if (!prefix_str2mac(argv[mac_idx + 1]->arg, &mac)) {
+ vty_out(vty, "%% Malformed MAC address\n");
+ return CMD_WARNING;
+ }
+
+ if (argv_find(argv, argc, "ip", &ip_idx))
+ if (str2ipaddr(argv[ip_idx + 1]->arg, &ip) != 0) {
+ vty_out(vty, "%% Malformed IP address\n");
+ return CMD_WARNING;
+ }
+
+ build_evpn_type2_prefix((struct prefix_evpn *)argv_p,
+ &mac, &ip);
+ } else if (evpn_type == BGP_EVPN_IMET_ROUTE) {
+ memset(&ip, 0, sizeof(ip));
+
+ if (argv_find(argv, argc, "ip", &ip_idx))
+ if (str2ipaddr(argv[ip_idx + 1]->arg, &ip) != 0) {
+ vty_out(vty, "%% Malformed IP address\n");
+ return CMD_WARNING;
+ }
+
+ build_evpn_type3_prefix((struct prefix_evpn *)argv_p,
+ ip.ipaddr_v4);
+ } else if (evpn_type == BGP_EVPN_IP_PREFIX_ROUTE) {
+ struct prefix ip_prefix;
+
+ memset(&ip_prefix, 0, sizeof(ip_prefix));
+ if (argv_find(argv, argc, "ip", &ip_idx)) {
+ (void)str2prefix(argv[ip_idx + 1]->arg, &ip_prefix);
+ apply_mask(&ip_prefix);
+ }
+ build_type5_prefix_from_ip_prefix(
+ (struct prefix_evpn *)argv_p,
+ &ip_prefix);
+ }
+
+ return CMD_SUCCESS;
+}
+
+/* Debug option setting interface. */
+unsigned long bgp_debug_option = 0;
+
+int debug(unsigned int option)
+{
+ return bgp_debug_option & option;
+}
+
+DEFUN (debug_bgp_as4,
+ debug_bgp_as4_cmd,
+ "debug bgp as4",
+ DEBUG_STR
+ BGP_STR
+ "BGP AS4 actions\n")
+{
+ if (vty->node == CONFIG_NODE)
+ DEBUG_ON(as4, AS4);
+ else {
+ TERM_DEBUG_ON(as4, AS4);
+ vty_out(vty, "BGP as4 debugging is on\n");
+ }
+ return CMD_SUCCESS;
+}
+
+DEFUN (no_debug_bgp_as4,
+ no_debug_bgp_as4_cmd,
+ "no debug bgp as4",
+ NO_STR
+ DEBUG_STR
+ BGP_STR
+ "BGP AS4 actions\n")
+{
+ if (vty->node == CONFIG_NODE)
+ DEBUG_OFF(as4, AS4);
+ else {
+ TERM_DEBUG_OFF(as4, AS4);
+ vty_out(vty, "BGP as4 debugging is off\n");
+ }
+ return CMD_SUCCESS;
+}
+
+DEFUN (debug_bgp_as4_segment,
+ debug_bgp_as4_segment_cmd,
+ "debug bgp as4 segment",
+ DEBUG_STR
+ BGP_STR
+ "BGP AS4 actions\n"
+ "BGP AS4 aspath segment handling\n")
+{
+ if (vty->node == CONFIG_NODE)
+ DEBUG_ON(as4, AS4_SEGMENT);
+ else {
+ TERM_DEBUG_ON(as4, AS4_SEGMENT);
+ vty_out(vty, "BGP as4 segment debugging is on\n");
+ }
+ return CMD_SUCCESS;
+}
+
+DEFUN (no_debug_bgp_as4_segment,
+ no_debug_bgp_as4_segment_cmd,
+ "no debug bgp as4 segment",
+ NO_STR
+ DEBUG_STR
+ BGP_STR
+ "BGP AS4 actions\n"
+ "BGP AS4 aspath segment handling\n")
+{
+ if (vty->node == CONFIG_NODE)
+ DEBUG_OFF(as4, AS4_SEGMENT);
+ else {
+ TERM_DEBUG_OFF(as4, AS4_SEGMENT);
+ vty_out(vty, "BGP as4 segment debugging is off\n");
+ }
+ return CMD_SUCCESS;
+}
+
+/* debug bgp neighbor_events */
+DEFUN (debug_bgp_neighbor_events,
+ debug_bgp_neighbor_events_cmd,
+ "debug bgp neighbor-events",
+ DEBUG_STR
+ BGP_STR
+ "BGP Neighbor Events\n")
+{
+ bgp_debug_list_free(bgp_debug_neighbor_events_peers);
+
+ if (vty->node == CONFIG_NODE)
+ DEBUG_ON(neighbor_events, NEIGHBOR_EVENTS);
+ else {
+ TERM_DEBUG_ON(neighbor_events, NEIGHBOR_EVENTS);
+ vty_out(vty, "BGP neighbor-events debugging is on\n");
+ }
+ return CMD_SUCCESS;
+}
+
+DEFUN (debug_bgp_neighbor_events_peer,
+ debug_bgp_neighbor_events_peer_cmd,
+ "debug bgp neighbor-events <A.B.C.D|X:X::X:X|WORD>",
+ DEBUG_STR
+ BGP_STR
+ "BGP Neighbor Events\n"
+ "BGP neighbor IP address to debug\n"
+ "BGP IPv6 neighbor to debug\n"
+ "BGP neighbor on interface to debug\n")
+{
+ int idx_peer = 3;
+ const char *host = argv[idx_peer]->arg;
+
+ if (!bgp_debug_neighbor_events_peers)
+ bgp_debug_neighbor_events_peers = list_new();
+
+ if (bgp_debug_list_has_entry(bgp_debug_neighbor_events_peers, host,
+ NULL)) {
+ vty_out(vty,
+ "BGP neighbor-events debugging is already enabled for %s\n",
+ host);
+ return CMD_SUCCESS;
+ }
+
+ bgp_debug_list_add_entry(bgp_debug_neighbor_events_peers, host, NULL);
+
+ if (vty->node == CONFIG_NODE)
+ DEBUG_ON(neighbor_events, NEIGHBOR_EVENTS);
+ else {
+ TERM_DEBUG_ON(neighbor_events, NEIGHBOR_EVENTS);
+ vty_out(vty, "BGP neighbor-events debugging is on for %s\n",
+ host);
+ }
+ return CMD_SUCCESS;
+}
+
+DEFUN (no_debug_bgp_neighbor_events,
+ no_debug_bgp_neighbor_events_cmd,
+ "no debug bgp neighbor-events",
+ NO_STR
+ DEBUG_STR
+ BGP_STR
+ "Neighbor Events\n")
+{
+ bgp_debug_list_free(bgp_debug_neighbor_events_peers);
+
+ if (vty->node == CONFIG_NODE)
+ DEBUG_OFF(neighbor_events, NEIGHBOR_EVENTS);
+ else {
+ TERM_DEBUG_OFF(neighbor_events, NEIGHBOR_EVENTS);
+ vty_out(vty, "BGP neighbor-events debugging is off\n");
+ }
+ return CMD_SUCCESS;
+}
+
+DEFUN (no_debug_bgp_neighbor_events_peer,
+ no_debug_bgp_neighbor_events_peer_cmd,
+ "no debug bgp neighbor-events <A.B.C.D|X:X::X:X|WORD>",
+ NO_STR
+ DEBUG_STR
+ BGP_STR
+ "Neighbor Events\n"
+ "BGP neighbor IP address to debug\n"
+ "BGP IPv6 neighbor to debug\n"
+ "BGP neighbor on interface to debug\n")
+{
+ int idx_peer = 4;
+ int found_peer = 0;
+ const char *host = argv[idx_peer]->arg;
+
+ if (bgp_debug_neighbor_events_peers
+ && !list_isempty(bgp_debug_neighbor_events_peers)) {
+ found_peer = bgp_debug_list_remove_entry(
+ bgp_debug_neighbor_events_peers, host, NULL);
+
+ if (list_isempty(bgp_debug_neighbor_events_peers)) {
+ if (vty->node == CONFIG_NODE)
+ DEBUG_OFF(neighbor_events, NEIGHBOR_EVENTS);
+ else
+ TERM_DEBUG_OFF(neighbor_events,
+ NEIGHBOR_EVENTS);
+ }
+ }
+
+ if (found_peer)
+ vty_out(vty, "BGP neighbor-events debugging is off for %s\n",
+ host);
+ else
+ vty_out(vty,
+ "BGP neighbor-events debugging was not enabled for %s\n",
+ host);
+
+ return CMD_SUCCESS;
+}
+
+/* debug bgp nht */
+DEFUN (debug_bgp_nht,
+ debug_bgp_nht_cmd,
+ "debug bgp nht",
+ DEBUG_STR
+ BGP_STR
+ "BGP nexthop tracking events\n")
+{
+ if (vty->node == CONFIG_NODE)
+ DEBUG_ON(nht, NHT);
+ else {
+ TERM_DEBUG_ON(nht, NHT);
+ vty_out(vty, "BGP nexthop tracking debugging is on\n");
+ }
+ return CMD_SUCCESS;
+}
+
+DEFUN (no_debug_bgp_nht,
+ no_debug_bgp_nht_cmd,
+ "no debug bgp nht",
+ NO_STR
+ DEBUG_STR
+ BGP_STR
+ "BGP nexthop tracking events\n")
+{
+ if (vty->node == CONFIG_NODE)
+ DEBUG_OFF(nht, NHT);
+ else {
+ TERM_DEBUG_OFF(nht, NHT);
+ vty_out(vty, "BGP nexthop tracking debugging is off\n");
+ }
+ return CMD_SUCCESS;
+}
+
+/* debug bgp keepalives */
+DEFUN (debug_bgp_keepalive,
+ debug_bgp_keepalive_cmd,
+ "debug bgp keepalives",
+ DEBUG_STR
+ BGP_STR
+ "BGP keepalives\n")
+{
+ bgp_debug_list_free(bgp_debug_keepalive_peers);
+
+ if (vty->node == CONFIG_NODE)
+ DEBUG_ON(keepalive, KEEPALIVE);
+ else {
+ TERM_DEBUG_ON(keepalive, KEEPALIVE);
+ vty_out(vty, "BGP keepalives debugging is on\n");
+ }
+ return CMD_SUCCESS;
+}
+
+DEFUN (debug_bgp_keepalive_peer,
+ debug_bgp_keepalive_peer_cmd,
+ "debug bgp keepalives <A.B.C.D|X:X::X:X|WORD>",
+ DEBUG_STR
+ BGP_STR
+ "BGP keepalives\n"
+ "BGP IPv4 neighbor to debug\n"
+ "BGP IPv6 neighbor to debug\n"
+ "BGP neighbor on interface to debug\n")
+{
+ int idx_peer = 3;
+ const char *host = argv[idx_peer]->arg;
+
+ if (!bgp_debug_keepalive_peers)
+ bgp_debug_keepalive_peers = list_new();
+
+ if (bgp_debug_list_has_entry(bgp_debug_keepalive_peers, host, NULL)) {
+ vty_out(vty,
+ "BGP keepalive debugging is already enabled for %s\n",
+ host);
+ return CMD_SUCCESS;
+ }
+
+ bgp_debug_list_add_entry(bgp_debug_keepalive_peers, host, NULL);
+
+ if (vty->node == CONFIG_NODE)
+ DEBUG_ON(keepalive, KEEPALIVE);
+ else {
+ TERM_DEBUG_ON(keepalive, KEEPALIVE);
+ vty_out(vty, "BGP keepalives debugging is on for %s\n", host);
+ }
+ return CMD_SUCCESS;
+}
+
+DEFUN (no_debug_bgp_keepalive,
+ no_debug_bgp_keepalive_cmd,
+ "no debug bgp keepalives",
+ NO_STR
+ DEBUG_STR
+ BGP_STR
+ "BGP keepalives\n")
+{
+ bgp_debug_list_free(bgp_debug_keepalive_peers);
+
+ if (vty->node == CONFIG_NODE)
+ DEBUG_OFF(keepalive, KEEPALIVE);
+ else {
+ TERM_DEBUG_OFF(keepalive, KEEPALIVE);
+ vty_out(vty, "BGP keepalives debugging is off\n");
+ }
+ return CMD_SUCCESS;
+}
+
+DEFUN (no_debug_bgp_keepalive_peer,
+ no_debug_bgp_keepalive_peer_cmd,
+ "no debug bgp keepalives <A.B.C.D|X:X::X:X|WORD>",
+ NO_STR
+ DEBUG_STR
+ BGP_STR
+ "BGP keepalives\n"
+ "BGP neighbor IP address to debug\n"
+ "BGP IPv6 neighbor to debug\n"
+ "BGP neighbor on interface to debug\n")
+{
+ int idx_peer = 4;
+ int found_peer = 0;
+ const char *host = argv[idx_peer]->arg;
+
+ if (bgp_debug_keepalive_peers
+ && !list_isempty(bgp_debug_keepalive_peers)) {
+ found_peer = bgp_debug_list_remove_entry(
+ bgp_debug_keepalive_peers, host, NULL);
+
+ if (list_isempty(bgp_debug_keepalive_peers)) {
+ if (vty->node == CONFIG_NODE)
+ DEBUG_OFF(keepalive, KEEPALIVE);
+ else
+ TERM_DEBUG_OFF(keepalive, KEEPALIVE);
+ }
+ }
+
+ if (found_peer)
+ vty_out(vty, "BGP keepalives debugging is off for %s\n", host);
+ else
+ vty_out(vty,
+ "BGP keepalives debugging was not enabled for %s\n",
+ host);
+
+ return CMD_SUCCESS;
+}
+
+/* debug bgp bestpath */
+DEFUN (debug_bgp_bestpath_prefix,
+ debug_bgp_bestpath_prefix_cmd,
+ "debug bgp bestpath <A.B.C.D/M|X:X::X:X/M>",
+ DEBUG_STR
+ BGP_STR
+ "BGP bestpath\n"
+ "IPv4 prefix\n"
+ "IPv6 prefix\n")
+{
+ struct prefix *argv_p;
+ int idx_ipv4_ipv6_prefixlen = 3;
+
+ argv_p = prefix_new();
+ (void)str2prefix(argv[idx_ipv4_ipv6_prefixlen]->arg, argv_p);
+ apply_mask(argv_p);
+
+ if (!bgp_debug_bestpath_prefixes)
+ bgp_debug_bestpath_prefixes = list_new();
+
+ if (bgp_debug_list_has_entry(bgp_debug_bestpath_prefixes, NULL,
+ argv_p)) {
+ vty_out(vty,
+ "BGP bestpath debugging is already enabled for %s\n",
+ argv[idx_ipv4_ipv6_prefixlen]->arg);
+ return CMD_SUCCESS;
+ }
+
+ bgp_debug_list_add_entry(bgp_debug_bestpath_prefixes, NULL, argv_p);
+
+ if (vty->node == CONFIG_NODE) {
+ DEBUG_ON(bestpath, BESTPATH);
+ } else {
+ TERM_DEBUG_ON(bestpath, BESTPATH);
+ vty_out(vty, "BGP bestpath debugging is on for %s\n",
+ argv[idx_ipv4_ipv6_prefixlen]->arg);
+ }
+
+ return CMD_SUCCESS;
+}
+
+DEFUN (no_debug_bgp_bestpath_prefix,
+ no_debug_bgp_bestpath_prefix_cmd,
+ "no debug bgp bestpath <A.B.C.D/M|X:X::X:X/M>",
+ NO_STR
+ DEBUG_STR
+ BGP_STR
+ "BGP bestpath\n"
+ "IPv4 prefix\n"
+ "IPv6 prefix\n")
+{
+ int idx_ipv4_ipv6_prefixlen = 4;
+ struct prefix *argv_p;
+ int found_prefix = 0;
+
+ argv_p = prefix_new();
+ (void)str2prefix(argv[idx_ipv4_ipv6_prefixlen]->arg, argv_p);
+ apply_mask(argv_p);
+
+ if (bgp_debug_bestpath_prefixes
+ && !list_isempty(bgp_debug_bestpath_prefixes)) {
+ found_prefix = bgp_debug_list_remove_entry(
+ bgp_debug_bestpath_prefixes, NULL, argv_p);
+
+ if (list_isempty(bgp_debug_bestpath_prefixes)) {
+ if (vty->node == CONFIG_NODE) {
+ DEBUG_OFF(bestpath, BESTPATH);
+ } else {
+ TERM_DEBUG_OFF(bestpath, BESTPATH);
+ vty_out(vty,
+ "BGP bestpath debugging (per prefix) is off\n");
+ }
+ }
+ }
+
+ if (found_prefix)
+ vty_out(vty, "BGP bestpath debugging is off for %s\n",
+ argv[idx_ipv4_ipv6_prefixlen]->arg);
+ else
+ vty_out(vty, "BGP bestpath debugging was not enabled for %s\n",
+ argv[idx_ipv4_ipv6_prefixlen]->arg);
+
+ return CMD_SUCCESS;
+}
+
+DEFUN (no_debug_bgp_bestpath,
+ no_debug_bgp_bestpath_cmd,
+ "no debug bgp bestpath",
+ NO_STR
+ DEBUG_STR
+ BGP_STR
+ "BGP bestpath\n")
+{
+ bgp_debug_list_free(bgp_debug_bestpath_prefixes);
+
+ if (vty->node == CONFIG_NODE)
+ DEBUG_OFF(bestpath, BESTPATH);
+ else {
+ TERM_DEBUG_OFF(bestpath, BESTPATH);
+ vty_out(vty, "BGP bestpath debugging is off\n");
+ }
+ return CMD_SUCCESS;
+}
+
+/* debug bgp updates */
+DEFUN (debug_bgp_update,
+ debug_bgp_update_cmd,
+ "debug bgp updates",
+ DEBUG_STR
+ BGP_STR
+ "BGP updates\n")
+{
+ bgp_debug_list_free(bgp_debug_update_in_peers);
+ bgp_debug_list_free(bgp_debug_update_out_peers);
+ bgp_debug_list_free(bgp_debug_update_prefixes);
+
+ if (vty->node == CONFIG_NODE) {
+ DEBUG_ON(update, UPDATE_IN);
+ DEBUG_ON(update, UPDATE_OUT);
+ } else {
+ TERM_DEBUG_ON(update, UPDATE_IN);
+ TERM_DEBUG_ON(update, UPDATE_OUT);
+ vty_out(vty, "BGP updates debugging is on\n");
+ }
+ return CMD_SUCCESS;
+}
+
+DEFUN (debug_bgp_update_direct,
+ debug_bgp_update_direct_cmd,
+ "debug bgp updates <in|out>",
+ DEBUG_STR
+ BGP_STR
+ "BGP updates\n"
+ "Inbound updates\n"
+ "Outbound updates\n")
+{
+ int idx_in_out = 3;
+
+ if (strncmp("i", argv[idx_in_out]->arg, 1) == 0)
+ bgp_debug_list_free(bgp_debug_update_in_peers);
+ else
+ bgp_debug_list_free(bgp_debug_update_out_peers);
+
+ if (vty->node == CONFIG_NODE) {
+ if (strncmp("i", argv[idx_in_out]->arg, 1) == 0)
+ DEBUG_ON(update, UPDATE_IN);
+ else
+ DEBUG_ON(update, UPDATE_OUT);
+ } else {
+ if (strncmp("i", argv[idx_in_out]->arg, 1) == 0) {
+ TERM_DEBUG_ON(update, UPDATE_IN);
+ vty_out(vty, "BGP updates debugging is on (inbound)\n");
+ } else {
+ TERM_DEBUG_ON(update, UPDATE_OUT);
+ vty_out(vty,
+ "BGP updates debugging is on (outbound)\n");
+ }
+ }
+ return CMD_SUCCESS;
+}
+
+DEFUN (debug_bgp_update_direct_peer,
+ debug_bgp_update_direct_peer_cmd,
+ "debug bgp updates <in|out> <A.B.C.D|X:X::X:X|WORD>",
+ DEBUG_STR
+ BGP_STR
+ "BGP updates\n"
+ "Inbound updates\n"
+ "Outbound updates\n"
+ "BGP neighbor IP address to debug\n"
+ "BGP IPv6 neighbor to debug\n"
+ "BGP neighbor on interface to debug\n")
+{
+ int idx_in_out = 3;
+ int idx_peer = 4;
+ const char *host = argv[idx_peer]->arg;
+ int inbound;
+
+ if (!bgp_debug_update_in_peers)
+ bgp_debug_update_in_peers = list_new();
+
+ if (!bgp_debug_update_out_peers)
+ bgp_debug_update_out_peers = list_new();
+
+ if (strncmp("i", argv[idx_in_out]->arg, 1) == 0)
+ inbound = 1;
+ else
+ inbound = 0;
+
+ if (inbound) {
+ if (bgp_debug_list_has_entry(bgp_debug_update_in_peers, host,
+ NULL)) {
+ vty_out(vty,
+ "BGP inbound update debugging is already enabled for %s\n",
+ host);
+ return CMD_SUCCESS;
+ }
+ }
+
+ else {
+ if (bgp_debug_list_has_entry(bgp_debug_update_out_peers, host,
+ NULL)) {
+ vty_out(vty,
+ "BGP outbound update debugging is already enabled for %s\n",
+ host);
+ return CMD_SUCCESS;
+ }
+ }
+
+ if (inbound)
+ bgp_debug_list_add_entry(bgp_debug_update_in_peers, host, NULL);
+ else {
+ struct peer *peer;
+ struct peer_af *paf;
+ int afidx;
+
+ bgp_debug_list_add_entry(bgp_debug_update_out_peers, host,
+ NULL);
+ peer = bgp_find_peer(vty, host);
+
+ if (peer) {
+ for (afidx = BGP_AF_START; afidx < BGP_AF_MAX;
+ afidx++) {
+ paf = peer->peer_af_array[afidx];
+ if (paf != NULL) {
+ if (PAF_SUBGRP(paf)) {
+ UPDGRP_PEER_DBG_EN(
+ PAF_SUBGRP(paf)
+ ->update_group);
+ }
+ }
+ }
+ }
+ }
+
+ if (vty->node == CONFIG_NODE) {
+ if (inbound)
+ DEBUG_ON(update, UPDATE_IN);
+ else
+ DEBUG_ON(update, UPDATE_OUT);
+ } else {
+ if (inbound) {
+ TERM_DEBUG_ON(update, UPDATE_IN);
+ vty_out(vty,
+ "BGP updates debugging is on (inbound) for %s\n",
+ argv[idx_peer]->arg);
+ } else {
+ TERM_DEBUG_ON(update, UPDATE_OUT);
+ vty_out(vty,
+ "BGP updates debugging is on (outbound) for %s\n",
+ argv[idx_peer]->arg);
+ }
+ }
+ return CMD_SUCCESS;
+}
+
+DEFUN (no_debug_bgp_update_direct,
+ no_debug_bgp_update_direct_cmd,
+ "no debug bgp updates <in|out>",
+ NO_STR
+ DEBUG_STR
+ BGP_STR
+ "BGP updates\n"
+ "Inbound updates\n"
+ "Outbound updates\n")
+{
+ int idx_in_out = 4;
+ if (strncmp("i", argv[idx_in_out]->arg, 1) == 0) {
+ bgp_debug_list_free(bgp_debug_update_in_peers);
+
+ if (vty->node == CONFIG_NODE) {
+ DEBUG_OFF(update, UPDATE_IN);
+ } else {
+ TERM_DEBUG_OFF(update, UPDATE_IN);
+ vty_out(vty,
+ "BGP updates debugging is off (inbound)\n");
+ }
+ } else {
+ bgp_debug_list_free(bgp_debug_update_out_peers);
+
+ if (vty->node == CONFIG_NODE) {
+ DEBUG_OFF(update, UPDATE_OUT);
+ } else {
+ TERM_DEBUG_OFF(update, UPDATE_OUT);
+ vty_out(vty,
+ "BGP updates debugging is off (outbound)\n");
+ }
+ }
+
+ return CMD_SUCCESS;
+}
+
+DEFUN (no_debug_bgp_update_direct_peer,
+ no_debug_bgp_update_direct_peer_cmd,
+ "no debug bgp updates <in|out> <A.B.C.D|X:X::X:X|WORD>",
+ NO_STR
+ DEBUG_STR
+ BGP_STR
+ "BGP updates\n"
+ "Inbound updates\n"
+ "Outbound updates\n"
+ "BGP neighbor IP address to debug\n"
+ "BGP IPv6 neighbor to debug\n"
+ "BGP neighbor on interface to debug\n")
+{
+ int idx_in_out = 4;
+ int idx_peer = 5;
+ int inbound;
+ int found_peer = 0;
+ const char *host = argv[idx_peer]->arg;
+
+ if (strncmp("i", argv[idx_in_out]->arg, 1) == 0)
+ inbound = 1;
+ else
+ inbound = 0;
+
+ if (inbound && bgp_debug_update_in_peers
+ && !list_isempty(bgp_debug_update_in_peers)) {
+ found_peer = bgp_debug_list_remove_entry(
+ bgp_debug_update_in_peers, host, NULL);
+
+ if (list_isempty(bgp_debug_update_in_peers)) {
+ if (vty->node == CONFIG_NODE)
+ DEBUG_OFF(update, UPDATE_IN);
+ else {
+ TERM_DEBUG_OFF(update, UPDATE_IN);
+ vty_out(vty,
+ "BGP updates debugging (inbound) is off\n");
+ }
+ }
+ }
+
+ if (!inbound && bgp_debug_update_out_peers
+ && !list_isempty(bgp_debug_update_out_peers)) {
+ found_peer = bgp_debug_list_remove_entry(
+ bgp_debug_update_out_peers, host, NULL);
+
+ if (list_isempty(bgp_debug_update_out_peers)) {
+ if (vty->node == CONFIG_NODE)
+ DEBUG_OFF(update, UPDATE_OUT);
+ else {
+ TERM_DEBUG_OFF(update, UPDATE_OUT);
+ vty_out(vty,
+ "BGP updates debugging (outbound) is off\n");
+ }
+ }
+
+ struct peer *peer;
+ struct peer_af *paf;
+ int afidx;
+ peer = bgp_find_peer(vty, host);
+
+ if (peer) {
+ for (afidx = BGP_AF_START; afidx < BGP_AF_MAX;
+ afidx++) {
+ paf = peer->peer_af_array[afidx];
+ if (paf != NULL) {
+ if (PAF_SUBGRP(paf)) {
+ UPDGRP_PEER_DBG_DIS(
+ PAF_SUBGRP(paf)
+ ->update_group);
+ }
+ }
+ }
+ }
+ }
+
+ if (found_peer)
+ if (inbound)
+ vty_out(vty,
+ "BGP updates debugging (inbound) is off for %s\n",
+ host);
+ else
+ vty_out(vty,
+ "BGP updates debugging (outbound) is off for %s\n",
+ host);
+ else if (inbound)
+ vty_out(vty,
+ "BGP updates debugging (inbound) was not enabled for %s\n",
+ host);
+ else
+ vty_out(vty,
+ "BGP updates debugging (outbound) was not enabled for %s\n",
+ host);
+
+ return CMD_SUCCESS;
+}
+
+#ifndef VTYSH_EXTRACT_PL
+#include "bgpd/bgp_debug_clippy.c"
+#endif
+
+DEFPY (debug_bgp_update_prefix_afi_safi,
+ debug_bgp_update_prefix_afi_safi_cmd,
+ "debug bgp updates prefix l2vpn$afi evpn$safi type <<macip|2> mac <X:X:X:X:X:X|X:X:X:X:X:X/M> [ip <A.B.C.D|X:X::X:X>]|<multicast|3> ip <A.B.C.D|X:X::X:X>|<prefix|5> ip <A.B.C.D/M|X:X::X:X/M>>",
+ DEBUG_STR
+ BGP_STR
+ "BGP updates\n"
+ "Specify a prefix to debug\n"
+ L2VPN_HELP_STR
+ EVPN_HELP_STR
+ EVPN_TYPE_HELP_STR
+ EVPN_TYPE_2_HELP_STR
+ EVPN_TYPE_2_HELP_STR
+ MAC_STR MAC_STR MAC_STR
+ IP_STR
+ "IPv4 address\n"
+ "IPv6 address\n"
+ EVPN_TYPE_3_HELP_STR
+ EVPN_TYPE_3_HELP_STR
+ IP_STR
+ "IPv4 address\n"
+ "IPv6 address\n"
+ EVPN_TYPE_5_HELP_STR
+ EVPN_TYPE_5_HELP_STR
+ IP_STR
+ "IPv4 prefix\n"
+ "IPv6 prefix\n")
+{
+ struct prefix *argv_p;
+ int ret = CMD_SUCCESS;
+
+ argv_p = prefix_new();
+
+ ret = bgp_debug_parse_evpn_prefix(vty, argv, argc, &argv_p);
+ if (ret != CMD_SUCCESS) {
+ prefix_free(&argv_p);
+ return ret;
+ }
+
+ if (!bgp_debug_update_prefixes)
+ bgp_debug_update_prefixes = list_new();
+
+ if (bgp_debug_list_has_entry(bgp_debug_update_prefixes, NULL, argv_p)) {
+ vty_out(vty,
+ "BGP updates debugging is already enabled for %pFX\n",
+ argv_p);
+ prefix_free(&argv_p);
+ return CMD_SUCCESS;
+ }
+
+ bgp_debug_list_add_entry(bgp_debug_update_prefixes, NULL, argv_p);
+
+ if (vty->node == CONFIG_NODE) {
+ DEBUG_ON(update, UPDATE_PREFIX);
+ } else {
+ TERM_DEBUG_ON(update, UPDATE_PREFIX);
+ vty_out(vty, "BGP updates debugging is on for %pFX\n", argv_p);
+ }
+
+ prefix_free(&argv_p);
+
+ return CMD_SUCCESS;
+}
+
+DEFPY (no_debug_bgp_update_prefix_afi_safi,
+ no_debug_bgp_update_prefix_afi_safi_cmd,
+ "no debug bgp updates prefix l2vpn$afi evpn$safi type <<macip|2> mac <X:X:X:X:X:X|X:X:X:X:X:X/M> [ip <A.B.C.D|X:X::X:X>]|<multicast|3> ip <A.B.C.D|X:X::X:X>|<prefix|5> ip <A.B.C.D/M|X:X::X:X/M>>",
+ NO_STR
+ DEBUG_STR
+ BGP_STR
+ "BGP updates\n"
+ "Specify a prefix to debug\n"
+ L2VPN_HELP_STR
+ EVPN_HELP_STR
+ EVPN_TYPE_HELP_STR
+ EVPN_TYPE_2_HELP_STR
+ EVPN_TYPE_2_HELP_STR
+ MAC_STR MAC_STR MAC_STR
+ IP_STR
+ "IPv4 address\n"
+ "IPv6 address\n"
+ EVPN_TYPE_3_HELP_STR
+ EVPN_TYPE_3_HELP_STR
+ IP_STR
+ "IPv4 address\n"
+ "IPv6 address\n"
+ EVPN_TYPE_5_HELP_STR
+ EVPN_TYPE_5_HELP_STR
+ IP_STR
+ "IPv4 prefix\n"
+ "IPv6 prefix\n")
+{
+ struct prefix *argv_p;
+ bool found_prefix = false;
+ int ret = CMD_SUCCESS;
+
+ argv_p = prefix_new();
+
+ ret = bgp_debug_parse_evpn_prefix(vty, argv, argc, &argv_p);
+ if (ret != CMD_SUCCESS) {
+ prefix_free(&argv_p);
+ return ret;
+ }
+
+ if (bgp_debug_update_prefixes
+ && !list_isempty(bgp_debug_update_prefixes)) {
+ found_prefix = bgp_debug_list_remove_entry(
+ bgp_debug_update_prefixes, NULL, argv_p);
+
+ if (list_isempty(bgp_debug_update_prefixes)) {
+ if (vty->node == CONFIG_NODE) {
+ DEBUG_OFF(update, UPDATE_PREFIX);
+ } else {
+ TERM_DEBUG_OFF(update, UPDATE_PREFIX);
+ vty_out(vty,
+ "BGP updates debugging (per prefix) is off\n");
+ }
+ }
+ }
+
+ if (found_prefix)
+ vty_out(vty, "BGP updates debugging is off for %pFX\n", argv_p);
+ else
+ vty_out(vty, "BGP updates debugging was not enabled for %pFX\n",
+ argv_p);
+
+ prefix_free(&argv_p);
+
+ return ret;
+}
+
+
+DEFUN (debug_bgp_update_prefix,
+ debug_bgp_update_prefix_cmd,
+ "debug bgp updates prefix <A.B.C.D/M|X:X::X:X/M>",
+ DEBUG_STR
+ BGP_STR
+ "BGP updates\n"
+ "Specify a prefix to debug\n"
+ "IPv4 prefix\n"
+ "IPv6 prefix\n")
+{
+ int idx_ipv4_ipv6_prefixlen = 4;
+ struct prefix *argv_p;
+
+ argv_p = prefix_new();
+ (void)str2prefix(argv[idx_ipv4_ipv6_prefixlen]->arg, argv_p);
+ apply_mask(argv_p);
+
+ if (!bgp_debug_update_prefixes)
+ bgp_debug_update_prefixes = list_new();
+
+ if (bgp_debug_list_has_entry(bgp_debug_update_prefixes, NULL, argv_p)) {
+ vty_out(vty,
+ "BGP updates debugging is already enabled for %s\n",
+ argv[idx_ipv4_ipv6_prefixlen]->arg);
+ return CMD_SUCCESS;
+ }
+
+ bgp_debug_list_add_entry(bgp_debug_update_prefixes, NULL, argv_p);
+
+ if (vty->node == CONFIG_NODE) {
+ DEBUG_ON(update, UPDATE_PREFIX);
+ } else {
+ TERM_DEBUG_ON(update, UPDATE_PREFIX);
+ vty_out(vty, "BGP updates debugging is on for %s\n",
+ argv[idx_ipv4_ipv6_prefixlen]->arg);
+ }
+
+ return CMD_SUCCESS;
+}
+
+DEFUN (no_debug_bgp_update_prefix,
+ no_debug_bgp_update_prefix_cmd,
+ "no debug bgp updates prefix <A.B.C.D/M|X:X::X:X/M>",
+ NO_STR
+ DEBUG_STR
+ BGP_STR
+ "BGP updates\n"
+ "Specify a prefix to debug\n"
+ "IPv4 prefix\n"
+ "IPv6 prefix\n")
+{
+ int idx_ipv4_ipv6_prefixlen = 5;
+ struct prefix *argv_p;
+ int found_prefix = 0;
+
+ argv_p = prefix_new();
+ (void)str2prefix(argv[idx_ipv4_ipv6_prefixlen]->arg, argv_p);
+ apply_mask(argv_p);
+
+ if (bgp_debug_update_prefixes
+ && !list_isempty(bgp_debug_update_prefixes)) {
+ found_prefix = bgp_debug_list_remove_entry(
+ bgp_debug_update_prefixes, NULL, argv_p);
+
+ if (list_isempty(bgp_debug_update_prefixes)) {
+ if (vty->node == CONFIG_NODE) {
+ DEBUG_OFF(update, UPDATE_PREFIX);
+ } else {
+ TERM_DEBUG_OFF(update, UPDATE_PREFIX);
+ vty_out(vty,
+ "BGP updates debugging (per prefix) is off\n");
+ }
+ }
+ }
+
+ if (found_prefix)
+ vty_out(vty, "BGP updates debugging is off for %s\n",
+ argv[idx_ipv4_ipv6_prefixlen]->arg);
+ else
+ vty_out(vty, "BGP updates debugging was not enabled for %s\n",
+ argv[idx_ipv4_ipv6_prefixlen]->arg);
+
+ return CMD_SUCCESS;
+}
+
+DEFUN (no_debug_bgp_update,
+ no_debug_bgp_update_cmd,
+ "no debug bgp updates",
+ NO_STR
+ DEBUG_STR
+ BGP_STR
+ "BGP updates\n")
+{
+ struct listnode *ln;
+ struct bgp *bgp;
+
+ bgp_debug_list_free(bgp_debug_update_in_peers);
+ bgp_debug_list_free(bgp_debug_update_out_peers);
+ bgp_debug_list_free(bgp_debug_update_prefixes);
+
+ for (ALL_LIST_ELEMENTS_RO(bm->bgp, ln, bgp))
+ bgp_debug_clear_updgrp_update_dbg(bgp);
+
+ if (vty->node == CONFIG_NODE) {
+ DEBUG_OFF(update, UPDATE_IN);
+ DEBUG_OFF(update, UPDATE_OUT);
+ DEBUG_OFF(update, UPDATE_PREFIX);
+ } else {
+ TERM_DEBUG_OFF(update, UPDATE_IN);
+ TERM_DEBUG_OFF(update, UPDATE_OUT);
+ TERM_DEBUG_OFF(update, UPDATE_PREFIX);
+ vty_out(vty, "BGP updates debugging is off\n");
+ }
+ return CMD_SUCCESS;
+}
+
+/* debug bgp zebra */
+DEFUN (debug_bgp_zebra,
+ debug_bgp_zebra_cmd,
+ "debug bgp zebra",
+ DEBUG_STR
+ BGP_STR
+ "BGP Zebra messages\n")
+{
+ if (vty->node == CONFIG_NODE)
+ DEBUG_ON(zebra, ZEBRA);
+ else {
+ TERM_DEBUG_ON(zebra, ZEBRA);
+ vty_out(vty, "BGP zebra debugging is on\n");
+ }
+ return CMD_SUCCESS;
+}
+
+DEFUN (debug_bgp_graceful_restart,
+ debug_bgp_graceful_restart_cmd,
+ "debug bgp graceful-restart",
+ DEBUG_STR
+ BGP_STR
+ GR_DEBUG)
+{
+ if (vty->node == CONFIG_NODE) {
+ DEBUG_ON(graceful_restart, GRACEFUL_RESTART);
+ } else {
+ TERM_DEBUG_ON(graceful_restart, GRACEFUL_RESTART);
+ vty_out(vty, "BGP Graceful Restart debugging is on\n");
+ }
+ return CMD_SUCCESS;
+}
+
+
+DEFUN (debug_bgp_zebra_prefix,
+ debug_bgp_zebra_prefix_cmd,
+ "debug bgp zebra prefix <A.B.C.D/M|X:X::X:X/M>",
+ DEBUG_STR
+ BGP_STR
+ "BGP Zebra messages\n"
+ "Specify a prefix to debug\n"
+ "IPv4 prefix\n"
+ "IPv6 prefix\n")
+{
+ int idx_ipv4_ipv6_prefixlen = 4;
+ struct prefix *argv_p;
+
+ argv_p = prefix_new();
+ (void)str2prefix(argv[idx_ipv4_ipv6_prefixlen]->arg, argv_p);
+ apply_mask(argv_p);
+
+ if (!bgp_debug_zebra_prefixes)
+ bgp_debug_zebra_prefixes = list_new();
+
+ if (bgp_debug_list_has_entry(bgp_debug_zebra_prefixes, NULL, argv_p)) {
+ vty_out(vty, "BGP zebra debugging is already enabled for %s\n",
+ argv[idx_ipv4_ipv6_prefixlen]->arg);
+ return CMD_SUCCESS;
+ }
+
+ bgp_debug_list_add_entry(bgp_debug_zebra_prefixes, NULL, argv_p);
+
+ if (vty->node == CONFIG_NODE)
+ DEBUG_ON(zebra, ZEBRA);
+ else {
+ TERM_DEBUG_ON(zebra, ZEBRA);
+ vty_out(vty, "BGP zebra debugging is on for %s\n",
+ argv[idx_ipv4_ipv6_prefixlen]->arg);
+ }
+
+ return CMD_SUCCESS;
+}
+
+DEFUN (no_debug_bgp_zebra,
+ no_debug_bgp_zebra_cmd,
+ "no debug bgp zebra",
+ NO_STR
+ DEBUG_STR
+ BGP_STR
+ "BGP Zebra messages\n")
+{
+ bgp_debug_list_free(bgp_debug_zebra_prefixes);
+
+ if (vty->node == CONFIG_NODE)
+ DEBUG_OFF(zebra, ZEBRA);
+ else {
+ TERM_DEBUG_OFF(zebra, ZEBRA);
+ vty_out(vty, "BGP zebra debugging is off\n");
+ }
+ return CMD_SUCCESS;
+}
+
+DEFUN (no_debug_bgp_graceful_restart,
+ no_debug_bgp_graceful_restart_cmd,
+ "no debug bgp graceful-restart",
+ DEBUG_STR
+ BGP_STR
+ GR_DEBUG
+ NO_STR)
+{
+ if (vty->node == CONFIG_NODE) {
+ DEBUG_OFF(graceful_restart, GRACEFUL_RESTART);
+ } else {
+ TERM_DEBUG_OFF(graceful_restart, GRACEFUL_RESTART);
+ vty_out(vty, "BGP Graceful Restart debugging is off\n");
+ }
+ return CMD_SUCCESS;
+}
+
+DEFUN (no_debug_bgp_zebra_prefix,
+ no_debug_bgp_zebra_prefix_cmd,
+ "no debug bgp zebra prefix <A.B.C.D/M|X:X::X:X/M>",
+ NO_STR
+ DEBUG_STR
+ BGP_STR
+ "BGP Zebra messages\n"
+ "Specify a prefix to debug\n"
+ "IPv4 prefix\n"
+ "IPv6 prefix\n")
+{
+ int idx_ipv4_ipv6_prefixlen = 5;
+ struct prefix *argv_p;
+ int found_prefix = 0;
+
+ argv_p = prefix_new();
+ (void)str2prefix(argv[idx_ipv4_ipv6_prefixlen]->arg, argv_p);
+ apply_mask(argv_p);
+
+ if (bgp_debug_zebra_prefixes
+ && !list_isempty(bgp_debug_zebra_prefixes)) {
+ found_prefix = bgp_debug_list_remove_entry(
+ bgp_debug_zebra_prefixes, NULL, argv_p);
+
+ if (list_isempty(bgp_debug_zebra_prefixes)) {
+ if (vty->node == CONFIG_NODE)
+ DEBUG_OFF(zebra, ZEBRA);
+ else {
+ TERM_DEBUG_OFF(zebra, ZEBRA);
+ vty_out(vty, "BGP zebra debugging is off\n");
+ }
+ }
+ }
+
+ if (found_prefix)
+ vty_out(vty, "BGP zebra debugging is off for %s\n",
+ argv[idx_ipv4_ipv6_prefixlen]->arg);
+ else
+ vty_out(vty, "BGP zebra debugging was not enabled for %s\n",
+ argv[idx_ipv4_ipv6_prefixlen]->arg);
+
+ return CMD_SUCCESS;
+}
+
+/* debug bgp update-groups */
+DEFUN (debug_bgp_update_groups,
+ debug_bgp_update_groups_cmd,
+ "debug bgp update-groups",
+ DEBUG_STR
+ BGP_STR
+ "BGP update-groups\n")
+{
+ if (vty->node == CONFIG_NODE)
+ DEBUG_ON(update_groups, UPDATE_GROUPS);
+ else {
+ TERM_DEBUG_ON(update_groups, UPDATE_GROUPS);
+ vty_out(vty, "BGP update-groups debugging is on\n");
+ }
+ return CMD_SUCCESS;
+}
+
+DEFUN (no_debug_bgp_update_groups,
+ no_debug_bgp_update_groups_cmd,
+ "no debug bgp update-groups",
+ NO_STR
+ DEBUG_STR
+ BGP_STR
+ "BGP update-groups\n")
+{
+ if (vty->node == CONFIG_NODE)
+ DEBUG_OFF(update_groups, UPDATE_GROUPS);
+ else {
+ TERM_DEBUG_OFF(update_groups, UPDATE_GROUPS);
+ vty_out(vty, "BGP update-groups debugging is off\n");
+ }
+ return CMD_SUCCESS;
+}
+
+DEFUN (debug_bgp_vpn,
+ debug_bgp_vpn_cmd,
+ "debug bgp vpn <leak-from-vrf|leak-to-vrf|rmap-event|label>",
+ DEBUG_STR
+ BGP_STR
+ "VPN routes\n"
+ "leaked from vrf to vpn\n"
+ "leaked to vrf from vpn\n"
+ "route-map updates\n"
+ "labels\n")
+{
+ int idx = 3;
+
+ if (argv_find(argv, argc, "leak-from-vrf", &idx)) {
+ if (vty->node == CONFIG_NODE)
+ DEBUG_ON(vpn, VPN_LEAK_FROM_VRF);
+ else
+ TERM_DEBUG_ON(vpn, VPN_LEAK_FROM_VRF);
+ } else if (argv_find(argv, argc, "leak-to-vrf", &idx)) {
+ if (vty->node == CONFIG_NODE)
+ DEBUG_ON(vpn, VPN_LEAK_TO_VRF);
+ else
+ TERM_DEBUG_ON(vpn, VPN_LEAK_TO_VRF);
+ } else if (argv_find(argv, argc, "rmap-event", &idx)) {
+ if (vty->node == CONFIG_NODE)
+ DEBUG_ON(vpn, VPN_LEAK_RMAP_EVENT);
+ else
+ TERM_DEBUG_ON(vpn, VPN_LEAK_RMAP_EVENT);
+ } else if (argv_find(argv, argc, "label", &idx)) {
+ if (vty->node == CONFIG_NODE)
+ DEBUG_ON(vpn, VPN_LEAK_LABEL);
+ else
+ TERM_DEBUG_ON(vpn, VPN_LEAK_LABEL);
+ } else {
+ vty_out(vty, "%% unknown debug bgp vpn keyword\n");
+ return CMD_WARNING_CONFIG_FAILED;
+ }
+
+ if (vty->node != CONFIG_NODE)
+ vty_out(vty, "enabled debug bgp vpn %s\n", argv[idx]->text);
+
+ return CMD_SUCCESS;
+}
+
+DEFUN (no_debug_bgp_vpn,
+ no_debug_bgp_vpn_cmd,
+ "no debug bgp vpn <leak-from-vrf|leak-to-vrf|rmap-event|label>",
+ NO_STR
+ DEBUG_STR
+ BGP_STR
+ "VPN routes\n"
+ "leaked from vrf to vpn\n"
+ "leaked to vrf from vpn\n"
+ "route-map updates\n"
+ "labels\n")
+{
+ int idx = 4;
+
+ if (argv_find(argv, argc, "leak-from-vrf", &idx)) {
+ if (vty->node == CONFIG_NODE)
+ DEBUG_OFF(vpn, VPN_LEAK_FROM_VRF);
+ else
+ TERM_DEBUG_OFF(vpn, VPN_LEAK_FROM_VRF);
+
+ } else if (argv_find(argv, argc, "leak-to-vrf", &idx)) {
+ if (vty->node == CONFIG_NODE)
+ DEBUG_OFF(vpn, VPN_LEAK_TO_VRF);
+ else
+ TERM_DEBUG_OFF(vpn, VPN_LEAK_TO_VRF);
+ } else if (argv_find(argv, argc, "rmap-event", &idx)) {
+ if (vty->node == CONFIG_NODE)
+ DEBUG_OFF(vpn, VPN_LEAK_RMAP_EVENT);
+ else
+ TERM_DEBUG_OFF(vpn, VPN_LEAK_RMAP_EVENT);
+ } else if (argv_find(argv, argc, "label", &idx)) {
+ if (vty->node == CONFIG_NODE)
+ DEBUG_OFF(vpn, VPN_LEAK_LABEL);
+ else
+ TERM_DEBUG_OFF(vpn, VPN_LEAK_LABEL);
+ } else {
+ vty_out(vty, "%% unknown debug bgp vpn keyword\n");
+ return CMD_WARNING_CONFIG_FAILED;
+ }
+
+ if (vty->node != CONFIG_NODE)
+ vty_out(vty, "disabled debug bgp vpn %s\n", argv[idx]->text);
+ return CMD_SUCCESS;
+}
+
+/* debug bgp pbr */
+DEFUN (debug_bgp_pbr,
+ debug_bgp_pbr_cmd,
+ "debug bgp pbr [error]",
+ DEBUG_STR
+ BGP_STR
+ "BGP policy based routing\n"
+ "BGP PBR error\n")
+{
+ int idx = 3;
+
+ if (argv_find(argv, argc, "error", &idx)) {
+ if (vty->node == CONFIG_NODE)
+ DEBUG_ON(pbr, PBR_ERROR);
+ else {
+ TERM_DEBUG_ON(pbr, PBR_ERROR);
+ vty_out(vty, "BGP policy based routing error is on\n");
+ }
+ return CMD_SUCCESS;
+ }
+ if (vty->node == CONFIG_NODE)
+ DEBUG_ON(pbr, PBR);
+ else {
+ TERM_DEBUG_ON(pbr, PBR);
+ vty_out(vty, "BGP policy based routing is on\n");
+ }
+ return CMD_SUCCESS;
+}
+
+DEFUN (no_debug_bgp_pbr,
+ no_debug_bgp_pbr_cmd,
+ "no debug bgp pbr [error]",
+ NO_STR
+ DEBUG_STR
+ BGP_STR
+ "BGP policy based routing\n"
+ "BGP PBR Error\n")
+{
+ int idx = 3;
+
+ if (argv_find(argv, argc, "error", &idx)) {
+ if (vty->node == CONFIG_NODE)
+ DEBUG_OFF(pbr, PBR_ERROR);
+ else {
+ TERM_DEBUG_OFF(pbr, PBR_ERROR);
+ vty_out(vty, "BGP policy based routing error is off\n");
+ }
+ return CMD_SUCCESS;
+ }
+ if (vty->node == CONFIG_NODE)
+ DEBUG_OFF(pbr, PBR);
+ else {
+ TERM_DEBUG_OFF(pbr, PBR);
+ vty_out(vty, "BGP policy based routing is off\n");
+ }
+ return CMD_SUCCESS;
+}
+
+DEFPY (debug_bgp_evpn_mh,
+ debug_bgp_evpn_mh_cmd,
+ "[no$no] debug bgp evpn mh <es$es|route$rt>",
+ NO_STR
+ DEBUG_STR
+ BGP_STR
+ "EVPN\n"
+ "Multihoming\n"
+ "Ethernet Segment debugging\n"
+ "Route debugging\n")
+{
+ if (es) {
+ if (vty->node == CONFIG_NODE) {
+ if (no)
+ DEBUG_OFF(evpn_mh, EVPN_MH_ES);
+ else
+ DEBUG_ON(evpn_mh, EVPN_MH_ES);
+ } else {
+ if (no) {
+ TERM_DEBUG_OFF(evpn_mh, EVPN_MH_ES);
+ vty_out(vty,
+ "BGP EVPN-MH ES debugging is off\n");
+ } else {
+ TERM_DEBUG_ON(evpn_mh, EVPN_MH_ES);
+ vty_out(vty,
+ "BGP EVPN-MH ES debugging is on\n");
+ }
+ }
+ }
+ if (rt) {
+ if (vty->node == CONFIG_NODE) {
+ if (no)
+ DEBUG_OFF(evpn_mh, EVPN_MH_RT);
+ else
+ DEBUG_ON(evpn_mh, EVPN_MH_RT);
+ } else {
+ if (no) {
+ TERM_DEBUG_OFF(evpn_mh, EVPN_MH_RT);
+ vty_out(vty,
+ "BGP EVPN-MH route debugging is off\n");
+ } else {
+ TERM_DEBUG_ON(evpn_mh, EVPN_MH_RT);
+ vty_out(vty,
+ "BGP EVPN-MH route debugging is on\n");
+ }
+ }
+ }
+
+ return CMD_SUCCESS;
+}
+
+DEFUN (debug_bgp_labelpool,
+ debug_bgp_labelpool_cmd,
+ "debug bgp labelpool",
+ DEBUG_STR
+ BGP_STR
+ "label pool\n")
+{
+ if (vty->node == CONFIG_NODE)
+ DEBUG_ON(labelpool, LABELPOOL);
+ else
+ TERM_DEBUG_ON(labelpool, LABELPOOL);
+
+ if (vty->node != CONFIG_NODE)
+ vty_out(vty, "enabled debug bgp labelpool\n");
+
+ return CMD_SUCCESS;
+}
+
+DEFUN (no_debug_bgp_labelpool,
+ no_debug_bgp_labelpool_cmd,
+ "no debug bgp labelpool",
+ NO_STR
+ DEBUG_STR
+ BGP_STR
+ "label pool\n")
+{
+ if (vty->node == CONFIG_NODE)
+ DEBUG_OFF(labelpool, LABELPOOL);
+ else
+ TERM_DEBUG_OFF(labelpool, LABELPOOL);
+
+
+ if (vty->node != CONFIG_NODE)
+ vty_out(vty, "disabled debug bgp labelpool\n");
+
+ return CMD_SUCCESS;
+}
+
+DEFPY(debug_bgp_bfd, debug_bgp_bfd_cmd,
+ "[no] debug bgp bfd",
+ NO_STR
+ DEBUG_STR
+ BGP_STR
+ "Bidirection Forwarding Detection\n")
+{
+ if (vty->node == CONFIG_NODE) {
+ if (no) {
+ DEBUG_OFF(bfd, BFD_LIB);
+ bfd_protocol_integration_set_debug(false);
+ } else {
+ DEBUG_ON(bfd, BFD_LIB);
+ bfd_protocol_integration_set_debug(true);
+ }
+ } else {
+ if (no) {
+ TERM_DEBUG_OFF(bfd, BFD_LIB);
+ bfd_protocol_integration_set_debug(false);
+ } else {
+ TERM_DEBUG_ON(bfd, BFD_LIB);
+ bfd_protocol_integration_set_debug(true);
+ }
+ }
+
+ return CMD_SUCCESS;
+}
+
+DEFUN (no_debug_bgp,
+ no_debug_bgp_cmd,
+ "no debug bgp",
+ NO_STR
+ DEBUG_STR
+ BGP_STR)
+{
+ struct bgp *bgp;
+ struct listnode *ln;
+
+ bgp_debug_list_free(bgp_debug_neighbor_events_peers);
+ bgp_debug_list_free(bgp_debug_keepalive_peers);
+ bgp_debug_list_free(bgp_debug_update_in_peers);
+ bgp_debug_list_free(bgp_debug_update_out_peers);
+ bgp_debug_list_free(bgp_debug_update_prefixes);
+ bgp_debug_list_free(bgp_debug_bestpath_prefixes);
+ bgp_debug_list_free(bgp_debug_zebra_prefixes);
+
+ for (ALL_LIST_ELEMENTS_RO(bm->bgp, ln, bgp))
+ bgp_debug_clear_updgrp_update_dbg(bgp);
+
+ TERM_DEBUG_OFF(keepalive, KEEPALIVE);
+ TERM_DEBUG_OFF(update, UPDATE_IN);
+ TERM_DEBUG_OFF(update, UPDATE_OUT);
+ TERM_DEBUG_OFF(update, UPDATE_PREFIX);
+ TERM_DEBUG_OFF(bestpath, BESTPATH);
+ TERM_DEBUG_OFF(as4, AS4);
+ TERM_DEBUG_OFF(as4, AS4_SEGMENT);
+ TERM_DEBUG_OFF(neighbor_events, NEIGHBOR_EVENTS);
+ TERM_DEBUG_OFF(zebra, ZEBRA);
+ TERM_DEBUG_OFF(allow_martians, ALLOW_MARTIANS);
+ TERM_DEBUG_OFF(nht, NHT);
+ TERM_DEBUG_OFF(vpn, VPN_LEAK_FROM_VRF);
+ TERM_DEBUG_OFF(vpn, VPN_LEAK_TO_VRF);
+ TERM_DEBUG_OFF(vpn, VPN_LEAK_RMAP_EVENT);
+ TERM_DEBUG_OFF(vpn, VPN_LEAK_LABEL);
+ TERM_DEBUG_OFF(flowspec, FLOWSPEC);
+ TERM_DEBUG_OFF(labelpool, LABELPOOL);
+ TERM_DEBUG_OFF(pbr, PBR);
+ TERM_DEBUG_OFF(pbr, PBR_ERROR);
+ TERM_DEBUG_OFF(graceful_restart, GRACEFUL_RESTART);
+ TERM_DEBUG_OFF(evpn_mh, EVPN_MH_ES);
+ TERM_DEBUG_OFF(evpn_mh, EVPN_MH_RT);
+ TERM_DEBUG_OFF(bfd, BFD_LIB);
+
+ vty_out(vty, "All possible debugging has been turned off\n");
+
+ return CMD_SUCCESS;
+}
+
+DEFUN_NOSH (show_debugging_bgp,
+ show_debugging_bgp_cmd,
+ "show debugging [bgp]",
+ SHOW_STR
+ DEBUG_STR
+ BGP_STR)
+{
+ vty_out(vty, "BGP debugging status:\n");
+
+ if (BGP_DEBUG(as4, AS4))
+ vty_out(vty, " BGP as4 debugging is on\n");
+
+ if (BGP_DEBUG(as4, AS4_SEGMENT))
+ vty_out(vty, " BGP as4 aspath segment debugging is on\n");
+
+ if (BGP_DEBUG(bestpath, BESTPATH))
+ bgp_debug_list_print(vty, " BGP bestpath debugging is on",
+ bgp_debug_bestpath_prefixes);
+
+ if (BGP_DEBUG(keepalive, KEEPALIVE))
+ bgp_debug_list_print(vty, " BGP keepalives debugging is on",
+ bgp_debug_keepalive_peers);
+
+ if (BGP_DEBUG(neighbor_events, NEIGHBOR_EVENTS))
+ bgp_debug_list_print(vty,
+ " BGP neighbor-events debugging is on",
+ bgp_debug_neighbor_events_peers);
+
+ if (BGP_DEBUG(nht, NHT))
+ vty_out(vty, " BGP next-hop tracking debugging is on\n");
+
+ if (BGP_DEBUG(update_groups, UPDATE_GROUPS))
+ vty_out(vty, " BGP update-groups debugging is on\n");
+
+ if (BGP_DEBUG(update, UPDATE_PREFIX))
+ bgp_debug_list_print(vty, " BGP updates debugging is on",
+ bgp_debug_update_prefixes);
+
+ if (BGP_DEBUG(update, UPDATE_IN))
+ bgp_debug_list_print(vty,
+ " BGP updates debugging is on (inbound)",
+ bgp_debug_update_in_peers);
+
+ if (BGP_DEBUG(update, UPDATE_OUT))
+ bgp_debug_list_print(vty,
+ " BGP updates debugging is on (outbound)",
+ bgp_debug_update_out_peers);
+
+ if (BGP_DEBUG(zebra, ZEBRA))
+ bgp_debug_list_print(vty, " BGP zebra debugging is on",
+ bgp_debug_zebra_prefixes);
+
+ if (BGP_DEBUG(graceful_restart, GRACEFUL_RESTART))
+ vty_out(vty, " BGP graceful-restart debugging is on\n");
+
+ if (BGP_DEBUG(allow_martians, ALLOW_MARTIANS))
+ vty_out(vty, " BGP allow martian next hop debugging is on\n");
+
+ if (BGP_DEBUG(vpn, VPN_LEAK_FROM_VRF))
+ vty_out(vty,
+ " BGP route leak from vrf to vpn debugging is on\n");
+ if (BGP_DEBUG(vpn, VPN_LEAK_TO_VRF))
+ vty_out(vty,
+ " BGP route leak to vrf from vpn debugging is on\n");
+ if (BGP_DEBUG(vpn, VPN_LEAK_RMAP_EVENT))
+ vty_out(vty, " BGP vpn route-map event debugging is on\n");
+ if (BGP_DEBUG(vpn, VPN_LEAK_LABEL))
+ vty_out(vty, " BGP vpn label event debugging is on\n");
+ if (BGP_DEBUG(flowspec, FLOWSPEC))
+ vty_out(vty, " BGP flowspec debugging is on\n");
+ if (BGP_DEBUG(labelpool, LABELPOOL))
+ vty_out(vty, " BGP labelpool debugging is on\n");
+
+ if (BGP_DEBUG(pbr, PBR))
+ vty_out(vty, " BGP policy based routing debugging is on\n");
+ if (BGP_DEBUG(pbr, PBR_ERROR))
+ vty_out(vty, " BGP policy based routing error debugging is on\n");
+
+ if (BGP_DEBUG(evpn_mh, EVPN_MH_ES))
+ vty_out(vty, " BGP EVPN-MH ES debugging is on\n");
+ if (BGP_DEBUG(evpn_mh, EVPN_MH_RT))
+ vty_out(vty, " BGP EVPN-MH route debugging is on\n");
+
+ if (BGP_DEBUG(bfd, BFD_LIB))
+ vty_out(vty, " BGP BFD library debugging is on\n");
+
+ return CMD_SUCCESS;
+}
+
+static int bgp_config_write_debug(struct vty *vty)
+{
+ int write = 0;
+
+ if (CONF_BGP_DEBUG(as4, AS4)) {
+ vty_out(vty, "debug bgp as4\n");
+ write++;
+ }
+
+ if (CONF_BGP_DEBUG(as4, AS4_SEGMENT)) {
+ vty_out(vty, "debug bgp as4 segment\n");
+ write++;
+ }
+
+ if (CONF_BGP_DEBUG(bestpath, BESTPATH)) {
+ write += bgp_debug_list_conf_print(vty, "debug bgp bestpath",
+ bgp_debug_bestpath_prefixes);
+ }
+
+ if (CONF_BGP_DEBUG(keepalive, KEEPALIVE)) {
+ write += bgp_debug_list_conf_print(vty, "debug bgp keepalives",
+ bgp_debug_keepalive_peers);
+ }
+
+ if (CONF_BGP_DEBUG(neighbor_events, NEIGHBOR_EVENTS)) {
+ write += bgp_debug_list_conf_print(
+ vty, "debug bgp neighbor-events",
+ bgp_debug_neighbor_events_peers);
+ }
+
+ if (CONF_BGP_DEBUG(nht, NHT)) {
+ vty_out(vty, "debug bgp nht\n");
+ write++;
+ }
+
+ if (CONF_BGP_DEBUG(update_groups, UPDATE_GROUPS)) {
+ vty_out(vty, "debug bgp update-groups\n");
+ write++;
+ }
+
+ if (CONF_BGP_DEBUG(update, UPDATE_PREFIX)) {
+ write += bgp_debug_list_conf_print(vty,
+ "debug bgp updates prefix",
+ bgp_debug_update_prefixes);
+ }
+
+ if (CONF_BGP_DEBUG(update, UPDATE_IN)) {
+ write += bgp_debug_list_conf_print(vty, "debug bgp updates in",
+ bgp_debug_update_in_peers);
+ }
+
+ if (CONF_BGP_DEBUG(update, UPDATE_OUT)) {
+ write += bgp_debug_list_conf_print(vty, "debug bgp updates out",
+ bgp_debug_update_out_peers);
+ }
+
+ if (CONF_BGP_DEBUG(zebra, ZEBRA)) {
+ if (!bgp_debug_zebra_prefixes
+ || list_isempty(bgp_debug_zebra_prefixes)) {
+ vty_out(vty, "debug bgp zebra\n");
+ write++;
+ } else {
+ write += bgp_debug_list_conf_print(
+ vty, "debug bgp zebra prefix",
+ bgp_debug_zebra_prefixes);
+ }
+ }
+
+ if (CONF_BGP_DEBUG(allow_martians, ALLOW_MARTIANS)) {
+ vty_out(vty, "debug bgp allow-martians\n");
+ write++;
+ }
+
+ if (CONF_BGP_DEBUG(vpn, VPN_LEAK_FROM_VRF)) {
+ vty_out(vty, "debug bgp vpn leak-from-vrf\n");
+ write++;
+ }
+ if (CONF_BGP_DEBUG(vpn, VPN_LEAK_TO_VRF)) {
+ vty_out(vty, "debug bgp vpn leak-to-vrf\n");
+ write++;
+ }
+ if (CONF_BGP_DEBUG(vpn, VPN_LEAK_RMAP_EVENT)) {
+ vty_out(vty, "debug bgp vpn rmap-event\n");
+ write++;
+ }
+ if (CONF_BGP_DEBUG(vpn, VPN_LEAK_LABEL)) {
+ vty_out(vty, "debug bgp vpn label\n");
+ write++;
+ }
+ if (CONF_BGP_DEBUG(flowspec, FLOWSPEC)) {
+ vty_out(vty, "debug bgp flowspec\n");
+ write++;
+ }
+ if (CONF_BGP_DEBUG(labelpool, LABELPOOL)) {
+ vty_out(vty, "debug bgp labelpool\n");
+ write++;
+ }
+
+ if (CONF_BGP_DEBUG(pbr, PBR)) {
+ vty_out(vty, "debug bgp pbr\n");
+ write++;
+ }
+ if (CONF_BGP_DEBUG(pbr, PBR_ERROR)) {
+ vty_out(vty, "debug bgp pbr error\n");
+ write++;
+ }
+
+ if (CONF_BGP_DEBUG(graceful_restart, GRACEFUL_RESTART)) {
+ vty_out(vty, "debug bgp graceful-restart\n");
+ write++;
+ }
+
+ if (CONF_BGP_DEBUG(evpn_mh, EVPN_MH_ES)) {
+ vty_out(vty, "debug bgp evpn mh es\n");
+ write++;
+ }
+ if (CONF_BGP_DEBUG(evpn_mh, EVPN_MH_RT)) {
+ vty_out(vty, "debug bgp evpn mh route\n");
+ write++;
+ }
+
+ if (CONF_BGP_DEBUG(bfd, BFD_LIB)) {
+ vty_out(vty, "debug bgp bfd\n");
+ write++;
+ }
+
+ return write;
+}
+
+static int bgp_config_write_debug(struct vty *vty);
+static struct cmd_node debug_node = {
+ .name = "debug",
+ .node = DEBUG_NODE,
+ .prompt = "",
+ .config_write = bgp_config_write_debug,
+};
+
+void bgp_debug_init(void)
+{
+ install_node(&debug_node);
+
+ install_element(ENABLE_NODE, &show_debugging_bgp_cmd);
+
+ install_element(ENABLE_NODE, &debug_bgp_as4_cmd);
+ install_element(CONFIG_NODE, &debug_bgp_as4_cmd);
+ install_element(ENABLE_NODE, &debug_bgp_as4_segment_cmd);
+ install_element(CONFIG_NODE, &debug_bgp_as4_segment_cmd);
+
+ install_element(ENABLE_NODE, &debug_bgp_neighbor_events_cmd);
+ install_element(CONFIG_NODE, &debug_bgp_neighbor_events_cmd);
+ install_element(ENABLE_NODE, &debug_bgp_nht_cmd);
+ install_element(CONFIG_NODE, &debug_bgp_nht_cmd);
+ install_element(ENABLE_NODE, &debug_bgp_keepalive_cmd);
+ install_element(CONFIG_NODE, &debug_bgp_keepalive_cmd);
+ install_element(ENABLE_NODE, &debug_bgp_update_cmd);
+ install_element(CONFIG_NODE, &debug_bgp_update_cmd);
+ install_element(ENABLE_NODE, &debug_bgp_zebra_cmd);
+ install_element(CONFIG_NODE, &debug_bgp_zebra_cmd);
+ install_element(ENABLE_NODE, &debug_bgp_update_groups_cmd);
+ install_element(CONFIG_NODE, &debug_bgp_update_groups_cmd);
+ install_element(ENABLE_NODE, &debug_bgp_bestpath_prefix_cmd);
+ install_element(CONFIG_NODE, &debug_bgp_bestpath_prefix_cmd);
+
+ install_element(ENABLE_NODE, &debug_bgp_graceful_restart_cmd);
+ install_element(CONFIG_NODE, &debug_bgp_graceful_restart_cmd);
+
+ /* debug bgp updates (in|out) */
+ install_element(ENABLE_NODE, &debug_bgp_update_direct_cmd);
+ install_element(CONFIG_NODE, &debug_bgp_update_direct_cmd);
+ install_element(ENABLE_NODE, &no_debug_bgp_update_direct_cmd);
+ install_element(CONFIG_NODE, &no_debug_bgp_update_direct_cmd);
+
+ /* debug bgp updates (in|out) A.B.C.D */
+ install_element(ENABLE_NODE, &debug_bgp_update_direct_peer_cmd);
+ install_element(CONFIG_NODE, &debug_bgp_update_direct_peer_cmd);
+ install_element(ENABLE_NODE, &no_debug_bgp_update_direct_peer_cmd);
+ install_element(CONFIG_NODE, &no_debug_bgp_update_direct_peer_cmd);
+
+ /* debug bgp updates prefix A.B.C.D/M */
+ install_element(ENABLE_NODE, &debug_bgp_update_prefix_cmd);
+ install_element(CONFIG_NODE, &debug_bgp_update_prefix_cmd);
+ install_element(ENABLE_NODE, &no_debug_bgp_update_prefix_cmd);
+ install_element(CONFIG_NODE, &no_debug_bgp_update_prefix_cmd);
+ install_element(ENABLE_NODE, &debug_bgp_update_prefix_afi_safi_cmd);
+ install_element(CONFIG_NODE, &debug_bgp_update_prefix_afi_safi_cmd);
+ install_element(ENABLE_NODE, &no_debug_bgp_update_prefix_afi_safi_cmd);
+ install_element(CONFIG_NODE, &no_debug_bgp_update_prefix_afi_safi_cmd);
+
+ /* debug bgp zebra prefix A.B.C.D/M */
+ install_element(ENABLE_NODE, &debug_bgp_zebra_prefix_cmd);
+ install_element(CONFIG_NODE, &debug_bgp_zebra_prefix_cmd);
+ install_element(ENABLE_NODE, &no_debug_bgp_zebra_prefix_cmd);
+ install_element(CONFIG_NODE, &no_debug_bgp_zebra_prefix_cmd);
+
+ install_element(ENABLE_NODE, &no_debug_bgp_as4_cmd);
+ install_element(CONFIG_NODE, &no_debug_bgp_as4_cmd);
+ install_element(ENABLE_NODE, &no_debug_bgp_as4_segment_cmd);
+ install_element(CONFIG_NODE, &no_debug_bgp_as4_segment_cmd);
+
+ /* debug bgp neighbor-events A.B.C.D */
+ install_element(ENABLE_NODE, &debug_bgp_neighbor_events_peer_cmd);
+ install_element(CONFIG_NODE, &debug_bgp_neighbor_events_peer_cmd);
+ install_element(ENABLE_NODE, &no_debug_bgp_neighbor_events_peer_cmd);
+ install_element(CONFIG_NODE, &no_debug_bgp_neighbor_events_peer_cmd);
+
+ /* debug bgp keepalive A.B.C.D */
+ install_element(ENABLE_NODE, &debug_bgp_keepalive_peer_cmd);
+ install_element(CONFIG_NODE, &debug_bgp_keepalive_peer_cmd);
+ install_element(ENABLE_NODE, &no_debug_bgp_keepalive_peer_cmd);
+ install_element(CONFIG_NODE, &no_debug_bgp_keepalive_peer_cmd);
+
+ install_element(ENABLE_NODE, &no_debug_bgp_neighbor_events_cmd);
+ install_element(CONFIG_NODE, &no_debug_bgp_neighbor_events_cmd);
+ install_element(ENABLE_NODE, &no_debug_bgp_nht_cmd);
+ install_element(CONFIG_NODE, &no_debug_bgp_nht_cmd);
+ install_element(ENABLE_NODE, &no_debug_bgp_keepalive_cmd);
+ install_element(CONFIG_NODE, &no_debug_bgp_keepalive_cmd);
+ install_element(ENABLE_NODE, &no_debug_bgp_update_cmd);
+ install_element(CONFIG_NODE, &no_debug_bgp_update_cmd);
+ install_element(ENABLE_NODE, &no_debug_bgp_zebra_cmd);
+ install_element(CONFIG_NODE, &no_debug_bgp_zebra_cmd);
+ install_element(ENABLE_NODE, &no_debug_bgp_update_groups_cmd);
+ install_element(CONFIG_NODE, &no_debug_bgp_update_groups_cmd);
+ install_element(ENABLE_NODE, &no_debug_bgp_cmd);
+ install_element(ENABLE_NODE, &no_debug_bgp_bestpath_cmd);
+ install_element(CONFIG_NODE, &no_debug_bgp_bestpath_cmd);
+ install_element(ENABLE_NODE, &no_debug_bgp_bestpath_prefix_cmd);
+ install_element(CONFIG_NODE, &no_debug_bgp_bestpath_prefix_cmd);
+
+ install_element(ENABLE_NODE, &no_debug_bgp_graceful_restart_cmd);
+ install_element(CONFIG_NODE, &no_debug_bgp_graceful_restart_cmd);
+
+ install_element(ENABLE_NODE, &debug_bgp_vpn_cmd);
+ install_element(CONFIG_NODE, &debug_bgp_vpn_cmd);
+ install_element(ENABLE_NODE, &no_debug_bgp_vpn_cmd);
+ install_element(CONFIG_NODE, &no_debug_bgp_vpn_cmd);
+
+ install_element(ENABLE_NODE, &debug_bgp_labelpool_cmd);
+ install_element(CONFIG_NODE, &debug_bgp_labelpool_cmd);
+ install_element(ENABLE_NODE, &no_debug_bgp_labelpool_cmd);
+ install_element(CONFIG_NODE, &no_debug_bgp_labelpool_cmd);
+
+ /* debug bgp pbr */
+ install_element(ENABLE_NODE, &debug_bgp_pbr_cmd);
+ install_element(CONFIG_NODE, &debug_bgp_pbr_cmd);
+ install_element(ENABLE_NODE, &no_debug_bgp_pbr_cmd);
+ install_element(CONFIG_NODE, &no_debug_bgp_pbr_cmd);
+
+ install_element(ENABLE_NODE, &debug_bgp_evpn_mh_cmd);
+ install_element(CONFIG_NODE, &debug_bgp_evpn_mh_cmd);
+
+ /* debug bgp bfd */
+ install_element(ENABLE_NODE, &debug_bgp_bfd_cmd);
+ install_element(CONFIG_NODE, &debug_bgp_bfd_cmd);
+}
+
+/* Return true if this prefix is on the per_prefix_list of prefixes to debug
+ * for BGP_DEBUG_TYPE
+ */
+static int bgp_debug_per_prefix(const struct prefix *p,
+ unsigned long term_bgp_debug_type,
+ unsigned int BGP_DEBUG_TYPE,
+ struct list *per_prefix_list)
+{
+ struct bgp_debug_filter *filter;
+ struct listnode *node, *nnode;
+
+ if (term_bgp_debug_type & BGP_DEBUG_TYPE) {
+ /* We are debugging all prefixes so return true */
+ if (!per_prefix_list || list_isempty(per_prefix_list))
+ return 1;
+
+ else {
+ if (!p)
+ return 0;
+
+ for (ALL_LIST_ELEMENTS(per_prefix_list, node, nnode,
+ filter))
+ if (filter->p->prefixlen == p->prefixlen
+ && prefix_match(filter->p, p))
+ return 1;
+
+ return 0;
+ }
+ }
+
+ return 0;
+}
+
+/* Return true if this peer is on the per_peer_list of peers to debug
+ * for BGP_DEBUG_TYPE
+ */
+static bool bgp_debug_per_peer(char *host, unsigned long term_bgp_debug_type,
+ unsigned int BGP_DEBUG_TYPE,
+ struct list *per_peer_list)
+{
+ struct bgp_debug_filter *filter;
+ struct listnode *node, *nnode;
+
+ if (term_bgp_debug_type & BGP_DEBUG_TYPE) {
+ /* We are debugging all peers so return true */
+ if (!per_peer_list || list_isempty(per_peer_list))
+ return true;
+
+ else {
+ if (!host)
+ return false;
+
+ for (ALL_LIST_ELEMENTS(per_peer_list, node, nnode,
+ filter))
+ if (strcmp(filter->host, host) == 0)
+ return true;
+
+ return false;
+ }
+ }
+
+ return false;
+}
+
+bool bgp_debug_neighbor_events(const struct peer *peer)
+{
+ char *host = NULL;
+
+ if (peer)
+ host = peer->host;
+
+ return bgp_debug_per_peer(host, term_bgp_debug_neighbor_events,
+ BGP_DEBUG_NEIGHBOR_EVENTS,
+ bgp_debug_neighbor_events_peers);
+}
+
+bool bgp_debug_keepalive(const struct peer *peer)
+{
+ char *host = NULL;
+
+ if (peer)
+ host = peer->host;
+
+ return bgp_debug_per_peer(host, term_bgp_debug_keepalive,
+ BGP_DEBUG_KEEPALIVE,
+ bgp_debug_keepalive_peers);
+}
+
+bool bgp_debug_update(const struct peer *peer, const struct prefix *p,
+ struct update_group *updgrp, unsigned int inbound)
+{
+ char *host = NULL;
+
+ if (peer)
+ host = peer->host;
+
+ if (inbound) {
+ if (bgp_debug_per_peer(host, term_bgp_debug_update,
+ BGP_DEBUG_UPDATE_IN,
+ bgp_debug_update_in_peers))
+ return true;
+ }
+
+ /* outbound */
+ else {
+ if (bgp_debug_per_peer(host, term_bgp_debug_update,
+ BGP_DEBUG_UPDATE_OUT,
+ bgp_debug_update_out_peers))
+ return true;
+
+ /* Check if update debugging implicitly enabled for the group.
+ */
+ if (updgrp && UPDGRP_DBG_ON(updgrp))
+ return true;
+ }
+
+
+ if (BGP_DEBUG(update, UPDATE_PREFIX)) {
+ if (bgp_debug_per_prefix(p, term_bgp_debug_update,
+ BGP_DEBUG_UPDATE_PREFIX,
+ bgp_debug_update_prefixes))
+ return true;
+ }
+
+ return false;
+}
+
+bool bgp_debug_bestpath(struct bgp_dest *dest)
+{
+ if (BGP_DEBUG(bestpath, BESTPATH)) {
+ if (bgp_debug_per_prefix(
+ bgp_dest_get_prefix(dest), term_bgp_debug_bestpath,
+ BGP_DEBUG_BESTPATH, bgp_debug_bestpath_prefixes))
+ return true;
+ }
+
+ return false;
+}
+
+bool bgp_debug_zebra(const struct prefix *p)
+{
+ if (BGP_DEBUG(zebra, ZEBRA)) {
+ if (bgp_debug_per_prefix(p, term_bgp_debug_zebra,
+ BGP_DEBUG_ZEBRA,
+ bgp_debug_zebra_prefixes))
+ return true;
+ }
+
+ return false;
+}
+
+const char *bgp_debug_rdpfxpath2str(afi_t afi, safi_t safi,
+ const struct prefix_rd *prd,
+ union prefixconstptr pu,
+ mpls_label_t *label, uint32_t num_labels,
+ int addpath_valid, uint32_t addpath_id,
+ struct bgp_route_evpn *overlay_index,
+ char *str, int size)
+{
+ char tag_buf[30];
+ char overlay_index_buf[INET6_ADDRSTRLEN + 14];
+ const struct prefix_evpn *evp;
+
+ /* ' with addpath ID ' 17
+ * max strlen of uint32 + 10
+ * +/- (just in case) + 1
+ * null terminator + 1
+ * ============================ 29 */
+ char pathid_buf[30];
+
+ if (size < BGP_PRD_PATH_STRLEN)
+ return NULL;
+
+ /* Note: Path-id is created by default, but only included in update
+ * sometimes. */
+ pathid_buf[0] = '\0';
+ if (addpath_valid)
+ snprintf(pathid_buf, sizeof(pathid_buf), " with addpath ID %u",
+ addpath_id);
+
+ overlay_index_buf[0] = '\0';
+ if (overlay_index && overlay_index->type == OVERLAY_INDEX_GATEWAY_IP) {
+ char obuf[INET6_ADDRSTRLEN];
+
+ obuf[0] = '\0';
+ evp = pu.evp;
+ if (is_evpn_prefix_ipaddr_v4(evp))
+ inet_ntop(AF_INET, &overlay_index->gw_ip, obuf,
+ sizeof(obuf));
+ else if (is_evpn_prefix_ipaddr_v6(evp))
+ inet_ntop(AF_INET6, &overlay_index->gw_ip, obuf,
+ sizeof(obuf));
+
+ snprintf(overlay_index_buf, sizeof(overlay_index_buf),
+ " gateway IP %s", obuf);
+ }
+
+ tag_buf[0] = '\0';
+ if (bgp_labeled_safi(safi) && num_labels) {
+
+ if (safi == SAFI_EVPN) {
+ char tag_buf2[20];
+
+ bgp_evpn_label2str(label, num_labels, tag_buf2, 20);
+ snprintf(tag_buf, sizeof(tag_buf), " label %s",
+ tag_buf2);
+ } else {
+ uint32_t label_value;
+
+ label_value = decode_label(label);
+ snprintf(tag_buf, sizeof(tag_buf), " label %u",
+ label_value);
+ }
+ }
+
+ if (prd)
+ snprintfrr(str, size, "RD %pRD %pFX%s%s%s %s %s", prd, pu.p,
+ overlay_index_buf, tag_buf, pathid_buf, afi2str(afi),
+ safi2str(safi));
+ else if (safi == SAFI_FLOWSPEC) {
+ char return_string[BGP_FLOWSPEC_NLRI_STRING_MAX];
+ const struct prefix_fs *fs = pu.fs;
+
+ bgp_fs_nlri_get_string((unsigned char *)fs->prefix.ptr,
+ fs->prefix.prefixlen,
+ return_string,
+ NLRI_STRING_FORMAT_DEBUG, NULL,
+ family2afi(fs->prefix.family));
+ snprintf(str, size, "FS %s Match{%s}", afi2str(afi),
+ return_string);
+ } else
+ snprintfrr(str, size, "%pFX%s%s %s %s", pu.p, tag_buf,
+ pathid_buf, afi2str(afi), safi2str(safi));
+
+ return str;
+}
diff --git a/bgpd/bgp_debug.h b/bgpd/bgp_debug.h
new file mode 100644
index 0000000..62f5340
--- /dev/null
+++ b/bgpd/bgp_debug.h
@@ -0,0 +1,192 @@
+/* BGP message debug header.
+ * Copyright (C) 1996, 97, 98 Kunihiro Ishiguro
+ *
+ * This file is part of GNU Zebra.
+ *
+ * GNU Zebra is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License as published by the
+ * Free Software Foundation; either version 2, or (at your option) any
+ * later version.
+ *
+ * GNU Zebra is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program; see the file COPYING; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
+ */
+
+#ifndef _QUAGGA_BGP_DEBUG_H
+#define _QUAGGA_BGP_DEBUG_H
+
+#include "bgp_attr.h"
+#include "bgp_updgrp.h"
+
+/* sort of packet direction */
+#define DUMP_ON 1
+#define DUMP_SEND 2
+#define DUMP_RECV 4
+
+/* for dump_update */
+#define DUMP_WITHDRAW 8
+#define DUMP_NLRI 16
+
+/* dump detail */
+#define DUMP_DETAIL 32
+
+/* RD + Prefix + Path-Id */
+#define BGP_PRD_PATH_STRLEN \
+ (PREFIX_STRLEN + RD_ADDRSTRLEN + INET6_ADDRSTRLEN + 34)
+
+extern int dump_open;
+extern int dump_update;
+extern int dump_keepalive;
+extern int dump_notify;
+
+extern int Debug_Event;
+extern int Debug_Keepalive;
+extern int Debug_Update;
+extern int Debug_Radix;
+
+#define NLRI 1
+#define WITHDRAW 2
+#define NO_OPT 3
+#define SEND 4
+#define RECV 5
+#define DETAIL 6
+
+/* Prototypes. */
+extern void bgp_debug_init(void);
+extern void bgp_packet_dump(struct stream *);
+
+extern int debug(unsigned int option);
+
+extern unsigned long conf_bgp_debug_as4;
+extern unsigned long conf_bgp_debug_neighbor_events;
+extern unsigned long conf_bgp_debug_packet;
+extern unsigned long conf_bgp_debug_keepalive;
+extern unsigned long conf_bgp_debug_update;
+extern unsigned long conf_bgp_debug_bestpath;
+extern unsigned long conf_bgp_debug_zebra;
+extern unsigned long conf_bgp_debug_allow_martians;
+extern unsigned long conf_bgp_debug_nht;
+extern unsigned long conf_bgp_debug_update_groups;
+extern unsigned long conf_bgp_debug_vpn;
+extern unsigned long conf_bgp_debug_flowspec;
+extern unsigned long conf_bgp_debug_labelpool;
+extern unsigned long conf_bgp_debug_pbr;
+extern unsigned long conf_bgp_debug_graceful_restart;
+extern unsigned long conf_bgp_debug_evpn_mh;
+extern unsigned long conf_bgp_debug_bfd;
+
+extern unsigned long term_bgp_debug_as4;
+extern unsigned long term_bgp_debug_neighbor_events;
+extern unsigned long term_bgp_debug_packet;
+extern unsigned long term_bgp_debug_keepalive;
+extern unsigned long term_bgp_debug_update;
+extern unsigned long term_bgp_debug_bestpath;
+extern unsigned long term_bgp_debug_zebra;
+extern unsigned long term_bgp_debug_allow_martians;
+extern unsigned long term_bgp_debug_nht;
+extern unsigned long term_bgp_debug_update_groups;
+extern unsigned long term_bgp_debug_vpn;
+extern unsigned long term_bgp_debug_flowspec;
+extern unsigned long term_bgp_debug_labelpool;
+extern unsigned long term_bgp_debug_pbr;
+extern unsigned long term_bgp_debug_graceful_restart;
+extern unsigned long term_bgp_debug_evpn_mh;
+extern unsigned long term_bgp_debug_bfd;
+
+extern struct list *bgp_debug_neighbor_events_peers;
+extern struct list *bgp_debug_keepalive_peers;
+extern struct list *bgp_debug_update_in_peers;
+extern struct list *bgp_debug_update_out_peers;
+extern struct list *bgp_debug_update_prefixes;
+extern struct list *bgp_debug_bestpath_prefixes;
+extern struct list *bgp_debug_zebra_prefixes;
+
+struct bgp_debug_filter {
+ char *host;
+ struct prefix *p;
+};
+
+#define BGP_DEBUG_AS4 0x01
+#define BGP_DEBUG_AS4_SEGMENT 0x02
+
+#define BGP_DEBUG_BESTPATH 0x01
+#define BGP_DEBUG_NEIGHBOR_EVENTS 0x01
+#define BGP_DEBUG_PACKET 0x01
+#define BGP_DEBUG_KEEPALIVE 0x01
+#define BGP_DEBUG_UPDATE_IN 0x01
+#define BGP_DEBUG_UPDATE_OUT 0x02
+#define BGP_DEBUG_UPDATE_PREFIX 0x04
+#define BGP_DEBUG_ZEBRA 0x01
+#define BGP_DEBUG_ALLOW_MARTIANS 0x01
+#define BGP_DEBUG_NHT 0x01
+#define BGP_DEBUG_UPDATE_GROUPS 0x01
+#define BGP_DEBUG_VPN_LEAK_FROM_VRF 0x01
+#define BGP_DEBUG_VPN_LEAK_TO_VRF 0x02
+#define BGP_DEBUG_VPN_LEAK_RMAP_EVENT 0x04
+#define BGP_DEBUG_VPN_LEAK_LABEL 0x08
+#define BGP_DEBUG_FLOWSPEC 0x01
+#define BGP_DEBUG_LABELPOOL 0x01
+#define BGP_DEBUG_PBR 0x01
+#define BGP_DEBUG_PBR_ERROR 0x02
+#define BGP_DEBUG_EVPN_MH_ES 0x01
+#define BGP_DEBUG_EVPN_MH_RT 0x02
+
+#define BGP_DEBUG_PACKET_SEND 0x01
+#define BGP_DEBUG_PACKET_SEND_DETAIL 0x02
+
+#define BGP_DEBUG_GRACEFUL_RESTART 0x01
+
+#define BGP_DEBUG_BFD_LIB 0x01
+
+#define CONF_DEBUG_ON(a, b) (conf_bgp_debug_ ## a |= (BGP_DEBUG_ ## b))
+#define CONF_DEBUG_OFF(a, b) (conf_bgp_debug_ ## a &= ~(BGP_DEBUG_ ## b))
+
+#define TERM_DEBUG_ON(a, b) (term_bgp_debug_ ## a |= (BGP_DEBUG_ ## b))
+#define TERM_DEBUG_OFF(a, b) (term_bgp_debug_ ## a &= ~(BGP_DEBUG_ ## b))
+
+#define DEBUG_ON(a, b) \
+ do { \
+ CONF_DEBUG_ON(a, b); \
+ TERM_DEBUG_ON(a, b); \
+ } while (0)
+#define DEBUG_OFF(a, b) \
+ do { \
+ CONF_DEBUG_OFF(a, b); \
+ TERM_DEBUG_OFF(a, b); \
+ } while (0)
+
+#define BGP_DEBUG(a, b) (term_bgp_debug_ ## a & BGP_DEBUG_ ## b)
+#define CONF_BGP_DEBUG(a, b) (conf_bgp_debug_ ## a & BGP_DEBUG_ ## b)
+
+extern const char *const bgp_type_str[];
+
+extern bool bgp_dump_attr(struct attr *attr, char *buf, size_t size);
+extern bool bgp_debug_peer_updout_enabled(char *host);
+extern const char *bgp_notify_code_str(char code);
+extern const char *bgp_notify_subcode_str(char code, char subcode);
+extern void bgp_notify_print(struct peer *peer, struct bgp_notify *bgp_notify,
+ const char *direct, bool hard_reset);
+
+extern const struct message bgp_status_msg[];
+extern bool bgp_debug_neighbor_events(const struct peer *peer);
+extern bool bgp_debug_keepalive(const struct peer *peer);
+extern bool bgp_debug_update(const struct peer *peer, const struct prefix *p,
+ struct update_group *updgrp, unsigned int inbound);
+extern bool bgp_debug_bestpath(struct bgp_dest *dest);
+extern bool bgp_debug_zebra(const struct prefix *p);
+
+extern const char *bgp_debug_rdpfxpath2str(
+ afi_t afi, safi_t safi, const struct prefix_rd *prd,
+ union prefixconstptr pu, mpls_label_t *label, uint32_t num_labels,
+ int addpath_valid, uint32_t addpath_id,
+ struct bgp_route_evpn *overlay_index, char *str, int size);
+const char *bgp_notify_admin_message(char *buf, size_t bufsz, uint8_t *data,
+ size_t datalen);
+
+#endif /* _QUAGGA_BGP_DEBUG_H */
diff --git a/bgpd/bgp_dump.c b/bgpd/bgp_dump.c
new file mode 100644
index 0000000..68499e3
--- /dev/null
+++ b/bgpd/bgp_dump.c
@@ -0,0 +1,880 @@
+/* BGP-4 dump routine
+ * Copyright (C) 1999 Kunihiro Ishiguro
+ *
+ * This file is part of GNU Zebra.
+ *
+ * GNU Zebra is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License as published by the
+ * Free Software Foundation; either version 2, or (at your option) any
+ * later version.
+ *
+ * GNU Zebra is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program; see the file COPYING; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
+ */
+
+#include <zebra.h>
+
+#include "log.h"
+#include "stream.h"
+#include "sockunion.h"
+#include "command.h"
+#include "prefix.h"
+#include "thread.h"
+#include "linklist.h"
+#include "queue.h"
+#include "memory.h"
+#include "filter.h"
+
+#include "bgpd/bgp_table.h"
+#include "bgpd/bgpd.h"
+#include "bgpd/bgp_route.h"
+#include "bgpd/bgp_attr.h"
+#include "bgpd/bgp_dump.h"
+#include "bgpd/bgp_errors.h"
+#include "bgpd/bgp_packet.h"
+
+enum bgp_dump_type {
+ BGP_DUMP_ALL,
+ BGP_DUMP_ALL_ET,
+ BGP_DUMP_UPDATES,
+ BGP_DUMP_UPDATES_ET,
+ BGP_DUMP_ROUTES
+};
+
+static const struct bgp_dump_type_map {
+ enum bgp_dump_type type;
+ const char *str;
+} bgp_dump_type_map[] = {
+ {BGP_DUMP_ALL, "all"}, {BGP_DUMP_ALL_ET, "all-et"},
+ {BGP_DUMP_UPDATES, "updates"}, {BGP_DUMP_UPDATES_ET, "updates-et"},
+ {BGP_DUMP_ROUTES, "routes-mrt"}, {0, NULL},
+};
+
+enum MRT_MSG_TYPES {
+ MSG_NULL,
+ MSG_START, /* sender is starting up */
+ MSG_DIE, /* receiver should shut down */
+ MSG_I_AM_DEAD, /* sender is shutting down */
+ MSG_PEER_DOWN, /* sender's peer is down */
+ MSG_PROTOCOL_BGP, /* msg is a BGP packet */
+ MSG_PROTOCOL_RIP, /* msg is a RIP packet */
+ MSG_PROTOCOL_IDRP, /* msg is an IDRP packet */
+ MSG_PROTOCOL_RIPNG, /* msg is a RIPNG packet */
+ MSG_PROTOCOL_BGP4PLUS, /* msg is a BGP4+ packet */
+ MSG_PROTOCOL_BGP4PLUS_01, /* msg is a BGP4+ (draft 01) packet */
+ MSG_PROTOCOL_OSPF, /* msg is an OSPF packet */
+ MSG_TABLE_DUMP, /* routing table dump */
+ MSG_TABLE_DUMP_V2 /* routing table dump, version 2 */
+};
+
+struct bgp_dump {
+ enum bgp_dump_type type;
+
+ char *filename;
+
+ FILE *fp;
+
+ unsigned int interval;
+
+ char *interval_str;
+
+ struct thread *t_interval;
+};
+
+static int bgp_dump_unset(struct bgp_dump *bgp_dump);
+static void bgp_dump_interval_func(struct thread *);
+
+/* BGP packet dump output buffer. */
+struct stream *bgp_dump_obuf;
+
+/* BGP dump strucuture for 'dump bgp all' */
+struct bgp_dump bgp_dump_all;
+
+/* BGP dump structure for 'dump bgp updates' */
+struct bgp_dump bgp_dump_updates;
+
+/* BGP dump structure for 'dump bgp routes' */
+struct bgp_dump bgp_dump_routes;
+
+static FILE *bgp_dump_open_file(struct bgp_dump *bgp_dump)
+{
+ int ret;
+ time_t clock;
+ struct tm tm;
+ char fullpath[MAXPATHLEN];
+ char realpath[MAXPATHLEN];
+ mode_t oldumask;
+
+ time(&clock);
+ localtime_r(&clock, &tm);
+
+ if (bgp_dump->filename[0] != DIRECTORY_SEP) {
+ snprintf(fullpath, sizeof(fullpath), "%s/%s", vty_get_cwd(),
+ bgp_dump->filename);
+ ret = strftime(realpath, MAXPATHLEN, fullpath, &tm);
+ } else
+ ret = strftime(realpath, MAXPATHLEN, bgp_dump->filename, &tm);
+
+ if (ret == 0) {
+ flog_warn(EC_BGP_DUMP, "%s: strftime error", __func__);
+ return NULL;
+ }
+
+ if (bgp_dump->fp)
+ fclose(bgp_dump->fp);
+
+
+ oldumask = umask(0777 & ~LOGFILE_MASK);
+ bgp_dump->fp = fopen(realpath, "w");
+
+ if (bgp_dump->fp == NULL) {
+ flog_warn(EC_BGP_DUMP, "%s: %s: %s", __func__, realpath,
+ strerror(errno));
+ umask(oldumask);
+ return NULL;
+ }
+ umask(oldumask);
+
+ return bgp_dump->fp;
+}
+
+static int bgp_dump_interval_add(struct bgp_dump *bgp_dump, int interval)
+{
+ int secs_into_day;
+ time_t t;
+ struct tm tm;
+
+ if (interval > 0) {
+ /* Periodic dump every interval seconds */
+ if ((interval < 86400) && ((86400 % interval) == 0)) {
+ /* Dump at predictable times: if a day has a whole
+ * number of
+ * intervals, dump every interval seconds starting from
+ * midnight
+ */
+ (void)time(&t);
+ localtime_r(&t, &tm);
+ secs_into_day = tm.tm_sec + 60 * tm.tm_min
+ + 60 * 60 * tm.tm_hour;
+ interval = interval
+ - secs_into_day % interval; /* always > 0 */
+ }
+ thread_add_timer(bm->master, bgp_dump_interval_func, bgp_dump,
+ interval, &bgp_dump->t_interval);
+ } else {
+ /* One-off dump: execute immediately, don't affect any scheduled
+ * dumps */
+ thread_add_event(bm->master, bgp_dump_interval_func, bgp_dump,
+ 0, &bgp_dump->t_interval);
+ }
+
+ return 0;
+}
+
+/* Dump common header. */
+static void bgp_dump_header(struct stream *obuf, int type, int subtype,
+ int dump_type)
+{
+ struct timeval clock;
+ long msecs;
+ time_t secs;
+
+ if ((dump_type == BGP_DUMP_ALL_ET || dump_type == BGP_DUMP_UPDATES_ET)
+ && type == MSG_PROTOCOL_BGP4MP)
+ type = MSG_PROTOCOL_BGP4MP_ET;
+
+ gettimeofday(&clock, NULL);
+
+ secs = clock.tv_sec;
+ msecs = clock.tv_usec;
+
+ /* Put dump packet header. */
+ stream_putl(obuf, secs);
+ stream_putw(obuf, type);
+ stream_putw(obuf, subtype);
+ stream_putl(obuf, 0); /* len */
+
+ /* Adding microseconds for the MRT Extended Header */
+ if (type == MSG_PROTOCOL_BGP4MP_ET)
+ stream_putl(obuf, msecs);
+}
+
+static void bgp_dump_set_size(struct stream *s, int type)
+{
+ /*
+ * The BGP_DUMP_HEADER_SIZE stay at 12 event when ET:
+ * "The Microsecond Timestamp is included in the computation
+ * of the Length field value." (RFC6396 2011)
+ */
+ stream_putl_at(s, 8, stream_get_endp(s) - BGP_DUMP_HEADER_SIZE);
+}
+
+static void bgp_dump_routes_index_table(struct bgp *bgp)
+{
+ struct peer *peer;
+ struct listnode *node;
+ uint16_t peerno = 1;
+ struct stream *obuf;
+
+ obuf = bgp_dump_obuf;
+ stream_reset(obuf);
+
+ /* MRT header */
+ bgp_dump_header(obuf, MSG_TABLE_DUMP_V2, TABLE_DUMP_V2_PEER_INDEX_TABLE,
+ BGP_DUMP_ROUTES);
+
+ /* Collector BGP ID */
+ stream_put_in_addr(obuf, &bgp->router_id);
+
+ /* View name */
+ if (bgp->name_pretty) {
+ stream_putw(obuf, strlen(bgp->name_pretty));
+ stream_put(obuf, bgp->name_pretty, strlen(bgp->name_pretty));
+ } else {
+ stream_putw(obuf, 0);
+ }
+
+ /* Peer count ( plus one extra internal peer ) */
+ stream_putw(obuf, listcount(bgp->peer) + 1);
+
+ /* Populate fake peer at index 0, for locally originated routes */
+ /* Peer type (IPv4) */
+ stream_putc(obuf,
+ TABLE_DUMP_V2_PEER_INDEX_TABLE_AS4
+ + TABLE_DUMP_V2_PEER_INDEX_TABLE_IP);
+ /* Peer BGP ID (0.0.0.0) */
+ stream_putl(obuf, 0);
+ /* Peer IP address (0.0.0.0) */
+ stream_putl(obuf, 0);
+ /* Peer ASN (0) */
+ stream_putl(obuf, 0);
+
+ /* Walk down all peers */
+ for (ALL_LIST_ELEMENTS_RO(bgp->peer, node, peer)) {
+
+ /* Peer's type */
+ if (sockunion_family(&peer->su) == AF_INET) {
+ stream_putc(
+ obuf,
+ TABLE_DUMP_V2_PEER_INDEX_TABLE_AS4
+ + TABLE_DUMP_V2_PEER_INDEX_TABLE_IP);
+ } else if (sockunion_family(&peer->su) == AF_INET6) {
+ stream_putc(
+ obuf,
+ TABLE_DUMP_V2_PEER_INDEX_TABLE_AS4
+ + TABLE_DUMP_V2_PEER_INDEX_TABLE_IP6);
+ }
+
+ /* Peer's BGP ID */
+ stream_put_in_addr(obuf, &peer->remote_id);
+
+ /* Peer's IP address */
+ if (sockunion_family(&peer->su) == AF_INET) {
+ stream_put_in_addr(obuf, &peer->su.sin.sin_addr);
+ } else if (sockunion_family(&peer->su) == AF_INET6) {
+ stream_write(obuf, (uint8_t *)&peer->su.sin6.sin6_addr,
+ IPV6_MAX_BYTELEN);
+ }
+
+ /* Peer's AS number. */
+ /* Note that, as this is an AS4 compliant quagga, the RIB is
+ * always AS4 */
+ stream_putl(obuf, peer->as);
+
+ /* Store the peer number for this peer */
+ peer->table_dump_index = peerno;
+ peerno++;
+ }
+
+ bgp_dump_set_size(obuf, MSG_TABLE_DUMP_V2);
+
+ fwrite(STREAM_DATA(obuf), stream_get_endp(obuf), 1, bgp_dump_routes.fp);
+ fflush(bgp_dump_routes.fp);
+}
+
+static struct bgp_path_info *
+bgp_dump_route_node_record(int afi, struct bgp_dest *dest,
+ struct bgp_path_info *path, unsigned int seq)
+{
+ struct stream *obuf;
+ size_t sizep;
+ size_t endp;
+ bool addpath_capable;
+ const struct prefix *p = bgp_dest_get_prefix(dest);
+
+ obuf = bgp_dump_obuf;
+ stream_reset(obuf);
+
+ addpath_capable = bgp_addpath_encode_rx(path->peer, afi, SAFI_UNICAST);
+
+ /* MRT header */
+ if (afi == AFI_IP && addpath_capable)
+ bgp_dump_header(obuf, MSG_TABLE_DUMP_V2,
+ TABLE_DUMP_V2_RIB_IPV4_UNICAST_ADDPATH,
+ BGP_DUMP_ROUTES);
+ else if (afi == AFI_IP)
+ bgp_dump_header(obuf, MSG_TABLE_DUMP_V2,
+ TABLE_DUMP_V2_RIB_IPV4_UNICAST,
+ BGP_DUMP_ROUTES);
+ else if (afi == AFI_IP6 && addpath_capable)
+ bgp_dump_header(obuf, MSG_TABLE_DUMP_V2,
+ TABLE_DUMP_V2_RIB_IPV6_UNICAST_ADDPATH,
+ BGP_DUMP_ROUTES);
+ else if (afi == AFI_IP6)
+ bgp_dump_header(obuf, MSG_TABLE_DUMP_V2,
+ TABLE_DUMP_V2_RIB_IPV6_UNICAST,
+ BGP_DUMP_ROUTES);
+
+ /* Sequence number */
+ stream_putl(obuf, seq);
+
+ /* Prefix length */
+ stream_putc(obuf, p->prefixlen);
+
+ /* Prefix */
+ if (afi == AFI_IP) {
+ /* We'll dump only the useful bits (those not 0), but have to
+ * align on 8 bits */
+ stream_write(obuf, (uint8_t *)&p->u.prefix4,
+ (p->prefixlen + 7) / 8);
+ } else if (afi == AFI_IP6) {
+ /* We'll dump only the useful bits (those not 0), but have to
+ * align on 8 bits */
+ stream_write(obuf, (uint8_t *)&p->u.prefix6,
+ (p->prefixlen + 7) / 8);
+ }
+
+ /* Save where we are now, so we can overwride the entry count later */
+ sizep = stream_get_endp(obuf);
+
+ /* Entry count */
+ uint16_t entry_count = 0;
+
+ /* Entry count, note that this is overwritten later */
+ stream_putw(obuf, 0);
+
+ endp = stream_get_endp(obuf);
+ for (; path; path = path->next) {
+ size_t cur_endp;
+
+ /* Peer index */
+ stream_putw(obuf, path->peer->table_dump_index);
+
+ /* Originated */
+ stream_putl(obuf, time(NULL) - (monotime(NULL) - path->uptime));
+
+ /*Path Identifier*/
+ if (addpath_capable) {
+ stream_putl(obuf, path->addpath_rx_id);
+ }
+
+ /* Dump attribute. */
+ /* Skip prefix & AFI/SAFI for MP_NLRI */
+ bgp_dump_routes_attr(obuf, path->attr, p);
+
+ cur_endp = stream_get_endp(obuf);
+ if (cur_endp > BGP_STANDARD_MESSAGE_MAX_PACKET_SIZE
+ + BGP_DUMP_MSG_HEADER
+ + BGP_DUMP_HEADER_SIZE) {
+ stream_set_endp(obuf, endp);
+ break;
+ }
+
+ entry_count++;
+ endp = cur_endp;
+ }
+
+ /* Overwrite the entry count, now that we know the right number */
+ stream_putw_at(obuf, sizep, entry_count);
+
+ bgp_dump_set_size(obuf, MSG_TABLE_DUMP_V2);
+ fwrite(STREAM_DATA(obuf), stream_get_endp(obuf), 1, bgp_dump_routes.fp);
+
+ return path;
+}
+
+
+/* Runs under child process. */
+static unsigned int bgp_dump_routes_func(int afi, int first_run,
+ unsigned int seq)
+{
+ struct bgp_path_info *path;
+ struct bgp_dest *dest;
+ struct bgp *bgp;
+ struct bgp_table *table;
+
+ bgp = bgp_get_default();
+ if (!bgp)
+ return seq;
+
+ if (bgp_dump_routes.fp == NULL)
+ return seq;
+
+ /* Note that bgp_dump_routes_index_table will do ipv4 and ipv6 peers,
+ so this should only be done on the first call to
+ bgp_dump_routes_func.
+ ( this function will be called once for ipv4 and once for ipv6 ) */
+ if (first_run)
+ bgp_dump_routes_index_table(bgp);
+
+ /* Walk down each BGP route. */
+ table = bgp->rib[afi][SAFI_UNICAST];
+
+ for (dest = bgp_table_top(table); dest; dest = bgp_route_next(dest)) {
+ path = bgp_dest_get_bgp_path_info(dest);
+ while (path) {
+ path = bgp_dump_route_node_record(afi, dest, path, seq);
+ seq++;
+ }
+ }
+
+ fflush(bgp_dump_routes.fp);
+
+ return seq;
+}
+
+static void bgp_dump_interval_func(struct thread *t)
+{
+ struct bgp_dump *bgp_dump;
+ bgp_dump = THREAD_ARG(t);
+
+ /* Reschedule dump even if file couldn't be opened this time... */
+ if (bgp_dump_open_file(bgp_dump) != NULL) {
+ /* In case of bgp_dump_routes, we need special route dump
+ * function. */
+ if (bgp_dump->type == BGP_DUMP_ROUTES) {
+ unsigned int seq = bgp_dump_routes_func(AFI_IP, 1, 0);
+ bgp_dump_routes_func(AFI_IP6, 0, seq);
+ /* Close the file now. For a RIB dump there's no point
+ * in leaving
+ * it open until the next scheduled dump starts. */
+ fclose(bgp_dump->fp);
+ bgp_dump->fp = NULL;
+ }
+ }
+
+ /* if interval is set reschedule */
+ if (bgp_dump->interval > 0)
+ bgp_dump_interval_add(bgp_dump, bgp_dump->interval);
+}
+
+/* Dump common information. */
+static void bgp_dump_common(struct stream *obuf, struct peer *peer,
+ int forceas4)
+{
+ char empty[16] = {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0};
+
+ /* Source AS number and Destination AS number. */
+ if (forceas4 || CHECK_FLAG(peer->cap, PEER_CAP_AS4_RCV)) {
+ stream_putl(obuf, peer->as);
+ stream_putl(obuf, peer->local_as);
+ } else {
+ stream_putw(obuf, peer->as);
+ stream_putw(obuf, peer->local_as);
+ }
+
+ if (peer->su.sa.sa_family == AF_INET) {
+ stream_putw(obuf, peer->ifp ? peer->ifp->ifindex : 0);
+ stream_putw(obuf, AFI_IP);
+
+ stream_put(obuf, &peer->su.sin.sin_addr, IPV4_MAX_BYTELEN);
+
+ if (peer->su_local)
+ stream_put(obuf, &peer->su_local->sin.sin_addr,
+ IPV4_MAX_BYTELEN);
+ else
+ stream_put(obuf, empty, IPV4_MAX_BYTELEN);
+ } else if (peer->su.sa.sa_family == AF_INET6) {
+ /* Interface Index and Address family. */
+ stream_putw(obuf, peer->ifp ? peer->ifp->ifindex : 0);
+ stream_putw(obuf, AFI_IP6);
+
+ /* Source IP Address and Destination IP Address. */
+ stream_put(obuf, &peer->su.sin6.sin6_addr, IPV6_MAX_BYTELEN);
+
+ if (peer->su_local)
+ stream_put(obuf, &peer->su_local->sin6.sin6_addr,
+ IPV6_MAX_BYTELEN);
+ else
+ stream_put(obuf, empty, IPV6_MAX_BYTELEN);
+ }
+}
+
+/* Dump BGP status change. */
+int bgp_dump_state(struct peer *peer)
+{
+ struct stream *obuf;
+
+ /* If dump file pointer is disabled return immediately. */
+ if (bgp_dump_all.fp == NULL)
+ return 0;
+
+ /* Make dump stream. */
+ obuf = bgp_dump_obuf;
+ stream_reset(obuf);
+
+ bgp_dump_header(obuf, MSG_PROTOCOL_BGP4MP, BGP4MP_STATE_CHANGE_AS4,
+ bgp_dump_all.type);
+ bgp_dump_common(obuf, peer, 1); /* force this in as4speak*/
+
+ stream_putw(obuf, peer->ostatus);
+ stream_putw(obuf, peer->status);
+
+ /* Set length. */
+ bgp_dump_set_size(obuf, MSG_PROTOCOL_BGP4MP);
+
+ /* Write to the stream. */
+ fwrite(STREAM_DATA(obuf), stream_get_endp(obuf), 1, bgp_dump_all.fp);
+ fflush(bgp_dump_all.fp);
+ return 0;
+}
+
+static void bgp_dump_packet_func(struct bgp_dump *bgp_dump, struct peer *peer,
+ struct stream *packet)
+{
+ struct stream *obuf;
+ bool addpath_capable = false;
+ /* If dump file pointer is disabled return immediately. */
+ if (bgp_dump->fp == NULL)
+ return;
+ if (peer->su.sa.sa_family == AF_INET) {
+ addpath_capable =
+ bgp_addpath_encode_rx(peer, AFI_IP, SAFI_UNICAST);
+ } else if (peer->su.sa.sa_family == AF_INET6) {
+ addpath_capable =
+ bgp_addpath_encode_rx(peer, AFI_IP6, SAFI_UNICAST);
+ }
+
+ /* Make dump stream. */
+ obuf = bgp_dump_obuf;
+ stream_reset(obuf);
+
+ /* Dump header and common part. */
+ if (CHECK_FLAG(peer->cap, PEER_CAP_AS4_RCV) && addpath_capable) {
+ bgp_dump_header(obuf, MSG_PROTOCOL_BGP4MP,
+ BGP4MP_MESSAGE_AS4_ADDPATH, bgp_dump->type);
+ } else if (CHECK_FLAG(peer->cap, PEER_CAP_AS4_RCV)) {
+ bgp_dump_header(obuf, MSG_PROTOCOL_BGP4MP, BGP4MP_MESSAGE_AS4,
+ bgp_dump->type);
+ } else if (addpath_capable) {
+ bgp_dump_header(obuf, MSG_PROTOCOL_BGP4MP,
+ BGP4MP_MESSAGE_ADDPATH, bgp_dump->type);
+ } else {
+ bgp_dump_header(obuf, MSG_PROTOCOL_BGP4MP, BGP4MP_MESSAGE,
+ bgp_dump->type);
+ }
+ bgp_dump_common(obuf, peer, 0);
+
+ /* Packet contents. */
+ stream_put(obuf, STREAM_DATA(packet), stream_get_endp(packet));
+
+ /* Set length. */
+ bgp_dump_set_size(obuf, MSG_PROTOCOL_BGP4MP);
+
+ /* Write to the stream. */
+ fwrite(STREAM_DATA(obuf), stream_get_endp(obuf), 1, bgp_dump->fp);
+ fflush(bgp_dump->fp);
+}
+
+/* Called from bgp_packet.c when BGP packet is received. */
+static int bgp_dump_packet(struct peer *peer, uint8_t type, bgp_size_t size,
+ struct stream *packet)
+{
+ /* bgp_dump_all. */
+ bgp_dump_packet_func(&bgp_dump_all, peer, packet);
+
+ /* bgp_dump_updates. */
+ if (type == BGP_MSG_UPDATE)
+ bgp_dump_packet_func(&bgp_dump_updates, peer, packet);
+ return 0;
+}
+
+static unsigned int bgp_dump_parse_time(const char *str)
+{
+ int i;
+ int len;
+ int seen_h;
+ int seen_m;
+ int time;
+ unsigned int total;
+
+ time = 0;
+ total = 0;
+ seen_h = 0;
+ seen_m = 0;
+ len = strlen(str);
+
+ for (i = 0; i < len; i++) {
+ if (isdigit((unsigned char)str[i])) {
+ time *= 10;
+ time += str[i] - '0';
+ } else if (str[i] == 'H' || str[i] == 'h') {
+ if (seen_h)
+ return 0;
+ if (seen_m)
+ return 0;
+ total += time * 60 * 60;
+ time = 0;
+ seen_h = 1;
+ } else if (str[i] == 'M' || str[i] == 'm') {
+ if (seen_m)
+ return 0;
+ total += time * 60;
+ time = 0;
+ seen_m = 1;
+ } else
+ return 0;
+ }
+ return total + time;
+}
+
+static int bgp_dump_set(struct vty *vty, struct bgp_dump *bgp_dump,
+ enum bgp_dump_type type, const char *path,
+ const char *interval_str)
+{
+ unsigned int interval;
+
+ /* Don't schedule duplicate dumps if the dump command is given twice */
+ if (bgp_dump->filename && strcmp(path, bgp_dump->filename) == 0
+ && type == bgp_dump->type) {
+ if (interval_str) {
+ if (bgp_dump->interval_str
+ && strcmp(bgp_dump->interval_str, interval_str)
+ == 0)
+ return CMD_SUCCESS;
+ } else {
+ if (!bgp_dump->interval_str)
+ return CMD_SUCCESS;
+ }
+ }
+
+ /* Removing previous config */
+ bgp_dump_unset(bgp_dump);
+
+ if (interval_str) {
+ /* Check interval string. */
+ interval = bgp_dump_parse_time(interval_str);
+ if (interval == 0) {
+ vty_out(vty, "Malformed interval string\n");
+ return CMD_WARNING_CONFIG_FAILED;
+ }
+
+ /* Setting interval string */
+ bgp_dump->interval_str =
+ XSTRDUP(MTYPE_BGP_DUMP_STR, interval_str);
+ } else {
+ interval = 0;
+ }
+
+ /* Set type. */
+ bgp_dump->type = type;
+
+ /* Set interval */
+ bgp_dump->interval = interval;
+
+ /* Set file name. */
+ bgp_dump->filename = XSTRDUP(MTYPE_BGP_DUMP_STR, path);
+
+ /* Create interval thread. */
+ bgp_dump_interval_add(bgp_dump, interval);
+
+ /* This should be called when interval is expired. */
+ bgp_dump_open_file(bgp_dump);
+
+ return CMD_SUCCESS;
+}
+
+static int bgp_dump_unset(struct bgp_dump *bgp_dump)
+{
+ /* Removing file name. */
+ XFREE(MTYPE_BGP_DUMP_STR, bgp_dump->filename);
+
+ /* Closing file. */
+ if (bgp_dump->fp) {
+ fclose(bgp_dump->fp);
+ bgp_dump->fp = NULL;
+ }
+
+ /* Removing interval event. */
+ THREAD_OFF(bgp_dump->t_interval);
+
+ bgp_dump->interval = 0;
+
+ /* Removing interval string. */
+ XFREE(MTYPE_BGP_DUMP_STR, bgp_dump->interval_str);
+
+ return CMD_SUCCESS;
+}
+
+DEFUN (dump_bgp_all,
+ dump_bgp_all_cmd,
+ "dump bgp <all|all-et|updates|updates-et|routes-mrt> PATH [INTERVAL]",
+ "Dump packet\n"
+ "BGP packet dump\n"
+ "Dump all BGP packets\nDump all BGP packets (Extended Timestamp Header)\n"
+ "Dump BGP updates only\nDump BGP updates only (Extended Timestamp Header)\n"
+ "Dump whole BGP routing table\n"
+ "Output filename\n"
+ "Interval of output\n")
+{
+ int idx_dump_routes = 2;
+ int idx_path = 3;
+ int idx_interval = 4;
+ int bgp_dump_type = 0;
+ const char *interval = NULL;
+ struct bgp_dump *bgp_dump_struct = NULL;
+ const struct bgp_dump_type_map *map = NULL;
+
+ for (map = bgp_dump_type_map; map->str; map++)
+ if (strmatch(argv[idx_dump_routes]->text, map->str))
+ bgp_dump_type = map->type;
+
+ switch (bgp_dump_type) {
+ case BGP_DUMP_ALL:
+ case BGP_DUMP_ALL_ET:
+ bgp_dump_struct = &bgp_dump_all;
+ break;
+ case BGP_DUMP_UPDATES:
+ case BGP_DUMP_UPDATES_ET:
+ bgp_dump_struct = &bgp_dump_updates;
+ break;
+ case BGP_DUMP_ROUTES:
+ default:
+ bgp_dump_struct = &bgp_dump_routes;
+ break;
+ }
+
+ /* When an interval is given */
+ if (argc == idx_interval + 1)
+ interval = argv[idx_interval]->arg;
+
+ return bgp_dump_set(vty, bgp_dump_struct, bgp_dump_type,
+ argv[idx_path]->arg, interval);
+}
+
+DEFUN (no_dump_bgp_all,
+ no_dump_bgp_all_cmd,
+ "no dump bgp <all|all-et|updates|updates-et|routes-mrt> [PATH [INTERVAL]]",
+ NO_STR
+ "Stop dump packet\n"
+ "Stop BGP packet dump\n"
+ "Stop dump process all\n"
+ "Stop dump process all-et\n"
+ "Stop dump process updates\n"
+ "Stop dump process updates-et\n"
+ "Stop dump process route-mrt\n"
+ "Output filename\n"
+ "Interval of output\n")
+{
+ int idx_dump_routes = 3;
+ int bgp_dump_type = 0;
+ const struct bgp_dump_type_map *map = NULL;
+ struct bgp_dump *bgp_dump_struct = NULL;
+
+ for (map = bgp_dump_type_map; map->str; map++)
+ if (strmatch(argv[idx_dump_routes]->text, map->str))
+ bgp_dump_type = map->type;
+
+ switch (bgp_dump_type) {
+ case BGP_DUMP_ALL:
+ case BGP_DUMP_ALL_ET:
+ bgp_dump_struct = &bgp_dump_all;
+ break;
+ case BGP_DUMP_UPDATES:
+ case BGP_DUMP_UPDATES_ET:
+ bgp_dump_struct = &bgp_dump_updates;
+ break;
+ case BGP_DUMP_ROUTES:
+ default:
+ bgp_dump_struct = &bgp_dump_routes;
+ break;
+ }
+
+ return bgp_dump_unset(bgp_dump_struct);
+}
+
+static int config_write_bgp_dump(struct vty *vty);
+/* BGP node structure. */
+static struct cmd_node bgp_dump_node = {
+ .name = "dump",
+ .node = DUMP_NODE,
+ .prompt = "",
+ .config_write = config_write_bgp_dump,
+};
+
+static int config_write_bgp_dump(struct vty *vty)
+{
+ if (bgp_dump_all.filename) {
+ const char *type_str = "all";
+ if (bgp_dump_all.type == BGP_DUMP_ALL_ET)
+ type_str = "all-et";
+
+ if (bgp_dump_all.interval_str)
+ vty_out(vty, "dump bgp %s %s %s\n", type_str,
+ bgp_dump_all.filename,
+ bgp_dump_all.interval_str);
+ else
+ vty_out(vty, "dump bgp %s %s\n", type_str,
+ bgp_dump_all.filename);
+ }
+ if (bgp_dump_updates.filename) {
+ const char *type_str = "updates";
+ if (bgp_dump_updates.type == BGP_DUMP_UPDATES_ET)
+ type_str = "updates-et";
+
+ if (bgp_dump_updates.interval_str)
+ vty_out(vty, "dump bgp %s %s %s\n", type_str,
+ bgp_dump_updates.filename,
+ bgp_dump_updates.interval_str);
+ else
+ vty_out(vty, "dump bgp %s %s\n", type_str,
+ bgp_dump_updates.filename);
+ }
+ if (bgp_dump_routes.filename) {
+ if (bgp_dump_routes.interval_str)
+ vty_out(vty, "dump bgp routes-mrt %s %s\n",
+ bgp_dump_routes.filename,
+ bgp_dump_routes.interval_str);
+ else
+ vty_out(vty, "dump bgp routes-mrt %s\n",
+ bgp_dump_routes.filename);
+ }
+ return 0;
+}
+
+/* Initialize BGP packet dump functionality. */
+void bgp_dump_init(void)
+{
+ memset(&bgp_dump_all, 0, sizeof(bgp_dump_all));
+ memset(&bgp_dump_updates, 0, sizeof(bgp_dump_updates));
+ memset(&bgp_dump_routes, 0, sizeof(bgp_dump_routes));
+
+ bgp_dump_obuf =
+ stream_new(BGP_MAX_PACKET_SIZE + BGP_MAX_PACKET_SIZE_OVERFLOW);
+
+ install_node(&bgp_dump_node);
+
+ install_element(CONFIG_NODE, &dump_bgp_all_cmd);
+ install_element(CONFIG_NODE, &no_dump_bgp_all_cmd);
+
+ hook_register(bgp_packet_dump, bgp_dump_packet);
+ hook_register(peer_status_changed, bgp_dump_state);
+}
+
+void bgp_dump_finish(void)
+{
+ bgp_dump_unset(&bgp_dump_all);
+ bgp_dump_unset(&bgp_dump_updates);
+ bgp_dump_unset(&bgp_dump_routes);
+
+ stream_free(bgp_dump_obuf);
+ bgp_dump_obuf = NULL;
+ hook_unregister(bgp_packet_dump, bgp_dump_packet);
+ hook_unregister(peer_status_changed, bgp_dump_state);
+}
diff --git a/bgpd/bgp_dump.h b/bgpd/bgp_dump.h
new file mode 100644
index 0000000..70e430a
--- /dev/null
+++ b/bgpd/bgp_dump.h
@@ -0,0 +1,64 @@
+/* BGP dump routine.
+ * Copyright (C) 1999 Kunihiro Ishiguro
+ *
+ * This file is part of GNU Zebra.
+ *
+ * GNU Zebra is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License as published by the
+ * Free Software Foundation; either version 2, or (at your option) any
+ * later version.
+ *
+ * GNU Zebra is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program; see the file COPYING; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
+ */
+
+#ifndef _QUAGGA_BGP_DUMP_H
+#define _QUAGGA_BGP_DUMP_H
+
+/* MRT compatible packet dump values. */
+/* type value */
+#define MSG_PROTOCOL_BGP4MP 16
+#define MSG_PROTOCOL_BGP4MP_ET 17
+
+/* subtype value */
+#define BGP4MP_STATE_CHANGE 0
+#define BGP4MP_MESSAGE 1
+#define BGP4MP_ENTRY 2
+#define BGP4MP_SNAPSHOT 3
+#define BGP4MP_MESSAGE_AS4 4
+#define BGP4MP_STATE_CHANGE_AS4 5
+#define BGP4MP_MESSAGE_ADDPATH 8
+#define BGP4MP_MESSAGE_AS4_ADDPATH 9
+#define BGP4MP_MESSAGE_LOCAL_ADDPATH 10
+#define BGP4MP_MESSAGE_AS4_LOCAL_ADDPATH 11
+
+#define BGP_DUMP_HEADER_SIZE 12
+#define BGP_DUMP_MSG_HEADER 40
+
+#define TABLE_DUMP_V2_PEER_INDEX_TABLE 1
+#define TABLE_DUMP_V2_RIB_IPV4_UNICAST 2
+#define TABLE_DUMP_V2_RIB_IPV4_MULTICAST 3
+#define TABLE_DUMP_V2_RIB_IPV6_UNICAST 4
+#define TABLE_DUMP_V2_RIB_IPV6_MULTICAST 5
+#define TABLE_DUMP_V2_RIB_IPV4_UNICAST_ADDPATH 8
+#define TABLE_DUMP_V2_RIB_IPV4_MULTICAST_ADDPATH 9
+#define TABLE_DUMP_V2_RIB_IPV6_UNICAST_ADDPATH 10
+#define TABLE_DUMP_V2_RIB_IPV6_MULTICAST_ADDPATH 11
+#define TABLE_DUMP_V2_RIB_GENERIC_ADDPATH 12
+
+#define TABLE_DUMP_V2_PEER_INDEX_TABLE_IP 0
+#define TABLE_DUMP_V2_PEER_INDEX_TABLE_IP6 1
+#define TABLE_DUMP_V2_PEER_INDEX_TABLE_AS2 0
+#define TABLE_DUMP_V2_PEER_INDEX_TABLE_AS4 2
+
+extern void bgp_dump_init(void);
+extern void bgp_dump_finish(void);
+extern int bgp_dump_state(struct peer *peer);
+
+#endif /* _QUAGGA_BGP_DUMP_H */
diff --git a/bgpd/bgp_ecommunity.c b/bgpd/bgp_ecommunity.c
new file mode 100644
index 0000000..2599bf2
--- /dev/null
+++ b/bgpd/bgp_ecommunity.c
@@ -0,0 +1,1716 @@
+/* BGP Extended Communities Attribute
+ * Copyright (C) 2000 Kunihiro Ishiguro <kunihiro@zebra.org>
+ *
+ * This file is part of GNU Zebra.
+ *
+ * GNU Zebra is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License as published by the
+ * Free Software Foundation; either version 2, or (at your option) any
+ * later version.
+ *
+ * GNU Zebra is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program; see the file COPYING; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
+ */
+
+#include <zebra.h>
+
+#include "hash.h"
+#include "memory.h"
+#include "prefix.h"
+#include "command.h"
+#include "queue.h"
+#include "filter.h"
+#include "jhash.h"
+#include "stream.h"
+
+#include "lib/printfrr.h"
+
+#include "bgpd/bgpd.h"
+#include "bgpd/bgp_ecommunity.h"
+#include "bgpd/bgp_lcommunity.h"
+#include "bgpd/bgp_aspath.h"
+#include "bgpd/bgp_flowspec_private.h"
+#include "bgpd/bgp_pbr.h"
+
+/* struct used to dump the rate contained in FS set traffic-rate EC */
+union traffic_rate {
+ float rate_float;
+ uint8_t rate_byte[4];
+};
+
+/* Hash of community attribute. */
+static struct hash *ecomhash;
+
+/* Allocate a new ecommunities. */
+struct ecommunity *ecommunity_new(void)
+{
+ struct ecommunity *ecom;
+
+ ecom = (struct ecommunity *)XCALLOC(MTYPE_ECOMMUNITY,
+ sizeof(struct ecommunity));
+ ecom->unit_size = ECOMMUNITY_SIZE;
+ return ecom;
+}
+
+void ecommunity_strfree(char **s)
+{
+ XFREE(MTYPE_ECOMMUNITY_STR, *s);
+}
+
+/* Free ecommunities. */
+void ecommunity_free(struct ecommunity **ecom)
+{
+ if (!(*ecom))
+ return;
+
+ XFREE(MTYPE_ECOMMUNITY_VAL, (*ecom)->val);
+ XFREE(MTYPE_ECOMMUNITY_STR, (*ecom)->str);
+ XFREE(MTYPE_ECOMMUNITY, *ecom);
+}
+
+static void ecommunity_hash_free(struct ecommunity *ecom)
+{
+ ecommunity_free(&ecom);
+}
+
+
+/* Add a new Extended Communities value to Extended Communities
+ Attribute structure. When the value is already exists in the
+ structure, we don't add the value. Newly added value is sorted by
+ numerical order. When the value is added to the structure return 1
+ else return 0.
+ The additional parameters 'unique' and 'overwrite' ensure a particular
+ extended community (based on type and sub-type) is present only
+ once and whether the new value should replace what is existing or
+ not.
+*/
+static bool ecommunity_add_val_internal(struct ecommunity *ecom,
+ const void *eval,
+ bool unique, bool overwrite,
+ uint8_t ecom_size)
+{
+ uint32_t c, ins_idx;
+ const struct ecommunity_val *eval4 = (struct ecommunity_val *)eval;
+ const struct ecommunity_val_ipv6 *eval6 =
+ (struct ecommunity_val_ipv6 *)eval;
+
+ /* When this is fist value, just add it. */
+ if (ecom->val == NULL) {
+ ecom->size = 1;
+ ecom->val = XMALLOC(MTYPE_ECOMMUNITY_VAL,
+ ecom_length_size(ecom, ecom_size));
+ memcpy(ecom->val, eval, ecom_size);
+ return true;
+ }
+
+ /* If the value already exists in the structure return 0. */
+ /* check also if the extended community itself exists. */
+ c = 0;
+
+ ins_idx = UINT32_MAX;
+ for (uint8_t *p = ecom->val; c < ecom->size;
+ p += ecom_size, c++) {
+ if (unique) {
+ if (ecom_size == ECOMMUNITY_SIZE) {
+ if (p[0] == eval4->val[0] &&
+ p[1] == eval4->val[1]) {
+ if (overwrite) {
+ memcpy(p, eval4->val,
+ ecom_size);
+ return true;
+ }
+ return false;
+ }
+ } else {
+ if (p[0] == eval6->val[0] &&
+ p[1] == eval6->val[1]) {
+ if (overwrite) {
+ memcpy(p, eval6->val,
+ ecom_size);
+ return true;
+ }
+ return false;
+ }
+ }
+ }
+ int ret = memcmp(p, eval, ecom_size);
+ if (ret == 0)
+ return false;
+ if (ret > 0) {
+ if (!unique)
+ break;
+ if (ins_idx == UINT32_MAX)
+ ins_idx = c;
+ }
+ }
+
+ if (ins_idx == UINT32_MAX)
+ ins_idx = c;
+
+ /* Add the value to the structure with numerical sorting. */
+ ecom->size++;
+ ecom->val = XREALLOC(MTYPE_ECOMMUNITY_VAL, ecom->val,
+ ecom_length_size(ecom, ecom_size));
+
+ memmove(ecom->val + ((ins_idx + 1) * ecom_size),
+ ecom->val + (ins_idx * ecom_size),
+ (ecom->size - 1 - ins_idx) * ecom_size);
+ memcpy(ecom->val + (ins_idx * ecom_size),
+ eval, ecom_size);
+
+ return true;
+}
+
+/* Add a new Extended Communities value to Extended Communities
+ * Attribute structure. When the value is already exists in the
+ * structure, we don't add the value. Newly added value is sorted by
+ * numerical order. When the value is added to the structure return 1
+ * else return 0.
+ */
+bool ecommunity_add_val(struct ecommunity *ecom, struct ecommunity_val *eval,
+ bool unique, bool overwrite)
+{
+ return ecommunity_add_val_internal(ecom, (const void *)eval, unique,
+ overwrite, ECOMMUNITY_SIZE);
+}
+
+bool ecommunity_add_val_ipv6(struct ecommunity *ecom,
+ struct ecommunity_val_ipv6 *eval,
+ bool unique, bool overwrite)
+{
+ return ecommunity_add_val_internal(ecom, (const void *)eval, unique,
+ overwrite, IPV6_ECOMMUNITY_SIZE);
+}
+
+static struct ecommunity *
+ecommunity_uniq_sort_internal(struct ecommunity *ecom,
+ unsigned short ecom_size)
+{
+ uint32_t i;
+ struct ecommunity *new;
+ const void *eval;
+
+ if (!ecom)
+ return NULL;
+
+ new = ecommunity_new();
+ new->unit_size = ecom_size;
+ new->disable_ieee_floating = ecom->disable_ieee_floating;
+
+ for (i = 0; i < ecom->size; i++) {
+ eval = (void *)(ecom->val + (i * ecom_size));
+ ecommunity_add_val_internal(new, eval, false, false, ecom_size);
+ }
+ return new;
+}
+
+/* This function takes pointer to Extended Communites structure then
+ * create a new Extended Communities structure by uniq and sort each
+ * Extended Communities value.
+ */
+struct ecommunity *ecommunity_uniq_sort(struct ecommunity *ecom)
+{
+ return ecommunity_uniq_sort_internal(ecom, ECOMMUNITY_SIZE);
+}
+
+/* Parse Extended Communites Attribute in BGP packet. */
+static struct ecommunity *ecommunity_parse_internal(uint8_t *pnt,
+ unsigned short length,
+ unsigned short size_ecom,
+ bool disable_ieee_floating)
+{
+ struct ecommunity tmp;
+ struct ecommunity *new;
+
+ /* Length check. */
+ if (length % size_ecom)
+ return NULL;
+
+ /* Prepare tmporary structure for making a new Extended Communities
+ Attribute. */
+ tmp.size = length / size_ecom;
+ tmp.val = pnt;
+ tmp.disable_ieee_floating = disable_ieee_floating;
+
+ /* Create a new Extended Communities Attribute by uniq and sort each
+ Extended Communities value */
+ new = ecommunity_uniq_sort_internal(&tmp, size_ecom);
+
+ return ecommunity_intern(new);
+}
+
+struct ecommunity *ecommunity_parse(uint8_t *pnt, unsigned short length,
+ bool disable_ieee_floating)
+{
+ return ecommunity_parse_internal(pnt, length, ECOMMUNITY_SIZE,
+ disable_ieee_floating);
+}
+
+struct ecommunity *ecommunity_parse_ipv6(uint8_t *pnt, unsigned short length,
+ bool disable_ieee_floating)
+{
+ return ecommunity_parse_internal(pnt, length, IPV6_ECOMMUNITY_SIZE,
+ disable_ieee_floating);
+}
+
+/* Duplicate the Extended Communities Attribute structure. */
+struct ecommunity *ecommunity_dup(struct ecommunity *ecom)
+{
+ struct ecommunity *new;
+
+ new = XCALLOC(MTYPE_ECOMMUNITY, sizeof(struct ecommunity));
+ new->size = ecom->size;
+ new->unit_size = ecom->unit_size;
+ if (new->size) {
+ new->val = XMALLOC(MTYPE_ECOMMUNITY_VAL,
+ ecom->size * ecom->unit_size);
+ memcpy(new->val, ecom->val,
+ (size_t)ecom->size * (size_t)ecom->unit_size);
+ } else
+ new->val = NULL;
+ return new;
+}
+
+/* Return string representation of ecommunities attribute. */
+char *ecommunity_str(struct ecommunity *ecom)
+{
+ if (!ecom->str)
+ ecom->str =
+ ecommunity_ecom2str(ecom, ECOMMUNITY_FORMAT_DISPLAY, 0);
+ return ecom->str;
+}
+
+/* Merge two Extended Communities Attribute structure. */
+struct ecommunity *ecommunity_merge(struct ecommunity *ecom1,
+ struct ecommunity *ecom2)
+{
+ ecom1->val = XREALLOC(MTYPE_ECOMMUNITY_VAL, ecom1->val,
+ (size_t)(ecom1->size + ecom2->size)
+ * (size_t)ecom1->unit_size);
+
+ memcpy(ecom1->val + (ecom1->size * ecom1->unit_size), ecom2->val,
+ (size_t)ecom2->size * (size_t)ecom1->unit_size);
+ ecom1->size += ecom2->size;
+
+ return ecom1;
+}
+
+/* Intern Extended Communities Attribute. */
+struct ecommunity *ecommunity_intern(struct ecommunity *ecom)
+{
+ struct ecommunity *find;
+
+ assert(ecom->refcnt == 0);
+ find = (struct ecommunity *)hash_get(ecomhash, ecom, hash_alloc_intern);
+ if (find != ecom)
+ ecommunity_free(&ecom);
+
+ find->refcnt++;
+
+ if (!find->str)
+ find->str =
+ ecommunity_ecom2str(find, ECOMMUNITY_FORMAT_DISPLAY, 0);
+
+ return find;
+}
+
+/* Unintern Extended Communities Attribute. */
+void ecommunity_unintern(struct ecommunity **ecom)
+{
+ struct ecommunity *ret;
+
+ if (!*ecom)
+ return;
+
+ if ((*ecom)->refcnt)
+ (*ecom)->refcnt--;
+
+ /* Pull off from hash. */
+ if ((*ecom)->refcnt == 0) {
+ /* Extended community must be in the hash. */
+ ret = (struct ecommunity *)hash_release(ecomhash, *ecom);
+ assert(ret != NULL);
+
+ ecommunity_free(ecom);
+ }
+}
+
+/* Utinity function to make hash key. */
+unsigned int ecommunity_hash_make(const void *arg)
+{
+ const struct ecommunity *ecom = arg;
+ int size = ecom->size * ecom->unit_size;
+
+ return jhash(ecom->val, size, 0x564321ab);
+}
+
+/* Compare two Extended Communities Attribute structure. */
+bool ecommunity_cmp(const void *arg1, const void *arg2)
+{
+ const struct ecommunity *ecom1 = arg1;
+ const struct ecommunity *ecom2 = arg2;
+
+ if (ecom1 == NULL && ecom2 == NULL)
+ return true;
+
+ if (ecom1 == NULL || ecom2 == NULL)
+ return false;
+
+ if (ecom1->unit_size != ecom2->unit_size)
+ return false;
+
+ return (ecom1->size == ecom2->size
+ && memcmp(ecom1->val, ecom2->val, ecom1->size *
+ ecom1->unit_size) == 0);
+}
+
+/* Initialize Extended Comminities related hash. */
+void ecommunity_init(void)
+{
+ ecomhash = hash_create(ecommunity_hash_make, ecommunity_cmp,
+ "BGP ecommunity hash");
+}
+
+void ecommunity_finish(void)
+{
+ hash_clean(ecomhash, (void (*)(void *))ecommunity_hash_free);
+ hash_free(ecomhash);
+ ecomhash = NULL;
+}
+
+/* Extended Communities token enum. */
+enum ecommunity_token {
+ ecommunity_token_unknown = 0,
+ ecommunity_token_rt,
+ ecommunity_token_soo,
+ ecommunity_token_val,
+ ecommunity_token_rt6,
+ ecommunity_token_val6,
+};
+
+static const char *ecommunity_origin_validation_state2str(
+ enum ecommunity_origin_validation_states state)
+{
+ switch (state) {
+ case ECOMMUNITY_ORIGIN_VALIDATION_STATE_VALID:
+ return "valid";
+ case ECOMMUNITY_ORIGIN_VALIDATION_STATE_NOTFOUND:
+ return "not-found";
+ case ECOMMUNITY_ORIGIN_VALIDATION_STATE_INVALID:
+ return "invalid";
+ case ECOMMUNITY_ORIGIN_VALIDATION_STATE_NOTUSED:
+ return "not-used";
+ }
+
+ return "ERROR";
+}
+
+static void ecommunity_origin_validation_state_str(char *buf, size_t bufsz,
+ uint8_t *ptr)
+{
+ /* Origin Validation State is encoded in the last octet
+ *
+ * 0 1 2 3
+ * 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1
+ * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+ * | 0x43 | 0x00 | Reserved |
+ * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+ * | Reserved |validationstate|
+ * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+ */
+ uint8_t state = *(ptr + ECOMMUNITY_SIZE - 3);
+
+ snprintf(buf, bufsz, "OVS:%s",
+ ecommunity_origin_validation_state2str(state));
+
+ (void)ptr; /* consume value */
+}
+
+static int ecommunity_encode_internal(uint8_t type, uint8_t sub_type,
+ int trans, as_t as,
+ struct in_addr *ip,
+ struct in6_addr *ip6,
+ uint32_t val,
+ void *eval_ptr)
+{
+ struct ecommunity_val *eval = (struct ecommunity_val *)eval_ptr;
+ struct ecommunity_val_ipv6 *eval6 =
+ (struct ecommunity_val_ipv6 *)eval_ptr;
+
+ assert(eval);
+ if (type == ECOMMUNITY_ENCODE_AS) {
+ if (as > BGP_AS_MAX)
+ return -1;
+ } else if (type == ECOMMUNITY_ENCODE_IP
+ || type == ECOMMUNITY_ENCODE_AS4) {
+ if (val > UINT16_MAX)
+ return -1;
+ } else if (type == ECOMMUNITY_ENCODE_TRANS_EXP &&
+ sub_type == ECOMMUNITY_FLOWSPEC_REDIRECT_IPV6 &&
+ (!ip6 || val > UINT16_MAX)) {
+ return -1;
+ }
+
+ /* Fill in the values. */
+ eval->val[0] = type;
+ if (!trans)
+ eval->val[0] |= ECOMMUNITY_FLAG_NON_TRANSITIVE;
+ eval->val[1] = sub_type;
+ if (type == ECOMMUNITY_ENCODE_AS) {
+ eval->val[2] = (as >> 8) & 0xff;
+ eval->val[3] = as & 0xff;
+ eval->val[4] = (val >> 24) & 0xff;
+ eval->val[5] = (val >> 16) & 0xff;
+ eval->val[6] = (val >> 8) & 0xff;
+ eval->val[7] = val & 0xff;
+ } else if (type == ECOMMUNITY_ENCODE_IP) {
+ memcpy(&eval->val[2], ip, sizeof(struct in_addr));
+ eval->val[6] = (val >> 8) & 0xff;
+ eval->val[7] = val & 0xff;
+ } else if (type == ECOMMUNITY_ENCODE_TRANS_EXP &&
+ sub_type == ECOMMUNITY_FLOWSPEC_REDIRECT_IPV6) {
+ memcpy(&eval6->val[2], ip6, sizeof(struct in6_addr));
+ eval6->val[18] = (val >> 8) & 0xff;
+ eval6->val[19] = val & 0xff;
+ } else {
+ eval->val[2] = (as >> 24) & 0xff;
+ eval->val[3] = (as >> 16) & 0xff;
+ eval->val[4] = (as >> 8) & 0xff;
+ eval->val[5] = as & 0xff;
+ eval->val[6] = (val >> 8) & 0xff;
+ eval->val[7] = val & 0xff;
+ }
+
+ return 0;
+}
+
+/*
+ * Encode BGP extended community from passed values. Supports types
+ * defined in RFC 4360 and well-known sub-types.
+ */
+static int ecommunity_encode(uint8_t type, uint8_t sub_type, int trans, as_t as,
+ struct in_addr ip, uint32_t val,
+ struct ecommunity_val *eval)
+{
+ return ecommunity_encode_internal(type, sub_type, trans, as,
+ &ip, NULL, val, (void *)eval);
+}
+
+/* Get next Extended Communities token from the string. */
+static const char *ecommunity_gettoken(const char *str,
+ void *eval_ptr,
+ enum ecommunity_token *token)
+{
+ int ret;
+ int dot = 0;
+ int digit = 0;
+ int separator = 0;
+ const char *p = str;
+ char *endptr;
+ struct in_addr ip;
+ struct in6_addr ip6;
+ as_t as = 0;
+ uint32_t val = 0;
+ uint8_t ecomm_type;
+ char buf[INET_ADDRSTRLEN + 1];
+ struct ecommunity_val *eval = (struct ecommunity_val *)eval_ptr;
+ uint64_t tmp_as = 0;
+
+ /* Skip white space. */
+ while (isspace((unsigned char)*p)) {
+ p++;
+ str++;
+ }
+
+ /* Check the end of the line. */
+ if (*p == '\0')
+ return NULL;
+
+ /* "rt" and "soo" keyword parse. */
+ if (!isdigit((unsigned char)*p)) {
+ /* "rt" match check. */
+ if (tolower((unsigned char)*p) == 'r') {
+ p++;
+ if (tolower((unsigned char)*p) == 't') {
+ p++;
+ if (*p != '\0' && tolower((int)*p) == '6')
+ *token = ecommunity_token_rt6;
+ else
+ *token = ecommunity_token_rt;
+ return p;
+ }
+ if (isspace((unsigned char)*p) || *p == '\0') {
+ *token = ecommunity_token_rt;
+ return p;
+ }
+ goto error;
+ }
+ /* "soo" match check. */
+ else if (tolower((unsigned char)*p) == 's') {
+ p++;
+ if (tolower((unsigned char)*p) == 'o') {
+ p++;
+ if (tolower((unsigned char)*p) == 'o') {
+ p++;
+ *token = ecommunity_token_soo;
+ return p;
+ }
+ if (isspace((unsigned char)*p) || *p == '\0') {
+ *token = ecommunity_token_soo;
+ return p;
+ }
+ goto error;
+ }
+ if (isspace((unsigned char)*p) || *p == '\0') {
+ *token = ecommunity_token_soo;
+ return p;
+ }
+ goto error;
+ }
+ goto error;
+ }
+
+ /* What a mess, there are several possibilities:
+ *
+ * a) A.B.C.D:MN
+ * b) EF:OPQR
+ * c) GHJK:MN
+ * d) <IPV6>:MN (only with rt6)
+ *
+ * A.B.C.D: Four Byte IP
+ * EF: Two byte ASN
+ * GHJK: Four-byte ASN
+ * MN: Two byte value
+ * OPQR: Four byte value
+ *
+ */
+ /* IPv6 case : look for last ':' */
+ if (*token == ecommunity_token_rt6 ||
+ *token == ecommunity_token_val6) {
+ char *limit;
+
+ limit = endptr = strrchr(p, ':');
+ if (!endptr)
+ goto error;
+
+ endptr++;
+ errno = 0;
+ tmp_as = strtoul(endptr, &endptr, 10);
+ /* 'unsigned long' is a uint64 on 64-bit
+ * systems, and uint32 on 32-bit systems. So for
+ * 64-bit we can just directly check the value
+ * against BGP_AS4_MAX/UINT32_MAX, and for
+ * 32-bit we can check for errno (set to ERANGE
+ * upon overflow).
+ */
+ if (*endptr != '\0' || tmp_as == BGP_AS4_MAX || errno)
+ goto error;
+ as = (as_t)tmp_as;
+
+ memcpy(buf, p, (limit - p));
+ buf[limit - p] = '\0';
+ ret = inet_pton(AF_INET6, buf, &ip6);
+ if (ret == 0)
+ goto error;
+
+ ecomm_type = ECOMMUNITY_ENCODE_TRANS_EXP;
+ if (ecommunity_encode_internal(ecomm_type,
+ ECOMMUNITY_FLOWSPEC_REDIRECT_IPV6,
+ 1, 0, NULL, &ip6, as, eval_ptr))
+ goto error;
+
+ *token = ecommunity_token_val6;
+ while (isdigit((int)*p) || *p == ':' || *p == '.') {
+ p++;
+ }
+ return p;
+ }
+ while (isdigit((unsigned char)*p) || *p == ':' || *p == '.') {
+ if (*p == ':') {
+ if (separator)
+ goto error;
+
+ separator = 1;
+ digit = 0;
+
+ if ((p - str) > INET_ADDRSTRLEN)
+ goto error;
+ memset(buf, 0, INET_ADDRSTRLEN + 1);
+ memcpy(buf, str, p - str);
+
+ if (dot) {
+ /* Parsing A.B.C.D in:
+ * A.B.C.D:MN
+ */
+ ret = inet_aton(buf, &ip);
+ if (ret == 0)
+ goto error;
+ } else {
+ /* ASN */
+ errno = 0;
+ tmp_as = strtoul(buf, &endptr, 10);
+ /* 'unsigned long' is a uint64 on 64-bit
+ * systems, and uint32 on 32-bit systems. So for
+ * 64-bit we can just directly check the value
+ * against BGP_AS4_MAX/UINT32_MAX, and for
+ * 32-bit we can check for errno (set to ERANGE
+ * upon overflow).
+ */
+ if (*endptr != '\0' || tmp_as > BGP_AS4_MAX ||
+ errno)
+ goto error;
+ as = (as_t)tmp_as;
+ }
+ } else if (*p == '.') {
+ if (separator)
+ goto error;
+ dot++;
+ if (dot > 4)
+ goto error;
+ } else {
+ digit = 1;
+
+ /* We're past the IP/ASN part */
+ if (separator) {
+ val *= 10;
+ val += (*p - '0');
+ }
+ }
+ p++;
+ }
+
+ /* Low digit part must be there. */
+ if (!digit || !separator)
+ goto error;
+
+ /* Encode result into extended community. */
+ if (dot)
+ ecomm_type = ECOMMUNITY_ENCODE_IP;
+ else if (as > BGP_AS_MAX)
+ ecomm_type = ECOMMUNITY_ENCODE_AS4;
+ else
+ ecomm_type = ECOMMUNITY_ENCODE_AS;
+ if (ecommunity_encode(ecomm_type, 0, 1, as, ip, val, eval))
+ goto error;
+ *token = ecommunity_token_val;
+ return p;
+
+error:
+ *token = ecommunity_token_unknown;
+ return p;
+}
+
+static struct ecommunity *ecommunity_str2com_internal(const char *str, int type,
+ int keyword_included,
+ bool is_ipv6_extcomm)
+{
+ struct ecommunity *ecom = NULL;
+ enum ecommunity_token token = ecommunity_token_unknown;
+ struct ecommunity_val_ipv6 eval;
+ int keyword = 0;
+
+ if (is_ipv6_extcomm)
+ token = ecommunity_token_rt6;
+ while ((str = ecommunity_gettoken(str, (void *)&eval, &token))) {
+ switch (token) {
+ case ecommunity_token_rt:
+ case ecommunity_token_soo:
+ if (!keyword_included || keyword) {
+ if (ecom)
+ ecommunity_free(&ecom);
+ return NULL;
+ }
+ keyword = 1;
+
+ if (token == ecommunity_token_rt ||
+ token == ecommunity_token_rt6) {
+ type = ECOMMUNITY_ROUTE_TARGET;
+ }
+ if (token == ecommunity_token_soo) {
+ type = ECOMMUNITY_SITE_ORIGIN;
+ }
+ break;
+ case ecommunity_token_val:
+ if (keyword_included) {
+ if (!keyword) {
+ ecommunity_free(&ecom);
+ return NULL;
+ }
+ keyword = 0;
+ }
+ if (ecom == NULL)
+ ecom = ecommunity_new();
+ eval.val[1] = type;
+ ecommunity_add_val_internal(ecom, (void *)&eval,
+ false, false,
+ ecom->unit_size);
+ break;
+ case ecommunity_token_val6:
+ if (keyword_included) {
+ if (!keyword) {
+ ecommunity_free(&ecom);
+ return NULL;
+ }
+ keyword = 0;
+ }
+ if (ecom == NULL)
+ ecom = ecommunity_new();
+ ecom->unit_size = IPV6_ECOMMUNITY_SIZE;
+ eval.val[1] = type;
+ ecommunity_add_val_internal(ecom, (void *)&eval, false, false,
+ ecom->unit_size);
+ break;
+ case ecommunity_token_unknown:
+ default:
+ if (ecom)
+ ecommunity_free(&ecom);
+ return NULL;
+ }
+ }
+ return ecom;
+}
+
+/* Convert string to extended community attribute.
+ *
+ * When type is already known, please specify both str and type. str
+ * should not include keyword such as "rt" and "soo". Type is
+ * ECOMMUNITY_ROUTE_TARGET or ECOMMUNITY_SITE_ORIGIN.
+ * keyword_included should be zero.
+ *
+ * For example route-map's "set extcommunity" command case:
+ *
+ * "rt 100:1 100:2 100:3" -> str = "100:1 100:2 100:3"
+ * type = ECOMMUNITY_ROUTE_TARGET
+ * keyword_included = 0
+ *
+ * "soo 100:1" -> str = "100:1"
+ * type = ECOMMUNITY_SITE_ORIGIN
+ * keyword_included = 0
+ *
+ * When string includes keyword for each extended community value.
+ * Please specify keyword_included as non-zero value.
+ *
+ * For example standard extcommunity-list case:
+ *
+ * "rt 100:1 rt 100:2 soo 100:1" -> str = "rt 100:1 rt 100:2 soo 100:1"
+ * type = 0
+ * keyword_include = 1
+ */
+struct ecommunity *ecommunity_str2com(const char *str, int type,
+ int keyword_included)
+{
+ return ecommunity_str2com_internal(str, type,
+ keyword_included, false);
+}
+
+struct ecommunity *ecommunity_str2com_ipv6(const char *str, int type,
+ int keyword_included)
+{
+ return ecommunity_str2com_internal(str, type,
+ keyword_included, true);
+}
+
+static int ecommunity_rt_soo_str_internal(char *buf, size_t bufsz,
+ const uint8_t *pnt, int type,
+ int sub_type, int format,
+ unsigned short ecom_size)
+{
+ int len = 0;
+ const char *prefix;
+ char buf_local[INET6_ADDRSTRLEN];
+
+ /* For parse Extended Community attribute tupple. */
+ struct ecommunity_as eas;
+ struct ecommunity_ip eip;
+ struct ecommunity_ip6 eip6;
+
+ /* Determine prefix for string, if any. */
+ switch (format) {
+ case ECOMMUNITY_FORMAT_COMMUNITY_LIST:
+ prefix = (sub_type == ECOMMUNITY_ROUTE_TARGET ? "rt " : "soo ");
+ break;
+ case ECOMMUNITY_FORMAT_DISPLAY:
+ prefix = (sub_type == ECOMMUNITY_ROUTE_TARGET ? "RT:" : "SoO:");
+ break;
+ case ECOMMUNITY_FORMAT_ROUTE_MAP:
+ prefix = "";
+ break;
+ default:
+ prefix = "";
+ break;
+ }
+
+ /* Put string into buffer. */
+ if (type == ECOMMUNITY_ENCODE_AS4) {
+ pnt = ptr_get_be32(pnt, &eas.as);
+ eas.val = (*pnt++ << 8);
+ eas.val |= (*pnt++);
+
+ len = snprintf(buf, bufsz, "%s%u:%u", prefix, eas.as, eas.val);
+ } else if (type == ECOMMUNITY_ENCODE_AS) {
+ if (ecom_size == ECOMMUNITY_SIZE) {
+ eas.as = (*pnt++ << 8);
+ eas.as |= (*pnt++);
+ pnt = ptr_get_be32(pnt, &eas.val);
+
+ len = snprintf(buf, bufsz, "%s%u:%u", prefix, eas.as,
+ eas.val);
+ } else {
+ /* this is an IPv6 ext community
+ * first 16 bytes stands for IPv6 addres
+ */
+ memcpy(&eip6.ip, pnt, 16);
+ pnt += 16;
+ eip6.val = (*pnt++ << 8);
+ eip6.val |= (*pnt++);
+
+ inet_ntop(AF_INET6, &eip6.ip, buf_local,
+ sizeof(buf_local));
+ len = snprintf(buf, bufsz, "%s%s:%u", prefix,
+ buf_local, eip6.val);
+ }
+ } else if (type == ECOMMUNITY_ENCODE_IP) {
+ memcpy(&eip.ip, pnt, 4);
+ pnt += 4;
+ eip.val = (*pnt++ << 8);
+ eip.val |= (*pnt++);
+
+ len = snprintfrr(buf, bufsz, "%s%pI4:%u", prefix, &eip.ip,
+ eip.val);
+ }
+
+ /* consume value */
+ (void)pnt;
+
+ return len;
+}
+
+static int ecommunity_rt_soo_str(char *buf, size_t bufsz, const uint8_t *pnt,
+ int type, int sub_type, int format)
+{
+ return ecommunity_rt_soo_str_internal(buf, bufsz, pnt, type,
+ sub_type, format,
+ ECOMMUNITY_SIZE);
+}
+
+/* Helper function to convert IEEE-754 Floating Point to uint32 */
+static uint32_t ieee_float_uint32_to_uint32(uint32_t u)
+{
+ union {
+ float r;
+ uint32_t d;
+ } f = {.d = u};
+
+ return (uint32_t)f.r;
+}
+
+static int ecommunity_lb_str(char *buf, size_t bufsz, const uint8_t *pnt,
+ bool disable_ieee_floating)
+{
+ int len = 0;
+ as_t as;
+ uint32_t bw_tmp, bw;
+ char bps_buf[20] = {0};
+
+#define ONE_GBPS_BYTES (1000 * 1000 * 1000 / 8)
+#define ONE_MBPS_BYTES (1000 * 1000 / 8)
+#define ONE_KBPS_BYTES (1000 / 8)
+
+ as = (*pnt++ << 8);
+ as |= (*pnt++);
+ (void)ptr_get_be32(pnt, &bw_tmp);
+
+ bw = disable_ieee_floating ? bw_tmp
+ : ieee_float_uint32_to_uint32(bw_tmp);
+
+ if (bw >= ONE_GBPS_BYTES)
+ snprintf(bps_buf, sizeof(bps_buf), "%.3f Gbps",
+ (float)(bw / ONE_GBPS_BYTES));
+ else if (bw >= ONE_MBPS_BYTES)
+ snprintf(bps_buf, sizeof(bps_buf), "%.3f Mbps",
+ (float)(bw / ONE_MBPS_BYTES));
+ else if (bw >= ONE_KBPS_BYTES)
+ snprintf(bps_buf, sizeof(bps_buf), "%.3f Kbps",
+ (float)(bw / ONE_KBPS_BYTES));
+ else
+ snprintf(bps_buf, sizeof(bps_buf), "%u bps", bw * 8);
+
+ len = snprintf(buf, bufsz, "LB:%u:%u (%s)", as, bw, bps_buf);
+ return len;
+}
+
+/* Convert extended community attribute to string.
+
+ Due to historical reason of industry standard implementation, there
+ are three types of format.
+
+ route-map set extcommunity format
+ "rt 100:1 100:2soo 100:3"
+
+ extcommunity-list
+ "rt 100:1 rt 100:2 soo 100:3show [ip] bgp" and extcommunity-list regular expression matching
+ "RT:100:1 RT:100:2 SoO:100:3"
+
+ For each formath please use below definition for format:
+
+ ECOMMUNITY_FORMAT_ROUTE_MAP
+ ECOMMUNITY_FORMAT_COMMUNITY_LIST
+ ECOMMUNITY_FORMAT_DISPLAY
+
+ Filter is added to display only ECOMMUNITY_ROUTE_TARGET in some cases.
+ 0 value displays all
+*/
+char *ecommunity_ecom2str(struct ecommunity *ecom, int format, int filter)
+{
+ uint32_t i;
+ uint8_t *pnt;
+ uint8_t type = 0;
+ uint8_t sub_type = 0;
+ int str_size;
+ char *str_buf;
+
+ if (!ecom || ecom->size == 0)
+ return XCALLOC(MTYPE_ECOMMUNITY_STR, 1);
+
+ /* ecom strlen + space + null term */
+ str_size = (ecom->size * (ECOMMUNITY_STRLEN + 1)) + 1;
+ str_buf = XCALLOC(MTYPE_ECOMMUNITY_STR, str_size);
+
+ char encbuf[128];
+
+ for (i = 0; i < ecom->size; i++) {
+ int unk_ecom = 0;
+ memset(encbuf, 0x00, sizeof(encbuf));
+
+ /* Space between each value. */
+ if (i > 0)
+ strlcat(str_buf, " ", str_size);
+
+ /* Retrieve value field */
+ pnt = ecom->val + (i * ecom->unit_size);
+
+ /* High-order octet is the type */
+ type = *pnt++;
+
+ if (type == ECOMMUNITY_ENCODE_AS || type == ECOMMUNITY_ENCODE_IP
+ || type == ECOMMUNITY_ENCODE_AS4) {
+ /* Low-order octet of type. */
+ sub_type = *pnt++;
+ if (sub_type != ECOMMUNITY_ROUTE_TARGET
+ && sub_type != ECOMMUNITY_SITE_ORIGIN) {
+ if (sub_type ==
+ ECOMMUNITY_FLOWSPEC_REDIRECT_IPV4 &&
+ type == ECOMMUNITY_ENCODE_IP) {
+ struct in_addr *ipv4 =
+ (struct in_addr *)pnt;
+ char ipv4str[INET_ADDRSTRLEN];
+
+ inet_ntop(AF_INET, ipv4,
+ ipv4str,
+ INET_ADDRSTRLEN);
+ snprintf(encbuf, sizeof(encbuf),
+ "NH:%s:%d", ipv4str, pnt[5]);
+ } else if (sub_type ==
+ ECOMMUNITY_LINK_BANDWIDTH &&
+ type == ECOMMUNITY_ENCODE_AS) {
+ ecommunity_lb_str(
+ encbuf, sizeof(encbuf), pnt,
+ ecom->disable_ieee_floating);
+ } else
+ unk_ecom = 1;
+ } else {
+ ecommunity_rt_soo_str(encbuf, sizeof(encbuf),
+ pnt, type, sub_type,
+ format);
+ }
+ } else if (type == ECOMMUNITY_ENCODE_OPAQUE) {
+ if (filter == ECOMMUNITY_ROUTE_TARGET)
+ continue;
+ if (*pnt == ECOMMUNITY_OPAQUE_SUBTYPE_ENCAP) {
+ uint16_t tunneltype;
+ memcpy(&tunneltype, pnt + 5, 2);
+ tunneltype = ntohs(tunneltype);
+
+ snprintf(encbuf, sizeof(encbuf), "ET:%d",
+ tunneltype);
+ } else if (*pnt == ECOMMUNITY_EVPN_SUBTYPE_DEF_GW) {
+ strlcpy(encbuf, "Default Gateway",
+ sizeof(encbuf));
+ } else {
+ unk_ecom = 1;
+ }
+ } else if (type == ECOMMUNITY_ENCODE_EVPN) {
+ if (filter == ECOMMUNITY_ROUTE_TARGET)
+ continue;
+ if (*pnt == ECOMMUNITY_EVPN_SUBTYPE_ROUTERMAC) {
+ struct ethaddr rmac;
+ pnt++;
+ memcpy(&rmac, pnt, ETH_ALEN);
+
+ snprintf(encbuf, sizeof(encbuf),
+ "Rmac:%02x:%02x:%02x:%02x:%02x:%02x",
+ (uint8_t)rmac.octet[0],
+ (uint8_t)rmac.octet[1],
+ (uint8_t)rmac.octet[2],
+ (uint8_t)rmac.octet[3],
+ (uint8_t)rmac.octet[4],
+ (uint8_t)rmac.octet[5]);
+ } else if (*pnt
+ == ECOMMUNITY_EVPN_SUBTYPE_MACMOBILITY) {
+ uint32_t seqnum;
+ uint8_t flags = *++pnt;
+
+ memcpy(&seqnum, pnt + 2, 4);
+ seqnum = ntohl(seqnum);
+
+ snprintf(encbuf, sizeof(encbuf), "MM:%u",
+ seqnum);
+
+ if (CHECK_FLAG(
+ flags,
+ ECOMMUNITY_EVPN_SUBTYPE_MACMOBILITY_FLAG_STICKY))
+ strlcat(encbuf, ", sticky MAC",
+ sizeof(encbuf));
+ } else if (*pnt == ECOMMUNITY_EVPN_SUBTYPE_ND) {
+ uint8_t flags = *++pnt;
+
+ if (CHECK_FLAG(
+ flags,
+ ECOMMUNITY_EVPN_SUBTYPE_ND_ROUTER_FLAG))
+ strlcpy(encbuf, "ND:Router Flag",
+ sizeof(encbuf));
+ if (CHECK_FLAG(
+ flags,
+ ECOMMUNITY_EVPN_SUBTYPE_PROXY_FLAG))
+ strlcpy(encbuf, "ND:Proxy",
+ sizeof(encbuf));
+ } else if (*pnt
+ == ECOMMUNITY_EVPN_SUBTYPE_ES_IMPORT_RT) {
+ struct ethaddr mac;
+
+ pnt++;
+ memcpy(&mac, pnt, ETH_ALEN);
+ snprintf(encbuf,
+ sizeof(encbuf),
+ "ES-Import-Rt:%02x:%02x:%02x:%02x:%02x:%02x",
+ (uint8_t)mac.octet[0],
+ (uint8_t)mac.octet[1],
+ (uint8_t)mac.octet[2],
+ (uint8_t)mac.octet[3],
+ (uint8_t)mac.octet[4],
+ (uint8_t)mac.octet[5]);
+ } else if (*pnt
+ == ECOMMUNITY_EVPN_SUBTYPE_ESI_LABEL) {
+ uint8_t flags = *++pnt;
+
+ snprintf(encbuf,
+ sizeof(encbuf), "ESI-label-Rt:%s",
+ (flags &
+ ECOMMUNITY_EVPN_SUBTYPE_ESI_SA_FLAG) ?
+ "SA":"AA");
+ } else if (*pnt
+ == ECOMMUNITY_EVPN_SUBTYPE_DF_ELECTION) {
+ uint8_t alg;
+ uint16_t pref;
+ uint16_t bmap;
+
+ alg = *(pnt + 1);
+ memcpy(&bmap, pnt + 2, 2);
+ bmap = ntohs(bmap);
+ memcpy(&pref, pnt + 5, 2);
+ pref = ntohs(pref);
+
+ if (bmap)
+ snprintf(
+ encbuf, sizeof(encbuf),
+ "DF: (alg: %u, bmap: 0x%x pref: %u)",
+ alg, bmap, pref);
+ else
+ snprintf(encbuf, sizeof(encbuf),
+ "DF: (alg: %u, pref: %u)", alg,
+ pref);
+ } else
+ unk_ecom = 1;
+ } else if (type == ECOMMUNITY_ENCODE_REDIRECT_IP_NH) {
+ sub_type = *pnt++;
+ if (sub_type == ECOMMUNITY_REDIRECT_IP_NH) {
+ snprintf(encbuf, sizeof(encbuf),
+ "FS:redirect IP 0x%x", *(pnt + 5));
+ } else
+ unk_ecom = 1;
+ } else if (type == ECOMMUNITY_ENCODE_TRANS_EXP ||
+ type == ECOMMUNITY_EXTENDED_COMMUNITY_PART_2 ||
+ type == ECOMMUNITY_EXTENDED_COMMUNITY_PART_3) {
+ sub_type = *pnt++;
+
+ if (sub_type == ECOMMUNITY_ROUTE_TARGET) {
+ char buf[ECOMMUNITY_STRLEN];
+
+ memset(buf, 0, sizeof(buf));
+ ecommunity_rt_soo_str_internal(buf, sizeof(buf),
+ (const uint8_t *)pnt,
+ type &
+ ~ECOMMUNITY_ENCODE_TRANS_EXP,
+ ECOMMUNITY_ROUTE_TARGET,
+ format,
+ ecom->unit_size);
+ snprintf(encbuf, sizeof(encbuf), "%s", buf);
+ } else if (sub_type ==
+ ECOMMUNITY_FLOWSPEC_REDIRECT_IPV6) {
+ char buf[64];
+
+ memset(buf, 0, sizeof(buf));
+ ecommunity_rt_soo_str_internal(buf, sizeof(buf),
+ (const uint8_t *)pnt,
+ type &
+ ~ECOMMUNITY_ENCODE_TRANS_EXP,
+ ECOMMUNITY_ROUTE_TARGET,
+ ECOMMUNITY_FORMAT_DISPLAY,
+ ecom->unit_size);
+ snprintf(encbuf, sizeof(encbuf),
+ "FS:redirect VRF %s", buf);
+ } else if (sub_type == ECOMMUNITY_REDIRECT_VRF) {
+ char buf[16];
+
+ memset(buf, 0, sizeof(buf));
+ ecommunity_rt_soo_str(buf, sizeof(buf),
+ (const uint8_t *)pnt,
+ type &
+ ~ECOMMUNITY_ENCODE_TRANS_EXP,
+ ECOMMUNITY_ROUTE_TARGET,
+ ECOMMUNITY_FORMAT_DISPLAY);
+ snprintf(encbuf, sizeof(encbuf),
+ "FS:redirect VRF %s", buf);
+ snprintf(encbuf, sizeof(encbuf),
+ "FS:redirect VRF %s", buf);
+ } else if (type != ECOMMUNITY_ENCODE_TRANS_EXP)
+ unk_ecom = 1;
+ else if (sub_type == ECOMMUNITY_TRAFFIC_ACTION) {
+ char action[64];
+
+ if (*(pnt+3) ==
+ 1 << FLOWSPEC_TRAFFIC_ACTION_TERMINAL)
+ strlcpy(action, "terminate (apply)",
+ sizeof(action));
+ else
+ strlcpy(action, "eval stops",
+ sizeof(action));
+
+ if (*(pnt+3) ==
+ 1 << FLOWSPEC_TRAFFIC_ACTION_SAMPLE)
+ strlcat(action, ", sample",
+ sizeof(action));
+
+
+ snprintf(encbuf, sizeof(encbuf), "FS:action %s",
+ action);
+ } else if (sub_type == ECOMMUNITY_TRAFFIC_RATE) {
+ union traffic_rate data;
+
+ data.rate_byte[3] = *(pnt+2);
+ data.rate_byte[2] = *(pnt+3);
+ data.rate_byte[1] = *(pnt+4);
+ data.rate_byte[0] = *(pnt+5);
+ snprintf(encbuf, sizeof(encbuf), "FS:rate %f",
+ data.rate_float);
+ } else if (sub_type == ECOMMUNITY_TRAFFIC_MARKING) {
+ snprintf(encbuf, sizeof(encbuf),
+ "FS:marking %u", *(pnt + 5));
+ } else
+ unk_ecom = 1;
+ } else if (type == ECOMMUNITY_ENCODE_AS_NON_TRANS) {
+ sub_type = *pnt++;
+ if (sub_type == ECOMMUNITY_LINK_BANDWIDTH)
+ ecommunity_lb_str(encbuf, sizeof(encbuf), pnt,
+ ecom->disable_ieee_floating);
+ else
+ unk_ecom = 1;
+ } else if (type == ECOMMUNITY_ENCODE_OPAQUE_NON_TRANS) {
+ sub_type = *pnt++;
+ if (sub_type == ECOMMUNITY_ORIGIN_VALIDATION_STATE)
+ ecommunity_origin_validation_state_str(
+ encbuf, sizeof(encbuf), pnt);
+ else
+ unk_ecom = 1;
+ } else {
+ sub_type = *pnt++;
+ unk_ecom = 1;
+ }
+
+ if (unk_ecom)
+ snprintf(encbuf, sizeof(encbuf), "UNK:%d, %d", type,
+ sub_type);
+
+ int r = strlcat(str_buf, encbuf, str_size);
+ assert(r < str_size);
+ }
+
+ return str_buf;
+}
+
+bool ecommunity_include(struct ecommunity *e1, struct ecommunity *e2)
+{
+ uint32_t i, j;
+
+ if (!e1 || !e2)
+ return false;
+ for (i = 0; i < e1->size; ++i) {
+ for (j = 0; j < e2->size; ++j) {
+ if (!memcmp(e1->val + (i * e1->unit_size),
+ e2->val + (j * e2->unit_size),
+ e1->unit_size))
+ return true;
+ }
+ }
+ return false;
+}
+
+bool ecommunity_match(const struct ecommunity *ecom1,
+ const struct ecommunity *ecom2)
+{
+ uint32_t i = 0;
+ uint32_t j = 0;
+
+ if (ecom1 == NULL && ecom2 == NULL)
+ return true;
+
+ if (ecom1 == NULL || ecom2 == NULL)
+ return false;
+
+ if (ecom1->size < ecom2->size)
+ return false;
+
+ /* Every community on com2 needs to be on com1 for this to match */
+ while (i < ecom1->size && j < ecom2->size) {
+ if (memcmp(ecom1->val + i * ecom1->unit_size,
+ ecom2->val + j * ecom2->unit_size,
+ ecom2->unit_size)
+ == 0)
+ j++;
+ i++;
+ }
+
+ if (j == ecom2->size)
+ return true;
+ else
+ return false;
+}
+
+/* return first occurence of type */
+extern struct ecommunity_val *ecommunity_lookup(const struct ecommunity *ecom,
+ uint8_t type, uint8_t subtype)
+{
+ uint8_t *p;
+ uint32_t c;
+
+ /* If the value already exists in the structure return 0. */
+ c = 0;
+ for (p = ecom->val; c < ecom->size; p += ecom->unit_size, c++) {
+ if (p == NULL) {
+ continue;
+ }
+ if (p[0] == type && p[1] == subtype)
+ return (struct ecommunity_val *)p;
+ }
+ return NULL;
+}
+
+/* remove ext. community matching type and subtype
+ * return 1 on success ( removed ), 0 otherwise (not present)
+ */
+bool ecommunity_strip(struct ecommunity *ecom, uint8_t type,
+ uint8_t subtype)
+{
+ uint8_t *p, *q, *new;
+ uint32_t c, found = 0;
+ /* When this is fist value, just add it. */
+ if (ecom == NULL || ecom->val == NULL)
+ return false;
+
+ /* Check if any existing ext community matches. */
+ /* Certain extended communities like the Route Target can be present
+ * multiple times, handle that.
+ */
+ c = 0;
+ for (p = ecom->val; c < ecom->size; p += ecom->unit_size, c++) {
+ if (p[0] == type && p[1] == subtype)
+ found++;
+ }
+ /* If no matching ext community exists, return. */
+ if (found == 0)
+ return false;
+
+ /* Handle the case where everything needs to be stripped. */
+ if (found == ecom->size) {
+ XFREE(MTYPE_ECOMMUNITY_VAL, ecom->val);
+ ecom->size = 0;
+ return true;
+ }
+
+ /* Strip matching ext community(ies). */
+ new = XMALLOC(MTYPE_ECOMMUNITY_VAL,
+ (ecom->size - found) * ecom->unit_size);
+ q = new;
+ for (c = 0, p = ecom->val; c < ecom->size; c++, p += ecom->unit_size) {
+ if (!(p[0] == type && p[1] == subtype)) {
+ memcpy(q, p, ecom->unit_size);
+ q += ecom->unit_size;
+ }
+ }
+ XFREE(MTYPE_ECOMMUNITY_VAL, ecom->val);
+ ecom->val = new;
+ ecom->size -= found;
+ return true;
+}
+
+/*
+ * Remove specified extended community value from extended community.
+ * Returns 1 if value was present (and hence, removed), 0 otherwise.
+ */
+bool ecommunity_del_val(struct ecommunity *ecom, struct ecommunity_val *eval)
+{
+ uint8_t *p;
+ uint32_t c, found = 0;
+
+ /* Make sure specified value exists. */
+ if (ecom == NULL || ecom->val == NULL)
+ return false;
+ c = 0;
+ for (p = ecom->val; c < ecom->size; p += ecom->unit_size, c++) {
+ if (!memcmp(p, eval->val, ecom->unit_size)) {
+ found = 1;
+ break;
+ }
+ }
+ if (found == 0)
+ return false;
+
+ /* Delete the selected value */
+ ecom->size--;
+ if (ecom->size) {
+ p = XMALLOC(MTYPE_ECOMMUNITY_VAL, ecom->size * ecom->unit_size);
+ if (c != 0)
+ memcpy(p, ecom->val, c * ecom->unit_size);
+ if ((ecom->size - c) != 0)
+ memcpy(p + (c)*ecom->unit_size,
+ ecom->val + (c + 1) * ecom->unit_size,
+ (ecom->size - c) * ecom->unit_size);
+ XFREE(MTYPE_ECOMMUNITY_VAL, ecom->val);
+ ecom->val = p;
+ } else
+ XFREE(MTYPE_ECOMMUNITY_VAL, ecom->val);
+
+ return true;
+}
+
+int ecommunity_fill_pbr_action(struct ecommunity_val *ecom_eval,
+ struct bgp_pbr_entry_action *api,
+ afi_t afi)
+{
+ if (ecom_eval->val[1] == ECOMMUNITY_TRAFFIC_RATE) {
+ api->action = ACTION_TRAFFICRATE;
+ api->u.r.rate_info[3] = ecom_eval->val[4];
+ api->u.r.rate_info[2] = ecom_eval->val[5];
+ api->u.r.rate_info[1] = ecom_eval->val[6];
+ api->u.r.rate_info[0] = ecom_eval->val[7];
+ } else if (ecom_eval->val[1] == ECOMMUNITY_TRAFFIC_ACTION) {
+ api->action = ACTION_TRAFFIC_ACTION;
+ /* else distribute code is set by default */
+ if (ecom_eval->val[5] & (1 << FLOWSPEC_TRAFFIC_ACTION_TERMINAL))
+ api->u.za.filter |= TRAFFIC_ACTION_TERMINATE;
+ else
+ api->u.za.filter |= TRAFFIC_ACTION_DISTRIBUTE;
+ if (ecom_eval->val[5] == 1 << FLOWSPEC_TRAFFIC_ACTION_SAMPLE)
+ api->u.za.filter |= TRAFFIC_ACTION_SAMPLE;
+
+ } else if (ecom_eval->val[1] == ECOMMUNITY_TRAFFIC_MARKING) {
+ api->action = ACTION_MARKING;
+ api->u.marking_dscp = ecom_eval->val[7];
+ } else if (ecom_eval->val[1] == ECOMMUNITY_REDIRECT_VRF) {
+ /* must use external function */
+ return 0;
+ } else if (ecom_eval->val[1] == ECOMMUNITY_REDIRECT_IP_NH &&
+ afi == AFI_IP) {
+ /* see draft-ietf-idr-flowspec-redirect-ip-02
+ * Q1: how come a ext. community can host ipv6 address
+ * Q2 : from cisco documentation:
+ * Announces the reachability of one or more flowspec NLRI.
+ * When a BGP speaker receives an UPDATE message with the
+ * redirect-to-IP extended community, it is expected to
+ * create a traffic filtering rule for every flow-spec
+ * NLRI in the message that has this path as its best
+ * path. The filter entry matches the IP packets
+ * described in the NLRI field and redirects them or
+ * copies them towards the IPv4 or IPv6 address specified
+ * in the 'Network Address of Next- Hop'
+ * field of the associated MP_REACH_NLRI.
+ */
+ struct ecommunity_ip *ip_ecom = (struct ecommunity_ip *)
+ ecom_eval + 2;
+
+ api->u.zr.redirect_ip_v4 = ip_ecom->ip;
+ } else
+ return -1;
+ return 0;
+}
+
+static struct ecommunity *bgp_aggr_ecommunity_lookup(
+ struct bgp_aggregate *aggregate,
+ struct ecommunity *ecommunity)
+{
+ return hash_lookup(aggregate->ecommunity_hash, ecommunity);
+}
+
+static void *bgp_aggr_ecommunty_hash_alloc(void *p)
+{
+ struct ecommunity *ref = (struct ecommunity *)p;
+ struct ecommunity *ecommunity = NULL;
+
+ ecommunity = ecommunity_dup(ref);
+ return ecommunity;
+}
+
+static void bgp_aggr_ecommunity_prepare(struct hash_bucket *hb, void *arg)
+{
+ struct ecommunity *hb_ecommunity = hb->data;
+ struct ecommunity **aggr_ecommunity = arg;
+
+ if (*aggr_ecommunity)
+ *aggr_ecommunity = ecommunity_merge(*aggr_ecommunity,
+ hb_ecommunity);
+ else
+ *aggr_ecommunity = ecommunity_dup(hb_ecommunity);
+}
+
+void bgp_aggr_ecommunity_remove(void *arg)
+{
+ struct ecommunity *ecommunity = arg;
+
+ ecommunity_free(&ecommunity);
+}
+
+void bgp_compute_aggregate_ecommunity(struct bgp_aggregate *aggregate,
+ struct ecommunity *ecommunity)
+{
+ bgp_compute_aggregate_ecommunity_hash(aggregate, ecommunity);
+ bgp_compute_aggregate_ecommunity_val(aggregate);
+}
+
+
+void bgp_compute_aggregate_ecommunity_hash(struct bgp_aggregate *aggregate,
+ struct ecommunity *ecommunity)
+{
+ struct ecommunity *aggr_ecommunity = NULL;
+
+ if ((aggregate == NULL) || (ecommunity == NULL))
+ return;
+
+ /* Create hash if not already created.
+ */
+ if (aggregate->ecommunity_hash == NULL)
+ aggregate->ecommunity_hash = hash_create(
+ ecommunity_hash_make, ecommunity_cmp,
+ "BGP Aggregator ecommunity hash");
+
+ aggr_ecommunity = bgp_aggr_ecommunity_lookup(aggregate, ecommunity);
+ if (aggr_ecommunity == NULL) {
+ /* Insert ecommunity into hash.
+ */
+ aggr_ecommunity = hash_get(aggregate->ecommunity_hash,
+ ecommunity,
+ bgp_aggr_ecommunty_hash_alloc);
+ }
+
+ /* Increment reference counter.
+ */
+ aggr_ecommunity->refcnt++;
+}
+
+void bgp_compute_aggregate_ecommunity_val(struct bgp_aggregate *aggregate)
+{
+ struct ecommunity *ecommerge = NULL;
+
+ if (aggregate == NULL)
+ return;
+
+ /* Re-compute aggregate's ecommunity.
+ */
+ if (aggregate->ecommunity)
+ ecommunity_free(&aggregate->ecommunity);
+ if (aggregate->ecommunity_hash
+ && aggregate->ecommunity_hash->count) {
+ hash_iterate(aggregate->ecommunity_hash,
+ bgp_aggr_ecommunity_prepare,
+ &aggregate->ecommunity);
+ ecommerge = aggregate->ecommunity;
+ aggregate->ecommunity = ecommunity_uniq_sort(ecommerge);
+ if (ecommerge)
+ ecommunity_free(&ecommerge);
+ }
+}
+
+void bgp_remove_ecommunity_from_aggregate(struct bgp_aggregate *aggregate,
+ struct ecommunity *ecommunity)
+{
+ struct ecommunity *aggr_ecommunity = NULL;
+ struct ecommunity *ret_ecomm = NULL;
+
+ if ((!aggregate)
+ || (!aggregate->ecommunity_hash)
+ || (!ecommunity))
+ return;
+
+ /* Look-up the ecommunity in the hash.
+ */
+ aggr_ecommunity = bgp_aggr_ecommunity_lookup(aggregate, ecommunity);
+ if (aggr_ecommunity) {
+ aggr_ecommunity->refcnt--;
+
+ if (aggr_ecommunity->refcnt == 0) {
+ ret_ecomm = hash_release(aggregate->ecommunity_hash,
+ aggr_ecommunity);
+ ecommunity_free(&ret_ecomm);
+ bgp_compute_aggregate_ecommunity_val(aggregate);
+ }
+ }
+}
+
+void bgp_remove_ecomm_from_aggregate_hash(struct bgp_aggregate *aggregate,
+ struct ecommunity *ecommunity)
+{
+
+ struct ecommunity *aggr_ecommunity = NULL;
+ struct ecommunity *ret_ecomm = NULL;
+
+ if ((!aggregate)
+ || (!aggregate->ecommunity_hash)
+ || (!ecommunity))
+ return;
+
+ /* Look-up the ecommunity in the hash.
+ */
+ aggr_ecommunity = bgp_aggr_ecommunity_lookup(aggregate, ecommunity);
+ if (aggr_ecommunity) {
+ aggr_ecommunity->refcnt--;
+
+ if (aggr_ecommunity->refcnt == 0) {
+ ret_ecomm = hash_release(aggregate->ecommunity_hash,
+ aggr_ecommunity);
+ ecommunity_free(&ret_ecomm);
+ }
+ }
+}
+
+struct ecommunity *
+ecommunity_add_origin_validation_state(enum rpki_states rpki_state,
+ struct ecommunity *old)
+{
+ struct ecommunity *new = NULL;
+ struct ecommunity ovs_ecomm = {0};
+ struct ecommunity_val ovs_eval;
+
+ encode_origin_validation_state(rpki_state, &ovs_eval);
+
+ if (old) {
+ new = ecommunity_dup(old);
+ ecommunity_add_val(new, &ovs_eval, true, true);
+ if (!old->refcnt)
+ ecommunity_free(&old);
+ } else {
+ ovs_ecomm.size = 1;
+ ovs_ecomm.unit_size = ECOMMUNITY_SIZE;
+ ovs_ecomm.val = (uint8_t *)&ovs_eval.val;
+ new = ecommunity_dup(&ovs_ecomm);
+ }
+
+ return new;
+}
+
+/*
+ * return the BGP link bandwidth extended community, if present;
+ * the actual bandwidth is returned via param
+ */
+const uint8_t *ecommunity_linkbw_present(struct ecommunity *ecom, uint32_t *bw)
+{
+ const uint8_t *eval;
+ uint32_t i;
+
+ if (bw)
+ *bw = 0;
+
+ if (!ecom || !ecom->size)
+ return NULL;
+
+ for (i = 0; i < ecom->size; i++) {
+ const uint8_t *pnt;
+ uint8_t type, sub_type;
+ uint32_t bwval;
+
+ eval = pnt = (ecom->val + (i * ECOMMUNITY_SIZE));
+ type = *pnt++;
+ sub_type = *pnt++;
+
+ if ((type == ECOMMUNITY_ENCODE_AS ||
+ type == ECOMMUNITY_ENCODE_AS_NON_TRANS) &&
+ sub_type == ECOMMUNITY_LINK_BANDWIDTH) {
+ pnt += 2; /* bandwidth is encoded as AS:val */
+ pnt = ptr_get_be32(pnt, &bwval);
+ (void)pnt; /* consume value */
+ if (bw)
+ *bw = ecom->disable_ieee_floating
+ ? bwval
+ : ieee_float_uint32_to_uint32(
+ bwval);
+ return eval;
+ }
+ }
+
+ return NULL;
+}
+
+
+struct ecommunity *ecommunity_replace_linkbw(as_t as, struct ecommunity *ecom,
+ uint64_t cum_bw,
+ bool disable_ieee_floating)
+{
+ struct ecommunity *new;
+ struct ecommunity_val lb_eval;
+ const uint8_t *eval;
+ uint8_t type;
+ uint32_t cur_bw;
+
+ /* Nothing to replace if link-bandwidth doesn't exist or
+ * is non-transitive - just return existing extcommunity.
+ */
+ new = ecom;
+ if (!ecom || !ecom->size)
+ return new;
+
+ eval = ecommunity_linkbw_present(ecom, &cur_bw);
+ if (!eval)
+ return new;
+
+ type = *eval;
+ if (type & ECOMMUNITY_FLAG_NON_TRANSITIVE)
+ return new;
+
+ /* Transitive link-bandwidth exists, replace with the passed
+ * (cumulative) bandwidth value. We need to create a new
+ * extcommunity for this - refer to AS-Path replace function
+ * for reference.
+ */
+ if (cum_bw > 0xFFFFFFFF)
+ cum_bw = 0xFFFFFFFF;
+ encode_lb_extcomm(as > BGP_AS_MAX ? BGP_AS_TRANS : as, cum_bw, false,
+ &lb_eval, disable_ieee_floating);
+ new = ecommunity_dup(ecom);
+ ecommunity_add_val(new, &lb_eval, true, true);
+
+ return new;
+}
diff --git a/bgpd/bgp_ecommunity.h b/bgpd/bgp_ecommunity.h
new file mode 100644
index 0000000..4d7d423
--- /dev/null
+++ b/bgpd/bgp_ecommunity.h
@@ -0,0 +1,356 @@
+/* BGP Extended Communities Attribute.
+ * Copyright (C) 2000 Kunihiro Ishiguro <kunihiro@zebra.org>
+ *
+ * This file is part of GNU Zebra.
+ *
+ * GNU Zebra is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License as published by the
+ * Free Software Foundation; either version 2, or (at your option) any
+ * later version.
+ *
+ * GNU Zebra is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program; see the file COPYING; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
+ */
+
+#ifndef _QUAGGA_BGP_ECOMMUNITY_H
+#define _QUAGGA_BGP_ECOMMUNITY_H
+
+#include "bgpd/bgp_route.h"
+#include "bgpd/bgp_rpki.h"
+#include "bgpd/bgpd.h"
+
+/* Refer to rfc7153 for the IANA registry definitions. These are
+ * updated by other standards like rfc7674.
+ */
+/* High-order octet of the Extended Communities type field. */
+#define ECOMMUNITY_ENCODE_AS 0x00
+#define ECOMMUNITY_ENCODE_IP 0x01
+#define ECOMMUNITY_ENCODE_AS4 0x02
+#define ECOMMUNITY_ENCODE_OPAQUE 0x03
+#define ECOMMUNITY_ENCODE_EVPN 0x06
+#define ECOMMUNITY_ENCODE_REDIRECT_IP_NH 0x08 /* Flow Spec */
+/* Generic Transitive Experimental */
+#define ECOMMUNITY_ENCODE_TRANS_EXP 0x80
+
+/* RFC7674 */
+#define ECOMMUNITY_EXTENDED_COMMUNITY_PART_2 0x81
+#define ECOMMUNITY_EXTENDED_COMMUNITY_PART_3 0x82
+
+/* Non-transitive extended community types. */
+#define ECOMMUNITY_ENCODE_AS_NON_TRANS 0x40
+#define ECOMMUNITY_ENCODE_IP_NON_TRANS 0x41
+#define ECOMMUNITY_ENCODE_AS4_NON_TRANS 0x42
+#define ECOMMUNITY_ENCODE_OPAQUE_NON_TRANS 0x43
+
+/* Low-order octet of the Extended Communities type field. */
+/* Note: This really depends on the high-order octet. This means that
+ * multiple definitions for the same value are possible.
+ */
+#define ECOMMUNITY_ORIGIN_VALIDATION_STATE 0x00
+#define ECOMMUNITY_ROUTE_TARGET 0x02
+#define ECOMMUNITY_SITE_ORIGIN 0x03
+#define ECOMMUNITY_LINK_BANDWIDTH 0x04
+#define ECOMMUNITY_TRAFFIC_RATE 0x06 /* Flow Spec */
+#define ECOMMUNITY_TRAFFIC_ACTION 0x07
+#define ECOMMUNITY_REDIRECT_VRF 0x08
+#define ECOMMUNITY_TRAFFIC_MARKING 0x09
+#define ECOMMUNITY_REDIRECT_IP_NH 0x00
+/* from IANA: bgp-extended-communities/bgp-extended-communities.xhtml
+ * 0x0c Flow-spec Redirect to IPv4 - draft-ietf-idr-flowspec-redirect
+ */
+#define ECOMMUNITY_FLOWSPEC_REDIRECT_IPV4 0x0c
+/* from draft-ietf-idr-flow-spec-v6-09
+ * 0x0b Flow-spec Redirect to IPv6
+ */
+#define ECOMMUNITY_FLOWSPEC_REDIRECT_IPV6 0x0b
+
+/* Low-order octet of the Extended Communities type field for EVPN types */
+#define ECOMMUNITY_EVPN_SUBTYPE_MACMOBILITY 0x00
+#define ECOMMUNITY_EVPN_SUBTYPE_ESI_LABEL 0x01
+#define ECOMMUNITY_EVPN_SUBTYPE_ES_IMPORT_RT 0x02
+#define ECOMMUNITY_EVPN_SUBTYPE_ROUTERMAC 0x03
+#define ECOMMUNITY_EVPN_SUBTYPE_DF_ELECTION 0x06
+#define ECOMMUNITY_EVPN_SUBTYPE_DEF_GW 0x0d
+#define ECOMMUNITY_EVPN_SUBTYPE_ND 0x08
+
+#define ECOMMUNITY_EVPN_SUBTYPE_MACMOBILITY_FLAG_STICKY 0x01
+
+/* DF alg bits - only lower 5 bits are applicable */
+#define ECOMMUNITY_EVPN_SUBTYPE_DF_ALG_BITS 0x1f
+
+#define ECOMMUNITY_EVPN_SUBTYPE_ND_ROUTER_FLAG 0x01
+#define ECOMMUNITY_EVPN_SUBTYPE_ND_OVERRIDE_FLAG 0x02
+#define ECOMMUNITY_EVPN_SUBTYPE_PROXY_FLAG 0x04
+
+#define ECOMMUNITY_EVPN_SUBTYPE_ESI_SA_FLAG (1 << 0) /* single-active */
+
+/* Low-order octet of the Extended Communities type field for OPAQUE types */
+#define ECOMMUNITY_OPAQUE_SUBTYPE_ENCAP 0x0c
+
+/* Extended communities attribute string format. */
+#define ECOMMUNITY_FORMAT_ROUTE_MAP 0
+#define ECOMMUNITY_FORMAT_COMMUNITY_LIST 1
+#define ECOMMUNITY_FORMAT_DISPLAY 2
+
+/* Extended Communities value is eight octet long. */
+#define ECOMMUNITY_SIZE 8
+#define IPV6_ECOMMUNITY_SIZE 20
+
+/* Extended Community Origin Validation State */
+enum ecommunity_origin_validation_states {
+ ECOMMUNITY_ORIGIN_VALIDATION_STATE_VALID,
+ ECOMMUNITY_ORIGIN_VALIDATION_STATE_NOTFOUND,
+ ECOMMUNITY_ORIGIN_VALIDATION_STATE_INVALID,
+ ECOMMUNITY_ORIGIN_VALIDATION_STATE_NOTUSED
+};
+
+/* Extended Communities type flag. */
+#define ECOMMUNITY_FLAG_NON_TRANSITIVE 0x40
+
+/* Extended Community readable string length */
+#define ECOMMUNITY_STRLEN 64
+
+/* Extended Communities attribute. */
+struct ecommunity {
+ /* Reference counter. */
+ unsigned long refcnt;
+
+ /* Size of Each Unit of Extended Communities attribute.
+ * to differentiate between IPv6 ext comm and ext comm
+ */
+ uint8_t unit_size;
+
+ /* Size of Extended Communities attribute. */
+ uint32_t size;
+
+ /* Extended Communities value. */
+ uint8_t *val;
+
+ /* Human readable format string. */
+ char *str;
+
+ /* Disable IEEE floating-point encoding for extended community */
+ bool disable_ieee_floating;
+};
+
+struct ecommunity_as {
+ as_t as;
+ uint32_t val;
+};
+
+struct ecommunity_ip {
+ struct in_addr ip;
+ uint16_t val;
+};
+
+struct ecommunity_ip6 {
+ struct in6_addr ip;
+ uint16_t val;
+};
+
+/* Extended community value is eight octet. */
+struct ecommunity_val {
+ char val[ECOMMUNITY_SIZE];
+};
+
+/* IPv6 Extended community value is eight octet. */
+struct ecommunity_val_ipv6 {
+ char val[IPV6_ECOMMUNITY_SIZE];
+};
+
+#define ecom_length_size(X, Y) ((X)->size * (Y))
+
+/*
+ * Encode BGP Route Target AS:nn.
+ */
+static inline void encode_route_target_as(as_t as, uint32_t val,
+ struct ecommunity_val *eval)
+{
+ eval->val[0] = ECOMMUNITY_ENCODE_AS;
+ eval->val[1] = ECOMMUNITY_ROUTE_TARGET;
+ eval->val[2] = (as >> 8) & 0xff;
+ eval->val[3] = as & 0xff;
+ eval->val[4] = (val >> 24) & 0xff;
+ eval->val[5] = (val >> 16) & 0xff;
+ eval->val[6] = (val >> 8) & 0xff;
+ eval->val[7] = val & 0xff;
+}
+
+/*
+ * Encode BGP Route Target IP:nn.
+ */
+static inline void encode_route_target_ip(struct in_addr ip, uint16_t val,
+ struct ecommunity_val *eval)
+{
+ eval->val[0] = ECOMMUNITY_ENCODE_IP;
+ eval->val[1] = ECOMMUNITY_ROUTE_TARGET;
+ memcpy(&eval->val[2], &ip, sizeof(struct in_addr));
+ eval->val[6] = (val >> 8) & 0xff;
+ eval->val[7] = val & 0xff;
+}
+
+/*
+ * Encode BGP Route Target AS4:nn.
+ */
+static inline void encode_route_target_as4(as_t as, uint16_t val,
+ struct ecommunity_val *eval)
+{
+ eval->val[0] = ECOMMUNITY_ENCODE_AS4;
+ eval->val[1] = ECOMMUNITY_ROUTE_TARGET;
+ eval->val[2] = (as >> 24) & 0xff;
+ eval->val[3] = (as >> 16) & 0xff;
+ eval->val[4] = (as >> 8) & 0xff;
+ eval->val[5] = as & 0xff;
+ eval->val[6] = (val >> 8) & 0xff;
+ eval->val[7] = val & 0xff;
+}
+
+/* Helper function to convert uint32 to IEEE-754 Floating Point */
+static uint32_t uint32_to_ieee_float_uint32(uint32_t u)
+{
+ union {
+ float r;
+ uint32_t d;
+ } f = {.r = (float)u};
+
+ return f.d;
+}
+
+/*
+ * Encode BGP Link Bandwidth extended community
+ * bandwidth (bw) is in bytes-per-sec
+ */
+static inline void encode_lb_extcomm(as_t as, uint32_t bw, bool non_trans,
+ struct ecommunity_val *eval,
+ bool disable_ieee_floating)
+{
+ uint32_t bandwidth =
+ disable_ieee_floating ? bw : uint32_to_ieee_float_uint32(bw);
+
+ memset(eval, 0, sizeof(*eval));
+ eval->val[0] = ECOMMUNITY_ENCODE_AS;
+ if (non_trans)
+ eval->val[0] |= ECOMMUNITY_FLAG_NON_TRANSITIVE;
+ eval->val[1] = ECOMMUNITY_LINK_BANDWIDTH;
+ eval->val[2] = (as >> 8) & 0xff;
+ eval->val[3] = as & 0xff;
+ eval->val[4] = (bandwidth >> 24) & 0xff;
+ eval->val[5] = (bandwidth >> 16) & 0xff;
+ eval->val[6] = (bandwidth >> 8) & 0xff;
+ eval->val[7] = bandwidth & 0xff;
+}
+
+static inline void encode_origin_validation_state(enum rpki_states state,
+ struct ecommunity_val *eval)
+{
+ enum ecommunity_origin_validation_states ovs_state =
+ ECOMMUNITY_ORIGIN_VALIDATION_STATE_NOTUSED;
+
+ switch (state) {
+ case RPKI_VALID:
+ ovs_state = ECOMMUNITY_ORIGIN_VALIDATION_STATE_VALID;
+ break;
+ case RPKI_NOTFOUND:
+ ovs_state = ECOMMUNITY_ORIGIN_VALIDATION_STATE_NOTFOUND;
+ break;
+ case RPKI_INVALID:
+ ovs_state = ECOMMUNITY_ORIGIN_VALIDATION_STATE_INVALID;
+ break;
+ case RPKI_NOT_BEING_USED:
+ break;
+ }
+
+ memset(eval, 0, sizeof(*eval));
+ eval->val[0] = ECOMMUNITY_ENCODE_OPAQUE_NON_TRANS;
+ eval->val[1] = ECOMMUNITY_ORIGIN_VALIDATION_STATE;
+ eval->val[7] = ovs_state;
+}
+
+extern void ecommunity_init(void);
+extern void ecommunity_finish(void);
+extern void ecommunity_free(struct ecommunity **);
+extern struct ecommunity *ecommunity_parse(uint8_t *, unsigned short,
+ bool disable_ieee_floating);
+extern struct ecommunity *ecommunity_parse_ipv6(uint8_t *pnt,
+ unsigned short length,
+ bool disable_ieee_floating);
+extern struct ecommunity *ecommunity_dup(struct ecommunity *);
+extern struct ecommunity *ecommunity_merge(struct ecommunity *,
+ struct ecommunity *);
+extern struct ecommunity *ecommunity_uniq_sort(struct ecommunity *);
+extern struct ecommunity *ecommunity_intern(struct ecommunity *);
+extern bool ecommunity_cmp(const void *arg1, const void *arg2);
+extern void ecommunity_unintern(struct ecommunity **ecommunity);
+extern unsigned int ecommunity_hash_make(const void *);
+extern struct ecommunity *ecommunity_str2com(const char *, int, int);
+extern struct ecommunity *ecommunity_str2com_ipv6(const char *str, int type,
+ int keyword_included);
+extern char *ecommunity_ecom2str(struct ecommunity *, int, int);
+extern void ecommunity_strfree(char **s);
+extern bool ecommunity_include(struct ecommunity *e1, struct ecommunity *e2);
+extern bool ecommunity_match(const struct ecommunity *,
+ const struct ecommunity *);
+extern char *ecommunity_str(struct ecommunity *);
+extern struct ecommunity_val *ecommunity_lookup(const struct ecommunity *,
+ uint8_t, uint8_t);
+
+extern bool ecommunity_add_val(struct ecommunity *ecom,
+ struct ecommunity_val *eval,
+ bool unique, bool overwrite);
+extern bool ecommunity_add_val_ipv6(struct ecommunity *ecom,
+ struct ecommunity_val_ipv6 *eval,
+ bool unique, bool overwrite);
+
+/* for vpn */
+extern struct ecommunity *ecommunity_new(void);
+extern bool ecommunity_strip(struct ecommunity *ecom, uint8_t type,
+ uint8_t subtype);
+extern struct ecommunity *ecommunity_new(void);
+extern bool ecommunity_del_val(struct ecommunity *ecom,
+ struct ecommunity_val *eval);
+struct bgp_pbr_entry_action;
+extern int ecommunity_fill_pbr_action(struct ecommunity_val *ecom_eval,
+ struct bgp_pbr_entry_action *api,
+ afi_t afi);
+
+extern void bgp_compute_aggregate_ecommunity(
+ struct bgp_aggregate *aggregate,
+ struct ecommunity *ecommunity);
+
+extern void bgp_compute_aggregate_ecommunity_hash(
+ struct bgp_aggregate *aggregate,
+ struct ecommunity *ecommunity);
+extern void bgp_compute_aggregate_ecommunity_val(
+ struct bgp_aggregate *aggregate);
+extern void bgp_remove_ecommunity_from_aggregate(
+ struct bgp_aggregate *aggregate,
+ struct ecommunity *ecommunity);
+extern void bgp_remove_ecomm_from_aggregate_hash(
+ struct bgp_aggregate *aggregate,
+ struct ecommunity *ecommunity);
+extern void bgp_aggr_ecommunity_remove(void *arg);
+extern const uint8_t *ecommunity_linkbw_present(struct ecommunity *ecom,
+ uint32_t *bw);
+extern struct ecommunity *ecommunity_replace_linkbw(as_t as,
+ struct ecommunity *ecom,
+ uint64_t cum_bw,
+ bool disable_ieee_floating);
+
+static inline void ecommunity_strip_rts(struct ecommunity *ecom)
+{
+ uint8_t subtype = ECOMMUNITY_ROUTE_TARGET;
+
+ ecommunity_strip(ecom, ECOMMUNITY_ENCODE_AS, subtype);
+ ecommunity_strip(ecom, ECOMMUNITY_ENCODE_IP, subtype);
+ ecommunity_strip(ecom, ECOMMUNITY_ENCODE_AS4, subtype);
+}
+extern struct ecommunity *
+ecommunity_add_origin_validation_state(enum rpki_states rpki_state,
+ struct ecommunity *ecom);
+#endif /* _QUAGGA_BGP_ECOMMUNITY_H */
diff --git a/bgpd/bgp_encap_tlv.c b/bgpd/bgp_encap_tlv.c
new file mode 100644
index 0000000..fd42912
--- /dev/null
+++ b/bgpd/bgp_encap_tlv.c
@@ -0,0 +1,1006 @@
+/*
+ * Copyright 2015, LabN Consulting, L.L.C.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program; see the file COPYING; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
+ */
+
+#include <zebra.h>
+
+#include "command.h"
+#include "memory.h"
+#include "prefix.h"
+#include "filter.h"
+#include "stream.h"
+
+#include "bgpd.h"
+#include "bgp_attr.h"
+
+#include "bgp_encap_types.h"
+#include "bgp_encap_tlv.h"
+
+/***********************************************************************
+ * SUBTLV ENCODE
+ ***********************************************************************/
+
+/* rfc5512 4.1 */
+static struct bgp_attr_encap_subtlv *subtlv_encode_encap_l2tpv3_over_ip(
+ struct bgp_tea_subtlv_encap_l2tpv3_over_ip *st)
+{
+ struct bgp_attr_encap_subtlv *new;
+ uint8_t *p;
+ int total = 4 + st->cookie_length;
+
+ /* sanity check */
+ assert(st->cookie_length <= sizeof(st->cookie));
+ assert(total <= 0xff);
+
+ new = XCALLOC(MTYPE_ENCAP_TLV,
+ sizeof(struct bgp_attr_encap_subtlv) + total);
+ assert(new);
+ new->type = BGP_ENCAP_SUBTLV_TYPE_ENCAPSULATION;
+ new->length = total;
+ p = new->value;
+
+ *p++ = (st->sessionid & 0xff000000) >> 24;
+ *p++ = (st->sessionid & 0xff0000) >> 16;
+ *p++ = (st->sessionid & 0xff00) >> 8;
+ *p++ = (st->sessionid & 0xff);
+ memcpy(p, st->cookie, st->cookie_length);
+ return new;
+}
+
+/* rfc5512 4.1 */
+static struct bgp_attr_encap_subtlv *
+subtlv_encode_encap_gre(struct bgp_tea_subtlv_encap_gre_key *st)
+{
+ struct bgp_attr_encap_subtlv *new;
+ uint8_t *p;
+ int total = 4;
+
+ assert(total <= 0xff);
+
+ new = XCALLOC(MTYPE_ENCAP_TLV,
+ sizeof(struct bgp_attr_encap_subtlv) + total);
+ assert(new);
+ new->type = BGP_ENCAP_SUBTLV_TYPE_ENCAPSULATION;
+ new->length = total;
+ p = new->value;
+
+ *p++ = (st->gre_key & 0xff000000) >> 24;
+ *p++ = (st->gre_key & 0xff0000) >> 16;
+ *p++ = (st->gre_key & 0xff00) >> 8;
+ *p++ = (st->gre_key & 0xff);
+ return new;
+}
+
+static struct bgp_attr_encap_subtlv *
+subtlv_encode_encap_pbb(struct bgp_tea_subtlv_encap_pbb *st)
+{
+ struct bgp_attr_encap_subtlv *new;
+ uint8_t *p;
+ int total = 1 + 3 + 6 + 2; /* flags + isid + madaddr + vid */
+
+ assert(total <= 0xff);
+
+ new = XCALLOC(MTYPE_ENCAP_TLV,
+ sizeof(struct bgp_attr_encap_subtlv) + total);
+ assert(new);
+ new->type = BGP_ENCAP_SUBTLV_TYPE_ENCAPSULATION;
+ new->length = total;
+ p = new->value;
+
+ *p++ = (st->flag_isid ? 0x80 : 0) | (st->flag_vid ? 0x40 : 0) | 0;
+ if (st->flag_isid) {
+ *p = (st->isid & 0xff0000) >> 16;
+ *(p + 1) = (st->isid & 0xff00) >> 8;
+ *(p + 2) = (st->isid & 0xff);
+ }
+ p += 3;
+ memcpy(p, st->macaddr, 6);
+ p += 6;
+ if (st->flag_vid) {
+ *p++ = (st->vid & 0xf00) >> 8;
+ *p++ = st->vid & 0xff;
+ }
+ return new;
+}
+
+/* rfc5512 4.2 */
+static struct bgp_attr_encap_subtlv *
+subtlv_encode_proto_type(struct bgp_tea_subtlv_proto_type *st)
+{
+ struct bgp_attr_encap_subtlv *new;
+ uint8_t *p;
+ int total = 2;
+
+ assert(total <= 0xff);
+
+ new = XCALLOC(MTYPE_ENCAP_TLV,
+ sizeof(struct bgp_attr_encap_subtlv) + total);
+ assert(new);
+ new->type = BGP_ENCAP_SUBTLV_TYPE_PROTO_TYPE;
+ new->length = total;
+ p = new->value;
+
+ *p++ = (st->proto & 0xff00) >> 8;
+ *p++ = (st->proto & 0xff);
+ return new;
+}
+
+/* rfc5512 4.3 */
+static struct bgp_attr_encap_subtlv *
+subtlv_encode_color(struct bgp_tea_subtlv_color *st)
+{
+ struct bgp_attr_encap_subtlv *new;
+ uint8_t *p;
+ int total = 8;
+
+ assert(total <= 0xff);
+
+ new = XCALLOC(MTYPE_ENCAP_TLV,
+ sizeof(struct bgp_attr_encap_subtlv) + total);
+ assert(new);
+ new->type = BGP_ENCAP_SUBTLV_TYPE_COLOR;
+ new->length = total;
+ p = new->value;
+
+ *p++ = 0x03; /* transitive*/
+ *p++ = 0x0b;
+ *p++ = 0; /* reserved */
+ *p++ = 0; /* reserved */
+
+ *p++ = (st->color & 0xff000000) >> 24;
+ *p++ = (st->color & 0xff0000) >> 16;
+ *p++ = (st->color & 0xff00) >> 8;
+ *p++ = (st->color & 0xff);
+
+ return new;
+}
+
+/* rfc 5566 4. */
+static struct bgp_attr_encap_subtlv *
+subtlv_encode_ipsec_ta(struct bgp_tea_subtlv_ipsec_ta *st)
+{
+ struct bgp_attr_encap_subtlv *new;
+ uint8_t *p;
+ int total = 2 + st->authenticator_length;
+
+ /* sanity check */
+ assert(st->authenticator_length <= sizeof(st->value));
+ assert(total <= 0xff);
+
+ new = XCALLOC(MTYPE_ENCAP_TLV,
+ sizeof(struct bgp_attr_encap_subtlv) + total);
+ assert(new);
+ new->type = BGP_ENCAP_SUBTLV_TYPE_IPSEC_TA;
+ new->length = total;
+ p = new->value;
+
+ *p++ = (st->authenticator_type & 0xff00) >> 8;
+ *p++ = st->authenticator_type & 0xff;
+ memcpy(p, st->value, st->authenticator_length);
+ return new;
+}
+
+/* draft-rosen-idr-tunnel-encaps 2.1 */
+static struct bgp_attr_encap_subtlv *
+subtlv_encode_remote_endpoint(struct bgp_tea_subtlv_remote_endpoint *st)
+{
+ struct bgp_attr_encap_subtlv *new;
+ uint8_t *p;
+
+ int total = (st->family == AF_INET ? 8 : 20);
+
+ assert(total <= 0xff);
+
+ new = XCALLOC(MTYPE_ENCAP_TLV,
+ sizeof(struct bgp_attr_encap_subtlv) + total);
+ assert(new);
+ new->type = BGP_ENCAP_SUBTLV_TYPE_REMOTE_ENDPOINT;
+ new->length = total;
+ p = new->value;
+ if (st->family == AF_INET) {
+ memcpy(p, &(st->ip_address.v4.s_addr), IPV4_MAX_BYTELEN);
+ p += IPV4_MAX_BYTELEN;
+ } else {
+ assert(st->family == AF_INET6);
+ memcpy(p, &(st->ip_address.v6.s6_addr), IPV6_MAX_BYTELEN);
+ p += IPV6_MAX_BYTELEN;
+ }
+ memcpy(p, &(st->as4), 4);
+ return new;
+}
+
+/***********************************************************************
+ * TUNNEL TYPE-SPECIFIC TLV ENCODE
+ ***********************************************************************/
+
+/*
+ * requires "extra" and "last" to be defined in caller
+ */
+#define ENC_SUBTLV(flag, function, field) \
+ do { \
+ struct bgp_attr_encap_subtlv *new; \
+ if (CHECK_FLAG(bet->valid_subtlvs, (flag))) { \
+ new = function(&bet->field); \
+ if (last) { \
+ last->next = new; \
+ } else { \
+ attr->encap_subtlvs = new; \
+ } \
+ last = new; \
+ } \
+ } while (0)
+
+void bgp_encap_type_l2tpv3overip_to_tlv(
+ struct bgp_encap_type_l2tpv3_over_ip *bet, /* input structure */
+ struct attr *attr)
+{
+ struct bgp_attr_encap_subtlv *last;
+
+ /* advance to last subtlv */
+ for (last = attr->encap_subtlvs; last && last->next; last = last->next)
+ ;
+
+ attr->encap_tunneltype = BGP_ENCAP_TYPE_L2TPV3_OVER_IP;
+
+ assert(CHECK_FLAG(bet->valid_subtlvs, BGP_TEA_SUBTLV_ENCAP));
+
+ ENC_SUBTLV(BGP_TEA_SUBTLV_ENCAP, subtlv_encode_encap_l2tpv3_over_ip,
+ st_encap);
+ ENC_SUBTLV(BGP_TEA_SUBTLV_PROTO_TYPE, subtlv_encode_proto_type,
+ st_proto);
+ ENC_SUBTLV(BGP_TEA_SUBTLV_COLOR, subtlv_encode_color, st_color);
+ ENC_SUBTLV(BGP_TEA_SUBTLV_REMOTE_ENDPOINT,
+ subtlv_encode_remote_endpoint, st_endpoint);
+}
+
+void bgp_encap_type_gre_to_tlv(
+ struct bgp_encap_type_gre *bet, /* input structure */
+ struct attr *attr)
+{
+ struct bgp_attr_encap_subtlv *last;
+
+ /* advance to last subtlv */
+ for (last = attr->encap_subtlvs; last && last->next; last = last->next)
+ ;
+
+ attr->encap_tunneltype = BGP_ENCAP_TYPE_GRE;
+
+ ENC_SUBTLV(BGP_TEA_SUBTLV_ENCAP, subtlv_encode_encap_gre, st_encap);
+ ENC_SUBTLV(BGP_TEA_SUBTLV_PROTO_TYPE, subtlv_encode_proto_type,
+ st_proto);
+ ENC_SUBTLV(BGP_TEA_SUBTLV_COLOR, subtlv_encode_color, st_color);
+ ENC_SUBTLV(BGP_TEA_SUBTLV_REMOTE_ENDPOINT,
+ subtlv_encode_remote_endpoint, st_endpoint);
+}
+
+void bgp_encap_type_ip_in_ip_to_tlv(
+ struct bgp_encap_type_ip_in_ip *bet, /* input structure */
+ struct attr *attr)
+{
+ struct bgp_attr_encap_subtlv *last;
+
+ /* advance to last subtlv */
+ for (last = attr->encap_subtlvs; last && last->next; last = last->next)
+ ;
+
+ attr->encap_tunneltype = BGP_ENCAP_TYPE_IP_IN_IP;
+
+ ENC_SUBTLV(BGP_TEA_SUBTLV_PROTO_TYPE, subtlv_encode_proto_type,
+ st_proto);
+ ENC_SUBTLV(BGP_TEA_SUBTLV_COLOR, subtlv_encode_color, st_color);
+ ENC_SUBTLV(BGP_TEA_SUBTLV_REMOTE_ENDPOINT,
+ subtlv_encode_remote_endpoint, st_endpoint);
+}
+
+void bgp_encap_type_transmit_tunnel_endpoint(
+ struct bgp_encap_type_transmit_tunnel_endpoint
+ *bet, /* input structure */
+ struct attr *attr)
+{
+ struct bgp_attr_encap_subtlv *last;
+
+ /* advance to last subtlv */
+ for (last = attr->encap_subtlvs; last && last->next; last = last->next)
+ ;
+
+ attr->encap_tunneltype = BGP_ENCAP_TYPE_TRANSMIT_TUNNEL_ENDPOINT;
+
+ /* no subtlvs for this type */
+}
+
+void bgp_encap_type_ipsec_in_tunnel_mode_to_tlv(
+ struct bgp_encap_type_ipsec_in_tunnel_mode *bet, /* input structure */
+ struct attr *attr)
+{
+ struct bgp_attr_encap_subtlv *last;
+
+ /* advance to last subtlv */
+ for (last = attr->encap_subtlvs; last && last->next; last = last->next)
+ ;
+
+ attr->encap_tunneltype = BGP_ENCAP_TYPE_IPSEC_IN_TUNNEL_MODE;
+
+ ENC_SUBTLV(BGP_TEA_SUBTLV_IPSEC_TA, subtlv_encode_ipsec_ta,
+ st_ipsec_ta);
+}
+
+void bgp_encap_type_ip_in_ip_tunnel_with_ipsec_transport_mode_to_tlv(
+ struct bgp_encap_type_ip_in_ip_tunnel_with_ipsec_transport_mode
+ *bet, /* input structure */
+ struct attr *attr)
+{
+ struct bgp_attr_encap_subtlv *last;
+
+ /* advance to last subtlv */
+ for (last = attr->encap_subtlvs; last && last->next; last = last->next)
+ ;
+
+ attr->encap_tunneltype =
+ BGP_ENCAP_TYPE_IP_IN_IP_TUNNEL_WITH_IPSEC_TRANSPORT_MODE;
+
+ ENC_SUBTLV(BGP_TEA_SUBTLV_IPSEC_TA, subtlv_encode_ipsec_ta,
+ st_ipsec_ta);
+}
+
+void bgp_encap_type_mpls_in_ip_tunnel_with_ipsec_transport_mode_to_tlv(
+ struct bgp_encap_type_mpls_in_ip_tunnel_with_ipsec_transport_mode
+ *bet, /* input structure */
+ struct attr *attr)
+{
+ struct bgp_attr_encap_subtlv *last;
+
+ /* advance to last subtlv */
+ for (last = attr->encap_subtlvs; last && last->next; last = last->next)
+ ;
+
+ attr->encap_tunneltype =
+ BGP_ENCAP_TYPE_MPLS_IN_IP_TUNNEL_WITH_IPSEC_TRANSPORT_MODE;
+
+ ENC_SUBTLV(BGP_TEA_SUBTLV_IPSEC_TA, subtlv_encode_ipsec_ta,
+ st_ipsec_ta);
+}
+
+void bgp_encap_type_pbb_to_tlv(
+ struct bgp_encap_type_pbb *bet, /* input structure */
+ struct attr *attr)
+{
+ struct bgp_attr_encap_subtlv *last;
+
+ /* advance to last subtlv */
+ for (last = attr->encap_subtlvs; last && last->next; last = last->next)
+ ;
+
+ attr->encap_tunneltype = BGP_ENCAP_TYPE_PBB;
+
+ assert(CHECK_FLAG(bet->valid_subtlvs, BGP_TEA_SUBTLV_ENCAP));
+ ENC_SUBTLV(BGP_TEA_SUBTLV_ENCAP, subtlv_encode_encap_pbb, st_encap);
+}
+
+void bgp_encap_type_vxlan_to_tlv(
+ struct bgp_encap_type_vxlan *bet, /* input structure */
+ struct attr *attr)
+{
+ struct bgp_attr_encap_subtlv *tlv;
+ uint32_t vnid;
+
+ attr->encap_tunneltype = BGP_ENCAP_TYPE_VXLAN;
+
+ if (bet == NULL || !bet->vnid)
+ return;
+ XFREE(MTYPE_ENCAP_TLV, attr->encap_subtlvs);
+ tlv = XCALLOC(MTYPE_ENCAP_TLV,
+ sizeof(struct bgp_attr_encap_subtlv) + 12);
+ tlv->type = 1; /* encapsulation type */
+ tlv->length = 12;
+ if (bet->vnid) {
+ vnid = htonl(bet->vnid | VXLAN_ENCAP_MASK_VNID_VALID);
+ memcpy(&tlv->value, &vnid, 4);
+ }
+ if (bet->mac_address) {
+ char *ptr = (char *)&tlv->value + 4;
+ memcpy(ptr, bet->mac_address, 6);
+ }
+ attr->encap_subtlvs = tlv;
+ return;
+}
+
+void bgp_encap_type_nvgre_to_tlv(
+ struct bgp_encap_type_nvgre *bet, /* input structure */
+ struct attr *attr)
+{
+ attr->encap_tunneltype = BGP_ENCAP_TYPE_NVGRE;
+}
+
+void bgp_encap_type_mpls_to_tlv(
+ struct bgp_encap_type_mpls *bet, /* input structure */
+ struct attr *attr)
+{
+ return; /* no encap attribute for MPLS */
+}
+
+void bgp_encap_type_mpls_in_gre_to_tlv(
+ struct bgp_encap_type_mpls_in_gre *bet, /* input structure */
+ struct attr *attr)
+{
+ attr->encap_tunneltype = BGP_ENCAP_TYPE_MPLS_IN_GRE;
+}
+
+void bgp_encap_type_vxlan_gpe_to_tlv(
+ struct bgp_encap_type_vxlan_gpe *bet, /* input structure */
+ struct attr *attr)
+{
+
+ attr->encap_tunneltype = BGP_ENCAP_TYPE_VXLAN_GPE;
+}
+
+void bgp_encap_type_mpls_in_udp_to_tlv(
+ struct bgp_encap_type_mpls_in_udp *bet, /* input structure */
+ struct attr *attr)
+{
+
+ attr->encap_tunneltype = BGP_ENCAP_TYPE_MPLS_IN_UDP;
+}
+
+
+/***********************************************************************
+ * SUBTLV DECODE
+ ***********************************************************************/
+/* rfc5512 4.1 */
+static int subtlv_decode_encap_l2tpv3_over_ip(
+ struct bgp_attr_encap_subtlv *subtlv,
+ struct bgp_tea_subtlv_encap_l2tpv3_over_ip *st)
+{
+ if (subtlv->length < 4) {
+ zlog_debug("%s, subtlv length %d is less than 4", __func__,
+ subtlv->length);
+ return -1;
+ }
+
+ ptr_get_be32(subtlv->value, &st->sessionid);
+ st->cookie_length = subtlv->length - 4;
+ if (st->cookie_length > sizeof(st->cookie)) {
+ zlog_debug("%s, subtlv length %d is greater than %d", __func__,
+ st->cookie_length, (int)sizeof(st->cookie));
+ return -1;
+ }
+ memcpy(st->cookie, subtlv->value + 4, st->cookie_length);
+ return 0;
+}
+
+/* rfc5512 4.1 */
+static int subtlv_decode_encap_gre(struct bgp_attr_encap_subtlv *subtlv,
+ struct bgp_tea_subtlv_encap_gre_key *st)
+{
+ if (subtlv->length != 4) {
+ zlog_debug("%s, subtlv length %d does not equal 4", __func__,
+ subtlv->length);
+ return -1;
+ }
+ ptr_get_be32(subtlv->value, &st->gre_key);
+ return 0;
+}
+
+static int subtlv_decode_encap_pbb(struct bgp_attr_encap_subtlv *subtlv,
+ struct bgp_tea_subtlv_encap_pbb *st)
+{
+ if (subtlv->length != 1 + 3 + 6 + 2) {
+ zlog_debug("%s, subtlv length %d does not equal %d", __func__,
+ subtlv->length, 1 + 3 + 6 + 2);
+ return -1;
+ }
+ if (subtlv->value[0] & 0x80) {
+ st->flag_isid = 1;
+ st->isid = (subtlv->value[1] << 16) | (subtlv->value[2] << 8)
+ | subtlv->value[3];
+ }
+ if (subtlv->value[0] & 0x40) {
+ st->flag_vid = 1;
+ st->vid = ((subtlv->value[10] & 0x0f) << 8) | subtlv->value[11];
+ }
+ memcpy(st->macaddr, subtlv->value + 4, 6);
+ return 0;
+}
+
+/* rfc5512 4.2 */
+static int subtlv_decode_proto_type(struct bgp_attr_encap_subtlv *subtlv,
+ struct bgp_tea_subtlv_proto_type *st)
+{
+ if (subtlv->length != 2) {
+ zlog_debug("%s, subtlv length %d does not equal 2", __func__,
+ subtlv->length);
+ return -1;
+ }
+ st->proto = (subtlv->value[0] << 8) | subtlv->value[1];
+ return 0;
+}
+
+/* rfc5512 4.3 */
+static int subtlv_decode_color(struct bgp_attr_encap_subtlv *subtlv,
+ struct bgp_tea_subtlv_color *st)
+{
+ if (subtlv->length != 8) {
+ zlog_debug("%s, subtlv length %d does not equal 8", __func__,
+ subtlv->length);
+ return -1;
+ }
+ if ((subtlv->value[0] != 0x03) || (subtlv->value[1] != 0x0b)
+ || (subtlv->value[2] != 0) || (subtlv->value[3] != 0)) {
+ zlog_debug("%s, subtlv value 1st 4 bytes are not 0x030b0000",
+ __func__);
+ return -1;
+ }
+ ptr_get_be32(subtlv->value + 4, &st->color);
+ return 0;
+}
+
+/* rfc 5566 4. */
+static int subtlv_decode_ipsec_ta(struct bgp_attr_encap_subtlv *subtlv,
+ struct bgp_tea_subtlv_ipsec_ta *st)
+{
+ st->authenticator_length = subtlv->length - 2;
+ if (st->authenticator_length > sizeof(st->value)) {
+ zlog_debug(
+ "%s, authenticator length %d exceeds storage maximum %d",
+ __func__, st->authenticator_length,
+ (int)sizeof(st->value));
+ return -1;
+ }
+ st->authenticator_type = (subtlv->value[0] << 8) | subtlv->value[1];
+ memcpy(st->value, subtlv->value + 2, st->authenticator_length);
+ return 0;
+}
+
+/* draft-rosen-idr-tunnel-encaps 2.1 */
+static int
+subtlv_decode_remote_endpoint(struct bgp_attr_encap_subtlv *subtlv,
+ struct bgp_tea_subtlv_remote_endpoint *st)
+{
+ int i;
+ if (subtlv->length != 8 && subtlv->length != 20) {
+ zlog_debug("%s, subtlv length %d does not equal 8 or 20",
+ __func__, subtlv->length);
+ return -1;
+ }
+ if (subtlv->length == 8) {
+ st->family = AF_INET;
+ memcpy(&st->ip_address.v4.s_addr, subtlv->value,
+ IPV4_MAX_BYTELEN);
+ } else {
+ st->family = AF_INET6;
+ memcpy(&(st->ip_address.v6.s6_addr), subtlv->value,
+ IPV6_MAX_BYTELEN);
+ }
+ i = subtlv->length - 4;
+ ptr_get_be32(subtlv->value + i, &st->as4);
+ return 0;
+}
+
+/***********************************************************************
+ * TUNNEL TYPE-SPECIFIC TLV DECODE
+ ***********************************************************************/
+
+int tlv_to_bgp_encap_type_l2tpv3overip(
+ struct bgp_attr_encap_subtlv *stlv, /* subtlv chain */
+ struct bgp_encap_type_l2tpv3_over_ip *bet) /* caller-allocated */
+{
+ struct bgp_attr_encap_subtlv *st;
+ int rc = 0;
+
+ for (st = stlv; st; st = st->next) {
+ switch (st->type) {
+ case BGP_ENCAP_SUBTLV_TYPE_ENCAPSULATION:
+ rc |= subtlv_decode_encap_l2tpv3_over_ip(
+ st, &bet->st_encap);
+ SET_SUBTLV_FLAG(bet, BGP_TEA_SUBTLV_ENCAP);
+ break;
+
+ case BGP_ENCAP_SUBTLV_TYPE_PROTO_TYPE:
+ rc |= subtlv_decode_proto_type(st, &bet->st_proto);
+ SET_SUBTLV_FLAG(bet, BGP_TEA_SUBTLV_PROTO_TYPE);
+ break;
+
+ case BGP_ENCAP_SUBTLV_TYPE_COLOR:
+ rc |= subtlv_decode_color(st, &bet->st_color);
+ SET_SUBTLV_FLAG(bet, BGP_TEA_SUBTLV_COLOR);
+ break;
+
+ case BGP_ENCAP_SUBTLV_TYPE_REMOTE_ENDPOINT:
+ rc |= subtlv_decode_remote_endpoint(st,
+ &bet->st_endpoint);
+ SET_SUBTLV_FLAG(bet, BGP_TEA_SUBTLV_REMOTE_ENDPOINT);
+ break;
+
+ default:
+ zlog_debug("%s: unexpected subtlv type %d", __func__,
+ st->type);
+ rc |= -1;
+ break;
+ }
+ }
+ return rc;
+}
+
+int tlv_to_bgp_encap_type_gre(
+ struct bgp_attr_encap_subtlv *stlv, /* subtlv chain */
+ struct bgp_encap_type_gre *bet) /* caller-allocated */
+{
+ struct bgp_attr_encap_subtlv *st;
+ int rc = 0;
+
+ for (st = stlv; st; st = st->next) {
+ switch (st->type) {
+ case BGP_ENCAP_SUBTLV_TYPE_ENCAPSULATION:
+ rc |= subtlv_decode_encap_gre(st, &bet->st_encap);
+ SET_SUBTLV_FLAG(bet, BGP_TEA_SUBTLV_ENCAP);
+ break;
+
+ case BGP_ENCAP_SUBTLV_TYPE_PROTO_TYPE:
+ rc |= subtlv_decode_proto_type(st, &bet->st_proto);
+ SET_SUBTLV_FLAG(bet, BGP_TEA_SUBTLV_PROTO_TYPE);
+ break;
+
+ case BGP_ENCAP_SUBTLV_TYPE_COLOR:
+ rc |= subtlv_decode_color(st, &bet->st_color);
+ SET_SUBTLV_FLAG(bet, BGP_TEA_SUBTLV_COLOR);
+ break;
+
+ case BGP_ENCAP_SUBTLV_TYPE_REMOTE_ENDPOINT:
+ rc |= subtlv_decode_remote_endpoint(st,
+ &bet->st_endpoint);
+ SET_SUBTLV_FLAG(bet, BGP_TEA_SUBTLV_REMOTE_ENDPOINT);
+ break;
+
+ default:
+ zlog_debug("%s: unexpected subtlv type %d", __func__,
+ st->type);
+ rc |= -1;
+ break;
+ }
+ }
+ return rc;
+}
+
+int tlv_to_bgp_encap_type_ip_in_ip(
+ struct bgp_attr_encap_subtlv *stlv, /* subtlv chain */
+ struct bgp_encap_type_ip_in_ip *bet) /* caller-allocated */
+{
+ struct bgp_attr_encap_subtlv *st;
+ int rc = 0;
+
+ for (st = stlv; st; st = st->next) {
+ switch (st->type) {
+ case BGP_ENCAP_SUBTLV_TYPE_PROTO_TYPE:
+ rc |= subtlv_decode_proto_type(st, &bet->st_proto);
+ SET_SUBTLV_FLAG(bet, BGP_TEA_SUBTLV_PROTO_TYPE);
+ break;
+
+ case BGP_ENCAP_SUBTLV_TYPE_COLOR:
+ rc |= subtlv_decode_color(st, &bet->st_color);
+ SET_SUBTLV_FLAG(bet, BGP_TEA_SUBTLV_COLOR);
+ break;
+
+ case BGP_ENCAP_SUBTLV_TYPE_REMOTE_ENDPOINT:
+ rc |= subtlv_decode_remote_endpoint(st,
+ &bet->st_endpoint);
+ SET_SUBTLV_FLAG(bet, BGP_TEA_SUBTLV_REMOTE_ENDPOINT);
+ break;
+
+ default:
+ zlog_debug("%s: unexpected subtlv type %d", __func__,
+ st->type);
+ rc |= -1;
+ break;
+ }
+ }
+ return rc;
+}
+
+int tlv_to_bgp_encap_type_transmit_tunnel_endpoint(
+ struct bgp_attr_encap_subtlv *stlv,
+ struct bgp_encap_type_transmit_tunnel_endpoint *bet)
+{
+ struct bgp_attr_encap_subtlv *st;
+ int rc = 0;
+
+ for (st = stlv; st; st = st->next) {
+ switch (st->type) {
+
+ case BGP_ENCAP_SUBTLV_TYPE_REMOTE_ENDPOINT:
+ rc |= subtlv_decode_remote_endpoint(st,
+ &bet->st_endpoint);
+ SET_SUBTLV_FLAG(bet, BGP_TEA_SUBTLV_REMOTE_ENDPOINT);
+ break;
+
+ default:
+ zlog_debug("%s: unexpected subtlv type %d", __func__,
+ st->type);
+ rc |= -1;
+ break;
+ }
+ }
+ return rc;
+}
+
+int tlv_to_bgp_encap_type_ipsec_in_tunnel_mode(
+ struct bgp_attr_encap_subtlv *stlv, /* subtlv chain */
+ struct bgp_encap_type_ipsec_in_tunnel_mode *bet) /* caller-allocated */
+{
+ struct bgp_attr_encap_subtlv *st;
+ int rc = 0;
+
+ for (st = stlv; st; st = st->next) {
+ switch (st->type) {
+ case BGP_ENCAP_SUBTLV_TYPE_IPSEC_TA:
+ rc |= subtlv_decode_ipsec_ta(st, &bet->st_ipsec_ta);
+ SET_SUBTLV_FLAG(bet, BGP_TEA_SUBTLV_IPSEC_TA);
+ break;
+
+ case BGP_ENCAP_SUBTLV_TYPE_REMOTE_ENDPOINT:
+ rc |= subtlv_decode_remote_endpoint(st,
+ &bet->st_endpoint);
+ SET_SUBTLV_FLAG(bet, BGP_TEA_SUBTLV_REMOTE_ENDPOINT);
+ break;
+
+ default:
+ zlog_debug("%s: unexpected subtlv type %d", __func__,
+ st->type);
+ rc |= -1;
+ break;
+ }
+ }
+ return rc;
+}
+
+int tlv_to_bgp_encap_type_ip_in_ip_tunnel_with_ipsec_transport_mode(
+ struct bgp_attr_encap_subtlv *stlv,
+ struct bgp_encap_type_ip_in_ip_tunnel_with_ipsec_transport_mode *bet)
+{
+ struct bgp_attr_encap_subtlv *st;
+ int rc = 0;
+
+ for (st = stlv; st; st = st->next) {
+ switch (st->type) {
+ case BGP_ENCAP_SUBTLV_TYPE_IPSEC_TA:
+ rc |= subtlv_decode_ipsec_ta(st, &bet->st_ipsec_ta);
+ SET_SUBTLV_FLAG(bet, BGP_TEA_SUBTLV_IPSEC_TA);
+ break;
+
+ case BGP_ENCAP_SUBTLV_TYPE_REMOTE_ENDPOINT:
+ rc |= subtlv_decode_remote_endpoint(st,
+ &bet->st_endpoint);
+ SET_SUBTLV_FLAG(bet, BGP_TEA_SUBTLV_REMOTE_ENDPOINT);
+ break;
+
+ default:
+ zlog_debug("%s: unexpected subtlv type %d", __func__,
+ st->type);
+ rc |= -1;
+ break;
+ }
+ }
+ return rc;
+}
+
+int tlv_to_bgp_encap_type_mpls_in_ip_tunnel_with_ipsec_transport_mode(
+ struct bgp_attr_encap_subtlv *stlv,
+ struct bgp_encap_type_mpls_in_ip_tunnel_with_ipsec_transport_mode *bet)
+{
+ struct bgp_attr_encap_subtlv *st;
+ int rc = 0;
+
+ for (st = stlv; st; st = st->next) {
+ switch (st->type) {
+ case BGP_ENCAP_SUBTLV_TYPE_IPSEC_TA:
+ rc |= subtlv_decode_ipsec_ta(st, &bet->st_ipsec_ta);
+ SET_SUBTLV_FLAG(bet, BGP_TEA_SUBTLV_IPSEC_TA);
+ break;
+
+ case BGP_ENCAP_SUBTLV_TYPE_REMOTE_ENDPOINT:
+ rc |= subtlv_decode_remote_endpoint(st,
+ &bet->st_endpoint);
+ SET_SUBTLV_FLAG(bet, BGP_TEA_SUBTLV_REMOTE_ENDPOINT);
+ break;
+
+ default:
+ zlog_debug("%s: unexpected subtlv type %d", __func__,
+ st->type);
+ rc |= -1;
+ break;
+ }
+ }
+ return rc;
+}
+
+int tlv_to_bgp_encap_type_vxlan(struct bgp_attr_encap_subtlv *stlv,
+ struct bgp_encap_type_vxlan *bet)
+{
+ struct bgp_attr_encap_subtlv *st;
+ int rc = 0;
+
+ for (st = stlv; st; st = st->next) {
+ switch (st->type) {
+
+ case BGP_ENCAP_SUBTLV_TYPE_REMOTE_ENDPOINT:
+ rc |= subtlv_decode_remote_endpoint(st,
+ &bet->st_endpoint);
+ SET_SUBTLV_FLAG(bet, BGP_TEA_SUBTLV_REMOTE_ENDPOINT);
+ break;
+
+ default:
+ zlog_debug("%s: unexpected subtlv type %d", __func__,
+ st->type);
+ rc |= -1;
+ break;
+ }
+ }
+ return rc;
+}
+
+int tlv_to_bgp_encap_type_nvgre(struct bgp_attr_encap_subtlv *stlv,
+ struct bgp_encap_type_nvgre *bet)
+{
+ struct bgp_attr_encap_subtlv *st;
+ int rc = 0;
+
+ for (st = stlv; st; st = st->next) {
+ switch (st->type) {
+
+ case BGP_ENCAP_SUBTLV_TYPE_REMOTE_ENDPOINT:
+ rc |= subtlv_decode_remote_endpoint(st,
+ &bet->st_endpoint);
+ SET_SUBTLV_FLAG(bet, BGP_TEA_SUBTLV_REMOTE_ENDPOINT);
+ break;
+
+ default:
+ zlog_debug("%s: unexpected subtlv type %d", __func__,
+ st->type);
+ rc |= -1;
+ break;
+ }
+ }
+ return rc;
+}
+
+int tlv_to_bgp_encap_type_mpls(struct bgp_attr_encap_subtlv *stlv,
+ struct bgp_encap_type_mpls *bet)
+{
+ struct bgp_attr_encap_subtlv *st;
+ int rc = 0;
+
+ for (st = stlv; st; st = st->next) {
+ switch (st->type) {
+
+ case BGP_ENCAP_SUBTLV_TYPE_REMOTE_ENDPOINT:
+ rc |= subtlv_decode_remote_endpoint(st,
+ &bet->st_endpoint);
+ SET_SUBTLV_FLAG(bet, BGP_TEA_SUBTLV_REMOTE_ENDPOINT);
+ break;
+
+ default:
+ zlog_debug("%s: unexpected subtlv type %d", __func__,
+ st->type);
+ rc |= -1;
+ break;
+ }
+ }
+ return rc;
+}
+
+int tlv_to_bgp_encap_type_mpls_in_gre(struct bgp_attr_encap_subtlv *stlv,
+ struct bgp_encap_type_mpls_in_gre *bet)
+{
+ struct bgp_attr_encap_subtlv *st;
+ int rc = 0;
+
+ for (st = stlv; st; st = st->next) {
+ switch (st->type) {
+
+ case BGP_ENCAP_SUBTLV_TYPE_REMOTE_ENDPOINT:
+ rc |= subtlv_decode_remote_endpoint(st,
+ &bet->st_endpoint);
+ SET_SUBTLV_FLAG(bet, BGP_TEA_SUBTLV_REMOTE_ENDPOINT);
+ break;
+
+ default:
+ zlog_debug("%s: unexpected subtlv type %d", __func__,
+ st->type);
+ rc |= -1;
+ break;
+ }
+ }
+ return rc;
+}
+
+int tlv_to_bgp_encap_type_vxlan_gpe(struct bgp_attr_encap_subtlv *stlv,
+ struct bgp_encap_type_vxlan_gpe *bet)
+{
+ struct bgp_attr_encap_subtlv *st;
+ int rc = 0;
+
+ for (st = stlv; st; st = st->next) {
+ switch (st->type) {
+
+ case BGP_ENCAP_SUBTLV_TYPE_REMOTE_ENDPOINT:
+ rc |= subtlv_decode_remote_endpoint(st,
+ &bet->st_endpoint);
+ SET_SUBTLV_FLAG(bet, BGP_TEA_SUBTLV_REMOTE_ENDPOINT);
+ break;
+
+ default:
+ zlog_debug("%s: unexpected subtlv type %d", __func__,
+ st->type);
+ rc |= -1;
+ break;
+ }
+ }
+ return rc;
+}
+
+int tlv_to_bgp_encap_type_mpls_in_udp(struct bgp_attr_encap_subtlv *stlv,
+ struct bgp_encap_type_mpls_in_udp *bet)
+{
+ struct bgp_attr_encap_subtlv *st;
+ int rc = 0;
+
+ for (st = stlv; st; st = st->next) {
+ switch (st->type) {
+
+ case BGP_ENCAP_SUBTLV_TYPE_REMOTE_ENDPOINT:
+ rc |= subtlv_decode_remote_endpoint(st,
+ &bet->st_endpoint);
+ SET_SUBTLV_FLAG(bet, BGP_TEA_SUBTLV_REMOTE_ENDPOINT);
+ break;
+
+ default:
+ zlog_debug("%s: unexpected subtlv type %d", __func__,
+ st->type);
+ rc |= -1;
+ break;
+ }
+ }
+ return rc;
+}
+
+int tlv_to_bgp_encap_type_pbb(
+ struct bgp_attr_encap_subtlv *stlv, /* subtlv chain */
+ struct bgp_encap_type_pbb *bet) /* caller-allocated */
+{
+ struct bgp_attr_encap_subtlv *st;
+ int rc = 0;
+
+ for (st = stlv; st; st = st->next) {
+ switch (st->type) {
+ case BGP_ENCAP_SUBTLV_TYPE_ENCAPSULATION:
+ rc |= subtlv_decode_encap_pbb(st, &bet->st_encap);
+ SET_SUBTLV_FLAG(bet, BGP_TEA_SUBTLV_ENCAP);
+ break;
+
+ case BGP_ENCAP_SUBTLV_TYPE_REMOTE_ENDPOINT:
+ rc |= subtlv_decode_remote_endpoint(st,
+ &bet->st_endpoint);
+ SET_SUBTLV_FLAG(bet, BGP_TEA_SUBTLV_REMOTE_ENDPOINT);
+ break;
+
+ default:
+ zlog_debug("%s: unexpected subtlv type %d", __func__,
+ st->type);
+ rc |= -1;
+ break;
+ }
+ }
+ return rc;
+}
diff --git a/bgpd/bgp_encap_tlv.h b/bgpd/bgp_encap_tlv.h
new file mode 100644
index 0000000..64f9a9c
--- /dev/null
+++ b/bgpd/bgp_encap_tlv.h
@@ -0,0 +1,134 @@
+/*
+ * Copyright 2015, LabN Consulting, L.L.C.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program; see the file COPYING; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
+ */
+
+#ifndef _QUAGGA_BGP_ENCAP_TLV_H
+#define _QUAGGA_BGP_ENCAP_TLV_H
+
+/***********************************************************************
+ * TUNNEL TYPE-SPECIFIC TLV ENCODE
+ ***********************************************************************/
+
+extern void
+bgp_encap_type_l2tpv3overip_to_tlv(struct bgp_encap_type_l2tpv3_over_ip *bet,
+ struct attr *attr);
+
+extern void bgp_encap_type_gre_to_tlv(struct bgp_encap_type_gre *bet,
+ struct attr *attr);
+
+extern void bgp_encap_type_ip_in_ip_to_tlv(struct bgp_encap_type_ip_in_ip *bet,
+ struct attr *attr);
+
+extern void bgp_encap_type_transmit_tunnel_endpoint(
+ struct bgp_encap_type_transmit_tunnel_endpoint *bet, struct attr *attr);
+
+extern void bgp_encap_type_ipsec_in_tunnel_mode_to_tlv(
+ struct bgp_encap_type_ipsec_in_tunnel_mode *bet, struct attr *attr);
+
+extern void bgp_encap_type_ip_in_ip_tunnel_with_ipsec_transport_mode_to_tlv(
+ struct bgp_encap_type_ip_in_ip_tunnel_with_ipsec_transport_mode *bet,
+ struct attr *attr);
+
+extern void bgp_encap_type_mpls_in_ip_tunnel_with_ipsec_transport_mode_to_tlv(
+ struct bgp_encap_type_mpls_in_ip_tunnel_with_ipsec_transport_mode *bet,
+ struct attr *attr);
+
+extern void bgp_encap_type_pbb_to_tlv(struct bgp_encap_type_pbb *bet,
+ struct attr *attr);
+
+extern void bgp_encap_type_vxlan_to_tlv(struct bgp_encap_type_vxlan *bet,
+ struct attr *attr);
+
+extern void bgp_encap_type_nvgre_to_tlv(struct bgp_encap_type_nvgre *bet,
+ struct attr *attr);
+
+extern void bgp_encap_type_mpls_to_tlv(struct bgp_encap_type_mpls *bet,
+ struct attr *attr);
+
+extern void
+bgp_encap_type_mpls_in_gre_to_tlv(struct bgp_encap_type_mpls_in_gre *bet,
+ struct attr *attr);
+
+extern void
+bgp_encap_type_vxlan_gpe_to_tlv(struct bgp_encap_type_vxlan_gpe *bet,
+ struct attr *attr);
+
+extern void
+bgp_encap_type_mpls_in_udp_to_tlv(struct bgp_encap_type_mpls_in_udp *bet,
+ struct attr *attr);
+
+/***********************************************************************
+ * TUNNEL TYPE-SPECIFIC TLV DECODE
+ ***********************************************************************/
+
+extern int tlv_to_bgp_encap_type_l2tpv3overip(
+ struct bgp_attr_encap_subtlv *stlv, /* subtlv chain */
+ struct bgp_encap_type_l2tpv3_over_ip *bet); /* caller-allocated */
+
+extern int tlv_to_bgp_encap_type_gre(
+ struct bgp_attr_encap_subtlv *stlv, /* subtlv chain */
+ struct bgp_encap_type_gre *bet); /* caller-allocated */
+
+extern int tlv_to_bgp_encap_type_ip_in_ip(
+ struct bgp_attr_encap_subtlv *stlv, /* subtlv chain */
+ struct bgp_encap_type_ip_in_ip *bet); /* caller-allocated */
+
+extern int tlv_to_bgp_encap_type_transmit_tunnel_endpoint(
+ struct bgp_attr_encap_subtlv *stlv,
+ struct bgp_encap_type_transmit_tunnel_endpoint *bet);
+
+extern int tlv_to_bgp_encap_type_ipsec_in_tunnel_mode(
+ struct bgp_attr_encap_subtlv *stlv, /* subtlv chain */
+ struct bgp_encap_type_ipsec_in_tunnel_mode *bet); /* caller-allocated */
+
+extern int tlv_to_bgp_encap_type_ip_in_ip_tunnel_with_ipsec_transport_mode(
+ struct bgp_attr_encap_subtlv *stlv,
+ struct bgp_encap_type_ip_in_ip_tunnel_with_ipsec_transport_mode *bet);
+
+extern int tlv_to_bgp_encap_type_mpls_in_ip_tunnel_with_ipsec_transport_mode(
+ struct bgp_attr_encap_subtlv *stlv,
+ struct bgp_encap_type_mpls_in_ip_tunnel_with_ipsec_transport_mode *bet);
+
+extern int tlv_to_bgp_encap_type_vxlan(struct bgp_attr_encap_subtlv *stlv,
+ struct bgp_encap_type_vxlan *bet);
+
+extern int tlv_to_bgp_encap_type_nvgre(struct bgp_attr_encap_subtlv *stlv,
+ struct bgp_encap_type_nvgre *bet);
+
+extern int tlv_to_bgp_encap_type_mpls(struct bgp_attr_encap_subtlv *stlv,
+ struct bgp_encap_type_mpls *bet);
+
+extern int tlv_to_bgp_encap_type_mpls(struct bgp_attr_encap_subtlv *stlv,
+ struct bgp_encap_type_mpls *bet);
+
+extern int
+tlv_to_bgp_encap_type_mpls_in_gre(struct bgp_attr_encap_subtlv *stlv,
+ struct bgp_encap_type_mpls_in_gre *bet);
+
+extern int
+tlv_to_bgp_encap_type_vxlan_gpe(struct bgp_attr_encap_subtlv *stlv,
+ struct bgp_encap_type_vxlan_gpe *bet);
+
+extern int
+tlv_to_bgp_encap_type_mpls_in_udp(struct bgp_attr_encap_subtlv *stlv,
+ struct bgp_encap_type_mpls_in_udp *bet);
+
+extern int tlv_to_bgp_encap_type_pbb(
+ struct bgp_attr_encap_subtlv *stlv, /* subtlv chain */
+ struct bgp_encap_type_pbb *bet); /* caller-allocated */
+
+#endif /* _QUAGGA_BGP_ENCAP_TLV_H */
diff --git a/bgpd/bgp_encap_types.h b/bgpd/bgp_encap_types.h
new file mode 100644
index 0000000..8d1bf68
--- /dev/null
+++ b/bgpd/bgp_encap_types.h
@@ -0,0 +1,231 @@
+/*
+ * Copyright 2015, LabN Consulting, L.L.C.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program; see the file COPYING; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
+ */
+
+#ifndef _QUAGGA_BGP_ENCAP_TYPES_H
+#define _QUAGGA_BGP_ENCAP_TYPES_H
+
+#include "bgpd/bgp_ecommunity.h"
+
+/* from
+ * http://www.iana.org/assignments/bgp-parameters/bgp-parameters.xhtml#tunnel-types
+ */
+typedef enum {
+ BGP_ENCAP_TYPE_RESERVED = 0,
+ BGP_ENCAP_TYPE_L2TPV3_OVER_IP = 1,
+ BGP_ENCAP_TYPE_GRE = 2,
+ BGP_ENCAP_TYPE_TRANSMIT_TUNNEL_ENDPOINT = 3,
+ BGP_ENCAP_TYPE_IPSEC_IN_TUNNEL_MODE = 4,
+ BGP_ENCAP_TYPE_IP_IN_IP_TUNNEL_WITH_IPSEC_TRANSPORT_MODE = 5,
+ BGP_ENCAP_TYPE_MPLS_IN_IP_TUNNEL_WITH_IPSEC_TRANSPORT_MODE = 6,
+ BGP_ENCAP_TYPE_IP_IN_IP = 7,
+ BGP_ENCAP_TYPE_VXLAN = 8,
+ BGP_ENCAP_TYPE_NVGRE = 9,
+ BGP_ENCAP_TYPE_MPLS = 10, /* NOTE: Encap SAFI&Attribute not used */
+ BGP_ENCAP_TYPE_MPLS_IN_GRE = 11,
+ BGP_ENCAP_TYPE_VXLAN_GPE = 12,
+ BGP_ENCAP_TYPE_MPLS_IN_UDP = 13,
+ BGP_ENCAP_TYPE_PBB
+} bgp_encap_types;
+
+typedef enum {
+ BGP_ENCAP_SUBTLV_TYPE_ENCAPSULATION = 1,
+ BGP_ENCAP_SUBTLV_TYPE_PROTO_TYPE = 2,
+ BGP_ENCAP_SUBTLV_TYPE_IPSEC_TA = 3,
+ BGP_ENCAP_SUBTLV_TYPE_COLOR = 4,
+ BGP_ENCAP_SUBTLV_TYPE_REMOTE_ENDPOINT =
+ 6 /* speculative, IANA assignment TBD */
+} bgp_encap_subtlv_types;
+
+/*
+ * Tunnel Encapsulation Attribute subtlvs
+ */
+struct bgp_tea_subtlv_encap_l2tpv3_over_ip {
+ uint32_t sessionid;
+ uint8_t cookie_length;
+ uint8_t cookie[8];
+};
+
+struct bgp_tea_subtlv_encap_gre_key {
+ uint32_t gre_key;
+};
+
+struct bgp_tea_subtlv_encap_pbb {
+ uint32_t flag_isid : 1;
+ uint32_t flag_vid : 1;
+ uint32_t isid : 24;
+ uint16_t vid : 12;
+ uint8_t macaddr[6];
+};
+
+struct bgp_tea_subtlv_proto_type {
+ uint16_t proto; /* ether-type */
+};
+
+struct bgp_tea_subtlv_color {
+ uint32_t color;
+};
+
+/* per draft-rosen-idr-tunnel-encaps */
+struct bgp_tea_subtlv_remote_endpoint {
+ uint8_t family; /* IPv4 or IPv6 */
+ union {
+ struct in_addr v4;
+ struct in6_addr v6;
+ } ip_address;
+ as_t as4; /* always 4 bytes */
+};
+
+/*
+ * This is the length of the value part of the ipsec tunnel authenticator
+ * subtlv. Currently we only support the length for authenticator type 1.
+ */
+#define BGP_ENCAP_SUBTLV_IPSEC_TA_SIZE 20
+
+struct bgp_tea_subtlv_ipsec_ta {
+ uint16_t authenticator_type; /* only type 1 is supported so far */
+ uint8_t authenticator_length; /* octets in value field */
+ uint8_t value[BGP_ENCAP_SUBTLV_IPSEC_TA_SIZE];
+};
+
+/*
+ * Subtlv valid flags
+ * TBD change names to add "VALID"
+ */
+#define BGP_TEA_SUBTLV_ENCAP 0x00000001
+#define BGP_TEA_SUBTLV_PROTO_TYPE 0x00000002
+#define BGP_TEA_SUBTLV_COLOR 0x00000004
+#define BGP_TEA_SUBTLV_IPSEC_TA 0x00000008
+#define BGP_TEA_SUBTLV_REMOTE_ENDPOINT 0x00000010
+
+#define CHECK_SUBTLV_FLAG(ptr, flag) CHECK_FLAG((ptr)->valid_subtlvs, (flag))
+#define SET_SUBTLV_FLAG(ptr, flag) SET_FLAG((ptr)->valid_subtlvs, (flag))
+#define UNSET_SUBTLV_FLAG(ptr, flag) UNSET_FLAG((ptr)->valid_subtlvs, (flag))
+
+/*
+ * Tunnel Type-specific APIs
+ */
+struct bgp_encap_type_reserved {
+ uint32_t valid_subtlvs;
+ struct bgp_tea_subtlv_remote_endpoint st_endpoint; /* optional */
+};
+
+struct bgp_encap_type_l2tpv3_over_ip {
+ uint32_t valid_subtlvs;
+ struct bgp_tea_subtlv_encap_l2tpv3_over_ip st_encap;
+ struct bgp_tea_subtlv_proto_type st_proto; /* optional */
+ struct bgp_tea_subtlv_color st_color; /* optional */
+ struct bgp_tea_subtlv_remote_endpoint st_endpoint; /* optional */
+};
+
+struct bgp_encap_type_gre {
+ uint32_t valid_subtlvs;
+ struct bgp_tea_subtlv_encap_gre_key st_encap; /* optional */
+ struct bgp_tea_subtlv_proto_type st_proto; /* optional */
+ struct bgp_tea_subtlv_color st_color; /* optional */
+ struct bgp_tea_subtlv_remote_endpoint st_endpoint; /* optional */
+};
+
+struct bgp_encap_type_ip_in_ip {
+ uint32_t valid_subtlvs;
+ struct bgp_tea_subtlv_proto_type st_proto; /* optional */
+ struct bgp_tea_subtlv_color st_color; /* optional */
+ struct bgp_tea_subtlv_remote_endpoint st_endpoint; /* optional */
+};
+
+struct bgp_encap_type_transmit_tunnel_endpoint {
+ uint32_t valid_subtlvs;
+ struct bgp_tea_subtlv_remote_endpoint st_endpoint; /* optional */
+ /* No subtlvs defined in spec? */
+};
+
+struct bgp_encap_type_ipsec_in_tunnel_mode {
+ uint32_t valid_subtlvs;
+ struct bgp_tea_subtlv_ipsec_ta st_ipsec_ta; /* optional */
+ struct bgp_tea_subtlv_remote_endpoint st_endpoint; /* optional */
+};
+
+struct bgp_encap_type_ip_in_ip_tunnel_with_ipsec_transport_mode {
+ uint32_t valid_subtlvs;
+ struct bgp_tea_subtlv_ipsec_ta st_ipsec_ta; /* optional */
+ struct bgp_tea_subtlv_remote_endpoint st_endpoint; /* optional */
+};
+
+struct bgp_encap_type_mpls_in_ip_tunnel_with_ipsec_transport_mode {
+ uint32_t valid_subtlvs;
+ struct bgp_tea_subtlv_ipsec_ta st_ipsec_ta; /* optional */
+ struct bgp_tea_subtlv_remote_endpoint st_endpoint; /* optional */
+};
+
+#define VXLAN_ENCAP_MASK_VNID_VALID 0x80000000
+#define VXLAN_ENCAP_MASK_MAC_VALID 0x40000000
+
+struct bgp_encap_type_vxlan {
+ uint32_t valid_subtlvs;
+ struct bgp_tea_subtlv_remote_endpoint st_endpoint; /* optional */
+ /* draft-ietf-idr-tunnel-encaps-02 */
+ uint32_t vnid; /* does not include V and M bit */
+ uint8_t *mac_address; /* optional */
+};
+
+struct bgp_encap_type_nvgre {
+ uint32_t valid_subtlvs;
+ struct bgp_tea_subtlv_remote_endpoint st_endpoint; /* optional */
+ /* No subtlvs defined in spec? */
+};
+
+struct bgp_encap_type_mpls {
+ uint32_t valid_subtlvs;
+ struct bgp_tea_subtlv_remote_endpoint st_endpoint; /* optional */
+ /* No subtlvs defined in spec? */
+};
+
+struct bgp_encap_type_mpls_in_gre {
+ uint32_t valid_subtlvs;
+ struct bgp_tea_subtlv_remote_endpoint st_endpoint; /* optional */
+ /* No subtlvs defined in spec? */
+};
+
+struct bgp_encap_type_vxlan_gpe {
+ uint32_t valid_subtlvs;
+ struct bgp_tea_subtlv_remote_endpoint st_endpoint; /* optional */
+ /* No subtlvs defined in spec? */
+};
+
+struct bgp_encap_type_mpls_in_udp {
+ uint32_t valid_subtlvs;
+ struct bgp_tea_subtlv_remote_endpoint st_endpoint; /* optional */
+ /* No subtlvs defined in spec? */
+};
+
+struct bgp_encap_type_pbb {
+ uint32_t valid_subtlvs;
+ struct bgp_tea_subtlv_remote_endpoint st_endpoint; /* optional */
+ struct bgp_tea_subtlv_encap_pbb st_encap;
+};
+
+static inline void encode_encap_extcomm(bgp_encap_types tnl_type,
+ struct ecommunity_val *eval)
+{
+ memset(eval, 0, sizeof(*eval));
+ eval->val[0] = ECOMMUNITY_ENCODE_OPAQUE;
+ eval->val[1] = ECOMMUNITY_OPAQUE_SUBTYPE_ENCAP;
+ eval->val[6] = ((tnl_type) >> 8) & 0xff;
+ eval->val[7] = (tnl_type)&0xff;
+}
+
+#endif /* _QUAGGA_BGP_ENCAP_TYPES_H */
diff --git a/bgpd/bgp_errors.c b/bgpd/bgp_errors.c
new file mode 100644
index 0000000..9f87f8c
--- /dev/null
+++ b/bgpd/bgp_errors.c
@@ -0,0 +1,505 @@
+/*
+ * BGP-specific error messages.
+ * Copyright (C) 2018 Cumulus Networks, Inc.
+ * Don Slice
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License as published by the Free
+ * Software Foundation; either version 2 of the License, or (at your option)
+ * any later version.
+ *
+ * This program is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
+ * more details.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program; see the file COPYING; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
+ */
+
+#include <zebra.h>
+
+#include "lib/ferr.h"
+#include "bgp_errors.h"
+
+/* clang-format off */
+static struct log_ref ferr_bgp_warn[] = {
+ {
+ .code = EC_BGP_ASPATH_FEWER_HOPS,
+ .title = "BGP AS-path conversion has failed",
+ .description = "BGP has attempted to convert a AS2 to AS4 path and has failed",
+ .suggestion = "Open an Issue with all relevant log files and restart FRR"
+ },
+ {
+ .code = EC_BGP_DEFUNCT_SNPA_LEN,
+ .title = "BGP has received a value in a reserved field",
+ .description = "BGP has received a non-zero value in a reserved field that was used for SNPA-length at one point in time",
+ .suggestion = "BGP has peered with either a router that is attempting to send SNPA data or it has received a corrupted packet. If we are peering with a SNPA aware router(unlikely) upgrade that router, else open an Issue after gathering relevant log files",
+ },
+ {
+ .code = EC_BGP_MISSING_ATTRIBUTE,
+ .title = "BGP has received an update with missing a missing attribute",
+ .description = "BGP received update packets must have some minimum attribute information within them",
+ .suggestion = "Gather log data from this and remote peer and open an Issue with this data",
+ },
+ {
+ .code = EC_BGP_ATTRIBUTE_TOO_SMALL,
+ .title = "BGP udate packet with attribute data that is too small",
+ .description = "BGP has received an update packet that is too small to parse a given attribute. This typically means that something has gone wrong between us and the remote peer",
+ .suggestion = "Gather log data from this and remote peer and open an Issue with this data",
+ },
+ {
+ .code = EC_BGP_EXT_ATTRIBUTE_TOO_SMALL,
+ .title = "BGP udate packet with extended attribute data that is too small",
+ .description = "BGP has received an update packet that is too small to parse a given extended attribute. This typically means that something has gone wrong between us and the remote peer",
+ .suggestion = "Gather log data from this and remote peer and open an Issue with this data",
+ },
+ {
+ .code = EC_BGP_ATTRIBUTE_REPEATED,
+ .title = "BGP update packet received with a repeated attribute",
+ .description = "BGP has received an update packet with a attribute that is repeated more than one time for a particular route. This typically means that something has gone wrong between us and the remote peer",
+ .suggestion = "Gather log data from this and remote peer and open an Issue with this data",
+ },
+ {
+ .code = EC_BGP_ATTRIBUTE_TOO_LARGE,
+ .title = "BGP udate packet with attribute data that is too large",
+ .description = "BGP has received an update packet that has too much data in a particular attribute. This typically means that something has gone wrong between us and the remote peer",
+ .suggestion = "Gather log data from this and remote peer and open an Issue with this data",
+ },
+ {
+ .code = EC_BGP_ATTRIBUTE_PARSE_ERROR,
+ .title = "BGP update packet with attribute data has a parse error, specific to the attribute",
+ .description = "BGP has received an update packet with an attribute that when parsed does not make sense in some manner",
+ .suggestion = "Gather log data from this and remote peer and open an Issue with this data",
+ },
+ {
+ .code = EC_BGP_ATTRIBUTE_PARSE_WITHDRAW,
+ .title = "BGP update packet with a broken optional attribute has caused a withdraw of associated routes",
+ .description = "BGP has received a update packet with optional attributes that did not parse correctly, instead of resetting the peer, withdraw associated routes and note that this has happened",
+ .suggestion = "Gather log data from this and remote peer and open an Issue with this data",
+ },
+ {
+ .code = EC_BGP_ATTRIBUTE_FETCH_ERROR,
+ .title = "BGP update packet with a broken length",
+ .description = "BGP has received a update packet with an attribute that has an incorrect length",
+ .suggestion = "Gather log data from this and remote peer and open an Issue with this data",
+ },
+ {
+ .code = EC_BGP_ATTRIBUTES_MISMATCH,
+ .title = "BGP update packet with a length different than attribute data length",
+ .description = "BGP has received a update packet with attributes that when parsed do not correctly add up to packet data length",
+ .suggestion = "Gather log data from this and remote peer and open an Issue with this data",
+ },
+ {
+ .code = EC_BGP_DUMP,
+ .title = "BGP MRT dump subsystem has encountered an issue",
+ .description = "BGP has found that the attempted write of MRT data to a dump file has failed",
+ .suggestion = "Ensure BGP has permissions to write the specified file",
+ },
+ {
+ .code = EC_BGP_UPDATE_PACKET_SHORT,
+ .title = "BGP Update Packet is to Small",
+ .description = "The update packet received from a peer is to small",
+ .suggestion = "Determine the source of the update packet and examine that peer for what has gone wrong",
+ },
+ {
+ .code = EC_BGP_UPDATE_PACKET_LONG,
+ .title = "BGP Update Packet is to large",
+ .description = "The update packet received from a peer is to large",
+ .suggestion = "Determine the source of the update packet and examine that peer for what has gone wrong",
+ },
+ {
+ .code = EC_BGP_UNRECOGNIZED_CAPABILITY,
+ .title = "Unknown BGP Capability Received",
+ .description = "The negotiation of capabilities has received a capability that we do not know what to do with",
+ .suggestion = "Determine the source of the capability and remove the capability from what is sent",
+ },
+ {
+ .code = EC_BGP_NO_TCP_MD5,
+ .title = "Unable to set TCP MD5 option on socket",
+ .description = "BGP attempted to setup TCP MD5 configuration on the socket as per configuration but was unable to",
+ .suggestion = "Please collect log files and open Issue",
+ },
+ {
+ .code = EC_BGP_EVPN_PMSI_PRESENT,
+ .title = "BGP Received a EVPN NLRI with PMSI included",
+ .description = "BGP has received a type-3 NLRI with PMSI information. At this time FRR is not capable of properly handling this NLRI type",
+ .suggestion = "Setup peer to not send this type of data to FRR"
+ },
+ {
+ .code = EC_BGP_EVPN_VPN_VNI,
+ .title = "BGP has received a local macip and cannot properly handle it",
+ .description = "BGP has received a local macip from zebra and has no way to properly handle the macip because the vni is not setup properly",
+ .suggestion = "Ensure proper setup of BGP EVPN",
+ },
+ {
+ .code = EC_BGP_EVPN_ESI,
+ .title = "BGP has received a local ESI for deletion",
+ .description = "BGP has received a local ESI for deletion but when attempting to find the stored data internally was unable to find the information for deletion",
+ .suggestion = "Gather logging and open an Issue",
+ },
+ {
+ .code = EC_BGP_INVALID_LABEL_STACK,
+ .title = "BGP has received a label stack in a NLRI that does not have the BOS marked",
+ .description = "BGP when it receives a NLRI with a label stack should have the BOS marked, this received packet does not have this",
+ .suggestion = "Gather log information from here and remote peer and open an Issue",
+ },
+ {
+ .code = EC_BGP_ZEBRA_SEND,
+ .title = "BGP has attempted to send data to zebra and has failed to do so",
+ .description = "BGP has attempted to send data to zebra but has been unable to do so",
+ .suggestion = "Gather log data, open an Issue and restart FRR"
+ },
+ {
+ .code = EC_BGP_CAPABILITY_INVALID_LENGTH,
+ .title = "BGP has received a capability with an invalid length",
+ .description = "BGP has received a capability from it's peer who's size is wrong",
+ .suggestion = "Gather log files from here and from peer and open an Issue",
+ },
+ {
+ .code = EC_BGP_CAPABILITY_INVALID_DATA,
+ .title = "BGP has received capability data with invalid information",
+ .description = "BGP has noticed that during processing of capability information that data was wrong",
+ .suggestion = "Gather log files from here and from peer and open an Issue",
+ },
+ {
+ .code = EC_BGP_CAPABILITY_VENDOR,
+ .title = "BGP has received capability data specific to a particular vendor",
+ .description = "BGP has received a capability that is vendor specific and as such we have no knowledge of how to use this capability in FRR",
+ .suggestion = "On peer turn off this feature"
+ },
+ {
+ .code = EC_BGP_CAPABILITY_UNKNOWN,
+ .title = "BGP has received capability data for a unknown capability",
+ .description = "BGP has received a capability that it does not know how to decode. This may be due to a new feature that has not been coded into FRR or it may be a bug in the remote peer",
+ .suggestion = "Gather log files from here and from peer and open an Issue",
+ },
+ {
+ .code = EC_BGP_INVALID_NEXTHOP_LENGTH,
+ .title = "BGP is attempting to write an invalid nexthop length value",
+ .description = "BGP is in the process of building NLRI information for a peer and has discovered an inconsistent internal state",
+ .suggestion = "Gather log files and open an Issue, restart FRR",
+ },
+ {
+ .code = EC_BGP_SENDQ_STUCK_WARN,
+ .title = "BGP has been unable to send anything to a peer for an extended time",
+ .description = "The BGP peer does not seem to be receiving or processing any data received from us, causing updates to be delayed.",
+ .suggestion = "Check connectivity to the peer and that it is not overloaded",
+ },
+ {
+ .code = END_FERR,
+ }
+};
+
+static struct log_ref ferr_bgp_err[] = {
+ {
+ .code = EC_BGP_ATTR_FLAG,
+ .title = "BGP attribute flag is incorrect",
+ .description = "BGP attribute flag is set to the wrong value (Optional/Transitive/Partial)",
+ .suggestion = "Determine the source of the attribute and determine why the attribute flag has been set incorrectly"
+ },
+ {
+ .code = EC_BGP_ATTR_LEN,
+ .title = "BGP attribute length is incorrect",
+ .description = "BGP attribute length is incorrect",
+ .suggestion = "Determine the source of the attribute and determine why the attribute length has been set incorrectly"
+ },
+ {
+ .code = EC_BGP_ATTR_ORIGIN,
+ .title = "BGP attribute origin value invalid",
+ .description = "BGP attribute origin value is invalid",
+ .suggestion = "Determine the source of the attribute and determine why the origin attribute has been set incorrectly"
+ },
+ {
+ .code = EC_BGP_ATTR_MAL_AS_PATH,
+ .title = "BGP as path is invalid",
+ .description = "BGP as path has been malformed",
+ .suggestion = "Determine the source of the update and determine why the as path has been set incorrectly"
+ },
+ {
+ .code = EC_BGP_ATTR_FIRST_AS,
+ .title = "BGP as path first as is invalid",
+ .description = "BGP update has invalid first as in as path",
+ .suggestion = "Determine the source of the update and determine why the as path first as value has been set incorrectly"
+ },
+ {
+ .code = EC_BGP_ATTR_PMSI_TYPE,
+ .title = "BGP PMSI tunnel attribute type is invalid",
+ .description = "BGP update has invalid type for PMSI tunnel",
+ .suggestion = "Determine the source of the update and determine why the PMSI tunnel attribute type has been set incorrectly"
+ },
+ {
+ .code = EC_BGP_ATTR_PMSI_LEN,
+ .title = "BGP PMSI tunnel attribute length is invalid",
+ .description = "BGP update has invalid length for PMSI tunnel",
+ .suggestion = "Determine the source of the update and determine why the PMSI tunnel attribute length has been set incorrectly"
+ },
+ {
+ .code = EC_BGP_PEER_GROUP,
+ .title = "BGP peergroup operated on in error",
+ .description = "BGP operating on peer-group instead of peers included",
+ .suggestion = "Ensure the config doesn't contain peergroups contained within peergroups"
+ },
+ {
+ .code = EC_BGP_PEER_DELETE,
+ .title = "BGP failed to delete peer structure",
+ .description = "BGP was unable to delete peer structure when address-family removed",
+ .suggestion = "Determine if all expected peers are removed and restart FRR if not. Most likely a bug"
+ },
+ {
+ .code = EC_BGP_TABLE_CHUNK,
+ .title = "BGP failed to get table chunk memory",
+ .description = "BGP unable to get chunk memory for table manager",
+ .suggestion = "Ensure there is adequate memory on the device to support the table requirements"
+ },
+ {
+ .code = EC_BGP_MACIP_LEN,
+ .title = "BGP received MACIP with invalid IP addr len",
+ .description = "BGP received MACIP with invalid IP addr len from Zebra",
+ .suggestion = "Verify MACIP entries inserted in Zebra are correct. Most likely a bug"
+ },
+ {
+ .code = EC_BGP_LM_ERROR,
+ .title = "BGP received invalid label manager message",
+ .description = "BGP received invalid label manager message from label manager",
+ .suggestion = "Label manager sent invalid essage to BGP for wrong protocol, instance, etc. Most likely a bug"
+ },
+ {
+ .code = EC_BGP_JSON_MEM_ERROR,
+ .title = "BGP unable to allocate memory for JSON output",
+ .description = "BGP attempted to generate JSON output and was unable to allocate the memory required",
+ .suggestion = "Ensure that the device has adequate memory to support the required functions"
+ },
+ {
+ .code = EC_BGP_UPDGRP_ATTR_LEN,
+ .title = "BGP update had attributes too long to send",
+ .description = "BGP attempted to send an update but the attributes were too long to fit",
+ .suggestion = "Most likely a bug. If the problem persists, report the problem for troubleshooting"
+ },
+ {
+ .code = EC_BGP_UPDGRP_CREATE,
+ .title = "BGP update group creation failed",
+ .description = "BGP attempted to create an update group but was unable to",
+ .suggestion = "Most likely a bug. If the problem persists, report the problem for troubleshooting"
+ },
+ {
+ .code = EC_BGP_UPDATE_SND,
+ .title = "BGP error creating update packet",
+ .description = "BGP attempted to create an update packet but was unable to",
+ .suggestion = "Most likely a bug. If the problem persists, report the problem for troubleshooting"
+ },
+ {
+ .code = EC_BGP_PKT_OPEN,
+ .title = "BGP error receiving open packet",
+ .description = "BGP received an open from a peer that was invalid",
+ .suggestion = "Determine the sending peer and correct his invalid open packet"
+ },
+ {
+ .code = EC_BGP_SND_FAIL,
+ .title = "BGP error sending to peer",
+ .description = "BGP attempted to respond to open from a peer and failed",
+ .suggestion = "BGP attempted to respond to an open and could not sene the packet. Check local IP address for source"
+ },
+ {
+ .code = EC_BGP_INVALID_STATUS,
+ .title = "BGP error receiving from peer",
+ .description = "BGP received an update from a peer but status was incorrect",
+ .suggestion = "Most likely a bug. If the problem persists, report the problem for troubleshooting"
+ },
+ {
+ .code = EC_BGP_UPDATE_RCV,
+ .title = "BGP error receiving update packet",
+ .description = "BGP received an invalid update packet",
+ .suggestion = "Determine the source of the update and resolve the invalid update being sent"
+ },
+ {
+ .code = EC_BGP_NO_CAP,
+ .title = "BGP error due to capability not enabled",
+ .description = "BGP attempted a function that did not have the capability enabled",
+ .suggestion = "Enable the capability if this functionality is desired"
+ },
+ {
+ .code = EC_BGP_NOTIFY_RCV,
+ .title = "BGP error receiving notify message",
+ .description = "BGP unable to process notification message",
+ .suggestion = "BGP notify received while in stopped state. If the problem persists, report for troubleshooting"
+ },
+ {
+ .code = EC_BGP_KEEP_RCV,
+ .title = "BGP error receiving keepalive packet",
+ .description = "BGP unable to process keepalive packet",
+ .suggestion = "BGP keepalive received while in stopped state. If the problem persists, report for troubleshooting"
+ },
+ {
+ .code = EC_BGP_RFSH_RCV,
+ .title = "BGP error receiving route refresh message",
+ .description = "BGP unable to process route refresh message",
+ .suggestion = "BGP route refresh received while in stopped state. If the problem persists, report for troubleshooting"},
+ {
+ .code = EC_BGP_CAP_RCV,
+ .title = "BGP error capability message",
+ .description = "BGP unable to process received capability",
+ .suggestion = "BGP capability message received while in stopped state. If the problem persists, report for troubleshooting"
+ },
+ {
+ .code = EC_BGP_NH_UPD,
+ .title = "BGP error with nexthopo update",
+ .description = "BGP unable to process nexthop update",
+ .suggestion = "BGP received nexthop update but nexthop is not reachable in this bgp instance. Report for troubleshooting"
+ },
+ {
+ .code = EC_BGP_LABEL,
+ .title = "Failure to apply label",
+ .description = "BGP attempted to attempted to apply a label but could not",
+ .suggestion = "Most likely a bug. If the problem persists, report the problem for troubleshooting"
+ },
+ {
+ .code = EC_BGP_MULTIPATH,
+ .title = "Multipath specified is invalid",
+ .description = "BGP was started with an invalid ecmp/multipath value",
+ .suggestion = "Correct the ecmp/multipath value supplied when starting the BGP daemon"
+ },
+ {
+ .code = EC_BGP_PKT_PROCESS,
+ .title = "Failure to process a packet",
+ .description = "BGP attempted to process a received packet but could not",
+ .suggestion = "Most likely a bug. If the problem persists, report the problem for troubleshooting"
+ },
+ {
+ .code = EC_BGP_CONNECT,
+ .title = "Failure to connect to peer",
+ .description = "BGP attempted to send open to peer but couldn't connect",
+ .suggestion = "Most likely a bug. If the problem persists, report the problem for troubleshooting"
+ },
+ {
+ .code = EC_BGP_FSM,
+ .title = "BGP FSM issue",
+ .description = "BGP neighbor transition problem",
+ .suggestion = "Most likely a bug. If the problem persists, report the problem for troubleshooting"
+ },
+ {
+ .code = EC_BGP_VNI,
+ .title = "BGP VNI creation issue",
+ .description = "BGP could not create a new VNI",
+ .suggestion = "Most likely a bug. If the problem persists, report the problem for troubleshooting"
+ },
+ {
+ .code = EC_BGP_NO_DFLT,
+ .title = "BGP default instance missing",
+ .description = "BGP could not find default instance",
+ .suggestion = "Define a default instance of BGP since some feature requires it's existence"
+ },
+ {
+ .code = EC_BGP_VTEP_INVALID,
+ .title = "BGP remote VTEP invalid",
+ .description = "BGP remote VTEP is invalid and cannot be used",
+ .suggestion = "Correct remote VTEP configuration or resolve the source of the problem"
+ },
+ {
+ .code = EC_BGP_ES_INVALID,
+ .title = "BGP ES route error",
+ .description = "BGP ES route incorrect, learned both local and remote",
+ .suggestion = "Correct configuration or addressing so that same not learned both local and remote"
+ },
+ {
+ .code = EC_BGP_EVPN_ROUTE_DELETE,
+ .title = "BGP EVPN route delete error",
+ .description = "BGP attempted to delete an EVPN route and failed",
+ .suggestion = "Most likely a bug. If the problem persists, report the problem for troubleshooting"
+ },
+ {
+ .code = EC_BGP_EVPN_FAIL,
+ .title = "BGP EVPN install/uninstall error",
+ .description = "BGP attempted to install or uninstall an EVPN prefix and failed",
+ .suggestion = "Most likely a bug. If the problem persists, report the problem for troubleshooting"
+ },
+ {
+ .code = EC_BGP_EVPN_ROUTE_INVALID,
+ .title = "BGP EVPN route received with invalid contents",
+ .description = "BGP received an EVPN route with invalid contents",
+ .suggestion = "Determine the source of the EVPN route and resolve whatever is causing invalid contents"
+ },
+ {
+ .code = EC_BGP_EVPN_ROUTE_CREATE,
+ .title = "BGP EVPN route create error",
+ .description = "BGP attempted to create an EVPN route and failed",
+ .suggestion = "Most likely a bug. If the problem persists, report the problem for troubleshooting"
+ },
+ {
+ .code = EC_BGP_ES_CREATE,
+ .title = "BGP EVPN ES entry create error",
+ .description = "BGP attempted to create an EVPN ES entry and failed",
+ .suggestion = "Most likely a bug. If the problem persists, report the problem for troubleshooting"
+ },
+ {
+ .code = EC_BGP_EVPN_AS_MISMATCH,
+ .title = "BGP AS configuration issue",
+ .description = "BGP configuration attempted for a different AS than currently configured",
+ .suggestion = "Correct the configuration so that the correct BGP AS number is used"
+ },
+ {
+ .code = EC_BGP_EVPN_INSTANCE_MISMATCH,
+ .title = "BGP EVPN AS and process name mismatch",
+ .description = "BGP configuration has AS and process name mismatch",
+ .suggestion = "Correct the configuration so that the BGP AS number and instance name are consistent"
+ },
+ {
+ .code = EC_BGP_FLOWSPEC_PACKET,
+ .title = "BGP Flowspec packet processing error",
+ .description = "The BGP flowspec subsystem has detected a error in the send or receive of a packet",
+ .suggestion = "Gather log files from both sides of the peering relationship and open an issue"
+ },
+ {
+ .code = EC_BGP_FLOWSPEC_INSTALLATION,
+ .title = "BGP Flowspec Installation/removal Error",
+ .description = "The BGP flowspec subsystem has detected that there was a failure for installation/removal/modification of Flowspec from the dataplane",
+ .suggestion = "Gather log files from the router and open an issue, Restart FRR"
+ },
+ {
+ .code = EC_BGP_DOPPELGANGER_CONFIG,
+ .title = "BGP has detected a configuration overwrite during peer collision resolution",
+ .description = "As part of BGP startup, the peer and ourselves can start connections to each other at the same time. During this process BGP received additional configuration, but it was only applied to one of the two nascent connections. Depending on the result of collision detection and resolution this configuration might be lost. To remedy this, after performing collision detection and resolution the peer session has been reset in order to apply the new configuration.",
+ .suggestion = "Gather data and open a Issue so that this developmental escape can be fixed, the peer should have been reset",
+ },
+ {
+ .code = EC_BGP_ROUTER_ID_SAME,
+ .title = "BGP has detected a duplicate router id during collision resolution",
+ .description = "As part of normal collision detection for opening a connection to a peer, BGP has detected that the remote peer's router-id is the same as ours",
+ .suggestion = "Change one of the two router-id's",
+ },
+ {
+ .code = EC_BGP_INVALID_BGP_INSTANCE,
+ .title = "BGP instance for the specific vrf is invalid",
+ .description = "Indicates that specified bgp instance is NULL",
+ .suggestion = "Get log files from router and open an issue",
+ },
+ {
+ .code = EC_BGP_INVALID_ROUTE,
+ .title = "BGP route node is invalid",
+ .description = "BGP route for the specified AFI/SAFI is NULL",
+ .suggestion = "Get log files from router and open an issue",
+ },
+ {
+ .code = EC_BGP_NO_LL_ADDRESS_AVAILABLE,
+ .title = "BGP v6 peer with no LL address on outgoing interface",
+ .description = "BGP when using a v6 peer requires a v6 LL address to be configured on the outgoing interface as per RFC 4291 section 2.1",
+ .suggestion = "Add a v6 LL address to the outgoing interfaces as per RFC",
+ },
+ {
+ .code = EC_BGP_SENDQ_STUCK_PROPER,
+ .title = "BGP is shutting down a peer due to being unable to send anything for an extended time",
+ .description = "No BGP updates were successfully sent to the peer for more than twice the holdtime.",
+ .suggestion = "Check connectivity to the peer and that it is not overloaded",
+ },
+ {
+ .code = END_FERR,
+ }
+};
+/* clang-format on */
+
+void bgp_error_init(void)
+{
+ log_ref_add(ferr_bgp_err);
+ log_ref_add(ferr_bgp_warn);
+}
diff --git a/bgpd/bgp_errors.h b/bgpd/bgp_errors.h
new file mode 100644
index 0000000..0c0917c
--- /dev/null
+++ b/bgpd/bgp_errors.h
@@ -0,0 +1,111 @@
+/*
+ * BGP-specific error messages.
+ * Copyright (C) 2018 Cumulus Networks, Inc.
+ * Don Slice
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License as published by the Free
+ * Software Foundation; either version 2 of the License, or (at your option)
+ * any later version.
+ *
+ * This program is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
+ * more details.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program; see the file COPYING; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
+ */
+
+#ifndef __BGP_ERRORS_H__
+#define __BGP_ERRORS_H__
+
+#include "lib/ferr.h"
+
+enum bgp_log_refs {
+
+ EC_BGP_ATTR_FLAG = BGP_FERR_START,
+ EC_BGP_ATTR_LEN,
+ EC_BGP_ATTR_ORIGIN,
+ EC_BGP_ATTR_MAL_AS_PATH,
+ EC_BGP_ATTR_FIRST_AS,
+ EC_BGP_ATTR_MARTIAN_NH,
+ EC_BGP_ATTR_PMSI_TYPE,
+ EC_BGP_ATTR_PMSI_LEN,
+ EC_BGP_ATTR_NH_SEND_LEN,
+ EC_BGP_PEER_GROUP,
+ EC_BGP_PEER_DELETE,
+ EC_BGP_TABLE_CHUNK,
+ EC_BGP_MACIP_LEN,
+ EC_BGP_LM_ERROR,
+ EC_BGP_JSON_MEM_ERROR,
+ EC_BGP_UPDGRP_ATTR_LEN,
+ EC_BGP_UPDGRP_CREATE,
+ EC_BGP_UPDATE_SND,
+ EC_BGP_PKT_OPEN,
+ EC_BGP_SND_FAIL,
+ EC_BGP_INVALID_STATUS,
+ EC_BGP_UPDATE_RCV,
+ EC_BGP_NO_CAP,
+ EC_BGP_NOTIFY_RCV,
+ EC_BGP_KEEP_RCV,
+ EC_BGP_RFSH_RCV,
+ EC_BGP_CAP_RCV,
+ EC_BGP_NH_UPD,
+ EC_BGP_LABEL,
+ EC_BGP_MULTIPATH,
+ EC_BGP_PKT_PROCESS,
+ EC_BGP_CONNECT,
+ EC_BGP_FSM,
+ EC_BGP_VNI,
+ EC_BGP_NO_DFLT,
+ EC_BGP_VTEP_INVALID,
+ EC_BGP_ES_INVALID,
+ EC_BGP_EVPN_ROUTE_DELETE,
+ EC_BGP_EVPN_FAIL,
+ EC_BGP_EVPN_ROUTE_INVALID,
+ EC_BGP_EVPN_ROUTE_CREATE,
+ EC_BGP_ES_CREATE,
+ EC_BGP_EVPN_AS_MISMATCH,
+ EC_BGP_EVPN_INSTANCE_MISMATCH,
+ EC_BGP_FLOWSPEC_PACKET,
+ EC_BGP_FLOWSPEC_INSTALLATION,
+ EC_BGP_ASPATH_FEWER_HOPS,
+ EC_BGP_DEFUNCT_SNPA_LEN,
+ EC_BGP_MISSING_ATTRIBUTE,
+ EC_BGP_ATTRIBUTE_TOO_SMALL,
+ EC_BGP_EXT_ATTRIBUTE_TOO_SMALL,
+ EC_BGP_ATTRIBUTE_REPEATED,
+ EC_BGP_ATTRIBUTE_TOO_LARGE,
+ EC_BGP_ATTRIBUTE_PARSE_ERROR,
+ EC_BGP_ATTRIBUTE_PARSE_WITHDRAW,
+ EC_BGP_ATTRIBUTE_FETCH_ERROR,
+ EC_BGP_ATTRIBUTES_MISMATCH,
+ EC_BGP_DUMP,
+ EC_BGP_UPDATE_PACKET_SHORT,
+ EC_BGP_UPDATE_PACKET_LONG,
+ EC_BGP_UNRECOGNIZED_CAPABILITY,
+ EC_BGP_NO_TCP_MD5,
+ EC_BGP_EVPN_PMSI_PRESENT,
+ EC_BGP_EVPN_VPN_VNI,
+ EC_BGP_EVPN_ESI,
+ EC_BGP_INVALID_LABEL_STACK,
+ EC_BGP_ZEBRA_SEND,
+ EC_BGP_CAPABILITY_INVALID_LENGTH,
+ EC_BGP_CAPABILITY_INVALID_DATA,
+ EC_BGP_CAPABILITY_VENDOR,
+ EC_BGP_CAPABILITY_UNKNOWN,
+ EC_BGP_INVALID_NEXTHOP_LENGTH,
+ EC_BGP_DOPPELGANGER_CONFIG,
+ EC_BGP_ROUTER_ID_SAME,
+ EC_BGP_INVALID_BGP_INSTANCE,
+ EC_BGP_INVALID_ROUTE,
+ EC_BGP_NO_LL_ADDRESS_AVAILABLE,
+ EC_BGP_SENDQ_STUCK_WARN,
+ EC_BGP_SENDQ_STUCK_PROPER,
+};
+
+extern void bgp_error_init(void);
+
+#endif
diff --git a/bgpd/bgp_evpn.c b/bgpd/bgp_evpn.c
new file mode 100644
index 0000000..16b7dc3
--- /dev/null
+++ b/bgpd/bgp_evpn.c
@@ -0,0 +1,6518 @@
+/* Ethernet-VPN Packet and vty Processing File
+ * Copyright (C) 2016 6WIND
+ * Copyright (C) 2017 Cumulus Networks, Inc.
+ *
+ * This file is part of FRR.
+ *
+ * FRRouting is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License as published by the
+ * Free Software Foundation; either version 2, or (at your option) any
+ * later version.
+ *
+ * FRRouting is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program; see the file COPYING; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
+ */
+
+#include <zebra.h>
+
+#include "command.h"
+#include "filter.h"
+#include "prefix.h"
+#include "log.h"
+#include "memory.h"
+#include "stream.h"
+#include "hash.h"
+#include "jhash.h"
+#include "zclient.h"
+
+#include "lib/printfrr.h"
+
+#include "bgpd/bgp_attr_evpn.h"
+#include "bgpd/bgpd.h"
+#include "bgpd/bgp_table.h"
+#include "bgpd/bgp_route.h"
+#include "bgpd/bgp_attr.h"
+#include "bgpd/bgp_mplsvpn.h"
+#include "bgpd/bgp_label.h"
+#include "bgpd/bgp_evpn.h"
+#include "bgpd/bgp_evpn_private.h"
+#include "bgpd/bgp_evpn_mh.h"
+#include "bgpd/bgp_ecommunity.h"
+#include "bgpd/bgp_encap_types.h"
+#include "bgpd/bgp_debug.h"
+#include "bgpd/bgp_errors.h"
+#include "bgpd/bgp_aspath.h"
+#include "bgpd/bgp_zebra.h"
+#include "bgpd/bgp_nexthop.h"
+#include "bgpd/bgp_addpath.h"
+#include "bgpd/bgp_mac.h"
+#include "bgpd/bgp_vty.h"
+#include "bgpd/bgp_nht.h"
+#include "bgpd/bgp_trace.h"
+
+/*
+ * Definitions and external declarations.
+ */
+DEFINE_QOBJ_TYPE(bgpevpn);
+DEFINE_QOBJ_TYPE(bgp_evpn_es);
+
+
+/*
+ * Static function declarations
+ */
+static int delete_all_vni_routes(struct bgp *bgp, struct bgpevpn *vpn);
+static void bgp_evpn_remote_ip_hash_init(struct bgpevpn *evpn);
+static void bgp_evpn_remote_ip_hash_destroy(struct bgpevpn *evpn);
+static void bgp_evpn_remote_ip_hash_add(struct bgpevpn *vpn,
+ struct bgp_path_info *pi);
+static void bgp_evpn_remote_ip_hash_del(struct bgpevpn *vpn,
+ struct bgp_path_info *pi);
+static void bgp_evpn_remote_ip_hash_iterate(struct bgpevpn *vpn,
+ void (*func)(struct hash_bucket *,
+ void *),
+ void *arg);
+static void bgp_evpn_link_to_vni_svi_hash(struct bgp *bgp, struct bgpevpn *vpn);
+static void bgp_evpn_unlink_from_vni_svi_hash(struct bgp *bgp,
+ struct bgpevpn *vpn);
+static unsigned int vni_svi_hash_key_make(const void *p);
+static bool vni_svi_hash_cmp(const void *p1, const void *p2);
+static void bgp_evpn_remote_ip_process_nexthops(struct bgpevpn *vpn,
+ struct ipaddr *addr,
+ bool resolve);
+static void bgp_evpn_remote_ip_hash_link_nexthop(struct hash_bucket *bucket,
+ void *args);
+static void bgp_evpn_remote_ip_hash_unlink_nexthop(struct hash_bucket *bucket,
+ void *args);
+static struct in_addr zero_vtep_ip;
+
+/*
+ * Private functions.
+ */
+
+/*
+ * Make vni hash key.
+ */
+static unsigned int vni_hash_key_make(const void *p)
+{
+ const struct bgpevpn *vpn = p;
+ return (jhash_1word(vpn->vni, 0));
+}
+
+/*
+ * Comparison function for vni hash
+ */
+static bool vni_hash_cmp(const void *p1, const void *p2)
+{
+ const struct bgpevpn *vpn1 = p1;
+ const struct bgpevpn *vpn2 = p2;
+
+ return vpn1->vni == vpn2->vni;
+}
+
+int vni_list_cmp(void *p1, void *p2)
+{
+ const struct bgpevpn *vpn1 = p1;
+ const struct bgpevpn *vpn2 = p2;
+
+ return vpn1->vni - vpn2->vni;
+}
+
+/*
+ * Make vrf import route target hash key.
+ */
+static unsigned int vrf_import_rt_hash_key_make(const void *p)
+{
+ const struct vrf_irt_node *irt = p;
+ const char *pnt = irt->rt.val;
+
+ return jhash(pnt, 8, 0x5abc1234);
+}
+
+/*
+ * Comparison function for vrf import rt hash
+ */
+static bool vrf_import_rt_hash_cmp(const void *p1, const void *p2)
+{
+ const struct vrf_irt_node *irt1 = p1;
+ const struct vrf_irt_node *irt2 = p2;
+
+ return (memcmp(irt1->rt.val, irt2->rt.val, ECOMMUNITY_SIZE) == 0);
+}
+
+/*
+ * Create a new vrf import_rt in evpn instance
+ */
+static struct vrf_irt_node *vrf_import_rt_new(struct ecommunity_val *rt)
+{
+ struct bgp *bgp_evpn = NULL;
+ struct vrf_irt_node *irt;
+
+ bgp_evpn = bgp_get_evpn();
+ if (!bgp_evpn) {
+ flog_err(EC_BGP_NO_DFLT,
+ "vrf import rt new - evpn instance not created yet");
+ return NULL;
+ }
+
+ irt = XCALLOC(MTYPE_BGP_EVPN_VRF_IMPORT_RT,
+ sizeof(struct vrf_irt_node));
+
+ irt->rt = *rt;
+ irt->vrfs = list_new();
+
+ /* Add to hash */
+ (void)hash_get(bgp_evpn->vrf_import_rt_hash, irt, hash_alloc_intern);
+
+ return irt;
+}
+
+/*
+ * Free the vrf import rt node
+ */
+static void vrf_import_rt_free(struct vrf_irt_node *irt)
+{
+ struct bgp *bgp_evpn = NULL;
+
+ bgp_evpn = bgp_get_evpn();
+ if (!bgp_evpn) {
+ flog_err(EC_BGP_NO_DFLT,
+ "vrf import rt free - evpn instance not created yet");
+ return;
+ }
+
+ hash_release(bgp_evpn->vrf_import_rt_hash, irt);
+ list_delete(&irt->vrfs);
+ XFREE(MTYPE_BGP_EVPN_VRF_IMPORT_RT, irt);
+}
+
+static void hash_vrf_import_rt_free(struct vrf_irt_node *irt)
+{
+ XFREE(MTYPE_BGP_EVPN_VRF_IMPORT_RT, irt);
+}
+
+/*
+ * Function to lookup Import RT node - used to map a RT to set of
+ * VNIs importing routes with that RT.
+ */
+static struct vrf_irt_node *lookup_vrf_import_rt(struct ecommunity_val *rt)
+{
+ struct bgp *bgp_evpn = NULL;
+ struct vrf_irt_node *irt;
+ struct vrf_irt_node tmp;
+
+ bgp_evpn = bgp_get_evpn();
+ if (!bgp_evpn) {
+ flog_err(
+ EC_BGP_NO_DFLT,
+ "vrf import rt lookup - evpn instance not created yet");
+ return NULL;
+ }
+
+ memset(&tmp, 0, sizeof(tmp));
+ memcpy(&tmp.rt, rt, ECOMMUNITY_SIZE);
+ irt = hash_lookup(bgp_evpn->vrf_import_rt_hash, &tmp);
+ return irt;
+}
+
+/*
+ * Is specified VRF present on the RT's list of "importing" VRFs?
+ */
+static int is_vrf_present_in_irt_vrfs(struct list *vrfs, struct bgp *bgp_vrf)
+{
+ struct listnode *node = NULL, *nnode = NULL;
+ struct bgp *tmp_bgp_vrf = NULL;
+
+ for (ALL_LIST_ELEMENTS(vrfs, node, nnode, tmp_bgp_vrf)) {
+ if (tmp_bgp_vrf == bgp_vrf)
+ return 1;
+ }
+ return 0;
+}
+
+/*
+ * Make import route target hash key.
+ */
+static unsigned int import_rt_hash_key_make(const void *p)
+{
+ const struct irt_node *irt = p;
+ const char *pnt = irt->rt.val;
+
+ return jhash(pnt, 8, 0xdeadbeef);
+}
+
+/*
+ * Comparison function for import rt hash
+ */
+static bool import_rt_hash_cmp(const void *p1, const void *p2)
+{
+ const struct irt_node *irt1 = p1;
+ const struct irt_node *irt2 = p2;
+
+ return (memcmp(irt1->rt.val, irt2->rt.val, ECOMMUNITY_SIZE) == 0);
+}
+
+/*
+ * Create a new import_rt
+ */
+static struct irt_node *import_rt_new(struct bgp *bgp,
+ struct ecommunity_val *rt)
+{
+ struct irt_node *irt;
+
+ irt = XCALLOC(MTYPE_BGP_EVPN_IMPORT_RT, sizeof(struct irt_node));
+
+ irt->rt = *rt;
+ irt->vnis = list_new();
+
+ /* Add to hash */
+ (void)hash_get(bgp->import_rt_hash, irt, hash_alloc_intern);
+
+ return irt;
+}
+
+/*
+ * Free the import rt node
+ */
+static void import_rt_free(struct bgp *bgp, struct irt_node *irt)
+{
+ hash_release(bgp->import_rt_hash, irt);
+ list_delete(&irt->vnis);
+ XFREE(MTYPE_BGP_EVPN_IMPORT_RT, irt);
+}
+
+static void hash_import_rt_free(struct irt_node *irt)
+{
+ XFREE(MTYPE_BGP_EVPN_IMPORT_RT, irt);
+}
+
+/*
+ * Function to lookup Import RT node - used to map a RT to set of
+ * VNIs importing routes with that RT.
+ */
+static struct irt_node *lookup_import_rt(struct bgp *bgp,
+ struct ecommunity_val *rt)
+{
+ struct irt_node *irt;
+ struct irt_node tmp;
+
+ memset(&tmp, 0, sizeof(tmp));
+ memcpy(&tmp.rt, rt, ECOMMUNITY_SIZE);
+ irt = hash_lookup(bgp->import_rt_hash, &tmp);
+ return irt;
+}
+
+/*
+ * Is specified VNI present on the RT's list of "importing" VNIs?
+ */
+static int is_vni_present_in_irt_vnis(struct list *vnis, struct bgpevpn *vpn)
+{
+ struct listnode *node, *nnode;
+ struct bgpevpn *tmp_vpn;
+
+ for (ALL_LIST_ELEMENTS(vnis, node, nnode, tmp_vpn)) {
+ if (tmp_vpn == vpn)
+ return 1;
+ }
+
+ return 0;
+}
+
+/*
+ * Compare Route Targets.
+ */
+int bgp_evpn_route_target_cmp(struct ecommunity *ecom1,
+ struct ecommunity *ecom2)
+{
+ if (ecom1 && !ecom2)
+ return -1;
+
+ if (!ecom1 && ecom2)
+ return 1;
+
+ if (!ecom1 && !ecom2)
+ return 0;
+
+ if (ecom1->str && !ecom2->str)
+ return -1;
+
+ if (!ecom1->str && ecom2->str)
+ return 1;
+
+ if (!ecom1->str && !ecom2->str)
+ return 0;
+
+ return strcmp(ecom1->str, ecom2->str);
+}
+
+void bgp_evpn_xxport_delete_ecomm(void *val)
+{
+ struct ecommunity *ecomm = val;
+ ecommunity_free(&ecomm);
+}
+
+/*
+ * Mask off global-admin field of specified extended community (RT),
+ * just retain the local-admin field.
+ */
+static inline void mask_ecom_global_admin(struct ecommunity_val *dst,
+ struct ecommunity_val *src)
+{
+ uint8_t type;
+
+ type = src->val[0];
+ dst->val[0] = 0;
+ if (type == ECOMMUNITY_ENCODE_AS) {
+ dst->val[2] = dst->val[3] = 0;
+ } else if (type == ECOMMUNITY_ENCODE_AS4
+ || type == ECOMMUNITY_ENCODE_IP) {
+ dst->val[2] = dst->val[3] = 0;
+ dst->val[4] = dst->val[5] = 0;
+ }
+}
+
+/*
+ * Map one RT to specified VRF.
+ * bgp_vrf = BGP vrf instance
+ */
+static void map_vrf_to_rt(struct bgp *bgp_vrf, struct ecommunity_val *eval)
+{
+ struct vrf_irt_node *irt = NULL;
+ struct ecommunity_val eval_tmp;
+
+ /* If using "automatic" RT,
+ * we only care about the local-admin sub-field.
+ * This is to facilitate using L3VNI(VRF-VNI)
+ * as the RT for EBGP peering too.
+ */
+ memcpy(&eval_tmp, eval, ECOMMUNITY_SIZE);
+ if (!CHECK_FLAG(bgp_vrf->vrf_flags, BGP_VRF_IMPORT_RT_CFGD))
+ mask_ecom_global_admin(&eval_tmp, eval);
+
+ irt = lookup_vrf_import_rt(&eval_tmp);
+ if (irt && is_vrf_present_in_irt_vrfs(irt->vrfs, bgp_vrf))
+ /* Already mapped. */
+ return;
+
+ if (!irt)
+ irt = vrf_import_rt_new(&eval_tmp);
+
+ /* Add VRF to the list for this RT. */
+ listnode_add(irt->vrfs, bgp_vrf);
+}
+
+/*
+ * Unmap specified VRF from specified RT. If there are no other
+ * VRFs for this RT, then the RT hash is deleted.
+ * bgp_vrf: BGP VRF specific instance
+ */
+static void unmap_vrf_from_rt(struct bgp *bgp_vrf, struct vrf_irt_node *irt)
+{
+ /* Delete VRF from list for this RT. */
+ listnode_delete(irt->vrfs, bgp_vrf);
+ if (!listnode_head(irt->vrfs)) {
+ vrf_import_rt_free(irt);
+ }
+}
+
+/*
+ * Map one RT to specified VNI.
+ */
+static void map_vni_to_rt(struct bgp *bgp, struct bgpevpn *vpn,
+ struct ecommunity_val *eval)
+{
+ struct irt_node *irt;
+ struct ecommunity_val eval_tmp;
+
+ /* If using "automatic" RT, we only care about the local-admin
+ * sub-field.
+ * This is to facilitate using VNI as the RT for EBGP peering too.
+ */
+ memcpy(&eval_tmp, eval, ECOMMUNITY_SIZE);
+ if (!is_import_rt_configured(vpn))
+ mask_ecom_global_admin(&eval_tmp, eval);
+
+ irt = lookup_import_rt(bgp, &eval_tmp);
+ if (irt)
+ if (is_vni_present_in_irt_vnis(irt->vnis, vpn))
+ /* Already mapped. */
+ return;
+
+ if (!irt)
+ irt = import_rt_new(bgp, &eval_tmp);
+
+ /* Add VNI to the hash list for this RT. */
+ listnode_add(irt->vnis, vpn);
+}
+
+/*
+ * Unmap specified VNI from specified RT. If there are no other
+ * VNIs for this RT, then the RT hash is deleted.
+ */
+static void unmap_vni_from_rt(struct bgp *bgp, struct bgpevpn *vpn,
+ struct irt_node *irt)
+{
+ /* Delete VNI from hash list for this RT. */
+ listnode_delete(irt->vnis, vpn);
+ if (!listnode_head(irt->vnis)) {
+ import_rt_free(bgp, irt);
+ }
+}
+
+static void bgp_evpn_get_rmac_nexthop(struct bgpevpn *vpn,
+ const struct prefix_evpn *p,
+ struct attr *attr, uint8_t flags)
+{
+ struct bgp *bgp_vrf = vpn->bgp_vrf;
+
+ memset(&attr->rmac, 0, sizeof(struct ethaddr));
+ if (!bgp_vrf)
+ return;
+
+ if (p->prefix.route_type != BGP_EVPN_MAC_IP_ROUTE)
+ return;
+
+ /* Copy sys (pip) RMAC and PIP IP as nexthop
+ * in case of route is self MAC-IP,
+ * advertise-pip and advertise-svi-ip features
+ * are enabled.
+ * Otherwise, for all host MAC-IP route's
+ * copy anycast RMAC.
+ */
+ if (CHECK_FLAG(flags, BGP_EVPN_MACIP_TYPE_SVI_IP)
+ && bgp_vrf->evpn_info->advertise_pip &&
+ bgp_vrf->evpn_info->is_anycast_mac) {
+ /* copy sys rmac */
+ memcpy(&attr->rmac, &bgp_vrf->evpn_info->pip_rmac,
+ ETH_ALEN);
+ attr->nexthop = bgp_vrf->evpn_info->pip_ip;
+ attr->mp_nexthop_global_in =
+ bgp_vrf->evpn_info->pip_ip;
+ } else
+ memcpy(&attr->rmac, &bgp_vrf->rmac, ETH_ALEN);
+}
+
+/*
+ * Create RT extended community automatically from passed information:
+ * of the form AS:VNI.
+ * NOTE: We use only the lower 16 bits of the AS. This is sufficient as
+ * the need is to get a RT value that will be unique across different
+ * VNIs but the same across routers (in the same AS) for a particular
+ * VNI.
+ */
+static void form_auto_rt(struct bgp *bgp, vni_t vni, struct list *rtl)
+{
+ struct ecommunity_val eval;
+ struct ecommunity *ecomadd, *ecom;
+ bool ecom_found = false;
+ struct listnode *node;
+
+ if (bgp->advertise_autort_rfc8365)
+ vni |= EVPN_AUTORT_VXLAN;
+ encode_route_target_as((bgp->as & 0xFFFF), vni, &eval);
+
+ ecomadd = ecommunity_new();
+ ecommunity_add_val(ecomadd, &eval, false, false);
+ for (ALL_LIST_ELEMENTS_RO(rtl, node, ecom))
+ if (ecommunity_cmp(ecomadd, ecom)) {
+ ecom_found = true;
+ break;
+ }
+
+ if (!ecom_found)
+ listnode_add_sort(rtl, ecomadd);
+ else
+ ecommunity_free(&ecomadd);
+}
+
+/*
+ * Derive RD and RT for a VNI automatically. Invoked at the time of
+ * creation of a VNI.
+ */
+static void derive_rd_rt_for_vni(struct bgp *bgp, struct bgpevpn *vpn)
+{
+ bgp_evpn_derive_auto_rd(bgp, vpn);
+ bgp_evpn_derive_auto_rt_import(bgp, vpn);
+ bgp_evpn_derive_auto_rt_export(bgp, vpn);
+}
+
+/*
+ * Convert nexthop (remote VTEP IP) into an IPv6 address.
+ */
+static void evpn_convert_nexthop_to_ipv6(struct attr *attr)
+{
+ if (BGP_ATTR_NEXTHOP_AFI_IP6(attr))
+ return;
+ ipv4_to_ipv4_mapped_ipv6(&attr->mp_nexthop_global, attr->nexthop);
+ attr->mp_nexthop_len = IPV6_MAX_BYTELEN;
+}
+
+struct bgp_dest *bgp_global_evpn_node_get(struct bgp_table *table, afi_t afi,
+ safi_t safi,
+ const struct prefix_evpn *evp,
+ struct prefix_rd *prd)
+{
+ struct prefix_evpn global_p;
+
+ if (evp->prefix.route_type == BGP_EVPN_AD_ROUTE) {
+ /* prefix in the global table doesn't include the VTEP-IP so
+ * we need to create a different copy of the prefix
+ */
+ evpn_type1_prefix_global_copy(&global_p, evp);
+ evp = &global_p;
+ }
+ return bgp_afi_node_get(table, afi, safi, (struct prefix *)evp, prd);
+}
+
+struct bgp_dest *bgp_global_evpn_node_lookup(struct bgp_table *table, afi_t afi,
+ safi_t safi,
+ const struct prefix_evpn *evp,
+ struct prefix_rd *prd)
+{
+ struct prefix_evpn global_p;
+
+ if (evp->prefix.route_type == BGP_EVPN_AD_ROUTE) {
+ /* prefix in the global table doesn't include the VTEP-IP so
+ * we need to create a different copy of the prefix
+ */
+ evpn_type1_prefix_global_copy(&global_p, evp);
+ evp = &global_p;
+ }
+ return bgp_afi_node_lookup(table, afi, safi, (struct prefix *)evp, prd);
+}
+
+/*
+ * Add (update) or delete MACIP from zebra.
+ */
+static int bgp_zebra_send_remote_macip(struct bgp *bgp, struct bgpevpn *vpn,
+ const struct prefix_evpn *p,
+ struct in_addr remote_vtep_ip, int add,
+ uint8_t flags, uint32_t seq, esi_t *esi)
+{
+ struct stream *s;
+ uint16_t ipa_len;
+ static struct in_addr zero_remote_vtep_ip;
+
+ /* Check socket. */
+ if (!zclient || zclient->sock < 0)
+ return 0;
+
+ /* Don't try to register if Zebra doesn't know of this instance. */
+ if (!IS_BGP_INST_KNOWN_TO_ZEBRA(bgp)) {
+ if (BGP_DEBUG(zebra, ZEBRA))
+ zlog_debug(
+ "%s: No zebra instance to talk to, not installing remote macip",
+ __func__);
+ return 0;
+ }
+
+ if (!esi)
+ esi = zero_esi;
+ s = zclient->obuf;
+ stream_reset(s);
+
+ zclient_create_header(
+ s, add ? ZEBRA_REMOTE_MACIP_ADD : ZEBRA_REMOTE_MACIP_DEL,
+ bgp->vrf_id);
+ stream_putl(s, vpn->vni);
+ stream_put(s, &p->prefix.macip_addr.mac.octet, ETH_ALEN); /* Mac Addr */
+ /* IP address length and IP address, if any. */
+ if (is_evpn_prefix_ipaddr_none(p))
+ stream_putw(s, 0);
+ else {
+ ipa_len = is_evpn_prefix_ipaddr_v4(p) ? IPV4_MAX_BYTELEN
+ : IPV6_MAX_BYTELEN;
+ stream_putw(s, ipa_len);
+ stream_put(s, &p->prefix.macip_addr.ip.ip.addr, ipa_len);
+ }
+ /* If the ESI is valid that becomes the nexthop; tape out the
+ * VTEP-IP for that case
+ */
+ if (bgp_evpn_is_esi_valid(esi))
+ stream_put_in_addr(s, &zero_remote_vtep_ip);
+ else
+ stream_put_in_addr(s, &remote_vtep_ip);
+
+ /* TX flags - MAC sticky status and/or gateway mac */
+ /* Also TX the sequence number of the best route. */
+ if (add) {
+ stream_putc(s, flags);
+ stream_putl(s, seq);
+ stream_put(s, esi, sizeof(esi_t));
+ }
+
+ stream_putw_at(s, 0, stream_get_endp(s));
+
+ if (bgp_debug_zebra(NULL))
+ zlog_debug(
+ "Tx %s MACIP, VNI %u MAC %pEA IP %pIA flags 0x%x seq %u remote VTEP %pI4",
+ add ? "ADD" : "DEL", vpn->vni,
+ &p->prefix.macip_addr.mac, &p->prefix.macip_addr.ip,
+ flags, seq, &remote_vtep_ip);
+
+ frrtrace(5, frr_bgp, evpn_mac_ip_zsend, add, vpn, p, remote_vtep_ip,
+ esi);
+
+ return zclient_send_message(zclient);
+}
+
+/*
+ * Add (update) or delete remote VTEP from zebra.
+ */
+static int bgp_zebra_send_remote_vtep(struct bgp *bgp, struct bgpevpn *vpn,
+ const struct prefix_evpn *p,
+ int flood_control, int add)
+{
+ struct stream *s;
+
+ /* Check socket. */
+ if (!zclient || zclient->sock < 0)
+ return 0;
+
+ /* Don't try to register if Zebra doesn't know of this instance. */
+ if (!IS_BGP_INST_KNOWN_TO_ZEBRA(bgp)) {
+ if (BGP_DEBUG(zebra, ZEBRA))
+ zlog_debug(
+ "%s: No zebra instance to talk to, not installing remote vtep",
+ __func__);
+ return 0;
+ }
+
+ s = zclient->obuf;
+ stream_reset(s);
+
+ zclient_create_header(
+ s, add ? ZEBRA_REMOTE_VTEP_ADD : ZEBRA_REMOTE_VTEP_DEL,
+ bgp->vrf_id);
+ stream_putl(s, vpn->vni);
+ if (is_evpn_prefix_ipaddr_v4(p))
+ stream_put_in_addr(s, &p->prefix.imet_addr.ip.ipaddr_v4);
+ else if (is_evpn_prefix_ipaddr_v6(p)) {
+ flog_err(
+ EC_BGP_VTEP_INVALID,
+ "Bad remote IP when trying to %s remote VTEP for VNI %u",
+ add ? "ADD" : "DEL", vpn->vni);
+ return -1;
+ }
+ stream_putl(s, flood_control);
+
+ stream_putw_at(s, 0, stream_get_endp(s));
+
+ if (bgp_debug_zebra(NULL))
+ zlog_debug("Tx %s Remote VTEP, VNI %u remote VTEP %pI4",
+ add ? "ADD" : "DEL", vpn->vni,
+ &p->prefix.imet_addr.ip.ipaddr_v4);
+
+ frrtrace(3, frr_bgp, evpn_bum_vtep_zsend, add, vpn, p);
+
+ return zclient_send_message(zclient);
+}
+
+/*
+ * Build extended communities for EVPN prefix route.
+ */
+static void build_evpn_type5_route_extcomm(struct bgp *bgp_vrf,
+ struct attr *attr)
+{
+ struct ecommunity ecom_encap;
+ struct ecommunity_val eval;
+ struct ecommunity_val eval_rmac;
+ bgp_encap_types tnl_type;
+ struct listnode *node, *nnode;
+ struct ecommunity *ecom;
+ struct ecommunity *old_ecom;
+ struct list *vrf_export_rtl = NULL;
+
+ /* Encap */
+ tnl_type = BGP_ENCAP_TYPE_VXLAN;
+ memset(&ecom_encap, 0, sizeof(ecom_encap));
+ encode_encap_extcomm(tnl_type, &eval);
+ ecom_encap.size = 1;
+ ecom_encap.unit_size = ECOMMUNITY_SIZE;
+ ecom_encap.val = (uint8_t *)eval.val;
+
+ /* Add Encap */
+ if (bgp_attr_get_ecommunity(attr)) {
+ old_ecom = bgp_attr_get_ecommunity(attr);
+ ecom = ecommunity_merge(ecommunity_dup(old_ecom), &ecom_encap);
+ if (!old_ecom->refcnt)
+ ecommunity_free(&old_ecom);
+ } else
+ ecom = ecommunity_dup(&ecom_encap);
+ bgp_attr_set_ecommunity(attr, ecom);
+ attr->encap_tunneltype = tnl_type;
+
+ /* Add the export RTs for L3VNI/VRF */
+ vrf_export_rtl = bgp_vrf->vrf_export_rtl;
+ for (ALL_LIST_ELEMENTS(vrf_export_rtl, node, nnode, ecom))
+ bgp_attr_set_ecommunity(
+ attr,
+ ecommunity_merge(bgp_attr_get_ecommunity(attr), ecom));
+
+ /* add the router mac extended community */
+ if (!is_zero_mac(&attr->rmac)) {
+ encode_rmac_extcomm(&eval_rmac, &attr->rmac);
+ ecommunity_add_val(bgp_attr_get_ecommunity(attr), &eval_rmac,
+ true, true);
+ }
+}
+
+/*
+ * Build extended communities for EVPN route.
+ * This function is applicable for type-2 and type-3 routes. The layer-2 RT
+ * and ENCAP extended communities are applicable for all routes.
+ * The default gateway extended community and MAC mobility (sticky) extended
+ * community are added as needed based on passed settings - only for type-2
+ * routes. Likewise, the layer-3 RT and Router MAC extended communities are
+ * added, if present, based on passed settings - only for non-link-local
+ * type-2 routes.
+ */
+static void build_evpn_route_extcomm(struct bgpevpn *vpn, struct attr *attr,
+ int add_l3_ecomm)
+{
+ struct ecommunity ecom_encap;
+ struct ecommunity ecom_sticky;
+ struct ecommunity ecom_default_gw;
+ struct ecommunity ecom_na;
+ struct ecommunity_val eval;
+ struct ecommunity_val eval_sticky;
+ struct ecommunity_val eval_default_gw;
+ struct ecommunity_val eval_rmac;
+ struct ecommunity_val eval_na;
+ bool proxy;
+
+ bgp_encap_types tnl_type;
+ struct listnode *node, *nnode;
+ struct ecommunity *ecom;
+ uint32_t seqnum;
+ struct list *vrf_export_rtl = NULL;
+
+ /* Encap */
+ tnl_type = BGP_ENCAP_TYPE_VXLAN;
+ memset(&ecom_encap, 0, sizeof(ecom_encap));
+ encode_encap_extcomm(tnl_type, &eval);
+ ecom_encap.size = 1;
+ ecom_encap.unit_size = ECOMMUNITY_SIZE;
+ ecom_encap.val = (uint8_t *)eval.val;
+
+ /* Add Encap */
+ bgp_attr_set_ecommunity(attr, ecommunity_dup(&ecom_encap));
+ attr->encap_tunneltype = tnl_type;
+
+ /* Add the export RTs for L2VNI */
+ for (ALL_LIST_ELEMENTS(vpn->export_rtl, node, nnode, ecom))
+ bgp_attr_set_ecommunity(
+ attr,
+ ecommunity_merge(bgp_attr_get_ecommunity(attr), ecom));
+
+ /* Add the export RTs for L3VNI if told to - caller determines
+ * when this should be done.
+ */
+ if (add_l3_ecomm) {
+ vrf_export_rtl = bgpevpn_get_vrf_export_rtl(vpn);
+ if (vrf_export_rtl && !list_isempty(vrf_export_rtl)) {
+ for (ALL_LIST_ELEMENTS(vrf_export_rtl, node, nnode,
+ ecom))
+ bgp_attr_set_ecommunity(
+ attr,
+ ecommunity_merge(
+ bgp_attr_get_ecommunity(attr),
+ ecom));
+ }
+ }
+
+ /* Add MAC mobility (sticky) if needed. */
+ if (attr->sticky) {
+ seqnum = 0;
+ memset(&ecom_sticky, 0, sizeof(ecom_sticky));
+ encode_mac_mobility_extcomm(1, seqnum, &eval_sticky);
+ ecom_sticky.size = 1;
+ ecom_sticky.unit_size = ECOMMUNITY_SIZE;
+ ecom_sticky.val = (uint8_t *)eval_sticky.val;
+ bgp_attr_set_ecommunity(
+ attr, ecommunity_merge(bgp_attr_get_ecommunity(attr),
+ &ecom_sticky));
+ }
+
+ /* Add RMAC, if told to. */
+ if (add_l3_ecomm) {
+ encode_rmac_extcomm(&eval_rmac, &attr->rmac);
+ ecommunity_add_val(bgp_attr_get_ecommunity(attr), &eval_rmac,
+ true, true);
+ }
+
+ /* Add default gateway, if needed. */
+ if (attr->default_gw) {
+ memset(&ecom_default_gw, 0, sizeof(ecom_default_gw));
+ encode_default_gw_extcomm(&eval_default_gw);
+ ecom_default_gw.size = 1;
+ ecom_default_gw.unit_size = ECOMMUNITY_SIZE;
+ ecom_default_gw.val = (uint8_t *)eval_default_gw.val;
+ bgp_attr_set_ecommunity(
+ attr, ecommunity_merge(bgp_attr_get_ecommunity(attr),
+ &ecom_default_gw));
+ }
+
+ proxy = !!(attr->es_flags & ATTR_ES_PROXY_ADVERT);
+ if (attr->router_flag || proxy) {
+ memset(&ecom_na, 0, sizeof(ecom_na));
+ encode_na_flag_extcomm(&eval_na, attr->router_flag, proxy);
+ ecom_na.size = 1;
+ ecom_na.unit_size = ECOMMUNITY_SIZE;
+ ecom_na.val = (uint8_t *)eval_na.val;
+ bgp_attr_set_ecommunity(
+ attr, ecommunity_merge(bgp_attr_get_ecommunity(attr),
+ &ecom_na));
+ }
+}
+
+/*
+ * Add MAC mobility extended community to attribute.
+ */
+static void add_mac_mobility_to_attr(uint32_t seq_num, struct attr *attr)
+{
+ struct ecommunity ecom_tmp;
+ struct ecommunity_val eval;
+ uint8_t *ecom_val_ptr;
+ uint32_t i;
+ uint8_t *pnt;
+ int type = 0;
+ int sub_type = 0;
+ struct ecommunity *ecomm = bgp_attr_get_ecommunity(attr);
+
+ /* Build MM */
+ encode_mac_mobility_extcomm(0, seq_num, &eval);
+
+ /* Find current MM ecommunity */
+ ecom_val_ptr = NULL;
+
+ if (ecomm) {
+ for (i = 0; i < ecomm->size; i++) {
+ pnt = ecomm->val + (i * ecomm->unit_size);
+ type = *pnt++;
+ sub_type = *pnt++;
+
+ if (type == ECOMMUNITY_ENCODE_EVPN
+ && sub_type
+ == ECOMMUNITY_EVPN_SUBTYPE_MACMOBILITY) {
+ ecom_val_ptr =
+ (ecomm->val + (i * ecomm->unit_size));
+ break;
+ }
+ }
+ }
+
+ /* Update the existing MM ecommunity */
+ if (ecom_val_ptr) {
+ memcpy(ecom_val_ptr, eval.val, sizeof(char) * ecomm->unit_size);
+ }
+ /* Add MM to existing */
+ else {
+ memset(&ecom_tmp, 0, sizeof(ecom_tmp));
+ ecom_tmp.size = 1;
+ ecom_tmp.unit_size = ECOMMUNITY_SIZE;
+ ecom_tmp.val = (uint8_t *)eval.val;
+
+ if (ecomm)
+ bgp_attr_set_ecommunity(
+ attr, ecommunity_merge(ecomm, &ecom_tmp));
+ else
+ bgp_attr_set_ecommunity(attr,
+ ecommunity_dup(&ecom_tmp));
+ }
+}
+
+/* Install EVPN route into zebra. */
+static int evpn_zebra_install(struct bgp *bgp, struct bgpevpn *vpn,
+ const struct prefix_evpn *p,
+ struct bgp_path_info *pi)
+{
+ int ret;
+ uint8_t flags;
+ int flood_control;
+ uint32_t seq;
+
+ if (p->prefix.route_type == BGP_EVPN_MAC_IP_ROUTE) {
+ flags = 0;
+
+ if (pi->sub_type == BGP_ROUTE_IMPORTED) {
+ if (pi->attr->sticky)
+ SET_FLAG(flags, ZEBRA_MACIP_TYPE_STICKY);
+ if (pi->attr->default_gw)
+ SET_FLAG(flags, ZEBRA_MACIP_TYPE_GW);
+ if (is_evpn_prefix_ipaddr_v6(p) &&
+ pi->attr->router_flag)
+ SET_FLAG(flags, ZEBRA_MACIP_TYPE_ROUTER_FLAG);
+
+ seq = mac_mobility_seqnum(pi->attr);
+ /* if local ES notify zebra that this is a sync path */
+ if (bgp_evpn_attr_is_local_es(pi->attr)) {
+ SET_FLAG(flags, ZEBRA_MACIP_TYPE_SYNC_PATH);
+ if (bgp_evpn_attr_is_proxy(pi->attr))
+ SET_FLAG(flags,
+ ZEBRA_MACIP_TYPE_PROXY_ADVERT);
+ }
+ } else {
+ if (!bgp_evpn_attr_is_sync(pi->attr))
+ return 0;
+
+ /* if a local path is being turned around and sent
+ * to zebra it is because it is a sync path on
+ * a local ES
+ */
+ SET_FLAG(flags, ZEBRA_MACIP_TYPE_SYNC_PATH);
+ /* supply the highest peer seq number to zebra
+ * for MM seq syncing
+ */
+ seq = bgp_evpn_attr_get_sync_seq(pi->attr);
+ /* if any of the paths from the peer have the ROUTER
+ * flag set install the local entry as a router entry
+ */
+ if (is_evpn_prefix_ipaddr_v6(p) &&
+ (pi->attr->es_flags &
+ ATTR_ES_PEER_ROUTER))
+ SET_FLAG(flags,
+ ZEBRA_MACIP_TYPE_ROUTER_FLAG);
+
+ if (!(pi->attr->es_flags & ATTR_ES_PEER_ACTIVE))
+ SET_FLAG(flags,
+ ZEBRA_MACIP_TYPE_PROXY_ADVERT);
+ }
+
+ ret = bgp_zebra_send_remote_macip(
+ bgp, vpn, p, pi->attr->nexthop, 1, flags,
+ seq, bgp_evpn_attr_get_esi(pi->attr));
+ } else if (p->prefix.route_type == BGP_EVPN_AD_ROUTE) {
+ ret = bgp_evpn_remote_es_evi_add(bgp, vpn, p);
+ } else {
+ switch (bgp_attr_get_pmsi_tnl_type(pi->attr)) {
+ case PMSI_TNLTYPE_INGR_REPL:
+ flood_control = VXLAN_FLOOD_HEAD_END_REPL;
+ break;
+
+ case PMSI_TNLTYPE_PIM_SM:
+ flood_control = VXLAN_FLOOD_PIM_SM;
+ break;
+
+ default:
+ flood_control = VXLAN_FLOOD_DISABLED;
+ break;
+ }
+ ret = bgp_zebra_send_remote_vtep(bgp, vpn, p, flood_control, 1);
+ }
+
+ return ret;
+}
+
+/* Uninstall EVPN route from zebra. */
+static int evpn_zebra_uninstall(struct bgp *bgp, struct bgpevpn *vpn,
+ const struct prefix_evpn *p,
+ struct in_addr remote_vtep_ip)
+{
+ int ret;
+
+ if (p->prefix.route_type == BGP_EVPN_MAC_IP_ROUTE)
+ ret = bgp_zebra_send_remote_macip(bgp, vpn, p, remote_vtep_ip,
+ 0, 0, 0, NULL);
+ else if (p->prefix.route_type == BGP_EVPN_AD_ROUTE)
+ ret = bgp_evpn_remote_es_evi_del(bgp, vpn, p);
+ else
+ ret = bgp_zebra_send_remote_vtep(bgp, vpn, p,
+ VXLAN_FLOOD_DISABLED, 0);
+
+ return ret;
+}
+
+/*
+ * Due to MAC mobility, the prior "local" best route has been supplanted
+ * by a "remote" best route. The prior route has to be deleted and withdrawn
+ * from peers.
+ */
+static void evpn_delete_old_local_route(struct bgp *bgp, struct bgpevpn *vpn,
+ struct bgp_dest *dest,
+ struct bgp_path_info *old_local,
+ struct bgp_path_info *new_select)
+{
+ struct bgp_dest *global_dest;
+ struct bgp_path_info *pi;
+ afi_t afi = AFI_L2VPN;
+ safi_t safi = SAFI_EVPN;
+
+ if (BGP_DEBUG(evpn_mh, EVPN_MH_RT)) {
+ char esi_buf[ESI_STR_LEN];
+ char esi_buf2[ESI_STR_LEN];
+ struct prefix_evpn *evp =
+ (struct prefix_evpn *)bgp_dest_get_prefix(dest);
+
+ zlog_debug("local path deleted %pFX es %s; new-path-es %s", evp,
+ esi_to_str(&old_local->attr->esi, esi_buf,
+ sizeof(esi_buf)),
+ new_select ? esi_to_str(&new_select->attr->esi,
+ esi_buf2, sizeof(esi_buf2))
+ : "");
+ }
+
+ /* Locate route node in the global EVPN routing table. Note that
+ * this table is a 2-level tree (RD-level + Prefix-level) similar to
+ * L3VPN routes.
+ */
+ global_dest = bgp_global_evpn_node_lookup(bgp->rib[afi][safi], afi, safi,
+ (const struct prefix_evpn *)bgp_dest_get_prefix(dest),
+ &vpn->prd);
+ if (global_dest) {
+ /* Delete route entry in the global EVPN table. */
+ delete_evpn_route_entry(bgp, afi, safi, global_dest, &pi);
+
+ /* Schedule for processing - withdraws to peers happen from
+ * this table.
+ */
+ if (pi)
+ bgp_process(bgp, global_dest, afi, safi);
+ bgp_dest_unlock_node(global_dest);
+ }
+
+ /* Delete route entry in the VNI route table, caller to remove. */
+ bgp_path_info_delete(dest, old_local);
+}
+
+/*
+ * Calculate the best path for an EVPN route. Install/update best path in zebra,
+ * if appropriate.
+ * Note: vpn is NULL for local EAD-ES routes.
+ */
+int evpn_route_select_install(struct bgp *bgp, struct bgpevpn *vpn,
+ struct bgp_dest *dest)
+{
+ struct bgp_path_info *old_select, *new_select;
+ struct bgp_path_info_pair old_and_new;
+ afi_t afi = AFI_L2VPN;
+ safi_t safi = SAFI_EVPN;
+ int ret = 0;
+
+ /* Compute the best path. */
+ 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 the best path hasn't changed - see if there is still something to
+ * update to zebra RIB.
+ * Remote routes and SYNC route (i.e. local routes with
+ * SYNCED_FROM_PEER flag) need to updated to zebra on any attr
+ * change.
+ */
+ if (old_select && old_select == new_select
+ && old_select->type == ZEBRA_ROUTE_BGP
+ && (old_select->sub_type == BGP_ROUTE_IMPORTED ||
+ bgp_evpn_attr_is_sync(old_select->attr))
+ && !CHECK_FLAG(dest->flags, BGP_NODE_USER_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))
+ ret = evpn_zebra_install(
+ bgp, vpn,
+ (const struct prefix_evpn *)bgp_dest_get_prefix(
+ dest),
+ 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);
+ return ret;
+ }
+
+ /* If the user did a "clear" this flag will be set */
+ UNSET_FLAG(dest->flags, BGP_NODE_USER_CLEAR);
+
+ /* bestpath has changed; update relevant fields and install or uninstall
+ * into the zebra RIB.
+ */
+ if (old_select || new_select)
+ bgp_bump_version(dest);
+
+ if (old_select)
+ bgp_path_info_unset_flag(dest, old_select, BGP_PATH_SELECTED);
+ if (new_select) {
+ 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);
+ }
+
+ /* a local entry with the SYNC flag also results in a MAC-IP update
+ * to zebra
+ */
+ if (new_select && new_select->type == ZEBRA_ROUTE_BGP
+ && (new_select->sub_type == BGP_ROUTE_IMPORTED ||
+ bgp_evpn_attr_is_sync(new_select->attr))) {
+ ret = evpn_zebra_install(
+ bgp, vpn,
+ (struct prefix_evpn *)bgp_dest_get_prefix(dest),
+ new_select);
+
+ /* If an old best existed and it was a "local" route, the only
+ * reason
+ * it would be supplanted is due to MAC mobility procedures. So,
+ * we
+ * need to do an implicit delete and withdraw that route from
+ * peers.
+ */
+ if (new_select->sub_type == BGP_ROUTE_IMPORTED &&
+ old_select && old_select->peer == bgp->peer_self
+ && old_select->type == ZEBRA_ROUTE_BGP
+ && old_select->sub_type == BGP_ROUTE_STATIC
+ && vpn)
+ evpn_delete_old_local_route(bgp, vpn, dest,
+ old_select, new_select);
+ } else {
+ if (old_select && old_select->type == ZEBRA_ROUTE_BGP
+ && old_select->sub_type == BGP_ROUTE_IMPORTED)
+ ret = evpn_zebra_uninstall(
+ bgp, vpn,
+ (const struct prefix_evpn *)bgp_dest_get_prefix(
+ dest),
+ old_select->attr->nexthop);
+ }
+
+ /* Clear any route change flags. */
+ bgp_zebra_clear_route_change_flags(dest);
+
+ /* 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 ret;
+}
+
+static struct bgp_path_info *bgp_evpn_route_get_local_path(
+ struct bgp *bgp, struct bgp_dest *dest)
+{
+ struct bgp_path_info *tmp_pi;
+ struct bgp_path_info *local_pi = NULL;
+
+ for (tmp_pi = bgp_dest_get_bgp_path_info(dest); tmp_pi;
+ tmp_pi = tmp_pi->next) {
+ if (bgp_evpn_is_path_local(bgp, tmp_pi)) {
+ local_pi = tmp_pi;
+ break;
+ }
+ }
+
+ return local_pi;
+}
+
+static int update_evpn_type5_route_entry(struct bgp *bgp_evpn,
+ struct bgp *bgp_vrf, afi_t afi,
+ safi_t safi, struct bgp_dest *dest,
+ struct attr *attr, int *route_changed)
+{
+ struct attr *attr_new = NULL;
+ struct bgp_path_info *pi = NULL;
+ mpls_label_t label = MPLS_INVALID_LABEL;
+ struct bgp_path_info *local_pi = NULL;
+ struct bgp_path_info *tmp_pi = NULL;
+
+ *route_changed = 0;
+ /* locate the local route entry if any */
+ for (tmp_pi = bgp_dest_get_bgp_path_info(dest); tmp_pi;
+ tmp_pi = tmp_pi->next) {
+ if (tmp_pi->peer == bgp_evpn->peer_self
+ && tmp_pi->type == ZEBRA_ROUTE_BGP
+ && tmp_pi->sub_type == BGP_ROUTE_STATIC)
+ local_pi = tmp_pi;
+ }
+
+ /*
+ * create a new route entry if one doesn't exist.
+ * Otherwise see if route attr has changed
+ */
+ if (!local_pi) {
+
+ /* route has changed as this is the first entry */
+ *route_changed = 1;
+
+ /* Add (or update) attribute to hash. */
+ attr_new = bgp_attr_intern(attr);
+
+ /* create the route info from attribute */
+ pi = info_make(ZEBRA_ROUTE_BGP, BGP_ROUTE_STATIC, 0,
+ bgp_evpn->peer_self, attr_new, dest);
+ SET_FLAG(pi->flags, BGP_PATH_VALID);
+
+ /* Type-5 routes advertise the L3-VNI */
+ bgp_path_info_extra_get(pi);
+ vni2label(bgp_vrf->l3vni, &label);
+ memcpy(&pi->extra->label, &label, sizeof(label));
+ pi->extra->num_labels = 1;
+
+ /* add the route entry to route node*/
+ bgp_path_info_add(dest, pi);
+ } else {
+
+ tmp_pi = local_pi;
+ if (!attrhash_cmp(tmp_pi->attr, attr)) {
+
+ /* attribute changed */
+ *route_changed = 1;
+
+ /* The attribute has changed. */
+ /* Add (or update) attribute to hash. */
+ attr_new = bgp_attr_intern(attr);
+ bgp_path_info_set_flag(dest, tmp_pi,
+ BGP_PATH_ATTR_CHANGED);
+
+ /* Restore route, if needed. */
+ if (CHECK_FLAG(tmp_pi->flags, BGP_PATH_REMOVED))
+ bgp_path_info_restore(dest, tmp_pi);
+
+ /* Unintern existing, set to new. */
+ bgp_attr_unintern(&tmp_pi->attr);
+ tmp_pi->attr = attr_new;
+ tmp_pi->uptime = monotime(NULL);
+ }
+ }
+ return 0;
+}
+
+/* update evpn type-5 route entry */
+static int update_evpn_type5_route(struct bgp *bgp_vrf, struct prefix_evpn *evp,
+ struct attr *src_attr, afi_t src_afi,
+ safi_t src_safi)
+{
+ afi_t afi = AFI_L2VPN;
+ safi_t safi = SAFI_EVPN;
+ struct attr attr;
+ struct bgp_dest *dest = NULL;
+ struct bgp *bgp_evpn = NULL;
+ int route_changed = 0;
+
+ bgp_evpn = bgp_get_evpn();
+ if (!bgp_evpn)
+ return 0;
+
+ /* Build path attribute for this route - use the source attr, if
+ * present, else treat as locally originated.
+ */
+ if (src_attr)
+ attr = *src_attr;
+ else {
+ memset(&attr, 0, sizeof(attr));
+ bgp_attr_default_set(&attr, bgp_vrf, BGP_ORIGIN_IGP);
+ }
+
+ /* Advertise Primary IP (PIP) is enabled, send individual
+ * IP (default instance router-id) as nexthop.
+ * PIP is disabled or vrr interface is not present
+ * use anycast-IP as nexthop and anycast RMAC.
+ */
+ if (!bgp_vrf->evpn_info->advertise_pip ||
+ (!bgp_vrf->evpn_info->is_anycast_mac)) {
+ attr.nexthop = bgp_vrf->originator_ip;
+ attr.mp_nexthop_global_in = bgp_vrf->originator_ip;
+ memcpy(&attr.rmac, &bgp_vrf->rmac, ETH_ALEN);
+ } else {
+ /* copy sys rmac */
+ memcpy(&attr.rmac, &bgp_vrf->evpn_info->pip_rmac, ETH_ALEN);
+ if (bgp_vrf->evpn_info->pip_ip.s_addr != INADDR_ANY) {
+ attr.nexthop = bgp_vrf->evpn_info->pip_ip;
+ attr.mp_nexthop_global_in = bgp_vrf->evpn_info->pip_ip;
+ } else if (bgp_vrf->evpn_info->pip_ip.s_addr == INADDR_ANY)
+ if (bgp_debug_zebra(NULL))
+ zlog_debug(
+ "VRF %s evp %pFX advertise-pip primary ip is not configured",
+ vrf_id_to_name(bgp_vrf->vrf_id), evp);
+ }
+
+ if (bgp_debug_zebra(NULL))
+ zlog_debug(
+ "VRF %s type-5 route evp %pFX RMAC %pEA nexthop %pI4",
+ vrf_id_to_name(bgp_vrf->vrf_id), evp, &attr.rmac,
+ &attr.nexthop);
+
+ attr.mp_nexthop_len = BGP_ATTR_NHLEN_IPV4;
+
+ if (src_afi == AFI_IP6 &&
+ CHECK_FLAG(bgp_vrf->af_flags[AFI_L2VPN][SAFI_EVPN],
+ BGP_L2VPN_EVPN_ADV_IPV6_UNICAST_GW_IP)) {
+ if (src_attr &&
+ !IN6_IS_ADDR_UNSPECIFIED(&src_attr->mp_nexthop_global)) {
+ attr.evpn_overlay.type = OVERLAY_INDEX_GATEWAY_IP;
+ SET_IPADDR_V6(&attr.evpn_overlay.gw_ip);
+ memcpy(&attr.evpn_overlay.gw_ip.ipaddr_v6,
+ &src_attr->mp_nexthop_global,
+ sizeof(struct in6_addr));
+ }
+ } else if (src_afi == AFI_IP &&
+ CHECK_FLAG(bgp_vrf->af_flags[AFI_L2VPN][SAFI_EVPN],
+ BGP_L2VPN_EVPN_ADV_IPV4_UNICAST_GW_IP)) {
+ if (src_attr && src_attr->nexthop.s_addr != 0) {
+ attr.evpn_overlay.type = OVERLAY_INDEX_GATEWAY_IP;
+ SET_IPADDR_V4(&attr.evpn_overlay.gw_ip);
+ memcpy(&attr.evpn_overlay.gw_ip.ipaddr_v4,
+ &src_attr->nexthop, sizeof(struct in_addr));
+ }
+ }
+
+ /* Setup RT and encap extended community */
+ build_evpn_type5_route_extcomm(bgp_vrf, &attr);
+
+ /* get the route node in global table */
+ dest = bgp_global_evpn_node_get(bgp_evpn->rib[afi][safi], afi, safi,
+ (const struct prefix_evpn *)evp,
+ &bgp_vrf->vrf_prd);
+ assert(dest);
+
+ /* create or update the route entry within the route node */
+ update_evpn_type5_route_entry(bgp_evpn, bgp_vrf, afi, safi, dest, &attr,
+ &route_changed);
+
+ /* schedule for processing and unlock node */
+ if (route_changed) {
+ bgp_process(bgp_evpn, dest, afi, safi);
+ bgp_dest_unlock_node(dest);
+ }
+
+ /* uninten temporary */
+ if (!src_attr)
+ aspath_unintern(&attr.aspath);
+ return 0;
+}
+
+static void bgp_evpn_get_sync_info(struct bgp *bgp, esi_t *esi,
+ struct bgp_dest *dest, uint32_t loc_seq,
+ uint32_t *max_sync_seq, bool *active_on_peer,
+ bool *peer_router, bool *proxy_from_peer)
+{
+ struct bgp_path_info *tmp_pi;
+ struct bgp_path_info *second_best_path = NULL;
+ uint32_t tmp_mm_seq = 0;
+ esi_t *tmp_esi;
+ int paths_eq;
+
+ /* find the best non-local path. a local path can only be present
+ * as best path
+ */
+ for (tmp_pi = bgp_dest_get_bgp_path_info(dest); tmp_pi;
+ tmp_pi = tmp_pi->next) {
+ if (tmp_pi->sub_type != BGP_ROUTE_IMPORTED ||
+ !CHECK_FLAG(tmp_pi->flags, BGP_PATH_VALID))
+ continue;
+
+ if (bgp_evpn_path_info_cmp(bgp, tmp_pi,
+ second_best_path, &paths_eq))
+ second_best_path = tmp_pi;
+ }
+
+ if (!second_best_path)
+ return;
+
+ tmp_esi = bgp_evpn_attr_get_esi(second_best_path->attr);
+ /* if this has the same ES desination as the local path
+ * it is a sync path
+ */
+ if (!memcmp(esi, tmp_esi, sizeof(esi_t))) {
+ tmp_mm_seq = mac_mobility_seqnum(second_best_path->attr);
+ if (tmp_mm_seq < loc_seq)
+ return;
+
+ /* we have a non-proxy path from the ES peer. */
+ if (second_best_path->attr->es_flags &
+ ATTR_ES_PROXY_ADVERT) {
+ *proxy_from_peer = true;
+ } else {
+ *active_on_peer = true;
+ }
+
+ if (second_best_path->attr->router_flag)
+ *peer_router = true;
+
+ /* we use both proxy and non-proxy imports to
+ * determine the max sync sequence
+ */
+ if (tmp_mm_seq > *max_sync_seq)
+ *max_sync_seq = tmp_mm_seq;
+ }
+}
+
+/* Bubble up sync-info from all paths (non-best) to the local-path.
+ * This is need for MM sequence number syncing and proxy advertisement.
+ * Note: The local path can only exist as a best path in the
+ * VPN route table. It will take precedence over all sync paths.
+ */
+static void update_evpn_route_entry_sync_info(struct bgp *bgp,
+ struct bgp_dest *dest,
+ struct attr *attr,
+ uint32_t loc_seq, bool setup_sync)
+{
+ esi_t *esi;
+ struct prefix_evpn *evp =
+ (struct prefix_evpn *)bgp_dest_get_prefix(dest);
+
+ if (evp->prefix.route_type != BGP_EVPN_MAC_IP_ROUTE)
+ return;
+
+ esi = bgp_evpn_attr_get_esi(attr);
+ if (bgp_evpn_is_esi_valid(esi)) {
+ if (setup_sync) {
+ uint32_t max_sync_seq = 0;
+ bool active_on_peer = false;
+ bool peer_router = false;
+ bool proxy_from_peer = false;
+
+ bgp_evpn_get_sync_info(bgp, esi, dest, loc_seq,
+ &max_sync_seq, &active_on_peer,
+ &peer_router, &proxy_from_peer);
+ attr->mm_sync_seqnum = max_sync_seq;
+ if (active_on_peer)
+ attr->es_flags |= ATTR_ES_PEER_ACTIVE;
+ else
+ attr->es_flags &= ~ATTR_ES_PEER_ACTIVE;
+ if (proxy_from_peer)
+ attr->es_flags |= ATTR_ES_PEER_PROXY;
+ else
+ attr->es_flags &= ~ATTR_ES_PEER_PROXY;
+ if (peer_router)
+ attr->es_flags |= ATTR_ES_PEER_ROUTER;
+ else
+ attr->es_flags &= ~ATTR_ES_PEER_ROUTER;
+
+ if (BGP_DEBUG(evpn_mh, EVPN_MH_RT)) {
+ char esi_buf[ESI_STR_LEN];
+
+ zlog_debug(
+ "setup sync info for %pFX es %s max_seq %d %s%s%s",
+ evp,
+ esi_to_str(esi, esi_buf,
+ sizeof(esi_buf)),
+ max_sync_seq,
+ (attr->es_flags & ATTR_ES_PEER_ACTIVE)
+ ? "peer-active "
+ : "",
+ (attr->es_flags & ATTR_ES_PEER_PROXY)
+ ? "peer-proxy "
+ : "",
+ (attr->es_flags & ATTR_ES_PEER_ROUTER)
+ ? "peer-router "
+ : "");
+ }
+ }
+ } else {
+ attr->mm_sync_seqnum = 0;
+ attr->es_flags &= ~ATTR_ES_PEER_ACTIVE;
+ attr->es_flags &= ~ATTR_ES_PEER_PROXY;
+ }
+}
+
+/*
+ * Create or update EVPN route entry. This could be in the VNI route table
+ * or the global route table.
+ */
+static int update_evpn_route_entry(struct bgp *bgp, struct bgpevpn *vpn,
+ afi_t afi, safi_t safi,
+ struct bgp_dest *dest, struct attr *attr,
+ int add, struct bgp_path_info **pi,
+ uint8_t flags, uint32_t seq, bool vpn_rt,
+ bool *old_is_sync)
+{
+ struct bgp_path_info *tmp_pi;
+ struct bgp_path_info *local_pi;
+ struct attr *attr_new;
+ mpls_label_t label[BGP_MAX_LABELS];
+ uint32_t num_labels = 1;
+ int route_change = 1;
+ uint8_t sticky = 0;
+ const struct prefix_evpn *evp;
+
+ *pi = NULL;
+ evp = (const struct prefix_evpn *)bgp_dest_get_prefix(dest);
+ memset(&label, 0, sizeof(label));
+
+ /* See if this is an update of an existing route, or a new add. */
+ local_pi = bgp_evpn_route_get_local_path(bgp, dest);
+
+ /* If route doesn't exist already, create a new one, if told to.
+ * Otherwise act based on whether the attributes of the route have
+ * changed or not.
+ */
+ if (!local_pi && !add)
+ return 0;
+
+ if (old_is_sync && local_pi)
+ *old_is_sync = bgp_evpn_attr_is_sync(local_pi->attr);
+
+ /* if a local path is being added with a non-zero esi look
+ * for SYNC paths from ES peers and bubble up the sync-info
+ */
+ update_evpn_route_entry_sync_info(bgp, dest, attr, seq, vpn_rt);
+
+ /* For non-GW MACs, update MAC mobility seq number, if needed. */
+ if (seq && !CHECK_FLAG(flags, ZEBRA_MACIP_TYPE_GW))
+ add_mac_mobility_to_attr(seq, attr);
+
+ if (!local_pi) {
+ /* Add (or update) attribute to hash. */
+ attr_new = bgp_attr_intern(attr);
+
+ /* Extract MAC mobility sequence number, if any. */
+ attr_new->mm_seqnum =
+ bgp_attr_mac_mobility_seqnum(attr_new, &sticky);
+ attr_new->sticky = sticky;
+
+ /* Create new route with its attribute. */
+ tmp_pi = info_make(ZEBRA_ROUTE_BGP, BGP_ROUTE_STATIC, 0,
+ bgp->peer_self, attr_new, dest);
+ SET_FLAG(tmp_pi->flags, BGP_PATH_VALID);
+ bgp_path_info_extra_get(tmp_pi);
+
+ /* The VNI goes into the 'label' field of the route */
+ vni2label(vpn->vni, &label[0]);
+
+ /* Type-2 routes may carry a second VNI - the L3-VNI.
+ * Only attach second label if we are advertising two labels for
+ * type-2 routes.
+ */
+ if (evp->prefix.route_type == BGP_EVPN_MAC_IP_ROUTE
+ && CHECK_FLAG(vpn->flags, VNI_FLAG_USE_TWO_LABELS)) {
+ vni_t l3vni;
+
+ l3vni = bgpevpn_get_l3vni(vpn);
+ if (l3vni) {
+ vni2label(l3vni, &label[1]);
+ num_labels++;
+ }
+ }
+
+ memcpy(&tmp_pi->extra->label, label, sizeof(label));
+ tmp_pi->extra->num_labels = num_labels;
+ /* Mark route as self type-2 route */
+ if (flags && CHECK_FLAG(flags, ZEBRA_MACIP_TYPE_SVI_IP))
+ tmp_pi->extra->af_flags = BGP_EVPN_MACIP_TYPE_SVI_IP;
+ bgp_path_info_add(dest, tmp_pi);
+ } else {
+ tmp_pi = local_pi;
+ if (attrhash_cmp(tmp_pi->attr, attr)
+ && !CHECK_FLAG(tmp_pi->flags, BGP_PATH_REMOVED))
+ route_change = 0;
+ else {
+ /*
+ * The attributes have changed, type-2 routes needs to
+ * be advertised with right labels.
+ */
+ vni2label(vpn->vni, &label[0]);
+ if (evp->prefix.route_type == BGP_EVPN_MAC_IP_ROUTE
+ && CHECK_FLAG(vpn->flags,
+ VNI_FLAG_USE_TWO_LABELS)) {
+ vni_t l3vni;
+
+ l3vni = bgpevpn_get_l3vni(vpn);
+ if (l3vni) {
+ vni2label(l3vni, &label[1]);
+ num_labels++;
+ }
+ }
+ memcpy(&tmp_pi->extra->label, label, sizeof(label));
+ tmp_pi->extra->num_labels = num_labels;
+
+ /* The attribute has changed. */
+ /* Add (or update) attribute to hash. */
+ attr_new = bgp_attr_intern(attr);
+ bgp_path_info_set_flag(dest, tmp_pi,
+ BGP_PATH_ATTR_CHANGED);
+
+ /* Extract MAC mobility sequence number, if any. */
+ attr_new->mm_seqnum =
+ bgp_attr_mac_mobility_seqnum(attr_new, &sticky);
+ attr_new->sticky = sticky;
+
+ /* Restore route, if needed. */
+ if (CHECK_FLAG(tmp_pi->flags, BGP_PATH_REMOVED))
+ bgp_path_info_restore(dest, tmp_pi);
+
+ /* Unintern existing, set to new. */
+ bgp_attr_unintern(&tmp_pi->attr);
+ tmp_pi->attr = attr_new;
+ tmp_pi->uptime = monotime(NULL);
+ }
+ }
+
+ /* local MAC-IP routes in the VNI table are linked to
+ * the destination ES
+ */
+ if (route_change && vpn_rt
+ && (evp->prefix.route_type == BGP_EVPN_MAC_IP_ROUTE))
+ bgp_evpn_path_es_link(tmp_pi, vpn->vni,
+ bgp_evpn_attr_get_esi(tmp_pi->attr));
+
+ /* Return back the route entry. */
+ *pi = tmp_pi;
+ return route_change;
+}
+
+static void evpn_zebra_reinstall_best_route(struct bgp *bgp,
+ struct bgpevpn *vpn,
+ struct bgp_dest *dest)
+{
+ struct bgp_path_info *tmp_ri;
+ struct bgp_path_info *curr_select = NULL;
+
+ for (tmp_ri = bgp_dest_get_bgp_path_info(dest); tmp_ri;
+ tmp_ri = tmp_ri->next) {
+ if (CHECK_FLAG(tmp_ri->flags, BGP_PATH_SELECTED)) {
+ curr_select = tmp_ri;
+ break;
+ }
+ }
+
+ if (curr_select && curr_select->type == ZEBRA_ROUTE_BGP
+ && (curr_select->sub_type == BGP_ROUTE_IMPORTED ||
+ bgp_evpn_attr_is_sync(curr_select->attr)))
+ evpn_zebra_install(bgp, vpn,
+ (const struct prefix_evpn *)bgp_dest_get_prefix(dest),
+ curr_select);
+}
+
+/*
+ * If the local route was not selected evict it and tell zebra to re-add
+ * the best remote dest.
+ *
+ * Typically a local path added by zebra is expected to be selected as
+ * best. In which case when a remote path wins as best (later)
+ * evpn_route_select_install itself evicts the older-local-best path.
+ *
+ * However if bgp's add and zebra's add cross paths (race condition) it
+ * is possible that the local path is no longer the "older" best path.
+ * It is a path that was never designated as best and hence requires
+ * additional handling to prevent bgp from injecting and holding on to a
+ * non-best local path.
+ */
+static void evpn_cleanup_local_non_best_route(struct bgp *bgp,
+ struct bgpevpn *vpn,
+ struct bgp_dest *dest,
+ struct bgp_path_info *local_pi)
+{
+ /* local path was not picked as the winner; kick it out */
+ if (bgp_debug_zebra(NULL))
+ zlog_debug("evicting local evpn prefix %pBD as remote won",
+ dest);
+
+ evpn_delete_old_local_route(bgp, vpn, dest, local_pi, NULL);
+ bgp_path_info_reap(dest, local_pi);
+
+ /* tell zebra to re-add the best remote path */
+ evpn_zebra_reinstall_best_route(bgp, vpn, dest);
+}
+
+static inline bool bgp_evpn_route_add_l3_ecomm_ok(struct bgpevpn *vpn,
+ const struct prefix_evpn *p,
+ esi_t *esi)
+{
+ return p->prefix.route_type == BGP_EVPN_MAC_IP_ROUTE
+ && (is_evpn_prefix_ipaddr_v4(p)
+ || (is_evpn_prefix_ipaddr_v6(p)
+ && !IN6_IS_ADDR_LINKLOCAL(
+ &p->prefix.macip_addr.ip.ipaddr_v6)))
+ && CHECK_FLAG(vpn->flags, VNI_FLAG_USE_TWO_LABELS)
+ && bgpevpn_get_l3vni(vpn) && bgp_evpn_es_add_l3_ecomm_ok(esi);
+}
+
+/*
+ * Create or update EVPN route (of type based on prefix) for specified VNI
+ * and schedule for processing.
+ */
+static int update_evpn_route(struct bgp *bgp, struct bgpevpn *vpn,
+ struct prefix_evpn *p, uint8_t flags,
+ uint32_t seq, esi_t *esi)
+{
+ struct bgp_dest *dest;
+ struct attr attr;
+ struct attr *attr_new;
+ int add_l3_ecomm = 0;
+ struct bgp_path_info *pi;
+ afi_t afi = AFI_L2VPN;
+ safi_t safi = SAFI_EVPN;
+ int route_change;
+ bool old_is_sync = false;
+
+ memset(&attr, 0, sizeof(attr));
+
+ /* Build path-attribute for this route. */
+ bgp_attr_default_set(&attr, bgp, BGP_ORIGIN_IGP);
+ attr.nexthop = vpn->originator_ip;
+ attr.mp_nexthop_global_in = vpn->originator_ip;
+ attr.mp_nexthop_len = BGP_ATTR_NHLEN_IPV4;
+ attr.sticky = CHECK_FLAG(flags, ZEBRA_MACIP_TYPE_STICKY) ? 1 : 0;
+ attr.default_gw = CHECK_FLAG(flags, ZEBRA_MACIP_TYPE_GW) ? 1 : 0;
+ attr.router_flag = CHECK_FLAG(flags,
+ ZEBRA_MACIP_TYPE_ROUTER_FLAG) ? 1 : 0;
+ if (CHECK_FLAG(flags, ZEBRA_MACIP_TYPE_PROXY_ADVERT))
+ attr.es_flags |= ATTR_ES_PROXY_ADVERT;
+
+ if (esi && bgp_evpn_is_esi_valid(esi)) {
+ memcpy(&attr.esi, esi, sizeof(esi_t));
+ attr.es_flags |= ATTR_ES_IS_LOCAL;
+ }
+
+ /* PMSI is only needed for type-3 routes */
+ if (p->prefix.route_type == BGP_EVPN_IMET_ROUTE) {
+ attr.flag |= ATTR_FLAG_BIT(BGP_ATTR_PMSI_TUNNEL);
+ bgp_attr_set_pmsi_tnl_type(&attr, PMSI_TNLTYPE_INGR_REPL);
+ }
+
+ /* router mac is only needed for type-2 routes here. */
+ if (p->prefix.route_type == BGP_EVPN_MAC_IP_ROUTE) {
+ uint8_t af_flags = 0;
+
+ if (CHECK_FLAG(flags, ZEBRA_MACIP_TYPE_SVI_IP))
+ SET_FLAG(af_flags, BGP_EVPN_MACIP_TYPE_SVI_IP);
+
+ bgp_evpn_get_rmac_nexthop(vpn, p, &attr, af_flags);
+ }
+
+ if (bgp_debug_zebra(NULL)) {
+ char buf3[ESI_STR_LEN];
+
+ zlog_debug(
+ "VRF %s vni %u type-2 route evp %pFX RMAC %pEA nexthop %pI4 esi %s",
+ vpn->bgp_vrf ? vrf_id_to_name(vpn->bgp_vrf->vrf_id)
+ : " ",
+ vpn->vni, p, &attr.rmac, &attr.mp_nexthop_global_in,
+ esi_to_str(esi, buf3, sizeof(buf3)));
+ }
+
+ vni2label(vpn->vni, &(attr.label));
+
+ /* Include L3 VNI related RTs and RMAC for type-2 routes, if they're
+ * IPv4 or IPv6 global addresses and we're advertising L3VNI with
+ * these routes.
+ */
+ add_l3_ecomm = bgp_evpn_route_add_l3_ecomm_ok(
+ vpn, p, (attr.es_flags & ATTR_ES_IS_LOCAL) ? &attr.esi : NULL);
+
+ /* Set up extended community. */
+ build_evpn_route_extcomm(vpn, &attr, add_l3_ecomm);
+
+ /* First, create (or fetch) route node within the VNI. */
+ /* NOTE: There is no RD here. */
+ dest = bgp_node_get(vpn->route_table, (struct prefix *)p);
+
+ /* Create or update route entry. */
+ route_change = update_evpn_route_entry(bgp, vpn, afi, safi, dest, &attr,
+ 1, &pi, flags, seq,
+ true /* setup_sync */, &old_is_sync);
+ assert(pi);
+ attr_new = pi->attr;
+
+ /* lock ri to prevent freeing in evpn_route_select_install */
+ bgp_path_info_lock(pi);
+
+ /* Perform route selection. Normally, the local route in the
+ * VNI is expected to win and be the best route. However, if
+ * there is a race condition where a host moved from local to
+ * remote and the remote route was received in BGP just prior
+ * to the local MACIP notification from zebra, the remote
+ * route would win, and we should evict the defunct local route
+ * and (re)install the remote route into zebra.
+ */
+ evpn_route_select_install(bgp, vpn, dest);
+ /*
+ * If the new local route was not selected evict it and tell zebra
+ * to re-add the best remote dest. BGP doesn't retain non-best local
+ * routes.
+ */
+ if (CHECK_FLAG(pi->flags, BGP_PATH_REMOVED)) {
+ route_change = 0;
+ } else {
+ if (!CHECK_FLAG(pi->flags, BGP_PATH_SELECTED)) {
+ route_change = 0;
+ evpn_cleanup_local_non_best_route(bgp, vpn, dest, pi);
+ } else {
+ bool new_is_sync;
+
+ /* If the local path already existed and is still the
+ * best path we need to also check if it transitioned
+ * from being a sync path to a non-sync path. If it
+ * it did we need to notify zebra that the sync-path
+ * has been removed.
+ */
+ new_is_sync = bgp_evpn_attr_is_sync(pi->attr);
+ if (!new_is_sync && old_is_sync)
+ evpn_zebra_uninstall(bgp, vpn, p, zero_vtep_ip);
+ }
+ }
+ bgp_path_info_unlock(pi);
+
+ bgp_dest_unlock_node(dest);
+
+ /* If this is a new route or some attribute has changed, export the
+ * route to the global table. The route will be advertised to peers
+ * from there. Note that this table is a 2-level tree (RD-level +
+ * Prefix-level) similar to L3VPN routes.
+ */
+ if (route_change) {
+ struct bgp_path_info *global_pi;
+
+ dest = bgp_global_evpn_node_get(bgp->rib[afi][safi], afi, safi,
+ (const struct prefix_evpn *)p,
+ &vpn->prd);
+ update_evpn_route_entry(bgp, vpn, afi, safi, dest, attr_new, 1,
+ &global_pi, flags, seq,
+ false /* setup_sync */, NULL /* old_is_sync */);
+
+ /* Schedule for processing and unlock node. */
+ bgp_process(bgp, dest, afi, safi);
+ bgp_dest_unlock_node(dest);
+ }
+
+ /* Unintern temporary. */
+ aspath_unintern(&attr.aspath);
+
+ return 0;
+}
+
+/*
+ * Delete EVPN route entry.
+ * The entry can be in ESI/VNI table or the global table.
+ */
+void delete_evpn_route_entry(struct bgp *bgp, afi_t afi, safi_t safi,
+ struct bgp_dest *dest,
+ struct bgp_path_info **pi)
+{
+ struct bgp_path_info *tmp_pi;
+
+ *pi = NULL;
+
+ /* Now, find matching route. */
+ for (tmp_pi = bgp_dest_get_bgp_path_info(dest); tmp_pi;
+ tmp_pi = tmp_pi->next)
+ if (tmp_pi->peer == bgp->peer_self
+ && tmp_pi->type == ZEBRA_ROUTE_BGP
+ && tmp_pi->sub_type == BGP_ROUTE_STATIC)
+ break;
+
+ *pi = tmp_pi;
+
+ /* Mark route for delete. */
+ if (tmp_pi)
+ bgp_path_info_delete(dest, tmp_pi);
+}
+
+/* Delete EVPN type5 route */
+static int delete_evpn_type5_route(struct bgp *bgp_vrf, struct prefix_evpn *evp)
+{
+ afi_t afi = AFI_L2VPN;
+ safi_t safi = SAFI_EVPN;
+ struct bgp_dest *dest = NULL;
+ struct bgp_path_info *pi = NULL;
+ struct bgp *bgp_evpn = NULL; /* evpn bgp instance */
+
+ bgp_evpn = bgp_get_evpn();
+ if (!bgp_evpn)
+ return 0;
+
+ /* locate the global route entry for this type-5 prefix */
+ dest = bgp_global_evpn_node_lookup(bgp_evpn->rib[afi][safi], afi, safi,
+ (const struct prefix_evpn *)evp, &bgp_vrf->vrf_prd);
+ if (!dest)
+ return 0;
+
+ delete_evpn_route_entry(bgp_evpn, afi, safi, dest, &pi);
+ if (pi)
+ bgp_process(bgp_evpn, dest, afi, safi);
+ bgp_dest_unlock_node(dest);
+ return 0;
+}
+
+/*
+ * Delete EVPN route (of type based on prefix) for specified VNI and
+ * schedule for processing.
+ */
+static int delete_evpn_route(struct bgp *bgp, struct bgpevpn *vpn,
+ struct prefix_evpn *p)
+{
+ struct bgp_dest *dest, *global_dest;
+ struct bgp_path_info *pi;
+ afi_t afi = AFI_L2VPN;
+ safi_t safi = SAFI_EVPN;
+
+ /* First, locate the route node within the VNI. If it doesn't exist,
+ * there
+ * is nothing further to do.
+ */
+ /* NOTE: There is no RD here. */
+ dest = bgp_node_lookup(vpn->route_table, (struct prefix *)p);
+ if (!dest)
+ return 0;
+
+ /* Next, locate route node in the global EVPN routing table. Note that
+ * this table is a 2-level tree (RD-level + Prefix-level) similar to
+ * L3VPN routes.
+ */
+ global_dest = bgp_global_evpn_node_lookup(bgp->rib[afi][safi], afi, safi,
+ (const struct prefix_evpn *)p, &vpn->prd);
+ if (global_dest) {
+ /* Delete route entry in the global EVPN table. */
+ delete_evpn_route_entry(bgp, afi, safi, global_dest, &pi);
+
+ /* Schedule for processing - withdraws to peers happen from
+ * this table.
+ */
+ if (pi)
+ bgp_process(bgp, global_dest, afi, safi);
+ bgp_dest_unlock_node(global_dest);
+ }
+
+ /* Delete route entry in the VNI route table. This can just be removed.
+ */
+ delete_evpn_route_entry(bgp, afi, safi, dest, &pi);
+ if (pi) {
+ bgp_path_info_reap(dest, pi);
+ evpn_route_select_install(bgp, vpn, dest);
+ }
+ bgp_dest_unlock_node(dest);
+
+ return 0;
+}
+
+void bgp_evpn_update_type2_route_entry(struct bgp *bgp, struct bgpevpn *vpn,
+ struct bgp_dest *dest,
+ struct bgp_path_info *local_pi,
+ const char *caller)
+{
+ afi_t afi = AFI_L2VPN;
+ safi_t safi = SAFI_EVPN;
+ struct bgp_path_info *pi;
+ struct attr attr;
+ struct attr *attr_new;
+ uint32_t seq;
+ int add_l3_ecomm = 0;
+ struct bgp_dest *global_dest;
+ struct bgp_path_info *global_pi;
+ struct prefix_evpn *evp =
+ (struct prefix_evpn *)bgp_dest_get_prefix(dest);
+ int route_change;
+ bool old_is_sync = false;
+
+ if (CHECK_FLAG(local_pi->flags, BGP_PATH_REMOVED))
+ return;
+
+ /*
+ * Build attribute per local route as the MAC mobility and
+ * some other values could differ for different routes. The
+ * attributes will be shared in the hash table.
+ */
+ bgp_attr_default_set(&attr, bgp, BGP_ORIGIN_IGP);
+ attr.nexthop = vpn->originator_ip;
+ attr.mp_nexthop_global_in = vpn->originator_ip;
+ attr.mp_nexthop_len = BGP_ATTR_NHLEN_IPV4;
+ attr.sticky = (local_pi->attr->sticky) ? 1 : 0;
+ attr.router_flag = (local_pi->attr->router_flag) ? 1 : 0;
+ attr.es_flags = local_pi->attr->es_flags;
+ if (local_pi->attr->default_gw) {
+ attr.default_gw = 1;
+ if (is_evpn_prefix_ipaddr_v6(evp))
+ attr.router_flag = 1;
+ }
+ memcpy(&attr.esi, &local_pi->attr->esi, sizeof(esi_t));
+ bgp_evpn_get_rmac_nexthop(vpn, evp, &attr,
+ local_pi->extra->af_flags);
+ vni2label(vpn->vni, &(attr.label));
+ /* Add L3 VNI RTs and RMAC for non IPv6 link-local if
+ * using L3 VNI for type-2 routes also.
+ */
+ add_l3_ecomm = bgp_evpn_route_add_l3_ecomm_ok(
+ vpn, evp,
+ (attr.es_flags & ATTR_ES_IS_LOCAL) ? &attr.esi : NULL);
+
+ /* Set up extended community. */
+ build_evpn_route_extcomm(vpn, &attr, add_l3_ecomm);
+ seq = mac_mobility_seqnum(local_pi->attr);
+
+ if (bgp_debug_zebra(NULL)) {
+ char buf3[ESI_STR_LEN];
+
+ zlog_debug(
+ "VRF %s vni %u evp %pFX RMAC %pEA nexthop %pI4 esi %s esf 0x%x from %s",
+ vpn->bgp_vrf ? vrf_id_to_name(vpn->bgp_vrf->vrf_id)
+ : " ",
+ vpn->vni, evp, &attr.rmac, &attr.mp_nexthop_global_in,
+ esi_to_str(&attr.esi, buf3, sizeof(buf3)),
+ attr.es_flags, caller);
+ }
+
+ /* Update the route entry. */
+ route_change = update_evpn_route_entry(
+ bgp, vpn, afi, safi, dest, &attr, 0, &pi, 0, seq,
+ true /* setup_sync */, &old_is_sync);
+
+ assert(pi);
+ attr_new = pi->attr;
+ /* lock ri to prevent freeing in evpn_route_select_install */
+ bgp_path_info_lock(pi);
+
+ /* Perform route selection. Normally, the local route in the
+ * VNI is expected to win and be the best route. However,
+ * under peculiar situations (e.g., tunnel (next hop) IP change
+ * that causes best selection to be based on next hop), a
+ * remote route could win. If the local route is the best,
+ * ensure it is updated in the global EVPN route table and
+ * advertised to peers; otherwise, ensure it is evicted and
+ * (re)install the remote route into zebra.
+ */
+ evpn_route_select_install(bgp, vpn, dest);
+
+ if (CHECK_FLAG(pi->flags, BGP_PATH_REMOVED)) {
+ route_change = 0;
+ } else {
+ if (!CHECK_FLAG(pi->flags, BGP_PATH_SELECTED)) {
+ route_change = 0;
+ evpn_cleanup_local_non_best_route(bgp, vpn, dest, pi);
+ } else {
+ bool new_is_sync;
+
+ /* If the local path already existed and is still the
+ * best path we need to also check if it transitioned
+ * from being a sync path to a non-sync path. If it
+ * it did we need to notify zebra that the sync-path
+ * has been removed.
+ */
+ new_is_sync = bgp_evpn_attr_is_sync(pi->attr);
+ if (!new_is_sync && old_is_sync)
+ evpn_zebra_uninstall(bgp, vpn,
+ evp, zero_vtep_ip);
+ }
+ }
+
+
+ /* unlock pi */
+ bgp_path_info_unlock(pi);
+
+ if (route_change) {
+ /* Update route in global routing table. */
+ global_dest = bgp_global_evpn_node_get(bgp->rib[afi][safi], afi,
+ safi, evp, &vpn->prd);
+ assert(global_dest);
+ update_evpn_route_entry(
+ bgp, vpn, afi, safi, global_dest, attr_new, 0,
+ &global_pi, 0, mac_mobility_seqnum(attr_new),
+ false /* setup_sync */, NULL /* old_is_sync */);
+
+ /* Schedule for processing and unlock node. */
+ bgp_process(bgp, global_dest, afi, safi);
+ bgp_dest_unlock_node(global_dest);
+ }
+
+ /* Unintern temporary. */
+ aspath_unintern(&attr.aspath);
+}
+
+/*
+ * Update all type-2 (MACIP) local routes for this VNI - these should also
+ * be scheduled for advertise to peers.
+ */
+static int update_all_type2_routes(struct bgp *bgp, struct bgpevpn *vpn)
+{
+ struct bgp_dest *dest;
+ struct bgp_path_info *tmp_pi;
+
+ /* Walk this VNI's route table and update local type-2 routes. For any
+ * routes updated, update corresponding entry in the global table too.
+ */
+ for (dest = bgp_table_top(vpn->route_table); dest;
+ dest = bgp_route_next(dest)) {
+ const struct prefix_evpn *evp =
+ (const struct prefix_evpn *)bgp_dest_get_prefix(dest);
+
+ if (evp->prefix.route_type != BGP_EVPN_MAC_IP_ROUTE)
+ continue;
+
+ /* Identify local route. */
+ for (tmp_pi = bgp_dest_get_bgp_path_info(dest); tmp_pi;
+ tmp_pi = tmp_pi->next) {
+ if (tmp_pi->peer == bgp->peer_self
+ && tmp_pi->type == ZEBRA_ROUTE_BGP
+ && tmp_pi->sub_type == BGP_ROUTE_STATIC)
+ break;
+ }
+
+ if (!tmp_pi)
+ continue;
+
+ bgp_evpn_update_type2_route_entry(bgp, vpn, dest, tmp_pi,
+ __func__);
+ }
+
+ return 0;
+}
+
+/*
+ * Delete all type-2 (MACIP) local routes for this VNI - only from the
+ * global routing table. These are also scheduled for withdraw from peers.
+ */
+static void delete_global_type2_routes(struct bgp *bgp, struct bgpevpn *vpn)
+{
+ afi_t afi;
+ safi_t safi;
+ struct bgp_dest *rddest, *dest;
+ struct bgp_table *table;
+ struct bgp_path_info *pi;
+
+ afi = AFI_L2VPN;
+ safi = SAFI_EVPN;
+
+ rddest = bgp_node_lookup(bgp->rib[afi][safi],
+ (struct prefix *)&vpn->prd);
+ if (rddest) {
+ table = bgp_dest_get_bgp_table_info(rddest);
+ for (dest = bgp_table_top(table); dest;
+ dest = bgp_route_next(dest)) {
+ const struct prefix_evpn *evp =
+ (const struct prefix_evpn *)bgp_dest_get_prefix(
+ dest);
+
+ if (evp->prefix.route_type != BGP_EVPN_MAC_IP_ROUTE)
+ continue;
+
+ delete_evpn_route_entry(bgp, afi, safi, dest, &pi);
+ if (pi)
+ bgp_process(bgp, dest, afi, safi);
+ }
+
+ /* Unlock RD node. */
+ bgp_dest_unlock_node(rddest);
+ }
+}
+
+/*
+ * Delete all type-2 (MACIP) local routes for this VNI - from the global
+ * table as well as the per-VNI route table.
+ */
+static int delete_all_type2_routes(struct bgp *bgp, struct bgpevpn *vpn)
+{
+ afi_t afi;
+ safi_t safi;
+ struct bgp_dest *dest;
+ struct bgp_path_info *pi;
+
+ afi = AFI_L2VPN;
+ safi = SAFI_EVPN;
+
+ /* First, walk the global route table for this VNI's type-2 local
+ * routes.
+ * EVPN routes are a 2-level table, first get the RD table.
+ */
+ delete_global_type2_routes(bgp, vpn);
+
+ /* Next, walk this VNI's route table and delete local type-2 routes. */
+ for (dest = bgp_table_top(vpn->route_table); dest;
+ dest = bgp_route_next(dest)) {
+ const struct prefix_evpn *evp =
+ (const struct prefix_evpn *)bgp_dest_get_prefix(dest);
+
+ if (evp->prefix.route_type != BGP_EVPN_MAC_IP_ROUTE)
+ continue;
+
+ delete_evpn_route_entry(bgp, afi, safi, dest, &pi);
+
+ /* Route entry in local table gets deleted immediately. */
+ if (pi)
+ bgp_path_info_reap(dest, pi);
+ }
+
+ return 0;
+}
+
+/*
+ * Delete all routes in the per-VNI route table.
+ */
+static int delete_all_vni_routes(struct bgp *bgp, struct bgpevpn *vpn)
+{
+ struct bgp_dest *dest;
+ struct bgp_path_info *pi, *nextpi;
+
+ /* Walk this VNI's route table and delete all routes. */
+ for (dest = bgp_table_top(vpn->route_table); dest;
+ dest = bgp_route_next(dest)) {
+ for (pi = bgp_dest_get_bgp_path_info(dest);
+ (pi != NULL) && (nextpi = pi->next, 1); pi = nextpi) {
+ bgp_evpn_remote_ip_hash_del(vpn, pi);
+ bgp_path_info_delete(dest, pi);
+ bgp_path_info_reap(dest, pi);
+ }
+ }
+
+ return 0;
+}
+
+/* BUM traffic flood mode per-l2-vni */
+static int bgp_evpn_vni_flood_mode_get(struct bgp *bgp,
+ struct bgpevpn *vpn)
+{
+ /* if flooding has been globally disabled per-vni mode is
+ * not relevant
+ */
+ if (bgp->vxlan_flood_ctrl == VXLAN_FLOOD_DISABLED)
+ return VXLAN_FLOOD_DISABLED;
+
+ /* if mcast group ip has been specified we use a PIM-SM MDT */
+ if (vpn->mcast_grp.s_addr != INADDR_ANY)
+ return VXLAN_FLOOD_PIM_SM;
+
+ /* default is ingress replication */
+ return VXLAN_FLOOD_HEAD_END_REPL;
+}
+
+/*
+ * Update (and advertise) local routes for a VNI. Invoked upon the VNI
+ * export RT getting modified or change to tunnel IP. Note that these
+ * situations need the route in the per-VNI table as well as the global
+ * table to be updated (as attributes change).
+ */
+int update_routes_for_vni(struct bgp *bgp, struct bgpevpn *vpn)
+{
+ int ret;
+ struct prefix_evpn p;
+
+ update_type1_routes_for_evi(bgp, vpn);
+
+ /* Update and advertise the type-3 route (only one) followed by the
+ * locally learnt type-2 routes (MACIP) - for this VNI.
+ *
+ * RT-3 only if doing head-end replication
+ */
+ if (bgp_evpn_vni_flood_mode_get(bgp, vpn)
+ == VXLAN_FLOOD_HEAD_END_REPL) {
+ build_evpn_type3_prefix(&p, vpn->originator_ip);
+ ret = update_evpn_route(bgp, vpn, &p, 0, 0, NULL);
+ if (ret)
+ return ret;
+ }
+
+ return update_all_type2_routes(bgp, vpn);
+}
+
+/*
+ * Delete (and withdraw) local routes for specified VNI from the global
+ * table and per-VNI table. After this, remove all other routes from
+ * the per-VNI table. Invoked upon the VNI being deleted or EVPN
+ * (advertise-all-vni) being disabled.
+ */
+static int delete_routes_for_vni(struct bgp *bgp, struct bgpevpn *vpn)
+{
+ int ret;
+ struct prefix_evpn p;
+
+ /* Delete and withdraw locally learnt type-2 routes (MACIP)
+ * followed by type-3 routes (only one) - for this VNI.
+ */
+ ret = delete_all_type2_routes(bgp, vpn);
+ if (ret)
+ return ret;
+
+ build_evpn_type3_prefix(&p, vpn->originator_ip);
+ ret = delete_evpn_route(bgp, vpn, &p);
+ if (ret)
+ return ret;
+
+ /* Delete all routes from the per-VNI table. */
+ return delete_all_vni_routes(bgp, vpn);
+}
+
+/*
+ * There is a flood mcast IP address change. Update the mcast-grp and
+ * remove the type-3 route if any. A new type-3 route will be generated
+ * post tunnel_ip update if the new flood mode is head-end-replication.
+ */
+static int bgp_evpn_mcast_grp_change(struct bgp *bgp, struct bgpevpn *vpn,
+ struct in_addr mcast_grp)
+{
+ struct prefix_evpn p;
+
+ vpn->mcast_grp = mcast_grp;
+
+ if (is_vni_live(vpn)) {
+ build_evpn_type3_prefix(&p, vpn->originator_ip);
+ delete_evpn_route(bgp, vpn, &p);
+ }
+
+ return 0;
+}
+
+/*
+ * There is a tunnel endpoint IP address change for this VNI, delete
+ * prior type-3 route (if needed) and update.
+ * Note: Route re-advertisement happens elsewhere after other processing
+ * other changes.
+ */
+static int handle_tunnel_ip_change(struct bgp *bgp, struct bgpevpn *vpn,
+ struct in_addr originator_ip)
+{
+ struct prefix_evpn p;
+
+ /* If VNI is not live, we only need to update the originator ip */
+ if (!is_vni_live(vpn)) {
+ vpn->originator_ip = originator_ip;
+ return 0;
+ }
+
+ /* Update the tunnel-ip hash */
+ bgp_tip_del(bgp, &vpn->originator_ip);
+ bgp_tip_add(bgp, &originator_ip);
+
+ /* filter routes as martian nexthop db has changed */
+ bgp_filter_evpn_routes_upon_martian_nh_change(bgp);
+
+ /* Need to withdraw type-3 route as the originator IP is part
+ * of the key.
+ */
+ build_evpn_type3_prefix(&p, vpn->originator_ip);
+ delete_evpn_route(bgp, vpn, &p);
+
+ /* Update the tunnel IP and re-advertise all routes for this VNI. */
+ vpn->originator_ip = originator_ip;
+ return 0;
+}
+
+static struct bgp_path_info *
+bgp_create_evpn_bgp_path_info(struct bgp_path_info *parent_pi,
+ struct bgp_dest *dest, struct attr *attr)
+{
+ struct attr *attr_new;
+ struct bgp_path_info *pi;
+
+ /* Add (or update) attribute to hash. */
+ attr_new = bgp_attr_intern(attr);
+
+ /* Create new route with its attribute. */
+ pi = info_make(parent_pi->type, BGP_ROUTE_IMPORTED, 0, parent_pi->peer,
+ attr_new, dest);
+ SET_FLAG(pi->flags, BGP_PATH_VALID);
+ bgp_path_info_extra_get(pi);
+ pi->extra->parent = bgp_path_info_lock(parent_pi);
+ bgp_dest_lock_node((struct bgp_dest *)parent_pi->net);
+ if (parent_pi->extra) {
+ memcpy(&pi->extra->label, &parent_pi->extra->label,
+ sizeof(pi->extra->label));
+ pi->extra->num_labels = parent_pi->extra->num_labels;
+ pi->extra->igpmetric = parent_pi->extra->igpmetric;
+ }
+ bgp_path_info_add(dest, pi);
+
+ return pi;
+}
+
+/*
+ * Install route entry into the VRF routing table and invoke route selection.
+ */
+static int install_evpn_route_entry_in_vrf(struct bgp *bgp_vrf,
+ const struct prefix_evpn *evp,
+ struct bgp_path_info *parent_pi)
+{
+ struct bgp_dest *dest;
+ struct bgp_path_info *pi;
+ struct attr attr;
+ struct attr *attr_new;
+ int ret = 0;
+ struct prefix p;
+ struct prefix *pp = &p;
+ afi_t afi = 0;
+ safi_t safi = 0;
+ bool new_pi = false;
+ bool use_l3nhg = false;
+ bool is_l3nhg_active = false;
+ char buf1[INET6_ADDRSTRLEN];
+
+ memset(pp, 0, sizeof(struct prefix));
+ ip_prefix_from_evpn_prefix(evp, pp);
+
+ if (bgp_debug_zebra(NULL))
+ zlog_debug(
+ "vrf %s: import evpn prefix %pFX parent %p flags 0x%x",
+ vrf_id_to_name(bgp_vrf->vrf_id), evp, parent_pi,
+ parent_pi->flags);
+
+ /* Create (or fetch) route within the VRF. */
+ /* NOTE: There is no RD here. */
+ if (is_evpn_prefix_ipaddr_v4(evp)) {
+ afi = AFI_IP;
+ safi = SAFI_UNICAST;
+ dest = bgp_node_get(bgp_vrf->rib[afi][safi], pp);
+ } else if (is_evpn_prefix_ipaddr_v6(evp)) {
+ afi = AFI_IP6;
+ safi = SAFI_UNICAST;
+ dest = bgp_node_get(bgp_vrf->rib[afi][safi], pp);
+ } else
+ return 0;
+
+ /* EVPN routes currently only support a IPv4 next hop which corresponds
+ * to the remote VTEP. When importing into a VRF, if it is IPv6 host
+ * or prefix route, we have to convert the next hop to an IPv4-mapped
+ * address for the rest of the code to flow through. In the case of IPv4,
+ * make sure to set the flag for next hop attribute.
+ */
+ attr = *parent_pi->attr;
+ if (attr.evpn_overlay.type != OVERLAY_INDEX_GATEWAY_IP) {
+ if (afi == AFI_IP6)
+ evpn_convert_nexthop_to_ipv6(&attr);
+ else {
+ attr.nexthop = attr.mp_nexthop_global_in;
+ attr.flag |= ATTR_FLAG_BIT(BGP_ATTR_NEXT_HOP);
+ }
+ } else {
+
+ /*
+ * If gateway IP overlay index is specified in the NLRI of
+ * EVPN RT-5, this gateway IP should be used as the nexthop
+ * for the prefix in the VRF
+ */
+ if (bgp_debug_zebra(NULL)) {
+ zlog_debug(
+ "Install gateway IP %s as nexthop for prefix %pFX in vrf %s",
+ inet_ntop(pp->family, &attr.evpn_overlay.gw_ip,
+ buf1, sizeof(buf1)), pp,
+ vrf_id_to_name(bgp_vrf->vrf_id));
+ }
+
+ if (afi == AFI_IP6) {
+ memcpy(&attr.mp_nexthop_global,
+ &attr.evpn_overlay.gw_ip.ipaddr_v6,
+ sizeof(struct in6_addr));
+ attr.mp_nexthop_len = IPV6_MAX_BYTELEN;
+ } else {
+ attr.nexthop = attr.evpn_overlay.gw_ip.ipaddr_v4;
+ attr.flag |= ATTR_FLAG_BIT(BGP_ATTR_NEXT_HOP);
+ }
+ }
+
+ bgp_evpn_es_vrf_use_nhg(bgp_vrf, &parent_pi->attr->esi, &use_l3nhg,
+ &is_l3nhg_active, NULL);
+ if (use_l3nhg)
+ attr.es_flags |= ATTR_ES_L3_NHG_USE;
+ if (is_l3nhg_active)
+ attr.es_flags |= ATTR_ES_L3_NHG_ACTIVE;
+
+ /* Check if route entry is already present. */
+ for (pi = bgp_dest_get_bgp_path_info(dest); pi; pi = pi->next)
+ if (pi->extra
+ && (struct bgp_path_info *)pi->extra->parent == parent_pi)
+ break;
+
+ if (!pi) {
+ pi = bgp_create_evpn_bgp_path_info(parent_pi, dest, &attr);
+ new_pi = true;
+ } else {
+ if (attrhash_cmp(pi->attr, &attr)
+ && !CHECK_FLAG(pi->flags, BGP_PATH_REMOVED)) {
+ bgp_dest_unlock_node(dest);
+ return 0;
+ }
+ /* The attribute has changed. */
+ /* Add (or update) attribute to hash. */
+ attr_new = bgp_attr_intern(&attr);
+
+ /* Restore route, if needed. */
+ if (CHECK_FLAG(pi->flags, BGP_PATH_REMOVED))
+ bgp_path_info_restore(dest, pi);
+
+ /* Mark if nexthop has changed. */
+ if ((afi == AFI_IP
+ && !IPV4_ADDR_SAME(&pi->attr->nexthop, &attr_new->nexthop))
+ || (afi == AFI_IP6
+ && !IPV6_ADDR_SAME(&pi->attr->mp_nexthop_global,
+ &attr_new->mp_nexthop_global)))
+ SET_FLAG(pi->flags, BGP_PATH_IGP_CHANGED);
+
+ bgp_path_info_set_flag(dest, pi, BGP_PATH_ATTR_CHANGED);
+ /* Unintern existing, set to new. */
+ bgp_attr_unintern(&pi->attr);
+ pi->attr = attr_new;
+ pi->uptime = monotime(NULL);
+ }
+
+ /* Gateway IP nexthop should be resolved */
+ if (attr.evpn_overlay.type == OVERLAY_INDEX_GATEWAY_IP) {
+ if (bgp_find_or_add_nexthop(bgp_vrf, bgp_vrf, afi, safi, pi,
+ NULL, 0, NULL))
+ bgp_path_info_set_flag(dest, pi, BGP_PATH_VALID);
+ else {
+ if (BGP_DEBUG(nht, NHT)) {
+ inet_ntop(pp->family,
+ &attr.evpn_overlay.gw_ip,
+ buf1, sizeof(buf1));
+ zlog_debug("%s: gateway IP NH unresolved",
+ buf1);
+ }
+ bgp_path_info_unset_flag(dest, pi, BGP_PATH_VALID);
+ }
+ } else {
+
+ /* as it is an importation, change nexthop */
+ bgp_path_info_set_flag(dest, pi, BGP_PATH_ANNC_NH_SELF);
+ }
+
+ /* Link path to evpn nexthop */
+ bgp_evpn_path_nh_add(bgp_vrf, pi);
+
+ bgp_aggregate_increment(bgp_vrf, bgp_dest_get_prefix(dest), pi, afi,
+ safi);
+
+ /* Perform route selection and update zebra, if required. */
+ bgp_process(bgp_vrf, dest, afi, safi);
+
+ /* Process for route leaking. */
+ vpn_leak_from_vrf_update(bgp_get_default(), bgp_vrf, pi);
+
+ bgp_dest_unlock_node(dest);
+
+ if (bgp_debug_zebra(NULL))
+ zlog_debug("... %s pi dest %p (l %d) pi %p (l %d, f 0x%x)",
+ new_pi ? "new" : "update", dest,
+ bgp_dest_get_lock_count(dest), pi, pi->lock,
+ pi->flags);
+
+ return ret;
+}
+
+/*
+ * Install route entry into the VNI routing table and invoke route selection.
+ */
+static int install_evpn_route_entry(struct bgp *bgp, struct bgpevpn *vpn,
+ const struct prefix_evpn *p,
+ struct bgp_path_info *parent_pi)
+{
+ struct bgp_dest *dest;
+ struct bgp_path_info *pi;
+ struct bgp_path_info *local_pi;
+ struct attr *attr_new;
+ int ret;
+ struct prefix_evpn ad_evp;
+ bool old_local_es = false;
+ bool new_local_es;
+
+ /* EAD prefix in the global table doesn't include the VTEP-IP so
+ * we need to create a different copy for the VNI
+ */
+ if (p->prefix.route_type == BGP_EVPN_AD_ROUTE)
+ p = evpn_type1_prefix_vni_copy(&ad_evp, p,
+ parent_pi->attr->nexthop);
+
+ /* Create (or fetch) route within the VNI. */
+ /* NOTE: There is no RD here. */
+ dest = bgp_node_get(vpn->route_table, (struct prefix *)p);
+
+ /* Check if route entry is already present. */
+ for (pi = bgp_dest_get_bgp_path_info(dest); pi; pi = pi->next)
+ if (pi->extra
+ && (struct bgp_path_info *)pi->extra->parent == parent_pi)
+ break;
+
+ if (!pi) {
+ /* Create an info */
+ pi = bgp_create_evpn_bgp_path_info(parent_pi, dest,
+ parent_pi->attr);
+ new_local_es = bgp_evpn_attr_is_local_es(pi->attr);
+ } else {
+ if (attrhash_cmp(pi->attr, parent_pi->attr)
+ && !CHECK_FLAG(pi->flags, BGP_PATH_REMOVED)) {
+ bgp_dest_unlock_node(dest);
+ return 0;
+ }
+ /* The attribute has changed. */
+ /* Add (or update) attribute to hash. */
+ attr_new = bgp_attr_intern(parent_pi->attr);
+
+ /* Restore route, if needed. */
+ if (CHECK_FLAG(pi->flags, BGP_PATH_REMOVED))
+ bgp_path_info_restore(dest, pi);
+
+ /* Mark if nexthop has changed. */
+ if (!IPV4_ADDR_SAME(&pi->attr->nexthop, &attr_new->nexthop))
+ SET_FLAG(pi->flags, BGP_PATH_IGP_CHANGED);
+
+ old_local_es = bgp_evpn_attr_is_local_es(pi->attr);
+ new_local_es = bgp_evpn_attr_is_local_es(attr_new);
+ /* If ESI is different or if its type has changed we
+ * need to reinstall the path in zebra
+ */
+ if ((old_local_es != new_local_es)
+ || memcmp(&pi->attr->esi, &attr_new->esi,
+ sizeof(attr_new->esi))) {
+
+ if (BGP_DEBUG(evpn_mh, EVPN_MH_RT))
+ zlog_debug("VNI %d path %pFX chg to %s es",
+ vpn->vni, &pi->net->p,
+ new_local_es ? "local"
+ : "non-local");
+ bgp_path_info_set_flag(dest, pi, BGP_PATH_ATTR_CHANGED);
+ }
+
+ /* Unintern existing, set to new. */
+ bgp_attr_unintern(&pi->attr);
+ pi->attr = attr_new;
+ pi->uptime = monotime(NULL);
+ }
+
+ /* Add this route to remote IP hashtable */
+ bgp_evpn_remote_ip_hash_add(vpn, pi);
+
+ /* Perform route selection and update zebra, if required. */
+ ret = evpn_route_select_install(bgp, vpn, dest);
+
+ /* if the best path is a local path with a non-zero ES
+ * sync info against the local path may need to be updated
+ * when a remote path is added/updated (including changes
+ * from sync-path to remote-path)
+ */
+ local_pi = bgp_evpn_route_get_local_path(bgp, dest);
+ if (local_pi && (old_local_es || new_local_es))
+ bgp_evpn_update_type2_route_entry(bgp, vpn, dest, local_pi,
+ __func__);
+ bgp_dest_unlock_node(dest);
+
+ return ret;
+}
+
+/*
+ * Uninstall route entry from the VRF routing table and send message
+ * to zebra, if appropriate.
+ */
+static int uninstall_evpn_route_entry_in_vrf(struct bgp *bgp_vrf,
+ const struct prefix_evpn *evp,
+ struct bgp_path_info *parent_pi)
+{
+ struct bgp_dest *dest;
+ struct bgp_path_info *pi;
+ int ret = 0;
+ struct prefix p;
+ struct prefix *pp = &p;
+ afi_t afi = 0;
+ safi_t safi = 0;
+
+ memset(pp, 0, sizeof(struct prefix));
+ ip_prefix_from_evpn_prefix(evp, pp);
+
+ if (bgp_debug_zebra(NULL))
+ zlog_debug(
+ "vrf %s: unimport evpn prefix %pFX parent %p flags 0x%x",
+ vrf_id_to_name(bgp_vrf->vrf_id), evp, parent_pi,
+ parent_pi->flags);
+
+ /* Locate route within the VRF. */
+ /* NOTE: There is no RD here. */
+ if (is_evpn_prefix_ipaddr_v4(evp)) {
+ afi = AFI_IP;
+ safi = SAFI_UNICAST;
+ dest = bgp_node_lookup(bgp_vrf->rib[afi][safi], pp);
+ } else {
+ afi = AFI_IP6;
+ safi = SAFI_UNICAST;
+ dest = bgp_node_lookup(bgp_vrf->rib[afi][safi], pp);
+ }
+
+ if (!dest)
+ return 0;
+
+ /* Find matching route entry. */
+ for (pi = bgp_dest_get_bgp_path_info(dest); pi; pi = pi->next)
+ if (pi->extra
+ && (struct bgp_path_info *)pi->extra->parent == parent_pi)
+ break;
+
+ if (!pi) {
+ bgp_dest_unlock_node(dest);
+ return 0;
+ }
+
+ if (bgp_debug_zebra(NULL))
+ zlog_debug("... delete dest %p (l %d) pi %p (l %d, f 0x%x)",
+ dest, bgp_dest_get_lock_count(dest), pi, pi->lock,
+ pi->flags);
+
+ /* Process for route leaking. */
+ vpn_leak_from_vrf_withdraw(bgp_get_default(), bgp_vrf, pi);
+
+ bgp_aggregate_decrement(bgp_vrf, bgp_dest_get_prefix(dest), pi, afi,
+ safi);
+
+ /* Mark entry for deletion */
+ bgp_path_info_delete(dest, pi);
+
+ /* Unlink path to evpn nexthop */
+ bgp_evpn_path_nh_del(bgp_vrf, pi);
+
+ /* Perform route selection and update zebra, if required. */
+ bgp_process(bgp_vrf, dest, afi, safi);
+
+ /* Unlock route node. */
+ bgp_dest_unlock_node(dest);
+
+ return ret;
+}
+
+/*
+ * Uninstall route entry from the VNI routing table and send message
+ * to zebra, if appropriate.
+ */
+static int uninstall_evpn_route_entry(struct bgp *bgp, struct bgpevpn *vpn,
+ const struct prefix_evpn *p,
+ struct bgp_path_info *parent_pi)
+{
+ struct bgp_dest *dest;
+ struct bgp_path_info *pi;
+ struct bgp_path_info *local_pi;
+ int ret;
+ struct prefix_evpn ad_evp;
+
+ /* EAD prefix in the global table doesn't include the VTEP-IP so
+ * we need to create a different copy for the VNI
+ */
+ if (p->prefix.route_type == BGP_EVPN_AD_ROUTE)
+ p = evpn_type1_prefix_vni_copy(&ad_evp, p,
+ parent_pi->attr->nexthop);
+
+ /* Locate route within the VNI. */
+ /* NOTE: There is no RD here. */
+ dest = bgp_node_lookup(vpn->route_table, (struct prefix *)p);
+ if (!dest)
+ return 0;
+
+ /* Find matching route entry. */
+ for (pi = bgp_dest_get_bgp_path_info(dest); pi; pi = pi->next)
+ if (pi->extra
+ && (struct bgp_path_info *)pi->extra->parent == parent_pi)
+ break;
+
+ if (!pi) {
+ bgp_dest_unlock_node(dest);
+ return 0;
+ }
+
+ bgp_evpn_remote_ip_hash_del(vpn, pi);
+
+ /* Mark entry for deletion */
+ bgp_path_info_delete(dest, pi);
+
+ /* Perform route selection and update zebra, if required. */
+ ret = evpn_route_select_install(bgp, vpn, dest);
+
+ /* if the best path is a local path with a non-zero ES
+ * sync info against the local path may need to be updated
+ * when a remote path is deleted
+ */
+ local_pi = bgp_evpn_route_get_local_path(bgp, dest);
+ if (local_pi && bgp_evpn_attr_is_local_es(local_pi->attr))
+ bgp_evpn_update_type2_route_entry(bgp, vpn, dest, local_pi,
+ __func__);
+
+ /* Unlock route node. */
+ bgp_dest_unlock_node(dest);
+
+ return ret;
+}
+
+/*
+ * Given a route entry and a VRF, see if this route entry should be
+ * imported into the VRF i.e., RTs match.
+ */
+static int is_route_matching_for_vrf(struct bgp *bgp_vrf,
+ struct bgp_path_info *pi)
+{
+ struct attr *attr = pi->attr;
+ struct ecommunity *ecom;
+ uint32_t i;
+
+ assert(attr);
+ /* Route should have valid RT to be even considered. */
+ if (!(attr->flag & ATTR_FLAG_BIT(BGP_ATTR_EXT_COMMUNITIES)))
+ return 0;
+
+ ecom = bgp_attr_get_ecommunity(attr);
+ if (!ecom || !ecom->size)
+ return 0;
+
+ /* For each extended community RT, see if it matches this VNI. If any RT
+ * matches, we're done.
+ */
+ for (i = 0; i < ecom->size; i++) {
+ uint8_t *pnt;
+ uint8_t type, sub_type;
+ struct ecommunity_val *eval;
+ struct ecommunity_val eval_tmp;
+ struct vrf_irt_node *irt;
+
+ /* Only deal with RTs */
+ pnt = (ecom->val + (i * ecom->unit_size));
+ eval = (struct ecommunity_val *)(ecom->val
+ + (i * ecom->unit_size));
+ type = *pnt++;
+ sub_type = *pnt++;
+ if (sub_type != ECOMMUNITY_ROUTE_TARGET)
+ continue;
+
+ /* See if this RT matches specified VNIs import RTs */
+ irt = lookup_vrf_import_rt(eval);
+ if (irt)
+ if (is_vrf_present_in_irt_vrfs(irt->vrfs, bgp_vrf))
+ return 1;
+
+ /* Also check for non-exact match. In this, we mask out the AS
+ * and
+ * only check on the local-admin sub-field. This is to
+ * facilitate using
+ * VNI as the RT for EBGP peering too.
+ */
+ irt = NULL;
+ if (type == ECOMMUNITY_ENCODE_AS
+ || type == ECOMMUNITY_ENCODE_AS4
+ || type == ECOMMUNITY_ENCODE_IP) {
+ memcpy(&eval_tmp, eval, ecom->unit_size);
+ mask_ecom_global_admin(&eval_tmp, eval);
+ irt = lookup_vrf_import_rt(&eval_tmp);
+ }
+ if (irt)
+ if (is_vrf_present_in_irt_vrfs(irt->vrfs, bgp_vrf))
+ return 1;
+ }
+
+ return 0;
+}
+
+/*
+ * Given a route entry and a VNI, see if this route entry should be
+ * imported into the VNI i.e., RTs match.
+ */
+static int is_route_matching_for_vni(struct bgp *bgp, struct bgpevpn *vpn,
+ struct bgp_path_info *pi)
+{
+ struct attr *attr = pi->attr;
+ struct ecommunity *ecom;
+ uint32_t i;
+
+ assert(attr);
+ /* Route should have valid RT to be even considered. */
+ if (!(attr->flag & ATTR_FLAG_BIT(BGP_ATTR_EXT_COMMUNITIES)))
+ return 0;
+
+ ecom = bgp_attr_get_ecommunity(attr);
+ if (!ecom || !ecom->size)
+ return 0;
+
+ /* For each extended community RT, see if it matches this VNI. If any RT
+ * matches, we're done.
+ */
+ for (i = 0; i < ecom->size; i++) {
+ uint8_t *pnt;
+ uint8_t type, sub_type;
+ struct ecommunity_val *eval;
+ struct ecommunity_val eval_tmp;
+ struct irt_node *irt;
+
+ /* Only deal with RTs */
+ pnt = (ecom->val + (i * ecom->unit_size));
+ eval = (struct ecommunity_val *)(ecom->val
+ + (i * ecom->unit_size));
+ type = *pnt++;
+ sub_type = *pnt++;
+ if (sub_type != ECOMMUNITY_ROUTE_TARGET)
+ continue;
+
+ /* See if this RT matches specified VNIs import RTs */
+ irt = lookup_import_rt(bgp, eval);
+ if (irt)
+ if (is_vni_present_in_irt_vnis(irt->vnis, vpn))
+ return 1;
+
+ /* Also check for non-exact match. In this, we mask out the AS
+ * and
+ * only check on the local-admin sub-field. This is to
+ * facilitate using
+ * VNI as the RT for EBGP peering too.
+ */
+ irt = NULL;
+ if (type == ECOMMUNITY_ENCODE_AS
+ || type == ECOMMUNITY_ENCODE_AS4
+ || type == ECOMMUNITY_ENCODE_IP) {
+ memcpy(&eval_tmp, eval, ecom->unit_size);
+ mask_ecom_global_admin(&eval_tmp, eval);
+ irt = lookup_import_rt(bgp, &eval_tmp);
+ }
+ if (irt)
+ if (is_vni_present_in_irt_vnis(irt->vnis, vpn))
+ return 1;
+ }
+
+ return 0;
+}
+
+/* This API will scan evpn routes for checking attribute's rmac
+ * macthes with bgp instance router mac. It avoid installing
+ * route into bgp vrf table and remote rmac in bridge table.
+ */
+static int bgp_evpn_route_rmac_self_check(struct bgp *bgp_vrf,
+ const struct prefix_evpn *evp,
+ struct bgp_path_info *pi)
+{
+ /* evpn route could have learnt prior to L3vni has come up,
+ * perform rmac check before installing route and
+ * remote router mac.
+ * The route will be removed from global bgp table once
+ * SVI comes up with MAC and stored in hash, triggers
+ * bgp_mac_rescan_all_evpn_tables.
+ */
+ if (memcmp(&bgp_vrf->rmac, &pi->attr->rmac, ETH_ALEN) == 0) {
+ if (bgp_debug_update(pi->peer, NULL, NULL, 1)) {
+ char attr_str[BUFSIZ] = {0};
+
+ bgp_dump_attr(pi->attr, attr_str, sizeof(attr_str));
+
+ zlog_debug(
+ "%s: bgp %u prefix %pFX with attr %s - DENIED due to self mac",
+ __func__, bgp_vrf->vrf_id, evp, attr_str);
+ }
+
+ return 1;
+ }
+
+ return 0;
+}
+
+/* don't import hosts that are locally attached */
+static inline bool
+bgp_evpn_skip_vrf_import_of_local_es(struct bgp *bgp_vrf,
+ const struct prefix_evpn *evp,
+ struct bgp_path_info *pi, int install)
+{
+ esi_t *esi;
+
+ if (evp->prefix.route_type == BGP_EVPN_MAC_IP_ROUTE) {
+ esi = bgp_evpn_attr_get_esi(pi->attr);
+
+ /* Don't import routes that point to a local destination */
+ if (bgp_evpn_attr_is_local_es(pi->attr)) {
+ if (BGP_DEBUG(evpn_mh, EVPN_MH_RT)) {
+ char esi_buf[ESI_STR_LEN];
+
+ zlog_debug(
+ "vrf %s of evpn prefix %pFX skipped, local es %s",
+ install ? "import" : "unimport", evp,
+ esi_to_str(esi, esi_buf,
+ sizeof(esi_buf)));
+ }
+ return true;
+ }
+ }
+ return false;
+}
+
+/*
+ * Install or uninstall a mac-ip route in the provided vrf if
+ * there is a rt match
+ */
+int bgp_evpn_route_entry_install_if_vrf_match(struct bgp *bgp_vrf,
+ struct bgp_path_info *pi,
+ int install)
+{
+ int ret = 0;
+ const struct prefix_evpn *evp =
+ (const struct prefix_evpn *)bgp_dest_get_prefix(pi->net);
+
+ /* Consider "valid" remote routes applicable for
+ * this VRF.
+ */
+ if (!(CHECK_FLAG(pi->flags, BGP_PATH_VALID)
+ && pi->type == ZEBRA_ROUTE_BGP
+ && pi->sub_type == BGP_ROUTE_NORMAL))
+ return 0;
+
+ if (is_route_matching_for_vrf(bgp_vrf, pi)) {
+ if (bgp_evpn_route_rmac_self_check(bgp_vrf, evp, pi))
+ return 0;
+
+ /* don't import hosts that are locally attached */
+ if (install && bgp_evpn_skip_vrf_import_of_local_es(
+ bgp_vrf, evp, pi, install))
+ return 0;
+
+ if (install)
+ ret = install_evpn_route_entry_in_vrf(bgp_vrf, evp, pi);
+ else
+ ret = uninstall_evpn_route_entry_in_vrf(bgp_vrf, evp,
+ pi);
+
+ if (ret)
+ flog_err(EC_BGP_EVPN_FAIL,
+ "Failed to %s EVPN %pFX route in VRF %s",
+ install ? "install" : "uninstall", evp,
+ vrf_id_to_name(bgp_vrf->vrf_id));
+ }
+
+ return ret;
+}
+
+/*
+ * Install or uninstall mac-ip routes are appropriate for this
+ * particular VRF.
+ */
+static int install_uninstall_routes_for_vrf(struct bgp *bgp_vrf, int install)
+{
+ afi_t afi;
+ safi_t safi;
+ struct bgp_dest *rd_dest, *dest;
+ struct bgp_table *table;
+ struct bgp_path_info *pi;
+ int ret;
+ struct bgp *bgp_evpn = NULL;
+
+ afi = AFI_L2VPN;
+ safi = SAFI_EVPN;
+ bgp_evpn = bgp_get_evpn();
+ if (!bgp_evpn)
+ return -1;
+
+ /* Walk entire global routing table and evaluate routes which could be
+ * imported into this VRF. Note that we need to loop through all global
+ * routes to determine which route matches the import rt on vrf
+ */
+ for (rd_dest = bgp_table_top(bgp_evpn->rib[afi][safi]); rd_dest;
+ rd_dest = bgp_route_next(rd_dest)) {
+ table = bgp_dest_get_bgp_table_info(rd_dest);
+ if (!table)
+ continue;
+
+ for (dest = bgp_table_top(table); dest;
+ dest = bgp_route_next(dest)) {
+ const struct prefix_evpn *evp =
+ (const struct prefix_evpn *)bgp_dest_get_prefix(
+ dest);
+
+ /* if not mac-ip route skip this route */
+ if (!(evp->prefix.route_type == BGP_EVPN_MAC_IP_ROUTE
+ || evp->prefix.route_type
+ == BGP_EVPN_IP_PREFIX_ROUTE))
+ continue;
+
+ /* if not a mac+ip route skip this route */
+ if (!(is_evpn_prefix_ipaddr_v4(evp)
+ || is_evpn_prefix_ipaddr_v6(evp)))
+ continue;
+
+ for (pi = bgp_dest_get_bgp_path_info(dest); pi;
+ pi = pi->next) {
+ ret = bgp_evpn_route_entry_install_if_vrf_match(
+ bgp_vrf, pi, install);
+ if (ret)
+ return ret;
+ }
+ }
+ }
+
+ return 0;
+}
+
+/*
+ * Install or uninstall routes of specified type that are appropriate for this
+ * particular VNI.
+ */
+static int install_uninstall_routes_for_vni(struct bgp *bgp,
+ struct bgpevpn *vpn,
+ bgp_evpn_route_type rtype,
+ int install)
+{
+ afi_t afi;
+ safi_t safi;
+ struct bgp_dest *rd_dest, *dest;
+ struct bgp_table *table;
+ struct bgp_path_info *pi;
+ int ret;
+
+ afi = AFI_L2VPN;
+ safi = SAFI_EVPN;
+
+ /* Walk entire global routing table and evaluate routes which could be
+ * imported into this VPN. Note that we cannot just look at the routes
+ * for
+ * the VNI's RD - remote routes applicable for this VNI could have any
+ * RD.
+ */
+ /* EVPN routes are a 2-level table. */
+ for (rd_dest = bgp_table_top(bgp->rib[afi][safi]); rd_dest;
+ rd_dest = bgp_route_next(rd_dest)) {
+ table = bgp_dest_get_bgp_table_info(rd_dest);
+ if (!table)
+ continue;
+
+ for (dest = bgp_table_top(table); dest;
+ dest = bgp_route_next(dest)) {
+ const struct prefix_evpn *evp =
+ (const struct prefix_evpn *)bgp_dest_get_prefix(
+ dest);
+
+ if (evp->prefix.route_type != rtype)
+ continue;
+
+ for (pi = bgp_dest_get_bgp_path_info(dest); pi;
+ pi = pi->next) {
+ /* Consider "valid" remote routes applicable for
+ * this VNI. */
+ if (!(CHECK_FLAG(pi->flags, BGP_PATH_VALID)
+ && pi->type == ZEBRA_ROUTE_BGP
+ && pi->sub_type == BGP_ROUTE_NORMAL))
+ continue;
+
+ if (is_route_matching_for_vni(bgp, vpn, pi)) {
+ if (install)
+ ret = install_evpn_route_entry(
+ bgp, vpn, evp, pi);
+ else
+ ret = uninstall_evpn_route_entry(
+ bgp, vpn, evp, pi);
+
+ if (ret) {
+ flog_err(
+ EC_BGP_EVPN_FAIL,
+ "%u: Failed to %s EVPN %s route in VNI %u",
+ bgp->vrf_id,
+ install ? "install"
+ : "uninstall",
+ rtype == BGP_EVPN_MAC_IP_ROUTE
+ ? "MACIP"
+ : "IMET",
+ vpn->vni);
+
+ bgp_dest_unlock_node(rd_dest);
+ bgp_dest_unlock_node(dest);
+ return ret;
+ }
+ }
+ }
+ }
+ }
+
+ return 0;
+}
+
+/* Install any existing remote routes applicable for this VRF into VRF RIB. This
+ * is invoked upon l3vni-add or l3vni import rt change
+ */
+static int install_routes_for_vrf(struct bgp *bgp_vrf)
+{
+ install_uninstall_routes_for_vrf(bgp_vrf, 1);
+ return 0;
+}
+
+/*
+ * Install any existing remote routes applicable for this VNI into its
+ * routing table. This is invoked when a VNI becomes "live" or its Import
+ * RT is changed.
+ */
+static int install_routes_for_vni(struct bgp *bgp, struct bgpevpn *vpn)
+{
+ int ret;
+
+ /* Install type-3 routes followed by type-2 routes - the ones applicable
+ * for this VNI.
+ */
+ ret = install_uninstall_routes_for_vni(bgp, vpn, BGP_EVPN_IMET_ROUTE,
+ 1);
+ if (ret)
+ return ret;
+
+ ret = install_uninstall_routes_for_vni(bgp, vpn, BGP_EVPN_AD_ROUTE,
+ 1);
+ if (ret)
+ return ret;
+
+ return install_uninstall_routes_for_vni(bgp, vpn, BGP_EVPN_MAC_IP_ROUTE,
+ 1);
+}
+
+/* uninstall routes from l3vni vrf. */
+static int uninstall_routes_for_vrf(struct bgp *bgp_vrf)
+{
+ install_uninstall_routes_for_vrf(bgp_vrf, 0);
+ return 0;
+}
+
+/*
+ * Uninstall any existing remote routes for this VNI. One scenario in which
+ * this is invoked is upon an import RT change.
+ */
+static int uninstall_routes_for_vni(struct bgp *bgp, struct bgpevpn *vpn)
+{
+ int ret;
+
+ /* Uninstall type-2 routes followed by type-3 routes - the ones
+ * applicable
+ * for this VNI.
+ */
+ ret = install_uninstall_routes_for_vni(bgp, vpn, BGP_EVPN_MAC_IP_ROUTE,
+ 0);
+ if (ret)
+ return ret;
+
+ ret = install_uninstall_routes_for_vni(bgp, vpn, BGP_EVPN_AD_ROUTE,
+ 0);
+ if (ret)
+ return ret;
+
+
+ return install_uninstall_routes_for_vni(bgp, vpn, BGP_EVPN_IMET_ROUTE,
+ 0);
+}
+
+/*
+ * Install or uninstall route in matching VRFs (list).
+ */
+static int install_uninstall_route_in_vrfs(struct bgp *bgp_def, afi_t afi,
+ safi_t safi, struct prefix_evpn *evp,
+ struct bgp_path_info *pi,
+ struct list *vrfs, int install)
+{
+ struct bgp *bgp_vrf;
+ struct listnode *node, *nnode;
+
+ /* Only type-2/type-5 routes go into a VRF */
+ if (!(evp->prefix.route_type == BGP_EVPN_MAC_IP_ROUTE
+ || evp->prefix.route_type == BGP_EVPN_IP_PREFIX_ROUTE))
+ return 0;
+
+ /* if it is type-2 route and not a mac+ip route skip this route */
+ if ((evp->prefix.route_type == BGP_EVPN_MAC_IP_ROUTE)
+ && !(is_evpn_prefix_ipaddr_v4(evp)
+ || is_evpn_prefix_ipaddr_v6(evp)))
+ return 0;
+
+ for (ALL_LIST_ELEMENTS(vrfs, node, nnode, bgp_vrf)) {
+ int ret;
+
+ /* don't import hosts that are locally attached */
+ if (install && bgp_evpn_skip_vrf_import_of_local_es(
+ bgp_vrf, evp, pi, install))
+ return 0;
+
+ if (install)
+ ret = install_evpn_route_entry_in_vrf(bgp_vrf, evp, pi);
+ else
+ ret = uninstall_evpn_route_entry_in_vrf(bgp_vrf, evp,
+ pi);
+
+ if (ret) {
+ flog_err(EC_BGP_EVPN_FAIL,
+ "%u: Failed to %s prefix %pFX in VRF %s",
+ bgp_def->vrf_id,
+ install ? "install" : "uninstall", evp,
+ vrf_id_to_name(bgp_vrf->vrf_id));
+ return ret;
+ }
+ }
+
+ return 0;
+}
+
+/*
+ * Install or uninstall route in matching VNIs (list).
+ */
+static int install_uninstall_route_in_vnis(struct bgp *bgp, afi_t afi,
+ safi_t safi, struct prefix_evpn *evp,
+ struct bgp_path_info *pi,
+ struct list *vnis, int install)
+{
+ struct bgpevpn *vpn;
+ struct listnode *node, *nnode;
+
+ for (ALL_LIST_ELEMENTS(vnis, node, nnode, vpn)) {
+ int ret;
+
+ if (!is_vni_live(vpn))
+ continue;
+
+ if (install)
+ ret = install_evpn_route_entry(bgp, vpn, evp, pi);
+ else
+ ret = uninstall_evpn_route_entry(bgp, vpn, evp, pi);
+
+ if (ret) {
+ flog_err(EC_BGP_EVPN_FAIL,
+ "%u: Failed to %s EVPN %s route in VNI %u",
+ bgp->vrf_id, install ? "install" : "uninstall",
+ evp->prefix.route_type == BGP_EVPN_MAC_IP_ROUTE
+ ? "MACIP"
+ : "IMET",
+ vpn->vni);
+ return ret;
+ }
+ }
+
+ return 0;
+}
+
+/*
+ * Install or uninstall route for appropriate VNIs/ESIs.
+ */
+static int bgp_evpn_install_uninstall_table(struct bgp *bgp, afi_t afi,
+ safi_t safi, const struct prefix *p,
+ struct bgp_path_info *pi,
+ int import, bool in_vni_rt,
+ bool in_vrf_rt)
+{
+ struct prefix_evpn *evp = (struct prefix_evpn *)p;
+ struct attr *attr = pi->attr;
+ struct ecommunity *ecom;
+ uint32_t i;
+ struct prefix_evpn ad_evp;
+
+ assert(attr);
+
+ /* Only type-1, type-2, type-3, type-4 and type-5
+ * are supported currently
+ */
+ if (!(evp->prefix.route_type == BGP_EVPN_MAC_IP_ROUTE
+ || evp->prefix.route_type == BGP_EVPN_IMET_ROUTE
+ || evp->prefix.route_type == BGP_EVPN_ES_ROUTE
+ || evp->prefix.route_type == BGP_EVPN_AD_ROUTE
+ || evp->prefix.route_type == BGP_EVPN_IP_PREFIX_ROUTE))
+ return 0;
+
+ /* If we don't have Route Target, nothing much to do. */
+ if (!(attr->flag & ATTR_FLAG_BIT(BGP_ATTR_EXT_COMMUNITIES)))
+ return 0;
+
+ /* EAD prefix in the global table doesn't include the VTEP-IP so
+ * we need to create a different copy for the VNI
+ */
+ if (evp->prefix.route_type == BGP_EVPN_AD_ROUTE)
+ evp = evpn_type1_prefix_vni_copy(&ad_evp, evp, attr->nexthop);
+
+ ecom = bgp_attr_get_ecommunity(attr);
+ if (!ecom || !ecom->size)
+ return -1;
+
+ /* An EVPN route belongs to a VNI or a VRF or an ESI based on the RTs
+ * attached to the route */
+ for (i = 0; i < ecom->size; i++) {
+ uint8_t *pnt;
+ uint8_t type, sub_type;
+ struct ecommunity_val *eval;
+ struct ecommunity_val eval_tmp;
+ struct irt_node *irt; /* import rt for l2vni */
+ struct vrf_irt_node *vrf_irt; /* import rt for l3vni */
+ struct bgp_evpn_es *es;
+
+ /* Only deal with RTs */
+ pnt = (ecom->val + (i * ecom->unit_size));
+ eval = (struct ecommunity_val *)(ecom->val
+ + (i * ecom->unit_size));
+ type = *pnt++;
+ sub_type = *pnt++;
+ if (sub_type != ECOMMUNITY_ROUTE_TARGET)
+ continue;
+
+ /* non-local MAC-IP routes in the global route table are linked
+ * to the destination ES
+ */
+ if (evp->prefix.route_type == BGP_EVPN_MAC_IP_ROUTE)
+ bgp_evpn_path_es_link(pi, 0,
+ bgp_evpn_attr_get_esi(pi->attr));
+
+ /*
+ * macip routes (type-2) are imported into VNI and VRF tables.
+ * IMET route is imported into VNI table.
+ * prefix routes are imported into VRF table.
+ */
+ if (evp->prefix.route_type == BGP_EVPN_MAC_IP_ROUTE ||
+ evp->prefix.route_type == BGP_EVPN_IMET_ROUTE ||
+ evp->prefix.route_type == BGP_EVPN_AD_ROUTE ||
+ evp->prefix.route_type == BGP_EVPN_IP_PREFIX_ROUTE) {
+
+ irt = in_vni_rt ? lookup_import_rt(bgp, eval) : NULL;
+ if (irt)
+ install_uninstall_route_in_vnis(
+ bgp, afi, safi, evp, pi, irt->vnis,
+ import);
+
+ vrf_irt = in_vrf_rt ? lookup_vrf_import_rt(eval) : NULL;
+ if (vrf_irt)
+ install_uninstall_route_in_vrfs(
+ bgp, afi, safi, evp, pi, vrf_irt->vrfs,
+ import);
+
+ /* Also check for non-exact match.
+ * In this, we mask out the AS and
+ * only check on the local-admin sub-field.
+ * This is to facilitate using
+ * VNI as the RT for EBGP peering too.
+ */
+ irt = NULL;
+ vrf_irt = NULL;
+ if (type == ECOMMUNITY_ENCODE_AS
+ || type == ECOMMUNITY_ENCODE_AS4
+ || type == ECOMMUNITY_ENCODE_IP) {
+ memcpy(&eval_tmp, eval, ecom->unit_size);
+ mask_ecom_global_admin(&eval_tmp, eval);
+ if (in_vni_rt)
+ irt = lookup_import_rt(bgp, &eval_tmp);
+ if (in_vrf_rt)
+ vrf_irt =
+ lookup_vrf_import_rt(&eval_tmp);
+ }
+
+ if (irt)
+ install_uninstall_route_in_vnis(
+ bgp, afi, safi, evp, pi, irt->vnis,
+ import);
+ if (vrf_irt)
+ install_uninstall_route_in_vrfs(
+ bgp, afi, safi, evp, pi, vrf_irt->vrfs,
+ import);
+ }
+
+ /* es route is imported into the es table */
+ if (evp->prefix.route_type == BGP_EVPN_ES_ROUTE) {
+
+ /* we will match based on the entire esi to avoid
+ * import of an es route for esi2 into esi1
+ */
+ es = bgp_evpn_es_find(&evp->prefix.es_addr.esi);
+ if (es && bgp_evpn_is_es_local(es))
+ bgp_evpn_es_route_install_uninstall(
+ bgp, es, afi, safi, evp, pi, import);
+ }
+ }
+
+ return 0;
+}
+
+/*
+ * Install or uninstall route for appropriate VNIs/ESIs.
+ */
+static int install_uninstall_evpn_route(struct bgp *bgp, afi_t afi, safi_t safi,
+ const struct prefix *p,
+ struct bgp_path_info *pi, int import)
+{
+ return bgp_evpn_install_uninstall_table(bgp, afi, safi, p, pi, import,
+ true, true);
+}
+
+void bgp_evpn_import_type2_route(struct bgp_path_info *pi, int import)
+{
+ struct bgp *bgp_evpn;
+
+ bgp_evpn = bgp_get_evpn();
+ if (!bgp_evpn)
+ return;
+
+ install_uninstall_evpn_route(bgp_evpn, AFI_L2VPN, SAFI_EVPN,
+ &pi->net->p, pi, import);
+}
+
+/*
+ * delete and withdraw all ipv4 and ipv6 routes in the vrf table as type-5
+ * routes
+ */
+static void delete_withdraw_vrf_routes(struct bgp *bgp_vrf)
+{
+ /* Delete ipv4 default route and withdraw from peers */
+ if (evpn_default_originate_set(bgp_vrf, AFI_IP, SAFI_UNICAST))
+ bgp_evpn_install_uninstall_default_route(bgp_vrf, AFI_IP,
+ SAFI_UNICAST, false);
+
+ /* delete all ipv4 routes and withdraw from peers */
+ if (advertise_type5_routes(bgp_vrf, AFI_IP))
+ bgp_evpn_withdraw_type5_routes(bgp_vrf, AFI_IP, SAFI_UNICAST);
+
+ /* Delete ipv6 default route and withdraw from peers */
+ if (evpn_default_originate_set(bgp_vrf, AFI_IP6, SAFI_UNICAST))
+ bgp_evpn_install_uninstall_default_route(bgp_vrf, AFI_IP6,
+ SAFI_UNICAST, false);
+
+ /* delete all ipv6 routes and withdraw from peers */
+ if (advertise_type5_routes(bgp_vrf, AFI_IP6))
+ bgp_evpn_withdraw_type5_routes(bgp_vrf, AFI_IP6, SAFI_UNICAST);
+}
+
+/*
+ * update and advertise all ipv4 and ipv6 routes in thr vrf table as type-5
+ * routes
+ */
+void update_advertise_vrf_routes(struct bgp *bgp_vrf)
+{
+ struct bgp *bgp_evpn = NULL; /* EVPN bgp instance */
+
+ bgp_evpn = bgp_get_evpn();
+ if (!bgp_evpn)
+ return;
+
+ /* update all ipv4 routes */
+ if (advertise_type5_routes(bgp_vrf, AFI_IP))
+ bgp_evpn_advertise_type5_routes(bgp_vrf, AFI_IP, SAFI_UNICAST);
+
+ /* update ipv4 default route and withdraw from peers */
+ if (evpn_default_originate_set(bgp_vrf, AFI_IP, SAFI_UNICAST))
+ bgp_evpn_install_uninstall_default_route(bgp_vrf, AFI_IP,
+ SAFI_UNICAST, true);
+
+ /* update all ipv6 routes */
+ if (advertise_type5_routes(bgp_vrf, AFI_IP6))
+ bgp_evpn_advertise_type5_routes(bgp_vrf, AFI_IP6, SAFI_UNICAST);
+
+ /* update ipv6 default route and withdraw from peers */
+ if (evpn_default_originate_set(bgp_vrf, AFI_IP6, SAFI_UNICAST))
+ bgp_evpn_install_uninstall_default_route(bgp_vrf, AFI_IP6,
+ SAFI_UNICAST, true);
+
+}
+
+/*
+ * update and advertise local routes for a VRF as type-5 routes.
+ * This is invoked upon RD change for a VRF. Note taht the processing is only
+ * done in the global route table using the routes which already exist in the
+ * VRF routing table
+ */
+static void update_router_id_vrf(struct bgp *bgp_vrf)
+{
+ /* skip if the RD is configured */
+ if (is_vrf_rd_configured(bgp_vrf))
+ return;
+
+ /* derive the RD for the VRF based on new router-id */
+ bgp_evpn_derive_auto_rd_for_vrf(bgp_vrf);
+
+ /* update advertise ipv4|ipv6 routes as type-5 routes */
+ update_advertise_vrf_routes(bgp_vrf);
+}
+
+/*
+ * Delete and withdraw all type-5 routes for the RD corresponding to VRF.
+ * This is invoked upon VRF RD change. The processing is done only from global
+ * table.
+ */
+static void withdraw_router_id_vrf(struct bgp *bgp_vrf)
+{
+ /* skip if the RD is configured */
+ if (is_vrf_rd_configured(bgp_vrf))
+ return;
+
+ /* delete/withdraw ipv4|ipv6 routes as type-5 routes */
+ delete_withdraw_vrf_routes(bgp_vrf);
+}
+
+/*
+ * Update and advertise local routes for a VNI. Invoked upon router-id
+ * change. Note that the processing is done only on the global route table
+ * using routes that already exist in the per-VNI table.
+ */
+static int update_advertise_vni_routes(struct bgp *bgp, struct bgpevpn *vpn)
+{
+ struct prefix_evpn p;
+ struct bgp_dest *dest, *global_dest;
+ struct bgp_path_info *pi, *global_pi;
+ struct attr *attr;
+ afi_t afi = AFI_L2VPN;
+ safi_t safi = SAFI_EVPN;
+
+ /* Locate type-3 route for VNI in the per-VNI table and use its
+ * attributes to create and advertise the type-3 route for this VNI
+ * in the global table.
+ *
+ * RT-3 only if doing head-end replication
+ */
+ if (bgp_evpn_vni_flood_mode_get(bgp, vpn)
+ == VXLAN_FLOOD_HEAD_END_REPL) {
+ build_evpn_type3_prefix(&p, vpn->originator_ip);
+ dest = bgp_node_lookup(vpn->route_table, (struct prefix *)&p);
+ if (!dest) /* unexpected */
+ return 0;
+ 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) {
+ bgp_dest_unlock_node(dest);
+ return 0;
+ }
+ attr = pi->attr;
+
+ global_dest = bgp_global_evpn_node_get(bgp->rib[afi][safi],
+ afi, safi, &p, &vpn->prd);
+ update_evpn_route_entry(bgp, vpn, afi, safi, global_dest, attr,
+ 1, &pi, 0, mac_mobility_seqnum(attr),
+ false /* setup_sync */, NULL /* old_is_sync */);
+
+ /* Schedule for processing and unlock node. */
+ bgp_process(bgp, global_dest, afi, safi);
+ bgp_dest_unlock_node(global_dest);
+ }
+
+ /* Now, walk this VNI's route table and use the route and its attribute
+ * to create and schedule route in global table.
+ */
+ for (dest = bgp_table_top(vpn->route_table); dest;
+ dest = bgp_route_next(dest)) {
+ const struct prefix_evpn *evp =
+ (const struct prefix_evpn *)bgp_dest_get_prefix(dest);
+
+ /*
+ * We have already processed type-3 routes.
+ * Process only type-1 and type-2 routes here.
+ */
+ if (evp->prefix.route_type != BGP_EVPN_MAC_IP_ROUTE
+ && evp->prefix.route_type != BGP_EVPN_AD_ROUTE)
+ continue;
+
+ 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)
+ continue;
+
+ /* Create route in global routing table using this route entry's
+ * attribute.
+ */
+ attr = pi->attr;
+ global_dest = bgp_global_evpn_node_get(bgp->rib[afi][safi], afi, safi,
+ evp, &vpn->prd);
+ assert(global_dest);
+
+ if (evp->prefix.route_type == BGP_EVPN_MAC_IP_ROUTE) {
+ /* Type-2 route */
+ update_evpn_route_entry(
+ bgp, vpn, afi, safi, global_dest, attr, 1,
+ &global_pi, 0, mac_mobility_seqnum(attr),
+ false /* setup_sync */, NULL /* old_is_sync */);
+ } else {
+ /* Type-1 route */
+ struct bgp_evpn_es *es;
+ int route_changed = 0;
+
+ es = bgp_evpn_es_find(&evp->prefix.ead_addr.esi);
+ bgp_evpn_mh_route_update(bgp, es, vpn, afi, safi,
+ global_dest, attr, &global_pi,
+ &route_changed);
+ }
+
+ /* Schedule for processing and unlock node. */
+ bgp_process(bgp, global_dest, afi, safi);
+ bgp_dest_unlock_node(global_dest);
+ }
+
+ return 0;
+}
+
+/*
+ * Delete (and withdraw) local routes for a VNI - only from the global
+ * table. Invoked upon router-id change.
+ */
+static int delete_withdraw_vni_routes(struct bgp *bgp, struct bgpevpn *vpn)
+{
+ struct prefix_evpn p;
+ struct bgp_dest *global_dest;
+ struct bgp_path_info *pi;
+ afi_t afi = AFI_L2VPN;
+ safi_t safi = SAFI_EVPN;
+
+ /* Delete and withdraw locally learnt type-2 routes (MACIP)
+ * for this VNI - from the global table.
+ */
+ delete_global_type2_routes(bgp, vpn);
+
+ /* Remove type-3 route for this VNI from global table. */
+ build_evpn_type3_prefix(&p, vpn->originator_ip);
+ global_dest = bgp_global_evpn_node_lookup(bgp->rib[afi][safi], afi, safi,
+ (const struct prefix_evpn *)&p, &vpn->prd);
+ if (global_dest) {
+ /* Delete route entry in the global EVPN table. */
+ delete_evpn_route_entry(bgp, afi, safi, global_dest, &pi);
+
+ /* Schedule for processing - withdraws to peers happen from
+ * this table.
+ */
+ if (pi)
+ bgp_process(bgp, global_dest, afi, safi);
+ bgp_dest_unlock_node(global_dest);
+ }
+
+
+ delete_global_ead_evi_routes(bgp, vpn);
+ return 0;
+}
+
+/*
+ * Handle router-id change. Update and advertise local routes corresponding
+ * to this VNI from peers. Note that this is invoked after updating the
+ * router-id. The routes in the per-VNI table are used to create routes in
+ * the global table and schedule them.
+ */
+static void update_router_id_vni(struct hash_bucket *bucket, struct bgp *bgp)
+{
+ struct bgpevpn *vpn = (struct bgpevpn *)bucket->data;
+
+ /* Skip VNIs with configured RD. */
+ if (is_rd_configured(vpn))
+ return;
+
+ bgp_evpn_derive_auto_rd(bgp, vpn);
+ update_advertise_vni_routes(bgp, vpn);
+}
+
+/*
+ * Handle router-id change. Delete and withdraw local routes corresponding
+ * to this VNI from peers. Note that this is invoked prior to updating
+ * the router-id and is done only on the global route table, the routes
+ * are needed in the per-VNI table to re-advertise with new router id.
+ */
+static void withdraw_router_id_vni(struct hash_bucket *bucket, struct bgp *bgp)
+{
+ struct bgpevpn *vpn = (struct bgpevpn *)bucket->data;
+
+ /* Skip VNIs with configured RD. */
+ if (is_rd_configured(vpn))
+ return;
+
+ delete_withdraw_vni_routes(bgp, vpn);
+}
+
+/*
+ * Create RT-3 for a VNI and schedule for processing and advertisement.
+ * This is invoked upon flooding mode changing to head-end replication.
+ */
+static void create_advertise_type3(struct hash_bucket *bucket, void *data)
+{
+ struct bgpevpn *vpn = bucket->data;
+ struct bgp *bgp = data;
+ struct prefix_evpn p;
+
+ if (!vpn || !is_vni_live(vpn) ||
+ bgp_evpn_vni_flood_mode_get(bgp, vpn)
+ != VXLAN_FLOOD_HEAD_END_REPL)
+ return;
+
+ build_evpn_type3_prefix(&p, vpn->originator_ip);
+ if (update_evpn_route(bgp, vpn, &p, 0, 0, NULL))
+ flog_err(EC_BGP_EVPN_ROUTE_CREATE,
+ "Type3 route creation failure for VNI %u", vpn->vni);
+}
+
+/*
+ * Delete RT-3 for a VNI and schedule for processing and withdrawal.
+ * This is invoked upon flooding mode changing to drop BUM packets.
+ */
+static void delete_withdraw_type3(struct hash_bucket *bucket, void *data)
+{
+ struct bgpevpn *vpn = bucket->data;
+ struct bgp *bgp = data;
+ struct prefix_evpn p;
+
+ if (!vpn || !is_vni_live(vpn))
+ return;
+
+ build_evpn_type3_prefix(&p, vpn->originator_ip);
+ delete_evpn_route(bgp, vpn, &p);
+}
+
+/*
+ * Process received EVPN type-2 route (advertise or withdraw).
+ */
+static int process_type2_route(struct peer *peer, afi_t afi, safi_t safi,
+ struct attr *attr, uint8_t *pfx, int psize,
+ uint32_t addpath_id)
+{
+ struct prefix_rd prd;
+ struct prefix_evpn p = {};
+ struct bgp_route_evpn evpn = {};
+ uint8_t ipaddr_len;
+ uint8_t macaddr_len;
+ /* holds the VNI(s) as in packet */
+ mpls_label_t label[BGP_MAX_LABELS] = {};
+ uint32_t num_labels = 0;
+ uint32_t eth_tag;
+ int ret;
+
+ /* Type-2 route should be either 33, 37 or 49 bytes or an
+ * additional 3 bytes if there is a second label (VNI):
+ * RD (8), ESI (10), Eth Tag (4), MAC Addr Len (1),
+ * MAC Addr (6), IP len (1), IP (0, 4 or 16),
+ * MPLS Lbl1 (3), MPLS Lbl2 (0 or 3)
+ */
+ if (psize != 33 && psize != 37 && psize != 49 && psize != 36
+ && psize != 40 && psize != 52) {
+ flog_err(EC_BGP_EVPN_ROUTE_INVALID,
+ "%u:%s - Rx EVPN Type-2 NLRI with invalid length %d",
+ peer->bgp->vrf_id, peer->host, psize);
+ return -1;
+ }
+
+ struct stream *pkt = stream_new(psize);
+ stream_put(pkt, pfx, psize);
+
+ /* Make prefix_rd */
+ prd.family = AF_UNSPEC;
+ prd.prefixlen = 64;
+
+ STREAM_GET(&prd.val, pkt, 8);
+
+ /* Make EVPN prefix. */
+ p.family = AF_EVPN;
+ p.prefixlen = EVPN_ROUTE_PREFIXLEN;
+ p.prefix.route_type = BGP_EVPN_MAC_IP_ROUTE;
+
+ /* Copy Ethernet Seg Identifier */
+ if (attr) {
+ STREAM_GET(&attr->esi, pkt, sizeof(esi_t));
+
+ if (bgp_evpn_is_esi_local_and_non_bypass(&attr->esi))
+ attr->es_flags |= ATTR_ES_IS_LOCAL;
+ else
+ attr->es_flags &= ~ATTR_ES_IS_LOCAL;
+ } else {
+ STREAM_FORWARD_GETP(pkt, sizeof(esi_t));
+ }
+
+ /* Copy Ethernet Tag */
+ STREAM_GET(&eth_tag, pkt, 4);
+ p.prefix.macip_addr.eth_tag = ntohl(eth_tag);
+
+ /* Get the MAC Addr len */
+ STREAM_GETC(pkt, macaddr_len);
+
+ /* Get the MAC Addr */
+ if (macaddr_len == (ETH_ALEN * 8)) {
+ STREAM_GET(&p.prefix.macip_addr.mac.octet, pkt, ETH_ALEN);
+ } else {
+ flog_err(
+ EC_BGP_EVPN_ROUTE_INVALID,
+ "%u:%s - Rx EVPN Type-2 NLRI with unsupported MAC address length %d",
+ peer->bgp->vrf_id, peer->host, macaddr_len);
+ goto fail;
+ }
+
+
+ /* Get the IP. */
+ STREAM_GETC(pkt, ipaddr_len);
+
+ if (ipaddr_len != 0 && ipaddr_len != IPV4_MAX_BITLEN
+ && ipaddr_len != IPV6_MAX_BITLEN) {
+ flog_err(
+ EC_BGP_EVPN_ROUTE_INVALID,
+ "%u:%s - Rx EVPN Type-2 NLRI with unsupported IP address length %d",
+ peer->bgp->vrf_id, peer->host, ipaddr_len);
+ goto fail;
+ }
+
+ if (ipaddr_len) {
+ ipaddr_len /= 8; /* Convert to bytes. */
+ p.prefix.macip_addr.ip.ipa_type = (ipaddr_len == IPV4_MAX_BYTELEN)
+ ? IPADDR_V4
+ : IPADDR_V6;
+ STREAM_GET(&p.prefix.macip_addr.ip.ip.addr, pkt, ipaddr_len);
+ }
+
+ /* Get the VNI(s). Stored as bytes here. */
+ STREAM_GET(&label[0], pkt, BGP_LABEL_BYTES);
+ num_labels++;
+
+ /* Do we have a second VNI? */
+ if (STREAM_READABLE(pkt)) {
+ num_labels++;
+ STREAM_GET(&label[1], pkt, BGP_LABEL_BYTES);
+ }
+
+ /* Process the route. */
+ if (attr)
+ ret = bgp_update(peer, (struct prefix *)&p, addpath_id, attr,
+ afi, safi, ZEBRA_ROUTE_BGP, BGP_ROUTE_NORMAL,
+ &prd, &label[0], num_labels, 0, &evpn);
+ else
+ ret = bgp_withdraw(peer, (struct prefix *)&p, addpath_id, attr,
+ afi, safi, ZEBRA_ROUTE_BGP, BGP_ROUTE_NORMAL,
+ &prd, &label[0], num_labels, &evpn);
+ goto done;
+
+fail:
+stream_failure:
+ flog_err(EC_BGP_EVPN_ROUTE_INVALID,
+ "%u:%s - Rx EVPN Type-2 NLRI - corrupt, discarding",
+ peer->bgp->vrf_id, peer->host);
+ ret = -1;
+done:
+ stream_free(pkt);
+ return ret;
+}
+
+/*
+ * Process received EVPN type-3 route (advertise or withdraw).
+ */
+static int process_type3_route(struct peer *peer, afi_t afi, safi_t safi,
+ struct attr *attr, uint8_t *pfx, int psize,
+ uint32_t addpath_id)
+{
+ struct prefix_rd prd;
+ struct prefix_evpn p;
+ uint8_t ipaddr_len;
+ uint32_t eth_tag;
+ int ret;
+
+ /* Type-3 route should be either 17 or 29 bytes: RD (8), Eth Tag (4),
+ * IP len (1) and IP (4 or 16).
+ */
+ if (psize != 17 && psize != 29) {
+ flog_err(EC_BGP_EVPN_ROUTE_INVALID,
+ "%u:%s - Rx EVPN Type-3 NLRI with invalid length %d",
+ peer->bgp->vrf_id, peer->host, psize);
+ return -1;
+ }
+
+ /* If PMSI is present, log if it is anything other than IR.
+ * Note: We just simply ignore the values as it is not clear if
+ * doing anything else is better.
+ */
+ if (attr &&
+ (attr->flag & ATTR_FLAG_BIT(BGP_ATTR_PMSI_TUNNEL))) {
+ enum pta_type pmsi_tnl_type = bgp_attr_get_pmsi_tnl_type(attr);
+
+ if (pmsi_tnl_type != PMSI_TNLTYPE_INGR_REPL
+ && pmsi_tnl_type != PMSI_TNLTYPE_PIM_SM) {
+ flog_warn(
+ EC_BGP_EVPN_PMSI_PRESENT,
+ "%u:%s - Rx EVPN Type-3 NLRI with unsupported PTA %d",
+ peer->bgp->vrf_id, peer->host, pmsi_tnl_type);
+ }
+ }
+
+ /* Make prefix_rd */
+ prd.family = AF_UNSPEC;
+ prd.prefixlen = 64;
+ memcpy(&prd.val, pfx, 8);
+ pfx += 8;
+
+ /* Make EVPN prefix. */
+ memset(&p, 0, sizeof(p));
+ p.family = AF_EVPN;
+ p.prefixlen = EVPN_ROUTE_PREFIXLEN;
+ p.prefix.route_type = BGP_EVPN_IMET_ROUTE;
+
+ /* Copy Ethernet Tag */
+ memcpy(&eth_tag, pfx, 4);
+ p.prefix.imet_addr.eth_tag = ntohl(eth_tag);
+ pfx += 4;
+
+ /* Get the IP. */
+ ipaddr_len = *pfx++;
+ if (ipaddr_len == IPV4_MAX_BITLEN) {
+ p.prefix.imet_addr.ip.ipa_type = IPADDR_V4;
+ memcpy(&p.prefix.imet_addr.ip.ip.addr, pfx, IPV4_MAX_BYTELEN);
+ } else {
+ flog_err(
+ EC_BGP_EVPN_ROUTE_INVALID,
+ "%u:%s - Rx EVPN Type-3 NLRI with unsupported IP address length %d",
+ peer->bgp->vrf_id, peer->host, ipaddr_len);
+ return -1;
+ }
+
+ /* Process the route. */
+ if (attr)
+ ret = bgp_update(peer, (struct prefix *)&p, addpath_id, attr,
+ afi, safi, ZEBRA_ROUTE_BGP, BGP_ROUTE_NORMAL,
+ &prd, NULL, 0, 0, NULL);
+ else
+ ret = bgp_withdraw(peer, (struct prefix *)&p, addpath_id, attr,
+ afi, safi, ZEBRA_ROUTE_BGP, BGP_ROUTE_NORMAL,
+ &prd, NULL, 0, NULL);
+ return ret;
+}
+
+/*
+ * Process received EVPN type-5 route (advertise or withdraw).
+ */
+static int process_type5_route(struct peer *peer, afi_t afi, safi_t safi,
+ struct attr *attr, uint8_t *pfx, int psize,
+ uint32_t addpath_id)
+{
+ struct prefix_rd prd;
+ struct prefix_evpn p;
+ struct bgp_route_evpn evpn;
+ uint8_t ippfx_len;
+ uint32_t eth_tag;
+ mpls_label_t label; /* holds the VNI as in the packet */
+ int ret;
+ bool is_valid_update = true;
+
+ /* Type-5 route should be 34 or 58 bytes:
+ * RD (8), ESI (10), Eth Tag (4), IP len (1), IP (4 or 16),
+ * GW (4 or 16) and VNI (3).
+ * Note that the IP and GW should both be IPv4 or both IPv6.
+ */
+ if (psize != 34 && psize != 58) {
+ flog_err(EC_BGP_EVPN_ROUTE_INVALID,
+ "%u:%s - Rx EVPN Type-5 NLRI with invalid length %d",
+ peer->bgp->vrf_id, peer->host, psize);
+ return -1;
+ }
+
+ /* Make prefix_rd */
+ prd.family = AF_UNSPEC;
+ prd.prefixlen = 64;
+ memcpy(&prd.val, pfx, 8);
+ pfx += 8;
+
+ /* Make EVPN prefix. */
+ memset(&p, 0, sizeof(p));
+ p.family = AF_EVPN;
+ p.prefixlen = EVPN_ROUTE_PREFIXLEN;
+ p.prefix.route_type = BGP_EVPN_IP_PREFIX_ROUTE;
+
+ /* Additional information outside of prefix - ESI and GW IP */
+ memset(&evpn, 0, sizeof(evpn));
+
+ /* Fetch ESI overlay index */
+ if (attr)
+ memcpy(&evpn.eth_s_id, pfx, sizeof(esi_t));
+ pfx += ESI_BYTES;
+
+ /* Fetch Ethernet Tag. */
+ memcpy(&eth_tag, pfx, 4);
+ p.prefix.prefix_addr.eth_tag = ntohl(eth_tag);
+ pfx += 4;
+
+ /* Fetch IP prefix length. */
+ ippfx_len = *pfx++;
+ if (ippfx_len > IPV6_MAX_BITLEN) {
+ flog_err(
+ EC_BGP_EVPN_ROUTE_INVALID,
+ "%u:%s - Rx EVPN Type-5 NLRI with invalid IP Prefix length %d",
+ peer->bgp->vrf_id, peer->host, ippfx_len);
+ return -1;
+ }
+ p.prefix.prefix_addr.ip_prefix_length = ippfx_len;
+
+ /* Determine IPv4 or IPv6 prefix */
+ /* Since the address and GW are from the same family, this just becomes
+ * a simple check on the total size.
+ */
+ if (psize == 34) {
+ SET_IPADDR_V4(&p.prefix.prefix_addr.ip);
+ memcpy(&p.prefix.prefix_addr.ip.ipaddr_v4, pfx, 4);
+ pfx += 4;
+ SET_IPADDR_V4(&evpn.gw_ip);
+ memcpy(&evpn.gw_ip.ipaddr_v4, pfx, 4);
+ pfx += 4;
+ } else {
+ SET_IPADDR_V6(&p.prefix.prefix_addr.ip);
+ memcpy(&p.prefix.prefix_addr.ip.ipaddr_v6, pfx,
+ IPV6_MAX_BYTELEN);
+ pfx += IPV6_MAX_BYTELEN;
+ SET_IPADDR_V6(&evpn.gw_ip);
+ memcpy(&evpn.gw_ip.ipaddr_v6, pfx, IPV6_MAX_BYTELEN);
+ pfx += IPV6_MAX_BYTELEN;
+ }
+
+ /* Get the VNI (in MPLS label field). Stored as bytes here. */
+ memset(&label, 0, sizeof(label));
+ memcpy(&label, pfx, BGP_LABEL_BYTES);
+
+ /*
+ * If in future, we are required to access additional fields,
+ * we MUST increment pfx by BGP_LABEL_BYTES in before reading the next
+ * field
+ */
+
+ /*
+ * An update containing a non-zero gateway IP and a non-zero ESI
+ * at the same time is should be treated as withdraw
+ */
+ if (bgp_evpn_is_esi_valid(&evpn.eth_s_id) &&
+ !ipaddr_is_zero(&evpn.gw_ip)) {
+ flog_err(EC_BGP_EVPN_ROUTE_INVALID,
+ "%s - Rx EVPN Type-5 ESI and gateway-IP both non-zero.",
+ peer->host);
+ is_valid_update = false;
+ } else if (bgp_evpn_is_esi_valid(&evpn.eth_s_id))
+ evpn.type = OVERLAY_INDEX_ESI;
+ else if (!ipaddr_is_zero(&evpn.gw_ip))
+ evpn.type = OVERLAY_INDEX_GATEWAY_IP;
+ if (attr) {
+ if (is_zero_mac(&attr->rmac) &&
+ !bgp_evpn_is_esi_valid(&evpn.eth_s_id) &&
+ ipaddr_is_zero(&evpn.gw_ip) && label == 0) {
+ flog_err(EC_BGP_EVPN_ROUTE_INVALID,
+ "%s - Rx EVPN Type-5 ESI, gateway-IP, RMAC and label all zero",
+ peer->host);
+ is_valid_update = false;
+ }
+
+ if (is_mcast_mac(&attr->rmac) || is_bcast_mac(&attr->rmac))
+ is_valid_update = false;
+ }
+
+ /* Process the route. */
+ if (attr && is_valid_update)
+ ret = bgp_update(peer, (struct prefix *)&p, addpath_id, attr,
+ afi, safi, ZEBRA_ROUTE_BGP, BGP_ROUTE_NORMAL,
+ &prd, &label, 1, 0, &evpn);
+ else {
+ if (!is_valid_update) {
+ char attr_str[BUFSIZ] = {0};
+
+ bgp_dump_attr(attr, attr_str, BUFSIZ);
+ zlog_warn(
+ "Invalid update from peer %s vrf %u prefix %pFX attr %s - treat as withdraw",
+ peer->hostname, peer->bgp->vrf_id, &p,
+ attr_str);
+ }
+ ret = bgp_withdraw(peer, (struct prefix *)&p, addpath_id, attr,
+ afi, safi, ZEBRA_ROUTE_BGP, BGP_ROUTE_NORMAL,
+ &prd, &label, 1, &evpn);
+ }
+
+ return ret;
+}
+
+static void evpn_mpattr_encode_type5(struct stream *s, const struct prefix *p,
+ const struct prefix_rd *prd,
+ mpls_label_t *label, uint32_t num_labels,
+ struct attr *attr)
+{
+ int len;
+ char temp[16];
+ const struct evpn_addr *p_evpn_p;
+
+ memset(&temp, 0, sizeof(temp));
+ if (p->family != AF_EVPN)
+ return;
+ p_evpn_p = &(p->u.prefix_evpn);
+
+ /* len denites the total len of IP and GW-IP in the route
+ IP and GW-IP have to be both ipv4 or ipv6
+ */
+ if (IS_IPADDR_V4(&p_evpn_p->prefix_addr.ip))
+ len = 8; /* IP and GWIP are both ipv4 */
+ else
+ len = 32; /* IP and GWIP are both ipv6 */
+ /* Prefix contains RD, ESI, EthTag, IP length, IP, GWIP and VNI */
+ stream_putc(s, 8 + 10 + 4 + 1 + len + 3);
+ stream_put(s, prd->val, 8);
+ if (attr && attr->evpn_overlay.type == OVERLAY_INDEX_ESI)
+ stream_put(s, &attr->esi, sizeof(esi_t));
+ else
+ stream_put(s, 0, sizeof(esi_t));
+ stream_putl(s, p_evpn_p->prefix_addr.eth_tag);
+ stream_putc(s, p_evpn_p->prefix_addr.ip_prefix_length);
+ if (IS_IPADDR_V4(&p_evpn_p->prefix_addr.ip))
+ stream_put_ipv4(s, p_evpn_p->prefix_addr.ip.ipaddr_v4.s_addr);
+ else
+ stream_put(s, &p_evpn_p->prefix_addr.ip.ipaddr_v6, 16);
+ if (attr && attr->evpn_overlay.type == OVERLAY_INDEX_GATEWAY_IP) {
+ const struct bgp_route_evpn *evpn_overlay =
+ bgp_attr_get_evpn_overlay(attr);
+
+ if (IS_IPADDR_V4(&p_evpn_p->prefix_addr.ip))
+ stream_put_ipv4(s,
+ evpn_overlay->gw_ip.ipaddr_v4.s_addr);
+ else
+ stream_put(s, &(evpn_overlay->gw_ip.ipaddr_v6), 16);
+ } else {
+ if (IS_IPADDR_V4(&p_evpn_p->prefix_addr.ip))
+ stream_put_ipv4(s, 0);
+ else
+ stream_put(s, &temp, 16);
+ }
+
+ if (num_labels)
+ stream_put(s, label, 3);
+ else
+ stream_put3(s, 0);
+}
+
+/*
+ * Cleanup specific VNI upon EVPN (advertise-all-vni) being disabled.
+ */
+static void cleanup_vni_on_disable(struct hash_bucket *bucket, struct bgp *bgp)
+{
+ struct bgpevpn *vpn = (struct bgpevpn *)bucket->data;
+
+ /* Remove EVPN routes and schedule for processing. */
+ delete_routes_for_vni(bgp, vpn);
+
+ /* Clear "live" flag and see if hash needs to be freed. */
+ UNSET_FLAG(vpn->flags, VNI_FLAG_LIVE);
+ if (!is_vni_configured(vpn))
+ bgp_evpn_free(bgp, vpn);
+}
+
+/*
+ * Free a VNI entry; iterator function called during cleanup.
+ */
+static void free_vni_entry(struct hash_bucket *bucket, struct bgp *bgp)
+{
+ struct bgpevpn *vpn = (struct bgpevpn *)bucket->data;
+
+ delete_all_vni_routes(bgp, vpn);
+ bgp_evpn_free(bgp, vpn);
+}
+
+/*
+ * Derive AUTO import RT for BGP VRF - L3VNI
+ */
+static void evpn_auto_rt_import_add_for_vrf(struct bgp *bgp_vrf)
+{
+ struct bgp *bgp_evpn = NULL;
+
+ form_auto_rt(bgp_vrf, bgp_vrf->l3vni, bgp_vrf->vrf_import_rtl);
+ UNSET_FLAG(bgp_vrf->vrf_flags, BGP_VRF_IMPORT_RT_CFGD);
+
+ /* Map RT to VRF */
+ bgp_evpn = bgp_get_evpn();
+ if (!bgp_evpn)
+ return;
+ bgp_evpn_map_vrf_to_its_rts(bgp_vrf);
+}
+
+/*
+ * Delete AUTO import RT from BGP VRF - L3VNI
+ */
+static void evpn_auto_rt_import_delete_for_vrf(struct bgp *bgp_vrf)
+{
+ evpn_rt_delete_auto(bgp_vrf, bgp_vrf->l3vni, bgp_vrf->vrf_import_rtl);
+}
+
+/*
+ * Derive AUTO export RT for BGP VRF - L3VNI
+ */
+static void evpn_auto_rt_export_add_for_vrf(struct bgp *bgp_vrf)
+{
+ UNSET_FLAG(bgp_vrf->vrf_flags, BGP_VRF_EXPORT_RT_CFGD);
+ form_auto_rt(bgp_vrf, bgp_vrf->l3vni, bgp_vrf->vrf_export_rtl);
+}
+
+/*
+ * Delete AUTO export RT from BGP VRF - L3VNI
+ */
+static void evpn_auto_rt_export_delete_for_vrf(struct bgp *bgp_vrf)
+{
+ evpn_rt_delete_auto(bgp_vrf, bgp_vrf->l3vni, bgp_vrf->vrf_export_rtl);
+}
+
+static void bgp_evpn_handle_export_rt_change_for_vrf(struct bgp *bgp_vrf)
+{
+ struct bgp *bgp_evpn = NULL;
+ struct listnode *node = NULL;
+ struct bgpevpn *vpn = NULL;
+
+ bgp_evpn = bgp_get_evpn();
+ if (!bgp_evpn)
+ return;
+
+ /* update all type-5 routes */
+ update_advertise_vrf_routes(bgp_vrf);
+
+ /* update all type-2 routes */
+ for (ALL_LIST_ELEMENTS_RO(bgp_vrf->l2vnis, node, vpn))
+ update_routes_for_vni(bgp_evpn, vpn);
+}
+
+/*
+ * Handle autort change for a given VNI.
+ */
+static void update_autort_vni(struct hash_bucket *bucket, struct bgp *bgp)
+{
+ struct bgpevpn *vpn = bucket->data;
+
+ if (!is_import_rt_configured(vpn)) {
+ if (is_vni_live(vpn))
+ bgp_evpn_uninstall_routes(bgp, vpn);
+ bgp_evpn_unmap_vni_from_its_rts(bgp, vpn);
+ list_delete_all_node(vpn->import_rtl);
+ bgp_evpn_derive_auto_rt_import(bgp, vpn);
+ if (is_vni_live(vpn))
+ bgp_evpn_install_routes(bgp, vpn);
+ }
+ if (!is_export_rt_configured(vpn)) {
+ list_delete_all_node(vpn->export_rtl);
+ bgp_evpn_derive_auto_rt_export(bgp, vpn);
+ if (is_vni_live(vpn))
+ bgp_evpn_handle_export_rt_change(bgp, vpn);
+ }
+}
+
+/*
+ * Handle autort change for L3VNI.
+ */
+static void update_autort_l3vni(struct bgp *bgp)
+{
+ if ((CHECK_FLAG(bgp->vrf_flags, BGP_VRF_IMPORT_RT_CFGD))
+ && (CHECK_FLAG(bgp->vrf_flags, BGP_VRF_EXPORT_RT_CFGD)))
+ return;
+
+ if (!CHECK_FLAG(bgp->vrf_flags, BGP_VRF_IMPORT_RT_CFGD)) {
+ if (is_l3vni_live(bgp))
+ uninstall_routes_for_vrf(bgp);
+
+ /* Cleanup the RT to VRF mapping */
+ bgp_evpn_unmap_vrf_from_its_rts(bgp);
+
+ /* Remove auto generated RT */
+ evpn_auto_rt_import_delete_for_vrf(bgp);
+
+ list_delete_all_node(bgp->vrf_import_rtl);
+
+ /* Map auto derive or configured RTs */
+ evpn_auto_rt_import_add_for_vrf(bgp);
+ }
+
+ if (!CHECK_FLAG(bgp->vrf_flags, BGP_VRF_EXPORT_RT_CFGD)) {
+ list_delete_all_node(bgp->vrf_export_rtl);
+
+ evpn_auto_rt_export_delete_for_vrf(bgp);
+
+ evpn_auto_rt_export_add_for_vrf(bgp);
+
+ if (is_l3vni_live(bgp))
+ bgp_evpn_map_vrf_to_its_rts(bgp);
+ }
+
+ if (!is_l3vni_live(bgp))
+ return;
+
+ /* advertise type-5 routes if needed */
+ update_advertise_vrf_routes(bgp);
+
+ /* install all remote routes belonging to this l3vni
+ * into corresponding vrf
+ */
+ install_routes_for_vrf(bgp);
+}
+
+/*
+ * Public functions.
+ */
+
+/* withdraw type-5 route corresponding to ip prefix */
+void bgp_evpn_withdraw_type5_route(struct bgp *bgp_vrf, const struct prefix *p,
+ afi_t afi, safi_t safi)
+{
+ int ret = 0;
+ struct prefix_evpn evp;
+
+ build_type5_prefix_from_ip_prefix(&evp, p);
+ ret = delete_evpn_type5_route(bgp_vrf, &evp);
+ if (ret)
+ flog_err(
+ EC_BGP_EVPN_ROUTE_DELETE,
+ "%u failed to delete type-5 route for prefix %pFX in vrf %s",
+ bgp_vrf->vrf_id, p, vrf_id_to_name(bgp_vrf->vrf_id));
+}
+
+/* withdraw all type-5 routes for an address family */
+void bgp_evpn_withdraw_type5_routes(struct bgp *bgp_vrf, afi_t afi, safi_t safi)
+{
+ struct bgp_table *table = NULL;
+ struct bgp_dest *dest = NULL;
+ struct bgp_path_info *pi;
+
+ table = bgp_vrf->rib[afi][safi];
+ for (dest = bgp_table_top(table); dest; dest = bgp_route_next(dest)) {
+ /* Only care about "selected" routes. Also ensure that
+ * these are routes that are injectable into EVPN.
+ */
+ /* TODO: Support for AddPath for EVPN. */
+ for (pi = bgp_dest_get_bgp_path_info(dest); pi; pi = pi->next) {
+ if (CHECK_FLAG(pi->flags, BGP_PATH_SELECTED)
+ && is_route_injectable_into_evpn(pi)) {
+ bgp_evpn_withdraw_type5_route(
+ bgp_vrf, bgp_dest_get_prefix(dest), afi,
+ safi);
+ break;
+ }
+ }
+ }
+}
+
+/*
+ * evpn - enable advertisement of default g/w
+ */
+void bgp_evpn_install_uninstall_default_route(struct bgp *bgp_vrf, afi_t afi,
+ safi_t safi, bool add)
+{
+ struct prefix ip_prefix;
+
+ /* form the default prefix 0.0.0.0/0 */
+ memset(&ip_prefix, 0, sizeof(ip_prefix));
+ ip_prefix.family = afi2family(afi);
+
+ if (add) {
+ bgp_evpn_advertise_type5_route(bgp_vrf, &ip_prefix,
+ NULL, afi, safi);
+ } else {
+ bgp_evpn_withdraw_type5_route(bgp_vrf, &ip_prefix,
+ afi, safi);
+ }
+}
+
+
+/*
+ * Advertise IP prefix as type-5 route. The afi/safi and src_attr passed
+ * to this function correspond to those of the source IP prefix (best
+ * path in the case of the attr. In the case of a local prefix (when we
+ * are advertising local subnets), the src_attr will be NULL.
+ */
+void bgp_evpn_advertise_type5_route(struct bgp *bgp_vrf, const struct prefix *p,
+ struct attr *src_attr, afi_t afi,
+ safi_t safi)
+{
+ int ret = 0;
+ struct prefix_evpn evp;
+
+ build_type5_prefix_from_ip_prefix(&evp, p);
+ ret = update_evpn_type5_route(bgp_vrf, &evp, src_attr, afi, safi);
+ if (ret)
+ flog_err(EC_BGP_EVPN_ROUTE_CREATE,
+ "%u: Failed to create type-5 route for prefix %pFX",
+ bgp_vrf->vrf_id, p);
+}
+
+/* Inject all prefixes of a particular address-family (currently, IPv4 or
+ * IPv6 unicast) into EVPN as type-5 routes. This is invoked when the
+ * advertisement is enabled.
+ */
+void bgp_evpn_advertise_type5_routes(struct bgp *bgp_vrf, afi_t afi,
+ safi_t safi)
+{
+ struct bgp_table *table = NULL;
+ struct bgp_dest *dest = NULL;
+ struct bgp_path_info *pi;
+
+ table = bgp_vrf->rib[afi][safi];
+ for (dest = bgp_table_top(table); dest; dest = bgp_route_next(dest)) {
+ /* Need to identify the "selected" route entry to use its
+ * attribute. Also, ensure that the route is injectable
+ * into EVPN.
+ * TODO: Support for AddPath for EVPN.
+ */
+ for (pi = bgp_dest_get_bgp_path_info(dest); pi; pi = pi->next) {
+ if (CHECK_FLAG(pi->flags, BGP_PATH_SELECTED)
+ && is_route_injectable_into_evpn(pi)) {
+
+ /* apply the route-map */
+ if (bgp_vrf->adv_cmd_rmap[afi][safi].map) {
+ route_map_result_t ret;
+ struct bgp_path_info tmp_pi;
+ struct bgp_path_info_extra tmp_pie;
+ struct attr tmp_attr;
+
+ tmp_attr = *pi->attr;
+
+ /* Fill temp path_info */
+ prep_for_rmap_apply(&tmp_pi, &tmp_pie,
+ dest, pi, pi->peer,
+ &tmp_attr);
+
+ RESET_FLAG(tmp_attr.rmap_change_flags);
+
+ ret = route_map_apply(
+ bgp_vrf->adv_cmd_rmap[afi][safi]
+ .map,
+ bgp_dest_get_prefix(dest),
+ &tmp_pi);
+ if (ret == RMAP_DENYMATCH) {
+ bgp_attr_flush(&tmp_attr);
+ continue;
+ }
+ bgp_evpn_advertise_type5_route(
+ bgp_vrf,
+ bgp_dest_get_prefix(dest),
+ &tmp_attr, afi, safi);
+ } else
+ bgp_evpn_advertise_type5_route(
+ bgp_vrf,
+ bgp_dest_get_prefix(dest),
+ pi->attr, afi, safi);
+ break;
+ }
+ }
+ }
+}
+
+void evpn_rt_delete_auto(struct bgp *bgp, vni_t vni, struct list *rtl)
+{
+ struct listnode *node, *nnode, *node_to_del;
+ struct ecommunity *ecom, *ecom_auto;
+ struct ecommunity_val eval;
+
+ if (bgp->advertise_autort_rfc8365)
+ vni |= EVPN_AUTORT_VXLAN;
+ encode_route_target_as((bgp->as & 0xFFFF), vni, &eval);
+
+ ecom_auto = ecommunity_new();
+ ecommunity_add_val(ecom_auto, &eval, false, false);
+ node_to_del = NULL;
+
+ for (ALL_LIST_ELEMENTS(rtl, node, nnode, ecom)) {
+ if (ecommunity_match(ecom, ecom_auto)) {
+ ecommunity_free(&ecom);
+ node_to_del = node;
+ break;
+ }
+ }
+
+ if (node_to_del)
+ list_delete_node(rtl, node_to_del);
+
+ ecommunity_free(&ecom_auto);
+}
+
+void bgp_evpn_configure_import_rt_for_vrf(struct bgp *bgp_vrf,
+ struct ecommunity *ecomadd)
+{
+ /* uninstall routes from vrf */
+ if (is_l3vni_live(bgp_vrf))
+ uninstall_routes_for_vrf(bgp_vrf);
+
+ /* Cleanup the RT to VRF mapping */
+ bgp_evpn_unmap_vrf_from_its_rts(bgp_vrf);
+
+ /* Remove auto generated RT */
+ evpn_auto_rt_import_delete_for_vrf(bgp_vrf);
+
+ /* Add the newly configured RT to RT list */
+ listnode_add_sort(bgp_vrf->vrf_import_rtl, ecomadd);
+ SET_FLAG(bgp_vrf->vrf_flags, BGP_VRF_IMPORT_RT_CFGD);
+
+ /* map VRF to its RTs and install routes matching the new RTs */
+ if (is_l3vni_live(bgp_vrf)) {
+ bgp_evpn_map_vrf_to_its_rts(bgp_vrf);
+ install_routes_for_vrf(bgp_vrf);
+ }
+}
+
+void bgp_evpn_unconfigure_import_rt_for_vrf(struct bgp *bgp_vrf,
+ struct ecommunity *ecomdel)
+{
+ struct listnode *node = NULL, *nnode = NULL, *node_to_del = NULL;
+ struct ecommunity *ecom = NULL;
+
+ /* uninstall routes from vrf */
+ if (is_l3vni_live(bgp_vrf))
+ uninstall_routes_for_vrf(bgp_vrf);
+
+ /* Cleanup the RT to VRF mapping */
+ bgp_evpn_unmap_vrf_from_its_rts(bgp_vrf);
+
+ /* remove the RT from the RT list */
+ for (ALL_LIST_ELEMENTS(bgp_vrf->vrf_import_rtl, node, nnode, ecom)) {
+ if (ecommunity_match(ecom, ecomdel)) {
+ ecommunity_free(&ecom);
+ node_to_del = node;
+ break;
+ }
+ }
+
+ if (node_to_del)
+ list_delete_node(bgp_vrf->vrf_import_rtl, node_to_del);
+
+ assert(bgp_vrf->vrf_import_rtl);
+ /* fallback to auto import rt, if this was the last RT */
+ if (bgp_vrf->vrf_import_rtl && list_isempty(bgp_vrf->vrf_import_rtl)) {
+ UNSET_FLAG(bgp_vrf->vrf_flags, BGP_VRF_IMPORT_RT_CFGD);
+ if (is_l3vni_live(bgp_vrf))
+ evpn_auto_rt_import_add_for_vrf(bgp_vrf);
+ }
+
+ /* map VRFs to its RTs and install routes matching this new RT */
+ if (is_l3vni_live(bgp_vrf)) {
+ bgp_evpn_map_vrf_to_its_rts(bgp_vrf);
+ install_routes_for_vrf(bgp_vrf);
+ }
+}
+
+void bgp_evpn_configure_export_rt_for_vrf(struct bgp *bgp_vrf,
+ struct ecommunity *ecomadd)
+{
+ /* remove auto-generated RT */
+ evpn_auto_rt_export_delete_for_vrf(bgp_vrf);
+
+ /* Add the new RT to the RT list */
+ listnode_add_sort(bgp_vrf->vrf_export_rtl, ecomadd);
+ SET_FLAG(bgp_vrf->vrf_flags, BGP_VRF_EXPORT_RT_CFGD);
+
+ if (is_l3vni_live(bgp_vrf))
+ bgp_evpn_handle_export_rt_change_for_vrf(bgp_vrf);
+}
+
+void bgp_evpn_unconfigure_export_rt_for_vrf(struct bgp *bgp_vrf,
+ struct ecommunity *ecomdel)
+{
+ struct listnode *node = NULL, *nnode = NULL, *node_to_del = NULL;
+ struct ecommunity *ecom = NULL;
+
+ /* Remove the RT from the RT list */
+ for (ALL_LIST_ELEMENTS(bgp_vrf->vrf_export_rtl, node, nnode, ecom)) {
+ if (ecommunity_match(ecom, ecomdel)) {
+ ecommunity_free(&ecom);
+ node_to_del = node;
+ break;
+ }
+ }
+
+ if (node_to_del)
+ list_delete_node(bgp_vrf->vrf_export_rtl, node_to_del);
+
+ /*
+ * Temporary assert to make SA happy.
+ * The ALL_LIST_ELEMENTS macro above has a NULL check
+ * which means that SA is going to complain about
+ * the list_isempty call, which doesn't NULL check.
+ * So until we get this situation cleaned up, here
+ * we are.
+ */
+ assert(bgp_vrf->vrf_export_rtl);
+
+ /* fall back to auto-generated RT if this was the last RT */
+ if (list_isempty(bgp_vrf->vrf_export_rtl)) {
+ UNSET_FLAG(bgp_vrf->vrf_flags, BGP_VRF_EXPORT_RT_CFGD);
+ if (is_l3vni_live(bgp_vrf))
+ evpn_auto_rt_export_add_for_vrf(bgp_vrf);
+ }
+
+ if (is_l3vni_live(bgp_vrf))
+ bgp_evpn_handle_export_rt_change_for_vrf(bgp_vrf);
+}
+
+/*
+ * Handle change to BGP router id. This is invoked twice by the change
+ * handler, first before the router id has been changed and then after
+ * the router id has been changed. The first invocation will result in
+ * local routes for all VNIs/VRF being deleted and withdrawn and the next
+ * will result in the routes being re-advertised.
+ */
+void bgp_evpn_handle_router_id_update(struct bgp *bgp, int withdraw)
+{
+ struct listnode *node;
+ struct bgp *bgp_vrf;
+
+ if (withdraw) {
+
+ /* delete and withdraw all the type-5 routes
+ stored in the global table for this vrf
+ */
+ withdraw_router_id_vrf(bgp);
+
+ /* delete all the VNI routes (type-2/type-3) routes for all the
+ * L2-VNIs
+ */
+ hash_iterate(bgp->vnihash,
+ (void (*)(struct hash_bucket *,
+ void *))withdraw_router_id_vni,
+ bgp);
+
+ if (bgp->inst_type == BGP_INSTANCE_TYPE_DEFAULT) {
+ for (ALL_LIST_ELEMENTS_RO(bm->bgp, node, bgp_vrf)) {
+ if (bgp_vrf->evpn_info->advertise_pip &&
+ (bgp_vrf->evpn_info->pip_ip_static.s_addr
+ == INADDR_ANY))
+ bgp_vrf->evpn_info->pip_ip.s_addr
+ = INADDR_ANY;
+ }
+ }
+ } else {
+
+ /* Assign new default instance router-id */
+ if (bgp->inst_type == BGP_INSTANCE_TYPE_DEFAULT) {
+ for (ALL_LIST_ELEMENTS_RO(bm->bgp, node, bgp_vrf)) {
+ if (bgp_vrf->evpn_info->advertise_pip &&
+ (bgp_vrf->evpn_info->pip_ip_static.s_addr
+ == INADDR_ANY)) {
+ bgp_vrf->evpn_info->pip_ip =
+ bgp->router_id;
+ /* advertise type-5 routes with
+ * new nexthop
+ */
+ update_advertise_vrf_routes(bgp_vrf);
+ }
+ }
+ }
+
+ /* advertise all routes in the vrf as type-5 routes with the new
+ * RD
+ */
+ update_router_id_vrf(bgp);
+
+ /* advertise all the VNI routes (type-2/type-3) routes with the
+ * new RD
+ */
+ hash_iterate(bgp->vnihash,
+ (void (*)(struct hash_bucket *,
+ void *))update_router_id_vni,
+ bgp);
+ }
+}
+
+/*
+ * Handle change to auto-RT algorithm - update and advertise local routes.
+ */
+void bgp_evpn_handle_autort_change(struct bgp *bgp)
+{
+ hash_iterate(bgp->vnihash,
+ (void (*)(struct hash_bucket *,
+ void*))update_autort_vni,
+ bgp);
+ if (bgp->l3vni)
+ update_autort_l3vni(bgp);
+}
+
+/*
+ * Handle change to export RT - update and advertise local routes.
+ */
+int bgp_evpn_handle_export_rt_change(struct bgp *bgp, struct bgpevpn *vpn)
+{
+ return update_routes_for_vni(bgp, vpn);
+}
+
+void bgp_evpn_handle_vrf_rd_change(struct bgp *bgp_vrf, int withdraw)
+{
+ if (withdraw)
+ delete_withdraw_vrf_routes(bgp_vrf);
+ else
+ update_advertise_vrf_routes(bgp_vrf);
+}
+
+/*
+ * Handle change to RD. This is invoked twice by the change handler,
+ * first before the RD has been changed and then after the RD has
+ * been changed. The first invocation will result in local routes
+ * of this VNI being deleted and withdrawn and the next will result
+ * in the routes being re-advertised.
+ */
+void bgp_evpn_handle_rd_change(struct bgp *bgp, struct bgpevpn *vpn,
+ int withdraw)
+{
+ if (withdraw)
+ delete_withdraw_vni_routes(bgp, vpn);
+ else
+ update_advertise_vni_routes(bgp, vpn);
+}
+
+/*
+ * Install routes for this VNI. Invoked upon change to Import RT.
+ */
+int bgp_evpn_install_routes(struct bgp *bgp, struct bgpevpn *vpn)
+{
+ return install_routes_for_vni(bgp, vpn);
+}
+
+/*
+ * Uninstall all routes installed for this VNI. Invoked upon change
+ * to Import RT.
+ */
+int bgp_evpn_uninstall_routes(struct bgp *bgp, struct bgpevpn *vpn)
+{
+ return uninstall_routes_for_vni(bgp, vpn);
+}
+
+/*
+ * TODO: Hardcoded for a maximum of 2 VNIs right now
+ */
+char *bgp_evpn_label2str(mpls_label_t *label, uint32_t num_labels, char *buf,
+ int len)
+{
+ vni_t vni1, vni2;
+
+ vni1 = label2vni(label);
+ if (num_labels == 2) {
+ vni2 = label2vni(label + 1);
+ snprintf(buf, len, "%u/%u", vni1, vni2);
+ } else
+ snprintf(buf, len, "%u", vni1);
+ return buf;
+}
+
+/*
+ * Function to convert evpn route to json format.
+ * NOTE: We don't use prefix2str as the output here is a bit different.
+ */
+void bgp_evpn_route2json(const struct prefix_evpn *p, json_object *json)
+{
+ char buf1[ETHER_ADDR_STRLEN];
+ char buf2[PREFIX2STR_BUFFER];
+ uint8_t family;
+ uint8_t prefixlen;
+
+ if (!json)
+ return;
+
+ json_object_int_add(json, "routeType", p->prefix.route_type);
+
+ switch (p->prefix.route_type) {
+ case BGP_EVPN_MAC_IP_ROUTE:
+ json_object_int_add(json, "ethTag",
+ p->prefix.macip_addr.eth_tag);
+ json_object_int_add(json, "macLen", 8 * ETH_ALEN);
+ json_object_string_add(json, "mac",
+ prefix_mac2str(&p->prefix.macip_addr.mac, buf1,
+ sizeof(buf1)));
+
+ if (!is_evpn_prefix_ipaddr_none(p)) {
+ family = is_evpn_prefix_ipaddr_v4(p) ? AF_INET :
+ AF_INET6;
+ prefixlen = (family == AF_INET) ?
+ IPV4_MAX_BITLEN : IPV6_MAX_BITLEN;
+ inet_ntop(family, &p->prefix.macip_addr.ip.ip.addr,
+ buf2, PREFIX2STR_BUFFER);
+ json_object_int_add(json, "ipLen", prefixlen);
+ json_object_string_add(json, "ip", buf2);
+ }
+ break;
+
+ case BGP_EVPN_IMET_ROUTE:
+ json_object_int_add(json, "ethTag",
+ p->prefix.imet_addr.eth_tag);
+ family = is_evpn_prefix_ipaddr_v4(p) ? AF_INET : AF_INET6;
+ prefixlen = (family == AF_INET) ? IPV4_MAX_BITLEN :
+ IPV6_MAX_BITLEN;
+ inet_ntop(family, &p->prefix.imet_addr.ip.ip.addr, buf2,
+ PREFIX2STR_BUFFER);
+ json_object_int_add(json, "ipLen", prefixlen);
+ json_object_string_add(json, "ip", buf2);
+ break;
+
+ case BGP_EVPN_IP_PREFIX_ROUTE:
+ json_object_int_add(json, "ethTag",
+ p->prefix.prefix_addr.eth_tag);
+ family = is_evpn_prefix_ipaddr_v4(p) ? AF_INET : AF_INET6;
+ inet_ntop(family, &p->prefix.prefix_addr.ip.ip.addr,
+ buf2, sizeof(buf2));
+ json_object_int_add(json, "ipLen",
+ p->prefix.prefix_addr.ip_prefix_length);
+ json_object_string_add(json, "ip", buf2);
+ break;
+
+ default:
+ break;
+ }
+}
+
+/*
+ * Encode EVPN prefix in Update (MP_REACH)
+ */
+void bgp_evpn_encode_prefix(struct stream *s, const struct prefix *p,
+ const struct prefix_rd *prd, mpls_label_t *label,
+ uint32_t num_labels, struct attr *attr,
+ bool addpath_capable, uint32_t addpath_tx_id)
+{
+ struct prefix_evpn *evp = (struct prefix_evpn *)p;
+ int len, ipa_len = 0;
+
+ if (addpath_capable)
+ stream_putl(s, addpath_tx_id);
+
+ /* Route type */
+ stream_putc(s, evp->prefix.route_type);
+
+ switch (evp->prefix.route_type) {
+ case BGP_EVPN_MAC_IP_ROUTE:
+ if (is_evpn_prefix_ipaddr_v4(evp))
+ ipa_len = IPV4_MAX_BYTELEN;
+ else if (is_evpn_prefix_ipaddr_v6(evp))
+ ipa_len = IPV6_MAX_BYTELEN;
+ /* RD, ESI, EthTag, MAC+len, IP len, [IP], 1 VNI */
+ len = 8 + 10 + 4 + 1 + 6 + 1 + ipa_len + 3;
+ if (ipa_len && num_labels > 1) /* There are 2 VNIs */
+ len += 3;
+ stream_putc(s, len);
+ stream_put(s, prd->val, 8); /* RD */
+ if (attr)
+ stream_put(s, &attr->esi, ESI_BYTES);
+ else
+ stream_put(s, 0, 10);
+ stream_putl(s, evp->prefix.macip_addr.eth_tag); /* Ethernet Tag ID */
+ stream_putc(s, 8 * ETH_ALEN); /* Mac Addr Len - bits */
+ stream_put(s, evp->prefix.macip_addr.mac.octet, 6); /* Mac Addr */
+ stream_putc(s, 8 * ipa_len); /* IP address Length */
+ if (ipa_len) /* IP */
+ stream_put(s, &evp->prefix.macip_addr.ip.ip.addr,
+ ipa_len);
+ /* 1st label is the L2 VNI */
+ stream_put(s, label, BGP_LABEL_BYTES);
+ /* Include 2nd label (L3 VNI) if advertising MAC+IP */
+ if (ipa_len && num_labels > 1)
+ stream_put(s, label + 1, BGP_LABEL_BYTES);
+ break;
+
+ case BGP_EVPN_IMET_ROUTE:
+ stream_putc(s, 17); // TODO: length - assumes IPv4 address
+ stream_put(s, prd->val, 8); /* RD */
+ stream_putl(s, evp->prefix.imet_addr.eth_tag); /* Ethernet Tag ID */
+ stream_putc(s, IPV4_MAX_BITLEN); /* IP address Length - bits */
+ /* Originating Router's IP Addr */
+ stream_put_in_addr(s, &evp->prefix.imet_addr.ip.ipaddr_v4);
+ break;
+
+ case BGP_EVPN_ES_ROUTE:
+ stream_putc(s, 23); /* TODO: length: assumes ipv4 VTEP */
+ stream_put(s, prd->val, 8); /* RD */
+ stream_put(s, evp->prefix.es_addr.esi.val, 10); /* ESI */
+ stream_putc(s, IPV4_MAX_BITLEN); /* IP address Length - bits */
+ /* VTEP IP */
+ stream_put_in_addr(s, &evp->prefix.es_addr.ip.ipaddr_v4);
+ break;
+
+ case BGP_EVPN_AD_ROUTE:
+ /* RD, ESI, EthTag, 1 VNI */
+ len = RD_BYTES + ESI_BYTES + EVPN_ETH_TAG_BYTES + BGP_LABEL_BYTES;
+ stream_putc(s, len);
+ stream_put(s, prd->val, RD_BYTES); /* RD */
+ stream_put(s, evp->prefix.ead_addr.esi.val, ESI_BYTES); /* ESI */
+ stream_putl(s, evp->prefix.ead_addr.eth_tag); /* Ethernet Tag */
+ stream_put(s, label, BGP_LABEL_BYTES);
+ break;
+
+ case BGP_EVPN_IP_PREFIX_ROUTE:
+ /* TODO: AddPath support. */
+ evpn_mpattr_encode_type5(s, p, prd, label, num_labels, attr);
+ break;
+
+ default:
+ break;
+ }
+}
+
+int bgp_nlri_parse_evpn(struct peer *peer, struct attr *attr,
+ struct bgp_nlri *packet, bool withdraw)
+{
+ uint8_t *pnt;
+ uint8_t *lim;
+ afi_t afi;
+ safi_t safi;
+ uint32_t addpath_id;
+ bool addpath_capable;
+ int psize = 0;
+ uint8_t rtype;
+ struct prefix p;
+
+ /* Start processing the NLRI - there may be multiple in the MP_REACH */
+ 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);
+
+ for (; pnt < lim; pnt += psize) {
+ /* Clear prefix structure. */
+ memset(&p, 0, sizeof(p));
+
+ /* Deal with path-id if AddPath is supported. */
+ 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;
+ }
+
+ /* All EVPN NLRI types start with type and length. */
+ if (pnt + 2 > lim)
+ return BGP_NLRI_PARSE_ERROR_EVPN_MISSING_TYPE;
+
+ rtype = *pnt++;
+ psize = *pnt++;
+
+ /* When packet overflow occur return immediately. */
+ if (pnt + psize > lim)
+ return BGP_NLRI_PARSE_ERROR_PACKET_OVERFLOW;
+
+ switch (rtype) {
+ case BGP_EVPN_MAC_IP_ROUTE:
+ if (process_type2_route(peer, afi, safi,
+ withdraw ? NULL : attr, pnt,
+ psize, addpath_id)) {
+ flog_err(
+ EC_BGP_EVPN_FAIL,
+ "%u:%s - Error in processing EVPN type-2 NLRI size %d",
+ peer->bgp->vrf_id, peer->host, psize);
+ return BGP_NLRI_PARSE_ERROR_EVPN_TYPE2_SIZE;
+ }
+ break;
+
+ case BGP_EVPN_IMET_ROUTE:
+ if (process_type3_route(peer, afi, safi,
+ withdraw ? NULL : attr, pnt,
+ psize, addpath_id)) {
+ flog_err(
+ EC_BGP_PKT_PROCESS,
+ "%u:%s - Error in processing EVPN type-3 NLRI size %d",
+ peer->bgp->vrf_id, peer->host, psize);
+ return BGP_NLRI_PARSE_ERROR_EVPN_TYPE3_SIZE;
+ }
+ break;
+
+ case BGP_EVPN_ES_ROUTE:
+ if (bgp_evpn_type4_route_process(peer, afi, safi,
+ withdraw ? NULL : attr, pnt,
+ psize, addpath_id)) {
+ flog_err(
+ EC_BGP_PKT_PROCESS,
+ "%u:%s - Error in processing EVPN type-4 NLRI size %d",
+ peer->bgp->vrf_id, peer->host, psize);
+ return BGP_NLRI_PARSE_ERROR_EVPN_TYPE4_SIZE;
+ }
+ break;
+
+ case BGP_EVPN_AD_ROUTE:
+ if (bgp_evpn_type1_route_process(peer, afi, safi,
+ withdraw ? NULL : attr, pnt,
+ psize, addpath_id)) {
+ flog_err(
+ EC_BGP_PKT_PROCESS,
+ "%u:%s - Error in processing EVPN type-1 NLRI size %d",
+ peer->bgp->vrf_id, peer->host, psize);
+ return BGP_NLRI_PARSE_ERROR_EVPN_TYPE1_SIZE;
+ }
+ break;
+
+ case BGP_EVPN_IP_PREFIX_ROUTE:
+ if (process_type5_route(peer, afi, safi,
+ withdraw ? NULL : attr, pnt,
+ psize, addpath_id)) {
+ flog_err(
+ EC_BGP_PKT_PROCESS,
+ "%u:%s - Error in processing EVPN type-5 NLRI size %d",
+ peer->bgp->vrf_id, peer->host, psize);
+ return BGP_NLRI_PARSE_ERROR_EVPN_TYPE5_SIZE;
+ }
+ break;
+
+ default:
+ break;
+ }
+ }
+
+ /* Packet length consistency check. */
+ if (pnt != lim)
+ return BGP_NLRI_PARSE_ERROR_PACKET_LENGTH;
+
+ return BGP_NLRI_PARSE_OK;
+}
+
+/*
+ * Map the RTs (configured or automatically derived) of a VRF to the VRF.
+ * The mapping will be used during route processing.
+ * bgp_vrf: specific bgp vrf instance on which RT is configured
+ */
+void bgp_evpn_map_vrf_to_its_rts(struct bgp *bgp_vrf)
+{
+ uint32_t i = 0;
+ struct ecommunity_val *eval = NULL;
+ struct listnode *node = NULL, *nnode = NULL;
+ struct ecommunity *ecom = NULL;
+
+ for (ALL_LIST_ELEMENTS(bgp_vrf->vrf_import_rtl, node, nnode, ecom)) {
+ for (i = 0; i < ecom->size; i++) {
+ eval = (struct ecommunity_val *)(ecom->val
+ + (i
+ * ECOMMUNITY_SIZE));
+ map_vrf_to_rt(bgp_vrf, eval);
+ }
+ }
+}
+
+/*
+ * Unmap the RTs (configured or automatically derived) of a VRF from the VRF.
+ */
+void bgp_evpn_unmap_vrf_from_its_rts(struct bgp *bgp_vrf)
+{
+ uint32_t i;
+ struct ecommunity_val *eval;
+ struct listnode *node, *nnode;
+ struct ecommunity *ecom;
+
+ for (ALL_LIST_ELEMENTS(bgp_vrf->vrf_import_rtl, node, nnode, ecom)) {
+ for (i = 0; i < ecom->size; i++) {
+ struct vrf_irt_node *irt;
+ struct ecommunity_val eval_tmp;
+
+ eval = (struct ecommunity_val *)(ecom->val
+ + (i
+ * ECOMMUNITY_SIZE));
+ /* If using "automatic" RT, we only care about the
+ * local-admin sub-field.
+ * This is to facilitate using VNI as the RT for EBGP
+ * peering too.
+ */
+ memcpy(&eval_tmp, eval, ECOMMUNITY_SIZE);
+ if (!CHECK_FLAG(bgp_vrf->vrf_flags,
+ BGP_VRF_IMPORT_RT_CFGD))
+ mask_ecom_global_admin(&eval_tmp, eval);
+
+ irt = lookup_vrf_import_rt(&eval_tmp);
+ if (irt)
+ unmap_vrf_from_rt(bgp_vrf, irt);
+ }
+ }
+}
+
+
+/*
+ * Map the RTs (configured or automatically derived) of a VNI to the VNI.
+ * The mapping will be used during route processing.
+ */
+void bgp_evpn_map_vni_to_its_rts(struct bgp *bgp, struct bgpevpn *vpn)
+{
+ uint32_t i;
+ struct ecommunity_val *eval;
+ struct listnode *node, *nnode;
+ struct ecommunity *ecom;
+
+ for (ALL_LIST_ELEMENTS(vpn->import_rtl, node, nnode, ecom)) {
+ for (i = 0; i < ecom->size; i++) {
+ eval = (struct ecommunity_val *)(ecom->val
+ + (i
+ * ECOMMUNITY_SIZE));
+ map_vni_to_rt(bgp, vpn, eval);
+ }
+ }
+}
+
+/*
+ * Unmap the RTs (configured or automatically derived) of a VNI from the VNI.
+ */
+void bgp_evpn_unmap_vni_from_its_rts(struct bgp *bgp, struct bgpevpn *vpn)
+{
+ uint32_t i;
+ struct ecommunity_val *eval;
+ struct listnode *node, *nnode;
+ struct ecommunity *ecom;
+
+ for (ALL_LIST_ELEMENTS(vpn->import_rtl, node, nnode, ecom)) {
+ for (i = 0; i < ecom->size; i++) {
+ struct irt_node *irt;
+ struct ecommunity_val eval_tmp;
+
+ eval = (struct ecommunity_val *)(ecom->val
+ + (i
+ * ECOMMUNITY_SIZE));
+ /* If using "automatic" RT, we only care about the
+ * local-admin sub-field.
+ * This is to facilitate using VNI as the RT for EBGP
+ * peering too.
+ */
+ memcpy(&eval_tmp, eval, ECOMMUNITY_SIZE);
+ if (!is_import_rt_configured(vpn))
+ mask_ecom_global_admin(&eval_tmp, eval);
+
+ irt = lookup_import_rt(bgp, &eval_tmp);
+ if (irt)
+ unmap_vni_from_rt(bgp, vpn, irt);
+ }
+ }
+}
+
+/*
+ * Derive Import RT automatically for VNI and map VNI to RT.
+ * The mapping will be used during route processing.
+ */
+void bgp_evpn_derive_auto_rt_import(struct bgp *bgp, struct bgpevpn *vpn)
+{
+ form_auto_rt(bgp, vpn->vni, vpn->import_rtl);
+ UNSET_FLAG(vpn->flags, VNI_FLAG_IMPRT_CFGD);
+
+ /* Map RT to VNI */
+ bgp_evpn_map_vni_to_its_rts(bgp, vpn);
+}
+
+/*
+ * Derive Export RT automatically for VNI.
+ */
+void bgp_evpn_derive_auto_rt_export(struct bgp *bgp, struct bgpevpn *vpn)
+{
+ form_auto_rt(bgp, vpn->vni, vpn->export_rtl);
+ UNSET_FLAG(vpn->flags, VNI_FLAG_EXPRT_CFGD);
+}
+
+/*
+ * Derive RD automatically for VNI using passed information - it
+ * is of the form RouterId:unique-id-for-vni.
+ */
+void bgp_evpn_derive_auto_rd_for_vrf(struct bgp *bgp)
+{
+ if (is_vrf_rd_configured(bgp))
+ return;
+
+ form_auto_rd(bgp->router_id, bgp->vrf_rd_id, &bgp->vrf_prd);
+}
+
+/*
+ * Derive RD automatically for VNI using passed information - it
+ * is of the form RouterId:unique-id-for-vni.
+ */
+void bgp_evpn_derive_auto_rd(struct bgp *bgp, struct bgpevpn *vpn)
+{
+ char buf[BGP_EVPN_PREFIX_RD_LEN];
+
+ vpn->prd.family = AF_UNSPEC;
+ vpn->prd.prefixlen = 64;
+ snprintfrr(buf, sizeof(buf), "%pI4:%hu", &bgp->router_id, vpn->rd_id);
+ (void)str2prefix_rd(buf, &vpn->prd);
+ UNSET_FLAG(vpn->flags, VNI_FLAG_RD_CFGD);
+}
+
+/*
+ * Lookup L3-VNI
+ */
+bool bgp_evpn_lookup_l3vni_l2vni_table(vni_t vni)
+{
+ struct list *inst = bm->bgp;
+ struct listnode *node;
+ struct bgp *bgp_vrf;
+
+ for (ALL_LIST_ELEMENTS_RO(inst, node, bgp_vrf)) {
+ if (bgp_vrf->l3vni == vni)
+ return true;
+ }
+
+ return false;
+}
+
+/*
+ * Lookup VNI.
+ */
+struct bgpevpn *bgp_evpn_lookup_vni(struct bgp *bgp, vni_t vni)
+{
+ struct bgpevpn *vpn;
+ struct bgpevpn tmp;
+
+ memset(&tmp, 0, sizeof(tmp));
+ tmp.vni = vni;
+ vpn = hash_lookup(bgp->vnihash, &tmp);
+ return vpn;
+}
+
+/*
+ * Create a new vpn - invoked upon configuration or zebra notification.
+ */
+struct bgpevpn *bgp_evpn_new(struct bgp *bgp, vni_t vni,
+ struct in_addr originator_ip,
+ vrf_id_t tenant_vrf_id,
+ struct in_addr mcast_grp,
+ ifindex_t svi_ifindex)
+{
+ struct bgpevpn *vpn;
+
+ vpn = XCALLOC(MTYPE_BGP_EVPN, sizeof(struct bgpevpn));
+
+ /* Set values - RD and RT set to defaults. */
+ vpn->vni = vni;
+ vpn->originator_ip = originator_ip;
+ vpn->tenant_vrf_id = tenant_vrf_id;
+ vpn->mcast_grp = mcast_grp;
+ vpn->svi_ifindex = svi_ifindex;
+
+ /* Initialize route-target import and export lists */
+ vpn->import_rtl = list_new();
+ vpn->import_rtl->cmp =
+ (int (*)(void *, void *))bgp_evpn_route_target_cmp;
+ vpn->import_rtl->del = bgp_evpn_xxport_delete_ecomm;
+ vpn->export_rtl = list_new();
+ vpn->export_rtl->cmp =
+ (int (*)(void *, void *))bgp_evpn_route_target_cmp;
+ vpn->export_rtl->del = bgp_evpn_xxport_delete_ecomm;
+ bf_assign_index(bm->rd_idspace, vpn->rd_id);
+ derive_rd_rt_for_vni(bgp, vpn);
+
+ /* Initialize EVPN route table. */
+ vpn->route_table = bgp_table_init(bgp, AFI_L2VPN, SAFI_EVPN);
+
+ /* Add to hash */
+ (void)hash_get(bgp->vnihash, vpn, hash_alloc_intern);
+
+ bgp_evpn_remote_ip_hash_init(vpn);
+ bgp_evpn_link_to_vni_svi_hash(bgp, vpn);
+
+ /* add to l2vni list on corresponding vrf */
+ bgpevpn_link_to_l3vni(vpn);
+
+ bgp_evpn_vni_es_init(vpn);
+
+ QOBJ_REG(vpn, bgpevpn);
+ return vpn;
+}
+
+/*
+ * Free a given VPN - called in multiple scenarios such as zebra
+ * notification, configuration being deleted, advertise-all-vni disabled etc.
+ * This just frees appropriate memory, caller should have taken other
+ * needed actions.
+ */
+void bgp_evpn_free(struct bgp *bgp, struct bgpevpn *vpn)
+{
+ bgp_evpn_remote_ip_hash_destroy(vpn);
+ bgp_evpn_vni_es_cleanup(vpn);
+ bgpevpn_unlink_from_l3vni(vpn);
+ bgp_table_unlock(vpn->route_table);
+ bgp_evpn_unmap_vni_from_its_rts(bgp, vpn);
+ list_delete(&vpn->import_rtl);
+ list_delete(&vpn->export_rtl);
+ bf_release_index(bm->rd_idspace, vpn->rd_id);
+ hash_release(bgp->vni_svi_hash, vpn);
+ hash_release(bgp->vnihash, vpn);
+ QOBJ_UNREG(vpn);
+ XFREE(MTYPE_BGP_EVPN, vpn);
+}
+
+static void hash_evpn_free(struct bgpevpn *vpn)
+{
+ XFREE(MTYPE_BGP_EVPN, vpn);
+}
+
+/*
+ * Import evpn route from global table to VNI/VRF/ESI.
+ */
+int bgp_evpn_import_route(struct bgp *bgp, afi_t afi, safi_t safi,
+ const struct prefix *p, struct bgp_path_info *pi)
+{
+ return install_uninstall_evpn_route(bgp, afi, safi, p, pi, 1);
+}
+
+/*
+ * Unimport evpn route from VNI/VRF/ESI.
+ */
+int bgp_evpn_unimport_route(struct bgp *bgp, afi_t afi, safi_t safi,
+ const struct prefix *p, struct bgp_path_info *pi)
+{
+ return install_uninstall_evpn_route(bgp, afi, safi, p, pi, 0);
+}
+
+/* filter routes which have martian next hops */
+int bgp_filter_evpn_routes_upon_martian_nh_change(struct bgp *bgp)
+{
+ afi_t afi;
+ safi_t safi;
+ struct bgp_dest *rd_dest, *dest;
+ struct bgp_table *table;
+ struct bgp_path_info *pi;
+
+ afi = AFI_L2VPN;
+ safi = SAFI_EVPN;
+
+ /* Walk entire global routing table and evaluate routes which could be
+ * imported into this VPN. Note that we cannot just look at the routes
+ * for the VNI's RD -
+ * remote routes applicable for this VNI could have any RD.
+ */
+ /* EVPN routes are a 2-level table. */
+ for (rd_dest = bgp_table_top(bgp->rib[afi][safi]); rd_dest;
+ rd_dest = bgp_route_next(rd_dest)) {
+ table = bgp_dest_get_bgp_table_info(rd_dest);
+ if (!table)
+ continue;
+
+ 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) {
+
+ /* Consider "valid" remote routes applicable for
+ * this VNI. */
+ if (!(pi->type == ZEBRA_ROUTE_BGP
+ && pi->sub_type == BGP_ROUTE_NORMAL))
+ continue;
+ if (bgp_nexthop_self(bgp, afi, pi->type,
+ pi->sub_type, pi->attr,
+ dest)) {
+ const struct prefix *p =
+ bgp_dest_get_prefix(dest);
+
+ if (bgp_debug_update(pi->peer, p, NULL,
+ 1)) {
+ char attr_str[BUFSIZ] = {0};
+
+ bgp_dump_attr(pi->attr,
+ attr_str,
+ sizeof(attr_str));
+
+ zlog_debug(
+ "%u: prefix %pBD with attr %s - DENIED due to martian or self nexthop",
+ bgp->vrf_id, dest,
+ attr_str);
+ }
+ bgp_evpn_unimport_route(bgp, afi, safi,
+ p, pi);
+
+ bgp_rib_remove(dest, pi, pi->peer, afi,
+ safi);
+ }
+ }
+ }
+ }
+
+ return 0;
+}
+
+/*
+ * Handle del of a local MACIP.
+ */
+int bgp_evpn_local_macip_del(struct bgp *bgp, vni_t vni, struct ethaddr *mac,
+ struct ipaddr *ip, int state)
+{
+ struct bgpevpn *vpn;
+ struct prefix_evpn p;
+ struct bgp_dest *dest;
+
+ /* Lookup VNI hash - should exist. */
+ vpn = bgp_evpn_lookup_vni(bgp, vni);
+ if (!vpn || !is_vni_live(vpn)) {
+ flog_warn(EC_BGP_EVPN_VPN_VNI,
+ "%u: VNI hash entry for VNI %u %s at MACIP DEL",
+ bgp->vrf_id, vni, vpn ? "not live" : "not found");
+ return -1;
+ }
+
+ build_evpn_type2_prefix(&p, mac, ip);
+ if (state == ZEBRA_NEIGH_ACTIVE) {
+ /* Remove EVPN type-2 route and schedule for processing. */
+ delete_evpn_route(bgp, vpn, &p);
+ } else {
+ /* Re-instate the current remote best path if any */
+ dest = bgp_node_lookup(vpn->route_table, (struct prefix *)&p);
+ if (dest) {
+ evpn_zebra_reinstall_best_route(bgp, vpn, dest);
+ bgp_dest_unlock_node(dest);
+ }
+ }
+
+ return 0;
+}
+
+/*
+ * Handle add of a local MACIP.
+ */
+int bgp_evpn_local_macip_add(struct bgp *bgp, vni_t vni, struct ethaddr *mac,
+ struct ipaddr *ip, uint8_t flags, uint32_t seq, esi_t *esi)
+{
+ struct bgpevpn *vpn;
+ struct prefix_evpn p;
+
+ /* Lookup VNI hash - should exist. */
+ vpn = bgp_evpn_lookup_vni(bgp, vni);
+ if (!vpn || !is_vni_live(vpn)) {
+ flog_warn(EC_BGP_EVPN_VPN_VNI,
+ "%u: VNI hash entry for VNI %u %s at MACIP ADD",
+ bgp->vrf_id, vni, vpn ? "not live" : "not found");
+ return -1;
+ }
+
+ /* Create EVPN type-2 route and schedule for processing. */
+ build_evpn_type2_prefix(&p, mac, ip);
+ if (update_evpn_route(bgp, vpn, &p, flags, seq, esi)) {
+ flog_err(
+ EC_BGP_EVPN_ROUTE_CREATE,
+ "%u:Failed to create Type-2 route, VNI %u %s MAC %pEA IP %pIA (flags: 0x%x)",
+ bgp->vrf_id, vpn->vni,
+ CHECK_FLAG(flags, ZEBRA_MACIP_TYPE_STICKY)
+ ? "sticky gateway"
+ : "",
+ mac, ip, flags);
+ return -1;
+ }
+
+ return 0;
+}
+
+static void link_l2vni_hash_to_l3vni(struct hash_bucket *bucket,
+ struct bgp *bgp_vrf)
+{
+ struct bgpevpn *vpn = (struct bgpevpn *)bucket->data;
+ struct bgp *bgp_evpn = NULL;
+
+ bgp_evpn = bgp_get_evpn();
+ assert(bgp_evpn);
+
+ if (vpn->tenant_vrf_id == bgp_vrf->vrf_id)
+ bgpevpn_link_to_l3vni(vpn);
+}
+
+int bgp_evpn_local_l3vni_add(vni_t l3vni, vrf_id_t vrf_id,
+ struct ethaddr *svi_rmac,
+ struct ethaddr *vrr_rmac,
+ struct in_addr originator_ip, int filter,
+ ifindex_t svi_ifindex,
+ bool is_anycast_mac)
+{
+ struct bgp *bgp_vrf = NULL; /* bgp VRF instance */
+ struct bgp *bgp_evpn = NULL; /* EVPN bgp instance */
+ struct listnode *node = NULL;
+ struct bgpevpn *vpn = NULL;
+ as_t as = 0;
+
+ /* get the EVPN instance - required to get the AS number for VRF
+ * auto-creatio
+ */
+ bgp_evpn = bgp_get_evpn();
+ if (!bgp_evpn) {
+ flog_err(
+ EC_BGP_NO_DFLT,
+ "Cannot process L3VNI %u ADD - EVPN BGP instance not yet created",
+ l3vni);
+ return -1;
+ }
+
+ if (CHECK_FLAG(bgp_evpn->flags, BGP_FLAG_DELETE_IN_PROGRESS)) {
+ flog_err(EC_BGP_NO_DFLT,
+ "Cannot process L3VNI %u ADD - EVPN BGP instance is shutting down",
+ l3vni);
+ return -1;
+ }
+
+ as = bgp_evpn->as;
+
+ /* if the BGP vrf instance doesn't exist - create one */
+ bgp_vrf = bgp_lookup_by_vrf_id(vrf_id);
+ if (!bgp_vrf) {
+
+ int ret = 0;
+
+ ret = bgp_get_vty(&bgp_vrf, &as, vrf_id_to_name(vrf_id),
+ vrf_id == VRF_DEFAULT
+ ? BGP_INSTANCE_TYPE_DEFAULT
+ : BGP_INSTANCE_TYPE_VRF);
+ switch (ret) {
+ case BGP_ERR_AS_MISMATCH:
+ flog_err(EC_BGP_EVPN_AS_MISMATCH,
+ "BGP instance is already running; AS is %u",
+ as);
+ return -1;
+ case BGP_ERR_INSTANCE_MISMATCH:
+ flog_err(EC_BGP_EVPN_INSTANCE_MISMATCH,
+ "BGP instance type mismatch");
+ return -1;
+ }
+
+ /* mark as auto created */
+ SET_FLAG(bgp_vrf->vrf_flags, BGP_VRF_AUTO);
+ }
+
+ /* associate the vrf with l3vni and related parameters */
+ bgp_vrf->l3vni = l3vni;
+ bgp_vrf->originator_ip = originator_ip;
+ bgp_vrf->l3vni_svi_ifindex = svi_ifindex;
+ bgp_vrf->evpn_info->is_anycast_mac = is_anycast_mac;
+
+ /* copy anycast MAC from VRR MAC */
+ memcpy(&bgp_vrf->rmac, vrr_rmac, ETH_ALEN);
+ /* copy sys RMAC from SVI MAC */
+ memcpy(&bgp_vrf->evpn_info->pip_rmac_zebra, svi_rmac, ETH_ALEN);
+ /* PIP user configured mac is not present use svi mac as sys mac */
+ if (is_zero_mac(&bgp_vrf->evpn_info->pip_rmac_static))
+ memcpy(&bgp_vrf->evpn_info->pip_rmac, svi_rmac, ETH_ALEN);
+
+ if (bgp_debug_zebra(NULL))
+ zlog_debug(
+ "VRF %s vni %u pip %s RMAC %pEA sys RMAC %pEA static RMAC %pEA is_anycast_mac %s",
+ vrf_id_to_name(bgp_vrf->vrf_id), bgp_vrf->l3vni,
+ bgp_vrf->evpn_info->advertise_pip ? "enable"
+ : "disable",
+ &bgp_vrf->rmac, &bgp_vrf->evpn_info->pip_rmac,
+ &bgp_vrf->evpn_info->pip_rmac_static,
+ is_anycast_mac ? "Enable" : "Disable");
+
+ /* set the right filter - are we using l3vni only for prefix routes? */
+ if (filter) {
+ SET_FLAG(bgp_vrf->vrf_flags, BGP_VRF_L3VNI_PREFIX_ROUTES_ONLY);
+
+ /*
+ * VNI_FLAG_USE_TWO_LABELS flag for linked L2VNIs should not be
+ * set before linking vrf to L3VNI. Thus, no need to clear
+ * that explicitly.
+ */
+ } else {
+ UNSET_FLAG(bgp_vrf->vrf_flags,
+ BGP_VRF_L3VNI_PREFIX_ROUTES_ONLY);
+
+ for (ALL_LIST_ELEMENTS_RO(bgp_vrf->l2vnis, node, vpn)) {
+ if (!CHECK_FLAG(vpn->flags, VNI_FLAG_USE_TWO_LABELS)) {
+
+ /*
+ * If we are flapping VNI_FLAG_USE_TWO_LABELS
+ * flag, update all MACIP routes in this VNI
+ */
+ SET_FLAG(vpn->flags, VNI_FLAG_USE_TWO_LABELS);
+ update_all_type2_routes(bgp_evpn, vpn);
+ }
+ }
+ }
+
+ /* Map auto derive or configured RTs */
+ if (!CHECK_FLAG(bgp_vrf->vrf_flags, BGP_VRF_IMPORT_RT_CFGD))
+ evpn_auto_rt_import_add_for_vrf(bgp_vrf);
+ else
+ bgp_evpn_map_vrf_to_its_rts(bgp_vrf);
+
+ if (!CHECK_FLAG(bgp_vrf->vrf_flags, BGP_VRF_EXPORT_RT_CFGD))
+ evpn_auto_rt_export_add_for_vrf(bgp_vrf);
+
+ /* auto derive RD */
+ bgp_evpn_derive_auto_rd_for_vrf(bgp_vrf);
+
+ /* link all corresponding l2vnis */
+ hash_iterate(bgp_evpn->vnihash,
+ (void (*)(struct hash_bucket *,
+ void *))link_l2vni_hash_to_l3vni,
+ bgp_vrf);
+
+ /* Only update all corresponding type-2 routes if we are advertising two
+ * labels along with type-2 routes
+ */
+ if (!filter)
+ for (ALL_LIST_ELEMENTS_RO(bgp_vrf->l2vnis, node, vpn))
+ update_routes_for_vni(bgp_evpn, vpn);
+
+ /* advertise type-5 routes if needed */
+ update_advertise_vrf_routes(bgp_vrf);
+
+ /* install all remote routes belonging to this l3vni into correspondng
+ * vrf */
+ install_routes_for_vrf(bgp_vrf);
+
+ return 0;
+}
+
+int bgp_evpn_local_l3vni_del(vni_t l3vni, vrf_id_t vrf_id)
+{
+ struct bgp *bgp_vrf = NULL; /* bgp vrf instance */
+ struct bgp *bgp_evpn = NULL; /* EVPN bgp instance */
+ struct listnode *node = NULL;
+ struct listnode *next = NULL;
+ struct bgpevpn *vpn = NULL;
+
+ bgp_vrf = bgp_lookup_by_vrf_id(vrf_id);
+ if (!bgp_vrf) {
+ flog_err(
+ EC_BGP_NO_DFLT,
+ "Cannot process L3VNI %u Del - Could not find BGP instance",
+ l3vni);
+ return -1;
+ }
+
+ bgp_evpn = bgp_get_evpn();
+ if (!bgp_evpn) {
+ flog_err(
+ EC_BGP_NO_DFLT,
+ "Cannot process L3VNI %u Del - Could not find EVPN BGP instance",
+ l3vni);
+ return -1;
+ }
+
+ if (CHECK_FLAG(bgp_evpn->flags, BGP_FLAG_DELETE_IN_PROGRESS)) {
+ flog_err(EC_BGP_NO_DFLT,
+ "Cannot process L3VNI %u ADD - EVPN BGP instance is shutting down",
+ l3vni);
+ return -1;
+ }
+
+ /* Remove remote routes from BGT VRF even if BGP_VRF_AUTO is configured,
+ * bgp_delete would not remove/decrement bgp_path_info of the ip_prefix
+ * routes. This will uninstalling the routes from zebra and decremnt the
+ * bgp info count.
+ */
+ uninstall_routes_for_vrf(bgp_vrf);
+
+ /* delete/withdraw all type-5 routes */
+ delete_withdraw_vrf_routes(bgp_vrf);
+
+ /* remove the l3vni from vrf instance */
+ bgp_vrf->l3vni = 0;
+
+ /* remove the Rmac from the BGP vrf */
+ memset(&bgp_vrf->rmac, 0, sizeof(struct ethaddr));
+ memset(&bgp_vrf->evpn_info->pip_rmac_zebra, 0, ETH_ALEN);
+ if (is_zero_mac(&bgp_vrf->evpn_info->pip_rmac_static) &&
+ !is_zero_mac(&bgp_vrf->evpn_info->pip_rmac))
+ memset(&bgp_vrf->evpn_info->pip_rmac, 0, ETH_ALEN);
+
+ /* remove default import RT or Unmap non-default import RT */
+ if (!list_isempty(bgp_vrf->vrf_import_rtl)) {
+ bgp_evpn_unmap_vrf_from_its_rts(bgp_vrf);
+ if (!CHECK_FLAG(bgp_vrf->vrf_flags, BGP_VRF_IMPORT_RT_CFGD))
+ list_delete_all_node(bgp_vrf->vrf_import_rtl);
+ }
+
+ /* remove default export RT */
+ if (!list_isempty(bgp_vrf->vrf_export_rtl) &&
+ !CHECK_FLAG(bgp_vrf->vrf_flags, BGP_VRF_EXPORT_RT_CFGD)) {
+ list_delete_all_node(bgp_vrf->vrf_export_rtl);
+ }
+
+ /* update all corresponding local mac-ip routes */
+ if (!CHECK_FLAG(bgp_vrf->vrf_flags, BGP_VRF_L3VNI_PREFIX_ROUTES_ONLY)) {
+ for (ALL_LIST_ELEMENTS_RO(bgp_vrf->l2vnis, node, vpn)) {
+ UNSET_FLAG(vpn->flags, VNI_FLAG_USE_TWO_LABELS);
+ update_routes_for_vni(bgp_evpn, vpn);
+ }
+ }
+
+ /* If any L2VNIs point to this instance, unlink them. */
+ for (ALL_LIST_ELEMENTS(bgp_vrf->l2vnis, node, next, vpn))
+ bgpevpn_unlink_from_l3vni(vpn);
+
+ UNSET_FLAG(bgp_vrf->vrf_flags, BGP_VRF_L3VNI_PREFIX_ROUTES_ONLY);
+
+ /* Delete the instance if it was autocreated */
+ if (CHECK_FLAG(bgp_vrf->vrf_flags, BGP_VRF_AUTO))
+ bgp_delete(bgp_vrf);
+
+ return 0;
+}
+
+/*
+ * Handle del of a local VNI.
+ */
+int bgp_evpn_local_vni_del(struct bgp *bgp, vni_t vni)
+{
+ struct bgpevpn *vpn;
+
+ /* Locate VNI hash */
+ vpn = bgp_evpn_lookup_vni(bgp, vni);
+ if (!vpn) {
+ if (bgp_debug_zebra(NULL))
+ flog_warn(
+ EC_BGP_EVPN_VPN_VNI,
+ "%u: VNI hash entry for VNI %u not found at DEL",
+ bgp->vrf_id, vni);
+ return 0;
+ }
+
+ /* Remove all local EVPN routes and schedule for processing (to
+ * withdraw from peers).
+ */
+ delete_routes_for_vni(bgp, vpn);
+
+ bgp_evpn_unlink_from_vni_svi_hash(bgp, vpn);
+
+ vpn->svi_ifindex = 0;
+ /*
+ * tunnel is no longer active, del tunnel ip address from tip_hash
+ */
+ bgp_tip_del(bgp, &vpn->originator_ip);
+
+ /* Clear "live" flag and see if hash needs to be freed. */
+ UNSET_FLAG(vpn->flags, VNI_FLAG_LIVE);
+ if (!is_vni_configured(vpn))
+ bgp_evpn_free(bgp, vpn);
+
+ return 0;
+}
+
+/*
+ * Handle add (or update) of a local VNI. The VNI changes we care
+ * about are for the local-tunnel-ip and the (tenant) VRF.
+ */
+int bgp_evpn_local_vni_add(struct bgp *bgp, vni_t vni,
+ struct in_addr originator_ip,
+ vrf_id_t tenant_vrf_id,
+ struct in_addr mcast_grp,
+ ifindex_t svi_ifindex)
+{
+ struct bgpevpn *vpn;
+ struct prefix_evpn p;
+
+ /* Lookup VNI. If present and no change, exit. */
+ vpn = bgp_evpn_lookup_vni(bgp, vni);
+ if (vpn) {
+
+ if (is_vni_live(vpn)
+ && IPV4_ADDR_SAME(&vpn->originator_ip, &originator_ip)
+ && IPV4_ADDR_SAME(&vpn->mcast_grp, &mcast_grp)
+ && vpn->tenant_vrf_id == tenant_vrf_id
+ && vpn->svi_ifindex == svi_ifindex)
+ /* Probably some other param has changed that we don't
+ * care about. */
+ return 0;
+
+ bgp_evpn_mcast_grp_change(bgp, vpn, mcast_grp);
+
+ if (vpn->svi_ifindex != svi_ifindex) {
+
+ /*
+ * Unresolve all the gateway IP nexthops for this VNI
+ * for old SVI
+ */
+ bgp_evpn_remote_ip_hash_iterate(
+ vpn,
+ (void (*)(struct hash_bucket *, void *))
+ bgp_evpn_remote_ip_hash_unlink_nexthop,
+ vpn);
+ bgp_evpn_unlink_from_vni_svi_hash(bgp, vpn);
+ vpn->svi_ifindex = svi_ifindex;
+ bgp_evpn_link_to_vni_svi_hash(bgp, vpn);
+
+ /*
+ * Resolve all the gateway IP nexthops for this VNI
+ * for new SVI
+ */
+ bgp_evpn_remote_ip_hash_iterate(
+ vpn,
+ (void (*)(struct hash_bucket *, void *))
+ bgp_evpn_remote_ip_hash_link_nexthop,
+ vpn);
+ }
+
+ /* Update tenant_vrf_id if it has changed. */
+ if (vpn->tenant_vrf_id != tenant_vrf_id) {
+
+ /*
+ * Unresolve all the gateway IP nexthops for this VNI
+ * in old tenant vrf
+ */
+ bgp_evpn_remote_ip_hash_iterate(
+ vpn,
+ (void (*)(struct hash_bucket *, void *))
+ bgp_evpn_remote_ip_hash_unlink_nexthop,
+ vpn);
+ bgpevpn_unlink_from_l3vni(vpn);
+ vpn->tenant_vrf_id = tenant_vrf_id;
+ bgpevpn_link_to_l3vni(vpn);
+
+ /*
+ * Resolve all the gateway IP nexthops for this VNI
+ * in new tenant vrf
+ */
+ bgp_evpn_remote_ip_hash_iterate(
+ vpn,
+ (void (*)(struct hash_bucket *, void *))
+ bgp_evpn_remote_ip_hash_link_nexthop,
+ vpn);
+ }
+
+ /* If tunnel endpoint IP has changed, update (and delete prior
+ * type-3 route, if needed.)
+ */
+ if (!IPV4_ADDR_SAME(&vpn->originator_ip, &originator_ip))
+ handle_tunnel_ip_change(bgp, vpn, originator_ip);
+
+ /* Update all routes with new endpoint IP and/or export RT
+ * for VRFs
+ */
+ if (is_vni_live(vpn))
+ update_routes_for_vni(bgp, vpn);
+ } else {
+ /* Create or update as appropriate. */
+ vpn = bgp_evpn_new(bgp, vni, originator_ip, tenant_vrf_id,
+ mcast_grp, svi_ifindex);
+ }
+
+ /* if the VNI is live already, there is nothing more to do */
+ if (is_vni_live(vpn))
+ return 0;
+
+ /* Mark as "live" */
+ SET_FLAG(vpn->flags, VNI_FLAG_LIVE);
+
+ /* tunnel is now active, add tunnel-ip to db */
+ bgp_tip_add(bgp, &originator_ip);
+
+ /* filter routes as nexthop database has changed */
+ bgp_filter_evpn_routes_upon_martian_nh_change(bgp);
+
+ /*
+ * Create EVPN type-3 route and schedule for processing.
+ *
+ * RT-3 only if doing head-end replication
+ */
+ if (bgp_evpn_vni_flood_mode_get(bgp, vpn)
+ == VXLAN_FLOOD_HEAD_END_REPL) {
+ build_evpn_type3_prefix(&p, vpn->originator_ip);
+ if (update_evpn_route(bgp, vpn, &p, 0, 0, NULL)) {
+ flog_err(EC_BGP_EVPN_ROUTE_CREATE,
+ "%u: Type3 route creation failure for VNI %u",
+ bgp->vrf_id, vni);
+ return -1;
+ }
+ }
+
+ /* If we have learnt and retained remote routes (VTEPs, MACs) for this
+ * VNI,
+ * install them.
+ */
+ install_routes_for_vni(bgp, vpn);
+
+ /* If we are advertising gateway mac-ip
+ It needs to be conveyed again to zebra */
+ bgp_zebra_advertise_gw_macip(bgp, vpn->advertise_gw_macip, vpn->vni);
+
+ /* advertise svi mac-ip knob to zebra */
+ bgp_zebra_advertise_svi_macip(bgp, vpn->advertise_svi_macip, vpn->vni);
+
+ return 0;
+}
+
+/*
+ * Handle change in setting for BUM handling. The supported values
+ * are head-end replication and dropping all BUM packets. Any change
+ * should be registered with zebra. Also, if doing head-end replication,
+ * need to advertise local VNIs as EVPN RT-3 wheras, if BUM packets are
+ * to be dropped, the RT-3s must be withdrawn.
+ */
+void bgp_evpn_flood_control_change(struct bgp *bgp)
+{
+ zlog_info("L2VPN EVPN BUM handling is %s",
+ bgp->vxlan_flood_ctrl == VXLAN_FLOOD_HEAD_END_REPL ?
+ "Flooding" : "Flooding Disabled");
+
+ bgp_zebra_vxlan_flood_control(bgp, bgp->vxlan_flood_ctrl);
+ if (bgp->vxlan_flood_ctrl == VXLAN_FLOOD_HEAD_END_REPL)
+ hash_iterate(bgp->vnihash, create_advertise_type3, bgp);
+ else if (bgp->vxlan_flood_ctrl == VXLAN_FLOOD_DISABLED)
+ hash_iterate(bgp->vnihash, delete_withdraw_type3, bgp);
+}
+
+/*
+ * Cleanup EVPN information on disable - Need to delete and withdraw
+ * EVPN routes from peers.
+ */
+void bgp_evpn_cleanup_on_disable(struct bgp *bgp)
+{
+ hash_iterate(bgp->vnihash, (void (*)(struct hash_bucket *,
+ void *))cleanup_vni_on_disable,
+ bgp);
+}
+
+/*
+ * Cleanup EVPN information - invoked at the time of bgpd exit or when the
+ * BGP instance (default) is being freed.
+ */
+void bgp_evpn_cleanup(struct bgp *bgp)
+{
+ hash_iterate(bgp->vnihash,
+ (void (*)(struct hash_bucket *, void *))free_vni_entry,
+ bgp);
+
+ hash_clean(bgp->import_rt_hash, (void (*)(void *))hash_import_rt_free);
+ hash_free(bgp->import_rt_hash);
+ bgp->import_rt_hash = NULL;
+
+ hash_clean(bgp->vrf_import_rt_hash,
+ (void (*)(void *))hash_vrf_import_rt_free);
+ hash_free(bgp->vrf_import_rt_hash);
+ bgp->vrf_import_rt_hash = NULL;
+
+ hash_clean(bgp->vni_svi_hash, (void (*)(void *))hash_evpn_free);
+ hash_free(bgp->vni_svi_hash);
+ bgp->vni_svi_hash = NULL;
+ hash_free(bgp->vnihash);
+ bgp->vnihash = NULL;
+
+ list_delete(&bgp->vrf_import_rtl);
+ list_delete(&bgp->vrf_export_rtl);
+ list_delete(&bgp->l2vnis);
+}
+
+/*
+ * Initialization for EVPN
+ * Create
+ * VNI hash table
+ * hash for RT to VNI
+ */
+void bgp_evpn_init(struct bgp *bgp)
+{
+ bgp->vnihash =
+ hash_create(vni_hash_key_make, vni_hash_cmp, "BGP VNI Hash");
+ bgp->vni_svi_hash =
+ hash_create(vni_svi_hash_key_make, vni_svi_hash_cmp,
+ "BGP VNI hash based on SVI ifindex");
+ bgp->import_rt_hash =
+ hash_create(import_rt_hash_key_make, import_rt_hash_cmp,
+ "BGP Import RT Hash");
+ bgp->vrf_import_rt_hash =
+ hash_create(vrf_import_rt_hash_key_make, vrf_import_rt_hash_cmp,
+ "BGP VRF Import RT Hash");
+ bgp->vrf_import_rtl = list_new();
+ bgp->vrf_import_rtl->cmp =
+ (int (*)(void *, void *))bgp_evpn_route_target_cmp;
+ bgp->vrf_import_rtl->del = bgp_evpn_xxport_delete_ecomm;
+ bgp->vrf_export_rtl = list_new();
+ bgp->vrf_export_rtl->cmp =
+ (int (*)(void *, void *))bgp_evpn_route_target_cmp;
+ bgp->vrf_export_rtl->del = bgp_evpn_xxport_delete_ecomm;
+ bgp->l2vnis = list_new();
+ bgp->l2vnis->cmp = vni_list_cmp;
+ /* By default Duplicate Address Dection is enabled.
+ * Max-moves (N) 5, detection time (M) 180
+ * default action is warning-only
+ * freeze action permanently freezes address,
+ * and freeze time (auto-recovery) is disabled.
+ */
+ if (bgp->evpn_info) {
+ bgp->evpn_info->dup_addr_detect = true;
+ bgp->evpn_info->dad_time = EVPN_DAD_DEFAULT_TIME;
+ bgp->evpn_info->dad_max_moves = EVPN_DAD_DEFAULT_MAX_MOVES;
+ bgp->evpn_info->dad_freeze = false;
+ bgp->evpn_info->dad_freeze_time = 0;
+ /* Initialize zebra vxlan */
+ bgp_zebra_dup_addr_detection(bgp);
+ /* Enable PIP feature by default for bgp vrf instance */
+ if (bgp->inst_type == BGP_INSTANCE_TYPE_VRF) {
+ struct bgp *bgp_default;
+
+ bgp->evpn_info->advertise_pip = true;
+ bgp_default = bgp_get_default();
+ if (bgp_default)
+ bgp->evpn_info->pip_ip = bgp_default->router_id;
+ }
+ }
+
+ /* Default BUM handling is to do head-end replication. */
+ bgp->vxlan_flood_ctrl = VXLAN_FLOOD_HEAD_END_REPL;
+
+ bgp_evpn_nh_init(bgp);
+}
+
+void bgp_evpn_vrf_delete(struct bgp *bgp_vrf)
+{
+ bgp_evpn_unmap_vrf_from_its_rts(bgp_vrf);
+ bgp_evpn_nh_finish(bgp_vrf);
+}
+
+/*
+ * Get the prefixlen of the ip prefix carried within the type5 evpn route.
+ */
+int bgp_evpn_get_type5_prefixlen(const struct prefix *pfx)
+{
+ struct prefix_evpn *evp = (struct prefix_evpn *)pfx;
+
+ if (!pfx || pfx->family != AF_EVPN)
+ return 0;
+
+ if (evp->prefix.route_type != BGP_EVPN_IP_PREFIX_ROUTE)
+ return 0;
+
+ return evp->prefix.prefix_addr.ip_prefix_length;
+}
+
+/*
+ * Should we register nexthop for this EVPN prefix for nexthop tracking?
+ */
+bool bgp_evpn_is_prefix_nht_supported(const struct prefix *pfx)
+{
+ struct prefix_evpn *evp = (struct prefix_evpn *)pfx;
+
+ /*
+ * EVPN routes should be marked as valid only if the nexthop is
+ * reachable. Only if this happens, the route should be imported
+ * (into VNI or VRF routing tables) and/or advertised.
+ * Note: This is currently applied for EVPN type-1, type-2,
+ * type-3, type-4 and type-5 routes.
+ * It may be tweaked later on for other routes, or
+ * even removed completely when all routes are handled.
+ */
+ if (pfx && pfx->family == AF_EVPN
+ && (evp->prefix.route_type == BGP_EVPN_MAC_IP_ROUTE
+ || evp->prefix.route_type == BGP_EVPN_AD_ROUTE
+ || evp->prefix.route_type == BGP_EVPN_ES_ROUTE
+ || evp->prefix.route_type == BGP_EVPN_IMET_ROUTE
+ || evp->prefix.route_type == BGP_EVPN_IP_PREFIX_ROUTE))
+ return true;
+
+ return false;
+}
+
+static void *bgp_evpn_remote_ip_hash_alloc(void *p)
+{
+ const struct evpn_remote_ip *key = (const struct evpn_remote_ip *)p;
+ struct evpn_remote_ip *ip;
+
+ ip = XMALLOC(MTYPE_EVPN_REMOTE_IP, sizeof(struct evpn_remote_ip));
+ *ip = *key;
+ ip->macip_path_list = list_new();
+
+ return ip;
+}
+
+static unsigned int bgp_evpn_remote_ip_hash_key_make(const void *p)
+{
+ const struct evpn_remote_ip *ip = p;
+ const struct ipaddr *addr = &ip->addr;
+
+ if (IS_IPADDR_V4(addr))
+ return jhash_1word(addr->ipaddr_v4.s_addr, 0);
+
+ return jhash2(addr->ipaddr_v6.s6_addr32,
+ array_size(addr->ipaddr_v6.s6_addr32), 0);
+}
+
+static bool bgp_evpn_remote_ip_hash_cmp(const void *p1, const void *p2)
+{
+ const struct evpn_remote_ip *ip1 = p1;
+ const struct evpn_remote_ip *ip2 = p2;
+
+ return !ipaddr_cmp(&ip1->addr, &ip2->addr);
+}
+
+static void bgp_evpn_remote_ip_hash_init(struct bgpevpn *vpn)
+{
+ if (!evpn_resolve_overlay_index())
+ return;
+
+ vpn->remote_ip_hash = hash_create(bgp_evpn_remote_ip_hash_key_make,
+ bgp_evpn_remote_ip_hash_cmp,
+ "BGP EVPN remote IP hash");
+}
+
+static void bgp_evpn_remote_ip_hash_free(struct hash_bucket *bucket, void *args)
+{
+ struct evpn_remote_ip *ip = (struct evpn_remote_ip *)bucket->data;
+ struct bgpevpn *vpn = (struct bgpevpn *)args;
+
+ bgp_evpn_remote_ip_process_nexthops(vpn, &ip->addr, false);
+
+ list_delete(&ip->macip_path_list);
+
+ hash_release(vpn->remote_ip_hash, ip);
+ XFREE(MTYPE_EVPN_REMOTE_IP, ip);
+}
+
+static void bgp_evpn_remote_ip_hash_destroy(struct bgpevpn *vpn)
+{
+ if (!evpn_resolve_overlay_index() || vpn->remote_ip_hash == NULL)
+ return;
+
+ hash_iterate(vpn->remote_ip_hash,
+ (void (*)(struct hash_bucket *, void *))bgp_evpn_remote_ip_hash_free,
+ vpn);
+
+ hash_free(vpn->remote_ip_hash);
+ vpn->remote_ip_hash = NULL;
+}
+
+/* Add a remote MAC/IP route to hash table */
+static void bgp_evpn_remote_ip_hash_add(struct bgpevpn *vpn,
+ struct bgp_path_info *pi)
+{
+ struct evpn_remote_ip tmp;
+ struct evpn_remote_ip *ip;
+ struct prefix_evpn *evp;
+
+ if (!evpn_resolve_overlay_index())
+ return;
+
+ if (pi->type != ZEBRA_ROUTE_BGP || pi->sub_type != BGP_ROUTE_IMPORTED
+ || !CHECK_FLAG(pi->flags, BGP_PATH_VALID))
+ return;
+
+ evp = (struct prefix_evpn *)&pi->net->p;
+
+ if (evp->family != AF_EVPN
+ || evp->prefix.route_type != BGP_EVPN_MAC_IP_ROUTE
+ || is_evpn_prefix_ipaddr_none(evp))
+ return;
+
+ tmp.addr = evp->prefix.macip_addr.ip;
+ ip = hash_lookup(vpn->remote_ip_hash, &tmp);
+ if (ip) {
+ if (listnode_lookup(ip->macip_path_list, pi) != NULL)
+ return;
+ (void)listnode_add(ip->macip_path_list, pi);
+ return;
+ }
+
+ ip = hash_get(vpn->remote_ip_hash, &tmp, bgp_evpn_remote_ip_hash_alloc);
+ (void)listnode_add(ip->macip_path_list, pi);
+
+ bgp_evpn_remote_ip_process_nexthops(vpn, &ip->addr, true);
+}
+
+/* Delete a remote MAC/IP route from hash table */
+static void bgp_evpn_remote_ip_hash_del(struct bgpevpn *vpn,
+ struct bgp_path_info *pi)
+{
+ struct evpn_remote_ip tmp;
+ struct evpn_remote_ip *ip;
+ struct prefix_evpn *evp;
+
+ if (!evpn_resolve_overlay_index())
+ return;
+
+ evp = (struct prefix_evpn *)&pi->net->p;
+
+ if (evp->family != AF_EVPN
+ || evp->prefix.route_type != BGP_EVPN_MAC_IP_ROUTE
+ || is_evpn_prefix_ipaddr_none(evp))
+ return;
+
+ tmp.addr = evp->prefix.macip_addr.ip;
+ ip = hash_lookup(vpn->remote_ip_hash, &tmp);
+ if (ip == NULL)
+ return;
+
+ listnode_delete(ip->macip_path_list, pi);
+
+ if (ip->macip_path_list->count == 0) {
+ bgp_evpn_remote_ip_process_nexthops(vpn, &ip->addr, false);
+ hash_release(vpn->remote_ip_hash, ip);
+ XFREE(MTYPE_EVPN_REMOTE_IP, ip);
+ }
+}
+
+static void bgp_evpn_remote_ip_hash_iterate(struct bgpevpn *vpn,
+ void (*func)(struct hash_bucket *,
+ void *),
+ void *arg)
+{
+ if (!evpn_resolve_overlay_index())
+ return;
+
+ hash_iterate(vpn->remote_ip_hash, func, arg);
+}
+
+static void show_remote_ip_entry(struct hash_bucket *bucket, void *args)
+{
+ char buf[INET6_ADDRSTRLEN];
+ struct listnode *node = NULL;
+ struct bgp_path_info *pi = NULL;
+ struct vty *vty = (struct vty *)args;
+ struct evpn_remote_ip *ip = (struct evpn_remote_ip *)bucket->data;
+
+ vty_out(vty, " Remote IP: %s\n",
+ ipaddr2str(&ip->addr, buf, sizeof(buf)));
+ vty_out(vty, " Linked MAC/IP routes:\n");
+ for (ALL_LIST_ELEMENTS_RO(ip->macip_path_list, node, pi))
+ vty_out(vty, " %pFX\n", &pi->net->p);
+}
+
+void bgp_evpn_show_remote_ip_hash(struct hash_bucket *bucket, void *args)
+{
+ struct bgpevpn *vpn = (struct bgpevpn *)bucket->data;
+ struct vty *vty = (struct vty *)args;
+
+ vty_out(vty, "VNI: %u\n", vpn->vni);
+ bgp_evpn_remote_ip_hash_iterate(
+ vpn,
+ (void (*)(struct hash_bucket *, void *))show_remote_ip_entry,
+ vty);
+ vty_out(vty, "\n");
+}
+
+static void bgp_evpn_remote_ip_hash_link_nexthop(struct hash_bucket *bucket,
+ void *args)
+{
+ struct evpn_remote_ip *ip = (struct evpn_remote_ip *)bucket->data;
+ struct bgpevpn *vpn = (struct bgpevpn *)args;
+
+ bgp_evpn_remote_ip_process_nexthops(vpn, &ip->addr, true);
+}
+
+static void bgp_evpn_remote_ip_hash_unlink_nexthop(struct hash_bucket *bucket,
+ void *args)
+{
+ struct evpn_remote_ip *ip = (struct evpn_remote_ip *)bucket->data;
+ struct bgpevpn *vpn = (struct bgpevpn *)args;
+
+ bgp_evpn_remote_ip_process_nexthops(vpn, &ip->addr, false);
+}
+
+static unsigned int vni_svi_hash_key_make(const void *p)
+{
+ const struct bgpevpn *vpn = p;
+
+ return jhash_1word(vpn->svi_ifindex, 0);
+}
+
+static bool vni_svi_hash_cmp(const void *p1, const void *p2)
+{
+ const struct bgpevpn *vpn1 = p1;
+ const struct bgpevpn *vpn2 = p2;
+
+ return (vpn1->svi_ifindex == vpn2->svi_ifindex);
+}
+
+static struct bgpevpn *bgp_evpn_vni_svi_hash_lookup(struct bgp *bgp,
+ ifindex_t svi)
+{
+ struct bgpevpn *vpn;
+ struct bgpevpn tmp;
+
+ memset(&tmp, 0, sizeof(tmp));
+ tmp.svi_ifindex = svi;
+ vpn = hash_lookup(bgp->vni_svi_hash, &tmp);
+ return vpn;
+}
+
+static void bgp_evpn_link_to_vni_svi_hash(struct bgp *bgp, struct bgpevpn *vpn)
+{
+ if (vpn->svi_ifindex == 0)
+ return;
+
+ (void)hash_get(bgp->vni_svi_hash, vpn, hash_alloc_intern);
+}
+
+static void bgp_evpn_unlink_from_vni_svi_hash(struct bgp *bgp,
+ struct bgpevpn *vpn)
+{
+ if (vpn->svi_ifindex == 0)
+ return;
+
+ hash_release(bgp->vni_svi_hash, vpn);
+}
+
+void bgp_evpn_show_vni_svi_hash(struct hash_bucket *bucket, void *args)
+{
+ struct bgpevpn *evpn = (struct bgpevpn *)bucket->data;
+ struct vty *vty = (struct vty *)args;
+
+ vty_out(vty, "SVI: %u VNI: %u\n", evpn->svi_ifindex, evpn->vni);
+}
+
+/*
+ * This function is called for a bgp_nexthop_cache entry when the nexthop is
+ * gateway IP overlay index.
+ * This function returns true if there is a remote MAC/IP route for the gateway
+ * IP in the EVI of the nexthop SVI.
+ */
+bool bgp_evpn_is_gateway_ip_resolved(struct bgp_nexthop_cache *bnc)
+{
+ struct bgp *bgp_evpn = NULL;
+ struct bgpevpn *vpn = NULL;
+ struct evpn_remote_ip tmp;
+ struct prefix *p;
+
+ if (!evpn_resolve_overlay_index())
+ return false;
+
+ if (!bnc->nexthop || bnc->nexthop->ifindex == 0)
+ return false;
+
+ bgp_evpn = bgp_get_evpn();
+ if (!bgp_evpn)
+ return false;
+
+ /*
+ * Gateway IP is resolved by nht over SVI interface.
+ * Use this SVI to find corresponding EVI(L2 context)
+ */
+ vpn = bgp_evpn_vni_svi_hash_lookup(bgp_evpn, bnc->nexthop->ifindex);
+ if (!vpn)
+ return false;
+
+ if (vpn->bgp_vrf != bnc->bgp)
+ return false;
+
+ /*
+ * Check if the gateway IP is present in the EVI remote_ip_hash table
+ * which stores all the remote IP addresses received via MAC/IP routes
+ * in this EVI
+ */
+ memset(&tmp, 0, sizeof(tmp));
+
+ p = &bnc->prefix;
+ if (p->family == AF_INET) {
+ tmp.addr.ipa_type = IPADDR_V4;
+ memcpy(&(tmp.addr.ipaddr_v4), &(p->u.prefix4),
+ sizeof(struct in_addr));
+ } else if (p->family == AF_INET6) {
+ tmp.addr.ipa_type = IPADDR_V6;
+ memcpy(&(tmp.addr.ipaddr_v6), &(p->u.prefix6),
+ sizeof(struct in6_addr));
+ } else
+ return false;
+
+ if (hash_lookup(vpn->remote_ip_hash, &tmp) == NULL)
+ return false;
+
+ return true;
+}
+
+/* Resolve/Unresolve nexthops when a MAC/IP route is added/deleted */
+static void bgp_evpn_remote_ip_process_nexthops(struct bgpevpn *vpn,
+ struct ipaddr *addr,
+ bool resolve)
+{
+ afi_t afi;
+ struct prefix p;
+ struct bgp_nexthop_cache *bnc;
+ struct bgp_nexthop_cache_head *tree = NULL;
+
+ if (!vpn->bgp_vrf || vpn->svi_ifindex == 0)
+ return;
+
+ memset(&p, 0, sizeof(p));
+
+ if (addr->ipa_type == IPADDR_V4) {
+ afi = AFI_IP;
+ p.family = AF_INET;
+ memcpy(&(p.u.prefix4), &(addr->ipaddr_v4),
+ sizeof(struct in_addr));
+ p.prefixlen = IPV4_MAX_BITLEN;
+ } else if (addr->ipa_type == IPADDR_V6) {
+ afi = AFI_IP6;
+ p.family = AF_INET6;
+ memcpy(&(p.u.prefix6), &(addr->ipaddr_v6),
+ sizeof(struct in6_addr));
+ p.prefixlen = IPV6_MAX_BITLEN;
+ } else
+ return;
+
+ tree = &vpn->bgp_vrf->nexthop_cache_table[afi];
+ bnc = bnc_find(tree, &p, 0, 0);
+
+ if (!bnc || !bnc->is_evpn_gwip_nexthop)
+ return;
+
+ if (!bnc->nexthop || bnc->nexthop->ifindex != vpn->svi_ifindex)
+ return;
+
+ if (BGP_DEBUG(nht, NHT))
+ zlog_debug("%s(%u): vni %u mac/ip %s for NH %pFX",
+ vpn->bgp_vrf->name_pretty, vpn->tenant_vrf_id,
+ vpn->vni, (resolve ? "add" : "delete"),
+ &bnc->prefix);
+
+ /*
+ * MAC/IP route or SVI or tenant vrf being added to EVI.
+ * Set nexthop as valid only if it is already L3 reachable
+ */
+ if (resolve && bnc->flags & BGP_NEXTHOP_EVPN_INCOMPLETE) {
+ bnc->flags &= ~BGP_NEXTHOP_EVPN_INCOMPLETE;
+ bnc->flags |= BGP_NEXTHOP_VALID;
+ bnc->change_flags |= BGP_NEXTHOP_MACIP_CHANGED;
+ evaluate_paths(bnc);
+ }
+
+ /* MAC/IP route or SVI or tenant vrf being deleted from EVI */
+ if (!resolve && bnc->flags & BGP_NEXTHOP_VALID) {
+ bnc->flags &= ~BGP_NEXTHOP_VALID;
+ bnc->flags |= BGP_NEXTHOP_EVPN_INCOMPLETE;
+ bnc->change_flags |= BGP_NEXTHOP_MACIP_CHANGED;
+ evaluate_paths(bnc);
+ }
+}
+
+void bgp_evpn_handle_resolve_overlay_index_set(struct hash_bucket *bucket,
+ void *arg)
+{
+ struct bgpevpn *vpn = (struct bgpevpn *)bucket->data;
+ struct bgp_dest *dest;
+ struct bgp_path_info *pi;
+
+ bgp_evpn_remote_ip_hash_init(vpn);
+
+ for (dest = bgp_table_top(vpn->route_table); dest;
+ dest = bgp_route_next(dest))
+ for (pi = bgp_dest_get_bgp_path_info(dest); pi; pi = pi->next)
+ bgp_evpn_remote_ip_hash_add(vpn, pi);
+}
+
+void bgp_evpn_handle_resolve_overlay_index_unset(struct hash_bucket *bucket,
+ void *arg)
+{
+ struct bgpevpn *vpn = (struct bgpevpn *)bucket->data;
+
+ bgp_evpn_remote_ip_hash_destroy(vpn);
+}
diff --git a/bgpd/bgp_evpn.h b/bgpd/bgp_evpn.h
new file mode 100644
index 0000000..3cbc5af
--- /dev/null
+++ b/bgpd/bgp_evpn.h
@@ -0,0 +1,233 @@
+/* E-VPN header for packet handling
+ * Copyright (C) 2016 6WIND
+ *
+ * This file is part of FRRouting.
+ *
+ * FRRouting is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License as published by the
+ * Free Software Foundation; either version 2, or (at your option) any
+ * later version.
+ *
+ * FRRouting is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program; see the file COPYING; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
+ */
+
+#ifndef _QUAGGA_BGP_EVPN_H
+#define _QUAGGA_BGP_EVPN_H
+
+#include "vxlan.h"
+#include "bgpd.h"
+
+#define EVPN_ROUTE_STRLEN 200 /* Must be >> MAC + IPv6 strings. */
+#define EVPN_AUTORT_VXLAN 0x10000000
+
+#define EVPN_ENABLED(bgp) (bgp)->advertise_all_vni
+static inline int is_evpn_enabled(void)
+{
+ struct bgp *bgp = NULL;
+
+ bgp = bgp_get_evpn();
+ return bgp ? EVPN_ENABLED(bgp) : 0;
+}
+
+static inline void vni2label(vni_t vni, mpls_label_t *label)
+{
+ uint8_t *tag = (uint8_t *)label;
+
+ tag[0] = (vni >> 16) & 0xFF;
+ tag[1] = (vni >> 8) & 0xFF;
+ tag[2] = vni & 0xFF;
+}
+
+static inline vni_t label2vni(mpls_label_t *label)
+{
+ uint8_t *tag = (uint8_t *)label;
+ vni_t vni;
+
+ vni = ((uint32_t)*tag++ << 16);
+ vni |= (uint32_t)*tag++ << 8;
+ vni |= (uint32_t)(*tag & 0xFF);
+
+ return vni;
+}
+
+static inline int advertise_type5_routes(struct bgp *bgp_vrf,
+ afi_t afi)
+{
+ if (!bgp_vrf->l3vni)
+ return 0;
+
+ if ((afi == AFI_IP)
+ && ((CHECK_FLAG(bgp_vrf->af_flags[AFI_L2VPN][SAFI_EVPN],
+ BGP_L2VPN_EVPN_ADV_IPV4_UNICAST))
+ || (CHECK_FLAG(bgp_vrf->af_flags[AFI_L2VPN][SAFI_EVPN],
+ BGP_L2VPN_EVPN_ADV_IPV4_UNICAST_GW_IP))))
+ return 1;
+
+ if ((afi == AFI_IP6)
+ && ((CHECK_FLAG(bgp_vrf->af_flags[AFI_L2VPN][SAFI_EVPN],
+ BGP_L2VPN_EVPN_ADV_IPV6_UNICAST))
+ || (CHECK_FLAG(bgp_vrf->af_flags[AFI_L2VPN][SAFI_EVPN],
+ BGP_L2VPN_EVPN_ADV_IPV6_UNICAST_GW_IP))))
+ return 1;
+
+ return 0;
+}
+
+/* Flag if the route's parent is a EVPN route. */
+static inline struct bgp_path_info *
+get_route_parent_evpn(struct bgp_path_info *ri)
+{
+ struct bgp_path_info *parent_ri;
+
+ /* If not imported (or doesn't have a parent), bail. */
+ if (ri->sub_type != BGP_ROUTE_IMPORTED ||
+ !ri->extra ||
+ !ri->extra->parent)
+ return NULL;
+
+ /* Determine parent recursively */
+ for (parent_ri = ri->extra->parent;
+ parent_ri->extra && parent_ri->extra->parent;
+ parent_ri = parent_ri->extra->parent)
+ ;
+
+ return parent_ri;
+}
+
+/* Flag if the route's parent is a EVPN route. */
+static inline int is_route_parent_evpn(struct bgp_path_info *ri)
+{
+ struct bgp_path_info *parent_ri;
+ struct bgp_table *table;
+ struct bgp_dest *dest;
+
+ parent_ri = get_route_parent_evpn(ri);
+ if (!parent_ri)
+ return 0;
+
+ /* See if of family L2VPN/EVPN */
+ dest = parent_ri->net;
+ if (!dest)
+ return 0;
+ table = bgp_dest_table(dest);
+ if (table &&
+ table->afi == AFI_L2VPN &&
+ table->safi == SAFI_EVPN)
+ return 1;
+ return 0;
+}
+
+/* Flag if the route path's family is EVPN. */
+static inline bool is_pi_family_evpn(struct bgp_path_info *pi)
+{
+ return is_pi_family_matching(pi, AFI_L2VPN, SAFI_EVPN);
+}
+
+/* Flag if the route is injectable into EVPN. This would be either a
+ * non-imported route or a non-EVPN imported route.
+ */
+static inline bool is_route_injectable_into_evpn(struct bgp_path_info *pi)
+{
+ struct bgp_path_info *parent_pi;
+ struct bgp_table *table;
+ struct bgp_dest *dest;
+
+ if (pi->sub_type != BGP_ROUTE_IMPORTED ||
+ !pi->extra ||
+ !pi->extra->parent)
+ return true;
+
+ parent_pi = (struct bgp_path_info *)pi->extra->parent;
+ dest = parent_pi->net;
+ if (!dest)
+ return true;
+ table = bgp_dest_table(dest);
+ if (table &&
+ table->afi == AFI_L2VPN &&
+ table->safi == SAFI_EVPN)
+ return false;
+ return true;
+}
+
+static inline bool evpn_resolve_overlay_index(void)
+{
+ struct bgp *bgp = NULL;
+
+ bgp = bgp_get_evpn();
+ return bgp ? bgp->resolve_overlay_index : false;
+}
+
+extern void bgp_evpn_advertise_type5_route(struct bgp *bgp_vrf,
+ const struct prefix *p,
+ struct attr *src_attr, afi_t afi,
+ safi_t safi);
+extern void bgp_evpn_withdraw_type5_route(struct bgp *bgp_vrf,
+ const struct prefix *p, afi_t afi,
+ safi_t safi);
+extern void bgp_evpn_withdraw_type5_routes(struct bgp *bgp_vrf, afi_t afi,
+ safi_t safi);
+extern void bgp_evpn_advertise_type5_routes(struct bgp *bgp_vrf, afi_t afi,
+ safi_t safi);
+extern void bgp_evpn_vrf_delete(struct bgp *bgp_vrf);
+extern void bgp_evpn_handle_router_id_update(struct bgp *bgp, int withdraw);
+extern char *bgp_evpn_label2str(mpls_label_t *label, uint32_t num_labels,
+ char *buf, int len);
+extern void bgp_evpn_route2json(const struct prefix_evpn *p, json_object *json);
+extern void bgp_evpn_encode_prefix(struct stream *s, const struct prefix *p,
+ const struct prefix_rd *prd,
+ mpls_label_t *label, uint32_t num_labels,
+ struct attr *attr, bool addpath_capable,
+ uint32_t addpath_tx_id);
+extern int bgp_nlri_parse_evpn(struct peer *peer, struct attr *attr,
+ struct bgp_nlri *packet, bool withdraw);
+extern int bgp_evpn_import_route(struct bgp *bgp, afi_t afi, safi_t safi,
+ const struct prefix *p,
+ struct bgp_path_info *ri);
+extern int bgp_evpn_unimport_route(struct bgp *bgp, afi_t afi, safi_t safi,
+ const struct prefix *p,
+ struct bgp_path_info *ri);
+extern int bgp_filter_evpn_routes_upon_martian_nh_change(struct bgp *bgp);
+extern int bgp_evpn_local_macip_del(struct bgp *bgp, vni_t vni,
+ struct ethaddr *mac, struct ipaddr *ip,
+ int state);
+extern int bgp_evpn_local_macip_add(struct bgp *bgp, vni_t vni,
+ struct ethaddr *mac, struct ipaddr *ip,
+ uint8_t flags, uint32_t seq, esi_t *esi);
+extern int bgp_evpn_local_l3vni_add(vni_t vni, vrf_id_t vrf_id,
+ struct ethaddr *rmac,
+ struct ethaddr *vrr_rmac,
+ struct in_addr originator_ip, int filter,
+ ifindex_t svi_ifindex, bool is_anycast_mac);
+extern int bgp_evpn_local_l3vni_del(vni_t vni, vrf_id_t vrf_id);
+extern int bgp_evpn_local_vni_del(struct bgp *bgp, vni_t vni);
+extern int bgp_evpn_local_vni_add(struct bgp *bgp, vni_t vni,
+ struct in_addr originator_ip,
+ vrf_id_t tenant_vrf_id,
+ struct in_addr mcast_grp,
+ ifindex_t svi_ifindex);
+extern void bgp_evpn_flood_control_change(struct bgp *bgp);
+extern void bgp_evpn_cleanup_on_disable(struct bgp *bgp);
+extern void bgp_evpn_cleanup(struct bgp *bgp);
+extern void bgp_evpn_init(struct bgp *bgp);
+extern int bgp_evpn_get_type5_prefixlen(const struct prefix *pfx);
+extern bool bgp_evpn_is_prefix_nht_supported(const struct prefix *pfx);
+extern void update_advertise_vrf_routes(struct bgp *bgp_vrf);
+extern void bgp_evpn_show_remote_ip_hash(struct hash_bucket *bucket,
+ void *args);
+extern void bgp_evpn_show_vni_svi_hash(struct hash_bucket *bucket, void *args);
+extern bool bgp_evpn_is_gateway_ip_resolved(struct bgp_nexthop_cache *bnc);
+extern void
+bgp_evpn_handle_resolve_overlay_index_set(struct hash_bucket *bucket,
+ void *arg);
+extern void
+bgp_evpn_handle_resolve_overlay_index_unset(struct hash_bucket *bucket,
+ void *arg);
+
+#endif /* _QUAGGA_BGP_EVPN_H */
diff --git a/bgpd/bgp_evpn_mh.c b/bgpd/bgp_evpn_mh.c
new file mode 100644
index 0000000..27a5bc9
--- /dev/null
+++ b/bgpd/bgp_evpn_mh.c
@@ -0,0 +1,4979 @@
+/* EVPN Multihoming procedures
+ *
+ * Copyright (C) 2019 Cumulus Networks, Inc.
+ * Anuradha Karuppiah
+ *
+ * This file is part of FRR.
+ *
+ * FRRouting is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License as published by the
+ * Free Software Foundation; either version 2, or (at your option) any
+ * later version.
+ *
+ * FRRouting is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ *
+ */
+
+#include <zebra.h>
+
+#include "command.h"
+#include "filter.h"
+#include "prefix.h"
+#include "log.h"
+#include "memory.h"
+#include "stream.h"
+#include "hash.h"
+#include "jhash.h"
+#include "zclient.h"
+
+#include "lib/printfrr.h"
+
+#include "bgpd/bgp_attr_evpn.h"
+#include "bgpd/bgpd.h"
+#include "bgpd/bgp_table.h"
+#include "bgpd/bgp_route.h"
+#include "bgpd/bgp_attr.h"
+#include "bgpd/bgp_mplsvpn.h"
+#include "bgpd/bgp_evpn.h"
+#include "bgpd/bgp_evpn_private.h"
+#include "bgpd/bgp_evpn_mh.h"
+#include "bgpd/bgp_ecommunity.h"
+#include "bgpd/bgp_encap_types.h"
+#include "bgpd/bgp_debug.h"
+#include "bgpd/bgp_errors.h"
+#include "bgpd/bgp_aspath.h"
+#include "bgpd/bgp_zebra.h"
+#include "bgpd/bgp_addpath.h"
+#include "bgpd/bgp_label.h"
+#include "bgpd/bgp_nht.h"
+#include "bgpd/bgp_mpath.h"
+#include "bgpd/bgp_trace.h"
+
+static void bgp_evpn_local_es_down(struct bgp *bgp,
+ struct bgp_evpn_es *es);
+static void bgp_evpn_local_type1_evi_route_del(struct bgp *bgp,
+ struct bgp_evpn_es *es);
+static struct bgp_evpn_es_vtep *bgp_evpn_es_vtep_add(struct bgp *bgp,
+ struct bgp_evpn_es *es,
+ struct in_addr vtep_ip,
+ bool esr, uint8_t df_alg,
+ uint16_t df_pref);
+static void bgp_evpn_es_vtep_del(struct bgp *bgp,
+ struct bgp_evpn_es *es, struct in_addr vtep_ip, bool esr);
+static void bgp_evpn_es_cons_checks_pend_add(struct bgp_evpn_es *es);
+static void bgp_evpn_es_cons_checks_pend_del(struct bgp_evpn_es *es);
+static struct bgp_evpn_es_evi *
+bgp_evpn_local_es_evi_do_del(struct bgp_evpn_es_evi *es_evi);
+static uint32_t bgp_evpn_es_get_active_vtep_cnt(struct bgp_evpn_es *es);
+static void bgp_evpn_l3nhg_update_on_vtep_chg(struct bgp_evpn_es *es);
+static struct bgp_evpn_es *bgp_evpn_es_new(struct bgp *bgp, const esi_t *esi);
+static void bgp_evpn_es_free(struct bgp_evpn_es *es, const char *caller);
+static void bgp_evpn_path_es_unlink(struct bgp_path_es_info *es_info);
+static void bgp_evpn_mac_update_on_es_local_chg(struct bgp_evpn_es *es,
+ bool is_local);
+
+esi_t zero_esi_buf, *zero_esi = &zero_esi_buf;
+static void bgp_evpn_run_consistency_checks(struct thread *t);
+static void bgp_evpn_path_nh_info_free(struct bgp_path_evpn_nh_info *nh_info);
+static void bgp_evpn_path_nh_unlink(struct bgp_path_evpn_nh_info *nh_info);
+
+/******************************************************************************
+ * per-ES (Ethernet Segment) routing table
+ *
+ * Following routes are added to the ES's routing table -
+ * 1. Local and remote ESR (Type-4)
+ * 2. Local EAD-per-ES (Type-1).
+ *
+ * Key for these routes is {ESI, VTEP-IP} so the path selection is practically
+ * a no-op i.e. all paths lead to same VTEP-IP (i.e. result in the same VTEP
+ * being added to same ES).
+ *
+ * Note the following routes go into the VNI routing table (instead of the
+ * ES routing table) -
+ * 1. Remote EAD-per-ES
+ * 2. Local and remote EAD-per-EVI
+ */
+
+/* Calculate the best path for a multi-homing (Type-1 or Type-4) route
+ * installed in the ES's routing table.
+ */
+static int bgp_evpn_es_route_select_install(struct bgp *bgp,
+ struct bgp_evpn_es *es,
+ struct bgp_dest *dest)
+{
+ int ret = 0;
+ afi_t afi = AFI_L2VPN;
+ safi_t safi = SAFI_EVPN;
+ struct bgp_path_info *old_select; /* old best */
+ struct bgp_path_info *new_select; /* new best */
+ struct bgp_path_info_pair old_and_new;
+
+ /* Compute the best path. */
+ 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 the best path hasn't changed - see if something needs to be
+ * updated
+ */
+ if (old_select && old_select == new_select
+ && old_select->type == ZEBRA_ROUTE_BGP
+ && old_select->sub_type == BGP_ROUTE_IMPORTED
+ && !CHECK_FLAG(dest->flags, BGP_NODE_USER_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)) {
+ bgp_evpn_es_vtep_add(bgp, es, old_select->attr->nexthop,
+ true /*esr*/,
+ old_select->attr->df_alg,
+ old_select->attr->df_pref);
+ }
+ UNSET_FLAG(old_select->flags, BGP_PATH_MULTIPATH_CHG);
+ bgp_zebra_clear_route_change_flags(dest);
+ return ret;
+ }
+
+ /* If the user did a "clear" this flag will be set */
+ UNSET_FLAG(dest->flags, BGP_NODE_USER_CLEAR);
+
+ /* bestpath has changed; update relevant fields and install or uninstall
+ * into the zebra RIB.
+ */
+ if (old_select || new_select)
+ bgp_bump_version(dest);
+
+ if (old_select)
+ bgp_path_info_unset_flag(dest, old_select, BGP_PATH_SELECTED);
+ if (new_select) {
+ 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);
+ }
+
+ if (new_select && new_select->type == ZEBRA_ROUTE_BGP
+ && new_select->sub_type == BGP_ROUTE_IMPORTED) {
+ bgp_evpn_es_vtep_add(bgp, es, new_select->attr->nexthop,
+ true /*esr */, new_select->attr->df_alg,
+ new_select->attr->df_pref);
+ } else {
+ if (old_select && old_select->type == ZEBRA_ROUTE_BGP
+ && old_select->sub_type == BGP_ROUTE_IMPORTED)
+ bgp_evpn_es_vtep_del(
+ bgp, es, old_select->attr->nexthop,
+ true /*esr*/);
+ }
+
+ /* Clear any route change flags. */
+ bgp_zebra_clear_route_change_flags(dest);
+
+ /* 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 ret;
+}
+
+/* Install Type-1/Type-4 route entry in the per-ES routing table */
+static int bgp_evpn_es_route_install(struct bgp *bgp,
+ struct bgp_evpn_es *es, struct prefix_evpn *p,
+ struct bgp_path_info *parent_pi)
+{
+ int ret = 0;
+ struct bgp_dest *dest = NULL;
+ struct bgp_path_info *pi = NULL;
+ struct attr *attr_new = NULL;
+
+ /* Create (or fetch) route within the VNI.
+ * NOTE: There is no RD here.
+ */
+ dest = bgp_node_get(es->route_table, (struct prefix *)p);
+
+ /* Check if route entry is already present. */
+ for (pi = bgp_dest_get_bgp_path_info(dest); pi; pi = pi->next)
+ if (pi->extra &&
+ (struct bgp_path_info *)pi->extra->parent == parent_pi)
+ break;
+
+ if (!pi) {
+ /* Add (or update) attribute to hash. */
+ attr_new = bgp_attr_intern(parent_pi->attr);
+
+ /* Create new route with its attribute. */
+ pi = info_make(parent_pi->type, BGP_ROUTE_IMPORTED, 0,
+ parent_pi->peer, attr_new, dest);
+ SET_FLAG(pi->flags, BGP_PATH_VALID);
+ bgp_path_info_extra_get(pi);
+ pi->extra->parent = bgp_path_info_lock(parent_pi);
+ bgp_dest_lock_node((struct bgp_dest *)parent_pi->net);
+ bgp_path_info_add(dest, pi);
+ } else {
+ if (attrhash_cmp(pi->attr, parent_pi->attr)
+ && !CHECK_FLAG(pi->flags, BGP_PATH_REMOVED)) {
+ bgp_dest_unlock_node(dest);
+ return 0;
+ }
+ /* The attribute has changed. */
+ /* Add (or update) attribute to hash. */
+ attr_new = bgp_attr_intern(parent_pi->attr);
+
+ /* Restore route, if needed. */
+ if (CHECK_FLAG(pi->flags, BGP_PATH_REMOVED))
+ bgp_path_info_restore(dest, pi);
+
+ /* Mark if nexthop has changed. */
+ if (!IPV4_ADDR_SAME(&pi->attr->nexthop, &attr_new->nexthop))
+ SET_FLAG(pi->flags, BGP_PATH_IGP_CHANGED);
+
+ /* Unintern existing, set to new. */
+ bgp_attr_unintern(&pi->attr);
+ pi->attr = attr_new;
+ pi->uptime = monotime(NULL);
+ }
+
+ /* Perform route selection and update zebra, if required. */
+ ret = bgp_evpn_es_route_select_install(bgp, es, dest);
+
+ bgp_dest_unlock_node(dest);
+
+ return ret;
+}
+
+/* Uninstall Type-1/Type-4 route entry from the ES routing table */
+static int bgp_evpn_es_route_uninstall(struct bgp *bgp, struct bgp_evpn_es *es,
+ struct prefix_evpn *p, struct bgp_path_info *parent_pi)
+{
+ int ret;
+ struct bgp_dest *dest;
+ struct bgp_path_info *pi;
+
+ if (!es->route_table)
+ return 0;
+
+ /* Locate route within the ESI.
+ * NOTE: There is no RD here.
+ */
+ dest = bgp_node_lookup(es->route_table, (struct prefix *)p);
+ if (!dest)
+ return 0;
+
+ /* Find matching route entry. */
+ for (pi = bgp_dest_get_bgp_path_info(dest); pi; pi = pi->next)
+ if (pi->extra
+ && (struct bgp_path_info *)pi->extra->parent ==
+ parent_pi)
+ break;
+
+ if (!pi) {
+ bgp_dest_unlock_node(dest);
+ return 0;
+ }
+
+ /* Mark entry for deletion */
+ bgp_path_info_delete(dest, pi);
+
+ /* Perform route selection and update zebra, if required. */
+ ret = bgp_evpn_es_route_select_install(bgp, es, dest);
+
+ /* Unlock route node. */
+ bgp_dest_unlock_node(dest);
+
+ return ret;
+}
+
+/* Install or unistall a Type-4 route in the per-ES routing table */
+int bgp_evpn_es_route_install_uninstall(struct bgp *bgp, struct bgp_evpn_es *es,
+ afi_t afi, safi_t safi, struct prefix_evpn *evp,
+ struct bgp_path_info *pi, int install)
+{
+ int ret = 0;
+
+ if (install)
+ ret = bgp_evpn_es_route_install(bgp, es, evp, pi);
+ else
+ ret = bgp_evpn_es_route_uninstall(bgp, es, evp, pi);
+
+ if (ret) {
+ flog_err(
+ EC_BGP_EVPN_FAIL,
+ "%u: Failed to %s EVPN %s route in ESI %s",
+ bgp->vrf_id,
+ install ? "install" : "uninstall",
+ "ES", es->esi_str);
+ return ret;
+ }
+ return 0;
+}
+
+/* Delete (and withdraw) local routes for specified ES from global and ES table.
+ * Also remove all remote routes from the per ES table. Invoked when ES
+ * is deleted.
+ */
+static void bgp_evpn_es_route_del_all(struct bgp *bgp, struct bgp_evpn_es *es)
+{
+ struct bgp_dest *dest;
+ struct bgp_path_info *pi, *nextpi;
+
+ /* de-activate the ES */
+ bgp_evpn_local_es_down(bgp, es);
+ bgp_evpn_local_type1_evi_route_del(bgp, es);
+
+ /* Walk this ES's routing table and delete all routes. */
+ for (dest = bgp_table_top(es->route_table); dest;
+ dest = bgp_route_next(dest)) {
+ for (pi = bgp_dest_get_bgp_path_info(dest);
+ (pi != NULL) && (nextpi = pi->next, 1); pi = nextpi) {
+ bgp_path_info_delete(dest, pi);
+ bgp_path_info_reap(dest, pi);
+ }
+ }
+}
+
+/*****************************************************************************
+ * Base APIs for creating MH routes (Type-1 or Type-4) on local ethernet
+ * segment updates.
+ */
+
+/* create or update local EVPN type1/type4 route entry.
+ *
+ * This could be in -
+ * the ES table if ESR/EAD-ES (or)
+ * the VNI table if EAD-EVI (or)
+ * the global table if ESR/EAD-ES/EAD-EVI
+ *
+ * Note: vpn is applicable only to EAD-EVI routes (NULL for EAD-ES and
+ * ESR).
+ */
+int bgp_evpn_mh_route_update(struct bgp *bgp, struct bgp_evpn_es *es,
+ struct bgpevpn *vpn, afi_t afi, safi_t safi,
+ struct bgp_dest *dest, struct attr *attr,
+ struct bgp_path_info **ri, int *route_changed)
+{
+ struct bgp_path_info *tmp_pi = NULL;
+ struct bgp_path_info *local_pi = NULL; /* local route entry if any */
+ struct bgp_path_info *remote_pi = NULL; /* remote route entry if any */
+ struct attr *attr_new = NULL;
+ struct prefix_evpn *evp;
+
+ *ri = NULL;
+ evp = (struct prefix_evpn *)bgp_dest_get_prefix(dest);
+ *route_changed = 1;
+
+ /* locate the local and remote entries if any */
+ for (tmp_pi = bgp_dest_get_bgp_path_info(dest); tmp_pi;
+ tmp_pi = tmp_pi->next) {
+ if (tmp_pi->peer == bgp->peer_self
+ && tmp_pi->type == ZEBRA_ROUTE_BGP
+ && tmp_pi->sub_type == BGP_ROUTE_STATIC)
+ local_pi = tmp_pi;
+ if (tmp_pi->type == ZEBRA_ROUTE_BGP
+ && tmp_pi->sub_type == BGP_ROUTE_IMPORTED
+ && CHECK_FLAG(tmp_pi->flags, BGP_PATH_VALID))
+ remote_pi = tmp_pi;
+ }
+
+ /* we don't expect to see a remote_pi at this point as
+ * an ES route has {esi, vtep_ip} as the key in the ES-rt-table
+ * in the VNI-rt-table.
+ */
+ if (remote_pi) {
+ flog_err(
+ EC_BGP_ES_INVALID,
+ "%u ERROR: local es route for ESI: %s vtep %pI4 also learnt from remote",
+ bgp->vrf_id, es ? es->esi_str : "Null",
+ es ? &es->originator_ip : NULL);
+ return -1;
+ }
+
+ /* create or update the entry */
+ if (!local_pi) {
+
+ /* Add or update attribute to hash */
+ attr_new = bgp_attr_intern(attr);
+
+ /* Create new route with its attribute. */
+ tmp_pi = info_make(ZEBRA_ROUTE_BGP, BGP_ROUTE_STATIC, 0,
+ bgp->peer_self, attr_new, dest);
+ SET_FLAG(tmp_pi->flags, BGP_PATH_VALID);
+
+ if (evp->prefix.route_type == BGP_EVPN_AD_ROUTE) {
+ bgp_path_info_extra_get(tmp_pi);
+ tmp_pi->extra->num_labels = 1;
+ if (vpn)
+ vni2label(vpn->vni, &tmp_pi->extra->label[0]);
+ else
+ tmp_pi->extra->label[0] = 0;
+ }
+
+ /* add the newly created path to the route-node */
+ bgp_path_info_add(dest, tmp_pi);
+ } else {
+ tmp_pi = local_pi;
+ if (attrhash_cmp(tmp_pi->attr, attr)
+ && !CHECK_FLAG(tmp_pi->flags, BGP_PATH_REMOVED))
+ *route_changed = 0;
+ else {
+ /* The attribute has changed.
+ * Add (or update) attribute to hash.
+ */
+ attr_new = bgp_attr_intern(attr);
+ bgp_path_info_set_flag(dest, tmp_pi,
+ BGP_PATH_ATTR_CHANGED);
+
+ /* Restore route, if needed. */
+ if (CHECK_FLAG(tmp_pi->flags, BGP_PATH_REMOVED))
+ bgp_path_info_restore(dest, tmp_pi);
+
+ /* Unintern existing, set to new. */
+ bgp_attr_unintern(&tmp_pi->attr);
+ tmp_pi->attr = attr_new;
+ tmp_pi->uptime = monotime(NULL);
+ }
+ }
+
+ if (*route_changed) {
+ if (BGP_DEBUG(evpn_mh, EVPN_MH_RT))
+ zlog_debug(
+ "local ES %s vni %u route-type %s nexthop %pI4 updated",
+ es ? es->esi_str : "Null", vpn ? vpn->vni : 0,
+ evp->prefix.route_type == BGP_EVPN_ES_ROUTE
+ ? "esr"
+ : (vpn ? "ead-evi" : "ead-es"),
+ &attr->mp_nexthop_global_in);
+ }
+
+ /* Return back the route entry. */
+ *ri = tmp_pi;
+ return 0;
+}
+
+/* Delete local EVPN ESR (type-4) and EAD (type-1) route
+ *
+ * Note: vpn is applicable only to EAD-EVI routes (NULL for EAD-ES and
+ * ESR).
+ */
+static int bgp_evpn_mh_route_delete(struct bgp *bgp, struct bgp_evpn_es *es,
+ struct bgpevpn *vpn,
+ struct bgp_evpn_es_frag *es_frag,
+ struct prefix_evpn *p)
+{
+ afi_t afi = AFI_L2VPN;
+ safi_t safi = SAFI_EVPN;
+ struct bgp_path_info *pi;
+ struct bgp_dest *dest = NULL; /* dest in esi table */
+ struct bgp_dest *global_dest = NULL; /* dest in global table */
+ struct bgp_table *rt_table;
+ struct prefix_rd *prd;
+
+ if (vpn) {
+ rt_table = vpn->route_table;
+ prd = &vpn->prd;
+ } else {
+ rt_table = es->route_table;
+ prd = &es_frag->prd;
+ }
+
+ /* First, locate the route node within the ESI or VNI.
+ * If it doesn't exist, ther is nothing to do.
+ * Note: there is no RD here.
+ */
+ dest = bgp_node_lookup(rt_table, (struct prefix *)p);
+ if (!dest)
+ return 0;
+
+ if (BGP_DEBUG(evpn_mh, EVPN_MH_RT))
+ zlog_debug(
+ "local ES %s vni %u route-type %s nexthop %pI4 delete",
+ es->esi_str, vpn ? vpn->vni : 0,
+ p->prefix.route_type == BGP_EVPN_ES_ROUTE
+ ? "esr"
+ : (vpn ? "ead-evi" : "ead-es"),
+ &es->originator_ip);
+
+ /* Next, locate route node in the global EVPN routing table.
+ * Note that this table is a 2-level tree (RD-level + Prefix-level)
+ */
+ global_dest =
+ bgp_global_evpn_node_lookup(bgp->rib[afi][safi], afi, safi,
+ (const struct prefix_evpn *)p, prd);
+ if (global_dest) {
+
+ /* Delete route entry in the global EVPN table. */
+ delete_evpn_route_entry(bgp, afi, safi, global_dest, &pi);
+
+ /* Schedule for processing - withdraws to peers happen from
+ * this table.
+ */
+ if (pi)
+ bgp_process(bgp, global_dest, afi, safi);
+ bgp_dest_unlock_node(global_dest);
+ }
+
+ /*
+ * Delete route entry in the ESI or VNI routing table.
+ * This can just be removed.
+ */
+ delete_evpn_route_entry(bgp, afi, safi, dest, &pi);
+ if (pi)
+ bgp_path_info_reap(dest, pi);
+ bgp_dest_unlock_node(dest);
+ return 0;
+}
+
+/*
+ * This function is called when the VNI RD changes.
+ * Delete all EAD/EVI local routes for this VNI from the global routing table.
+ * These routes are scheduled for withdraw from peers.
+ */
+int delete_global_ead_evi_routes(struct bgp *bgp, struct bgpevpn *vpn)
+{
+ afi_t afi;
+ safi_t safi;
+ struct bgp_dest *rdrn, *rn;
+ struct bgp_table *table;
+ struct bgp_path_info *pi;
+
+ afi = AFI_L2VPN;
+ safi = SAFI_EVPN;
+
+ /* Find the RD node for the VNI in the global table */
+ rdrn = bgp_node_lookup(bgp->rib[afi][safi], (struct prefix *)&vpn->prd);
+ if (rdrn && bgp_dest_has_bgp_path_info_data(rdrn)) {
+ table = bgp_dest_get_bgp_table_info(rdrn);
+
+ /*
+ * Iterate over all the routes in this table and delete EAD/EVI
+ * routes
+ */
+ for (rn = bgp_table_top(table); rn; rn = bgp_route_next(rn)) {
+ struct prefix_evpn *evp = (struct prefix_evpn *)&rn->p;
+
+ if (evp->prefix.route_type != BGP_EVPN_AD_ROUTE)
+ continue;
+
+ delete_evpn_route_entry(bgp, afi, safi, rn, &pi);
+ if (pi)
+ bgp_process(bgp, rn, afi, safi);
+ }
+ }
+
+ /* Unlock RD node. */
+ if (rdrn)
+ bgp_dest_unlock_node(rdrn);
+
+ return 0;
+}
+
+/*****************************************************************************
+ * Ethernet Segment (Type-4) Routes
+ * ESRs are used for DF election. Currently service-carving described in
+ * RFC 7432 is NOT supported. Instead preference based DF election is
+ * used by default.
+ * Reference: draft-ietf-bess-evpn-pref-df
+ */
+/* Build extended community for EVPN ES (type-4) route */
+static void bgp_evpn_type4_route_extcomm_build(struct bgp_evpn_es *es,
+ struct attr *attr)
+{
+ struct ecommunity ecom_encap;
+ struct ecommunity ecom_es_rt;
+ struct ecommunity ecom_df;
+ struct ecommunity_val eval;
+ struct ecommunity_val eval_es_rt;
+ struct ecommunity_val eval_df;
+ bgp_encap_types tnl_type;
+ struct ethaddr mac;
+
+ /* Encap */
+ tnl_type = BGP_ENCAP_TYPE_VXLAN;
+ memset(&ecom_encap, 0, sizeof(ecom_encap));
+ encode_encap_extcomm(tnl_type, &eval);
+ ecom_encap.size = 1;
+ ecom_encap.unit_size = ECOMMUNITY_SIZE;
+ ecom_encap.val = (uint8_t *)eval.val;
+ bgp_attr_set_ecommunity(attr, ecommunity_dup(&ecom_encap));
+
+ /* ES import RT */
+ memset(&mac, 0, sizeof(mac));
+ memset(&ecom_es_rt, 0, sizeof(ecom_es_rt));
+ es_get_system_mac(&es->esi, &mac);
+ encode_es_rt_extcomm(&eval_es_rt, &mac);
+ ecom_es_rt.size = 1;
+ ecom_es_rt.unit_size = ECOMMUNITY_SIZE;
+ ecom_es_rt.val = (uint8_t *)eval_es_rt.val;
+ bgp_attr_set_ecommunity(
+ attr,
+ ecommunity_merge(bgp_attr_get_ecommunity(attr), &ecom_es_rt));
+
+ /* DF election extended community */
+ memset(&ecom_df, 0, sizeof(ecom_df));
+ encode_df_elect_extcomm(&eval_df, es->df_pref);
+ ecom_df.size = 1;
+ ecom_df.val = (uint8_t *)eval_df.val;
+ bgp_attr_set_ecommunity(
+ attr,
+ ecommunity_merge(bgp_attr_get_ecommunity(attr), &ecom_df));
+}
+
+/* Create or update local type-4 route */
+static int bgp_evpn_type4_route_update(struct bgp *bgp,
+ struct bgp_evpn_es *es, struct prefix_evpn *p)
+{
+ int ret = 0;
+ int route_changed = 0;
+ afi_t afi = AFI_L2VPN;
+ safi_t safi = SAFI_EVPN;
+ struct attr attr;
+ struct attr *attr_new = NULL;
+ struct bgp_dest *dest = NULL;
+ struct bgp_path_info *pi = NULL;
+
+ memset(&attr, 0, sizeof(attr));
+
+ /* Build path-attribute for this route. */
+ bgp_attr_default_set(&attr, bgp, BGP_ORIGIN_IGP);
+ attr.nexthop = es->originator_ip;
+ attr.mp_nexthop_global_in = es->originator_ip;
+ attr.mp_nexthop_len = BGP_ATTR_NHLEN_IPV4;
+
+ /* Set up extended community. */
+ bgp_evpn_type4_route_extcomm_build(es, &attr);
+
+ /* First, create (or fetch) route node within the ESI. */
+ /* NOTE: There is no RD here. */
+ dest = bgp_node_get(es->route_table, (struct prefix *)p);
+
+ /* Create or update route entry. */
+ ret = bgp_evpn_mh_route_update(bgp, es, NULL, afi, safi, dest, &attr,
+ &pi, &route_changed);
+ if (ret != 0)
+ flog_err(
+ EC_BGP_ES_INVALID,
+ "%u ERROR: Failed to updated ES route ESI: %s VTEP %pI4",
+ bgp->vrf_id, es->esi_str, &es->originator_ip);
+
+ assert(pi);
+ attr_new = pi->attr;
+
+ /* Perform route selection;
+ * this is just to set the flags correctly
+ * as local route in the ES always wins.
+ */
+ bgp_evpn_es_route_select_install(bgp, es, dest);
+ bgp_dest_unlock_node(dest);
+
+ /* If this is a new route or some attribute has changed, export the
+ * route to the global table. The route will be advertised to peers
+ * from there. Note that this table is a 2-level tree (RD-level +
+ * Prefix-level) similar to L3VPN routes.
+ */
+ if (route_changed) {
+ struct bgp_path_info *global_pi;
+
+ dest = bgp_global_evpn_node_get(bgp->rib[afi][safi], afi, safi,
+ p, &es->es_base_frag->prd);
+ bgp_evpn_mh_route_update(bgp, es, NULL, afi, safi, dest,
+ attr_new, &global_pi, &route_changed);
+
+ /* Schedule for processing and unlock node. */
+ bgp_process(bgp, dest, afi, safi);
+ bgp_dest_unlock_node(dest);
+ }
+
+ /* Unintern temporary. */
+ aspath_unintern(&attr.aspath);
+ return 0;
+}
+
+/* Delete local type-4 route */
+static int bgp_evpn_type4_route_delete(struct bgp *bgp,
+ struct bgp_evpn_es *es, struct prefix_evpn *p)
+{
+ if (!es->es_base_frag)
+ return -1;
+
+ return bgp_evpn_mh_route_delete(bgp, es, NULL /* l2vni */,
+ es->es_base_frag, p);
+}
+
+/* Process remote/received EVPN type-4 route (advertise or withdraw) */
+int bgp_evpn_type4_route_process(struct peer *peer, afi_t afi, safi_t safi,
+ struct attr *attr, uint8_t *pfx, int psize,
+ uint32_t addpath_id)
+{
+ int ret;
+ esi_t esi;
+ uint8_t ipaddr_len;
+ struct in_addr vtep_ip;
+ struct prefix_rd prd;
+ struct prefix_evpn p;
+
+ /* Type-4 route should be either 23 or 35 bytes
+ * RD (8), ESI (10), ip-len (1), ip (4 or 16)
+ */
+ if (psize != BGP_EVPN_TYPE4_V4_PSIZE &&
+ psize != BGP_EVPN_TYPE4_V6_PSIZE) {
+ flog_err(EC_BGP_EVPN_ROUTE_INVALID,
+ "%u:%s - Rx EVPN Type-4 NLRI with invalid length %d",
+ peer->bgp->vrf_id, peer->host, psize);
+ return -1;
+ }
+
+ /* Make prefix_rd */
+ prd.family = AF_UNSPEC;
+ prd.prefixlen = 64;
+ memcpy(&prd.val, pfx, RD_BYTES);
+ pfx += RD_BYTES;
+
+ /* get the ESI */
+ memcpy(&esi, pfx, ESI_BYTES);
+ pfx += ESI_BYTES;
+
+
+ /* Get the IP. */
+ ipaddr_len = *pfx++;
+ if (ipaddr_len == IPV4_MAX_BITLEN) {
+ memcpy(&vtep_ip, pfx, IPV4_MAX_BYTELEN);
+ } else {
+ flog_err(
+ EC_BGP_EVPN_ROUTE_INVALID,
+ "%u:%s - Rx EVPN Type-4 NLRI with unsupported IP address length %d",
+ peer->bgp->vrf_id, peer->host, ipaddr_len);
+ return -1;
+ }
+
+ build_evpn_type4_prefix(&p, &esi, vtep_ip);
+ /* Process the route. */
+ if (attr) {
+ ret = bgp_update(peer, (struct prefix *)&p, addpath_id, attr,
+ afi, safi, ZEBRA_ROUTE_BGP, BGP_ROUTE_NORMAL,
+ &prd, NULL, 0, 0, NULL);
+ } else {
+ ret = bgp_withdraw(peer, (struct prefix *)&p, addpath_id, attr,
+ afi, safi, ZEBRA_ROUTE_BGP, BGP_ROUTE_NORMAL,
+ &prd, NULL, 0, NULL);
+ }
+ return ret;
+}
+
+/* Check if a prefix belongs to the local ES */
+static bool bgp_evpn_type4_prefix_match(struct prefix_evpn *p,
+ struct bgp_evpn_es *es)
+{
+ return (p->prefix.route_type == BGP_EVPN_ES_ROUTE) &&
+ !memcmp(&p->prefix.es_addr.esi, &es->esi, sizeof(esi_t));
+}
+
+/* Import remote ESRs on local ethernet segment add */
+static int bgp_evpn_type4_remote_routes_import(struct bgp *bgp,
+ struct bgp_evpn_es *es, bool install)
+{
+ int ret;
+ afi_t afi;
+ safi_t safi;
+ struct bgp_dest *rd_dest, *dest;
+ struct bgp_table *table;
+ struct bgp_path_info *pi;
+
+ afi = AFI_L2VPN;
+ safi = SAFI_EVPN;
+
+ /* Walk entire global routing table and evaluate routes which could be
+ * imported into this Ethernet Segment.
+ */
+ for (rd_dest = bgp_table_top(bgp->rib[afi][safi]); rd_dest;
+ rd_dest = bgp_route_next(rd_dest)) {
+ table = bgp_dest_get_bgp_table_info(rd_dest);
+ if (!table)
+ continue;
+
+ for (dest = bgp_table_top(table); dest;
+ dest = bgp_route_next(dest)) {
+ struct prefix_evpn *evp =
+ (struct prefix_evpn *)bgp_dest_get_prefix(dest);
+
+ for (pi = bgp_dest_get_bgp_path_info(dest); pi;
+ pi = pi->next) {
+ /*
+ * Consider "valid" remote routes applicable for
+ * this ES.
+ */
+ if (!(CHECK_FLAG(pi->flags, BGP_PATH_VALID)
+ && pi->type == ZEBRA_ROUTE_BGP
+ && pi->sub_type == BGP_ROUTE_NORMAL))
+ continue;
+
+ if (!bgp_evpn_type4_prefix_match(evp, es))
+ continue;
+
+ if (install)
+ ret = bgp_evpn_es_route_install(
+ bgp, es, evp, pi);
+ else
+ ret = bgp_evpn_es_route_uninstall(
+ bgp, es, evp, pi);
+
+ if (ret) {
+ flog_err(
+ EC_BGP_EVPN_FAIL,
+ "Failed to %s EVPN %pFX route in ESI %s",
+ install ? "install"
+ : "uninstall",
+ evp, es->esi_str);
+
+ bgp_dest_unlock_node(rd_dest);
+ bgp_dest_unlock_node(dest);
+ return ret;
+ }
+ }
+ }
+ }
+ return 0;
+}
+
+/*****************************************************************************
+ * Ethernet Auto Discovery (EAD/Type-1) route handling
+ * There are two types of EAD routes -
+ * 1. EAD-per-ES - Key: {ESI, ET=0xffffffff}
+ * 2. EAD-per-EVI - Key: {ESI, ET=0}
+ */
+
+/* Extended communities associated with EAD-per-ES */
+static void
+bgp_evpn_type1_es_route_extcomm_build(struct bgp_evpn_es_frag *es_frag,
+ struct attr *attr)
+{
+ struct ecommunity ecom_encap;
+ struct ecommunity ecom_esi_label;
+ struct ecommunity_val eval;
+ struct ecommunity_val eval_esi_label;
+ bgp_encap_types tnl_type;
+ struct listnode *evi_node, *rt_node;
+ struct ecommunity *ecom;
+ struct bgp_evpn_es_evi *es_evi;
+
+ /* Encap */
+ tnl_type = BGP_ENCAP_TYPE_VXLAN;
+ memset(&ecom_encap, 0, sizeof(ecom_encap));
+ encode_encap_extcomm(tnl_type, &eval);
+ ecom_encap.size = 1;
+ ecom_encap.unit_size = ECOMMUNITY_SIZE;
+ ecom_encap.val = (uint8_t *)eval.val;
+ bgp_attr_set_ecommunity(attr, ecommunity_dup(&ecom_encap));
+
+ /* ESI label */
+ encode_esi_label_extcomm(&eval_esi_label,
+ false /*single_active*/);
+ ecom_esi_label.size = 1;
+ ecom_esi_label.unit_size = ECOMMUNITY_SIZE;
+ ecom_esi_label.val = (uint8_t *)eval_esi_label.val;
+ bgp_attr_set_ecommunity(attr,
+ ecommunity_merge(bgp_attr_get_ecommunity(attr),
+ &ecom_esi_label));
+
+ /* Add export RTs for all L2-VNIs associated with this ES */
+ /* XXX - suppress EAD-ES advertisment if there are no EVIs associated
+ * with it.
+ */
+ if (listcount(bgp_mh_info->ead_es_export_rtl)) {
+ for (ALL_LIST_ELEMENTS_RO(bgp_mh_info->ead_es_export_rtl,
+ rt_node, ecom))
+ bgp_attr_set_ecommunity(
+ attr, ecommunity_merge(attr->ecommunity, ecom));
+ } else {
+ for (ALL_LIST_ELEMENTS_RO(es_frag->es_evi_frag_list, evi_node,
+ es_evi)) {
+ if (!CHECK_FLAG(es_evi->flags, BGP_EVPNES_EVI_LOCAL))
+ continue;
+ for (ALL_LIST_ELEMENTS_RO(es_evi->vpn->export_rtl,
+ rt_node, ecom))
+ bgp_attr_set_ecommunity(
+ attr, ecommunity_merge(attr->ecommunity,
+ ecom));
+ }
+ }
+}
+
+/* Extended communities associated with EAD-per-EVI */
+static void bgp_evpn_type1_evi_route_extcomm_build(struct bgp_evpn_es *es,
+ struct bgpevpn *vpn, struct attr *attr)
+{
+ struct ecommunity ecom_encap;
+ struct ecommunity_val eval;
+ bgp_encap_types tnl_type;
+ struct listnode *rt_node;
+ struct ecommunity *ecom;
+
+ /* Encap */
+ tnl_type = BGP_ENCAP_TYPE_VXLAN;
+ memset(&ecom_encap, 0, sizeof(ecom_encap));
+ encode_encap_extcomm(tnl_type, &eval);
+ ecom_encap.size = 1;
+ ecom_encap.unit_size = ECOMMUNITY_SIZE;
+ ecom_encap.val = (uint8_t *)eval.val;
+ bgp_attr_set_ecommunity(attr, ecommunity_dup(&ecom_encap));
+
+ /* Add export RTs for the L2-VNI */
+ for (ALL_LIST_ELEMENTS_RO(vpn->export_rtl, rt_node, ecom))
+ bgp_attr_set_ecommunity(
+ attr,
+ ecommunity_merge(bgp_attr_get_ecommunity(attr), ecom));
+}
+
+/* Update EVPN EAD (type-1) route -
+ * vpn - valid for EAD-EVI routes and NULL for EAD-ES routes
+ */
+static int bgp_evpn_type1_route_update(struct bgp *bgp, struct bgp_evpn_es *es,
+ struct bgpevpn *vpn,
+ struct bgp_evpn_es_frag *es_frag,
+ struct prefix_evpn *p)
+{
+ int ret = 0;
+ afi_t afi = AFI_L2VPN;
+ safi_t safi = SAFI_EVPN;
+ struct attr attr;
+ struct attr *attr_new = NULL;
+ struct bgp_dest *dest = NULL;
+ struct bgp_path_info *pi = NULL;
+ int route_changed = 0;
+ struct prefix_rd *global_rd;
+
+ memset(&attr, 0, sizeof(attr));
+
+ /* Build path-attribute for this route. */
+ bgp_attr_default_set(&attr, bgp, BGP_ORIGIN_IGP);
+ attr.nexthop = es->originator_ip;
+ attr.mp_nexthop_global_in = es->originator_ip;
+ attr.mp_nexthop_len = BGP_ATTR_NHLEN_IPV4;
+
+ if (vpn) {
+ /* EAD-EVI route update */
+ /* MPLS label */
+ vni2label(vpn->vni, &(attr.label));
+
+ /* Set up extended community */
+ bgp_evpn_type1_evi_route_extcomm_build(es, vpn, &attr);
+
+ /* First, create (or fetch) route node within the VNI. */
+ dest = bgp_node_get(vpn->route_table, (struct prefix *)p);
+
+ /* Create or update route entry. */
+ ret = bgp_evpn_mh_route_update(bgp, es, vpn, afi, safi, dest,
+ &attr, &pi, &route_changed);
+ if (ret != 0)
+ flog_err(
+ EC_BGP_ES_INVALID,
+ "%u Failed to update EAD-EVI route ESI: %s VNI %u VTEP %pI4",
+ bgp->vrf_id, es->esi_str, vpn->vni,
+ &es->originator_ip);
+ global_rd = &vpn->prd;
+ } else {
+ /* EAD-ES route update */
+ /* MPLS label is 0 for EAD-ES route */
+
+ /* Set up extended community */
+ bgp_evpn_type1_es_route_extcomm_build(es_frag, &attr);
+
+ /* First, create (or fetch) route node within the ES. */
+ /* NOTE: There is no RD here. */
+ /* XXX: fragment ID must be included as a part of the prefix. */
+ dest = bgp_node_get(es->route_table, (struct prefix *)p);
+
+ /* Create or update route entry. */
+ ret = bgp_evpn_mh_route_update(bgp, es, vpn, afi, safi, dest,
+ &attr, &pi, &route_changed);
+ if (ret != 0) {
+ flog_err(
+ EC_BGP_ES_INVALID,
+ "%u ERROR: Failed to updated EAD-ES route ESI: %s VTEP %pI4",
+ bgp->vrf_id, es->esi_str, &es->originator_ip);
+ }
+ global_rd = &es_frag->prd;
+ }
+
+
+ assert(pi);
+ attr_new = pi->attr;
+
+ /* Perform route selection;
+ * this is just to set the flags correctly as local route in
+ * the ES always wins.
+ */
+ evpn_route_select_install(bgp, vpn, dest);
+ bgp_dest_unlock_node(dest);
+
+ /* If this is a new route or some attribute has changed, export the
+ * route to the global table. The route will be advertised to peers
+ * from there. Note that this table is a 2-level tree (RD-level +
+ * Prefix-level) similar to L3VPN routes.
+ */
+ if (route_changed) {
+ struct bgp_path_info *global_pi;
+
+ dest = bgp_global_evpn_node_get(bgp->rib[afi][safi], afi, safi,
+ p, global_rd);
+ bgp_evpn_mh_route_update(bgp, es, vpn, afi, safi, dest,
+ attr_new, &global_pi, &route_changed);
+
+ /* Schedule for processing and unlock node. */
+ bgp_process(bgp, dest, afi, safi);
+ bgp_dest_unlock_node(dest);
+ }
+
+ /* Unintern temporary. */
+ aspath_unintern(&attr.aspath);
+ return 0;
+}
+
+/*
+ * This function is called when the export RT for a VNI changes.
+ * Update all type-1 local routes for this VNI from VNI/ES tables and the global
+ * table and advertise these routes to peers.
+ */
+
+static void bgp_evpn_ead_es_route_update(struct bgp *bgp,
+ struct bgp_evpn_es *es)
+{
+ struct listnode *node;
+ struct bgp_evpn_es_frag *es_frag;
+ struct prefix_evpn p;
+
+ build_evpn_type1_prefix(&p, BGP_EVPN_AD_ES_ETH_TAG, &es->esi,
+ es->originator_ip);
+ for (ALL_LIST_ELEMENTS_RO(es->es_frag_list, node, es_frag)) {
+ if (!listcount(es_frag->es_evi_frag_list))
+ continue;
+
+ p.prefix.ead_addr.frag_id = es_frag->rd_id;
+ if (bgp_evpn_type1_route_update(bgp, es, NULL, es_frag, &p))
+ flog_err(
+ EC_BGP_EVPN_ROUTE_CREATE,
+ "EAD-ES route creation failure for ESI %s frag %u",
+ es->esi_str, es_frag->rd_id);
+ }
+}
+
+static void bgp_evpn_ead_evi_route_update(struct bgp *bgp,
+ struct bgp_evpn_es *es,
+ struct bgpevpn *vpn,
+ struct prefix_evpn *p)
+{
+ if (bgp_evpn_type1_route_update(bgp, es, vpn, NULL, p))
+ flog_err(EC_BGP_EVPN_ROUTE_CREATE,
+ "EAD-EVI route creation failure for ESI %s VNI %u",
+ es->esi_str, vpn->vni);
+}
+
+void update_type1_routes_for_evi(struct bgp *bgp, struct bgpevpn *vpn)
+{
+ struct prefix_evpn p;
+ struct bgp_evpn_es *es;
+ struct bgp_evpn_es_evi *es_evi;
+
+
+ RB_FOREACH (es_evi, bgp_es_evi_rb_head, &vpn->es_evi_rb_tree) {
+ es = es_evi->es;
+
+ if (es_evi->vpn != vpn)
+ continue;
+
+ /* Update EAD-ES */
+ bgp_evpn_ead_es_route_update(bgp, es);
+
+ /* Update EAD-EVI */
+ if (CHECK_FLAG(es->flags, BGP_EVPNES_ADV_EVI)) {
+ build_evpn_type1_prefix(&p, BGP_EVPN_AD_EVI_ETH_TAG,
+ &es->esi, es->originator_ip);
+ bgp_evpn_ead_evi_route_update(bgp, es, vpn, &p);
+ }
+ }
+}
+
+/* Delete local Type-1 route */
+static void bgp_evpn_ead_es_route_delete(struct bgp *bgp,
+ struct bgp_evpn_es *es)
+{
+ struct listnode *node;
+ struct bgp_evpn_es_frag *es_frag;
+ struct prefix_evpn p;
+
+ build_evpn_type1_prefix(&p, BGP_EVPN_AD_ES_ETH_TAG, &es->esi,
+ es->originator_ip);
+ for (ALL_LIST_ELEMENTS_RO(es->es_frag_list, node, es_frag)) {
+ p.prefix.ead_addr.frag_id = es_frag->rd_id;
+ bgp_evpn_mh_route_delete(bgp, es, NULL, es_frag, &p);
+ }
+}
+
+static int bgp_evpn_ead_evi_route_delete(struct bgp *bgp,
+ struct bgp_evpn_es *es,
+ struct bgpevpn *vpn,
+ struct prefix_evpn *p)
+{
+ return bgp_evpn_mh_route_delete(bgp, es, vpn, NULL, p);
+}
+
+/* Generate EAD-EVI for all VNIs */
+static void bgp_evpn_local_type1_evi_route_add(struct bgp *bgp,
+ struct bgp_evpn_es *es)
+{
+ struct listnode *evi_node;
+ struct prefix_evpn p;
+ struct bgp_evpn_es_evi *es_evi;
+
+ /* EAD-per-EVI routes have been suppressed */
+ if (!bgp_mh_info->ead_evi_tx)
+ return;
+
+ if (CHECK_FLAG(es->flags, BGP_EVPNES_ADV_EVI))
+ /* EAD-EVI route add for this ES is already done */
+ return;
+
+ SET_FLAG(es->flags, BGP_EVPNES_ADV_EVI);
+ build_evpn_type1_prefix(&p, BGP_EVPN_AD_EVI_ETH_TAG,
+ &es->esi, es->originator_ip);
+
+ for (ALL_LIST_ELEMENTS_RO(es->es_evi_list, evi_node, es_evi)) {
+ if (!CHECK_FLAG(es_evi->flags, BGP_EVPNES_EVI_LOCAL))
+ continue;
+ bgp_evpn_ead_evi_route_update(bgp, es, es_evi->vpn, &p);
+ }
+}
+
+/*
+ * Withdraw EAD-EVI for all VNIs
+ */
+static void bgp_evpn_local_type1_evi_route_del(struct bgp *bgp,
+ struct bgp_evpn_es *es)
+{
+ struct listnode *evi_node;
+ struct prefix_evpn p;
+ struct bgp_evpn_es_evi *es_evi;
+
+ /* Delete and withdraw locally learnt EAD-EVI route */
+ if (!CHECK_FLAG(es->flags, BGP_EVPNES_ADV_EVI))
+ /* EAD-EVI route has not been advertised for this ES */
+ return;
+
+ UNSET_FLAG(es->flags, BGP_EVPNES_ADV_EVI);
+ build_evpn_type1_prefix(&p, BGP_EVPN_AD_EVI_ETH_TAG,
+ &es->esi, es->originator_ip);
+ for (ALL_LIST_ELEMENTS_RO(es->es_evi_list, evi_node, es_evi)) {
+ if (!CHECK_FLAG(es_evi->flags, BGP_EVPNES_EVI_LOCAL))
+ continue;
+ if (bgp_evpn_mh_route_delete(bgp, es, es_evi->vpn, NULL, &p))
+ flog_err(EC_BGP_EVPN_ROUTE_CREATE,
+ "%u: Type4 route creation failure for ESI %s",
+ bgp->vrf_id, es->esi_str);
+ }
+}
+
+/*
+ * Process received EVPN type-1 route (advertise or withdraw).
+ */
+int bgp_evpn_type1_route_process(struct peer *peer, afi_t afi, safi_t safi,
+ struct attr *attr, uint8_t *pfx, int psize,
+ uint32_t addpath_id)
+{
+ int ret;
+ struct prefix_rd prd;
+ esi_t esi;
+ uint32_t eth_tag;
+ mpls_label_t label;
+ struct in_addr vtep_ip;
+ struct prefix_evpn p;
+
+ if (psize != BGP_EVPN_TYPE1_PSIZE) {
+ flog_err(EC_BGP_EVPN_ROUTE_INVALID,
+ "%u:%s - Rx EVPN Type-1 NLRI with invalid length %d",
+ peer->bgp->vrf_id, peer->host, psize);
+ return -1;
+ }
+
+ /* Make prefix_rd */
+ prd.family = AF_UNSPEC;
+ prd.prefixlen = 64;
+ memcpy(&prd.val, pfx, RD_BYTES);
+ pfx += RD_BYTES;
+
+ /* get the ESI */
+ memcpy(&esi, pfx, ESI_BYTES);
+ pfx += ESI_BYTES;
+
+ /* Copy Ethernet Tag */
+ memcpy(&eth_tag, pfx, EVPN_ETH_TAG_BYTES);
+ eth_tag = ntohl(eth_tag);
+ pfx += EVPN_ETH_TAG_BYTES;
+
+ memcpy(&label, pfx, BGP_LABEL_BYTES);
+
+ /* EAD route prefix doesn't include the nexthop in the global
+ * table
+ */
+ vtep_ip.s_addr = INADDR_ANY;
+ build_evpn_type1_prefix(&p, eth_tag, &esi, vtep_ip);
+ /* Process the route. */
+ if (attr) {
+ ret = bgp_update(peer, (struct prefix *)&p, addpath_id, attr,
+ afi, safi, ZEBRA_ROUTE_BGP, BGP_ROUTE_NORMAL,
+ &prd, NULL, 0, 0, NULL);
+ } else {
+ ret = bgp_withdraw(peer, (struct prefix *)&p, addpath_id, attr,
+ afi, safi, ZEBRA_ROUTE_BGP, BGP_ROUTE_NORMAL,
+ &prd, NULL, 0, NULL);
+ }
+ return ret;
+}
+
+void bgp_evpn_mh_config_ead_export_rt(struct bgp *bgp,
+ struct ecommunity *ecomcfg, bool del)
+{
+ struct listnode *node, *nnode, *node_to_del;
+ struct ecommunity *ecom;
+ struct bgp_evpn_es *es;
+
+ if (del) {
+ if (ecomcfg == NULL) {
+ /* Reset to default and process all routes. */
+ for (ALL_LIST_ELEMENTS(bgp_mh_info->ead_es_export_rtl,
+ node, nnode, ecom)) {
+ ecommunity_free(&ecom);
+ list_delete_node(bgp_mh_info->ead_es_export_rtl,
+ node);
+ }
+ }
+
+ /* Delete a specific export RT */
+ else {
+ node_to_del = NULL;
+
+ for (ALL_LIST_ELEMENTS(bgp_mh_info->ead_es_export_rtl,
+ node, nnode, ecom)) {
+ if (ecommunity_match(ecom, ecomcfg)) {
+ ecommunity_free(&ecom);
+ node_to_del = node;
+ break;
+ }
+ }
+
+ assert(node_to_del);
+ list_delete_node(bgp_mh_info->ead_es_export_rtl,
+ node_to_del);
+ }
+ } else {
+ listnode_add_sort(bgp_mh_info->ead_es_export_rtl, ecomcfg);
+ }
+
+ if (BGP_DEBUG(evpn_mh, EVPN_MH_RT))
+ zlog_debug("local ES del/re-add EAD route on export RT change");
+ /*
+ * walk through all active ESs withdraw the old EAD and
+ * generate a new one
+ */
+ RB_FOREACH (es, bgp_es_rb_head, &bgp_mh_info->es_rb_tree) {
+ if (!bgp_evpn_is_es_local(es) ||
+ !bgp_evpn_local_es_is_active(es))
+ continue;
+
+ if (BGP_DEBUG(evpn_mh, EVPN_MH_RT))
+ zlog_debug(
+ "local ES %s del/re-add EAD route on export RT change",
+ es->esi_str);
+
+ /*
+ * withdraw EAD-ES. XXX - this should technically not be
+ * needed; can be removed after testing
+ */
+ bgp_evpn_ead_es_route_delete(bgp, es);
+
+ /* generate EAD-ES */
+ bgp_evpn_ead_es_route_update(bgp, es);
+ }
+}
+
+/*****************************************************************************/
+/* Ethernet Segment Management
+ * 1. Ethernet Segment is a collection of links attached to the same
+ * server (MHD) or switch (MHN)
+ * 2. An Ethernet Segment can span multiple PEs and is identified by the
+ * 10-byte ES-ID.
+ * 3. Local ESs are configured in zebra and sent to BGP
+ * 4. Remote ESs are created by BGP when one or more ES-EVIs reference it i.e.
+ * created on first reference and release on last de-reference
+ * 5. An ES can be both local and remote. Infact most local ESs are expected
+ * to have an ES peer.
+ */
+
+/* A list of remote VTEPs is maintained for each ES. This list includes -
+ * 1. VTEPs for which we have imported the ESR i.e. ES-peers
+ * 2. VTEPs that have an "active" ES-EVI VTEP i.e. EAD-per-ES and EAD-per-EVI
+ * have been imported into one or more VNIs
+ */
+static int bgp_evpn_es_vtep_cmp(void *p1, void *p2)
+{
+ const struct bgp_evpn_es_vtep *es_vtep1 = p1;
+ const struct bgp_evpn_es_vtep *es_vtep2 = p2;
+
+ return es_vtep1->vtep_ip.s_addr - es_vtep2->vtep_ip.s_addr;
+}
+
+static struct bgp_evpn_es_vtep *bgp_evpn_es_vtep_new(struct bgp_evpn_es *es,
+ struct in_addr vtep_ip)
+{
+ struct bgp_evpn_es_vtep *es_vtep;
+
+ es_vtep = XCALLOC(MTYPE_BGP_EVPN_ES_VTEP, sizeof(*es_vtep));
+
+ es_vtep->es = es;
+ es_vtep->vtep_ip.s_addr = vtep_ip.s_addr;
+ inet_ntop(AF_INET, &es_vtep->vtep_ip, es_vtep->vtep_str,
+ sizeof(es_vtep->vtep_str));
+ listnode_init(&es_vtep->es_listnode, es_vtep);
+ listnode_add_sort(es->es_vtep_list, &es_vtep->es_listnode);
+
+ return es_vtep;
+}
+
+static void bgp_evpn_es_vtep_free(struct bgp_evpn_es_vtep *es_vtep)
+{
+ struct bgp_evpn_es *es = es_vtep->es;
+
+ if (CHECK_FLAG(es_vtep->flags, BGP_EVPNES_VTEP_ESR) ||
+ es_vtep->evi_cnt)
+ /* as long as there is some reference we can't free it */
+ return;
+
+ list_delete_node(es->es_vtep_list, &es_vtep->es_listnode);
+ XFREE(MTYPE_BGP_EVPN_ES_VTEP, es_vtep);
+}
+
+/* check if VTEP is already part of the list */
+static struct bgp_evpn_es_vtep *bgp_evpn_es_vtep_find(struct bgp_evpn_es *es,
+ struct in_addr vtep_ip)
+{
+ struct listnode *node = NULL;
+ struct bgp_evpn_es_vtep *es_vtep;
+
+ for (ALL_LIST_ELEMENTS_RO(es->es_vtep_list, node, es_vtep)) {
+ if (es_vtep->vtep_ip.s_addr == vtep_ip.s_addr)
+ return es_vtep;
+ }
+ return NULL;
+}
+
+/* Send the remote ES to zebra for NHG programming */
+static int bgp_zebra_send_remote_es_vtep(struct bgp *bgp,
+ struct bgp_evpn_es_vtep *es_vtep, bool add)
+{
+ struct bgp_evpn_es *es = es_vtep->es;
+ struct stream *s;
+ uint32_t flags = 0;
+
+ /* Check socket. */
+ if (!zclient || zclient->sock < 0)
+ return 0;
+
+ /* Don't try to register if Zebra doesn't know of this instance. */
+ if (!IS_BGP_INST_KNOWN_TO_ZEBRA(bgp)) {
+ if (BGP_DEBUG(zebra, ZEBRA))
+ zlog_debug("No zebra instance, not installing remote es %s",
+ es->esi_str);
+ return 0;
+ }
+
+ if (es_vtep->flags & BGP_EVPNES_VTEP_ESR)
+ flags |= ZAPI_ES_VTEP_FLAG_ESR_RXED;
+
+ s = zclient->obuf;
+ stream_reset(s);
+
+ zclient_create_header(s,
+ add ? ZEBRA_REMOTE_ES_VTEP_ADD : ZEBRA_REMOTE_ES_VTEP_DEL,
+ bgp->vrf_id);
+ stream_put(s, &es->esi, sizeof(esi_t));
+ stream_put_ipv4(s, es_vtep->vtep_ip.s_addr);
+ if (add) {
+ stream_putl(s, flags);
+ stream_putc(s, es_vtep->df_alg);
+ stream_putw(s, es_vtep->df_pref);
+ }
+
+ stream_putw_at(s, 0, stream_get_endp(s));
+
+ if (BGP_DEBUG(evpn_mh, EVPN_MH_ES))
+ zlog_debug("Tx %s Remote ESI %s VTEP %pI4", add ? "ADD" : "DEL",
+ es->esi_str, &es_vtep->vtep_ip);
+
+ frrtrace(3, frr_bgp, evpn_mh_vtep_zsend, add, es, es_vtep);
+
+ return zclient_send_message(zclient);
+}
+
+static void bgp_evpn_es_vtep_re_eval_active(struct bgp *bgp,
+ struct bgp_evpn_es_vtep *es_vtep,
+ bool param_change)
+{
+ bool old_active;
+ bool new_active;
+
+ old_active = CHECK_FLAG(es_vtep->flags, BGP_EVPNES_VTEP_ACTIVE);
+ /* currently we need an active EVI reference to use the VTEP as
+ * a nexthop. this may change...
+ */
+ if (es_vtep->evi_cnt)
+ SET_FLAG(es_vtep->flags, BGP_EVPNES_VTEP_ACTIVE);
+ else
+ UNSET_FLAG(es_vtep->flags, BGP_EVPNES_VTEP_ACTIVE);
+
+ new_active = CHECK_FLAG(es_vtep->flags, BGP_EVPNES_VTEP_ACTIVE);
+
+ if ((old_active != new_active) || (new_active && param_change)) {
+
+ if (BGP_DEBUG(evpn_mh, EVPN_MH_ES))
+ zlog_debug("es %s vtep %pI4 %s df %u/%u",
+ es_vtep->es->esi_str, &es_vtep->vtep_ip,
+ new_active ? "active" : "inactive",
+ es_vtep->df_alg, es_vtep->df_pref);
+
+ /* send remote ES to zebra */
+ bgp_zebra_send_remote_es_vtep(bgp, es_vtep, new_active);
+
+ /* The NHG is updated first for efficient failover handling.
+ * Note the NHG can be de-activated while there are bgp
+ * routes referencing it. Zebra is capable of handling that
+ * elegantly by holding the NHG till all routes using it are
+ * removed.
+ */
+ bgp_evpn_l3nhg_update_on_vtep_chg(es_vtep->es);
+ /* queue up the es for background consistency checks */
+ bgp_evpn_es_cons_checks_pend_add(es_vtep->es);
+ }
+}
+
+static struct bgp_evpn_es_vtep *bgp_evpn_es_vtep_add(struct bgp *bgp,
+ struct bgp_evpn_es *es,
+ struct in_addr vtep_ip,
+ bool esr, uint8_t df_alg,
+ uint16_t df_pref)
+{
+ struct bgp_evpn_es_vtep *es_vtep;
+ bool param_change = false;
+
+ es_vtep = bgp_evpn_es_vtep_find(es, vtep_ip);
+
+ if (!es_vtep)
+ es_vtep = bgp_evpn_es_vtep_new(es, vtep_ip);
+
+ if (BGP_DEBUG(evpn_mh, EVPN_MH_ES))
+ zlog_debug("es %s vtep %pI4 add %s df %u/%u",
+ es_vtep->es->esi_str, &es_vtep->vtep_ip,
+ esr ? "esr" : "ead", df_alg, df_pref);
+
+ if (esr) {
+ SET_FLAG(es_vtep->flags, BGP_EVPNES_VTEP_ESR);
+ if ((es_vtep->df_pref != df_pref)
+ || (es_vtep->df_alg != df_alg)) {
+ param_change = true;
+ es_vtep->df_pref = df_pref;
+ es_vtep->df_alg = df_alg;
+ }
+ } else {
+ ++es_vtep->evi_cnt;
+ }
+
+ bgp_evpn_es_vtep_re_eval_active(bgp, es_vtep, param_change);
+
+ return es_vtep;
+}
+
+static void bgp_evpn_es_vtep_do_del(struct bgp *bgp,
+ struct bgp_evpn_es_vtep *es_vtep, bool esr)
+{
+ bool param_change = false;
+
+ if (BGP_DEBUG(evpn_mh, EVPN_MH_ES))
+ zlog_debug("es %s vtep %pI4 del %s", es_vtep->es->esi_str,
+ &es_vtep->vtep_ip, esr ? "esr" : "ead");
+ if (esr) {
+ UNSET_FLAG(es_vtep->flags, BGP_EVPNES_VTEP_ESR);
+ if (es_vtep->df_pref || es_vtep->df_alg) {
+ param_change = true;
+ es_vtep->df_pref = 0;
+ es_vtep->df_alg = 0;
+ }
+ } else {
+ if (es_vtep->evi_cnt)
+ --es_vtep->evi_cnt;
+ }
+
+ bgp_evpn_es_vtep_re_eval_active(bgp, es_vtep, param_change);
+ bgp_evpn_es_vtep_free(es_vtep);
+}
+
+static void bgp_evpn_es_vtep_del(struct bgp *bgp,
+ struct bgp_evpn_es *es, struct in_addr vtep_ip, bool esr)
+{
+ struct bgp_evpn_es_vtep *es_vtep;
+
+ es_vtep = bgp_evpn_es_vtep_find(es, vtep_ip);
+ if (es_vtep)
+ bgp_evpn_es_vtep_do_del(bgp, es_vtep, esr);
+}
+
+/********************** ES MAC-IP paths *************************************
+ * 1. Local MAC-IP routes in the VNI routing table are linked to the
+ * destination ES (macip_evi_path_list) for efficient updates on ES oper
+ * state changes.
+ * 2. Non-local MAC-IP routes in the global routing table are linked to
+ * the detination for efficient updates on -
+ * a. VTEP add/del - this results in a L3NHG update.
+ * b. ES-VRF add/del - this may result in the host route being migrated to
+ * L3NHG or vice versa (flat multipath list).
+ ****************************************************************************/
+static void bgp_evpn_path_es_info_free(struct bgp_path_es_info *es_info)
+{
+ bgp_evpn_path_es_unlink(es_info);
+ XFREE(MTYPE_BGP_EVPN_PATH_ES_INFO, es_info);
+}
+
+void bgp_evpn_path_mh_info_free(struct bgp_path_mh_info *mh_info)
+{
+ if (mh_info->es_info)
+ bgp_evpn_path_es_info_free(mh_info->es_info);
+ if (mh_info->nh_info)
+ bgp_evpn_path_nh_info_free(mh_info->nh_info);
+ XFREE(MTYPE_BGP_EVPN_PATH_MH_INFO, mh_info);
+}
+
+static struct bgp_path_es_info *
+bgp_evpn_path_es_info_new(struct bgp_path_info *pi, vni_t vni)
+{
+ struct bgp_path_info_extra *e;
+ struct bgp_path_mh_info *mh_info;
+ struct bgp_path_es_info *es_info;
+
+ e = bgp_path_info_extra_get(pi);
+
+ /* If mh_info doesn't exist allocate it */
+ mh_info = e->mh_info;
+ if (!mh_info)
+ e->mh_info = mh_info = XCALLOC(MTYPE_BGP_EVPN_PATH_MH_INFO,
+ sizeof(struct bgp_path_mh_info));
+
+ /* If es_info doesn't exist allocate it */
+ es_info = mh_info->es_info;
+ if (!es_info) {
+ mh_info->es_info = es_info =
+ XCALLOC(MTYPE_BGP_EVPN_PATH_ES_INFO,
+ sizeof(struct bgp_path_es_info));
+ es_info->vni = vni;
+ es_info->pi = pi;
+ }
+
+ return es_info;
+}
+
+static void bgp_evpn_path_es_unlink(struct bgp_path_es_info *es_info)
+{
+ struct bgp_evpn_es *es = es_info->es;
+ struct bgp_path_info *pi;
+
+ if (!es)
+ return;
+
+ pi = es_info->pi;
+ if (BGP_DEBUG(evpn_mh, EVPN_MH_RT))
+ zlog_debug("vni %u path %pFX unlinked from es %s", es_info->vni,
+ &pi->net->p, es->esi_str);
+
+ if (es_info->vni)
+ list_delete_node(es->macip_evi_path_list,
+ &es_info->es_listnode);
+ else
+ list_delete_node(es->macip_global_path_list,
+ &es_info->es_listnode);
+
+ es_info->es = NULL;
+
+ /* if there are no other references against the ES it
+ * needs to be freed
+ */
+ bgp_evpn_es_free(es, __func__);
+
+ /* Note we don't free the path es_info on unlink; it will be freed up
+ * along with the path.
+ */
+}
+
+void bgp_evpn_path_es_link(struct bgp_path_info *pi, vni_t vni, esi_t *esi)
+{
+ struct bgp_path_es_info *es_info;
+ struct bgp_evpn_es *es;
+ struct bgp *bgp_evpn;
+
+ es_info = (pi->extra && pi->extra->mh_info)
+ ? pi->extra->mh_info->es_info
+ : NULL;
+ /* if the esi is zero just unlink the path from the old es */
+ if (!esi || !memcmp(esi, zero_esi, sizeof(*esi))) {
+ if (es_info)
+ bgp_evpn_path_es_unlink(es_info);
+ return;
+ }
+
+ bgp_evpn = bgp_get_evpn();
+ if (!bgp_evpn)
+ return;
+
+ /* setup es_info against the path if it doesn't aleady exist */
+ if (!es_info)
+ es_info = bgp_evpn_path_es_info_new(pi, vni);
+
+ /* find-create ES */
+ es = bgp_evpn_es_find(esi);
+ if (!es)
+ es = bgp_evpn_es_new(bgp_evpn, esi);
+
+ /* dup check */
+ if (es_info->es == es)
+ return;
+
+ /* unlink old ES if any */
+ bgp_evpn_path_es_unlink(es_info);
+
+ if (BGP_DEBUG(evpn_mh, EVPN_MH_RT))
+ zlog_debug("vni %u path %pFX linked to es %s", vni, &pi->net->p,
+ es->esi_str);
+
+ /* link mac-ip path to the new destination ES */
+ es_info->es = es;
+ listnode_init(&es_info->es_listnode, es_info);
+ if (es_info->vni)
+ listnode_add(es->macip_evi_path_list, &es_info->es_listnode);
+ else
+ listnode_add(es->macip_global_path_list, &es_info->es_listnode);
+}
+
+static bool bgp_evpn_is_macip_path(struct bgp_path_info *pi)
+{
+ struct prefix_evpn *evp;
+
+ /* Only MAC-IP routes need to be linked (MAC-only routes can be
+ * skipped) as these lists are maintained for managing
+ * host routes in the tenant VRF
+ */
+ evp = (struct prefix_evpn *)&pi->net->p;
+ return is_evpn_prefix_ipaddr_v4(evp) || is_evpn_prefix_ipaddr_v6(evp);
+}
+
+/* When a remote ES is added to a VRF, routes using that as
+ * a destination need to be migrated to a L3NHG or viceversa.
+ * This is done indirectly by re-attempting an install of the
+ * route in the associated VRFs. As a part of the VRF install use
+ * of l3 NHG is evaluated and this results in the
+ * attr.es_flag ATTR_ES_L3_NHG_USE being set or cleared.
+ */
+static void
+bgp_evpn_es_path_update_on_es_vrf_chg(struct bgp_evpn_es_vrf *es_vrf,
+ const char *reason)
+{
+ struct listnode *node;
+ struct bgp_path_es_info *es_info;
+ struct bgp_path_info *pi;
+ struct bgp_evpn_es *es = es_vrf->es;
+
+ if (!bgp_mh_info->host_routes_use_l3nhg)
+ return;
+
+ if (BGP_DEBUG(evpn_mh, EVPN_MH_RT))
+ zlog_debug("update paths linked to es %s on es-vrf %s %s",
+ es->esi_str, es_vrf->bgp_vrf->name, reason);
+
+ for (ALL_LIST_ELEMENTS_RO(es->macip_global_path_list, node, es_info)) {
+ pi = es_info->pi;
+
+ if (!bgp_evpn_is_macip_path(pi))
+ continue;
+
+ if (BGP_DEBUG(evpn_mh, EVPN_MH_RT))
+ zlog_debug(
+ "update path %pFX linked to es %s on vrf chg",
+ &pi->net->p, es->esi_str);
+ bgp_evpn_route_entry_install_if_vrf_match(es_vrf->bgp_vrf, pi,
+ 1);
+ }
+}
+
+static void bgp_evpn_es_frag_free(struct bgp_evpn_es_frag *es_frag)
+{
+ struct bgp_evpn_es *es = es_frag->es;
+
+ if (es->es_base_frag == es_frag)
+ es->es_base_frag = NULL;
+
+ if (BGP_DEBUG(evpn_mh, EVPN_MH_ES))
+ zlog_debug("es %s frag %u free", es->esi_str, es_frag->rd_id);
+ list_delete_node(es->es_frag_list, &es_frag->es_listnode);
+
+ /* EVIs that are advertised using the info in this fragment */
+ list_delete(&es_frag->es_evi_frag_list);
+
+ bf_release_index(bm->rd_idspace, es_frag->rd_id);
+
+
+ XFREE(MTYPE_BGP_EVPN_ES_FRAG, es_frag);
+}
+
+static void bgp_evpn_es_frag_free_unused(struct bgp_evpn_es_frag *es_frag)
+{
+ if ((es_frag->es->es_base_frag == es_frag) ||
+ listcount(es_frag->es_evi_frag_list))
+ return;
+
+ bgp_evpn_es_frag_free(es_frag);
+}
+
+static void bgp_evpn_es_frag_free_all(struct bgp_evpn_es *es)
+{
+ struct listnode *node;
+ struct listnode *nnode;
+ struct bgp_evpn_es_frag *es_frag;
+
+ for (ALL_LIST_ELEMENTS(es->es_frag_list, node, nnode, es_frag))
+ bgp_evpn_es_frag_free(es_frag);
+}
+
+static struct bgp_evpn_es_frag *bgp_evpn_es_frag_new(struct bgp_evpn_es *es)
+{
+ struct bgp_evpn_es_frag *es_frag;
+ char buf[BGP_EVPN_PREFIX_RD_LEN];
+ struct bgp *bgp;
+
+ es_frag = XCALLOC(MTYPE_BGP_EVPN_ES_FRAG, sizeof(*es_frag));
+ bf_assign_index(bm->rd_idspace, es_frag->rd_id);
+ es_frag->prd.family = AF_UNSPEC;
+ es_frag->prd.prefixlen = 64;
+ bgp = bgp_get_evpn();
+ snprintfrr(buf, sizeof(buf), "%pI4:%hu", &bgp->router_id,
+ es_frag->rd_id);
+ (void)str2prefix_rd(buf, &es_frag->prd);
+
+ /* EVIs that are advertised using the info in this fragment */
+ es_frag->es_evi_frag_list = list_new();
+ listset_app_node_mem(es_frag->es_evi_frag_list);
+
+ /* Link the fragment to the parent ES */
+ es_frag->es = es;
+ listnode_init(&es_frag->es_listnode, es_frag);
+ listnode_add(es->es_frag_list, &es_frag->es_listnode);
+
+ if (BGP_DEBUG(evpn_mh, EVPN_MH_ES))
+ zlog_debug("es %s frag %u new", es->esi_str, es_frag->rd_id);
+ return es_frag;
+}
+
+static struct bgp_evpn_es_frag *
+bgp_evpn_es_find_frag_with_space(struct bgp_evpn_es *es)
+{
+ struct listnode *node;
+ struct bgp_evpn_es_frag *es_frag;
+
+ for (ALL_LIST_ELEMENTS_RO(es->es_frag_list, node, es_frag)) {
+ if (listcount(es_frag->es_evi_frag_list) <
+ bgp_mh_info->evi_per_es_frag)
+ return es_frag;
+ }
+
+ /* No frags where found with space; allocate a new one */
+ return bgp_evpn_es_frag_new(es);
+}
+
+/* Link the ES-EVI to one of the ES fragments */
+static void bgp_evpn_es_frag_evi_add(struct bgp_evpn_es_evi *es_evi)
+{
+ struct bgp_evpn_es_frag *es_frag;
+ struct bgp_evpn_es *es = es_evi->es;
+
+ if (es_evi->es_frag ||
+ !(CHECK_FLAG(es_evi->flags, BGP_EVPNES_EVI_LOCAL)))
+ return;
+
+ es_frag = bgp_evpn_es_find_frag_with_space(es);
+
+ es_evi->es_frag = es_frag;
+ listnode_init(&es_evi->es_frag_listnode, es_evi);
+ listnode_add(es_frag->es_evi_frag_list, &es_evi->es_frag_listnode);
+
+ if (BGP_DEBUG(evpn_mh, EVPN_MH_ES))
+ zlog_debug("es %s vni %d linked to frag %u", es->esi_str,
+ es_evi->vpn->vni, es_frag->rd_id);
+}
+
+/* UnLink the ES-EVI from the ES fragment */
+static void bgp_evpn_es_frag_evi_del(struct bgp_evpn_es_evi *es_evi,
+ bool send_ead_del_if_empty)
+{
+ struct bgp_evpn_es_frag *es_frag = es_evi->es_frag;
+ struct prefix_evpn p;
+ struct bgp_evpn_es *es;
+ struct bgp *bgp;
+
+ if (!es_frag)
+ return;
+
+ es = es_frag->es;
+ es_evi->es_frag = NULL;
+ if (BGP_DEBUG(evpn_mh, EVPN_MH_ES))
+ zlog_debug("es %s vni %d unlinked from frag %u", es->esi_str,
+ es_evi->vpn->vni, es_frag->rd_id);
+
+ list_delete_node(es_frag->es_evi_frag_list, &es_evi->es_frag_listnode);
+
+ /*
+ * if there are no other EVIs on the fragment deleted the EAD-ES for
+ * the fragment
+ */
+ if (send_ead_del_if_empty && !listcount(es_frag->es_evi_frag_list)) {
+ bgp = bgp_get_evpn();
+
+ if (BGP_DEBUG(evpn_mh, EVPN_MH_ES))
+ zlog_debug("es %s frag %u ead-es route delete",
+ es->esi_str, es_frag->rd_id);
+ build_evpn_type1_prefix(&p, BGP_EVPN_AD_ES_ETH_TAG, &es->esi,
+ es->originator_ip);
+ p.prefix.ead_addr.frag_id = es_frag->rd_id;
+ bgp_evpn_mh_route_delete(bgp, es, NULL, es_frag, &p);
+ }
+
+ /* We don't attempt to coalesce frags that may not be full. Instead we
+ * only free up the frag when it is completely empty.
+ */
+ bgp_evpn_es_frag_free_unused(es_frag);
+}
+
+/* Link the ES-EVIs to one of the ES fragments */
+static void bgp_evpn_es_frag_evi_update_all(struct bgp_evpn_es *es, bool add)
+{
+ struct listnode *node;
+ struct bgp_evpn_es_evi *es_evi;
+
+ for (ALL_LIST_ELEMENTS_RO(es->es_evi_list, node, es_evi)) {
+ if (add)
+ bgp_evpn_es_frag_evi_add(es_evi);
+ else
+ bgp_evpn_es_frag_evi_del(es_evi, false);
+ }
+}
+
+/* compare ES-IDs for the global ES RB tree */
+static int bgp_es_rb_cmp(const struct bgp_evpn_es *es1,
+ const struct bgp_evpn_es *es2)
+{
+ return memcmp(&es1->esi, &es2->esi, ESI_BYTES);
+}
+RB_GENERATE(bgp_es_rb_head, bgp_evpn_es, rb_node, bgp_es_rb_cmp);
+
+struct bgp_evpn_es *bgp_evpn_es_find(const esi_t *esi)
+{
+ struct bgp_evpn_es tmp;
+
+ memcpy(&tmp.esi, esi, sizeof(esi_t));
+ return RB_FIND(bgp_es_rb_head, &bgp_mh_info->es_rb_tree, &tmp);
+}
+
+static struct bgp_evpn_es *bgp_evpn_es_new(struct bgp *bgp, const esi_t *esi)
+{
+ struct bgp_evpn_es *es;
+
+ es = XCALLOC(MTYPE_BGP_EVPN_ES, sizeof(struct bgp_evpn_es));
+
+ /* set the ESI */
+ memcpy(&es->esi, esi, sizeof(esi_t));
+
+ /* Initialise the VTEP list */
+ es->es_vtep_list = list_new();
+ listset_app_node_mem(es->es_vtep_list);
+ es->es_vtep_list->cmp = bgp_evpn_es_vtep_cmp;
+
+ esi_to_str(&es->esi, es->esi_str, sizeof(es->esi_str));
+
+ /* Initialize the ES routing table */
+ es->route_table = bgp_table_init(bgp, AFI_L2VPN, SAFI_EVPN);
+
+ /* Add to rb_tree */
+ RB_INSERT(bgp_es_rb_head, &bgp_mh_info->es_rb_tree, es);
+
+ /* Initialise the ES-EVI list */
+ es->es_evi_list = list_new();
+ listset_app_node_mem(es->es_evi_list);
+
+ /* Initialise the ES-VRF list used for L3NHG management */
+ es->es_vrf_list = list_new();
+ listset_app_node_mem(es->es_vrf_list);
+
+ /* Initialise the route list used for efficient event handling */
+ es->macip_evi_path_list = list_new();
+ listset_app_node_mem(es->macip_evi_path_list);
+ es->macip_global_path_list = list_new();
+ listset_app_node_mem(es->macip_global_path_list);
+ es->es_frag_list = list_new();
+ listset_app_node_mem(es->es_frag_list);
+
+ QOBJ_REG(es, bgp_evpn_es);
+
+ return es;
+}
+
+/* Free a given ES -
+ * This just frees appropriate memory, caller should have taken other
+ * needed actions.
+ */
+static void bgp_evpn_es_free(struct bgp_evpn_es *es, const char *caller)
+{
+ if ((es->flags & (BGP_EVPNES_LOCAL | BGP_EVPNES_REMOTE))
+ || listcount(es->macip_evi_path_list)
+ || listcount(es->macip_global_path_list))
+ return;
+
+ if (BGP_DEBUG(evpn_mh, EVPN_MH_ES))
+ zlog_debug("%s: es %s free", caller, es->esi_str);
+
+ /* cleanup resources maintained against the ES */
+ list_delete(&es->es_evi_list);
+ list_delete(&es->es_vrf_list);
+ list_delete(&es->es_vtep_list);
+ list_delete(&es->macip_evi_path_list);
+ list_delete(&es->macip_global_path_list);
+ list_delete(&es->es_frag_list);
+ bgp_table_unlock(es->route_table);
+
+ /* remove the entry from various databases */
+ RB_REMOVE(bgp_es_rb_head, &bgp_mh_info->es_rb_tree, es);
+ bgp_evpn_es_cons_checks_pend_del(es);
+
+ QOBJ_UNREG(es);
+ XFREE(MTYPE_BGP_EVPN_ES, es);
+}
+
+static inline bool bgp_evpn_is_es_local_and_non_bypass(struct bgp_evpn_es *es)
+{
+ return (es->flags & BGP_EVPNES_LOCAL)
+ && !(es->flags & BGP_EVPNES_BYPASS);
+}
+
+/* init local info associated with the ES */
+static void bgp_evpn_es_local_info_set(struct bgp *bgp, struct bgp_evpn_es *es)
+{
+ bool old_is_local;
+ bool is_local;
+
+ if (CHECK_FLAG(es->flags, BGP_EVPNES_LOCAL))
+ return;
+
+ old_is_local = bgp_evpn_is_es_local_and_non_bypass(es);
+ SET_FLAG(es->flags, BGP_EVPNES_LOCAL);
+
+ listnode_init(&es->es_listnode, es);
+ listnode_add(bgp_mh_info->local_es_list, &es->es_listnode);
+
+ /* setup the first ES fragment; more fragments may be allocated based
+ * on the the number of EVI entries
+ */
+ es->es_base_frag = bgp_evpn_es_frag_new(es);
+ /* distribute ES-EVIs to one or more ES fragments */
+ bgp_evpn_es_frag_evi_update_all(es, true);
+
+ is_local = bgp_evpn_is_es_local_and_non_bypass(es);
+ if (old_is_local != is_local)
+ bgp_evpn_mac_update_on_es_local_chg(es, is_local);
+}
+
+/* clear any local info associated with the ES */
+static void bgp_evpn_es_local_info_clear(struct bgp_evpn_es *es, bool finish)
+{
+ bool old_is_local;
+ bool is_local;
+
+ if (!CHECK_FLAG(es->flags, BGP_EVPNES_LOCAL))
+ return;
+
+ /* clear the es frag references and free them up */
+ bgp_evpn_es_frag_evi_update_all(es, false);
+ es->es_base_frag = NULL;
+ bgp_evpn_es_frag_free_all(es);
+
+ old_is_local = bgp_evpn_is_es_local_and_non_bypass(es);
+ UNSET_FLAG(es->flags, BGP_EVPNES_LOCAL);
+
+ is_local = bgp_evpn_is_es_local_and_non_bypass(es);
+ if (!finish && (old_is_local != is_local))
+ bgp_evpn_mac_update_on_es_local_chg(es, is_local);
+
+ /* remove from the ES local list */
+ list_delete_node(bgp_mh_info->local_es_list, &es->es_listnode);
+
+ bgp_evpn_es_free(es, __func__);
+}
+
+/* eval remote info associated with the ES */
+static void bgp_evpn_es_remote_info_re_eval(struct bgp_evpn_es *es)
+{
+ if (es->remote_es_evi_cnt) {
+ SET_FLAG(es->flags, BGP_EVPNES_REMOTE);
+ } else {
+ if (CHECK_FLAG(es->flags, BGP_EVPNES_REMOTE)) {
+ UNSET_FLAG(es->flags, BGP_EVPNES_REMOTE);
+ bgp_evpn_es_free(es, __func__);
+ }
+ }
+}
+
+/* If ES is present and local it needs to be active/oper-up for
+ * including L3 EC
+ */
+bool bgp_evpn_es_add_l3_ecomm_ok(esi_t *esi)
+{
+ struct bgp_evpn_es *es;
+
+ if (!esi || !bgp_mh_info->suppress_l3_ecomm_on_inactive_es)
+ return true;
+
+ es = bgp_evpn_es_find(esi);
+
+ return (!es || !(es->flags & BGP_EVPNES_LOCAL)
+ || bgp_evpn_local_es_is_active(es));
+}
+
+static bool bgp_evpn_is_valid_local_path(struct bgp_path_info *pi)
+{
+ return (CHECK_FLAG(pi->flags, BGP_PATH_VALID)
+ && pi->type == ZEBRA_ROUTE_BGP
+ && pi->sub_type == BGP_ROUTE_STATIC);
+}
+
+/* Update all local MAC-IP routes in the VNI routing table associated
+ * with the ES. When the ES is down the routes are advertised without
+ * the L3 extcomm
+ */
+static void bgp_evpn_mac_update_on_es_oper_chg(struct bgp_evpn_es *es)
+{
+ struct listnode *node;
+ struct bgp_path_es_info *es_info;
+ struct bgp_path_info *pi;
+ struct bgp *bgp;
+ struct bgpevpn *vpn;
+
+ if (!bgp_mh_info->suppress_l3_ecomm_on_inactive_es)
+ return;
+
+ if (BGP_DEBUG(evpn_mh, EVPN_MH_ES))
+ zlog_debug("update paths linked to es %s on oper chg",
+ es->esi_str);
+
+ bgp = bgp_get_evpn();
+ for (ALL_LIST_ELEMENTS_RO(es->macip_evi_path_list, node, es_info)) {
+ pi = es_info->pi;
+
+ if (!bgp_evpn_is_valid_local_path(pi))
+ continue;
+
+ if (!bgp_evpn_is_macip_path(pi))
+ continue;
+
+ vpn = bgp_evpn_lookup_vni(bgp, es_info->vni);
+ if (!vpn)
+ continue;
+
+ if (BGP_DEBUG(evpn_mh, EVPN_MH_RT))
+ zlog_debug(
+ "update path %d %pFX linked to es %s on oper chg",
+ es_info->vni, &pi->net->p, es->esi_str);
+
+ bgp_evpn_update_type2_route_entry(bgp, vpn, pi->net, pi,
+ __func__);
+ }
+}
+
+static bool bgp_evpn_is_valid_bgp_path(struct bgp_path_info *pi)
+{
+ return (CHECK_FLAG(pi->flags, BGP_PATH_VALID)
+ && pi->type == ZEBRA_ROUTE_BGP
+ && pi->sub_type == BGP_ROUTE_NORMAL);
+}
+
+/* If an ES is no longer local (or becomes local) we need to re-install
+ * paths using that ES as destination. This is needed as the criteria
+ * for best path selection has changed.
+ */
+static void bgp_evpn_mac_update_on_es_local_chg(struct bgp_evpn_es *es,
+ bool is_local)
+{
+ struct listnode *node;
+ struct bgp_path_es_info *es_info;
+ struct bgp_path_info *pi;
+ bool tmp_local;
+ struct attr *attr_new;
+ struct attr attr_tmp;
+
+ if (BGP_DEBUG(evpn_mh, EVPN_MH_ES))
+ zlog_debug("update paths linked to es %s on chg to %s",
+ es->esi_str, is_local ? "local" : "non-local");
+
+ for (ALL_LIST_ELEMENTS_RO(es->macip_global_path_list, node, es_info)) {
+ pi = es_info->pi;
+
+ /* Consider "valid" remote routes */
+ if (!bgp_evpn_is_valid_bgp_path(pi))
+ continue;
+
+ if (!pi->attr)
+ continue;
+
+ tmp_local = !!(pi->attr->es_flags & ATTR_ES_IS_LOCAL);
+ if (tmp_local == is_local)
+ continue;
+
+ if (BGP_DEBUG(evpn_mh, EVPN_MH_RT))
+ zlog_debug(
+ "update path %pFX linked to es %s on chg to %s",
+ &pi->net->p, es->esi_str,
+ is_local ? "local" : "non-local");
+
+ attr_tmp = *pi->attr;
+ if (is_local)
+ attr_tmp.es_flags |= ATTR_ES_IS_LOCAL;
+ else
+ attr_tmp.es_flags &= ~ATTR_ES_IS_LOCAL;
+ attr_new = bgp_attr_intern(&attr_tmp);
+ bgp_attr_unintern(&pi->attr);
+ pi->attr = attr_new;
+ bgp_evpn_import_type2_route(pi, 1);
+ }
+}
+
+static void bgp_evpn_local_es_deactivate(struct bgp *bgp,
+ struct bgp_evpn_es *es)
+{
+ struct prefix_evpn p;
+ int ret;
+
+ /* withdraw ESR */
+ /* Delete and withdraw locally learnt ES route */
+ build_evpn_type4_prefix(&p, &es->esi, es->originator_ip);
+ ret = bgp_evpn_type4_route_delete(bgp, es, &p);
+ if (ret) {
+ flog_err(EC_BGP_EVPN_ROUTE_DELETE,
+ "%u failed to delete type-4 route for ESI %s",
+ bgp->vrf_id, es->esi_str);
+ }
+
+ /* withdraw EAD-EVI */
+ if (!bgp_mh_info->ead_evi_adv_for_down_links)
+ bgp_evpn_local_type1_evi_route_del(bgp, es);
+
+ /* withdraw EAD-ES */
+ bgp_evpn_ead_es_route_delete(bgp, es);
+
+ bgp_evpn_mac_update_on_es_oper_chg(es);
+}
+
+/* Process ES link oper-down by withdrawing ES-EAD and ESR */
+static void bgp_evpn_local_es_down(struct bgp *bgp, struct bgp_evpn_es *es)
+{
+ bool old_active;
+
+ if (!CHECK_FLAG(es->flags, BGP_EVPNES_OPER_UP))
+ return;
+
+ old_active = bgp_evpn_local_es_is_active(es);
+ UNSET_FLAG(es->flags, BGP_EVPNES_OPER_UP);
+
+ if (BGP_DEBUG(evpn_mh, EVPN_MH_ES))
+ zlog_debug("local es %s down", es->esi_str);
+
+ if (old_active)
+ bgp_evpn_local_es_deactivate(bgp, es);
+}
+
+static void bgp_evpn_local_es_activate(struct bgp *bgp, struct bgp_evpn_es *es,
+ bool regen_ead, bool regen_esr)
+{
+ struct prefix_evpn p;
+
+ if (regen_esr) {
+ if (BGP_DEBUG(evpn_mh, EVPN_MH_ES))
+ zlog_debug("local es %s generate ESR", es->esi_str);
+ /* generate ESR */
+ build_evpn_type4_prefix(&p, &es->esi, es->originator_ip);
+ if (bgp_evpn_type4_route_update(bgp, es, &p))
+ flog_err(EC_BGP_EVPN_ROUTE_CREATE,
+ "%u: Type4 route creation failure for ESI %s",
+ bgp->vrf_id, es->esi_str);
+ }
+
+ if (regen_ead) {
+ if (BGP_DEBUG(evpn_mh, EVPN_MH_ES))
+ zlog_debug("local es %s generate EAD", es->esi_str);
+ /* generate EAD-EVI */
+ bgp_evpn_local_type1_evi_route_add(bgp, es);
+
+ /* generate EAD-ES */
+ bgp_evpn_ead_es_route_update(bgp, es);
+ }
+
+ bgp_evpn_mac_update_on_es_oper_chg(es);
+}
+
+/* Process ES link oper-up by generating ES-EAD and ESR */
+static void bgp_evpn_local_es_up(struct bgp *bgp, struct bgp_evpn_es *es,
+ bool regen_esr)
+{
+ bool regen_ead = false;
+ bool active = false;
+
+ if (!CHECK_FLAG(es->flags, BGP_EVPNES_OPER_UP)) {
+ if (BGP_DEBUG(evpn_mh, EVPN_MH_ES))
+ zlog_debug("local es %s up", es->esi_str);
+
+ SET_FLAG(es->flags, BGP_EVPNES_OPER_UP);
+ regen_esr = true;
+ regen_ead = true;
+ }
+
+ active = bgp_evpn_local_es_is_active(es);
+ if (active && (regen_ead || regen_esr))
+ bgp_evpn_local_es_activate(bgp, es, regen_ead, regen_esr);
+}
+
+/* If an ethernet segment is in LACP bypass we cannot advertise
+ * reachability to it i.e. EAD-per-ES and ESR is not advertised in
+ * bypass state.
+ * PS: EAD-per-EVI will continue to be advertised
+ */
+static void bgp_evpn_local_es_bypass_update(struct bgp *bgp,
+ struct bgp_evpn_es *es, bool bypass)
+{
+ bool old_bypass = !!(es->flags & BGP_EVPNES_BYPASS);
+ bool old_active;
+ bool new_active;
+ bool old_is_local;
+ bool is_local;
+
+ if (bypass == old_bypass)
+ return;
+
+ old_active = bgp_evpn_local_es_is_active(es);
+ old_is_local = bgp_evpn_is_es_local_and_non_bypass(es);
+ if (bypass)
+ SET_FLAG(es->flags, BGP_EVPNES_BYPASS);
+ else
+ UNSET_FLAG(es->flags, BGP_EVPNES_BYPASS);
+
+ if (BGP_DEBUG(evpn_mh, EVPN_MH_ES))
+ zlog_debug("local es %s bypass %s", es->esi_str,
+ bypass ? "set" : "clear");
+
+ new_active = bgp_evpn_local_es_is_active(es);
+ if (old_active != new_active) {
+ if (new_active)
+ bgp_evpn_local_es_activate(bgp, es, true, true);
+ else
+ bgp_evpn_local_es_deactivate(bgp, es);
+ }
+
+ is_local = bgp_evpn_is_es_local_and_non_bypass(es);
+ if (old_is_local != is_local)
+ bgp_evpn_mac_update_on_es_local_chg(es, is_local);
+}
+
+static void bgp_evpn_local_es_do_del(struct bgp *bgp, struct bgp_evpn_es *es)
+{
+ struct bgp_evpn_es_evi *es_evi;
+ struct listnode *evi_node, *evi_next_node;
+
+ if (BGP_DEBUG(evpn_mh, EVPN_MH_ES))
+ zlog_debug("del local es %s", es->esi_str);
+
+ /* Delete all local EVPN ES routes from ESI table
+ * and schedule for processing (to withdraw from peers))
+ */
+ bgp_evpn_es_route_del_all(bgp, es);
+
+ /* release all local ES EVIs associated with the ES */
+ for (ALL_LIST_ELEMENTS(es->es_evi_list, evi_node,
+ evi_next_node, es_evi)) {
+ bgp_evpn_local_es_evi_do_del(es_evi);
+ }
+
+ /* Clear local info associated with the ES and free it up if there is
+ * no remote reference
+ */
+ bgp_evpn_es_local_info_clear(es, false);
+}
+
+bool bgp_evpn_is_esi_local_and_non_bypass(esi_t *esi)
+{
+ struct bgp_evpn_es *es = NULL;
+
+ /* Lookup ESI hash - should exist. */
+ es = bgp_evpn_es_find(esi);
+
+ return es && bgp_evpn_is_es_local_and_non_bypass(es);
+}
+
+int bgp_evpn_local_es_del(struct bgp *bgp, esi_t *esi)
+{
+ struct bgp_evpn_es *es = NULL;
+
+ /* Lookup ESI hash - should exist. */
+ es = bgp_evpn_es_find(esi);
+ if (!es) {
+ flog_warn(EC_BGP_EVPN_ESI, "%u: ES missing at local ES DEL",
+ bgp->vrf_id);
+ return -1;
+ }
+
+ bgp_evpn_local_es_do_del(bgp, es);
+ return 0;
+}
+
+/* Handle device to ES id association. Results in the creation of a local
+ * ES.
+ */
+int bgp_evpn_local_es_add(struct bgp *bgp, esi_t *esi,
+ struct in_addr originator_ip, bool oper_up,
+ uint16_t df_pref, bool bypass)
+{
+ struct bgp_evpn_es *es;
+ bool new_es = true;
+ bool regen_esr = false;
+
+ /* create the new es */
+ es = bgp_evpn_es_find(esi);
+ if (es) {
+ if (CHECK_FLAG(es->flags, BGP_EVPNES_LOCAL))
+ new_es = false;
+ } else
+ es = bgp_evpn_es_new(bgp, esi);
+
+ if (BGP_DEBUG(evpn_mh, EVPN_MH_ES))
+ zlog_debug("add local es %s orig-ip %pI4 df_pref %u %s",
+ es->esi_str, &originator_ip, df_pref,
+ bypass ? "bypass" : "");
+
+ es->originator_ip = originator_ip;
+ if (df_pref != es->df_pref) {
+ es->df_pref = df_pref;
+ regen_esr = true;
+ }
+ bgp_evpn_es_local_info_set(bgp, es);
+
+ /* import all remote Type-4 routes in the ES table */
+ if (new_es)
+ bgp_evpn_type4_remote_routes_import(bgp, es,
+ true /* install */);
+
+ /* create and advertise EAD-EVI routes for the ES -
+ * XXX - till an ES-EVI reference is created there is really nothing to
+ * advertise
+ */
+ if (bgp_mh_info->ead_evi_adv_for_down_links)
+ bgp_evpn_local_type1_evi_route_add(bgp, es);
+
+ bgp_evpn_local_es_bypass_update(bgp, es, bypass);
+
+ /* If the ES link is operationally up generate EAD-ES. EAD-EVI
+ * can be generated even if the link is inactive.
+ */
+ if (oper_up)
+ bgp_evpn_local_es_up(bgp, es, regen_esr);
+ else
+ bgp_evpn_local_es_down(bgp, es);
+
+ return 0;
+}
+
+static void bgp_evpn_es_json_frag_fill(json_object *json_frags,
+ struct bgp_evpn_es *es)
+{
+ json_object *json_frag;
+ struct listnode *node;
+ struct bgp_evpn_es_frag *es_frag;
+
+ for (ALL_LIST_ELEMENTS_RO(es->es_frag_list, node, es_frag)) {
+ json_frag = json_object_new_object();
+
+ json_object_string_addf(json_frag, "rd", "%pRD", &es_frag->prd);
+ json_object_int_add(json_frag, "eviCount",
+ listcount(es_frag->es_evi_frag_list));
+
+ json_object_array_add(json_frags, json_frag);
+ }
+}
+
+static void bgp_evpn_es_frag_show_detail(struct vty *vty,
+ struct bgp_evpn_es *es)
+{
+ struct listnode *node;
+ struct bgp_evpn_es_frag *es_frag;
+
+ for (ALL_LIST_ELEMENTS_RO(es->es_frag_list, node, es_frag)) {
+ vty_out(vty, " %pRD EVIs: %d\n", &es_frag->prd,
+ listcount(es_frag->es_evi_frag_list));
+ }
+}
+
+static char *bgp_evpn_es_vteps_str(char *vtep_str, struct bgp_evpn_es *es,
+ uint8_t vtep_str_size)
+{
+ char vtep_flag_str[BGP_EVPN_FLAG_STR_SZ];
+ struct listnode *node;
+ struct bgp_evpn_es_vtep *es_vtep;
+ bool first = true;
+ char ip_buf[INET6_ADDRSTRLEN];
+
+ vtep_str[0] = '\0';
+ for (ALL_LIST_ELEMENTS_RO(es->es_vtep_list, node, es_vtep)) {
+ vtep_flag_str[0] = '\0';
+
+ if (es_vtep->flags & BGP_EVPNES_VTEP_ESR)
+ strlcat(vtep_flag_str, "E", sizeof(vtep_flag_str));
+ if (es_vtep->flags & BGP_EVPNES_VTEP_ACTIVE)
+ strlcat(vtep_flag_str, "A", sizeof(vtep_flag_str));
+
+ if (!strlen(vtep_flag_str))
+ strlcat(vtep_flag_str, "-", sizeof(vtep_flag_str));
+ if (first)
+ first = false;
+ else
+ strlcat(vtep_str, ",", vtep_str_size);
+ strlcat(vtep_str,
+ inet_ntop(AF_INET, &es_vtep->vtep_ip, ip_buf,
+ sizeof(ip_buf)),
+ vtep_str_size);
+ strlcat(vtep_str, "(", vtep_str_size);
+ strlcat(vtep_str, vtep_flag_str, vtep_str_size);
+ strlcat(vtep_str, ")", vtep_str_size);
+ }
+
+ return vtep_str;
+}
+
+static void bgp_evpn_es_json_vtep_fill(json_object *json_vteps,
+ struct bgp_evpn_es_vtep *es_vtep)
+{
+ json_object *json_vtep_entry;
+ json_object *json_flags;
+
+ json_vtep_entry = json_object_new_object();
+
+ json_object_string_addf(json_vtep_entry, "vtep_ip", "%pI4",
+ &es_vtep->vtep_ip);
+ if (es_vtep->flags & (BGP_EVPNES_VTEP_ESR |
+ BGP_EVPNES_VTEP_ACTIVE)) {
+ json_flags = json_object_new_array();
+ if (es_vtep->flags & BGP_EVPNES_VTEP_ESR)
+ json_array_string_add(json_flags, "esr");
+ if (es_vtep->flags & BGP_EVPNES_VTEP_ACTIVE)
+ json_array_string_add(json_flags, "active");
+ json_object_object_add(json_vtep_entry, "flags", json_flags);
+ if (es_vtep->flags & BGP_EVPNES_VTEP_ESR) {
+ json_object_int_add(json_vtep_entry, "dfPreference",
+ es_vtep->df_pref);
+ json_object_int_add(json_vtep_entry, "dfAlgorithm",
+ es_vtep->df_pref);
+ }
+ }
+
+ json_object_array_add(json_vteps,
+ json_vtep_entry);
+}
+
+static void bgp_evpn_es_vteps_show_detail(struct vty *vty,
+ struct bgp_evpn_es *es)
+{
+ char vtep_flag_str[BGP_EVPN_FLAG_STR_SZ];
+ struct listnode *node;
+ struct bgp_evpn_es_vtep *es_vtep;
+ char alg_buf[EVPN_DF_ALG_STR_LEN];
+
+ for (ALL_LIST_ELEMENTS_RO(es->es_vtep_list, node, es_vtep)) {
+ vtep_flag_str[0] = '\0';
+ if (es_vtep->flags & BGP_EVPNES_VTEP_ESR)
+ strlcat(vtep_flag_str, "E", sizeof(vtep_flag_str));
+ if (es_vtep->flags & BGP_EVPNES_VTEP_ACTIVE)
+ strlcat(vtep_flag_str, "A", sizeof(vtep_flag_str));
+
+ if (!strlen(vtep_flag_str))
+ strlcat(vtep_flag_str, "-", sizeof(vtep_flag_str));
+
+ vty_out(vty, " %pI4 flags: %s", &es_vtep->vtep_ip,
+ vtep_flag_str);
+
+ if (es_vtep->flags & BGP_EVPNES_VTEP_ESR)
+ vty_out(vty, " df_alg: %s df_pref: %u\n",
+ evpn_es_df_alg2str(es_vtep->df_alg, alg_buf,
+ sizeof(alg_buf)),
+ es_vtep->df_pref);
+ else
+ vty_out(vty, "\n");
+ }
+}
+
+static void bgp_evpn_es_show_entry(struct vty *vty,
+ struct bgp_evpn_es *es, json_object *json)
+{
+ struct listnode *node;
+ struct bgp_evpn_es_vtep *es_vtep;
+
+ if (json) {
+ json_object *json_vteps;
+ json_object *json_types;
+
+ json_object_string_add(json, "esi", es->esi_str);
+ if (es->es_base_frag)
+ json_object_string_addf(json, "rd", "%pRD",
+ &es->es_base_frag->prd);
+
+ if (es->flags & (BGP_EVPNES_LOCAL | BGP_EVPNES_REMOTE)) {
+ json_types = json_object_new_array();
+ if (es->flags & BGP_EVPNES_LOCAL)
+ json_array_string_add(json_types, "local");
+ if (es->flags & BGP_EVPNES_REMOTE)
+ json_array_string_add(json_types, "remote");
+ json_object_object_add(json, "type", json_types);
+ }
+
+ if (listcount(es->es_vtep_list)) {
+ json_vteps = json_object_new_array();
+ for (ALL_LIST_ELEMENTS_RO(es->es_vtep_list,
+ node, es_vtep)) {
+ bgp_evpn_es_json_vtep_fill(json_vteps, es_vtep);
+ }
+ json_object_object_add(json, "vteps", json_vteps);
+ }
+ json_object_int_add(json, "vniCount",
+ listcount(es->es_evi_list));
+ } else {
+ char type_str[4];
+ char vtep_str[ES_VTEP_LIST_STR_SZ + BGP_EVPN_VTEPS_FLAG_STR_SZ];
+
+ type_str[0] = '\0';
+ if (es->flags & BGP_EVPNES_BYPASS)
+ strlcat(type_str, "B", sizeof(type_str));
+ if (es->flags & BGP_EVPNES_LOCAL)
+ strlcat(type_str, "L", sizeof(type_str));
+ if (es->flags & BGP_EVPNES_REMOTE)
+ strlcat(type_str, "R", sizeof(type_str));
+ if (es->inconsistencies)
+ strlcat(type_str, "I", sizeof(type_str));
+
+ bgp_evpn_es_vteps_str(vtep_str, es, sizeof(vtep_str));
+
+ vty_out(vty, "%-30s %-5s %-21pRD %-8d %s\n", es->esi_str,
+ type_str,
+ es->es_base_frag ? &es->es_base_frag->prd : NULL,
+ listcount(es->es_evi_list), vtep_str);
+ }
+}
+
+static void bgp_evpn_es_show_entry_detail(struct vty *vty,
+ struct bgp_evpn_es *es, json_object *json)
+{
+ if (json) {
+ json_object *json_flags;
+ json_object *json_incons;
+ json_object *json_vteps;
+ json_object *json_frags;
+ struct listnode *node;
+ struct bgp_evpn_es_vtep *es_vtep;
+
+ /* Add the "brief" info first */
+ bgp_evpn_es_show_entry(vty, es, json);
+ if (es->flags
+ & (BGP_EVPNES_OPER_UP | BGP_EVPNES_ADV_EVI
+ | BGP_EVPNES_BYPASS)) {
+ json_flags = json_object_new_array();
+ if (es->flags & BGP_EVPNES_OPER_UP)
+ json_array_string_add(json_flags, "up");
+ if (es->flags & BGP_EVPNES_ADV_EVI)
+ json_array_string_add(json_flags,
+ "advertiseEVI");
+ if (es->flags & BGP_EVPNES_BYPASS)
+ json_array_string_add(json_flags, "bypass");
+ json_object_object_add(json, "flags", json_flags);
+ }
+ json_object_string_addf(json, "originator_ip", "%pI4",
+ &es->originator_ip);
+ json_object_int_add(json, "remoteVniCount",
+ es->remote_es_evi_cnt);
+ json_object_int_add(json, "vrfCount",
+ listcount(es->es_vrf_list));
+ json_object_int_add(json, "macipPathCount",
+ listcount(es->macip_evi_path_list));
+ json_object_int_add(json, "macipGlobalPathCount",
+ listcount(es->macip_global_path_list));
+ json_object_int_add(json, "inconsistentVniVtepCount",
+ es->incons_evi_vtep_cnt);
+ if (listcount(es->es_vtep_list)) {
+ json_vteps = json_object_new_array();
+ for (ALL_LIST_ELEMENTS_RO(es->es_vtep_list, node,
+ es_vtep)) {
+ bgp_evpn_es_json_vtep_fill(json_vteps, es_vtep);
+ }
+ json_object_object_add(json, "vteps", json_vteps);
+ }
+ if (listcount(es->es_frag_list)) {
+ json_frags = json_object_new_array();
+ bgp_evpn_es_json_frag_fill(json_frags, es);
+ json_object_object_add(json, "fragments", json_frags);
+ }
+ if (es->inconsistencies) {
+ json_incons = json_object_new_array();
+ if (es->inconsistencies & BGP_EVPNES_INCONS_VTEP_LIST)
+ json_array_string_add(json_incons,
+ "vni-vtep-mismatch");
+ json_object_object_add(json, "inconsistencies",
+ json_incons);
+ }
+ } else {
+ char incons_str[BGP_EVPNES_INCONS_STR_SZ];
+ char type_str[4];
+
+ type_str[0] = '\0';
+ if (es->flags & BGP_EVPNES_LOCAL)
+ strlcat(type_str, "L", sizeof(type_str));
+ if (es->flags & BGP_EVPNES_REMOTE)
+ strlcat(type_str, "R", sizeof(type_str));
+
+ vty_out(vty, "ESI: %s\n", es->esi_str);
+ vty_out(vty, " Type: %s\n", type_str);
+ vty_out(vty, " RD: %pRD\n",
+ es->es_base_frag ? &es->es_base_frag->prd : NULL);
+ vty_out(vty, " Originator-IP: %pI4\n", &es->originator_ip);
+ if (es->flags & BGP_EVPNES_LOCAL)
+ vty_out(vty, " Local ES DF preference: %u\n",
+ es->df_pref);
+ if (es->flags & BGP_EVPNES_BYPASS)
+ vty_out(vty, " LACP bypass: on\n");
+ vty_out(vty, " VNI Count: %d\n", listcount(es->es_evi_list));
+ vty_out(vty, " Remote VNI Count: %d\n",
+ es->remote_es_evi_cnt);
+ vty_out(vty, " VRF Count: %d\n", listcount(es->es_vrf_list));
+ vty_out(vty, " MACIP EVI Path Count: %d\n",
+ listcount(es->macip_evi_path_list));
+ vty_out(vty, " MACIP Global Path Count: %d\n",
+ listcount(es->macip_global_path_list));
+ vty_out(vty, " Inconsistent VNI VTEP Count: %d\n",
+ es->incons_evi_vtep_cnt);
+ if (es->inconsistencies) {
+ incons_str[0] = '\0';
+ if (es->inconsistencies & BGP_EVPNES_INCONS_VTEP_LIST)
+ strlcat(incons_str, "vni-vtep-mismatch",
+ sizeof(incons_str));
+ } else {
+ strlcpy(incons_str, "-", sizeof(incons_str));
+ }
+ vty_out(vty, " Inconsistencies: %s\n",
+ incons_str);
+ if (listcount(es->es_frag_list)) {
+ vty_out(vty, " Fragments:\n");
+ bgp_evpn_es_frag_show_detail(vty, es);
+ }
+ if (listcount(es->es_vtep_list)) {
+ vty_out(vty, " VTEPs:\n");
+ bgp_evpn_es_vteps_show_detail(vty, es);
+ }
+ vty_out(vty, "\n");
+ }
+}
+
+/* Display all ESs */
+void bgp_evpn_es_show(struct vty *vty, bool uj, bool detail)
+{
+ struct bgp_evpn_es *es;
+ json_object *json_array = NULL;
+ json_object *json = NULL;
+
+ if (uj) {
+ /* create an array of ESs */
+ json_array = json_object_new_array();
+ } else {
+ if (!detail) {
+ vty_out(vty,
+ "ES Flags: B - bypass, L local, R remote, I inconsistent\n");
+ vty_out(vty,
+ "VTEP Flags: E ESR/Type-4, A active nexthop\n");
+ vty_out(vty,
+ "%-30s %-5s %-21s %-8s %s\n",
+ "ESI", "Flags", "RD", "#VNIs", "VTEPs");
+ }
+ }
+
+ RB_FOREACH(es, bgp_es_rb_head, &bgp_mh_info->es_rb_tree) {
+ if (uj)
+ /* create a separate json object for each ES */
+ json = json_object_new_object();
+ if (detail)
+ bgp_evpn_es_show_entry_detail(vty, es, json);
+ else
+ bgp_evpn_es_show_entry(vty, es, json);
+ /* add ES to the json array */
+ if (uj)
+ json_object_array_add(json_array, json);
+ }
+
+ /* print the array of json-ESs */
+ if (uj)
+ vty_json(vty, json_array);
+}
+
+/* Display specific ES */
+void bgp_evpn_es_show_esi(struct vty *vty, esi_t *esi, bool uj)
+{
+ struct bgp_evpn_es *es;
+ json_object *json = NULL;
+
+ if (uj)
+ json = json_object_new_object();
+
+ es = bgp_evpn_es_find(esi);
+ if (es) {
+ bgp_evpn_es_show_entry_detail(vty, es, json);
+ } else {
+ if (!uj)
+ vty_out(vty, "ESI not found\n");
+ }
+
+ if (uj)
+ vty_json(vty, json);
+}
+
+/*****************************************************************************/
+/* Ethernet Segment to VRF association -
+ * 1. Each ES-EVI entry is associated with a tenant VRF. This associaton
+ * triggers the creation of an ES-VRF entry.
+ * 2. The ES-VRF entry is maintained for the purpose of L3-NHG creation
+ * 3. Type-2/MAC-IP routes are imported into a tenant VRF and programmed as
+ * a /32 or host route entry in the dataplane. If the destination of
+ * the host route is a remote-ES the route is programmed with the
+ * corresponding (keyed in by {vrf,ES-id}) L3-NHG.
+ * 4. The reason for this indirection (route->L3-NHG, L3-NHG->list-of-VTEPs)
+ * is to avoid route updates to the dplane when a remote-ES link flaps i.e.
+ * instead of updating all the dependent routes the NHG's contents are updated.
+ * This reduces the amount of datplane updates (nhg updates vs. route updates)
+ * allowing for a faster failover.
+ *
+ * XXX - can the L3 SVI index change without change in vpn->bgp_vrf
+ * association? If yes we need to handle that by updating all the L3 NHGs
+ * in that VRF.
+ */
+/******************************** L3 NHG management *************************/
+static void bgp_evpn_l3nhg_zebra_add_v4_or_v6(struct bgp_evpn_es_vrf *es_vrf,
+ bool v4_nhg)
+{
+ uint32_t nhg_id = v4_nhg ? es_vrf->nhg_id : es_vrf->v6_nhg_id;
+ struct bgp_evpn_es *es = es_vrf->es;
+ struct listnode *node;
+ struct bgp_evpn_es_vtep *es_vtep;
+ struct nexthop nh;
+ struct zapi_nexthop *api_nh;
+ struct zapi_nhg api_nhg = {};
+
+ /* Skip installation of L3-NHG if host routes used */
+ if (!nhg_id)
+ return;
+
+ if (BGP_DEBUG(evpn_mh, EVPN_MH_ES))
+ zlog_debug("es %s vrf %u %s nhg %u to zebra", es->esi_str,
+ es_vrf->bgp_vrf->vrf_id,
+ v4_nhg ? "v4_nhg" : "v6_nhg", nhg_id);
+
+ frrtrace(4, frr_bgp, evpn_mh_nhg_zsend, true, v4_nhg, nhg_id, es_vrf);
+
+ /* only the gateway ip changes for each NH. rest of the params
+ * are constant
+ */
+ memset(&nh, 0, sizeof(nh));
+ nh.vrf_id = es_vrf->bgp_vrf->vrf_id;
+ nh.flags = NEXTHOP_FLAG_ONLINK;
+ nh.ifindex = es_vrf->bgp_vrf->l3vni_svi_ifindex;
+ nh.weight = 1;
+ nh.type =
+ v4_nhg ? NEXTHOP_TYPE_IPV4_IFINDEX : NEXTHOP_TYPE_IPV6_IFINDEX;
+
+ api_nhg.id = nhg_id;
+ for (ALL_LIST_ELEMENTS_RO(es->es_vtep_list, node, es_vtep)) {
+ if (!CHECK_FLAG(es_vtep->flags, BGP_EVPNES_VTEP_ACTIVE))
+ continue;
+
+ /* Don't overrun the zapi buffer. */
+ if (api_nhg.nexthop_num == MULTIPATH_NUM)
+ break;
+
+ /* overwrite the gw */
+ if (v4_nhg)
+ nh.gate.ipv4 = es_vtep->vtep_ip;
+ else
+ ipv4_to_ipv4_mapped_ipv6(&nh.gate.ipv6,
+ es_vtep->vtep_ip);
+
+ /* convert to zapi format */
+ api_nh = &api_nhg.nexthops[api_nhg.nexthop_num];
+ zapi_nexthop_from_nexthop(api_nh, &nh);
+
+ ++api_nhg.nexthop_num;
+ if (BGP_DEBUG(evpn_mh, EVPN_MH_ES))
+ zlog_debug("nhg %u vtep %pI4 l3-svi %d", api_nhg.id,
+ &es_vtep->vtep_ip,
+ es_vrf->bgp_vrf->l3vni_svi_ifindex);
+
+ frrtrace(3, frr_bgp, evpn_mh_nh_zsend, nhg_id, es_vtep, es_vrf);
+ }
+
+ if (!api_nhg.nexthop_num)
+ return;
+
+ zclient_nhg_send(zclient, ZEBRA_NHG_ADD, &api_nhg);
+}
+
+static bool bgp_evpn_l3nhg_zebra_ok(struct bgp_evpn_es_vrf *es_vrf)
+{
+ if (!bgp_mh_info->host_routes_use_l3nhg)
+ return false;
+
+ /* Check socket. */
+ if (!zclient || zclient->sock < 0)
+ return false;
+
+ return true;
+}
+
+static void bgp_evpn_l3nhg_zebra_add(struct bgp_evpn_es_vrf *es_vrf)
+{
+ if (!bgp_evpn_l3nhg_zebra_ok(es_vrf))
+ return;
+
+ bgp_evpn_l3nhg_zebra_add_v4_or_v6(es_vrf, true /*v4_nhg*/);
+ bgp_evpn_l3nhg_zebra_add_v4_or_v6(es_vrf, false /*v4_nhg*/);
+}
+
+static void bgp_evpn_l3nhg_zebra_del_v4_or_v6(struct bgp_evpn_es_vrf *es_vrf,
+ bool v4_nhg)
+{
+ struct zapi_nhg api_nhg = {};
+
+ api_nhg.id = v4_nhg ? es_vrf->nhg_id : es_vrf->v6_nhg_id;
+
+ /* Skip installation of L3-NHG if host routes used */
+ if (!api_nhg.id)
+ return;
+
+ if (BGP_DEBUG(evpn_mh, EVPN_MH_ES))
+ zlog_debug("es %s vrf %u %s nhg %u to zebra",
+ es_vrf->es->esi_str, es_vrf->bgp_vrf->vrf_id,
+ v4_nhg ? "v4_nhg" : "v6_nhg", api_nhg.id);
+
+
+ frrtrace(4, frr_bgp, evpn_mh_nhg_zsend, false, v4_nhg, api_nhg.id,
+ es_vrf);
+
+ zclient_nhg_send(zclient, ZEBRA_NHG_DEL, &api_nhg);
+}
+
+static void bgp_evpn_l3nhg_zebra_del(struct bgp_evpn_es_vrf *es_vrf)
+{
+ if (!bgp_evpn_l3nhg_zebra_ok(es_vrf))
+ return;
+
+ bgp_evpn_l3nhg_zebra_del_v4_or_v6(es_vrf, true /*v4_nhg*/);
+ bgp_evpn_l3nhg_zebra_del_v4_or_v6(es_vrf, false /*v4_nhg*/);
+}
+
+static void bgp_evpn_l3nhg_deactivate(struct bgp_evpn_es_vrf *es_vrf)
+{
+ if (!(es_vrf->flags & BGP_EVPNES_VRF_NHG_ACTIVE))
+ return;
+
+ if (BGP_DEBUG(evpn_mh, EVPN_MH_ES))
+ zlog_debug("es %s vrf %u nhg %u de-activate",
+ es_vrf->es->esi_str, es_vrf->bgp_vrf->vrf_id,
+ es_vrf->nhg_id);
+ bgp_evpn_l3nhg_zebra_del(es_vrf);
+ es_vrf->flags &= ~BGP_EVPNES_VRF_NHG_ACTIVE;
+ /* MAC-IPs can now be installed via the L3NHG */
+ bgp_evpn_es_path_update_on_es_vrf_chg(es_vrf, "l3nhg-deactivate");
+}
+
+static void bgp_evpn_l3nhg_activate(struct bgp_evpn_es_vrf *es_vrf, bool update)
+{
+ if (!bgp_evpn_es_get_active_vtep_cnt(es_vrf->es)) {
+ bgp_evpn_l3nhg_deactivate(es_vrf);
+ return;
+ }
+
+ if (es_vrf->flags & BGP_EVPNES_VRF_NHG_ACTIVE) {
+ if (!update)
+ return;
+ } else {
+ if (BGP_DEBUG(evpn_mh, EVPN_MH_ES))
+ zlog_debug("es %s vrf %u nhg %u activate",
+ es_vrf->es->esi_str, es_vrf->bgp_vrf->vrf_id,
+ es_vrf->nhg_id);
+ es_vrf->flags |= BGP_EVPNES_VRF_NHG_ACTIVE;
+ /* MAC-IPs can now be installed via the L3NHG */
+ bgp_evpn_es_path_update_on_es_vrf_chg(es_vrf, "l3nhg_activate");
+ }
+
+ bgp_evpn_l3nhg_zebra_add(es_vrf);
+}
+
+/* when a VTEP is activated or de-activated against an ES associated
+ * VRFs' NHG needs to be updated
+ */
+static void bgp_evpn_l3nhg_update_on_vtep_chg(struct bgp_evpn_es *es)
+{
+ struct bgp_evpn_es_vrf *es_vrf;
+ struct listnode *es_vrf_node;
+
+ if (BGP_DEBUG(evpn_mh, EVPN_MH_ES))
+ zlog_debug("es %s nhg update on vtep chg", es->esi_str);
+
+ for (ALL_LIST_ELEMENTS_RO(es->es_vrf_list, es_vrf_node, es_vrf))
+ bgp_evpn_l3nhg_activate(es_vrf, true /* update */);
+}
+
+/* compare ES-IDs for the ES-VRF RB tree maintained per-VRF */
+static int bgp_es_vrf_rb_cmp(const struct bgp_evpn_es_vrf *es_vrf1,
+ const struct bgp_evpn_es_vrf *es_vrf2)
+{
+ return memcmp(&es_vrf1->es->esi, &es_vrf2->es->esi, ESI_BYTES);
+}
+RB_GENERATE(bgp_es_vrf_rb_head, bgp_evpn_es_vrf, rb_node, bgp_es_vrf_rb_cmp);
+
+/* Initialize the ES tables maintained per-tenant vrf */
+void bgp_evpn_vrf_es_init(struct bgp *bgp_vrf)
+{
+ /* Initialize the ES-VRF RB tree */
+ RB_INIT(bgp_es_vrf_rb_head, &bgp_vrf->es_vrf_rb_tree);
+}
+
+/* find the ES-VRF in the per-VRF RB tree */
+static struct bgp_evpn_es_vrf *bgp_evpn_es_vrf_find(struct bgp_evpn_es *es,
+ struct bgp *bgp_vrf)
+{
+ struct bgp_evpn_es_vrf es_vrf;
+
+ es_vrf.es = es;
+
+ return RB_FIND(bgp_es_vrf_rb_head, &bgp_vrf->es_vrf_rb_tree, &es_vrf);
+}
+
+/* allocate a new ES-VRF and setup L3NHG for it */
+static struct bgp_evpn_es_vrf *bgp_evpn_es_vrf_create(struct bgp_evpn_es *es,
+ struct bgp *bgp_vrf)
+{
+ struct bgp_evpn_es_vrf *es_vrf;
+
+ es_vrf = XCALLOC(MTYPE_BGP_EVPN_ES_VRF, sizeof(*es_vrf));
+
+ es_vrf->es = es;
+ es_vrf->bgp_vrf = bgp_vrf;
+
+ /* insert into the VRF-ESI rb tree */
+ RB_INSERT(bgp_es_vrf_rb_head, &bgp_vrf->es_vrf_rb_tree, es_vrf);
+
+ /* add to the ES's VRF list */
+ listnode_init(&es_vrf->es_listnode, es_vrf);
+ listnode_add(es->es_vrf_list, &es_vrf->es_listnode);
+
+ /* setup the L3 NHG id for the ES */
+ es_vrf->nhg_id = bgp_l3nhg_id_alloc();
+ es_vrf->v6_nhg_id = bgp_l3nhg_id_alloc();
+
+ if (BGP_DEBUG(evpn_mh, EVPN_MH_ES))
+ zlog_debug("es %s vrf %u nhg %u v6_nhg %d create", es->esi_str,
+ bgp_vrf->vrf_id, es_vrf->nhg_id, es_vrf->v6_nhg_id);
+ bgp_evpn_l3nhg_activate(es_vrf, false /* update */);
+
+ /* update paths in the VRF that may already be associated with
+ * this destination ES
+ */
+ bgp_evpn_es_path_update_on_es_vrf_chg(es_vrf, "es-vrf-create");
+
+ return es_vrf;
+}
+
+/* remove the L3-NHG associated with the ES-VRF and free it */
+static void bgp_evpn_es_vrf_delete(struct bgp_evpn_es_vrf *es_vrf)
+{
+ struct bgp_evpn_es *es = es_vrf->es;
+ struct bgp *bgp_vrf = es_vrf->bgp_vrf;
+
+ if (BGP_DEBUG(evpn_mh, EVPN_MH_ES))
+ zlog_debug("es %s vrf %u nhg %u delete", es->esi_str,
+ bgp_vrf->vrf_id, es_vrf->nhg_id);
+
+ /* Remove the NHG resources */
+ bgp_evpn_l3nhg_deactivate(es_vrf);
+ if (es_vrf->nhg_id)
+ bgp_l3nhg_id_free(es_vrf->nhg_id);
+ es_vrf->nhg_id = 0;
+ if (es_vrf->v6_nhg_id)
+ bgp_l3nhg_id_free(es_vrf->v6_nhg_id);
+ es_vrf->v6_nhg_id = 0;
+
+ /* remove from the ES's VRF list */
+ list_delete_node(es->es_vrf_list, &es_vrf->es_listnode);
+
+ /* remove from the VRF-ESI rb tree */
+ RB_REMOVE(bgp_es_vrf_rb_head, &bgp_vrf->es_vrf_rb_tree, es_vrf);
+
+ /* update paths in the VRF that may already be associated with
+ * this destination ES
+ */
+ bgp_evpn_es_path_update_on_es_vrf_chg(es_vrf, "es-vrf-delete");
+
+ XFREE(MTYPE_BGP_EVPN_ES_VRF, es_vrf);
+}
+
+/* deref and delete if there are no references */
+void bgp_evpn_es_vrf_deref(struct bgp_evpn_es_evi *es_evi)
+{
+ struct bgp_evpn_es_vrf *es_vrf = es_evi->es_vrf;
+
+ if (!es_vrf)
+ return;
+
+ if (BGP_DEBUG(evpn_mh, EVPN_MH_ES))
+ zlog_debug("es-evi %s vni %u vrf %u de-ref",
+ es_evi->es->esi_str, es_evi->vpn->vni,
+ es_vrf->bgp_vrf->vrf_id);
+
+ es_evi->es_vrf = NULL;
+ if (es_vrf->ref_cnt)
+ --es_vrf->ref_cnt;
+
+ if (!es_vrf->ref_cnt)
+ bgp_evpn_es_vrf_delete(es_vrf);
+}
+
+/* find or create and reference */
+void bgp_evpn_es_vrf_ref(struct bgp_evpn_es_evi *es_evi, struct bgp *bgp_vrf)
+{
+ struct bgp_evpn_es *es = es_evi->es;
+ struct bgp_evpn_es_vrf *es_vrf = es_evi->es_vrf;
+ struct bgp *old_bgp_vrf = NULL;
+
+ if (es_vrf)
+ old_bgp_vrf = es_vrf->bgp_vrf;
+
+ if (old_bgp_vrf == bgp_vrf)
+ return;
+
+ /* deref the old ES-VRF */
+ bgp_evpn_es_vrf_deref(es_evi);
+
+ if (!bgp_vrf)
+ return;
+
+ if (BGP_DEBUG(evpn_mh, EVPN_MH_ES))
+ zlog_debug("es-evi %s vni %u vrf %u ref", es_evi->es->esi_str,
+ es_evi->vpn->vni, bgp_vrf->vrf_id);
+
+ /* find-create the new ES-VRF */
+ es_vrf = bgp_evpn_es_vrf_find(es, bgp_vrf);
+ if (!es_vrf)
+ es_vrf = bgp_evpn_es_vrf_create(es, bgp_vrf);
+
+ es_evi->es_vrf = es_vrf;
+ ++es_vrf->ref_cnt;
+}
+
+/* When the L2-VNI is associated with a L3-VNI/VRF update all the
+ * associated ES-EVI entries
+ */
+void bgp_evpn_es_evi_vrf_deref(struct bgpevpn *vpn)
+{
+ struct bgp_evpn_es_evi *es_evi;
+
+ if (BGP_DEBUG(evpn_mh, EVPN_MH_ES))
+ zlog_debug("es-vrf de-ref for vni %u", vpn->vni);
+
+ RB_FOREACH (es_evi, bgp_es_evi_rb_head, &vpn->es_evi_rb_tree)
+ bgp_evpn_es_vrf_deref(es_evi);
+}
+void bgp_evpn_es_evi_vrf_ref(struct bgpevpn *vpn)
+{
+ struct bgp_evpn_es_evi *es_evi;
+
+ if (BGP_DEBUG(evpn_mh, EVPN_MH_ES))
+ zlog_debug("es-vrf ref for vni %u", vpn->vni);
+
+ RB_FOREACH (es_evi, bgp_es_evi_rb_head, &vpn->es_evi_rb_tree)
+ bgp_evpn_es_vrf_ref(es_evi, vpn->bgp_vrf);
+}
+
+/* 1. If ES-VRF is not present install the host route with the exploded/flat
+ * multi-path list.
+ * 2. If ES-VRF is present -
+ * - if L3NHG has not been activated for the ES-VRF (this could be because
+ * all the PEs attached to the VRF are down) do not install the route
+ * in zebra.
+ * - if L3NHG has been activated install the route via that L3NHG
+ */
+void bgp_evpn_es_vrf_use_nhg(struct bgp *bgp_vrf, esi_t *esi, bool *use_l3nhg,
+ bool *is_l3nhg_active,
+ struct bgp_evpn_es_vrf **es_vrf_p)
+{
+ struct bgp_evpn_es *es;
+ struct bgp_evpn_es_vrf *es_vrf;
+
+ if (!bgp_mh_info->host_routes_use_l3nhg)
+ return;
+
+ es = bgp_evpn_es_find(esi);
+ if (!es)
+ return;
+
+ es_vrf = bgp_evpn_es_vrf_find(es, bgp_vrf);
+ if (!es_vrf)
+ return;
+
+ *use_l3nhg = true;
+ if (es_vrf->flags & BGP_EVPNES_VRF_NHG_ACTIVE)
+ *is_l3nhg_active = true;
+ if (es_vrf_p)
+ *es_vrf_p = es_vrf;
+}
+
+/* returns false if legacy-exploded mp needs to be used for route install */
+bool bgp_evpn_path_es_use_nhg(struct bgp *bgp_vrf, struct bgp_path_info *pi,
+ uint32_t *nhg_p)
+{
+ esi_t *esi;
+ struct bgp_evpn_es_vrf *es_vrf = NULL;
+ struct bgp_path_info *parent_pi;
+ struct bgp_node *rn;
+ struct prefix_evpn *evp;
+ struct bgp_path_info *mpinfo;
+ bool use_l3nhg = false;
+ bool is_l3nhg_active = false;
+
+ *nhg_p = 0;
+
+ /* we don't support NHG for routes leaked from another VRF yet */
+ if (pi->extra && pi->extra->bgp_orig)
+ return false;
+
+ parent_pi = get_route_parent_evpn(pi);
+ if (!parent_pi)
+ return false;
+
+ rn = parent_pi->net;
+ if (!rn)
+ return false;
+
+ evp = (struct prefix_evpn *)&rn->p;
+ if (evp->prefix.route_type != BGP_EVPN_MAC_IP_ROUTE)
+ return false;
+
+ /* non-es path, use legacy-exploded multipath */
+ esi = bgp_evpn_attr_get_esi(parent_pi->attr);
+ if (!memcmp(esi, zero_esi, sizeof(*esi)))
+ return false;
+
+ bgp_evpn_es_vrf_use_nhg(bgp_vrf, esi, &use_l3nhg, &is_l3nhg_active,
+ &es_vrf);
+
+ /* L3NHG support is disabled, use legacy-exploded multipath */
+ if (!use_l3nhg)
+ return false;
+
+ /* if the NHG has not been installed we cannot install the route yet,
+ * return a 0-NHG to indicate that
+ */
+ if (!is_l3nhg_active)
+ return true;
+
+ /* this needs to be set the v6NHG if v6route */
+ if (is_evpn_prefix_ipaddr_v6(evp))
+ *nhg_p = es_vrf->v6_nhg_id;
+ else
+ *nhg_p = es_vrf->nhg_id;
+
+ for (mpinfo = bgp_path_info_mpath_next(pi); mpinfo;
+ mpinfo = bgp_path_info_mpath_next(mpinfo)) {
+ /* if any of the paths have a different ESI we can't use
+ * the NHG associated with the ES. fallback to legacy-exploded
+ * multipath
+ */
+ if (memcmp(esi, bgp_evpn_attr_get_esi(mpinfo->attr),
+ sizeof(*esi)))
+ return false;
+ }
+
+ return true;
+}
+
+static void bgp_evpn_es_vrf_show_entry(struct vty *vty,
+ struct bgp_evpn_es_vrf *es_vrf,
+ json_object *json)
+{
+ struct bgp_evpn_es *es = es_vrf->es;
+ struct bgp *bgp_vrf = es_vrf->bgp_vrf;
+
+ if (json) {
+ json_object *json_types;
+
+ json_object_string_add(json, "esi", es->esi_str);
+ json_object_string_add(json, "vrf", bgp_vrf->name);
+
+ if (es_vrf->flags & (BGP_EVPNES_VRF_NHG_ACTIVE)) {
+ json_types = json_object_new_array();
+ if (es_vrf->flags & BGP_EVPNES_VRF_NHG_ACTIVE)
+ json_array_string_add(json_types, "active");
+ json_object_object_add(json, "flags", json_types);
+ }
+
+ json_object_int_add(json, "ipv4NHG", es_vrf->nhg_id);
+ json_object_int_add(json, "ipv6NHG", es_vrf->v6_nhg_id);
+ json_object_int_add(json, "refCount", es_vrf->ref_cnt);
+ } else {
+ char flags_str[4];
+
+ flags_str[0] = '\0';
+ if (es_vrf->flags & BGP_EVPNES_VRF_NHG_ACTIVE)
+ strlcat(flags_str, "A", sizeof(flags_str));
+
+ vty_out(vty, "%-30s %-15s %-5s %-8u %-8u %u\n", es->esi_str,
+ bgp_vrf->name, flags_str, es_vrf->nhg_id,
+ es_vrf->v6_nhg_id, es_vrf->ref_cnt);
+ }
+}
+
+static void bgp_evpn_es_vrf_show_es(struct vty *vty, json_object *json_array,
+ struct bgp_evpn_es *es)
+{
+ json_object *json = NULL;
+ struct listnode *es_vrf_node;
+ struct bgp_evpn_es_vrf *es_vrf;
+
+ for (ALL_LIST_ELEMENTS_RO(es->es_vrf_list, es_vrf_node, es_vrf)) {
+ /* create a separate json object for each ES-VRF */
+ if (json_array)
+ json = json_object_new_object();
+ bgp_evpn_es_vrf_show_entry(vty, es_vrf, json);
+ /* add ES-VRF to the json array */
+ if (json_array)
+ json_object_array_add(json_array, json);
+ }
+}
+
+/* Display all ES VRFs */
+void bgp_evpn_es_vrf_show(struct vty *vty, bool uj, struct bgp_evpn_es *es)
+{
+ json_object *json_array = NULL;
+
+ if (uj) {
+ /* create an array of ESs */
+ json_array = json_object_new_array();
+ } else {
+ vty_out(vty, "ES-VRF Flags: A Active\n");
+ vty_out(vty, "%-30s %-15s %-5s %-8s %-8s %s\n", "ESI", "VRF",
+ "Flags", "IPv4-NHG", "IPv6-NHG", "Ref");
+ }
+
+ if (es) {
+ bgp_evpn_es_vrf_show_es(vty, json_array, es);
+ } else {
+ RB_FOREACH (es, bgp_es_rb_head, &bgp_mh_info->es_rb_tree)
+ bgp_evpn_es_vrf_show_es(vty, json_array, es);
+ }
+
+ /* print the array of json-ESs */
+ if (uj)
+ vty_json(vty, json_array);
+}
+
+/* Display specific ES VRF */
+void bgp_evpn_es_vrf_show_esi(struct vty *vty, esi_t *esi, bool uj)
+{
+ struct bgp_evpn_es *es;
+
+ es = bgp_evpn_es_find(esi);
+ if (es) {
+ bgp_evpn_es_vrf_show(vty, uj, es);
+ } else {
+ if (!uj)
+ vty_out(vty, "ESI not found\n");
+ }
+}
+
+/*****************************************************************************/
+/* Ethernet Segment to EVI association -
+ * 1. The ES-EVI entry is maintained as a RB tree per L2-VNI
+ * (bgpevpn->es_evi_rb_tree).
+ * 2. Each local ES-EVI entry is rxed from zebra and then used by BGP to
+ * advertises an EAD-EVI (Type-1 EVPN) route
+ * 3. The remote ES-EVI is created when a bgp_evpn_es_evi_vtep references
+ * it.
+ */
+
+/* A list of remote VTEPs is maintained for each ES-EVI. This list includes -
+ * 1. VTEPs for which we have imported the EAD-per-ES Type1 route
+ * 2. VTEPs for which we have imported the EAD-per-EVI Type1 route
+ * VTEPs for which both routes have been rxed are activated. Activation
+ * creates a NHG in the parent ES.
+ */
+static int bgp_evpn_es_evi_vtep_cmp(void *p1, void *p2)
+{
+ const struct bgp_evpn_es_evi_vtep *evi_vtep1 = p1;
+ const struct bgp_evpn_es_evi_vtep *evi_vtep2 = p2;
+
+ return evi_vtep1->vtep_ip.s_addr - evi_vtep2->vtep_ip.s_addr;
+}
+
+static struct bgp_evpn_es_evi_vtep *bgp_evpn_es_evi_vtep_new(
+ struct bgp_evpn_es_evi *es_evi, struct in_addr vtep_ip)
+{
+ struct bgp_evpn_es_evi_vtep *evi_vtep;
+
+ evi_vtep = XCALLOC(MTYPE_BGP_EVPN_ES_EVI_VTEP, sizeof(*evi_vtep));
+
+ evi_vtep->es_evi = es_evi;
+ evi_vtep->vtep_ip.s_addr = vtep_ip.s_addr;
+ listnode_init(&evi_vtep->es_evi_listnode, evi_vtep);
+ listnode_add_sort(es_evi->es_evi_vtep_list, &evi_vtep->es_evi_listnode);
+
+ return evi_vtep;
+}
+
+static void bgp_evpn_es_evi_vtep_free(struct bgp_evpn_es_evi_vtep *evi_vtep)
+{
+ struct bgp_evpn_es_evi *es_evi = evi_vtep->es_evi;
+
+ if (evi_vtep->flags & (BGP_EVPN_EVI_VTEP_EAD))
+ /* as long as there is some reference we can't free it */
+ return;
+
+ list_delete_node(es_evi->es_evi_vtep_list, &evi_vtep->es_evi_listnode);
+ XFREE(MTYPE_BGP_EVPN_ES_EVI_VTEP, evi_vtep);
+}
+
+/* check if VTEP is already part of the list */
+static struct bgp_evpn_es_evi_vtep *bgp_evpn_es_evi_vtep_find(
+ struct bgp_evpn_es_evi *es_evi, struct in_addr vtep_ip)
+{
+ struct listnode *node = NULL;
+ struct bgp_evpn_es_evi_vtep *evi_vtep;
+
+ for (ALL_LIST_ELEMENTS_RO(es_evi->es_evi_vtep_list, node, evi_vtep)) {
+ if (evi_vtep->vtep_ip.s_addr == vtep_ip.s_addr)
+ return evi_vtep;
+ }
+ return NULL;
+}
+
+/* A VTEP can be added as "active" attach to an ES if EAD-per-ES and
+ * EAD-per-EVI routes are rxed from it.
+ */
+static void bgp_evpn_es_evi_vtep_re_eval_active(struct bgp *bgp,
+ struct bgp_evpn_es_evi_vtep *evi_vtep)
+{
+ bool old_active;
+ bool new_active;
+ uint32_t ead_activity_flags;
+
+ old_active = CHECK_FLAG(evi_vtep->flags, BGP_EVPN_EVI_VTEP_ACTIVE);
+
+ if (bgp_mh_info->ead_evi_rx)
+ /* Both EAD-per-ES and EAD-per-EVI routes must be rxed from a PE
+ * before it can be activated.
+ */
+ ead_activity_flags = BGP_EVPN_EVI_VTEP_EAD;
+ else
+ /* EAD-per-ES is sufficent to activate the PE */
+ ead_activity_flags = BGP_EVPN_EVI_VTEP_EAD_PER_ES;
+
+ if ((evi_vtep->flags & ead_activity_flags) == ead_activity_flags)
+ SET_FLAG(evi_vtep->flags, BGP_EVPN_EVI_VTEP_ACTIVE);
+ else
+ UNSET_FLAG(evi_vtep->flags, BGP_EVPN_EVI_VTEP_ACTIVE);
+
+ new_active = CHECK_FLAG(evi_vtep->flags, BGP_EVPN_EVI_VTEP_ACTIVE);
+
+ if (old_active == new_active)
+ return;
+
+ if (BGP_DEBUG(evpn_mh, EVPN_MH_ES))
+ zlog_debug("es %s evi %u vtep %pI4 %s",
+ evi_vtep->es_evi->es->esi_str,
+ evi_vtep->es_evi->vpn->vni, &evi_vtep->vtep_ip,
+ new_active ? "active" : "inactive");
+
+ /* add VTEP to parent es */
+ if (new_active)
+ evi_vtep->es_vtep = bgp_evpn_es_vtep_add(
+ bgp, evi_vtep->es_evi->es, evi_vtep->vtep_ip,
+ false /*esr*/, 0, 0);
+ else {
+ if (evi_vtep->es_vtep) {
+ bgp_evpn_es_vtep_do_del(bgp, evi_vtep->es_vtep,
+ false /*esr*/);
+ evi_vtep->es_vtep = NULL;
+ }
+ }
+ /* queue up the parent es for background consistency checks */
+ bgp_evpn_es_cons_checks_pend_add(evi_vtep->es_evi->es);
+}
+
+static void bgp_evpn_es_evi_vtep_add(struct bgp *bgp,
+ struct bgp_evpn_es_evi *es_evi, struct in_addr vtep_ip,
+ bool ead_es)
+{
+ struct bgp_evpn_es_evi_vtep *evi_vtep;
+
+ evi_vtep = bgp_evpn_es_evi_vtep_find(es_evi, vtep_ip);
+
+ if (!evi_vtep)
+ evi_vtep = bgp_evpn_es_evi_vtep_new(es_evi, vtep_ip);
+
+ if (BGP_DEBUG(evpn_mh, EVPN_MH_ES))
+ zlog_debug("add es %s evi %u vtep %pI4 %s",
+ evi_vtep->es_evi->es->esi_str,
+ evi_vtep->es_evi->vpn->vni, &evi_vtep->vtep_ip,
+ ead_es ? "ead_es" : "ead_evi");
+
+ if (ead_es)
+ SET_FLAG(evi_vtep->flags, BGP_EVPN_EVI_VTEP_EAD_PER_ES);
+ else
+ SET_FLAG(evi_vtep->flags, BGP_EVPN_EVI_VTEP_EAD_PER_EVI);
+
+ bgp_evpn_es_evi_vtep_re_eval_active(bgp, evi_vtep);
+}
+
+static void bgp_evpn_es_evi_vtep_del(struct bgp *bgp,
+ struct bgp_evpn_es_evi *es_evi, struct in_addr vtep_ip,
+ bool ead_es)
+{
+ struct bgp_evpn_es_evi_vtep *evi_vtep;
+
+ evi_vtep = bgp_evpn_es_evi_vtep_find(es_evi, vtep_ip);
+ if (!evi_vtep)
+ return;
+
+ if (BGP_DEBUG(evpn_mh, EVPN_MH_ES))
+ zlog_debug("del es %s evi %u vtep %pI4 %s",
+ evi_vtep->es_evi->es->esi_str,
+ evi_vtep->es_evi->vpn->vni, &evi_vtep->vtep_ip,
+ ead_es ? "ead_es" : "ead_evi");
+
+ if (ead_es)
+ UNSET_FLAG(evi_vtep->flags, BGP_EVPN_EVI_VTEP_EAD_PER_ES);
+ else
+ UNSET_FLAG(evi_vtep->flags, BGP_EVPN_EVI_VTEP_EAD_PER_EVI);
+
+ bgp_evpn_es_evi_vtep_re_eval_active(bgp, evi_vtep);
+ bgp_evpn_es_evi_vtep_free(evi_vtep);
+}
+
+/* compare ES-IDs for the ES-EVI RB tree maintained per-VNI */
+static int bgp_es_evi_rb_cmp(const struct bgp_evpn_es_evi *es_evi1,
+ const struct bgp_evpn_es_evi *es_evi2)
+{
+ return memcmp(&es_evi1->es->esi, &es_evi2->es->esi, ESI_BYTES);
+}
+RB_GENERATE(bgp_es_evi_rb_head, bgp_evpn_es_evi, rb_node, bgp_es_evi_rb_cmp);
+
+/* find the ES-EVI in the per-L2-VNI RB tree */
+static struct bgp_evpn_es_evi *bgp_evpn_es_evi_find(struct bgp_evpn_es *es,
+ struct bgpevpn *vpn)
+{
+ struct bgp_evpn_es_evi es_evi;
+
+ es_evi.es = es;
+
+ return RB_FIND(bgp_es_evi_rb_head, &vpn->es_evi_rb_tree, &es_evi);
+}
+
+/* allocate a new ES-EVI and insert it into the per-L2-VNI and per-ES
+ * tables.
+ */
+static struct bgp_evpn_es_evi *bgp_evpn_es_evi_new(struct bgp_evpn_es *es,
+ struct bgpevpn *vpn)
+{
+ struct bgp_evpn_es_evi *es_evi;
+
+ es_evi = XCALLOC(MTYPE_BGP_EVPN_ES_EVI, sizeof(*es_evi));
+
+ es_evi->es = es;
+ es_evi->vpn = vpn;
+
+ /* Initialise the VTEP list */
+ es_evi->es_evi_vtep_list = list_new();
+ listset_app_node_mem(es_evi->es_evi_vtep_list);
+ es_evi->es_evi_vtep_list->cmp = bgp_evpn_es_evi_vtep_cmp;
+
+ /* insert into the VNI-ESI rb tree */
+ RB_INSERT(bgp_es_evi_rb_head, &vpn->es_evi_rb_tree, es_evi);
+
+ /* add to the ES's VNI list */
+ listnode_init(&es_evi->es_listnode, es_evi);
+ listnode_add(es->es_evi_list, &es_evi->es_listnode);
+
+ bgp_evpn_es_vrf_ref(es_evi, vpn->bgp_vrf);
+
+ return es_evi;
+}
+
+/* remove the ES-EVI from the per-L2-VNI and per-ES tables and free
+ * up the memory.
+ */
+static struct bgp_evpn_es_evi *
+bgp_evpn_es_evi_free(struct bgp_evpn_es_evi *es_evi)
+{
+ struct bgp_evpn_es *es = es_evi->es;
+ struct bgpevpn *vpn = es_evi->vpn;
+
+ /* cannot free the element as long as there is a local or remote
+ * reference
+ */
+ if (es_evi->flags & (BGP_EVPNES_EVI_LOCAL | BGP_EVPNES_EVI_REMOTE))
+ return es_evi;
+ bgp_evpn_es_frag_evi_del(es_evi, false);
+ bgp_evpn_es_vrf_deref(es_evi);
+
+ /* remove from the ES's VNI list */
+ list_delete_node(es->es_evi_list, &es_evi->es_listnode);
+
+ /* remove from the VNI-ESI rb tree */
+ RB_REMOVE(bgp_es_evi_rb_head, &vpn->es_evi_rb_tree, es_evi);
+
+ /* free the VTEP list */
+ list_delete(&es_evi->es_evi_vtep_list);
+
+ /* remove from the VNI-ESI rb tree */
+ XFREE(MTYPE_BGP_EVPN_ES_EVI, es_evi);
+
+ return NULL;
+}
+
+/* init local info associated with the ES-EVI */
+static void bgp_evpn_es_evi_local_info_set(struct bgp_evpn_es_evi *es_evi)
+{
+ struct bgpevpn *vpn = es_evi->vpn;
+
+ if (CHECK_FLAG(es_evi->flags, BGP_EVPNES_EVI_LOCAL))
+ return;
+
+ SET_FLAG(es_evi->flags, BGP_EVPNES_EVI_LOCAL);
+ listnode_init(&es_evi->l2vni_listnode, es_evi);
+ listnode_add(vpn->local_es_evi_list, &es_evi->l2vni_listnode);
+ bgp_evpn_es_frag_evi_add(es_evi);
+}
+
+/* clear any local info associated with the ES-EVI */
+static struct bgp_evpn_es_evi *
+bgp_evpn_es_evi_local_info_clear(struct bgp_evpn_es_evi *es_evi)
+{
+ struct bgpevpn *vpn = es_evi->vpn;
+
+ UNSET_FLAG(es_evi->flags, BGP_EVPNES_EVI_LOCAL);
+ list_delete_node(vpn->local_es_evi_list, &es_evi->l2vni_listnode);
+
+ return bgp_evpn_es_evi_free(es_evi);
+}
+
+/* eval remote info associated with the ES */
+static void bgp_evpn_es_evi_remote_info_re_eval(struct bgp_evpn_es_evi *es_evi)
+{
+ struct bgp_evpn_es *es = es_evi->es;
+
+ /* if there are remote VTEPs the ES-EVI is classified as "remote" */
+ if (listcount(es_evi->es_evi_vtep_list)) {
+ if (!CHECK_FLAG(es_evi->flags, BGP_EVPNES_EVI_REMOTE)) {
+ SET_FLAG(es_evi->flags, BGP_EVPNES_EVI_REMOTE);
+ ++es->remote_es_evi_cnt;
+ /* set remote on the parent es */
+ bgp_evpn_es_remote_info_re_eval(es);
+ }
+ } else {
+ if (CHECK_FLAG(es_evi->flags, BGP_EVPNES_EVI_REMOTE)) {
+ UNSET_FLAG(es_evi->flags, BGP_EVPNES_EVI_REMOTE);
+ if (es->remote_es_evi_cnt)
+ --es->remote_es_evi_cnt;
+ bgp_evpn_es_evi_free(es_evi);
+ /* check if "remote" can be cleared from the
+ * parent es.
+ */
+ bgp_evpn_es_remote_info_re_eval(es);
+ }
+ }
+}
+
+static struct bgp_evpn_es_evi *
+bgp_evpn_local_es_evi_do_del(struct bgp_evpn_es_evi *es_evi)
+{
+ struct prefix_evpn p;
+ struct bgp_evpn_es *es = es_evi->es;
+ struct bgp *bgp;
+
+ if (!CHECK_FLAG(es_evi->flags, BGP_EVPNES_EVI_LOCAL))
+ return es_evi;
+
+ if (BGP_DEBUG(evpn_mh, EVPN_MH_ES))
+ zlog_debug("del local es %s evi %u",
+ es_evi->es->esi_str,
+ es_evi->vpn->vni);
+
+ bgp = bgp_get_evpn();
+
+ /* remove the es_evi from the es_frag before sending the update */
+ bgp_evpn_es_frag_evi_del(es_evi, true);
+ if (bgp) {
+ /* update EAD-ES with new list of VNIs */
+ if (bgp_evpn_local_es_is_active(es))
+ bgp_evpn_ead_es_route_update(bgp, es);
+
+ /* withdraw and delete EAD-EVI */
+ if (CHECK_FLAG(es->flags, BGP_EVPNES_ADV_EVI)) {
+ build_evpn_type1_prefix(&p, BGP_EVPN_AD_EVI_ETH_TAG,
+ &es->esi, es->originator_ip);
+ if (bgp_evpn_ead_evi_route_delete(bgp, es, es_evi->vpn,
+ &p))
+ flog_err(EC_BGP_EVPN_ROUTE_DELETE,
+ "%u: EAD-EVI route deletion failure for ESI %s VNI %u",
+ bgp->vrf_id, es->esi_str,
+ es_evi->vpn->vni);
+ }
+ }
+
+ return bgp_evpn_es_evi_local_info_clear(es_evi);
+}
+
+int bgp_evpn_local_es_evi_del(struct bgp *bgp, esi_t *esi, vni_t vni)
+{
+ struct bgpevpn *vpn;
+ struct bgp_evpn_es *es;
+ struct bgp_evpn_es_evi *es_evi;
+ char buf[ESI_STR_LEN];
+
+ es = bgp_evpn_es_find(esi);
+ if (!es) {
+ flog_err(
+ EC_BGP_ES_CREATE,
+ "%u: Failed to deref VNI %d from ESI %s; ES not present",
+ bgp->vrf_id, vni,
+ esi_to_str(esi, buf, sizeof(buf)));
+ return -1;
+ }
+
+ vpn = bgp_evpn_lookup_vni(bgp, vni);
+ if (!vpn) {
+ flog_err(
+ EC_BGP_ES_CREATE,
+ "%u: Failed to deref VNI %d from ESI %s; VNI not present",
+ bgp->vrf_id, vni, es->esi_str);
+ return -1;
+ }
+
+ es_evi = bgp_evpn_es_evi_find(es, vpn);
+ if (!es_evi) {
+ flog_err(
+ EC_BGP_ES_CREATE,
+ "%u: Failed to deref VNI %d from ESI %s; ES-VNI not present",
+ bgp->vrf_id, vni, es->esi_str);
+ return -1;
+ }
+
+ bgp_evpn_local_es_evi_do_del(es_evi);
+ return 0;
+}
+
+/* Create ES-EVI and advertise the corresponding EAD routes */
+int bgp_evpn_local_es_evi_add(struct bgp *bgp, esi_t *esi, vni_t vni)
+{
+ struct bgpevpn *vpn;
+ struct prefix_evpn p;
+ struct bgp_evpn_es *es;
+ struct bgp_evpn_es_evi *es_evi;
+ char buf[ESI_STR_LEN];
+
+ es = bgp_evpn_es_find(esi);
+ if (!es) {
+ flog_err(
+ EC_BGP_ES_CREATE,
+ "%u: Failed to associate VNI %d with ESI %s; ES not present",
+ bgp->vrf_id, vni,
+ esi_to_str(esi, buf, sizeof(buf)));
+ return -1;
+ }
+
+ vpn = bgp_evpn_lookup_vni(bgp, vni);
+ if (!vpn) {
+ flog_err(
+ EC_BGP_ES_CREATE,
+ "%u: Failed to associate VNI %d with ESI %s; VNI not present",
+ bgp->vrf_id, vni, es->esi_str);
+ return -1;
+ }
+
+ if (BGP_DEBUG(evpn_mh, EVPN_MH_ES))
+ zlog_debug("add local es %s evi %u",
+ es->esi_str, vni);
+
+ es_evi = bgp_evpn_es_evi_find(es, vpn);
+
+ if (es_evi) {
+ if (CHECK_FLAG(es_evi->flags, BGP_EVPNES_EVI_LOCAL))
+ /* dup */
+ return 0;
+ } else
+ es_evi = bgp_evpn_es_evi_new(es, vpn);
+
+ bgp_evpn_es_evi_local_info_set(es_evi);
+
+ /* generate an EAD-EVI for this new VNI */
+ if (CHECK_FLAG(es->flags, BGP_EVPNES_ADV_EVI)) {
+ build_evpn_type1_prefix(&p, BGP_EVPN_AD_EVI_ETH_TAG, &es->esi,
+ es->originator_ip);
+ bgp_evpn_ead_evi_route_update(bgp, es, vpn, &p);
+ }
+
+ /* update EAD-ES */
+ if (bgp_evpn_local_es_is_active(es))
+ bgp_evpn_ead_es_route_update(bgp, es);
+
+ return 0;
+}
+
+/* Add remote ES-EVI entry. This is actually the remote VTEP add and the
+ * ES-EVI is implicity created on first VTEP's reference.
+ */
+int bgp_evpn_remote_es_evi_add(struct bgp *bgp, struct bgpevpn *vpn,
+ const struct prefix_evpn *p)
+{
+ char buf[ESI_STR_LEN];
+ struct bgp_evpn_es *es;
+ struct bgp_evpn_es_evi *es_evi;
+ bool ead_es;
+ const esi_t *esi = &p->prefix.ead_addr.esi;
+
+ if (!vpn)
+ /* local EAD-ES need not be sent back to zebra */
+ return 0;
+
+ if (BGP_DEBUG(evpn_mh, EVPN_MH_ES))
+ zlog_debug("add remote %s es %s evi %u vtep %pI4",
+ p->prefix.ead_addr.eth_tag ? "ead-es" : "ead-evi",
+ esi_to_str(esi, buf, sizeof(buf)), vpn->vni,
+ &p->prefix.ead_addr.ip.ipaddr_v4);
+
+ es = bgp_evpn_es_find(esi);
+ if (!es)
+ es = bgp_evpn_es_new(bgp, esi);
+
+ es_evi = bgp_evpn_es_evi_find(es, vpn);
+ if (!es_evi)
+ es_evi = bgp_evpn_es_evi_new(es, vpn);
+
+ ead_es = !!p->prefix.ead_addr.eth_tag;
+ bgp_evpn_es_evi_vtep_add(bgp, es_evi, p->prefix.ead_addr.ip.ipaddr_v4,
+ ead_es);
+
+ bgp_evpn_es_evi_remote_info_re_eval(es_evi);
+ return 0;
+}
+
+/* A remote VTEP has withdrawn. The es-evi-vtep will be deleted and the
+ * parent es-evi freed up implicitly in last VTEP's deref.
+ */
+int bgp_evpn_remote_es_evi_del(struct bgp *bgp, struct bgpevpn *vpn,
+ const struct prefix_evpn *p)
+{
+ char buf[ESI_STR_LEN];
+ struct bgp_evpn_es *es;
+ struct bgp_evpn_es_evi *es_evi;
+ bool ead_es;
+
+ if (!vpn)
+ /* local EAD-ES need not be sent back to zebra */
+ return 0;
+
+ if (BGP_DEBUG(evpn_mh, EVPN_MH_ES))
+ zlog_debug(
+ "del remote %s es %s evi %u vtep %pI4",
+ p->prefix.ead_addr.eth_tag ? "ead-es" : "ead-evi",
+ esi_to_str(&p->prefix.ead_addr.esi, buf, sizeof(buf)),
+ vpn->vni, &p->prefix.ead_addr.ip.ipaddr_v4);
+
+ es = bgp_evpn_es_find(&p->prefix.ead_addr.esi);
+ if (!es) {
+ if (BGP_DEBUG(evpn_mh, EVPN_MH_ES))
+ zlog_debug(
+ "del remote %s es %s evi %u vtep %pI4, NO es",
+ p->prefix.ead_addr.eth_tag ? "ead-es"
+ : "ead-evi",
+ esi_to_str(&p->prefix.ead_addr.esi, buf,
+ sizeof(buf)),
+ vpn->vni, &p->prefix.ead_addr.ip.ipaddr_v4);
+ return 0;
+ }
+ es_evi = bgp_evpn_es_evi_find(es, vpn);
+ if (!es_evi) {
+ if (BGP_DEBUG(evpn_mh, EVPN_MH_ES))
+ zlog_debug(
+ "del remote %s es %s evi %u vtep %pI4, NO es-evi",
+ p->prefix.ead_addr.eth_tag ? "ead-es"
+ : "ead-evi",
+ esi_to_str(&p->prefix.ead_addr.esi, buf,
+ sizeof(buf)),
+ vpn->vni,
+ &p->prefix.ead_addr.ip.ipaddr_v4);
+ return 0;
+ }
+
+ ead_es = !!p->prefix.ead_addr.eth_tag;
+ bgp_evpn_es_evi_vtep_del(bgp, es_evi, p->prefix.ead_addr.ip.ipaddr_v4,
+ ead_es);
+ bgp_evpn_es_evi_remote_info_re_eval(es_evi);
+ return 0;
+}
+
+/* If a VNI is being deleted we need to force del all remote VTEPs */
+static void bgp_evpn_remote_es_evi_flush(struct bgp_evpn_es_evi *es_evi)
+{
+ struct listnode *node = NULL;
+ struct listnode *nnode = NULL;
+ struct bgp_evpn_es_evi_vtep *evi_vtep;
+ struct bgp *bgp;
+
+ bgp = bgp_get_evpn();
+ if (!bgp)
+ return;
+
+ /* delete all VTEPs */
+ for (ALL_LIST_ELEMENTS(es_evi->es_evi_vtep_list, node, nnode,
+ evi_vtep)) {
+ evi_vtep->flags &= ~(BGP_EVPN_EVI_VTEP_EAD_PER_ES
+ | BGP_EVPN_EVI_VTEP_EAD_PER_EVI);
+ bgp_evpn_es_evi_vtep_re_eval_active(bgp, evi_vtep);
+ bgp_evpn_es_evi_vtep_free(evi_vtep);
+ }
+ /* delete the EVI */
+ bgp_evpn_es_evi_remote_info_re_eval(es_evi);
+}
+
+/* Initialize the ES tables maintained per-L2_VNI */
+void bgp_evpn_vni_es_init(struct bgpevpn *vpn)
+{
+ /* Initialize the ES-EVI RB tree */
+ RB_INIT(bgp_es_evi_rb_head, &vpn->es_evi_rb_tree);
+
+ /* Initialize the local list maintained for quick walks by type */
+ vpn->local_es_evi_list = list_new();
+ listset_app_node_mem(vpn->local_es_evi_list);
+}
+
+/* Cleanup the ES info maintained per-L2_VNI */
+void bgp_evpn_vni_es_cleanup(struct bgpevpn *vpn)
+{
+ struct bgp_evpn_es_evi *es_evi;
+ struct bgp_evpn_es_evi *es_evi_next;
+
+ RB_FOREACH_SAFE(es_evi, bgp_es_evi_rb_head,
+ &vpn->es_evi_rb_tree, es_evi_next) {
+ es_evi = bgp_evpn_local_es_evi_do_del(es_evi);
+ if (es_evi)
+ bgp_evpn_remote_es_evi_flush(es_evi);
+ }
+
+ list_delete(&vpn->local_es_evi_list);
+}
+
+static char *bgp_evpn_es_evi_vteps_str(char *vtep_str,
+ struct bgp_evpn_es_evi *es_evi,
+ uint8_t vtep_str_size)
+{
+ char vtep_flag_str[BGP_EVPN_FLAG_STR_SZ];
+ struct listnode *node;
+ struct bgp_evpn_es_evi_vtep *evi_vtep;
+ bool first = true;
+ char ip_buf[INET6_ADDRSTRLEN];
+
+ vtep_str[0] = '\0';
+ for (ALL_LIST_ELEMENTS_RO(es_evi->es_evi_vtep_list, node, evi_vtep)) {
+ vtep_flag_str[0] = '\0';
+ if (evi_vtep->flags & BGP_EVPN_EVI_VTEP_EAD_PER_ES)
+ strlcat(vtep_flag_str, "E", sizeof(vtep_flag_str));
+ if (evi_vtep->flags & BGP_EVPN_EVI_VTEP_EAD_PER_EVI)
+ strlcat(vtep_flag_str, "V", sizeof(vtep_flag_str));
+
+ if (!strnlen(vtep_flag_str, sizeof(vtep_flag_str)))
+ strlcpy(vtep_flag_str, "-", sizeof(vtep_flag_str));
+ if (first)
+ first = false;
+ else
+ strlcat(vtep_str, ",", vtep_str_size);
+ strlcat(vtep_str,
+ inet_ntop(AF_INET, &evi_vtep->vtep_ip, ip_buf,
+ sizeof(ip_buf)),
+ vtep_str_size);
+ strlcat(vtep_str, "(", vtep_str_size);
+ strlcat(vtep_str, vtep_flag_str, vtep_str_size);
+ strlcat(vtep_str, ")", vtep_str_size);
+ }
+
+ return vtep_str;
+}
+
+static void bgp_evpn_es_evi_json_vtep_fill(json_object *json_vteps,
+ struct bgp_evpn_es_evi_vtep *evi_vtep)
+{
+ json_object *json_vtep_entry;
+ json_object *json_flags;
+
+ json_vtep_entry = json_object_new_object();
+
+ json_object_string_addf(json_vtep_entry, "vtep_ip", "%pI4",
+ &evi_vtep->vtep_ip);
+ if (evi_vtep->flags & (BGP_EVPN_EVI_VTEP_EAD_PER_ES |
+ BGP_EVPN_EVI_VTEP_EAD_PER_EVI)) {
+ json_flags = json_object_new_array();
+ if (evi_vtep->flags & BGP_EVPN_EVI_VTEP_EAD_PER_ES)
+ json_array_string_add(json_flags, "ead-per-es");
+ if (evi_vtep->flags & BGP_EVPN_EVI_VTEP_EAD_PER_EVI)
+ json_array_string_add(json_flags, "ead-per-evi");
+ json_object_object_add(json_vtep_entry,
+ "flags", json_flags);
+ }
+
+ json_object_array_add(json_vteps,
+ json_vtep_entry);
+}
+
+static void bgp_evpn_es_evi_show_entry(struct vty *vty,
+ struct bgp_evpn_es_evi *es_evi, json_object *json)
+{
+ struct listnode *node;
+ struct bgp_evpn_es_evi_vtep *evi_vtep;
+
+ if (json) {
+ json_object *json_vteps;
+ json_object *json_types;
+
+ json_object_string_add(json, "esi", es_evi->es->esi_str);
+ json_object_int_add(json, "vni", es_evi->vpn->vni);
+
+ if (es_evi->flags & (BGP_EVPNES_EVI_LOCAL |
+ BGP_EVPNES_EVI_REMOTE)) {
+ json_types = json_object_new_array();
+ if (es_evi->flags & BGP_EVPNES_EVI_LOCAL)
+ json_array_string_add(json_types, "local");
+ if (es_evi->flags & BGP_EVPNES_EVI_REMOTE)
+ json_array_string_add(json_types, "remote");
+ json_object_object_add(json, "type", json_types);
+ }
+
+ if (listcount(es_evi->es_evi_vtep_list)) {
+ json_vteps = json_object_new_array();
+ for (ALL_LIST_ELEMENTS_RO(es_evi->es_evi_vtep_list,
+ node, evi_vtep)) {
+ bgp_evpn_es_evi_json_vtep_fill(json_vteps,
+ evi_vtep);
+ }
+ json_object_object_add(json, "vteps", json_vteps);
+ }
+ } else {
+ char type_str[4];
+ char vtep_str[ES_VTEP_LIST_STR_SZ + BGP_EVPN_VTEPS_FLAG_STR_SZ];
+
+ type_str[0] = '\0';
+ if (es_evi->flags & BGP_EVPNES_EVI_LOCAL)
+ strlcat(type_str, "L", sizeof(type_str));
+ if (es_evi->flags & BGP_EVPNES_EVI_REMOTE)
+ strlcat(type_str, "R", sizeof(type_str));
+ if (es_evi->flags & BGP_EVPNES_EVI_INCONS_VTEP_LIST)
+ strlcat(type_str, "I", sizeof(type_str));
+
+ bgp_evpn_es_evi_vteps_str(vtep_str, es_evi, sizeof(vtep_str));
+
+ vty_out(vty, "%-8d %-30s %-5s %s\n",
+ es_evi->vpn->vni, es_evi->es->esi_str,
+ type_str, vtep_str);
+ }
+}
+
+static void bgp_evpn_es_evi_show_entry_detail(struct vty *vty,
+ struct bgp_evpn_es_evi *es_evi, json_object *json)
+{
+ if (json) {
+ json_object *json_flags;
+
+ /* Add the "brief" info first */
+ bgp_evpn_es_evi_show_entry(vty, es_evi, json);
+ if (es_evi->es_frag)
+ json_object_string_addf(json, "esFragmentRd", "%pRD",
+ &es_evi->es_frag->prd);
+ if (es_evi->flags & BGP_EVPNES_EVI_INCONS_VTEP_LIST) {
+ json_flags = json_object_new_array();
+ json_array_string_add(json_flags, "es-vtep-mismatch");
+ json_object_object_add(json, "flags", json_flags);
+ }
+ } else {
+ char vtep_str[ES_VTEP_LIST_STR_SZ + BGP_EVPN_VTEPS_FLAG_STR_SZ];
+ char type_str[4];
+
+ type_str[0] = '\0';
+ if (es_evi->flags & BGP_EVPNES_EVI_LOCAL)
+ strlcat(type_str, "L", sizeof(type_str));
+ if (es_evi->flags & BGP_EVPNES_EVI_REMOTE)
+ strlcat(type_str, "R", sizeof(type_str));
+
+ bgp_evpn_es_evi_vteps_str(vtep_str, es_evi, sizeof(vtep_str));
+ if (!strlen(vtep_str))
+ strlcpy(vtep_str, "-", sizeof(type_str));
+
+ vty_out(vty, "VNI: %d ESI: %s\n",
+ es_evi->vpn->vni, es_evi->es->esi_str);
+ vty_out(vty, " Type: %s\n", type_str);
+ if (es_evi->es_frag)
+ vty_out(vty, " ES fragment RD: %pRD\n",
+ &es_evi->es_frag->prd);
+ vty_out(vty, " Inconsistencies: %s\n",
+ (es_evi->flags & BGP_EVPNES_EVI_INCONS_VTEP_LIST) ?
+ "es-vtep-mismatch":"-");
+ vty_out(vty, " VTEPs: %s\n", vtep_str);
+ vty_out(vty, "\n");
+ }
+}
+
+static void bgp_evpn_es_evi_show_one_vni(struct bgpevpn *vpn, struct vty *vty,
+ json_object *json_array, bool detail)
+{
+ struct bgp_evpn_es_evi *es_evi;
+ json_object *json = NULL;
+
+ RB_FOREACH(es_evi, bgp_es_evi_rb_head, &vpn->es_evi_rb_tree) {
+ if (json_array)
+ /* create a separate json object for each ES */
+ json = json_object_new_object();
+ if (detail)
+ bgp_evpn_es_evi_show_entry_detail(vty, es_evi, json);
+ else
+ bgp_evpn_es_evi_show_entry(vty, es_evi, json);
+ /* add ES to the json array */
+ if (json_array)
+ json_object_array_add(json_array, json);
+ }
+}
+
+struct es_evi_show_ctx {
+ struct vty *vty;
+ json_object *json;
+ int detail;
+};
+
+static void bgp_evpn_es_evi_show_one_vni_hash_cb(struct hash_bucket *bucket,
+ void *ctxt)
+{
+ struct bgpevpn *vpn = (struct bgpevpn *)bucket->data;
+ struct es_evi_show_ctx *wctx = (struct es_evi_show_ctx *)ctxt;
+
+ bgp_evpn_es_evi_show_one_vni(vpn, wctx->vty, wctx->json, wctx->detail);
+}
+
+/* Display all ES EVIs */
+void bgp_evpn_es_evi_show(struct vty *vty, bool uj, bool detail)
+{
+ json_object *json_array = NULL;
+ struct es_evi_show_ctx wctx;
+ struct bgp *bgp;
+
+ if (uj) {
+ /* create an array of ES-EVIs */
+ json_array = json_object_new_array();
+ }
+
+ wctx.vty = vty;
+ wctx.json = json_array;
+ wctx.detail = detail;
+
+ bgp = bgp_get_evpn();
+
+ if (!json_array && !detail) {
+ vty_out(vty, "Flags: L local, R remote, I inconsistent\n");
+ vty_out(vty, "VTEP-Flags: E EAD-per-ES, V EAD-per-EVI\n");
+ vty_out(vty, "%-8s %-30s %-5s %s\n",
+ "VNI", "ESI", "Flags", "VTEPs");
+ }
+
+ if (bgp)
+ hash_iterate(bgp->vnihash,
+ (void (*)(struct hash_bucket *,
+ void *))bgp_evpn_es_evi_show_one_vni_hash_cb,
+ &wctx);
+ if (uj)
+ vty_json(vty, json_array);
+}
+
+/* Display specific ES EVI */
+void bgp_evpn_es_evi_show_vni(struct vty *vty, vni_t vni,
+ bool uj, bool detail)
+{
+ struct bgpevpn *vpn = NULL;
+ json_object *json_array = NULL;
+ struct bgp *bgp;
+
+ if (uj) {
+ /* create an array of ES-EVIs */
+ json_array = json_object_new_array();
+ }
+
+ bgp = bgp_get_evpn();
+ if (bgp)
+ vpn = bgp_evpn_lookup_vni(bgp, vni);
+
+ if (vpn) {
+ if (!json_array && !detail) {
+ vty_out(vty, "Flags: L local, R remote, I inconsistent\n");
+ vty_out(vty, "VTEP-Flags: E EAD-per-ES, V EAD-per-EVI\n");
+ vty_out(vty, "%-8s %-30s %-5s %s\n",
+ "VNI", "ESI", "Flags", "VTEPs");
+ }
+
+ bgp_evpn_es_evi_show_one_vni(vpn, vty, json_array, detail);
+ } else {
+ if (!uj)
+ vty_out(vty, "VNI not found\n");
+ }
+
+ if (uj)
+ vty_json(vty, json_array);
+}
+
+/*****************************************************************************
+ * Ethernet Segment Consistency checks
+ * Consistency checking is done to detect misconfig or mis-cabling. When
+ * an inconsistency is detected it is simply logged (and displayed via
+ * show commands) at this point. A more drastic action can be executed (based
+ * on user config) in the future.
+ */
+static void bgp_evpn_es_cons_checks_timer_start(void)
+{
+ if (!bgp_mh_info->consistency_checking || bgp_mh_info->t_cons_check)
+ return;
+
+ if (BGP_DEBUG(evpn_mh, EVPN_MH_ES))
+ zlog_debug("periodic consistency checking started");
+
+ thread_add_timer(bm->master, bgp_evpn_run_consistency_checks, NULL,
+ BGP_EVPN_CONS_CHECK_INTERVAL,
+ &bgp_mh_info->t_cons_check);
+}
+
+/* queue up the es for background consistency checks */
+static void bgp_evpn_es_cons_checks_pend_add(struct bgp_evpn_es *es)
+{
+ if (!bgp_mh_info->consistency_checking)
+ /* consistency checking is not enabled */
+ return;
+
+ if (CHECK_FLAG(es->flags, BGP_EVPNES_CONS_CHECK_PEND))
+ /* already queued for consistency checking */
+ return;
+
+ /* start the periodic timer for consistency checks if it is not
+ * already running */
+ bgp_evpn_es_cons_checks_timer_start();
+
+ SET_FLAG(es->flags, BGP_EVPNES_CONS_CHECK_PEND);
+ listnode_init(&es->pend_es_listnode, es);
+ listnode_add_after(bgp_mh_info->pend_es_list,
+ listtail_unchecked(bgp_mh_info->pend_es_list),
+ &es->pend_es_listnode);
+}
+
+/* pull the ES from the consistency check list */
+static void bgp_evpn_es_cons_checks_pend_del(struct bgp_evpn_es *es)
+{
+ if (!CHECK_FLAG(es->flags, BGP_EVPNES_CONS_CHECK_PEND))
+ return;
+
+ UNSET_FLAG(es->flags, BGP_EVPNES_CONS_CHECK_PEND);
+ list_delete_node(bgp_mh_info->pend_es_list,
+ &es->pend_es_listnode);
+}
+
+/* Number of active VTEPs associated with the ES-per-EVI */
+static uint32_t bgp_evpn_es_evi_get_active_vtep_cnt(
+ struct bgp_evpn_es_evi *es_evi)
+{
+ struct bgp_evpn_es_evi_vtep *evi_vtep;
+ struct listnode *node;
+ uint32_t vtep_cnt = 0;
+
+ for (ALL_LIST_ELEMENTS_RO(es_evi->es_evi_vtep_list, node, evi_vtep)) {
+ if (CHECK_FLAG(evi_vtep->flags, BGP_EVPN_EVI_VTEP_ACTIVE))
+ ++vtep_cnt;
+ }
+
+ return vtep_cnt;
+}
+
+/* Number of active VTEPs associated with the ES */
+static uint32_t bgp_evpn_es_get_active_vtep_cnt(struct bgp_evpn_es *es)
+{
+ struct listnode *node;
+ uint32_t vtep_cnt = 0;
+ struct bgp_evpn_es_vtep *es_vtep;
+
+ for (ALL_LIST_ELEMENTS_RO(es->es_vtep_list, node, es_vtep)) {
+ if (CHECK_FLAG(es_vtep->flags, BGP_EVPNES_VTEP_ACTIVE))
+ ++vtep_cnt;
+ }
+
+ return vtep_cnt;
+}
+
+static struct bgp_evpn_es_vtep *bgp_evpn_es_get_next_active_vtep(
+ struct bgp_evpn_es *es, struct bgp_evpn_es_vtep *es_vtep)
+{
+ struct listnode *node;
+ struct bgp_evpn_es_vtep *next_es_vtep;
+
+ if (es_vtep)
+ node = listnextnode_unchecked(&es_vtep->es_listnode);
+ else
+ node = listhead(es->es_vtep_list);
+
+ for (; node; node = listnextnode_unchecked(node)) {
+ next_es_vtep = listgetdata(node);
+ if (CHECK_FLAG(next_es_vtep->flags, BGP_EVPNES_VTEP_ACTIVE))
+ return next_es_vtep;
+ }
+
+ return NULL;
+}
+
+static struct bgp_evpn_es_evi_vtep *bgp_evpn_es_evi_get_next_active_vtep(
+ struct bgp_evpn_es_evi *es_evi,
+ struct bgp_evpn_es_evi_vtep *evi_vtep)
+{
+ struct listnode *node;
+ struct bgp_evpn_es_evi_vtep *next_evi_vtep;
+
+ if (evi_vtep)
+ node = listnextnode_unchecked(&evi_vtep->es_evi_listnode);
+ else
+ node = listhead(es_evi->es_evi_vtep_list);
+
+ for (; node; node = listnextnode_unchecked(node)) {
+ next_evi_vtep = listgetdata(node);
+ if (CHECK_FLAG(next_evi_vtep->flags, BGP_EVPN_EVI_VTEP_ACTIVE))
+ return next_evi_vtep;
+ }
+
+ return NULL;
+}
+
+static void bgp_evpn_es_evi_set_inconsistent(struct bgp_evpn_es_evi *es_evi)
+{
+ if (!CHECK_FLAG(es_evi->flags, BGP_EVPNES_EVI_INCONS_VTEP_LIST)) {
+ if (BGP_DEBUG(evpn_mh, EVPN_MH_ES))
+ zlog_debug("inconsistency detected - es %s evi %u vtep list mismatch",
+ es_evi->es->esi_str,
+ es_evi->vpn->vni);
+ SET_FLAG(es_evi->flags, BGP_EVPNES_EVI_INCONS_VTEP_LIST);
+
+ /* update parent ES with the incosistency setting */
+ if (!es_evi->es->incons_evi_vtep_cnt &&
+ BGP_DEBUG(evpn_mh, EVPN_MH_ES))
+ zlog_debug("inconsistency detected - es %s vtep list mismatch",
+ es_evi->es->esi_str);
+ ++es_evi->es->incons_evi_vtep_cnt;
+ SET_FLAG(es_evi->es->inconsistencies,
+ BGP_EVPNES_INCONS_VTEP_LIST);
+ }
+}
+
+static uint32_t bgp_evpn_es_run_consistency_checks(struct bgp_evpn_es *es)
+{
+ int proc_cnt = 0;
+ int es_active_vtep_cnt;
+ int evi_active_vtep_cnt;
+ struct bgp_evpn_es_evi *es_evi;
+ struct listnode *evi_node;
+ struct bgp_evpn_es_vtep *es_vtep;
+ struct bgp_evpn_es_evi_vtep *evi_vtep;
+
+ /* reset the inconsistencies and re-evaluate */
+ es->incons_evi_vtep_cnt = 0;
+ es->inconsistencies = 0;
+
+ es_active_vtep_cnt = bgp_evpn_es_get_active_vtep_cnt(es);
+ for (ALL_LIST_ELEMENTS_RO(es->es_evi_list,
+ evi_node, es_evi)) {
+ ++proc_cnt;
+
+ /* reset the inconsistencies on the EVI and re-evaluate*/
+ UNSET_FLAG(es_evi->flags, BGP_EVPNES_EVI_INCONS_VTEP_LIST);
+
+ evi_active_vtep_cnt =
+ bgp_evpn_es_evi_get_active_vtep_cnt(es_evi);
+ if (es_active_vtep_cnt != evi_active_vtep_cnt) {
+ bgp_evpn_es_evi_set_inconsistent(es_evi);
+ continue;
+ }
+
+ if (!es_active_vtep_cnt)
+ continue;
+
+ es_vtep = NULL;
+ evi_vtep = NULL;
+ while ((es_vtep = bgp_evpn_es_get_next_active_vtep(
+ es, es_vtep))) {
+ evi_vtep = bgp_evpn_es_evi_get_next_active_vtep(es_evi,
+ evi_vtep);
+ if (!evi_vtep) {
+ bgp_evpn_es_evi_set_inconsistent(es_evi);
+ break;
+ }
+ if (es_vtep->vtep_ip.s_addr !=
+ evi_vtep->vtep_ip.s_addr) {
+ /* inconsistency detected; set it and move
+ * to the next evi
+ */
+ bgp_evpn_es_evi_set_inconsistent(es_evi);
+ break;
+ }
+ }
+ }
+
+ return proc_cnt;
+}
+
+static void bgp_evpn_run_consistency_checks(struct thread *t)
+{
+ int proc_cnt = 0;
+ struct listnode *node;
+ struct listnode *nextnode;
+ struct bgp_evpn_es *es;
+
+ for (ALL_LIST_ELEMENTS(bgp_mh_info->pend_es_list,
+ node, nextnode, es)) {
+ ++proc_cnt;
+ /* run consistency checks on the ES and remove it from the
+ * pending list
+ */
+ proc_cnt += bgp_evpn_es_run_consistency_checks(es);
+ bgp_evpn_es_cons_checks_pend_del(es);
+ if (proc_cnt > 500)
+ break;
+ }
+
+ /* restart the timer */
+ thread_add_timer(bm->master, bgp_evpn_run_consistency_checks, NULL,
+ BGP_EVPN_CONS_CHECK_INTERVAL,
+ &bgp_mh_info->t_cons_check);
+}
+
+/*****************************************************************************
+ * EVPN-Nexthop and RMAC management: nexthops associated with Type-2 routes
+ * that have an ES as destination are consolidated by BGP into a per-VRF
+ * nh->rmac mapping which is sent to zebra. Zebra installs the nexthop
+ * as a remote neigh/fdb entry with a dummy (type-1) prefix referencing it.
+ *
+ * This handling is needed because Type-2 routes with ES as dest use NHG
+ * that is setup using EAD routes (i.e. such NHGs do not include the
+ * RMAC info).
+ ****************************************************************************/
+static void bgp_evpn_nh_zebra_update_send(struct bgp_evpn_nh *nh, bool add)
+{
+ struct stream *s;
+ struct bgp *bgp_vrf = nh->bgp_vrf;
+
+ /* Check socket. */
+ if (!zclient || zclient->sock < 0)
+ return;
+
+ /* Don't try to register if Zebra doesn't know of this instance. */
+ if (!IS_BGP_INST_KNOWN_TO_ZEBRA(bgp_vrf)) {
+ if (BGP_DEBUG(zebra, ZEBRA))
+ zlog_debug("No zebra instance, not %s remote nh %s",
+ add ? "adding" : "deleting", nh->nh_str);
+ return;
+ }
+
+ s = zclient->obuf;
+ stream_reset(s);
+
+ zclient_create_header(
+ s, add ? ZEBRA_EVPN_REMOTE_NH_ADD : ZEBRA_EVPN_REMOTE_NH_DEL,
+ bgp_vrf->vrf_id);
+ stream_putl(s, bgp_vrf->vrf_id);
+ stream_put(s, &nh->ip, sizeof(nh->ip));
+ if (add)
+ stream_put(s, &nh->rmac, sizeof(nh->rmac));
+
+ stream_putw_at(s, 0, stream_get_endp(s));
+
+ if (BGP_DEBUG(evpn_mh, EVPN_MH_ES)) {
+ if (add)
+ zlog_debug("evpn vrf %s nh %s rmac %pEA add to zebra",
+ nh->bgp_vrf->name, nh->nh_str, &nh->rmac);
+ else if (BGP_DEBUG(evpn_mh, EVPN_MH_ES))
+ zlog_debug("evpn vrf %s nh %s del to zebra",
+ nh->bgp_vrf->name, nh->nh_str);
+ }
+
+ frrtrace(2, frr_bgp, evpn_mh_nh_rmac_zsend, add, nh);
+
+ zclient_send_message(zclient);
+}
+
+static void bgp_evpn_nh_zebra_update(struct bgp_evpn_nh *nh, bool add)
+{
+ if (add && !is_zero_mac(&nh->rmac)) {
+ nh->flags |= BGP_EVPN_NH_READY_FOR_ZEBRA;
+ bgp_evpn_nh_zebra_update_send(nh, true);
+ } else {
+ if (!(nh->flags & BGP_EVPN_NH_READY_FOR_ZEBRA))
+ return;
+ nh->flags &= ~BGP_EVPN_NH_READY_FOR_ZEBRA;
+ bgp_evpn_nh_zebra_update_send(nh, false);
+ }
+}
+
+static void *bgp_evpn_nh_alloc(void *p)
+{
+ struct bgp_evpn_nh *tmp_n = p;
+ struct bgp_evpn_nh *n;
+
+ n = XCALLOC(MTYPE_BGP_EVPN_NH, sizeof(struct bgp_evpn_nh));
+ *n = *tmp_n;
+
+ return ((void *)n);
+}
+
+static struct bgp_evpn_nh *bgp_evpn_nh_find(struct bgp *bgp_vrf,
+ struct ipaddr *ip)
+{
+ struct bgp_evpn_nh tmp;
+ struct bgp_evpn_nh *n;
+
+ memset(&tmp, 0, sizeof(tmp));
+ memcpy(&tmp.ip, ip, sizeof(struct ipaddr));
+ n = hash_lookup(bgp_vrf->evpn_nh_table, &tmp);
+
+ return n;
+}
+
+/* Add nexthop entry - implicitly created on first path reference */
+static struct bgp_evpn_nh *bgp_evpn_nh_add(struct bgp *bgp_vrf,
+ struct ipaddr *ip,
+ struct bgp_path_info *pi)
+{
+ struct bgp_evpn_nh tmp_n;
+ struct bgp_evpn_nh *n = NULL;
+
+ memset(&tmp_n, 0, sizeof(tmp_n));
+ memcpy(&tmp_n.ip, ip, sizeof(struct ipaddr));
+ n = hash_get(bgp_vrf->evpn_nh_table, &tmp_n, bgp_evpn_nh_alloc);
+ ipaddr2str(ip, n->nh_str, sizeof(n->nh_str));
+ n->bgp_vrf = bgp_vrf;
+
+ n->pi_list = list_new();
+ listset_app_node_mem(n->pi_list);
+
+ /* Setup ref_pi when the nh is created */
+ if (CHECK_FLAG(pi->flags, BGP_PATH_VALID) && pi->attr) {
+ n->ref_pi = pi;
+ memcpy(&n->rmac, &pi->attr->rmac, ETH_ALEN);
+ }
+
+ if (BGP_DEBUG(evpn_mh, EVPN_MH_ES))
+ zlog_debug("evpn vrf %s nh %s rmac %pEA add", n->bgp_vrf->name,
+ n->nh_str, &n->rmac);
+ bgp_evpn_nh_zebra_update(n, true);
+ return n;
+}
+
+/* Delete nexthop entry if there are no paths referencing it */
+static void bgp_evpn_nh_del(struct bgp_evpn_nh *n)
+{
+ struct bgp_evpn_nh *tmp_n;
+ struct bgp *bgp_vrf = n->bgp_vrf;
+
+ if (listcount(n->pi_list))
+ return;
+
+ if (BGP_DEBUG(evpn_mh, EVPN_MH_ES))
+ zlog_debug("evpn vrf %s nh %s del to zebra", bgp_vrf->name,
+ n->nh_str);
+
+ bgp_evpn_nh_zebra_update(n, false);
+ list_delete(&n->pi_list);
+ tmp_n = hash_release(bgp_vrf->evpn_nh_table, n);
+ XFREE(MTYPE_BGP_EVPN_NH, tmp_n);
+}
+
+static void hash_evpn_nh_free(struct bgp_evpn_nh *ben)
+{
+ XFREE(MTYPE_BGP_EVPN_NH, ben);
+}
+
+static unsigned int bgp_evpn_nh_hash_keymake(const void *p)
+{
+ const struct bgp_evpn_nh *n = p;
+ const struct ipaddr *ip = &n->ip;
+
+ if (IS_IPADDR_V4(ip))
+ return jhash_1word(ip->ipaddr_v4.s_addr, 0);
+
+ return jhash2(ip->ipaddr_v6.s6_addr32,
+ array_size(ip->ipaddr_v6.s6_addr32), 0);
+}
+
+static bool bgp_evpn_nh_cmp(const void *p1, const void *p2)
+{
+ const struct bgp_evpn_nh *n1 = p1;
+ const struct bgp_evpn_nh *n2 = p2;
+
+ if (n1 == NULL && n2 == NULL)
+ return true;
+
+ if (n1 == NULL || n2 == NULL)
+ return false;
+
+ return (ipaddr_cmp(&n1->ip, &n2->ip) == 0);
+}
+
+void bgp_evpn_nh_init(struct bgp *bgp_vrf)
+{
+ if (BGP_DEBUG(evpn_mh, EVPN_MH_ES))
+ zlog_debug("evpn vrf %s nh init", bgp_vrf->name);
+ bgp_vrf->evpn_nh_table = hash_create(
+ bgp_evpn_nh_hash_keymake, bgp_evpn_nh_cmp, "BGP EVPN NH table");
+}
+
+static void bgp_evpn_nh_flush_entry(struct bgp_evpn_nh *nh)
+{
+ struct listnode *node;
+ struct listnode *nnode;
+ struct bgp_path_evpn_nh_info *nh_info;
+
+ if (BGP_DEBUG(evpn_mh, EVPN_MH_ES))
+ zlog_debug("evpn vrf %s nh %s flush", nh->bgp_vrf->name,
+ nh->nh_str);
+
+ /* force flush paths */
+ for (ALL_LIST_ELEMENTS(nh->pi_list, node, nnode, nh_info))
+ bgp_evpn_path_nh_del(nh->bgp_vrf, nh_info->pi);
+}
+
+static void bgp_evpn_nh_flush_cb(struct hash_bucket *bucket, void *ctxt)
+{
+ struct bgp_evpn_nh *nh = (struct bgp_evpn_nh *)bucket->data;
+
+ bgp_evpn_nh_flush_entry(nh);
+}
+
+void bgp_evpn_nh_finish(struct bgp *bgp_vrf)
+{
+ if (BGP_DEBUG(evpn_mh, EVPN_MH_ES))
+ zlog_debug("evpn vrf %s nh finish", bgp_vrf->name);
+ hash_iterate(
+ bgp_vrf->evpn_nh_table,
+ (void (*)(struct hash_bucket *, void *))bgp_evpn_nh_flush_cb,
+ NULL);
+ hash_clean(bgp_vrf->evpn_nh_table, (void (*)(void *))hash_evpn_nh_free);
+ hash_free(bgp_vrf->evpn_nh_table);
+ bgp_vrf->evpn_nh_table = NULL;
+}
+
+static void bgp_evpn_nh_update_ref_pi(struct bgp_evpn_nh *nh)
+{
+ struct listnode *node;
+ struct bgp_path_info *pi;
+ struct bgp_path_evpn_nh_info *nh_info;
+
+ if (nh->ref_pi)
+ return;
+
+ for (ALL_LIST_ELEMENTS_RO(nh->pi_list, node, nh_info)) {
+ pi = nh_info->pi;
+ if (!CHECK_FLAG(pi->flags, BGP_PATH_VALID) || !pi->attr)
+ continue;
+
+ if (BGP_DEBUG(evpn_mh, EVPN_MH_ES))
+ zlog_debug("evpn vrf %s nh %s ref_pi update",
+ nh->bgp_vrf->name, nh->nh_str);
+ nh->ref_pi = pi;
+ /* If we have a new pi copy rmac from it and update
+ * zebra if the new rmac is different
+ */
+ if (memcmp(&nh->rmac, &nh->ref_pi->attr->rmac, ETH_ALEN)) {
+ memcpy(&nh->rmac, &nh->ref_pi->attr->rmac, ETH_ALEN);
+ bgp_evpn_nh_zebra_update(nh, true);
+ }
+ break;
+ }
+}
+
+static void bgp_evpn_nh_clear_ref_pi(struct bgp_evpn_nh *nh,
+ struct bgp_path_info *pi)
+{
+ if (nh->ref_pi != pi)
+ return;
+
+ if (BGP_DEBUG(evpn_mh, EVPN_MH_ES))
+ zlog_debug("evpn vrf %s nh %s ref_pi clear", nh->bgp_vrf->name,
+ nh->nh_str);
+ nh->ref_pi = NULL;
+ /* try to find another ref_pi */
+ bgp_evpn_nh_update_ref_pi(nh);
+ /* couldn't find one - clear the old rmac and notify zebra */
+ if (!nh->ref_pi) {
+ memset(&nh->rmac, 0, ETH_ALEN);
+ bgp_evpn_nh_zebra_update(nh, true);
+ }
+}
+
+static void bgp_evpn_path_nh_info_free(struct bgp_path_evpn_nh_info *nh_info)
+{
+ bgp_evpn_path_nh_unlink(nh_info);
+ XFREE(MTYPE_BGP_EVPN_PATH_NH_INFO, nh_info);
+}
+
+static struct bgp_path_evpn_nh_info *
+bgp_evpn_path_nh_info_new(struct bgp_path_info *pi)
+{
+ struct bgp_path_info_extra *e;
+ struct bgp_path_mh_info *mh_info;
+ struct bgp_path_evpn_nh_info *nh_info;
+
+ e = bgp_path_info_extra_get(pi);
+
+ /* If mh_info doesn't exist allocate it */
+ mh_info = e->mh_info;
+ if (!mh_info)
+ e->mh_info = mh_info = XCALLOC(MTYPE_BGP_EVPN_PATH_MH_INFO,
+ sizeof(struct bgp_path_mh_info));
+
+ /* If nh_info doesn't exist allocate it */
+ nh_info = mh_info->nh_info;
+ if (!nh_info) {
+ mh_info->nh_info = nh_info =
+ XCALLOC(MTYPE_BGP_EVPN_PATH_NH_INFO,
+ sizeof(struct bgp_path_evpn_nh_info));
+ nh_info->pi = pi;
+ }
+
+ return nh_info;
+}
+
+static void bgp_evpn_path_nh_unlink(struct bgp_path_evpn_nh_info *nh_info)
+{
+ struct bgp_evpn_nh *nh = nh_info->nh;
+ struct bgp_path_info *pi;
+ char prefix_buf[PREFIX_STRLEN];
+
+ if (!nh)
+ return;
+
+ pi = nh_info->pi;
+ if (BGP_DEBUG(evpn_mh, EVPN_MH_RT))
+ zlog_debug("path %s unlinked from nh %s %s",
+ pi->net ? prefix2str(&pi->net->p, prefix_buf,
+ sizeof(prefix_buf))
+ : "",
+ nh->bgp_vrf->name, nh->nh_str);
+
+ list_delete_node(nh->pi_list, &nh_info->nh_listnode);
+
+ nh_info->nh = NULL;
+
+ /* check if the ref_pi need to be updated */
+ bgp_evpn_nh_clear_ref_pi(nh, pi);
+
+ /* if there are no other references against the nh it
+ * needs to be freed
+ */
+ bgp_evpn_nh_del(nh);
+
+ /* Note we don't free the path nh_info on unlink; it will be freed up
+ * along with the path.
+ */
+}
+
+static void bgp_evpn_path_nh_link(struct bgp *bgp_vrf, struct bgp_path_info *pi)
+{
+ struct bgp_path_evpn_nh_info *nh_info;
+ struct bgp_evpn_nh *nh;
+ struct ipaddr ip;
+
+ /* EVPN nexthop setup in bgp has been turned off */
+ if (!bgp_mh_info->bgp_evpn_nh_setup)
+ return;
+
+ if (!bgp_vrf->evpn_nh_table) {
+ if (BGP_DEBUG(evpn_mh, EVPN_MH_RT))
+ zlog_debug("path %pFX linked to vrf %s failed",
+ &pi->net->p, bgp_vrf->name);
+ return;
+ }
+
+ nh_info = (pi->extra && pi->extra->mh_info)
+ ? pi->extra->mh_info->nh_info
+ : NULL;
+
+ /* if NHG is not being used for this path we don't need to manage the
+ * nexthops in bgp (they are managed by zebra instead)
+ */
+ if (!(pi->attr->es_flags & ATTR_ES_L3_NHG_USE)) {
+ if (nh_info)
+ bgp_evpn_path_nh_unlink(nh_info);
+ return;
+ }
+
+ /* setup nh_info against the path if it doesn't aleady exist */
+ if (!nh_info)
+ nh_info = bgp_evpn_path_nh_info_new(pi);
+
+ /* find-create nh */
+ memset(&ip, 0, sizeof(ip));
+ if (pi->net->p.family == AF_INET6) {
+ SET_IPADDR_V6(&ip);
+ memcpy(&ip.ipaddr_v6, &pi->attr->mp_nexthop_global,
+ sizeof(ip.ipaddr_v6));
+ } else {
+ SET_IPADDR_V4(&ip);
+ memcpy(&ip.ipaddr_v4, &pi->attr->nexthop, sizeof(ip.ipaddr_v4));
+ }
+
+ nh = bgp_evpn_nh_find(bgp_vrf, &ip);
+ if (!nh)
+ nh = bgp_evpn_nh_add(bgp_vrf, &ip, pi);
+
+ /* dup check */
+ if (nh_info->nh == nh) {
+ /* Check if any of the paths are now valid */
+ bgp_evpn_nh_update_ref_pi(nh);
+ return;
+ }
+
+ /* unlink old nh if any */
+ bgp_evpn_path_nh_unlink(nh_info);
+
+ if (BGP_DEBUG(evpn_mh, EVPN_MH_RT))
+ zlog_debug("path %pFX linked to nh %s %s", &pi->net->p,
+ nh->bgp_vrf->name, nh->nh_str);
+
+ /* link mac-ip path to the new nh */
+ nh_info->nh = nh;
+ listnode_init(&nh_info->nh_listnode, nh_info);
+ listnode_add(nh->pi_list, &nh_info->nh_listnode);
+ /* If a new valid path got linked to the nh see if can get the rmac
+ * from it
+ */
+ bgp_evpn_nh_update_ref_pi(nh);
+ if (BGP_DEBUG(evpn_mh, EVPN_MH_ES)) {
+ if (!nh->ref_pi)
+ zlog_debug(
+ "path %pFX linked to nh %s %s with no valid pi",
+ &pi->net->p, nh->bgp_vrf->name, nh->nh_str);
+ }
+}
+
+void bgp_evpn_path_nh_del(struct bgp *bgp_vrf, struct bgp_path_info *pi)
+{
+ struct bgp_path_evpn_nh_info *nh_info;
+
+ nh_info = (pi->extra && pi->extra->mh_info)
+ ? pi->extra->mh_info->nh_info
+ : NULL;
+
+ if (!nh_info)
+ return;
+
+ bgp_evpn_path_nh_unlink(nh_info);
+}
+
+void bgp_evpn_path_nh_add(struct bgp *bgp_vrf, struct bgp_path_info *pi)
+{
+ bgp_evpn_path_nh_link(bgp_vrf, pi);
+}
+
+static void bgp_evpn_nh_show_entry(struct bgp_evpn_nh *nh, struct vty *vty,
+ json_object *json_array)
+{
+ json_object *json = NULL;
+ char mac_buf[ETHER_ADDR_STRLEN];
+ char prefix_buf[PREFIX_STRLEN];
+
+ if (json_array)
+ /* create a separate json object for each ES */
+ json = json_object_new_object();
+
+ prefix_mac2str(&nh->rmac, mac_buf, sizeof(mac_buf));
+ if (nh->ref_pi && nh->ref_pi->net)
+ prefix2str(&nh->ref_pi->net->p, prefix_buf, sizeof(prefix_buf));
+ else
+ prefix_buf[0] = '\0';
+ if (json) {
+ json_object_string_add(json, "vrf", nh->bgp_vrf->name);
+ json_object_string_add(json, "ip", nh->nh_str);
+ json_object_string_add(json, "rmac", mac_buf);
+ json_object_string_add(json, "basePath", prefix_buf);
+ json_object_int_add(json, "pathCount", listcount(nh->pi_list));
+ } else {
+ vty_out(vty, "%-15s %-15s %-17s %-10d %s\n", nh->bgp_vrf->name,
+ nh->nh_str, mac_buf, listcount(nh->pi_list),
+ prefix_buf);
+ }
+
+ /* add ES to the json array */
+ if (json_array)
+ json_object_array_add(json_array, json);
+}
+
+struct nh_show_ctx {
+ struct vty *vty;
+ json_object *json;
+};
+
+static void bgp_evpn_nh_show_hash_cb(struct hash_bucket *bucket, void *ctxt)
+{
+ struct bgp_evpn_nh *nh = (struct bgp_evpn_nh *)bucket->data;
+ struct nh_show_ctx *wctx = (struct nh_show_ctx *)ctxt;
+
+ bgp_evpn_nh_show_entry(nh, wctx->vty, wctx->json);
+}
+
+/* Display all evpn nexthops */
+void bgp_evpn_nh_show(struct vty *vty, bool uj)
+{
+ json_object *json_array = NULL;
+ struct bgp *bgp_vrf;
+ struct listnode *node;
+ struct nh_show_ctx wctx;
+
+ if (uj) {
+ /* create an array of nexthops */
+ json_array = json_object_new_array();
+ } else {
+ vty_out(vty, "%-15s %-15s %-17s %-10s %s\n", "VRF", "IP",
+ "RMAC", "#Paths", "Base Path");
+ }
+
+ wctx.vty = vty;
+ wctx.json = json_array;
+
+ /* walk through all vrfs */
+ for (ALL_LIST_ELEMENTS_RO(bm->bgp, node, bgp_vrf)) {
+ hash_iterate(bgp_vrf->evpn_nh_table,
+ (void (*)(struct hash_bucket *,
+ void *))bgp_evpn_nh_show_hash_cb,
+ &wctx);
+ }
+
+ /* print the array of json-ESs */
+ if (uj)
+ vty_json(vty, json_array);
+}
+
+/*****************************************************************************/
+void bgp_evpn_mh_init(void)
+{
+ bm->mh_info = XCALLOC(MTYPE_BGP_EVPN_MH_INFO, sizeof(*bm->mh_info));
+
+ /* setup ES tables */
+ RB_INIT(bgp_es_rb_head, &bgp_mh_info->es_rb_tree);
+ /* local ES list */
+ bgp_mh_info->local_es_list = list_new();
+ listset_app_node_mem(bgp_mh_info->local_es_list);
+ /* list of ESs with pending processing */
+ bgp_mh_info->pend_es_list = list_new();
+ listset_app_node_mem(bgp_mh_info->pend_es_list);
+
+ bgp_mh_info->ead_evi_rx = BGP_EVPN_MH_EAD_EVI_RX_DEF;
+ bgp_mh_info->ead_evi_tx = BGP_EVPN_MH_EAD_EVI_TX_DEF;
+ bgp_mh_info->ead_es_export_rtl = list_new();
+ bgp_mh_info->ead_es_export_rtl->cmp =
+ (int (*)(void *, void *))bgp_evpn_route_target_cmp;
+ bgp_mh_info->ead_es_export_rtl->del = bgp_evpn_xxport_delete_ecomm;
+
+ /* config knobs - XXX add cli to control it */
+ bgp_mh_info->ead_evi_adv_for_down_links = true;
+ bgp_mh_info->consistency_checking = true;
+ bgp_mh_info->host_routes_use_l3nhg = BGP_EVPN_MH_USE_ES_L3NHG_DEF;
+ bgp_mh_info->suppress_l3_ecomm_on_inactive_es = true;
+ bgp_mh_info->bgp_evpn_nh_setup = true;
+ bgp_mh_info->evi_per_es_frag = BGP_EVPN_MAX_EVI_PER_ES_FRAG;
+
+ memset(&zero_esi_buf, 0, sizeof(esi_t));
+}
+
+void bgp_evpn_mh_finish(void)
+{
+ struct bgp_evpn_es *es;
+ struct bgp_evpn_es *es_next;
+
+ if (BGP_DEBUG(evpn_mh, EVPN_MH_RT))
+ zlog_debug("evpn mh finish");
+
+ RB_FOREACH_SAFE (es, bgp_es_rb_head, &bgp_mh_info->es_rb_tree,
+ es_next) {
+ bgp_evpn_es_local_info_clear(es, true);
+ }
+ if (bgp_mh_info->t_cons_check)
+ THREAD_OFF(bgp_mh_info->t_cons_check);
+ list_delete(&bgp_mh_info->local_es_list);
+ list_delete(&bgp_mh_info->pend_es_list);
+ list_delete(&bgp_mh_info->ead_es_export_rtl);
+
+ XFREE(MTYPE_BGP_EVPN_MH_INFO, bgp_mh_info);
+}
+
+/* This function is called when disable-ead-evi-rx knob flaps */
+void bgp_evpn_switch_ead_evi_rx(void)
+{
+ struct bgp *bgp;
+ struct bgp_evpn_es *es;
+ struct bgp_evpn_es_evi *es_evi;
+ struct listnode *evi_node = NULL;
+ struct listnode *evi_next = NULL;
+ struct bgp_evpn_es_evi_vtep *vtep;
+ struct listnode *vtep_node = NULL;
+ struct listnode *vtep_next = NULL;
+
+ bgp = bgp_get_evpn();
+ if (!bgp)
+ return;
+
+ /*
+ * Process all the remote es_evi_vteps and reevaluate if the es_evi_vtep
+ * is active.
+ */
+ RB_FOREACH(es, bgp_es_rb_head, &bgp_mh_info->es_rb_tree) {
+ if (!CHECK_FLAG(es->flags, BGP_EVPNES_REMOTE))
+ continue;
+
+ for (ALL_LIST_ELEMENTS(es->es_evi_list, evi_node, evi_next,
+ es_evi)) {
+ if (!CHECK_FLAG(es_evi->flags, BGP_EVPNES_EVI_REMOTE))
+ continue;
+
+ for (ALL_LIST_ELEMENTS(es_evi->es_evi_vtep_list,
+ vtep_node, vtep_next, vtep))
+ bgp_evpn_es_evi_vtep_re_eval_active(bgp, vtep);
+ }
+ }
+}
diff --git a/bgpd/bgp_evpn_mh.h b/bgpd/bgp_evpn_mh.h
new file mode 100644
index 0000000..11030e3
--- /dev/null
+++ b/bgpd/bgp_evpn_mh.h
@@ -0,0 +1,477 @@
+/* EVPN header for multihoming procedures
+ *
+ * Copyright (C) 2019 Cumulus Networks
+ * Anuradha Karuppiah
+ *
+ * This file is part of FRRouting.
+ *
+ * FRRouting is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License as published by the
+ * Free Software Foundation; either version 2, or (at your option) any
+ * later version.
+ *
+ * FRRouting is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ *
+ */
+
+#ifndef _FRR_BGP_EVPN_MH_H
+#define _FRR_BGP_EVPN_MH_H
+
+#include "vxlan.h"
+#include "bgpd.h"
+#include "bgp_evpn.h"
+#include "bgp_evpn_private.h"
+
+#define BGP_EVPN_AD_ES_ETH_TAG 0xffffffff
+#define BGP_EVPN_AD_EVI_ETH_TAG 0
+
+#define BGP_EVPNES_INCONS_STR_SZ 80
+#define BGP_EVPN_VTEPS_FLAG_STR_SZ (BGP_EVPN_FLAG_STR_SZ * ES_VTEP_MAX_CNT)
+
+#define BGP_EVPN_CONS_CHECK_INTERVAL 60
+
+#define BGP_EVPN_MH_USE_ES_L3NHG_DEF true
+
+/* XXX - tune this */
+#define BGP_EVPN_MAX_EVI_PER_ES_FRAG 128
+
+/* An ES can result in multiple EAD-per-ES route. Each EAD fragment is
+ * associated with an unique RD
+ */
+struct bgp_evpn_es_frag {
+ /* frag is associated with a parent ES */
+ struct bgp_evpn_es *es;
+
+ /* Id for deriving the RD automatically for this ES fragment */
+ uint16_t rd_id;
+ /* RD for this ES fragment */
+ struct prefix_rd prd;
+
+ /* Memory used for linking bgp_evpn_es_frag to
+ * bgp_evpn_es->es_frag_list
+ */
+ struct listnode es_listnode;
+
+ /* List of ES-EVIs associated with this fragment */
+ struct list *es_evi_frag_list;
+};
+
+/* Ethernet Segment entry -
+ * - Local and remote ESs are maintained in a global RB tree,
+ * bgp_mh_info->es_rb_tree using ESI as key
+ * - Local ESs are received from zebra (BGP_EVPNES_LOCAL)
+ * - Remotes ESs are implicitly created (by reference) by a remote ES-EVI
+ * (BGP_EVPNES_REMOTE)
+ * - An ES can be simultaneously LOCAL and REMOTE; infact all LOCAL ESs are
+ * expected to have REMOTE ES peers.
+ */
+struct bgp_evpn_es {
+ /* Ethernet Segment Identifier */
+ esi_t esi;
+ char esi_str[ESI_STR_LEN];
+
+ /* es flags */
+ uint32_t flags;
+ /* created via zebra config */
+#define BGP_EVPNES_LOCAL (1 << 0)
+ /* created implicitly by a remote ES-EVI reference */
+#define BGP_EVPNES_REMOTE (1 << 1)
+ /* local ES link is oper-up */
+#define BGP_EVPNES_OPER_UP (1 << 2)
+ /* enable generation of EAD-EVI routes */
+#define BGP_EVPNES_ADV_EVI (1 << 3)
+ /* consistency checks pending */
+#define BGP_EVPNES_CONS_CHECK_PEND (1 << 4)
+ /* ES is in LACP bypass mode - don't advertise EAD-ES or ESR */
+#define BGP_EVPNES_BYPASS (1 << 5)
+ /* bits needed for printing the flags + null */
+#define BGP_EVPN_FLAG_STR_SZ 7
+
+ /* memory used for adding the es to bgp->es_rb_tree */
+ RB_ENTRY(bgp_evpn_es) rb_node;
+
+ /* [EVPNES_LOCAL] memory used for linking the es to
+ * bgp_mh_info->local_es_list
+ */
+ struct listnode es_listnode;
+
+ /* memory used for linking the es to "processing" pending list
+ * bgp_mh_info->pend_es_list
+ */
+ struct listnode pend_es_listnode;
+
+ /* [EVPNES_LOCAL] List of RDs for this ES (bgp_evpn_es_frag) */
+ struct list *es_frag_list;
+ struct bgp_evpn_es_frag *es_base_frag;
+
+ /* [EVPNES_LOCAL] originator ip address */
+ struct in_addr originator_ip;
+
+ /* [EVPNES_LOCAL] Route table for EVPN routes for this ESI-
+ * - Type-4 local and remote routes
+ * - Type-1 local routes
+ */
+ struct bgp_table *route_table;
+
+ /* list of PEs (bgp_evpn_es_vtep) attached to the ES */
+ struct list *es_vtep_list;
+
+ /* List of ES-EVIs associated with this ES */
+ struct list *es_evi_list;
+
+ /* List of ES-VRFs associated with this ES */
+ struct list *es_vrf_list;
+
+ /* List of MAC-IP VNI paths using this ES as destination -
+ * element is bgp_path_info_extra->es_info
+ * Note: Only local/zebra-added MACIP paths in the VNI
+ * routing table are linked to this list
+ */
+ struct list *macip_evi_path_list;
+
+ /* List of MAC-IP paths in the global routing table using this
+ * ES as destination - data is bgp_path_info_extra->es_info
+ * Note: Only non-local/imported MACIP paths in the global
+ * routing table are linked to this list
+ */
+ struct list *macip_global_path_list;
+
+ /* Number of remote VNIs referencing this ES */
+ uint32_t remote_es_evi_cnt;
+
+ uint32_t inconsistencies;
+ /* there are one or more EVIs whose VTEP list doesn't match
+ * with the ES's VTEP list
+ */
+#define BGP_EVPNES_INCONS_VTEP_LIST (1 << 0)
+
+ /* number of es-evi entries whose VTEP list doesn't match
+ * with the ES's
+ */
+ uint32_t incons_evi_vtep_cnt;
+
+ /* preference config for BUM-DF election. advertised via the ESR. */
+ uint16_t df_pref;
+
+ QOBJ_FIELDS;
+};
+DECLARE_QOBJ_TYPE(bgp_evpn_es);
+RB_HEAD(bgp_es_rb_head, bgp_evpn_es);
+RB_PROTOTYPE(bgp_es_rb_head, bgp_evpn_es, rb_node, bgp_es_rb_cmp);
+
+/* PE attached to an ES */
+struct bgp_evpn_es_vtep {
+ struct bgp_evpn_es *es; /* parent ES */
+ struct in_addr vtep_ip;
+
+ char vtep_str[INET6_ADDRSTRLEN];
+
+ uint32_t flags;
+ /* Rxed a Type4 route from this PE */
+#define BGP_EVPNES_VTEP_ESR (1 << 0)
+ /* Active (rxed EAD-ES and EAD-EVI) and can be included as
+ * a nexthop
+ */
+#define BGP_EVPNES_VTEP_ACTIVE (1 << 1)
+
+ uint32_t evi_cnt; /* es_evis referencing this vtep as an active path */
+
+ /* Algorithm and preference for DF election. Rxed via the ESR */
+ uint8_t df_alg;
+ uint16_t df_pref;
+
+ /* memory used for adding the entry to es->es_vtep_list */
+ struct listnode es_listnode;
+};
+
+/* ES-VRF element needed for managing L3 NHGs. It is implicitly created
+ * when an ES-EVI is associated with a tenant VRF
+ */
+struct bgp_evpn_es_vrf {
+ struct bgp_evpn_es *es;
+ struct bgp *bgp_vrf;
+
+ uint32_t flags;
+/* NHG can only be activated if there are active VTEPs in the ES and
+ * there is a valid L3-VNI associated with the VRF
+ */
+#define BGP_EVPNES_VRF_NHG_ACTIVE (1 << 0)
+
+ /* memory used for adding the es_vrf to
+ * es_vrf->bgp_vrf->es_vrf_rb_tree
+ */
+ RB_ENTRY(bgp_evpn_es_vrf) rb_node;
+
+ /* memory used for linking the es_vrf to es_vrf->es->es_vrf_list */
+ struct listnode es_listnode;
+
+ uint32_t nhg_id;
+ uint32_t v6_nhg_id;
+
+ /* Number of ES-EVI entries associated with this ES-VRF */
+ uint32_t ref_cnt;
+};
+
+/* ES per-EVI info
+ * - ES-EVIs are maintained per-L2-VNI (vpn->es_evi_rb_tree)
+ * - ES-EVIs are also linked to the parent ES (es->es_evi_list)
+ * - Local ES-EVIs are created by zebra (via config). They are linked to a
+ * per-VNI list (vpn->local_es_evi_list) for quick access
+ * - Remote ES-EVIs are created implicitly when a bgp_evpn_es_evi_vtep
+ * references it.
+ */
+struct bgp_evpn_es_evi {
+ struct bgp_evpn_es *es;
+ /* Only applicableif EVI_LOCAL */
+ struct bgp_evpn_es_frag *es_frag;
+ struct bgpevpn *vpn;
+
+ /* ES-EVI flags */
+ uint32_t flags;
+/* local ES-EVI, created by zebra */
+#define BGP_EVPNES_EVI_LOCAL (1 << 0)
+/* created via a remote VTEP imported by BGP */
+#define BGP_EVPNES_EVI_REMOTE (1 << 1)
+#define BGP_EVPNES_EVI_INCONS_VTEP_LIST (1 << 2)
+
+ /* memory used for adding the es_evi to es_evi->vpn->es_evi_rb_tree */
+ RB_ENTRY(bgp_evpn_es_evi) rb_node;
+ /* memory used for linking the es_evi to
+ * es_evi->vpn->local_es_evi_list
+ */
+ struct listnode l2vni_listnode;
+ /* memory used for linking the es_evi to
+ * es_evi->es->es_evi_list
+ */
+ struct listnode es_listnode;
+
+ /* memory used for linking the es_evi to
+ * es_evi->es_frag->es_evi_frag_list
+ */
+ struct listnode es_frag_listnode;
+ /* list of PEs (bgp_evpn_es_evi_vtep) attached to the ES for this VNI */
+ struct list *es_evi_vtep_list;
+
+ struct bgp_evpn_es_vrf *es_vrf;
+};
+
+/* PE attached to an ES for a VNI. This entry is created when an EAD-per-ES
+ * or EAD-per-EVI Type1 route is imported into the VNI.
+ */
+struct bgp_evpn_es_evi_vtep {
+ struct bgp_evpn_es_evi *es_evi; /* parent ES-EVI */
+ struct in_addr vtep_ip;
+
+ uint32_t flags;
+ /* Rxed an EAD-per-ES route from the PE */
+#define BGP_EVPN_EVI_VTEP_EAD_PER_ES (1 << 0) /* rxed EAD-per-ES */
+ /* Rxed an EAD-per-EVI route from the PE */
+#define BGP_EVPN_EVI_VTEP_EAD_PER_EVI (1 << 1) /* rxed EAD-per-EVI */
+ /* VTEP is active i.e. will result in the creation of an es-vtep */
+#define BGP_EVPN_EVI_VTEP_ACTIVE (1 << 2)
+#define BGP_EVPN_EVI_VTEP_EAD (BGP_EVPN_EVI_VTEP_EAD_PER_ES |\
+ BGP_EVPN_EVI_VTEP_EAD_PER_EVI)
+
+ /* memory used for adding the entry to es_evi->es_evi_vtep_list */
+ struct listnode es_evi_listnode;
+ struct bgp_evpn_es_vtep *es_vtep;
+};
+
+/* A nexthop is created when a path (imported from an EVPN type-2 route)
+ * is added to the VRF route table using that nexthop.
+ * It is added on first pi reference and removed on last pi deref.
+ */
+struct bgp_evpn_nh {
+ /* backpointer to the VRF */
+ struct bgp *bgp_vrf;
+ /* nexthop/VTEP IP */
+ struct ipaddr ip;
+ /* description for easy logging */
+ char nh_str[INET6_ADDRSTRLEN];
+ struct ethaddr rmac;
+ /* pi from which we are pulling the nh RMAC */
+ struct bgp_path_info *ref_pi;
+ /* List of VRF paths using this nexthop */
+ struct list *pi_list;
+ uint8_t flags;
+#define BGP_EVPN_NH_READY_FOR_ZEBRA (1 << 0)
+};
+
+/* multihoming information stored in bgp_master */
+#define bgp_mh_info (bm->mh_info)
+struct bgp_evpn_mh_info {
+ /* RB tree of Ethernet segments (used for EVPN-MH) */
+ struct bgp_es_rb_head es_rb_tree;
+ /* List of local ESs */
+ struct list *local_es_list;
+ /* List of ESs with pending/periodic processing */
+ struct list *pend_es_list;
+ /* periodic timer for running background consistency checks */
+ struct thread *t_cons_check;
+
+ /* config knobs for optimizing or interop */
+ /* Generate EAD-EVI routes even if the ES is oper-down. This can be
+ * enabled as an optimization to avoid a storm of updates when an ES
+ * link flaps.
+ */
+ bool ead_evi_adv_for_down_links;
+ /* Enable ES consistency checking */
+ bool consistency_checking;
+ /* Use L3 NHGs for host routes in symmetric IRB */
+ bool host_routes_use_l3nhg;
+ /* Some vendors are not generating the EAD-per-EVI route. This knob
+ * can be turned off to activate a remote ES-PE when the EAD-per-ES
+ * route is rxed i.e. not wait on the EAD-per-EVI route
+ */
+ bool ead_evi_rx;
+#define BGP_EVPN_MH_EAD_EVI_RX_DEF true
+ /* Skip EAD-EVI advertisements by turning off this knob */
+ bool ead_evi_tx;
+#define BGP_EVPN_MH_EAD_EVI_TX_DEF true
+ /* If the Local ES is inactive we advertise the MAC-IP without the
+ * L3 ecomm
+ */
+ bool suppress_l3_ecomm_on_inactive_es;
+ /* Setup EVPN PE nexthops and their RMAC in bgpd */
+ bool bgp_evpn_nh_setup;
+
+ /* If global export-rts are configured that is used for sending
+ * sending the ead-per-es route instead of the L2-VNI(s) RTs
+ */
+ struct list *ead_es_export_rtl;
+
+ /* Number of EVIs in an ES fragment - used of EAD-per-ES route
+ * construction
+ */
+ uint32_t evi_per_es_frag;
+};
+
+/****************************************************************************/
+static inline int bgp_evpn_is_es_local(struct bgp_evpn_es *es)
+{
+ return CHECK_FLAG(es->flags, BGP_EVPNES_LOCAL) ? 1 : 0;
+}
+
+extern esi_t *zero_esi;
+static inline bool bgp_evpn_is_esi_valid(esi_t *esi)
+{
+ return !!memcmp(esi, zero_esi, sizeof(esi_t));
+}
+
+static inline esi_t *bgp_evpn_attr_get_esi(struct attr *attr)
+{
+ return attr ? &attr->esi : zero_esi;
+}
+
+static inline bool bgp_evpn_attr_is_sync(struct attr *attr)
+{
+ return attr ? !!(attr->es_flags &
+ (ATTR_ES_PEER_PROXY | ATTR_ES_PEER_ACTIVE)) : false;
+}
+
+static inline uint32_t bgp_evpn_attr_get_sync_seq(struct attr *attr)
+{
+ return attr ? attr->mm_sync_seqnum : 0;
+}
+
+static inline bool bgp_evpn_attr_is_active_on_peer(struct attr *attr)
+{
+ return attr ?
+ !!(attr->es_flags & ATTR_ES_PEER_ACTIVE) : false;
+}
+
+static inline bool bgp_evpn_attr_is_router_on_peer(struct attr *attr)
+{
+ return attr ?
+ !!(attr->es_flags & ATTR_ES_PEER_ROUTER) : false;
+}
+
+static inline bool bgp_evpn_attr_is_proxy(struct attr *attr)
+{
+ return attr ? !!(attr->es_flags & ATTR_ES_PROXY_ADVERT) : false;
+}
+
+static inline bool bgp_evpn_attr_is_local_es(struct attr *attr)
+{
+ return attr ? !!(attr->es_flags & ATTR_ES_IS_LOCAL) : false;
+}
+
+static inline uint32_t bgp_evpn_attr_get_df_pref(struct attr *attr)
+{
+ return (attr) ? attr->df_pref : 0;
+}
+
+static inline bool bgp_evpn_local_es_is_active(struct bgp_evpn_es *es)
+{
+ return (es->flags & BGP_EVPNES_OPER_UP)
+ && !(es->flags & BGP_EVPNES_BYPASS);
+}
+
+/****************************************************************************/
+extern int bgp_evpn_es_route_install_uninstall(struct bgp *bgp,
+ struct bgp_evpn_es *es, afi_t afi, safi_t safi,
+ struct prefix_evpn *evp, struct bgp_path_info *pi,
+ int install);
+extern void update_type1_routes_for_evi(struct bgp *bgp, struct bgpevpn *vpn);
+extern int delete_global_ead_evi_routes(struct bgp *bgp, struct bgpevpn *vpn);
+extern int bgp_evpn_mh_route_update(struct bgp *bgp, struct bgp_evpn_es *es,
+ struct bgpevpn *vpn, afi_t afi, safi_t safi,
+ struct bgp_dest *dest, struct attr *attr,
+ struct bgp_path_info **ri,
+ int *route_changed);
+int bgp_evpn_type1_route_process(struct peer *peer, afi_t afi, safi_t safi,
+ struct attr *attr, uint8_t *pfx, int psize,
+ uint32_t addpath_id);
+int bgp_evpn_type4_route_process(struct peer *peer, afi_t afi, safi_t safi,
+ struct attr *attr, uint8_t *pfx, int psize,
+ uint32_t addpath_id);
+extern int bgp_evpn_local_es_add(struct bgp *bgp, esi_t *esi,
+ struct in_addr originator_ip, bool oper_up,
+ uint16_t df_pref, bool bypass);
+extern int bgp_evpn_local_es_del(struct bgp *bgp, esi_t *esi);
+extern int bgp_evpn_local_es_evi_add(struct bgp *bgp, esi_t *esi, vni_t vni);
+extern int bgp_evpn_local_es_evi_del(struct bgp *bgp, esi_t *esi, vni_t vni);
+extern int bgp_evpn_remote_es_evi_add(struct bgp *bgp, struct bgpevpn *vpn,
+ const struct prefix_evpn *p);
+extern int bgp_evpn_remote_es_evi_del(struct bgp *bgp, struct bgpevpn *vpn,
+ const struct prefix_evpn *p);
+extern void bgp_evpn_mh_init(void);
+extern void bgp_evpn_mh_finish(void);
+void bgp_evpn_vni_es_init(struct bgpevpn *vpn);
+void bgp_evpn_vni_es_cleanup(struct bgpevpn *vpn);
+void bgp_evpn_es_show_esi(struct vty *vty, esi_t *esi, bool uj);
+void bgp_evpn_es_show(struct vty *vty, bool uj, bool detail);
+void bgp_evpn_es_evi_show_vni(struct vty *vty, vni_t vni,
+ bool uj, bool detail);
+void bgp_evpn_es_evi_show(struct vty *vty, bool uj, bool detail);
+struct bgp_evpn_es *bgp_evpn_es_find(const esi_t *esi);
+extern void bgp_evpn_vrf_es_init(struct bgp *bgp_vrf);
+extern bool bgp_evpn_is_esi_local_and_non_bypass(esi_t *esi);
+extern void bgp_evpn_es_vrf_deref(struct bgp_evpn_es_evi *es_evi);
+extern void bgp_evpn_es_vrf_ref(struct bgp_evpn_es_evi *es_evi,
+ struct bgp *bgp_vrf);
+extern void bgp_evpn_path_mh_info_free(struct bgp_path_mh_info *mh_info);
+extern void bgp_evpn_path_es_link(struct bgp_path_info *pi, vni_t vni,
+ esi_t *esi);
+extern bool bgp_evpn_path_es_use_nhg(struct bgp *bgp_vrf,
+ struct bgp_path_info *pi, uint32_t *nhg_p);
+extern void bgp_evpn_es_vrf_show(struct vty *vty, bool uj,
+ struct bgp_evpn_es *es);
+extern void bgp_evpn_es_vrf_show_esi(struct vty *vty, esi_t *esi, bool uj);
+extern void bgp_evpn_switch_ead_evi_rx(void);
+extern bool bgp_evpn_es_add_l3_ecomm_ok(esi_t *esi);
+extern void bgp_evpn_es_vrf_use_nhg(struct bgp *bgp_vrf, esi_t *esi,
+ bool *use_l3nhg, bool *is_l3nhg_active,
+ struct bgp_evpn_es_vrf **es_vrf_p);
+extern void bgp_evpn_nh_init(struct bgp *bgp_vrf);
+extern void bgp_evpn_nh_finish(struct bgp *bgp_vrf);
+extern void bgp_evpn_nh_show(struct vty *vty, bool uj);
+extern void bgp_evpn_path_nh_add(struct bgp *bgp_vrf, struct bgp_path_info *pi);
+extern void bgp_evpn_path_nh_del(struct bgp *bgp_vrf, struct bgp_path_info *pi);
+extern void bgp_evpn_mh_config_ead_export_rt(struct bgp *bgp,
+ struct ecommunity *ecom, bool del);
+
+#endif /* _FRR_BGP_EVPN_MH_H */
diff --git a/bgpd/bgp_evpn_private.h b/bgpd/bgp_evpn_private.h
new file mode 100644
index 0000000..64fdc29
--- /dev/null
+++ b/bgpd/bgp_evpn_private.h
@@ -0,0 +1,656 @@
+/* BGP EVPN internal definitions
+ * Copyright (C) 2017 Cumulus Networks, Inc.
+ *
+ * This file is part of FRR.
+ *
+ * FRR is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License as published by the
+ * Free Software Foundation; either version 2, or (at your option) any
+ * later version.
+ *
+ * FRR is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with FRR; see the file COPYING. If not, write to the Free
+ * Software Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA
+ * 02111-1307, USA.
+ */
+
+#ifndef _BGP_EVPN_PRIVATE_H
+#define _BGP_EVPN_PRIVATE_H
+
+#include "vxlan.h"
+#include "zebra.h"
+
+#include "bgpd/bgpd.h"
+#include "bgpd/bgp_ecommunity.h"
+
+#define RT_ADDRSTRLEN 28
+
+/* EVPN prefix lengths. This represents the sizeof struct evpn_addr
+ * in bits */
+#define EVPN_ROUTE_PREFIXLEN (sizeof(struct evpn_addr) * 8)
+
+/* EVPN route RD buffer length */
+#define BGP_EVPN_PREFIX_RD_LEN 100
+
+/* packet sizes for EVPN routes */
+/* Type-1 route should be 25 bytes
+ * RD (8), ESI (10), eth-tag (4), vni (3)
+ */
+#define BGP_EVPN_TYPE1_PSIZE 25
+/* Type-4 route should be either 23 or 35 bytes
+ * RD (8), ESI (10), ip-len (1), ip (4 or 16)
+ */
+#define BGP_EVPN_TYPE4_V4_PSIZE 23
+#define BGP_EVPN_TYPE4_V6_PSIZE 34
+
+RB_HEAD(bgp_es_evi_rb_head, bgp_evpn_es_evi);
+RB_PROTOTYPE(bgp_es_evi_rb_head, bgp_evpn_es_evi, rb_node,
+ bgp_es_evi_rb_cmp);
+/*
+ * Hash table of EVIs. Right now, the only type of EVI supported is with
+ * VxLAN encapsulation, hence each EVI corresponds to a L2 VNI.
+ * The VNIs are not "created" through BGP but through some other interface
+ * on the system. This table stores VNIs that BGP comes to know as present
+ * on the system (through interaction with zebra) as well as pre-configured
+ * VNIs (which need to be defined in the system to become "live").
+ */
+struct bgpevpn {
+ vni_t vni;
+ vrf_id_t tenant_vrf_id;
+ ifindex_t svi_ifindex;
+ uint32_t flags;
+#define VNI_FLAG_CFGD 0x1 /* VNI is user configured */
+#define VNI_FLAG_LIVE 0x2 /* VNI is "live" */
+#define VNI_FLAG_RD_CFGD 0x4 /* RD is user configured. */
+#define VNI_FLAG_IMPRT_CFGD 0x8 /* Import RT is user configured */
+#define VNI_FLAG_EXPRT_CFGD 0x10 /* Export RT is user configured */
+#define VNI_FLAG_USE_TWO_LABELS 0x20 /* Attach both L2-VNI and L3-VNI if
+ needed for this VPN */
+
+ struct bgp *bgp_vrf; /* back pointer to the vrf instance */
+
+ /* Flag to indicate if we are
+ * advertising the g/w mac ip for
+ * this VNI*/
+ uint8_t advertise_gw_macip;
+
+ /* Flag to indicate if we are
+ * advertising subnet for this VNI */
+ uint8_t advertise_subnet;
+
+ /* Flag to indicate if we are advertising the svi mac ip for this VNI*/
+ uint8_t advertise_svi_macip;
+
+ /* Id for deriving the RD
+ * automatically for this VNI */
+ uint16_t rd_id;
+
+ /* RD for this VNI. */
+ struct prefix_rd prd;
+
+ /* Route type 3 field */
+ struct in_addr originator_ip;
+
+ /* PIM-SM MDT group for BUM flooding */
+ struct in_addr mcast_grp;
+
+ /* Import and Export RTs. */
+ struct list *import_rtl;
+ struct list *export_rtl;
+
+ /*
+ * EVPN route that uses gateway IP overlay index as its nexthop
+ * needs to do a recursive lookup.
+ * A remote MAC/IP entry should be present for the gateway IP.
+ * Maintain a hash of the addresses received via remote MAC/IP routes
+ * for efficient gateway IP recursive lookup in this EVI
+ */
+ struct hash *remote_ip_hash;
+
+ /* Route table for EVPN routes for
+ * this VNI. */
+ struct bgp_table *route_table;
+
+ /* RB tree of ES-EVIs */
+ struct bgp_es_evi_rb_head es_evi_rb_tree;
+
+ /* List of local ESs */
+ struct list *local_es_evi_list;
+
+ QOBJ_FIELDS;
+};
+
+DECLARE_QOBJ_TYPE(bgpevpn);
+
+/* Mapping of Import RT to VNIs.
+ * The Import RTs of all VNIs are maintained in a hash table with each
+ * RT linking to all VNIs that will import routes matching this RT.
+ */
+struct irt_node {
+ /* RT */
+ struct ecommunity_val rt;
+
+ /* List of VNIs importing routes matching this RT. */
+ struct list *vnis;
+};
+
+/* Mapping of Import RT to VRFs.
+ * The Import RTs of all VRFss are maintained in a hash table with each
+ * RT linking to all VRFs that will import routes matching this RT.
+ */
+struct vrf_irt_node {
+ /* RT */
+ struct ecommunity_val rt;
+
+ /* List of VNIs importing routes matching this RT. */
+ struct list *vrfs;
+};
+
+
+#define RT_TYPE_IMPORT 1
+#define RT_TYPE_EXPORT 2
+#define RT_TYPE_BOTH 3
+
+#define EVPN_DAD_DEFAULT_TIME 180 /* secs */
+#define EVPN_DAD_DEFAULT_MAX_MOVES 5 /* default from RFC 7432 */
+#define EVPN_DAD_DEFAULT_AUTO_RECOVERY_TIME 1800 /* secs */
+
+struct bgp_evpn_info {
+ /* enable disable dup detect */
+ bool dup_addr_detect;
+
+ /* Detection time(M) */
+ int dad_time;
+ /* Detection max moves(N) */
+ uint32_t dad_max_moves;
+ /* Permanent freeze */
+ bool dad_freeze;
+ /* Recovery time */
+ uint32_t dad_freeze_time;
+
+ /* EVPN enable - advertise svi macip routes */
+ int advertise_svi_macip;
+
+ /* PIP feature knob */
+ bool advertise_pip;
+ /* PIP IP (sys ip) */
+ struct in_addr pip_ip;
+ struct in_addr pip_ip_static;
+ /* PIP MAC (sys MAC) */
+ struct ethaddr pip_rmac;
+ struct ethaddr pip_rmac_static;
+ struct ethaddr pip_rmac_zebra;
+ bool is_anycast_mac;
+};
+
+/* This structure defines an entry in remote_ip_hash */
+struct evpn_remote_ip {
+ struct ipaddr addr;
+ struct list *macip_path_list;
+};
+
+static inline int is_vrf_rd_configured(struct bgp *bgp_vrf)
+{
+ return (CHECK_FLAG(bgp_vrf->vrf_flags, BGP_VRF_RD_CFGD));
+}
+
+static inline int bgp_evpn_vrf_rd_matches_existing(struct bgp *bgp_vrf,
+ struct prefix_rd *prd)
+{
+ return (memcmp(&bgp_vrf->vrf_prd.val, prd->val, ECOMMUNITY_SIZE) == 0);
+}
+
+static inline vni_t bgpevpn_get_l3vni(struct bgpevpn *vpn)
+{
+ return vpn->bgp_vrf ? vpn->bgp_vrf->l3vni : 0;
+}
+
+static inline void bgpevpn_get_rmac(struct bgpevpn *vpn, struct ethaddr *rmac)
+{
+ memset(rmac, 0, sizeof(struct ethaddr));
+ if (!vpn->bgp_vrf)
+ return;
+ memcpy(rmac, &vpn->bgp_vrf->rmac, sizeof(struct ethaddr));
+}
+
+static inline struct list *bgpevpn_get_vrf_export_rtl(struct bgpevpn *vpn)
+{
+ if (!vpn->bgp_vrf)
+ return NULL;
+
+ return vpn->bgp_vrf->vrf_export_rtl;
+}
+
+static inline struct list *bgpevpn_get_vrf_import_rtl(struct bgpevpn *vpn)
+{
+ if (!vpn->bgp_vrf)
+ return NULL;
+
+ return vpn->bgp_vrf->vrf_import_rtl;
+}
+
+extern void bgp_evpn_es_evi_vrf_ref(struct bgpevpn *vpn);
+extern void bgp_evpn_es_evi_vrf_deref(struct bgpevpn *vpn);
+
+static inline void bgpevpn_unlink_from_l3vni(struct bgpevpn *vpn)
+{
+ /* bail if vpn is not associated to bgp_vrf */
+ if (!vpn->bgp_vrf)
+ return;
+
+ UNSET_FLAG(vpn->flags, VNI_FLAG_USE_TWO_LABELS);
+ listnode_delete(vpn->bgp_vrf->l2vnis, vpn);
+
+ bgp_evpn_es_evi_vrf_deref(vpn);
+
+ /* remove the backpointer to the vrf instance */
+ bgp_unlock(vpn->bgp_vrf);
+ vpn->bgp_vrf = NULL;
+}
+
+static inline void bgpevpn_link_to_l3vni(struct bgpevpn *vpn)
+{
+ struct bgp *bgp_vrf = NULL;
+
+ /* bail if vpn is already associated to vrf */
+ if (vpn->bgp_vrf)
+ return;
+
+ bgp_vrf = bgp_lookup_by_vrf_id(vpn->tenant_vrf_id);
+ if (!bgp_vrf)
+ return;
+
+ /* associate the vpn to the bgp_vrf instance */
+ vpn->bgp_vrf = bgp_lock(bgp_vrf);
+ listnode_add_sort(bgp_vrf->l2vnis, vpn);
+
+ /*
+ * If L3VNI is configured,
+ * check if we are advertising two labels for this vpn
+ */
+ if (bgp_vrf->l3vni &&
+ !CHECK_FLAG(bgp_vrf->vrf_flags, BGP_VRF_L3VNI_PREFIX_ROUTES_ONLY))
+ SET_FLAG(vpn->flags, VNI_FLAG_USE_TWO_LABELS);
+
+ bgp_evpn_es_evi_vrf_ref(vpn);
+}
+
+static inline int is_vni_configured(struct bgpevpn *vpn)
+{
+ return (CHECK_FLAG(vpn->flags, VNI_FLAG_CFGD));
+}
+
+static inline int is_vni_live(struct bgpevpn *vpn)
+{
+ return (CHECK_FLAG(vpn->flags, VNI_FLAG_LIVE));
+}
+
+static inline int is_l3vni_live(struct bgp *bgp_vrf)
+{
+ return (bgp_vrf->l3vni && bgp_vrf->l3vni_svi_ifindex);
+}
+
+static inline int is_rd_configured(struct bgpevpn *vpn)
+{
+ return (CHECK_FLAG(vpn->flags, VNI_FLAG_RD_CFGD));
+}
+
+static inline int bgp_evpn_rd_matches_existing(struct bgpevpn *vpn,
+ struct prefix_rd *prd)
+{
+ return (memcmp(&vpn->prd.val, prd->val, ECOMMUNITY_SIZE) == 0);
+}
+
+static inline int is_import_rt_configured(struct bgpevpn *vpn)
+{
+ return (CHECK_FLAG(vpn->flags, VNI_FLAG_IMPRT_CFGD));
+}
+
+static inline int is_export_rt_configured(struct bgpevpn *vpn)
+{
+ return (CHECK_FLAG(vpn->flags, VNI_FLAG_EXPRT_CFGD));
+}
+
+static inline void encode_es_rt_extcomm(struct ecommunity_val *eval,
+ struct ethaddr *mac)
+{
+ memset(eval, 0, sizeof(struct ecommunity_val));
+ eval->val[0] = ECOMMUNITY_ENCODE_EVPN;
+ eval->val[1] = ECOMMUNITY_EVPN_SUBTYPE_ES_IMPORT_RT;
+ memcpy(&eval->val[2], mac, ETH_ALEN);
+}
+
+static inline void encode_df_elect_extcomm(struct ecommunity_val *eval,
+ uint16_t pref)
+{
+ memset(eval, 0, sizeof(*eval));
+ eval->val[0] = ECOMMUNITY_ENCODE_EVPN;
+ eval->val[1] = ECOMMUNITY_EVPN_SUBTYPE_DF_ELECTION;
+ eval->val[2] = EVPN_MH_DF_ALG_PREF;
+ eval->val[6] = (pref >> 8) & 0xff;
+ eval->val[7] = pref & 0xff;
+}
+
+static inline void encode_esi_label_extcomm(struct ecommunity_val *eval,
+ bool single_active)
+{
+ memset(eval, 0, sizeof(struct ecommunity_val));
+ eval->val[0] = ECOMMUNITY_ENCODE_EVPN;
+ eval->val[1] = ECOMMUNITY_EVPN_SUBTYPE_ESI_LABEL;
+ if (single_active)
+ eval->val[2] |= (1 << 0);
+}
+
+static inline void encode_rmac_extcomm(struct ecommunity_val *eval,
+ struct ethaddr *rmac)
+{
+ memset(eval, 0, sizeof(*eval));
+ eval->val[0] = ECOMMUNITY_ENCODE_EVPN;
+ eval->val[1] = ECOMMUNITY_EVPN_SUBTYPE_ROUTERMAC;
+ memcpy(&eval->val[2], rmac, ETH_ALEN);
+}
+
+static inline void encode_default_gw_extcomm(struct ecommunity_val *eval)
+{
+ memset(eval, 0, sizeof(*eval));
+ eval->val[0] = ECOMMUNITY_ENCODE_OPAQUE;
+ eval->val[1] = ECOMMUNITY_EVPN_SUBTYPE_DEF_GW;
+}
+
+static inline void encode_mac_mobility_extcomm(int static_mac, uint32_t seq,
+ struct ecommunity_val *eval)
+{
+ memset(eval, 0, sizeof(*eval));
+ eval->val[0] = ECOMMUNITY_ENCODE_EVPN;
+ eval->val[1] = ECOMMUNITY_EVPN_SUBTYPE_MACMOBILITY;
+ if (static_mac)
+ eval->val[2] = ECOMMUNITY_EVPN_SUBTYPE_MACMOBILITY_FLAG_STICKY;
+ eval->val[4] = (seq >> 24) & 0xff;
+ eval->val[5] = (seq >> 16) & 0xff;
+ eval->val[6] = (seq >> 8) & 0xff;
+ eval->val[7] = seq & 0xff;
+}
+
+static inline void encode_na_flag_extcomm(struct ecommunity_val *eval,
+ uint8_t na_flag, bool proxy)
+{
+ memset(eval, 0, sizeof(*eval));
+ eval->val[0] = ECOMMUNITY_ENCODE_EVPN;
+ eval->val[1] = ECOMMUNITY_EVPN_SUBTYPE_ND;
+ if (na_flag)
+ eval->val[2] |= ECOMMUNITY_EVPN_SUBTYPE_ND_ROUTER_FLAG;
+ if (proxy)
+ eval->val[2] |= ECOMMUNITY_EVPN_SUBTYPE_PROXY_FLAG;
+}
+
+static inline void ip_prefix_from_type5_prefix(const struct prefix_evpn *evp,
+ struct prefix *ip)
+{
+ memset(ip, 0, sizeof(struct prefix));
+ if (is_evpn_prefix_ipaddr_v4(evp)) {
+ ip->family = AF_INET;
+ ip->prefixlen = evp->prefix.prefix_addr.ip_prefix_length;
+ memcpy(&(ip->u.prefix4), &(evp->prefix.prefix_addr.ip.ip),
+ IPV4_MAX_BYTELEN);
+ } else if (is_evpn_prefix_ipaddr_v6(evp)) {
+ ip->family = AF_INET6;
+ ip->prefixlen = evp->prefix.prefix_addr.ip_prefix_length;
+ memcpy(&(ip->u.prefix6), &(evp->prefix.prefix_addr.ip.ip),
+ IPV6_MAX_BYTELEN);
+ }
+}
+
+static inline int is_evpn_prefix_default(const struct prefix *evp)
+{
+ if (evp->family != AF_EVPN)
+ return 0;
+
+ return ((evp->u.prefix_evpn.prefix_addr.ip_prefix_length == 0) ?
+ 1 : 0);
+}
+
+static inline void ip_prefix_from_type2_prefix(const struct prefix_evpn *evp,
+ struct prefix *ip)
+{
+ memset(ip, 0, sizeof(struct prefix));
+ if (is_evpn_prefix_ipaddr_v4(evp)) {
+ ip->family = AF_INET;
+ ip->prefixlen = IPV4_MAX_BITLEN;
+ memcpy(&(ip->u.prefix4), &(evp->prefix.macip_addr.ip.ip),
+ IPV4_MAX_BYTELEN);
+ } else if (is_evpn_prefix_ipaddr_v6(evp)) {
+ ip->family = AF_INET6;
+ ip->prefixlen = IPV6_MAX_BITLEN;
+ memcpy(&(ip->u.prefix6), &(evp->prefix.macip_addr.ip.ip),
+ IPV6_MAX_BYTELEN);
+ }
+}
+
+static inline void ip_prefix_from_evpn_prefix(const struct prefix_evpn *evp,
+ struct prefix *ip)
+{
+ if (evp->prefix.route_type == BGP_EVPN_MAC_IP_ROUTE)
+ ip_prefix_from_type2_prefix(evp, ip);
+ else if (evp->prefix.route_type == BGP_EVPN_IP_PREFIX_ROUTE)
+ ip_prefix_from_type5_prefix(evp, ip);
+}
+
+static inline void build_evpn_type2_prefix(struct prefix_evpn *p,
+ struct ethaddr *mac,
+ struct ipaddr *ip)
+{
+ memset(p, 0, sizeof(struct prefix_evpn));
+ p->family = AF_EVPN;
+ p->prefixlen = EVPN_ROUTE_PREFIXLEN;
+ p->prefix.route_type = BGP_EVPN_MAC_IP_ROUTE;
+ memcpy(&p->prefix.macip_addr.mac.octet, mac->octet, ETH_ALEN);
+ p->prefix.macip_addr.ip.ipa_type = IPADDR_NONE;
+ memcpy(&p->prefix.macip_addr.ip, ip, sizeof(*ip));
+}
+
+static inline void
+build_type5_prefix_from_ip_prefix(struct prefix_evpn *evp,
+ const struct prefix *ip_prefix)
+{
+ struct ipaddr ip;
+
+ memset(&ip, 0, sizeof(struct ipaddr));
+ if (ip_prefix->family == AF_INET) {
+ ip.ipa_type = IPADDR_V4;
+ memcpy(&ip.ipaddr_v4, &ip_prefix->u.prefix4,
+ sizeof(struct in_addr));
+ } else {
+ ip.ipa_type = IPADDR_V6;
+ memcpy(&ip.ipaddr_v6, &ip_prefix->u.prefix6,
+ sizeof(struct in6_addr));
+ }
+
+ memset(evp, 0, sizeof(struct prefix_evpn));
+ evp->family = AF_EVPN;
+ evp->prefixlen = EVPN_ROUTE_PREFIXLEN;
+ evp->prefix.route_type = BGP_EVPN_IP_PREFIX_ROUTE;
+ evp->prefix.prefix_addr.ip_prefix_length = ip_prefix->prefixlen;
+ evp->prefix.prefix_addr.ip.ipa_type = ip.ipa_type;
+ memcpy(&evp->prefix.prefix_addr.ip, &ip, sizeof(struct ipaddr));
+}
+
+static inline void build_evpn_type3_prefix(struct prefix_evpn *p,
+ struct in_addr originator_ip)
+{
+ memset(p, 0, sizeof(struct prefix_evpn));
+ p->family = AF_EVPN;
+ p->prefixlen = EVPN_ROUTE_PREFIXLEN;
+ p->prefix.route_type = BGP_EVPN_IMET_ROUTE;
+ p->prefix.imet_addr.ip.ipa_type = IPADDR_V4;
+ p->prefix.imet_addr.ip.ipaddr_v4 = originator_ip;
+}
+
+static inline void build_evpn_type4_prefix(struct prefix_evpn *p,
+ esi_t *esi,
+ struct in_addr originator_ip)
+{
+ memset(p, 0, sizeof(struct prefix_evpn));
+ p->family = AF_EVPN;
+ p->prefixlen = EVPN_ROUTE_PREFIXLEN;
+ p->prefix.route_type = BGP_EVPN_ES_ROUTE;
+ p->prefix.es_addr.ip_prefix_length = IPV4_MAX_BITLEN;
+ p->prefix.es_addr.ip.ipa_type = IPADDR_V4;
+ p->prefix.es_addr.ip.ipaddr_v4 = originator_ip;
+ memcpy(&p->prefix.es_addr.esi, esi, sizeof(esi_t));
+}
+
+static inline void build_evpn_type1_prefix(struct prefix_evpn *p,
+ uint32_t eth_tag,
+ esi_t *esi,
+ struct in_addr originator_ip)
+{
+ memset(p, 0, sizeof(struct prefix_evpn));
+ p->family = AF_EVPN;
+ p->prefixlen = EVPN_ROUTE_PREFIXLEN;
+ p->prefix.route_type = BGP_EVPN_AD_ROUTE;
+ p->prefix.ead_addr.eth_tag = eth_tag;
+ p->prefix.ead_addr.ip.ipa_type = IPADDR_V4;
+ p->prefix.ead_addr.ip.ipaddr_v4 = originator_ip;
+ memcpy(&p->prefix.ead_addr.esi, esi, sizeof(esi_t));
+}
+
+static inline void evpn_type1_prefix_global_copy(struct prefix_evpn *global_p,
+ const struct prefix_evpn *vni_p)
+{
+ memcpy(global_p, vni_p, sizeof(*global_p));
+ global_p->prefix.ead_addr.ip.ipa_type = 0;
+ global_p->prefix.ead_addr.ip.ipaddr_v4.s_addr = INADDR_ANY;
+ global_p->prefix.ead_addr.frag_id = 0;
+}
+
+/* EAD prefix in the global table doesn't include the VTEP-IP so
+ * we need to create a different copy for the VNI
+ */
+static inline struct prefix_evpn *evpn_type1_prefix_vni_copy(
+ struct prefix_evpn *vni_p,
+ const struct prefix_evpn *global_p,
+ struct in_addr originator_ip)
+{
+ memcpy(vni_p, global_p, sizeof(*vni_p));
+ vni_p->prefix.ead_addr.ip.ipa_type = IPADDR_V4;
+ vni_p->prefix.ead_addr.ip.ipaddr_v4 = originator_ip;
+
+ return vni_p;
+}
+
+static inline int evpn_default_originate_set(struct bgp *bgp, afi_t afi,
+ safi_t safi)
+{
+ if (afi == AFI_IP &&
+ CHECK_FLAG(bgp->af_flags[AFI_L2VPN][SAFI_EVPN],
+ BGP_L2VPN_EVPN_DEFAULT_ORIGINATE_IPV4))
+ return 1;
+ else if (afi == AFI_IP6 &&
+ CHECK_FLAG(bgp->af_flags[AFI_L2VPN][SAFI_EVPN],
+ BGP_L2VPN_EVPN_DEFAULT_ORIGINATE_IPV6))
+ return 1;
+ return 0;
+}
+
+static inline void es_get_system_mac(esi_t *esi,
+ struct ethaddr *mac)
+{
+ /*
+ * for type-1 and type-3 ESIs,
+ * the system mac starts at val[1]
+ */
+ memcpy(mac, &esi->val[1], ETH_ALEN);
+}
+
+static inline bool bgp_evpn_is_svi_macip_enabled(struct bgpevpn *vpn)
+{
+ struct bgp *bgp_evpn = NULL;
+
+ bgp_evpn = bgp_get_evpn();
+
+ return (bgp_evpn->evpn_info->advertise_svi_macip ||
+ vpn->advertise_svi_macip);
+}
+
+static inline bool bgp_evpn_is_path_local(struct bgp *bgp,
+ struct bgp_path_info *pi)
+{
+ return (pi->peer == bgp->peer_self
+ && pi->type == ZEBRA_ROUTE_BGP
+ && pi->sub_type == BGP_ROUTE_STATIC);
+}
+
+extern struct zclient *zclient;
+
+extern void bgp_evpn_install_uninstall_default_route(struct bgp *bgp_vrf,
+ afi_t afi, safi_t safi,
+ bool add);
+extern void evpn_rt_delete_auto(struct bgp *, vni_t, struct list *);
+extern void bgp_evpn_configure_export_rt_for_vrf(struct bgp *bgp_vrf,
+ struct ecommunity *ecomadd);
+extern void bgp_evpn_unconfigure_export_rt_for_vrf(struct bgp *bgp_vrf,
+ struct ecommunity *ecomdel);
+extern void bgp_evpn_configure_import_rt_for_vrf(struct bgp *bgp_vrf,
+ struct ecommunity *ecomadd);
+extern void bgp_evpn_unconfigure_import_rt_for_vrf(struct bgp *bgp_vrf,
+ struct ecommunity *ecomdel);
+extern int bgp_evpn_handle_export_rt_change(struct bgp *bgp,
+ struct bgpevpn *vpn);
+extern void bgp_evpn_handle_autort_change(struct bgp *bgp);
+extern void bgp_evpn_handle_vrf_rd_change(struct bgp *bgp_vrf, int withdraw);
+extern void bgp_evpn_handle_rd_change(struct bgp *bgp, struct bgpevpn *vpn,
+ int withdraw);
+extern int bgp_evpn_install_routes(struct bgp *bgp, struct bgpevpn *vpn);
+extern int bgp_evpn_uninstall_routes(struct bgp *bgp, struct bgpevpn *vpn);
+extern void bgp_evpn_map_vrf_to_its_rts(struct bgp *bgp_vrf);
+extern void bgp_evpn_unmap_vrf_from_its_rts(struct bgp *bgp_vrf);
+extern void bgp_evpn_map_vni_to_its_rts(struct bgp *bgp, struct bgpevpn *vpn);
+extern void bgp_evpn_unmap_vni_from_its_rts(struct bgp *bgp,
+ struct bgpevpn *vpn);
+extern void bgp_evpn_derive_auto_rt_import(struct bgp *bgp,
+ struct bgpevpn *vpn);
+extern void bgp_evpn_derive_auto_rt_export(struct bgp *bgp,
+ struct bgpevpn *vpn);
+extern void bgp_evpn_derive_auto_rd(struct bgp *bgp, struct bgpevpn *vpn);
+extern void bgp_evpn_derive_auto_rd_for_vrf(struct bgp *bgp);
+extern struct bgpevpn *bgp_evpn_lookup_vni(struct bgp *bgp, vni_t vni);
+extern struct bgpevpn *bgp_evpn_new(struct bgp *bgp, vni_t vni,
+ struct in_addr originator_ip,
+ vrf_id_t tenant_vrf_id,
+ struct in_addr mcast_grp,
+ ifindex_t svi_ifindex);
+extern void bgp_evpn_free(struct bgp *bgp, struct bgpevpn *vpn);
+extern bool bgp_evpn_lookup_l3vni_l2vni_table(vni_t vni);
+extern int update_routes_for_vni(struct bgp *bgp, struct bgpevpn *vpn);
+extern void delete_evpn_route_entry(struct bgp *bgp, afi_t afi, safi_t safi,
+ struct bgp_dest *dest,
+ struct bgp_path_info **pi);
+int vni_list_cmp(void *p1, void *p2);
+extern int evpn_route_select_install(struct bgp *bgp, struct bgpevpn *vpn,
+ struct bgp_dest *dest);
+extern struct bgp_dest *bgp_global_evpn_node_get(struct bgp_table *table,
+ afi_t afi, safi_t safi,
+ const struct prefix_evpn *evp,
+ struct prefix_rd *prd);
+extern struct bgp_dest *
+bgp_global_evpn_node_lookup(struct bgp_table *table, afi_t afi, safi_t safi,
+ const struct prefix_evpn *evp,
+ struct prefix_rd *prd);
+extern void bgp_evpn_update_type2_route_entry(struct bgp *bgp,
+ struct bgpevpn *vpn,
+ struct bgp_node *rn,
+ struct bgp_path_info *local_pi,
+ const char *caller);
+extern int bgp_evpn_route_entry_install_if_vrf_match(struct bgp *bgp_vrf,
+ struct bgp_path_info *pi,
+ int install);
+extern void bgp_evpn_import_type2_route(struct bgp_path_info *pi, int import);
+extern void bgp_evpn_xxport_delete_ecomm(void *val);
+extern int bgp_evpn_route_target_cmp(struct ecommunity *ecom1,
+ struct ecommunity *ecom2);
+#endif /* _BGP_EVPN_PRIVATE_H */
diff --git a/bgpd/bgp_evpn_vty.c b/bgpd/bgp_evpn_vty.c
new file mode 100644
index 0000000..e4b67e2
--- /dev/null
+++ b/bgpd/bgp_evpn_vty.c
@@ -0,0 +1,6578 @@
+/* Ethernet-VPN Packet and vty Processing File
+ * Copyright (C) 2017 6WIND
+ *
+ * This file is part of FRRouting
+ *
+ * FRRouting is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License as published by the
+ * Free Software Foundation; either version 2, or (at your option) any
+ * later version.
+ *
+ * FRRouting is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program; see the file COPYING; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
+ */
+
+#include <zebra.h>
+#include "command.h"
+#include "prefix.h"
+#include "lib/json.h"
+#include "lib/printfrr.h"
+#include "lib/vxlan.h"
+#include "stream.h"
+
+#include "bgpd/bgpd.h"
+#include "bgpd/bgp_table.h"
+#include "bgpd/bgp_attr.h"
+#include "bgpd/bgp_route.h"
+#include "bgpd/bgp_mplsvpn.h"
+#include "bgpd/bgp_vpn.h"
+#include "bgpd/bgp_evpn_vty.h"
+#include "bgpd/bgp_evpn.h"
+#include "bgpd/bgp_evpn_private.h"
+#include "bgpd/bgp_evpn_mh.h"
+#include "bgpd/bgp_zebra.h"
+#include "bgpd/bgp_vty.h"
+#include "bgpd/bgp_errors.h"
+#include "bgpd/bgp_ecommunity.h"
+#include "bgpd/bgp_lcommunity.h"
+#include "bgpd/bgp_community.h"
+
+#define SHOW_DISPLAY_STANDARD 0
+#define SHOW_DISPLAY_TAGS 1
+#define SHOW_DISPLAY_OVERLAY 2
+#define VNI_STR_LEN 32
+
+/*
+ * Context for VNI hash walk - used by callbacks.
+ */
+struct vni_walk_ctx {
+ struct bgp *bgp;
+ struct vty *vty;
+ struct in_addr vtep_ip;
+ json_object *json;
+ int detail;
+};
+
+int argv_find_and_parse_oly_idx(struct cmd_token **argv, int argc, int *oly_idx,
+ enum overlay_index_type *oly)
+{
+ *oly = OVERLAY_INDEX_TYPE_NONE;
+ if (argv_find(argv, argc, "gateway-ip", oly_idx))
+ *oly = OVERLAY_INDEX_GATEWAY_IP;
+ return 1;
+}
+
+static void display_vrf_import_rt(struct vty *vty, struct vrf_irt_node *irt,
+ json_object *json)
+{
+ const uint8_t *pnt;
+ uint8_t type, sub_type;
+ struct ecommunity_as eas;
+ struct ecommunity_ip eip;
+ struct listnode *node, *nnode;
+ struct bgp *tmp_bgp_vrf = NULL;
+ json_object *json_rt = NULL;
+ json_object *json_vrfs = NULL;
+ char rt_buf[RT_ADDRSTRLEN];
+
+ if (json) {
+ json_rt = json_object_new_object();
+ json_vrfs = json_object_new_array();
+ }
+
+ pnt = (uint8_t *)&irt->rt.val;
+ type = *pnt++;
+ sub_type = *pnt++;
+ if (sub_type != ECOMMUNITY_ROUTE_TARGET)
+ return;
+
+ memset(&eas, 0, sizeof(eas));
+ switch (type) {
+ case ECOMMUNITY_ENCODE_AS:
+ eas.as = (*pnt++ << 8);
+ eas.as |= (*pnt++);
+ ptr_get_be32(pnt, &eas.val);
+
+ snprintf(rt_buf, sizeof(rt_buf), "%u:%u", eas.as, eas.val);
+
+ if (json)
+ json_object_string_add(json_rt, "rt", rt_buf);
+ else
+ vty_out(vty, "Route-target: %s", rt_buf);
+
+ break;
+
+ case ECOMMUNITY_ENCODE_IP:
+ memcpy(&eip.ip, pnt, 4);
+ pnt += 4;
+ eip.val = (*pnt++ << 8);
+ eip.val |= (*pnt++);
+
+ snprintfrr(rt_buf, sizeof(rt_buf), "%pI4:%u", &eip.ip, eip.val);
+
+ if (json)
+ json_object_string_add(json_rt, "rt", rt_buf);
+ else
+ vty_out(vty, "Route-target: %s", rt_buf);
+
+ break;
+
+ case ECOMMUNITY_ENCODE_AS4:
+ pnt = ptr_get_be32(pnt, &eas.val);
+ eas.val = (*pnt++ << 8);
+ eas.val |= (*pnt++);
+
+ snprintf(rt_buf, sizeof(rt_buf), "%u:%u", eas.as, eas.val);
+
+ if (json)
+ json_object_string_add(json_rt, "rt", rt_buf);
+ else
+ vty_out(vty, "Route-target: %s", rt_buf);
+
+ break;
+
+ default:
+ return;
+ }
+
+ if (!json) {
+ vty_out(vty,
+ "\nList of VRFs importing routes with this route-target:\n");
+ }
+
+ for (ALL_LIST_ELEMENTS(irt->vrfs, node, nnode, tmp_bgp_vrf)) {
+ if (json)
+ json_object_array_add(
+ json_vrfs,
+ json_object_new_string(
+ vrf_id_to_name(tmp_bgp_vrf->vrf_id)));
+ else
+ vty_out(vty, " %s\n",
+ vrf_id_to_name(tmp_bgp_vrf->vrf_id));
+ }
+
+ if (json) {
+ json_object_object_add(json_rt, "vrfs", json_vrfs);
+ json_object_object_add(json, rt_buf, json_rt);
+ }
+}
+
+static void show_vrf_import_rt_entry(struct hash_bucket *bucket, void *args[])
+{
+ json_object *json = NULL;
+ struct vty *vty = NULL;
+ struct vrf_irt_node *irt = (struct vrf_irt_node *)bucket->data;
+
+ vty = (struct vty *)args[0];
+ json = (struct json_object *)args[1];
+
+ display_vrf_import_rt(vty, irt, json);
+}
+
+static void display_import_rt(struct vty *vty, struct irt_node *irt,
+ json_object *json)
+{
+ const uint8_t *pnt;
+ uint8_t type, sub_type;
+ struct ecommunity_as eas;
+ struct ecommunity_ip eip;
+ struct listnode *node, *nnode;
+ struct bgpevpn *tmp_vpn;
+ json_object *json_rt = NULL;
+ json_object *json_vnis = NULL;
+ char rt_buf[RT_ADDRSTRLEN];
+
+ if (json) {
+ json_rt = json_object_new_object();
+ json_vnis = json_object_new_array();
+ }
+
+ /* TODO: This needs to go into a function */
+
+ pnt = (uint8_t *)&irt->rt.val;
+ type = *pnt++;
+ sub_type = *pnt++;
+ if (sub_type != ECOMMUNITY_ROUTE_TARGET)
+ return;
+
+ memset(&eas, 0, sizeof(eas));
+ switch (type) {
+ case ECOMMUNITY_ENCODE_AS:
+ eas.as = (*pnt++ << 8);
+ eas.as |= (*pnt++);
+ ptr_get_be32(pnt, &eas.val);
+
+ snprintf(rt_buf, sizeof(rt_buf), "%u:%u", eas.as, eas.val);
+
+ if (json)
+ json_object_string_add(json_rt, "rt", rt_buf);
+ else
+ vty_out(vty, "Route-target: %s", rt_buf);
+
+ break;
+
+ case ECOMMUNITY_ENCODE_IP:
+ memcpy(&eip.ip, pnt, 4);
+ pnt += 4;
+ eip.val = (*pnt++ << 8);
+ eip.val |= (*pnt++);
+
+ snprintfrr(rt_buf, sizeof(rt_buf), "%pI4:%u", &eip.ip, eip.val);
+
+ if (json)
+ json_object_string_add(json_rt, "rt", rt_buf);
+ else
+ vty_out(vty, "Route-target: %s", rt_buf);
+
+ break;
+
+ case ECOMMUNITY_ENCODE_AS4:
+ pnt = ptr_get_be32(pnt, &eas.val);
+ eas.val = (*pnt++ << 8);
+ eas.val |= (*pnt++);
+
+ snprintf(rt_buf, sizeof(rt_buf), "%u:%u", eas.as, eas.val);
+
+ if (json)
+ json_object_string_add(json_rt, "rt", rt_buf);
+ else
+ vty_out(vty, "Route-target: %s", rt_buf);
+
+ break;
+
+ default:
+ return;
+ }
+
+ if (!json) {
+ vty_out(vty,
+ "\nList of VNIs importing routes with this route-target:\n");
+ }
+
+ for (ALL_LIST_ELEMENTS(irt->vnis, node, nnode, tmp_vpn)) {
+ if (json)
+ json_object_array_add(
+ json_vnis, json_object_new_int(tmp_vpn->vni));
+ else
+ vty_out(vty, " %u\n", tmp_vpn->vni);
+ }
+
+ if (json) {
+ json_object_object_add(json_rt, "vnis", json_vnis);
+ json_object_object_add(json, rt_buf, json_rt);
+ }
+}
+
+static void show_import_rt_entry(struct hash_bucket *bucket, void *args[])
+{
+ json_object *json = NULL;
+ struct vty *vty = NULL;
+ struct irt_node *irt = (struct irt_node *)bucket->data;
+
+ vty = args[0];
+ json = args[1];
+
+ display_import_rt(vty, irt, json);
+
+ return;
+}
+
+static void bgp_evpn_show_route_rd_header(struct vty *vty,
+ struct bgp_dest *rd_dest,
+ json_object *json, char *rd_str,
+ int len)
+{
+ uint16_t type;
+ struct rd_as rd_as;
+ struct rd_ip rd_ip;
+ const uint8_t *pnt;
+ const struct prefix *p = bgp_dest_get_prefix(rd_dest);
+
+ pnt = p->u.val;
+
+ /* Decode RD type. */
+ type = decode_rd_type(pnt);
+
+ if (!json)
+ vty_out(vty, "Route Distinguisher: ");
+
+ switch (type) {
+ case RD_TYPE_AS:
+ decode_rd_as(pnt + 2, &rd_as);
+ snprintf(rd_str, len, "%u:%d", rd_as.as, rd_as.val);
+ if (json)
+ json_object_string_add(json, "rd", rd_str);
+ else
+ vty_out(vty, "%s\n", rd_str);
+ break;
+
+ case RD_TYPE_AS4:
+ decode_rd_as4(pnt + 2, &rd_as);
+ snprintf(rd_str, len, "%u:%d", rd_as.as, rd_as.val);
+ if (json)
+ json_object_string_add(json, "rd", rd_str);
+ else
+ vty_out(vty, "%s\n", rd_str);
+ break;
+
+ case RD_TYPE_IP:
+ decode_rd_ip(pnt + 2, &rd_ip);
+ snprintfrr(rd_str, len, "%pI4:%d", &rd_ip.ip, rd_ip.val);
+ if (json)
+ json_object_string_add(json, "rd", rd_str);
+ else
+ vty_out(vty, "%s\n", rd_str);
+ break;
+
+ default:
+ if (json) {
+ snprintf(rd_str, len, "Unknown");
+ json_object_string_add(json, "rd", rd_str);
+ } else {
+ snprintf(rd_str, len, "Unknown RD type");
+ vty_out(vty, "%s\n", rd_str);
+ }
+ break;
+ }
+}
+
+static void bgp_evpn_show_route_header(struct vty *vty, struct bgp *bgp,
+ uint64_t tbl_ver, json_object *json)
+{
+ char ri_header[] =
+ " Network Next Hop Metric LocPrf Weight Path\n";
+
+ if (json)
+ return;
+
+ vty_out(vty,
+ "BGP table version is %" PRIu64 ", local router ID is %pI4\n",
+ tbl_ver, &bgp->router_id);
+ vty_out(vty,
+ "Status codes: s suppressed, d damped, h history, * valid, > best, i - internal\n");
+ vty_out(vty, "Origin codes: i - IGP, e - EGP, ? - incomplete\n");
+ vty_out(vty,
+ "EVPN type-1 prefix: [1]:[EthTag]:[ESI]:[IPlen]:[VTEP-IP]:[Frag-id]\n");
+ vty_out(vty,
+ "EVPN type-2 prefix: [2]:[EthTag]:[MAClen]:[MAC]:[IPlen]:[IP]\n");
+ vty_out(vty, "EVPN type-3 prefix: [3]:[EthTag]:[IPlen]:[OrigIP]\n");
+ vty_out(vty, "EVPN type-4 prefix: [4]:[ESI]:[IPlen]:[OrigIP]\n");
+ vty_out(vty, "EVPN type-5 prefix: [5]:[EthTag]:[IPlen]:[IP]\n\n");
+ vty_out(vty, "%s", ri_header);
+}
+
+static void display_l3vni(struct vty *vty, struct bgp *bgp_vrf,
+ json_object *json)
+{
+ char buf1[INET6_ADDRSTRLEN];
+ char *ecom_str;
+ struct listnode *node, *nnode;
+ struct ecommunity *ecom;
+ json_object *json_import_rtl = NULL;
+ json_object *json_export_rtl = NULL;
+ char buf2[ETHER_ADDR_STRLEN];
+
+ json_import_rtl = json_export_rtl = 0;
+
+ if (json) {
+ json_import_rtl = json_object_new_array();
+ json_export_rtl = json_object_new_array();
+ json_object_int_add(json, "vni", bgp_vrf->l3vni);
+ json_object_string_add(json, "type", "L3");
+ json_object_string_add(json, "inKernel", "True");
+ json_object_string_addf(json, "rd", "%pRD", &bgp_vrf->vrf_prd);
+ json_object_string_addf(json, "originatorIp", "%pI4",
+ &bgp_vrf->originator_ip);
+ json_object_string_add(json, "advertiseGatewayMacip", "n/a");
+ json_object_string_add(json, "advertiseSviMacIp", "n/a");
+ json_object_string_add(json, "advertisePip",
+ bgp_vrf->evpn_info->advertise_pip ?
+ "Enabled" : "Disabled");
+ json_object_string_addf(json, "sysIP", "%pI4",
+ &bgp_vrf->evpn_info->pip_ip);
+ json_object_string_add(json, "sysMac",
+ prefix_mac2str(&bgp_vrf->evpn_info->pip_rmac,
+ buf2, sizeof(buf2)));
+ json_object_string_add(json, "rmac",
+ prefix_mac2str(&bgp_vrf->rmac,
+ buf2, sizeof(buf2)));
+ } else {
+ vty_out(vty, "VNI: %d", bgp_vrf->l3vni);
+ vty_out(vty, " (known to the kernel)");
+ vty_out(vty, "\n");
+
+ vty_out(vty, " Type: %s\n", "L3");
+ vty_out(vty, " Tenant VRF: %s\n",
+ vrf_id_to_name(bgp_vrf->vrf_id));
+ vty_out(vty, " RD: %pRD\n", &bgp_vrf->vrf_prd);
+ vty_out(vty, " Originator IP: %pI4\n",
+ &bgp_vrf->originator_ip);
+ vty_out(vty, " Advertise-gw-macip : %s\n", "n/a");
+ vty_out(vty, " Advertise-svi-macip : %s\n", "n/a");
+ vty_out(vty, " Advertise-pip: %s\n",
+ bgp_vrf->evpn_info->advertise_pip ? "Yes" : "No");
+ vty_out(vty, " System-IP: %s\n",
+ inet_ntop(AF_INET, &bgp_vrf->evpn_info->pip_ip,
+ buf1, INET_ADDRSTRLEN));
+ vty_out(vty, " System-MAC: %s\n",
+ prefix_mac2str(&bgp_vrf->evpn_info->pip_rmac,
+ buf2, sizeof(buf2)));
+ vty_out(vty, " Router-MAC: %s\n",
+ prefix_mac2str(&bgp_vrf->rmac,
+ buf2, sizeof(buf2)));
+ }
+
+ if (!json)
+ vty_out(vty, " Import Route Target:\n");
+
+ for (ALL_LIST_ELEMENTS(bgp_vrf->vrf_import_rtl, node, nnode, ecom)) {
+ ecom_str = ecommunity_ecom2str(ecom,
+ ECOMMUNITY_FORMAT_ROUTE_MAP, 0);
+
+ if (json)
+ json_object_array_add(json_import_rtl,
+ json_object_new_string(ecom_str));
+ else
+ vty_out(vty, " %s\n", ecom_str);
+
+ XFREE(MTYPE_ECOMMUNITY_STR, ecom_str);
+ }
+
+ if (json)
+ json_object_object_add(json, "importRts", json_import_rtl);
+ else
+ vty_out(vty, " Export Route Target:\n");
+
+ for (ALL_LIST_ELEMENTS(bgp_vrf->vrf_export_rtl, node, nnode, ecom)) {
+ ecom_str = ecommunity_ecom2str(ecom,
+ ECOMMUNITY_FORMAT_ROUTE_MAP, 0);
+
+ if (json)
+ json_object_array_add(json_export_rtl,
+ json_object_new_string(ecom_str));
+ else
+ vty_out(vty, " %s\n", ecom_str);
+
+ XFREE(MTYPE_ECOMMUNITY_STR, ecom_str);
+ }
+
+ if (json)
+ json_object_object_add(json, "exportRts", json_export_rtl);
+}
+
+static void display_vni(struct vty *vty, struct bgpevpn *vpn, json_object *json)
+{
+ char *ecom_str;
+ struct listnode *node, *nnode;
+ struct ecommunity *ecom;
+ json_object *json_import_rtl = NULL;
+ json_object *json_export_rtl = NULL;
+ struct bgp *bgp_evpn;
+
+ bgp_evpn = bgp_get_evpn();
+
+ if (json) {
+ json_import_rtl = json_object_new_array();
+ json_export_rtl = json_object_new_array();
+ json_object_int_add(json, "vni", vpn->vni);
+ json_object_string_add(json, "type", "L2");
+ json_object_string_add(json, "inKernel",
+ is_vni_live(vpn) ? "True" : "False");
+ json_object_string_addf(json, "rd", "%pRD", &vpn->prd);
+ json_object_string_addf(json, "originatorIp", "%pI4",
+ &vpn->originator_ip);
+ json_object_string_addf(json, "mcastGroup", "%pI4",
+ &vpn->mcast_grp);
+ /* per vni knob is enabled -- Enabled
+ * Global knob is enabled -- Active
+ * default -- Disabled
+ */
+ if (!vpn->advertise_gw_macip &&
+ bgp_evpn && bgp_evpn->advertise_gw_macip)
+ json_object_string_add(json, "advertiseGatewayMacip",
+ "Active");
+ else if (vpn->advertise_gw_macip)
+ json_object_string_add(json, "advertiseGatewayMacip",
+ "Enabled");
+ else
+ json_object_string_add(json, "advertiseGatewayMacip",
+ "Disabled");
+ if (!vpn->advertise_svi_macip && bgp_evpn &&
+ bgp_evpn->evpn_info->advertise_svi_macip)
+ json_object_string_add(json, "advertiseSviMacIp",
+ "Active");
+ else if (vpn->advertise_svi_macip)
+ json_object_string_add(json, "advertiseSviMacIp",
+ "Enabled");
+ else
+ json_object_string_add(json, "advertiseSviMacIp",
+ "Disabled");
+ json_object_string_add(
+ json, "sviInterface",
+ ifindex2ifname(vpn->svi_ifindex, vpn->tenant_vrf_id));
+ } else {
+ vty_out(vty, "VNI: %d", vpn->vni);
+ if (is_vni_live(vpn))
+ vty_out(vty, " (known to the kernel)");
+ vty_out(vty, "\n");
+
+ vty_out(vty, " Type: %s\n", "L2");
+ vty_out(vty, " Tenant-Vrf: %s\n",
+ vrf_id_to_name(vpn->tenant_vrf_id));
+ vty_out(vty, " RD: %pRD\n", &vpn->prd);
+ vty_out(vty, " Originator IP: %pI4\n", &vpn->originator_ip);
+ vty_out(vty, " Mcast group: %pI4\n", &vpn->mcast_grp);
+ if (!vpn->advertise_gw_macip &&
+ bgp_evpn && bgp_evpn->advertise_gw_macip)
+ vty_out(vty, " Advertise-gw-macip : %s\n",
+ "Active");
+ else if (vpn->advertise_gw_macip)
+ vty_out(vty, " Advertise-gw-macip : %s\n",
+ "Enabled");
+ else
+ vty_out(vty, " Advertise-gw-macip : %s\n",
+ "Disabled");
+ if (!vpn->advertise_svi_macip && bgp_evpn &&
+ bgp_evpn->evpn_info->advertise_svi_macip)
+ vty_out(vty, " Advertise-svi-macip : %s\n",
+ "Active");
+ else if (vpn->advertise_svi_macip)
+ vty_out(vty, " Advertise-svi-macip : %s\n",
+ "Enabled");
+ else
+ vty_out(vty, " Advertise-svi-macip : %s\n",
+ "Disabled");
+ vty_out(vty, " SVI interface : %s\n",
+ ifindex2ifname(vpn->svi_ifindex, vpn->tenant_vrf_id));
+ }
+
+ if (!json)
+ vty_out(vty, " Import Route Target:\n");
+
+ for (ALL_LIST_ELEMENTS(vpn->import_rtl, node, nnode, ecom)) {
+ ecom_str = ecommunity_ecom2str(ecom,
+ ECOMMUNITY_FORMAT_ROUTE_MAP, 0);
+
+ if (json)
+ json_object_array_add(json_import_rtl,
+ json_object_new_string(ecom_str));
+ else
+ vty_out(vty, " %s\n", ecom_str);
+
+ XFREE(MTYPE_ECOMMUNITY_STR, ecom_str);
+ }
+
+ if (json)
+ json_object_object_add(json, "importRts", json_import_rtl);
+ else
+ vty_out(vty, " Export Route Target:\n");
+
+ for (ALL_LIST_ELEMENTS(vpn->export_rtl, node, nnode, ecom)) {
+ ecom_str = ecommunity_ecom2str(ecom,
+ ECOMMUNITY_FORMAT_ROUTE_MAP, 0);
+
+ if (json)
+ json_object_array_add(json_export_rtl,
+ json_object_new_string(ecom_str));
+ else
+ vty_out(vty, " %s\n", ecom_str);
+
+ XFREE(MTYPE_ECOMMUNITY_STR, ecom_str);
+ }
+
+ if (json)
+ json_object_object_add(json, "exportRts", json_export_rtl);
+}
+
+static void show_esi_routes(struct bgp *bgp,
+ struct bgp_evpn_es *es,
+ struct vty *vty,
+ json_object *json)
+{
+ int header = 1;
+ struct bgp_dest *dest;
+ struct bgp_path_info *pi;
+ uint32_t prefix_cnt, path_cnt;
+ uint64_t tbl_ver;
+
+ prefix_cnt = path_cnt = 0;
+
+ tbl_ver = es->route_table->version;
+ for (dest = bgp_table_top(es->route_table); dest;
+ dest = bgp_route_next(dest)) {
+ int add_prefix_to_json = 0;
+ json_object *json_paths = NULL;
+ json_object *json_prefix = NULL;
+ const struct prefix *p = bgp_dest_get_prefix(dest);
+
+ if (json)
+ json_prefix = json_object_new_object();
+
+ pi = bgp_dest_get_bgp_path_info(dest);
+ if (pi) {
+ /* Overall header/legend displayed once. */
+ if (header) {
+ bgp_evpn_show_route_header(vty, bgp,
+ tbl_ver, json);
+ header = 0;
+ }
+
+ prefix_cnt++;
+ }
+
+ if (json)
+ json_paths = json_object_new_array();
+
+ /* For EVPN, the prefix is displayed for each path (to fit in
+ * with code that already exists).
+ */
+ for (; pi; pi = pi->next) {
+ json_object *json_path = NULL;
+
+ if (json)
+ json_path = json_object_new_array();
+
+ route_vty_out(vty, p, pi, 0, SAFI_EVPN, json_path,
+ false);
+
+ if (json)
+ json_object_array_add(json_paths, json_path);
+
+ path_cnt++;
+ add_prefix_to_json = 1;
+ }
+
+ if (json) {
+ if (add_prefix_to_json) {
+ json_object_string_addf(json_prefix, "prefix",
+ "%pFX", p);
+ json_object_int_add(json_prefix, "prefixLen",
+ p->prefixlen);
+ json_object_object_add(json_prefix, "paths",
+ json_paths);
+ json_object_object_addf(json, json_prefix,
+ "%pFX", p);
+ } else {
+ json_object_free(json_paths);
+ json_object_free(json_prefix);
+ json_paths = NULL;
+ json_prefix = NULL;
+ }
+ }
+ }
+
+ if (json) {
+ json_object_int_add(json, "numPrefix", prefix_cnt);
+ json_object_int_add(json, "numPaths", path_cnt);
+ } else {
+ if (prefix_cnt == 0)
+ vty_out(vty, "No EVPN prefixes exist for this ESI\n");
+ else
+ vty_out(vty, "\nDisplayed %u prefixes (%u paths)\n",
+ prefix_cnt, path_cnt);
+ }
+}
+
+/* Display all MAC-IP VNI routes linked to an ES */
+static void bgp_evpn_show_routes_mac_ip_es(struct vty *vty, esi_t *esi,
+ json_object *json, int detail,
+ bool global_table)
+{
+ struct bgp_node *rn;
+ struct bgp_path_info *pi;
+ int header = detail ? 0 : 1;
+ uint32_t path_cnt;
+ struct listnode *node;
+ struct bgp_evpn_es *es;
+ struct bgp_path_es_info *es_info;
+ struct bgp *bgp = bgp_get_evpn();
+ json_object *json_paths = NULL;
+
+ if (!bgp)
+ return;
+
+ path_cnt = 0;
+
+ if (json)
+ json_paths = json_object_new_array();
+
+ RB_FOREACH (es, bgp_es_rb_head, &bgp_mh_info->es_rb_tree) {
+ struct list *es_list;
+
+ if (esi && memcmp(esi, &es->esi, sizeof(*esi)))
+ continue;
+
+ if (global_table)
+ es_list = es->macip_global_path_list;
+ else
+ es_list = es->macip_evi_path_list;
+
+ for (ALL_LIST_ELEMENTS_RO(es_list, node, es_info)) {
+ json_object *json_path = NULL;
+
+ pi = es_info->pi;
+ rn = pi->net;
+
+ if (!CHECK_FLAG(pi->flags, BGP_PATH_VALID))
+ continue;
+
+ /* Overall header/legend displayed once. */
+ if (header) {
+ bgp_evpn_show_route_header(vty, bgp, 0, json);
+ header = 0;
+ }
+
+ path_cnt++;
+
+ if (json)
+ json_path = json_object_new_array();
+
+ if (detail)
+ route_vty_out_detail(
+ vty, bgp, rn, pi, AFI_L2VPN, SAFI_EVPN,
+ RPKI_NOT_BEING_USED, json_path);
+ else
+ route_vty_out(vty, &rn->p, pi, 0, SAFI_EVPN,
+ json_path, false);
+
+ if (json)
+ json_object_array_add(json_paths, json_path);
+ }
+ }
+
+ if (json) {
+ json_object_object_add(json, "paths", json_paths);
+ json_object_int_add(json, "numPaths", path_cnt);
+ } else {
+ if (path_cnt == 0)
+ vty_out(vty, "There are no MAC-IP ES paths");
+ else
+ vty_out(vty, "\nDisplayed %u paths\n", path_cnt);
+ vty_out(vty, "\n");
+ }
+}
+
+static void bgp_evpn_show_routes_mac_ip_evi_es(struct vty *vty, esi_t *esi,
+ json_object *json, int detail)
+{
+ bgp_evpn_show_routes_mac_ip_es(vty, esi, json, detail, false);
+}
+
+static void bgp_evpn_show_routes_mac_ip_global_es(struct vty *vty, esi_t *esi,
+ json_object *json, int detail)
+{
+ bgp_evpn_show_routes_mac_ip_es(vty, esi, json, detail, true);
+}
+
+static void show_vni_routes(struct bgp *bgp, struct bgpevpn *vpn, int type,
+ struct vty *vty, struct in_addr vtep_ip,
+ json_object *json, int detail)
+{
+ struct bgp_dest *dest;
+ struct bgp_path_info *pi;
+ struct bgp_table *table;
+ int header = detail ? 0 : 1;
+ uint64_t tbl_ver;
+ uint32_t prefix_cnt, path_cnt;
+
+ prefix_cnt = path_cnt = 0;
+
+ table = vpn->route_table;
+ tbl_ver = table->version;
+ for (dest = bgp_table_top(table); dest; dest = bgp_route_next(dest)) {
+ const struct prefix_evpn *evp =
+ (const struct prefix_evpn *)bgp_dest_get_prefix(dest);
+ int add_prefix_to_json = 0;
+ json_object *json_paths = NULL;
+ json_object *json_prefix = NULL;
+ const struct prefix *p = bgp_dest_get_prefix(dest);
+
+ if (type && evp->prefix.route_type != type)
+ continue;
+
+ if (json)
+ json_prefix = json_object_new_object();
+
+ pi = bgp_dest_get_bgp_path_info(dest);
+ if (pi) {
+ /* Overall header/legend displayed once. */
+ if (header) {
+ bgp_evpn_show_route_header(vty, bgp,
+ tbl_ver, json);
+ header = 0;
+ }
+
+ prefix_cnt++;
+ }
+
+ if (json)
+ json_paths = json_object_new_array();
+
+ /* For EVPN, the prefix is displayed for each path (to fit in
+ * with code that already exists).
+ */
+ for (; pi; pi = pi->next) {
+ json_object *json_path = NULL;
+
+ if (vtep_ip.s_addr != INADDR_ANY
+ && !IPV4_ADDR_SAME(&(vtep_ip),
+ &(pi->attr->nexthop)))
+ continue;
+
+ if (json)
+ json_path = json_object_new_array();
+
+ if (detail)
+ route_vty_out_detail(vty, bgp, dest, pi,
+ AFI_L2VPN, SAFI_EVPN,
+ RPKI_NOT_BEING_USED,
+ json_path);
+ else
+ route_vty_out(vty, p, pi, 0, SAFI_EVPN,
+ json_path, false);
+
+ if (json)
+ json_object_array_add(json_paths, json_path);
+
+ path_cnt++;
+ add_prefix_to_json = 1;
+ }
+
+ if (json) {
+ if (add_prefix_to_json) {
+ json_object_string_addf(json_prefix, "prefix",
+ "%pFX", p);
+ json_object_int_add(json_prefix, "prefixLen",
+ p->prefixlen);
+ json_object_object_add(json_prefix, "paths",
+ json_paths);
+ json_object_object_addf(json, json_prefix,
+ "%pFX", p);
+ } else {
+ json_object_free(json_paths);
+ json_object_free(json_prefix);
+ json_paths = NULL;
+ json_prefix = NULL;
+ }
+ }
+ }
+
+ if (json) {
+ json_object_int_add(json, "numPrefix", prefix_cnt);
+ json_object_int_add(json, "numPaths", path_cnt);
+ } else {
+ if (prefix_cnt == 0)
+ vty_out(vty, "No EVPN prefixes %sexist for this VNI",
+ type ? "(of requested type) " : "");
+ else
+ vty_out(vty, "\nDisplayed %u prefixes (%u paths)%s\n",
+ prefix_cnt, path_cnt,
+ type ? " (of requested type)" : "");
+ vty_out(vty, "\n");
+ }
+}
+
+static void show_vni_routes_hash(struct hash_bucket *bucket, void *arg)
+{
+ struct bgpevpn *vpn = (struct bgpevpn *)bucket->data;
+ struct vni_walk_ctx *wctx = arg;
+ struct vty *vty = wctx->vty;
+ json_object *json = wctx->json;
+ json_object *json_vni = NULL;
+ char vni_str[VNI_STR_LEN];
+
+ snprintf(vni_str, sizeof(vni_str), "%d", vpn->vni);
+ if (json) {
+ json_vni = json_object_new_object();
+ json_object_int_add(json_vni, "vni", vpn->vni);
+ } else {
+ vty_out(vty, "\nVNI: %d\n\n", vpn->vni);
+ }
+
+ show_vni_routes(wctx->bgp, vpn, 0, wctx->vty, wctx->vtep_ip, json_vni,
+ wctx->detail);
+
+ if (json)
+ json_object_object_add(json, vni_str, json_vni);
+}
+
+static void show_l3vni_entry(struct vty *vty, struct bgp *bgp,
+ json_object *json)
+{
+ json_object *json_vni = NULL;
+ json_object *json_import_rtl = NULL;
+ json_object *json_export_rtl = NULL;
+ char buf1[10];
+ char buf2[INET6_ADDRSTRLEN];
+ char rt_buf[25];
+ char *ecom_str;
+ struct listnode *node, *nnode;
+ struct ecommunity *ecom;
+
+ if (!bgp->l3vni)
+ return;
+
+ if (json) {
+ json_vni = json_object_new_object();
+ json_import_rtl = json_object_new_array();
+ json_export_rtl = json_object_new_array();
+ }
+
+ /* if an l3vni is present in bgp it is live */
+ buf1[0] = '\0';
+ snprintf(buf1, sizeof(buf1), "*");
+
+ if (json) {
+ json_object_int_add(json_vni, "vni", bgp->l3vni);
+ json_object_string_add(json_vni, "type", "L3");
+ json_object_string_add(json_vni, "inKernel", "True");
+ json_object_string_addf(json_vni, "originatorIp", "%pI4",
+ &bgp->originator_ip);
+ json_object_string_addf(json_vni, "rd", "%pRD", &bgp->vrf_prd);
+ json_object_string_add(json_vni, "advertiseGatewayMacip",
+ "n/a");
+ json_object_string_add(json_vni, "advertiseSviMacIp", "n/a");
+ json_object_string_add(
+ json_vni, "advertisePip",
+ bgp->evpn_info->advertise_pip ? "Enabled" : "Disabled");
+ json_object_string_addf(json_vni, "sysIP", "%pI4",
+ &bgp->evpn_info->pip_ip);
+ json_object_string_add(json_vni, "sysMAC",
+ prefix_mac2str(&bgp->evpn_info->pip_rmac,
+ buf2, sizeof(buf2)));
+ json_object_string_add(
+ json_vni, "rmac",
+ prefix_mac2str(&bgp->rmac, buf2, sizeof(buf2)));
+ } else {
+ vty_out(vty, "%-1s %-10u %-4s %-21pRD", buf1, bgp->l3vni, "L3",
+ &bgp->vrf_prd);
+ }
+
+ for (ALL_LIST_ELEMENTS(bgp->vrf_import_rtl, node, nnode, ecom)) {
+ ecom_str = ecommunity_ecom2str(ecom,
+ ECOMMUNITY_FORMAT_ROUTE_MAP, 0);
+
+ if (json) {
+ json_object_array_add(json_import_rtl,
+ json_object_new_string(ecom_str));
+ } else {
+ if (listcount(bgp->vrf_import_rtl) > 1)
+ snprintf(rt_buf, sizeof(rt_buf), "%s, ...",
+ ecom_str);
+ else
+ snprintf(rt_buf, sizeof(rt_buf), "%s",
+ ecom_str);
+ vty_out(vty, " %-25s", rt_buf);
+ }
+
+ XFREE(MTYPE_ECOMMUNITY_STR, ecom_str);
+
+ /* If there are multiple import RTs we break here and show only
+ * one */
+ if (!json)
+ break;
+ }
+
+ if (json)
+ json_object_object_add(json_vni, "importRTs", json_import_rtl);
+
+ for (ALL_LIST_ELEMENTS(bgp->vrf_export_rtl, node, nnode, ecom)) {
+ ecom_str = ecommunity_ecom2str(ecom,
+ ECOMMUNITY_FORMAT_ROUTE_MAP, 0);
+
+ if (json) {
+ json_object_array_add(json_export_rtl,
+ json_object_new_string(ecom_str));
+ } else {
+ if (listcount(bgp->vrf_export_rtl) > 1)
+ snprintf(rt_buf, sizeof(rt_buf), "%s, ...",
+ ecom_str);
+ else
+ snprintf(rt_buf, sizeof(rt_buf), "%s",
+ ecom_str);
+ vty_out(vty, " %-25s", rt_buf);
+ }
+
+ XFREE(MTYPE_ECOMMUNITY_STR, ecom_str);
+
+ /* If there are multiple export RTs we break here and show only
+ * one */
+ if (!json) {
+ vty_out(vty, "%-37s", vrf_id_to_name(bgp->vrf_id));
+ break;
+ }
+ }
+
+ if (json) {
+ char vni_str[VNI_STR_LEN];
+
+ json_object_object_add(json_vni, "exportRTs", json_export_rtl);
+ snprintf(vni_str, sizeof(vni_str), "%u", bgp->l3vni);
+ json_object_object_add(json, vni_str, json_vni);
+ } else {
+ vty_out(vty, "\n");
+ }
+}
+
+static void show_vni_entry(struct hash_bucket *bucket, void *args[])
+{
+ struct vty *vty;
+ json_object *json;
+ json_object *json_vni = NULL;
+ json_object *json_import_rtl = NULL;
+ json_object *json_export_rtl = NULL;
+ struct bgpevpn *vpn = (struct bgpevpn *)bucket->data;
+ char buf1[10];
+ char rt_buf[25];
+ char *ecom_str;
+ struct listnode *node, *nnode;
+ struct ecommunity *ecom;
+ struct bgp *bgp_evpn;
+
+ vty = args[0];
+ json = args[1];
+
+ bgp_evpn = bgp_get_evpn();
+
+ if (json) {
+ json_vni = json_object_new_object();
+ json_import_rtl = json_object_new_array();
+ json_export_rtl = json_object_new_array();
+ }
+
+ buf1[0] = '\0';
+ if (is_vni_live(vpn))
+ snprintf(buf1, sizeof(buf1), "*");
+
+ if (json) {
+ json_object_int_add(json_vni, "vni", vpn->vni);
+ json_object_string_add(json_vni, "type", "L2");
+ json_object_string_add(json_vni, "inKernel",
+ is_vni_live(vpn) ? "True" : "False");
+ json_object_string_addf(json_vni, "rd", "%pRD", &vpn->prd);
+ json_object_string_addf(json_vni, "originatorIp", "%pI4",
+ &vpn->originator_ip);
+ json_object_string_addf(json_vni, "mcastGroup", "%pI4",
+ &vpn->mcast_grp);
+ /* per vni knob is enabled -- Enabled
+ * Global knob is enabled -- Active
+ * default -- Disabled
+ */
+ if (!vpn->advertise_gw_macip && bgp_evpn
+ && bgp_evpn->advertise_gw_macip)
+ json_object_string_add(
+ json_vni, "advertiseGatewayMacip", "Active");
+ else if (vpn->advertise_gw_macip)
+ json_object_string_add(
+ json_vni, "advertiseGatewayMacip", "Enabled");
+ else
+ json_object_string_add(
+ json_vni, "advertiseGatewayMacip", "Disabled");
+ if (!vpn->advertise_svi_macip && bgp_evpn
+ && bgp_evpn->evpn_info->advertise_svi_macip)
+ json_object_string_add(json_vni, "advertiseSviMacIp",
+ "Active");
+ else if (vpn->advertise_svi_macip)
+ json_object_string_add(json_vni, "advertiseSviMacIp",
+ "Enabled");
+ else
+ json_object_string_add(json_vni, "advertiseSviMacIp",
+ "Disabled");
+ } else {
+ vty_out(vty, "%-1s %-10u %-4s %-21pRD", buf1, vpn->vni, "L2",
+ &vpn->prd);
+ }
+
+ for (ALL_LIST_ELEMENTS(vpn->import_rtl, node, nnode, ecom)) {
+ ecom_str = ecommunity_ecom2str(ecom,
+ ECOMMUNITY_FORMAT_ROUTE_MAP, 0);
+
+ if (json) {
+ json_object_array_add(json_import_rtl,
+ json_object_new_string(ecom_str));
+ } else {
+ if (listcount(vpn->import_rtl) > 1)
+ snprintf(rt_buf, sizeof(rt_buf), "%s, ...",
+ ecom_str);
+ else
+ snprintf(rt_buf, sizeof(rt_buf), "%s",
+ ecom_str);
+ vty_out(vty, " %-25s", rt_buf);
+ }
+
+ XFREE(MTYPE_ECOMMUNITY_STR, ecom_str);
+
+ /* If there are multiple import RTs we break here and show only
+ * one */
+ if (!json)
+ break;
+ }
+
+ if (json)
+ json_object_object_add(json_vni, "importRTs", json_import_rtl);
+
+ for (ALL_LIST_ELEMENTS(vpn->export_rtl, node, nnode, ecom)) {
+ ecom_str = ecommunity_ecom2str(ecom,
+ ECOMMUNITY_FORMAT_ROUTE_MAP, 0);
+
+ if (json) {
+ json_object_array_add(json_export_rtl,
+ json_object_new_string(ecom_str));
+ } else {
+ if (listcount(vpn->export_rtl) > 1)
+ snprintf(rt_buf, sizeof(rt_buf), "%s, ...",
+ ecom_str);
+ else
+ snprintf(rt_buf, sizeof(rt_buf), "%s",
+ ecom_str);
+ vty_out(vty, " %-25s", rt_buf);
+ }
+
+ XFREE(MTYPE_ECOMMUNITY_STR, ecom_str);
+
+ /* If there are multiple export RTs we break here and show only
+ * one */
+ if (!json) {
+ vty_out(vty, "%-37s",
+ vrf_id_to_name(vpn->tenant_vrf_id));
+ break;
+ }
+ }
+
+ if (json) {
+ char vni_str[VNI_STR_LEN];
+
+ json_object_object_add(json_vni, "exportRTs", json_export_rtl);
+ snprintf(vni_str, sizeof(vni_str), "%u", vpn->vni);
+ json_object_object_add(json, vni_str, json_vni);
+ } else {
+ vty_out(vty, "\n");
+ }
+}
+
+static int bgp_show_ethernet_vpn(struct vty *vty, struct prefix_rd *prd,
+ enum bgp_show_type type, void *output_arg,
+ int option, bool use_json)
+{
+ afi_t afi = AFI_L2VPN;
+ struct bgp *bgp;
+ struct bgp_table *table;
+ struct bgp_dest *dest;
+ struct bgp_dest *rm;
+ struct bgp_path_info *pi;
+ int rd_header;
+ int header = 1;
+ char rd_str[RD_ADDRSTRLEN];
+ int no_display;
+
+ unsigned long output_count = 0;
+ unsigned long total_count = 0;
+ json_object *json = NULL;
+ json_object *json_array = NULL;
+ json_object *json_prefix_info = NULL;
+
+ memset(rd_str, 0, RD_ADDRSTRLEN);
+
+ bgp = bgp_get_evpn();
+ if (bgp == NULL) {
+ if (!use_json)
+ vty_out(vty, "No BGP process is configured\n");
+ else
+ vty_out(vty, "{}\n");
+ return CMD_WARNING;
+ }
+
+ if (use_json)
+ json = json_object_new_object();
+
+ for (dest = bgp_table_top(bgp->rib[afi][SAFI_EVPN]); dest;
+ dest = bgp_route_next(dest)) {
+ uint64_t tbl_ver;
+ json_object *json_nroute = NULL;
+ const struct prefix *p = bgp_dest_get_prefix(dest);
+
+ if (prd && memcmp(p->u.val, prd->val, 8) != 0)
+ continue;
+
+ table = bgp_dest_get_bgp_table_info(dest);
+ if (!table)
+ continue;
+
+ rd_header = 1;
+ tbl_ver = table->version;
+
+ for (rm = bgp_table_top(table); rm; rm = bgp_route_next(rm)) {
+ pi = bgp_dest_get_bgp_path_info(rm);
+ if (pi == NULL)
+ continue;
+
+ no_display = 0;
+ for (; pi; pi = pi->next) {
+ struct community *picomm = NULL;
+
+ picomm = bgp_attr_get_community(pi->attr);
+
+ total_count++;
+ if (type == bgp_show_type_neighbor) {
+ struct peer *peer = output_arg;
+
+ if (peer_cmp(peer, pi->peer) != 0)
+ 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) {
+ 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_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 (header) {
+ if (use_json) {
+ json_object_int_add(
+ json, "bgpTableVersion",
+ tbl_ver);
+ 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",
+ bgp->as);
+ } else {
+ if (option == SHOW_DISPLAY_TAGS)
+ vty_out(vty,
+ V4_HEADER_TAG);
+ else if (
+ option
+ == SHOW_DISPLAY_OVERLAY)
+ vty_out(vty,
+ V4_HEADER_OVERLAY);
+ else {
+ bgp_evpn_show_route_header(vty, bgp, tbl_ver, NULL);
+ }
+ }
+ header = 0;
+ }
+ if (rd_header) {
+ if (use_json)
+ json_nroute =
+ json_object_new_object();
+ bgp_evpn_show_route_rd_header(
+ vty, dest, json_nroute, rd_str,
+ RD_ADDRSTRLEN);
+ rd_header = 0;
+ }
+ if (use_json && !json_array)
+ json_array = json_object_new_array();
+
+ if (option == SHOW_DISPLAY_TAGS)
+ route_vty_out_tag(
+ vty, bgp_dest_get_prefix(rm),
+ pi, no_display, SAFI_EVPN,
+ json_array);
+ else if (option == SHOW_DISPLAY_OVERLAY)
+ route_vty_out_overlay(
+ vty, bgp_dest_get_prefix(rm),
+ pi, no_display, json_array);
+ else
+ route_vty_out(vty,
+ bgp_dest_get_prefix(rm),
+ pi, no_display, SAFI_EVPN,
+ json_array, false);
+ no_display = 1;
+ }
+
+ if (no_display)
+ output_count++;
+
+ if (use_json && json_array) {
+ const struct prefix *p =
+ bgp_dest_get_prefix(rm);
+
+ json_prefix_info = json_object_new_object();
+
+ json_object_string_addf(json_prefix_info,
+ "prefix", "%pFX", p);
+
+ json_object_int_add(json_prefix_info,
+ "prefixLen", p->prefixlen);
+
+ json_object_object_add(json_prefix_info,
+ "paths", json_array);
+ json_object_object_addf(json_nroute,
+ json_prefix_info,
+ "%pFX", p);
+ json_array = NULL;
+ }
+ }
+
+ if (use_json && json_nroute)
+ json_object_object_add(json, rd_str, json_nroute);
+ }
+
+ if (use_json) {
+ json_object_int_add(json, "numPrefix", output_count);
+ json_object_int_add(json, "totalPrefix", total_count);
+ vty_json(vty, json);
+ } else {
+ if (output_count == 0)
+ vty_out(vty, "No prefixes displayed, %ld exist\n",
+ total_count);
+ else
+ vty_out(vty,
+ "\nDisplayed %ld out of %ld total prefixes\n",
+ output_count, total_count);
+ }
+ return CMD_SUCCESS;
+}
+
+DEFUN(show_ip_bgp_l2vpn_evpn,
+ show_ip_bgp_l2vpn_evpn_cmd,
+ "show [ip] bgp l2vpn evpn [json]",
+ SHOW_STR IP_STR BGP_STR L2VPN_HELP_STR EVPN_HELP_STR JSON_STR)
+{
+ return bgp_show_ethernet_vpn(vty, NULL, bgp_show_type_normal, NULL,
+ SHOW_DISPLAY_STANDARD,
+ use_json(argc, argv));
+}
+
+DEFUN(show_ip_bgp_l2vpn_evpn_rd,
+ show_ip_bgp_l2vpn_evpn_rd_cmd,
+ "show [ip] bgp l2vpn evpn rd <ASN:NN_OR_IP-ADDRESS:NN|all> [json]",
+ SHOW_STR
+ IP_STR
+ BGP_STR
+ L2VPN_HELP_STR
+ EVPN_HELP_STR
+ "Display information for a route distinguisher\n"
+ "VPN Route Distinguisher\n"
+ "All VPN Route Distinguishers\n"
+ JSON_STR)
+{
+ int idx_ext_community = 0;
+ int ret;
+ struct prefix_rd prd;
+ int rd_all = 0;
+
+ if (argv_find(argv, argc, "all", &rd_all))
+ return bgp_show_ethernet_vpn(vty, NULL, bgp_show_type_normal,
+ NULL, SHOW_DISPLAY_STANDARD,
+ use_json(argc, argv));
+
+ argv_find(argv, argc, "ASN:NN_OR_IP-ADDRESS:NN", &idx_ext_community);
+ ret = str2prefix_rd(argv[idx_ext_community]->arg, &prd);
+ if (!ret) {
+ vty_out(vty, "%% Malformed Route Distinguisher\n");
+ return CMD_WARNING;
+ }
+ return bgp_show_ethernet_vpn(vty, &prd, bgp_show_type_normal, NULL,
+ SHOW_DISPLAY_STANDARD,
+ use_json(argc, argv));
+}
+
+DEFUN(show_ip_bgp_l2vpn_evpn_all_tags,
+ show_ip_bgp_l2vpn_evpn_all_tags_cmd,
+ "show [ip] bgp l2vpn evpn all tags",
+ SHOW_STR
+ IP_STR
+ BGP_STR
+ L2VPN_HELP_STR
+ EVPN_HELP_STR
+ "Display information about all EVPN NLRIs\n"
+ "Display BGP tags for prefixes\n")
+{
+ return bgp_show_ethernet_vpn(vty, NULL, bgp_show_type_normal, NULL,
+ SHOW_DISPLAY_TAGS, 0);
+}
+
+DEFUN(show_ip_bgp_l2vpn_evpn_rd_tags,
+ show_ip_bgp_l2vpn_evpn_rd_tags_cmd,
+ "show [ip] bgp l2vpn evpn rd <ASN:NN_OR_IP-ADDRESS:NN|all> tags",
+ SHOW_STR
+ IP_STR
+ BGP_STR
+ L2VPN_HELP_STR
+ EVPN_HELP_STR
+ "Display information for a route distinguisher\n"
+ "VPN Route Distinguisher\n"
+ "All VPN Route Distinguishers\n"
+ "Display BGP tags for prefixes\n")
+{
+ int idx_ext_community = 0;
+ int ret;
+ struct prefix_rd prd;
+ int rd_all = 0;
+
+ if (argv_find(argv, argc, "all", &rd_all))
+ return bgp_show_ethernet_vpn(vty, NULL, bgp_show_type_normal,
+ NULL, SHOW_DISPLAY_TAGS, 0);
+
+ argv_find(argv, argc, "ASN:NN_OR_IP-ADDRESS:NN", &idx_ext_community);
+ ret = str2prefix_rd(argv[idx_ext_community]->arg, &prd);
+ if (!ret) {
+ vty_out(vty, "%% Malformed Route Distinguisher\n");
+ return CMD_WARNING;
+ }
+ return bgp_show_ethernet_vpn(vty, &prd, bgp_show_type_normal, NULL,
+ SHOW_DISPLAY_TAGS, 0);
+}
+
+DEFUN(show_ip_bgp_l2vpn_evpn_neighbor_routes,
+ show_ip_bgp_l2vpn_evpn_neighbor_routes_cmd,
+ "show [ip] bgp l2vpn evpn neighbors <A.B.C.D|X:X::X:X|WORD> routes [json]",
+ SHOW_STR
+ IP_STR
+ BGP_STR
+ L2VPN_HELP_STR
+ EVPN_HELP_STR
+ "Detailed information on TCP and BGP neighbor connections\n"
+ "IPv4 Neighbor to display information about\n"
+ "IPv6 Neighbor to display information about\n"
+ "Neighbor on BGP configured interface\n"
+ "Display routes learned from neighbor\n" JSON_STR)
+{
+ int idx = 0;
+ struct peer *peer;
+ char *peerstr = NULL;
+ bool uj = use_json(argc, argv);
+ afi_t afi = AFI_L2VPN;
+ safi_t safi = SAFI_EVPN;
+ struct bgp *bgp = NULL;
+
+ bgp_vty_find_and_parse_afi_safi_bgp(vty, argv, argc, &idx, &afi, &safi,
+ &bgp, uj);
+ if (!idx) {
+ vty_out(vty, "No index\n");
+ 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) {
+ if (uj) {
+ json_object *json_no = NULL;
+ json_no = json_object_new_object();
+ json_object_string_add(json_no, "warning",
+ "Malformed address");
+ vty_out(vty, "%s\n",
+ json_object_to_json_string(json_no));
+ json_object_free(json_no);
+ } else
+ vty_out(vty, "Malformed address: %s\n",
+ argv[idx]->arg);
+ return CMD_WARNING;
+ }
+ if (!peer || !peer->afc[AFI_L2VPN][SAFI_EVPN]) {
+ if (uj) {
+ 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;
+ }
+
+ return bgp_show_ethernet_vpn(vty, NULL, bgp_show_type_neighbor, peer,
+ SHOW_DISPLAY_STANDARD, uj);
+}
+
+DEFUN(show_ip_bgp_l2vpn_evpn_rd_neighbor_routes,
+ show_ip_bgp_l2vpn_evpn_rd_neighbor_routes_cmd,
+ "show [ip] bgp l2vpn evpn rd <ASN:NN_OR_IP-ADDRESS:NN|all> neighbors <A.B.C.D|X:X::X:X|WORD> routes [json]",
+ SHOW_STR
+ IP_STR
+ BGP_STR
+ L2VPN_HELP_STR
+ EVPN_HELP_STR
+ "Display information for a route distinguisher\n"
+ "VPN Route Distinguisher\n"
+ "All VPN Route Distinguishers\n"
+ "Detailed information on TCP and BGP neighbor connections\n"
+ "IPv4 Neighbor to display information about\n"
+ "IPv6 Neighbor to display information about\n"
+ "Neighbor on BGP configured interface\n"
+ "Display routes learned from neighbor\n" JSON_STR)
+{
+ int idx_ext_community = 0;
+ int idx = 0;
+ int ret;
+ struct peer *peer;
+ char *peerstr = NULL;
+ struct prefix_rd prd = {};
+ bool uj = use_json(argc, argv);
+ afi_t afi = AFI_L2VPN;
+ safi_t safi = SAFI_EVPN;
+ struct bgp *bgp = NULL;
+ int rd_all = 0;
+
+ bgp_vty_find_and_parse_afi_safi_bgp(vty, argv, argc, &idx, &afi, &safi,
+ &bgp, uj);
+ if (!idx) {
+ vty_out(vty, "No index\n");
+ return CMD_WARNING;
+ }
+
+ if (argv_find(argv, argc, "all", &rd_all)) {
+ argv_find(argv, argc, "ASN:NN_OR_IP-ADDRESS:NN",
+ &idx_ext_community);
+ ret = str2prefix_rd(argv[idx_ext_community]->arg, &prd);
+ if (!ret) {
+ if (uj) {
+ json_object *json_no = NULL;
+ json_no = json_object_new_object();
+ json_object_string_add(
+ json_no, "warning",
+ "Malformed Route Distinguisher");
+ vty_out(vty, "%s\n",
+ json_object_to_json_string(json_no));
+ json_object_free(json_no);
+ } else
+ vty_out(vty,
+ "%% Malformed Route Distinguisher\n");
+ 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) {
+ if (uj) {
+ json_object *json_no = NULL;
+ json_no = json_object_new_object();
+ json_object_string_add(json_no, "warning",
+ "Malformed address");
+ vty_out(vty, "%s\n",
+ json_object_to_json_string(json_no));
+ json_object_free(json_no);
+ } else
+ vty_out(vty, "Malformed address: %s\n",
+ argv[idx]->arg);
+ return CMD_WARNING;
+ }
+ if (!peer || !peer->afc[AFI_L2VPN][SAFI_EVPN]) {
+ if (uj) {
+ 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;
+ }
+
+
+ if (rd_all)
+ return bgp_show_ethernet_vpn(vty, NULL, bgp_show_type_neighbor,
+ peer, SHOW_DISPLAY_STANDARD, uj);
+ else
+ return bgp_show_ethernet_vpn(vty, &prd, bgp_show_type_neighbor,
+ peer, SHOW_DISPLAY_STANDARD, uj);
+}
+
+DEFUN(show_ip_bgp_l2vpn_evpn_neighbor_advertised_routes,
+ show_ip_bgp_l2vpn_evpn_neighbor_advertised_routes_cmd,
+ "show [ip] bgp l2vpn evpn neighbors <A.B.C.D|X:X::X:X|WORD> advertised-routes [json]",
+ SHOW_STR
+ IP_STR
+ BGP_STR
+ L2VPN_HELP_STR
+ EVPN_HELP_STR
+ "Detailed information on TCP and BGP neighbor connections\n"
+ "IPv4 Neighbor to display information about\n"
+ "IPv6 Neighbor to display information about\n"
+ "Neighbor on BGP configured interface\n"
+ "Display the routes advertised to a BGP neighbor\n" JSON_STR)
+{
+ int idx = 0;
+ struct peer *peer;
+ bool uj = use_json(argc, argv);
+ struct bgp *bgp = NULL;
+ afi_t afi = AFI_L2VPN;
+ safi_t safi = SAFI_EVPN;
+ char *peerstr = NULL;
+
+ if (uj)
+ argc--;
+
+ bgp_vty_find_and_parse_afi_safi_bgp(vty, argv, argc, &idx, &afi, &safi,
+ &bgp, uj);
+ if (!idx) {
+ vty_out(vty, "No index\n");
+ 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) {
+ if (uj) {
+ json_object *json_no = NULL;
+ json_no = json_object_new_object();
+ json_object_string_add(json_no, "warning",
+ "Malformed address");
+ vty_out(vty, "%s\n",
+ json_object_to_json_string(json_no));
+ json_object_free(json_no);
+ } else
+ vty_out(vty, "Malformed address: %s\n",
+ argv[idx]->arg);
+ return CMD_WARNING;
+ }
+ if (!peer || !peer->afc[AFI_L2VPN][SAFI_EVPN]) {
+ if (uj) {
+ 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;
+ }
+
+ return show_adj_route_vpn(vty, peer, NULL, AFI_L2VPN, SAFI_EVPN, uj);
+}
+
+DEFUN(show_ip_bgp_l2vpn_evpn_rd_neighbor_advertised_routes,
+ show_ip_bgp_l2vpn_evpn_rd_neighbor_advertised_routes_cmd,
+ "show [ip] bgp l2vpn evpn rd <ASN:NN_OR_IP-ADDRESS:NN|all> neighbors <A.B.C.D|X:X::X:X|WORD> advertised-routes [json]",
+ SHOW_STR
+ IP_STR
+ BGP_STR
+ L2VPN_HELP_STR
+ EVPN_HELP_STR
+ "Display information for a route distinguisher\n"
+ "VPN Route Distinguisher\n"
+ "All VPN Route Distinguishers\n"
+ "Detailed information on TCP and BGP neighbor connections\n"
+ "IPv4 Neighbor to display information about\n"
+ "IPv6 Neighbor to display information about\n"
+ "Neighbor on BGP configured interface\n"
+ "Display the routes advertised to a BGP neighbor\n" JSON_STR)
+{
+ int idx_ext_community = 0;
+ int idx = 0;
+ int ret;
+ struct peer *peer;
+ struct prefix_rd prd;
+ struct bgp *bgp = NULL;
+ bool uj = use_json(argc, argv);
+ char *peerstr = NULL;
+ afi_t afi = AFI_L2VPN;
+ safi_t safi = SAFI_EVPN;
+ int rd_all = 0;
+
+ if (uj)
+ argc--;
+
+ if (uj)
+ argc--;
+
+ bgp_vty_find_and_parse_afi_safi_bgp(vty, argv, argc, &idx, &afi, &safi,
+ &bgp, uj);
+ if (!idx) {
+ vty_out(vty, "No index\n");
+ 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) {
+ if (uj) {
+ json_object *json_no = NULL;
+ json_no = json_object_new_object();
+ json_object_string_add(json_no, "warning",
+ "Malformed address");
+ vty_out(vty, "%s\n",
+ json_object_to_json_string(json_no));
+ json_object_free(json_no);
+ } else
+ vty_out(vty, "Malformed address: %s\n",
+ argv[idx]->arg);
+ return CMD_WARNING;
+ }
+ if (!peer || !peer->afc[AFI_L2VPN][SAFI_EVPN]) {
+ if (uj) {
+ 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;
+ }
+
+ if (argv_find(argv, argc, "all", &rd_all))
+ return show_adj_route_vpn(vty, peer, NULL, AFI_L2VPN, SAFI_EVPN,
+ uj);
+ else {
+ argv_find(argv, argc, "ASN:NN_OR_IP-ADDRESS:NN",
+ &idx_ext_community);
+ ret = str2prefix_rd(argv[idx_ext_community]->arg, &prd);
+ if (!ret) {
+ if (uj) {
+ json_object *json_no = NULL;
+ json_no = json_object_new_object();
+ json_object_string_add(
+ json_no, "warning",
+ "Malformed Route Distinguisher");
+ vty_out(vty, "%s\n",
+ json_object_to_json_string(json_no));
+ json_object_free(json_no);
+ } else
+ vty_out(vty,
+ "%% Malformed Route Distinguisher\n");
+ return CMD_WARNING;
+ }
+ }
+
+ return show_adj_route_vpn(vty, peer, &prd, AFI_L2VPN, SAFI_EVPN, uj);
+}
+
+DEFUN(show_ip_bgp_l2vpn_evpn_all_overlay,
+ show_ip_bgp_l2vpn_evpn_all_overlay_cmd,
+ "show [ip] bgp l2vpn evpn all overlay [json]",
+ SHOW_STR
+ IP_STR
+ BGP_STR
+ L2VPN_HELP_STR
+ EVPN_HELP_STR
+ "Display information about all EVPN NLRIs\n"
+ "Display BGP Overlay Information for prefixes\n"
+ JSON_STR)
+{
+ return bgp_show_ethernet_vpn(vty, NULL, bgp_show_type_normal, NULL,
+ SHOW_DISPLAY_OVERLAY,
+ use_json(argc, argv));
+}
+
+DEFUN(show_ip_bgp_evpn_rd_overlay,
+ show_ip_bgp_evpn_rd_overlay_cmd,
+ "show [ip] bgp l2vpn evpn rd <ASN:NN_OR_IP-ADDRESS:NN|all> overlay",
+ SHOW_STR
+ IP_STR
+ BGP_STR
+ L2VPN_HELP_STR
+ EVPN_HELP_STR
+ "Display information for a route distinguisher\n"
+ "VPN Route Distinguisher\n"
+ "All VPN Route Distinguishers\n"
+ "Display BGP Overlay Information for prefixes\n")
+{
+ int idx_ext_community = 0;
+ int ret;
+ struct prefix_rd prd;
+ int rd_all = 0;
+
+ if (argv_find(argv, argc, "all", &rd_all))
+ return bgp_show_ethernet_vpn(vty, NULL, bgp_show_type_normal,
+ NULL, SHOW_DISPLAY_OVERLAY,
+ use_json(argc, argv));
+
+ argv_find(argv, argc, "ASN:NN_OR_IP-ADDRESS:NN", &idx_ext_community);
+ ret = str2prefix_rd(argv[idx_ext_community]->arg, &prd);
+ if (!ret) {
+ vty_out(vty, "%% Malformed Route Distinguisher\n");
+ return CMD_WARNING;
+ }
+ return bgp_show_ethernet_vpn(vty, &prd, bgp_show_type_normal, NULL,
+ SHOW_DISPLAY_OVERLAY,
+ use_json(argc, argv));
+}
+
+DEFUN(show_bgp_l2vpn_evpn_com,
+ show_bgp_l2vpn_evpn_com_cmd,
+ "show bgp l2vpn evpn \
+ <community AA:NN|large-community AA:BB:CC> \
+ [exact-match] [json]",
+ SHOW_STR
+ BGP_STR
+ L2VPN_HELP_STR
+ EVPN_HELP_STR
+ "Display routes matching the community\n"
+ "Community number where AA and NN are (0-65535)\n"
+ "Display routes matching the large-community\n"
+ "List of large-community numbers\n"
+ "Exact match of the communities\n"
+ JSON_STR)
+{
+ int idx = 0;
+ int ret = 0;
+ const char *clist_number_or_name;
+ int show_type = bgp_show_type_normal;
+ struct community *com;
+ struct lcommunity *lcom;
+
+ if (argv_find(argv, argc, "large-community", &idx)) {
+ clist_number_or_name = argv[++idx]->arg;
+ show_type = bgp_show_type_lcommunity;
+
+ if (++idx < argc && strmatch(argv[idx]->text, "exact-match"))
+ show_type = bgp_show_type_lcommunity_exact;
+
+ lcom = lcommunity_str2com(clist_number_or_name);
+ if (!lcom) {
+ vty_out(vty, "%% Large-community malformed\n");
+ return CMD_WARNING;
+ }
+
+ ret = bgp_show_ethernet_vpn(vty, NULL, show_type, lcom,
+ SHOW_DISPLAY_STANDARD,
+ use_json(argc, argv));
+
+ lcommunity_free(&lcom);
+ } else if (argv_find(argv, argc, "community", &idx)) {
+ clist_number_or_name = argv[++idx]->arg;
+ show_type = bgp_show_type_community;
+
+ if (++idx < argc && strmatch(argv[idx]->text, "exact-match"))
+ show_type = bgp_show_type_community_exact;
+
+ com = community_str2com(clist_number_or_name);
+
+ if (!com) {
+ vty_out(vty, "%% Community malformed: %s\n",
+ clist_number_or_name);
+ return CMD_WARNING;
+ }
+
+ ret = bgp_show_ethernet_vpn(vty, NULL, show_type, com,
+ SHOW_DISPLAY_STANDARD,
+ use_json(argc, argv));
+ community_free(&com);
+ }
+
+ return ret;
+}
+
+/* For testing purpose, static route of EVPN RT-5. */
+DEFUN(evpnrt5_network,
+ evpnrt5_network_cmd,
+ "network <A.B.C.D/M|X:X::X:X/M> rd ASN:NN_OR_IP-ADDRESS:NN ethtag WORD label WORD esi WORD gwip <A.B.C.D|X:X::X:X> routermac WORD [route-map RMAP_NAME]",
+ "Specify a network to announce via BGP\n"
+ "IP prefix\n"
+ "IPv6 prefix\n"
+ "Specify Route Distinguisher\n"
+ "VPN Route Distinguisher\n"
+ "Ethernet Tag\n"
+ "Ethernet Tag Value\n"
+ "BGP label\n"
+ "label value\n"
+ "Ethernet Segment Identifier\n"
+ "ESI value ( 00:11:22:33:44:55:66:77:88:99 format) \n"
+ "Gateway IP\n"
+ "Gateway IP ( A.B.C.D )\n"
+ "Gateway IPv6 ( X:X::X:X )\n"
+ "Router Mac Ext Comm\n"
+ "Router Mac address Value ( aa:bb:cc:dd:ee:ff format)\n"
+ "Route-map to modify the attributes\n"
+ "Name of the route map\n")
+{
+ int idx_ipv4_prefixlen = 1;
+ int idx_route_distinguisher = 3;
+ int idx_label = 7;
+ int idx_esi = 9;
+ int idx_gwip = 11;
+ int idx_ethtag = 5;
+ int idx_routermac = 13;
+
+ return bgp_static_set_safi(
+ AFI_L2VPN, SAFI_EVPN, vty, argv[idx_ipv4_prefixlen]->arg,
+ argv[idx_route_distinguisher]->arg, argv[idx_label]->arg, NULL,
+ BGP_EVPN_IP_PREFIX_ROUTE, argv[idx_esi]->arg,
+ argv[idx_gwip]->arg, argv[idx_ethtag]->arg,
+ argv[idx_routermac]->arg);
+}
+
+/* For testing purpose, static route of EVPN RT-5. */
+DEFUN(no_evpnrt5_network,
+ no_evpnrt5_network_cmd,
+ "no network <A.B.C.D/M|X:X::X:X/M> rd ASN:NN_OR_IP-ADDRESS:NN ethtag WORD label WORD esi WORD gwip <A.B.C.D|X:X::X:X>",
+ NO_STR
+ "Specify a network to announce via BGP\n"
+ "IP prefix\n"
+ "IPv6 prefix\n"
+ "Specify Route Distinguisher\n"
+ "VPN Route Distinguisher\n"
+ "Ethernet Tag\n"
+ "Ethernet Tag Value\n"
+ "BGP label\n"
+ "label value\n"
+ "Ethernet Segment Identifier\n"
+ "ESI value ( 00:11:22:33:44:55:66:77:88:99 format) \n"
+ "Gateway IP\n" "Gateway IP ( A.B.C.D )\n" "Gateway IPv6 ( X:X::X:X )\n")
+{
+ int idx_ipv4_prefixlen = 2;
+ int idx_ext_community = 4;
+ int idx_label = 8;
+ int idx_ethtag = 6;
+ int idx_esi = 10;
+ int idx_gwip = 12;
+ return bgp_static_unset_safi(
+ AFI_L2VPN, SAFI_EVPN, vty, argv[idx_ipv4_prefixlen]->arg,
+ argv[idx_ext_community]->arg, argv[idx_label]->arg,
+ BGP_EVPN_IP_PREFIX_ROUTE, argv[idx_esi]->arg,
+ argv[idx_gwip]->arg, argv[idx_ethtag]->arg);
+}
+
+static void evpn_import_rt_delete_auto(struct bgp *bgp, struct bgpevpn *vpn)
+{
+ evpn_rt_delete_auto(bgp, vpn->vni, vpn->import_rtl);
+}
+
+static void evpn_export_rt_delete_auto(struct bgp *bgp, struct bgpevpn *vpn)
+{
+ evpn_rt_delete_auto(bgp, vpn->vni, vpn->export_rtl);
+}
+
+/*
+ * Configure the Import RTs for a VNI (vty handler). Caller expected to
+ * check that this is a change.
+ */
+static void evpn_configure_import_rt(struct bgp *bgp, struct bgpevpn *vpn,
+ struct ecommunity *ecomadd)
+{
+ /* If the VNI is "live", we need to uninstall routes using the current
+ * import RT(s) first before we update the import RT, and subsequently
+ * install routes.
+ */
+ if (is_vni_live(vpn))
+ bgp_evpn_uninstall_routes(bgp, vpn);
+
+ /* Cleanup the RT to VNI mapping and get rid of existing import RT. */
+ bgp_evpn_unmap_vni_from_its_rts(bgp, vpn);
+
+ /* If the auto route-target is in use we must remove it */
+ evpn_import_rt_delete_auto(bgp, vpn);
+
+ /* Add new RT and rebuild the RT to VNI mapping */
+ listnode_add_sort(vpn->import_rtl, ecomadd);
+
+ SET_FLAG(vpn->flags, VNI_FLAG_IMPRT_CFGD);
+ bgp_evpn_map_vni_to_its_rts(bgp, vpn);
+
+ /* Install routes that match new import RT */
+ if (is_vni_live(vpn))
+ bgp_evpn_install_routes(bgp, vpn);
+}
+
+/*
+ * Unconfigure Import RT(s) for a VNI (vty handler).
+ */
+static void evpn_unconfigure_import_rt(struct bgp *bgp, struct bgpevpn *vpn,
+ struct ecommunity *ecomdel)
+{
+ struct listnode *node, *nnode, *node_to_del;
+ struct ecommunity *ecom;
+
+ /* Along the lines of "configure" except we have to reset to the
+ * automatic value.
+ */
+ if (is_vni_live(vpn))
+ bgp_evpn_uninstall_routes(bgp, vpn);
+
+ /* Cleanup the RT to VNI mapping and get rid of existing import RT. */
+ bgp_evpn_unmap_vni_from_its_rts(bgp, vpn);
+
+ /* Delete all import RTs */
+ if (ecomdel == NULL) {
+ for (ALL_LIST_ELEMENTS(vpn->import_rtl, node, nnode, ecom)) {
+ ecommunity_free(&ecom);
+ list_delete_node(vpn->import_rtl, node);
+ }
+ }
+
+ /* Delete a specific import RT */
+ else {
+ node_to_del = NULL;
+
+ for (ALL_LIST_ELEMENTS(vpn->import_rtl, node, nnode, ecom)) {
+ if (ecommunity_match(ecom, ecomdel)) {
+ ecommunity_free(&ecom);
+ node_to_del = node;
+ break;
+ }
+ }
+
+ if (node_to_del)
+ list_delete_node(vpn->import_rtl, node_to_del);
+ }
+
+ assert(vpn->import_rtl);
+ /* Reset to auto RT - this also rebuilds the RT to VNI mapping */
+ if (list_isempty(vpn->import_rtl)) {
+ UNSET_FLAG(vpn->flags, VNI_FLAG_IMPRT_CFGD);
+ bgp_evpn_derive_auto_rt_import(bgp, vpn);
+ }
+ /* Rebuild the RT to VNI mapping */
+ else
+ bgp_evpn_map_vni_to_its_rts(bgp, vpn);
+
+ /* Install routes that match new import RT */
+ if (is_vni_live(vpn))
+ bgp_evpn_install_routes(bgp, vpn);
+}
+
+/*
+ * Configure the Export RT for a VNI (vty handler). Caller expected to
+ * check that this is a change. Note that only a single export RT is
+ * allowed for a VNI and any change to configuration is implemented as
+ * a "replace" (similar to other configuration).
+ */
+static void evpn_configure_export_rt(struct bgp *bgp, struct bgpevpn *vpn,
+ struct ecommunity *ecomadd)
+{
+ /* If the auto route-target is in use we must remove it */
+ evpn_export_rt_delete_auto(bgp, vpn);
+
+ listnode_add_sort(vpn->export_rtl, ecomadd);
+ SET_FLAG(vpn->flags, VNI_FLAG_EXPRT_CFGD);
+
+ if (is_vni_live(vpn))
+ bgp_evpn_handle_export_rt_change(bgp, vpn);
+}
+
+/*
+ * Unconfigure the Export RT for a VNI (vty handler)
+ */
+static void evpn_unconfigure_export_rt(struct bgp *bgp, struct bgpevpn *vpn,
+ struct ecommunity *ecomdel)
+{
+ struct listnode *node, *nnode, *node_to_del;
+ struct ecommunity *ecom;
+
+ /* Delete all export RTs */
+ if (ecomdel == NULL) {
+ /* Reset to default and process all routes. */
+ for (ALL_LIST_ELEMENTS(vpn->export_rtl, node, nnode, ecom)) {
+ ecommunity_free(&ecom);
+ list_delete_node(vpn->export_rtl, node);
+ }
+ }
+
+ /* Delete a specific export RT */
+ else {
+ node_to_del = NULL;
+
+ for (ALL_LIST_ELEMENTS(vpn->export_rtl, node, nnode, ecom)) {
+ if (ecommunity_match(ecom, ecomdel)) {
+ ecommunity_free(&ecom);
+ node_to_del = node;
+ break;
+ }
+ }
+
+ if (node_to_del)
+ list_delete_node(vpn->export_rtl, node_to_del);
+ }
+
+ assert(vpn->export_rtl);
+ if (list_isempty(vpn->export_rtl)) {
+ UNSET_FLAG(vpn->flags, VNI_FLAG_EXPRT_CFGD);
+ bgp_evpn_derive_auto_rt_export(bgp, vpn);
+ }
+
+ if (is_vni_live(vpn))
+ bgp_evpn_handle_export_rt_change(bgp, vpn);
+}
+
+/*
+ * Configure RD for VRF
+ */
+static void evpn_configure_vrf_rd(struct bgp *bgp_vrf, struct prefix_rd *rd)
+{
+ /* If we have already advertise type-5 routes with a diffrent RD, we
+ * have to delete and withdraw them firs
+ */
+ bgp_evpn_handle_vrf_rd_change(bgp_vrf, 1);
+
+ /* update RD */
+ memcpy(&bgp_vrf->vrf_prd, rd, sizeof(struct prefix_rd));
+ SET_FLAG(bgp_vrf->vrf_flags, BGP_VRF_RD_CFGD);
+
+ /* We have a new RD for VRF.
+ * Advertise all type-5 routes again with the new RD
+ */
+ bgp_evpn_handle_vrf_rd_change(bgp_vrf, 0);
+}
+
+/*
+ * Unconfigure RD for VRF
+ */
+static void evpn_unconfigure_vrf_rd(struct bgp *bgp_vrf)
+{
+ /* If we have already advertise type-5 routes with a diffrent RD, we
+ * have to delete and withdraw them firs
+ */
+ bgp_evpn_handle_vrf_rd_change(bgp_vrf, 1);
+
+ /* fall back to default RD */
+ bgp_evpn_derive_auto_rd_for_vrf(bgp_vrf);
+ UNSET_FLAG(bgp_vrf->vrf_flags, BGP_VRF_RD_CFGD);
+
+ /* We have a new RD for VRF.
+ * Advertise all type-5 routes again with the new RD
+ */
+ bgp_evpn_handle_vrf_rd_change(bgp_vrf, 0);
+}
+
+/*
+ * Configure RD for a VNI (vty handler)
+ */
+static void evpn_configure_rd(struct bgp *bgp, struct bgpevpn *vpn,
+ struct prefix_rd *rd)
+{
+ /* If the VNI is "live", we need to delete and withdraw this VNI's
+ * local routes with the prior RD first. Then, after updating RD,
+ * need to re-advertise.
+ */
+ if (is_vni_live(vpn))
+ bgp_evpn_handle_rd_change(bgp, vpn, 1);
+
+ /* update RD */
+ memcpy(&vpn->prd, rd, sizeof(struct prefix_rd));
+ SET_FLAG(vpn->flags, VNI_FLAG_RD_CFGD);
+
+ if (is_vni_live(vpn))
+ bgp_evpn_handle_rd_change(bgp, vpn, 0);
+}
+
+/*
+ * Unconfigure RD for a VNI (vty handler)
+ */
+static void evpn_unconfigure_rd(struct bgp *bgp, struct bgpevpn *vpn)
+{
+ /* If the VNI is "live", we need to delete and withdraw this VNI's
+ * local routes with the prior RD first. Then, after resetting RD
+ * to automatic value, need to re-advertise.
+ */
+ if (is_vni_live(vpn))
+ bgp_evpn_handle_rd_change(bgp, vpn, 1);
+
+ /* reset RD to default */
+ bgp_evpn_derive_auto_rd(bgp, vpn);
+
+ if (is_vni_live(vpn))
+ bgp_evpn_handle_rd_change(bgp, vpn, 0);
+}
+
+/*
+ * Create VNI, if not already present (VTY handler). Mark as configured.
+ */
+static struct bgpevpn *evpn_create_update_vni(struct bgp *bgp, vni_t vni)
+{
+ struct bgpevpn *vpn;
+ struct in_addr mcast_grp = {INADDR_ANY};
+
+ vpn = bgp_evpn_lookup_vni(bgp, vni);
+ if (!vpn) {
+ /* Check if this L2VNI is already configured as L3VNI */
+ if (bgp_evpn_lookup_l3vni_l2vni_table(vni)) {
+ flog_err(
+ EC_BGP_VNI,
+ "%u: Failed to create L2VNI %u, it is configured as L3VNI",
+ bgp->vrf_id, vni);
+ return NULL;
+ }
+
+ /* tenant vrf will be updated when we get local_vni_add from
+ * zebra
+ */
+ vpn = bgp_evpn_new(bgp, vni, bgp->router_id, 0, mcast_grp, 0);
+ }
+
+ /* Mark as configured. */
+ SET_FLAG(vpn->flags, VNI_FLAG_CFGD);
+ return vpn;
+}
+
+/*
+ * Delete VNI. If VNI does not exist in the system (i.e., just
+ * configuration), all that is needed is to free it. Otherwise,
+ * any parameters configured for the VNI need to be reset (with
+ * appropriate action) and the VNI marked as unconfigured; the
+ * VNI will continue to exist, purely as a "learnt" entity.
+ */
+static void evpn_delete_vni(struct bgp *bgp, struct bgpevpn *vpn)
+{
+ if (!is_vni_live(vpn)) {
+ bgp_evpn_free(bgp, vpn);
+ return;
+ }
+
+ /* We need to take the unconfigure action for each parameter of this VNI
+ * that is configured. Some optimization is possible, but not worth the
+ * additional code for an operation that should be pretty rare.
+ */
+ UNSET_FLAG(vpn->flags, VNI_FLAG_CFGD);
+
+ /* First, deal with the export side - RD and export RT changes. */
+ if (is_rd_configured(vpn))
+ evpn_unconfigure_rd(bgp, vpn);
+ if (is_export_rt_configured(vpn))
+ evpn_unconfigure_export_rt(bgp, vpn, NULL);
+
+ /* Next, deal with the import side. */
+ if (is_import_rt_configured(vpn))
+ evpn_unconfigure_import_rt(bgp, vpn, NULL);
+}
+
+/*
+ * Display import RT mapping to VRFs (vty handler)
+ * bgp_evpn: evpn bgp instance
+ */
+static void evpn_show_vrf_import_rts(struct vty *vty, struct bgp *bgp_evpn,
+ json_object *json)
+{
+ void *args[2];
+
+ args[0] = vty;
+ args[1] = json;
+
+ hash_iterate(bgp_evpn->vrf_import_rt_hash,
+ (void (*)(struct hash_bucket *,
+ void *))show_vrf_import_rt_entry,
+ args);
+}
+
+/*
+ * Display import RT mapping to VNIs (vty handler)
+ */
+static void evpn_show_import_rts(struct vty *vty, struct bgp *bgp,
+ json_object *json)
+{
+ void *args[2];
+
+ args[0] = vty;
+ args[1] = json;
+
+ hash_iterate(
+ bgp->import_rt_hash,
+ (void (*)(struct hash_bucket *, void *))show_import_rt_entry,
+ args);
+}
+
+/*
+ * Display EVPN routes for all VNIs - vty handler.
+ */
+static void evpn_show_routes_vni_all(struct vty *vty, struct bgp *bgp,
+ struct in_addr vtep_ip, json_object *json,
+ int detail)
+{
+ uint32_t num_vnis;
+ struct vni_walk_ctx wctx;
+
+ num_vnis = hashcount(bgp->vnihash);
+ if (!num_vnis)
+ return;
+ memset(&wctx, 0, sizeof(wctx));
+ wctx.bgp = bgp;
+ wctx.vty = vty;
+ wctx.vtep_ip = vtep_ip;
+ wctx.json = json;
+ wctx.detail = detail;
+ hash_iterate(bgp->vnihash, (void (*)(struct hash_bucket *,
+ void *))show_vni_routes_hash,
+ &wctx);
+}
+
+/*
+ * Display EVPN routes for a VNI -- for specific type-3 route (vty handler).
+ */
+static void evpn_show_route_vni_multicast(struct vty *vty, struct bgp *bgp,
+ vni_t vni, struct in_addr orig_ip,
+ json_object *json)
+{
+ struct bgpevpn *vpn;
+ struct prefix_evpn p;
+ struct bgp_dest *dest;
+ struct bgp_path_info *pi;
+ uint32_t path_cnt = 0;
+ afi_t afi;
+ safi_t safi;
+ json_object *json_paths = NULL;
+
+ afi = AFI_L2VPN;
+ safi = SAFI_EVPN;
+
+ /* Locate VNI. */
+ vpn = bgp_evpn_lookup_vni(bgp, vni);
+ if (!vpn) {
+ vty_out(vty, "VNI not found\n");
+ return;
+ }
+
+ /* See if route exists. */
+ build_evpn_type3_prefix(&p, orig_ip);
+ dest = bgp_node_lookup(vpn->route_table, (struct prefix *)&p);
+ if (!dest || !bgp_dest_has_bgp_path_info_data(dest)) {
+ if (!json)
+ vty_out(vty, "%% Network not in table\n");
+
+ if (dest)
+ bgp_dest_unlock_node(dest);
+
+ return;
+ }
+
+ if (json)
+ json_paths = json_object_new_array();
+
+ /* Prefix and num paths displayed once per prefix. */
+ route_vty_out_detail_header(vty, bgp, dest, NULL, afi, safi, json);
+
+ /* Display each path for this prefix. */
+ for (pi = bgp_dest_get_bgp_path_info(dest); pi; pi = pi->next) {
+ json_object *json_path = NULL;
+
+ if (json)
+ json_path = json_object_new_array();
+
+ route_vty_out_detail(vty, bgp, dest, pi, afi, safi,
+ RPKI_NOT_BEING_USED, json_path);
+
+ if (json)
+ json_object_array_add(json_paths, json_path);
+
+ path_cnt++;
+ }
+
+ if (json) {
+ if (path_cnt)
+ json_object_object_add(json, "paths", json_paths);
+
+ json_object_int_add(json, "numPaths", path_cnt);
+ } else {
+ vty_out(vty, "\nDisplayed %u paths for requested prefix\n",
+ path_cnt);
+ }
+
+ bgp_dest_unlock_node(dest);
+}
+
+/*
+ * Display EVPN routes for a VNI -- for specific MAC and/or IP (vty handler).
+ * By definition, only matching type-2 route will be displayed.
+ */
+static void evpn_show_route_vni_macip(struct vty *vty, struct bgp *bgp,
+ vni_t vni, struct ethaddr *mac,
+ struct ipaddr *ip, json_object *json)
+{
+ struct bgpevpn *vpn;
+ struct prefix_evpn p;
+ struct bgp_dest *dest;
+ struct bgp_path_info *pi;
+ uint32_t path_cnt = 0;
+ afi_t afi;
+ safi_t safi;
+ json_object *json_paths = NULL;
+
+ afi = AFI_L2VPN;
+ safi = SAFI_EVPN;
+
+ /* Locate VNI. */
+ vpn = bgp_evpn_lookup_vni(bgp, vni);
+ if (!vpn) {
+ if (!json)
+ vty_out(vty, "VNI not found\n");
+ return;
+ }
+
+ /* See if route exists. Look for both non-sticky and sticky. */
+ build_evpn_type2_prefix(&p, mac, ip);
+ dest = bgp_node_lookup(vpn->route_table, (struct prefix *)&p);
+ if (!dest || !bgp_dest_has_bgp_path_info_data(dest)) {
+ if (!json)
+ vty_out(vty, "%% Network not in table\n");
+
+ if (dest)
+ bgp_dest_unlock_node(dest);
+
+ return;
+ }
+
+ if (json)
+ json_paths = json_object_new_array();
+
+ /* Prefix and num paths displayed once per prefix. */
+ route_vty_out_detail_header(vty, bgp, dest, NULL, afi, safi, json);
+
+ /* Display each path for this prefix. */
+ for (pi = bgp_dest_get_bgp_path_info(dest); pi; pi = pi->next) {
+ json_object *json_path = NULL;
+
+ if (json)
+ json_path = json_object_new_array();
+
+ route_vty_out_detail(vty, bgp, dest, pi, afi, safi,
+ RPKI_NOT_BEING_USED, json_path);
+
+ if (json)
+ json_object_array_add(json_paths, json_path);
+
+ path_cnt++;
+ }
+
+ if (json) {
+ if (path_cnt)
+ json_object_object_add(json, "paths", json_paths);
+
+ json_object_int_add(json, "numPaths", path_cnt);
+ } else {
+ vty_out(vty, "\nDisplayed %u paths for requested prefix\n",
+ path_cnt);
+ }
+
+ bgp_dest_unlock_node(dest);
+}
+
+/* Disaplay EVPN routes for a ESI - VTY handler */
+static void evpn_show_routes_esi(struct vty *vty, struct bgp *bgp,
+ esi_t *esi, json_object *json)
+{
+ struct bgp_evpn_es *es = NULL;
+
+ /* locate the ES */
+ es = bgp_evpn_es_find(esi);
+ if (!es) {
+ if (!json)
+ vty_out(vty, "ESI not found\n");
+ return;
+ }
+
+ show_esi_routes(bgp, es, vty, json);
+}
+
+/*
+ * Display EVPN routes for a VNI - vty handler.
+ * If 'type' is non-zero, only routes matching that type are shown.
+ * If the vtep_ip is non zero, only routes behind that vtep are shown
+ */
+static void evpn_show_routes_vni(struct vty *vty, struct bgp *bgp, vni_t vni,
+ int type, struct in_addr vtep_ip,
+ json_object *json)
+{
+ struct bgpevpn *vpn;
+
+ /* Locate VNI. */
+ vpn = bgp_evpn_lookup_vni(bgp, vni);
+ if (!vpn) {
+ if (!json)
+ vty_out(vty, "VNI not found\n");
+ return;
+ }
+
+ /* Walk this VNI's route table and display appropriate routes. */
+ show_vni_routes(bgp, vpn, type, vty, vtep_ip, json, 0);
+}
+
+/*
+ * Display BGP EVPN routing table -- for specific RD and MAC and/or
+ * IP (vty handler). By definition, only matching type-2 route will be
+ * displayed.
+ */
+static void evpn_show_route_rd_macip(struct vty *vty, struct bgp *bgp,
+ struct prefix_rd *prd, struct ethaddr *mac,
+ struct ipaddr *ip, json_object *json)
+{
+ struct prefix_evpn p;
+ struct bgp_dest *dest;
+ struct bgp_path_info *pi;
+ afi_t afi;
+ safi_t safi;
+ uint32_t path_cnt = 0;
+ json_object *json_paths = NULL;
+
+ afi = AFI_L2VPN;
+ safi = SAFI_EVPN;
+
+ /* See if route exists. Look for both non-sticky and sticky. */
+ build_evpn_type2_prefix(&p, mac, ip);
+ dest = bgp_afi_node_lookup(bgp->rib[afi][safi], afi, safi,
+ (struct prefix *)&p, prd);
+ if (!dest || !bgp_dest_has_bgp_path_info_data(dest)) {
+ if (!json)
+ vty_out(vty, "%% Network not in table\n");
+
+ if (dest)
+ bgp_dest_unlock_node(dest);
+
+ return;
+ }
+
+ /* Prefix and num paths displayed once per prefix. */
+ route_vty_out_detail_header(vty, bgp, dest, prd, afi, safi, json);
+
+ if (json)
+ json_paths = json_object_new_array();
+
+ /* Display each path for this prefix. */
+ for (pi = bgp_dest_get_bgp_path_info(dest); pi; pi = pi->next) {
+ json_object *json_path = NULL;
+
+ if (json)
+ json_path = json_object_new_array();
+
+ route_vty_out_detail(vty, bgp, dest, pi, afi, safi,
+ RPKI_NOT_BEING_USED, json_path);
+
+ if (json)
+ json_object_array_add(json_paths, json_path);
+
+ path_cnt++;
+ }
+
+ if (json && path_cnt) {
+ if (path_cnt)
+ json_object_object_addf(json, json_paths, "%pFX", &p);
+ json_object_int_add(json, "numPaths", path_cnt);
+ } else {
+ vty_out(vty, "\nDisplayed %u paths for requested prefix\n",
+ path_cnt);
+ }
+
+ bgp_dest_unlock_node(dest);
+}
+
+/*
+ * Display BGP EVPN routing table -- for specific RD (vty handler)
+ * If 'type' is non-zero, only routes matching that type are shown.
+ */
+static void evpn_show_route_rd(struct vty *vty, struct bgp *bgp,
+ struct prefix_rd *prd, int type,
+ json_object *json)
+{
+ struct bgp_dest *rd_dest;
+ struct bgp_table *table;
+ struct bgp_dest *dest;
+ struct bgp_path_info *pi;
+ int rd_header = 1;
+ afi_t afi;
+ safi_t safi;
+ uint32_t prefix_cnt, path_cnt;
+ json_object *json_rd = NULL;
+ int add_rd_to_json = 0;
+
+ afi = AFI_L2VPN;
+ safi = SAFI_EVPN;
+ prefix_cnt = path_cnt = 0;
+
+ rd_dest = bgp_node_lookup(bgp->rib[afi][safi], (struct prefix *)prd);
+ if (!rd_dest)
+ return;
+
+ table = bgp_dest_get_bgp_table_info(rd_dest);
+ if (table == NULL) {
+ bgp_dest_unlock_node(rd_dest);
+ return;
+ }
+
+ if (json) {
+ json_rd = json_object_new_object();
+ json_object_string_addf(json_rd, "rd", "%pRD", prd);
+ }
+
+ bgp_dest_unlock_node(rd_dest);
+
+ /* Display all prefixes with this RD. */
+ for (dest = bgp_table_top(table); dest; dest = bgp_route_next(dest)) {
+ const struct prefix_evpn *evp =
+ (const struct prefix_evpn *)bgp_dest_get_prefix(dest);
+ json_object *json_prefix = NULL;
+ json_object *json_paths = NULL;
+ int add_prefix_to_json = 0;
+
+ if (type && evp->prefix.route_type != type)
+ continue;
+
+ if (json)
+ json_prefix = json_object_new_object();
+
+ pi = bgp_dest_get_bgp_path_info(dest);
+ if (pi) {
+ /* RD header and legend - once overall. */
+ if (rd_header && !json) {
+ vty_out(vty,
+ "EVPN type-1 prefix: [1]:[EthTag]:[ESI]:[IPlen]:[VTEP-IP]:[Frag-id]\n");
+ vty_out(vty,
+ "EVPN type-2 prefix: [2]:[EthTag]:[MAClen]:[MAC]\n");
+ vty_out(vty,
+ "EVPN type-3 prefix: [3]:[EthTag]:[IPlen]:[OrigIP]\n");
+ vty_out(vty,
+ "EVPN type-4 prefix: [4]:[ESI]:[IPlen]:[OrigIP]\n");
+ vty_out(vty,
+ "EVPN type-5 prefix: [5]:[EthTag]:[IPlen]:[IP]\n\n");
+ rd_header = 0;
+ }
+
+ /* Prefix and num paths displayed once per prefix. */
+ route_vty_out_detail_header(vty, bgp, dest, prd, afi,
+ safi, json_prefix);
+
+ prefix_cnt++;
+ }
+
+ if (json)
+ json_paths = json_object_new_array();
+
+ /* Display each path for this prefix. */
+ for (; pi; pi = pi->next) {
+ json_object *json_path = NULL;
+
+ if (json)
+ json_path = json_object_new_array();
+
+ route_vty_out_detail(vty, bgp, dest, pi, afi, safi,
+ RPKI_NOT_BEING_USED, json_path);
+
+ if (json)
+ json_object_array_add(json_paths, json_path);
+
+ path_cnt++;
+ add_prefix_to_json = 1;
+ add_rd_to_json = 1;
+ }
+
+ if (json) {
+ if (add_prefix_to_json) {
+ json_object_object_add(json_prefix, "paths",
+ json_paths);
+ json_object_object_addf(json_rd, json_prefix,
+ "%pFX", evp);
+ } else {
+ json_object_free(json_paths);
+ json_object_free(json_prefix);
+ json_paths = NULL;
+ json_prefix = NULL;
+ }
+ }
+ }
+
+ if (json) {
+ if (add_rd_to_json)
+ json_object_object_addf(json, json_rd, "%pRD", prd);
+ else {
+ json_object_free(json_rd);
+ json_rd = NULL;
+ }
+
+ json_object_int_add(json, "numPrefix", prefix_cnt);
+ json_object_int_add(json, "numPaths", path_cnt);
+ } else {
+ if (prefix_cnt == 0)
+ vty_out(vty, "No prefixes exist with this RD%s\n",
+ type ? " (of requested type)" : "");
+ else
+ vty_out(vty,
+ "\nDisplayed %u prefixes (%u paths) with this RD%s\n",
+ prefix_cnt, path_cnt,
+ type ? " (of requested type)" : "");
+ }
+}
+
+/*
+ * Display BGP EVPN routing table -- all RDs and MAC and/or IP
+ * (vty handler). Only matching type-2 routes will be displayed.
+ */
+static void evpn_show_route_rd_all_macip(struct vty *vty, struct bgp *bgp,
+ struct ethaddr *mac, struct ipaddr *ip,
+ json_object *json)
+{
+ struct bgp_dest *rd_dest;
+ struct bgp_table *table;
+ struct bgp_dest *dest;
+ struct bgp_path_info *pi;
+ afi_t afi = AFI_L2VPN;
+ safi_t safi = SAFI_EVPN;
+ uint32_t prefix_cnt, path_cnt;
+ prefix_cnt = path_cnt = 0;
+
+ /* EVPN routing table is a 2-level table with the first level being
+ * the RD. We need to look in every RD we know about.
+ */
+ for (rd_dest = bgp_table_top(bgp->rib[afi][safi]); rd_dest;
+ rd_dest = bgp_route_next(rd_dest)) {
+ json_object *json_paths = NULL; /* paths array for prefix */
+ json_object *json_prefix = NULL; /* prefix within an RD */
+ json_object *json_rd = NULL; /* holds all prefixes for RD */
+ char rd_str[RD_ADDRSTRLEN];
+ int add_rd_to_json = 0;
+ struct prefix_evpn ep;
+ const struct prefix *rd_destp = bgp_dest_get_prefix(rd_dest);
+
+ table = bgp_dest_get_bgp_table_info(rd_dest);
+ if (table == NULL)
+ continue;
+
+ prefix_rd2str((struct prefix_rd *)rd_destp, rd_str,
+ sizeof(rd_str));
+
+ /* Construct an RT-2 from the user-supplied mac(ip),
+ * then search the l2vpn evpn table for it.
+ */
+ build_evpn_type2_prefix(&ep, mac, ip);
+ dest = bgp_afi_node_lookup(bgp->rib[afi][safi], afi, safi,
+ (struct prefix *)&ep,
+ (struct prefix_rd *)rd_destp);
+ if (!dest)
+ continue;
+
+ if (json)
+ json_rd = json_object_new_object();
+
+ const struct prefix *p = bgp_dest_get_prefix(dest);
+
+ pi = bgp_dest_get_bgp_path_info(dest);
+ if (pi) {
+ /* RD header - per RD. */
+ bgp_evpn_show_route_rd_header(vty, rd_dest, json_rd,
+ rd_str, RD_ADDRSTRLEN);
+ prefix_cnt++;
+ }
+
+ if (json) {
+ json_prefix = json_object_new_object();
+ json_paths = json_object_new_array();
+ json_object_string_addf(json_prefix, "prefix", "%pFX",
+ p);
+ json_object_int_add(json_prefix, "prefixLen",
+ p->prefixlen);
+ } else
+ /* Prefix and num paths displayed once per prefix. */
+ route_vty_out_detail_header(
+ vty, bgp, dest, (struct prefix_rd *)rd_destp,
+ AFI_L2VPN, SAFI_EVPN, json_prefix);
+
+ /* For EVPN, the prefix is displayed for each path (to
+ * fit in with code that already exists).
+ */
+ for (; pi; pi = pi->next) {
+ json_object *json_path = NULL;
+
+ add_rd_to_json = 1;
+ path_cnt++;
+
+ if (json)
+ json_path = json_object_new_array();
+
+ route_vty_out_detail(vty, bgp, dest, pi, AFI_L2VPN,
+ SAFI_EVPN, RPKI_NOT_BEING_USED,
+ json_path);
+
+ if (json)
+ json_object_array_add(json_paths, json_path);
+ else
+ vty_out(vty, "\n");
+ }
+
+ if (json) {
+ json_object_object_add(json_prefix, "paths",
+ json_paths);
+ json_object_object_addf(json_rd, json_prefix, "%pFX",
+ p);
+ if (add_rd_to_json)
+ json_object_object_add(json, rd_str, json_rd);
+ else {
+ json_object_free(json_rd);
+ json_rd = NULL;
+ }
+ }
+
+ bgp_dest_unlock_node(dest);
+ }
+
+ if (json) {
+ json_object_int_add(json, "numPrefix", prefix_cnt);
+ json_object_int_add(json, "numPaths", path_cnt);
+ } else {
+ if (prefix_cnt == 0) {
+ vty_out(vty, "No Matching EVPN prefixes exist\n");
+ } else {
+ vty_out(vty, "Displayed %u prefixes (%u paths)\n",
+ prefix_cnt, path_cnt);
+ }
+ }
+}
+
+/*
+ * Display BGP EVPN routing table - all routes (vty handler).
+ * If 'type' is non-zero, only routes matching that type are shown.
+ */
+static void evpn_show_all_routes(struct vty *vty, struct bgp *bgp, int type,
+ json_object *json, int detail)
+{
+ struct bgp_dest *rd_dest;
+ struct bgp_table *table;
+ struct bgp_dest *dest;
+ struct bgp_path_info *pi;
+ int header = detail ? 0 : 1;
+ int rd_header;
+ afi_t afi;
+ safi_t safi;
+ uint32_t prefix_cnt, path_cnt;
+
+ afi = AFI_L2VPN;
+ safi = SAFI_EVPN;
+ prefix_cnt = path_cnt = 0;
+
+ /* EVPN routing table is a 2-level table with the first level being
+ * the RD.
+ */
+ for (rd_dest = bgp_table_top(bgp->rib[afi][safi]); rd_dest;
+ rd_dest = bgp_route_next(rd_dest)) {
+ char rd_str[RD_ADDRSTRLEN];
+ json_object *json_rd = NULL; /* contains routes for an RD */
+ int add_rd_to_json = 0;
+ uint64_t tbl_ver;
+ const struct prefix *rd_destp = bgp_dest_get_prefix(rd_dest);
+
+ table = bgp_dest_get_bgp_table_info(rd_dest);
+ if (table == NULL)
+ continue;
+
+ tbl_ver = table->version;
+ prefix_rd2str((struct prefix_rd *)rd_destp, rd_str,
+ sizeof(rd_str));
+
+ if (json)
+ json_rd = json_object_new_object();
+
+ rd_header = 1;
+
+ /* Display all prefixes for an RD */
+ for (dest = bgp_table_top(table); dest;
+ dest = bgp_route_next(dest)) {
+ json_object *json_prefix =
+ NULL; /* contains prefix under a RD */
+ json_object *json_paths =
+ NULL; /* array of paths under a prefix*/
+ const struct prefix_evpn *evp =
+ (const struct prefix_evpn *)bgp_dest_get_prefix(
+ dest);
+ int add_prefix_to_json = 0;
+ const struct prefix *p = bgp_dest_get_prefix(dest);
+
+ if (type && evp->prefix.route_type != type)
+ continue;
+
+ pi = bgp_dest_get_bgp_path_info(dest);
+ if (pi) {
+ /* Overall header/legend displayed once. */
+ if (header) {
+ bgp_evpn_show_route_header(vty, bgp,
+ tbl_ver,
+ json);
+ if (!json)
+ vty_out(vty,
+ "%19s Extended Community\n"
+ , " ");
+ header = 0;
+ }
+
+ /* RD header - per RD. */
+ if (rd_header) {
+ bgp_evpn_show_route_rd_header(
+ vty, rd_dest, json_rd, rd_str,
+ RD_ADDRSTRLEN);
+ rd_header = 0;
+ }
+
+ prefix_cnt++;
+ }
+
+ if (json) {
+ json_prefix = json_object_new_object();
+ json_paths = json_object_new_array();
+ json_object_string_addf(json_prefix, "prefix",
+ "%pFX", p);
+ json_object_int_add(json_prefix, "prefixLen",
+ p->prefixlen);
+ }
+
+ /* Prefix and num paths displayed once per prefix. */
+ if (detail)
+ route_vty_out_detail_header(
+ vty, bgp, dest,
+ (struct prefix_rd *)rd_destp, AFI_L2VPN,
+ SAFI_EVPN, json_prefix);
+
+ /* For EVPN, the prefix is displayed for each path (to
+ * fit in
+ * with code that already exists).
+ */
+ for (; pi; pi = pi->next) {
+ json_object *json_path = NULL;
+
+ path_cnt++;
+ add_prefix_to_json = 1;
+ add_rd_to_json = 1;
+
+ if (json)
+ json_path = json_object_new_array();
+
+ if (detail) {
+ route_vty_out_detail(
+ vty, bgp, dest, pi, AFI_L2VPN,
+ SAFI_EVPN, RPKI_NOT_BEING_USED,
+ json_path);
+ } else
+ route_vty_out(vty, p, pi, 0, SAFI_EVPN,
+ json_path, false);
+
+ if (json)
+ json_object_array_add(json_paths,
+ json_path);
+ }
+
+ if (json) {
+ if (add_prefix_to_json) {
+ json_object_object_add(json_prefix,
+ "paths",
+ json_paths);
+ json_object_object_addf(json_rd,
+ json_prefix,
+ "%pFX", p);
+ } else {
+ json_object_free(json_prefix);
+ json_object_free(json_paths);
+ json_prefix = NULL;
+ json_paths = NULL;
+ }
+ }
+ }
+
+ if (json) {
+ if (add_rd_to_json)
+ json_object_object_add(json, rd_str, json_rd);
+ else {
+ json_object_free(json_rd);
+ json_rd = NULL;
+ }
+ }
+ }
+
+ if (json) {
+ json_object_int_add(json, "numPrefix", prefix_cnt);
+ json_object_int_add(json, "numPaths", path_cnt);
+ } else {
+ if (prefix_cnt == 0) {
+ vty_out(vty, "No EVPN prefixes %sexist\n",
+ type ? "(of requested type) " : "");
+ } else {
+ vty_out(vty, "\nDisplayed %u prefixes (%u paths)%s\n",
+ prefix_cnt, path_cnt,
+ type ? " (of requested type)" : "");
+ }
+ }
+}
+
+/*
+ * Display specified VNI (vty handler)
+ */
+static void evpn_show_vni(struct vty *vty, struct bgp *bgp, vni_t vni,
+ json_object *json)
+{
+ uint8_t found = 0;
+ struct bgpevpn *vpn;
+
+ vpn = bgp_evpn_lookup_vni(bgp, vni);
+ if (vpn) {
+ found = 1;
+ display_vni(vty, vpn, json);
+ } else {
+ struct bgp *bgp_temp;
+ struct listnode *node = NULL;
+
+ for (ALL_LIST_ELEMENTS_RO(bm->bgp, node, bgp_temp)) {
+ if (bgp_temp->l3vni == vni) {
+ found = 1;
+ display_l3vni(vty, bgp_temp, json);
+ }
+ }
+ }
+
+ if (!found) {
+ if (json) {
+ vty_out(vty, "{}\n");
+ } else {
+ vty_out(vty, "VNI not found\n");
+ return;
+ }
+ }
+}
+
+/*
+ * Display a VNI (upon user query).
+ */
+static void evpn_show_all_vnis(struct vty *vty, struct bgp *bgp,
+ json_object *json)
+{
+ void *args[2];
+ struct bgp *bgp_temp = NULL;
+ struct listnode *node;
+
+
+ if (!json) {
+ vty_out(vty, "Flags: * - Kernel\n");
+ vty_out(vty, " %-10s %-4s %-21s %-25s %-25s %-37s\n", "VNI",
+ "Type", "RD", "Import RT", "Export RT", "Tenant VRF");
+ }
+
+ /* print all L2 VNIS */
+ args[0] = vty;
+ args[1] = json;
+ hash_iterate(bgp->vnihash,
+ (void (*)(struct hash_bucket *, void *))show_vni_entry,
+ args);
+
+ /* print all L3 VNIs */
+ for (ALL_LIST_ELEMENTS_RO(bm->bgp, node, bgp_temp))
+ show_l3vni_entry(vty, bgp_temp, json);
+}
+
+/*
+ * evpn - enable advertisement of svi MAC-IP
+ */
+static void evpn_set_advertise_svi_macip(struct bgp *bgp, struct bgpevpn *vpn,
+ uint32_t set)
+{
+ if (!vpn) {
+ if (set && bgp->evpn_info->advertise_svi_macip)
+ return;
+ else if (!set && !bgp->evpn_info->advertise_svi_macip)
+ return;
+
+ bgp->evpn_info->advertise_svi_macip = set;
+ bgp_zebra_advertise_svi_macip(bgp,
+ bgp->evpn_info->advertise_svi_macip, 0);
+ } else {
+ if (set && vpn->advertise_svi_macip)
+ return;
+ else if (!set && !vpn->advertise_svi_macip)
+ return;
+
+ vpn->advertise_svi_macip = set;
+ bgp_zebra_advertise_svi_macip(bgp, vpn->advertise_svi_macip,
+ vpn->vni);
+ }
+}
+
+/*
+ * evpn - enable advertisement of default g/w
+ */
+static void evpn_set_advertise_default_gw(struct bgp *bgp, struct bgpevpn *vpn)
+{
+ if (!vpn) {
+ if (bgp->advertise_gw_macip)
+ return;
+
+ bgp->advertise_gw_macip = 1;
+ bgp_zebra_advertise_gw_macip(bgp, bgp->advertise_gw_macip, 0);
+ } else {
+ if (vpn->advertise_gw_macip)
+ return;
+
+ vpn->advertise_gw_macip = 1;
+ bgp_zebra_advertise_gw_macip(bgp, vpn->advertise_gw_macip,
+ vpn->vni);
+ }
+ return;
+}
+
+/*
+ * evpn - disable advertisement of default g/w
+ */
+static void evpn_unset_advertise_default_gw(struct bgp *bgp,
+ struct bgpevpn *vpn)
+{
+ if (!vpn) {
+ if (!bgp->advertise_gw_macip)
+ return;
+
+ bgp->advertise_gw_macip = 0;
+ bgp_zebra_advertise_gw_macip(bgp, bgp->advertise_gw_macip, 0);
+ } else {
+ if (!vpn->advertise_gw_macip)
+ return;
+
+ vpn->advertise_gw_macip = 0;
+ bgp_zebra_advertise_gw_macip(bgp, vpn->advertise_gw_macip,
+ vpn->vni);
+ }
+ return;
+}
+
+/*
+ * evpn - enable advertisement of default g/w
+ */
+static void evpn_process_default_originate_cmd(struct bgp *bgp_vrf,
+ afi_t afi, bool add)
+{
+ safi_t safi = SAFI_UNICAST; /* ipv4/ipv6 unicast */
+
+ if (add) {
+ /* bail if we are already advertising default route */
+ if (evpn_default_originate_set(bgp_vrf, afi, safi))
+ return;
+
+ if (afi == AFI_IP)
+ SET_FLAG(bgp_vrf->af_flags[AFI_L2VPN][SAFI_EVPN],
+ BGP_L2VPN_EVPN_DEFAULT_ORIGINATE_IPV4);
+ else if (afi == AFI_IP6)
+ SET_FLAG(bgp_vrf->af_flags[AFI_L2VPN][SAFI_EVPN],
+ BGP_L2VPN_EVPN_DEFAULT_ORIGINATE_IPV6);
+ } else {
+ /* bail out if we havent advertised the default route */
+ if (!evpn_default_originate_set(bgp_vrf, afi, safi))
+ return;
+ if (afi == AFI_IP)
+ UNSET_FLAG(bgp_vrf->af_flags[AFI_L2VPN][SAFI_EVPN],
+ BGP_L2VPN_EVPN_DEFAULT_ORIGINATE_IPV4);
+ else if (afi == AFI_IP6)
+ UNSET_FLAG(bgp_vrf->af_flags[AFI_L2VPN][SAFI_EVPN],
+ BGP_L2VPN_EVPN_DEFAULT_ORIGINATE_IPV6);
+ }
+
+ bgp_evpn_install_uninstall_default_route(bgp_vrf, afi, safi, add);
+}
+
+/*
+ * evpn - enable advertisement of default g/w
+ */
+static void evpn_set_advertise_subnet(struct bgp *bgp,
+ struct bgpevpn *vpn)
+{
+ if (vpn->advertise_subnet)
+ return;
+
+ vpn->advertise_subnet = 1;
+ bgp_zebra_advertise_subnet(bgp, vpn->advertise_subnet, vpn->vni);
+}
+
+/*
+ * evpn - disable advertisement of default g/w
+ */
+static void evpn_unset_advertise_subnet(struct bgp *bgp, struct bgpevpn *vpn)
+{
+ if (!vpn->advertise_subnet)
+ return;
+
+ vpn->advertise_subnet = 0;
+ bgp_zebra_advertise_subnet(bgp, vpn->advertise_subnet, vpn->vni);
+}
+
+/*
+ * EVPN (VNI advertisement) enabled. Register with zebra.
+ */
+static void evpn_set_advertise_all_vni(struct bgp *bgp)
+{
+ bgp->advertise_all_vni = 1;
+ bgp_set_evpn(bgp);
+ bgp_zebra_advertise_all_vni(bgp, bgp->advertise_all_vni);
+}
+
+/*
+ * EVPN (VNI advertisement) disabled. De-register with zebra. Cleanup VNI
+ * cache, EVPN routes (delete and withdraw from peers).
+ */
+static void evpn_unset_advertise_all_vni(struct bgp *bgp)
+{
+ bgp->advertise_all_vni = 0;
+ bgp_set_evpn(bgp_get_default());
+ bgp_zebra_advertise_all_vni(bgp, bgp->advertise_all_vni);
+ bgp_evpn_cleanup_on_disable(bgp);
+}
+
+/* Set resolve overlay index flag */
+static void bgp_evpn_set_unset_resolve_overlay_index(struct bgp *bgp, bool set)
+{
+ if (set == bgp->resolve_overlay_index)
+ return;
+
+ if (set) {
+ bgp->resolve_overlay_index = true;
+ hash_iterate(bgp->vnihash,
+ (void (*)(struct hash_bucket *, void *))
+ bgp_evpn_handle_resolve_overlay_index_set,
+ NULL);
+ } else {
+ hash_iterate(
+ bgp->vnihash,
+ (void (*)(struct hash_bucket *, void *))
+ bgp_evpn_handle_resolve_overlay_index_unset,
+ NULL);
+ bgp->resolve_overlay_index = false;
+ }
+}
+
+/*
+ * EVPN - use RFC8365 to auto-derive RT
+ */
+static void evpn_set_advertise_autort_rfc8365(struct bgp *bgp)
+{
+ bgp->advertise_autort_rfc8365 = 1;
+ bgp_evpn_handle_autort_change(bgp);
+}
+
+/*
+ * EVPN - don't use RFC8365 to auto-derive RT
+ */
+static void evpn_unset_advertise_autort_rfc8365(struct bgp *bgp)
+{
+ bgp->advertise_autort_rfc8365 = 0;
+ bgp_evpn_handle_autort_change(bgp);
+}
+
+static void write_vni_config(struct vty *vty, struct bgpevpn *vpn)
+{
+ char *ecom_str;
+ struct listnode *node, *nnode;
+ struct ecommunity *ecom;
+
+ if (is_vni_configured(vpn)) {
+ vty_out(vty, " vni %d\n", vpn->vni);
+ if (is_rd_configured(vpn))
+ vty_out(vty, " rd %pRD\n", &vpn->prd);
+
+ if (is_import_rt_configured(vpn)) {
+ for (ALL_LIST_ELEMENTS(vpn->import_rtl, node, nnode,
+ ecom)) {
+ ecom_str = ecommunity_ecom2str(
+ ecom, ECOMMUNITY_FORMAT_ROUTE_MAP, 0);
+ vty_out(vty, " route-target import %s\n",
+ ecom_str);
+ XFREE(MTYPE_ECOMMUNITY_STR, ecom_str);
+ }
+ }
+
+ if (is_export_rt_configured(vpn)) {
+ for (ALL_LIST_ELEMENTS(vpn->export_rtl, node, nnode,
+ ecom)) {
+ ecom_str = ecommunity_ecom2str(
+ ecom, ECOMMUNITY_FORMAT_ROUTE_MAP, 0);
+ vty_out(vty, " route-target export %s\n",
+ ecom_str);
+ XFREE(MTYPE_ECOMMUNITY_STR, ecom_str);
+ }
+ }
+
+ if (vpn->advertise_gw_macip)
+ vty_out(vty, " advertise-default-gw\n");
+
+ if (vpn->advertise_svi_macip)
+ vty_out(vty, " advertise-svi-ip\n");
+
+ if (vpn->advertise_subnet)
+ vty_out(vty, " advertise-subnet\n");
+
+ vty_out(vty, " exit-vni\n");
+ }
+}
+
+#ifndef VTYSH_EXTRACT_PL
+#include "bgpd/bgp_evpn_vty_clippy.c"
+#endif
+
+DEFPY(bgp_evpn_flood_control,
+ bgp_evpn_flood_control_cmd,
+ "[no$no] flooding <disable$disable|head-end-replication$her>",
+ NO_STR
+ "Specify handling for BUM packets\n"
+ "Do not flood any BUM packets\n"
+ "Flood BUM packets using head-end replication\n")
+{
+ struct bgp *bgp = VTY_GET_CONTEXT(bgp);
+ enum vxlan_flood_control flood_ctrl;
+
+ if (!bgp)
+ return CMD_WARNING;
+
+ if (disable && !no)
+ flood_ctrl = VXLAN_FLOOD_DISABLED;
+ else if (her || no)
+ flood_ctrl = VXLAN_FLOOD_HEAD_END_REPL;
+ else
+ return CMD_WARNING;
+
+ if (bgp->vxlan_flood_ctrl == flood_ctrl)
+ return CMD_SUCCESS;
+
+ bgp->vxlan_flood_ctrl = flood_ctrl;
+ bgp_evpn_flood_control_change(bgp);
+
+ return CMD_SUCCESS;
+}
+
+DEFUN (bgp_evpn_advertise_default_gw_vni,
+ bgp_evpn_advertise_default_gw_vni_cmd,
+ "advertise-default-gw",
+ "Advertise default g/w mac-ip routes in EVPN for a VNI\n")
+{
+ struct bgp *bgp = VTY_GET_CONTEXT(bgp);
+ VTY_DECLVAR_CONTEXT_SUB(bgpevpn, vpn);
+
+ if (!bgp)
+ return CMD_WARNING;
+
+ evpn_set_advertise_default_gw(bgp, vpn);
+
+ return CMD_SUCCESS;
+}
+
+DEFUN (no_bgp_evpn_advertise_default_vni_gw,
+ no_bgp_evpn_advertise_default_gw_vni_cmd,
+ "no advertise-default-gw",
+ NO_STR
+ "Withdraw default g/w mac-ip routes from EVPN for a VNI\n")
+{
+ struct bgp *bgp = VTY_GET_CONTEXT(bgp);
+ VTY_DECLVAR_CONTEXT_SUB(bgpevpn, vpn);
+
+ if (!bgp)
+ return CMD_WARNING;
+
+ evpn_unset_advertise_default_gw(bgp, vpn);
+
+ return CMD_SUCCESS;
+}
+
+
+DEFUN (bgp_evpn_advertise_default_gw,
+ bgp_evpn_advertise_default_gw_cmd,
+ "advertise-default-gw",
+ "Advertise All default g/w mac-ip routes in EVPN\n")
+{
+ struct bgp *bgp = VTY_GET_CONTEXT(bgp);
+
+ if (!bgp)
+ return CMD_WARNING;
+
+ if (!EVPN_ENABLED(bgp)) {
+ vty_out(vty,
+ "This command is only supported under the EVPN VRF\n");
+ return CMD_WARNING;
+ }
+
+ evpn_set_advertise_default_gw(bgp, NULL);
+
+ return CMD_SUCCESS;
+}
+
+DEFUN (no_bgp_evpn_advertise_default_gw,
+ no_bgp_evpn_advertise_default_gw_cmd,
+ "no advertise-default-gw",
+ NO_STR
+ "Withdraw All default g/w mac-ip routes from EVPN\n")
+{
+ struct bgp *bgp = VTY_GET_CONTEXT(bgp);
+
+ if (!bgp)
+ return CMD_WARNING;
+
+ evpn_unset_advertise_default_gw(bgp, NULL);
+
+ return CMD_SUCCESS;
+}
+
+DEFUN (bgp_evpn_advertise_all_vni,
+ bgp_evpn_advertise_all_vni_cmd,
+ "advertise-all-vni",
+ "Advertise All local VNIs\n")
+{
+ struct bgp *bgp = VTY_GET_CONTEXT(bgp);
+ struct bgp *bgp_evpn = NULL;
+
+ if (!bgp)
+ return CMD_WARNING;
+
+ bgp_evpn = bgp_get_evpn();
+ if (bgp_evpn && bgp_evpn != bgp) {
+ vty_out(vty, "%% Please unconfigure EVPN in %s\n",
+ bgp_evpn->name_pretty);
+ return CMD_WARNING_CONFIG_FAILED;
+ }
+
+ evpn_set_advertise_all_vni(bgp);
+ return CMD_SUCCESS;
+}
+
+DEFUN (no_bgp_evpn_advertise_all_vni,
+ no_bgp_evpn_advertise_all_vni_cmd,
+ "no advertise-all-vni",
+ NO_STR
+ "Advertise All local VNIs\n")
+{
+ struct bgp *bgp = VTY_GET_CONTEXT(bgp);
+
+ if (!bgp)
+ return CMD_WARNING;
+ evpn_unset_advertise_all_vni(bgp);
+ return CMD_SUCCESS;
+}
+
+DEFUN (bgp_evpn_advertise_autort_rfc8365,
+ bgp_evpn_advertise_autort_rfc8365_cmd,
+ "autort rfc8365-compatible",
+ "Auto-derivation of RT\n"
+ "Auto-derivation of RT using RFC8365\n")
+{
+ struct bgp *bgp = VTY_GET_CONTEXT(bgp);
+
+ if (!bgp)
+ return CMD_WARNING;
+ evpn_set_advertise_autort_rfc8365(bgp);
+ return CMD_SUCCESS;
+}
+
+DEFUN (no_bgp_evpn_advertise_autort_rfc8365,
+ no_bgp_evpn_advertise_autort_rfc8365_cmd,
+ "no autort rfc8365-compatible",
+ NO_STR
+ "Auto-derivation of RT\n"
+ "Auto-derivation of RT using RFC8365\n")
+{
+ struct bgp *bgp = VTY_GET_CONTEXT(bgp);
+
+ if (!bgp)
+ return CMD_WARNING;
+ evpn_unset_advertise_autort_rfc8365(bgp);
+ return CMD_SUCCESS;
+}
+
+DEFUN (bgp_evpn_default_originate,
+ bgp_evpn_default_originate_cmd,
+ "default-originate <ipv4 | ipv6>",
+ "originate a default route\n"
+ "ipv4 address family\n"
+ "ipv6 address family\n")
+{
+ afi_t afi = 0;
+ int idx_afi = 0;
+ struct bgp *bgp_vrf = VTY_GET_CONTEXT(bgp);
+
+ if (!bgp_vrf)
+ return CMD_WARNING;
+ argv_find_and_parse_afi(argv, argc, &idx_afi, &afi);
+ evpn_process_default_originate_cmd(bgp_vrf, afi, true);
+ return CMD_SUCCESS;
+}
+
+DEFUN (no_bgp_evpn_default_originate,
+ no_bgp_evpn_default_originate_cmd,
+ "no default-originate <ipv4 | ipv6>",
+ NO_STR
+ "withdraw a default route\n"
+ "ipv4 address family\n"
+ "ipv6 address family\n")
+{
+ afi_t afi = 0;
+ int idx_afi = 0;
+ struct bgp *bgp_vrf = VTY_GET_CONTEXT(bgp);
+
+ if (!bgp_vrf)
+ return CMD_WARNING;
+ argv_find_and_parse_afi(argv, argc, &idx_afi, &afi);
+ evpn_process_default_originate_cmd(bgp_vrf, afi, false);
+ return CMD_SUCCESS;
+}
+
+DEFPY (dup_addr_detection,
+ dup_addr_detection_cmd,
+ "dup-addr-detection [max-moves (2-1000)$max_moves_val time (2-1800)$time_val]",
+ "Duplicate address detection\n"
+ "Max allowed moves before address detected as duplicate\n"
+ "Num of max allowed moves (2-1000) default 5\n"
+ "Duplicate address detection time\n"
+ "Time in seconds (2-1800) default 180\n")
+{
+ struct bgp *bgp_vrf = VTY_GET_CONTEXT(bgp);
+
+ if (!bgp_vrf)
+ return CMD_WARNING;
+
+ if (!EVPN_ENABLED(bgp_vrf)) {
+ vty_out(vty,
+ "This command is only supported under the EVPN VRF\n");
+ return CMD_WARNING;
+ }
+
+ bgp_vrf->evpn_info->dup_addr_detect = true;
+
+ if (time_val)
+ bgp_vrf->evpn_info->dad_time = time_val;
+ if (max_moves_val)
+ bgp_vrf->evpn_info->dad_max_moves = max_moves_val;
+
+ bgp_zebra_dup_addr_detection(bgp_vrf);
+
+ return CMD_SUCCESS;
+}
+
+DEFPY (dup_addr_detection_auto_recovery,
+ dup_addr_detection_auto_recovery_cmd,
+ "dup-addr-detection freeze <permanent |(30-3600)$freeze_time_val>",
+ "Duplicate address detection\n"
+ "Duplicate address detection freeze\n"
+ "Duplicate address detection permanent freeze\n"
+ "Duplicate address detection freeze time (30-3600)\n")
+{
+ struct bgp *bgp_vrf = VTY_GET_CONTEXT(bgp);
+ uint32_t freeze_time = freeze_time_val;
+
+ if (!bgp_vrf)
+ return CMD_WARNING;
+
+ if (!EVPN_ENABLED(bgp_vrf)) {
+ vty_out(vty,
+ "This command is only supported under the EVPN VRF\n");
+ return CMD_WARNING;
+ }
+
+ bgp_vrf->evpn_info->dup_addr_detect = true;
+ bgp_vrf->evpn_info->dad_freeze = true;
+ bgp_vrf->evpn_info->dad_freeze_time = freeze_time;
+
+ bgp_zebra_dup_addr_detection(bgp_vrf);
+
+ return CMD_SUCCESS;
+}
+
+DEFPY (no_dup_addr_detection,
+ no_dup_addr_detection_cmd,
+ "no dup-addr-detection [max-moves (2-1000)$max_moves_val time (2-1800)$time_val | freeze <permanent$permanent_val | (30-3600)$freeze_time_val>]",
+ NO_STR
+ "Duplicate address detection\n"
+ "Max allowed moves before address detected as duplicate\n"
+ "Num of max allowed moves (2-1000) default 5\n"
+ "Duplicate address detection time\n"
+ "Time in seconds (2-1800) default 180\n"
+ "Duplicate address detection freeze\n"
+ "Duplicate address detection permanent freeze\n"
+ "Duplicate address detection freeze time (30-3600)\n")
+{
+ struct bgp *bgp_vrf = VTY_GET_CONTEXT(bgp);
+ uint32_t max_moves = (uint32_t)max_moves_val;
+ uint32_t freeze_time = (uint32_t)freeze_time_val;
+
+ if (!bgp_vrf)
+ return CMD_WARNING;
+
+ if (!EVPN_ENABLED(bgp_vrf)) {
+ vty_out(vty,
+ "This command is only supported under the EVPN VRF\n");
+ return CMD_WARNING;
+ }
+
+ if (argc == 2) {
+ if (!bgp_vrf->evpn_info->dup_addr_detect)
+ return CMD_SUCCESS;
+ /* Reset all parameters to default. */
+ bgp_vrf->evpn_info->dup_addr_detect = false;
+ bgp_vrf->evpn_info->dad_time = EVPN_DAD_DEFAULT_TIME;
+ bgp_vrf->evpn_info->dad_max_moves = EVPN_DAD_DEFAULT_MAX_MOVES;
+ bgp_vrf->evpn_info->dad_freeze = false;
+ bgp_vrf->evpn_info->dad_freeze_time = 0;
+ } else {
+ if (max_moves) {
+ if (bgp_vrf->evpn_info->dad_max_moves != max_moves) {
+ vty_out(vty,
+ "%% Value does not match with config\n");
+ return CMD_SUCCESS;
+ }
+ bgp_vrf->evpn_info->dad_max_moves =
+ EVPN_DAD_DEFAULT_MAX_MOVES;
+ }
+
+ if (time_val) {
+ if (bgp_vrf->evpn_info->dad_time != time_val) {
+ vty_out(vty,
+ "%% Value does not match with config\n");
+ return CMD_SUCCESS;
+ }
+ bgp_vrf->evpn_info->dad_time = EVPN_DAD_DEFAULT_TIME;
+ }
+
+ if (freeze_time) {
+ if (bgp_vrf->evpn_info->dad_freeze_time
+ != freeze_time) {
+ vty_out(vty,
+ "%% Value does not match with config\n");
+ return CMD_SUCCESS;
+ }
+ bgp_vrf->evpn_info->dad_freeze_time = 0;
+ bgp_vrf->evpn_info->dad_freeze = false;
+ }
+
+ if (permanent_val) {
+ if (bgp_vrf->evpn_info->dad_freeze_time) {
+ vty_out(vty,
+ "%% Value does not match with config\n");
+ return CMD_SUCCESS;
+ }
+ bgp_vrf->evpn_info->dad_freeze = false;
+ }
+ }
+
+ bgp_zebra_dup_addr_detection(bgp_vrf);
+
+ return CMD_SUCCESS;
+}
+
+DEFPY(bgp_evpn_advertise_svi_ip,
+ bgp_evpn_advertise_svi_ip_cmd,
+ "[no$no] advertise-svi-ip",
+ NO_STR
+ "Advertise svi mac-ip routes in EVPN\n")
+{
+ struct bgp *bgp = VTY_GET_CONTEXT(bgp);
+
+ if (!bgp)
+ return CMD_WARNING;
+
+ if (no)
+ evpn_set_advertise_svi_macip(bgp, NULL, 0);
+ else {
+ if (!EVPN_ENABLED(bgp)) {
+ vty_out(vty,
+ "This command is only supported under EVPN VRF\n");
+ return CMD_WARNING;
+ }
+ evpn_set_advertise_svi_macip(bgp, NULL, 1);
+ }
+
+ return CMD_SUCCESS;
+}
+
+DEFPY(bgp_evpn_advertise_svi_ip_vni,
+ bgp_evpn_advertise_svi_ip_vni_cmd,
+ "[no$no] advertise-svi-ip",
+ NO_STR
+ "Advertise svi mac-ip routes in EVPN for a VNI\n")
+{
+ struct bgp *bgp = VTY_GET_CONTEXT(bgp);
+ VTY_DECLVAR_CONTEXT_SUB(bgpevpn, vpn);
+
+ if (!bgp)
+ return CMD_WARNING;
+
+ if (no)
+ evpn_set_advertise_svi_macip(bgp, vpn, 0);
+ else
+ evpn_set_advertise_svi_macip(bgp, vpn, 1);
+
+ return CMD_SUCCESS;
+}
+
+DEFUN_HIDDEN (bgp_evpn_advertise_vni_subnet,
+ bgp_evpn_advertise_vni_subnet_cmd,
+ "advertise-subnet",
+ "Advertise the subnet corresponding to VNI\n")
+{
+ struct bgp *bgp_vrf = NULL;
+ struct bgp *bgp = VTY_GET_CONTEXT(bgp);
+ VTY_DECLVAR_CONTEXT_SUB(bgpevpn, vpn);
+
+ if (!bgp)
+ return CMD_WARNING;
+
+ bgp_vrf = bgp_lookup_by_vrf_id(vpn->tenant_vrf_id);
+ if (!bgp_vrf)
+ return CMD_WARNING;
+
+ evpn_set_advertise_subnet(bgp, vpn);
+ return CMD_SUCCESS;
+}
+
+DEFUN_HIDDEN (no_bgp_evpn_advertise_vni_subnet,
+ no_bgp_evpn_advertise_vni_subnet_cmd,
+ "no advertise-subnet",
+ NO_STR
+ "Advertise All local VNIs\n")
+{
+ struct bgp *bgp = VTY_GET_CONTEXT(bgp);
+ VTY_DECLVAR_CONTEXT_SUB(bgpevpn, vpn);
+
+ if (!bgp)
+ return CMD_WARNING;
+
+ evpn_unset_advertise_subnet(bgp, vpn);
+ return CMD_SUCCESS;
+}
+
+DEFUN (bgp_evpn_advertise_type5,
+ bgp_evpn_advertise_type5_cmd,
+ "advertise " BGP_AFI_CMD_STR "" BGP_SAFI_CMD_STR " [gateway-ip] [route-map RMAP_NAME]",
+ "Advertise prefix routes\n"
+ BGP_AFI_HELP_STR
+ BGP_SAFI_HELP_STR
+ "advertise gateway IP overlay index\n"
+ "route-map for filtering specific routes\n"
+ "Name of the route map\n")
+{
+ struct bgp *bgp_vrf = VTY_GET_CONTEXT(bgp); /* bgp vrf instance */
+ int idx_afi = 0;
+ int idx_safi = 0;
+ int idx_rmap = 0;
+ afi_t afi = 0;
+ safi_t safi = 0;
+ int ret = 0;
+ int rmap_changed = 0;
+ enum overlay_index_type oly = OVERLAY_INDEX_TYPE_NONE;
+ int idx_oly = 0;
+ bool adv_flag_changed = false;
+
+ argv_find_and_parse_afi(argv, argc, &idx_afi, &afi);
+ argv_find_and_parse_safi(argv, argc, &idx_safi, &safi);
+ argv_find_and_parse_oly_idx(argv, argc, &idx_oly, &oly);
+
+ ret = argv_find(argv, argc, "route-map", &idx_rmap);
+ if (ret) {
+ if (!bgp_vrf->adv_cmd_rmap[afi][safi].name)
+ rmap_changed = 1;
+ else if (strcmp(argv[idx_rmap + 1]->arg,
+ bgp_vrf->adv_cmd_rmap[afi][safi].name)
+ != 0)
+ rmap_changed = 1;
+ } else if (bgp_vrf->adv_cmd_rmap[afi][safi].name) {
+ rmap_changed = 1;
+ }
+
+ if (!(afi == AFI_IP || afi == AFI_IP6)) {
+ vty_out(vty,
+ "%% Only ipv4 or ipv6 address families are supported\n");
+ return CMD_WARNING;
+ }
+
+ if (safi != SAFI_UNICAST) {
+ vty_out(vty,
+ "%% Only ipv4 unicast or ipv6 unicast are supported\n");
+ return CMD_WARNING;
+ }
+
+ if ((oly != OVERLAY_INDEX_TYPE_NONE)
+ && (oly != OVERLAY_INDEX_GATEWAY_IP)) {
+ vty_out(vty, "%% Unknown overlay-index type specified\n");
+ return CMD_WARNING;
+ }
+
+ if (afi == AFI_IP) {
+ if ((!CHECK_FLAG(bgp_vrf->af_flags[AFI_L2VPN][SAFI_EVPN],
+ BGP_L2VPN_EVPN_ADV_IPV4_UNICAST))
+ && (!CHECK_FLAG(bgp_vrf->af_flags[AFI_L2VPN][SAFI_EVPN],
+ BGP_L2VPN_EVPN_ADV_IPV4_UNICAST_GW_IP))) {
+
+ /*
+ * this is the case for first time ever configuration
+ * adv ipv4 unicast is enabled for the first time.
+ * So no need to reset any flag
+ */
+ if (oly == OVERLAY_INDEX_TYPE_NONE)
+ SET_FLAG(
+ bgp_vrf->af_flags[AFI_L2VPN][SAFI_EVPN],
+ BGP_L2VPN_EVPN_ADV_IPV4_UNICAST);
+ else if (oly == OVERLAY_INDEX_GATEWAY_IP)
+ SET_FLAG(
+ bgp_vrf->af_flags[AFI_L2VPN][SAFI_EVPN],
+ BGP_L2VPN_EVPN_ADV_IPV4_UNICAST_GW_IP);
+ } else if ((oly == OVERLAY_INDEX_TYPE_NONE)
+ && (!CHECK_FLAG(
+ bgp_vrf->af_flags[AFI_L2VPN][SAFI_EVPN],
+ BGP_L2VPN_EVPN_ADV_IPV4_UNICAST))) {
+
+ /*
+ * This is modify case from gateway-ip
+ * to no overlay index
+ */
+ adv_flag_changed = true;
+ UNSET_FLAG(bgp_vrf->af_flags[AFI_L2VPN][SAFI_EVPN],
+ BGP_L2VPN_EVPN_ADV_IPV4_UNICAST_GW_IP);
+ SET_FLAG(bgp_vrf->af_flags[AFI_L2VPN][SAFI_EVPN],
+ BGP_L2VPN_EVPN_ADV_IPV4_UNICAST);
+ } else if ((oly == OVERLAY_INDEX_GATEWAY_IP)
+ && (!CHECK_FLAG(
+ bgp_vrf->af_flags[AFI_L2VPN][SAFI_EVPN],
+ BGP_L2VPN_EVPN_ADV_IPV4_UNICAST_GW_IP))) {
+
+ /*
+ * This is modify case from no overlay index
+ * to gateway-ip
+ */
+ adv_flag_changed = true;
+ UNSET_FLAG(bgp_vrf->af_flags[AFI_L2VPN][SAFI_EVPN],
+ BGP_L2VPN_EVPN_ADV_IPV4_UNICAST);
+ SET_FLAG(bgp_vrf->af_flags[AFI_L2VPN][SAFI_EVPN],
+ BGP_L2VPN_EVPN_ADV_IPV4_UNICAST_GW_IP);
+ } else {
+
+ /*
+ * Command is issued with the same option
+ * (no overlay index or gateway-ip) which was
+ * already configured. So nothing to do.
+ * However, route-map may have been modified.
+ * check if route-map has been modified.
+ * If not, return an error
+ */
+ if (!rmap_changed)
+ return CMD_WARNING;
+ }
+ } else {
+ if ((!CHECK_FLAG(bgp_vrf->af_flags[AFI_L2VPN][SAFI_EVPN],
+ BGP_L2VPN_EVPN_ADV_IPV6_UNICAST))
+ && (!CHECK_FLAG(bgp_vrf->af_flags[AFI_L2VPN][SAFI_EVPN],
+ BGP_L2VPN_EVPN_ADV_IPV6_UNICAST_GW_IP))) {
+
+ /*
+ * this is the case for first time ever configuration
+ * adv ipv6 unicast is enabled for the first time.
+ * So no need to reset any flag
+ */
+ if (oly == OVERLAY_INDEX_TYPE_NONE)
+ SET_FLAG(
+ bgp_vrf->af_flags[AFI_L2VPN][SAFI_EVPN],
+ BGP_L2VPN_EVPN_ADV_IPV6_UNICAST);
+ else if (oly == OVERLAY_INDEX_GATEWAY_IP)
+ SET_FLAG(
+ bgp_vrf->af_flags[AFI_L2VPN][SAFI_EVPN],
+ BGP_L2VPN_EVPN_ADV_IPV6_UNICAST_GW_IP);
+ } else if ((oly == OVERLAY_INDEX_TYPE_NONE)
+ && (!CHECK_FLAG(
+ bgp_vrf->af_flags[AFI_L2VPN][SAFI_EVPN],
+ BGP_L2VPN_EVPN_ADV_IPV6_UNICAST))) {
+
+ /*
+ * This is modify case from gateway-ip
+ * to no overlay index
+ */
+ adv_flag_changed = true;
+ UNSET_FLAG(bgp_vrf->af_flags[AFI_L2VPN][SAFI_EVPN],
+ BGP_L2VPN_EVPN_ADV_IPV6_UNICAST_GW_IP);
+ SET_FLAG(bgp_vrf->af_flags[AFI_L2VPN][SAFI_EVPN],
+ BGP_L2VPN_EVPN_ADV_IPV6_UNICAST);
+ } else if ((oly == OVERLAY_INDEX_GATEWAY_IP)
+ && (!CHECK_FLAG(
+ bgp_vrf->af_flags[AFI_L2VPN][SAFI_EVPN],
+ BGP_L2VPN_EVPN_ADV_IPV6_UNICAST_GW_IP))) {
+
+ /*
+ * This is modify case from no overlay index
+ * to gateway-ip
+ */
+ adv_flag_changed = true;
+ UNSET_FLAG(bgp_vrf->af_flags[AFI_L2VPN][SAFI_EVPN],
+ BGP_L2VPN_EVPN_ADV_IPV6_UNICAST);
+ SET_FLAG(bgp_vrf->af_flags[AFI_L2VPN][SAFI_EVPN],
+ BGP_L2VPN_EVPN_ADV_IPV6_UNICAST_GW_IP);
+ } else {
+
+ /*
+ * Command is issued with the same option
+ * (no overlay index or gateway-ip) which was
+ * already configured. So nothing to do.
+ * However, route-map may have been modified.
+ * check if route-map has been modified.
+ * If not, return an error
+ */
+ if (!rmap_changed)
+ return CMD_WARNING;
+ }
+ }
+
+ if ((rmap_changed) || (adv_flag_changed)) {
+
+ /* If either of these are changed, then FRR needs to
+ * withdraw already advertised type5 routes.
+ */
+ bgp_evpn_withdraw_type5_routes(bgp_vrf, afi, safi);
+ if (rmap_changed) {
+ if (bgp_vrf->adv_cmd_rmap[afi][safi].name) {
+ XFREE(MTYPE_ROUTE_MAP_NAME,
+ bgp_vrf->adv_cmd_rmap[afi][safi].name);
+ route_map_counter_decrement(
+ bgp_vrf->adv_cmd_rmap[afi][safi].map);
+ bgp_vrf->adv_cmd_rmap[afi][safi].name = NULL;
+ bgp_vrf->adv_cmd_rmap[afi][safi].map = NULL;
+ }
+ }
+ }
+
+ /* set the route-map for advertise command */
+ if (ret && argv[idx_rmap + 1]->arg) {
+ bgp_vrf->adv_cmd_rmap[afi][safi].name =
+ XSTRDUP(MTYPE_ROUTE_MAP_NAME, argv[idx_rmap + 1]->arg);
+ bgp_vrf->adv_cmd_rmap[afi][safi].map =
+ route_map_lookup_by_name(argv[idx_rmap + 1]->arg);
+ route_map_counter_increment(
+ bgp_vrf->adv_cmd_rmap[afi][safi].map);
+ }
+
+ /* advertise type-5 routes */
+ if (advertise_type5_routes(bgp_vrf, afi))
+ bgp_evpn_advertise_type5_routes(bgp_vrf, afi, safi);
+ return CMD_SUCCESS;
+}
+
+DEFUN (no_bgp_evpn_advertise_type5,
+ no_bgp_evpn_advertise_type5_cmd,
+ "no advertise " BGP_AFI_CMD_STR "" BGP_SAFI_CMD_STR,
+ NO_STR
+ "Advertise prefix routes\n"
+ BGP_AFI_HELP_STR
+ BGP_SAFI_HELP_STR)
+{
+ struct bgp *bgp_vrf = VTY_GET_CONTEXT(bgp); /* bgp vrf instance */
+ int idx_afi = 0;
+ int idx_safi = 0;
+ afi_t afi = 0;
+ safi_t safi = 0;
+
+ if (!bgp_vrf)
+ return CMD_WARNING;
+
+ argv_find_and_parse_afi(argv, argc, &idx_afi, &afi);
+ argv_find_and_parse_safi(argv, argc, &idx_safi, &safi);
+
+ if (!(afi == AFI_IP || afi == AFI_IP6)) {
+ vty_out(vty,
+ "%% Only ipv4 or ipv6 address families are supported\n");
+ return CMD_WARNING;
+ }
+
+ if (safi != SAFI_UNICAST) {
+ vty_out(vty,
+ "%% Only ipv4 unicast or ipv6 unicast are supported\n");
+ return CMD_WARNING;
+ }
+
+ if (afi == AFI_IP) {
+
+ /* if we are not advertising ipv4 prefix as type-5
+ * nothing to do
+ */
+ if ((CHECK_FLAG(bgp_vrf->af_flags[AFI_L2VPN][SAFI_EVPN],
+ BGP_L2VPN_EVPN_ADV_IPV4_UNICAST)) ||
+ (CHECK_FLAG(bgp_vrf->af_flags[AFI_L2VPN][SAFI_EVPN],
+ BGP_L2VPN_EVPN_ADV_IPV4_UNICAST_GW_IP))) {
+ bgp_evpn_withdraw_type5_routes(bgp_vrf, afi, safi);
+ UNSET_FLAG(bgp_vrf->af_flags[AFI_L2VPN][SAFI_EVPN],
+ BGP_L2VPN_EVPN_ADV_IPV4_UNICAST);
+ UNSET_FLAG(bgp_vrf->af_flags[AFI_L2VPN][SAFI_EVPN],
+ BGP_L2VPN_EVPN_ADV_IPV4_UNICAST_GW_IP);
+ }
+ } else {
+
+ /* if we are not advertising ipv6 prefix as type-5
+ * nothing to do
+ */
+ if ((CHECK_FLAG(bgp_vrf->af_flags[AFI_L2VPN][SAFI_EVPN],
+ BGP_L2VPN_EVPN_ADV_IPV6_UNICAST)) ||
+ (CHECK_FLAG(bgp_vrf->af_flags[AFI_L2VPN][SAFI_EVPN],
+ BGP_L2VPN_EVPN_ADV_IPV6_UNICAST_GW_IP))){
+ bgp_evpn_withdraw_type5_routes(bgp_vrf, afi, safi);
+ UNSET_FLAG(bgp_vrf->af_flags[AFI_L2VPN][SAFI_EVPN],
+ BGP_L2VPN_EVPN_ADV_IPV6_UNICAST);
+ UNSET_FLAG(bgp_vrf->af_flags[AFI_L2VPN][SAFI_EVPN],
+ BGP_L2VPN_EVPN_ADV_IPV6_UNICAST_GW_IP);
+ }
+ }
+
+ /* clear the route-map information for advertise ipv4/ipv6 unicast */
+ if (bgp_vrf->adv_cmd_rmap[afi][safi].name) {
+ XFREE(MTYPE_ROUTE_MAP_NAME,
+ bgp_vrf->adv_cmd_rmap[afi][safi].name);
+ bgp_vrf->adv_cmd_rmap[afi][safi].name = NULL;
+ bgp_vrf->adv_cmd_rmap[afi][safi].map = NULL;
+ }
+
+ return CMD_SUCCESS;
+}
+
+DEFPY (bgp_evpn_use_es_l3nhg,
+ bgp_evpn_use_es_l3nhg_cmd,
+ "[no$no] use-es-l3nhg",
+ NO_STR
+ "use L3 nexthop group for host routes with ES destination\n")
+{
+ bgp_mh_info->host_routes_use_l3nhg = no ? false :true;
+ return CMD_SUCCESS;
+}
+
+DEFPY (bgp_evpn_ead_evi_rx_disable,
+ bgp_evpn_ead_evi_rx_disable_cmd,
+ "[no$no] disable-ead-evi-rx",
+ NO_STR
+ "Activate PE on EAD-ES even if EAD-EVI is not received\n")
+{
+ bool ead_evi_rx = no? true :false;
+
+ if (ead_evi_rx != bgp_mh_info->ead_evi_rx) {
+ bgp_mh_info->ead_evi_rx = ead_evi_rx;
+ bgp_evpn_switch_ead_evi_rx();
+ }
+ return CMD_SUCCESS;
+}
+
+DEFPY (bgp_evpn_ead_evi_tx_disable,
+ bgp_evpn_ead_evi_tx_disable_cmd,
+ "[no$no] disable-ead-evi-tx",
+ NO_STR
+ "Don't advertise EAD-EVI for local ESs\n")
+{
+ bgp_mh_info->ead_evi_tx = no? true :false;
+ return CMD_SUCCESS;
+}
+
+DEFPY (bgp_evpn_enable_resolve_overlay_index,
+ bgp_evpn_enable_resolve_overlay_index_cmd,
+ "[no$no] enable-resolve-overlay-index",
+ NO_STR
+ "Enable Recursive Resolution of type-5 route overlay index\n")
+{
+ struct bgp *bgp = VTY_GET_CONTEXT(bgp);
+
+ if (bgp != bgp_get_evpn()) {
+ vty_out(vty, "This command is only supported under EVPN VRF\n");
+ return CMD_WARNING;
+ }
+
+ bgp_evpn_set_unset_resolve_overlay_index(bgp, no ? false : true);
+ return CMD_SUCCESS;
+}
+
+DEFPY (bgp_evpn_advertise_pip_ip_mac,
+ bgp_evpn_advertise_pip_ip_mac_cmd,
+ "[no$no] advertise-pip [ip <A.B.C.D> [mac <X:X:X:X:X:X|X:X:X:X:X:X/M>]]",
+ NO_STR
+ "evpn system primary IP\n"
+ IP_STR
+ "ip address\n"
+ MAC_STR MAC_STR MAC_STR)
+{
+ struct bgp *bgp_vrf = VTY_GET_CONTEXT(bgp); /* bgp vrf instance */
+ struct bgp *bgp_evpn = NULL;
+
+ if (EVPN_ENABLED(bgp_vrf)) {
+ vty_out(vty,
+ "This command is supported under L3VNI BGP EVPN VRF\n");
+ return CMD_WARNING_CONFIG_FAILED;
+ }
+ bgp_evpn = bgp_get_evpn();
+
+ if (!no) {
+ /* pip is already enabled */
+ if (argc == 1 && bgp_vrf->evpn_info->advertise_pip)
+ return CMD_SUCCESS;
+
+ bgp_vrf->evpn_info->advertise_pip = true;
+ if (ip.s_addr != INADDR_ANY) {
+ /* Already configured with same IP */
+ if (IPV4_ADDR_SAME(&ip,
+ &bgp_vrf->evpn_info->pip_ip_static))
+ return CMD_SUCCESS;
+
+ bgp_vrf->evpn_info->pip_ip_static = ip;
+ bgp_vrf->evpn_info->pip_ip = ip;
+ } else {
+ bgp_vrf->evpn_info->pip_ip_static.s_addr
+ = INADDR_ANY;
+ /* default instance router-id assignemt */
+ if (bgp_evpn)
+ bgp_vrf->evpn_info->pip_ip =
+ bgp_evpn->router_id;
+ }
+ /* parse sys mac */
+ if (!is_zero_mac(&mac->eth_addr)) {
+ /* Already configured with same MAC */
+ if (memcmp(&bgp_vrf->evpn_info->pip_rmac_static,
+ &mac->eth_addr, ETH_ALEN) == 0)
+ return CMD_SUCCESS;
+
+ memcpy(&bgp_vrf->evpn_info->pip_rmac_static,
+ &mac->eth_addr, ETH_ALEN);
+ memcpy(&bgp_vrf->evpn_info->pip_rmac,
+ &bgp_vrf->evpn_info->pip_rmac_static,
+ ETH_ALEN);
+ } else {
+ /* Copy zebra sys mac */
+ if (!is_zero_mac(&bgp_vrf->evpn_info->pip_rmac_zebra))
+ memcpy(&bgp_vrf->evpn_info->pip_rmac,
+ &bgp_vrf->evpn_info->pip_rmac_zebra,
+ ETH_ALEN);
+ }
+ } else {
+ if (argc == 2) {
+ if (!bgp_vrf->evpn_info->advertise_pip)
+ return CMD_SUCCESS;
+ /* Disable PIP feature */
+ bgp_vrf->evpn_info->advertise_pip = false;
+ /* copy anycast mac */
+ memcpy(&bgp_vrf->evpn_info->pip_rmac,
+ &bgp_vrf->rmac, ETH_ALEN);
+ } else {
+ /* remove MAC-IP option retain PIP knob. */
+ if ((ip.s_addr != INADDR_ANY) &&
+ !IPV4_ADDR_SAME(&ip,
+ &bgp_vrf->evpn_info->pip_ip_static)) {
+ vty_out(vty,
+ "%% BGP EVPN PIP IP does not match\n");
+ return CMD_WARNING_CONFIG_FAILED;
+ }
+
+ if (!is_zero_mac(&mac->eth_addr) &&
+ memcmp(&bgp_vrf->evpn_info->pip_rmac_static,
+ &mac->eth_addr, ETH_ALEN) != 0) {
+ vty_out(vty,
+ "%% BGP EVPN PIP MAC does not match\n");
+ return CMD_WARNING_CONFIG_FAILED;
+ }
+ /* pip_rmac can carry vrr_rmac reset only if it matches
+ * with static value.
+ */
+ if (memcmp(&bgp_vrf->evpn_info->pip_rmac,
+ &bgp_vrf->evpn_info->pip_rmac_static,
+ ETH_ALEN) == 0) {
+ /* Copy zebra sys mac */
+ if (!is_zero_mac(
+ &bgp_vrf->evpn_info->pip_rmac_zebra))
+ memcpy(&bgp_vrf->evpn_info->pip_rmac,
+ &bgp_vrf->evpn_info->pip_rmac_zebra,
+ ETH_ALEN);
+ else {
+ /* copy anycast mac */
+ memcpy(&bgp_vrf->evpn_info->pip_rmac,
+ &bgp_vrf->rmac, ETH_ALEN);
+ }
+ }
+ }
+ /* reset user configured sys MAC */
+ memset(&bgp_vrf->evpn_info->pip_rmac_static, 0, ETH_ALEN);
+ /* reset user configured sys IP */
+ bgp_vrf->evpn_info->pip_ip_static.s_addr = INADDR_ANY;
+ /* Assign default PIP IP (bgp instance router-id) */
+ if (bgp_evpn)
+ bgp_vrf->evpn_info->pip_ip = bgp_evpn->router_id;
+ else
+ bgp_vrf->evpn_info->pip_ip.s_addr = INADDR_ANY;
+ }
+
+ if (is_evpn_enabled()) {
+ struct listnode *node = NULL;
+ struct bgpevpn *vpn = NULL;
+
+ /*
+ * At this point if bgp_evpn is NULL and evpn is enabled
+ * something stupid has gone wrong
+ */
+ assert(bgp_evpn);
+
+ update_advertise_vrf_routes(bgp_vrf);
+
+ /* Update (svi) type-2 routes */
+ for (ALL_LIST_ELEMENTS_RO(bgp_vrf->l2vnis, node, vpn)) {
+ if (!bgp_evpn_is_svi_macip_enabled(vpn))
+ continue;
+ update_routes_for_vni(bgp_evpn, vpn);
+ }
+ }
+
+ return CMD_SUCCESS;
+}
+
+/*
+ * Display VNI information - for all or a specific VNI
+ */
+DEFUN(show_bgp_l2vpn_evpn_vni,
+ show_bgp_l2vpn_evpn_vni_cmd,
+ "show bgp l2vpn evpn vni [" CMD_VNI_RANGE "] [json]",
+ SHOW_STR
+ BGP_STR
+ L2VPN_HELP_STR
+ EVPN_HELP_STR
+ "Show VNI\n"
+ "VNI number\n"
+ JSON_STR)
+{
+ struct bgp *bgp_evpn;
+ vni_t vni;
+ int idx = 0;
+ bool uj = false;
+ json_object *json = NULL;
+ uint32_t num_l2vnis = 0;
+ uint32_t num_l3vnis = 0;
+ uint32_t num_vnis = 0;
+ struct listnode *node = NULL;
+ struct bgp *bgp_temp = NULL;
+
+ uj = use_json(argc, argv);
+
+ bgp_evpn = bgp_get_evpn();
+ if (!bgp_evpn)
+ return CMD_WARNING;
+
+ if (!argv_find(argv, argc, "evpn", &idx))
+ return CMD_WARNING;
+
+ if (uj)
+ json = json_object_new_object();
+
+ if ((uj && argc == ((idx + 1) + 2)) || (!uj && argc == (idx + 1) + 1)) {
+
+ num_l2vnis = hashcount(bgp_evpn->vnihash);
+
+ for (ALL_LIST_ELEMENTS_RO(bm->bgp, node, bgp_temp)) {
+ if (bgp_temp->l3vni)
+ num_l3vnis++;
+ }
+ num_vnis = num_l2vnis + num_l3vnis;
+ if (uj) {
+ json_object_string_add(json, "advertiseGatewayMacip",
+ bgp_evpn->advertise_gw_macip
+ ? "Enabled"
+ : "Disabled");
+ json_object_string_add(json, "advertiseSviMacIp",
+ bgp_evpn->evpn_info->advertise_svi_macip
+ ? "Enabled" : "Disabled");
+ json_object_string_add(json, "advertiseAllVnis",
+ is_evpn_enabled() ? "Enabled"
+ : "Disabled");
+ json_object_string_add(
+ json, "flooding",
+ bgp_evpn->vxlan_flood_ctrl ==
+ VXLAN_FLOOD_HEAD_END_REPL
+ ? "Head-end replication"
+ : "Disabled");
+ json_object_string_add(
+ json, "vxlanFlooding",
+ bgp_evpn->vxlan_flood_ctrl ==
+ VXLAN_FLOOD_HEAD_END_REPL
+ ? "Enabled"
+ : "Disabled");
+ json_object_int_add(json, "numVnis", num_vnis);
+ json_object_int_add(json, "numL2Vnis", num_l2vnis);
+ json_object_int_add(json, "numL3Vnis", num_l3vnis);
+ } else {
+ vty_out(vty, "Advertise Gateway Macip: %s\n",
+ bgp_evpn->advertise_gw_macip ? "Enabled"
+ : "Disabled");
+ vty_out(vty, "Advertise SVI Macip: %s\n",
+ bgp_evpn->evpn_info->advertise_svi_macip ? "Enabled"
+ : "Disabled");
+ vty_out(vty, "Advertise All VNI flag: %s\n",
+ is_evpn_enabled() ? "Enabled" : "Disabled");
+ vty_out(vty, "BUM flooding: %s\n",
+ bgp_evpn->vxlan_flood_ctrl ==
+ VXLAN_FLOOD_HEAD_END_REPL
+ ? "Head-end replication"
+ : "Disabled");
+ vty_out(vty, "VXLAN flooding: %s\n",
+ bgp_evpn->vxlan_flood_ctrl ==
+ VXLAN_FLOOD_HEAD_END_REPL
+ ? "Enabled"
+ : "Disabled");
+ vty_out(vty, "Number of L2 VNIs: %u\n", num_l2vnis);
+ vty_out(vty, "Number of L3 VNIs: %u\n", num_l3vnis);
+ }
+ evpn_show_all_vnis(vty, bgp_evpn, json);
+ } else {
+ int vni_idx = 0;
+
+ if (!argv_find(argv, argc, "vni", &vni_idx))
+ return CMD_WARNING;
+
+ /* Display specific VNI */
+ vni = strtoul(argv[vni_idx + 1]->arg, NULL, 10);
+ evpn_show_vni(vty, bgp_evpn, vni, json);
+ }
+
+ if (uj)
+ vty_json(vty, json);
+
+ return CMD_SUCCESS;
+}
+
+DEFUN_HIDDEN(show_bgp_l2vpn_evpn_vni_remote_ip_hash,
+ show_bgp_l2vpn_evpn_vni_remote_ip_hash_cmd,
+ "show bgp l2vpn evpn vni remote-ip-hash",
+ SHOW_STR
+ BGP_STR
+ L2VPN_HELP_STR
+ EVPN_HELP_STR
+ "Show VNI\n"
+ "Remote IP hash\n")
+{
+ struct bgp *bgp_evpn;
+ int idx = 0;
+
+ bgp_evpn = bgp_get_evpn();
+ if (!bgp_evpn)
+ return CMD_WARNING;
+
+ if (!argv_find(argv, argc, "evpn", &idx))
+ return CMD_WARNING;
+
+ hash_iterate(bgp_evpn->vnihash,
+ (void (*)(struct hash_bucket *,
+ void *))bgp_evpn_show_remote_ip_hash,
+ vty);
+
+ return CMD_SUCCESS;
+}
+
+DEFUN_HIDDEN(show_bgp_l2vpn_evpn_vni_svi_hash,
+ show_bgp_l2vpn_evpn_vni_svi_hash_cmd,
+ "show bgp l2vpn evpn vni-svi-hash",
+ SHOW_STR
+ BGP_STR
+ L2VPN_HELP_STR
+ EVPN_HELP_STR
+ "Show vni-svi-hash\n")
+{
+ struct bgp *bgp_evpn;
+ int idx = 0;
+
+ bgp_evpn = bgp_get_evpn();
+ if (!bgp_evpn)
+ return CMD_WARNING;
+
+ if (!argv_find(argv, argc, "evpn", &idx))
+ return CMD_WARNING;
+
+ hash_iterate(bgp_evpn->vni_svi_hash,
+ (void (*)(struct hash_bucket *,
+ void *))bgp_evpn_show_vni_svi_hash,
+ vty);
+
+ return CMD_SUCCESS;
+}
+
+DEFPY(show_bgp_l2vpn_evpn_es_evi,
+ show_bgp_l2vpn_evpn_es_evi_cmd,
+ "show bgp l2vpn evpn es-evi [vni (1-16777215)$vni] [json$uj] [detail$detail]",
+ SHOW_STR
+ BGP_STR
+ L2VPN_HELP_STR
+ EVPN_HELP_STR
+ "ES per EVI\n"
+ "VxLAN Network Identifier\n"
+ "VNI\n"
+ JSON_STR
+ "Detailed information\n")
+{
+ if (vni)
+ bgp_evpn_es_evi_show_vni(vty, vni, !!uj, !!detail);
+ else
+ bgp_evpn_es_evi_show(vty, !!uj, !!detail);
+
+ return CMD_SUCCESS;
+}
+
+DEFPY(show_bgp_l2vpn_evpn_es,
+ show_bgp_l2vpn_evpn_es_cmd,
+ "show bgp l2vpn evpn es [NAME$esi_str|detail$detail] [json$uj]",
+ SHOW_STR
+ BGP_STR
+ L2VPN_HELP_STR
+ EVPN_HELP_STR
+ "Ethernet Segment\n"
+ "ES ID\n"
+ "Detailed information\n"
+ JSON_STR)
+{
+ esi_t esi;
+
+ if (esi_str) {
+ if (!str_to_esi(esi_str, &esi)) {
+ vty_out(vty, "%% Malformed ESI\n");
+ return CMD_WARNING;
+ }
+ bgp_evpn_es_show_esi(vty, &esi, uj);
+ } else {
+
+ bgp_evpn_es_show(vty, uj, !!detail);
+ }
+
+ return CMD_SUCCESS;
+}
+
+DEFPY(show_bgp_l2vpn_evpn_es_vrf, show_bgp_l2vpn_evpn_es_vrf_cmd,
+ "show bgp l2vpn evpn es-vrf [NAME$esi_str] [json$uj]",
+ SHOW_STR BGP_STR L2VPN_HELP_STR EVPN_HELP_STR
+ "Ethernet Segment\n"
+ "ES ID\n" JSON_STR)
+{
+ esi_t esi;
+
+ if (esi_str) {
+ if (!str_to_esi(esi_str, &esi)) {
+ vty_out(vty, "%% Malformed ESI\n");
+ return CMD_WARNING;
+ }
+ bgp_evpn_es_vrf_show_esi(vty, &esi, uj);
+ } else {
+
+ bgp_evpn_es_vrf_show(vty, uj, NULL);
+ }
+
+ return CMD_SUCCESS;
+}
+
+DEFPY(show_bgp_l2vpn_evpn_nh,
+ show_bgp_l2vpn_evpn_nh_cmd,
+ "show bgp l2vpn evpn next-hops [json$uj]",
+ SHOW_STR
+ BGP_STR
+ L2VPN_HELP_STR
+ EVPN_HELP_STR
+ "Nexthops\n"
+ JSON_STR)
+{
+ bgp_evpn_nh_show(vty, uj);
+
+ return CMD_SUCCESS;
+}
+
+/*
+ * Display EVPN neighbor summary.
+ */
+DEFUN(show_bgp_l2vpn_evpn_summary, show_bgp_l2vpn_evpn_summary_cmd,
+ "show bgp [vrf VRFNAME] l2vpn evpn summary [established|failed] [<neighbor <A.B.C.D|X:X::X:X|WORD>|remote-as <(1-4294967295)|internal|external>>] [terse] [wide] [json]",
+ SHOW_STR BGP_STR
+ "bgp vrf\n"
+ "vrf name\n" L2VPN_HELP_STR EVPN_HELP_STR
+ "Summary of BGP neighbor status\n"
+ "Show only sessions in Established state\n"
+ "Show only sessions not in Established state\n"
+ "Show only the specified neighbor session\n"
+ "Neighbor to display information about\n"
+ "Neighbor to display information about\n"
+ "Neighbor on BGP configured interface\n"
+ "Show only the specified remote AS sessions\n"
+ "AS number\n"
+ "Internal (iBGP) AS sessions\n"
+ "External (eBGP) AS sessions\n"
+ "Shorten the information on BGP instances\n"
+ "Increase table width for longer output\n" JSON_STR)
+{
+ int idx_vrf = 0;
+ int idx = 0;
+ char *vrf = NULL;
+ char *neighbor = NULL;
+ as_t as = 0; /* 0 means AS filter not set */
+ int as_type = AS_UNSPECIFIED;
+ uint16_t show_flags = 0;
+
+ if (argv_find(argv, argc, "vrf", &idx_vrf))
+ vrf = argv[++idx_vrf]->arg;
+
+ if (argv_find(argv, argc, "failed", &idx))
+ SET_FLAG(show_flags, BGP_SHOW_OPT_FAILED);
+
+ if (argv_find(argv, argc, "established", &idx))
+ SET_FLAG(show_flags, BGP_SHOW_OPT_ESTABLISHED);
+
+
+ if (argv_find(argv, argc, "neighbor", &idx))
+ neighbor = argv[idx + 1]->arg;
+
+ if (argv_find(argv, argc, "remote-as", &idx)) {
+ if (argv[idx + 1]->arg[0] == 'i')
+ as_type = AS_INTERNAL;
+ else if (argv[idx + 1]->arg[0] == 'e')
+ as_type = AS_EXTERNAL;
+ else
+ as = (as_t)atoi(argv[idx + 1]->arg);
+ }
+
+ if (argv_find(argv, argc, "terse", &idx))
+ SET_FLAG(show_flags, BGP_SHOW_OPT_TERSE);
+
+ if (argv_find(argv, argc, "wide", &idx))
+ SET_FLAG(show_flags, BGP_SHOW_OPT_WIDE);
+
+ if (use_json(argc, argv))
+ SET_FLAG(show_flags, BGP_SHOW_OPT_JSON);
+
+ return bgp_show_summary_vty(vty, vrf, AFI_L2VPN, SAFI_EVPN, neighbor,
+ as_type, as, show_flags);
+}
+
+int bgp_evpn_cli_parse_type(int *type, struct cmd_token **argv, int argc)
+{
+ int type_idx = 0;
+
+ if (argv_find(argv, argc, "type", &type_idx)) {
+ /* Specific type is requested */
+ if ((strncmp(argv[type_idx + 1]->arg, "ma", 2) == 0)
+ || (strmatch(argv[type_idx + 1]->arg, "2")))
+ *type = BGP_EVPN_MAC_IP_ROUTE;
+ else if ((strncmp(argv[type_idx + 1]->arg, "mu", 2) == 0)
+ || (strmatch(argv[type_idx + 1]->arg, "3")))
+ *type = BGP_EVPN_IMET_ROUTE;
+ else if ((strncmp(argv[type_idx + 1]->arg, "es", 2) == 0)
+ || (strmatch(argv[type_idx + 1]->arg, "4")))
+ *type = BGP_EVPN_ES_ROUTE;
+ else if ((strncmp(argv[type_idx + 1]->arg, "ea", 2) == 0)
+ || (strmatch(argv[type_idx + 1]->arg, "1")))
+ *type = BGP_EVPN_AD_ROUTE;
+ else if ((strncmp(argv[type_idx + 1]->arg, "p", 1) == 0)
+ || (strmatch(argv[type_idx + 1]->arg, "5")))
+ *type = BGP_EVPN_IP_PREFIX_ROUTE;
+ else
+ return -1;
+ }
+
+ return 0;
+}
+
+/*
+ * Display global EVPN routing table.
+ */
+DEFUN(show_bgp_l2vpn_evpn_route,
+ show_bgp_l2vpn_evpn_route_cmd,
+ "show bgp l2vpn evpn route [detail] [type "EVPN_TYPE_ALL_LIST"] [json]",
+ SHOW_STR
+ BGP_STR
+ L2VPN_HELP_STR
+ EVPN_HELP_STR
+ EVPN_RT_HELP_STR
+ "Display Detailed Information\n"
+ EVPN_TYPE_HELP_STR
+ EVPN_TYPE_ALL_LIST_HELP_STR
+ JSON_STR)
+{
+ struct bgp *bgp;
+ int detail = 0;
+ int type = 0;
+ bool uj = false;
+ json_object *json = NULL;
+
+ uj = use_json(argc, argv);
+
+ bgp = bgp_get_evpn();
+ if (!bgp)
+ return CMD_WARNING;
+
+ if (uj)
+ json = json_object_new_object();
+
+ if (bgp_evpn_cli_parse_type(&type, argv, argc) < 0)
+ return CMD_WARNING;
+
+ if (argv_find(argv, argc, "detail", &detail))
+ detail = 1;
+
+ evpn_show_all_routes(vty, bgp, type, json, detail);
+
+ /*
+ * This is an extremely expensive operation at scale
+ * and as such we need to save as much time as is
+ * possible.
+ */
+ if (uj)
+ vty_json_no_pretty(vty, json);
+
+ return CMD_SUCCESS;
+}
+
+/*
+ * Display global EVPN routing table for specific RD.
+ */
+DEFUN(show_bgp_l2vpn_evpn_route_rd,
+ show_bgp_l2vpn_evpn_route_rd_cmd,
+ "show bgp l2vpn evpn route rd <ASN:NN_OR_IP-ADDRESS:NN|all> [type "EVPN_TYPE_ALL_LIST"] [json]",
+ SHOW_STR
+ BGP_STR
+ L2VPN_HELP_STR
+ EVPN_HELP_STR
+ EVPN_RT_HELP_STR
+ EVPN_RT_DIST_HELP_STR
+ EVPN_ASN_IP_HELP_STR
+ "All VPN Route Distinguishers\n"
+ EVPN_TYPE_HELP_STR
+ EVPN_TYPE_ALL_LIST_HELP_STR
+ JSON_STR)
+{
+ struct bgp *bgp;
+ int ret = 0;
+ struct prefix_rd prd;
+ int type = 0;
+ bool uj = false;
+ json_object *json = NULL;
+ int idx_ext_community = 0;
+ int rd_all = 0;
+
+ bgp = bgp_get_evpn();
+ if (!bgp)
+ return CMD_WARNING;
+
+ /* check if we need json output */
+ uj = use_json(argc, argv);
+ if (uj)
+ json = json_object_new_object();
+
+ if (!argv_find(argv, argc, "all", &rd_all)) {
+ /* get the RD */
+ if (argv_find(argv, argc, "ASN:NN_OR_IP-ADDRESS:NN",
+ &idx_ext_community)) {
+ ret = str2prefix_rd(argv[idx_ext_community]->arg, &prd);
+ if (!ret) {
+ vty_out(vty,
+ "%% Malformed Route Distinguisher\n");
+ return CMD_WARNING;
+ }
+ }
+ }
+
+ if (bgp_evpn_cli_parse_type(&type, argv, argc) < 0)
+ return CMD_WARNING;
+
+ if (rd_all)
+ evpn_show_all_routes(vty, bgp, type, json, 1);
+ else
+ evpn_show_route_rd(vty, bgp, &prd, type, json);
+
+ if (uj)
+ vty_json(vty, json);
+
+ return CMD_SUCCESS;
+}
+
+/*
+ * Display global EVPN routing table for specific RD and MACIP.
+ */
+DEFUN(show_bgp_l2vpn_evpn_route_rd_macip,
+ show_bgp_l2vpn_evpn_route_rd_macip_cmd,
+ "show bgp l2vpn evpn route rd <ASN:NN_OR_IP-ADDRESS:NN|all> mac WORD [ip WORD] [json]",
+ SHOW_STR
+ BGP_STR
+ L2VPN_HELP_STR
+ EVPN_HELP_STR
+ EVPN_RT_HELP_STR
+ EVPN_RT_DIST_HELP_STR
+ EVPN_ASN_IP_HELP_STR
+ "All VPN Route Distinguishers\n"
+ "MAC\n"
+ "MAC address (e.g., 00:e0:ec:20:12:62)\n"
+ "IP\n"
+ "IP address (IPv4 or IPv6)\n"
+ JSON_STR)
+{
+ struct bgp *bgp;
+ int ret = 0;
+ struct prefix_rd prd;
+ struct ethaddr mac;
+ struct ipaddr ip;
+ int idx_ext_community = 0;
+ int mac_idx = 0;
+ int ip_idx = 0;
+ bool uj = false;
+ json_object *json = NULL;
+ int rd_all = 0;
+
+ memset(&mac, 0, sizeof(struct ethaddr));
+ memset(&ip, 0, sizeof(struct ipaddr));
+
+ bgp = bgp_get_evpn();
+ if (!bgp)
+ return CMD_WARNING;
+
+ /* check if we need json output */
+ uj = use_json(argc, argv);
+ if (uj)
+ json = json_object_new_object();
+
+ /* get the prd */
+ if (!argv_find(argv, argc, "all", &rd_all)) {
+ if (argv_find(argv, argc, "ASN:NN_OR_IP-ADDRESS:NN",
+ &idx_ext_community)) {
+ ret = str2prefix_rd(argv[idx_ext_community]->arg, &prd);
+ if (!ret) {
+ vty_out(vty,
+ "%% Malformed Route Distinguisher\n");
+ return CMD_WARNING;
+ }
+ }
+ }
+
+ /* get the mac */
+ if (argv_find(argv, argc, "mac", &mac_idx)) {
+ if (!prefix_str2mac(argv[mac_idx + 1]->arg, &mac)) {
+ vty_out(vty, "%% Malformed MAC address\n");
+ return CMD_WARNING;
+ }
+ }
+
+ /* get the ip if specified */
+ if (argv_find(argv, argc, "ip", &ip_idx)) {
+ if (str2ipaddr(argv[ip_idx + 1]->arg, &ip) != 0) {
+ vty_out(vty, "%% Malformed IP address\n");
+ return CMD_WARNING;
+ }
+ }
+
+ if (rd_all)
+ evpn_show_route_rd_all_macip(vty, bgp, &mac, &ip, json);
+ else
+ evpn_show_route_rd_macip(vty, bgp, &prd, &mac, &ip, json);
+
+ if (uj)
+ vty_json(vty, json);
+
+ return CMD_SUCCESS;
+}
+
+/* Display per ESI routing table */
+DEFUN(show_bgp_l2vpn_evpn_route_esi,
+ show_bgp_l2vpn_evpn_route_esi_cmd,
+ "show bgp l2vpn evpn route esi ESI [json]",
+ SHOW_STR
+ BGP_STR
+ L2VPN_HELP_STR
+ EVPN_HELP_STR
+ EVPN_RT_HELP_STR
+ "Ethernet Segment Identifier\n"
+ "ESI ID\n"
+ JSON_STR)
+{
+ bool uj = false;
+ esi_t esi;
+ struct bgp *bgp = NULL;
+ json_object *json = NULL;
+
+ memset(&esi, 0, sizeof(esi));
+ bgp = bgp_get_evpn();
+ if (!bgp)
+ return CMD_WARNING;
+
+ uj = use_json(argc, argv);
+ if (uj)
+ json = json_object_new_object();
+
+ /* get the ESI - ESI-ID is at argv[6] */
+ if (!str_to_esi(argv[6]->arg, &esi)) {
+ vty_out(vty, "%% Malformed ESI\n");
+ return CMD_WARNING;
+ }
+
+ evpn_show_routes_esi(vty, bgp, &esi, json);
+
+ if (uj)
+ vty_json(vty, json);
+
+ return CMD_SUCCESS;
+}
+
+
+/*
+ * Display per-VNI EVPN routing table.
+ */
+DEFUN(show_bgp_l2vpn_evpn_route_vni, show_bgp_l2vpn_evpn_route_vni_cmd,
+ "show bgp l2vpn evpn route vni " CMD_VNI_RANGE " [<type <ead|1|macip|2|multicast|3> | vtep A.B.C.D>] [json]",
+ SHOW_STR
+ BGP_STR
+ L2VPN_HELP_STR
+ EVPN_HELP_STR
+ EVPN_RT_HELP_STR
+ "VXLAN Network Identifier\n"
+ "VNI number\n"
+ EVPN_TYPE_HELP_STR
+ EVPN_TYPE_1_HELP_STR
+ EVPN_TYPE_1_HELP_STR
+ EVPN_TYPE_2_HELP_STR
+ EVPN_TYPE_2_HELP_STR
+ EVPN_TYPE_3_HELP_STR
+ EVPN_TYPE_3_HELP_STR
+ "Remote VTEP\n"
+ "Remote VTEP IP address\n"
+ JSON_STR)
+{
+ vni_t vni;
+ struct bgp *bgp;
+ struct in_addr vtep_ip;
+ int type = 0;
+ int idx = 0;
+ int vtep_idx = 0;
+ bool uj = false;
+ json_object *json = NULL;
+
+ bgp = bgp_get_evpn();
+ if (!bgp)
+ return CMD_WARNING;
+
+ /* check if we need json output */
+ uj = use_json(argc, argv);
+ if (uj)
+ json = json_object_new_object();
+
+ if (!argv_find(argv, argc, "evpn", &idx))
+ return CMD_WARNING;
+
+ vtep_ip.s_addr = 0;
+
+ vni = strtoul(argv[idx + 3]->arg, NULL, 10);
+
+ if (bgp_evpn_cli_parse_type(&type, argv, argc) < 0)
+ return CMD_WARNING;
+
+ if (argv_find(argv, argc, "vtep", &vtep_idx)) {
+ if (!inet_aton(argv[vtep_idx + 1]->arg, &vtep_ip)) {
+ vty_out(vty, "%% Malformed VTEP IP address\n");
+ return CMD_WARNING;
+ }
+ }
+
+ evpn_show_routes_vni(vty, bgp, vni, type, vtep_ip, json);
+
+ if (uj)
+ vty_json(vty, json);
+
+ return CMD_SUCCESS;
+}
+
+/*
+ * Display per-VNI EVPN routing table for specific MACIP.
+ */
+DEFUN(show_bgp_l2vpn_evpn_route_vni_macip,
+ show_bgp_l2vpn_evpn_route_vni_macip_cmd,
+ "show bgp l2vpn evpn route vni " CMD_VNI_RANGE " mac WORD [ip WORD] [json]",
+ SHOW_STR
+ BGP_STR
+ L2VPN_HELP_STR
+ EVPN_HELP_STR
+ EVPN_RT_HELP_STR
+ "VXLAN Network Identifier\n"
+ "VNI number\n"
+ "MAC\n"
+ "MAC address (e.g., 00:e0:ec:20:12:62)\n"
+ "IP\n"
+ "IP address (IPv4 or IPv6)\n"
+ JSON_STR)
+{
+ vni_t vni;
+ struct bgp *bgp;
+ struct ethaddr mac;
+ struct ipaddr ip;
+ int idx = 0;
+ bool uj = false;
+ json_object *json = NULL;
+
+ bgp = bgp_get_evpn();
+ if (!bgp)
+ return CMD_WARNING;
+
+ /* check if we need json output */
+ uj = use_json(argc, argv);
+ if (uj)
+ json = json_object_new_object();
+
+ if (!argv_find(argv, argc, "evpn", &idx))
+ return CMD_WARNING;
+
+ /* get the VNI */
+ vni = strtoul(argv[idx + 3]->arg, NULL, 10);
+
+ /* get the mac */
+ if (!prefix_str2mac(argv[idx + 5]->arg, &mac)) {
+ vty_out(vty, "%% Malformed MAC address\n");
+ return CMD_WARNING;
+ }
+
+ /* get the ip */
+ memset(&ip, 0, sizeof(ip));
+ if ((!uj && ((argc == (idx + 1 + 7)) && argv[idx + 7]->arg != NULL))
+ || (uj
+ && ((argc == (idx + 1 + 8)) && argv[idx + 7]->arg != NULL))) {
+ if (str2ipaddr(argv[idx + 7]->arg, &ip) != 0) {
+ vty_out(vty, "%% Malformed IP address\n");
+ return CMD_WARNING;
+ }
+ }
+
+ evpn_show_route_vni_macip(vty, bgp, vni, &mac, &ip, json);
+
+ if (uj)
+ vty_json(vty, json);
+
+ return CMD_SUCCESS;
+}
+
+/*
+ * Display per-VNI EVPN routing table for specific multicast IP (remote VTEP).
+ */
+DEFUN(show_bgp_l2vpn_evpn_route_vni_multicast,
+ show_bgp_l2vpn_evpn_route_vni_multicast_cmd,
+ "show bgp l2vpn evpn route vni " CMD_VNI_RANGE " multicast A.B.C.D [json]",
+ SHOW_STR
+ BGP_STR
+ L2VPN_HELP_STR
+ EVPN_HELP_STR
+ EVPN_RT_HELP_STR
+ "VXLAN Network Identifier\n"
+ "VNI number\n"
+ EVPN_TYPE_3_HELP_STR
+ "Originating Router IP address\n"
+ JSON_STR)
+{
+ vni_t vni;
+ struct bgp *bgp;
+ int ret;
+ struct in_addr orig_ip;
+ int idx = 0;
+ bool uj = false;
+ json_object *json = NULL;
+
+ bgp = bgp_get_evpn();
+ if (!bgp)
+ return CMD_WARNING;
+
+ /* check if we need json output */
+ uj = use_json(argc, argv);
+ if (uj)
+ json = json_object_new_object();
+
+ if (!argv_find(argv, argc, "evpn", &idx))
+ return CMD_WARNING;
+
+ /* get the VNI */
+ vni = strtoul(argv[idx + 3]->arg, NULL, 10);
+
+ /* get the ip */
+ ret = inet_aton(argv[idx + 5]->arg, &orig_ip);
+ if (!ret) {
+ vty_out(vty, "%% Malformed Originating Router IP address\n");
+ return CMD_WARNING;
+ }
+
+ evpn_show_route_vni_multicast(vty, bgp, vni, orig_ip, json);
+
+ if (uj)
+ vty_json(vty, json);
+
+ return CMD_SUCCESS;
+}
+
+/*
+ * Display per-VNI EVPN routing table - for all VNIs.
+ */
+DEFUN(show_bgp_l2vpn_evpn_route_vni_all,
+ show_bgp_l2vpn_evpn_route_vni_all_cmd,
+ "show bgp l2vpn evpn route vni all [detail] [vtep A.B.C.D] [json]",
+ SHOW_STR
+ BGP_STR
+ L2VPN_HELP_STR
+ EVPN_HELP_STR
+ EVPN_RT_HELP_STR
+ "VXLAN Network Identifier\n"
+ "All VNIs\n"
+ "Print Detailed Output\n"
+ "Remote VTEP\n"
+ "Remote VTEP IP address\n"
+ JSON_STR)
+{
+ struct bgp *bgp;
+ struct in_addr vtep_ip;
+ int idx = 0;
+ bool uj = false;
+ json_object *json = NULL;
+ /* Detail Adjust. Adjust indexes according to detail option */
+ int da = 0;
+
+ bgp = bgp_get_evpn();
+ if (!bgp)
+ return CMD_WARNING;
+
+ /* check if we need json output */
+ uj = use_json(argc, argv);
+ if (uj)
+ json = json_object_new_object();
+
+ if (!argv_find(argv, argc, "evpn", &idx))
+ return CMD_WARNING;
+
+ if (argv_find(argv, argc, "detail", &da))
+ da = 1;
+
+ /* vtep-ip position depends on detail option */
+ vtep_ip.s_addr = 0;
+ if ((!uj && (argc == (idx + 1 + 5 + da) && argv[idx + 5 + da]->arg))
+ || (uj
+ && (argc == (idx + 1 + 6 + da) && argv[idx + 5 + da]->arg))) {
+ if (!inet_aton(argv[idx + 5 + da]->arg, &vtep_ip)) {
+ vty_out(vty, "%% Malformed VTEP IP address\n");
+ return CMD_WARNING;
+ }
+ }
+
+ evpn_show_routes_vni_all(vty, bgp, vtep_ip, json, da);
+
+ if (uj)
+ vty_json(vty, json);
+
+ return CMD_SUCCESS;
+}
+
+DEFPY_HIDDEN(
+ show_bgp_l2vpn_evpn_route_mac_ip_evi_es,
+ show_bgp_l2vpn_evpn_route_mac_ip_evi_es_cmd,
+ "show bgp l2vpn evpn route mac-ip-evi-es [NAME$esi_str|detail$detail] [json$uj]",
+ SHOW_STR BGP_STR L2VPN_HELP_STR EVPN_HELP_STR
+ "EVPN route information\n"
+ "MAC IP routes in the EVI tables linked to the ES\n"
+ "ES ID\n"
+ "Detailed information\n" JSON_STR)
+{
+ esi_t esi;
+ esi_t *esi_p;
+ json_object *json = NULL;
+
+ if (esi_str) {
+ if (!str_to_esi(esi_str, &esi)) {
+ vty_out(vty, "%% Malformed ESI\n");
+ return CMD_WARNING;
+ }
+ esi_p = &esi;
+ } else {
+ esi_p = NULL;
+ }
+
+ if (uj)
+ json = json_object_new_object();
+ bgp_evpn_show_routes_mac_ip_evi_es(vty, esi_p, json, !!detail);
+ if (uj)
+ vty_json(vty, json);
+
+ return CMD_SUCCESS;
+}
+
+DEFPY_HIDDEN(
+ show_bgp_l2vpn_evpn_route_mac_ip_global_es,
+ show_bgp_l2vpn_evpn_route_mac_ip_global_es_cmd,
+ "show bgp l2vpn evpn route mac-ip-global-es [NAME$esi_str|detail$detail] [json$uj]",
+ SHOW_STR BGP_STR L2VPN_HELP_STR EVPN_HELP_STR
+ "EVPN route information\n"
+ "MAC IP routes in the global table linked to the ES\n"
+ "ES ID\n"
+ "Detailed information\n" JSON_STR)
+{
+ esi_t esi;
+ esi_t *esi_p;
+ json_object *json = NULL;
+
+ if (esi_str) {
+ if (!str_to_esi(esi_str, &esi)) {
+ vty_out(vty, "%% Malformed ESI\n");
+ return CMD_WARNING;
+ }
+ esi_p = &esi;
+ } else {
+ esi_p = NULL;
+ }
+
+ if (uj)
+ json = json_object_new_object();
+ bgp_evpn_show_routes_mac_ip_global_es(vty, esi_p, json, !!detail);
+ if (uj)
+ vty_json(vty, json);
+
+ return CMD_SUCCESS;
+}
+
+/*
+ * Display EVPN import route-target hash table
+ */
+DEFUN(show_bgp_l2vpn_evpn_vrf_import_rt,
+ show_bgp_l2vpn_evpn_vrf_import_rt_cmd,
+ "show bgp l2vpn evpn vrf-import-rt [json]",
+ SHOW_STR
+ BGP_STR
+ L2VPN_HELP_STR
+ EVPN_HELP_STR
+ "Show vrf import route target\n"
+ JSON_STR)
+{
+ bool uj = false;
+ struct bgp *bgp_evpn = NULL;
+ json_object *json = NULL;
+
+ bgp_evpn = bgp_get_evpn();
+ if (!bgp_evpn)
+ return CMD_WARNING;
+
+ uj = use_json(argc, argv);
+ if (uj)
+ json = json_object_new_object();
+
+ evpn_show_vrf_import_rts(vty, bgp_evpn, json);
+
+ if (uj)
+ vty_json(vty, json);
+
+ return CMD_SUCCESS;
+}
+
+/*
+ * Display EVPN import route-target hash table
+ */
+DEFUN(show_bgp_l2vpn_evpn_import_rt,
+ show_bgp_l2vpn_evpn_import_rt_cmd,
+ "show bgp l2vpn evpn import-rt [json]",
+ SHOW_STR
+ BGP_STR
+ L2VPN_HELP_STR
+ EVPN_HELP_STR
+ "Show import route target\n"
+ JSON_STR)
+{
+ struct bgp *bgp;
+ bool uj = false;
+ json_object *json = NULL;
+
+ bgp = bgp_get_evpn();
+ if (!bgp)
+ return CMD_WARNING;
+
+ uj = use_json(argc, argv);
+ if (uj)
+ json = json_object_new_object();
+
+ evpn_show_import_rts(vty, bgp, json);
+
+ if (uj)
+ vty_json(vty, json);
+
+ return CMD_SUCCESS;
+}
+
+DEFPY_HIDDEN(test_es_add,
+ test_es_add_cmd,
+ "[no$no] test es NAME$esi_str [state NAME$state_str]",
+ NO_STR
+ "Test\n"
+ "Ethernet-segment\n"
+ "Ethernet-Segment Identifier\n"
+ "ES link state\n"
+ "up|down\n"
+)
+{
+ int ret = 0;
+ esi_t esi;
+ struct bgp *bgp;
+ struct in_addr vtep_ip;
+ bool oper_up;
+
+ bgp = bgp_get_evpn();
+ if (!bgp) {
+ vty_out(vty, "%% EVPN BGP instance not yet created\n");
+ return CMD_WARNING;
+ }
+
+ if (!str_to_esi(esi_str, &esi)) {
+ vty_out(vty, "%% Malformed ESI\n");
+ return CMD_WARNING;
+ }
+
+ if (no) {
+ ret = bgp_evpn_local_es_del(bgp, &esi);
+ if (ret == -1) {
+ vty_out(vty, "%% Failed to delete ES\n");
+ return CMD_WARNING;
+ }
+ } else {
+ if (state_str && !strcmp(state_str, "up"))
+ oper_up = true;
+ else
+ oper_up = false;
+ vtep_ip = bgp->router_id;
+
+ ret = bgp_evpn_local_es_add(bgp, &esi, vtep_ip, oper_up,
+ EVPN_MH_DF_PREF_MIN, false);
+ if (ret == -1) {
+ vty_out(vty, "%% Failed to add ES\n");
+ return CMD_WARNING;
+ }
+ }
+ return CMD_SUCCESS;
+}
+
+DEFPY_HIDDEN(test_es_vni_add,
+ test_es_vni_add_cmd,
+ "[no$no] test es NAME$esi_str vni (1-16777215)$vni",
+ NO_STR
+ "Test\n"
+ "Ethernet-segment\n"
+ "Ethernet-Segment Identifier\n"
+ "VNI\n"
+ "1-16777215\n"
+)
+{
+ int ret = 0;
+ esi_t esi;
+ struct bgp *bgp;
+
+ bgp = bgp_get_evpn();
+ if (!bgp) {
+ vty_out(vty, "%% EVPN BGP instance not yet created\n");
+ return CMD_WARNING;
+ }
+
+ if (!str_to_esi(esi_str, &esi)) {
+ vty_out(vty, "%% Malformed ESI\n");
+ return CMD_WARNING;
+ }
+
+ if (no) {
+ ret = bgp_evpn_local_es_evi_del(bgp, &esi, vni);
+ if (ret == -1) {
+ vty_out(vty, "%% Failed to deref ES VNI\n");
+ return CMD_WARNING;
+ }
+ } else {
+ ret = bgp_evpn_local_es_evi_add(bgp, &esi, vni);
+ if (ret == -1) {
+ vty_out(vty, "%% Failed to ref ES VNI\n");
+ return CMD_WARNING;
+ }
+ }
+ return CMD_SUCCESS;
+}
+
+ALIAS_HIDDEN(show_bgp_l2vpn_evpn_vni, show_bgp_evpn_vni_cmd,
+ "show bgp evpn vni [" CMD_VNI_RANGE "]", SHOW_STR BGP_STR EVPN_HELP_STR
+ "Show VNI\n"
+ "VNI number\n")
+
+ALIAS_HIDDEN(show_bgp_l2vpn_evpn_summary, show_bgp_evpn_summary_cmd,
+ "show bgp evpn summary [json]", SHOW_STR BGP_STR EVPN_HELP_STR
+ "Summary of BGP neighbor status\n" JSON_STR)
+
+ALIAS_HIDDEN(show_bgp_l2vpn_evpn_route, show_bgp_evpn_route_cmd,
+ "show bgp evpn route [detail] [type <macip|2|multicast|3>]",
+ SHOW_STR BGP_STR EVPN_HELP_STR
+ EVPN_RT_HELP_STR
+ "Display Detailed Information\n"
+ EVPN_TYPE_HELP_STR
+ EVPN_TYPE_2_HELP_STR
+ EVPN_TYPE_2_HELP_STR
+ EVPN_TYPE_3_HELP_STR
+ EVPN_TYPE_3_HELP_STR)
+
+ALIAS_HIDDEN(
+ show_bgp_l2vpn_evpn_route_rd, show_bgp_evpn_route_rd_cmd,
+ "show bgp evpn route rd ASN:NN_OR_IP-ADDRESS:NN [type <macip|2|multicast|3>]",
+ SHOW_STR BGP_STR EVPN_HELP_STR
+ EVPN_RT_HELP_STR
+ EVPN_RT_DIST_HELP_STR
+ EVPN_ASN_IP_HELP_STR
+ EVPN_TYPE_HELP_STR
+ EVPN_TYPE_2_HELP_STR
+ EVPN_TYPE_2_HELP_STR
+ EVPN_TYPE_3_HELP_STR
+ EVPN_TYPE_3_HELP_STR)
+
+ALIAS_HIDDEN(
+ show_bgp_l2vpn_evpn_route_rd_macip, show_bgp_evpn_route_rd_macip_cmd,
+ "show bgp evpn route rd ASN:NN_OR_IP-ADDRESS:NN mac WORD [ip WORD]",
+ SHOW_STR BGP_STR EVPN_HELP_STR
+ EVPN_RT_HELP_STR
+ EVPN_RT_DIST_HELP_STR
+ EVPN_ASN_IP_HELP_STR
+ "MAC\n"
+ "MAC address (e.g., 00:e0:ec:20:12:62)\n"
+ "IP\n"
+ "IP address (IPv4 or IPv6)\n")
+
+ALIAS_HIDDEN(
+ show_bgp_l2vpn_evpn_route_vni, show_bgp_evpn_route_vni_cmd,
+ "show bgp evpn route vni " CMD_VNI_RANGE " [<type <macip|2|multicast|3> | vtep A.B.C.D>]",
+ SHOW_STR BGP_STR EVPN_HELP_STR
+ EVPN_RT_HELP_STR
+ "VXLAN Network Identifier\n"
+ "VNI number\n"
+ EVPN_TYPE_HELP_STR
+ EVPN_TYPE_2_HELP_STR
+ EVPN_TYPE_2_HELP_STR
+ EVPN_TYPE_3_HELP_STR
+ EVPN_TYPE_3_HELP_STR
+ "Remote VTEP\n"
+ "Remote VTEP IP address\n")
+
+ALIAS_HIDDEN(show_bgp_l2vpn_evpn_route_vni_macip,
+ show_bgp_evpn_route_vni_macip_cmd,
+ "show bgp evpn route vni " CMD_VNI_RANGE " mac WORD [ip WORD]",
+ SHOW_STR BGP_STR EVPN_HELP_STR
+ EVPN_RT_HELP_STR
+ "VXLAN Network Identifier\n"
+ "VNI number\n"
+ "MAC\n"
+ "MAC address (e.g., 00:e0:ec:20:12:62)\n"
+ "IP\n"
+ "IP address (IPv4 or IPv6)\n")
+
+ALIAS_HIDDEN(show_bgp_l2vpn_evpn_route_vni_multicast,
+ show_bgp_evpn_route_vni_multicast_cmd,
+ "show bgp evpn route vni " CMD_VNI_RANGE " multicast A.B.C.D",
+ SHOW_STR BGP_STR EVPN_HELP_STR
+ EVPN_RT_HELP_STR
+ "VXLAN Network Identifier\n"
+ "VNI number\n"
+ EVPN_TYPE_3_HELP_STR
+ "Originating Router IP address\n")
+
+ALIAS_HIDDEN(show_bgp_l2vpn_evpn_route_vni_all, show_bgp_evpn_route_vni_all_cmd,
+ "show bgp evpn route vni all [detail] [vtep A.B.C.D]",
+ SHOW_STR BGP_STR EVPN_HELP_STR
+ EVPN_RT_HELP_STR
+ "VXLAN Network Identifier\n"
+ "All VNIs\n"
+ "Print Detailed Output\n"
+ "Remote VTEP\n"
+ "Remote VTEP IP address\n")
+
+ALIAS_HIDDEN(show_bgp_l2vpn_evpn_import_rt, show_bgp_evpn_import_rt_cmd,
+ "show bgp evpn import-rt",
+ SHOW_STR BGP_STR EVPN_HELP_STR "Show import route target\n")
+
+DEFUN_NOSH (bgp_evpn_vni,
+ bgp_evpn_vni_cmd,
+ "vni " CMD_VNI_RANGE,
+ "VXLAN Network Identifier\n"
+ "VNI number\n")
+{
+ vni_t vni;
+ struct bgp *bgp = VTY_GET_CONTEXT(bgp);
+ struct bgpevpn *vpn;
+
+ if (!bgp)
+ return CMD_WARNING;
+
+ vni = strtoul(argv[1]->arg, NULL, 10);
+
+ /* Create VNI, or mark as configured. */
+ vpn = evpn_create_update_vni(bgp, vni);
+ if (!vpn) {
+ vty_out(vty, "%% Failed to create VNI \n");
+ return CMD_WARNING;
+ }
+
+ VTY_PUSH_CONTEXT_SUB(BGP_EVPN_VNI_NODE, vpn);
+ return CMD_SUCCESS;
+}
+
+DEFUN (no_bgp_evpn_vni,
+ no_bgp_evpn_vni_cmd,
+ "no vni " CMD_VNI_RANGE,
+ NO_STR
+ "VXLAN Network Identifier\n"
+ "VNI number\n")
+{
+ vni_t vni;
+ struct bgp *bgp = VTY_GET_CONTEXT(bgp);
+ struct bgpevpn *vpn;
+
+ if (!bgp)
+ return CMD_WARNING;
+
+ vni = strtoul(argv[2]->arg, NULL, 10);
+
+ /* Check if we should disallow. */
+ vpn = bgp_evpn_lookup_vni(bgp, vni);
+ if (!vpn) {
+ vty_out(vty, "%% Specified VNI does not exist\n");
+ return CMD_WARNING;
+ }
+ if (!is_vni_configured(vpn)) {
+ vty_out(vty, "%% Specified VNI is not configured\n");
+ return CMD_WARNING;
+ }
+
+ evpn_delete_vni(bgp, vpn);
+ return CMD_SUCCESS;
+}
+
+DEFUN_NOSH (exit_vni,
+ exit_vni_cmd,
+ "exit-vni",
+ "Exit from VNI mode\n")
+{
+ if (vty->node == BGP_EVPN_VNI_NODE)
+ vty->node = BGP_EVPN_NODE;
+ return CMD_SUCCESS;
+}
+
+DEFUN (bgp_evpn_vrf_rd,
+ bgp_evpn_vrf_rd_cmd,
+ "rd ASN:NN_OR_IP-ADDRESS:NN",
+ EVPN_RT_DIST_HELP_STR
+ EVPN_ASN_IP_HELP_STR)
+{
+ int ret;
+ struct prefix_rd prd;
+ struct bgp *bgp_vrf = VTY_GET_CONTEXT(bgp);
+
+ if (!bgp_vrf)
+ return CMD_WARNING;
+
+ ret = str2prefix_rd(argv[1]->arg, &prd);
+ if (!ret) {
+ vty_out(vty, "%% Malformed Route Distinguisher\n");
+ return CMD_WARNING;
+ }
+
+ /* If same as existing value, there is nothing more to do. */
+ if (bgp_evpn_vrf_rd_matches_existing(bgp_vrf, &prd))
+ return CMD_SUCCESS;
+
+ /* Configure or update the RD. */
+ evpn_configure_vrf_rd(bgp_vrf, &prd);
+ return CMD_SUCCESS;
+}
+
+DEFUN (no_bgp_evpn_vrf_rd,
+ no_bgp_evpn_vrf_rd_cmd,
+ "no rd ASN:NN_OR_IP-ADDRESS:NN",
+ NO_STR
+ EVPN_RT_DIST_HELP_STR
+ EVPN_ASN_IP_HELP_STR)
+{
+ int ret;
+ struct prefix_rd prd;
+ struct bgp *bgp_vrf = VTY_GET_CONTEXT(bgp);
+
+ if (!bgp_vrf)
+ return CMD_WARNING;
+
+ ret = str2prefix_rd(argv[2]->arg, &prd);
+ if (!ret) {
+ vty_out(vty, "%% Malformed Route Distinguisher\n");
+ return CMD_WARNING;
+ }
+
+ /* Check if we should disallow. */
+ if (!is_vrf_rd_configured(bgp_vrf)) {
+ vty_out(vty, "%% RD is not configured for this VRF\n");
+ return CMD_WARNING;
+ }
+
+ if (!bgp_evpn_vrf_rd_matches_existing(bgp_vrf, &prd)) {
+ vty_out(vty,
+ "%% RD specified does not match configuration for this VRF\n");
+ return CMD_WARNING;
+ }
+
+ evpn_unconfigure_vrf_rd(bgp_vrf);
+ return CMD_SUCCESS;
+}
+
+DEFUN (no_bgp_evpn_vrf_rd_without_val,
+ no_bgp_evpn_vrf_rd_without_val_cmd,
+ "no rd",
+ NO_STR
+ EVPN_RT_DIST_HELP_STR)
+{
+ struct bgp *bgp_vrf = VTY_GET_CONTEXT(bgp);
+
+ if (!bgp_vrf)
+ return CMD_WARNING;
+
+ /* Check if we should disallow. */
+ if (!is_vrf_rd_configured(bgp_vrf)) {
+ vty_out(vty, "%% RD is not configured for this VRF\n");
+ return CMD_WARNING;
+ }
+
+ evpn_unconfigure_vrf_rd(bgp_vrf);
+ return CMD_SUCCESS;
+}
+
+DEFUN (bgp_evpn_vni_rd,
+ bgp_evpn_vni_rd_cmd,
+ "rd ASN:NN_OR_IP-ADDRESS:NN",
+ EVPN_RT_DIST_HELP_STR
+ EVPN_ASN_IP_HELP_STR)
+{
+ struct prefix_rd prd;
+ struct bgp *bgp = VTY_GET_CONTEXT(bgp);
+ VTY_DECLVAR_CONTEXT_SUB(bgpevpn, vpn);
+ int ret;
+
+ if (!bgp)
+ return CMD_WARNING;
+
+ if (!EVPN_ENABLED(bgp)) {
+ vty_out(vty,
+ "This command is only supported under EVPN VRF\n");
+ return CMD_WARNING;
+ }
+
+ ret = str2prefix_rd(argv[1]->arg, &prd);
+ if (!ret) {
+ vty_out(vty, "%% Malformed Route Distinguisher\n");
+ return CMD_WARNING;
+ }
+
+ /* If same as existing value, there is nothing more to do. */
+ if (bgp_evpn_rd_matches_existing(vpn, &prd))
+ return CMD_SUCCESS;
+
+ /* Configure or update the RD. */
+ evpn_configure_rd(bgp, vpn, &prd);
+ return CMD_SUCCESS;
+}
+
+DEFUN (no_bgp_evpn_vni_rd,
+ no_bgp_evpn_vni_rd_cmd,
+ "no rd ASN:NN_OR_IP-ADDRESS:NN",
+ NO_STR
+ EVPN_RT_DIST_HELP_STR
+ EVPN_ASN_IP_HELP_STR)
+{
+ struct prefix_rd prd;
+ struct bgp *bgp = VTY_GET_CONTEXT(bgp);
+ VTY_DECLVAR_CONTEXT_SUB(bgpevpn, vpn);
+ int ret;
+
+ if (!bgp)
+ return CMD_WARNING;
+
+ if (!EVPN_ENABLED(bgp)) {
+ vty_out(vty,
+ "This command is only supported under EVPN VRF\n");
+ return CMD_WARNING;
+ }
+
+ ret = str2prefix_rd(argv[2]->arg, &prd);
+ if (!ret) {
+ vty_out(vty, "%% Malformed Route Distinguisher\n");
+ return CMD_WARNING;
+ }
+
+ /* Check if we should disallow. */
+ if (!is_rd_configured(vpn)) {
+ vty_out(vty, "%% RD is not configured for this VNI\n");
+ return CMD_WARNING;
+ }
+
+ if (!bgp_evpn_rd_matches_existing(vpn, &prd)) {
+ vty_out(vty,
+ "%% RD specified does not match configuration for this VNI\n");
+ return CMD_WARNING;
+ }
+
+ evpn_unconfigure_rd(bgp, vpn);
+ return CMD_SUCCESS;
+}
+
+DEFUN (no_bgp_evpn_vni_rd_without_val,
+ no_bgp_evpn_vni_rd_without_val_cmd,
+ "no rd",
+ NO_STR
+ EVPN_RT_DIST_HELP_STR)
+{
+ struct bgp *bgp = VTY_GET_CONTEXT(bgp);
+ VTY_DECLVAR_CONTEXT_SUB(bgpevpn, vpn);
+
+ if (!bgp)
+ return CMD_WARNING;
+
+ if (!EVPN_ENABLED(bgp)) {
+ vty_out(vty,
+ "This command is only supported under EVPN VRF\n");
+ return CMD_WARNING;
+ }
+
+ /* Check if we should disallow. */
+ if (!is_rd_configured(vpn)) {
+ vty_out(vty, "%% RD is not configured for this VNI\n");
+ return CMD_WARNING;
+ }
+
+ evpn_unconfigure_rd(bgp, vpn);
+ return CMD_SUCCESS;
+}
+
+/*
+ * Loop over all extended-communities in the route-target list rtl and
+ * return 1 if we find ecomtarget
+ */
+static int bgp_evpn_rt_matches_existing(struct list *rtl,
+ struct ecommunity *ecomtarget)
+{
+ struct listnode *node, *nnode;
+ struct ecommunity *ecom;
+
+ for (ALL_LIST_ELEMENTS(rtl, node, nnode, ecom)) {
+ if (ecommunity_match(ecom, ecomtarget))
+ return 1;
+ }
+
+ return 0;
+}
+
+/* display L3VNI related info for a VRF instance */
+DEFUN (show_bgp_vrf_l3vni_info,
+ show_bgp_vrf_l3vni_info_cmd,
+ "show bgp vrf VRFNAME vni [json]",
+ SHOW_STR
+ BGP_STR
+ "show bgp vrf\n"
+ "VRF Name\n"
+ "L3-VNI\n"
+ JSON_STR)
+{
+ char buf[ETHER_ADDR_STRLEN];
+ int idx_vrf = 3;
+ const char *name = NULL;
+ struct bgp *bgp = NULL;
+ struct listnode *node = NULL;
+ struct bgpevpn *vpn = NULL;
+ struct ecommunity *ecom = NULL;
+ json_object *json = NULL;
+ json_object *json_vnis = NULL;
+ json_object *json_export_rts = NULL;
+ json_object *json_import_rts = NULL;
+ bool uj = use_json(argc, argv);
+
+ if (uj) {
+ json = json_object_new_object();
+ json_vnis = json_object_new_array();
+ json_export_rts = json_object_new_array();
+ json_import_rts = json_object_new_array();
+ }
+
+ name = argv[idx_vrf]->arg;
+ bgp = bgp_lookup_by_name(name);
+ if (strmatch(name, VRF_DEFAULT_NAME))
+ bgp = bgp_get_default();
+
+ if (!bgp) {
+ if (!uj)
+ vty_out(vty, "BGP instance for VRF %s not found\n",
+ name);
+ else {
+ json_object_string_add(json, "warning",
+ "BGP instance not found");
+ vty_out(vty, "%s\n", json_object_to_json_string(json));
+ json_object_free(json);
+ }
+ return CMD_WARNING;
+ }
+
+ if (!json) {
+ vty_out(vty, "BGP VRF: %s\n", name);
+ vty_out(vty, " Local-Ip: %pI4\n", &bgp->originator_ip);
+ vty_out(vty, " L3-VNI: %u\n", bgp->l3vni);
+ vty_out(vty, " Rmac: %s\n",
+ prefix_mac2str(&bgp->rmac, buf, sizeof(buf)));
+ vty_out(vty, " VNI Filter: %s\n",
+ CHECK_FLAG(bgp->vrf_flags,
+ BGP_VRF_L3VNI_PREFIX_ROUTES_ONLY)
+ ? "prefix-routes-only"
+ : "none");
+ vty_out(vty, " L2-VNI List:\n");
+ vty_out(vty, " ");
+ for (ALL_LIST_ELEMENTS_RO(bgp->l2vnis, node, vpn))
+ vty_out(vty, "%u ", vpn->vni);
+ vty_out(vty, "\n");
+ vty_out(vty, " Export-RTs:\n");
+ vty_out(vty, " ");
+ for (ALL_LIST_ELEMENTS_RO(bgp->vrf_export_rtl, node, ecom))
+ vty_out(vty, "%s ", ecommunity_str(ecom));
+ vty_out(vty, "\n");
+ vty_out(vty, " Import-RTs:\n");
+ vty_out(vty, " ");
+ for (ALL_LIST_ELEMENTS_RO(bgp->vrf_import_rtl, node, ecom))
+ vty_out(vty, "%s ", ecommunity_str(ecom));
+ vty_out(vty, "\n");
+ vty_out(vty, " RD: %pRD\n", &bgp->vrf_prd);
+ } else {
+ json_object_string_add(json, "vrf", name);
+ json_object_string_addf(json, "local-ip", "%pI4",
+ &bgp->originator_ip);
+ json_object_int_add(json, "l3vni", bgp->l3vni);
+ json_object_string_add(
+ json, "rmac",
+ prefix_mac2str(&bgp->rmac, buf, sizeof(buf)));
+ json_object_string_add(
+ json, "vniFilter",
+ CHECK_FLAG(bgp->vrf_flags,
+ BGP_VRF_L3VNI_PREFIX_ROUTES_ONLY)
+ ? "prefix-routes-only"
+ : "none");
+ /* list of l2vnis */
+ for (ALL_LIST_ELEMENTS_RO(bgp->l2vnis, node, vpn))
+ json_object_array_add(json_vnis,
+ json_object_new_int(vpn->vni));
+ json_object_object_add(json, "l2vnis", json_vnis);
+
+ /* export rts */
+ for (ALL_LIST_ELEMENTS_RO(bgp->vrf_export_rtl, node, ecom))
+ json_object_array_add(
+ json_export_rts,
+ json_object_new_string(ecommunity_str(ecom)));
+ json_object_object_add(json, "export-rts", json_export_rts);
+
+ /* import rts */
+ for (ALL_LIST_ELEMENTS_RO(bgp->vrf_import_rtl, node, ecom))
+ json_object_array_add(
+ json_import_rts,
+ json_object_new_string(ecommunity_str(ecom)));
+ json_object_object_add(json, "import-rts", json_import_rts);
+ json_object_string_addf(json, "rd", "%pRD", &bgp->vrf_prd);
+ }
+
+ if (uj)
+ vty_json(vty, json);
+ return CMD_SUCCESS;
+}
+
+/* import/export rt for l3vni-vrf */
+DEFUN (bgp_evpn_vrf_rt,
+ bgp_evpn_vrf_rt_cmd,
+ "route-target <both|import|export> RT",
+ "Route Target\n"
+ "import and export\n"
+ "import\n"
+ "export\n"
+ "Route target (A.B.C.D:MN|EF:OPQR|GHJK:MN)\n")
+{
+ int rt_type;
+ struct bgp *bgp = VTY_GET_CONTEXT(bgp);
+ struct ecommunity *ecomadd = NULL;
+
+ if (!bgp)
+ return CMD_WARNING;
+
+ if (!strcmp(argv[1]->arg, "import"))
+ rt_type = RT_TYPE_IMPORT;
+ else if (!strcmp(argv[1]->arg, "export"))
+ rt_type = RT_TYPE_EXPORT;
+ else if (!strcmp(argv[1]->arg, "both"))
+ rt_type = RT_TYPE_BOTH;
+ else {
+ vty_out(vty, "%% Invalid Route Target type\n");
+ return CMD_WARNING;
+ }
+
+ ecomadd = ecommunity_str2com(argv[2]->arg, ECOMMUNITY_ROUTE_TARGET, 0);
+ if (!ecomadd) {
+ vty_out(vty, "%% Malformed Route Target list\n");
+ return CMD_WARNING;
+ }
+ ecommunity_str(ecomadd);
+
+ /* Add/update the import route-target */
+ if (rt_type == RT_TYPE_BOTH || rt_type == RT_TYPE_IMPORT) {
+ /* Do nothing if we already have this import route-target */
+ if (!bgp_evpn_rt_matches_existing(bgp->vrf_import_rtl, ecomadd))
+ bgp_evpn_configure_import_rt_for_vrf(bgp, ecomadd);
+ }
+
+ /* Add/update the export route-target */
+ if (rt_type == RT_TYPE_BOTH || rt_type == RT_TYPE_EXPORT) {
+ /* Do nothing if we already have this export route-target */
+ if (!bgp_evpn_rt_matches_existing(bgp->vrf_export_rtl, ecomadd))
+ bgp_evpn_configure_export_rt_for_vrf(bgp, ecomadd);
+ }
+
+ return CMD_SUCCESS;
+}
+
+DEFUN (no_bgp_evpn_vrf_rt,
+ no_bgp_evpn_vrf_rt_cmd,
+ "no route-target <both|import|export> RT",
+ NO_STR
+ "Route Target\n"
+ "import and export\n"
+ "import\n"
+ "export\n"
+ EVPN_ASN_IP_HELP_STR)
+{
+ struct bgp *bgp = VTY_GET_CONTEXT(bgp);
+ int rt_type, found_ecomdel;
+ struct ecommunity *ecomdel = NULL;
+
+ if (!bgp)
+ return CMD_WARNING;
+
+ if (!strcmp(argv[2]->arg, "import"))
+ rt_type = RT_TYPE_IMPORT;
+ else if (!strcmp(argv[2]->arg, "export"))
+ rt_type = RT_TYPE_EXPORT;
+ else if (!strcmp(argv[2]->arg, "both"))
+ rt_type = RT_TYPE_BOTH;
+ else {
+ vty_out(vty, "%% Invalid Route Target type\n");
+ return CMD_WARNING;
+ }
+
+ if (rt_type == RT_TYPE_IMPORT) {
+ if (!CHECK_FLAG(bgp->vrf_flags, BGP_VRF_IMPORT_RT_CFGD)) {
+ vty_out(vty,
+ "%% Import RT is not configured for this VRF\n");
+ return CMD_WARNING;
+ }
+ } else if (rt_type == RT_TYPE_EXPORT) {
+ if (!CHECK_FLAG(bgp->vrf_flags, BGP_VRF_EXPORT_RT_CFGD)) {
+ vty_out(vty,
+ "%% Export RT is not configured for this VRF\n");
+ return CMD_WARNING;
+ }
+ } else if (rt_type == RT_TYPE_BOTH) {
+ if (!CHECK_FLAG(bgp->vrf_flags, BGP_VRF_IMPORT_RT_CFGD)
+ && !CHECK_FLAG(bgp->vrf_flags, BGP_VRF_EXPORT_RT_CFGD)) {
+ vty_out(vty,
+ "%% Import/Export RT is not configured for this VRF\n");
+ return CMD_WARNING;
+ }
+ }
+
+ ecomdel = ecommunity_str2com(argv[3]->arg, ECOMMUNITY_ROUTE_TARGET, 0);
+ if (!ecomdel) {
+ vty_out(vty, "%% Malformed Route Target list\n");
+ return CMD_WARNING;
+ }
+ ecommunity_str(ecomdel);
+
+ if (rt_type == RT_TYPE_IMPORT) {
+ if (!bgp_evpn_rt_matches_existing(bgp->vrf_import_rtl,
+ ecomdel)) {
+ ecommunity_free(&ecomdel);
+ vty_out(vty,
+ "%% RT specified does not match configuration for this VRF\n");
+ return CMD_WARNING;
+ }
+ bgp_evpn_unconfigure_import_rt_for_vrf(bgp, ecomdel);
+ } else if (rt_type == RT_TYPE_EXPORT) {
+ if (!bgp_evpn_rt_matches_existing(bgp->vrf_export_rtl,
+ ecomdel)) {
+ ecommunity_free(&ecomdel);
+ vty_out(vty,
+ "%% RT specified does not match configuration for this VRF\n");
+ return CMD_WARNING;
+ }
+ bgp_evpn_unconfigure_export_rt_for_vrf(bgp, ecomdel);
+ } else if (rt_type == RT_TYPE_BOTH) {
+ found_ecomdel = 0;
+
+ if (bgp_evpn_rt_matches_existing(bgp->vrf_import_rtl,
+ ecomdel)) {
+ bgp_evpn_unconfigure_import_rt_for_vrf(bgp, ecomdel);
+ found_ecomdel = 1;
+ }
+
+ if (bgp_evpn_rt_matches_existing(bgp->vrf_export_rtl,
+ ecomdel)) {
+ bgp_evpn_unconfigure_export_rt_for_vrf(bgp, ecomdel);
+ found_ecomdel = 1;
+ }
+
+ if (!found_ecomdel) {
+ ecommunity_free(&ecomdel);
+ vty_out(vty,
+ "%% RT specified does not match configuration for this VRF\n");
+ return CMD_WARNING;
+ }
+ }
+
+ ecommunity_free(&ecomdel);
+ return CMD_SUCCESS;
+}
+
+DEFPY(bgp_evpn_ead_ess_frag_evi_limit, bgp_evpn_ead_es_frag_evi_limit_cmd,
+ "[no$no] ead-es-frag evi-limit (1-1000)$limit",
+ NO_STR
+ "EAD ES fragment config\n"
+ "EVIs per-fragment\n"
+ "limit\n")
+{
+ bgp_mh_info->evi_per_es_frag =
+ no ? BGP_EVPN_MAX_EVI_PER_ES_FRAG : limit;
+
+ return CMD_SUCCESS;
+}
+
+DEFUN(bgp_evpn_ead_es_rt, bgp_evpn_ead_es_rt_cmd,
+ "ead-es-route-target export RT",
+ "EAD ES Route Target\n"
+ "export\n"
+ "Route target (A.B.C.D:MN|EF:OPQR|GHJK:MN)\n")
+{
+ struct bgp *bgp = VTY_GET_CONTEXT(bgp);
+ struct ecommunity *ecomadd = NULL;
+
+ if (!bgp)
+ return CMD_WARNING;
+
+ if (!EVPN_ENABLED(bgp)) {
+ vty_out(vty, "This command is only supported under EVPN VRF\n");
+ return CMD_WARNING;
+ }
+
+ /* Add/update the export route-target */
+ ecomadd = ecommunity_str2com(argv[2]->arg, ECOMMUNITY_ROUTE_TARGET, 0);
+ if (!ecomadd) {
+ vty_out(vty, "%% Malformed Route Target list\n");
+ return CMD_WARNING;
+ }
+ ecommunity_str(ecomadd);
+
+ /* Do nothing if we already have this export route-target */
+ if (!bgp_evpn_rt_matches_existing(bgp_mh_info->ead_es_export_rtl,
+ ecomadd))
+ bgp_evpn_mh_config_ead_export_rt(bgp, ecomadd, false);
+
+ return CMD_SUCCESS;
+}
+
+DEFUN(no_bgp_evpn_ead_es_rt, no_bgp_evpn_ead_es_rt_cmd,
+ "no ead-es-route-target export RT",
+ NO_STR
+ "EAD ES Route Target\n"
+ "export\n" EVPN_ASN_IP_HELP_STR)
+{
+ struct bgp *bgp = VTY_GET_CONTEXT(bgp);
+ struct ecommunity *ecomdel = NULL;
+
+ if (!bgp)
+ return CMD_WARNING;
+
+ if (!EVPN_ENABLED(bgp)) {
+ vty_out(vty, "This command is only supported under EVPN VRF\n");
+ return CMD_WARNING;
+ }
+
+ ecomdel = ecommunity_str2com(argv[3]->arg, ECOMMUNITY_ROUTE_TARGET, 0);
+ if (!ecomdel) {
+ vty_out(vty, "%% Malformed Route Target list\n");
+ return CMD_WARNING;
+ }
+ ecommunity_str(ecomdel);
+
+ if (!bgp_evpn_rt_matches_existing(bgp_mh_info->ead_es_export_rtl,
+ ecomdel)) {
+ ecommunity_free(&ecomdel);
+ vty_out(vty,
+ "%% RT specified does not match EAD-ES RT configuration\n");
+ return CMD_WARNING;
+ }
+ bgp_evpn_mh_config_ead_export_rt(bgp, ecomdel, true);
+
+ return CMD_SUCCESS;
+}
+
+DEFUN (bgp_evpn_vni_rt,
+ bgp_evpn_vni_rt_cmd,
+ "route-target <both|import|export> RT",
+ "Route Target\n"
+ "import and export\n"
+ "import\n"
+ "export\n"
+ "Route target (A.B.C.D:MN|EF:OPQR|GHJK:MN)\n")
+{
+ struct bgp *bgp = VTY_GET_CONTEXT(bgp);
+ VTY_DECLVAR_CONTEXT_SUB(bgpevpn, vpn);
+ int rt_type;
+ struct ecommunity *ecomadd = NULL;
+
+ if (!bgp)
+ return CMD_WARNING;
+
+ if (!EVPN_ENABLED(bgp)) {
+ vty_out(vty,
+ "This command is only supported under EVPN VRF\n");
+ return CMD_WARNING;
+ }
+
+ if (!strcmp(argv[1]->text, "import"))
+ rt_type = RT_TYPE_IMPORT;
+ else if (!strcmp(argv[1]->text, "export"))
+ rt_type = RT_TYPE_EXPORT;
+ else if (!strcmp(argv[1]->text, "both"))
+ rt_type = RT_TYPE_BOTH;
+ else {
+ vty_out(vty, "%% Invalid Route Target type\n");
+ return CMD_WARNING;
+ }
+
+ /* Add/update the import route-target */
+ if (rt_type == RT_TYPE_BOTH || rt_type == RT_TYPE_IMPORT) {
+ /* Note that first of the two RTs is created for "both" type */
+ ecomadd = ecommunity_str2com(argv[2]->arg,
+ ECOMMUNITY_ROUTE_TARGET, 0);
+ if (!ecomadd) {
+ vty_out(vty, "%% Malformed Route Target list\n");
+ return CMD_WARNING;
+ }
+ ecommunity_str(ecomadd);
+
+ /* Do nothing if we already have this import route-target */
+ if (!bgp_evpn_rt_matches_existing(vpn->import_rtl, ecomadd))
+ evpn_configure_import_rt(bgp, vpn, ecomadd);
+ }
+
+ /* Add/update the export route-target */
+ if (rt_type == RT_TYPE_BOTH || rt_type == RT_TYPE_EXPORT) {
+ /* Note that second of the two RTs is created for "both" type */
+ ecomadd = ecommunity_str2com(argv[2]->arg,
+ ECOMMUNITY_ROUTE_TARGET, 0);
+ if (!ecomadd) {
+ vty_out(vty, "%% Malformed Route Target list\n");
+ return CMD_WARNING;
+ }
+ ecommunity_str(ecomadd);
+
+ /* Do nothing if we already have this export route-target */
+ if (!bgp_evpn_rt_matches_existing(vpn->export_rtl, ecomadd))
+ evpn_configure_export_rt(bgp, vpn, ecomadd);
+ }
+
+ return CMD_SUCCESS;
+}
+
+DEFUN (no_bgp_evpn_vni_rt,
+ no_bgp_evpn_vni_rt_cmd,
+ "no route-target <both|import|export> RT",
+ NO_STR
+ "Route Target\n"
+ "import and export\n"
+ "import\n"
+ "export\n"
+ EVPN_ASN_IP_HELP_STR)
+{
+ struct bgp *bgp = VTY_GET_CONTEXT(bgp);
+ VTY_DECLVAR_CONTEXT_SUB(bgpevpn, vpn);
+ int rt_type, found_ecomdel;
+ struct ecommunity *ecomdel = NULL;
+
+ if (!bgp)
+ return CMD_WARNING;
+
+ if (!EVPN_ENABLED(bgp)) {
+ vty_out(vty,
+ "This command is only supported under EVPN VRF\n");
+ return CMD_WARNING;
+ }
+
+ if (!strcmp(argv[2]->text, "import"))
+ rt_type = RT_TYPE_IMPORT;
+ else if (!strcmp(argv[2]->text, "export"))
+ rt_type = RT_TYPE_EXPORT;
+ else if (!strcmp(argv[2]->text, "both"))
+ rt_type = RT_TYPE_BOTH;
+ else {
+ vty_out(vty, "%% Invalid Route Target type\n");
+ return CMD_WARNING;
+ }
+
+ /* The user did "no route-target import", check to see if there are any
+ * import route-targets configured. */
+ if (rt_type == RT_TYPE_IMPORT) {
+ if (!is_import_rt_configured(vpn)) {
+ vty_out(vty,
+ "%% Import RT is not configured for this VNI\n");
+ return CMD_WARNING;
+ }
+ } else if (rt_type == RT_TYPE_EXPORT) {
+ if (!is_export_rt_configured(vpn)) {
+ vty_out(vty,
+ "%% Export RT is not configured for this VNI\n");
+ return CMD_WARNING;
+ }
+ } else if (rt_type == RT_TYPE_BOTH) {
+ if (!is_import_rt_configured(vpn)
+ && !is_export_rt_configured(vpn)) {
+ vty_out(vty,
+ "%% Import/Export RT is not configured for this VNI\n");
+ return CMD_WARNING;
+ }
+ }
+
+ ecomdel = ecommunity_str2com(argv[3]->arg, ECOMMUNITY_ROUTE_TARGET, 0);
+ if (!ecomdel) {
+ vty_out(vty, "%% Malformed Route Target list\n");
+ return CMD_WARNING;
+ }
+ ecommunity_str(ecomdel);
+
+ if (rt_type == RT_TYPE_IMPORT) {
+ if (!bgp_evpn_rt_matches_existing(vpn->import_rtl, ecomdel)) {
+ ecommunity_free(&ecomdel);
+ vty_out(vty,
+ "%% RT specified does not match configuration for this VNI\n");
+ return CMD_WARNING;
+ }
+ evpn_unconfigure_import_rt(bgp, vpn, ecomdel);
+ } else if (rt_type == RT_TYPE_EXPORT) {
+ if (!bgp_evpn_rt_matches_existing(vpn->export_rtl, ecomdel)) {
+ ecommunity_free(&ecomdel);
+ vty_out(vty,
+ "%% RT specified does not match configuration for this VNI\n");
+ return CMD_WARNING;
+ }
+ evpn_unconfigure_export_rt(bgp, vpn, ecomdel);
+ } else if (rt_type == RT_TYPE_BOTH) {
+ found_ecomdel = 0;
+
+ if (bgp_evpn_rt_matches_existing(vpn->import_rtl, ecomdel)) {
+ evpn_unconfigure_import_rt(bgp, vpn, ecomdel);
+ found_ecomdel = 1;
+ }
+
+ if (bgp_evpn_rt_matches_existing(vpn->export_rtl, ecomdel)) {
+ evpn_unconfigure_export_rt(bgp, vpn, ecomdel);
+ found_ecomdel = 1;
+ }
+
+ if (!found_ecomdel) {
+ ecommunity_free(&ecomdel);
+ vty_out(vty,
+ "%% RT specified does not match configuration for this VNI\n");
+ return CMD_WARNING;
+ }
+ }
+
+ return CMD_SUCCESS;
+}
+
+DEFUN (no_bgp_evpn_vni_rt_without_val,
+ no_bgp_evpn_vni_rt_without_val_cmd,
+ "no route-target <import|export>",
+ NO_STR
+ "Route Target\n"
+ "import\n"
+ "export\n")
+{
+ struct bgp *bgp = VTY_GET_CONTEXT(bgp);
+ VTY_DECLVAR_CONTEXT_SUB(bgpevpn, vpn);
+ int rt_type;
+
+ if (!bgp)
+ return CMD_WARNING;
+
+ if (!EVPN_ENABLED(bgp)) {
+ vty_out(vty,
+ "This command is only supported under EVPN VRF\n");
+ return CMD_WARNING;
+ }
+
+ if (!strcmp(argv[2]->text, "import")) {
+ rt_type = RT_TYPE_IMPORT;
+ } else if (!strcmp(argv[2]->text, "export")) {
+ rt_type = RT_TYPE_EXPORT;
+ } else {
+ vty_out(vty, "%% Invalid Route Target type\n");
+ return CMD_WARNING;
+ }
+
+ /* Check if we should disallow. */
+ if (rt_type == RT_TYPE_IMPORT) {
+ if (!is_import_rt_configured(vpn)) {
+ vty_out(vty,
+ "%% Import RT is not configured for this VNI\n");
+ return CMD_WARNING;
+ }
+ } else {
+ if (!is_export_rt_configured(vpn)) {
+ vty_out(vty,
+ "%% Export RT is not configured for this VNI\n");
+ return CMD_WARNING;
+ }
+ }
+
+ /* Unconfigure the RT. */
+ if (rt_type == RT_TYPE_IMPORT)
+ evpn_unconfigure_import_rt(bgp, vpn, NULL);
+ else
+ evpn_unconfigure_export_rt(bgp, vpn, NULL);
+ return CMD_SUCCESS;
+}
+
+static int vni_cmp(const void **a, const void **b)
+{
+ const struct bgpevpn *first = *a;
+ const struct bgpevpn *secnd = *b;
+
+ return secnd->vni - first->vni;
+}
+
+/*
+ * Output EVPN configuration information.
+ */
+void bgp_config_write_evpn_info(struct vty *vty, struct bgp *bgp, afi_t afi,
+ safi_t safi)
+{
+ char buf2[INET6_ADDRSTRLEN];
+
+ if (bgp->advertise_all_vni)
+ vty_out(vty, " advertise-all-vni\n");
+
+ if (hashcount(bgp->vnihash)) {
+ struct list *vnilist = hash_to_list(bgp->vnihash);
+ struct listnode *ln;
+ struct bgpevpn *data;
+
+ list_sort(vnilist, vni_cmp);
+ for (ALL_LIST_ELEMENTS_RO(vnilist, ln, data))
+ write_vni_config(vty, data);
+
+ list_delete(&vnilist);
+ }
+
+ if (bgp->advertise_autort_rfc8365)
+ vty_out(vty, " autort rfc8365-compatible\n");
+
+ if (bgp->advertise_gw_macip)
+ vty_out(vty, " advertise-default-gw\n");
+
+ if (bgp->evpn_info->advertise_svi_macip)
+ vty_out(vty, " advertise-svi-ip\n");
+
+ if (bgp->resolve_overlay_index)
+ vty_out(vty, " enable-resolve-overlay-index\n");
+
+ if (bgp_mh_info->evi_per_es_frag != BGP_EVPN_MAX_EVI_PER_ES_FRAG)
+ vty_out(vty, " ead-es-frag evi-limit %u\n",
+ bgp_mh_info->evi_per_es_frag);
+
+ if (bgp_mh_info->host_routes_use_l3nhg !=
+ BGP_EVPN_MH_USE_ES_L3NHG_DEF) {
+ if (bgp_mh_info->host_routes_use_l3nhg)
+ vty_out(vty, " use-es-l3nhg\n");
+ else
+ vty_out(vty, " no use-es-l3nhg\n");
+ }
+
+ if (bgp_mh_info->ead_evi_rx != BGP_EVPN_MH_EAD_EVI_RX_DEF) {
+ if (bgp_mh_info->ead_evi_rx)
+ vty_out(vty, " no disable-ead-evi-rx\n");
+ else
+ vty_out(vty, " disable-ead-evi-rx\n");
+ }
+
+ if (bgp_mh_info->ead_evi_tx != BGP_EVPN_MH_EAD_EVI_TX_DEF) {
+ if (bgp_mh_info->ead_evi_tx)
+ vty_out(vty, " no disable-ead-evi-tx\n");
+ else
+ vty_out(vty, " disable-ead-evi-tx\n");
+ }
+
+ if (!bgp->evpn_info->dup_addr_detect)
+ vty_out(vty, " no dup-addr-detection\n");
+
+ if (bgp->evpn_info->dad_max_moves !=
+ EVPN_DAD_DEFAULT_MAX_MOVES ||
+ bgp->evpn_info->dad_time != EVPN_DAD_DEFAULT_TIME)
+ vty_out(vty, " dup-addr-detection max-moves %u time %u\n",
+ bgp->evpn_info->dad_max_moves,
+ bgp->evpn_info->dad_time);
+
+ if (bgp->evpn_info->dad_freeze) {
+ if (bgp->evpn_info->dad_freeze_time)
+ vty_out(vty,
+ " dup-addr-detection freeze %u\n",
+ bgp->evpn_info->dad_freeze_time);
+ else
+ vty_out(vty,
+ " dup-addr-detection freeze permanent\n");
+ }
+
+ if (bgp->vxlan_flood_ctrl == VXLAN_FLOOD_DISABLED)
+ vty_out(vty, " flooding disable\n");
+
+ if (CHECK_FLAG(bgp->af_flags[AFI_L2VPN][SAFI_EVPN],
+ BGP_L2VPN_EVPN_ADV_IPV4_UNICAST)) {
+ if (bgp->adv_cmd_rmap[AFI_IP][SAFI_UNICAST].name)
+ vty_out(vty, " advertise ipv4 unicast route-map %s\n",
+ bgp->adv_cmd_rmap[AFI_IP][SAFI_UNICAST].name);
+ else
+ vty_out(vty,
+ " advertise ipv4 unicast\n");
+ } else if (CHECK_FLAG(bgp->af_flags[AFI_L2VPN][SAFI_EVPN],
+ BGP_L2VPN_EVPN_ADV_IPV4_UNICAST_GW_IP)) {
+ if (bgp->adv_cmd_rmap[AFI_IP][SAFI_UNICAST].name)
+ vty_out(vty,
+ " advertise ipv4 unicast gateway-ip route-map %s\n",
+ bgp->adv_cmd_rmap[AFI_IP][SAFI_UNICAST].name);
+ else
+ vty_out(vty, " advertise ipv4 unicast gateway-ip\n");
+ }
+
+ /* EAD ES export route-target */
+ if (listcount(bgp_mh_info->ead_es_export_rtl)) {
+ struct ecommunity *ecom;
+ char *ecom_str;
+ struct listnode *node;
+
+ for (ALL_LIST_ELEMENTS_RO(bgp_mh_info->ead_es_export_rtl, node,
+ ecom)) {
+
+ ecom_str = ecommunity_ecom2str(
+ ecom, ECOMMUNITY_FORMAT_ROUTE_MAP, 0);
+ vty_out(vty, " ead-es-route-target export %s\n",
+ ecom_str);
+ XFREE(MTYPE_ECOMMUNITY_STR, ecom_str);
+ }
+ }
+
+ if (CHECK_FLAG(bgp->af_flags[AFI_L2VPN][SAFI_EVPN],
+ BGP_L2VPN_EVPN_ADV_IPV6_UNICAST)) {
+ if (bgp->adv_cmd_rmap[AFI_IP6][SAFI_UNICAST].name)
+ vty_out(vty,
+ " advertise ipv6 unicast route-map %s\n",
+ bgp->adv_cmd_rmap[AFI_IP6][SAFI_UNICAST].name);
+ else
+ vty_out(vty,
+ " advertise ipv6 unicast\n");
+ } else if (CHECK_FLAG(bgp->af_flags[AFI_L2VPN][SAFI_EVPN],
+ BGP_L2VPN_EVPN_ADV_IPV6_UNICAST_GW_IP)) {
+ if (bgp->adv_cmd_rmap[AFI_IP6][SAFI_UNICAST].name)
+ vty_out(vty,
+ " advertise ipv6 unicast gateway-ip route-map %s\n",
+ bgp->adv_cmd_rmap[AFI_IP6][SAFI_UNICAST].name);
+ else
+ vty_out(vty, " advertise ipv6 unicast gateway-ip\n");
+ }
+
+ if (CHECK_FLAG(bgp->af_flags[AFI_L2VPN][SAFI_EVPN],
+ BGP_L2VPN_EVPN_DEFAULT_ORIGINATE_IPV4))
+ vty_out(vty, " default-originate ipv4\n");
+
+ if (CHECK_FLAG(bgp->af_flags[AFI_L2VPN][SAFI_EVPN],
+ BGP_L2VPN_EVPN_DEFAULT_ORIGINATE_IPV6))
+ vty_out(vty, " default-originate ipv6\n");
+
+ if (bgp->inst_type == BGP_INSTANCE_TYPE_VRF) {
+ if (!bgp->evpn_info->advertise_pip)
+ vty_out(vty, " no advertise-pip\n");
+ if (bgp->evpn_info->advertise_pip) {
+ if (bgp->evpn_info->pip_ip_static.s_addr
+ != INADDR_ANY) {
+ vty_out(vty, " advertise-pip ip %s",
+ inet_ntop(AF_INET,
+ &bgp->evpn_info->pip_ip_static,
+ buf2, INET_ADDRSTRLEN));
+ if (!is_zero_mac(&(
+ bgp->evpn_info->pip_rmac_static))) {
+ char buf[ETHER_ADDR_STRLEN];
+
+ vty_out(vty, " mac %s",
+ prefix_mac2str(
+ &bgp->evpn_info
+ ->pip_rmac,
+ buf, sizeof(buf)));
+ }
+ vty_out(vty, "\n");
+ }
+ }
+ }
+ if (CHECK_FLAG(bgp->vrf_flags, BGP_VRF_RD_CFGD))
+ vty_out(vty, " rd %pRD\n", &bgp->vrf_prd);
+
+ /* import route-target */
+ if (CHECK_FLAG(bgp->vrf_flags, BGP_VRF_IMPORT_RT_CFGD)) {
+ char *ecom_str;
+ struct listnode *node, *nnode;
+ struct ecommunity *ecom;
+
+ for (ALL_LIST_ELEMENTS(bgp->vrf_import_rtl, node, nnode,
+ ecom)) {
+ ecom_str = ecommunity_ecom2str(
+ ecom, ECOMMUNITY_FORMAT_ROUTE_MAP, 0);
+ vty_out(vty, " route-target import %s\n", ecom_str);
+ XFREE(MTYPE_ECOMMUNITY_STR, ecom_str);
+ }
+ }
+
+ /* export route-target */
+ if (CHECK_FLAG(bgp->vrf_flags, BGP_VRF_EXPORT_RT_CFGD)) {
+ char *ecom_str;
+ struct listnode *node, *nnode;
+ struct ecommunity *ecom;
+
+ for (ALL_LIST_ELEMENTS(bgp->vrf_export_rtl, node, nnode,
+ ecom)) {
+ ecom_str = ecommunity_ecom2str(
+ ecom, ECOMMUNITY_FORMAT_ROUTE_MAP, 0);
+ vty_out(vty, " route-target export %s\n", ecom_str);
+ XFREE(MTYPE_ECOMMUNITY_STR, ecom_str);
+ }
+ }
+}
+
+void bgp_ethernetvpn_init(void)
+{
+ install_element(VIEW_NODE, &show_ip_bgp_l2vpn_evpn_cmd);
+ install_element(VIEW_NODE, &show_ip_bgp_l2vpn_evpn_rd_cmd);
+ install_element(VIEW_NODE, &show_ip_bgp_l2vpn_evpn_all_tags_cmd);
+ install_element(VIEW_NODE, &show_ip_bgp_l2vpn_evpn_rd_tags_cmd);
+ install_element(VIEW_NODE,
+ &show_ip_bgp_l2vpn_evpn_neighbor_routes_cmd);
+ install_element(VIEW_NODE,
+ &show_ip_bgp_l2vpn_evpn_rd_neighbor_routes_cmd);
+ install_element(
+ VIEW_NODE,
+ &show_ip_bgp_l2vpn_evpn_neighbor_advertised_routes_cmd);
+ install_element(
+ VIEW_NODE,
+ &show_ip_bgp_l2vpn_evpn_rd_neighbor_advertised_routes_cmd);
+ install_element(VIEW_NODE, &show_ip_bgp_evpn_rd_overlay_cmd);
+ install_element(VIEW_NODE, &show_ip_bgp_l2vpn_evpn_all_overlay_cmd);
+ install_element(BGP_EVPN_NODE, &no_evpnrt5_network_cmd);
+ install_element(BGP_EVPN_NODE, &evpnrt5_network_cmd);
+ install_element(BGP_EVPN_NODE, &bgp_evpn_advertise_all_vni_cmd);
+ install_element(BGP_EVPN_NODE, &no_bgp_evpn_advertise_all_vni_cmd);
+ install_element(BGP_EVPN_NODE, &bgp_evpn_advertise_autort_rfc8365_cmd);
+ install_element(BGP_EVPN_NODE, &no_bgp_evpn_advertise_autort_rfc8365_cmd);
+ install_element(BGP_EVPN_NODE, &bgp_evpn_advertise_default_gw_cmd);
+ install_element(BGP_EVPN_NODE, &no_bgp_evpn_advertise_default_gw_cmd);
+ install_element(BGP_EVPN_NODE, &bgp_evpn_advertise_svi_ip_cmd);
+ install_element(BGP_EVPN_NODE, &bgp_evpn_advertise_type5_cmd);
+ install_element(BGP_EVPN_NODE, &no_bgp_evpn_advertise_type5_cmd);
+ install_element(BGP_EVPN_NODE, &bgp_evpn_default_originate_cmd);
+ install_element(BGP_EVPN_NODE, &no_bgp_evpn_default_originate_cmd);
+ install_element(BGP_EVPN_NODE, &dup_addr_detection_cmd);
+ install_element(BGP_EVPN_NODE, &dup_addr_detection_auto_recovery_cmd);
+ install_element(BGP_EVPN_NODE, &no_dup_addr_detection_cmd);
+ install_element(BGP_EVPN_NODE, &bgp_evpn_flood_control_cmd);
+ install_element(BGP_EVPN_NODE, &bgp_evpn_advertise_pip_ip_mac_cmd);
+ install_element(BGP_EVPN_NODE, &bgp_evpn_use_es_l3nhg_cmd);
+ install_element(BGP_EVPN_NODE, &bgp_evpn_ead_evi_rx_disable_cmd);
+ install_element(BGP_EVPN_NODE, &bgp_evpn_ead_evi_tx_disable_cmd);
+ install_element(BGP_EVPN_NODE,
+ &bgp_evpn_enable_resolve_overlay_index_cmd);
+
+ /* test commands */
+ install_element(BGP_EVPN_NODE, &test_es_add_cmd);
+ install_element(BGP_EVPN_NODE, &test_es_vni_add_cmd);
+
+ /* "show bgp l2vpn evpn" commands. */
+ install_element(VIEW_NODE, &show_bgp_l2vpn_evpn_es_cmd);
+ install_element(VIEW_NODE, &show_bgp_l2vpn_evpn_es_evi_cmd);
+ install_element(VIEW_NODE, &show_bgp_l2vpn_evpn_es_vrf_cmd);
+ install_element(VIEW_NODE, &show_bgp_l2vpn_evpn_nh_cmd);
+ install_element(VIEW_NODE, &show_bgp_l2vpn_evpn_vni_cmd);
+ install_element(VIEW_NODE, &show_bgp_l2vpn_evpn_vni_remote_ip_hash_cmd);
+ install_element(VIEW_NODE, &show_bgp_l2vpn_evpn_vni_svi_hash_cmd);
+ install_element(VIEW_NODE, &show_bgp_l2vpn_evpn_summary_cmd);
+ install_element(VIEW_NODE, &show_bgp_l2vpn_evpn_route_cmd);
+ install_element(VIEW_NODE, &show_bgp_l2vpn_evpn_route_rd_cmd);
+ install_element(VIEW_NODE, &show_bgp_l2vpn_evpn_route_rd_macip_cmd);
+ install_element(VIEW_NODE, &show_bgp_l2vpn_evpn_route_esi_cmd);
+ install_element(VIEW_NODE, &show_bgp_l2vpn_evpn_route_vni_cmd);
+ install_element(VIEW_NODE,
+ &show_bgp_l2vpn_evpn_route_vni_multicast_cmd);
+ install_element(VIEW_NODE, &show_bgp_l2vpn_evpn_route_vni_macip_cmd);
+ install_element(VIEW_NODE, &show_bgp_l2vpn_evpn_route_vni_all_cmd);
+ install_element(VIEW_NODE,
+ &show_bgp_l2vpn_evpn_route_mac_ip_evi_es_cmd);
+ install_element(VIEW_NODE,
+ &show_bgp_l2vpn_evpn_route_mac_ip_global_es_cmd);
+ install_element(VIEW_NODE, &show_bgp_l2vpn_evpn_import_rt_cmd);
+ install_element(VIEW_NODE, &show_bgp_l2vpn_evpn_vrf_import_rt_cmd);
+
+ /* "show bgp evpn" commands. */
+ install_element(VIEW_NODE, &show_bgp_evpn_vni_cmd);
+ install_element(VIEW_NODE, &show_bgp_evpn_summary_cmd);
+ install_element(VIEW_NODE, &show_bgp_evpn_route_cmd);
+ install_element(VIEW_NODE, &show_bgp_evpn_route_rd_cmd);
+ install_element(VIEW_NODE, &show_bgp_evpn_route_rd_macip_cmd);
+ install_element(VIEW_NODE, &show_bgp_evpn_route_vni_cmd);
+ install_element(VIEW_NODE, &show_bgp_evpn_route_vni_multicast_cmd);
+ install_element(VIEW_NODE, &show_bgp_evpn_route_vni_macip_cmd);
+ install_element(VIEW_NODE, &show_bgp_evpn_route_vni_all_cmd);
+ install_element(VIEW_NODE, &show_bgp_evpn_import_rt_cmd);
+ install_element(VIEW_NODE, &show_bgp_vrf_l3vni_info_cmd);
+ install_element(VIEW_NODE, &show_bgp_l2vpn_evpn_com_cmd);
+
+ install_element(BGP_EVPN_NODE, &bgp_evpn_vni_cmd);
+ install_element(BGP_EVPN_NODE, &no_bgp_evpn_vni_cmd);
+ install_element(BGP_EVPN_VNI_NODE, &exit_vni_cmd);
+ install_element(BGP_EVPN_VNI_NODE, &bgp_evpn_vni_rd_cmd);
+ install_element(BGP_EVPN_VNI_NODE, &no_bgp_evpn_vni_rd_cmd);
+ install_element(BGP_EVPN_VNI_NODE, &no_bgp_evpn_vni_rd_without_val_cmd);
+ install_element(BGP_EVPN_VNI_NODE, &bgp_evpn_vni_rt_cmd);
+ install_element(BGP_EVPN_VNI_NODE, &no_bgp_evpn_vni_rt_cmd);
+ install_element(BGP_EVPN_VNI_NODE, &no_bgp_evpn_vni_rt_without_val_cmd);
+ install_element(BGP_EVPN_NODE, &bgp_evpn_vrf_rd_cmd);
+ install_element(BGP_EVPN_NODE, &no_bgp_evpn_vrf_rd_cmd);
+ install_element(BGP_NODE, &no_bgp_evpn_vrf_rd_without_val_cmd);
+ install_element(BGP_EVPN_NODE, &bgp_evpn_vrf_rt_cmd);
+ install_element(BGP_EVPN_NODE, &no_bgp_evpn_vrf_rt_cmd);
+ install_element(BGP_EVPN_NODE, &bgp_evpn_ead_es_rt_cmd);
+ install_element(BGP_EVPN_NODE, &no_bgp_evpn_ead_es_rt_cmd);
+ install_element(BGP_EVPN_NODE, &bgp_evpn_ead_es_frag_evi_limit_cmd);
+ install_element(BGP_EVPN_VNI_NODE, &bgp_evpn_advertise_svi_ip_vni_cmd);
+ install_element(BGP_EVPN_VNI_NODE,
+ &bgp_evpn_advertise_default_gw_vni_cmd);
+ install_element(BGP_EVPN_VNI_NODE,
+ &no_bgp_evpn_advertise_default_gw_vni_cmd);
+ install_element(BGP_EVPN_VNI_NODE, &bgp_evpn_advertise_vni_subnet_cmd);
+ install_element(BGP_EVPN_VNI_NODE,
+ &no_bgp_evpn_advertise_vni_subnet_cmd);
+}
diff --git a/bgpd/bgp_evpn_vty.h b/bgpd/bgp_evpn_vty.h
new file mode 100644
index 0000000..137365d
--- /dev/null
+++ b/bgpd/bgp_evpn_vty.h
@@ -0,0 +1,39 @@
+/* EVPN VTY functions to EVPN
+ * Copyright (C) 2017 6WIND
+ *
+ * This file is part of FRRouting.
+ *
+ * FRRouting is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License as published by the
+ * Free Software Foundation; either version 2, or (at your option) any
+ * later version.
+ *
+ * FRRouting is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program; see the file COPYING; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
+ */
+
+#ifndef _FRR_BGP_EVPN_VTY_H
+#define _FRR_BGP_EVPN_VTY_H
+
+extern void bgp_config_write_evpn_info(struct vty *vty, struct bgp *bgp,
+ afi_t afi, safi_t safi);
+extern void bgp_ethernetvpn_init(void);
+
+#define L2VPN_HELP_STR "Layer 2 Virtual Private Network\n"
+#define EVPN_HELP_STR "Ethernet Virtual Private Network\n"
+
+extern int argv_find_and_parse_oly_idx(struct cmd_token **argv, int argc,
+ int *oly_idx,
+ enum overlay_index_type *oly);
+
+/* Parse type from "type <ead|1|...>", return -1 on failure */
+extern int bgp_evpn_cli_parse_type(int *type, struct cmd_token **argv,
+ int argc);
+
+#endif /* _QUAGGA_BGP_EVPN_VTY_H */
diff --git a/bgpd/bgp_filter.c b/bgpd/bgp_filter.c
new file mode 100644
index 0000000..8921622
--- /dev/null
+++ b/bgpd/bgp_filter.c
@@ -0,0 +1,793 @@
+/* AS path filter list.
+ * Copyright (C) 1999 Kunihiro Ishiguro
+ *
+ * This file is part of GNU Zebra.
+ *
+ * GNU Zebra is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License as published by the
+ * Free Software Foundation; either version 2, or (at your option) any
+ * later version.
+ *
+ * GNU Zebra is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program; see the file COPYING; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
+ */
+
+#include <zebra.h>
+
+#include "command.h"
+#include "log.h"
+#include "memory.h"
+#include "buffer.h"
+#include "queue.h"
+#include "filter.h"
+
+#include "bgpd/bgpd.h"
+#include "bgpd/bgp_aspath.h"
+#include "bgpd/bgp_regex.h"
+#include "bgpd/bgp_filter.h"
+
+/* List of AS filter list. */
+struct as_list_list {
+ struct as_list *head;
+ struct as_list *tail;
+};
+
+/* AS path filter master. */
+struct as_list_master {
+ /* List of access_list which name is string. */
+ struct as_list_list str;
+
+ /* Hook function which is executed when new access_list is added. */
+ void (*add_hook)(char *);
+
+ /* Hook function which is executed when access_list is deleted. */
+ void (*delete_hook)(const char *);
+};
+
+/* Element of AS path filter. */
+struct as_filter {
+ struct as_filter *next;
+ struct as_filter *prev;
+
+ enum as_filter_type type;
+
+ regex_t *reg;
+ char *reg_str;
+
+ /* Sequence number. */
+ int64_t seq;
+};
+
+/* AS path filter list. */
+struct as_list {
+ char *name;
+
+ struct as_list *next;
+ struct as_list *prev;
+
+ struct as_filter *head;
+ struct as_filter *tail;
+};
+
+
+/* Calculate new sequential number. */
+static int64_t bgp_alist_new_seq_get(struct as_list *list)
+{
+ int64_t maxseq;
+ int64_t newseq;
+ struct as_filter *entry;
+
+ maxseq = 0;
+
+ for (entry = list->head; entry; entry = entry->next) {
+ if (maxseq < entry->seq)
+ maxseq = entry->seq;
+ }
+
+ newseq = ((maxseq / 5) * 5) + 5;
+
+ return (newseq > UINT_MAX) ? UINT_MAX : newseq;
+}
+
+/* Return as-list entry which has same seq number. */
+static struct as_filter *bgp_aslist_seq_check(struct as_list *list, int64_t seq)
+{
+ struct as_filter *entry;
+
+ for (entry = list->head; entry; entry = entry->next)
+ if (entry->seq == seq)
+ return entry;
+
+ return NULL;
+}
+
+/* as-path access-list 10 permit AS1. */
+
+static struct as_list_master as_list_master = {{NULL, NULL},
+ NULL,
+ NULL};
+
+/* Allocate new AS filter. */
+static struct as_filter *as_filter_new(void)
+{
+ return XCALLOC(MTYPE_AS_FILTER, sizeof(struct as_filter));
+}
+
+/* Free allocated AS filter. */
+static void as_filter_free(struct as_filter *asfilter)
+{
+ if (asfilter->reg)
+ bgp_regex_free(asfilter->reg);
+ XFREE(MTYPE_AS_FILTER_STR, asfilter->reg_str);
+ XFREE(MTYPE_AS_FILTER, asfilter);
+}
+
+/* Make new AS filter. */
+static struct as_filter *as_filter_make(regex_t *reg, const char *reg_str,
+ enum as_filter_type type)
+{
+ struct as_filter *asfilter;
+
+ asfilter = as_filter_new();
+ asfilter->reg = reg;
+ asfilter->type = type;
+ asfilter->reg_str = XSTRDUP(MTYPE_AS_FILTER_STR, reg_str);
+
+ return asfilter;
+}
+
+static struct as_filter *as_filter_lookup(struct as_list *aslist,
+ const char *reg_str,
+ enum as_filter_type type)
+{
+ struct as_filter *asfilter;
+
+ for (asfilter = aslist->head; asfilter; asfilter = asfilter->next)
+ if (strcmp(reg_str, asfilter->reg_str) == 0)
+ return asfilter;
+ return NULL;
+}
+
+static void as_filter_entry_replace(struct as_list *list,
+ struct as_filter *replace,
+ struct as_filter *entry)
+{
+ if (replace->next) {
+ entry->next = replace->next;
+ replace->next->prev = entry;
+ } else {
+ entry->next = NULL;
+ list->tail = entry;
+ }
+
+ if (replace->prev) {
+ entry->prev = replace->prev;
+ replace->prev->next = entry;
+ } else {
+ entry->prev = NULL;
+ list->head = entry;
+ }
+
+ as_filter_free(replace);
+}
+
+static void as_list_filter_add(struct as_list *aslist,
+ struct as_filter *asfilter)
+{
+ struct as_filter *point;
+ struct as_filter *replace;
+
+ if (aslist->tail && asfilter->seq > aslist->tail->seq)
+ point = NULL;
+ else {
+ replace = bgp_aslist_seq_check(aslist, asfilter->seq);
+ if (replace) {
+ as_filter_entry_replace(aslist, replace, asfilter);
+ goto hook;
+ }
+
+ /* Check insert point. */
+ for (point = aslist->head; point; point = point->next)
+ if (point->seq >= asfilter->seq)
+ break;
+ }
+
+ asfilter->next = point;
+
+ if (point) {
+ if (point->prev)
+ point->prev->next = asfilter;
+ else
+ aslist->head = asfilter;
+
+ asfilter->prev = point->prev;
+ point->prev = asfilter;
+ } else {
+ if (aslist->tail)
+ aslist->tail->next = asfilter;
+ else
+ aslist->head = asfilter;
+
+ asfilter->prev = aslist->tail;
+ aslist->tail = asfilter;
+ }
+
+hook:
+ /* Run hook function. */
+ if (as_list_master.add_hook)
+ (*as_list_master.add_hook)(aslist->name);
+}
+
+/* Lookup as_list from list of as_list by name. */
+struct as_list *as_list_lookup(const char *name)
+{
+ struct as_list *aslist;
+
+ if (name == NULL)
+ return NULL;
+
+ for (aslist = as_list_master.str.head; aslist; aslist = aslist->next)
+ if (strcmp(aslist->name, name) == 0)
+ return aslist;
+
+ return NULL;
+}
+
+static struct as_list *as_list_new(void)
+{
+ return XCALLOC(MTYPE_AS_LIST, sizeof(struct as_list));
+}
+
+static void as_list_free(struct as_list *aslist)
+{
+ XFREE(MTYPE_AS_STR, aslist->name);
+ XFREE(MTYPE_AS_LIST, aslist);
+}
+
+/* Insert new AS list to list of as_list. Each as_list is sorted by
+ the name. */
+static struct as_list *as_list_insert(const char *name)
+{
+ struct as_list *aslist;
+ struct as_list *point;
+ struct as_list_list *list;
+
+ /* Allocate new access_list and copy given name. */
+ aslist = as_list_new();
+ aslist->name = XSTRDUP(MTYPE_AS_STR, name);
+ assert(aslist->name);
+
+ /* Set access_list to string list. */
+ list = &as_list_master.str;
+
+ /* Set point to insertion point. */
+ for (point = list->head; point; point = point->next)
+ if (strcmp(point->name, name) >= 0)
+ break;
+
+ /* In case of this is the first element of master. */
+ if (list->head == NULL) {
+ list->head = list->tail = aslist;
+ return aslist;
+ }
+
+ /* In case of insertion is made at the tail of access_list. */
+ if (point == NULL) {
+ aslist->prev = list->tail;
+ list->tail->next = aslist;
+ list->tail = aslist;
+ return aslist;
+ }
+
+ /* In case of insertion is made at the head of access_list. */
+ if (point == list->head) {
+ aslist->next = list->head;
+ list->head->prev = aslist;
+ list->head = aslist;
+ return aslist;
+ }
+
+ /* Insertion is made at middle of the access_list. */
+ aslist->next = point;
+ aslist->prev = point->prev;
+
+ if (point->prev)
+ point->prev->next = aslist;
+ point->prev = aslist;
+
+ return aslist;
+}
+
+static struct as_list *as_list_get(const char *name)
+{
+ struct as_list *aslist;
+
+ aslist = as_list_lookup(name);
+ if (aslist == NULL)
+ aslist = as_list_insert(name);
+
+ return aslist;
+}
+
+static const char *filter_type_str(enum as_filter_type type)
+{
+ switch (type) {
+ case AS_FILTER_PERMIT:
+ return "permit";
+ case AS_FILTER_DENY:
+ return "deny";
+ default:
+ return "";
+ }
+}
+
+static void as_list_delete(struct as_list *aslist)
+{
+ struct as_list_list *list;
+ struct as_filter *filter, *next;
+
+ for (filter = aslist->head; filter; filter = next) {
+ next = filter->next;
+ as_filter_free(filter);
+ }
+
+ list = &as_list_master.str;
+
+ if (aslist->next)
+ aslist->next->prev = aslist->prev;
+ else
+ list->tail = aslist->prev;
+
+ if (aslist->prev)
+ aslist->prev->next = aslist->next;
+ else
+ list->head = aslist->next;
+
+ as_list_free(aslist);
+}
+
+static bool as_list_empty(struct as_list *aslist)
+{
+ return aslist->head == NULL && aslist->tail == NULL;
+}
+
+static void as_list_filter_delete(struct as_list *aslist,
+ struct as_filter *asfilter)
+{
+ char *name = XSTRDUP(MTYPE_AS_STR, aslist->name);
+
+ if (asfilter->next)
+ asfilter->next->prev = asfilter->prev;
+ else
+ aslist->tail = asfilter->prev;
+
+ if (asfilter->prev)
+ asfilter->prev->next = asfilter->next;
+ else
+ aslist->head = asfilter->next;
+
+ as_filter_free(asfilter);
+
+ /* If access_list becomes empty delete it from access_master. */
+ if (as_list_empty(aslist))
+ as_list_delete(aslist);
+
+ /* Run hook function. */
+ if (as_list_master.delete_hook)
+ (*as_list_master.delete_hook)(name);
+ XFREE(MTYPE_AS_STR, name);
+}
+
+static bool as_filter_match(struct as_filter *asfilter, struct aspath *aspath)
+{
+ return bgp_regexec(asfilter->reg, aspath) != REG_NOMATCH;
+}
+
+/* Apply AS path filter to AS. */
+enum as_filter_type as_list_apply(struct as_list *aslist, void *object)
+{
+ struct as_filter *asfilter;
+ struct aspath *aspath;
+
+ aspath = (struct aspath *)object;
+
+ if (aslist == NULL)
+ return AS_FILTER_DENY;
+
+ for (asfilter = aslist->head; asfilter; asfilter = asfilter->next) {
+ if (as_filter_match(asfilter, aspath))
+ return asfilter->type;
+ }
+ return AS_FILTER_DENY;
+}
+
+/* Add hook function. */
+void as_list_add_hook(void (*func)(char *))
+{
+ as_list_master.add_hook = func;
+}
+
+/* Delete hook function. */
+void as_list_delete_hook(void (*func)(const char *))
+{
+ as_list_master.delete_hook = func;
+}
+
+static bool as_list_dup_check(struct as_list *aslist, struct as_filter *new)
+{
+ struct as_filter *asfilter;
+
+ for (asfilter = aslist->head; asfilter; asfilter = asfilter->next) {
+ if (asfilter->type == new->type
+ && strcmp(asfilter->reg_str, new->reg_str) == 0)
+ return true;
+ }
+ return false;
+}
+
+bool config_bgp_aspath_validate(const char *regstr)
+{
+ char valid_chars[] = "1234567890_^|[,{}() ]$*+.?-\\";
+
+ if (strspn(regstr, valid_chars) == strlen(regstr))
+ return true;
+ return false;
+}
+
+DEFUN(as_path, bgp_as_path_cmd,
+ "bgp as-path access-list AS_PATH_FILTER_NAME [seq (0-4294967295)] <deny|permit> LINE...",
+ BGP_STR
+ "BGP autonomous system path filter\n"
+ "Specify an access list name\n"
+ "Regular expression access list name\n"
+ "Sequence number of an entry\n"
+ "Sequence number\n"
+ "Specify packets to reject\n"
+ "Specify packets to forward\n"
+ "A regular-expression (1234567890_^|[,{}() ]$*+.?-\\) to match the BGP AS paths\n")
+{
+ int idx = 0;
+ enum as_filter_type type;
+ struct as_filter *asfilter;
+ struct as_list *aslist;
+ regex_t *regex;
+ char *regstr;
+ int64_t seqnum = ASPATH_SEQ_NUMBER_AUTO;
+
+ /* Retrieve access list name */
+ argv_find(argv, argc, "AS_PATH_FILTER_NAME", &idx);
+ char *alname = argv[idx]->arg;
+
+ if (argv_find(argv, argc, "(0-4294967295)", &idx))
+ seqnum = (int64_t)atol(argv[idx]->arg);
+
+ /* Check the filter type. */
+ type = argv_find(argv, argc, "deny", &idx) ? AS_FILTER_DENY
+ : AS_FILTER_PERMIT;
+
+ /* Check AS path regex. */
+ argv_find(argv, argc, "LINE", &idx);
+ regstr = argv_concat(argv, argc, idx);
+
+ regex = bgp_regcomp(regstr);
+ if (!regex) {
+ vty_out(vty, "can't compile regexp %s\n", regstr);
+ XFREE(MTYPE_TMP, regstr);
+ return CMD_WARNING_CONFIG_FAILED;
+ }
+
+ if (!config_bgp_aspath_validate(regstr)) {
+ vty_out(vty, "Invalid character in as-path access-list %s\n",
+ regstr);
+ XFREE(MTYPE_TMP, regstr);
+ return CMD_WARNING_CONFIG_FAILED;
+ }
+
+ asfilter = as_filter_make(regex, regstr, type);
+
+ XFREE(MTYPE_TMP, regstr);
+
+ /* Install new filter to the access_list. */
+ aslist = as_list_get(alname);
+
+ if (seqnum == ASPATH_SEQ_NUMBER_AUTO)
+ seqnum = bgp_alist_new_seq_get(aslist);
+
+ asfilter->seq = seqnum;
+
+ /* Duplicate insertion check. */;
+ if (as_list_dup_check(aslist, asfilter))
+ as_filter_free(asfilter);
+ else
+ as_list_filter_add(aslist, asfilter);
+
+ return CMD_SUCCESS;
+}
+
+DEFUN(no_as_path, no_bgp_as_path_cmd,
+ "no bgp as-path access-list AS_PATH_FILTER_NAME [seq (0-4294967295)] <deny|permit> LINE...",
+ NO_STR
+ BGP_STR
+ "BGP autonomous system path filter\n"
+ "Specify an access list name\n"
+ "Regular expression access list name\n"
+ "Sequence number of an entry\n"
+ "Sequence number\n"
+ "Specify packets to reject\n"
+ "Specify packets to forward\n"
+ "A regular-expression (1234567890_^|[,{}() ]$*+.?-\\) to match the BGP AS paths\n")
+{
+ int idx = 0;
+ enum as_filter_type type;
+ struct as_filter *asfilter;
+ struct as_list *aslist;
+ char *regstr;
+ regex_t *regex;
+
+ char *aslistname =
+ argv_find(argv, argc, "AS_PATH_FILTER_NAME", &idx) ? argv[idx]->arg : NULL;
+
+ /* Lookup AS list from AS path list. */
+ aslist = as_list_lookup(aslistname);
+ if (aslist == NULL) {
+ vty_out(vty, "bgp as-path access-list %s doesn't exist\n",
+ aslistname);
+ return CMD_WARNING_CONFIG_FAILED;
+ }
+
+ /* Check the filter type. */
+ if (argv_find(argv, argc, "permit", &idx))
+ type = AS_FILTER_PERMIT;
+ else if (argv_find(argv, argc, "deny", &idx))
+ type = AS_FILTER_DENY;
+ else {
+ vty_out(vty, "filter type must be [permit|deny]\n");
+ return CMD_WARNING_CONFIG_FAILED;
+ }
+
+ /* Compile AS path. */
+ argv_find(argv, argc, "LINE", &idx);
+ regstr = argv_concat(argv, argc, idx);
+
+ if (!config_bgp_aspath_validate(regstr)) {
+ vty_out(vty, "Invalid character in as-path access-list %s\n",
+ regstr);
+ return CMD_WARNING_CONFIG_FAILED;
+ }
+
+ regex = bgp_regcomp(regstr);
+ if (!regex) {
+ vty_out(vty, "can't compile regexp %s\n", regstr);
+ XFREE(MTYPE_TMP, regstr);
+ return CMD_WARNING_CONFIG_FAILED;
+ }
+
+ /* Lookup asfilter. */
+ asfilter = as_filter_lookup(aslist, regstr, type);
+
+ bgp_regex_free(regex);
+
+ if (asfilter == NULL) {
+ vty_out(vty, "Regex entered %s does not exist\n", regstr);
+ XFREE(MTYPE_TMP, regstr);
+ return CMD_WARNING_CONFIG_FAILED;
+ }
+
+ XFREE(MTYPE_TMP, regstr);
+
+ as_list_filter_delete(aslist, asfilter);
+
+ return CMD_SUCCESS;
+}
+
+DEFUN (no_as_path_all,
+ no_bgp_as_path_all_cmd,
+ "no bgp as-path access-list AS_PATH_FILTER_NAME",
+ NO_STR
+ BGP_STR
+ "BGP autonomous system path filter\n"
+ "Specify an access list name\n"
+ "Regular expression access list name\n")
+{
+ int idx_word = 4;
+ struct as_list *aslist;
+
+ aslist = as_list_lookup(argv[idx_word]->arg);
+ if (aslist == NULL) {
+ vty_out(vty, "bgp as-path access-list %s doesn't exist\n",
+ argv[idx_word]->arg);
+ return CMD_WARNING_CONFIG_FAILED;
+ }
+
+ as_list_delete(aslist);
+
+ /* Run hook function. */
+ if (as_list_master.delete_hook)
+ (*as_list_master.delete_hook)(argv[idx_word]->arg);
+
+ return CMD_SUCCESS;
+}
+
+static void as_list_show(struct vty *vty, struct as_list *aslist,
+ json_object *json)
+{
+ struct as_filter *asfilter;
+ json_object *json_aslist = NULL;
+
+ if (json) {
+ json_aslist = json_object_new_array();
+ json_object_object_add(json, aslist->name, json_aslist);
+ } else
+ vty_out(vty, "AS path access list %s\n", aslist->name);
+
+ for (asfilter = aslist->head; asfilter; asfilter = asfilter->next) {
+ if (json) {
+ json_object *json_asfilter = json_object_new_object();
+
+ json_object_int_add(json_asfilter, "sequenceNumber",
+ asfilter->seq);
+ json_object_string_add(json_asfilter, "type",
+ filter_type_str(asfilter->type));
+ json_object_string_add(json_asfilter, "regExp",
+ asfilter->reg_str);
+
+ json_object_array_add(json_aslist, json_asfilter);
+ } else
+ vty_out(vty, " %s %s\n",
+ filter_type_str(asfilter->type),
+ asfilter->reg_str);
+ }
+}
+
+static void as_list_show_all(struct vty *vty, json_object *json)
+{
+ struct as_list *aslist;
+
+ for (aslist = as_list_master.str.head; aslist; aslist = aslist->next)
+ as_list_show(vty, aslist, json);
+}
+
+DEFUN (show_as_path_access_list,
+ show_bgp_as_path_access_list_cmd,
+ "show bgp as-path-access-list AS_PATH_FILTER_NAME [json]",
+ SHOW_STR
+ BGP_STR
+ "List AS path access lists\n"
+ "AS path access list name\n"
+ JSON_STR)
+{
+ int idx_word = 3;
+ struct as_list *aslist;
+ bool uj = use_json(argc, argv);
+ json_object *json = NULL;
+
+ if (uj)
+ json = json_object_new_object();
+
+ aslist = as_list_lookup(argv[idx_word]->arg);
+ if (aslist)
+ as_list_show(vty, aslist, json);
+
+ if (uj)
+ vty_json(vty, json);
+
+ return CMD_SUCCESS;
+}
+
+ALIAS (show_as_path_access_list,
+ show_ip_as_path_access_list_cmd,
+ "show ip as-path-access-list AS_PATH_FILTER_NAME [json]",
+ SHOW_STR
+ IP_STR
+ "List AS path access lists\n"
+ "AS path access list name\n"
+ JSON_STR)
+
+DEFUN (show_as_path_access_list_all,
+ show_bgp_as_path_access_list_all_cmd,
+ "show bgp as-path-access-list [json]",
+ SHOW_STR
+ BGP_STR
+ "List AS path access lists\n"
+ JSON_STR)
+{
+ bool uj = use_json(argc, argv);
+ json_object *json = NULL;
+
+ if (uj)
+ json = json_object_new_object();
+
+ as_list_show_all(vty, json);
+
+ if (uj)
+ vty_json(vty, json);
+
+ return CMD_SUCCESS;
+}
+
+ALIAS (show_as_path_access_list_all,
+ show_ip_as_path_access_list_all_cmd,
+ "show ip as-path-access-list [json]",
+ SHOW_STR
+ IP_STR
+ "List AS path access lists\n"
+ JSON_STR)
+
+static int config_write_as_list(struct vty *vty)
+{
+ struct as_list *aslist;
+ struct as_filter *asfilter;
+ int write = 0;
+
+ for (aslist = as_list_master.str.head; aslist; aslist = aslist->next)
+ for (asfilter = aslist->head; asfilter;
+ asfilter = asfilter->next) {
+ vty_out(vty,
+ "bgp as-path access-list %s seq %" PRId64
+ " %s %s\n",
+ aslist->name, asfilter->seq,
+ filter_type_str(asfilter->type),
+ asfilter->reg_str);
+ write++;
+ }
+ return write;
+}
+
+static int config_write_as_list(struct vty *vty);
+static struct cmd_node as_list_node = {
+ .name = "as list",
+ .node = AS_LIST_NODE,
+ .prompt = "",
+ .config_write = config_write_as_list,
+};
+
+static void bgp_aspath_filter_cmd_completion(vector comps,
+ struct cmd_token *token)
+{
+ struct as_list *aslist;
+
+ for (aslist = as_list_master.str.head; aslist; aslist = aslist->next)
+ vector_set(comps, XSTRDUP(MTYPE_COMPLETION, aslist->name));
+}
+
+static const struct cmd_variable_handler aspath_filter_handlers[] = {
+ {.tokenname = "AS_PATH_FILTER_NAME",
+ .completions = bgp_aspath_filter_cmd_completion},
+ {.completions = NULL}};
+
+/* Register functions. */
+void bgp_filter_init(void)
+{
+ install_node(&as_list_node);
+
+ install_element(CONFIG_NODE, &bgp_as_path_cmd);
+ install_element(CONFIG_NODE, &no_bgp_as_path_cmd);
+ install_element(CONFIG_NODE, &no_bgp_as_path_all_cmd);
+
+ install_element(VIEW_NODE, &show_bgp_as_path_access_list_cmd);
+ install_element(VIEW_NODE, &show_ip_as_path_access_list_cmd);
+ install_element(VIEW_NODE, &show_bgp_as_path_access_list_all_cmd);
+ install_element(VIEW_NODE, &show_ip_as_path_access_list_all_cmd);
+
+ cmd_variable_handler_register(aspath_filter_handlers);
+}
+
+void bgp_filter_reset(void)
+{
+ struct as_list *aslist;
+ struct as_list *next;
+
+ for (aslist = as_list_master.str.head; aslist; aslist = next) {
+ next = aslist->next;
+ as_list_delete(aslist);
+ }
+
+ assert(as_list_master.str.head == NULL);
+ assert(as_list_master.str.tail == NULL);
+}
diff --git a/bgpd/bgp_filter.h b/bgpd/bgp_filter.h
new file mode 100644
index 0000000..66c83d9
--- /dev/null
+++ b/bgpd/bgp_filter.h
@@ -0,0 +1,38 @@
+/* AS path filter list.
+ * Copyright (C) 1999 Kunihiro Ishiguro
+ *
+ * This file is part of GNU Zebra.
+ *
+ * GNU Zebra is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License as published by the
+ * Free Software Foundation; either version 2, or (at your option) any
+ * later version.
+ *
+ * GNU Zebra is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program; see the file COPYING; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
+ */
+
+#ifndef _QUAGGA_BGP_FILTER_H
+#define _QUAGGA_BGP_FILTER_H
+
+#define ASPATH_SEQ_NUMBER_AUTO -1
+
+enum as_filter_type { AS_FILTER_DENY, AS_FILTER_PERMIT };
+
+extern void bgp_filter_init(void);
+extern void bgp_filter_reset(void);
+
+extern enum as_filter_type as_list_apply(struct as_list *, void *);
+
+extern struct as_list *as_list_lookup(const char *);
+extern void as_list_add_hook(void (*func)(char *));
+extern void as_list_delete_hook(void (*func)(const char *));
+extern bool config_bgp_aspath_validate(const char *regstr);
+
+#endif /* _QUAGGA_BGP_FILTER_H */
diff --git a/bgpd/bgp_flowspec.c b/bgpd/bgp_flowspec.c
new file mode 100644
index 0000000..c511d01
--- /dev/null
+++ b/bgpd/bgp_flowspec.c
@@ -0,0 +1,224 @@
+/* BGP FlowSpec for packet handling
+ * Portions:
+ * Copyright (C) 2017 ChinaTelecom SDN Group
+ * Copyright (C) 2018 6WIND
+ *
+ * FRRouting is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License as published by the
+ * Free Software Foundation; either version 2, or (at your option) any
+ * later version.
+ *
+ * FRRouting is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program; see the file COPYING; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
+ */
+
+#include <zebra.h>
+#include <math.h>
+
+#include "prefix.h"
+#include "lib_errors.h"
+
+#include "bgpd/bgpd.h"
+#include "bgpd/bgp_route.h"
+#include "bgpd/bgp_flowspec.h"
+#include "bgpd/bgp_flowspec_util.h"
+#include "bgpd/bgp_flowspec_private.h"
+#include "bgpd/bgp_ecommunity.h"
+#include "bgpd/bgp_debug.h"
+#include "bgpd/bgp_errors.h"
+
+static int bgp_fs_nlri_validate(uint8_t *nlri_content, uint32_t len,
+ afi_t afi)
+{
+ uint32_t offset = 0;
+ int type;
+ int ret = 0, error = 0;
+
+ while (offset < len-1) {
+ type = nlri_content[offset];
+ offset++;
+ switch (type) {
+ case FLOWSPEC_DEST_PREFIX:
+ case FLOWSPEC_SRC_PREFIX:
+ ret = bgp_flowspec_ip_address(
+ BGP_FLOWSPEC_VALIDATE_ONLY,
+ nlri_content + offset,
+ len - offset, NULL, &error,
+ afi, NULL);
+ break;
+ case FLOWSPEC_FLOW_LABEL:
+ if (afi == AFI_IP)
+ return -1;
+ ret = bgp_flowspec_op_decode(BGP_FLOWSPEC_VALIDATE_ONLY,
+ nlri_content + offset,
+ len - offset, NULL, &error);
+ break;
+ case FLOWSPEC_IP_PROTOCOL:
+ case FLOWSPEC_PORT:
+ case FLOWSPEC_DEST_PORT:
+ case FLOWSPEC_SRC_PORT:
+ case FLOWSPEC_ICMP_TYPE:
+ case FLOWSPEC_ICMP_CODE:
+ ret = bgp_flowspec_op_decode(BGP_FLOWSPEC_VALIDATE_ONLY,
+ nlri_content + offset,
+ len - offset, NULL, &error);
+ break;
+ case FLOWSPEC_TCP_FLAGS:
+ case FLOWSPEC_FRAGMENT:
+ ret = bgp_flowspec_bitmask_decode(
+ BGP_FLOWSPEC_VALIDATE_ONLY,
+ nlri_content + offset,
+ len - offset, NULL, &error);
+ break;
+ case FLOWSPEC_PKT_LEN:
+ case FLOWSPEC_DSCP:
+ ret = bgp_flowspec_op_decode(
+ BGP_FLOWSPEC_VALIDATE_ONLY,
+ nlri_content + offset,
+ len - offset, NULL, &error);
+ break;
+ default:
+ error = -1;
+ break;
+ }
+ offset += ret;
+ if (error < 0)
+ break;
+ }
+ return error;
+}
+
+int bgp_nlri_parse_flowspec(struct peer *peer, struct attr *attr,
+ struct bgp_nlri *packet, bool withdraw)
+{
+ uint8_t *pnt;
+ uint8_t *lim;
+ afi_t afi;
+ safi_t safi;
+ int psize = 0;
+ struct prefix p;
+ int ret;
+ void *temp;
+
+ /* Start processing the NLRI - there may be multiple in the MP_REACH */
+ pnt = packet->nlri;
+ lim = pnt + packet->length;
+ afi = packet->afi;
+ safi = packet->safi;
+
+ /*
+ * All other AFI/SAFI's treat no attribute as a implicit
+ * withdraw. Flowspec should as well.
+ */
+ if (!attr)
+ withdraw = true;
+
+ if (packet->length >= FLOWSPEC_NLRI_SIZELIMIT_EXTENDED) {
+ flog_err(EC_BGP_FLOWSPEC_PACKET,
+ "BGP flowspec nlri length maximum reached (%u)",
+ packet->length);
+ return BGP_NLRI_PARSE_ERROR_FLOWSPEC_NLRI_SIZELIMIT;
+ }
+
+ for (; pnt < lim; pnt += psize) {
+ /* Clear prefix structure. */
+ memset(&p, 0, sizeof(p));
+
+ /* All FlowSpec NLRI begin with length. */
+ if (pnt + 1 > lim)
+ return BGP_NLRI_PARSE_ERROR_PACKET_OVERFLOW;
+
+ psize = *pnt++;
+ if (psize >= FLOWSPEC_NLRI_SIZELIMIT) {
+ psize &= 0x0f;
+ psize = psize << 8;
+ psize |= *pnt++;
+ }
+ /* When packet overflow occur return immediately. */
+ if (pnt + psize > lim) {
+ flog_err(
+ EC_BGP_FLOWSPEC_PACKET,
+ "Flowspec NLRI length inconsistent ( size %u seen)",
+ psize);
+ return BGP_NLRI_PARSE_ERROR_PACKET_OVERFLOW;
+ }
+
+ if (psize == 0) {
+ flog_err(EC_BGP_FLOWSPEC_PACKET,
+ "Flowspec NLRI length 0 which makes no sense");
+ return BGP_NLRI_PARSE_ERROR_PACKET_OVERFLOW;
+ }
+
+ if (bgp_fs_nlri_validate(pnt, psize, afi) < 0) {
+ flog_err(
+ EC_BGP_FLOWSPEC_PACKET,
+ "Bad flowspec format or NLRI options not supported");
+ return BGP_NLRI_PARSE_ERROR_FLOWSPEC_BAD_FORMAT;
+ }
+ p.family = AF_FLOWSPEC;
+ p.prefixlen = 0;
+ /* Flowspec encoding is in bytes */
+ p.u.prefix_flowspec.prefixlen = psize;
+ p.u.prefix_flowspec.family = afi2family(afi);
+ temp = XCALLOC(MTYPE_TMP, psize);
+ memcpy(temp, pnt, psize);
+ p.u.prefix_flowspec.ptr = (uintptr_t) temp;
+
+ if (BGP_DEBUG(flowspec, FLOWSPEC)) {
+ char return_string[BGP_FLOWSPEC_NLRI_STRING_MAX];
+ char local_string[BGP_FLOWSPEC_NLRI_STRING_MAX*2+16];
+ char ec_string[BGP_FLOWSPEC_NLRI_STRING_MAX];
+ char *s = NULL;
+
+ bgp_fs_nlri_get_string((unsigned char *)
+ p.u.prefix_flowspec.ptr,
+ p.u.prefix_flowspec.prefixlen,
+ return_string,
+ NLRI_STRING_FORMAT_MIN, NULL,
+ afi);
+ snprintf(ec_string, sizeof(ec_string),
+ "EC{none}");
+ if (attr && bgp_attr_get_ecommunity(attr)) {
+ s = ecommunity_ecom2str(
+ bgp_attr_get_ecommunity(attr),
+ ECOMMUNITY_FORMAT_ROUTE_MAP, 0);
+ snprintf(ec_string, sizeof(ec_string),
+ "EC{%s}",
+ s == NULL ? "none" : s);
+
+ if (s)
+ ecommunity_strfree(&s);
+ }
+ snprintf(local_string, sizeof(local_string),
+ "FS Rx %s %s %s %s", withdraw ?
+ "Withdraw":"Update",
+ afi2str(afi), return_string,
+ attr != NULL ? ec_string : "");
+ zlog_info("%s", local_string);
+ }
+ /* Process the route. */
+ if (!withdraw)
+ ret = bgp_update(peer, &p, 0, attr,
+ afi, safi,
+ ZEBRA_ROUTE_BGP, BGP_ROUTE_NORMAL,
+ NULL, NULL, 0, 0, NULL);
+ else
+ ret = bgp_withdraw(peer, &p, 0, attr,
+ afi, safi,
+ ZEBRA_ROUTE_BGP, BGP_ROUTE_NORMAL,
+ NULL, NULL, 0, NULL);
+ if (ret) {
+ flog_err(EC_BGP_FLOWSPEC_INSTALLATION,
+ "Flowspec NLRI failed to be %s.",
+ attr ? "added" : "withdrawn");
+ return BGP_NLRI_PARSE_ERROR;
+ }
+ }
+ return BGP_NLRI_PARSE_OK;
+}
diff --git a/bgpd/bgp_flowspec.h b/bgpd/bgp_flowspec.h
new file mode 100644
index 0000000..64b6a8b
--- /dev/null
+++ b/bgpd/bgp_flowspec.h
@@ -0,0 +1,59 @@
+/* BGP Flowspec header for packet handling
+ * Copyright (C) 2018 6WIND
+ *
+ * FRRouting is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License as published by the
+ * Free Software Foundation; either version 2, or (at your option) any
+ * later version.
+ *
+ * FRRouting is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program; see the file COPYING; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
+ */
+
+#ifndef _FRR_BGP_FLOWSPEC_H
+#define _FRR_BGP_FLOWSPEC_H
+
+#define NLRI_STRING_FORMAT_LARGE 0
+#define NLRI_STRING_FORMAT_DEBUG 1
+#define NLRI_STRING_FORMAT_MIN 2
+#define NLRI_STRING_FORMAT_JSON 3
+#define NLRI_STRING_FORMAT_JSON_SIMPLE 4
+
+#define BGP_FLOWSPEC_NLRI_STRING_MAX 512
+
+extern int bgp_nlri_parse_flowspec(struct peer *peer, struct attr *attr,
+ struct bgp_nlri *packet, bool withdraw);
+
+extern void bgp_flowspec_vty_init(void);
+
+extern int bgp_show_table_flowspec(struct vty *vty, struct bgp *bgp, afi_t afi,
+ struct bgp_table *table,
+ enum bgp_show_type type, void *output_arg,
+ bool use_json, int is_last,
+ unsigned long *output_cum,
+ unsigned long *total_cum);
+
+extern void bgp_fs_nlri_get_string(unsigned char *nlri_content, size_t len,
+ char *return_string, int format,
+ json_object *json_path,
+ afi_t afi);
+
+extern void route_vty_out_flowspec(struct vty *vty, const struct prefix *p,
+ struct bgp_path_info *path, int display,
+ json_object *json_paths);
+extern int bgp_fs_config_write_pbr(struct vty *vty, struct bgp *bgp,
+ afi_t afi, safi_t safi);
+
+extern int bgp_flowspec_display_match_per_ip(afi_t afi, struct bgp_table *rib,
+ struct prefix *match,
+ int prefix_check, struct vty *vty,
+ bool use_json,
+ json_object *json_paths);
+
+#endif /* _FRR_BGP_FLOWSPEC_H */
diff --git a/bgpd/bgp_flowspec_private.h b/bgpd/bgp_flowspec_private.h
new file mode 100644
index 0000000..757a8ae
--- /dev/null
+++ b/bgpd/bgp_flowspec_private.h
@@ -0,0 +1,46 @@
+/* BGP Flowspec header . private structs and defines
+ * Copyright (C) 2018 6WIND
+ *
+ * FRRouting is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License as published by the
+ * Free Software Foundation; either version 2, or (at your option) any
+ * later version.
+ *
+ * FRRouting is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program; see the file COPYING; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
+ */
+
+#ifndef _FRR_BGP_FLOWSPEC_PRIVATE_H
+#define _FRR_BGP_FLOWSPEC_PRIVATE_H
+
+#define FLOWSPEC_NLRI_SIZELIMIT 240
+#define FLOWSPEC_NLRI_SIZELIMIT_EXTENDED 4095
+
+/* Flowspec raffic action bit*/
+#define FLOWSPEC_TRAFFIC_ACTION_TERMINAL 1
+#define FLOWSPEC_TRAFFIC_ACTION_SAMPLE 0
+#define FLOWSPEC_TRAFFIC_ACTION_DISTRIBUTE 1
+
+/* Flow Spec Component Types */
+#define NUM_OF_FLOWSPEC_MATCH_TYPES 12
+#define FLOWSPEC_DEST_PREFIX 1
+#define FLOWSPEC_SRC_PREFIX 2
+#define FLOWSPEC_IP_PROTOCOL 3
+#define FLOWSPEC_PORT 4
+#define FLOWSPEC_DEST_PORT 5
+#define FLOWSPEC_SRC_PORT 6
+#define FLOWSPEC_ICMP_TYPE 7
+#define FLOWSPEC_ICMP_CODE 8
+#define FLOWSPEC_TCP_FLAGS 9
+#define FLOWSPEC_PKT_LEN 10
+#define FLOWSPEC_DSCP 11
+#define FLOWSPEC_FRAGMENT 12
+#define FLOWSPEC_FLOW_LABEL 13 /* For IPv6 only */
+
+#endif /* _FRR_BGP_FLOWSPEC_PRIVATE_H */
diff --git a/bgpd/bgp_flowspec_util.c b/bgpd/bgp_flowspec_util.c
new file mode 100644
index 0000000..9f3ea49
--- /dev/null
+++ b/bgpd/bgp_flowspec_util.c
@@ -0,0 +1,689 @@
+/* BGP FlowSpec Utilities
+ * Portions:
+ * Copyright (C) 2017 ChinaTelecom SDN Group
+ * Copyright (C) 2018 6WIND
+ *
+ * FRRouting is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License as published by the
+ * Free Software Foundation; either version 2, or (at your option) any
+ * later version.
+ *
+ * FRRouting is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program; see the file COPYING; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
+ */
+
+#include "zebra.h"
+
+#include "lib/printfrr.h"
+
+#include "prefix.h"
+#include "lib_errors.h"
+
+#include "bgp_route.h"
+#include "bgp_table.h"
+#include "bgp_flowspec_util.h"
+#include "bgp_flowspec_private.h"
+#include "bgp_pbr.h"
+#include "bgp_errors.h"
+
+static void hex2bin(uint8_t *hex, int *bin)
+{
+ int remainder = *hex;
+ int i = 0;
+
+ while (remainder >= 1 && i < 8) {
+ bin[7-i] = remainder % 2;
+ remainder = remainder / 2;
+ i++;
+ }
+ for (; i < 8; i++)
+ bin[7-i] = 0;
+}
+
+static int hexstr2num(uint8_t *hexstr, int len)
+{
+ int i = 0;
+ int num = 0;
+
+ for (i = 0; i < len; i++)
+ num = hexstr[i] + 16*16*num;
+ return num;
+}
+
+/* call bgp_flowspec_op_decode
+ * returns offset
+ */
+static int bgp_flowspec_call_non_opaque_decode(uint8_t *nlri_content, int len,
+ struct bgp_pbr_match_val *mval,
+ uint8_t *match_num, int *error)
+{
+ int ret;
+
+ ret = bgp_flowspec_op_decode(
+ BGP_FLOWSPEC_CONVERT_TO_NON_OPAQUE,
+ nlri_content,
+ len,
+ mval, error);
+ if (*error < 0)
+ flog_err(EC_BGP_FLOWSPEC_PACKET,
+ "%s: flowspec_op_decode error %d", __func__, *error);
+ else
+ *match_num = *error;
+ return ret;
+}
+
+
+bool bgp_flowspec_contains_prefix(const struct prefix *pfs,
+ struct prefix *input, int prefix_check)
+{
+ uint32_t offset = 0;
+ int type;
+ int ret = 0, error = 0;
+ uint8_t *nlri_content = (uint8_t *)pfs->u.prefix_flowspec.ptr;
+ size_t len = pfs->u.prefix_flowspec.prefixlen;
+ afi_t afi = family2afi(pfs->u.prefix_flowspec.family);
+ struct prefix compare;
+
+ error = 0;
+ while (offset < len-1 && error >= 0) {
+ type = nlri_content[offset];
+ offset++;
+ switch (type) {
+ case FLOWSPEC_DEST_PREFIX:
+ case FLOWSPEC_SRC_PREFIX:
+ memset(&compare, 0, sizeof(compare));
+ ret = bgp_flowspec_ip_address(
+ BGP_FLOWSPEC_CONVERT_TO_NON_OPAQUE,
+ nlri_content+offset,
+ len - offset,
+ &compare, &error,
+ afi, NULL);
+ if (ret <= 0)
+ break;
+ if (prefix_check &&
+ compare.prefixlen != input->prefixlen)
+ break;
+ if (compare.family != input->family)
+ break;
+ if ((input->family == AF_INET) &&
+ IPV4_ADDR_SAME(&input->u.prefix4,
+ &compare.u.prefix4))
+ return true;
+ if ((input->family == AF_INET6) &&
+ IPV6_ADDR_SAME(&input->u.prefix6.s6_addr,
+ &compare.u.prefix6.s6_addr))
+ return true;
+ break;
+ case FLOWSPEC_FLOW_LABEL:
+ if (afi == AFI_IP) {
+ error = -1;
+ continue;
+ }
+ ret = bgp_flowspec_op_decode(BGP_FLOWSPEC_VALIDATE_ONLY,
+ nlri_content+offset,
+ len - offset,
+ NULL, &error);
+ break;
+ case FLOWSPEC_IP_PROTOCOL:
+ case FLOWSPEC_PORT:
+ case FLOWSPEC_DEST_PORT:
+ case FLOWSPEC_SRC_PORT:
+ case FLOWSPEC_ICMP_TYPE:
+ case FLOWSPEC_ICMP_CODE:
+ ret = bgp_flowspec_op_decode(BGP_FLOWSPEC_VALIDATE_ONLY,
+ nlri_content+offset,
+ len - offset,
+ NULL, &error);
+ break;
+ case FLOWSPEC_FRAGMENT:
+ case FLOWSPEC_TCP_FLAGS:
+ ret = bgp_flowspec_bitmask_decode(
+ BGP_FLOWSPEC_VALIDATE_ONLY,
+ nlri_content+offset,
+ len - offset,
+ NULL, &error);
+ break;
+ case FLOWSPEC_PKT_LEN:
+ case FLOWSPEC_DSCP:
+ ret = bgp_flowspec_op_decode(
+ BGP_FLOWSPEC_VALIDATE_ONLY,
+ nlri_content + offset,
+ len - offset, NULL,
+ &error);
+ break;
+ default:
+ error = -1;
+ break;
+ }
+ offset += ret;
+ }
+ return false;
+}
+
+/*
+ * handle the flowspec address src/dst or generic address NLRI
+ * return number of bytes analysed ( >= 0).
+ */
+int bgp_flowspec_ip_address(enum bgp_flowspec_util_nlri_t type,
+ uint8_t *nlri_ptr,
+ uint32_t max_len,
+ void *result, int *error,
+ afi_t afi,
+ uint8_t *ipv6_offset)
+{
+ char *display = (char *)result; /* for return_string */
+ struct prefix *prefix = (struct prefix *)result;
+ uint32_t offset = 0;
+ struct prefix prefix_local;
+ int psize;
+ uint8_t prefix_offset = 0;
+
+ *error = 0;
+ memset(&prefix_local, 0, sizeof(prefix_local));
+ /* read the prefix length */
+ prefix_local.prefixlen = nlri_ptr[offset];
+ psize = PSIZE(prefix_local.prefixlen);
+ offset++;
+ prefix_local.family = afi2family(afi);
+ if (prefix_local.family == AF_INET6) {
+ prefix_offset = nlri_ptr[offset];
+ if (ipv6_offset)
+ *ipv6_offset = prefix_offset;
+ offset++;
+ }
+ /* Prefix length check. */
+ if (prefix_local.prefixlen > prefix_blen(&prefix_local) * 8)
+ *error = -1;
+ /* When packet overflow occur return immediately. */
+ if (psize + offset > max_len)
+ *error = -1;
+ /* Defensive coding, double-check
+ * the psize fits in a struct prefix
+ */
+ if (psize > (ssize_t)sizeof(prefix_local.u))
+ *error = -1;
+ memcpy(&prefix_local.u.prefix, &nlri_ptr[offset], psize);
+ offset += psize;
+ switch (type) {
+ case BGP_FLOWSPEC_RETURN_STRING:
+ if (prefix_local.family == AF_INET6) {
+ int ret;
+
+ ret = snprintfrr(
+ display, BGP_FLOWSPEC_STRING_DISPLAY_MAX,
+ "%pFX/off %u", &prefix_local, prefix_offset);
+ if (ret < 0) {
+ *error = -1;
+ break;
+ }
+ } else
+ prefix2str(&prefix_local, display,
+ BGP_FLOWSPEC_STRING_DISPLAY_MAX);
+ break;
+ case BGP_FLOWSPEC_CONVERT_TO_NON_OPAQUE:
+ if (prefix)
+ prefix_copy(prefix, &prefix_local);
+ break;
+ case BGP_FLOWSPEC_VALIDATE_ONLY:
+ default:
+ break;
+ }
+ return offset;
+}
+
+/*
+ * handle the flowspec operator NLRI
+ * return number of bytes analysed
+ * if there is an error, the passed error param is used to give error:
+ * -1 if decoding error,
+ * if result is a string, its assumed length
+ * is BGP_FLOWSPEC_STRING_DISPLAY_MAX
+ */
+int bgp_flowspec_op_decode(enum bgp_flowspec_util_nlri_t type,
+ uint8_t *nlri_ptr,
+ uint32_t max_len,
+ void *result, int *error)
+{
+ int op[8];
+ int len, value, value_size;
+ int loop = 0;
+ char *ptr = (char *)result; /* for return_string */
+ uint32_t offset = 0;
+ int len_string = BGP_FLOWSPEC_STRING_DISPLAY_MAX;
+ int len_written;
+ struct bgp_pbr_match_val *mval = (struct bgp_pbr_match_val *)result;
+
+ *error = 0;
+ do {
+ if (loop > BGP_PBR_MATCH_VAL_MAX)
+ *error = -2;
+ hex2bin(&nlri_ptr[offset], op);
+ offset++;
+ len = 2*op[2]+op[3];
+ value_size = 1 << len;
+ value = hexstr2num(&nlri_ptr[offset], value_size);
+ /* can not be < and > at the same time */
+ if (op[5] == 1 && op[6] == 1)
+ *error = -1;
+ /* if first element, AND bit can not be set */
+ if (op[1] == 1 && loop == 0)
+ *error = -1;
+ switch (type) {
+ case BGP_FLOWSPEC_RETURN_STRING:
+ if (loop) {
+ len_written = snprintf(ptr, len_string,
+ ", ");
+ len_string -= len_written;
+ ptr += len_written;
+ }
+ if (op[5] == 1) {
+ len_written = snprintf(ptr, len_string,
+ "<");
+ len_string -= len_written;
+ ptr += len_written;
+ }
+ if (op[6] == 1) {
+ len_written = snprintf(ptr, len_string,
+ ">");
+ len_string -= len_written;
+ ptr += len_written;
+ }
+ if (op[7] == 1) {
+ len_written = snprintf(ptr, len_string,
+ "=");
+ len_string -= len_written;
+ ptr += len_written;
+ }
+ len_written = snprintf(ptr, len_string,
+ " %d ", value);
+ len_string -= len_written;
+ ptr += len_written;
+ break;
+ case BGP_FLOWSPEC_CONVERT_TO_NON_OPAQUE:
+ /* limitation: stop converting */
+ if (*error == -2)
+ break;
+ mval->value = value;
+ if (op[5] == 1)
+ mval->compare_operator |=
+ OPERATOR_COMPARE_LESS_THAN;
+ if (op[6] == 1)
+ mval->compare_operator |=
+ OPERATOR_COMPARE_GREATER_THAN;
+ if (op[7] == 1)
+ mval->compare_operator |=
+ OPERATOR_COMPARE_EQUAL_TO;
+ if (op[1] == 1)
+ mval->unary_operator = OPERATOR_UNARY_AND;
+ else
+ mval->unary_operator = OPERATOR_UNARY_OR;
+ mval++;
+ break;
+ case BGP_FLOWSPEC_VALIDATE_ONLY:
+ default:
+ /* no action */
+ break;
+ }
+ offset += value_size;
+ loop++;
+ } while (op[0] == 0 && offset < max_len - 1);
+ if (offset > max_len)
+ *error = -1;
+ /* use error parameter to count the number of entries */
+ if (*error == 0)
+ *error = loop;
+ return offset;
+}
+
+
+/*
+ * handle the flowspec tcpflags or fragment field
+ * return number of bytes analysed
+ * if there is an error, the passed error param is used to give error:
+ * -1 if decoding error,
+ * if result is a string, its assumed length
+ * is BGP_FLOWSPEC_STRING_DISPLAY_MAX
+ */
+int bgp_flowspec_bitmask_decode(enum bgp_flowspec_util_nlri_t type,
+ uint8_t *nlri_ptr,
+ uint32_t max_len,
+ void *result, int *error)
+{
+ int op[8];
+ int len, value_size, loop = 0, value;
+ char *ptr = (char *)result; /* for return_string */
+ struct bgp_pbr_match_val *mval = (struct bgp_pbr_match_val *)result;
+ uint32_t offset = 0;
+ int len_string = BGP_FLOWSPEC_STRING_DISPLAY_MAX;
+ int len_written;
+
+ *error = 0;
+ do {
+ if (loop > BGP_PBR_MATCH_VAL_MAX)
+ *error = -2;
+ hex2bin(&nlri_ptr[offset], op);
+ /* if first element, AND bit can not be set */
+ if (op[1] == 1 && loop == 0)
+ *error = -1;
+ offset++;
+ len = 2 * op[2] + op[3];
+ value_size = 1 << len;
+ value = hexstr2num(&nlri_ptr[offset], value_size);
+ switch (type) {
+ case BGP_FLOWSPEC_RETURN_STRING:
+ if (op[1] == 1 && loop != 0) {
+ len_written = snprintf(ptr, len_string,
+ ",&");
+ len_string -= len_written;
+ ptr += len_written;
+ } else if (op[1] == 0 && loop != 0) {
+ len_written = snprintf(ptr, len_string,
+ ",|");
+ len_string -= len_written;
+ ptr += len_written;
+ }
+ if (op[7] == 1) {
+ len_written = snprintf(ptr, len_string,
+ "= ");
+ len_string -= len_written;
+ ptr += len_written;
+ } else {
+ len_written = snprintf(ptr, len_string,
+ "∋ ");
+ len_string -= len_written;
+ ptr += len_written;
+ }
+ if (op[6] == 1) {
+ len_written = snprintf(ptr, len_string,
+ "! ");
+ len_string -= len_written;
+ ptr += len_written;
+ }
+ len_written = snprintf(ptr, len_string,
+ "%d", value);
+ len_string -= len_written;
+ ptr += len_written;
+ break;
+ case BGP_FLOWSPEC_CONVERT_TO_NON_OPAQUE:
+ /* limitation: stop converting */
+ if (*error == -2)
+ break;
+ mval->value = value;
+ if (op[6] == 1) {
+ /* different from */
+ mval->compare_operator |=
+ OPERATOR_COMPARE_LESS_THAN;
+ mval->compare_operator |=
+ OPERATOR_COMPARE_GREATER_THAN;
+ } else
+ mval->compare_operator |=
+ OPERATOR_COMPARE_EQUAL_TO;
+ if (op[7] == 1)
+ mval->compare_operator |=
+ OPERATOR_COMPARE_EXACT_MATCH;
+ if (op[1] == 1)
+ mval->unary_operator =
+ OPERATOR_UNARY_AND;
+ else
+ mval->unary_operator =
+ OPERATOR_UNARY_OR;
+ mval++;
+ break;
+ case BGP_FLOWSPEC_VALIDATE_ONLY:
+ default:
+ /* no action */
+ break;
+ }
+ offset += value_size;
+ loop++;
+ } while (op[0] == 0 && offset < max_len - 1);
+ if (offset > max_len)
+ *error = -1;
+ /* use error parameter to count the number of entries */
+ if (*error == 0)
+ *error = loop;
+ return offset;
+}
+
+int bgp_flowspec_match_rules_fill(uint8_t *nlri_content, int len,
+ struct bgp_pbr_entry_main *bpem,
+ afi_t afi)
+{
+ int offset = 0, error = 0;
+ struct prefix *prefix;
+ struct bgp_pbr_match_val *mval;
+ uint8_t *match_num;
+ uint8_t bitmask = 0;
+ int ret = 0, type;
+ uint8_t *prefix_offset;
+
+ while (offset < len - 1 && error >= 0) {
+ type = nlri_content[offset];
+ offset++;
+ switch (type) {
+ case FLOWSPEC_DEST_PREFIX:
+ case FLOWSPEC_SRC_PREFIX:
+ bitmask = 0;
+ if (type == FLOWSPEC_DEST_PREFIX) {
+ bitmask |= PREFIX_DST_PRESENT;
+ prefix = &bpem->dst_prefix;
+ prefix_offset = &bpem->dst_prefix_offset;
+ } else {
+ bitmask |= PREFIX_SRC_PRESENT;
+ prefix = &bpem->src_prefix;
+ prefix_offset = &bpem->src_prefix_offset;
+ }
+ ret = bgp_flowspec_ip_address(
+ BGP_FLOWSPEC_CONVERT_TO_NON_OPAQUE,
+ nlri_content + offset,
+ len - offset,
+ prefix, &error,
+ afi, prefix_offset);
+ if (error < 0)
+ flog_err(EC_BGP_FLOWSPEC_PACKET,
+ "%s: flowspec_ip_address error %d",
+ __func__, error);
+ else {
+ /* if src or dst address is 0.0.0.0,
+ * ignore that rule
+ */
+ if (prefix->family == AF_INET
+ && prefix->u.prefix4.s_addr == INADDR_ANY)
+ bpem->match_bitmask_iprule |= bitmask;
+ else if (prefix->family == AF_INET6
+ && !memcmp(&prefix->u.prefix6,
+ &in6addr_any,
+ sizeof(struct in6_addr)))
+ bpem->match_bitmask_iprule |= bitmask;
+ else
+ bpem->match_bitmask |= bitmask;
+ }
+ offset += ret;
+ break;
+ case FLOWSPEC_FLOW_LABEL:
+ if (afi == AFI_IP) {
+ error = -1;
+ continue;
+ }
+ match_num = &(bpem->match_flowlabel_num);
+ mval = (struct bgp_pbr_match_val *)
+ &(bpem->flow_label);
+ offset += bgp_flowspec_call_non_opaque_decode(
+ nlri_content + offset,
+ len - offset,
+ mval, match_num,
+ &error);
+ break;
+ case FLOWSPEC_IP_PROTOCOL:
+ match_num = &(bpem->match_protocol_num);
+ mval = (struct bgp_pbr_match_val *)
+ &(bpem->protocol);
+ offset += bgp_flowspec_call_non_opaque_decode(
+ nlri_content + offset,
+ len - offset,
+ mval, match_num,
+ &error);
+ break;
+ case FLOWSPEC_PORT:
+ match_num = &(bpem->match_port_num);
+ mval = (struct bgp_pbr_match_val *)
+ &(bpem->port);
+ offset += bgp_flowspec_call_non_opaque_decode(
+ nlri_content + offset,
+ len - offset,
+ mval, match_num,
+ &error);
+ break;
+ case FLOWSPEC_DEST_PORT:
+ match_num = &(bpem->match_dst_port_num);
+ mval = (struct bgp_pbr_match_val *)
+ &(bpem->dst_port);
+ offset += bgp_flowspec_call_non_opaque_decode(
+ nlri_content + offset,
+ len - offset,
+ mval, match_num,
+ &error);
+ break;
+ case FLOWSPEC_SRC_PORT:
+ match_num = &(bpem->match_src_port_num);
+ mval = (struct bgp_pbr_match_val *)
+ &(bpem->src_port);
+ offset += bgp_flowspec_call_non_opaque_decode(
+ nlri_content + offset,
+ len - offset,
+ mval, match_num,
+ &error);
+ break;
+ case FLOWSPEC_ICMP_TYPE:
+ match_num = &(bpem->match_icmp_type_num);
+ mval = (struct bgp_pbr_match_val *)
+ &(bpem->icmp_type);
+ offset += bgp_flowspec_call_non_opaque_decode(
+ nlri_content + offset,
+ len - offset,
+ mval, match_num,
+ &error);
+ break;
+ case FLOWSPEC_ICMP_CODE:
+ match_num = &(bpem->match_icmp_code_num);
+ mval = (struct bgp_pbr_match_val *)
+ &(bpem->icmp_code);
+ offset += bgp_flowspec_call_non_opaque_decode(
+ nlri_content + offset,
+ len - offset,
+ mval, match_num,
+ &error);
+ break;
+ case FLOWSPEC_PKT_LEN:
+ match_num =
+ &(bpem->match_packet_length_num);
+ mval = (struct bgp_pbr_match_val *)
+ &(bpem->packet_length);
+ offset += bgp_flowspec_call_non_opaque_decode(
+ nlri_content + offset,
+ len - offset,
+ mval, match_num,
+ &error);
+ break;
+ case FLOWSPEC_DSCP:
+ match_num = &(bpem->match_dscp_num);
+ mval = (struct bgp_pbr_match_val *)
+ &(bpem->dscp);
+ offset += bgp_flowspec_call_non_opaque_decode(
+ nlri_content + offset,
+ len - offset,
+ mval, match_num,
+ &error);
+ break;
+ case FLOWSPEC_TCP_FLAGS:
+ ret = bgp_flowspec_bitmask_decode(
+ BGP_FLOWSPEC_CONVERT_TO_NON_OPAQUE,
+ nlri_content + offset,
+ len - offset,
+ &bpem->tcpflags, &error);
+ if (error < 0)
+ flog_err(
+ EC_BGP_FLOWSPEC_PACKET,
+ "%s: flowspec_tcpflags_decode error %d",
+ __func__, error);
+ else
+ bpem->match_tcpflags_num = error;
+ /* contains the number of slots used */
+ offset += ret;
+ break;
+ case FLOWSPEC_FRAGMENT:
+ ret = bgp_flowspec_bitmask_decode(
+ BGP_FLOWSPEC_CONVERT_TO_NON_OPAQUE,
+ nlri_content + offset,
+ len - offset, &bpem->fragment,
+ &error);
+ if (error < 0)
+ flog_err(
+ EC_BGP_FLOWSPEC_PACKET,
+ "%s: flowspec_fragment_type_decode error %d",
+ __func__, error);
+ else
+ bpem->match_fragment_num = error;
+ offset += ret;
+ break;
+ default:
+ flog_err(EC_LIB_DEVELOPMENT, "%s: unknown type %d",
+ __func__, type);
+ }
+ }
+ if (bpem->match_packet_length_num || bpem->match_fragment_num
+ || bpem->match_tcpflags_num || bpem->match_dscp_num
+ || bpem->match_icmp_code_num || bpem->match_icmp_type_num
+ || bpem->match_port_num || bpem->match_src_port_num
+ || bpem->match_dst_port_num || bpem->match_protocol_num
+ || bpem->match_bitmask || bpem->match_flowlabel_num)
+ bpem->type = BGP_PBR_IPSET;
+ else if ((bpem->match_bitmask_iprule & PREFIX_SRC_PRESENT) ||
+ (bpem->match_bitmask_iprule & PREFIX_DST_PRESENT))
+ /* the extracted policy rule may not need an
+ * iptables/ipset filtering. check this may not be
+ * a standard ip rule : permit any to any ( eg)
+ */
+ bpem->type = BGP_PBR_IPRULE;
+ else
+ bpem->type = BGP_PBR_UNDEFINED;
+ return error;
+}
+
+/* return 1 if FS entry invalid or no NH IP */
+bool bgp_flowspec_get_first_nh(struct bgp *bgp, struct bgp_path_info *pi,
+ struct prefix *p, afi_t afi)
+{
+ struct bgp_pbr_entry_main api;
+ int i;
+ struct bgp_dest *dest = pi->net;
+ struct bgp_pbr_entry_action *api_action;
+
+ memset(&api, 0, sizeof(api));
+ if (bgp_pbr_build_and_validate_entry(bgp_dest_get_prefix(dest), pi,
+ &api)
+ < 0)
+ return true;
+ for (i = 0; i < api.action_num; i++) {
+ api_action = &api.actions[i];
+ if (api_action->action != ACTION_REDIRECT_IP)
+ continue;
+ p->family = afi2family(afi);
+ if (afi == AFI_IP) {
+ p->prefixlen = IPV4_MAX_BITLEN;
+ p->u.prefix4 = api_action->u.zr.redirect_ip_v4;
+ } else {
+ p->prefixlen = IPV6_MAX_BITLEN;
+ memcpy(&p->u.prefix6, &api_action->u.zr.redirect_ip_v6,
+ sizeof(struct in6_addr));
+ }
+ return false;
+ }
+ return true;
+}
diff --git a/bgpd/bgp_flowspec_util.h b/bgpd/bgp_flowspec_util.h
new file mode 100644
index 0000000..6cc4854
--- /dev/null
+++ b/bgpd/bgp_flowspec_util.h
@@ -0,0 +1,62 @@
+/* BGP Flowspec header for utilities
+ * Copyright (C) 2018 6WIND
+ *
+ * FRRouting is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License as published by the
+ * Free Software Foundation; either version 2, or (at your option) any
+ * later version.
+ *
+ * FRRouting is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program; see the file COPYING; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
+ */
+
+#ifndef _FRR_BGP_FLOWSPEC_UTIL_H
+#define _FRR_BGP_FLOWSPEC_UTIL_H
+
+#include "zclient.h"
+
+#define BGP_FLOWSPEC_STRING_DISPLAY_MAX 512
+
+enum bgp_flowspec_util_nlri_t {
+ BGP_FLOWSPEC_VALIDATE_ONLY = 0,
+ BGP_FLOWSPEC_RETURN_STRING = 1,
+ BGP_FLOWSPEC_CONVERT_TO_NON_OPAQUE = 2,
+ BGP_FLOWSPEC_RETURN_JSON = 3,
+};
+
+
+extern int bgp_flowspec_op_decode(enum bgp_flowspec_util_nlri_t type,
+ uint8_t *nlri_ptr,
+ uint32_t max_len,
+ void *result, int *error);
+
+extern int bgp_flowspec_ip_address(enum bgp_flowspec_util_nlri_t type,
+ uint8_t *nlri_ptr,
+ uint32_t max_len,
+ void *result, int *error,
+ afi_t afi, uint8_t *ipv6_offset);
+
+extern int bgp_flowspec_bitmask_decode(enum bgp_flowspec_util_nlri_t type,
+ uint8_t *nlri_ptr,
+ uint32_t max_len,
+ void *result, int *error);
+
+struct bgp_pbr_entry_main;
+extern int bgp_flowspec_match_rules_fill(uint8_t *nlri_content, int len,
+ struct bgp_pbr_entry_main *bpem,
+ afi_t afi);
+
+extern bool bgp_flowspec_contains_prefix(const struct prefix *pfs,
+ struct prefix *input,
+ int prefix_check);
+
+extern bool bgp_flowspec_get_first_nh(struct bgp *bgp, struct bgp_path_info *pi,
+ struct prefix *nh, afi_t afi);
+
+#endif /* _FRR_BGP_FLOWSPEC_UTIL_H */
diff --git a/bgpd/bgp_flowspec_vty.c b/bgpd/bgp_flowspec_vty.c
new file mode 100644
index 0000000..02dcdfc
--- /dev/null
+++ b/bgpd/bgp_flowspec_vty.c
@@ -0,0 +1,633 @@
+/* BGP FlowSpec VTY
+ * Copyright (C) 2018 6WIND
+ *
+ * FRRouting is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License as published by the
+ * Free Software Foundation; either version 2, or (at your option) any
+ * later version.
+ *
+ * FRRouting is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program; see the file COPYING; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
+ */
+
+#include <zebra.h>
+#include "command.h"
+
+#include "bgpd/bgpd.h"
+#include "bgpd/bgp_table.h"
+#include "bgpd/bgp_attr.h"
+#include "bgpd/bgp_ecommunity.h"
+#include "bgpd/bgp_vty.h"
+#include "bgpd/bgp_route.h"
+#include "bgpd/bgp_aspath.h"
+#include "bgpd/bgp_flowspec.h"
+#include "bgpd/bgp_flowspec_util.h"
+#include "bgpd/bgp_flowspec_private.h"
+#include "bgpd/bgp_debug.h"
+#include "bgpd/bgp_pbr.h"
+
+/* Local Structures and variables declarations
+ * This code block hosts the struct declared that host the flowspec rules
+ * as well as some structure used to convert to stringx
+ */
+
+static const struct message bgp_flowspec_display_large[] = {
+ {FLOWSPEC_DEST_PREFIX, "Destination Address"},
+ {FLOWSPEC_SRC_PREFIX, "Source Address"},
+ {FLOWSPEC_IP_PROTOCOL, "IP Protocol"},
+ {FLOWSPEC_PORT, "Port"},
+ {FLOWSPEC_DEST_PORT, "Destination Port"},
+ {FLOWSPEC_SRC_PORT, "Source Port"},
+ {FLOWSPEC_ICMP_TYPE, "ICMP Type"},
+ {FLOWSPEC_ICMP_CODE, "ICMP Code"},
+ {FLOWSPEC_TCP_FLAGS, "TCP Flags"},
+ {FLOWSPEC_PKT_LEN, "Packet Length"},
+ {FLOWSPEC_DSCP, "DSCP field"},
+ {FLOWSPEC_FRAGMENT, "Packet Fragment"},
+ {FLOWSPEC_FLOW_LABEL, "Packet Flow Label"},
+ {0}
+};
+
+static const struct message bgp_flowspec_display_min[] = {
+ {FLOWSPEC_DEST_PREFIX, "to"},
+ {FLOWSPEC_SRC_PREFIX, "from"},
+ {FLOWSPEC_IP_PROTOCOL, "proto"},
+ {FLOWSPEC_PORT, "port"},
+ {FLOWSPEC_DEST_PORT, "dstp"},
+ {FLOWSPEC_SRC_PORT, "srcp"},
+ {FLOWSPEC_ICMP_TYPE, "type"},
+ {FLOWSPEC_ICMP_CODE, "code"},
+ {FLOWSPEC_TCP_FLAGS, "tcp"},
+ {FLOWSPEC_PKT_LEN, "pktlen"},
+ {FLOWSPEC_DSCP, "dscp"},
+ {FLOWSPEC_FRAGMENT, "pktfrag"},
+ {FLOWSPEC_FLOW_LABEL, "flwlbl"},
+ {0}
+};
+
+#define FS_STRING_UPDATE(count, ptr, format, remaining_len) do { \
+ int _len_written; \
+ \
+ if (((format) == NLRI_STRING_FORMAT_DEBUG) && (count)) {\
+ _len_written = snprintf((ptr), (remaining_len), \
+ ", "); \
+ (remaining_len) -= _len_written; \
+ (ptr) += _len_written; \
+ } else if (((format) == NLRI_STRING_FORMAT_MIN) \
+ && (count)) { \
+ _len_written = snprintf((ptr), (remaining_len), \
+ " "); \
+ (remaining_len) -= _len_written; \
+ (ptr) += _len_written; \
+ } \
+ count++; \
+ } while (0)
+
+/* Parse FLOWSPEC NLRI
+ * passed return_string string has assumed length
+ * BGP_FLOWSPEC_STRING_DISPLAY_MAX
+ */
+void bgp_fs_nlri_get_string(unsigned char *nlri_content, size_t len,
+ char *return_string, int format,
+ json_object *json_path,
+ afi_t afi)
+{
+ uint32_t offset = 0;
+ int type;
+ int ret = 0, error = 0;
+ char *ptr = return_string;
+ char local_string[BGP_FLOWSPEC_STRING_DISPLAY_MAX];
+ int count = 0;
+ char extra[2] = "";
+ char pre_extra[2] = "";
+ const struct message *bgp_flowspec_display;
+ enum bgp_flowspec_util_nlri_t type_util;
+ int len_string = BGP_FLOWSPEC_STRING_DISPLAY_MAX;
+ int len_written;
+
+ if (format == NLRI_STRING_FORMAT_LARGE) {
+ snprintf(pre_extra, sizeof(pre_extra), "\t");
+ snprintf(extra, sizeof(extra), "\n");
+ bgp_flowspec_display = bgp_flowspec_display_large;
+ } else
+ bgp_flowspec_display = bgp_flowspec_display_min;
+ /* if needed. type_util can be set to other values */
+ type_util = BGP_FLOWSPEC_RETURN_STRING;
+ error = 0;
+ while (offset < len-1 && error >= 0) {
+ type = nlri_content[offset];
+ offset++;
+ switch (type) {
+ case FLOWSPEC_DEST_PREFIX:
+ case FLOWSPEC_SRC_PREFIX:
+ ret = bgp_flowspec_ip_address(
+ type_util,
+ nlri_content+offset,
+ len - offset,
+ local_string, &error,
+ afi, NULL);
+ if (ret <= 0)
+ break;
+ if (json_path) {
+ json_object_string_add(json_path,
+ lookup_msg(bgp_flowspec_display, type, ""),
+ local_string);
+ break;
+ }
+ FS_STRING_UPDATE(count, ptr, format, len_string);
+ len_written = snprintf(ptr, len_string, "%s%s %s%s",
+ pre_extra,
+ lookup_msg(bgp_flowspec_display,
+ type, ""),
+ local_string, extra);
+ len_string -= len_written;
+ ptr += len_written;
+ break;
+ case FLOWSPEC_FLOW_LABEL:
+ case FLOWSPEC_IP_PROTOCOL:
+ case FLOWSPEC_PORT:
+ case FLOWSPEC_DEST_PORT:
+ case FLOWSPEC_SRC_PORT:
+ case FLOWSPEC_ICMP_TYPE:
+ case FLOWSPEC_ICMP_CODE:
+ ret = bgp_flowspec_op_decode(type_util,
+ nlri_content+offset,
+ len - offset,
+ local_string, &error);
+ if (ret <= 0)
+ break;
+ if (json_path) {
+ json_object_string_add(json_path,
+ lookup_msg(bgp_flowspec_display, type, ""),
+ local_string);
+ break;
+ }
+ FS_STRING_UPDATE(count, ptr, format, len_string);
+ len_written = snprintf(ptr, len_string, "%s%s %s%s",
+ pre_extra,
+ lookup_msg(bgp_flowspec_display,
+ type, ""),
+ local_string, extra);
+ len_string -= len_written;
+ ptr += len_written;
+ break;
+ case FLOWSPEC_TCP_FLAGS:
+ ret = bgp_flowspec_bitmask_decode(
+ type_util,
+ nlri_content+offset,
+ len - offset,
+ local_string, &error);
+ if (ret <= 0)
+ break;
+ if (json_path) {
+ json_object_string_add(json_path,
+ lookup_msg(bgp_flowspec_display,
+ type, ""),
+ local_string);
+ break;
+ }
+ FS_STRING_UPDATE(count, ptr, format, len_string);
+ len_written = snprintf(ptr, len_string, "%s%s %s%s",
+ pre_extra,
+ lookup_msg(bgp_flowspec_display,
+ type, ""),
+ local_string, extra);
+ len_string -= len_written;
+ ptr += len_written;
+ break;
+ case FLOWSPEC_PKT_LEN:
+ case FLOWSPEC_DSCP:
+ ret = bgp_flowspec_op_decode(
+ type_util,
+ nlri_content + offset,
+ len - offset, local_string,
+ &error);
+ if (ret <= 0)
+ break;
+ if (json_path) {
+ json_object_string_add(json_path,
+ lookup_msg(bgp_flowspec_display, type, ""),
+ local_string);
+ break;
+ }
+ FS_STRING_UPDATE(count, ptr, format, len_string);
+ len_written = snprintf(ptr, len_string, "%s%s %s%s",
+ pre_extra,
+ lookup_msg(bgp_flowspec_display,
+ type, ""),
+ local_string, extra);
+ len_string -= len_written;
+ ptr += len_written;
+ break;
+ case FLOWSPEC_FRAGMENT:
+ ret = bgp_flowspec_bitmask_decode(
+ type_util,
+ nlri_content+offset,
+ len - offset,
+ local_string, &error);
+ if (ret <= 0)
+ break;
+ if (json_path) {
+ json_object_string_add(json_path,
+ lookup_msg(bgp_flowspec_display,
+ type, ""),
+ local_string);
+ break;
+ }
+ FS_STRING_UPDATE(count, ptr, format, len_string);
+ len_written = snprintf(ptr, len_string, "%s%s %s%s",
+ pre_extra,
+ lookup_msg(bgp_flowspec_display,
+ type, ""),
+ local_string, extra);
+ len_string -= len_written;
+ ptr += len_written;
+ break;
+ default:
+ error = -1;
+ break;
+ }
+ offset += ret;
+ }
+}
+
+void route_vty_out_flowspec(struct vty *vty, const struct prefix *p,
+ struct bgp_path_info *path, int display,
+ json_object *json_paths)
+{
+ struct attr *attr;
+ char return_string[BGP_FLOWSPEC_STRING_DISPLAY_MAX];
+ char *s1 = NULL, *s2 = NULL;
+ json_object *json_nlri_path = NULL;
+ json_object *json_ecom_path = NULL;
+ json_object *json_time_path = NULL;
+ char timebuf[BGP_UPTIME_LEN];
+ struct ecommunity *ipv6_ecomm = NULL;
+
+ if (p == NULL || p->family != AF_FLOWSPEC)
+ return;
+ if (json_paths) {
+ if (display == NLRI_STRING_FORMAT_JSON)
+ json_nlri_path = json_object_new_object();
+ else
+ json_nlri_path = json_paths;
+ }
+ if (display == NLRI_STRING_FORMAT_LARGE && path)
+ vty_out(vty, "BGP flowspec entry: (flags 0x%x)\n",
+ path->flags);
+ bgp_fs_nlri_get_string((unsigned char *)
+ p->u.prefix_flowspec.ptr,
+ p->u.prefix_flowspec.prefixlen,
+ return_string,
+ display,
+ json_nlri_path,
+ family2afi(p->u.prefix_flowspec
+ .family));
+ if (display == NLRI_STRING_FORMAT_LARGE)
+ vty_out(vty, "%s", return_string);
+ else if (display == NLRI_STRING_FORMAT_DEBUG)
+ vty_out(vty, "%s", return_string);
+ else if (display == NLRI_STRING_FORMAT_MIN)
+ vty_out(vty, " %-30s", return_string);
+ else if (json_paths && display == NLRI_STRING_FORMAT_JSON)
+ json_object_array_add(json_paths, json_nlri_path);
+ if (!path)
+ return;
+
+ if (path->attr)
+ ipv6_ecomm = bgp_attr_get_ipv6_ecommunity(path->attr);
+
+ if (path->attr && (bgp_attr_get_ecommunity(path->attr) || ipv6_ecomm)) {
+ /* Print attribute */
+ attr = path->attr;
+ if (bgp_attr_get_ecommunity(attr))
+ s1 = ecommunity_ecom2str(bgp_attr_get_ecommunity(attr),
+ ECOMMUNITY_FORMAT_ROUTE_MAP,
+ 0);
+ if (ipv6_ecomm)
+ s2 = ecommunity_ecom2str(
+ ipv6_ecomm, ECOMMUNITY_FORMAT_ROUTE_MAP, 0);
+ if (!s1 && !s2)
+ return;
+ if (display == NLRI_STRING_FORMAT_LARGE)
+ vty_out(vty, "\t%s%s%s\n", s1 ? s1 : "",
+ s2 && s1 ? " " : "", s2 ? s2 : "");
+ else if (display == NLRI_STRING_FORMAT_MIN)
+ vty_out(vty, "%s%s", s1 ? s1 : "", s2 ? s2 : "");
+ else if (json_paths) {
+ json_ecom_path = json_object_new_object();
+ if (s1)
+ json_object_string_add(json_ecom_path,
+ "ecomlist", s1);
+ if (s2)
+ json_object_string_add(json_ecom_path,
+ "ecom6list", s2);
+ if (display == NLRI_STRING_FORMAT_JSON)
+ json_object_array_add(json_paths,
+ json_ecom_path);
+ }
+ if (display == NLRI_STRING_FORMAT_LARGE) {
+ char local_buff[INET6_ADDRSTRLEN];
+
+ local_buff[0] = '\0';
+ if (p->u.prefix_flowspec.family == AF_INET
+ && attr->nexthop.s_addr != INADDR_ANY)
+ inet_ntop(AF_INET,
+ &attr->nexthop.s_addr,
+ local_buff,
+ INET6_ADDRSTRLEN);
+ else if (p->u.prefix_flowspec.family == AF_INET6 &&
+ attr->mp_nexthop_len != 0 &&
+ attr->mp_nexthop_len != BGP_ATTR_NHLEN_IPV4 &&
+ attr->mp_nexthop_len != BGP_ATTR_NHLEN_VPNV4)
+ inet_ntop(AF_INET6,
+ &attr->mp_nexthop_global,
+ local_buff,
+ INET6_ADDRSTRLEN);
+ if (local_buff[0] != '\0')
+ vty_out(vty, "\tNLRI NH %s\n",
+ local_buff);
+ }
+ XFREE(MTYPE_ECOMMUNITY_STR, s1);
+ XFREE(MTYPE_ECOMMUNITY_STR, s2);
+ }
+ peer_uptime(path->uptime, timebuf, BGP_UPTIME_LEN, 0, NULL);
+ if (display == NLRI_STRING_FORMAT_LARGE) {
+ vty_out(vty, "\treceived for %8s\n", timebuf);
+ } else if (json_paths) {
+ json_time_path = json_object_new_object();
+ json_object_string_add(json_time_path,
+ "time", timebuf);
+ if (display == NLRI_STRING_FORMAT_JSON)
+ json_object_array_add(json_paths, json_time_path);
+ }
+ if (display == NLRI_STRING_FORMAT_LARGE) {
+ struct bgp_path_info_extra *extra =
+ bgp_path_info_extra_get(path);
+ bool list_began = false;
+
+ if (extra->bgp_fs_pbr && listcount(extra->bgp_fs_pbr)) {
+ struct listnode *node;
+ struct bgp_pbr_match_entry *bpme;
+ struct bgp_pbr_match *bpm;
+ struct list *list_bpm;
+
+ list_bpm = list_new();
+ vty_out(vty, "\tinstalled in PBR");
+ for (ALL_LIST_ELEMENTS_RO(extra->bgp_fs_pbr,
+ node, bpme)) {
+ bpm = bpme->backpointer;
+ if (listnode_lookup(list_bpm, bpm))
+ continue;
+ listnode_add(list_bpm, bpm);
+ if (!list_began) {
+ vty_out(vty, " (");
+ list_began = true;
+ } else
+ vty_out(vty, ", ");
+ vty_out(vty, "%s", bpm->ipset_name);
+ }
+ list_delete(&list_bpm);
+ }
+ if (extra->bgp_fs_iprule && listcount(extra->bgp_fs_iprule)) {
+ struct listnode *node;
+ struct bgp_pbr_rule *bpr;
+
+ if (!list_began)
+ vty_out(vty, "\tinstalled in PBR");
+ for (ALL_LIST_ELEMENTS_RO(extra->bgp_fs_iprule,
+ node, bpr)) {
+ if (!bpr->action)
+ continue;
+ if (!list_began) {
+ vty_out(vty, " (");
+ list_began = true;
+ } else
+ vty_out(vty, ", ");
+ vty_out(vty, "-ipv4-rule %d action lookup %u-",
+ bpr->priority,
+ bpr->action->table_id);
+ }
+ }
+ if (list_began)
+ vty_out(vty, ")\n");
+ else
+ vty_out(vty, "\tnot installed in PBR\n");
+ }
+}
+
+int bgp_show_table_flowspec(struct vty *vty, struct bgp *bgp, afi_t afi,
+ struct bgp_table *table, enum bgp_show_type type,
+ void *output_arg, bool use_json, int is_last,
+ unsigned long *output_cum, unsigned long *total_cum)
+{
+ struct bgp_path_info *pi;
+ struct bgp_dest *dest;
+ unsigned long total_count = 0;
+ json_object *json_paths = NULL;
+ int display = NLRI_STRING_FORMAT_LARGE;
+
+ if (type != bgp_show_type_detail)
+ return CMD_SUCCESS;
+
+ for (dest = bgp_table_top(table); dest; dest = bgp_route_next(dest)) {
+ pi = bgp_dest_get_bgp_path_info(dest);
+ if (pi == NULL)
+ continue;
+ if (use_json) {
+ json_paths = json_object_new_array();
+ display = NLRI_STRING_FORMAT_JSON;
+ }
+ for (; pi; pi = pi->next) {
+ total_count++;
+ route_vty_out_flowspec(vty, bgp_dest_get_prefix(dest),
+ pi, display, json_paths);
+ }
+ if (use_json) {
+ vty_json(vty, json_paths);
+ json_paths = NULL;
+ }
+ }
+ if (total_count && !use_json)
+ vty_out(vty,
+ "\nDisplayed %ld flowspec entries\n",
+ total_count);
+ return CMD_SUCCESS;
+}
+
+DEFUN (debug_bgp_flowspec,
+ debug_bgp_flowspec_cmd,
+ "debug bgp flowspec",
+ DEBUG_STR
+ BGP_STR
+ "BGP allow flowspec debugging entries\n")
+{
+ if (vty->node == CONFIG_NODE)
+ DEBUG_ON(flowspec, FLOWSPEC);
+ else {
+ TERM_DEBUG_ON(flowspec, FLOWSPEC);
+ vty_out(vty, "BGP flowspec debugging is on\n");
+ }
+ return CMD_SUCCESS;
+}
+
+DEFUN (no_debug_bgp_flowspec,
+ no_debug_bgp_flowspec_cmd,
+ "no debug bgp flowspec",
+ NO_STR
+ DEBUG_STR
+ BGP_STR
+ "BGP allow flowspec debugging entries\n")
+{
+ if (vty->node == CONFIG_NODE)
+ DEBUG_OFF(flowspec, FLOWSPEC);
+ else {
+ TERM_DEBUG_OFF(flowspec, FLOWSPEC);
+ vty_out(vty, "BGP flowspec debugging is off\n");
+ }
+ return CMD_SUCCESS;
+}
+
+int bgp_fs_config_write_pbr(struct vty *vty, struct bgp *bgp,
+ afi_t afi, safi_t safi)
+{
+ struct bgp_pbr_interface *pbr_if;
+ bool declare_node = false;
+ struct bgp_pbr_config *bgp_pbr_cfg = bgp->bgp_pbr_cfg;
+ struct bgp_pbr_interface_head *head;
+ bool bgp_pbr_interface_any;
+
+ if (!bgp_pbr_cfg || safi != SAFI_FLOWSPEC)
+ return 0;
+ if (afi == AFI_IP) {
+ head = &(bgp_pbr_cfg->ifaces_by_name_ipv4);
+ bgp_pbr_interface_any = bgp_pbr_cfg->pbr_interface_any_ipv4;
+ } else if (afi == AFI_IP6) {
+ head = &(bgp_pbr_cfg->ifaces_by_name_ipv6);
+ bgp_pbr_interface_any = bgp_pbr_cfg->pbr_interface_any_ipv6;
+ } else {
+ return 0;
+ }
+ if (!RB_EMPTY(bgp_pbr_interface_head, head) ||
+ !bgp_pbr_interface_any)
+ declare_node = true;
+ RB_FOREACH (pbr_if, bgp_pbr_interface_head, head) {
+ vty_out(vty, " local-install %s\n", pbr_if->name);
+ }
+ return declare_node ? 1 : 0;
+}
+
+static int bgp_fs_local_install_interface(struct bgp *bgp,
+ const char *no, const char *ifname,
+ afi_t afi)
+{
+ struct bgp_pbr_interface *pbr_if;
+ struct bgp_pbr_config *bgp_pbr_cfg = bgp->bgp_pbr_cfg;
+ struct bgp_pbr_interface_head *head;
+ bool *bgp_pbr_interface_any;
+
+ if (!bgp_pbr_cfg)
+ return CMD_SUCCESS;
+ if (afi == AFI_IP) {
+ head = &(bgp_pbr_cfg->ifaces_by_name_ipv4);
+ bgp_pbr_interface_any = &(bgp_pbr_cfg->pbr_interface_any_ipv4);
+ } else {
+ head = &(bgp_pbr_cfg->ifaces_by_name_ipv6);
+ bgp_pbr_interface_any = &(bgp_pbr_cfg->pbr_interface_any_ipv6);
+ }
+ if (no) {
+ if (!ifname) {
+ if (*bgp_pbr_interface_any) {
+ *bgp_pbr_interface_any = false;
+ /* remove all other interface list */
+ bgp_pbr_reset(bgp, afi);
+ }
+ return CMD_SUCCESS;
+ }
+ pbr_if = bgp_pbr_interface_lookup(ifname, head);
+ if (!pbr_if)
+ return CMD_SUCCESS;
+ RB_REMOVE(bgp_pbr_interface_head, head, pbr_if);
+ return CMD_SUCCESS;
+ }
+ if (ifname) {
+ pbr_if = bgp_pbr_interface_lookup(ifname, head);
+ if (pbr_if)
+ return CMD_SUCCESS;
+ pbr_if = XCALLOC(MTYPE_TMP,
+ sizeof(struct bgp_pbr_interface));
+ strlcpy(pbr_if->name, ifname, INTERFACE_NAMSIZ);
+ RB_INSERT(bgp_pbr_interface_head, head, pbr_if);
+ *bgp_pbr_interface_any = false;
+ } else {
+ /* set to default */
+ if (!*bgp_pbr_interface_any) {
+ /* remove all other interface list
+ */
+ bgp_pbr_reset(bgp, afi);
+ *bgp_pbr_interface_any = true;
+ }
+ }
+ return CMD_SUCCESS;
+}
+
+DEFUN (bgp_fs_local_install_ifname,
+ bgp_fs_local_install_ifname_cmd,
+ "[no] local-install INTERFACE",
+ NO_STR
+ "Apply local policy routing\n"
+ "Interface name\n")
+{
+ struct bgp *bgp = VTY_GET_CONTEXT(bgp);
+ int idx = 0;
+ const char *no = strmatch(argv[0]->text, "no") ? "no" : NULL;
+ char *ifname = argv_find(argv, argc, "INTERFACE", &idx) ?
+ argv[idx]->arg : NULL;
+
+ return bgp_fs_local_install_interface(bgp, no, ifname,
+ bgp_node_afi(vty));
+}
+
+extern int bgp_flowspec_display_match_per_ip(afi_t afi, struct bgp_table *rib,
+ struct prefix *match,
+ int prefix_check, struct vty *vty,
+ bool use_json,
+ json_object *json_paths)
+{
+ struct bgp_dest *dest;
+ const struct prefix *prefix;
+ int display = 0;
+
+ for (dest = bgp_table_top(rib); dest; dest = bgp_route_next(dest)) {
+ prefix = bgp_dest_get_prefix(dest);
+
+ if (prefix->family != AF_FLOWSPEC)
+ continue;
+
+ if (bgp_flowspec_contains_prefix(prefix, match, prefix_check)) {
+ route_vty_out_flowspec(
+ vty, prefix, bgp_dest_get_bgp_path_info(dest),
+ use_json ? NLRI_STRING_FORMAT_JSON
+ : NLRI_STRING_FORMAT_LARGE,
+ json_paths);
+ display++;
+ }
+ }
+ return display;
+}
+
+void bgp_flowspec_vty_init(void)
+{
+ install_element(ENABLE_NODE, &debug_bgp_flowspec_cmd);
+ install_element(CONFIG_NODE, &debug_bgp_flowspec_cmd);
+ install_element(ENABLE_NODE, &no_debug_bgp_flowspec_cmd);
+ install_element(CONFIG_NODE, &no_debug_bgp_flowspec_cmd);
+ install_element(BGP_FLOWSPECV4_NODE, &bgp_fs_local_install_ifname_cmd);
+ install_element(BGP_FLOWSPECV6_NODE, &bgp_fs_local_install_ifname_cmd);
+}
diff --git a/bgpd/bgp_fsm.c b/bgpd/bgp_fsm.c
new file mode 100644
index 0000000..82487cf
--- /dev/null
+++ b/bgpd/bgp_fsm.c
@@ -0,0 +1,3130 @@
+/* BGP-4 Finite State Machine
+ * From RFC1771 [A Border Gateway Protocol 4 (BGP-4)]
+ * Copyright (C) 1996, 97, 98 Kunihiro Ishiguro
+ *
+ * This file is part of GNU Zebra.
+ *
+ * GNU Zebra is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License as published by the
+ * Free Software Foundation; either version 2, or (at your option) any
+ * later version.
+ *
+ * GNU Zebra is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program; see the file COPYING; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
+ */
+
+#include <zebra.h>
+
+#include "linklist.h"
+#include "prefix.h"
+#include "sockunion.h"
+#include "thread.h"
+#include "log.h"
+#include "stream.h"
+#include "ringbuf.h"
+#include "memory.h"
+#include "plist.h"
+#include "workqueue.h"
+#include "queue.h"
+#include "filter.h"
+#include "command.h"
+#include "lib_errors.h"
+#include "zclient.h"
+#include "lib/json.h"
+#include "bgpd/bgpd.h"
+#include "bgpd/bgp_attr.h"
+#include "bgpd/bgp_debug.h"
+#include "bgpd/bgp_errors.h"
+#include "bgpd/bgp_fsm.h"
+#include "bgpd/bgp_packet.h"
+#include "bgpd/bgp_network.h"
+#include "bgpd/bgp_route.h"
+#include "bgpd/bgp_dump.h"
+#include "bgpd/bgp_open.h"
+#include "bgpd/bgp_advertise.h"
+#include "bgpd/bgp_community.h"
+#include "bgpd/bgp_updgrp.h"
+#include "bgpd/bgp_nht.h"
+#include "bgpd/bgp_bfd.h"
+#include "bgpd/bgp_memory.h"
+#include "bgpd/bgp_keepalives.h"
+#include "bgpd/bgp_io.h"
+#include "bgpd/bgp_zebra.h"
+#include "bgpd/bgp_vty.h"
+
+DEFINE_HOOK(peer_backward_transition, (struct peer * peer), (peer));
+DEFINE_HOOK(peer_status_changed, (struct peer * peer), (peer));
+
+/* Definition of display strings corresponding to FSM events. This should be
+ * kept consistent with the events defined in bgpd.h
+ */
+static const char *const bgp_event_str[] = {
+ NULL,
+ "BGP_Start",
+ "BGP_Stop",
+ "TCP_connection_open",
+ "TCP_connection_open_w_delay",
+ "TCP_connection_closed",
+ "TCP_connection_open_failed",
+ "TCP_fatal_error",
+ "ConnectRetry_timer_expired",
+ "Hold_Timer_expired",
+ "KeepAlive_timer_expired",
+ "DelayOpen_timer_expired",
+ "Receive_OPEN_message",
+ "Receive_KEEPALIVE_message",
+ "Receive_UPDATE_message",
+ "Receive_NOTIFICATION_message",
+ "Clearing_Completed",
+};
+
+/* BGP FSM (finite state machine) has three types of functions. Type
+ one is thread functions. Type two is event functions. Type three
+ is FSM functions. Timer functions are set by bgp_timer_set
+ function. */
+
+/* BGP event function. */
+void bgp_event(struct thread *);
+
+/* BGP thread functions. */
+static void bgp_start_timer(struct thread *);
+static void bgp_connect_timer(struct thread *);
+static void bgp_holdtime_timer(struct thread *);
+static void bgp_delayopen_timer(struct thread *);
+
+/* BGP FSM functions. */
+static int bgp_start(struct peer *);
+
+/* Register peer with NHT */
+int bgp_peer_reg_with_nht(struct peer *peer)
+{
+ int connected = 0;
+
+ if (peer->sort == BGP_PEER_EBGP && peer->ttl == BGP_DEFAULT_TTL
+ && !CHECK_FLAG(peer->flags, PEER_FLAG_DISABLE_CONNECTED_CHECK)
+ && !CHECK_FLAG(peer->bgp->flags, BGP_FLAG_DISABLE_NH_CONNECTED_CHK))
+ connected = 1;
+
+ return bgp_find_or_add_nexthop(
+ peer->bgp, peer->bgp, family2afi(peer->su.sa.sa_family),
+ SAFI_UNICAST, NULL, peer, connected, NULL);
+}
+
+static void peer_xfer_stats(struct peer *peer_dst, struct peer *peer_src)
+{
+ /* Copy stats over. These are only the pre-established state stats */
+ peer_dst->open_in += peer_src->open_in;
+ peer_dst->open_out += peer_src->open_out;
+ peer_dst->keepalive_in += peer_src->keepalive_in;
+ peer_dst->keepalive_out += peer_src->keepalive_out;
+ peer_dst->notify_in += peer_src->notify_in;
+ peer_dst->notify_out += peer_src->notify_out;
+ peer_dst->dynamic_cap_in += peer_src->dynamic_cap_in;
+ peer_dst->dynamic_cap_out += peer_src->dynamic_cap_out;
+}
+
+static struct peer *peer_xfer_conn(struct peer *from_peer)
+{
+ struct peer *peer;
+ afi_t afi;
+ safi_t safi;
+ int fd;
+ enum bgp_fsm_status status, pstatus;
+ enum bgp_fsm_events last_evt, last_maj_evt;
+
+ assert(from_peer != NULL);
+
+ peer = from_peer->doppelganger;
+
+ if (!peer || !CHECK_FLAG(peer->flags, PEER_FLAG_CONFIG_NODE))
+ return from_peer;
+
+ /*
+ * Let's check that we are not going to loose known configuration
+ * state based upon doppelganger rules.
+ */
+ FOREACH_AFI_SAFI (afi, safi) {
+ if (from_peer->afc[afi][safi] != peer->afc[afi][safi]) {
+ flog_err(
+ EC_BGP_DOPPELGANGER_CONFIG,
+ "from_peer->afc[%d][%d] is not the same as what we are overwriting",
+ afi, safi);
+ return NULL;
+ }
+ }
+
+ if (bgp_debug_neighbor_events(peer))
+ zlog_debug("%s: peer transfer %p fd %d -> %p fd %d)",
+ from_peer->host, from_peer, from_peer->fd, peer,
+ peer->fd);
+
+ bgp_writes_off(peer);
+ bgp_reads_off(peer);
+ bgp_writes_off(from_peer);
+ bgp_reads_off(from_peer);
+
+ /*
+ * Before exchanging FD remove doppelganger from
+ * keepalive peer hash. It could be possible conf peer
+ * fd is set to -1. If blocked on lock then keepalive
+ * thread can access peer pointer with fd -1.
+ */
+ bgp_keepalives_off(from_peer);
+
+ THREAD_OFF(peer->t_routeadv);
+ THREAD_OFF(peer->t_connect);
+ THREAD_OFF(peer->t_delayopen);
+ THREAD_OFF(peer->t_connect_check_r);
+ THREAD_OFF(peer->t_connect_check_w);
+ THREAD_OFF(from_peer->t_routeadv);
+ THREAD_OFF(from_peer->t_connect);
+ THREAD_OFF(from_peer->t_delayopen);
+ THREAD_OFF(from_peer->t_connect_check_r);
+ THREAD_OFF(from_peer->t_connect_check_w);
+ THREAD_OFF(from_peer->t_process_packet);
+
+ /*
+ * At this point in time, it is possible that there are packets pending
+ * on various buffers. Those need to be transferred or dropped,
+ * otherwise we'll get spurious failures during session establishment.
+ */
+ frr_with_mutex (&peer->io_mtx, &from_peer->io_mtx) {
+ fd = peer->fd;
+ peer->fd = from_peer->fd;
+ from_peer->fd = fd;
+
+ stream_fifo_clean(peer->ibuf);
+ stream_fifo_clean(peer->obuf);
+
+ /*
+ * this should never happen, since bgp_process_packet() is the
+ * only task that sets and unsets the current packet and it
+ * runs in our pthread.
+ */
+ if (peer->curr) {
+ flog_err(
+ EC_BGP_PKT_PROCESS,
+ "[%s] Dropping pending packet on connection transfer:",
+ peer->host);
+ /* there used to be a bgp_packet_dump call here, but
+ * that's extremely confusing since there's no way to
+ * identify the packet in MRT dumps or BMP as dropped
+ * due to connection transfer.
+ */
+ stream_free(peer->curr);
+ peer->curr = NULL;
+ }
+
+ // copy each packet from old peer's output queue to new peer
+ while (from_peer->obuf->head)
+ stream_fifo_push(peer->obuf,
+ stream_fifo_pop(from_peer->obuf));
+
+ // copy each packet from old peer's input queue to new peer
+ while (from_peer->ibuf->head)
+ stream_fifo_push(peer->ibuf,
+ stream_fifo_pop(from_peer->ibuf));
+
+ ringbuf_wipe(peer->ibuf_work);
+ ringbuf_copy(peer->ibuf_work, from_peer->ibuf_work,
+ ringbuf_remain(from_peer->ibuf_work));
+ }
+
+ peer->as = from_peer->as;
+ peer->v_holdtime = from_peer->v_holdtime;
+ peer->v_keepalive = from_peer->v_keepalive;
+ peer->v_routeadv = from_peer->v_routeadv;
+ peer->v_delayopen = from_peer->v_delayopen;
+ peer->v_gr_restart = from_peer->v_gr_restart;
+ peer->cap = from_peer->cap;
+ peer->remote_role = from_peer->remote_role;
+ status = peer->status;
+ pstatus = peer->ostatus;
+ last_evt = peer->last_event;
+ last_maj_evt = peer->last_major_event;
+ peer->status = from_peer->status;
+ peer->ostatus = from_peer->ostatus;
+ peer->last_event = from_peer->last_event;
+ peer->last_major_event = from_peer->last_major_event;
+ from_peer->status = status;
+ from_peer->ostatus = pstatus;
+ from_peer->last_event = last_evt;
+ from_peer->last_major_event = last_maj_evt;
+ peer->remote_id = from_peer->remote_id;
+ peer->last_reset = from_peer->last_reset;
+ peer->max_packet_size = from_peer->max_packet_size;
+
+ BGP_GR_ROUTER_DETECT_AND_SEND_CAPABILITY_TO_ZEBRA(peer->bgp,
+ peer->bgp->peer);
+
+ if (bgp_peer_gr_mode_get(peer) == PEER_DISABLE) {
+
+ UNSET_FLAG(peer->sflags, PEER_STATUS_NSF_MODE);
+
+ if (CHECK_FLAG(peer->sflags, PEER_STATUS_NSF_WAIT)) {
+ peer_nsf_stop(peer);
+ }
+ }
+
+ if (from_peer->hostname != NULL) {
+ if (peer->hostname) {
+ XFREE(MTYPE_BGP_PEER_HOST, peer->hostname);
+ peer->hostname = NULL;
+ }
+
+ peer->hostname = from_peer->hostname;
+ from_peer->hostname = NULL;
+ }
+
+ if (from_peer->domainname != NULL) {
+ if (peer->domainname) {
+ XFREE(MTYPE_BGP_PEER_HOST, peer->domainname);
+ peer->domainname = NULL;
+ }
+
+ peer->domainname = from_peer->domainname;
+ from_peer->domainname = NULL;
+ }
+
+ FOREACH_AFI_SAFI (afi, safi) {
+ peer->af_sflags[afi][safi] = from_peer->af_sflags[afi][safi];
+ peer->af_cap[afi][safi] = from_peer->af_cap[afi][safi];
+ peer->afc_nego[afi][safi] = from_peer->afc_nego[afi][safi];
+ peer->afc_adv[afi][safi] = from_peer->afc_adv[afi][safi];
+ peer->afc_recv[afi][safi] = from_peer->afc_recv[afi][safi];
+ peer->orf_plist[afi][safi] = from_peer->orf_plist[afi][safi];
+ peer->llgr[afi][safi] = from_peer->llgr[afi][safi];
+ }
+
+ if (bgp_getsockname(peer) < 0) {
+ flog_err(
+ EC_LIB_SOCKET,
+ "%%bgp_getsockname() failed for %s peer %s fd %d (from_peer fd %d)",
+ (CHECK_FLAG(peer->sflags, PEER_STATUS_ACCEPT_PEER)
+ ? "accept"
+ : ""),
+ peer->host, peer->fd, from_peer->fd);
+ BGP_EVENT_ADD(peer, BGP_Stop);
+ BGP_EVENT_ADD(from_peer, BGP_Stop);
+ return NULL;
+ }
+ if (from_peer->status > Active) {
+ if (bgp_getsockname(from_peer) < 0) {
+ flog_err(
+ EC_LIB_SOCKET,
+ "%%bgp_getsockname() failed for %s from_peer %s fd %d (peer fd %d)",
+
+ (CHECK_FLAG(from_peer->sflags,
+ PEER_STATUS_ACCEPT_PEER)
+ ? "accept"
+ : ""),
+ from_peer->host, from_peer->fd, peer->fd);
+ bgp_stop(from_peer);
+ from_peer = NULL;
+ }
+ }
+
+
+ // Note: peer_xfer_stats() must be called with I/O turned OFF
+ if (from_peer)
+ peer_xfer_stats(peer, from_peer);
+
+ /* Register peer for NHT. This is to allow RAs to be enabled when
+ * needed, even on a passive connection.
+ */
+ bgp_peer_reg_with_nht(peer);
+ if (from_peer)
+ bgp_replace_nexthop_by_peer(from_peer, peer);
+
+ bgp_reads_on(peer);
+ bgp_writes_on(peer);
+ thread_add_event(bm->master, bgp_process_packet, peer, 0,
+ &peer->t_process_packet);
+
+ return (peer);
+}
+
+/* Hook function called after bgp event is occered. And vty's
+ neighbor command invoke this function after making neighbor
+ structure. */
+void bgp_timer_set(struct peer *peer)
+{
+ afi_t afi;
+ safi_t safi;
+
+ switch (peer->status) {
+ case Idle:
+ /* First entry point of peer's finite state machine. In Idle
+ status start timer is on unless peer is shutdown or peer is
+ inactive. All other timer must be turned off */
+ if (BGP_PEER_START_SUPPRESSED(peer) || !peer_active(peer)
+ || peer->bgp->vrf_id == VRF_UNKNOWN) {
+ THREAD_OFF(peer->t_start);
+ } else {
+ BGP_TIMER_ON(peer->t_start, bgp_start_timer,
+ peer->v_start);
+ }
+ THREAD_OFF(peer->t_connect);
+ THREAD_OFF(peer->t_holdtime);
+ bgp_keepalives_off(peer);
+ THREAD_OFF(peer->t_routeadv);
+ THREAD_OFF(peer->t_delayopen);
+ break;
+
+ case Connect:
+ /* After start timer is expired, the peer moves to Connect
+ status. Make sure start timer is off and connect timer is
+ on. */
+ THREAD_OFF(peer->t_start);
+ if (CHECK_FLAG(peer->flags, PEER_FLAG_TIMER_DELAYOPEN))
+ BGP_TIMER_ON(peer->t_connect, bgp_connect_timer,
+ (peer->v_delayopen + peer->v_connect));
+ else
+ BGP_TIMER_ON(peer->t_connect, bgp_connect_timer,
+ peer->v_connect);
+
+ THREAD_OFF(peer->t_holdtime);
+ bgp_keepalives_off(peer);
+ THREAD_OFF(peer->t_routeadv);
+ break;
+
+ case Active:
+ /* Active is waiting connection from remote peer. And if
+ connect timer is expired, change status to Connect. */
+ THREAD_OFF(peer->t_start);
+ /* If peer is passive mode, do not set connect timer. */
+ if (CHECK_FLAG(peer->flags, PEER_FLAG_PASSIVE)
+ || CHECK_FLAG(peer->sflags, PEER_STATUS_NSF_WAIT)) {
+ THREAD_OFF(peer->t_connect);
+ } else {
+ if (CHECK_FLAG(peer->flags, PEER_FLAG_TIMER_DELAYOPEN))
+ BGP_TIMER_ON(
+ peer->t_connect, bgp_connect_timer,
+ (peer->v_delayopen + peer->v_connect));
+ else
+ BGP_TIMER_ON(peer->t_connect, bgp_connect_timer,
+ peer->v_connect);
+ }
+ THREAD_OFF(peer->t_holdtime);
+ bgp_keepalives_off(peer);
+ THREAD_OFF(peer->t_routeadv);
+ break;
+
+ case OpenSent:
+ /* OpenSent status. */
+ THREAD_OFF(peer->t_start);
+ THREAD_OFF(peer->t_connect);
+ if (peer->v_holdtime != 0) {
+ BGP_TIMER_ON(peer->t_holdtime, bgp_holdtime_timer,
+ peer->v_holdtime);
+ } else {
+ THREAD_OFF(peer->t_holdtime);
+ }
+ bgp_keepalives_off(peer);
+ THREAD_OFF(peer->t_routeadv);
+ THREAD_OFF(peer->t_delayopen);
+ break;
+
+ case OpenConfirm:
+ /* OpenConfirm status. */
+ THREAD_OFF(peer->t_start);
+ THREAD_OFF(peer->t_connect);
+
+ /* If the negotiated Hold Time value is zero, then the Hold Time
+ timer and KeepAlive timers are not started. */
+ if (peer->v_holdtime == 0) {
+ THREAD_OFF(peer->t_holdtime);
+ bgp_keepalives_off(peer);
+ } else {
+ BGP_TIMER_ON(peer->t_holdtime, bgp_holdtime_timer,
+ peer->v_holdtime);
+ bgp_keepalives_on(peer);
+ }
+ THREAD_OFF(peer->t_routeadv);
+ THREAD_OFF(peer->t_delayopen);
+ break;
+
+ case Established:
+ /* In Established status start and connect timer is turned
+ off. */
+ THREAD_OFF(peer->t_start);
+ THREAD_OFF(peer->t_connect);
+ THREAD_OFF(peer->t_delayopen);
+
+ /* Same as OpenConfirm, if holdtime is zero then both holdtime
+ and keepalive must be turned off. */
+ if (peer->v_holdtime == 0) {
+ THREAD_OFF(peer->t_holdtime);
+ bgp_keepalives_off(peer);
+ } else {
+ BGP_TIMER_ON(peer->t_holdtime, bgp_holdtime_timer,
+ peer->v_holdtime);
+ bgp_keepalives_on(peer);
+ }
+ break;
+ case Deleted:
+ THREAD_OFF(peer->t_gr_restart);
+ THREAD_OFF(peer->t_gr_stale);
+
+ FOREACH_AFI_SAFI (afi, safi)
+ THREAD_OFF(peer->t_llgr_stale[afi][safi]);
+
+ THREAD_OFF(peer->t_pmax_restart);
+ THREAD_OFF(peer->t_refresh_stalepath);
+ /* fallthru */
+ case Clearing:
+ THREAD_OFF(peer->t_start);
+ THREAD_OFF(peer->t_connect);
+ THREAD_OFF(peer->t_holdtime);
+ bgp_keepalives_off(peer);
+ THREAD_OFF(peer->t_routeadv);
+ THREAD_OFF(peer->t_delayopen);
+ break;
+ case BGP_STATUS_MAX:
+ flog_err(EC_LIB_DEVELOPMENT,
+ "BGP_STATUS_MAX while a legal state is not valid state for the FSM");
+ break;
+ }
+}
+
+/* BGP start timer. This function set BGP_Start event to thread value
+ and process event. */
+static void bgp_start_timer(struct thread *thread)
+{
+ struct peer *peer;
+
+ peer = THREAD_ARG(thread);
+
+ if (bgp_debug_neighbor_events(peer))
+ zlog_debug("%s [FSM] Timer (start timer expire).", peer->host);
+
+ THREAD_VAL(thread) = BGP_Start;
+ bgp_event(thread); /* bgp_event unlocks peer */
+}
+
+/* BGP connect retry timer. */
+static void bgp_connect_timer(struct thread *thread)
+{
+ struct peer *peer;
+
+ peer = THREAD_ARG(thread);
+
+ /* stop the DelayOpenTimer if it is running */
+ THREAD_OFF(peer->t_delayopen);
+
+ assert(!peer->t_write);
+ assert(!peer->t_read);
+
+ if (bgp_debug_neighbor_events(peer))
+ zlog_debug("%s [FSM] Timer (connect timer expire)", peer->host);
+
+ if (CHECK_FLAG(peer->sflags, PEER_STATUS_ACCEPT_PEER))
+ bgp_stop(peer);
+ else {
+ THREAD_VAL(thread) = ConnectRetry_timer_expired;
+ bgp_event(thread); /* bgp_event unlocks peer */
+ }
+}
+
+/* BGP holdtime timer. */
+static void bgp_holdtime_timer(struct thread *thread)
+{
+ atomic_size_t inq_count;
+ struct peer *peer;
+
+ peer = THREAD_ARG(thread);
+
+ if (bgp_debug_neighbor_events(peer))
+ zlog_debug("%s [FSM] Timer (holdtime timer expire)",
+ peer->host);
+
+ /*
+ * Given that we do not have any expectation of ordering
+ * for handling packets from a peer -vs- handling
+ * the hold timer for a peer as that they are both
+ * events on the peer. If we have incoming
+ * data on the peers inq, let's give the system a chance
+ * to handle that data. This can be especially true
+ * for systems where we are heavily loaded for one
+ * reason or another.
+ */
+ inq_count = atomic_load_explicit(&peer->ibuf->count,
+ memory_order_relaxed);
+ if (inq_count)
+ BGP_TIMER_ON(peer->t_holdtime, bgp_holdtime_timer,
+ peer->v_holdtime);
+
+ THREAD_VAL(thread) = Hold_Timer_expired;
+ bgp_event(thread); /* bgp_event unlocks peer */
+}
+
+void bgp_routeadv_timer(struct thread *thread)
+{
+ struct peer *peer;
+
+ peer = THREAD_ARG(thread);
+
+ if (bgp_debug_neighbor_events(peer))
+ zlog_debug("%s [FSM] Timer (routeadv timer expire)",
+ peer->host);
+
+ peer->synctime = monotime(NULL);
+
+ thread_add_timer_msec(bm->master, bgp_generate_updgrp_packets, peer, 0,
+ &peer->t_generate_updgrp_packets);
+
+ /* MRAI timer will be started again when FIFO is built, no need to
+ * do it here.
+ */
+}
+
+/* RFC 4271 DelayOpenTimer */
+void bgp_delayopen_timer(struct thread *thread)
+{
+ struct peer *peer;
+
+ peer = THREAD_ARG(thread);
+
+ if (bgp_debug_neighbor_events(peer))
+ zlog_debug("%s [FSM] Timer (DelayOpentimer expire)",
+ peer->host);
+
+ THREAD_VAL(thread) = DelayOpen_timer_expired;
+ bgp_event(thread); /* bgp_event unlocks peer */
+}
+
+/* BGP Peer Down Cause */
+const char *const peer_down_str[] = {"",
+ "Router ID changed",
+ "Remote AS changed",
+ "Local AS change",
+ "Cluster ID changed",
+ "Confederation identifier changed",
+ "Confederation peer changed",
+ "RR client config change",
+ "RS client config change",
+ "Update source change",
+ "Address family activated",
+ "Admin. shutdown",
+ "User reset",
+ "BGP Notification received",
+ "BGP Notification send",
+ "Peer closed the session",
+ "Neighbor deleted",
+ "Peer-group add member",
+ "Peer-group delete member",
+ "Capability changed",
+ "Passive config change",
+ "Multihop config change",
+ "NSF peer closed the session",
+ "Intf peering v6only config change",
+ "BFD down received",
+ "Interface down",
+ "Neighbor address lost",
+ "Waiting for NHT",
+ "Waiting for Peer IPv6 LLA",
+ "Waiting for VRF to be initialized",
+ "No AFI/SAFI activated for peer",
+ "AS Set config change",
+ "Waiting for peer OPEN",
+ "Reached received prefix count",
+ "Socket Error"};
+
+static void bgp_graceful_restart_timer_off(struct peer *peer)
+{
+ afi_t afi;
+ safi_t safi;
+
+ FOREACH_AFI_SAFI (afi, safi)
+ if (CHECK_FLAG(peer->af_sflags[afi][safi],
+ PEER_STATUS_LLGR_WAIT))
+ return;
+
+ UNSET_FLAG(peer->sflags, PEER_STATUS_NSF_WAIT);
+ THREAD_OFF(peer->t_gr_stale);
+
+ if (peer_dynamic_neighbor(peer) &&
+ !(CHECK_FLAG(peer->flags, PEER_FLAG_DELETE))) {
+ if (bgp_debug_neighbor_events(peer))
+ zlog_debug("%s (dynamic neighbor) deleted (%s)",
+ peer->host, __func__);
+ peer_delete(peer);
+ }
+
+ bgp_timer_set(peer);
+}
+
+static void bgp_llgr_stale_timer_expire(struct thread *thread)
+{
+ struct peer_af *paf;
+ struct peer *peer;
+ afi_t afi;
+ safi_t safi;
+
+ paf = THREAD_ARG(thread);
+
+ peer = paf->peer;
+ afi = paf->afi;
+ safi = paf->safi;
+
+ /* If the timer for the "Long-lived Stale Time" expires before the
+ * session is re-established, the helper MUST delete all the
+ * stale routes from the neighbor that it is retaining.
+ */
+ if (bgp_debug_neighbor_events(peer))
+ zlog_debug("%pBP Long-lived stale timer (%s) expired", peer,
+ get_afi_safi_str(afi, safi, false));
+
+ UNSET_FLAG(peer->af_sflags[afi][safi], PEER_STATUS_LLGR_WAIT);
+
+ bgp_clear_stale_route(peer, afi, safi);
+
+ bgp_graceful_restart_timer_off(peer);
+}
+
+static void bgp_set_llgr_stale(struct peer *peer, afi_t afi, safi_t safi)
+{
+ struct bgp_dest *dest;
+ struct bgp_path_info *pi;
+ struct bgp_table *table;
+ struct attr attr;
+
+ 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;
+
+ 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 (bgp_attr_get_community(pi->attr) &&
+ community_include(
+ bgp_attr_get_community(
+ pi->attr),
+ COMMUNITY_NO_LLGR))
+ continue;
+
+ if (bgp_debug_neighbor_events(peer))
+ zlog_debug(
+ "%pBP Long-lived set stale community (LLGR_STALE) for: %pFX",
+ peer, &dest->p);
+
+ attr = *pi->attr;
+ bgp_attr_add_llgr_community(&attr);
+ pi->attr = bgp_attr_intern(&attr);
+ bgp_recalculate_afi_safi_bestpaths(
+ peer->bgp, 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 (bgp_attr_get_community(pi->attr) &&
+ community_include(
+ bgp_attr_get_community(pi->attr),
+ COMMUNITY_NO_LLGR))
+ continue;
+
+ if (bgp_debug_neighbor_events(peer))
+ zlog_debug(
+ "%pBP Long-lived set stale community (LLGR_STALE) for: %pFX",
+ peer, &dest->p);
+
+ attr = *pi->attr;
+ bgp_attr_add_llgr_community(&attr);
+ pi->attr = bgp_attr_intern(&attr);
+ bgp_recalculate_afi_safi_bestpaths(peer->bgp,
+ afi, safi);
+
+ break;
+ }
+ }
+}
+
+static void bgp_graceful_restart_timer_expire(struct thread *thread)
+{
+ struct peer *peer, *tmp_peer;
+ struct listnode *node, *nnode;
+ struct peer_af *paf;
+ afi_t afi;
+ safi_t safi;
+
+ peer = THREAD_ARG(thread);
+
+ if (bgp_debug_neighbor_events(peer)) {
+ zlog_debug("%pBP graceful restart timer expired", peer);
+ zlog_debug("%pBP graceful restart stalepath timer stopped",
+ peer);
+ }
+
+ FOREACH_AFI_SAFI (afi, safi) {
+ if (!peer->nsf[afi][safi])
+ continue;
+
+ /* Once the "Restart Time" period ends, the LLGR period is
+ * said to have begun and the following procedures MUST be
+ * performed:
+ *
+ * The helper router MUST start a timer for the
+ * "Long-lived Stale Time".
+ *
+ * The helper router MUST attach the LLGR_STALE community
+ * for the stale routes being retained. Note that this
+ * requirement implies that the routes would need to be
+ * readvertised, to disseminate the modified community.
+ */
+ if (peer->llgr[afi][safi].stale_time) {
+ paf = peer_af_find(peer, afi, safi);
+ if (!paf)
+ continue;
+
+ if (bgp_debug_neighbor_events(peer))
+ zlog_debug(
+ "%pBP Long-lived stale timer (%s) started for %d sec",
+ peer,
+ get_afi_safi_str(afi, safi, false),
+ peer->llgr[afi][safi].stale_time);
+
+ SET_FLAG(peer->af_sflags[afi][safi],
+ PEER_STATUS_LLGR_WAIT);
+
+ bgp_set_llgr_stale(peer, afi, safi);
+ bgp_clear_stale_route(peer, afi, safi);
+
+ thread_add_timer(bm->master,
+ bgp_llgr_stale_timer_expire, paf,
+ peer->llgr[afi][safi].stale_time,
+ &peer->t_llgr_stale[afi][safi]);
+
+ for (ALL_LIST_ELEMENTS(peer->bgp->peer, node, nnode,
+ tmp_peer))
+ bgp_announce_route(tmp_peer, afi, safi, false);
+ } else {
+ bgp_clear_stale_route(peer, afi, safi);
+ }
+ }
+
+ bgp_graceful_restart_timer_off(peer);
+}
+
+static void bgp_graceful_stale_timer_expire(struct thread *thread)
+{
+ struct peer *peer;
+ afi_t afi;
+ safi_t safi;
+
+ peer = THREAD_ARG(thread);
+
+ if (bgp_debug_neighbor_events(peer))
+ zlog_debug("%pBP graceful restart stalepath timer expired",
+ peer);
+
+ /* NSF delete stale route */
+ FOREACH_AFI_SAFI_NSF (afi, safi)
+ if (peer->nsf[afi][safi])
+ bgp_clear_stale_route(peer, afi, safi);
+}
+
+/* Selection deferral timer processing function */
+static void bgp_graceful_deferral_timer_expire(struct thread *thread)
+{
+ struct afi_safi_info *info;
+ afi_t afi;
+ safi_t safi;
+ struct bgp *bgp;
+
+ info = THREAD_ARG(thread);
+ afi = info->afi;
+ safi = info->safi;
+ bgp = info->bgp;
+
+ if (BGP_DEBUG(update, UPDATE_OUT))
+ zlog_debug(
+ "afi %d, safi %d : graceful restart deferral timer expired",
+ afi, safi);
+
+ bgp->gr_info[afi][safi].eor_required = 0;
+ bgp->gr_info[afi][safi].eor_received = 0;
+ XFREE(MTYPE_TMP, info);
+
+ /* Best path selection */
+ bgp_best_path_select_defer(bgp, afi, safi);
+}
+
+static bool bgp_update_delay_applicable(struct bgp *bgp)
+{
+ /* update_delay_over flag should be reset (set to 0) for any new
+ applicability of the update-delay during BGP process lifetime.
+ And it should be set after an occurence of the update-delay is
+ over)*/
+ if (!bgp->update_delay_over)
+ return true;
+ return false;
+}
+
+bool bgp_update_delay_active(struct bgp *bgp)
+{
+ if (bgp->t_update_delay)
+ return true;
+ return false;
+}
+
+bool bgp_update_delay_configured(struct bgp *bgp)
+{
+ if (bgp->v_update_delay)
+ return true;
+ return false;
+}
+
+/* Do the post-processing needed when bgp comes out of the read-only mode
+ on ending the update delay. */
+void bgp_update_delay_end(struct bgp *bgp)
+{
+ THREAD_OFF(bgp->t_update_delay);
+ THREAD_OFF(bgp->t_establish_wait);
+
+ /* Reset update-delay related state */
+ bgp->update_delay_over = 1;
+ bgp->established = 0;
+ bgp->restarted_peers = 0;
+ bgp->implicit_eors = 0;
+ bgp->explicit_eors = 0;
+
+ frr_timestamp(3, bgp->update_delay_end_time,
+ sizeof(bgp->update_delay_end_time));
+
+ /*
+ * Add an end-of-initial-update marker to the main process queues so
+ * that
+ * the route advertisement timer for the peers can be started. Also set
+ * the zebra and peer update hold flags. These flags are used to achieve
+ * three stages in the update-delay post processing:
+ * 1. Finish best-path selection for all the prefixes held on the
+ * queues.
+ * (routes in BGP are updated, and peers sync queues are populated
+ * too)
+ * 2. As the eoiu mark is reached in the bgp process routine, ship all
+ * the
+ * routes to zebra. With that zebra should see updates from BGP
+ * close
+ * to each other.
+ * 3. Unblock the peer update writes. With that peer update packing
+ * with
+ * the prefixes should be at its maximum.
+ */
+ bgp_add_eoiu_mark(bgp);
+ bgp->main_zebra_update_hold = 1;
+ bgp->main_peers_update_hold = 1;
+
+ /*
+ * Resume the queue processing. This should trigger the event that would
+ * take care of processing any work that was queued during the read-only
+ * mode.
+ */
+ work_queue_unplug(bgp->process_queue);
+}
+
+/**
+ * see bgp_fsm.h
+ */
+void bgp_start_routeadv(struct bgp *bgp)
+{
+ struct listnode *node, *nnode;
+ struct peer *peer;
+
+ zlog_info("%s, update hold status %d", __func__,
+ bgp->main_peers_update_hold);
+
+ if (bgp->main_peers_update_hold)
+ return;
+
+ frr_timestamp(3, bgp->update_delay_peers_resume_time,
+ sizeof(bgp->update_delay_peers_resume_time));
+
+ for (ALL_LIST_ELEMENTS(bgp->peer, node, nnode, peer)) {
+ if (!peer_established(peer))
+ continue;
+ THREAD_OFF(peer->t_routeadv);
+ BGP_TIMER_ON(peer->t_routeadv, bgp_routeadv_timer, 0);
+ }
+}
+
+/**
+ * see bgp_fsm.h
+ */
+void bgp_adjust_routeadv(struct peer *peer)
+{
+ time_t nowtime = monotime(NULL);
+ double diff;
+ unsigned long remain;
+
+ /* Bypass checks for special case of MRAI being 0 */
+ if (peer->v_routeadv == 0) {
+ /* Stop existing timer, just in case it is running for a
+ * different
+ * duration and schedule write thread immediately.
+ */
+ THREAD_OFF(peer->t_routeadv);
+
+ peer->synctime = monotime(NULL);
+ /* If suppress fib pending is enabled, route is advertised to
+ * peers when the status is received from the FIB. The delay
+ * is added to update group packet generate which will allow
+ * more routes to be sent in the update message
+ */
+ BGP_UPDATE_GROUP_TIMER_ON(&peer->t_generate_updgrp_packets,
+ bgp_generate_updgrp_packets);
+ return;
+ }
+
+
+ /*
+ * CASE I:
+ * If the last update was written more than MRAI back, expire the timer
+ * instantly so that we can send the update out sooner.
+ *
+ * <------- MRAI --------->
+ * |-----------------|-----------------------|
+ * <------------- m ------------>
+ * ^ ^ ^
+ * | | |
+ * | | current time
+ * | timer start
+ * last write
+ *
+ * m > MRAI
+ */
+ diff = difftime(nowtime, peer->last_update);
+ if (diff > (double)peer->v_routeadv) {
+ THREAD_OFF(peer->t_routeadv);
+ BGP_TIMER_ON(peer->t_routeadv, bgp_routeadv_timer, 0);
+ return;
+ }
+
+ /*
+ * CASE II:
+ * - Find when to expire the MRAI timer.
+ * If MRAI timer is not active, assume we can start it now.
+ *
+ * <------- MRAI --------->
+ * |------------|-----------------------|
+ * <-------- m ----------><----- r ----->
+ * ^ ^ ^
+ * | | |
+ * | | current time
+ * | timer start
+ * last write
+ *
+ * (MRAI - m) < r
+ */
+ if (peer->t_routeadv)
+ remain = thread_timer_remain_second(peer->t_routeadv);
+ else
+ remain = peer->v_routeadv;
+ diff = peer->v_routeadv - diff;
+ if (diff <= (double)remain) {
+ THREAD_OFF(peer->t_routeadv);
+ BGP_TIMER_ON(peer->t_routeadv, bgp_routeadv_timer, diff);
+ }
+}
+
+static bool bgp_maxmed_onstartup_applicable(struct bgp *bgp)
+{
+ if (!bgp->maxmed_onstartup_over)
+ return true;
+ return false;
+}
+
+bool bgp_maxmed_onstartup_configured(struct bgp *bgp)
+{
+ if (bgp->v_maxmed_onstartup != BGP_MAXMED_ONSTARTUP_UNCONFIGURED)
+ return true;
+ return false;
+}
+
+bool bgp_maxmed_onstartup_active(struct bgp *bgp)
+{
+ if (bgp->t_maxmed_onstartup)
+ return true;
+ return false;
+}
+
+void bgp_maxmed_update(struct bgp *bgp)
+{
+ uint8_t maxmed_active;
+ uint32_t maxmed_value;
+
+ if (bgp->v_maxmed_admin) {
+ maxmed_active = 1;
+ maxmed_value = bgp->maxmed_admin_value;
+ } else if (bgp->t_maxmed_onstartup) {
+ maxmed_active = 1;
+ maxmed_value = bgp->maxmed_onstartup_value;
+ } else {
+ maxmed_active = 0;
+ maxmed_value = BGP_MAXMED_VALUE_DEFAULT;
+ }
+
+ if (bgp->maxmed_active != maxmed_active
+ || bgp->maxmed_value != maxmed_value) {
+ bgp->maxmed_active = maxmed_active;
+ bgp->maxmed_value = maxmed_value;
+
+ update_group_announce(bgp);
+ }
+}
+
+int bgp_fsm_error_subcode(int status)
+{
+ int fsm_err_subcode = BGP_NOTIFY_FSM_ERR_SUBCODE_UNSPECIFIC;
+
+ switch (status) {
+ case OpenSent:
+ fsm_err_subcode = BGP_NOTIFY_FSM_ERR_SUBCODE_OPENSENT;
+ break;
+ case OpenConfirm:
+ fsm_err_subcode = BGP_NOTIFY_FSM_ERR_SUBCODE_OPENCONFIRM;
+ break;
+ case Established:
+ fsm_err_subcode = BGP_NOTIFY_FSM_ERR_SUBCODE_ESTABLISHED;
+ break;
+ default:
+ break;
+ }
+
+ return fsm_err_subcode;
+}
+
+/* The maxmed onstartup timer expiry callback. */
+static void bgp_maxmed_onstartup_timer(struct thread *thread)
+{
+ struct bgp *bgp;
+
+ zlog_info("Max med on startup ended - timer expired.");
+
+ bgp = THREAD_ARG(thread);
+ THREAD_OFF(bgp->t_maxmed_onstartup);
+ bgp->maxmed_onstartup_over = 1;
+
+ bgp_maxmed_update(bgp);
+}
+
+static void bgp_maxmed_onstartup_begin(struct bgp *bgp)
+{
+ /* Applicable only once in the process lifetime on the startup */
+ if (bgp->maxmed_onstartup_over)
+ return;
+
+ zlog_info("Begin maxmed onstartup mode - timer %d seconds",
+ bgp->v_maxmed_onstartup);
+
+ thread_add_timer(bm->master, bgp_maxmed_onstartup_timer, bgp,
+ bgp->v_maxmed_onstartup, &bgp->t_maxmed_onstartup);
+
+ if (!bgp->v_maxmed_admin) {
+ bgp->maxmed_active = 1;
+ bgp->maxmed_value = bgp->maxmed_onstartup_value;
+ }
+
+ /* Route announce to all peers should happen after this in
+ * bgp_establish() */
+}
+
+static void bgp_maxmed_onstartup_process_status_change(struct peer *peer)
+{
+ if (peer_established(peer) && !peer->bgp->established) {
+ bgp_maxmed_onstartup_begin(peer->bgp);
+ }
+}
+
+/* The update delay timer expiry callback. */
+static void bgp_update_delay_timer(struct thread *thread)
+{
+ struct bgp *bgp;
+
+ zlog_info("Update delay ended - timer expired.");
+
+ bgp = THREAD_ARG(thread);
+ THREAD_OFF(bgp->t_update_delay);
+ bgp_update_delay_end(bgp);
+}
+
+/* The establish wait timer expiry callback. */
+static void bgp_establish_wait_timer(struct thread *thread)
+{
+ struct bgp *bgp;
+
+ zlog_info("Establish wait - timer expired.");
+
+ bgp = THREAD_ARG(thread);
+ THREAD_OFF(bgp->t_establish_wait);
+ bgp_check_update_delay(bgp);
+}
+
+/* Steps to begin the update delay:
+ - initialize queues if needed
+ - stop the queue processing
+ - start the timer */
+static void bgp_update_delay_begin(struct bgp *bgp)
+{
+ struct listnode *node, *nnode;
+ struct peer *peer;
+
+ /* Stop the processing of queued work. Enqueue shall continue */
+ work_queue_plug(bgp->process_queue);
+
+ for (ALL_LIST_ELEMENTS(bgp->peer, node, nnode, peer))
+ peer->update_delay_over = 0;
+
+ /* Start the update-delay timer */
+ thread_add_timer(bm->master, bgp_update_delay_timer, bgp,
+ bgp->v_update_delay, &bgp->t_update_delay);
+
+ if (bgp->v_establish_wait != bgp->v_update_delay)
+ thread_add_timer(bm->master, bgp_establish_wait_timer, bgp,
+ bgp->v_establish_wait, &bgp->t_establish_wait);
+
+ frr_timestamp(3, bgp->update_delay_begin_time,
+ sizeof(bgp->update_delay_begin_time));
+}
+
+static void bgp_update_delay_process_status_change(struct peer *peer)
+{
+ if (peer_established(peer)) {
+ if (!peer->bgp->established++) {
+ bgp_update_delay_begin(peer->bgp);
+ zlog_info(
+ "Begin read-only mode - update-delay timer %d seconds",
+ peer->bgp->v_update_delay);
+ }
+ if (CHECK_FLAG(peer->cap, PEER_CAP_GRACEFUL_RESTART_R_BIT_RCV))
+ bgp_update_restarted_peers(peer);
+ }
+ if (peer->ostatus == Established
+ && bgp_update_delay_active(peer->bgp)) {
+ /* Adjust the update-delay state to account for this flap.
+ NOTE: Intentionally skipping adjusting implicit_eors or
+ explicit_eors
+ counters. Extra sanity check in bgp_check_update_delay()
+ should
+ be enough to take care of any additive discrepancy in bgp eor
+ counters */
+ peer->bgp->established--;
+ peer->update_delay_over = 0;
+ }
+}
+
+/* Called after event occurred, this function change status and reset
+ read/write and timer thread. */
+void bgp_fsm_change_status(struct peer *peer, int status)
+{
+ struct bgp *bgp;
+ uint32_t peer_count;
+
+ bgp = peer->bgp;
+ peer_count = bgp->established_peers;
+
+ if (status == Established)
+ bgp->established_peers++;
+ else if ((peer_established(peer)) && (status != Established))
+ bgp->established_peers--;
+
+ if (bgp_debug_neighbor_events(peer)) {
+ struct vrf *vrf = vrf_lookup_by_id(bgp->vrf_id);
+
+ zlog_debug("%s : vrf %s(%u), Status: %s established_peers %u", __func__,
+ vrf ? vrf->name : "Unknown", bgp->vrf_id,
+ lookup_msg(bgp_status_msg, status, NULL),
+ bgp->established_peers);
+ }
+
+ /* Set to router ID to the value provided by RIB if there are no peers
+ * in the established state and peer count did not change
+ */
+ if ((peer_count != bgp->established_peers) &&
+ (bgp->established_peers == 0))
+ bgp_router_id_zebra_bump(bgp->vrf_id, NULL);
+
+ /* Transition into Clearing or Deleted must /always/ clear all routes..
+ * (and must do so before actually changing into Deleted..
+ */
+ if (status >= Clearing) {
+ bgp_clear_route_all(peer);
+
+ /* If no route was queued for the clear-node processing,
+ * generate the
+ * completion event here. This is needed because if there are no
+ * routes
+ * to trigger the background clear-node thread, the event won't
+ * get
+ * generated and the peer would be stuck in Clearing. Note that
+ * this
+ * event is for the peer and helps the peer transition out of
+ * Clearing
+ * state; it should not be generated per (AFI,SAFI). The event
+ * is
+ * directly posted here without calling clear_node_complete() as
+ * we
+ * shouldn't do an extra unlock. This event will get processed
+ * after
+ * the state change that happens below, so peer will be in
+ * Clearing
+ * (or Deleted).
+ */
+ if (!work_queue_is_scheduled(peer->clear_node_queue))
+ BGP_EVENT_ADD(peer, Clearing_Completed);
+ }
+
+ /* Preserve old status and change into new status. */
+ peer->ostatus = peer->status;
+ peer->status = status;
+
+ /* Reset received keepalives counter on every FSM change */
+ peer->rtt_keepalive_rcv = 0;
+
+ /* Fire backward transition hook if that's the case */
+ if (peer->ostatus > peer->status)
+ hook_call(peer_backward_transition, peer);
+
+ /* Save event that caused status change. */
+ peer->last_major_event = peer->cur_event;
+
+ /* Operations after status change */
+ hook_call(peer_status_changed, peer);
+
+ if (status == Established)
+ UNSET_FLAG(peer->sflags, PEER_STATUS_ACCEPT_PEER);
+
+ /* If max-med processing is applicable, do the necessary. */
+ if (status == Established) {
+ if (bgp_maxmed_onstartup_configured(peer->bgp)
+ && bgp_maxmed_onstartup_applicable(peer->bgp))
+ bgp_maxmed_onstartup_process_status_change(peer);
+ else
+ peer->bgp->maxmed_onstartup_over = 1;
+ }
+
+ /* If update-delay processing is applicable, do the necessary. */
+ if (bgp_update_delay_configured(peer->bgp)
+ && bgp_update_delay_applicable(peer->bgp))
+ bgp_update_delay_process_status_change(peer);
+
+ if (bgp_debug_neighbor_events(peer))
+ zlog_debug("%s went from %s to %s", peer->host,
+ lookup_msg(bgp_status_msg, peer->ostatus, NULL),
+ lookup_msg(bgp_status_msg, peer->status, NULL));
+}
+
+/* Flush the event queue and ensure the peer is shut down */
+static int bgp_clearing_completed(struct peer *peer)
+{
+ int rc = bgp_stop(peer);
+
+ if (rc >= 0)
+ BGP_EVENT_FLUSH(peer);
+
+ return rc;
+}
+
+/* Administrative BGP peer stop event. */
+/* May be called multiple times for the same peer */
+int bgp_stop(struct peer *peer)
+{
+ afi_t afi;
+ safi_t safi;
+ char orf_name[BUFSIZ];
+ int ret = 0;
+ struct bgp *bgp = peer->bgp;
+ struct graceful_restart_info *gr_info = NULL;
+
+ peer->nsf_af_count = 0;
+
+ /* deregister peer */
+ if (peer->bfd_config
+ && peer->last_reset == PEER_DOWN_UPDATE_SOURCE_CHANGE)
+ bfd_sess_uninstall(peer->bfd_config->session);
+
+ if (peer_dynamic_neighbor_no_nsf(peer) &&
+ !(CHECK_FLAG(peer->flags, PEER_FLAG_DELETE))) {
+ if (bgp_debug_neighbor_events(peer))
+ zlog_debug("%s (dynamic neighbor) deleted (%s)",
+ peer->host, __func__);
+ peer_delete(peer);
+ return -1;
+ }
+
+ /* Can't do this in Clearing; events are used for state transitions */
+ if (peer->status != Clearing) {
+ /* Delete all existing events of the peer */
+ BGP_EVENT_FLUSH(peer);
+ }
+
+ /* Increment Dropped count. */
+ if (peer_established(peer)) {
+ peer->dropped++;
+
+ /* Notify BGP conditional advertisement process */
+ peer->advmap_table_change = true;
+
+ /* bgp log-neighbor-changes of neighbor Down */
+ if (CHECK_FLAG(peer->bgp->flags,
+ BGP_FLAG_LOG_NEIGHBOR_CHANGES)) {
+ struct vrf *vrf = vrf_lookup_by_id(peer->bgp->vrf_id);
+
+ zlog_info(
+ "%%ADJCHANGE: neighbor %pBP in vrf %s Down %s",
+ peer,
+ vrf ? ((vrf->vrf_id != VRF_DEFAULT)
+ ? vrf->name
+ : VRF_DEFAULT_NAME)
+ : "",
+ peer_down_str[(int)peer->last_reset]);
+ }
+
+ /* graceful restart */
+ if (peer->t_gr_stale) {
+ THREAD_OFF(peer->t_gr_stale);
+ if (bgp_debug_neighbor_events(peer))
+ zlog_debug(
+ "%pBP graceful restart stalepath timer stopped",
+ peer);
+ }
+ if (CHECK_FLAG(peer->sflags, PEER_STATUS_NSF_WAIT)) {
+ if (bgp_debug_neighbor_events(peer)) {
+ zlog_debug(
+ "%pBP graceful restart timer started for %d sec",
+ peer, peer->v_gr_restart);
+ zlog_debug(
+ "%pBP graceful restart stalepath timer started for %d sec",
+ peer, peer->bgp->stalepath_time);
+ }
+ BGP_TIMER_ON(peer->t_gr_restart,
+ bgp_graceful_restart_timer_expire,
+ peer->v_gr_restart);
+ BGP_TIMER_ON(peer->t_gr_stale,
+ bgp_graceful_stale_timer_expire,
+ peer->bgp->stalepath_time);
+ } else {
+ UNSET_FLAG(peer->sflags, PEER_STATUS_NSF_MODE);
+
+ FOREACH_AFI_SAFI_NSF (afi, safi)
+ peer->nsf[afi][safi] = 0;
+ }
+
+ /* Stop route-refresh stalepath timer */
+ if (peer->t_refresh_stalepath) {
+ THREAD_OFF(peer->t_refresh_stalepath);
+
+ if (bgp_debug_neighbor_events(peer))
+ zlog_debug(
+ "%pBP route-refresh restart stalepath timer stopped",
+ peer);
+ }
+
+ /* If peer reset before receiving EOR, decrement EOR count and
+ * cancel the selection deferral timer if there are no
+ * pending EOR messages to be received
+ */
+ if (BGP_PEER_GRACEFUL_RESTART_CAPABLE(peer)) {
+ FOREACH_AFI_SAFI (afi, safi) {
+ if (!peer->afc_nego[afi][safi]
+ || CHECK_FLAG(peer->af_sflags[afi][safi],
+ PEER_STATUS_EOR_RECEIVED))
+ continue;
+
+ gr_info = &bgp->gr_info[afi][safi];
+ if (!gr_info)
+ continue;
+
+ if (gr_info->eor_required)
+ gr_info->eor_required--;
+
+ if (BGP_DEBUG(update, UPDATE_OUT))
+ zlog_debug("peer %s, EOR_required %d",
+ peer->host,
+ gr_info->eor_required);
+
+ /* There is no pending EOR message */
+ if (gr_info->eor_required == 0) {
+ if (gr_info->t_select_deferral) {
+ void *info = THREAD_ARG(
+ gr_info->t_select_deferral);
+ XFREE(MTYPE_TMP, info);
+ }
+ THREAD_OFF(gr_info->t_select_deferral);
+ gr_info->eor_received = 0;
+ }
+ }
+ }
+
+ /* set last reset time */
+ peer->resettime = peer->uptime = monotime(NULL);
+
+ if (BGP_DEBUG(update_groups, UPDATE_GROUPS))
+ zlog_debug("%s remove from all update group",
+ peer->host);
+ update_group_remove_peer_afs(peer);
+
+ /* Reset peer synctime */
+ peer->synctime = 0;
+ }
+
+ /* stop keepalives */
+ bgp_keepalives_off(peer);
+
+ /* Stop read and write threads. */
+ bgp_writes_off(peer);
+ bgp_reads_off(peer);
+
+ THREAD_OFF(peer->t_connect_check_r);
+ THREAD_OFF(peer->t_connect_check_w);
+
+ /* Stop all timers. */
+ THREAD_OFF(peer->t_start);
+ THREAD_OFF(peer->t_connect);
+ THREAD_OFF(peer->t_holdtime);
+ THREAD_OFF(peer->t_routeadv);
+ THREAD_OFF(peer->t_delayopen);
+
+ /* Clear input and output buffer. */
+ frr_with_mutex (&peer->io_mtx) {
+ if (peer->ibuf)
+ stream_fifo_clean(peer->ibuf);
+ if (peer->obuf)
+ stream_fifo_clean(peer->obuf);
+
+ if (peer->ibuf_work)
+ ringbuf_wipe(peer->ibuf_work);
+ if (peer->obuf_work)
+ stream_reset(peer->obuf_work);
+
+ if (peer->curr) {
+ stream_free(peer->curr);
+ peer->curr = NULL;
+ }
+ }
+
+ /* Close of file descriptor. */
+ if (peer->fd >= 0) {
+ close(peer->fd);
+ peer->fd = -1;
+ }
+
+ /* Reset capabilities. */
+ peer->cap = 0;
+
+ /* Resetting neighbor role to the default value */
+ peer->remote_role = ROLE_UNDEFINED;
+
+ FOREACH_AFI_SAFI (afi, safi) {
+ /* Reset all negotiated variables */
+ peer->afc_nego[afi][safi] = 0;
+ peer->afc_adv[afi][safi] = 0;
+ peer->afc_recv[afi][safi] = 0;
+
+ /* peer address family capability flags*/
+ peer->af_cap[afi][safi] = 0;
+
+ /* peer address family status flags*/
+ peer->af_sflags[afi][safi] = 0;
+
+ /* Received ORF prefix-filter */
+ peer->orf_plist[afi][safi] = NULL;
+
+ if ((peer->status == OpenConfirm) || (peer_established(peer))) {
+ /* ORF received prefix-filter pnt */
+ snprintf(orf_name, sizeof(orf_name), "%s.%d.%d",
+ peer->host, afi, safi);
+ prefix_bgp_orf_remove_all(afi, orf_name);
+ }
+ }
+
+ /* Reset keepalive and holdtime */
+ if (CHECK_FLAG(peer->flags, PEER_FLAG_TIMER)) {
+ peer->v_keepalive = peer->keepalive;
+ peer->v_holdtime = peer->holdtime;
+ } else {
+ peer->v_keepalive = peer->bgp->default_keepalive;
+ peer->v_holdtime = peer->bgp->default_holdtime;
+ }
+
+ /* Reset DelayOpenTime */
+ if (CHECK_FLAG(peer->flags, PEER_FLAG_TIMER_DELAYOPEN))
+ peer->v_delayopen = peer->delayopen;
+ else
+ peer->v_delayopen = peer->bgp->default_delayopen;
+
+ peer->update_time = 0;
+
+ if (!CHECK_FLAG(peer->flags, PEER_FLAG_CONFIG_NODE)
+ && !(CHECK_FLAG(peer->flags, PEER_FLAG_DELETE))) {
+ peer_delete(peer);
+ ret = -1;
+ } else {
+ bgp_peer_conf_if_to_su_update(peer);
+ }
+ return ret;
+}
+
+/* BGP peer is stoped by the error. */
+static int bgp_stop_with_error(struct peer *peer)
+{
+ /* Double start timer. */
+ peer->v_start *= 2;
+
+ /* Overflow check. */
+ if (peer->v_start >= (60 * 2))
+ peer->v_start = (60 * 2);
+
+ if (peer_dynamic_neighbor_no_nsf(peer)) {
+ if (bgp_debug_neighbor_events(peer))
+ zlog_debug("%s (dynamic neighbor) deleted (%s)",
+ peer->host, __func__);
+ peer_delete(peer);
+ return -1;
+ }
+
+ return (bgp_stop(peer));
+}
+
+
+/* something went wrong, send notify and tear down */
+static int bgp_stop_with_notify(struct peer *peer, uint8_t code,
+ uint8_t sub_code)
+{
+ /* Send notify to remote peer */
+ bgp_notify_send(peer, code, sub_code);
+
+ if (peer_dynamic_neighbor_no_nsf(peer)) {
+ if (bgp_debug_neighbor_events(peer))
+ zlog_debug("%s (dynamic neighbor) deleted (%s)",
+ peer->host, __func__);
+ peer_delete(peer);
+ return -1;
+ }
+
+ /* Clear start timer value to default. */
+ peer->v_start = BGP_INIT_START_TIMER;
+
+ return (bgp_stop(peer));
+}
+
+/**
+ * Determines whether a TCP session has successfully established for a peer and
+ * events as appropriate.
+ *
+ * This function is called when setting up a new session. After connect() is
+ * called on the peer's socket (in bgp_start()), the fd is passed to poll()
+ * to wait for connection success or failure. When poll() returns, this
+ * function is called to evaluate the result.
+ *
+ * Due to differences in behavior of poll() on Linux and BSD - specifically,
+ * the value of .revents in the case of a closed connection - this function is
+ * scheduled both for a read and a write event. The write event is triggered
+ * when the connection is established. A read event is triggered when the
+ * connection is closed. Thus we need to cancel whichever one did not occur.
+ */
+static void bgp_connect_check(struct thread *thread)
+{
+ int status;
+ socklen_t slen;
+ int ret;
+ struct peer *peer;
+
+ peer = THREAD_ARG(thread);
+ assert(!CHECK_FLAG(peer->thread_flags, PEER_THREAD_READS_ON));
+ assert(!CHECK_FLAG(peer->thread_flags, PEER_THREAD_WRITES_ON));
+ assert(!peer->t_read);
+ assert(!peer->t_write);
+
+ THREAD_OFF(peer->t_connect_check_r);
+ THREAD_OFF(peer->t_connect_check_w);
+
+ /* Check file descriptor. */
+ slen = sizeof(status);
+ ret = getsockopt(peer->fd, SOL_SOCKET, SO_ERROR, (void *)&status,
+ &slen);
+
+ /* If getsockopt is fail, this is fatal error. */
+ if (ret < 0) {
+ zlog_err("can't get sockopt for nonblocking connect: %d(%s)",
+ errno, safe_strerror(errno));
+ BGP_EVENT_ADD(peer, TCP_fatal_error);
+ return;
+ }
+
+ /* When status is 0 then TCP connection is established. */
+ if (status == 0) {
+ if (CHECK_FLAG(peer->flags, PEER_FLAG_TIMER_DELAYOPEN))
+ BGP_EVENT_ADD(peer, TCP_connection_open_w_delay);
+ else
+ BGP_EVENT_ADD(peer, TCP_connection_open);
+ return;
+ } else {
+ if (bgp_debug_neighbor_events(peer))
+ zlog_debug("%s [Event] Connect failed %d(%s)",
+ peer->host, status, safe_strerror(status));
+ BGP_EVENT_ADD(peer, TCP_connection_open_failed);
+ return;
+ }
+}
+
+/* TCP connection open. Next we send open message to remote peer. And
+ add read thread for reading open message. */
+static int bgp_connect_success(struct peer *peer)
+{
+ if (peer->fd < 0) {
+ flog_err(EC_BGP_CONNECT, "%s peer's fd is negative value %d",
+ __func__, peer->fd);
+ bgp_stop(peer);
+ return -1;
+ }
+
+ if (bgp_getsockname(peer) < 0) {
+ flog_err_sys(EC_LIB_SOCKET,
+ "%s: bgp_getsockname(): failed for peer %s, fd %d",
+ __func__, peer->host, peer->fd);
+ bgp_notify_send(peer, BGP_NOTIFY_FSM_ERR,
+ bgp_fsm_error_subcode(peer->status));
+ bgp_writes_on(peer);
+ return -1;
+ }
+
+ /*
+ * If we are doing nht for a peer that ls v6 LL based
+ * massage the event system to make things happy
+ */
+ bgp_nht_interface_events(peer);
+
+ bgp_reads_on(peer);
+
+ if (bgp_debug_neighbor_events(peer)) {
+ if (!CHECK_FLAG(peer->sflags, PEER_STATUS_ACCEPT_PEER))
+ zlog_debug("%s open active, local address %pSU",
+ peer->host, peer->su_local);
+ else
+ zlog_debug("%s passive open", peer->host);
+ }
+
+ /* Send an open message */
+ bgp_open_send(peer);
+
+ return 0;
+}
+
+/* TCP connection open with RFC 4271 optional session attribute DelayOpen flag
+ * set.
+ */
+static int bgp_connect_success_w_delayopen(struct peer *peer)
+{
+ if (peer->fd < 0) {
+ flog_err(EC_BGP_CONNECT, "%s: peer's fd is negative value %d",
+ __func__, peer->fd);
+ bgp_stop(peer);
+ return -1;
+ }
+
+ if (bgp_getsockname(peer) < 0) {
+ flog_err_sys(EC_LIB_SOCKET,
+ "%s: bgp_getsockname(): failed for peer %s, fd %d",
+ __func__, peer->host, peer->fd);
+ bgp_notify_send(peer, BGP_NOTIFY_FSM_ERR,
+ bgp_fsm_error_subcode(peer->status));
+ bgp_writes_on(peer);
+ return -1;
+ }
+
+ /*
+ * If we are doing nht for a peer that ls v6 LL based
+ * massage the event system to make things happy
+ */
+ bgp_nht_interface_events(peer);
+
+ bgp_reads_on(peer);
+
+ if (bgp_debug_neighbor_events(peer)) {
+ if (!CHECK_FLAG(peer->sflags, PEER_STATUS_ACCEPT_PEER))
+ zlog_debug("%s open active, local address %pSU",
+ peer->host, peer->su_local);
+ else
+ zlog_debug("%s passive open", peer->host);
+ }
+
+ /* set the DelayOpenTime to the inital value */
+ peer->v_delayopen = peer->delayopen;
+
+ /* Start the DelayOpenTimer if it is not already running */
+ if (!peer->t_delayopen)
+ BGP_TIMER_ON(peer->t_delayopen, bgp_delayopen_timer,
+ peer->v_delayopen);
+
+ if (bgp_debug_neighbor_events(peer))
+ zlog_debug("%s [FSM] BGP OPEN message delayed for %d seconds",
+ peer->host, peer->delayopen);
+
+ return 0;
+}
+
+/* TCP connect fail */
+static int bgp_connect_fail(struct peer *peer)
+{
+ if (peer_dynamic_neighbor_no_nsf(peer)) {
+ if (bgp_debug_neighbor_events(peer))
+ zlog_debug("%s (dynamic neighbor) deleted (%s)",
+ peer->host, __func__);
+ peer_delete(peer);
+ return -1;
+ }
+
+ /*
+ * If we are doing nht for a peer that ls v6 LL based
+ * massage the event system to make things happy
+ */
+ bgp_nht_interface_events(peer);
+
+ return (bgp_stop(peer));
+}
+
+/* This function is the first starting point of all BGP connection. It
+ * try to connect to remote peer with non-blocking IO.
+ */
+int bgp_start(struct peer *peer)
+{
+ int status;
+
+ bgp_peer_conf_if_to_su_update(peer);
+
+ if (peer->su.sa.sa_family == AF_UNSPEC) {
+ if (bgp_debug_neighbor_events(peer))
+ zlog_debug(
+ "%s [FSM] Unable to get neighbor's IP address, waiting...",
+ peer->host);
+ peer->last_reset = PEER_DOWN_NBR_ADDR;
+ return -1;
+ }
+
+ if (BGP_PEER_START_SUPPRESSED(peer)) {
+ if (bgp_debug_neighbor_events(peer))
+ flog_err(EC_BGP_FSM,
+ "%s [FSM] Trying to start suppressed peer - this is never supposed to happen!",
+ peer->host);
+ if (CHECK_FLAG(peer->flags, PEER_FLAG_SHUTDOWN))
+ peer->last_reset = PEER_DOWN_USER_SHUTDOWN;
+ else if (CHECK_FLAG(peer->bgp->flags, BGP_FLAG_SHUTDOWN))
+ peer->last_reset = PEER_DOWN_USER_SHUTDOWN;
+ else if (CHECK_FLAG(peer->sflags, PEER_STATUS_PREFIX_OVERFLOW))
+ peer->last_reset = PEER_DOWN_PFX_COUNT;
+ return -1;
+ }
+
+ /* Scrub some information that might be left over from a previous,
+ * session
+ */
+ /* Connection information. */
+ if (peer->su_local) {
+ sockunion_free(peer->su_local);
+ peer->su_local = NULL;
+ }
+
+ if (peer->su_remote) {
+ sockunion_free(peer->su_remote);
+ peer->su_remote = NULL;
+ }
+
+ /* Clear remote router-id. */
+ peer->remote_id.s_addr = INADDR_ANY;
+
+ /* Clear peer capability flag. */
+ peer->cap = 0;
+
+ /* If the peer is passive mode, force to move to Active mode. */
+ if (CHECK_FLAG(peer->flags, PEER_FLAG_PASSIVE)) {
+ BGP_EVENT_ADD(peer, TCP_connection_open_failed);
+ return 0;
+ }
+
+ if (peer->bgp->vrf_id == VRF_UNKNOWN) {
+ if (bgp_debug_neighbor_events(peer))
+ flog_err(
+ EC_BGP_FSM,
+ "%s [FSM] In a VRF that is not initialised yet",
+ peer->host);
+ peer->last_reset = PEER_DOWN_VRF_UNINIT;
+ return -1;
+ }
+
+ /* Register peer for NHT. If next hop is already resolved, proceed
+ * with connection setup, else wait.
+ */
+ if (!bgp_peer_reg_with_nht(peer)) {
+ if (bgp_zebra_num_connects()) {
+ if (bgp_debug_neighbor_events(peer))
+ zlog_debug("%s [FSM] Waiting for NHT",
+ peer->host);
+ peer->last_reset = PEER_DOWN_WAITING_NHT;
+ BGP_EVENT_ADD(peer, TCP_connection_open_failed);
+ return 0;
+ }
+ }
+
+ assert(!peer->t_write);
+ assert(!peer->t_read);
+ assert(!CHECK_FLAG(peer->thread_flags, PEER_THREAD_WRITES_ON));
+ assert(!CHECK_FLAG(peer->thread_flags, PEER_THREAD_READS_ON));
+ status = bgp_connect(peer);
+
+ switch (status) {
+ case connect_error:
+ if (bgp_debug_neighbor_events(peer))
+ zlog_debug("%s [FSM] Connect error", peer->host);
+ BGP_EVENT_ADD(peer, TCP_connection_open_failed);
+ break;
+ case connect_success:
+ if (bgp_debug_neighbor_events(peer))
+ zlog_debug(
+ "%s [FSM] Connect immediately success, fd %d",
+ peer->host, peer->fd);
+
+ BGP_EVENT_ADD(peer, TCP_connection_open);
+ break;
+ case connect_in_progress:
+ /* To check nonblocking connect, we wait until socket is
+ readable or writable. */
+ if (bgp_debug_neighbor_events(peer))
+ zlog_debug(
+ "%s [FSM] Non blocking connect waiting result, fd %d",
+ peer->host, peer->fd);
+ if (peer->fd < 0) {
+ flog_err(EC_BGP_FSM,
+ "%s peer's fd is negative value %d", __func__,
+ peer->fd);
+ return -1;
+ }
+ /*
+ * - when the socket becomes ready, poll() will signify POLLOUT
+ * - if it fails to connect, poll() will signify POLLHUP
+ * - POLLHUP is handled as a 'read' event by thread.c
+ *
+ * therefore, we schedule both a read and a write event with
+ * bgp_connect_check() as the handler for each and cancel the
+ * unused event in that function.
+ */
+ thread_add_read(bm->master, bgp_connect_check, peer, peer->fd,
+ &peer->t_connect_check_r);
+ thread_add_write(bm->master, bgp_connect_check, peer, peer->fd,
+ &peer->t_connect_check_w);
+ break;
+ }
+ return 0;
+}
+
+/* Connect retry timer is expired when the peer status is Connect. */
+static int bgp_reconnect(struct peer *peer)
+{
+ if (bgp_stop(peer) < 0)
+ return -1;
+
+ /* Send graceful restart capabilty */
+ BGP_GR_ROUTER_DETECT_AND_SEND_CAPABILITY_TO_ZEBRA(peer->bgp,
+ peer->bgp->peer);
+
+ bgp_start(peer);
+ return 0;
+}
+
+static int bgp_fsm_open(struct peer *peer)
+{
+ /* If DelayOpen is active, we may still need to send an open message */
+ if ((peer->status == Connect) || (peer->status == Active))
+ bgp_open_send(peer);
+
+ /* Send keepalive and make keepalive timer */
+ bgp_keepalive_send(peer);
+
+ return 0;
+}
+
+/* FSM error, unexpected event. This is error of BGP connection. So cut the
+ peer and change to Idle status. */
+static int bgp_fsm_event_error(struct peer *peer)
+{
+ flog_err(EC_BGP_FSM, "%s [FSM] unexpected packet received in state %s",
+ peer->host, lookup_msg(bgp_status_msg, peer->status, NULL));
+
+ return bgp_stop_with_notify(peer, BGP_NOTIFY_FSM_ERR,
+ bgp_fsm_error_subcode(peer->status));
+}
+
+/* Hold timer expire. This is error of BGP connection. So cut the
+ peer and change to Idle status. */
+static int bgp_fsm_holdtime_expire(struct peer *peer)
+{
+ if (bgp_debug_neighbor_events(peer))
+ zlog_debug("%s [FSM] Hold timer expire", peer->host);
+
+ /* RFC8538 updates RFC 4724 by defining an extension that permits
+ * the Graceful Restart procedures to be performed when the BGP
+ * speaker receives a BGP NOTIFICATION message or the Hold Time expires.
+ */
+ if (peer_established(peer) &&
+ bgp_has_graceful_restart_notification(peer))
+ if (CHECK_FLAG(peer->sflags, PEER_STATUS_NSF_MODE))
+ SET_FLAG(peer->sflags, PEER_STATUS_NSF_WAIT);
+
+ return bgp_stop_with_notify(peer, BGP_NOTIFY_HOLD_ERR, 0);
+}
+
+/* RFC 4271 DelayOpenTimer_Expires event */
+static int bgp_fsm_delayopen_timer_expire(struct peer *peer)
+{
+ /* Stop the DelayOpenTimer */
+ THREAD_OFF(peer->t_delayopen);
+
+ /* Send open message to peer */
+ bgp_open_send(peer);
+
+ /* Set the HoldTimer to a large value (4 minutes) */
+ peer->v_holdtime = 245;
+
+ return 0;
+}
+
+/* Start the selection deferral timer thread for the specified AFI, SAFI */
+static int bgp_start_deferral_timer(struct bgp *bgp, afi_t afi, safi_t safi,
+ struct graceful_restart_info *gr_info)
+{
+ struct afi_safi_info *thread_info;
+
+ /* If the deferral timer is active, then increment eor count */
+ if (gr_info->t_select_deferral) {
+ gr_info->eor_required++;
+ return 0;
+ }
+
+ /* Start the deferral timer when the first peer enabled for the graceful
+ * restart is established
+ */
+ if (gr_info->eor_required == 0) {
+ thread_info = XMALLOC(MTYPE_TMP, sizeof(struct afi_safi_info));
+
+ thread_info->afi = afi;
+ thread_info->safi = safi;
+ thread_info->bgp = bgp;
+
+ thread_add_timer(bm->master, bgp_graceful_deferral_timer_expire,
+ thread_info, bgp->select_defer_time,
+ &gr_info->t_select_deferral);
+ }
+ gr_info->eor_required++;
+ /* Send message to RIB indicating route update pending */
+ if (gr_info->af_enabled[afi][safi] == false) {
+ gr_info->af_enabled[afi][safi] = true;
+ /* Send message to RIB */
+ bgp_zebra_update(afi, safi, bgp->vrf_id,
+ ZEBRA_CLIENT_ROUTE_UPDATE_PENDING);
+ }
+ if (BGP_DEBUG(update, UPDATE_OUT))
+ zlog_debug("Started the deferral timer for %s eor_required %d",
+ get_afi_safi_str(afi, safi, false),
+ gr_info->eor_required);
+ return 0;
+}
+
+/* Update the graceful restart information for the specified AFI, SAFI */
+static int bgp_update_gr_info(struct peer *peer, afi_t afi, safi_t safi)
+{
+ struct graceful_restart_info *gr_info;
+ struct bgp *bgp = peer->bgp;
+ int ret = 0;
+
+ if ((afi < AFI_IP) || (afi >= AFI_MAX)) {
+ if (BGP_DEBUG(update, UPDATE_OUT))
+ zlog_debug("%s : invalid afi %d", __func__, afi);
+ return -1;
+ }
+
+ if ((safi < SAFI_UNICAST) || (safi > SAFI_MPLS_VPN)) {
+ if (BGP_DEBUG(update, UPDATE_OUT))
+ zlog_debug("%s : invalid safi %d", __func__, safi);
+ return -1;
+ }
+
+ /* Restarting router */
+ if (BGP_PEER_GRACEFUL_RESTART_CAPABLE(peer)
+ && BGP_PEER_RESTARTING_MODE(peer)) {
+ /* Check if the forwarding state is preserved */
+ if (CHECK_FLAG(bgp->flags, BGP_FLAG_GR_PRESERVE_FWD)) {
+ gr_info = &(bgp->gr_info[afi][safi]);
+ ret = bgp_start_deferral_timer(bgp, afi, safi, gr_info);
+ }
+ }
+ return ret;
+}
+
+/**
+ * Transition to Established state.
+ *
+ * Convert peer from stub to full fledged peer, set some timers, and generate
+ * initial updates.
+ */
+static int bgp_establish(struct peer *peer)
+{
+ afi_t afi;
+ safi_t safi;
+ int nsf_af_count = 0;
+ int ret = 0;
+ struct peer *other;
+ int status;
+
+ other = peer->doppelganger;
+ peer = peer_xfer_conn(peer);
+ if (!peer) {
+ flog_err(EC_BGP_CONNECT, "%%Neighbor failed in xfer_conn");
+ return -1;
+ }
+
+ if (other == peer)
+ ret = 1; /* bgp_establish specific code when xfer_conn
+ happens. */
+
+ /* Reset capability open status flag. */
+ if (!CHECK_FLAG(peer->sflags, PEER_STATUS_CAPABILITY_OPEN))
+ SET_FLAG(peer->sflags, PEER_STATUS_CAPABILITY_OPEN);
+
+ /* Clear start timer value to default. */
+ peer->v_start = BGP_INIT_START_TIMER;
+
+ /* Increment established count. */
+ peer->established++;
+ bgp_fsm_change_status(peer, Established);
+
+ /* bgp log-neighbor-changes of neighbor Up */
+ if (CHECK_FLAG(peer->bgp->flags, BGP_FLAG_LOG_NEIGHBOR_CHANGES)) {
+ struct vrf *vrf = vrf_lookup_by_id(peer->bgp->vrf_id);
+ zlog_info("%%ADJCHANGE: neighbor %pBP in vrf %s Up", peer,
+ vrf ? ((vrf->vrf_id != VRF_DEFAULT)
+ ? vrf->name
+ : VRF_DEFAULT_NAME)
+ : "");
+ }
+ /* assign update-group/subgroup */
+ update_group_adjust_peer_afs(peer);
+
+ /* graceful restart */
+ UNSET_FLAG(peer->sflags, PEER_STATUS_NSF_WAIT);
+ if (bgp_debug_neighbor_events(peer)) {
+ if (BGP_PEER_RESTARTING_MODE(peer))
+ zlog_debug("%pBP BGP_RESTARTING_MODE", peer);
+ else if (BGP_PEER_HELPER_MODE(peer))
+ zlog_debug("%pBP BGP_HELPER_MODE", peer);
+ }
+
+ FOREACH_AFI_SAFI_NSF (afi, safi) {
+ if (peer->afc_nego[afi][safi] &&
+ CHECK_FLAG(peer->cap, PEER_CAP_RESTART_ADV) &&
+ CHECK_FLAG(peer->af_cap[afi][safi],
+ PEER_CAP_RESTART_AF_RCV)) {
+ if (peer->nsf[afi][safi] &&
+ !CHECK_FLAG(peer->af_cap[afi][safi],
+ PEER_CAP_RESTART_AF_PRESERVE_RCV))
+ bgp_clear_stale_route(peer, afi, safi);
+
+ peer->nsf[afi][safi] = 1;
+ nsf_af_count++;
+ } else {
+ if (peer->nsf[afi][safi])
+ bgp_clear_stale_route(peer, afi, safi);
+ peer->nsf[afi][safi] = 0;
+ }
+ /* Update the graceful restart information */
+ if (peer->afc_nego[afi][safi]) {
+ if (!BGP_SELECT_DEFER_DISABLE(peer->bgp)) {
+ status = bgp_update_gr_info(peer, afi, safi);
+ if (status < 0)
+ zlog_err(
+ "Error in updating graceful restart for %s",
+ get_afi_safi_str(afi, safi,
+ false));
+ } else {
+ if (BGP_PEER_GRACEFUL_RESTART_CAPABLE(peer) &&
+ BGP_PEER_RESTARTING_MODE(peer) &&
+ CHECK_FLAG(peer->bgp->flags,
+ BGP_FLAG_GR_PRESERVE_FWD))
+ peer->bgp->gr_info[afi][safi]
+ .eor_required++;
+ }
+ }
+ }
+
+ if (!CHECK_FLAG(peer->cap, PEER_CAP_RESTART_RCV)) {
+ if ((bgp_peer_gr_mode_get(peer) == PEER_GR)
+ || ((bgp_peer_gr_mode_get(peer) == PEER_GLOBAL_INHERIT)
+ && (bgp_global_gr_mode_get(peer->bgp) == GLOBAL_GR))) {
+ FOREACH_AFI_SAFI (afi, safi)
+ /* Send route processing complete
+ message to RIB */
+ bgp_zebra_update(
+ afi, safi, peer->bgp->vrf_id,
+ ZEBRA_CLIENT_ROUTE_UPDATE_COMPLETE);
+ }
+ } else {
+ /* Peer sends R-bit. In this case, we need to send
+ * ZEBRA_CLIENT_ROUTE_UPDATE_COMPLETE to Zebra. */
+ if (CHECK_FLAG(peer->cap,
+ PEER_CAP_GRACEFUL_RESTART_R_BIT_RCV)) {
+ FOREACH_AFI_SAFI (afi, safi)
+ /* Send route processing complete
+ message to RIB */
+ bgp_zebra_update(
+ afi, safi, peer->bgp->vrf_id,
+ ZEBRA_CLIENT_ROUTE_UPDATE_COMPLETE);
+ }
+ }
+
+ peer->nsf_af_count = nsf_af_count;
+
+ if (nsf_af_count)
+ SET_FLAG(peer->sflags, PEER_STATUS_NSF_MODE);
+ else {
+ UNSET_FLAG(peer->sflags, PEER_STATUS_NSF_MODE);
+ if (peer->t_gr_stale) {
+ THREAD_OFF(peer->t_gr_stale);
+ if (bgp_debug_neighbor_events(peer))
+ zlog_debug(
+ "%pBP graceful restart stalepath timer stopped",
+ peer);
+ }
+ }
+
+ if (peer->t_gr_restart) {
+ THREAD_OFF(peer->t_gr_restart);
+ if (bgp_debug_neighbor_events(peer))
+ zlog_debug("%pBP graceful restart timer stopped", peer);
+ }
+
+ /* Reset uptime, turn on keepalives, send current table. */
+ if (!peer->v_holdtime)
+ bgp_keepalives_on(peer);
+
+ peer->uptime = monotime(NULL);
+
+ /* Send route-refresh when ORF is enabled.
+ * Stop Long-lived Graceful Restart timers.
+ */
+ FOREACH_AFI_SAFI (afi, safi) {
+ if (peer->t_llgr_stale[afi][safi]) {
+ THREAD_OFF(peer->t_llgr_stale[afi][safi]);
+ if (bgp_debug_neighbor_events(peer))
+ zlog_debug(
+ "%pBP Long-lived stale timer stopped for afi/safi: %d/%d",
+ peer, afi, safi);
+ }
+
+ if (CHECK_FLAG(peer->af_cap[afi][safi],
+ PEER_CAP_ORF_PREFIX_SM_ADV)) {
+ if (CHECK_FLAG(peer->af_cap[afi][safi],
+ PEER_CAP_ORF_PREFIX_RM_RCV))
+ bgp_route_refresh_send(
+ peer, afi, safi, ORF_TYPE_PREFIX,
+ REFRESH_IMMEDIATE, 0,
+ BGP_ROUTE_REFRESH_NORMAL);
+ else if (CHECK_FLAG(peer->af_cap[afi][safi],
+ PEER_CAP_ORF_PREFIX_RM_OLD_RCV))
+ bgp_route_refresh_send(
+ peer, afi, safi, ORF_TYPE_PREFIX_OLD,
+ REFRESH_IMMEDIATE, 0,
+ BGP_ROUTE_REFRESH_NORMAL);
+ }
+ }
+
+ /* First update is deferred until ORF or ROUTE-REFRESH is received */
+ FOREACH_AFI_SAFI (afi, safi) {
+ if (CHECK_FLAG(peer->af_cap[afi][safi],
+ PEER_CAP_ORF_PREFIX_RM_ADV))
+ if (CHECK_FLAG(peer->af_cap[afi][safi],
+ PEER_CAP_ORF_PREFIX_SM_RCV)
+ || CHECK_FLAG(peer->af_cap[afi][safi],
+ PEER_CAP_ORF_PREFIX_SM_OLD_RCV))
+ SET_FLAG(peer->af_sflags[afi][safi],
+ PEER_STATUS_ORF_WAIT_REFRESH);
+ }
+
+ bgp_announce_peer(peer);
+
+ /* Start the route advertisement timer to send updates to the peer - if
+ * BGP
+ * is not in read-only mode. If it is, the timer will be started at the
+ * end
+ * of read-only mode.
+ */
+ if (!bgp_update_delay_active(peer->bgp)) {
+ THREAD_OFF(peer->t_routeadv);
+ BGP_TIMER_ON(peer->t_routeadv, bgp_routeadv_timer, 0);
+ }
+
+ if (peer->doppelganger && (peer->doppelganger->status != Deleted)) {
+ if (bgp_debug_neighbor_events(peer))
+ zlog_debug(
+ "[Event] Deleting stub connection for peer %s",
+ peer->host);
+
+ if (peer->doppelganger->status > Active)
+ bgp_notify_send(peer->doppelganger, BGP_NOTIFY_CEASE,
+ BGP_NOTIFY_CEASE_COLLISION_RESOLUTION);
+ else
+ peer_delete(peer->doppelganger);
+ }
+
+ /*
+ * If we are replacing the old peer for a doppelganger
+ * then switch it around in the bgp->peerhash
+ * the doppelgangers su and this peer's su are the same
+ * so the hash_release is the same for either.
+ */
+ hash_release(peer->bgp->peerhash, peer);
+ (void)hash_get(peer->bgp->peerhash, peer, hash_alloc_intern);
+
+ /* Start BFD peer if not already running. */
+ if (peer->bfd_config)
+ bgp_peer_bfd_update_source(peer);
+
+ return ret;
+}
+
+/* Keepalive packet is received. */
+static int bgp_fsm_keepalive(struct peer *peer)
+{
+ THREAD_OFF(peer->t_holdtime);
+ return 0;
+}
+
+/* Update packet is received. */
+static int bgp_fsm_update(struct peer *peer)
+{
+ THREAD_OFF(peer->t_holdtime);
+ return 0;
+}
+
+/* This is empty event. */
+static int bgp_ignore(struct peer *peer)
+{
+ flog_err(
+ EC_BGP_FSM,
+ "%s [FSM] Ignoring event %s in state %s, prior events %s, %s, fd %d",
+ peer->host, bgp_event_str[peer->cur_event],
+ lookup_msg(bgp_status_msg, peer->status, NULL),
+ bgp_event_str[peer->last_event],
+ bgp_event_str[peer->last_major_event], peer->fd);
+ return 0;
+}
+
+/* This is to handle unexpected events.. */
+static int bgp_fsm_exeption(struct peer *peer)
+{
+ flog_err(
+ EC_BGP_FSM,
+ "%s [FSM] Unexpected event %s in state %s, prior events %s, %s, fd %d",
+ peer->host, bgp_event_str[peer->cur_event],
+ lookup_msg(bgp_status_msg, peer->status, NULL),
+ bgp_event_str[peer->last_event],
+ bgp_event_str[peer->last_major_event], peer->fd);
+ return (bgp_stop(peer));
+}
+
+void bgp_fsm_nht_update(struct peer *peer, bool has_valid_nexthops)
+{
+ if (!peer)
+ return;
+
+ switch (peer->status) {
+ case Idle:
+ if (has_valid_nexthops)
+ BGP_EVENT_ADD(peer, BGP_Start);
+ break;
+ case Connect:
+ if (!has_valid_nexthops) {
+ THREAD_OFF(peer->t_connect);
+ BGP_EVENT_ADD(peer, TCP_fatal_error);
+ }
+ break;
+ case Active:
+ if (has_valid_nexthops) {
+ THREAD_OFF(peer->t_connect);
+ BGP_EVENT_ADD(peer, ConnectRetry_timer_expired);
+ }
+ break;
+ case OpenSent:
+ case OpenConfirm:
+ case Established:
+ if (!has_valid_nexthops
+ && (peer->gtsm_hops == BGP_GTSM_HOPS_CONNECTED
+ || peer->bgp->fast_convergence))
+ BGP_EVENT_ADD(peer, TCP_fatal_error);
+ case Clearing:
+ case Deleted:
+ default:
+ break;
+ }
+}
+
+/* Finite State Machine structure */
+static const struct {
+ int (*func)(struct peer *);
+ enum bgp_fsm_status next_state;
+} FSM[BGP_STATUS_MAX - 1][BGP_EVENTS_MAX - 1] = {
+ {
+ /* Idle state: In Idle state, all events other than BGP_Start is
+ ignored. With BGP_Start event, finite state machine calls
+ bgp_start(). */
+ {bgp_start, Connect}, /* BGP_Start */
+ {bgp_stop, Idle}, /* BGP_Stop */
+ {bgp_stop, Idle}, /* TCP_connection_open */
+ {bgp_stop, Idle}, /* TCP_connection_open_w_delay */
+ {bgp_stop, Idle}, /* TCP_connection_closed */
+ {bgp_ignore, Idle}, /* TCP_connection_open_failed */
+ {bgp_stop, Idle}, /* TCP_fatal_error */
+ {bgp_ignore, Idle}, /* ConnectRetry_timer_expired */
+ {bgp_ignore, Idle}, /* Hold_Timer_expired */
+ {bgp_ignore, Idle}, /* KeepAlive_timer_expired */
+ {bgp_ignore, Idle}, /* DelayOpen_timer_expired */
+ {bgp_ignore, Idle}, /* Receive_OPEN_message */
+ {bgp_ignore, Idle}, /* Receive_KEEPALIVE_message */
+ {bgp_ignore, Idle}, /* Receive_UPDATE_message */
+ {bgp_ignore, Idle}, /* Receive_NOTIFICATION_message */
+ {bgp_ignore, Idle}, /* Clearing_Completed */
+ },
+ {
+ /* Connect */
+ {bgp_ignore, Connect}, /* BGP_Start */
+ {bgp_stop, Idle}, /* BGP_Stop */
+ {bgp_connect_success, OpenSent}, /* TCP_connection_open */
+ {bgp_connect_success_w_delayopen,
+ Connect}, /* TCP_connection_open_w_delay */
+ {bgp_stop, Idle}, /* TCP_connection_closed */
+ {bgp_connect_fail, Active}, /* TCP_connection_open_failed */
+ {bgp_connect_fail, Idle}, /* TCP_fatal_error */
+ {bgp_reconnect, Connect}, /* ConnectRetry_timer_expired */
+ {bgp_fsm_exeption, Idle}, /* Hold_Timer_expired */
+ {bgp_fsm_exeption, Idle}, /* KeepAlive_timer_expired */
+ {bgp_fsm_delayopen_timer_expire,
+ OpenSent}, /* DelayOpen_timer_expired */
+ {bgp_fsm_open, OpenConfirm}, /* Receive_OPEN_message */
+ {bgp_fsm_exeption, Idle}, /* Receive_KEEPALIVE_message */
+ {bgp_fsm_exeption, Idle}, /* Receive_UPDATE_message */
+ {bgp_stop, Idle}, /* Receive_NOTIFICATION_message */
+ {bgp_fsm_exeption, Idle}, /* Clearing_Completed */
+ },
+ {
+ /* Active, */
+ {bgp_ignore, Active}, /* BGP_Start */
+ {bgp_stop, Idle}, /* BGP_Stop */
+ {bgp_connect_success, OpenSent}, /* TCP_connection_open */
+ {bgp_connect_success_w_delayopen,
+ Active}, /* TCP_connection_open_w_delay */
+ {bgp_stop, Idle}, /* TCP_connection_closed */
+ {bgp_ignore, Active}, /* TCP_connection_open_failed */
+ {bgp_fsm_exeption, Idle}, /* TCP_fatal_error */
+ {bgp_start, Connect}, /* ConnectRetry_timer_expired */
+ {bgp_fsm_exeption, Idle}, /* Hold_Timer_expired */
+ {bgp_fsm_exeption, Idle}, /* KeepAlive_timer_expired */
+ {bgp_fsm_delayopen_timer_expire,
+ OpenSent}, /* DelayOpen_timer_expired */
+ {bgp_fsm_open, OpenConfirm}, /* Receive_OPEN_message */
+ {bgp_fsm_exeption, Idle}, /* Receive_KEEPALIVE_message */
+ {bgp_fsm_exeption, Idle}, /* Receive_UPDATE_message */
+ {bgp_fsm_exeption, Idle}, /* Receive_NOTIFICATION_message */
+ {bgp_fsm_exeption, Idle}, /* Clearing_Completed */
+ },
+ {
+ /* OpenSent, */
+ {bgp_ignore, OpenSent}, /* BGP_Start */
+ {bgp_stop, Idle}, /* BGP_Stop */
+ {bgp_stop, Active}, /* TCP_connection_open */
+ {bgp_fsm_exeption, Idle}, /* TCP_connection_open_w_delay */
+ {bgp_stop, Active}, /* TCP_connection_closed */
+ {bgp_stop, Active}, /* TCP_connection_open_failed */
+ {bgp_stop, Active}, /* TCP_fatal_error */
+ {bgp_fsm_exeption, Idle}, /* ConnectRetry_timer_expired */
+ {bgp_fsm_holdtime_expire, Idle}, /* Hold_Timer_expired */
+ {bgp_fsm_exeption, Idle}, /* KeepAlive_timer_expired */
+ {bgp_fsm_exeption, Idle}, /* DelayOpen_timer_expired */
+ {bgp_fsm_open, OpenConfirm}, /* Receive_OPEN_message */
+ {bgp_fsm_event_error, Idle}, /* Receive_KEEPALIVE_message */
+ {bgp_fsm_event_error, Idle}, /* Receive_UPDATE_message */
+ {bgp_fsm_event_error, Idle}, /* Receive_NOTIFICATION_message */
+ {bgp_fsm_exeption, Idle}, /* Clearing_Completed */
+ },
+ {
+ /* OpenConfirm, */
+ {bgp_ignore, OpenConfirm}, /* BGP_Start */
+ {bgp_stop, Idle}, /* BGP_Stop */
+ {bgp_stop, Idle}, /* TCP_connection_open */
+ {bgp_fsm_exeption, Idle}, /* TCP_connection_open_w_delay */
+ {bgp_stop, Idle}, /* TCP_connection_closed */
+ {bgp_stop, Idle}, /* TCP_connection_open_failed */
+ {bgp_stop, Idle}, /* TCP_fatal_error */
+ {bgp_fsm_exeption, Idle}, /* ConnectRetry_timer_expired */
+ {bgp_fsm_holdtime_expire, Idle}, /* Hold_Timer_expired */
+ {bgp_ignore, OpenConfirm}, /* KeepAlive_timer_expired */
+ {bgp_fsm_exeption, Idle}, /* DelayOpen_timer_expired */
+ {bgp_fsm_exeption, Idle}, /* Receive_OPEN_message */
+ {bgp_establish, Established}, /* Receive_KEEPALIVE_message */
+ {bgp_fsm_exeption, Idle}, /* Receive_UPDATE_message */
+ {bgp_stop_with_error, Idle}, /* Receive_NOTIFICATION_message */
+ {bgp_fsm_exeption, Idle}, /* Clearing_Completed */
+ },
+ {
+ /* Established, */
+ {bgp_ignore, Established}, /* BGP_Start */
+ {bgp_stop, Clearing}, /* BGP_Stop */
+ {bgp_stop, Clearing}, /* TCP_connection_open */
+ {bgp_fsm_exeption, Idle}, /* TCP_connection_open_w_delay */
+ {bgp_stop, Clearing}, /* TCP_connection_closed */
+ {bgp_stop, Clearing}, /* TCP_connection_open_failed */
+ {bgp_stop, Clearing}, /* TCP_fatal_error */
+ {bgp_stop, Clearing}, /* ConnectRetry_timer_expired */
+ {bgp_fsm_holdtime_expire, Clearing}, /* Hold_Timer_expired */
+ {bgp_ignore, Established}, /* KeepAlive_timer_expired */
+ {bgp_fsm_exeption, Idle}, /* DelayOpen_timer_expired */
+ {bgp_stop, Clearing}, /* Receive_OPEN_message */
+ {bgp_fsm_keepalive,
+ Established}, /* Receive_KEEPALIVE_message */
+ {bgp_fsm_update, Established}, /* Receive_UPDATE_message */
+ {bgp_stop_with_error,
+ Clearing}, /* Receive_NOTIFICATION_message */
+ {bgp_fsm_exeption, Idle}, /* Clearing_Completed */
+ },
+ {
+ /* Clearing, */
+ {bgp_ignore, Clearing}, /* BGP_Start */
+ {bgp_stop, Clearing}, /* BGP_Stop */
+ {bgp_stop, Clearing}, /* TCP_connection_open */
+ {bgp_stop, Clearing}, /* TCP_connection_open_w_delay */
+ {bgp_stop, Clearing}, /* TCP_connection_closed */
+ {bgp_stop, Clearing}, /* TCP_connection_open_failed */
+ {bgp_stop, Clearing}, /* TCP_fatal_error */
+ {bgp_stop, Clearing}, /* ConnectRetry_timer_expired */
+ {bgp_stop, Clearing}, /* Hold_Timer_expired */
+ {bgp_stop, Clearing}, /* KeepAlive_timer_expired */
+ {bgp_stop, Clearing}, /* DelayOpen_timer_expired */
+ {bgp_stop, Clearing}, /* Receive_OPEN_message */
+ {bgp_stop, Clearing}, /* Receive_KEEPALIVE_message */
+ {bgp_stop, Clearing}, /* Receive_UPDATE_message */
+ {bgp_stop, Clearing}, /* Receive_NOTIFICATION_message */
+ {bgp_clearing_completed, Idle}, /* Clearing_Completed */
+ },
+ {
+ /* Deleted, */
+ {bgp_ignore, Deleted}, /* BGP_Start */
+ {bgp_ignore, Deleted}, /* BGP_Stop */
+ {bgp_ignore, Deleted}, /* TCP_connection_open */
+ {bgp_ignore, Deleted}, /* TCP_connection_open_w_delay */
+ {bgp_ignore, Deleted}, /* TCP_connection_closed */
+ {bgp_ignore, Deleted}, /* TCP_connection_open_failed */
+ {bgp_ignore, Deleted}, /* TCP_fatal_error */
+ {bgp_ignore, Deleted}, /* ConnectRetry_timer_expired */
+ {bgp_ignore, Deleted}, /* Hold_Timer_expired */
+ {bgp_ignore, Deleted}, /* KeepAlive_timer_expired */
+ {bgp_ignore, Deleted}, /* DelayOpen_timer_expired */
+ {bgp_ignore, Deleted}, /* Receive_OPEN_message */
+ {bgp_ignore, Deleted}, /* Receive_KEEPALIVE_message */
+ {bgp_ignore, Deleted}, /* Receive_UPDATE_message */
+ {bgp_ignore, Deleted}, /* Receive_NOTIFICATION_message */
+ {bgp_ignore, Deleted}, /* Clearing_Completed */
+ },
+};
+
+/* Execute event process. */
+void bgp_event(struct thread *thread)
+{
+ enum bgp_fsm_events event;
+ struct peer *peer;
+
+ peer = THREAD_ARG(thread);
+ event = THREAD_VAL(thread);
+
+ bgp_event_update(peer, event);
+}
+
+int bgp_event_update(struct peer *peer, enum bgp_fsm_events event)
+{
+ enum bgp_fsm_status next;
+ int ret = 0;
+ struct peer *other;
+ int passive_conn = 0;
+ int dyn_nbr;
+
+ /* default return code */
+ ret = FSM_PEER_NOOP;
+
+ other = peer->doppelganger;
+ passive_conn =
+ (CHECK_FLAG(peer->sflags, PEER_STATUS_ACCEPT_PEER)) ? 1 : 0;
+ dyn_nbr = peer_dynamic_neighbor(peer);
+
+ /* Logging this event. */
+ next = FSM[peer->status - 1][event - 1].next_state;
+
+ if (bgp_debug_neighbor_events(peer) && peer->status != next)
+ zlog_debug("%s [FSM] %s (%s->%s), fd %d", peer->host,
+ bgp_event_str[event],
+ lookup_msg(bgp_status_msg, peer->status, NULL),
+ lookup_msg(bgp_status_msg, next, NULL), peer->fd);
+
+ peer->last_event = peer->cur_event;
+ peer->cur_event = event;
+
+ /* Call function. */
+ if (FSM[peer->status - 1][event - 1].func)
+ ret = (*(FSM[peer->status - 1][event - 1].func))(peer);
+
+ if (ret >= 0) {
+ if (ret == 1 && next == Established) {
+ /* The case when doppelganger swap accurred in
+ bgp_establish.
+ Update the peer pointer accordingly */
+ ret = FSM_PEER_TRANSFERRED;
+ peer = other;
+ }
+
+ /* If status is changed. */
+ if (next != peer->status) {
+ bgp_fsm_change_status(peer, next);
+
+ /*
+ * If we're going to ESTABLISHED then we executed a
+ * peer transfer. In this case we can either return
+ * FSM_PEER_TRANSITIONED or FSM_PEER_TRANSFERRED.
+ * Opting for TRANSFERRED since transfer implies
+ * session establishment.
+ */
+ if (ret != FSM_PEER_TRANSFERRED)
+ ret = FSM_PEER_TRANSITIONED;
+ }
+
+ /* Make sure timer is set. */
+ bgp_timer_set(peer);
+
+ } else {
+ /*
+ * If we got a return value of -1, that means there was an
+ * error, restart the FSM. Since bgp_stop() was called on the
+ * peer. only a few fields are safe to access here. In any case
+ * we need to indicate that the peer was stopped in the return
+ * code.
+ */
+ if (!dyn_nbr && !passive_conn && peer->bgp) {
+ flog_err(
+ EC_BGP_FSM,
+ "%s [FSM] Failure handling event %s in state %s, prior events %s, %s, fd %d",
+ peer->host, bgp_event_str[peer->cur_event],
+ lookup_msg(bgp_status_msg, peer->status, NULL),
+ bgp_event_str[peer->last_event],
+ bgp_event_str[peer->last_major_event],
+ peer->fd);
+ bgp_stop(peer);
+ bgp_fsm_change_status(peer, Idle);
+ bgp_timer_set(peer);
+ }
+ ret = FSM_PEER_STOPPED;
+ }
+
+ return ret;
+}
+/* BGP GR Code */
+
+int bgp_gr_lookup_n_update_all_peer(struct bgp *bgp,
+ enum global_mode global_new_state,
+ enum global_mode global_old_state)
+{
+ struct peer *peer = {0};
+ struct listnode *node = {0};
+ struct listnode *nnode = {0};
+ enum peer_mode peer_old_state = PEER_INVALID;
+
+ for (ALL_LIST_ELEMENTS(bgp->peer, node, nnode, peer)) {
+
+ if (BGP_DEBUG(graceful_restart, GRACEFUL_RESTART))
+ zlog_debug("%s [BGP_GR] Peer: (%s) :", __func__,
+ peer->host);
+
+ peer_old_state = bgp_peer_gr_mode_get(peer);
+
+ if (peer_old_state == PEER_GLOBAL_INHERIT) {
+
+ /*
+ *Reset only these peers and send a
+ *new open message with the change capabilities.
+ *Considering the mode to be "global_new_state" and
+ *do all operation accordingly
+ */
+
+ switch (global_new_state) {
+ case GLOBAL_HELPER:
+ BGP_PEER_GR_HELPER_ENABLE(peer);
+ break;
+ case GLOBAL_GR:
+ BGP_PEER_GR_ENABLE(peer);
+ break;
+ case GLOBAL_DISABLE:
+ BGP_PEER_GR_DISABLE(peer);
+ break;
+ case GLOBAL_INVALID:
+ zlog_debug("%s [BGP_GR] GLOBAL_INVALID",
+ __func__);
+ return BGP_ERR_GR_OPERATION_FAILED;
+ }
+ }
+ }
+
+ bgp->global_gr_present_state = global_new_state;
+
+ return BGP_GR_SUCCESS;
+}
+
+int bgp_gr_update_all(struct bgp *bgp, int global_gr_cmd)
+{
+ enum global_mode global_new_state = GLOBAL_INVALID;
+ enum global_mode global_old_state = GLOBAL_INVALID;
+
+ if (BGP_DEBUG(graceful_restart, GRACEFUL_RESTART))
+ zlog_debug("%s [BGP_GR]START: global_gr_cmd :%s:", __func__,
+ print_global_gr_cmd(global_gr_cmd));
+
+ global_old_state = bgp_global_gr_mode_get(bgp);
+
+ if (BGP_DEBUG(graceful_restart, GRACEFUL_RESTART))
+ zlog_debug("[BGP_GR] global_old_gr_state :%s:",
+ print_global_gr_mode(global_old_state));
+
+ if (global_old_state != GLOBAL_INVALID) {
+ global_new_state =
+ bgp->GLOBAL_GR_FSM[global_old_state][global_gr_cmd];
+
+ if (BGP_DEBUG(graceful_restart, GRACEFUL_RESTART))
+ zlog_debug("[BGP_GR] global_new_gr_state :%s:",
+ print_global_gr_mode(global_new_state));
+ } else {
+ zlog_err("%s [BGP_GR] global_old_state == GLOBAL_INVALID",
+ __func__);
+ return BGP_ERR_GR_OPERATION_FAILED;
+ }
+
+ if (global_new_state == GLOBAL_INVALID) {
+ zlog_err("%s [BGP_GR] global_new_state == GLOBAL_INVALID",
+ __func__);
+ return BGP_ERR_GR_INVALID_CMD;
+ }
+ if (global_new_state == global_old_state) {
+ /* Trace msg */
+ if (BGP_DEBUG(graceful_restart, GRACEFUL_RESTART))
+ zlog_debug(
+ "%s [BGP_GR] global_new_state == global_old_state :%s",
+ __func__,
+ print_global_gr_mode(global_new_state));
+ return BGP_GR_NO_OPERATION;
+ }
+
+ return bgp_gr_lookup_n_update_all_peer(bgp, global_new_state,
+ global_old_state);
+}
+
+const char *print_peer_gr_mode(enum peer_mode pr_mode)
+{
+ const char *peer_gr_mode = NULL;
+
+ switch (pr_mode) {
+ case PEER_HELPER:
+ peer_gr_mode = "PEER_HELPER";
+ break;
+ case PEER_GR:
+ peer_gr_mode = "PEER_GR";
+ break;
+ case PEER_DISABLE:
+ peer_gr_mode = "PEER_DISABLE";
+ break;
+ case PEER_INVALID:
+ peer_gr_mode = "PEER_INVALID";
+ break;
+ case PEER_GLOBAL_INHERIT:
+ peer_gr_mode = "PEER_GLOBAL_INHERIT";
+ break;
+ }
+
+ return peer_gr_mode;
+}
+
+const char *print_peer_gr_cmd(enum peer_gr_command pr_gr_cmd)
+{
+ const char *peer_gr_cmd = NULL;
+
+ switch (pr_gr_cmd) {
+ case PEER_GR_CMD:
+ peer_gr_cmd = "PEER_GR_CMD";
+ break;
+ case NO_PEER_GR_CMD:
+ peer_gr_cmd = "NO_PEER_GR_CMD";
+ break;
+ case PEER_DISABLE_CMD:
+ peer_gr_cmd = "PEER_DISABLE_GR_CMD";
+ break;
+ case NO_PEER_DISABLE_CMD:
+ peer_gr_cmd = "NO_PEER_DISABLE_GR_CMD";
+ break;
+ case PEER_HELPER_CMD:
+ peer_gr_cmd = "PEER_HELPER_CMD";
+ break;
+ case NO_PEER_HELPER_CMD:
+ peer_gr_cmd = "NO_PEER_HELPER_CMD";
+ break;
+ }
+
+ return peer_gr_cmd;
+}
+
+const char *print_global_gr_mode(enum global_mode gl_mode)
+{
+ const char *global_gr_mode = NULL;
+
+ switch (gl_mode) {
+ case GLOBAL_HELPER:
+ global_gr_mode = "GLOBAL_HELPER";
+ break;
+ case GLOBAL_GR:
+ global_gr_mode = "GLOBAL_GR";
+ break;
+ case GLOBAL_DISABLE:
+ global_gr_mode = "GLOBAL_DISABLE";
+ break;
+ case GLOBAL_INVALID:
+ global_gr_mode = "GLOBAL_INVALID";
+ break;
+ }
+
+ return global_gr_mode;
+}
+
+const char *print_global_gr_cmd(enum global_gr_command gl_gr_cmd)
+{
+ const char *global_gr_cmd = NULL;
+
+ switch (gl_gr_cmd) {
+ case GLOBAL_GR_CMD:
+ global_gr_cmd = "GLOBAL_GR_CMD";
+ break;
+ case NO_GLOBAL_GR_CMD:
+ global_gr_cmd = "NO_GLOBAL_GR_CMD";
+ break;
+ case GLOBAL_DISABLE_CMD:
+ global_gr_cmd = "GLOBAL_DISABLE_CMD";
+ break;
+ case NO_GLOBAL_DISABLE_CMD:
+ global_gr_cmd = "NO_GLOBAL_DISABLE_CMD";
+ break;
+ }
+
+ return global_gr_cmd;
+}
+
+enum global_mode bgp_global_gr_mode_get(struct bgp *bgp)
+{
+ return bgp->global_gr_present_state;
+}
+
+enum peer_mode bgp_peer_gr_mode_get(struct peer *peer)
+{
+ return peer->peer_gr_present_state;
+}
+
+int bgp_neighbor_graceful_restart(struct peer *peer, int peer_gr_cmd)
+{
+ enum peer_mode peer_new_state = PEER_INVALID;
+ enum peer_mode peer_old_state = PEER_INVALID;
+ struct bgp_peer_gr peer_state;
+ int result = BGP_GR_FAILURE;
+
+ /*
+ * fetch peer_old_state from peer structure also
+ * fetch global_old_state from bgp structure,
+ * peer had a back pointer to bgpo struct ;
+ */
+
+ if (BGP_DEBUG(graceful_restart, GRACEFUL_RESTART))
+ zlog_debug("%s [BGP_GR] START:Peer: (%s) : peer_gr_cmd :%s:",
+ __func__, peer->host,
+ print_peer_gr_cmd(peer_gr_cmd));
+
+ peer_old_state = bgp_peer_gr_mode_get(peer);
+
+ if (peer_old_state == PEER_INVALID) {
+ zlog_debug("[BGP_GR] peer_old_state == Invalid state !");
+ return BGP_ERR_GR_OPERATION_FAILED;
+ }
+
+ peer_state = peer->PEER_GR_FSM[peer_old_state][peer_gr_cmd];
+ peer_new_state = peer_state.next_state;
+
+ if (peer_new_state == PEER_INVALID) {
+ zlog_debug(
+ "[BGP_GR] Invalid bgp graceful restart command used !");
+ return BGP_ERR_GR_INVALID_CMD;
+ }
+
+ if (peer_new_state != peer_old_state) {
+ result = peer_state.action_fun(peer, peer_old_state,
+ peer_new_state);
+ } else {
+ if (BGP_DEBUG(graceful_restart, GRACEFUL_RESTART))
+ zlog_debug(
+ "[BGP_GR] peer_old_state == peer_new_state !");
+ return BGP_GR_NO_OPERATION;
+ }
+
+ if (result == BGP_GR_SUCCESS) {
+
+ /* Update the mode i.e peer_new_state into the peer structure */
+ peer->peer_gr_present_state = peer_new_state;
+ if (BGP_DEBUG(graceful_restart, GRACEFUL_RESTART))
+ zlog_debug(
+ "[BGP_GR] Successfully change the state of the peer to : %s : !",
+ print_peer_gr_mode(peer_new_state));
+
+ return BGP_GR_SUCCESS;
+ }
+
+ return result;
+}
+
+unsigned int bgp_peer_gr_action(struct peer *peer, int old_peer_state,
+ int new_peer_state)
+{
+ if (BGP_DEBUG(graceful_restart, GRACEFUL_RESTART))
+ zlog_debug(
+ "%s [BGP_GR] Move peer from old_peer_state :%s: to new_peer_state :%s: !!!!",
+ __func__, print_peer_gr_mode(old_peer_state),
+ print_peer_gr_mode(new_peer_state));
+
+ int bgp_gr_global_mode = GLOBAL_INVALID;
+ unsigned int ret = BGP_GR_FAILURE;
+
+ if (old_peer_state == new_peer_state) {
+ /* Nothing to do over here as the present and old state is the
+ * same */
+ return BGP_GR_NO_OPERATION;
+ }
+ if ((old_peer_state == PEER_INVALID)
+ || (new_peer_state == PEER_INVALID)) {
+ /* something bad happend , print error message */
+ return BGP_ERR_GR_INVALID_CMD;
+ }
+
+ bgp_gr_global_mode = bgp_global_gr_mode_get(peer->bgp);
+
+ if ((old_peer_state == PEER_GLOBAL_INHERIT)
+ && (new_peer_state != PEER_GLOBAL_INHERIT)) {
+
+ /* fetch the Mode running in the Global state machine
+ *from the bgp structure into a variable called
+ *bgp_gr_global_mode
+ */
+
+ /* Here we are checking if the
+ *1. peer_new_state == global_mode == helper_mode
+ *2. peer_new_state == global_mode == GR_mode
+ *3. peer_new_state == global_mode == disabled_mode
+ */
+
+ BGP_PEER_GR_GLOBAL_INHERIT_UNSET(peer);
+
+ if (new_peer_state == bgp_gr_global_mode) {
+ /*This is incremental updates i.e no tear down
+ *of the existing session
+ *as the peer is already working in the same mode.
+ */
+ ret = BGP_GR_SUCCESS;
+ } else {
+ if (BGP_DEBUG(graceful_restart, GRACEFUL_RESTART))
+ zlog_debug(
+ "[BGP_GR] Peer state changed from :%s ",
+ print_peer_gr_mode(old_peer_state));
+
+ bgp_peer_move_to_gr_mode(peer, new_peer_state);
+
+ ret = BGP_GR_SUCCESS;
+ }
+ }
+ /* In the case below peer is going into Global inherit mode i.e.
+ * the peer would work as the mode configured at the global level
+ */
+ else if ((new_peer_state == PEER_GLOBAL_INHERIT)
+ && (old_peer_state != PEER_GLOBAL_INHERIT)) {
+ /* Here in this case it would be destructive
+ * in all the cases except one case when,
+ * Global GR is configured Disabled
+ * and present_peer_state is not disable
+ */
+
+ BGP_PEER_GR_GLOBAL_INHERIT_SET(peer);
+
+ if (old_peer_state == bgp_gr_global_mode) {
+
+ /* This is incremental updates
+ *i.e no tear down of the existing session
+ *as the peer is already working in the same mode.
+ */
+ ret = BGP_GR_SUCCESS;
+ } else {
+ /* Destructive always */
+ /* Tear down the old session
+ * and send the new capability
+ * as per the bgp_gr_global_mode
+ */
+
+ if (BGP_DEBUG(graceful_restart, GRACEFUL_RESTART))
+ zlog_debug(
+ "[BGP_GR] Peer state changed from :%s",
+ print_peer_gr_mode(old_peer_state));
+
+ bgp_peer_move_to_gr_mode(peer, bgp_gr_global_mode);
+
+ ret = BGP_GR_SUCCESS;
+ }
+ } else {
+ /*
+ *This else case, it include all the cases except -->
+ *(new_peer_state != Peer_Global) &&
+ *( old_peer_state != Peer_Global )
+ */
+ if (BGP_DEBUG(graceful_restart, GRACEFUL_RESTART))
+ zlog_debug("[BGP_GR] Peer state changed from :%s",
+ print_peer_gr_mode(old_peer_state));
+
+ bgp_peer_move_to_gr_mode(peer, new_peer_state);
+
+ ret = BGP_GR_SUCCESS;
+ }
+
+ return ret;
+}
+
+inline void bgp_peer_move_to_gr_mode(struct peer *peer, int new_state)
+
+{
+ int bgp_global_gr_mode = bgp_global_gr_mode_get(peer->bgp);
+
+ switch (new_state) {
+ case PEER_HELPER:
+ BGP_PEER_GR_HELPER_ENABLE(peer);
+ break;
+ case PEER_GR:
+ BGP_PEER_GR_ENABLE(peer);
+ break;
+ case PEER_DISABLE:
+ BGP_PEER_GR_DISABLE(peer);
+ break;
+ case PEER_GLOBAL_INHERIT:
+ BGP_PEER_GR_GLOBAL_INHERIT_SET(peer);
+
+ if (bgp_global_gr_mode == GLOBAL_HELPER) {
+ BGP_PEER_GR_HELPER_ENABLE(peer);
+ } else if (bgp_global_gr_mode == GLOBAL_GR) {
+ BGP_PEER_GR_ENABLE(peer);
+ } else if (bgp_global_gr_mode == GLOBAL_DISABLE) {
+ BGP_PEER_GR_DISABLE(peer);
+ } else {
+ zlog_err(
+ "[BGP_GR] Default switch inherit mode ::: SOMETHING IS WRONG !!!");
+ }
+ break;
+ default:
+ zlog_err(
+ "[BGP_GR] Default switch mode ::: SOMETHING IS WRONG !!!");
+ break;
+ }
+ if (BGP_DEBUG(graceful_restart, GRACEFUL_RESTART))
+ zlog_debug("[BGP_GR] Peer state changed --to--> : %d : !",
+ new_state);
+}
+
+void bgp_peer_gr_flags_update(struct peer *peer)
+{
+ if (BGP_DEBUG(graceful_restart, GRACEFUL_RESTART))
+ zlog_debug("%s [BGP_GR] called !", __func__);
+ if (CHECK_FLAG(peer->peer_gr_new_status_flag,
+ PEER_GRACEFUL_RESTART_NEW_STATE_HELPER))
+ SET_FLAG(peer->flags, PEER_FLAG_GRACEFUL_RESTART_HELPER);
+ else
+ UNSET_FLAG(peer->flags, PEER_FLAG_GRACEFUL_RESTART_HELPER);
+ if (BGP_DEBUG(graceful_restart, GRACEFUL_RESTART))
+ zlog_debug(
+ "[BGP_GR] Peer %s Flag PEER_FLAG_GRACEFUL_RESTART_HELPER : %s : !",
+ peer->host,
+ (CHECK_FLAG(peer->flags,
+ PEER_FLAG_GRACEFUL_RESTART_HELPER)
+ ? "Set"
+ : "UnSet"));
+ if (CHECK_FLAG(peer->peer_gr_new_status_flag,
+ PEER_GRACEFUL_RESTART_NEW_STATE_RESTART))
+ SET_FLAG(peer->flags, PEER_FLAG_GRACEFUL_RESTART);
+ else
+ UNSET_FLAG(peer->flags, PEER_FLAG_GRACEFUL_RESTART);
+ if (BGP_DEBUG(graceful_restart, GRACEFUL_RESTART))
+ zlog_debug(
+ "[BGP_GR] Peer %s Flag PEER_FLAG_GRACEFUL_RESTART : %s : !",
+ peer->host,
+ (CHECK_FLAG(peer->flags, PEER_FLAG_GRACEFUL_RESTART)
+ ? "Set"
+ : "UnSet"));
+ if (CHECK_FLAG(peer->peer_gr_new_status_flag,
+ PEER_GRACEFUL_RESTART_NEW_STATE_INHERIT))
+ SET_FLAG(peer->flags,
+ PEER_FLAG_GRACEFUL_RESTART_GLOBAL_INHERIT);
+ else
+ UNSET_FLAG(peer->flags,
+ PEER_FLAG_GRACEFUL_RESTART_GLOBAL_INHERIT);
+ if (BGP_DEBUG(graceful_restart, GRACEFUL_RESTART))
+ zlog_debug(
+ "[BGP_GR] Peer %s Flag PEER_FLAG_GRACEFUL_RESTART_GLOBAL_INHERIT : %s : !",
+ peer->host,
+ (CHECK_FLAG(peer->flags,
+ PEER_FLAG_GRACEFUL_RESTART_GLOBAL_INHERIT)
+ ? "Set"
+ : "UnSet"));
+
+ if (!CHECK_FLAG(peer->flags, PEER_FLAG_GRACEFUL_RESTART)
+ && !CHECK_FLAG(peer->flags, PEER_FLAG_GRACEFUL_RESTART_HELPER)) {
+ zlog_debug("[BGP_GR] Peer %s UNSET PEER_STATUS_NSF_MODE!",
+ peer->host);
+
+ UNSET_FLAG(peer->sflags, PEER_STATUS_NSF_MODE);
+
+ if (CHECK_FLAG(peer->sflags, PEER_STATUS_NSF_WAIT)) {
+
+ peer_nsf_stop(peer);
+ zlog_debug(
+ "[BGP_GR] Peer %s UNSET PEER_STATUS_NSF_WAIT!",
+ peer->host);
+ }
+ }
+}
diff --git a/bgpd/bgp_fsm.h b/bgpd/bgp_fsm.h
new file mode 100644
index 0000000..aaf6c48
--- /dev/null
+++ b/bgpd/bgp_fsm.h
@@ -0,0 +1,178 @@
+/* BGP-4 Finite State Machine
+ * From RFC1771 [A Border Gateway Protocol 4 (BGP-4)]
+ * Copyright (C) 1998 Kunihiro Ishiguro
+ *
+ * This file is part of GNU Zebra.
+ *
+ * GNU Zebra is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License as published by the
+ * Free Software Foundation; either version 2, or (at your option) any
+ * later version.
+ *
+ * GNU Zebra is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program; see the file COPYING; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
+ */
+
+#ifndef _QUAGGA_BGP_FSM_H
+#define _QUAGGA_BGP_FSM_H
+
+/* Macro for BGP read, write and timer thread. */
+#define BGP_TIMER_ON(T, F, V) \
+ do { \
+ if ((peer->status != Deleted)) \
+ thread_add_timer(bm->master, (F), peer, (V), &(T)); \
+ } while (0)
+
+#define BGP_EVENT_ADD(P, E) \
+ do { \
+ if ((P)->status != Deleted) \
+ thread_add_event(bm->master, bgp_event, (P), (E), \
+ NULL); \
+ } while (0)
+
+#define BGP_EVENT_FLUSH(P) \
+ do { \
+ assert(peer); \
+ thread_cancel_event_ready(bm->master, (P)); \
+ } while (0)
+
+#define BGP_UPDATE_GROUP_TIMER_ON(T, F) \
+ do { \
+ if (BGP_SUPPRESS_FIB_ENABLED(peer->bgp) && \
+ PEER_ROUTE_ADV_DELAY(peer)) \
+ thread_add_timer_msec(bm->master, (F), peer, \
+ (BGP_DEFAULT_UPDATE_ADVERTISEMENT_TIME * 1000),\
+ (T)); \
+ else \
+ thread_add_timer_msec(bm->master, (F), peer, \
+ 0, (T)); \
+ } while (0) \
+
+#define BGP_MSEC_JITTER 10
+
+/* Status codes for bgp_event_update() */
+#define FSM_PEER_NOOP 0
+#define FSM_PEER_STOPPED 1
+#define FSM_PEER_TRANSFERRED 2
+#define FSM_PEER_TRANSITIONED 3
+
+#define BGP_PEER_GR_HELPER_ENABLE(peer) \
+ do { \
+ UNSET_FLAG( \
+ peer->peer_gr_new_status_flag, \
+ PEER_GRACEFUL_RESTART_NEW_STATE_RESTART); \
+ SET_FLAG( \
+ peer->peer_gr_new_status_flag, \
+ PEER_GRACEFUL_RESTART_NEW_STATE_HELPER);\
+ } while (0)
+
+#define BGP_PEER_GR_ENABLE(peer)\
+ do { \
+ SET_FLAG( \
+ peer->peer_gr_new_status_flag, \
+ PEER_GRACEFUL_RESTART_NEW_STATE_RESTART); \
+ UNSET_FLAG( \
+ peer->peer_gr_new_status_flag, \
+ PEER_GRACEFUL_RESTART_NEW_STATE_HELPER);\
+ } while (0)
+
+#define BGP_PEER_GR_DISABLE(peer)\
+ do { \
+ UNSET_FLAG( \
+ peer->peer_gr_new_status_flag, \
+ PEER_GRACEFUL_RESTART_NEW_STATE_RESTART);\
+ UNSET_FLAG(\
+ peer->peer_gr_new_status_flag, \
+ PEER_GRACEFUL_RESTART_NEW_STATE_HELPER);\
+ } while (0)
+
+#define BGP_PEER_GR_GLOBAL_INHERIT_SET(peer) \
+ SET_FLAG(peer->peer_gr_new_status_flag, \
+ PEER_GRACEFUL_RESTART_NEW_STATE_INHERIT)
+
+#define BGP_PEER_GR_GLOBAL_INHERIT_UNSET(peer) \
+ UNSET_FLAG(peer->peer_gr_new_status_flag, \
+ PEER_GRACEFUL_RESTART_NEW_STATE_INHERIT)
+
+#define BGP_PEER_GRACEFUL_RESTART_CAPABLE(peer) \
+ (CHECK_FLAG(peer->cap, PEER_CAP_RESTART_ADV) \
+ && CHECK_FLAG(peer->cap, PEER_CAP_RESTART_RCV))
+
+#define BGP_PEER_RESTARTING_MODE(peer) \
+ (CHECK_FLAG(peer->flags, PEER_FLAG_GRACEFUL_RESTART) && \
+ CHECK_FLAG(peer->cap, PEER_CAP_GRACEFUL_RESTART_R_BIT_ADV) && \
+ !CHECK_FLAG(peer->cap, PEER_CAP_GRACEFUL_RESTART_R_BIT_RCV))
+
+#define BGP_PEER_HELPER_MODE(peer) \
+ (CHECK_FLAG(peer->flags, PEER_FLAG_GRACEFUL_RESTART_HELPER) && \
+ CHECK_FLAG(peer->cap, PEER_CAP_GRACEFUL_RESTART_R_BIT_RCV) && \
+ !CHECK_FLAG(peer->cap, PEER_CAP_GRACEFUL_RESTART_R_BIT_ADV))
+
+/* Prototypes. */
+
+/*
+ * Update FSM for peer based on whether we have valid nexthops or not.
+ */
+extern void bgp_fsm_nht_update(struct peer *peer, bool has_valid_nexthops);
+extern void bgp_event(struct thread *);
+extern int bgp_event_update(struct peer *, enum bgp_fsm_events event);
+extern int bgp_stop(struct peer *peer);
+extern void bgp_timer_set(struct peer *);
+extern void bgp_routeadv_timer(struct thread *);
+extern void bgp_fsm_change_status(struct peer *peer, int status);
+extern const char *const peer_down_str[];
+extern void bgp_update_delay_end(struct bgp *);
+extern void bgp_maxmed_update(struct bgp *);
+extern bool bgp_maxmed_onstartup_configured(struct bgp *);
+extern bool bgp_maxmed_onstartup_active(struct bgp *);
+extern int bgp_fsm_error_subcode(int status);
+
+/**
+ * Start the route advertisement timer (that honors MRAI) for all the
+ * peers. Typically called at the end of initial convergence, coming
+ * out of read-only mode.
+ */
+extern void bgp_start_routeadv(struct bgp *);
+
+/**
+ * See if the route advertisement timer needs to be adjusted for a
+ * peer. For example, if the last update was written to the peer a
+ * long while back, we don't need to wait for the periodic advertisement
+ * timer to expire to send the new set of prefixes. It should fire
+ * instantly and updates should go out sooner.
+ */
+extern void bgp_adjust_routeadv(struct peer *);
+
+#include "hook.h"
+DECLARE_HOOK(peer_backward_transition, (struct peer *peer), (peer));
+DECLARE_HOOK(peer_established, (struct peer *peer), (peer));
+
+int bgp_gr_update_all(struct bgp *bgp, int global_gr_cmd);
+int bgp_neighbor_graceful_restart(struct peer *peer, int peer_gr_cmd);
+unsigned int bgp_peer_gr_action(struct peer *peer,
+ int old_peer_state, int new_peer_state);
+void bgp_peer_move_to_gr_mode(struct peer *peer, int new_state);
+unsigned int bgp_peer_gr_helper_enable(struct peer *peer);
+unsigned int bgp_peer_gr_enable(struct peer *peer);
+unsigned int bgp_peer_gr_global_inherit(struct peer *peer);
+unsigned int bgp_peer_gr_disable(struct peer *peer);
+enum peer_mode bgp_peer_gr_mode_get(struct peer *peer);
+enum global_mode bgp_global_gr_mode_get(struct bgp *bgp);
+enum peer_mode bgp_get_peer_gr_mode_from_flags(struct peer *peer);
+unsigned int bgp_peer_gr_global_inherit_unset(struct peer *peer);
+int bgp_gr_lookup_n_update_all_peer(struct bgp *bgp,
+ enum global_mode global_new_state,
+ enum global_mode global_old_state);
+void bgp_peer_gr_flags_update(struct peer *peer);
+const char *print_peer_gr_mode(enum peer_mode pr_mode);
+const char *print_peer_gr_cmd(enum peer_gr_command pr_gr_cmd);
+const char *print_global_gr_mode(enum global_mode gl_mode);
+const char *print_global_gr_cmd(enum global_gr_command gl_gr_cmd);
+int bgp_peer_reg_with_nht(struct peer *peer);
+#endif /* _QUAGGA_BGP_FSM_H */
diff --git a/bgpd/bgp_io.c b/bgpd/bgp_io.c
new file mode 100644
index 0000000..f9bb8d5
--- /dev/null
+++ b/bgpd/bgp_io.c
@@ -0,0 +1,583 @@
+/* BGP I/O.
+ * Implements packet I/O in a pthread.
+ * Copyright (C) 2017 Cumulus Networks
+ * Quentin Young
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; see the file COPYING; if not, write to the
+ * Free Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston,
+ * MA 02110-1301 USA
+ */
+
+/* clang-format off */
+#include <zebra.h>
+#include <pthread.h> // for pthread_mutex_unlock, pthread_mutex_lock
+#include <sys/uio.h> // for writev
+
+#include "frr_pthread.h"
+#include "linklist.h" // for list_delete, list_delete_all_node, lis...
+#include "log.h" // for zlog_debug, safe_strerror, zlog_err
+#include "memory.h" // for MTYPE_TMP, XCALLOC, XFREE
+#include "network.h" // for ERRNO_IO_RETRY
+#include "stream.h" // for stream_get_endp, stream_getw_from, str...
+#include "ringbuf.h" // for ringbuf_remain, ringbuf_peek, ringbuf_...
+#include "thread.h" // for THREAD_OFF, THREAD_ARG, thread...
+
+#include "bgpd/bgp_io.h"
+#include "bgpd/bgp_debug.h" // for bgp_debug_neighbor_events, bgp_type_str
+#include "bgpd/bgp_errors.h" // for expanded error reference information
+#include "bgpd/bgp_fsm.h" // for BGP_EVENT_ADD, bgp_event
+#include "bgpd/bgp_packet.h" // for bgp_notify_io_invalid...
+#include "bgpd/bgp_trace.h" // for frrtraces
+#include "bgpd/bgpd.h" // for peer, BGP_MARKER_SIZE, bgp_master, bm
+/* clang-format on */
+
+/* forward declarations */
+static uint16_t bgp_write(struct peer *);
+static uint16_t bgp_read(struct peer *peer, int *code_p);
+static void bgp_process_writes(struct thread *);
+static void bgp_process_reads(struct thread *);
+static bool validate_header(struct peer *);
+
+/* generic i/o status codes */
+#define BGP_IO_TRANS_ERR (1 << 0) // EAGAIN or similar occurred
+#define BGP_IO_FATAL_ERR (1 << 1) // some kind of fatal TCP error
+
+/* Thread external API ----------------------------------------------------- */
+
+void bgp_writes_on(struct peer *peer)
+{
+ struct frr_pthread *fpt = bgp_pth_io;
+ assert(fpt->running);
+
+ assert(peer->status != Deleted);
+ assert(peer->obuf);
+ assert(peer->ibuf);
+ assert(peer->ibuf_work);
+ assert(!peer->t_connect_check_r);
+ assert(!peer->t_connect_check_w);
+ assert(peer->fd);
+
+ thread_add_write(fpt->master, bgp_process_writes, peer, peer->fd,
+ &peer->t_write);
+ SET_FLAG(peer->thread_flags, PEER_THREAD_WRITES_ON);
+}
+
+void bgp_writes_off(struct peer *peer)
+{
+ struct frr_pthread *fpt = bgp_pth_io;
+ assert(fpt->running);
+
+ thread_cancel_async(fpt->master, &peer->t_write, NULL);
+ THREAD_OFF(peer->t_generate_updgrp_packets);
+
+ UNSET_FLAG(peer->thread_flags, PEER_THREAD_WRITES_ON);
+}
+
+void bgp_reads_on(struct peer *peer)
+{
+ struct frr_pthread *fpt = bgp_pth_io;
+ assert(fpt->running);
+
+ assert(peer->status != Deleted);
+ assert(peer->ibuf);
+ assert(peer->fd);
+ assert(peer->ibuf_work);
+ assert(peer->obuf);
+ assert(!peer->t_connect_check_r);
+ assert(!peer->t_connect_check_w);
+ assert(peer->fd);
+
+ thread_add_read(fpt->master, bgp_process_reads, peer, peer->fd,
+ &peer->t_read);
+
+ SET_FLAG(peer->thread_flags, PEER_THREAD_READS_ON);
+}
+
+void bgp_reads_off(struct peer *peer)
+{
+ struct frr_pthread *fpt = bgp_pth_io;
+ assert(fpt->running);
+
+ thread_cancel_async(fpt->master, &peer->t_read, NULL);
+ THREAD_OFF(peer->t_process_packet);
+ THREAD_OFF(peer->t_process_packet_error);
+
+ UNSET_FLAG(peer->thread_flags, PEER_THREAD_READS_ON);
+}
+
+/* Thread internal functions ----------------------------------------------- */
+
+/*
+ * Called from I/O pthread when a file descriptor has become ready for writing.
+ */
+static void bgp_process_writes(struct thread *thread)
+{
+ static struct peer *peer;
+ peer = THREAD_ARG(thread);
+ uint16_t status;
+ bool reschedule;
+ bool fatal = false;
+
+ if (peer->fd < 0)
+ return;
+
+ struct frr_pthread *fpt = bgp_pth_io;
+
+ frr_with_mutex (&peer->io_mtx) {
+ status = bgp_write(peer);
+ reschedule = (stream_fifo_head(peer->obuf) != NULL);
+ }
+
+ /* no problem */
+ if (CHECK_FLAG(status, BGP_IO_TRANS_ERR)) {
+ }
+
+ /* problem */
+ if (CHECK_FLAG(status, BGP_IO_FATAL_ERR)) {
+ reschedule = false;
+ fatal = true;
+ }
+
+ /* If suppress fib pending is enabled, route is advertised to peers when
+ * the status is received from the FIB. The delay is added
+ * to update group packet generate which will allow more routes to be
+ * sent in the update message
+ */
+ if (reschedule) {
+ thread_add_write(fpt->master, bgp_process_writes, peer,
+ peer->fd, &peer->t_write);
+ } else if (!fatal) {
+ BGP_UPDATE_GROUP_TIMER_ON(&peer->t_generate_updgrp_packets,
+ bgp_generate_updgrp_packets);
+ }
+}
+
+/*
+ * Called from I/O pthread when a file descriptor has become ready for reading,
+ * or has hung up.
+ *
+ * We read as much data as possible, process as many packets as we can and
+ * place them on peer->ibuf for secondary processing by the main thread.
+ */
+static void bgp_process_reads(struct thread *thread)
+{
+ /* clang-format off */
+ static struct peer *peer; // peer to read from
+ uint16_t status; // bgp_read status code
+ bool more = true; // whether we got more data
+ bool fatal = false; // whether fatal error occurred
+ bool added_pkt = false; // whether we pushed onto ->ibuf
+ int code = 0; // FSM code if error occurred
+ /* clang-format on */
+
+ peer = THREAD_ARG(thread);
+
+ if (peer->fd < 0 || bm->terminating)
+ return;
+
+ struct frr_pthread *fpt = bgp_pth_io;
+
+ frr_with_mutex (&peer->io_mtx) {
+ status = bgp_read(peer, &code);
+ }
+
+ /* error checking phase */
+ if (CHECK_FLAG(status, BGP_IO_TRANS_ERR)) {
+ /* no problem; just don't process packets */
+ more = false;
+ }
+
+ if (CHECK_FLAG(status, BGP_IO_FATAL_ERR)) {
+ /* problem; tear down session */
+ more = false;
+ fatal = true;
+
+ /* Handle the error in the main pthread, include the
+ * specific state change from 'bgp_read'.
+ */
+ thread_add_event(bm->master, bgp_packet_process_error,
+ peer, code, &peer->t_process_packet_error);
+ }
+
+ while (more) {
+ /* static buffer for transferring packets */
+ /* shorter alias to peer's input buffer */
+ struct ringbuf *ibw = peer->ibuf_work;
+ /* packet size as given by header */
+ uint16_t pktsize = 0;
+
+ /* check that we have enough data for a header */
+ if (ringbuf_remain(ibw) < BGP_HEADER_SIZE)
+ break;
+
+ /* check that header is valid */
+ if (!validate_header(peer)) {
+ fatal = true;
+ break;
+ }
+
+ /* header is valid; retrieve packet size */
+ ringbuf_peek(ibw, BGP_MARKER_SIZE, &pktsize, sizeof(pktsize));
+
+ pktsize = ntohs(pktsize);
+
+ /* if this fails we are seriously screwed */
+ assert(pktsize <= peer->max_packet_size);
+
+ /*
+ * If we have that much data, chuck it into its own
+ * stream and append to input queue for processing.
+ */
+ if (ringbuf_remain(ibw) >= pktsize) {
+ struct stream *pkt = stream_new(pktsize);
+
+ assert(STREAM_WRITEABLE(pkt) == pktsize);
+ assert(ringbuf_get(ibw, pkt->data, pktsize) == pktsize);
+ stream_set_endp(pkt, pktsize);
+
+ frrtrace(2, frr_bgp, packet_read, peer, pkt);
+ frr_with_mutex (&peer->io_mtx) {
+ stream_fifo_push(peer->ibuf, pkt);
+ }
+
+ added_pkt = true;
+ } else
+ break;
+ }
+
+ /* handle invalid header */
+ if (fatal) {
+ /* wipe buffer just in case someone screwed up */
+ ringbuf_wipe(peer->ibuf_work);
+ } else {
+ assert(ringbuf_space(peer->ibuf_work) >= peer->max_packet_size);
+
+ thread_add_read(fpt->master, bgp_process_reads, peer, peer->fd,
+ &peer->t_read);
+ if (added_pkt)
+ thread_add_event(bm->master, bgp_process_packet,
+ peer, 0, &peer->t_process_packet);
+ }
+}
+
+/*
+ * Flush peer output buffer.
+ *
+ * This function pops packets off of peer->obuf and writes them to peer->fd.
+ * The amount of packets written is equal to the minimum of peer->wpkt_quanta
+ * and the number of packets on the output buffer, unless an error occurs.
+ *
+ * If write() returns an error, the appropriate FSM event is generated.
+ *
+ * The return value is equal to the number of packets written
+ * (which may be zero).
+ */
+static uint16_t bgp_write(struct peer *peer)
+{
+ uint8_t type;
+ struct stream *s;
+ int update_last_write = 0;
+ unsigned int count;
+ uint32_t uo = 0;
+ uint16_t status = 0;
+ uint32_t wpkt_quanta_old;
+
+ int writenum = 0;
+ int num;
+ unsigned int iovsz;
+ unsigned int strmsz;
+ unsigned int total_written;
+ time_t now;
+
+ wpkt_quanta_old = atomic_load_explicit(&peer->bgp->wpkt_quanta,
+ memory_order_relaxed);
+ struct stream *ostreams[wpkt_quanta_old];
+ struct stream **streams = ostreams;
+ struct iovec iov[wpkt_quanta_old];
+
+ s = stream_fifo_head(peer->obuf);
+
+ if (!s)
+ goto done;
+
+ count = iovsz = 0;
+ while (count < wpkt_quanta_old && iovsz < array_size(iov) && s) {
+ ostreams[iovsz] = s;
+ iov[iovsz].iov_base = stream_pnt(s);
+ iov[iovsz].iov_len = STREAM_READABLE(s);
+ writenum += STREAM_READABLE(s);
+ s = s->next;
+ ++iovsz;
+ ++count;
+ }
+
+ strmsz = iovsz;
+ total_written = 0;
+
+ do {
+ num = writev(peer->fd, iov, iovsz);
+
+ if (num < 0) {
+ if (!ERRNO_IO_RETRY(errno)) {
+ BGP_EVENT_ADD(peer, TCP_fatal_error);
+ SET_FLAG(status, BGP_IO_FATAL_ERR);
+ } else {
+ SET_FLAG(status, BGP_IO_TRANS_ERR);
+ }
+
+ break;
+ } else if (num != writenum) {
+ unsigned int msg_written = 0;
+ unsigned int ic = iovsz;
+
+ for (unsigned int i = 0; i < ic; i++) {
+ size_t ss = iov[i].iov_len;
+
+ if (ss > (unsigned int) num)
+ break;
+
+ msg_written++;
+ iovsz--;
+ writenum -= ss;
+ num -= ss;
+ }
+
+ total_written += msg_written;
+
+ assert(total_written < count);
+
+ memmove(&iov, &iov[msg_written],
+ sizeof(iov[0]) * iovsz);
+ streams = &streams[msg_written];
+ stream_forward_getp(streams[0], num);
+ iov[0].iov_base = stream_pnt(streams[0]);
+ iov[0].iov_len = STREAM_READABLE(streams[0]);
+
+ writenum -= num;
+ num = 0;
+ assert(writenum > 0);
+ } else {
+ total_written = strmsz;
+ }
+
+ } while (num != writenum);
+
+ /* Handle statistics */
+ for (unsigned int i = 0; i < total_written; i++) {
+ s = stream_fifo_pop(peer->obuf);
+
+ assert(s == ostreams[i]);
+
+ /* Retrieve BGP packet type. */
+ stream_set_getp(s, BGP_MARKER_SIZE + 2);
+ type = stream_getc(s);
+
+ switch (type) {
+ case BGP_MSG_OPEN:
+ atomic_fetch_add_explicit(&peer->open_out, 1,
+ memory_order_relaxed);
+ break;
+ case BGP_MSG_UPDATE:
+ atomic_fetch_add_explicit(&peer->update_out, 1,
+ memory_order_relaxed);
+ uo++;
+ break;
+ case BGP_MSG_NOTIFY:
+ atomic_fetch_add_explicit(&peer->notify_out, 1,
+ memory_order_relaxed);
+ /* Double start timer. */
+ peer->v_start *= 2;
+
+ /* Overflow check. */
+ if (peer->v_start >= (60 * 2))
+ peer->v_start = (60 * 2);
+
+ /*
+ * Handle Graceful Restart case where the state changes
+ * to Connect instead of Idle.
+ */
+ BGP_EVENT_ADD(peer, BGP_Stop);
+ goto done;
+
+ case BGP_MSG_KEEPALIVE:
+ atomic_fetch_add_explicit(&peer->keepalive_out, 1,
+ memory_order_relaxed);
+ break;
+ case BGP_MSG_ROUTE_REFRESH_NEW:
+ case BGP_MSG_ROUTE_REFRESH_OLD:
+ atomic_fetch_add_explicit(&peer->refresh_out, 1,
+ memory_order_relaxed);
+ break;
+ case BGP_MSG_CAPABILITY:
+ atomic_fetch_add_explicit(&peer->dynamic_cap_out, 1,
+ memory_order_relaxed);
+ break;
+ }
+
+ stream_free(s);
+ ostreams[i] = NULL;
+ update_last_write = 1;
+ }
+
+done : {
+ now = monotime(NULL);
+ /*
+ * Update last_update if UPDATEs were written.
+ * Note: that these are only updated at end,
+ * not per message (i.e., per loop)
+ */
+ if (uo)
+ atomic_store_explicit(&peer->last_update, now,
+ memory_order_relaxed);
+
+ /* If we TXed any flavor of packet */
+ if (update_last_write) {
+ atomic_store_explicit(&peer->last_write, now,
+ memory_order_relaxed);
+ peer->last_sendq_ok = now;
+ }
+}
+
+ return status;
+}
+
+/*
+ * Reads a chunk of data from peer->fd into peer->ibuf_work.
+ *
+ * code_p
+ * Pointer to location to store FSM event code in case of fatal error.
+ *
+ * @return status flag (see top-of-file)
+ */
+static uint16_t bgp_read(struct peer *peer, int *code_p)
+{
+ size_t readsize; // how many bytes we want to read
+ ssize_t nbytes; // how many bytes we actually read
+ uint16_t status = 0;
+
+ readsize =
+ MIN(ringbuf_space(peer->ibuf_work), sizeof(peer->ibuf_scratch));
+ nbytes = read(peer->fd, peer->ibuf_scratch, readsize);
+
+ /* EAGAIN or EWOULDBLOCK; come back later */
+ if (nbytes < 0 && ERRNO_IO_RETRY(errno)) {
+ SET_FLAG(status, BGP_IO_TRANS_ERR);
+ } else if (nbytes < 0) {
+ /* Fatal error; tear down session */
+ flog_err(EC_BGP_UPDATE_RCV,
+ "%s [Error] bgp_read_packet error: %s", peer->host,
+ safe_strerror(errno));
+
+ /* Handle the error in the main pthread. */
+ if (code_p)
+ *code_p = TCP_fatal_error;
+
+ SET_FLAG(status, BGP_IO_FATAL_ERR);
+
+ } else if (nbytes == 0) {
+ /* Received EOF / TCP session closed */
+ if (bgp_debug_neighbor_events(peer))
+ zlog_debug("%s [Event] BGP connection closed fd %d",
+ peer->host, peer->fd);
+
+ /* Handle the error in the main pthread. */
+ if (code_p)
+ *code_p = TCP_connection_closed;
+
+ SET_FLAG(status, BGP_IO_FATAL_ERR);
+ } else {
+ assert(ringbuf_put(peer->ibuf_work, peer->ibuf_scratch, nbytes)
+ == (size_t)nbytes);
+ }
+
+ return status;
+}
+
+/*
+ * Called after we have read a BGP packet header. Validates marker, message
+ * type and packet length. If any of these aren't correct, sends a notify.
+ *
+ * Assumes that there are at least BGP_HEADER_SIZE readable bytes in the input
+ * buffer.
+ */
+static bool validate_header(struct peer *peer)
+{
+ uint16_t size;
+ uint8_t type;
+ struct ringbuf *pkt = peer->ibuf_work;
+
+ static const uint8_t m_correct[BGP_MARKER_SIZE] = {
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff};
+ uint8_t m_rx[BGP_MARKER_SIZE] = {0x00};
+
+ if (ringbuf_peek(pkt, 0, m_rx, BGP_MARKER_SIZE) != BGP_MARKER_SIZE)
+ return false;
+
+ if (memcmp(m_correct, m_rx, BGP_MARKER_SIZE) != 0) {
+ bgp_notify_io_invalid(peer, BGP_NOTIFY_HEADER_ERR,
+ BGP_NOTIFY_HEADER_NOT_SYNC, NULL, 0);
+ return false;
+ }
+
+ /* Get size and type in network byte order. */
+ ringbuf_peek(pkt, BGP_MARKER_SIZE, &size, sizeof(size));
+ ringbuf_peek(pkt, BGP_MARKER_SIZE + 2, &type, sizeof(type));
+
+ size = ntohs(size);
+
+ /* BGP type check. */
+ if (type != BGP_MSG_OPEN && type != BGP_MSG_UPDATE
+ && type != BGP_MSG_NOTIFY && type != BGP_MSG_KEEPALIVE
+ && type != BGP_MSG_ROUTE_REFRESH_NEW
+ && type != BGP_MSG_ROUTE_REFRESH_OLD
+ && type != BGP_MSG_CAPABILITY) {
+ if (bgp_debug_neighbor_events(peer))
+ zlog_debug("%s unknown message type 0x%02x", peer->host,
+ type);
+
+ bgp_notify_io_invalid(peer, BGP_NOTIFY_HEADER_ERR,
+ BGP_NOTIFY_HEADER_BAD_MESTYPE, &type, 1);
+ return false;
+ }
+
+ /* Minimum packet length check. */
+ if ((size < BGP_HEADER_SIZE) || (size > peer->max_packet_size)
+ || (type == BGP_MSG_OPEN && size < BGP_MSG_OPEN_MIN_SIZE)
+ || (type == BGP_MSG_UPDATE && size < BGP_MSG_UPDATE_MIN_SIZE)
+ || (type == BGP_MSG_NOTIFY && size < BGP_MSG_NOTIFY_MIN_SIZE)
+ || (type == BGP_MSG_KEEPALIVE && size != BGP_MSG_KEEPALIVE_MIN_SIZE)
+ || (type == BGP_MSG_ROUTE_REFRESH_NEW
+ && size < BGP_MSG_ROUTE_REFRESH_MIN_SIZE)
+ || (type == BGP_MSG_ROUTE_REFRESH_OLD
+ && size < BGP_MSG_ROUTE_REFRESH_MIN_SIZE)
+ || (type == BGP_MSG_CAPABILITY
+ && size < BGP_MSG_CAPABILITY_MIN_SIZE)) {
+ if (bgp_debug_neighbor_events(peer)) {
+ zlog_debug("%s bad message length - %d for %s",
+ peer->host, size,
+ type == 128 ? "ROUTE-REFRESH"
+ : bgp_type_str[(int)type]);
+ }
+
+ uint16_t nsize = htons(size);
+
+ bgp_notify_io_invalid(peer, BGP_NOTIFY_HEADER_ERR,
+ BGP_NOTIFY_HEADER_BAD_MESLEN,
+ (unsigned char *)&nsize, 2);
+ return false;
+ }
+
+ return true;
+}
diff --git a/bgpd/bgp_io.h b/bgpd/bgp_io.h
new file mode 100644
index 0000000..75d014f
--- /dev/null
+++ b/bgpd/bgp_io.h
@@ -0,0 +1,103 @@
+/* BGP I/O.
+ * Implements packet I/O in a pthread.
+ * Copyright (C) 2017 Cumulus Networks
+ * Quentin Young
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; see the file COPYING; if not, write to the
+ * Free Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston,
+ * MA 02110-1301 USA
+ */
+
+#ifndef _FRR_BGP_IO_H
+#define _FRR_BGP_IO_H
+
+#define BGP_WRITE_PACKET_MAX 64U
+#define BGP_READ_PACKET_MAX 10U
+
+#include "bgpd/bgpd.h"
+#include "frr_pthread.h"
+
+/**
+ * Start function for write thread.
+ *
+ * @param arg - unused
+ */
+extern void *bgp_io_start(void *arg);
+
+/**
+ * Start function for write thread.
+ *
+ * Uninitializes all resources and stops the thread.
+ *
+ * @param result - where to store data result, unused
+ */
+extern int bgp_io_stop(void **result, struct frr_pthread *fpt);
+
+/**
+ * Turns on packet writing for a peer.
+ *
+ * After this function is called, any packets placed on peer->obuf will be
+ * written to peer->fd until no more packets remain.
+ *
+ * Additionally, it becomes unsafe to perform socket actions on peer->fd.
+ *
+ * @param peer - peer to register
+ */
+extern void bgp_writes_on(struct peer *peer);
+
+/**
+ * Turns off packet writing for a peer.
+ *
+ * After this function returns, packets placed on peer->obuf will not be
+ * written to peer->fd by the I/O thread.
+ *
+ * After this function returns it becomes safe to perform socket actions on
+ * peer->fd.
+ *
+ * @param peer - peer to deregister
+ * @param flush - as described
+ */
+extern void bgp_writes_off(struct peer *peer);
+
+/**
+ * Turns on packet reading for a peer.
+ *
+ * After this function is called, any packets received on peer->fd will be read
+ * and copied into the FIFO queue peer->ibuf.
+ *
+ * Additionally, it becomes unsafe to perform socket actions on peer->fd.
+ *
+ * Whenever one or more packets are placed onto peer->ibuf, a task of type
+ * THREAD_EVENT will be placed on the main thread whose handler is
+ *
+ * bgp_packet.c:bgp_process_packet()
+ *
+ * @param peer - peer to register
+ */
+extern void bgp_reads_on(struct peer *peer);
+
+/**
+ * Turns off packet reading for a peer.
+ *
+ * After this function is called, any packets received on peer->fd will not be
+ * read by the I/O thread.
+ *
+ * After this function returns it becomes safe to perform socket actions on
+ * peer->fd.
+ *
+ * @param peer - peer to deregister
+ */
+extern void bgp_reads_off(struct peer *peer);
+
+#endif /* _FRR_BGP_IO_H */
diff --git a/bgpd/bgp_keepalives.c b/bgpd/bgp_keepalives.c
new file mode 100644
index 0000000..aeb9171
--- /dev/null
+++ b/bgpd/bgp_keepalives.c
@@ -0,0 +1,323 @@
+/* BGP Keepalives.
+ * Implements a producer thread to generate BGP keepalives for peers.
+ * Copyright (C) 2017 Cumulus Networks, Inc.
+ * Quentin Young
+ *
+ * This file is part of FRRouting.
+ *
+ * FRRouting is free software; you can redistribute it and/or modify it under
+ * the terms of the GNU General Public License as published by the Free
+ * Software Foundation; either version 2, or (at your option) any later
+ * version.
+ *
+ * FRRouting is distributed in the hope that it will be useful, but WITHOUT ANY
+ * WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
+ * FOR A PARTICULAR PURPOSE. See the GNU General Public License for more
+ * details.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program; see the file COPYING; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
+ */
+
+/* clang-format off */
+#include <zebra.h>
+#include <pthread.h> // for pthread_mutex_lock, pthread_mutex_unlock
+
+#include "frr_pthread.h" // for frr_pthread
+#include "hash.h" // for hash, hash_clean, hash_create_size...
+#include "log.h" // for zlog_debug
+#include "memory.h" // for MTYPE_TMP, XFREE, XCALLOC, XMALLOC
+#include "monotime.h" // for monotime, monotime_since
+
+#include "bgpd/bgpd.h" // for peer, PEER_THREAD_KEEPALIVES_ON, peer...
+#include "bgpd/bgp_debug.h" // for bgp_debug_neighbor_events
+#include "bgpd/bgp_packet.h" // for bgp_keepalive_send
+#include "bgpd/bgp_keepalives.h"
+/* clang-format on */
+
+DEFINE_MTYPE_STATIC(BGPD, BGP_PKAT, "Peer KeepAlive Timer");
+DEFINE_MTYPE_STATIC(BGPD, BGP_COND, "BGP Peer pthread Conditional");
+DEFINE_MTYPE_STATIC(BGPD, BGP_MUTEX, "BGP Peer pthread Mutex");
+
+/*
+ * Peer KeepAlive Timer.
+ * Associates a peer with the time of its last keepalive.
+ */
+struct pkat {
+ /* the peer to send keepalives to */
+ struct peer *peer;
+ /* absolute time of last keepalive sent */
+ struct timeval last;
+};
+
+/* List of peers we are sending keepalives for, and associated mutex. */
+static pthread_mutex_t *peerhash_mtx;
+static pthread_cond_t *peerhash_cond;
+static struct hash *peerhash;
+
+static struct pkat *pkat_new(struct peer *peer)
+{
+ struct pkat *pkat = XMALLOC(MTYPE_BGP_PKAT, sizeof(struct pkat));
+ pkat->peer = peer;
+ monotime(&pkat->last);
+ return pkat;
+}
+
+static void pkat_del(void *pkat)
+{
+ XFREE(MTYPE_BGP_PKAT, pkat);
+}
+
+
+/*
+ * Callback for hash_iterate. Determines if a peer needs a keepalive and if so,
+ * generates and sends it.
+ *
+ * For any given peer, if the elapsed time since its last keepalive exceeds its
+ * configured keepalive timer, a keepalive is sent to the peer and its
+ * last-sent time is reset. Additionally, If the elapsed time does not exceed
+ * the configured keepalive timer, but the time until the next keepalive is due
+ * is within a hardcoded tolerance, a keepalive is sent as if the configured
+ * timer was exceeded. Doing this helps alleviate nanosecond sleeps between
+ * ticks by grouping together peers who are due for keepalives at roughly the
+ * same time. This tolerance value is arbitrarily chosen to be 100ms.
+ *
+ * In addition, this function calculates the maximum amount of time that the
+ * keepalive thread can sleep before another tick needs to take place. This is
+ * equivalent to shortest time until a keepalive is due for any one peer.
+ *
+ * @return maximum time to wait until next update (0 if infinity)
+ */
+static void peer_process(struct hash_bucket *hb, void *arg)
+{
+ struct pkat *pkat = hb->data;
+
+ struct timeval *next_update = arg;
+
+ static struct timeval elapsed; // elapsed time since keepalive
+ static struct timeval ka = {0}; // peer->v_keepalive as a timeval
+ static struct timeval diff; // ka - elapsed
+
+ static const struct timeval tolerance = {0, 100000};
+
+ uint32_t v_ka = atomic_load_explicit(&pkat->peer->v_keepalive,
+ memory_order_relaxed);
+
+ /* 0 keepalive timer means no keepalives */
+ if (v_ka == 0)
+ return;
+
+ /* calculate elapsed time since last keepalive */
+ monotime_since(&pkat->last, &elapsed);
+
+ /* calculate difference between elapsed time and configured time */
+ ka.tv_sec = v_ka;
+ timersub(&ka, &elapsed, &diff);
+
+ int send_keepalive =
+ elapsed.tv_sec >= ka.tv_sec || timercmp(&diff, &tolerance, <);
+
+ if (send_keepalive) {
+ if (bgp_debug_neighbor_events(pkat->peer))
+ zlog_debug("%s [FSM] Timer (keepalive timer expire)",
+ pkat->peer->host);
+
+ bgp_keepalive_send(pkat->peer);
+ monotime(&pkat->last);
+ memset(&elapsed, 0, sizeof(elapsed));
+ diff = ka;
+ }
+
+ /* if calculated next update for this peer < current delay, use it */
+ if (next_update->tv_sec < 0 || timercmp(&diff, next_update, <))
+ *next_update = diff;
+}
+
+static bool peer_hash_cmp(const void *f, const void *s)
+{
+ const struct pkat *p1 = f;
+ const struct pkat *p2 = s;
+
+ return p1->peer == p2->peer;
+}
+
+static unsigned int peer_hash_key(const void *arg)
+{
+ const struct pkat *pkat = arg;
+ return (uintptr_t)pkat->peer;
+}
+
+/* Cleanup handler / deinitializer. */
+static void bgp_keepalives_finish(void *arg)
+{
+ if (peerhash) {
+ hash_clean(peerhash, pkat_del);
+ hash_free(peerhash);
+ }
+
+ peerhash = NULL;
+
+ pthread_mutex_unlock(peerhash_mtx);
+ pthread_mutex_destroy(peerhash_mtx);
+ pthread_cond_destroy(peerhash_cond);
+
+ XFREE(MTYPE_BGP_MUTEX, peerhash_mtx);
+ XFREE(MTYPE_BGP_COND, peerhash_cond);
+}
+
+/*
+ * Entry function for peer keepalive generation pthread.
+ */
+void *bgp_keepalives_start(void *arg)
+{
+ struct frr_pthread *fpt = arg;
+ fpt->master->owner = pthread_self();
+
+ struct timeval currtime = {0, 0};
+ struct timeval aftertime = {0, 0};
+ struct timeval next_update = {0, 0};
+ struct timespec next_update_ts = {0, 0};
+
+ /*
+ * The RCU mechanism for each pthread is initialized in a "locked"
+ * state. That's ok for pthreads using the frr_pthread,
+ * thread_fetch event loop, because that event loop unlocks regularly.
+ * For foreign pthreads, the lock needs to be unlocked so that the
+ * background rcu pthread can run.
+ */
+ rcu_read_unlock();
+
+ peerhash_mtx = XCALLOC(MTYPE_BGP_MUTEX, sizeof(pthread_mutex_t));
+ peerhash_cond = XCALLOC(MTYPE_BGP_COND, sizeof(pthread_cond_t));
+
+ /* initialize mutex */
+ pthread_mutex_init(peerhash_mtx, NULL);
+
+ /* use monotonic clock with condition variable */
+ pthread_condattr_t attrs;
+ pthread_condattr_init(&attrs);
+ pthread_condattr_setclock(&attrs, CLOCK_MONOTONIC);
+ pthread_cond_init(peerhash_cond, &attrs);
+ pthread_condattr_destroy(&attrs);
+
+ /*
+ * We are not using normal FRR pthread mechanics and are
+ * not using fpt_run
+ */
+ frr_pthread_set_name(fpt);
+
+ /* initialize peer hashtable */
+ peerhash = hash_create_size(2048, peer_hash_key, peer_hash_cmp, NULL);
+ pthread_mutex_lock(peerhash_mtx);
+
+ /* register cleanup handler */
+ pthread_cleanup_push(&bgp_keepalives_finish, NULL);
+
+ /* notify anybody waiting on us that we are done starting up */
+ frr_pthread_notify_running(fpt);
+
+ while (atomic_load_explicit(&fpt->running, memory_order_relaxed)) {
+ if (peerhash->count > 0)
+ pthread_cond_timedwait(peerhash_cond, peerhash_mtx,
+ &next_update_ts);
+ else
+ while (peerhash->count == 0
+ && atomic_load_explicit(&fpt->running,
+ memory_order_relaxed))
+ pthread_cond_wait(peerhash_cond, peerhash_mtx);
+
+ monotime(&currtime);
+
+ next_update.tv_sec = -1;
+
+ hash_iterate(peerhash, peer_process, &next_update);
+ if (next_update.tv_sec == -1)
+ memset(&next_update, 0, sizeof(next_update));
+
+ monotime_since(&currtime, &aftertime);
+
+ timeradd(&currtime, &next_update, &next_update);
+ TIMEVAL_TO_TIMESPEC(&next_update, &next_update_ts);
+ }
+
+ /* clean up */
+ pthread_cleanup_pop(1);
+
+ return NULL;
+}
+
+/* --- thread external functions ------------------------------------------- */
+
+void bgp_keepalives_on(struct peer *peer)
+{
+ if (CHECK_FLAG(peer->thread_flags, PEER_THREAD_KEEPALIVES_ON))
+ return;
+
+ struct frr_pthread *fpt = bgp_pth_ka;
+ assert(fpt->running);
+
+ /* placeholder bucket data to use for fast key lookups */
+ static struct pkat holder = {0};
+
+ /*
+ * We need to ensure that bgp_keepalives_init was called first
+ */
+ assert(peerhash_mtx);
+
+ frr_with_mutex (peerhash_mtx) {
+ holder.peer = peer;
+ if (!hash_lookup(peerhash, &holder)) {
+ struct pkat *pkat = pkat_new(peer);
+ (void)hash_get(peerhash, pkat, hash_alloc_intern);
+ peer_lock(peer);
+ }
+ SET_FLAG(peer->thread_flags, PEER_THREAD_KEEPALIVES_ON);
+ }
+ bgp_keepalives_wake();
+}
+
+void bgp_keepalives_off(struct peer *peer)
+{
+ if (!CHECK_FLAG(peer->thread_flags, PEER_THREAD_KEEPALIVES_ON))
+ return;
+
+ struct frr_pthread *fpt = bgp_pth_ka;
+ assert(fpt->running);
+
+ /* placeholder bucket data to use for fast key lookups */
+ static struct pkat holder = {0};
+
+ /*
+ * We need to ensure that bgp_keepalives_init was called first
+ */
+ assert(peerhash_mtx);
+
+ frr_with_mutex (peerhash_mtx) {
+ holder.peer = peer;
+ struct pkat *res = hash_release(peerhash, &holder);
+ if (res) {
+ pkat_del(res);
+ peer_unlock(peer);
+ }
+ UNSET_FLAG(peer->thread_flags, PEER_THREAD_KEEPALIVES_ON);
+ }
+}
+
+void bgp_keepalives_wake(void)
+{
+ frr_with_mutex (peerhash_mtx) {
+ pthread_cond_signal(peerhash_cond);
+ }
+}
+
+int bgp_keepalives_stop(struct frr_pthread *fpt, void **result)
+{
+ assert(fpt->running);
+
+ atomic_store_explicit(&fpt->running, false, memory_order_relaxed);
+ bgp_keepalives_wake();
+
+ pthread_join(fpt->thread, result);
+ return 0;
+}
diff --git a/bgpd/bgp_keepalives.h b/bgpd/bgp_keepalives.h
new file mode 100644
index 0000000..d1cb7d2
--- /dev/null
+++ b/bgpd/bgp_keepalives.h
@@ -0,0 +1,93 @@
+/* BGP Keepalives.
+ * Implements a producer thread to generate BGP keepalives for peers.
+ * Copyright (C) 2017 Cumulus Networks, Inc.
+ * Quentin Young
+ *
+ * This file is part of FRRouting.
+ *
+ * FRRouting is free software; you can redistribute it and/or modify it under
+ * the terms of the GNU General Public License as published by the Free
+ * Software Foundation; either version 2, or (at your option) any later
+ * version.
+ *
+ * FRRouting is distributed in the hope that it will be useful, but WITHOUT ANY
+ * WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
+ * FOR A PARTICULAR PURPOSE. See the GNU General Public License for more
+ * details.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program; see the file COPYING; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
+ */
+
+#ifndef _FRR_BGP_KEEPALIVES_H
+#define _FRR_BGP_KEEPALIVES_H
+
+#include "frr_pthread.h"
+#include "bgpd.h"
+
+/**
+ * Turns on keepalives for a peer.
+ *
+ * This function adds the peer to an internal list of peers to generate
+ * keepalives for.
+ *
+ * At set intervals, a BGP KEEPALIVE packet is generated and placed on
+ * peer->obuf. This operation is thread-safe with respect to peer->obuf.
+ *
+ * peer->v_keepalive determines the interval. Changing this value before
+ * unregistering this peer with bgp_keepalives_off() results in undefined
+ * behavior.
+ *
+ * If the peer is already registered for keepalives via this function, nothing
+ * happens.
+ */
+extern void bgp_keepalives_on(struct peer *);
+
+/**
+ * Turns off keepalives for a peer.
+ *
+ * Removes the peer from the internal list of peers to generate keepalives for.
+ *
+ * If the peer is already unregistered for keepalives, nothing happens.
+ */
+extern void bgp_keepalives_off(struct peer *);
+
+/**
+ * Pre-run initialization function for keepalives pthread.
+ *
+ * Initializes synchronization primitives. This should be called before
+ * anything else to avoid race conditions.
+ */
+extern void bgp_keepalives_init(void);
+
+/**
+ * Entry function for keepalives pthread.
+ *
+ * This function loops over an internal list of peers, generating keepalives at
+ * regular intervals as determined by each peer's keepalive timer.
+ *
+ * See bgp_keepalives_on() for additional details.
+ *
+ * @param arg pthread arg, not used
+ */
+extern void *bgp_keepalives_start(void *arg);
+
+/**
+ * Poking function for keepalives pthread.
+ *
+ * Under normal circumstances the pthread will automatically wake itself
+ * whenever it is necessary to do work. This function may be used to force the
+ * thread to wake up and see if there is any work to do, or if it is time to
+ * die.
+ *
+ * It is not necessary to call this after bgp_keepalives_on().
+ */
+extern void bgp_keepalives_wake(void);
+
+/**
+ * Stops the thread and blocks until it terminates.
+ */
+int bgp_keepalives_stop(struct frr_pthread *fpt, void **result);
+
+#endif /* _FRR_BGP_KEEPALIVES_H */
diff --git a/bgpd/bgp_label.c b/bgpd/bgp_label.c
new file mode 100644
index 0000000..38f34a8
--- /dev/null
+++ b/bgpd/bgp_label.c
@@ -0,0 +1,476 @@
+/* BGP carrying label information
+ * Copyright (C) 2013 Cumulus Networks, Inc.
+ *
+ * This file is part of GNU Zebra.
+ *
+ * GNU Zebra is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License as published by the
+ * Free Software Foundation; either version 2, or (at your option) any
+ * later version.
+ *
+ * GNU Zebra is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program; see the file COPYING; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
+ */
+
+#include <zebra.h>
+
+#include "command.h"
+#include "thread.h"
+#include "prefix.h"
+#include "zclient.h"
+#include "stream.h"
+#include "network.h"
+#include "log.h"
+#include "memory.h"
+#include "nexthop.h"
+#include "mpls.h"
+
+#include "bgpd/bgpd.h"
+#include "bgpd/bgp_table.h"
+#include "bgpd/bgp_route.h"
+#include "bgpd/bgp_attr.h"
+#include "bgpd/bgp_label.h"
+#include "bgpd/bgp_packet.h"
+#include "bgpd/bgp_debug.h"
+#include "bgpd/bgp_errors.h"
+
+extern struct zclient *zclient;
+
+int bgp_parse_fec_update(void)
+{
+ struct stream *s;
+ struct bgp_dest *dest;
+ struct bgp *bgp;
+ struct bgp_table *table;
+ struct prefix p;
+ uint32_t label;
+ afi_t afi;
+ safi_t safi;
+
+ s = zclient->ibuf;
+
+ memset(&p, 0, sizeof(p));
+ p.family = stream_getw(s);
+ p.prefixlen = stream_getc(s);
+ stream_get(p.u.val, s, PSIZE(p.prefixlen));
+ label = stream_getl(s);
+
+ /* hack for the bgp instance & SAFI = have to send/receive it */
+ afi = family2afi(p.family);
+ safi = SAFI_UNICAST;
+ bgp = bgp_get_default();
+ if (!bgp) {
+ zlog_debug("no default bgp instance");
+ return -1;
+ }
+
+ table = bgp->rib[afi][safi];
+ if (!table) {
+ zlog_debug("no %u unicast table", p.family);
+ return -1;
+ }
+ dest = bgp_node_lookup(table, &p);
+ if (!dest) {
+ zlog_debug("no node for the prefix");
+ return -1;
+ }
+
+ /* treat it as implicit withdraw - the label is invalid */
+ if (label == MPLS_INVALID_LABEL)
+ bgp_unset_valid_label(&dest->local_label);
+ else {
+ dest->local_label = mpls_lse_encode(label, 0, 0, 1);
+ bgp_set_valid_label(&dest->local_label);
+ }
+ SET_FLAG(dest->flags, BGP_NODE_LABEL_CHANGED);
+ bgp_process(bgp, dest, afi, safi);
+ bgp_dest_unlock_node(dest);
+ return 1;
+}
+
+mpls_label_t bgp_adv_label(struct bgp_dest *dest, struct bgp_path_info *pi,
+ struct peer *to, afi_t afi, safi_t safi)
+{
+ struct peer *from;
+ mpls_label_t remote_label;
+ int reflect;
+
+ if (!dest || !pi || !to)
+ return MPLS_INVALID_LABEL;
+
+ remote_label = pi->extra ? pi->extra->label[0] : MPLS_INVALID_LABEL;
+ from = pi->peer;
+ reflect =
+ ((from->sort == BGP_PEER_IBGP) && (to->sort == BGP_PEER_IBGP));
+
+ if (reflect
+ && !CHECK_FLAG(to->af_flags[afi][safi],
+ PEER_FLAG_FORCE_NEXTHOP_SELF))
+ return remote_label;
+
+ if (CHECK_FLAG(to->af_flags[afi][safi], PEER_FLAG_NEXTHOP_UNCHANGED))
+ return remote_label;
+
+ return dest->local_label;
+}
+
+static void bgp_send_fec_register_label_msg(struct bgp_dest *dest, bool reg,
+ uint32_t label_index)
+{
+ struct stream *s;
+ int command;
+ const struct prefix *p;
+ uint16_t flags = 0;
+ size_t flags_pos = 0;
+ mpls_label_t *local_label = &(dest->local_label);
+ uint32_t ttl = 0;
+ uint32_t bos = 0;
+ uint32_t exp = 0;
+ mpls_label_t label = MPLS_INVALID_LABEL;
+ bool have_label_to_reg;
+
+ mpls_lse_decode(*local_label, &label, &ttl, &exp, &bos);
+
+ have_label_to_reg = bgp_is_valid_label(local_label) &&
+ label != MPLS_LABEL_IMPLICIT_NULL;
+
+ p = bgp_dest_get_prefix(dest);
+
+ /* Check socket. */
+ if (!zclient || zclient->sock < 0)
+ return;
+
+ if (BGP_DEBUG(labelpool, LABELPOOL))
+ zlog_debug("%s: FEC %sregister %pRN label_index=%u label=%u",
+ __func__, reg ? "" : "un", bgp_dest_to_rnode(dest),
+ label_index, label);
+ /* If the route node has a local_label assigned or the
+ * path node has an MPLS SR label index allowing zebra to
+ * derive the label, proceed with registration. */
+ s = zclient->obuf;
+ stream_reset(s);
+ command = (reg) ? ZEBRA_FEC_REGISTER : ZEBRA_FEC_UNREGISTER;
+ zclient_create_header(s, command, VRF_DEFAULT);
+ flags_pos = stream_get_endp(s); /* save position of 'flags' */
+ stream_putw(s, flags); /* initial flags */
+ stream_putw(s, PREFIX_FAMILY(p));
+ stream_put_prefix(s, p);
+ if (reg) {
+ /* label index takes precedence over auto-assigned label. */
+ if (label_index != 0) {
+ flags |= ZEBRA_FEC_REGISTER_LABEL_INDEX;
+ stream_putl(s, label_index);
+ } else if (have_label_to_reg) {
+ flags |= ZEBRA_FEC_REGISTER_LABEL;
+ stream_putl(s, label);
+ }
+ SET_FLAG(dest->flags, BGP_NODE_REGISTERED_FOR_LABEL);
+ } else
+ UNSET_FLAG(dest->flags, BGP_NODE_REGISTERED_FOR_LABEL);
+
+ /* Set length and flags */
+ stream_putw_at(s, 0, stream_get_endp(s));
+
+ /*
+ * We only need to write new flags if this is a register
+ */
+ if (reg)
+ stream_putw_at(s, flags_pos, flags);
+
+ zclient_send_message(zclient);
+}
+
+/**
+ * This is passed as the callback function to bgp_labelpool.c:bgp_lp_get()
+ * by bgp_reg_dereg_for_label() when a label needs to be obtained from
+ * label pool.
+ * Note that it will reject the allocated label if a label index is found,
+ * because the label index supposes predictable labels
+ */
+int bgp_reg_for_label_callback(mpls_label_t new_label, void *labelid,
+ bool allocated)
+{
+ struct bgp_dest *dest;
+
+ dest = labelid;
+
+ /*
+ * if the route had been removed or the request has gone then reject
+ * the allocated label. The requesting code will have done what is
+ * required to allocate the correct label
+ */
+ if (!CHECK_FLAG(dest->flags, BGP_NODE_LABEL_REQUESTED)) {
+ bgp_dest_unlock_node(dest);
+ return -1;
+ }
+
+ bgp_dest_unlock_node(dest);
+
+ if (BGP_DEBUG(labelpool, LABELPOOL))
+ zlog_debug("%s: FEC %pRN label=%u, allocated=%d", __func__,
+ bgp_dest_to_rnode(dest), new_label, allocated);
+
+ if (!allocated) {
+ /*
+ * previously-allocated label is now invalid, set to implicit
+ * null until new label arrives
+ */
+ if (CHECK_FLAG(dest->flags, BGP_NODE_REGISTERED_FOR_LABEL)) {
+ UNSET_FLAG(dest->flags, BGP_NODE_LABEL_REQUESTED);
+ dest->local_label = mpls_lse_encode(
+ MPLS_LABEL_IMPLICIT_NULL, 0, 0, 1);
+ bgp_set_valid_label(&dest->local_label);
+ }
+ }
+
+ dest->local_label = mpls_lse_encode(new_label, 0, 0, 1);
+ bgp_set_valid_label(&dest->local_label);
+
+ /*
+ * Get back to registering the FEC
+ */
+ bgp_send_fec_register_label_msg(dest, true, 0);
+
+ return 0;
+}
+
+void bgp_reg_dereg_for_label(struct bgp_dest *dest, struct bgp_path_info *pi,
+ bool reg)
+{
+ bool with_label_index = false;
+ const struct prefix *p;
+ bool have_label_to_reg;
+ uint32_t ttl = 0;
+ uint32_t bos = 0;
+ uint32_t exp = 0;
+ mpls_label_t label = MPLS_INVALID_LABEL;
+
+ mpls_lse_decode(dest->local_label, &label, &ttl, &exp, &bos);
+
+ have_label_to_reg = bgp_is_valid_label(&dest->local_label) &&
+ label != MPLS_LABEL_IMPLICIT_NULL;
+
+ p = bgp_dest_get_prefix(dest);
+
+ if (BGP_DEBUG(labelpool, LABELPOOL))
+ zlog_debug("%s: %pFX: %s ", __func__, p,
+ (reg ? "reg" : "dereg"));
+
+ if (reg) {
+ assert(pi);
+ /*
+ * Determine if we will let zebra should derive label from
+ * label index instead of bgpd requesting from label pool
+ */
+ if (CHECK_FLAG(pi->attr->flag,
+ ATTR_FLAG_BIT(BGP_ATTR_PREFIX_SID))
+ && pi->attr->label_index != BGP_INVALID_LABEL_INDEX) {
+ with_label_index = true;
+ UNSET_FLAG(dest->flags, BGP_NODE_LABEL_REQUESTED);
+ } else {
+ /*
+ * If no label has been registered -- assume any label
+ * from label pool will do. This means that label index
+ * always takes precedence over auto-assigned labels.
+ */
+ if (!have_label_to_reg) {
+ SET_FLAG(dest->flags, BGP_NODE_LABEL_REQUESTED);
+ if (BGP_DEBUG(labelpool, LABELPOOL))
+ zlog_debug(
+ "%s: Requesting label from LP for %pFX",
+ __func__, p);
+ /* bgp_reg_for_label_callback() will deal with
+ * fec registration when it gets a label from
+ * the pool. This means we'll never register
+ * FECs withoutvalid labels.
+ */
+ bgp_lp_get(LP_TYPE_BGP_LU, dest,
+ bgp_reg_for_label_callback);
+ return;
+ }
+ }
+ } else {
+ UNSET_FLAG(dest->flags, BGP_NODE_LABEL_REQUESTED);
+ bgp_lp_release(LP_TYPE_BGP_LU, dest, label);
+ }
+
+ bgp_send_fec_register_label_msg(
+ dest, reg, with_label_index ? pi->attr->label_index : 0);
+}
+
+static int bgp_nlri_get_labels(struct peer *peer, uint8_t *pnt, uint8_t plen,
+ mpls_label_t *label)
+{
+ uint8_t *data = pnt;
+ uint8_t *lim = pnt + plen;
+ uint8_t llen = 0;
+ uint8_t label_depth = 0;
+
+ for (; data < lim; data += BGP_LABEL_BYTES) {
+ memcpy(label, data, BGP_LABEL_BYTES);
+ llen += BGP_LABEL_BYTES;
+
+ bgp_set_valid_label(label);
+ label_depth += 1;
+
+ if (bgp_is_withdraw_label(label) || label_bos(label))
+ break;
+ }
+
+ /* If we RX multiple labels we will end up keeping only the last
+ * one. We do not yet support a label stack greater than 1. */
+ if (label_depth > 1)
+ zlog_info("%pBP rcvd UPDATE with label stack %d deep", peer,
+ label_depth);
+
+ if (!(bgp_is_withdraw_label(label) || label_bos(label)))
+ flog_warn(
+ EC_BGP_INVALID_LABEL_STACK,
+ "%pBP rcvd UPDATE with invalid label stack - no bottom of stack",
+ peer);
+
+ return llen;
+}
+
+int bgp_nlri_parse_label(struct peer *peer, struct attr *attr,
+ struct bgp_nlri *packet)
+{
+ uint8_t *pnt;
+ uint8_t *lim;
+ struct prefix p;
+ int psize = 0;
+ int prefixlen;
+ afi_t afi;
+ safi_t safi;
+ bool addpath_capable;
+ uint32_t addpath_id;
+ mpls_label_t label = MPLS_INVALID_LABEL;
+ uint8_t llen;
+
+ 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);
+
+ 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. */
+ prefixlen = *pnt++;
+ p.family = afi2family(packet->afi);
+ psize = PSIZE(prefixlen);
+
+ /* sanity check against packet data */
+ if ((pnt + psize) > lim) {
+ flog_err(
+ EC_BGP_UPDATE_RCV,
+ "%s [Error] Update packet error / L-U (prefix length %d exceeds packet size %u)",
+ peer->host, prefixlen, (uint)(lim - pnt));
+ return BGP_NLRI_PARSE_ERROR_PACKET_OVERFLOW;
+ }
+
+ /* Fill in the labels */
+ llen = bgp_nlri_get_labels(peer, pnt, psize, &label);
+ p.prefixlen = prefixlen - BSIZE(llen);
+
+ /* There needs to be at least one label */
+ if (prefixlen < 24) {
+ flog_err(EC_BGP_UPDATE_RCV,
+ "%s [Error] Update packet error (wrong label length %d)",
+ peer->host, prefixlen);
+ bgp_notify_send(peer, BGP_NOTIFY_UPDATE_ERR,
+ BGP_NOTIFY_UPDATE_INVAL_NETWORK);
+ return BGP_NLRI_PARSE_ERROR_LABEL_LENGTH;
+ }
+
+ if ((afi == AFI_IP && p.prefixlen > IPV4_MAX_BITLEN)
+ || (afi == AFI_IP6 && p.prefixlen > IPV6_MAX_BITLEN))
+ return BGP_NLRI_PARSE_ERROR_PREFIX_LENGTH;
+
+ /* Fetch prefix from NLRI packet */
+ memcpy(&p.u.prefix, pnt + llen, psize - llen);
+
+ /* Check address. */
+ if (afi == AFI_IP && safi == SAFI_LABELED_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 labeled-unicast NLRI is multicast address %pI4, ignoring",
+ peer->host, &p.u.prefix4);
+ continue;
+ }
+ }
+
+ /* Check address. */
+ if (afi == AFI_IP6 && safi == SAFI_LABELED_UNICAST) {
+ if (IN6_IS_ADDR_LINKLOCAL(&p.u.prefix6)) {
+ flog_err(
+ EC_BGP_UPDATE_RCV,
+ "%s: IPv6 labeled-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;
+ }
+ }
+
+ if (attr) {
+ bgp_update(peer, &p, addpath_id, attr, packet->afi,
+ safi, ZEBRA_ROUTE_BGP, BGP_ROUTE_NORMAL,
+ NULL, &label, 1, 0, NULL);
+ } else {
+ bgp_withdraw(peer, &p, addpath_id, attr, packet->afi,
+ SAFI_UNICAST, ZEBRA_ROUTE_BGP,
+ BGP_ROUTE_NORMAL, NULL, &label, 1, NULL);
+ }
+ }
+
+ /* Packet length consistency check. */
+ if (pnt != lim) {
+ flog_err(
+ EC_BGP_UPDATE_RCV,
+ "%s [Error] Update packet error / L-U (%td data remaining after parsing)",
+ peer->host, lim - pnt);
+ return BGP_NLRI_PARSE_ERROR_PACKET_LENGTH;
+ }
+
+ return BGP_NLRI_PARSE_OK;
+}
diff --git a/bgpd/bgp_label.h b/bgpd/bgp_label.h
new file mode 100644
index 0000000..4061e87
--- /dev/null
+++ b/bgpd/bgp_label.h
@@ -0,0 +1,108 @@
+/* BGP carrying Label information
+ * Copyright (C) 2013 Cumulus Networks, Inc.
+ *
+ * This file is part of GNU Zebra.
+ *
+ * GNU Zebra is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License as published by the
+ * Free Software Foundation; either version 2, or (at your option) any
+ * later version.
+ *
+ * GNU Zebra is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program; see the file COPYING; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
+ */
+
+#ifndef _BGP_LABEL_H
+#define _BGP_LABEL_H
+
+#define BGP_LABEL_BYTES 3
+#define BGP_LABEL_BITS 24
+#define BGP_WITHDRAW_LABEL 0x800000
+#define BGP_PREVENT_VRF_2_VRF_LEAK 0xFFFFFFFE
+
+struct bgp_dest;
+struct bgp_path_info;
+struct peer;
+
+extern int bgp_reg_for_label_callback(mpls_label_t new_label, void *labelid,
+ bool allocated);
+extern void bgp_reg_dereg_for_label(struct bgp_dest *dest,
+ struct bgp_path_info *pi, bool reg);
+extern int bgp_parse_fec_update(void);
+extern mpls_label_t bgp_adv_label(struct bgp_dest *dest,
+ struct bgp_path_info *pi, struct peer *to,
+ afi_t afi, safi_t safi);
+
+extern int bgp_nlri_parse_label(struct peer *peer, struct attr *attr,
+ struct bgp_nlri *packet);
+
+static inline int bgp_labeled_safi(safi_t safi)
+{
+ /* NOTE: This API really says a label (tag) MAY be present. Not all EVPN
+ * routes will have a label.
+ */
+ if ((safi == SAFI_LABELED_UNICAST) || (safi == SAFI_MPLS_VPN)
+ || (safi == SAFI_EVPN))
+ return 1;
+ return 0;
+}
+
+static inline int bgp_is_withdraw_label(mpls_label_t *label)
+{
+ uint8_t *pkt = (uint8_t *)label;
+
+ /* The check on pkt[2] for 0x00 or 0x02 is in case bgp_set_valid_label()
+ * was called on the withdraw label */
+ if (((pkt[0] == 0x80) || (pkt[0] == 0x00)) && (pkt[1] == 0x00)
+ && ((pkt[2] == 0x00) || (pkt[2] == 0x02)))
+ return 1;
+ return 0;
+}
+
+static inline int bgp_is_valid_label(const mpls_label_t *label)
+{
+ uint8_t *t = (uint8_t *)label;
+ if (!t)
+ return 0;
+ return (t[2] & 0x02);
+}
+
+static inline void bgp_set_valid_label(mpls_label_t *label)
+{
+ uint8_t *t = (uint8_t *)label;
+ if (t)
+ t[2] |= 0x02;
+}
+
+static inline void bgp_unset_valid_label(mpls_label_t *label)
+{
+ uint8_t *t = (uint8_t *)label;
+ if (t)
+ t[2] &= ~0x02;
+}
+
+static inline void bgp_register_for_label(struct bgp_dest *dest,
+ struct bgp_path_info *pi)
+{
+ bgp_reg_dereg_for_label(dest, pi, true);
+}
+
+static inline void bgp_unregister_for_label(struct bgp_dest *dest)
+{
+ bgp_reg_dereg_for_label(dest, NULL, false);
+}
+
+/* Return BOS value of label stream */
+static inline uint8_t label_bos(mpls_label_t *label)
+{
+ uint8_t *t = (uint8_t *)label;
+ return (t[2] & 0x01);
+};
+
+#endif /* _BGP_LABEL_H */
diff --git a/bgpd/bgp_labelpool.c b/bgpd/bgp_labelpool.c
new file mode 100644
index 0000000..c227a5e
--- /dev/null
+++ b/bgpd/bgp_labelpool.c
@@ -0,0 +1,1559 @@
+/*
+ * BGP Label Pool - Manage label chunk allocations from zebra asynchronously
+ *
+ * Copyright (C) 2018 LabN Consulting, L.L.C.
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License as published by the Free
+ * Software Foundation; either version 2 of the License, or (at your option)
+ * any later version.
+ *
+ * This program is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
+ * more details.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program; see the file COPYING; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
+ */
+
+#include <zebra.h>
+
+#include "log.h"
+#include "memory.h"
+#include "stream.h"
+#include "mpls.h"
+#include "vty.h"
+#include "linklist.h"
+#include "skiplist.h"
+#include "workqueue.h"
+#include "zclient.h"
+#include "mpls.h"
+
+#include "bgpd/bgpd.h"
+#include "bgpd/bgp_labelpool.h"
+#include "bgpd/bgp_debug.h"
+#include "bgpd/bgp_errors.h"
+#include "bgpd/bgp_route.h"
+
+#define BGP_LABELPOOL_ENABLE_TESTS 0
+
+#ifndef VTYSH_EXTRACT_PL
+#include "bgpd/bgp_labelpool_clippy.c"
+#endif
+
+
+/*
+ * Definitions and external declarations.
+ */
+extern struct zclient *zclient;
+
+#if BGP_LABELPOOL_ENABLE_TESTS
+static void lptest_init(void);
+static void lptest_finish(void);
+#endif
+
+/*
+ * Remember where pool data are kept
+ */
+static struct labelpool *lp;
+
+/*
+ * Number of labels requested at a time from the zebra label manager.
+ * We start small but double the request size each time up to a
+ * maximum size.
+ *
+ * The label space is 20 bits which is shared with other FRR processes
+ * on this host, so to avoid greedily requesting a mostly wasted chunk,
+ * we limit the chunk size to 1/16 of the label space (that's the -4 bits
+ * in the definition below). This limit slightly increases our cost of
+ * finding free labels in our allocated chunks.
+ */
+#define LP_CHUNK_SIZE_MIN 128
+#define LP_CHUNK_SIZE_MAX (1 << (20 - 4))
+
+DEFINE_MTYPE_STATIC(BGPD, BGP_LABEL_CHUNK, "BGP Label Chunk");
+DEFINE_MTYPE_STATIC(BGPD, BGP_LABEL_FIFO, "BGP Label FIFO item");
+DEFINE_MTYPE_STATIC(BGPD, BGP_LABEL_CB, "BGP Dynamic Label Assignment");
+DEFINE_MTYPE_STATIC(BGPD, BGP_LABEL_CBQ, "BGP Dynamic Label Callback");
+
+struct lp_chunk {
+ uint32_t first;
+ uint32_t last;
+ uint32_t nfree; /* un-allocated count */
+ uint32_t idx_last_allocated; /* start looking here */
+ bitfield_t allocated_map;
+};
+
+/*
+ * label control block
+ */
+struct lp_lcb {
+ mpls_label_t label; /* MPLS_LABEL_NONE = not allocated */
+ int type;
+ void *labelid; /* unique ID */
+ /*
+ * callback for label allocation and loss
+ *
+ * allocated: false = lost
+ */
+ int (*cbfunc)(mpls_label_t label, void *lblid, bool alloc);
+};
+
+struct lp_fifo {
+ struct lp_fifo_item fifo;
+ struct lp_lcb lcb;
+};
+
+DECLARE_LIST(lp_fifo, struct lp_fifo, fifo);
+
+struct lp_cbq_item {
+ int (*cbfunc)(mpls_label_t label, void *lblid, bool alloc);
+ int type;
+ mpls_label_t label;
+ void *labelid;
+ bool allocated; /* false = lost */
+};
+
+static wq_item_status lp_cbq_docallback(struct work_queue *wq, void *data)
+{
+ struct lp_cbq_item *lcbq = data;
+ int rc;
+ int debug = BGP_DEBUG(labelpool, LABELPOOL);
+
+ if (debug)
+ zlog_debug("%s: calling callback with labelid=%p label=%u allocated=%d",
+ __func__, lcbq->labelid, lcbq->label, lcbq->allocated);
+
+ if (lcbq->label == MPLS_LABEL_NONE) {
+ /* shouldn't happen */
+ flog_err(EC_BGP_LABEL, "%s: error: label==MPLS_LABEL_NONE",
+ __func__);
+ return WQ_SUCCESS;
+ }
+
+ rc = (*(lcbq->cbfunc))(lcbq->label, lcbq->labelid, lcbq->allocated);
+
+ if (lcbq->allocated && rc) {
+ /*
+ * Callback rejected allocation. This situation could arise
+ * if there was a label request followed by the requestor
+ * deciding it didn't need the assignment (e.g., config
+ * change) while the reply to the original request (with
+ * label) was in the work queue.
+ */
+ if (debug)
+ zlog_debug("%s: callback rejected allocation, releasing labelid=%p label=%u",
+ __func__, lcbq->labelid, lcbq->label);
+
+ uintptr_t lbl = lcbq->label;
+ void *labelid;
+ struct lp_lcb *lcb;
+
+ /*
+ * If the rejected label was marked inuse by this labelid,
+ * release the label back to the pool.
+ *
+ * Further, if the rejected label was still assigned to
+ * this labelid in the LCB, delete the LCB.
+ */
+ if (!skiplist_search(lp->inuse, (void *)lbl, &labelid)) {
+ if (labelid == lcbq->labelid) {
+ if (!skiplist_search(lp->ledger, labelid,
+ (void **)&lcb)) {
+ if (lcbq->label == lcb->label)
+ skiplist_delete(lp->ledger,
+ labelid, NULL);
+ }
+ skiplist_delete(lp->inuse, (void *)lbl, NULL);
+ }
+ }
+ }
+
+ return WQ_SUCCESS;
+}
+
+static void lp_cbq_item_free(struct work_queue *wq, void *data)
+{
+ XFREE(MTYPE_BGP_LABEL_CBQ, data);
+}
+
+static void lp_lcb_free(void *goner)
+{
+ XFREE(MTYPE_BGP_LABEL_CB, goner);
+}
+
+static void lp_chunk_free(void *goner)
+{
+ struct lp_chunk *chunk = (struct lp_chunk *)goner;
+
+ bf_free(chunk->allocated_map);
+ XFREE(MTYPE_BGP_LABEL_CHUNK, goner);
+}
+
+void bgp_lp_init(struct thread_master *master, struct labelpool *pool)
+{
+ if (BGP_DEBUG(labelpool, LABELPOOL))
+ zlog_debug("%s: entry", __func__);
+
+ lp = pool; /* Set module pointer to pool data */
+
+ lp->ledger = skiplist_new(0, NULL, lp_lcb_free);
+ lp->inuse = skiplist_new(0, NULL, NULL);
+ lp->chunks = list_new();
+ lp->chunks->del = lp_chunk_free;
+ lp_fifo_init(&lp->requests);
+ lp->callback_q = work_queue_new(master, "label callbacks");
+
+ lp->callback_q->spec.workfunc = lp_cbq_docallback;
+ lp->callback_q->spec.del_item_data = lp_cbq_item_free;
+ lp->callback_q->spec.max_retries = 0;
+
+ lp->next_chunksize = LP_CHUNK_SIZE_MIN;
+
+#if BGP_LABELPOOL_ENABLE_TESTS
+ lptest_init();
+#endif
+}
+
+/* check if a label callback was for a BGP LU node, and if so, unlock it */
+static void check_bgp_lu_cb_unlock(struct lp_lcb *lcb)
+{
+ if (lcb->type == LP_TYPE_BGP_LU)
+ bgp_dest_unlock_node(lcb->labelid);
+}
+
+/* check if a label callback was for a BGP LU node, and if so, lock it */
+static void check_bgp_lu_cb_lock(struct lp_lcb *lcb)
+{
+ if (lcb->type == LP_TYPE_BGP_LU)
+ bgp_dest_lock_node(lcb->labelid);
+}
+
+void bgp_lp_finish(void)
+{
+ struct lp_fifo *lf;
+ struct work_queue_item *item, *titem;
+
+#if BGP_LABELPOOL_ENABLE_TESTS
+ lptest_finish();
+#endif
+ if (!lp)
+ return;
+
+ skiplist_free(lp->ledger);
+ lp->ledger = NULL;
+
+ skiplist_free(lp->inuse);
+ lp->inuse = NULL;
+
+ list_delete(&lp->chunks);
+
+ while ((lf = lp_fifo_pop(&lp->requests))) {
+ check_bgp_lu_cb_unlock(&lf->lcb);
+ XFREE(MTYPE_BGP_LABEL_FIFO, lf);
+ }
+ lp_fifo_fini(&lp->requests);
+
+ /* we must unlock path infos for LU callbacks; but we cannot do that
+ * in the deletion callback of the workqueue, as that is also called
+ * to remove an element from the queue after it has been run, resulting
+ * in a double unlock. Hence we need to iterate over our queues and
+ * lists and manually perform the unlocking (ugh)
+ */
+ STAILQ_FOREACH_SAFE (item, &lp->callback_q->items, wq, titem)
+ check_bgp_lu_cb_unlock(item->data);
+
+ work_queue_free_and_null(&lp->callback_q);
+
+ lp = NULL;
+}
+
+static mpls_label_t get_label_from_pool(void *labelid)
+{
+ struct listnode *node;
+ struct lp_chunk *chunk;
+ int debug = BGP_DEBUG(labelpool, LABELPOOL);
+
+ /*
+ * Find a free label
+ */
+ for (ALL_LIST_ELEMENTS_RO(lp->chunks, node, chunk)) {
+ uintptr_t lbl;
+ unsigned int index;
+
+ if (debug)
+ zlog_debug("%s: chunk first=%u last=%u",
+ __func__, chunk->first, chunk->last);
+
+ /*
+ * don't look in chunks with no available labels
+ */
+ if (!chunk->nfree)
+ continue;
+
+ /*
+ * roll through bitfield starting where we stopped
+ * last time
+ */
+ index = bf_find_next_clear_bit_wrap(
+ &chunk->allocated_map, chunk->idx_last_allocated + 1,
+ 0);
+
+ /*
+ * since chunk->nfree is non-zero, we should always get
+ * a valid index
+ */
+ assert(index != WORD_MAX);
+
+ lbl = chunk->first + index;
+ if (skiplist_insert(lp->inuse, (void *)lbl, labelid)) {
+ /* something is very wrong */
+ zlog_err("%s: unable to insert inuse label %u (id %p)",
+ __func__, (uint32_t)lbl, labelid);
+ return MPLS_LABEL_NONE;
+ }
+
+ /*
+ * Success
+ */
+ bf_set_bit(chunk->allocated_map, index);
+ chunk->idx_last_allocated = index;
+ chunk->nfree -= 1;
+
+ return lbl;
+ }
+
+ return MPLS_LABEL_NONE;
+}
+
+/*
+ * Success indicated by value of "label" field in returned LCB
+ */
+static struct lp_lcb *lcb_alloc(
+ int type,
+ void *labelid,
+ int (*cbfunc)(mpls_label_t label, void *labelid, bool allocated))
+{
+ /*
+ * Set up label control block
+ */
+ struct lp_lcb *new = XCALLOC(MTYPE_BGP_LABEL_CB,
+ sizeof(struct lp_lcb));
+
+ new->label = get_label_from_pool(labelid);
+ new->type = type;
+ new->labelid = labelid;
+ new->cbfunc = cbfunc;
+
+ return new;
+}
+
+/*
+ * Callers who need labels must supply a type, labelid, and callback.
+ * The type is a value defined in bgp_labelpool.h (add types as needed).
+ * The callback is for asynchronous notification of label allocation.
+ * The labelid is passed as an argument to the callback. It should be unique
+ * to the requested label instance.
+ *
+ * If zebra is not connected, callbacks with labels will be delayed
+ * until connection is established. If zebra connection is lost after
+ * labels have been assigned, existing assignments via this labelpool
+ * module will continue until reconnection.
+ *
+ * When connection to zebra is reestablished, previous label assignments
+ * will be invalidated (via callbacks having the "allocated" parameter unset)
+ * and new labels will be automatically reassigned by this labelpool module
+ * (that is, a requestor does not need to call bgp_lp_get() again if it is
+ * notified via callback that its label has been lost: it will eventually
+ * get another callback with a new label assignment).
+ *
+ * The callback function should return 0 to accept the allocation
+ * and non-zero to refuse it. The callback function return value is
+ * ignored for invalidations (i.e., when the "allocated" parameter is false)
+ *
+ * Prior requests for a given labelid are detected so that requests and
+ * assignments are not duplicated.
+ */
+void bgp_lp_get(
+ int type,
+ void *labelid,
+ int (*cbfunc)(mpls_label_t label, void *labelid, bool allocated))
+{
+ struct lp_lcb *lcb;
+ int requested = 0;
+ int debug = BGP_DEBUG(labelpool, LABELPOOL);
+
+ if (debug)
+ zlog_debug("%s: labelid=%p", __func__, labelid);
+
+ /*
+ * Have we seen this request before?
+ */
+ if (!skiplist_search(lp->ledger, labelid, (void **)&lcb)) {
+ requested = 1;
+ } else {
+ lcb = lcb_alloc(type, labelid, cbfunc);
+ if (debug)
+ zlog_debug("%s: inserting lcb=%p label=%u",
+ __func__, lcb, lcb->label);
+ int rc = skiplist_insert(lp->ledger, labelid, lcb);
+
+ if (rc) {
+ /* shouldn't happen */
+ flog_err(EC_BGP_LABEL,
+ "%s: can't insert new LCB into ledger list",
+ __func__);
+ XFREE(MTYPE_BGP_LABEL_CB, lcb);
+ return;
+ }
+ }
+
+ if (lcb->label != MPLS_LABEL_NONE) {
+ /*
+ * Fast path: we filled the request from local pool (or
+ * this is a duplicate request that we filled already).
+ * Enqueue response work item with new label.
+ */
+ struct lp_cbq_item *q;
+
+ q = XCALLOC(MTYPE_BGP_LABEL_CBQ, sizeof(struct lp_cbq_item));
+
+ q->cbfunc = lcb->cbfunc;
+ q->type = lcb->type;
+ q->label = lcb->label;
+ q->labelid = lcb->labelid;
+ q->allocated = true;
+
+ /* if this is a LU request, lock node before queueing */
+ check_bgp_lu_cb_lock(lcb);
+
+ work_queue_add(lp->callback_q, q);
+
+ return;
+ }
+
+ if (requested)
+ return;
+
+ if (debug)
+ zlog_debug("%s: slow path. lcb=%p label=%u",
+ __func__, lcb, lcb->label);
+
+ /*
+ * Slow path: we are out of labels in the local pool,
+ * so remember the request and also get another chunk from
+ * the label manager.
+ *
+ * We track number of outstanding label requests: don't
+ * need to get a chunk for each one.
+ */
+
+ struct lp_fifo *lf = XCALLOC(MTYPE_BGP_LABEL_FIFO,
+ sizeof(struct lp_fifo));
+
+ lf->lcb = *lcb;
+ /* if this is a LU request, lock node before queueing */
+ check_bgp_lu_cb_lock(lcb);
+
+ lp_fifo_add_tail(&lp->requests, lf);
+
+ if (lp_fifo_count(&lp->requests) > lp->pending_count) {
+ if (!zclient || zclient->sock < 0)
+ return;
+ if (zclient_send_get_label_chunk(zclient, 0, lp->next_chunksize,
+ MPLS_LABEL_BASE_ANY) !=
+ ZCLIENT_SEND_FAILURE) {
+ lp->pending_count += lp->next_chunksize;
+ if ((lp->next_chunksize << 1) <= LP_CHUNK_SIZE_MAX)
+ lp->next_chunksize <<= 1;
+ }
+ }
+}
+
+void bgp_lp_release(
+ int type,
+ void *labelid,
+ mpls_label_t label)
+{
+ struct lp_lcb *lcb;
+
+ if (!skiplist_search(lp->ledger, labelid, (void **)&lcb)) {
+ if (label == lcb->label && type == lcb->type) {
+ struct listnode *node;
+ struct lp_chunk *chunk;
+ uintptr_t lbl = label;
+ bool deallocated = false;
+
+ /* no longer in use */
+ skiplist_delete(lp->inuse, (void *)lbl, NULL);
+
+ /* no longer requested */
+ skiplist_delete(lp->ledger, labelid, NULL);
+
+ /*
+ * Find the chunk this label belongs to and
+ * deallocate the label
+ */
+ for (ALL_LIST_ELEMENTS_RO(lp->chunks, node, chunk)) {
+ uint32_t index;
+
+ if ((label < chunk->first) ||
+ (label > chunk->last))
+ continue;
+
+ index = label - chunk->first;
+ assert(bf_test_index(chunk->allocated_map,
+ index));
+ bf_release_index(chunk->allocated_map, index);
+ chunk->nfree += 1;
+ deallocated = true;
+ }
+ assert(deallocated);
+ }
+ }
+}
+
+/*
+ * zebra response giving us a chunk of labels
+ */
+void bgp_lp_event_chunk(uint8_t keep, uint32_t first, uint32_t last)
+{
+ struct lp_chunk *chunk;
+ int debug = BGP_DEBUG(labelpool, LABELPOOL);
+ struct lp_fifo *lf;
+ uint32_t labelcount;
+
+ if (last < first) {
+ flog_err(EC_BGP_LABEL,
+ "%s: zebra label chunk invalid: first=%u, last=%u",
+ __func__, first, last);
+ return;
+ }
+
+ chunk = XCALLOC(MTYPE_BGP_LABEL_CHUNK, sizeof(struct lp_chunk));
+
+ labelcount = last - first + 1;
+
+ chunk->first = first;
+ chunk->last = last;
+ chunk->nfree = labelcount;
+ bf_init(chunk->allocated_map, labelcount);
+
+ /*
+ * Optimize for allocation by adding the new (presumably larger)
+ * chunk at the head of the list so it is examined first.
+ */
+ listnode_add_head(lp->chunks, chunk);
+
+ lp->pending_count -= labelcount;
+
+ if (debug) {
+ zlog_debug("%s: %zu pending requests", __func__,
+ lp_fifo_count(&lp->requests));
+ }
+
+ while (labelcount && (lf = lp_fifo_first(&lp->requests))) {
+
+ struct lp_lcb *lcb;
+ void *labelid = lf->lcb.labelid;
+
+ if (skiplist_search(lp->ledger, labelid, (void **)&lcb)) {
+ /* request no longer in effect */
+
+ if (debug) {
+ zlog_debug("%s: labelid %p: request no longer in effect",
+ __func__, labelid);
+ }
+ /* if this was a BGP_LU request, unlock node
+ */
+ check_bgp_lu_cb_unlock(lcb);
+ goto finishedrequest;
+ }
+
+ /* have LCB */
+ if (lcb->label != MPLS_LABEL_NONE) {
+ /* request already has a label */
+ if (debug) {
+ zlog_debug("%s: labelid %p: request already has a label: %u=0x%x, lcb=%p",
+ __func__, labelid,
+ lcb->label, lcb->label, lcb);
+ }
+ /* if this was a BGP_LU request, unlock node
+ */
+ check_bgp_lu_cb_unlock(lcb);
+
+ goto finishedrequest;
+ }
+
+ lcb->label = get_label_from_pool(lcb->labelid);
+
+ if (lcb->label == MPLS_LABEL_NONE) {
+ /*
+ * Out of labels in local pool, await next chunk
+ */
+ if (debug) {
+ zlog_debug("%s: out of labels, await more",
+ __func__);
+ }
+ break;
+ }
+
+ labelcount -= 1;
+
+ /*
+ * we filled the request from local pool.
+ * Enqueue response work item with new label.
+ */
+ struct lp_cbq_item *q = XCALLOC(MTYPE_BGP_LABEL_CBQ,
+ sizeof(struct lp_cbq_item));
+
+ q->cbfunc = lcb->cbfunc;
+ q->type = lcb->type;
+ q->label = lcb->label;
+ q->labelid = lcb->labelid;
+ q->allocated = true;
+
+ if (debug)
+ zlog_debug("%s: assigning label %u to labelid %p",
+ __func__, q->label, q->labelid);
+
+ work_queue_add(lp->callback_q, q);
+
+finishedrequest:
+ lp_fifo_del(&lp->requests, lf);
+ XFREE(MTYPE_BGP_LABEL_FIFO, lf);
+ }
+}
+
+/*
+ * continue using allocated labels until zebra returns
+ */
+void bgp_lp_event_zebra_down(void)
+{
+ /* rats. */
+}
+
+/*
+ * Inform owners of previously-allocated labels that their labels
+ * are not valid. Request chunk from zebra large enough to satisfy
+ * previously-allocated labels plus any outstanding requests.
+ */
+void bgp_lp_event_zebra_up(void)
+{
+ unsigned int labels_needed;
+ unsigned int chunks_needed;
+ void *labelid;
+ struct lp_lcb *lcb;
+ int lm_init_ok;
+
+ lp->reconnect_count++;
+ /*
+ * Get label chunk allocation request dispatched to zebra
+ */
+ labels_needed = lp_fifo_count(&lp->requests) +
+ skiplist_count(lp->inuse);
+
+ if (labels_needed > lp->next_chunksize) {
+ while ((lp->next_chunksize < labels_needed) &&
+ (lp->next_chunksize << 1 <= LP_CHUNK_SIZE_MAX))
+
+ lp->next_chunksize <<= 1;
+ }
+
+ /* round up */
+ chunks_needed = (labels_needed / lp->next_chunksize) + 1;
+ labels_needed = chunks_needed * lp->next_chunksize;
+
+ lm_init_ok = lm_label_manager_connect(zclient, 1) == 0;
+
+ if (!lm_init_ok) {
+ zlog_err("%s: label manager connection error", __func__);
+ return;
+ }
+
+ zclient_send_get_label_chunk(zclient, 0, labels_needed,
+ MPLS_LABEL_BASE_ANY);
+ lp->pending_count = labels_needed;
+
+ /*
+ * Invalidate current list of chunks
+ */
+ list_delete_all_node(lp->chunks);
+
+ /*
+ * Invalidate any existing labels and requeue them as requests
+ */
+ while (!skiplist_first(lp->inuse, NULL, &labelid)) {
+
+ /*
+ * Get LCB
+ */
+ if (!skiplist_search(lp->ledger, labelid, (void **)&lcb)) {
+
+ if (lcb->label != MPLS_LABEL_NONE) {
+ /*
+ * invalidate
+ */
+ struct lp_cbq_item *q;
+
+ q = XCALLOC(MTYPE_BGP_LABEL_CBQ,
+ sizeof(struct lp_cbq_item));
+ q->cbfunc = lcb->cbfunc;
+ q->type = lcb->type;
+ q->label = lcb->label;
+ q->labelid = lcb->labelid;
+ q->allocated = false;
+ check_bgp_lu_cb_lock(lcb);
+ work_queue_add(lp->callback_q, q);
+
+ lcb->label = MPLS_LABEL_NONE;
+ }
+
+ /*
+ * request queue
+ */
+ struct lp_fifo *lf = XCALLOC(MTYPE_BGP_LABEL_FIFO,
+ sizeof(struct lp_fifo));
+
+ lf->lcb = *lcb;
+ check_bgp_lu_cb_lock(lcb);
+ lp_fifo_add_tail(&lp->requests, lf);
+ }
+
+ skiplist_delete_first(lp->inuse);
+ }
+}
+
+DEFUN(show_bgp_labelpool_summary, show_bgp_labelpool_summary_cmd,
+ "show bgp labelpool summary [json]",
+ SHOW_STR BGP_STR
+ "BGP Labelpool information\n"
+ "BGP Labelpool summary\n" JSON_STR)
+{
+ bool uj = use_json(argc, argv);
+ json_object *json = NULL;
+
+ if (!lp) {
+ if (uj)
+ vty_out(vty, "{}\n");
+ else
+ vty_out(vty, "No existing BGP labelpool\n");
+ return (CMD_WARNING);
+ }
+
+ if (uj) {
+ json = json_object_new_object();
+#if CONFDATE > 20230131
+CPP_NOTICE("Remove JSON object commands with keys starting with capital")
+#endif
+ json_object_int_add(json, "Ledger", skiplist_count(lp->ledger));
+ json_object_int_add(json, "ledger", skiplist_count(lp->ledger));
+ json_object_int_add(json, "InUse", skiplist_count(lp->inuse));
+ json_object_int_add(json, "inUse", skiplist_count(lp->inuse));
+ json_object_int_add(json, "Requests",
+ lp_fifo_count(&lp->requests));
+ json_object_int_add(json, "requests",
+ lp_fifo_count(&lp->requests));
+ json_object_int_add(json, "LabelChunks", listcount(lp->chunks));
+ json_object_int_add(json, "labelChunks", listcount(lp->chunks));
+ json_object_int_add(json, "Pending", lp->pending_count);
+ json_object_int_add(json, "pending", lp->pending_count);
+ json_object_int_add(json, "Reconnects", lp->reconnect_count);
+ json_object_int_add(json, "reconnects", lp->reconnect_count);
+ vty_json(vty, json);
+ } else {
+ vty_out(vty, "Labelpool Summary\n");
+ vty_out(vty, "-----------------\n");
+ vty_out(vty, "%-13s %d\n",
+ "Ledger:", skiplist_count(lp->ledger));
+ vty_out(vty, "%-13s %d\n", "InUse:", skiplist_count(lp->inuse));
+ vty_out(vty, "%-13s %zu\n",
+ "Requests:", lp_fifo_count(&lp->requests));
+ vty_out(vty, "%-13s %d\n",
+ "LabelChunks:", listcount(lp->chunks));
+ vty_out(vty, "%-13s %d\n", "Pending:", lp->pending_count);
+ vty_out(vty, "%-13s %d\n", "Reconnects:", lp->reconnect_count);
+ }
+ return CMD_SUCCESS;
+}
+
+DEFUN(show_bgp_labelpool_ledger, show_bgp_labelpool_ledger_cmd,
+ "show bgp labelpool ledger [json]",
+ SHOW_STR BGP_STR
+ "BGP Labelpool information\n"
+ "BGP Labelpool ledger\n" JSON_STR)
+{
+ bool uj = use_json(argc, argv);
+ json_object *json = NULL, *json_elem = NULL;
+ struct lp_lcb *lcb = NULL;
+ struct bgp_dest *dest;
+ void *cursor = NULL;
+ const struct prefix *p;
+ int rc, count;
+
+ if (!lp) {
+ if (uj)
+ vty_out(vty, "{}\n");
+ else
+ vty_out(vty, "No existing BGP labelpool\n");
+ return (CMD_WARNING);
+ }
+
+ if (uj) {
+ count = skiplist_count(lp->ledger);
+ if (!count) {
+ vty_out(vty, "{}\n");
+ return CMD_SUCCESS;
+ }
+ json = json_object_new_array();
+ } else {
+ vty_out(vty, "Prefix Label\n");
+ vty_out(vty, "---------------------------\n");
+ }
+
+ for (rc = skiplist_next(lp->ledger, (void **)&dest, (void **)&lcb,
+ &cursor);
+ !rc; rc = skiplist_next(lp->ledger, (void **)&dest, (void **)&lcb,
+ &cursor)) {
+ if (uj) {
+ json_elem = json_object_new_object();
+ json_object_array_add(json, json_elem);
+ }
+ switch (lcb->type) {
+ case LP_TYPE_BGP_LU:
+ if (!CHECK_FLAG(dest->flags, BGP_NODE_LABEL_REQUESTED))
+ if (uj) {
+ json_object_string_add(
+ json_elem, "prefix", "INVALID");
+ json_object_int_add(json_elem, "label",
+ lcb->label);
+ } else
+ vty_out(vty, "%-18s %u\n",
+ "INVALID", lcb->label);
+ else {
+ p = bgp_dest_get_prefix(dest);
+ if (uj) {
+ json_object_string_addf(
+ json_elem, "prefix", "%pFX", p);
+ json_object_int_add(json_elem, "label",
+ lcb->label);
+ } else
+ vty_out(vty, "%-18pFX %u\n", p,
+ lcb->label);
+ }
+ break;
+ case LP_TYPE_VRF:
+ if (uj) {
+ json_object_string_add(json_elem, "prefix",
+ "VRF");
+ json_object_int_add(json_elem, "label",
+ lcb->label);
+ } else
+ vty_out(vty, "%-18s %u\n", "VRF",
+ lcb->label);
+
+ break;
+ }
+ }
+ if (uj)
+ vty_json(vty, json);
+ return CMD_SUCCESS;
+}
+
+DEFUN(show_bgp_labelpool_inuse, show_bgp_labelpool_inuse_cmd,
+ "show bgp labelpool inuse [json]",
+ SHOW_STR BGP_STR
+ "BGP Labelpool information\n"
+ "BGP Labelpool inuse\n" JSON_STR)
+{
+ bool uj = use_json(argc, argv);
+ json_object *json = NULL, *json_elem = NULL;
+ struct bgp_dest *dest;
+ mpls_label_t label;
+ struct lp_lcb *lcb;
+ void *cursor = NULL;
+ const struct prefix *p;
+ int rc, count;
+
+ if (!lp) {
+ vty_out(vty, "No existing BGP labelpool\n");
+ return (CMD_WARNING);
+ }
+ if (!lp) {
+ if (uj)
+ vty_out(vty, "{}\n");
+ else
+ vty_out(vty, "No existing BGP labelpool\n");
+ return (CMD_WARNING);
+ }
+
+ if (uj) {
+ count = skiplist_count(lp->inuse);
+ if (!count) {
+ vty_out(vty, "{}\n");
+ return CMD_SUCCESS;
+ }
+ json = json_object_new_array();
+ } else {
+ vty_out(vty, "Prefix Label\n");
+ vty_out(vty, "---------------------------\n");
+ }
+ for (rc = skiplist_next(lp->inuse, (void **)&label, (void **)&dest,
+ &cursor);
+ !rc; rc = skiplist_next(lp->ledger, (void **)&label,
+ (void **)&dest, &cursor)) {
+ if (skiplist_search(lp->ledger, dest, (void **)&lcb))
+ continue;
+
+ if (uj) {
+ json_elem = json_object_new_object();
+ json_object_array_add(json, json_elem);
+ }
+
+ switch (lcb->type) {
+ case LP_TYPE_BGP_LU:
+ if (!CHECK_FLAG(dest->flags, BGP_NODE_LABEL_REQUESTED))
+ if (uj) {
+ json_object_string_add(
+ json_elem, "prefix", "INVALID");
+ json_object_int_add(json_elem, "label",
+ label);
+ } else
+ vty_out(vty, "INVALID %u\n",
+ label);
+ else {
+ p = bgp_dest_get_prefix(dest);
+ if (uj) {
+ json_object_string_addf(
+ json_elem, "prefix", "%pFX", p);
+ json_object_int_add(json_elem, "label",
+ label);
+ } else
+ vty_out(vty, "%-18pFX %u\n", p,
+ label);
+ }
+ break;
+ case LP_TYPE_VRF:
+ if (uj) {
+ json_object_string_add(json_elem, "prefix",
+ "VRF");
+ json_object_int_add(json_elem, "label", label);
+ } else
+ vty_out(vty, "%-18s %u\n", "VRF",
+ label);
+ break;
+ }
+ }
+ if (uj)
+ vty_json(vty, json);
+ return CMD_SUCCESS;
+}
+
+DEFUN(show_bgp_labelpool_requests, show_bgp_labelpool_requests_cmd,
+ "show bgp labelpool requests [json]",
+ SHOW_STR BGP_STR
+ "BGP Labelpool information\n"
+ "BGP Labelpool requests\n" JSON_STR)
+{
+ bool uj = use_json(argc, argv);
+ json_object *json = NULL, *json_elem = NULL;
+ struct bgp_dest *dest;
+ const struct prefix *p;
+ struct lp_fifo *item, *next;
+ int count;
+
+ if (!lp) {
+ if (uj)
+ vty_out(vty, "{}\n");
+ else
+ vty_out(vty, "No existing BGP labelpool\n");
+ return (CMD_WARNING);
+ }
+
+ if (uj) {
+ count = lp_fifo_count(&lp->requests);
+ if (!count) {
+ vty_out(vty, "{}\n");
+ return CMD_SUCCESS;
+ }
+ json = json_object_new_array();
+ } else {
+ vty_out(vty, "Prefix \n");
+ vty_out(vty, "----------------\n");
+ }
+
+ for (item = lp_fifo_first(&lp->requests); item; item = next) {
+ next = lp_fifo_next_safe(&lp->requests, item);
+ dest = item->lcb.labelid;
+ if (uj) {
+ json_elem = json_object_new_object();
+ json_object_array_add(json, json_elem);
+ }
+ switch (item->lcb.type) {
+ case LP_TYPE_BGP_LU:
+ if (!CHECK_FLAG(dest->flags,
+ BGP_NODE_LABEL_REQUESTED)) {
+ if (uj)
+ json_object_string_add(
+ json_elem, "prefix", "INVALID");
+ else
+ vty_out(vty, "INVALID\n");
+ } else {
+ p = bgp_dest_get_prefix(dest);
+ if (uj)
+ json_object_string_addf(
+ json_elem, "prefix", "%pFX", p);
+ else
+ vty_out(vty, "%-18pFX\n", p);
+ }
+ break;
+ case LP_TYPE_VRF:
+ if (uj)
+ json_object_string_add(json_elem, "prefix",
+ "VRF");
+ else
+ vty_out(vty, "VRF\n");
+ break;
+ }
+ }
+ if (uj)
+ vty_json(vty, json);
+ return CMD_SUCCESS;
+}
+
+DEFUN(show_bgp_labelpool_chunks, show_bgp_labelpool_chunks_cmd,
+ "show bgp labelpool chunks [json]",
+ SHOW_STR BGP_STR
+ "BGP Labelpool information\n"
+ "BGP Labelpool chunks\n" JSON_STR)
+{
+ bool uj = use_json(argc, argv);
+ json_object *json = NULL, *json_elem;
+ struct listnode *node;
+ struct lp_chunk *chunk;
+ int count;
+
+ if (!lp) {
+ if (uj)
+ vty_out(vty, "{}\n");
+ else
+ vty_out(vty, "No existing BGP labelpool\n");
+ return (CMD_WARNING);
+ }
+
+ if (uj) {
+ count = listcount(lp->chunks);
+ if (!count) {
+ vty_out(vty, "{}\n");
+ return CMD_SUCCESS;
+ }
+ json = json_object_new_array();
+ } else {
+ vty_out(vty, "%10s %10s %10s %10s\n", "First", "Last", "Size",
+ "nfree");
+ vty_out(vty, "-------------------------------------------\n");
+ }
+
+ for (ALL_LIST_ELEMENTS_RO(lp->chunks, node, chunk)) {
+ uint32_t size;
+
+ size = chunk->last - chunk->first + 1;
+
+ if (uj) {
+ json_elem = json_object_new_object();
+ json_object_array_add(json, json_elem);
+ json_object_int_add(json_elem, "first", chunk->first);
+ json_object_int_add(json_elem, "last", chunk->last);
+ json_object_int_add(json_elem, "size", size);
+ json_object_int_add(json_elem, "numberFree",
+ chunk->nfree);
+ } else
+ vty_out(vty, "%10u %10u %10u %10u\n", chunk->first,
+ chunk->last, size, chunk->nfree);
+ }
+ if (uj)
+ vty_json(vty, json);
+ return CMD_SUCCESS;
+}
+
+#if BGP_LABELPOOL_ENABLE_TESTS
+/*------------------------------------------------------------------------
+ * Testing code start
+ *------------------------------------------------------------------------*/
+
+DEFINE_MTYPE_STATIC(BGPD, LABELPOOL_TEST, "Label pool test");
+
+#define LPT_STAT_INSERT_FAIL 0
+#define LPT_STAT_DELETE_FAIL 1
+#define LPT_STAT_ALLOCATED 2
+#define LPT_STAT_DEALLOCATED 3
+#define LPT_STAT_MAX 4
+
+const char *lpt_counter_names[] = {
+ "sl insert failures",
+ "sl delete failures",
+ "labels allocated",
+ "labels deallocated",
+};
+
+static uint8_t lpt_generation;
+static bool lpt_inprogress;
+static struct skiplist *lp_tests;
+static unsigned int lpt_test_cb_tcb_lookup_fails;
+static unsigned int lpt_release_tcb_lookup_fails;
+static unsigned int lpt_test_event_tcb_lookup_fails;
+static unsigned int lpt_stop_tcb_lookup_fails;
+
+struct lp_test {
+ uint8_t generation;
+ unsigned int request_maximum;
+ unsigned int request_blocksize;
+ uintptr_t request_count; /* match type of labelid */
+ int label_type;
+ struct skiplist *labels;
+ struct timeval starttime;
+ struct skiplist *timestamps_alloc;
+ struct skiplist *timestamps_dealloc;
+ struct thread *event_thread;
+ unsigned int counter[LPT_STAT_MAX];
+};
+
+/* test parameters */
+#define LPT_MAX_COUNT 500000 /* get this many labels in all */
+#define LPT_BLKSIZE 10000 /* this many at a time, then yield */
+#define LPT_TS_INTERVAL 10000 /* timestamp every this many labels */
+
+
+static int test_cb(mpls_label_t label, void *labelid, bool allocated)
+{
+ uintptr_t generation;
+ struct lp_test *tcb;
+
+ generation = ((uintptr_t)labelid >> 24) & 0xff;
+
+ if (skiplist_search(lp_tests, (void *)generation, (void **)&tcb)) {
+
+ /* couldn't find current test in progress */
+ ++lpt_test_cb_tcb_lookup_fails;
+ return -1; /* reject allocation */
+ }
+
+ if (allocated) {
+ ++tcb->counter[LPT_STAT_ALLOCATED];
+ if (!(tcb->counter[LPT_STAT_ALLOCATED] % LPT_TS_INTERVAL)) {
+ uintptr_t time_ms;
+
+ time_ms = monotime_since(&tcb->starttime, NULL) / 1000;
+ skiplist_insert(tcb->timestamps_alloc,
+ (void *)(uintptr_t)tcb
+ ->counter[LPT_STAT_ALLOCATED],
+ (void *)time_ms);
+ }
+ if (skiplist_insert(tcb->labels, labelid,
+ (void *)(uintptr_t)label)) {
+ ++tcb->counter[LPT_STAT_INSERT_FAIL];
+ return -1;
+ }
+ } else {
+ ++tcb->counter[LPT_STAT_DEALLOCATED];
+ if (!(tcb->counter[LPT_STAT_DEALLOCATED] % LPT_TS_INTERVAL)) {
+ uintptr_t time_ms;
+
+ time_ms = monotime_since(&tcb->starttime, NULL) / 1000;
+ skiplist_insert(tcb->timestamps_dealloc,
+ (void *)(uintptr_t)tcb
+ ->counter[LPT_STAT_ALLOCATED],
+ (void *)time_ms);
+ }
+ if (skiplist_delete(tcb->labels, labelid, 0)) {
+ ++tcb->counter[LPT_STAT_DELETE_FAIL];
+ return -1;
+ }
+ }
+ return 0;
+}
+
+static void labelpool_test_event_handler(struct thread *thread)
+{
+ struct lp_test *tcb;
+
+ if (skiplist_search(lp_tests, (void *)(uintptr_t)(lpt_generation),
+ (void **)&tcb)) {
+
+ /* couldn't find current test in progress */
+ ++lpt_test_event_tcb_lookup_fails;
+ return;
+ }
+
+ /*
+ * request a bunch of labels
+ */
+ for (unsigned int i = 0; (i < tcb->request_blocksize) &&
+ (tcb->request_count < tcb->request_maximum);
+ ++i) {
+
+ uintptr_t id;
+
+ ++tcb->request_count;
+
+ /*
+ * construct 32-bit id from request_count and generation
+ */
+ id = ((uintptr_t)tcb->generation << 24) |
+ (tcb->request_count & 0x00ffffff);
+ bgp_lp_get(LP_TYPE_VRF, (void *)id, test_cb);
+ }
+
+ if (tcb->request_count < tcb->request_maximum)
+ thread_add_event(bm->master, labelpool_test_event_handler, NULL,
+ 0, &tcb->event_thread);
+}
+
+static void lptest_stop(void)
+{
+ struct lp_test *tcb;
+
+ if (!lpt_inprogress)
+ return;
+
+ if (skiplist_search(lp_tests, (void *)(uintptr_t)(lpt_generation),
+ (void **)&tcb)) {
+
+ /* couldn't find current test in progress */
+ ++lpt_stop_tcb_lookup_fails;
+ return;
+ }
+
+ if (tcb->event_thread)
+ thread_cancel(&tcb->event_thread);
+
+ lpt_inprogress = false;
+}
+
+static int lptest_start(struct vty *vty)
+{
+ struct lp_test *tcb;
+
+ if (lpt_inprogress) {
+ vty_out(vty, "test already in progress\n");
+ return -1;
+ }
+
+ if (skiplist_count(lp_tests) >=
+ (1 << (8 * sizeof(lpt_generation))) - 1) {
+ /*
+ * Too many test runs
+ */
+ vty_out(vty, "too many tests: clear first\n");
+ return -1;
+ }
+
+ /*
+ * We pack the generation and request number into the labelid;
+ * make sure they fit.
+ */
+ unsigned int n1 = LPT_MAX_COUNT;
+ unsigned int sh = 0;
+ unsigned int label_bits;
+
+ label_bits = 8 * (sizeof(tcb->request_count) - sizeof(lpt_generation));
+
+ /* n1 should be same type as tcb->request_maximum */
+ assert(sizeof(n1) == sizeof(tcb->request_maximum));
+
+ while (n1 >>= 1)
+ ++sh;
+ sh += 1; /* number of bits needed to hold LPT_MAX_COUNT */
+
+ if (sh > label_bits) {
+ vty_out(vty,
+ "Sorry, test iteration count too big on this platform (LPT_MAX_COUNT %u, need %u bits, but label_bits is only %u)\n",
+ LPT_MAX_COUNT, sh, label_bits);
+ return -1;
+ }
+
+ lpt_inprogress = true;
+ ++lpt_generation;
+
+ tcb = XCALLOC(MTYPE_LABELPOOL_TEST, sizeof(*tcb));
+
+ tcb->generation = lpt_generation;
+ tcb->label_type = LP_TYPE_VRF;
+ tcb->request_maximum = LPT_MAX_COUNT;
+ tcb->request_blocksize = LPT_BLKSIZE;
+ tcb->labels = skiplist_new(0, NULL, NULL);
+ tcb->timestamps_alloc = skiplist_new(0, NULL, NULL);
+ tcb->timestamps_dealloc = skiplist_new(0, NULL, NULL);
+ thread_add_event(bm->master, labelpool_test_event_handler, NULL, 0,
+ &tcb->event_thread);
+ monotime(&tcb->starttime);
+
+ skiplist_insert(lp_tests, (void *)(uintptr_t)tcb->generation, tcb);
+ return 0;
+}
+
+DEFPY(start_labelpool_perf_test, start_labelpool_perf_test_cmd,
+ "debug bgp lptest start",
+ DEBUG_STR BGP_STR
+ "label pool test\n"
+ "start\n")
+{
+ lptest_start(vty);
+ return CMD_SUCCESS;
+}
+
+static void lptest_print_stats(struct vty *vty, struct lp_test *tcb)
+{
+ unsigned int i;
+
+ vty_out(vty, "Global Lookup Failures in test_cb: %5u\n",
+ lpt_test_cb_tcb_lookup_fails);
+ vty_out(vty, "Global Lookup Failures in release: %5u\n",
+ lpt_release_tcb_lookup_fails);
+ vty_out(vty, "Global Lookup Failures in event: %5u\n",
+ lpt_test_event_tcb_lookup_fails);
+ vty_out(vty, "Global Lookup Failures in stop: %5u\n",
+ lpt_stop_tcb_lookup_fails);
+ vty_out(vty, "\n");
+
+ if (!tcb) {
+ if (skiplist_search(lp_tests, (void *)(uintptr_t)lpt_generation,
+ (void **)&tcb)) {
+ vty_out(vty, "Error: can't find test %u\n",
+ lpt_generation);
+ return;
+ }
+ }
+
+ vty_out(vty, "Test Generation %u:\n", tcb->generation);
+
+ vty_out(vty, "Counter Value\n");
+ for (i = 0; i < LPT_STAT_MAX; ++i) {
+ vty_out(vty, "%20s: %10u\n", lpt_counter_names[i],
+ tcb->counter[i]);
+ }
+ vty_out(vty, "\n");
+
+ if (tcb->timestamps_alloc) {
+ void *Key;
+ void *Value;
+ void *cursor;
+
+ float elapsed;
+
+ vty_out(vty, "%10s %10s\n", "Count", "Seconds");
+
+ cursor = NULL;
+ while (!skiplist_next(tcb->timestamps_alloc, &Key, &Value,
+ &cursor)) {
+
+ elapsed = ((float)(uintptr_t)Value) / 1000;
+
+ vty_out(vty, "%10llu %10.3f\n",
+ (unsigned long long)(uintptr_t)Key, elapsed);
+ }
+ vty_out(vty, "\n");
+ }
+}
+
+DEFPY(show_labelpool_perf_test, show_labelpool_perf_test_cmd,
+ "debug bgp lptest show",
+ DEBUG_STR BGP_STR
+ "label pool test\n"
+ "show\n")
+{
+
+ if (lp_tests) {
+ void *Key;
+ void *Value;
+ void *cursor;
+
+ cursor = NULL;
+ while (!skiplist_next(lp_tests, &Key, &Value, &cursor)) {
+ lptest_print_stats(vty, (struct lp_test *)Value);
+ }
+ } else {
+ vty_out(vty, "no test results\n");
+ }
+ return CMD_SUCCESS;
+}
+
+DEFPY(stop_labelpool_perf_test, stop_labelpool_perf_test_cmd,
+ "debug bgp lptest stop",
+ DEBUG_STR BGP_STR
+ "label pool test\n"
+ "stop\n")
+{
+
+ if (lpt_inprogress) {
+ lptest_stop();
+ lptest_print_stats(vty, NULL);
+ } else {
+ vty_out(vty, "no test in progress\n");
+ }
+ return CMD_SUCCESS;
+}
+
+DEFPY(clear_labelpool_perf_test, clear_labelpool_perf_test_cmd,
+ "debug bgp lptest clear",
+ DEBUG_STR BGP_STR
+ "label pool test\n"
+ "clear\n")
+{
+
+ if (lpt_inprogress) {
+ lptest_stop();
+ }
+ if (lp_tests) {
+ while (!skiplist_first(lp_tests, NULL, NULL))
+ /* del function of skiplist cleans up tcbs */
+ skiplist_delete_first(lp_tests);
+ }
+ return CMD_SUCCESS;
+}
+
+/*
+ * With the "release" command, we can release labels at intervals through
+ * the ID space. Thus we can to exercise the bitfield-wrapping behavior
+ * of the allocator in a subsequent test.
+ */
+/* clang-format off */
+DEFPY(release_labelpool_perf_test, release_labelpool_perf_test_cmd,
+ "debug bgp lptest release test GENERATION$generation every (1-5)$every_nth",
+ DEBUG_STR
+ BGP_STR
+ "label pool test\n"
+ "release labels\n"
+ "\"test\"\n"
+ "test number\n"
+ "\"every\"\n"
+ "label fraction denominator\n")
+{
+ /* clang-format on */
+
+ unsigned long testnum;
+ char *end;
+ struct lp_test *tcb;
+
+ testnum = strtoul(generation, &end, 0);
+ if (*end) {
+ vty_out(vty, "Invalid test number: \"%s\"\n", generation);
+ return CMD_SUCCESS;
+ }
+ if (lpt_inprogress && (testnum == lpt_generation)) {
+ vty_out(vty,
+ "Error: Test %lu is still in progress (stop first)\n",
+ testnum);
+ return CMD_SUCCESS;
+ }
+
+ if (skiplist_search(lp_tests, (void *)(uintptr_t)testnum,
+ (void **)&tcb)) {
+
+ /* couldn't find current test in progress */
+ vty_out(vty, "Error: Can't look up test number: \"%lu\"\n",
+ testnum);
+ ++lpt_release_tcb_lookup_fails;
+ return CMD_SUCCESS;
+ }
+
+ void *Key, *cKey;
+ void *Value, *cValue;
+ void *cursor;
+ unsigned int iteration;
+ int rc;
+
+ cursor = NULL;
+ iteration = 0;
+ rc = skiplist_next(tcb->labels, &Key, &Value, &cursor);
+
+ while (!rc) {
+ cKey = Key;
+ cValue = Value;
+
+ /* find next item before we delete this one */
+ rc = skiplist_next(tcb->labels, &Key, &Value, &cursor);
+
+ if (!(iteration % every_nth)) {
+ bgp_lp_release(tcb->label_type, cKey,
+ (mpls_label_t)(uintptr_t)cValue);
+ skiplist_delete(tcb->labels, cKey, NULL);
+ ++tcb->counter[LPT_STAT_DEALLOCATED];
+ }
+ ++iteration;
+ }
+
+ return CMD_SUCCESS;
+}
+
+static void lptest_delete(void *val)
+{
+ struct lp_test *tcb = (struct lp_test *)val;
+ void *Key;
+ void *Value;
+ void *cursor;
+
+ if (tcb->labels) {
+ cursor = NULL;
+ while (!skiplist_next(tcb->labels, &Key, &Value, &cursor))
+ bgp_lp_release(tcb->label_type, Key,
+ (mpls_label_t)(uintptr_t)Value);
+ skiplist_free(tcb->labels);
+ tcb->labels = NULL;
+ }
+ if (tcb->timestamps_alloc) {
+ cursor = NULL;
+ skiplist_free(tcb->timestamps_alloc);
+ tcb->timestamps_alloc = NULL;
+ }
+
+ if (tcb->timestamps_dealloc) {
+ cursor = NULL;
+ skiplist_free(tcb->timestamps_dealloc);
+ tcb->timestamps_dealloc = NULL;
+ }
+
+ if (tcb->event_thread)
+ thread_cancel(&tcb->event_thread);
+
+ memset(tcb, 0, sizeof(*tcb));
+
+ XFREE(MTYPE_LABELPOOL_TEST, tcb);
+}
+
+static void lptest_init(void)
+{
+ lp_tests = skiplist_new(0, NULL, lptest_delete);
+}
+
+static void lptest_finish(void)
+{
+ if (lp_tests) {
+ skiplist_free(lp_tests);
+ lp_tests = NULL;
+ }
+}
+
+/*------------------------------------------------------------------------
+ * Testing code end
+ *------------------------------------------------------------------------*/
+#endif /* BGP_LABELPOOL_ENABLE_TESTS */
+
+void bgp_lp_vty_init(void)
+{
+ install_element(VIEW_NODE, &show_bgp_labelpool_summary_cmd);
+ install_element(VIEW_NODE, &show_bgp_labelpool_ledger_cmd);
+ install_element(VIEW_NODE, &show_bgp_labelpool_inuse_cmd);
+ install_element(VIEW_NODE, &show_bgp_labelpool_requests_cmd);
+ install_element(VIEW_NODE, &show_bgp_labelpool_chunks_cmd);
+
+#if BGP_LABELPOOL_ENABLE_TESTS
+ install_element(ENABLE_NODE, &start_labelpool_perf_test_cmd);
+ install_element(ENABLE_NODE, &show_labelpool_perf_test_cmd);
+ install_element(ENABLE_NODE, &stop_labelpool_perf_test_cmd);
+ install_element(ENABLE_NODE, &release_labelpool_perf_test_cmd);
+ install_element(ENABLE_NODE, &clear_labelpool_perf_test_cmd);
+#endif /* BGP_LABELPOOL_ENABLE_TESTS */
+}
diff --git a/bgpd/bgp_labelpool.h b/bgpd/bgp_labelpool.h
new file mode 100644
index 0000000..2f3ffe4
--- /dev/null
+++ b/bgpd/bgp_labelpool.h
@@ -0,0 +1,57 @@
+/*
+ * BGP Label Pool - Manage label chunk allocations from zebra asynchronously
+ *
+ * Copyright (C) 2018 LabN Consulting, L.L.C.
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License as published by the Free
+ * Software Foundation; either version 2 of the License, or (at your option)
+ * any later version.
+ *
+ * This program is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
+ * more details.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program; see the file COPYING; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
+ */
+
+#ifndef _FRR_BGP_LABELPOOL_H
+#define _FRR_BGP_LABELPOOL_H
+
+#include <zebra.h>
+
+#include "mpls.h"
+
+/*
+ * Types used in bgp_lp_get for debug tracking; add more as needed
+ */
+#define LP_TYPE_VRF 0x00000001
+#define LP_TYPE_BGP_LU 0x00000002
+
+PREDECL_LIST(lp_fifo);
+
+struct labelpool {
+ struct skiplist *ledger; /* all requests */
+ struct skiplist *inuse; /* individual labels */
+ struct list *chunks; /* granted by zebra */
+ struct lp_fifo_head requests; /* blocked on zebra */
+ struct work_queue *callback_q;
+ uint32_t pending_count; /* requested from zebra */
+ uint32_t reconnect_count; /* zebra reconnections */
+ uint32_t next_chunksize; /* request this many labels */
+};
+
+extern void bgp_lp_init(struct thread_master *master, struct labelpool *pool);
+extern void bgp_lp_finish(void);
+extern void bgp_lp_get(int type, void *labelid,
+ int (*cbfunc)(mpls_label_t label, void *labelid, bool allocated));
+extern void bgp_lp_release(int type, void *labelid, mpls_label_t label);
+extern void bgp_lp_event_chunk(uint8_t keep, uint32_t first, uint32_t last);
+extern void bgp_lp_event_zebra_down(void);
+extern void bgp_lp_event_zebra_up(void);
+extern void bgp_lp_vty_init(void);
+
+#endif /* _FRR_BGP_LABELPOOL_H */
diff --git a/bgpd/bgp_lcommunity.c b/bgpd/bgp_lcommunity.c
new file mode 100644
index 0000000..223882b
--- /dev/null
+++ b/bgpd/bgp_lcommunity.c
@@ -0,0 +1,690 @@
+/* BGP Large Communities Attribute
+ *
+ * Copyright (C) 2016 Keyur Patel <keyur@arrcus.com>
+ *
+ * This file is part of FRRouting (FRR).
+ *
+ * FRR is free software; you can redistribute it and/or modify it under the
+ * terms of the GNU General Public License as published by the Free Software
+ * Foundation; either version 2, or (at your option) any later version.
+ *
+ * FRR is distributed in the hope that it will be useful, but WITHOUT ANY
+ * WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
+ * FOR A PARTICULAR PURPOSE. See the GNU General Public License for more
+ * details.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program; see the file COPYING; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
+ */
+
+#include <zebra.h>
+
+#include "hash.h"
+#include "memory.h"
+#include "prefix.h"
+#include "command.h"
+#include "filter.h"
+#include "jhash.h"
+#include "stream.h"
+
+#include "bgpd/bgpd.h"
+#include "bgpd/bgp_lcommunity.h"
+#include "bgpd/bgp_community_alias.h"
+#include "bgpd/bgp_aspath.h"
+
+/* Hash of community attribute. */
+static struct hash *lcomhash;
+
+/* Allocate a new lcommunities. */
+static struct lcommunity *lcommunity_new(void)
+{
+ return XCALLOC(MTYPE_LCOMMUNITY, sizeof(struct lcommunity));
+}
+
+/* Allocate lcommunities. */
+void lcommunity_free(struct lcommunity **lcom)
+{
+ if (!(*lcom))
+ return;
+
+ XFREE(MTYPE_LCOMMUNITY_VAL, (*lcom)->val);
+ XFREE(MTYPE_LCOMMUNITY_STR, (*lcom)->str);
+ if ((*lcom)->json)
+ json_object_free((*lcom)->json);
+ XFREE(MTYPE_LCOMMUNITY, *lcom);
+}
+
+static void lcommunity_hash_free(struct lcommunity *lcom)
+{
+ lcommunity_free(&lcom);
+}
+
+/* Add a new Large Communities value to Large Communities
+ Attribute structure. When the value is already exists in the
+ structure, we don't add the value. Newly added value is sorted by
+ numerical order. When the value is added to the structure return 1
+ else return 0. */
+static bool lcommunity_add_val(struct lcommunity *lcom,
+ struct lcommunity_val *lval)
+{
+ uint8_t *p;
+ int ret;
+ int c;
+
+ /* When this is fist value, just add it. */
+ if (lcom->val == NULL) {
+ lcom->size++;
+ lcom->val = XMALLOC(MTYPE_LCOMMUNITY_VAL, lcom_length(lcom));
+ memcpy(lcom->val, lval->val, LCOMMUNITY_SIZE);
+ return true;
+ }
+
+ /* If the value already exists in the structure return 0. */
+ c = 0;
+ for (p = lcom->val; c < lcom->size; p += LCOMMUNITY_SIZE, c++) {
+ ret = memcmp(p, lval->val, LCOMMUNITY_SIZE);
+ if (ret == 0)
+ return false;
+ if (ret > 0)
+ break;
+ }
+
+ /* Add the value to the structure with numerical sorting. */
+ lcom->size++;
+ lcom->val =
+ XREALLOC(MTYPE_LCOMMUNITY_VAL, lcom->val, lcom_length(lcom));
+
+ memmove(lcom->val + (c + 1) * LCOMMUNITY_SIZE,
+ lcom->val + c * LCOMMUNITY_SIZE,
+ (lcom->size - 1 - c) * LCOMMUNITY_SIZE);
+ memcpy(lcom->val + c * LCOMMUNITY_SIZE, lval->val, LCOMMUNITY_SIZE);
+
+ return true;
+}
+
+/* This function takes pointer to Large Communites structure then
+ create a new Large Communities structure by uniq and sort each
+ Large Communities value. */
+struct lcommunity *lcommunity_uniq_sort(struct lcommunity *lcom)
+{
+ int i;
+ struct lcommunity *new;
+ struct lcommunity_val *lval;
+
+ if (!lcom)
+ return NULL;
+
+ new = lcommunity_new();
+
+ for (i = 0; i < lcom->size; i++) {
+ lval = (struct lcommunity_val *)(lcom->val
+ + (i * LCOMMUNITY_SIZE));
+ lcommunity_add_val(new, lval);
+ }
+ return new;
+}
+
+/* Parse Large Communites Attribute in BGP packet. */
+struct lcommunity *lcommunity_parse(uint8_t *pnt, unsigned short length)
+{
+ struct lcommunity tmp;
+ struct lcommunity *new;
+
+ /* Length check. */
+ if (length % LCOMMUNITY_SIZE)
+ return NULL;
+
+ /* Prepare tmporary structure for making a new Large Communities
+ Attribute. */
+ tmp.size = length / LCOMMUNITY_SIZE;
+ tmp.val = pnt;
+
+ /* Create a new Large Communities Attribute by uniq and sort each
+ Large Communities value */
+ new = lcommunity_uniq_sort(&tmp);
+
+ return lcommunity_intern(new);
+}
+
+/* Duplicate the Large Communities Attribute structure. */
+struct lcommunity *lcommunity_dup(struct lcommunity *lcom)
+{
+ struct lcommunity *new;
+
+ new = lcommunity_new();
+ new->size = lcom->size;
+ if (new->size) {
+ new->val = XMALLOC(MTYPE_LCOMMUNITY_VAL, lcom_length(lcom));
+ memcpy(new->val, lcom->val, lcom_length(lcom));
+ } else
+ new->val = NULL;
+ return new;
+}
+
+/* Merge two Large Communities Attribute structure. */
+struct lcommunity *lcommunity_merge(struct lcommunity *lcom1,
+ struct lcommunity *lcom2)
+{
+ lcom1->val = XREALLOC(MTYPE_LCOMMUNITY_VAL, lcom1->val,
+ lcom_length(lcom1) + lcom_length(lcom2));
+
+ memcpy(lcom1->val + lcom_length(lcom1), lcom2->val, lcom_length(lcom2));
+ lcom1->size += lcom2->size;
+
+ return lcom1;
+}
+
+static void set_lcommunity_string(struct lcommunity *lcom, bool make_json,
+ bool translate_alias)
+{
+ int i;
+ int len;
+ char *str_buf;
+ const uint8_t *pnt;
+ uint32_t global, local1, local2;
+ json_object *json_lcommunity_list = NULL;
+ json_object *json_string = NULL;
+
+ /* 3 32-bit integers, 2 colons, and a space */
+#define LCOMMUNITY_STRLEN (10 * 3 + 2 + 1)
+
+ if (!lcom)
+ return;
+
+ if (make_json) {
+ lcom->json = json_object_new_object();
+ json_lcommunity_list = json_object_new_array();
+ }
+
+ if (lcom->size == 0) {
+ str_buf = XCALLOC(MTYPE_LCOMMUNITY_STR, 1);
+
+ if (make_json) {
+ json_object_string_add(lcom->json, "string", "");
+ json_object_object_add(lcom->json, "list",
+ json_lcommunity_list);
+ }
+
+ lcom->str = str_buf;
+ return;
+ }
+
+ /* 1 space + lcom->size lcom strings + null terminator */
+ size_t str_buf_sz = (LCOMMUNITY_STRLEN * lcom->size) + 2;
+ str_buf = XCALLOC(MTYPE_LCOMMUNITY_STR, str_buf_sz);
+
+ len = 0;
+ for (i = 0; i < lcom->size; i++) {
+ if (i > 0)
+ len = strlcat(str_buf, " ", str_buf_sz);
+
+ pnt = lcom->val + (i * LCOMMUNITY_SIZE);
+ pnt = ptr_get_be32(pnt, &global);
+ pnt = ptr_get_be32(pnt, &local1);
+ pnt = ptr_get_be32(pnt, &local2);
+ (void)pnt;
+
+ char lcsb[LCOMMUNITY_STRLEN + 1];
+
+ snprintf(lcsb, sizeof(lcsb), "%u:%u:%u", global, local1,
+ local2);
+
+ /*
+ * Aliases can cause havoc, if the alias length is greater
+ * than the LCOMMUNITY_STRLEN for a particular item
+ * then we need to realloc the memory associated
+ * with the string so that it can fit
+ */
+ const char *com2alias =
+ translate_alias ? bgp_community2alias(lcsb) : lcsb;
+ size_t individual_len = strlen(com2alias);
+ if (individual_len + len > str_buf_sz) {
+ str_buf_sz = individual_len + len + 1;
+ str_buf = XREALLOC(MTYPE_LCOMMUNITY_STR, str_buf,
+ str_buf_sz);
+ }
+
+ len = strlcat(str_buf, com2alias, str_buf_sz);
+
+ if (make_json) {
+ json_string = json_object_new_string(com2alias);
+ json_object_array_add(json_lcommunity_list,
+ json_string);
+ }
+ }
+
+ if (make_json) {
+ json_object_string_add(lcom->json, "string", str_buf);
+ json_object_object_add(lcom->json, "list",
+ json_lcommunity_list);
+ }
+
+ lcom->str = str_buf;
+}
+
+/* Intern Large Communities Attribute. */
+struct lcommunity *lcommunity_intern(struct lcommunity *lcom)
+{
+ struct lcommunity *find;
+
+ assert(lcom->refcnt == 0);
+
+ find = (struct lcommunity *)hash_get(lcomhash, lcom, hash_alloc_intern);
+
+ if (find != lcom)
+ lcommunity_free(&lcom);
+
+ find->refcnt++;
+
+ if (!find->str)
+ set_lcommunity_string(find, false, true);
+
+ return find;
+}
+
+/* Unintern Large Communities Attribute. */
+void lcommunity_unintern(struct lcommunity **lcom)
+{
+ struct lcommunity *ret;
+
+ if (!*lcom)
+ return;
+
+ if ((*lcom)->refcnt)
+ (*lcom)->refcnt--;
+
+ /* Pull off from hash. */
+ if ((*lcom)->refcnt == 0) {
+ /* Large community must be in the hash. */
+ ret = (struct lcommunity *)hash_release(lcomhash, *lcom);
+ assert(ret != NULL);
+
+ lcommunity_free(lcom);
+ }
+}
+
+/* Return string representation of lcommunities attribute. */
+char *lcommunity_str(struct lcommunity *lcom, bool make_json,
+ bool translate_alias)
+{
+ if (!lcom)
+ return NULL;
+
+ if (make_json && !lcom->json && lcom->str)
+ XFREE(MTYPE_LCOMMUNITY_STR, lcom->str);
+
+ if (!lcom->str)
+ set_lcommunity_string(lcom, make_json, translate_alias);
+
+ return lcom->str;
+}
+
+/* Utility function to make hash key. */
+unsigned int lcommunity_hash_make(const void *arg)
+{
+ const struct lcommunity *lcom = arg;
+ int size = lcom_length(lcom);
+
+ return jhash(lcom->val, size, 0xab125423);
+}
+
+/* Compare two Large Communities Attribute structure. */
+bool lcommunity_cmp(const void *arg1, const void *arg2)
+{
+ const struct lcommunity *lcom1 = arg1;
+ const struct lcommunity *lcom2 = arg2;
+
+ if (lcom1 == NULL && lcom2 == NULL)
+ return true;
+
+ if (lcom1 == NULL || lcom2 == NULL)
+ return false;
+
+ return (lcom1->size == lcom2->size
+ && memcmp(lcom1->val, lcom2->val, lcom_length(lcom1)) == 0);
+}
+
+/* Return communities hash. */
+struct hash *lcommunity_hash(void)
+{
+ return lcomhash;
+}
+
+/* Initialize Large Comminities related hash. */
+void lcommunity_init(void)
+{
+ lcomhash = hash_create(lcommunity_hash_make, lcommunity_cmp,
+ "BGP lcommunity hash");
+}
+
+void lcommunity_finish(void)
+{
+ hash_clean(lcomhash, (void (*)(void *))lcommunity_hash_free);
+ hash_free(lcomhash);
+ lcomhash = NULL;
+}
+
+/* Get next Large Communities token from the string.
+ * Assumes str is space-delimeted and describes 0 or more
+ * valid large communities
+ */
+static const char *lcommunity_gettoken(const char *str,
+ struct lcommunity_val *lval)
+{
+ const char *p = str;
+
+ /* Skip white space. */
+ while (isspace((unsigned char)*p)) {
+ p++;
+ str++;
+ }
+
+ /* Check the end of the line. */
+ if (*p == '\0')
+ return NULL;
+
+ /* Community value. */
+ int separator = 0;
+ int digit = 0;
+ uint32_t globaladmin = 0;
+ uint32_t localdata1 = 0;
+ uint32_t localdata2 = 0;
+
+ while (*p && *p != ' ') {
+ /* large community valid chars */
+ assert(isdigit((unsigned char)*p) || *p == ':');
+
+ if (*p == ':') {
+ separator++;
+ digit = 0;
+ if (separator == 1) {
+ globaladmin = localdata2;
+ } else {
+ localdata1 = localdata2;
+ }
+ localdata2 = 0;
+ } else {
+ digit = 1;
+ /* left shift the accumulated value and add current
+ * digit
+ */
+ localdata2 *= 10;
+ localdata2 += (*p - '0');
+ }
+ p++;
+ }
+
+ /* Assert str was a valid large community */
+ assert(separator == 2 && digit == 1);
+
+ /*
+ * Copy the large comm.
+ */
+ lval->val[0] = (globaladmin >> 24) & 0xff;
+ lval->val[1] = (globaladmin >> 16) & 0xff;
+ lval->val[2] = (globaladmin >> 8) & 0xff;
+ lval->val[3] = globaladmin & 0xff;
+ lval->val[4] = (localdata1 >> 24) & 0xff;
+ lval->val[5] = (localdata1 >> 16) & 0xff;
+ lval->val[6] = (localdata1 >> 8) & 0xff;
+ lval->val[7] = localdata1 & 0xff;
+ lval->val[8] = (localdata2 >> 24) & 0xff;
+ lval->val[9] = (localdata2 >> 16) & 0xff;
+ lval->val[10] = (localdata2 >> 8) & 0xff;
+ lval->val[11] = localdata2 & 0xff;
+
+ return p;
+}
+
+/*
+ Convert string to large community attribute.
+ When type is already known, please specify both str and type.
+
+ When string includes keyword for each large community value.
+ Please specify keyword_included as non-zero value.
+*/
+struct lcommunity *lcommunity_str2com(const char *str)
+{
+ struct lcommunity *lcom = NULL;
+ struct lcommunity_val lval;
+
+ if (!lcommunity_list_valid(str, LARGE_COMMUNITY_LIST_STANDARD))
+ return NULL;
+
+ do {
+ str = lcommunity_gettoken(str, &lval);
+ if (lcom == NULL)
+ lcom = lcommunity_new();
+ lcommunity_add_val(lcom, &lval);
+ } while (str);
+
+ return lcom;
+}
+
+bool lcommunity_include(struct lcommunity *lcom, uint8_t *ptr)
+{
+ int i;
+ uint8_t *lcom_ptr;
+
+ for (i = 0; i < lcom->size; i++) {
+ lcom_ptr = lcom->val + (i * LCOMMUNITY_SIZE);
+ if (memcmp(ptr, lcom_ptr, LCOMMUNITY_SIZE) == 0)
+ return true;
+ }
+ return false;
+}
+
+bool lcommunity_match(const struct lcommunity *lcom1,
+ const struct lcommunity *lcom2)
+{
+ int i = 0;
+ int j = 0;
+
+ if (lcom1 == NULL && lcom2 == NULL)
+ return true;
+
+ if (lcom1 == NULL || lcom2 == NULL)
+ return false;
+
+ if (lcom1->size < lcom2->size)
+ return false;
+
+ /* Every community on com2 needs to be on com1 for this to match */
+ while (i < lcom1->size && j < lcom2->size) {
+ if (memcmp(lcom1->val + (i * LCOMMUNITY_SIZE),
+ lcom2->val + (j * LCOMMUNITY_SIZE), LCOMMUNITY_SIZE)
+ == 0)
+ j++;
+ i++;
+ }
+
+ if (j == lcom2->size)
+ return true;
+ else
+ return false;
+}
+
+/* Delete one lcommunity. */
+void lcommunity_del_val(struct lcommunity *lcom, uint8_t *ptr)
+{
+ int i = 0;
+ int c = 0;
+
+ if (!lcom->val)
+ return;
+
+ while (i < lcom->size) {
+ if (memcmp(lcom->val + i * LCOMMUNITY_SIZE, ptr,
+ LCOMMUNITY_SIZE)
+ == 0) {
+ c = lcom->size - i - 1;
+
+ if (c > 0)
+ memmove(lcom->val + i * LCOMMUNITY_SIZE,
+ lcom->val + (i + 1) * LCOMMUNITY_SIZE,
+ c * LCOMMUNITY_SIZE);
+
+ lcom->size--;
+
+ if (lcom->size > 0)
+ lcom->val =
+ XREALLOC(MTYPE_LCOMMUNITY_VAL,
+ lcom->val, lcom_length(lcom));
+ else {
+ XFREE(MTYPE_LCOMMUNITY_VAL, lcom->val);
+ }
+ return;
+ }
+ i++;
+ }
+}
+
+static struct lcommunity *bgp_aggr_lcommunity_lookup(
+ struct bgp_aggregate *aggregate,
+ struct lcommunity *lcommunity)
+{
+ return hash_lookup(aggregate->lcommunity_hash, lcommunity);
+}
+
+static void *bgp_aggr_lcommunty_hash_alloc(void *p)
+{
+ struct lcommunity *ref = (struct lcommunity *)p;
+ struct lcommunity *lcommunity = NULL;
+
+ lcommunity = lcommunity_dup(ref);
+ return lcommunity;
+}
+
+static void bgp_aggr_lcommunity_prepare(struct hash_bucket *hb, void *arg)
+{
+ struct lcommunity *hb_lcommunity = hb->data;
+ struct lcommunity **aggr_lcommunity = arg;
+
+ if (*aggr_lcommunity)
+ *aggr_lcommunity = lcommunity_merge(*aggr_lcommunity,
+ hb_lcommunity);
+ else
+ *aggr_lcommunity = lcommunity_dup(hb_lcommunity);
+}
+
+void bgp_aggr_lcommunity_remove(void *arg)
+{
+ struct lcommunity *lcommunity = arg;
+
+ lcommunity_free(&lcommunity);
+}
+
+void bgp_compute_aggregate_lcommunity(struct bgp_aggregate *aggregate,
+ struct lcommunity *lcommunity)
+{
+
+ bgp_compute_aggregate_lcommunity_hash(aggregate, lcommunity);
+ bgp_compute_aggregate_lcommunity_val(aggregate);
+}
+
+void bgp_compute_aggregate_lcommunity_hash(struct bgp_aggregate *aggregate,
+ struct lcommunity *lcommunity)
+{
+
+ struct lcommunity *aggr_lcommunity = NULL;
+
+ if ((aggregate == NULL) || (lcommunity == NULL))
+ return;
+
+ /* Create hash if not already created.
+ */
+ if (aggregate->lcommunity_hash == NULL)
+ aggregate->lcommunity_hash = hash_create(
+ lcommunity_hash_make, lcommunity_cmp,
+ "BGP Aggregator lcommunity hash");
+
+ aggr_lcommunity = bgp_aggr_lcommunity_lookup(aggregate, lcommunity);
+ if (aggr_lcommunity == NULL) {
+ /* Insert lcommunity into hash.
+ */
+ aggr_lcommunity = hash_get(aggregate->lcommunity_hash,
+ lcommunity,
+ bgp_aggr_lcommunty_hash_alloc);
+ }
+
+ /* Increment reference counter.
+ */
+ aggr_lcommunity->refcnt++;
+}
+
+void bgp_compute_aggregate_lcommunity_val(struct bgp_aggregate *aggregate)
+{
+ struct lcommunity *lcommerge = NULL;
+
+ if (aggregate == NULL)
+ return;
+
+ /* Re-compute aggregate's lcommunity.
+ */
+ if (aggregate->lcommunity)
+ lcommunity_free(&aggregate->lcommunity);
+ if (aggregate->lcommunity_hash &&
+ aggregate->lcommunity_hash->count) {
+ hash_iterate(aggregate->lcommunity_hash,
+ bgp_aggr_lcommunity_prepare,
+ &aggregate->lcommunity);
+ lcommerge = aggregate->lcommunity;
+ aggregate->lcommunity = lcommunity_uniq_sort(lcommerge);
+ if (lcommerge)
+ lcommunity_free(&lcommerge);
+ }
+}
+
+void bgp_remove_lcommunity_from_aggregate(struct bgp_aggregate *aggregate,
+ struct lcommunity *lcommunity)
+{
+ struct lcommunity *aggr_lcommunity = NULL;
+ struct lcommunity *ret_lcomm = NULL;
+
+ if ((!aggregate)
+ || (!aggregate->lcommunity_hash)
+ || (!lcommunity))
+ return;
+
+ /* Look-up the lcommunity in the hash.
+ */
+ aggr_lcommunity = bgp_aggr_lcommunity_lookup(aggregate, lcommunity);
+ if (aggr_lcommunity) {
+ aggr_lcommunity->refcnt--;
+
+ if (aggr_lcommunity->refcnt == 0) {
+ ret_lcomm = hash_release(aggregate->lcommunity_hash,
+ aggr_lcommunity);
+ lcommunity_free(&ret_lcomm);
+
+ bgp_compute_aggregate_lcommunity_val(aggregate);
+
+ }
+ }
+}
+
+void bgp_remove_lcomm_from_aggregate_hash(struct bgp_aggregate *aggregate,
+ struct lcommunity *lcommunity)
+{
+ struct lcommunity *aggr_lcommunity = NULL;
+ struct lcommunity *ret_lcomm = NULL;
+
+ if ((!aggregate)
+ || (!aggregate->lcommunity_hash)
+ || (!lcommunity))
+ return;
+
+ /* Look-up the lcommunity in the hash.
+ */
+ aggr_lcommunity = bgp_aggr_lcommunity_lookup(aggregate, lcommunity);
+ if (aggr_lcommunity) {
+ aggr_lcommunity->refcnt--;
+
+ if (aggr_lcommunity->refcnt == 0) {
+ ret_lcomm = hash_release(aggregate->lcommunity_hash,
+ aggr_lcommunity);
+ lcommunity_free(&ret_lcomm);
+ }
+ }
+}
diff --git a/bgpd/bgp_lcommunity.h b/bgpd/bgp_lcommunity.h
new file mode 100644
index 0000000..b9b5fe3
--- /dev/null
+++ b/bgpd/bgp_lcommunity.h
@@ -0,0 +1,95 @@
+/* BGP Large Communities Attribute.
+ *
+ * Copyright (C) 2016 Keyur Patel <keyur@arrcus.com>
+ *
+ * This file is part of FRRouting (FRR).
+ *
+ * FRR is free software; you can redistribute it and/or modify it under the
+ * terms of the GNU General Public License as published by the Free Software
+ * Foundation; either version 2, or (at your option) any later version.
+ *
+ * FRR is distributed in the hope that it will be useful, but WITHOUT ANY
+ * WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
+ * FOR A PARTICULAR PURPOSE. See the GNU General Public License for more
+ * details.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program; see the file COPYING; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
+ */
+
+#ifndef _QUAGGA_BGP_LCOMMUNITY_H
+#define _QUAGGA_BGP_LCOMMUNITY_H
+
+#include "lib/json.h"
+#include "bgpd/bgp_route.h"
+#include "bgpd/bgp_clist.h"
+
+/* Large Communities value is twelve octets long. */
+#define LCOMMUNITY_SIZE 12
+
+/* Large Communities attribute. */
+struct lcommunity {
+ /* Reference counter. */
+ unsigned long refcnt;
+
+ /* Size of Extended Communities attribute. */
+ int size;
+
+ /* Large Communities value. */
+ uint8_t *val;
+
+ /* Large Communities as a json object */
+ json_object *json;
+
+ /* Human readable format string. */
+ char *str;
+};
+
+/* Large community value is 12 octets. */
+struct lcommunity_val {
+ char val[LCOMMUNITY_SIZE];
+};
+
+#define lcom_length(X) ((X)->size * LCOMMUNITY_SIZE)
+
+extern void lcommunity_init(void);
+extern void lcommunity_finish(void);
+extern void lcommunity_free(struct lcommunity **);
+extern struct lcommunity *lcommunity_parse(uint8_t *, unsigned short);
+extern struct lcommunity *lcommunity_dup(struct lcommunity *);
+extern struct lcommunity *lcommunity_merge(struct lcommunity *,
+ struct lcommunity *);
+extern struct lcommunity *lcommunity_uniq_sort(struct lcommunity *);
+extern struct lcommunity *lcommunity_intern(struct lcommunity *);
+extern bool lcommunity_cmp(const void *arg1, const void *arg2);
+extern void lcommunity_unintern(struct lcommunity **);
+extern unsigned int lcommunity_hash_make(const void *);
+extern struct hash *lcommunity_hash(void);
+extern struct lcommunity *lcommunity_str2com(const char *);
+extern bool lcommunity_match(const struct lcommunity *,
+ const struct lcommunity *);
+extern char *lcommunity_str(struct lcommunity *, bool make_json,
+ bool translate_alias);
+extern bool lcommunity_include(struct lcommunity *lcom, uint8_t *ptr);
+extern void lcommunity_del_val(struct lcommunity *lcom, uint8_t *ptr);
+
+extern void bgp_compute_aggregate_lcommunity(
+ struct bgp_aggregate *aggregate,
+ struct lcommunity *lcommunity);
+
+extern void bgp_compute_aggregate_lcommunity_hash(
+ struct bgp_aggregate *aggregate,
+ struct lcommunity *lcommunity);
+extern void bgp_compute_aggregate_lcommunity_val(
+ struct bgp_aggregate *aggregate);
+
+extern void bgp_remove_lcommunity_from_aggregate(
+ struct bgp_aggregate *aggregate,
+ struct lcommunity *lcommunity);
+extern void bgp_remove_lcomm_from_aggregate_hash(
+ struct bgp_aggregate *aggregate,
+ struct lcommunity *lcommunity);
+extern void bgp_aggr_lcommunity_remove(void *arg);
+
+#endif /* _QUAGGA_BGP_LCOMMUNITY_H */
diff --git a/bgpd/bgp_mac.c b/bgpd/bgp_mac.c
new file mode 100644
index 0000000..02b7e64
--- /dev/null
+++ b/bgpd/bgp_mac.c
@@ -0,0 +1,423 @@
+/*
+ * BGPd - Mac hash code
+ * Copyright (C) 2018 Cumulus Networks, Inc.
+ * Donald Sharp
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License as published by the Free
+ * Software Foundation; either version 2 of the License, or (at your option)
+ * any later version.
+ *
+ * This program is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
+ * more details.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program; see the file COPYING; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
+ */
+#include <zebra.h>
+
+#include <jhash.h>
+#include <hash.h>
+#include <prefix.h>
+#include <memory.h>
+
+#include "bgpd/bgpd.h"
+#include "bgpd/bgp_mac.h"
+#include "bgpd/bgp_memory.h"
+#include "bgpd/bgp_route.h"
+#include "bgpd/bgp_packet.h"
+#include "bgpd/bgp_rd.h"
+#include "bgpd/bgp_debug.h"
+#include "bgpd/bgp_evpn_private.h"
+
+DEFINE_MTYPE_STATIC(BGPD, BSM, "Mac Hash Entry");
+DEFINE_MTYPE_STATIC(BGPD, BSM_STRING, "Mac Hash Entry Intf String");
+
+struct bgp_self_mac {
+ struct ethaddr macaddr;
+ struct list *ifp_list;
+};
+
+static unsigned int bgp_mac_hash_key_make(const void *data)
+{
+ const struct bgp_self_mac *bsm = data;
+
+ return jhash(&bsm->macaddr, ETH_ALEN, 0xa5a5dead);
+}
+
+static bool bgp_mac_hash_cmp(const void *d1, const void *d2)
+{
+ const struct bgp_self_mac *bsm1 = d1;
+ const struct bgp_self_mac *bsm2 = d2;
+
+ if (memcmp(&bsm1->macaddr, &bsm2->macaddr, ETH_ALEN) == 0)
+ return true;
+
+ return false;
+}
+
+void bgp_mac_init(void)
+{
+ bm->self_mac_hash = hash_create(bgp_mac_hash_key_make, bgp_mac_hash_cmp,
+ "BGP MAC Hash");
+}
+
+static void bgp_mac_hash_free(void *data)
+{
+ struct bgp_self_mac *bsm = data;
+
+ if (bsm->ifp_list)
+ list_delete(&bsm->ifp_list);
+
+ XFREE(MTYPE_BSM, bsm);
+}
+
+void bgp_mac_finish(void)
+{
+ hash_clean(bm->self_mac_hash, bgp_mac_hash_free);
+ hash_free(bm->self_mac_hash);
+}
+
+static void bgp_mac_hash_interface_string_del(void *val)
+{
+ char *data = val;
+
+ XFREE(MTYPE_BSM_STRING, data);
+}
+
+static void *bgp_mac_hash_alloc(void *p)
+{
+ const struct bgp_self_mac *orig = p;
+ struct bgp_self_mac *bsm;
+
+ bsm = XCALLOC(MTYPE_BSM, sizeof(struct bgp_self_mac));
+ memcpy(&bsm->macaddr, &orig->macaddr, ETH_ALEN);
+
+ bsm->ifp_list = list_new();
+ bsm->ifp_list->del = bgp_mac_hash_interface_string_del;
+
+ return bsm;
+}
+
+struct bgp_mac_find_internal {
+ struct bgp_self_mac *bsm;
+ const char *ifname;
+};
+
+static void bgp_mac_find_ifp_internal(struct hash_bucket *bucket, void *arg)
+{
+ struct bgp_mac_find_internal *bmfi = arg;
+ struct bgp_self_mac *bsm = bucket->data;
+ struct listnode *node;
+ char *name;
+
+ for (ALL_LIST_ELEMENTS_RO(bsm->ifp_list, node, name)) {
+ if (strcmp(name, bmfi->ifname) == 0) {
+ bmfi->bsm = bsm;
+ return;
+ }
+ }
+}
+
+static struct bgp_self_mac *bgp_mac_find_interface_name(const char *ifname)
+{
+ struct bgp_mac_find_internal bmfi;
+
+ bmfi.bsm = NULL;
+ bmfi.ifname = ifname;
+ hash_iterate(bm->self_mac_hash, bgp_mac_find_ifp_internal, &bmfi);
+
+ return bmfi.bsm;
+}
+
+static void bgp_process_mac_rescan_table(struct bgp *bgp, struct peer *peer,
+ struct bgp_table *table,
+ struct ethaddr *macaddr)
+{
+ struct bgp_dest *pdest, *dest;
+ struct bgp_path_info *pi;
+
+ for (pdest = bgp_table_top(table); pdest;
+ pdest = bgp_route_next(pdest)) {
+ struct bgp_table *sub = pdest->info;
+ const struct prefix *pdest_p = bgp_dest_get_prefix(pdest);
+
+ if (!sub)
+ continue;
+
+ for (dest = bgp_table_top(sub); dest;
+ dest = bgp_route_next(dest)) {
+ bool dest_affected;
+ const struct prefix *p = bgp_dest_get_prefix(dest);
+ struct prefix_evpn *pevpn = (struct prefix_evpn *)dest;
+ struct prefix_rd prd;
+ uint32_t num_labels = 0;
+ mpls_label_t *label_pnt = NULL;
+ struct bgp_route_evpn *evpn;
+
+ if (pevpn->family == AF_EVPN
+ && pevpn->prefix.route_type == BGP_EVPN_MAC_IP_ROUTE
+ && memcmp(&p->u.prefix_evpn.macip_addr.mac, macaddr,
+ ETH_ALEN)
+ == 0)
+ dest_affected = true;
+ else
+ dest_affected = false;
+
+ for (pi = dest->info; pi; pi = pi->next) {
+ if (pi->peer == peer)
+ break;
+ }
+
+ if (!pi)
+ continue;
+
+ /*
+ * If the mac address is not the same then
+ * we don't care and since we are looking
+ */
+ if ((memcmp(&pi->attr->rmac, macaddr, ETH_ALEN) != 0)
+ && !dest_affected)
+ continue;
+
+ if (pi->extra)
+ num_labels = pi->extra->num_labels;
+ if (num_labels)
+ label_pnt = &pi->extra->label[0];
+
+ prd.family = AF_UNSPEC;
+ prd.prefixlen = 64;
+ memcpy(&prd.val, pdest_p->u.val, 8);
+
+ if (CHECK_FLAG(pi->flags, BGP_PATH_REMOVED)) {
+ if (bgp_debug_update(peer, p, NULL, 1)) {
+ char pfx_buf[BGP_PRD_PATH_STRLEN];
+
+ bgp_debug_rdpfxpath2str(
+ AFI_L2VPN, SAFI_EVPN, &prd,
+ p, label_pnt, num_labels,
+ pi->addpath_rx_id ? 1 : 0,
+ pi->addpath_rx_id, NULL,
+ pfx_buf, sizeof(pfx_buf));
+ zlog_debug(
+ "%s skip update of %s marked as removed",
+ peer->host, pfx_buf);
+ }
+ continue;
+ }
+
+ memcpy(&evpn, bgp_attr_get_evpn_overlay(pi->attr),
+ sizeof(evpn));
+ int32_t ret = bgp_update(peer, p,
+ pi->addpath_rx_id,
+ pi->attr, AFI_L2VPN, SAFI_EVPN,
+ ZEBRA_ROUTE_BGP,
+ BGP_ROUTE_NORMAL, &prd,
+ label_pnt, num_labels,
+ 1, evpn);
+
+ if (ret < 0)
+ bgp_dest_unlock_node(dest);
+ }
+ }
+}
+
+static void bgp_mac_rescan_evpn_table(struct bgp *bgp, struct ethaddr *macaddr)
+{
+ struct listnode *node;
+ struct peer *peer;
+ safi_t safi;
+ afi_t afi;
+
+ afi = AFI_L2VPN;
+ safi = SAFI_EVPN;
+ for (ALL_LIST_ELEMENTS_RO(bgp->peer, node, peer)) {
+
+ if (CHECK_FLAG(peer->sflags, PEER_STATUS_GROUP))
+ continue;
+
+ if (!peer_established(peer))
+ continue;
+
+ if (CHECK_FLAG(peer->af_flags[afi][safi],
+ PEER_FLAG_SOFT_RECONFIG)) {
+ if (bgp_debug_update(peer, NULL, NULL, 1))
+ zlog_debug("Processing EVPN MAC interface change on peer %s (inbound, soft-reconfig)",
+ peer->host);
+
+ bgp_soft_reconfig_in(peer, afi, safi);
+ } else {
+ struct bgp_table *table = bgp->rib[afi][safi];
+
+ if (bgp_debug_update(peer, NULL, NULL, 1))
+ zlog_debug("Processing EVPN MAC interface change on peer %s",
+ peer->host);
+ bgp_process_mac_rescan_table(bgp, peer, table, macaddr);
+ }
+ }
+}
+
+static void bgp_mac_rescan_all_evpn_tables(struct ethaddr *macaddr)
+{
+ struct listnode *node;
+ struct bgp *bgp;
+
+ for (ALL_LIST_ELEMENTS_RO(bm->bgp, node, bgp)) {
+ struct bgp_table *table = bgp->rib[AFI_L2VPN][SAFI_EVPN];
+
+ if (table)
+ bgp_mac_rescan_evpn_table(bgp, macaddr);
+ }
+}
+
+static void bgp_mac_remove_ifp_internal(struct bgp_self_mac *bsm, char *ifname,
+ struct ethaddr *macaddr)
+{
+ struct listnode *node = NULL;
+ char *name;
+
+ for (ALL_LIST_ELEMENTS_RO(bsm->ifp_list, node, name)) {
+ if (strcmp(name, ifname) == 0)
+ break;
+ }
+
+ if (node) {
+ list_delete_node(bsm->ifp_list, node);
+ XFREE(MTYPE_BSM_STRING, name);
+ }
+
+ if (bsm->ifp_list->count == 0) {
+ struct ethaddr mac = *macaddr;
+
+ hash_release(bm->self_mac_hash, bsm);
+ list_delete(&bsm->ifp_list);
+ XFREE(MTYPE_BSM, bsm);
+
+ bgp_mac_rescan_all_evpn_tables(&mac);
+ }
+}
+
+void bgp_mac_add_mac_entry(struct interface *ifp)
+{
+ struct bgp_self_mac lookup;
+ struct bgp_self_mac *bsm;
+ struct bgp_self_mac *old_bsm;
+ char *ifname;
+
+ memcpy(&lookup.macaddr, &ifp->hw_addr, ETH_ALEN);
+ bsm = hash_get(bm->self_mac_hash, &lookup, bgp_mac_hash_alloc);
+
+ /*
+ * Does this happen to be a move
+ */
+ old_bsm = bgp_mac_find_interface_name(ifp->name);
+ ifname = XSTRDUP(MTYPE_BSM_STRING, ifp->name);
+
+ if (bsm->ifp_list->count == 0) {
+
+ listnode_add(bsm->ifp_list, ifname);
+ if (old_bsm)
+ bgp_mac_remove_ifp_internal(old_bsm, ifname,
+ &old_bsm->macaddr);
+ } else {
+ /*
+ * If old mac address is the same as the new,
+ * then there is nothing to do here
+ */
+ if (old_bsm == bsm) {
+ XFREE(MTYPE_BSM_STRING, ifname);
+ return;
+ }
+
+ if (old_bsm)
+ bgp_mac_remove_ifp_internal(old_bsm, ifp->name,
+ &old_bsm->macaddr);
+
+ listnode_add(bsm->ifp_list, ifname);
+ }
+
+ bgp_mac_rescan_all_evpn_tables(&bsm->macaddr);
+}
+
+void bgp_mac_del_mac_entry(struct interface *ifp)
+{
+ struct bgp_self_mac lookup;
+ struct bgp_self_mac *bsm;
+
+ memcpy(&lookup.macaddr, &ifp->hw_addr, ETH_ALEN);
+ bsm = hash_lookup(bm->self_mac_hash, &lookup);
+ if (!bsm)
+ return;
+
+ /*
+ * Write code to allow old mac address to no-longer
+ * win if we happen to have received it from a peer.
+ */
+ bgp_mac_remove_ifp_internal(bsm, ifp->name, &bsm->macaddr);
+}
+
+/* This API checks MAC address against any of local
+ * assigned (SVIs) MAC address.
+ * An example: router-mac attribute in any of evpn update
+ * requires to compare against local mac.
+ */
+bool bgp_mac_exist(const struct ethaddr *mac)
+{
+ struct bgp_self_mac lookup;
+ struct bgp_self_mac *bsm;
+ static uint8_t tmp [ETHER_ADDR_STRLEN] = {0};
+
+ if (memcmp(mac, &tmp, ETH_ALEN) == 0)
+ return false;
+
+ memcpy(&lookup.macaddr, mac, ETH_ALEN);
+ bsm = hash_lookup(bm->self_mac_hash, &lookup);
+ if (!bsm)
+ return false;
+
+ return true;
+}
+
+/* This API checks EVPN type-2 prefix and comapares
+ * mac against any of local assigned (SVIs) MAC
+ * address.
+ */
+bool bgp_mac_entry_exists(const struct prefix *p)
+{
+ const struct prefix_evpn *pevpn = (const struct prefix_evpn *)p;
+
+ if (pevpn->family != AF_EVPN)
+ return false;
+
+ if (pevpn->prefix.route_type != BGP_EVPN_MAC_IP_ROUTE)
+ return false;
+
+ return bgp_mac_exist(&p->u.prefix_evpn.macip_addr.mac);
+
+ return true;
+}
+
+static void bgp_mac_show_mac_entry(struct hash_bucket *bucket, void *arg)
+{
+ struct vty *vty = arg;
+ struct bgp_self_mac *bsm = bucket->data;
+ struct listnode *node;
+ char *name;
+ char buf_mac[ETHER_ADDR_STRLEN];
+
+ vty_out(vty, "Mac Address: %s ",
+ prefix_mac2str(&bsm->macaddr, buf_mac, sizeof(buf_mac)));
+
+ for (ALL_LIST_ELEMENTS_RO(bsm->ifp_list, node, name))
+ vty_out(vty, "%s ", name);
+
+ vty_out(vty, "\n");
+}
+
+void bgp_mac_dump_table(struct vty *vty)
+{
+ hash_iterate(bm->self_mac_hash, bgp_mac_show_mac_entry, vty);
+}
diff --git a/bgpd/bgp_mac.h b/bgpd/bgp_mac.h
new file mode 100644
index 0000000..4b94d80
--- /dev/null
+++ b/bgpd/bgp_mac.h
@@ -0,0 +1,42 @@
+/*
+ * BGPd - Mac hash header
+ * Copyright (C) 2018 Cumulus Networks, Inc.
+ * Donald Sharp
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License as published by the Free
+ * Software Foundation; either version 2 of the License, or (at your option)
+ * any later version.
+ *
+ * This program is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
+ * more details.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program; see the file COPYING; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
+ */
+#ifndef __BGP_MAC_H__
+#define __BGP_MAC_H__
+
+void bgp_mac_init(void);
+void bgp_mac_finish(void);
+
+/*
+ * Functions to add/delete the mac entry from the appropriate
+ * bgp hash's. Additionally to do some additional processing
+ * to allow the win/loss to be processed.
+ */
+void bgp_mac_add_mac_entry(struct interface *ifp);
+void bgp_mac_del_mac_entry(struct interface *ifp);
+
+void bgp_mac_dump_table(struct vty *vty);
+
+/*
+ * Function to lookup the prefix and see if we have a matching mac
+ */
+bool bgp_mac_entry_exists(const struct prefix *p);
+bool bgp_mac_exist(const struct ethaddr *mac);
+
+#endif
diff --git a/bgpd/bgp_main.c b/bgpd/bgp_main.c
new file mode 100644
index 0000000..90ae580
--- /dev/null
+++ b/bgpd/bgp_main.c
@@ -0,0 +1,524 @@
+/* Main routine of bgpd.
+ * Copyright (C) 1996, 97, 98, 1999 Kunihiro Ishiguro
+ *
+ * This file is part of GNU Zebra.
+ *
+ * GNU Zebra is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License as published by the
+ * Free Software Foundation; either version 2, or (at your option) any
+ * later version.
+ *
+ * GNU Zebra is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program; see the file COPYING; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
+ */
+
+#include <zebra.h>
+
+#include <pthread.h>
+#include "vector.h"
+#include "command.h"
+#include "getopt.h"
+#include "thread.h"
+#include <lib/version.h>
+#include "memory.h"
+#include "prefix.h"
+#include "log.h"
+#include "privs.h"
+#include "sigevent.h"
+#include "zclient.h"
+#include "routemap.h"
+#include "filter.h"
+#include "plist.h"
+#include "stream.h"
+#include "queue.h"
+#include "vrf.h"
+#include "bfd.h"
+#include "libfrr.h"
+#include "ns.h"
+
+#include "bgpd/bgpd.h"
+#include "bgpd/bgp_attr.h"
+#include "bgpd/bgp_route.h"
+#include "bgpd/bgp_mplsvpn.h"
+#include "bgpd/bgp_aspath.h"
+#include "bgpd/bgp_dump.h"
+#include "bgpd/bgp_route.h"
+#include "bgpd/bgp_nexthop.h"
+#include "bgpd/bgp_regex.h"
+#include "bgpd/bgp_clist.h"
+#include "bgpd/bgp_debug.h"
+#include "bgpd/bgp_errors.h"
+#include "bgpd/bgp_filter.h"
+#include "bgpd/bgp_zebra.h"
+#include "bgpd/bgp_packet.h"
+#include "bgpd/bgp_keepalives.h"
+#include "bgpd/bgp_network.h"
+#include "bgpd/bgp_errors.h"
+#include "bgpd/bgp_script.h"
+#include "bgpd/bgp_evpn_mh.h"
+#include "bgpd/bgp_nht.h"
+#include "bgpd/bgp_routemap_nb.h"
+#include "bgpd/bgp_community_alias.h"
+
+#ifdef ENABLE_BGP_VNC
+#include "bgpd/rfapi/rfapi_backend.h"
+#endif
+
+/* bgpd options, we use GNU getopt library. */
+static const struct option longopts[] = {
+ {"bgp_port", required_argument, NULL, 'p'},
+ {"listenon", required_argument, NULL, 'l'},
+ {"no_kernel", no_argument, NULL, 'n'},
+ {"skip_runas", no_argument, NULL, 'S'},
+ {"ecmp", required_argument, NULL, 'e'},
+ {"int_num", required_argument, NULL, 'I'},
+ {"no_zebra", no_argument, NULL, 'Z'},
+ {"socket_size", required_argument, NULL, 's'},
+ {0}};
+
+/* signal definitions */
+void sighup(void);
+void sigint(void);
+void sigusr1(void);
+
+static void bgp_exit(int);
+static void bgp_vrf_terminate(void);
+
+static struct frr_signal_t bgp_signals[] = {
+ {
+ .signal = SIGHUP,
+ .handler = &sighup,
+ },
+ {
+ .signal = SIGUSR1,
+ .handler = &sigusr1,
+ },
+ {
+ .signal = SIGINT,
+ .handler = &sigint,
+ },
+ {
+ .signal = SIGTERM,
+ .handler = &sigint,
+ },
+};
+
+/* privileges */
+static zebra_capabilities_t _caps_p[] = {ZCAP_BIND, ZCAP_NET_RAW,
+ ZCAP_NET_ADMIN, ZCAP_SYS_ADMIN};
+
+struct zebra_privs_t bgpd_privs = {
+#if defined(FRR_USER) && defined(FRR_GROUP)
+ .user = FRR_USER,
+ .group = FRR_GROUP,
+#endif
+#ifdef VTY_GROUP
+ .vty_group = VTY_GROUP,
+#endif
+ .caps_p = _caps_p,
+ .cap_num_p = array_size(_caps_p),
+ .cap_num_i = 0,
+};
+
+static struct frr_daemon_info bgpd_di;
+
+/* SIGHUP handler. */
+void sighup(void)
+{
+ zlog_info("SIGHUP received, ignoring");
+
+ return;
+
+ /*
+ * This is turned off for the moment. There is all
+ * sorts of config turned off by bgp_terminate
+ * that is not setup properly again in bgp_reset.
+ * I see no easy way to do this nor do I see that
+ * this is a desirable way to reload config
+ * given the yang work.
+ */
+ /* Terminate all thread. */
+ /*
+ * bgp_terminate();
+ * bgp_reset();
+ * zlog_info("bgpd restarting!");
+
+ * Reload config file.
+ * vty_read_config(NULL, bgpd_di.config_file, config_default);
+ */
+ /* Try to return to normal operation. */
+}
+
+/* SIGINT handler. */
+__attribute__((__noreturn__)) void sigint(void)
+{
+ zlog_notice("Terminating on signal");
+ assert(bm->terminating == false);
+ bm->terminating = true; /* global flag that shutting down */
+
+ /* Disable BFD events to avoid wasting processing. */
+ bfd_protocol_integration_set_shutdown(true);
+
+ bgp_terminate();
+
+ bgp_exit(0);
+
+ exit(0);
+}
+
+/* SIGUSR1 handler. */
+void sigusr1(void)
+{
+ zlog_rotate();
+}
+
+/*
+ Try to free up allocations we know about so that diagnostic tools such as
+ valgrind are able to better illuminate leaks.
+
+ Zebra route removal and protocol teardown are not meant to be done here.
+ For example, "retain_mode" may be set.
+*/
+static __attribute__((__noreturn__)) void bgp_exit(int status)
+{
+ struct bgp *bgp, *bgp_default, *bgp_evpn;
+ struct listnode *node, *nnode;
+
+ /* it only makes sense for this to be called on a clean exit */
+ assert(status == 0);
+
+ frr_early_fini();
+
+ bgp_close();
+
+ bgp_default = bgp_get_default();
+ bgp_evpn = bgp_get_evpn();
+
+ /* reverse bgp_master_init */
+ for (ALL_LIST_ELEMENTS(bm->bgp, node, nnode, bgp)) {
+ if (bgp_default == bgp || bgp_evpn == bgp)
+ continue;
+ bgp_delete(bgp);
+ }
+ if (bgp_evpn && bgp_evpn != bgp_default)
+ bgp_delete(bgp_evpn);
+ if (bgp_default)
+ bgp_delete(bgp_default);
+
+ bgp_evpn_mh_finish();
+ bgp_l3nhg_finish();
+
+ /* reverse bgp_dump_init */
+ bgp_dump_finish();
+
+ /* BGP community aliases */
+ bgp_community_alias_finish();
+
+ /* reverse bgp_route_init */
+ bgp_route_finish();
+
+ /* cleanup route maps */
+ bgp_route_map_terminate();
+
+ /* reverse bgp_attr_init */
+ bgp_attr_finish();
+
+ /* stop pthreads */
+ bgp_pthreads_finish();
+
+ /* reverse access_list_init */
+ access_list_add_hook(NULL);
+ access_list_delete_hook(NULL);
+ access_list_reset();
+
+ /* reverse bgp_filter_init */
+ as_list_add_hook(NULL);
+ as_list_delete_hook(NULL);
+ bgp_filter_reset();
+
+ /* reverse prefix_list_init */
+ prefix_list_add_hook(NULL);
+ prefix_list_delete_hook(NULL);
+ prefix_list_reset();
+
+ /* reverse community_list_init */
+ community_list_terminate(bgp_clist);
+
+ bgp_vrf_terminate();
+#ifdef ENABLE_BGP_VNC
+ vnc_zebra_destroy();
+#endif
+ bgp_zebra_destroy();
+
+ bf_free(bm->rd_idspace);
+ list_delete(&bm->bgp);
+ list_delete(&bm->addresses);
+
+ bgp_lp_finish();
+
+ memset(bm, 0, sizeof(*bm));
+
+ frr_fini();
+ exit(status);
+}
+
+static int bgp_vrf_new(struct vrf *vrf)
+{
+ if (BGP_DEBUG(zebra, ZEBRA))
+ zlog_debug("VRF Created: %s(%u)", vrf->name, vrf->vrf_id);
+
+ return 0;
+}
+
+static int bgp_vrf_delete(struct vrf *vrf)
+{
+ if (BGP_DEBUG(zebra, ZEBRA))
+ zlog_debug("VRF Deletion: %s(%u)", vrf->name, vrf->vrf_id);
+
+ return 0;
+}
+
+static int bgp_vrf_enable(struct vrf *vrf)
+{
+ struct bgp *bgp;
+ vrf_id_t old_vrf_id;
+
+ if (BGP_DEBUG(zebra, ZEBRA))
+ zlog_debug("VRF enable add %s id %u", vrf->name, vrf->vrf_id);
+
+ bgp = bgp_lookup_by_name(vrf->name);
+ if (bgp && bgp->vrf_id != vrf->vrf_id) {
+ old_vrf_id = bgp->vrf_id;
+ /* We have instance configured, link to VRF and make it "up". */
+ bgp_vrf_link(bgp, vrf);
+
+ bgp_handle_socket(bgp, vrf, old_vrf_id, true);
+ bgp_instance_up(bgp);
+ vpn_leak_zebra_vrf_label_update(bgp, AFI_IP);
+ vpn_leak_zebra_vrf_label_update(bgp, AFI_IP6);
+ vpn_leak_zebra_vrf_sid_update(bgp, AFI_IP);
+ vpn_leak_zebra_vrf_sid_update(bgp, AFI_IP6);
+ vpn_leak_postchange(BGP_VPN_POLICY_DIR_TOVPN, AFI_IP,
+ bgp_get_default(), bgp);
+ vpn_leak_postchange(BGP_VPN_POLICY_DIR_FROMVPN, AFI_IP,
+ bgp_get_default(), bgp);
+ vpn_leak_postchange(BGP_VPN_POLICY_DIR_TOVPN, AFI_IP6,
+ bgp_get_default(), bgp);
+ vpn_leak_postchange(BGP_VPN_POLICY_DIR_FROMVPN, AFI_IP6,
+ bgp_get_default(), bgp);
+ }
+
+ return 0;
+}
+
+static int bgp_vrf_disable(struct vrf *vrf)
+{
+ struct bgp *bgp;
+
+ if (vrf->vrf_id == VRF_DEFAULT)
+ return 0;
+
+ if (BGP_DEBUG(zebra, ZEBRA))
+ zlog_debug("VRF disable %s id %d", vrf->name, vrf->vrf_id);
+
+ bgp = bgp_lookup_by_name(vrf->name);
+ if (bgp) {
+
+ vpn_leak_zebra_vrf_label_withdraw(bgp, AFI_IP);
+ vpn_leak_zebra_vrf_label_withdraw(bgp, AFI_IP6);
+ vpn_leak_prechange(BGP_VPN_POLICY_DIR_TOVPN, AFI_IP,
+ bgp_get_default(), bgp);
+ vpn_leak_prechange(BGP_VPN_POLICY_DIR_FROMVPN, AFI_IP,
+ bgp_get_default(), bgp);
+ vpn_leak_prechange(BGP_VPN_POLICY_DIR_TOVPN, AFI_IP6,
+ bgp_get_default(), bgp);
+ vpn_leak_prechange(BGP_VPN_POLICY_DIR_FROMVPN, AFI_IP6,
+ bgp_get_default(), bgp);
+
+ bgp_handle_socket(bgp, vrf, VRF_UNKNOWN, false);
+ /* We have instance configured, unlink from VRF and make it
+ * "down". */
+ bgp_instance_down(bgp);
+ bgp_vrf_unlink(bgp, vrf);
+ }
+
+ /* Note: This is a callback, the VRF will be deleted by the caller. */
+ return 0;
+}
+
+static void bgp_vrf_init(void)
+{
+ vrf_init(bgp_vrf_new, bgp_vrf_enable, bgp_vrf_disable, bgp_vrf_delete);
+}
+
+static void bgp_vrf_terminate(void)
+{
+ vrf_terminate();
+}
+
+static const struct frr_yang_module_info *const bgpd_yang_modules[] = {
+ &frr_filter_info,
+ &frr_interface_info,
+ &frr_route_map_info,
+ &frr_vrf_info,
+ &frr_bgp_route_map_info,
+};
+
+FRR_DAEMON_INFO(bgpd, BGP, .vty_port = BGP_VTY_PORT,
+
+ .proghelp = "Implementation of the BGP routing protocol.",
+
+ .signals = bgp_signals, .n_signals = array_size(bgp_signals),
+
+ .privs = &bgpd_privs, .yang_modules = bgpd_yang_modules,
+ .n_yang_modules = array_size(bgpd_yang_modules),
+);
+
+#define DEPRECATED_OPTIONS ""
+
+/* Main routine of bgpd. Treatment of argument and start bgp finite
+ state machine is handled at here. */
+int main(int argc, char **argv)
+{
+ int opt;
+ int tmp_port;
+
+ int bgp_port = BGP_PORT_DEFAULT;
+ struct list *addresses = list_new();
+ int no_fib_flag = 0;
+ int no_zebra_flag = 0;
+ int skip_runas = 0;
+ int instance = 0;
+ int buffer_size = BGP_SOCKET_SNDBUF_SIZE;
+ char *address;
+ struct listnode *node;
+
+ addresses->cmp = (int (*)(void *, void *))strcmp;
+
+ frr_preinit(&bgpd_di, argc, argv);
+ frr_opt_add(
+ "p:l:SnZe:I:s:" DEPRECATED_OPTIONS, longopts,
+ " -p, --bgp_port Set BGP listen port number (0 means do not listen).\n"
+ " -l, --listenon Listen on specified address (implies -n)\n"
+ " -n, --no_kernel Do not install route to kernel.\n"
+ " -Z, --no_zebra Do not communicate with Zebra.\n"
+ " -S, --skip_runas Skip capabilities checks, and changing user and group IDs.\n"
+ " -e, --ecmp Specify ECMP to use.\n"
+ " -I, --int_num Set instance number (label-manager)\n"
+ " -s, --socket_size Set BGP peer socket send buffer size\n");
+
+ /* Command line argument treatment. */
+ while (1) {
+ opt = frr_getopt(argc, argv, 0);
+
+ if (opt && opt < 128 && strchr(DEPRECATED_OPTIONS, opt)) {
+ fprintf(stderr,
+ "The -%c option no longer exists.\nPlease refer to the manual.\n",
+ opt);
+ continue;
+ }
+
+ if (opt == EOF)
+ break;
+
+ switch (opt) {
+ case 0:
+ break;
+ case 'p':
+ tmp_port = atoi(optarg);
+ if (tmp_port < 0 || tmp_port > 0xffff)
+ bgp_port = BGP_PORT_DEFAULT;
+ else
+ bgp_port = tmp_port;
+ break;
+ case 'e': {
+ unsigned long int parsed_multipath =
+ strtoul(optarg, NULL, 10);
+ if (parsed_multipath == 0
+ || parsed_multipath > MULTIPATH_NUM
+ || parsed_multipath > UINT_MAX) {
+ flog_err(
+ EC_BGP_MULTIPATH,
+ "Multipath Number specified must be less than %u and greater than 0",
+ MULTIPATH_NUM);
+ return 1;
+ }
+ multipath_num = parsed_multipath;
+ break;
+ }
+ case 'l':
+ listnode_add_sort_nodup(addresses, optarg);
+ break;
+ case 'n':
+ no_fib_flag = 1;
+ break;
+ case 'Z':
+ no_zebra_flag = 1;
+ break;
+ case 'S':
+ skip_runas = 1;
+ break;
+ case 'I':
+ instance = atoi(optarg);
+ if (instance > (unsigned short)-1)
+ zlog_err("Instance %i out of range (0..%u)",
+ instance, (unsigned short)-1);
+ break;
+ case 's':
+ buffer_size = atoi(optarg);
+ break;
+ default:
+ frr_help_exit(1);
+ }
+ }
+ if (skip_runas)
+ memset(&bgpd_privs, 0, sizeof(bgpd_privs));
+
+ /* BGP master init. */
+ bgp_master_init(frr_init(), buffer_size, addresses);
+ bm->port = bgp_port;
+ if (bgp_port == 0)
+ bgp_option_set(BGP_OPT_NO_LISTEN);
+ if (no_fib_flag || no_zebra_flag)
+ bgp_option_set(BGP_OPT_NO_FIB);
+ if (no_zebra_flag)
+ bgp_option_set(BGP_OPT_NO_ZEBRA);
+ bgp_error_init();
+ /* Initializations. */
+ bgp_vrf_init();
+
+#ifdef HAVE_SCRIPTING
+ bgp_script_init();
+#endif
+
+ /* BGP related initialization. */
+ bgp_init((unsigned short)instance);
+
+ if (list_isempty(bm->addresses)) {
+ snprintf(bgpd_di.startinfo, sizeof(bgpd_di.startinfo),
+ ", bgp@<all>:%d", bm->port);
+ } else {
+ for (ALL_LIST_ELEMENTS_RO(bm->addresses, node, address))
+ snprintf(bgpd_di.startinfo + strlen(bgpd_di.startinfo),
+ sizeof(bgpd_di.startinfo)
+ - strlen(bgpd_di.startinfo),
+ ", bgp@%s:%d", address, bm->port);
+ }
+
+ bgp_if_init();
+
+ frr_config_fork();
+ /* must be called after fork() */
+ bgp_gr_apply_running_config();
+ bgp_pthreads_run();
+ frr_run(bm->master);
+
+ /* Not reached. */
+ return 0;
+}
diff --git a/bgpd/bgp_memory.c b/bgpd/bgp_memory.c
new file mode 100644
index 0000000..850657d
--- /dev/null
+++ b/bgpd/bgp_memory.c
@@ -0,0 +1,141 @@
+/* bgpd memory type definitions
+ *
+ * Copyright (C) 2015 David Lamparter
+ *
+ * This file is part of Quagga.
+ *
+ * Quagga is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License as published by the
+ * Free Software Foundation; either version 2, or (at your option) any
+ * later version.
+ *
+ * Quagga is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program; see the file COPYING; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
+ */
+
+#ifdef HAVE_CONFIG_H
+#include "config.h"
+#endif
+
+#include "bgp_memory.h"
+
+/* this file is temporary in nature; definitions should be moved to the
+ * files they're used in */
+
+DEFINE_MGROUP(BGPD, "bgpd");
+DEFINE_MTYPE(BGPD, BGP, "BGP instance");
+DEFINE_MTYPE(BGPD, BGP_LISTENER, "BGP listen socket details");
+DEFINE_MTYPE(BGPD, BGP_PEER, "BGP peer");
+DEFINE_MTYPE(BGPD, BGP_PEER_HOST, "BGP peer hostname");
+DEFINE_MTYPE(BGPD, BGP_PEER_IFNAME, "BGP peer ifname");
+DEFINE_MTYPE(BGPD, PEER_GROUP, "Peer group");
+DEFINE_MTYPE(BGPD, PEER_GROUP_HOST, "BGP Peer group hostname");
+DEFINE_MTYPE(BGPD, PEER_DESC, "Peer description");
+DEFINE_MTYPE(BGPD, PEER_PASSWORD, "Peer password string");
+DEFINE_MTYPE(BGPD, BGP_PEER_AF, "BGP peer af");
+DEFINE_MTYPE(BGPD, BGP_UPDGRP, "BGP update group");
+DEFINE_MTYPE(BGPD, BGP_UPD_SUBGRP, "BGP update subgroup");
+DEFINE_MTYPE(BGPD, BGP_PACKET, "BGP packet");
+DEFINE_MTYPE(BGPD, ATTR, "BGP attribute");
+DEFINE_MTYPE(BGPD, AS_PATH, "BGP aspath");
+DEFINE_MTYPE(BGPD, AS_SEG, "BGP aspath seg");
+DEFINE_MTYPE(BGPD, AS_SEG_DATA, "BGP aspath segment data");
+DEFINE_MTYPE(BGPD, AS_STR, "BGP aspath str");
+
+DEFINE_MTYPE(BGPD, BGP_TABLE, "BGP table");
+DEFINE_MTYPE(BGPD, BGP_NODE, "BGP node");
+DEFINE_MTYPE(BGPD, BGP_ROUTE, "BGP route");
+DEFINE_MTYPE(BGPD, BGP_ROUTE_EXTRA, "BGP ancillary route info");
+DEFINE_MTYPE(BGPD, BGP_CONN, "BGP connected");
+DEFINE_MTYPE(BGPD, BGP_STATIC, "BGP static");
+DEFINE_MTYPE(BGPD, BGP_ADVERTISE_ATTR, "BGP adv attr");
+DEFINE_MTYPE(BGPD, BGP_ADVERTISE, "BGP adv");
+DEFINE_MTYPE(BGPD, BGP_SYNCHRONISE, "BGP synchronise");
+DEFINE_MTYPE(BGPD, BGP_ADJ_IN, "BGP adj in");
+DEFINE_MTYPE(BGPD, BGP_ADJ_OUT, "BGP adj out");
+DEFINE_MTYPE(BGPD, BGP_MPATH_INFO, "BGP multipath info");
+
+DEFINE_MTYPE(BGPD, AS_LIST, "BGP AS list");
+DEFINE_MTYPE(BGPD, AS_FILTER, "BGP AS filter");
+DEFINE_MTYPE(BGPD, AS_FILTER_STR, "BGP AS filter str");
+
+DEFINE_MTYPE(BGPD, COMMUNITY_ALIAS, "community alias");
+
+DEFINE_MTYPE(BGPD, COMMUNITY, "community");
+DEFINE_MTYPE(BGPD, COMMUNITY_VAL, "community val");
+DEFINE_MTYPE(BGPD, COMMUNITY_STR, "community str");
+
+DEFINE_MTYPE(BGPD, ECOMMUNITY, "extcommunity");
+DEFINE_MTYPE(BGPD, ECOMMUNITY_VAL, "extcommunity val");
+DEFINE_MTYPE(BGPD, ECOMMUNITY_STR, "extcommunity str");
+
+DEFINE_MTYPE(BGPD, COMMUNITY_LIST, "community-list");
+DEFINE_MTYPE(BGPD, COMMUNITY_LIST_NAME, "community-list name");
+DEFINE_MTYPE(BGPD, COMMUNITY_LIST_ENTRY, "community-list entry");
+DEFINE_MTYPE(BGPD, COMMUNITY_LIST_CONFIG, "community-list config");
+DEFINE_MTYPE(BGPD, COMMUNITY_LIST_HANDLER, "community-list handler");
+
+DEFINE_MTYPE(BGPD, CLUSTER, "Cluster list");
+DEFINE_MTYPE(BGPD, CLUSTER_VAL, "Cluster list val");
+
+DEFINE_MTYPE(BGPD, BGP_PROCESS_QUEUE, "BGP Process queue");
+DEFINE_MTYPE(BGPD, BGP_CLEAR_NODE_QUEUE, "BGP node clear queue");
+
+DEFINE_MTYPE(BGPD, TRANSIT, "BGP transit attr");
+DEFINE_MTYPE(BGPD, TRANSIT_VAL, "BGP transit val");
+
+DEFINE_MTYPE(BGPD, BGP_DEBUG_FILTER, "BGP debug filter");
+DEFINE_MTYPE(BGPD, BGP_DEBUG_STR, "BGP debug filter string");
+
+DEFINE_MTYPE(BGPD, BGP_DISTANCE, "BGP distance");
+DEFINE_MTYPE(BGPD, BGP_NEXTHOP_CACHE, "BGP nexthop");
+DEFINE_MTYPE(BGPD, BGP_CONFED_LIST, "BGP confed list");
+DEFINE_MTYPE(BGPD, PEER_UPDATE_SOURCE, "BGP peer update interface");
+DEFINE_MTYPE(BGPD, PEER_CONF_IF, "BGP peer config interface");
+DEFINE_MTYPE(BGPD, BGP_DAMP_INFO, "Dampening info");
+DEFINE_MTYPE(BGPD, BGP_DAMP_ARRAY, "BGP Dampening array");
+DEFINE_MTYPE(BGPD, BGP_REGEXP, "BGP regexp");
+DEFINE_MTYPE(BGPD, BGP_AGGREGATE, "BGP aggregate");
+DEFINE_MTYPE(BGPD, BGP_ADDR, "BGP own address");
+DEFINE_MTYPE(BGPD, TIP_ADDR, "BGP own tunnel-ip address");
+
+DEFINE_MTYPE(BGPD, BGP_REDIST, "BGP redistribution");
+DEFINE_MTYPE(BGPD, BGP_FILTER_NAME, "BGP Filter Information");
+DEFINE_MTYPE(BGPD, BGP_DUMP_STR, "BGP Dump String Information");
+DEFINE_MTYPE(BGPD, ENCAP_TLV, "ENCAP TLV");
+
+DEFINE_MTYPE(BGPD, BGP_TEA_OPTIONS, "BGP TEA Options");
+DEFINE_MTYPE(BGPD, BGP_TEA_OPTIONS_VALUE, "BGP TEA Options Value");
+
+DEFINE_MTYPE(BGPD, LCOMMUNITY, "Large Community");
+DEFINE_MTYPE(BGPD, LCOMMUNITY_STR, "Large Community display string");
+DEFINE_MTYPE(BGPD, LCOMMUNITY_VAL, "Large Community value");
+
+DEFINE_MTYPE(BGPD, BGP_EVPN, "BGP EVPN Information");
+DEFINE_MTYPE(BGPD, BGP_EVPN_MH_INFO, "BGP EVPN MH Information");
+DEFINE_MTYPE(BGPD, BGP_EVPN_ES_VTEP, "BGP EVPN ES VTEP");
+DEFINE_MTYPE(BGPD, BGP_EVPN_PATH_MH_INFO, "BGP EVPN PATH MH Information");
+DEFINE_MTYPE(BGPD, BGP_EVPN_PATH_ES_INFO, "BGP EVPN PATH ES Information");
+DEFINE_MTYPE(BGPD, BGP_EVPN_PATH_NH_INFO, "BGP EVPN PATH NH Information");
+DEFINE_MTYPE(BGPD, BGP_EVPN_NH, "BGP EVPN Nexthop");
+DEFINE_MTYPE(BGPD, BGP_EVPN_ES_EVI_VTEP, "BGP EVPN ES-EVI VTEP");
+DEFINE_MTYPE(BGPD, BGP_EVPN_ES, "BGP EVPN ESI Information");
+DEFINE_MTYPE(BGPD, BGP_EVPN_ES_FRAG, "BGP EVPN ES Fragment Information");
+DEFINE_MTYPE(BGPD, BGP_EVPN_ES_EVI, "BGP EVPN ES-per-EVI Information");
+DEFINE_MTYPE(BGPD, BGP_EVPN_ES_VRF, "BGP EVPN ES-per-VRF Information");
+DEFINE_MTYPE(BGPD, BGP_EVPN_IMPORT_RT, "BGP EVPN Import RT");
+DEFINE_MTYPE(BGPD, BGP_EVPN_VRF_IMPORT_RT, "BGP EVPN VRF Import RT");
+
+DEFINE_MTYPE(BGPD, BGP_SRV6_L3VPN, "BGP prefix-sid srv6 l3vpn servcie");
+DEFINE_MTYPE(BGPD, BGP_SRV6_VPN, "BGP prefix-sid srv6 vpn service");
+DEFINE_MTYPE(BGPD, BGP_SRV6_SID, "BGP srv6 segment-id");
+DEFINE_MTYPE(BGPD, BGP_SRV6_FUNCTION, "BGP srv6 function");
+DEFINE_MTYPE(BGPD, EVPN_REMOTE_IP, "BGP EVPN Remote IP hash entry");
+
+DEFINE_MTYPE(BGPD, BGP_NOTIFICATION, "BGP Notification Message");
diff --git a/bgpd/bgp_memory.h b/bgpd/bgp_memory.h
new file mode 100644
index 0000000..510cfa2
--- /dev/null
+++ b/bgpd/bgp_memory.h
@@ -0,0 +1,141 @@
+/* bgpd memory type declarations
+ *
+ * Copyright (C) 2015 David Lamparter
+ *
+ * This file is part of Quagga.
+ *
+ * Quagga is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License as published by the
+ * Free Software Foundation; either version 2, or (at your option) any
+ * later version.
+ *
+ * Quagga is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program; see the file COPYING; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
+ */
+
+#ifndef _QUAGGA_BGP_MEMORY_H
+#define _QUAGGA_BGP_MEMORY_H
+
+#include "memory.h"
+
+DECLARE_MGROUP(BGPD);
+DECLARE_MTYPE(BGP);
+DECLARE_MTYPE(BGP_LISTENER);
+DECLARE_MTYPE(BGP_PEER);
+DECLARE_MTYPE(BGP_PEER_HOST);
+DECLARE_MTYPE(BGP_PEER_IFNAME);
+DECLARE_MTYPE(PEER_GROUP);
+DECLARE_MTYPE(PEER_GROUP_HOST);
+DECLARE_MTYPE(PEER_DESC);
+DECLARE_MTYPE(PEER_PASSWORD);
+DECLARE_MTYPE(BGP_PEER_AF);
+DECLARE_MTYPE(BGP_UPDGRP);
+DECLARE_MTYPE(BGP_UPD_SUBGRP);
+DECLARE_MTYPE(BGP_PACKET);
+DECLARE_MTYPE(ATTR);
+DECLARE_MTYPE(AS_PATH);
+DECLARE_MTYPE(AS_SEG);
+DECLARE_MTYPE(AS_SEG_DATA);
+DECLARE_MTYPE(AS_STR);
+
+DECLARE_MTYPE(BGP_TABLE);
+DECLARE_MTYPE(BGP_NODE);
+DECLARE_MTYPE(BGP_ROUTE);
+DECLARE_MTYPE(BGP_ROUTE_EXTRA);
+DECLARE_MTYPE(BGP_CONN);
+DECLARE_MTYPE(BGP_STATIC);
+DECLARE_MTYPE(BGP_ADVERTISE_ATTR);
+DECLARE_MTYPE(BGP_ADVERTISE);
+DECLARE_MTYPE(BGP_SYNCHRONISE);
+DECLARE_MTYPE(BGP_ADJ_IN);
+DECLARE_MTYPE(BGP_ADJ_OUT);
+DECLARE_MTYPE(BGP_MPATH_INFO);
+
+DECLARE_MTYPE(AS_LIST);
+DECLARE_MTYPE(AS_FILTER);
+DECLARE_MTYPE(AS_FILTER_STR);
+
+DECLARE_MTYPE(COMMUNITY_ALIAS);
+
+DECLARE_MTYPE(COMMUNITY);
+DECLARE_MTYPE(COMMUNITY_VAL);
+DECLARE_MTYPE(COMMUNITY_STR);
+
+DECLARE_MTYPE(ECOMMUNITY);
+DECLARE_MTYPE(ECOMMUNITY_VAL);
+DECLARE_MTYPE(ECOMMUNITY_STR);
+
+DECLARE_MTYPE(COMMUNITY_LIST);
+DECLARE_MTYPE(COMMUNITY_LIST_NAME);
+DECLARE_MTYPE(COMMUNITY_LIST_ENTRY);
+DECLARE_MTYPE(COMMUNITY_LIST_CONFIG);
+DECLARE_MTYPE(COMMUNITY_LIST_HANDLER);
+
+DECLARE_MTYPE(CLUSTER);
+DECLARE_MTYPE(CLUSTER_VAL);
+
+DECLARE_MTYPE(BGP_PROCESS_QUEUE);
+DECLARE_MTYPE(BGP_CLEAR_NODE_QUEUE);
+
+DECLARE_MTYPE(TRANSIT);
+DECLARE_MTYPE(TRANSIT_VAL);
+
+DECLARE_MTYPE(BGP_DEBUG_FILTER);
+DECLARE_MTYPE(BGP_DEBUG_STR);
+
+DECLARE_MTYPE(BGP_DISTANCE);
+DECLARE_MTYPE(BGP_NEXTHOP_CACHE);
+DECLARE_MTYPE(BGP_CONFED_LIST);
+DECLARE_MTYPE(PEER_UPDATE_SOURCE);
+DECLARE_MTYPE(PEER_CONF_IF);
+DECLARE_MTYPE(BGP_DAMP_INFO);
+DECLARE_MTYPE(BGP_DAMP_ARRAY);
+DECLARE_MTYPE(BGP_REGEXP);
+DECLARE_MTYPE(BGP_AGGREGATE);
+DECLARE_MTYPE(BGP_ADDR);
+DECLARE_MTYPE(TIP_ADDR);
+
+DECLARE_MTYPE(BGP_REDIST);
+DECLARE_MTYPE(BGP_FILTER_NAME);
+DECLARE_MTYPE(BGP_DUMP_STR);
+DECLARE_MTYPE(ENCAP_TLV);
+
+DECLARE_MTYPE(BGP_TEA_OPTIONS);
+DECLARE_MTYPE(BGP_TEA_OPTIONS_VALUE);
+
+DECLARE_MTYPE(LCOMMUNITY);
+DECLARE_MTYPE(LCOMMUNITY_STR);
+DECLARE_MTYPE(LCOMMUNITY_VAL);
+
+DECLARE_MTYPE(BGP_EVPN_MH_INFO);
+DECLARE_MTYPE(BGP_EVPN_ES);
+DECLARE_MTYPE(BGP_EVPN_ES_FRAG);
+DECLARE_MTYPE(BGP_EVPN_ES_EVI);
+DECLARE_MTYPE(BGP_EVPN_ES_VRF);
+DECLARE_MTYPE(BGP_EVPN_ES_VTEP);
+DECLARE_MTYPE(BGP_EVPN_PATH_ES_INFO);
+DECLARE_MTYPE(BGP_EVPN_PATH_MH_INFO);
+DECLARE_MTYPE(BGP_EVPN_PATH_NH_INFO);
+DECLARE_MTYPE(BGP_EVPN_NH);
+DECLARE_MTYPE(BGP_EVPN_ES_EVI_VTEP);
+
+DECLARE_MTYPE(BGP_EVPN);
+DECLARE_MTYPE(BGP_EVPN_IMPORT_RT);
+DECLARE_MTYPE(BGP_EVPN_VRF_IMPORT_RT);
+
+DECLARE_MTYPE(BGP_SRV6_L3VPN);
+DECLARE_MTYPE(BGP_SRV6_VPN);
+DECLARE_MTYPE(BGP_SRV6_SID);
+DECLARE_MTYPE(BGP_SRV6_FUNCTION);
+
+DECLARE_MTYPE(EVPN_REMOTE_IP);
+
+DECLARE_MTYPE(BGP_NOTIFICATION);
+
+#endif /* _QUAGGA_BGP_MEMORY_H */
diff --git a/bgpd/bgp_mpath.c b/bgpd/bgp_mpath.c
new file mode 100644
index 0000000..64af8a5
--- /dev/null
+++ b/bgpd/bgp_mpath.c
@@ -0,0 +1,931 @@
+/*
+ * BGP Multipath
+ * Copyright (C) 2010 Google Inc.
+ *
+ * This file is part of Quagga
+ *
+ * Quagga is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License as published by the
+ * Free Software Foundation; either version 2, or (at your option) any
+ * later version.
+ *
+ * Quagga is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program; see the file COPYING; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
+ */
+
+#include <zebra.h>
+
+#include "command.h"
+#include "prefix.h"
+#include "linklist.h"
+#include "sockunion.h"
+#include "memory.h"
+#include "queue.h"
+#include "filter.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_aspath.h"
+#include "bgpd/bgp_community.h"
+#include "bgpd/bgp_ecommunity.h"
+#include "bgpd/bgp_lcommunity.h"
+#include "bgpd/bgp_mpath.h"
+
+/*
+ * bgp_maximum_paths_set
+ *
+ * Record maximum-paths configuration for BGP instance
+ */
+int bgp_maximum_paths_set(struct bgp *bgp, afi_t afi, safi_t safi, int peertype,
+ uint16_t maxpaths, bool same_clusterlen)
+{
+ if (!bgp || (afi >= AFI_MAX) || (safi >= SAFI_MAX))
+ return -1;
+
+ switch (peertype) {
+ case BGP_PEER_IBGP:
+ bgp->maxpaths[afi][safi].maxpaths_ibgp = maxpaths;
+ bgp->maxpaths[afi][safi].same_clusterlen = same_clusterlen;
+ break;
+ case BGP_PEER_EBGP:
+ bgp->maxpaths[afi][safi].maxpaths_ebgp = maxpaths;
+ break;
+ default:
+ return -1;
+ }
+
+ return 0;
+}
+
+/*
+ * bgp_maximum_paths_unset
+ *
+ * Remove maximum-paths configuration from BGP instance
+ */
+int bgp_maximum_paths_unset(struct bgp *bgp, afi_t afi, safi_t safi,
+ int peertype)
+{
+ if (!bgp || (afi >= AFI_MAX) || (safi >= SAFI_MAX))
+ return -1;
+
+ switch (peertype) {
+ case BGP_PEER_IBGP:
+ bgp->maxpaths[afi][safi].maxpaths_ibgp = multipath_num;
+ bgp->maxpaths[afi][safi].same_clusterlen = false;
+ break;
+ case BGP_PEER_EBGP:
+ bgp->maxpaths[afi][safi].maxpaths_ebgp = multipath_num;
+ break;
+ default:
+ return -1;
+ }
+
+ return 0;
+}
+
+/*
+ * bgp_interface_same
+ *
+ * Return true if ifindex for ifp1 and ifp2 are the same, else return false.
+ */
+static int bgp_interface_same(struct interface *ifp1, struct interface *ifp2)
+{
+ if (!ifp1 && !ifp2)
+ return 1;
+
+ if (!ifp1 && ifp2)
+ return 0;
+
+ if (ifp1 && !ifp2)
+ return 0;
+
+ return (ifp1->ifindex == ifp2->ifindex);
+}
+
+
+/*
+ * bgp_path_info_nexthop_cmp
+ *
+ * Compare the nexthops of two paths. Return value is less than, equal to,
+ * or greater than zero if bpi1 is respectively less than, equal to,
+ * or greater than bpi2.
+ */
+int bgp_path_info_nexthop_cmp(struct bgp_path_info *bpi1,
+ struct bgp_path_info *bpi2)
+{
+ int compare;
+ struct in6_addr addr1, addr2;
+
+ compare = IPV4_ADDR_CMP(&bpi1->attr->nexthop, &bpi2->attr->nexthop);
+ if (!compare) {
+ if (bpi1->attr->mp_nexthop_len == bpi2->attr->mp_nexthop_len) {
+ switch (bpi1->attr->mp_nexthop_len) {
+ case BGP_ATTR_NHLEN_IPV4:
+ case BGP_ATTR_NHLEN_VPNV4:
+ compare = IPV4_ADDR_CMP(
+ &bpi1->attr->mp_nexthop_global_in,
+ &bpi2->attr->mp_nexthop_global_in);
+ break;
+ case BGP_ATTR_NHLEN_IPV6_GLOBAL:
+ case BGP_ATTR_NHLEN_VPNV6_GLOBAL:
+ compare = IPV6_ADDR_CMP(
+ &bpi1->attr->mp_nexthop_global,
+ &bpi2->attr->mp_nexthop_global);
+ break;
+ case BGP_ATTR_NHLEN_IPV6_GLOBAL_AND_LL:
+ addr1 = (bpi1->attr->mp_nexthop_prefer_global)
+ ? bpi1->attr->mp_nexthop_global
+ : bpi1->attr->mp_nexthop_local;
+ addr2 = (bpi2->attr->mp_nexthop_prefer_global)
+ ? bpi2->attr->mp_nexthop_global
+ : bpi2->attr->mp_nexthop_local;
+
+ if (!bpi1->attr->mp_nexthop_prefer_global
+ && !bpi2->attr->mp_nexthop_prefer_global)
+ compare = !bgp_interface_same(
+ bpi1->peer->ifp,
+ bpi2->peer->ifp);
+
+ if (!compare)
+ compare = IPV6_ADDR_CMP(&addr1, &addr2);
+ break;
+ }
+ }
+
+ /* This can happen if one IPv6 peer sends you global and
+ * link-local
+ * nexthops but another IPv6 peer only sends you global
+ */
+ else if (bpi1->attr->mp_nexthop_len
+ == BGP_ATTR_NHLEN_IPV6_GLOBAL
+ || bpi1->attr->mp_nexthop_len
+ == BGP_ATTR_NHLEN_IPV6_GLOBAL_AND_LL) {
+ compare = IPV6_ADDR_CMP(&bpi1->attr->mp_nexthop_global,
+ &bpi2->attr->mp_nexthop_global);
+ if (!compare) {
+ if (bpi1->attr->mp_nexthop_len
+ < bpi2->attr->mp_nexthop_len)
+ compare = -1;
+ else
+ compare = 1;
+ }
+ }
+ }
+
+ /*
+ * If both nexthops are same then check
+ * if they belong to same VRF
+ */
+ if (!compare && bpi1->attr->nh_type != NEXTHOP_TYPE_BLACKHOLE) {
+ if (bpi1->extra && bpi1->extra->bgp_orig && bpi2->extra
+ && bpi2->extra->bgp_orig) {
+ if (bpi1->extra->bgp_orig->vrf_id
+ != bpi2->extra->bgp_orig->vrf_id) {
+ compare = 1;
+ }
+ }
+ }
+
+ return compare;
+}
+
+/*
+ * bgp_path_info_mpath_cmp
+ *
+ * This function determines our multipath list ordering. By ordering
+ * the list we can deterministically select which paths are included
+ * in the multipath set. The ordering also helps in detecting changes
+ * in the multipath selection so we can detect whether to send an
+ * update to zebra.
+ *
+ * The order of paths is determined first by received nexthop, and then
+ * by peer address if the nexthops are the same.
+ */
+static int bgp_path_info_mpath_cmp(void *val1, void *val2)
+{
+ struct bgp_path_info *bpi1, *bpi2;
+ int compare;
+
+ bpi1 = val1;
+ bpi2 = val2;
+
+ compare = bgp_path_info_nexthop_cmp(bpi1, bpi2);
+
+ if (!compare) {
+ if (!bpi1->peer->su_remote && !bpi2->peer->su_remote)
+ compare = 0;
+ else if (!bpi1->peer->su_remote)
+ compare = 1;
+ else if (!bpi2->peer->su_remote)
+ compare = -1;
+ else
+ compare = sockunion_cmp(bpi1->peer->su_remote,
+ bpi2->peer->su_remote);
+ }
+
+ return compare;
+}
+
+/*
+ * bgp_mp_list_init
+ *
+ * Initialize the mp_list, which holds the list of multipaths
+ * selected by bgp_best_selection
+ */
+void bgp_mp_list_init(struct list *mp_list)
+{
+ assert(mp_list);
+ memset(mp_list, 0, sizeof(struct list));
+ mp_list->cmp = bgp_path_info_mpath_cmp;
+}
+
+/*
+ * bgp_mp_list_clear
+ *
+ * Clears all entries out of the mp_list
+ */
+void bgp_mp_list_clear(struct list *mp_list)
+{
+ assert(mp_list);
+ list_delete_all_node(mp_list);
+}
+
+/*
+ * bgp_mp_list_add
+ *
+ * Adds a multipath entry to the mp_list
+ */
+void bgp_mp_list_add(struct list *mp_list, struct bgp_path_info *mpinfo)
+{
+ assert(mp_list && mpinfo);
+ listnode_add_sort(mp_list, mpinfo);
+}
+
+/*
+ * bgp_path_info_mpath_new
+ *
+ * Allocate and zero memory for a new bgp_path_info_mpath element
+ */
+static struct bgp_path_info_mpath *bgp_path_info_mpath_new(void)
+{
+ struct bgp_path_info_mpath *new_mpath;
+ new_mpath = XCALLOC(MTYPE_BGP_MPATH_INFO,
+ sizeof(struct bgp_path_info_mpath));
+ return new_mpath;
+}
+
+/*
+ * bgp_path_info_mpath_free
+ *
+ * Release resources for a bgp_path_info_mpath element and zero out pointer
+ */
+void bgp_path_info_mpath_free(struct bgp_path_info_mpath **mpath)
+{
+ if (mpath && *mpath) {
+ if ((*mpath)->mp_attr)
+ bgp_attr_unintern(&(*mpath)->mp_attr);
+ XFREE(MTYPE_BGP_MPATH_INFO, *mpath);
+ }
+}
+
+/*
+ * bgp_path_info_mpath_get
+ *
+ * Fetch the mpath element for the given bgp_path_info. Used for
+ * doing lazy allocation.
+ */
+static struct bgp_path_info_mpath *
+bgp_path_info_mpath_get(struct bgp_path_info *path)
+{
+ struct bgp_path_info_mpath *mpath;
+
+ if (!path)
+ return NULL;
+
+ if (!path->mpath) {
+ mpath = bgp_path_info_mpath_new();
+ if (!mpath)
+ return NULL;
+ path->mpath = mpath;
+ mpath->mp_info = path;
+ }
+ return path->mpath;
+}
+
+/*
+ * bgp_path_info_mpath_enqueue
+ *
+ * Enqueue a path onto the multipath list given the previous multipath
+ * list entry
+ */
+static void bgp_path_info_mpath_enqueue(struct bgp_path_info *prev_info,
+ struct bgp_path_info *path)
+{
+ struct bgp_path_info_mpath *prev, *mpath;
+
+ prev = bgp_path_info_mpath_get(prev_info);
+ mpath = bgp_path_info_mpath_get(path);
+ if (!prev || !mpath)
+ return;
+
+ mpath->mp_next = prev->mp_next;
+ mpath->mp_prev = prev;
+ if (prev->mp_next)
+ prev->mp_next->mp_prev = mpath;
+ prev->mp_next = mpath;
+
+ SET_FLAG(path->flags, BGP_PATH_MULTIPATH);
+}
+
+/*
+ * bgp_path_info_mpath_dequeue
+ *
+ * Remove a path from the multipath list
+ */
+void bgp_path_info_mpath_dequeue(struct bgp_path_info *path)
+{
+ struct bgp_path_info_mpath *mpath = path->mpath;
+ if (!mpath)
+ return;
+ if (mpath->mp_prev)
+ mpath->mp_prev->mp_next = mpath->mp_next;
+ if (mpath->mp_next)
+ mpath->mp_next->mp_prev = mpath->mp_prev;
+ mpath->mp_next = mpath->mp_prev = NULL;
+ UNSET_FLAG(path->flags, BGP_PATH_MULTIPATH);
+}
+
+/*
+ * bgp_path_info_mpath_next
+ *
+ * Given a bgp_path_info, return the next multipath entry
+ */
+struct bgp_path_info *bgp_path_info_mpath_next(struct bgp_path_info *path)
+{
+ if (!path->mpath || !path->mpath->mp_next)
+ return NULL;
+ return path->mpath->mp_next->mp_info;
+}
+
+/*
+ * bgp_path_info_mpath_first
+ *
+ * Given bestpath bgp_path_info, return the first multipath entry.
+ */
+struct bgp_path_info *bgp_path_info_mpath_first(struct bgp_path_info *path)
+{
+ return bgp_path_info_mpath_next(path);
+}
+
+/*
+ * bgp_path_info_mpath_count
+ *
+ * Given the bestpath bgp_path_info, return the number of multipath entries
+ */
+uint32_t bgp_path_info_mpath_count(struct bgp_path_info *path)
+{
+ if (!path->mpath)
+ return 0;
+ return path->mpath->mp_count;
+}
+
+/*
+ * bgp_path_info_mpath_count_set
+ *
+ * Sets the count of multipaths into bestpath's mpath element
+ */
+static void bgp_path_info_mpath_count_set(struct bgp_path_info *path,
+ uint16_t count)
+{
+ struct bgp_path_info_mpath *mpath;
+ if (!count && !path->mpath)
+ return;
+ mpath = bgp_path_info_mpath_get(path);
+ if (!mpath)
+ return;
+ mpath->mp_count = count;
+}
+
+/*
+ * bgp_path_info_mpath_lb_update
+ *
+ * Update cumulative info related to link-bandwidth
+ */
+static void bgp_path_info_mpath_lb_update(struct bgp_path_info *path, bool set,
+ bool all_paths_lb, uint64_t cum_bw)
+{
+ struct bgp_path_info_mpath *mpath;
+
+ mpath = path->mpath;
+ if (mpath == NULL) {
+ if (!set || (cum_bw == 0 && !all_paths_lb))
+ return;
+
+ mpath = bgp_path_info_mpath_get(path);
+ if (!mpath)
+ return;
+ }
+ if (set) {
+ if (cum_bw)
+ SET_FLAG(mpath->mp_flags, BGP_MP_LB_PRESENT);
+ else
+ UNSET_FLAG(mpath->mp_flags, BGP_MP_LB_PRESENT);
+ if (all_paths_lb)
+ SET_FLAG(mpath->mp_flags, BGP_MP_LB_ALL);
+ else
+ UNSET_FLAG(mpath->mp_flags, BGP_MP_LB_ALL);
+ mpath->cum_bw = cum_bw;
+ } else {
+ mpath->mp_flags = 0;
+ mpath->cum_bw = 0;
+ }
+}
+
+/*
+ * bgp_path_info_mpath_attr
+ *
+ * Given bestpath bgp_path_info, return aggregated attribute set used
+ * for advertising the multipath route
+ */
+struct attr *bgp_path_info_mpath_attr(struct bgp_path_info *path)
+{
+ if (!path->mpath)
+ return NULL;
+ return path->mpath->mp_attr;
+}
+
+/*
+ * bgp_path_info_chkwtd
+ *
+ * Return if we should attempt to do weighted ECMP or not
+ * The path passed in is the bestpath.
+ */
+bool bgp_path_info_mpath_chkwtd(struct bgp *bgp, struct bgp_path_info *path)
+{
+ /* Check if told to ignore weights or not multipath */
+ if (bgp->lb_handling == BGP_LINK_BW_IGNORE_BW || !path->mpath)
+ return false;
+
+ /* All paths in multipath should have associated weight (bandwidth)
+ * unless told explicitly otherwise.
+ */
+ if (bgp->lb_handling != BGP_LINK_BW_SKIP_MISSING &&
+ bgp->lb_handling != BGP_LINK_BW_DEFWT_4_MISSING)
+ return (path->mpath->mp_flags & BGP_MP_LB_ALL);
+
+ /* At least one path should have bandwidth. */
+ return (path->mpath->mp_flags & BGP_MP_LB_PRESENT);
+}
+
+/*
+ * bgp_path_info_mpath_attr
+ *
+ * Given bestpath bgp_path_info, return cumulative bandwidth
+ * computed for all multipaths with bandwidth info
+ */
+uint64_t bgp_path_info_mpath_cumbw(struct bgp_path_info *path)
+{
+ if (!path->mpath)
+ return 0;
+ return path->mpath->cum_bw;
+}
+
+/*
+ * bgp_path_info_mpath_attr_set
+ *
+ * Sets the aggregated attribute into bestpath's mpath element
+ */
+static void bgp_path_info_mpath_attr_set(struct bgp_path_info *path,
+ struct attr *attr)
+{
+ struct bgp_path_info_mpath *mpath;
+ if (!attr && !path->mpath)
+ return;
+ mpath = bgp_path_info_mpath_get(path);
+ if (!mpath)
+ return;
+ mpath->mp_attr = attr;
+}
+
+/*
+ * bgp_path_info_mpath_update
+ *
+ * Compare and sync up the multipath list with the mp_list generated by
+ * bgp_best_selection
+ */
+void bgp_path_info_mpath_update(struct bgp *bgp, struct bgp_dest *dest,
+ struct bgp_path_info *new_best,
+ struct bgp_path_info *old_best,
+ struct list *mp_list,
+ struct bgp_maxpaths_cfg *mpath_cfg)
+{
+ uint16_t maxpaths, mpath_count, old_mpath_count;
+ uint32_t bwval;
+ uint64_t cum_bw, old_cum_bw;
+ struct listnode *mp_node, *mp_next_node;
+ struct bgp_path_info *cur_mpath, *new_mpath, *next_mpath, *prev_mpath;
+ int mpath_changed, debug;
+ bool all_paths_lb;
+ char path_buf[PATH_ADDPATH_STR_BUFFER];
+
+ mpath_changed = 0;
+ maxpaths = multipath_num;
+ mpath_count = 0;
+ cur_mpath = NULL;
+ old_mpath_count = 0;
+ old_cum_bw = cum_bw = 0;
+ prev_mpath = new_best;
+ mp_node = listhead(mp_list);
+ debug = bgp_debug_bestpath(dest);
+
+ if (new_best) {
+ mpath_count++;
+ if (new_best != old_best)
+ bgp_path_info_mpath_dequeue(new_best);
+ maxpaths = (new_best->peer->sort == BGP_PEER_IBGP)
+ ? mpath_cfg->maxpaths_ibgp
+ : mpath_cfg->maxpaths_ebgp;
+ }
+
+ if (old_best) {
+ cur_mpath = bgp_path_info_mpath_first(old_best);
+ old_mpath_count = bgp_path_info_mpath_count(old_best);
+ old_cum_bw = bgp_path_info_mpath_cumbw(old_best);
+ bgp_path_info_mpath_count_set(old_best, 0);
+ bgp_path_info_mpath_lb_update(old_best, false, false, 0);
+ bgp_path_info_mpath_dequeue(old_best);
+ }
+
+ if (debug)
+ zlog_debug(
+ "%pRN(%s): starting mpath update, newbest %s num candidates %d old-mpath-count %d old-cum-bw u%" PRIu64,
+ bgp_dest_to_rnode(dest), bgp->name_pretty,
+ new_best ? new_best->peer->host : "NONE",
+ mp_list ? listcount(mp_list) : 0, old_mpath_count,
+ old_cum_bw);
+
+ /*
+ * We perform an ordered walk through both lists in parallel.
+ * The reason for the ordered walk is that if there are paths
+ * that were previously multipaths and are still multipaths, the walk
+ * should encounter them in both lists at the same time. Otherwise
+ * there will be paths that are in one list or another, and we
+ * will deal with these separately.
+ *
+ * Note that new_best might be somewhere in the mp_list, so we need
+ * to skip over it
+ */
+ all_paths_lb = true; /* We'll reset if any path doesn't have LB. */
+ while (mp_node || cur_mpath) {
+ struct bgp_path_info *tmp_info;
+
+ /*
+ * We can bail out of this loop if all existing paths on the
+ * multipath list have been visited (for cleanup purposes) and
+ * the maxpath requirement is fulfulled
+ */
+ if (!cur_mpath && (mpath_count >= maxpaths))
+ break;
+
+ mp_next_node = mp_node ? listnextnode(mp_node) : NULL;
+ next_mpath =
+ cur_mpath ? bgp_path_info_mpath_next(cur_mpath) : NULL;
+ tmp_info = mp_node ? listgetdata(mp_node) : NULL;
+
+ if (debug)
+ zlog_debug(
+ "%pRN(%s): comparing candidate %s with existing mpath %s",
+ bgp_dest_to_rnode(dest), bgp->name_pretty,
+ tmp_info ? tmp_info->peer->host : "NONE",
+ cur_mpath ? cur_mpath->peer->host : "NONE");
+
+ /*
+ * If equal, the path was a multipath and is still a multipath.
+ * Insert onto new multipath list if maxpaths allows.
+ */
+ if (mp_node && (listgetdata(mp_node) == cur_mpath)) {
+ list_delete_node(mp_list, mp_node);
+ bgp_path_info_mpath_dequeue(cur_mpath);
+ if ((mpath_count < maxpaths)
+ && prev_mpath
+ && bgp_path_info_nexthop_cmp(prev_mpath,
+ cur_mpath)) {
+ bgp_path_info_mpath_enqueue(prev_mpath,
+ cur_mpath);
+ prev_mpath = cur_mpath;
+ mpath_count++;
+ if (ecommunity_linkbw_present(
+ bgp_attr_get_ecommunity(
+ cur_mpath->attr),
+ &bwval))
+ cum_bw += bwval;
+ else
+ all_paths_lb = false;
+ if (debug) {
+ bgp_path_info_path_with_addpath_rx_str(
+ cur_mpath, path_buf,
+ sizeof(path_buf));
+ zlog_debug(
+ "%pRN: %s is still multipath, cur count %d",
+ bgp_dest_to_rnode(dest),
+ path_buf, mpath_count);
+ }
+ } else {
+ mpath_changed = 1;
+ if (debug) {
+ bgp_path_info_path_with_addpath_rx_str(
+ cur_mpath, path_buf,
+ sizeof(path_buf));
+ zlog_debug(
+ "%pRN: remove mpath %s nexthop %pI4, cur count %d",
+ bgp_dest_to_rnode(dest),
+ path_buf,
+ &cur_mpath->attr->nexthop,
+ mpath_count);
+ }
+ }
+ mp_node = mp_next_node;
+ cur_mpath = next_mpath;
+ continue;
+ }
+
+ if (cur_mpath
+ && (!mp_node
+ || (bgp_path_info_mpath_cmp(cur_mpath,
+ listgetdata(mp_node))
+ < 0))) {
+ /*
+ * If here, we have an old multipath and either the
+ * mp_list
+ * is finished or the next mp_node points to a later
+ * multipath, so we need to purge this path from the
+ * multipath list
+ */
+ bgp_path_info_mpath_dequeue(cur_mpath);
+ mpath_changed = 1;
+ if (debug) {
+ bgp_path_info_path_with_addpath_rx_str(
+ cur_mpath, path_buf, sizeof(path_buf));
+ zlog_debug(
+ "%pRN: remove mpath %s nexthop %pI4, cur count %d",
+ bgp_dest_to_rnode(dest), path_buf,
+ &cur_mpath->attr->nexthop, mpath_count);
+ }
+ cur_mpath = next_mpath;
+ } else {
+ /*
+ * If here, we have a path on the mp_list that was not
+ * previously
+ * a multipath (due to non-equivalance or maxpaths
+ * exceeded),
+ * or the matching multipath is sorted later in the
+ * multipath
+ * list. Before we enqueue the path on the new multipath
+ * list,
+ * make sure its not on the old_best multipath list or
+ * referenced
+ * via next_mpath:
+ * - If next_mpath points to this new path, update
+ * next_mpath to
+ * point to the multipath after this one
+ * - Dequeue the path from the multipath list just to
+ * make sure
+ */
+ new_mpath = listgetdata(mp_node);
+ list_delete_node(mp_list, mp_node);
+ assert(new_mpath);
+ assert(prev_mpath);
+ if ((mpath_count < maxpaths) && (new_mpath != new_best)
+ && bgp_path_info_nexthop_cmp(prev_mpath,
+ new_mpath)) {
+ bgp_path_info_mpath_dequeue(new_mpath);
+
+ bgp_path_info_mpath_enqueue(prev_mpath,
+ new_mpath);
+ prev_mpath = new_mpath;
+ mpath_changed = 1;
+ mpath_count++;
+ if (ecommunity_linkbw_present(
+ bgp_attr_get_ecommunity(
+ new_mpath->attr),
+ &bwval))
+ cum_bw += bwval;
+ else
+ all_paths_lb = false;
+ if (debug) {
+ bgp_path_info_path_with_addpath_rx_str(
+ new_mpath, path_buf,
+ sizeof(path_buf));
+ zlog_debug(
+ "%pRN: add mpath %s nexthop %pI4, cur count %d",
+ bgp_dest_to_rnode(dest),
+ path_buf,
+ &new_mpath->attr->nexthop,
+ mpath_count);
+ }
+ }
+ mp_node = mp_next_node;
+ }
+ }
+
+ if (new_best) {
+ bgp_path_info_mpath_count_set(new_best, mpath_count - 1);
+ if (mpath_count <= 1 ||
+ !ecommunity_linkbw_present(
+ bgp_attr_get_ecommunity(new_best->attr), &bwval))
+ all_paths_lb = false;
+ else
+ cum_bw += bwval;
+ bgp_path_info_mpath_lb_update(new_best, true,
+ all_paths_lb, cum_bw);
+
+ if (debug)
+ zlog_debug(
+ "%pRN(%s): New mpath count (incl newbest) %d mpath-change %s all_paths_lb %d cum_bw u%" PRIu64,
+ bgp_dest_to_rnode(dest), bgp->name_pretty,
+ mpath_count, mpath_changed ? "YES" : "NO",
+ all_paths_lb, cum_bw);
+
+ if (mpath_changed
+ || (bgp_path_info_mpath_count(new_best) != old_mpath_count))
+ SET_FLAG(new_best->flags, BGP_PATH_MULTIPATH_CHG);
+ if ((mpath_count - 1) != old_mpath_count ||
+ old_cum_bw != cum_bw)
+ SET_FLAG(new_best->flags, BGP_PATH_LINK_BW_CHG);
+ }
+}
+
+/*
+ * bgp_mp_dmed_deselect
+ *
+ * Clean up multipath information for BGP_PATH_DMED_SELECTED path that
+ * is not selected as best path
+ */
+void bgp_mp_dmed_deselect(struct bgp_path_info *dmed_best)
+{
+ struct bgp_path_info *mpinfo, *mpnext;
+
+ if (!dmed_best)
+ return;
+
+ for (mpinfo = bgp_path_info_mpath_first(dmed_best); mpinfo;
+ mpinfo = mpnext) {
+ mpnext = bgp_path_info_mpath_next(mpinfo);
+ bgp_path_info_mpath_dequeue(mpinfo);
+ }
+
+ bgp_path_info_mpath_count_set(dmed_best, 0);
+ UNSET_FLAG(dmed_best->flags, BGP_PATH_MULTIPATH_CHG);
+ UNSET_FLAG(dmed_best->flags, BGP_PATH_LINK_BW_CHG);
+ assert(bgp_path_info_mpath_first(dmed_best) == NULL);
+}
+
+/*
+ * bgp_path_info_mpath_aggregate_update
+ *
+ * Set the multipath aggregate attribute. We need to see if the
+ * aggregate has changed and then set the ATTR_CHANGED flag on the
+ * bestpath info so that a peer update will be generated. The
+ * change is detected by generating the current attribute,
+ * interning it, and then comparing the interned pointer with the
+ * current value. We can skip this generate/compare step if there
+ * is no change in multipath selection and no attribute change in
+ * any multipath.
+ */
+void bgp_path_info_mpath_aggregate_update(struct bgp_path_info *new_best,
+ struct bgp_path_info *old_best)
+{
+ struct bgp_path_info *mpinfo;
+ struct aspath *aspath;
+ struct aspath *asmerge;
+ struct attr *new_attr, *old_attr;
+ uint8_t origin;
+ struct community *community, *commerge;
+ struct ecommunity *ecomm, *ecommerge;
+ struct lcommunity *lcomm, *lcommerge;
+ struct attr attr = {0};
+
+ if (old_best && (old_best != new_best)
+ && (old_attr = bgp_path_info_mpath_attr(old_best))) {
+ bgp_attr_unintern(&old_attr);
+ bgp_path_info_mpath_attr_set(old_best, NULL);
+ }
+
+ if (!new_best)
+ return;
+
+ if (!bgp_path_info_mpath_count(new_best)) {
+ if ((new_attr = bgp_path_info_mpath_attr(new_best))) {
+ bgp_attr_unintern(&new_attr);
+ bgp_path_info_mpath_attr_set(new_best, NULL);
+ SET_FLAG(new_best->flags, BGP_PATH_ATTR_CHANGED);
+ }
+ return;
+ }
+
+ attr = *new_best->attr;
+
+ if (new_best->peer
+ && CHECK_FLAG(new_best->peer->bgp->flags,
+ BGP_FLAG_MULTIPATH_RELAX_AS_SET)) {
+
+ /* aggregate attribute from multipath constituents */
+ aspath = aspath_dup(attr.aspath);
+ origin = attr.origin;
+ community =
+ bgp_attr_get_community(&attr)
+ ? community_dup(bgp_attr_get_community(&attr))
+ : NULL;
+ ecomm = (bgp_attr_get_ecommunity(&attr))
+ ? ecommunity_dup(bgp_attr_get_ecommunity(&attr))
+ : NULL;
+ lcomm = (bgp_attr_get_lcommunity(&attr))
+ ? lcommunity_dup(bgp_attr_get_lcommunity(&attr))
+ : NULL;
+
+ for (mpinfo = bgp_path_info_mpath_first(new_best); mpinfo;
+ mpinfo = bgp_path_info_mpath_next(mpinfo)) {
+ asmerge =
+ aspath_aggregate(aspath, mpinfo->attr->aspath);
+ aspath_free(aspath);
+ aspath = asmerge;
+
+ if (origin < mpinfo->attr->origin)
+ origin = mpinfo->attr->origin;
+
+ if (bgp_attr_get_community(mpinfo->attr)) {
+ if (community) {
+ commerge = community_merge(
+ community,
+ bgp_attr_get_community(
+ mpinfo->attr));
+ community =
+ community_uniq_sort(commerge);
+ community_free(&commerge);
+ } else
+ community = community_dup(
+ bgp_attr_get_community(
+ mpinfo->attr));
+ }
+
+ if (bgp_attr_get_ecommunity(mpinfo->attr)) {
+ if (ecomm) {
+ ecommerge = ecommunity_merge(
+ ecomm, bgp_attr_get_ecommunity(
+ mpinfo->attr));
+ ecomm = ecommunity_uniq_sort(ecommerge);
+ ecommunity_free(&ecommerge);
+ } else
+ ecomm = ecommunity_dup(
+ bgp_attr_get_ecommunity(
+ mpinfo->attr));
+ }
+ if (bgp_attr_get_lcommunity(mpinfo->attr)) {
+ if (lcomm) {
+ lcommerge = lcommunity_merge(
+ lcomm, bgp_attr_get_lcommunity(
+ mpinfo->attr));
+ lcomm = lcommunity_uniq_sort(lcommerge);
+ lcommunity_free(&lcommerge);
+ } else
+ lcomm = lcommunity_dup(
+ bgp_attr_get_lcommunity(
+ mpinfo->attr));
+ }
+ }
+
+ attr.aspath = aspath;
+ attr.origin = origin;
+ if (community)
+ bgp_attr_set_community(&attr, community);
+ if (ecomm)
+ bgp_attr_set_ecommunity(&attr, ecomm);
+ if (lcomm)
+ bgp_attr_set_lcommunity(&attr, lcomm);
+
+ /* Zap multipath attr nexthop so we set nexthop to self */
+ attr.nexthop.s_addr = INADDR_ANY;
+ memset(&attr.mp_nexthop_global, 0, sizeof(struct in6_addr));
+
+ /* TODO: should we set ATOMIC_AGGREGATE and AGGREGATOR? */
+ }
+
+ new_attr = bgp_attr_intern(&attr);
+
+ if (new_attr != bgp_path_info_mpath_attr(new_best)) {
+ if ((old_attr = bgp_path_info_mpath_attr(new_best)))
+ bgp_attr_unintern(&old_attr);
+ bgp_path_info_mpath_attr_set(new_best, new_attr);
+ SET_FLAG(new_best->flags, BGP_PATH_ATTR_CHANGED);
+ } else
+ bgp_attr_unintern(&new_attr);
+}
diff --git a/bgpd/bgp_mpath.h b/bgpd/bgp_mpath.h
new file mode 100644
index 0000000..4925f16
--- /dev/null
+++ b/bgpd/bgp_mpath.h
@@ -0,0 +1,95 @@
+/*
+ * BGP Multipath
+ * Copyright (C) 2010 Google Inc.
+ *
+ * This file is part of Quagga
+ *
+ * Quagga is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License as published by the
+ * Free Software Foundation; either version 2, or (at your option) any
+ * later version.
+ *
+ * Quagga is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program; see the file COPYING; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
+ */
+
+#ifndef _FRR_BGP_MPATH_H
+#define _FRR_BGP_MPATH_H
+
+/* Supplemental information linked to bgp_path_info for keeping track of
+ * multipath selections, lazily allocated to save memory
+ */
+struct bgp_path_info_mpath {
+ /* Points to the first multipath (on bestpath) or the next multipath */
+ struct bgp_path_info_mpath *mp_next;
+
+ /* Points to the previous multipath or NULL on bestpath */
+ struct bgp_path_info_mpath *mp_prev;
+
+ /* Points to bgp_path_info associated with this multipath info */
+ struct bgp_path_info *mp_info;
+
+ /* When attached to best path, the number of selected multipaths */
+ uint16_t mp_count;
+
+ /* Flags - relevant as noted. */
+ uint16_t mp_flags;
+#define BGP_MP_LB_PRESENT 0x1 /* Link-bandwidth present for >= 1 path */
+#define BGP_MP_LB_ALL 0x2 /* Link-bandwidth present for all multipaths */
+
+ /* Aggregated attribute for advertising multipath route */
+ struct attr *mp_attr;
+
+ /* Cumulative bandiwdth of all multipaths - attached to best path. */
+ uint64_t cum_bw;
+};
+
+/* Functions to support maximum-paths configuration */
+extern int bgp_maximum_paths_set(struct bgp *bgp, afi_t afi, safi_t safi,
+ int peertype, uint16_t maxpaths,
+ bool clusterlen);
+extern int bgp_maximum_paths_unset(struct bgp *bgp, afi_t afi, safi_t safi,
+ int peertype);
+
+/* Functions used by bgp_best_selection to record current
+ * multipath selections
+ */
+extern int bgp_path_info_nexthop_cmp(struct bgp_path_info *bpi1,
+ struct bgp_path_info *bpi2);
+extern void bgp_mp_list_init(struct list *mp_list);
+extern void bgp_mp_list_clear(struct list *mp_list);
+extern void bgp_mp_list_add(struct list *mp_list, struct bgp_path_info *mpinfo);
+extern void bgp_mp_dmed_deselect(struct bgp_path_info *dmed_best);
+extern void bgp_path_info_mpath_update(struct bgp *bgp, struct bgp_dest *dest,
+ struct bgp_path_info *new_best,
+ struct bgp_path_info *old_best,
+ struct list *mp_list,
+ struct bgp_maxpaths_cfg *mpath_cfg);
+extern void
+bgp_path_info_mpath_aggregate_update(struct bgp_path_info *new_best,
+ struct bgp_path_info *old_best);
+
+/* Unlink and free multipath information associated with a bgp_path_info */
+extern void bgp_path_info_mpath_dequeue(struct bgp_path_info *path);
+extern void bgp_path_info_mpath_free(struct bgp_path_info_mpath **mpath);
+
+/* Walk list of multipaths associated with a best path */
+extern struct bgp_path_info *
+bgp_path_info_mpath_first(struct bgp_path_info *path);
+extern struct bgp_path_info *
+bgp_path_info_mpath_next(struct bgp_path_info *path);
+
+/* Accessors for multipath information */
+extern uint32_t bgp_path_info_mpath_count(struct bgp_path_info *path);
+extern struct attr *bgp_path_info_mpath_attr(struct bgp_path_info *path);
+extern bool bgp_path_info_mpath_chkwtd(struct bgp *bgp,
+ struct bgp_path_info *path);
+extern uint64_t bgp_path_info_mpath_cumbw(struct bgp_path_info *path);
+
+#endif /* _FRR_BGP_MPATH_H */
diff --git a/bgpd/bgp_mplsvpn.c b/bgpd/bgp_mplsvpn.c
new file mode 100644
index 0000000..5d7aefa
--- /dev/null
+++ b/bgpd/bgp_mplsvpn.c
@@ -0,0 +1,3159 @@
+/* MPLS-VPN
+ * Copyright (C) 2000 Kunihiro Ishiguro <kunihiro@zebra.org>
+ *
+ * This file is part of GNU Zebra.
+ *
+ * GNU Zebra is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License as published by the
+ * Free Software Foundation; either version 2, or (at your option) any
+ * later version.
+ *
+ * GNU Zebra is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program; see the file COPYING; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
+ */
+
+#include <zebra.h>
+
+#include "command.h"
+#include "prefix.h"
+#include "log.h"
+#include "memory.h"
+#include "stream.h"
+#include "queue.h"
+#include "filter.h"
+#include "mpls.h"
+#include "json.h"
+#include "zclient.h"
+
+#include "bgpd/bgpd.h"
+#include "bgpd/bgp_debug.h"
+#include "bgpd/bgp_errors.h"
+#include "bgpd/bgp_table.h"
+#include "bgpd/bgp_route.h"
+#include "bgpd/bgp_attr.h"
+#include "bgpd/bgp_label.h"
+#include "bgpd/bgp_mplsvpn.h"
+#include "bgpd/bgp_packet.h"
+#include "bgpd/bgp_vty.h"
+#include "bgpd/bgp_vpn.h"
+#include "bgpd/bgp_ecommunity.h"
+#include "bgpd/bgp_zebra.h"
+#include "bgpd/bgp_nexthop.h"
+#include "bgpd/bgp_nht.h"
+#include "bgpd/bgp_evpn.h"
+#include "bgpd/bgp_memory.h"
+
+#ifdef ENABLE_BGP_VNC
+#include "bgpd/rfapi/rfapi_backend.h"
+#endif
+
+/*
+ * Definitions and external declarations.
+ */
+extern struct zclient *zclient;
+
+extern int argv_find_and_parse_vpnvx(struct cmd_token **argv, int argc,
+ int *index, afi_t *afi)
+{
+ int ret = 0;
+ if (argv_find(argv, argc, "vpnv4", index)) {
+ ret = 1;
+ if (afi)
+ *afi = AFI_IP;
+ } else if (argv_find(argv, argc, "vpnv6", index)) {
+ ret = 1;
+ if (afi)
+ *afi = AFI_IP6;
+ }
+ return ret;
+}
+
+uint32_t decode_label(mpls_label_t *label_pnt)
+{
+ uint32_t l;
+ uint8_t *pnt = (uint8_t *)label_pnt;
+
+ l = ((uint32_t)*pnt++ << 12);
+ l |= (uint32_t)*pnt++ << 4;
+ l |= (uint32_t)((*pnt & 0xf0) >> 4);
+ return l;
+}
+
+void encode_label(mpls_label_t label, mpls_label_t *label_pnt)
+{
+ uint8_t *pnt = (uint8_t *)label_pnt;
+ if (pnt == NULL)
+ return;
+ if (label == BGP_PREVENT_VRF_2_VRF_LEAK) {
+ *label_pnt = label;
+ return;
+ }
+ *pnt++ = (label >> 12) & 0xff;
+ *pnt++ = (label >> 4) & 0xff;
+ *pnt++ = ((label << 4) + 1) & 0xff; /* S=1 */
+}
+
+int bgp_nlri_parse_vpn(struct peer *peer, struct attr *attr,
+ struct bgp_nlri *packet)
+{
+ struct prefix p;
+ uint8_t psize = 0;
+ uint8_t prefixlen;
+ uint16_t type;
+ struct rd_as rd_as;
+ struct rd_ip rd_ip;
+ struct prefix_rd prd = {0};
+ mpls_label_t label = {0};
+ afi_t afi;
+ safi_t safi;
+ bool addpath_capable;
+ uint32_t addpath_id;
+ int ret = 0;
+
+ /* Make prefix_rd */
+ prd.family = AF_UNSPEC;
+ prd.prefixlen = 64;
+
+ struct stream *data = stream_new(packet->length);
+ stream_put(data, packet->nlri, packet->length);
+ afi = packet->afi;
+ safi = packet->safi;
+ addpath_id = 0;
+
+ addpath_capable = bgp_addpath_encode_rx(peer, afi, safi);
+
+#define VPN_PREFIXLEN_MIN_BYTES (3 + 8) /* label + RD */
+ while (STREAM_READABLE(data) > 0) {
+ /* Clear prefix structure. */
+ memset(&p, 0, sizeof(p));
+
+ if (addpath_capable) {
+ STREAM_GET(&addpath_id, data, BGP_ADDPATH_ID_LEN);
+ addpath_id = ntohl(addpath_id);
+ }
+
+ if (STREAM_READABLE(data) < 1) {
+ flog_err(
+ EC_BGP_UPDATE_RCV,
+ "%s [Error] Update packet error / VPN (truncated NLRI of size %u; no prefix length)",
+ peer->host, packet->length);
+ ret = BGP_NLRI_PARSE_ERROR_PACKET_LENGTH;
+ goto done;
+ }
+
+ /* Fetch prefix length. */
+ STREAM_GETC(data, prefixlen);
+ p.family = afi2family(packet->afi);
+ psize = PSIZE(prefixlen);
+
+ if (prefixlen < VPN_PREFIXLEN_MIN_BYTES * 8) {
+ flog_err(
+ EC_BGP_UPDATE_RCV,
+ "%s [Error] Update packet error / VPN (prefix length %d less than VPN min length)",
+ peer->host, prefixlen);
+ ret = BGP_NLRI_PARSE_ERROR_PREFIX_LENGTH;
+ goto done;
+ }
+
+ /* sanity check against packet data */
+ if (STREAM_READABLE(data) < psize) {
+ flog_err(
+ EC_BGP_UPDATE_RCV,
+ "%s [Error] Update packet error / VPN (prefix length %d exceeds packet size %u)",
+ peer->host, prefixlen, packet->length);
+ ret = BGP_NLRI_PARSE_ERROR_PACKET_OVERFLOW;
+ goto done;
+ }
+
+ /* sanity check against storage for the IP address portion */
+ if ((psize - VPN_PREFIXLEN_MIN_BYTES) > (ssize_t)sizeof(p.u)) {
+ flog_err(
+ EC_BGP_UPDATE_RCV,
+ "%s [Error] Update packet error / VPN (psize %d exceeds storage size %zu)",
+ peer->host,
+ prefixlen - VPN_PREFIXLEN_MIN_BYTES * 8,
+ sizeof(p.u));
+ ret = BGP_NLRI_PARSE_ERROR_PACKET_LENGTH;
+ goto done;
+ }
+
+ /* Sanity check against max bitlen of the address family */
+ if ((psize - VPN_PREFIXLEN_MIN_BYTES) > prefix_blen(&p)) {
+ flog_err(
+ EC_BGP_UPDATE_RCV,
+ "%s [Error] Update packet error / VPN (psize %d exceeds family (%u) max byte len %u)",
+ peer->host,
+ prefixlen - VPN_PREFIXLEN_MIN_BYTES * 8,
+ p.family, prefix_blen(&p));
+ ret = BGP_NLRI_PARSE_ERROR_PACKET_LENGTH;
+ goto done;
+ }
+
+ /* Copy label to prefix. */
+ if (STREAM_READABLE(data) < BGP_LABEL_BYTES) {
+ flog_err(
+ EC_BGP_UPDATE_RCV,
+ "%s [Error] Update packet error / VPN (truncated NLRI of size %u; no label)",
+ peer->host, packet->length);
+ ret = BGP_NLRI_PARSE_ERROR_PACKET_LENGTH;
+ goto done;
+ }
+
+ STREAM_GET(&label, data, BGP_LABEL_BYTES);
+ bgp_set_valid_label(&label);
+
+ /* Copy routing distinguisher to rd. */
+ if (STREAM_READABLE(data) < 8) {
+ flog_err(
+ EC_BGP_UPDATE_RCV,
+ "%s [Error] Update packet error / VPN (truncated NLRI of size %u; no RD)",
+ peer->host, packet->length);
+ ret = BGP_NLRI_PARSE_ERROR_PACKET_LENGTH;
+ goto done;
+ }
+ STREAM_GET(&prd.val, data, 8);
+
+ /* Decode RD type. */
+ type = decode_rd_type(prd.val);
+
+ switch (type) {
+ case RD_TYPE_AS:
+ decode_rd_as(&prd.val[2], &rd_as);
+ break;
+
+ case RD_TYPE_AS4:
+ decode_rd_as4(&prd.val[2], &rd_as);
+ break;
+
+ case RD_TYPE_IP:
+ decode_rd_ip(&prd.val[2], &rd_ip);
+ break;
+
+#ifdef ENABLE_BGP_VNC
+ case RD_TYPE_VNC_ETH:
+ break;
+#endif
+
+ default:
+ flog_err(EC_BGP_UPDATE_RCV, "Unknown RD type %d", type);
+ break; /* just report */
+ }
+
+ /* exclude label & RD */
+ p.prefixlen = prefixlen - VPN_PREFIXLEN_MIN_BYTES * 8;
+ STREAM_GET(p.u.val, data, psize - VPN_PREFIXLEN_MIN_BYTES);
+
+ if (attr) {
+ bgp_update(peer, &p, addpath_id, attr, packet->afi,
+ SAFI_MPLS_VPN, ZEBRA_ROUTE_BGP,
+ BGP_ROUTE_NORMAL, &prd, &label, 1, 0, NULL);
+ } else {
+ bgp_withdraw(peer, &p, addpath_id, attr, packet->afi,
+ SAFI_MPLS_VPN, ZEBRA_ROUTE_BGP,
+ BGP_ROUTE_NORMAL, &prd, &label, 1, NULL);
+ }
+ }
+ /* Packet length consistency check. */
+ if (STREAM_READABLE(data) != 0) {
+ flog_err(
+ EC_BGP_UPDATE_RCV,
+ "%s [Error] Update packet error / VPN (%zu data remaining after parsing)",
+ peer->host, STREAM_READABLE(data));
+ return BGP_NLRI_PARSE_ERROR_PACKET_LENGTH;
+ }
+
+ goto done;
+
+stream_failure:
+ flog_err(
+ EC_BGP_UPDATE_RCV,
+ "%s [Error] Update packet error / VPN (NLRI of size %u - length error)",
+ peer->host, packet->length);
+ ret = BGP_NLRI_PARSE_ERROR_PACKET_LENGTH;
+
+done:
+ stream_free(data);
+ return ret;
+
+#undef VPN_PREFIXLEN_MIN_BYTES
+}
+
+/*
+ * This function informs zebra of the label this vrf sets on routes
+ * leaked to VPN. Zebra should install this label in the kernel with
+ * an action of "pop label and then use this vrf's IP FIB to route the PDU."
+ *
+ * Sending this vrf-label association is qualified by a) whether vrf->vpn
+ * exporting is active ("export vpn" is enabled, vpn-policy RD and RT list
+ * are set) and b) whether vpn-policy label is set.
+ *
+ * If any of these conditions do not hold, then we send MPLS_LABEL_NONE
+ * for this vrf, which zebra interprets to mean "delete this vrf-label
+ * association."
+ */
+void vpn_leak_zebra_vrf_label_update(struct bgp *bgp, afi_t afi)
+{
+ mpls_label_t label = MPLS_LABEL_NONE;
+ int debug = BGP_DEBUG(vpn, VPN_LEAK_LABEL);
+
+ if (bgp->vrf_id == VRF_UNKNOWN) {
+ if (debug) {
+ zlog_debug(
+ "%s: vrf %s: afi %s: vrf_id not set, can't set zebra vrf label",
+ __func__, bgp->name_pretty, afi2str(afi));
+ }
+ return;
+ }
+
+ if (vpn_leak_to_vpn_active(bgp, afi, NULL)) {
+ label = bgp->vpn_policy[afi].tovpn_label;
+ }
+
+ if (debug) {
+ zlog_debug("%s: vrf %s: afi %s: setting label %d for vrf id %d",
+ __func__, bgp->name_pretty, afi2str(afi), label,
+ bgp->vrf_id);
+ }
+
+ if (label == BGP_PREVENT_VRF_2_VRF_LEAK)
+ label = MPLS_LABEL_NONE;
+ zclient_send_vrf_label(zclient, bgp->vrf_id, afi, label, ZEBRA_LSP_BGP);
+ bgp->vpn_policy[afi].tovpn_zebra_vrf_label_last_sent = label;
+}
+
+/*
+ * If zebra tells us vrf has become unconfigured, tell zebra not to
+ * use this label to forward to the vrf anymore
+ */
+void vpn_leak_zebra_vrf_label_withdraw(struct bgp *bgp, afi_t afi)
+{
+ mpls_label_t label = MPLS_LABEL_NONE;
+ int debug = BGP_DEBUG(vpn, VPN_LEAK_LABEL);
+
+ if (bgp->vrf_id == VRF_UNKNOWN) {
+ if (debug) {
+ zlog_debug(
+ "%s: vrf_id not set, can't delete zebra vrf label",
+ __func__);
+ }
+ return;
+ }
+
+ if (debug) {
+ zlog_debug("%s: deleting label for vrf %s (id=%d)", __func__,
+ bgp->name_pretty, bgp->vrf_id);
+ }
+
+ zclient_send_vrf_label(zclient, bgp->vrf_id, afi, label, ZEBRA_LSP_BGP);
+ bgp->vpn_policy[afi].tovpn_zebra_vrf_label_last_sent = label;
+}
+
+/*
+ * This function informs zebra of the srv6-function this vrf sets on routes
+ * leaked to VPN. Zebra should install this srv6-function in the kernel with
+ * an action of "End.DT4/6's IP FIB to route the PDU."
+ */
+void vpn_leak_zebra_vrf_sid_update(struct bgp *bgp, afi_t afi)
+{
+ int debug = BGP_DEBUG(vpn, VPN_LEAK_LABEL);
+ enum seg6local_action_t act;
+ struct seg6local_context ctx = {};
+ struct in6_addr *tovpn_sid = NULL;
+ struct in6_addr *tovpn_sid_ls = NULL;
+ struct vrf *vrf;
+ char buf[256] = {0};
+
+ if (bgp->vrf_id == VRF_UNKNOWN) {
+ if (debug)
+ zlog_debug("%s: vrf %s: afi %s: vrf_id not set, can't set zebra vrf label",
+ __func__, bgp->name_pretty, afi2str(afi));
+ return;
+ }
+
+ tovpn_sid = bgp->vpn_policy[afi].tovpn_sid;
+ if (!tovpn_sid) {
+ if (debug)
+ zlog_debug("%s: vrf %s: afi %s: sid not set", __func__,
+ bgp->name_pretty, afi2str(afi));
+ return;
+ }
+
+ if (debug) {
+ inet_ntop(AF_INET6, tovpn_sid, buf, sizeof(buf));
+ zlog_debug("%s: vrf %s: afi %s: setting sid %s for vrf id %d",
+ __func__, bgp->name_pretty, afi2str(afi), buf,
+ bgp->vrf_id);
+ }
+
+ vrf = vrf_lookup_by_id(bgp->vrf_id);
+ if (!vrf)
+ return;
+
+ ctx.table = vrf->data.l.table_id;
+ act = afi == AFI_IP ? ZEBRA_SEG6_LOCAL_ACTION_END_DT4
+ : ZEBRA_SEG6_LOCAL_ACTION_END_DT6;
+ zclient_send_localsid(zclient, tovpn_sid, bgp->vrf_id, act, &ctx);
+
+ tovpn_sid_ls = XCALLOC(MTYPE_BGP_SRV6_SID, sizeof(struct in6_addr));
+ *tovpn_sid_ls = *tovpn_sid;
+ bgp->vpn_policy[afi].tovpn_zebra_vrf_sid_last_sent = tovpn_sid_ls;
+}
+
+/*
+ * If zebra tells us vrf has become unconfigured, tell zebra not to
+ * use this srv6-function to forward to the vrf anymore
+ */
+void vpn_leak_zebra_vrf_sid_withdraw(struct bgp *bgp, afi_t afi)
+{
+ int debug = BGP_DEBUG(vpn, VPN_LEAK_LABEL);
+
+ if (bgp->vrf_id == VRF_UNKNOWN) {
+ if (debug)
+ zlog_debug("%s: vrf %s: afi %s: vrf_id not set, can't set zebra vrf label",
+ __func__, bgp->name_pretty, afi2str(afi));
+ return;
+ }
+
+ if (debug)
+ zlog_debug("%s: deleting sid for vrf %s afi (id=%d)", __func__,
+ bgp->name_pretty, bgp->vrf_id);
+
+ zclient_send_localsid(zclient,
+ bgp->vpn_policy[afi].tovpn_zebra_vrf_sid_last_sent,
+ bgp->vrf_id, ZEBRA_SEG6_LOCAL_ACTION_UNSPEC, NULL);
+ XFREE(MTYPE_BGP_SRV6_SID,
+ bgp->vpn_policy[afi].tovpn_zebra_vrf_sid_last_sent);
+}
+
+int vpn_leak_label_callback(
+ mpls_label_t label,
+ void *labelid,
+ bool allocated)
+{
+ struct vpn_policy *vp = (struct vpn_policy *)labelid;
+ int debug = BGP_DEBUG(vpn, VPN_LEAK_LABEL);
+
+ if (debug)
+ zlog_debug("%s: label=%u, allocated=%d",
+ __func__, label, allocated);
+
+ if (!allocated) {
+ /*
+ * previously-allocated label is now invalid
+ */
+ if (CHECK_FLAG(vp->flags, BGP_VPN_POLICY_TOVPN_LABEL_AUTO) &&
+ (vp->tovpn_label != MPLS_LABEL_NONE)) {
+
+ vpn_leak_prechange(BGP_VPN_POLICY_DIR_TOVPN,
+ vp->afi, bgp_get_default(), vp->bgp);
+ vp->tovpn_label = MPLS_LABEL_NONE;
+ vpn_leak_postchange(BGP_VPN_POLICY_DIR_TOVPN,
+ vp->afi, bgp_get_default(), vp->bgp);
+ }
+ return 0;
+ }
+
+ /*
+ * New label allocation
+ */
+ if (!CHECK_FLAG(vp->flags, BGP_VPN_POLICY_TOVPN_LABEL_AUTO)) {
+
+ /*
+ * not currently configured for auto label, reject allocation
+ */
+ return -1;
+ }
+
+ if (vp->tovpn_label != MPLS_LABEL_NONE) {
+ if (label == vp->tovpn_label) {
+ /* already have same label, accept but do nothing */
+ return 0;
+ }
+ /* Shouldn't happen: different label allocation */
+ flog_err(EC_BGP_LABEL,
+ "%s: %s had label %u but got new assignment %u",
+ __func__, vp->bgp->name_pretty, vp->tovpn_label,
+ label);
+ /* use new one */
+ }
+
+ vpn_leak_prechange(BGP_VPN_POLICY_DIR_TOVPN,
+ vp->afi, bgp_get_default(), vp->bgp);
+ vp->tovpn_label = label;
+ vpn_leak_postchange(BGP_VPN_POLICY_DIR_TOVPN,
+ vp->afi, bgp_get_default(), vp->bgp);
+
+ return 0;
+}
+
+static void sid_register(struct bgp *bgp, const struct in6_addr *sid,
+ const char *locator_name)
+{
+ struct bgp_srv6_function *func;
+ func = XCALLOC(MTYPE_BGP_SRV6_FUNCTION,
+ sizeof(struct bgp_srv6_function));
+ func->sid = *sid;
+ snprintf(func->locator_name, sizeof(func->locator_name),
+ "%s", locator_name);
+ listnode_add(bgp->srv6_functions, func);
+}
+
+static bool sid_exist(struct bgp *bgp, const struct in6_addr *sid)
+{
+ struct listnode *node;
+ struct bgp_srv6_function *func;
+
+ for (ALL_LIST_ELEMENTS_RO(bgp->srv6_functions, node, func))
+ if (sid_same(&func->sid, sid))
+ return true;
+ return false;
+}
+
+/*
+ * This function generates a new SID based on bgp->srv6_locator_chunks and
+ * index. The locator and generated SID are stored in arguments sid_locator
+ * and sid, respectively.
+ *
+ * if index != 0: try to allocate as index-mode
+ * else: try to allocate as auto-mode
+ */
+static uint32_t alloc_new_sid(struct bgp *bgp, uint32_t index,
+ struct in6_addr *sid_locator,
+ struct in6_addr *sid)
+{
+ struct listnode *node;
+ struct srv6_locator_chunk *chunk;
+ bool alloced = false;
+ int label = 0;
+ uint8_t offset = 0;
+ uint8_t len = 0;
+
+ if (!bgp || !sid_locator || !sid)
+ return false;
+
+ for (ALL_LIST_ELEMENTS_RO(bgp->srv6_locator_chunks, node, chunk)) {
+ *sid_locator = chunk->prefix.prefix;
+ *sid = chunk->prefix.prefix;
+ offset = chunk->block_bits_length + chunk->node_bits_length;
+ len = chunk->function_bits_length ?: 16;
+
+ if (index != 0) {
+ label = index << 12;
+ transpose_sid(sid, label, offset, len);
+ if (sid_exist(bgp, sid))
+ return false;
+ alloced = true;
+ break;
+ }
+
+ for (size_t i = 1; i < 255; i++) {
+ label = i << 12;
+ transpose_sid(sid, label, offset, len);
+ if (sid_exist(bgp, sid))
+ continue;
+ alloced = true;
+ break;
+ }
+ }
+
+ if (!alloced)
+ return 0;
+
+ sid_register(bgp, sid, bgp->srv6_locator_name);
+ return label;
+}
+
+void ensure_vrf_tovpn_sid(struct bgp *bgp_vpn, struct bgp *bgp_vrf, afi_t afi)
+{
+ int debug = BGP_DEBUG(vpn, VPN_LEAK_FROM_VRF);
+ char buf[256];
+ struct in6_addr *tovpn_sid, *tovpn_sid_locator;
+ uint32_t tovpn_sid_index = 0, tovpn_sid_transpose_label;
+ bool tovpn_sid_auto = false;
+
+ if (debug)
+ zlog_debug("%s: try to allocate new SID for vrf %s: afi %s",
+ __func__, bgp_vrf->name_pretty, afi2str(afi));
+
+ /* skip when tovpn sid is already allocated on vrf instance */
+ if (bgp_vrf->vpn_policy[afi].tovpn_sid)
+ return;
+
+ /*
+ * skip when bgp vpn instance ins't allocated
+ * or srv6 locator chunk isn't allocated
+ */
+ if (!bgp_vpn || !bgp_vpn->srv6_locator_chunks)
+ return;
+
+ tovpn_sid_index = bgp_vrf->vpn_policy[afi].tovpn_sid_index;
+ tovpn_sid_auto = CHECK_FLAG(bgp_vrf->vpn_policy[afi].flags,
+ BGP_VPN_POLICY_TOVPN_SID_AUTO);
+
+ /* skip when VPN isn't configured on vrf-instance */
+ if (tovpn_sid_index == 0 && !tovpn_sid_auto)
+ return;
+
+ /* check invalid case both configured index and auto */
+ if (tovpn_sid_index != 0 && tovpn_sid_auto) {
+ zlog_err("%s: index-mode and auto-mode both selected. ignored.",
+ __func__);
+ return;
+ }
+
+ tovpn_sid_locator =
+ XCALLOC(MTYPE_BGP_SRV6_SID, sizeof(struct in6_addr));
+ tovpn_sid = XCALLOC(MTYPE_BGP_SRV6_SID, sizeof(struct in6_addr));
+
+ tovpn_sid_transpose_label = alloc_new_sid(bgp_vpn, tovpn_sid_index,
+ tovpn_sid_locator, tovpn_sid);
+
+ if (tovpn_sid_transpose_label == 0) {
+ zlog_debug("%s: not allocated new sid for vrf %s: afi %s",
+ __func__, bgp_vrf->name_pretty, afi2str(afi));
+ XFREE(MTYPE_BGP_SRV6_SID, tovpn_sid_locator);
+ XFREE(MTYPE_BGP_SRV6_SID, tovpn_sid);
+ return;
+ }
+
+ if (debug) {
+ inet_ntop(AF_INET6, tovpn_sid, buf, sizeof(buf));
+ zlog_debug("%s: new sid %s allocated for vrf %s: afi %s",
+ __func__, buf, bgp_vrf->name_pretty,
+ afi2str(afi));
+ }
+
+ bgp_vrf->vpn_policy[afi].tovpn_sid = tovpn_sid;
+ bgp_vrf->vpn_policy[afi].tovpn_sid_locator = tovpn_sid_locator;
+ bgp_vrf->vpn_policy[afi].tovpn_sid_transpose_label =
+ tovpn_sid_transpose_label;
+}
+
+/*
+ * This function shifts "label" 4 bits to the right and
+ * embeds it by length "len", starting at offset "offset"
+ * as seen from the MSB (Most Significant Bit) of "sid".
+ *
+ * e.g. if "label" is 0x1000 and "len" is 16, "label" is
+ * embedded in "sid" as follows:
+ *
+ * <---- len ----->
+ * label: 0000 0001 0000 0000 0000
+ * sid: .... 0000 0001 0000 0000
+ * <---- len ----->
+ * ^
+ * |
+ * offset from MSB
+ */
+void transpose_sid(struct in6_addr *sid, uint32_t label, uint8_t offset,
+ uint8_t len)
+{
+ for (uint8_t idx = 0; idx < len; idx++) {
+ uint8_t tidx = offset + idx;
+ sid->s6_addr[tidx / 8] &= ~(0x1 << (7 - tidx % 8));
+ if (label >> (len + 3 - idx) & 0x1)
+ sid->s6_addr[tidx / 8] |= 0x1 << (7 - tidx % 8);
+ }
+}
+
+static bool labels_same(struct bgp_path_info *bpi, mpls_label_t *label,
+ uint32_t n)
+{
+ uint32_t i;
+
+ if (!bpi->extra) {
+ if (!n)
+ return true;
+ else
+ return false;
+ }
+
+ if (n != bpi->extra->num_labels)
+ return false;
+
+ for (i = 0; i < n; ++i) {
+ if (label[i] != bpi->extra->label[i])
+ return false;
+ }
+ return true;
+}
+
+/*
+ * make encoded route labels match specified encoded label set
+ */
+static void setlabels(struct bgp_path_info *bpi,
+ mpls_label_t *label, /* array of labels */
+ uint32_t num_labels)
+{
+ if (num_labels)
+ assert(label);
+ assert(num_labels <= BGP_MAX_LABELS);
+
+ if (!num_labels) {
+ if (bpi->extra)
+ bpi->extra->num_labels = 0;
+ return;
+ }
+
+ struct bgp_path_info_extra *extra = bgp_path_info_extra_get(bpi);
+ uint32_t i;
+
+ for (i = 0; i < num_labels; ++i) {
+ extra->label[i] = label[i];
+ if (!bgp_is_valid_label(&label[i])) {
+ bgp_set_valid_label(&extra->label[i]);
+ }
+ }
+ extra->num_labels = num_labels;
+}
+
+/*
+ * make encoded route SIDs match specified encoded sid set
+ */
+static void setsids(struct bgp_path_info *bpi,
+ struct in6_addr *sid,
+ uint32_t num_sids)
+{
+ uint32_t i;
+ struct bgp_path_info_extra *extra;
+
+ if (num_sids)
+ assert(sid);
+ assert(num_sids <= BGP_MAX_SIDS);
+
+ if (!num_sids) {
+ if (bpi->extra)
+ bpi->extra->num_sids = 0;
+ return;
+ }
+
+ extra = bgp_path_info_extra_get(bpi);
+ for (i = 0; i < num_sids; i++)
+ memcpy(&extra->sid[i].sid, &sid[i], sizeof(struct in6_addr));
+ extra->num_sids = num_sids;
+}
+
+static void unsetsids(struct bgp_path_info *bpi)
+{
+ struct bgp_path_info_extra *extra;
+
+ extra = bgp_path_info_extra_get(bpi);
+ extra->num_sids = 0;
+ memset(extra->sid, 0, sizeof(extra->sid));
+}
+
+static bool leak_update_nexthop_valid(struct bgp *to_bgp, struct bgp_dest *bn,
+ struct attr *new_attr, afi_t afi,
+ safi_t safi,
+ struct bgp_path_info *source_bpi,
+ struct bgp_path_info *bpi,
+ struct bgp *bgp_orig,
+ const struct prefix *p, int debug)
+{
+ struct bgp_path_info *bpi_ultimate;
+ struct bgp *bgp_nexthop;
+ bool nh_valid;
+
+ bpi_ultimate = bgp_get_imported_bpi_ultimate(source_bpi);
+
+ if (bpi->extra && bpi->extra->bgp_orig)
+ bgp_nexthop = bpi->extra->bgp_orig;
+ else
+ bgp_nexthop = bgp_orig;
+
+ /*
+ * No nexthop tracking for redistributed routes or for
+ * EVPN-imported routes that get leaked.
+ */
+ if (bpi_ultimate->sub_type == BGP_ROUTE_REDISTRIBUTE ||
+ is_pi_family_evpn(bpi_ultimate))
+ nh_valid = 1;
+ else
+ /*
+ * TBD do we need to do anything about the
+ * 'connected' parameter?
+ */
+ nh_valid = bgp_find_or_add_nexthop(to_bgp, bgp_nexthop, afi,
+ safi, bpi, NULL, 0, p);
+
+ /*
+ * If you are using SRv6 VPN instead of MPLS, it need to check
+ * the SID allocation. If the sid is not allocated, the rib
+ * will be invalid.
+ */
+ if (to_bgp->srv6_enabled &&
+ (!new_attr->srv6_l3vpn && !new_attr->srv6_vpn)) {
+ nh_valid = false;
+ }
+
+ if (debug)
+ zlog_debug("%s: %pFX nexthop is %svalid (in vrf %s)", __func__,
+ p, (nh_valid ? "" : "not "),
+ bgp_nexthop->name_pretty);
+
+ return nh_valid;
+}
+
+/*
+ * returns pointer to new bgp_path_info upon success
+ */
+static struct bgp_path_info *
+leak_update(struct bgp *to_bgp, struct bgp_dest *bn,
+ struct attr *new_attr, /* already interned */
+ afi_t afi, safi_t safi, struct bgp_path_info *source_bpi,
+ mpls_label_t *label, uint32_t num_labels, struct bgp *bgp_orig,
+ struct prefix *nexthop_orig, int nexthop_self_flag, int debug)
+{
+ const struct prefix *p = bgp_dest_get_prefix(bn);
+ struct bgp_path_info *bpi;
+ struct bgp_path_info *new;
+ struct bgp_path_info_extra *extra;
+ uint32_t num_sids = 0;
+ struct bgp_path_info *parent = source_bpi;
+
+ if (new_attr->srv6_l3vpn || new_attr->srv6_vpn)
+ num_sids = 1;
+
+ if (debug)
+ zlog_debug(
+ "%s: entry: leak-to=%s, p=%pBD, type=%d, sub_type=%d",
+ __func__, to_bgp->name_pretty, bn, source_bpi->type,
+ source_bpi->sub_type);
+
+ /*
+ * Routes that are redistributed into BGP from zebra do not get
+ * nexthop tracking. However, if those routes are subsequently
+ * imported to other RIBs within BGP, the leaked routes do not
+ * carry the original BGP_ROUTE_REDISTRIBUTE sub_type. Therefore,
+ * in order to determine if the route we are currently leaking
+ * should have nexthop tracking, we must find the ultimate
+ * parent so we can check its sub_type.
+ *
+ * As of now, source_bpi may at most be a second-generation route
+ * (only one hop back to ultimate parent for vrf-vpn-vrf scheme).
+ * Using a loop here supports more complex intra-bgp import-export
+ * schemes that could be implemented in the future.
+ *
+ */
+
+ /*
+ * match parent
+ */
+ for (bpi = bgp_dest_get_bgp_path_info(bn); bpi; bpi = bpi->next) {
+ if (bpi->extra && bpi->extra->parent == parent)
+ break;
+ }
+
+ if (bpi) {
+ bool labelssame = labels_same(bpi, label, num_labels);
+
+ if (CHECK_FLAG(source_bpi->flags, BGP_PATH_REMOVED)
+ && CHECK_FLAG(bpi->flags, BGP_PATH_REMOVED)) {
+ if (debug) {
+ zlog_debug(
+ "%s: ->%s(s_flags: 0x%x b_flags: 0x%x): %pFX: Found route, being removed, not leaking",
+ __func__, to_bgp->name_pretty,
+ source_bpi->flags, bpi->flags, p);
+ }
+ return NULL;
+ }
+
+ if (attrhash_cmp(bpi->attr, new_attr) && labelssame
+ && !CHECK_FLAG(bpi->flags, BGP_PATH_REMOVED)) {
+
+ bgp_attr_unintern(&new_attr);
+ if (debug)
+ zlog_debug(
+ "%s: ->%s: %pBD: Found route, no change",
+ __func__, to_bgp->name_pretty, bn);
+ return NULL;
+ }
+
+ /* If the RT was changed via extended communities as an
+ * import/export list, we should withdraw implicitly the old
+ * path from VRFs.
+ * For instance, RT list was modified using route-maps:
+ * route-map test permit 10
+ * set extcommunity rt none
+ */
+ if (CHECK_FLAG(bpi->attr->flag,
+ ATTR_FLAG_BIT(BGP_ATTR_EXT_COMMUNITIES)) &&
+ CHECK_FLAG(new_attr->flag,
+ ATTR_FLAG_BIT(BGP_ATTR_EXT_COMMUNITIES))) {
+ if (!ecommunity_cmp(
+ bgp_attr_get_ecommunity(bpi->attr),
+ bgp_attr_get_ecommunity(new_attr))) {
+ vpn_leak_to_vrf_withdraw(to_bgp, bpi);
+ bgp_aggregate_decrement(to_bgp, p, bpi, afi,
+ safi);
+ bgp_path_info_delete(bn, bpi);
+ }
+ }
+
+ /* attr 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(to_bgp, p, bpi, afi, safi);
+ bgp_attr_unintern(&bpi->attr);
+ bpi->attr = new_attr;
+ bpi->uptime = monotime(NULL);
+
+ /*
+ * rewrite labels
+ */
+ if (!labelssame)
+ setlabels(bpi, label, num_labels);
+
+ /*
+ * rewrite sid
+ */
+ if (num_sids) {
+ if (new_attr->srv6_l3vpn) {
+ setsids(bpi, &new_attr->srv6_l3vpn->sid,
+ num_sids);
+
+ extra = bgp_path_info_extra_get(bpi);
+
+ extra->sid[0].loc_block_len =
+ new_attr->srv6_l3vpn->loc_block_len;
+ extra->sid[0].loc_node_len =
+ new_attr->srv6_l3vpn->loc_node_len;
+ extra->sid[0].func_len =
+ new_attr->srv6_l3vpn->func_len;
+ extra->sid[0].arg_len =
+ new_attr->srv6_l3vpn->arg_len;
+ extra->sid[0].transposition_len =
+ new_attr->srv6_l3vpn->transposition_len;
+ extra->sid[0].transposition_offset =
+ new_attr->srv6_l3vpn
+ ->transposition_offset;
+ } else if (new_attr->srv6_vpn)
+ setsids(bpi, &new_attr->srv6_vpn->sid,
+ num_sids);
+ } else
+ unsetsids(bpi);
+
+ if (nexthop_self_flag)
+ bgp_path_info_set_flag(bn, bpi, BGP_PATH_ANNC_NH_SELF);
+
+ if (leak_update_nexthop_valid(to_bgp, bn, new_attr, afi, safi,
+ source_bpi, bpi, bgp_orig, p,
+ debug))
+ bgp_path_info_set_flag(bn, bpi, BGP_PATH_VALID);
+ else
+ bgp_path_info_unset_flag(bn, bpi, BGP_PATH_VALID);
+
+ /* Process change. */
+ bgp_aggregate_increment(to_bgp, p, bpi, afi, safi);
+ bgp_process(to_bgp, bn, afi, safi);
+ bgp_dest_unlock_node(bn);
+
+ if (debug)
+ zlog_debug("%s: ->%s: %pBD Found route, changed attr",
+ __func__, to_bgp->name_pretty, bn);
+
+ return bpi;
+ }
+
+ if (CHECK_FLAG(source_bpi->flags, BGP_PATH_REMOVED)) {
+ if (debug) {
+ zlog_debug(
+ "%s: ->%s(s_flags: 0x%x): %pFX: New route, being removed, not leaking",
+ __func__, to_bgp->name_pretty,
+ source_bpi->flags, p);
+ }
+ return NULL;
+ }
+
+ new = info_make(ZEBRA_ROUTE_BGP, BGP_ROUTE_IMPORTED, 0,
+ to_bgp->peer_self, new_attr, bn);
+
+ if (source_bpi->peer) {
+ extra = bgp_path_info_extra_get(new);
+ extra->peer_orig = peer_lock(source_bpi->peer);
+ }
+
+ if (nexthop_self_flag)
+ bgp_path_info_set_flag(bn, new, BGP_PATH_ANNC_NH_SELF);
+
+ bgp_path_info_extra_get(new);
+
+ /*
+ * rewrite sid
+ */
+ if (num_sids) {
+ if (new_attr->srv6_l3vpn) {
+ setsids(new, &new_attr->srv6_l3vpn->sid, num_sids);
+
+ extra = bgp_path_info_extra_get(new);
+
+ extra->sid[0].loc_block_len =
+ new_attr->srv6_l3vpn->loc_block_len;
+ extra->sid[0].loc_node_len =
+ new_attr->srv6_l3vpn->loc_node_len;
+ extra->sid[0].func_len = new_attr->srv6_l3vpn->func_len;
+ extra->sid[0].arg_len = new_attr->srv6_l3vpn->arg_len;
+ extra->sid[0].transposition_len =
+ new_attr->srv6_l3vpn->transposition_len;
+ extra->sid[0].transposition_offset =
+ new_attr->srv6_l3vpn->transposition_offset;
+ } else if (new_attr->srv6_vpn)
+ setsids(new, &new_attr->srv6_vpn->sid, num_sids);
+ } else
+ unsetsids(new);
+
+ if (num_labels)
+ setlabels(new, label, num_labels);
+
+ new->extra->parent = bgp_path_info_lock(parent);
+ bgp_dest_lock_node(
+ (struct bgp_dest *)parent->net);
+ if (bgp_orig)
+ new->extra->bgp_orig = bgp_lock(bgp_orig);
+ if (nexthop_orig)
+ new->extra->nexthop_orig = *nexthop_orig;
+
+ if (leak_update_nexthop_valid(to_bgp, bn, new_attr, afi, safi,
+ source_bpi, new, bgp_orig, p, debug))
+ bgp_path_info_set_flag(bn, new, BGP_PATH_VALID);
+ else
+ bgp_path_info_unset_flag(bn, new, BGP_PATH_VALID);
+
+ bgp_aggregate_increment(to_bgp, p, new, afi, safi);
+ bgp_path_info_add(bn, new);
+
+ bgp_dest_unlock_node(bn);
+ bgp_process(to_bgp, bn, afi, safi);
+
+ if (debug)
+ zlog_debug("%s: ->%s: %pBD: Added new route", __func__,
+ to_bgp->name_pretty, bn);
+
+ return new;
+}
+
+/* cf vnc_import_bgp_add_route_mode_nvegroup() and add_vnc_route() */
+void vpn_leak_from_vrf_update(struct bgp *to_bgp, /* to */
+ struct bgp *from_bgp, /* from */
+ struct bgp_path_info *path_vrf) /* route */
+{
+ int debug = BGP_DEBUG(vpn, VPN_LEAK_FROM_VRF);
+ const struct prefix *p = bgp_dest_get_prefix(path_vrf->net);
+ afi_t afi = family2afi(p->family);
+ struct attr static_attr = {0};
+ struct attr *new_attr = NULL;
+ safi_t safi = SAFI_MPLS_VPN;
+ mpls_label_t label_val;
+ mpls_label_t label;
+ struct bgp_dest *bn;
+ const char *debugmsg;
+ int nexthop_self_flag = 0;
+
+ if (debug)
+ zlog_debug("%s: from vrf %s", __func__, from_bgp->name_pretty);
+
+ if (debug && bgp_attr_get_ecommunity(path_vrf->attr)) {
+ char *s = ecommunity_ecom2str(
+ bgp_attr_get_ecommunity(path_vrf->attr),
+ ECOMMUNITY_FORMAT_ROUTE_MAP, 0);
+
+ zlog_debug("%s: %s path_vrf->type=%d, EC{%s}", __func__,
+ from_bgp->name, path_vrf->type, s);
+ XFREE(MTYPE_ECOMMUNITY_STR, s);
+ }
+
+ if (!to_bgp)
+ return;
+
+ if (!afi) {
+ if (debug)
+ zlog_debug("%s: can't get afi of prefix", __func__);
+ return;
+ }
+
+ /* Is this route exportable into the VPN table? */
+ if (!is_route_injectable_into_vpn(path_vrf))
+ return;
+
+ if (!vpn_leak_to_vpn_active(from_bgp, afi, &debugmsg)) {
+ if (debug)
+ zlog_debug("%s: %s skipping: %s", __func__,
+ from_bgp->name, debugmsg);
+ return;
+ }
+
+ /* shallow copy */
+ static_attr = *path_vrf->attr;
+
+ /*
+ * route map handling
+ */
+ if (from_bgp->vpn_policy[afi].rmap[BGP_VPN_POLICY_DIR_TOVPN]) {
+ struct bgp_path_info info;
+ route_map_result_t ret;
+
+ memset(&info, 0, sizeof(info));
+ info.peer = to_bgp->peer_self;
+ info.attr = &static_attr;
+ ret = route_map_apply(from_bgp->vpn_policy[afi]
+ .rmap[BGP_VPN_POLICY_DIR_TOVPN],
+ p, &info);
+ if (RMAP_DENYMATCH == ret) {
+ bgp_attr_flush(&static_attr); /* free any added parts */
+ if (debug)
+ zlog_debug(
+ "%s: vrf %s route map \"%s\" says DENY, returning",
+ __func__, from_bgp->name_pretty,
+ from_bgp->vpn_policy[afi]
+ .rmap[BGP_VPN_POLICY_DIR_TOVPN]
+ ->name);
+ return;
+ }
+ }
+
+ if (debug && bgp_attr_get_ecommunity(&static_attr)) {
+ char *s = ecommunity_ecom2str(
+ bgp_attr_get_ecommunity(&static_attr),
+ ECOMMUNITY_FORMAT_ROUTE_MAP, 0);
+
+ zlog_debug("%s: post route map static_attr.ecommunity{%s}",
+ __func__, s);
+ XFREE(MTYPE_ECOMMUNITY_STR, s);
+ }
+
+ /*
+ * Add the vpn-policy rt-list
+ */
+ struct ecommunity *old_ecom;
+ struct ecommunity *new_ecom;
+
+ /* Export with the 'from' instance's export RTs. */
+ /* If doing VRF-to-VRF leaking, strip existing RTs first. */
+ old_ecom = bgp_attr_get_ecommunity(&static_attr);
+ if (old_ecom) {
+ new_ecom = ecommunity_dup(old_ecom);
+ if (CHECK_FLAG(from_bgp->af_flags[afi][SAFI_UNICAST],
+ BGP_CONFIG_VRF_TO_VRF_EXPORT))
+ ecommunity_strip_rts(new_ecom);
+ new_ecom = ecommunity_merge(
+ new_ecom, from_bgp->vpn_policy[afi]
+ .rtlist[BGP_VPN_POLICY_DIR_TOVPN]);
+ if (!old_ecom->refcnt)
+ ecommunity_free(&old_ecom);
+ } else {
+ new_ecom = ecommunity_dup(
+ from_bgp->vpn_policy[afi]
+ .rtlist[BGP_VPN_POLICY_DIR_TOVPN]);
+ }
+ bgp_attr_set_ecommunity(&static_attr, new_ecom);
+
+ if (debug && bgp_attr_get_ecommunity(&static_attr)) {
+ char *s = ecommunity_ecom2str(
+ bgp_attr_get_ecommunity(&static_attr),
+ ECOMMUNITY_FORMAT_ROUTE_MAP, 0);
+
+ zlog_debug("%s: post merge static_attr.ecommunity{%s}",
+ __func__, s);
+ XFREE(MTYPE_ECOMMUNITY_STR, s);
+ }
+
+ /* Nexthop */
+ /* if policy nexthop not set, use 0 */
+ if (CHECK_FLAG(from_bgp->vpn_policy[afi].flags,
+ BGP_VPN_POLICY_TOVPN_NEXTHOP_SET)) {
+ struct prefix *nexthop =
+ &from_bgp->vpn_policy[afi].tovpn_nexthop;
+
+ switch (nexthop->family) {
+ case AF_INET:
+ /* prevent mp_nexthop_global_in <- self in bgp_route.c
+ */
+ static_attr.nexthop.s_addr = nexthop->u.prefix4.s_addr;
+
+ static_attr.mp_nexthop_global_in = nexthop->u.prefix4;
+ static_attr.mp_nexthop_len = BGP_ATTR_NHLEN_IPV4;
+ break;
+
+ case AF_INET6:
+ static_attr.mp_nexthop_global = nexthop->u.prefix6;
+ static_attr.mp_nexthop_len = BGP_ATTR_NHLEN_IPV6_GLOBAL;
+ break;
+
+ default:
+ assert(0);
+ }
+ } else {
+ if (!CHECK_FLAG(from_bgp->af_flags[afi][SAFI_UNICAST],
+ BGP_CONFIG_VRF_TO_VRF_EXPORT)) {
+ if (afi == AFI_IP &&
+ !BGP_ATTR_NEXTHOP_AFI_IP6(path_vrf->attr)) {
+ /*
+ * For ipv4, copy to multiprotocol
+ * nexthop field
+ */
+ static_attr.mp_nexthop_global_in =
+ static_attr.nexthop;
+ static_attr.mp_nexthop_len =
+ BGP_ATTR_NHLEN_IPV4;
+ /*
+ * XXX Leave static_attr.nexthop
+ * intact for NHT
+ */
+ static_attr.flag &=
+ ~ATTR_FLAG_BIT(BGP_ATTR_NEXT_HOP);
+ }
+ } else {
+ /* Update based on next-hop family to account for
+ * RFC 5549 (BGP unnumbered) scenario. Note that
+ * specific action is only needed for the case of
+ * IPv4 nexthops as the attr has been copied
+ * otherwise.
+ */
+ if (afi == AFI_IP
+ && !BGP_ATTR_NEXTHOP_AFI_IP6(path_vrf->attr)) {
+ static_attr.mp_nexthop_global_in.s_addr =
+ static_attr.nexthop.s_addr;
+ static_attr.mp_nexthop_len =
+ BGP_ATTR_NHLEN_IPV4;
+ static_attr.flag |=
+ ATTR_FLAG_BIT(BGP_ATTR_NEXT_HOP);
+ }
+ }
+ nexthop_self_flag = 1;
+ }
+
+ label_val = from_bgp->vpn_policy[afi].tovpn_label;
+ if (label_val == MPLS_LABEL_NONE) {
+ encode_label(MPLS_LABEL_IMPLICIT_NULL, &label);
+ } else {
+ encode_label(label_val, &label);
+ }
+
+ /* Set originator ID to "me" */
+ SET_FLAG(static_attr.flag, ATTR_FLAG_BIT(BGP_ATTR_ORIGINATOR_ID));
+ static_attr.originator_id = to_bgp->router_id;
+
+ /* Set SID for SRv6 VPN */
+ if (from_bgp->vpn_policy[afi].tovpn_sid_locator) {
+ encode_label(
+ from_bgp->vpn_policy[afi].tovpn_sid_transpose_label,
+ &label);
+ static_attr.srv6_l3vpn = XCALLOC(MTYPE_BGP_SRV6_L3VPN,
+ sizeof(struct bgp_attr_srv6_l3vpn));
+ static_attr.srv6_l3vpn->sid_flags = 0x00;
+ static_attr.srv6_l3vpn->endpoint_behavior = 0xffff;
+ static_attr.srv6_l3vpn->loc_block_len =
+ BGP_PREFIX_SID_SRV6_LOCATOR_BLOCK_LENGTH;
+ static_attr.srv6_l3vpn->loc_node_len =
+ BGP_PREFIX_SID_SRV6_LOCATOR_NODE_LENGTH;
+ static_attr.srv6_l3vpn->func_len =
+ BGP_PREFIX_SID_SRV6_FUNCTION_LENGTH;
+ static_attr.srv6_l3vpn->arg_len =
+ BGP_PREFIX_SID_SRV6_ARGUMENT_LENGTH;
+ static_attr.srv6_l3vpn->transposition_len =
+ BGP_PREFIX_SID_SRV6_TRANSPOSITION_LENGTH;
+ static_attr.srv6_l3vpn->transposition_offset =
+ BGP_PREFIX_SID_SRV6_TRANSPOSITION_OFFSET;
+ memcpy(&static_attr.srv6_l3vpn->sid,
+ from_bgp->vpn_policy[afi].tovpn_sid_locator,
+ sizeof(struct in6_addr));
+ }
+
+
+ new_attr = bgp_attr_intern(
+ &static_attr); /* hashed refcounted everything */
+ bgp_attr_flush(&static_attr); /* free locally-allocated parts */
+
+ if (debug && bgp_attr_get_ecommunity(new_attr)) {
+ char *s = ecommunity_ecom2str(bgp_attr_get_ecommunity(new_attr),
+ ECOMMUNITY_FORMAT_ROUTE_MAP, 0);
+
+ zlog_debug("%s: new_attr->ecommunity{%s}", __func__, s);
+ XFREE(MTYPE_ECOMMUNITY_STR, s);
+ }
+
+ /* Now new_attr is an allocated interned attr */
+
+ bn = bgp_afi_node_get(to_bgp->rib[afi][safi], afi, safi, p,
+ &(from_bgp->vpn_policy[afi].tovpn_rd));
+
+ struct bgp_path_info *new_info;
+
+ new_info =
+ leak_update(to_bgp, bn, new_attr, afi, safi, path_vrf, &label,
+ 1, from_bgp, NULL, nexthop_self_flag, debug);
+
+ /*
+ * Routes actually installed in the vpn RIB must also be
+ * offered to all vrfs (because now they originate from
+ * the vpn RIB).
+ *
+ * Acceptance into other vrfs depends on rt-lists.
+ * Originating vrf will not accept the looped back route
+ * because of loop checking.
+ */
+ if (new_info)
+ vpn_leak_to_vrf_update(from_bgp, new_info);
+}
+
+void vpn_leak_from_vrf_withdraw(struct bgp *to_bgp, /* to */
+ struct bgp *from_bgp, /* from */
+ struct bgp_path_info *path_vrf) /* route */
+{
+ int debug = BGP_DEBUG(vpn, VPN_LEAK_FROM_VRF);
+ const struct prefix *p = bgp_dest_get_prefix(path_vrf->net);
+ afi_t afi = family2afi(p->family);
+ safi_t safi = SAFI_MPLS_VPN;
+ struct bgp_path_info *bpi;
+ struct bgp_dest *bn;
+ const char *debugmsg;
+
+ if (debug) {
+ zlog_debug(
+ "%s: entry: leak-from=%s, p=%pBD, type=%d, sub_type=%d",
+ __func__, from_bgp->name_pretty, path_vrf->net,
+ path_vrf->type, path_vrf->sub_type);
+ }
+
+ if (!to_bgp)
+ return;
+
+ if (!afi) {
+ if (debug)
+ zlog_debug("%s: can't get afi of prefix", __func__);
+ return;
+ }
+
+ /* Is this route exportable into the VPN table? */
+ if (!is_route_injectable_into_vpn(path_vrf))
+ return;
+
+ if (!vpn_leak_to_vpn_active(from_bgp, afi, &debugmsg)) {
+ if (debug)
+ zlog_debug("%s: skipping: %s", __func__, debugmsg);
+ return;
+ }
+
+ if (debug)
+ zlog_debug("%s: withdrawing (path_vrf=%p)", __func__, path_vrf);
+
+ bn = bgp_afi_node_get(to_bgp->rib[afi][safi], afi, safi, p,
+ &(from_bgp->vpn_policy[afi].tovpn_rd));
+
+ if (!bn)
+ return;
+ /*
+ * vrf -> vpn
+ * match original bpi imported from
+ */
+ for (bpi = bgp_dest_get_bgp_path_info(bn); bpi; bpi = bpi->next) {
+ if (bpi->extra && bpi->extra->parent == path_vrf) {
+ break;
+ }
+ }
+
+ if (bpi) {
+ /* withdraw from looped vrfs as well */
+ vpn_leak_to_vrf_withdraw(to_bgp, bpi);
+
+ bgp_aggregate_decrement(to_bgp, p, bpi, afi, safi);
+ bgp_path_info_delete(bn, bpi);
+ bgp_process(to_bgp, bn, afi, safi);
+ }
+ bgp_dest_unlock_node(bn);
+}
+
+void vpn_leak_from_vrf_withdraw_all(struct bgp *to_bgp, struct bgp *from_bgp,
+ afi_t afi)
+{
+ int debug = BGP_DEBUG(vpn, VPN_LEAK_FROM_VRF);
+ struct bgp_dest *pdest;
+ safi_t safi = SAFI_MPLS_VPN;
+
+ /*
+ * Walk vpn table, delete bpi with bgp_orig == from_bgp
+ */
+ for (pdest = bgp_table_top(to_bgp->rib[afi][safi]); pdest;
+ pdest = bgp_route_next(pdest)) {
+
+ struct bgp_table *table;
+ struct bgp_dest *bn;
+ struct bgp_path_info *bpi;
+
+ /* This is the per-RD table of prefixes */
+ table = bgp_dest_get_bgp_table_info(pdest);
+
+ if (!table)
+ continue;
+
+ for (bn = bgp_table_top(table); bn; bn = bgp_route_next(bn)) {
+ bpi = bgp_dest_get_bgp_path_info(bn);
+ if (debug && bpi) {
+ zlog_debug("%s: looking at prefix %pBD",
+ __func__, bn);
+ }
+
+ for (; bpi; bpi = bpi->next) {
+ if (debug)
+ zlog_debug("%s: type %d, sub_type %d",
+ __func__, bpi->type,
+ bpi->sub_type);
+ if (bpi->sub_type != BGP_ROUTE_IMPORTED)
+ continue;
+ if (!bpi->extra)
+ continue;
+ if ((struct bgp *)bpi->extra->bgp_orig ==
+ from_bgp) {
+ /* delete route */
+ if (debug)
+ zlog_debug("%s: deleting it",
+ __func__);
+ /* withdraw from leak-to vrfs as well */
+ vpn_leak_to_vrf_withdraw(to_bgp, bpi);
+ bgp_aggregate_decrement(
+ to_bgp, bgp_dest_get_prefix(bn),
+ bpi, afi, safi);
+ bgp_path_info_delete(bn, bpi);
+ bgp_process(to_bgp, bn, afi, safi);
+ }
+ }
+ }
+ }
+}
+
+void vpn_leak_from_vrf_update_all(struct bgp *to_bgp, struct bgp *from_bgp,
+ afi_t afi)
+{
+ struct bgp_dest *bn;
+ struct bgp_path_info *bpi;
+ int debug = BGP_DEBUG(vpn, VPN_LEAK_FROM_VRF);
+
+ if (debug)
+ zlog_debug("%s: entry, afi=%d, vrf=%s", __func__, afi,
+ from_bgp->name_pretty);
+
+ for (bn = bgp_table_top(from_bgp->rib[afi][SAFI_UNICAST]); bn;
+ bn = bgp_route_next(bn)) {
+
+ if (debug)
+ zlog_debug("%s: node=%p", __func__, bn);
+
+ for (bpi = bgp_dest_get_bgp_path_info(bn); bpi;
+ bpi = bpi->next) {
+ if (debug)
+ zlog_debug(
+ "%s: calling vpn_leak_from_vrf_update",
+ __func__);
+ vpn_leak_from_vrf_update(to_bgp, from_bgp, bpi);
+ }
+ }
+}
+
+static bool
+vpn_leak_to_vrf_update_onevrf(struct bgp *to_bgp, /* to */
+ struct bgp *from_bgp, /* from */
+ struct bgp_path_info *path_vpn) /* route */
+{
+ const struct prefix *p = bgp_dest_get_prefix(path_vpn->net);
+ afi_t afi = family2afi(p->family);
+
+ struct attr static_attr = {0};
+ struct attr *new_attr = NULL;
+ struct bgp_dest *bn;
+ safi_t safi = SAFI_UNICAST;
+ const char *debugmsg;
+ struct prefix nexthop_orig;
+ mpls_label_t *pLabels = NULL;
+ uint32_t num_labels = 0;
+ int nexthop_self_flag = 1;
+ struct bgp_path_info *bpi_ultimate = NULL;
+ int origin_local = 0;
+ struct bgp *src_vrf;
+
+ int debug = BGP_DEBUG(vpn, VPN_LEAK_TO_VRF);
+
+ if (!vpn_leak_from_vpn_active(to_bgp, afi, &debugmsg)) {
+ if (debug)
+ zlog_debug("%s: skipping: %s", __func__, debugmsg);
+ return false;
+ }
+
+ /* Check for intersection of route targets */
+ if (!ecommunity_include(
+ to_bgp->vpn_policy[afi].rtlist[BGP_VPN_POLICY_DIR_FROMVPN],
+ bgp_attr_get_ecommunity(path_vpn->attr))) {
+ if (debug)
+ zlog_debug(
+ "from vpn (%s) to vrf (%s), skipping after no intersection of route targets",
+ from_bgp->name_pretty, to_bgp->name_pretty);
+ return false;
+ }
+
+ if (debug)
+ zlog_debug("%s: updating %pFX to vrf %s", __func__, p,
+ to_bgp->name_pretty);
+
+ /* shallow copy */
+ static_attr = *path_vpn->attr;
+
+ struct ecommunity *old_ecom;
+ struct ecommunity *new_ecom;
+
+ /* If doing VRF-to-VRF leaking, strip RTs. */
+ old_ecom = bgp_attr_get_ecommunity(&static_attr);
+ if (old_ecom && CHECK_FLAG(to_bgp->af_flags[afi][safi],
+ BGP_CONFIG_VRF_TO_VRF_IMPORT)) {
+ new_ecom = ecommunity_dup(old_ecom);
+ ecommunity_strip_rts(new_ecom);
+ bgp_attr_set_ecommunity(&static_attr, new_ecom);
+
+ if (new_ecom->size == 0) {
+ ecommunity_free(&new_ecom);
+ bgp_attr_set_ecommunity(&static_attr, NULL);
+ }
+
+ if (!old_ecom->refcnt)
+ ecommunity_free(&old_ecom);
+ }
+
+ /*
+ * Nexthop: stash and clear
+ *
+ * Nexthop is valid in context of VPN core, but not in destination vrf.
+ * Stash it for later label resolution by vrf ingress path and then
+ * overwrite with 0, i.e., "me", for the sake of vrf advertisement.
+ */
+ uint8_t nhfamily = NEXTHOP_FAMILY(path_vpn->attr->mp_nexthop_len);
+
+ memset(&nexthop_orig, 0, sizeof(nexthop_orig));
+ nexthop_orig.family = nhfamily;
+
+ switch (nhfamily) {
+ case AF_INET:
+ /* save */
+ nexthop_orig.u.prefix4 = path_vpn->attr->mp_nexthop_global_in;
+ nexthop_orig.prefixlen = IPV4_MAX_BITLEN;
+
+ if (CHECK_FLAG(to_bgp->af_flags[afi][safi],
+ BGP_CONFIG_VRF_TO_VRF_IMPORT)) {
+ static_attr.nexthop.s_addr =
+ nexthop_orig.u.prefix4.s_addr;
+
+ static_attr.mp_nexthop_global_in =
+ path_vpn->attr->mp_nexthop_global_in;
+ static_attr.mp_nexthop_len =
+ path_vpn->attr->mp_nexthop_len;
+ }
+ static_attr.flag |= ATTR_FLAG_BIT(BGP_ATTR_NEXT_HOP);
+ break;
+ case AF_INET6:
+ /* save */
+ nexthop_orig.u.prefix6 = path_vpn->attr->mp_nexthop_global;
+ nexthop_orig.prefixlen = IPV6_MAX_BITLEN;
+
+ if (CHECK_FLAG(to_bgp->af_flags[afi][safi],
+ BGP_CONFIG_VRF_TO_VRF_IMPORT)) {
+ static_attr.mp_nexthop_global = nexthop_orig.u.prefix6;
+ }
+ break;
+ }
+
+ /*
+ * route map handling
+ */
+ if (to_bgp->vpn_policy[afi].rmap[BGP_VPN_POLICY_DIR_FROMVPN]) {
+ struct bgp_path_info info;
+ route_map_result_t ret;
+
+ memset(&info, 0, sizeof(info));
+ info.peer = to_bgp->peer_self;
+ info.attr = &static_attr;
+ info.extra = path_vpn->extra; /* Used for source-vrf filter */
+ ret = route_map_apply(to_bgp->vpn_policy[afi]
+ .rmap[BGP_VPN_POLICY_DIR_FROMVPN],
+ p, &info);
+ if (RMAP_DENYMATCH == ret) {
+ bgp_attr_flush(&static_attr); /* free any added parts */
+ if (debug)
+ zlog_debug(
+ "%s: vrf %s vpn-policy route map \"%s\" says DENY, returning",
+ __func__, to_bgp->name_pretty,
+ to_bgp->vpn_policy[afi]
+ .rmap[BGP_VPN_POLICY_DIR_FROMVPN]
+ ->name);
+ return false;
+ }
+ /*
+ * if route-map changed nexthop, don't nexthop-self on output
+ */
+ if (!CHECK_FLAG(static_attr.rmap_change_flags,
+ BATTR_RMAP_NEXTHOP_UNCHANGED))
+ nexthop_self_flag = 0;
+ }
+
+ new_attr = bgp_attr_intern(&static_attr);
+ bgp_attr_flush(&static_attr);
+
+ bn = bgp_afi_node_get(to_bgp->rib[afi][safi], afi, safi, p, NULL);
+
+ /*
+ * ensure labels are copied
+ *
+ * However, there is a special case: if the route originated in
+ * another local VRF (as opposed to arriving via VPN), then the
+ * nexthop is reached by hairpinning through this router (me)
+ * using IP forwarding only (no LSP). Therefore, the route
+ * imported to the VRF should not have labels attached. Note
+ * that nexthop tracking is also involved: eliminating the
+ * labels for these routes enables the non-labeled nexthops
+ * from the originating VRF to be considered valid for this route.
+ */
+ if (!CHECK_FLAG(to_bgp->af_flags[afi][safi],
+ BGP_CONFIG_VRF_TO_VRF_IMPORT)) {
+ /* work back to original route */
+ bpi_ultimate = bgp_get_imported_bpi_ultimate(path_vpn);
+
+ /*
+ * if original route was unicast,
+ * then it did not arrive over vpn
+ */
+ if (bpi_ultimate->net) {
+ struct bgp_table *table;
+
+ table = bgp_dest_table(bpi_ultimate->net);
+ if (table && (table->safi == SAFI_UNICAST))
+ origin_local = 1;
+ }
+
+ /* copy labels */
+ if (!origin_local && path_vpn->extra
+ && path_vpn->extra->num_labels) {
+ num_labels = path_vpn->extra->num_labels;
+ if (num_labels > BGP_MAX_LABELS)
+ num_labels = BGP_MAX_LABELS;
+ pLabels = path_vpn->extra->label;
+ }
+ }
+
+ if (debug)
+ zlog_debug("%s: pfx %pBD: num_labels %d", __func__,
+ path_vpn->net, num_labels);
+
+ /*
+ * For VRF-2-VRF route-leaking,
+ * the source will be the originating VRF.
+ */
+ if (path_vpn->extra && path_vpn->extra->bgp_orig)
+ src_vrf = path_vpn->extra->bgp_orig;
+ else
+ src_vrf = from_bgp;
+
+ leak_update(to_bgp, bn, new_attr, afi, safi, path_vpn, pLabels,
+ num_labels, src_vrf, &nexthop_orig, nexthop_self_flag,
+ debug);
+ return true;
+}
+
+bool vpn_leak_to_vrf_update(struct bgp *from_bgp, /* from */
+ struct bgp_path_info *path_vpn) /* route */
+{
+ struct listnode *mnode, *mnnode;
+ struct bgp *bgp;
+ bool leak_success = false;
+
+ int debug = BGP_DEBUG(vpn, VPN_LEAK_TO_VRF);
+
+ if (debug)
+ zlog_debug("%s: start (path_vpn=%p)", __func__, path_vpn);
+
+ /* Loop over VRFs */
+ for (ALL_LIST_ELEMENTS(bm->bgp, mnode, mnnode, bgp)) {
+
+ if (!path_vpn->extra
+ || path_vpn->extra->bgp_orig != bgp) { /* no loop */
+ leak_success |= vpn_leak_to_vrf_update_onevrf(
+ bgp, from_bgp, path_vpn);
+ }
+ }
+ return leak_success;
+}
+
+void vpn_leak_to_vrf_withdraw(struct bgp *from_bgp, /* from */
+ struct bgp_path_info *path_vpn) /* route */
+{
+ const struct prefix *p;
+ afi_t afi;
+ safi_t safi = SAFI_UNICAST;
+ struct bgp *bgp;
+ struct listnode *mnode, *mnnode;
+ struct bgp_dest *bn;
+ struct bgp_path_info *bpi;
+ const char *debugmsg;
+
+ int debug = BGP_DEBUG(vpn, VPN_LEAK_TO_VRF);
+
+ if (debug)
+ zlog_debug("%s: entry: p=%pBD, type=%d, sub_type=%d", __func__,
+ path_vpn->net, path_vpn->type, path_vpn->sub_type);
+
+ if (debug)
+ zlog_debug("%s: start (path_vpn=%p)", __func__, path_vpn);
+
+ if (!path_vpn->net) {
+#ifdef ENABLE_BGP_VNC
+ /* BGP_ROUTE_RFP routes do not have path_vpn->net set (yet) */
+ if (path_vpn->type == ZEBRA_ROUTE_BGP
+ && path_vpn->sub_type == BGP_ROUTE_RFP) {
+
+ return;
+ }
+#endif
+ if (debug)
+ zlog_debug(
+ "%s: path_vpn->net unexpectedly NULL, no prefix, bailing",
+ __func__);
+ return;
+ }
+
+ p = bgp_dest_get_prefix(path_vpn->net);
+ afi = family2afi(p->family);
+
+ /* Loop over VRFs */
+ for (ALL_LIST_ELEMENTS(bm->bgp, mnode, mnnode, bgp)) {
+ if (!vpn_leak_from_vpn_active(bgp, afi, &debugmsg)) {
+ if (debug)
+ zlog_debug("%s: skipping: %s", __func__,
+ debugmsg);
+ continue;
+ }
+
+ /* Check for intersection of route targets */
+ if (!ecommunity_include(
+ bgp->vpn_policy[afi]
+ .rtlist[BGP_VPN_POLICY_DIR_FROMVPN],
+ bgp_attr_get_ecommunity(path_vpn->attr))) {
+
+ continue;
+ }
+
+ if (debug)
+ zlog_debug("%s: withdrawing from vrf %s", __func__,
+ bgp->name_pretty);
+
+ bn = bgp_afi_node_get(bgp->rib[afi][safi], afi, safi, p, NULL);
+
+ for (bpi = bgp_dest_get_bgp_path_info(bn); bpi;
+ bpi = bpi->next) {
+ if (bpi->extra
+ && (struct bgp_path_info *)bpi->extra->parent
+ == path_vpn) {
+ break;
+ }
+ }
+
+ if (bpi) {
+ if (debug)
+ zlog_debug("%s: deleting bpi %p", __func__,
+ bpi);
+ bgp_aggregate_decrement(bgp, p, bpi, afi, safi);
+ bgp_path_info_delete(bn, bpi);
+ bgp_process(bgp, bn, afi, safi);
+ }
+ bgp_dest_unlock_node(bn);
+ }
+}
+
+void vpn_leak_to_vrf_withdraw_all(struct bgp *to_bgp, afi_t afi)
+{
+ struct bgp_dest *bn;
+ struct bgp_path_info *bpi;
+ safi_t safi = SAFI_UNICAST;
+ int debug = BGP_DEBUG(vpn, VPN_LEAK_TO_VRF);
+
+ if (debug)
+ zlog_debug("%s: entry", __func__);
+ /*
+ * Walk vrf table, delete bpi with bgp_orig in a different vrf
+ */
+ for (bn = bgp_table_top(to_bgp->rib[afi][safi]); bn;
+ bn = bgp_route_next(bn)) {
+
+ for (bpi = bgp_dest_get_bgp_path_info(bn); bpi;
+ bpi = bpi->next) {
+ if (bpi->extra && bpi->extra->bgp_orig != to_bgp &&
+ bpi->extra->parent &&
+ is_pi_family_vpn(bpi->extra->parent)) {
+
+ /* delete route */
+ bgp_aggregate_decrement(to_bgp,
+ bgp_dest_get_prefix(bn),
+ bpi, afi, safi);
+ bgp_path_info_delete(bn, bpi);
+ bgp_process(to_bgp, bn, afi, safi);
+ }
+ }
+ }
+}
+
+void vpn_leak_to_vrf_update_all(struct bgp *to_bgp, struct bgp *vpn_from,
+ afi_t afi)
+{
+ struct bgp_dest *pdest;
+ safi_t safi = SAFI_MPLS_VPN;
+
+ assert(vpn_from);
+
+ /*
+ * Walk vpn table
+ */
+ for (pdest = bgp_table_top(vpn_from->rib[afi][safi]); pdest;
+ pdest = bgp_route_next(pdest)) {
+ struct bgp_table *table;
+ struct bgp_dest *bn;
+ struct bgp_path_info *bpi;
+
+ /* This is the per-RD table of prefixes */
+ table = bgp_dest_get_bgp_table_info(pdest);
+
+ if (!table)
+ continue;
+
+ for (bn = bgp_table_top(table); bn; bn = bgp_route_next(bn)) {
+
+ for (bpi = bgp_dest_get_bgp_path_info(bn); bpi;
+ bpi = bpi->next) {
+
+ if (bpi->extra &&
+ bpi->extra->bgp_orig == to_bgp)
+ continue;
+
+ vpn_leak_to_vrf_update_onevrf(to_bgp, vpn_from,
+ bpi);
+ }
+ }
+ }
+}
+
+/*
+ * This function is called for definition/deletion/change to a route-map
+ */
+static void vpn_policy_routemap_update(struct bgp *bgp, const char *rmap_name)
+{
+ int debug = BGP_DEBUG(vpn, VPN_LEAK_RMAP_EVENT);
+ afi_t afi;
+ struct route_map *rmap;
+
+ if (bgp->inst_type != BGP_INSTANCE_TYPE_DEFAULT
+ && bgp->inst_type != BGP_INSTANCE_TYPE_VRF) {
+
+ return;
+ }
+
+ rmap = route_map_lookup_by_name(rmap_name); /* NULL if deleted */
+
+ for (afi = 0; afi < AFI_MAX; ++afi) {
+
+ if (bgp->vpn_policy[afi].rmap_name[BGP_VPN_POLICY_DIR_TOVPN]
+ && !strcmp(rmap_name,
+ bgp->vpn_policy[afi]
+ .rmap_name[BGP_VPN_POLICY_DIR_TOVPN])) {
+
+ if (debug)
+ zlog_debug(
+ "%s: rmap \"%s\" matches vrf-policy tovpn for as %d afi %s",
+ __func__, rmap_name, bgp->as,
+ afi2str(afi));
+
+ vpn_leak_prechange(BGP_VPN_POLICY_DIR_TOVPN, afi,
+ bgp_get_default(), bgp);
+ if (debug)
+ zlog_debug("%s: after vpn_leak_prechange",
+ __func__);
+
+ /* in case of definition/deletion */
+ bgp->vpn_policy[afi].rmap[BGP_VPN_POLICY_DIR_TOVPN] =
+ rmap;
+
+ vpn_leak_postchange(BGP_VPN_POLICY_DIR_TOVPN, afi,
+ bgp_get_default(), bgp);
+
+ if (debug)
+ zlog_debug("%s: after vpn_leak_postchange",
+ __func__);
+ }
+
+ if (bgp->vpn_policy[afi].rmap_name[BGP_VPN_POLICY_DIR_FROMVPN]
+ && !strcmp(rmap_name,
+ bgp->vpn_policy[afi]
+ .rmap_name[BGP_VPN_POLICY_DIR_FROMVPN])) {
+
+ if (debug) {
+ zlog_debug("%s: rmap \"%s\" matches vrf-policy fromvpn for as %d afi %s",
+ __func__, rmap_name, bgp->as,
+ afi2str(afi));
+ }
+
+ vpn_leak_prechange(BGP_VPN_POLICY_DIR_FROMVPN, afi,
+ bgp_get_default(), bgp);
+
+ /* in case of definition/deletion */
+ bgp->vpn_policy[afi].rmap[BGP_VPN_POLICY_DIR_FROMVPN] =
+ rmap;
+
+ vpn_leak_postchange(BGP_VPN_POLICY_DIR_FROMVPN, afi,
+ bgp_get_default(), bgp);
+ }
+ }
+}
+
+/* This API is used during router-id change, reflect VPNs
+ * auto RD and RT values and readvertise routes to VPN table.
+ */
+void vpn_handle_router_id_update(struct bgp *bgp, bool withdraw,
+ bool is_config)
+{
+ afi_t afi;
+ int debug = (BGP_DEBUG(vpn, VPN_LEAK_TO_VRF)
+ | BGP_DEBUG(vpn, VPN_LEAK_FROM_VRF));
+ char *vname;
+ const char *export_name;
+ char buf[RD_ADDRSTRLEN];
+ struct bgp *bgp_import;
+ struct listnode *node;
+ struct ecommunity *ecom;
+ enum vpn_policy_direction idir, edir;
+
+ /*
+ * Router-id change that is not explicitly configured
+ * (a change from zebra, frr restart for example)
+ * should not replace a configured vpn RD/RT.
+ */
+ if (!is_config) {
+ if (debug)
+ zlog_debug("%s: skipping non explicit router-id change",
+ __func__);
+ return;
+ }
+
+ if (bgp->inst_type != BGP_INSTANCE_TYPE_DEFAULT
+ && bgp->inst_type != BGP_INSTANCE_TYPE_VRF)
+ return;
+
+ export_name = bgp->name ? bgp->name : VRF_DEFAULT_NAME;
+ idir = BGP_VPN_POLICY_DIR_FROMVPN;
+ edir = BGP_VPN_POLICY_DIR_TOVPN;
+
+ for (afi = 0; afi < AFI_MAX; ++afi) {
+ if (!vpn_leak_to_vpn_active(bgp, afi, NULL))
+ continue;
+
+ if (withdraw) {
+ vpn_leak_prechange(BGP_VPN_POLICY_DIR_TOVPN,
+ afi, bgp_get_default(), bgp);
+ if (debug)
+ zlog_debug("%s: %s after to_vpn vpn_leak_prechange",
+ __func__, export_name);
+
+ /* Remove import RT from VRFs */
+ ecom = bgp->vpn_policy[afi].rtlist[edir];
+ for (ALL_LIST_ELEMENTS_RO(bgp->vpn_policy[afi].
+ export_vrf, node, vname)) {
+ if (strcmp(vname, VRF_DEFAULT_NAME) == 0)
+ bgp_import = bgp_get_default();
+ else
+ bgp_import = bgp_lookup_by_name(vname);
+ if (!bgp_import)
+ continue;
+
+ ecommunity_del_val(
+ bgp_import->vpn_policy[afi]
+ .rtlist[idir],
+ (struct ecommunity_val *)ecom->val);
+ }
+ } else {
+ /* New router-id derive auto RD and RT and export
+ * to VPN
+ */
+ form_auto_rd(bgp->router_id, bgp->vrf_rd_id,
+ &bgp->vrf_prd_auto);
+ bgp->vpn_policy[afi].tovpn_rd = bgp->vrf_prd_auto;
+ prefix_rd2str(&bgp->vpn_policy[afi].tovpn_rd, buf,
+ sizeof(buf));
+
+ /* free up pre-existing memory if any and allocate
+ * the ecommunity attribute with new RD/RT
+ */
+ if (bgp->vpn_policy[afi].rtlist[edir])
+ ecommunity_free(
+ &bgp->vpn_policy[afi].rtlist[edir]);
+ bgp->vpn_policy[afi].rtlist[edir] = ecommunity_str2com(
+ buf, ECOMMUNITY_ROUTE_TARGET, 0);
+
+ /* Update import_vrf rt_list */
+ ecom = bgp->vpn_policy[afi].rtlist[edir];
+ for (ALL_LIST_ELEMENTS_RO(bgp->vpn_policy[afi].
+ export_vrf, node, vname)) {
+ if (strcmp(vname, VRF_DEFAULT_NAME) == 0)
+ bgp_import = bgp_get_default();
+ else
+ bgp_import = bgp_lookup_by_name(vname);
+ if (!bgp_import)
+ continue;
+ if (bgp_import->vpn_policy[afi].rtlist[idir])
+ bgp_import->vpn_policy[afi].rtlist[idir]
+ = ecommunity_merge(
+ bgp_import->vpn_policy[afi]
+ .rtlist[idir], ecom);
+ else
+ bgp_import->vpn_policy[afi].rtlist[idir]
+ = ecommunity_dup(ecom);
+ }
+
+ /* Update routes to VPN */
+ vpn_leak_postchange(BGP_VPN_POLICY_DIR_TOVPN,
+ afi, bgp_get_default(),
+ bgp);
+ if (debug)
+ zlog_debug("%s: %s after to_vpn vpn_leak_postchange",
+ __func__, export_name);
+ }
+ }
+}
+
+void vpn_policy_routemap_event(const char *rmap_name)
+{
+ int debug = BGP_DEBUG(vpn, VPN_LEAK_RMAP_EVENT);
+ struct listnode *mnode, *mnnode;
+ struct bgp *bgp;
+
+ if (debug)
+ zlog_debug("%s: entry", __func__);
+
+ if (bm->bgp == NULL) /* may be called during cleanup */
+ return;
+
+ for (ALL_LIST_ELEMENTS(bm->bgp, mnode, mnnode, bgp))
+ vpn_policy_routemap_update(bgp, rmap_name);
+}
+
+void vrf_import_from_vrf(struct bgp *to_bgp, struct bgp *from_bgp,
+ afi_t afi, safi_t safi)
+{
+ const char *export_name;
+ enum vpn_policy_direction idir, edir;
+ char *vname, *tmp_name;
+ char buf[RD_ADDRSTRLEN];
+ struct ecommunity *ecom;
+ bool first_export = false;
+ int debug;
+ struct listnode *node;
+ bool is_inst_match = false;
+
+ export_name = to_bgp->name ? to_bgp->name : VRF_DEFAULT_NAME;
+ idir = BGP_VPN_POLICY_DIR_FROMVPN;
+ edir = BGP_VPN_POLICY_DIR_TOVPN;
+
+ debug = (BGP_DEBUG(vpn, VPN_LEAK_TO_VRF) |
+ BGP_DEBUG(vpn, VPN_LEAK_FROM_VRF));
+
+ /*
+ * Cross-ref both VRFs. Also, note if this is the first time
+ * any VRF is importing from "import_vrf".
+ */
+ vname = (from_bgp->name ? XSTRDUP(MTYPE_TMP, from_bgp->name)
+ : XSTRDUP(MTYPE_TMP, VRF_DEFAULT_NAME));
+
+ /* Check the import_vrf list of destination vrf for the source vrf name,
+ * insert otherwise.
+ */
+ for (ALL_LIST_ELEMENTS_RO(to_bgp->vpn_policy[afi].import_vrf,
+ node, tmp_name)) {
+ if (strcmp(vname, tmp_name) == 0) {
+ is_inst_match = true;
+ break;
+ }
+ }
+ if (!is_inst_match)
+ listnode_add(to_bgp->vpn_policy[afi].import_vrf,
+ vname);
+ else
+ XFREE(MTYPE_TMP, vname);
+
+ /* Check if the source vrf already exports to any vrf,
+ * first time export requires to setup auto derived RD/RT values.
+ * Add the destination vrf name to export vrf list if it is
+ * not present.
+ */
+ is_inst_match = false;
+ vname = XSTRDUP(MTYPE_TMP, export_name);
+ if (!listcount(from_bgp->vpn_policy[afi].export_vrf)) {
+ first_export = true;
+ } else {
+ for (ALL_LIST_ELEMENTS_RO(from_bgp->vpn_policy[afi].export_vrf,
+ node, tmp_name)) {
+ if (strcmp(vname, tmp_name) == 0) {
+ is_inst_match = true;
+ break;
+ }
+ }
+ }
+ if (!is_inst_match)
+ listnode_add(from_bgp->vpn_policy[afi].export_vrf,
+ vname);
+ else
+ XFREE(MTYPE_TMP, vname);
+
+ /* Update import RT for current VRF using export RT of the VRF we're
+ * importing from. First though, make sure "import_vrf" has that
+ * set.
+ */
+ if (first_export) {
+ form_auto_rd(from_bgp->router_id, from_bgp->vrf_rd_id,
+ &from_bgp->vrf_prd_auto);
+ from_bgp->vpn_policy[afi].tovpn_rd = from_bgp->vrf_prd_auto;
+ SET_FLAG(from_bgp->vpn_policy[afi].flags,
+ BGP_VPN_POLICY_TOVPN_RD_SET);
+ prefix_rd2str(&from_bgp->vpn_policy[afi].tovpn_rd,
+ buf, sizeof(buf));
+ from_bgp->vpn_policy[afi].rtlist[edir] =
+ ecommunity_str2com(buf, ECOMMUNITY_ROUTE_TARGET, 0);
+ SET_FLAG(from_bgp->af_flags[afi][safi],
+ BGP_CONFIG_VRF_TO_VRF_EXPORT);
+ from_bgp->vpn_policy[afi].tovpn_label =
+ BGP_PREVENT_VRF_2_VRF_LEAK;
+ }
+ ecom = from_bgp->vpn_policy[afi].rtlist[edir];
+ if (to_bgp->vpn_policy[afi].rtlist[idir])
+ to_bgp->vpn_policy[afi].rtlist[idir] =
+ ecommunity_merge(to_bgp->vpn_policy[afi]
+ .rtlist[idir], ecom);
+ else
+ to_bgp->vpn_policy[afi].rtlist[idir] = ecommunity_dup(ecom);
+ SET_FLAG(to_bgp->af_flags[afi][safi], BGP_CONFIG_VRF_TO_VRF_IMPORT);
+
+ if (debug) {
+ const char *from_name;
+ char *ecom1, *ecom2;
+
+ from_name = from_bgp->name ? from_bgp->name :
+ VRF_DEFAULT_NAME;
+
+ ecom1 = ecommunity_ecom2str(
+ to_bgp->vpn_policy[afi].rtlist[idir],
+ ECOMMUNITY_FORMAT_ROUTE_MAP, 0);
+
+ ecom2 = ecommunity_ecom2str(
+ to_bgp->vpn_policy[afi].rtlist[edir],
+ ECOMMUNITY_FORMAT_ROUTE_MAP, 0);
+
+ zlog_debug(
+ "%s from %s to %s first_export %u import-rt %s export-rt %s",
+ __func__, from_name, export_name, first_export, ecom1,
+ ecom2);
+
+ ecommunity_strfree(&ecom1);
+ ecommunity_strfree(&ecom2);
+ }
+
+ /* Does "import_vrf" first need to export its routes or that
+ * is already done and we just need to import those routes
+ * from the global table?
+ */
+ if (first_export)
+ vpn_leak_postchange(edir, afi, bgp_get_default(), from_bgp);
+ else
+ vpn_leak_postchange(idir, afi, bgp_get_default(), to_bgp);
+}
+
+void vrf_unimport_from_vrf(struct bgp *to_bgp, struct bgp *from_bgp,
+ afi_t afi, safi_t safi)
+{
+ const char *export_name, *tmp_name;
+ enum vpn_policy_direction idir, edir;
+ char *vname;
+ struct ecommunity *ecom = NULL;
+ struct listnode *node;
+ int debug;
+
+ export_name = to_bgp->name ? to_bgp->name : VRF_DEFAULT_NAME;
+ tmp_name = from_bgp->name ? from_bgp->name : VRF_DEFAULT_NAME;
+ idir = BGP_VPN_POLICY_DIR_FROMVPN;
+ edir = BGP_VPN_POLICY_DIR_TOVPN;
+
+ debug = (BGP_DEBUG(vpn, VPN_LEAK_TO_VRF) |
+ BGP_DEBUG(vpn, VPN_LEAK_FROM_VRF));
+
+ /* Were we importing from "import_vrf"? */
+ for (ALL_LIST_ELEMENTS_RO(to_bgp->vpn_policy[afi].import_vrf, node,
+ vname)) {
+ if (strcmp(vname, tmp_name) == 0)
+ break;
+ }
+
+ /*
+ * We do not check in the cli if the passed in bgp
+ * instance is actually imported into us before
+ * we call this function. As such if we do not
+ * find this in the import_vrf list than
+ * we just need to return safely.
+ */
+ if (!vname)
+ return;
+
+ if (debug)
+ zlog_debug("%s from %s to %s", __func__, tmp_name, export_name);
+
+ /* Remove "import_vrf" from our import list. */
+ listnode_delete(to_bgp->vpn_policy[afi].import_vrf, vname);
+ XFREE(MTYPE_TMP, vname);
+
+ /* Remove routes imported from "import_vrf". */
+ /* TODO: In the current logic, we have to first remove all
+ * imported routes and then (if needed) import back routes
+ */
+ vpn_leak_prechange(idir, afi, bgp_get_default(), to_bgp);
+
+ if (to_bgp->vpn_policy[afi].import_vrf->count == 0) {
+ if (!to_bgp->vpn_policy[afi].rmap[idir])
+ UNSET_FLAG(to_bgp->af_flags[afi][safi],
+ BGP_CONFIG_VRF_TO_VRF_IMPORT);
+ if (to_bgp->vpn_policy[afi].rtlist[idir])
+ ecommunity_free(&to_bgp->vpn_policy[afi].rtlist[idir]);
+ } else {
+ ecom = from_bgp->vpn_policy[afi].rtlist[edir];
+ if (ecom)
+ ecommunity_del_val(to_bgp->vpn_policy[afi].rtlist[idir],
+ (struct ecommunity_val *)ecom->val);
+ vpn_leak_postchange(idir, afi, bgp_get_default(), to_bgp);
+ }
+
+ /*
+ * What?
+ * So SA is assuming that since the ALL_LIST_ELEMENTS_RO
+ * below is checking for NULL that export_vrf can be
+ * NULL, consequently it is complaining( like a cabbage )
+ * that we could dereference and crash in the listcount(..)
+ * check below.
+ * So make it happy, under protest, with liberty and justice
+ * for all.
+ */
+ assert(from_bgp->vpn_policy[afi].export_vrf);
+
+ /* Remove us from "import_vrf's" export list. If no other VRF
+ * is importing from "import_vrf", cleanup appropriately.
+ */
+ for (ALL_LIST_ELEMENTS_RO(from_bgp->vpn_policy[afi].export_vrf,
+ node, vname)) {
+ if (strcmp(vname, export_name) == 0)
+ break;
+ }
+
+ /*
+ * If we have gotten to this point then the vname must
+ * exist. If not, we are in a world of trouble and
+ * have slag sitting around.
+ *
+ * import_vrf and export_vrf must match in having
+ * the in/out names as appropriate.
+ * export_vrf list could have been cleaned up
+ * as part of no router bgp source instnace.
+ */
+ if (!vname)
+ return;
+
+ listnode_delete(from_bgp->vpn_policy[afi].export_vrf, vname);
+ XFREE(MTYPE_TMP, vname);
+
+ if (!listcount(from_bgp->vpn_policy[afi].export_vrf)) {
+ vpn_leak_prechange(edir, afi, bgp_get_default(), from_bgp);
+ ecommunity_free(&from_bgp->vpn_policy[afi].rtlist[edir]);
+ UNSET_FLAG(from_bgp->af_flags[afi][safi],
+ BGP_CONFIG_VRF_TO_VRF_EXPORT);
+ memset(&from_bgp->vpn_policy[afi].tovpn_rd, 0,
+ sizeof(struct prefix_rd));
+ UNSET_FLAG(from_bgp->vpn_policy[afi].flags,
+ BGP_VPN_POLICY_TOVPN_RD_SET);
+ from_bgp->vpn_policy[afi].tovpn_label = MPLS_LABEL_NONE;
+
+ }
+}
+
+/* For testing purpose, static route of MPLS-VPN. */
+DEFUN (vpnv4_network,
+ vpnv4_network_cmd,
+ "network A.B.C.D/M rd ASN:NN_OR_IP-ADDRESS:NN <tag|label> (0-1048575)",
+ "Specify a network to announce via BGP\n"
+ "IPv4 prefix\n"
+ "Specify Route Distinguisher\n"
+ "VPN Route Distinguisher\n"
+ "VPN NLRI label (tag)\n"
+ "VPN NLRI label (tag)\n"
+ "Label value\n")
+{
+ int idx_ipv4_prefixlen = 1;
+ int idx_ext_community = 3;
+ int idx_label = 5;
+ return bgp_static_set_safi(
+ AFI_IP, SAFI_MPLS_VPN, vty, argv[idx_ipv4_prefixlen]->arg,
+ argv[idx_ext_community]->arg, argv[idx_label]->arg, NULL, 0,
+ NULL, NULL, NULL, NULL);
+}
+
+DEFUN (vpnv4_network_route_map,
+ vpnv4_network_route_map_cmd,
+ "network A.B.C.D/M rd ASN:NN_OR_IP-ADDRESS:NN <tag|label> (0-1048575) route-map RMAP_NAME",
+ "Specify a network to announce via BGP\n"
+ "IPv4 prefix\n"
+ "Specify Route Distinguisher\n"
+ "VPN Route Distinguisher\n"
+ "VPN NLRI label (tag)\n"
+ "VPN NLRI label (tag)\n"
+ "Label value\n"
+ "route map\n"
+ "route map name\n")
+{
+ int idx_ipv4_prefixlen = 1;
+ int idx_ext_community = 3;
+ int idx_label = 5;
+ int idx_word_2 = 7;
+ return bgp_static_set_safi(
+ AFI_IP, SAFI_MPLS_VPN, vty, argv[idx_ipv4_prefixlen]->arg,
+ argv[idx_ext_community]->arg, argv[idx_label]->arg,
+ argv[idx_word_2]->arg, 0, NULL, NULL, NULL, NULL);
+}
+
+/* For testing purpose, static route of MPLS-VPN. */
+DEFUN (no_vpnv4_network,
+ no_vpnv4_network_cmd,
+ "no network A.B.C.D/M rd ASN:NN_OR_IP-ADDRESS:NN <tag|label> (0-1048575)",
+ NO_STR
+ "Specify a network to announce via BGP\n"
+ "IPv4 prefix\n"
+ "Specify Route Distinguisher\n"
+ "VPN Route Distinguisher\n"
+ "VPN NLRI label (tag)\n"
+ "VPN NLRI label (tag)\n"
+ "Label value\n")
+{
+ int idx_ipv4_prefixlen = 2;
+ int idx_ext_community = 4;
+ int idx_label = 6;
+ return bgp_static_unset_safi(AFI_IP, SAFI_MPLS_VPN, vty,
+ argv[idx_ipv4_prefixlen]->arg,
+ argv[idx_ext_community]->arg,
+ argv[idx_label]->arg, 0, NULL, NULL, NULL);
+}
+
+DEFUN (vpnv6_network,
+ vpnv6_network_cmd,
+ "network X:X::X:X/M rd ASN:NN_OR_IP-ADDRESS:NN <tag|label> (0-1048575) [route-map RMAP_NAME]",
+ "Specify a network to announce via BGP\n"
+ "IPv6 prefix <network>/<length>, e.g., 3ffe::/16\n"
+ "Specify Route Distinguisher\n"
+ "VPN Route Distinguisher\n"
+ "VPN NLRI label (tag)\n"
+ "VPN NLRI label (tag)\n"
+ "Label value\n"
+ "route map\n"
+ "route map name\n")
+{
+ int idx_ipv6_prefix = 1;
+ int idx_ext_community = 3;
+ int idx_label = 5;
+ int idx_word_2 = 7;
+ if (argc == 8)
+ return bgp_static_set_safi(
+ AFI_IP6, SAFI_MPLS_VPN, vty, argv[idx_ipv6_prefix]->arg,
+ argv[idx_ext_community]->arg, argv[idx_label]->arg,
+ argv[idx_word_2]->arg, 0, NULL, NULL, NULL, NULL);
+ else
+ return bgp_static_set_safi(
+ AFI_IP6, SAFI_MPLS_VPN, vty, argv[idx_ipv6_prefix]->arg,
+ argv[idx_ext_community]->arg, argv[idx_label]->arg,
+ NULL, 0, NULL, NULL, NULL, NULL);
+}
+
+/* For testing purpose, static route of MPLS-VPN. */
+DEFUN (no_vpnv6_network,
+ no_vpnv6_network_cmd,
+ "no network X:X::X:X/M rd ASN:NN_OR_IP-ADDRESS:NN <tag|label> (0-1048575)",
+ NO_STR
+ "Specify a network to announce via BGP\n"
+ "IPv6 prefix <network>/<length>, e.g., 3ffe::/16\n"
+ "Specify Route Distinguisher\n"
+ "VPN Route Distinguisher\n"
+ "VPN NLRI label (tag)\n"
+ "VPN NLRI label (tag)\n"
+ "Label value\n")
+{
+ int idx_ipv6_prefix = 2;
+ int idx_ext_community = 4;
+ int idx_label = 6;
+ return bgp_static_unset_safi(AFI_IP6, SAFI_MPLS_VPN, vty,
+ argv[idx_ipv6_prefix]->arg,
+ argv[idx_ext_community]->arg,
+ argv[idx_label]->arg, 0, NULL, NULL, NULL);
+}
+
+int bgp_show_mpls_vpn(struct vty *vty, afi_t afi, struct prefix_rd *prd,
+ enum bgp_show_type type, void *output_arg, int tags,
+ bool use_json)
+{
+ struct bgp *bgp;
+ struct bgp_table *table;
+
+ 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;
+ }
+ table = bgp->rib[afi][SAFI_MPLS_VPN];
+ return bgp_show_table_rd(vty, bgp, SAFI_MPLS_VPN, table, prd, type,
+ output_arg, use_json);
+}
+
+DEFUN (show_bgp_ip_vpn_all_rd,
+ show_bgp_ip_vpn_all_rd_cmd,
+ "show bgp "BGP_AFI_CMD_STR" vpn all [rd <ASN:NN_OR_IP-ADDRESS:NN|all>] [json]",
+ SHOW_STR
+ BGP_STR
+ BGP_VPNVX_HELP_STR
+ "Display VPN NLRI specific information\n"
+ "Display VPN NLRI specific information\n"
+ "Display information for a route distinguisher\n"
+ "VPN Route Distinguisher\n"
+ "All VPN Route Distinguishers\n"
+ JSON_STR)
+{
+ int ret;
+ struct prefix_rd prd;
+ afi_t afi;
+ int idx = 0;
+
+ if (argv_find_and_parse_afi(argv, argc, &idx, &afi)) {
+ /* Constrain search if user supplies RD && RD != "all" */
+ if (argv_find(argv, argc, "rd", &idx)
+ && strcmp(argv[idx + 1]->arg, "all")) {
+ ret = str2prefix_rd(argv[idx + 1]->arg, &prd);
+ if (!ret) {
+ vty_out(vty,
+ "%% Malformed Route Distinguisher\n");
+ return CMD_WARNING;
+ }
+ return bgp_show_mpls_vpn(vty, afi, &prd,
+ bgp_show_type_normal, NULL, 0,
+ use_json(argc, argv));
+ } else {
+ return bgp_show_mpls_vpn(vty, afi, NULL,
+ bgp_show_type_normal, NULL, 0,
+ use_json(argc, argv));
+ }
+ }
+ return CMD_SUCCESS;
+}
+
+ALIAS(show_bgp_ip_vpn_all_rd,
+ show_bgp_ip_vpn_rd_cmd,
+ "show bgp "BGP_AFI_CMD_STR" vpn rd <ASN:NN_OR_IP-ADDRESS:NN|all> [json]",
+ SHOW_STR
+ BGP_STR
+ BGP_VPNVX_HELP_STR
+ "Display VPN NLRI specific information\n"
+ "Display information for a route distinguisher\n"
+ "VPN Route Distinguisher\n"
+ "All VPN Route Distinguishers\n"
+ JSON_STR)
+
+#ifdef KEEP_OLD_VPN_COMMANDS
+DEFUN (show_ip_bgp_vpn_rd,
+ show_ip_bgp_vpn_rd_cmd,
+ "show ip bgp "BGP_AFI_CMD_STR" vpn rd <ASN:NN_OR_IP-ADDRESS:NN|all>",
+ SHOW_STR
+ IP_STR
+ BGP_STR
+ BGP_AFI_HELP_STR
+ BGP_AF_MODIFIER_STR
+ "Display information for a route distinguisher\n"
+ "VPN Route Distinguisher\n"
+ "All VPN Route Distinguishers\n")
+{
+ int idx_ext_community = argc - 1;
+ int ret;
+ struct prefix_rd prd;
+ afi_t afi;
+ int idx = 0;
+
+ if (argv_find_and_parse_vpnvx(argv, argc, &idx, &afi)) {
+ if (!strcmp(argv[idx_ext_community]->arg, "all"))
+ return bgp_show_mpls_vpn(vty, afi, NULL,
+ bgp_show_type_normal, NULL, 0,
+ 0);
+ ret = str2prefix_rd(argv[idx_ext_community]->arg, &prd);
+ if (!ret) {
+ vty_out(vty, "%% Malformed Route Distinguisher\n");
+ return CMD_WARNING;
+ }
+ return bgp_show_mpls_vpn(vty, afi, &prd, bgp_show_type_normal,
+ NULL, 0, 0);
+ }
+ return CMD_SUCCESS;
+}
+
+DEFUN (show_ip_bgp_vpn_all,
+ show_ip_bgp_vpn_all_cmd,
+ "show [ip] bgp <vpnv4|vpnv6>",
+ SHOW_STR
+ IP_STR
+ BGP_STR
+ BGP_VPNVX_HELP_STR)
+{
+ afi_t afi;
+ int idx = 0;
+
+ if (argv_find_and_parse_vpnvx(argv, argc, &idx, &afi))
+ return bgp_show_mpls_vpn(vty, afi, NULL, bgp_show_type_normal,
+ NULL, 0, 0);
+ return CMD_SUCCESS;
+}
+
+DEFUN (show_ip_bgp_vpn_all_tags,
+ show_ip_bgp_vpn_all_tags_cmd,
+ "show [ip] bgp <vpnv4|vpnv6> all tags",
+ SHOW_STR
+ IP_STR
+ BGP_STR
+ BGP_VPNVX_HELP_STR
+ "Display information about all VPNv4/VPNV6 NLRIs\n"
+ "Display BGP tags for prefixes\n")
+{
+ afi_t afi;
+ int idx = 0;
+
+ if (argv_find_and_parse_vpnvx(argv, argc, &idx, &afi))
+ return bgp_show_mpls_vpn(vty, afi, NULL, bgp_show_type_normal,
+ NULL, 1, 0);
+ return CMD_SUCCESS;
+}
+
+DEFUN (show_ip_bgp_vpn_rd_tags,
+ show_ip_bgp_vpn_rd_tags_cmd,
+ "show [ip] bgp <vpnv4|vpnv6> rd <ASN:NN_OR_IP-ADDRESS:NN|all> tags",
+ SHOW_STR
+ IP_STR
+ BGP_STR
+ BGP_VPNVX_HELP_STR
+ "Display information for a route distinguisher\n"
+ "VPN Route Distinguisher\n"
+ "All VPN Route Distinguishers\n"
+ "Display BGP tags for prefixes\n")
+{
+ int idx_ext_community = 5;
+ int ret;
+ struct prefix_rd prd;
+ afi_t afi;
+ int idx = 0;
+
+ if (argv_find_and_parse_vpnvx(argv, argc, &idx, &afi)) {
+ if (!strcmp(argv[idx_ext_community]->arg, "all"))
+ return bgp_show_mpls_vpn(vty, afi, NULL,
+ bgp_show_type_normal, NULL, 1,
+ 0);
+ ret = str2prefix_rd(argv[idx_ext_community]->arg, &prd);
+ if (!ret) {
+ vty_out(vty, "%% Malformed Route Distinguisher\n");
+ return CMD_WARNING;
+ }
+ return bgp_show_mpls_vpn(vty, afi, &prd, bgp_show_type_normal,
+ NULL, 1, 0);
+ }
+ return CMD_SUCCESS;
+}
+
+DEFUN (show_ip_bgp_vpn_all_neighbor_routes,
+ show_ip_bgp_vpn_all_neighbor_routes_cmd,
+ "show [ip] bgp <vpnv4|vpnv6> all neighbors A.B.C.D routes [json]",
+ SHOW_STR
+ IP_STR
+ BGP_STR
+ BGP_VPNVX_HELP_STR
+ "Display information about all VPNv4/VPNv6 NLRIs\n"
+ "Detailed information on TCP and BGP neighbor connections\n"
+ "Neighbor to display information about\n"
+ "Display routes learned from neighbor\n"
+ JSON_STR)
+{
+ int idx_ipv4 = 6;
+ union sockunion su;
+ struct peer *peer;
+ int ret;
+ bool uj = use_json(argc, argv);
+ afi_t afi;
+ int idx = 0;
+
+ if (argv_find_and_parse_vpnvx(argv, argc, &idx, &afi)) {
+ ret = str2sockunion(argv[idx_ipv4]->arg, &su);
+ if (ret < 0) {
+ if (uj) {
+ json_object *json_no = NULL;
+ json_no = json_object_new_object();
+ json_object_string_add(json_no, "warning",
+ "Malformed address");
+ vty_out(vty, "%s\n",
+ json_object_to_json_string(json_no));
+ json_object_free(json_no);
+ } else
+ vty_out(vty, "Malformed address: %s\n",
+ argv[idx_ipv4]->arg);
+ return CMD_WARNING;
+ }
+
+ peer = peer_lookup(NULL, &su);
+ if (!peer || !peer->afc[afi][SAFI_MPLS_VPN]) {
+ if (uj) {
+ 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;
+ }
+
+ return bgp_show_mpls_vpn(vty, afi, NULL, bgp_show_type_neighbor,
+ &su, 0, uj);
+ }
+ return CMD_SUCCESS;
+}
+
+DEFUN (show_ip_bgp_vpn_rd_neighbor_routes,
+ show_ip_bgp_vpn_rd_neighbor_routes_cmd,
+ "show [ip] bgp <vpnv4|vpnv6> rd <ASN:NN_OR_IP-ADDRESS:NN|all> neighbors A.B.C.D routes [json]",
+ SHOW_STR
+ IP_STR
+ BGP_STR
+ BGP_VPNVX_HELP_STR
+ "Display information for a route distinguisher\n"
+ "VPN Route Distinguisher\n"
+ "All VPN Route Distinguishers\n"
+ "Detailed information on TCP and BGP neighbor connections\n"
+ "Neighbor to display information about\n"
+ "Display routes learned from neighbor\n"
+ JSON_STR)
+{
+ int idx_ext_community = 5;
+ int idx_ipv4 = 7;
+ int ret;
+ union sockunion su;
+ struct peer *peer;
+ struct prefix_rd prd;
+ bool prefix_rd_all = false;
+ bool uj = use_json(argc, argv);
+ afi_t afi;
+ int idx = 0;
+
+ if (argv_find_and_parse_vpnvx(argv, argc, &idx, &afi)) {
+ if (!strcmp(argv[idx_ext_community]->arg, "all"))
+ prefix_rd_all = true;
+ else {
+ ret = str2prefix_rd(argv[idx_ext_community]->arg, &prd);
+ if (!ret) {
+ if (uj) {
+ json_object *json_no = NULL;
+ json_no = json_object_new_object();
+ json_object_string_add(
+ json_no, "warning",
+ "Malformed Route Distinguisher");
+ vty_out(vty, "%s\n",
+ json_object_to_json_string(
+ json_no));
+ json_object_free(json_no);
+ } else
+ vty_out(vty,
+ "%% Malformed Route Distinguisher\n");
+ return CMD_WARNING;
+ }
+ }
+
+ ret = str2sockunion(argv[idx_ipv4]->arg, &su);
+ if (ret < 0) {
+ if (uj) {
+ json_object *json_no = NULL;
+ json_no = json_object_new_object();
+ json_object_string_add(json_no, "warning",
+ "Malformed address");
+ vty_out(vty, "%s\n",
+ json_object_to_json_string(json_no));
+ json_object_free(json_no);
+ } else
+ vty_out(vty, "Malformed address: %s\n",
+ argv[idx_ext_community]->arg);
+ return CMD_WARNING;
+ }
+
+ peer = peer_lookup(NULL, &su);
+ if (!peer || !peer->afc[afi][SAFI_MPLS_VPN]) {
+ if (uj) {
+ 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;
+ }
+
+ if (prefix_rd_all)
+ return bgp_show_mpls_vpn(vty, afi, NULL,
+ bgp_show_type_neighbor, &su, 0,
+ uj);
+ else
+ return bgp_show_mpls_vpn(vty, afi, &prd,
+ bgp_show_type_neighbor, &su, 0,
+ uj);
+ }
+ return CMD_SUCCESS;
+}
+
+DEFUN (show_ip_bgp_vpn_all_neighbor_advertised_routes,
+ show_ip_bgp_vpn_all_neighbor_advertised_routes_cmd,
+ "show [ip] bgp <vpnv4|vpnv6> all neighbors A.B.C.D advertised-routes [json]",
+ SHOW_STR
+ IP_STR
+ BGP_STR
+ BGP_VPNVX_HELP_STR
+ "Display information about all VPNv4/VPNv6 NLRIs\n"
+ "Detailed information on TCP and BGP neighbor connections\n"
+ "Neighbor to display information about\n"
+ "Display the routes advertised to a BGP neighbor\n"
+ JSON_STR)
+{
+ int idx_ipv4 = 6;
+ int ret;
+ struct peer *peer;
+ union sockunion su;
+ bool uj = use_json(argc, argv);
+ afi_t afi;
+ int idx = 0;
+
+ if (argv_find_and_parse_vpnvx(argv, argc, &idx, &afi)) {
+ ret = str2sockunion(argv[idx_ipv4]->arg, &su);
+ if (ret < 0) {
+ if (uj) {
+ json_object *json_no = NULL;
+ json_no = json_object_new_object();
+ json_object_string_add(json_no, "warning",
+ "Malformed address");
+ vty_out(vty, "%s\n",
+ json_object_to_json_string(json_no));
+ json_object_free(json_no);
+ } else
+ vty_out(vty, "Malformed address: %s\n",
+ argv[idx_ipv4]->arg);
+ return CMD_WARNING;
+ }
+ peer = peer_lookup(NULL, &su);
+ if (!peer || !peer->afc[afi][SAFI_MPLS_VPN]) {
+ if (uj) {
+ 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;
+ }
+ return show_adj_route_vpn(vty, peer, NULL, AFI_IP,
+ SAFI_MPLS_VPN, uj);
+ }
+ return CMD_SUCCESS;
+}
+
+DEFUN (show_ip_bgp_vpn_rd_neighbor_advertised_routes,
+ show_ip_bgp_vpn_rd_neighbor_advertised_routes_cmd,
+ "show [ip] bgp <vpnv4|vpnv6> rd <ASN:NN_OR_IP-ADDRESS:NN|all> neighbors A.B.C.D advertised-routes [json]",
+ SHOW_STR
+ IP_STR
+ BGP_STR
+ BGP_VPNVX_HELP_STR
+ "Display information for a route distinguisher\n"
+ "VPN Route Distinguisher\n"
+ "All VPN Route Distinguishers\n"
+ "Detailed information on TCP and BGP neighbor connections\n"
+ "Neighbor to display information about\n"
+ "Display the routes advertised to a BGP neighbor\n"
+ JSON_STR)
+{
+ int idx_ext_community = 5;
+ int idx_ipv4 = 7;
+ int ret;
+ struct peer *peer;
+ struct prefix_rd prd;
+ union sockunion su;
+ bool uj = use_json(argc, argv);
+ afi_t afi;
+ int idx = 0;
+
+ if (argv_find_and_parse_vpnvx(argv, argc, &idx, &afi)) {
+ ret = str2sockunion(argv[idx_ipv4]->arg, &su);
+ if (ret < 0) {
+ if (uj) {
+ json_object *json_no = NULL;
+ json_no = json_object_new_object();
+ json_object_string_add(json_no, "warning",
+ "Malformed address");
+ vty_out(vty, "%s\n",
+ json_object_to_json_string(json_no));
+ json_object_free(json_no);
+ } else
+ vty_out(vty, "Malformed address: %s\n",
+ argv[idx_ext_community]->arg);
+ return CMD_WARNING;
+ }
+ peer = peer_lookup(NULL, &su);
+ if (!peer || !peer->afc[afi][SAFI_MPLS_VPN]) {
+ if (uj) {
+ 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;
+ }
+
+ if (!strcmp(argv[idx_ext_community]->arg, "all"))
+ return show_adj_route_vpn(vty, peer, NULL, AFI_IP,
+ SAFI_MPLS_VPN, uj);
+ ret = str2prefix_rd(argv[idx_ext_community]->arg, &prd);
+ if (!ret) {
+ if (uj) {
+ json_object *json_no = NULL;
+ json_no = json_object_new_object();
+ json_object_string_add(
+ json_no, "warning",
+ "Malformed Route Distinguisher");
+ vty_out(vty, "%s\n",
+ json_object_to_json_string(json_no));
+ json_object_free(json_no);
+ } else
+ vty_out(vty,
+ "%% Malformed Route Distinguisher\n");
+ return CMD_WARNING;
+ }
+
+ return show_adj_route_vpn(vty, peer, &prd, AFI_IP,
+ SAFI_MPLS_VPN, uj);
+ }
+ return CMD_SUCCESS;
+}
+#endif /* KEEP_OLD_VPN_COMMANDS */
+
+void bgp_mplsvpn_init(void)
+{
+ install_element(BGP_VPNV4_NODE, &vpnv4_network_cmd);
+ install_element(BGP_VPNV4_NODE, &vpnv4_network_route_map_cmd);
+ install_element(BGP_VPNV4_NODE, &no_vpnv4_network_cmd);
+
+ install_element(BGP_VPNV6_NODE, &vpnv6_network_cmd);
+ install_element(BGP_VPNV6_NODE, &no_vpnv6_network_cmd);
+
+ install_element(VIEW_NODE, &show_bgp_ip_vpn_all_rd_cmd);
+ install_element(VIEW_NODE, &show_bgp_ip_vpn_rd_cmd);
+#ifdef KEEP_OLD_VPN_COMMANDS
+ install_element(VIEW_NODE, &show_ip_bgp_vpn_rd_cmd);
+ install_element(VIEW_NODE, &show_ip_bgp_vpn_all_cmd);
+ install_element(VIEW_NODE, &show_ip_bgp_vpn_all_tags_cmd);
+ install_element(VIEW_NODE, &show_ip_bgp_vpn_rd_tags_cmd);
+ install_element(VIEW_NODE, &show_ip_bgp_vpn_all_neighbor_routes_cmd);
+ install_element(VIEW_NODE, &show_ip_bgp_vpn_rd_neighbor_routes_cmd);
+ install_element(VIEW_NODE,
+ &show_ip_bgp_vpn_all_neighbor_advertised_routes_cmd);
+ install_element(VIEW_NODE,
+ &show_ip_bgp_vpn_rd_neighbor_advertised_routes_cmd);
+#endif /* KEEP_OLD_VPN_COMMANDS */
+}
+
+vrf_id_t get_first_vrf_for_redirect_with_rt(struct ecommunity *eckey)
+{
+ struct listnode *mnode, *mnnode;
+ struct bgp *bgp;
+ afi_t afi = AFI_IP;
+
+ if (eckey->unit_size == IPV6_ECOMMUNITY_SIZE)
+ afi = AFI_IP6;
+
+ for (ALL_LIST_ELEMENTS(bm->bgp, mnode, mnnode, bgp)) {
+ struct ecommunity *ec;
+
+ if (bgp->inst_type != BGP_INSTANCE_TYPE_VRF)
+ continue;
+
+ ec = bgp->vpn_policy[afi].import_redirect_rtlist;
+
+ if (ec && eckey->unit_size != ec->unit_size)
+ continue;
+
+ if (ecommunity_include(ec, eckey))
+ return bgp->vrf_id;
+ }
+ return VRF_UNKNOWN;
+}
+
+/*
+ * The purpose of this function is to process leaks that were deferred
+ * from earlier per-vrf configuration due to not-yet-existing default
+ * vrf, in other words, configuration such as:
+ *
+ * router bgp MMM vrf FOO
+ * address-family ipv4 unicast
+ * rd vpn export 1:1
+ * exit-address-family
+ *
+ * router bgp NNN
+ * ...
+ *
+ * This function gets called when the default instance ("router bgp NNN")
+ * is created.
+ */
+void vpn_leak_postchange_all(void)
+{
+ struct listnode *next;
+ struct bgp *bgp;
+ struct bgp *bgp_default = bgp_get_default();
+
+ assert(bgp_default);
+
+ /* First, do any exporting from VRFs to the single VPN RIB */
+ for (ALL_LIST_ELEMENTS_RO(bm->bgp, next, bgp)) {
+
+ if (bgp->inst_type != BGP_INSTANCE_TYPE_VRF)
+ continue;
+
+ vpn_leak_postchange(
+ BGP_VPN_POLICY_DIR_TOVPN,
+ AFI_IP,
+ bgp_default,
+ bgp);
+
+ vpn_leak_postchange(
+ BGP_VPN_POLICY_DIR_TOVPN,
+ AFI_IP6,
+ bgp_default,
+ bgp);
+ }
+
+ /* Now, do any importing to VRFs from the single VPN RIB */
+ for (ALL_LIST_ELEMENTS_RO(bm->bgp, next, bgp)) {
+
+ if (bgp->inst_type != BGP_INSTANCE_TYPE_VRF)
+ continue;
+
+ vpn_leak_postchange(
+ BGP_VPN_POLICY_DIR_FROMVPN,
+ AFI_IP,
+ bgp_default,
+ bgp);
+
+ vpn_leak_postchange(
+ BGP_VPN_POLICY_DIR_FROMVPN,
+ AFI_IP6,
+ bgp_default,
+ bgp);
+ }
+}
+
+/* When a bgp vrf instance is unconfigured, remove its routes
+ * from the VPN table and this vrf could be importing routes from other
+ * bgp vrf instnaces, unimport them.
+ * VRF X and VRF Y are exporting routes to each other.
+ * When VRF X is deleted, unimport its routes from all target vrfs,
+ * also VRF Y should unimport its routes from VRF X table.
+ * This will ensure VPN table is cleaned up appropriately.
+ */
+void bgp_vpn_leak_unimport(struct bgp *from_bgp)
+{
+ struct bgp *to_bgp;
+ const char *tmp_name;
+ char *vname;
+ struct listnode *node, *next;
+ safi_t safi = SAFI_UNICAST;
+ afi_t afi;
+ bool is_vrf_leak_bind;
+ int debug;
+
+ if (from_bgp->inst_type != BGP_INSTANCE_TYPE_VRF)
+ return;
+
+ debug = (BGP_DEBUG(vpn, VPN_LEAK_TO_VRF) |
+ BGP_DEBUG(vpn, VPN_LEAK_FROM_VRF));
+
+ tmp_name = from_bgp->name ? from_bgp->name : VRF_DEFAULT_NAME;
+
+ for (afi = 0; afi < AFI_MAX; ++afi) {
+ /* vrf leak is for IPv4 and IPv6 Unicast only */
+ if (afi != AFI_IP && afi != AFI_IP6)
+ continue;
+
+ for (ALL_LIST_ELEMENTS_RO(bm->bgp, next, to_bgp)) {
+ if (from_bgp == to_bgp)
+ continue;
+
+ /* Unimport and remove source vrf from the
+ * other vrfs import list.
+ */
+ struct vpn_policy *to_vpolicy;
+
+ is_vrf_leak_bind = false;
+ to_vpolicy = &(to_bgp->vpn_policy[afi]);
+ for (ALL_LIST_ELEMENTS_RO(to_vpolicy->import_vrf, node,
+ vname)) {
+ if (strcmp(vname, tmp_name) == 0) {
+ is_vrf_leak_bind = true;
+ break;
+ }
+ }
+ /* skip this bgp instance as there is no leak to this
+ * vrf instance.
+ */
+ if (!is_vrf_leak_bind)
+ continue;
+
+ if (debug)
+ zlog_debug("%s: unimport routes from %s to_bgp %s afi %s import vrfs count %u",
+ __func__, from_bgp->name_pretty,
+ to_bgp->name_pretty, afi2str(afi),
+ to_vpolicy->import_vrf->count);
+
+ vrf_unimport_from_vrf(to_bgp, from_bgp, afi, safi);
+
+ /* readd vrf name as unimport removes import vrf name
+ * from the destination vrf's import list where the
+ * `import vrf` configuration still exist.
+ */
+ vname = XSTRDUP(MTYPE_TMP, tmp_name);
+ listnode_add(to_bgp->vpn_policy[afi].import_vrf,
+ vname);
+ SET_FLAG(to_bgp->af_flags[afi][safi],
+ BGP_CONFIG_VRF_TO_VRF_IMPORT);
+
+ /* If to_bgp exports its routes to the bgp vrf
+ * which is being deleted, un-import the
+ * to_bgp routes from VPN.
+ */
+ for (ALL_LIST_ELEMENTS_RO(to_bgp->vpn_policy[afi]
+ .export_vrf, node,
+ vname)) {
+ if (strcmp(vname, tmp_name) == 0) {
+ vrf_unimport_from_vrf(from_bgp, to_bgp,
+ afi, safi);
+ break;
+ }
+ }
+ }
+ }
+ return;
+}
+
+/* When a router bgp is configured, there could be a bgp vrf
+ * instance importing routes from this newly configured
+ * bgp vrf instance. Export routes from configured
+ * bgp vrf to VPN.
+ * VRF Y has import from bgp vrf x,
+ * when a bgp vrf x instance is created, export its routes
+ * to VRF Y instance.
+ */
+void bgp_vpn_leak_export(struct bgp *from_bgp)
+{
+ afi_t afi;
+ const char *export_name;
+ char *vname;
+ struct listnode *node, *next;
+ struct ecommunity *ecom;
+ enum vpn_policy_direction idir, edir;
+ safi_t safi = SAFI_UNICAST;
+ struct bgp *to_bgp;
+ int debug;
+
+ debug = (BGP_DEBUG(vpn, VPN_LEAK_TO_VRF) |
+ BGP_DEBUG(vpn, VPN_LEAK_FROM_VRF));
+
+ idir = BGP_VPN_POLICY_DIR_FROMVPN;
+ edir = BGP_VPN_POLICY_DIR_TOVPN;
+
+ export_name = from_bgp->name ? from_bgp->name : VRF_DEFAULT_NAME;
+
+ for (afi = 0; afi < AFI_MAX; ++afi) {
+ /* vrf leak is for IPv4 and IPv6 Unicast only */
+ if (afi != AFI_IP && afi != AFI_IP6)
+ continue;
+
+ for (ALL_LIST_ELEMENTS_RO(bm->bgp, next, to_bgp)) {
+ if (from_bgp == to_bgp)
+ continue;
+
+ /* bgp instance has import list, check to see if newly
+ * configured bgp instance is the list.
+ */
+ struct vpn_policy *to_vpolicy;
+
+ to_vpolicy = &(to_bgp->vpn_policy[afi]);
+ for (ALL_LIST_ELEMENTS_RO(to_vpolicy->import_vrf,
+ node, vname)) {
+ if (strcmp(vname, export_name) != 0)
+ continue;
+
+ if (debug)
+ zlog_debug("%s: found from_bgp %s in to_bgp %s import list, import routes.",
+ __func__,
+ export_name, to_bgp->name_pretty);
+
+ ecom = from_bgp->vpn_policy[afi].rtlist[edir];
+ /* remove import rt, it will be readded
+ * as part of import from vrf.
+ */
+ if (ecom)
+ ecommunity_del_val(
+ to_vpolicy->rtlist[idir],
+ (struct ecommunity_val *)
+ ecom->val);
+ vrf_import_from_vrf(to_bgp, from_bgp,
+ afi, safi);
+ break;
+
+ }
+ }
+ }
+}
diff --git a/bgpd/bgp_mplsvpn.h b/bgpd/bgp_mplsvpn.h
new file mode 100644
index 0000000..c5cc7d4
--- /dev/null
+++ b/bgpd/bgp_mplsvpn.h
@@ -0,0 +1,311 @@
+/* MPLS-VPN
+ * Copyright (C) 2000 Kunihiro Ishiguro <kunihiro@zebra.org>
+ *
+ * This file is part of GxNU Zebra.
+ *
+ * GNU Zebra is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License as published by the
+ * Free Software Foundation; either version 2, or (at your option) any
+ * later version.
+ *
+ * GNU Zebra is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program; see the file COPYING; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
+ */
+
+#ifndef _QUAGGA_BGP_MPLSVPN_H
+#define _QUAGGA_BGP_MPLSVPN_H
+
+#include "bgpd/bgp_attr.h"
+#include "bgpd/bgp_route.h"
+#include "bgpd/bgp_rd.h"
+#include "bgpd/bgp_zebra.h"
+#include "bgpd/bgp_vty.h"
+
+#define MPLS_LABEL_IS_SPECIAL(label) ((label) <= MPLS_LABEL_EXTENSION)
+#define MPLS_LABEL_IS_NULL(label) \
+ ((label) == MPLS_LABEL_IPV4_EXPLICIT_NULL \
+ || (label) == MPLS_LABEL_IPV6_EXPLICIT_NULL \
+ || (label) == MPLS_LABEL_IMPLICIT_NULL)
+
+#define BGP_VPNVX_HELP_STR BGP_AF_STR BGP_AF_STR
+
+#define V4_HEADER \
+ " Network Next Hop Metric LocPrf Weight Path\n"
+#define V4_HEADER_TAG " Network Next Hop In tag/Out tag\n"
+#define V4_HEADER_OVERLAY \
+ " Network Next Hop EthTag Overlay Index RouterMac\n"
+
+extern void bgp_mplsvpn_init(void);
+extern int bgp_nlri_parse_vpn(struct peer *, struct attr *, struct bgp_nlri *);
+extern uint32_t decode_label(mpls_label_t *);
+extern void encode_label(mpls_label_t, mpls_label_t *);
+
+extern int argv_find_and_parse_vpnvx(struct cmd_token **argv, int argc,
+ int *index, afi_t *afi);
+extern int bgp_show_mpls_vpn(struct vty *vty, afi_t afi, struct prefix_rd *prd,
+ enum bgp_show_type type, void *output_arg,
+ int tags, bool use_json);
+
+extern void vpn_leak_from_vrf_update(struct bgp *to_bgp, struct bgp *from_bgp,
+ struct bgp_path_info *path_vrf);
+
+extern void vpn_leak_from_vrf_withdraw(struct bgp *to_bgp, struct bgp *from_bgp,
+ struct bgp_path_info *path_vrf);
+
+extern void vpn_leak_from_vrf_withdraw_all(struct bgp *to_bgp,
+ struct bgp *from_bgp, afi_t afi);
+
+extern void vpn_leak_from_vrf_update_all(struct bgp *to_bgp,
+ struct bgp *from_bgp, afi_t afi);
+
+extern void vpn_leak_to_vrf_withdraw_all(struct bgp *to_bgp, afi_t afi);
+
+extern void vpn_leak_to_vrf_update_all(struct bgp *to_bgp, struct bgp *from_bgp,
+ afi_t afi);
+
+extern bool vpn_leak_to_vrf_update(struct bgp *from_bgp,
+ struct bgp_path_info *path_vpn);
+
+extern void vpn_leak_to_vrf_withdraw(struct bgp *from_bgp,
+ struct bgp_path_info *path_vpn);
+
+extern void vpn_leak_zebra_vrf_label_update(struct bgp *bgp, afi_t afi);
+extern void vpn_leak_zebra_vrf_label_withdraw(struct bgp *bgp, afi_t afi);
+extern void vpn_leak_zebra_vrf_sid_update(struct bgp *bgp, afi_t afi);
+extern void vpn_leak_zebra_vrf_sid_withdraw(struct bgp *bgp, afi_t afi);
+extern int vpn_leak_label_callback(mpls_label_t label, void *lblid, bool alloc);
+extern void ensure_vrf_tovpn_sid(struct bgp *vpn, struct bgp *vrf, afi_t afi);
+extern void transpose_sid(struct in6_addr *sid, uint32_t label, uint8_t offset,
+ uint8_t size);
+extern void vrf_import_from_vrf(struct bgp *to_bgp, struct bgp *from_bgp,
+ afi_t afi, safi_t safi);
+void vrf_unimport_from_vrf(struct bgp *to_bgp, struct bgp *from_bgp,
+ afi_t afi, safi_t safi);
+
+static inline bool is_bgp_vrf_mplsvpn(struct bgp *bgp)
+{
+ afi_t afi;
+
+ if (bgp->inst_type == BGP_INSTANCE_TYPE_VRF)
+ for (afi = 0; afi < AFI_MAX; ++afi) {
+ if (CHECK_FLAG(bgp->af_flags[afi][SAFI_UNICAST],
+ BGP_CONFIG_VRF_TO_MPLSVPN_EXPORT)
+ || CHECK_FLAG(bgp->af_flags[afi][SAFI_UNICAST],
+ BGP_CONFIG_MPLSVPN_TO_VRF_IMPORT))
+ return true;
+ }
+ return false;
+}
+
+static inline int vpn_leak_to_vpn_active(struct bgp *bgp_vrf, afi_t afi,
+ const char **pmsg)
+{
+ if (bgp_vrf->inst_type != BGP_INSTANCE_TYPE_VRF
+ && bgp_vrf->inst_type != BGP_INSTANCE_TYPE_DEFAULT) {
+
+ if (pmsg)
+ *pmsg = "source bgp instance neither vrf nor default";
+ return 0;
+ }
+
+ /* Is vrf configured to export to vpn? */
+ if (!CHECK_FLAG(bgp_vrf->af_flags[afi][SAFI_UNICAST],
+ BGP_CONFIG_VRF_TO_MPLSVPN_EXPORT)
+ && !CHECK_FLAG(bgp_vrf->af_flags[afi][SAFI_UNICAST],
+ BGP_CONFIG_VRF_TO_VRF_EXPORT)) {
+ if (pmsg)
+ *pmsg = "export not set";
+ return 0;
+ }
+
+ /* Is there an RT list set? */
+ if (!bgp_vrf->vpn_policy[afi].rtlist[BGP_VPN_POLICY_DIR_TOVPN]) {
+ if (pmsg)
+ *pmsg = "rtlist tovpn not defined";
+ return 0;
+ }
+
+ /* Is there an RD set? */
+ if (!CHECK_FLAG(bgp_vrf->vpn_policy[afi].flags,
+ BGP_VPN_POLICY_TOVPN_RD_SET)) {
+ if (pmsg)
+ *pmsg = "rd not defined";
+ return 0;
+ }
+
+ /* Is a route-map specified, but not defined? */
+ if (bgp_vrf->vpn_policy[afi].rmap_name[BGP_VPN_POLICY_DIR_TOVPN] &&
+ !bgp_vrf->vpn_policy[afi].rmap[BGP_VPN_POLICY_DIR_TOVPN]) {
+ if (pmsg)
+ *pmsg = "route-map tovpn named but not defined";
+ return 0;
+ }
+
+ /* Is there an "auto" export label that isn't allocated yet? */
+ if (CHECK_FLAG(bgp_vrf->vpn_policy[afi].flags,
+ BGP_VPN_POLICY_TOVPN_LABEL_AUTO) &&
+ (bgp_vrf->vpn_policy[afi].tovpn_label == MPLS_LABEL_NONE)) {
+
+ if (pmsg)
+ *pmsg = "auto label not allocated";
+ return 0;
+ }
+
+ return 1;
+}
+
+static inline int vpn_leak_from_vpn_active(struct bgp *bgp_vrf, afi_t afi,
+ const char **pmsg)
+{
+ if (bgp_vrf->inst_type != BGP_INSTANCE_TYPE_VRF
+ && bgp_vrf->inst_type != BGP_INSTANCE_TYPE_DEFAULT) {
+
+ if (pmsg)
+ *pmsg = "destination bgp instance neither vrf nor default";
+ return 0;
+ }
+
+ if (bgp_vrf->vrf_id == VRF_UNKNOWN) {
+ if (pmsg)
+ *pmsg = "destination bgp instance vrf is VRF_UNKNOWN";
+ return 0;
+ }
+
+ /* Is vrf configured to import from vpn? */
+ if (!CHECK_FLAG(bgp_vrf->af_flags[afi][SAFI_UNICAST],
+ BGP_CONFIG_MPLSVPN_TO_VRF_IMPORT)
+ && !CHECK_FLAG(bgp_vrf->af_flags[afi][SAFI_UNICAST],
+ BGP_CONFIG_VRF_TO_VRF_IMPORT)) {
+ if (pmsg)
+ *pmsg = "import not set";
+ return 0;
+ }
+
+ /* Is there an RT list set? */
+ if (!bgp_vrf->vpn_policy[afi].rtlist[BGP_VPN_POLICY_DIR_FROMVPN]) {
+ if (pmsg)
+ *pmsg = "rtlist fromvpn not defined";
+ return 0;
+ }
+
+ /* Is a route-map specified, but not defined? */
+ if (bgp_vrf->vpn_policy[afi].rmap_name[BGP_VPN_POLICY_DIR_FROMVPN] &&
+ !bgp_vrf->vpn_policy[afi].rmap[BGP_VPN_POLICY_DIR_FROMVPN]) {
+ if (pmsg)
+ *pmsg = "route-map fromvpn named but not defined";
+ return 0;
+ }
+ return 1;
+}
+
+static inline void vpn_leak_prechange(enum vpn_policy_direction direction,
+ afi_t afi, struct bgp *bgp_vpn,
+ struct bgp *bgp_vrf)
+{
+ /* Detect when default bgp instance is not (yet) defined by config */
+ if (!bgp_vpn)
+ return;
+
+ if ((direction == BGP_VPN_POLICY_DIR_FROMVPN) &&
+ vpn_leak_from_vpn_active(bgp_vrf, afi, NULL)) {
+
+ vpn_leak_to_vrf_withdraw_all(bgp_vrf, afi);
+ }
+ if ((direction == BGP_VPN_POLICY_DIR_TOVPN) &&
+ vpn_leak_to_vpn_active(bgp_vrf, afi, NULL)) {
+
+ vpn_leak_from_vrf_withdraw_all(bgp_vpn, bgp_vrf, afi);
+ }
+}
+
+static inline void vpn_leak_postchange(enum vpn_policy_direction direction,
+ afi_t afi, struct bgp *bgp_vpn,
+ struct bgp *bgp_vrf)
+{
+ /* Detect when default bgp instance is not (yet) defined by config */
+ if (!bgp_vpn)
+ return;
+
+ if (direction == BGP_VPN_POLICY_DIR_FROMVPN) {
+ /* trigger a flush to re-sync with ADJ-RIB-in */
+ if (!CHECK_FLAG(bgp_vpn->af_flags[afi][SAFI_MPLS_VPN],
+ BGP_VPNVX_RETAIN_ROUTE_TARGET_ALL))
+ bgp_clear_soft_in(bgp_vpn, afi, SAFI_MPLS_VPN);
+ else
+ vpn_leak_to_vrf_update_all(bgp_vrf, bgp_vpn, afi);
+ }
+ if (direction == BGP_VPN_POLICY_DIR_TOVPN) {
+
+ if (bgp_vrf->vpn_policy[afi].tovpn_label !=
+ bgp_vrf->vpn_policy[afi]
+ .tovpn_zebra_vrf_label_last_sent) {
+ vpn_leak_zebra_vrf_label_update(bgp_vrf, afi);
+ }
+
+ if (!bgp_vrf->vpn_policy[afi].tovpn_sid)
+ ensure_vrf_tovpn_sid(bgp_vpn, bgp_vrf, afi);
+
+ if (!bgp_vrf->vpn_policy[afi].tovpn_sid
+ && bgp_vrf->vpn_policy[afi].tovpn_zebra_vrf_sid_last_sent)
+ vpn_leak_zebra_vrf_sid_withdraw(bgp_vrf, afi);
+
+ if (sid_diff(bgp_vrf->vpn_policy[afi].tovpn_sid,
+ bgp_vrf->vpn_policy[afi]
+ .tovpn_zebra_vrf_sid_last_sent)) {
+ vpn_leak_zebra_vrf_sid_update(bgp_vrf, afi);
+ }
+
+ vpn_leak_from_vrf_update_all(bgp_vpn, bgp_vrf, afi);
+ }
+}
+
+/* Flag if the route is injectable into VPN. This would be either a
+ * non-imported route or a non-VPN imported route.
+ */
+static inline bool is_route_injectable_into_vpn(struct bgp_path_info *pi)
+{
+ struct bgp_path_info *parent_pi;
+ struct bgp_table *table;
+ struct bgp_dest *dest;
+
+ if (pi->sub_type != BGP_ROUTE_IMPORTED ||
+ !pi->extra ||
+ !pi->extra->parent)
+ return true;
+
+ parent_pi = (struct bgp_path_info *)pi->extra->parent;
+ dest = parent_pi->net;
+ if (!dest)
+ return true;
+ table = bgp_dest_table(dest);
+ if (table &&
+ (table->afi == AFI_IP || table->afi == AFI_IP6) &&
+ table->safi == SAFI_MPLS_VPN)
+ return false;
+ return true;
+}
+
+/* Flag if the route path's family is VPN. */
+static inline bool is_pi_family_vpn(struct bgp_path_info *pi)
+{
+ return (is_pi_family_matching(pi, AFI_IP, SAFI_MPLS_VPN) ||
+ is_pi_family_matching(pi, AFI_IP6, SAFI_MPLS_VPN));
+}
+
+extern void vpn_policy_routemap_event(const char *rmap_name);
+
+extern vrf_id_t get_first_vrf_for_redirect_with_rt(struct ecommunity *eckey);
+
+extern void vpn_leak_postchange_all(void);
+extern void vpn_handle_router_id_update(struct bgp *bgp, bool withdraw,
+ bool is_config);
+extern void bgp_vpn_leak_unimport(struct bgp *from_bgp);
+extern void bgp_vpn_leak_export(struct bgp *from_bgp);
+
+#endif /* _QUAGGA_BGP_MPLSVPN_H */
diff --git a/bgpd/bgp_mplsvpn_snmp.c b/bgpd/bgp_mplsvpn_snmp.c
new file mode 100644
index 0000000..7a2f618
--- /dev/null
+++ b/bgpd/bgp_mplsvpn_snmp.c
@@ -0,0 +1,1693 @@
+/* MPLS/BGP L3VPN MIB
+ * Copyright (C) 2020 Volta Networks Inc
+ *
+ * This file is part of FRR.
+ *
+ * FRRouting is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License as published by the
+ * Free Software Foundation; either version 2, or (at your option) any
+ * later version.
+ *
+ * FRRouting is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program; see the file COPYING; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
+ */
+
+#include <zebra.h>
+
+#include <net-snmp/net-snmp-config.h>
+#include <net-snmp/net-snmp-includes.h>
+
+#include "if.h"
+#include "log.h"
+#include "prefix.h"
+#include "command.h"
+#include "thread.h"
+#include "smux.h"
+#include "filter.h"
+#include "hook.h"
+#include "libfrr.h"
+#include "lib/version.h"
+
+#include "bgpd/bgpd.h"
+#include "bgpd/bgp_route.h"
+#include "bgpd/bgp_attr.h"
+#include "bgpd/bgp_ecommunity.h"
+#include "bgpd/bgp_mplsvpn.h"
+#include "bgpd/bgp_mplsvpn_snmp.h"
+
+#define BGP_mplsvpn_notif_enable_true 1
+#define BGP_mplsvpn_notif_enable_false 2
+
+/* MPLSL3VPN MIB described in RFC4382 */
+#define MPLSL3VPNMIB 1, 3, 6, 1, 2, 1, 10, 166, 11
+
+/* MPLSL3VPN Scalars */
+#define MPLSL3VPNCONFIGUREDVRFS 1
+#define MPLSL3VPNACTIVEVRFS 2
+#define MPLSL3VPNCONNECTEDINTERFACES 3
+#define MPLSL3VPNNOTIFICATIONENABLE 4
+#define MPLSL3VPNCONFMAXPOSSRTS 5
+#define MPLSL3VPNVRFCONFRTEMXTHRSHTIME 6
+#define MPLSL3VPNILLLBLRCVTHRSH 7
+
+/* MPLSL3VPN IFConf Table */
+#define MPLSL3VPNIFVPNCLASSIFICATION 1
+#define MPLSL3VPNIFCONFSTORAGETYPE 2
+#define MPLSL3VPNIFCONFROWSTATUS 3
+
+/* MPLSL3VPN VRF Table */
+#define MPLSL3VPNVRFVPNID 1
+#define MPLSL3VPNVRFDESC 2
+#define MPLSL3VPNVRFRD 3
+#define MPLSL3VPNVRFCREATIONTIME 4
+#define MPLSL3VPNVRFOPERSTATUS 5
+#define MPLSL3VPNVRFACTIVEINTERFACES 6
+#define MPLSL3VPNVRFASSOCIATEDINTERFACES 7
+#define MPLSL3VPNVRFCONFMIDRTETHRESH 8
+#define MPLSL3VPNVRFCONFHIGHRTETHRSH 9
+#define MPLSL3VPNVRFCONFMAXROUTES 10
+#define MPLSL3VPNVRFCONFLASTCHANGED 11
+#define MPLSL3VPNVRFCONFROWSTATUS 12
+#define MPLSL3VPNVRFCONFADMINSTATUS 13
+#define MPLSL3VPNVRFCONFSTORAGETYPE 14
+
+/* MPLSL3VPN RT Table */
+#define MPLSL3VPNVRFRT 1
+#define MPLSL3VPNVRFRTDESCR 2
+#define MPLSL3VPNVRFRTROWSTATUS 3
+#define MPLSL3VPNVRFRTSTORAGETYPE 4
+
+/* MPLSL3VPN PERF Table */
+#define MPLSL3VPNVRFPERFROUTESADDED 1
+#define MPLSL3VPNVRFPERFROUTESDELETED 2
+#define MPLSL3VPNVRFPERFCURRNUMROUTES 3
+
+/* MPLSL3VPN RTE Table */
+#define MPLSL3VPNVRFRTEINETCIDRDESTTYPE 1
+#define MPLSL3VPNVRFRTEINETCIDRDEST 2
+#define MPLSL3VPNVRFRTEINETCIDRPFXLEN 3
+#define MPLSL3VPNVRFRTEINETCIDRPOLICY 4
+#define MPLSL3VPNVRFRTEINETCIDRNHOPTYPE 5
+#define MPLSL3VPNVRFRTEINETCIDRNEXTHOP 6
+#define MPLSL3VPNVRFRTEINETCIDRIFINDEX 7
+#define MPLSL3VPNVRFRTEINETCIDRTYPE 8
+#define MPLSL3VPNVRFRTEINETCIDRPROTO 9
+#define MPLSL3VPNVRFRTEINETCIDRAGE 10
+#define MPLSL3VPNVRFRTEINETCIDRNEXTHOPAS 11
+#define MPLSL3VPNVRFRTEINETCIDRMETRIC1 12
+#define MPLSL3VPNVRFRTEINETCIDRMETRIC2 13
+#define MPLSL3VPNVRFRTEINETCIDRMETRIC3 14
+#define MPLSL3VPNVRFRTEINETCIDRMETRIC4 15
+#define MPLSL3VPNVRFRTEINETCIDRMETRIC5 16
+#define MPLSL3VPNVRFRTEINETCIDRXCPOINTER 17
+#define MPLSL3VPNVRFRTEINETCIDRSTATUS 18
+
+/* BGP Trap */
+#define MPLSL3VPNVRFUP 1
+#define MPLSL3VPNDOWN 2
+
+/* SNMP value hack. */
+#define INTEGER ASN_INTEGER
+#define INTEGER32 ASN_INTEGER
+#define COUNTER32 ASN_COUNTER
+#define OCTET_STRING ASN_OCTET_STR
+#define IPADDRESS ASN_IPADDRESS
+#define GAUGE32 ASN_UNSIGNED
+#define TIMETICKS ASN_TIMETICKS
+#define OID ASN_OBJECT_ID
+
+/* Declare static local variables for convenience. */
+SNMP_LOCAL_VARIABLES
+
+#define RT_PREAMBLE_SIZE 20
+
+/* BGP-MPLS-MIB instances */
+static oid mpls_l3vpn_oid[] = {MPLSL3VPNMIB};
+static oid mpls_l3vpn_trap_oid[] = {MPLSL3VPNMIB, 0};
+static char rd_buf[RD_ADDRSTRLEN];
+/* Notifications enabled by default */
+static uint8_t bgp_mplsvpn_notif_enable = SNMP_TRUE;
+static oid mpls_l3vpn_policy_oid[2] = {0, 0};
+static const char *empty_nhop = "";
+char rt_description[VRF_NAMSIZ + RT_PREAMBLE_SIZE];
+
+static uint8_t *mplsL3vpnConfiguredVrfs(struct variable *, oid[], size_t *, int,
+ size_t *, WriteMethod **);
+
+static uint8_t *mplsL3vpnActiveVrfs(struct variable *, oid[], size_t *, int,
+ size_t *, WriteMethod **);
+
+static uint8_t *mplsL3vpnConnectedInterfaces(struct variable *, oid[], size_t *,
+ int, size_t *, WriteMethod **);
+
+static uint8_t *mplsL3vpnNotificationEnable(struct variable *, oid[], size_t *,
+ int, size_t *, WriteMethod **);
+
+static uint8_t *mplsL3vpnVrfConfMaxPossRts(struct variable *, oid[], size_t *,
+ int, size_t *, WriteMethod **);
+
+static uint8_t *mplsL3vpnVrfConfRteMxThrshTime(struct variable *, oid[],
+ size_t *, int, size_t *,
+ WriteMethod **);
+
+static uint8_t *mplsL3vpnIllLblRcvThrsh(struct variable *, oid[], size_t *, int,
+ size_t *, WriteMethod **);
+
+static uint8_t *mplsL3vpnVrfTable(struct variable *, oid[], size_t *, int,
+ size_t *, WriteMethod **);
+
+static uint8_t *mplsL3vpnVrfRtTable(struct variable *, oid[], size_t *, int,
+ size_t *, WriteMethod **);
+
+static uint8_t *mplsL3vpnIfConfTable(struct variable *, oid[], size_t *, int,
+ size_t *, WriteMethod **);
+
+static uint8_t *mplsL3vpnPerfTable(struct variable *, oid[], size_t *, int,
+ size_t *, WriteMethod **);
+
+static uint8_t *mplsL3vpnRteTable(struct variable *, oid[], size_t *, int,
+ size_t *, WriteMethod **);
+
+
+static struct variable mpls_l3vpn_variables[] = {
+ /* BGP version. */
+ {MPLSL3VPNCONFIGUREDVRFS,
+ GAUGE32,
+ RONLY,
+ mplsL3vpnConfiguredVrfs,
+ 3,
+ {1, 1, 1} },
+ {MPLSL3VPNACTIVEVRFS,
+ GAUGE32,
+ RONLY,
+ mplsL3vpnActiveVrfs,
+ 3,
+ {1, 1, 2} },
+ {MPLSL3VPNCONNECTEDINTERFACES,
+ GAUGE32,
+ RONLY,
+ mplsL3vpnConnectedInterfaces,
+ 3,
+ {1, 1, 3} },
+ {MPLSL3VPNNOTIFICATIONENABLE,
+ INTEGER,
+ RWRITE,
+ mplsL3vpnNotificationEnable,
+ 3,
+ {1, 1, 4} },
+ {MPLSL3VPNCONFMAXPOSSRTS,
+ GAUGE32,
+ RONLY,
+ mplsL3vpnVrfConfMaxPossRts,
+ 3,
+ {1, 1, 5} },
+ {MPLSL3VPNVRFCONFRTEMXTHRSHTIME,
+ GAUGE32,
+ RONLY,
+ mplsL3vpnVrfConfRteMxThrshTime,
+ 3,
+ {1, 1, 6} },
+ {MPLSL3VPNILLLBLRCVTHRSH,
+ GAUGE32,
+ RONLY,
+ mplsL3vpnIllLblRcvThrsh,
+ 3,
+ {1, 1, 7} },
+
+ /* Ifconf Table */
+ {MPLSL3VPNIFVPNCLASSIFICATION,
+ INTEGER,
+ RONLY,
+ mplsL3vpnIfConfTable,
+ 5,
+ {1, 2, 1, 1, 2} },
+ {MPLSL3VPNIFCONFSTORAGETYPE,
+ INTEGER,
+ RONLY,
+ mplsL3vpnIfConfTable,
+ 5,
+ {1, 2, 1, 1, 4} },
+ {MPLSL3VPNIFCONFROWSTATUS,
+ INTEGER,
+ RONLY,
+ mplsL3vpnIfConfTable,
+ 5,
+ {1, 2, 1, 1, 5} },
+
+ /* mplsL3VpnVrf Table */
+ {MPLSL3VPNVRFVPNID,
+ OCTET_STRING,
+ RONLY,
+ mplsL3vpnVrfTable,
+ 5,
+ {1, 2, 2, 1, 2} },
+ {MPLSL3VPNVRFDESC,
+ OCTET_STRING,
+ RONLY,
+ mplsL3vpnVrfTable,
+ 5,
+ {1, 2, 2, 1, 3} },
+ {MPLSL3VPNVRFRD,
+ OCTET_STRING,
+ RONLY,
+ mplsL3vpnVrfTable,
+ 5,
+ {1, 2, 2, 1, 4} },
+ {MPLSL3VPNVRFCREATIONTIME,
+ TIMETICKS,
+ RONLY,
+ mplsL3vpnVrfTable,
+ 5,
+ {1, 2, 2, 1, 5} },
+ {MPLSL3VPNVRFOPERSTATUS,
+ INTEGER,
+ RONLY,
+ mplsL3vpnVrfTable,
+ 5,
+ {1, 2, 2, 1, 6} },
+ {MPLSL3VPNVRFACTIVEINTERFACES,
+ GAUGE32,
+ RONLY,
+ mplsL3vpnVrfTable,
+ 5,
+ {1, 2, 2, 1, 7} },
+ {MPLSL3VPNVRFASSOCIATEDINTERFACES,
+ GAUGE32,
+ RONLY,
+ mplsL3vpnVrfTable,
+ 5,
+ {1, 2, 2, 1, 8} },
+ {MPLSL3VPNVRFCONFMIDRTETHRESH,
+ GAUGE32,
+ RONLY,
+ mplsL3vpnVrfTable,
+ 5,
+ {1, 2, 2, 1, 9} },
+ {MPLSL3VPNVRFCONFHIGHRTETHRSH,
+ GAUGE32,
+ RONLY,
+ mplsL3vpnVrfTable,
+ 5,
+ {1, 2, 2, 1, 10} },
+ {MPLSL3VPNVRFCONFMAXROUTES,
+ GAUGE32,
+ RONLY,
+ mplsL3vpnVrfTable,
+ 5,
+ {1, 2, 2, 1, 11} },
+ {MPLSL3VPNVRFCONFLASTCHANGED,
+ TIMETICKS,
+ RONLY,
+ mplsL3vpnVrfTable,
+ 5,
+ {1, 2, 2, 1, 12} },
+ {MPLSL3VPNVRFCONFROWSTATUS,
+ INTEGER,
+ RONLY,
+ mplsL3vpnVrfTable,
+ 5,
+ {1, 2, 2, 1, 13} },
+ {MPLSL3VPNVRFCONFADMINSTATUS,
+ INTEGER,
+ RONLY,
+ mplsL3vpnVrfTable,
+ 5,
+ {1, 2, 2, 1, 14} },
+ {MPLSL3VPNVRFCONFSTORAGETYPE,
+ INTEGER,
+ RONLY,
+ mplsL3vpnVrfTable,
+ 5,
+ {1, 2, 2, 1, 15} },
+
+ /* mplsL3vpnVrfRt Table */
+ {MPLSL3VPNVRFRT,
+ OCTET_STRING,
+ RONLY,
+ mplsL3vpnVrfRtTable,
+ 5,
+ {1, 2, 3, 1, 4} },
+ {MPLSL3VPNVRFRTDESCR,
+ OCTET_STRING,
+ RONLY,
+ mplsL3vpnVrfRtTable,
+ 5,
+ {1, 2, 3, 1, 5} },
+ {MPLSL3VPNVRFRTROWSTATUS,
+ INTEGER,
+ RONLY,
+ mplsL3vpnVrfRtTable,
+ 5,
+ {1, 2, 3, 1, 6} },
+ {MPLSL3VPNVRFRTSTORAGETYPE,
+ INTEGER,
+ RONLY,
+ mplsL3vpnVrfRtTable,
+ 5,
+ {1, 2, 3, 1, 7} },
+
+ /* mplsL3VpnPerfTable */
+ {MPLSL3VPNVRFPERFROUTESADDED,
+ COUNTER32,
+ RONLY,
+ mplsL3vpnPerfTable,
+ 5,
+ {1, 3, 1, 1, 1} },
+ {MPLSL3VPNVRFPERFROUTESDELETED,
+ COUNTER32,
+ RONLY,
+ mplsL3vpnPerfTable,
+ 5,
+ {1, 3, 1, 1, 2} },
+ {MPLSL3VPNVRFPERFCURRNUMROUTES,
+ GAUGE32,
+ RONLY,
+ mplsL3vpnPerfTable,
+ 5,
+ {1, 3, 1, 1, 3} },
+
+ /* mplsVpnRteTable */
+ {MPLSL3VPNVRFRTEINETCIDRDESTTYPE,
+ INTEGER,
+ RONLY,
+ mplsL3vpnRteTable,
+ 5,
+ {1, 4, 1, 1, 1} },
+ {MPLSL3VPNVRFRTEINETCIDRDEST,
+ OCTET_STRING,
+ RONLY,
+ mplsL3vpnRteTable,
+ 5,
+ {1, 4, 1, 1, 2} },
+ {MPLSL3VPNVRFRTEINETCIDRPFXLEN,
+ GAUGE32,
+ RONLY,
+ mplsL3vpnRteTable,
+ 5,
+ {1, 4, 1, 1, 3} },
+ {MPLSL3VPNVRFRTEINETCIDRPOLICY,
+ OID,
+ RONLY,
+ mplsL3vpnRteTable,
+ 5,
+ {1, 4, 1, 1, 4} },
+ {MPLSL3VPNVRFRTEINETCIDRNHOPTYPE,
+ INTEGER,
+ RONLY,
+ mplsL3vpnRteTable,
+ 5,
+ {1, 4, 1, 1, 5} },
+ {MPLSL3VPNVRFRTEINETCIDRNEXTHOP,
+ OCTET_STRING,
+ RONLY,
+ mplsL3vpnRteTable,
+ 5,
+ {1, 4, 1, 1, 6} },
+ {MPLSL3VPNVRFRTEINETCIDRIFINDEX,
+ INTEGER,
+ RONLY,
+ mplsL3vpnRteTable,
+ 5,
+ {1, 4, 1, 1, 7} },
+ {MPLSL3VPNVRFRTEINETCIDRTYPE,
+ INTEGER,
+ RONLY,
+ mplsL3vpnRteTable,
+ 5,
+ {1, 4, 1, 1, 8} },
+ {MPLSL3VPNVRFRTEINETCIDRPROTO,
+ INTEGER,
+ RONLY,
+ mplsL3vpnRteTable,
+ 5,
+ {1, 4, 1, 1, 9} },
+ {MPLSL3VPNVRFRTEINETCIDRAGE,
+ GAUGE32,
+ RONLY,
+ mplsL3vpnRteTable,
+ 5,
+ {1, 4, 1, 1, 10} },
+ {MPLSL3VPNVRFRTEINETCIDRNEXTHOPAS,
+ GAUGE32,
+ RONLY,
+ mplsL3vpnRteTable,
+ 5,
+ {1, 4, 1, 1, 11} },
+ {MPLSL3VPNVRFRTEINETCIDRMETRIC1,
+ INTEGER,
+ RONLY,
+ mplsL3vpnRteTable,
+ 5,
+ {1, 4, 1, 1, 12} },
+ {MPLSL3VPNVRFRTEINETCIDRMETRIC2,
+ INTEGER,
+ RONLY,
+ mplsL3vpnRteTable,
+ 5,
+ {1, 4, 1, 1, 13} },
+ {MPLSL3VPNVRFRTEINETCIDRMETRIC3,
+ INTEGER,
+ RONLY,
+ mplsL3vpnRteTable,
+ 5,
+ {1, 4, 1, 1, 14} },
+ {MPLSL3VPNVRFRTEINETCIDRMETRIC4,
+ INTEGER,
+ RONLY,
+ mplsL3vpnRteTable,
+ 5,
+ {1, 4, 1, 1, 15} },
+ {MPLSL3VPNVRFRTEINETCIDRMETRIC5,
+ INTEGER,
+ RONLY,
+ mplsL3vpnRteTable,
+ 5,
+ {1, 4, 1, 1, 16} },
+ {MPLSL3VPNVRFRTEINETCIDRXCPOINTER,
+ OCTET_STRING,
+ RONLY,
+ mplsL3vpnRteTable,
+ 5,
+ {1, 4, 1, 1, 17} },
+ {MPLSL3VPNVRFRTEINETCIDRSTATUS,
+ INTEGER,
+ RONLY,
+ mplsL3vpnRteTable,
+ 5,
+ {1, 4, 1, 1, 18} },
+};
+
+/* timeticks are in hundredths of a second */
+static void bgp_mpls_l3vpn_update_timeticks(time_t *counter)
+{
+ struct timeval tv;
+
+ monotime(&tv);
+ *counter = (tv.tv_sec * 100) + (tv.tv_usec / 10000);
+}
+
+static int bgp_mpls_l3vpn_update_last_changed(struct bgp *bgp)
+{
+ if (bgp->snmp_stats)
+ bgp_mpls_l3vpn_update_timeticks(
+ &(bgp->snmp_stats->modify_time));
+ return 0;
+}
+
+static uint32_t bgp_mpls_l3vpn_current_routes(struct bgp *l3vpn_bgp)
+{
+ uint32_t count = 0;
+ struct bgp_table *table;
+ struct bgp_dest *dest;
+ struct bgp_path_info *pi;
+
+ table = l3vpn_bgp->rib[AFI_IP][SAFI_UNICAST];
+ for (dest = bgp_table_top(table); dest; dest = bgp_route_next(dest)) {
+ pi = bgp_dest_get_bgp_path_info(dest);
+ for (; pi; pi = pi->next)
+ count++;
+ }
+ table = l3vpn_bgp->rib[AFI_IP6][SAFI_UNICAST];
+ for (dest = bgp_table_top(table); dest; dest = bgp_route_next(dest)) {
+ pi = bgp_dest_get_bgp_path_info(dest);
+ for (; pi; pi = pi->next)
+ count++;
+ }
+ return count;
+}
+
+static int bgp_init_snmp_stats(struct bgp *bgp)
+{
+ if (is_bgp_vrf_mplsvpn(bgp)) {
+ if (bgp->snmp_stats == NULL) {
+ bgp->snmp_stats = XCALLOC(
+ MTYPE_BGP, sizeof(struct bgp_snmp_stats));
+ /* fix up added routes */
+ if (bgp->snmp_stats) {
+ bgp->snmp_stats->routes_added =
+ bgp_mpls_l3vpn_current_routes(bgp);
+ bgp_mpls_l3vpn_update_timeticks(
+ &(bgp->snmp_stats->creation_time));
+ }
+ }
+ } else {
+ if (bgp->snmp_stats) {
+ XFREE(MTYPE_BGP, bgp->snmp_stats);
+ bgp->snmp_stats = NULL;
+ }
+ }
+ /* Something changed - update the timestamp */
+ bgp_mpls_l3vpn_update_last_changed(bgp);
+ return 0;
+}
+
+static int bgp_snmp_update_route_stats(struct bgp_dest *dest,
+ struct bgp_path_info *pi, bool added)
+{
+ struct bgp_table *table;
+
+ if (dest) {
+ table = bgp_dest_table(dest);
+ /* only update if we have a stats block - MPLSVPN vrfs for now*/
+ if (table && table->bgp && table->bgp->snmp_stats) {
+ if (added)
+ table->bgp->snmp_stats->routes_added++;
+ else
+ table->bgp->snmp_stats->routes_deleted++;
+ }
+ }
+ return 0;
+}
+
+static bool is_bgp_vrf_active(struct bgp *bgp)
+{
+ struct vrf *vrf;
+ struct interface *ifp;
+
+ /* if there is one interface in the vrf which is up then it is deemed
+ * active
+ */
+ vrf = vrf_lookup_by_id(bgp->vrf_id);
+ if (vrf == NULL)
+ return false;
+ RB_FOREACH (ifp, if_name_head, &vrf->ifaces_by_name) {
+ /* if we are in a vrf skip the l3mdev */
+ if (bgp->name && strncmp(ifp->name, bgp->name, VRF_NAMSIZ) == 0)
+ continue;
+
+ if (if_is_up(ifp))
+ return true;
+ }
+ return false;
+}
+
+/* BGP Traps. */
+static struct trap_object l3vpn_trap_list[] = {{5, {1, 2, 1, 1, 5} },
+ {5, {1, 2, 2, 1, 6} } };
+
+static int bgp_vrf_check_update_active(struct bgp *bgp, struct interface *ifp)
+{
+ bool new_active = false;
+ oid trap;
+ struct index_oid trap_index[2];
+
+ if (!is_bgp_vrf_mplsvpn(bgp) || bgp->snmp_stats == NULL
+ || !bgp_mplsvpn_notif_enable)
+ return 0;
+ new_active = is_bgp_vrf_active(bgp);
+ if (bgp->snmp_stats->active != new_active) {
+ /* add trap in here */
+ bgp->snmp_stats->active = new_active;
+
+ /* send relevent trap */
+ if (bgp->snmp_stats->active)
+ trap = MPLSL3VPNVRFUP;
+ else
+ trap = MPLSL3VPNDOWN;
+
+ /*
+ * first index vrf_name + ifindex
+ * second index vrf_name
+ */
+ trap_index[1].indexlen = strnlen(bgp->name, VRF_NAMSIZ);
+ oid_copy_str(trap_index[0].indexname, bgp->name,
+ trap_index[1].indexlen);
+ oid_copy_str(trap_index[1].indexname, bgp->name,
+ trap_index[1].indexlen);
+ trap_index[0].indexlen =
+ trap_index[1].indexlen + sizeof(ifindex_t);
+ oid_copy_int(trap_index[0].indexname + trap_index[1].indexlen,
+ (int *)&(ifp->ifindex));
+
+ smux_trap_multi_index(
+ mpls_l3vpn_variables, array_size(mpls_l3vpn_variables),
+ mpls_l3vpn_trap_oid, array_size(mpls_l3vpn_trap_oid),
+ mpls_l3vpn_oid, sizeof(mpls_l3vpn_oid) / sizeof(oid),
+ trap_index, array_size(trap_index), l3vpn_trap_list,
+ array_size(l3vpn_trap_list), trap);
+ }
+ bgp_mpls_l3vpn_update_last_changed(bgp);
+ return 0;
+}
+
+static uint8_t *mplsL3vpnConfiguredVrfs(struct variable *v, oid name[],
+ size_t *length, int exact,
+ size_t *var_len,
+ WriteMethod **write_method)
+{
+ struct listnode *node, *nnode;
+ struct bgp *bgp;
+ uint32_t count = 0;
+
+ if (smux_header_generic(v, name, length, exact, var_len, write_method)
+ == MATCH_FAILED)
+ return NULL;
+
+ for (ALL_LIST_ELEMENTS(bm->bgp, node, nnode, bgp)) {
+ if (is_bgp_vrf_mplsvpn(bgp))
+ count++;
+ }
+ return SNMP_INTEGER(count);
+}
+
+static uint8_t *mplsL3vpnActiveVrfs(struct variable *v, oid name[],
+ size_t *length, int exact, size_t *var_len,
+ WriteMethod **write_method)
+{
+ struct listnode *node, *nnode;
+ struct bgp *bgp;
+ uint32_t count = 0;
+
+ if (smux_header_generic(v, name, length, exact, var_len, write_method)
+ == MATCH_FAILED)
+ return NULL;
+
+ for (ALL_LIST_ELEMENTS(bm->bgp, node, nnode, bgp)) {
+ if (is_bgp_vrf_mplsvpn(bgp) && is_bgp_vrf_active(bgp))
+ count++;
+ }
+ return SNMP_INTEGER(count);
+}
+
+static uint8_t *mplsL3vpnConnectedInterfaces(struct variable *v, oid name[],
+ size_t *length, int exact,
+ size_t *var_len,
+ WriteMethod **write_method)
+{
+ struct listnode *node, *nnode;
+ struct bgp *bgp;
+ uint32_t count = 0;
+ struct vrf *vrf;
+
+ if (smux_header_generic(v, name, length, exact, var_len, write_method)
+ == MATCH_FAILED)
+ return NULL;
+
+ for (ALL_LIST_ELEMENTS(bm->bgp, node, nnode, bgp)) {
+ if (is_bgp_vrf_mplsvpn(bgp)) {
+ vrf = vrf_lookup_by_name(bgp->name);
+ if (vrf == NULL)
+ continue;
+
+ count += vrf_interface_count(vrf);
+ }
+ }
+
+ return SNMP_INTEGER(count);
+}
+
+static int write_mplsL3vpnNotificationEnable(int action, uint8_t *var_val,
+ uint8_t var_val_type,
+ size_t var_val_len, uint8_t *statP,
+ oid *name, size_t length)
+{
+ uint32_t intval;
+
+ if (var_val_type != ASN_INTEGER)
+ return SNMP_ERR_WRONGTYPE;
+
+ if (var_val_len != sizeof(long))
+ return SNMP_ERR_WRONGLENGTH;
+
+ intval = *(long *)var_val;
+ bgp_mplsvpn_notif_enable = intval;
+ return SNMP_ERR_NOERROR;
+}
+
+static uint8_t *mplsL3vpnNotificationEnable(struct variable *v, oid name[],
+ size_t *length, int exact,
+ size_t *var_len,
+ WriteMethod **write_method)
+{
+ if (smux_header_generic(v, name, length, exact, var_len, write_method)
+ == MATCH_FAILED)
+ return NULL;
+
+ *write_method = write_mplsL3vpnNotificationEnable;
+ return SNMP_INTEGER(bgp_mplsvpn_notif_enable);
+}
+
+static uint8_t *mplsL3vpnVrfConfMaxPossRts(struct variable *v, oid name[],
+ size_t *length, int exact,
+ size_t *var_len,
+ WriteMethod **write_method)
+{
+ if (smux_header_generic(v, name, length, exact, var_len, write_method)
+ == MATCH_FAILED)
+ return NULL;
+
+ return SNMP_INTEGER(0);
+}
+
+static uint8_t *mplsL3vpnVrfConfRteMxThrshTime(struct variable *v, oid name[],
+ size_t *length, int exact,
+ size_t *var_len,
+ WriteMethod **write_method)
+{
+ if (smux_header_generic(v, name, length, exact, var_len, write_method)
+ == MATCH_FAILED)
+ return NULL;
+
+ return SNMP_INTEGER(0);
+}
+
+static uint8_t *mplsL3vpnIllLblRcvThrsh(struct variable *v, oid name[],
+ size_t *length, int exact,
+ size_t *var_len,
+ WriteMethod **write_method)
+{
+ if (smux_header_generic(v, name, length, exact, var_len, write_method)
+ == MATCH_FAILED)
+ return NULL;
+
+ return SNMP_INTEGER(0);
+}
+
+
+static struct bgp *bgp_lookup_by_name_next(char *vrf_name)
+{
+ struct bgp *bgp, *bgp_next = NULL;
+ struct listnode *node, *nnode;
+ bool first = false;
+
+ /*
+ * the vrfs are not stored alphabetically but since we are using the
+ * vrf name as an index we need the getnext function to return them
+ * in a atrict order. Thus run through and find the best next one.
+ */
+ for (ALL_LIST_ELEMENTS(bm->bgp, node, nnode, bgp)) {
+ if (!is_bgp_vrf_mplsvpn(bgp))
+ continue;
+ if (strnlen(vrf_name, VRF_NAMSIZ) == 0 && bgp_next == NULL) {
+ first = true;
+ bgp_next = bgp;
+ continue;
+ }
+ if (first || strncmp(bgp->name, vrf_name, VRF_NAMSIZ) > 0) {
+ if (bgp_next == NULL)
+ bgp_next = bgp;
+ else if (strncmp(bgp->name, bgp_next->name, VRF_NAMSIZ)
+ < 0)
+ bgp_next = bgp;
+ }
+ }
+ return bgp_next;
+}
+
+/* 1.3.6.1.2.1.10.166.11.1.2.1.1.x = 14*/
+#define IFCONFTAB_NAMELEN 14
+static struct bgp *bgpL3vpnIfConf_lookup(struct variable *v, oid name[],
+ size_t *length, char *vrf_name,
+ ifindex_t *ifindex, int exact)
+{
+ struct bgp *bgp = NULL;
+ size_t namelen = v ? v->namelen : IFCONFTAB_NAMELEN;
+ struct interface *ifp;
+ int vrf_name_len, len;
+
+ /* too long ? */
+ if (*length - namelen > (VRF_NAMSIZ + sizeof(uint32_t)))
+ return NULL;
+ /* do we have index info in the oid ? */
+ if (*length - namelen != 0 && *length - namelen >= sizeof(uint32_t)) {
+ /* copy the info from the oid */
+ vrf_name_len = *length - (namelen + sizeof(ifindex_t));
+ oid2string(name + namelen, vrf_name_len, vrf_name);
+ oid2int(name + namelen + vrf_name_len, ifindex);
+ }
+
+ if (exact) {
+ /* Check the length. */
+ bgp = bgp_lookup_by_name(vrf_name);
+ if (bgp && !is_bgp_vrf_mplsvpn(bgp))
+ return NULL;
+ if (!bgp)
+ return NULL;
+ ifp = if_lookup_by_index(*ifindex, bgp->vrf_id);
+ if (!ifp)
+ return NULL;
+ } else {
+ if (strnlen(vrf_name, VRF_NAMSIZ) == 0)
+ bgp = bgp_lookup_by_name_next(vrf_name);
+ else
+ bgp = bgp_lookup_by_name(vrf_name);
+
+ while (bgp) {
+ ifp = if_vrf_lookup_by_index_next(*ifindex,
+ bgp->vrf_id);
+ if (ifp) {
+ vrf_name_len = strnlen(bgp->name, VRF_NAMSIZ);
+ *ifindex = ifp->ifindex;
+ len = vrf_name_len + sizeof(ifindex_t);
+ oid_copy_str(name + namelen, bgp->name,
+ vrf_name_len);
+ oid_copy_int(name + namelen + vrf_name_len,
+ ifindex);
+ *length = len + namelen;
+
+ return bgp;
+ }
+ *ifindex = 0;
+ bgp = bgp_lookup_by_name_next(bgp->name);
+ }
+
+ return NULL;
+ }
+ return bgp;
+}
+
+static uint8_t *mplsL3vpnIfConfTable(struct variable *v, oid name[],
+ size_t *length, int exact, size_t *var_len,
+ WriteMethod **write_method)
+{
+ char vrf_name[VRF_NAMSIZ];
+ ifindex_t ifindex = 0;
+ struct bgp *l3vpn_bgp;
+
+ if (smux_header_table(v, name, length, exact, var_len, write_method)
+ == MATCH_FAILED)
+ return NULL;
+
+ memset(vrf_name, 0, VRF_NAMSIZ);
+ l3vpn_bgp = bgpL3vpnIfConf_lookup(v, name, length, vrf_name, &ifindex,
+ exact);
+ if (!l3vpn_bgp)
+ return NULL;
+
+ switch (v->magic) {
+ case MPLSL3VPNIFVPNCLASSIFICATION:
+ return SNMP_INTEGER(2);
+ case MPLSL3VPNIFCONFSTORAGETYPE:
+ return SNMP_INTEGER(2);
+ case MPLSL3VPNIFCONFROWSTATUS:
+ return SNMP_INTEGER(1);
+ }
+ return NULL;
+}
+
+/* 1.3.6.1.2.1.10.166.11.1.2.2.1.x = 14*/
+#define VRFTAB_NAMELEN 14
+
+static struct bgp *bgpL3vpnVrf_lookup(struct variable *v, oid name[],
+ size_t *length, char *vrf_name, int exact)
+{
+ struct bgp *bgp = NULL;
+ size_t namelen = v ? v->namelen : VRFTAB_NAMELEN;
+ int len;
+
+ if (*length - namelen > VRF_NAMSIZ)
+ return NULL;
+ oid2string(name + namelen, *length - namelen, vrf_name);
+ if (exact) {
+ /* Check the length. */
+ bgp = bgp_lookup_by_name(vrf_name);
+ if (bgp && !is_bgp_vrf_mplsvpn(bgp))
+ return NULL;
+ } else {
+ bgp = bgp_lookup_by_name_next(vrf_name);
+
+ if (bgp == NULL)
+ return NULL;
+
+ len = strnlen(bgp->name, VRF_NAMSIZ);
+ oid_copy_str(name + namelen, bgp->name, len);
+ *length = len + namelen;
+ }
+ return bgp;
+}
+
+static uint8_t *mplsL3vpnVrfTable(struct variable *v, oid name[],
+ size_t *length, int exact, size_t *var_len,
+ WriteMethod **write_method)
+{
+ char vrf_name[VRF_NAMSIZ];
+ struct bgp *l3vpn_bgp;
+
+ if (smux_header_table(v, name, length, exact, var_len, write_method)
+ == MATCH_FAILED)
+ return NULL;
+
+ memset(vrf_name, 0, VRF_NAMSIZ);
+ l3vpn_bgp = bgpL3vpnVrf_lookup(v, name, length, vrf_name, exact);
+
+ if (!l3vpn_bgp)
+ return NULL;
+
+ switch (v->magic) {
+ case MPLSL3VPNVRFVPNID:
+ *var_len = 0;
+ return NULL;
+ case MPLSL3VPNVRFDESC:
+ *var_len = strnlen(l3vpn_bgp->name, VRF_NAMSIZ);
+ return (uint8_t *)l3vpn_bgp->name;
+ case MPLSL3VPNVRFRD:
+ /*
+ * this is a horror show but the MIB dicates one RD per vrf
+ * and not one RD per AFI as we (FRR) have. So this little gem
+ * returns the V4 one if it's set OR the v6 one if it's set or
+ * zero-length string id neither are set
+ */
+ memset(rd_buf, 0, RD_ADDRSTRLEN);
+ if (CHECK_FLAG(l3vpn_bgp->vpn_policy[AFI_IP].flags,
+ BGP_VPN_POLICY_TOVPN_RD_SET))
+ prefix_rd2str(&l3vpn_bgp->vpn_policy[AFI_IP].tovpn_rd,
+ rd_buf, sizeof(rd_buf));
+ else if (CHECK_FLAG(l3vpn_bgp->vpn_policy[AFI_IP6].flags,
+ BGP_VPN_POLICY_TOVPN_RD_SET))
+ prefix_rd2str(&l3vpn_bgp->vpn_policy[AFI_IP6].tovpn_rd,
+ rd_buf, sizeof(rd_buf));
+
+ *var_len = strnlen(rd_buf, RD_ADDRSTRLEN);
+ return (uint8_t *)rd_buf;
+ case MPLSL3VPNVRFCREATIONTIME:
+ return SNMP_INTEGER(
+ (uint32_t)l3vpn_bgp->snmp_stats->creation_time);
+ case MPLSL3VPNVRFOPERSTATUS:
+ if (l3vpn_bgp->snmp_stats->active)
+ return SNMP_INTEGER(1);
+ else
+ return SNMP_INTEGER(2);
+ case MPLSL3VPNVRFACTIVEINTERFACES:
+ return SNMP_INTEGER(bgp_vrf_interfaces(l3vpn_bgp, true));
+ case MPLSL3VPNVRFASSOCIATEDINTERFACES:
+ return SNMP_INTEGER(bgp_vrf_interfaces(l3vpn_bgp, false));
+ case MPLSL3VPNVRFCONFMIDRTETHRESH:
+ return SNMP_INTEGER(0);
+ case MPLSL3VPNVRFCONFHIGHRTETHRSH:
+ return SNMP_INTEGER(0);
+ case MPLSL3VPNVRFCONFMAXROUTES:
+ return SNMP_INTEGER(0);
+ case MPLSL3VPNVRFCONFLASTCHANGED:
+ return SNMP_INTEGER(
+ (uint32_t)l3vpn_bgp->snmp_stats->modify_time);
+ case MPLSL3VPNVRFCONFROWSTATUS:
+ return SNMP_INTEGER(1);
+ case MPLSL3VPNVRFCONFADMINSTATUS:
+ return SNMP_INTEGER(1);
+ case MPLSL3VPNVRFCONFSTORAGETYPE:
+ return SNMP_INTEGER(2);
+ }
+ return NULL;
+}
+
+/* 1.3.6.1.2.1.10.166.11.1.2.3.1.x = 14*/
+#define VRFRTTAB_NAMELEN 14
+static struct bgp *bgpL3vpnVrfRt_lookup(struct variable *v, oid name[],
+ size_t *length, char *vrf_name,
+ uint32_t *rt_index, uint8_t *rt_type,
+ int exact)
+{
+ uint32_t type_index_size;
+ struct bgp *l3vpn_bgp;
+ size_t namelen = v ? v->namelen : VRFRTTAB_NAMELEN;
+ int vrf_name_len, len;
+
+ /* too long ? */
+ if (*length - namelen
+ > (VRF_NAMSIZ + sizeof(uint32_t)) + sizeof(uint8_t))
+ return NULL;
+
+ type_index_size = sizeof(uint32_t) + sizeof(uint8_t);
+ /* do we have index info in the oid ? */
+ if (*length - namelen != 0 && *length - namelen >= type_index_size) {
+ /* copy the info from the oid */
+ vrf_name_len = *length - (namelen + type_index_size);
+ oid2string(name + namelen, vrf_name_len, vrf_name);
+ oid2int(name + namelen + vrf_name_len, (int *)rt_index);
+ *rt_type = name[namelen + vrf_name_len + sizeof(uint32_t)];
+ }
+
+ /* validate the RT index is in range */
+ if (*rt_index > AFI_IP6)
+ return NULL;
+
+ if (exact) {
+ l3vpn_bgp = bgp_lookup_by_name(vrf_name);
+ if (l3vpn_bgp && !is_bgp_vrf_mplsvpn(l3vpn_bgp))
+ return NULL;
+ if (!l3vpn_bgp)
+ return NULL;
+ if ((*rt_index != AFI_IP) && (*rt_index != AFI_IP6))
+ return NULL;
+ /* do we have RT config */
+ if (!(l3vpn_bgp->vpn_policy[*rt_index]
+ .rtlist[BGP_VPN_POLICY_DIR_FROMVPN]
+ || l3vpn_bgp->vpn_policy[*rt_index]
+ .rtlist[BGP_VPN_POLICY_DIR_TOVPN]))
+ return NULL;
+ return l3vpn_bgp;
+ }
+ if (strnlen(vrf_name, VRF_NAMSIZ) == 0)
+ l3vpn_bgp = bgp_lookup_by_name_next(vrf_name);
+ else
+ l3vpn_bgp = bgp_lookup_by_name(vrf_name);
+ while (l3vpn_bgp) {
+ switch (*rt_index) {
+ case 0:
+ *rt_index = AFI_IP;
+ break;
+ case AFI_IP:
+ *rt_index = AFI_IP6;
+ break;
+ case AFI_IP6:
+ *rt_index = 0;
+ continue;
+ }
+ if (*rt_index) {
+ switch (*rt_type) {
+ case 0:
+ *rt_type = MPLSVPNVRFRTTYPEIMPORT;
+ break;
+ case MPLSVPNVRFRTTYPEIMPORT:
+ *rt_type = MPLSVPNVRFRTTYPEEXPORT;
+ break;
+ case MPLSVPNVRFRTTYPEEXPORT:
+ case MPLSVPNVRFRTTYPEBOTH:
+ *rt_type = 0;
+ break;
+ }
+ if (*rt_type) {
+ bool import, export;
+
+ import =
+ (!!l3vpn_bgp->vpn_policy[*rt_index].rtlist
+ [BGP_VPN_POLICY_DIR_FROMVPN]);
+ export =
+ (!!l3vpn_bgp->vpn_policy[*rt_index].rtlist
+ [BGP_VPN_POLICY_DIR_TOVPN]);
+ if (*rt_type == MPLSVPNVRFRTTYPEIMPORT
+ && !import)
+ continue;
+ if (*rt_type == MPLSVPNVRFRTTYPEEXPORT
+ && !export)
+ continue;
+ /* ckeck for both */
+ if (*rt_type == MPLSVPNVRFRTTYPEIMPORT && import
+ && export
+ && ecommunity_cmp(
+ l3vpn_bgp->vpn_policy[*rt_index].rtlist
+ [BGP_VPN_POLICY_DIR_FROMVPN],
+ l3vpn_bgp->vpn_policy[*rt_index].rtlist
+ [BGP_VPN_POLICY_DIR_TOVPN]))
+ *rt_type = MPLSVPNVRFRTTYPEBOTH;
+
+ /* we have a match copy the oid info */
+ vrf_name_len =
+ strnlen(l3vpn_bgp->name, VRF_NAMSIZ);
+ len = vrf_name_len + sizeof(uint32_t)
+ + sizeof(uint8_t);
+ oid_copy_str(name + namelen, l3vpn_bgp->name,
+ vrf_name_len);
+ oid_copy_int(name + namelen + vrf_name_len,
+ (int *)rt_index);
+ name[(namelen + len) - 1] = *rt_type;
+ *length = len + namelen;
+ return l3vpn_bgp;
+ }
+ l3vpn_bgp = bgp_lookup_by_name_next(l3vpn_bgp->name);
+ }
+ }
+ return NULL;
+}
+
+static const char *rt_type2str(uint8_t rt_type)
+{
+ switch (rt_type) {
+ case MPLSVPNVRFRTTYPEIMPORT:
+ return "import";
+ case MPLSVPNVRFRTTYPEEXPORT:
+ return "export";
+ case MPLSVPNVRFRTTYPEBOTH:
+ return "both";
+ default:
+ return "unknown";
+ }
+}
+static uint8_t *mplsL3vpnVrfRtTable(struct variable *v, oid name[],
+ size_t *length, int exact, size_t *var_len,
+ WriteMethod **write_method)
+{
+ char vrf_name[VRF_NAMSIZ];
+ struct bgp *l3vpn_bgp;
+ uint32_t rt_index = 0;
+ uint8_t rt_type = 0;
+ char *rt_b;
+
+ if (smux_header_table(v, name, length, exact, var_len, write_method)
+ == MATCH_FAILED)
+ return NULL;
+
+ memset(vrf_name, 0, VRF_NAMSIZ);
+ l3vpn_bgp = bgpL3vpnVrfRt_lookup(v, name, length, vrf_name, &rt_index,
+ &rt_type, exact);
+
+ if (!l3vpn_bgp)
+ return NULL;
+
+ switch (v->magic) {
+ case MPLSL3VPNVRFRT:
+ switch (rt_type) {
+ case MPLSVPNVRFRTTYPEIMPORT:
+ rt_b = ecommunity_ecom2str(
+ l3vpn_bgp->vpn_policy[rt_index]
+ .rtlist[BGP_VPN_POLICY_DIR_FROMVPN],
+ ECOMMUNITY_FORMAT_ROUTE_MAP,
+ ECOMMUNITY_ROUTE_TARGET);
+ break;
+ case MPLSVPNVRFRTTYPEEXPORT:
+ case MPLSVPNVRFRTTYPEBOTH:
+ rt_b = ecommunity_ecom2str(
+ l3vpn_bgp->vpn_policy[rt_index]
+ .rtlist[BGP_VPN_POLICY_DIR_TOVPN],
+ ECOMMUNITY_FORMAT_ROUTE_MAP,
+ ECOMMUNITY_ROUTE_TARGET);
+ break;
+ default:
+ rt_b = NULL;
+ break;
+ }
+ if (rt_b)
+ *var_len = strnlen(rt_b, ECOMMUNITY_STRLEN);
+ else
+ *var_len = 0;
+ return (uint8_t *)rt_b;
+ case MPLSL3VPNVRFRTDESCR:
+ /* since we dont have a description generate one */
+ memset(rt_description, 0, VRF_NAMSIZ + RT_PREAMBLE_SIZE);
+ snprintf(rt_description, VRF_NAMSIZ + RT_PREAMBLE_SIZE,
+ "RT %s for VRF %s", rt_type2str(rt_type),
+ l3vpn_bgp->name);
+ *var_len =
+ strnlen(rt_description, VRF_NAMSIZ + RT_PREAMBLE_SIZE);
+ return (uint8_t *)rt_description;
+ case MPLSL3VPNVRFRTROWSTATUS:
+ return SNMP_INTEGER(1);
+ case MPLSL3VPNVRFRTSTORAGETYPE:
+ return SNMP_INTEGER(2);
+ }
+ return NULL;
+}
+
+/* 1.3.6.1.2.1.10.166.11.1.3.1.1.x = 14*/
+#define PERFTAB_NAMELEN 14
+
+static uint8_t *mplsL3vpnPerfTable(struct variable *v, oid name[],
+ size_t *length, int exact, size_t *var_len,
+ WriteMethod **write_method)
+{
+ char vrf_name[VRF_NAMSIZ];
+ struct bgp *l3vpn_bgp;
+
+ if (smux_header_table(v, name, length, exact, var_len, write_method)
+ == MATCH_FAILED)
+ return NULL;
+
+ memset(vrf_name, 0, VRF_NAMSIZ);
+ l3vpn_bgp = bgpL3vpnVrf_lookup(v, name, length, vrf_name, exact);
+
+ if (!l3vpn_bgp)
+ return NULL;
+
+ switch (v->magic) {
+ case MPLSL3VPNVRFPERFROUTESADDED:
+ return SNMP_INTEGER(l3vpn_bgp->snmp_stats->routes_added);
+ case MPLSL3VPNVRFPERFROUTESDELETED:
+ return SNMP_INTEGER(l3vpn_bgp->snmp_stats->routes_deleted);
+ case MPLSL3VPNVRFPERFCURRNUMROUTES:
+ return SNMP_INTEGER(bgp_mpls_l3vpn_current_routes(l3vpn_bgp));
+ }
+ return NULL;
+}
+
+static struct bgp_path_info *
+bgp_lookup_route(struct bgp *l3vpn_bgp, struct bgp_dest **dest,
+ struct prefix *prefix, uint16_t policy, struct ipaddr *nexthop)
+{
+ struct bgp_path_info *pi = NULL;
+ struct bgp_table *table;
+
+ switch (prefix->family) {
+ case AF_INET:
+ table = l3vpn_bgp->rib[AFI_IP][SAFI_UNICAST];
+ break;
+ case AF_INET6:
+ table = l3vpn_bgp->rib[AFI_IP6][SAFI_UNICAST];
+ break;
+ default:
+ return NULL;
+ }
+
+ /*get the prefix */
+ *dest = bgp_node_lookup(table, prefix);
+ if (*dest == NULL)
+ return NULL;
+
+ /* now find the right path */
+ pi = bgp_dest_get_bgp_path_info(*dest);
+ for (; pi; pi = pi->next) {
+ switch (nexthop->ipa_type) {
+ case IPADDR_V4:
+ if (nexthop->ip._v4_addr.s_addr
+ == pi->attr->nexthop.s_addr)
+ return pi;
+ break;
+ case IPADDR_V6:
+ if (memcmp(&nexthop->ip._v6_addr,
+ &pi->attr->mp_nexthop_global,
+ sizeof(struct in6_addr))
+ == 0)
+ return pi;
+ break;
+ default:
+ return pi;
+ }
+ }
+ return NULL;
+}
+
+static struct bgp_path_info *bgp_lookup_route_next(struct bgp **l3vpn_bgp,
+ struct bgp_dest **dest,
+ struct prefix *prefix,
+ uint16_t *policy,
+ struct ipaddr *nexthop)
+{
+ struct bgp_path_info *pi = NULL;
+ struct bgp_table *table;
+ const struct prefix *p;
+ uint8_t family;
+
+ /* First route?*/
+ if (prefix->prefixlen == 0) {
+ /* try V4 table */
+ table = (*l3vpn_bgp)->rib[AFI_IP][SAFI_UNICAST];
+ for (*dest = bgp_table_top(table); *dest;
+ *dest = bgp_route_next(*dest)) {
+ pi = bgp_dest_get_bgp_path_info(*dest);
+ if (pi)
+ break;
+ }
+
+ if (!pi) {
+ /* try V6 table */
+ table = (*l3vpn_bgp)->rib[AFI_IP6][SAFI_UNICAST];
+ for (*dest = bgp_table_top(table); *dest;
+ *dest = bgp_route_next(*dest)) {
+ pi = bgp_dest_get_bgp_path_info(*dest);
+ if (pi)
+ break;
+ }
+ }
+ return pi;
+ }
+ /* real next search for the entry first use exact lookup */
+ pi = bgp_lookup_route(*l3vpn_bgp, dest, prefix, *policy, nexthop);
+
+ if (pi == NULL)
+ return NULL;
+
+ p = bgp_dest_get_prefix(*dest);
+ family = p->family;
+
+ /* We have found the input path let's find the next one in the list */
+ if (pi->next) {
+ /* ensure OID is always higher for multipath routes by
+ * incrementing opaque policy oid
+ */
+ *policy += 1;
+ return pi->next;
+ }
+
+ /* No more paths in the input route so find the next route */
+ for (; *l3vpn_bgp;
+ *l3vpn_bgp = bgp_lookup_by_name_next((*l3vpn_bgp)->name)) {
+ *policy = 0;
+ if (!*dest) {
+ table = (*l3vpn_bgp)->rib[AFI_IP][SAFI_UNICAST];
+ *dest = bgp_table_top(table);
+ family = AF_INET;
+ } else
+ *dest = bgp_route_next(*dest);
+
+ while (true) {
+ for (; *dest; *dest = bgp_route_next(*dest)) {
+ pi = bgp_dest_get_bgp_path_info(*dest);
+
+ if (pi)
+ return pi;
+ }
+ if (family == AF_INET) {
+ table = (*l3vpn_bgp)
+ ->rib[AFI_IP6][SAFI_UNICAST];
+ *dest = bgp_table_top(table);
+ family = AF_INET6;
+ continue;
+ }
+ break;
+ }
+ }
+
+ return NULL;
+}
+
+static bool is_addr_type(oid id)
+{
+ switch (id) {
+ case INETADDRESSTYPEUNKNOWN:
+ case INETADDRESSTYPEIPV4:
+ case INETADDRESSTYPEIPV6:
+ return true;
+ }
+ return false;
+}
+
+/* 1.3.6.1.2.1.10.166.11.1.4.1.1.x = 14*/
+#define PERFTAB_NAMELEN 14
+
+static struct bgp_path_info *bgpL3vpnRte_lookup(struct variable *v, oid name[],
+ size_t *length, char *vrf_name,
+ struct bgp **l3vpn_bgp,
+ struct bgp_dest **dest,
+ uint16_t *policy, int exact)
+{
+ uint8_t i;
+ uint8_t vrf_name_len = 0;
+ struct bgp_path_info *pi = NULL;
+ size_t namelen = v ? v->namelen : IFCONFTAB_NAMELEN;
+ struct prefix prefix = {0};
+ struct ipaddr nexthop = {0};
+ uint8_t prefix_type;
+ uint8_t nexthop_type;
+
+ if ((uint32_t)(*length - namelen) > (VRF_NAMSIZ + 37))
+ return NULL;
+
+ if (*length - namelen != 0) {
+ /* parse incoming OID */
+ for (i = namelen; i < (*length); i++) {
+ if (is_addr_type(name[i]))
+ break;
+ vrf_name_len++;
+ }
+ if (vrf_name_len > VRF_NAMSIZ)
+ return NULL;
+
+ oid2string(name + namelen, vrf_name_len, vrf_name);
+ prefix_type = name[i++];
+ switch (prefix_type) {
+ case INETADDRESSTYPEUNKNOWN:
+ prefix.family = AF_UNSPEC;
+ break;
+ case INETADDRESSTYPEIPV4:
+ prefix.family = AF_INET;
+ oid2in_addr(&name[i], sizeof(struct in_addr),
+ &prefix.u.prefix4);
+ i += sizeof(struct in_addr);
+ break;
+ case INETADDRESSTYPEIPV6:
+ prefix.family = AF_INET6;
+ oid2in6_addr(&name[i], &prefix.u.prefix6);
+ i += sizeof(struct in6_addr);
+ break;
+ }
+ prefix.prefixlen = (uint8_t)name[i++];
+ *policy |= name[i++] << 8;
+ *policy |= name[i++];
+ nexthop_type = name[i++];
+ switch (nexthop_type) {
+ case INETADDRESSTYPEUNKNOWN:
+ nexthop.ipa_type = (prefix.family == AF_INET)
+ ? IPADDR_V4
+ : IPADDR_V6;
+ break;
+ case INETADDRESSTYPEIPV4:
+ nexthop.ipa_type = IPADDR_V4;
+ oid2in_addr(&name[i], sizeof(struct in_addr),
+ &nexthop.ip._v4_addr);
+ /* i += sizeof(struct in_addr); */
+ break;
+ case INETADDRESSTYPEIPV6:
+ nexthop.ipa_type = IPADDR_V6;
+ oid2in6_addr(&name[i], &nexthop.ip._v6_addr);
+ /* i += sizeof(struct in6_addr); */
+ break;
+ }
+ }
+
+ if (exact) {
+ *l3vpn_bgp = bgp_lookup_by_name(vrf_name);
+ if (*l3vpn_bgp && !is_bgp_vrf_mplsvpn(*l3vpn_bgp))
+ return NULL;
+ if (*l3vpn_bgp == NULL)
+ return NULL;
+
+ /* now lookup the route in this bgp table */
+ pi = bgp_lookup_route(*l3vpn_bgp, dest, &prefix, *policy,
+ &nexthop);
+ } else {
+ int str_len;
+
+ str_len = strnlen(vrf_name, VRF_NAMSIZ);
+ if (str_len == 0) {
+ *l3vpn_bgp = bgp_lookup_by_name_next(vrf_name);
+ } else
+ /* otherwise lookup the one we have */
+ *l3vpn_bgp = bgp_lookup_by_name(vrf_name);
+
+ if (*l3vpn_bgp == NULL)
+ return NULL;
+
+ pi = bgp_lookup_route_next(l3vpn_bgp, dest, &prefix, policy,
+ &nexthop);
+ if (pi) {
+ uint8_t vrf_name_len =
+ strnlen((*l3vpn_bgp)->name, VRF_NAMSIZ);
+ const struct prefix *p = bgp_dest_get_prefix(*dest);
+ uint8_t oid_index;
+ bool v4 = (p->family == AF_INET);
+ uint8_t addr_len = v4 ? sizeof(struct in_addr)
+ : sizeof(struct in6_addr);
+ struct attr *attr = pi->attr;
+
+ /* copy the index parameters */
+ oid_copy_str(&name[namelen], (*l3vpn_bgp)->name,
+ vrf_name_len);
+ oid_index = namelen + vrf_name_len;
+ if (v4) {
+ name[oid_index++] = INETADDRESSTYPEIPV4;
+ oid_copy_in_addr(&name[oid_index],
+ &p->u.prefix4);
+ } else {
+ name[oid_index++] = INETADDRESSTYPEIPV6;
+ oid_copy_in6_addr(&name[oid_index],
+ &p->u.prefix6);
+ }
+
+ oid_index += addr_len;
+ name[oid_index++] = p->prefixlen;
+ name[oid_index++] = *policy >> 8;
+ name[oid_index++] = *policy & 0xff;
+
+ if (!BGP_ATTR_NEXTHOP_AFI_IP6(attr)) {
+ if (attr->nexthop.s_addr == INADDR_ANY)
+ name[oid_index++] =
+ INETADDRESSTYPEUNKNOWN;
+ else {
+ name[oid_index++] = INETADDRESSTYPEIPV4;
+ oid_copy_in_addr(&name[oid_index],
+ &attr->nexthop);
+ oid_index += sizeof(struct in_addr);
+ }
+ } else {
+ if (IN6_IS_ADDR_UNSPECIFIED(
+ &attr->mp_nexthop_global))
+ name[oid_index++] =
+ INETADDRESSTYPEUNKNOWN;
+ else {
+ name[oid_index++] = INETADDRESSTYPEIPV6;
+ oid_copy_in6_addr(
+ &name[oid_index],
+ &attr->mp_nexthop_global);
+ oid_index += sizeof(struct in6_addr);
+ }
+ }
+ *length = oid_index;
+ }
+ }
+ return pi;
+}
+
+static uint8_t *mplsL3vpnRteTable(struct variable *v, oid name[],
+ size_t *length, int exact, size_t *var_len,
+ WriteMethod **write_method)
+{
+ char vrf_name[VRF_NAMSIZ];
+ struct bgp *l3vpn_bgp;
+ struct bgp_dest *dest;
+ struct bgp_path_info *pi;
+ const struct prefix *p;
+ uint16_t policy = 0;
+
+ if (smux_header_table(v, name, length, exact, var_len, write_method)
+ == MATCH_FAILED)
+ return NULL;
+
+ memset(vrf_name, 0, VRF_NAMSIZ);
+ pi = bgpL3vpnRte_lookup(v, name, length, vrf_name, &l3vpn_bgp, &dest,
+ &policy, exact);
+
+
+ if (!pi)
+ return NULL;
+
+ p = bgp_dest_get_prefix(dest);
+
+ if (!p)
+ return NULL;
+
+ switch (v->magic) {
+ case MPLSL3VPNVRFRTEINETCIDRDESTTYPE:
+ switch (p->family) {
+ case AF_INET:
+ return SNMP_INTEGER(INETADDRESSTYPEIPV4);
+ case AF_INET6:
+ return SNMP_INTEGER(INETADDRESSTYPEIPV6);
+ default:
+ return SNMP_INTEGER(INETADDRESSTYPEUNKNOWN);
+ }
+ case MPLSL3VPNVRFRTEINETCIDRDEST:
+ switch (p->family) {
+ case AF_INET:
+ return SNMP_IPADDRESS(p->u.prefix4);
+ case AF_INET6:
+ return SNMP_IP6ADDRESS(p->u.prefix6);
+ default:
+ *var_len = 0;
+ return NULL;
+ }
+ case MPLSL3VPNVRFRTEINETCIDRPFXLEN:
+ return SNMP_INTEGER(p->prefixlen);
+ case MPLSL3VPNVRFRTEINETCIDRPOLICY:
+ *var_len = sizeof(mpls_l3vpn_policy_oid);
+ mpls_l3vpn_policy_oid[0] = policy >> 8;
+ mpls_l3vpn_policy_oid[1] = policy & 0xff;
+ return (uint8_t *)mpls_l3vpn_policy_oid;
+ case MPLSL3VPNVRFRTEINETCIDRNHOPTYPE:
+ if (!BGP_ATTR_NEXTHOP_AFI_IP6(pi->attr)) {
+ if (pi->attr->nexthop.s_addr == INADDR_ANY)
+ return SNMP_INTEGER(INETADDRESSTYPEUNKNOWN);
+ else
+ return SNMP_INTEGER(INETADDRESSTYPEIPV4);
+ } else if (IN6_IS_ADDR_UNSPECIFIED(
+ &pi->attr->mp_nexthop_global))
+ return SNMP_INTEGER(INETADDRESSTYPEUNKNOWN);
+ else
+ return SNMP_INTEGER(INETADDRESSTYPEIPV6);
+
+ case MPLSL3VPNVRFRTEINETCIDRNEXTHOP:
+ if (!BGP_ATTR_NEXTHOP_AFI_IP6(pi->attr))
+ if (pi->attr->nexthop.s_addr == INADDR_ANY) {
+ *var_len = 0;
+ return (uint8_t *)empty_nhop;
+ } else
+ return SNMP_IPADDRESS(pi->attr->nexthop);
+ else if (IN6_IS_ADDR_UNSPECIFIED(
+ &pi->attr->mp_nexthop_global)) {
+ *var_len = 0;
+ return (uint8_t *)empty_nhop;
+ } else
+ return SNMP_IP6ADDRESS(pi->attr->mp_nexthop_global);
+
+ case MPLSL3VPNVRFRTEINETCIDRIFINDEX:
+ if (pi->nexthop && pi->nexthop->nexthop)
+ return SNMP_INTEGER(pi->nexthop->nexthop->ifindex);
+ else
+ return SNMP_INTEGER(0);
+ case MPLSL3VPNVRFRTEINETCIDRTYPE:
+ if (pi->nexthop && pi->nexthop->nexthop) {
+ switch (pi->nexthop->nexthop->type) {
+ case NEXTHOP_TYPE_IFINDEX:
+ return SNMP_INTEGER(
+ MPLSL3VPNVRFRTECIDRTYPELOCAL);
+ case NEXTHOP_TYPE_IPV4:
+ case NEXTHOP_TYPE_IPV4_IFINDEX:
+ case NEXTHOP_TYPE_IPV6:
+ case NEXTHOP_TYPE_IPV6_IFINDEX:
+ return SNMP_INTEGER(
+ MPLSL3VPNVRFRTECIDRTYPEREMOTE);
+ case NEXTHOP_TYPE_BLACKHOLE:
+ switch (pi->nexthop->nexthop->bh_type) {
+ case BLACKHOLE_REJECT:
+ return SNMP_INTEGER(
+ MPLSL3VPNVRFRTECIDRTYPEREJECT);
+ default:
+ return SNMP_INTEGER(
+ MPLSL3VPNVRFRTECIDRTYPEBLACKHOLE);
+ }
+ default:
+ return SNMP_INTEGER(
+ MPLSL3VPNVRFRTECIDRTYPEOTHER);
+ }
+ } else
+ return SNMP_INTEGER(MPLSL3VPNVRFRTECIDRTYPEOTHER);
+ case MPLSL3VPNVRFRTEINETCIDRPROTO:
+ switch (pi->type) {
+ case ZEBRA_ROUTE_CONNECT:
+ return SNMP_INTEGER(IANAIPROUTEPROTOCOLLOCAL);
+ case ZEBRA_ROUTE_STATIC:
+ return SNMP_INTEGER(IANAIPROUTEPROTOCOLNETMGMT);
+ case ZEBRA_ROUTE_RIP:
+ case ZEBRA_ROUTE_RIPNG:
+ return SNMP_INTEGER(IANAIPROUTEPROTOCOLRIP);
+ case ZEBRA_ROUTE_OSPF:
+ case ZEBRA_ROUTE_OSPF6:
+ return SNMP_INTEGER(IANAIPROUTEPROTOCOLOSPF);
+ case ZEBRA_ROUTE_ISIS:
+ return SNMP_INTEGER(IANAIPROUTEPROTOCOLISIS);
+ case ZEBRA_ROUTE_BGP:
+ return SNMP_INTEGER(IANAIPROUTEPROTOCOLBGP);
+ case ZEBRA_ROUTE_EIGRP:
+ return SNMP_INTEGER(IANAIPROUTEPROTOCOLCISCOEIGRP);
+ default:
+ return SNMP_INTEGER(IANAIPROUTEPROTOCOLOTHER);
+ }
+ case MPLSL3VPNVRFRTEINETCIDRAGE:
+ return SNMP_INTEGER(pi->uptime);
+ case MPLSL3VPNVRFRTEINETCIDRNEXTHOPAS:
+ return SNMP_INTEGER(pi->peer ? pi->peer->as : 0);
+ case MPLSL3VPNVRFRTEINETCIDRMETRIC1:
+ if (pi->extra)
+ return SNMP_INTEGER(pi->extra->igpmetric);
+ else
+ return SNMP_INTEGER(0);
+ case MPLSL3VPNVRFRTEINETCIDRMETRIC2:
+ return SNMP_INTEGER(-1);
+ case MPLSL3VPNVRFRTEINETCIDRMETRIC3:
+ return SNMP_INTEGER(-1);
+ case MPLSL3VPNVRFRTEINETCIDRMETRIC4:
+ return SNMP_INTEGER(-1);
+ case MPLSL3VPNVRFRTEINETCIDRMETRIC5:
+ return SNMP_INTEGER(-1);
+ case MPLSL3VPNVRFRTEINETCIDRXCPOINTER:
+ return SNMP_OCTET(0);
+ case MPLSL3VPNVRFRTEINETCIDRSTATUS:
+ return SNMP_INTEGER(1);
+ }
+ return NULL;
+}
+
+void bgp_mpls_l3vpn_module_init(void)
+{
+ hook_register(bgp_vrf_status_changed, bgp_vrf_check_update_active);
+ hook_register(bgp_snmp_init_stats, bgp_init_snmp_stats);
+ hook_register(bgp_snmp_update_last_changed,
+ bgp_mpls_l3vpn_update_last_changed);
+ hook_register(bgp_snmp_update_stats, bgp_snmp_update_route_stats);
+ REGISTER_MIB("mplsL3VpnMIB", mpls_l3vpn_variables, variable,
+ mpls_l3vpn_oid);
+}
diff --git a/bgpd/bgp_mplsvpn_snmp.h b/bgpd/bgp_mplsvpn_snmp.h
new file mode 100644
index 0000000..781d5e9
--- /dev/null
+++ b/bgpd/bgp_mplsvpn_snmp.h
@@ -0,0 +1,31 @@
+/* MPLS/BGP L3VPN MIB
+ * Copyright (C) 2020 Volta Networks Inc
+ *
+ * This file is part of FRR.
+ *
+ * FRRouting is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License as published by the
+ * Free Software Foundation; either version 2, or (at your option) any
+ * later version.
+ *
+ * FRRouting is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program; see the file COPYING; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
+ */
+
+void bgp_mpls_l3vpn_module_init(void);
+
+#define MPLSL3VPNVRFRTECIDRTYPEOTHER 1
+#define MPLSL3VPNVRFRTECIDRTYPEREJECT 2
+#define MPLSL3VPNVRFRTECIDRTYPELOCAL 3
+#define MPLSL3VPNVRFRTECIDRTYPEREMOTE 4
+#define MPLSL3VPNVRFRTECIDRTYPEBLACKHOLE 5
+
+#define MPLSVPNVRFRTTYPEIMPORT 1
+#define MPLSVPNVRFRTTYPEEXPORT 2
+#define MPLSVPNVRFRTTYPEBOTH 3
diff --git a/bgpd/bgp_network.c b/bgpd/bgp_network.c
new file mode 100644
index 0000000..483f377
--- /dev/null
+++ b/bgpd/bgp_network.c
@@ -0,0 +1,977 @@
+/* BGP network related fucntions
+ * Copyright (C) 1999 Kunihiro Ishiguro
+ *
+ * This file is part of GNU Zebra.
+ *
+ * GNU Zebra is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License as published by the
+ * Free Software Foundation; either version 2, or (at your option) any
+ * later version.
+ *
+ * GNU Zebra is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program; see the file COPYING; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
+ */
+
+#include <zebra.h>
+
+#include "thread.h"
+#include "sockunion.h"
+#include "sockopt.h"
+#include "memory.h"
+#include "log.h"
+#include "if.h"
+#include "prefix.h"
+#include "command.h"
+#include "privs.h"
+#include "linklist.h"
+#include "network.h"
+#include "queue.h"
+#include "hash.h"
+#include "filter.h"
+#include "ns.h"
+#include "lib_errors.h"
+#include "nexthop.h"
+
+#include "bgpd/bgpd.h"
+#include "bgpd/bgp_open.h"
+#include "bgpd/bgp_fsm.h"
+#include "bgpd/bgp_attr.h"
+#include "bgpd/bgp_debug.h"
+#include "bgpd/bgp_errors.h"
+#include "bgpd/bgp_network.h"
+#include "bgpd/bgp_zebra.h"
+#include "bgpd/bgp_nht.h"
+
+extern struct zebra_privs_t bgpd_privs;
+
+static char *bgp_get_bound_name(struct peer *peer);
+
+void bgp_dump_listener_info(struct vty *vty)
+{
+ struct listnode *node;
+ struct bgp_listener *listener;
+
+ vty_out(vty, "Name fd Address\n");
+ vty_out(vty, "---------------------------\n");
+ for (ALL_LIST_ELEMENTS_RO(bm->listen_sockets, node, listener))
+ vty_out(vty, "%-16s %d %pSU\n",
+ listener->name ? listener->name : VRF_DEFAULT_NAME,
+ listener->fd, &listener->su);
+}
+
+/*
+ * Set MD5 key for the socket, for the given IPv4 peer address.
+ * If the password is NULL or zero-length, the option will be disabled.
+ */
+static int bgp_md5_set_socket(int socket, union sockunion *su,
+ uint16_t prefixlen, const char *password)
+{
+ int ret = -1;
+ int en = ENOSYS;
+#if HAVE_DECL_TCP_MD5SIG
+ union sockunion su2;
+#endif /* HAVE_TCP_MD5SIG */
+
+ assert(socket >= 0);
+
+#if HAVE_DECL_TCP_MD5SIG
+ /* Ensure there is no extraneous port information. */
+ memcpy(&su2, su, sizeof(union sockunion));
+ if (su2.sa.sa_family == AF_INET)
+ su2.sin.sin_port = 0;
+ else
+ su2.sin6.sin6_port = 0;
+
+ /* For addresses, use the non-extended signature functionality */
+ if ((su2.sa.sa_family == AF_INET && prefixlen == IPV4_MAX_BITLEN)
+ || (su2.sa.sa_family == AF_INET6 && prefixlen == IPV6_MAX_BITLEN))
+ ret = sockopt_tcp_signature(socket, &su2, password);
+ else
+ ret = sockopt_tcp_signature_ext(socket, &su2, prefixlen,
+ password);
+ en = errno;
+#endif /* HAVE_TCP_MD5SIG */
+
+ if (ret < 0) {
+ switch (ret) {
+ case -2:
+ flog_warn(
+ EC_BGP_NO_TCP_MD5,
+ "Unable to set TCP MD5 option on socket for peer %pSU (sock=%d): This platform does not support MD5 auth for prefixes",
+ su, socket);
+ break;
+ default:
+ flog_warn(
+ EC_BGP_NO_TCP_MD5,
+ "Unable to set TCP MD5 option on socket for peer %pSU (sock=%d): %s",
+ su, socket, safe_strerror(en));
+ }
+ }
+
+ return ret;
+}
+
+/* Helper for bgp_connect */
+static int bgp_md5_set_connect(int socket, union sockunion *su,
+ uint16_t prefixlen, const char *password)
+{
+ int ret = -1;
+
+#if HAVE_DECL_TCP_MD5SIG
+ frr_with_privs(&bgpd_privs) {
+ ret = bgp_md5_set_socket(socket, su, prefixlen, password);
+ }
+#endif /* HAVE_TCP_MD5SIG */
+
+ return ret;
+}
+
+static int bgp_md5_set_password(struct peer *peer, const char *password)
+{
+ struct listnode *node;
+ int ret = 0;
+ struct bgp_listener *listener;
+
+ /*
+ * Set or unset the password on the listen socket(s). Outbound
+ * connections are taken care of in bgp_connect() below.
+ */
+ frr_with_privs(&bgpd_privs) {
+ for (ALL_LIST_ELEMENTS_RO(bm->listen_sockets, node, listener))
+ if (listener->su.sa.sa_family ==
+ peer->su.sa.sa_family) {
+ uint16_t prefixlen =
+ peer->su.sa.sa_family == AF_INET
+ ? IPV4_MAX_BITLEN
+ : IPV6_MAX_BITLEN;
+
+ /*
+ * if we have stored a BGP vrf instance in the
+ * listener it must match the bgp instance in
+ * the peer otherwise the peer bgp instance
+ * must be the default vrf or a view instance
+ */
+ if (!listener->bgp) {
+ if (peer->bgp->vrf_id != VRF_DEFAULT)
+ continue;
+ } else if (listener->bgp != peer->bgp)
+ continue;
+
+ ret = bgp_md5_set_socket(listener->fd,
+ &peer->su, prefixlen,
+ password);
+ break;
+ }
+ }
+ return ret;
+}
+
+int bgp_md5_set_prefix(struct bgp *bgp, struct prefix *p, const char *password)
+{
+ int ret = 0;
+ union sockunion su;
+ struct listnode *node;
+ struct bgp_listener *listener;
+
+ /* Set or unset the password on the listen socket(s). */
+ frr_with_privs(&bgpd_privs) {
+ for (ALL_LIST_ELEMENTS_RO(bm->listen_sockets, node, listener))
+ if (listener->su.sa.sa_family == p->family
+ && ((bgp->vrf_id == VRF_DEFAULT)
+ || (listener->bgp == bgp))) {
+ prefix2sockunion(p, &su);
+ ret = bgp_md5_set_socket(listener->fd, &su,
+ p->prefixlen,
+ password);
+ break;
+ }
+ }
+
+ return ret;
+}
+
+int bgp_md5_unset_prefix(struct bgp *bgp, struct prefix *p)
+{
+ return bgp_md5_set_prefix(bgp, p, NULL);
+}
+
+int bgp_md5_set(struct peer *peer)
+{
+ /* Set the password from listen socket. */
+ return bgp_md5_set_password(peer, peer->password);
+}
+
+int bgp_md5_unset(struct peer *peer)
+{
+ /* Unset the password from listen socket. */
+ return bgp_md5_set_password(peer, NULL);
+}
+
+int bgp_set_socket_ttl(struct peer *peer, int bgp_sock)
+{
+ int ret = 0;
+
+ if (!peer->gtsm_hops) {
+ ret = sockopt_ttl(peer->su.sa.sa_family, bgp_sock, peer->ttl);
+ if (ret) {
+ flog_err(
+ EC_LIB_SOCKET,
+ "%s: Can't set TxTTL on peer (rtrid %pI4) socket, err = %d",
+ __func__, &peer->remote_id, errno);
+ return ret;
+ }
+ } else {
+ /* On Linux, setting minttl without setting ttl seems to mess
+ with the
+ outgoing ttl. Therefore setting both.
+ */
+ ret = sockopt_ttl(peer->su.sa.sa_family, bgp_sock, MAXTTL);
+ if (ret) {
+ flog_err(
+ EC_LIB_SOCKET,
+ "%s: Can't set TxTTL on peer (rtrid %pI4) socket, err = %d",
+ __func__, &peer->remote_id, errno);
+ return ret;
+ }
+ ret = sockopt_minttl(peer->su.sa.sa_family, bgp_sock,
+ MAXTTL + 1 - peer->gtsm_hops);
+ if (ret) {
+ flog_err(
+ EC_LIB_SOCKET,
+ "%s: Can't set MinTTL on peer (rtrid %pI4) socket, err = %d",
+ __func__, &peer->remote_id, errno);
+ return ret;
+ }
+ }
+
+ return ret;
+}
+
+/*
+ * Obtain the BGP instance that the incoming connection should be processed
+ * against. This is important because more than one VRF could be using the
+ * same IP address space. The instance is got by obtaining the device to
+ * which the incoming connection is bound to. This could either be a VRF
+ * or it could be an interface, which in turn determines the VRF.
+ */
+static int bgp_get_instance_for_inc_conn(int sock, struct bgp **bgp_inst)
+{
+#ifndef SO_BINDTODEVICE
+ /* only Linux has SO_BINDTODEVICE, but we're in Linux-specific code here
+ * anyway since the assumption is that the interface name returned by
+ * getsockopt() is useful in identifying the VRF, particularly with
+ * Linux's
+ * VRF l3master device. The whole mechanism is specific to Linux, so...
+ * when other platforms add VRF support, this will need handling here as
+ * well. (or, some restructuring) */
+ *bgp_inst = bgp_get_default();
+ return !*bgp_inst;
+
+#else
+ char name[VRF_NAMSIZ + 1];
+ socklen_t name_len = VRF_NAMSIZ;
+ struct bgp *bgp;
+ int rc;
+ struct listnode *node, *nnode;
+
+ *bgp_inst = NULL;
+ name[0] = '\0';
+ rc = getsockopt(sock, SOL_SOCKET, SO_BINDTODEVICE, name, &name_len);
+ if (rc != 0) {
+#if defined(HAVE_CUMULUS)
+ flog_err(EC_LIB_SOCKET,
+ "[Error] BGP SO_BINDTODEVICE get failed (%s), sock %d",
+ safe_strerror(errno), sock);
+ return -1;
+#endif
+ }
+
+ if (!strlen(name)) {
+ *bgp_inst = bgp_get_default();
+ return 0; /* default instance. */
+ }
+
+ /* First try match to instance; if that fails, check for interfaces. */
+ bgp = bgp_lookup_by_name(name);
+ if (bgp) {
+ if (!bgp->vrf_id) // unexpected
+ return -1;
+ *bgp_inst = bgp;
+ return 0;
+ }
+
+ /* TODO - This will be optimized once interfaces move into the NS */
+ for (ALL_LIST_ELEMENTS(bm->bgp, node, nnode, bgp)) {
+ struct interface *ifp;
+
+ if (bgp->inst_type == BGP_INSTANCE_TYPE_VIEW)
+ continue;
+
+ ifp = if_lookup_by_name(name, bgp->vrf_id);
+ if (ifp) {
+ *bgp_inst = bgp;
+ return 0;
+ }
+ }
+
+ /* We didn't match to either an instance or an interface. */
+ return -1;
+#endif
+}
+
+static void bgp_socket_set_buffer_size(const int fd)
+{
+ if (getsockopt_so_sendbuf(fd) < (int)bm->socket_buffer)
+ setsockopt_so_sendbuf(fd, bm->socket_buffer);
+ if (getsockopt_so_recvbuf(fd) < (int)bm->socket_buffer)
+ setsockopt_so_recvbuf(fd, bm->socket_buffer);
+}
+
+/* Accept bgp connection. */
+static void bgp_accept(struct thread *thread)
+{
+ int bgp_sock;
+ int accept_sock;
+ union sockunion su;
+ struct bgp_listener *listener = THREAD_ARG(thread);
+ struct peer *peer;
+ struct peer *peer1;
+ char buf[SU_ADDRSTRLEN];
+ struct bgp *bgp = NULL;
+
+ sockunion_init(&su);
+
+ bgp = bgp_lookup_by_name(listener->name);
+
+ /* Register accept thread. */
+ accept_sock = THREAD_FD(thread);
+ if (accept_sock < 0) {
+ flog_err_sys(EC_LIB_SOCKET,
+ "[Error] BGP accept socket fd is negative: %d",
+ accept_sock);
+ return;
+ }
+
+ thread_add_read(bm->master, bgp_accept, listener, accept_sock,
+ &listener->thread);
+
+ /* Accept client connection. */
+ bgp_sock = sockunion_accept(accept_sock, &su);
+ int save_errno = errno;
+ if (bgp_sock < 0) {
+ if (save_errno == EINVAL) {
+ struct vrf *vrf =
+ bgp ? vrf_lookup_by_id(bgp->vrf_id) : NULL;
+
+ /*
+ * It appears that sometimes, when VRFs are deleted on
+ * the system, it takes a little while for us to get
+ * notified about that. In the meantime we endlessly
+ * loop on accept(), because the socket, having been
+ * bound to a now-deleted VRF device, is in some weird
+ * state which causes accept() to fail.
+ *
+ * To avoid this, if we see accept() fail with EINVAL,
+ * we cancel ourselves and trust that when the VRF
+ * deletion notification comes in the event handler for
+ * that will take care of cleaning us up.
+ */
+ flog_err_sys(
+ EC_LIB_SOCKET,
+ "[Error] accept() failed with error \"%s\" on BGP listener socket %d for BGP instance in VRF \"%s\"; refreshing socket",
+ safe_strerror(save_errno), accept_sock,
+ VRF_LOGNAME(vrf));
+ THREAD_OFF(listener->thread);
+ } else {
+ flog_err_sys(
+ EC_LIB_SOCKET,
+ "[Error] BGP socket accept failed (%s); retrying",
+ safe_strerror(save_errno));
+ }
+ return;
+ }
+ set_nonblocking(bgp_sock);
+
+ /* Obtain BGP instance this connection is meant for.
+ * - if it is a VRF netns sock, then BGP is in listener structure
+ * - otherwise, the bgp instance need to be demultiplexed
+ */
+ if (listener->bgp)
+ bgp = listener->bgp;
+ else if (bgp_get_instance_for_inc_conn(bgp_sock, &bgp)) {
+ if (bgp_debug_neighbor_events(NULL))
+ zlog_debug(
+ "[Event] Could not get instance for incoming conn from %s",
+ inet_sutop(&su, buf));
+ close(bgp_sock);
+ return;
+ }
+
+ bgp_socket_set_buffer_size(bgp_sock);
+
+ /* Check remote IP address */
+ peer1 = peer_lookup(bgp, &su);
+
+ if (!peer1) {
+ peer1 = peer_lookup_dynamic_neighbor(bgp, &su);
+ if (peer1) {
+ /* Dynamic neighbor has been created, let it proceed */
+ peer1->fd = bgp_sock;
+
+ /* Set the user configured MSS to TCP socket */
+ if (CHECK_FLAG(peer1->flags, PEER_FLAG_TCP_MSS))
+ sockopt_tcp_mss_set(bgp_sock, peer1->tcp_mss);
+
+ bgp_fsm_change_status(peer1, Active);
+ THREAD_OFF(
+ peer1->t_start); /* created in peer_create() */
+
+ if (peer_active(peer1)) {
+ if (CHECK_FLAG(peer1->flags,
+ PEER_FLAG_TIMER_DELAYOPEN))
+ BGP_EVENT_ADD(
+ peer1,
+ TCP_connection_open_w_delay);
+ else
+ BGP_EVENT_ADD(peer1,
+ TCP_connection_open);
+ }
+
+ return;
+ }
+ }
+
+ if (!peer1) {
+ if (bgp_debug_neighbor_events(NULL)) {
+ zlog_debug(
+ "[Event] %s connection rejected(%s:%u:%s) - not configured and not valid for dynamic",
+ inet_sutop(&su, buf), bgp->name_pretty, bgp->as,
+ VRF_LOGNAME(vrf_lookup_by_id(bgp->vrf_id)));
+ }
+ close(bgp_sock);
+ return;
+ }
+
+ if (CHECK_FLAG(peer1->flags, PEER_FLAG_SHUTDOWN)
+ || CHECK_FLAG(peer1->bgp->flags, BGP_FLAG_SHUTDOWN)) {
+ if (bgp_debug_neighbor_events(peer1))
+ zlog_debug(
+ "[Event] connection from %s rejected(%s:%u:%s) due to admin shutdown",
+ inet_sutop(&su, buf), bgp->name_pretty, bgp->as,
+ VRF_LOGNAME(vrf_lookup_by_id(bgp->vrf_id)));
+ close(bgp_sock);
+ return;
+ }
+
+ /*
+ * Do not accept incoming connections in Clearing state. This can result
+ * in incorect state transitions - e.g., the connection goes back to
+ * Established and then the Clearing_Completed event is generated. Also,
+ * block incoming connection in Deleted state.
+ */
+ if (peer1->status == Clearing || peer1->status == Deleted) {
+ if (bgp_debug_neighbor_events(peer1))
+ zlog_debug(
+ "[Event] Closing incoming conn for %s (%p) state %d",
+ peer1->host, peer1, peer1->status);
+ close(bgp_sock);
+ return;
+ }
+
+ /* Check that at least one AF is activated for the peer. */
+ if (!peer_active(peer1)) {
+ if (bgp_debug_neighbor_events(peer1))
+ zlog_debug(
+ "%s - incoming conn rejected - no AF activated for peer",
+ peer1->host);
+ close(bgp_sock);
+ return;
+ }
+
+ /* Do not try to reconnect if the peer reached maximum
+ * prefixes, restart timer is still running or the peer
+ * is shutdown.
+ */
+ if (BGP_PEER_START_SUPPRESSED(peer1)) {
+ if (bgp_debug_neighbor_events(peer1))
+ zlog_debug(
+ "[Event] Incoming BGP connection rejected from %s due to maximum-prefix or shutdown",
+ peer1->host);
+ close(bgp_sock);
+ return;
+ }
+
+ if (bgp_debug_neighbor_events(peer1))
+ zlog_debug("[Event] BGP connection from host %s fd %d",
+ inet_sutop(&su, buf), bgp_sock);
+
+ if (peer1->doppelganger) {
+ /* We have an existing connection. Kill the existing one and run
+ with this one.
+ */
+ if (bgp_debug_neighbor_events(peer1))
+ zlog_debug(
+ "[Event] New active connection from peer %s, Killing previous active connection",
+ peer1->host);
+ peer_delete(peer1->doppelganger);
+ }
+
+ if (bgp_set_socket_ttl(peer1, bgp_sock) < 0)
+ if (bgp_debug_neighbor_events(peer1))
+ zlog_debug(
+ "[Event] Unable to set min/max TTL on peer %s, Continuing",
+ peer1->host);
+
+ peer = peer_create(&su, peer1->conf_if, peer1->bgp, peer1->local_as,
+ peer1->as, peer1->as_type, NULL);
+ hash_release(peer->bgp->peerhash, peer);
+ (void)hash_get(peer->bgp->peerhash, peer, hash_alloc_intern);
+
+ peer_xfer_config(peer, peer1);
+ bgp_peer_gr_flags_update(peer);
+
+ BGP_GR_ROUTER_DETECT_AND_SEND_CAPABILITY_TO_ZEBRA(peer->bgp,
+ peer->bgp->peer);
+
+ if (bgp_peer_gr_mode_get(peer) == PEER_DISABLE) {
+
+ UNSET_FLAG(peer->sflags, PEER_STATUS_NSF_MODE);
+
+ if (CHECK_FLAG(peer->sflags, PEER_STATUS_NSF_WAIT)) {
+ peer_nsf_stop(peer);
+ }
+ }
+
+ UNSET_FLAG(peer->flags, PEER_FLAG_CONFIG_NODE);
+
+ peer->doppelganger = peer1;
+ peer1->doppelganger = peer;
+ peer->fd = bgp_sock;
+ frr_with_privs(&bgpd_privs) {
+ vrf_bind(peer->bgp->vrf_id, bgp_sock, bgp_get_bound_name(peer));
+ }
+ bgp_peer_reg_with_nht(peer);
+ bgp_fsm_change_status(peer, Active);
+ THREAD_OFF(peer->t_start); /* created in peer_create() */
+
+ SET_FLAG(peer->sflags, PEER_STATUS_ACCEPT_PEER);
+ /* Make dummy peer until read Open packet. */
+ if (peer_established(peer1)
+ && CHECK_FLAG(peer1->sflags, PEER_STATUS_NSF_MODE)) {
+ /* If we have an existing established connection with graceful
+ * restart
+ * capability announced with one or more address families, then
+ * drop
+ * existing established connection and move state to connect.
+ */
+ peer1->last_reset = PEER_DOWN_NSF_CLOSE_SESSION;
+
+ if (CHECK_FLAG(peer1->flags, PEER_FLAG_GRACEFUL_RESTART)
+ || CHECK_FLAG(peer1->flags,
+ PEER_FLAG_GRACEFUL_RESTART_HELPER))
+ SET_FLAG(peer1->sflags, PEER_STATUS_NSF_WAIT);
+
+ bgp_event_update(peer1, TCP_connection_closed);
+ }
+
+ if (peer_active(peer)) {
+ if (CHECK_FLAG(peer->flags, PEER_FLAG_TIMER_DELAYOPEN))
+ BGP_EVENT_ADD(peer, TCP_connection_open_w_delay);
+ else
+ BGP_EVENT_ADD(peer, TCP_connection_open);
+ }
+
+ /*
+ * If we are doing nht for a peer that is v6 LL based
+ * massage the event system to make things happy
+ */
+ bgp_nht_interface_events(peer);
+}
+
+/* BGP socket bind. */
+static char *bgp_get_bound_name(struct peer *peer)
+{
+ if (!peer)
+ return NULL;
+
+ if ((peer->bgp->vrf_id == VRF_DEFAULT) && !peer->ifname
+ && !peer->conf_if)
+ return NULL;
+
+ if (peer->su.sa.sa_family != AF_INET
+ && peer->su.sa.sa_family != AF_INET6)
+ return NULL; // unexpected
+
+ /* For IPv6 peering, interface (unnumbered or link-local with interface)
+ * takes precedence over VRF. For IPv4 peering, explicit interface or
+ * VRF are the situations to bind.
+ */
+ if (peer->su.sa.sa_family == AF_INET6 && peer->conf_if)
+ return peer->conf_if;
+
+ if (peer->ifname)
+ return peer->ifname;
+
+ if (peer->bgp->inst_type == BGP_INSTANCE_TYPE_VIEW)
+ return NULL;
+
+ return peer->bgp->name;
+}
+
+int bgp_update_address(struct interface *ifp, const union sockunion *dst,
+ union sockunion *addr)
+{
+ struct prefix *p, *sel, d;
+ struct connected *connected;
+ struct listnode *node;
+ int common;
+
+ if (!sockunion2hostprefix(dst, &d))
+ return 1;
+
+ sel = NULL;
+ common = -1;
+
+ for (ALL_LIST_ELEMENTS_RO(ifp->connected, node, connected)) {
+ p = connected->address;
+ if (p->family != d.family)
+ continue;
+ if (prefix_common_bits(p, &d) > common) {
+ sel = p;
+ common = prefix_common_bits(sel, &d);
+ }
+ }
+
+ if (!sel)
+ return 1;
+
+ prefix2sockunion(sel, addr);
+ return 0;
+}
+
+/* Update source selection. */
+static int bgp_update_source(struct peer *peer)
+{
+ struct interface *ifp;
+ union sockunion addr;
+ int ret = 0;
+
+ sockunion_init(&addr);
+
+ /* Source is specified with interface name. */
+ if (peer->update_if) {
+ ifp = if_lookup_by_name(peer->update_if, peer->bgp->vrf_id);
+ if (!ifp)
+ return -1;
+
+ if (bgp_update_address(ifp, &peer->su, &addr))
+ return -1;
+
+ ret = sockunion_bind(peer->fd, &addr, 0, &addr);
+ }
+
+ /* Source is specified with IP address. */
+ if (peer->update_source)
+ ret = sockunion_bind(peer->fd, peer->update_source, 0,
+ peer->update_source);
+
+ return ret;
+}
+
+/* BGP try to connect to the peer. */
+int bgp_connect(struct peer *peer)
+{
+ assert(!CHECK_FLAG(peer->thread_flags, PEER_THREAD_WRITES_ON));
+ assert(!CHECK_FLAG(peer->thread_flags, PEER_THREAD_READS_ON));
+ ifindex_t ifindex = 0;
+
+ if (peer->conf_if && BGP_PEER_SU_UNSPEC(peer)) {
+ if (bgp_debug_neighbor_events(peer))
+ zlog_debug("Peer address not learnt: Returning from connect");
+ return 0;
+ }
+ frr_with_privs(&bgpd_privs) {
+ /* Make socket for the peer. */
+ peer->fd = vrf_sockunion_socket(&peer->su, peer->bgp->vrf_id,
+ bgp_get_bound_name(peer));
+ }
+ if (peer->fd < 0) {
+ peer->last_reset = PEER_DOWN_SOCKET_ERROR;
+ if (bgp_debug_neighbor_events(peer))
+ zlog_debug("%s: Failure to create socket for connection to %s, error received: %s(%d)",
+ __func__, peer->host, safe_strerror(errno),
+ errno);
+ return -1;
+ }
+
+ set_nonblocking(peer->fd);
+
+ /* Set the user configured MSS to TCP socket */
+ if (CHECK_FLAG(peer->flags, PEER_FLAG_TCP_MSS))
+ sockopt_tcp_mss_set(peer->fd, peer->tcp_mss);
+
+ bgp_socket_set_buffer_size(peer->fd);
+
+ if (bgp_set_socket_ttl(peer, peer->fd) < 0) {
+ peer->last_reset = PEER_DOWN_SOCKET_ERROR;
+ if (bgp_debug_neighbor_events(peer))
+ zlog_debug("%s: Failure to set socket ttl for connection to %s, error received: %s(%d)",
+ __func__, peer->host, safe_strerror(errno),
+ errno);
+ return -1;
+ }
+
+ sockopt_reuseaddr(peer->fd);
+ sockopt_reuseport(peer->fd);
+
+#ifdef IPTOS_PREC_INTERNETCONTROL
+ frr_with_privs(&bgpd_privs) {
+ if (sockunion_family(&peer->su) == AF_INET)
+ setsockopt_ipv4_tos(peer->fd, bm->tcp_dscp);
+ else if (sockunion_family(&peer->su) == AF_INET6)
+ setsockopt_ipv6_tclass(peer->fd, bm->tcp_dscp);
+ }
+#endif
+
+ if (peer->password) {
+ uint16_t prefixlen = peer->su.sa.sa_family == AF_INET
+ ? IPV4_MAX_BITLEN
+ : IPV6_MAX_BITLEN;
+
+ bgp_md5_set_connect(peer->fd, &peer->su, prefixlen,
+ peer->password);
+ }
+
+ /* Update source bind. */
+ if (bgp_update_source(peer) < 0) {
+ peer->last_reset = PEER_DOWN_SOCKET_ERROR;
+ return connect_error;
+ }
+
+ if (peer->conf_if || peer->ifname)
+ ifindex = ifname2ifindex(peer->conf_if ? peer->conf_if
+ : peer->ifname,
+ peer->bgp->vrf_id);
+
+ if (bgp_debug_neighbor_events(peer))
+ zlog_debug("%s [Event] Connect start to %s fd %d", peer->host,
+ peer->host, peer->fd);
+
+ /* Connect to the remote peer. */
+ return sockunion_connect(peer->fd, &peer->su, htons(peer->port),
+ ifindex);
+}
+
+/* After TCP connection is established. Get local address and port. */
+int bgp_getsockname(struct peer *peer)
+{
+ if (peer->su_local) {
+ sockunion_free(peer->su_local);
+ peer->su_local = NULL;
+ }
+
+ if (peer->su_remote) {
+ sockunion_free(peer->su_remote);
+ peer->su_remote = NULL;
+ }
+
+ peer->su_local = sockunion_getsockname(peer->fd);
+ if (!peer->su_local)
+ return -1;
+ peer->su_remote = sockunion_getpeername(peer->fd);
+ if (!peer->su_remote)
+ return -1;
+
+ if (!bgp_zebra_nexthop_set(peer->su_local, peer->su_remote,
+ &peer->nexthop, peer)) {
+ flog_err(
+ EC_BGP_NH_UPD,
+ "%s: nexthop_set failed, resetting connection - intf %s",
+ peer->host,
+ peer->nexthop.ifp ? peer->nexthop.ifp->name
+ : "(Unknown)");
+ return -1;
+ }
+ return 0;
+}
+
+
+static int bgp_listener(int sock, struct sockaddr *sa, socklen_t salen,
+ struct bgp *bgp)
+{
+ struct bgp_listener *listener;
+ int ret, en;
+
+ sockopt_reuseaddr(sock);
+ sockopt_reuseport(sock);
+
+ frr_with_privs(&bgpd_privs) {
+
+#ifdef IPTOS_PREC_INTERNETCONTROL
+ if (sa->sa_family == AF_INET)
+ setsockopt_ipv4_tos(sock, bm->tcp_dscp);
+ else if (sa->sa_family == AF_INET6)
+ setsockopt_ipv6_tclass(sock, bm->tcp_dscp);
+#endif
+
+ sockopt_v6only(sa->sa_family, sock);
+
+ ret = bind(sock, sa, salen);
+ en = errno;
+ }
+
+ if (ret < 0) {
+ flog_err_sys(EC_LIB_SOCKET, "bind: %s", safe_strerror(en));
+ return ret;
+ }
+
+ ret = listen(sock, SOMAXCONN);
+ if (ret < 0) {
+ flog_err_sys(EC_LIB_SOCKET, "listen: %s", safe_strerror(errno));
+ return ret;
+ }
+
+ listener = XCALLOC(MTYPE_BGP_LISTENER, sizeof(*listener));
+ listener->fd = sock;
+ listener->name = XSTRDUP(MTYPE_BGP_LISTENER, bgp->name);
+
+ /* this socket is in a vrf record bgp back pointer */
+ if (bgp->vrf_id != VRF_DEFAULT)
+ listener->bgp = bgp;
+
+ memcpy(&listener->su, sa, salen);
+ thread_add_read(bm->master, bgp_accept, listener, sock,
+ &listener->thread);
+ listnode_add(bm->listen_sockets, listener);
+
+ return 0;
+}
+
+/* IPv6 supported version of BGP server socket setup. */
+int bgp_socket(struct bgp *bgp, unsigned short port, const char *address)
+{
+ struct addrinfo *ainfo;
+ struct addrinfo *ainfo_save;
+ static const struct addrinfo req = {
+ .ai_family = AF_UNSPEC,
+ .ai_flags = AI_PASSIVE,
+ .ai_socktype = SOCK_STREAM,
+ };
+ int ret, count;
+ char port_str[BUFSIZ];
+
+ snprintf(port_str, sizeof(port_str), "%d", port);
+ port_str[sizeof(port_str) - 1] = '\0';
+
+ frr_with_privs(&bgpd_privs) {
+ ret = vrf_getaddrinfo(address, port_str, &req, &ainfo_save,
+ bgp->vrf_id);
+ }
+ if (ret != 0) {
+ flog_err_sys(EC_LIB_SOCKET, "getaddrinfo: %s",
+ gai_strerror(ret));
+ return -1;
+ }
+ if (bgp_option_check(BGP_OPT_NO_ZEBRA) &&
+ bgp->vrf_id != VRF_DEFAULT) {
+ freeaddrinfo(ainfo_save);
+ return -1;
+ }
+ count = 0;
+ for (ainfo = ainfo_save; ainfo; ainfo = ainfo->ai_next) {
+ int sock;
+
+ if (ainfo->ai_family != AF_INET && ainfo->ai_family != AF_INET6)
+ continue;
+
+ frr_with_privs(&bgpd_privs) {
+ sock = vrf_socket(ainfo->ai_family,
+ ainfo->ai_socktype,
+ ainfo->ai_protocol,
+ bgp->vrf_id,
+ (bgp->inst_type
+ == BGP_INSTANCE_TYPE_VRF
+ ? bgp->name : NULL));
+ }
+ if (sock < 0) {
+ flog_err_sys(EC_LIB_SOCKET, "socket: %s",
+ safe_strerror(errno));
+ continue;
+ }
+
+ /* if we intend to implement ttl-security, this socket needs
+ * ttl=255 */
+ sockopt_ttl(ainfo->ai_family, sock, MAXTTL);
+
+ ret = bgp_listener(sock, ainfo->ai_addr, ainfo->ai_addrlen,
+ bgp);
+ if (ret == 0)
+ ++count;
+ else
+ close(sock);
+ }
+ freeaddrinfo(ainfo_save);
+ if (count == 0 && bgp->inst_type != BGP_INSTANCE_TYPE_VRF) {
+ flog_err(
+ EC_LIB_SOCKET,
+ "%s: no usable addresses please check other programs usage of specified port %d",
+ __func__, port);
+ flog_err_sys(EC_LIB_SOCKET, "%s: Program cannot continue",
+ __func__);
+ exit(-1);
+ }
+
+ return 0;
+}
+
+/* this function closes vrf socket
+ * this should be called only for vrf socket with netns backend
+ */
+void bgp_close_vrf_socket(struct bgp *bgp)
+{
+ struct listnode *node, *next;
+ struct bgp_listener *listener;
+
+ if (!bgp)
+ return;
+
+ if (bm->listen_sockets == NULL)
+ return;
+
+ for (ALL_LIST_ELEMENTS(bm->listen_sockets, node, next, listener)) {
+ if (listener->bgp == bgp) {
+ THREAD_OFF(listener->thread);
+ close(listener->fd);
+ listnode_delete(bm->listen_sockets, listener);
+ XFREE(MTYPE_BGP_LISTENER, listener->name);
+ XFREE(MTYPE_BGP_LISTENER, listener);
+ }
+ }
+}
+
+/* this function closes main socket
+ */
+void bgp_close(void)
+{
+ struct listnode *node, *next;
+ struct bgp_listener *listener;
+
+ if (bm->listen_sockets == NULL)
+ return;
+
+ for (ALL_LIST_ELEMENTS(bm->listen_sockets, node, next, listener)) {
+ if (listener->bgp)
+ continue;
+ THREAD_OFF(listener->thread);
+ close(listener->fd);
+ listnode_delete(bm->listen_sockets, listener);
+ XFREE(MTYPE_BGP_LISTENER, listener->name);
+ XFREE(MTYPE_BGP_LISTENER, listener);
+ }
+}
diff --git a/bgpd/bgp_network.h b/bgpd/bgp_network.h
new file mode 100644
index 0000000..7420397
--- /dev/null
+++ b/bgpd/bgp_network.h
@@ -0,0 +1,51 @@
+/* BGP network related header
+ * Copyright (C) 1999 Kunihiro Ishiguro
+ *
+ * This file is part of GNU Zebra.
+ *
+ * GNU Zebra is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License as published by the
+ * Free Software Foundation; either version 2, or (at your option) any
+ * later version.
+ *
+ * GNU Zebra is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program; see the file COPYING; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
+ */
+
+#ifndef _QUAGGA_BGP_NETWORK_H
+#define _QUAGGA_BGP_NETWORK_H
+
+#define BGP_SOCKET_SNDBUF_SIZE 65536
+
+struct bgp_listener {
+ int fd;
+ union sockunion su;
+ struct thread *thread;
+ struct bgp *bgp;
+ char *name;
+};
+
+extern void bgp_dump_listener_info(struct vty *vty);
+extern int bgp_socket(struct bgp *bgp, unsigned short port,
+ const char *address);
+extern void bgp_close_vrf_socket(struct bgp *bgp);
+extern void bgp_close(void);
+extern int bgp_connect(struct peer *);
+extern int bgp_getsockname(struct peer *);
+
+extern int bgp_md5_set_prefix(struct bgp *bgp, struct prefix *p,
+ const char *password);
+extern int bgp_md5_unset_prefix(struct bgp *bgp, struct prefix *p);
+extern int bgp_md5_set(struct peer *);
+extern int bgp_md5_unset(struct peer *);
+extern int bgp_set_socket_ttl(struct peer *, int fd);
+extern int bgp_update_address(struct interface *ifp, const union sockunion *dst,
+ union sockunion *addr);
+
+#endif /* _QUAGGA_BGP_NETWORK_H */
diff --git a/bgpd/bgp_nexthop.c b/bgpd/bgp_nexthop.c
new file mode 100644
index 0000000..8a6ddb5
--- /dev/null
+++ b/bgpd/bgp_nexthop.c
@@ -0,0 +1,1086 @@
+/* BGP nexthop scan
+ * Copyright (C) 2000 Kunihiro Ishiguro
+ *
+ * This file is part of GNU Zebra.
+ *
+ * GNU Zebra is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License as published by the
+ * Free Software Foundation; either version 2, or (at your option) any
+ * later version.
+ *
+ * GNU Zebra is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program; see the file COPYING; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
+ */
+
+#include <zebra.h>
+
+#include "command.h"
+#include "thread.h"
+#include "prefix.h"
+#include "zclient.h"
+#include "stream.h"
+#include "network.h"
+#include "log.h"
+#include "memory.h"
+#include "hash.h"
+#include "jhash.h"
+#include "nexthop.h"
+#include "queue.h"
+#include "filter.h"
+#include "printfrr.h"
+
+#include "bgpd/bgpd.h"
+#include "bgpd/bgp_route.h"
+#include "bgpd/bgp_attr.h"
+#include "bgpd/bgp_nexthop.h"
+#include "bgpd/bgp_nht.h"
+#include "bgpd/bgp_debug.h"
+#include "bgpd/bgp_damp.h"
+#include "bgpd/bgp_fsm.h"
+#include "bgpd/bgp_vty.h"
+#include "bgpd/bgp_rd.h"
+
+DEFINE_MTYPE_STATIC(BGPD, MARTIAN_STRING, "BGP Martian Addr Intf String");
+
+int bgp_nexthop_cache_compare(const struct bgp_nexthop_cache *a,
+ const struct bgp_nexthop_cache *b)
+{
+ if (a->srte_color < b->srte_color)
+ return -1;
+ if (a->srte_color > b->srte_color)
+ return 1;
+
+ if (a->ifindex < b->ifindex)
+ return -1;
+ if (a->ifindex > b->ifindex)
+ return 1;
+
+ return prefix_cmp(&a->prefix, &b->prefix);
+}
+
+void bnc_nexthop_free(struct bgp_nexthop_cache *bnc)
+{
+ nexthops_free(bnc->nexthop);
+}
+
+struct bgp_nexthop_cache *bnc_new(struct bgp_nexthop_cache_head *tree,
+ struct prefix *prefix, uint32_t srte_color,
+ ifindex_t ifindex)
+{
+ struct bgp_nexthop_cache *bnc;
+
+ bnc = XCALLOC(MTYPE_BGP_NEXTHOP_CACHE,
+ sizeof(struct bgp_nexthop_cache));
+ bnc->prefix = *prefix;
+ bnc->ifindex = ifindex;
+ bnc->srte_color = srte_color;
+ bnc->tree = tree;
+ LIST_INIT(&(bnc->paths));
+ bgp_nexthop_cache_add(tree, bnc);
+
+ return bnc;
+}
+
+bool bnc_existing_for_prefix(struct bgp_nexthop_cache *bnc)
+{
+ struct bgp_nexthop_cache *bnc_tmp;
+
+ frr_each (bgp_nexthop_cache, bnc->tree, bnc_tmp) {
+ if (bnc_tmp == bnc)
+ continue;
+ if (prefix_cmp(&bnc->prefix, &bnc_tmp->prefix) == 0)
+ return true;
+ }
+ return false;
+}
+
+void bnc_free(struct bgp_nexthop_cache *bnc)
+{
+ bnc_nexthop_free(bnc);
+ bgp_nexthop_cache_del(bnc->tree, bnc);
+ XFREE(MTYPE_BGP_NEXTHOP_CACHE, bnc);
+}
+
+struct bgp_nexthop_cache *bnc_find(struct bgp_nexthop_cache_head *tree,
+ struct prefix *prefix, uint32_t srte_color,
+ ifindex_t ifindex)
+{
+ struct bgp_nexthop_cache bnc = {};
+
+ if (!tree)
+ return NULL;
+
+ bnc.prefix = *prefix;
+ bnc.srte_color = srte_color;
+ bnc.ifindex = ifindex;
+ return bgp_nexthop_cache_find(tree, &bnc);
+}
+
+/* Reset and free all BGP nexthop cache. */
+static void bgp_nexthop_cache_reset(struct bgp_nexthop_cache_head *tree)
+{
+ struct bgp_nexthop_cache *bnc;
+
+ while (bgp_nexthop_cache_count(tree) > 0) {
+ bnc = bgp_nexthop_cache_first(tree);
+
+ while (!LIST_EMPTY(&(bnc->paths))) {
+ struct bgp_path_info *path = LIST_FIRST(&(bnc->paths));
+
+ path_nh_map(path, bnc, false);
+ }
+
+ bnc_free(bnc);
+ }
+}
+
+static void *bgp_tip_hash_alloc(void *p)
+{
+ const struct in_addr *val = (const struct in_addr *)p;
+ struct tip_addr *addr;
+
+ addr = XMALLOC(MTYPE_TIP_ADDR, sizeof(struct tip_addr));
+ addr->refcnt = 0;
+ addr->addr.s_addr = val->s_addr;
+
+ return addr;
+}
+
+static void bgp_tip_hash_free(void *addr)
+{
+ XFREE(MTYPE_TIP_ADDR, addr);
+}
+
+static unsigned int bgp_tip_hash_key_make(const void *p)
+{
+ const struct tip_addr *addr = p;
+
+ return jhash_1word(addr->addr.s_addr, 0);
+}
+
+static bool bgp_tip_hash_cmp(const void *p1, const void *p2)
+{
+ const struct tip_addr *addr1 = p1;
+ const struct tip_addr *addr2 = p2;
+
+ return addr1->addr.s_addr == addr2->addr.s_addr;
+}
+
+void bgp_tip_hash_init(struct bgp *bgp)
+{
+ bgp->tip_hash = hash_create(bgp_tip_hash_key_make, bgp_tip_hash_cmp,
+ "BGP TIP hash");
+}
+
+void bgp_tip_hash_destroy(struct bgp *bgp)
+{
+ if (bgp->tip_hash == NULL)
+ return;
+ hash_clean(bgp->tip_hash, bgp_tip_hash_free);
+ hash_free(bgp->tip_hash);
+ bgp->tip_hash = NULL;
+}
+
+void bgp_tip_add(struct bgp *bgp, struct in_addr *tip)
+{
+ struct tip_addr tmp;
+ struct tip_addr *addr;
+
+ tmp.addr = *tip;
+
+ addr = hash_get(bgp->tip_hash, &tmp, bgp_tip_hash_alloc);
+ addr->refcnt++;
+}
+
+void bgp_tip_del(struct bgp *bgp, struct in_addr *tip)
+{
+ struct tip_addr tmp;
+ struct tip_addr *addr;
+
+ tmp.addr = *tip;
+
+ addr = hash_lookup(bgp->tip_hash, &tmp);
+ /* may have been deleted earlier by bgp_interface_down() */
+ if (addr == NULL)
+ return;
+
+ addr->refcnt--;
+
+ if (addr->refcnt == 0) {
+ hash_release(bgp->tip_hash, addr);
+ XFREE(MTYPE_TIP_ADDR, addr);
+ }
+}
+
+/* BGP own address structure */
+struct bgp_addr {
+ struct prefix p;
+ struct list *ifp_name_list;
+};
+
+static void show_address_entry(struct hash_bucket *bucket, void *args)
+{
+ struct vty *vty = (struct vty *)args;
+ struct bgp_addr *addr = (struct bgp_addr *)bucket->data;
+ char *name;
+ struct listnode *node;
+ char str[INET6_ADDRSTRLEN] = {0};
+
+ vty_out(vty, "addr: %s, count: %d : ",
+ inet_ntop(addr->p.family, &(addr->p.u.prefix),
+ str, INET6_ADDRSTRLEN),
+ addr->ifp_name_list->count);
+
+ for (ALL_LIST_ELEMENTS_RO(addr->ifp_name_list, node, name)) {
+ vty_out(vty, " %s,", name);
+ }
+
+ vty_out(vty, "\n");
+}
+
+void bgp_nexthop_show_address_hash(struct vty *vty, struct bgp *bgp)
+{
+ hash_iterate(bgp->address_hash,
+ (void (*)(struct hash_bucket *, void *))show_address_entry,
+ vty);
+}
+
+static void bgp_address_hash_string_del(void *val)
+{
+ char *data = val;
+
+ XFREE(MTYPE_MARTIAN_STRING, data);
+}
+
+static void *bgp_address_hash_alloc(void *p)
+{
+ struct bgp_addr *copy_addr = p;
+ struct bgp_addr *addr = NULL;
+
+ addr = XMALLOC(MTYPE_BGP_ADDR, sizeof(struct bgp_addr));
+ prefix_copy(&addr->p, &copy_addr->p);
+
+ addr->ifp_name_list = list_new();
+ addr->ifp_name_list->del = bgp_address_hash_string_del;
+
+ return addr;
+}
+
+static void bgp_address_hash_free(void *data)
+{
+ struct bgp_addr *addr = data;
+
+ list_delete(&addr->ifp_name_list);
+ XFREE(MTYPE_BGP_ADDR, addr);
+}
+
+static unsigned int bgp_address_hash_key_make(const void *p)
+{
+ const struct bgp_addr *addr = p;
+
+ return prefix_hash_key(&addr->p);
+}
+
+static bool bgp_address_hash_cmp(const void *p1, const void *p2)
+{
+ const struct bgp_addr *addr1 = p1;
+ const struct bgp_addr *addr2 = p2;
+
+ return prefix_same(&addr1->p, &addr2->p);
+}
+
+void bgp_address_init(struct bgp *bgp)
+{
+ bgp->address_hash =
+ hash_create(bgp_address_hash_key_make, bgp_address_hash_cmp,
+ "BGP Connected Address Hash");
+}
+
+void bgp_address_destroy(struct bgp *bgp)
+{
+ if (bgp->address_hash == NULL)
+ return;
+ hash_clean(bgp->address_hash, bgp_address_hash_free);
+ hash_free(bgp->address_hash);
+ bgp->address_hash = NULL;
+}
+
+static void bgp_address_add(struct bgp *bgp, struct connected *ifc,
+ struct prefix *p)
+{
+ struct bgp_addr tmp;
+ struct bgp_addr *addr;
+ struct listnode *node;
+ char *name;
+
+ tmp.p = *p;
+
+ if (tmp.p.family == AF_INET)
+ tmp.p.prefixlen = IPV4_MAX_BITLEN;
+ else if (tmp.p.family == AF_INET6)
+ tmp.p.prefixlen = IPV6_MAX_BITLEN;
+
+ addr = hash_get(bgp->address_hash, &tmp, bgp_address_hash_alloc);
+
+ for (ALL_LIST_ELEMENTS_RO(addr->ifp_name_list, node, name)) {
+ if (strcmp(ifc->ifp->name, name) == 0)
+ break;
+ }
+ if (!node) {
+ name = XSTRDUP(MTYPE_MARTIAN_STRING, ifc->ifp->name);
+ listnode_add(addr->ifp_name_list, name);
+ }
+}
+
+static void bgp_address_del(struct bgp *bgp, struct connected *ifc,
+ struct prefix *p)
+{
+ struct bgp_addr tmp;
+ struct bgp_addr *addr;
+ struct listnode *node;
+ char *name;
+
+ tmp.p = *p;
+
+ if (tmp.p.family == AF_INET)
+ tmp.p.prefixlen = IPV4_MAX_BITLEN;
+ else if (tmp.p.family == AF_INET6)
+ tmp.p.prefixlen = IPV6_MAX_BITLEN;
+
+ addr = hash_lookup(bgp->address_hash, &tmp);
+ /* may have been deleted earlier by bgp_interface_down() */
+ if (addr == NULL)
+ return;
+
+ for (ALL_LIST_ELEMENTS_RO(addr->ifp_name_list, node, name)) {
+ if (strcmp(ifc->ifp->name, name) == 0)
+ break;
+ }
+
+ if (node) {
+ list_delete_node(addr->ifp_name_list, node);
+ XFREE(MTYPE_MARTIAN_STRING, name);
+ }
+
+ if (addr->ifp_name_list->count == 0) {
+ hash_release(bgp->address_hash, addr);
+ list_delete(&addr->ifp_name_list);
+ XFREE(MTYPE_BGP_ADDR, addr);
+ }
+}
+
+
+struct bgp_connected_ref {
+ unsigned int refcnt;
+};
+
+void bgp_connected_add(struct bgp *bgp, struct connected *ifc)
+{
+ struct prefix p;
+ struct prefix *addr;
+ struct bgp_dest *dest;
+ struct bgp_connected_ref *bc;
+ struct listnode *node, *nnode;
+ struct peer *peer;
+
+ addr = ifc->address;
+
+ p = *(CONNECTED_PREFIX(ifc));
+ if (addr->family == AF_INET) {
+ apply_mask_ipv4((struct prefix_ipv4 *)&p);
+
+ if (prefix_ipv4_any((struct prefix_ipv4 *)&p))
+ return;
+
+ bgp_address_add(bgp, ifc, addr);
+
+ dest = bgp_node_get(bgp->connected_table[AFI_IP], &p);
+ bc = bgp_dest_get_bgp_connected_ref_info(dest);
+ if (bc)
+ bc->refcnt++;
+ else {
+ bc = XCALLOC(MTYPE_BGP_CONN,
+ sizeof(struct bgp_connected_ref));
+ bc->refcnt = 1;
+ bgp_dest_set_bgp_connected_ref_info(dest, bc);
+ }
+
+ for (ALL_LIST_ELEMENTS(bgp->peer, node, nnode, peer)) {
+ if (peer->conf_if
+ && (strcmp(peer->conf_if, ifc->ifp->name) == 0)
+ && !peer_established(peer)
+ && !CHECK_FLAG(peer->flags,
+ PEER_FLAG_IFPEER_V6ONLY)) {
+ if (peer_active(peer))
+ BGP_EVENT_ADD(peer, BGP_Stop);
+ BGP_EVENT_ADD(peer, BGP_Start);
+ }
+ }
+ } else if (addr->family == AF_INET6) {
+ apply_mask_ipv6((struct prefix_ipv6 *)&p);
+
+ if (IN6_IS_ADDR_UNSPECIFIED(&p.u.prefix6))
+ return;
+
+ if (IN6_IS_ADDR_LINKLOCAL(&p.u.prefix6))
+ return;
+
+ bgp_address_add(bgp, ifc, addr);
+
+ dest = bgp_node_get(bgp->connected_table[AFI_IP6], &p);
+
+ bc = bgp_dest_get_bgp_connected_ref_info(dest);
+ if (bc)
+ bc->refcnt++;
+ else {
+ bc = XCALLOC(MTYPE_BGP_CONN,
+ sizeof(struct bgp_connected_ref));
+ bc->refcnt = 1;
+ bgp_dest_set_bgp_connected_ref_info(dest, bc);
+ }
+ }
+}
+
+void bgp_connected_delete(struct bgp *bgp, struct connected *ifc)
+{
+ struct prefix p;
+ struct prefix *addr;
+ struct bgp_dest *dest = NULL;
+ struct bgp_connected_ref *bc;
+
+ addr = ifc->address;
+
+ p = *(CONNECTED_PREFIX(ifc));
+ apply_mask(&p);
+ if (addr->family == AF_INET) {
+ if (prefix_ipv4_any((struct prefix_ipv4 *)&p))
+ return;
+
+ bgp_address_del(bgp, ifc, addr);
+
+ dest = bgp_node_lookup(bgp->connected_table[AFI_IP], &p);
+ } else if (addr->family == AF_INET6) {
+ if (IN6_IS_ADDR_UNSPECIFIED(&p.u.prefix6))
+ return;
+
+ if (IN6_IS_ADDR_LINKLOCAL(&p.u.prefix6))
+ return;
+
+ bgp_address_del(bgp, ifc, addr);
+
+ dest = bgp_node_lookup(bgp->connected_table[AFI_IP6], &p);
+ }
+
+ if (!dest)
+ return;
+
+ bc = bgp_dest_get_bgp_connected_ref_info(dest);
+ bc->refcnt--;
+ if (bc->refcnt == 0) {
+ XFREE(MTYPE_BGP_CONN, bc);
+ bgp_dest_set_bgp_connected_ref_info(dest, NULL);
+ }
+ bgp_dest_unlock_node(dest);
+ bgp_dest_unlock_node(dest);
+}
+
+static void bgp_connected_cleanup(struct route_table *table,
+ struct route_node *rn)
+{
+ struct bgp_connected_ref *bc;
+ struct bgp_dest *bn = bgp_dest_from_rnode(rn);
+
+ bc = bgp_dest_get_bgp_connected_ref_info(bn);
+ if (!bc)
+ return;
+
+ XFREE(MTYPE_BGP_CONN, bc);
+ bgp_dest_set_bgp_connected_ref_info(bn, NULL);
+}
+
+bool bgp_nexthop_self(struct bgp *bgp, afi_t afi, uint8_t type,
+ uint8_t sub_type, struct attr *attr,
+ struct bgp_dest *dest)
+{
+ uint8_t new_afi = afi == AFI_IP ? AF_INET : AF_INET6;
+ struct bgp_addr tmp_addr = {{0}}, *addr = NULL;
+ struct tip_addr tmp_tip, *tip = NULL;
+ const struct prefix *p = bgp_dest_get_prefix(dest);
+ bool is_bgp_static_route =
+ ((type == ZEBRA_ROUTE_BGP) && (sub_type == BGP_ROUTE_STATIC))
+ ? true
+ : false;
+
+ if (!is_bgp_static_route)
+ new_afi = BGP_ATTR_NEXTHOP_AFI_IP6(attr) ? AF_INET6 : AF_INET;
+
+ tmp_addr.p.family = new_afi;
+ switch (new_afi) {
+ case AF_INET:
+ if (is_bgp_static_route) {
+ tmp_addr.p.u.prefix4 = p->u.prefix4;
+ tmp_addr.p.prefixlen = p->prefixlen;
+ } else {
+ /* Here we need to find out which nexthop to be used*/
+ if (attr->flag & ATTR_FLAG_BIT(BGP_ATTR_NEXT_HOP)) {
+ tmp_addr.p.u.prefix4 = attr->nexthop;
+ tmp_addr.p.prefixlen = IPV4_MAX_BITLEN;
+ } else if ((attr->mp_nexthop_len)
+ && ((attr->mp_nexthop_len
+ == BGP_ATTR_NHLEN_IPV4)
+ || (attr->mp_nexthop_len
+ == BGP_ATTR_NHLEN_VPNV4))) {
+ tmp_addr.p.u.prefix4 =
+ attr->mp_nexthop_global_in;
+ tmp_addr.p.prefixlen = IPV4_MAX_BITLEN;
+ } else
+ return false;
+ }
+ break;
+ case AF_INET6:
+ if (is_bgp_static_route) {
+ tmp_addr.p.u.prefix6 = p->u.prefix6;
+ tmp_addr.p.prefixlen = p->prefixlen;
+ } else {
+ tmp_addr.p.u.prefix6 = attr->mp_nexthop_global;
+ tmp_addr.p.prefixlen = IPV6_MAX_BITLEN;
+ }
+ break;
+ default:
+ break;
+ }
+
+ addr = hash_lookup(bgp->address_hash, &tmp_addr);
+ if (addr)
+ return true;
+
+ if (new_afi == AF_INET && hashcount(bgp->tip_hash)) {
+ memset(&tmp_tip, 0, sizeof(tmp_tip));
+ tmp_tip.addr = attr->nexthop;
+
+ if (attr->flag & ATTR_FLAG_BIT(BGP_ATTR_NEXT_HOP)) {
+ tmp_tip.addr = attr->nexthop;
+ } else if ((attr->mp_nexthop_len) &&
+ ((attr->mp_nexthop_len == BGP_ATTR_NHLEN_IPV4)
+ || (attr->mp_nexthop_len == BGP_ATTR_NHLEN_VPNV4))) {
+ tmp_tip.addr = attr->mp_nexthop_global_in;
+ }
+
+ tip = hash_lookup(bgp->tip_hash, &tmp_tip);
+ if (tip)
+ return true;
+ }
+
+ return false;
+}
+
+bool bgp_multiaccess_check_v4(struct in_addr nexthop, struct peer *peer)
+{
+ struct bgp_dest *dest1;
+ struct bgp_dest *dest2;
+ struct prefix p;
+ int ret;
+
+ p.family = AF_INET;
+ p.prefixlen = IPV4_MAX_BITLEN;
+ p.u.prefix4 = nexthop;
+
+ dest1 = bgp_node_match(peer->bgp->connected_table[AFI_IP], &p);
+ if (!dest1)
+ return false;
+
+ p.family = AF_INET;
+ p.prefixlen = IPV4_MAX_BITLEN;
+ p.u.prefix4 = peer->su.sin.sin_addr;
+
+ dest2 = bgp_node_match(peer->bgp->connected_table[AFI_IP], &p);
+ if (!dest2) {
+ bgp_dest_unlock_node(dest1);
+ return false;
+ }
+
+ ret = (dest1 == dest2);
+
+ bgp_dest_unlock_node(dest1);
+ bgp_dest_unlock_node(dest2);
+
+ return ret;
+}
+
+bool bgp_multiaccess_check_v6(struct in6_addr nexthop, struct peer *peer)
+{
+ struct bgp_dest *dest1;
+ struct bgp_dest *dest2;
+ struct prefix p;
+ int ret;
+
+ p.family = AF_INET6;
+ p.prefixlen = IPV6_MAX_BITLEN;
+ p.u.prefix6 = nexthop;
+
+ dest1 = bgp_node_match(peer->bgp->connected_table[AFI_IP6], &p);
+ if (!dest1)
+ return false;
+
+ p.family = AF_INET6;
+ p.prefixlen = IPV6_MAX_BITLEN;
+ p.u.prefix6 = peer->su.sin6.sin6_addr;
+
+ dest2 = bgp_node_match(peer->bgp->connected_table[AFI_IP6], &p);
+ if (!dest2) {
+ bgp_dest_unlock_node(dest1);
+ return false;
+ }
+
+ ret = (dest1 == dest2);
+
+ bgp_dest_unlock_node(dest1);
+ bgp_dest_unlock_node(dest2);
+
+ return ret;
+}
+
+bool bgp_subgrp_multiaccess_check_v6(struct in6_addr nexthop,
+ struct update_subgroup *subgrp,
+ struct peer *exclude)
+{
+ struct bgp_dest *dest1 = NULL, *dest2 = NULL;
+ struct peer_af *paf = NULL;
+ struct prefix p = {0}, np = {0};
+ struct bgp *bgp = NULL;
+
+ np.family = AF_INET6;
+ np.prefixlen = IPV6_MAX_BITLEN;
+ np.u.prefix6 = nexthop;
+
+ p.family = AF_INET;
+ p.prefixlen = IPV6_MAX_BITLEN;
+
+ bgp = SUBGRP_INST(subgrp);
+ dest1 = bgp_node_match(bgp->connected_table[AFI_IP6], &np);
+ if (!dest1)
+ return false;
+
+ SUBGRP_FOREACH_PEER (subgrp, paf) {
+ /* Skip peer we're told to exclude - e.g., source of route. */
+ if (paf->peer == exclude)
+ continue;
+
+ p.u.prefix6 = paf->peer->su.sin6.sin6_addr;
+ dest2 = bgp_node_match(bgp->connected_table[AFI_IP6], &p);
+ if (dest1 == dest2) {
+ bgp_dest_unlock_node(dest1);
+ bgp_dest_unlock_node(dest2);
+ return true;
+ }
+
+ if (dest2)
+ bgp_dest_unlock_node(dest2);
+ }
+
+ bgp_dest_unlock_node(dest1);
+ return false;
+}
+
+bool bgp_subgrp_multiaccess_check_v4(struct in_addr nexthop,
+ struct update_subgroup *subgrp,
+ struct peer *exclude)
+{
+ struct bgp_dest *dest1, *dest2;
+ struct peer_af *paf;
+ struct prefix p, np;
+ struct bgp *bgp;
+
+ np.family = AF_INET;
+ np.prefixlen = IPV4_MAX_BITLEN;
+ np.u.prefix4 = nexthop;
+
+ p.family = AF_INET;
+ p.prefixlen = IPV4_MAX_BITLEN;
+
+ bgp = SUBGRP_INST(subgrp);
+ dest1 = bgp_node_match(bgp->connected_table[AFI_IP], &np);
+ if (!dest1)
+ return false;
+
+ SUBGRP_FOREACH_PEER (subgrp, paf) {
+ /* Skip peer we're told to exclude - e.g., source of route. */
+ if (paf->peer == exclude)
+ continue;
+
+ p.u.prefix4 = paf->peer->su.sin.sin_addr;
+
+ dest2 = bgp_node_match(bgp->connected_table[AFI_IP], &p);
+ if (dest1 == dest2) {
+ bgp_dest_unlock_node(dest1);
+ bgp_dest_unlock_node(dest2);
+ return true;
+ }
+
+ if (dest2)
+ bgp_dest_unlock_node(dest2);
+ }
+
+ bgp_dest_unlock_node(dest1);
+ return false;
+}
+
+static void bgp_show_nexthop_paths(struct vty *vty, struct bgp *bgp,
+ struct bgp_nexthop_cache *bnc)
+{
+ struct bgp_dest *dest;
+ struct bgp_path_info *path;
+ int afi;
+ safi_t safi;
+ struct bgp_table *table;
+ struct bgp *bgp_path;
+
+ vty_out(vty, " Paths:\n");
+ LIST_FOREACH (path, &(bnc->paths), nh_thread) {
+ dest = path->net;
+ assert(dest && bgp_dest_table(dest));
+ afi = family2afi(bgp_dest_get_prefix(dest)->family);
+ table = bgp_dest_table(dest);
+ safi = table->safi;
+ bgp_path = table->bgp;
+
+ if (dest->pdest)
+ vty_out(vty, " %d/%d %pBD RD %pRD %s flags 0x%x\n",
+ afi, safi, dest,
+ (struct prefix_rd *)bgp_dest_get_prefix(
+ dest->pdest),
+ bgp_path->name_pretty, path->flags);
+ else
+ vty_out(vty, " %d/%d %pBD %s flags 0x%x\n",
+ afi, safi, dest, bgp_path->name_pretty, path->flags);
+ }
+}
+
+static void bgp_show_nexthops_detail(struct vty *vty, struct bgp *bgp,
+ struct bgp_nexthop_cache *bnc)
+{
+ char buf[PREFIX2STR_BUFFER];
+ struct nexthop *nexthop;
+
+ for (nexthop = bnc->nexthop; nexthop; nexthop = nexthop->next) {
+ switch (nexthop->type) {
+ case NEXTHOP_TYPE_IPV6:
+ vty_out(vty, " gate %s\n",
+ inet_ntop(AF_INET6, &nexthop->gate.ipv6, buf,
+ sizeof(buf)));
+ break;
+ case NEXTHOP_TYPE_IPV6_IFINDEX:
+ vty_out(vty, " gate %s, if %s\n",
+ inet_ntop(AF_INET6, &nexthop->gate.ipv6, buf,
+ sizeof(buf)),
+ ifindex2ifname(bnc->ifindex ? bnc->ifindex
+ : nexthop->ifindex,
+ bgp->vrf_id));
+ break;
+ case NEXTHOP_TYPE_IPV4:
+ vty_out(vty, " gate %s\n",
+ inet_ntop(AF_INET, &nexthop->gate.ipv4, buf,
+ sizeof(buf)));
+ break;
+ case NEXTHOP_TYPE_IFINDEX:
+ vty_out(vty, " if %s\n",
+ ifindex2ifname(bnc->ifindex ? bnc->ifindex
+ : nexthop->ifindex,
+ bgp->vrf_id));
+ break;
+ case NEXTHOP_TYPE_IPV4_IFINDEX:
+ vty_out(vty, " gate %s, if %s\n",
+ inet_ntop(AF_INET, &nexthop->gate.ipv4, buf,
+ sizeof(buf)),
+ ifindex2ifname(bnc->ifindex ? bnc->ifindex
+ : nexthop->ifindex,
+ bgp->vrf_id));
+ break;
+ case NEXTHOP_TYPE_BLACKHOLE:
+ vty_out(vty, " blackhole\n");
+ break;
+ default:
+ vty_out(vty, " invalid nexthop type %u\n",
+ nexthop->type);
+ }
+ }
+}
+
+static void bgp_show_nexthop(struct vty *vty, struct bgp *bgp,
+ struct bgp_nexthop_cache *bnc,
+ bool specific)
+{
+ char buf[PREFIX2STR_BUFFER];
+ time_t tbuf;
+ struct peer *peer;
+
+ peer = (struct peer *)bnc->nht_info;
+
+ if (bnc->srte_color)
+ vty_out(vty, " SR-TE color %u -", bnc->srte_color);
+ if (CHECK_FLAG(bnc->flags, BGP_NEXTHOP_VALID)) {
+ vty_out(vty, " %s valid [IGP metric %d], #paths %d",
+ inet_ntop(bnc->prefix.family, &bnc->prefix.u.prefix,
+ buf, sizeof(buf)),
+ bnc->metric, bnc->path_count);
+ if (peer)
+ vty_out(vty, ", peer %s", peer->host);
+ if (bnc->is_evpn_gwip_nexthop)
+ vty_out(vty, " EVPN Gateway IP");
+ vty_out(vty, "\n");
+ bgp_show_nexthops_detail(vty, bgp, bnc);
+ } else if (CHECK_FLAG(bnc->flags, BGP_NEXTHOP_EVPN_INCOMPLETE)) {
+ vty_out(vty,
+ " %s overlay index unresolved [IGP metric %d], #paths %d",
+ inet_ntop(bnc->prefix.family, &bnc->prefix.u.prefix,
+ buf, sizeof(buf)),
+ bnc->metric, bnc->path_count);
+ if (bnc->is_evpn_gwip_nexthop)
+ vty_out(vty, " EVPN Gateway IP");
+ vty_out(vty, "\n");
+ bgp_show_nexthops_detail(vty, bgp, bnc);
+ } else {
+ vty_out(vty, " %s invalid, #paths %d",
+ inet_ntop(bnc->prefix.family, &bnc->prefix.u.prefix,
+ buf, sizeof(buf)),
+ bnc->path_count);
+ if (peer)
+ vty_out(vty, ", peer %s", peer->host);
+ if (bnc->is_evpn_gwip_nexthop)
+ vty_out(vty, " EVPN Gateway IP");
+ vty_out(vty, "\n");
+ if (CHECK_FLAG(bnc->flags, BGP_NEXTHOP_CONNECTED))
+ vty_out(vty, " Must be Connected\n");
+ if (!CHECK_FLAG(bnc->flags, BGP_NEXTHOP_REGISTERED))
+ vty_out(vty, " Is not Registered\n");
+ }
+ tbuf = time(NULL) - (monotime(NULL) - bnc->last_update);
+ vty_out(vty, " Last update: %s", ctime(&tbuf));
+ vty_out(vty, "\n");
+
+ /* show paths dependent on nexthop, if needed. */
+ if (specific)
+ bgp_show_nexthop_paths(vty, bgp, bnc);
+}
+
+static void bgp_show_nexthops(struct vty *vty, struct bgp *bgp,
+ bool import_table)
+{
+ struct bgp_nexthop_cache *bnc;
+ afi_t afi;
+ struct bgp_nexthop_cache_head(*tree)[AFI_MAX];
+
+ if (import_table)
+ vty_out(vty, "Current BGP import check cache:\n");
+ else
+ vty_out(vty, "Current BGP nexthop cache:\n");
+ if (import_table)
+ tree = &bgp->import_check_table;
+ else
+ tree = &bgp->nexthop_cache_table;
+ for (afi = AFI_IP; afi < AFI_MAX; afi++) {
+ frr_each (bgp_nexthop_cache, &(*tree)[afi], bnc)
+ bgp_show_nexthop(vty, bgp, bnc, false);
+ }
+}
+
+static int show_ip_bgp_nexthop_table(struct vty *vty, const char *name,
+ const char *nhopip_str,
+ bool import_table)
+{
+ struct bgp *bgp;
+
+ if (name)
+ bgp = bgp_lookup_by_name(name);
+ else
+ bgp = bgp_get_default();
+ if (!bgp) {
+ vty_out(vty, "%% No such BGP instance exist\n");
+ return CMD_WARNING;
+ }
+
+ if (nhopip_str) {
+ struct prefix nhop;
+ struct bgp_nexthop_cache_head (*tree)[AFI_MAX];
+ struct bgp_nexthop_cache *bnc;
+
+ if (!str2prefix(nhopip_str, &nhop)) {
+ vty_out(vty, "nexthop address is malformed\n");
+ return CMD_WARNING;
+ }
+ tree = import_table ? &bgp->import_check_table
+ : &bgp->nexthop_cache_table;
+ bnc = bnc_find(&(*tree)[family2afi(nhop.family)], &nhop, 0, 0);
+ if (!bnc) {
+ vty_out(vty, "specified nexthop does not have entry\n");
+ return CMD_SUCCESS;
+ }
+ bgp_show_nexthop(vty, bgp, bnc, true);
+ } else
+ bgp_show_nexthops(vty, bgp, import_table);
+
+ return CMD_SUCCESS;
+}
+
+static void bgp_show_all_instances_nexthops_vty(struct vty *vty)
+{
+ struct listnode *node, *nnode;
+ struct bgp *bgp;
+
+ for (ALL_LIST_ELEMENTS(bm->bgp, node, nnode, bgp)) {
+ vty_out(vty, "\nInstance %s:\n",
+ (bgp->inst_type == BGP_INSTANCE_TYPE_DEFAULT)
+ ? VRF_DEFAULT_NAME
+ : bgp->name);
+ bgp_show_nexthops(vty, bgp, false);
+ }
+}
+
+DEFUN (show_ip_bgp_nexthop,
+ show_ip_bgp_nexthop_cmd,
+ "show [ip] bgp [<view|vrf> VIEWVRFNAME] nexthop [<A.B.C.D|X:X::X:X>] [detail]",
+ SHOW_STR
+ IP_STR
+ BGP_STR
+ BGP_INSTANCE_HELP_STR
+ "BGP nexthop table\n"
+ "IPv4 nexthop address\n"
+ "IPv6 nexthop address\n"
+ "Show detailed information\n")
+{
+ int idx = 0;
+ int nh_idx = 0;
+ char *vrf = NULL;
+ char *nhop_ip = NULL;
+
+ if (argv_find(argv, argc, "view", &idx)
+ || argv_find(argv, argc, "vrf", &idx))
+ vrf = argv[++idx]->arg;
+
+ if (argv_find(argv, argc, "A.B.C.D", &nh_idx)
+ || argv_find(argv, argc, "X:X::X:X", &nh_idx))
+ nhop_ip = argv[nh_idx]->arg;
+
+ return show_ip_bgp_nexthop_table(vty, vrf, nhop_ip, false);
+}
+
+DEFUN (show_ip_bgp_import_check,
+ show_ip_bgp_import_check_cmd,
+ "show [ip] bgp [<view|vrf> VIEWVRFNAME] import-check-table [detail]",
+ SHOW_STR
+ IP_STR
+ BGP_STR
+ BGP_INSTANCE_HELP_STR
+ "BGP import check table\n"
+ "Show detailed information\n")
+{
+ int idx = 0;
+ char *vrf = NULL;
+
+ if (argv_find(argv, argc, "view", &idx)
+ || argv_find(argv, argc, "vrf", &idx))
+ vrf = argv[++idx]->arg;
+
+ return show_ip_bgp_nexthop_table(vty, vrf, NULL, true);
+}
+
+DEFUN (show_ip_bgp_instance_all_nexthop,
+ show_ip_bgp_instance_all_nexthop_cmd,
+ "show [ip] bgp <view|vrf> all nexthop",
+ SHOW_STR
+ IP_STR
+ BGP_STR
+ BGP_INSTANCE_ALL_HELP_STR
+ "BGP nexthop table\n")
+{
+ bgp_show_all_instances_nexthops_vty(vty);
+ return CMD_SUCCESS;
+}
+
+void bgp_scan_init(struct bgp *bgp)
+{
+ afi_t afi;
+
+ for (afi = AFI_IP; afi < AFI_MAX; afi++) {
+ bgp_nexthop_cache_init(&bgp->nexthop_cache_table[afi]);
+ bgp_nexthop_cache_init(&bgp->import_check_table[afi]);
+ bgp->connected_table[afi] = bgp_table_init(bgp, afi,
+ SAFI_UNICAST);
+ }
+}
+
+void bgp_scan_vty_init(void)
+{
+ install_element(VIEW_NODE, &show_ip_bgp_nexthop_cmd);
+ install_element(VIEW_NODE, &show_ip_bgp_import_check_cmd);
+ install_element(VIEW_NODE, &show_ip_bgp_instance_all_nexthop_cmd);
+}
+
+void bgp_scan_finish(struct bgp *bgp)
+{
+ afi_t afi;
+
+ for (afi = AFI_IP; afi < AFI_MAX; afi++) {
+ /* Only the current one needs to be reset. */
+ bgp_nexthop_cache_reset(&bgp->nexthop_cache_table[afi]);
+ bgp_nexthop_cache_reset(&bgp->import_check_table[afi]);
+
+ bgp->connected_table[afi]->route_table->cleanup =
+ bgp_connected_cleanup;
+ bgp_table_unlock(bgp->connected_table[afi]);
+ bgp->connected_table[afi] = NULL;
+ }
+}
+
+char *bgp_nexthop_dump_bnc_flags(struct bgp_nexthop_cache *bnc, char *buf,
+ size_t len)
+{
+ if (bnc->flags == 0) {
+ snprintfrr(buf, len, "None ");
+ return buf;
+ }
+
+ snprintfrr(buf, len, "%s%s%s%s%s%s%s",
+ CHECK_FLAG(bnc->flags, BGP_NEXTHOP_VALID) ? "Valid " : "",
+ CHECK_FLAG(bnc->flags, BGP_NEXTHOP_REGISTERED) ? "Reg " : "",
+ CHECK_FLAG(bnc->flags, BGP_NEXTHOP_CONNECTED) ? "Conn " : "",
+ CHECK_FLAG(bnc->flags, BGP_NEXTHOP_PEER_NOTIFIED) ? "Notify "
+ : "",
+ CHECK_FLAG(bnc->flags, BGP_STATIC_ROUTE) ? "Static " : "",
+ CHECK_FLAG(bnc->flags, BGP_STATIC_ROUTE_EXACT_MATCH)
+ ? "Static Exact "
+ : "",
+ CHECK_FLAG(bnc->flags, BGP_NEXTHOP_LABELED_VALID)
+ ? "Label Valid "
+ : "");
+
+ return buf;
+}
+
+char *bgp_nexthop_dump_bnc_change_flags(struct bgp_nexthop_cache *bnc,
+ char *buf, size_t len)
+{
+ if (bnc->flags == 0) {
+ snprintfrr(buf, len, "None ");
+ return buf;
+ }
+
+ snprintfrr(buf, len, "%s%s%s",
+ CHECK_FLAG(bnc->change_flags, BGP_NEXTHOP_CHANGED)
+ ? "Changed "
+ : "",
+ CHECK_FLAG(bnc->change_flags, BGP_NEXTHOP_METRIC_CHANGED)
+ ? "Metric "
+ : "",
+ CHECK_FLAG(bnc->change_flags, BGP_NEXTHOP_CONNECTED_CHANGED)
+ ? "Connected "
+ : "");
+
+ return buf;
+}
diff --git a/bgpd/bgp_nexthop.h b/bgpd/bgp_nexthop.h
new file mode 100644
index 0000000..efad906
--- /dev/null
+++ b/bgpd/bgp_nexthop.h
@@ -0,0 +1,175 @@
+/* BGP nexthop scan
+ * Copyright (C) 2000 Kunihiro Ishiguro
+ *
+ * This file is part of GNU Zebra.
+ *
+ * GNU Zebra is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License as published by the
+ * Free Software Foundation; either version 2, or (at your option) any
+ * later version.
+ *
+ * GNU Zebra is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program; see the file COPYING; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
+ */
+
+#ifndef _QUAGGA_BGP_NEXTHOP_H
+#define _QUAGGA_BGP_NEXTHOP_H
+
+#include "if.h"
+#include "queue.h"
+#include "prefix.h"
+#include "bgp_table.h"
+
+#define NEXTHOP_FAMILY(nexthop_len) \
+ (((nexthop_len) == 4 || (nexthop_len) == 12 \
+ ? AF_INET \
+ : ((nexthop_len) == 16 || (nexthop_len) == 24 \
+ || (nexthop_len) == 32 \
+ || (nexthop_len) == 48 \
+ ? AF_INET6 \
+ : AF_UNSPEC)))
+
+#define BGP_MP_NEXTHOP_FAMILY NEXTHOP_FAMILY
+
+PREDECL_RBTREE_UNIQ(bgp_nexthop_cache);
+
+/* BGP nexthop cache value structure. */
+struct bgp_nexthop_cache {
+ /* The ifindex of the outgoing interface *if* it's a v6 LL */
+ ifindex_t ifindex;
+
+ /* RB-tree entry. */
+ struct bgp_nexthop_cache_item entry;
+
+ /* IGP route's metric. */
+ uint32_t metric;
+
+ /* Nexthop number and nexthop linked list.*/
+ uint8_t nexthop_num;
+ struct nexthop *nexthop;
+ time_t last_update;
+ uint16_t flags;
+
+/*
+ * If the nexthop is EVPN gateway IP NH, VALID flag is set only if the nexthop
+ * is RIB reachable as well as MAC/IP is present
+ */
+#define BGP_NEXTHOP_VALID (1 << 0)
+#define BGP_NEXTHOP_REGISTERED (1 << 1)
+#define BGP_NEXTHOP_CONNECTED (1 << 2)
+#define BGP_NEXTHOP_PEER_NOTIFIED (1 << 3)
+#define BGP_STATIC_ROUTE (1 << 4)
+#define BGP_STATIC_ROUTE_EXACT_MATCH (1 << 5)
+#define BGP_NEXTHOP_LABELED_VALID (1 << 6)
+
+/*
+ * This flag is added for EVPN gateway IP nexthops.
+ * If the nexthop is RIB reachable, but a MAC/IP is not yet
+ * resolved, this flag is set.
+ * Following table explains the combination of L3 and L2 reachability w.r.t.
+ * VALID and INCOMPLETE flags
+ *
+ * | MACIP resolved | MACIP unresolved
+ *----------------|----------------|------------------
+ * L3 reachable | VALID = 1 | VALID = 0
+ * | INCOMPLETE = 0 | INCOMPLETE = 1
+ * ---------------|----------------|--------------------
+ * L3 unreachable | VALID = 0 | VALID = 0
+ * | INCOMPLETE = 0 | INCOMPLETE = 0
+ */
+#define BGP_NEXTHOP_EVPN_INCOMPLETE (1 << 7)
+
+ uint16_t change_flags;
+
+#define BGP_NEXTHOP_CHANGED (1 << 0)
+#define BGP_NEXTHOP_METRIC_CHANGED (1 << 1)
+#define BGP_NEXTHOP_CONNECTED_CHANGED (1 << 2)
+#define BGP_NEXTHOP_MACIP_CHANGED (1 << 3)
+
+ /* Back pointer to the cache tree this entry belongs to. */
+ struct bgp_nexthop_cache_head *tree;
+
+ uint32_t srte_color;
+ struct prefix prefix;
+ void *nht_info; /* In BGP, peer session */
+ LIST_HEAD(path_list, bgp_path_info) paths;
+ unsigned int path_count;
+ struct bgp *bgp;
+
+ /* This flag is set to TRUE for a bnc that is gateway IP overlay index
+ * nexthop.
+ */
+ bool is_evpn_gwip_nexthop;
+};
+
+extern int bgp_nexthop_cache_compare(const struct bgp_nexthop_cache *a,
+ const struct bgp_nexthop_cache *b);
+DECLARE_RBTREE_UNIQ(bgp_nexthop_cache, struct bgp_nexthop_cache, entry,
+ bgp_nexthop_cache_compare);
+
+/* Own tunnel-ip address structure */
+struct tip_addr {
+ struct in_addr addr;
+ int refcnt;
+};
+
+struct bgp_addrv6 {
+ struct in6_addr addrv6;
+ struct list *ifp_name_list;
+};
+
+/* Forward declaration(s). */
+struct peer;
+struct update_subgroup;
+struct bgp_dest;
+struct attr;
+
+#define BNC_FLAG_DUMP_SIZE 180
+extern char *bgp_nexthop_dump_bnc_flags(struct bgp_nexthop_cache *bnc,
+ char *buf, size_t len);
+extern char *bgp_nexthop_dump_bnc_change_flags(struct bgp_nexthop_cache *bnc,
+ char *buf, size_t len);
+extern void bgp_connected_add(struct bgp *bgp, struct connected *c);
+extern void bgp_connected_delete(struct bgp *bgp, struct connected *c);
+extern bool bgp_subgrp_multiaccess_check_v4(struct in_addr nexthop,
+ struct update_subgroup *subgrp,
+ struct peer *exclude);
+extern bool bgp_subgrp_multiaccess_check_v6(struct in6_addr nexthop,
+ struct update_subgroup *subgrp,
+ struct peer *exclude);
+extern bool bgp_multiaccess_check_v4(struct in_addr nexthop, struct peer *peer);
+extern bool bgp_multiaccess_check_v6(struct in6_addr nexthop,
+ struct peer *peer);
+extern int bgp_config_write_scan_time(struct vty *);
+extern bool bgp_nexthop_self(struct bgp *bgp, afi_t afi, uint8_t type,
+ uint8_t sub_type, struct attr *attr,
+ struct bgp_dest *dest);
+extern struct bgp_nexthop_cache *bnc_new(struct bgp_nexthop_cache_head *tree,
+ struct prefix *prefix,
+ uint32_t srte_color,
+ ifindex_t ifindex);
+extern bool bnc_existing_for_prefix(struct bgp_nexthop_cache *bnc);
+extern void bnc_free(struct bgp_nexthop_cache *bnc);
+extern struct bgp_nexthop_cache *bnc_find(struct bgp_nexthop_cache_head *tree,
+ struct prefix *prefix,
+ uint32_t srte_color,
+ ifindex_t ifindex);
+extern void bnc_nexthop_free(struct bgp_nexthop_cache *bnc);
+extern void bgp_scan_init(struct bgp *bgp);
+extern void bgp_scan_finish(struct bgp *bgp);
+extern void bgp_scan_vty_init(void);
+extern void bgp_address_init(struct bgp *bgp);
+extern void bgp_address_destroy(struct bgp *bgp);
+extern void bgp_tip_add(struct bgp *bgp, struct in_addr *tip);
+extern void bgp_tip_del(struct bgp *bgp, struct in_addr *tip);
+extern void bgp_tip_hash_init(struct bgp *bgp);
+extern void bgp_tip_hash_destroy(struct bgp *bgp);
+
+extern void bgp_nexthop_show_address_hash(struct vty *vty, struct bgp *bgp);
+#endif /* _QUAGGA_BGP_NEXTHOP_H */
diff --git a/bgpd/bgp_nht.c b/bgpd/bgp_nht.c
new file mode 100644
index 0000000..7274bcd
--- /dev/null
+++ b/bgpd/bgp_nht.c
@@ -0,0 +1,1455 @@
+/* BGP Nexthop tracking
+ * Copyright (C) 2013 Cumulus Networks, Inc.
+ *
+ * This file is part of GNU Zebra.
+ *
+ * GNU Zebra is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License as published by the
+ * Free Software Foundation; either version 2, or (at your option) any
+ * later version.
+ *
+ * GNU Zebra is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program; see the file COPYING; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
+ */
+
+#include <zebra.h>
+
+#include "command.h"
+#include "thread.h"
+#include "prefix.h"
+#include "zclient.h"
+#include "stream.h"
+#include "network.h"
+#include "log.h"
+#include "memory.h"
+#include "nexthop.h"
+#include "vrf.h"
+#include "filter.h"
+#include "nexthop_group.h"
+
+#include "bgpd/bgpd.h"
+#include "bgpd/bgp_table.h"
+#include "bgpd/bgp_route.h"
+#include "bgpd/bgp_attr.h"
+#include "bgpd/bgp_nexthop.h"
+#include "bgpd/bgp_debug.h"
+#include "bgpd/bgp_errors.h"
+#include "bgpd/bgp_nht.h"
+#include "bgpd/bgp_fsm.h"
+#include "bgpd/bgp_zebra.h"
+#include "bgpd/bgp_flowspec_util.h"
+#include "bgpd/bgp_evpn.h"
+#include "bgpd/bgp_rd.h"
+
+extern struct zclient *zclient;
+
+static void register_zebra_rnh(struct bgp_nexthop_cache *bnc);
+static void unregister_zebra_rnh(struct bgp_nexthop_cache *bnc);
+static int make_prefix(int afi, struct bgp_path_info *pi, struct prefix *p);
+static void bgp_nht_ifp_initial(struct thread *thread);
+
+static int bgp_isvalid_nexthop(struct bgp_nexthop_cache *bnc)
+{
+ return (bgp_zebra_num_connects() == 0
+ || (bnc && CHECK_FLAG(bnc->flags, BGP_NEXTHOP_VALID)
+ && bnc->nexthop_num > 0));
+}
+
+static int bgp_isvalid_nexthop_for_ebgp(struct bgp_nexthop_cache *bnc,
+ struct bgp_path_info *path)
+{
+ struct interface *ifp = NULL;
+ struct nexthop *nexthop;
+ struct bgp_interface *iifp;
+ struct peer *peer;
+
+ if (!path->extra || !path->extra->peer_orig)
+ return false;
+
+ peer = path->extra->peer_orig;
+
+ /* only connected ebgp peers are valid */
+ if (peer->sort != BGP_PEER_EBGP || peer->ttl != BGP_DEFAULT_TTL ||
+ CHECK_FLAG(peer->flags, PEER_FLAG_DISABLE_CONNECTED_CHECK) ||
+ CHECK_FLAG(peer->bgp->flags, BGP_FLAG_DISABLE_NH_CONNECTED_CHK))
+ return false;
+
+ for (nexthop = bnc->nexthop; nexthop; nexthop = nexthop->next) {
+ if (nexthop->type == NEXTHOP_TYPE_IFINDEX ||
+ nexthop->type == NEXTHOP_TYPE_IPV4_IFINDEX ||
+ nexthop->type == NEXTHOP_TYPE_IPV6_IFINDEX) {
+ ifp = if_lookup_by_index(
+ bnc->ifindex ? bnc->ifindex : nexthop->ifindex,
+ bnc->bgp->vrf_id);
+ }
+ if (!ifp)
+ continue;
+ iifp = ifp->info;
+ if (CHECK_FLAG(iifp->flags, BGP_INTERFACE_MPLS_BGP_FORWARDING))
+ return true;
+ }
+ return false;
+}
+
+static int bgp_isvalid_nexthop_for_mplsovergre(struct bgp_nexthop_cache *bnc,
+ struct bgp_path_info *path)
+{
+ struct interface *ifp = NULL;
+ struct nexthop *nexthop;
+
+ for (nexthop = bnc->nexthop; nexthop; nexthop = nexthop->next) {
+ if (nexthop->type != NEXTHOP_TYPE_BLACKHOLE) {
+ ifp = if_lookup_by_index(
+ bnc->ifindex ? bnc->ifindex : nexthop->ifindex,
+ bnc->bgp->vrf_id);
+ if (ifp && (ifp->ll_type == ZEBRA_LLT_IPGRE ||
+ ifp->ll_type == ZEBRA_LLT_IP6GRE))
+ break;
+ }
+ }
+ if (!ifp)
+ return false;
+
+ if (CHECK_FLAG(path->attr->rmap_change_flags,
+ BATTR_RMAP_L3VPN_ACCEPT_GRE))
+ return true;
+
+ return false;
+}
+
+static int bgp_isvalid_nexthop_for_mpls(struct bgp_nexthop_cache *bnc,
+ struct bgp_path_info *path)
+{
+ /*
+ * - In the case of MPLS-VPN, the label is learned from LDP or other
+ * protocols, and nexthop tracking is enabled for the label.
+ * The value is recorded as BGP_NEXTHOP_LABELED_VALID.
+ * - In the case of SRv6-VPN, we need to track the reachability to the
+ * SID (in other words, IPv6 address). As in MPLS, we need to record
+ * the value as BGP_NEXTHOP_SID_VALID. However, this function is
+ * currently not implemented, and this function assumes that all
+ * Transit routes for SRv6-VPN are valid.
+ * - Otherwise check for mpls-gre acceptance
+ */
+ return (bgp_zebra_num_connects() == 0 ||
+ (bnc && (bnc->nexthop_num > 0 &&
+ (CHECK_FLAG(bnc->flags, BGP_NEXTHOP_LABELED_VALID) ||
+ bnc->bgp->srv6_enabled ||
+ bgp_isvalid_nexthop_for_ebgp(bnc, path) ||
+ bgp_isvalid_nexthop_for_mplsovergre(bnc, path)))));
+}
+
+static void bgp_unlink_nexthop_check(struct bgp_nexthop_cache *bnc)
+{
+ if (LIST_EMPTY(&(bnc->paths)) && !bnc->nht_info) {
+ if (BGP_DEBUG(nht, NHT))
+ zlog_debug("%s: freeing bnc %pFX(%d)(%u)(%s)", __func__,
+ &bnc->prefix, bnc->ifindex, bnc->srte_color,
+ bnc->bgp->name_pretty);
+ /* only unregister if this is the last nh for this prefix*/
+ if (!bnc_existing_for_prefix(bnc))
+ unregister_zebra_rnh(bnc);
+ bnc_free(bnc);
+ }
+}
+
+void bgp_unlink_nexthop(struct bgp_path_info *path)
+{
+ struct bgp_nexthop_cache *bnc = path->nexthop;
+
+ if (!bnc)
+ return;
+
+ path_nh_map(path, NULL, false);
+
+ bgp_unlink_nexthop_check(bnc);
+}
+
+void bgp_replace_nexthop_by_peer(struct peer *from, struct peer *to)
+{
+ struct prefix pp;
+ struct prefix pt;
+ struct bgp_nexthop_cache *bncp, *bnct;
+ afi_t afi;
+ ifindex_t ifindex = 0;
+
+ if (!sockunion2hostprefix(&from->su, &pp))
+ return;
+
+ /*
+ * Gather the ifindex for if up/down events to be
+ * tagged into this fun
+ */
+ if (from->conf_if && IN6_IS_ADDR_LINKLOCAL(&from->su.sin6.sin6_addr))
+ ifindex = from->su.sin6.sin6_scope_id;
+
+ afi = family2afi(pp.family);
+ bncp = bnc_find(&from->bgp->nexthop_cache_table[afi], &pp, 0, ifindex);
+
+ if (!sockunion2hostprefix(&to->su, &pt))
+ return;
+
+ /*
+ * Gather the ifindex for if up/down events to be
+ * tagged into this fun
+ */
+ ifindex = 0;
+ if (to->conf_if && IN6_IS_ADDR_LINKLOCAL(&to->su.sin6.sin6_addr))
+ ifindex = to->su.sin6.sin6_scope_id;
+ bnct = bnc_find(&to->bgp->nexthop_cache_table[afi], &pt, 0, ifindex);
+
+ if (bnct != bncp)
+ return;
+
+ if (bnct)
+ bnct->nht_info = to;
+}
+
+void bgp_unlink_nexthop_by_peer(struct peer *peer)
+{
+ struct prefix p;
+ struct bgp_nexthop_cache *bnc;
+ afi_t afi = family2afi(peer->su.sa.sa_family);
+ ifindex_t ifindex = 0;
+
+ if (!sockunion2hostprefix(&peer->su, &p))
+ return;
+ /*
+ * Gather the ifindex for if up/down events to be
+ * tagged into this fun
+ */
+ if (afi == AFI_IP6 && IN6_IS_ADDR_LINKLOCAL(&peer->su.sin6.sin6_addr))
+ ifindex = peer->su.sin6.sin6_scope_id;
+ bnc = bnc_find(&peer->bgp->nexthop_cache_table[afi], &p, 0, ifindex);
+ if (!bnc)
+ return;
+
+ /* cleanup the peer reference */
+ bnc->nht_info = NULL;
+
+ bgp_unlink_nexthop_check(bnc);
+}
+
+/*
+ * A route and its nexthop might belong to different VRFs. Therefore,
+ * we need both the bgp_route and bgp_nexthop pointers.
+ */
+int bgp_find_or_add_nexthop(struct bgp *bgp_route, struct bgp *bgp_nexthop,
+ afi_t afi, safi_t safi, struct bgp_path_info *pi,
+ struct peer *peer, int connected,
+ const struct prefix *orig_prefix)
+{
+ struct bgp_nexthop_cache_head *tree = NULL;
+ struct bgp_nexthop_cache *bnc;
+ struct prefix p;
+ uint32_t srte_color = 0;
+ int is_bgp_static_route = 0;
+ ifindex_t ifindex = 0;
+
+ if (pi) {
+ is_bgp_static_route = ((pi->type == ZEBRA_ROUTE_BGP)
+ && (pi->sub_type == BGP_ROUTE_STATIC))
+ ? 1
+ : 0;
+
+ /* Since Extended Next-hop Encoding (RFC5549) support, we want
+ to derive
+ address-family from the next-hop. */
+ if (!is_bgp_static_route)
+ afi = BGP_ATTR_MP_NEXTHOP_LEN_IP6(pi->attr) ? AFI_IP6
+ : AFI_IP;
+
+ /* Validation for the ipv4 mapped ipv6 nexthop. */
+ if (IS_MAPPED_IPV6(&pi->attr->mp_nexthop_global)) {
+ afi = AFI_IP;
+ }
+
+ /* This will return true if the global IPv6 NH is a link local
+ * addr */
+ if (make_prefix(afi, pi, &p) < 0)
+ return 1;
+
+ if (!is_bgp_static_route && orig_prefix
+ && prefix_same(&p, orig_prefix)) {
+ if (BGP_DEBUG(nht, NHT)) {
+ zlog_debug(
+ "%s(%pFX): prefix loops through itself",
+ __func__, &p);
+ }
+ return 0;
+ }
+
+ srte_color = pi->attr->srte_color;
+ } else if (peer) {
+ /*
+ * Gather the ifindex for if up/down events to be
+ * tagged into this fun
+ */
+ if (afi == AFI_IP6 &&
+ IN6_IS_ADDR_LINKLOCAL(&peer->su.sin6.sin6_addr)) {
+ ifindex = peer->su.sin6.sin6_scope_id;
+ if (ifindex == 0) {
+ if (BGP_DEBUG(nht, NHT)) {
+ zlog_debug(
+ "%s: Unable to locate ifindex, waiting till we have one",
+ peer->conf_if);
+ }
+ return 0;
+ }
+ }
+
+ if (!sockunion2hostprefix(&peer->su, &p)) {
+ if (BGP_DEBUG(nht, NHT)) {
+ zlog_debug(
+ "%s: Attempting to register with unknown AFI %d (not %d or %d)",
+ __func__, afi, AFI_IP, AFI_IP6);
+ }
+ return 0;
+ }
+ } else
+ return 0;
+
+ if (is_bgp_static_route)
+ tree = &bgp_nexthop->import_check_table[afi];
+ else
+ tree = &bgp_nexthop->nexthop_cache_table[afi];
+
+ bnc = bnc_find(tree, &p, srte_color, ifindex);
+ if (!bnc) {
+ bnc = bnc_new(tree, &p, srte_color, ifindex);
+ bnc->bgp = bgp_nexthop;
+ if (BGP_DEBUG(nht, NHT))
+ zlog_debug("Allocated bnc %pFX(%d)(%u)(%s) peer %p",
+ &bnc->prefix, bnc->ifindex, bnc->srte_color,
+ bnc->bgp->name_pretty, peer);
+ } else {
+ if (BGP_DEBUG(nht, NHT))
+ zlog_debug(
+ "Found existing bnc %pFX(%d)(%s) flags 0x%x ifindex %d #paths %d peer %p",
+ &bnc->prefix, bnc->ifindex,
+ bnc->bgp->name_pretty, bnc->flags, bnc->ifindex,
+ bnc->path_count, bnc->nht_info);
+ }
+
+ if (pi && is_route_parent_evpn(pi))
+ bnc->is_evpn_gwip_nexthop = true;
+
+ if (is_bgp_static_route) {
+ SET_FLAG(bnc->flags, BGP_STATIC_ROUTE);
+
+ /* If we're toggling the type, re-register */
+ if ((CHECK_FLAG(bgp_route->flags, BGP_FLAG_IMPORT_CHECK))
+ && !CHECK_FLAG(bnc->flags, BGP_STATIC_ROUTE_EXACT_MATCH)) {
+ SET_FLAG(bnc->flags, BGP_STATIC_ROUTE_EXACT_MATCH);
+ UNSET_FLAG(bnc->flags, BGP_NEXTHOP_REGISTERED);
+ UNSET_FLAG(bnc->flags, BGP_NEXTHOP_VALID);
+ } else if ((!CHECK_FLAG(bgp_route->flags,
+ BGP_FLAG_IMPORT_CHECK))
+ && CHECK_FLAG(bnc->flags,
+ BGP_STATIC_ROUTE_EXACT_MATCH)) {
+ UNSET_FLAG(bnc->flags, BGP_STATIC_ROUTE_EXACT_MATCH);
+ UNSET_FLAG(bnc->flags, BGP_NEXTHOP_REGISTERED);
+ UNSET_FLAG(bnc->flags, BGP_NEXTHOP_VALID);
+ }
+ }
+ /* When nexthop is already known, but now requires 'connected'
+ * resolution,
+ * re-register it. The reverse scenario where the nexthop currently
+ * requires
+ * 'connected' resolution does not need a re-register (i.e., we treat
+ * 'connected-required' as an override) except in the scenario where
+ * this
+ * is actually a case of tracking a peer for connectivity (e.g., after
+ * disable connected-check).
+ * NOTE: We don't track the number of paths separately for 'connected-
+ * required' vs 'connected-not-required' as this change is not a common
+ * scenario.
+ */
+ else if (connected && !CHECK_FLAG(bnc->flags, BGP_NEXTHOP_CONNECTED)) {
+ SET_FLAG(bnc->flags, BGP_NEXTHOP_CONNECTED);
+ UNSET_FLAG(bnc->flags, BGP_NEXTHOP_REGISTERED);
+ UNSET_FLAG(bnc->flags, BGP_NEXTHOP_VALID);
+ } else if (peer && !connected
+ && CHECK_FLAG(bnc->flags, BGP_NEXTHOP_CONNECTED)) {
+ UNSET_FLAG(bnc->flags, BGP_NEXTHOP_CONNECTED);
+ UNSET_FLAG(bnc->flags, BGP_NEXTHOP_REGISTERED);
+ UNSET_FLAG(bnc->flags, BGP_NEXTHOP_VALID);
+ }
+ if (peer && (bnc->ifindex != ifindex)) {
+ UNSET_FLAG(bnc->flags, BGP_NEXTHOP_REGISTERED);
+ UNSET_FLAG(bnc->flags, BGP_NEXTHOP_VALID);
+ bnc->ifindex = ifindex;
+ }
+ if (bgp_route->inst_type == BGP_INSTANCE_TYPE_VIEW) {
+ SET_FLAG(bnc->flags, BGP_NEXTHOP_REGISTERED);
+ SET_FLAG(bnc->flags, BGP_NEXTHOP_VALID);
+ } else if (!CHECK_FLAG(bnc->flags, BGP_NEXTHOP_REGISTERED)
+ && !is_default_host_route(&bnc->prefix))
+ register_zebra_rnh(bnc);
+
+ if (pi && pi->nexthop != bnc) {
+ /* Unlink from existing nexthop cache, if any. This will also
+ * free
+ * the nexthop cache entry, if appropriate.
+ */
+ bgp_unlink_nexthop(pi);
+
+ /* updates NHT pi list reference */
+ path_nh_map(pi, bnc, true);
+
+ if (CHECK_FLAG(bnc->flags, BGP_NEXTHOP_VALID) && bnc->metric)
+ (bgp_path_info_extra_get(pi))->igpmetric = bnc->metric;
+ else if (pi->extra)
+ pi->extra->igpmetric = 0;
+ } else if (peer) {
+ /*
+ * Let's not accidentally save the peer data for a peer
+ * we are going to throw away in a second or so.
+ * When we come back around we'll fix up this
+ * data properly in replace_nexthop_by_peer
+ */
+ if (CHECK_FLAG(peer->flags, PEER_FLAG_CONFIG_NODE))
+ bnc->nht_info = (void *)peer; /* NHT peer reference */
+ }
+
+ /*
+ * We are cheating here. Views have no associated underlying
+ * ability to detect nexthops. So when we have a view
+ * just tell everyone the nexthop is valid
+ */
+ if (bgp_route->inst_type == BGP_INSTANCE_TYPE_VIEW)
+ return 1;
+ else if (safi == SAFI_UNICAST && pi &&
+ pi->sub_type == BGP_ROUTE_IMPORTED && pi->extra &&
+ pi->extra->num_labels && !bnc->is_evpn_gwip_nexthop)
+ return bgp_isvalid_nexthop_for_mpls(bnc, pi);
+ else
+ return (bgp_isvalid_nexthop(bnc));
+}
+
+void bgp_delete_connected_nexthop(afi_t afi, struct peer *peer)
+{
+ struct bgp_nexthop_cache *bnc;
+ struct prefix p;
+ ifindex_t ifindex = 0;
+
+ if (!peer)
+ return;
+
+ if (!sockunion2hostprefix(&peer->su, &p))
+ return;
+ /*
+ * Gather the ifindex for if up/down events to be
+ * tagged into this fun
+ */
+ if (afi == AFI_IP6 && IN6_IS_ADDR_LINKLOCAL(&peer->su.sin6.sin6_addr))
+ ifindex = peer->su.sin6.sin6_scope_id;
+ bnc = bnc_find(&peer->bgp->nexthop_cache_table[family2afi(p.family)],
+ &p, 0, ifindex);
+ if (!bnc) {
+ if (BGP_DEBUG(nht, NHT))
+ zlog_debug(
+ "Cannot find connected NHT node for peer %s(%s)",
+ peer->host, peer->bgp->name_pretty);
+ return;
+ }
+
+ if (bnc->nht_info != peer) {
+ if (BGP_DEBUG(nht, NHT))
+ zlog_debug(
+ "Connected NHT %p node for peer %s(%s) points to %p",
+ bnc, peer->host, bnc->bgp->name_pretty,
+ bnc->nht_info);
+ return;
+ }
+
+ bnc->nht_info = NULL;
+
+ if (LIST_EMPTY(&(bnc->paths))) {
+ if (BGP_DEBUG(nht, NHT))
+ zlog_debug(
+ "Freeing connected NHT node %p for peer %s(%s)",
+ bnc, peer->host, bnc->bgp->name_pretty);
+ unregister_zebra_rnh(bnc);
+ bnc_free(bnc);
+ }
+}
+
+static void bgp_process_nexthop_update(struct bgp_nexthop_cache *bnc,
+ struct zapi_route *nhr,
+ bool import_check)
+{
+ struct nexthop *nexthop;
+ struct nexthop *oldnh;
+ struct nexthop *nhlist_head = NULL;
+ struct nexthop *nhlist_tail = NULL;
+ int i;
+ bool evpn_resolved = false;
+
+ bnc->last_update = monotime(NULL);
+ bnc->change_flags = 0;
+
+ /* debug print the input */
+ if (BGP_DEBUG(nht, NHT)) {
+ char bnc_buf[BNC_FLAG_DUMP_SIZE];
+
+ zlog_debug(
+ "%s(%u): Rcvd NH update %pFX(%u)%u) - metric %d/%d #nhops %d/%d flags %s",
+ bnc->bgp->name_pretty, bnc->bgp->vrf_id, &nhr->prefix,
+ bnc->ifindex, bnc->srte_color, nhr->metric, bnc->metric,
+ nhr->nexthop_num, bnc->nexthop_num,
+ bgp_nexthop_dump_bnc_flags(bnc, bnc_buf,
+ sizeof(bnc_buf)));
+ }
+
+ if (nhr->metric != bnc->metric)
+ bnc->change_flags |= BGP_NEXTHOP_METRIC_CHANGED;
+
+ if (nhr->nexthop_num != bnc->nexthop_num)
+ bnc->change_flags |= BGP_NEXTHOP_CHANGED;
+
+ if (import_check && (nhr->type == ZEBRA_ROUTE_BGP ||
+ !prefix_same(&bnc->prefix, &nhr->prefix))) {
+ SET_FLAG(bnc->change_flags, BGP_NEXTHOP_CHANGED);
+ UNSET_FLAG(bnc->flags, BGP_NEXTHOP_VALID);
+ UNSET_FLAG(bnc->flags, BGP_NEXTHOP_LABELED_VALID);
+ UNSET_FLAG(bnc->flags, BGP_NEXTHOP_EVPN_INCOMPLETE);
+
+ bnc_nexthop_free(bnc);
+ bnc->nexthop = NULL;
+
+ if (BGP_DEBUG(nht, NHT))
+ zlog_debug(
+ "%s: Import Check does not resolve to the same prefix for %pFX received %pFX or matching route is BGP",
+ __func__, &bnc->prefix, &nhr->prefix);
+ } else if (nhr->nexthop_num) {
+ struct peer *peer = bnc->nht_info;
+
+ /* notify bgp fsm if nbr ip goes from invalid->valid */
+ if (!bnc->nexthop_num)
+ UNSET_FLAG(bnc->flags, BGP_NEXTHOP_PEER_NOTIFIED);
+
+ if (!bnc->is_evpn_gwip_nexthop)
+ bnc->flags |= BGP_NEXTHOP_VALID;
+ bnc->metric = nhr->metric;
+ bnc->nexthop_num = nhr->nexthop_num;
+
+ bnc->flags &= ~BGP_NEXTHOP_LABELED_VALID; /* check below */
+
+ for (i = 0; i < nhr->nexthop_num; i++) {
+ int num_labels = 0;
+
+ nexthop = nexthop_from_zapi_nexthop(&nhr->nexthops[i]);
+
+ /*
+ * Turn on RA for the v6 nexthops
+ * we receive from bgp. This is to allow us
+ * to work with v4 routing over v6 nexthops
+ */
+ if (peer && !peer->ifp
+ && CHECK_FLAG(peer->flags,
+ PEER_FLAG_CAPABILITY_ENHE)
+ && nhr->prefix.family == AF_INET6
+ && nexthop->type != NEXTHOP_TYPE_BLACKHOLE) {
+ struct interface *ifp;
+
+ ifp = if_lookup_by_index(nexthop->ifindex,
+ nexthop->vrf_id);
+ if (ifp)
+ zclient_send_interface_radv_req(
+ zclient, nexthop->vrf_id, ifp,
+ true,
+ BGP_UNNUM_DEFAULT_RA_INTERVAL);
+ }
+ /* There is at least one label-switched path */
+ if (nexthop->nh_label &&
+ nexthop->nh_label->num_labels) {
+
+ bnc->flags |= BGP_NEXTHOP_LABELED_VALID;
+ num_labels = nexthop->nh_label->num_labels;
+ }
+
+ if (BGP_DEBUG(nht, NHT)) {
+ char buf[NEXTHOP_STRLEN];
+ zlog_debug(
+ " nhop via %s (%d labels)",
+ nexthop2str(nexthop, buf, sizeof(buf)),
+ num_labels);
+ }
+
+ if (nhlist_tail) {
+ nhlist_tail->next = nexthop;
+ nhlist_tail = nexthop;
+ } else {
+ nhlist_tail = nexthop;
+ nhlist_head = nexthop;
+ }
+
+ /* No need to evaluate the nexthop if we have already
+ * determined
+ * that there has been a change.
+ */
+ if (bnc->change_flags & BGP_NEXTHOP_CHANGED)
+ continue;
+
+ for (oldnh = bnc->nexthop; oldnh; oldnh = oldnh->next)
+ if (nexthop_same(oldnh, nexthop))
+ break;
+
+ if (!oldnh)
+ bnc->change_flags |= BGP_NEXTHOP_CHANGED;
+ }
+ bnc_nexthop_free(bnc);
+ bnc->nexthop = nhlist_head;
+
+ /*
+ * Gateway IP nexthop is L3 reachable. Mark it as
+ * BGP_NEXTHOP_VALID only if it is recursively resolved with a
+ * remote EVPN RT-2.
+ * Else, mark it as BGP_NEXTHOP_EVPN_INCOMPLETE.
+ * When its mapping with EVPN RT-2 is established, unset
+ * BGP_NEXTHOP_EVPN_INCOMPLETE and set BGP_NEXTHOP_VALID.
+ */
+ if (bnc->is_evpn_gwip_nexthop) {
+ evpn_resolved = bgp_evpn_is_gateway_ip_resolved(bnc);
+
+ if (BGP_DEBUG(nht, NHT))
+ zlog_debug(
+ "EVPN gateway IP %pFX recursive MAC/IP lookup %s",
+ &bnc->prefix,
+ (evpn_resolved ? "successful"
+ : "failed"));
+
+ if (evpn_resolved) {
+ bnc->flags |= BGP_NEXTHOP_VALID;
+ bnc->flags &= ~BGP_NEXTHOP_EVPN_INCOMPLETE;
+ bnc->change_flags |= BGP_NEXTHOP_MACIP_CHANGED;
+ } else {
+ bnc->flags |= BGP_NEXTHOP_EVPN_INCOMPLETE;
+ bnc->flags &= ~BGP_NEXTHOP_VALID;
+ }
+ }
+ } else {
+ bnc->flags &= ~BGP_NEXTHOP_EVPN_INCOMPLETE;
+ bnc->flags &= ~BGP_NEXTHOP_VALID;
+ bnc->flags &= ~BGP_NEXTHOP_LABELED_VALID;
+ bnc->nexthop_num = nhr->nexthop_num;
+
+ /* notify bgp fsm if nbr ip goes from valid->invalid */
+ UNSET_FLAG(bnc->flags, BGP_NEXTHOP_PEER_NOTIFIED);
+
+ bnc_nexthop_free(bnc);
+ bnc->nexthop = NULL;
+ }
+
+ evaluate_paths(bnc);
+}
+
+static void bgp_nht_ifp_table_handle(struct bgp *bgp,
+ struct bgp_nexthop_cache_head *table,
+ struct interface *ifp, bool up)
+{
+ struct bgp_nexthop_cache *bnc;
+
+ frr_each (bgp_nexthop_cache, table, bnc) {
+ if (bnc->ifindex != ifp->ifindex)
+ continue;
+
+ bnc->last_update = monotime(NULL);
+ bnc->change_flags = 0;
+
+ /*
+ * For interface based routes ( ala the v6 LL routes
+ * that this was written for ) the metric received
+ * for the connected route is 0 not 1.
+ */
+ bnc->metric = 0;
+ if (up) {
+ SET_FLAG(bnc->flags, BGP_NEXTHOP_VALID);
+ SET_FLAG(bnc->change_flags, BGP_NEXTHOP_CHANGED);
+ bnc->nexthop_num = 1;
+ } else {
+ UNSET_FLAG(bnc->flags, BGP_NEXTHOP_PEER_NOTIFIED);
+ UNSET_FLAG(bnc->flags, BGP_NEXTHOP_VALID);
+ SET_FLAG(bnc->change_flags, BGP_NEXTHOP_CHANGED);
+ bnc->nexthop_num = 0;
+ }
+
+ evaluate_paths(bnc);
+ }
+}
+static void bgp_nht_ifp_handle(struct interface *ifp, bool up)
+{
+ struct bgp *bgp;
+
+ bgp = ifp->vrf->info;
+ if (!bgp)
+ return;
+
+ bgp_nht_ifp_table_handle(bgp, &bgp->nexthop_cache_table[AFI_IP], ifp,
+ up);
+ bgp_nht_ifp_table_handle(bgp, &bgp->import_check_table[AFI_IP], ifp,
+ up);
+ bgp_nht_ifp_table_handle(bgp, &bgp->nexthop_cache_table[AFI_IP6], ifp,
+ up);
+ bgp_nht_ifp_table_handle(bgp, &bgp->import_check_table[AFI_IP6], ifp,
+ up);
+}
+
+void bgp_nht_ifp_up(struct interface *ifp)
+{
+ bgp_nht_ifp_handle(ifp, true);
+}
+
+void bgp_nht_ifp_down(struct interface *ifp)
+{
+ bgp_nht_ifp_handle(ifp, false);
+}
+
+static void bgp_nht_ifp_initial(struct thread *thread)
+{
+ ifindex_t ifindex = THREAD_VAL(thread);
+ struct bgp *bgp = THREAD_ARG(thread);
+ struct interface *ifp = if_lookup_by_index(ifindex, bgp->vrf_id);
+
+ if (!ifp)
+ return;
+
+ if (BGP_DEBUG(nht, NHT))
+ zlog_debug(
+ "Handle NHT initial update for Intf %s(%d) status %s",
+ ifp->name, ifp->ifindex, if_is_up(ifp) ? "up" : "down");
+
+ if (if_is_up(ifp))
+ bgp_nht_ifp_up(ifp);
+ else
+ bgp_nht_ifp_down(ifp);
+}
+
+/*
+ * So the bnc code has the ability to handle interface up/down
+ * events to properly handle v6 LL peering.
+ * What is happening here:
+ * The event system for peering expects the nht code to
+ * report on the tracking events after we move to active
+ * So let's give the system a chance to report on that event
+ * in a manner that is expected.
+ */
+void bgp_nht_interface_events(struct peer *peer)
+{
+ struct bgp *bgp = peer->bgp;
+ struct bgp_nexthop_cache_head *table;
+ struct bgp_nexthop_cache *bnc;
+ struct prefix p;
+ ifindex_t ifindex = 0;
+
+ if (!IN6_IS_ADDR_LINKLOCAL(&peer->su.sin6.sin6_addr))
+ return;
+
+ if (!sockunion2hostprefix(&peer->su, &p))
+ return;
+ /*
+ * Gather the ifindex for if up/down events to be
+ * tagged into this fun
+ */
+ if (peer->conf_if && IN6_IS_ADDR_LINKLOCAL(&peer->su.sin6.sin6_addr))
+ ifindex = peer->su.sin6.sin6_scope_id;
+
+ table = &bgp->nexthop_cache_table[AFI_IP6];
+ bnc = bnc_find(table, &p, 0, ifindex);
+ if (!bnc)
+ return;
+
+ if (bnc->ifindex)
+ thread_add_event(bm->master, bgp_nht_ifp_initial, bnc->bgp,
+ bnc->ifindex, NULL);
+}
+
+void bgp_parse_nexthop_update(int command, vrf_id_t vrf_id)
+{
+ struct bgp_nexthop_cache_head *tree = NULL;
+ struct bgp_nexthop_cache *bnc_nhc, *bnc_import;
+ struct bgp *bgp;
+ struct prefix match;
+ struct zapi_route nhr;
+ afi_t afi;
+
+ bgp = bgp_lookup_by_vrf_id(vrf_id);
+ if (!bgp) {
+ flog_err(
+ EC_BGP_NH_UPD,
+ "parse nexthop update: instance not found for vrf_id %u",
+ vrf_id);
+ return;
+ }
+
+ if (!zapi_nexthop_update_decode(zclient->ibuf, &match, &nhr)) {
+ zlog_err("%s[%s]: Failure to decode nexthop update", __func__,
+ bgp->name_pretty);
+ return;
+ }
+
+ afi = family2afi(match.family);
+ tree = &bgp->nexthop_cache_table[afi];
+
+ bnc_nhc = bnc_find(tree, &match, nhr.srte_color, 0);
+ if (!bnc_nhc) {
+ if (BGP_DEBUG(nht, NHT))
+ zlog_debug(
+ "parse nexthop update(%pFX(%u)(%s)): bnc info not found for nexthop cache",
+ &nhr.prefix, nhr.srte_color, bgp->name_pretty);
+ } else
+ bgp_process_nexthop_update(bnc_nhc, &nhr, false);
+
+ tree = &bgp->import_check_table[afi];
+
+ bnc_import = bnc_find(tree, &match, nhr.srte_color, 0);
+ if (!bnc_import) {
+ if (BGP_DEBUG(nht, NHT))
+ zlog_debug(
+ "parse nexthop update(%pFX(%u)(%s)): bnc info not found for import check",
+ &nhr.prefix, nhr.srte_color, bgp->name_pretty);
+ } else
+ bgp_process_nexthop_update(bnc_import, &nhr, true);
+
+ /*
+ * HACK: if any BGP route is dependant on an SR-policy that doesn't
+ * exist, zebra will never send NH updates relative to that policy. In
+ * that case, whenever we receive an update about a colorless NH, update
+ * the corresponding colorful NHs that share the same endpoint but that
+ * are inactive. This ugly hack should work around the problem at the
+ * cost of a performance pernalty. Long term, what should be done is to
+ * make zebra's RNH subsystem aware of SR-TE colors (like bgpd is),
+ * which should provide a better infrastructure to solve this issue in
+ * a more efficient and elegant way.
+ */
+ if (nhr.srte_color == 0 && bnc_nhc) {
+ struct bgp_nexthop_cache *bnc_iter;
+
+ frr_each (bgp_nexthop_cache, &bgp->nexthop_cache_table[afi],
+ bnc_iter) {
+ if (!prefix_same(&bnc_nhc->prefix, &bnc_iter->prefix) ||
+ bnc_iter->srte_color == 0 ||
+ CHECK_FLAG(bnc_iter->flags, BGP_NEXTHOP_VALID))
+ continue;
+
+ bgp_process_nexthop_update(bnc_iter, &nhr, false);
+ }
+ }
+}
+
+/*
+ * Cleanup nexthop registration and status information for BGP nexthops
+ * pertaining to this VRF. This is invoked upon VRF deletion.
+ */
+void bgp_cleanup_nexthops(struct bgp *bgp)
+{
+ for (afi_t afi = AFI_IP; afi < AFI_MAX; afi++) {
+ struct bgp_nexthop_cache *bnc;
+
+ frr_each (bgp_nexthop_cache, &bgp->nexthop_cache_table[afi],
+ bnc) {
+ /* Clear relevant flags. */
+ UNSET_FLAG(bnc->flags, BGP_NEXTHOP_VALID);
+ UNSET_FLAG(bnc->flags, BGP_NEXTHOP_REGISTERED);
+ UNSET_FLAG(bnc->flags, BGP_NEXTHOP_PEER_NOTIFIED);
+ UNSET_FLAG(bnc->flags, BGP_NEXTHOP_EVPN_INCOMPLETE);
+ }
+ }
+}
+
+/**
+ * make_prefix - make a prefix structure from the path (essentially
+ * path's node.
+ */
+static int make_prefix(int afi, struct bgp_path_info *pi, struct prefix *p)
+{
+
+ int is_bgp_static = ((pi->type == ZEBRA_ROUTE_BGP)
+ && (pi->sub_type == BGP_ROUTE_STATIC))
+ ? 1
+ : 0;
+ struct bgp_dest *net = pi->net;
+ const struct prefix *p_orig = bgp_dest_get_prefix(net);
+ struct in_addr ipv4;
+
+ if (p_orig->family == AF_FLOWSPEC) {
+ if (!pi->peer)
+ return -1;
+ return bgp_flowspec_get_first_nh(pi->peer->bgp,
+ pi, p, afi);
+ }
+ memset(p, 0, sizeof(struct prefix));
+ switch (afi) {
+ case AFI_IP:
+ p->family = AF_INET;
+ if (is_bgp_static) {
+ p->u.prefix4 = p_orig->u.prefix4;
+ p->prefixlen = p_orig->prefixlen;
+ } else {
+ if (IS_MAPPED_IPV6(&pi->attr->mp_nexthop_global)) {
+ ipv4_mapped_ipv6_to_ipv4(
+ &pi->attr->mp_nexthop_global, &ipv4);
+ p->u.prefix4 = ipv4;
+ p->prefixlen = IPV4_MAX_BITLEN;
+ } else {
+ if (p_orig->family == AF_EVPN)
+ p->u.prefix4 =
+ pi->attr->mp_nexthop_global_in;
+ else
+ p->u.prefix4 = pi->attr->nexthop;
+ p->prefixlen = IPV4_MAX_BITLEN;
+ }
+ }
+ break;
+ case AFI_IP6:
+ p->family = AF_INET6;
+
+ if (is_bgp_static) {
+ p->u.prefix6 = p_orig->u.prefix6;
+ p->prefixlen = p_orig->prefixlen;
+ } else {
+ /* If we receive MP_REACH nexthop with ::(LL)
+ * or LL(LL), use LL address as nexthop cache.
+ */
+ if (pi->attr->mp_nexthop_len
+ == BGP_ATTR_NHLEN_IPV6_GLOBAL_AND_LL
+ && (IN6_IS_ADDR_UNSPECIFIED(
+ &pi->attr->mp_nexthop_global)
+ || IN6_IS_ADDR_LINKLOCAL(
+ &pi->attr->mp_nexthop_global)))
+ p->u.prefix6 = pi->attr->mp_nexthop_local;
+ /* If we receive MR_REACH with (GA)::(LL)
+ * then check for route-map to choose GA or LL
+ */
+ else if (pi->attr->mp_nexthop_len
+ == BGP_ATTR_NHLEN_IPV6_GLOBAL_AND_LL) {
+ if (pi->attr->mp_nexthop_prefer_global)
+ p->u.prefix6 =
+ pi->attr->mp_nexthop_global;
+ else
+ p->u.prefix6 =
+ pi->attr->mp_nexthop_local;
+ } else
+ p->u.prefix6 = pi->attr->mp_nexthop_global;
+ p->prefixlen = IPV6_MAX_BITLEN;
+ }
+ break;
+ default:
+ if (BGP_DEBUG(nht, NHT)) {
+ zlog_debug(
+ "%s: Attempting to make prefix with unknown AFI %d (not %d or %d)",
+ __func__, afi, AFI_IP, AFI_IP6);
+ }
+ break;
+ }
+ return 0;
+}
+
+/**
+ * sendmsg_zebra_rnh -- Format and send a nexthop register/Unregister
+ * command to Zebra.
+ * ARGUMENTS:
+ * struct bgp_nexthop_cache *bnc -- the nexthop structure.
+ * int command -- command to send to zebra
+ * RETURNS:
+ * void.
+ */
+static void sendmsg_zebra_rnh(struct bgp_nexthop_cache *bnc, int command)
+{
+ bool exact_match = false;
+ bool resolve_via_default = false;
+ int ret;
+
+ if (!zclient)
+ return;
+
+ /* Don't try to register if Zebra doesn't know of this instance. */
+ if (!IS_BGP_INST_KNOWN_TO_ZEBRA(bnc->bgp)) {
+ if (BGP_DEBUG(zebra, ZEBRA))
+ zlog_debug(
+ "%s: No zebra instance to talk to, not installing NHT entry",
+ __func__);
+ return;
+ }
+
+ if (!bgp_zebra_num_connects()) {
+ if (BGP_DEBUG(zebra, ZEBRA))
+ zlog_debug(
+ "%s: We have not connected yet, cannot send nexthops",
+ __func__);
+ }
+ if (command == ZEBRA_NEXTHOP_REGISTER) {
+ if (CHECK_FLAG(bnc->flags, BGP_NEXTHOP_CONNECTED))
+ exact_match = true;
+ if (CHECK_FLAG(bnc->flags, BGP_STATIC_ROUTE_EXACT_MATCH))
+ resolve_via_default = true;
+ }
+
+ if (BGP_DEBUG(zebra, ZEBRA))
+ zlog_debug("%s: sending cmd %s for %pFX (vrf %s)", __func__,
+ zserv_command_string(command), &bnc->prefix,
+ bnc->bgp->name_pretty);
+
+ ret = zclient_send_rnh(zclient, command, &bnc->prefix, SAFI_UNICAST,
+ exact_match, resolve_via_default,
+ bnc->bgp->vrf_id);
+ if (ret == ZCLIENT_SEND_FAILURE) {
+ flog_warn(EC_BGP_ZEBRA_SEND,
+ "sendmsg_nexthop: zclient_send_message() failed");
+ return;
+ }
+
+ if (command == ZEBRA_NEXTHOP_REGISTER)
+ SET_FLAG(bnc->flags, BGP_NEXTHOP_REGISTERED);
+ else if (command == ZEBRA_NEXTHOP_UNREGISTER)
+ UNSET_FLAG(bnc->flags, BGP_NEXTHOP_REGISTERED);
+ return;
+}
+
+/**
+ * register_zebra_rnh - register a NH/route with Zebra for notification
+ * when the route or the route to the nexthop changes.
+ * ARGUMENTS:
+ * struct bgp_nexthop_cache *bnc
+ * RETURNS:
+ * void.
+ */
+static void register_zebra_rnh(struct bgp_nexthop_cache *bnc)
+{
+ /* Check if we have already registered */
+ if (bnc->flags & BGP_NEXTHOP_REGISTERED)
+ return;
+
+ if (bnc->ifindex) {
+ SET_FLAG(bnc->flags, BGP_NEXTHOP_REGISTERED);
+ return;
+ }
+
+ sendmsg_zebra_rnh(bnc, ZEBRA_NEXTHOP_REGISTER);
+}
+
+/**
+ * unregister_zebra_rnh -- Unregister the route/nexthop from Zebra.
+ * ARGUMENTS:
+ * struct bgp_nexthop_cache *bnc
+ * RETURNS:
+ * void.
+ */
+static void unregister_zebra_rnh(struct bgp_nexthop_cache *bnc)
+{
+ /* Check if we have already registered */
+ if (!CHECK_FLAG(bnc->flags, BGP_NEXTHOP_REGISTERED))
+ return;
+
+ if (bnc->ifindex) {
+ UNSET_FLAG(bnc->flags, BGP_NEXTHOP_REGISTERED);
+ return;
+ }
+
+ sendmsg_zebra_rnh(bnc, ZEBRA_NEXTHOP_UNREGISTER);
+}
+
+/**
+ * evaluate_paths - Evaluate the paths/nets associated with a nexthop.
+ * ARGUMENTS:
+ * struct bgp_nexthop_cache *bnc -- the nexthop structure.
+ * RETURNS:
+ * void.
+ */
+void evaluate_paths(struct bgp_nexthop_cache *bnc)
+{
+ struct bgp_dest *dest;
+ struct bgp_path_info *path;
+ int afi;
+ struct peer *peer = (struct peer *)bnc->nht_info;
+ struct bgp_table *table;
+ safi_t safi;
+ struct bgp *bgp_path;
+ const struct prefix *p;
+
+ if (BGP_DEBUG(nht, NHT)) {
+ char bnc_buf[BNC_FLAG_DUMP_SIZE];
+ char chg_buf[BNC_FLAG_DUMP_SIZE];
+
+ zlog_debug(
+ "NH update for %pFX(%d)(%u)(%s) - flags %s chgflags %s- evaluate paths",
+ &bnc->prefix, bnc->ifindex, bnc->srte_color,
+ bnc->bgp->name_pretty,
+ bgp_nexthop_dump_bnc_flags(bnc, bnc_buf,
+ sizeof(bnc_buf)),
+ bgp_nexthop_dump_bnc_change_flags(bnc, chg_buf,
+ sizeof(bnc_buf)));
+ }
+
+ LIST_FOREACH (path, &(bnc->paths), nh_thread) {
+ if (!(path->type == ZEBRA_ROUTE_BGP
+ && ((path->sub_type == BGP_ROUTE_NORMAL)
+ || (path->sub_type == BGP_ROUTE_STATIC)
+ || (path->sub_type == BGP_ROUTE_IMPORTED))))
+ continue;
+
+ dest = path->net;
+ assert(dest && bgp_dest_table(dest));
+ p = bgp_dest_get_prefix(dest);
+ afi = family2afi(p->family);
+ table = bgp_dest_table(dest);
+ safi = table->safi;
+
+ /*
+ * handle routes from other VRFs (they can have a
+ * nexthop in THIS VRF). bgp_path is the bgp instance
+ * that owns the route referencing this nexthop.
+ */
+ bgp_path = table->bgp;
+
+ /*
+ * Path becomes valid/invalid depending on whether the nexthop
+ * reachable/unreachable.
+ *
+ * In case of unicast routes that were imported from vpn
+ * and that have labels, they are valid only if there are
+ * nexthops with labels
+ *
+ * If the nexthop is EVPN gateway-IP,
+ * do not check for a valid label.
+ */
+
+ bool bnc_is_valid_nexthop = false;
+ bool path_valid = false;
+
+ if (safi == SAFI_UNICAST && path->sub_type == BGP_ROUTE_IMPORTED
+ && path->extra && path->extra->num_labels
+ && (path->attr->evpn_overlay.type
+ != OVERLAY_INDEX_GATEWAY_IP)) {
+ bnc_is_valid_nexthop =
+ bgp_isvalid_nexthop_for_mpls(bnc, path) ? true
+ : false;
+ } else {
+ if (bgp_update_martian_nexthop(
+ bnc->bgp, afi, safi, path->type,
+ path->sub_type, path->attr, dest)) {
+ if (BGP_DEBUG(nht, NHT))
+ zlog_debug(
+ "%s: prefix %pBD (vrf %s), ignoring path due to martian or self-next-hop",
+ __func__, dest, bgp_path->name);
+ } else
+ bnc_is_valid_nexthop =
+ bgp_isvalid_nexthop(bnc) ? true : false;
+ }
+
+ if (BGP_DEBUG(nht, NHT)) {
+ if (dest->pdest)
+ zlog_debug(
+ "... eval path %d/%d %pBD RD %pRD %s flags 0x%x",
+ afi, safi, dest,
+ (struct prefix_rd *)bgp_dest_get_prefix(
+ dest->pdest),
+ bgp_path->name_pretty, path->flags);
+ else
+ zlog_debug(
+ "... eval path %d/%d %pBD %s flags 0x%x",
+ afi, safi, dest, bgp_path->name_pretty,
+ path->flags);
+ }
+
+ /* Skip paths marked for removal or as history. */
+ if (CHECK_FLAG(path->flags, BGP_PATH_REMOVED)
+ || CHECK_FLAG(path->flags, BGP_PATH_HISTORY))
+ continue;
+
+ /* Copy the metric to the path. Will be used for bestpath
+ * computation */
+ if (bgp_isvalid_nexthop(bnc) && bnc->metric)
+ (bgp_path_info_extra_get(path))->igpmetric =
+ bnc->metric;
+ else if (path->extra)
+ path->extra->igpmetric = 0;
+
+ if (CHECK_FLAG(bnc->change_flags, BGP_NEXTHOP_METRIC_CHANGED)
+ || CHECK_FLAG(bnc->change_flags, BGP_NEXTHOP_CHANGED)
+ || path->attr->srte_color != 0)
+ SET_FLAG(path->flags, BGP_PATH_IGP_CHANGED);
+
+ path_valid = CHECK_FLAG(path->flags, BGP_PATH_VALID);
+ if (path_valid != bnc_is_valid_nexthop) {
+ if (path_valid) {
+ /* No longer valid, clear flag; also for EVPN
+ * routes, unimport from VRFs if needed.
+ */
+ bgp_aggregate_decrement(bgp_path, p, path, afi,
+ safi);
+ bgp_path_info_unset_flag(dest, path,
+ BGP_PATH_VALID);
+ if (safi == SAFI_EVPN &&
+ bgp_evpn_is_prefix_nht_supported(bgp_dest_get_prefix(dest)))
+ bgp_evpn_unimport_route(bgp_path,
+ afi, safi, bgp_dest_get_prefix(dest), path);
+ } else {
+ /* Path becomes valid, set flag; also for EVPN
+ * routes, import from VRFs if needed.
+ */
+ bgp_path_info_set_flag(dest, path,
+ BGP_PATH_VALID);
+ bgp_aggregate_increment(bgp_path, p, path, afi,
+ safi);
+ if (safi == SAFI_EVPN &&
+ bgp_evpn_is_prefix_nht_supported(bgp_dest_get_prefix(dest)))
+ bgp_evpn_import_route(bgp_path,
+ afi, safi, bgp_dest_get_prefix(dest), path);
+ }
+ }
+
+ bgp_process(bgp_path, dest, afi, safi);
+ }
+
+ if (peer) {
+ int valid_nexthops = bgp_isvalid_nexthop(bnc);
+
+ if (valid_nexthops) {
+ /*
+ * Peering cannot occur across a blackhole nexthop
+ */
+ if (bnc->nexthop_num == 1 && bnc->nexthop
+ && bnc->nexthop->type == NEXTHOP_TYPE_BLACKHOLE) {
+ peer->last_reset = PEER_DOWN_WAITING_NHT;
+ valid_nexthops = 0;
+ } else
+ peer->last_reset = PEER_DOWN_WAITING_OPEN;
+ } else
+ peer->last_reset = PEER_DOWN_WAITING_NHT;
+
+ if (!CHECK_FLAG(bnc->flags, BGP_NEXTHOP_PEER_NOTIFIED)) {
+ if (BGP_DEBUG(nht, NHT))
+ zlog_debug(
+ "%s: Updating peer (%s(%s)) status with NHT nexthops %d",
+ __func__, peer->host,
+ peer->bgp->name_pretty,
+ !!valid_nexthops);
+ bgp_fsm_nht_update(peer, !!valid_nexthops);
+ SET_FLAG(bnc->flags, BGP_NEXTHOP_PEER_NOTIFIED);
+ }
+ }
+
+ RESET_FLAG(bnc->change_flags);
+}
+
+/**
+ * path_nh_map - make or break path-to-nexthop association.
+ * ARGUMENTS:
+ * path - pointer to the path structure
+ * bnc - pointer to the nexthop structure
+ * make - if set, make the association. if unset, just break the existing
+ * association.
+ */
+void path_nh_map(struct bgp_path_info *path, struct bgp_nexthop_cache *bnc,
+ bool make)
+{
+ if (path->nexthop) {
+ LIST_REMOVE(path, nh_thread);
+ path->nexthop->path_count--;
+ path->nexthop = NULL;
+ }
+ if (make) {
+ LIST_INSERT_HEAD(&(bnc->paths), path, nh_thread);
+ path->nexthop = bnc;
+ path->nexthop->path_count++;
+ }
+}
+
+/*
+ * This function is called to register nexthops to zebra
+ * as that we may have tried to install the nexthops
+ * before we actually have a zebra connection
+ */
+void bgp_nht_register_nexthops(struct bgp *bgp)
+{
+ for (afi_t afi = AFI_IP; afi < AFI_MAX; afi++) {
+ struct bgp_nexthop_cache *bnc;
+
+ frr_each (bgp_nexthop_cache, &bgp->nexthop_cache_table[afi],
+ bnc) {
+ register_zebra_rnh(bnc);
+ }
+ }
+}
+
+void bgp_nht_reg_enhe_cap_intfs(struct peer *peer)
+{
+ struct bgp *bgp;
+ struct bgp_nexthop_cache *bnc;
+ struct nexthop *nhop;
+ struct interface *ifp;
+ struct prefix p;
+ ifindex_t ifindex = 0;
+
+ if (peer->ifp)
+ return;
+
+ bgp = peer->bgp;
+ if (!sockunion2hostprefix(&peer->su, &p)) {
+ zlog_warn("%s: Unable to convert sockunion to prefix for %s",
+ __func__, peer->host);
+ return;
+ }
+
+ if (p.family != AF_INET6)
+ return;
+ /*
+ * Gather the ifindex for if up/down events to be
+ * tagged into this fun
+ */
+ if (peer->conf_if && IN6_IS_ADDR_LINKLOCAL(&peer->su.sin6.sin6_addr))
+ ifindex = peer->su.sin6.sin6_scope_id;
+
+ bnc = bnc_find(&bgp->nexthop_cache_table[AFI_IP6], &p, 0, ifindex);
+ if (!bnc)
+ return;
+
+ if (peer != bnc->nht_info)
+ return;
+
+ for (nhop = bnc->nexthop; nhop; nhop = nhop->next) {
+ ifp = if_lookup_by_index(nhop->ifindex, nhop->vrf_id);
+
+ if (!ifp)
+ continue;
+
+ zclient_send_interface_radv_req(zclient,
+ nhop->vrf_id,
+ ifp, true,
+ BGP_UNNUM_DEFAULT_RA_INTERVAL);
+ }
+}
+
+void bgp_nht_dereg_enhe_cap_intfs(struct peer *peer)
+{
+ struct bgp *bgp;
+ struct bgp_nexthop_cache *bnc;
+ struct nexthop *nhop;
+ struct interface *ifp;
+ struct prefix p;
+ ifindex_t ifindex = 0;
+
+ if (peer->ifp)
+ return;
+
+ bgp = peer->bgp;
+
+ if (!sockunion2hostprefix(&peer->su, &p)) {
+ zlog_warn("%s: Unable to convert sockunion to prefix for %s",
+ __func__, peer->host);
+ return;
+ }
+
+ if (p.family != AF_INET6)
+ return;
+ /*
+ * Gather the ifindex for if up/down events to be
+ * tagged into this fun
+ */
+ if (peer->conf_if && IN6_IS_ADDR_LINKLOCAL(&peer->su.sin6.sin6_addr))
+ ifindex = peer->su.sin6.sin6_scope_id;
+
+ bnc = bnc_find(&bgp->nexthop_cache_table[AFI_IP6], &p, 0, ifindex);
+ if (!bnc)
+ return;
+
+ if (peer != bnc->nht_info)
+ return;
+
+ for (nhop = bnc->nexthop; nhop; nhop = nhop->next) {
+ ifp = if_lookup_by_index(nhop->ifindex, nhop->vrf_id);
+
+ if (!ifp)
+ continue;
+
+ zclient_send_interface_radv_req(zclient, nhop->vrf_id, ifp, 0,
+ 0);
+ }
+}
+
+/****************************************************************************
+ * L3 NHGs are used for fast failover of nexthops in the dplane. These are
+ * the APIs for allocating L3 NHG ids. Management of the L3 NHG itself is
+ * left to the application using it.
+ * PS: Currently EVPN host routes is the only app using L3 NHG for fast
+ * failover of remote ES links.
+ ***************************************************************************/
+static bitfield_t bgp_nh_id_bitmap;
+static uint32_t bgp_l3nhg_start;
+
+/* XXX - currently we do nothing on the callbacks */
+static void bgp_l3nhg_add_cb(const char *name)
+{
+}
+static void bgp_l3nhg_add_nexthop_cb(const struct nexthop_group_cmd *nhgc,
+ const struct nexthop *nhop)
+{
+}
+static void bgp_l3nhg_del_nexthop_cb(const struct nexthop_group_cmd *nhgc,
+ const struct nexthop *nhop)
+{
+}
+static void bgp_l3nhg_del_cb(const char *name)
+{
+}
+
+static void bgp_l3nhg_zebra_init(void)
+{
+ static bool bgp_l3nhg_zebra_inited;
+ if (bgp_l3nhg_zebra_inited)
+ return;
+
+ bgp_l3nhg_zebra_inited = true;
+ bgp_l3nhg_start = zclient_get_nhg_start(ZEBRA_ROUTE_BGP);
+ nexthop_group_init(bgp_l3nhg_add_cb, bgp_l3nhg_add_nexthop_cb,
+ bgp_l3nhg_del_nexthop_cb, bgp_l3nhg_del_cb);
+}
+
+
+void bgp_l3nhg_init(void)
+{
+ uint32_t id_max;
+
+ id_max = MIN(ZEBRA_NHG_PROTO_SPACING - 1, 16 * 1024);
+ bf_init(bgp_nh_id_bitmap, id_max);
+ bf_assign_zero_index(bgp_nh_id_bitmap);
+
+ if (BGP_DEBUG(nht, NHT) || BGP_DEBUG(evpn_mh, EVPN_MH_ES))
+ zlog_debug("bgp l3_nhg range %u - %u", bgp_l3nhg_start + 1,
+ bgp_l3nhg_start + id_max);
+}
+
+void bgp_l3nhg_finish(void)
+{
+ bf_free(bgp_nh_id_bitmap);
+}
+
+uint32_t bgp_l3nhg_id_alloc(void)
+{
+ uint32_t nhg_id = 0;
+
+ bgp_l3nhg_zebra_init();
+ bf_assign_index(bgp_nh_id_bitmap, nhg_id);
+ if (nhg_id)
+ nhg_id += bgp_l3nhg_start;
+
+ return nhg_id;
+}
+
+void bgp_l3nhg_id_free(uint32_t nhg_id)
+{
+ if (!nhg_id || (nhg_id <= bgp_l3nhg_start))
+ return;
+
+ nhg_id -= bgp_l3nhg_start;
+
+ bf_release_index(bgp_nh_id_bitmap, nhg_id);
+}
diff --git a/bgpd/bgp_nht.h b/bgpd/bgp_nht.h
new file mode 100644
index 0000000..43f0aa2
--- /dev/null
+++ b/bgpd/bgp_nht.h
@@ -0,0 +1,106 @@
+/* BGP Nexthop tracking
+ * Copyright (C) 2013 Cumulus Networks, Inc.
+ *
+ * This file is part of GNU Zebra.
+ *
+ * GNU Zebra is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License as published by the
+ * Free Software Foundation; either version 2, or (at your option) any
+ * later version.
+ *
+ * GNU Zebra is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program; see the file COPYING; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
+ */
+
+#ifndef _BGP_NHT_H
+#define _BGP_NHT_H
+
+/**
+ * bgp_parse_nexthop_update() - parse a nexthop update message from Zebra.
+ */
+extern void bgp_parse_nexthop_update(int command, vrf_id_t vrf_id);
+
+/**
+ * bgp_find_or_add_nexthop() - lookup the nexthop cache table for the bnc
+ * object. If not found, create a new object and register with ZEBRA for
+ * nexthop notification.
+ * ARGUMENTS:
+ * bgp_route - BGP instance of route
+ * bgp_nexthop - BGP instance of nexthop
+ * a - afi: AFI_IP or AF_IP6
+ * safi - safi: to check which table nhs are being imported to
+ * p - path for which the nexthop object is being looked up
+ * peer - The BGP peer associated with this NHT
+ * connected - True if NH MUST be a connected route
+ */
+extern int bgp_find_or_add_nexthop(struct bgp *bgp_route,
+ struct bgp *bgp_nexthop, afi_t a,
+ safi_t safi, struct bgp_path_info *p,
+ struct peer *peer, int connected,
+ const struct prefix *orig_prefix);
+
+/**
+ * bgp_unlink_nexthop() - Unlink the nexthop object from the path structure.
+ * ARGUMENTS:
+ * p - path structure.
+ */
+extern void bgp_unlink_nexthop(struct bgp_path_info *p);
+void bgp_unlink_nexthop_by_peer(struct peer *peer);
+void bgp_replace_nexthop_by_peer(struct peer *from, struct peer *to);
+/**
+ * bgp_delete_connected_nexthop() - Reset the 'peer' pointer for a connected
+ * nexthop entry. If no paths reference the nexthop, it will be unregistered
+ * and freed.
+ * ARGUMENTS:
+ * afi - afi: AFI_IP or AF_IP6
+ * peer - Ptr to peer
+ */
+extern void bgp_delete_connected_nexthop(afi_t afi, struct peer *peer);
+
+/*
+ * Cleanup nexthop registration and status information for BGP nexthops
+ * pertaining to this VRF. This is invoked upon VRF deletion.
+ */
+extern void bgp_cleanup_nexthops(struct bgp *bgp);
+
+/*
+ * Add or remove the tracking of the bgp_path_info that
+ * uses this nexthop
+ */
+extern void path_nh_map(struct bgp_path_info *path,
+ struct bgp_nexthop_cache *bnc, bool make);
+/*
+ * When we actually have the connection to
+ * the zebra daemon, we need to reregister
+ * any nexthops we may have sitting around
+ */
+extern void bgp_nht_register_nexthops(struct bgp *bgp);
+
+/*
+ * When we have the the PEER_FLAG_CAPABILITY_ENHE flag
+ * set on a peer *after* it has been brought up we need
+ * to notice and setup the interface based RA,
+ * this code can walk the registered nexthops and
+ * register the important ones with zebra for RA.
+ */
+extern void bgp_nht_reg_enhe_cap_intfs(struct peer *peer);
+extern void bgp_nht_dereg_enhe_cap_intfs(struct peer *peer);
+extern void evaluate_paths(struct bgp_nexthop_cache *bnc);
+
+/* APIs for setting up and allocating L3 nexthop group ids */
+extern uint32_t bgp_l3nhg_id_alloc(void);
+extern void bgp_l3nhg_id_free(uint32_t nhg_id);
+extern void bgp_l3nhg_init(void);
+void bgp_l3nhg_finish(void);
+
+extern void bgp_nht_ifp_up(struct interface *ifp);
+extern void bgp_nht_ifp_down(struct interface *ifp);
+
+extern void bgp_nht_interface_events(struct peer *peer);
+#endif /* _BGP_NHT_H */
diff --git a/bgpd/bgp_open.c b/bgpd/bgp_open.c
new file mode 100644
index 0000000..907e75e
--- /dev/null
+++ b/bgpd/bgp_open.c
@@ -0,0 +1,1936 @@
+/* BGP open message handling
+ * Copyright (C) 1998, 1999 Kunihiro Ishiguro
+ *
+ * This file is part of GNU Zebra.
+ *
+ * GNU Zebra is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License as published by the
+ * Free Software Foundation; either version 2, or (at your option) any
+ * later version.
+ *
+ * GNU Zebra is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program; see the file COPYING; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
+ */
+
+#include <zebra.h>
+
+#include "linklist.h"
+#include "prefix.h"
+#include "stream.h"
+#include "thread.h"
+#include "log.h"
+#include "command.h"
+#include "memory.h"
+#include "queue.h"
+#include "filter.h"
+
+#include "lib/json.h"
+#include "bgpd/bgpd.h"
+#include "bgpd/bgp_attr.h"
+#include "bgpd/bgp_debug.h"
+#include "bgpd/bgp_errors.h"
+#include "bgpd/bgp_fsm.h"
+#include "bgpd/bgp_packet.h"
+#include "bgpd/bgp_open.h"
+#include "bgpd/bgp_aspath.h"
+#include "bgpd/bgp_vty.h"
+#include "bgpd/bgp_memory.h"
+
+static const struct message capcode_str[] = {
+ {CAPABILITY_CODE_MP, "MultiProtocol Extensions"},
+ {CAPABILITY_CODE_REFRESH, "Route Refresh"},
+ {CAPABILITY_CODE_ORF, "Cooperative Route Filtering"},
+ {CAPABILITY_CODE_RESTART, "Graceful Restart"},
+ {CAPABILITY_CODE_AS4, "4-octet AS number"},
+ {CAPABILITY_CODE_ADDPATH, "AddPath"},
+ {CAPABILITY_CODE_DYNAMIC, "Dynamic"},
+ {CAPABILITY_CODE_ENHE, "Extended Next Hop Encoding"},
+ {CAPABILITY_CODE_DYNAMIC_OLD, "Dynamic (Old)"},
+ {CAPABILITY_CODE_REFRESH_OLD, "Route Refresh (Old)"},
+ {CAPABILITY_CODE_ORF_OLD, "ORF (Old)"},
+ {CAPABILITY_CODE_FQDN, "FQDN"},
+ {CAPABILITY_CODE_ENHANCED_RR, "Enhanced Route Refresh"},
+ {CAPABILITY_CODE_EXT_MESSAGE, "BGP Extended Message"},
+ {CAPABILITY_CODE_LLGR, "Long-lived BGP Graceful Restart"},
+ {CAPABILITY_CODE_ROLE, "Role"},
+ {0}};
+
+/* Minimum sizes for length field of each cap (so not inc. the header) */
+static const size_t cap_minsizes[] = {
+ [CAPABILITY_CODE_MP] = CAPABILITY_CODE_MP_LEN,
+ [CAPABILITY_CODE_REFRESH] = CAPABILITY_CODE_REFRESH_LEN,
+ [CAPABILITY_CODE_ORF] = CAPABILITY_CODE_ORF_LEN,
+ [CAPABILITY_CODE_RESTART] = CAPABILITY_CODE_RESTART_LEN,
+ [CAPABILITY_CODE_AS4] = CAPABILITY_CODE_AS4_LEN,
+ [CAPABILITY_CODE_ADDPATH] = CAPABILITY_CODE_ADDPATH_LEN,
+ [CAPABILITY_CODE_DYNAMIC] = CAPABILITY_CODE_DYNAMIC_LEN,
+ [CAPABILITY_CODE_DYNAMIC_OLD] = CAPABILITY_CODE_DYNAMIC_LEN,
+ [CAPABILITY_CODE_ENHE] = CAPABILITY_CODE_ENHE_LEN,
+ [CAPABILITY_CODE_REFRESH_OLD] = CAPABILITY_CODE_REFRESH_LEN,
+ [CAPABILITY_CODE_ORF_OLD] = CAPABILITY_CODE_ORF_LEN,
+ [CAPABILITY_CODE_FQDN] = CAPABILITY_CODE_MIN_FQDN_LEN,
+ [CAPABILITY_CODE_ENHANCED_RR] = CAPABILITY_CODE_ENHANCED_LEN,
+ [CAPABILITY_CODE_EXT_MESSAGE] = CAPABILITY_CODE_EXT_MESSAGE_LEN,
+ [CAPABILITY_CODE_LLGR] = CAPABILITY_CODE_LLGR_LEN,
+ [CAPABILITY_CODE_ROLE] = CAPABILITY_CODE_ROLE_LEN,
+};
+
+/* value the capability must be a multiple of.
+ * 0-data capabilities won't be checked against this.
+ * Other capabilities whose data doesn't fall on convenient boundaries for this
+ * table should be set to 1.
+ */
+static const size_t cap_modsizes[] = {
+ [CAPABILITY_CODE_MP] = 4,
+ [CAPABILITY_CODE_REFRESH] = 1,
+ [CAPABILITY_CODE_ORF] = 1,
+ [CAPABILITY_CODE_RESTART] = 1,
+ [CAPABILITY_CODE_AS4] = 4,
+ [CAPABILITY_CODE_ADDPATH] = 4,
+ [CAPABILITY_CODE_DYNAMIC] = 1,
+ [CAPABILITY_CODE_DYNAMIC_OLD] = 1,
+ [CAPABILITY_CODE_ENHE] = 6,
+ [CAPABILITY_CODE_REFRESH_OLD] = 1,
+ [CAPABILITY_CODE_ORF_OLD] = 1,
+ [CAPABILITY_CODE_FQDN] = 1,
+ [CAPABILITY_CODE_ENHANCED_RR] = 1,
+ [CAPABILITY_CODE_EXT_MESSAGE] = 1,
+ [CAPABILITY_CODE_LLGR] = 1,
+ [CAPABILITY_CODE_ROLE] = 1,
+};
+
+/* BGP-4 Multiprotocol Extentions lead us to the complex world. We can
+ negotiate remote peer supports extentions or not. But if
+ remote-peer doesn't supports negotiation process itself. We would
+ like to do manual configuration.
+
+ So there is many configurable point. First of all we want set each
+ peer whether we send capability negotiation to the peer or not.
+ Next, if we send capability to the peer we want to set my capability
+ inforation at each peer. */
+
+void bgp_capability_vty_out(struct vty *vty, struct peer *peer, bool use_json,
+ json_object *json_neigh)
+{
+ char *pnt;
+ char *end;
+ struct capability_mp_data mpc;
+ struct capability_header *hdr;
+ json_object *json_cap = NULL;
+
+ if (use_json)
+ json_cap = json_object_new_object();
+
+ pnt = peer->notify.data;
+ end = pnt + peer->notify.length;
+
+ while (pnt < end) {
+ if (pnt + sizeof(struct capability_mp_data) + 2 > end)
+ return;
+
+ hdr = (struct capability_header *)pnt;
+ if (pnt + hdr->length + 2 > end)
+ return;
+
+ memcpy(&mpc, pnt + 2, sizeof(struct capability_mp_data));
+
+ if (hdr->code == CAPABILITY_CODE_MP) {
+ afi_t afi;
+ safi_t safi;
+
+ (void)bgp_map_afi_safi_iana2int(ntohs(mpc.afi),
+ mpc.safi, &afi, &safi);
+
+ if (use_json) {
+ switch (afi) {
+ case AFI_IP:
+ json_object_string_add(
+ json_cap,
+ "capabilityErrorMultiProtocolAfi",
+ "IPv4");
+ break;
+ case AFI_IP6:
+ json_object_string_add(
+ json_cap,
+ "capabilityErrorMultiProtocolAfi",
+ "IPv6");
+ break;
+ case AFI_L2VPN:
+ json_object_string_add(
+ json_cap,
+ "capabilityErrorMultiProtocolAfi",
+ "L2VPN");
+ break;
+ default:
+ json_object_int_add(
+ json_cap,
+ "capabilityErrorMultiProtocolAfiUnknown",
+ ntohs(mpc.afi));
+ break;
+ }
+ switch (safi) {
+ case SAFI_UNICAST:
+ json_object_string_add(
+ json_cap,
+ "capabilityErrorMultiProtocolSafi",
+ "unicast");
+ break;
+ case SAFI_MULTICAST:
+ json_object_string_add(
+ json_cap,
+ "capabilityErrorMultiProtocolSafi",
+ "multicast");
+ break;
+ case SAFI_LABELED_UNICAST:
+ json_object_string_add(
+ json_cap,
+ "capabilityErrorMultiProtocolSafi",
+ "labeled-unicast");
+ break;
+ case SAFI_MPLS_VPN:
+ json_object_string_add(
+ json_cap,
+ "capabilityErrorMultiProtocolSafi",
+ "MPLS-labeled VPN");
+ break;
+ case SAFI_ENCAP:
+ json_object_string_add(
+ json_cap,
+ "capabilityErrorMultiProtocolSafi",
+ "encap");
+ break;
+ case SAFI_EVPN:
+ json_object_string_add(
+ json_cap,
+ "capabilityErrorMultiProtocolSafi",
+ "EVPN");
+ break;
+ case SAFI_FLOWSPEC:
+ json_object_string_add(
+ json_cap,
+ "capabilityErrorMultiProtocolSafi",
+ "flowspec");
+ break;
+ default:
+ json_object_int_add(
+ json_cap,
+ "capabilityErrorMultiProtocolSafiUnknown",
+ mpc.safi);
+ break;
+ }
+ } else {
+ vty_out(vty,
+ " Capability error for: Multi protocol ");
+ switch (afi) {
+ case AFI_IP:
+ vty_out(vty, "AFI IPv4, ");
+ break;
+ case AFI_IP6:
+ vty_out(vty, "AFI IPv6, ");
+ break;
+ case AFI_L2VPN:
+ vty_out(vty, "AFI L2VPN, ");
+ break;
+ default:
+ vty_out(vty, "AFI Unknown %d, ",
+ ntohs(mpc.afi));
+ break;
+ }
+ switch (safi) {
+ case SAFI_UNICAST:
+ vty_out(vty, "SAFI Unicast");
+ break;
+ case SAFI_MULTICAST:
+ vty_out(vty, "SAFI Multicast");
+ break;
+ case SAFI_LABELED_UNICAST:
+ vty_out(vty, "SAFI Labeled-unicast");
+ break;
+ case SAFI_MPLS_VPN:
+ vty_out(vty, "SAFI MPLS-labeled VPN");
+ break;
+ case SAFI_ENCAP:
+ vty_out(vty, "SAFI ENCAP");
+ break;
+ case SAFI_FLOWSPEC:
+ vty_out(vty, "SAFI FLOWSPEC");
+ break;
+ case SAFI_EVPN:
+ vty_out(vty, "SAFI EVPN");
+ break;
+ default:
+ vty_out(vty, "SAFI Unknown %d ",
+ mpc.safi);
+ break;
+ }
+ vty_out(vty, "\n");
+ }
+ } else if (hdr->code >= 128) {
+ if (use_json)
+ json_object_int_add(
+ json_cap,
+ "capabilityErrorVendorSpecificCapabilityCode",
+ hdr->code);
+ else
+ vty_out(vty,
+ " Capability error: vendor specific capability code %d",
+ hdr->code);
+ } else {
+ if (use_json)
+ json_object_int_add(
+ json_cap,
+ "capabilityErrorUnknownCapabilityCode",
+ hdr->code);
+ else
+ vty_out(vty,
+ " Capability error: unknown capability code %d",
+ hdr->code);
+ }
+ pnt += hdr->length + 2;
+ }
+ if (use_json)
+ json_object_object_add(json_neigh, "capabilityErrors",
+ json_cap);
+}
+
+static void bgp_capability_mp_data(struct stream *s,
+ struct capability_mp_data *mpc)
+{
+ mpc->afi = stream_getw(s);
+ mpc->reserved = stream_getc(s);
+ mpc->safi = stream_getc(s);
+}
+
+/* Set negotiated capability value. */
+static int bgp_capability_mp(struct peer *peer, struct capability_header *hdr)
+{
+ struct capability_mp_data mpc;
+ struct stream *s = BGP_INPUT(peer);
+ afi_t afi;
+ safi_t safi;
+
+ /* Verify length is 4 */
+ if (hdr->length != 4) {
+ flog_warn(
+ EC_BGP_CAPABILITY_INVALID_LENGTH,
+ "MP Cap: Received invalid length %d, non-multiple of 4",
+ hdr->length);
+ return -1;
+ }
+
+ bgp_capability_mp_data(s, &mpc);
+
+ if (bgp_debug_neighbor_events(peer))
+ zlog_debug("%s OPEN has %s capability for afi/safi: %s/%s",
+ peer->host, lookup_msg(capcode_str, hdr->code, NULL),
+ iana_afi2str(mpc.afi), iana_safi2str(mpc.safi));
+
+ /* Convert AFI, SAFI to internal values, check. */
+ if (bgp_map_afi_safi_iana2int(mpc.afi, mpc.safi, &afi, &safi))
+ return -1;
+
+ /* Now safi remapped, and afi/safi are valid array indices */
+ peer->afc_recv[afi][safi] = 1;
+
+ if (peer->afc[afi][safi])
+ peer->afc_nego[afi][safi] = 1;
+ else
+ return -1;
+
+ return 0;
+}
+
+static void bgp_capability_orf_not_support(struct peer *peer, iana_afi_t afi,
+ iana_safi_t safi, uint8_t type,
+ uint8_t mode)
+{
+ if (bgp_debug_neighbor_events(peer))
+ zlog_debug(
+ "%s Addr-family %d/%d has ORF type/mode %d/%d not supported",
+ peer->host, afi, safi, type, mode);
+}
+
+static const struct message orf_type_str[] = {
+ {ORF_TYPE_PREFIX, "Prefixlist"},
+ {ORF_TYPE_PREFIX_OLD, "Prefixlist (old)"},
+ {0}};
+
+static const struct message orf_mode_str[] = {{ORF_MODE_RECEIVE, "Receive"},
+ {ORF_MODE_SEND, "Send"},
+ {ORF_MODE_BOTH, "Both"},
+ {0}};
+
+static int bgp_capability_orf_entry(struct peer *peer,
+ struct capability_header *hdr)
+{
+ struct stream *s = BGP_INPUT(peer);
+ struct capability_mp_data mpc;
+ uint8_t num;
+ iana_afi_t pkt_afi;
+ afi_t afi;
+ iana_safi_t pkt_safi;
+ safi_t safi;
+ uint8_t type;
+ uint8_t mode;
+ uint16_t sm_cap = 0; /* capability send-mode receive */
+ uint16_t rm_cap = 0; /* capability receive-mode receive */
+ int i;
+
+ /* ORF Entry header */
+ bgp_capability_mp_data(s, &mpc);
+ num = stream_getc(s);
+ pkt_afi = mpc.afi;
+ pkt_safi = mpc.safi;
+
+ if (bgp_debug_neighbor_events(peer))
+ zlog_debug("%s ORF Cap entry for afi/safi: %s/%s", peer->host,
+ iana_afi2str(mpc.afi), iana_safi2str(mpc.safi));
+
+ /* Convert AFI, SAFI to internal values, check. */
+ if (bgp_map_afi_safi_iana2int(pkt_afi, pkt_safi, &afi, &safi)) {
+ zlog_info(
+ "%s Addr-family %d/%d not supported. Ignoring the ORF capability",
+ peer->host, pkt_afi, pkt_safi);
+ return 0;
+ }
+
+ mpc.afi = pkt_afi;
+ mpc.safi = safi;
+
+ /* validate number field */
+ if (CAPABILITY_CODE_ORF_LEN + (num * 2) > hdr->length) {
+ zlog_info(
+ "%s ORF Capability entry length error, Cap length %u, num %u",
+ peer->host, hdr->length, num);
+ bgp_notify_send(peer, BGP_NOTIFY_OPEN_ERR,
+ BGP_NOTIFY_OPEN_MALFORMED_ATTR);
+ return -1;
+ }
+
+ for (i = 0; i < num; i++) {
+ type = stream_getc(s);
+ mode = stream_getc(s);
+
+ /* ORF Mode error check */
+ switch (mode) {
+ case ORF_MODE_BOTH:
+ case ORF_MODE_SEND:
+ case ORF_MODE_RECEIVE:
+ break;
+ default:
+ bgp_capability_orf_not_support(peer, pkt_afi, pkt_safi,
+ type, mode);
+ continue;
+ }
+ /* ORF Type and afi/safi error checks */
+ /* capcode versus type */
+ switch (hdr->code) {
+ case CAPABILITY_CODE_ORF:
+ switch (type) {
+ case ORF_TYPE_PREFIX:
+ break;
+ default:
+ bgp_capability_orf_not_support(
+ peer, pkt_afi, pkt_safi, type, mode);
+ continue;
+ }
+ break;
+ case CAPABILITY_CODE_ORF_OLD:
+ switch (type) {
+ case ORF_TYPE_PREFIX_OLD:
+ break;
+ default:
+ bgp_capability_orf_not_support(
+ peer, pkt_afi, pkt_safi, type, mode);
+ continue;
+ }
+ break;
+ default:
+ bgp_capability_orf_not_support(peer, pkt_afi, pkt_safi,
+ type, mode);
+ continue;
+ }
+
+ /* AFI vs SAFI */
+ if (!((afi == AFI_IP && safi == SAFI_UNICAST)
+ || (afi == AFI_IP && safi == SAFI_MULTICAST)
+ || (afi == AFI_IP6 && safi == SAFI_UNICAST))) {
+ bgp_capability_orf_not_support(peer, pkt_afi, pkt_safi,
+ type, mode);
+ continue;
+ }
+
+ if (bgp_debug_neighbor_events(peer))
+ zlog_debug(
+ "%s OPEN has %s ORF capability as %s for afi/safi: %s/%s",
+ peer->host,
+ lookup_msg(orf_type_str, type, NULL),
+ lookup_msg(orf_mode_str, mode, NULL),
+ iana_afi2str(pkt_afi), iana_safi2str(pkt_safi));
+
+ if (hdr->code == CAPABILITY_CODE_ORF) {
+ sm_cap = PEER_CAP_ORF_PREFIX_SM_RCV;
+ rm_cap = PEER_CAP_ORF_PREFIX_RM_RCV;
+ } else if (hdr->code == CAPABILITY_CODE_ORF_OLD) {
+ sm_cap = PEER_CAP_ORF_PREFIX_SM_OLD_RCV;
+ rm_cap = PEER_CAP_ORF_PREFIX_RM_OLD_RCV;
+ } else {
+ bgp_capability_orf_not_support(peer, pkt_afi, pkt_safi,
+ type, mode);
+ continue;
+ }
+
+ switch (mode) {
+ case ORF_MODE_BOTH:
+ SET_FLAG(peer->af_cap[afi][safi], sm_cap);
+ SET_FLAG(peer->af_cap[afi][safi], rm_cap);
+ break;
+ case ORF_MODE_SEND:
+ SET_FLAG(peer->af_cap[afi][safi], sm_cap);
+ break;
+ case ORF_MODE_RECEIVE:
+ SET_FLAG(peer->af_cap[afi][safi], rm_cap);
+ break;
+ }
+ }
+ return 0;
+}
+
+static int bgp_capability_restart(struct peer *peer,
+ struct capability_header *caphdr)
+{
+ struct stream *s = BGP_INPUT(peer);
+ uint16_t restart_flag_time;
+ size_t end = stream_get_getp(s) + caphdr->length;
+
+ /* Verify length is a multiple of 4 */
+ if ((caphdr->length - 2) % 4) {
+ flog_warn(
+ EC_BGP_CAPABILITY_INVALID_LENGTH,
+ "Restart Cap: Received invalid length %d, non-multiple of 4",
+ caphdr->length);
+ return -1;
+ }
+
+ SET_FLAG(peer->cap, PEER_CAP_RESTART_RCV);
+ restart_flag_time = stream_getw(s);
+
+ /* The most significant bit is defined in [RFC4724] as
+ * the Restart State ("R") bit.
+ */
+ if (CHECK_FLAG(restart_flag_time, GRACEFUL_RESTART_R_BIT))
+ SET_FLAG(peer->cap, PEER_CAP_GRACEFUL_RESTART_R_BIT_RCV);
+ else
+ UNSET_FLAG(peer->cap, PEER_CAP_GRACEFUL_RESTART_R_BIT_RCV);
+
+ /* The second most significant bit is defined in this
+ * document as the Graceful Notification ("N") bit.
+ */
+ if (CHECK_FLAG(restart_flag_time, GRACEFUL_RESTART_N_BIT))
+ SET_FLAG(peer->cap, PEER_CAP_GRACEFUL_RESTART_N_BIT_RCV);
+ else
+ UNSET_FLAG(peer->cap, PEER_CAP_GRACEFUL_RESTART_N_BIT_RCV);
+
+ UNSET_FLAG(restart_flag_time, 0xF000);
+ peer->v_gr_restart = restart_flag_time;
+
+ if (bgp_debug_neighbor_events(peer)) {
+ zlog_debug(
+ "%s Peer has%srestarted. Restart Time: %d, N-bit set: %s",
+ peer->host,
+ CHECK_FLAG(peer->cap,
+ PEER_CAP_GRACEFUL_RESTART_R_BIT_RCV)
+ ? " "
+ : " not ",
+ peer->v_gr_restart,
+ CHECK_FLAG(peer->cap,
+ PEER_CAP_GRACEFUL_RESTART_N_BIT_RCV)
+ ? "yes"
+ : "no");
+ }
+
+ while (stream_get_getp(s) + 4 <= end) {
+ afi_t afi;
+ safi_t safi;
+ iana_afi_t pkt_afi = stream_getw(s);
+ iana_safi_t pkt_safi = stream_getc(s);
+ uint8_t flag = stream_getc(s);
+
+ /* Convert AFI, SAFI to internal values, check. */
+ if (bgp_map_afi_safi_iana2int(pkt_afi, pkt_safi, &afi, &safi)) {
+ if (bgp_debug_neighbor_events(peer))
+ zlog_debug(
+ "%s Addr-family %s/%s(afi/safi) not supported. Ignore the Graceful Restart capability for this AFI/SAFI",
+ peer->host, iana_afi2str(pkt_afi),
+ iana_safi2str(pkt_safi));
+ } else if (!peer->afc[afi][safi]) {
+ if (bgp_debug_neighbor_events(peer))
+ zlog_debug(
+ "%s Addr-family %s/%s(afi/safi) not enabled. Ignore the Graceful Restart capability",
+ peer->host, iana_afi2str(pkt_afi),
+ iana_safi2str(pkt_safi));
+ } else {
+ if (bgp_debug_neighbor_events(peer))
+ zlog_debug(
+ "%s Address family %s is%spreserved",
+ peer->host, get_afi_safi_str(afi, safi, false),
+ CHECK_FLAG(
+ peer->af_cap[afi][safi],
+ PEER_CAP_RESTART_AF_PRESERVE_RCV)
+ ? " "
+ : " not ");
+
+ SET_FLAG(peer->af_cap[afi][safi],
+ PEER_CAP_RESTART_AF_RCV);
+ if (CHECK_FLAG(flag, GRACEFUL_RESTART_F_BIT))
+ SET_FLAG(peer->af_cap[afi][safi],
+ PEER_CAP_RESTART_AF_PRESERVE_RCV);
+ }
+ }
+ return 0;
+}
+
+static int bgp_capability_llgr(struct peer *peer,
+ struct capability_header *caphdr)
+{
+/*
+ * +--------------------------------------------------+
+ * | Address Family Identifier (16 bits) |
+ * +--------------------------------------------------+
+ * | Subsequent Address Family Identifier (8 bits) |
+ * +--------------------------------------------------+
+ * | Flags for Address Family (8 bits) |
+ * +--------------------------------------------------+
+ * | Long-lived Stale Time (24 bits) |
+ * +--------------------------------------------------+
+ */
+#define BGP_CAP_LLGR_MIN_PACKET_LEN 7
+ struct stream *s = BGP_INPUT(peer);
+ size_t end = stream_get_getp(s) + caphdr->length;
+
+ SET_FLAG(peer->cap, PEER_CAP_LLGR_RCV);
+
+ while (stream_get_getp(s) + BGP_CAP_LLGR_MIN_PACKET_LEN <= end) {
+ afi_t afi;
+ safi_t safi;
+ iana_afi_t pkt_afi = stream_getw(s);
+ iana_safi_t pkt_safi = stream_getc(s);
+ uint8_t flags = stream_getc(s);
+ uint32_t stale_time = stream_get3(s);
+
+ if (bgp_map_afi_safi_iana2int(pkt_afi, pkt_safi, &afi, &safi)) {
+ if (bgp_debug_neighbor_events(peer))
+ zlog_debug(
+ "%s Addr-family %s/%s(afi/safi) not supported. Ignore the Long-lived Graceful Restart capability for this AFI/SAFI",
+ peer->host, iana_afi2str(pkt_afi),
+ iana_safi2str(pkt_safi));
+ } else if (!peer->afc[afi][safi]
+ || !CHECK_FLAG(peer->af_cap[afi][safi],
+ PEER_CAP_RESTART_AF_RCV)) {
+ if (bgp_debug_neighbor_events(peer))
+ zlog_debug(
+ "%s Addr-family %s/%s(afi/safi) not enabled. Ignore the Long-lived Graceful Restart capability",
+ peer->host, iana_afi2str(pkt_afi),
+ iana_safi2str(pkt_safi));
+ } else {
+ if (bgp_debug_neighbor_events(peer))
+ zlog_debug(
+ "%s Addr-family %s/%s(afi/safi) Long-lived Graceful Restart capability stale time %u sec",
+ peer->host, iana_afi2str(pkt_afi),
+ iana_safi2str(pkt_safi), stale_time);
+
+ peer->llgr[afi][safi].flags = flags;
+ peer->llgr[afi][safi].stale_time =
+ MIN(stale_time, peer->bgp->llgr_stale_time);
+ SET_FLAG(peer->af_cap[afi][safi], PEER_CAP_LLGR_AF_RCV);
+ }
+ }
+
+ return 0;
+}
+
+/* Unlike other capability parsing routines, this one returns 0 on error */
+static as_t bgp_capability_as4(struct peer *peer, struct capability_header *hdr)
+{
+ SET_FLAG(peer->cap, PEER_CAP_AS4_RCV);
+
+ if (hdr->length != CAPABILITY_CODE_AS4_LEN) {
+ flog_err(EC_BGP_PKT_OPEN,
+ "%s AS4 capability has incorrect data length %d",
+ peer->host, hdr->length);
+ return 0;
+ }
+
+ as_t as4 = stream_getl(BGP_INPUT(peer));
+
+ if (BGP_DEBUG(as4, AS4))
+ zlog_debug(
+ "%s [AS4] about to set cap PEER_CAP_AS4_RCV, got as4 %u",
+ peer->host, as4);
+ return as4;
+}
+
+static int bgp_capability_ext_message(struct peer *peer,
+ struct capability_header *hdr)
+{
+ if (hdr->length != CAPABILITY_CODE_EXT_MESSAGE_LEN) {
+ flog_err(
+ EC_BGP_PKT_OPEN,
+ "%s: BGP Extended Message capability has incorrect data length %d",
+ peer->host, hdr->length);
+ return -1;
+ }
+
+ SET_FLAG(peer->cap, PEER_CAP_EXTENDED_MESSAGE_RCV);
+
+ return 0;
+}
+
+static int bgp_capability_addpath(struct peer *peer,
+ struct capability_header *hdr)
+{
+ struct stream *s = BGP_INPUT(peer);
+ size_t end = stream_get_getp(s) + hdr->length;
+
+ SET_FLAG(peer->cap, PEER_CAP_ADDPATH_RCV);
+
+ /* Verify length is a multiple of 4 */
+ if (hdr->length % 4) {
+ flog_warn(
+ EC_BGP_CAPABILITY_INVALID_LENGTH,
+ "Add Path: Received invalid length %d, non-multiple of 4",
+ hdr->length);
+ return -1;
+ }
+
+ while (stream_get_getp(s) + 4 <= end) {
+ afi_t afi;
+ safi_t safi;
+ iana_afi_t pkt_afi = stream_getw(s);
+ iana_safi_t pkt_safi = stream_getc(s);
+ uint8_t send_receive = stream_getc(s);
+
+ if (bgp_debug_neighbor_events(peer))
+ zlog_debug(
+ "%s OPEN has %s capability for afi/safi: %s/%s%s%s",
+ peer->host,
+ lookup_msg(capcode_str, hdr->code, NULL),
+ iana_afi2str(pkt_afi), iana_safi2str(pkt_safi),
+ (send_receive & BGP_ADDPATH_RX) ? ", receive"
+ : "",
+ (send_receive & BGP_ADDPATH_TX) ? ", transmit"
+ : "");
+
+ /* Convert AFI, SAFI to internal values, check. */
+ if (bgp_map_afi_safi_iana2int(pkt_afi, pkt_safi, &afi, &safi)) {
+ if (bgp_debug_neighbor_events(peer))
+ zlog_debug(
+ "%s Addr-family %s/%s(afi/safi) not supported. Ignore the Addpath Attribute for this AFI/SAFI",
+ peer->host, iana_afi2str(pkt_afi),
+ iana_safi2str(pkt_safi));
+ continue;
+ } else if (!peer->afc[afi][safi]) {
+ if (bgp_debug_neighbor_events(peer))
+ zlog_debug(
+ "%s Addr-family %s/%s(afi/safi) not enabled. Ignore the AddPath capability for this AFI/SAFI",
+ peer->host, iana_afi2str(pkt_afi),
+ iana_safi2str(pkt_safi));
+ continue;
+ }
+
+ if (send_receive & BGP_ADDPATH_RX)
+ SET_FLAG(peer->af_cap[afi][safi],
+ PEER_CAP_ADDPATH_AF_RX_RCV);
+
+ if (send_receive & BGP_ADDPATH_TX)
+ SET_FLAG(peer->af_cap[afi][safi],
+ PEER_CAP_ADDPATH_AF_TX_RCV);
+ }
+
+ return 0;
+}
+
+static int bgp_capability_enhe(struct peer *peer, struct capability_header *hdr)
+{
+ struct stream *s = BGP_INPUT(peer);
+ size_t end = stream_get_getp(s) + hdr->length;
+
+ /* Verify length is a multiple of 4 */
+ if (hdr->length % 6) {
+ flog_warn(
+ EC_BGP_CAPABILITY_INVALID_LENGTH,
+ "Extended NH: Received invalid length %d, non-multiple of 6",
+ hdr->length);
+ return -1;
+ }
+
+ while (stream_get_getp(s) + 6 <= end) {
+ iana_afi_t pkt_afi = stream_getw(s);
+ afi_t afi;
+ iana_safi_t pkt_safi = stream_getw(s);
+ safi_t safi;
+ iana_afi_t pkt_nh_afi = stream_getw(s);
+ afi_t nh_afi;
+
+ if (bgp_debug_neighbor_events(peer))
+ zlog_debug(
+ "%s Received with afi/safi/next-hop afi: %s/%s/%u",
+ peer->host, iana_afi2str(pkt_afi),
+ iana_safi2str(pkt_safi), pkt_nh_afi);
+
+ /* Convert AFI, SAFI to internal values, check. */
+ if (bgp_map_afi_safi_iana2int(pkt_afi, pkt_safi, &afi, &safi)) {
+ if (bgp_debug_neighbor_events(peer))
+ zlog_debug(
+ "%s Addr-family %s/%s(afi/safi) not supported. Ignore the ENHE Attribute for this AFI/SAFI",
+ peer->host, iana_afi2str(pkt_afi),
+ iana_safi2str(pkt_safi));
+ continue;
+ }
+
+ /* RFC 5549 specifies use of this capability only for IPv4 AFI,
+ * with
+ * the Nexthop AFI being IPv6. A future spec may introduce other
+ * possibilities, so we ignore other values with a log. Also,
+ * only
+ * SAFI_UNICAST and SAFI_LABELED_UNICAST are currently supported
+ * (and expected).
+ */
+ nh_afi = afi_iana2int(pkt_nh_afi);
+
+ if (afi != AFI_IP || nh_afi != AFI_IP6
+ || !(safi == SAFI_UNICAST || safi == SAFI_MPLS_VPN
+ || safi == SAFI_LABELED_UNICAST)) {
+ flog_warn(
+ EC_BGP_CAPABILITY_INVALID_DATA,
+ "%s Unexpected afi/safi/next-hop afi: %s/%s/%u in Extended Next-hop capability, ignoring",
+ peer->host, iana_afi2str(pkt_afi),
+ iana_safi2str(pkt_safi), pkt_nh_afi);
+ continue;
+ }
+
+ SET_FLAG(peer->af_cap[afi][safi], PEER_CAP_ENHE_AF_RCV);
+
+ if (CHECK_FLAG(peer->af_cap[afi][safi], PEER_CAP_ENHE_AF_ADV))
+ SET_FLAG(peer->af_cap[afi][safi],
+ PEER_CAP_ENHE_AF_NEGO);
+ }
+
+ SET_FLAG(peer->cap, PEER_CAP_ENHE_RCV);
+
+ return 0;
+}
+
+static int bgp_capability_hostname(struct peer *peer,
+ struct capability_header *hdr)
+{
+ struct stream *s = BGP_INPUT(peer);
+ char str[BGP_MAX_HOSTNAME + 1];
+ size_t end = stream_get_getp(s) + hdr->length;
+ uint8_t len;
+
+ SET_FLAG(peer->cap, PEER_CAP_HOSTNAME_RCV);
+
+ len = stream_getc(s);
+ if (stream_get_getp(s) + len > end) {
+ flog_warn(
+ EC_BGP_CAPABILITY_INVALID_DATA,
+ "%s: Received malformed hostname capability from peer %s",
+ __func__, peer->host);
+ return -1;
+ }
+
+ if (len > BGP_MAX_HOSTNAME) {
+ stream_get(str, s, BGP_MAX_HOSTNAME);
+ stream_forward_getp(s, len - BGP_MAX_HOSTNAME);
+ len = BGP_MAX_HOSTNAME; /* to set the '\0' below */
+ } else if (len)
+ stream_get(str, s, len);
+
+ if (len) {
+ str[len] = '\0';
+
+ XFREE(MTYPE_BGP_PEER_HOST, peer->hostname);
+ XFREE(MTYPE_BGP_PEER_HOST, peer->domainname);
+
+ peer->hostname = XSTRDUP(MTYPE_BGP_PEER_HOST, str);
+ }
+
+ if (stream_get_getp(s) + 1 > end) {
+ flog_warn(
+ EC_BGP_CAPABILITY_INVALID_DATA,
+ "%s: Received invalid domain name len (hostname capability) from peer %s",
+ __func__, peer->host);
+ return -1;
+ }
+
+ len = stream_getc(s);
+ if (stream_get_getp(s) + len > end) {
+ flog_warn(
+ EC_BGP_CAPABILITY_INVALID_DATA,
+ "%s: Received runt domain name (hostname capability) from peer %s",
+ __func__, peer->host);
+ return -1;
+ }
+
+ if (len > BGP_MAX_HOSTNAME) {
+ stream_get(str, s, BGP_MAX_HOSTNAME);
+ stream_forward_getp(s, len - BGP_MAX_HOSTNAME);
+ len = BGP_MAX_HOSTNAME; /* to set the '\0' below */
+ } else if (len)
+ stream_get(str, s, len);
+
+ if (len) {
+ str[len] = '\0';
+
+ XFREE(MTYPE_BGP_PEER_HOST, peer->domainname);
+
+ peer->domainname = XSTRDUP(MTYPE_BGP_PEER_HOST, str);
+ }
+
+ if (bgp_debug_neighbor_events(peer)) {
+ zlog_debug("%s received hostname %s, domainname %s", peer->host,
+ peer->hostname, peer->domainname);
+ }
+
+ return 0;
+}
+
+static int bgp_capability_role(struct peer *peer, struct capability_header *hdr)
+{
+ SET_FLAG(peer->cap, PEER_CAP_ROLE_RCV);
+ if (hdr->length != CAPABILITY_CODE_ROLE_LEN) {
+ flog_warn(EC_BGP_CAPABILITY_INVALID_LENGTH,
+ "Role: Received invalid length %d", hdr->length);
+ return -1;
+ }
+ uint8_t role = stream_getc(BGP_INPUT(peer));
+
+ peer->remote_role = role;
+ return 0;
+}
+
+/**
+ * Parse given capability.
+ * XXX: This is reading into a stream, but not using stream API
+ *
+ * @param[out] mp_capability Set to 1 on return iff one or more Multiprotocol
+ * capabilities were encountered.
+ */
+static int bgp_capability_parse(struct peer *peer, size_t length,
+ int *mp_capability, uint8_t **error)
+{
+ int ret;
+ struct stream *s = BGP_INPUT(peer);
+ size_t end = stream_get_getp(s) + length;
+ uint16_t restart_flag_time = 0;
+
+ assert(STREAM_READABLE(s) >= length);
+
+ while (stream_get_getp(s) < end) {
+ size_t start;
+ uint8_t *sp = stream_pnt(s);
+ struct capability_header caphdr;
+
+ ret = 0;
+ /* We need at least capability code and capability length. */
+ if (stream_get_getp(s) + 2 > end) {
+ zlog_info("%s Capability length error (< header)",
+ peer->host);
+ bgp_notify_send(peer, BGP_NOTIFY_OPEN_ERR,
+ BGP_NOTIFY_OPEN_MALFORMED_ATTR);
+ return -1;
+ }
+
+ caphdr.code = stream_getc(s);
+ caphdr.length = stream_getc(s);
+ start = stream_get_getp(s);
+
+ /* Capability length check sanity check. */
+ if (start + caphdr.length > end) {
+ zlog_info("%s Capability length error (< length)",
+ peer->host);
+ bgp_notify_send(peer, BGP_NOTIFY_OPEN_ERR,
+ BGP_NOTIFY_OPEN_MALFORMED_ATTR);
+ return -1;
+ }
+
+ if (bgp_debug_neighbor_events(peer))
+ zlog_debug("%s OPEN has %s capability (%u), length %u",
+ peer->host,
+ lookup_msg(capcode_str, caphdr.code, NULL),
+ caphdr.code, caphdr.length);
+
+ /* Length sanity check, type-specific, for known capabilities */
+ switch (caphdr.code) {
+ case CAPABILITY_CODE_MP:
+ case CAPABILITY_CODE_REFRESH:
+ case CAPABILITY_CODE_REFRESH_OLD:
+ case CAPABILITY_CODE_ORF:
+ case CAPABILITY_CODE_ORF_OLD:
+ case CAPABILITY_CODE_RESTART:
+ case CAPABILITY_CODE_AS4:
+ case CAPABILITY_CODE_ADDPATH:
+ case CAPABILITY_CODE_DYNAMIC:
+ case CAPABILITY_CODE_DYNAMIC_OLD:
+ case CAPABILITY_CODE_ENHE:
+ case CAPABILITY_CODE_FQDN:
+ case CAPABILITY_CODE_ENHANCED_RR:
+ case CAPABILITY_CODE_EXT_MESSAGE:
+ case CAPABILITY_CODE_ROLE:
+ /* Check length. */
+ if (caphdr.length < cap_minsizes[caphdr.code]) {
+ zlog_info(
+ "%s %s Capability length error: got %u, expected at least %u",
+ peer->host,
+ lookup_msg(capcode_str, caphdr.code,
+ NULL),
+ caphdr.length,
+ (unsigned)cap_minsizes[caphdr.code]);
+ bgp_notify_send(peer, BGP_NOTIFY_OPEN_ERR,
+ BGP_NOTIFY_OPEN_MALFORMED_ATTR);
+ return -1;
+ }
+ if (caphdr.length
+ && caphdr.length % cap_modsizes[caphdr.code] != 0) {
+ zlog_info(
+ "%s %s Capability length error: got %u, expected a multiple of %u",
+ peer->host,
+ lookup_msg(capcode_str, caphdr.code,
+ NULL),
+ caphdr.length,
+ (unsigned)cap_modsizes[caphdr.code]);
+ bgp_notify_send(peer, BGP_NOTIFY_OPEN_ERR,
+ BGP_NOTIFY_OPEN_MALFORMED_ATTR);
+ return -1;
+ }
+ /* we deliberately ignore unknown codes, see below */
+ default:
+ break;
+ }
+
+ switch (caphdr.code) {
+ case CAPABILITY_CODE_MP: {
+ *mp_capability = 1;
+
+ /* Ignore capability when override-capability is set. */
+ if (!CHECK_FLAG(peer->flags,
+ PEER_FLAG_OVERRIDE_CAPABILITY)) {
+ /* Set negotiated value. */
+ ret = bgp_capability_mp(peer, &caphdr);
+
+ /* Unsupported Capability. */
+ if (ret < 0) {
+ /* Store return data. */
+ memcpy(*error, sp, caphdr.length + 2);
+ *error += caphdr.length + 2;
+ }
+ ret = 0; /* Don't return error for this */
+ }
+ } break;
+ case CAPABILITY_CODE_ENHANCED_RR:
+ case CAPABILITY_CODE_REFRESH:
+ case CAPABILITY_CODE_REFRESH_OLD: {
+ /* BGP refresh capability */
+ if (caphdr.code == CAPABILITY_CODE_ENHANCED_RR)
+ SET_FLAG(peer->cap, PEER_CAP_ENHANCED_RR_RCV);
+ else if (caphdr.code == CAPABILITY_CODE_REFRESH_OLD)
+ SET_FLAG(peer->cap, PEER_CAP_REFRESH_OLD_RCV);
+ else
+ SET_FLAG(peer->cap, PEER_CAP_REFRESH_NEW_RCV);
+ } break;
+ case CAPABILITY_CODE_ORF:
+ case CAPABILITY_CODE_ORF_OLD:
+ ret = bgp_capability_orf_entry(peer, &caphdr);
+ break;
+ case CAPABILITY_CODE_RESTART:
+ ret = bgp_capability_restart(peer, &caphdr);
+ break;
+ case CAPABILITY_CODE_LLGR:
+ ret = bgp_capability_llgr(peer, &caphdr);
+ break;
+ case CAPABILITY_CODE_DYNAMIC:
+ case CAPABILITY_CODE_DYNAMIC_OLD:
+ SET_FLAG(peer->cap, PEER_CAP_DYNAMIC_RCV);
+ break;
+ case CAPABILITY_CODE_AS4:
+ /* Already handled as a special-case parsing of the
+ * capabilities
+ * at the beginning of OPEN processing. So we care not a
+ * jot
+ * for the value really, only error case.
+ */
+ if (!bgp_capability_as4(peer, &caphdr))
+ ret = -1;
+ break;
+ case CAPABILITY_CODE_ADDPATH:
+ ret = bgp_capability_addpath(peer, &caphdr);
+ break;
+ case CAPABILITY_CODE_ENHE:
+ ret = bgp_capability_enhe(peer, &caphdr);
+ break;
+ case CAPABILITY_CODE_EXT_MESSAGE:
+ ret = bgp_capability_ext_message(peer, &caphdr);
+ break;
+ case CAPABILITY_CODE_FQDN:
+ ret = bgp_capability_hostname(peer, &caphdr);
+ break;
+ case CAPABILITY_CODE_ROLE:
+ ret = bgp_capability_role(peer, &caphdr);
+ break;
+ default:
+ if (caphdr.code > 128) {
+ /* We don't send Notification for unknown vendor
+ specific
+ capabilities. It seems reasonable for now...
+ */
+ flog_warn(EC_BGP_CAPABILITY_VENDOR,
+ "%s Vendor specific capability %d",
+ peer->host, caphdr.code);
+ } else {
+ flog_warn(
+ EC_BGP_CAPABILITY_UNKNOWN,
+ "%s unrecognized capability code: %d - ignored",
+ peer->host, caphdr.code);
+ memcpy(*error, sp, caphdr.length + 2);
+ *error += caphdr.length + 2;
+ }
+ }
+
+ if (ret < 0) {
+ bgp_notify_send(peer, BGP_NOTIFY_OPEN_ERR,
+ BGP_NOTIFY_OPEN_MALFORMED_ATTR);
+ return -1;
+ }
+ if (stream_get_getp(s) != (start + caphdr.length)) {
+ if (stream_get_getp(s) > (start + caphdr.length))
+ flog_warn(
+ EC_BGP_CAPABILITY_INVALID_LENGTH,
+ "%s Cap-parser for %s read past cap-length, %u!",
+ peer->host,
+ lookup_msg(capcode_str, caphdr.code,
+ NULL),
+ caphdr.length);
+ stream_set_getp(s, start + caphdr.length);
+ }
+
+ if (!CHECK_FLAG(peer->cap, PEER_CAP_RESTART_RCV)) {
+ UNSET_FLAG(restart_flag_time, 0xF000);
+ peer->v_gr_restart = restart_flag_time;
+ }
+ }
+ return 0;
+}
+
+static int bgp_auth_parse(struct peer *peer, size_t length)
+{
+ bgp_notify_send(peer, BGP_NOTIFY_OPEN_ERR,
+ BGP_NOTIFY_OPEN_AUTH_FAILURE);
+ return -1;
+}
+
+static bool strict_capability_same(struct peer *peer)
+{
+ int i, j;
+
+ for (i = AFI_IP; i < AFI_MAX; i++)
+ for (j = SAFI_UNICAST; j < SAFI_MAX; j++)
+ if (peer->afc[i][j] != peer->afc_nego[i][j])
+ return false;
+ return true;
+}
+
+
+static bool bgp_role_violation(struct peer *peer)
+{
+ uint8_t local_role = peer->local_role;
+ uint8_t remote_role = peer->remote_role;
+
+ if (local_role != ROLE_UNDEFINED && remote_role != ROLE_UNDEFINED &&
+ !((local_role == ROLE_PEER && remote_role == ROLE_PEER) ||
+ (local_role == ROLE_PROVIDER && remote_role == ROLE_CUSTOMER) ||
+ (local_role == ROLE_CUSTOMER && remote_role == ROLE_PROVIDER) ||
+ (local_role == ROLE_RS_SERVER && remote_role == ROLE_RS_CLIENT) ||
+ (local_role == ROLE_RS_CLIENT &&
+ remote_role == ROLE_RS_SERVER))) {
+ bgp_notify_send(peer, BGP_NOTIFY_OPEN_ERR,
+ BGP_NOTIFY_OPEN_ROLE_MISMATCH);
+ return true;
+ }
+ if (remote_role == ROLE_UNDEFINED &&
+ CHECK_FLAG(peer->flags, PEER_FLAG_ROLE_STRICT_MODE)) {
+ const char *err_msg =
+ "Strict mode. Please set the role on your side.";
+ bgp_notify_send_with_data(peer, BGP_NOTIFY_OPEN_ERR,
+ BGP_NOTIFY_OPEN_ROLE_MISMATCH,
+ (uint8_t *)err_msg, strlen(err_msg));
+ return true;
+ }
+ return false;
+}
+
+
+/* peek into option, stores ASN to *as4 if the AS4 capability was found.
+ * Returns 0 if no as4 found, as4cap value otherwise.
+ */
+as_t peek_for_as4_capability(struct peer *peer, uint16_t length)
+{
+ struct stream *s = BGP_INPUT(peer);
+ size_t orig_getp = stream_get_getp(s);
+ size_t end = orig_getp + length;
+ as_t as4 = 0;
+
+ if (BGP_DEBUG(as4, AS4))
+ zlog_debug(
+ "%s [AS4] rcv OPEN w/ OPTION parameter len: %u, peeking for as4",
+ peer->host, length);
+ /* the error cases we DONT handle, we ONLY try to read as4 out of
+ * correctly formatted options.
+ */
+ while (stream_get_getp(s) < end) {
+ uint8_t opt_type;
+ uint16_t opt_length;
+
+ /* Ensure we can read the option type */
+ if (stream_get_getp(s) + 1 > end)
+ goto end;
+
+ /* Fetch the option type */
+ opt_type = stream_getc(s);
+
+ /*
+ * Check the length and fetch the opt_length
+ * If the peer is BGP_OPEN_EXT_OPT_PARAMS_CAPABLE(peer)
+ * then we do a getw which is 2 bytes. So we need to
+ * ensure that we can read that as well
+ */
+ if (BGP_OPEN_EXT_OPT_PARAMS_CAPABLE(peer)) {
+ if (stream_get_getp(s) + 2 > end)
+ goto end;
+
+ opt_length = stream_getw(s);
+ } else {
+ if (stream_get_getp(s) + 1 > end)
+ goto end;
+
+ opt_length = stream_getc(s);
+ }
+
+ /* Option length check. */
+ if (stream_get_getp(s) + opt_length > end)
+ goto end;
+
+ if (opt_type == BGP_OPEN_OPT_CAP) {
+ unsigned long capd_start = stream_get_getp(s);
+ unsigned long capd_end = capd_start + opt_length;
+
+ assert(capd_end <= end);
+
+ while (stream_get_getp(s) < capd_end) {
+ struct capability_header hdr;
+
+ if (stream_get_getp(s) + 2 > capd_end)
+ goto end;
+
+ hdr.code = stream_getc(s);
+ hdr.length = stream_getc(s);
+
+ if ((stream_get_getp(s) + hdr.length)
+ > capd_end)
+ goto end;
+
+ if (hdr.code == CAPABILITY_CODE_AS4) {
+ if (BGP_DEBUG(as4, AS4))
+ zlog_debug(
+ "[AS4] found AS4 capability, about to parse");
+ as4 = bgp_capability_as4(peer, &hdr);
+
+ goto end;
+ }
+ stream_forward_getp(s, hdr.length);
+ }
+ }
+ }
+
+end:
+ stream_set_getp(s, orig_getp);
+ return as4;
+}
+
+/**
+ * Parse open option.
+ *
+ * @param[out] mp_capability @see bgp_capability_parse() for semantics.
+ */
+int bgp_open_option_parse(struct peer *peer, uint16_t length,
+ int *mp_capability)
+{
+ int ret = 0;
+ uint8_t *error;
+ uint8_t error_data[BGP_STANDARD_MESSAGE_MAX_PACKET_SIZE];
+ struct stream *s = BGP_INPUT(peer);
+ size_t end = stream_get_getp(s) + length;
+
+ error = error_data;
+
+ if (bgp_debug_neighbor_events(peer))
+ zlog_debug("%s rcv OPEN w/ OPTION parameter len: %u",
+ peer->host, length);
+
+ /* Unset any previously received GR capability. */
+ UNSET_FLAG(peer->cap, PEER_CAP_RESTART_RCV);
+
+ while (stream_get_getp(s) < end) {
+ uint8_t opt_type;
+ uint16_t opt_length;
+
+ /*
+ * Check that we can read the opt_type and fetch it
+ */
+ if (STREAM_READABLE(s) < 1) {
+ zlog_info("%s Option length error", peer->host);
+ bgp_notify_send(peer, BGP_NOTIFY_OPEN_ERR,
+ BGP_NOTIFY_OPEN_MALFORMED_ATTR);
+ return -1;
+ }
+ opt_type = stream_getc(s);
+
+ /*
+ * Check the length of the stream to ensure that
+ * FRR can properly read the opt_length. Then read it
+ */
+ if (BGP_OPEN_EXT_OPT_PARAMS_CAPABLE(peer)) {
+ if (STREAM_READABLE(s) < 2) {
+ zlog_info("%s Option length error", peer->host);
+ bgp_notify_send(peer, BGP_NOTIFY_OPEN_ERR,
+ BGP_NOTIFY_OPEN_MALFORMED_ATTR);
+ return -1;
+ }
+
+ opt_length = stream_getw(s);
+ } else {
+ if (STREAM_READABLE(s) < 1) {
+ zlog_info("%s Option length error", peer->host);
+ bgp_notify_send(peer, BGP_NOTIFY_OPEN_ERR,
+ BGP_NOTIFY_OPEN_MALFORMED_ATTR);
+ return -1;
+ }
+
+ opt_length = stream_getc(s);
+ }
+
+ /* Option length check. */
+ if (STREAM_READABLE(s) < opt_length) {
+ zlog_info("%s Option length error (%d)", peer->host,
+ opt_length);
+ bgp_notify_send(peer, BGP_NOTIFY_OPEN_ERR,
+ BGP_NOTIFY_OPEN_MALFORMED_ATTR);
+ return -1;
+ }
+
+ if (bgp_debug_neighbor_events(peer))
+ zlog_debug(
+ "%s rcvd OPEN w/ optional parameter type %u (%s) len %u",
+ peer->host, opt_type,
+ opt_type == BGP_OPEN_OPT_AUTH
+ ? "Authentication"
+ : opt_type == BGP_OPEN_OPT_CAP
+ ? "Capability"
+ : "Unknown",
+ opt_length);
+
+ switch (opt_type) {
+ case BGP_OPEN_OPT_AUTH:
+ ret = bgp_auth_parse(peer, opt_length);
+ break;
+ case BGP_OPEN_OPT_CAP:
+ ret = bgp_capability_parse(peer, opt_length,
+ mp_capability, &error);
+ break;
+ default:
+ bgp_notify_send(peer, BGP_NOTIFY_OPEN_ERR,
+ BGP_NOTIFY_OPEN_UNSUP_PARAM);
+ ret = -1;
+ break;
+ }
+
+ /* Parse error. To accumulate all unsupported capability codes,
+ bgp_capability_parse does not return -1 when encounter
+ unsupported capability code. To detect that, please check
+ error and erro_data pointer, like below. */
+ if (ret < 0)
+ return -1;
+ }
+
+ /* All OPEN option is parsed. Check capability when strict compare
+ flag is enabled.*/
+ if (CHECK_FLAG(peer->flags, PEER_FLAG_STRICT_CAP_MATCH)) {
+ /* If Unsupported Capability exists. */
+ if (error != error_data) {
+ bgp_notify_send_with_data(peer, BGP_NOTIFY_OPEN_ERR,
+ BGP_NOTIFY_OPEN_UNSUP_CAPBL,
+ error_data,
+ error - error_data);
+ return -1;
+ }
+
+ /* Check local capability does not negotiated with remote
+ peer. */
+ if (!strict_capability_same(peer)) {
+ bgp_notify_send(peer, BGP_NOTIFY_OPEN_ERR,
+ BGP_NOTIFY_OPEN_UNSUP_CAPBL);
+ return -1;
+ }
+ }
+
+ /* Extended Message Support */
+ peer->max_packet_size =
+ (CHECK_FLAG(peer->cap, PEER_CAP_EXTENDED_MESSAGE_RCV)
+ && CHECK_FLAG(peer->cap, PEER_CAP_EXTENDED_MESSAGE_ADV))
+ ? BGP_EXTENDED_MESSAGE_MAX_PACKET_SIZE
+ : BGP_STANDARD_MESSAGE_MAX_PACKET_SIZE;
+
+ /* Check that roles are corresponding to each other */
+ if (bgp_role_violation(peer))
+ return -1;
+
+ /* Check there are no common AFI/SAFIs and send Unsupported Capability
+ error. */
+ if (*mp_capability
+ && !CHECK_FLAG(peer->flags, PEER_FLAG_OVERRIDE_CAPABILITY)) {
+ if (!peer->afc_nego[AFI_IP][SAFI_UNICAST]
+ && !peer->afc_nego[AFI_IP][SAFI_MULTICAST]
+ && !peer->afc_nego[AFI_IP][SAFI_LABELED_UNICAST]
+ && !peer->afc_nego[AFI_IP][SAFI_MPLS_VPN]
+ && !peer->afc_nego[AFI_IP][SAFI_ENCAP]
+ && !peer->afc_nego[AFI_IP][SAFI_FLOWSPEC]
+ && !peer->afc_nego[AFI_IP6][SAFI_UNICAST]
+ && !peer->afc_nego[AFI_IP6][SAFI_MULTICAST]
+ && !peer->afc_nego[AFI_IP6][SAFI_LABELED_UNICAST]
+ && !peer->afc_nego[AFI_IP6][SAFI_MPLS_VPN]
+ && !peer->afc_nego[AFI_IP6][SAFI_ENCAP]
+ && !peer->afc_nego[AFI_IP6][SAFI_FLOWSPEC]
+ && !peer->afc_nego[AFI_L2VPN][SAFI_EVPN]) {
+ flog_err(EC_BGP_PKT_OPEN,
+ "%s [Error] Configured AFI/SAFIs do not overlap with received MP capabilities",
+ peer->host);
+
+ if (error != error_data)
+ bgp_notify_send_with_data(
+ peer, BGP_NOTIFY_OPEN_ERR,
+ BGP_NOTIFY_OPEN_UNSUP_CAPBL, error_data,
+ error - error_data);
+ else
+ bgp_notify_send(peer, BGP_NOTIFY_OPEN_ERR,
+ BGP_NOTIFY_OPEN_UNSUP_CAPBL);
+ return -1;
+ }
+ }
+ return 0;
+}
+
+static void bgp_open_capability_orf(struct stream *s, struct peer *peer,
+ afi_t afi, safi_t safi, uint8_t code,
+ bool ext_opt_params)
+{
+ uint16_t cap_len;
+ uint8_t orf_len;
+ unsigned long capp;
+ unsigned long orfp;
+ unsigned long numberp;
+ int number_of_orfs = 0;
+ iana_afi_t pkt_afi = IANA_AFI_IPV4;
+ iana_safi_t pkt_safi = IANA_SAFI_UNICAST;
+
+ /* Convert AFI, SAFI to values for packet. */
+ bgp_map_afi_safi_int2iana(afi, safi, &pkt_afi, &pkt_safi);
+
+ stream_putc(s, BGP_OPEN_OPT_CAP);
+ capp = stream_get_endp(s); /* Set Capability Len Pointer */
+ ext_opt_params ? stream_putw(s, 0)
+ : stream_putc(s, 0); /* Capability Length */
+ stream_putc(s, code); /* Capability Code */
+ orfp = stream_get_endp(s); /* Set ORF Len Pointer */
+ stream_putc(s, 0); /* ORF Length */
+ stream_putw(s, pkt_afi);
+ stream_putc(s, 0);
+ stream_putc(s, pkt_safi);
+ numberp = stream_get_endp(s); /* Set Number Pointer */
+ stream_putc(s, 0); /* Number of ORFs */
+
+ /* Address Prefix ORF */
+ if (CHECK_FLAG(peer->af_flags[afi][safi], PEER_FLAG_ORF_PREFIX_SM)
+ || CHECK_FLAG(peer->af_flags[afi][safi], PEER_FLAG_ORF_PREFIX_RM)) {
+ stream_putc(s, (code == CAPABILITY_CODE_ORF
+ ? ORF_TYPE_PREFIX
+ : ORF_TYPE_PREFIX_OLD));
+
+ if (CHECK_FLAG(peer->af_flags[afi][safi],
+ PEER_FLAG_ORF_PREFIX_SM)
+ && CHECK_FLAG(peer->af_flags[afi][safi],
+ PEER_FLAG_ORF_PREFIX_RM)) {
+ SET_FLAG(peer->af_cap[afi][safi],
+ PEER_CAP_ORF_PREFIX_SM_ADV);
+ SET_FLAG(peer->af_cap[afi][safi],
+ PEER_CAP_ORF_PREFIX_RM_ADV);
+ stream_putc(s, ORF_MODE_BOTH);
+ } else if (CHECK_FLAG(peer->af_flags[afi][safi],
+ PEER_FLAG_ORF_PREFIX_SM)) {
+ SET_FLAG(peer->af_cap[afi][safi],
+ PEER_CAP_ORF_PREFIX_SM_ADV);
+ stream_putc(s, ORF_MODE_SEND);
+ } else {
+ SET_FLAG(peer->af_cap[afi][safi],
+ PEER_CAP_ORF_PREFIX_RM_ADV);
+ stream_putc(s, ORF_MODE_RECEIVE);
+ }
+ number_of_orfs++;
+ }
+
+ /* Total Number of ORFs. */
+ stream_putc_at(s, numberp, number_of_orfs);
+
+ /* Total ORF Len. */
+ orf_len = stream_get_endp(s) - orfp - 1;
+ stream_putc_at(s, orfp, orf_len);
+
+ /* Total Capability Len. */
+ cap_len = stream_get_endp(s) - capp - 1;
+ ext_opt_params ? stream_putw_at(s, capp, cap_len)
+ : stream_putc_at(s, capp, cap_len);
+}
+
+static void bgp_peer_send_gr_capability(struct stream *s, struct peer *peer,
+ bool ext_opt_params)
+{
+ int len;
+ iana_afi_t pkt_afi = IANA_AFI_IPV4;
+ afi_t afi;
+ safi_t safi;
+ iana_safi_t pkt_safi = IANA_SAFI_UNICAST;
+ uint32_t restart_time;
+ unsigned long capp = 0;
+ unsigned long rcapp = 0;
+
+ if (!CHECK_FLAG(peer->flags, PEER_FLAG_GRACEFUL_RESTART)
+ && !CHECK_FLAG(peer->flags, PEER_FLAG_GRACEFUL_RESTART_HELPER))
+ return;
+
+ if (BGP_DEBUG(graceful_restart, GRACEFUL_RESTART))
+ zlog_debug("[BGP_GR] Sending helper Capability for Peer :%s :",
+ peer->host);
+
+ SET_FLAG(peer->cap, PEER_CAP_RESTART_ADV);
+ stream_putc(s, BGP_OPEN_OPT_CAP);
+ capp = stream_get_endp(s); /* Set Capability Len Pointer */
+ ext_opt_params ? stream_putw(s, 0)
+ : stream_putc(s, 0); /* Capability Length */
+ stream_putc(s, CAPABILITY_CODE_RESTART);
+ /* Set Restart Capability Len Pointer */
+ rcapp = stream_get_endp(s);
+ stream_putc(s, 0);
+ restart_time = peer->bgp->restart_time;
+ if (peer->bgp->t_startup) {
+ SET_FLAG(restart_time, GRACEFUL_RESTART_R_BIT);
+ SET_FLAG(peer->cap, PEER_CAP_GRACEFUL_RESTART_R_BIT_ADV);
+ if (BGP_DEBUG(graceful_restart, GRACEFUL_RESTART))
+ zlog_debug("[BGP_GR] Sending R-Bit for peer: %s",
+ peer->host);
+ }
+
+ if (CHECK_FLAG(peer->bgp->flags, BGP_FLAG_GRACEFUL_NOTIFICATION)) {
+ SET_FLAG(restart_time, GRACEFUL_RESTART_N_BIT);
+ SET_FLAG(peer->cap, PEER_CAP_GRACEFUL_RESTART_N_BIT_ADV);
+ if (BGP_DEBUG(graceful_restart, GRACEFUL_RESTART))
+ zlog_debug("[BGP_GR] Sending N-Bit for peer: %s",
+ peer->host);
+ }
+
+ stream_putw(s, restart_time);
+
+ /* Send address-family specific graceful-restart capability
+ * only when GR config is present
+ */
+ if (CHECK_FLAG(peer->flags, PEER_FLAG_GRACEFUL_RESTART)) {
+ if (CHECK_FLAG(peer->bgp->flags, BGP_FLAG_GR_PRESERVE_FWD)
+ && BGP_DEBUG(graceful_restart, GRACEFUL_RESTART))
+ zlog_debug("[BGP_GR] F bit Set");
+
+ FOREACH_AFI_SAFI (afi, safi) {
+ if (!peer->afc[afi][safi])
+ continue;
+
+ if (BGP_DEBUG(graceful_restart, GRACEFUL_RESTART))
+ zlog_debug(
+ "[BGP_GR] Sending GR Capability for AFI :%d :, SAFI :%d:",
+ afi, safi);
+
+ /* Convert AFI, SAFI to values for
+ * packet.
+ */
+ bgp_map_afi_safi_int2iana(afi, safi, &pkt_afi,
+ &pkt_safi);
+ stream_putw(s, pkt_afi);
+ stream_putc(s, pkt_safi);
+ if (CHECK_FLAG(peer->bgp->flags,
+ BGP_FLAG_GR_PRESERVE_FWD))
+ stream_putc(s, GRACEFUL_RESTART_F_BIT);
+ else
+ stream_putc(s, 0);
+ }
+ }
+
+ /* Total Graceful restart capability Len. */
+ len = stream_get_endp(s) - rcapp - 1;
+ stream_putc_at(s, rcapp, len);
+
+ /* Total Capability Len. */
+ len = stream_get_endp(s) - capp - 1;
+ ext_opt_params ? stream_putw_at(s, capp, len - 1)
+ : stream_putc_at(s, capp, len);
+}
+
+static void bgp_peer_send_llgr_capability(struct stream *s, struct peer *peer,
+ bool ext_opt_params)
+{
+ int len;
+ iana_afi_t pkt_afi = IANA_AFI_IPV4;
+ afi_t afi;
+ safi_t safi;
+ iana_safi_t pkt_safi = IANA_SAFI_UNICAST;
+ unsigned long capp = 0;
+ unsigned long rcapp = 0;
+
+ if (!CHECK_FLAG(peer->cap, PEER_CAP_RESTART_ADV))
+ return;
+
+ SET_FLAG(peer->cap, PEER_CAP_LLGR_ADV);
+
+ stream_putc(s, BGP_OPEN_OPT_CAP);
+ capp = stream_get_endp(s); /* Set Capability Len Pointer */
+ ext_opt_params ? stream_putw(s, 0)
+ : stream_putc(s, 0); /* Capability Length */
+ stream_putc(s, CAPABILITY_CODE_LLGR);
+
+ rcapp = stream_get_endp(s);
+ stream_putc(s, 0);
+
+ FOREACH_AFI_SAFI (afi, safi) {
+ if (!peer->afc[afi][safi])
+ continue;
+
+ bgp_map_afi_safi_int2iana(afi, safi, &pkt_afi, &pkt_safi);
+
+ stream_putw(s, pkt_afi);
+ stream_putc(s, pkt_safi);
+ stream_putc(s, LLGR_F_BIT);
+ stream_put3(s, peer->bgp->llgr_stale_time);
+
+ SET_FLAG(peer->af_cap[afi][safi], PEER_CAP_LLGR_AF_ADV);
+ }
+
+ /* Total Long-lived Graceful Restart capability Len. */
+ len = stream_get_endp(s) - rcapp - 1;
+ stream_putc_at(s, rcapp, len);
+
+ /* Total Capability Len. */
+ len = stream_get_endp(s) - capp - 1;
+ ext_opt_params ? stream_putw_at(s, capp, len - 1)
+ : stream_putc_at(s, capp, len);
+}
+
+/* Fill in capability open option to the packet. */
+uint16_t bgp_open_capability(struct stream *s, struct peer *peer,
+ bool ext_opt_params)
+{
+ uint16_t len;
+ unsigned long cp, capp, rcapp, eopl = 0;
+ iana_afi_t pkt_afi = IANA_AFI_IPV4;
+ afi_t afi;
+ safi_t safi;
+ iana_safi_t pkt_safi = IANA_SAFI_UNICAST;
+ as_t local_as;
+ uint8_t afi_safi_count = 0;
+ int adv_addpath_tx = 0;
+
+ /* Non-Ext OP Len. */
+ cp = stream_get_endp(s);
+ stream_putc(s, 0);
+
+ if (ext_opt_params) {
+ /* Non-Ext OP Len. */
+ stream_putc_at(s, cp, BGP_OPEN_NON_EXT_OPT_LEN);
+
+ /* Non-Ext OP Type */
+ stream_putc(s, BGP_OPEN_NON_EXT_OPT_TYPE_EXTENDED_LENGTH);
+
+ /* Extended Opt. Parm. Length */
+ eopl = stream_get_endp(s);
+ stream_putw(s, 0);
+ }
+
+ /* Do not send capability. */
+ if (!CHECK_FLAG(peer->sflags, PEER_STATUS_CAPABILITY_OPEN)
+ || CHECK_FLAG(peer->flags, PEER_FLAG_DONT_CAPABILITY))
+ return 0;
+
+ /* MP capability for configured AFI, SAFI */
+ FOREACH_AFI_SAFI (afi, safi) {
+ if (peer->afc[afi][safi]) {
+ /* Convert AFI, SAFI to values for packet. */
+ bgp_map_afi_safi_int2iana(afi, safi, &pkt_afi,
+ &pkt_safi);
+
+ peer->afc_adv[afi][safi] = 1;
+ stream_putc(s, BGP_OPEN_OPT_CAP);
+ ext_opt_params
+ ? stream_putw(s, CAPABILITY_CODE_MP_LEN + 2)
+ : stream_putc(s, CAPABILITY_CODE_MP_LEN + 2);
+ stream_putc(s, CAPABILITY_CODE_MP);
+ stream_putc(s, CAPABILITY_CODE_MP_LEN);
+ stream_putw(s, pkt_afi);
+ stream_putc(s, 0);
+ stream_putc(s, pkt_safi);
+
+ /* Extended nexthop capability - currently
+ * supporting RFC-5549 for
+ * Link-Local peering only
+ */
+ if (CHECK_FLAG(peer->flags, PEER_FLAG_CAPABILITY_ENHE)
+ && peer->su.sa.sa_family == AF_INET6
+ && afi == AFI_IP
+ && (safi == SAFI_UNICAST || safi == SAFI_MPLS_VPN
+ || safi == SAFI_LABELED_UNICAST)) {
+ /* RFC 5549 Extended Next Hop Encoding
+ */
+ SET_FLAG(peer->cap, PEER_CAP_ENHE_ADV);
+ stream_putc(s, BGP_OPEN_OPT_CAP);
+ ext_opt_params
+ ? stream_putw(s,
+ CAPABILITY_CODE_ENHE_LEN
+ + 2)
+ : stream_putc(s,
+ CAPABILITY_CODE_ENHE_LEN
+ + 2);
+ stream_putc(s, CAPABILITY_CODE_ENHE);
+ stream_putc(s, CAPABILITY_CODE_ENHE_LEN);
+
+ SET_FLAG(peer->af_cap[AFI_IP][safi],
+ PEER_CAP_ENHE_AF_ADV);
+ stream_putw(s, pkt_afi);
+ stream_putw(s, pkt_safi);
+ stream_putw(s, afi_int2iana(AFI_IP6));
+
+ if (CHECK_FLAG(peer->af_cap[afi][safi],
+ PEER_CAP_ENHE_AF_RCV))
+ SET_FLAG(peer->af_cap[afi][safi],
+ PEER_CAP_ENHE_AF_NEGO);
+ }
+ }
+ }
+
+ /* Route refresh. */
+ SET_FLAG(peer->cap, PEER_CAP_REFRESH_ADV);
+ stream_putc(s, BGP_OPEN_OPT_CAP);
+ ext_opt_params ? stream_putw(s, CAPABILITY_CODE_REFRESH_LEN + 2)
+ : stream_putc(s, CAPABILITY_CODE_REFRESH_LEN + 2);
+ stream_putc(s, CAPABILITY_CODE_REFRESH_OLD);
+ stream_putc(s, CAPABILITY_CODE_REFRESH_LEN);
+ stream_putc(s, BGP_OPEN_OPT_CAP);
+ ext_opt_params ? stream_putw(s, CAPABILITY_CODE_REFRESH_LEN + 2)
+ : stream_putc(s, CAPABILITY_CODE_REFRESH_LEN + 2);
+ stream_putc(s, CAPABILITY_CODE_REFRESH);
+ stream_putc(s, CAPABILITY_CODE_REFRESH_LEN);
+
+ /* Enhanced Route Refresh. */
+ SET_FLAG(peer->cap, PEER_CAP_ENHANCED_RR_ADV);
+ stream_putc(s, BGP_OPEN_OPT_CAP);
+ ext_opt_params ? stream_putw(s, CAPABILITY_CODE_ENHANCED_LEN + 2)
+ : stream_putc(s, CAPABILITY_CODE_ENHANCED_LEN + 2);
+ stream_putc(s, CAPABILITY_CODE_ENHANCED_RR);
+ stream_putc(s, CAPABILITY_CODE_ENHANCED_LEN);
+
+ /* AS4 */
+ SET_FLAG(peer->cap, PEER_CAP_AS4_ADV);
+ stream_putc(s, BGP_OPEN_OPT_CAP);
+ ext_opt_params ? stream_putw(s, CAPABILITY_CODE_AS4_LEN + 2)
+ : stream_putc(s, CAPABILITY_CODE_AS4_LEN + 2);
+ stream_putc(s, CAPABILITY_CODE_AS4);
+ stream_putc(s, CAPABILITY_CODE_AS4_LEN);
+ if (peer->change_local_as)
+ local_as = peer->change_local_as;
+ else
+ local_as = peer->local_as;
+ stream_putl(s, local_as);
+
+ /* Extended Message Support */
+ SET_FLAG(peer->cap, PEER_CAP_EXTENDED_MESSAGE_ADV);
+ stream_putc(s, BGP_OPEN_OPT_CAP);
+ ext_opt_params ? stream_putw(s, CAPABILITY_CODE_EXT_MESSAGE_LEN + 2)
+ : stream_putc(s, CAPABILITY_CODE_EXT_MESSAGE_LEN + 2);
+ stream_putc(s, CAPABILITY_CODE_EXT_MESSAGE);
+ stream_putc(s, CAPABILITY_CODE_EXT_MESSAGE_LEN);
+
+ /* Role*/
+ if (peer->local_role != ROLE_UNDEFINED) {
+ SET_FLAG(peer->cap, PEER_CAP_ROLE_ADV);
+ stream_putc(s, BGP_OPEN_OPT_CAP);
+ stream_putc(s, CAPABILITY_CODE_ROLE_LEN + 2);
+ stream_putc(s, CAPABILITY_CODE_ROLE);
+ stream_putc(s, CAPABILITY_CODE_ROLE_LEN);
+ stream_putc(s, peer->local_role);
+ }
+
+ /* AddPath */
+ FOREACH_AFI_SAFI (afi, safi) {
+ if (peer->afc[afi][safi]) {
+ afi_safi_count++;
+
+ /* Only advertise addpath TX if a feature that
+ * will use it is
+ * configured */
+ if (peer->addpath_type[afi][safi] != BGP_ADDPATH_NONE)
+ adv_addpath_tx = 1;
+ }
+ }
+
+ SET_FLAG(peer->cap, PEER_CAP_ADDPATH_ADV);
+ stream_putc(s, BGP_OPEN_OPT_CAP);
+ ext_opt_params
+ ? stream_putw(s, (CAPABILITY_CODE_ADDPATH_LEN * afi_safi_count)
+ + 2)
+ : stream_putc(s, (CAPABILITY_CODE_ADDPATH_LEN * afi_safi_count)
+ + 2);
+ stream_putc(s, CAPABILITY_CODE_ADDPATH);
+ stream_putc(s, CAPABILITY_CODE_ADDPATH_LEN * afi_safi_count);
+
+ FOREACH_AFI_SAFI (afi, safi) {
+ if (peer->afc[afi][safi]) {
+ bool adv_addpath_rx =
+ !CHECK_FLAG(peer->af_flags[afi][safi],
+ PEER_FLAG_DISABLE_ADDPATH_RX);
+ uint8_t flags = 0;
+
+ /* Convert AFI, SAFI to values for packet. */
+ bgp_map_afi_safi_int2iana(afi, safi, &pkt_afi,
+ &pkt_safi);
+
+ stream_putw(s, pkt_afi);
+ stream_putc(s, pkt_safi);
+
+ if (adv_addpath_rx) {
+ SET_FLAG(flags, BGP_ADDPATH_RX);
+ SET_FLAG(peer->af_cap[afi][safi],
+ PEER_CAP_ADDPATH_AF_RX_ADV);
+ } else {
+ UNSET_FLAG(peer->af_cap[afi][safi],
+ PEER_CAP_ADDPATH_AF_RX_ADV);
+ }
+
+ if (adv_addpath_tx) {
+ SET_FLAG(flags, BGP_ADDPATH_TX);
+ SET_FLAG(peer->af_cap[afi][safi],
+ PEER_CAP_ADDPATH_AF_TX_ADV);
+ } else {
+ UNSET_FLAG(peer->af_cap[afi][safi],
+ PEER_CAP_ADDPATH_AF_TX_ADV);
+ }
+
+ stream_putc(s, flags);
+ }
+ }
+
+ /* ORF capability. */
+ FOREACH_AFI_SAFI (afi, safi) {
+ if (CHECK_FLAG(peer->af_flags[afi][safi],
+ PEER_FLAG_ORF_PREFIX_SM)
+ || CHECK_FLAG(peer->af_flags[afi][safi],
+ PEER_FLAG_ORF_PREFIX_RM)) {
+ bgp_open_capability_orf(s, peer, afi, safi,
+ CAPABILITY_CODE_ORF_OLD,
+ ext_opt_params);
+ bgp_open_capability_orf(s, peer, afi, safi,
+ CAPABILITY_CODE_ORF,
+ ext_opt_params);
+ }
+ }
+
+ /* Dynamic capability. */
+ if (CHECK_FLAG(peer->flags, PEER_FLAG_DYNAMIC_CAPABILITY)) {
+ SET_FLAG(peer->cap, PEER_CAP_DYNAMIC_ADV);
+ stream_putc(s, BGP_OPEN_OPT_CAP);
+ ext_opt_params
+ ? stream_putw(s, CAPABILITY_CODE_DYNAMIC_LEN + 2)
+ : stream_putc(s, CAPABILITY_CODE_DYNAMIC_LEN + 2);
+ stream_putc(s, CAPABILITY_CODE_DYNAMIC_OLD);
+ stream_putc(s, CAPABILITY_CODE_DYNAMIC_LEN);
+ stream_putc(s, BGP_OPEN_OPT_CAP);
+ ext_opt_params
+ ? stream_putw(s, CAPABILITY_CODE_DYNAMIC_LEN + 2)
+ : stream_putc(s, CAPABILITY_CODE_DYNAMIC_LEN + 2);
+ stream_putc(s, CAPABILITY_CODE_DYNAMIC);
+ stream_putc(s, CAPABILITY_CODE_DYNAMIC_LEN);
+ }
+
+ /* Hostname capability */
+ if (cmd_hostname_get()) {
+ SET_FLAG(peer->cap, PEER_CAP_HOSTNAME_ADV);
+ stream_putc(s, BGP_OPEN_OPT_CAP);
+ rcapp = stream_get_endp(s); /* Ptr to length placeholder */
+ ext_opt_params ? stream_putw(s, 0)
+ : stream_putc(s, 0); /* Capability Length */
+ stream_putc(s, CAPABILITY_CODE_FQDN);
+ capp = stream_get_endp(s);
+ stream_putc(s, 0); /* dummy len for now */
+ len = strlen(cmd_hostname_get());
+ if (len > BGP_MAX_HOSTNAME)
+ len = BGP_MAX_HOSTNAME;
+
+ stream_putc(s, len);
+ stream_put(s, cmd_hostname_get(), len);
+ if (cmd_domainname_get()) {
+ len = strlen(cmd_domainname_get());
+ if (len > BGP_MAX_HOSTNAME)
+ len = BGP_MAX_HOSTNAME;
+
+ stream_putc(s, len);
+ stream_put(s, cmd_domainname_get(), len);
+ } else
+ stream_putc(s, 0); /* 0 length */
+
+ /* Set the lengths straight */
+ len = stream_get_endp(s) - rcapp - 1;
+ ext_opt_params ? stream_putw_at(s, rcapp, len - 1)
+ : stream_putc_at(s, rcapp, len);
+
+ len = stream_get_endp(s) - capp - 1;
+ stream_putc_at(s, capp, len);
+
+ if (bgp_debug_neighbor_events(peer))
+ zlog_debug(
+ "%s Sending hostname cap with hn = %s, dn = %s",
+ peer->host, cmd_hostname_get(),
+ cmd_domainname_get());
+ }
+
+ bgp_peer_send_gr_capability(s, peer, ext_opt_params);
+ bgp_peer_send_llgr_capability(s, peer, ext_opt_params);
+
+ /* Total Opt Parm Len. */
+ len = stream_get_endp(s) - cp - 1;
+
+ if (ext_opt_params) {
+ len = stream_get_endp(s) - eopl - 2;
+ stream_putw_at(s, eopl, len);
+ } else {
+ stream_putc_at(s, cp, len);
+ }
+
+ return len;
+}
diff --git a/bgpd/bgp_open.h b/bgpd/bgp_open.h
new file mode 100644
index 0000000..19ddd9b
--- /dev/null
+++ b/bgpd/bgp_open.h
@@ -0,0 +1,114 @@
+/* BGP open message handling
+ * Copyright (C) 1999 Kunihiro Ishiguro
+ *
+ * This file is part of GNU Zebra.
+ *
+ * GNU Zebra is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License as published by the
+ * Free Software Foundation; either version 2, or (at your option) any
+ * later version.
+ *
+ * GNU Zebra is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program; see the file COPYING; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
+ */
+
+#ifndef _QUAGGA_BGP_OPEN_H
+#define _QUAGGA_BGP_OPEN_H
+
+/* Standard header for capability TLV */
+struct capability_header {
+ uint8_t code;
+ uint8_t length;
+};
+
+/* Generic MP capability data */
+struct capability_mp_data {
+ uint16_t afi; /* iana_afi_t */
+ uint8_t reserved;
+ uint8_t safi; /* iana_safi_t */
+};
+
+struct graceful_restart_af {
+ afi_t afi;
+ safi_t safi;
+ uint8_t flag;
+};
+
+/* Capability Code */
+#define CAPABILITY_CODE_MP 1 /* Multiprotocol Extensions */
+#define CAPABILITY_CODE_REFRESH 2 /* Route Refresh Capability */
+#define CAPABILITY_CODE_ORF 3 /* Cooperative Route Filtering Capability */
+#define CAPABILITY_CODE_RESTART 64 /* Graceful Restart Capability */
+#define CAPABILITY_CODE_AS4 65 /* 4-octet AS number Capability */
+#define CAPABILITY_CODE_DYNAMIC_OLD 66 /* Dynamic Capability, deprecated since 2003 */
+#define CAPABILITY_CODE_DYNAMIC 67 /* Dynamic Capability */
+#define CAPABILITY_CODE_ADDPATH 69 /* Addpath Capability */
+#define CAPABILITY_CODE_ENHANCED_RR 70 /* Enhanced Route Refresh capability */
+#define CAPABILITY_CODE_LLGR 71 /* Long-lived Graceful Restart */
+#define CAPABILITY_CODE_FQDN 73 /* Advertise hostname capability */
+#define CAPABILITY_CODE_ENHE 5 /* Extended Next Hop Encoding */
+#define CAPABILITY_CODE_REFRESH_OLD 128 /* Route Refresh Capability(cisco) */
+#define CAPABILITY_CODE_ORF_OLD 130 /* Cooperative Route Filtering Capability(cisco) */
+#define CAPABILITY_CODE_EXT_MESSAGE 6 /* Extended Message Support */
+#define CAPABILITY_CODE_ROLE 9 /* Role Capability */
+
+/* Capability Length */
+#define CAPABILITY_CODE_MP_LEN 4
+#define CAPABILITY_CODE_REFRESH_LEN 0
+#define CAPABILITY_CODE_DYNAMIC_LEN 0
+#define CAPABILITY_CODE_RESTART_LEN 2 /* Receiving only case */
+#define CAPABILITY_CODE_AS4_LEN 4
+#define CAPABILITY_CODE_ADDPATH_LEN 4
+#define CAPABILITY_CODE_ENHE_LEN 6 /* NRLI AFI = 2, SAFI = 2, Nexthop AFI = 2 */
+#define CAPABILITY_CODE_MIN_FQDN_LEN 2
+#define CAPABILITY_CODE_ENHANCED_LEN 0
+#define CAPABILITY_CODE_LLGR_LEN 0
+#define CAPABILITY_CODE_ORF_LEN 5
+#define CAPABILITY_CODE_EXT_MESSAGE_LEN 0 /* Extended Message Support */
+#define CAPABILITY_CODE_ROLE_LEN 1
+
+/* Cooperative Route Filtering Capability. */
+
+/* ORF Type */
+#define ORF_TYPE_PREFIX 64
+#define ORF_TYPE_PREFIX_OLD 128
+
+/* ORF Mode */
+#define ORF_MODE_RECEIVE 1
+#define ORF_MODE_SEND 2
+#define ORF_MODE_BOTH 3
+
+/* Capability Message Action. */
+#define CAPABILITY_ACTION_SET 0
+#define CAPABILITY_ACTION_UNSET 1
+
+/* Graceful Restart */
+#define GRACEFUL_RESTART_R_BIT 0x8000
+#define GRACEFUL_RESTART_N_BIT 0x4000
+#define GRACEFUL_RESTART_F_BIT 0x80
+
+/* Long-lived Graceful Restart */
+#define LLGR_F_BIT 0x80
+
+/* Optional Parameters */
+#define BGP_OPEN_NON_EXT_OPT_LEN 255 /* Non-Ext OP Len. */
+#define BGP_OPEN_NON_EXT_OPT_TYPE_EXTENDED_LENGTH 255 /* Non-Ext OP Type */
+#define BGP_OPEN_EXT_OPT_PARAMS_CAPABLE(peer) \
+ (CHECK_FLAG(peer->flags, PEER_FLAG_EXTENDED_OPT_PARAMS) \
+ || CHECK_FLAG(peer->sflags, PEER_STATUS_EXT_OPT_PARAMS_LENGTH))
+
+extern int bgp_open_option_parse(struct peer *peer, uint16_t length,
+ int *mp_capability);
+extern uint16_t bgp_open_capability(struct stream *s, struct peer *peer,
+ bool ext_opt_params);
+extern void bgp_capability_vty_out(struct vty *vty, struct peer *peer,
+ bool use_json, json_object *json_neigh);
+extern as_t peek_for_as4_capability(struct peer *peer, uint16_t length);
+
+#endif /* _QUAGGA_BGP_OPEN_H */
diff --git a/bgpd/bgp_packet.c b/bgpd/bgp_packet.c
new file mode 100644
index 0000000..ec54943
--- /dev/null
+++ b/bgpd/bgp_packet.c
@@ -0,0 +1,3047 @@
+/* BGP packet management routine.
+ * Contains utility functions for constructing and consuming BGP messages.
+ * Copyright (C) 2017 Cumulus Networks
+ * Copyright (C) 1999 Kunihiro Ishiguro
+ *
+ * This file is part of GNU Zebra.
+ *
+ * GNU Zebra is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License as published by the
+ * Free Software Foundation; either version 2, or (at your option) any
+ * later version.
+ *
+ * GNU Zebra is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program; see the file COPYING; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
+ */
+
+#include <zebra.h>
+#include <sys/time.h>
+
+#include "thread.h"
+#include "stream.h"
+#include "network.h"
+#include "prefix.h"
+#include "command.h"
+#include "log.h"
+#include "memory.h"
+#include "sockunion.h" /* for inet_ntop () */
+#include "sockopt.h"
+#include "linklist.h"
+#include "plist.h"
+#include "queue.h"
+#include "filter.h"
+#include "lib_errors.h"
+
+#include "bgpd/bgpd.h"
+#include "bgpd/bgp_table.h"
+#include "bgpd/bgp_dump.h"
+#include "bgpd/bgp_bmp.h"
+#include "bgpd/bgp_attr.h"
+#include "bgpd/bgp_debug.h"
+#include "bgpd/bgp_errors.h"
+#include "bgpd/bgp_fsm.h"
+#include "bgpd/bgp_route.h"
+#include "bgpd/bgp_packet.h"
+#include "bgpd/bgp_open.h"
+#include "bgpd/bgp_aspath.h"
+#include "bgpd/bgp_community.h"
+#include "bgpd/bgp_ecommunity.h"
+#include "bgpd/bgp_lcommunity.h"
+#include "bgpd/bgp_network.h"
+#include "bgpd/bgp_mplsvpn.h"
+#include "bgpd/bgp_evpn.h"
+#include "bgpd/bgp_advertise.h"
+#include "bgpd/bgp_vty.h"
+#include "bgpd/bgp_updgrp.h"
+#include "bgpd/bgp_label.h"
+#include "bgpd/bgp_io.h"
+#include "bgpd/bgp_keepalives.h"
+#include "bgpd/bgp_flowspec.h"
+#include "bgpd/bgp_trace.h"
+
+DEFINE_HOOK(bgp_packet_dump,
+ (struct peer *peer, uint8_t type, bgp_size_t size,
+ struct stream *s),
+ (peer, type, size, s));
+
+DEFINE_HOOK(bgp_packet_send,
+ (struct peer *peer, uint8_t type, bgp_size_t size,
+ struct stream *s),
+ (peer, type, size, s));
+
+/**
+ * Sets marker and type fields for a BGP message.
+ *
+ * @param s the stream containing the packet
+ * @param type the packet type
+ * @return the size of the stream
+ */
+int bgp_packet_set_marker(struct stream *s, uint8_t type)
+{
+ int i;
+
+ /* Fill in marker. */
+ for (i = 0; i < BGP_MARKER_SIZE; i++)
+ stream_putc(s, 0xff);
+
+ /* Dummy total length. This field is should be filled in later on. */
+ stream_putw(s, 0);
+
+ /* BGP packet type. */
+ stream_putc(s, type);
+
+ /* Return current stream size. */
+ return stream_get_endp(s);
+}
+
+/**
+ * Sets size field for a BGP message.
+ *
+ * Size field is set to the size of the stream passed.
+ *
+ * @param s the stream containing the packet
+ */
+void bgp_packet_set_size(struct stream *s)
+{
+ int cp;
+
+ /* Preserve current pointer. */
+ cp = stream_get_endp(s);
+ stream_putw_at(s, BGP_MARKER_SIZE, cp);
+}
+
+/*
+ * Push a packet onto the beginning of the peer's output queue.
+ * This function acquires the peer's write mutex before proceeding.
+ */
+static void bgp_packet_add(struct peer *peer, struct stream *s)
+{
+ intmax_t delta;
+ uint32_t holdtime;
+ intmax_t sendholdtime;
+
+ frr_with_mutex (&peer->io_mtx) {
+ /* if the queue is empty, reset the "last OK" timestamp to
+ * now, otherwise if we write another packet immediately
+ * after it'll get confused
+ */
+ if (!stream_fifo_count_safe(peer->obuf))
+ peer->last_sendq_ok = monotime(NULL);
+
+ stream_fifo_push(peer->obuf, s);
+
+ delta = monotime(NULL) - peer->last_sendq_ok;
+
+ if (CHECK_FLAG(peer->flags, PEER_FLAG_TIMER))
+ holdtime = atomic_load_explicit(&peer->holdtime,
+ memory_order_relaxed);
+ else
+ holdtime = peer->bgp->default_holdtime;
+
+ sendholdtime = holdtime * 2;
+
+ /* Note that when we're here, we're adding some packet to the
+ * OutQ. That includes keepalives when there is nothing to
+ * do, so there's a guarantee we pass by here once in a while.
+ *
+ * That implies there is no need to go set up another separate
+ * timer that ticks down SendHoldTime, as we'll be here sooner
+ * or later anyway and will see the checks below failing.
+ */
+ if (!holdtime) {
+ /* no holdtime, do nothing. */
+ } else if (delta > sendholdtime) {
+ flog_err(
+ EC_BGP_SENDQ_STUCK_PROPER,
+ "%pBP has not made any SendQ progress for 2 holdtimes (%jds), terminating session",
+ peer, sendholdtime);
+ BGP_EVENT_ADD(peer, TCP_fatal_error);
+ } else if (delta > (intmax_t)holdtime &&
+ monotime(NULL) - peer->last_sendq_warn > 5) {
+ flog_warn(
+ EC_BGP_SENDQ_STUCK_WARN,
+ "%pBP has not made any SendQ progress for 1 holdtime (%us), peer overloaded?",
+ peer, holdtime);
+ peer->last_sendq_warn = monotime(NULL);
+ }
+ }
+}
+
+static struct stream *bgp_update_packet_eor(struct peer *peer, afi_t afi,
+ safi_t safi)
+{
+ struct stream *s;
+ iana_afi_t pkt_afi = IANA_AFI_IPV4;
+ iana_safi_t pkt_safi = IANA_SAFI_UNICAST;
+
+ if (DISABLE_BGP_ANNOUNCE)
+ return NULL;
+
+ if (bgp_debug_neighbor_events(peer))
+ zlog_debug("send End-of-RIB for %s to %s",
+ get_afi_safi_str(afi, safi, false), peer->host);
+
+ s = stream_new(peer->max_packet_size);
+
+ /* Make BGP update packet. */
+ bgp_packet_set_marker(s, BGP_MSG_UPDATE);
+
+ /* Unfeasible Routes Length */
+ stream_putw(s, 0);
+
+ if (afi == AFI_IP && safi == SAFI_UNICAST) {
+ /* Total Path Attribute Length */
+ stream_putw(s, 0);
+ } else {
+ /* Convert AFI, SAFI to values for packet. */
+ bgp_map_afi_safi_int2iana(afi, safi, &pkt_afi, &pkt_safi);
+
+ /* Total Path Attribute Length */
+ stream_putw(s, 6);
+ stream_putc(s, BGP_ATTR_FLAG_OPTIONAL);
+ stream_putc(s, BGP_ATTR_MP_UNREACH_NLRI);
+ stream_putc(s, 3);
+ stream_putw(s, pkt_afi);
+ stream_putc(s, pkt_safi);
+ }
+
+ bgp_packet_set_size(s);
+ return s;
+}
+
+/* Called when there is a change in the EOR(implicit or explicit) status of a
+ * peer. Ends the update-delay if all expected peers are done with EORs. */
+void bgp_check_update_delay(struct bgp *bgp)
+{
+ struct listnode *node, *nnode;
+ struct peer *peer = NULL;
+
+ if (bgp_debug_neighbor_events(peer))
+ zlog_debug("Checking update delay, T: %d R: %d I:%d E: %d",
+ bgp->established, bgp->restarted_peers,
+ bgp->implicit_eors, bgp->explicit_eors);
+
+ if (bgp->established
+ <= bgp->restarted_peers + bgp->implicit_eors + bgp->explicit_eors) {
+ /*
+ * This is an extra sanity check to make sure we wait for all
+ * the eligible configured peers. This check is performed if
+ * establish wait timer is on, or establish wait option is not
+ * given with the update-delay command
+ */
+ if (bgp->t_establish_wait
+ || (bgp->v_establish_wait == bgp->v_update_delay))
+ for (ALL_LIST_ELEMENTS(bgp->peer, node, nnode, peer)) {
+ if (CHECK_FLAG(peer->flags,
+ PEER_FLAG_CONFIG_NODE)
+ && !CHECK_FLAG(peer->flags,
+ PEER_FLAG_SHUTDOWN)
+ && !CHECK_FLAG(peer->bgp->flags,
+ BGP_FLAG_SHUTDOWN)
+ && !peer->update_delay_over) {
+ if (bgp_debug_neighbor_events(peer))
+ zlog_debug(
+ " Peer %s pending, continuing read-only mode",
+ peer->host);
+ return;
+ }
+ }
+
+ zlog_info(
+ "Update delay ended, restarted: %d, EORs implicit: %d, explicit: %d",
+ bgp->restarted_peers, bgp->implicit_eors,
+ bgp->explicit_eors);
+ bgp_update_delay_end(bgp);
+ }
+}
+
+/*
+ * Called if peer is known to have restarted. The restart-state bit in
+ * Graceful-Restart capability is used for that
+ */
+void bgp_update_restarted_peers(struct peer *peer)
+{
+ if (!bgp_update_delay_active(peer->bgp))
+ return; /* BGP update delay has ended */
+ if (peer->update_delay_over)
+ return; /* This peer has already been considered */
+
+ if (bgp_debug_neighbor_events(peer))
+ zlog_debug("Peer %s: Checking restarted", peer->host);
+
+ if (peer_established(peer)) {
+ peer->update_delay_over = 1;
+ peer->bgp->restarted_peers++;
+ bgp_check_update_delay(peer->bgp);
+ }
+}
+
+/*
+ * Called as peer receives a keep-alive. Determines if this occurence can be
+ * taken as an implicit EOR for this peer.
+ * NOTE: The very first keep-alive after the Established state of a peer is
+ * considered implicit EOR for the update-delay purposes
+ */
+void bgp_update_implicit_eors(struct peer *peer)
+{
+ if (!bgp_update_delay_active(peer->bgp))
+ return; /* BGP update delay has ended */
+ if (peer->update_delay_over)
+ return; /* This peer has already been considered */
+
+ if (bgp_debug_neighbor_events(peer))
+ zlog_debug("Peer %s: Checking implicit EORs", peer->host);
+
+ if (peer_established(peer)) {
+ peer->update_delay_over = 1;
+ peer->bgp->implicit_eors++;
+ bgp_check_update_delay(peer->bgp);
+ }
+}
+
+/*
+ * Should be called only when there is a change in the EOR_RECEIVED status
+ * for any afi/safi on a peer.
+ */
+static void bgp_update_explicit_eors(struct peer *peer)
+{
+ afi_t afi;
+ safi_t safi;
+
+ if (!bgp_update_delay_active(peer->bgp))
+ return; /* BGP update delay has ended */
+ if (peer->update_delay_over)
+ return; /* This peer has already been considered */
+
+ if (bgp_debug_neighbor_events(peer))
+ zlog_debug("Peer %s: Checking explicit EORs", peer->host);
+
+ FOREACH_AFI_SAFI (afi, safi) {
+ if (peer->afc_nego[afi][safi]
+ && !CHECK_FLAG(peer->af_sflags[afi][safi],
+ PEER_STATUS_EOR_RECEIVED)) {
+ if (bgp_debug_neighbor_events(peer))
+ zlog_debug(
+ " afi %d safi %d didn't receive EOR",
+ afi, safi);
+ return;
+ }
+ }
+
+ peer->update_delay_over = 1;
+ peer->bgp->explicit_eors++;
+ bgp_check_update_delay(peer->bgp);
+}
+
+/**
+ * Frontend for NLRI parsing, to fan-out to AFI/SAFI specific parsers.
+ *
+ * mp_withdraw, if set, is used to nullify attr structure on most of the
+ * calling safi function and for evpn, passed as parameter
+ */
+int bgp_nlri_parse(struct peer *peer, struct attr *attr,
+ struct bgp_nlri *packet, bool mp_withdraw)
+{
+ switch (packet->safi) {
+ case SAFI_UNICAST:
+ case SAFI_MULTICAST:
+ return bgp_nlri_parse_ip(peer, mp_withdraw ? NULL : attr,
+ packet);
+ case SAFI_LABELED_UNICAST:
+ return bgp_nlri_parse_label(peer, mp_withdraw ? NULL : attr,
+ packet);
+ case SAFI_MPLS_VPN:
+ return bgp_nlri_parse_vpn(peer, mp_withdraw ? NULL : attr,
+ packet);
+ case SAFI_EVPN:
+ return bgp_nlri_parse_evpn(peer, attr, packet, mp_withdraw);
+ case SAFI_FLOWSPEC:
+ return bgp_nlri_parse_flowspec(peer, attr, packet, mp_withdraw);
+ }
+ return BGP_NLRI_PARSE_ERROR;
+}
+
+
+/*
+ * Check if route-refresh request from peer is pending (received before EoR),
+ * and process it now.
+ */
+static void bgp_process_pending_refresh(struct peer *peer, afi_t afi,
+ safi_t safi)
+{
+ if (CHECK_FLAG(peer->af_sflags[afi][safi],
+ PEER_STATUS_REFRESH_PENDING)) {
+ UNSET_FLAG(peer->af_sflags[afi][safi],
+ PEER_STATUS_REFRESH_PENDING);
+ bgp_route_refresh_send(peer, afi, safi, 0, 0, 0,
+ BGP_ROUTE_REFRESH_BORR);
+ if (bgp_debug_neighbor_events(peer))
+ zlog_debug(
+ "%pBP sending route-refresh (BoRR) for %s/%s (for pending REQUEST)",
+ peer, afi2str(afi), safi2str(safi));
+
+ SET_FLAG(peer->af_sflags[afi][safi], PEER_STATUS_BORR_SEND);
+ UNSET_FLAG(peer->af_sflags[afi][safi], PEER_STATUS_EORR_SEND);
+ bgp_announce_route(peer, afi, safi, true);
+ }
+}
+
+/*
+ * Checks a variety of conditions to determine whether the peer needs to be
+ * rescheduled for packet generation again, and does so if necessary.
+ *
+ * @param peer to check for rescheduling
+ */
+static void bgp_write_proceed_actions(struct peer *peer)
+{
+ afi_t afi;
+ safi_t safi;
+ struct peer_af *paf;
+ struct bpacket *next_pkt;
+ struct update_subgroup *subgrp;
+ enum bgp_af_index index;
+
+ for (index = BGP_AF_START; index < BGP_AF_MAX; index++) {
+ paf = peer->peer_af_array[index];
+ if (!paf)
+ continue;
+
+ subgrp = paf->subgroup;
+ if (!subgrp)
+ continue;
+
+ next_pkt = paf->next_pkt_to_send;
+ if (next_pkt && next_pkt->buffer) {
+ BGP_TIMER_ON(peer->t_generate_updgrp_packets,
+ bgp_generate_updgrp_packets, 0);
+ return;
+ }
+
+ /* No packets readily available for AFI/SAFI, are there
+ * subgroup packets
+ * that need to be generated? */
+ if (bpacket_queue_is_full(SUBGRP_INST(subgrp),
+ SUBGRP_PKTQ(subgrp))
+ || subgroup_packets_to_build(subgrp)) {
+ BGP_TIMER_ON(peer->t_generate_updgrp_packets,
+ bgp_generate_updgrp_packets, 0);
+ return;
+ }
+
+ afi = paf->afi;
+ safi = paf->safi;
+
+ /* No packets to send, see if EOR is pending */
+ if (CHECK_FLAG(peer->cap, PEER_CAP_RESTART_RCV)) {
+ if (!subgrp->t_coalesce && peer->afc_nego[afi][safi]
+ && peer->synctime
+ && !CHECK_FLAG(peer->af_sflags[afi][safi],
+ PEER_STATUS_EOR_SEND)
+ && safi != SAFI_MPLS_VPN) {
+ BGP_TIMER_ON(peer->t_generate_updgrp_packets,
+ bgp_generate_updgrp_packets, 0);
+ return;
+ }
+ }
+ }
+}
+
+/*
+ * Generate advertisement information (withdraws, updates, EOR) from each
+ * update group a peer belongs to, encode this information into packets, and
+ * enqueue the packets onto the peer's output buffer.
+ */
+void bgp_generate_updgrp_packets(struct thread *thread)
+{
+ struct peer *peer = THREAD_ARG(thread);
+
+ struct stream *s;
+ struct peer_af *paf;
+ struct bpacket *next_pkt;
+ uint32_t wpq;
+ uint32_t generated = 0;
+ afi_t afi;
+ safi_t safi;
+
+ wpq = atomic_load_explicit(&peer->bgp->wpkt_quanta,
+ memory_order_relaxed);
+
+ /*
+ * The code beyond this part deals with update packets, proceed only
+ * if peer is Established and updates are not on hold (as part of
+ * update-delay processing).
+ */
+ if (!peer_established(peer))
+ return;
+
+ if ((peer->bgp->main_peers_update_hold)
+ || bgp_update_delay_active(peer->bgp))
+ return;
+
+ if (peer->t_routeadv)
+ return;
+
+ do {
+ enum bgp_af_index index;
+
+ s = NULL;
+ for (index = BGP_AF_START; index < BGP_AF_MAX; index++) {
+ paf = peer->peer_af_array[index];
+ if (!paf || !PAF_SUBGRP(paf))
+ continue;
+
+ afi = paf->afi;
+ safi = paf->safi;
+ next_pkt = paf->next_pkt_to_send;
+
+ /*
+ * Try to generate a packet for the peer if we are at
+ * the end of the list. Always try to push out
+ * WITHDRAWs first.
+ */
+ if (!next_pkt || !next_pkt->buffer) {
+ next_pkt = subgroup_withdraw_packet(
+ PAF_SUBGRP(paf));
+ if (!next_pkt || !next_pkt->buffer)
+ subgroup_update_packet(PAF_SUBGRP(paf));
+ next_pkt = paf->next_pkt_to_send;
+ }
+
+ /*
+ * If we still don't have a packet to send to the peer,
+ * then try to find out out if we have to send eor or
+ * if not, skip to the next AFI, SAFI. Don't send the
+ * EOR prematurely; if the subgroup's coalesce timer is
+ * running, the adjacency-out structure is not created
+ * yet.
+ */
+ if (!next_pkt || !next_pkt->buffer) {
+ if (!paf->t_announce_route) {
+ /* Make sure we supress BGP UPDATES
+ * for normal processing later again.
+ */
+ UNSET_FLAG(paf->subgroup->sflags,
+ SUBGRP_STATUS_FORCE_UPDATES);
+
+ /* If route-refresh BoRR message was
+ * already sent and we are done with
+ * re-announcing tables for a decent
+ * afi/safi, we ready to send
+ * EoRR request.
+ */
+ if (CHECK_FLAG(
+ peer->af_sflags[afi][safi],
+ PEER_STATUS_BORR_SEND)) {
+ bgp_route_refresh_send(
+ peer, afi, safi, 0, 0,
+ 0,
+ BGP_ROUTE_REFRESH_EORR);
+
+ SET_FLAG(peer->af_sflags[afi]
+ [safi],
+ PEER_STATUS_EORR_SEND);
+ UNSET_FLAG(
+ peer->af_sflags[afi]
+ [safi],
+ PEER_STATUS_BORR_SEND);
+
+ if (bgp_debug_neighbor_events(
+ peer))
+ zlog_debug(
+ "%pBP sending route-refresh (EoRR) for %s/%s",
+ peer,
+ afi2str(afi),
+ safi2str(safi));
+ }
+ }
+
+ if (CHECK_FLAG(peer->cap,
+ PEER_CAP_RESTART_RCV)) {
+ if (!(PAF_SUBGRP(paf))->t_coalesce
+ && peer->afc_nego[afi][safi]
+ && peer->synctime
+ && !CHECK_FLAG(
+ peer->af_sflags[afi][safi],
+ PEER_STATUS_EOR_SEND)) {
+ /* If EOR is disabled,
+ * the message is not sent
+ */
+ if (BGP_SEND_EOR(peer->bgp, afi,
+ safi)) {
+ SET_FLAG(
+ peer->af_sflags
+ [afi]
+ [safi],
+ PEER_STATUS_EOR_SEND);
+
+ /* Update EOR
+ * send time
+ */
+ peer->eor_stime[afi]
+ [safi] =
+ monotime(NULL);
+
+ BGP_UPDATE_EOR_PKT(
+ peer, afi, safi,
+ s);
+ bgp_process_pending_refresh(
+ peer, afi,
+ safi);
+ }
+ }
+ }
+ continue;
+ }
+
+ /* Update packet send time */
+ peer->pkt_stime[afi][safi] = monotime(NULL);
+
+ /* Found a packet template to send, overwrite
+ * packet with appropriate attributes from peer
+ * and advance peer */
+ s = bpacket_reformat_for_peer(next_pkt, paf);
+ bgp_packet_add(peer, s);
+ bpacket_queue_advance_peer(paf);
+ }
+ } while (s && (++generated < wpq));
+
+ if (generated)
+ bgp_writes_on(peer);
+
+ bgp_write_proceed_actions(peer);
+}
+
+/*
+ * Creates a BGP Keepalive packet and appends it to the peer's output queue.
+ */
+void bgp_keepalive_send(struct peer *peer)
+{
+ struct stream *s;
+
+ s = stream_new(BGP_STANDARD_MESSAGE_MAX_PACKET_SIZE);
+
+ /* Make keepalive packet. */
+ bgp_packet_set_marker(s, BGP_MSG_KEEPALIVE);
+
+ /* Set packet size. */
+ bgp_packet_set_size(s);
+
+ /* Dump packet if debug option is set. */
+ /* bgp_packet_dump (s); */
+
+ if (bgp_debug_keepalive(peer))
+ zlog_debug("%s sending KEEPALIVE", peer->host);
+
+ /* Add packet to the peer. */
+ bgp_packet_add(peer, s);
+
+ bgp_writes_on(peer);
+}
+
+/*
+ * Creates a BGP Open packet and appends it to the peer's output queue.
+ * Sets capabilities as necessary.
+ */
+void bgp_open_send(struct peer *peer)
+{
+ struct stream *s;
+ uint16_t send_holdtime;
+ as_t local_as;
+
+ if (CHECK_FLAG(peer->flags, PEER_FLAG_TIMER))
+ send_holdtime = peer->holdtime;
+ else
+ send_holdtime = peer->bgp->default_holdtime;
+
+ /* local-as Change */
+ if (peer->change_local_as)
+ local_as = peer->change_local_as;
+ else
+ local_as = peer->local_as;
+
+ s = stream_new(BGP_STANDARD_MESSAGE_MAX_PACKET_SIZE);
+
+ /* Make open packet. */
+ bgp_packet_set_marker(s, BGP_MSG_OPEN);
+
+ /* Set open packet values. */
+ stream_putc(s, BGP_VERSION_4); /* BGP version */
+ stream_putw(s, (local_as <= BGP_AS_MAX) ? (uint16_t)local_as
+ : BGP_AS_TRANS);
+ stream_putw(s, send_holdtime); /* Hold Time */
+ stream_put_in_addr(s, &peer->local_id); /* BGP Identifier */
+
+ /* Set capabilities */
+ if (CHECK_FLAG(peer->flags, PEER_FLAG_EXTENDED_OPT_PARAMS)) {
+ (void)bgp_open_capability(s, peer, true);
+ } else {
+ struct stream *tmp = stream_new(STREAM_SIZE(s));
+
+ stream_copy(tmp, s);
+ if (bgp_open_capability(tmp, peer, false)
+ > BGP_OPEN_NON_EXT_OPT_LEN) {
+ stream_free(tmp);
+ (void)bgp_open_capability(s, peer, true);
+ } else {
+ stream_copy(s, tmp);
+ stream_free(tmp);
+ }
+ }
+
+ /* Set BGP packet length. */
+ bgp_packet_set_size(s);
+
+ if (bgp_debug_neighbor_events(peer))
+ zlog_debug(
+ "%s sending OPEN, version %d, my as %u, holdtime %d, id %pI4",
+ peer->host, BGP_VERSION_4, local_as, send_holdtime,
+ &peer->local_id);
+
+ /* Dump packet if debug option is set. */
+ /* bgp_packet_dump (s); */
+ hook_call(bgp_packet_send, peer, BGP_MSG_OPEN, stream_get_endp(s), s);
+
+ /* Add packet to the peer. */
+ bgp_packet_add(peer, s);
+
+ bgp_writes_on(peer);
+}
+
+/*
+ * Writes NOTIFICATION message directly to a peer socket without waiting for
+ * the I/O thread.
+ *
+ * There must be exactly one stream on the peer->obuf FIFO, and the data within
+ * this stream must match the format of a BGP NOTIFICATION message.
+ * Transmission is best-effort.
+ *
+ * @requires peer->io_mtx
+ * @param peer
+ * @return 0
+ */
+static void bgp_write_notify(struct peer *peer)
+{
+ int ret, val;
+ uint8_t type;
+ struct stream *s;
+
+ /* There should be at least one packet. */
+ s = stream_fifo_pop(peer->obuf);
+
+ if (!s)
+ return;
+
+ assert(stream_get_endp(s) >= BGP_HEADER_SIZE);
+
+ /*
+ * socket is in nonblocking mode, if we can't deliver the NOTIFY, well,
+ * we only care about getting a clean shutdown at this point.
+ */
+ ret = write(peer->fd, STREAM_DATA(s), stream_get_endp(s));
+
+ /*
+ * only connection reset/close gets counted as TCP_fatal_error, failure
+ * to write the entire NOTIFY doesn't get different FSM treatment
+ */
+ if (ret <= 0) {
+ stream_free(s);
+ BGP_EVENT_ADD(peer, TCP_fatal_error);
+ return;
+ }
+
+ /* Disable Nagle, make NOTIFY packet go out right away */
+ val = 1;
+ (void)setsockopt(peer->fd, IPPROTO_TCP, TCP_NODELAY, (char *)&val,
+ sizeof(val));
+
+ /* Retrieve BGP packet type. */
+ stream_set_getp(s, BGP_MARKER_SIZE + 2);
+ type = stream_getc(s);
+
+ assert(type == BGP_MSG_NOTIFY);
+
+ /* Type should be notify. */
+ atomic_fetch_add_explicit(&peer->notify_out, 1, memory_order_relaxed);
+
+ /* Double start timer. */
+ peer->v_start *= 2;
+
+ /* Overflow check. */
+ if (peer->v_start >= (60 * 2))
+ peer->v_start = (60 * 2);
+
+ /*
+ * Handle Graceful Restart case where the state changes to
+ * Connect instead of Idle
+ */
+ BGP_EVENT_ADD(peer, BGP_Stop);
+
+ stream_free(s);
+}
+
+/*
+ * Encapsulate an original BGP CEASE Notification into Hard Reset
+ */
+static uint8_t *bgp_notify_encapsulate_hard_reset(uint8_t code, uint8_t subcode,
+ uint8_t *data, size_t datalen)
+{
+ uint8_t *message = XCALLOC(MTYPE_BGP_NOTIFICATION, datalen + 2);
+
+ /* ErrCode */
+ message[0] = code;
+ /* Subcode */
+ message[1] = subcode;
+ /* Data */
+ if (datalen)
+ memcpy(message + 2, data, datalen);
+
+ return message;
+}
+
+/*
+ * Decapsulate an original BGP CEASE Notification from Hard Reset
+ */
+struct bgp_notify bgp_notify_decapsulate_hard_reset(struct bgp_notify *notify)
+{
+ struct bgp_notify bn = {};
+
+ bn.code = notify->raw_data[0];
+ bn.subcode = notify->raw_data[1];
+ bn.length = notify->length - 2;
+
+ bn.raw_data = XMALLOC(MTYPE_BGP_NOTIFICATION, bn.length);
+ memcpy(bn.raw_data, notify->raw_data + 2, bn.length);
+
+ return bn;
+}
+
+/* Check if Graceful-Restart N-bit is exchanged */
+bool bgp_has_graceful_restart_notification(struct peer *peer)
+{
+ return CHECK_FLAG(peer->cap, PEER_CAP_GRACEFUL_RESTART_N_BIT_RCV) &&
+ CHECK_FLAG(peer->cap, PEER_CAP_GRACEFUL_RESTART_N_BIT_ADV);
+}
+
+/*
+ * Check if to send BGP CEASE Notification/Hard Reset?
+ */
+bool bgp_notify_send_hard_reset(struct peer *peer, uint8_t code,
+ uint8_t subcode)
+{
+ /* When the "N" bit has been exchanged, a Hard Reset message is used to
+ * indicate to the peer that the session is to be fully terminated.
+ */
+ if (!bgp_has_graceful_restart_notification(peer))
+ return false;
+
+ /*
+ * https://datatracker.ietf.org/doc/html/rfc8538#section-5.1
+ */
+ if (code == BGP_NOTIFY_CEASE) {
+ switch (subcode) {
+ case BGP_NOTIFY_CEASE_MAX_PREFIX:
+ case BGP_NOTIFY_CEASE_ADMIN_SHUTDOWN:
+ case BGP_NOTIFY_CEASE_PEER_UNCONFIG:
+ case BGP_NOTIFY_CEASE_HARD_RESET:
+ case BGP_NOTIFY_CEASE_BFD_DOWN:
+ return true;
+ case BGP_NOTIFY_CEASE_ADMIN_RESET:
+ /* Provide user control:
+ * `bgp hard-adminstrative-reset`
+ */
+ if (CHECK_FLAG(peer->bgp->flags,
+ BGP_FLAG_HARD_ADMIN_RESET))
+ return true;
+ else
+ return false;
+ default:
+ break;
+ }
+ }
+
+ return false;
+}
+
+/*
+ * Check if received BGP CEASE Notification/Hard Reset?
+ */
+bool bgp_notify_received_hard_reset(struct peer *peer, uint8_t code,
+ uint8_t subcode)
+{
+ /* When the "N" bit has been exchanged, a Hard Reset message is used to
+ * indicate to the peer that the session is to be fully terminated.
+ */
+ if (!bgp_has_graceful_restart_notification(peer))
+ return false;
+
+ if (code == BGP_NOTIFY_CEASE && subcode == BGP_NOTIFY_CEASE_HARD_RESET)
+ return true;
+
+ return false;
+}
+
+/*
+ * Creates a BGP Notify and appends it to the peer's output queue.
+ *
+ * This function attempts to write the packet from the thread it is called
+ * from, to ensure the packet gets out ASAP.
+ *
+ * This function may be called from multiple threads. Since the function
+ * modifies I/O buffer(s) in the peer, these are locked for the duration of the
+ * call to prevent tampering from other threads.
+ *
+ * Delivery of the NOTIFICATION is attempted once and is best-effort. After
+ * return, the peer structure *must* be reset; no assumptions about session
+ * state are valid.
+ *
+ * @param peer
+ * @param code BGP error code
+ * @param sub_code BGP error subcode
+ * @param data Data portion
+ * @param datalen length of data portion
+ */
+static void bgp_notify_send_internal(struct peer *peer, uint8_t code,
+ uint8_t sub_code, uint8_t *data,
+ size_t datalen, bool use_curr)
+{
+ struct stream *s;
+ bool hard_reset = bgp_notify_send_hard_reset(peer, code, sub_code);
+
+ /* Lock I/O mutex to prevent other threads from pushing packets */
+ frr_mutex_lock_autounlock(&peer->io_mtx);
+ /* ============================================== */
+
+ /* Allocate new stream. */
+ s = stream_new(peer->max_packet_size);
+
+ /* Make notify packet. */
+ bgp_packet_set_marker(s, BGP_MSG_NOTIFY);
+
+ /* Check if we should send Hard Reset Notification or not */
+ if (hard_reset) {
+ uint8_t *hard_reset_message = bgp_notify_encapsulate_hard_reset(
+ code, sub_code, data, datalen);
+
+ /* Hard Reset encapsulates another NOTIFICATION message
+ * in its data portion.
+ */
+ stream_putc(s, BGP_NOTIFY_CEASE);
+ stream_putc(s, BGP_NOTIFY_CEASE_HARD_RESET);
+ stream_write(s, hard_reset_message, datalen + 2);
+
+ XFREE(MTYPE_BGP_NOTIFICATION, hard_reset_message);
+ } else {
+ stream_putc(s, code);
+ stream_putc(s, sub_code);
+ if (data)
+ stream_write(s, data, datalen);
+ }
+
+ /* Set BGP packet length. */
+ bgp_packet_set_size(s);
+
+ /* wipe output buffer */
+ stream_fifo_clean(peer->obuf);
+
+ /*
+ * If possible, store last packet for debugging purposes. This check is
+ * in place because we are sometimes called with a doppelganger peer,
+ * who tends to have a plethora of fields nulled out.
+ *
+ * Some callers should not attempt this - the io pthread for example
+ * should not touch internals of the peer struct.
+ */
+ if (use_curr && peer->curr) {
+ size_t packetsize = stream_get_endp(peer->curr);
+ assert(packetsize <= peer->max_packet_size);
+ memcpy(peer->last_reset_cause, peer->curr->data, packetsize);
+ peer->last_reset_cause_size = packetsize;
+ }
+
+ /* For debug */
+ {
+ struct bgp_notify bgp_notify;
+ int first = 0;
+ int i;
+ char c[4];
+
+ bgp_notify.code = code;
+ bgp_notify.subcode = sub_code;
+ bgp_notify.data = NULL;
+ bgp_notify.length = datalen;
+ bgp_notify.raw_data = data;
+
+ peer->notify.code = bgp_notify.code;
+ peer->notify.subcode = bgp_notify.subcode;
+
+ if (bgp_notify.length && data) {
+ bgp_notify.data =
+ XMALLOC(MTYPE_TMP, bgp_notify.length * 3);
+ for (i = 0; i < bgp_notify.length; i++)
+ if (first) {
+ snprintf(c, sizeof(c), " %02x",
+ data[i]);
+
+ strlcat(bgp_notify.data, c,
+ bgp_notify.length);
+
+ } else {
+ first = 1;
+ snprintf(c, sizeof(c), "%02x", data[i]);
+
+ strlcpy(bgp_notify.data, c,
+ bgp_notify.length);
+ }
+ }
+ bgp_notify_print(peer, &bgp_notify, "sending", hard_reset);
+
+ if (bgp_notify.data) {
+ XFREE(MTYPE_TMP, bgp_notify.data);
+ bgp_notify.length = 0;
+ }
+ }
+
+ /* peer reset cause */
+ if (code == BGP_NOTIFY_CEASE) {
+ if (sub_code == BGP_NOTIFY_CEASE_ADMIN_RESET)
+ peer->last_reset = PEER_DOWN_USER_RESET;
+ else if (sub_code == BGP_NOTIFY_CEASE_ADMIN_SHUTDOWN)
+ peer->last_reset = PEER_DOWN_USER_SHUTDOWN;
+ else
+ peer->last_reset = PEER_DOWN_NOTIFY_SEND;
+ } else
+ peer->last_reset = PEER_DOWN_NOTIFY_SEND;
+
+ /* Add packet to peer's output queue */
+ stream_fifo_push(peer->obuf, s);
+
+ bgp_peer_gr_flags_update(peer);
+ BGP_GR_ROUTER_DETECT_AND_SEND_CAPABILITY_TO_ZEBRA(peer->bgp,
+ peer->bgp->peer);
+
+ bgp_write_notify(peer);
+}
+
+/*
+ * Creates a BGP Notify and appends it to the peer's output queue.
+ *
+ * This function attempts to write the packet from the thread it is called
+ * from, to ensure the packet gets out ASAP.
+ *
+ * @param peer
+ * @param code BGP error code
+ * @param sub_code BGP error subcode
+ */
+void bgp_notify_send(struct peer *peer, uint8_t code, uint8_t sub_code)
+{
+ bgp_notify_send_internal(peer, code, sub_code, NULL, 0, true);
+}
+
+/*
+ * Enqueue notification; called from the main pthread, peer object access is ok.
+ */
+void bgp_notify_send_with_data(struct peer *peer, uint8_t code,
+ uint8_t sub_code, uint8_t *data, size_t datalen)
+{
+ bgp_notify_send_internal(peer, code, sub_code, data, datalen, true);
+}
+
+/*
+ * For use by the io pthread, queueing a notification but avoiding access to
+ * the peer object.
+ */
+void bgp_notify_io_invalid(struct peer *peer, uint8_t code, uint8_t sub_code,
+ uint8_t *data, size_t datalen)
+{
+ /* Avoid touching the peer object */
+ bgp_notify_send_internal(peer, code, sub_code, data, datalen, false);
+}
+
+/*
+ * Creates BGP Route Refresh packet and appends it to the peer's output queue.
+ *
+ * @param peer
+ * @param afi Address Family Identifier
+ * @param safi Subsequent Address Family Identifier
+ * @param orf_type Outbound Route Filtering type
+ * @param when_to_refresh Whether to refresh immediately or defer
+ * @param remove Whether to remove ORF for specified AFI/SAFI
+ */
+void bgp_route_refresh_send(struct peer *peer, afi_t afi, safi_t safi,
+ uint8_t orf_type, uint8_t when_to_refresh,
+ int remove, uint8_t subtype)
+{
+ struct stream *s;
+ struct bgp_filter *filter;
+ int orf_refresh = 0;
+ iana_afi_t pkt_afi = IANA_AFI_IPV4;
+ iana_safi_t pkt_safi = IANA_SAFI_UNICAST;
+
+ if (DISABLE_BGP_ANNOUNCE)
+ return;
+
+ filter = &peer->filter[afi][safi];
+
+ /* Convert AFI, SAFI to values for packet. */
+ bgp_map_afi_safi_int2iana(afi, safi, &pkt_afi, &pkt_safi);
+
+ s = stream_new(peer->max_packet_size);
+
+ /* Make BGP update packet. */
+ if (CHECK_FLAG(peer->cap, PEER_CAP_REFRESH_NEW_RCV))
+ bgp_packet_set_marker(s, BGP_MSG_ROUTE_REFRESH_NEW);
+ else
+ bgp_packet_set_marker(s, BGP_MSG_ROUTE_REFRESH_OLD);
+
+ /* Encode Route Refresh message. */
+ stream_putw(s, pkt_afi);
+ if (subtype)
+ stream_putc(s, subtype);
+ else
+ stream_putc(s, 0);
+ stream_putc(s, pkt_safi);
+
+ if (orf_type == ORF_TYPE_PREFIX || orf_type == ORF_TYPE_PREFIX_OLD)
+ if (remove || filter->plist[FILTER_IN].plist) {
+ uint16_t orf_len;
+ unsigned long orfp;
+
+ orf_refresh = 1;
+ stream_putc(s, when_to_refresh);
+ stream_putc(s, orf_type);
+ orfp = stream_get_endp(s);
+ stream_putw(s, 0);
+
+ if (remove) {
+ UNSET_FLAG(peer->af_sflags[afi][safi],
+ PEER_STATUS_ORF_PREFIX_SEND);
+ stream_putc(s, ORF_COMMON_PART_REMOVE_ALL);
+ if (bgp_debug_neighbor_events(peer))
+ zlog_debug(
+ "%pBP sending REFRESH_REQ to remove ORF(%d) (%s) for afi/safi: %s/%s",
+ peer, orf_type,
+ (when_to_refresh ==
+ REFRESH_DEFER
+ ? "defer"
+ : "immediate"),
+ iana_afi2str(pkt_afi),
+ iana_safi2str(pkt_safi));
+ } else {
+ SET_FLAG(peer->af_sflags[afi][safi],
+ PEER_STATUS_ORF_PREFIX_SEND);
+ prefix_bgp_orf_entry(
+ s, filter->plist[FILTER_IN].plist,
+ ORF_COMMON_PART_ADD,
+ ORF_COMMON_PART_PERMIT,
+ ORF_COMMON_PART_DENY);
+ if (bgp_debug_neighbor_events(peer))
+ zlog_debug(
+ "%pBP sending REFRESH_REQ with pfxlist ORF(%d) (%s) for afi/safi: %s/%s",
+ peer, orf_type,
+ (when_to_refresh ==
+ REFRESH_DEFER
+ ? "defer"
+ : "immediate"),
+ iana_afi2str(pkt_afi),
+ iana_safi2str(pkt_safi));
+ }
+
+ /* Total ORF Entry Len. */
+ orf_len = stream_get_endp(s) - orfp - 2;
+ stream_putw_at(s, orfp, orf_len);
+ }
+
+ /* Set packet size. */
+ bgp_packet_set_size(s);
+
+ if (bgp_debug_neighbor_events(peer)) {
+ if (!orf_refresh)
+ zlog_debug(
+ "%pBP sending REFRESH_REQ for afi/safi: %s/%s",
+ peer, iana_afi2str(pkt_afi),
+ iana_safi2str(pkt_safi));
+ }
+
+ /* Add packet to the peer. */
+ bgp_packet_add(peer, s);
+
+ bgp_writes_on(peer);
+}
+
+/*
+ * Create a BGP Capability packet and append it to the peer's output queue.
+ *
+ * @param peer
+ * @param afi Address Family Identifier
+ * @param safi Subsequent Address Family Identifier
+ * @param capability_code BGP Capability Code
+ * @param action Set or Remove capability
+ */
+void bgp_capability_send(struct peer *peer, afi_t afi, safi_t safi,
+ int capability_code, int action)
+{
+ struct stream *s;
+ iana_afi_t pkt_afi = IANA_AFI_IPV4;
+ iana_safi_t pkt_safi = IANA_SAFI_UNICAST;
+
+ /* Convert AFI, SAFI to values for packet. */
+ bgp_map_afi_safi_int2iana(afi, safi, &pkt_afi, &pkt_safi);
+
+ s = stream_new(peer->max_packet_size);
+
+ /* Make BGP update packet. */
+ bgp_packet_set_marker(s, BGP_MSG_CAPABILITY);
+
+ /* Encode MP_EXT capability. */
+ if (capability_code == CAPABILITY_CODE_MP) {
+ stream_putc(s, action);
+ stream_putc(s, CAPABILITY_CODE_MP);
+ stream_putc(s, CAPABILITY_CODE_MP_LEN);
+ stream_putw(s, pkt_afi);
+ stream_putc(s, 0);
+ stream_putc(s, pkt_safi);
+
+ if (bgp_debug_neighbor_events(peer))
+ zlog_debug(
+ "%pBP sending CAPABILITY has %s MP_EXT CAP for afi/safi: %s/%s",
+ peer,
+ action == CAPABILITY_ACTION_SET ? "Advertising"
+ : "Removing",
+ iana_afi2str(pkt_afi), iana_safi2str(pkt_safi));
+ }
+
+ /* Set packet size. */
+ bgp_packet_set_size(s);
+
+ /* Add packet to the peer. */
+ bgp_packet_add(peer, s);
+
+ bgp_writes_on(peer);
+}
+
+/* RFC1771 6.8 Connection collision detection. */
+static int bgp_collision_detect(struct peer *new, struct in_addr remote_id)
+{
+ struct peer *peer;
+
+ /*
+ * Upon receipt of an OPEN message, the local system must examine
+ * all of its connections that are in the OpenConfirm state. A BGP
+ * speaker may also examine connections in an OpenSent state if it
+ * knows the BGP Identifier of the peer by means outside of the
+ * protocol. If among these connections there is a connection to a
+ * remote BGP speaker whose BGP Identifier equals the one in the
+ * OPEN message, then the local system performs the following
+ * collision resolution procedure:
+ */
+ peer = new->doppelganger;
+ if (peer == NULL)
+ return 0;
+
+ /*
+ * Do not accept the new connection in Established or Clearing
+ * states. Note that a peer GR is handled by closing the existing
+ * connection upon receipt of new one.
+ */
+ if (peer_established(peer) || peer->status == Clearing) {
+ bgp_notify_send(new, BGP_NOTIFY_CEASE,
+ BGP_NOTIFY_CEASE_COLLISION_RESOLUTION);
+ return -1;
+ }
+
+ if ((peer->status != OpenConfirm) && (peer->status != OpenSent))
+ return 0;
+
+ /*
+ * 1. The BGP Identifier of the local system is
+ * compared to the BGP Identifier of the remote
+ * system (as specified in the OPEN message).
+ *
+ * If the BGP Identifiers of the peers
+ * involved in the connection collision
+ * are identical, then the connection
+ * initiated by the BGP speaker with the
+ * larger AS number is preserved.
+ */
+ if (ntohl(peer->local_id.s_addr) < ntohl(remote_id.s_addr)
+ || (ntohl(peer->local_id.s_addr) == ntohl(remote_id.s_addr)
+ && peer->local_as < peer->as))
+ if (!CHECK_FLAG(peer->sflags, PEER_STATUS_ACCEPT_PEER)) {
+ /*
+ * 2. If the value of the local BGP
+ * Identifier is less than the remote one,
+ * the local system closes BGP connection
+ * that already exists (the one that is
+ * already in the OpenConfirm state),
+ * and accepts BGP connection initiated by
+ * the remote system.
+ */
+ bgp_notify_send(peer, BGP_NOTIFY_CEASE,
+ BGP_NOTIFY_CEASE_COLLISION_RESOLUTION);
+ return 1;
+ } else {
+ bgp_notify_send(new, BGP_NOTIFY_CEASE,
+ BGP_NOTIFY_CEASE_COLLISION_RESOLUTION);
+ return -1;
+ }
+ else {
+ if (ntohl(peer->local_id.s_addr) == ntohl(remote_id.s_addr)
+ && peer->local_as == peer->as)
+ flog_err(EC_BGP_ROUTER_ID_SAME,
+ "Peer's router-id %pI4 is the same as ours",
+ &remote_id);
+
+ /*
+ * 3. Otherwise, the local system closes newly
+ * created BGP connection (the one associated with the
+ * newly received OPEN message), and continues to use
+ * the existing one (the one that is already in the
+ * OpenConfirm state).
+ */
+ if (CHECK_FLAG(peer->sflags, PEER_STATUS_ACCEPT_PEER)) {
+ bgp_notify_send(peer, BGP_NOTIFY_CEASE,
+ BGP_NOTIFY_CEASE_COLLISION_RESOLUTION);
+ return 1;
+ } else {
+ bgp_notify_send(new, BGP_NOTIFY_CEASE,
+ BGP_NOTIFY_CEASE_COLLISION_RESOLUTION);
+ return -1;
+ }
+ }
+}
+
+/* Packet processing routines ---------------------------------------------- */
+/*
+ * This is a family of functions designed to be called from
+ * bgp_process_packet(). These functions all share similar behavior and should
+ * adhere to the following invariants and restrictions:
+ *
+ * Return codes
+ * ------------
+ * The return code of any one of those functions should be one of the FSM event
+ * codes specified in bgpd.h. If a NOTIFY was sent, this event code MUST be
+ * BGP_Stop. Otherwise, the code SHOULD correspond to the function's expected
+ * packet type. For example, bgp_open_receive() should return BGP_Stop upon
+ * error and Receive_OPEN_message otherwise.
+ *
+ * If no action is necessary, the correct return code is BGP_PACKET_NOOP as
+ * defined below.
+ *
+ * Side effects
+ * ------------
+ * - May send NOTIFY messages
+ * - May not modify peer->status
+ * - May not call bgp_event_update()
+ */
+
+#define BGP_PACKET_NOOP 0
+
+/**
+ * Process BGP OPEN message for peer.
+ *
+ * If any errors are encountered in the OPEN message, immediately sends NOTIFY
+ * and returns BGP_Stop.
+ *
+ * @param peer
+ * @param size size of the packet
+ * @return as in summary
+ */
+static int bgp_open_receive(struct peer *peer, bgp_size_t size)
+{
+ int ret;
+ uint8_t version;
+ uint16_t optlen;
+ uint16_t holdtime;
+ uint16_t send_holdtime;
+ as_t remote_as;
+ as_t as4 = 0, as4_be;
+ struct in_addr remote_id;
+ int mp_capability;
+ uint8_t notify_data_remote_as[2];
+ uint8_t notify_data_remote_as4[4];
+ uint8_t notify_data_remote_id[4];
+ uint16_t *holdtime_ptr;
+
+ /* Parse open packet. */
+ version = stream_getc(peer->curr);
+ memcpy(notify_data_remote_as, stream_pnt(peer->curr), 2);
+ remote_as = stream_getw(peer->curr);
+ holdtime_ptr = (uint16_t *)stream_pnt(peer->curr);
+ holdtime = stream_getw(peer->curr);
+ memcpy(notify_data_remote_id, stream_pnt(peer->curr), 4);
+ remote_id.s_addr = stream_get_ipv4(peer->curr);
+
+ /* BEGIN to read the capability here, but dont do it yet */
+ mp_capability = 0;
+ optlen = stream_getc(peer->curr);
+
+ /* Extended Optional Parameters Length for BGP OPEN Message */
+ if (optlen == BGP_OPEN_NON_EXT_OPT_LEN
+ || CHECK_FLAG(peer->flags, PEER_FLAG_EXTENDED_OPT_PARAMS)) {
+ uint8_t opttype;
+
+ if (STREAM_READABLE(peer->curr) < 1) {
+ flog_err(
+ EC_BGP_PKT_OPEN,
+ "%s: stream does not have enough bytes for extended optional parameters",
+ peer->host);
+ bgp_notify_send(peer, BGP_NOTIFY_OPEN_ERR,
+ BGP_NOTIFY_OPEN_MALFORMED_ATTR);
+ return BGP_Stop;
+ }
+
+ opttype = stream_getc(peer->curr);
+ if (opttype == BGP_OPEN_NON_EXT_OPT_TYPE_EXTENDED_LENGTH) {
+ if (STREAM_READABLE(peer->curr) < 2) {
+ flog_err(
+ EC_BGP_PKT_OPEN,
+ "%s: stream does not have enough bytes to read the extended optional parameters optlen",
+ peer->host);
+ bgp_notify_send(peer, BGP_NOTIFY_OPEN_ERR,
+ BGP_NOTIFY_OPEN_MALFORMED_ATTR);
+ return BGP_Stop;
+ }
+ optlen = stream_getw(peer->curr);
+ SET_FLAG(peer->sflags,
+ PEER_STATUS_EXT_OPT_PARAMS_LENGTH);
+ }
+ }
+
+ /* Receive OPEN message log */
+ if (bgp_debug_neighbor_events(peer))
+ zlog_debug(
+ "%s rcv OPEN%s, version %d, remote-as (in open) %u, holdtime %d, id %pI4",
+ peer->host,
+ CHECK_FLAG(peer->sflags,
+ PEER_STATUS_EXT_OPT_PARAMS_LENGTH)
+ ? " (Extended)"
+ : "",
+ version, remote_as, holdtime, &remote_id);
+
+ if (optlen != 0) {
+ /* If not enough bytes, it is an error. */
+ if (STREAM_READABLE(peer->curr) < optlen) {
+ flog_err(EC_BGP_PKT_OPEN,
+ "%s: stream has not enough bytes (%u)",
+ peer->host, optlen);
+ bgp_notify_send(peer, BGP_NOTIFY_OPEN_ERR,
+ BGP_NOTIFY_OPEN_MALFORMED_ATTR);
+ return BGP_Stop;
+ }
+
+ /* We need the as4 capability value *right now* because
+ * if it is there, we have not got the remote_as yet, and
+ * without
+ * that we do not know which peer is connecting to us now.
+ */
+ as4 = peek_for_as4_capability(peer, optlen);
+ }
+
+ as4_be = htonl(as4);
+ memcpy(notify_data_remote_as4, &as4_be, 4);
+
+ /* Just in case we have a silly peer who sends AS4 capability set to 0
+ */
+ if (CHECK_FLAG(peer->cap, PEER_CAP_AS4_RCV) && !as4) {
+ flog_err(EC_BGP_PKT_OPEN,
+ "%s bad OPEN, got AS4 capability, but AS4 set to 0",
+ peer->host);
+ bgp_notify_send_with_data(peer, BGP_NOTIFY_OPEN_ERR,
+ BGP_NOTIFY_OPEN_BAD_PEER_AS,
+ notify_data_remote_as4, 4);
+ return BGP_Stop;
+ }
+
+ /* Codification of AS 0 Processing */
+ if (remote_as == BGP_AS_ZERO) {
+ flog_err(EC_BGP_PKT_OPEN, "%s bad OPEN, got AS set to 0",
+ peer->host);
+ bgp_notify_send(peer, BGP_NOTIFY_OPEN_ERR,
+ BGP_NOTIFY_OPEN_BAD_PEER_AS);
+ return BGP_Stop;
+ }
+
+ if (remote_as == BGP_AS_TRANS) {
+ /* Take the AS4 from the capability. We must have received the
+ * capability now! Otherwise we have a asn16 peer who uses
+ * BGP_AS_TRANS, for some unknown reason.
+ */
+ if (as4 == BGP_AS_TRANS) {
+ flog_err(
+ EC_BGP_PKT_OPEN,
+ "%s [AS4] NEW speaker using AS_TRANS for AS4, not allowed",
+ peer->host);
+ bgp_notify_send_with_data(peer, BGP_NOTIFY_OPEN_ERR,
+ BGP_NOTIFY_OPEN_BAD_PEER_AS,
+ notify_data_remote_as4, 4);
+ return BGP_Stop;
+ }
+
+ if (!as4 && BGP_DEBUG(as4, AS4))
+ zlog_debug(
+ "%s [AS4] OPEN remote_as is AS_TRANS, but no AS4. Odd, but proceeding.",
+ peer->host);
+ else if (as4 < BGP_AS_MAX && BGP_DEBUG(as4, AS4))
+ zlog_debug(
+ "%s [AS4] OPEN remote_as is AS_TRANS, but AS4 (%u) fits in 2-bytes, very odd peer.",
+ peer->host, as4);
+ if (as4)
+ remote_as = as4;
+ } else {
+ /* We may have a partner with AS4 who has an asno < BGP_AS_MAX
+ */
+ /* If we have got the capability, peer->as4cap must match
+ * remote_as */
+ if (CHECK_FLAG(peer->cap, PEER_CAP_AS4_RCV)
+ && as4 != remote_as) {
+ /* raise error, log this, close session */
+ flog_err(
+ EC_BGP_PKT_OPEN,
+ "%s bad OPEN, got AS4 capability, but remote_as %u mismatch with 16bit 'myasn' %u in open",
+ peer->host, as4, remote_as);
+ bgp_notify_send_with_data(peer, BGP_NOTIFY_OPEN_ERR,
+ BGP_NOTIFY_OPEN_BAD_PEER_AS,
+ notify_data_remote_as4, 4);
+ return BGP_Stop;
+ }
+ }
+
+ /* rfc6286:
+ * If the BGP Identifier field of the OPEN message
+ * is zero, or if it is the same as the BGP Identifier
+ * of the local BGP speaker and the message is from an
+ * internal peer, then the Error Subcode is set to
+ * "Bad BGP Identifier".
+ */
+ if (remote_id.s_addr == INADDR_ANY
+ || (peer->sort == BGP_PEER_IBGP
+ && ntohl(peer->local_id.s_addr) == ntohl(remote_id.s_addr))) {
+ if (bgp_debug_neighbor_events(peer))
+ zlog_debug("%s bad OPEN, wrong router identifier %pI4",
+ peer->host, &remote_id);
+ bgp_notify_send_with_data(peer, BGP_NOTIFY_OPEN_ERR,
+ BGP_NOTIFY_OPEN_BAD_BGP_IDENT,
+ notify_data_remote_id, 4);
+ return BGP_Stop;
+ }
+
+ /* Peer BGP version check. */
+ if (version != BGP_VERSION_4) {
+ uint16_t maxver = htons(BGP_VERSION_4);
+ /* XXX this reply may not be correct if version < 4 XXX */
+ if (bgp_debug_neighbor_events(peer))
+ zlog_debug(
+ "%s bad protocol version, remote requested %d, local request %d",
+ peer->host, version, BGP_VERSION_4);
+ /* Data must be in network byte order here */
+ bgp_notify_send_with_data(peer, BGP_NOTIFY_OPEN_ERR,
+ BGP_NOTIFY_OPEN_UNSUP_VERSION,
+ (uint8_t *)&maxver, 2);
+ return BGP_Stop;
+ }
+
+ /* Check neighbor as number. */
+ if (peer->as_type == AS_UNSPECIFIED) {
+ if (bgp_debug_neighbor_events(peer))
+ zlog_debug(
+ "%s bad OPEN, remote AS is unspecified currently",
+ peer->host);
+ bgp_notify_send_with_data(peer, BGP_NOTIFY_OPEN_ERR,
+ BGP_NOTIFY_OPEN_BAD_PEER_AS,
+ notify_data_remote_as, 2);
+ return BGP_Stop;
+ } else if (peer->as_type == AS_INTERNAL) {
+ if (remote_as != peer->bgp->as) {
+ if (bgp_debug_neighbor_events(peer))
+ zlog_debug(
+ "%s bad OPEN, remote AS is %u, internal specified",
+ peer->host, remote_as);
+ bgp_notify_send_with_data(peer, BGP_NOTIFY_OPEN_ERR,
+ BGP_NOTIFY_OPEN_BAD_PEER_AS,
+ notify_data_remote_as, 2);
+ return BGP_Stop;
+ }
+ peer->as = peer->local_as;
+ } else if (peer->as_type == AS_EXTERNAL) {
+ if (remote_as == peer->bgp->as) {
+ if (bgp_debug_neighbor_events(peer))
+ zlog_debug(
+ "%s bad OPEN, remote AS is %u, external specified",
+ peer->host, remote_as);
+ bgp_notify_send_with_data(peer, BGP_NOTIFY_OPEN_ERR,
+ BGP_NOTIFY_OPEN_BAD_PEER_AS,
+ notify_data_remote_as, 2);
+ return BGP_Stop;
+ }
+ peer->as = remote_as;
+ } else if ((peer->as_type == AS_SPECIFIED) && (remote_as != peer->as)) {
+ if (bgp_debug_neighbor_events(peer))
+ zlog_debug("%s bad OPEN, remote AS is %u, expected %u",
+ peer->host, remote_as, peer->as);
+ bgp_notify_send_with_data(peer, BGP_NOTIFY_OPEN_ERR,
+ BGP_NOTIFY_OPEN_BAD_PEER_AS,
+ notify_data_remote_as, 2);
+ return BGP_Stop;
+ }
+
+ /*
+ * When collision is detected and this peer is closed.
+ * Return immediately.
+ */
+ ret = bgp_collision_detect(peer, remote_id);
+ if (ret < 0)
+ return BGP_Stop;
+
+ /* Get sockname. */
+ if (bgp_getsockname(peer) < 0) {
+ flog_err_sys(EC_LIB_SOCKET,
+ "%s: bgp_getsockname() failed for peer: %s",
+ __func__, peer->host);
+ return BGP_Stop;
+ }
+
+ /* Set remote router-id */
+ peer->remote_id = remote_id;
+
+ /* From the rfc: Upon receipt of an OPEN message, a BGP speaker MUST
+ calculate the value of the Hold Timer by using the smaller of its
+ configured Hold Time and the Hold Time received in the OPEN message.
+ The Hold Time MUST be either zero or at least three seconds. An
+ implementation may reject connections on the basis of the Hold Time.
+ */
+
+ if (holdtime < 3 && holdtime != 0) {
+ bgp_notify_send_with_data(peer, BGP_NOTIFY_OPEN_ERR,
+ BGP_NOTIFY_OPEN_UNACEP_HOLDTIME,
+ (uint8_t *)holdtime_ptr, 2);
+ return BGP_Stop;
+ }
+
+ /* Send notification message when Hold Time received in the OPEN message
+ * is smaller than configured minimum Hold Time. */
+ if (holdtime < peer->bgp->default_min_holdtime
+ && peer->bgp->default_min_holdtime != 0) {
+ bgp_notify_send_with_data(peer, BGP_NOTIFY_OPEN_ERR,
+ BGP_NOTIFY_OPEN_UNACEP_HOLDTIME,
+ (uint8_t *)holdtime_ptr, 2);
+ return BGP_Stop;
+ }
+
+ /* From the rfc: A reasonable maximum time between KEEPALIVE messages
+ would be one third of the Hold Time interval. KEEPALIVE messages
+ MUST NOT be sent more frequently than one per second. An
+ implementation MAY adjust the rate at which it sends KEEPALIVE
+ messages as a function of the Hold Time interval. */
+
+ if (CHECK_FLAG(peer->flags, PEER_FLAG_TIMER))
+ send_holdtime = peer->holdtime;
+ else
+ send_holdtime = peer->bgp->default_holdtime;
+
+ if (holdtime < send_holdtime)
+ peer->v_holdtime = holdtime;
+ else
+ peer->v_holdtime = send_holdtime;
+
+ /* Set effective keepalive to 1/3 the effective holdtime.
+ * Use configured keeplive when < effective keepalive.
+ */
+ peer->v_keepalive = peer->v_holdtime / 3;
+ if (CHECK_FLAG(peer->flags, PEER_FLAG_TIMER)) {
+ if (peer->keepalive && peer->keepalive < peer->v_keepalive)
+ peer->v_keepalive = peer->keepalive;
+ } else {
+ if (peer->bgp->default_keepalive
+ && peer->bgp->default_keepalive < peer->v_keepalive)
+ peer->v_keepalive = peer->bgp->default_keepalive;
+ }
+
+ /* Open option part parse. */
+ if (optlen != 0) {
+ if (bgp_open_option_parse(peer, optlen, &mp_capability) < 0)
+ return BGP_Stop;
+ } else {
+ if (bgp_debug_neighbor_events(peer))
+ zlog_debug("%s rcvd OPEN w/ OPTION parameter len: 0",
+ peer->host);
+ }
+
+ /*
+ * Assume that the peer supports the locally configured set of
+ * AFI/SAFIs if the peer did not send us any Mulitiprotocol
+ * capabilities, or if 'override-capability' is configured.
+ */
+ if (!mp_capability
+ || CHECK_FLAG(peer->flags, PEER_FLAG_OVERRIDE_CAPABILITY)) {
+ peer->afc_nego[AFI_IP][SAFI_UNICAST] =
+ peer->afc[AFI_IP][SAFI_UNICAST];
+ peer->afc_nego[AFI_IP][SAFI_MULTICAST] =
+ peer->afc[AFI_IP][SAFI_MULTICAST];
+ peer->afc_nego[AFI_IP][SAFI_LABELED_UNICAST] =
+ peer->afc[AFI_IP][SAFI_LABELED_UNICAST];
+ peer->afc_nego[AFI_IP][SAFI_FLOWSPEC] =
+ peer->afc[AFI_IP][SAFI_FLOWSPEC];
+ peer->afc_nego[AFI_IP6][SAFI_UNICAST] =
+ peer->afc[AFI_IP6][SAFI_UNICAST];
+ peer->afc_nego[AFI_IP6][SAFI_MULTICAST] =
+ peer->afc[AFI_IP6][SAFI_MULTICAST];
+ peer->afc_nego[AFI_IP6][SAFI_LABELED_UNICAST] =
+ peer->afc[AFI_IP6][SAFI_LABELED_UNICAST];
+ peer->afc_nego[AFI_L2VPN][SAFI_EVPN] =
+ peer->afc[AFI_L2VPN][SAFI_EVPN];
+ peer->afc_nego[AFI_IP6][SAFI_FLOWSPEC] =
+ peer->afc[AFI_IP6][SAFI_FLOWSPEC];
+ }
+
+ /* Verify valid local address present based on negotiated
+ * address-families. */
+ if (peer->afc_nego[AFI_IP][SAFI_UNICAST]
+ || peer->afc_nego[AFI_IP][SAFI_LABELED_UNICAST]
+ || peer->afc_nego[AFI_IP][SAFI_MULTICAST]
+ || peer->afc_nego[AFI_IP][SAFI_MPLS_VPN]
+ || peer->afc_nego[AFI_IP][SAFI_ENCAP]) {
+ if (peer->nexthop.v4.s_addr == INADDR_ANY) {
+#if defined(HAVE_CUMULUS)
+ zlog_warn("%s: No local IPv4 addr, BGP routing may not work",
+ peer->host);
+#endif
+ }
+ }
+ if (peer->afc_nego[AFI_IP6][SAFI_UNICAST]
+ || peer->afc_nego[AFI_IP6][SAFI_LABELED_UNICAST]
+ || peer->afc_nego[AFI_IP6][SAFI_MULTICAST]
+ || peer->afc_nego[AFI_IP6][SAFI_MPLS_VPN]
+ || peer->afc_nego[AFI_IP6][SAFI_ENCAP]) {
+ if (IN6_IS_ADDR_UNSPECIFIED(&peer->nexthop.v6_global)) {
+#if defined(HAVE_CUMULUS)
+ zlog_warn("%s: No local IPv6 address, BGP routing may not work",
+ peer->host);
+#endif
+ }
+ }
+ peer->rtt = sockopt_tcp_rtt(peer->fd);
+
+ return Receive_OPEN_message;
+}
+
+/**
+ * Process BGP KEEPALIVE message for peer.
+ *
+ * @param peer
+ * @param size size of the packet
+ * @return as in summary
+ */
+static int bgp_keepalive_receive(struct peer *peer, bgp_size_t size)
+{
+ if (bgp_debug_keepalive(peer))
+ zlog_debug("%s KEEPALIVE rcvd", peer->host);
+
+ bgp_update_implicit_eors(peer);
+
+ peer->rtt = sockopt_tcp_rtt(peer->fd);
+
+ /* If the peer's RTT is higher than expected, shutdown
+ * the peer automatically.
+ */
+ if (CHECK_FLAG(peer->flags, PEER_FLAG_RTT_SHUTDOWN)
+ && peer->rtt > peer->rtt_expected) {
+
+ peer->rtt_keepalive_rcv++;
+
+ if (peer->rtt_keepalive_rcv > peer->rtt_keepalive_conf) {
+ zlog_warn(
+ "%s shutdown due to high round-trip-time (%dms > %dms)",
+ peer->host, peer->rtt, peer->rtt_expected);
+ peer_flag_set(peer, PEER_FLAG_SHUTDOWN);
+ }
+ } else {
+ if (peer->rtt_keepalive_rcv)
+ peer->rtt_keepalive_rcv--;
+ }
+
+ return Receive_KEEPALIVE_message;
+}
+
+static void bgp_refresh_stalepath_timer_expire(struct thread *thread)
+{
+ struct peer_af *paf;
+
+ paf = THREAD_ARG(thread);
+
+ afi_t afi = paf->afi;
+ safi_t safi = paf->safi;
+ struct peer *peer = paf->peer;
+
+ peer->t_refresh_stalepath = NULL;
+
+ if (peer->nsf[afi][safi])
+ bgp_clear_stale_route(peer, afi, safi);
+
+ if (bgp_debug_neighbor_events(peer))
+ zlog_debug(
+ "%pBP route-refresh (BoRR) timer expired for afi/safi: %d/%d",
+ peer, afi, safi);
+
+ bgp_timer_set(peer);
+}
+
+/**
+ * Process BGP UPDATE message for peer.
+ *
+ * Parses UPDATE and creates attribute object.
+ *
+ * @param peer
+ * @param size size of the packet
+ * @return as in summary
+ */
+static int bgp_update_receive(struct peer *peer, bgp_size_t size)
+{
+ int ret, nlri_ret;
+ uint8_t *end;
+ struct stream *s;
+ struct attr attr;
+ bgp_size_t attribute_len;
+ bgp_size_t update_len;
+ bgp_size_t withdraw_len;
+ bool restart = false;
+
+ enum NLRI_TYPES {
+ NLRI_UPDATE,
+ NLRI_WITHDRAW,
+ NLRI_MP_UPDATE,
+ NLRI_MP_WITHDRAW,
+ NLRI_TYPE_MAX
+ };
+ struct bgp_nlri nlris[NLRI_TYPE_MAX];
+
+ /* Status must be Established. */
+ if (!peer_established(peer)) {
+ flog_err(EC_BGP_INVALID_STATUS,
+ "%s [FSM] Update packet received under status %s",
+ peer->host,
+ lookup_msg(bgp_status_msg, peer->status, NULL));
+ bgp_notify_send(peer, BGP_NOTIFY_FSM_ERR,
+ bgp_fsm_error_subcode(peer->status));
+ return BGP_Stop;
+ }
+
+ /* Set initial values. */
+ memset(&attr, 0, sizeof(attr));
+ attr.label_index = BGP_INVALID_LABEL_INDEX;
+ attr.label = MPLS_INVALID_LABEL;
+ memset(&nlris, 0, sizeof(nlris));
+ memset(peer->rcvd_attr_str, 0, BUFSIZ);
+ peer->rcvd_attr_printed = 0;
+
+ s = peer->curr;
+ end = stream_pnt(s) + size;
+
+ /* RFC1771 6.3 If the Unfeasible Routes Length or Total Attribute
+ Length is too large (i.e., if Unfeasible Routes Length + Total
+ Attribute Length + 23 exceeds the message Length), then the Error
+ Subcode is set to Malformed Attribute List. */
+ if (stream_pnt(s) + 2 > end) {
+ flog_err(EC_BGP_UPDATE_RCV,
+ "%s [Error] Update packet error (packet length is short for unfeasible length)",
+ peer->host);
+ bgp_notify_send(peer, BGP_NOTIFY_UPDATE_ERR,
+ BGP_NOTIFY_UPDATE_MAL_ATTR);
+ return BGP_Stop;
+ }
+
+ /* Unfeasible Route Length. */
+ withdraw_len = stream_getw(s);
+
+ /* Unfeasible Route Length check. */
+ if (stream_pnt(s) + withdraw_len > end) {
+ flog_err(EC_BGP_UPDATE_RCV,
+ "%s [Error] Update packet error (packet unfeasible length overflow %d)",
+ peer->host, withdraw_len);
+ bgp_notify_send(peer, BGP_NOTIFY_UPDATE_ERR,
+ BGP_NOTIFY_UPDATE_MAL_ATTR);
+ return BGP_Stop;
+ }
+
+ /* Unfeasible Route packet format check. */
+ if (withdraw_len > 0) {
+ nlris[NLRI_WITHDRAW].afi = AFI_IP;
+ nlris[NLRI_WITHDRAW].safi = SAFI_UNICAST;
+ nlris[NLRI_WITHDRAW].nlri = stream_pnt(s);
+ nlris[NLRI_WITHDRAW].length = withdraw_len;
+ stream_forward_getp(s, withdraw_len);
+ }
+
+ /* Attribute total length check. */
+ if (stream_pnt(s) + 2 > end) {
+ flog_warn(
+ EC_BGP_UPDATE_PACKET_SHORT,
+ "%s [Error] Packet Error (update packet is short for attribute length)",
+ peer->host);
+ bgp_notify_send(peer, BGP_NOTIFY_UPDATE_ERR,
+ BGP_NOTIFY_UPDATE_MAL_ATTR);
+ return BGP_Stop;
+ }
+
+ /* Fetch attribute total length. */
+ attribute_len = stream_getw(s);
+
+ /* Attribute length check. */
+ if (stream_pnt(s) + attribute_len > end) {
+ flog_warn(
+ EC_BGP_UPDATE_PACKET_LONG,
+ "%s [Error] Packet Error (update packet attribute length overflow %d)",
+ peer->host, attribute_len);
+ bgp_notify_send(peer, BGP_NOTIFY_UPDATE_ERR,
+ BGP_NOTIFY_UPDATE_MAL_ATTR);
+ return BGP_Stop;
+ }
+
+ /* Certain attribute parsing errors should not be considered bad enough
+ * to reset the session for, most particularly any partial/optional
+ * attributes that have 'tunneled' over speakers that don't understand
+ * them. Instead we withdraw only the prefix concerned.
+ *
+ * Complicates the flow a little though..
+ */
+ enum bgp_attr_parse_ret attr_parse_ret = BGP_ATTR_PARSE_PROCEED;
+/* This define morphs the update case into a withdraw when lower levels
+ * have signalled an error condition where this is best.
+ */
+#define NLRI_ATTR_ARG (attr_parse_ret != BGP_ATTR_PARSE_WITHDRAW ? &attr : NULL)
+
+ /* Parse attribute when it exists. */
+ if (attribute_len) {
+ attr_parse_ret = bgp_attr_parse(peer, &attr, attribute_len,
+ &nlris[NLRI_MP_UPDATE],
+ &nlris[NLRI_MP_WITHDRAW]);
+ if (attr_parse_ret == BGP_ATTR_PARSE_ERROR) {
+ bgp_attr_unintern_sub(&attr);
+ return BGP_Stop;
+ }
+ }
+
+ /* Logging the attribute. */
+ if (attr_parse_ret == BGP_ATTR_PARSE_WITHDRAW
+ || BGP_DEBUG(update, UPDATE_IN)
+ || BGP_DEBUG(update, UPDATE_PREFIX)) {
+ ret = bgp_dump_attr(&attr, peer->rcvd_attr_str,
+ sizeof(peer->rcvd_attr_str));
+
+ peer->stat_upd_7606++;
+
+ if (attr_parse_ret == BGP_ATTR_PARSE_WITHDRAW)
+ flog_err(
+ EC_BGP_UPDATE_RCV,
+ "%pBP rcvd UPDATE with errors in attr(s)!! Withdrawing route.",
+ peer);
+
+ if (ret && bgp_debug_update(peer, NULL, NULL, 1)) {
+ zlog_debug("%pBP rcvd UPDATE w/ attr: %s", peer,
+ peer->rcvd_attr_str);
+ peer->rcvd_attr_printed = 1;
+ }
+ }
+
+ /* Network Layer Reachability Information. */
+ update_len = end - stream_pnt(s);
+
+ if (update_len) {
+ /* Set NLRI portion to structure. */
+ nlris[NLRI_UPDATE].afi = AFI_IP;
+ nlris[NLRI_UPDATE].safi = SAFI_UNICAST;
+ nlris[NLRI_UPDATE].nlri = stream_pnt(s);
+ nlris[NLRI_UPDATE].length = update_len;
+ stream_forward_getp(s, update_len);
+
+ if (CHECK_FLAG(attr.flag, ATTR_FLAG_BIT(BGP_ATTR_MP_REACH_NLRI))) {
+ /*
+ * We skipped nexthop attribute validation earlier so
+ * validate the nexthop now.
+ */
+ if (bgp_attr_nexthop_valid(peer, &attr) < 0) {
+ bgp_attr_unintern_sub(&attr);
+ return BGP_Stop;
+ }
+ }
+ }
+
+ if (BGP_DEBUG(update, UPDATE_IN))
+ zlog_debug("%pBP rcvd UPDATE wlen %d attrlen %d alen %d", peer,
+ withdraw_len, attribute_len, update_len);
+
+ /* Parse any given NLRIs */
+ for (int i = NLRI_UPDATE; i < NLRI_TYPE_MAX; i++) {
+ if (!nlris[i].nlri)
+ continue;
+
+ /* NLRI is processed iff the peer if configured for the specific
+ * afi/safi */
+ if (!peer->afc[nlris[i].afi][nlris[i].safi]) {
+ zlog_info(
+ "%s [Info] UPDATE for non-enabled AFI/SAFI %u/%u",
+ peer->host, nlris[i].afi, nlris[i].safi);
+ continue;
+ }
+
+ /* EoR handled later */
+ if (nlris[i].length == 0)
+ continue;
+
+ switch (i) {
+ case NLRI_UPDATE:
+ case NLRI_MP_UPDATE:
+ nlri_ret = bgp_nlri_parse(peer, NLRI_ATTR_ARG,
+ &nlris[i], 0);
+ break;
+ case NLRI_WITHDRAW:
+ case NLRI_MP_WITHDRAW:
+ nlri_ret = bgp_nlri_parse(peer, &attr, &nlris[i], 1);
+ break;
+ default:
+ nlri_ret = BGP_NLRI_PARSE_ERROR;
+ }
+
+ if (nlri_ret < BGP_NLRI_PARSE_OK
+ && nlri_ret != BGP_NLRI_PARSE_ERROR_PREFIX_OVERFLOW) {
+ flog_err(EC_BGP_UPDATE_RCV,
+ "%s [Error] Error parsing NLRI", peer->host);
+ if (peer_established(peer))
+ bgp_notify_send(
+ peer, BGP_NOTIFY_UPDATE_ERR,
+ i <= NLRI_WITHDRAW
+ ? BGP_NOTIFY_UPDATE_INVAL_NETWORK
+ : BGP_NOTIFY_UPDATE_OPT_ATTR_ERR);
+ bgp_attr_unintern_sub(&attr);
+ return BGP_Stop;
+ }
+ }
+
+ /* EoR checks
+ *
+ * Non-MP IPv4/Unicast EoR is a completely empty UPDATE
+ * and MP EoR should have only an empty MP_UNREACH
+ */
+ if ((!update_len && !withdraw_len && nlris[NLRI_MP_UPDATE].length == 0)
+ || (attr_parse_ret == BGP_ATTR_PARSE_EOR)) {
+ afi_t afi = 0;
+ safi_t safi;
+ struct graceful_restart_info *gr_info;
+
+ /* Restarting router */
+ if (BGP_PEER_GRACEFUL_RESTART_CAPABLE(peer)
+ && BGP_PEER_RESTARTING_MODE(peer))
+ restart = true;
+
+ /* Non-MP IPv4/Unicast is a completely emtpy UPDATE - already
+ * checked
+ * update and withdraw NLRI lengths are 0.
+ */
+ if (!attribute_len) {
+ afi = AFI_IP;
+ safi = SAFI_UNICAST;
+ } else if (attr.flag & ATTR_FLAG_BIT(BGP_ATTR_MP_UNREACH_NLRI)
+ && nlris[NLRI_MP_WITHDRAW].length == 0) {
+ afi = nlris[NLRI_MP_WITHDRAW].afi;
+ safi = nlris[NLRI_MP_WITHDRAW].safi;
+ } else if (attr_parse_ret == BGP_ATTR_PARSE_EOR) {
+ afi = nlris[NLRI_MP_UPDATE].afi;
+ safi = nlris[NLRI_MP_UPDATE].safi;
+ }
+
+ if (afi && peer->afc[afi][safi]) {
+ struct vrf *vrf = vrf_lookup_by_id(peer->bgp->vrf_id);
+
+ /* End-of-RIB received */
+ if (!CHECK_FLAG(peer->af_sflags[afi][safi],
+ PEER_STATUS_EOR_RECEIVED)) {
+ SET_FLAG(peer->af_sflags[afi][safi],
+ PEER_STATUS_EOR_RECEIVED);
+ bgp_update_explicit_eors(peer);
+ /* Update graceful restart information */
+ gr_info = &(peer->bgp->gr_info[afi][safi]);
+ if (restart)
+ gr_info->eor_received++;
+ /* If EOR received from all peers and selection
+ * deferral timer is running, cancel the timer
+ * and invoke the best path calculation
+ */
+ if (gr_info->eor_required
+ == gr_info->eor_received) {
+ if (bgp_debug_neighbor_events(peer))
+ zlog_debug(
+ "%s %d, %s %d",
+ "EOR REQ",
+ gr_info->eor_required,
+ "EOR RCV",
+ gr_info->eor_received);
+ if (gr_info->t_select_deferral) {
+ void *info = THREAD_ARG(
+ gr_info->t_select_deferral);
+ XFREE(MTYPE_TMP, info);
+ }
+ THREAD_OFF(gr_info->t_select_deferral);
+ gr_info->eor_required = 0;
+ gr_info->eor_received = 0;
+ /* Best path selection */
+ bgp_best_path_select_defer(peer->bgp,
+ afi, safi);
+ }
+ }
+
+ /* NSF delete stale route */
+ if (peer->nsf[afi][safi])
+ bgp_clear_stale_route(peer, afi, safi);
+
+ zlog_info(
+ "%s: rcvd End-of-RIB for %s from %s in vrf %s",
+ __func__, get_afi_safi_str(afi, safi, false),
+ peer->host, vrf ? vrf->name : VRF_DEFAULT_NAME);
+ }
+ }
+
+ /* Everything is done. We unintern temporary structures which
+ interned in bgp_attr_parse(). */
+ bgp_attr_unintern_sub(&attr);
+
+ peer->update_time = monotime(NULL);
+
+ /* Notify BGP Conditional advertisement scanner process */
+ peer->advmap_table_change = true;
+
+ return Receive_UPDATE_message;
+}
+
+/**
+ * Process BGP NOTIFY message for peer.
+ *
+ * @param peer
+ * @param size size of the packet
+ * @return as in summary
+ */
+static int bgp_notify_receive(struct peer *peer, bgp_size_t size)
+{
+ struct bgp_notify outer = {};
+ struct bgp_notify inner = {};
+ bool hard_reset = false;
+
+ if (peer->notify.data) {
+ XFREE(MTYPE_BGP_NOTIFICATION, peer->notify.data);
+ peer->notify.length = 0;
+ peer->notify.hard_reset = false;
+ }
+
+ outer.code = stream_getc(peer->curr);
+ outer.subcode = stream_getc(peer->curr);
+ outer.length = size - 2;
+ outer.data = NULL;
+ outer.raw_data = NULL;
+ if (outer.length) {
+ outer.raw_data = XMALLOC(MTYPE_BGP_NOTIFICATION, outer.length);
+ memcpy(outer.raw_data, stream_pnt(peer->curr), outer.length);
+ }
+
+ hard_reset =
+ bgp_notify_received_hard_reset(peer, outer.code, outer.subcode);
+ if (hard_reset && outer.length) {
+ inner = bgp_notify_decapsulate_hard_reset(&outer);
+ peer->notify.hard_reset = true;
+ } else {
+ inner = outer;
+ }
+
+ /* Preserv notify code and sub code. */
+ peer->notify.code = inner.code;
+ peer->notify.subcode = inner.subcode;
+ /* For further diagnostic record returned Data. */
+ if (inner.length) {
+ peer->notify.length = inner.length;
+ peer->notify.data =
+ XMALLOC(MTYPE_BGP_NOTIFICATION, inner.length);
+ memcpy(peer->notify.data, inner.raw_data, inner.length);
+ }
+
+ /* For debug */
+ {
+ int i;
+ int first = 0;
+ char c[4];
+
+ if (inner.length) {
+ inner.data = XMALLOC(MTYPE_BGP_NOTIFICATION,
+ inner.length * 3);
+ for (i = 0; i < inner.length; i++)
+ if (first) {
+ snprintf(c, sizeof(c), " %02x",
+ stream_getc(peer->curr));
+
+ strlcat(inner.data, c,
+ inner.length * 3);
+
+ } else {
+ first = 1;
+ snprintf(c, sizeof(c), "%02x",
+ stream_getc(peer->curr));
+
+ strlcpy(inner.data, c,
+ inner.length * 3);
+ }
+ }
+
+ bgp_notify_print(peer, &inner, "received", hard_reset);
+ if (inner.length) {
+ XFREE(MTYPE_BGP_NOTIFICATION, inner.data);
+ inner.length = 0;
+ }
+ if (outer.length) {
+ XFREE(MTYPE_BGP_NOTIFICATION, outer.data);
+ XFREE(MTYPE_BGP_NOTIFICATION, outer.raw_data);
+
+ /* If this is a Hard Reset notification, we MUST free
+ * the inner (encapsulated) notification too.
+ */
+ if (hard_reset)
+ XFREE(MTYPE_BGP_NOTIFICATION, inner.raw_data);
+ outer.length = 0;
+ }
+ }
+
+ /* peer count update */
+ atomic_fetch_add_explicit(&peer->notify_in, 1, memory_order_relaxed);
+
+ peer->last_reset = PEER_DOWN_NOTIFY_RECEIVED;
+
+ /* We have to check for Notify with Unsupported Optional Parameter.
+ in that case we fallback to open without the capability option.
+ But this done in bgp_stop. We just mark it here to avoid changing
+ the fsm tables. */
+ if (inner.code == BGP_NOTIFY_OPEN_ERR &&
+ inner.subcode == BGP_NOTIFY_OPEN_UNSUP_PARAM)
+ UNSET_FLAG(peer->sflags, PEER_STATUS_CAPABILITY_OPEN);
+
+ /* If Graceful-Restart N-bit (Notification) is exchanged,
+ * and it's not a Hard Reset, let's retain the routes.
+ */
+ if (bgp_has_graceful_restart_notification(peer) && !hard_reset &&
+ CHECK_FLAG(peer->sflags, PEER_STATUS_NSF_MODE))
+ SET_FLAG(peer->sflags, PEER_STATUS_NSF_WAIT);
+
+ bgp_peer_gr_flags_update(peer);
+ BGP_GR_ROUTER_DETECT_AND_SEND_CAPABILITY_TO_ZEBRA(peer->bgp,
+ peer->bgp->peer);
+
+ return Receive_NOTIFICATION_message;
+}
+
+/**
+ * Process BGP ROUTEREFRESH message for peer.
+ *
+ * @param peer
+ * @param size size of the packet
+ * @return as in summary
+ */
+static int bgp_route_refresh_receive(struct peer *peer, bgp_size_t size)
+{
+ iana_afi_t pkt_afi;
+ afi_t afi;
+ iana_safi_t pkt_safi;
+ safi_t safi;
+ struct stream *s;
+ struct peer_af *paf;
+ struct update_group *updgrp;
+ struct peer *updgrp_peer;
+ uint8_t subtype;
+ bool force_update = false;
+ bgp_size_t msg_length =
+ size - (BGP_MSG_ROUTE_REFRESH_MIN_SIZE - BGP_HEADER_SIZE);
+
+ /* If peer does not have the capability, send notification. */
+ if (!CHECK_FLAG(peer->cap, PEER_CAP_REFRESH_ADV)) {
+ flog_err(EC_BGP_NO_CAP,
+ "%s [Error] BGP route refresh is not enabled",
+ peer->host);
+ bgp_notify_send(peer, BGP_NOTIFY_HEADER_ERR,
+ BGP_NOTIFY_HEADER_BAD_MESTYPE);
+ return BGP_Stop;
+ }
+
+ /* Status must be Established. */
+ if (!peer_established(peer)) {
+ flog_err(
+ EC_BGP_INVALID_STATUS,
+ "%s [Error] Route refresh packet received under status %s",
+ peer->host,
+ lookup_msg(bgp_status_msg, peer->status, NULL));
+ bgp_notify_send(peer, BGP_NOTIFY_FSM_ERR,
+ bgp_fsm_error_subcode(peer->status));
+ return BGP_Stop;
+ }
+
+ s = peer->curr;
+
+ /* Parse packet. */
+ pkt_afi = stream_getw(s);
+ subtype = stream_getc(s);
+ pkt_safi = stream_getc(s);
+
+ /* Convert AFI, SAFI to internal values and check. */
+ if (bgp_map_afi_safi_iana2int(pkt_afi, pkt_safi, &afi, &safi)) {
+ zlog_info(
+ "%s REFRESH_REQ for unrecognized afi/safi: %s/%s - ignored",
+ peer->host, iana_afi2str(pkt_afi),
+ iana_safi2str(pkt_safi));
+ return BGP_PACKET_NOOP;
+ }
+
+ if (size != BGP_MSG_ROUTE_REFRESH_MIN_SIZE - BGP_HEADER_SIZE) {
+ uint8_t *end;
+ uint8_t when_to_refresh;
+ uint8_t orf_type;
+ uint16_t orf_len;
+
+ if (subtype) {
+ /* If the length, excluding the fixed-size message
+ * header, of the received ROUTE-REFRESH message with
+ * Message Subtype 1 and 2 is not 4, then the BGP
+ * speaker MUST send a NOTIFICATION message with the
+ * Error Code of "ROUTE-REFRESH Message Error" and the
+ * subcode of "Invalid Message Length".
+ */
+ if (msg_length != 4) {
+ zlog_err(
+ "%s Enhanced Route Refresh message length error",
+ peer->host);
+ bgp_notify_send(
+ peer, BGP_NOTIFY_ROUTE_REFRESH_ERR,
+ BGP_NOTIFY_ROUTE_REFRESH_INVALID_MSG_LEN);
+ }
+
+ /* When the BGP speaker receives a ROUTE-REFRESH message
+ * with a "Message Subtype" field other than 0, 1, or 2,
+ * it MUST ignore the received ROUTE-REFRESH message.
+ */
+ if (subtype > 2)
+ zlog_err(
+ "%s Enhanced Route Refresh invalid subtype",
+ peer->host);
+ }
+
+ if (msg_length < 5) {
+ zlog_info("%s ORF route refresh length error",
+ peer->host);
+ bgp_notify_send(peer, BGP_NOTIFY_CEASE,
+ BGP_NOTIFY_SUBCODE_UNSPECIFIC);
+ return BGP_Stop;
+ }
+
+ when_to_refresh = stream_getc(s);
+ end = stream_pnt(s) + (size - 5);
+
+ while ((stream_pnt(s) + 2) < end) {
+ orf_type = stream_getc(s);
+ orf_len = stream_getw(s);
+
+ /* orf_len in bounds? */
+ if ((stream_pnt(s) + orf_len) > end)
+ break; /* XXX: Notify instead?? */
+ if (orf_type == ORF_TYPE_PREFIX
+ || orf_type == ORF_TYPE_PREFIX_OLD) {
+ uint8_t *p_pnt = stream_pnt(s);
+ uint8_t *p_end = stream_pnt(s) + orf_len;
+ struct orf_prefix orfp;
+ uint8_t common = 0;
+ uint32_t seq;
+ int psize;
+ char name[BUFSIZ];
+ int ret = CMD_SUCCESS;
+
+ if (bgp_debug_neighbor_events(peer)) {
+ zlog_debug(
+ "%pBP rcvd Prefixlist ORF(%d) length %d",
+ peer, orf_type, orf_len);
+ }
+
+ /* ORF prefix-list name */
+ snprintf(name, sizeof(name), "%s.%d.%d",
+ peer->host, afi, safi);
+
+ /* we're going to read at least 1 byte of common
+ * ORF header,
+ * and 7 bytes of ORF Address-filter entry from
+ * the stream
+ */
+ if (*p_pnt & ORF_COMMON_PART_REMOVE_ALL) {
+ if (bgp_debug_neighbor_events(peer))
+ zlog_debug(
+ "%pBP rcvd Remove-All pfxlist ORF request",
+ peer);
+ prefix_bgp_orf_remove_all(afi, name);
+ break;
+ }
+
+ if (orf_len < 7)
+ break;
+
+ while (p_pnt < p_end) {
+ /* If the ORF entry is malformed, want
+ * to read as much of it
+ * as possible without going beyond the
+ * bounds of the entry,
+ * to maximise debug information.
+ */
+ int ok;
+ memset(&orfp, 0, sizeof(orfp));
+ common = *p_pnt++;
+ /* after ++: p_pnt <= p_end */
+ ok = ((uint32_t)(p_end - p_pnt)
+ >= sizeof(uint32_t));
+ if (ok) {
+ memcpy(&seq, p_pnt,
+ sizeof(uint32_t));
+ p_pnt += sizeof(uint32_t);
+ orfp.seq = ntohl(seq);
+ } else
+ p_pnt = p_end;
+
+ /* val checked in prefix_bgp_orf_set */
+ if (p_pnt < p_end)
+ orfp.ge = *p_pnt++;
+
+ /* val checked in prefix_bgp_orf_set */
+ if (p_pnt < p_end)
+ orfp.le = *p_pnt++;
+
+ if ((ok = (p_pnt < p_end)))
+ orfp.p.prefixlen = *p_pnt++;
+
+ /* afi checked already */
+ orfp.p.family = afi2family(afi);
+
+ /* 0 if not ok */
+ psize = PSIZE(orfp.p.prefixlen);
+ /* valid for family ? */
+ if (psize > prefix_blen(&orfp.p)) {
+ ok = 0;
+ psize = prefix_blen(&orfp.p);
+ }
+ /* valid for packet ? */
+ if (psize > (p_end - p_pnt)) {
+ ok = 0;
+ psize = p_end - p_pnt;
+ }
+
+ if (psize > 0)
+ memcpy(&orfp.p.u.prefix, p_pnt,
+ psize);
+ p_pnt += psize;
+
+ if (bgp_debug_neighbor_events(peer)) {
+ char buf[INET6_BUFSIZ];
+
+ zlog_debug(
+ "%pBP rcvd %s %s seq %u %s/%d ge %d le %d%s",
+ peer,
+ (common & ORF_COMMON_PART_REMOVE
+ ? "Remove"
+ : "Add"),
+ (common & ORF_COMMON_PART_DENY
+ ? "deny"
+ : "permit"),
+ orfp.seq,
+ inet_ntop(
+ orfp.p.family,
+ &orfp.p.u.prefix,
+ buf,
+ INET6_BUFSIZ),
+ orfp.p.prefixlen,
+ orfp.ge, orfp.le,
+ ok ? "" : " MALFORMED");
+ }
+
+ if (ok)
+ ret = prefix_bgp_orf_set(
+ name, afi, &orfp,
+ (common & ORF_COMMON_PART_DENY
+ ? 0
+ : 1),
+ (common & ORF_COMMON_PART_REMOVE
+ ? 0
+ : 1));
+
+ if (!ok || (ok && ret != CMD_SUCCESS)) {
+ zlog_info(
+ "%pBP Received misformatted prefixlist ORF. Remove All pfxlist",
+ peer);
+ prefix_bgp_orf_remove_all(afi,
+ name);
+ break;
+ }
+ }
+
+ peer->orf_plist[afi][safi] =
+ prefix_bgp_orf_lookup(afi, name);
+ }
+ stream_forward_getp(s, orf_len);
+ }
+ if (bgp_debug_neighbor_events(peer))
+ zlog_debug("%pBP rcvd Refresh %s ORF request", peer,
+ when_to_refresh == REFRESH_DEFER
+ ? "Defer"
+ : "Immediate");
+ if (when_to_refresh == REFRESH_DEFER)
+ return BGP_PACKET_NOOP;
+ }
+
+ /* First update is deferred until ORF or ROUTE-REFRESH is received */
+ if (CHECK_FLAG(peer->af_sflags[afi][safi],
+ PEER_STATUS_ORF_WAIT_REFRESH))
+ UNSET_FLAG(peer->af_sflags[afi][safi],
+ PEER_STATUS_ORF_WAIT_REFRESH);
+
+ paf = peer_af_find(peer, afi, safi);
+ if (paf && paf->subgroup) {
+ if (peer->orf_plist[afi][safi]) {
+ updgrp = PAF_UPDGRP(paf);
+ updgrp_peer = UPDGRP_PEER(updgrp);
+ updgrp_peer->orf_plist[afi][safi] =
+ peer->orf_plist[afi][safi];
+ }
+
+ /* Avoid supressing duplicate routes later
+ * when processing in subgroup_announce_table().
+ */
+ force_update = true;
+
+ /* If the peer is configured for default-originate clear the
+ * SUBGRP_STATUS_DEFAULT_ORIGINATE flag so that we will
+ * re-advertise the
+ * default
+ */
+ if (CHECK_FLAG(paf->subgroup->sflags,
+ SUBGRP_STATUS_DEFAULT_ORIGINATE))
+ UNSET_FLAG(paf->subgroup->sflags,
+ SUBGRP_STATUS_DEFAULT_ORIGINATE);
+ }
+
+ if (subtype == BGP_ROUTE_REFRESH_BORR) {
+ /* A BGP speaker that has received the Graceful Restart
+ * Capability from its neighbor MUST ignore any BoRRs for
+ * an <AFI, SAFI> from the neighbor before the speaker
+ * receives the EoR for the given <AFI, SAFI> from the
+ * neighbor.
+ */
+ if (CHECK_FLAG(peer->cap, PEER_CAP_RESTART_RCV)
+ && !CHECK_FLAG(peer->af_sflags[afi][safi],
+ PEER_STATUS_EOR_RECEIVED)) {
+ if (bgp_debug_neighbor_events(peer))
+ zlog_debug(
+ "%pBP rcvd route-refresh (BoRR) for %s/%s before EoR",
+ peer, afi2str(afi), safi2str(safi));
+ return BGP_PACKET_NOOP;
+ }
+
+ if (peer->t_refresh_stalepath) {
+ if (bgp_debug_neighbor_events(peer))
+ zlog_debug(
+ "%pBP rcvd route-refresh (BoRR) for %s/%s, whereas BoRR already received",
+ peer, afi2str(afi), safi2str(safi));
+ return BGP_PACKET_NOOP;
+ }
+
+ SET_FLAG(peer->af_sflags[afi][safi], PEER_STATUS_BORR_RECEIVED);
+ UNSET_FLAG(peer->af_sflags[afi][safi],
+ PEER_STATUS_EORR_RECEIVED);
+
+ /* When a BGP speaker receives a BoRR message from
+ * a peer, it MUST mark all the routes with the given
+ * Address Family Identifier and Subsequent Address
+ * Family Identifier, <AFI, SAFI> [RFC2918], from
+ * that peer as stale.
+ */
+ if (peer_active_nego(peer)) {
+ SET_FLAG(peer->af_sflags[afi][safi],
+ PEER_STATUS_ENHANCED_REFRESH);
+ bgp_set_stale_route(peer, afi, safi);
+ }
+
+ if (peer_established(peer))
+ thread_add_timer(bm->master,
+ bgp_refresh_stalepath_timer_expire,
+ paf, peer->bgp->stalepath_time,
+ &peer->t_refresh_stalepath);
+
+ if (bgp_debug_neighbor_events(peer))
+ zlog_debug(
+ "%pBP rcvd route-refresh (BoRR) for %s/%s, triggering timer for %u seconds",
+ peer, afi2str(afi), safi2str(safi),
+ peer->bgp->stalepath_time);
+ } else if (subtype == BGP_ROUTE_REFRESH_EORR) {
+ if (!peer->t_refresh_stalepath) {
+ zlog_err(
+ "%pBP rcvd route-refresh (EoRR) for %s/%s, whereas no BoRR received",
+ peer, afi2str(afi), safi2str(safi));
+ return BGP_PACKET_NOOP;
+ }
+
+ THREAD_OFF(peer->t_refresh_stalepath);
+
+ SET_FLAG(peer->af_sflags[afi][safi], PEER_STATUS_EORR_RECEIVED);
+ UNSET_FLAG(peer->af_sflags[afi][safi],
+ PEER_STATUS_BORR_RECEIVED);
+
+ if (bgp_debug_neighbor_events(peer))
+ zlog_debug(
+ "%pBP rcvd route-refresh (EoRR) for %s/%s, stopping BoRR timer",
+ peer, afi2str(afi), safi2str(safi));
+
+ if (peer->nsf[afi][safi])
+ bgp_clear_stale_route(peer, afi, safi);
+ } else {
+ if (bgp_debug_neighbor_events(peer))
+ zlog_debug(
+ "%pBP rcvd route-refresh (REQUEST) for %s/%s",
+ peer, afi2str(afi), safi2str(safi));
+
+ /* In response to a "normal route refresh request" from the
+ * peer, the speaker MUST send a BoRR message.
+ */
+ if (CHECK_FLAG(peer->cap, PEER_CAP_ENHANCED_RR_RCV)) {
+ /* For a BGP speaker that supports the BGP Graceful
+ * Restart, it MUST NOT send a BoRR for an <AFI, SAFI>
+ * to a neighbor before it sends the EoR for the
+ * <AFI, SAFI> to the neighbor.
+ */
+ if (!CHECK_FLAG(peer->af_sflags[afi][safi],
+ PEER_STATUS_EOR_SEND)) {
+ if (bgp_debug_neighbor_events(peer))
+ zlog_debug(
+ "%pBP rcvd route-refresh (REQUEST) for %s/%s before EoR",
+ peer, afi2str(afi),
+ safi2str(safi));
+ /* Can't send BoRR now, postpone after EoR */
+ SET_FLAG(peer->af_sflags[afi][safi],
+ PEER_STATUS_REFRESH_PENDING);
+ return BGP_PACKET_NOOP;
+ }
+
+ bgp_route_refresh_send(peer, afi, safi, 0, 0, 0,
+ BGP_ROUTE_REFRESH_BORR);
+
+ if (bgp_debug_neighbor_events(peer))
+ zlog_debug(
+ "%pBP sending route-refresh (BoRR) for %s/%s",
+ peer, afi2str(afi), safi2str(safi));
+
+ /* Set flag Ready-To-Send to know when we can send EoRR
+ * message.
+ */
+ SET_FLAG(peer->af_sflags[afi][safi],
+ PEER_STATUS_BORR_SEND);
+ UNSET_FLAG(peer->af_sflags[afi][safi],
+ PEER_STATUS_EORR_SEND);
+ }
+ }
+
+ /* Perform route refreshment to the peer */
+ bgp_announce_route(peer, afi, safi, force_update);
+
+ /* No FSM action necessary */
+ return BGP_PACKET_NOOP;
+}
+
+/**
+ * Parse BGP CAPABILITY message for peer.
+ *
+ * @param peer
+ * @param size size of the packet
+ * @return as in summary
+ */
+static int bgp_capability_msg_parse(struct peer *peer, uint8_t *pnt,
+ bgp_size_t length)
+{
+ uint8_t *end;
+ struct capability_mp_data mpc;
+ struct capability_header *hdr;
+ uint8_t action;
+ iana_afi_t pkt_afi;
+ afi_t afi;
+ iana_safi_t pkt_safi;
+ safi_t safi;
+
+ end = pnt + length;
+
+ while (pnt < end) {
+ /* We need at least action, capability code and capability
+ * length. */
+ if (pnt + 3 > end) {
+ zlog_info("%s Capability length error", peer->host);
+ bgp_notify_send(peer, BGP_NOTIFY_CEASE,
+ BGP_NOTIFY_SUBCODE_UNSPECIFIC);
+ return BGP_Stop;
+ }
+ action = *pnt;
+ hdr = (struct capability_header *)(pnt + 1);
+
+ /* Action value check. */
+ if (action != CAPABILITY_ACTION_SET
+ && action != CAPABILITY_ACTION_UNSET) {
+ zlog_info("%s Capability Action Value error %d",
+ peer->host, action);
+ bgp_notify_send(peer, BGP_NOTIFY_CEASE,
+ BGP_NOTIFY_SUBCODE_UNSPECIFIC);
+ return BGP_Stop;
+ }
+
+ if (bgp_debug_neighbor_events(peer))
+ zlog_debug(
+ "%s CAPABILITY has action: %d, code: %u, length %u",
+ peer->host, action, hdr->code, hdr->length);
+
+ if (hdr->length < sizeof(struct capability_mp_data)) {
+ zlog_info(
+ "%pBP Capability structure is not properly filled out, expected at least %zu bytes but header length specified is %d",
+ peer, sizeof(struct capability_mp_data),
+ hdr->length);
+ return BGP_Stop;
+ }
+
+ /* Capability length check. */
+ if ((pnt + hdr->length + 3) > end) {
+ zlog_info("%s Capability length error", peer->host);
+ bgp_notify_send(peer, BGP_NOTIFY_CEASE,
+ BGP_NOTIFY_SUBCODE_UNSPECIFIC);
+ return BGP_Stop;
+ }
+
+ /* Fetch structure to the byte stream. */
+ memcpy(&mpc, pnt + 3, sizeof(struct capability_mp_data));
+ pnt += hdr->length + 3;
+
+ /* We know MP Capability Code. */
+ if (hdr->code == CAPABILITY_CODE_MP) {
+ pkt_afi = ntohs(mpc.afi);
+ pkt_safi = mpc.safi;
+
+ /* Ignore capability when override-capability is set. */
+ if (CHECK_FLAG(peer->flags,
+ PEER_FLAG_OVERRIDE_CAPABILITY))
+ continue;
+
+ /* Convert AFI, SAFI to internal values. */
+ if (bgp_map_afi_safi_iana2int(pkt_afi, pkt_safi, &afi,
+ &safi)) {
+ if (bgp_debug_neighbor_events(peer))
+ zlog_debug(
+ "%s Dynamic Capability MP_EXT afi/safi invalid (%s/%s)",
+ peer->host,
+ iana_afi2str(pkt_afi),
+ iana_safi2str(pkt_safi));
+ continue;
+ }
+
+ /* Address family check. */
+ if (bgp_debug_neighbor_events(peer))
+ zlog_debug(
+ "%s CAPABILITY has %s MP_EXT CAP for afi/safi: %s/%s",
+ peer->host,
+ action == CAPABILITY_ACTION_SET
+ ? "Advertising"
+ : "Removing",
+ iana_afi2str(pkt_afi),
+ iana_safi2str(pkt_safi));
+
+ if (action == CAPABILITY_ACTION_SET) {
+ peer->afc_recv[afi][safi] = 1;
+ if (peer->afc[afi][safi]) {
+ peer->afc_nego[afi][safi] = 1;
+ bgp_announce_route(peer, afi, safi,
+ false);
+ }
+ } else {
+ peer->afc_recv[afi][safi] = 0;
+ peer->afc_nego[afi][safi] = 0;
+
+ if (peer_active_nego(peer))
+ bgp_clear_route(peer, afi, safi);
+ else
+ return BGP_Stop;
+ }
+ } else {
+ flog_warn(
+ EC_BGP_UNRECOGNIZED_CAPABILITY,
+ "%s unrecognized capability code: %d - ignored",
+ peer->host, hdr->code);
+ }
+ }
+
+ /* No FSM action necessary */
+ return BGP_PACKET_NOOP;
+}
+
+/**
+ * Parse BGP CAPABILITY message for peer.
+ *
+ * Exported for unit testing.
+ *
+ * @param peer
+ * @param size size of the packet
+ * @return as in summary
+ */
+int bgp_capability_receive(struct peer *peer, bgp_size_t size)
+{
+ uint8_t *pnt;
+
+ /* Fetch pointer. */
+ pnt = stream_pnt(peer->curr);
+
+ if (bgp_debug_neighbor_events(peer))
+ zlog_debug("%s rcv CAPABILITY", peer->host);
+
+ /* If peer does not have the capability, send notification. */
+ if (!CHECK_FLAG(peer->cap, PEER_CAP_DYNAMIC_ADV)) {
+ flog_err(EC_BGP_NO_CAP,
+ "%s [Error] BGP dynamic capability is not enabled",
+ peer->host);
+ bgp_notify_send(peer, BGP_NOTIFY_HEADER_ERR,
+ BGP_NOTIFY_HEADER_BAD_MESTYPE);
+ return BGP_Stop;
+ }
+
+ /* Status must be Established. */
+ if (!peer_established(peer)) {
+ flog_err(
+ EC_BGP_NO_CAP,
+ "%s [Error] Dynamic capability packet received under status %s",
+ peer->host,
+ lookup_msg(bgp_status_msg, peer->status, NULL));
+ bgp_notify_send(peer, BGP_NOTIFY_FSM_ERR,
+ bgp_fsm_error_subcode(peer->status));
+ return BGP_Stop;
+ }
+
+ /* Parse packet. */
+ return bgp_capability_msg_parse(peer, pnt, size);
+}
+
+/**
+ * Processes a peer's input buffer.
+ *
+ * This function sidesteps the event loop and directly calls bgp_event_update()
+ * after processing each BGP message. This is necessary to ensure proper
+ * ordering of FSM events and unifies the behavior that was present previously,
+ * whereby some of the packet handling functions would update the FSM and some
+ * would not, making event flow difficult to understand. Please think twice
+ * before hacking this.
+ *
+ * Thread type: THREAD_EVENT
+ * @param thread
+ * @return 0
+ */
+void bgp_process_packet(struct thread *thread)
+{
+ /* Yes first of all get peer pointer. */
+ struct peer *peer; // peer
+ uint32_t rpkt_quanta_old; // how many packets to read
+ int fsm_update_result; // return code of bgp_event_update()
+ int mprc; // message processing return code
+
+ peer = THREAD_ARG(thread);
+ rpkt_quanta_old = atomic_load_explicit(&peer->bgp->rpkt_quanta,
+ memory_order_relaxed);
+ fsm_update_result = 0;
+
+ /* Guard against scheduled events that occur after peer deletion. */
+ if (peer->status == Deleted || peer->status == Clearing)
+ return;
+
+ unsigned int processed = 0;
+
+ while (processed < rpkt_quanta_old) {
+ uint8_t type = 0;
+ bgp_size_t size;
+ char notify_data_length[2];
+
+ frr_with_mutex (&peer->io_mtx) {
+ peer->curr = stream_fifo_pop(peer->ibuf);
+ }
+
+ if (peer->curr == NULL) // no packets to process, hmm...
+ return;
+
+ /* skip the marker and copy the packet length */
+ stream_forward_getp(peer->curr, BGP_MARKER_SIZE);
+ memcpy(notify_data_length, stream_pnt(peer->curr), 2);
+
+ /* read in the packet length and type */
+ size = stream_getw(peer->curr);
+ type = stream_getc(peer->curr);
+
+ hook_call(bgp_packet_dump, peer, type, size, peer->curr);
+
+ /* adjust size to exclude the marker + length + type */
+ size -= BGP_HEADER_SIZE;
+
+ /* Read rest of the packet and call each sort of packet routine
+ */
+ switch (type) {
+ case BGP_MSG_OPEN:
+ frrtrace(2, frr_bgp, open_process, peer, size);
+ atomic_fetch_add_explicit(&peer->open_in, 1,
+ memory_order_relaxed);
+ mprc = bgp_open_receive(peer, size);
+ if (mprc == BGP_Stop)
+ flog_err(
+ EC_BGP_PKT_OPEN,
+ "%s: BGP OPEN receipt failed for peer: %s",
+ __func__, peer->host);
+ break;
+ case BGP_MSG_UPDATE:
+ frrtrace(2, frr_bgp, update_process, peer, size);
+ atomic_fetch_add_explicit(&peer->update_in, 1,
+ memory_order_relaxed);
+ peer->readtime = monotime(NULL);
+ mprc = bgp_update_receive(peer, size);
+ if (mprc == BGP_Stop)
+ flog_err(
+ EC_BGP_UPDATE_RCV,
+ "%s: BGP UPDATE receipt failed for peer: %s",
+ __func__, peer->host);
+ break;
+ case BGP_MSG_NOTIFY:
+ frrtrace(2, frr_bgp, notification_process, peer, size);
+ atomic_fetch_add_explicit(&peer->notify_in, 1,
+ memory_order_relaxed);
+ mprc = bgp_notify_receive(peer, size);
+ if (mprc == BGP_Stop)
+ flog_err(
+ EC_BGP_NOTIFY_RCV,
+ "%s: BGP NOTIFY receipt failed for peer: %s",
+ __func__, peer->host);
+ break;
+ case BGP_MSG_KEEPALIVE:
+ frrtrace(2, frr_bgp, keepalive_process, peer, size);
+ peer->readtime = monotime(NULL);
+ atomic_fetch_add_explicit(&peer->keepalive_in, 1,
+ memory_order_relaxed);
+ mprc = bgp_keepalive_receive(peer, size);
+ if (mprc == BGP_Stop)
+ flog_err(
+ EC_BGP_KEEP_RCV,
+ "%s: BGP KEEPALIVE receipt failed for peer: %s",
+ __func__, peer->host);
+ break;
+ case BGP_MSG_ROUTE_REFRESH_NEW:
+ case BGP_MSG_ROUTE_REFRESH_OLD:
+ frrtrace(2, frr_bgp, refresh_process, peer, size);
+ atomic_fetch_add_explicit(&peer->refresh_in, 1,
+ memory_order_relaxed);
+ mprc = bgp_route_refresh_receive(peer, size);
+ if (mprc == BGP_Stop)
+ flog_err(
+ EC_BGP_RFSH_RCV,
+ "%s: BGP ROUTEREFRESH receipt failed for peer: %s",
+ __func__, peer->host);
+ break;
+ case BGP_MSG_CAPABILITY:
+ frrtrace(2, frr_bgp, capability_process, peer, size);
+ atomic_fetch_add_explicit(&peer->dynamic_cap_in, 1,
+ memory_order_relaxed);
+ mprc = bgp_capability_receive(peer, size);
+ if (mprc == BGP_Stop)
+ flog_err(
+ EC_BGP_CAP_RCV,
+ "%s: BGP CAPABILITY receipt failed for peer: %s",
+ __func__, peer->host);
+ break;
+ default:
+ /* Suppress uninitialized variable warning */
+ mprc = 0;
+ (void)mprc;
+ /*
+ * The message type should have been sanitized before
+ * we ever got here. Receipt of a message with an
+ * invalid header at this point is indicative of a
+ * security issue.
+ */
+ assert (!"Message of invalid type received during input processing");
+ }
+
+ /* delete processed packet */
+ stream_free(peer->curr);
+ peer->curr = NULL;
+ processed++;
+
+ /* Update FSM */
+ if (mprc != BGP_PACKET_NOOP)
+ fsm_update_result = bgp_event_update(peer, mprc);
+ else
+ continue;
+
+ /*
+ * If peer was deleted, do not process any more packets. This
+ * is usually due to executing BGP_Stop or a stub deletion.
+ */
+ if (fsm_update_result == FSM_PEER_TRANSFERRED
+ || fsm_update_result == FSM_PEER_STOPPED)
+ break;
+ }
+
+ if (fsm_update_result != FSM_PEER_TRANSFERRED
+ && fsm_update_result != FSM_PEER_STOPPED) {
+ frr_with_mutex (&peer->io_mtx) {
+ // more work to do, come back later
+ if (peer->ibuf->count > 0)
+ thread_add_event(
+ bm->master, bgp_process_packet, peer, 0,
+ &peer->t_process_packet);
+ }
+ }
+}
+
+/* Send EOR when routes are processed by selection deferral timer */
+void bgp_send_delayed_eor(struct bgp *bgp)
+{
+ struct peer *peer;
+ struct listnode *node, *nnode;
+
+ /* EOR message sent in bgp_write_proceed_actions */
+ for (ALL_LIST_ELEMENTS(bgp->peer, node, nnode, peer))
+ bgp_write_proceed_actions(peer);
+}
+
+/*
+ * Task callback to handle socket error encountered in the io pthread. We avoid
+ * having the io pthread try to enqueue fsm events or mess with the peer
+ * struct.
+ */
+void bgp_packet_process_error(struct thread *thread)
+{
+ struct peer *peer;
+ int code;
+
+ peer = THREAD_ARG(thread);
+ code = THREAD_VAL(thread);
+
+ if (bgp_debug_neighbor_events(peer))
+ zlog_debug("%s [Event] BGP error %d on fd %d",
+ peer->host, code, peer->fd);
+
+ /* Closed connection or error on the socket */
+ if (peer_established(peer)) {
+ if ((CHECK_FLAG(peer->flags, PEER_FLAG_GRACEFUL_RESTART)
+ || CHECK_FLAG(peer->flags,
+ PEER_FLAG_GRACEFUL_RESTART_HELPER))
+ && CHECK_FLAG(peer->sflags, PEER_STATUS_NSF_MODE)) {
+ peer->last_reset = PEER_DOWN_NSF_CLOSE_SESSION;
+ SET_FLAG(peer->sflags, PEER_STATUS_NSF_WAIT);
+ } else
+ peer->last_reset = PEER_DOWN_CLOSE_SESSION;
+ }
+
+ bgp_event_update(peer, code);
+}
diff --git a/bgpd/bgp_packet.h b/bgpd/bgp_packet.h
new file mode 100644
index 0000000..a72e855
--- /dev/null
+++ b/bgpd/bgp_packet.h
@@ -0,0 +1,101 @@
+/* BGP packet management header.
+ * Copyright (C) 1999 Kunihiro Ishiguro
+ *
+ * This file is part of GNU Zebra.
+ *
+ * GNU Zebra is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License as published by the
+ * Free Software Foundation; either version 2, or (at your option) any
+ * later version.
+ *
+ * GNU Zebra is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program; see the file COPYING; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
+ */
+
+#ifndef _QUAGGA_BGP_PACKET_H
+#define _QUAGGA_BGP_PACKET_H
+
+#include "hook.h"
+
+DECLARE_HOOK(bgp_packet_dump,
+ (struct peer *peer, uint8_t type, bgp_size_t size,
+ struct stream *s),
+ (peer, type, size, s));
+
+DECLARE_HOOK(bgp_packet_send,
+ (struct peer *peer, uint8_t type, bgp_size_t size,
+ struct stream *s),
+ (peer, type, size, s));
+
+#define BGP_NLRI_LENGTH 1U
+#define BGP_TOTAL_ATTR_LEN 2U
+#define BGP_UNFEASIBLE_LEN 2U
+
+/* When to refresh */
+#define REFRESH_IMMEDIATE 1
+#define REFRESH_DEFER 2
+
+/* ORF Common part flag */
+#define ORF_COMMON_PART_ADD 0x00
+#define ORF_COMMON_PART_REMOVE 0x80
+#define ORF_COMMON_PART_REMOVE_ALL 0xC0
+#define ORF_COMMON_PART_PERMIT 0x00
+#define ORF_COMMON_PART_DENY 0x20
+
+#define BGP_UPDATE_EOR_PKT(_peer, _afi, _safi, _s) \
+ do { \
+ _s = bgp_update_packet_eor(_peer, _afi, _safi); \
+ if (_s) { \
+ bgp_packet_add(_peer, _s); \
+ } \
+ } while (0)
+
+/* Packet send and receive function prototypes. */
+extern void bgp_keepalive_send(struct peer *peer);
+extern void bgp_open_send(struct peer *peer);
+extern void bgp_notify_send(struct peer *peer, uint8_t code, uint8_t sub_code);
+extern void bgp_notify_send_with_data(struct peer *peer, uint8_t code,
+ uint8_t sub_code, uint8_t *data,
+ size_t datalen);
+void bgp_notify_io_invalid(struct peer *peer, uint8_t code, uint8_t sub_code,
+ uint8_t *data, size_t datalen);
+extern void bgp_route_refresh_send(struct peer *peer, afi_t afi, safi_t safi,
+ uint8_t orf_type, uint8_t when_to_refresh,
+ int remove, uint8_t subtype);
+extern void bgp_capability_send(struct peer *peer, afi_t afi, safi_t safi,
+ int capabilty_code, int action);
+
+extern int bgp_capability_receive(struct peer *peer, bgp_size_t length);
+
+extern int bgp_nlri_parse(struct peer *peer, struct attr *attr,
+ struct bgp_nlri *nlri, bool mp_withdraw);
+
+extern void bgp_update_restarted_peers(struct peer *peer);
+extern void bgp_update_implicit_eors(struct peer *peer);
+extern void bgp_check_update_delay(struct bgp *peer);
+
+extern int bgp_packet_set_marker(struct stream *s, uint8_t type);
+extern void bgp_packet_set_size(struct stream *s);
+
+extern void bgp_generate_updgrp_packets(struct thread *);
+extern void bgp_process_packet(struct thread *);
+
+extern void bgp_send_delayed_eor(struct bgp *bgp);
+
+/* Task callback to handle socket error encountered in the io pthread */
+void bgp_packet_process_error(struct thread *thread);
+extern struct bgp_notify
+bgp_notify_decapsulate_hard_reset(struct bgp_notify *notify);
+extern bool bgp_has_graceful_restart_notification(struct peer *peer);
+extern bool bgp_notify_send_hard_reset(struct peer *peer, uint8_t code,
+ uint8_t subcode);
+extern bool bgp_notify_received_hard_reset(struct peer *peer, uint8_t code,
+ uint8_t subcode);
+
+#endif /* _QUAGGA_BGP_PACKET_H */
diff --git a/bgpd/bgp_pbr.c b/bgpd/bgp_pbr.c
new file mode 100644
index 0000000..b71e19a
--- /dev/null
+++ b/bgpd/bgp_pbr.c
@@ -0,0 +1,2960 @@
+/*
+ * BGP pbr
+ * Copyright (C) 6WIND
+ *
+ * FRR is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License as published by the
+ * Free Software Foundation; either version 2, or (at your option) any
+ * later version.
+ *
+ * FRR is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program; see the file COPYING; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
+ */
+
+#include "zebra.h"
+#include "prefix.h"
+#include "zclient.h"
+#include "jhash.h"
+#include "pbr.h"
+
+#include "lib/printfrr.h"
+
+#include "bgpd/bgpd.h"
+#include "bgpd/bgp_pbr.h"
+#include "bgpd/bgp_debug.h"
+#include "bgpd/bgp_flowspec_util.h"
+#include "bgpd/bgp_ecommunity.h"
+#include "bgpd/bgp_route.h"
+#include "bgpd/bgp_attr.h"
+#include "bgpd/bgp_zebra.h"
+#include "bgpd/bgp_mplsvpn.h"
+#include "bgpd/bgp_flowspec_private.h"
+#include "bgpd/bgp_errors.h"
+
+DEFINE_MTYPE_STATIC(BGPD, PBR_MATCH_ENTRY, "PBR match entry");
+DEFINE_MTYPE_STATIC(BGPD, PBR_MATCH, "PBR match");
+DEFINE_MTYPE_STATIC(BGPD, PBR_ACTION, "PBR action");
+DEFINE_MTYPE_STATIC(BGPD, PBR_RULE, "PBR rule");
+DEFINE_MTYPE_STATIC(BGPD, PBR, "BGP PBR Context");
+DEFINE_MTYPE_STATIC(BGPD, PBR_VALMASK, "BGP PBR Val Mask Value");
+
+/* chain strings too long to fit in one line */
+#define FSPEC_ACTION_EXCEED_LIMIT "flowspec actions exceeds limit"
+#define IPV6_FRAGMENT_INVALID "fragment not valid for IPv6 for this implementation"
+
+RB_GENERATE(bgp_pbr_interface_head, bgp_pbr_interface,
+ id_entry, bgp_pbr_interface_compare);
+struct bgp_pbr_interface_head ifaces_by_name_ipv4 =
+ RB_INITIALIZER(&ifaces_by_name_ipv4);
+
+static int bgp_pbr_match_counter_unique;
+static int bgp_pbr_match_entry_counter_unique;
+static int bgp_pbr_action_counter_unique;
+static int bgp_pbr_match_iptable_counter_unique;
+
+struct bgp_pbr_match_iptable_unique {
+ uint32_t unique;
+ struct bgp_pbr_match *bpm_found;
+};
+
+struct bgp_pbr_match_entry_unique {
+ uint32_t unique;
+ struct bgp_pbr_match_entry *bpme_found;
+};
+
+struct bgp_pbr_action_unique {
+ uint32_t unique;
+ struct bgp_pbr_action *bpa_found;
+};
+
+struct bgp_pbr_rule_unique {
+ uint32_t unique;
+ struct bgp_pbr_rule *bpr_found;
+};
+
+static int bgp_pbr_rule_walkcb(struct hash_bucket *bucket, void *arg)
+{
+ struct bgp_pbr_rule *bpr = (struct bgp_pbr_rule *)bucket->data;
+ struct bgp_pbr_rule_unique *bpru = (struct bgp_pbr_rule_unique *)
+ arg;
+ uint32_t unique = bpru->unique;
+
+ if (bpr->unique == unique) {
+ bpru->bpr_found = bpr;
+ return HASHWALK_ABORT;
+ }
+ return HASHWALK_CONTINUE;
+}
+
+static int bgp_pbr_action_walkcb(struct hash_bucket *bucket, void *arg)
+{
+ struct bgp_pbr_action *bpa = (struct bgp_pbr_action *)bucket->data;
+ struct bgp_pbr_action_unique *bpau = (struct bgp_pbr_action_unique *)
+ arg;
+ uint32_t unique = bpau->unique;
+
+ if (bpa->unique == unique) {
+ bpau->bpa_found = bpa;
+ return HASHWALK_ABORT;
+ }
+ return HASHWALK_CONTINUE;
+}
+
+static int bgp_pbr_match_entry_walkcb(struct hash_bucket *bucket, void *arg)
+{
+ struct bgp_pbr_match_entry *bpme =
+ (struct bgp_pbr_match_entry *)bucket->data;
+ struct bgp_pbr_match_entry_unique *bpmeu =
+ (struct bgp_pbr_match_entry_unique *)arg;
+ uint32_t unique = bpmeu->unique;
+
+ if (bpme->unique == unique) {
+ bpmeu->bpme_found = bpme;
+ return HASHWALK_ABORT;
+ }
+ return HASHWALK_CONTINUE;
+}
+
+struct bgp_pbr_match_ipsetname {
+ char *ipsetname;
+ struct bgp_pbr_match *bpm_found;
+};
+
+static int bgp_pbr_match_pername_walkcb(struct hash_bucket *bucket, void *arg)
+{
+ struct bgp_pbr_match *bpm = (struct bgp_pbr_match *)bucket->data;
+ struct bgp_pbr_match_ipsetname *bpmi =
+ (struct bgp_pbr_match_ipsetname *)arg;
+ char *ipset_name = bpmi->ipsetname;
+
+ if (!strncmp(ipset_name, bpm->ipset_name,
+ ZEBRA_IPSET_NAME_SIZE)) {
+ bpmi->bpm_found = bpm;
+ return HASHWALK_ABORT;
+ }
+ return HASHWALK_CONTINUE;
+}
+
+static int bgp_pbr_match_iptable_walkcb(struct hash_bucket *bucket, void *arg)
+{
+ struct bgp_pbr_match *bpm = (struct bgp_pbr_match *)bucket->data;
+ struct bgp_pbr_match_iptable_unique *bpmiu =
+ (struct bgp_pbr_match_iptable_unique *)arg;
+ uint32_t unique = bpmiu->unique;
+
+ if (bpm->unique2 == unique) {
+ bpmiu->bpm_found = bpm;
+ return HASHWALK_ABORT;
+ }
+ return HASHWALK_CONTINUE;
+}
+
+struct bgp_pbr_match_unique {
+ uint32_t unique;
+ struct bgp_pbr_match *bpm_found;
+};
+
+static int bgp_pbr_match_walkcb(struct hash_bucket *bucket, void *arg)
+{
+ struct bgp_pbr_match *bpm = (struct bgp_pbr_match *)bucket->data;
+ struct bgp_pbr_match_unique *bpmu = (struct bgp_pbr_match_unique *)
+ arg;
+ uint32_t unique = bpmu->unique;
+
+ if (bpm->unique == unique) {
+ bpmu->bpm_found = bpm;
+ return HASHWALK_ABORT;
+ }
+ return HASHWALK_CONTINUE;
+}
+
+static int snprintf_bgp_pbr_match_val(char *str, int len,
+ struct bgp_pbr_match_val *mval,
+ const char *prepend)
+{
+ char *ptr = str;
+ int delta;
+
+ if (prepend) {
+ delta = snprintf(ptr, len, "%s", prepend);
+ ptr += delta;
+ len -= delta;
+ } else {
+ if (mval->unary_operator & OPERATOR_UNARY_OR) {
+ delta = snprintf(ptr, len, ", or ");
+ ptr += delta;
+ len -= delta;
+ }
+ if (mval->unary_operator & OPERATOR_UNARY_AND) {
+ delta = snprintf(ptr, len, ", and ");
+ ptr += delta;
+ len -= delta;
+ }
+ }
+ if (mval->compare_operator & OPERATOR_COMPARE_LESS_THAN) {
+ delta = snprintf(ptr, len, "<");
+ ptr += delta;
+ len -= delta;
+ }
+ if (mval->compare_operator & OPERATOR_COMPARE_GREATER_THAN) {
+ delta = snprintf(ptr, len, ">");
+ ptr += delta;
+ len -= delta;
+ }
+ if (mval->compare_operator & OPERATOR_COMPARE_EQUAL_TO) {
+ delta = snprintf(ptr, len, "=");
+ ptr += delta;
+ len -= delta;
+ }
+ if (mval->compare_operator & OPERATOR_COMPARE_EXACT_MATCH) {
+ delta = snprintf(ptr, len, "match");
+ ptr += delta;
+ len -= delta;
+ }
+ ptr += snprintf(ptr, len, " %u", mval->value);
+ return (int)(ptr - str);
+}
+
+#define INCREMENT_DISPLAY(_ptr, _cnt, _len) do { \
+ int sn_delta; \
+ \
+ if (_cnt) { \
+ sn_delta = snprintf((_ptr), (_len), "; ");\
+ (_len) -= sn_delta; \
+ (_ptr) += sn_delta; \
+ } \
+ (_cnt)++; \
+ } while (0)
+
+/* this structure can be used for port range,
+ * but also for other values range like packet length range
+ */
+struct bgp_pbr_range_port {
+ uint16_t min_port;
+ uint16_t max_port;
+};
+
+/* this structure can be used to filter with a mask
+ * for instance it supports not instructions like for
+ * tcpflags
+ */
+struct bgp_pbr_val_mask {
+ uint16_t val;
+ uint16_t mask;
+};
+
+/* this structure is used to pass instructs
+ * so that BGP can create pbr instructions to ZEBRA
+ */
+struct bgp_pbr_filter {
+ uint8_t type;
+ vrf_id_t vrf_id;
+ uint8_t family;
+ struct prefix *src;
+ struct prefix *dst;
+ uint8_t bitmask_iprule;
+ uint8_t protocol;
+ struct bgp_pbr_range_port *pkt_len;
+ struct bgp_pbr_range_port *src_port;
+ struct bgp_pbr_range_port *dst_port;
+ struct bgp_pbr_val_mask *tcp_flags;
+ struct bgp_pbr_val_mask *dscp;
+ struct bgp_pbr_val_mask *flow_label;
+ struct bgp_pbr_val_mask *pkt_len_val;
+ struct bgp_pbr_val_mask *fragment;
+};
+
+/* this structure is used to contain OR instructions
+ * so that BGP can create multiple pbr instructions
+ * to ZEBRA
+ */
+struct bgp_pbr_or_filter {
+ struct list *tcpflags;
+ struct list *dscp;
+ struct list *flowlabel;
+ struct list *pkt_len;
+ struct list *fragment;
+ struct list *icmp_type;
+ struct list *icmp_code;
+};
+
+static void bgp_pbr_policyroute_add_to_zebra_unit(struct bgp *bgp,
+ struct bgp_path_info *path,
+ struct bgp_pbr_filter *bpf,
+ struct nexthop *nh,
+ float *rate);
+
+static void bgp_pbr_dump_entry(struct bgp_pbr_filter *bpf, bool add);
+
+static bool bgp_pbr_extract_enumerate_unary_opposite(
+ uint8_t unary_operator,
+ struct bgp_pbr_val_mask *and_valmask,
+ struct list *or_valmask, uint32_t value,
+ uint8_t type_entry)
+{
+ if (unary_operator == OPERATOR_UNARY_AND && and_valmask) {
+ if (type_entry == FLOWSPEC_TCP_FLAGS) {
+ and_valmask->mask |=
+ TCP_HEADER_ALL_FLAGS &
+ ~(value);
+ } else if (type_entry == FLOWSPEC_DSCP ||
+ type_entry == FLOWSPEC_FLOW_LABEL ||
+ type_entry == FLOWSPEC_PKT_LEN ||
+ type_entry == FLOWSPEC_FRAGMENT) {
+ and_valmask->val = value;
+ and_valmask->mask = 1; /* inverse */
+ }
+ } else if (unary_operator == OPERATOR_UNARY_OR && or_valmask) {
+ and_valmask = XCALLOC(MTYPE_PBR_VALMASK,
+ sizeof(struct bgp_pbr_val_mask));
+ if (type_entry == FLOWSPEC_TCP_FLAGS) {
+ and_valmask->val = TCP_HEADER_ALL_FLAGS;
+ and_valmask->mask |=
+ TCP_HEADER_ALL_FLAGS &
+ ~(value);
+ } else if (type_entry == FLOWSPEC_DSCP ||
+ type_entry == FLOWSPEC_FLOW_LABEL ||
+ type_entry == FLOWSPEC_FRAGMENT ||
+ type_entry == FLOWSPEC_PKT_LEN) {
+ and_valmask->val = value;
+ and_valmask->mask = 1; /* inverse */
+ }
+ listnode_add(or_valmask, and_valmask);
+ } else if (type_entry == FLOWSPEC_ICMP_CODE ||
+ type_entry == FLOWSPEC_ICMP_TYPE)
+ return false;
+ return true;
+}
+
+/* TCP : FIN and SYN -> val = ALL; mask = 3
+ * TCP : not (FIN and SYN) -> val = ALL; mask = ALL & ~(FIN|RST)
+ * other variables type: dscp, pkt len, fragment, flow label
+ * - value is copied in bgp_pbr_val_mask->val value
+ * - if negate form is identifierd, bgp_pbr_val_mask->mask set to 1
+ */
+static bool bgp_pbr_extract_enumerate_unary(struct bgp_pbr_match_val list[],
+ int num, uint8_t unary_operator,
+ void *valmask, uint8_t type_entry)
+{
+ int i = 0;
+ struct bgp_pbr_val_mask *and_valmask = NULL;
+ struct list *or_valmask = NULL;
+ bool ret;
+
+ if (valmask) {
+ if (unary_operator == OPERATOR_UNARY_AND) {
+ and_valmask = (struct bgp_pbr_val_mask *)valmask;
+ memset(and_valmask, 0, sizeof(struct bgp_pbr_val_mask));
+ } else if (unary_operator == OPERATOR_UNARY_OR) {
+ or_valmask = (struct list *)valmask;
+ }
+ }
+ for (i = 0; i < num; i++) {
+ if (i != 0 && list[i].unary_operator !=
+ unary_operator)
+ return false;
+ if (!(list[i].compare_operator &
+ OPERATOR_COMPARE_EQUAL_TO) &&
+ !(list[i].compare_operator &
+ OPERATOR_COMPARE_EXACT_MATCH)) {
+ if ((list[i].compare_operator &
+ OPERATOR_COMPARE_LESS_THAN) &&
+ (list[i].compare_operator &
+ OPERATOR_COMPARE_GREATER_THAN)) {
+ ret = bgp_pbr_extract_enumerate_unary_opposite(
+ unary_operator, and_valmask,
+ or_valmask, list[i].value,
+ type_entry);
+ if (!ret)
+ return ret;
+ continue;
+ }
+ return false;
+ }
+ if (unary_operator == OPERATOR_UNARY_AND && and_valmask) {
+ if (type_entry == FLOWSPEC_TCP_FLAGS)
+ and_valmask->mask |=
+ TCP_HEADER_ALL_FLAGS & list[i].value;
+ } else if (unary_operator == OPERATOR_UNARY_OR && or_valmask) {
+ and_valmask = XCALLOC(MTYPE_PBR_VALMASK,
+ sizeof(struct bgp_pbr_val_mask));
+ if (type_entry == FLOWSPEC_TCP_FLAGS) {
+ and_valmask->val = TCP_HEADER_ALL_FLAGS;
+ and_valmask->mask |=
+ TCP_HEADER_ALL_FLAGS & list[i].value;
+ } else if (type_entry == FLOWSPEC_DSCP ||
+ type_entry == FLOWSPEC_FLOW_LABEL ||
+ type_entry == FLOWSPEC_ICMP_TYPE ||
+ type_entry == FLOWSPEC_ICMP_CODE ||
+ type_entry == FLOWSPEC_FRAGMENT ||
+ type_entry == FLOWSPEC_PKT_LEN)
+ and_valmask->val = list[i].value;
+ listnode_add(or_valmask, and_valmask);
+ }
+ }
+ if (unary_operator == OPERATOR_UNARY_AND && and_valmask
+ && type_entry == FLOWSPEC_TCP_FLAGS)
+ and_valmask->val = TCP_HEADER_ALL_FLAGS;
+ return true;
+}
+
+/* if unary operator can either be UNARY_OR/AND/OR-AND.
+ * in the latter case, combinationf of both is not handled
+ */
+static bool bgp_pbr_extract_enumerate(struct bgp_pbr_match_val list[],
+ int num, uint8_t unary_operator,
+ void *valmask, uint8_t type_entry)
+{
+ bool ret;
+ uint8_t unary_operator_val;
+ bool double_check = false;
+
+ if ((unary_operator & OPERATOR_UNARY_OR) &&
+ (unary_operator & OPERATOR_UNARY_AND)) {
+ unary_operator_val = OPERATOR_UNARY_AND;
+ double_check = true;
+ } else
+ unary_operator_val = unary_operator;
+ ret = bgp_pbr_extract_enumerate_unary(list, num, unary_operator_val,
+ valmask, type_entry);
+ if (!ret && double_check)
+ ret = bgp_pbr_extract_enumerate_unary(list, num,
+ OPERATOR_UNARY_OR,
+ valmask,
+ type_entry);
+ return ret;
+}
+
+/* returns the unary operator that is in the list
+ * return 0 if both operators are used
+ */
+static uint8_t bgp_pbr_match_val_get_operator(struct bgp_pbr_match_val list[],
+ int num)
+
+{
+ int i;
+ uint8_t unary_operator = OPERATOR_UNARY_AND;
+
+ for (i = 0; i < num; i++) {
+ if (i == 0)
+ continue;
+ if (list[i].unary_operator & OPERATOR_UNARY_OR)
+ unary_operator = OPERATOR_UNARY_OR;
+ if ((list[i].unary_operator & OPERATOR_UNARY_AND
+ && unary_operator == OPERATOR_UNARY_OR) ||
+ (list[i].unary_operator & OPERATOR_UNARY_OR
+ && unary_operator == OPERATOR_UNARY_AND))
+ return 0;
+ }
+ return unary_operator;
+}
+
+
+/* return true if extraction ok
+ */
+static bool bgp_pbr_extract(struct bgp_pbr_match_val list[],
+ int num,
+ struct bgp_pbr_range_port *range)
+{
+ int i = 0;
+ bool exact_match = false;
+
+ if (range)
+ memset(range, 0, sizeof(struct bgp_pbr_range_port));
+
+ if (num > 2)
+ return false;
+ for (i = 0; i < num; i++) {
+ if (i != 0 && (list[i].compare_operator ==
+ OPERATOR_COMPARE_EQUAL_TO))
+ return false;
+ if (i == 0 && (list[i].compare_operator ==
+ OPERATOR_COMPARE_EQUAL_TO)) {
+ if (range)
+ range->min_port = list[i].value;
+ exact_match = true;
+ }
+ if (exact_match && i > 0)
+ return false;
+ if (list[i].compare_operator ==
+ (OPERATOR_COMPARE_GREATER_THAN +
+ OPERATOR_COMPARE_EQUAL_TO)) {
+ if (range)
+ range->min_port = list[i].value;
+ } else if (list[i].compare_operator ==
+ (OPERATOR_COMPARE_LESS_THAN +
+ OPERATOR_COMPARE_EQUAL_TO)) {
+ if (range)
+ range->max_port = list[i].value;
+ } else if (list[i].compare_operator ==
+ OPERATOR_COMPARE_LESS_THAN) {
+ if (range)
+ range->max_port = list[i].value - 1;
+ } else if (list[i].compare_operator ==
+ OPERATOR_COMPARE_GREATER_THAN) {
+ if (range)
+ range->min_port = list[i].value + 1;
+ }
+ }
+ return true;
+}
+
+static int bgp_pbr_validate_policy_route(struct bgp_pbr_entry_main *api)
+{
+ bool enumerate_icmp = false;
+
+ if (api->type == BGP_PBR_UNDEFINED) {
+ if (BGP_DEBUG(pbr, PBR))
+ zlog_debug("BGP: pbr entry undefined. cancel.");
+ return 0;
+ }
+ /* because bgp pbr entry may contain unsupported
+ * combinations, a message will be displayed here if
+ * not supported.
+ * for now, only match/set supported is
+ * - combination src/dst => redirect nexthop [ + rate]
+ * - combination src/dst => redirect VRF [ + rate]
+ * - combination src/dst => drop
+ * - combination srcport + @IP
+ */
+ if (api->match_protocol_num > 1) {
+ if (BGP_DEBUG(pbr, PBR))
+ zlog_debug("BGP: match protocol operations:multiple protocols ( %d). ignoring.",
+ api->match_protocol_num);
+ return 0;
+ }
+ if (api->src_prefix_offset > 0 ||
+ api->dst_prefix_offset > 0) {
+ if (BGP_DEBUG(pbr, PBR))
+ zlog_debug("BGP: match prefix offset:"
+ "implementation does not support it.");
+ return 0;
+ }
+ if (api->match_protocol_num == 1 &&
+ api->protocol[0].value != PROTOCOL_UDP &&
+ api->protocol[0].value != PROTOCOL_ICMP &&
+ api->protocol[0].value != PROTOCOL_ICMPV6 &&
+ api->protocol[0].value != PROTOCOL_TCP) {
+ if (BGP_DEBUG(pbr, PBR))
+ zlog_debug("BGP: match protocol operations:protocol (%d) not supported. ignoring",
+ api->match_protocol_num);
+ return 0;
+ }
+ if (!bgp_pbr_extract(api->src_port, api->match_src_port_num, NULL)) {
+ if (BGP_DEBUG(pbr, PBR))
+ zlog_debug("BGP: match src port operations:too complex. ignoring.");
+ return 0;
+ }
+ if (!bgp_pbr_extract(api->dst_port, api->match_dst_port_num, NULL)) {
+ if (BGP_DEBUG(pbr, PBR))
+ zlog_debug("BGP: match dst port operations:too complex. ignoring.");
+ return 0;
+ }
+ if (!bgp_pbr_extract_enumerate(api->tcpflags,
+ api->match_tcpflags_num,
+ OPERATOR_UNARY_AND |
+ OPERATOR_UNARY_OR, NULL,
+ FLOWSPEC_TCP_FLAGS)) {
+ if (BGP_DEBUG(pbr, PBR))
+ zlog_debug("BGP: match tcp flags:too complex. ignoring.");
+ return 0;
+ }
+ if (!bgp_pbr_extract(api->icmp_type, api->match_icmp_type_num, NULL)) {
+ if (!bgp_pbr_extract_enumerate(api->icmp_type,
+ api->match_icmp_type_num,
+ OPERATOR_UNARY_OR, NULL,
+ FLOWSPEC_ICMP_TYPE)) {
+ if (BGP_DEBUG(pbr, PBR))
+ zlog_debug("BGP: match icmp type operations:too complex. ignoring.");
+ return 0;
+ }
+ enumerate_icmp = true;
+ }
+ if (!bgp_pbr_extract(api->icmp_code, api->match_icmp_code_num, NULL)) {
+ if (!bgp_pbr_extract_enumerate(api->icmp_code,
+ api->match_icmp_code_num,
+ OPERATOR_UNARY_OR, NULL,
+ FLOWSPEC_ICMP_CODE)) {
+ if (BGP_DEBUG(pbr, PBR))
+ zlog_debug("BGP: match icmp code operations:too complex. ignoring.");
+ return 0;
+ } else if (api->match_icmp_type_num > 1 &&
+ !enumerate_icmp) {
+ if (BGP_DEBUG(pbr, PBR))
+ zlog_debug("BGP: match icmp code is enumerate, and icmp type is not. too complex. ignoring.");
+ return 0;
+ }
+ }
+ if (!bgp_pbr_extract(api->port, api->match_port_num, NULL)) {
+ if (BGP_DEBUG(pbr, PBR))
+ zlog_debug("BGP: match port operations:too complex. ignoring.");
+ return 0;
+ }
+ if (api->match_packet_length_num) {
+ bool ret;
+
+ ret = bgp_pbr_extract(api->packet_length,
+ api->match_packet_length_num, NULL);
+ if (!ret)
+ ret = bgp_pbr_extract_enumerate(api->packet_length,
+ api->match_packet_length_num,
+ OPERATOR_UNARY_OR
+ | OPERATOR_UNARY_AND,
+ NULL, FLOWSPEC_PKT_LEN);
+ if (!ret) {
+ if (BGP_DEBUG(pbr, PBR))
+ zlog_debug("BGP: match packet length operations:too complex. ignoring.");
+ return 0;
+ }
+ }
+ if (api->match_dscp_num) {
+ if (!bgp_pbr_extract_enumerate(api->dscp, api->match_dscp_num,
+ OPERATOR_UNARY_OR | OPERATOR_UNARY_AND,
+ NULL, FLOWSPEC_DSCP)) {
+ if (BGP_DEBUG(pbr, PBR))
+ zlog_debug("BGP: match DSCP operations:too complex. ignoring.");
+ return 0;
+ }
+ }
+ if (api->match_flowlabel_num) {
+ if (api->afi == AFI_IP) {
+ if (BGP_DEBUG(pbr, PBR))
+ zlog_debug("BGP: match Flow Label operations:"
+ "Not for IPv4.");
+ return 0;
+ }
+ if (!bgp_pbr_extract_enumerate(api->flow_label,
+ api->match_flowlabel_num,
+ OPERATOR_UNARY_OR | OPERATOR_UNARY_AND,
+ NULL, FLOWSPEC_FLOW_LABEL)) {
+ if (BGP_DEBUG(pbr, PBR))
+ zlog_debug("BGP: match FlowLabel operations:"
+ "too complex. ignoring.");
+ return 0;
+ }
+ if (BGP_DEBUG(pbr, PBR))
+ zlog_debug("BGP: match FlowLabel operations "
+ "not supported. ignoring.");
+ return 0;
+ }
+ if (api->match_fragment_num) {
+ char fail_str[64];
+ bool success;
+
+ success = bgp_pbr_extract_enumerate(api->fragment,
+ api->match_fragment_num,
+ OPERATOR_UNARY_OR
+ | OPERATOR_UNARY_AND,
+ NULL, FLOWSPEC_FRAGMENT);
+ if (success) {
+ int i;
+
+ for (i = 0; i < api->match_fragment_num; i++) {
+ if (api->fragment[i].value != 1 &&
+ api->fragment[i].value != 2 &&
+ api->fragment[i].value != 4 &&
+ api->fragment[i].value != 8) {
+ success = false;
+ snprintf(
+ fail_str, sizeof(fail_str),
+ "Value not valid (%d) for this implementation",
+ api->fragment[i].value);
+ }
+ if (api->afi == AFI_IP6 &&
+ api->fragment[i].value == 1) {
+ success = false;
+ snprintf(fail_str, sizeof(fail_str),
+ "IPv6 dont fragment match invalid (%d)",
+ api->fragment[i].value);
+ }
+ }
+ if (api->afi == AFI_IP6) {
+ success = false;
+ snprintf(fail_str, sizeof(fail_str),
+ "%s", IPV6_FRAGMENT_INVALID);
+ }
+ } else
+ snprintf(fail_str, sizeof(fail_str),
+ "too complex. ignoring");
+ if (!success) {
+ if (BGP_DEBUG(pbr, PBR))
+ zlog_debug("BGP: match fragment operation (%d) %s",
+ api->match_fragment_num,
+ fail_str);
+ return 0;
+ }
+ }
+
+ /* no combinations with both src_port and dst_port
+ * or port with src_port and dst_port
+ */
+ if (api->match_src_port_num + api->match_dst_port_num +
+ api->match_port_num > 3) {
+ if (BGP_DEBUG(pbr, PBR))
+ zlog_debug("BGP: match multiple port operations: too complex. ignoring.");
+ return 0;
+ }
+ if ((api->match_src_port_num || api->match_dst_port_num
+ || api->match_port_num) && (api->match_icmp_type_num
+ || api->match_icmp_code_num)) {
+ if (BGP_DEBUG(pbr, PBR))
+ zlog_debug("BGP: match multiple port/imcp operations: too complex. ignoring.");
+ return 0;
+ }
+ /* iprule only supports redirect IP */
+ if (api->type == BGP_PBR_IPRULE) {
+ int i;
+
+ for (i = 0; i < api->action_num; i++) {
+ if (api->actions[i].action == ACTION_TRAFFICRATE &&
+ api->actions[i].u.r.rate == 0) {
+ if (BGP_DEBUG(pbr, PBR)) {
+ bgp_pbr_print_policy_route(api);
+ zlog_debug("BGP: iprule match actions drop not supported");
+ }
+ return 0;
+ }
+ if (api->actions[i].action == ACTION_MARKING) {
+ if (BGP_DEBUG(pbr, PBR)) {
+ bgp_pbr_print_policy_route(api);
+ zlog_warn("PBR: iprule set DSCP/Flow Label %u not supported",
+ api->actions[i].u.marking_dscp);
+ }
+ }
+ if (api->actions[i].action == ACTION_REDIRECT) {
+ if (BGP_DEBUG(pbr, PBR)) {
+ bgp_pbr_print_policy_route(api);
+ zlog_warn("PBR: iprule redirect VRF %u not supported",
+ api->actions[i].u.redirect_vrf);
+ }
+ }
+ }
+
+ } else if (!(api->match_bitmask & PREFIX_SRC_PRESENT) &&
+ !(api->match_bitmask & PREFIX_DST_PRESENT)) {
+ if (BGP_DEBUG(pbr, PBR)) {
+ bgp_pbr_print_policy_route(api);
+ zlog_debug("BGP: match actions without src or dst address can not operate. ignoring.");
+ }
+ return 0;
+ }
+ return 1;
+}
+
+/* return -1 if build or validation failed */
+
+int bgp_pbr_build_and_validate_entry(const struct prefix *p,
+ struct bgp_path_info *path,
+ struct bgp_pbr_entry_main *api)
+{
+ int ret;
+ uint32_t i, action_count = 0;
+ struct ecommunity *ecom;
+ struct ecommunity_val *ecom_eval;
+ struct bgp_pbr_entry_action *api_action;
+ struct prefix *src = NULL, *dst = NULL;
+ int valid_prefix = 0;
+ struct bgp_pbr_entry_action *api_action_redirect_ip = NULL;
+ bool discard_action_found = false;
+ afi_t afi = family2afi(p->u.prefix_flowspec.family);
+
+ /* extract match from flowspec entries */
+ ret = bgp_flowspec_match_rules_fill((uint8_t *)p->u.prefix_flowspec.ptr,
+ p->u.prefix_flowspec.prefixlen, api, afi);
+ if (ret < 0)
+ return -1;
+ /* extract actiosn from flowspec ecom list */
+ if (path && bgp_attr_get_ecommunity(path->attr)) {
+ ecom = bgp_attr_get_ecommunity(path->attr);
+ for (i = 0; i < ecom->size; i++) {
+ ecom_eval = (struct ecommunity_val *)
+ (ecom->val + (i * ECOMMUNITY_SIZE));
+ action_count++;
+ if (action_count > ACTIONS_MAX_NUM) {
+ if (BGP_DEBUG(pbr, PBR_ERROR))
+ flog_err(
+ EC_BGP_FLOWSPEC_PACKET,
+ "%s: %s (max %u)",
+ __func__,
+ FSPEC_ACTION_EXCEED_LIMIT,
+ action_count);
+ break;
+ }
+ api_action = &api->actions[action_count - 1];
+
+ if ((ecom_eval->val[1] ==
+ (char)ECOMMUNITY_REDIRECT_VRF) &&
+ (ecom_eval->val[0] ==
+ (char)ECOMMUNITY_ENCODE_TRANS_EXP ||
+ ecom_eval->val[0] ==
+ (char)ECOMMUNITY_EXTENDED_COMMUNITY_PART_2 ||
+ ecom_eval->val[0] ==
+ (char)ECOMMUNITY_EXTENDED_COMMUNITY_PART_3)) {
+ struct ecommunity *eckey = ecommunity_new();
+ struct ecommunity_val ecom_copy;
+
+ memcpy(&ecom_copy, ecom_eval,
+ sizeof(struct ecommunity_val));
+ ecom_copy.val[0] &=
+ ~ECOMMUNITY_ENCODE_TRANS_EXP;
+ ecom_copy.val[1] = ECOMMUNITY_ROUTE_TARGET;
+ ecommunity_add_val(eckey, &ecom_copy,
+ false, false);
+
+ api_action->action = ACTION_REDIRECT;
+ api_action->u.redirect_vrf =
+ get_first_vrf_for_redirect_with_rt(
+ eckey);
+ ecommunity_free(&eckey);
+ } else if ((ecom_eval->val[0] ==
+ (char)ECOMMUNITY_ENCODE_REDIRECT_IP_NH) &&
+ (ecom_eval->val[1] ==
+ (char)ECOMMUNITY_REDIRECT_IP_NH)) {
+ /* in case the 2 ecom present,
+ * do not overwrite
+ * draft-ietf-idr-flowspec-redirect
+ */
+ if (api_action_redirect_ip &&
+ p->u.prefix_flowspec.family == AF_INET) {
+ if (api_action_redirect_ip->u
+ .zr.redirect_ip_v4.s_addr
+ != INADDR_ANY)
+ continue;
+ if (path->attr->nexthop.s_addr
+ == INADDR_ANY)
+ continue;
+ api_action_redirect_ip->u.zr
+ .redirect_ip_v4.s_addr =
+ path->attr->nexthop.s_addr;
+ api_action_redirect_ip->u.zr.duplicate
+ = ecom_eval->val[7];
+ continue;
+ } else if (api_action_redirect_ip &&
+ p->u.prefix_flowspec.family == AF_INET6) {
+ if (memcmp(&api_action_redirect_ip->u
+ .zr.redirect_ip_v6,
+ &in6addr_any,
+ sizeof(struct in6_addr)))
+ continue;
+ if (path->attr->mp_nexthop_len == 0 ||
+ path->attr->mp_nexthop_len ==
+ BGP_ATTR_NHLEN_IPV4 ||
+ path->attr->mp_nexthop_len ==
+ BGP_ATTR_NHLEN_VPNV4)
+ continue;
+ memcpy(&api_action_redirect_ip->u
+ .zr.redirect_ip_v6,
+ &path->attr->mp_nexthop_global,
+ sizeof(struct in6_addr));
+ api_action_redirect_ip->u.zr.duplicate
+ = ecom_eval->val[7];
+ continue;
+ } else if (p->u.prefix_flowspec.family ==
+ AF_INET) {
+ api_action->action = ACTION_REDIRECT_IP;
+ api_action->u.zr.redirect_ip_v4.s_addr =
+ path->attr->nexthop.s_addr;
+ api_action->u.zr.duplicate =
+ ecom_eval->val[7];
+ api_action_redirect_ip = api_action;
+ } else if (p->u.prefix_flowspec.family ==
+ AF_INET6) {
+ api_action->action = ACTION_REDIRECT_IP;
+ memcpy(&api_action->u
+ .zr.redirect_ip_v6,
+ &path->attr->mp_nexthop_global,
+ sizeof(struct in6_addr));
+ api_action->u.zr.duplicate
+ = ecom_eval->val[7];
+ api_action_redirect_ip = api_action;
+ }
+ } else if ((ecom_eval->val[0] ==
+ (char)ECOMMUNITY_ENCODE_IP) &&
+ (ecom_eval->val[1] ==
+ (char)ECOMMUNITY_FLOWSPEC_REDIRECT_IPV4)) {
+ /* in case the 2 ecom present,
+ * overwrite simpson draft
+ * update redirect ip fields
+ */
+ if (api_action_redirect_ip) {
+ memcpy(&(api_action_redirect_ip->u
+ .zr.redirect_ip_v4.s_addr),
+ (ecom_eval->val+2), 4);
+ api_action_redirect_ip->u
+ .zr.duplicate =
+ ecom_eval->val[7];
+ continue;
+ } else {
+ api_action->action = ACTION_REDIRECT_IP;
+ memcpy(&(api_action->u
+ .zr.redirect_ip_v4.s_addr),
+ (ecom_eval->val+2), 4);
+ api_action->u.zr.duplicate =
+ ecom_eval->val[7];
+ api_action_redirect_ip = api_action;
+ }
+ } else {
+ if (ecom_eval->val[0] !=
+ (char)ECOMMUNITY_ENCODE_TRANS_EXP)
+ continue;
+ ret = ecommunity_fill_pbr_action(ecom_eval,
+ api_action,
+ afi);
+ if (ret != 0)
+ continue;
+ if ((api_action->action == ACTION_TRAFFICRATE)
+ && api->actions[i].u.r.rate == 0)
+ discard_action_found = true;
+ }
+ api->action_num++;
+ }
+ }
+ if (path && path->attr && bgp_attr_get_ipv6_ecommunity(path->attr)) {
+ struct ecommunity_val_ipv6 *ipv6_ecom_eval;
+
+ ecom = bgp_attr_get_ipv6_ecommunity(path->attr);
+ for (i = 0; i < ecom->size; i++) {
+ ipv6_ecom_eval = (struct ecommunity_val_ipv6 *)
+ (ecom->val + (i * ecom->unit_size));
+ action_count++;
+ if (action_count > ACTIONS_MAX_NUM) {
+ if (BGP_DEBUG(pbr, PBR_ERROR))
+ flog_err(
+ EC_BGP_FLOWSPEC_PACKET,
+ "%s: flowspec actions exceeds limit (max %u)",
+ __func__, action_count);
+ break;
+ }
+ api_action = &api->actions[action_count - 1];
+ if ((ipv6_ecom_eval->val[1] ==
+ (char)ECOMMUNITY_FLOWSPEC_REDIRECT_IPV6) &&
+ (ipv6_ecom_eval->val[0] ==
+ (char)ECOMMUNITY_ENCODE_TRANS_EXP)) {
+ struct ecommunity *eckey = ecommunity_new();
+ struct ecommunity_val_ipv6 ecom_copy;
+
+ eckey->unit_size = IPV6_ECOMMUNITY_SIZE;
+ memcpy(&ecom_copy, ipv6_ecom_eval,
+ sizeof(struct ecommunity_val_ipv6));
+ ecom_copy.val[1] = ECOMMUNITY_ROUTE_TARGET;
+ ecommunity_add_val_ipv6(eckey, &ecom_copy,
+ false, false);
+ api_action->action = ACTION_REDIRECT;
+ api_action->u.redirect_vrf =
+ get_first_vrf_for_redirect_with_rt(
+ eckey);
+ ecommunity_free(&eckey);
+ api->action_num++;
+ }
+ }
+ }
+ /* if ECOMMUNITY_TRAFFIC_RATE = 0 as action
+ * then reduce the API action list to that action
+ */
+ if (api->action_num > 1 && discard_action_found) {
+ api->action_num = 1;
+ memset(&api->actions[0], 0,
+ sizeof(struct bgp_pbr_entry_action));
+ api->actions[0].action = ACTION_TRAFFICRATE;
+ }
+
+ /* validate if incoming matc/action is compatible
+ * with our policy routing engine
+ */
+ if (!bgp_pbr_validate_policy_route(api))
+ return -1;
+
+ /* check inconsistency in the match rule */
+ if (api->match_bitmask & PREFIX_SRC_PRESENT) {
+ src = &api->src_prefix;
+ afi = family2afi(src->family);
+ valid_prefix = 1;
+ }
+ if (api->match_bitmask & PREFIX_DST_PRESENT) {
+ dst = &api->dst_prefix;
+ if (valid_prefix && afi != family2afi(dst->family)) {
+ if (BGP_DEBUG(pbr, PBR)) {
+ bgp_pbr_print_policy_route(api);
+ zlog_debug("%s: inconsistency: no match for afi src and dst (%u/%u)",
+ __func__, afi, family2afi(dst->family));
+ }
+ return -1;
+ }
+ }
+ return 0;
+}
+
+static void bgp_pbr_match_entry_free(void *arg)
+{
+ struct bgp_pbr_match_entry *bpme;
+
+ bpme = (struct bgp_pbr_match_entry *)arg;
+
+ if (bpme->installed) {
+ bgp_send_pbr_ipset_entry_match(bpme, false);
+ bpme->installed = false;
+ bpme->backpointer = NULL;
+ }
+ XFREE(MTYPE_PBR_MATCH_ENTRY, bpme);
+}
+
+static void bgp_pbr_match_free(void *arg)
+{
+ struct bgp_pbr_match *bpm;
+
+ bpm = (struct bgp_pbr_match *)arg;
+
+ hash_clean(bpm->entry_hash, bgp_pbr_match_entry_free);
+
+ if (hashcount(bpm->entry_hash) == 0) {
+ /* delete iptable entry first */
+ /* then delete ipset match */
+ if (bpm->installed) {
+ if (bpm->installed_in_iptable) {
+ bgp_send_pbr_iptable(bpm->action,
+ bpm, false);
+ bpm->installed_in_iptable = false;
+ bpm->action->refcnt--;
+ }
+ bgp_send_pbr_ipset_match(bpm, false);
+ bpm->installed = false;
+ bpm->action = NULL;
+ }
+ }
+ hash_free(bpm->entry_hash);
+
+ XFREE(MTYPE_PBR_MATCH, bpm);
+}
+
+static void *bgp_pbr_match_alloc_intern(void *arg)
+{
+ struct bgp_pbr_match *bpm, *new;
+
+ bpm = (struct bgp_pbr_match *)arg;
+
+ new = XCALLOC(MTYPE_PBR_MATCH, sizeof(*new));
+ memcpy(new, bpm, sizeof(*bpm));
+
+ return new;
+}
+
+static void bgp_pbr_rule_free(void *arg)
+{
+ struct bgp_pbr_rule *bpr;
+
+ bpr = (struct bgp_pbr_rule *)arg;
+
+ /* delete iprule */
+ if (bpr->installed) {
+ bgp_send_pbr_rule_action(bpr->action, bpr, false);
+ bpr->installed = false;
+ bpr->action->refcnt--;
+ bpr->action = NULL;
+ }
+ XFREE(MTYPE_PBR_RULE, bpr);
+}
+
+static void *bgp_pbr_rule_alloc_intern(void *arg)
+{
+ struct bgp_pbr_rule *bpr, *new;
+
+ bpr = (struct bgp_pbr_rule *)arg;
+
+ new = XCALLOC(MTYPE_PBR_RULE, sizeof(*new));
+ memcpy(new, bpr, sizeof(*bpr));
+
+ return new;
+}
+
+static void bgp_pbr_bpa_remove(struct bgp_pbr_action *bpa)
+{
+ if ((bpa->refcnt == 0) && bpa->installed && bpa->table_id != 0) {
+ bgp_send_pbr_rule_action(bpa, NULL, false);
+ bgp_zebra_announce_default(bpa->bgp, &bpa->nh, bpa->afi,
+ bpa->table_id, false);
+ bpa->installed = false;
+ }
+}
+
+static void bgp_pbr_bpa_add(struct bgp_pbr_action *bpa)
+{
+ if (!bpa->installed && !bpa->install_in_progress) {
+ bgp_send_pbr_rule_action(bpa, NULL, true);
+ bgp_zebra_announce_default(bpa->bgp, &bpa->nh, bpa->afi,
+ bpa->table_id, true);
+ }
+}
+
+static void bgp_pbr_action_free(void *arg)
+{
+ struct bgp_pbr_action *bpa = arg;
+
+ bgp_pbr_bpa_remove(bpa);
+
+ XFREE(MTYPE_PBR_ACTION, bpa);
+}
+
+static void *bgp_pbr_action_alloc_intern(void *arg)
+{
+ struct bgp_pbr_action *bpa, *new;
+
+ bpa = (struct bgp_pbr_action *)arg;
+
+ new = XCALLOC(MTYPE_PBR_ACTION, sizeof(*new));
+
+ memcpy(new, bpa, sizeof(*bpa));
+
+ return new;
+}
+
+static void *bgp_pbr_match_entry_alloc_intern(void *arg)
+{
+ struct bgp_pbr_match_entry *bpme, *new;
+
+ bpme = (struct bgp_pbr_match_entry *)arg;
+
+ new = XCALLOC(MTYPE_PBR_MATCH_ENTRY, sizeof(*new));
+
+ memcpy(new, bpme, sizeof(*bpme));
+
+ return new;
+}
+
+uint32_t bgp_pbr_match_hash_key(const void *arg)
+{
+ const struct bgp_pbr_match *pbm = arg;
+ uint32_t key;
+
+ key = jhash_1word(pbm->vrf_id, 0x4312abde);
+ key = jhash_1word(pbm->flags, key);
+ key = jhash_1word(pbm->family, key);
+ key = jhash(&pbm->pkt_len_min, 2, key);
+ key = jhash(&pbm->pkt_len_max, 2, key);
+ key = jhash(&pbm->tcp_flags, 2, key);
+ key = jhash(&pbm->tcp_mask_flags, 2, key);
+ key = jhash(&pbm->dscp_value, 1, key);
+ key = jhash(&pbm->flow_label, 2, key);
+ key = jhash(&pbm->fragment, 1, key);
+ key = jhash(&pbm->protocol, 1, key);
+ return jhash_1word(pbm->type, key);
+}
+
+bool bgp_pbr_match_hash_equal(const void *arg1, const void *arg2)
+{
+ const struct bgp_pbr_match *r1, *r2;
+
+ r1 = (const struct bgp_pbr_match *)arg1;
+ r2 = (const struct bgp_pbr_match *)arg2;
+
+ if (r1->vrf_id != r2->vrf_id)
+ return false;
+
+ if (r1->family != r2->family)
+ return false;
+
+ if (r1->type != r2->type)
+ return false;
+
+ if (r1->flags != r2->flags)
+ return false;
+
+ if (r1->action != r2->action)
+ return false;
+
+ if (r1->pkt_len_min != r2->pkt_len_min)
+ return false;
+
+ if (r1->pkt_len_max != r2->pkt_len_max)
+ return false;
+
+ if (r1->tcp_flags != r2->tcp_flags)
+ return false;
+
+ if (r1->tcp_mask_flags != r2->tcp_mask_flags)
+ return false;
+
+ if (r1->dscp_value != r2->dscp_value)
+ return false;
+
+ if (r1->flow_label != r2->flow_label)
+ return false;
+
+ if (r1->fragment != r2->fragment)
+ return false;
+
+ if (r1->protocol != r2->protocol)
+ return false;
+ return true;
+}
+
+uint32_t bgp_pbr_rule_hash_key(const void *arg)
+{
+ const struct bgp_pbr_rule *pbr = arg;
+ uint32_t key;
+
+ key = prefix_hash_key(&pbr->src);
+ key = jhash_1word(pbr->vrf_id, key);
+ key = jhash_1word(pbr->flags, key);
+ return jhash_1word(prefix_hash_key(&pbr->dst), key);
+}
+
+bool bgp_pbr_rule_hash_equal(const void *arg1, const void *arg2)
+{
+ const struct bgp_pbr_rule *r1, *r2;
+
+ r1 = (const struct bgp_pbr_rule *)arg1;
+ r2 = (const struct bgp_pbr_rule *)arg2;
+
+ if (r1->vrf_id != r2->vrf_id)
+ return false;
+
+ if (r1->flags != r2->flags)
+ return false;
+
+ if (r1->action != r2->action)
+ return false;
+
+ if ((r1->flags & MATCH_IP_SRC_SET) &&
+ !prefix_same(&r1->src, &r2->src))
+ return false;
+
+ if ((r1->flags & MATCH_IP_DST_SET) &&
+ !prefix_same(&r1->dst, &r2->dst))
+ return false;
+
+ return true;
+}
+
+uint32_t bgp_pbr_match_entry_hash_key(const void *arg)
+{
+ const struct bgp_pbr_match_entry *pbme;
+ uint32_t key;
+
+ pbme = arg;
+ key = prefix_hash_key(&pbme->src);
+ key = jhash_1word(prefix_hash_key(&pbme->dst), key);
+ key = jhash(&pbme->dst_port_min, 2, key);
+ key = jhash(&pbme->src_port_min, 2, key);
+ key = jhash(&pbme->dst_port_max, 2, key);
+ key = jhash(&pbme->src_port_max, 2, key);
+ key = jhash(&pbme->proto, 1, key);
+
+ return key;
+}
+
+bool bgp_pbr_match_entry_hash_equal(const void *arg1, const void *arg2)
+{
+ const struct bgp_pbr_match_entry *r1, *r2;
+
+ r1 = (const struct bgp_pbr_match_entry *)arg1;
+ r2 = (const struct bgp_pbr_match_entry *)arg2;
+
+ /*
+ * on updates, comparing backpointer is not necessary
+ * unique value is self calculated
+ * rate is ignored for now
+ */
+
+ if (!prefix_same(&r1->src, &r2->src))
+ return false;
+
+ if (!prefix_same(&r1->dst, &r2->dst))
+ return false;
+
+ if (r1->src_port_min != r2->src_port_min)
+ return false;
+
+ if (r1->dst_port_min != r2->dst_port_min)
+ return false;
+
+ if (r1->src_port_max != r2->src_port_max)
+ return false;
+
+ if (r1->dst_port_max != r2->dst_port_max)
+ return false;
+
+ if (r1->proto != r2->proto)
+ return false;
+
+ return true;
+}
+
+uint32_t bgp_pbr_action_hash_key(const void *arg)
+{
+ const struct bgp_pbr_action *pbra;
+ uint32_t key;
+
+ pbra = arg;
+ key = jhash_1word(pbra->table_id, 0x4312abde);
+ key = jhash_1word(pbra->fwmark, key);
+ key = jhash_1word(pbra->afi, key);
+ return key;
+}
+
+bool bgp_pbr_action_hash_equal(const void *arg1, const void *arg2)
+{
+ const struct bgp_pbr_action *r1, *r2;
+
+ r1 = (const struct bgp_pbr_action *)arg1;
+ r2 = (const struct bgp_pbr_action *)arg2;
+
+ /* unique value is self calculated
+ * table and fwmark is self calculated
+ * rate is ignored
+ */
+ if (r1->vrf_id != r2->vrf_id)
+ return false;
+
+ if (r1->afi != r2->afi)
+ return false;
+
+ return nexthop_same(&r1->nh, &r2->nh);
+}
+
+struct bgp_pbr_rule *bgp_pbr_rule_lookup(vrf_id_t vrf_id,
+ uint32_t unique)
+{
+ struct bgp *bgp = bgp_lookup_by_vrf_id(vrf_id);
+ struct bgp_pbr_rule_unique bpru;
+
+ if (!bgp || unique == 0)
+ return NULL;
+ bpru.unique = unique;
+ bpru.bpr_found = NULL;
+ hash_walk(bgp->pbr_rule_hash, bgp_pbr_rule_walkcb, &bpru);
+ return bpru.bpr_found;
+}
+
+struct bgp_pbr_action *bgp_pbr_action_rule_lookup(vrf_id_t vrf_id,
+ uint32_t unique)
+{
+ struct bgp *bgp = bgp_lookup_by_vrf_id(vrf_id);
+ struct bgp_pbr_action_unique bpau;
+
+ if (!bgp || unique == 0)
+ return NULL;
+ bpau.unique = unique;
+ bpau.bpa_found = NULL;
+ hash_walk(bgp->pbr_action_hash, bgp_pbr_action_walkcb, &bpau);
+ return bpau.bpa_found;
+}
+
+struct bgp_pbr_match *bgp_pbr_match_ipset_lookup(vrf_id_t vrf_id,
+ uint32_t unique)
+{
+ struct bgp *bgp = bgp_lookup_by_vrf_id(vrf_id);
+ struct bgp_pbr_match_unique bpmu;
+
+ if (!bgp || unique == 0)
+ return NULL;
+ bpmu.unique = unique;
+ bpmu.bpm_found = NULL;
+ hash_walk(bgp->pbr_match_hash, bgp_pbr_match_walkcb, &bpmu);
+ return bpmu.bpm_found;
+}
+
+struct bgp_pbr_match_entry *bgp_pbr_match_ipset_entry_lookup(vrf_id_t vrf_id,
+ char *ipset_name,
+ uint32_t unique)
+{
+ struct bgp *bgp = bgp_lookup_by_vrf_id(vrf_id);
+ struct bgp_pbr_match_entry_unique bpmeu;
+ struct bgp_pbr_match_ipsetname bpmi;
+
+ if (!bgp || unique == 0)
+ return NULL;
+ bpmi.ipsetname = XCALLOC(MTYPE_TMP, ZEBRA_IPSET_NAME_SIZE);
+ snprintf(bpmi.ipsetname, ZEBRA_IPSET_NAME_SIZE, "%s", ipset_name);
+ bpmi.bpm_found = NULL;
+ hash_walk(bgp->pbr_match_hash, bgp_pbr_match_pername_walkcb, &bpmi);
+ XFREE(MTYPE_TMP, bpmi.ipsetname);
+ if (!bpmi.bpm_found)
+ return NULL;
+ bpmeu.bpme_found = NULL;
+ bpmeu.unique = unique;
+ hash_walk(bpmi.bpm_found->entry_hash,
+ bgp_pbr_match_entry_walkcb, &bpmeu);
+ return bpmeu.bpme_found;
+}
+
+struct bgp_pbr_match *bgp_pbr_match_iptable_lookup(vrf_id_t vrf_id,
+ uint32_t unique)
+{
+ struct bgp *bgp = bgp_lookup_by_vrf_id(vrf_id);
+ struct bgp_pbr_match_iptable_unique bpmiu;
+
+ if (!bgp || unique == 0)
+ return NULL;
+ bpmiu.unique = unique;
+ bpmiu.bpm_found = NULL;
+ hash_walk(bgp->pbr_match_hash, bgp_pbr_match_iptable_walkcb, &bpmiu);
+ return bpmiu.bpm_found;
+}
+
+void bgp_pbr_cleanup(struct bgp *bgp)
+{
+ if (bgp->pbr_match_hash) {
+ hash_clean(bgp->pbr_match_hash, bgp_pbr_match_free);
+ hash_free(bgp->pbr_match_hash);
+ bgp->pbr_match_hash = NULL;
+ }
+ if (bgp->pbr_rule_hash) {
+ hash_clean(bgp->pbr_rule_hash, bgp_pbr_rule_free);
+ hash_free(bgp->pbr_rule_hash);
+ bgp->pbr_rule_hash = NULL;
+ }
+ if (bgp->pbr_action_hash) {
+ hash_clean(bgp->pbr_action_hash, bgp_pbr_action_free);
+ hash_free(bgp->pbr_action_hash);
+ bgp->pbr_action_hash = NULL;
+ }
+ if (bgp->bgp_pbr_cfg == NULL)
+ return;
+ bgp_pbr_reset(bgp, AFI_IP);
+ bgp_pbr_reset(bgp, AFI_IP6);
+ XFREE(MTYPE_PBR, bgp->bgp_pbr_cfg);
+}
+
+void bgp_pbr_init(struct bgp *bgp)
+{
+ bgp->pbr_match_hash =
+ hash_create_size(8, bgp_pbr_match_hash_key,
+ bgp_pbr_match_hash_equal,
+ "Match Hash");
+ bgp->pbr_action_hash =
+ hash_create_size(8, bgp_pbr_action_hash_key,
+ bgp_pbr_action_hash_equal,
+ "Match Hash Entry");
+
+ bgp->pbr_rule_hash =
+ hash_create_size(8, bgp_pbr_rule_hash_key,
+ bgp_pbr_rule_hash_equal,
+ "Match Rule");
+
+ bgp->bgp_pbr_cfg = XCALLOC(MTYPE_PBR, sizeof(struct bgp_pbr_config));
+ bgp->bgp_pbr_cfg->pbr_interface_any_ipv4 = true;
+}
+
+void bgp_pbr_print_policy_route(struct bgp_pbr_entry_main *api)
+{
+ int i = 0;
+ char return_string[512];
+ char *ptr = return_string;
+ int nb_items = 0;
+ int delta, len = sizeof(return_string);
+
+ delta = snprintf(ptr, sizeof(return_string), "MATCH : ");
+ len -= delta;
+ ptr += delta;
+ if (api->match_bitmask & PREFIX_SRC_PRESENT) {
+ struct prefix *p = &(api->src_prefix);
+
+ if (api->src_prefix_offset)
+ delta = snprintfrr(ptr, len, "@src %pFX/off%u", p,
+ api->src_prefix_offset);
+ else
+ delta = snprintfrr(ptr, len, "@src %pFX", p);
+ len -= delta;
+ ptr += delta;
+ INCREMENT_DISPLAY(ptr, nb_items, len);
+ }
+ if (api->match_bitmask & PREFIX_DST_PRESENT) {
+ struct prefix *p = &(api->dst_prefix);
+
+ INCREMENT_DISPLAY(ptr, nb_items, len);
+ if (api->dst_prefix_offset)
+ delta = snprintfrr(ptr, len, "@dst %pFX/off%u", p,
+ api->dst_prefix_offset);
+ else
+ delta = snprintfrr(ptr, len, "@dst %pFX", p);
+ len -= delta;
+ ptr += delta;
+ }
+
+ if (api->match_protocol_num)
+ INCREMENT_DISPLAY(ptr, nb_items, len);
+ for (i = 0; i < api->match_protocol_num; i++) {
+ delta = snprintf_bgp_pbr_match_val(ptr, len, &api->protocol[i],
+ i > 0 ? NULL : "@proto ");
+ len -= delta;
+ ptr += delta;
+ }
+
+ if (api->match_src_port_num)
+ INCREMENT_DISPLAY(ptr, nb_items, len);
+ for (i = 0; i < api->match_src_port_num; i++) {
+ delta = snprintf_bgp_pbr_match_val(ptr, len, &api->src_port[i],
+ i > 0 ? NULL : "@srcport ");
+ len -= delta;
+ ptr += delta;
+ }
+
+ if (api->match_dst_port_num)
+ INCREMENT_DISPLAY(ptr, nb_items, len);
+ for (i = 0; i < api->match_dst_port_num; i++) {
+ delta = snprintf_bgp_pbr_match_val(ptr, len, &api->dst_port[i],
+ i > 0 ? NULL : "@dstport ");
+ len -= delta;
+ ptr += delta;
+ }
+
+ if (api->match_port_num)
+ INCREMENT_DISPLAY(ptr, nb_items, len);
+ for (i = 0; i < api->match_port_num; i++) {
+ delta = snprintf_bgp_pbr_match_val(ptr, len, &api->port[i],
+ i > 0 ? NULL : "@port ");
+ len -= delta;
+ ptr += delta;
+ }
+
+ if (api->match_icmp_type_num)
+ INCREMENT_DISPLAY(ptr, nb_items, len);
+ for (i = 0; i < api->match_icmp_type_num; i++) {
+ delta = snprintf_bgp_pbr_match_val(ptr, len, &api->icmp_type[i],
+ i > 0 ? NULL : "@icmptype ");
+ len -= delta;
+ ptr += delta;
+ }
+
+ if (api->match_icmp_code_num)
+ INCREMENT_DISPLAY(ptr, nb_items, len);
+ for (i = 0; i < api->match_icmp_code_num; i++) {
+ delta = snprintf_bgp_pbr_match_val(ptr, len, &api->icmp_code[i],
+ i > 0 ? NULL : "@icmpcode ");
+ len -= delta;
+ ptr += delta;
+ }
+
+ if (api->match_packet_length_num)
+ INCREMENT_DISPLAY(ptr, nb_items, len);
+ for (i = 0; i < api->match_packet_length_num; i++) {
+ delta = snprintf_bgp_pbr_match_val(ptr, len,
+ &api->packet_length[i],
+ i > 0 ? NULL : "@plen ");
+ len -= delta;
+ ptr += delta;
+ }
+
+ if (api->match_dscp_num)
+ INCREMENT_DISPLAY(ptr, nb_items, len);
+ for (i = 0; i < api->match_dscp_num; i++) {
+ delta = snprintf_bgp_pbr_match_val(ptr, len, &api->dscp[i],
+ i > 0 ? NULL : "@dscp ");
+ len -= delta;
+ ptr += delta;
+ }
+
+ if (api->match_flowlabel_num)
+ INCREMENT_DISPLAY(ptr, nb_items, len);
+ for (i = 0; i < api->match_flowlabel_num; i++) {
+ delta = snprintf_bgp_pbr_match_val(ptr, len,
+ &api->flow_label[i],
+ i > 0 ? NULL : "@flowlabel ");
+ len -= delta;
+ ptr += delta;
+ }
+
+ if (api->match_tcpflags_num)
+ INCREMENT_DISPLAY(ptr, nb_items, len);
+ for (i = 0; i < api->match_tcpflags_num; i++) {
+ delta = snprintf_bgp_pbr_match_val(ptr, len, &api->tcpflags[i],
+ i > 0 ? NULL : "@tcpflags ");
+ len -= delta;
+ ptr += delta;
+ }
+
+ if (api->match_fragment_num)
+ INCREMENT_DISPLAY(ptr, nb_items, len);
+ for (i = 0; i < api->match_fragment_num; i++) {
+ delta = snprintf_bgp_pbr_match_val(ptr, len, &api->fragment[i],
+ i > 0 ? NULL : "@fragment ");
+ len -= delta;
+ ptr += delta;
+ }
+
+ len = sizeof(return_string);
+ if (!nb_items) {
+ ptr = return_string;
+ } else {
+ len -= (ptr - return_string);
+ delta = snprintf(ptr, len, "; ");
+ len -= delta;
+ ptr += delta;
+ }
+ if (api->action_num) {
+ delta = snprintf(ptr, len, "SET : ");
+ len -= delta;
+ ptr += delta;
+ }
+ nb_items = 0;
+ for (i = 0; i < api->action_num; i++) {
+ switch (api->actions[i].action) {
+ case ACTION_TRAFFICRATE:
+ INCREMENT_DISPLAY(ptr, nb_items, len);
+ delta = snprintf(ptr, len, "@set rate %f",
+ api->actions[i].u.r.rate);
+ len -= delta;
+ ptr += delta;
+ break;
+ case ACTION_TRAFFIC_ACTION:
+ INCREMENT_DISPLAY(ptr, nb_items, len);
+ delta = snprintf(ptr, len, "@action ");
+ len -= delta;
+ ptr += delta;
+ if (api->actions[i].u.za.filter
+ & TRAFFIC_ACTION_TERMINATE) {
+ delta = snprintf(ptr, len,
+ " terminate (apply filter(s))");
+ len -= delta;
+ ptr += delta;
+ }
+ if (api->actions[i].u.za.filter
+ & TRAFFIC_ACTION_DISTRIBUTE) {
+ delta = snprintf(ptr, len, " distribute");
+ len -= delta;
+ ptr += delta;
+ }
+ if (api->actions[i].u.za.filter
+ & TRAFFIC_ACTION_SAMPLE) {
+ delta = snprintf(ptr, len, " sample");
+ len -= delta;
+ ptr += delta;
+ }
+ break;
+ case ACTION_REDIRECT_IP: {
+ char local_buff[INET6_ADDRSTRLEN];
+ void *ptr_ip;
+
+ INCREMENT_DISPLAY(ptr, nb_items, len);
+ if (api->afi == AF_INET)
+ ptr_ip = &api->actions[i].u.zr.redirect_ip_v4;
+ else
+ ptr_ip = &api->actions[i].u.zr.redirect_ip_v6;
+ if (inet_ntop(afi2family(api->afi),
+ ptr_ip, local_buff,
+ INET6_ADDRSTRLEN) != NULL) {
+ delta = snprintf(ptr, len,
+ "@redirect ip nh %s", local_buff);
+ len -= delta;
+ ptr += delta;
+ }
+ break;
+ }
+ case ACTION_REDIRECT: {
+ struct vrf *vrf;
+
+ vrf = vrf_lookup_by_id(api->actions[i].u.redirect_vrf);
+ INCREMENT_DISPLAY(ptr, nb_items, len);
+ delta = snprintf(ptr, len, "@redirect vrf %s(%u)",
+ VRF_LOGNAME(vrf),
+ api->actions[i].u.redirect_vrf);
+ len -= delta;
+ ptr += delta;
+ break;
+ }
+ case ACTION_MARKING:
+ INCREMENT_DISPLAY(ptr, nb_items, len);
+ delta = snprintf(ptr, len, "@set dscp/flowlabel %u",
+ api->actions[i].u.marking_dscp);
+ len -= delta;
+ ptr += delta;
+ break;
+ default:
+ break;
+ }
+ }
+ zlog_info("%s", return_string);
+}
+
+static void bgp_pbr_flush_iprule(struct bgp *bgp, struct bgp_pbr_action *bpa,
+ struct bgp_pbr_rule *bpr)
+{
+ /* if bpr is null, do nothing
+ */
+ if (bpr == NULL)
+ return;
+ if (bpr->installed) {
+ bgp_send_pbr_rule_action(bpa, bpr, false);
+ bpr->installed = false;
+ bpr->action->refcnt--;
+ bpr->action = NULL;
+ if (bpr->path) {
+ struct bgp_path_info *path;
+ struct bgp_path_info_extra *extra;
+
+ /* unlink path to bpme */
+ path = (struct bgp_path_info *)bpr->path;
+ extra = bgp_path_info_extra_get(path);
+ if (extra->bgp_fs_iprule)
+ listnode_delete(extra->bgp_fs_iprule, bpr);
+ bpr->path = NULL;
+ }
+ }
+ hash_release(bgp->pbr_rule_hash, bpr);
+ bgp_pbr_bpa_remove(bpa);
+}
+
+static void bgp_pbr_flush_entry(struct bgp *bgp, struct bgp_pbr_action *bpa,
+ struct bgp_pbr_match *bpm,
+ struct bgp_pbr_match_entry *bpme)
+{
+ /* if bpme is null, bpm is also null
+ */
+ if (bpme == NULL)
+ return;
+ /* ipset del entry */
+ if (bpme->installed) {
+ bgp_send_pbr_ipset_entry_match(bpme, false);
+ bpme->installed = false;
+ bpme->backpointer = NULL;
+ if (bpme->path) {
+ struct bgp_path_info *path;
+ struct bgp_path_info_extra *extra;
+
+ /* unlink path to bpme */
+ path = (struct bgp_path_info *)bpme->path;
+ extra = bgp_path_info_extra_get(path);
+ if (extra->bgp_fs_pbr)
+ listnode_delete(extra->bgp_fs_pbr, bpme);
+ bpme->path = NULL;
+ }
+ }
+ hash_release(bpm->entry_hash, bpme);
+ if (hashcount(bpm->entry_hash) == 0) {
+ /* delete iptable entry first */
+ /* then delete ipset match */
+ if (bpm->installed) {
+ if (bpm->installed_in_iptable) {
+ bgp_send_pbr_iptable(bpm->action,
+ bpm, false);
+ bpm->installed_in_iptable = false;
+ bpm->action->refcnt--;
+ }
+ bgp_send_pbr_ipset_match(bpm, false);
+ bpm->installed = false;
+ bpm->action = NULL;
+ }
+ hash_release(bgp->pbr_match_hash, bpm);
+ /* XXX release pbr_match_action if not used
+ * note that drop does not need to call send_pbr_action
+ */
+ }
+ bgp_pbr_bpa_remove(bpa);
+}
+
+struct bgp_pbr_match_entry_remain {
+ struct bgp_pbr_match_entry *bpme_to_match;
+ struct bgp_pbr_match_entry *bpme_found;
+};
+
+struct bgp_pbr_rule_remain {
+ struct bgp_pbr_rule *bpr_to_match;
+ struct bgp_pbr_rule *bpr_found;
+};
+
+static int bgp_pbr_get_same_rule(struct hash_bucket *bucket, void *arg)
+{
+ struct bgp_pbr_rule *r1 = (struct bgp_pbr_rule *)bucket->data;
+ struct bgp_pbr_rule_remain *ctxt =
+ (struct bgp_pbr_rule_remain *)arg;
+ struct bgp_pbr_rule *r2;
+
+ r2 = ctxt->bpr_to_match;
+
+ if (r1->vrf_id != r2->vrf_id)
+ return HASHWALK_CONTINUE;
+
+ if (r1->flags != r2->flags)
+ return HASHWALK_CONTINUE;
+
+ if ((r1->flags & MATCH_IP_SRC_SET) &&
+ !prefix_same(&r1->src, &r2->src))
+ return HASHWALK_CONTINUE;
+
+ if ((r1->flags & MATCH_IP_DST_SET) &&
+ !prefix_same(&r1->dst, &r2->dst))
+ return HASHWALK_CONTINUE;
+
+ /* this function is used for two cases:
+ * - remove an entry upon withdraw request
+ * (case r2->action is null)
+ * - replace an old iprule with different action
+ * (case r2->action is != null)
+ * the old one is removed after the new one
+ * this is to avoid disruption in traffic
+ */
+ if (r2->action == NULL ||
+ r1->action != r2->action) {
+ ctxt->bpr_found = r1;
+ return HASHWALK_ABORT;
+ }
+ return HASHWALK_CONTINUE;
+}
+
+static int bgp_pbr_get_remaining_entry(struct hash_bucket *bucket, void *arg)
+{
+ struct bgp_pbr_match *bpm = (struct bgp_pbr_match *)bucket->data;
+ struct bgp_pbr_match_entry_remain *bpmer =
+ (struct bgp_pbr_match_entry_remain *)arg;
+ struct bgp_pbr_match *bpm_temp;
+ struct bgp_pbr_match_entry *bpme = bpmer->bpme_to_match;
+
+ if (!bpme->backpointer ||
+ bpm == bpme->backpointer ||
+ bpme->backpointer->action == bpm->action)
+ return HASHWALK_CONTINUE;
+ /* ensure bpm other characteristics are equal */
+ bpm_temp = bpme->backpointer;
+ if (bpm_temp->vrf_id != bpm->vrf_id ||
+ bpm_temp->type != bpm->type ||
+ bpm_temp->flags != bpm->flags ||
+ bpm_temp->tcp_flags != bpm->tcp_flags ||
+ bpm_temp->tcp_mask_flags != bpm->tcp_mask_flags ||
+ bpm_temp->pkt_len_min != bpm->pkt_len_min ||
+ bpm_temp->pkt_len_max != bpm->pkt_len_max ||
+ bpm_temp->dscp_value != bpm->dscp_value ||
+ bpm_temp->flow_label != bpm->flow_label ||
+ bpm_temp->family != bpm->family ||
+ bpm_temp->fragment != bpm->fragment)
+ return HASHWALK_CONTINUE;
+
+ /* look for remaining bpme */
+ bpmer->bpme_found = hash_lookup(bpm->entry_hash, bpme);
+ if (!bpmer->bpme_found)
+ return HASHWALK_CONTINUE;
+ return HASHWALK_ABORT;
+}
+
+static void bgp_pbr_policyroute_remove_from_zebra_unit(
+ struct bgp *bgp, struct bgp_path_info *path, struct bgp_pbr_filter *bpf)
+{
+ struct bgp_pbr_match temp;
+ struct bgp_pbr_match_entry temp2;
+ struct bgp_pbr_rule pbr_rule;
+ struct bgp_pbr_rule *bpr;
+ struct bgp_pbr_match *bpm;
+ struct bgp_pbr_match_entry *bpme;
+ struct bgp_pbr_match_entry_remain bpmer;
+ struct bgp_pbr_range_port *src_port;
+ struct bgp_pbr_range_port *dst_port;
+ struct bgp_pbr_range_port *pkt_len;
+ struct bgp_pbr_rule_remain bprr;
+
+ if (!bpf)
+ return;
+ src_port = bpf->src_port;
+ dst_port = bpf->dst_port;
+ pkt_len = bpf->pkt_len;
+
+ if (BGP_DEBUG(zebra, ZEBRA))
+ bgp_pbr_dump_entry(bpf, false);
+
+ /* as we don't know information from EC
+ * look for bpm that have the bpm
+ * with vrf_id characteristics
+ */
+ memset(&temp2, 0, sizeof(temp2));
+ memset(&temp, 0, sizeof(temp));
+
+ if (bpf->type == BGP_PBR_IPRULE) {
+ memset(&pbr_rule, 0, sizeof(pbr_rule));
+ pbr_rule.vrf_id = bpf->vrf_id;
+ if (bpf->src) {
+ prefix_copy(&pbr_rule.src, bpf->src);
+ pbr_rule.flags |= MATCH_IP_SRC_SET;
+ }
+ if (bpf->dst) {
+ prefix_copy(&pbr_rule.dst, bpf->dst);
+ pbr_rule.flags |= MATCH_IP_DST_SET;
+ }
+ bpr = &pbr_rule;
+ /* A previous entry may already exist
+ * flush previous entry if necessary
+ */
+ bprr.bpr_to_match = bpr;
+ bprr.bpr_found = NULL;
+ hash_walk(bgp->pbr_rule_hash, bgp_pbr_get_same_rule, &bprr);
+ if (bprr.bpr_found) {
+ static struct bgp_pbr_rule *local_bpr;
+ static struct bgp_pbr_action *local_bpa;
+
+ local_bpr = bprr.bpr_found;
+ local_bpa = local_bpr->action;
+ bgp_pbr_flush_iprule(bgp, local_bpa,
+ local_bpr);
+ }
+ return;
+ }
+
+ temp.family = bpf->family;
+ if (bpf->src) {
+ temp.flags |= MATCH_IP_SRC_SET;
+ prefix_copy(&temp2.src, bpf->src);
+ } else
+ temp2.src.family = bpf->family;
+ if (bpf->dst) {
+ temp.flags |= MATCH_IP_DST_SET;
+ prefix_copy(&temp2.dst, bpf->dst);
+ } else
+ temp2.dst.family = bpf->family;
+ if (src_port && (src_port->min_port || bpf->protocol == IPPROTO_ICMP)) {
+ if (bpf->protocol == IPPROTO_ICMP)
+ temp.flags |= MATCH_ICMP_SET;
+ temp.flags |= MATCH_PORT_SRC_SET;
+ temp2.src_port_min = src_port->min_port;
+ if (src_port->max_port) {
+ temp.flags |= MATCH_PORT_SRC_RANGE_SET;
+ temp2.src_port_max = src_port->max_port;
+ }
+ }
+ if (dst_port && (dst_port->min_port || bpf->protocol == IPPROTO_ICMP)) {
+ if (bpf->protocol == IPPROTO_ICMP)
+ temp.flags |= MATCH_ICMP_SET;
+ temp.flags |= MATCH_PORT_DST_SET;
+ temp2.dst_port_min = dst_port->min_port;
+ if (dst_port->max_port) {
+ temp.flags |= MATCH_PORT_DST_RANGE_SET;
+ temp2.dst_port_max = dst_port->max_port;
+ }
+ }
+ temp2.proto = bpf->protocol;
+
+ if (pkt_len) {
+ temp.pkt_len_min = pkt_len->min_port;
+ if (pkt_len->max_port)
+ temp.pkt_len_max = pkt_len->max_port;
+ } else if (bpf->pkt_len_val) {
+ if (bpf->pkt_len_val->mask)
+ temp.flags |= MATCH_PKT_LEN_INVERSE_SET;
+ temp.pkt_len_min = bpf->pkt_len_val->val;
+ }
+ if (bpf->tcp_flags) {
+ temp.tcp_flags = bpf->tcp_flags->val;
+ temp.tcp_mask_flags = bpf->tcp_flags->mask;
+ }
+ if (bpf->dscp) {
+ if (bpf->dscp->mask)
+ temp.flags |= MATCH_DSCP_INVERSE_SET;
+ else
+ temp.flags |= MATCH_DSCP_SET;
+ temp.dscp_value = bpf->dscp->val;
+ }
+ if (bpf->flow_label) {
+ if (bpf->flow_label->mask)
+ temp.flags |= MATCH_FLOW_LABEL_INVERSE_SET;
+ else
+ temp.flags |= MATCH_FLOW_LABEL_SET;
+ temp.flow_label = bpf->flow_label->val;
+ }
+
+ if (bpf->fragment) {
+ if (bpf->fragment->mask)
+ temp.flags |= MATCH_FRAGMENT_INVERSE_SET;
+ temp.fragment = bpf->fragment->val;
+ }
+
+ if (bpf->src == NULL || bpf->dst == NULL) {
+ if (temp.flags & (MATCH_PORT_DST_SET | MATCH_PORT_SRC_SET))
+ temp.type = IPSET_NET_PORT;
+ else
+ temp.type = IPSET_NET;
+ } else {
+ if (temp.flags & (MATCH_PORT_DST_SET | MATCH_PORT_SRC_SET))
+ temp.type = IPSET_NET_PORT_NET;
+ else
+ temp.type = IPSET_NET_NET;
+ }
+ if (bpf->vrf_id == VRF_UNKNOWN) /* XXX case BGP destroy */
+ temp.vrf_id = VRF_DEFAULT;
+ else
+ temp.vrf_id = bpf->vrf_id;
+ bpme = &temp2;
+ bpm = &temp;
+ bpme->backpointer = bpm;
+ /* right now, a previous entry may already exist
+ * flush previous entry if necessary
+ */
+ bpmer.bpme_to_match = bpme;
+ bpmer.bpme_found = NULL;
+ hash_walk(bgp->pbr_match_hash, bgp_pbr_get_remaining_entry, &bpmer);
+ if (bpmer.bpme_found) {
+ static struct bgp_pbr_match *local_bpm;
+ static struct bgp_pbr_action *local_bpa;
+
+ local_bpm = bpmer.bpme_found->backpointer;
+ local_bpa = local_bpm->action;
+ bgp_pbr_flush_entry(bgp, local_bpa,
+ local_bpm, bpmer.bpme_found);
+ }
+}
+
+static uint8_t bgp_pbr_next_type_entry(uint8_t type_entry)
+{
+ if (type_entry == FLOWSPEC_TCP_FLAGS)
+ return FLOWSPEC_DSCP;
+ if (type_entry == FLOWSPEC_DSCP)
+ return FLOWSPEC_FLOW_LABEL;
+ if (type_entry == FLOWSPEC_FLOW_LABEL)
+ return FLOWSPEC_PKT_LEN;
+ if (type_entry == FLOWSPEC_PKT_LEN)
+ return FLOWSPEC_FRAGMENT;
+ if (type_entry == FLOWSPEC_FRAGMENT)
+ return FLOWSPEC_ICMP_TYPE;
+ return 0;
+}
+
+static void bgp_pbr_icmp_action(struct bgp *bgp, struct bgp_path_info *path,
+ struct bgp_pbr_filter *bpf,
+ struct bgp_pbr_or_filter *bpof, bool add,
+ struct nexthop *nh, float *rate)
+{
+ struct bgp_pbr_range_port srcp, dstp;
+ struct bgp_pbr_val_mask *icmp_type, *icmp_code;
+ struct listnode *tnode, *cnode;
+
+ if (!bpf)
+ return;
+ if (bpf->protocol != IPPROTO_ICMP)
+ return;
+
+ memset(&srcp, 0, sizeof(srcp));
+ memset(&dstp, 0, sizeof(dstp));
+ bpf->src_port = &srcp;
+ bpf->dst_port = &dstp;
+ /* parse icmp type and lookup appropriate icmp code
+ * if no icmp code found, create as many entryes as
+ * there are listed icmp codes for that icmp type
+ */
+ if (!bpof->icmp_type) {
+ srcp.min_port = 0;
+ srcp.max_port = 255;
+ for (ALL_LIST_ELEMENTS_RO(bpof->icmp_code, cnode, icmp_code)) {
+ dstp.min_port = icmp_code->val;
+ if (add)
+ bgp_pbr_policyroute_add_to_zebra_unit(
+ bgp, path, bpf, nh, rate);
+ else
+ bgp_pbr_policyroute_remove_from_zebra_unit(
+ bgp, path, bpf);
+ }
+ return;
+ }
+ for (ALL_LIST_ELEMENTS_RO(bpof->icmp_type, tnode, icmp_type)) {
+ srcp.min_port = icmp_type->val;
+ srcp.max_port = 0;
+ dstp.max_port = 0;
+ /* only icmp type. create an entry only with icmp type */
+ if (!bpof->icmp_code) {
+ /* icmp type is not one of the above
+ * forge an entry only based on the icmp type
+ */
+ dstp.min_port = 0;
+ dstp.max_port = 255;
+ if (add)
+ bgp_pbr_policyroute_add_to_zebra_unit(
+ bgp, path, bpf, nh, rate);
+ else
+ bgp_pbr_policyroute_remove_from_zebra_unit(
+ bgp, path, bpf);
+ continue;
+ }
+ for (ALL_LIST_ELEMENTS_RO(bpof->icmp_code, cnode, icmp_code)) {
+ dstp.min_port = icmp_code->val;
+ if (add)
+ bgp_pbr_policyroute_add_to_zebra_unit(
+ bgp, path, bpf, nh, rate);
+ else
+ bgp_pbr_policyroute_remove_from_zebra_unit(
+ bgp, path, bpf);
+ }
+ }
+
+ bpf->src_port = NULL;
+ bpf->dst_port = NULL;
+}
+
+static void bgp_pbr_policyroute_remove_from_zebra_recursive(
+ struct bgp *bgp, struct bgp_path_info *path, struct bgp_pbr_filter *bpf,
+ struct bgp_pbr_or_filter *bpof, uint8_t type_entry)
+{
+ struct listnode *node, *nnode;
+ struct bgp_pbr_val_mask *valmask;
+ uint8_t next_type_entry;
+ struct list *orig_list;
+ struct bgp_pbr_val_mask **target_val;
+
+ if (type_entry == 0) {
+ bgp_pbr_policyroute_remove_from_zebra_unit(bgp, path, bpf);
+ return;
+ }
+ next_type_entry = bgp_pbr_next_type_entry(type_entry);
+ if (type_entry == FLOWSPEC_TCP_FLAGS && bpof->tcpflags) {
+ orig_list = bpof->tcpflags;
+ target_val = &bpf->tcp_flags;
+ } else if (type_entry == FLOWSPEC_DSCP && bpof->dscp) {
+ orig_list = bpof->dscp;
+ target_val = &bpf->dscp;
+ } else if (type_entry == FLOWSPEC_FLOW_LABEL && bpof->flowlabel) {
+ orig_list = bpof->flowlabel;
+ target_val = &bpf->flow_label;
+ } else if (type_entry == FLOWSPEC_PKT_LEN && bpof->pkt_len) {
+ orig_list = bpof->pkt_len;
+ target_val = &bpf->pkt_len_val;
+ } else if (type_entry == FLOWSPEC_FRAGMENT && bpof->fragment) {
+ orig_list = bpof->fragment;
+ target_val = &bpf->fragment;
+ } else if (type_entry == FLOWSPEC_ICMP_TYPE &&
+ (bpof->icmp_type || bpof->icmp_code)) {
+ /* enumerate list for icmp - must be last one */
+ bgp_pbr_icmp_action(bgp, path, bpf, bpof, false, NULL, NULL);
+ return;
+ } else {
+ bgp_pbr_policyroute_remove_from_zebra_recursive(
+ bgp, path, bpf, bpof, next_type_entry);
+ return;
+ }
+ for (ALL_LIST_ELEMENTS(orig_list, node, nnode, valmask)) {
+ *target_val = valmask;
+ bgp_pbr_policyroute_remove_from_zebra_recursive(
+ bgp, path, bpf, bpof, next_type_entry);
+ }
+}
+
+static void bgp_pbr_policyroute_remove_from_zebra(
+ struct bgp *bgp, struct bgp_path_info *path, struct bgp_pbr_filter *bpf,
+ struct bgp_pbr_or_filter *bpof)
+{
+ if (!bpof) {
+ bgp_pbr_policyroute_remove_from_zebra_unit(bgp, path, bpf);
+ return;
+ }
+ if (bpof->tcpflags)
+ bgp_pbr_policyroute_remove_from_zebra_recursive(
+ bgp, path, bpf, bpof, FLOWSPEC_TCP_FLAGS);
+ else if (bpof->dscp)
+ bgp_pbr_policyroute_remove_from_zebra_recursive(
+ bgp, path, bpf, bpof, FLOWSPEC_DSCP);
+ else if (bpof->flowlabel)
+ bgp_pbr_policyroute_remove_from_zebra_recursive(
+ bgp, path, bpf, bpof, FLOWSPEC_FLOW_LABEL);
+ else if (bpof->pkt_len)
+ bgp_pbr_policyroute_remove_from_zebra_recursive(
+ bgp, path, bpf, bpof, FLOWSPEC_PKT_LEN);
+ else if (bpof->fragment)
+ bgp_pbr_policyroute_remove_from_zebra_recursive(
+ bgp, path, bpf, bpof, FLOWSPEC_FRAGMENT);
+ else if (bpof->icmp_type || bpof->icmp_code)
+ bgp_pbr_policyroute_remove_from_zebra_recursive(
+ bgp, path, bpf, bpof, FLOWSPEC_ICMP_TYPE);
+ else
+ bgp_pbr_policyroute_remove_from_zebra_unit(bgp, path, bpf);
+ /* flush bpof */
+ if (bpof->tcpflags)
+ list_delete_all_node(bpof->tcpflags);
+ if (bpof->dscp)
+ list_delete_all_node(bpof->dscp);
+ if (bpof->flowlabel)
+ list_delete_all_node(bpof->flowlabel);
+ if (bpof->pkt_len)
+ list_delete_all_node(bpof->pkt_len);
+ if (bpof->fragment)
+ list_delete_all_node(bpof->fragment);
+}
+
+static void bgp_pbr_dump_entry(struct bgp_pbr_filter *bpf, bool add)
+{
+ struct bgp_pbr_range_port *src_port;
+ struct bgp_pbr_range_port *dst_port;
+ struct bgp_pbr_range_port *pkt_len;
+ char bufsrc[64], bufdst[64];
+ char buffer[64];
+ int remaining_len = 0;
+ char protocol_str[16];
+
+ if (!bpf)
+ return;
+ src_port = bpf->src_port;
+ dst_port = bpf->dst_port;
+ pkt_len = bpf->pkt_len;
+
+ protocol_str[0] = '\0';
+ if (bpf->tcp_flags && bpf->tcp_flags->mask)
+ bpf->protocol = IPPROTO_TCP;
+ if (bpf->protocol)
+ snprintf(protocol_str, sizeof(protocol_str),
+ "proto %d", bpf->protocol);
+ buffer[0] = '\0';
+ if (bpf->protocol == IPPROTO_ICMP && src_port && dst_port)
+ remaining_len += snprintf(buffer, sizeof(buffer),
+ "type %d, code %d",
+ src_port->min_port,
+ dst_port->min_port);
+ else if (bpf->protocol == IPPROTO_UDP ||
+ bpf->protocol == IPPROTO_TCP) {
+
+ if (src_port && src_port->min_port)
+ remaining_len += snprintf(buffer,
+ sizeof(buffer),
+ "from [%u:%u]",
+ src_port->min_port,
+ src_port->max_port ?
+ src_port->max_port :
+ src_port->min_port);
+ if (dst_port && dst_port->min_port)
+ remaining_len += snprintf(buffer +
+ remaining_len,
+ sizeof(buffer)
+ - remaining_len,
+ "to [%u:%u]",
+ dst_port->min_port,
+ dst_port->max_port ?
+ dst_port->max_port :
+ dst_port->min_port);
+ }
+ if (pkt_len && (pkt_len->min_port || pkt_len->max_port)) {
+ remaining_len += snprintf(buffer + remaining_len,
+ sizeof(buffer)
+ - remaining_len,
+ " len [%u:%u]",
+ pkt_len->min_port,
+ pkt_len->max_port ?
+ pkt_len->max_port :
+ pkt_len->min_port);
+ } else if (bpf->pkt_len_val) {
+ remaining_len += snprintf(buffer + remaining_len,
+ sizeof(buffer)
+ - remaining_len,
+ " %s len %u",
+ bpf->pkt_len_val->mask
+ ? "!" : "",
+ bpf->pkt_len_val->val);
+ }
+ if (bpf->tcp_flags) {
+ remaining_len += snprintf(buffer + remaining_len,
+ sizeof(buffer)
+ - remaining_len,
+ "tcpflags %x/%x",
+ bpf->tcp_flags->val,
+ bpf->tcp_flags->mask);
+ }
+ if (bpf->dscp) {
+ snprintf(buffer + remaining_len,
+ sizeof(buffer)
+ - remaining_len,
+ "%s dscp %d",
+ bpf->dscp->mask
+ ? "!" : "",
+ bpf->dscp->val);
+ }
+ if (bpf->flow_label) {
+ snprintf(buffer + remaining_len,
+ sizeof(buffer)
+ - remaining_len,
+ "%s flow_label %d",
+ bpf->flow_label->mask
+ ? "!" : "",
+ bpf->flow_label->val);
+ }
+ zlog_debug("BGP: %s FS PBR from %s to %s, %s %s",
+ add ? "adding" : "removing",
+ bpf->src == NULL ? "<all>" :
+ prefix2str(bpf->src, bufsrc, sizeof(bufsrc)),
+ bpf->dst == NULL ? "<all>" :
+ prefix2str(bpf->dst, bufdst, sizeof(bufdst)),
+ protocol_str, buffer);
+
+}
+
+static void bgp_pbr_policyroute_add_to_zebra_unit(struct bgp *bgp,
+ struct bgp_path_info *path,
+ struct bgp_pbr_filter *bpf,
+ struct nexthop *nh,
+ float *rate)
+{
+ struct bgp_pbr_match temp;
+ struct bgp_pbr_match_entry temp2;
+ struct bgp_pbr_match *bpm;
+ struct bgp_pbr_match_entry *bpme = NULL;
+ struct bgp_pbr_action temp3;
+ struct bgp_pbr_action *bpa = NULL;
+ struct bgp_pbr_match_entry_remain bpmer;
+ struct bgp_pbr_rule_remain bprr;
+ struct bgp_pbr_range_port *src_port;
+ struct bgp_pbr_range_port *dst_port;
+ struct bgp_pbr_range_port *pkt_len;
+ struct bgp_pbr_rule pbr_rule;
+ struct bgp_pbr_rule *bpr;
+ bool bpr_found = false;
+ bool bpme_found = false;
+ struct vrf *vrf = NULL;
+
+ if (!bpf)
+ return;
+ src_port = bpf->src_port;
+ dst_port = bpf->dst_port;
+ pkt_len = bpf->pkt_len;
+
+ if (BGP_DEBUG(zebra, ZEBRA))
+ bgp_pbr_dump_entry(bpf, true);
+
+ /* look for bpa first */
+ memset(&temp3, 0, sizeof(temp3));
+ if (rate)
+ temp3.rate = *rate;
+ if (nh)
+ memcpy(&temp3.nh, nh, sizeof(struct nexthop));
+ temp3.vrf_id = bpf->vrf_id;
+ temp3.afi = family2afi(bpf->family);
+ bpa = hash_get(bgp->pbr_action_hash, &temp3,
+ bgp_pbr_action_alloc_intern);
+
+ if (nh)
+ vrf = vrf_lookup_by_id(nh->vrf_id);
+ if (bpa->fwmark == 0) {
+ /* drop is handled by iptable */
+ if (nh && nh->type == NEXTHOP_TYPE_BLACKHOLE) {
+ bpa->table_id = 0;
+ bpa->installed = true;
+ } else {
+ bpa->fwmark = bgp_zebra_tm_get_id();
+ /* if action is redirect-vrf, then
+ * use directly table_id of vrf
+ */
+ if (nh && vrf && !vrf_is_backend_netns()
+ && bpf->vrf_id != vrf->vrf_id)
+ bpa->table_id = vrf->data.l.table_id;
+ else
+ bpa->table_id = bpa->fwmark;
+ bpa->installed = false;
+ }
+ bpa->bgp = bgp;
+ bpa->unique = ++bgp_pbr_action_counter_unique;
+ /* 0 value is forbidden */
+ bpa->install_in_progress = false;
+ }
+ if (bpf->type == BGP_PBR_IPRULE) {
+ memset(&pbr_rule, 0, sizeof(pbr_rule));
+ pbr_rule.vrf_id = bpf->vrf_id;
+ pbr_rule.priority = 20;
+ if (bpf->src) {
+ pbr_rule.flags |= MATCH_IP_SRC_SET;
+ prefix_copy(&pbr_rule.src, bpf->src);
+ }
+ if (bpf->dst) {
+ pbr_rule.flags |= MATCH_IP_DST_SET;
+ prefix_copy(&pbr_rule.dst, bpf->dst);
+ }
+ pbr_rule.action = bpa;
+ bpr = hash_get(bgp->pbr_rule_hash, &pbr_rule,
+ bgp_pbr_rule_alloc_intern);
+ if (bpr->unique == 0) {
+ bpr->unique = ++bgp_pbr_action_counter_unique;
+ bpr->installed = false;
+ bpr->install_in_progress = false;
+ /* link bgp info to bpr */
+ bpr->path = (void *)path;
+ } else
+ bpr_found = true;
+ /* already installed */
+ if (bpr_found) {
+ struct bgp_path_info_extra *extra =
+ bgp_path_info_extra_get(path);
+
+ if (extra &&
+ listnode_lookup_nocheck(extra->bgp_fs_iprule,
+ bpr)) {
+ if (BGP_DEBUG(pbr, PBR_ERROR))
+ zlog_err("%s: entry %p/%p already installed in bgp pbr iprule",
+ __func__, path, bpr);
+ return;
+ }
+ }
+
+ bgp_pbr_bpa_add(bpa);
+
+ /* ip rule add */
+ if (!bpr->installed)
+ bgp_send_pbr_rule_action(bpa, bpr, true);
+
+ /* A previous entry may already exist
+ * flush previous entry if necessary
+ */
+ bprr.bpr_to_match = bpr;
+ bprr.bpr_found = NULL;
+ hash_walk(bgp->pbr_rule_hash, bgp_pbr_get_same_rule, &bprr);
+ if (bprr.bpr_found) {
+ static struct bgp_pbr_rule *local_bpr;
+ static struct bgp_pbr_action *local_bpa;
+
+ local_bpr = bprr.bpr_found;
+ local_bpa = local_bpr->action;
+ bgp_pbr_flush_iprule(bgp, local_bpa,
+ local_bpr);
+ }
+ return;
+ }
+ /* then look for bpm */
+ memset(&temp, 0, sizeof(temp));
+ temp.vrf_id = bpf->vrf_id;
+ temp.family = bpf->family;
+ if (bpf->src)
+ temp.flags |= MATCH_IP_SRC_SET;
+ if (bpf->dst)
+ temp.flags |= MATCH_IP_DST_SET;
+
+ if (src_port && (src_port->min_port || bpf->protocol == IPPROTO_ICMP)) {
+ if (bpf->protocol == IPPROTO_ICMP)
+ temp.flags |= MATCH_ICMP_SET;
+ temp.flags |= MATCH_PORT_SRC_SET;
+ }
+ if (dst_port && (dst_port->min_port || bpf->protocol == IPPROTO_ICMP)) {
+ if (bpf->protocol == IPPROTO_ICMP)
+ temp.flags |= MATCH_ICMP_SET;
+ temp.flags |= MATCH_PORT_DST_SET;
+ }
+ if (src_port && src_port->max_port)
+ temp.flags |= MATCH_PORT_SRC_RANGE_SET;
+ if (dst_port && dst_port->max_port)
+ temp.flags |= MATCH_PORT_DST_RANGE_SET;
+
+ if (bpf->src == NULL || bpf->dst == NULL) {
+ if (temp.flags & (MATCH_PORT_DST_SET | MATCH_PORT_SRC_SET))
+ temp.type = IPSET_NET_PORT;
+ else
+ temp.type = IPSET_NET;
+ } else {
+ if (temp.flags & (MATCH_PORT_DST_SET | MATCH_PORT_SRC_SET))
+ temp.type = IPSET_NET_PORT_NET;
+ else
+ temp.type = IPSET_NET_NET;
+ }
+ if (pkt_len) {
+ temp.pkt_len_min = pkt_len->min_port;
+ if (pkt_len->max_port)
+ temp.pkt_len_max = pkt_len->max_port;
+ } else if (bpf->pkt_len_val) {
+ if (bpf->pkt_len_val->mask)
+ temp.flags |= MATCH_PKT_LEN_INVERSE_SET;
+ temp.pkt_len_min = bpf->pkt_len_val->val;
+ }
+ if (bpf->tcp_flags) {
+ temp.tcp_flags = bpf->tcp_flags->val;
+ temp.tcp_mask_flags = bpf->tcp_flags->mask;
+ }
+ if (bpf->dscp) {
+ if (bpf->dscp->mask)
+ temp.flags |= MATCH_DSCP_INVERSE_SET;
+ else
+ temp.flags |= MATCH_DSCP_SET;
+ temp.dscp_value = bpf->dscp->val;
+ }
+ if (bpf->flow_label) {
+ if (bpf->flow_label->mask)
+ temp.flags |= MATCH_FLOW_LABEL_INVERSE_SET;
+ else
+ temp.flags |= MATCH_FLOW_LABEL_SET;
+ temp.flow_label = bpf->flow_label->val;
+ }
+ if (bpf->fragment) {
+ if (bpf->fragment->mask)
+ temp.flags |= MATCH_FRAGMENT_INVERSE_SET;
+ temp.fragment = bpf->fragment->val;
+ }
+ if (bpf->protocol) {
+ temp.protocol = bpf->protocol;
+ temp.flags |= MATCH_PROTOCOL_SET;
+ }
+ temp.action = bpa;
+ bpm = hash_get(bgp->pbr_match_hash, &temp,
+ bgp_pbr_match_alloc_intern);
+
+ /* new, then self allocate ipset_name and unique */
+ if (bpm->unique == 0) {
+ bpm->unique = ++bgp_pbr_match_counter_unique;
+ /* 0 value is forbidden */
+ snprintf(bpm->ipset_name, sizeof(bpm->ipset_name),
+ "match%p", bpm);
+ bpm->entry_hash = hash_create_size(8,
+ bgp_pbr_match_entry_hash_key,
+ bgp_pbr_match_entry_hash_equal,
+ "Match Entry Hash");
+ bpm->installed = false;
+
+ /* unique2 should be updated too */
+ bpm->unique2 = ++bgp_pbr_match_iptable_counter_unique;
+ bpm->installed_in_iptable = false;
+ bpm->install_in_progress = false;
+ bpm->install_iptable_in_progress = false;
+ }
+
+ memset(&temp2, 0, sizeof(temp2));
+ if (bpf->src)
+ prefix_copy(&temp2.src, bpf->src);
+ else
+ temp2.src.family = bpf->family;
+ if (bpf->dst)
+ prefix_copy(&temp2.dst, bpf->dst);
+ else
+ temp2.dst.family = bpf->family;
+ temp2.src_port_min = src_port ? src_port->min_port : 0;
+ temp2.dst_port_min = dst_port ? dst_port->min_port : 0;
+ temp2.src_port_max = src_port ? src_port->max_port : 0;
+ temp2.dst_port_max = dst_port ? dst_port->max_port : 0;
+ temp2.proto = bpf->protocol;
+ bpme = hash_get(bpm->entry_hash, &temp2,
+ bgp_pbr_match_entry_alloc_intern);
+ if (bpme->unique == 0) {
+ bpme->unique = ++bgp_pbr_match_entry_counter_unique;
+ /* 0 value is forbidden */
+ bpme->backpointer = bpm;
+ bpme->installed = false;
+ bpme->install_in_progress = false;
+ /* link bgp info to bpme */
+ bpme->path = (void *)path;
+ } else
+ bpme_found = true;
+
+ /* already installed */
+ if (bpme_found) {
+ struct bgp_path_info_extra *extra =
+ bgp_path_info_extra_get(path);
+
+ if (extra &&
+ listnode_lookup_nocheck(extra->bgp_fs_pbr, bpme)) {
+ if (BGP_DEBUG(pbr, PBR_ERROR))
+ zlog_err(
+ "%s: entry %p/%p already installed in bgp pbr",
+ __func__, path, bpme);
+ return;
+ }
+ }
+ /* BGP FS: append entry to zebra
+ * - policies are not routing entries and as such
+ * route replace semantics don't necessarily follow
+ * through to policy entries
+ * - because of that, not all policing information will be stored
+ * into zebra. and non selected policies will be suppressed from zebra
+ * - as consequence, in order to bring consistency
+ * a policy will be added, then ifan ecmp policy exists,
+ * it will be suppressed subsequently
+ */
+ /* ip rule add */
+ bgp_pbr_bpa_add(bpa);
+
+ /* ipset create */
+ if (!bpm->installed)
+ bgp_send_pbr_ipset_match(bpm, true);
+ /* ipset add */
+ if (!bpme->installed)
+ bgp_send_pbr_ipset_entry_match(bpme, true);
+
+ /* iptables */
+ if (!bpm->installed_in_iptable)
+ bgp_send_pbr_iptable(bpa, bpm, true);
+
+ /* A previous entry may already exist
+ * flush previous entry if necessary
+ */
+ bpmer.bpme_to_match = bpme;
+ bpmer.bpme_found = NULL;
+ hash_walk(bgp->pbr_match_hash, bgp_pbr_get_remaining_entry, &bpmer);
+ if (bpmer.bpme_found) {
+ static struct bgp_pbr_match *local_bpm;
+ static struct bgp_pbr_action *local_bpa;
+
+ local_bpm = bpmer.bpme_found->backpointer;
+ local_bpa = local_bpm->action;
+ bgp_pbr_flush_entry(bgp, local_bpa,
+ local_bpm, bpmer.bpme_found);
+ }
+
+
+}
+
+static void bgp_pbr_policyroute_add_to_zebra_recursive(
+ struct bgp *bgp, struct bgp_path_info *path, struct bgp_pbr_filter *bpf,
+ struct bgp_pbr_or_filter *bpof, struct nexthop *nh, float *rate,
+ uint8_t type_entry)
+{
+ struct listnode *node, *nnode;
+ struct bgp_pbr_val_mask *valmask;
+ uint8_t next_type_entry;
+ struct list *orig_list;
+ struct bgp_pbr_val_mask **target_val;
+
+ if (type_entry == 0) {
+ bgp_pbr_policyroute_add_to_zebra_unit(bgp, path, bpf, nh, rate);
+ return;
+ }
+ next_type_entry = bgp_pbr_next_type_entry(type_entry);
+ if (type_entry == FLOWSPEC_TCP_FLAGS && bpof->tcpflags) {
+ orig_list = bpof->tcpflags;
+ target_val = &bpf->tcp_flags;
+ } else if (type_entry == FLOWSPEC_DSCP && bpof->dscp) {
+ orig_list = bpof->dscp;
+ target_val = &bpf->dscp;
+ } else if (type_entry == FLOWSPEC_PKT_LEN && bpof->pkt_len) {
+ orig_list = bpof->pkt_len;
+ target_val = &bpf->pkt_len_val;
+ } else if (type_entry == FLOWSPEC_FRAGMENT && bpof->fragment) {
+ orig_list = bpof->fragment;
+ target_val = &bpf->fragment;
+ } else if (type_entry == FLOWSPEC_ICMP_TYPE &&
+ (bpof->icmp_type || bpof->icmp_code)) {
+ /* enumerate list for icmp - must be last one */
+ bgp_pbr_icmp_action(bgp, path, bpf, bpof, true, nh, rate);
+ return;
+ } else {
+ bgp_pbr_policyroute_add_to_zebra_recursive(
+ bgp, path, bpf, bpof, nh, rate, next_type_entry);
+ return;
+ }
+ for (ALL_LIST_ELEMENTS(orig_list, node, nnode, valmask)) {
+ *target_val = valmask;
+ bgp_pbr_policyroute_add_to_zebra_recursive(
+ bgp, path, bpf, bpof, nh, rate, next_type_entry);
+ }
+}
+
+static void bgp_pbr_policyroute_add_to_zebra(struct bgp *bgp,
+ struct bgp_path_info *path,
+ struct bgp_pbr_filter *bpf,
+ struct bgp_pbr_or_filter *bpof,
+ struct nexthop *nh, float *rate)
+{
+ if (!bpof) {
+ bgp_pbr_policyroute_add_to_zebra_unit(bgp, path, bpf, nh, rate);
+ return;
+ }
+ if (bpof->tcpflags)
+ bgp_pbr_policyroute_add_to_zebra_recursive(
+ bgp, path, bpf, bpof, nh, rate, FLOWSPEC_TCP_FLAGS);
+ else if (bpof->dscp)
+ bgp_pbr_policyroute_add_to_zebra_recursive(
+ bgp, path, bpf, bpof, nh, rate, FLOWSPEC_DSCP);
+ else if (bpof->pkt_len)
+ bgp_pbr_policyroute_add_to_zebra_recursive(
+ bgp, path, bpf, bpof, nh, rate, FLOWSPEC_PKT_LEN);
+ else if (bpof->fragment)
+ bgp_pbr_policyroute_add_to_zebra_recursive(
+ bgp, path, bpf, bpof, nh, rate, FLOWSPEC_FRAGMENT);
+ else if (bpof->icmp_type || bpof->icmp_code)
+ bgp_pbr_policyroute_add_to_zebra_recursive(
+ bgp, path, bpf, bpof, nh, rate, FLOWSPEC_ICMP_TYPE);
+ else
+ bgp_pbr_policyroute_add_to_zebra_unit(bgp, path, bpf, nh, rate);
+ /* flush bpof */
+ if (bpof->tcpflags)
+ list_delete_all_node(bpof->tcpflags);
+ if (bpof->dscp)
+ list_delete_all_node(bpof->dscp);
+ if (bpof->pkt_len)
+ list_delete_all_node(bpof->pkt_len);
+ if (bpof->fragment)
+ list_delete_all_node(bpof->fragment);
+ if (bpof->icmp_type)
+ list_delete_all_node(bpof->icmp_type);
+ if (bpof->icmp_code)
+ list_delete_all_node(bpof->icmp_code);
+}
+
+static void bgp_pbr_handle_entry(struct bgp *bgp, struct bgp_path_info *path,
+ struct bgp_pbr_entry_main *api, bool add)
+{
+ struct nexthop nh;
+ int i = 0;
+ int continue_loop = 1;
+ float rate = 0;
+ struct prefix *src = NULL, *dst = NULL;
+ uint8_t proto = 0;
+ struct bgp_pbr_range_port *srcp = NULL, *dstp = NULL;
+ struct bgp_pbr_range_port range, range_icmp_code;
+ struct bgp_pbr_range_port pkt_len;
+ struct bgp_pbr_filter bpf;
+ uint8_t kind_enum;
+ struct bgp_pbr_or_filter bpof;
+ struct bgp_pbr_val_mask bpvm;
+
+ memset(&range, 0, sizeof(range));
+ memset(&nh, 0, sizeof(nh));
+ memset(&bpf, 0, sizeof(bpf));
+ memset(&bpof, 0, sizeof(bpof));
+ if (api->match_bitmask & PREFIX_SRC_PRESENT ||
+ (api->type == BGP_PBR_IPRULE &&
+ api->match_bitmask_iprule & PREFIX_SRC_PRESENT))
+ src = &api->src_prefix;
+ if (api->match_bitmask & PREFIX_DST_PRESENT ||
+ (api->type == BGP_PBR_IPRULE &&
+ api->match_bitmask_iprule & PREFIX_DST_PRESENT))
+ dst = &api->dst_prefix;
+ if (api->type == BGP_PBR_IPRULE)
+ bpf.type = api->type;
+ memset(&nh, 0, sizeof(nh));
+ nh.vrf_id = VRF_UNKNOWN;
+ if (api->match_protocol_num) {
+ proto = (uint8_t)api->protocol[0].value;
+ if (api->afi == AF_INET6 && proto == IPPROTO_ICMPV6)
+ proto = IPPROTO_ICMP;
+ }
+ /* if match_port is selected, then either src or dst port will be parsed
+ * but not both at the same time
+ */
+ if (api->match_port_num >= 1) {
+ bgp_pbr_extract(api->port,
+ api->match_port_num,
+ &range);
+ srcp = dstp = &range;
+ } else if (api->match_src_port_num >= 1) {
+ bgp_pbr_extract(api->src_port,
+ api->match_src_port_num,
+ &range);
+ srcp = &range;
+ dstp = NULL;
+ } else if (api->match_dst_port_num >= 1) {
+ bgp_pbr_extract(api->dst_port,
+ api->match_dst_port_num,
+ &range);
+ dstp = &range;
+ srcp = NULL;
+ }
+ if (api->match_icmp_type_num >= 1) {
+ proto = IPPROTO_ICMP;
+ if (bgp_pbr_extract(api->icmp_type,
+ api->match_icmp_type_num,
+ &range))
+ srcp = &range;
+ else {
+ bpof.icmp_type = list_new();
+ bgp_pbr_extract_enumerate(api->icmp_type,
+ api->match_icmp_type_num,
+ OPERATOR_UNARY_OR,
+ bpof.icmp_type,
+ FLOWSPEC_ICMP_TYPE);
+ }
+ }
+ if (api->match_icmp_code_num >= 1) {
+ proto = IPPROTO_ICMP;
+ if (bgp_pbr_extract(api->icmp_code,
+ api->match_icmp_code_num,
+ &range_icmp_code))
+ dstp = &range_icmp_code;
+ else {
+ bpof.icmp_code = list_new();
+ bgp_pbr_extract_enumerate(api->icmp_code,
+ api->match_icmp_code_num,
+ OPERATOR_UNARY_OR,
+ bpof.icmp_code,
+ FLOWSPEC_ICMP_CODE);
+ }
+ }
+
+ if (api->match_tcpflags_num) {
+ kind_enum = bgp_pbr_match_val_get_operator(api->tcpflags,
+ api->match_tcpflags_num);
+ if (kind_enum == OPERATOR_UNARY_AND) {
+ bpf.tcp_flags = &bpvm;
+ bgp_pbr_extract_enumerate(api->tcpflags,
+ api->match_tcpflags_num,
+ OPERATOR_UNARY_AND,
+ bpf.tcp_flags,
+ FLOWSPEC_TCP_FLAGS);
+ } else if (kind_enum == OPERATOR_UNARY_OR) {
+ bpof.tcpflags = list_new();
+ bgp_pbr_extract_enumerate(api->tcpflags,
+ api->match_tcpflags_num,
+ OPERATOR_UNARY_OR,
+ bpof.tcpflags,
+ FLOWSPEC_TCP_FLAGS);
+ }
+ }
+ if (api->match_packet_length_num) {
+ bool ret;
+
+ ret = bgp_pbr_extract(api->packet_length,
+ api->match_packet_length_num,
+ &pkt_len);
+ if (ret)
+ bpf.pkt_len = &pkt_len;
+ else {
+ bpof.pkt_len = list_new();
+ bgp_pbr_extract_enumerate(api->packet_length,
+ api->match_packet_length_num,
+ OPERATOR_UNARY_OR,
+ bpof.pkt_len,
+ FLOWSPEC_PKT_LEN);
+ }
+ }
+ if (api->match_dscp_num >= 1) {
+ bpof.dscp = list_new();
+ bgp_pbr_extract_enumerate(api->dscp, api->match_dscp_num,
+ OPERATOR_UNARY_OR,
+ bpof.dscp, FLOWSPEC_DSCP);
+ }
+ if (api->match_fragment_num) {
+ bpof.fragment = list_new();
+ bgp_pbr_extract_enumerate(api->fragment,
+ api->match_fragment_num,
+ OPERATOR_UNARY_OR,
+ bpof.fragment,
+ FLOWSPEC_FRAGMENT);
+ }
+ bpf.vrf_id = api->vrf_id;
+ bpf.src = src;
+ bpf.dst = dst;
+ bpf.protocol = proto;
+ bpf.src_port = srcp;
+ bpf.dst_port = dstp;
+ bpf.family = afi2family(api->afi);
+ if (!add) {
+ bgp_pbr_policyroute_remove_from_zebra(bgp, path, &bpf, &bpof);
+ return;
+ }
+ /* no action for add = true */
+ for (i = 0; i < api->action_num; i++) {
+ switch (api->actions[i].action) {
+ case ACTION_TRAFFICRATE:
+ /* drop packet */
+ if (api->actions[i].u.r.rate == 0) {
+ nh.vrf_id = api->vrf_id;
+ nh.type = NEXTHOP_TYPE_BLACKHOLE;
+ bgp_pbr_policyroute_add_to_zebra(
+ bgp, path, &bpf, &bpof, &nh, &rate);
+ } else {
+ /* update rate. can be reentrant */
+ rate = api->actions[i].u.r.rate;
+ if (BGP_DEBUG(pbr, PBR)) {
+ bgp_pbr_print_policy_route(api);
+ zlog_warn("PBR: ignoring Set action rate %f",
+ api->actions[i].u.r.rate);
+ }
+ }
+ break;
+ case ACTION_TRAFFIC_ACTION:
+ if (api->actions[i].u.za.filter
+ & TRAFFIC_ACTION_SAMPLE) {
+ if (BGP_DEBUG(pbr, PBR)) {
+ bgp_pbr_print_policy_route(api);
+ zlog_warn("PBR: Sample action Ignored");
+ }
+ }
+ /* terminate action: run other filters
+ */
+ break;
+ case ACTION_REDIRECT_IP:
+ nh.vrf_id = api->vrf_id;
+ if (api->afi == AFI_IP) {
+ nh.type = NEXTHOP_TYPE_IPV4;
+ nh.gate.ipv4.s_addr =
+ api->actions[i].u.zr.
+ redirect_ip_v4.s_addr;
+ } else {
+ nh.type = NEXTHOP_TYPE_IPV6;
+ memcpy(&nh.gate.ipv6,
+ &api->actions[i].u.zr.redirect_ip_v6,
+ sizeof(struct in6_addr));
+ }
+ bgp_pbr_policyroute_add_to_zebra(bgp, path, &bpf, &bpof,
+ &nh, &rate);
+ /* XXX combination with REDIRECT_VRF
+ * + REDIRECT_NH_IP not done
+ */
+ continue_loop = 0;
+ break;
+ case ACTION_REDIRECT:
+ if (api->afi == AFI_IP)
+ nh.type = NEXTHOP_TYPE_IPV4;
+ else
+ nh.type = NEXTHOP_TYPE_IPV6;
+ nh.vrf_id = api->actions[i].u.redirect_vrf;
+ bgp_pbr_policyroute_add_to_zebra(bgp, path, &bpf, &bpof,
+ &nh, &rate);
+ continue_loop = 0;
+ break;
+ case ACTION_MARKING:
+ if (BGP_DEBUG(pbr, PBR)) {
+ bgp_pbr_print_policy_route(api);
+ zlog_warn("PBR: Set DSCP/FlowLabel %u Ignored",
+ api->actions[i].u.marking_dscp);
+ }
+ break;
+ default:
+ break;
+ }
+ if (continue_loop == 0)
+ break;
+ }
+}
+
+void bgp_pbr_update_entry(struct bgp *bgp, const struct prefix *p,
+ struct bgp_path_info *info, afi_t afi, safi_t safi,
+ bool nlri_update)
+{
+ struct bgp_pbr_entry_main api;
+
+ if (safi != SAFI_FLOWSPEC)
+ return; /* not supported */
+ /* Make Zebra API structure. */
+ memset(&api, 0, sizeof(api));
+ api.vrf_id = bgp->vrf_id;
+ api.afi = afi;
+
+ if (!bgp_zebra_tm_chunk_obtained()) {
+ if (BGP_DEBUG(pbr, PBR_ERROR))
+ flog_err(EC_BGP_TABLE_CHUNK,
+ "%s: table chunk not obtained yet", __func__);
+ return;
+ }
+
+ if (bgp_pbr_build_and_validate_entry(p, info, &api) < 0) {
+ if (BGP_DEBUG(pbr, PBR_ERROR))
+ flog_err(EC_BGP_FLOWSPEC_INSTALLATION,
+ "%s: cancel updating entry %p in bgp pbr",
+ __func__, info);
+ return;
+ }
+ bgp_pbr_handle_entry(bgp, info, &api, nlri_update);
+}
+
+int bgp_pbr_interface_compare(const struct bgp_pbr_interface *a,
+ const struct bgp_pbr_interface *b)
+{
+ return strcmp(a->name, b->name);
+}
+
+struct bgp_pbr_interface *bgp_pbr_interface_lookup(const char *name,
+ struct bgp_pbr_interface_head *head)
+{
+ struct bgp_pbr_interface pbr_if;
+
+ strlcpy(pbr_if.name, name, sizeof(pbr_if.name));
+ return (RB_FIND(bgp_pbr_interface_head,
+ head, &pbr_if));
+}
+
+/* this function resets to the default policy routing
+ * go back to default status
+ */
+void bgp_pbr_reset(struct bgp *bgp, afi_t afi)
+{
+ struct bgp_pbr_config *bgp_pbr_cfg = bgp->bgp_pbr_cfg;
+ struct bgp_pbr_interface_head *head;
+ struct bgp_pbr_interface *pbr_if;
+
+ if (!bgp_pbr_cfg)
+ return;
+ if (afi == AFI_IP)
+ head = &(bgp_pbr_cfg->ifaces_by_name_ipv4);
+ else
+ head = &(bgp_pbr_cfg->ifaces_by_name_ipv6);
+ while (!RB_EMPTY(bgp_pbr_interface_head, head)) {
+ pbr_if = RB_ROOT(bgp_pbr_interface_head, head);
+ RB_REMOVE(bgp_pbr_interface_head, head, pbr_if);
+ XFREE(MTYPE_TMP, pbr_if);
+ }
+}
diff --git a/bgpd/bgp_pbr.h b/bgpd/bgp_pbr.h
new file mode 100644
index 0000000..379ac40
--- /dev/null
+++ b/bgpd/bgp_pbr.h
@@ -0,0 +1,317 @@
+/*
+ * BGP pbr
+ * Copyright (C) 6WIND
+ *
+ * FRR is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License as published by the
+ * Free Software Foundation; either version 2, or (at your option) any
+ * later version.
+ *
+ * FRR is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program; see the file COPYING; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
+ */
+#ifndef __BGP_PBR_H__
+#define __BGP_PBR_H__
+
+#include "nexthop.h"
+#include "zclient.h"
+
+/* flowspec case: 0 to 3 actions maximum:
+ * 1 redirect
+ * 1 set dscp
+ * 1 set traffic rate
+ */
+#define ACTIONS_MAX_NUM 4
+enum bgp_pbr_action_enum {
+ ACTION_TRAFFICRATE = 1,
+ ACTION_TRAFFIC_ACTION = 2,
+ ACTION_REDIRECT = 3,
+ ACTION_MARKING = 4,
+ ACTION_REDIRECT_IP = 5
+};
+
+#define TRAFFIC_ACTION_SAMPLE (1 << 0)
+#define TRAFFIC_ACTION_TERMINATE (1 << 1)
+#define TRAFFIC_ACTION_DISTRIBUTE (1 << 2)
+
+#define OPERATOR_COMPARE_LESS_THAN (1<<1)
+#define OPERATOR_COMPARE_GREATER_THAN (1<<2)
+#define OPERATOR_COMPARE_EQUAL_TO (1<<3)
+#define OPERATOR_COMPARE_EXACT_MATCH (1<<4)
+
+#define OPERATOR_UNARY_OR (1<<1)
+#define OPERATOR_UNARY_AND (1<<2)
+
+/* struct used to store values [0;65535]
+ * this can be used for port number of protocol
+ */
+#define BGP_PBR_MATCH_VAL_MAX 5
+
+struct bgp_pbr_match_val {
+ uint16_t value;
+ uint8_t compare_operator;
+ uint8_t unary_operator;
+};
+
+#define FRAGMENT_DONT 1
+#define FRAGMENT_IS 2
+#define FRAGMENT_FIRST 4
+#define FRAGMENT_LAST 8
+
+struct bgp_pbr_entry_action {
+ /* used to store enum bgp_pbr_action_enum enumerate */
+ uint8_t action;
+ union {
+ union {
+ uint8_t rate_info[4]; /* IEEE.754.1985 */
+ float rate;
+ } r __attribute__((aligned(8)));
+ struct _pbr_action {
+ uint8_t do_sample;
+ uint8_t filter;
+ } za;
+ vrf_id_t redirect_vrf;
+ struct _pbr_redirect_ip {
+ struct in_addr redirect_ip_v4;
+ struct in6_addr redirect_ip_v6;
+ uint8_t duplicate;
+ } zr;
+ uint8_t marking_dscp;
+ } u __attribute__((aligned(8)));
+};
+
+/* BGP Policy Route structure */
+struct bgp_pbr_entry_main {
+#define BGP_PBR_UNDEFINED 0
+#define BGP_PBR_IPSET 1
+#define BGP_PBR_IPRULE 2
+ uint8_t type;
+
+ /*
+ * This is an enum but we are going to treat it as a uint8_t
+ * for purpose of encoding/decoding
+ */
+ afi_t afi;
+ safi_t safi;
+
+#define PREFIX_SRC_PRESENT (1 << 0)
+#define PREFIX_DST_PRESENT (1 << 1)
+ uint8_t match_bitmask_iprule;
+ uint8_t match_bitmask;
+
+ uint8_t match_src_port_num;
+ uint8_t match_dst_port_num;
+ uint8_t match_port_num;
+ uint8_t match_protocol_num;
+ uint8_t match_icmp_type_num;
+ uint8_t match_icmp_code_num;
+ uint8_t match_packet_length_num;
+ uint8_t match_dscp_num;
+ uint8_t match_tcpflags_num;
+ uint8_t match_fragment_num;
+ uint8_t match_flowlabel_num;
+
+ struct prefix src_prefix;
+ struct prefix dst_prefix;
+ uint8_t src_prefix_offset;
+ uint8_t dst_prefix_offset;
+
+#define PROTOCOL_UDP 17
+#define PROTOCOL_TCP 6
+#define PROTOCOL_ICMP 1
+#define PROTOCOL_ICMPV6 58
+ struct bgp_pbr_match_val protocol[BGP_PBR_MATCH_VAL_MAX];
+ struct bgp_pbr_match_val src_port[BGP_PBR_MATCH_VAL_MAX];
+ struct bgp_pbr_match_val dst_port[BGP_PBR_MATCH_VAL_MAX];
+ struct bgp_pbr_match_val port[BGP_PBR_MATCH_VAL_MAX];
+ struct bgp_pbr_match_val icmp_type[BGP_PBR_MATCH_VAL_MAX];
+ struct bgp_pbr_match_val icmp_code[BGP_PBR_MATCH_VAL_MAX];
+ struct bgp_pbr_match_val packet_length[BGP_PBR_MATCH_VAL_MAX];
+ struct bgp_pbr_match_val dscp[BGP_PBR_MATCH_VAL_MAX];
+ struct bgp_pbr_match_val flow_label[BGP_PBR_MATCH_VAL_MAX];
+
+ struct bgp_pbr_match_val tcpflags[BGP_PBR_MATCH_VAL_MAX];
+ struct bgp_pbr_match_val fragment[BGP_PBR_MATCH_VAL_MAX];
+
+ uint16_t action_num;
+ struct bgp_pbr_entry_action actions[ACTIONS_MAX_NUM];
+
+ vrf_id_t vrf_id;
+};
+
+struct bgp_pbr_interface {
+ RB_ENTRY(bgp_pbr_interface) id_entry;
+ char name[INTERFACE_NAMSIZ];
+};
+
+RB_HEAD(bgp_pbr_interface_head, bgp_pbr_interface);
+RB_PROTOTYPE(bgp_pbr_interface_head, bgp_pbr_interface, id_entry,
+ bgp_pbr_interface_compare);
+
+extern int bgp_pbr_interface_compare(const struct bgp_pbr_interface *a,
+ const struct bgp_pbr_interface *b);
+
+struct bgp_pbr_config {
+ struct bgp_pbr_interface_head ifaces_by_name_ipv4;
+ bool pbr_interface_any_ipv4;
+ struct bgp_pbr_interface_head ifaces_by_name_ipv6;
+ bool pbr_interface_any_ipv6;
+};
+
+extern struct bgp_pbr_config *bgp_pbr_cfg;
+
+struct bgp_pbr_rule {
+ uint32_t flags;
+ struct prefix src;
+ struct prefix dst;
+ struct bgp_pbr_action *action;
+ vrf_id_t vrf_id;
+ uint32_t unique;
+ uint32_t priority;
+ bool installed;
+ bool install_in_progress;
+ void *path;
+};
+
+struct bgp_pbr_match {
+ char ipset_name[ZEBRA_IPSET_NAME_SIZE];
+
+ /* mapped on enum ipset_type
+ */
+ uint32_t type;
+
+ uint32_t flags;
+ uint8_t family;
+
+ uint16_t pkt_len_min;
+ uint16_t pkt_len_max;
+ uint16_t tcp_flags;
+ uint16_t tcp_mask_flags;
+ uint8_t dscp_value;
+ uint8_t fragment;
+ uint8_t protocol;
+ uint16_t flow_label;
+
+ vrf_id_t vrf_id;
+
+ /* unique identifier for ipset create transaction
+ */
+ uint32_t unique;
+
+ /* unique identifier for iptable add transaction
+ */
+ uint32_t unique2;
+
+ bool installed;
+ bool install_in_progress;
+
+ bool installed_in_iptable;
+ bool install_iptable_in_progress;
+
+ struct hash *entry_hash;
+
+ struct bgp_pbr_action *action;
+
+};
+
+struct bgp_pbr_match_entry {
+ struct bgp_pbr_match *backpointer;
+
+ uint32_t unique;
+
+ struct prefix src;
+ struct prefix dst;
+
+ uint16_t src_port_min;
+ uint16_t src_port_max;
+ uint16_t dst_port_min;
+ uint16_t dst_port_max;
+ uint8_t proto;
+
+ void *path;
+
+ bool installed;
+ bool install_in_progress;
+};
+
+struct bgp_pbr_action {
+
+ /*
+ * The Unique identifier of this specific pbrms
+ */
+ uint32_t unique;
+
+ uint32_t fwmark;
+
+ uint32_t table_id;
+
+ float rate;
+
+ /*
+ * nexthop information, or drop information
+ * contains src vrf_id and nh contains dest vrf_id
+ */
+ vrf_id_t vrf_id;
+ struct nexthop nh;
+
+ bool installed;
+ bool install_in_progress;
+ uint32_t refcnt;
+ struct bgp *bgp;
+ afi_t afi;
+};
+
+extern struct bgp_pbr_rule *bgp_pbr_rule_lookup(vrf_id_t vrf_id,
+ uint32_t unique);
+
+extern struct bgp_pbr_action *bgp_pbr_action_rule_lookup(vrf_id_t vrf_id,
+ uint32_t unique);
+
+extern struct bgp_pbr_match *bgp_pbr_match_ipset_lookup(vrf_id_t vrf_id,
+ uint32_t unique);
+
+extern struct bgp_pbr_match_entry *bgp_pbr_match_ipset_entry_lookup(
+ vrf_id_t vrf_id, char *name,
+ uint32_t unique);
+extern struct bgp_pbr_match *bgp_pbr_match_iptable_lookup(vrf_id_t vrf_id,
+ uint32_t unique);
+
+extern void bgp_pbr_cleanup(struct bgp *bgp);
+extern void bgp_pbr_init(struct bgp *bgp);
+
+extern uint32_t bgp_pbr_rule_hash_key(const void *arg);
+extern bool bgp_pbr_rule_hash_equal(const void *arg1,
+ const void *arg2);
+extern uint32_t bgp_pbr_action_hash_key(const void *arg);
+extern bool bgp_pbr_action_hash_equal(const void *arg1,
+ const void *arg2);
+extern uint32_t bgp_pbr_match_entry_hash_key(const void *arg);
+extern bool bgp_pbr_match_entry_hash_equal(const void *arg1,
+ const void *arg2);
+extern uint32_t bgp_pbr_match_hash_key(const void *arg);
+extern bool bgp_pbr_match_hash_equal(const void *arg1,
+ const void *arg2);
+
+void bgp_pbr_print_policy_route(struct bgp_pbr_entry_main *api);
+
+struct bgp_path_info;
+extern void bgp_pbr_update_entry(struct bgp *bgp, const struct prefix *p,
+ struct bgp_path_info *new_select, afi_t afi,
+ safi_t safi, bool nlri_update);
+
+/* bgp pbr utilities */
+extern struct bgp_pbr_interface *pbr_interface_lookup(const char *name);
+extern void bgp_pbr_reset(struct bgp *bgp, afi_t afi);
+extern struct bgp_pbr_interface *bgp_pbr_interface_lookup(const char *name,
+ struct bgp_pbr_interface_head *head);
+
+extern int bgp_pbr_build_and_validate_entry(const struct prefix *p,
+ struct bgp_path_info *path,
+ struct bgp_pbr_entry_main *api);
+#endif /* __BGP_PBR_H__ */
diff --git a/bgpd/bgp_rd.c b/bgpd/bgp_rd.c
new file mode 100644
index 0000000..b4bcbdb
--- /dev/null
+++ b/bgpd/bgp_rd.c
@@ -0,0 +1,227 @@
+/* BGP RD definitions for BGP-based VPNs (IP/EVPN)
+ * -- brought over from bgpd/bgp_mplsvpn.c
+ * Copyright (C) 2000 Kunihiro Ishiguro <kunihiro@zebra.org>
+ *
+ * This file is part of FRR.
+ *
+ * FRR is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License as published by the
+ * Free Software Foundation; either version 2, or (at your option) any
+ * later version.
+ *
+ * FRR is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with FRR; see the file COPYING. If not, write to the Free
+ * Software Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA
+ * 02111-1307, USA.
+ */
+
+#include <zebra.h>
+#include "command.h"
+#include "log.h"
+#include "prefix.h"
+#include "memory.h"
+#include "stream.h"
+#include "filter.h"
+#include "frrstr.h"
+
+#include "lib/printfrr.h"
+
+#include "bgpd/bgpd.h"
+#include "bgpd/bgp_rd.h"
+#include "bgpd/bgp_attr.h"
+
+#ifdef ENABLE_BGP_VNC
+#include "bgpd/rfapi/rfapi_backend.h"
+#endif
+
+uint16_t decode_rd_type(const uint8_t *pnt)
+{
+ uint16_t v;
+
+ v = ((uint16_t)*pnt++ << 8);
+#ifdef ENABLE_BGP_VNC
+ /*
+ * VNC L2 stores LHI in lower byte, so omit it
+ */
+ if (v != RD_TYPE_VNC_ETH)
+ v |= (uint16_t)*pnt;
+#else /* duplicate code for clarity */
+ v |= (uint16_t)*pnt;
+#endif
+ return v;
+}
+
+void encode_rd_type(uint16_t v, uint8_t *pnt)
+{
+ *((uint16_t *)pnt) = htons(v);
+}
+
+/* type == RD_TYPE_AS */
+void decode_rd_as(const uint8_t *pnt, struct rd_as *rd_as)
+{
+ rd_as->as = (uint16_t)*pnt++ << 8;
+ rd_as->as |= (uint16_t)*pnt++;
+ ptr_get_be32(pnt, &rd_as->val);
+}
+
+/* type == RD_TYPE_AS4 */
+void decode_rd_as4(const uint8_t *pnt, struct rd_as *rd_as)
+{
+ pnt = ptr_get_be32(pnt, &rd_as->as);
+ rd_as->val = ((uint16_t)*pnt++ << 8);
+ rd_as->val |= (uint16_t)*pnt;
+}
+
+/* type == RD_TYPE_IP */
+void decode_rd_ip(const uint8_t *pnt, struct rd_ip *rd_ip)
+{
+ memcpy(&rd_ip->ip, pnt, 4);
+ pnt += 4;
+
+ rd_ip->val = ((uint16_t)*pnt++ << 8);
+ rd_ip->val |= (uint16_t)*pnt;
+}
+
+#ifdef ENABLE_BGP_VNC
+/* type == RD_TYPE_VNC_ETH */
+void decode_rd_vnc_eth(const uint8_t *pnt, struct rd_vnc_eth *rd_vnc_eth)
+{
+ rd_vnc_eth->type = RD_TYPE_VNC_ETH;
+ rd_vnc_eth->local_nve_id = pnt[1];
+ memcpy(rd_vnc_eth->macaddr.octet, pnt + 2, ETH_ALEN);
+}
+#endif
+
+int str2prefix_rd(const char *str, struct prefix_rd *prd)
+{
+ int ret = 0;
+ char *p;
+ char *p2;
+ struct stream *s = NULL;
+ char *half = NULL;
+ struct in_addr addr;
+
+ prd->family = AF_UNSPEC;
+ prd->prefixlen = 64;
+
+ p = strchr(str, ':');
+ if (!p)
+ goto out;
+
+ if (!all_digit(p + 1))
+ goto out;
+
+ s = stream_new(RD_BYTES);
+
+ half = XMALLOC(MTYPE_TMP, (p - str) + 1);
+ memcpy(half, str, (p - str));
+ half[p - str] = '\0';
+
+ p2 = strchr(str, '.');
+
+ if (!p2) {
+ unsigned long as_val;
+
+ if (!all_digit(half))
+ goto out;
+
+ as_val = atol(half);
+ if (as_val > 0xffff) {
+ stream_putw(s, RD_TYPE_AS4);
+ stream_putl(s, as_val);
+ stream_putw(s, atol(p + 1));
+ } else {
+ stream_putw(s, RD_TYPE_AS);
+ stream_putw(s, as_val);
+ stream_putl(s, atol(p + 1));
+ }
+ } else {
+ if (!inet_aton(half, &addr))
+ goto out;
+
+ stream_putw(s, RD_TYPE_IP);
+ stream_put_in_addr(s, &addr);
+ stream_putw(s, atol(p + 1));
+ }
+ memcpy(prd->val, s->data, 8);
+ ret = 1;
+
+out:
+ if (s)
+ stream_free(s);
+ XFREE(MTYPE_TMP, half);
+ return ret;
+}
+
+char *prefix_rd2str(const struct prefix_rd *prd, char *buf, size_t size)
+{
+ const uint8_t *pnt;
+ uint16_t type;
+ struct rd_as rd_as;
+ struct rd_ip rd_ip;
+
+ assert(size >= RD_ADDRSTRLEN);
+
+ pnt = prd->val;
+
+ type = decode_rd_type(pnt);
+
+ if (type == RD_TYPE_AS) {
+ decode_rd_as(pnt + 2, &rd_as);
+ snprintf(buf, size, "%u:%u", rd_as.as, rd_as.val);
+ return buf;
+ } else if (type == RD_TYPE_AS4) {
+ decode_rd_as4(pnt + 2, &rd_as);
+ snprintf(buf, size, "%u:%u", rd_as.as, rd_as.val);
+ return buf;
+ } else if (type == RD_TYPE_IP) {
+ decode_rd_ip(pnt + 2, &rd_ip);
+ snprintfrr(buf, size, "%pI4:%hu", &rd_ip.ip, rd_ip.val);
+ return buf;
+ }
+#ifdef ENABLE_BGP_VNC
+ else if (type == RD_TYPE_VNC_ETH) {
+ snprintf(buf, size, "LHI:%d, %02x:%02x:%02x:%02x:%02x:%02x",
+ *(pnt + 1), /* LHI */
+ *(pnt + 2), /* MAC[0] */
+ *(pnt + 3), *(pnt + 4), *(pnt + 5), *(pnt + 6),
+ *(pnt + 7));
+
+ return buf;
+ }
+#endif
+
+ snprintf(buf, size, "Unknown Type: %d", type);
+ return buf;
+}
+
+void form_auto_rd(struct in_addr router_id,
+ uint16_t rd_id,
+ struct prefix_rd *prd)
+{
+ char buf[100];
+
+ prd->family = AF_UNSPEC;
+ prd->prefixlen = 64;
+ snprintfrr(buf, sizeof(buf), "%pI4:%hu", &router_id, rd_id);
+ (void)str2prefix_rd(buf, prd);
+}
+
+printfrr_ext_autoreg_p("RD", printfrr_prd);
+static ssize_t printfrr_prd(struct fbuf *buf, struct printfrr_eargs *ea,
+ const void *ptr)
+{
+ char rd_buf[RD_ADDRSTRLEN];
+
+ if (!ptr)
+ return bputs(buf, "(null)");
+
+ prefix_rd2str(ptr, rd_buf, sizeof(rd_buf));
+
+ return bputs(buf, rd_buf);
+}
diff --git a/bgpd/bgp_rd.h b/bgpd/bgp_rd.h
new file mode 100644
index 0000000..2aee44c
--- /dev/null
+++ b/bgpd/bgp_rd.h
@@ -0,0 +1,74 @@
+/* BGP RD definitions for BGP-based VPNs (IP/EVPN)
+ * -- brought over from bgpd/bgp_mplsvpn.h
+ * Copyright (C) 2000 Kunihiro Ishiguro <kunihiro@zebra.org>
+ *
+ * This file is part of FRR.
+ *
+ * FRR is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License as published by the
+ * Free Software Foundation; either version 2, or (at your option) any
+ * later version.
+ *
+ * FRR is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with FRR; see the file COPYING. If not, write to the Free
+ * Software Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA
+ * 02111-1307, USA.
+ */
+
+#ifndef _QUAGGA_BGP_RD_H
+#define _QUAGGA_BGP_RD_H
+
+/* RD types */
+#define RD_TYPE_AS 0
+#define RD_TYPE_IP 1
+#define RD_TYPE_AS4 2
+
+#ifdef ENABLE_BGP_VNC
+#define RD_TYPE_VNC_ETH 0xff00 /* VNC L2VPN */
+#endif
+
+#define RD_ADDRSTRLEN 28
+#define RD_BYTES 8
+
+struct rd_as {
+ uint16_t type;
+ as_t as;
+ uint32_t val;
+};
+
+struct rd_ip {
+ uint16_t type;
+ struct in_addr ip;
+ uint16_t val;
+};
+
+#ifdef ENABLE_BGP_VNC
+struct rd_vnc_eth {
+ uint16_t type;
+ uint8_t local_nve_id;
+ struct ethaddr macaddr;
+};
+#endif
+
+extern uint16_t decode_rd_type(const uint8_t *pnt);
+extern void encode_rd_type(uint16_t, uint8_t *);
+
+extern void decode_rd_as(const uint8_t *pnt, struct rd_as *rd_as);
+extern void decode_rd_as4(const uint8_t *pnt, struct rd_as *rd_as);
+extern void decode_rd_ip(const uint8_t *pnt, struct rd_ip *rd_ip);
+#ifdef ENABLE_BGP_VNC
+extern void decode_rd_vnc_eth(const uint8_t *pnt,
+ struct rd_vnc_eth *rd_vnc_eth);
+#endif
+
+extern int str2prefix_rd(const char *, struct prefix_rd *);
+extern char *prefix_rd2str(const struct prefix_rd *, char *, size_t);
+extern void form_auto_rd(struct in_addr router_id, uint16_t rd_id,
+ struct prefix_rd *prd);
+
+#endif /* _QUAGGA_BGP_RD_H */
diff --git a/bgpd/bgp_regex.c b/bgpd/bgp_regex.c
new file mode 100644
index 0000000..6065684
--- /dev/null
+++ b/bgpd/bgp_regex.c
@@ -0,0 +1,89 @@
+/* AS regular expression routine
+ * Copyright (C) 1999 Kunihiro Ishiguro
+ *
+ * This file is part of GNU Zebra.
+ *
+ * GNU Zebra is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License as published by the
+ * Free Software Foundation; either version 2, or (at your option) any
+ * later version.
+ *
+ * GNU Zebra is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program; see the file COPYING; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
+ */
+
+#include <zebra.h>
+
+#include "log.h"
+#include "command.h"
+#include "memory.h"
+#include "queue.h"
+#include "filter.h"
+
+#include "bgpd.h"
+#include "bgp_aspath.h"
+#include "bgp_regex.h"
+
+/* Character `_' has special mean. It represents [,{}() ] and the
+ beginning of the line(^) and the end of the line ($).
+
+ (^|[,{}() ]|$) */
+
+regex_t *bgp_regcomp(const char *regstr)
+{
+ /* Convert _ character to generic regular expression. */
+ int i, j;
+ int len;
+ int magic = 0;
+ char *magic_str;
+ char magic_regexp[] = "(^|[,{}() ]|$)";
+ int ret;
+ regex_t *regex;
+
+ len = strlen(regstr);
+ for (i = 0; i < len; i++)
+ if (regstr[i] == '_')
+ magic++;
+
+ magic_str = XMALLOC(MTYPE_TMP, len + (14 * magic) + 1);
+
+ for (i = 0, j = 0; i < len; i++) {
+ if (regstr[i] == '_') {
+ memcpy(magic_str + j, magic_regexp,
+ strlen(magic_regexp));
+ j += strlen(magic_regexp);
+ } else
+ magic_str[j++] = regstr[i];
+ }
+ magic_str[j] = '\0';
+
+ regex = XMALLOC(MTYPE_BGP_REGEXP, sizeof(regex_t));
+
+ ret = regcomp(regex, magic_str, REG_EXTENDED | REG_NOSUB);
+
+ XFREE(MTYPE_TMP, magic_str);
+
+ if (ret != 0) {
+ XFREE(MTYPE_BGP_REGEXP, regex);
+ return NULL;
+ }
+
+ return regex;
+}
+
+int bgp_regexec(regex_t *regex, struct aspath *aspath)
+{
+ return regexec(regex, aspath->str, 0, NULL, 0);
+}
+
+void bgp_regex_free(regex_t *regex)
+{
+ regfree(regex);
+ XFREE(MTYPE_BGP_REGEXP, regex);
+}
diff --git a/bgpd/bgp_regex.h b/bgpd/bgp_regex.h
new file mode 100644
index 0000000..43ebb9a
--- /dev/null
+++ b/bgpd/bgp_regex.h
@@ -0,0 +1,36 @@
+/* AS regular expression routine
+ * Copyright (C) 1999 Kunihiro Ishiguro
+ *
+ * This file is part of GNU Zebra.
+ *
+ * GNU Zebra is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License as published by the
+ * Free Software Foundation; either version 2, or (at your option) any
+ * later version.
+ *
+ * GNU Zebra is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program; see the file COPYING; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
+ */
+
+#ifndef _QUAGGA_BGP_REGEX_H
+#define _QUAGGA_BGP_REGEX_H
+
+#include <zebra.h>
+
+#ifdef HAVE_LIBPCREPOSIX
+#include <pcreposix.h>
+#else
+#include <regex.h>
+#endif /* HAVE_LIBPCREPOSIX */
+
+extern void bgp_regex_free(regex_t *regex);
+extern regex_t *bgp_regcomp(const char *str);
+extern int bgp_regexec(regex_t *regex, struct aspath *aspath);
+
+#endif /* _QUAGGA_BGP_REGEX_H */
diff --git a/bgpd/bgp_route.c b/bgpd/bgp_route.c
new file mode 100644
index 0000000..a57a6b4
--- /dev/null
+++ b/bgpd/bgp_route.c
@@ -0,0 +1,15633 @@
+/* BGP routing information
+ * Copyright (C) 1996, 97, 98, 99 Kunihiro Ishiguro
+ * Copyright (C) 2016 Job Snijders <job@instituut.net>
+ *
+ * This file is part of GNU Zebra.
+ *
+ * GNU Zebra is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License as published by the
+ * Free Software Foundation; either version 2, or (at your option) any
+ * later version.
+ *
+ * GNU Zebra is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program; see the file COPYING; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
+ */
+
+#include <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 "thread.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"
+
+#ifndef VTYSH_EXTRACT_PL
+#include "bgpd/bgp_route_clippy.c"
+#endif
+
+DEFINE_HOOK(bgp_snmp_update_stats,
+ (struct bgp_node *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));
+
+/* Render dest to prefix_rd based on safi */
+static const struct prefix_rd *bgp_rd_from_dest(const struct bgp_dest *dest,
+ safi_t safi);
+
+/* 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
+ bgp_dest_unlock_node(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_afi_node_lookup(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;
+
+ 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->bgp_fs_pbr = NULL;
+ new->bgp_fs_iprule = 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->parent) {
+ struct bgp_path_info *bpi = (struct bgp_path_info *)e->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->parent);
+ e->parent = NULL;
+ }
+
+ if (e->bgp_orig)
+ bgp_unlock(e->bgp_orig);
+
+ if (e->peer_orig)
+ peer_unlock(e->peer_orig);
+
+ if (e->aggr_suppressors)
+ list_delete(&e->aggr_suppressors);
+
+ if (e->mh_info)
+ bgp_evpn_path_mh_info_free(e->mh_info);
+
+ if ((*extra)->bgp_fs_iprule)
+ list_delete(&((*extra)->bgp_fs_iprule));
+ if ((*extra)->bgp_fs_pbr)
+ list_delete(&((*extra)->bgp_fs_pbr));
+ 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();
+ return pi->extra;
+}
+
+/* Free bgp route information. */
+static void bgp_path_info_free(struct bgp_path_info *path)
+{
+ 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;
+}
+
+/* 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))
+ zlog_debug(
+ "Route %pBD is in workqueue and being processed, not deferred.",
+ dest);
+
+ 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, dest %p", dest,
+ dest);
+ return 0;
+ }
+ }
+ return -1;
+}
+
+void bgp_path_info_add(struct bgp_dest *dest, struct bgp_path_info *pi)
+{
+ 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* */
+void 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);
+ 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)
+{
+ if (pi->addpath_rx_id)
+ snprintf(buf, buf_len, "path %s (addpath rxid %d)",
+ pi->peer->host, pi->addpath_rx_id);
+ else
+ snprintf(buf, buf_len, "path %s", pi->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->parent;
+ bpi_ultimate = bpi_ultimate->extra->parent)
+ ;
+
+ return bpi_ultimate;
+}
+
+/* Compare two bgp route entity. If 'new' is preferable over 'exist' return 1.
+ */
+static 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, int 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;
+
+ *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 (!debug) {
+ prefix2str(new_p, pfx_buf,
+ sizeof(*pfx_buf)
+ * PREFIX2STR_BUFFER);
+ bgp_path_info_path_with_addpath_rx_str(
+ new, new_buf, sizeof(new_buf));
+ bgp_path_info_path_with_addpath_rx_str(
+ exist, exist_buf, sizeof(exist_buf));
+ }
+
+ 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;
+ }
+
+ /* 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;
+ }
+ }
+
+ /* 7. Peer type check. */
+ new_sort = new->peer->sort;
+ exist_sort = exist->peer->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(new->peer) == BGP_PEER_IBGP &&
+ peer_sort_lookup(exist->peer) == 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 (new->peer->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 (new->peer->as == exist->peer->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 = new->peer->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 = exist->peer->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 (new->peer->su_remote == NULL) {
+ *reason = bgp_path_selection_local_configured;
+ return 0;
+ }
+ if (exist->peer->su_remote == NULL) {
+ *reason = bgp_path_selection_local_configured;
+ return 1;
+ }
+
+ ret = sockunion_cmp(new->peer->su_remote, exist->peer->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)
+{
+ enum bgp_path_selection_reason reason;
+ char pfx_buf[PREFIX2STR_BUFFER];
+
+ return bgp_path_info_cmp(bgp, new, exist, paths_eq, NULL, 0, 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;
+ ret = bgp_path_info_cmp(bgp, new, exist, &paths_eq, NULL, 0, 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;
+
+ 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_tx_path(peer->addpath_type[afi][safi], pi)) {
+ 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) {
+ mpls_label_t 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;
+ }
+ }
+
+ /* 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)
+ || CHECK_FLAG(peer->af_cap[afi][safi],
+ PEER_CAP_ORF_PREFIX_SM_OLD_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 (onlypeer && onlypeer->as_path_loop_detection
+ && aspath_loop_check(piattr->aspath, onlypeer->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.",
+ onlypeer, onlypeer->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(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;
+
+ /* 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))) {
+ 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;
+
+ bgp_peer_remove_private_as(bgp, afi, safi, peer, attr);
+ bgp_peer_as_override(bgp, afi, safi, peer, attr);
+
+ 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);
+
+ /* don't confuse inbound and outbound setting */
+ RESET_FLAG(attr->rmap_change_flags);
+
+ /*
+ * 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;
+ }
+ }
+
+ /* 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: BGP_PATH_ANNC_NH_SELF, family=%s",
+ __func__, 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 thread *thread)
+{
+ struct afi_safi_info *info;
+ afi_t afi;
+ safi_t safi;
+ struct bgp *bgp;
+
+ info = THREAD_ARG(thread);
+ afi = info->afi;
+ safi = info->safi;
+ bgp = info->bgp;
+
+ if (BGP_DEBUG(update, UPDATE_OUT))
+ zlog_debug("afi %d, safi %d : route select timer expired", afi,
+ safi);
+
+ 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, 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))
+ 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))
+ if (pi2->peer->status
+ != Established)
+ 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))
+ bgp_path_info_reap(dest, pi);
+
+ if (debug)
+ zlog_debug("%s: pi %p in holddown", __func__,
+ pi);
+
+ continue;
+ }
+
+ if (pi->peer && pi->peer != bgp->peer_self
+ && !CHECK_FLAG(pi->peer->sflags, PEER_STATUS_NSF_WAIT))
+ if (!peer_established(pi->peer)) {
+
+ if (debug)
+ zlog_debug(
+ "%s: pi %p non self peer %s not estab state",
+ __func__, pi, 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: pi %p dmed", __func__, pi);
+ 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))
+ continue;
+
+ if (!bgp_path_info_nexthop_cmp(pi, new_select)) {
+ if (debug)
+ zlog_debug(
+ "%pBD: %s has the same nexthop as the bestpath, skip it",
+ dest, 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 is equivalent to the bestpath, add to the multipath list",
+ dest, 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,
+ uint32_t addpath_tx_id)
+{
+ const struct prefix *p;
+ struct peer *onlypeer;
+ struct attr attr;
+ afi_t afi;
+ safi_t safi;
+ struct bgp *bgp;
+ bool advertise;
+
+ p = bgp_dest_get_prefix(dest);
+ afi = SUBGRP_AFI(subgrp);
+ safi = SUBGRP_SAFI(subgrp);
+ 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);
+
+ 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))
+ bgp_adj_out_set_subgroup(
+ dest, subgrp, &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_imp_null(const struct bgp_path_info *new_select)
+{
+ /* 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)
+ return true;
+ else if (new_select->extra == NULL ||
+ !bgp_is_valid_label(&new_select->extra->label[0]))
+ /* TODO -- should be configurable? */
+ return true;
+ else
+ return false;
+}
+
+/*
+ * 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",
+ __func__, dest);
+ 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=%pBDi(%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", dest);
+ 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;
+
+ /* Do we need to allocate or free labels?
+ * 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 implicit-null
+ * - no valid label (due to removed static label binding) -> get new one
+ */
+ if (bgp->allocate_mpls_labels[afi][safi]) {
+ 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)) {
+ /* Enforced penultimate hop popping:
+ * implicit-null for local routes, aggregate
+ * and redistributed routes
+ */
+ if (bgp_lu_need_imp_null(new_select)) {
+ 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_IMPLICIT_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);
+ }
+
+ 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(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 (BGP_SUPPRESS_FIB_ENABLED(bgp)
+ && new_select->sub_type == BGP_ROUTE_NORMAL)
+ SET_FLAG(dest->flags,
+ BGP_NODE_FIB_INSTALL_PENDING);
+
+ 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);
+
+ /* bestpath has changed; bump version */
+ if (old_select || new_select) {
+ bgp_bump_version(dest);
+
+ if (!bgp->t_rmap_def_originate_eval) {
+ bgp_lock(bgp);
+ thread_add_timer(
+ bm->master,
+ update_group_refresh_default_originate_route_map,
+ bgp, RMAP_DEFAULT_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 (BGP_SUPPRESS_FIB_ENABLED(bgp))
+ SET_FLAG(dest->flags,
+ BGP_NODE_FIB_INSTALL_PENDING);
+
+ /* 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);
+
+ /* 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);
+
+ UNSET_FLAG(dest->flags, BGP_NODE_PROCESS_SCHEDULED);
+ 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 thread *t = bgp->gr_info[afi][safi].t_route_select;
+
+ thread_info = THREAD_ARG(t);
+ XFREE(MTYPE_TMP, thread_info);
+ THREAD_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(afi, safi, bgp->vrf_id,
+ 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
+ */
+ thread_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 thread *thread)
+{
+ struct peer *peer;
+
+ peer = THREAD_ARG(thread);
+ peer->t_pmax_restart = NULL;
+
+ 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];
+
+ 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(peer, 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(peer->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;
+
+ /*
+ * 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);
+}
+
+int 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;
+ 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;
+ uint8_t pi_type = 0;
+ uint8_t pi_sub_type = 0;
+ bool force_evpn_import = false;
+ safi_t orig_safi = safi;
+ bool leak_success = true;
+
+ 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);
+ }
+
+ /* 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 (peer->allowas_in[afi][safi])
+ aspath_loop_count = peer->allowas_in[afi][safi];
+ 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]
+ || (CHECK_FLAG(bgp->config, BGP_CONFIG_CONFEDERATION)
+ && aspath_loop_check(attr->aspath, bgp->confed_id)
+ > peer->allowas_in[afi][safi])) {
+ peer->stat_pfx_aspath_loop++;
+ reason = "as-path contains our own AS;";
+ goto filtered;
+ }
+ }
+
+ /* Route reflector originator ID check. */
+ if (attr->flag & ATTR_FLAG_BIT(BGP_ATTR_ORIGINATOR_ID)
+ && IPV4_ADDR_SAME(&bgp->router_id, &attr->originator_id)) {
+ 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;
+ }
+
+ /* 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 then add the GSHUT
+ * community to all paths received from eBGP peers */
+ } else if (bgp_in_graceful_shutdown(peer->bgp))
+ bgp_attr_add_gshut_community(&new_attr);
+ }
+
+ if (pi) {
+ pi_type = pi->type;
+ pi_sub_type = pi->sub_type;
+ }
+
+ /* next hop check. */
+ if (!CHECK_FLAG(peer->flags, PEER_FLAG_IS_RFAPI_HD)
+ && bgp_update_martian_nexthop(bgp, afi, safi, pi_type, pi_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;
+ }
+ /* The flag BGP_NODE_FIB_INSTALL_PENDING is for the following
+ * condition :
+ * Suppress fib is enabled
+ * BGP_OPT_NO_FIB is not enabled
+ * Route type is BGP_ROUTE_NORMAL (peer learnt routes)
+ * Route is being installed first time (BGP_NODE_FIB_INSTALLED not set)
+ */
+ if (bgp_fibupd_safi(safi) && BGP_SUPPRESS_FIB_ENABLED(bgp)
+ && (sub_type == BGP_ROUTE_NORMAL)
+ && (!bgp_option_check(BGP_OPT_NO_FIB))
+ && (!CHECK_FLAG(dest->flags, BGP_NODE_FIB_INSTALLED)))
+ SET_FLAG(dest->flags, BGP_NODE_FIB_INSTALL_PENDING);
+
+ /* If maximum prefix count is configured and current prefix
+ * count exeed it.
+ */
+ if (bgp_maximum_prefix_overflow(peer, afi, safi, 0)) {
+ bgp_attr_flush(&new_attr);
+ return -1;
+ }
+
+ /* 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 0;
+ }
+
+ /* 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(bgp,
+ 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]);
+ }
+
+ /* Update SRv6 SID */
+ if (attr->srv6_l3vpn) {
+ extra = bgp_path_info_extra_get(pi);
+ if (sid_diff(&extra->sid[0].sid,
+ &attr->srv6_l3vpn->sid)) {
+ sid_copy(&extra->sid[0].sid,
+ &attr->srv6_l3vpn->sid);
+ extra->num_sids = 1;
+
+ extra->sid[0].loc_block_len = 0;
+ extra->sid[0].loc_node_len = 0;
+ extra->sid[0].func_len = 0;
+ extra->sid[0].arg_len = 0;
+ extra->sid[0].transposition_len = 0;
+ extra->sid[0].transposition_offset = 0;
+
+ if (attr->srv6_l3vpn->loc_block_len != 0) {
+ extra->sid[0].loc_block_len =
+ attr->srv6_l3vpn->loc_block_len;
+ extra->sid[0].loc_node_len =
+ attr->srv6_l3vpn->loc_node_len;
+ extra->sid[0].func_len =
+ attr->srv6_l3vpn->func_len;
+ extra->sid[0].arg_len =
+ attr->srv6_l3vpn->arg_len;
+ extra->sid[0].transposition_len =
+ attr->srv6_l3vpn
+ ->transposition_len;
+ extra->sid[0].transposition_offset =
+ attr->srv6_l3vpn
+ ->transposition_offset;
+ }
+ }
+ } else if (attr->srv6_vpn) {
+ extra = bgp_path_info_extra_get(pi);
+ if (sid_diff(&extra->sid[0].sid,
+ &attr->srv6_vpn->sid)) {
+ sid_copy(&extra->sid[0].sid,
+ &attr->srv6_vpn->sid);
+ extra->num_sids = 1;
+ }
+ }
+
+#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 0;
+ }
+ }
+
+ /* Nexthop reachability check - for unicast and
+ * labeled-unicast.. */
+ if (((afi == AFI_IP || afi == AFI_IP6)
+ && (safi == SAFI_UNICAST || safi == SAFI_LABELED_UNICAST))
+ || (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->bgp_orig)
+ bgp_nexthop = pi->extra->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))
+ 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
+ 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)) {
+
+ leak_success = vpn_leak_to_vrf_update(bgp, pi);
+ }
+
+#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
+ if ((safi == SAFI_MPLS_VPN) &&
+ !CHECK_FLAG(bgp->af_flags[afi][safi],
+ BGP_VPNVX_RETAIN_ROUTE_TARGET_ALL) &&
+ !leak_success) {
+ bgp_unlink_nexthop(pi);
+ bgp_path_info_delete(dest, pi);
+ }
+ return 0;
+ } // 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]);
+ }
+
+ /* Update SRv6 SID */
+ if (safi == SAFI_MPLS_VPN) {
+ extra = bgp_path_info_extra_get(new);
+ if (attr->srv6_l3vpn) {
+ sid_copy(&extra->sid[0].sid, &attr->srv6_l3vpn->sid);
+ extra->num_sids = 1;
+
+ extra->sid[0].loc_block_len =
+ attr->srv6_l3vpn->loc_block_len;
+ extra->sid[0].loc_node_len =
+ attr->srv6_l3vpn->loc_node_len;
+ extra->sid[0].func_len = attr->srv6_l3vpn->func_len;
+ extra->sid[0].arg_len = attr->srv6_l3vpn->arg_len;
+ extra->sid[0].transposition_len =
+ attr->srv6_l3vpn->transposition_len;
+ extra->sid[0].transposition_offset =
+ attr->srv6_l3vpn->transposition_offset;
+ } else if (attr->srv6_vpn) {
+ sid_copy(&extra->sid[0].sid, &attr->srv6_vpn->sid);
+ extra->num_sids = 1;
+ }
+ }
+
+ /* Nexthop reachability check. */
+ if (((afi == AFI_IP || afi == AFI_IP6)
+ && (safi == SAFI_UNICAST || safi == SAFI_LABELED_UNICAST))
+ || (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))
+ bgp_path_info_set_flag(dest, new, BGP_PATH_VALID);
+ else {
+ if (BGP_DEBUG(nht, NHT)) {
+ char buf1[INET6_ADDRSTRLEN];
+ inet_ntop(AF_INET,
+ (const void *)&attr_new->nexthop,
+ buf1, INET6_ADDRSTRLEN);
+ zlog_debug("%s(%s): NH unresolved", __func__,
+ buf1);
+ }
+ bgp_path_info_unset_flag(dest, new, BGP_PATH_VALID);
+ }
+ } else
+ bgp_path_info_set_flag(dest, new, BGP_PATH_VALID);
+
+ /* 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)) {
+ leak_success = vpn_leak_to_vrf_update(bgp, new);
+ }
+#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
+ if ((safi == SAFI_MPLS_VPN) &&
+ !CHECK_FLAG(bgp->af_flags[afi][safi],
+ BGP_VPNVX_RETAIN_ROUTE_TARGET_ALL) &&
+ !leak_success) {
+ bgp_unlink_nexthop(new);
+ bgp_path_info_delete(dest, new);
+ }
+
+ return 0;
+
+/* This BGP update is filtered. Log the reason then update BGP
+ entry. */
+filtered:
+ 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(bgp, 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 0;
+}
+
+int bgp_withdraw(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, 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)) {
+ 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 0;
+ }
+
+ /* Lookup withdrawn 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;
+
+ /* 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(bgp, 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 0;
+}
+
+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;
+
+ THREAD_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 thread *t)
+{
+ struct peer_af *paf;
+ struct peer *peer;
+
+ paf = THREAD_ARG(t);
+ peer = paf->peer;
+
+ if (!peer_established(peer))
+ 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.
+ */
+ thread_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 int 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));
+
+ return 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)
+{
+ int ret;
+ 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;
+
+ ret = bgp_soft_reconfig_table_update(peer, dest, ain,
+ afi, safi, prd);
+
+ if (ret < 0) {
+ bgp_dest_unlock_node(dest);
+ return;
+ }
+ }
+}
+
+/* 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 thread *thread)
+{
+ uint32_t iter, max_iter;
+ int ret;
+ 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 = THREAD_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;
+
+ ret = bgp_soft_reconfig_table_update(
+ peer, dest, ain, table->afi,
+ table->safi, prd);
+ iter++;
+
+ if (ret < 0) {
+ bgp_dest_unlock_node(dest);
+ listnode_delete(
+ table->soft_reconfig_peers,
+ peer);
+ bgp_announce_route(peer, table->afi,
+ table->safi, false);
+ if (list_isempty(
+ table->soft_reconfig_peers)) {
+ list_delete(
+ &table->soft_reconfig_peers);
+ bgp_soft_reconfig_table_flag(
+ table, false);
+ return;
+ }
+ }
+ }
+ }
+ }
+
+ /* 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;
+ thread_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);
+ THREAD_OFF(ntable->soft_reconfig_thread);
+ }
+}
+
+void 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 (!peer_established(peer))
+ return;
+
+ if ((safi != SAFI_MPLS_VPN) && (safi != SAFI_ENCAP)
+ && (safi != SAFI_EVPN)) {
+ table = peer->bgp->rib[afi][safi];
+ if (!table)
+ return;
+
+ 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)
+ thread_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);
+ }
+}
+
+
+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(bgp, 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, 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;
+ }
+
+ for (pi = bgp_dest_get_bgp_path_info(dest); pi; pi = next) {
+ next = pi->next;
+ if (pi->peer != peer)
+ continue;
+
+ if (force)
+ bgp_path_info_reap(dest, pi);
+ 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;
+ }
+ }
+}
+
+/* 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(
+ peer->bgp, 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);
+ }
+
+ bgp_path_info_reap(dest, pi);
+ }
+}
+
+/* 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);
+ bgp_dest_unlock_node(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);
+ bgp_dest_unlock_node(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);
+ bgp_dest_unlock_node(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;
+ int ret;
+ 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);
+
+ /* RFC4771 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)
+ ret = bgp_update(peer, &p, addpath_id, attr, afi, safi,
+ ZEBRA_ROUTE_BGP, BGP_ROUTE_NORMAL,
+ NULL, NULL, 0, 0, NULL);
+ else
+ ret = bgp_withdraw(peer, &p, addpath_id, attr, 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;
+
+ /* Address family configuration mismatch. */
+ if (ret < 0)
+ return BGP_NLRI_PARSE_ERROR_ADDRESS_FAMILY;
+ }
+
+ /* 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 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);
+
+ 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;
+#endif
+
+ assert(bgp_static);
+
+ dest = bgp_afi_node_get(bgp->rib[afi][safi], afi, safi, p, NULL);
+
+ 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->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);
+ }
+
+ /* 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_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);
+ }
+ }
+#endif
+
+ /* Nexthop reachability check. */
+ if (CHECK_FLAG(bgp->flags, BGP_FLAG_IMPORT_CHECK)
+ && (safi == SAFI_UNICAST
+ || safi == SAFI_LABELED_UNICAST)) {
+
+ struct bgp *bgp_nexthop = bgp;
+
+ if (pi->extra && pi->extra->bgp_orig)
+ bgp_nexthop = pi->extra->bgp_orig;
+
+ if (bgp_find_or_add_nexthop(bgp, bgp_nexthop,
+ afi, safi, pi, NULL,
+ 0, p))
+ bgp_path_info_set_flag(dest, pi,
+ BGP_PATH_VALID);
+ else {
+ if (BGP_DEBUG(nht, NHT)) {
+ char buf1[INET6_ADDRSTRLEN];
+ inet_ntop(p->family,
+ &p->u.prefix, buf1,
+ INET6_ADDRSTRLEN);
+ zlog_debug(
+ "%s(%s): Route not in table, not advertising",
+ __func__, buf1);
+ }
+ bgp_path_info_unset_flag(
+ dest, pi, 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(pi);
+ bgp_path_info_set_flag(dest, pi,
+ BGP_PATH_VALID);
+ }
+ /* Process change. */
+ bgp_aggregate_increment(bgp, p, pi, afi, safi);
+ 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,
+ 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);
+ /* Nexthop reachability check. */
+ if (CHECK_FLAG(bgp->flags, BGP_FLAG_IMPORT_CHECK)
+ && (safi == SAFI_UNICAST || safi == SAFI_LABELED_UNICAST)) {
+ if (bgp_find_or_add_nexthop(bgp, bgp, afi, safi, new, NULL, 0,
+ p))
+ bgp_path_info_set_flag(dest, new, BGP_PATH_VALID);
+ else {
+ if (BGP_DEBUG(nht, NHT)) {
+ char buf1[INET6_ADDRSTRLEN];
+ inet_ntop(p->family, &p->u.prefix, buf1,
+ INET6_ADDRSTRLEN);
+ zlog_debug(
+ "%s(%s): Route not in table, not advertising",
+ __func__, buf1);
+ }
+ bgp_path_info_unset_flag(dest, new, 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(new);
+
+ bgp_path_info_set_flag(dest, new, BGP_PATH_VALID);
+ }
+
+ /* 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);
+ }
+
+ /* Unintern original. */
+ aspath_unintern(&attr.aspath);
+}
+
+void bgp_static_withdraw(struct bgp *bgp, const struct prefix *p, afi_t afi,
+ safi_t safi)
+{
+ struct bgp_dest *dest;
+ struct bgp_path_info *pi;
+
+ dest = bgp_afi_node_get(bgp->rib[afi][safi], afi, safi, p, NULL);
+
+ /* 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) {
+ 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);
+ }
+ 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);
+}
+
+/*
+ * Used for SAFI_MPLS_VPN and SAFI_ENCAP
+ */
+static void bgp_static_withdraw_safi(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
+ rfapiProcessWithdraw(
+ pi->peer, NULL, p, prd, pi->attr, afi, safi, pi->type,
+ 1); /* Kill, since it is an administrative change */
+#endif
+ if (SAFI_MPLS_VPN == safi
+ && bgp->inst_type == BGP_INSTANCE_TYPE_DEFAULT) {
+ vpn_leak_to_vrf_withdraw(bgp, pi);
+ }
+ bgp_aggregate_decrement(bgp, p, pi, afi, safi);
+ bgp_path_info_delete(dest, pi);
+ bgp_process(bgp, dest, afi, safi);
+ }
+
+ /* Unlock bgp_node_lookup. */
+ bgp_dest_unlock_node(dest);
+}
+
+static void bgp_static_update_safi(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 *new;
+ struct attr *attr_new;
+ struct attr attr = {0};
+ struct bgp_path_info *pi;
+#ifdef ENABLE_BGP_VNC
+ mpls_label_t label = 0;
+#endif
+ uint32_t num_labels = 0;
+
+ assert(bgp_static);
+
+ if (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 ((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;
+ struct bgp_path_info rmap_path;
+ route_map_result_t ret;
+
+ 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_safi(bgp, p, afi, safi,
+ &bgp_static->prd);
+ bgp_dest_unlock_node(dest);
+ return;
+ }
+
+ attr_new = bgp_attr_intern(&attr_tmp);
+ } else {
+ 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)) {
+ 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);
+ bgp_attr_unintern(&pi->attr);
+ pi->attr = attr_new;
+ pi->uptime = monotime(NULL);
+#ifdef ENABLE_BGP_VNC
+ if (pi->extra)
+ label = decode_label(&pi->extra->label[0]);
+#endif
+
+ /* 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);
+ }
+#ifdef ENABLE_BGP_VNC
+ rfapiProcessUpdate(pi->peer, NULL, p, &bgp_static->prd,
+ pi->attr, afi, safi, pi->type,
+ pi->sub_type, &label);
+#endif
+ 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);
+ 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
+
+ /* 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_MPLS_VPN == safi
+ && bgp->inst_type == BGP_INSTANCE_TYPE_DEFAULT) {
+ vpn_leak_to_vrf_update(bgp, new);
+ }
+#ifdef ENABLE_BGP_VNC
+ 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);
+}
+
+/* Configure static BGP network. When user don't run zebra, static
+ route should be installed as valid. */
+static int bgp_static_set(struct vty *vty, const char *negate,
+ const char *ip_str, afi_t afi, safi_t safi,
+ const char *rmap, int backdoor, uint32_t label_index)
+{
+ VTY_DECLVAR_CONTEXT(bgp, bgp);
+ int ret;
+ struct prefix p;
+ struct bgp_static *bgp_static;
+ struct bgp_dest *dest;
+ uint8_t need_update = 0;
+
+ /* 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 (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 ((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);
+
+ /* Clear configuration. */
+ bgp_static_free(bgp_static);
+ bgp_dest_set_bgp_static_info(dest, NULL);
+ bgp_dest_unlock_node(dest);
+ bgp_dest_unlock_node(dest);
+ } else {
+
+ /* Set BGP static route configuration. */
+ dest = bgp_node_get(bgp->route[afi][safi], &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;
+
+ 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);
+ }
+ bgp_dest_set_bgp_static_info(dest, bgp_static);
+ }
+
+ bgp_static->valid = 1;
+ if (need_update)
+ bgp_static_withdraw(bgp, &p, afi, safi);
+
+ 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_safi(
+ 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_safi(
+ 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);
+ bgp_dest_unlock_node(rm);
+ }
+ } else {
+ bgp_static = bgp_dest_get_bgp_static_info(dest);
+ bgp_static_withdraw(bgp,
+ bgp_dest_get_prefix(dest),
+ afi, safi);
+ bgp_static_free(bgp_static);
+ bgp_dest_set_bgp_static_info(dest, NULL);
+ bgp_dest_unlock_node(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_safi(
+ 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);
+}
+
+/*
+ * gpz 110624
+ * Currently this is used to set static routes for VPN and ENCAP.
+ * I think it can probably be factored with bgp_static_set.
+ */
+int bgp_static_set_safi(afi_t afi, safi_t safi, struct vty *vty,
+ const char *ip_str, const char *rd_str,
+ const char *label_str, const char *rmap_str,
+ 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 prefix_rd prd;
+ struct bgp_dest *pdest;
+ struct bgp_dest *dest;
+ struct bgp_table *table;
+ struct bgp_static *bgp_static;
+ mpls_label_t label = MPLS_INVALID_LABEL;
+ struct prefix gw_ip;
+
+ /* validate ip prefix */
+ ret = str2prefix(ip_str, &p);
+ if (!ret) {
+ vty_out(vty, "%% Malformed prefix\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;
+ }
+
+ 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;
+ }
+ }
+ }
+ 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);
+
+ dest = bgp_node_get(table, &p);
+
+ if (bgp_dest_has_bgp_path_info_data(dest)) {
+ vty_out(vty, "%% Same network configuration exists\n");
+ bgp_dest_unlock_node(dest);
+ } else {
+ /* New configuration. */
+ bgp_static = bgp_static_new();
+ bgp_static->backdoor = 0;
+ bgp_static->valid = 0;
+ bgp_static->igpmetric = 0;
+ bgp_static->igpnexthop.s_addr = INADDR_ANY;
+ bgp_static->label = label;
+ bgp_static->prd = prd;
+
+ if (rmap_str) {
+ 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_str);
+ bgp_static->rmap.map =
+ route_map_lookup_by_name(rmap_str);
+ 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;
+ bgp_static_update_safi(bgp, &p, bgp_static, afi, safi);
+ }
+
+ return CMD_SUCCESS;
+}
+
+/* Configure static BGP network. */
+int bgp_static_unset_safi(afi_t afi, safi_t safi, struct vty *vty,
+ const char *ip_str, const char *rd_str,
+ const char *label_str, int evpn_type, const char *esi,
+ const char *gwip, const char *ethtag)
+{
+ VTY_DECLVAR_CONTEXT(bgp, bgp);
+ int ret;
+ struct prefix p;
+ struct prefix_rd prd;
+ struct bgp_dest *pdest;
+ struct bgp_dest *dest;
+ struct bgp_table *table;
+ struct bgp_static *bgp_static;
+ mpls_label_t label = MPLS_INVALID_LABEL;
+
+ /* 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;
+ }
+ 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;
+ }
+ 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);
+ }
+
+ 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));
+ else
+ bgp_dest_unlock_node(pdest);
+ table = bgp_dest_get_bgp_table_info(pdest);
+
+ dest = bgp_node_lookup(table, &p);
+
+ if (dest) {
+ bgp_static_withdraw_safi(bgp, &p, afi, safi, &prd);
+
+ bgp_static = bgp_dest_get_bgp_static_info(dest);
+ bgp_static_free(bgp_static);
+ bgp_dest_set_bgp_static_info(dest, NULL);
+ bgp_dest_unlock_node(dest);
+ bgp_dest_unlock_node(dest);
+ } else
+ vty_out(vty, "%% Can't find the route\n");
+
+ return CMD_SUCCESS;
+}
+
+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, AFI_IP,
+ bgp_node_safi(vty), map_name, backdoor ? 1 : 0,
+ label_index ? (uint32_t)label_index : BGP_INVALID_LABEL_INDEX);
+}
+
+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, AFI_IP6, bgp_node_safi(vty), map_name, 0,
+ label_index ? (uint32_t)label_index : BGP_INVALID_LABEL_INDEX);
+}
+
+static struct bgp_aggregate *bgp_aggregate_new(void)
+{
+ return XCALLOC(MTYPE_BGP_AGGREGATE, sizeof(struct bgp_aggregate));
+}
+
+static 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();
+ 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;
+
+ if (!ae)
+ ae = aspath_empty();
+
+ 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 (bgp_aggregate_info_same(orig, 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) {
+ 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 */
+void 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;
+
+ /* 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))
+ 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);
+}
+
+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))
+ 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);
+ bgp_dest_unlock_node(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. */
+ bgp_aggregate_route(bgp, &p, afi, safi, aggregate);
+
+ 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);
+
+ if (aggregate->community_hash) {
+ /* Delete all communities in the hash.
+ */
+ hash_clean(aggregate->community_hash,
+ bgp_aggr_community_remove);
+ /* Free up the community_hash.
+ */
+ hash_free(aggregate->community_hash);
+ }
+
+ if (aggregate->ecommunity)
+ ecommunity_free(&aggregate->ecommunity);
+
+ if (aggregate->ecommunity_hash) {
+ /* Delete all ecommunities in the hash.
+ */
+ hash_clean(aggregate->ecommunity_hash,
+ bgp_aggr_ecommunity_remove);
+ /* Free up the ecommunity_hash.
+ */
+ hash_free(aggregate->ecommunity_hash);
+ }
+
+ if (aggregate->lcommunity)
+ lcommunity_free(&aggregate->lcommunity);
+
+ if (aggregate->lcommunity_hash) {
+ /* Delete all lcommunities in the hash.
+ */
+ hash_clean(aggregate->lcommunity_hash,
+ bgp_aggr_lcommunity_remove);
+ /* Free up the lcommunity_hash.
+ */
+ hash_free(aggregate->lcommunity_hash);
+ }
+
+ if (aggregate->aspath)
+ aspath_free(aggregate->aspath);
+
+ if (aggregate->aspath_hash) {
+ /* Delete all as-paths in the hash.
+ */
+ hash_clean(aggregate->aspath_hash,
+ bgp_aggr_aspath_remove);
+ /* Free up the aspath_hash.
+ */
+ hash_free(aggregate->aspath_hash);
+ }
+
+ 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;
+
+ /* Make default attribute. */
+ bgp_attr_default_set(&attr, bgp, BGP_ORIGIN_INCOMPLETE);
+ /*
+ * This must not be NULL to satisfy Coverity SA
+ */
+ assert(attr.aspath);
+
+ 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;
+ 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;
+ 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;
+ 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;
+
+ 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;
+
+ /* 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
+ bgp_path_info_reap(dest, pi);
+ }
+ }
+}
+
+/* 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[BUFSIZ];
+
+ 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,
+ BUFSIZ));
+ 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,
+ BUFSIZ));
+ 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_local_route:
+ return "Local Route";
+ 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");
+
+ /* 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->bgp_orig) {
+ const char *self = "";
+
+ if (nexthop_self)
+ self = "<";
+
+ nexthop_othervrf = true;
+ nexthop_vrfid = path->extra->bgp_orig->vrf_id;
+
+ if (path->extra->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->bgp_orig->vrf_id, self);
+
+ if (path->extra->bgp_orig->inst_type
+ != BGP_INSTANCE_TYPE_DEFAULT)
+
+ nexthop_vrfname = path->extra->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 buf[BUFSIZ];
+ char nexthop[128];
+ int af = NEXTHOP_FAMILY(attr->mp_nexthop_len);
+
+ switch (af) {
+ case AF_INET:
+ snprintf(nexthop, sizeof(nexthop), "%s",
+ inet_ntop(af, &attr->mp_nexthop_global_in, buf,
+ BUFSIZ));
+ break;
+ case AF_INET6:
+ snprintf(nexthop, sizeof(nexthop), "%s",
+ inet_ntop(af, &attr->mp_nexthop_global, buf,
+ BUFSIZ));
+ 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->su);
+
+ /* Print aspath */
+ if (attr->aspath) {
+ if (json_paths)
+ json_object_string_add(json_path, "path",
+ attr->aspath->str);
+ else
+ aspath_print_vty(vty, "%s", 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 */
+ json_object_string_add(json_net, "bgpOriginCode",
+ bgp_origin_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)) {
+ char buf[BUFSIZ];
+
+ len = vty_out(
+ vty, "%s",
+ inet_ntop(AF_INET6,
+ &attr->mp_nexthop_global, buf,
+ BUFSIZ));
+ 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, "%s", attr->aspath, " ");
+
+ /* Print origin */
+ vty_out(vty, "%s", bgp_origin_str[attr->origin]);
+ }
+ }
+ if (use_json) {
+ json_object_boolean_true_add(json_status, "*");
+ json_object_boolean_true_add(json_status, ">");
+ 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);
+ }
+ }
+
+ label = decode_label(&path->extra->label[0]);
+
+ if (bgp_is_valid_label(&label)) {
+ 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");
+ 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, "%s", 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, "%s", 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->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->su);
+ } else {
+ if (peer->conf_if)
+ vty_out(vty, " %s", peer->conf_if);
+ else
+ vty_out(vty, " %pSU", &peer->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,
+ 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 buf1[BUFSIZ];
+ struct attr *attr = path->attr;
+ time_t tbuf;
+ 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;
+
+ 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 %pRN", bn);
+ }
+
+ if (path->extra) {
+ char tag_buf[30];
+
+ tag_buf[0] = '\0';
+ 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);
+ json_object_string_add(json_path, "vni",
+ tag_buf);
+ }
+ }
+ }
+
+ if (path->extra && path->extra->parent && !json_paths) {
+ struct bgp_path_info *parent_ri;
+ struct bgp_dest *dest, *pdest;
+
+ parent_ri = (struct bgp_path_info *)path->extra->parent;
+ dest = parent_ri->net;
+ if (dest && dest->pdest) {
+ pdest = dest->pdest;
+ if (is_pi_family_evpn(parent_ri)) {
+ vty_out(vty,
+ " Imported from %pRD:%pFX, VNI %s",
+ (struct prefix_rd *)
+ bgp_dest_get_prefix(
+ pdest),
+ (struct prefix_evpn *)
+ bgp_dest_get_prefix(
+ dest),
+ tag_buf);
+ if (attr->es_flags & ATTR_ES_L3_NHG)
+ vty_out(vty, ", L3NHG %s",
+ (attr->es_flags
+ & ATTR_ES_L3_NHG_ACTIVE)
+ ? "active"
+ : "inactive");
+ vty_out(vty, "\n");
+
+ } else
+ vty_out(vty,
+ " Imported from %pRD:%pFX\n",
+ (struct prefix_rd *)
+ bgp_dest_get_prefix(
+ pdest),
+ (struct prefix_evpn *)
+ bgp_dest_get_prefix(
+ dest));
+ }
+ }
+ }
+
+ 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");
+
+ /* 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)
+ aspath_print_vty(vty, " %s", attr->aspath, "");
+ 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 */
+ const struct prefix *bn_p = bgp_dest_get_prefix(bn);
+
+ if ((bn_p->family == AF_INET || bn_p->family == AF_ETHERNET ||
+ bn_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(path->flags, BGP_PATH_VALID)) {
+ if (json_paths)
+ json_object_boolean_false_add(json_nexthop_global,
+ "accessible");
+ else
+ vty_out(vty, " (inaccessible)");
+ } else {
+ if (path->extra && path->extra->igpmetric) {
+ if (json_paths)
+ json_object_int_add(json_nexthop_global,
+ "metric",
+ path->extra->igpmetric);
+ else
+ vty_out(vty, " (metric %u)",
+ path->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 || (bn_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->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->su);
+ }
+
+ if (attr->flag & ATTR_FLAG_BIT(BGP_ATTR_ORIGINATOR_ID))
+ vty_out(vty, " (%pI4)", &attr->originator_id);
+ else
+ vty_out(vty, " (%s)",
+ inet_ntop(AF_INET,
+ &path->peer->remote_id, buf1,
+ sizeof(buf1)));
+ }
+ }
+
+ /*
+ * Note when vrfid of nexthop is different from that of prefix
+ */
+ if (path->extra && path->extra->bgp_orig) {
+ vrf_id_t nexthop_vrfid = path->extra->bgp_orig->vrf_id;
+
+ if (json_paths) {
+ const char *vn;
+
+ if (path->extra->bgp_orig->inst_type
+ == BGP_INSTANCE_TYPE_DEFAULT)
+ vn = VRF_DEFAULT_NAME;
+ else
+ vn = path->extra->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->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->extra && path->extra->num_sids > 0 && safi != SAFI_EVPN) {
+ inet_ntop(AF_INET6, &path->extra->sid[0].sid, buf, sizeof(buf));
+ if (json_paths)
+ json_object_string_add(json_path, "remoteSid", buf);
+ else
+ vty_out(vty, " Remote SID: %s\n", buf);
+ }
+
+ /* 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(&tbuf));
+ json_object_object_add(json_path, "lastUpdate",
+ json_last_update);
+ } else
+ vty_out(vty, " Last update: %s", ctime(&tbuf));
+
+ /* 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->t_gr_restart &&
+ CHECK_FLAG(path->flags, BGP_PATH_STALE)) {
+ unsigned long gr_remaining =
+ thread_timer_remain_second(path->peer->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 = thread_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, 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);
+
+ 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\": %u,\n \"routes\": { ",
+ 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, bgp->as);
+ 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 && CHECK_FLAG(show_flags, BGP_SHOW_OPT_DETAIL) &&
+ 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 = json_detail_header;
+
+ 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 (!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 %u\n", bgp->as);
+ 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
+ vty_out(vty, (wide ? BGP_SHOW_HEADER_WIDE
+ : BGP_SHOW_HEADER));
+ header = false;
+
+ } else if (json_detail && json_paths != NULL) {
+ const struct prefix_rd *prd;
+ json_object *jtemp;
+
+ /* Use common detail header, for most types;
+ * need a json 'object'.
+ */
+
+ jtemp = json_object_new_object();
+ prd = bgp_rd_from_dest(dest, safi);
+
+ route_vty_out_detail_header(
+ vty, bgp, dest, prd, table->afi,
+ safi, jtemp);
+
+ json_object_array_add(json_paths, jtemp);
+
+ json_detail = 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_IP, 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_IP, safi, use_json,
+ json_paths);
+ else {
+ if (CHECK_FLAG(show_flags, BGP_SHOW_OPT_DETAIL))
+ route_vty_out_detail(
+ vty, bgp, dest, 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);
+ }
+ /*
+ * 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);
+ 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, safi_t safi,
+ struct bgp_table *table, struct prefix_rd *prd_match,
+ enum bgp_show_type type, void *output_arg, bool use_json)
+{
+ 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;
+ uint16_t show_flags = 0;
+
+ show_msg = (!use_json && type == bgp_show_type_normal);
+
+ if (use_json)
+ SET_FLAG(show_flags, BGP_SHOW_OPT_JSON);
+
+ 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_show_table(vty, bgp, 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);
+ }
+ 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, safi, table, NULL, type,
+ output_arg, use_json);
+ }
+
+ 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);
+ }
+
+ return bgp_show_table(vty, bgp, 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_rd *prd,
+ afi_t afi, safi_t safi, json_object *json)
+{
+ struct bgp_path_info *pi;
+ const struct prefix *p;
+ 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);
+
+ p = bgp_dest_get_prefix(dest);
+ 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))
+ : "",
+ prd ? ":" : "", (struct prefix_evpn *)p);
+ } else {
+ json_object_string_add(json, "rd",
+ prd ? prefix_rd2str(prd, buf1, sizeof(buf1)) :
+ "");
+ 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))
+ : ""),
+ safi == SAFI_MPLS_VPN && prd ? ":" : "", p,
+ 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)
+ 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) {
+ if (json_adv_to) {
+ json_object_object_add(json, "advertisedTo",
+ json_adv_to);
+ }
+ } else {
+ if (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, pfx_rd,
+ AFI_IP, safi, json_header);
+ 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, pi, AFI_IP,
+ 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, "%pRD",
+ pfx_rd);
+ }
+}
+
+/*
+ * Return rd based on safi
+ */
+static 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));
+ default:
+ return NULL;
+
+ }
+}
+
+/* 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;
+ bgp_dest_unlock_node(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\
+ ] [json$uj [detail$detail] | 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"
+ 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)
+ SET_FLAG(show_flags, BGP_SHOW_OPT_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;
+ }
+
+ /* 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;
+ }
+
+ 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));
+ } 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 thread *t)
+{
+ struct bgp_dest *dest, *ndest;
+ struct bgp_dest *top;
+ struct bgp_table_stats *ts = THREAD_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;
+ default:
+ 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];
+ thread_execute(bm->master, bgp_table_stats_walker, &ts, 0);
+
+ 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;
+ default:
+ break;
+ }
+
+ if (json) {
+ json_bitlen = json_object_new_array();
+
+ for (i = 0; i <= bitlen; i++) {
+ struct json_object *ind_bit = json_object_new_object();
+
+ if (!ts.prefix_len_count[i])
+ continue;
+
+ 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 thread *t)
+{
+ struct bgp_dest *rn, *rm;
+ const struct bgp_table *table;
+ struct peer_pcounts *pc = THREAD_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()).
+ */
+ thread_execute(bm->master, bgp_peer_count_walker, &pcounts, 0);
+
+ 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)
+{
+ 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);
+ 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)
+ 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,
+ unsigned long *output_count, unsigned long *filtered_count)
+{
+ struct bgp_adj_in *ain;
+ struct bgp_adj_out *adj;
+ struct bgp_dest *dest;
+ struct bgp *bgp;
+ struct attr attr;
+ int ret;
+ struct update_subgroup *subgrp;
+ struct peer_af *paf;
+ bool route_filtered;
+ 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;
+
+ bgp = peer->bgp;
+
+ 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);
+ 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");
+ }
+ *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);
+
+ 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)++;
+
+ 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);
+
+ 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;
+ }
+ }
+ 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);
+
+ 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;
+
+ route_vty_out_tmp(vty, dest,
+ bgp_dest_get_prefix(dest),
+ 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, 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();
+
+ 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");
+
+ 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));
+
+ show_adj_route(vty, peer, table, afi, safi, type,
+ rmap_name, json, json_routes, json_scode,
+ json_ocode, show_flags, &header1,
+ &header2, rd_str, &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,
+ &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);
+ }
+
+ vty_json(vty, json);
+ } else if (output_count > 0) {
+ if (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 [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"
+ 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 (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,
+ 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] [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"
+ 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 (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,
+ 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, 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, 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->su, show_flags,
+ RPKI_NOT_BEING_USED);
+}
+
+DEFUN (show_ip_bgp_flowspec_routes_detailed,
+ show_ip_bgp_flowspec_routes_detailed_cmd,
+ "show [ip] bgp [<view|vrf> VIEWVRFNAME] ["BGP_AFI_CMD_STR" flowspec] detail [json]",
+ SHOW_STR
+ IP_STR
+ BGP_STR
+ BGP_INSTANCE_HELP_STR
+ BGP_AFI_HELP_STR
+ "SAFI Flowspec\n"
+ "Detailed information on flowspec entries\n"
+ JSON_STR)
+{
+ afi_t afi = AFI_IP6;
+ safi_t safi = SAFI_UNICAST;
+ struct bgp *bgp = NULL;
+ int idx = 0;
+ bool uj = use_json(argc, argv);
+ uint16_t show_flags = BGP_SHOW_OPT_DETAIL;
+
+ 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;
+
+ 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);
+ bgp_dest_unlock_node(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;
+
+ if (!bgp)
+ return 0;
+
+ peer = pinfo->peer;
+
+ if (pinfo->attr->distance)
+ return pinfo->attr->distance;
+
+ /* Check source address.
+ * Note: for aggregate route, peer can have unspec af type.
+ */
+ if (pinfo->sub_type != BGP_ROUTE_AGGREGATE
+ && !sockunion2hostprefix(&peer->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->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);
+ 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;
+ const struct prefix_rd *prd;
+ 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);
+ prd = (const struct prefix_rd *)bgp_dest_get_prefix(
+ pdest);
+
+ /* "network" configuration display. */
+ label = decode_label(&bgp_static->label);
+
+ vty_out(vty, " network %pFX rd %pRD", p, prd);
+ 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;
+ const struct prefix_rd *prd;
+ 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);
+ prd = (struct prefix_rd *)bgp_dest_get_prefix(pdest);
+
+ /* "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, PREFIX_STRLEN);
+ 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 %pRD ethtag %u label %u esi %s gwip %s routermac %s\n",
+ buf, prd, 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 ipv4 flowspec detailed */
+ install_element(VIEW_NODE, &show_ip_bgp_flowspec_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;
+ }
+}
diff --git a/bgpd/bgp_route.h b/bgpd/bgp_route.h
new file mode 100644
index 0000000..e776ae4
--- /dev/null
+++ b/bgpd/bgp_route.h
@@ -0,0 +1,872 @@
+/* BGP routing information base
+ * Copyright (C) 1996, 97, 98, 2000 Kunihiro Ishiguro
+ *
+ * This file is part of GNU Zebra.
+ *
+ * GNU Zebra is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License as published by the
+ * Free Software Foundation; either version 2, or (at your option) any
+ * later version.
+ *
+ * GNU Zebra is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program; see the file COPYING; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
+ */
+
+#ifndef _QUAGGA_BGP_ROUTE_H
+#define _QUAGGA_BGP_ROUTE_H
+
+#include <stdbool.h>
+
+#include "hook.h"
+#include "queue.h"
+#include "nexthop.h"
+#include "bgp_table.h"
+#include "bgp_addpath_types.h"
+#include "bgp_rpki.h"
+
+struct bgp_nexthop_cache;
+struct bgp_route_evpn;
+
+enum bgp_show_type {
+ bgp_show_type_normal,
+ bgp_show_type_regexp,
+ bgp_show_type_prefix_list,
+ bgp_show_type_access_list,
+ bgp_show_type_filter_list,
+ bgp_show_type_route_map,
+ bgp_show_type_neighbor,
+ bgp_show_type_cidr_only,
+ bgp_show_type_prefix_longer,
+ bgp_show_type_community_alias,
+ bgp_show_type_community_all,
+ bgp_show_type_community,
+ bgp_show_type_community_exact,
+ bgp_show_type_community_list,
+ bgp_show_type_community_list_exact,
+ bgp_show_type_lcommunity_all,
+ bgp_show_type_lcommunity,
+ bgp_show_type_lcommunity_exact,
+ bgp_show_type_lcommunity_list,
+ bgp_show_type_lcommunity_list_exact,
+ bgp_show_type_flap_statistics,
+ bgp_show_type_flap_neighbor,
+ bgp_show_type_dampend_paths,
+ bgp_show_type_damp_neighbor,
+ bgp_show_type_detail,
+ bgp_show_type_rpki,
+ bgp_show_type_prefix_version,
+};
+
+enum bgp_show_adj_route_type {
+ bgp_show_adj_route_advertised,
+ bgp_show_adj_route_received,
+ bgp_show_adj_route_filtered,
+ bgp_show_adj_route_bestpath,
+};
+
+
+#define BGP_SHOW_SCODE_HEADER \
+ "Status codes: s suppressed, d damped, " \
+ "h history, * valid, > best, = multipath,\n" \
+ " i internal, r RIB-failure, S Stale, R Removed\n"
+#define BGP_SHOW_OCODE_HEADER \
+ "Origin codes: i - IGP, e - EGP, ? - incomplete\n"
+#define BGP_SHOW_NCODE_HEADER "Nexthop codes: @NNN nexthop's vrf id, < announce-nh-self\n"
+#define BGP_SHOW_RPKI_HEADER \
+ "RPKI validation codes: V valid, I invalid, N Not found\n\n"
+#define BGP_SHOW_HEADER " Network Next Hop Metric LocPrf Weight Path\n"
+#define BGP_SHOW_HEADER_WIDE " Network Next Hop Metric LocPrf Weight Path\n"
+
+/* Maximum number of labels we can process or send with a prefix. We
+ * really do only 1 for MPLS (BGP-LU) but we can do 2 for EVPN-VxLAN.
+ */
+#define BGP_MAX_LABELS 2
+
+/* Maximum number of sids we can process or send with a prefix. */
+#define BGP_MAX_SIDS 6
+
+/* Maximum buffer length for storing BGP best path selection reason */
+#define BGP_MAX_SELECTION_REASON_STR_BUF 32
+
+/* Error codes for handling NLRI */
+#define BGP_NLRI_PARSE_OK 0
+#define BGP_NLRI_PARSE_ERROR_PREFIX_OVERFLOW -1
+#define BGP_NLRI_PARSE_ERROR_PACKET_OVERFLOW -2
+#define BGP_NLRI_PARSE_ERROR_PREFIX_LENGTH -3
+#define BGP_NLRI_PARSE_ERROR_PACKET_LENGTH -4
+#define BGP_NLRI_PARSE_ERROR_LABEL_LENGTH -5
+#define BGP_NLRI_PARSE_ERROR_EVPN_MISSING_TYPE -6
+#define BGP_NLRI_PARSE_ERROR_EVPN_TYPE2_SIZE -7
+#define BGP_NLRI_PARSE_ERROR_EVPN_TYPE3_SIZE -8
+#define BGP_NLRI_PARSE_ERROR_EVPN_TYPE4_SIZE -9
+#define BGP_NLRI_PARSE_ERROR_EVPN_TYPE5_SIZE -10
+#define BGP_NLRI_PARSE_ERROR_FLOWSPEC_IPV6_NOT_SUPPORTED -11
+#define BGP_NLRI_PARSE_ERROR_FLOWSPEC_NLRI_SIZELIMIT -12
+#define BGP_NLRI_PARSE_ERROR_FLOWSPEC_BAD_FORMAT -13
+#define BGP_NLRI_PARSE_ERROR_ADDRESS_FAMILY -14
+#define BGP_NLRI_PARSE_ERROR_EVPN_TYPE1_SIZE -15
+#define BGP_NLRI_PARSE_ERROR -32
+
+/* 1. local MAC-IP/type-2 paths in the VNI routing table are linked to the
+ * destination ES
+ * 2. remote MAC-IP paths in the global routing table are linked to the
+ * destination ES
+ */
+struct bgp_path_es_info {
+ /* back pointer to the route */
+ struct bgp_path_info *pi;
+ vni_t vni;
+ /* destination ES */
+ struct bgp_evpn_es *es;
+ /* memory used for linking the path to the destination ES */
+ struct listnode es_listnode;
+ uint8_t flags;
+/* Path is linked to the VNI list */
+#define BGP_EVPN_PATH_ES_INFO_VNI_LIST (1 << 0)
+/* Path is linked to the global list */
+#define BGP_EVPN_PATH_ES_INFO_GLOBAL_LIST (1 << 1)
+};
+
+/* IP paths imported into the VRF from an EVPN route source
+ * are linked to the nexthop/VTEP IP
+ */
+struct bgp_path_evpn_nh_info {
+ /* back pointer to the route */
+ struct bgp_path_info *pi;
+ struct bgp_evpn_nh *nh;
+ /* memory used for linking the path to the nexthop */
+ struct listnode nh_listnode;
+};
+
+struct bgp_path_mh_info {
+ struct bgp_path_es_info *es_info;
+ struct bgp_path_evpn_nh_info *nh_info;
+};
+
+struct bgp_sid_info {
+ struct in6_addr sid;
+ uint8_t loc_block_len;
+ uint8_t loc_node_len;
+ uint8_t func_len;
+ uint8_t arg_len;
+ uint8_t transposition_len;
+ uint8_t transposition_offset;
+};
+
+/* Ancillary information to struct bgp_path_info,
+ * used for uncommonly used data (aggregation, MPLS, etc.)
+ * and lazily allocated to save memory.
+ */
+struct bgp_path_info_extra {
+ /* Pointer to dampening structure. */
+ struct bgp_damp_info *damp_info;
+
+ /** List of aggregations that suppress this path. */
+ struct list *aggr_suppressors;
+
+ /* Nexthop reachability check. */
+ uint32_t igpmetric;
+
+ /* MPLS label(s) - VNI(s) for EVPN-VxLAN */
+ mpls_label_t label[BGP_MAX_LABELS];
+ uint32_t num_labels;
+
+ /* af specific flags */
+ uint16_t af_flags;
+#define BGP_EVPN_MACIP_TYPE_SVI_IP (1 << 0)
+
+ /* SRv6 SID(s) for SRv6-VPN */
+ struct bgp_sid_info sid[BGP_MAX_SIDS];
+ uint32_t num_sids;
+
+#ifdef ENABLE_BGP_VNC
+ union {
+
+ struct {
+ void *rfapi_handle; /* export: NVE advertising this
+ route */
+ struct list *local_nexthops; /* optional, for static
+ routes */
+ } export;
+
+ struct {
+ struct thread *timer;
+ void *hme; /* encap monitor, if this is a VPN route */
+ struct prefix_rd
+ rd; /* import: route's route-distinguisher */
+ uint8_t un_family; /* family of cached un address, 0 if
+ unset */
+ union {
+ struct in_addr addr4;
+ struct in6_addr addr6;
+ } un; /* cached un address */
+ time_t create_time;
+ struct prefix aux_prefix; /* AFI_L2VPN: the IP addr,
+ if family set */
+ } import;
+
+ } vnc;
+#endif
+
+ /* For imported routes into a VNI (or VRF), this points to the parent.
+ */
+ void *parent;
+
+ /*
+ * Some tunnelish parameters follow. Maybe consolidate into an
+ * internal tunnel structure?
+ */
+
+ /*
+ * Original bgp instance for imported routes. Needed for:
+ * 1. Find all routes from a specific vrf for deletion
+ * 2. vrf context of original nexthop
+ *
+ * Store pointer to bgp instance rather than bgp->vrf_id because
+ * bgp->vrf_id is not always valid (or may change?).
+ *
+ * Set to NULL if route is not imported from another bgp instance.
+ */
+ struct bgp *bgp_orig;
+
+ /*
+ * Original bgp session to know if the session is a
+ * connected EBGP session or not
+ */
+ struct peer *peer_orig;
+
+ /*
+ * Nexthop in context of original bgp instance. Needed
+ * for label resolution of core mpls routes exported to a vrf.
+ * Set nexthop_orig.family to 0 if not valid.
+ */
+ struct prefix nexthop_orig;
+ /* presence of FS pbr firewall based entry */
+ struct list *bgp_fs_pbr;
+ /* presence of FS pbr iprule based entry */
+ struct list *bgp_fs_iprule;
+ /* Destination Ethernet Segment links for EVPN MH */
+ struct bgp_path_mh_info *mh_info;
+};
+
+struct bgp_path_info {
+ /* For linked list. */
+ struct bgp_path_info *next;
+ struct bgp_path_info *prev;
+
+ /* For nexthop linked list */
+ LIST_ENTRY(bgp_path_info) nh_thread;
+
+ /* Back pointer to the prefix node */
+ struct bgp_dest *net;
+
+ /* Back pointer to the nexthop structure */
+ struct bgp_nexthop_cache *nexthop;
+
+ /* Peer structure. */
+ struct peer *peer;
+
+ /* Attribute structure. */
+ struct attr *attr;
+
+ /* Extra information */
+ struct bgp_path_info_extra *extra;
+
+
+ /* Multipath information */
+ struct bgp_path_info_mpath *mpath;
+
+ /* Uptime. */
+ time_t uptime;
+
+ /* reference count */
+ int lock;
+
+ /* BGP information status. */
+ uint16_t flags;
+#define BGP_PATH_IGP_CHANGED (1 << 0)
+#define BGP_PATH_DAMPED (1 << 1)
+#define BGP_PATH_HISTORY (1 << 2)
+#define BGP_PATH_SELECTED (1 << 3)
+#define BGP_PATH_VALID (1 << 4)
+#define BGP_PATH_ATTR_CHANGED (1 << 5)
+#define BGP_PATH_DMED_CHECK (1 << 6)
+#define BGP_PATH_DMED_SELECTED (1 << 7)
+#define BGP_PATH_STALE (1 << 8)
+#define BGP_PATH_REMOVED (1 << 9)
+#define BGP_PATH_COUNTED (1 << 10)
+#define BGP_PATH_MULTIPATH (1 << 11)
+#define BGP_PATH_MULTIPATH_CHG (1 << 12)
+#define BGP_PATH_RIB_ATTR_CHG (1 << 13)
+#define BGP_PATH_ANNC_NH_SELF (1 << 14)
+#define BGP_PATH_LINK_BW_CHG (1 << 15)
+
+ /* BGP route type. This can be static, RIP, OSPF, BGP etc. */
+ uint8_t type;
+
+ /* When above type is BGP. This sub type specify BGP sub type
+ information. */
+ uint8_t sub_type;
+#define BGP_ROUTE_NORMAL 0
+#define BGP_ROUTE_STATIC 1
+#define BGP_ROUTE_AGGREGATE 2
+#define BGP_ROUTE_REDISTRIBUTE 3
+#ifdef ENABLE_BGP_VNC
+# define BGP_ROUTE_RFP 4
+#endif
+#define BGP_ROUTE_IMPORTED 5 /* from another bgp instance/safi */
+
+ unsigned short instance;
+
+ /* Addpath identifiers */
+ uint32_t addpath_rx_id;
+ struct bgp_addpath_info_data tx_addpath;
+};
+
+/* Structure used in BGP path selection */
+struct bgp_path_info_pair {
+ struct bgp_path_info *old;
+ struct bgp_path_info *new;
+};
+
+/* BGP static route configuration. */
+struct bgp_static {
+ /* Backdoor configuration. */
+ int backdoor;
+
+ /* Label index configuration; applies to LU prefixes. */
+ uint32_t label_index;
+#define BGP_INVALID_LABEL_INDEX 0xFFFFFFFF
+
+ /* Import check status. */
+ uint8_t valid;
+
+ /* IGP metric. */
+ uint32_t igpmetric;
+
+ /* IGP nexthop. */
+ struct in_addr igpnexthop;
+
+ /* Atomic set reference count (ie cause of pathlimit) */
+ uint32_t atomic;
+
+ /* BGP redistribute route-map. */
+ struct {
+ char *name;
+ struct route_map *map;
+ } rmap;
+
+ /* Route Distinguisher */
+ struct prefix_rd prd;
+
+ /* MPLS label. */
+ mpls_label_t label;
+
+ /* EVPN */
+ esi_t *eth_s_id;
+ struct ethaddr *router_mac;
+ uint16_t encap_tunneltype;
+ struct prefix gatewayIp;
+};
+
+/* Aggreagete address:
+ *
+ * advertise-map Set condition to advertise attribute
+ * as-set Generate AS set path information
+ * attribute-map Set attributes of aggregate
+ * route-map Set parameters of aggregate
+ * summary-only Filter more specific routes from updates
+ * suppress-map Conditionally filter more specific routes from updates
+ * <cr>
+ */
+struct bgp_aggregate {
+ /* Summary-only flag. */
+ uint8_t summary_only;
+
+ /* AS set generation. */
+ uint8_t as_set;
+
+ /* Route-map for aggregated route. */
+ struct {
+ char *name;
+ struct route_map *map;
+ } rmap;
+
+ /* Suppress-count. */
+ unsigned long count;
+
+ /* Count of routes of origin type incomplete under this aggregate. */
+ unsigned long incomplete_origin_count;
+
+ /* Count of routes of origin type egp under this aggregate. */
+ unsigned long egp_origin_count;
+
+ /* Optional modify flag to override ORIGIN */
+ uint8_t origin;
+
+ /* Hash containing the communities of all the
+ * routes under this aggregate.
+ */
+ struct hash *community_hash;
+
+ /* Hash containing the extended communities of all the
+ * routes under this aggregate.
+ */
+ struct hash *ecommunity_hash;
+
+ /* Hash containing the large communities of all the
+ * routes under this aggregate.
+ */
+ struct hash *lcommunity_hash;
+
+ /* Hash containing the AS-Path of all the
+ * routes under this aggregate.
+ */
+ struct hash *aspath_hash;
+
+ /* Aggregate route's community. */
+ struct community *community;
+
+ /* Aggregate route's extended community. */
+ struct ecommunity *ecommunity;
+
+ /* Aggregate route's large community. */
+ struct lcommunity *lcommunity;
+
+ /* Aggregate route's as-path. */
+ struct aspath *aspath;
+
+ /* SAFI configuration. */
+ safi_t safi;
+
+ /** Match only equal MED. */
+ bool match_med;
+ /* MED matching state. */
+ /** Did we get the first MED value? */
+ bool med_initialized;
+ /** Are there MED mismatches? */
+ bool med_mismatched;
+ /** MED value found in current group. */
+ uint32_t med_matched_value;
+
+ /**
+ * Test if aggregated address MED of all route match, otherwise
+ * returns `false`. This macro will also return `true` if MED
+ * matching is disabled.
+ */
+#define AGGREGATE_MED_VALID(aggregate) \
+ (((aggregate)->match_med && !(aggregate)->med_mismatched) \
+ || !(aggregate)->match_med)
+
+ /** Suppress map route map name (`NULL` when disabled). */
+ char *suppress_map_name;
+ /** Suppress map route map pointer. */
+ struct route_map *suppress_map;
+};
+
+#define BGP_NEXTHOP_AFI_FROM_NHLEN(nhlen) \
+ ((nhlen) < IPV4_MAX_BYTELEN \
+ ? 0 \
+ : ((nhlen) < IPV6_MAX_BYTELEN ? AFI_IP : AFI_IP6))
+
+#define BGP_ATTR_MP_NEXTHOP_LEN_IP6(attr) \
+ ((attr)->mp_nexthop_len == BGP_ATTR_NHLEN_IPV6_GLOBAL || \
+ (attr)->mp_nexthop_len == BGP_ATTR_NHLEN_IPV6_GLOBAL_AND_LL || \
+ (attr)->mp_nexthop_len == BGP_ATTR_NHLEN_VPNV6_GLOBAL || \
+ (attr)->mp_nexthop_len == BGP_ATTR_NHLEN_VPNV6_GLOBAL_AND_LL)
+
+#define BGP_ATTR_NEXTHOP_AFI_IP6(attr) \
+ (!CHECK_FLAG(attr->flag, ATTR_FLAG_BIT(BGP_ATTR_NEXT_HOP)) && \
+ BGP_ATTR_MP_NEXTHOP_LEN_IP6(attr))
+
+#define BGP_PATH_COUNTABLE(BI) \
+ (!CHECK_FLAG((BI)->flags, BGP_PATH_HISTORY) \
+ && !CHECK_FLAG((BI)->flags, BGP_PATH_REMOVED))
+
+/* Flags which indicate a route is unuseable in some form */
+#define BGP_PATH_UNUSEABLE \
+ (BGP_PATH_HISTORY | BGP_PATH_DAMPED | BGP_PATH_REMOVED)
+/* Macro to check BGP information is alive or not. Sadly,
+ * not equivalent to just checking previous, because of the
+ * sense of the additional VALID flag.
+ */
+#define BGP_PATH_HOLDDOWN(BI) \
+ (!CHECK_FLAG((BI)->flags, BGP_PATH_VALID) \
+ || CHECK_FLAG((BI)->flags, BGP_PATH_UNUSEABLE))
+
+#define DISTRIBUTE_IN_NAME(F) ((F)->dlist[FILTER_IN].name)
+#define DISTRIBUTE_IN(F) ((F)->dlist[FILTER_IN].alist)
+#define DISTRIBUTE_OUT_NAME(F) ((F)->dlist[FILTER_OUT].name)
+#define DISTRIBUTE_OUT(F) ((F)->dlist[FILTER_OUT].alist)
+
+#define PREFIX_LIST_IN_NAME(F) ((F)->plist[FILTER_IN].name)
+#define PREFIX_LIST_IN(F) ((F)->plist[FILTER_IN].plist)
+#define PREFIX_LIST_OUT_NAME(F) ((F)->plist[FILTER_OUT].name)
+#define PREFIX_LIST_OUT(F) ((F)->plist[FILTER_OUT].plist)
+
+#define FILTER_LIST_IN_NAME(F) ((F)->aslist[FILTER_IN].name)
+#define FILTER_LIST_IN(F) ((F)->aslist[FILTER_IN].aslist)
+#define FILTER_LIST_OUT_NAME(F) ((F)->aslist[FILTER_OUT].name)
+#define FILTER_LIST_OUT(F) ((F)->aslist[FILTER_OUT].aslist)
+
+#define ROUTE_MAP_IN_NAME(F) ((F)->map[RMAP_IN].name)
+#define ROUTE_MAP_IN(F) ((F)->map[RMAP_IN].map)
+#define ROUTE_MAP_OUT_NAME(F) ((F)->map[RMAP_OUT].name)
+#define ROUTE_MAP_OUT(F) ((F)->map[RMAP_OUT].map)
+
+#define UNSUPPRESS_MAP_NAME(F) ((F)->usmap.name)
+#define UNSUPPRESS_MAP(F) ((F)->usmap.map)
+
+#define ADVERTISE_MAP_NAME(F) ((F)->advmap.aname)
+#define ADVERTISE_MAP(F) ((F)->advmap.amap)
+
+#define ADVERTISE_CONDITION(F) ((F)->advmap.condition)
+
+#define CONDITION_MAP_NAME(F) ((F)->advmap.cname)
+#define CONDITION_MAP(F) ((F)->advmap.cmap)
+
+/* path PREFIX (addpath rxid NUMBER) */
+#define PATH_ADDPATH_STR_BUFFER PREFIX2STR_BUFFER + 32
+
+enum bgp_path_type {
+ BGP_PATH_SHOW_ALL,
+ BGP_PATH_SHOW_BESTPATH,
+ BGP_PATH_SHOW_MULTIPATH
+};
+
+static inline void bgp_bump_version(struct bgp_dest *dest)
+{
+ dest->version = bgp_table_next_version(bgp_dest_table(dest));
+}
+
+static inline int bgp_fibupd_safi(safi_t safi)
+{
+ if (safi == SAFI_UNICAST || safi == SAFI_MULTICAST
+ || safi == SAFI_LABELED_UNICAST
+ || safi == SAFI_FLOWSPEC)
+ return 1;
+ return 0;
+}
+
+/* Flag if the route path's family matches params. */
+static inline bool is_pi_family_matching(struct bgp_path_info *pi,
+ afi_t afi, safi_t safi)
+{
+ struct bgp_table *table;
+ struct bgp_dest *dest;
+
+ dest = pi->net;
+ if (!dest)
+ return false;
+ table = bgp_dest_table(dest);
+ if (table &&
+ table->afi == afi &&
+ table->safi == safi)
+ return true;
+ return false;
+}
+
+static inline void prep_for_rmap_apply(struct bgp_path_info *dst_pi,
+ struct bgp_path_info_extra *dst_pie,
+ struct bgp_dest *dest,
+ struct bgp_path_info *src_pi,
+ struct peer *peer, struct attr *attr)
+{
+ memset(dst_pi, 0, sizeof(struct bgp_path_info));
+ dst_pi->peer = peer;
+ dst_pi->attr = attr;
+ dst_pi->net = dest;
+ dst_pi->flags = src_pi->flags;
+ dst_pi->type = src_pi->type;
+ dst_pi->sub_type = src_pi->sub_type;
+ dst_pi->mpath = src_pi->mpath;
+ if (src_pi->extra) {
+ memcpy(dst_pie, src_pi->extra,
+ sizeof(struct bgp_path_info_extra));
+ dst_pi->extra = dst_pie;
+ }
+}
+
+static inline bool bgp_check_advertise(struct bgp *bgp, struct bgp_dest *dest)
+{
+ return (!(BGP_SUPPRESS_FIB_ENABLED(bgp) &&
+ CHECK_FLAG(dest->flags, BGP_NODE_FIB_INSTALL_PENDING) &&
+ (!bgp_option_check(BGP_OPT_NO_FIB))));
+}
+
+/*
+ * If we have a fib result and it failed to install( or was withdrawn due
+ * to better admin distance we need to send down the wire a withdrawal.
+ * This function assumes that bgp_check_advertise was already returned
+ * as good to go.
+ */
+static inline bool bgp_check_withdrawal(struct bgp *bgp, struct bgp_dest *dest)
+{
+ struct bgp_path_info *pi, *selected = NULL;
+
+ if (!BGP_SUPPRESS_FIB_ENABLED(bgp))
+ return false;
+
+ for (pi = bgp_dest_get_bgp_path_info(dest); pi; pi = pi->next) {
+ if (CHECK_FLAG(pi->flags, BGP_PATH_SELECTED)) {
+ selected = pi;
+ continue;
+ }
+
+ if (pi->sub_type != BGP_ROUTE_NORMAL)
+ return true;
+ }
+
+ /*
+ * pi is selected and bgp is dealing with a static route
+ * ( ie a network statement of some sort ). FIB installed
+ * is irrelevant
+ *
+ * I am not sure what the above for loop is wanted in this
+ * manner at this point. But I do know that if I have
+ * a static route that is selected and it's the one
+ * being checked for should I withdrawal we do not
+ * want to withdraw the route on installation :)
+ */
+ if (selected && selected->sub_type == BGP_ROUTE_STATIC)
+ return false;
+
+ if (CHECK_FLAG(dest->flags, BGP_NODE_FIB_INSTALLED))
+ return false;
+
+ return true;
+}
+
+/* called before bgp_process() */
+DECLARE_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));
+
+/* BGP show options */
+#define BGP_SHOW_OPT_JSON (1 << 0)
+#define BGP_SHOW_OPT_WIDE (1 << 1)
+#define BGP_SHOW_OPT_AFI_ALL (1 << 2)
+#define BGP_SHOW_OPT_AFI_IP (1 << 3)
+#define BGP_SHOW_OPT_AFI_IP6 (1 << 4)
+#define BGP_SHOW_OPT_ESTABLISHED (1 << 5)
+#define BGP_SHOW_OPT_FAILED (1 << 6)
+#define BGP_SHOW_OPT_DETAIL (1 << 7)
+#define BGP_SHOW_OPT_TERSE (1 << 8)
+
+/* Prototypes. */
+extern void bgp_rib_remove(struct bgp_dest *dest, struct bgp_path_info *pi,
+ struct peer *peer, afi_t afi, safi_t safi);
+extern void bgp_process_queue_init(struct bgp *bgp);
+extern void bgp_route_init(void);
+extern void bgp_route_finish(void);
+extern void bgp_cleanup_routes(struct bgp *);
+extern void bgp_free_aggregate_info(struct bgp_aggregate *aggregate);
+extern void bgp_announce_route(struct peer *peer, afi_t afi, safi_t safi,
+ bool force);
+extern void bgp_stop_announce_route_timer(struct peer_af *paf);
+extern void bgp_announce_route_all(struct peer *);
+extern void bgp_default_originate(struct peer *, afi_t, safi_t, int);
+extern void bgp_soft_reconfig_table_task_cancel(const struct bgp *bgp,
+ const struct bgp_table *table,
+ const struct peer *peer);
+extern void bgp_soft_reconfig_in(struct peer *, afi_t, safi_t);
+extern void bgp_clear_route(struct peer *, afi_t, safi_t);
+extern void bgp_clear_route_all(struct peer *);
+extern void bgp_clear_adj_in(struct peer *, afi_t, safi_t);
+extern void bgp_clear_stale_route(struct peer *, afi_t, safi_t);
+extern void bgp_set_stale_route(struct peer *peer, afi_t afi, safi_t safi);
+extern bool bgp_outbound_policy_exists(struct peer *, struct bgp_filter *);
+extern bool bgp_inbound_policy_exists(struct peer *, struct bgp_filter *);
+
+extern 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);
+extern struct bgp_path_info *bgp_path_info_lock(struct bgp_path_info *path);
+extern struct bgp_path_info *bgp_path_info_unlock(struct bgp_path_info *path);
+extern struct bgp_path_info *
+bgp_get_imported_bpi_ultimate(struct bgp_path_info *info);
+extern void bgp_path_info_add(struct bgp_dest *dest, struct bgp_path_info *pi);
+extern void bgp_path_info_extra_free(struct bgp_path_info_extra **extra);
+extern void bgp_path_info_reap(struct bgp_dest *dest, struct bgp_path_info *pi);
+extern void bgp_path_info_delete(struct bgp_dest *dest,
+ struct bgp_path_info *pi);
+extern struct bgp_path_info_extra *
+bgp_path_info_extra_get(struct bgp_path_info *path);
+extern void bgp_path_info_set_flag(struct bgp_dest *dest,
+ struct bgp_path_info *path, uint32_t flag);
+extern void bgp_path_info_unset_flag(struct bgp_dest *dest,
+ struct bgp_path_info *path, uint32_t flag);
+extern void bgp_path_info_path_with_addpath_rx_str(struct bgp_path_info *pi,
+ char *buf, size_t buf_len);
+
+extern int bgp_nlri_parse_ip(struct peer *, struct attr *, struct bgp_nlri *);
+
+extern bool bgp_maximum_prefix_overflow(struct peer *, afi_t, safi_t, int);
+
+extern 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);
+extern void bgp_redistribute_delete(struct bgp *, struct prefix *, uint8_t,
+ unsigned short);
+extern void bgp_redistribute_withdraw(struct bgp *, afi_t, int, unsigned short);
+
+extern void bgp_static_add(struct bgp *);
+extern void bgp_static_delete(struct bgp *);
+extern void bgp_static_redo_import_check(struct bgp *);
+extern void bgp_purge_static_redist_routes(struct bgp *bgp);
+extern void bgp_static_update(struct bgp *bgp, const struct prefix *p,
+ struct bgp_static *s, afi_t afi, safi_t safi);
+extern void bgp_static_withdraw(struct bgp *bgp, const struct prefix *p,
+ afi_t afi, safi_t safi);
+
+extern int bgp_static_set_safi(afi_t afi, safi_t safi, struct vty *vty,
+ const char *, const char *, const char *,
+ const char *, int, const char *, const char *,
+ const char *, const char *);
+
+extern int bgp_static_unset_safi(afi_t afi, safi_t safi, struct vty *,
+ const char *, const char *, const char *, int,
+ const char *, const char *, const char *);
+
+/* this is primarily for MPLS-VPN */
+extern int 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);
+extern int bgp_withdraw(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, struct bgp_route_evpn *evpn);
+
+/* for bgp_nexthop and bgp_damp */
+extern void bgp_process(struct bgp *, struct bgp_dest *, afi_t, safi_t);
+
+/*
+ * Add an end-of-initial-update marker to the process queue. This is just a
+ * queue element with NULL bgp node.
+ */
+extern void bgp_add_eoiu_mark(struct bgp *);
+extern void bgp_config_write_table_map(struct vty *, struct bgp *, afi_t,
+ safi_t);
+extern void bgp_config_write_network(struct vty *, struct bgp *, afi_t, safi_t);
+extern void bgp_config_write_distance(struct vty *, struct bgp *, afi_t,
+ safi_t);
+
+extern void bgp_aggregate_delete(struct bgp *bgp, const struct prefix *p,
+ afi_t afi, safi_t safi,
+ struct bgp_aggregate *aggregate);
+extern void bgp_aggregate_route(struct bgp *bgp, const struct prefix *p,
+ afi_t afi, safi_t safi,
+ struct bgp_aggregate *aggregate);
+extern void bgp_aggregate_increment(struct bgp *bgp, const struct prefix *p,
+ struct bgp_path_info *path, afi_t afi,
+ safi_t safi);
+extern void bgp_aggregate_decrement(struct bgp *bgp, const struct prefix *p,
+ struct bgp_path_info *path, afi_t afi,
+ safi_t safi);
+
+extern uint8_t bgp_distance_apply(const struct prefix *p,
+ struct bgp_path_info *path, afi_t afi,
+ safi_t safi, struct bgp *bgp);
+
+extern afi_t bgp_node_afi(struct vty *);
+extern safi_t bgp_node_safi(struct vty *);
+
+extern struct bgp_path_info *info_make(int type, int sub_type,
+ unsigned short instance,
+ struct peer *peer, struct attr *attr,
+ struct bgp_dest *dest);
+
+extern 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);
+extern 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);
+extern 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);
+extern void route_vty_out_overlay(struct vty *vty, const struct prefix *p,
+ struct bgp_path_info *path, int display,
+ json_object *json);
+
+extern void bgp_notify_conditional_adv_scanner(struct update_subgroup *subgrp);
+
+extern void subgroup_process_announce_selected(struct update_subgroup *subgrp,
+ struct bgp_path_info *selected,
+ struct bgp_dest *dest,
+ uint32_t addpath_tx_id);
+
+extern 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);
+
+extern void bgp_peer_clear_node_queue_drain_immediate(struct peer *peer);
+extern void bgp_process_queues_drain_immediate(void);
+
+/* for encap/vpn */
+extern struct bgp_dest *bgp_afi_node_lookup(struct bgp_table *table, afi_t afi,
+ safi_t safi, const struct prefix *p,
+ struct prefix_rd *prd);
+extern void bgp_path_info_restore(struct bgp_dest *dest,
+ struct bgp_path_info *path);
+
+extern 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);
+extern void bgp_attr_add_llgr_community(struct attr *attr);
+extern void bgp_attr_add_gshut_community(struct attr *attr);
+
+extern 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);
+extern void bgp_zebra_clear_route_change_flags(struct bgp_dest *dest);
+extern bool bgp_zebra_has_route_changed(struct bgp_path_info *selected);
+
+extern void route_vty_out_detail_header(struct vty *vty, struct bgp *bgp,
+ struct bgp_dest *dest,
+ const struct prefix_rd *prd, afi_t afi,
+ safi_t safi, json_object *json);
+extern void route_vty_out_detail(struct vty *vty, struct bgp *bgp,
+ struct bgp_dest *bn,
+ struct bgp_path_info *path, afi_t afi,
+ safi_t safi, enum rpki_states,
+ json_object *json_paths);
+extern int bgp_show_table_rd(struct vty *vty, struct bgp *bgp, safi_t safi,
+ struct bgp_table *table, struct prefix_rd *prd,
+ enum bgp_show_type type, void *output_arg,
+ bool use_json);
+extern void bgp_best_path_select_defer(struct bgp *bgp, afi_t afi, safi_t safi);
+extern 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);
+extern int bgp_evpn_path_info_cmp(struct bgp *bgp, struct bgp_path_info *new,
+ struct bgp_path_info *exist, int *paths_eq);
+extern void bgp_aggregate_toggle_suppressed(struct bgp_aggregate *aggregate,
+ struct bgp *bgp,
+ const struct prefix *p, afi_t afi,
+ safi_t safi, bool suppress);
+extern void subgroup_announce_reset_nhop(uint8_t family, struct attr *attr);
+const char *
+bgp_path_selection_reason2str(enum bgp_path_selection_reason reason);
+extern bool bgp_addpath_encode_rx(struct peer *peer, afi_t afi, safi_t safi);
+#endif /* _QUAGGA_BGP_ROUTE_H */
diff --git a/bgpd/bgp_routemap.c b/bgpd/bgp_routemap.c
new file mode 100644
index 0000000..143df08
--- /dev/null
+++ b/bgpd/bgp_routemap.c
@@ -0,0 +1,7166 @@
+/* Route map function of bgpd.
+ * Copyright (C) 1998, 1999 Kunihiro Ishiguro
+ *
+ * This file is part of GNU Zebra.
+ *
+ * GNU Zebra is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License as published by the
+ * Free Software Foundation; either version 2, or (at your option) any
+ * later version.
+ *
+ * GNU Zebra is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program; see the file COPYING; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
+ */
+
+#include <zebra.h>
+
+#include "prefix.h"
+#include "filter.h"
+#include "routemap.h"
+#include "command.h"
+#include "linklist.h"
+#include "plist.h"
+#include "memory.h"
+#include "log.h"
+#include "frrlua.h"
+#include "frrscript.h"
+#ifdef HAVE_LIBPCREPOSIX
+#include <pcreposix.h>
+#else
+#include <regex.h>
+#endif /* HAVE_LIBPCREPOSIX */
+#include "buffer.h"
+#include "sockunion.h"
+#include "hash.h"
+#include "queue.h"
+#include "frrstr.h"
+#include "network.h"
+#include "lib/northbound_cli.h"
+
+#include "bgpd/bgpd.h"
+#include "bgpd/bgp_table.h"
+#include "bgpd/bgp_attr.h"
+#include "bgpd/bgp_aspath.h"
+#include "bgpd/bgp_packet.h"
+#include "bgpd/bgp_route.h"
+#include "bgpd/bgp_zebra.h"
+#include "bgpd/bgp_regex.h"
+#include "bgpd/bgp_community.h"
+#include "bgpd/bgp_community_alias.h"
+#include "bgpd/bgp_clist.h"
+#include "bgpd/bgp_filter.h"
+#include "bgpd/bgp_mplsvpn.h"
+#include "bgpd/bgp_ecommunity.h"
+#include "bgpd/bgp_lcommunity.h"
+#include "bgpd/bgp_vty.h"
+#include "bgpd/bgp_debug.h"
+#include "bgpd/bgp_evpn.h"
+#include "bgpd/bgp_evpn_private.h"
+#include "bgpd/bgp_evpn_vty.h"
+#include "bgpd/bgp_mplsvpn.h"
+#include "bgpd/bgp_pbr.h"
+#include "bgpd/bgp_flowspec_util.h"
+#include "bgpd/bgp_encap_types.h"
+#include "bgpd/bgp_mpath.h"
+#include "bgpd/bgp_script.h"
+
+#ifdef ENABLE_BGP_VNC
+#include "bgpd/rfapi/bgp_rfapi_cfg.h"
+#endif
+
+#ifndef VTYSH_EXTRACT_PL
+#include "bgpd/bgp_routemap_clippy.c"
+#endif
+
+/* Memo of route-map commands.
+
+o Cisco route-map
+
+ match as-path : Done
+ community : Done
+ interface : Done
+ ip address : Done
+ ip next-hop : Done
+ ip route-source : Done
+ ip prefix-list : Done
+ ipv6 address : Done
+ ipv6 next-hop : Done
+ ipv6 route-source: (This will not be implemented by bgpd)
+ ipv6 prefix-list : Done
+ length : (This will not be implemented by bgpd)
+ metric : Done
+ route-type : (This will not be implemented by bgpd)
+ tag : Done
+ local-preference : Done
+
+ set as-path prepend : Done
+ as-path tag : Not yet
+ automatic-tag : (This will not be implemented by bgpd)
+ community : Done
+ large-community : Done
+ large-comm-list : Done
+ comm-list : Not yet
+ dampning : Not yet
+ default : (This will not be implemented by bgpd)
+ interface : (This will not be implemented by bgpd)
+ ip default : (This will not be implemented by bgpd)
+ ip next-hop : Done
+ ip precedence : (This will not be implemented by bgpd)
+ ip tos : (This will not be implemented by bgpd)
+ level : (This will not be implemented by bgpd)
+ local-preference : Done
+ metric : Done
+ metric-type : Not yet
+ origin : Done
+ tag : Done
+ weight : Done
+ table : Done
+
+o Local extensions
+
+ set ipv6 next-hop global: Done
+ set ipv6 next-hop prefer-global: Done
+ set ipv6 next-hop local : Done
+ set as-path exclude : Done
+
+*/
+
+/* generic value manipulation to be shared in multiple rules */
+
+#define RMAP_VALUE_SET 0
+#define RMAP_VALUE_ADD 1
+#define RMAP_VALUE_SUB 2
+
+struct rmap_value {
+ uint8_t action;
+ uint8_t variable;
+ uint32_t value;
+};
+
+static int route_value_match(struct rmap_value *rv, uint32_t value)
+{
+ if (rv->variable == 0 && value == rv->value)
+ return RMAP_MATCH;
+
+ return RMAP_NOMATCH;
+}
+
+static uint32_t route_value_adjust(struct rmap_value *rv, uint32_t current,
+ struct peer *peer)
+{
+ uint32_t value;
+
+ switch (rv->variable) {
+ case 1:
+ value = peer->rtt;
+ break;
+ default:
+ value = rv->value;
+ break;
+ }
+
+ switch (rv->action) {
+ case RMAP_VALUE_ADD:
+ if (current > UINT32_MAX - value)
+ return UINT32_MAX;
+ return current + value;
+ case RMAP_VALUE_SUB:
+ if (current <= value)
+ return 0;
+ return current - value;
+ default:
+ return value;
+ }
+}
+
+static void *route_value_compile(const char *arg)
+{
+ uint8_t action = RMAP_VALUE_SET, var = 0;
+ unsigned long larg = 0;
+ char *endptr = NULL;
+ struct rmap_value *rv;
+
+ if (arg[0] == '+') {
+ action = RMAP_VALUE_ADD;
+ arg++;
+ } else if (arg[0] == '-') {
+ action = RMAP_VALUE_SUB;
+ arg++;
+ }
+
+ if (all_digit(arg)) {
+ errno = 0;
+ larg = strtoul(arg, &endptr, 10);
+ if (*arg == 0 || *endptr != 0 || errno || larg > UINT32_MAX)
+ return NULL;
+ } else {
+ if (strcmp(arg, "rtt") == 0)
+ var = 1;
+ else
+ return NULL;
+ }
+
+ rv = XMALLOC(MTYPE_ROUTE_MAP_COMPILED, sizeof(struct rmap_value));
+
+ rv->action = action;
+ rv->variable = var;
+ rv->value = larg;
+ return rv;
+}
+
+static void route_value_free(void *rule)
+{
+ XFREE(MTYPE_ROUTE_MAP_COMPILED, rule);
+}
+
+/* generic as path object to be shared in multiple rules */
+
+static void *route_aspath_compile(const char *arg)
+{
+ struct aspath *aspath;
+
+ aspath = aspath_str2aspath(arg);
+ if (!aspath)
+ return NULL;
+ return aspath;
+}
+
+static void route_aspath_free(void *rule)
+{
+ struct aspath *aspath = rule;
+ aspath_free(aspath);
+}
+
+struct bgp_match_peer_compiled {
+ char *interface;
+ union sockunion su;
+};
+
+/* 'match peer (A.B.C.D|X:X::X:X|WORD)' */
+
+/* Compares the peer specified in the 'match peer' clause with the peer
+ received in bgp_path_info->peer. If it is the same, or if the peer structure
+ received is a peer_group containing it, returns RMAP_MATCH. */
+static enum route_map_cmd_result_t
+route_match_peer(void *rule, const struct prefix *prefix, void *object)
+{
+ struct bgp_match_peer_compiled *pc;
+ union sockunion *su;
+ union sockunion su_def = {
+ .sin = {.sin_family = AF_INET, .sin_addr.s_addr = INADDR_ANY}};
+ struct peer_group *group;
+ struct peer *peer;
+ struct listnode *node, *nnode;
+
+ pc = rule;
+ su = &pc->su;
+ peer = ((struct bgp_path_info *)object)->peer;
+
+ if (pc->interface) {
+ if (!peer->conf_if || !peer->group)
+ return RMAP_NOMATCH;
+
+ if (peer->conf_if && strcmp(peer->conf_if, pc->interface) == 0)
+ return RMAP_MATCH;
+
+ if (peer->group &&
+ strcmp(peer->group->name, pc->interface) == 0)
+ return RMAP_MATCH;
+
+ return RMAP_NOMATCH;
+ }
+
+ /* If su='0.0.0.0' (command 'match peer local'), and it's a
+ NETWORK,
+ REDISTRIBUTE, AGGREGATE-ADDRESS or DEFAULT_GENERATED route
+ => return RMAP_MATCH
+ */
+ if (sockunion_same(su, &su_def)) {
+ int ret;
+ if (CHECK_FLAG(peer->rmap_type, PEER_RMAP_TYPE_NETWORK)
+ || CHECK_FLAG(peer->rmap_type, PEER_RMAP_TYPE_REDISTRIBUTE)
+ || CHECK_FLAG(peer->rmap_type, PEER_RMAP_TYPE_AGGREGATE)
+ || CHECK_FLAG(peer->rmap_type, PEER_RMAP_TYPE_DEFAULT))
+ ret = RMAP_MATCH;
+ else
+ ret = RMAP_NOMATCH;
+ return ret;
+ }
+
+ if (!CHECK_FLAG(peer->sflags, PEER_STATUS_GROUP)) {
+ if (sockunion_same(su, &peer->su))
+ return RMAP_MATCH;
+
+ return RMAP_NOMATCH;
+ } else {
+ group = peer->group;
+ for (ALL_LIST_ELEMENTS(group->peer, node, nnode, peer)) {
+ if (sockunion_same(su, &peer->su))
+ return RMAP_MATCH;
+ }
+ return RMAP_NOMATCH;
+ }
+
+ return RMAP_NOMATCH;
+}
+
+static void *route_match_peer_compile(const char *arg)
+{
+ struct bgp_match_peer_compiled *pc;
+ int ret;
+
+ pc = XCALLOC(MTYPE_ROUTE_MAP_COMPILED,
+ sizeof(struct bgp_match_peer_compiled));
+
+ ret = str2sockunion(strcmp(arg, "local") ? arg : "0.0.0.0", &pc->su);
+ if (ret < 0) {
+ pc->interface = XSTRDUP(MTYPE_ROUTE_MAP_COMPILED, arg);
+ return pc;
+ }
+
+ return pc;
+}
+
+/* Free route map's compiled `ip address' value. */
+static void route_match_peer_free(void *rule)
+{
+ struct bgp_match_peer_compiled *pc = rule;
+
+ XFREE(MTYPE_ROUTE_MAP_COMPILED, pc->interface);
+
+ XFREE(MTYPE_ROUTE_MAP_COMPILED, rule);
+}
+
+/* Route map commands for ip address matching. */
+static const struct route_map_rule_cmd route_match_peer_cmd = {
+ "peer",
+ route_match_peer,
+ route_match_peer_compile,
+ route_match_peer_free
+};
+
+#ifdef HAVE_SCRIPTING
+
+enum frrlua_rm_status {
+ /*
+ * Script function run failure. This will translate into a deny
+ */
+ LUA_RM_FAILURE = 0,
+ /*
+ * No Match was found for the route map function
+ */
+ LUA_RM_NOMATCH,
+ /*
+ * Match was found but no changes were made to the incoming data.
+ */
+ LUA_RM_MATCH,
+ /*
+ * Match was found and data was modified, so figure out what changed
+ */
+ LUA_RM_MATCH_AND_CHANGE,
+};
+
+static enum route_map_cmd_result_t
+route_match_script(void *rule, const struct prefix *prefix, void *object)
+{
+ const char *scriptname = rule;
+ const char *routematch_function = "route_match";
+ struct bgp_path_info *path = (struct bgp_path_info *)object;
+
+ struct frrscript *fs = frrscript_new(scriptname);
+
+ if (frrscript_load(fs, routematch_function, NULL)) {
+ zlog_err(
+ "Issue loading script or function; defaulting to no match");
+ return RMAP_NOMATCH;
+ }
+
+ struct attr newattr = *path->attr;
+
+ int result = frrscript_call(
+ fs, routematch_function, ("prefix", prefix),
+ ("attributes", &newattr), ("peer", path->peer),
+ ("RM_FAILURE", LUA_RM_FAILURE), ("RM_NOMATCH", LUA_RM_NOMATCH),
+ ("RM_MATCH", LUA_RM_MATCH),
+ ("RM_MATCH_AND_CHANGE", LUA_RM_MATCH_AND_CHANGE));
+
+ if (result) {
+ zlog_err("Issue running script rule; defaulting to no match");
+ return RMAP_NOMATCH;
+ }
+
+ long long *action = frrscript_get_result(fs, routematch_function,
+ "action", lua_tointegerp);
+
+ int status = RMAP_NOMATCH;
+
+ switch (*action) {
+ case LUA_RM_FAILURE:
+ zlog_err(
+ "Executing route-map match script '%s' failed; defaulting to no match",
+ scriptname);
+ status = RMAP_NOMATCH;
+ break;
+ case LUA_RM_NOMATCH:
+ status = RMAP_NOMATCH;
+ break;
+ case LUA_RM_MATCH_AND_CHANGE:
+ status = RMAP_MATCH;
+ zlog_debug("Updating attribute based on script's values");
+
+ uint32_t locpref = 0;
+
+ path->attr->med = newattr.med;
+
+ if (path->attr->flag & ATTR_FLAG_BIT(BGP_ATTR_LOCAL_PREF))
+ locpref = path->attr->local_pref;
+ if (locpref != newattr.local_pref) {
+ SET_FLAG(path->attr->flag,
+ ATTR_FLAG_BIT(BGP_ATTR_LOCAL_PREF));
+ path->attr->local_pref = newattr.local_pref;
+ }
+ break;
+ case LUA_RM_MATCH:
+ status = RMAP_MATCH;
+ break;
+ }
+
+ XFREE(MTYPE_SCRIPT_RES, action);
+
+ frrscript_delete(fs);
+
+ return status;
+}
+
+static void *route_match_script_compile(const char *arg)
+{
+ char *scriptname;
+
+ scriptname = XSTRDUP(MTYPE_ROUTE_MAP_COMPILED, arg);
+
+ return scriptname;
+}
+
+static void route_match_script_free(void *rule)
+{
+ XFREE(MTYPE_ROUTE_MAP_COMPILED, rule);
+}
+
+static const struct route_map_rule_cmd route_match_script_cmd = {
+ "script",
+ route_match_script,
+ route_match_script_compile,
+ route_match_script_free
+};
+
+#endif /* HAVE_SCRIPTING */
+
+/* `match ip address IP_ACCESS_LIST' */
+
+/* Match function should return 1 if match is success else return
+ zero. */
+static enum route_map_cmd_result_t
+route_match_ip_address(void *rule, const struct prefix *prefix, void *object)
+{
+ struct access_list *alist;
+
+ if (prefix->family == AF_INET) {
+ alist = access_list_lookup(AFI_IP, (char *)rule);
+ if (alist == NULL)
+ return RMAP_NOMATCH;
+
+ return (access_list_apply(alist, prefix) == FILTER_DENY
+ ? RMAP_NOMATCH
+ : RMAP_MATCH);
+ }
+ return RMAP_NOMATCH;
+}
+
+/* Route map `ip address' match statement. `arg' should be
+ access-list name. */
+static void *route_match_ip_address_compile(const char *arg)
+{
+ return XSTRDUP(MTYPE_ROUTE_MAP_COMPILED, arg);
+}
+
+/* Free route map's compiled `ip address' value. */
+static void route_match_ip_address_free(void *rule)
+{
+ XFREE(MTYPE_ROUTE_MAP_COMPILED, rule);
+}
+
+/* Route map commands for ip address matching. */
+static const struct route_map_rule_cmd route_match_ip_address_cmd = {
+ "ip address",
+ route_match_ip_address,
+ route_match_ip_address_compile,
+ route_match_ip_address_free
+};
+
+/* `match ip next-hop IP_ADDRESS' */
+
+/* Match function return 1 if match is success else return zero. */
+static enum route_map_cmd_result_t
+route_match_ip_next_hop(void *rule, const struct prefix *prefix, void *object)
+{
+ struct access_list *alist;
+ struct bgp_path_info *path;
+ struct prefix_ipv4 p;
+
+ if (prefix->family == AF_INET) {
+ path = object;
+ p.family = AF_INET;
+ p.prefix = path->attr->nexthop;
+ p.prefixlen = IPV4_MAX_BITLEN;
+
+ alist = access_list_lookup(AFI_IP, (char *)rule);
+ if (alist == NULL)
+ return RMAP_NOMATCH;
+
+ return (access_list_apply(alist, &p) == FILTER_DENY
+ ? RMAP_NOMATCH
+ : RMAP_MATCH);
+ }
+ return RMAP_NOMATCH;
+}
+
+/* Route map `ip next-hop' match statement. `arg' is
+ access-list name. */
+static void *route_match_ip_next_hop_compile(const char *arg)
+{
+ return XSTRDUP(MTYPE_ROUTE_MAP_COMPILED, arg);
+}
+
+/* Free route map's compiled `ip address' value. */
+static void route_match_ip_next_hop_free(void *rule)
+{
+ XFREE(MTYPE_ROUTE_MAP_COMPILED, rule);
+}
+
+/* Route map commands for ip next-hop matching. */
+static const struct route_map_rule_cmd route_match_ip_next_hop_cmd = {
+ "ip next-hop",
+ route_match_ip_next_hop,
+ route_match_ip_next_hop_compile,
+ route_match_ip_next_hop_free
+};
+
+/* `match ip route-source ACCESS-LIST' */
+
+/* Match function return 1 if match is success else return zero. */
+static enum route_map_cmd_result_t
+route_match_ip_route_source(void *rule, const struct prefix *pfx, void *object)
+{
+ struct access_list *alist;
+ struct bgp_path_info *path;
+ struct peer *peer;
+ struct prefix_ipv4 p;
+
+ if (pfx->family == AF_INET) {
+ path = object;
+ peer = path->peer;
+
+ if (!peer || sockunion_family(&peer->su) != AF_INET)
+ return RMAP_NOMATCH;
+
+ p.family = AF_INET;
+ p.prefix = peer->su.sin.sin_addr;
+ p.prefixlen = IPV4_MAX_BITLEN;
+
+ alist = access_list_lookup(AFI_IP, (char *)rule);
+ if (alist == NULL)
+ return RMAP_NOMATCH;
+
+ return (access_list_apply(alist, &p) == FILTER_DENY
+ ? RMAP_NOMATCH
+ : RMAP_MATCH);
+ }
+ return RMAP_NOMATCH;
+}
+
+/* Route map `ip route-source' match statement. `arg' is
+ access-list name. */
+static void *route_match_ip_route_source_compile(const char *arg)
+{
+ return XSTRDUP(MTYPE_ROUTE_MAP_COMPILED, arg);
+}
+
+/* Free route map's compiled `ip address' value. */
+static void route_match_ip_route_source_free(void *rule)
+{
+ XFREE(MTYPE_ROUTE_MAP_COMPILED, rule);
+}
+
+/* Route map commands for ip route-source matching. */
+static const struct route_map_rule_cmd route_match_ip_route_source_cmd = {
+ "ip route-source",
+ route_match_ip_route_source,
+ route_match_ip_route_source_compile,
+ route_match_ip_route_source_free
+};
+
+static enum route_map_cmd_result_t
+route_match_prefix_list_flowspec(afi_t afi, struct prefix_list *plist,
+ const struct prefix *p)
+{
+ int ret;
+ struct bgp_pbr_entry_main api;
+
+ memset(&api, 0, sizeof(api));
+
+ if (family2afi(p->u.prefix_flowspec.family) != afi)
+ return RMAP_NOMATCH;
+
+ /* extract match from flowspec entries */
+ ret = bgp_flowspec_match_rules_fill(
+ (uint8_t *)p->u.prefix_flowspec.ptr,
+ p->u.prefix_flowspec.prefixlen, &api,
+ afi);
+ if (ret < 0)
+ return RMAP_NOMATCH;
+ if (api.match_bitmask & PREFIX_DST_PRESENT ||
+ api.match_bitmask_iprule & PREFIX_DST_PRESENT) {
+ if (family2afi((&api.dst_prefix)->family) != afi)
+ return RMAP_NOMATCH;
+ return prefix_list_apply(plist, &api.dst_prefix) == PREFIX_DENY
+ ? RMAP_NOMATCH
+ : RMAP_MATCH;
+ } else if (api.match_bitmask & PREFIX_SRC_PRESENT ||
+ api.match_bitmask_iprule & PREFIX_SRC_PRESENT) {
+ if (family2afi((&api.src_prefix)->family) != afi)
+ return RMAP_NOMATCH;
+ return (prefix_list_apply(plist, &api.src_prefix) == PREFIX_DENY
+ ? RMAP_NOMATCH
+ : RMAP_MATCH);
+ }
+ return RMAP_NOMATCH;
+}
+
+static enum route_map_cmd_result_t
+route_match_address_prefix_list(void *rule, afi_t afi,
+ const struct prefix *prefix, void *object)
+{
+ struct prefix_list *plist;
+
+ plist = prefix_list_lookup(afi, (char *)rule);
+ if (plist == NULL)
+ return RMAP_NOMATCH;
+
+ if (prefix->family == AF_FLOWSPEC)
+ return route_match_prefix_list_flowspec(afi, plist,
+ prefix);
+ return (prefix_list_apply(plist, prefix) == PREFIX_DENY ? RMAP_NOMATCH
+ : RMAP_MATCH);
+}
+
+static enum route_map_cmd_result_t
+route_match_ip_address_prefix_list(void *rule, const struct prefix *prefix,
+ void *object)
+{
+ return route_match_address_prefix_list(rule, AFI_IP, prefix, object);
+}
+
+static void *route_match_ip_address_prefix_list_compile(const char *arg)
+{
+ return XSTRDUP(MTYPE_ROUTE_MAP_COMPILED, arg);
+}
+
+static void route_match_ip_address_prefix_list_free(void *rule)
+{
+ XFREE(MTYPE_ROUTE_MAP_COMPILED, rule);
+}
+
+static const struct route_map_rule_cmd
+ route_match_ip_address_prefix_list_cmd = {
+ "ip address prefix-list",
+ route_match_ip_address_prefix_list,
+ route_match_ip_address_prefix_list_compile,
+ route_match_ip_address_prefix_list_free
+};
+
+/* `match ip next-hop prefix-list PREFIX_LIST' */
+
+static enum route_map_cmd_result_t
+route_match_ip_next_hop_prefix_list(void *rule, const struct prefix *prefix,
+ void *object)
+{
+ struct prefix_list *plist;
+ struct bgp_path_info *path;
+ struct prefix_ipv4 p;
+
+ if (prefix->family == AF_INET) {
+ path = object;
+ p.family = AF_INET;
+ p.prefix = path->attr->nexthop;
+ p.prefixlen = IPV4_MAX_BITLEN;
+
+ plist = prefix_list_lookup(AFI_IP, (char *)rule);
+ if (plist == NULL)
+ return RMAP_NOMATCH;
+
+ return (prefix_list_apply(plist, &p) == PREFIX_DENY
+ ? RMAP_NOMATCH
+ : RMAP_MATCH);
+ }
+ return RMAP_NOMATCH;
+}
+
+static void *route_match_ip_next_hop_prefix_list_compile(const char *arg)
+{
+ return XSTRDUP(MTYPE_ROUTE_MAP_COMPILED, arg);
+}
+
+static void route_match_ip_next_hop_prefix_list_free(void *rule)
+{
+ XFREE(MTYPE_ROUTE_MAP_COMPILED, rule);
+}
+
+static const struct route_map_rule_cmd
+ route_match_ip_next_hop_prefix_list_cmd = {
+ "ip next-hop prefix-list",
+ route_match_ip_next_hop_prefix_list,
+ route_match_ip_next_hop_prefix_list_compile,
+ route_match_ip_next_hop_prefix_list_free
+};
+
+/* `match ipv6 next-hop prefix-list PREFIXLIST_NAME' */
+static enum route_map_cmd_result_t
+route_match_ipv6_next_hop_prefix_list(void *rule, const struct prefix *prefix,
+ void *object)
+{
+ struct prefix_list *plist;
+ struct bgp_path_info *path;
+ struct prefix_ipv6 p;
+
+ if (prefix->family == AF_INET6) {
+ path = object;
+ p.family = AF_INET6;
+ p.prefix = path->attr->mp_nexthop_global;
+ p.prefixlen = IPV6_MAX_BITLEN;
+
+ plist = prefix_list_lookup(AFI_IP6, (char *)rule);
+ if (!plist)
+ return RMAP_NOMATCH;
+
+ if (prefix_list_apply(plist, &p) == PREFIX_PERMIT)
+ return RMAP_MATCH;
+
+ if (path->attr->mp_nexthop_len
+ == BGP_ATTR_NHLEN_IPV6_GLOBAL_AND_LL) {
+ p.prefix = path->attr->mp_nexthop_local;
+ if (prefix_list_apply(plist, &p) == PREFIX_PERMIT)
+ return RMAP_MATCH;
+ }
+ }
+
+ return RMAP_NOMATCH;
+}
+
+static void *route_match_ipv6_next_hop_prefix_list_compile(const char *arg)
+{
+ return XSTRDUP(MTYPE_ROUTE_MAP_COMPILED, arg);
+}
+
+static void route_match_ipv6_next_hop_prefix_list_free(void *rule)
+{
+ XFREE(MTYPE_ROUTE_MAP_COMPILED, rule);
+}
+
+static const struct route_map_rule_cmd
+ route_match_ipv6_next_hop_prefix_list_cmd = {
+ "ipv6 next-hop prefix-list",
+ route_match_ipv6_next_hop_prefix_list,
+ route_match_ipv6_next_hop_prefix_list_compile,
+ route_match_ipv6_next_hop_prefix_list_free
+};
+
+/* `match ip next-hop type <blackhole>' */
+
+static enum route_map_cmd_result_t
+route_match_ip_next_hop_type(void *rule, const struct prefix *prefix,
+ void *object)
+{
+ struct bgp_path_info *path;
+
+ if (prefix->family == AF_INET) {
+ path = (struct bgp_path_info *)object;
+ if (!path)
+ return RMAP_NOMATCH;
+
+ /* If nexthop interface's index can't be resolved and nexthop is
+ set to any address then mark it as type `blackhole`.
+ This logic works for matching kernel/static routes like:
+ `ip route add blackhole 10.0.0.1`. */
+ if (path->attr->nexthop.s_addr == INADDR_ANY
+ && !path->attr->nh_ifindex)
+ return RMAP_MATCH;
+ }
+ return RMAP_NOMATCH;
+}
+
+static void *route_match_ip_next_hop_type_compile(const char *arg)
+{
+ return XSTRDUP(MTYPE_ROUTE_MAP_COMPILED, arg);
+}
+
+static void route_match_ip_next_hop_type_free(void *rule)
+{
+ XFREE(MTYPE_ROUTE_MAP_COMPILED, rule);
+}
+
+static const struct route_map_rule_cmd
+ route_match_ip_next_hop_type_cmd = {
+ "ip next-hop type",
+ route_match_ip_next_hop_type,
+ route_match_ip_next_hop_type_compile,
+ route_match_ip_next_hop_type_free
+};
+
+/* `match ip route-source prefix-list PREFIX_LIST' */
+
+static enum route_map_cmd_result_t
+route_match_ip_route_source_prefix_list(void *rule, const struct prefix *prefix,
+ void *object)
+{
+ struct prefix_list *plist;
+ struct bgp_path_info *path;
+ struct peer *peer;
+ struct prefix_ipv4 p;
+
+ if (prefix->family == AF_INET) {
+ path = object;
+ peer = path->peer;
+
+ if (!peer || sockunion_family(&peer->su) != AF_INET)
+ return RMAP_NOMATCH;
+
+ p.family = AF_INET;
+ p.prefix = peer->su.sin.sin_addr;
+ p.prefixlen = IPV4_MAX_BITLEN;
+
+ plist = prefix_list_lookup(AFI_IP, (char *)rule);
+ if (plist == NULL)
+ return RMAP_NOMATCH;
+
+ return (prefix_list_apply(plist, &p) == PREFIX_DENY
+ ? RMAP_NOMATCH
+ : RMAP_MATCH);
+ }
+ return RMAP_NOMATCH;
+}
+
+static void *route_match_ip_route_source_prefix_list_compile(const char *arg)
+{
+ return XSTRDUP(MTYPE_ROUTE_MAP_COMPILED, arg);
+}
+
+static void route_match_ip_route_source_prefix_list_free(void *rule)
+{
+ XFREE(MTYPE_ROUTE_MAP_COMPILED, rule);
+}
+
+static const struct route_map_rule_cmd
+ route_match_ip_route_source_prefix_list_cmd = {
+ "ip route-source prefix-list",
+ route_match_ip_route_source_prefix_list,
+ route_match_ip_route_source_prefix_list_compile,
+ route_match_ip_route_source_prefix_list_free
+};
+
+/* `match evpn default-route' */
+
+/* Match function should return 1 if match is success else 0 */
+static enum route_map_cmd_result_t
+route_match_evpn_default_route(void *rule, const struct prefix *p, void *object)
+{
+ if (is_evpn_prefix_default(p))
+ return RMAP_MATCH;
+
+ return RMAP_NOMATCH;
+}
+
+/* Route map commands for default-route matching. */
+static const struct route_map_rule_cmd
+ route_match_evpn_default_route_cmd = {
+ "evpn default-route",
+ route_match_evpn_default_route,
+ NULL,
+ NULL
+};
+
+/* `match mac address MAC_ACCESS_LIST' */
+
+/* Match function should return 1 if match is success else return
+ zero. */
+static enum route_map_cmd_result_t
+route_match_mac_address(void *rule, const struct prefix *prefix, void *object)
+{
+ struct access_list *alist;
+ struct prefix p;
+
+ alist = access_list_lookup(AFI_L2VPN, (char *)rule);
+ if (alist == NULL)
+ return RMAP_NOMATCH;
+
+ if (prefix->u.prefix_evpn.route_type != BGP_EVPN_MAC_IP_ROUTE)
+ return RMAP_NOMATCH;
+
+ p.family = AF_ETHERNET;
+ p.prefixlen = ETH_ALEN * 8;
+ p.u.prefix_eth = prefix->u.prefix_evpn.macip_addr.mac;
+
+ return (access_list_apply(alist, &p) == FILTER_DENY ? RMAP_NOMATCH
+ : RMAP_MATCH);
+}
+
+/* Route map `mac address' match statement. `arg' should be
+ access-list name. */
+static void *route_match_mac_address_compile(const char *arg)
+{
+ return XSTRDUP(MTYPE_ROUTE_MAP_COMPILED, arg);
+}
+
+/* Free route map's compiled `ip address' value. */
+static void route_match_mac_address_free(void *rule)
+{
+ XFREE(MTYPE_ROUTE_MAP_COMPILED, rule);
+}
+
+/* Route map commands for mac address matching. */
+static const struct route_map_rule_cmd route_match_mac_address_cmd = {
+ "mac address",
+ route_match_mac_address,
+ route_match_mac_address_compile,
+ route_match_mac_address_free
+};
+
+/*
+ * Match function returns:
+ * ...RMAP_MATCH if match is found.
+ * ...RMAP_NOMATCH if match is not found.
+ * ...RMAP_NOOP to ignore this match check.
+ */
+static enum route_map_cmd_result_t
+route_match_vni(void *rule, const struct prefix *prefix, void *object)
+{
+ vni_t vni = 0;
+ unsigned int label_cnt = 0;
+ struct bgp_path_info *path = NULL;
+ struct prefix_evpn *evp = (struct prefix_evpn *) prefix;
+
+ vni = *((vni_t *)rule);
+ path = (struct bgp_path_info *)object;
+
+ /*
+ * This rmap filter is valid for vxlan tunnel type only.
+ * For any other tunnel type, return noop to ignore
+ * this check.
+ */
+ if (path->attr->encap_tunneltype != BGP_ENCAP_TYPE_VXLAN)
+ return RMAP_NOOP;
+
+ /*
+ * Apply filter to type 1, 2, 5 routes only.
+ * Other route types do not have vni label.
+ */
+ if (evp
+ && (evp->prefix.route_type != BGP_EVPN_AD_ROUTE
+ && evp->prefix.route_type != BGP_EVPN_MAC_IP_ROUTE
+ && evp->prefix.route_type != BGP_EVPN_IP_PREFIX_ROUTE))
+ return RMAP_NOOP;
+
+ if (path->extra == NULL)
+ return RMAP_NOMATCH;
+
+ for (;
+ label_cnt < BGP_MAX_LABELS && label_cnt < path->extra->num_labels;
+ label_cnt++) {
+ if (vni == label2vni(&path->extra->label[label_cnt]))
+ return RMAP_MATCH;
+ }
+
+ return RMAP_NOMATCH;
+}
+
+/* Route map `vni' match statement. */
+static void *route_match_vni_compile(const char *arg)
+{
+ vni_t *vni = NULL;
+ char *end = NULL;
+
+ vni = XMALLOC(MTYPE_ROUTE_MAP_COMPILED, sizeof(vni_t));
+
+ *vni = strtoul(arg, &end, 10);
+ if (*end != '\0') {
+ XFREE(MTYPE_ROUTE_MAP_COMPILED, vni);
+ return NULL;
+ }
+
+ return vni;
+}
+
+/* Free route map's compiled `vni' value. */
+static void route_match_vni_free(void *rule)
+{
+ XFREE(MTYPE_ROUTE_MAP_COMPILED, rule);
+}
+
+/* Route map commands for vni matching. */
+static const struct route_map_rule_cmd route_match_evpn_vni_cmd = {
+ "evpn vni",
+ route_match_vni,
+ route_match_vni_compile,
+ route_match_vni_free
+};
+
+/* `match evpn route-type' */
+
+/* Match function should return 1 if match is success else return
+ zero. */
+static enum route_map_cmd_result_t
+route_match_evpn_route_type(void *rule, const struct prefix *pfx, void *object)
+{
+ uint8_t route_type = 0;
+
+ route_type = *((uint8_t *)rule);
+
+ if (route_type == pfx->u.prefix_evpn.route_type)
+ return RMAP_MATCH;
+
+ return RMAP_NOMATCH;
+}
+
+/* Route map `route-type' match statement. */
+static void *route_match_evpn_route_type_compile(const char *arg)
+{
+ uint8_t *route_type = NULL;
+
+ route_type = XMALLOC(MTYPE_ROUTE_MAP_COMPILED, sizeof(uint8_t));
+
+ if (strncmp(arg, "ea", 2) == 0)
+ *route_type = BGP_EVPN_AD_ROUTE;
+ else if (strncmp(arg, "ma", 2) == 0)
+ *route_type = BGP_EVPN_MAC_IP_ROUTE;
+ else if (strncmp(arg, "mu", 2) == 0)
+ *route_type = BGP_EVPN_IMET_ROUTE;
+ else if (strncmp(arg, "es", 2) == 0)
+ *route_type = BGP_EVPN_ES_ROUTE;
+ else
+ *route_type = BGP_EVPN_IP_PREFIX_ROUTE;
+
+ return route_type;
+}
+
+/* Free route map's compiled `route-type' value. */
+static void route_match_evpn_route_type_free(void *rule)
+{
+ XFREE(MTYPE_ROUTE_MAP_COMPILED, rule);
+}
+
+/* Route map commands for evpn route-type matching. */
+static const struct route_map_rule_cmd route_match_evpn_route_type_cmd = {
+ "evpn route-type",
+ route_match_evpn_route_type,
+ route_match_evpn_route_type_compile,
+ route_match_evpn_route_type_free
+};
+
+/* `match rd' */
+
+/* Match function should return 1 if match is success else return zero. */
+static enum route_map_cmd_result_t
+route_match_rd(void *rule, const struct prefix *prefix, void *object)
+{
+ struct prefix_rd *prd_rule = NULL;
+ const struct prefix_rd *prd_route = NULL;
+ struct bgp_path_info *path = NULL;
+
+ if (prefix->family != AF_EVPN)
+ return RMAP_NOMATCH;
+
+ prd_rule = (struct prefix_rd *)rule;
+ path = (struct bgp_path_info *)object;
+
+ if (path->net == NULL || path->net->pdest == NULL)
+ return RMAP_NOMATCH;
+
+ prd_route = (struct prefix_rd *)bgp_dest_get_prefix(path->net->pdest);
+ if (memcmp(prd_route->val, prd_rule->val, ECOMMUNITY_SIZE) == 0)
+ return RMAP_MATCH;
+
+ return RMAP_NOMATCH;
+}
+
+/* Route map `rd' match statement. */
+static void *route_match_rd_compile(const char *arg)
+{
+ struct prefix_rd *prd;
+ int ret;
+
+ prd = XMALLOC(MTYPE_ROUTE_MAP_COMPILED, sizeof(struct prefix_rd));
+
+ ret = str2prefix_rd(arg, prd);
+ if (!ret) {
+ XFREE(MTYPE_ROUTE_MAP_COMPILED, prd);
+ return NULL;
+ }
+
+ return prd;
+}
+
+/* Free route map's compiled `rd' value. */
+static void route_match_rd_free(void *rule)
+{
+ XFREE(MTYPE_ROUTE_MAP_COMPILED, rule);
+}
+
+/* Route map commands for rd matching. */
+static const struct route_map_rule_cmd route_match_evpn_rd_cmd = {
+ "evpn rd",
+ route_match_rd,
+ route_match_rd_compile,
+ route_match_rd_free
+};
+
+static enum route_map_cmd_result_t
+route_set_evpn_gateway_ip(void *rule, const struct prefix *prefix, void *object)
+{
+ struct ipaddr *gw_ip = rule;
+ struct bgp_path_info *path;
+ struct prefix_evpn *evp;
+
+ if (prefix->family != AF_EVPN)
+ return RMAP_OKAY;
+
+ evp = (struct prefix_evpn *)prefix;
+ if (evp->prefix.route_type != BGP_EVPN_IP_PREFIX_ROUTE)
+ return RMAP_OKAY;
+
+ if ((is_evpn_prefix_ipaddr_v4(evp) && IPADDRSZ(gw_ip) != 4)
+ || (is_evpn_prefix_ipaddr_v6(evp) && IPADDRSZ(gw_ip) != 16))
+ return RMAP_OKAY;
+
+ path = object;
+
+ /* Set gateway-ip value. */
+ path->attr->evpn_overlay.type = OVERLAY_INDEX_GATEWAY_IP;
+ memcpy(&path->attr->evpn_overlay.gw_ip, &gw_ip->ip.addr,
+ IPADDRSZ(gw_ip));
+
+ return RMAP_OKAY;
+}
+
+/*
+ * Route map `evpn gateway-ip' compile function.
+ * Given string is converted to struct ipaddr structure
+ */
+static void *route_set_evpn_gateway_ip_compile(const char *arg)
+{
+ struct ipaddr *gw_ip = NULL;
+ int ret;
+
+ gw_ip = XMALLOC(MTYPE_ROUTE_MAP_COMPILED, sizeof(struct ipaddr));
+
+ ret = str2ipaddr(arg, gw_ip);
+ if (ret < 0) {
+ XFREE(MTYPE_ROUTE_MAP_COMPILED, gw_ip);
+ return NULL;
+ }
+ return gw_ip;
+}
+
+/* Free route map's compiled `evpn gateway_ip' value. */
+static void route_set_evpn_gateway_ip_free(void *rule)
+{
+ struct ipaddr *gw_ip = rule;
+
+ XFREE(MTYPE_ROUTE_MAP_COMPILED, gw_ip);
+}
+
+/* Route map commands for set evpn gateway-ip ipv4. */
+struct route_map_rule_cmd route_set_evpn_gateway_ip_ipv4_cmd = {
+ "evpn gateway-ip ipv4", route_set_evpn_gateway_ip,
+ route_set_evpn_gateway_ip_compile, route_set_evpn_gateway_ip_free};
+
+/* Route map commands for set evpn gateway-ip ipv6. */
+struct route_map_rule_cmd route_set_evpn_gateway_ip_ipv6_cmd = {
+ "evpn gateway-ip ipv6", route_set_evpn_gateway_ip,
+ route_set_evpn_gateway_ip_compile, route_set_evpn_gateway_ip_free};
+
+/* Route map commands for VRF route leak with source vrf matching */
+static enum route_map_cmd_result_t
+route_match_vrl_source_vrf(void *rule, const struct prefix *prefix,
+ void *object)
+{
+ struct bgp_path_info *path;
+ char *vrf_name;
+
+ vrf_name = rule;
+ path = (struct bgp_path_info *)object;
+
+ if (strncmp(vrf_name, "n/a", VRF_NAMSIZ) == 0)
+ return RMAP_NOMATCH;
+
+ if (path->extra == NULL || path->extra->bgp_orig == NULL)
+ return RMAP_NOMATCH;
+
+ if (strncmp(vrf_name, vrf_id_to_name(path->extra->bgp_orig->vrf_id),
+ VRF_NAMSIZ)
+ == 0)
+ return RMAP_MATCH;
+
+ return RMAP_NOMATCH;
+}
+
+static void *route_match_vrl_source_vrf_compile(const char *arg)
+{
+ uint8_t *vrf_name = NULL;
+
+ vrf_name = XSTRDUP(MTYPE_ROUTE_MAP_COMPILED, arg);
+
+ return vrf_name;
+}
+
+/* Free route map's compiled `route-type' value. */
+static void route_match_vrl_source_vrf_free(void *rule)
+{
+ XFREE(MTYPE_ROUTE_MAP_COMPILED, rule);
+}
+
+static const struct route_map_rule_cmd route_match_vrl_source_vrf_cmd = {
+ "source-vrf",
+ route_match_vrl_source_vrf,
+ route_match_vrl_source_vrf_compile,
+ route_match_vrl_source_vrf_free
+};
+
+/* `match alias` */
+static enum route_map_cmd_result_t
+route_match_alias(void *rule, const struct prefix *prefix, void *object)
+{
+ char *alias = rule;
+ struct bgp_path_info *path = object;
+ char **communities;
+ int num;
+ bool found;
+
+ if (bgp_attr_get_community(path->attr)) {
+ found = false;
+ frrstr_split(bgp_attr_get_community(path->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)
+ return RMAP_MATCH;
+ }
+
+ if (bgp_attr_get_lcommunity(path->attr)) {
+ found = false;
+ frrstr_split(bgp_attr_get_lcommunity(path->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)
+ return RMAP_MATCH;
+ }
+
+ return RMAP_NOMATCH;
+}
+
+static void *route_match_alias_compile(const char *arg)
+{
+
+ return XSTRDUP(MTYPE_ROUTE_MAP_COMPILED, arg);
+}
+
+static void route_match_alias_free(void *rule)
+{
+ XFREE(MTYPE_ROUTE_MAP_COMPILED, rule);
+}
+
+static const struct route_map_rule_cmd route_match_alias_cmd = {
+ "alias", route_match_alias, route_match_alias_compile,
+ route_match_alias_free};
+
+/* `match local-preference LOCAL-PREF' */
+
+/* Match function return 1 if match is success else return zero. */
+static enum route_map_cmd_result_t
+route_match_local_pref(void *rule, const struct prefix *prefix, void *object)
+{
+ uint32_t *local_pref;
+ struct bgp_path_info *path;
+
+ local_pref = rule;
+ path = object;
+
+ if (path->attr->local_pref == *local_pref)
+ return RMAP_MATCH;
+ else
+ return RMAP_NOMATCH;
+}
+
+/*
+ * Route map `match local-preference' match statement.
+ * `arg' is local-pref value
+ */
+static void *route_match_local_pref_compile(const char *arg)
+{
+ uint32_t *local_pref;
+ char *endptr = NULL;
+ unsigned long tmpval;
+
+ /* Locpref value shoud be integer. */
+ if (!all_digit(arg))
+ return NULL;
+
+ errno = 0;
+ tmpval = strtoul(arg, &endptr, 10);
+ if (*endptr != '\0' || errno || tmpval > UINT32_MAX)
+ return NULL;
+
+ local_pref = XMALLOC(MTYPE_ROUTE_MAP_COMPILED, sizeof(uint32_t));
+
+ *local_pref = tmpval;
+ return local_pref;
+}
+
+/* Free route map's compiled `match local-preference' value. */
+static void route_match_local_pref_free(void *rule)
+{
+ XFREE(MTYPE_ROUTE_MAP_COMPILED, rule);
+}
+
+/* Route map commands for metric matching. */
+static const struct route_map_rule_cmd route_match_local_pref_cmd = {
+ "local-preference",
+ route_match_local_pref,
+ route_match_local_pref_compile,
+ route_match_local_pref_free
+};
+
+/* `match metric METRIC' */
+
+/* Match function return 1 if match is success else return zero. */
+static enum route_map_cmd_result_t
+route_match_metric(void *rule, const struct prefix *prefix, void *object)
+{
+ struct rmap_value *rv;
+ struct bgp_path_info *path;
+
+ rv = rule;
+ path = object;
+ return route_value_match(rv, path->attr->med);
+}
+
+/* Route map commands for metric matching. */
+static const struct route_map_rule_cmd route_match_metric_cmd = {
+ "metric",
+ route_match_metric,
+ route_value_compile,
+ route_value_free,
+};
+
+/* `match as-path ASPATH' */
+
+/* Match function for as-path match. I assume given object is */
+static enum route_map_cmd_result_t
+route_match_aspath(void *rule, const struct prefix *prefix, void *object)
+{
+
+ struct as_list *as_list;
+ struct bgp_path_info *path;
+
+ as_list = as_list_lookup((char *)rule);
+ if (as_list == NULL)
+ return RMAP_NOMATCH;
+
+ path = object;
+
+ /* Perform match. */
+ return ((as_list_apply(as_list, path->attr->aspath) == AS_FILTER_DENY)
+ ? RMAP_NOMATCH
+ : RMAP_MATCH);
+}
+
+/* Compile function for as-path match. */
+static void *route_match_aspath_compile(const char *arg)
+{
+ return XSTRDUP(MTYPE_ROUTE_MAP_COMPILED, arg);
+}
+
+/* Compile function for as-path match. */
+static void route_match_aspath_free(void *rule)
+{
+ XFREE(MTYPE_ROUTE_MAP_COMPILED, rule);
+}
+
+/* Route map commands for aspath matching. */
+static const struct route_map_rule_cmd route_match_aspath_cmd = {
+ "as-path",
+ route_match_aspath,
+ route_match_aspath_compile,
+ route_match_aspath_free
+};
+
+/* `match community COMMUNIY' */
+struct rmap_community {
+ char *name;
+ uint32_t name_hash;
+ int exact;
+};
+
+/* Match function for community match. */
+static enum route_map_cmd_result_t
+route_match_community(void *rule, const struct prefix *prefix, void *object)
+{
+ struct community_list *list;
+ struct bgp_path_info *path;
+ struct rmap_community *rcom = rule;
+
+ path = object;
+ rcom = rule;
+
+ list = community_list_lookup(bgp_clist, rcom->name, rcom->name_hash,
+ COMMUNITY_LIST_MASTER);
+ if (!list)
+ return RMAP_NOMATCH;
+
+ if (rcom->exact) {
+ if (community_list_exact_match(
+ bgp_attr_get_community(path->attr), list))
+ return RMAP_MATCH;
+ } else {
+ if (community_list_match(bgp_attr_get_community(path->attr),
+ list))
+ return RMAP_MATCH;
+ }
+
+ return RMAP_NOMATCH;
+}
+
+/* Compile function for community match. */
+static void *route_match_community_compile(const char *arg)
+{
+ struct rmap_community *rcom;
+ int len;
+ char *p;
+
+ rcom = XCALLOC(MTYPE_ROUTE_MAP_COMPILED, sizeof(struct rmap_community));
+
+ p = strchr(arg, ' ');
+ if (p) {
+ len = p - arg;
+ rcom->name = XCALLOC(MTYPE_ROUTE_MAP_COMPILED, len + 1);
+ memcpy(rcom->name, arg, len);
+ rcom->exact = 1;
+ } else {
+ rcom->name = XSTRDUP(MTYPE_ROUTE_MAP_COMPILED, arg);
+ rcom->exact = 0;
+ }
+
+ rcom->name_hash = bgp_clist_hash_key(rcom->name);
+ return rcom;
+}
+
+/* Compile function for community match. */
+static void route_match_community_free(void *rule)
+{
+ struct rmap_community *rcom = rule;
+
+ XFREE(MTYPE_ROUTE_MAP_COMPILED, rcom->name);
+ XFREE(MTYPE_ROUTE_MAP_COMPILED, rcom);
+}
+
+/*
+ * In routemap processing there is a need to add the
+ * name as a rule_key in the dependency table. Routemap
+ * lib is unaware of rule_key when exact-match clause
+ * is in use. routemap lib uses the compiled output to
+ * get the rule_key value.
+ */
+static void *route_match_get_community_key(void *rule)
+{
+ struct rmap_community *rcom;
+
+ rcom = rule;
+ return rcom->name;
+}
+
+
+/* Route map commands for community matching. */
+static const struct route_map_rule_cmd route_match_community_cmd = {
+ "community",
+ route_match_community,
+ route_match_community_compile,
+ route_match_community_free,
+ route_match_get_community_key
+};
+
+/* Match function for lcommunity match. */
+static enum route_map_cmd_result_t
+route_match_lcommunity(void *rule, const struct prefix *prefix, void *object)
+{
+ struct community_list *list;
+ struct bgp_path_info *path;
+ struct rmap_community *rcom = rule;
+
+ path = object;
+
+ list = community_list_lookup(bgp_clist, rcom->name, rcom->name_hash,
+ LARGE_COMMUNITY_LIST_MASTER);
+ if (!list)
+ return RMAP_NOMATCH;
+
+ if (rcom->exact) {
+ if (lcommunity_list_exact_match(
+ bgp_attr_get_lcommunity(path->attr), list))
+ return RMAP_MATCH;
+ } else {
+ if (lcommunity_list_match(bgp_attr_get_lcommunity(path->attr),
+ list))
+ return RMAP_MATCH;
+ }
+
+ return RMAP_NOMATCH;
+}
+
+/* Compile function for community match. */
+static void *route_match_lcommunity_compile(const char *arg)
+{
+ struct rmap_community *rcom;
+ int len;
+ char *p;
+
+ rcom = XCALLOC(MTYPE_ROUTE_MAP_COMPILED, sizeof(struct rmap_community));
+
+ p = strchr(arg, ' ');
+ if (p) {
+ len = p - arg;
+ rcom->name = XCALLOC(MTYPE_ROUTE_MAP_COMPILED, len + 1);
+ memcpy(rcom->name, arg, len);
+ rcom->exact = 1;
+ } else {
+ rcom->name = XSTRDUP(MTYPE_ROUTE_MAP_COMPILED, arg);
+ rcom->exact = 0;
+ }
+
+ rcom->name_hash = bgp_clist_hash_key(rcom->name);
+ return rcom;
+}
+
+/* Compile function for community match. */
+static void route_match_lcommunity_free(void *rule)
+{
+ struct rmap_community *rcom = rule;
+
+ XFREE(MTYPE_ROUTE_MAP_COMPILED, rcom->name);
+ XFREE(MTYPE_ROUTE_MAP_COMPILED, rcom);
+}
+
+/* Route map commands for community matching. */
+static const struct route_map_rule_cmd route_match_lcommunity_cmd = {
+ "large-community",
+ route_match_lcommunity,
+ route_match_lcommunity_compile,
+ route_match_lcommunity_free,
+ route_match_get_community_key
+};
+
+
+/* Match function for extcommunity match. */
+static enum route_map_cmd_result_t
+route_match_ecommunity(void *rule, const struct prefix *prefix, void *object)
+{
+ struct community_list *list;
+ struct bgp_path_info *path;
+ struct rmap_community *rcom = rule;
+
+ path = object;
+
+ list = community_list_lookup(bgp_clist, rcom->name, rcom->name_hash,
+ EXTCOMMUNITY_LIST_MASTER);
+ if (!list)
+ return RMAP_NOMATCH;
+
+ if (ecommunity_list_match(bgp_attr_get_ecommunity(path->attr), list))
+ return RMAP_MATCH;
+
+ return RMAP_NOMATCH;
+}
+
+/* Compile function for extcommunity match. */
+static void *route_match_ecommunity_compile(const char *arg)
+{
+ struct rmap_community *rcom;
+
+ rcom = XCALLOC(MTYPE_ROUTE_MAP_COMPILED, sizeof(struct rmap_community));
+ rcom->name = XSTRDUP(MTYPE_ROUTE_MAP_COMPILED, arg);
+ rcom->name_hash = bgp_clist_hash_key(rcom->name);
+
+ return rcom;
+}
+
+/* Compile function for extcommunity match. */
+static void route_match_ecommunity_free(void *rule)
+{
+ struct rmap_community *rcom = rule;
+
+ XFREE(MTYPE_ROUTE_MAP_COMPILED, rcom->name);
+ XFREE(MTYPE_ROUTE_MAP_COMPILED, rcom);
+}
+
+/* Route map commands for community matching. */
+static const struct route_map_rule_cmd route_match_ecommunity_cmd = {
+ "extcommunity",
+ route_match_ecommunity,
+ route_match_ecommunity_compile,
+ route_match_ecommunity_free
+};
+
+/* `match nlri` and `set nlri` are replaced by `address-family ipv4`
+ and `address-family vpnv4'. */
+
+/* `match origin' */
+static enum route_map_cmd_result_t
+route_match_origin(void *rule, const struct prefix *prefix, void *object)
+{
+ uint8_t *origin;
+ struct bgp_path_info *path;
+
+ origin = rule;
+ path = object;
+
+ if (path->attr->origin == *origin)
+ return RMAP_MATCH;
+
+ return RMAP_NOMATCH;
+}
+
+static void *route_match_origin_compile(const char *arg)
+{
+ uint8_t *origin;
+
+ origin = XMALLOC(MTYPE_ROUTE_MAP_COMPILED, sizeof(uint8_t));
+
+ if (strcmp(arg, "igp") == 0)
+ *origin = 0;
+ else if (strcmp(arg, "egp") == 0)
+ *origin = 1;
+ else
+ *origin = 2;
+
+ return origin;
+}
+
+/* Free route map's compiled `ip address' value. */
+static void route_match_origin_free(void *rule)
+{
+ XFREE(MTYPE_ROUTE_MAP_COMPILED, rule);
+}
+
+/* Route map commands for origin matching. */
+static const struct route_map_rule_cmd route_match_origin_cmd = {
+ "origin",
+ route_match_origin,
+ route_match_origin_compile,
+ route_match_origin_free
+};
+
+/* match probability { */
+
+static enum route_map_cmd_result_t
+route_match_probability(void *rule, const struct prefix *prefix, void *object)
+{
+ long r = frr_weak_random();
+
+ switch (*(long *)rule) {
+ case 0:
+ break;
+ case RAND_MAX:
+ return RMAP_MATCH;
+ default:
+ if (r < *(long *)rule) {
+ return RMAP_MATCH;
+ }
+ }
+
+ return RMAP_NOMATCH;
+}
+
+static void *route_match_probability_compile(const char *arg)
+{
+ long *lobule;
+ unsigned perc;
+
+ perc = atoi(arg);
+ lobule = XMALLOC(MTYPE_ROUTE_MAP_COMPILED, sizeof(long));
+
+ switch (perc) {
+ case 0:
+ *lobule = 0;
+ break;
+ case 100:
+ *lobule = RAND_MAX;
+ break;
+ default:
+ *lobule = RAND_MAX / 100 * perc;
+ }
+
+ return lobule;
+}
+
+static void route_match_probability_free(void *rule)
+{
+ XFREE(MTYPE_ROUTE_MAP_COMPILED, rule);
+}
+
+static const struct route_map_rule_cmd route_match_probability_cmd = {
+ "probability",
+ route_match_probability,
+ route_match_probability_compile,
+ route_match_probability_free
+};
+
+/* `match interface IFNAME' */
+/* Match function should return 1 if match is success else return
+ zero. */
+static enum route_map_cmd_result_t
+route_match_interface(void *rule, const struct prefix *prefix, void *object)
+{
+ struct interface *ifp;
+ struct bgp_path_info *path;
+
+ path = object;
+
+ if (!path || !path->peer || !path->peer->bgp)
+ return RMAP_NOMATCH;
+
+ ifp = if_lookup_by_name((char *)rule, path->peer->bgp->vrf_id);
+
+ if (ifp == NULL || ifp->ifindex != path->attr->nh_ifindex)
+ return RMAP_NOMATCH;
+
+ return RMAP_MATCH;
+}
+
+/* Route map `interface' match statement. `arg' should be
+ interface name. */
+static void *route_match_interface_compile(const char *arg)
+{
+ return XSTRDUP(MTYPE_ROUTE_MAP_COMPILED, arg);
+}
+
+/* Free route map's compiled `interface' value. */
+static void route_match_interface_free(void *rule)
+{
+ XFREE(MTYPE_ROUTE_MAP_COMPILED, rule);
+}
+
+/* Route map commands for ip address matching. */
+static const struct route_map_rule_cmd route_match_interface_cmd = {
+ "interface",
+ route_match_interface,
+ route_match_interface_compile,
+ route_match_interface_free
+};
+
+/* } */
+
+/* `set ip next-hop IP_ADDRESS' */
+
+/* Match function return 1 if match is success else return zero. */
+static enum route_map_cmd_result_t
+route_match_tag(void *rule, const struct prefix *prefix, void *object)
+{
+ route_tag_t *tag;
+ struct bgp_path_info *path;
+
+ tag = rule;
+ path = object;
+
+ return ((path->attr->tag == *tag) ? RMAP_MATCH : RMAP_NOMATCH);
+}
+
+
+/* Route map commands for tag matching. */
+static const struct route_map_rule_cmd route_match_tag_cmd = {
+ "tag",
+ route_match_tag,
+ route_map_rule_tag_compile,
+ route_map_rule_tag_free,
+};
+
+static enum route_map_cmd_result_t
+route_set_srte_color(void *rule, const struct prefix *prefix, void *object)
+{
+ uint32_t *srte_color = rule;
+ struct bgp_path_info *path;
+
+ path = object;
+
+ path->attr->srte_color = *srte_color;
+ path->attr->flag |= ATTR_FLAG_BIT(BGP_ATTR_SRTE_COLOR);
+
+ return RMAP_OKAY;
+}
+
+/* Route map `sr-te color' compile function */
+static void *route_set_srte_color_compile(const char *arg)
+{
+ uint32_t *color;
+
+ color = XMALLOC(MTYPE_ROUTE_MAP_COMPILED, sizeof(uint32_t));
+ *color = atoi(arg);
+
+ return color;
+}
+
+/* Free route map's compiled `sr-te color' value. */
+static void route_set_srte_color_free(void *rule)
+{
+ XFREE(MTYPE_ROUTE_MAP_COMPILED, rule);
+}
+
+/* Route map commands for sr-te color set. */
+struct route_map_rule_cmd route_set_srte_color_cmd = {
+ "sr-te color", route_set_srte_color, route_set_srte_color_compile,
+ route_set_srte_color_free};
+
+/* Set nexthop to object. object must be pointer to struct attr. */
+struct rmap_ip_nexthop_set {
+ struct in_addr *address;
+ int peer_address;
+ int unchanged;
+};
+
+static enum route_map_cmd_result_t
+route_set_ip_nexthop(void *rule, const struct prefix *prefix, void *object)
+{
+ struct rmap_ip_nexthop_set *rins = rule;
+ struct bgp_path_info *path;
+ struct peer *peer;
+
+ if (prefix->family == AF_INET6)
+ return RMAP_OKAY;
+
+ path = object;
+ peer = path->peer;
+
+ if (rins->unchanged) {
+ SET_FLAG(path->attr->rmap_change_flags,
+ BATTR_RMAP_NEXTHOP_UNCHANGED);
+ } else if (rins->peer_address) {
+ if ((CHECK_FLAG(peer->rmap_type, PEER_RMAP_TYPE_IN)
+ || CHECK_FLAG(peer->rmap_type, PEER_RMAP_TYPE_IMPORT))
+ && peer->su_remote
+ && sockunion_family(peer->su_remote) == AF_INET) {
+ path->attr->nexthop.s_addr =
+ sockunion2ip(peer->su_remote);
+ path->attr->flag |= ATTR_FLAG_BIT(BGP_ATTR_NEXT_HOP);
+ } else if (CHECK_FLAG(peer->rmap_type, PEER_RMAP_TYPE_OUT)) {
+ /* The next hop value will be set as part of
+ * packet rewrite. Set the flags here to indicate
+ * that rewrite needs to be done.
+ * Also, clear the value.
+ */
+ SET_FLAG(path->attr->rmap_change_flags,
+ BATTR_RMAP_NEXTHOP_PEER_ADDRESS);
+ path->attr->nexthop.s_addr = INADDR_ANY;
+ }
+ } else {
+ /* Set next hop value. */
+ path->attr->flag |= ATTR_FLAG_BIT(BGP_ATTR_NEXT_HOP);
+ path->attr->nexthop = *rins->address;
+ SET_FLAG(path->attr->rmap_change_flags,
+ BATTR_RMAP_IPV4_NHOP_CHANGED);
+ /* case for MP-BGP : MPLS VPN */
+ path->attr->mp_nexthop_global_in = *rins->address;
+ path->attr->mp_nexthop_len = sizeof(*rins->address);
+ }
+
+ return RMAP_OKAY;
+}
+
+/* Route map `ip nexthop' compile function. Given string is converted
+ to struct in_addr structure. */
+static void *route_set_ip_nexthop_compile(const char *arg)
+{
+ struct rmap_ip_nexthop_set *rins;
+ struct in_addr *address = NULL;
+ int peer_address = 0;
+ int unchanged = 0;
+ int ret;
+
+ if (strcmp(arg, "peer-address") == 0)
+ peer_address = 1;
+ else if (strcmp(arg, "unchanged") == 0)
+ unchanged = 1;
+ else {
+ address = XMALLOC(MTYPE_ROUTE_MAP_COMPILED,
+ sizeof(struct in_addr));
+ ret = inet_aton(arg, address);
+
+ if (ret == 0) {
+ XFREE(MTYPE_ROUTE_MAP_COMPILED, address);
+ return NULL;
+ }
+ }
+
+ rins = XCALLOC(MTYPE_ROUTE_MAP_COMPILED,
+ sizeof(struct rmap_ip_nexthop_set));
+
+ rins->address = address;
+ rins->peer_address = peer_address;
+ rins->unchanged = unchanged;
+
+ return rins;
+}
+
+/* Free route map's compiled `ip nexthop' value. */
+static void route_set_ip_nexthop_free(void *rule)
+{
+ struct rmap_ip_nexthop_set *rins = rule;
+
+ XFREE(MTYPE_ROUTE_MAP_COMPILED, rins->address);
+
+ XFREE(MTYPE_ROUTE_MAP_COMPILED, rins);
+}
+
+/* Route map commands for ip nexthop set. */
+static const struct route_map_rule_cmd route_set_ip_nexthop_cmd = {
+ "ip next-hop",
+ route_set_ip_nexthop,
+ route_set_ip_nexthop_compile,
+ route_set_ip_nexthop_free
+};
+
+/* `set l3vpn next-hop encapsulation l3vpn gre' */
+
+/* Set nexthop to object */
+struct rmap_l3vpn_nexthop_encapsulation_set {
+ uint8_t protocol;
+};
+
+static enum route_map_cmd_result_t
+route_set_l3vpn_nexthop_encapsulation(void *rule, const struct prefix *prefix,
+ void *object)
+{
+ struct rmap_l3vpn_nexthop_encapsulation_set *rins = rule;
+ struct bgp_path_info *path;
+
+ path = object;
+
+ if (rins->protocol != IPPROTO_GRE)
+ return RMAP_OKAY;
+
+ SET_FLAG(path->attr->rmap_change_flags, BATTR_RMAP_L3VPN_ACCEPT_GRE);
+ return RMAP_OKAY;
+}
+
+/* Route map `l3vpn nexthop encapsulation' compile function. */
+static void *route_set_l3vpn_nexthop_encapsulation_compile(const char *arg)
+{
+ struct rmap_l3vpn_nexthop_encapsulation_set *rins;
+
+ rins = XCALLOC(MTYPE_ROUTE_MAP_COMPILED,
+ sizeof(struct rmap_l3vpn_nexthop_encapsulation_set));
+
+ /* XXX ALL GRE modes are accepted for now: gre or ip6gre */
+ rins->protocol = IPPROTO_GRE;
+
+ return rins;
+}
+
+/* Free route map's compiled `ip nexthop' value. */
+static void route_set_l3vpn_nexthop_encapsulation_free(void *rule)
+{
+ XFREE(MTYPE_ROUTE_MAP_COMPILED, rule);
+}
+
+/* Route map commands for l3vpn next-hop encapsulation set. */
+static const struct route_map_rule_cmd
+ route_set_l3vpn_nexthop_encapsulation_cmd = {
+ "l3vpn next-hop encapsulation",
+ route_set_l3vpn_nexthop_encapsulation,
+ route_set_l3vpn_nexthop_encapsulation_compile,
+ route_set_l3vpn_nexthop_encapsulation_free};
+
+/* `set local-preference LOCAL_PREF' */
+
+/* Set local preference. */
+static enum route_map_cmd_result_t
+route_set_local_pref(void *rule, const struct prefix *prefix, void *object)
+{
+ struct rmap_value *rv;
+ struct bgp_path_info *path;
+ uint32_t locpref = 0;
+
+ /* Fetch routemap's rule information. */
+ rv = rule;
+ path = object;
+
+ /* Set local preference value. */
+ if (path->attr->local_pref)
+ locpref = path->attr->local_pref;
+
+ path->attr->flag |= ATTR_FLAG_BIT(BGP_ATTR_LOCAL_PREF);
+ path->attr->local_pref = route_value_adjust(rv, locpref, path->peer);
+
+ return RMAP_OKAY;
+}
+
+/* Set local preference rule structure. */
+static const struct route_map_rule_cmd route_set_local_pref_cmd = {
+ "local-preference",
+ route_set_local_pref,
+ route_value_compile,
+ route_value_free,
+};
+
+/* `set weight WEIGHT' */
+
+/* Set weight. */
+static enum route_map_cmd_result_t
+route_set_weight(void *rule, const struct prefix *prefix, void *object)
+{
+ struct rmap_value *rv;
+ struct bgp_path_info *path;
+
+ /* Fetch routemap's rule information. */
+ rv = rule;
+ path = object;
+
+ /* Set weight value. */
+ path->attr->weight = route_value_adjust(rv, 0, path->peer);
+
+ return RMAP_OKAY;
+}
+
+/* Set local preference rule structure. */
+static const struct route_map_rule_cmd route_set_weight_cmd = {
+ "weight",
+ route_set_weight,
+ route_value_compile,
+ route_value_free,
+};
+
+/* `set distance DISTANCE */
+static enum route_map_cmd_result_t
+route_set_distance(void *rule, const struct prefix *prefix, void *object)
+{
+ struct bgp_path_info *path = object;
+ struct rmap_value *rv = rule;
+
+ path->attr->distance = rv->value;
+
+ return RMAP_OKAY;
+}
+
+/* set distance rule structure */
+static const struct route_map_rule_cmd route_set_distance_cmd = {
+ "distance",
+ route_set_distance,
+ route_value_compile,
+ route_value_free,
+};
+
+/* `set metric METRIC' */
+
+/* Set metric to attribute. */
+static enum route_map_cmd_result_t
+route_set_metric(void *rule, const struct prefix *prefix, void *object)
+{
+ struct rmap_value *rv;
+ struct bgp_path_info *path;
+ uint32_t med = 0;
+
+ /* Fetch routemap's rule information. */
+ rv = rule;
+ path = object;
+
+ if (path->attr->flag & ATTR_FLAG_BIT(BGP_ATTR_MULTI_EXIT_DISC))
+ med = path->attr->med;
+
+ path->attr->med = route_value_adjust(rv, med, path->peer);
+ path->attr->flag |= ATTR_FLAG_BIT(BGP_ATTR_MULTI_EXIT_DISC);
+
+ return RMAP_OKAY;
+}
+
+/* Set metric rule structure. */
+static const struct route_map_rule_cmd route_set_metric_cmd = {
+ "metric",
+ route_set_metric,
+ route_value_compile,
+ route_value_free,
+};
+
+/* `set table (1-4294967295)' */
+
+static enum route_map_cmd_result_t
+route_set_table_id(void *rule, const struct prefix *prefix,
+
+ void *object)
+{
+ struct rmap_value *rv;
+ struct bgp_path_info *path;
+
+ /* Fetch routemap's rule information. */
+ rv = rule;
+ path = object;
+
+ path->attr->rmap_table_id = rv->value;
+
+ return RMAP_OKAY;
+}
+
+/* Set table_id rule structure. */
+static const struct route_map_rule_cmd route_set_table_id_cmd = {
+ "table",
+ route_set_table_id,
+ route_value_compile,
+ route_value_free
+};
+
+/* `set as-path prepend ASPATH' */
+
+/* For AS path prepend mechanism. */
+static enum route_map_cmd_result_t
+route_set_aspath_prepend(void *rule, const struct prefix *prefix, void *object)
+{
+ struct aspath *aspath;
+ struct aspath *new;
+ struct bgp_path_info *path;
+
+ path = object;
+
+ if (path->attr->aspath->refcnt)
+ new = aspath_dup(path->attr->aspath);
+ else
+ new = path->attr->aspath;
+
+ if ((uintptr_t)rule > 10) {
+ aspath = rule;
+ aspath_prepend(aspath, new);
+ } else {
+ as_t as = aspath_leftmost(new);
+ if (as)
+ new = aspath_add_seq_n(new, as, (uintptr_t)rule);
+ }
+
+ path->attr->aspath = new;
+
+ return RMAP_OKAY;
+}
+
+static void *route_set_aspath_prepend_compile(const char *arg)
+{
+ unsigned int num;
+
+ if (sscanf(arg, "last-as %u", &num) == 1 && num > 0 && num <= 10)
+ return (void *)(uintptr_t)num;
+
+ return route_aspath_compile(arg);
+}
+
+static void route_set_aspath_prepend_free(void *rule)
+{
+ if ((uintptr_t)rule > 10)
+ route_aspath_free(rule);
+}
+
+
+/* Set as-path prepend rule structure. */
+static const struct route_map_rule_cmd route_set_aspath_prepend_cmd = {
+ "as-path prepend",
+ route_set_aspath_prepend,
+ route_set_aspath_prepend_compile,
+ route_set_aspath_prepend_free,
+};
+
+/* `set as-path exclude ASn' */
+
+/* For ASN exclude mechanism.
+ * Iterate over ASns requested and filter them from the given AS_PATH one by
+ * one.
+ * Make a deep copy of existing AS_PATH, but for the first ASn only.
+ */
+static enum route_map_cmd_result_t
+route_set_aspath_exclude(void *rule, const struct prefix *dummy, void *object)
+{
+ struct aspath *new_path, *exclude_path;
+ struct bgp_path_info *path;
+
+ exclude_path = rule;
+ path = object;
+ if (path->attr->aspath->refcnt)
+ new_path = aspath_dup(path->attr->aspath);
+ else
+ new_path = path->attr->aspath;
+ path->attr->aspath = aspath_filter_exclude(new_path, exclude_path);
+
+ return RMAP_OKAY;
+}
+
+/* Set ASn exlude rule structure. */
+static const struct route_map_rule_cmd route_set_aspath_exclude_cmd = {
+ "as-path exclude",
+ route_set_aspath_exclude,
+ route_aspath_compile,
+ route_aspath_free,
+};
+
+/* `set as-path replace AS-PATH` */
+static void *route_aspath_replace_compile(const char *arg)
+{
+ return XSTRDUP(MTYPE_ROUTE_MAP_COMPILED, arg);
+}
+
+static void route_aspath_replace_free(void *rule)
+{
+ XFREE(MTYPE_ROUTE_MAP_COMPILED, rule);
+}
+
+static enum route_map_cmd_result_t
+route_set_aspath_replace(void *rule, const struct prefix *dummy, void *object)
+{
+ struct aspath *aspath_new;
+ const char *replace = rule;
+ struct bgp_path_info *path = object;
+ as_t own_asn = path->peer->change_local_as ? path->peer->change_local_as
+ : path->peer->local_as;
+
+ if (path->peer->sort != BGP_PEER_EBGP) {
+ zlog_warn(
+ "`set as-path replace` is supported only for EBGP peers");
+ return RMAP_NOOP;
+ }
+
+ if (path->attr->aspath->refcnt)
+ aspath_new = aspath_dup(path->attr->aspath);
+ else
+ aspath_new = path->attr->aspath;
+
+ if (strmatch(replace, "any")) {
+ path->attr->aspath =
+ aspath_replace_all_asn(aspath_new, own_asn);
+ } else {
+ as_t replace_asn = strtoul(replace, NULL, 10);
+
+ path->attr->aspath = aspath_replace_specific_asn(
+ aspath_new, replace_asn, own_asn);
+ }
+
+ aspath_free(aspath_new);
+
+ return RMAP_OKAY;
+}
+
+static const struct route_map_rule_cmd route_set_aspath_replace_cmd = {
+ "as-path replace",
+ route_set_aspath_replace,
+ route_aspath_replace_compile,
+ route_aspath_replace_free,
+};
+
+/* `set community COMMUNITY' */
+struct rmap_com_set {
+ struct community *com;
+ int additive;
+ int none;
+};
+
+/* For community set mechanism. */
+static enum route_map_cmd_result_t
+route_set_community(void *rule, const struct prefix *prefix, void *object)
+{
+ struct rmap_com_set *rcs;
+ struct bgp_path_info *path;
+ struct attr *attr;
+ struct community *new = NULL;
+ struct community *old;
+ struct community *merge;
+
+ rcs = rule;
+ path = object;
+ attr = path->attr;
+ old = bgp_attr_get_community(attr);
+
+ /* "none" case. */
+ if (rcs->none) {
+ bgp_attr_set_community(attr, NULL);
+ /* See the longer comment down below. */
+ if (old && old->refcnt == 0)
+ community_free(&old);
+ return RMAP_OKAY;
+ }
+
+ /* "additive" case. */
+ if (rcs->additive && old) {
+ merge = community_merge(community_dup(old), rcs->com);
+
+ new = community_uniq_sort(merge);
+ community_free(&merge);
+ } else
+ new = community_dup(rcs->com);
+
+ /* HACK: if the old community is not intern'd,
+ * we should free it here, or all reference to it may be
+ * lost.
+ * Really need to cleanup attribute caching sometime.
+ */
+ if (old && old->refcnt == 0)
+ community_free(&old);
+
+ /* will be interned by caller if required */
+ bgp_attr_set_community(attr, new);
+
+ return RMAP_OKAY;
+}
+
+/* Compile function for set community. */
+static void *route_set_community_compile(const char *arg)
+{
+ struct rmap_com_set *rcs;
+ struct community *com = NULL;
+ char *sp;
+ int additive = 0;
+ int none = 0;
+
+ if (strcmp(arg, "none") == 0)
+ none = 1;
+ else {
+ sp = strstr(arg, "additive");
+
+ if (sp && sp > arg) {
+ /* "additive" keyword is included. */
+ additive = 1;
+ *(sp - 1) = '\0';
+ }
+
+ com = community_str2com(arg);
+
+ if (additive)
+ *(sp - 1) = ' ';
+
+ if (!com)
+ return NULL;
+ }
+
+ rcs = XCALLOC(MTYPE_ROUTE_MAP_COMPILED, sizeof(struct rmap_com_set));
+ rcs->com = com;
+ rcs->additive = additive;
+ rcs->none = none;
+
+ return rcs;
+}
+
+/* Free function for set community. */
+static void route_set_community_free(void *rule)
+{
+ struct rmap_com_set *rcs = rule;
+
+ if (rcs->com)
+ community_free(&rcs->com);
+ XFREE(MTYPE_ROUTE_MAP_COMPILED, rcs);
+}
+
+/* Set community rule structure. */
+static const struct route_map_rule_cmd route_set_community_cmd = {
+ "community",
+ route_set_community,
+ route_set_community_compile,
+ route_set_community_free,
+};
+
+/* `set community COMMUNITY' */
+struct rmap_lcom_set {
+ struct lcommunity *lcom;
+ int additive;
+ int none;
+};
+
+
+/* For lcommunity set mechanism. */
+static enum route_map_cmd_result_t
+route_set_lcommunity(void *rule, const struct prefix *prefix, void *object)
+{
+ struct rmap_lcom_set *rcs;
+ struct bgp_path_info *path;
+ struct attr *attr;
+ struct lcommunity *new = NULL;
+ struct lcommunity *old;
+ struct lcommunity *merge;
+
+ rcs = rule;
+ path = object;
+ attr = path->attr;
+ old = bgp_attr_get_lcommunity(attr);
+
+ /* "none" case. */
+ if (rcs->none) {
+ bgp_attr_set_lcommunity(attr, NULL);
+
+ /* See the longer comment down below. */
+ if (old && old->refcnt == 0)
+ lcommunity_free(&old);
+ return RMAP_OKAY;
+ }
+
+ if (rcs->additive && old) {
+ merge = lcommunity_merge(lcommunity_dup(old), rcs->lcom);
+
+ new = lcommunity_uniq_sort(merge);
+ lcommunity_free(&merge);
+ } else
+ new = lcommunity_dup(rcs->lcom);
+
+ /* HACK: if the old large-community is not intern'd,
+ * we should free it here, or all reference to it may be
+ * lost.
+ * Really need to cleanup attribute caching sometime.
+ */
+ if (old && old->refcnt == 0)
+ lcommunity_free(&old);
+
+ /* will be intern()'d or attr_flush()'d by bgp_update_main() */
+ bgp_attr_set_lcommunity(attr, new);
+
+ return RMAP_OKAY;
+}
+
+/* Compile function for set community. */
+static void *route_set_lcommunity_compile(const char *arg)
+{
+ struct rmap_lcom_set *rcs;
+ struct lcommunity *lcom = NULL;
+ char *sp;
+ int additive = 0;
+ int none = 0;
+
+ if (strcmp(arg, "none") == 0)
+ none = 1;
+ else {
+ sp = strstr(arg, "additive");
+
+ if (sp && sp > arg) {
+ /* "additive" keyworkd is included. */
+ additive = 1;
+ *(sp - 1) = '\0';
+ }
+
+ lcom = lcommunity_str2com(arg);
+
+ if (additive)
+ *(sp - 1) = ' ';
+
+ if (!lcom)
+ return NULL;
+ }
+
+ rcs = XCALLOC(MTYPE_ROUTE_MAP_COMPILED, sizeof(struct rmap_com_set));
+ rcs->lcom = lcom;
+ rcs->additive = additive;
+ rcs->none = none;
+
+ return rcs;
+}
+
+/* Free function for set lcommunity. */
+static void route_set_lcommunity_free(void *rule)
+{
+ struct rmap_lcom_set *rcs = rule;
+
+ if (rcs->lcom) {
+ lcommunity_free(&rcs->lcom);
+ }
+ XFREE(MTYPE_ROUTE_MAP_COMPILED, rcs);
+}
+
+/* Set community rule structure. */
+static const struct route_map_rule_cmd route_set_lcommunity_cmd = {
+ "large-community",
+ route_set_lcommunity,
+ route_set_lcommunity_compile,
+ route_set_lcommunity_free,
+};
+
+/* `set large-comm-list (<1-99>|<100-500>|WORD) delete' */
+
+/* For large community set mechanism. */
+static enum route_map_cmd_result_t
+route_set_lcommunity_delete(void *rule, const struct prefix *pfx, void *object)
+{
+ struct community_list *list;
+ struct lcommunity *merge;
+ struct lcommunity *new;
+ struct lcommunity *old;
+ struct bgp_path_info *path;
+ struct rmap_community *rcom = rule;
+
+ if (!rcom)
+ return RMAP_OKAY;
+
+ path = object;
+ list = community_list_lookup(bgp_clist, rcom->name, rcom->name_hash,
+ LARGE_COMMUNITY_LIST_MASTER);
+ old = bgp_attr_get_lcommunity(path->attr);
+
+ if (list && old) {
+ merge = lcommunity_list_match_delete(lcommunity_dup(old), list);
+ new = lcommunity_uniq_sort(merge);
+ lcommunity_free(&merge);
+
+ /* HACK: if the old community is not intern'd,
+ * we should free it here, or all reference to it may be
+ * lost.
+ * Really need to cleanup attribute caching sometime.
+ */
+ if (old->refcnt == 0)
+ lcommunity_free(&old);
+
+ if (new->size == 0) {
+ bgp_attr_set_lcommunity(path->attr, NULL);
+ lcommunity_free(&new);
+ } else {
+ bgp_attr_set_lcommunity(path->attr, new);
+ }
+ }
+
+ return RMAP_OKAY;
+}
+
+/* Compile function for set lcommunity. */
+static void *route_set_lcommunity_delete_compile(const char *arg)
+{
+ struct rmap_community *rcom;
+ char **splits;
+ int num;
+
+ frrstr_split(arg, " ", &splits, &num);
+
+ rcom = XCALLOC(MTYPE_ROUTE_MAP_COMPILED, sizeof(struct rmap_community));
+ rcom->name = XSTRDUP(MTYPE_ROUTE_MAP_COMPILED, splits[0]);
+ rcom->name_hash = bgp_clist_hash_key(rcom->name);
+
+ for (int i = 0; i < num; i++)
+ XFREE(MTYPE_TMP, splits[i]);
+ XFREE(MTYPE_TMP, splits);
+
+ return rcom;
+}
+
+/* Free function for set lcommunity. */
+static void route_set_lcommunity_delete_free(void *rule)
+{
+ struct rmap_community *rcom = rule;
+
+ XFREE(MTYPE_ROUTE_MAP_COMPILED, rcom->name);
+ XFREE(MTYPE_ROUTE_MAP_COMPILED, rcom);
+}
+
+/* Set lcommunity rule structure. */
+static const struct route_map_rule_cmd route_set_lcommunity_delete_cmd = {
+ "large-comm-list",
+ route_set_lcommunity_delete,
+ route_set_lcommunity_delete_compile,
+ route_set_lcommunity_delete_free,
+};
+
+
+/* `set comm-list (<1-99>|<100-500>|WORD) delete' */
+
+/* For community set mechanism. */
+static enum route_map_cmd_result_t
+route_set_community_delete(void *rule, const struct prefix *prefix,
+ void *object)
+{
+ struct community_list *list;
+ struct community *merge;
+ struct community *new;
+ struct community *old;
+ struct bgp_path_info *path;
+ struct rmap_community *rcom = rule;
+
+ if (!rcom)
+ return RMAP_OKAY;
+
+ path = object;
+ list = community_list_lookup(bgp_clist, rcom->name, rcom->name_hash,
+ COMMUNITY_LIST_MASTER);
+ old = bgp_attr_get_community(path->attr);
+
+ if (list && old) {
+ merge = community_list_match_delete(community_dup(old), list);
+ new = community_uniq_sort(merge);
+ community_free(&merge);
+
+ /* HACK: if the old community is not intern'd,
+ * we should free it here, or all reference to it may be
+ * lost.
+ * Really need to cleanup attribute caching sometime.
+ */
+ if (old->refcnt == 0)
+ community_free(&old);
+
+ if (new->size == 0) {
+ bgp_attr_set_community(path->attr, NULL);
+ community_free(&new);
+ } else {
+ bgp_attr_set_community(path->attr, new);
+ }
+ }
+
+ return RMAP_OKAY;
+}
+
+/* Compile function for set community. */
+static void *route_set_community_delete_compile(const char *arg)
+{
+ struct rmap_community *rcom;
+ char **splits;
+ int num;
+
+ frrstr_split(arg, " ", &splits, &num);
+
+ rcom = XCALLOC(MTYPE_ROUTE_MAP_COMPILED, sizeof(struct rmap_community));
+ rcom->name = XSTRDUP(MTYPE_ROUTE_MAP_COMPILED, splits[0]);
+ rcom->name_hash = bgp_clist_hash_key(rcom->name);
+
+ for (int i = 0; i < num; i++)
+ XFREE(MTYPE_TMP, splits[i]);
+ XFREE(MTYPE_TMP, splits);
+
+ return rcom;
+}
+
+/* Free function for set community. */
+static void route_set_community_delete_free(void *rule)
+{
+ struct rmap_community *rcom = rule;
+
+ XFREE(MTYPE_ROUTE_MAP_COMPILED, rcom->name);
+ XFREE(MTYPE_ROUTE_MAP_COMPILED, rcom);
+}
+
+/* Set community rule structure. */
+static const struct route_map_rule_cmd route_set_community_delete_cmd = {
+ "comm-list",
+ route_set_community_delete,
+ route_set_community_delete_compile,
+ route_set_community_delete_free,
+};
+
+/* `set extcommunity rt COMMUNITY' */
+
+struct rmap_ecom_set {
+ struct ecommunity *ecom;
+ bool none;
+};
+
+/* For community set mechanism. Used by _rt and _soo. */
+static enum route_map_cmd_result_t
+route_set_ecommunity(void *rule, const struct prefix *prefix, void *object)
+{
+ struct rmap_ecom_set *rcs;
+ struct ecommunity *new_ecom;
+ struct ecommunity *old_ecom;
+ struct bgp_path_info *path;
+ struct attr *attr;
+
+ rcs = rule;
+ path = object;
+ attr = path->attr;
+
+ if (rcs->none) {
+ bgp_attr_set_ecommunity(attr, NULL);
+ return RMAP_OKAY;
+ }
+
+ if (!rcs->ecom)
+ return RMAP_OKAY;
+
+ /* We assume additive for Extended Community. */
+ old_ecom = bgp_attr_get_ecommunity(path->attr);
+
+ if (old_ecom) {
+ new_ecom =
+ ecommunity_merge(ecommunity_dup(old_ecom), rcs->ecom);
+
+ /* old_ecom->refcnt = 1 => owned elsewhere, e.g.
+ * bgp_update_receive()
+ * ->refcnt = 0 => set by a previous route-map
+ * statement */
+ if (!old_ecom->refcnt)
+ ecommunity_free(&old_ecom);
+ } else
+ new_ecom = ecommunity_dup(rcs->ecom);
+
+ /* will be intern()'d or attr_flush()'d by bgp_update_main() */
+ bgp_attr_set_ecommunity(path->attr, new_ecom);
+
+ return RMAP_OKAY;
+}
+
+static void *route_set_ecommunity_none_compile(const char *arg)
+{
+ struct rmap_ecom_set *rcs;
+ bool none = false;
+
+ if (strncmp(arg, "none", 4) == 0)
+ none = true;
+
+ rcs = XCALLOC(MTYPE_ROUTE_MAP_COMPILED, sizeof(struct rmap_ecom_set));
+ rcs->ecom = NULL;
+ rcs->none = none;
+
+ return rcs;
+}
+
+static void *route_set_ecommunity_rt_compile(const char *arg)
+{
+ struct rmap_ecom_set *rcs;
+ struct ecommunity *ecom;
+
+ ecom = ecommunity_str2com(arg, ECOMMUNITY_ROUTE_TARGET, 0);
+ if (!ecom)
+ return NULL;
+
+ rcs = XCALLOC(MTYPE_ROUTE_MAP_COMPILED, sizeof(struct rmap_ecom_set));
+ rcs->ecom = ecommunity_intern(ecom);
+ rcs->none = false;
+
+ return rcs;
+}
+
+/* Free function for set community. Used by _rt and _soo */
+static void route_set_ecommunity_free(void *rule)
+{
+ struct rmap_ecom_set *rcs = rule;
+
+ if (rcs->ecom)
+ ecommunity_unintern(&rcs->ecom);
+
+ XFREE(MTYPE_ROUTE_MAP_COMPILED, rcs);
+}
+
+static const struct route_map_rule_cmd route_set_ecommunity_none_cmd = {
+ "extcommunity",
+ route_set_ecommunity,
+ route_set_ecommunity_none_compile,
+ route_set_ecommunity_free,
+};
+
+/* Set community rule structure. */
+static const struct route_map_rule_cmd route_set_ecommunity_rt_cmd = {
+ "extcommunity rt",
+ route_set_ecommunity,
+ route_set_ecommunity_rt_compile,
+ route_set_ecommunity_free,
+};
+
+/* `set extcommunity soo COMMUNITY' */
+
+/* Compile function for set community. */
+static void *route_set_ecommunity_soo_compile(const char *arg)
+{
+ struct rmap_ecom_set *rcs;
+ struct ecommunity *ecom;
+
+ ecom = ecommunity_str2com(arg, ECOMMUNITY_SITE_ORIGIN, 0);
+ if (!ecom)
+ return NULL;
+
+ rcs = XCALLOC(MTYPE_ROUTE_MAP_COMPILED, sizeof(struct rmap_ecom_set));
+ rcs->ecom = ecommunity_intern(ecom);
+ rcs->none = false;
+
+ return rcs;
+}
+
+/* Set community rule structure. */
+static const struct route_map_rule_cmd route_set_ecommunity_soo_cmd = {
+ "extcommunity soo",
+ route_set_ecommunity,
+ route_set_ecommunity_soo_compile,
+ route_set_ecommunity_free,
+};
+
+/* `set extcommunity bandwidth' */
+
+struct rmap_ecomm_lb_set {
+ uint8_t lb_type;
+#define RMAP_ECOMM_LB_SET_VALUE 1
+#define RMAP_ECOMM_LB_SET_CUMUL 2
+#define RMAP_ECOMM_LB_SET_NUM_MPATH 3
+ bool non_trans;
+ uint32_t bw;
+};
+
+static enum route_map_cmd_result_t
+route_set_ecommunity_lb(void *rule, const struct prefix *prefix, void *object)
+{
+ struct rmap_ecomm_lb_set *rels = rule;
+ struct bgp_path_info *path;
+ struct peer *peer;
+ struct ecommunity ecom_lb = {0};
+ struct ecommunity_val lb_eval;
+ uint32_t bw_bytes = 0;
+ uint16_t mpath_count = 0;
+ struct ecommunity *new_ecom;
+ struct ecommunity *old_ecom;
+ as_t as;
+
+ path = object;
+ peer = path->peer;
+ if (!peer || !peer->bgp)
+ return RMAP_ERROR;
+
+ /* Build link bandwidth extended community */
+ as = (peer->bgp->as > BGP_AS_MAX) ? BGP_AS_TRANS : peer->bgp->as;
+ if (rels->lb_type == RMAP_ECOMM_LB_SET_VALUE) {
+ bw_bytes = ((uint64_t)rels->bw * 1000 * 1000) / 8;
+ } else if (rels->lb_type == RMAP_ECOMM_LB_SET_CUMUL) {
+ /* process this only for the best path. */
+ if (!CHECK_FLAG(path->flags, BGP_PATH_SELECTED))
+ return RMAP_OKAY;
+
+ bw_bytes = (uint32_t)bgp_path_info_mpath_cumbw(path);
+ if (!bw_bytes)
+ return RMAP_OKAY;
+
+ } else if (rels->lb_type == RMAP_ECOMM_LB_SET_NUM_MPATH) {
+
+ /* process this only for the best path. */
+ if (!CHECK_FLAG(path->flags, BGP_PATH_SELECTED))
+ return RMAP_OKAY;
+
+ bw_bytes = ((uint64_t)peer->bgp->lb_ref_bw * 1000 * 1000) / 8;
+ mpath_count = bgp_path_info_mpath_count(path) + 1;
+ bw_bytes *= mpath_count;
+ }
+
+ encode_lb_extcomm(as, bw_bytes, rels->non_trans, &lb_eval,
+ CHECK_FLAG(peer->flags,
+ PEER_FLAG_DISABLE_LINK_BW_ENCODING_IEEE));
+
+ /* add to route or merge with existing */
+ old_ecom = bgp_attr_get_ecommunity(path->attr);
+ if (old_ecom) {
+ new_ecom = ecommunity_dup(old_ecom);
+ ecommunity_add_val(new_ecom, &lb_eval, true, true);
+ if (!old_ecom->refcnt)
+ ecommunity_free(&old_ecom);
+ } else {
+ ecom_lb.size = 1;
+ ecom_lb.unit_size = ECOMMUNITY_SIZE;
+ ecom_lb.val = (uint8_t *)lb_eval.val;
+ new_ecom = ecommunity_dup(&ecom_lb);
+ }
+
+ /* new_ecom will be intern()'d or attr_flush()'d in call stack */
+ bgp_attr_set_ecommunity(path->attr, new_ecom);
+
+ /* Mark that route-map has set link bandwidth; used in attribute
+ * setting decisions.
+ */
+ SET_FLAG(path->attr->rmap_change_flags, BATTR_RMAP_LINK_BW_SET);
+
+ return RMAP_OKAY;
+}
+
+static void *route_set_ecommunity_lb_compile(const char *arg)
+{
+ struct rmap_ecomm_lb_set *rels;
+ uint8_t lb_type;
+ uint32_t bw = 0;
+ char bw_str[40] = {0};
+ char *p, *str;
+ bool non_trans = false;
+
+ str = (char *)arg;
+ p = strchr(arg, ' ');
+ if (p) {
+ int len;
+
+ len = p - arg;
+ memcpy(bw_str, arg, len);
+ non_trans = true;
+ str = bw_str;
+ }
+
+ if (strcmp(str, "cumulative") == 0)
+ lb_type = RMAP_ECOMM_LB_SET_CUMUL;
+ else if (strcmp(str, "num-multipaths") == 0)
+ lb_type = RMAP_ECOMM_LB_SET_NUM_MPATH;
+ else {
+ char *end = NULL;
+
+ bw = strtoul(str, &end, 10);
+ if (*end != '\0')
+ return NULL;
+ lb_type = RMAP_ECOMM_LB_SET_VALUE;
+ }
+
+ rels = XCALLOC(MTYPE_ROUTE_MAP_COMPILED,
+ sizeof(struct rmap_ecomm_lb_set));
+ rels->lb_type = lb_type;
+ rels->bw = bw;
+ rels->non_trans = non_trans;
+
+ return rels;
+}
+
+static void route_set_ecommunity_lb_free(void *rule)
+{
+ XFREE(MTYPE_ROUTE_MAP_COMPILED, rule);
+}
+
+/* Set community rule structure. */
+struct route_map_rule_cmd route_set_ecommunity_lb_cmd = {
+ "extcommunity bandwidth",
+ route_set_ecommunity_lb,
+ route_set_ecommunity_lb_compile,
+ route_set_ecommunity_lb_free,
+};
+
+/* `set origin ORIGIN' */
+
+/* For origin set. */
+static enum route_map_cmd_result_t
+route_set_origin(void *rule, const struct prefix *prefix, void *object)
+{
+ uint8_t *origin;
+ struct bgp_path_info *path;
+
+ origin = rule;
+ path = object;
+
+ path->attr->origin = *origin;
+
+ return RMAP_OKAY;
+}
+
+/* Compile function for origin set. */
+static void *route_set_origin_compile(const char *arg)
+{
+ uint8_t *origin;
+
+ origin = XMALLOC(MTYPE_ROUTE_MAP_COMPILED, sizeof(uint8_t));
+
+ if (strcmp(arg, "igp") == 0)
+ *origin = 0;
+ else if (strcmp(arg, "egp") == 0)
+ *origin = 1;
+ else
+ *origin = 2;
+
+ return origin;
+}
+
+/* Compile function for origin set. */
+static void route_set_origin_free(void *rule)
+{
+ XFREE(MTYPE_ROUTE_MAP_COMPILED, rule);
+}
+
+/* Set origin rule structure. */
+static const struct route_map_rule_cmd route_set_origin_cmd = {
+ "origin",
+ route_set_origin,
+ route_set_origin_compile,
+ route_set_origin_free,
+};
+
+/* `set atomic-aggregate' */
+
+/* For atomic aggregate set. */
+static enum route_map_cmd_result_t
+route_set_atomic_aggregate(void *rule, const struct prefix *pfx, void *object)
+{
+ struct bgp_path_info *path;
+
+ path = object;
+ path->attr->flag |= ATTR_FLAG_BIT(BGP_ATTR_ATOMIC_AGGREGATE);
+
+ return RMAP_OKAY;
+}
+
+/* Compile function for atomic aggregate. */
+static void *route_set_atomic_aggregate_compile(const char *arg)
+{
+ return (void *)1;
+}
+
+/* Compile function for atomic aggregate. */
+static void route_set_atomic_aggregate_free(void *rule)
+{
+ return;
+}
+
+/* Set atomic aggregate rule structure. */
+static const struct route_map_rule_cmd route_set_atomic_aggregate_cmd = {
+ "atomic-aggregate",
+ route_set_atomic_aggregate,
+ route_set_atomic_aggregate_compile,
+ route_set_atomic_aggregate_free,
+};
+
+/* `set aggregator as AS A.B.C.D' */
+struct aggregator {
+ as_t as;
+ struct in_addr address;
+};
+
+static enum route_map_cmd_result_t
+route_set_aggregator_as(void *rule, const struct prefix *prefix, void *object)
+{
+ struct bgp_path_info *path;
+ struct aggregator *aggregator;
+
+ path = object;
+ aggregator = rule;
+
+ path->attr->aggregator_as = aggregator->as;
+ path->attr->aggregator_addr = aggregator->address;
+ path->attr->flag |= ATTR_FLAG_BIT(BGP_ATTR_AGGREGATOR);
+
+ return RMAP_OKAY;
+}
+
+static void *route_set_aggregator_as_compile(const char *arg)
+{
+ struct aggregator *aggregator;
+ char as[10];
+ char address[20];
+ int ret;
+
+ aggregator =
+ XCALLOC(MTYPE_ROUTE_MAP_COMPILED, sizeof(struct aggregator));
+ if (sscanf(arg, "%s %s", as, address) != 2) {
+ XFREE(MTYPE_ROUTE_MAP_COMPILED, aggregator);
+ return NULL;
+ }
+
+ aggregator->as = strtoul(as, NULL, 10);
+ ret = inet_aton(address, &aggregator->address);
+ if (ret == 0) {
+ XFREE(MTYPE_ROUTE_MAP_COMPILED, aggregator);
+ return NULL;
+ }
+ return aggregator;
+}
+
+static void route_set_aggregator_as_free(void *rule)
+{
+ XFREE(MTYPE_ROUTE_MAP_COMPILED, rule);
+}
+
+static const struct route_map_rule_cmd route_set_aggregator_as_cmd = {
+ "aggregator as",
+ route_set_aggregator_as,
+ route_set_aggregator_as_compile,
+ route_set_aggregator_as_free,
+};
+
+/* Set tag to object. object must be pointer to struct bgp_path_info */
+static enum route_map_cmd_result_t
+route_set_tag(void *rule, const struct prefix *prefix, void *object)
+{
+ route_tag_t *tag;
+ struct bgp_path_info *path;
+
+ tag = rule;
+ path = object;
+
+ /* Set tag value */
+ path->attr->tag = *tag;
+
+ return RMAP_OKAY;
+}
+
+/* Route map commands for tag set. */
+static const struct route_map_rule_cmd route_set_tag_cmd = {
+ "tag",
+ route_set_tag,
+ route_map_rule_tag_compile,
+ route_map_rule_tag_free,
+};
+
+/* Set label-index to object. object must be pointer to struct bgp_path_info */
+static enum route_map_cmd_result_t
+route_set_label_index(void *rule, const struct prefix *prefix, void *object)
+{
+ struct rmap_value *rv;
+ struct bgp_path_info *path;
+ uint32_t label_index;
+
+ /* Fetch routemap's rule information. */
+ rv = rule;
+ path = object;
+
+ /* Set label-index value. */
+ label_index = rv->value;
+ if (label_index) {
+ path->attr->label_index = label_index;
+ path->attr->flag |= ATTR_FLAG_BIT(BGP_ATTR_PREFIX_SID);
+ }
+
+ return RMAP_OKAY;
+}
+
+/* Route map commands for label-index set. */
+static const struct route_map_rule_cmd route_set_label_index_cmd = {
+ "label-index",
+ route_set_label_index,
+ route_value_compile,
+ route_value_free,
+};
+
+/* `match ipv6 address IP_ACCESS_LIST' */
+
+static enum route_map_cmd_result_t
+route_match_ipv6_address(void *rule, const struct prefix *prefix, void *object)
+{
+ struct access_list *alist;
+
+ if (prefix->family == AF_INET6) {
+ alist = access_list_lookup(AFI_IP6, (char *)rule);
+ if (alist == NULL)
+ return RMAP_NOMATCH;
+
+ return (access_list_apply(alist, prefix) == FILTER_DENY
+ ? RMAP_NOMATCH
+ : RMAP_MATCH);
+ }
+ return RMAP_NOMATCH;
+}
+
+static void *route_match_ipv6_address_compile(const char *arg)
+{
+ return XSTRDUP(MTYPE_ROUTE_MAP_COMPILED, arg);
+}
+
+static void route_match_ipv6_address_free(void *rule)
+{
+ XFREE(MTYPE_ROUTE_MAP_COMPILED, rule);
+}
+
+/* Route map commands for ip address matching. */
+static const struct route_map_rule_cmd route_match_ipv6_address_cmd = {
+ "ipv6 address",
+ route_match_ipv6_address,
+ route_match_ipv6_address_compile,
+ route_match_ipv6_address_free
+};
+
+/* `match ipv6 next-hop ACCESSLIST6_NAME' */
+static enum route_map_cmd_result_t
+route_match_ipv6_next_hop(void *rule, const struct prefix *prefix, void *object)
+{
+ struct bgp_path_info *path;
+ struct access_list *alist;
+ struct prefix_ipv6 p;
+
+ if (prefix->family == AF_INET6) {
+ path = object;
+ p.family = AF_INET6;
+ p.prefix = path->attr->mp_nexthop_global;
+ p.prefixlen = IPV6_MAX_BITLEN;
+
+ alist = access_list_lookup(AFI_IP6, (char *)rule);
+ if (!alist)
+ return RMAP_NOMATCH;
+
+ if (access_list_apply(alist, &p) == FILTER_PERMIT)
+ return RMAP_MATCH;
+
+ if (path->attr->mp_nexthop_len
+ == BGP_ATTR_NHLEN_IPV6_GLOBAL_AND_LL) {
+ p.prefix = path->attr->mp_nexthop_local;
+ if (access_list_apply(alist, &p) == FILTER_PERMIT)
+ return RMAP_MATCH;
+ }
+ }
+
+ return RMAP_NOMATCH;
+}
+
+static void *route_match_ipv6_next_hop_compile(const char *arg)
+{
+ return XSTRDUP(MTYPE_ROUTE_MAP_COMPILED, arg);
+}
+
+static void route_match_ipv6_next_hop_free(void *rule)
+{
+ XFREE(MTYPE_ROUTE_MAP_COMPILED, rule);
+}
+
+static const struct route_map_rule_cmd route_match_ipv6_next_hop_cmd = {
+ "ipv6 next-hop",
+ route_match_ipv6_next_hop,
+ route_match_ipv6_next_hop_compile,
+ route_match_ipv6_next_hop_free
+};
+
+/* `match ipv6 next-hop IP_ADDRESS' */
+
+static enum route_map_cmd_result_t
+route_match_ipv6_next_hop_address(void *rule, const struct prefix *prefix,
+ void *object)
+{
+ struct in6_addr *addr = rule;
+ struct bgp_path_info *path;
+
+ path = object;
+
+ if (IPV6_ADDR_SAME(&path->attr->mp_nexthop_global, addr))
+ return RMAP_MATCH;
+
+ if (path->attr->mp_nexthop_len == BGP_ATTR_NHLEN_IPV6_GLOBAL_AND_LL
+ && IPV6_ADDR_SAME(&path->attr->mp_nexthop_local, rule))
+ return RMAP_MATCH;
+
+ return RMAP_NOMATCH;
+}
+
+static void *route_match_ipv6_next_hop_address_compile(const char *arg)
+{
+ struct in6_addr *address;
+ int ret;
+
+ address = XMALLOC(MTYPE_ROUTE_MAP_COMPILED, sizeof(struct in6_addr));
+
+ ret = inet_pton(AF_INET6, arg, address);
+ if (!ret) {
+ XFREE(MTYPE_ROUTE_MAP_COMPILED, address);
+ return NULL;
+ }
+
+ return address;
+}
+
+static void route_match_ipv6_next_hop_address_free(void *rule)
+{
+ XFREE(MTYPE_ROUTE_MAP_COMPILED, rule);
+}
+
+static const struct route_map_rule_cmd route_match_ipv6_next_hop_address_cmd = {
+ "ipv6 next-hop address",
+ route_match_ipv6_next_hop_address,
+ route_match_ipv6_next_hop_address_compile,
+ route_match_ipv6_next_hop_address_free
+};
+
+/* `match ip next-hop IP_ADDRESS' */
+
+static enum route_map_cmd_result_t
+route_match_ipv4_next_hop(void *rule, const struct prefix *prefix, void *object)
+{
+ struct in_addr *addr = rule;
+ struct bgp_path_info *path;
+
+ path = object;
+
+ if (path->attr->nexthop.s_addr == addr->s_addr
+ || (path->attr->mp_nexthop_len == BGP_ATTR_NHLEN_IPV4
+ && IPV4_ADDR_SAME(&path->attr->mp_nexthop_global_in, addr)))
+ return RMAP_MATCH;
+
+ return RMAP_NOMATCH;
+}
+
+static void *route_match_ipv4_next_hop_compile(const char *arg)
+{
+ struct in_addr *address;
+ int ret;
+
+ address = XMALLOC(MTYPE_ROUTE_MAP_COMPILED, sizeof(struct in_addr));
+
+ ret = inet_pton(AF_INET, arg, address);
+ if (!ret) {
+ XFREE(MTYPE_ROUTE_MAP_COMPILED, address);
+ return NULL;
+ }
+
+ return address;
+}
+
+static void route_match_ipv4_next_hop_free(void *rule)
+{
+ XFREE(MTYPE_ROUTE_MAP_COMPILED, rule);
+}
+
+static const struct route_map_rule_cmd route_match_ipv4_next_hop_cmd = {
+ "ip next-hop address",
+ route_match_ipv4_next_hop,
+ route_match_ipv4_next_hop_compile,
+ route_match_ipv4_next_hop_free
+};
+
+/* `match ipv6 address prefix-list PREFIX_LIST' */
+
+static enum route_map_cmd_result_t
+route_match_ipv6_address_prefix_list(void *rule, const struct prefix *prefix,
+ void *object)
+{
+ return route_match_address_prefix_list(rule, AFI_IP6, prefix, object);
+}
+
+static void *route_match_ipv6_address_prefix_list_compile(const char *arg)
+{
+ return XSTRDUP(MTYPE_ROUTE_MAP_COMPILED, arg);
+}
+
+static void route_match_ipv6_address_prefix_list_free(void *rule)
+{
+ XFREE(MTYPE_ROUTE_MAP_COMPILED, rule);
+}
+
+static const struct route_map_rule_cmd
+ route_match_ipv6_address_prefix_list_cmd = {
+ "ipv6 address prefix-list",
+ route_match_ipv6_address_prefix_list,
+ route_match_ipv6_address_prefix_list_compile,
+ route_match_ipv6_address_prefix_list_free
+};
+
+/* `match ipv6 next-hop type <TYPE>' */
+
+static enum route_map_cmd_result_t
+route_match_ipv6_next_hop_type(void *rule, const struct prefix *prefix,
+ void *object)
+{
+ struct bgp_path_info *path;
+ struct in6_addr *addr = rule;
+
+ if (prefix->family == AF_INET6) {
+ path = (struct bgp_path_info *)object;
+ if (!path)
+ return RMAP_NOMATCH;
+
+ if (IPV6_ADDR_SAME(&path->attr->mp_nexthop_global, addr)
+ && !path->attr->nh_ifindex)
+ return RMAP_MATCH;
+ }
+
+ return RMAP_NOMATCH;
+}
+
+static void *route_match_ipv6_next_hop_type_compile(const char *arg)
+{
+ struct in6_addr *address;
+ int ret;
+
+ address = XMALLOC(MTYPE_ROUTE_MAP_COMPILED, sizeof(struct in6_addr));
+
+ ret = inet_pton(AF_INET6, "::0", address);
+ if (!ret) {
+ XFREE(MTYPE_ROUTE_MAP_COMPILED, address);
+ return NULL;
+ }
+
+ return address;
+}
+
+static void route_match_ipv6_next_hop_type_free(void *rule)
+{
+ XFREE(MTYPE_ROUTE_MAP_COMPILED, rule);
+}
+
+static const struct route_map_rule_cmd
+ route_match_ipv6_next_hop_type_cmd = {
+ "ipv6 next-hop type",
+ route_match_ipv6_next_hop_type,
+ route_match_ipv6_next_hop_type_compile,
+ route_match_ipv6_next_hop_type_free
+};
+
+/* `set ipv6 nexthop global IP_ADDRESS' */
+
+/* Set nexthop to object. object must be pointer to struct attr. */
+static enum route_map_cmd_result_t
+route_set_ipv6_nexthop_global(void *rule, const struct prefix *p, void *object)
+{
+ struct in6_addr *address;
+ struct bgp_path_info *path;
+
+ /* Fetch routemap's rule information. */
+ address = rule;
+ path = object;
+
+ /* Set next hop value. */
+ path->attr->mp_nexthop_global = *address;
+
+ /* Set nexthop length. */
+ if (path->attr->mp_nexthop_len == 0)
+ path->attr->mp_nexthop_len = BGP_ATTR_NHLEN_IPV6_GLOBAL;
+
+ SET_FLAG(path->attr->rmap_change_flags,
+ BATTR_RMAP_IPV6_GLOBAL_NHOP_CHANGED);
+
+ return RMAP_OKAY;
+}
+
+/* Route map `ip next-hop' compile function. Given string is converted
+ to struct in_addr structure. */
+static void *route_set_ipv6_nexthop_global_compile(const char *arg)
+{
+ int ret;
+ struct in6_addr *address;
+
+ address = XMALLOC(MTYPE_ROUTE_MAP_COMPILED, sizeof(struct in6_addr));
+
+ ret = inet_pton(AF_INET6, arg, address);
+
+ if (ret == 0) {
+ XFREE(MTYPE_ROUTE_MAP_COMPILED, address);
+ return NULL;
+ }
+
+ return address;
+}
+
+/* Free route map's compiled `ip next-hop' value. */
+static void route_set_ipv6_nexthop_global_free(void *rule)
+{
+ XFREE(MTYPE_ROUTE_MAP_COMPILED, rule);
+}
+
+/* Route map commands for ip nexthop set. */
+static const struct route_map_rule_cmd
+ route_set_ipv6_nexthop_global_cmd = {
+ "ipv6 next-hop global",
+ route_set_ipv6_nexthop_global,
+ route_set_ipv6_nexthop_global_compile,
+ route_set_ipv6_nexthop_global_free
+};
+
+/* Set next-hop preference value. */
+static enum route_map_cmd_result_t
+route_set_ipv6_nexthop_prefer_global(void *rule, const struct prefix *prefix,
+ void *object)
+{
+ struct bgp_path_info *path;
+ struct peer *peer;
+
+ /* Fetch routemap's rule information. */
+ path = object;
+ peer = path->peer;
+
+ if (CHECK_FLAG(peer->rmap_type, PEER_RMAP_TYPE_IN)
+ || CHECK_FLAG(peer->rmap_type, PEER_RMAP_TYPE_IMPORT)) {
+ /* Set next hop preference to global */
+ path->attr->mp_nexthop_prefer_global = true;
+ SET_FLAG(path->attr->rmap_change_flags,
+ BATTR_RMAP_IPV6_PREFER_GLOBAL_CHANGED);
+ } else {
+ path->attr->mp_nexthop_prefer_global = false;
+ SET_FLAG(path->attr->rmap_change_flags,
+ BATTR_RMAP_IPV6_PREFER_GLOBAL_CHANGED);
+ }
+
+ return RMAP_OKAY;
+}
+
+static void *route_set_ipv6_nexthop_prefer_global_compile(const char *arg)
+{
+ int *rins = NULL;
+
+ rins = XCALLOC(MTYPE_ROUTE_MAP_COMPILED, sizeof(int));
+ *rins = 1;
+
+ return rins;
+}
+
+/* Free route map's compiled `ip next-hop' value. */
+static void route_set_ipv6_nexthop_prefer_global_free(void *rule)
+{
+ XFREE(MTYPE_ROUTE_MAP_COMPILED, rule);
+}
+
+/* Route map commands for ip nexthop set preferred. */
+static const struct route_map_rule_cmd
+ route_set_ipv6_nexthop_prefer_global_cmd = {
+ "ipv6 next-hop prefer-global",
+ route_set_ipv6_nexthop_prefer_global,
+ route_set_ipv6_nexthop_prefer_global_compile,
+ route_set_ipv6_nexthop_prefer_global_free
+};
+
+/* `set ipv6 nexthop local IP_ADDRESS' */
+
+/* Set nexthop to object. object must be pointer to struct attr. */
+static enum route_map_cmd_result_t
+route_set_ipv6_nexthop_local(void *rule, const struct prefix *p, void *object)
+{
+ struct in6_addr *address;
+ struct bgp_path_info *path;
+
+ /* Fetch routemap's rule information. */
+ address = rule;
+ path = object;
+
+ /* Set next hop value. */
+ path->attr->mp_nexthop_local = *address;
+
+ /* Set nexthop length. */
+ if (path->attr->mp_nexthop_len != BGP_ATTR_NHLEN_IPV6_GLOBAL_AND_LL)
+ path->attr->mp_nexthop_len = BGP_ATTR_NHLEN_IPV6_GLOBAL_AND_LL;
+
+ SET_FLAG(path->attr->rmap_change_flags,
+ BATTR_RMAP_IPV6_LL_NHOP_CHANGED);
+
+ return RMAP_OKAY;
+}
+
+/* Route map `ip nexthop' compile function. Given string is converted
+ to struct in_addr structure. */
+static void *route_set_ipv6_nexthop_local_compile(const char *arg)
+{
+ int ret;
+ struct in6_addr *address;
+
+ address = XMALLOC(MTYPE_ROUTE_MAP_COMPILED, sizeof(struct in6_addr));
+
+ ret = inet_pton(AF_INET6, arg, address);
+
+ if (ret == 0) {
+ XFREE(MTYPE_ROUTE_MAP_COMPILED, address);
+ return NULL;
+ }
+
+ return address;
+}
+
+/* Free route map's compiled `ip nexthop' value. */
+static void route_set_ipv6_nexthop_local_free(void *rule)
+{
+ XFREE(MTYPE_ROUTE_MAP_COMPILED, rule);
+}
+
+/* Route map commands for ip nexthop set. */
+static const struct route_map_rule_cmd
+ route_set_ipv6_nexthop_local_cmd = {
+ "ipv6 next-hop local",
+ route_set_ipv6_nexthop_local,
+ route_set_ipv6_nexthop_local_compile,
+ route_set_ipv6_nexthop_local_free
+};
+
+/* `set ipv6 nexthop peer-address' */
+
+/* Set nexthop to object. object must be pointer to struct attr. */
+static enum route_map_cmd_result_t
+route_set_ipv6_nexthop_peer(void *rule, const struct prefix *pfx, void *object)
+{
+ struct in6_addr peer_address;
+ struct bgp_path_info *path;
+ struct peer *peer;
+
+ /* Fetch routemap's rule information. */
+ path = object;
+ peer = path->peer;
+
+ if ((CHECK_FLAG(peer->rmap_type, PEER_RMAP_TYPE_IN)
+ || CHECK_FLAG(peer->rmap_type, PEER_RMAP_TYPE_IMPORT))
+ && peer->su_remote
+ && sockunion_family(peer->su_remote) == AF_INET6) {
+ peer_address = peer->su_remote->sin6.sin6_addr;
+ /* Set next hop value and length in attribute. */
+ if (IN6_IS_ADDR_LINKLOCAL(&peer_address)) {
+ path->attr->mp_nexthop_local = peer_address;
+ if (path->attr->mp_nexthop_len
+ != BGP_ATTR_NHLEN_IPV6_GLOBAL_AND_LL)
+ path->attr->mp_nexthop_len =
+ BGP_ATTR_NHLEN_IPV6_GLOBAL_AND_LL;
+ } else {
+ path->attr->mp_nexthop_global = peer_address;
+ if (path->attr->mp_nexthop_len == 0)
+ path->attr->mp_nexthop_len =
+ BGP_ATTR_NHLEN_IPV6_GLOBAL;
+ }
+
+ } else if (CHECK_FLAG(peer->rmap_type, PEER_RMAP_TYPE_OUT)) {
+ /* The next hop value will be set as part of packet
+ * rewrite.
+ * Set the flags here to indicate that rewrite needs to
+ * be done.
+ * Also, clear the value - we clear both global and
+ * link-local
+ * nexthops, whether we send one or both is determined
+ * elsewhere.
+ */
+ SET_FLAG(path->attr->rmap_change_flags,
+ BATTR_RMAP_NEXTHOP_PEER_ADDRESS);
+ /* clear next hop value. */
+ memset(&(path->attr->mp_nexthop_global), 0,
+ sizeof(struct in6_addr));
+ memset(&(path->attr->mp_nexthop_local), 0,
+ sizeof(struct in6_addr));
+ }
+
+ return RMAP_OKAY;
+}
+
+/* Route map `ip next-hop' compile function. Given string is converted
+ to struct in_addr structure. */
+static void *route_set_ipv6_nexthop_peer_compile(const char *arg)
+{
+ int *rins = NULL;
+
+ rins = XCALLOC(MTYPE_ROUTE_MAP_COMPILED, sizeof(int));
+ *rins = 1;
+
+ return rins;
+}
+
+/* Free route map's compiled `ip next-hop' value. */
+static void route_set_ipv6_nexthop_peer_free(void *rule)
+{
+ XFREE(MTYPE_ROUTE_MAP_COMPILED, rule);
+}
+
+/* Route map commands for ip nexthop set. */
+static const struct route_map_rule_cmd route_set_ipv6_nexthop_peer_cmd = {
+ "ipv6 next-hop peer-address",
+ route_set_ipv6_nexthop_peer,
+ route_set_ipv6_nexthop_peer_compile,
+ route_set_ipv6_nexthop_peer_free
+};
+
+/* `set ipv4 vpn next-hop A.B.C.D' */
+
+static enum route_map_cmd_result_t
+route_set_vpnv4_nexthop(void *rule, const struct prefix *prefix, void *object)
+{
+ struct in_addr *address;
+ struct bgp_path_info *path;
+
+ /* Fetch routemap's rule information. */
+ address = rule;
+ path = object;
+
+ /* Set next hop value. */
+ path->attr->mp_nexthop_global_in = *address;
+ path->attr->mp_nexthop_len = BGP_ATTR_NHLEN_IPV4;
+
+ return RMAP_OKAY;
+}
+
+static void *route_set_vpnv4_nexthop_compile(const char *arg)
+{
+ int ret;
+ struct in_addr *address;
+
+ address = XMALLOC(MTYPE_ROUTE_MAP_COMPILED, sizeof(struct in_addr));
+
+ ret = inet_aton(arg, address);
+
+ if (ret == 0) {
+ XFREE(MTYPE_ROUTE_MAP_COMPILED, address);
+ return NULL;
+ }
+
+ return address;
+}
+
+/* `set ipv6 vpn next-hop A.B.C.D' */
+
+static enum route_map_cmd_result_t
+route_set_vpnv6_nexthop(void *rule, const struct prefix *prefix, void *object)
+{
+ struct in6_addr *address;
+ struct bgp_path_info *path;
+
+ /* Fetch routemap's rule information. */
+ address = rule;
+ path = object;
+
+ /* Set next hop value. */
+ memcpy(&path->attr->mp_nexthop_global, address,
+ sizeof(struct in6_addr));
+ path->attr->mp_nexthop_len = BGP_ATTR_NHLEN_VPNV6_GLOBAL;
+
+ return RMAP_OKAY;
+}
+
+static void *route_set_vpnv6_nexthop_compile(const char *arg)
+{
+ int ret;
+ struct in6_addr *address;
+
+ address = XMALLOC(MTYPE_ROUTE_MAP_COMPILED, sizeof(struct in6_addr));
+ ret = inet_pton(AF_INET6, arg, address);
+
+ if (ret == 0) {
+ XFREE(MTYPE_ROUTE_MAP_COMPILED, address);
+ return NULL;
+ }
+
+ return address;
+}
+
+static void route_set_vpn_nexthop_free(void *rule)
+{
+ XFREE(MTYPE_ROUTE_MAP_COMPILED, rule);
+}
+
+/* Route map commands for ipv4 next-hop set. */
+static const struct route_map_rule_cmd route_set_vpnv4_nexthop_cmd = {
+ "ipv4 vpn next-hop",
+ route_set_vpnv4_nexthop,
+ route_set_vpnv4_nexthop_compile,
+ route_set_vpn_nexthop_free
+};
+
+/* Route map commands for ipv6 next-hop set. */
+static const struct route_map_rule_cmd route_set_vpnv6_nexthop_cmd = {
+ "ipv6 vpn next-hop",
+ route_set_vpnv6_nexthop,
+ route_set_vpnv6_nexthop_compile,
+ route_set_vpn_nexthop_free
+};
+
+/* `set originator-id' */
+
+/* For origin set. */
+static enum route_map_cmd_result_t
+route_set_originator_id(void *rule, const struct prefix *prefix, void *object)
+{
+ struct in_addr *address;
+ struct bgp_path_info *path;
+
+ address = rule;
+ path = object;
+
+ path->attr->flag |= ATTR_FLAG_BIT(BGP_ATTR_ORIGINATOR_ID);
+ path->attr->originator_id = *address;
+
+ return RMAP_OKAY;
+}
+
+/* Compile function for originator-id set. */
+static void *route_set_originator_id_compile(const char *arg)
+{
+ int ret;
+ struct in_addr *address;
+
+ address = XMALLOC(MTYPE_ROUTE_MAP_COMPILED, sizeof(struct in_addr));
+
+ ret = inet_aton(arg, address);
+
+ if (ret == 0) {
+ XFREE(MTYPE_ROUTE_MAP_COMPILED, address);
+ return NULL;
+ }
+
+ return address;
+}
+
+/* Compile function for originator_id set. */
+static void route_set_originator_id_free(void *rule)
+{
+ XFREE(MTYPE_ROUTE_MAP_COMPILED, rule);
+}
+
+/* Set originator-id rule structure. */
+static const struct route_map_rule_cmd route_set_originator_id_cmd = {
+ "originator-id",
+ route_set_originator_id,
+ route_set_originator_id_compile,
+ route_set_originator_id_free,
+};
+
+static enum route_map_cmd_result_t
+route_match_rpki_extcommunity(void *rule, const struct prefix *prefix,
+ void *object)
+{
+ struct bgp_path_info *path;
+ struct ecommunity *ecomm;
+ struct ecommunity_val *ecomm_val;
+ enum rpki_states *rpki_status = rule;
+ enum rpki_states ecomm_rpki_status = RPKI_NOT_BEING_USED;
+
+ path = object;
+
+ ecomm = bgp_attr_get_ecommunity(path->attr);
+ if (!ecomm)
+ return RMAP_NOMATCH;
+
+ ecomm_val = ecommunity_lookup(ecomm, ECOMMUNITY_ENCODE_OPAQUE_NON_TRANS,
+ ECOMMUNITY_ORIGIN_VALIDATION_STATE);
+ if (!ecomm_val)
+ return RMAP_NOMATCH;
+
+ /* The Origin Validation State is encoded in the last octet of
+ * the extended community.
+ */
+ switch (ecomm_val->val[7]) {
+ case ECOMMUNITY_ORIGIN_VALIDATION_STATE_VALID:
+ ecomm_rpki_status = RPKI_VALID;
+ break;
+ case ECOMMUNITY_ORIGIN_VALIDATION_STATE_NOTFOUND:
+ ecomm_rpki_status = RPKI_NOTFOUND;
+ break;
+ case ECOMMUNITY_ORIGIN_VALIDATION_STATE_INVALID:
+ ecomm_rpki_status = RPKI_INVALID;
+ break;
+ case ECOMMUNITY_ORIGIN_VALIDATION_STATE_NOTUSED:
+ break;
+ }
+
+ if (ecomm_rpki_status == *rpki_status)
+ return RMAP_MATCH;
+
+ return RMAP_NOMATCH;
+}
+
+static void *route_match_extcommunity_compile(const char *arg)
+{
+ int *rpki_status;
+
+ rpki_status = XMALLOC(MTYPE_ROUTE_MAP_COMPILED, sizeof(int));
+
+ if (strcmp(arg, "valid") == 0)
+ *rpki_status = RPKI_VALID;
+ else if (strcmp(arg, "invalid") == 0)
+ *rpki_status = RPKI_INVALID;
+ else
+ *rpki_status = RPKI_NOTFOUND;
+
+ return rpki_status;
+}
+
+static const struct route_map_rule_cmd route_match_rpki_extcommunity_cmd = {
+ "rpki-extcommunity",
+ route_match_rpki_extcommunity,
+ route_match_extcommunity_compile,
+ route_value_free
+};
+
+/*
+ * This is the workhorse routine for processing in/out routemap
+ * modifications.
+ */
+static void bgp_route_map_process_peer(const char *rmap_name,
+ struct route_map *map, struct peer *peer,
+ int afi, int safi, int route_update)
+{
+ struct bgp_filter *filter;
+
+ if (!peer || !rmap_name)
+ return;
+
+ filter = &peer->filter[afi][safi];
+ /*
+ * in is for non-route-server clients,
+ * out is for all peers
+ */
+ if (filter->map[RMAP_IN].name
+ && (strcmp(rmap_name, filter->map[RMAP_IN].name) == 0)) {
+ filter->map[RMAP_IN].map = map;
+
+ if (route_update && peer_established(peer)) {
+ if (CHECK_FLAG(peer->af_flags[afi][safi],
+ PEER_FLAG_SOFT_RECONFIG)) {
+ if (bgp_debug_update(peer, NULL, NULL, 1))
+ zlog_debug(
+ "Processing route_map %s(%s:%s) update on peer %s (inbound, soft-reconfig)",
+ rmap_name, afi2str(afi),
+ safi2str(safi), peer->host);
+
+ bgp_soft_reconfig_in(peer, afi, safi);
+ } else if (CHECK_FLAG(peer->cap,
+ PEER_CAP_REFRESH_OLD_RCV)
+ || CHECK_FLAG(peer->cap,
+ PEER_CAP_REFRESH_NEW_RCV)) {
+ if (bgp_debug_update(peer, NULL, NULL, 1))
+ zlog_debug(
+ "Processing route_map %s(%s:%s) update on peer %s (inbound, route-refresh)",
+ rmap_name, afi2str(afi),
+ safi2str(safi), peer->host);
+ bgp_route_refresh_send(
+ peer, afi, safi, 0, 0, 0,
+ BGP_ROUTE_REFRESH_NORMAL);
+ }
+ }
+ }
+
+ /*
+ * For outbound, unsuppress and default-originate map change (content or
+ * map created), merely update the "config" here, the actual route
+ * announcement happens at the group level.
+ */
+ if (filter->map[RMAP_OUT].name
+ && (strcmp(rmap_name, filter->map[RMAP_OUT].name) == 0))
+ filter->map[RMAP_OUT].map = map;
+
+ if (filter->usmap.name && (strcmp(rmap_name, filter->usmap.name) == 0))
+ filter->usmap.map = map;
+
+ if (filter->advmap.aname
+ && (strcmp(rmap_name, filter->advmap.aname) == 0)) {
+ filter->advmap.amap = map;
+ }
+
+ if (filter->advmap.cname
+ && (strcmp(rmap_name, filter->advmap.cname) == 0)) {
+ filter->advmap.cmap = map;
+ }
+
+ if (peer->default_rmap[afi][safi].name
+ && (strcmp(rmap_name, peer->default_rmap[afi][safi].name) == 0))
+ peer->default_rmap[afi][safi].map = map;
+
+ /* Notify BGP conditional advertisement scanner percess */
+ peer->advmap_config_change[afi][safi] = true;
+}
+
+static void bgp_route_map_update_peer_group(const char *rmap_name,
+ struct route_map *map,
+ struct bgp *bgp)
+{
+ struct peer_group *group;
+ struct listnode *node, *nnode;
+ struct bgp_filter *filter;
+ int afi, safi;
+ int direct;
+
+ if (!bgp)
+ return;
+
+ /* All the peers have been updated correctly already. This is
+ * just updating the placeholder data. No real update required.
+ */
+ for (ALL_LIST_ELEMENTS(bgp->group, node, nnode, group)) {
+ FOREACH_AFI_SAFI (afi, safi) {
+ filter = &group->conf->filter[afi][safi];
+
+ for (direct = RMAP_IN; direct < RMAP_MAX; direct++) {
+ if ((filter->map[direct].name)
+ && (strcmp(rmap_name,
+ filter->map[direct].name)
+ == 0))
+ filter->map[direct].map = map;
+ }
+
+ if (filter->usmap.name
+ && (strcmp(rmap_name, filter->usmap.name) == 0))
+ filter->usmap.map = map;
+
+ if (filter->advmap.aname &&
+ (strcmp(rmap_name, filter->advmap.aname) == 0))
+ filter->advmap.amap = map;
+
+ if (filter->advmap.cname &&
+ (strcmp(rmap_name, filter->advmap.cname) == 0))
+ filter->advmap.cmap = map;
+ }
+ }
+}
+
+/*
+ * Note that if an extreme number (tens of thousands) of route-maps are in use
+ * and if bgp has an extreme number of peers, network statements, etc then this
+ * function can consume a lot of cycles. This is due to this function being
+ * called for each route-map and within this function we walk the list of peers,
+ * network statements, etc looking to see if they use this route-map.
+ */
+static void bgp_route_map_process_update(struct bgp *bgp, const char *rmap_name,
+ bool route_update)
+{
+ int i;
+ bool matched;
+ afi_t afi;
+ safi_t safi;
+ struct peer *peer;
+ struct bgp_dest *bn;
+ struct bgp_static *bgp_static;
+ struct bgp_aggregate *aggregate;
+ struct listnode *node, *nnode;
+ struct route_map *map;
+ char buf[INET6_ADDRSTRLEN];
+
+ map = route_map_lookup_by_name(rmap_name);
+
+ for (ALL_LIST_ELEMENTS(bgp->peer, node, nnode, peer)) {
+
+ /* Ignore dummy peer-group structure */
+ if (CHECK_FLAG(peer->sflags, PEER_STATUS_GROUP))
+ continue;
+
+ FOREACH_AFI_SAFI (afi, safi) {
+ /* process in/out/import/export/default-orig
+ * route-maps */
+ bgp_route_map_process_peer(rmap_name, map, peer, afi,
+ safi, route_update);
+ }
+ }
+
+ /* for outbound/default-orig route-maps, process for groups */
+ update_group_policy_update(bgp, BGP_POLICY_ROUTE_MAP, rmap_name,
+ route_update, 0);
+
+ /* update peer-group config (template) */
+ bgp_route_map_update_peer_group(rmap_name, map, bgp);
+
+ FOREACH_AFI_SAFI (afi, safi) {
+ /* For table route-map updates. */
+ if (!bgp_fibupd_safi(safi))
+ continue;
+
+ if (bgp->table_map[afi][safi].name
+ && (strcmp(rmap_name, bgp->table_map[afi][safi].name)
+ == 0)) {
+
+ /* bgp->table_map[afi][safi].map is NULL.
+ * i.e Route map creation event.
+ * So update applied_counter.
+ * If it is not NULL, i.e It may be routemap updation or
+ * deletion. so no need to update the counter.
+ */
+ if (!bgp->table_map[afi][safi].map)
+ route_map_counter_increment(map);
+ bgp->table_map[afi][safi].map = map;
+
+ if (BGP_DEBUG(zebra, ZEBRA))
+ zlog_debug(
+ "Processing route_map %s(%s:%s) update on table map",
+ rmap_name, afi2str(afi),
+ safi2str(safi));
+ if (route_update)
+ bgp_zebra_announce_table(bgp, afi, safi);
+ }
+
+ /* For network route-map updates. */
+ for (bn = bgp_table_top(bgp->route[afi][safi]); bn;
+ bn = bgp_route_next(bn)) {
+ bgp_static = bgp_dest_get_bgp_static_info(bn);
+ if (!bgp_static)
+ continue;
+
+ if (!bgp_static->rmap.name
+ || (strcmp(rmap_name, bgp_static->rmap.name) != 0))
+ continue;
+
+ if (!bgp_static->rmap.map)
+ route_map_counter_increment(map);
+
+ bgp_static->rmap.map = map;
+
+ if (route_update && !bgp_static->backdoor) {
+ const struct prefix *bn_p =
+ bgp_dest_get_prefix(bn);
+
+ if (bgp_debug_zebra(bn_p))
+ zlog_debug(
+ "Processing route_map %s(%s:%s) update on static route %s",
+ rmap_name, afi2str(afi),
+ safi2str(safi),
+ inet_ntop(bn_p->family,
+ &bn_p->u.prefix, buf,
+ INET6_ADDRSTRLEN));
+ bgp_static_update(bgp, bn_p, bgp_static, afi,
+ safi);
+ }
+ }
+
+ /* For aggregate-address route-map updates. */
+ for (bn = bgp_table_top(bgp->aggregate[afi][safi]); bn;
+ bn = bgp_route_next(bn)) {
+ aggregate = bgp_dest_get_bgp_aggregate_info(bn);
+ if (!aggregate)
+ continue;
+
+ matched = false;
+
+ /* Update suppress map pointer. */
+ if (aggregate->suppress_map_name
+ && strmatch(aggregate->suppress_map_name,
+ rmap_name)) {
+ if (aggregate->rmap.map == NULL)
+ route_map_counter_increment(map);
+
+ aggregate->suppress_map = map;
+
+ bgp_aggregate_toggle_suppressed(
+ aggregate, bgp, bgp_dest_get_prefix(bn),
+ afi, safi, false);
+
+ matched = true;
+ }
+
+ if (aggregate->rmap.name
+ && strmatch(rmap_name, aggregate->rmap.name)) {
+ if (aggregate->rmap.map == NULL)
+ route_map_counter_increment(map);
+
+ aggregate->rmap.map = map;
+
+ matched = true;
+ }
+
+ if (matched && route_update) {
+ const struct prefix *bn_p =
+ bgp_dest_get_prefix(bn);
+
+ if (bgp_debug_zebra(bn_p))
+ zlog_debug(
+ "Processing route_map %s(%s:%s) update on aggregate-address route %s",
+ rmap_name, afi2str(afi),
+ safi2str(safi),
+ inet_ntop(bn_p->family,
+ &bn_p->u.prefix, buf,
+ INET6_ADDRSTRLEN));
+ bgp_aggregate_route(bgp, bn_p, afi, safi,
+ aggregate);
+ }
+ }
+ }
+
+ /* For redistribute route-map updates. */
+ for (afi = AFI_IP; afi < AFI_MAX; afi++)
+ for (i = 0; i < ZEBRA_ROUTE_MAX; i++) {
+ struct list *red_list;
+ struct bgp_redist *red;
+
+ red_list = bgp->redist[afi][i];
+ if (!red_list)
+ continue;
+
+ for (ALL_LIST_ELEMENTS_RO(red_list, node, red)) {
+ if (!red->rmap.name
+ || (strcmp(rmap_name, red->rmap.name) != 0))
+ continue;
+
+ if (!red->rmap.map)
+ route_map_counter_increment(map);
+
+ red->rmap.map = map;
+
+ if (!route_update)
+ continue;
+
+ if (BGP_DEBUG(zebra, ZEBRA))
+ zlog_debug(
+ "Processing route_map %s(%s:%s) update on redistributed routes",
+ rmap_name, afi2str(afi),
+ safi2str(safi));
+
+ bgp_redistribute_resend(bgp, afi, i,
+ red->instance);
+ }
+ }
+
+ /* for type5 command route-maps */
+ FOREACH_AFI_SAFI (afi, safi) {
+ if (!bgp->adv_cmd_rmap[afi][safi].name
+ || strcmp(rmap_name, bgp->adv_cmd_rmap[afi][safi].name)
+ != 0)
+ continue;
+
+ /* Make sure the route-map is populated here if not already done */
+ bgp->adv_cmd_rmap[afi][safi].map = map;
+
+ if (BGP_DEBUG(zebra, ZEBRA))
+ zlog_debug(
+ "Processing route_map %s(%s:%s) update on advertise type5 route command",
+ rmap_name, afi2str(afi), safi2str(safi));
+
+ if (route_update && advertise_type5_routes(bgp, afi)) {
+ bgp_evpn_withdraw_type5_routes(bgp, afi, safi);
+ bgp_evpn_advertise_type5_routes(bgp, afi, safi);
+ }
+ }
+}
+
+static void bgp_route_map_process_update_cb(char *rmap_name)
+{
+ struct listnode *node, *nnode;
+ struct bgp *bgp;
+
+ for (ALL_LIST_ELEMENTS(bm->bgp, node, nnode, bgp)) {
+ bgp_route_map_process_update(bgp, rmap_name, true);
+
+#ifdef ENABLE_BGP_VNC
+ vnc_routemap_update(bgp, __func__);
+#endif
+ }
+
+ vpn_policy_routemap_event(rmap_name);
+}
+
+void bgp_route_map_update_timer(struct thread *thread)
+{
+ route_map_walk_update_list(bgp_route_map_process_update_cb);
+}
+
+static void bgp_route_map_mark_update(const char *rmap_name)
+{
+ struct listnode *node, *nnode;
+ struct bgp *bgp;
+
+ /* If new update is received before the current timer timed out,
+ * turn it off and start a new timer.
+ */
+ THREAD_OFF(bm->t_rmap_update);
+
+ /* rmap_update_timer of 0 means don't do route updates */
+ if (bm->rmap_update_timer) {
+ thread_add_timer(bm->master, bgp_route_map_update_timer,
+ NULL, bm->rmap_update_timer,
+ &bm->t_rmap_update);
+
+ /* Signal the groups that a route-map update event has
+ * started */
+ for (ALL_LIST_ELEMENTS(bm->bgp, node, nnode, bgp))
+ update_group_policy_update(bgp, BGP_POLICY_ROUTE_MAP,
+ rmap_name, true, 1);
+ } else {
+ for (ALL_LIST_ELEMENTS(bm->bgp, node, nnode, bgp)) {
+ bgp_route_map_process_update(bgp, rmap_name, false);
+#ifdef ENABLE_BGP_VNC
+ vnc_routemap_update(bgp, __func__);
+#endif
+ }
+
+ vpn_policy_routemap_event(rmap_name);
+ }
+}
+
+static void bgp_route_map_add(const char *rmap_name)
+{
+ if (route_map_mark_updated(rmap_name) == 0)
+ bgp_route_map_mark_update(rmap_name);
+
+ route_map_notify_dependencies(rmap_name, RMAP_EVENT_MATCH_ADDED);
+}
+
+static void bgp_route_map_delete(const char *rmap_name)
+{
+ if (route_map_mark_updated(rmap_name) == 0)
+ bgp_route_map_mark_update(rmap_name);
+
+ route_map_notify_dependencies(rmap_name, RMAP_EVENT_MATCH_DELETED);
+}
+
+static void bgp_route_map_event(const char *rmap_name)
+{
+ if (route_map_mark_updated(rmap_name) == 0)
+ bgp_route_map_mark_update(rmap_name);
+
+ route_map_notify_dependencies(rmap_name, RMAP_EVENT_MATCH_ADDED);
+}
+
+DEFUN_YANG (match_mac_address,
+ match_mac_address_cmd,
+ "match mac address ACCESSLIST_MAC_NAME",
+ MATCH_STR
+ "mac address\n"
+ "Match address of route\n"
+ "MAC Access-list name\n")
+{
+ const char *xpath =
+ "./match-condition[condition='frr-bgp-route-map:mac-address-list']";
+ char xpath_value[XPATH_MAXLEN];
+
+ nb_cli_enqueue_change(vty, xpath, NB_OP_CREATE, NULL);
+ snprintf(xpath_value, sizeof(xpath_value),
+ "%s/rmap-match-condition/frr-bgp-route-map:list-name", xpath);
+ nb_cli_enqueue_change(vty, xpath_value, NB_OP_MODIFY, argv[3]->arg);
+
+ return nb_cli_apply_changes(vty, NULL);
+}
+
+DEFUN_YANG (no_match_mac_address,
+ no_match_mac_address_cmd,
+ "no match mac address ACCESSLIST_MAC_NAME",
+ NO_STR
+ MATCH_STR
+ "mac\n"
+ "Match address of route\n"
+ "MAC acess-list name\n")
+{
+ const char *xpath =
+ "./match-condition[condition='frr-bgp-route-map:mac-address-list']";
+
+ nb_cli_enqueue_change(vty, xpath, NB_OP_DESTROY, NULL);
+ return nb_cli_apply_changes(vty, NULL);
+}
+
+/*
+ * Helper to handle the case of the user passing in a number or type string
+ */
+static const char *parse_evpn_rt_type(const char *num_rt_type)
+{
+ switch (num_rt_type[0]) {
+ case '1':
+ return "ead";
+ case '2':
+ return "macip";
+ case '3':
+ return "multicast";
+ case '4':
+ return "es";
+ case '5':
+ return "prefix";
+ default:
+ break;
+ }
+
+ /* Was already full type string */
+ return num_rt_type;
+}
+
+DEFUN_YANG (match_evpn_route_type,
+ match_evpn_route_type_cmd,
+ "match evpn route-type <ead|1|macip|2|multicast|3|es|4|prefix|5>",
+ MATCH_STR
+ EVPN_HELP_STR
+ EVPN_TYPE_HELP_STR
+ EVPN_TYPE_1_HELP_STR
+ EVPN_TYPE_1_HELP_STR
+ EVPN_TYPE_2_HELP_STR
+ EVPN_TYPE_2_HELP_STR
+ EVPN_TYPE_3_HELP_STR
+ EVPN_TYPE_3_HELP_STR
+ EVPN_TYPE_4_HELP_STR
+ EVPN_TYPE_4_HELP_STR
+ EVPN_TYPE_5_HELP_STR
+ EVPN_TYPE_5_HELP_STR)
+{
+ const char *xpath =
+ "./match-condition[condition='frr-bgp-route-map:evpn-route-type']";
+ char xpath_value[XPATH_MAXLEN];
+
+ nb_cli_enqueue_change(vty, xpath, NB_OP_CREATE, NULL);
+ snprintf(xpath_value, sizeof(xpath_value),
+ "%s/rmap-match-condition/frr-bgp-route-map:evpn-route-type",
+ xpath);
+ nb_cli_enqueue_change(vty, xpath_value, NB_OP_MODIFY,
+ parse_evpn_rt_type(argv[3]->arg));
+
+ return nb_cli_apply_changes(vty, NULL);
+}
+
+DEFUN_YANG (no_match_evpn_route_type,
+ no_match_evpn_route_type_cmd,
+ "no match evpn route-type <ead|1|macip|2|multicast|3|es|4|prefix|5>",
+ NO_STR
+ MATCH_STR
+ EVPN_HELP_STR
+ EVPN_TYPE_HELP_STR
+ EVPN_TYPE_1_HELP_STR
+ EVPN_TYPE_1_HELP_STR
+ EVPN_TYPE_2_HELP_STR
+ EVPN_TYPE_2_HELP_STR
+ EVPN_TYPE_3_HELP_STR
+ EVPN_TYPE_3_HELP_STR
+ EVPN_TYPE_4_HELP_STR
+ EVPN_TYPE_4_HELP_STR
+ EVPN_TYPE_5_HELP_STR
+ EVPN_TYPE_5_HELP_STR)
+{
+ const char *xpath =
+ "./match-condition[condition='frr-bgp-route-map:evpn-route-type']";
+
+ nb_cli_enqueue_change(vty, xpath, NB_OP_DESTROY, NULL);
+
+ return nb_cli_apply_changes(vty, NULL);
+}
+
+
+DEFUN_YANG (match_evpn_vni,
+ match_evpn_vni_cmd,
+ "match evpn vni " CMD_VNI_RANGE,
+ MATCH_STR
+ EVPN_HELP_STR
+ "Match VNI\n"
+ "VNI ID\n")
+{
+ const char *xpath =
+ "./match-condition[condition='frr-bgp-route-map:evpn-vni']";
+ char xpath_value[XPATH_MAXLEN];
+
+ nb_cli_enqueue_change(vty, xpath, NB_OP_CREATE, NULL);
+ snprintf(xpath_value, sizeof(xpath_value),
+ "%s/rmap-match-condition/frr-bgp-route-map:evpn-vni", xpath);
+ nb_cli_enqueue_change(vty, xpath_value, NB_OP_MODIFY, argv[3]->arg);
+
+ return nb_cli_apply_changes(vty, NULL);
+}
+
+DEFUN_YANG (no_match_evpn_vni,
+ no_match_evpn_vni_cmd,
+ "no match evpn vni " CMD_VNI_RANGE,
+ NO_STR
+ MATCH_STR
+ EVPN_HELP_STR
+ "Match VNI\n"
+ "VNI ID\n")
+{
+ const char *xpath =
+ "./match-condition[condition='frr-bgp-route-map:evpn-vni']";
+ char xpath_value[XPATH_MAXLEN];
+
+ nb_cli_enqueue_change(vty, xpath, NB_OP_DESTROY, NULL);
+ snprintf(xpath_value, sizeof(xpath_value),
+ "%s/rmap-match-condition/frr-bgp-route-map:evpn-vni", xpath);
+ nb_cli_enqueue_change(vty, xpath_value, NB_OP_DESTROY, argv[3]->arg);
+
+ return nb_cli_apply_changes(vty, NULL);
+}
+
+DEFUN_YANG (match_evpn_default_route,
+ match_evpn_default_route_cmd,
+ "match evpn default-route",
+ MATCH_STR
+ EVPN_HELP_STR
+ "default EVPN type-5 route\n")
+{
+ const char *xpath =
+ "./match-condition[condition='frr-bgp-route-map:evpn-default-route']";
+ char xpath_value[XPATH_MAXLEN];
+
+ nb_cli_enqueue_change(vty, xpath, NB_OP_CREATE, NULL);
+
+ snprintf(xpath_value, sizeof(xpath_value),
+ "%s/rmap-match-condition/frr-bgp-route-map:evpn-default-route",
+ xpath);
+ nb_cli_enqueue_change(vty, xpath_value, NB_OP_MODIFY, NULL);
+
+ return nb_cli_apply_changes(vty, NULL);
+}
+
+DEFUN_YANG (no_match_evpn_default_route,
+ no_match_evpn_default_route_cmd,
+ "no match evpn default-route",
+ NO_STR
+ MATCH_STR
+ EVPN_HELP_STR
+ "default EVPN type-5 route\n")
+{
+ const char *xpath =
+ "./match-condition[condition='frr-bgp-route-map:evpn-default-route']";
+
+ nb_cli_enqueue_change(vty, xpath, NB_OP_DESTROY, NULL);
+
+ return nb_cli_apply_changes(vty, NULL);
+}
+
+DEFUN_YANG (match_evpn_rd,
+ match_evpn_rd_cmd,
+ "match evpn rd ASN:NN_OR_IP-ADDRESS:NN",
+ MATCH_STR
+ EVPN_HELP_STR
+ "Route Distinguisher\n"
+ "ASN:XX or A.B.C.D:XX\n")
+{
+ const char *xpath =
+ "./match-condition[condition='frr-bgp-route-map:evpn-rd']";
+ char xpath_value[XPATH_MAXLEN];
+
+ nb_cli_enqueue_change(vty, xpath, NB_OP_CREATE, NULL);
+ snprintf(
+ xpath_value, sizeof(xpath_value),
+ "%s/rmap-match-condition/frr-bgp-route-map:route-distinguisher",
+ xpath);
+ nb_cli_enqueue_change(vty, xpath_value, NB_OP_MODIFY, argv[3]->arg);
+
+ return nb_cli_apply_changes(vty, NULL);
+}
+
+DEFUN_YANG (no_match_evpn_rd,
+ no_match_evpn_rd_cmd,
+ "no match evpn rd ASN:NN_OR_IP-ADDRESS:NN",
+ NO_STR
+ MATCH_STR
+ EVPN_HELP_STR
+ "Route Distinguisher\n"
+ "ASN:XX or A.B.C.D:XX\n")
+{
+ const char *xpath =
+ "./match-condition[condition='frr-bgp-route-map:evpn-rd']";
+
+ nb_cli_enqueue_change(vty, xpath, NB_OP_DESTROY, NULL);
+ return nb_cli_apply_changes(vty, NULL);
+}
+
+DEFUN_YANG (set_evpn_gw_ip_ipv4,
+ set_evpn_gw_ip_ipv4_cmd,
+ "set evpn gateway-ip ipv4 A.B.C.D",
+ SET_STR
+ EVPN_HELP_STR
+ "Set gateway IP for prefix advertisement route\n"
+ "IPv4 address\n"
+ "Gateway IP address in IPv4 format\n")
+{
+ int ret;
+ union sockunion su;
+ const char *xpath =
+ "./set-action[action='frr-bgp-route-map:set-evpn-gateway-ip-ipv4']";
+ char xpath_value[XPATH_MAXLEN];
+
+ ret = str2sockunion(argv[4]->arg, &su);
+ if (ret < 0) {
+ vty_out(vty, "%% Malformed gateway IP\n");
+ return CMD_WARNING_CONFIG_FAILED;
+ }
+
+ if (su.sin.sin_addr.s_addr == 0 ||
+ !ipv4_unicast_valid(&su.sin.sin_addr)) {
+ vty_out(vty,
+ "%% Gateway IP cannot be 0.0.0.0, multicast or reserved\n");
+ return CMD_WARNING_CONFIG_FAILED;
+ }
+
+ nb_cli_enqueue_change(vty, xpath, NB_OP_CREATE, NULL);
+
+ snprintf(xpath_value, sizeof(xpath_value),
+ "%s/rmap-set-action/frr-bgp-route-map:evpn-gateway-ip-ipv4",
+ xpath);
+
+ nb_cli_enqueue_change(vty, xpath_value, NB_OP_MODIFY, argv[4]->arg);
+ return nb_cli_apply_changes(vty, NULL);
+}
+
+DEFUN_YANG (no_set_evpn_gw_ip_ipv4,
+ no_set_evpn_gw_ip_ipv4_cmd,
+ "no set evpn gateway-ip ipv4 A.B.C.D",
+ NO_STR
+ SET_STR
+ EVPN_HELP_STR
+ "Set gateway IP for prefix advertisement route\n"
+ "IPv4 address\n"
+ "Gateway IP address in IPv4 format\n")
+{
+ int ret;
+ union sockunion su;
+ const char *xpath =
+ "./set-action[action='frr-bgp-route-map:set-evpn-gateway-ip-ipv4']";
+
+ ret = str2sockunion(argv[5]->arg, &su);
+ if (ret < 0) {
+ vty_out(vty, "%% Malformed gateway IP\n");
+ return CMD_WARNING_CONFIG_FAILED;
+ }
+
+ if (su.sin.sin_addr.s_addr == 0 ||
+ !ipv4_unicast_valid(&su.sin.sin_addr)) {
+ vty_out(vty,
+ "%% Gateway IP cannot be 0.0.0.0, multicast or reserved\n");
+ return CMD_WARNING_CONFIG_FAILED;
+ }
+
+ nb_cli_enqueue_change(vty, xpath, NB_OP_DESTROY, NULL);
+
+ return nb_cli_apply_changes(vty, NULL);
+}
+
+DEFUN_YANG (set_evpn_gw_ip_ipv6,
+ set_evpn_gw_ip_ipv6_cmd,
+ "set evpn gateway-ip ipv6 X:X::X:X",
+ SET_STR
+ EVPN_HELP_STR
+ "Set gateway IP for prefix advertisement route\n"
+ "IPv6 address\n"
+ "Gateway IP address in IPv6 format\n")
+{
+ int ret;
+ union sockunion su;
+ const char *xpath =
+ "./set-action[action='frr-bgp-route-map:set-evpn-gateway-ip-ipv6']";
+ char xpath_value[XPATH_MAXLEN];
+
+ ret = str2sockunion(argv[4]->arg, &su);
+ if (ret < 0) {
+ vty_out(vty, "%% Malformed gateway IP\n");
+ return CMD_WARNING_CONFIG_FAILED;
+ }
+
+ if (IN6_IS_ADDR_LINKLOCAL(&su.sin6.sin6_addr)
+ || IN6_IS_ADDR_MULTICAST(&su.sin6.sin6_addr)) {
+ vty_out(vty,
+ "%% Gateway IP cannot be a linklocal or multicast address\n");
+ return CMD_WARNING_CONFIG_FAILED;
+ }
+
+ nb_cli_enqueue_change(vty, xpath, NB_OP_CREATE, NULL);
+
+ snprintf(xpath_value, sizeof(xpath_value),
+ "%s/rmap-set-action/frr-bgp-route-map:evpn-gateway-ip-ipv6",
+ xpath);
+
+ nb_cli_enqueue_change(vty, xpath_value, NB_OP_MODIFY, argv[4]->arg);
+ return nb_cli_apply_changes(vty, NULL);
+}
+
+DEFUN_YANG (no_set_evpn_gw_ip_ipv6,
+ no_set_evpn_gw_ip_ipv6_cmd,
+ "no set evpn gateway-ip ipv6 X:X::X:X",
+ NO_STR
+ SET_STR
+ EVPN_HELP_STR
+ "Set gateway IP for prefix advertisement route\n"
+ "IPv4 address\n"
+ "Gateway IP address in IPv4 format\n")
+{
+ int ret;
+ union sockunion su;
+ const char *xpath =
+ "./set-action[action='frr-bgp-route-map:set-evpn-gateway-ip-ipv6']";
+
+ ret = str2sockunion(argv[5]->arg, &su);
+ if (ret < 0) {
+ vty_out(vty, "%% Malformed gateway IP\n");
+ return CMD_WARNING_CONFIG_FAILED;
+ }
+
+ if (IN6_IS_ADDR_LINKLOCAL(&su.sin6.sin6_addr)
+ || IN6_IS_ADDR_MULTICAST(&su.sin6.sin6_addr)) {
+ vty_out(vty,
+ "%% Gateway IP cannot be a linklocal or multicast address\n");
+ return CMD_WARNING_CONFIG_FAILED;
+ }
+
+ nb_cli_enqueue_change(vty, xpath, NB_OP_DESTROY, NULL);
+
+ return nb_cli_apply_changes(vty, NULL);
+}
+
+DEFPY_YANG(match_vrl_source_vrf,
+ match_vrl_source_vrf_cmd,
+ "match source-vrf NAME$vrf_name",
+ MATCH_STR
+ "source vrf\n"
+ "The VRF name\n")
+{
+ const char *xpath =
+ "./match-condition[condition='frr-bgp-route-map:source-vrf']";
+ char xpath_value[XPATH_MAXLEN];
+
+ nb_cli_enqueue_change(vty, xpath, NB_OP_CREATE, NULL);
+ snprintf(xpath_value, sizeof(xpath_value),
+ "%s/rmap-match-condition/frr-bgp-route-map:source-vrf", xpath);
+ nb_cli_enqueue_change(vty, xpath_value, NB_OP_MODIFY, vrf_name);
+
+ return nb_cli_apply_changes(vty, NULL);
+}
+
+DEFPY_YANG(no_match_vrl_source_vrf,
+ no_match_vrl_source_vrf_cmd,
+ "no match source-vrf NAME$vrf_name",
+ NO_STR MATCH_STR
+ "source vrf\n"
+ "The VRF name\n")
+{
+ const char *xpath =
+ "./match-condition[condition='frr-bgp-route-map:source-vrf']";
+
+ nb_cli_enqueue_change(vty, xpath, NB_OP_DESTROY, NULL);
+ return nb_cli_apply_changes(vty, NULL);
+}
+
+DEFPY_YANG (match_peer,
+ match_peer_cmd,
+ "match peer <A.B.C.D$addrv4|X:X::X:X$addrv6|WORD$intf>",
+ MATCH_STR
+ "Match peer address\n"
+ "IP address of peer\n"
+ "IPv6 address of peer\n"
+ "Interface name of peer or peer group name\n")
+{
+ const char *xpath =
+ "./match-condition[condition='frr-bgp-route-map:peer']";
+ char xpath_value[XPATH_MAXLEN];
+
+ nb_cli_enqueue_change(vty, xpath, NB_OP_CREATE, NULL);
+
+ if (addrv4_str) {
+ snprintf(
+ xpath_value, sizeof(xpath_value),
+ "%s/rmap-match-condition/frr-bgp-route-map:peer-ipv4-address",
+ xpath);
+ nb_cli_enqueue_change(vty, xpath_value, NB_OP_MODIFY,
+ addrv4_str);
+ } else if (addrv6_str) {
+ snprintf(
+ xpath_value, sizeof(xpath_value),
+ "%s/rmap-match-condition/frr-bgp-route-map:peer-ipv6-address",
+ xpath);
+ nb_cli_enqueue_change(vty, xpath_value, NB_OP_MODIFY,
+ addrv6_str);
+ } else {
+ snprintf(
+ xpath_value, sizeof(xpath_value),
+ "%s/rmap-match-condition/frr-bgp-route-map:peer-interface",
+ xpath);
+ nb_cli_enqueue_change(vty, xpath_value, NB_OP_MODIFY, intf);
+ }
+
+ return nb_cli_apply_changes(vty, NULL);
+}
+
+DEFUN_YANG (match_peer_local,
+ match_peer_local_cmd,
+ "match peer local",
+ MATCH_STR
+ "Match peer address\n"
+ "Static or Redistributed routes\n")
+{
+ const char *xpath =
+ "./match-condition[condition='frr-bgp-route-map:peer']";
+ char xpath_value[XPATH_MAXLEN];
+
+ nb_cli_enqueue_change(vty, xpath, NB_OP_CREATE, NULL);
+
+ snprintf(xpath_value, sizeof(xpath_value),
+ "%s/rmap-match-condition/frr-bgp-route-map:peer-local", xpath);
+ nb_cli_enqueue_change(vty, xpath_value, NB_OP_MODIFY, "true");
+
+ return nb_cli_apply_changes(vty, NULL);
+}
+
+DEFUN_YANG (no_match_peer,
+ no_match_peer_cmd,
+ "no match peer [<local|A.B.C.D|X:X::X:X|WORD>]",
+ NO_STR
+ MATCH_STR
+ "Match peer address\n"
+ "Static or Redistributed routes\n"
+ "IP address of peer\n"
+ "IPv6 address of peer\n"
+ "Interface name of peer\n")
+{
+ const char *xpath =
+ "./match-condition[condition='frr-bgp-route-map:peer']";
+
+ nb_cli_enqueue_change(vty, xpath, NB_OP_DESTROY, NULL);
+
+ return nb_cli_apply_changes(vty, NULL);
+}
+
+#ifdef HAVE_SCRIPTING
+DEFUN_YANG (match_script,
+ match_script_cmd,
+ "[no] match script WORD",
+ NO_STR
+ MATCH_STR
+ "Execute script to determine match\n"
+ "The script name to run, without .lua; e.g. 'myroutemap' to run myroutemap.lua\n")
+{
+ bool no = strmatch(argv[0]->text, "no");
+ int i = 0;
+ argv_find(argv, argc, "WORD", &i);
+ const char *script = argv[i]->arg;
+ const char *xpath =
+ "./match-condition[condition='frr-bgp-route-map:match-script']";
+ char xpath_value[XPATH_MAXLEN];
+
+ if (no) {
+ nb_cli_enqueue_change(vty, xpath, NB_OP_DESTROY, NULL);
+ snprintf(xpath_value, sizeof(xpath_value),
+ "%s/rmap-match-condition/frr-bgp-route-map:script",
+ xpath);
+ nb_cli_enqueue_change(vty, xpath_value, NB_OP_DESTROY,
+ script);
+
+ return nb_cli_apply_changes(vty, NULL);
+ }
+
+ nb_cli_enqueue_change(vty, xpath, NB_OP_CREATE, NULL);
+ snprintf(xpath_value, sizeof(xpath_value),
+ "%s/rmap-match-condition/frr-bgp-route-map:script",
+ xpath);
+ nb_cli_enqueue_change(vty, xpath_value, NB_OP_MODIFY,
+ script);
+
+ return nb_cli_apply_changes(vty, NULL);
+}
+#endif /* HAVE_SCRIPTING */
+
+/* match probability */
+DEFUN_YANG (match_probability,
+ match_probability_cmd,
+ "match probability (0-100)",
+ MATCH_STR
+ "Match portion of routes defined by percentage value\n"
+ "Percentage of routes\n")
+{
+ int idx_number = 2;
+
+ const char *xpath =
+ "./match-condition[condition='frr-bgp-route-map:probability']";
+ char xpath_value[XPATH_MAXLEN];
+
+ nb_cli_enqueue_change(vty, xpath, NB_OP_CREATE, NULL);
+ snprintf(xpath_value, sizeof(xpath_value),
+ "%s/rmap-match-condition/frr-bgp-route-map:probability",
+ xpath);
+ nb_cli_enqueue_change(vty, xpath_value, NB_OP_MODIFY,
+ argv[idx_number]->arg);
+
+ return nb_cli_apply_changes(vty, NULL);
+}
+
+
+DEFUN_YANG (no_match_probability,
+ no_match_probability_cmd,
+ "no match probability [(1-99)]",
+ NO_STR
+ MATCH_STR
+ "Match portion of routes defined by percentage value\n"
+ "Percentage of routes\n")
+{
+ int idx_number = 3;
+ const char *xpath =
+ "./match-condition[condition='frr-bgp-route-map:probability']";
+ char xpath_value[XPATH_MAXLEN];
+
+ nb_cli_enqueue_change(vty, xpath, NB_OP_DESTROY, NULL);
+
+ if (argc <= idx_number)
+ return nb_cli_apply_changes(vty, NULL);
+
+ snprintf(xpath_value, sizeof(xpath_value),
+ "%s/rmap-match-condition/frr-bgp-route-map:probability",
+ xpath);
+ nb_cli_enqueue_change(vty, xpath_value, NB_OP_DESTROY,
+ argv[idx_number]->arg);
+
+ return nb_cli_apply_changes(vty, NULL);
+}
+
+
+DEFPY_YANG (match_ip_route_source,
+ match_ip_route_source_cmd,
+ "match ip route-source ACCESSLIST4_NAME",
+ MATCH_STR
+ IP_STR
+ "Match advertising source address of route\n"
+ "IP Access-list name\n")
+{
+ const char *xpath =
+ "./match-condition[condition='frr-bgp-route-map:ip-route-source']";
+ char xpath_value[XPATH_MAXLEN + 32];
+ int idx_acl = 3;
+
+ nb_cli_enqueue_change(vty, xpath, NB_OP_CREATE, NULL);
+ snprintf(xpath_value, sizeof(xpath_value),
+ "%s/rmap-match-condition/frr-bgp-route-map:list-name",
+ xpath);
+ nb_cli_enqueue_change(vty, xpath_value, NB_OP_MODIFY,
+ argv[idx_acl]->arg);
+
+ return nb_cli_apply_changes(vty, NULL);
+}
+
+
+DEFUN_YANG (no_match_ip_route_source,
+ no_match_ip_route_source_cmd,
+ "no match ip route-source [ACCESSLIST4_NAME]",
+ NO_STR
+ MATCH_STR
+ IP_STR
+ "Match advertising source address of route\n"
+ "IP Access-list name\n")
+{
+ const char *xpath =
+ "./match-condition[condition='frr-bgp-route-map:ip-route-source']";
+
+ nb_cli_enqueue_change(vty, xpath, NB_OP_DESTROY, NULL);
+ return nb_cli_apply_changes(vty, NULL);
+}
+
+DEFUN_YANG (match_ip_route_source_prefix_list,
+ match_ip_route_source_prefix_list_cmd,
+ "match ip route-source prefix-list PREFIXLIST_NAME",
+ MATCH_STR
+ IP_STR
+ "Match advertising source address of route\n"
+ "Match entries of prefix-lists\n"
+ "IP prefix-list name\n")
+{
+ int idx_word = 4;
+ const char *xpath =
+ "./match-condition[condition='frr-bgp-route-map:ip-route-source-prefix-list']";
+ char xpath_value[XPATH_MAXLEN + 32];
+
+ nb_cli_enqueue_change(vty, xpath, NB_OP_CREATE, NULL);
+ snprintf(xpath_value, sizeof(xpath_value),
+ "%s/rmap-match-condition/frr-bgp-route-map:list-name", xpath);
+ nb_cli_enqueue_change(vty, xpath_value, NB_OP_MODIFY,
+ argv[idx_word]->arg);
+
+ return nb_cli_apply_changes(vty, NULL);
+}
+
+
+DEFUN_YANG (no_match_ip_route_source_prefix_list,
+ no_match_ip_route_source_prefix_list_cmd,
+ "no match ip route-source prefix-list [PREFIXLIST_NAME]",
+ NO_STR
+ MATCH_STR
+ IP_STR
+ "Match advertising source address of route\n"
+ "Match entries of prefix-lists\n"
+ "IP prefix-list name\n")
+{
+ const char *xpath =
+ "./match-condition[condition='frr-bgp-route-map:ip-route-source-prefix-list']";
+
+ nb_cli_enqueue_change(vty, xpath, NB_OP_DESTROY, NULL);
+ return nb_cli_apply_changes(vty, NULL);
+}
+
+DEFUN_YANG (match_local_pref,
+ match_local_pref_cmd,
+ "match local-preference (0-4294967295)",
+ MATCH_STR
+ "Match local-preference of route\n"
+ "Metric value\n")
+{
+ int idx_number = 2;
+
+ const char *xpath =
+ "./match-condition[condition='frr-bgp-route-map:match-local-preference']";
+ char xpath_value[XPATH_MAXLEN];
+
+ nb_cli_enqueue_change(vty, xpath, NB_OP_CREATE, NULL);
+ snprintf(xpath_value, sizeof(xpath_value),
+ "%s/rmap-match-condition/frr-bgp-route-map:local-preference",
+ xpath);
+ nb_cli_enqueue_change(vty, xpath_value, NB_OP_MODIFY,
+ argv[idx_number]->arg);
+
+ return nb_cli_apply_changes(vty, NULL);
+}
+
+
+DEFUN_YANG (no_match_local_pref,
+ no_match_local_pref_cmd,
+ "no match local-preference [(0-4294967295)]",
+ NO_STR
+ MATCH_STR
+ "Match local preference of route\n"
+ "Local preference value\n")
+{
+ int idx_localpref = 3;
+ const char *xpath =
+ "./match-condition[condition='frr-bgp-route-map:match-local-preference']";
+ char xpath_value[XPATH_MAXLEN];
+
+ nb_cli_enqueue_change(vty, xpath, NB_OP_DESTROY, NULL);
+
+ if (argc <= idx_localpref)
+ return nb_cli_apply_changes(vty, NULL);
+
+ snprintf(xpath_value, sizeof(xpath_value),
+ "%s/rmap-match-condition/frr-bgp-route-map:local-preference",
+ xpath);
+ nb_cli_enqueue_change(vty, xpath_value, NB_OP_DESTROY,
+ argv[idx_localpref]->arg);
+
+ return nb_cli_apply_changes(vty, NULL);
+}
+
+DEFUN_YANG(match_alias, match_alias_cmd, "match alias ALIAS_NAME",
+ MATCH_STR
+ "Match BGP community alias name\n"
+ "BGP community alias name\n")
+{
+ const char *alias = argv[2]->arg;
+ struct community_alias ca1;
+ struct community_alias *lookup_alias;
+
+ const char *xpath =
+ "./match-condition[condition='frr-bgp-route-map:match-alias']";
+ char xpath_value[XPATH_MAXLEN];
+
+ memset(&ca1, 0, sizeof(ca1));
+ strlcpy(ca1.alias, alias, sizeof(ca1.alias));
+ lookup_alias = bgp_ca_alias_lookup(&ca1);
+ if (!lookup_alias) {
+ vty_out(vty, "%% BGP alias name '%s' does not exist\n", alias);
+ return CMD_WARNING_CONFIG_FAILED;
+ }
+
+ nb_cli_enqueue_change(vty, xpath, NB_OP_CREATE, NULL);
+ snprintf(xpath_value, sizeof(xpath_value),
+ "%s/rmap-match-condition/frr-bgp-route-map:alias", xpath);
+ nb_cli_enqueue_change(vty, xpath_value, NB_OP_MODIFY, alias);
+
+ return nb_cli_apply_changes(vty, NULL);
+}
+
+
+DEFUN_YANG(no_match_alias, no_match_alias_cmd, "no match alias [ALIAS_NAME]",
+ NO_STR MATCH_STR
+ "Match BGP community alias name\n"
+ "BGP community alias name\n")
+{
+ int idx_alias = 3;
+ const char *xpath =
+ "./match-condition[condition='frr-bgp-route-map:match-alias']";
+ char xpath_value[XPATH_MAXLEN];
+
+ nb_cli_enqueue_change(vty, xpath, NB_OP_DESTROY, NULL);
+
+ if (argc <= idx_alias)
+ return nb_cli_apply_changes(vty, NULL);
+
+ snprintf(xpath_value, sizeof(xpath_value),
+ "%s/rmap-match-condition/frr-bgp-route-map:alias", xpath);
+ nb_cli_enqueue_change(vty, xpath_value, NB_OP_DESTROY,
+ argv[idx_alias]->arg);
+
+ return nb_cli_apply_changes(vty, NULL);
+}
+
+DEFPY_YANG (match_community,
+ match_community_cmd,
+ "match community <(1-99)|(100-500)|COMMUNITY_LIST_NAME> [exact-match]",
+ MATCH_STR
+ "Match BGP community list\n"
+ "Community-list number (standard)\n"
+ "Community-list number (expanded)\n"
+ "Community-list name\n"
+ "Do exact matching of communities\n")
+{
+ const char *xpath =
+ "./match-condition[condition='frr-bgp-route-map:match-community']";
+ char xpath_value[XPATH_MAXLEN];
+ char xpath_match[XPATH_MAXLEN];
+ int idx_comm_list = 2;
+
+ nb_cli_enqueue_change(vty, xpath, NB_OP_CREATE, NULL);
+
+ snprintf(
+ xpath_value, sizeof(xpath_value),
+ "%s/rmap-match-condition/frr-bgp-route-map:comm-list/comm-list-name",
+ xpath);
+ nb_cli_enqueue_change(vty, xpath_value, NB_OP_MODIFY, argv[idx_comm_list]->arg);
+
+ if (argc == 4) {
+ snprintf(
+ xpath_match, sizeof(xpath_match),
+ "%s/rmap-match-condition/frr-bgp-route-map:comm-list/comm-list-name-exact-match",
+ xpath);
+ nb_cli_enqueue_change(vty, xpath_match, NB_OP_MODIFY,
+ "true");
+ } else {
+ snprintf(
+ xpath_match, sizeof(xpath_match),
+ "%s/rmap-match-condition/frr-bgp-route-map:comm-list/comm-list-name-exact-match",
+ xpath);
+ nb_cli_enqueue_change(vty, xpath_match, NB_OP_MODIFY,
+ "false");
+ }
+
+ return nb_cli_apply_changes(vty, NULL);
+}
+
+DEFUN_YANG (no_match_community,
+ no_match_community_cmd,
+ "no match community [<(1-99)|(100-500)|COMMUNITY_LIST_NAME> [exact-match]]",
+ NO_STR
+ MATCH_STR
+ "Match BGP community list\n"
+ "Community-list number (standard)\n"
+ "Community-list number (expanded)\n"
+ "Community-list name\n"
+ "Do exact matching of communities\n")
+{
+ const char *xpath =
+ "./match-condition[condition='frr-bgp-route-map:match-community']";
+
+ nb_cli_enqueue_change(vty, xpath, NB_OP_DESTROY, NULL);
+ return nb_cli_apply_changes(vty, NULL);
+}
+
+DEFPY_YANG (match_lcommunity,
+ match_lcommunity_cmd,
+ "match large-community <(1-99)|(100-500)|LCOMMUNITY_LIST_NAME> [exact-match]",
+ MATCH_STR
+ "Match BGP large community list\n"
+ "Large Community-list number (standard)\n"
+ "Large Community-list number (expanded)\n"
+ "Large Community-list name\n"
+ "Do exact matching of communities\n")
+{
+ const char *xpath =
+ "./match-condition[condition='frr-bgp-route-map:match-large-community']";
+ char xpath_value[XPATH_MAXLEN];
+ char xpath_match[XPATH_MAXLEN];
+ int idx_lcomm_list = 2;
+
+ nb_cli_enqueue_change(vty, xpath, NB_OP_CREATE, NULL);
+
+ snprintf(
+ xpath_value, sizeof(xpath_value),
+ "%s/rmap-match-condition/frr-bgp-route-map:comm-list/comm-list-name",
+ xpath);
+ nb_cli_enqueue_change(vty, xpath_value, NB_OP_MODIFY, argv[idx_lcomm_list]->arg);
+
+ if (argc == 4) {
+ snprintf(
+ xpath_match, sizeof(xpath_match),
+ "%s/rmap-match-condition/frr-bgp-route-map:comm-list/comm-list-name-exact-match",
+ xpath);
+ nb_cli_enqueue_change(vty, xpath_match, NB_OP_MODIFY,
+ "true");
+ } else {
+ snprintf(
+ xpath_match, sizeof(xpath_match),
+ "%s/rmap-match-condition/frr-bgp-route-map:comm-list/comm-list-name-exact-match",
+ xpath);
+ nb_cli_enqueue_change(vty, xpath_match, NB_OP_MODIFY,
+ "false");
+ }
+
+ return nb_cli_apply_changes(vty, NULL);
+}
+
+DEFUN_YANG (no_match_lcommunity,
+ no_match_lcommunity_cmd,
+ "no match large-community [<(1-99)|(100-500)|LCOMMUNITY_LIST_NAME> [exact-match]]",
+ NO_STR
+ MATCH_STR
+ "Match BGP large community list\n"
+ "Large Community-list number (standard)\n"
+ "Large Community-list number (expanded)\n"
+ "Large Community-list name\n"
+ "Do exact matching of communities\n")
+{
+ const char *xpath =
+ "./match-condition[condition='frr-bgp-route-map:match-large-community']";
+
+ nb_cli_enqueue_change(vty, xpath, NB_OP_DESTROY, NULL);
+ return nb_cli_apply_changes(vty, NULL);
+}
+
+DEFPY_YANG (match_ecommunity,
+ match_ecommunity_cmd,
+ "match extcommunity <(1-99)|(100-500)|EXTCOMMUNITY_LIST_NAME>",
+ MATCH_STR
+ "Match BGP/VPN extended community list\n"
+ "Extended community-list number (standard)\n"
+ "Extended community-list number (expanded)\n"
+ "Extended community-list name\n")
+{
+ const char *xpath =
+ "./match-condition[condition='frr-bgp-route-map:match-extcommunity']";
+ char xpath_value[XPATH_MAXLEN];
+ int idx_comm_list = 2;
+
+ nb_cli_enqueue_change(vty, xpath, NB_OP_CREATE, NULL);
+
+ snprintf(
+ xpath_value, sizeof(xpath_value),
+ "%s/rmap-match-condition/frr-bgp-route-map:comm-list/comm-list-name",
+ xpath);
+ nb_cli_enqueue_change(vty, xpath_value, NB_OP_MODIFY, argv[idx_comm_list]->arg);
+
+ return nb_cli_apply_changes(vty, NULL);
+}
+
+
+DEFUN_YANG (no_match_ecommunity,
+ no_match_ecommunity_cmd,
+ "no match extcommunity [<(1-99)|(100-500)|EXTCOMMUNITY_LIST_NAME>]",
+ NO_STR
+ MATCH_STR
+ "Match BGP/VPN extended community list\n"
+ "Extended community-list number (standard)\n"
+ "Extended community-list number (expanded)\n"
+ "Extended community-list name\n")
+{
+ const char *xpath =
+ "./match-condition[condition='frr-bgp-route-map:match-extcommunity']";
+
+ nb_cli_enqueue_change(vty, xpath, NB_OP_DESTROY, NULL);
+ return nb_cli_apply_changes(vty, NULL);
+}
+
+
+DEFUN_YANG (match_aspath,
+ match_aspath_cmd,
+ "match as-path AS_PATH_FILTER_NAME",
+ MATCH_STR
+ "Match BGP AS path list\n"
+ "AS path access-list name\n")
+{
+ int idx_word = 2;
+
+ const char *xpath =
+ "./match-condition[condition='frr-bgp-route-map:as-path-list']";
+ char xpath_value[XPATH_MAXLEN];
+
+ nb_cli_enqueue_change(vty, xpath, NB_OP_CREATE, NULL);
+ snprintf(xpath_value, sizeof(xpath_value),
+ "%s/rmap-match-condition/frr-bgp-route-map:list-name", xpath);
+ nb_cli_enqueue_change(vty, xpath_value, NB_OP_MODIFY,
+ argv[idx_word]->arg);
+
+ return nb_cli_apply_changes(vty, NULL);
+}
+
+
+DEFUN_YANG (no_match_aspath,
+ no_match_aspath_cmd,
+ "no match as-path [AS_PATH_FILTER_NAME]",
+ NO_STR
+ MATCH_STR
+ "Match BGP AS path list\n"
+ "AS path access-list name\n")
+{
+ const char *xpath =
+ "./match-condition[condition='frr-bgp-route-map:as-path-list']";
+
+ nb_cli_enqueue_change(vty, xpath, NB_OP_DESTROY, NULL);
+
+ return nb_cli_apply_changes(vty, NULL);
+}
+
+DEFUN_YANG (match_origin,
+ match_origin_cmd,
+ "match origin <egp|igp|incomplete>",
+ MATCH_STR
+ "BGP origin code\n"
+ "remote EGP\n"
+ "local IGP\n"
+ "unknown heritage\n")
+{
+ int idx_origin = 2;
+ const char *origin_type;
+ const char *xpath =
+ "./match-condition[condition='frr-bgp-route-map:match-origin']";
+ char xpath_value[XPATH_MAXLEN];
+
+ if (strncmp(argv[idx_origin]->arg, "igp", 2) == 0)
+ origin_type = "igp";
+ else if (strncmp(argv[idx_origin]->arg, "egp", 1) == 0)
+ origin_type = "egp";
+ else if (strncmp(argv[idx_origin]->arg, "incomplete", 2) == 0)
+ origin_type = "incomplete";
+ else {
+ vty_out(vty, "%% Invalid match origin type\n");
+ return CMD_WARNING_CONFIG_FAILED;
+ }
+
+ nb_cli_enqueue_change(vty, xpath, NB_OP_CREATE, NULL);
+ snprintf(xpath_value, sizeof(xpath_value),
+ "%s/rmap-match-condition/frr-bgp-route-map:origin", xpath);
+ nb_cli_enqueue_change(vty, xpath_value, NB_OP_MODIFY, origin_type);
+
+ return nb_cli_apply_changes(vty, NULL);
+}
+
+
+DEFUN_YANG (no_match_origin,
+ no_match_origin_cmd,
+ "no match origin [<egp|igp|incomplete>]",
+ NO_STR
+ MATCH_STR
+ "BGP origin code\n"
+ "remote EGP\n"
+ "local IGP\n"
+ "unknown heritage\n")
+{
+ const char *xpath =
+ "./match-condition[condition='frr-bgp-route-map:match-origin']";
+ nb_cli_enqueue_change(vty, xpath, NB_OP_DESTROY, NULL);
+ return nb_cli_apply_changes(vty, NULL);
+}
+
+DEFUN_YANG (set_table_id,
+ set_table_id_cmd,
+ "set table (1-4294967295)",
+ SET_STR
+ "export route to non-main kernel table\n"
+ "Kernel routing table id\n")
+{
+ int idx_number = 2;
+ const char *xpath = "./set-action[action='frr-bgp-route-map:table']";
+ char xpath_value[XPATH_MAXLEN];
+
+ nb_cli_enqueue_change(vty, xpath, NB_OP_CREATE, NULL);
+ snprintf(xpath_value, sizeof(xpath_value),
+ "%s/rmap-set-action/frr-bgp-route-map:table", xpath);
+ nb_cli_enqueue_change(vty, xpath_value, NB_OP_MODIFY,
+ argv[idx_number]->arg);
+ return nb_cli_apply_changes(vty, NULL);
+}
+
+DEFUN_YANG (no_set_table_id,
+ no_set_table_id_cmd,
+ "no set table",
+ NO_STR
+ SET_STR
+ "export route to non-main kernel table\n")
+{
+ const char *xpath = "./set-action[action='frr-bgp-route-map:table']";
+ nb_cli_enqueue_change(vty, xpath, NB_OP_DESTROY, NULL);
+ return nb_cli_apply_changes(vty, NULL);
+}
+
+DEFUN_YANG (set_ip_nexthop_peer,
+ set_ip_nexthop_peer_cmd,
+ "[no] set ip next-hop peer-address",
+ NO_STR
+ SET_STR
+ IP_STR
+ "Next hop address\n"
+ "Use peer address (for BGP only)\n")
+{
+ char xpath_value[XPATH_MAXLEN];
+ const char *xpath =
+ "./set-action[action='frr-bgp-route-map:set-ipv4-nexthop']";
+
+ if (strmatch(argv[0]->text, "no"))
+ nb_cli_enqueue_change(vty, xpath, NB_OP_DESTROY, NULL);
+ else {
+ nb_cli_enqueue_change(vty, xpath, NB_OP_CREATE, NULL);
+ snprintf(xpath_value, sizeof(xpath_value),
+ "%s/rmap-set-action/frr-bgp-route-map:ipv4-nexthop",
+ xpath);
+ nb_cli_enqueue_change(vty, xpath_value, NB_OP_MODIFY,
+ "peer-address");
+ }
+ return nb_cli_apply_changes(vty, NULL);
+}
+
+DEFUN_YANG (set_ip_nexthop_unchanged,
+ set_ip_nexthop_unchanged_cmd,
+ "[no] set ip next-hop unchanged",
+ NO_STR
+ SET_STR
+ IP_STR
+ "Next hop address\n"
+ "Don't modify existing Next hop address\n")
+{
+ char xpath_value[XPATH_MAXLEN];
+ const char *xpath =
+ "./set-action[action='frr-bgp-route-map:set-ipv4-nexthop']";
+
+ if (strmatch(argv[0]->text, "no"))
+ nb_cli_enqueue_change(vty, xpath, NB_OP_DESTROY, NULL);
+ else {
+ nb_cli_enqueue_change(vty, xpath, NB_OP_CREATE, NULL);
+ snprintf(xpath_value, sizeof(xpath_value),
+ "%s/rmap-set-action/frr-bgp-route-map:ipv4-nexthop",
+ xpath);
+ nb_cli_enqueue_change(vty, xpath_value, NB_OP_MODIFY,
+ "unchanged");
+ }
+ return nb_cli_apply_changes(vty, NULL);
+}
+
+DEFUN_YANG (set_distance,
+ set_distance_cmd,
+ "set distance (0-255)",
+ SET_STR
+ "BGP Administrative Distance to use\n"
+ "Distance value\n")
+{
+ int idx_number = 2;
+ const char *xpath = "./set-action[action='frr-bgp-route-map:distance']";
+ char xpath_value[XPATH_MAXLEN];
+
+ nb_cli_enqueue_change(vty, xpath, NB_OP_CREATE, NULL);
+ snprintf(xpath_value, sizeof(xpath_value),
+ "%s/rmap-set-action/frr-bgp-route-map:distance", xpath);
+ nb_cli_enqueue_change(vty, xpath_value, NB_OP_MODIFY,
+ argv[idx_number]->arg);
+ return nb_cli_apply_changes(vty, NULL);
+}
+
+DEFUN_YANG (no_set_distance,
+ no_set_distance_cmd,
+ "no set distance [(0-255)]",
+ NO_STR SET_STR
+ "BGP Administrative Distance to use\n"
+ "Distance value\n")
+{
+ const char *xpath = "./set-action[action='frr-bgp-route-map:distance']";
+
+ nb_cli_enqueue_change(vty, xpath, NB_OP_DESTROY, NULL);
+ return nb_cli_apply_changes(vty, NULL);
+}
+
+DEFPY_YANG(set_l3vpn_nexthop_encapsulation, set_l3vpn_nexthop_encapsulation_cmd,
+ "[no] set l3vpn next-hop encapsulation gre",
+ NO_STR SET_STR
+ "L3VPN operations\n"
+ "Next hop Information\n"
+ "Encapsulation options (for BGP only)\n"
+ "Accept L3VPN traffic over GRE encapsulation\n")
+{
+ const char *xpath =
+ "./set-action[action='frr-bgp-route-map:set-l3vpn-nexthop-encapsulation']";
+ const char *xpath_value =
+ "./set-action[action='frr-bgp-route-map:set-l3vpn-nexthop-encapsulation']/rmap-set-action/frr-bgp-route-map:l3vpn-nexthop-encapsulation";
+ enum nb_operation operation;
+
+ if (no)
+ operation = NB_OP_DESTROY;
+ else
+ operation = NB_OP_CREATE;
+
+ nb_cli_enqueue_change(vty, xpath, operation, NULL);
+ if (operation == NB_OP_DESTROY)
+ return nb_cli_apply_changes(vty, NULL);
+
+ nb_cli_enqueue_change(vty, xpath_value, NB_OP_MODIFY, "gre");
+
+ return nb_cli_apply_changes(vty, NULL);
+}
+
+DEFUN_YANG (set_local_pref,
+ set_local_pref_cmd,
+ "set local-preference WORD",
+ SET_STR
+ "BGP local preference path attribute\n"
+ "Preference value (0-4294967295)\n")
+{
+ int idx_number = 2;
+ const char *xpath =
+ "./set-action[action='frr-bgp-route-map:set-local-preference']";
+ char xpath_value[XPATH_MAXLEN];
+
+ nb_cli_enqueue_change(vty, xpath, NB_OP_CREATE, NULL);
+ snprintf(xpath_value, sizeof(xpath_value),
+ "%s/rmap-set-action/frr-bgp-route-map:local-pref", xpath);
+ nb_cli_enqueue_change(vty, xpath_value, NB_OP_MODIFY,
+ argv[idx_number]->arg);
+ return nb_cli_apply_changes(vty, NULL);
+}
+
+DEFUN_YANG (no_set_local_pref,
+ no_set_local_pref_cmd,
+ "no set local-preference [WORD]",
+ NO_STR
+ SET_STR
+ "BGP local preference path attribute\n"
+ "Preference value (0-4294967295)\n")
+{
+ const char *xpath =
+ "./set-action[action='frr-bgp-route-map:set-local-preference']";
+
+ nb_cli_enqueue_change(vty, xpath, NB_OP_DESTROY, NULL);
+ return nb_cli_apply_changes(vty, NULL);
+}
+
+DEFUN_YANG (set_weight,
+ set_weight_cmd,
+ "set weight (0-4294967295)",
+ SET_STR
+ "BGP weight for routing table\n"
+ "Weight value\n")
+{
+ int idx_number = 2;
+ const char *xpath = "./set-action[action='frr-bgp-route-map:weight']";
+ char xpath_value[XPATH_MAXLEN];
+
+ nb_cli_enqueue_change(vty, xpath, NB_OP_CREATE, NULL);
+ snprintf(xpath_value, sizeof(xpath_value),
+ "%s/rmap-set-action/frr-bgp-route-map:weight", xpath);
+ nb_cli_enqueue_change(vty, xpath_value, NB_OP_MODIFY,
+ argv[idx_number]->arg);
+ return nb_cli_apply_changes(vty, NULL);
+}
+
+DEFUN_YANG (no_set_weight,
+ no_set_weight_cmd,
+ "no set weight [(0-4294967295)]",
+ NO_STR
+ SET_STR
+ "BGP weight for routing table\n"
+ "Weight value\n")
+{
+ const char *xpath = "./set-action[action='frr-bgp-route-map:weight']";
+
+ nb_cli_enqueue_change(vty, xpath, NB_OP_DESTROY, NULL);
+ return nb_cli_apply_changes(vty, NULL);
+}
+
+DEFUN_YANG (set_label_index,
+ set_label_index_cmd,
+ "set label-index (0-1048560)",
+ SET_STR
+ "Label index to associate with the prefix\n"
+ "Label index value\n")
+{
+ int idx_number = 2;
+ const char *xpath =
+ "./set-action[action='frr-bgp-route-map:label-index']";
+ char xpath_value[XPATH_MAXLEN];
+
+ nb_cli_enqueue_change(vty, xpath, NB_OP_CREATE, NULL);
+ snprintf(xpath_value, sizeof(xpath_value),
+ "%s/rmap-set-action/frr-bgp-route-map:label-index", xpath);
+ nb_cli_enqueue_change(vty, xpath_value, NB_OP_MODIFY,
+ argv[idx_number]->arg);
+ return nb_cli_apply_changes(vty, NULL);
+}
+
+DEFUN_YANG (no_set_label_index,
+ no_set_label_index_cmd,
+ "no set label-index [(0-1048560)]",
+ NO_STR
+ SET_STR
+ "Label index to associate with the prefix\n"
+ "Label index value\n")
+{
+ const char *xpath =
+ "./set-action[action='frr-bgp-route-map:label-index']";
+
+ nb_cli_enqueue_change(vty, xpath, NB_OP_DESTROY, NULL);
+ return nb_cli_apply_changes(vty, NULL);
+}
+
+DEFUN_YANG (set_aspath_prepend_asn,
+ set_aspath_prepend_asn_cmd,
+ "set as-path prepend (1-4294967295)...",
+ SET_STR
+ "Transform BGP AS_PATH attribute\n"
+ "Prepend to the as-path\n"
+ "AS number\n")
+{
+ int idx_asn = 3;
+ int ret;
+ char *str;
+
+ str = argv_concat(argv, argc, idx_asn);
+
+ const char *xpath =
+ "./set-action[action='frr-bgp-route-map:as-path-prepend']";
+ char xpath_value[XPATH_MAXLEN];
+
+ nb_cli_enqueue_change(vty, xpath, NB_OP_CREATE, NULL);
+ snprintf(xpath_value, sizeof(xpath_value),
+ "%s/rmap-set-action/frr-bgp-route-map:prepend-as-path", xpath);
+ nb_cli_enqueue_change(vty, xpath_value, NB_OP_MODIFY, str);
+ ret = nb_cli_apply_changes(vty, NULL);
+ XFREE(MTYPE_TMP, str);
+ return ret;
+}
+
+DEFUN_YANG (set_aspath_prepend_lastas,
+ set_aspath_prepend_lastas_cmd,
+ "set as-path prepend last-as (1-10)",
+ SET_STR
+ "Transform BGP AS_PATH attribute\n"
+ "Prepend to the as-path\n"
+ "Use the last AS-number in the as-path\n"
+ "Number of times to insert\n")
+{
+ int idx_num = 4;
+
+ const char *xpath =
+ "./set-action[action='frr-bgp-route-map:as-path-prepend']";
+ char xpath_value[XPATH_MAXLEN];
+
+ nb_cli_enqueue_change(vty, xpath, NB_OP_CREATE, NULL);
+ snprintf(xpath_value, sizeof(xpath_value),
+ "%s/rmap-set-action/frr-bgp-route-map:last-as", xpath);
+ nb_cli_enqueue_change(vty, xpath_value, NB_OP_MODIFY,
+ argv[idx_num]->arg);
+ return nb_cli_apply_changes(vty, NULL);
+}
+
+DEFPY_YANG (set_aspath_replace_asn,
+ set_aspath_replace_asn_cmd,
+ "set as-path replace <any|(1-4294967295)>$replace",
+ SET_STR
+ "Transform BGP AS_PATH attribute\n"
+ "Replace AS number to local AS number\n"
+ "Replace any AS number to local AS number\n"
+ "Replace a specific AS number to local AS number\n")
+{
+ const char *xpath =
+ "./set-action[action='frr-bgp-route-map:as-path-replace']";
+ char xpath_value[XPATH_MAXLEN];
+
+ nb_cli_enqueue_change(vty, xpath, NB_OP_CREATE, NULL);
+ snprintf(xpath_value, sizeof(xpath_value),
+ "%s/rmap-set-action/frr-bgp-route-map:replace-as-path", xpath);
+ nb_cli_enqueue_change(vty, xpath_value, NB_OP_MODIFY, replace);
+ return nb_cli_apply_changes(vty, NULL);
+}
+
+DEFPY_YANG (no_set_aspath_replace_asn,
+ no_set_aspath_replace_asn_cmd,
+ "no set as-path replace [<any|(1-4294967295)>]",
+ NO_STR
+ SET_STR
+ "Transform BGP AS_PATH attribute\n"
+ "Replace AS number to local AS number\n"
+ "Replace any AS number to local AS number\n"
+ "Replace a specific AS number to local AS number\n")
+{
+ const char *xpath =
+ "./set-action[action='frr-bgp-route-map:as-path-replace']";
+
+ nb_cli_enqueue_change(vty, xpath, NB_OP_DESTROY, NULL);
+ return nb_cli_apply_changes(vty, NULL);
+}
+
+DEFUN_YANG (no_set_aspath_prepend,
+ no_set_aspath_prepend_cmd,
+ "no set as-path prepend [(1-4294967295)]",
+ NO_STR
+ SET_STR
+ "Transform BGP AS_PATH attribute\n"
+ "Prepend to the as-path\n"
+ "AS number\n")
+{
+ const char *xpath =
+ "./set-action[action='frr-bgp-route-map:as-path-prepend']";
+
+ nb_cli_enqueue_change(vty, xpath, NB_OP_DESTROY, NULL);
+ return nb_cli_apply_changes(vty, NULL);
+}
+
+DEFUN_YANG (no_set_aspath_prepend_lastas,
+ no_set_aspath_prepend_lastas_cmd,
+ "no set as-path prepend last-as [(1-10)]",
+ NO_STR
+ SET_STR
+ "Transform BGP AS_PATH attribute\n"
+ "Prepend to the as-path\n"
+ "Use the peers AS-number\n"
+ "Number of times to insert\n")
+{
+ const char *xpath =
+ "./set-action[action='frr-bgp-route-map:as-path-prepend']";
+
+ nb_cli_enqueue_change(vty, xpath, NB_OP_DESTROY, NULL);
+ return nb_cli_apply_changes(vty, NULL);
+}
+
+DEFUN_YANG (set_aspath_exclude,
+ set_aspath_exclude_cmd,
+ "set as-path exclude (1-4294967295)...",
+ SET_STR
+ "Transform BGP AS-path attribute\n"
+ "Exclude from the as-path\n"
+ "AS number\n")
+{
+ int idx_asn = 3;
+ int ret;
+ char *str;
+
+ str = argv_concat(argv, argc, idx_asn);
+
+ const char *xpath =
+ "./set-action[action='frr-bgp-route-map:as-path-exclude']";
+ char xpath_value[XPATH_MAXLEN];
+
+ nb_cli_enqueue_change(vty, xpath, NB_OP_CREATE, NULL);
+ snprintf(xpath_value, sizeof(xpath_value),
+ "%s/rmap-set-action/frr-bgp-route-map:exclude-as-path", xpath);
+ nb_cli_enqueue_change(vty, xpath_value, NB_OP_MODIFY, str);
+ ret = nb_cli_apply_changes(vty, NULL);
+ XFREE(MTYPE_TMP, str);
+ return ret;
+}
+
+DEFUN_YANG (no_set_aspath_exclude,
+ no_set_aspath_exclude_cmd,
+ "no set as-path exclude (1-4294967295)...",
+ NO_STR
+ SET_STR
+ "Transform BGP AS_PATH attribute\n"
+ "Exclude from the as-path\n"
+ "AS number\n")
+{
+ const char *xpath =
+ "./set-action[action='frr-bgp-route-map:as-path-exclude']";
+
+ nb_cli_enqueue_change(vty, xpath, NB_OP_DESTROY, NULL);
+ return nb_cli_apply_changes(vty, NULL);
+}
+
+ALIAS_YANG (no_set_aspath_exclude, no_set_aspath_exclude_all_cmd,
+ "no set as-path exclude",
+ NO_STR SET_STR
+ "Transform BGP AS_PATH attribute\n"
+ "Exclude from the as-path\n")
+
+DEFUN_YANG (set_community,
+ set_community_cmd,
+ "set community AA:NN...",
+ SET_STR
+ "BGP community attribute\n"
+ COMMUNITY_VAL_STR)
+{
+ int idx_aa_nn = 2;
+ int i;
+ int first = 0;
+ int additive = 0;
+ struct buffer *b;
+ struct community *com = NULL;
+ char *str;
+ char *argstr = NULL;
+ int ret;
+
+ const char *xpath =
+ "./set-action[action='frr-bgp-route-map:set-community']";
+ char xpath_value[XPATH_MAXLEN];
+
+ nb_cli_enqueue_change(vty, xpath, NB_OP_CREATE, NULL);
+ snprintf(xpath_value, sizeof(xpath_value),
+ "%s/rmap-set-action/frr-bgp-route-map:community-string",
+ xpath);
+
+ b = buffer_new(1024);
+
+ for (i = idx_aa_nn; i < argc; i++) {
+ if (strncmp(argv[i]->arg, "additive", strlen(argv[i]->arg))
+ == 0) {
+ additive = 1;
+ continue;
+ }
+
+ if (first)
+ buffer_putc(b, ' ');
+ else
+ first = 1;
+
+ if (strncmp(argv[i]->arg, "internet", strlen(argv[i]->arg))
+ == 0) {
+ buffer_putstr(b, "internet");
+ continue;
+ }
+ if (strncmp(argv[i]->arg, "local-AS", strlen(argv[i]->arg))
+ == 0) {
+ buffer_putstr(b, "local-AS");
+ continue;
+ }
+ if (strncmp(argv[i]->arg, "no-a", strlen("no-a")) == 0
+ && strncmp(argv[i]->arg, "no-advertise",
+ strlen(argv[i]->arg))
+ == 0) {
+ buffer_putstr(b, "no-advertise");
+ continue;
+ }
+ if (strncmp(argv[i]->arg, "no-e", strlen("no-e")) == 0
+ && strncmp(argv[i]->arg, "no-export", strlen(argv[i]->arg))
+ == 0) {
+ buffer_putstr(b, "no-export");
+ continue;
+ }
+ if (strncmp(argv[i]->arg, "blackhole", strlen(argv[i]->arg))
+ == 0) {
+ buffer_putstr(b, "blackhole");
+ continue;
+ }
+ if (strncmp(argv[i]->arg, "graceful-shutdown",
+ strlen(argv[i]->arg))
+ == 0) {
+ buffer_putstr(b, "graceful-shutdown");
+ continue;
+ }
+ buffer_putstr(b, argv[i]->arg);
+ }
+ buffer_putc(b, '\0');
+
+ /* Fetch result string then compile it to communities attribute. */
+ str = buffer_getstr(b);
+ buffer_free(b);
+
+ if (str)
+ com = community_str2com(str);
+
+ /* Can't compile user input into communities attribute. */
+ if (!com) {
+ vty_out(vty, "%% Malformed communities attribute '%s'\n", str);
+ XFREE(MTYPE_TMP, str);
+ return CMD_WARNING_CONFIG_FAILED;
+ }
+ XFREE(MTYPE_TMP, str);
+
+ /* Set communites attribute string. */
+ str = community_str(com, false, false);
+
+ if (additive) {
+ size_t argstr_sz = strlen(str) + strlen(" additive") + 1;
+ argstr = XCALLOC(MTYPE_TMP, argstr_sz);
+ strlcpy(argstr, str, argstr_sz);
+ strlcat(argstr, " additive", argstr_sz);
+ nb_cli_enqueue_change(vty, xpath_value, NB_OP_MODIFY, argstr);
+ } else
+ nb_cli_enqueue_change(vty, xpath_value, NB_OP_MODIFY, str);
+
+ ret = nb_cli_apply_changes(vty, NULL);
+
+ if (argstr)
+ XFREE(MTYPE_TMP, argstr);
+ community_free(&com);
+
+ return ret;
+}
+
+DEFUN_YANG (set_community_none,
+ set_community_none_cmd,
+ "set community none",
+ SET_STR
+ "BGP community attribute\n"
+ "No community attribute\n")
+{
+ const char *xpath =
+ "./set-action[action='frr-bgp-route-map:set-community']";
+ char xpath_value[XPATH_MAXLEN];
+
+ nb_cli_enqueue_change(vty, xpath, NB_OP_CREATE, NULL);
+
+ snprintf(xpath_value, sizeof(xpath_value),
+ "%s/rmap-set-action/frr-bgp-route-map:community-none", xpath);
+ nb_cli_enqueue_change(vty, xpath_value, NB_OP_MODIFY, "true");
+ return nb_cli_apply_changes(vty, NULL);
+}
+
+DEFUN_YANG (no_set_community,
+ no_set_community_cmd,
+ "no set community AA:NN...",
+ NO_STR
+ SET_STR
+ "BGP community attribute\n"
+ COMMUNITY_VAL_STR)
+{
+ const char *xpath =
+ "./set-action[action='frr-bgp-route-map:set-community']";
+
+ nb_cli_enqueue_change(vty, xpath, NB_OP_DESTROY, NULL);
+ return nb_cli_apply_changes(vty, NULL);
+}
+
+ALIAS_YANG (no_set_community,
+ no_set_community_short_cmd,
+ "no set community",
+ NO_STR
+ SET_STR
+ "BGP community attribute\n")
+
+DEFPY_YANG (set_community_delete,
+ set_community_delete_cmd,
+ "set comm-list <(1-99)|(100-500)|COMMUNITY_LIST_NAME> delete",
+ SET_STR
+ "set BGP community list (for deletion)\n"
+ "Community-list number (standard)\n"
+ "Community-list number (expanded)\n"
+ "Community-list name\n"
+ "Delete matching communities\n")
+{
+ const char *xpath =
+ "./set-action[action='frr-bgp-route-map:comm-list-delete']";
+ char xpath_value[XPATH_MAXLEN];
+ int idx_comm_list = 2;
+
+ nb_cli_enqueue_change(vty, xpath, NB_OP_CREATE, NULL);
+
+ snprintf(xpath_value, sizeof(xpath_value),
+ "%s/rmap-set-action/frr-bgp-route-map:comm-list-name",
+ xpath);
+ nb_cli_enqueue_change(vty, xpath_value, NB_OP_MODIFY,
+ argv[idx_comm_list]->arg);
+
+ return nb_cli_apply_changes(vty, NULL);
+
+}
+
+DEFUN_YANG (no_set_community_delete,
+ no_set_community_delete_cmd,
+ "no set comm-list [<(1-99)|(100-500)|COMMUNITY_LIST_NAME> delete]",
+ NO_STR
+ SET_STR
+ "set BGP community list (for deletion)\n"
+ "Community-list number (standard)\n"
+ "Community-list number (expanded)\n"
+ "Community-list name\n"
+ "Delete matching communities\n")
+{
+ const char *xpath =
+ "./set-action[action='frr-bgp-route-map:comm-list-delete']";
+
+ nb_cli_enqueue_change(vty, xpath, NB_OP_DESTROY, NULL);
+ return nb_cli_apply_changes(vty, NULL);
+}
+
+DEFUN_YANG (set_lcommunity,
+ set_lcommunity_cmd,
+ "set large-community AA:BB:CC...",
+ SET_STR
+ "BGP large community attribute\n"
+ "Large Community number in aa:bb:cc format or additive\n")
+{
+ char *str;
+ int ret;
+ const char *xpath =
+ "./set-action[action='frr-bgp-route-map:set-large-community']";
+ char xpath_value[XPATH_MAXLEN];
+
+ nb_cli_enqueue_change(vty, xpath, NB_OP_CREATE, NULL);
+
+ snprintf(xpath_value, sizeof(xpath_value),
+ "%s/rmap-set-action/frr-bgp-route-map:large-community-string",
+ xpath);
+ str = argv_concat(argv, argc, 2);
+ nb_cli_enqueue_change(vty, xpath_value, NB_OP_MODIFY, str);
+ ret = nb_cli_apply_changes(vty, NULL);
+ XFREE(MTYPE_TMP, str);
+ return ret;
+}
+
+DEFUN_YANG (set_lcommunity_none,
+ set_lcommunity_none_cmd,
+ "set large-community none",
+ SET_STR
+ "BGP large community attribute\n"
+ "No large community attribute\n")
+{
+ const char *xpath =
+ "./set-action[action='frr-bgp-route-map:set-large-community']";
+ char xpath_value[XPATH_MAXLEN];
+
+ nb_cli_enqueue_change(vty, xpath, NB_OP_CREATE, NULL);
+
+ snprintf(xpath_value, sizeof(xpath_value),
+ "%s/rmap-set-action/frr-bgp-route-map:large-community-none",
+ xpath);
+ nb_cli_enqueue_change(vty, xpath_value, NB_OP_MODIFY, "true");
+ return nb_cli_apply_changes(vty, NULL);
+}
+
+DEFUN_YANG (no_set_lcommunity,
+ no_set_lcommunity_cmd,
+ "no set large-community none",
+ NO_STR
+ SET_STR
+ "BGP large community attribute\n"
+ "No community attribute\n")
+{
+ const char *xpath =
+ "./set-action[action='frr-bgp-route-map:set-large-community']";
+
+ nb_cli_enqueue_change(vty, xpath, NB_OP_DESTROY, NULL);
+ return nb_cli_apply_changes(vty, NULL);
+}
+
+DEFUN_YANG (no_set_lcommunity1,
+ no_set_lcommunity1_cmd,
+ "no set large-community AA:BB:CC...",
+ NO_STR
+ SET_STR
+ "BGP large community attribute\n"
+ "Large community in AA:BB:CC... format or additive\n")
+{
+ const char *xpath =
+ "./set-action[action='frr-bgp-route-map:set-large-community']";
+
+ nb_cli_enqueue_change(vty, xpath, NB_OP_DESTROY, NULL);
+ return nb_cli_apply_changes(vty, NULL);
+}
+
+ALIAS_YANG (no_set_lcommunity1,
+ no_set_lcommunity1_short_cmd,
+ "no set large-community",
+ NO_STR
+ SET_STR
+ "BGP large community attribute\n")
+
+DEFPY_YANG (set_lcommunity_delete,
+ set_lcommunity_delete_cmd,
+ "set large-comm-list <(1-99)|(100-500)|LCOMMUNITY_LIST_NAME> delete",
+ SET_STR
+ "set BGP large community list (for deletion)\n"
+ "Large Community-list number (standard)\n"
+ "Large Communitly-list number (expanded)\n"
+ "Large Community-list name\n"
+ "Delete matching large communities\n")
+{
+ const char *xpath =
+ "./set-action[action='frr-bgp-route-map:large-comm-list-delete']";
+ char xpath_value[XPATH_MAXLEN];
+ int idx_lcomm_list = 2;
+
+ nb_cli_enqueue_change(vty, xpath, NB_OP_CREATE, NULL);
+
+ snprintf(xpath_value, sizeof(xpath_value),
+ "%s/rmap-set-action/frr-bgp-route-map:comm-list-name",
+ xpath);
+ nb_cli_enqueue_change(vty, xpath_value, NB_OP_MODIFY,
+ argv[idx_lcomm_list]->arg);
+
+ return nb_cli_apply_changes(vty, NULL);
+}
+
+DEFUN_YANG (no_set_lcommunity_delete,
+ no_set_lcommunity_delete_cmd,
+ "no set large-comm-list <(1-99)|(100-500)|LCOMMUNITY_LIST_NAME> [delete]",
+ NO_STR
+ SET_STR
+ "set BGP large community list (for deletion)\n"
+ "Large Community-list number (standard)\n"
+ "Large Communitly-list number (expanded)\n"
+ "Large Community-list name\n"
+ "Delete matching large communities\n")
+{
+ const char *xpath =
+ "./set-action[action='frr-bgp-route-map:large-comm-list-delete']";
+
+ nb_cli_enqueue_change(vty, xpath, NB_OP_DESTROY, NULL);
+ return nb_cli_apply_changes(vty, NULL);
+}
+
+ALIAS_YANG (no_set_lcommunity_delete,
+ no_set_lcommunity_delete_short_cmd,
+ "no set large-comm-list",
+ NO_STR
+ SET_STR
+ "set BGP large community list (for deletion)\n")
+
+DEFUN_YANG (set_ecommunity_rt,
+ set_ecommunity_rt_cmd,
+ "set extcommunity rt ASN:NN_OR_IP-ADDRESS:NN...",
+ SET_STR
+ "BGP extended community attribute\n"
+ "Route Target extended community\n"
+ "VPN extended community\n")
+{
+ int idx_asn_nn = 3;
+ char *str;
+ int ret;
+ const char *xpath =
+ "./set-action[action='frr-bgp-route-map:set-extcommunity-rt']";
+ char xpath_value[XPATH_MAXLEN];
+
+ nb_cli_enqueue_change(vty, xpath, NB_OP_CREATE, NULL);
+
+ snprintf(xpath_value, sizeof(xpath_value),
+ "%s/rmap-set-action/frr-bgp-route-map:extcommunity-rt", xpath);
+ str = argv_concat(argv, argc, idx_asn_nn);
+ nb_cli_enqueue_change(vty, xpath_value, NB_OP_MODIFY, str);
+ ret = nb_cli_apply_changes(vty, NULL);
+ XFREE(MTYPE_TMP, str);
+ return ret;
+}
+
+DEFUN_YANG (no_set_ecommunity_rt,
+ no_set_ecommunity_rt_cmd,
+ "no set extcommunity rt ASN:NN_OR_IP-ADDRESS:NN...",
+ NO_STR
+ SET_STR
+ "BGP extended community attribute\n"
+ "Route Target extended community\n"
+ "VPN extended community\n")
+{
+ const char *xpath =
+ "./set-action[action='frr-bgp-route-map:set-extcommunity-rt']";
+ nb_cli_enqueue_change(vty, xpath, NB_OP_DESTROY, NULL);
+ return nb_cli_apply_changes(vty, NULL);
+}
+
+ALIAS_YANG (no_set_ecommunity_rt,
+ no_set_ecommunity_rt_short_cmd,
+ "no set extcommunity rt",
+ NO_STR
+ SET_STR
+ "BGP extended community attribute\n"
+ "Route Target extended community\n")
+
+DEFUN_YANG (set_ecommunity_soo,
+ set_ecommunity_soo_cmd,
+ "set extcommunity soo ASN:NN_OR_IP-ADDRESS:NN...",
+ SET_STR
+ "BGP extended community attribute\n"
+ "Site-of-Origin extended community\n"
+ "VPN extended community\n")
+{
+ int idx_asn_nn = 3;
+ char *str;
+ int ret;
+ const char *xpath =
+ "./set-action[action='frr-bgp-route-map:set-extcommunity-soo']";
+ char xpath_value[XPATH_MAXLEN];
+
+ nb_cli_enqueue_change(vty, xpath, NB_OP_CREATE, NULL);
+
+ snprintf(xpath_value, sizeof(xpath_value),
+ "%s/rmap-set-action/frr-bgp-route-map:extcommunity-soo",
+ xpath);
+ str = argv_concat(argv, argc, idx_asn_nn);
+ nb_cli_enqueue_change(vty, xpath_value, NB_OP_MODIFY, str);
+ ret = nb_cli_apply_changes(vty, NULL);
+ XFREE(MTYPE_TMP, str);
+ return ret;
+}
+
+DEFUN_YANG (no_set_ecommunity_soo,
+ no_set_ecommunity_soo_cmd,
+ "no set extcommunity soo ASN:NN_OR_IP-ADDRESS:NN...",
+ NO_STR
+ SET_STR
+ "BGP extended community attribute\n"
+ "Site-of-Origin extended community\n"
+ "VPN extended community\n")
+{
+ const char *xpath =
+ "./set-action[action='frr-bgp-route-map:set-extcommunity-soo']";
+ nb_cli_enqueue_change(vty, xpath, NB_OP_DESTROY, NULL);
+ return nb_cli_apply_changes(vty, NULL);
+}
+
+ALIAS_YANG (no_set_ecommunity_soo,
+ no_set_ecommunity_soo_short_cmd,
+ "no set extcommunity soo",
+ NO_STR
+ SET_STR
+ "GP extended community attribute\n"
+ "Site-of-Origin extended community\n")
+
+DEFUN_YANG(set_ecommunity_none, set_ecommunity_none_cmd,
+ "set extcommunity none",
+ SET_STR
+ "BGP extended community attribute\n"
+ "No extended community attribute\n")
+{
+ const char *xpath =
+ "./set-action[action='frr-bgp-route-map:set-extcommunity-none']";
+ char xpath_value[XPATH_MAXLEN];
+
+ nb_cli_enqueue_change(vty, xpath, NB_OP_CREATE, NULL);
+
+ snprintf(xpath_value, sizeof(xpath_value),
+ "%s/rmap-set-action/frr-bgp-route-map:extcommunity-none",
+ xpath);
+ nb_cli_enqueue_change(vty, xpath_value, NB_OP_MODIFY, "true");
+ return nb_cli_apply_changes(vty, NULL);
+}
+
+DEFUN_YANG(no_set_ecommunity_none, no_set_ecommunity_none_cmd,
+ "no set extcommunity none",
+ NO_STR SET_STR
+ "BGP extended community attribute\n"
+ "No extended community attribute\n")
+{
+ const char *xpath =
+ "./set-action[action='frr-bgp-route-map:set-extcommunity-none']";
+ nb_cli_enqueue_change(vty, xpath, NB_OP_DESTROY, NULL);
+ return nb_cli_apply_changes(vty, NULL);
+}
+
+DEFUN_YANG (set_ecommunity_lb,
+ set_ecommunity_lb_cmd,
+ "set extcommunity bandwidth <(1-25600)|cumulative|num-multipaths> [non-transitive]",
+ SET_STR
+ "BGP extended community attribute\n"
+ "Link bandwidth extended community\n"
+ "Bandwidth value in Mbps\n"
+ "Cumulative bandwidth of all multipaths (outbound-only)\n"
+ "Internally computed bandwidth based on number of multipaths (outbound-only)\n"
+ "Attribute is set as non-transitive\n")
+{
+ int idx_lb = 3;
+ int idx_non_transitive = 0;
+ const char *xpath =
+ "./set-action[action='frr-bgp-route-map:set-extcommunity-lb']";
+ char xpath_lb_type[XPATH_MAXLEN];
+ char xpath_bandwidth[XPATH_MAXLEN];
+ char xpath_non_transitive[XPATH_MAXLEN];
+
+ nb_cli_enqueue_change(vty, xpath, NB_OP_CREATE, NULL);
+
+ snprintf(xpath_lb_type, sizeof(xpath_lb_type),
+ "%s/rmap-set-action/frr-bgp-route-map:extcommunity-lb/lb-type",
+ xpath);
+ snprintf(xpath_bandwidth, sizeof(xpath_bandwidth),
+ "%s/rmap-set-action/frr-bgp-route-map:extcommunity-lb/bandwidth",
+ xpath);
+ snprintf(xpath_non_transitive, sizeof(xpath_non_transitive),
+ "%s/rmap-set-action/frr-bgp-route-map:extcommunity-lb/two-octet-as-specific",
+ xpath);
+
+ if ((strcmp(argv[idx_lb]->arg, "cumulative")) == 0)
+ nb_cli_enqueue_change(vty, xpath_lb_type, NB_OP_MODIFY,
+ "cumulative-bandwidth");
+ else if ((strcmp(argv[idx_lb]->arg, "num-multipaths")) == 0)
+ nb_cli_enqueue_change(vty, xpath_lb_type, NB_OP_MODIFY,
+ "computed-bandwidth");
+ else {
+ nb_cli_enqueue_change(vty, xpath_lb_type, NB_OP_MODIFY,
+ "explicit-bandwidth");
+ nb_cli_enqueue_change(vty, xpath_bandwidth, NB_OP_MODIFY,
+ argv[idx_lb]->arg);
+ }
+
+ if (argv_find(argv, argc, "non-transitive", &idx_non_transitive))
+ nb_cli_enqueue_change(vty, xpath_non_transitive, NB_OP_MODIFY,
+ "true");
+ else
+ nb_cli_enqueue_change(vty, xpath_non_transitive, NB_OP_MODIFY,
+ "false");
+
+ return nb_cli_apply_changes(vty, NULL);
+}
+
+DEFUN_YANG (no_set_ecommunity_lb,
+ no_set_ecommunity_lb_cmd,
+ "no set extcommunity bandwidth <(1-25600)|cumulative|num-multipaths> [non-transitive]",
+ NO_STR
+ SET_STR
+ "BGP extended community attribute\n"
+ "Link bandwidth extended community\n"
+ "Bandwidth value in Mbps\n"
+ "Cumulative bandwidth of all multipaths (outbound-only)\n"
+ "Internally computed bandwidth based on number of multipaths (outbound-only)\n"
+ "Attribute is set as non-transitive\n")
+{
+ const char *xpath =
+ "./set-action[action='frr-bgp-route-map:set-extcommunity-lb']";
+
+ nb_cli_enqueue_change(vty, xpath, NB_OP_DESTROY, NULL);
+ return nb_cli_apply_changes(vty, NULL);
+}
+
+ALIAS_YANG (no_set_ecommunity_lb,
+ no_set_ecommunity_lb_short_cmd,
+ "no set extcommunity bandwidth",
+ NO_STR
+ SET_STR
+ "BGP extended community attribute\n"
+ "Link bandwidth extended community\n")
+
+DEFUN_YANG (set_origin,
+ set_origin_cmd,
+ "set origin <egp|igp|incomplete>",
+ SET_STR
+ "BGP origin code\n"
+ "remote EGP\n"
+ "local IGP\n"
+ "unknown heritage\n")
+{
+ int idx_origin = 2;
+ const char *origin_type;
+ const char *xpath =
+ "./set-action[action='frr-bgp-route-map:set-origin']";
+ char xpath_value[XPATH_MAXLEN];
+
+ if (strncmp(argv[idx_origin]->arg, "igp", 2) == 0)
+ origin_type = "igp";
+ else if (strncmp(argv[idx_origin]->arg, "egp", 1) == 0)
+ origin_type = "egp";
+ else if (strncmp(argv[idx_origin]->arg, "incomplete", 2) == 0)
+ origin_type = "incomplete";
+ else {
+ vty_out(vty, "%% Invalid match origin type\n");
+ return CMD_WARNING_CONFIG_FAILED;
+ }
+
+ nb_cli_enqueue_change(vty, xpath, NB_OP_CREATE, NULL);
+ snprintf(xpath_value, sizeof(xpath_value),
+ "%s/rmap-set-action/frr-bgp-route-map:origin", xpath);
+ nb_cli_enqueue_change(vty, xpath_value, NB_OP_MODIFY, origin_type);
+
+ return nb_cli_apply_changes(vty, NULL);
+}
+
+DEFUN_YANG (no_set_origin,
+ no_set_origin_cmd,
+ "no set origin [<egp|igp|incomplete>]",
+ NO_STR
+ SET_STR
+ "BGP origin code\n"
+ "remote EGP\n"
+ "local IGP\n"
+ "unknown heritage\n")
+{
+ const char *xpath =
+ "./set-action[action='frr-bgp-route-map:set-origin']";
+
+ nb_cli_enqueue_change(vty, xpath, NB_OP_DESTROY, NULL);
+ return nb_cli_apply_changes(vty, NULL);
+}
+
+DEFUN_YANG (set_atomic_aggregate,
+ set_atomic_aggregate_cmd,
+ "set atomic-aggregate",
+ SET_STR
+ "BGP atomic aggregate attribute\n" )
+{
+ const char *xpath =
+ "./set-action[action='frr-bgp-route-map:atomic-aggregate']";
+ char xpath_value[XPATH_MAXLEN];
+
+ nb_cli_enqueue_change(vty, xpath, NB_OP_CREATE, NULL);
+ snprintf(xpath_value, sizeof(xpath_value),
+ "%s/rmap-set-action/frr-bgp-route-map:atomic-aggregate",
+ xpath);
+ nb_cli_enqueue_change(vty, xpath_value, NB_OP_MODIFY, NULL);
+
+ return nb_cli_apply_changes(vty, NULL);
+}
+
+DEFUN_YANG (no_set_atomic_aggregate,
+ no_set_atomic_aggregate_cmd,
+ "no set atomic-aggregate",
+ NO_STR
+ SET_STR
+ "BGP atomic aggregate attribute\n" )
+{
+ const char *xpath =
+ "./set-action[action='frr-bgp-route-map:atomic-aggregate']";
+
+ nb_cli_enqueue_change(vty, xpath, NB_OP_DESTROY, NULL);
+ return nb_cli_apply_changes(vty, NULL);
+}
+
+DEFUN_YANG (set_aggregator_as,
+ set_aggregator_as_cmd,
+ "set aggregator as (1-4294967295) A.B.C.D",
+ SET_STR
+ "BGP aggregator attribute\n"
+ "AS number of aggregator\n"
+ "AS number\n"
+ "IP address of aggregator\n")
+{
+ int idx_number = 3;
+ int idx_ipv4 = 4;
+ char xpath_asn[XPATH_MAXLEN];
+ char xpath_addr[XPATH_MAXLEN];
+ const char *xpath =
+ "./set-action[action='frr-bgp-route-map:aggregator']";
+
+ nb_cli_enqueue_change(vty, xpath, NB_OP_CREATE, NULL);
+
+ snprintf(
+ xpath_asn, sizeof(xpath_asn),
+ "%s/rmap-set-action/frr-bgp-route-map:aggregator/aggregator-asn",
+ xpath);
+ nb_cli_enqueue_change(vty, xpath_asn, NB_OP_MODIFY,
+ argv[idx_number]->arg);
+
+ snprintf(
+ xpath_addr, sizeof(xpath_addr),
+ "%s/rmap-set-action/frr-bgp-route-map:aggregator/aggregator-address",
+ xpath);
+ nb_cli_enqueue_change(vty, xpath_addr, NB_OP_MODIFY,
+ argv[idx_ipv4]->arg);
+
+ return nb_cli_apply_changes(vty, NULL);
+}
+
+DEFUN_YANG (no_set_aggregator_as,
+ no_set_aggregator_as_cmd,
+ "no set aggregator as [(1-4294967295) A.B.C.D]",
+ NO_STR
+ SET_STR
+ "BGP aggregator attribute\n"
+ "AS number of aggregator\n"
+ "AS number\n"
+ "IP address of aggregator\n")
+{
+ const char *xpath =
+ "./set-action[action='frr-bgp-route-map:aggregator']";
+
+ nb_cli_enqueue_change(vty, xpath, NB_OP_DESTROY, NULL);
+ return nb_cli_apply_changes(vty, NULL);
+}
+
+DEFUN_YANG (match_ipv6_next_hop,
+ match_ipv6_next_hop_cmd,
+ "match ipv6 next-hop ACCESSLIST6_NAME",
+ MATCH_STR
+ IPV6_STR
+ "Match IPv6 next-hop address of route\n"
+ "IPv6 access-list name\n")
+{
+ const char *xpath =
+ "./match-condition[condition='frr-route-map:ipv6-next-hop-list']";
+ char xpath_value[XPATH_MAXLEN];
+
+ nb_cli_enqueue_change(vty, xpath, NB_OP_CREATE, NULL);
+ snprintf(xpath_value, sizeof(xpath_value),
+ "%s/rmap-match-condition/list-name", xpath);
+ nb_cli_enqueue_change(vty, xpath_value, NB_OP_MODIFY,
+ argv[argc - 1]->arg);
+
+ return nb_cli_apply_changes(vty, NULL);
+}
+
+DEFUN_YANG (no_match_ipv6_next_hop,
+ no_match_ipv6_next_hop_cmd,
+ "no match ipv6 next-hop [ACCESSLIST6_NAME]",
+ NO_STR
+ MATCH_STR
+ IPV6_STR
+ "Match IPv6 next-hop address of route\n"
+ "IPv6 access-list name\n")
+{
+ const char *xpath =
+ "./match-condition[condition='frr-route-map:ipv6-next-hop-list']";
+
+ nb_cli_enqueue_change(vty, xpath, NB_OP_DESTROY, NULL);
+ return nb_cli_apply_changes(vty, NULL);
+}
+
+DEFUN_YANG (match_ipv6_next_hop_address,
+ match_ipv6_next_hop_address_cmd,
+ "match ipv6 next-hop address X:X::X:X",
+ MATCH_STR
+ IPV6_STR
+ "Match IPv6 next-hop address of route\n"
+ "IPv6 address\n"
+ "IPv6 address of next hop\n")
+{
+ const char *xpath =
+ "./match-condition[condition='frr-bgp-route-map:ipv6-nexthop']";
+ char xpath_value[XPATH_MAXLEN];
+
+ nb_cli_enqueue_change(vty, xpath, NB_OP_CREATE, NULL);
+ snprintf(xpath_value, sizeof(xpath_value),
+ "%s/rmap-match-condition/frr-bgp-route-map:ipv6-address",
+ xpath);
+ nb_cli_enqueue_change(vty, xpath_value, NB_OP_MODIFY,
+ argv[argc - 1]->arg);
+
+ return nb_cli_apply_changes(vty, NULL);
+}
+
+DEFUN_YANG (no_match_ipv6_next_hop_address,
+ no_match_ipv6_next_hop_address_cmd,
+ "no match ipv6 next-hop address X:X::X:X",
+ NO_STR
+ MATCH_STR
+ IPV6_STR
+ "Match IPv6 next-hop address of route\n"
+ "IPv6 address\n"
+ "IPv6 address of next hop\n")
+{
+ const char *xpath =
+ "./match-condition[condition='frr-bgp-route-map:ipv6-nexthop']";
+
+ nb_cli_enqueue_change(vty, xpath, NB_OP_DESTROY, NULL);
+ return nb_cli_apply_changes(vty, NULL);
+}
+
+ALIAS_HIDDEN (match_ipv6_next_hop_address,
+ match_ipv6_next_hop_old_cmd,
+ "match ipv6 next-hop X:X::X:X",
+ MATCH_STR
+ IPV6_STR
+ "Match IPv6 next-hop address of route\n"
+ "IPv6 address of next hop\n")
+
+ALIAS_HIDDEN (no_match_ipv6_next_hop_address,
+ no_match_ipv6_next_hop_old_cmd,
+ "no match ipv6 next-hop X:X::X:X",
+ NO_STR
+ MATCH_STR
+ IPV6_STR
+ "Match IPv6 next-hop address of route\n"
+ "IPv6 address of next hop\n")
+
+DEFUN_YANG (match_ipv6_next_hop_prefix_list,
+ match_ipv6_next_hop_prefix_list_cmd,
+ "match ipv6 next-hop prefix-list PREFIXLIST_NAME",
+ MATCH_STR
+ IPV6_STR
+ "Match IPv6 next-hop address of route\n"
+ "Match entries by prefix-list\n"
+ "IPv6 prefix-list name\n")
+{
+ const char *xpath =
+ "./match-condition[condition='frr-route-map:ipv6-next-hop-prefix-list']";
+ char xpath_value[XPATH_MAXLEN];
+
+ nb_cli_enqueue_change(vty, xpath, NB_OP_CREATE, NULL);
+ snprintf(xpath_value, sizeof(xpath_value),
+ "%s/rmap-match-condition/list-name", xpath);
+ nb_cli_enqueue_change(vty, xpath_value, NB_OP_MODIFY,
+ argv[argc - 1]->arg);
+
+ return nb_cli_apply_changes(vty, NULL);
+}
+
+DEFUN_YANG (no_match_ipv6_next_hop_prefix_list,
+ no_match_ipv6_next_hop_prefix_list_cmd,
+ "no match ipv6 next-hop prefix-list [PREFIXLIST_NAME]",
+ NO_STR
+ MATCH_STR
+ IPV6_STR
+ "Match IPv6 next-hop address of route\n"
+ "Match entries by prefix-list\n"
+ "IPv6 prefix-list name\n")
+{
+ const char *xpath =
+ "./match-condition[condition='frr-route-map:ipv6-next-hop-prefix-list']";
+
+ nb_cli_enqueue_change(vty, xpath, NB_OP_DESTROY, NULL);
+ return nb_cli_apply_changes(vty, NULL);
+}
+
+DEFPY_YANG (match_ipv4_next_hop,
+ match_ipv4_next_hop_cmd,
+ "match ip next-hop address A.B.C.D",
+ MATCH_STR
+ IP_STR
+ "Match IP next-hop address of route\n"
+ "IP address\n"
+ "IP address of next-hop\n")
+{
+ const char *xpath =
+ "./match-condition[condition='frr-bgp-route-map:ipv4-nexthop']";
+ char xpath_value[XPATH_MAXLEN];
+
+ nb_cli_enqueue_change(vty, xpath, NB_OP_CREATE, NULL);
+ snprintf(xpath_value, sizeof(xpath_value),
+ "%s/rmap-match-condition/frr-bgp-route-map:ipv4-address",
+ xpath);
+ nb_cli_enqueue_change(vty, xpath_value, NB_OP_MODIFY, argv[4]->arg);
+
+ return nb_cli_apply_changes(vty, NULL);
+}
+
+DEFPY_YANG (no_match_ipv4_next_hop,
+ no_match_ipv4_next_hop_cmd,
+ "no match ip next-hop address [A.B.C.D]",
+ NO_STR
+ MATCH_STR
+ IP_STR
+ "Match IP next-hop address of route\n"
+ "IP address\n"
+ "IP address of next-hop\n")
+{
+ const char *xpath =
+ "./match-condition[condition='frr-bgp-route-map:ipv4-nexthop']";
+
+ nb_cli_enqueue_change(vty, xpath, NB_OP_DESTROY, NULL);
+ return nb_cli_apply_changes(vty, NULL);
+}
+
+DEFUN_YANG (set_ipv6_nexthop_peer,
+ set_ipv6_nexthop_peer_cmd,
+ "set ipv6 next-hop peer-address",
+ SET_STR
+ IPV6_STR
+ "Next hop address\n"
+ "Use peer address (for BGP only)\n")
+{
+ const char *xpath =
+ "./set-action[action='frr-bgp-route-map:ipv6-peer-address']";
+ char xpath_value[XPATH_MAXLEN];
+
+ nb_cli_enqueue_change(vty, xpath, NB_OP_CREATE, NULL);
+ snprintf(xpath_value, sizeof(xpath_value),
+ "%s/rmap-set-action/frr-bgp-route-map:preference", xpath);
+ nb_cli_enqueue_change(vty, xpath_value, NB_OP_MODIFY, "true");
+
+ return nb_cli_apply_changes(vty, NULL);
+}
+
+DEFUN_YANG (no_set_ipv6_nexthop_peer,
+ no_set_ipv6_nexthop_peer_cmd,
+ "no set ipv6 next-hop peer-address",
+ NO_STR
+ SET_STR
+ IPV6_STR
+ "IPv6 next-hop address\n"
+ "Use peer address (for BGP only)\n")
+{
+ const char *xpath =
+ "./set-action[action='frr-bgp-route-map:ipv6-peer-address']";
+
+ nb_cli_enqueue_change(vty, xpath, NB_OP_DESTROY, NULL);
+ return nb_cli_apply_changes(vty, NULL);
+}
+
+DEFUN_YANG (set_ipv6_nexthop_prefer_global,
+ set_ipv6_nexthop_prefer_global_cmd,
+ "set ipv6 next-hop prefer-global",
+ SET_STR
+ IPV6_STR
+ "IPv6 next-hop address\n"
+ "Prefer global over link-local if both exist\n")
+{
+ const char *xpath =
+ "./set-action[action='frr-bgp-route-map:ipv6-prefer-global']";
+ char xpath_value[XPATH_MAXLEN];
+
+ nb_cli_enqueue_change(vty, xpath, NB_OP_CREATE, NULL);
+ snprintf(xpath_value, sizeof(xpath_value),
+ "%s/rmap-set-action/frr-bgp-route-map:preference", xpath);
+ nb_cli_enqueue_change(vty, xpath_value, NB_OP_MODIFY, "true");
+
+ return nb_cli_apply_changes(vty, NULL);
+}
+
+DEFUN_YANG (no_set_ipv6_nexthop_prefer_global,
+ no_set_ipv6_nexthop_prefer_global_cmd,
+ "no set ipv6 next-hop prefer-global",
+ NO_STR
+ SET_STR
+ IPV6_STR
+ "IPv6 next-hop address\n"
+ "Prefer global over link-local if both exist\n")
+{
+ const char *xpath =
+ "./set-action[action='frr-bgp-route-map:ipv6-prefer-global']";
+
+ nb_cli_enqueue_change(vty, xpath, NB_OP_DESTROY, NULL);
+ return nb_cli_apply_changes(vty, NULL);
+}
+
+DEFUN_YANG (set_ipv6_nexthop_global,
+ set_ipv6_nexthop_global_cmd,
+ "set ipv6 next-hop global X:X::X:X",
+ SET_STR
+ IPV6_STR
+ "IPv6 next-hop address\n"
+ "IPv6 global address\n"
+ "IPv6 address of next hop\n")
+{
+ int idx_ipv6 = 4;
+ const char *xpath =
+ "./set-action[action='frr-bgp-route-map:ipv6-nexthop-global']";
+ char xpath_value[XPATH_MAXLEN];
+
+ nb_cli_enqueue_change(vty, xpath, NB_OP_CREATE, NULL);
+ snprintf(xpath_value, sizeof(xpath_value),
+ "%s/rmap-set-action/frr-bgp-route-map:ipv6-address", xpath);
+ nb_cli_enqueue_change(vty, xpath_value, NB_OP_MODIFY,
+ argv[idx_ipv6]->arg);
+
+ return nb_cli_apply_changes(vty, NULL);
+}
+
+DEFUN_YANG (no_set_ipv6_nexthop_global,
+ no_set_ipv6_nexthop_global_cmd,
+ "no set ipv6 next-hop global X:X::X:X",
+ NO_STR
+ SET_STR
+ IPV6_STR
+ "IPv6 next-hop address\n"
+ "IPv6 global address\n"
+ "IPv6 address of next hop\n")
+{
+ const char *xpath =
+ "./set-action[action='frr-bgp-route-map:ipv6-nexthop-global']";
+
+ nb_cli_enqueue_change(vty, xpath, NB_OP_DESTROY, NULL);
+ return nb_cli_apply_changes(vty, NULL);
+}
+
+#ifdef KEEP_OLD_VPN_COMMANDS
+DEFUN_YANG (set_vpn_nexthop,
+ set_vpn_nexthop_cmd,
+ "set <vpnv4 next-hop A.B.C.D|vpnv6 next-hop X:X::X:X>",
+ SET_STR
+ "VPNv4 information\n"
+ "VPN next-hop address\n"
+ "IP address of next hop\n"
+ "VPNv6 information\n"
+ "VPN next-hop address\n"
+ "IPv6 address of next hop\n")
+{
+ int idx_ip = 3;
+ afi_t afi;
+ int idx = 0;
+ char xpath_value[XPATH_MAXLEN];
+
+ if (argv_find_and_parse_vpnvx(argv, argc, &idx, &afi)) {
+ if (afi == AFI_IP) {
+ const char *xpath =
+ "./set-action[action='frr-bgp-route-map:ipv4-vpn-address']";
+
+ nb_cli_enqueue_change(vty, xpath, NB_OP_CREATE, NULL);
+ snprintf(
+ xpath_value, sizeof(xpath_value),
+ "%s/rmap-set-action/frr-bgp-route-map:ipv4-address",
+ xpath);
+ } else {
+ const char *xpath =
+ "./set-action[action='frr-bgp-route-map:ipv6-vpn-address']";
+
+ nb_cli_enqueue_change(vty, xpath, NB_OP_CREATE, NULL);
+ snprintf(
+ xpath_value, sizeof(xpath_value),
+ "%s/rmap-set-action/frr-bgp-route-map:ipv6-address",
+ xpath);
+ }
+
+ nb_cli_enqueue_change(vty, xpath_value, NB_OP_MODIFY,
+ argv[idx_ip]->arg);
+
+ return nb_cli_apply_changes(vty, NULL);
+ }
+
+ return CMD_SUCCESS;
+}
+
+DEFUN_YANG (no_set_vpn_nexthop,
+ no_set_vpn_nexthop_cmd,
+ "no set <vpnv4 next-hop A.B.C.D|vpnv6 next-hop X:X::X:X>",
+ NO_STR
+ SET_STR
+ "VPNv4 information\n"
+ "VPN next-hop address\n"
+ "IP address of next hop\n"
+ "VPNv6 information\n"
+ "VPN next-hop address\n"
+ "IPv6 address of next hop\n")
+{
+ afi_t afi;
+ int idx = 0;
+
+ if (argv_find_and_parse_vpnvx(argv, argc, &idx, &afi)) {
+ if (afi == AFI_IP) {
+ const char *xpath =
+ "./set-action[action='frr-bgp-route-map:ipv4-vpn-address']";
+ nb_cli_enqueue_change(vty, xpath, NB_OP_DESTROY, NULL);
+ } else {
+ const char *xpath =
+ "./set-action[action='frr-bgp-route-map:ipv6-vpn-address']";
+ nb_cli_enqueue_change(vty, xpath, NB_OP_DESTROY, NULL);
+ }
+ return nb_cli_apply_changes(vty, NULL);
+ }
+ return CMD_SUCCESS;
+}
+#endif /* KEEP_OLD_VPN_COMMANDS */
+
+DEFPY_YANG (set_ipx_vpn_nexthop,
+ set_ipx_vpn_nexthop_cmd,
+ "set <ipv4|ipv6> vpn next-hop <A.B.C.D$addrv4|X:X::X:X$addrv6>",
+ SET_STR
+ "IPv4 information\n"
+ "IPv6 information\n"
+ "VPN information\n"
+ "VPN next-hop address\n"
+ "IP address of next hop\n"
+ "IPv6 address of next hop\n")
+{
+ int idx_ip = 4;
+ afi_t afi;
+ int idx = 0;
+ char xpath_value[XPATH_MAXLEN];
+
+ if (argv_find_and_parse_afi(argv, argc, &idx, &afi)) {
+ if (afi == AFI_IP) {
+ if (addrv6_str) {
+ vty_out(vty, "%% IPv4 next-hop expected\n");
+ return CMD_WARNING_CONFIG_FAILED;
+ }
+
+ const char *xpath =
+ "./set-action[action='frr-bgp-route-map:ipv4-vpn-address']";
+
+ nb_cli_enqueue_change(vty, xpath, NB_OP_CREATE, NULL);
+ snprintf(
+ xpath_value, sizeof(xpath_value),
+ "%s/rmap-set-action/frr-bgp-route-map:ipv4-address",
+ xpath);
+ } else {
+ if (addrv4_str) {
+ vty_out(vty, "%% IPv6 next-hop expected\n");
+ return CMD_WARNING_CONFIG_FAILED;
+ }
+
+ const char *xpath =
+ "./set-action[action='frr-bgp-route-map:ipv6-vpn-address']";
+
+ nb_cli_enqueue_change(vty, xpath, NB_OP_CREATE, NULL);
+ snprintf(
+ xpath_value, sizeof(xpath_value),
+ "%s/rmap-set-action/frr-bgp-route-map:ipv6-address",
+ xpath);
+ }
+ nb_cli_enqueue_change(vty, xpath_value, NB_OP_MODIFY,
+ argv[idx_ip]->arg);
+ return nb_cli_apply_changes(vty, NULL);
+ }
+ return CMD_SUCCESS;
+}
+
+DEFUN_YANG (no_set_ipx_vpn_nexthop,
+ no_set_ipx_vpn_nexthop_cmd,
+ "no set <ipv4|ipv6> vpn next-hop [<A.B.C.D|X:X::X:X>]",
+ NO_STR
+ SET_STR
+ "IPv4 information\n"
+ "IPv6 information\n"
+ "VPN information\n"
+ "VPN next-hop address\n"
+ "IP address of next hop\n"
+ "IPv6 address of next hop\n")
+{
+ afi_t afi;
+ int idx = 0;
+
+ if (argv_find_and_parse_afi(argv, argc, &idx, &afi)) {
+ if (afi == AFI_IP) {
+ const char *xpath =
+ "./set-action[action='frr-bgp-route-map:ipv4-vpn-address']";
+ nb_cli_enqueue_change(vty, xpath, NB_OP_DESTROY, NULL);
+ } else {
+ const char *xpath =
+ "./set-action[action='frr-bgp-route-map:ipv6-vpn-address']";
+ nb_cli_enqueue_change(vty, xpath, NB_OP_DESTROY, NULL);
+ }
+ return nb_cli_apply_changes(vty, NULL);
+ }
+ return CMD_SUCCESS;
+}
+
+DEFUN_YANG (set_originator_id,
+ set_originator_id_cmd,
+ "set originator-id A.B.C.D",
+ SET_STR
+ "BGP originator ID attribute\n"
+ "IP address of originator\n")
+{
+ int idx_ipv4 = 2;
+ const char *xpath =
+ "./set-action[action='frr-bgp-route-map:originator-id']";
+ char xpath_value[XPATH_MAXLEN];
+
+ nb_cli_enqueue_change(vty, xpath, NB_OP_CREATE, NULL);
+ snprintf(xpath_value, sizeof(xpath_value),
+ "%s/rmap-set-action/frr-bgp-route-map:originator-id", xpath);
+ nb_cli_enqueue_change(vty, xpath_value, NB_OP_MODIFY,
+ argv[idx_ipv4]->arg);
+
+ return nb_cli_apply_changes(vty, NULL);
+}
+
+DEFUN_YANG (no_set_originator_id,
+ no_set_originator_id_cmd,
+ "no set originator-id [A.B.C.D]",
+ NO_STR
+ SET_STR
+ "BGP originator ID attribute\n"
+ "IP address of originator\n")
+{
+ const char *xpath =
+ "./set-action[action='frr-bgp-route-map:originator-id']";
+
+ nb_cli_enqueue_change(vty, xpath, NB_OP_DESTROY, NULL);
+ return nb_cli_apply_changes(vty, NULL);
+}
+
+DEFPY_YANG (match_rpki_extcommunity,
+ match_rpki_extcommunity_cmd,
+ "[no$no] match rpki-extcommunity <valid|invalid|notfound>",
+ NO_STR
+ MATCH_STR
+ "BGP RPKI (Origin Validation State) extended community attribute\n"
+ "Valid prefix\n"
+ "Invalid prefix\n"
+ "Prefix not found\n")
+{
+ const char *xpath =
+ "./match-condition[condition='frr-bgp-route-map:rpki-extcommunity']";
+ char xpath_value[XPATH_MAXLEN];
+
+ nb_cli_enqueue_change(vty, xpath, NB_OP_CREATE, NULL);
+
+ if (!no) {
+ snprintf(
+ xpath_value, sizeof(xpath_value),
+ "%s/rmap-match-condition/frr-bgp-route-map:rpki-extcommunity",
+ xpath);
+ nb_cli_enqueue_change(vty, xpath_value, NB_OP_MODIFY,
+ argv[2]->arg);
+ }
+
+ return nb_cli_apply_changes(vty, NULL);
+}
+
+/* Initialization of route map. */
+void bgp_route_map_init(void)
+{
+ route_map_init();
+
+ route_map_add_hook(bgp_route_map_add);
+ route_map_delete_hook(bgp_route_map_delete);
+ route_map_event_hook(bgp_route_map_event);
+
+ route_map_match_interface_hook(generic_match_add);
+ route_map_no_match_interface_hook(generic_match_delete);
+
+ route_map_match_ip_address_hook(generic_match_add);
+ route_map_no_match_ip_address_hook(generic_match_delete);
+
+ route_map_match_ip_address_prefix_list_hook(generic_match_add);
+ route_map_no_match_ip_address_prefix_list_hook(generic_match_delete);
+
+ route_map_match_ip_next_hop_hook(generic_match_add);
+ route_map_no_match_ip_next_hop_hook(generic_match_delete);
+
+ route_map_match_ipv6_next_hop_hook(generic_match_add);
+ route_map_no_match_ipv6_next_hop_hook(generic_match_delete);
+
+ route_map_match_ip_next_hop_prefix_list_hook(generic_match_add);
+ route_map_no_match_ip_next_hop_prefix_list_hook(generic_match_delete);
+
+ route_map_match_ip_next_hop_type_hook(generic_match_add);
+ route_map_no_match_ip_next_hop_type_hook(generic_match_delete);
+
+ route_map_match_ipv6_address_hook(generic_match_add);
+ route_map_no_match_ipv6_address_hook(generic_match_delete);
+
+ route_map_match_ipv6_address_prefix_list_hook(generic_match_add);
+ route_map_no_match_ipv6_address_prefix_list_hook(generic_match_delete);
+
+ route_map_match_ipv6_next_hop_type_hook(generic_match_add);
+ route_map_no_match_ipv6_next_hop_type_hook(generic_match_delete);
+
+ route_map_match_ipv6_next_hop_prefix_list_hook(generic_match_add);
+ route_map_no_match_ipv6_next_hop_prefix_list_hook(generic_match_delete);
+
+ route_map_match_metric_hook(generic_match_add);
+ route_map_no_match_metric_hook(generic_match_delete);
+
+ route_map_match_tag_hook(generic_match_add);
+ route_map_no_match_tag_hook(generic_match_delete);
+
+ route_map_set_srte_color_hook(generic_set_add);
+ route_map_no_set_srte_color_hook(generic_set_delete);
+
+ route_map_set_ip_nexthop_hook(generic_set_add);
+ route_map_no_set_ip_nexthop_hook(generic_set_delete);
+
+ route_map_set_ipv6_nexthop_local_hook(generic_set_add);
+ route_map_no_set_ipv6_nexthop_local_hook(generic_set_delete);
+
+ route_map_set_metric_hook(generic_set_add);
+ route_map_no_set_metric_hook(generic_set_delete);
+
+ route_map_set_tag_hook(generic_set_add);
+ route_map_no_set_tag_hook(generic_set_delete);
+
+ route_map_install_match(&route_match_peer_cmd);
+ route_map_install_match(&route_match_alias_cmd);
+ route_map_install_match(&route_match_local_pref_cmd);
+#ifdef HAVE_SCRIPTING
+ route_map_install_match(&route_match_script_cmd);
+#endif
+ route_map_install_match(&route_match_ip_address_cmd);
+ route_map_install_match(&route_match_ip_next_hop_cmd);
+ route_map_install_match(&route_match_ip_route_source_cmd);
+ route_map_install_match(&route_match_ip_address_prefix_list_cmd);
+ route_map_install_match(&route_match_ip_next_hop_prefix_list_cmd);
+ route_map_install_match(&route_match_ip_next_hop_type_cmd);
+ route_map_install_match(&route_match_ip_route_source_prefix_list_cmd);
+ route_map_install_match(&route_match_aspath_cmd);
+ route_map_install_match(&route_match_community_cmd);
+ route_map_install_match(&route_match_lcommunity_cmd);
+ route_map_install_match(&route_match_ecommunity_cmd);
+ route_map_install_match(&route_match_local_pref_cmd);
+ route_map_install_match(&route_match_metric_cmd);
+ route_map_install_match(&route_match_origin_cmd);
+ route_map_install_match(&route_match_probability_cmd);
+ route_map_install_match(&route_match_interface_cmd);
+ route_map_install_match(&route_match_tag_cmd);
+ route_map_install_match(&route_match_mac_address_cmd);
+ route_map_install_match(&route_match_evpn_vni_cmd);
+ route_map_install_match(&route_match_evpn_route_type_cmd);
+ route_map_install_match(&route_match_evpn_rd_cmd);
+ route_map_install_match(&route_match_evpn_default_route_cmd);
+ route_map_install_match(&route_match_vrl_source_vrf_cmd);
+
+ route_map_install_set(&route_set_evpn_gateway_ip_ipv4_cmd);
+ route_map_install_set(&route_set_evpn_gateway_ip_ipv6_cmd);
+ route_map_install_set(&route_set_table_id_cmd);
+ route_map_install_set(&route_set_srte_color_cmd);
+ route_map_install_set(&route_set_ip_nexthop_cmd);
+ route_map_install_set(&route_set_local_pref_cmd);
+ route_map_install_set(&route_set_weight_cmd);
+ route_map_install_set(&route_set_label_index_cmd);
+ route_map_install_set(&route_set_metric_cmd);
+ route_map_install_set(&route_set_distance_cmd);
+ route_map_install_set(&route_set_aspath_prepend_cmd);
+ route_map_install_set(&route_set_aspath_exclude_cmd);
+ route_map_install_set(&route_set_aspath_replace_cmd);
+ route_map_install_set(&route_set_origin_cmd);
+ route_map_install_set(&route_set_atomic_aggregate_cmd);
+ route_map_install_set(&route_set_aggregator_as_cmd);
+ route_map_install_set(&route_set_community_cmd);
+ route_map_install_set(&route_set_community_delete_cmd);
+ route_map_install_set(&route_set_lcommunity_cmd);
+ route_map_install_set(&route_set_lcommunity_delete_cmd);
+ route_map_install_set(&route_set_vpnv4_nexthop_cmd);
+ route_map_install_set(&route_set_vpnv6_nexthop_cmd);
+ route_map_install_set(&route_set_originator_id_cmd);
+ route_map_install_set(&route_set_ecommunity_rt_cmd);
+ route_map_install_set(&route_set_ecommunity_soo_cmd);
+ route_map_install_set(&route_set_ecommunity_lb_cmd);
+ route_map_install_set(&route_set_ecommunity_none_cmd);
+ route_map_install_set(&route_set_tag_cmd);
+ route_map_install_set(&route_set_label_index_cmd);
+ route_map_install_set(&route_set_l3vpn_nexthop_encapsulation_cmd);
+
+ install_element(RMAP_NODE, &match_peer_cmd);
+ install_element(RMAP_NODE, &match_peer_local_cmd);
+ install_element(RMAP_NODE, &no_match_peer_cmd);
+ install_element(RMAP_NODE, &match_ip_route_source_cmd);
+ install_element(RMAP_NODE, &no_match_ip_route_source_cmd);
+ install_element(RMAP_NODE, &match_ip_route_source_prefix_list_cmd);
+ install_element(RMAP_NODE, &no_match_ip_route_source_prefix_list_cmd);
+ install_element(RMAP_NODE, &match_mac_address_cmd);
+ install_element(RMAP_NODE, &no_match_mac_address_cmd);
+ install_element(RMAP_NODE, &match_evpn_vni_cmd);
+ install_element(RMAP_NODE, &no_match_evpn_vni_cmd);
+ install_element(RMAP_NODE, &match_evpn_route_type_cmd);
+ install_element(RMAP_NODE, &no_match_evpn_route_type_cmd);
+ install_element(RMAP_NODE, &match_evpn_rd_cmd);
+ install_element(RMAP_NODE, &no_match_evpn_rd_cmd);
+ install_element(RMAP_NODE, &match_evpn_default_route_cmd);
+ install_element(RMAP_NODE, &no_match_evpn_default_route_cmd);
+ install_element(RMAP_NODE, &set_evpn_gw_ip_ipv4_cmd);
+ install_element(RMAP_NODE, &no_set_evpn_gw_ip_ipv4_cmd);
+ install_element(RMAP_NODE, &set_evpn_gw_ip_ipv6_cmd);
+ install_element(RMAP_NODE, &no_set_evpn_gw_ip_ipv6_cmd);
+ install_element(RMAP_NODE, &match_vrl_source_vrf_cmd);
+ install_element(RMAP_NODE, &no_match_vrl_source_vrf_cmd);
+
+ install_element(RMAP_NODE, &match_aspath_cmd);
+ install_element(RMAP_NODE, &no_match_aspath_cmd);
+ install_element(RMAP_NODE, &match_local_pref_cmd);
+ install_element(RMAP_NODE, &no_match_local_pref_cmd);
+ install_element(RMAP_NODE, &match_alias_cmd);
+ install_element(RMAP_NODE, &no_match_alias_cmd);
+ install_element(RMAP_NODE, &match_community_cmd);
+ install_element(RMAP_NODE, &no_match_community_cmd);
+ install_element(RMAP_NODE, &match_lcommunity_cmd);
+ install_element(RMAP_NODE, &no_match_lcommunity_cmd);
+ install_element(RMAP_NODE, &match_ecommunity_cmd);
+ install_element(RMAP_NODE, &no_match_ecommunity_cmd);
+ install_element(RMAP_NODE, &match_origin_cmd);
+ install_element(RMAP_NODE, &no_match_origin_cmd);
+ install_element(RMAP_NODE, &match_probability_cmd);
+ install_element(RMAP_NODE, &no_match_probability_cmd);
+
+ install_element(RMAP_NODE, &no_set_table_id_cmd);
+ install_element(RMAP_NODE, &set_table_id_cmd);
+ install_element(RMAP_NODE, &set_ip_nexthop_peer_cmd);
+ install_element(RMAP_NODE, &set_ip_nexthop_unchanged_cmd);
+ install_element(RMAP_NODE, &set_local_pref_cmd);
+ install_element(RMAP_NODE, &set_distance_cmd);
+ install_element(RMAP_NODE, &no_set_distance_cmd);
+ install_element(RMAP_NODE, &no_set_local_pref_cmd);
+ install_element(RMAP_NODE, &set_weight_cmd);
+ install_element(RMAP_NODE, &set_label_index_cmd);
+ install_element(RMAP_NODE, &no_set_weight_cmd);
+ install_element(RMAP_NODE, &no_set_label_index_cmd);
+ install_element(RMAP_NODE, &set_aspath_prepend_asn_cmd);
+ install_element(RMAP_NODE, &set_aspath_prepend_lastas_cmd);
+ install_element(RMAP_NODE, &set_aspath_exclude_cmd);
+ install_element(RMAP_NODE, &set_aspath_replace_asn_cmd);
+ install_element(RMAP_NODE, &no_set_aspath_prepend_cmd);
+ install_element(RMAP_NODE, &no_set_aspath_prepend_lastas_cmd);
+ install_element(RMAP_NODE, &no_set_aspath_exclude_cmd);
+ install_element(RMAP_NODE, &no_set_aspath_exclude_all_cmd);
+ install_element(RMAP_NODE, &no_set_aspath_replace_asn_cmd);
+ install_element(RMAP_NODE, &set_origin_cmd);
+ install_element(RMAP_NODE, &no_set_origin_cmd);
+ install_element(RMAP_NODE, &set_atomic_aggregate_cmd);
+ install_element(RMAP_NODE, &no_set_atomic_aggregate_cmd);
+ install_element(RMAP_NODE, &set_aggregator_as_cmd);
+ install_element(RMAP_NODE, &no_set_aggregator_as_cmd);
+ install_element(RMAP_NODE, &set_community_cmd);
+ install_element(RMAP_NODE, &set_community_none_cmd);
+ install_element(RMAP_NODE, &no_set_community_cmd);
+ install_element(RMAP_NODE, &no_set_community_short_cmd);
+ install_element(RMAP_NODE, &set_community_delete_cmd);
+ install_element(RMAP_NODE, &no_set_community_delete_cmd);
+ install_element(RMAP_NODE, &set_lcommunity_cmd);
+ install_element(RMAP_NODE, &set_lcommunity_none_cmd);
+ install_element(RMAP_NODE, &no_set_lcommunity_cmd);
+ install_element(RMAP_NODE, &no_set_lcommunity1_cmd);
+ install_element(RMAP_NODE, &no_set_lcommunity1_short_cmd);
+ install_element(RMAP_NODE, &set_lcommunity_delete_cmd);
+ install_element(RMAP_NODE, &no_set_lcommunity_delete_cmd);
+ install_element(RMAP_NODE, &no_set_lcommunity_delete_short_cmd);
+ install_element(RMAP_NODE, &set_ecommunity_rt_cmd);
+ install_element(RMAP_NODE, &no_set_ecommunity_rt_cmd);
+ install_element(RMAP_NODE, &no_set_ecommunity_rt_short_cmd);
+ install_element(RMAP_NODE, &set_ecommunity_soo_cmd);
+ install_element(RMAP_NODE, &no_set_ecommunity_soo_cmd);
+ install_element(RMAP_NODE, &no_set_ecommunity_soo_short_cmd);
+ install_element(RMAP_NODE, &set_ecommunity_lb_cmd);
+ install_element(RMAP_NODE, &no_set_ecommunity_lb_cmd);
+ install_element(RMAP_NODE, &no_set_ecommunity_lb_short_cmd);
+ install_element(RMAP_NODE, &set_ecommunity_none_cmd);
+ install_element(RMAP_NODE, &no_set_ecommunity_none_cmd);
+#ifdef KEEP_OLD_VPN_COMMANDS
+ install_element(RMAP_NODE, &set_vpn_nexthop_cmd);
+ install_element(RMAP_NODE, &no_set_vpn_nexthop_cmd);
+#endif /* KEEP_OLD_VPN_COMMANDS */
+ install_element(RMAP_NODE, &set_ipx_vpn_nexthop_cmd);
+ install_element(RMAP_NODE, &no_set_ipx_vpn_nexthop_cmd);
+ install_element(RMAP_NODE, &set_originator_id_cmd);
+ install_element(RMAP_NODE, &no_set_originator_id_cmd);
+ install_element(RMAP_NODE, &set_l3vpn_nexthop_encapsulation_cmd);
+
+ route_map_install_match(&route_match_ipv6_address_cmd);
+ route_map_install_match(&route_match_ipv6_next_hop_cmd);
+ route_map_install_match(&route_match_ipv6_next_hop_address_cmd);
+ route_map_install_match(&route_match_ipv6_next_hop_prefix_list_cmd);
+ route_map_install_match(&route_match_ipv4_next_hop_cmd);
+ route_map_install_match(&route_match_ipv6_address_prefix_list_cmd);
+ route_map_install_match(&route_match_ipv6_next_hop_type_cmd);
+ route_map_install_set(&route_set_ipv6_nexthop_global_cmd);
+ route_map_install_set(&route_set_ipv6_nexthop_prefer_global_cmd);
+ route_map_install_set(&route_set_ipv6_nexthop_local_cmd);
+ route_map_install_set(&route_set_ipv6_nexthop_peer_cmd);
+ route_map_install_match(&route_match_rpki_extcommunity_cmd);
+
+ install_element(RMAP_NODE, &match_ipv6_next_hop_cmd);
+ install_element(RMAP_NODE, &match_ipv6_next_hop_address_cmd);
+ install_element(RMAP_NODE, &match_ipv6_next_hop_prefix_list_cmd);
+ install_element(RMAP_NODE, &no_match_ipv6_next_hop_cmd);
+ install_element(RMAP_NODE, &no_match_ipv6_next_hop_address_cmd);
+ install_element(RMAP_NODE, &no_match_ipv6_next_hop_prefix_list_cmd);
+ install_element(RMAP_NODE, &match_ipv6_next_hop_old_cmd);
+ install_element(RMAP_NODE, &no_match_ipv6_next_hop_old_cmd);
+ install_element(RMAP_NODE, &match_ipv4_next_hop_cmd);
+ install_element(RMAP_NODE, &no_match_ipv4_next_hop_cmd);
+ install_element(RMAP_NODE, &set_ipv6_nexthop_global_cmd);
+ install_element(RMAP_NODE, &no_set_ipv6_nexthop_global_cmd);
+ install_element(RMAP_NODE, &set_ipv6_nexthop_prefer_global_cmd);
+ install_element(RMAP_NODE, &no_set_ipv6_nexthop_prefer_global_cmd);
+ install_element(RMAP_NODE, &set_ipv6_nexthop_peer_cmd);
+ install_element(RMAP_NODE, &no_set_ipv6_nexthop_peer_cmd);
+ install_element(RMAP_NODE, &match_rpki_extcommunity_cmd);
+#ifdef HAVE_SCRIPTING
+ install_element(RMAP_NODE, &match_script_cmd);
+#endif
+}
+
+void bgp_route_map_terminate(void)
+{
+ /* ToDo: Cleanup all the used memory */
+ route_map_finish();
+}
diff --git a/bgpd/bgp_routemap_nb.c b/bgpd/bgp_routemap_nb.c
new file mode 100644
index 0000000..c47c37d
--- /dev/null
+++ b/bgpd/bgp_routemap_nb.c
@@ -0,0 +1,427 @@
+/*
+ * Copyright (C) 2020 Vmware
+ * Sarita Patra
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License as published by the Free
+ * Software Foundation; either version 2 of the License, or (at your option)
+ * any later version.
+ *
+ * This program is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
+ * more details.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program; see the file COPYING; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
+ */
+
+
+#include <zebra.h>
+
+#include "lib/command.h"
+#include "lib/log.h"
+#include "lib/northbound.h"
+#include "lib/routemap.h"
+#include "bgpd/bgpd.h"
+#include "bgpd/bgp_routemap_nb.h"
+
+/* clang-format off */
+const struct frr_yang_module_info frr_bgp_route_map_info = {
+ .name = "frr-bgp-route-map",
+ .nodes = {
+ {
+ .xpath = "/frr-route-map:lib/route-map/entry/match-condition/rmap-match-condition/frr-bgp-route-map:local-preference",
+ .cbs = {
+ .modify = lib_route_map_entry_match_condition_rmap_match_condition_local_preference_modify,
+ .destroy = lib_route_map_entry_match_condition_rmap_match_condition_local_preference_destroy,
+ }
+ },
+ {
+ .xpath = "/frr-route-map:lib/route-map/entry/match-condition/rmap-match-condition/frr-bgp-route-map:alias",
+ .cbs = {
+ .modify = lib_route_map_entry_match_condition_rmap_match_condition_alias_modify,
+ .destroy = lib_route_map_entry_match_condition_rmap_match_condition_alias_destroy,
+ }
+ },
+ {
+ .xpath = "/frr-route-map:lib/route-map/entry/match-condition/rmap-match-condition/frr-bgp-route-map:script",
+ .cbs = {
+ .modify = lib_route_map_entry_match_condition_rmap_match_condition_script_modify,
+ .destroy = lib_route_map_entry_match_condition_rmap_match_condition_script_destroy,
+ }
+ },
+ {
+ .xpath = "/frr-route-map:lib/route-map/entry/match-condition/rmap-match-condition/frr-bgp-route-map:origin",
+ .cbs = {
+ .modify = lib_route_map_entry_match_condition_rmap_match_condition_origin_modify,
+ .destroy = lib_route_map_entry_match_condition_rmap_match_condition_origin_destroy,
+ }
+ },
+ {
+ .xpath = "/frr-route-map:lib/route-map/entry/match-condition/rmap-match-condition/frr-bgp-route-map:rpki",
+ .cbs = {
+ .modify = lib_route_map_entry_match_condition_rmap_match_condition_rpki_modify,
+ .destroy = lib_route_map_entry_match_condition_rmap_match_condition_rpki_destroy,
+ }
+ },
+ {
+ .xpath = "/frr-route-map:lib/route-map/entry/match-condition/rmap-match-condition/frr-bgp-route-map:rpki-extcommunity",
+ .cbs = {
+ .modify = lib_route_map_entry_match_condition_rmap_match_condition_rpki_extcommunity_modify,
+ .destroy = lib_route_map_entry_match_condition_rmap_match_condition_rpki_extcommunity_destroy,
+ }
+ },
+ {
+ .xpath = "/frr-route-map:lib/route-map/entry/match-condition/rmap-match-condition/frr-bgp-route-map:probability",
+ .cbs = {
+ .modify = lib_route_map_entry_match_condition_rmap_match_condition_probability_modify,
+ .destroy = lib_route_map_entry_match_condition_rmap_match_condition_probability_destroy,
+ }
+ },
+ {
+ .xpath = "/frr-route-map:lib/route-map/entry/match-condition/rmap-match-condition/frr-bgp-route-map:source-vrf",
+ .cbs = {
+ .modify = lib_route_map_entry_match_condition_rmap_match_condition_source_vrf_modify,
+ .destroy = lib_route_map_entry_match_condition_rmap_match_condition_source_vrf_destroy,
+ }
+ },
+ {
+ .xpath = "/frr-route-map:lib/route-map/entry/match-condition/rmap-match-condition/frr-bgp-route-map:peer-ipv4-address",
+ .cbs = {
+ .modify = lib_route_map_entry_match_condition_rmap_match_condition_peer_ipv4_address_modify,
+ .destroy = lib_route_map_entry_match_condition_rmap_match_condition_peer_ipv4_address_destroy,
+ }
+ },
+ {
+ .xpath = "/frr-route-map:lib/route-map/entry/match-condition/rmap-match-condition/frr-bgp-route-map:peer-interface",
+ .cbs = {
+ .modify = lib_route_map_entry_match_condition_rmap_match_condition_peer_interface_modify,
+ .destroy = lib_route_map_entry_match_condition_rmap_match_condition_peer_interface_destroy,
+ }
+ },
+ {
+ .xpath = "/frr-route-map:lib/route-map/entry/match-condition/rmap-match-condition/frr-bgp-route-map:peer-ipv6-address",
+ .cbs = {
+ .modify = lib_route_map_entry_match_condition_rmap_match_condition_peer_ipv6_address_modify,
+ .destroy = lib_route_map_entry_match_condition_rmap_match_condition_peer_ipv6_address_destroy,
+ }
+ },
+ {
+ .xpath = "/frr-route-map:lib/route-map/entry/match-condition/rmap-match-condition/frr-bgp-route-map:peer-local",
+ .cbs = {
+ .modify = lib_route_map_entry_match_condition_rmap_match_condition_peer_local_modify,
+ .destroy = lib_route_map_entry_match_condition_rmap_match_condition_peer_local_destroy,
+ }
+ },
+ {
+ .xpath = "/frr-route-map:lib/route-map/entry/match-condition/rmap-match-condition/frr-bgp-route-map:list-name",
+ .cbs = {
+ .modify = lib_route_map_entry_match_condition_rmap_match_condition_list_name_modify,
+ .destroy = lib_route_map_entry_match_condition_rmap_match_condition_list_name_destroy,
+ }
+ },
+ {
+ .xpath = "/frr-route-map:lib/route-map/entry/match-condition/rmap-match-condition/frr-bgp-route-map:evpn-default-route",
+ .cbs = {
+ .create = lib_route_map_entry_match_condition_rmap_match_condition_evpn_default_route_create,
+ .destroy = lib_route_map_entry_match_condition_rmap_match_condition_evpn_default_route_destroy,
+ }
+ },
+ {
+ .xpath = "/frr-route-map:lib/route-map/entry/match-condition/rmap-match-condition/frr-bgp-route-map:evpn-vni",
+ .cbs = {
+ .modify = lib_route_map_entry_match_condition_rmap_match_condition_evpn_vni_modify,
+ .destroy = lib_route_map_entry_match_condition_rmap_match_condition_evpn_vni_destroy,
+ }
+ },
+ {
+ .xpath = "/frr-route-map:lib/route-map/entry/match-condition/rmap-match-condition/frr-bgp-route-map:evpn-route-type",
+ .cbs = {
+ .modify = lib_route_map_entry_match_condition_rmap_match_condition_evpn_route_type_modify,
+ .destroy = lib_route_map_entry_match_condition_rmap_match_condition_evpn_route_type_destroy,
+ }
+ },
+ {
+ .xpath = "/frr-route-map:lib/route-map/entry/match-condition/rmap-match-condition/frr-bgp-route-map:route-distinguisher",
+ .cbs = {
+ .modify = lib_route_map_entry_match_condition_rmap_match_condition_route_distinguisher_modify,
+ .destroy = lib_route_map_entry_match_condition_rmap_match_condition_route_distinguisher_destroy,
+ }
+ },
+ {
+ .xpath = "/frr-route-map:lib/route-map/entry/match-condition/rmap-match-condition/frr-bgp-route-map:comm-list",
+ .cbs = {
+ .apply_finish = lib_route_map_entry_match_condition_rmap_match_condition_comm_list_finish,
+ }
+ },
+ {
+ .xpath = "/frr-route-map:lib/route-map/entry/match-condition/rmap-match-condition/frr-bgp-route-map:comm-list/comm-list-name",
+ .cbs = {
+ .modify = lib_route_map_entry_match_condition_rmap_match_condition_comm_list_comm_list_name_modify,
+ .destroy = lib_route_map_entry_match_condition_rmap_match_condition_comm_list_comm_list_name_destroy,
+ }
+ },
+ {
+ .xpath = "/frr-route-map:lib/route-map/entry/match-condition/rmap-match-condition/frr-bgp-route-map:comm-list/comm-list-name-exact-match",
+ .cbs = {
+ .modify = lib_route_map_entry_match_condition_rmap_match_condition_comm_list_comm_list_name_exact_match_modify,
+ .destroy = lib_route_map_entry_match_condition_rmap_match_condition_comm_list_comm_list_name_exact_match_destroy,
+ }
+ },
+ {
+ .xpath = "/frr-route-map:lib/route-map/entry/match-condition/rmap-match-condition/frr-bgp-route-map:ipv4-address",
+ .cbs = {
+ .modify = lib_route_map_entry_match_condition_rmap_match_condition_ipv4_address_modify,
+ .destroy = lib_route_map_entry_match_condition_rmap_match_condition_ipv4_address_destroy,
+ }
+ },
+ {
+ .xpath = "/frr-route-map:lib/route-map/entry/match-condition/rmap-match-condition/frr-bgp-route-map:ipv6-address",
+ .cbs = {
+ .modify = lib_route_map_entry_match_condition_rmap_match_condition_ipv6_address_modify,
+ .destroy = lib_route_map_entry_match_condition_rmap_match_condition_ipv6_address_destroy,
+ }
+ },
+ {
+ .xpath = "/frr-route-map:lib/route-map/entry/set-action/rmap-set-action/frr-bgp-route-map:distance",
+ .cbs = {
+ .modify = lib_route_map_entry_set_action_rmap_set_action_distance_modify,
+ .destroy = lib_route_map_entry_set_action_rmap_set_action_distance_destroy,
+ }
+ },
+ {
+ .xpath = "/frr-route-map:lib/route-map/entry/set-action/rmap-set-action/frr-bgp-route-map:extcommunity-rt",
+ .cbs = {
+ .modify = lib_route_map_entry_set_action_rmap_set_action_extcommunity_rt_modify,
+ .destroy = lib_route_map_entry_set_action_rmap_set_action_extcommunity_rt_destroy,
+ }
+ },
+ {
+ .xpath = "/frr-route-map:lib/route-map/entry/set-action/rmap-set-action/frr-bgp-route-map:extcommunity-soo",
+ .cbs = {
+ .modify = lib_route_map_entry_set_action_rmap_set_action_extcommunity_soo_modify,
+ .destroy = lib_route_map_entry_set_action_rmap_set_action_extcommunity_soo_destroy,
+ }
+ },
+ {
+ .xpath = "/frr-route-map:lib/route-map/entry/set-action/rmap-set-action/frr-bgp-route-map:ipv4-address",
+ .cbs = {
+ .modify = lib_route_map_entry_set_action_rmap_set_action_ipv4_address_modify,
+ .destroy = lib_route_map_entry_set_action_rmap_set_action_ipv4_address_destroy,
+ }
+ },
+ {
+ .xpath = "/frr-route-map:lib/route-map/entry/set-action/rmap-set-action/frr-bgp-route-map:ipv4-nexthop",
+ .cbs = {
+ .modify = lib_route_map_entry_set_action_rmap_set_action_ipv4_nexthop_modify,
+ .destroy = lib_route_map_entry_set_action_rmap_set_action_ipv4_nexthop_destroy,
+ }
+ },
+ {
+ .xpath = "/frr-route-map:lib/route-map/entry/set-action/rmap-set-action/frr-bgp-route-map:ipv6-address",
+ .cbs = {
+ .modify = lib_route_map_entry_set_action_rmap_set_action_ipv6_address_modify,
+ .destroy = lib_route_map_entry_set_action_rmap_set_action_ipv6_address_destroy,
+ }
+ },
+ {
+ .xpath = "/frr-route-map:lib/route-map/entry/set-action/rmap-set-action/frr-bgp-route-map:preference",
+ .cbs = {
+ .modify = lib_route_map_entry_set_action_rmap_set_action_preference_modify,
+ .destroy = lib_route_map_entry_set_action_rmap_set_action_preference_destroy,
+ }
+ },
+ {
+ .xpath = "/frr-route-map:lib/route-map/entry/set-action/rmap-set-action/frr-bgp-route-map:label-index",
+ .cbs = {
+ .modify = lib_route_map_entry_set_action_rmap_set_action_label_index_modify,
+ .destroy = lib_route_map_entry_set_action_rmap_set_action_label_index_destroy,
+ }
+ },
+ {
+ .xpath = "/frr-route-map:lib/route-map/entry/set-action/rmap-set-action/frr-bgp-route-map:local-pref",
+ .cbs = {
+ .modify = lib_route_map_entry_set_action_rmap_set_action_local_pref_modify,
+ .destroy = lib_route_map_entry_set_action_rmap_set_action_local_pref_destroy,
+ }
+ },
+ {
+ .xpath = "/frr-route-map:lib/route-map/entry/set-action/rmap-set-action/frr-bgp-route-map:weight",
+ .cbs = {
+ .modify = lib_route_map_entry_set_action_rmap_set_action_weight_modify,
+ .destroy = lib_route_map_entry_set_action_rmap_set_action_weight_destroy,
+ }
+ },
+ {
+ .xpath = "/frr-route-map:lib/route-map/entry/set-action/rmap-set-action/frr-bgp-route-map:origin",
+ .cbs = {
+ .modify = lib_route_map_entry_set_action_rmap_set_action_origin_modify,
+ .destroy = lib_route_map_entry_set_action_rmap_set_action_origin_destroy,
+ }
+ },
+ {
+ .xpath = "/frr-route-map:lib/route-map/entry/set-action/rmap-set-action/frr-bgp-route-map:originator-id",
+ .cbs = {
+ .modify = lib_route_map_entry_set_action_rmap_set_action_originator_id_modify,
+ .destroy = lib_route_map_entry_set_action_rmap_set_action_originator_id_destroy,
+ }
+ },
+ {
+ .xpath = "/frr-route-map:lib/route-map/entry/set-action/rmap-set-action/frr-bgp-route-map:table",
+ .cbs = {
+ .modify = lib_route_map_entry_set_action_rmap_set_action_table_modify,
+ .destroy = lib_route_map_entry_set_action_rmap_set_action_table_destroy,
+ }
+ },
+ {
+ .xpath = "/frr-route-map:lib/route-map/entry/set-action/rmap-set-action/frr-bgp-route-map:atomic-aggregate",
+ .cbs = {
+ .create = lib_route_map_entry_set_action_rmap_set_action_atomic_aggregate_create,
+ .destroy = lib_route_map_entry_set_action_rmap_set_action_atomic_aggregate_destroy,
+ }
+ },
+ {
+ .xpath = "/frr-route-map:lib/route-map/entry/set-action/rmap-set-action/frr-bgp-route-map:prepend-as-path",
+ .cbs = {
+ .modify = lib_route_map_entry_set_action_rmap_set_action_prepend_as_path_modify,
+ .destroy = lib_route_map_entry_set_action_rmap_set_action_prepend_as_path_destroy,
+ }
+ },
+ {
+ .xpath = "/frr-route-map:lib/route-map/entry/set-action/rmap-set-action/frr-bgp-route-map:last-as",
+ .cbs = {
+ .modify = lib_route_map_entry_set_action_rmap_set_action_last_as_modify,
+ .destroy = lib_route_map_entry_set_action_rmap_set_action_last_as_destroy,
+ }
+ },
+ {
+ .xpath = "/frr-route-map:lib/route-map/entry/set-action/rmap-set-action/frr-bgp-route-map:exclude-as-path",
+ .cbs = {
+ .modify = lib_route_map_entry_set_action_rmap_set_action_exclude_as_path_modify,
+ .destroy = lib_route_map_entry_set_action_rmap_set_action_exclude_as_path_destroy,
+ }
+ },
+ {
+ .xpath = "/frr-route-map:lib/route-map/entry/set-action/rmap-set-action/frr-bgp-route-map:replace-as-path",
+ .cbs = {
+ .modify = lib_route_map_entry_set_action_rmap_set_action_replace_as_path_modify,
+ .destroy = lib_route_map_entry_set_action_rmap_set_action_replace_as_path_destroy,
+ }
+ },
+ {
+ .xpath = "/frr-route-map:lib/route-map/entry/set-action/rmap-set-action/frr-bgp-route-map:community-none",
+ .cbs = {
+ .modify = lib_route_map_entry_set_action_rmap_set_action_community_none_modify,
+ .destroy = lib_route_map_entry_set_action_rmap_set_action_community_none_destroy,
+ }
+ },
+ {
+ .xpath = "/frr-route-map:lib/route-map/entry/set-action/rmap-set-action/frr-bgp-route-map:community-string",
+ .cbs = {
+ .modify = lib_route_map_entry_set_action_rmap_set_action_community_string_modify,
+ .destroy = lib_route_map_entry_set_action_rmap_set_action_community_string_destroy,
+ }
+ },
+ {
+ .xpath = "/frr-route-map:lib/route-map/entry/set-action/rmap-set-action/frr-bgp-route-map:large-community-none",
+ .cbs = {
+ .modify = lib_route_map_entry_set_action_rmap_set_action_large_community_none_modify,
+ .destroy = lib_route_map_entry_set_action_rmap_set_action_large_community_none_destroy,
+ }
+ },
+ {
+ .xpath = "/frr-route-map:lib/route-map/entry/set-action/rmap-set-action/frr-bgp-route-map:large-community-string",
+ .cbs = {
+ .modify = lib_route_map_entry_set_action_rmap_set_action_large_community_string_modify,
+ .destroy = lib_route_map_entry_set_action_rmap_set_action_large_community_string_destroy,
+ }
+ },
+ {
+ .xpath = "/frr-route-map:lib/route-map/entry/set-action/rmap-set-action/frr-bgp-route-map:aggregator",
+ .cbs = {
+ .apply_finish = lib_route_map_entry_set_action_rmap_set_action_aggregator_finish,
+ }
+ },
+ {
+ .xpath = "/frr-route-map:lib/route-map/entry/set-action/rmap-set-action/frr-bgp-route-map:aggregator/aggregator-asn",
+ .cbs = {
+ .modify = lib_route_map_entry_set_action_rmap_set_action_aggregator_aggregator_asn_modify,
+ .destroy = lib_route_map_entry_set_action_rmap_set_action_aggregator_aggregator_asn_destroy,
+ }
+ },
+ {
+ .xpath = "/frr-route-map:lib/route-map/entry/set-action/rmap-set-action/frr-bgp-route-map:aggregator/aggregator-address",
+ .cbs = {
+ .modify = lib_route_map_entry_set_action_rmap_set_action_aggregator_aggregator_address_modify,
+ .destroy = lib_route_map_entry_set_action_rmap_set_action_aggregator_aggregator_address_destroy,
+ }
+ },
+ {
+ .xpath = "/frr-route-map:lib/route-map/entry/set-action/rmap-set-action/frr-bgp-route-map:comm-list-name",
+ .cbs = {
+ .modify = lib_route_map_entry_set_action_rmap_set_action_comm_list_name_modify,
+ .destroy = lib_route_map_entry_set_action_rmap_set_action_comm_list_name_destroy,
+ }
+ },
+ {
+ .xpath = "/frr-route-map:lib/route-map/entry/set-action/rmap-set-action/frr-bgp-route-map:extcommunity-none",
+ .cbs = {
+ .modify = lib_route_map_entry_set_action_rmap_set_action_extcommunity_none_modify,
+ .destroy = lib_route_map_entry_set_action_rmap_set_action_extcommunity_none_destroy,
+ }
+ },
+ {
+ .xpath = "/frr-route-map:lib/route-map/entry/set-action/rmap-set-action/frr-bgp-route-map:extcommunity-lb",
+ .cbs = {
+ .apply_finish = lib_route_map_entry_set_action_rmap_set_action_extcommunity_lb_finish,
+ }
+ },
+ {
+ .xpath = "/frr-route-map:lib/route-map/entry/set-action/rmap-set-action/frr-bgp-route-map:extcommunity-lb/lb-type",
+ .cbs = {
+ .modify = lib_route_map_entry_set_action_rmap_set_action_extcommunity_lb_lb_type_modify,
+ .destroy = lib_route_map_entry_set_action_rmap_set_action_extcommunity_lb_lb_type_destroy,
+ }
+ },
+ {
+ .xpath = "/frr-route-map:lib/route-map/entry/set-action/rmap-set-action/frr-bgp-route-map:extcommunity-lb/bandwidth",
+ .cbs = {
+ .modify = lib_route_map_entry_set_action_rmap_set_action_extcommunity_lb_bandwidth_modify,
+ .destroy = lib_route_map_entry_set_action_rmap_set_action_extcommunity_lb_bandwidth_destroy,
+ }
+ },
+ {
+ .xpath = "/frr-route-map:lib/route-map/entry/set-action/rmap-set-action/frr-bgp-route-map:extcommunity-lb/two-octet-as-specific",
+ .cbs = {
+ .modify = lib_route_map_entry_set_action_rmap_set_action_extcommunity_lb_two_octet_as_specific_modify,
+ .destroy = lib_route_map_entry_set_action_rmap_set_action_extcommunity_lb_two_octet_as_specific_destroy,
+ }
+ },
+ {
+ .xpath = "/frr-route-map:lib/route-map/entry/set-action/rmap-set-action/frr-bgp-route-map:evpn-gateway-ip-ipv4",
+ .cbs = {
+ .modify = lib_route_map_entry_set_action_rmap_set_action_evpn_gateway_ip_ipv4_modify,
+ .destroy = lib_route_map_entry_set_action_rmap_set_action_evpn_gateway_ip_ipv4_destroy,
+ }
+ },
+ {
+ .xpath = "/frr-route-map:lib/route-map/entry/set-action/rmap-set-action/frr-bgp-route-map:evpn-gateway-ip-ipv6",
+ .cbs = {
+ .modify = lib_route_map_entry_set_action_rmap_set_action_evpn_gateway_ip_ipv6_modify,
+ .destroy = lib_route_map_entry_set_action_rmap_set_action_evpn_gateway_ip_ipv6_destroy,
+ }
+ },
+ {
+ .xpath = "/frr-route-map:lib/route-map/entry/set-action/rmap-set-action/frr-bgp-route-map:l3vpn-nexthop-encapsulation",
+ .cbs = {
+ .modify = lib_route_map_entry_set_action_rmap_set_action_l3vpn_nexthop_encapsulation_modify,
+ .destroy = lib_route_map_entry_set_action_rmap_set_action_l3vpn_nexthop_encapsulation_destroy,
+ }
+ },
+ {
+ .xpath = NULL,
+ },
+ }
+};
diff --git a/bgpd/bgp_routemap_nb.h b/bgpd/bgp_routemap_nb.h
new file mode 100644
index 0000000..163e3b5
--- /dev/null
+++ b/bgpd/bgp_routemap_nb.h
@@ -0,0 +1,166 @@
+/*
+ * Copyright (C) 2020 Vmware
+ * Sarita Patra
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License as published by the Free
+ * Software Foundation; either version 2 of the License, or (at your option)
+ * any later version.
+ *
+ * This program is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
+ * more details.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program; see the file COPYING; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
+ */
+
+#ifndef _FRR_BGP_ROUTEMAP_NB_H_
+#define _FRR_BGP_ROUTEMAP_NB_H_
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+extern const struct frr_yang_module_info frr_bgp_route_map_info;
+
+/* prototypes */
+int lib_route_map_entry_match_condition_rmap_match_condition_local_preference_modify(struct nb_cb_modify_args *args);
+int lib_route_map_entry_match_condition_rmap_match_condition_local_preference_destroy(struct nb_cb_destroy_args *args);
+int lib_route_map_entry_match_condition_rmap_match_condition_alias_modify(
+ struct nb_cb_modify_args *args);
+int lib_route_map_entry_match_condition_rmap_match_condition_alias_destroy(
+ struct nb_cb_destroy_args *args);
+int lib_route_map_entry_match_condition_rmap_match_condition_script_modify(struct nb_cb_modify_args *args);
+int lib_route_map_entry_match_condition_rmap_match_condition_script_destroy(struct nb_cb_destroy_args *args);
+int lib_route_map_entry_match_condition_rmap_match_condition_origin_modify(struct nb_cb_modify_args *args);
+int lib_route_map_entry_match_condition_rmap_match_condition_origin_destroy(struct nb_cb_destroy_args *args);
+int lib_route_map_entry_match_condition_rmap_match_condition_rpki_modify(struct nb_cb_modify_args *args);
+int lib_route_map_entry_match_condition_rmap_match_condition_rpki_destroy(struct nb_cb_destroy_args *args);
+int lib_route_map_entry_match_condition_rmap_match_condition_rpki_extcommunity_modify(
+ struct nb_cb_modify_args *args);
+int lib_route_map_entry_match_condition_rmap_match_condition_rpki_extcommunity_destroy(
+ struct nb_cb_destroy_args *args);
+int lib_route_map_entry_match_condition_rmap_match_condition_probability_modify(struct nb_cb_modify_args *args);
+int lib_route_map_entry_match_condition_rmap_match_condition_probability_destroy(struct nb_cb_destroy_args *args);
+int lib_route_map_entry_match_condition_rmap_match_condition_source_vrf_modify(struct nb_cb_modify_args *args);
+int lib_route_map_entry_match_condition_rmap_match_condition_source_vrf_destroy(struct nb_cb_destroy_args *args);
+int lib_route_map_entry_match_condition_rmap_match_condition_peer_ipv4_address_modify(struct nb_cb_modify_args *args);
+int lib_route_map_entry_match_condition_rmap_match_condition_peer_ipv4_address_destroy(struct nb_cb_destroy_args *args);
+int lib_route_map_entry_match_condition_rmap_match_condition_peer_interface_modify(struct nb_cb_modify_args *args);
+int lib_route_map_entry_match_condition_rmap_match_condition_peer_interface_destroy(struct nb_cb_destroy_args *args);
+int lib_route_map_entry_match_condition_rmap_match_condition_peer_ipv6_address_modify(struct nb_cb_modify_args *args);
+int lib_route_map_entry_match_condition_rmap_match_condition_peer_ipv6_address_destroy(struct nb_cb_destroy_args *args);
+int lib_route_map_entry_match_condition_rmap_match_condition_peer_local_modify(struct nb_cb_modify_args *args);
+int lib_route_map_entry_match_condition_rmap_match_condition_peer_local_destroy(struct nb_cb_destroy_args *args);
+int lib_route_map_entry_match_condition_rmap_match_condition_access_list_num_modify(struct nb_cb_modify_args *args);
+int lib_route_map_entry_match_condition_rmap_match_condition_access_list_num_destroy(struct nb_cb_destroy_args *args);
+int lib_route_map_entry_match_condition_rmap_match_condition_access_list_num_extended_modify(struct nb_cb_modify_args *args);
+int lib_route_map_entry_match_condition_rmap_match_condition_access_list_num_extended_destroy(struct nb_cb_destroy_args *args);
+int lib_route_map_entry_match_condition_rmap_match_condition_list_name_modify(struct nb_cb_modify_args *args);
+int lib_route_map_entry_match_condition_rmap_match_condition_list_name_destroy(struct nb_cb_destroy_args *args);
+int lib_route_map_entry_match_condition_rmap_match_condition_evpn_default_route_create(struct nb_cb_create_args *args);
+int lib_route_map_entry_match_condition_rmap_match_condition_evpn_default_route_destroy(struct nb_cb_destroy_args *args);
+int lib_route_map_entry_match_condition_rmap_match_condition_evpn_vni_modify(struct nb_cb_modify_args *args);
+int lib_route_map_entry_match_condition_rmap_match_condition_evpn_vni_destroy(struct nb_cb_destroy_args *args);
+int lib_route_map_entry_match_condition_rmap_match_condition_evpn_route_type_modify(struct nb_cb_modify_args *args);
+int lib_route_map_entry_match_condition_rmap_match_condition_evpn_route_type_destroy(struct nb_cb_destroy_args *args);
+int lib_route_map_entry_match_condition_rmap_match_condition_route_distinguisher_modify(struct nb_cb_modify_args *args);
+int lib_route_map_entry_match_condition_rmap_match_condition_route_distinguisher_destroy(struct nb_cb_destroy_args *args);
+void lib_route_map_entry_match_condition_rmap_match_condition_comm_list_finish(struct nb_cb_apply_finish_args *args);
+int lib_route_map_entry_match_condition_rmap_match_condition_comm_list_comm_list_name_modify(struct nb_cb_modify_args *args);
+int lib_route_map_entry_match_condition_rmap_match_condition_comm_list_comm_list_name_destroy(struct nb_cb_destroy_args *args);
+int lib_route_map_entry_match_condition_rmap_match_condition_comm_list_comm_list_name_exact_match_modify(struct nb_cb_modify_args *args);
+int lib_route_map_entry_match_condition_rmap_match_condition_comm_list_comm_list_name_exact_match_destroy(struct nb_cb_destroy_args *args);
+int lib_route_map_entry_match_condition_rmap_match_condition_ipv4_address_modify(struct nb_cb_modify_args *args);
+int lib_route_map_entry_match_condition_rmap_match_condition_ipv4_address_destroy(struct nb_cb_destroy_args *args);
+int lib_route_map_entry_match_condition_rmap_match_condition_ipv6_address_modify(struct nb_cb_modify_args *args);
+int lib_route_map_entry_match_condition_rmap_match_condition_ipv6_address_destroy(struct nb_cb_destroy_args *args);
+int lib_route_map_entry_set_action_rmap_set_action_distance_modify(struct nb_cb_modify_args *args);
+int lib_route_map_entry_set_action_rmap_set_action_distance_destroy(struct nb_cb_destroy_args *args);
+int lib_route_map_entry_set_action_rmap_set_action_extcommunity_rt_modify(struct nb_cb_modify_args *args);
+int lib_route_map_entry_set_action_rmap_set_action_extcommunity_rt_destroy(struct nb_cb_destroy_args *args);
+int lib_route_map_entry_set_action_rmap_set_action_extcommunity_soo_modify(struct nb_cb_modify_args *args);
+int lib_route_map_entry_set_action_rmap_set_action_extcommunity_soo_destroy(struct nb_cb_destroy_args *args);
+int lib_route_map_entry_set_action_rmap_set_action_ipv4_address_modify(struct nb_cb_modify_args *args);
+int lib_route_map_entry_set_action_rmap_set_action_ipv4_address_destroy(struct nb_cb_destroy_args *args);
+int lib_route_map_entry_set_action_rmap_set_action_ipv4_nexthop_modify(struct nb_cb_modify_args *args);
+int lib_route_map_entry_set_action_rmap_set_action_ipv4_nexthop_destroy(struct nb_cb_destroy_args *args);
+int lib_route_map_entry_set_action_rmap_set_action_ipv6_address_modify(struct nb_cb_modify_args *args);
+int lib_route_map_entry_set_action_rmap_set_action_ipv6_address_destroy(struct nb_cb_destroy_args *args);
+int lib_route_map_entry_set_action_rmap_set_action_preference_modify(struct nb_cb_modify_args *args);
+int lib_route_map_entry_set_action_rmap_set_action_preference_destroy(struct nb_cb_destroy_args *args);
+int lib_route_map_entry_set_action_rmap_set_action_label_index_modify(struct nb_cb_modify_args *args);
+int lib_route_map_entry_set_action_rmap_set_action_label_index_destroy(struct nb_cb_destroy_args *args);
+int lib_route_map_entry_set_action_rmap_set_action_local_pref_modify(struct nb_cb_modify_args *args);
+int lib_route_map_entry_set_action_rmap_set_action_local_pref_destroy(struct nb_cb_destroy_args *args);
+int lib_route_map_entry_set_action_rmap_set_action_weight_modify(struct nb_cb_modify_args *args);
+int lib_route_map_entry_set_action_rmap_set_action_weight_destroy(struct nb_cb_destroy_args *args);
+int lib_route_map_entry_set_action_rmap_set_action_origin_modify(struct nb_cb_modify_args *args);
+int lib_route_map_entry_set_action_rmap_set_action_origin_destroy(struct nb_cb_destroy_args *args);
+int lib_route_map_entry_set_action_rmap_set_action_originator_id_modify(struct nb_cb_modify_args *args);
+int lib_route_map_entry_set_action_rmap_set_action_originator_id_destroy(struct nb_cb_destroy_args *args);
+int lib_route_map_entry_set_action_rmap_set_action_table_modify(struct nb_cb_modify_args *args);
+int lib_route_map_entry_set_action_rmap_set_action_table_destroy(struct nb_cb_destroy_args *args);
+int lib_route_map_entry_set_action_rmap_set_action_atomic_aggregate_create(struct nb_cb_create_args *args);
+int lib_route_map_entry_set_action_rmap_set_action_atomic_aggregate_destroy(struct nb_cb_destroy_args *args);
+int lib_route_map_entry_set_action_rmap_set_action_prepend_as_path_modify(struct nb_cb_modify_args *args);
+int lib_route_map_entry_set_action_rmap_set_action_prepend_as_path_destroy(struct nb_cb_destroy_args *args);
+int lib_route_map_entry_set_action_rmap_set_action_last_as_modify(struct nb_cb_modify_args *args);
+int lib_route_map_entry_set_action_rmap_set_action_last_as_destroy(struct nb_cb_destroy_args *args);
+int lib_route_map_entry_set_action_rmap_set_action_exclude_as_path_modify(struct nb_cb_modify_args *args);
+int lib_route_map_entry_set_action_rmap_set_action_exclude_as_path_destroy(struct nb_cb_destroy_args *args);
+int lib_route_map_entry_set_action_rmap_set_action_replace_as_path_modify(
+ struct nb_cb_modify_args *args);
+int lib_route_map_entry_set_action_rmap_set_action_replace_as_path_destroy(
+ struct nb_cb_destroy_args *args);
+int lib_route_map_entry_set_action_rmap_set_action_community_none_modify(struct nb_cb_modify_args *args);
+int lib_route_map_entry_set_action_rmap_set_action_community_none_destroy(struct nb_cb_destroy_args *args);
+int lib_route_map_entry_set_action_rmap_set_action_community_string_modify(struct nb_cb_modify_args *args);
+int lib_route_map_entry_set_action_rmap_set_action_community_string_destroy(struct nb_cb_destroy_args *args);
+int lib_route_map_entry_set_action_rmap_set_action_large_community_none_modify(struct nb_cb_modify_args *args);
+int lib_route_map_entry_set_action_rmap_set_action_large_community_none_destroy(struct nb_cb_destroy_args *args);
+int lib_route_map_entry_set_action_rmap_set_action_large_community_string_modify(struct nb_cb_modify_args *args);
+int lib_route_map_entry_set_action_rmap_set_action_large_community_string_destroy(struct nb_cb_destroy_args *args);
+void lib_route_map_entry_set_action_rmap_set_action_aggregator_finish(struct nb_cb_apply_finish_args *args);
+int lib_route_map_entry_set_action_rmap_set_action_aggregator_aggregator_asn_modify(struct nb_cb_modify_args *args);
+int lib_route_map_entry_set_action_rmap_set_action_aggregator_aggregator_asn_destroy(struct nb_cb_destroy_args *args);
+int lib_route_map_entry_set_action_rmap_set_action_aggregator_aggregator_address_modify(struct nb_cb_modify_args *args);
+int lib_route_map_entry_set_action_rmap_set_action_aggregator_aggregator_address_destroy(struct nb_cb_destroy_args *args);
+int lib_route_map_entry_set_action_rmap_set_action_comm_list_num_modify(struct nb_cb_modify_args *args);
+int lib_route_map_entry_set_action_rmap_set_action_comm_list_num_destroy(struct nb_cb_destroy_args *args);
+int lib_route_map_entry_set_action_rmap_set_action_comm_list_num_extended_modify(struct nb_cb_modify_args *args);
+int lib_route_map_entry_set_action_rmap_set_action_comm_list_num_extended_destroy(struct nb_cb_destroy_args *args);
+int lib_route_map_entry_set_action_rmap_set_action_comm_list_name_modify(struct nb_cb_modify_args *args);
+int lib_route_map_entry_set_action_rmap_set_action_comm_list_name_destroy(struct nb_cb_destroy_args *args);
+void lib_route_map_entry_set_action_rmap_set_action_extcommunity_lb_finish(struct nb_cb_apply_finish_args *args);
+int lib_route_map_entry_set_action_rmap_set_action_extcommunity_lb_lb_type_modify(struct nb_cb_modify_args *args);
+int lib_route_map_entry_set_action_rmap_set_action_extcommunity_lb_lb_type_destroy(struct nb_cb_destroy_args *args);
+int lib_route_map_entry_set_action_rmap_set_action_extcommunity_lb_bandwidth_modify(struct nb_cb_modify_args *args);
+int lib_route_map_entry_set_action_rmap_set_action_extcommunity_lb_bandwidth_destroy(struct nb_cb_destroy_args *args);
+int lib_route_map_entry_set_action_rmap_set_action_extcommunity_lb_two_octet_as_specific_modify(struct nb_cb_modify_args *args);
+int lib_route_map_entry_set_action_rmap_set_action_extcommunity_lb_two_octet_as_specific_destroy(struct nb_cb_destroy_args *args);
+int lib_route_map_entry_set_action_rmap_set_action_extcommunity_none_modify(
+ struct nb_cb_modify_args *args);
+int lib_route_map_entry_set_action_rmap_set_action_extcommunity_none_destroy(
+ struct nb_cb_destroy_args *args);
+int lib_route_map_entry_set_action_rmap_set_action_evpn_gateway_ip_ipv4_modify(
+ struct nb_cb_modify_args *args);
+int lib_route_map_entry_set_action_rmap_set_action_evpn_gateway_ip_ipv4_destroy(
+ struct nb_cb_destroy_args *args);
+int lib_route_map_entry_set_action_rmap_set_action_evpn_gateway_ip_ipv6_modify(
+ struct nb_cb_modify_args *args);
+int lib_route_map_entry_set_action_rmap_set_action_evpn_gateway_ip_ipv6_destroy(
+ struct nb_cb_destroy_args *args);
+int lib_route_map_entry_set_action_rmap_set_action_l3vpn_nexthop_encapsulation_modify(
+ struct nb_cb_modify_args *args);
+int lib_route_map_entry_set_action_rmap_set_action_l3vpn_nexthop_encapsulation_destroy(
+ struct nb_cb_destroy_args *args);
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif
diff --git a/bgpd/bgp_routemap_nb_config.c b/bgpd/bgp_routemap_nb_config.c
new file mode 100644
index 0000000..b18cf9d
--- /dev/null
+++ b/bgpd/bgp_routemap_nb_config.c
@@ -0,0 +1,3031 @@
+/*
+ * Copyright (C) 2020 Vmware
+ * Sarita Patra
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License as published by the Free
+ * Software Foundation; either version 2 of the License, or (at your option)
+ * any later version.
+ *
+ * This program is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
+ * more details.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program; see the file COPYING; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
+ */
+
+#include <zebra.h>
+
+#include "lib/command.h"
+#include "lib/log.h"
+#include "lib/northbound.h"
+#include "lib/routemap.h"
+#include "bgpd/bgpd.h"
+#include "bgpd/bgp_routemap_nb.h"
+
+/* Add bgp route map rule. */
+static int bgp_route_match_add(struct route_map_index *index,
+ const char *command, const char *arg,
+ route_map_event_t type,
+ char *errmsg, size_t errmsg_len)
+{
+ int retval = CMD_SUCCESS;
+ enum rmap_compile_rets ret;
+
+ ret = route_map_add_match(index, command, arg, type);
+ switch (ret) {
+ case RMAP_RULE_MISSING:
+ snprintf(errmsg, errmsg_len, "%% BGP Can't find rule.");
+ retval = CMD_WARNING_CONFIG_FAILED;
+ break;
+ case RMAP_COMPILE_ERROR:
+ snprintf(errmsg, errmsg_len, "%% BGP Argument is malformed.");
+ retval = CMD_WARNING_CONFIG_FAILED;
+ break;
+ case RMAP_COMPILE_SUCCESS:
+ /*
+ * Intentionally doing nothing here.
+ */
+ break;
+ }
+
+ return retval;
+}
+
+/* Delete bgp route map rule. */
+static int bgp_route_match_delete(struct route_map_index *index,
+ const char *command, const char *arg,
+ route_map_event_t type,
+ char *errmsg, size_t errmsg_len)
+{
+ enum rmap_compile_rets ret;
+ int retval = CMD_SUCCESS;
+ char *dep_name = NULL;
+ const char *tmpstr;
+ char *rmap_name = NULL;
+
+ if (type != RMAP_EVENT_MATCH_DELETED) {
+ /* ignore the mundane, the types without any dependency */
+ if (arg == NULL) {
+ tmpstr = route_map_get_match_arg(index, command);
+ if (tmpstr != NULL)
+ dep_name =
+ XSTRDUP(MTYPE_ROUTE_MAP_RULE, tmpstr);
+ } else {
+ dep_name = XSTRDUP(MTYPE_ROUTE_MAP_RULE, arg);
+ }
+ rmap_name = XSTRDUP(MTYPE_ROUTE_MAP_NAME, index->map->name);
+ }
+
+ ret = route_map_delete_match(index, command, dep_name, type);
+ switch (ret) {
+ case RMAP_RULE_MISSING:
+ snprintf(errmsg, errmsg_len, "%% BGP Can't find rule.");
+ retval = CMD_WARNING_CONFIG_FAILED;
+ break;
+ case RMAP_COMPILE_ERROR:
+ snprintf(errmsg, errmsg_len,
+ "%% BGP Argument is malformed.");
+ retval = CMD_WARNING_CONFIG_FAILED;
+ break;
+ case RMAP_COMPILE_SUCCESS:
+ /*
+ * Nothing to do here
+ */
+ break;
+ }
+
+ XFREE(MTYPE_ROUTE_MAP_RULE, dep_name);
+ XFREE(MTYPE_ROUTE_MAP_NAME, rmap_name);
+
+ return retval;
+}
+
+/*
+ * XPath: /frr-route-map:lib/route-map/entry/match-condition/rmap-match-condition/frr-bgp-route-map:local-preference
+ */
+int
+lib_route_map_entry_match_condition_rmap_match_condition_local_preference_modify(
+ struct nb_cb_modify_args *args)
+{
+ struct routemap_hook_context *rhc;
+ const char *local_pref;
+ enum rmap_compile_rets ret;
+
+ switch (args->event) {
+ case NB_EV_VALIDATE:
+ case NB_EV_PREPARE:
+ case NB_EV_ABORT:
+ break;
+ case NB_EV_APPLY:
+ /* Add configuration. */
+ rhc = nb_running_get_entry(args->dnode, NULL, true);
+ local_pref = yang_dnode_get_string(args->dnode, NULL);
+
+ /* Set destroy information. */
+ rhc->rhc_mhook = bgp_route_match_delete;
+ rhc->rhc_rule = "local-preference";
+ rhc->rhc_event = RMAP_EVENT_MATCH_DELETED;
+
+ ret = bgp_route_match_add(rhc->rhc_rmi, "local-preference",
+ local_pref, RMAP_EVENT_MATCH_ADDED,
+ args->errmsg, args->errmsg_len);
+
+ if (ret != RMAP_COMPILE_SUCCESS) {
+ rhc->rhc_mhook = NULL;
+ return NB_ERR_INCONSISTENCY;
+ }
+ }
+
+ return NB_OK;
+}
+
+int
+lib_route_map_entry_match_condition_rmap_match_condition_local_preference_destroy(
+ struct nb_cb_destroy_args *args)
+{
+ switch (args->event) {
+ case NB_EV_VALIDATE:
+ case NB_EV_PREPARE:
+ case NB_EV_ABORT:
+ break;
+ case NB_EV_APPLY:
+ return lib_route_map_entry_match_destroy(args);
+ }
+
+ return NB_OK;
+}
+
+/*
+ * XPath:
+ * /frr-route-map:lib/route-map/entry/match-condition/rmap-match-condition/frr-bgp-route-map:alias
+ */
+int lib_route_map_entry_match_condition_rmap_match_condition_alias_modify(
+ struct nb_cb_modify_args *args)
+{
+ struct routemap_hook_context *rhc;
+ const char *alias;
+ enum rmap_compile_rets ret;
+
+ switch (args->event) {
+ case NB_EV_VALIDATE:
+ case NB_EV_PREPARE:
+ case NB_EV_ABORT:
+ break;
+ case NB_EV_APPLY:
+ /* Add configuration. */
+ rhc = nb_running_get_entry(args->dnode, NULL, true);
+ alias = yang_dnode_get_string(args->dnode, NULL);
+
+ /* Set destroy information. */
+ rhc->rhc_mhook = bgp_route_match_delete;
+ rhc->rhc_rule = "alias";
+ rhc->rhc_event = RMAP_EVENT_MATCH_DELETED;
+
+ ret = bgp_route_match_add(rhc->rhc_rmi, "alias", alias,
+ RMAP_EVENT_MATCH_ADDED, args->errmsg,
+ args->errmsg_len);
+
+ if (ret != RMAP_COMPILE_SUCCESS) {
+ rhc->rhc_mhook = NULL;
+ return NB_ERR_VALIDATION;
+ }
+
+ break;
+ }
+
+ return NB_OK;
+}
+
+int lib_route_map_entry_match_condition_rmap_match_condition_alias_destroy(
+ struct nb_cb_destroy_args *args)
+{
+ switch (args->event) {
+ case NB_EV_VALIDATE:
+ case NB_EV_PREPARE:
+ case NB_EV_ABORT:
+ break;
+ case NB_EV_APPLY:
+ return lib_route_map_entry_match_destroy(args);
+ }
+
+ return NB_OK;
+}
+
+/*
+ * XPath: /frr-route-map:lib/route-map/entry/match-condition/rmap-match-condition/frr-bgp-route-map:script
+ */
+int
+lib_route_map_entry_match_condition_rmap_match_condition_script_modify(
+ struct nb_cb_modify_args *args)
+{
+ struct routemap_hook_context *rhc;
+ const char *script;
+ enum rmap_compile_rets ret;
+
+ switch (args->event) {
+ case NB_EV_VALIDATE:
+ case NB_EV_PREPARE:
+ case NB_EV_ABORT:
+ break;
+ case NB_EV_APPLY:
+ /* Add configuration. */
+ rhc = nb_running_get_entry(args->dnode, NULL, true);
+ script = yang_dnode_get_string(args->dnode, NULL);
+
+ /* Set destroy information. */
+ rhc->rhc_mhook = bgp_route_match_delete;
+ rhc->rhc_rule = "script";
+ rhc->rhc_event = RMAP_EVENT_MATCH_DELETED;
+
+ ret = bgp_route_match_add(rhc->rhc_rmi, "script",
+ script, RMAP_EVENT_MATCH_ADDED,
+ args->errmsg, args->errmsg_len);
+
+ if (ret != RMAP_COMPILE_SUCCESS) {
+ rhc->rhc_mhook = NULL;
+ return NB_ERR_INCONSISTENCY;
+ }
+ }
+
+ return NB_OK;
+}
+
+int
+lib_route_map_entry_match_condition_rmap_match_condition_script_destroy(
+ struct nb_cb_destroy_args *args)
+{
+ switch (args->event) {
+ case NB_EV_VALIDATE:
+ case NB_EV_PREPARE:
+ case NB_EV_ABORT:
+ break;
+ case NB_EV_APPLY:
+ return lib_route_map_entry_match_destroy(args);
+ }
+
+ return NB_OK;
+}
+
+/*
+ * XPath: /frr-route-map:lib/route-map/entry/match-condition/rmap-match-condition/frr-bgp-route-map:origin
+ */
+int
+lib_route_map_entry_match_condition_rmap_match_condition_origin_modify(
+ struct nb_cb_modify_args *args)
+{
+ struct routemap_hook_context *rhc;
+ const char *origin;
+ enum rmap_compile_rets ret;
+
+ switch (args->event) {
+ case NB_EV_VALIDATE:
+ case NB_EV_PREPARE:
+ case NB_EV_ABORT:
+ break;
+ case NB_EV_APPLY:
+ /* Add configuration. */
+ rhc = nb_running_get_entry(args->dnode, NULL, true);
+ origin = yang_dnode_get_string(args->dnode, NULL);
+
+ /* Set destroy information. */
+ rhc->rhc_mhook = bgp_route_match_delete;
+ rhc->rhc_rule = "origin";
+ rhc->rhc_event = RMAP_EVENT_MATCH_DELETED;
+
+ ret = bgp_route_match_add(rhc->rhc_rmi, "origin", origin,
+ RMAP_EVENT_MATCH_ADDED,
+ args->errmsg, args->errmsg_len);
+
+ if (ret != RMAP_COMPILE_SUCCESS) {
+ rhc->rhc_mhook = NULL;
+ return NB_ERR_INCONSISTENCY;
+ }
+ }
+
+ return NB_OK;
+}
+
+int
+lib_route_map_entry_match_condition_rmap_match_condition_origin_destroy(
+ struct nb_cb_destroy_args *args)
+{
+ switch (args->event) {
+ case NB_EV_VALIDATE:
+ case NB_EV_PREPARE:
+ case NB_EV_ABORT:
+ break;
+ case NB_EV_APPLY:
+ return lib_route_map_entry_match_destroy(args);
+ }
+
+ return NB_OK;
+}
+
+/*
+ * XPath: /frr-route-map:lib/route-map/entry/match-condition/rmap-match-condition/frr-bgp-route-map:rpki
+ */
+int
+lib_route_map_entry_match_condition_rmap_match_condition_rpki_modify(
+ struct nb_cb_modify_args *args)
+{
+ struct routemap_hook_context *rhc;
+ const char *rpki;
+ enum rmap_compile_rets ret;
+
+ switch (args->event) {
+ case NB_EV_VALIDATE:
+ case NB_EV_PREPARE:
+ case NB_EV_ABORT:
+ break;
+ case NB_EV_APPLY:
+ /* Add configuration. */
+ rhc = nb_running_get_entry(args->dnode, NULL, true);
+ rpki = yang_dnode_get_string(args->dnode, NULL);
+
+ /* Set destroy information. */
+ rhc->rhc_mhook = bgp_route_match_delete;
+ rhc->rhc_rule = "rpki";
+ rhc->rhc_event = RMAP_EVENT_MATCH_DELETED;
+
+ ret = bgp_route_match_add(rhc->rhc_rmi, "rpki", rpki,
+ RMAP_EVENT_MATCH_ADDED,
+ args->errmsg, args->errmsg_len);
+
+ if (ret != RMAP_COMPILE_SUCCESS) {
+ rhc->rhc_mhook = NULL;
+ return NB_ERR_INCONSISTENCY;
+ }
+ }
+
+ return NB_OK;
+}
+
+int
+lib_route_map_entry_match_condition_rmap_match_condition_rpki_destroy(
+ struct nb_cb_destroy_args *args)
+{
+ switch (args->event) {
+ case NB_EV_VALIDATE:
+ case NB_EV_PREPARE:
+ case NB_EV_ABORT:
+ break;
+ case NB_EV_APPLY:
+ return lib_route_map_entry_match_destroy(args);
+ }
+
+ return NB_OK;
+}
+
+/*
+ * XPath:
+ * /frr-route-map:lib/route-map/entry/match-condition/rmap-match-condition/frr-bgp-route-map:rpki-extcommunity
+ */
+int lib_route_map_entry_match_condition_rmap_match_condition_rpki_extcommunity_modify(
+ struct nb_cb_modify_args *args)
+{
+ struct routemap_hook_context *rhc;
+ const char *rpki;
+ enum rmap_compile_rets ret;
+
+ switch (args->event) {
+ case NB_EV_VALIDATE:
+ case NB_EV_PREPARE:
+ case NB_EV_ABORT:
+ break;
+ case NB_EV_APPLY:
+ /* Add configuration. */
+ rhc = nb_running_get_entry(args->dnode, NULL, true);
+ rpki = yang_dnode_get_string(args->dnode, NULL);
+
+ /* Set destroy information. */
+ rhc->rhc_mhook = bgp_route_match_delete;
+ rhc->rhc_rule = "rpki-extcommunity";
+ rhc->rhc_event = RMAP_EVENT_MATCH_DELETED;
+
+ ret = bgp_route_match_add(rhc->rhc_rmi, "rpki-extcommunity",
+ rpki, RMAP_EVENT_MATCH_ADDED,
+ args->errmsg, args->errmsg_len);
+
+ if (ret != RMAP_COMPILE_SUCCESS) {
+ rhc->rhc_mhook = NULL;
+ return NB_ERR_INCONSISTENCY;
+ }
+ }
+
+ return NB_OK;
+}
+
+int lib_route_map_entry_match_condition_rmap_match_condition_rpki_extcommunity_destroy(
+ struct nb_cb_destroy_args *args)
+{
+ switch (args->event) {
+ case NB_EV_VALIDATE:
+ case NB_EV_PREPARE:
+ case NB_EV_ABORT:
+ break;
+ case NB_EV_APPLY:
+ return lib_route_map_entry_match_destroy(args);
+ }
+
+ return NB_OK;
+}
+
+/*
+ * XPath: /frr-route-map:lib/route-map/entry/match-condition/rmap-match-condition/frr-bgp-route-map:probability
+ */
+int
+lib_route_map_entry_match_condition_rmap_match_condition_probability_modify(
+ struct nb_cb_modify_args *args)
+{
+ struct routemap_hook_context *rhc;
+ const char *probability;
+ enum rmap_compile_rets ret;
+
+ switch (args->event) {
+ case NB_EV_VALIDATE:
+ case NB_EV_PREPARE:
+ case NB_EV_ABORT:
+ break;
+ case NB_EV_APPLY:
+ /* Add configuration. */
+ rhc = nb_running_get_entry(args->dnode, NULL, true);
+ probability = yang_dnode_get_string(args->dnode, NULL);
+
+ /* Set destroy information. */
+ rhc->rhc_mhook = bgp_route_match_delete;
+ rhc->rhc_rule = "probability";
+ rhc->rhc_event = RMAP_EVENT_MATCH_DELETED;
+
+ ret = bgp_route_match_add(rhc->rhc_rmi, "probability",
+ probability, RMAP_EVENT_MATCH_ADDED,
+ args->errmsg, args->errmsg_len);
+
+ if (ret != RMAP_COMPILE_SUCCESS) {
+ rhc->rhc_mhook = NULL;
+ return NB_ERR_INCONSISTENCY;
+ }
+ }
+
+ return NB_OK;
+}
+
+int
+lib_route_map_entry_match_condition_rmap_match_condition_probability_destroy(
+ struct nb_cb_destroy_args *args)
+{
+ switch (args->event) {
+ case NB_EV_VALIDATE:
+ case NB_EV_PREPARE:
+ case NB_EV_ABORT:
+ case NB_EV_APPLY:
+ return lib_route_map_entry_match_destroy(args);
+ }
+
+ return NB_OK;
+}
+
+/*
+ * XPath: /frr-route-map:lib/route-map/entry/match-condition/rmap-match-condition/frr-bgp-route-map:source-vrf
+ */
+int
+lib_route_map_entry_match_condition_rmap_match_condition_source_vrf_modify(
+ struct nb_cb_modify_args *args)
+{
+ struct routemap_hook_context *rhc;
+ const char *vrf;
+ enum rmap_compile_rets ret;
+
+ switch (args->event) {
+ case NB_EV_VALIDATE:
+ case NB_EV_PREPARE:
+ case NB_EV_ABORT:
+ break;
+ case NB_EV_APPLY:
+ /* Add configuration. */
+ rhc = nb_running_get_entry(args->dnode, NULL, true);
+ vrf = yang_dnode_get_string(args->dnode, NULL);
+
+ /* Set destroy information. */
+ rhc->rhc_mhook = bgp_route_match_delete;
+ rhc->rhc_rule = "source-vrf";
+ rhc->rhc_event = RMAP_EVENT_MATCH_DELETED;
+
+ ret = bgp_route_match_add(rhc->rhc_rmi, "source-vrf", vrf,
+ RMAP_EVENT_MATCH_ADDED,
+ args->errmsg, args->errmsg_len);
+
+ if (ret != RMAP_COMPILE_SUCCESS) {
+ rhc->rhc_mhook = NULL;
+ return NB_ERR_INCONSISTENCY;
+ }
+ }
+
+ return NB_OK;
+}
+
+int
+lib_route_map_entry_match_condition_rmap_match_condition_source_vrf_destroy(
+ struct nb_cb_destroy_args *args)
+{
+ switch (args->event) {
+ case NB_EV_VALIDATE:
+ case NB_EV_PREPARE:
+ case NB_EV_ABORT:
+ break;
+ case NB_EV_APPLY:
+ return lib_route_map_entry_match_destroy(args);
+ }
+
+ return NB_OK;
+}
+
+/*
+ * XPath: /frr-route-map:lib/route-map/entry/match-condition/rmap-match-condition/frr-bgp-route-map:peer-ipv4-address
+ */
+int
+lib_route_map_entry_match_condition_rmap_match_condition_peer_ipv4_address_modify(
+ struct nb_cb_modify_args *args)
+{
+ struct routemap_hook_context *rhc;
+ const char *peer;
+ enum rmap_compile_rets ret;
+
+ switch (args->event) {
+ case NB_EV_VALIDATE:
+ case NB_EV_PREPARE:
+ case NB_EV_ABORT:
+ break;
+ case NB_EV_APPLY:
+ /* Add configuration. */
+ rhc = nb_running_get_entry(args->dnode, NULL, true);
+ peer = yang_dnode_get_string(args->dnode, NULL);
+
+ /* Set destroy information. */
+ rhc->rhc_mhook = bgp_route_match_delete;
+ rhc->rhc_rule = "peer";
+ rhc->rhc_event = RMAP_EVENT_MATCH_DELETED;
+
+ ret = bgp_route_match_add(rhc->rhc_rmi, "peer", peer,
+ RMAP_EVENT_MATCH_ADDED,
+ args->errmsg, args->errmsg_len);
+
+ if (ret != RMAP_COMPILE_SUCCESS) {
+ rhc->rhc_mhook = NULL;
+ return NB_ERR_INCONSISTENCY;
+ }
+ }
+
+ return NB_OK;
+}
+
+int
+lib_route_map_entry_match_condition_rmap_match_condition_peer_ipv4_address_destroy(
+ struct nb_cb_destroy_args *args)
+{
+ switch (args->event) {
+ case NB_EV_VALIDATE:
+ case NB_EV_PREPARE:
+ case NB_EV_ABORT:
+ break;
+ case NB_EV_APPLY:
+ return lib_route_map_entry_match_destroy(args);
+ }
+
+ return NB_OK;
+}
+
+/*
+ * XPath: /frr-route-map:lib/route-map/entry/match-condition/rmap-match-condition/frr-bgp-route-map:peer-interface
+ */
+int
+lib_route_map_entry_match_condition_rmap_match_condition_peer_interface_modify(
+ struct nb_cb_modify_args *args)
+{
+ struct routemap_hook_context *rhc;
+ const char *peer;
+ enum rmap_compile_rets ret;
+
+ switch (args->event) {
+ case NB_EV_VALIDATE:
+ case NB_EV_PREPARE:
+ case NB_EV_ABORT:
+ break;
+ case NB_EV_APPLY:
+ /* Add configuration. */
+ rhc = nb_running_get_entry(args->dnode, NULL, true);
+ peer = yang_dnode_get_string(args->dnode, NULL);
+
+ /* Set destroy information. */
+ rhc->rhc_mhook = bgp_route_match_delete;
+ rhc->rhc_rule = "peer";
+ rhc->rhc_event = RMAP_EVENT_MATCH_DELETED;
+
+ ret = bgp_route_match_add(rhc->rhc_rmi, "peer", peer,
+ RMAP_EVENT_MATCH_ADDED,
+ args->errmsg, args->errmsg_len);
+
+ if (ret != RMAP_COMPILE_SUCCESS) {
+ rhc->rhc_mhook = NULL;
+ return NB_ERR_INCONSISTENCY;
+ }
+ }
+
+ return NB_OK;
+}
+
+int
+lib_route_map_entry_match_condition_rmap_match_condition_peer_interface_destroy(
+ struct nb_cb_destroy_args *args)
+{
+ switch (args->event) {
+ case NB_EV_VALIDATE:
+ case NB_EV_PREPARE:
+ case NB_EV_ABORT:
+ break;
+ case NB_EV_APPLY:
+ return lib_route_map_entry_match_destroy(args);
+ }
+
+ return NB_OK;
+}
+
+/*
+ * XPath: /frr-route-map:lib/route-map/entry/match-condition/rmap-match-condition/frr-bgp-route-map:peer-ipv6-address
+ */
+int
+lib_route_map_entry_match_condition_rmap_match_condition_peer_ipv6_address_modify(
+ struct nb_cb_modify_args *args)
+{
+ struct routemap_hook_context *rhc;
+ const char *peer;
+ enum rmap_compile_rets ret;
+
+ switch (args->event) {
+ case NB_EV_VALIDATE:
+ case NB_EV_PREPARE:
+ case NB_EV_ABORT:
+ break;
+ case NB_EV_APPLY:
+ /* Add configuration. */
+ rhc = nb_running_get_entry(args->dnode, NULL, true);
+ peer = yang_dnode_get_string(args->dnode, NULL);
+
+ /* Set destroy information. */
+ rhc->rhc_mhook = bgp_route_match_delete;
+ rhc->rhc_rule = "peer";
+ rhc->rhc_event = RMAP_EVENT_MATCH_DELETED;
+
+ ret = bgp_route_match_add(rhc->rhc_rmi, "peer", peer,
+ RMAP_EVENT_MATCH_ADDED,
+ args->errmsg, args->errmsg_len);
+
+ if (ret != RMAP_COMPILE_SUCCESS) {
+ rhc->rhc_mhook = NULL;
+ return NB_ERR_INCONSISTENCY;
+ }
+ }
+
+ return NB_OK;
+}
+
+int
+lib_route_map_entry_match_condition_rmap_match_condition_peer_ipv6_address_destroy(
+ struct nb_cb_destroy_args *args)
+{
+ switch (args->event) {
+ case NB_EV_VALIDATE:
+ case NB_EV_PREPARE:
+ case NB_EV_ABORT:
+ break;
+ case NB_EV_APPLY:
+ return lib_route_map_entry_match_destroy(args);
+ }
+
+ return NB_OK;
+}
+
+/*
+ * XPath: /frr-route-map:lib/route-map/entry/match-condition/rmap-match-condition/frr-bgp-route-map:peer-local
+ */
+int
+lib_route_map_entry_match_condition_rmap_match_condition_peer_local_modify(
+ struct nb_cb_modify_args *args)
+{
+ struct routemap_hook_context *rhc;
+ bool value;
+ enum rmap_compile_rets ret;
+
+ switch (args->event) {
+ case NB_EV_VALIDATE:
+ case NB_EV_PREPARE:
+ case NB_EV_ABORT:
+ break;
+ case NB_EV_APPLY:
+ /* Add configuration. */
+ rhc = nb_running_get_entry(args->dnode, NULL, true);
+ value = yang_dnode_get_bool(args->dnode, NULL);
+
+ /* Set destroy information. */
+ rhc->rhc_mhook = bgp_route_match_delete;
+ rhc->rhc_rule = "peer";
+ rhc->rhc_event = RMAP_EVENT_MATCH_DELETED;
+
+ if (value) {
+ ret = bgp_route_match_add(rhc->rhc_rmi, "peer",
+ "local",
+ RMAP_EVENT_MATCH_ADDED,
+ args->errmsg, args->errmsg_len);
+
+ if (ret != RMAP_COMPILE_SUCCESS) {
+ rhc->rhc_mhook = NULL;
+ return NB_ERR_INCONSISTENCY;
+ }
+ }
+ }
+
+ return NB_OK;
+}
+
+int
+lib_route_map_entry_match_condition_rmap_match_condition_peer_local_destroy(
+ struct nb_cb_destroy_args *args)
+{
+ switch (args->event) {
+ case NB_EV_VALIDATE:
+ case NB_EV_PREPARE:
+ case NB_EV_ABORT:
+ break;
+ case NB_EV_APPLY:
+ return lib_route_map_entry_match_destroy(args);
+ }
+
+ return NB_OK;
+}
+
+/*
+ * XPath: /frr-route-map:lib/route-map/entry/match-condition/rmap-match-condition/frr-bgp-route-map:list-name
+ */
+int
+lib_route_map_entry_match_condition_rmap_match_condition_list_name_modify(
+ struct nb_cb_modify_args *args)
+{
+ struct routemap_hook_context *rhc;
+ const char *list_name;
+ enum rmap_compile_rets ret = RMAP_COMPILE_SUCCESS;
+ const char *condition;
+
+ switch (args->event) {
+ case NB_EV_VALIDATE:
+ case NB_EV_PREPARE:
+ case NB_EV_ABORT:
+ break;
+ case NB_EV_APPLY:
+ /* Add configuration. */
+ rhc = nb_running_get_entry(args->dnode, NULL, true);
+ list_name = yang_dnode_get_string(args->dnode, NULL);
+ condition = yang_dnode_get_string(args->dnode,
+ "../../frr-route-map:condition");
+
+ if (IS_MATCH_AS_LIST(condition)) {
+ /* Set destroy information. */
+ rhc->rhc_mhook = bgp_route_match_delete;
+ rhc->rhc_rule = "as-path";
+ rhc->rhc_event = RMAP_EVENT_ASLIST_DELETED;
+
+ ret = bgp_route_match_add(rhc->rhc_rmi, "as-path",
+ list_name, RMAP_EVENT_ASLIST_ADDED,
+ args->errmsg, args->errmsg_len);
+ } else if (IS_MATCH_MAC_LIST(condition)) {
+ /* Set destroy information. */
+ rhc->rhc_mhook = bgp_route_match_delete;
+ rhc->rhc_rule = "mac address";
+ rhc->rhc_event = RMAP_EVENT_FILTER_DELETED;
+
+ ret = bgp_route_match_add(rhc->rhc_rmi,
+ "mac address",
+ list_name,
+ RMAP_EVENT_FILTER_ADDED,
+ args->errmsg, args->errmsg_len);
+ } else if (IS_MATCH_ROUTE_SRC(condition)) {
+ /* Set destroy information. */
+ rhc->rhc_mhook = bgp_route_match_delete;
+ rhc->rhc_rule = "ip route-source";
+ rhc->rhc_event = RMAP_EVENT_FILTER_DELETED;
+
+ ret = bgp_route_match_add(rhc->rhc_rmi,
+ "ip route-source",
+ list_name, RMAP_EVENT_FILTER_ADDED,
+ args->errmsg, args->errmsg_len);
+ } else if (IS_MATCH_ROUTE_SRC_PL(condition)) {
+ /* Set destroy information. */
+ rhc->rhc_mhook = bgp_route_match_delete;
+ rhc->rhc_rule = "ip route-source prefix-list";
+ rhc->rhc_event = RMAP_EVENT_PLIST_DELETED;
+
+ ret = bgp_route_match_add(rhc->rhc_rmi,
+ "ip route-source prefix-list",
+ list_name, RMAP_EVENT_PLIST_ADDED,
+ args->errmsg, args->errmsg_len);
+ }
+
+ if (ret != RMAP_COMPILE_SUCCESS) {
+ rhc->rhc_mhook = NULL;
+ return NB_ERR_INCONSISTENCY;
+ }
+ }
+
+ return NB_OK;
+}
+
+int
+lib_route_map_entry_match_condition_rmap_match_condition_list_name_destroy(
+ struct nb_cb_destroy_args *args)
+{
+ switch (args->event) {
+ case NB_EV_VALIDATE:
+ case NB_EV_PREPARE:
+ case NB_EV_ABORT:
+ break;
+ case NB_EV_APPLY:
+ return lib_route_map_entry_match_destroy(args);
+ }
+
+ return NB_OK;
+}
+
+/*
+ * XPath: /frr-route-map:lib/route-map/entry/match-condition/rmap-match-condition/frr-bgp-route-map:evpn-default-route
+ */
+int
+lib_route_map_entry_match_condition_rmap_match_condition_evpn_default_route_create(
+ struct nb_cb_create_args *args)
+{
+ struct routemap_hook_context *rhc;
+ enum rmap_compile_rets ret;
+
+ switch (args->event) {
+ case NB_EV_VALIDATE:
+ case NB_EV_PREPARE:
+ case NB_EV_ABORT:
+ break;
+ case NB_EV_APPLY:
+ /* Add configuration. */
+ rhc = nb_running_get_entry(args->dnode, NULL, true);
+
+ /* Set destroy information. */
+ rhc->rhc_mhook = bgp_route_match_delete;
+ rhc->rhc_rule = "evpn default-route";
+ rhc->rhc_event = RMAP_EVENT_MATCH_DELETED;
+
+ ret = bgp_route_match_add(rhc->rhc_rmi, "evpn default-route",
+ NULL, RMAP_EVENT_MATCH_ADDED,
+ args->errmsg, args->errmsg_len);
+
+ if (ret != RMAP_COMPILE_SUCCESS) {
+ rhc->rhc_mhook = NULL;
+ return NB_ERR_INCONSISTENCY;
+ }
+ }
+
+ return NB_OK;
+}
+
+int
+lib_route_map_entry_match_condition_rmap_match_condition_evpn_default_route_destroy(
+ struct nb_cb_destroy_args *args)
+{
+ switch (args->event) {
+ case NB_EV_VALIDATE:
+ case NB_EV_PREPARE:
+ case NB_EV_ABORT:
+ break;
+ case NB_EV_APPLY:
+ return lib_route_map_entry_match_destroy(args);
+ }
+
+ return NB_OK;
+}
+
+/*
+ * XPath: /frr-route-map:lib/route-map/entry/match-condition/rmap-match-condition/frr-bgp-route-map:evpn-vni
+ */
+int
+lib_route_map_entry_match_condition_rmap_match_condition_evpn_vni_modify(
+ struct nb_cb_modify_args *args)
+{
+ struct routemap_hook_context *rhc;
+ const char *vni;
+ enum rmap_compile_rets ret;
+
+ switch (args->event) {
+ case NB_EV_VALIDATE:
+ case NB_EV_PREPARE:
+ case NB_EV_ABORT:
+ break;
+ case NB_EV_APPLY:
+ /* Add configuration. */
+ rhc = nb_running_get_entry(args->dnode, NULL, true);
+ vni = yang_dnode_get_string(args->dnode, NULL);
+
+ /* Set destroy information. */
+ rhc->rhc_mhook = bgp_route_match_delete;
+ rhc->rhc_rule = "evpn vni";
+ rhc->rhc_event = RMAP_EVENT_MATCH_DELETED;
+
+ ret = bgp_route_match_add(rhc->rhc_rmi, "evpn vni", vni,
+ RMAP_EVENT_MATCH_ADDED,
+ args->errmsg, args->errmsg_len);
+
+ if (ret != RMAP_COMPILE_SUCCESS) {
+ rhc->rhc_mhook = NULL;
+ return NB_ERR_INCONSISTENCY;
+ }
+ }
+
+ return NB_OK;
+}
+
+int
+lib_route_map_entry_match_condition_rmap_match_condition_evpn_vni_destroy(
+ struct nb_cb_destroy_args *args)
+{
+ switch (args->event) {
+ case NB_EV_VALIDATE:
+ case NB_EV_PREPARE:
+ case NB_EV_ABORT:
+ break;
+ case NB_EV_APPLY:
+ return lib_route_map_entry_match_destroy(args);
+ }
+
+ return NB_OK;
+}
+
+/*
+ * XPath: /frr-route-map:lib/route-map/entry/match-condition/rmap-match-condition/frr-bgp-route-map:evpn-route-type
+ */
+int
+lib_route_map_entry_match_condition_rmap_match_condition_evpn_route_type_modify(
+ struct nb_cb_modify_args *args)
+{
+ struct routemap_hook_context *rhc;
+ const char *type;
+ enum rmap_compile_rets ret;
+
+ switch (args->event) {
+ case NB_EV_VALIDATE:
+ case NB_EV_PREPARE:
+ case NB_EV_ABORT:
+ break;
+ case NB_EV_APPLY:
+ /* Add configuration. */
+ rhc = nb_running_get_entry(args->dnode, NULL, true);
+ type = yang_dnode_get_string(args->dnode, NULL);
+
+ /* Set destroy information. */
+ rhc->rhc_mhook = bgp_route_match_delete;
+ rhc->rhc_rule = "evpn route-type";
+ rhc->rhc_event = RMAP_EVENT_MATCH_DELETED;
+
+ ret = bgp_route_match_add(rhc->rhc_rmi, "evpn route-type",
+ type,
+ RMAP_EVENT_MATCH_ADDED,
+ args->errmsg, args->errmsg_len);
+
+ if (ret != RMAP_COMPILE_SUCCESS) {
+ rhc->rhc_mhook = NULL;
+ return NB_ERR_INCONSISTENCY;
+ }
+ }
+
+ return NB_OK;
+}
+
+int
+lib_route_map_entry_match_condition_rmap_match_condition_evpn_route_type_destroy(
+ struct nb_cb_destroy_args *args)
+{
+ switch (args->event) {
+ case NB_EV_VALIDATE:
+ case NB_EV_PREPARE:
+ case NB_EV_ABORT:
+ break;
+ case NB_EV_APPLY:
+ return lib_route_map_entry_match_destroy(args);
+ }
+
+ return NB_OK;
+}
+
+/*
+ * XPath: /frr-route-map:lib/route-map/entry/match-condition/rmap-match-condition/frr-bgp-route-map:route-distinguisher
+ */
+int
+lib_route_map_entry_match_condition_rmap_match_condition_route_distinguisher_modify(
+ struct nb_cb_modify_args *args)
+{
+ struct routemap_hook_context *rhc;
+ const char *rd;
+ enum rmap_compile_rets ret;
+
+ switch (args->event) {
+ case NB_EV_VALIDATE:
+ case NB_EV_PREPARE:
+ case NB_EV_ABORT:
+ break;
+ case NB_EV_APPLY:
+ /* Add configuration. */
+ rhc = nb_running_get_entry(args->dnode, NULL, true);
+ rd = yang_dnode_get_string(args->dnode, NULL);
+
+ /* Set destroy information. */
+ rhc->rhc_mhook = bgp_route_match_delete;
+ rhc->rhc_rule = "evpn rd";
+ rhc->rhc_event = RMAP_EVENT_MATCH_DELETED;
+
+ ret = bgp_route_match_add(rhc->rhc_rmi, "evpn rd", rd,
+ RMAP_EVENT_MATCH_ADDED,
+ args->errmsg, args->errmsg_len);
+
+ if (ret != RMAP_COMPILE_SUCCESS) {
+ rhc->rhc_mhook = NULL;
+ return NB_ERR_INCONSISTENCY;
+ }
+ }
+
+ return NB_OK;
+}
+
+int
+lib_route_map_entry_match_condition_rmap_match_condition_route_distinguisher_destroy(
+ struct nb_cb_destroy_args *args)
+{
+ switch (args->event) {
+ case NB_EV_VALIDATE:
+ case NB_EV_PREPARE:
+ case NB_EV_ABORT:
+ break;
+ case NB_EV_APPLY:
+ return lib_route_map_entry_match_destroy(args);
+ }
+
+ return NB_OK;
+}
+
+/*
+ * XPath = /frr-route-map:lib/route-map/entry/match-condition/rmap-match-condition/frr-bgp-route-map:comm-list
+ */
+void
+lib_route_map_entry_match_condition_rmap_match_condition_comm_list_finish(
+ struct nb_cb_apply_finish_args *args)
+{
+ struct routemap_hook_context *rhc;
+ const char *value;
+ bool exact_match = false;
+ char *argstr;
+ const char *condition;
+ route_map_event_t event;
+ int ret;
+
+ /* Add configuration. */
+ rhc = nb_running_get_entry(args->dnode, NULL, true);
+ value = yang_dnode_get_string(args->dnode, "./comm-list-name");
+
+ if (yang_dnode_exists(args->dnode, "./comm-list-name-exact-match"))
+ exact_match = yang_dnode_get_bool(
+ args->dnode, "./comm-list-name-exact-match");
+
+ if (exact_match) {
+ argstr = XMALLOC(MTYPE_ROUTE_MAP_COMPILED,
+ strlen(value) + strlen("exact-match") + 2);
+
+ snprintf(argstr, (strlen(value) + strlen("exact-match") + 2),
+ "%s exact-match", value);
+ } else
+ argstr = (char *)value;
+
+ /* Set destroy information. */
+ rhc->rhc_mhook = bgp_route_match_delete;
+
+ condition = yang_dnode_get_string(args->dnode,
+ "../../frr-route-map:condition");
+ if (IS_MATCH_COMMUNITY(condition)) {
+ rhc->rhc_rule = "community";
+ event = RMAP_EVENT_CLIST_ADDED;
+ rhc->rhc_event = RMAP_EVENT_CLIST_DELETED;
+ } else if (IS_MATCH_LCOMMUNITY(condition)) {
+ rhc->rhc_rule = "large-community";
+ event = RMAP_EVENT_LLIST_ADDED;
+ rhc->rhc_event = RMAP_EVENT_LLIST_DELETED;
+ } else {
+ rhc->rhc_rule = "extcommunity";
+ event = RMAP_EVENT_ECLIST_ADDED;
+ rhc->rhc_event = RMAP_EVENT_ECLIST_DELETED;
+ }
+
+ ret = bgp_route_match_add(rhc->rhc_rmi, rhc->rhc_rule, argstr, event,
+ args->errmsg, args->errmsg_len);
+ /*
+ * At this point if this is not a successful operation
+ * bgpd is about to crash. Let's just cut to the
+ * chase and do it.
+ */
+ assert(ret == RMAP_COMPILE_SUCCESS);
+
+ if (argstr != value)
+ XFREE(MTYPE_ROUTE_MAP_COMPILED, argstr);
+}
+
+/*
+ * XPath:
+ * /frr-route-map:lib/route-map/entry/match-condition/rmap-match-condition/frr-bgp-route-map:comm-list/comm-list-name
+ */
+int
+lib_route_map_entry_match_condition_rmap_match_condition_comm_list_comm_list_name_modify(
+ struct nb_cb_modify_args *args)
+{
+ switch (args->event) {
+ case NB_EV_VALIDATE:
+ case NB_EV_PREPARE:
+ case NB_EV_ABORT:
+ case NB_EV_APPLY:
+ break;
+ }
+
+ return NB_OK;
+}
+
+int
+lib_route_map_entry_match_condition_rmap_match_condition_comm_list_comm_list_name_destroy(
+ struct nb_cb_destroy_args *args)
+{
+ switch (args->event) {
+ case NB_EV_VALIDATE:
+ case NB_EV_PREPARE:
+ case NB_EV_ABORT:
+ break;
+ case NB_EV_APPLY:
+ return lib_route_map_entry_match_destroy(args);
+ }
+
+ return NB_OK;
+
+}
+
+/*
+ * XPath: /frr-route-map:lib/route-map/entry/match-condition/rmap-match-condition/frr-bgp-route-map:comm-list/comm-list-name-exact-match
+ */
+int
+lib_route_map_entry_match_condition_rmap_match_condition_comm_list_comm_list_name_exact_match_modify(
+ struct nb_cb_modify_args *args)
+{
+ switch (args->event) {
+ case NB_EV_VALIDATE:
+ case NB_EV_PREPARE:
+ case NB_EV_ABORT:
+ case NB_EV_APPLY:
+ break;
+ }
+
+ return NB_OK;
+}
+
+int
+lib_route_map_entry_match_condition_rmap_match_condition_comm_list_comm_list_name_exact_match_destroy(
+ struct nb_cb_destroy_args *args)
+{
+ switch (args->event) {
+ case NB_EV_VALIDATE:
+ case NB_EV_PREPARE:
+ case NB_EV_ABORT:
+ break;
+ case NB_EV_APPLY:
+ return lib_route_map_entry_match_destroy(args);
+ }
+
+ return NB_OK;
+}
+
+/*
+ * XPath: /frr-route-map:lib/route-map/entry/match-condition/rmap-match-condition/frr-bgp-route-map:ipv4-address
+ */
+int
+lib_route_map_entry_match_condition_rmap_match_condition_ipv4_address_modify(
+ struct nb_cb_modify_args *args)
+{
+ struct routemap_hook_context *rhc;
+ const char *peer;
+ enum rmap_compile_rets ret;
+
+ switch (args->event) {
+ case NB_EV_VALIDATE:
+ case NB_EV_PREPARE:
+ case NB_EV_ABORT:
+ break;
+ case NB_EV_APPLY:
+ /* Add configuration. */
+ rhc = nb_running_get_entry(args->dnode, NULL, true);
+ peer = yang_dnode_get_string(args->dnode, NULL);
+
+ /* Set destroy information. */
+ rhc->rhc_mhook = bgp_route_match_delete;
+ rhc->rhc_rule = "ip next-hop address";
+ rhc->rhc_event = RMAP_EVENT_MATCH_DELETED;
+
+ ret = bgp_route_match_add(rhc->rhc_rmi, rhc->rhc_rule,
+ peer, RMAP_EVENT_MATCH_ADDED,
+ args->errmsg, args->errmsg_len);
+
+ if (ret != RMAP_COMPILE_SUCCESS) {
+ rhc->rhc_mhook = NULL;
+ return NB_ERR_INCONSISTENCY;
+ }
+ }
+
+ return NB_OK;
+}
+
+int
+lib_route_map_entry_match_condition_rmap_match_condition_ipv4_address_destroy(
+ struct nb_cb_destroy_args *args)
+{
+ switch (args->event) {
+ case NB_EV_VALIDATE:
+ case NB_EV_PREPARE:
+ case NB_EV_ABORT:
+ break;
+ case NB_EV_APPLY:
+ return lib_route_map_entry_match_destroy(args);
+ }
+
+ return NB_OK;
+}
+
+/*
+ * XPath: /frr-route-map:lib/route-map/entry/match-condition/rmap-match-condition/frr-bgp-route-map:ipv6-address
+ */
+int
+lib_route_map_entry_match_condition_rmap_match_condition_ipv6_address_modify(
+ struct nb_cb_modify_args *args)
+{
+ struct routemap_hook_context *rhc;
+ const char *peer;
+ enum rmap_compile_rets ret;
+
+ switch (args->event) {
+ case NB_EV_VALIDATE:
+ case NB_EV_PREPARE:
+ case NB_EV_ABORT:
+ break;
+ case NB_EV_APPLY:
+ /* Add configuration. */
+ rhc = nb_running_get_entry(args->dnode, NULL, true);
+ peer = yang_dnode_get_string(args->dnode, NULL);
+
+ /* Set destroy information. */
+ rhc->rhc_mhook = bgp_route_match_delete;
+ rhc->rhc_rule = "ipv6 next-hop address";
+ rhc->rhc_event = RMAP_EVENT_MATCH_DELETED;
+
+ ret = bgp_route_match_add(rhc->rhc_rmi, rhc->rhc_rule,
+ peer, RMAP_EVENT_MATCH_ADDED,
+ args->errmsg, args->errmsg_len);
+
+ if (ret != RMAP_COMPILE_SUCCESS) {
+ rhc->rhc_mhook = NULL;
+ return NB_ERR_INCONSISTENCY;
+ }
+ }
+
+ return NB_OK;
+}
+
+int
+lib_route_map_entry_match_condition_rmap_match_condition_ipv6_address_destroy(
+ struct nb_cb_destroy_args *args)
+{
+ switch (args->event) {
+ case NB_EV_VALIDATE:
+ case NB_EV_PREPARE:
+ case NB_EV_ABORT:
+ break;
+ case NB_EV_APPLY:
+ return lib_route_map_entry_match_destroy(args);
+ }
+
+ return NB_OK;
+}
+
+/*
+ * XPath:
+ * /frr-route-map:lib/route-map/entry/set-action/rmap-set-action/frr-bgp-route-map:distance
+ */
+int lib_route_map_entry_set_action_rmap_set_action_distance_modify(
+ struct nb_cb_modify_args *args)
+{
+ struct routemap_hook_context *rhc;
+ const char *type;
+ int rv;
+
+ switch (args->event) {
+ case NB_EV_VALIDATE:
+ case NB_EV_PREPARE:
+ case NB_EV_ABORT:
+ break;
+ case NB_EV_APPLY:
+ /* Add configuration. */
+ rhc = nb_running_get_entry(args->dnode, NULL, true);
+ type = yang_dnode_get_string(args->dnode, NULL);
+
+ /* Set destroy information. */
+ rhc->rhc_shook = generic_set_delete;
+ rhc->rhc_rule = "distance";
+ rhc->rhc_event = RMAP_EVENT_SET_DELETED;
+
+ rv = generic_set_add(rhc->rhc_rmi, "distance", type,
+ args->errmsg, args->errmsg_len);
+ if (rv != CMD_SUCCESS) {
+ rhc->rhc_shook = NULL;
+ return NB_ERR_INCONSISTENCY;
+ }
+ }
+
+ return NB_OK;
+}
+
+int lib_route_map_entry_set_action_rmap_set_action_distance_destroy(
+ struct nb_cb_destroy_args *args)
+{
+ switch (args->event) {
+ case NB_EV_VALIDATE:
+ case NB_EV_PREPARE:
+ case NB_EV_ABORT:
+ break;
+ case NB_EV_APPLY:
+ return lib_route_map_entry_match_destroy(args);
+ }
+
+ return NB_OK;
+}
+
+/*
+ * XPath:
+ * /frr-route-map:lib/route-map/entry/set-action/rmap-set-action/frr-bgp-route-map:extcommunity-rt
+ */
+int
+lib_route_map_entry_set_action_rmap_set_action_extcommunity_rt_modify(
+ struct nb_cb_modify_args *args)
+{
+ struct routemap_hook_context *rhc;
+ const char *type;
+ int rv;
+
+ switch (args->event) {
+ case NB_EV_VALIDATE:
+ case NB_EV_PREPARE:
+ case NB_EV_ABORT:
+ break;
+ case NB_EV_APPLY:
+ /* Add configuration. */
+ rhc = nb_running_get_entry(args->dnode, NULL, true);
+ type = yang_dnode_get_string(args->dnode, NULL);
+
+ /* Set destroy information. */
+ rhc->rhc_shook = generic_set_delete;
+ rhc->rhc_rule = "extcommunity rt";
+ rhc->rhc_event = RMAP_EVENT_SET_DELETED;
+
+ rv = generic_set_add(rhc->rhc_rmi, "extcommunity rt", type,
+ args->errmsg, args->errmsg_len);
+ if (rv != CMD_SUCCESS) {
+ rhc->rhc_shook = NULL;
+ return NB_ERR_INCONSISTENCY;
+ }
+ }
+
+ return NB_OK;
+}
+
+int
+lib_route_map_entry_set_action_rmap_set_action_extcommunity_rt_destroy(
+ struct nb_cb_destroy_args *args)
+{
+ switch (args->event) {
+ case NB_EV_VALIDATE:
+ case NB_EV_PREPARE:
+ case NB_EV_ABORT:
+ break;
+ case NB_EV_APPLY:
+ return lib_route_map_entry_match_destroy(args);
+ }
+
+ return NB_OK;
+}
+
+/*
+ * XPath:
+ * /frr-route-map:lib/route-map/entry/set-action/rmap-set-action/frr-bgp-route-map:extcommunity-soo
+ */
+int
+lib_route_map_entry_set_action_rmap_set_action_extcommunity_soo_modify(
+ struct nb_cb_modify_args *args)
+{
+ struct routemap_hook_context *rhc;
+ const char *type;
+ int rv;
+
+ switch (args->event) {
+ case NB_EV_VALIDATE:
+ case NB_EV_PREPARE:
+ case NB_EV_ABORT:
+ break;
+ case NB_EV_APPLY:
+ /* Add configuration. */
+ rhc = nb_running_get_entry(args->dnode, NULL, true);
+ type = yang_dnode_get_string(args->dnode, NULL);
+
+ /* Set destroy information. */
+ rhc->rhc_shook = generic_set_delete;
+ rhc->rhc_rule = "extcommunity soo";
+ rhc->rhc_event = RMAP_EVENT_SET_DELETED;
+
+ rv = generic_set_add(rhc->rhc_rmi, "extcommunity soo",
+ type,
+ args->errmsg, args->errmsg_len);
+ if (rv != CMD_SUCCESS) {
+ rhc->rhc_shook = NULL;
+ return NB_ERR_INCONSISTENCY;
+ }
+ }
+
+ return NB_OK;
+}
+
+int
+lib_route_map_entry_set_action_rmap_set_action_extcommunity_soo_destroy(
+ struct nb_cb_destroy_args *args)
+{
+ switch (args->event) {
+ case NB_EV_VALIDATE:
+ case NB_EV_PREPARE:
+ case NB_EV_ABORT:
+ break;
+ case NB_EV_APPLY:
+ return lib_route_map_entry_match_destroy(args);
+ }
+
+ return NB_OK;
+}
+
+/*
+ * XPath:
+ * /frr-route-map:lib/route-map/entry/set-action/rmap-set-action/frr-bgp-route-map:ipv4-address
+ */
+int lib_route_map_entry_set_action_rmap_set_action_ipv4_address_modify(
+ struct nb_cb_modify_args *args)
+{
+ struct routemap_hook_context *rhc;
+ const char *addr;
+ int rv = CMD_SUCCESS;
+
+ switch (args->event) {
+ case NB_EV_VALIDATE:
+ case NB_EV_PREPARE:
+ case NB_EV_ABORT:
+ break;
+ case NB_EV_APPLY:
+ /* Add configuration. */
+ rhc = nb_running_get_entry(args->dnode, NULL, true);
+ addr = yang_dnode_get_string(args->dnode, NULL);
+
+ rhc->rhc_shook = generic_set_delete;
+ rhc->rhc_event = RMAP_EVENT_SET_DELETED;
+ rhc->rhc_rule = "ipv4 vpn next-hop";
+
+ rv = generic_set_add(rhc->rhc_rmi, rhc->rhc_rule, addr,
+ args->errmsg, args->errmsg_len);
+
+ if (rv != CMD_SUCCESS) {
+ rhc->rhc_shook = NULL;
+ return NB_ERR_INCONSISTENCY;
+ }
+ }
+
+ return NB_OK;
+}
+
+int lib_route_map_entry_set_action_rmap_set_action_ipv4_address_destroy(
+ struct nb_cb_destroy_args *args)
+{
+ switch (args->event) {
+ case NB_EV_VALIDATE:
+ case NB_EV_PREPARE:
+ case NB_EV_ABORT:
+ break;
+ case NB_EV_APPLY:
+ return lib_route_map_entry_set_destroy(args);
+ }
+
+ return NB_OK;
+}
+
+/*
+ * XPath:
+ * /frr-route-map:lib/route-map/entry/set-action/rmap-set-action/frr-bgp-route-map:ipv4-nexthop
+ */
+int lib_route_map_entry_set_action_rmap_set_action_ipv4_nexthop_modify(
+ struct nb_cb_modify_args *args)
+{
+ struct routemap_hook_context *rhc;
+ const char *type;
+ int rv;
+
+ switch (args->event) {
+ case NB_EV_VALIDATE:
+ case NB_EV_PREPARE:
+ case NB_EV_ABORT:
+ break;
+ case NB_EV_APPLY:
+ /* Add configuration. */
+ rhc = nb_running_get_entry(args->dnode, NULL, true);
+ type = yang_dnode_get_string(args->dnode, NULL);
+
+ /* Set destroy information. */
+ rhc->rhc_shook = generic_set_delete;
+ rhc->rhc_rule = "ip next-hop";
+ rhc->rhc_event = RMAP_EVENT_SET_DELETED;
+
+ rv = generic_set_add(rhc->rhc_rmi, rhc->rhc_rule, type,
+ args->errmsg, args->errmsg_len);
+
+ if (rv != CMD_SUCCESS) {
+ rhc->rhc_shook = NULL;
+ return NB_ERR_INCONSISTENCY;
+ }
+ }
+
+ return NB_OK;
+}
+
+int lib_route_map_entry_set_action_rmap_set_action_ipv4_nexthop_destroy(
+ struct nb_cb_destroy_args *args)
+{
+ switch (args->event) {
+ case NB_EV_VALIDATE:
+ case NB_EV_PREPARE:
+ case NB_EV_ABORT:
+ break;
+ case NB_EV_APPLY:
+ return lib_route_map_entry_set_destroy(args);
+ }
+
+ return NB_OK;
+}
+
+/*
+ * XPath:
+ * /frr-route-map:lib/route-map/entry/set-action/rmap-set-action/frr-bgp-route-map:ipv6-address
+ */
+int lib_route_map_entry_set_action_rmap_set_action_ipv6_address_modify(
+ struct nb_cb_modify_args *args)
+{
+ struct routemap_hook_context *rhc;
+ const char *addr;
+ int rv = CMD_SUCCESS;
+ const char *action = NULL;
+ struct in6_addr i6a;
+
+ action = yang_dnode_get_string(args->dnode,
+ "../../frr-route-map:action");
+ switch (args->event) {
+ case NB_EV_VALIDATE:
+ if (action && IS_SET_IPV6_NH_GLOBAL(action)) {
+ yang_dnode_get_ipv6(&i6a, args->dnode, NULL);
+ if (IN6_IS_ADDR_UNSPECIFIED(&i6a)
+ || IN6_IS_ADDR_LOOPBACK(&i6a)
+ || IN6_IS_ADDR_MULTICAST(&i6a)
+ || IN6_IS_ADDR_LINKLOCAL(&i6a))
+ return NB_ERR_VALIDATION;
+ }
+ /* FALLTHROUGH */
+ case NB_EV_PREPARE:
+ case NB_EV_ABORT:
+ return NB_OK;
+ case NB_EV_APPLY:
+ break;
+ }
+
+ /* Add configuration. */
+ rhc = nb_running_get_entry(args->dnode, NULL, true);
+ addr = yang_dnode_get_string(args->dnode, NULL);
+
+ rhc->rhc_shook = generic_set_delete;
+ rhc->rhc_event = RMAP_EVENT_SET_DELETED;
+
+ if (IS_SET_IPV6_NH_GLOBAL(action))
+ /* Set destroy information. */
+ rhc->rhc_rule = "ipv6 next-hop global";
+ else
+ rhc->rhc_rule = "ipv6 vpn next-hop";
+
+ rv = generic_set_add(rhc->rhc_rmi, rhc->rhc_rule, addr,
+ args->errmsg, args->errmsg_len);
+
+ if (rv != CMD_SUCCESS) {
+ rhc->rhc_shook = NULL;
+ return NB_ERR_INCONSISTENCY;
+ }
+
+ return NB_OK;
+}
+
+int lib_route_map_entry_set_action_rmap_set_action_ipv6_address_destroy(
+ struct nb_cb_destroy_args *args)
+{
+ switch (args->event) {
+ case NB_EV_VALIDATE:
+ case NB_EV_PREPARE:
+ case NB_EV_ABORT:
+ break;
+ case NB_EV_APPLY:
+ return lib_route_map_entry_set_destroy(args);
+ }
+
+ return NB_OK;
+}
+
+/*
+ * XPath:
+ * /frr-route-map:lib/route-map/entry/set-action/rmap-set-action/frr-bgp-route-map:preference
+ */
+int lib_route_map_entry_set_action_rmap_set_action_preference_modify(
+ struct nb_cb_modify_args *args)
+{
+ struct routemap_hook_context *rhc;
+ int rv = CMD_SUCCESS;
+ const char *action = NULL;
+ bool value;
+
+ switch (args->event) {
+ case NB_EV_VALIDATE:
+ case NB_EV_PREPARE:
+ case NB_EV_ABORT:
+ break;
+ case NB_EV_APPLY:
+ /* Add configuration. */
+ rhc = nb_running_get_entry(args->dnode, NULL, true);
+ value = yang_dnode_get_bool(args->dnode, NULL);
+
+ rhc->rhc_shook = generic_set_delete;
+ rhc->rhc_event = RMAP_EVENT_SET_DELETED;
+
+ action = yang_dnode_get_string(args->dnode,
+ "../../frr-route-map:action");
+
+ if (value) {
+ if (IS_SET_IPV6_PEER_ADDR(action))
+ /* Set destroy information. */
+ rhc->rhc_rule = "ipv6 next-hop peer-address";
+ else
+ rhc->rhc_rule = "ipv6 next-hop prefer-global";
+
+ rv = generic_set_add(rhc->rhc_rmi, rhc->rhc_rule,
+ NULL,
+ args->errmsg, args->errmsg_len);
+ }
+
+ if (rv != CMD_SUCCESS) {
+ rhc->rhc_shook = NULL;
+ return NB_ERR_INCONSISTENCY;
+ }
+ }
+
+ return NB_OK;
+}
+
+int lib_route_map_entry_set_action_rmap_set_action_preference_destroy(
+ struct nb_cb_destroy_args *args)
+{
+ switch (args->event) {
+ case NB_EV_VALIDATE:
+ case NB_EV_PREPARE:
+ case NB_EV_ABORT:
+ break;
+ case NB_EV_APPLY:
+ return lib_route_map_entry_set_destroy(args);
+ }
+
+ return NB_OK;
+}
+
+/*
+ * XPath:
+ * /frr-route-map:lib/route-map/entry/set-action/rmap-set-action/frr-bgp-route-map:label-index
+ */
+int lib_route_map_entry_set_action_rmap_set_action_label_index_modify(
+ struct nb_cb_modify_args *args)
+{
+ struct routemap_hook_context *rhc;
+ const char *type;
+ int rv;
+
+ switch (args->event) {
+ case NB_EV_VALIDATE:
+ case NB_EV_PREPARE:
+ case NB_EV_ABORT:
+ break;
+ case NB_EV_APPLY:
+ /* Add configuration. */
+ rhc = nb_running_get_entry(args->dnode, NULL, true);
+ type = yang_dnode_get_string(args->dnode, NULL);
+
+ /* Set destroy information. */
+ rhc->rhc_shook = generic_set_delete;
+ rhc->rhc_rule = "label-index";
+ rhc->rhc_event = RMAP_EVENT_SET_DELETED;
+
+ rv = generic_set_add(rhc->rhc_rmi, "label-index", type,
+ args->errmsg, args->errmsg_len);
+ if (rv != CMD_SUCCESS) {
+ rhc->rhc_shook = NULL;
+ return NB_ERR_INCONSISTENCY;
+ }
+ }
+
+ return NB_OK;
+}
+
+int lib_route_map_entry_set_action_rmap_set_action_label_index_destroy(
+ struct nb_cb_destroy_args *args)
+{
+ switch (args->event) {
+ case NB_EV_VALIDATE:
+ case NB_EV_PREPARE:
+ case NB_EV_ABORT:
+ break;
+ case NB_EV_APPLY:
+ return lib_route_map_entry_set_destroy(args);
+ }
+
+ return NB_OK;
+}
+
+/*
+ * XPath:
+ * /frr-route-map:lib/route-map/entry/set-action/rmap-set-action/frr-bgp-route-map:local-pref
+ */
+int lib_route_map_entry_set_action_rmap_set_action_local_pref_modify(
+ struct nb_cb_modify_args *args)
+{
+ struct routemap_hook_context *rhc;
+ const char *type;
+ int rv;
+
+ switch (args->event) {
+ case NB_EV_VALIDATE:
+ case NB_EV_PREPARE:
+ case NB_EV_ABORT:
+ break;
+ case NB_EV_APPLY:
+ /* Add configuration. */
+ rhc = nb_running_get_entry(args->dnode, NULL, true);
+ type = yang_dnode_get_string(args->dnode, NULL);
+
+ /* Set destroy information. */
+ rhc->rhc_shook = generic_set_delete;
+ rhc->rhc_rule = "local-preference";
+ rhc->rhc_event = RMAP_EVENT_SET_DELETED;
+
+ rv = generic_set_add(rhc->rhc_rmi, "local-preference",
+ type,
+ args->errmsg, args->errmsg_len);
+ if (rv != CMD_SUCCESS) {
+ rhc->rhc_shook = NULL;
+ return NB_ERR_INCONSISTENCY;
+ }
+ }
+
+ return NB_OK;
+}
+
+int lib_route_map_entry_set_action_rmap_set_action_local_pref_destroy(
+ struct nb_cb_destroy_args *args)
+{
+ switch (args->event) {
+ case NB_EV_VALIDATE:
+ case NB_EV_PREPARE:
+ case NB_EV_ABORT:
+ break;
+ case NB_EV_APPLY:
+ return lib_route_map_entry_set_destroy(args);
+ }
+
+ return NB_OK;
+}
+
+/*
+ * XPath:
+ * /frr-route-map:lib/route-map/entry/set-action/rmap-set-action/frr-bgp-route-map:weight
+ */
+int lib_route_map_entry_set_action_rmap_set_action_weight_modify(
+ struct nb_cb_modify_args *args)
+{
+ struct routemap_hook_context *rhc;
+ const char *type;
+ int rv;
+
+ switch (args->event) {
+ case NB_EV_VALIDATE:
+ case NB_EV_PREPARE:
+ case NB_EV_ABORT:
+ break;
+ case NB_EV_APPLY:
+ /* Add configuration. */
+ rhc = nb_running_get_entry(args->dnode, NULL, true);
+ type = yang_dnode_get_string(args->dnode, NULL);
+
+ /* Set destroy information. */
+ rhc->rhc_shook = generic_set_delete;
+ rhc->rhc_rule = "weight";
+ rhc->rhc_event = RMAP_EVENT_SET_DELETED;
+
+ rv = generic_set_add(rhc->rhc_rmi, "weight", type,
+ args->errmsg, args->errmsg_len);
+ if (rv != CMD_SUCCESS) {
+ rhc->rhc_shook = NULL;
+ return NB_ERR_INCONSISTENCY;
+ }
+ }
+
+ return NB_OK;
+}
+
+int lib_route_map_entry_set_action_rmap_set_action_weight_destroy(
+ struct nb_cb_destroy_args *args)
+{
+ switch (args->event) {
+ case NB_EV_VALIDATE:
+ case NB_EV_PREPARE:
+ case NB_EV_ABORT:
+ break;
+ case NB_EV_APPLY:
+ return lib_route_map_entry_set_destroy(args);
+ }
+
+ return NB_OK;
+}
+
+/*
+ * XPath:
+ * /frr-route-map:lib/route-map/entry/set-action/rmap-set-action/frr-bgp-route-map:origin
+ */
+int lib_route_map_entry_set_action_rmap_set_action_origin_modify(
+ struct nb_cb_modify_args *args)
+{
+ struct routemap_hook_context *rhc;
+ const char *type;
+ int rv;
+
+ switch (args->event) {
+ case NB_EV_VALIDATE:
+ case NB_EV_PREPARE:
+ case NB_EV_ABORT:
+ break;
+ case NB_EV_APPLY:
+ /* Add configuration. */
+ rhc = nb_running_get_entry(args->dnode, NULL, true);
+ type = yang_dnode_get_string(args->dnode, NULL);
+
+ /* Set destroy information. */
+ rhc->rhc_shook = generic_set_delete;
+ rhc->rhc_rule = "origin";
+ rhc->rhc_event = RMAP_EVENT_SET_DELETED;
+
+ rv = generic_set_add(rhc->rhc_rmi, "origin", type,
+ args->errmsg, args->errmsg_len);
+ if (rv != CMD_SUCCESS) {
+ rhc->rhc_shook = NULL;
+ return NB_ERR_INCONSISTENCY;
+ }
+ }
+
+ return NB_OK;
+}
+
+int lib_route_map_entry_set_action_rmap_set_action_origin_destroy(
+ struct nb_cb_destroy_args *args)
+{
+ switch (args->event) {
+ case NB_EV_VALIDATE:
+ case NB_EV_PREPARE:
+ case NB_EV_ABORT:
+ break;
+ case NB_EV_APPLY:
+ return lib_route_map_entry_set_destroy(args);
+
+ }
+
+ return NB_OK;
+}
+
+/*
+ * XPath:
+ * /frr-route-map:lib/route-map/entry/set-action/rmap-set-action/frr-bgp-route-map:originator-id
+ */
+int lib_route_map_entry_set_action_rmap_set_action_originator_id_modify(
+ struct nb_cb_modify_args *args)
+{
+ struct routemap_hook_context *rhc;
+ const char *type;
+ int rv;
+
+ switch (args->event) {
+ case NB_EV_VALIDATE:
+ case NB_EV_PREPARE:
+ case NB_EV_ABORT:
+ break;
+ case NB_EV_APPLY:
+ /* Add configuration. */
+ rhc = nb_running_get_entry(args->dnode, NULL, true);
+ type = yang_dnode_get_string(args->dnode, NULL);
+
+ /* Set destroy information. */
+ rhc->rhc_shook = generic_set_delete;
+ rhc->rhc_rule = "originator-id";
+ rhc->rhc_event = RMAP_EVENT_SET_DELETED;
+
+ rv = generic_set_add(rhc->rhc_rmi, "originator-id", type,
+ args->errmsg, args->errmsg_len);
+ if (rv != CMD_SUCCESS) {
+ rhc->rhc_shook = NULL;
+ return NB_ERR_INCONSISTENCY;
+ }
+ }
+
+ return NB_OK;
+}
+
+int lib_route_map_entry_set_action_rmap_set_action_originator_id_destroy(
+ struct nb_cb_destroy_args *args)
+{
+ switch (args->event) {
+ case NB_EV_VALIDATE:
+ case NB_EV_PREPARE:
+ case NB_EV_ABORT:
+ break;
+ case NB_EV_APPLY:
+ return lib_route_map_entry_set_destroy(args);
+ }
+
+ return NB_OK;
+}
+
+/*
+ * XPath:
+ * /frr-route-map:lib/route-map/entry/set-action/rmap-set-action/frr-bgp-route-map:table
+ */
+int lib_route_map_entry_set_action_rmap_set_action_table_modify(
+ struct nb_cb_modify_args *args)
+{
+ struct routemap_hook_context *rhc;
+ const char *type;
+ int rv;
+
+ switch (args->event) {
+ case NB_EV_VALIDATE:
+ case NB_EV_PREPARE:
+ case NB_EV_ABORT:
+ break;
+ case NB_EV_APPLY:
+ /* Add configuration. */
+ rhc = nb_running_get_entry(args->dnode, NULL, true);
+ type = yang_dnode_get_string(args->dnode, NULL);
+
+ /* Set destroy information. */
+ rhc->rhc_shook = generic_set_delete;
+ rhc->rhc_rule = "table";
+ rhc->rhc_event = RMAP_EVENT_SET_DELETED;
+
+ rv = generic_set_add(rhc->rhc_rmi, "table", type,
+ args->errmsg, args->errmsg_len);
+ if (rv != CMD_SUCCESS) {
+ rhc->rhc_shook = NULL;
+ return NB_ERR_INCONSISTENCY;
+ }
+ }
+
+ return NB_OK;
+}
+
+int lib_route_map_entry_set_action_rmap_set_action_table_destroy(
+ struct nb_cb_destroy_args *args)
+{
+ switch (args->event) {
+ case NB_EV_VALIDATE:
+ case NB_EV_PREPARE:
+ case NB_EV_ABORT:
+ break;
+ case NB_EV_APPLY:
+ return lib_route_map_entry_set_destroy(args);
+ }
+
+ return NB_OK;
+}
+
+/*
+ * XPath:
+ * /frr-route-map:lib/route-map/entry/set-action/rmap-set-action/frr-bgp-route-map:atomic-aggregate
+ */
+int
+lib_route_map_entry_set_action_rmap_set_action_atomic_aggregate_create(
+ struct nb_cb_create_args *args)
+{
+ struct routemap_hook_context *rhc;
+ int rv;
+
+ switch (args->event) {
+ case NB_EV_VALIDATE:
+ case NB_EV_PREPARE:
+ case NB_EV_ABORT:
+ break;
+ case NB_EV_APPLY:
+ /* Add configuration. */
+ rhc = nb_running_get_entry(args->dnode, NULL, true);
+
+ /* Set destroy information. */
+ rhc->rhc_shook = generic_set_delete;
+ rhc->rhc_rule = "atomic-aggregate";
+ rhc->rhc_event = RMAP_EVENT_SET_DELETED;
+
+ rv = generic_set_add(rhc->rhc_rmi, rhc->rhc_rule, NULL,
+ args->errmsg, args->errmsg_len);
+ if (rv != CMD_SUCCESS) {
+ rhc->rhc_shook = NULL;
+ return NB_ERR_INCONSISTENCY;
+ }
+ }
+
+ return NB_OK;
+}
+
+int
+lib_route_map_entry_set_action_rmap_set_action_atomic_aggregate_destroy(
+ struct nb_cb_destroy_args *args)
+{
+ switch (args->event) {
+ case NB_EV_VALIDATE:
+ case NB_EV_PREPARE:
+ case NB_EV_ABORT:
+ break;
+ case NB_EV_APPLY:
+ return lib_route_map_entry_set_destroy(args);
+ }
+
+ return NB_OK;
+}
+
+/*
+ * XPath:
+ * /frr-route-map:lib/route-map/entry/set-action/rmap-set-action/frr-bgp-route-map:prepend-as-path
+ */
+int
+lib_route_map_entry_set_action_rmap_set_action_prepend_as_path_modify(
+ struct nb_cb_modify_args *args)
+{
+ struct routemap_hook_context *rhc;
+ const char *type;
+ int rv;
+
+ switch (args->event) {
+ case NB_EV_VALIDATE:
+ case NB_EV_PREPARE:
+ case NB_EV_ABORT:
+ break;
+ case NB_EV_APPLY:
+ /* Add configuration. */
+ rhc = nb_running_get_entry(args->dnode, NULL, true);
+ type = yang_dnode_get_string(args->dnode, NULL);
+
+ /* Set destroy information. */
+ rhc->rhc_shook = generic_set_delete;
+ rhc->rhc_rule = "as-path prepend";
+ rhc->rhc_event = RMAP_EVENT_SET_DELETED;
+
+ rv = generic_set_add(rhc->rhc_rmi, "as-path prepend",
+ type,
+ args->errmsg, args->errmsg_len);
+ if (rv != CMD_SUCCESS) {
+ rhc->rhc_shook = NULL;
+ return NB_ERR_INCONSISTENCY;
+ }
+ }
+
+ return NB_OK;
+}
+
+int
+lib_route_map_entry_set_action_rmap_set_action_prepend_as_path_destroy(
+ struct nb_cb_destroy_args *args)
+{
+ switch (args->event) {
+ case NB_EV_VALIDATE:
+ case NB_EV_PREPARE:
+ case NB_EV_ABORT:
+ break;
+ case NB_EV_APPLY:
+ return lib_route_map_entry_set_destroy(args);
+ }
+
+ return NB_OK;
+}
+
+/*
+ * XPath:
+ * /frr-route-map:lib/route-map/entry/set-action/rmap-set-action/frr-bgp-route-map:last-as
+ */
+int lib_route_map_entry_set_action_rmap_set_action_last_as_modify(
+ struct nb_cb_modify_args *args)
+{
+ struct routemap_hook_context *rhc;
+ const char *value;
+ char *argstr;
+ int rv;
+
+ switch (args->event) {
+ case NB_EV_VALIDATE:
+ case NB_EV_PREPARE:
+ case NB_EV_ABORT:
+ break;
+ case NB_EV_APPLY:
+ /* Add configuration. */
+ rhc = nb_running_get_entry(args->dnode, NULL, true);
+ value = yang_dnode_get_string(args->dnode, NULL);
+
+ /* Set destroy information. */
+ rhc->rhc_shook = generic_set_delete;
+ rhc->rhc_rule = "as-path prepend";
+ rhc->rhc_event = RMAP_EVENT_SET_DELETED;
+
+ argstr = XMALLOC(MTYPE_ROUTE_MAP_COMPILED,
+ strlen(value) + strlen("last-as") + 2);
+
+ snprintf(argstr, (strlen(value) + strlen("last-as") + 2),
+ "last-as %s", value);
+
+ rv = generic_set_add(rhc->rhc_rmi, "as-path prepend",
+ argstr,
+ args->errmsg, args->errmsg_len);
+ if (rv != CMD_SUCCESS) {
+ rhc->rhc_shook = NULL;
+ XFREE(MTYPE_ROUTE_MAP_COMPILED, argstr);
+ return NB_ERR_INCONSISTENCY;
+ }
+
+ XFREE(MTYPE_ROUTE_MAP_COMPILED, argstr);
+ }
+
+ return NB_OK;
+}
+
+int lib_route_map_entry_set_action_rmap_set_action_last_as_destroy(
+ struct nb_cb_destroy_args *args)
+{
+ switch (args->event) {
+ case NB_EV_VALIDATE:
+ case NB_EV_PREPARE:
+ case NB_EV_ABORT:
+ break;
+ case NB_EV_APPLY:
+ return lib_route_map_entry_set_destroy(args);
+ }
+
+ return NB_OK;
+}
+
+/*
+ * XPath:
+ * /frr-route-map:lib/route-map/entry/set-action/rmap-set-action/frr-bgp-route-map:exclude-as-path
+ */
+int
+lib_route_map_entry_set_action_rmap_set_action_exclude_as_path_modify(
+ struct nb_cb_modify_args *args)
+{
+ struct routemap_hook_context *rhc;
+ const char *type;
+ int rv;
+
+ switch (args->event) {
+ case NB_EV_VALIDATE:
+ case NB_EV_PREPARE:
+ case NB_EV_ABORT:
+ break;
+ case NB_EV_APPLY:
+ /* Add configuration. */
+ rhc = nb_running_get_entry(args->dnode, NULL, true);
+ type = yang_dnode_get_string(args->dnode, NULL);
+
+ /* Set destroy information. */
+ rhc->rhc_shook = generic_set_delete;
+ rhc->rhc_rule = "as-path exclude";
+ rhc->rhc_event = RMAP_EVENT_SET_DELETED;
+
+ rv = generic_set_add(rhc->rhc_rmi, "as-path exclude",
+ type,
+ args->errmsg, args->errmsg_len);
+ if (rv != CMD_SUCCESS) {
+ rhc->rhc_shook = NULL;
+ return NB_ERR_INCONSISTENCY;
+ }
+ }
+
+ return NB_OK;
+}
+
+int
+lib_route_map_entry_set_action_rmap_set_action_exclude_as_path_destroy(
+ struct nb_cb_destroy_args *args)
+{
+ switch (args->event) {
+ case NB_EV_VALIDATE:
+ case NB_EV_PREPARE:
+ case NB_EV_ABORT:
+ break;
+ case NB_EV_APPLY:
+ return lib_route_map_entry_set_destroy(args);
+ }
+
+ return NB_OK;
+}
+
+/*
+ * XPath:
+ * /frr-route-map:lib/route-map/entry/set-action/rmap-set-action/frr-bgp-route-map:replace-as-path
+ */
+int lib_route_map_entry_set_action_rmap_set_action_replace_as_path_modify(
+ struct nb_cb_modify_args *args)
+{
+ struct routemap_hook_context *rhc;
+ const char *type;
+ int rv;
+
+ switch (args->event) {
+ case NB_EV_VALIDATE:
+ case NB_EV_PREPARE:
+ case NB_EV_ABORT:
+ break;
+ case NB_EV_APPLY:
+ /* Add configuration. */
+ rhc = nb_running_get_entry(args->dnode, NULL, true);
+ type = yang_dnode_get_string(args->dnode, NULL);
+
+ /* Set destroy information. */
+ rhc->rhc_shook = generic_set_delete;
+ rhc->rhc_rule = "as-path replace";
+ rhc->rhc_event = RMAP_EVENT_SET_DELETED;
+
+ rv = generic_set_add(rhc->rhc_rmi, "as-path replace", type,
+ args->errmsg, args->errmsg_len);
+ if (rv != CMD_SUCCESS) {
+ rhc->rhc_shook = NULL;
+ return NB_ERR_INCONSISTENCY;
+ }
+ }
+
+ return NB_OK;
+}
+
+int lib_route_map_entry_set_action_rmap_set_action_replace_as_path_destroy(
+ struct nb_cb_destroy_args *args)
+{
+ switch (args->event) {
+ case NB_EV_VALIDATE:
+ case NB_EV_PREPARE:
+ case NB_EV_ABORT:
+ break;
+ case NB_EV_APPLY:
+ return lib_route_map_entry_set_destroy(args);
+ }
+
+ return NB_OK;
+}
+
+/*
+ * XPath:
+ * /frr-route-map:lib/route-map/entry/set-action/rmap-set-action/frr-bgp-route-map:community-none
+ */
+int lib_route_map_entry_set_action_rmap_set_action_community_none_modify(
+ struct nb_cb_modify_args *args)
+{
+ struct routemap_hook_context *rhc;
+ bool none = false;
+ int rv;
+
+ switch (args->event) {
+ case NB_EV_VALIDATE:
+ case NB_EV_PREPARE:
+ case NB_EV_ABORT:
+ break;
+ case NB_EV_APPLY:
+ /* Add configuration. */
+ rhc = nb_running_get_entry(args->dnode, NULL, true);
+ none = yang_dnode_get_bool(args->dnode, NULL);
+
+ /* Set destroy information. */
+ rhc->rhc_shook = generic_set_delete;
+ rhc->rhc_rule = "community";
+ rhc->rhc_event = RMAP_EVENT_SET_DELETED;
+
+ if (none) {
+ rv = generic_set_add(rhc->rhc_rmi, "community",
+ "none",
+ args->errmsg, args->errmsg_len);
+ if (rv != CMD_SUCCESS) {
+ rhc->rhc_shook = NULL;
+ return NB_ERR_INCONSISTENCY;
+ }
+ return NB_OK;
+ }
+
+ return NB_ERR_INCONSISTENCY;
+ }
+
+ return NB_OK;
+}
+
+int
+lib_route_map_entry_set_action_rmap_set_action_community_none_destroy(
+ struct nb_cb_destroy_args *args)
+{
+ switch (args->event) {
+ case NB_EV_VALIDATE:
+ case NB_EV_PREPARE:
+ case NB_EV_ABORT:
+ break;
+ case NB_EV_APPLY:
+ return lib_route_map_entry_set_destroy(args);
+ }
+
+ return NB_OK;
+}
+
+/*
+ * XPath:
+ * /frr-route-map:lib/route-map/entry/set-action/rmap-set-action/frr-bgp-route-map:community-string
+ */
+int
+lib_route_map_entry_set_action_rmap_set_action_community_string_modify(
+ struct nb_cb_modify_args *args)
+{
+ struct routemap_hook_context *rhc;
+ const char *type;
+ int rv;
+
+ switch (args->event) {
+ case NB_EV_VALIDATE:
+ case NB_EV_PREPARE:
+ case NB_EV_ABORT:
+ break;
+ case NB_EV_APPLY:
+ /* Add configuration. */
+ rhc = nb_running_get_entry(args->dnode, NULL, true);
+ type = yang_dnode_get_string(args->dnode, NULL);
+
+ /* Set destroy information. */
+ rhc->rhc_shook = generic_set_delete;
+ rhc->rhc_rule = "community";
+ rhc->rhc_event = RMAP_EVENT_SET_DELETED;
+
+ rv = generic_set_add(rhc->rhc_rmi, "community", type,
+ args->errmsg, args->errmsg_len);
+ if (rv != CMD_SUCCESS) {
+ rhc->rhc_shook = NULL;
+ return NB_ERR_INCONSISTENCY;
+ }
+ }
+
+ return NB_OK;
+}
+
+int
+lib_route_map_entry_set_action_rmap_set_action_community_string_destroy(
+ struct nb_cb_destroy_args *args)
+{
+ switch (args->event) {
+ case NB_EV_VALIDATE:
+ case NB_EV_PREPARE:
+ case NB_EV_ABORT:
+ break;
+ case NB_EV_APPLY:
+ return lib_route_map_entry_set_destroy(args);
+ }
+
+ return NB_OK;
+}
+
+/*
+ * XPath:
+ * /frr-route-map:lib/route-map/entry/set-action/rmap-set-action/frr-bgp-route-map:large-community-none
+ */
+int
+lib_route_map_entry_set_action_rmap_set_action_large_community_none_modify(
+ struct nb_cb_modify_args *args)
+{
+ struct routemap_hook_context *rhc;
+ bool none = false;
+ int rv;
+
+ switch (args->event) {
+ case NB_EV_VALIDATE:
+ case NB_EV_PREPARE:
+ case NB_EV_ABORT:
+ break;
+ case NB_EV_APPLY:
+ /* Add configuration. */
+ rhc = nb_running_get_entry(args->dnode, NULL, true);
+ none = yang_dnode_get_bool(args->dnode, NULL);
+
+ /* Set destroy information. */
+ rhc->rhc_shook = generic_set_delete;
+ rhc->rhc_rule = "large-community";
+ rhc->rhc_event = RMAP_EVENT_SET_DELETED;
+
+ if (none) {
+ rv = generic_set_add(rhc->rhc_rmi,
+ "large-community",
+ "none",
+ args->errmsg, args->errmsg_len);
+ if (rv != CMD_SUCCESS) {
+ rhc->rhc_shook = NULL;
+ return NB_ERR_INCONSISTENCY;
+ }
+ return NB_OK;
+ }
+
+ return NB_ERR_INCONSISTENCY;
+ }
+
+ return NB_OK;
+}
+
+int
+lib_route_map_entry_set_action_rmap_set_action_large_community_none_destroy(
+ struct nb_cb_destroy_args *args)
+{
+ switch (args->event) {
+ case NB_EV_VALIDATE:
+ case NB_EV_PREPARE:
+ case NB_EV_ABORT:
+ break;
+ case NB_EV_APPLY:
+ return lib_route_map_entry_set_destroy(args);
+ }
+
+ return NB_OK;
+}
+
+/*
+ * XPath:
+ * /frr-route-map:lib/route-map/entry/set-action/rmap-set-action/frr-bgp-route-map:large-community-string
+ */
+int
+lib_route_map_entry_set_action_rmap_set_action_large_community_string_modify(
+ struct nb_cb_modify_args *args)
+{
+ struct routemap_hook_context *rhc;
+ const char *type;
+ int rv;
+
+ switch (args->event) {
+ case NB_EV_VALIDATE:
+ case NB_EV_PREPARE:
+ case NB_EV_ABORT:
+ break;
+ case NB_EV_APPLY:
+ /* Add configuration. */
+ rhc = nb_running_get_entry(args->dnode, NULL, true);
+ type = yang_dnode_get_string(args->dnode, NULL);
+
+ /* Set destroy information. */
+ rhc->rhc_shook = generic_set_delete;
+ rhc->rhc_rule = "large-community";
+ rhc->rhc_event = RMAP_EVENT_SET_DELETED;
+
+ rv = generic_set_add(rhc->rhc_rmi, "large-community",
+ type,
+ args->errmsg, args->errmsg_len);
+ if (rv != CMD_SUCCESS) {
+ rhc->rhc_shook = NULL;
+ return NB_ERR_INCONSISTENCY;
+ }
+ }
+
+ return NB_OK;
+}
+
+int
+lib_route_map_entry_set_action_rmap_set_action_large_community_string_destroy(
+ struct nb_cb_destroy_args *args)
+{
+ switch (args->event) {
+ case NB_EV_VALIDATE:
+ case NB_EV_PREPARE:
+ case NB_EV_ABORT:
+ break;
+ case NB_EV_APPLY:
+ return lib_route_map_entry_set_destroy(args);
+ }
+
+ return NB_OK;
+}
+
+/*
+ * xpath =
+ * /frr-route-map:lib/route-map/entry/set-action/rmap-set-action/frr-bgp-route-map:aggregator
+ */
+void lib_route_map_entry_set_action_rmap_set_action_aggregator_finish(
+ struct nb_cb_apply_finish_args *args)
+{
+ struct routemap_hook_context *rhc;
+ const char *asn;
+ const char *addr;
+ char *argstr;
+ int ret;
+
+ /* Add configuration. */
+ rhc = nb_running_get_entry(args->dnode, NULL, true);
+ asn = yang_dnode_get_string(args->dnode, "./aggregator-asn");
+ addr = yang_dnode_get_string(args->dnode, "./aggregator-address");
+
+ argstr = XMALLOC(MTYPE_ROUTE_MAP_COMPILED,
+ strlen(asn) + strlen(addr) + 2);
+
+ snprintf(argstr, (strlen(asn) + strlen(addr) + 2), "%s %s", asn, addr);
+
+ /* Set destroy information. */
+ rhc->rhc_shook = generic_set_delete;
+ rhc->rhc_rule = "aggregator as";
+ rhc->rhc_event = RMAP_EVENT_SET_DELETED;
+
+ ret = generic_set_add(rhc->rhc_rmi, rhc->rhc_rule, argstr, args->errmsg,
+ args->errmsg_len);
+ /*
+ * At this point if this is not a successful operation
+ * bgpd is about to crash. Let's just cut to the
+ * chase and do it.
+ */
+ assert(ret == CMD_SUCCESS);
+
+ XFREE(MTYPE_ROUTE_MAP_COMPILED, argstr);
+}
+/*
+ * XPath:
+ * /frr-route-map:lib/route-map/entry/set-action/rmap-set-action/frr-bgp-route-map:aggregator/aggregator-asn
+ */
+int
+lib_route_map_entry_set_action_rmap_set_action_aggregator_aggregator_asn_modify(
+ struct nb_cb_modify_args *args)
+{
+ switch (args->event) {
+ case NB_EV_VALIDATE:
+ case NB_EV_PREPARE:
+ case NB_EV_ABORT:
+ case NB_EV_APPLY:
+ break;
+ }
+
+ return NB_OK;
+}
+
+int
+lib_route_map_entry_set_action_rmap_set_action_aggregator_aggregator_asn_destroy(
+ struct nb_cb_destroy_args *args)
+{
+ switch (args->event) {
+ case NB_EV_VALIDATE:
+ case NB_EV_PREPARE:
+ case NB_EV_ABORT:
+ break;
+ case NB_EV_APPLY:
+ return lib_route_map_entry_set_destroy(args);
+ }
+
+ return NB_OK;
+}
+
+/*
+ * XPath:
+ * /frr-route-map:lib/route-map/entry/set-action/rmap-set-action/frr-bgp-route-map:aggregator/aggregator-address
+ */
+int
+lib_route_map_entry_set_action_rmap_set_action_aggregator_aggregator_address_modify(
+ struct nb_cb_modify_args *args)
+{
+ switch (args->event) {
+ case NB_EV_VALIDATE:
+ case NB_EV_PREPARE:
+ case NB_EV_ABORT:
+ case NB_EV_APPLY:
+ break;
+ }
+
+ return NB_OK;
+}
+
+int
+lib_route_map_entry_set_action_rmap_set_action_aggregator_aggregator_address_destroy(
+ struct nb_cb_destroy_args *args)
+{
+ switch (args->event) {
+ case NB_EV_VALIDATE:
+ case NB_EV_PREPARE:
+ case NB_EV_ABORT:
+ break;
+ case NB_EV_APPLY:
+ return lib_route_map_entry_set_destroy(args);
+ }
+
+ return NB_OK;
+}
+
+/*
+ * XPath:
+ * /frr-route-map:lib/route-map/entry/set-action/rmap-set-action/frr-bgp-route-map:comm-list-name
+ */
+int lib_route_map_entry_set_action_rmap_set_action_comm_list_name_modify(
+ struct nb_cb_modify_args *args)
+{
+ struct routemap_hook_context *rhc;
+ const char *value;
+ const char *action;
+ int rv = CMD_SUCCESS;
+
+ switch (args->event) {
+ case NB_EV_VALIDATE:
+ case NB_EV_PREPARE:
+ case NB_EV_ABORT:
+ break;
+ case NB_EV_APPLY:
+ /* Add configuration. */
+ rhc = nb_running_get_entry(args->dnode, NULL, true);
+ value = yang_dnode_get_string(args->dnode, NULL);
+
+ /* Set destroy information. */
+ rhc->rhc_shook = generic_set_delete;
+
+ action = yang_dnode_get_string(args->dnode,
+ "../../frr-route-map:action");
+ if (IS_SET_COMM_LIST_DEL(action))
+ rhc->rhc_rule = "comm-list";
+ else
+ rhc->rhc_rule = "large-comm-list";
+
+ rhc->rhc_event = RMAP_EVENT_SET_DELETED;
+
+ rv = generic_set_add(rhc->rhc_rmi, rhc->rhc_rule, value,
+ args->errmsg, args->errmsg_len);
+
+ if (rv != CMD_SUCCESS) {
+ rhc->rhc_shook = NULL;
+ return NB_ERR_INCONSISTENCY;
+ }
+ }
+
+ return NB_OK;
+}
+
+int
+lib_route_map_entry_set_action_rmap_set_action_comm_list_name_destroy(
+ struct nb_cb_destroy_args *args)
+{
+ switch (args->event) {
+ case NB_EV_VALIDATE:
+ case NB_EV_PREPARE:
+ case NB_EV_ABORT:
+ break;
+ case NB_EV_APPLY:
+ return lib_route_map_entry_set_destroy(args);
+ }
+
+ return NB_OK;
+}
+
+/*
+ * XPath:
+ * /frr-route-map:lib/route-map/entry/set-action/rmap-set-action/frr-bgp-route-map:extcommunity-lb
+ */
+void
+lib_route_map_entry_set_action_rmap_set_action_extcommunity_lb_finish(
+ struct nb_cb_apply_finish_args *args)
+{
+ struct routemap_hook_context *rhc;
+ enum ecommunity_lb_type lb_type;
+ char str[VTY_BUFSIZ];
+ uint16_t bandwidth;
+ int ret;
+
+ /* Add configuration. */
+ rhc = nb_running_get_entry(args->dnode, NULL, true);
+ lb_type = yang_dnode_get_enum(args->dnode, "./lb-type");
+
+ /* Set destroy information. */
+ rhc->rhc_shook = generic_set_delete;
+ rhc->rhc_rule = "extcommunity bandwidth";
+ rhc->rhc_event = RMAP_EVENT_SET_DELETED;
+
+ switch (lb_type) {
+ case EXPLICIT_BANDWIDTH:
+ bandwidth = yang_dnode_get_uint16(args->dnode, "./bandwidth");
+ snprintf(str, sizeof(str), "%d", bandwidth);
+ break;
+ case CUMULATIVE_BANDWIDTH:
+ snprintf(str, sizeof(str), "%s", "cumulative");
+ break;
+ case COMPUTED_BANDWIDTH:
+ snprintf(str, sizeof(str), "%s", "num-multipaths");
+ }
+
+ if (yang_dnode_get_bool(args->dnode, "./two-octet-as-specific"))
+ strlcat(str, " non-transitive", sizeof(str));
+
+ ret = generic_set_add(rhc->rhc_rmi, "extcommunity bandwidth", str,
+ args->errmsg, args->errmsg_len);
+ /*
+ * At this point if this is not a successful operation
+ * bgpd is about to crash. Let's just cut to the
+ * chase and do it.
+ */
+ assert(ret == CMD_SUCCESS);
+}
+
+/*
+ * XPath:
+ * /frr-route-map:lib/route-map/entry/set-action/rmap-set-action/frr-bgp-route-map:extcommunity-lb/lb-type
+ */
+int
+lib_route_map_entry_set_action_rmap_set_action_extcommunity_lb_lb_type_modify(
+ struct nb_cb_modify_args *args)
+{
+ return NB_OK;
+}
+
+int
+lib_route_map_entry_set_action_rmap_set_action_extcommunity_lb_lb_type_destroy(
+ struct nb_cb_destroy_args *args)
+{
+ return lib_route_map_entry_set_destroy(args);
+}
+
+/*
+ * XPath:
+ * /frr-route-map:lib/route-map/entry/set-action/rmap-set-action/frr-bgp-route-map:extcommunity-lb/bandwidth
+ */
+int
+lib_route_map_entry_set_action_rmap_set_action_extcommunity_lb_bandwidth_modify(
+ struct nb_cb_modify_args *args)
+{
+ return NB_OK;
+}
+
+int
+lib_route_map_entry_set_action_rmap_set_action_extcommunity_lb_bandwidth_destroy(
+ struct nb_cb_destroy_args *args)
+{
+ return lib_route_map_entry_set_destroy(args);
+}
+
+/*
+ * XPath:
+ * /frr-route-map:lib/route-map/entry/set-action/rmap-set-action/frr-bgp-route-map:extcommunity-lb/two-octet-as-specific
+ */
+int
+lib_route_map_entry_set_action_rmap_set_action_extcommunity_lb_two_octet_as_specific_modify(
+ struct nb_cb_modify_args *args)
+{
+ return NB_OK;
+}
+
+int
+lib_route_map_entry_set_action_rmap_set_action_extcommunity_lb_two_octet_as_specific_destroy(
+ struct nb_cb_destroy_args *args)
+{
+ return lib_route_map_entry_set_destroy(args);
+}
+
+/*
+ * XPath:
+ * /frr-route-map:lib/route-map/entry/set-action/rmap-set-action/frr-bgp-route-map:extcommunity-none
+ */
+int lib_route_map_entry_set_action_rmap_set_action_extcommunity_none_modify(
+ struct nb_cb_modify_args *args)
+{
+ struct routemap_hook_context *rhc;
+ bool none = false;
+ int rv;
+
+ switch (args->event) {
+ case NB_EV_VALIDATE:
+ case NB_EV_PREPARE:
+ case NB_EV_ABORT:
+ break;
+ case NB_EV_APPLY:
+ /* Add configuration. */
+ rhc = nb_running_get_entry(args->dnode, NULL, true);
+ none = yang_dnode_get_bool(args->dnode, NULL);
+
+ /* Set destroy information. */
+ rhc->rhc_shook = generic_set_delete;
+ rhc->rhc_rule = "extcommunity";
+ rhc->rhc_event = RMAP_EVENT_SET_DELETED;
+
+ if (none) {
+ rv = generic_set_add(rhc->rhc_rmi, "extcommunity",
+ "none", args->errmsg,
+ args->errmsg_len);
+ if (rv != CMD_SUCCESS) {
+ rhc->rhc_shook = NULL;
+ return NB_ERR_INCONSISTENCY;
+ }
+ return NB_OK;
+ }
+
+ return NB_ERR_INCONSISTENCY;
+ }
+
+ return NB_OK;
+}
+
+int lib_route_map_entry_set_action_rmap_set_action_extcommunity_none_destroy(
+ struct nb_cb_destroy_args *args)
+{
+ switch (args->event) {
+ case NB_EV_VALIDATE:
+ case NB_EV_PREPARE:
+ case NB_EV_ABORT:
+ break;
+ case NB_EV_APPLY:
+ return lib_route_map_entry_set_destroy(args);
+ }
+
+ return NB_OK;
+}
+
+/*
+ * XPath:
+ * /frr-route-map:lib/route-map/entry/set-action/rmap-set-action/frr-bgp-route-map:evpn-gateway-ip-ipv4
+ */
+int lib_route_map_entry_set_action_rmap_set_action_evpn_gateway_ip_ipv4_modify(
+ struct nb_cb_modify_args *args)
+{
+ struct routemap_hook_context *rhc;
+ const char *type;
+ int rv;
+
+ switch (args->event) {
+ case NB_EV_VALIDATE:
+ case NB_EV_PREPARE:
+ case NB_EV_ABORT:
+ break;
+ case NB_EV_APPLY:
+ /* Add configuration. */
+ rhc = nb_running_get_entry(args->dnode, NULL, true);
+ type = yang_dnode_get_string(args->dnode, NULL);
+
+ /* Set destroy information. */
+ rhc->rhc_shook = generic_set_delete;
+ rhc->rhc_rule = "evpn gateway-ip ipv4";
+ rhc->rhc_event = RMAP_EVENT_SET_DELETED;
+
+ rv = generic_set_add(rhc->rhc_rmi, "evpn gateway-ip ipv4", type,
+ args->errmsg, args->errmsg_len);
+ if (rv != CMD_SUCCESS) {
+ rhc->rhc_shook = NULL;
+ return NB_ERR_INCONSISTENCY;
+ }
+ }
+
+ return NB_OK;
+}
+
+int lib_route_map_entry_set_action_rmap_set_action_evpn_gateway_ip_ipv4_destroy(
+ struct nb_cb_destroy_args *args)
+{
+ switch (args->event) {
+ case NB_EV_VALIDATE:
+ case NB_EV_PREPARE:
+ case NB_EV_ABORT:
+ break;
+ case NB_EV_APPLY:
+ return lib_route_map_entry_set_destroy(args);
+ }
+
+ return NB_OK;
+}
+
+/*
+ * XPath:
+ * /frr-route-map:lib/route-map/entry/set-action/rmap-set-action/frr-bgp-route-map:evpn-gateway-ip-ipv6
+ */
+int lib_route_map_entry_set_action_rmap_set_action_evpn_gateway_ip_ipv6_modify(
+ struct nb_cb_modify_args *args)
+{
+ struct routemap_hook_context *rhc;
+ const char *type;
+ int rv;
+
+ switch (args->event) {
+ case NB_EV_VALIDATE:
+ case NB_EV_PREPARE:
+ case NB_EV_ABORT:
+ break;
+ case NB_EV_APPLY:
+ /* Add configuration. */
+ rhc = nb_running_get_entry(args->dnode, NULL, true);
+ type = yang_dnode_get_string(args->dnode, NULL);
+
+ /* Set destroy information. */
+ rhc->rhc_shook = generic_set_delete;
+ rhc->rhc_rule = "evpn gateway-ip ipv6";
+ rhc->rhc_event = RMAP_EVENT_SET_DELETED;
+
+ rv = generic_set_add(rhc->rhc_rmi, "evpn gateway-ip ipv6", type,
+ args->errmsg, args->errmsg_len);
+ if (rv != CMD_SUCCESS) {
+ rhc->rhc_shook = NULL;
+ return NB_ERR_INCONSISTENCY;
+ }
+ }
+
+ return NB_OK;
+}
+
+int lib_route_map_entry_set_action_rmap_set_action_evpn_gateway_ip_ipv6_destroy(
+ struct nb_cb_destroy_args *args)
+{
+ switch (args->event) {
+ case NB_EV_VALIDATE:
+ case NB_EV_PREPARE:
+ case NB_EV_ABORT:
+ break;
+ case NB_EV_APPLY:
+ return lib_route_map_entry_set_destroy(args);
+ }
+
+ return NB_OK;
+}
+
+/*
+ * XPath:
+ * /frr-route-map:lib/route-map/entry/set-action/rmap-set-action/l3vpn-nexthop-encapsulation
+ */
+int lib_route_map_entry_set_action_rmap_set_action_l3vpn_nexthop_encapsulation_modify(
+ struct nb_cb_modify_args *args)
+{
+ struct routemap_hook_context *rhc;
+ const char *type;
+ int rv;
+
+ switch (args->event) {
+ case NB_EV_VALIDATE:
+ case NB_EV_PREPARE:
+ case NB_EV_ABORT:
+ break;
+ case NB_EV_APPLY:
+ /* Add configuration. */
+ rhc = nb_running_get_entry(args->dnode, NULL, true);
+ type = yang_dnode_get_string(args->dnode, NULL);
+
+ /* Set destroy information. */
+ rhc->rhc_shook = generic_set_delete;
+ rhc->rhc_rule = "l3vpn next-hop encapsulation";
+ rhc->rhc_event = RMAP_EVENT_SET_DELETED;
+
+ rv = generic_set_add(rhc->rhc_rmi,
+ "l3vpn next-hop encapsulation", type,
+ args->errmsg, args->errmsg_len);
+ if (rv != CMD_SUCCESS) {
+ rhc->rhc_shook = NULL;
+ return NB_ERR_INCONSISTENCY;
+ }
+ }
+
+ return NB_OK;
+}
+
+int lib_route_map_entry_set_action_rmap_set_action_l3vpn_nexthop_encapsulation_destroy(
+ struct nb_cb_destroy_args *args)
+{
+ switch (args->event) {
+ case NB_EV_VALIDATE:
+ case NB_EV_PREPARE:
+ case NB_EV_ABORT:
+ break;
+ case NB_EV_APPLY:
+ return lib_route_map_entry_set_destroy(args);
+ }
+
+ return NB_OK;
+}
diff --git a/bgpd/bgp_rpki.c b/bgpd/bgp_rpki.c
new file mode 100644
index 0000000..5743e5d
--- /dev/null
+++ b/bgpd/bgp_rpki.c
@@ -0,0 +1,1747 @@
+/*
+ * BGP RPKI
+ * Copyright (C) 2013 Michael Mester (m.mester@fu-berlin.de), for FU Berlin
+ * Copyright (C) 2014-2017 Andreas Reuter (andreas.reuter@fu-berlin.de), for FU
+ * Berlin
+ * Copyright (C) 2016-2017 Colin Sames (colin.sames@haw-hamburg.de), for HAW
+ * Hamburg
+ * Copyright (C) 2017-2018 Marcel Röthke (marcel.roethke@haw-hamburg.de),
+ * for HAW Hamburg
+ *
+ * This file is part of FRRouting.
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License as published by the Free
+ * Software Foundation; either version 2 of the License, or (at your option)
+ * any later version.
+ *
+ * This program is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
+ * more details.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program; see the file COPYING; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
+ */
+
+/* If rtrlib compiled with ssh support, don`t fail build */
+#define LIBSSH_LEGACY_0_4
+
+#include <zebra.h>
+#include <pthread.h>
+#include <time.h>
+#include <stdbool.h>
+#include <stdlib.h>
+#include "prefix.h"
+#include "log.h"
+#include "command.h"
+#include "linklist.h"
+#include "memory.h"
+#include "thread.h"
+#include "filter.h"
+#include "bgpd/bgpd.h"
+#include "bgpd/bgp_table.h"
+#include "bgp_advertise.h"
+#include "bgpd/bgp_debug.h"
+#include "bgpd/bgp_attr.h"
+#include "bgpd/bgp_aspath.h"
+#include "bgpd/bgp_route.h"
+#include "bgpd/bgp_rpki.h"
+#include "northbound_cli.h"
+
+#include "lib/network.h"
+#include "lib/thread.h"
+#ifndef VTYSH_EXTRACT_PL
+#include "rtrlib/rtrlib.h"
+#endif
+#include "hook.h"
+#include "libfrr.h"
+#include "lib/version.h"
+
+#ifndef VTYSH_EXTRACT_PL
+#include "bgpd/bgp_rpki_clippy.c"
+#endif
+
+DEFINE_MTYPE_STATIC(BGPD, BGP_RPKI_CACHE, "BGP RPKI Cache server");
+DEFINE_MTYPE_STATIC(BGPD, BGP_RPKI_CACHE_GROUP, "BGP RPKI Cache server group");
+DEFINE_MTYPE_STATIC(BGPD, BGP_RPKI_RTRLIB, "BGP RPKI RTRLib");
+
+#define POLLING_PERIOD_DEFAULT 3600
+#define EXPIRE_INTERVAL_DEFAULT 7200
+#define RETRY_INTERVAL_DEFAULT 600
+#define BGP_RPKI_CACHE_SERVER_SYNC_RETRY_TIMEOUT 3
+
+static struct thread *t_rpki_sync;
+
+#define RPKI_DEBUG(...) \
+ if (rpki_debug) { \
+ zlog_debug("RPKI: " __VA_ARGS__); \
+ }
+
+#define RPKI_OUTPUT_STRING "Control rpki specific settings\n"
+
+struct cache {
+ enum { TCP, SSH } type;
+ struct tr_socket *tr_socket;
+ union {
+ struct tr_tcp_config *tcp_config;
+ struct tr_ssh_config *ssh_config;
+ } tr_config;
+ struct rtr_socket *rtr_socket;
+ uint8_t preference;
+};
+
+enum return_values { SUCCESS = 0, ERROR = -1 };
+
+struct rpki_for_each_record_arg {
+ struct vty *vty;
+ unsigned int *prefix_amount;
+ as_t as;
+ json_object *json;
+};
+
+static int start(void);
+static void stop(void);
+static int reset(bool force);
+static struct rtr_mgr_group *get_connected_group(void);
+static void print_prefix_table(struct vty *vty, json_object *json);
+static void install_cli_commands(void);
+static int config_write(struct vty *vty);
+static int config_on_exit(struct vty *vty);
+static void free_cache(struct cache *cache);
+static struct rtr_mgr_group *get_groups(void);
+#if defined(FOUND_SSH)
+static int add_ssh_cache(const char *host, const unsigned int port,
+ const char *username, const char *client_privkey_path,
+ const char *server_pubkey_path,
+ const uint8_t preference, const char *bindaddr);
+#endif
+static struct rtr_socket *create_rtr_socket(struct tr_socket *tr_socket);
+static struct cache *find_cache(const uint8_t preference);
+static void rpki_delete_all_cache_nodes(void);
+static int add_tcp_cache(const char *host, const char *port,
+ const uint8_t preference, const char *bindaddr);
+static void print_record(const struct pfx_record *record, struct vty *vty,
+ json_object *json);
+static bool is_synchronized(void);
+static bool is_running(void);
+static bool is_stopping(void);
+static void route_match_free(void *rule);
+static enum route_map_cmd_result_t route_match(void *rule,
+ const struct prefix *prefix,
+
+ void *object);
+static void *route_match_compile(const char *arg);
+static void revalidate_bgp_node(struct bgp_dest *dest, afi_t afi, safi_t safi);
+static void revalidate_all_routes(void);
+
+static struct rtr_mgr_config *rtr_config;
+static struct list *cache_list;
+static bool rtr_is_running;
+static bool rtr_is_stopping;
+static bool rtr_is_synced;
+static _Atomic int rtr_update_overflow;
+static bool rpki_debug;
+static unsigned int polling_period;
+static unsigned int expire_interval;
+static unsigned int retry_interval;
+static int rpki_sync_socket_rtr;
+static int rpki_sync_socket_bgpd;
+
+static struct cmd_node rpki_node = {
+ .name = "rpki",
+ .node = RPKI_NODE,
+ .parent_node = CONFIG_NODE,
+ .prompt = "%s(config-rpki)# ",
+ .config_write = config_write,
+ .node_exit = config_on_exit,
+};
+static const struct route_map_rule_cmd route_match_rpki_cmd = {
+ "rpki", route_match, route_match_compile, route_match_free};
+
+static void *malloc_wrapper(size_t size)
+{
+ return XMALLOC(MTYPE_BGP_RPKI_RTRLIB, size);
+}
+
+static void *realloc_wrapper(void *ptr, size_t size)
+{
+ return XREALLOC(MTYPE_BGP_RPKI_RTRLIB, ptr, size);
+}
+
+static void free_wrapper(void *ptr)
+{
+ XFREE(MTYPE_BGP_RPKI_RTRLIB, ptr);
+}
+
+static void init_tr_socket(struct cache *cache)
+{
+ if (cache->type == TCP)
+ tr_tcp_init(cache->tr_config.tcp_config,
+ cache->tr_socket);
+#if defined(FOUND_SSH)
+ else
+ tr_ssh_init(cache->tr_config.ssh_config,
+ cache->tr_socket);
+#endif
+}
+
+static void free_tr_socket(struct cache *cache)
+{
+ if (cache->type == TCP)
+ tr_tcp_init(cache->tr_config.tcp_config,
+ cache->tr_socket);
+#if defined(FOUND_SSH)
+ else
+ tr_ssh_init(cache->tr_config.ssh_config,
+ cache->tr_socket);
+#endif
+}
+
+static int rpki_validate_prefix(struct peer *peer, struct attr *attr,
+ const struct prefix *prefix);
+
+static void ipv6_addr_to_network_byte_order(const uint32_t *src, uint32_t *dest)
+{
+ int i;
+
+ for (i = 0; i < 4; i++)
+ dest[i] = htonl(src[i]);
+}
+
+static void ipv6_addr_to_host_byte_order(const uint32_t *src, uint32_t *dest)
+{
+ int i;
+
+ for (i = 0; i < 4; i++)
+ dest[i] = ntohl(src[i]);
+}
+
+static enum route_map_cmd_result_t route_match(void *rule,
+ const struct prefix *prefix,
+ void *object)
+{
+ int *rpki_status = rule;
+ struct bgp_path_info *path;
+
+ path = object;
+
+ if (rpki_validate_prefix(path->peer, path->attr, prefix)
+ == *rpki_status) {
+ return RMAP_MATCH;
+ }
+
+ return RMAP_NOMATCH;
+}
+
+static void *route_match_compile(const char *arg)
+{
+ int *rpki_status;
+
+ rpki_status = XMALLOC(MTYPE_ROUTE_MAP_COMPILED, sizeof(int));
+
+ if (strcmp(arg, "valid") == 0)
+ *rpki_status = RPKI_VALID;
+ else if (strcmp(arg, "invalid") == 0)
+ *rpki_status = RPKI_INVALID;
+ else
+ *rpki_status = RPKI_NOTFOUND;
+
+ return rpki_status;
+}
+
+static void route_match_free(void *rule)
+{
+ XFREE(MTYPE_ROUTE_MAP_COMPILED, rule);
+}
+
+static struct rtr_socket *create_rtr_socket(struct tr_socket *tr_socket)
+{
+ struct rtr_socket *rtr_socket =
+ XMALLOC(MTYPE_BGP_RPKI_CACHE, sizeof(struct rtr_socket));
+ rtr_socket->tr_socket = tr_socket;
+ return rtr_socket;
+}
+
+static struct cache *find_cache(const uint8_t preference)
+{
+ struct listnode *cache_node;
+ struct cache *cache;
+
+ for (ALL_LIST_ELEMENTS_RO(cache_list, cache_node, cache)) {
+ if (cache->preference == preference)
+ return cache;
+ }
+ return NULL;
+}
+
+static void rpki_delete_all_cache_nodes(void)
+{
+ struct listnode *cache_node, *cache_next;
+ struct cache *cache;
+
+ for (ALL_LIST_ELEMENTS(cache_list, cache_node, cache_next, cache)) {
+ rtr_mgr_remove_group(rtr_config, cache->preference);
+ listnode_delete(cache_list, cache);
+ }
+}
+
+static void print_record(const struct pfx_record *record, struct vty *vty,
+ json_object *json)
+{
+ char ip[INET6_ADDRSTRLEN];
+ json_object *json_record = NULL;
+
+ lrtr_ip_addr_to_str(&record->prefix, ip, sizeof(ip));
+
+ if (!json) {
+ vty_out(vty, "%-40s %3u - %3u %10u\n", ip, record->min_len,
+ record->max_len, record->asn);
+ } else {
+ json_record = json_object_new_object();
+ json_object_string_add(json_record, "prefix", ip);
+ json_object_int_add(json_record, "prefixLenMin",
+ record->min_len);
+ json_object_int_add(json_record, "prefixLenMax",
+ record->max_len);
+ json_object_int_add(json_record, "asn", record->asn);
+ json_object_array_add(json, json_record);
+ }
+}
+
+static void print_record_by_asn(const struct pfx_record *record, void *data)
+{
+ struct rpki_for_each_record_arg *arg = data;
+ struct vty *vty = arg->vty;
+
+ if (record->asn == arg->as) {
+ (*arg->prefix_amount)++;
+ print_record(record, vty, arg->json);
+ }
+}
+
+static void print_record_cb(const struct pfx_record *record, void *data)
+{
+ struct rpki_for_each_record_arg *arg = data;
+ struct vty *vty = arg->vty;
+
+ (*arg->prefix_amount)++;
+
+ print_record(record, vty, arg->json);
+}
+
+static struct rtr_mgr_group *get_groups(void)
+{
+ struct listnode *cache_node;
+ struct rtr_mgr_group *rtr_mgr_groups;
+ struct cache *cache;
+
+ int group_count = listcount(cache_list);
+
+ if (group_count == 0)
+ return NULL;
+
+ rtr_mgr_groups = XMALLOC(MTYPE_BGP_RPKI_CACHE_GROUP,
+ group_count * sizeof(struct rtr_mgr_group));
+
+ size_t i = 0;
+
+ for (ALL_LIST_ELEMENTS_RO(cache_list, cache_node, cache)) {
+ rtr_mgr_groups[i].sockets = &cache->rtr_socket;
+ rtr_mgr_groups[i].sockets_len = 1;
+ rtr_mgr_groups[i].preference = cache->preference;
+
+ init_tr_socket(cache);
+
+ i++;
+ }
+
+ return rtr_mgr_groups;
+}
+
+inline bool is_synchronized(void)
+{
+ return rtr_is_synced;
+}
+
+inline bool is_running(void)
+{
+ return rtr_is_running;
+}
+
+inline bool is_stopping(void)
+{
+ return rtr_is_stopping;
+}
+
+static struct prefix *pfx_record_to_prefix(struct pfx_record *record)
+{
+ struct prefix *prefix = prefix_new();
+
+ prefix->prefixlen = record->min_len;
+
+ if (record->prefix.ver == LRTR_IPV4) {
+ prefix->family = AF_INET;
+ prefix->u.prefix4.s_addr = htonl(record->prefix.u.addr4.addr);
+ } else {
+ prefix->family = AF_INET6;
+ ipv6_addr_to_network_byte_order(record->prefix.u.addr6.addr,
+ prefix->u.prefix6.s6_addr32);
+ }
+
+ return prefix;
+}
+
+static void bgpd_sync_callback(struct thread *thread)
+{
+ struct bgp *bgp;
+ struct listnode *node;
+ struct prefix *prefix;
+ struct pfx_record rec;
+
+ thread_add_read(bm->master, bgpd_sync_callback, NULL,
+ rpki_sync_socket_bgpd, NULL);
+
+ if (atomic_load_explicit(&rtr_update_overflow, memory_order_seq_cst)) {
+ while (read(rpki_sync_socket_bgpd, &rec,
+ sizeof(struct pfx_record)) != -1)
+ ;
+
+ atomic_store_explicit(&rtr_update_overflow, 0,
+ memory_order_seq_cst);
+ revalidate_all_routes();
+ return;
+ }
+
+ int retval =
+ read(rpki_sync_socket_bgpd, &rec, sizeof(struct pfx_record));
+ if (retval != sizeof(struct pfx_record)) {
+ RPKI_DEBUG("Could not read from rpki_sync_socket_bgpd");
+ return;
+ }
+ prefix = pfx_record_to_prefix(&rec);
+
+ afi_t afi = (rec.prefix.ver == LRTR_IPV4) ? AFI_IP : AFI_IP6;
+
+ for (ALL_LIST_ELEMENTS_RO(bm->bgp, node, bgp)) {
+ safi_t safi;
+
+ for (safi = SAFI_UNICAST; safi < SAFI_MAX; safi++) {
+ struct bgp_table *table = bgp->rib[afi][safi];
+
+ if (!table)
+ continue;
+
+ struct bgp_dest *match;
+ struct bgp_dest *node;
+
+ match = bgp_table_subtree_lookup(table, prefix);
+ node = match;
+
+ while (node) {
+ if (bgp_dest_has_bgp_path_info_data(node)) {
+ revalidate_bgp_node(node, afi, safi);
+ }
+
+ node = bgp_route_next_until(node, match);
+ }
+ }
+ }
+
+ prefix_free(&prefix);
+}
+
+static void revalidate_bgp_node(struct bgp_dest *bgp_dest, afi_t afi,
+ safi_t safi)
+{
+ struct bgp_adj_in *ain;
+
+ for (ain = bgp_dest->adj_in; ain; ain = ain->next) {
+ struct bgp_path_info *path =
+ bgp_dest_get_bgp_path_info(bgp_dest);
+ mpls_label_t *label = NULL;
+ uint32_t num_labels = 0;
+
+ if (path && path->extra) {
+ label = path->extra->label;
+ num_labels = path->extra->num_labels;
+ }
+ (void)bgp_update(ain->peer, bgp_dest_get_prefix(bgp_dest),
+ ain->addpath_rx_id, ain->attr, afi, safi,
+ ZEBRA_ROUTE_BGP, BGP_ROUTE_NORMAL, NULL, label,
+ num_labels, 1, NULL);
+ }
+}
+
+static void revalidate_all_routes(void)
+{
+ struct bgp *bgp;
+ struct listnode *node;
+
+ for (ALL_LIST_ELEMENTS_RO(bm->bgp, node, bgp)) {
+ struct peer *peer;
+ struct listnode *peer_listnode;
+
+ for (ALL_LIST_ELEMENTS_RO(bgp->peer, peer_listnode, peer)) {
+
+ for (size_t i = 0; i < 2; i++) {
+ safi_t safi;
+ afi_t afi = (i == 0) ? AFI_IP : AFI_IP6;
+
+ for (safi = SAFI_UNICAST; safi < SAFI_MAX;
+ safi++) {
+ if (!peer->bgp->rib[afi][safi])
+ continue;
+
+ bgp_soft_reconfig_in(peer, afi, safi);
+ }
+ }
+ }
+ }
+}
+
+static void rpki_update_cb_sync_rtr(struct pfx_table *p __attribute__((unused)),
+ const struct pfx_record rec,
+ const bool added __attribute__((unused)))
+{
+ if (is_stopping() ||
+ atomic_load_explicit(&rtr_update_overflow, memory_order_seq_cst))
+ return;
+
+ int retval =
+ write(rpki_sync_socket_rtr, &rec, sizeof(struct pfx_record));
+ if (retval == -1 && (errno == EAGAIN || errno == EWOULDBLOCK))
+ atomic_store_explicit(&rtr_update_overflow, 1,
+ memory_order_seq_cst);
+
+ else if (retval != sizeof(struct pfx_record))
+ RPKI_DEBUG("Could not write to rpki_sync_socket_rtr");
+}
+
+static void rpki_init_sync_socket(void)
+{
+ int fds[2];
+ const char *msg;
+
+ RPKI_DEBUG("initializing sync socket");
+ if (socketpair(PF_LOCAL, SOCK_DGRAM, 0, fds) != 0) {
+ msg = "could not open rpki sync socketpair";
+ goto err;
+ }
+ rpki_sync_socket_rtr = fds[0];
+ rpki_sync_socket_bgpd = fds[1];
+
+ if (set_nonblocking(rpki_sync_socket_rtr) != 0) {
+ msg = "could not set rpki_sync_socket_rtr to non blocking";
+ goto err;
+ }
+
+ if (set_nonblocking(rpki_sync_socket_bgpd) != 0) {
+ msg = "could not set rpki_sync_socket_bgpd to non blocking";
+ goto err;
+ }
+
+
+ thread_add_read(bm->master, bgpd_sync_callback, NULL,
+ rpki_sync_socket_bgpd, NULL);
+
+ return;
+
+err:
+ zlog_err("RPKI: %s", msg);
+ abort();
+
+}
+
+static int bgp_rpki_init(struct thread_master *master)
+{
+ rpki_debug = false;
+ rtr_is_running = false;
+ rtr_is_stopping = false;
+ rtr_is_synced = false;
+
+ cache_list = list_new();
+ cache_list->del = (void (*)(void *)) & free_cache;
+
+ polling_period = POLLING_PERIOD_DEFAULT;
+ expire_interval = EXPIRE_INTERVAL_DEFAULT;
+ retry_interval = RETRY_INTERVAL_DEFAULT;
+ install_cli_commands();
+ rpki_init_sync_socket();
+ return 0;
+}
+
+static int bgp_rpki_fini(void)
+{
+ stop();
+ list_delete(&cache_list);
+
+ close(rpki_sync_socket_rtr);
+ close(rpki_sync_socket_bgpd);
+
+ return 0;
+}
+
+static int bgp_rpki_module_init(void)
+{
+ lrtr_set_alloc_functions(malloc_wrapper, realloc_wrapper, free_wrapper);
+
+ hook_register(bgp_rpki_prefix_status, rpki_validate_prefix);
+ hook_register(frr_late_init, bgp_rpki_init);
+ hook_register(frr_early_fini, &bgp_rpki_fini);
+
+ return 0;
+}
+
+static void sync_expired(struct thread *thread)
+{
+ if (!rtr_mgr_conf_in_sync(rtr_config)) {
+ RPKI_DEBUG("rtr_mgr is not synced, retrying.");
+ thread_add_timer(bm->master, sync_expired, NULL,
+ BGP_RPKI_CACHE_SERVER_SYNC_RETRY_TIMEOUT,
+ &t_rpki_sync);
+ return;
+ }
+
+ RPKI_DEBUG("rtr_mgr sync is done.");
+
+ rtr_is_synced = true;
+}
+
+static int start(void)
+{
+ int ret;
+
+ rtr_is_stopping = false;
+ rtr_is_synced = false;
+ rtr_update_overflow = 0;
+
+ if (list_isempty(cache_list)) {
+ RPKI_DEBUG(
+ "No caches were found in config. Prefix validation is off.");
+ return ERROR;
+ }
+ RPKI_DEBUG("Init rtr_mgr.");
+ int groups_len = listcount(cache_list);
+ struct rtr_mgr_group *groups = get_groups();
+
+ RPKI_DEBUG("Polling period: %d", polling_period);
+ ret = rtr_mgr_init(&rtr_config, groups, groups_len, polling_period,
+ expire_interval, retry_interval,
+ rpki_update_cb_sync_rtr, NULL, NULL, NULL);
+ if (ret == RTR_ERROR) {
+ RPKI_DEBUG("Init rtr_mgr failed.");
+ return ERROR;
+ }
+
+ RPKI_DEBUG("Starting rtr_mgr.");
+ ret = rtr_mgr_start(rtr_config);
+ if (ret == RTR_ERROR) {
+ RPKI_DEBUG("Starting rtr_mgr failed.");
+ rtr_mgr_free(rtr_config);
+ return ERROR;
+ }
+
+ thread_add_timer(bm->master, sync_expired, NULL, 0, &t_rpki_sync);
+
+ XFREE(MTYPE_BGP_RPKI_CACHE_GROUP, groups);
+
+ rtr_is_running = true;
+
+ return SUCCESS;
+}
+
+static void stop(void)
+{
+ rtr_is_stopping = true;
+ if (is_running()) {
+ THREAD_OFF(t_rpki_sync);
+ rtr_mgr_stop(rtr_config);
+ rtr_mgr_free(rtr_config);
+ rtr_is_running = false;
+ }
+}
+
+static int reset(bool force)
+{
+ if (is_running() && !force)
+ return SUCCESS;
+
+ RPKI_DEBUG("Resetting RPKI Session");
+ stop();
+ return start();
+}
+
+static struct rtr_mgr_group *get_connected_group(void)
+{
+ if (!cache_list || list_isempty(cache_list))
+ return NULL;
+
+ return rtr_mgr_get_first_group(rtr_config);
+}
+
+static void print_prefix_table_by_asn(struct vty *vty, as_t as,
+ json_object *json)
+{
+ unsigned int number_of_ipv4_prefixes = 0;
+ unsigned int number_of_ipv6_prefixes = 0;
+ struct rtr_mgr_group *group = get_connected_group();
+ struct rpki_for_each_record_arg arg;
+ json_object *json_records = NULL;
+
+ arg.vty = vty;
+ arg.as = as;
+ arg.json = NULL;
+
+ if (!group) {
+ if (!json)
+ vty_out(vty, "Cannot find a connected group.\n");
+ return;
+ }
+
+ struct pfx_table *pfx_table = group->sockets[0]->pfx_table;
+
+ if (!json) {
+ vty_out(vty, "RPKI/RTR prefix table\n");
+ vty_out(vty, "%-40s %s %s\n", "Prefix", "Prefix Length",
+ "Origin-AS");
+ } else {
+ json_records = json_object_new_array();
+ json_object_object_add(json, "prefixes", json_records);
+ arg.json = json_records;
+ }
+
+ arg.prefix_amount = &number_of_ipv4_prefixes;
+ pfx_table_for_each_ipv4_record(pfx_table, print_record_by_asn, &arg);
+
+ arg.prefix_amount = &number_of_ipv6_prefixes;
+ pfx_table_for_each_ipv6_record(pfx_table, print_record_by_asn, &arg);
+
+ if (!json) {
+ vty_out(vty, "Number of IPv4 Prefixes: %u\n",
+ number_of_ipv4_prefixes);
+ vty_out(vty, "Number of IPv6 Prefixes: %u\n",
+ number_of_ipv6_prefixes);
+ } else {
+ json_object_int_add(json, "ipv4PrefixCount",
+ number_of_ipv4_prefixes);
+ json_object_int_add(json, "ipv6PrefixCount",
+ number_of_ipv6_prefixes);
+ }
+
+ if (json)
+ vty_json(vty, json);
+}
+
+static void print_prefix_table(struct vty *vty, json_object *json)
+{
+ struct rpki_for_each_record_arg arg;
+
+ unsigned int number_of_ipv4_prefixes = 0;
+ unsigned int number_of_ipv6_prefixes = 0;
+ struct rtr_mgr_group *group = get_connected_group();
+ json_object *json_records = NULL;
+
+ arg.vty = vty;
+ arg.json = NULL;
+
+ if (!group) {
+ if (!json)
+ vty_out(vty, "Cannot find a connected group.\n");
+ return;
+ }
+
+ struct pfx_table *pfx_table = group->sockets[0]->pfx_table;
+
+ if (!json) {
+ vty_out(vty, "RPKI/RTR prefix table\n");
+ vty_out(vty, "%-40s %s %s\n", "Prefix", "Prefix Length",
+ "Origin-AS");
+ } else {
+ json_records = json_object_new_array();
+ json_object_object_add(json, "prefixes", json_records);
+ arg.json = json_records;
+ }
+
+ arg.prefix_amount = &number_of_ipv4_prefixes;
+ pfx_table_for_each_ipv4_record(pfx_table, print_record_cb, &arg);
+
+ arg.prefix_amount = &number_of_ipv6_prefixes;
+ pfx_table_for_each_ipv6_record(pfx_table, print_record_cb, &arg);
+
+ if (!json) {
+ vty_out(vty, "Number of IPv4 Prefixes: %u\n",
+ number_of_ipv4_prefixes);
+ vty_out(vty, "Number of IPv6 Prefixes: %u\n",
+ number_of_ipv6_prefixes);
+ } else {
+ json_object_int_add(json, "ipv4PrefixCount",
+ number_of_ipv4_prefixes);
+ json_object_int_add(json, "ipv6PrefixCount",
+ number_of_ipv6_prefixes);
+ }
+
+ if (json)
+ vty_json(vty, json);
+}
+
+static int rpki_validate_prefix(struct peer *peer, struct attr *attr,
+ const struct prefix *prefix)
+{
+ struct assegment *as_segment;
+ as_t as_number = 0;
+ struct lrtr_ip_addr ip_addr_prefix;
+ enum pfxv_state result;
+
+ if (!is_synchronized())
+ return RPKI_NOT_BEING_USED;
+
+ // No aspath means route comes from iBGP
+ if (!attr->aspath || !attr->aspath->segments) {
+ // Set own as number
+ as_number = peer->bgp->as;
+ } else {
+ as_segment = attr->aspath->segments;
+ // Find last AsSegment
+ while (as_segment->next)
+ as_segment = as_segment->next;
+
+ if (as_segment->type == AS_SEQUENCE) {
+ // Get rightmost asn
+ as_number = as_segment->as[as_segment->length - 1];
+ } else if (as_segment->type == AS_CONFED_SEQUENCE
+ || as_segment->type == AS_CONFED_SET) {
+ // Set own as number
+ as_number = peer->bgp->as;
+ } else {
+ // RFC says: "Take distinguished value NONE as asn"
+ // which means state is unknown
+ return RPKI_NOTFOUND;
+ }
+ }
+
+ // Get the prefix in requested format
+ switch (prefix->family) {
+ case AF_INET:
+ ip_addr_prefix.ver = LRTR_IPV4;
+ ip_addr_prefix.u.addr4.addr = ntohl(prefix->u.prefix4.s_addr);
+ break;
+
+ case AF_INET6:
+ ip_addr_prefix.ver = LRTR_IPV6;
+ ipv6_addr_to_host_byte_order(prefix->u.prefix6.s6_addr32,
+ ip_addr_prefix.u.addr6.addr);
+ break;
+
+ default:
+ return RPKI_NOT_BEING_USED;
+ }
+
+ // Do the actual validation
+ rtr_mgr_validate(rtr_config, as_number, &ip_addr_prefix,
+ prefix->prefixlen, &result);
+
+ // Print Debug output
+ switch (result) {
+ case BGP_PFXV_STATE_VALID:
+ RPKI_DEBUG(
+ "Validating Prefix %pFX from asn %u Result: VALID",
+ prefix, as_number);
+ return RPKI_VALID;
+ case BGP_PFXV_STATE_NOT_FOUND:
+ RPKI_DEBUG(
+ "Validating Prefix %pFX from asn %u Result: NOT FOUND",
+ prefix, as_number);
+ return RPKI_NOTFOUND;
+ case BGP_PFXV_STATE_INVALID:
+ RPKI_DEBUG(
+ "Validating Prefix %pFX from asn %u Result: INVALID",
+ prefix, as_number);
+ return RPKI_INVALID;
+ default:
+ RPKI_DEBUG(
+ "Validating Prefix %pFX from asn %u Result: CANNOT VALIDATE",
+ prefix, as_number);
+ break;
+ }
+ return RPKI_NOT_BEING_USED;
+}
+
+static int add_cache(struct cache *cache)
+{
+ uint8_t preference = cache->preference;
+ struct rtr_mgr_group group;
+
+ group.preference = preference;
+ group.sockets_len = 1;
+ group.sockets = &cache->rtr_socket;
+
+ if (is_running()) {
+ init_tr_socket(cache);
+
+ if (rtr_mgr_add_group(rtr_config, &group) != RTR_SUCCESS) {
+ free_tr_socket(cache);
+ return ERROR;
+ }
+ }
+
+ listnode_add(cache_list, cache);
+
+ return SUCCESS;
+}
+
+static int add_tcp_cache(const char *host, const char *port,
+ const uint8_t preference, const char *bindaddr)
+{
+ struct rtr_socket *rtr_socket;
+ struct tr_tcp_config *tcp_config =
+ XCALLOC(MTYPE_BGP_RPKI_CACHE, sizeof(struct tr_tcp_config));
+ struct tr_socket *tr_socket =
+ XMALLOC(MTYPE_BGP_RPKI_CACHE, sizeof(struct tr_socket));
+ struct cache *cache =
+ XMALLOC(MTYPE_BGP_RPKI_CACHE, sizeof(struct cache));
+
+ tcp_config->host = XSTRDUP(MTYPE_BGP_RPKI_CACHE, host);
+ tcp_config->port = XSTRDUP(MTYPE_BGP_RPKI_CACHE, port);
+ if (bindaddr)
+ tcp_config->bindaddr = XSTRDUP(MTYPE_BGP_RPKI_CACHE, bindaddr);
+ else
+ tcp_config->bindaddr = NULL;
+
+ rtr_socket = create_rtr_socket(tr_socket);
+
+ cache->type = TCP;
+ cache->tr_socket = tr_socket;
+ cache->tr_config.tcp_config = tcp_config;
+ cache->rtr_socket = rtr_socket;
+ cache->preference = preference;
+
+ int ret = add_cache(cache);
+ if (ret != SUCCESS) {
+ free_cache(cache);
+ }
+
+ return ret;
+}
+
+#if defined(FOUND_SSH)
+static int add_ssh_cache(const char *host, const unsigned int port,
+ const char *username, const char *client_privkey_path,
+ const char *server_pubkey_path,
+ const uint8_t preference, const char *bindaddr)
+{
+ struct tr_ssh_config *ssh_config =
+ XCALLOC(MTYPE_BGP_RPKI_CACHE, sizeof(struct tr_ssh_config));
+ struct cache *cache =
+ XMALLOC(MTYPE_BGP_RPKI_CACHE, sizeof(struct cache));
+ struct tr_socket *tr_socket =
+ XMALLOC(MTYPE_BGP_RPKI_CACHE, sizeof(struct tr_socket));
+ struct rtr_socket *rtr_socket;
+
+ ssh_config->port = port;
+ ssh_config->host = XSTRDUP(MTYPE_BGP_RPKI_CACHE, host);
+ if (bindaddr)
+ ssh_config->bindaddr = XSTRDUP(MTYPE_BGP_RPKI_CACHE, bindaddr);
+ else
+ ssh_config->bindaddr = NULL;
+
+ ssh_config->username = XSTRDUP(MTYPE_BGP_RPKI_CACHE, username);
+ ssh_config->client_privkey_path =
+ XSTRDUP(MTYPE_BGP_RPKI_CACHE, client_privkey_path);
+ ssh_config->server_hostkey_path =
+ XSTRDUP(MTYPE_BGP_RPKI_CACHE, server_pubkey_path);
+
+ rtr_socket = create_rtr_socket(tr_socket);
+
+ cache->type = SSH;
+ cache->tr_socket = tr_socket;
+ cache->tr_config.ssh_config = ssh_config;
+ cache->rtr_socket = rtr_socket;
+ cache->preference = preference;
+
+ int ret = add_cache(cache);
+ if (ret != SUCCESS) {
+ free_cache(cache);
+ }
+
+ return ret;
+}
+#endif
+
+static void free_cache(struct cache *cache)
+{
+ if (cache->type == TCP) {
+ XFREE(MTYPE_BGP_RPKI_CACHE, cache->tr_config.tcp_config->host);
+ XFREE(MTYPE_BGP_RPKI_CACHE, cache->tr_config.tcp_config->port);
+ XFREE(MTYPE_BGP_RPKI_CACHE,
+ cache->tr_config.tcp_config->bindaddr);
+ XFREE(MTYPE_BGP_RPKI_CACHE, cache->tr_config.tcp_config);
+ }
+#if defined(FOUND_SSH)
+ else {
+ XFREE(MTYPE_BGP_RPKI_CACHE, cache->tr_config.ssh_config->host);
+ XFREE(MTYPE_BGP_RPKI_CACHE,
+ cache->tr_config.ssh_config->username);
+ XFREE(MTYPE_BGP_RPKI_CACHE,
+ cache->tr_config.ssh_config->client_privkey_path);
+ XFREE(MTYPE_BGP_RPKI_CACHE,
+ cache->tr_config.ssh_config->server_hostkey_path);
+ XFREE(MTYPE_BGP_RPKI_CACHE,
+ cache->tr_config.ssh_config->bindaddr);
+ XFREE(MTYPE_BGP_RPKI_CACHE, cache->tr_config.ssh_config);
+ }
+#endif
+ XFREE(MTYPE_BGP_RPKI_CACHE, cache->tr_socket);
+ XFREE(MTYPE_BGP_RPKI_CACHE, cache->rtr_socket);
+ XFREE(MTYPE_BGP_RPKI_CACHE, cache);
+}
+
+static int config_write(struct vty *vty)
+{
+ struct listnode *cache_node;
+ struct cache *cache;
+
+ if (rpki_debug)
+ vty_out(vty, "debug rpki\n");
+
+ vty_out(vty, "!\n");
+ vty_out(vty, "rpki\n");
+
+ if (polling_period != POLLING_PERIOD_DEFAULT)
+ vty_out(vty, " rpki polling_period %d\n", polling_period);
+ if (retry_interval != RETRY_INTERVAL_DEFAULT)
+ vty_out(vty, " rpki retry_interval %d\n", retry_interval);
+ if (expire_interval != EXPIRE_INTERVAL_DEFAULT)
+ vty_out(vty, " rpki expire_interval %d\n", expire_interval);
+
+ for (ALL_LIST_ELEMENTS_RO(cache_list, cache_node, cache)) {
+ switch (cache->type) {
+ struct tr_tcp_config *tcp_config;
+#if defined(FOUND_SSH)
+ struct tr_ssh_config *ssh_config;
+#endif
+ case TCP:
+ tcp_config = cache->tr_config.tcp_config;
+ vty_out(vty, " rpki cache %s %s ", tcp_config->host,
+ tcp_config->port);
+ if (tcp_config->bindaddr)
+ vty_out(vty, "source %s ",
+ tcp_config->bindaddr);
+ break;
+#if defined(FOUND_SSH)
+ case SSH:
+ ssh_config = cache->tr_config.ssh_config;
+ vty_out(vty, " rpki cache %s %u %s %s %s ",
+ ssh_config->host, ssh_config->port,
+ ssh_config->username,
+ ssh_config->client_privkey_path,
+ ssh_config->server_hostkey_path != NULL
+ ? ssh_config->server_hostkey_path
+ : " ");
+ if (ssh_config->bindaddr)
+ vty_out(vty, "source %s ",
+ ssh_config->bindaddr);
+ break;
+#endif
+ default:
+ break;
+ }
+
+ vty_out(vty, "preference %hhu\n", cache->preference);
+ }
+ vty_out(vty, "exit\n");
+
+ return 1;
+}
+
+DEFUN_NOSH (rpki,
+ rpki_cmd,
+ "rpki",
+ "Enable rpki and enter rpki configuration mode\n")
+{
+ vty->node = RPKI_NODE;
+ return CMD_SUCCESS;
+}
+
+DEFPY (no_rpki,
+ no_rpki_cmd,
+ "no rpki",
+ NO_STR
+ "Enable rpki and enter rpki configuration mode\n")
+{
+ rpki_delete_all_cache_nodes();
+ stop();
+ return CMD_SUCCESS;
+}
+
+DEFUN (bgp_rpki_start,
+ bgp_rpki_start_cmd,
+ "rpki start",
+ RPKI_OUTPUT_STRING
+ "start rpki support\n")
+{
+ if (listcount(cache_list) == 0)
+ vty_out(vty,
+ "Could not start rpki because no caches are configured\n");
+
+ if (!is_running()) {
+ if (start() == ERROR) {
+ RPKI_DEBUG("RPKI failed to start");
+ return CMD_WARNING;
+ }
+ }
+ return CMD_SUCCESS;
+}
+
+DEFUN (bgp_rpki_stop,
+ bgp_rpki_stop_cmd,
+ "rpki stop",
+ RPKI_OUTPUT_STRING
+ "start rpki support\n")
+{
+ if (is_running())
+ stop();
+
+ return CMD_SUCCESS;
+}
+
+DEFPY (rpki_polling_period,
+ rpki_polling_period_cmd,
+ "rpki polling_period (1-86400)$pp",
+ RPKI_OUTPUT_STRING
+ "Set polling period\n"
+ "Polling period value\n")
+{
+ polling_period = pp;
+ return CMD_SUCCESS;
+}
+
+DEFUN (no_rpki_polling_period,
+ no_rpki_polling_period_cmd,
+ "no rpki polling_period [(1-86400)]",
+ NO_STR
+ RPKI_OUTPUT_STRING
+ "Set polling period back to default\n"
+ "Polling period value\n")
+{
+ polling_period = POLLING_PERIOD_DEFAULT;
+ return CMD_SUCCESS;
+}
+
+DEFPY (rpki_expire_interval,
+ rpki_expire_interval_cmd,
+ "rpki expire_interval (600-172800)$tmp",
+ RPKI_OUTPUT_STRING
+ "Set expire interval\n"
+ "Expire interval value\n")
+{
+ if ((unsigned int)tmp >= polling_period) {
+ expire_interval = tmp;
+ return CMD_SUCCESS;
+ }
+
+ vty_out(vty, "%% Expiry interval must be polling period or larger\n");
+ return CMD_WARNING_CONFIG_FAILED;
+}
+
+DEFUN (no_rpki_expire_interval,
+ no_rpki_expire_interval_cmd,
+ "no rpki expire_interval [(600-172800)]",
+ NO_STR
+ RPKI_OUTPUT_STRING
+ "Set expire interval back to default\n"
+ "Expire interval value\n")
+{
+ expire_interval = polling_period * 2;
+ return CMD_SUCCESS;
+}
+
+DEFPY (rpki_retry_interval,
+ rpki_retry_interval_cmd,
+ "rpki retry_interval (1-7200)$tmp",
+ RPKI_OUTPUT_STRING
+ "Set retry interval\n"
+ "retry interval value\n")
+{
+ retry_interval = tmp;
+ return CMD_SUCCESS;
+}
+
+DEFUN (no_rpki_retry_interval,
+ no_rpki_retry_interval_cmd,
+ "no rpki retry_interval [(1-7200)]",
+ NO_STR
+ RPKI_OUTPUT_STRING
+ "Set retry interval back to default\n"
+ "retry interval value\n")
+{
+ retry_interval = RETRY_INTERVAL_DEFAULT;
+ return CMD_SUCCESS;
+}
+
+DEFPY(rpki_cache, rpki_cache_cmd,
+ "rpki cache <A.B.C.D|WORD> <TCPPORT|(1-65535)$sshport SSH_UNAME SSH_PRIVKEY [SERVER_PUBKEY]> [source <A.B.C.D>$bindaddr] preference (1-255)",
+ RPKI_OUTPUT_STRING
+ "Install a cache server to current group\n"
+ "IP address of cache server\n"
+ "Hostname of cache server\n"
+ "TCP port number\n"
+ "SSH port number\n"
+ "SSH user name\n"
+ "Path to own SSH private key\n"
+ "Path to Public key of cache server\n"
+ "Configure source IP address of RPKI connection\n"
+ "Define a Source IP Address\n"
+ "Preference of the cache server\n"
+ "Preference value\n")
+{
+ int return_value;
+ struct listnode *cache_node;
+ struct cache *current_cache;
+
+ for (ALL_LIST_ELEMENTS_RO(cache_list, cache_node, current_cache)) {
+ if (current_cache->preference == preference) {
+ vty_out(vty,
+ "Cache with preference %ld is already configured\n",
+ preference);
+ return CMD_WARNING;
+ }
+ }
+
+
+ // use ssh connection
+ if (ssh_uname) {
+#if defined(FOUND_SSH)
+ return_value =
+ add_ssh_cache(cache, sshport, ssh_uname, ssh_privkey,
+ server_pubkey, preference, bindaddr_str);
+#else
+ return_value = SUCCESS;
+ vty_out(vty,
+ "ssh sockets are not supported. Please recompile rtrlib and frr with ssh support. If you want to use it\n");
+#endif
+ } else { // use tcp connection
+ return_value =
+ add_tcp_cache(cache, tcpport, preference, bindaddr_str);
+ }
+
+ if (return_value == ERROR) {
+ vty_out(vty, "Could not create new rpki cache\n");
+ return CMD_WARNING;
+ }
+
+ return CMD_SUCCESS;
+}
+
+DEFPY (no_rpki_cache,
+ no_rpki_cache_cmd,
+ "no rpki cache <A.B.C.D|WORD> <TCPPORT|(1-65535)$sshport SSH_UNAME SSH_PRIVKEY [SERVER_PUBKEY]> [source <A.B.C.D>$bindaddr] preference (1-255)",
+ NO_STR
+ RPKI_OUTPUT_STRING
+ "Install a cache server to current group\n"
+ "IP address of cache server\n"
+ "Hostname of cache server\n"
+ "TCP port number\n"
+ "SSH port number\n"
+ "SSH user name\n"
+ "Path to own SSH private key\n"
+ "Path to Public key of cache server\n"
+ "Configure source IP address of RPKI connection\n"
+ "Define a Source IP Address\n"
+ "Preference of the cache server\n"
+ "Preference value\n")
+{
+ struct cache *cache_p = find_cache(preference);
+
+ if (!cache_p) {
+ vty_out(vty, "Could not find cache with preference %ld\n",
+ preference);
+ return CMD_WARNING;
+ }
+
+ if (is_running() && listcount(cache_list) == 1) {
+ stop();
+ } else if (is_running()) {
+ if (rtr_mgr_remove_group(rtr_config, preference) == RTR_ERROR) {
+ vty_out(vty,
+ "Could not remove cache with preference %ld\n",
+ preference);
+ return CMD_WARNING;
+ }
+ }
+
+ listnode_delete(cache_list, cache_p);
+ free_cache(cache_p);
+
+ return CMD_SUCCESS;
+}
+
+DEFPY (show_rpki_prefix_table,
+ show_rpki_prefix_table_cmd,
+ "show rpki prefix-table [json$uj]",
+ SHOW_STR
+ RPKI_OUTPUT_STRING
+ "Show validated prefixes which were received from RPKI Cache\n"
+ JSON_STR)
+{
+ struct json_object *json = NULL;
+
+ if (!is_synchronized()) {
+ if (!uj)
+ vty_out(vty, "No connection to RPKI cache server.\n");
+ return CMD_WARNING;
+ }
+
+ if (uj)
+ json = json_object_new_object();
+
+ print_prefix_table(vty, json);
+ return CMD_SUCCESS;
+}
+
+DEFPY (show_rpki_as_number,
+ show_rpki_as_number_cmd,
+ "show rpki as-number (1-4294967295)$by_asn [json$uj]",
+ SHOW_STR
+ RPKI_OUTPUT_STRING
+ "Lookup by ASN in prefix table\n"
+ "AS Number\n"
+ JSON_STR)
+{
+ struct json_object *json = NULL;
+
+ if (!is_synchronized()) {
+ if (!uj)
+ vty_out(vty, "No Connection to RPKI cache server.\n");
+ return CMD_WARNING;
+ }
+
+ if (uj)
+ json = json_object_new_object();
+
+ print_prefix_table_by_asn(vty, by_asn, json);
+ return CMD_SUCCESS;
+}
+
+DEFPY (show_rpki_prefix,
+ show_rpki_prefix_cmd,
+ "show rpki prefix <A.B.C.D/M|X:X::X:X/M> [(1-4294967295)$asn] [json$uj]",
+ SHOW_STR
+ RPKI_OUTPUT_STRING
+ "Lookup IP prefix and optionally ASN in prefix table\n"
+ "IPv4 prefix\n"
+ "IPv6 prefix\n"
+ "AS Number\n"
+ JSON_STR)
+{
+ json_object *json = NULL;
+ json_object *json_records = NULL;
+
+ if (!is_synchronized()) {
+ if (!uj)
+ vty_out(vty, "No Connection to RPKI cache server.\n");
+ return CMD_WARNING;
+ }
+
+ struct lrtr_ip_addr addr;
+ char addr_str[INET6_ADDRSTRLEN];
+ size_t addr_len = strchr(prefix_str, '/') - prefix_str;
+
+ memset(addr_str, 0, sizeof(addr_str));
+ memcpy(addr_str, prefix_str, addr_len);
+
+ if (lrtr_ip_str_to_addr(addr_str, &addr) != 0) {
+ if (!json)
+ vty_out(vty, "Invalid IP prefix\n");
+ return CMD_WARNING;
+ }
+
+ struct pfx_record *matches = NULL;
+ unsigned int match_count = 0;
+ enum pfxv_state result;
+
+ if (pfx_table_validate_r(rtr_config->pfx_table, &matches, &match_count,
+ asn, &addr, prefix->prefixlen, &result)
+ != PFX_SUCCESS) {
+ if (!json)
+ vty_out(vty, "Prefix lookup failed\n");
+ return CMD_WARNING;
+ }
+
+ if (uj)
+ json = json_object_new_object();
+
+ if (!json) {
+ vty_out(vty, "%-40s %s %s\n", "Prefix", "Prefix Length",
+ "Origin-AS");
+ } else {
+ json_records = json_object_new_array();
+ json_object_object_add(json, "prefixes", json_records);
+ }
+
+ for (size_t i = 0; i < match_count; ++i) {
+ const struct pfx_record *record = &matches[i];
+
+ if (record->max_len >= prefix->prefixlen
+ && ((asn != 0 && (uint32_t)asn == record->asn)
+ || asn == 0)) {
+ print_record(&matches[i], vty, json_records);
+ }
+ }
+
+ if (json)
+ vty_json(vty, json);
+
+ return CMD_SUCCESS;
+}
+
+DEFPY (show_rpki_cache_server,
+ show_rpki_cache_server_cmd,
+ "show rpki cache-server [json$uj]",
+ SHOW_STR
+ RPKI_OUTPUT_STRING
+ "Show configured cache server\n"
+ JSON_STR)
+{
+ struct json_object *json = NULL;
+ struct json_object *json_server = NULL;
+ struct json_object *json_servers = NULL;
+ struct listnode *cache_node;
+ struct cache *cache;
+
+ if (uj) {
+ json = json_object_new_object();
+ json_servers = json_object_new_array();
+ json_object_object_add(json, "servers", json_servers);
+ }
+
+ for (ALL_LIST_ELEMENTS_RO(cache_list, cache_node, cache)) {
+ if (cache->type == TCP) {
+ if (!json) {
+ vty_out(vty,
+ "host: %s port: %s, preference: %hhu\n",
+ cache->tr_config.tcp_config->host,
+ cache->tr_config.tcp_config->port,
+ cache->preference);
+ } else {
+ json_server = json_object_new_object();
+ json_object_string_add(json_server, "mode",
+ "tcp");
+ json_object_string_add(
+ json_server, "host",
+ cache->tr_config.tcp_config->host);
+ json_object_string_add(
+ json_server, "port",
+ cache->tr_config.tcp_config->port);
+ json_object_int_add(json_server, "preference",
+ cache->preference);
+ json_object_array_add(json_servers,
+ json_server);
+ }
+
+#if defined(FOUND_SSH)
+ } else if (cache->type == SSH) {
+ if (!json) {
+ vty_out(vty,
+ "host: %s port: %d username: %s server_hostkey_path: %s client_privkey_path: %s, preference: %hhu\n",
+ cache->tr_config.ssh_config->host,
+ cache->tr_config.ssh_config->port,
+ cache->tr_config.ssh_config->username,
+ cache->tr_config.ssh_config
+ ->server_hostkey_path,
+ cache->tr_config.ssh_config
+ ->client_privkey_path,
+ cache->preference);
+ } else {
+ json_server = json_object_new_object();
+ json_object_string_add(json_server, "mode",
+ "ssh");
+ json_object_string_add(
+ json_server, "host",
+ cache->tr_config.ssh_config->host);
+ json_object_int_add(
+ json_server, "port",
+ cache->tr_config.ssh_config->port);
+ json_object_string_add(
+ json_server, "username",
+ cache->tr_config.ssh_config->username);
+ json_object_string_add(
+ json_server, "serverHostkeyPath",
+ cache->tr_config.ssh_config
+ ->server_hostkey_path);
+ json_object_string_add(
+ json_server, "clientPrivkeyPath",
+ cache->tr_config.ssh_config
+ ->client_privkey_path);
+ json_object_int_add(json_server, "preference",
+ cache->preference);
+ json_object_array_add(json_servers,
+ json_server);
+ }
+#endif
+ }
+ }
+
+ if (json)
+ vty_json(vty, json);
+
+ return CMD_SUCCESS;
+}
+
+DEFPY (show_rpki_cache_connection,
+ show_rpki_cache_connection_cmd,
+ "show rpki cache-connection [json$uj]",
+ SHOW_STR
+ RPKI_OUTPUT_STRING
+ "Show to which RPKI Cache Servers we have a connection\n"
+ JSON_STR)
+{
+ struct json_object *json = NULL;
+ struct json_object *json_conn = NULL;
+ struct json_object *json_conns = NULL;
+ struct listnode *cache_node;
+ struct cache *cache;
+ struct rtr_mgr_group *group;
+
+ if (uj)
+ json = json_object_new_object();
+
+ if (!is_synchronized()) {
+ if (!json)
+ vty_out(vty, "No connection to RPKI cache server.\n");
+ else
+ vty_json(vty, json);
+
+ return CMD_SUCCESS;
+ }
+
+ group = get_connected_group();
+ if (!group) {
+ if (!json)
+ vty_out(vty, "Cannot find a connected group.\n");
+ else
+ vty_json(vty, json);
+
+ return CMD_SUCCESS;
+ }
+
+ if (!json) {
+ vty_out(vty, "Connected to group %d\n", group->preference);
+ } else {
+ json_conns = json_object_new_array();
+ json_object_int_add(json, "connectedGroup", group->preference);
+ json_object_object_add(json, "connections", json_conns);
+ }
+
+ for (ALL_LIST_ELEMENTS_RO(cache_list, cache_node, cache)) {
+ struct tr_tcp_config *tcp_config;
+#if defined(FOUND_SSH)
+ struct tr_ssh_config *ssh_config;
+#endif
+ switch (cache->type) {
+ case TCP:
+ tcp_config = cache->tr_config.tcp_config;
+
+ if (!json) {
+ vty_out(vty,
+ "rpki tcp cache %s %s pref %hhu%s\n",
+ tcp_config->host, tcp_config->port,
+ cache->preference,
+ cache->rtr_socket->state ==
+ RTR_ESTABLISHED
+ ? " (connected)"
+ : "");
+ } else {
+ json_conn = json_object_new_object();
+ json_object_string_add(json_conn, "mode",
+ "tcp");
+ json_object_string_add(json_conn, "host",
+ tcp_config->host);
+ json_object_string_add(json_conn, "port",
+ tcp_config->port);
+ json_object_int_add(json_conn, "preference",
+ cache->preference);
+ json_object_string_add(
+ json_conn, "state",
+ cache->rtr_socket->state ==
+ RTR_ESTABLISHED
+ ? "connected"
+ : "disconnected");
+ json_object_array_add(json_conns, json_conn);
+ }
+ break;
+#if defined(FOUND_SSH)
+ case SSH:
+ ssh_config = cache->tr_config.ssh_config;
+
+ if (!json) {
+ vty_out(vty,
+ "rpki ssh cache %s %u pref %hhu%s\n",
+ ssh_config->host, ssh_config->port,
+ cache->preference,
+ cache->rtr_socket->state ==
+ RTR_ESTABLISHED
+ ? " (connected)"
+ : "");
+ } else {
+ json_conn = json_object_new_object();
+ json_object_string_add(json_conn, "mode",
+ "ssh");
+ json_object_string_add(json_conn, "host",
+ ssh_config->host);
+ json_object_int_add(json_conn, "port",
+ ssh_config->port);
+ json_object_int_add(json_conn, "preference",
+ cache->preference);
+ json_object_string_add(
+ json_conn, "state",
+ cache->rtr_socket->state ==
+ RTR_ESTABLISHED
+ ? "connected"
+ : "disconnected");
+ json_object_array_add(json_conns, json_conn);
+ }
+ break;
+#endif
+ default:
+ break;
+ }
+ }
+
+ if (json)
+ vty_json(vty, json);
+
+ return CMD_SUCCESS;
+}
+
+static int config_on_exit(struct vty *vty)
+{
+ reset(false);
+ return 1;
+}
+
+DEFUN (rpki_reset,
+ rpki_reset_cmd,
+ "rpki reset",
+ RPKI_OUTPUT_STRING
+ "reset rpki\n")
+{
+ return reset(true) == SUCCESS ? CMD_SUCCESS : CMD_WARNING;
+}
+
+DEFUN (debug_rpki,
+ debug_rpki_cmd,
+ "debug rpki",
+ DEBUG_STR
+ "Enable debugging for rpki\n")
+{
+ rpki_debug = true;
+ return CMD_SUCCESS;
+}
+
+DEFUN (no_debug_rpki,
+ no_debug_rpki_cmd,
+ "no debug rpki",
+ NO_STR
+ DEBUG_STR
+ "Disable debugging for rpki\n")
+{
+ rpki_debug = false;
+ return CMD_SUCCESS;
+}
+
+DEFUN_YANG (match_rpki,
+ match_rpki_cmd,
+ "match rpki <valid|invalid|notfound>",
+ MATCH_STR
+ RPKI_OUTPUT_STRING
+ "Valid prefix\n"
+ "Invalid prefix\n"
+ "Prefix not found\n")
+{
+ const char *xpath =
+ "./match-condition[condition='frr-bgp-route-map:rpki']";
+ char xpath_value[XPATH_MAXLEN];
+
+ nb_cli_enqueue_change(vty, xpath, NB_OP_CREATE, NULL);
+ snprintf(xpath_value, sizeof(xpath_value),
+ "%s/rmap-match-condition/frr-bgp-route-map:rpki", xpath);
+ nb_cli_enqueue_change(vty, xpath_value, NB_OP_MODIFY, argv[2]->arg);
+
+ return nb_cli_apply_changes(vty, NULL);
+}
+
+DEFUN_YANG (no_match_rpki,
+ no_match_rpki_cmd,
+ "no match rpki <valid|invalid|notfound>",
+ NO_STR
+ MATCH_STR
+ RPKI_OUTPUT_STRING
+ "Valid prefix\n"
+ "Invalid prefix\n"
+ "Prefix not found\n")
+{
+ const char *xpath =
+ "./match-condition[condition='frr-bgp-route-map:rpki']";
+
+ nb_cli_enqueue_change(vty, xpath, NB_OP_DESTROY, NULL);
+ return nb_cli_apply_changes(vty, NULL);
+}
+
+static void install_cli_commands(void)
+{
+ // TODO: make config write work
+ install_node(&rpki_node);
+ install_default(RPKI_NODE);
+ install_element(CONFIG_NODE, &rpki_cmd);
+ install_element(ENABLE_NODE, &rpki_cmd);
+ install_element(CONFIG_NODE, &no_rpki_cmd);
+
+
+ install_element(ENABLE_NODE, &bgp_rpki_start_cmd);
+ install_element(ENABLE_NODE, &bgp_rpki_stop_cmd);
+
+ /* Install rpki reset command */
+ install_element(ENABLE_NODE, &rpki_reset_cmd);
+ install_element(RPKI_NODE, &rpki_reset_cmd);
+
+ /* Install rpki polling period commands */
+ install_element(RPKI_NODE, &rpki_polling_period_cmd);
+ install_element(RPKI_NODE, &no_rpki_polling_period_cmd);
+
+ /* Install rpki expire interval commands */
+ install_element(RPKI_NODE, &rpki_expire_interval_cmd);
+ install_element(RPKI_NODE, &no_rpki_expire_interval_cmd);
+
+ /* Install rpki retry interval commands */
+ install_element(RPKI_NODE, &rpki_retry_interval_cmd);
+ install_element(RPKI_NODE, &no_rpki_retry_interval_cmd);
+
+ /* Install rpki cache commands */
+ install_element(RPKI_NODE, &rpki_cache_cmd);
+ install_element(RPKI_NODE, &no_rpki_cache_cmd);
+
+ /* Install show commands */
+ install_element(VIEW_NODE, &show_rpki_prefix_table_cmd);
+ install_element(VIEW_NODE, &show_rpki_cache_connection_cmd);
+ install_element(VIEW_NODE, &show_rpki_cache_server_cmd);
+ install_element(VIEW_NODE, &show_rpki_prefix_cmd);
+ install_element(VIEW_NODE, &show_rpki_as_number_cmd);
+
+ /* Install debug commands */
+ install_element(CONFIG_NODE, &debug_rpki_cmd);
+ install_element(ENABLE_NODE, &debug_rpki_cmd);
+ install_element(CONFIG_NODE, &no_debug_rpki_cmd);
+ install_element(ENABLE_NODE, &no_debug_rpki_cmd);
+
+ /* Install route match */
+ route_map_install_match(&route_match_rpki_cmd);
+ install_element(RMAP_NODE, &match_rpki_cmd);
+ install_element(RMAP_NODE, &no_match_rpki_cmd);
+}
+
+FRR_MODULE_SETUP(.name = "bgpd_rpki", .version = "0.3.6",
+ .description = "Enable RPKI support for FRR.",
+ .init = bgp_rpki_module_init,
+);
diff --git a/bgpd/bgp_rpki.h b/bgpd/bgp_rpki.h
new file mode 100644
index 0000000..4dd4b4a
--- /dev/null
+++ b/bgpd/bgp_rpki.h
@@ -0,0 +1,33 @@
+/*
+ * bgp_rpki code
+ * Copyright (C) 2021 NVIDIA Corporation and Mellanox Technologies, LTD
+ * All Rights Reserved
+ * Donald Sharp
+ *
+ * This file is part of FRR.
+ *
+ * FRR is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License as published by the
+ * Free Software Foundation; either version 2, or (at your option) any
+ * later version.
+ *
+ * FRR is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program; see the file COPYING; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
+ */
+#ifndef __BGP_RPKI_H__
+#define __BGP_RPKI_H__
+
+enum rpki_states {
+ RPKI_NOT_BEING_USED,
+ RPKI_VALID,
+ RPKI_NOTFOUND,
+ RPKI_INVALID
+};
+
+#endif
diff --git a/bgpd/bgp_script.c b/bgpd/bgp_script.c
new file mode 100644
index 0000000..bf3e612
--- /dev/null
+++ b/bgpd/bgp_script.c
@@ -0,0 +1,196 @@
+/* BGP scripting foo
+ * Copyright (C) 2020 NVIDIA Corporation
+ * Quentin Young
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; see the file COPYING; if not, write to the
+ * Free Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston,
+ * MA 02110-1301 USA
+ */
+
+#include <zebra.h>
+
+#ifdef HAVE_SCRIPTING
+
+#include "bgpd.h"
+#include "bgp_script.h"
+#include "bgp_debug.h"
+#include "bgp_aspath.h"
+#include "frratomic.h"
+#include "frrscript.h"
+
+void lua_pushpeer(lua_State *L, const struct peer *peer)
+{
+ lua_newtable(L);
+ lua_pushinteger(L, peer->as);
+ lua_setfield(L, -2, "remote_as");
+ lua_pushinteger(L, peer->local_as);
+ lua_setfield(L, -2, "local_as");
+ lua_pushinaddr(L, &peer->remote_id);
+ lua_setfield(L, -2, "remote_id");
+ lua_pushinaddr(L, &peer->local_id);
+ lua_setfield(L, -2, "local_id");
+ lua_pushstring(L, lookup_msg(bgp_status_msg, peer->status, NULL));
+ lua_setfield(L, -2, "state");
+ lua_pushstring(L, peer->desc ? peer->desc : "");
+ lua_setfield(L, -2, "description");
+ lua_pushtimet(L, &peer->uptime);
+ lua_setfield(L, -2, "uptime");
+ lua_pushtimet(L, &peer->readtime);
+ lua_setfield(L, -2, "last_readtime");
+ lua_pushtimet(L, &peer->resettime);
+ lua_setfield(L, -2, "last_resettime");
+ lua_pushsockunion(L, peer->su_local);
+ lua_setfield(L, -2, "local_address");
+ lua_pushsockunion(L, peer->su_remote);
+ lua_setfield(L, -2, "remote_address");
+ lua_pushinteger(L, peer->cap);
+ lua_setfield(L, -2, "capabilities");
+ lua_pushinteger(L, peer->flags);
+ lua_setfield(L, -2, "flags");
+ lua_pushstring(L, peer->password ? peer->password : "");
+ lua_setfield(L, -2, "password");
+
+ /* Nested tables here */
+ lua_newtable(L);
+ {
+ lua_newtable(L);
+ {
+ lua_pushinteger(L, peer->holdtime);
+ lua_setfield(L, -2, "hold");
+ lua_pushinteger(L, peer->keepalive);
+ lua_setfield(L, -2, "keepalive");
+ lua_pushinteger(L, peer->connect);
+ lua_setfield(L, -2, "connect");
+ lua_pushinteger(L, peer->routeadv);
+ lua_setfield(L, -2, "route_advertisement");
+ }
+ lua_setfield(L, -2, "configured");
+
+ lua_newtable(L);
+ {
+ lua_pushinteger(L, peer->v_holdtime);
+ lua_setfield(L, -2, "hold");
+ lua_pushinteger(L, peer->v_keepalive);
+ lua_setfield(L, -2, "keepalive");
+ lua_pushinteger(L, peer->v_connect);
+ lua_setfield(L, -2, "connect");
+ lua_pushinteger(L, peer->v_routeadv);
+ lua_setfield(L, -2, "route_advertisement");
+ }
+ lua_setfield(L, -2, "negotiated");
+ }
+ lua_setfield(L, -2, "timers");
+
+ lua_newtable(L);
+ {
+ lua_pushinteger(L, atomic_load_explicit(&peer->open_in,
+ memory_order_relaxed));
+ lua_setfield(L, -2, "open_in");
+ lua_pushinteger(L, atomic_load_explicit(&peer->open_out,
+ memory_order_relaxed));
+ lua_setfield(L, -2, "open_out");
+ lua_pushinteger(L, atomic_load_explicit(&peer->update_in,
+ memory_order_relaxed));
+ lua_setfield(L, -2, "update_in");
+ lua_pushinteger(L, atomic_load_explicit(&peer->update_out,
+ memory_order_relaxed));
+ lua_setfield(L, -2, "update_out");
+ lua_pushinteger(L, atomic_load_explicit(&peer->update_time,
+ memory_order_relaxed));
+ lua_setfield(L, -2, "update_time");
+ lua_pushinteger(L, atomic_load_explicit(&peer->keepalive_in,
+ memory_order_relaxed));
+ lua_setfield(L, -2, "keepalive_in");
+ lua_pushinteger(L, atomic_load_explicit(&peer->keepalive_out,
+ memory_order_relaxed));
+ lua_setfield(L, -2, "keepalive_out");
+ lua_pushinteger(L, atomic_load_explicit(&peer->notify_in,
+ memory_order_relaxed));
+ lua_setfield(L, -2, "notify_in");
+ lua_pushinteger(L, atomic_load_explicit(&peer->notify_out,
+ memory_order_relaxed));
+ lua_setfield(L, -2, "notify_out");
+ lua_pushinteger(L, atomic_load_explicit(&peer->refresh_in,
+ memory_order_relaxed));
+ lua_setfield(L, -2, "refresh_in");
+ lua_pushinteger(L, atomic_load_explicit(&peer->refresh_out,
+ memory_order_relaxed));
+ lua_setfield(L, -2, "refresh_out");
+ lua_pushinteger(L, atomic_load_explicit(&peer->dynamic_cap_in,
+ memory_order_relaxed));
+ lua_setfield(L, -2, "dynamic_cap_in");
+ lua_pushinteger(L, atomic_load_explicit(&peer->dynamic_cap_out,
+ memory_order_relaxed));
+ lua_setfield(L, -2, "dynamic_cap_out");
+ lua_pushinteger(L, peer->established);
+ lua_setfield(L, -2, "times_established");
+ lua_pushinteger(L, peer->dropped);
+ lua_setfield(L, -2, "times_dropped");
+ }
+ lua_setfield(L, -2, "stats");
+}
+
+void lua_pushattr(lua_State *L, const struct attr *attr)
+{
+ lua_newtable(L);
+ lua_pushinteger(L, attr->med);
+ lua_setfield(L, -2, "metric");
+ lua_pushinteger(L, attr->nh_ifindex);
+ lua_setfield(L, -2, "ifindex");
+ lua_pushstring(L, attr->aspath->str);
+ lua_setfield(L, -2, "aspath");
+ lua_pushinteger(L, attr->local_pref);
+ lua_setfield(L, -2, "localpref");
+}
+
+void lua_decode_attr(lua_State *L, int idx, struct attr *attr)
+{
+ lua_getfield(L, idx, "metric");
+ attr->med = lua_tointeger(L, -1);
+ lua_pop(L, 1);
+ lua_getfield(L, idx, "ifindex");
+ attr->nh_ifindex = lua_tointeger(L, -1);
+ lua_pop(L, 1);
+ lua_getfield(L, idx, "aspath");
+ attr->aspath = aspath_str2aspath(lua_tostring(L, -1));
+ lua_pop(L, 1);
+ lua_getfield(L, idx, "localpref");
+ attr->local_pref = lua_tointeger(L, -1);
+ lua_pop(L, 1);
+ lua_pop(L, 1);
+}
+
+void *lua_toattr(lua_State *L, int idx)
+{
+ struct attr *attr = XCALLOC(MTYPE_TMP, sizeof(struct attr));
+
+ lua_decode_attr(L, idx, attr);
+ return attr;
+}
+
+struct frrscript_codec frrscript_codecs_bgpd[] = {
+ {.typename = "peer",
+ .encoder = (encoder_func)lua_pushpeer,
+ .decoder = NULL},
+ {.typename = "attr",
+ .encoder = (encoder_func)lua_pushattr,
+ .decoder = lua_toattr},
+ {}};
+
+void bgp_script_init(void)
+{
+ frrscript_register_type_codecs(frrscript_codecs_bgpd);
+}
+
+#endif /* HAVE_SCRIPTING */
diff --git a/bgpd/bgp_script.h b/bgpd/bgp_script.h
new file mode 100644
index 0000000..f8178aa
--- /dev/null
+++ b/bgpd/bgp_script.h
@@ -0,0 +1,45 @@
+/* BGP scripting foo
+ * Copyright (C) 2020 NVIDIA Corporation
+ * Quentin Young
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; see the file COPYING; if not, write to the
+ * Free Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston,
+ * MA 02110-1301 USA
+ */
+#ifndef __BGP_SCRIPT__
+#define __BGP_SCRIPT__
+
+#include <zebra.h>
+#include "bgpd.h"
+
+#ifdef HAVE_SCRIPTING
+
+#include "frrlua.h"
+
+/*
+ * Initialize scripting stuff.
+ */
+void bgp_script_init(void);
+
+void lua_pushpeer(lua_State *L, const struct peer *peer);
+
+void lua_pushattr(lua_State *L, const struct attr *attr);
+
+void lua_decode_attr(lua_State *L, int idx, struct attr *attr);
+
+void *lua_toattr(lua_State *L, int idx);
+
+#endif /* HAVE_SCRIPTING */
+
+#endif /* __BGP_SCRIPT__ */
diff --git a/bgpd/bgp_snmp.c b/bgpd/bgp_snmp.c
new file mode 100644
index 0000000..6bc3134
--- /dev/null
+++ b/bgpd/bgp_snmp.c
@@ -0,0 +1,916 @@
+/* BGP4 SNMP support
+ * Copyright (C) 1999, 2000 Kunihiro Ishiguro
+ *
+ * This file is part of GNU Zebra.
+ *
+ * GNU Zebra is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License as published by the
+ * Free Software Foundation; either version 2, or (at your option) any
+ * later version.
+ *
+ * GNU Zebra is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program; see the file COPYING; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
+ */
+
+#include <zebra.h>
+
+#include <net-snmp/net-snmp-config.h>
+#include <net-snmp/net-snmp-includes.h>
+
+#include "if.h"
+#include "log.h"
+#include "prefix.h"
+#include "command.h"
+#include "thread.h"
+#include "smux.h"
+#include "filter.h"
+#include "hook.h"
+#include "libfrr.h"
+#include "lib/version.h"
+
+#include "bgpd/bgpd.h"
+#include "bgpd/bgp_table.h"
+#include "bgpd/bgp_aspath.h"
+#include "bgpd/bgp_attr.h"
+#include "bgpd/bgp_route.h"
+#include "bgpd/bgp_fsm.h"
+#include "bgpd/bgp_mplsvpn_snmp.h"
+
+/* BGP4-MIB described in RFC1657. */
+#define BGP4MIB 1,3,6,1,2,1,15
+
+/* BGP TRAP. */
+#define BGPESTABLISHED 1
+#define BGPBACKWARDTRANSITION 2
+
+/* BGP MIB bgpVersion. */
+#define BGPVERSION 0
+
+/* BGP MIB bgpLocalAs. */
+#define BGPLOCALAS 0
+
+/* BGP MIB bgpPeerTable. */
+#define BGPPEERIDENTIFIER 1
+#define BGPPEERSTATE 2
+#define BGPPEERADMINSTATUS 3
+#define BGPPEERNEGOTIATEDVERSION 4
+#define BGPPEERLOCALADDR 5
+#define BGPPEERLOCALPORT 6
+#define BGPPEERREMOTEADDR 7
+#define BGPPEERREMOTEPORT 8
+#define BGPPEERREMOTEAS 9
+#define BGPPEERINUPDATES 10
+#define BGPPEEROUTUPDATES 11
+#define BGPPEERINTOTALMESSAGES 12
+#define BGPPEEROUTTOTALMESSAGES 13
+#define BGPPEERLASTERROR 14
+#define BGPPEERFSMESTABLISHEDTRANSITIONS 15
+#define BGPPEERFSMESTABLISHEDTIME 16
+#define BGPPEERCONNECTRETRYINTERVAL 17
+#define BGPPEERHOLDTIME 18
+#define BGPPEERKEEPALIVE 19
+#define BGPPEERHOLDTIMECONFIGURED 20
+#define BGPPEERKEEPALIVECONFIGURED 21
+#define BGPPEERMINROUTEADVERTISEMENTINTERVAL 22
+#define BGPPEERINUPDATEELAPSEDTIME 23
+
+/* BGP MIB bgpIdentifier. */
+#define BGPIDENTIFIER 0
+
+/* BGP MIB bgpRcvdPathAttrTable */
+#define BGPPATHATTRPEER 1
+#define BGPPATHATTRDESTNETWORK 2
+#define BGPPATHATTRORIGIN 3
+#define BGPPATHATTRASPATH 4
+#define BGPPATHATTRNEXTHOP 5
+#define BGPPATHATTRINTERASMETRIC 6
+
+/* BGP MIB bgp4PathAttrTable. */
+#define BGP4PATHATTRPEER 1
+#define BGP4PATHATTRIPADDRPREFIXLEN 2
+#define BGP4PATHATTRIPADDRPREFIX 3
+#define BGP4PATHATTRORIGIN 4
+#define BGP4PATHATTRASPATHSEGMENT 5
+#define BGP4PATHATTRNEXTHOP 6
+#define BGP4PATHATTRMULTIEXITDISC 7
+#define BGP4PATHATTRLOCALPREF 8
+#define BGP4PATHATTRATOMICAGGREGATE 9
+#define BGP4PATHATTRAGGREGATORAS 10
+#define BGP4PATHATTRAGGREGATORADDR 11
+#define BGP4PATHATTRCALCLOCALPREF 12
+#define BGP4PATHATTRBEST 13
+#define BGP4PATHATTRUNKNOWN 14
+
+/* SNMP value hack. */
+#define INTEGER ASN_INTEGER
+#define INTEGER32 ASN_INTEGER
+#define COUNTER32 ASN_COUNTER
+#define OCTET_STRING ASN_OCTET_STR
+#define IPADDRESS ASN_IPADDRESS
+#define GAUGE32 ASN_UNSIGNED
+
+/* Declare static local variables for convenience. */
+SNMP_LOCAL_VARIABLES
+
+/* BGP-MIB instances. */
+static oid bgp_oid[] = {BGP4MIB};
+static oid bgp_trap_oid[] = {BGP4MIB, 0};
+
+/* IP address 0.0.0.0. */
+static struct in_addr bgp_empty_addr = {.s_addr = 0};
+
+/* Hook functions. */
+static uint8_t *bgpVersion(struct variable *, oid[], size_t *, int, size_t *,
+ WriteMethod **);
+static uint8_t *bgpLocalAs(struct variable *, oid[], size_t *, int, size_t *,
+ WriteMethod **);
+static uint8_t *bgpPeerTable(struct variable *, oid[], size_t *, int, size_t *,
+ WriteMethod **);
+static uint8_t *bgpRcvdPathAttrTable(struct variable *, oid[], size_t *, int,
+ size_t *, WriteMethod **);
+static uint8_t *bgpIdentifier(struct variable *, oid[], size_t *, int, size_t *,
+ WriteMethod **);
+static uint8_t *bgp4PathAttrTable(struct variable *, oid[], size_t *, int,
+ size_t *, WriteMethod **);
+/* static uint8_t *bgpTraps (); */
+
+static struct variable bgp_variables[] = {
+ /* BGP version. */
+ {BGPVERSION, OCTET_STRING, RONLY, bgpVersion, 1, {1}},
+ /* BGP local AS. */
+ {BGPLOCALAS, INTEGER, RONLY, bgpLocalAs, 1, {2}},
+ /* BGP peer table. */
+ {BGPPEERIDENTIFIER, IPADDRESS, RONLY, bgpPeerTable, 3, {3, 1, 1}},
+ {BGPPEERSTATE, INTEGER, RONLY, bgpPeerTable, 3, {3, 1, 2}},
+ {BGPPEERADMINSTATUS, INTEGER, RWRITE, bgpPeerTable, 3, {3, 1, 3}},
+ {BGPPEERNEGOTIATEDVERSION,
+ INTEGER32,
+ RONLY,
+ bgpPeerTable,
+ 3,
+ {3, 1, 4}},
+ {BGPPEERLOCALADDR, IPADDRESS, RONLY, bgpPeerTable, 3, {3, 1, 5}},
+ {BGPPEERLOCALPORT, INTEGER, RONLY, bgpPeerTable, 3, {3, 1, 6}},
+ {BGPPEERREMOTEADDR, IPADDRESS, RONLY, bgpPeerTable, 3, {3, 1, 7}},
+ {BGPPEERREMOTEPORT, INTEGER, RONLY, bgpPeerTable, 3, {3, 1, 8}},
+ {BGPPEERREMOTEAS, INTEGER, RONLY, bgpPeerTable, 3, {3, 1, 9}},
+ {BGPPEERINUPDATES, COUNTER32, RONLY, bgpPeerTable, 3, {3, 1, 10}},
+ {BGPPEEROUTUPDATES, COUNTER32, RONLY, bgpPeerTable, 3, {3, 1, 11}},
+ {BGPPEERINTOTALMESSAGES, COUNTER32, RONLY, bgpPeerTable, 3, {3, 1, 12}},
+ {BGPPEEROUTTOTALMESSAGES,
+ COUNTER32,
+ RONLY,
+ bgpPeerTable,
+ 3,
+ {3, 1, 13}},
+ {BGPPEERLASTERROR, OCTET_STRING, RONLY, bgpPeerTable, 3, {3, 1, 14}},
+ {BGPPEERFSMESTABLISHEDTRANSITIONS,
+ COUNTER32,
+ RONLY,
+ bgpPeerTable,
+ 3,
+ {3, 1, 15}},
+ {BGPPEERFSMESTABLISHEDTIME,
+ GAUGE32,
+ RONLY,
+ bgpPeerTable,
+ 3,
+ {3, 1, 16}},
+ {BGPPEERCONNECTRETRYINTERVAL,
+ INTEGER,
+ RWRITE,
+ bgpPeerTable,
+ 3,
+ {3, 1, 17}},
+ {BGPPEERHOLDTIME, INTEGER, RONLY, bgpPeerTable, 3, {3, 1, 18}},
+ {BGPPEERKEEPALIVE, INTEGER, RONLY, bgpPeerTable, 3, {3, 1, 19}},
+ {BGPPEERHOLDTIMECONFIGURED,
+ INTEGER,
+ RWRITE,
+ bgpPeerTable,
+ 3,
+ {3, 1, 20}},
+ {BGPPEERKEEPALIVECONFIGURED,
+ INTEGER,
+ RWRITE,
+ bgpPeerTable,
+ 3,
+ {3, 1, 21}},
+ {BGPPEERMINROUTEADVERTISEMENTINTERVAL,
+ INTEGER,
+ RWRITE,
+ bgpPeerTable,
+ 3,
+ {3, 1, 23}},
+ {BGPPEERINUPDATEELAPSEDTIME,
+ GAUGE32,
+ RONLY,
+ bgpPeerTable,
+ 3,
+ {3, 1, 24}},
+ /* BGP identifier. */
+ {BGPIDENTIFIER, IPADDRESS, RONLY, bgpIdentifier, 1, {4}},
+ /* BGP received path attribute table. */
+ {BGPPATHATTRPEER, IPADDRESS, RONLY, bgpRcvdPathAttrTable, 3, {5, 1, 1}},
+ {BGPPATHATTRDESTNETWORK,
+ IPADDRESS,
+ RONLY,
+ bgpRcvdPathAttrTable,
+ 3,
+ {5, 1, 2}},
+ {BGPPATHATTRORIGIN, INTEGER, RONLY, bgpRcvdPathAttrTable, 3, {5, 1, 3}},
+ {BGPPATHATTRASPATH,
+ OCTET_STRING,
+ RONLY,
+ bgpRcvdPathAttrTable,
+ 3,
+ {5, 1, 4}},
+ {BGPPATHATTRNEXTHOP,
+ IPADDRESS,
+ RONLY,
+ bgpRcvdPathAttrTable,
+ 3,
+ {5, 1, 5}},
+ {BGPPATHATTRINTERASMETRIC,
+ INTEGER32,
+ RONLY,
+ bgpRcvdPathAttrTable,
+ 3,
+ {5, 1, 6}},
+ /* BGP-4 received path attribute table. */
+ {BGP4PATHATTRPEER, IPADDRESS, RONLY, bgp4PathAttrTable, 3, {6, 1, 1}},
+ {BGP4PATHATTRIPADDRPREFIXLEN,
+ INTEGER,
+ RONLY,
+ bgp4PathAttrTable,
+ 3,
+ {6, 1, 2}},
+ {BGP4PATHATTRIPADDRPREFIX,
+ IPADDRESS,
+ RONLY,
+ bgp4PathAttrTable,
+ 3,
+ {6, 1, 3}},
+ {BGP4PATHATTRORIGIN, INTEGER, RONLY, bgp4PathAttrTable, 3, {6, 1, 4}},
+ {BGP4PATHATTRASPATHSEGMENT,
+ OCTET_STRING,
+ RONLY,
+ bgp4PathAttrTable,
+ 3,
+ {6, 1, 5}},
+ {BGP4PATHATTRNEXTHOP,
+ IPADDRESS,
+ RONLY,
+ bgp4PathAttrTable,
+ 3,
+ {6, 1, 6}},
+ {BGP4PATHATTRMULTIEXITDISC,
+ INTEGER,
+ RONLY,
+ bgp4PathAttrTable,
+ 3,
+ {6, 1, 7}},
+ {BGP4PATHATTRLOCALPREF,
+ INTEGER,
+ RONLY,
+ bgp4PathAttrTable,
+ 3,
+ {6, 1, 8}},
+ {BGP4PATHATTRATOMICAGGREGATE,
+ INTEGER,
+ RONLY,
+ bgp4PathAttrTable,
+ 3,
+ {6, 1, 9}},
+ {BGP4PATHATTRAGGREGATORAS,
+ INTEGER,
+ RONLY,
+ bgp4PathAttrTable,
+ 3,
+ {6, 1, 10}},
+ {BGP4PATHATTRAGGREGATORADDR,
+ IPADDRESS,
+ RONLY,
+ bgp4PathAttrTable,
+ 3,
+ {6, 1, 11}},
+ {BGP4PATHATTRCALCLOCALPREF,
+ INTEGER,
+ RONLY,
+ bgp4PathAttrTable,
+ 3,
+ {6, 1, 12}},
+ {BGP4PATHATTRBEST, INTEGER, RONLY, bgp4PathAttrTable, 3, {6, 1, 13}},
+ {BGP4PATHATTRUNKNOWN,
+ OCTET_STRING,
+ RONLY,
+ bgp4PathAttrTable,
+ 3,
+ {6, 1, 14}},
+};
+
+
+static uint8_t *bgpVersion(struct variable *v, oid name[], size_t *length,
+ int exact, size_t *var_len,
+ WriteMethod **write_method)
+{
+ static uint8_t version;
+
+ if (smux_header_generic(v, name, length, exact, var_len, write_method)
+ == MATCH_FAILED)
+ return NULL;
+
+ /* Return BGP version. Zebra bgpd only support version 4. */
+ version = (0x80 >> (BGP_VERSION_4 - 1));
+
+ /* Return octet string length 1. */
+ *var_len = 1;
+ return &version;
+}
+
+static uint8_t *bgpLocalAs(struct variable *v, oid name[], size_t *length,
+ int exact, size_t *var_len,
+ WriteMethod **write_method)
+{
+ struct bgp *bgp;
+
+ if (smux_header_generic(v, name, length, exact, var_len, write_method)
+ == MATCH_FAILED)
+ return NULL;
+
+ /* Get BGP structure. */
+ bgp = bgp_get_default();
+ if (!bgp)
+ return NULL;
+
+ return SNMP_INTEGER(bgp->as);
+}
+
+static struct peer *peer_lookup_addr_ipv4(struct in_addr *src)
+{
+ struct bgp *bgp;
+ struct peer *peer;
+ struct listnode *node;
+ struct listnode *bgpnode;
+
+ for (ALL_LIST_ELEMENTS_RO(bm->bgp, bgpnode, bgp)) {
+ for (ALL_LIST_ELEMENTS_RO(bgp->peer, node, peer)) {
+ if (sockunion_family(&peer->su) != AF_INET)
+ continue;
+
+ if (sockunion2ip(&peer->su) == src->s_addr)
+ return peer;
+ }
+ }
+
+ return NULL;
+}
+
+static struct peer *bgp_peer_lookup_next(struct in_addr *src)
+{
+ struct bgp *bgp;
+ struct peer *peer;
+ struct peer *next_peer = NULL;
+ struct listnode *node;
+ struct listnode *bgpnode;
+
+ for (ALL_LIST_ELEMENTS_RO(bm->bgp, bgpnode, bgp)) {
+ for (ALL_LIST_ELEMENTS_RO(bgp->peer, node, peer)) {
+ if (sockunion_family(&peer->su) != AF_INET)
+ continue;
+ if (ntohl(sockunion2ip(&peer->su)) <= ntohl(src->s_addr))
+ continue;
+
+ if (!next_peer
+ || ntohl(sockunion2ip(&next_peer->su))
+ > ntohl(sockunion2ip(&peer->su))) {
+ next_peer = peer;
+ }
+ }
+ }
+
+ if (next_peer) {
+ src->s_addr = sockunion2ip(&next_peer->su);
+ return next_peer;
+ }
+
+ return NULL;
+}
+
+/* 1.3.6.1.2.1.15.3.1.x = 10 */
+#define PEERTAB_NAMELEN 10
+
+static struct peer *bgpPeerTable_lookup(struct variable *v, oid name[],
+ size_t *length, struct in_addr *addr,
+ int exact)
+{
+ struct peer *peer = NULL;
+ size_t namelen = v ? v->namelen : PEERTAB_NAMELEN;
+ int len;
+
+ if (exact) {
+ /* Check the length. */
+ if (*length - namelen != sizeof(struct in_addr))
+ return NULL;
+
+ oid2in_addr(name + namelen, IN_ADDR_SIZE, addr);
+
+ peer = peer_lookup_addr_ipv4(addr);
+ return peer;
+ } else {
+ len = *length - namelen;
+ if (len > 4)
+ len = 4;
+
+ oid2in_addr(name + namelen, len, addr);
+
+ peer = bgp_peer_lookup_next(addr);
+
+ if (peer == NULL)
+ return NULL;
+
+ oid_copy_in_addr(name + namelen, addr);
+ *length = sizeof(struct in_addr) + namelen;
+
+ return peer;
+ }
+ return NULL;
+}
+
+/* BGP write methods. */
+static int write_bgpPeerTable(int action, uint8_t *var_val,
+ uint8_t var_val_type, size_t var_val_len,
+ uint8_t *statP, oid *name, size_t length)
+{
+ struct in_addr addr;
+ struct peer *peer;
+ long intval;
+
+ if (var_val_type != ASN_INTEGER) {
+ return SNMP_ERR_WRONGTYPE;
+ }
+ if (var_val_len != sizeof(long)) {
+ return SNMP_ERR_WRONGLENGTH;
+ }
+
+ intval = *(long *)var_val;
+
+ memset(&addr, 0, sizeof(addr));
+
+ peer = bgpPeerTable_lookup(NULL, name, &length, &addr, 1);
+ if (!peer)
+ return SNMP_ERR_NOSUCHNAME;
+
+ if (action != SNMP_MSG_INTERNAL_SET_COMMIT)
+ return SNMP_ERR_NOERROR;
+
+ zlog_info("%s: SNMP write .%ld = %ld", peer->host,
+ (long)name[PEERTAB_NAMELEN - 1], intval);
+
+ switch (name[PEERTAB_NAMELEN - 1]) {
+ case BGPPEERADMINSTATUS:
+#define BGP_PeerAdmin_stop 1
+#define BGP_PeerAdmin_start 2
+ /* When the peer is established, */
+ if (intval == BGP_PeerAdmin_stop)
+ BGP_EVENT_ADD(peer, BGP_Stop);
+ else if (intval == BGP_PeerAdmin_start)
+ ; /* Do nothing. */
+ else
+ return SNMP_ERR_NOSUCHNAME;
+ break;
+ case BGPPEERCONNECTRETRYINTERVAL:
+ peer_flag_set(peer, PEER_FLAG_TIMER_CONNECT);
+ peer->connect = intval;
+ peer->v_connect = intval;
+ break;
+ case BGPPEERHOLDTIMECONFIGURED:
+ peer_flag_set(peer, PEER_FLAG_TIMER);
+ peer->holdtime = intval;
+ peer->v_holdtime = intval;
+ break;
+ case BGPPEERKEEPALIVECONFIGURED:
+ peer_flag_set(peer, PEER_FLAG_TIMER);
+ peer->keepalive = intval;
+ peer->v_keepalive = intval;
+ break;
+ case BGPPEERMINROUTEADVERTISEMENTINTERVAL:
+ peer->v_routeadv = intval;
+ break;
+ }
+ return SNMP_ERR_NOERROR;
+}
+
+static uint8_t *bgpPeerTable(struct variable *v, oid name[], size_t *length,
+ int exact, size_t *var_len,
+ WriteMethod **write_method)
+{
+ static struct in_addr addr;
+ struct peer *peer;
+ uint32_t ui, uo;
+
+ if (smux_header_table(v, name, length, exact, var_len, write_method)
+ == MATCH_FAILED)
+ return NULL;
+ memset(&addr, 0, sizeof(addr));
+
+ peer = bgpPeerTable_lookup(v, name, length, &addr, exact);
+ if (!peer)
+ return NULL;
+
+ switch (v->magic) {
+ case BGPPEERIDENTIFIER:
+ return SNMP_IPADDRESS(peer->remote_id);
+ case BGPPEERSTATE:
+ return SNMP_INTEGER(peer->status);
+ case BGPPEERADMINSTATUS:
+ *write_method = write_bgpPeerTable;
+#define BGP_PeerAdmin_stop 1
+#define BGP_PeerAdmin_start 2
+ if (CHECK_FLAG(peer->flags, PEER_FLAG_SHUTDOWN))
+ return SNMP_INTEGER(BGP_PeerAdmin_stop);
+ else
+ return SNMP_INTEGER(BGP_PeerAdmin_start);
+ case BGPPEERNEGOTIATEDVERSION:
+ return SNMP_INTEGER(BGP_VERSION_4);
+ case BGPPEERLOCALADDR:
+ if (peer->su_local)
+ return SNMP_IPADDRESS(peer->su_local->sin.sin_addr);
+ else
+ return SNMP_IPADDRESS(bgp_empty_addr);
+ case BGPPEERLOCALPORT:
+ if (peer->su_local)
+ return SNMP_INTEGER(
+ ntohs(peer->su_local->sin.sin_port));
+ else
+ return SNMP_INTEGER(0);
+ case BGPPEERREMOTEADDR:
+ if (peer->su_remote)
+ return SNMP_IPADDRESS(peer->su_remote->sin.sin_addr);
+ else
+ return SNMP_IPADDRESS(bgp_empty_addr);
+ case BGPPEERREMOTEPORT:
+ if (peer->su_remote)
+ return SNMP_INTEGER(
+ ntohs(peer->su_remote->sin.sin_port));
+ else
+ return SNMP_INTEGER(0);
+ case BGPPEERREMOTEAS:
+ return SNMP_INTEGER(peer->as);
+ case BGPPEERINUPDATES:
+ ui = atomic_load_explicit(&peer->update_in,
+ memory_order_relaxed);
+ return SNMP_INTEGER(ui);
+ case BGPPEEROUTUPDATES:
+ uo = atomic_load_explicit(&peer->update_out,
+ memory_order_relaxed);
+ return SNMP_INTEGER(uo);
+ case BGPPEERINTOTALMESSAGES:
+ return SNMP_INTEGER(PEER_TOTAL_RX(peer));
+ case BGPPEEROUTTOTALMESSAGES:
+ return SNMP_INTEGER(PEER_TOTAL_TX(peer));
+ case BGPPEERLASTERROR: {
+ static uint8_t lasterror[2];
+ lasterror[0] = peer->notify.code;
+ lasterror[1] = peer->notify.subcode;
+ *var_len = 2;
+ return (uint8_t *)&lasterror;
+ }
+ case BGPPEERFSMESTABLISHEDTRANSITIONS:
+ return SNMP_INTEGER(peer->established);
+ case BGPPEERFSMESTABLISHEDTIME:
+ if (peer->uptime == 0)
+ return SNMP_INTEGER(0);
+ else
+ return SNMP_INTEGER(monotime(NULL) - peer->uptime);
+ case BGPPEERCONNECTRETRYINTERVAL:
+ *write_method = write_bgpPeerTable;
+ return SNMP_INTEGER(peer->v_connect);
+ case BGPPEERHOLDTIME:
+ return SNMP_INTEGER(peer->v_holdtime);
+ case BGPPEERKEEPALIVE:
+ return SNMP_INTEGER(peer->v_keepalive);
+ case BGPPEERHOLDTIMECONFIGURED:
+ *write_method = write_bgpPeerTable;
+ if (CHECK_FLAG(peer->flags, PEER_FLAG_TIMER))
+ return SNMP_INTEGER(peer->holdtime);
+ else
+ return SNMP_INTEGER(peer->v_holdtime);
+ case BGPPEERKEEPALIVECONFIGURED:
+ *write_method = write_bgpPeerTable;
+ if (CHECK_FLAG(peer->flags, PEER_FLAG_TIMER))
+ return SNMP_INTEGER(peer->keepalive);
+ else
+ return SNMP_INTEGER(peer->v_keepalive);
+ case BGPPEERMINROUTEADVERTISEMENTINTERVAL:
+ *write_method = write_bgpPeerTable;
+ return SNMP_INTEGER(peer->v_routeadv);
+ case BGPPEERINUPDATEELAPSEDTIME:
+ if (peer->update_time == 0)
+ return SNMP_INTEGER(0);
+ else
+ return SNMP_INTEGER(monotime(NULL) - peer->update_time);
+ default:
+ return NULL;
+ }
+ return NULL;
+}
+
+static uint8_t *bgpIdentifier(struct variable *v, oid name[], size_t *length,
+ int exact, size_t *var_len,
+ WriteMethod **write_method)
+{
+ struct bgp *bgp;
+
+ if (smux_header_generic(v, name, length, exact, var_len, write_method)
+ == MATCH_FAILED)
+ return NULL;
+
+ bgp = bgp_get_default();
+ if (!bgp)
+ return NULL;
+
+ return SNMP_IPADDRESS(bgp->router_id);
+}
+
+static uint8_t *bgpRcvdPathAttrTable(struct variable *v, oid name[],
+ size_t *length, int exact, size_t *var_len,
+ WriteMethod **write_method)
+{
+ /* Received Path Attribute Table. This table contains, one entry
+ per path to a network, path attributes received from all peers
+ running BGP version 3 or less. This table is obsolete, having
+ been replaced in functionality with the bgp4PathAttrTable. */
+ return NULL;
+}
+
+static struct bgp_path_info *bgp4PathAttrLookup(struct variable *v, oid name[],
+ size_t *length, struct bgp *bgp,
+ struct prefix_ipv4 *addr,
+ int exact)
+{
+ oid *offset;
+ int offsetlen;
+ struct bgp_path_info *path;
+ struct bgp_path_info *min;
+ struct bgp_dest *dest;
+ union sockunion su;
+ unsigned int len;
+ struct in_addr paddr;
+
+ sockunion_init(&su);
+
+#define BGP_PATHATTR_ENTRY_OFFSET (IN_ADDR_SIZE + 1 + IN_ADDR_SIZE)
+
+ if (exact) {
+ if (*length - v->namelen != BGP_PATHATTR_ENTRY_OFFSET)
+ return NULL;
+
+ /* Set OID offset for prefix. */
+ offset = name + v->namelen;
+ oid2in_addr(offset, IN_ADDR_SIZE, &addr->prefix);
+ offset += IN_ADDR_SIZE;
+
+ /* Prefix length. */
+ addr->prefixlen = *offset;
+ offset++;
+
+ /* Peer address. */
+ su.sin.sin_family = AF_INET;
+ oid2in_addr(offset, IN_ADDR_SIZE, &su.sin.sin_addr);
+
+ /* Lookup node. */
+ dest = bgp_node_lookup(bgp->rib[AFI_IP][SAFI_UNICAST],
+ (struct prefix *)addr);
+ if (dest) {
+ for (path = bgp_dest_get_bgp_path_info(dest); path;
+ path = path->next)
+ if (sockunion_same(&path->peer->su, &su))
+ return path;
+
+ bgp_dest_unlock_node(dest);
+ }
+ } else {
+ offset = name + v->namelen;
+ offsetlen = *length - v->namelen;
+ len = offsetlen;
+
+ if (offsetlen == 0)
+ dest = bgp_table_top(bgp->rib[AFI_IP][SAFI_UNICAST]);
+ else {
+ if (len > IN_ADDR_SIZE)
+ len = IN_ADDR_SIZE;
+
+ oid2in_addr(offset, len, &addr->prefix);
+
+ offset += IN_ADDR_SIZE;
+ offsetlen -= IN_ADDR_SIZE;
+
+ if (offsetlen > 0)
+ addr->prefixlen = *offset;
+ else
+ addr->prefixlen = len * 8;
+
+ dest = bgp_node_get(bgp->rib[AFI_IP][SAFI_UNICAST],
+ (struct prefix *)addr);
+
+ offset++;
+ offsetlen--;
+ }
+
+ if (offsetlen > 0) {
+ len = offsetlen;
+ if (len > IN_ADDR_SIZE)
+ len = IN_ADDR_SIZE;
+
+ oid2in_addr(offset, len, &paddr);
+ } else
+ paddr.s_addr = INADDR_ANY;
+
+ if (!dest)
+ return NULL;
+
+ do {
+ min = NULL;
+
+ for (path = bgp_dest_get_bgp_path_info(dest); path;
+ path = path->next) {
+ if (path->peer->su.sin.sin_family == AF_INET
+ && ntohl(paddr.s_addr)
+ < ntohl(path->peer->su.sin
+ .sin_addr
+ .s_addr)) {
+ if (min) {
+ if (ntohl(path->peer->su.sin
+ .sin_addr
+ .s_addr)
+ < ntohl(min->peer->su.sin
+ .sin_addr
+ .s_addr))
+ min = path;
+ } else
+ min = path;
+ }
+ }
+
+ if (min) {
+ const struct prefix *rn_p =
+ bgp_dest_get_prefix(dest);
+
+ *length =
+ v->namelen + BGP_PATHATTR_ENTRY_OFFSET;
+
+ offset = name + v->namelen;
+ oid_copy_in_addr(offset, &rn_p->u.prefix4);
+ offset += IN_ADDR_SIZE;
+ *offset = rn_p->prefixlen;
+ offset++;
+ oid_copy_in_addr(offset,
+ &min->peer->su.sin.sin_addr);
+ addr->prefix = rn_p->u.prefix4;
+ addr->prefixlen = rn_p->prefixlen;
+
+ bgp_dest_unlock_node(dest);
+
+ return min;
+ }
+
+ paddr.s_addr = INADDR_ANY;
+ } while ((dest = bgp_route_next(dest)) != NULL);
+ }
+ return NULL;
+}
+
+static uint8_t *bgp4PathAttrTable(struct variable *v, oid name[],
+ size_t *length, int exact, size_t *var_len,
+ WriteMethod **write_method)
+{
+ struct bgp *bgp;
+ struct bgp_path_info *path;
+ struct prefix_ipv4 addr;
+
+ bgp = bgp_get_default();
+ if (!bgp)
+ return NULL;
+
+ if (smux_header_table(v, name, length, exact, var_len, write_method)
+ == MATCH_FAILED)
+ return NULL;
+ memset(&addr, 0, sizeof(addr));
+
+ path = bgp4PathAttrLookup(v, name, length, bgp, &addr, exact);
+ if (!path)
+ return NULL;
+
+ switch (v->magic) {
+ case BGP4PATHATTRPEER: /* 1 */
+ return SNMP_IPADDRESS(path->peer->su.sin.sin_addr);
+ case BGP4PATHATTRIPADDRPREFIXLEN: /* 2 */
+ return SNMP_INTEGER(addr.prefixlen);
+ case BGP4PATHATTRIPADDRPREFIX: /* 3 */
+ return SNMP_IPADDRESS(addr.prefix);
+ case BGP4PATHATTRORIGIN: /* 4 */
+ return SNMP_INTEGER(path->attr->origin);
+ case BGP4PATHATTRASPATHSEGMENT: /* 5 */
+ return aspath_snmp_pathseg(path->attr->aspath, var_len);
+ case BGP4PATHATTRNEXTHOP: /* 6 */
+ return SNMP_IPADDRESS(path->attr->nexthop);
+ case BGP4PATHATTRMULTIEXITDISC: /* 7 */
+ return SNMP_INTEGER(path->attr->med);
+ case BGP4PATHATTRLOCALPREF: /* 8 */
+ return SNMP_INTEGER(path->attr->local_pref);
+ case BGP4PATHATTRATOMICAGGREGATE: /* 9 */
+ return SNMP_INTEGER(1);
+ case BGP4PATHATTRAGGREGATORAS: /* 10 */
+ return SNMP_INTEGER(path->attr->aggregator_as);
+ case BGP4PATHATTRAGGREGATORADDR: /* 11 */
+ return SNMP_IPADDRESS(path->attr->aggregator_addr);
+ case BGP4PATHATTRCALCLOCALPREF: /* 12 */
+ return SNMP_INTEGER(-1);
+ case BGP4PATHATTRBEST: /* 13 */
+#define BGP4_PathAttrBest_false 1
+#define BGP4_PathAttrBest_true 2
+ if (CHECK_FLAG(path->flags, BGP_PATH_SELECTED))
+ return SNMP_INTEGER(BGP4_PathAttrBest_true);
+ else
+ return SNMP_INTEGER(BGP4_PathAttrBest_false);
+ case BGP4PATHATTRUNKNOWN: /* 14 */
+ *var_len = 0;
+ return NULL;
+ }
+ return NULL;
+}
+
+/* BGP Traps. */
+static struct trap_object bgpTrapList[] = {{3, {3, 1, BGPPEERREMOTEADDR} },
+ {3, {3, 1, BGPPEERLASTERROR} },
+ {3, {3, 1, BGPPEERSTATE} } };
+
+static int bgpTrapEstablished(struct peer *peer)
+{
+ int ret;
+ struct in_addr addr;
+ oid index[sizeof(oid) * IN_ADDR_SIZE];
+
+ /* Check if this peer just went to Established */
+ if ((peer->ostatus != OpenConfirm) || !(peer_established(peer)))
+ return 0;
+
+ ret = inet_aton(peer->host, &addr);
+ if (ret == 0)
+ return 0;
+
+ oid_copy_in_addr(index, &addr);
+
+ smux_trap(bgp_variables, array_size(bgp_variables), bgp_trap_oid,
+ array_size(bgp_trap_oid), bgp_oid,
+ sizeof(bgp_oid) / sizeof(oid), index, IN_ADDR_SIZE,
+ bgpTrapList, array_size(bgpTrapList), BGPESTABLISHED);
+ return 0;
+}
+
+static int bgpTrapBackwardTransition(struct peer *peer)
+{
+ int ret;
+ struct in_addr addr;
+ oid index[sizeof(oid) * IN_ADDR_SIZE];
+
+ ret = inet_aton(peer->host, &addr);
+ if (ret == 0)
+ return 0;
+
+ oid_copy_in_addr(index, &addr);
+
+ smux_trap(bgp_variables, array_size(bgp_variables), bgp_trap_oid,
+ array_size(bgp_trap_oid), bgp_oid,
+ sizeof(bgp_oid) / sizeof(oid), index, IN_ADDR_SIZE,
+ bgpTrapList, array_size(bgpTrapList), BGPBACKWARDTRANSITION);
+ return 0;
+}
+
+static int bgp_snmp_init(struct thread_master *tm)
+{
+ smux_init(tm);
+ REGISTER_MIB("mibII/bgp", bgp_variables, variable, bgp_oid);
+ bgp_mpls_l3vpn_module_init();
+ return 0;
+}
+
+static int bgp_snmp_module_init(void)
+{
+ hook_register(peer_status_changed, bgpTrapEstablished);
+ hook_register(peer_backward_transition, bgpTrapBackwardTransition);
+ hook_register(frr_late_init, bgp_snmp_init);
+ return 0;
+}
+
+FRR_MODULE_SETUP(.name = "bgpd_snmp", .version = FRR_VERSION,
+ .description = "bgpd AgentX SNMP module",
+ .init = bgp_snmp_module_init,
+);
diff --git a/bgpd/bgp_table.c b/bgpd/bgp_table.c
new file mode 100644
index 0000000..0ca78d0
--- /dev/null
+++ b/bgpd/bgp_table.c
@@ -0,0 +1,254 @@
+/* BGP routing table
+ * Copyright (C) 1998, 2001 Kunihiro Ishiguro
+ *
+ * This file is part of GNU Zebra.
+ *
+ * GNU Zebra is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License as published by the
+ * Free Software Foundation; either version 2, or (at your option) any
+ * later version.
+ *
+ * GNU Zebra is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program; see the file COPYING; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
+ */
+
+#include <zebra.h>
+
+#include "prefix.h"
+#include "memory.h"
+#include "sockunion.h"
+#include "queue.h"
+#include "filter.h"
+#include "command.h"
+#include "printfrr.h"
+
+#include "bgpd/bgpd.h"
+#include "bgpd/bgp_table.h"
+#include "bgp_addpath.h"
+#include "bgp_trace.h"
+
+void bgp_table_lock(struct bgp_table *rt)
+{
+ rt->lock++;
+}
+
+void bgp_table_unlock(struct bgp_table *rt)
+{
+ assert(rt->lock > 0);
+ rt->lock--;
+
+ if (rt->lock != 0) {
+ return;
+ }
+
+ route_table_finish(rt->route_table);
+ rt->route_table = NULL;
+
+ XFREE(MTYPE_BGP_TABLE, rt);
+}
+
+void bgp_table_finish(struct bgp_table **rt)
+{
+ if (*rt != NULL) {
+ bgp_table_unlock(*rt);
+ *rt = NULL;
+ }
+}
+
+/*
+ * bgp_dest_unlock_node
+ */
+void bgp_dest_unlock_node(struct bgp_dest *dest)
+{
+ frrtrace(1, frr_bgp, bgp_dest_unlock, dest);
+ bgp_delete_listnode(dest);
+ route_unlock_node(bgp_dest_to_rnode(dest));
+}
+
+/*
+ * bgp_dest_lock_node
+ */
+struct bgp_dest *bgp_dest_lock_node(struct bgp_dest *dest)
+{
+ frrtrace(1, frr_bgp, bgp_dest_lock, dest);
+ struct route_node *rn = route_lock_node(bgp_dest_to_rnode(dest));
+
+ return bgp_dest_from_rnode(rn);
+}
+
+/*
+ * bgp_dest_get_prefix_str
+ */
+const char *bgp_dest_get_prefix_str(struct bgp_dest *dest)
+{
+ const struct prefix *p = NULL;
+ static char str[PREFIX_STRLEN] = {0};
+
+ p = bgp_dest_get_prefix(dest);
+ if (p)
+ return prefix2str(p, str, sizeof(str));
+
+ return NULL;
+}
+
+/*
+ * bgp_node_create
+ */
+static struct route_node *bgp_node_create(route_table_delegate_t *delegate,
+ struct route_table *table)
+{
+ struct bgp_node *node;
+ node = XCALLOC(MTYPE_BGP_NODE, sizeof(struct bgp_node));
+
+ RB_INIT(bgp_adj_out_rb, &node->adj_out);
+ return bgp_dest_to_rnode(node);
+}
+
+/*
+ * bgp_node_destroy
+ */
+static void bgp_node_destroy(route_table_delegate_t *delegate,
+ struct route_table *table, struct route_node *node)
+{
+ struct bgp_node *bgp_node;
+ struct bgp_table *rt;
+ bgp_node = bgp_dest_from_rnode(node);
+ rt = table->info;
+
+ if (rt->bgp) {
+ bgp_addpath_free_node_data(&rt->bgp->tx_addpath,
+ &bgp_node->tx_addpath,
+ rt->afi, rt->safi);
+ }
+
+ XFREE(MTYPE_BGP_NODE, bgp_node);
+}
+
+/*
+ * Function vector to customize the behavior of the route table
+ * library for BGP route tables.
+ */
+route_table_delegate_t bgp_table_delegate = {.create_node = bgp_node_create,
+ .destroy_node = bgp_node_destroy};
+
+/*
+ * bgp_table_init
+ */
+struct bgp_table *bgp_table_init(struct bgp *bgp, afi_t afi, safi_t safi)
+{
+ struct bgp_table *rt;
+
+ rt = XCALLOC(MTYPE_BGP_TABLE, sizeof(struct bgp_table));
+
+ rt->route_table = route_table_init_with_delegate(&bgp_table_delegate);
+
+ /*
+ * Set up back pointer to bgp_table.
+ */
+ route_table_set_info(rt->route_table, rt);
+
+ /*
+ * pointer to bgp instance allows working back from bgp_path_info to bgp
+ */
+ rt->bgp = bgp;
+
+ bgp_table_lock(rt);
+ rt->afi = afi;
+ rt->safi = safi;
+
+ return rt;
+}
+
+/* Delete the route node from the selection deferral route list */
+void bgp_delete_listnode(struct bgp_node *node)
+{
+ struct route_node *rn = NULL;
+ struct bgp_table *table = NULL;
+ struct bgp *bgp = NULL;
+ afi_t afi;
+ safi_t safi;
+
+ /* If the route to be deleted is selection pending, update the
+ * route node in gr_info
+ */
+ if (CHECK_FLAG(node->flags, BGP_NODE_SELECT_DEFER)) {
+ table = bgp_dest_table(node);
+
+ if (table) {
+ bgp = table->bgp;
+ afi = table->afi;
+ safi = table->safi;
+ } else
+ return;
+
+ rn = bgp_dest_to_rnode(node);
+
+ if (bgp && rn && rn->lock == 1) {
+ /* Delete the route from the selection pending list */
+ bgp->gr_info[afi][safi].gr_deferred--;
+ UNSET_FLAG(node->flags, BGP_NODE_SELECT_DEFER);
+ }
+ }
+}
+
+struct bgp_node *bgp_table_subtree_lookup(const struct bgp_table *table,
+ const struct prefix *p)
+{
+ struct bgp_node *node = bgp_dest_from_rnode(table->route_table->top);
+ struct bgp_node *matched = NULL;
+
+ if (node == NULL)
+ return NULL;
+
+
+ while (node) {
+ const struct prefix *node_p = bgp_dest_get_prefix(node);
+
+ if (node_p->prefixlen >= p->prefixlen) {
+ if (!prefix_match(p, node_p))
+ return NULL;
+
+ matched = node;
+ break;
+ }
+
+ if (!prefix_match(node_p, p))
+ return NULL;
+
+ if (node_p->prefixlen == p->prefixlen) {
+ matched = node;
+ break;
+ }
+
+ node = bgp_dest_from_rnode(node->link[prefix_bit(
+ &p->u.prefix, node_p->prefixlen)]);
+ }
+
+ if (!matched)
+ return NULL;
+
+ bgp_dest_lock_node(matched);
+ return matched;
+}
+
+printfrr_ext_autoreg_p("BD", printfrr_bd);
+static ssize_t printfrr_bd(struct fbuf *buf, struct printfrr_eargs *ea,
+ const void *ptr)
+{
+ const struct bgp_dest *dest = ptr;
+ const struct prefix *p = bgp_dest_get_prefix(dest);
+ char cbuf[PREFIX_STRLEN];
+
+ if (!dest)
+ return bputs(buf, "(null)");
+
+ /* need to get the real length even if buffer too small */
+ prefix2str(p, cbuf, sizeof(cbuf));
+ return bputs(buf, cbuf);
+}
diff --git a/bgpd/bgp_table.h b/bgpd/bgp_table.h
new file mode 100644
index 0000000..86cd4f3
--- /dev/null
+++ b/bgpd/bgp_table.h
@@ -0,0 +1,401 @@
+/* BGP routing table
+ * Copyright (C) 1998, 2001 Kunihiro Ishiguro
+ *
+ * This file is part of GNU Zebra.
+ *
+ * GNU Zebra is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License as published by the
+ * Free Software Foundation; either version 2, or (at your option) any
+ * later version.
+ *
+ * GNU Zebra is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program; see the file COPYING; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
+ */
+
+#ifndef _QUAGGA_BGP_TABLE_H
+#define _QUAGGA_BGP_TABLE_H
+
+/* XXX BEGIN TEMPORARY COMPAT */
+#define bgp_dest bgp_node
+/* XXX END TEMPORARY COMPAT */
+
+#include "mpls.h"
+#include "table.h"
+#include "queue.h"
+#include "linklist.h"
+#include "bgpd.h"
+#include "bgp_advertise.h"
+
+struct bgp_table {
+ /* table belongs to this instance */
+ struct bgp *bgp;
+
+ /* afi/safi of this table */
+ afi_t afi;
+ safi_t safi;
+
+ int lock;
+
+ /* soft_reconfig_table in progress */
+ bool soft_reconfig_init;
+ struct thread *soft_reconfig_thread;
+
+ /* list of peers on which soft_reconfig_table has to run */
+ struct list *soft_reconfig_peers;
+
+ struct route_table *route_table;
+ uint64_t version;
+};
+
+enum bgp_path_selection_reason {
+ bgp_path_selection_none,
+ bgp_path_selection_first,
+ bgp_path_selection_evpn_sticky_mac,
+ bgp_path_selection_evpn_seq,
+ bgp_path_selection_evpn_local_path,
+ bgp_path_selection_evpn_non_proxy,
+ bgp_path_selection_evpn_lower_ip,
+ bgp_path_selection_weight,
+ bgp_path_selection_local_pref,
+ bgp_path_selection_local_route,
+ bgp_path_selection_confed_as_path,
+ bgp_path_selection_as_path,
+ bgp_path_selection_origin,
+ bgp_path_selection_med,
+ bgp_path_selection_peer,
+ bgp_path_selection_confed,
+ bgp_path_selection_igp_metric,
+ bgp_path_selection_older,
+ bgp_path_selection_router_id,
+ bgp_path_selection_cluster_length,
+ bgp_path_selection_stale,
+ bgp_path_selection_local_configured,
+ bgp_path_selection_neighbor_ip,
+ bgp_path_selection_default,
+};
+
+struct bgp_node {
+ /*
+ * CAUTION
+ *
+ * These fields must be the very first fields in this structure.
+ *
+ * @see bgp_node_to_rnode
+ * @see bgp_node_from_rnode
+ */
+ ROUTE_NODE_FIELDS
+
+ struct bgp_adj_out_rb adj_out;
+
+ struct bgp_adj_in *adj_in;
+
+ struct bgp_dest *pdest;
+
+ STAILQ_ENTRY(bgp_dest) pq;
+
+ uint64_t version;
+
+ mpls_label_t local_label;
+
+ uint16_t flags;
+#define BGP_NODE_PROCESS_SCHEDULED (1 << 0)
+#define BGP_NODE_USER_CLEAR (1 << 1)
+#define BGP_NODE_LABEL_CHANGED (1 << 2)
+#define BGP_NODE_REGISTERED_FOR_LABEL (1 << 3)
+#define BGP_NODE_SELECT_DEFER (1 << 4)
+#define BGP_NODE_FIB_INSTALL_PENDING (1 << 5)
+#define BGP_NODE_FIB_INSTALLED (1 << 6)
+#define BGP_NODE_LABEL_REQUESTED (1 << 7)
+#define BGP_NODE_SOFT_RECONFIG (1 << 8)
+
+ struct bgp_addpath_node_data tx_addpath;
+
+ enum bgp_path_selection_reason reason;
+};
+
+extern void bgp_delete_listnode(struct bgp_dest *dest);
+/*
+ * bgp_table_iter_t
+ *
+ * Structure that holds state for iterating over a bgp table.
+ */
+typedef struct bgp_table_iter_t_ {
+ struct bgp_table *table;
+ route_table_iter_t rt_iter;
+} bgp_table_iter_t;
+
+extern struct bgp_table *bgp_table_init(struct bgp *bgp, afi_t, safi_t);
+extern void bgp_table_lock(struct bgp_table *);
+extern void bgp_table_unlock(struct bgp_table *);
+extern void bgp_table_finish(struct bgp_table **);
+extern void bgp_dest_unlock_node(struct bgp_dest *dest);
+extern struct bgp_dest *bgp_dest_lock_node(struct bgp_dest *dest);
+extern const char *bgp_dest_get_prefix_str(struct bgp_dest *dest);
+
+
+/*
+ * bgp_dest_from_rnode
+ *
+ * Returns the bgp_dest structure corresponding to a route_node.
+ */
+static inline struct bgp_dest *bgp_dest_from_rnode(struct route_node *rnode)
+{
+ return (struct bgp_dest *)rnode;
+}
+
+/*
+ * bgp_dest_to_rnode
+ *
+ * Returns the route_node structure corresponding to a bgp_dest.
+ */
+static inline struct route_node *bgp_dest_to_rnode(const struct bgp_dest *dest)
+{
+ return (struct route_node *)dest;
+}
+
+/*
+ * bgp_dest_table
+ *
+ * Returns the bgp_table that the given dest is in.
+ */
+static inline struct bgp_table *bgp_dest_table(struct bgp_dest *dest)
+{
+ return route_table_get_info(bgp_dest_to_rnode(dest)->table);
+}
+
+/*
+ * bgp_dest_parent_nolock
+ *
+ * Gets the parent dest of the given node without locking it.
+ */
+static inline struct bgp_dest *bgp_dest_parent_nolock(struct bgp_dest *dest)
+{
+ struct route_node *rn = bgp_dest_to_rnode(dest)->parent;
+
+ return bgp_dest_from_rnode(rn);
+}
+
+/*
+ * bgp_table_top_nolock
+ *
+ * Gets the top dest in the table without locking it.
+ *
+ * @see bgp_table_top
+ */
+static inline struct bgp_dest *
+bgp_table_top_nolock(const struct bgp_table *const table)
+{
+ return bgp_dest_from_rnode(table->route_table->top);
+}
+
+/*
+ * bgp_table_top
+ */
+static inline struct bgp_dest *
+bgp_table_top(const struct bgp_table *const table)
+{
+ return bgp_dest_from_rnode(route_top(table->route_table));
+}
+
+/*
+ * bgp_route_next
+ */
+static inline struct bgp_dest *bgp_route_next(struct bgp_dest *dest)
+{
+ return bgp_dest_from_rnode(route_next(bgp_dest_to_rnode(dest)));
+}
+
+/*
+ * bgp_route_next_until
+ */
+static inline struct bgp_dest *bgp_route_next_until(struct bgp_dest *dest,
+ struct bgp_dest *limit)
+{
+ struct route_node *rnode;
+
+ rnode = route_next_until(bgp_dest_to_rnode(dest),
+ bgp_dest_to_rnode(limit));
+
+ return bgp_dest_from_rnode(rnode);
+}
+
+/*
+ * bgp_node_get
+ */
+static inline struct bgp_dest *bgp_node_get(struct bgp_table *const table,
+ const struct prefix *p)
+{
+ return bgp_dest_from_rnode(route_node_get(table->route_table, p));
+}
+
+/*
+ * bgp_node_lookup
+ */
+static inline struct bgp_dest *
+bgp_node_lookup(const struct bgp_table *const table, const struct prefix *p)
+{
+ struct route_node *rn = route_node_lookup(table->route_table, p);
+
+ return bgp_dest_from_rnode(rn);
+}
+
+/*
+ * bgp_node_match
+ */
+static inline struct bgp_dest *bgp_node_match(const struct bgp_table *table,
+ const struct prefix *p)
+{
+ struct route_node *rn = route_node_match(table->route_table, p);
+
+ return bgp_dest_from_rnode(rn);
+}
+
+static inline unsigned long bgp_table_count(const struct bgp_table *const table)
+{
+ return route_table_count(table->route_table);
+}
+
+/*
+ * bgp_table_get_next
+ */
+static inline struct bgp_dest *bgp_table_get_next(const struct bgp_table *table,
+ const struct prefix *p)
+{
+ return bgp_dest_from_rnode(route_table_get_next(table->route_table, p));
+}
+
+/* This would benefit from a real atomic operation...
+ * until then. */
+static inline uint64_t bgp_table_next_version(struct bgp_table *table)
+{
+ return ++table->version;
+}
+
+static inline uint64_t bgp_table_version(struct bgp_table *table)
+{
+ return table->version;
+}
+
+/* Find the subtree of the prefix p
+ *
+ * This will return the first node that belongs the the subtree of p. Including
+ * p itself, if it is in the tree.
+ *
+ * If the subtree is not present in the table, NULL is returned.
+ */
+struct bgp_dest *bgp_table_subtree_lookup(const struct bgp_table *table,
+ const struct prefix *p);
+
+static inline struct bgp_aggregate *
+bgp_dest_get_bgp_aggregate_info(struct bgp_dest *dest)
+{
+ return dest ? dest->info : NULL;
+}
+
+static inline void
+bgp_dest_set_bgp_aggregate_info(struct bgp_dest *dest,
+ struct bgp_aggregate *aggregate)
+{
+ dest->info = aggregate;
+}
+
+static inline struct bgp_distance *
+bgp_dest_get_bgp_distance_info(struct bgp_dest *dest)
+{
+ return dest ? dest->info : NULL;
+}
+
+static inline void bgp_dest_set_bgp_distance_info(struct bgp_dest *dest,
+ struct bgp_distance *distance)
+{
+ dest->info = distance;
+}
+
+static inline struct bgp_static *
+bgp_dest_get_bgp_static_info(struct bgp_dest *dest)
+{
+ return dest ? dest->info : NULL;
+}
+
+static inline void bgp_dest_set_bgp_static_info(struct bgp_dest *dest,
+ struct bgp_static *bgp_static)
+{
+ dest->info = bgp_static;
+}
+
+static inline struct bgp_connected_ref *
+bgp_dest_get_bgp_connected_ref_info(struct bgp_dest *dest)
+{
+ return dest ? dest->info : NULL;
+}
+
+static inline void
+bgp_dest_set_bgp_connected_ref_info(struct bgp_dest *dest,
+ struct bgp_connected_ref *bc)
+{
+ dest->info = bc;
+}
+
+static inline struct bgp_nexthop_cache *
+bgp_dest_get_bgp_nexthop_info(struct bgp_dest *dest)
+{
+ return dest ? dest->info : NULL;
+}
+
+static inline void bgp_dest_set_bgp_nexthop_info(struct bgp_dest *dest,
+ struct bgp_nexthop_cache *bnc)
+{
+ dest->info = bnc;
+}
+
+static inline struct bgp_path_info *
+bgp_dest_get_bgp_path_info(struct bgp_dest *dest)
+{
+ return dest ? dest->info : NULL;
+}
+
+static inline void bgp_dest_set_bgp_path_info(struct bgp_dest *dest,
+ struct bgp_path_info *bi)
+{
+ dest->info = bi;
+}
+
+static inline struct bgp_table *
+bgp_dest_get_bgp_table_info(struct bgp_dest *dest)
+{
+ return dest->info;
+}
+
+static inline void bgp_dest_set_bgp_table_info(struct bgp_dest *dest,
+ struct bgp_table *table)
+{
+ dest->info = table;
+}
+
+static inline bool bgp_dest_has_bgp_path_info_data(struct bgp_dest *dest)
+{
+ return !!dest->info;
+}
+
+static inline const struct prefix *bgp_dest_get_prefix(const struct bgp_dest *dest)
+{
+ return &dest->p;
+}
+
+static inline unsigned int bgp_dest_get_lock_count(const struct bgp_dest *dest)
+{
+ return dest->lock;
+}
+
+#ifdef _FRR_ATTRIBUTE_PRINTFRR
+#pragma FRR printfrr_ext "%pRN" (struct bgp_node *)
+#pragma FRR printfrr_ext "%pBD" (struct bgp_dest *)
+#endif
+
+#endif /* _QUAGGA_BGP_TABLE_H */
diff --git a/bgpd/bgp_trace.c b/bgpd/bgp_trace.c
new file mode 100644
index 0000000..02afbeb
--- /dev/null
+++ b/bgpd/bgp_trace.c
@@ -0,0 +1,6 @@
+#define TRACEPOINT_CREATE_PROBES
+#define TRACEPOINT_DEFINE
+
+#include <zebra.h>
+
+#include "bgp_trace.h"
diff --git a/bgpd/bgp_trace.h b/bgpd/bgp_trace.h
new file mode 100644
index 0000000..14149b5
--- /dev/null
+++ b/bgpd/bgp_trace.h
@@ -0,0 +1,484 @@
+/* Tracing for BGP
+ *
+ * Copyright (C) 2020 NVIDIA Corporation
+ * Quentin Young
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License as published by the Free
+ * Software Foundation; either version 2 of the License, or (at your option)
+ * any later version.
+ *
+ * This program is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
+ * more details.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program; see the file COPYING; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
+ */
+
+#if !defined(_BGP_TRACE_H) || defined(TRACEPOINT_HEADER_MULTI_READ)
+#define _BGP_TRACE_H
+
+#include "lib/trace.h"
+
+#ifdef HAVE_LTTNG
+
+#undef TRACEPOINT_PROVIDER
+#define TRACEPOINT_PROVIDER frr_bgp
+
+#undef TRACEPOINT_INCLUDE
+#define TRACEPOINT_INCLUDE "bgpd/bgp_trace.h"
+
+#include <lttng/tracepoint.h>
+
+#include "bgpd/bgpd.h"
+#include "bgpd/bgp_attr.h"
+#include "lib/stream.h"
+#include "bgpd/bgp_evpn_private.h"
+#include "bgpd/bgp_evpn_mh.h"
+
+
+/* clang-format off */
+
+TRACEPOINT_EVENT_CLASS(
+ frr_bgp,
+ packet_process,
+ TP_ARGS(struct peer *, peer, bgp_size_t, size),
+ TP_FIELDS(
+ ctf_string(peer, PEER_HOSTNAME(peer))
+ )
+)
+
+#define PKT_PROCESS_TRACEPOINT_INSTANCE(name) \
+ TRACEPOINT_EVENT_INSTANCE( \
+ frr_bgp, packet_process, name, \
+ TP_ARGS(struct peer *, peer, bgp_size_t, size)) \
+ TRACEPOINT_LOGLEVEL(frr_bgp, name, TRACE_INFO)
+
+PKT_PROCESS_TRACEPOINT_INSTANCE(open_process)
+PKT_PROCESS_TRACEPOINT_INSTANCE(keepalive_process)
+PKT_PROCESS_TRACEPOINT_INSTANCE(update_process)
+PKT_PROCESS_TRACEPOINT_INSTANCE(notification_process)
+PKT_PROCESS_TRACEPOINT_INSTANCE(capability_process)
+PKT_PROCESS_TRACEPOINT_INSTANCE(refresh_process)
+
+TRACEPOINT_EVENT(
+ frr_bgp,
+ packet_read,
+ TP_ARGS(struct peer *, peer, struct stream *, pkt),
+ TP_FIELDS(
+ ctf_string(peer, PEER_HOSTNAME(peer))
+ ctf_sequence_hex(uint8_t, packet, pkt->data, size_t,
+ STREAM_READABLE(pkt))
+ )
+)
+
+TRACEPOINT_LOGLEVEL(frr_bgp, packet_read, TRACE_INFO)
+
+TRACEPOINT_EVENT(
+ frr_bgp,
+ process_update,
+ TP_ARGS(struct peer *, peer, char *, pfx, uint32_t, addpath_id, afi_t,
+ afi, safi_t, safi, struct attr *, attr),
+ TP_FIELDS(
+ ctf_string(peer, PEER_HOSTNAME(peer))
+ ctf_string(prefix, pfx)
+ ctf_integer(uint32_t, addpath_id, addpath_id)
+ ctf_integer(afi_t, afi, afi)
+ ctf_integer(safi_t, safi, safi)
+ ctf_integer_hex(intptr_t, attribute_ptr, attr)
+ )
+)
+
+TRACEPOINT_LOGLEVEL(frr_bgp, process_update, TRACE_INFO)
+
+TRACEPOINT_EVENT(
+ frr_bgp,
+ input_filter,
+ TP_ARGS(struct peer *, peer, char *, pfx, afi_t, afi, safi_t, safi,
+ const char *, result),
+ TP_FIELDS(
+ ctf_string(peer, PEER_HOSTNAME(peer))
+ ctf_string(prefix, pfx)
+ ctf_integer(afi_t, afi, afi)
+ ctf_integer(safi_t, safi, safi)
+ ctf_string(action, result)
+ )
+)
+
+TRACEPOINT_LOGLEVEL(frr_bgp, input_filter, TRACE_INFO)
+
+TRACEPOINT_EVENT(
+ frr_bgp,
+ output_filter,
+ TP_ARGS(struct peer *, peer, char *, pfx, afi_t, afi, safi_t, safi,
+ const char *, result),
+ TP_FIELDS(
+ ctf_string(peer, PEER_HOSTNAME(peer))
+ ctf_string(prefix, pfx)
+ ctf_integer(afi_t, afi, afi)
+ ctf_integer(safi_t, safi, safi)
+ ctf_string(action, result)
+ )
+)
+
+TRACEPOINT_LOGLEVEL(frr_bgp, output_filter, TRACE_INFO)
+
+/* BMP tracepoints */
+
+/* BMP mirrors a packet to all mirror-enabled targets */
+TRACEPOINT_EVENT(
+ frr_bgp,
+ bmp_mirror_packet,
+ TP_ARGS(struct peer *, peer, uint8_t, type, struct stream *, pkt),
+ TP_FIELDS(
+ ctf_string(peer, PEER_HOSTNAME(peer))
+ ctf_integer(uint8_t, type, type)
+ ctf_sequence_hex(uint8_t, packet, pkt->data, size_t,
+ STREAM_READABLE(pkt))
+ )
+)
+
+TRACEPOINT_LOGLEVEL(frr_bgp, bmp_mirror_packet, TRACE_INFO)
+
+
+/* BMP sends an EOR */
+TRACEPOINT_EVENT(
+ frr_bgp,
+ bmp_eor,
+ TP_ARGS(afi_t, afi, safi_t, safi, uint8_t, flags),
+ TP_FIELDS(
+ ctf_integer(afi_t, afi, afi)
+ ctf_integer(safi_t, safi, safi)
+ ctf_integer(uint8_t, flags, flags)
+ )
+)
+
+TRACEPOINT_LOGLEVEL(frr_bgp, bmp_eor, TRACE_INFO)
+
+
+/* BMP updates its copy of the last OPEN a peer sent */
+TRACEPOINT_EVENT(
+ frr_bgp,
+ bmp_update_saved_open,
+ TP_ARGS(struct peer *, peer, struct stream *, pkt),
+ TP_FIELDS(
+ ctf_string(peer, PEER_HOSTNAME(peer))
+ ctf_sequence_hex(uint8_t, packet, pkt->data, size_t,
+ STREAM_READABLE(pkt))
+ )
+)
+
+TRACEPOINT_LOGLEVEL(frr_bgp, bmp_update_saved_open, TRACE_DEBUG)
+
+
+/* BMP is notified of a peer status change internally */
+TRACEPOINT_EVENT(
+ frr_bgp,
+ bmp_peer_status_changed,
+ TP_ARGS(struct peer *, peer),
+ TP_FIELDS(
+ ctf_string(peer, PEER_HOSTNAME(peer))
+ )
+)
+
+TRACEPOINT_LOGLEVEL(frr_bgp, bmp_peer_status_changed, TRACE_DEBUG)
+
+
+/*
+ * BMP is notified that a peer has transitioned in the opposite direction of
+ * Established internally
+ */
+TRACEPOINT_EVENT(
+ frr_bgp,
+ bmp_peer_backward_transition,
+ TP_ARGS(struct peer *, peer),
+ TP_FIELDS(
+ ctf_string(peer, PEER_HOSTNAME(peer))
+ )
+)
+
+TRACEPOINT_LOGLEVEL(frr_bgp, bmp_peer_backward, TRACE_DEBUG)
+
+
+/*
+ * BMP is hooked for a route process
+ */
+TRACEPOINT_EVENT(
+ frr_bgp,
+ bmp_process,
+ TP_ARGS(struct peer *, peer, char *, pfx, afi_t,
+ afi, safi_t, safi, bool, withdraw),
+ TP_FIELDS(
+ ctf_string(peer, PEER_HOSTNAME(peer))
+ ctf_string(prefix, pfx)
+ ctf_integer(afi_t, afi, afi)
+ ctf_integer(safi_t, safi, safi)
+ ctf_integer(bool, withdraw, withdraw)
+ )
+)
+
+TRACEPOINT_LOGLEVEL(frr_bgp, bmp_process, TRACE_DEBUG)
+
+/*
+ * bgp_dest_lock/bgp_dest_unlock
+ */
+TRACEPOINT_EVENT(
+ frr_bgp,
+ bgp_dest_lock,
+ TP_ARGS(struct bgp_dest *, dest),
+ TP_FIELDS(
+ ctf_string(prefix, bgp_dest_get_prefix_str(dest))
+ ctf_integer(unsigned int, count, bgp_dest_get_lock_count(dest))
+ )
+)
+TRACEPOINT_LOGLEVEL(frr_bgp, bgp_dest_lock, TRACE_INFO)
+
+TRACEPOINT_EVENT(
+ frr_bgp,
+ bgp_dest_unlock,
+ TP_ARGS(struct bgp_dest *, dest),
+ TP_FIELDS(
+ ctf_string(prefix, bgp_dest_get_prefix_str(dest))
+ ctf_integer(unsigned int, count, bgp_dest_get_lock_count(dest))
+ )
+)
+TRACEPOINT_LOGLEVEL(frr_bgp, bgp_dest_unlock, TRACE_INFO)
+
+TRACEPOINT_EVENT(
+ frr_bgp,
+ evpn_mac_ip_zsend,
+ TP_ARGS(int, add, struct bgpevpn *, vpn,
+ const struct prefix_evpn *, pfx,
+ struct in_addr, vtep, esi_t *, esi),
+ TP_FIELDS(
+ ctf_string(action, add ? "add" : "del")
+ ctf_integer(vni_t, vni, vpn->vni)
+ ctf_array(unsigned char, mac, &pfx->prefix.macip_addr.mac,
+ sizeof(struct ethaddr))
+ ctf_array(unsigned char, ip, &pfx->prefix.macip_addr.ip,
+ sizeof(struct ipaddr))
+ ctf_integer_network_hex(unsigned int, vtep, vtep.s_addr)
+ ctf_array(unsigned char, esi, esi, sizeof(esi_t))
+ )
+)
+TRACEPOINT_LOGLEVEL(frr_bgp, evpn_mac_ip_zsend, TRACE_INFO)
+
+TRACEPOINT_EVENT(
+ frr_bgp,
+ evpn_bum_vtep_zsend,
+ TP_ARGS(int, add, struct bgpevpn *, vpn,
+ const struct prefix_evpn *, pfx),
+ TP_FIELDS(
+ ctf_string(action, add ? "add" : "del")
+ ctf_integer(vni_t, vni, vpn->vni)
+ ctf_integer_network_hex(unsigned int, vtep,
+ pfx->prefix.imet_addr.ip.ipaddr_v4.s_addr)
+ )
+)
+TRACEPOINT_LOGLEVEL(frr_bgp, evpn_bum_vtep_zsend, TRACE_INFO)
+
+TRACEPOINT_EVENT(
+ frr_bgp,
+ evpn_mh_vtep_zsend,
+ TP_ARGS(bool, add, struct bgp_evpn_es *, es,
+ struct bgp_evpn_es_vtep *, es_vtep),
+ TP_FIELDS(
+ ctf_string(action, add ? "add" : "del")
+ ctf_string(esi, es->esi_str)
+ ctf_string(vtep, es_vtep->vtep_str)
+ )
+)
+TRACEPOINT_LOGLEVEL(frr_bgp, evpn_mh_vtep_zsend, TRACE_INFO)
+
+TRACEPOINT_EVENT(
+ frr_bgp,
+ evpn_mh_nhg_zsend,
+ TP_ARGS(bool, add, bool, type_v4, uint32_t, nhg_id,
+ struct bgp_evpn_es_vrf *, es_vrf),
+ TP_FIELDS(
+ ctf_string(action, add ? "add" : "del")
+ ctf_string(type, type_v4 ? "v4" : "v6")
+ ctf_integer(unsigned int, nhg, nhg_id)
+ ctf_string(esi, es_vrf->es->esi_str)
+ ctf_integer(int, vrf, es_vrf->bgp_vrf->vrf_id)
+ )
+)
+TRACEPOINT_LOGLEVEL(frr_bgp, evpn_mh_nhg_zsend, TRACE_INFO)
+
+TRACEPOINT_EVENT(
+ frr_bgp,
+ evpn_mh_nh_zsend,
+ TP_ARGS(uint32_t, nhg_id, struct bgp_evpn_es_vtep *, vtep,
+ struct bgp_evpn_es_vrf *, es_vrf),
+ TP_FIELDS(
+ ctf_integer(unsigned int, nhg, nhg_id)
+ ctf_string(vtep, vtep->vtep_str)
+ ctf_integer(int, svi, es_vrf->bgp_vrf->l3vni_svi_ifindex)
+ )
+)
+TRACEPOINT_LOGLEVEL(frr_bgp, evpn_mh_nh_zsend, TRACE_INFO)
+
+TRACEPOINT_EVENT(
+ frr_bgp,
+ evpn_mh_nh_rmac_zsend,
+ TP_ARGS(bool, add, struct bgp_evpn_nh *, nh),
+ TP_FIELDS(
+ ctf_string(action, add ? "add" : "del")
+ ctf_integer(int, vrf, nh->bgp_vrf->vrf_id)
+ ctf_string(nh, nh->nh_str)
+ ctf_array(unsigned char, rmac, &nh->rmac,
+ sizeof(struct ethaddr))
+ )
+)
+TRACEPOINT_LOGLEVEL(frr_bgp, evpn_nh_rmac_zsend, TRACE_INFO)
+
+TRACEPOINT_EVENT(
+ frr_bgp,
+ evpn_mh_local_es_add_zrecv,
+ TP_ARGS(esi_t *, esi, struct in_addr, vtep,
+ uint8_t, active, uint8_t, bypass, uint16_t, df_pref),
+ TP_FIELDS(
+ ctf_array(unsigned char, esi, esi, sizeof(esi_t))
+ ctf_integer_network_hex(unsigned int, vtep, vtep.s_addr)
+ ctf_integer(uint8_t, active, active)
+ ctf_integer(uint8_t, bypass, bypass)
+ ctf_integer(uint16_t, df_pref, df_pref)
+ )
+)
+TRACEPOINT_LOGLEVEL(frr_bgp, evpn_mh_local_es_add_zrecv, TRACE_INFO)
+
+TRACEPOINT_EVENT(
+ frr_bgp,
+ evpn_mh_local_es_del_zrecv,
+ TP_ARGS(esi_t *, esi),
+ TP_FIELDS(
+ ctf_array(unsigned char, esi, esi, sizeof(esi_t))
+ )
+)
+TRACEPOINT_LOGLEVEL(frr_bgp, evpn_mh_local_es_del_zrecv, TRACE_INFO)
+
+TRACEPOINT_EVENT(
+ frr_bgp,
+ evpn_mh_local_es_evi_add_zrecv,
+ TP_ARGS(esi_t *, esi, vni_t, vni),
+ TP_FIELDS(
+ ctf_array(unsigned char, esi, esi, sizeof(esi_t))
+ ctf_integer(vni_t, vni, vni)
+ )
+)
+TRACEPOINT_LOGLEVEL(frr_bgp, evpn_mh_local_es_evi_add_zrecv, TRACE_INFO)
+
+TRACEPOINT_EVENT(
+ frr_bgp,
+ evpn_mh_local_es_evi_del_zrecv,
+ TP_ARGS(esi_t *, esi, vni_t, vni),
+ TP_FIELDS(
+ ctf_array(unsigned char, esi, esi, sizeof(esi_t))
+ ctf_integer(vni_t, vni, vni)
+ )
+)
+TRACEPOINT_LOGLEVEL(frr_bgp, evpn_mh_local_es_evi_del_zrecv, TRACE_INFO)
+
+TRACEPOINT_EVENT(
+ frr_bgp,
+ evpn_local_vni_add_zrecv,
+ TP_ARGS(vni_t, vni, struct in_addr, vtep, vrf_id_t, vrf,
+ struct in_addr, mc_grp),
+ TP_FIELDS(
+ ctf_integer(vni_t, vni, vni)
+ ctf_integer_network_hex(unsigned int, vtep, vtep.s_addr)
+ ctf_integer_network_hex(unsigned int, mc_grp,
+ mc_grp.s_addr)
+ ctf_integer(int, vrf, vrf)
+ )
+)
+TRACEPOINT_LOGLEVEL(frr_bgp, evpn_local_vni_add_zrecv, TRACE_INFO)
+
+TRACEPOINT_EVENT(
+ frr_bgp,
+ evpn_local_vni_del_zrecv,
+ TP_ARGS(vni_t, vni),
+ TP_FIELDS(
+ ctf_integer(vni_t, vni, vni)
+ )
+)
+TRACEPOINT_LOGLEVEL(frr_bgp, evpn_local_vni_del_zrecv, TRACE_INFO)
+
+TRACEPOINT_EVENT(
+ frr_bgp,
+ evpn_local_macip_add_zrecv,
+ TP_ARGS(vni_t, vni, struct ethaddr *, mac,
+ struct ipaddr *, ip, uint32_t, flags,
+ uint32_t, seqnum, esi_t *, esi),
+ TP_FIELDS(
+ ctf_integer(vni_t, vni, vni)
+ ctf_array(unsigned char, mac, mac,
+ sizeof(struct ethaddr))
+ ctf_array(unsigned char, ip, ip,
+ sizeof(struct ipaddr))
+ ctf_integer(uint32_t, flags, flags)
+ ctf_integer(uint32_t, seq, seqnum)
+ ctf_array(unsigned char, esi, esi, sizeof(esi_t))
+ )
+)
+TRACEPOINT_LOGLEVEL(frr_bgp, evpn_local_macip_add_zrecv, TRACE_INFO)
+
+TRACEPOINT_EVENT(
+ frr_bgp,
+ evpn_local_macip_del_zrecv,
+ TP_ARGS(vni_t, vni, struct ethaddr *, mac, struct ipaddr *, ip,
+ int, state),
+ TP_FIELDS(
+ ctf_integer(vni_t, vni, vni)
+ ctf_array(unsigned char, mac, mac,
+ sizeof(struct ethaddr))
+ ctf_array(unsigned char, ip, ip,
+ sizeof(struct ipaddr))
+ ctf_integer(int, state, state)
+ )
+)
+TRACEPOINT_LOGLEVEL(frr_bgp, evpn_local_macip_del_zrecv, TRACE_INFO)
+
+TRACEPOINT_EVENT(
+ frr_bgp,
+ evpn_local_l3vni_add_zrecv,
+ TP_ARGS(vni_t, vni, vrf_id_t, vrf,
+ struct ethaddr *, svi_rmac,
+ struct ethaddr *, vrr_rmac, int, filter,
+ struct in_addr, vtep, int, svi_ifindex,
+ bool, anycast_mac),
+ TP_FIELDS(
+ ctf_integer(vni_t, vni, vni)
+ ctf_integer(int, vrf, vrf)
+ ctf_array(unsigned char, svi_rmac, svi_rmac,
+ sizeof(struct ethaddr))
+ ctf_array(unsigned char, vrr_rmac, vrr_rmac,
+ sizeof(struct ethaddr))
+ ctf_integer_network_hex(unsigned int, vtep, vtep.s_addr)
+ ctf_integer(int, filter, filter)
+ ctf_integer(int, svi_ifindex, svi_ifindex)
+ ctf_string(anycast_mac, anycast_mac ? "y" : "n")
+ )
+)
+TRACEPOINT_LOGLEVEL(frr_bgp, evpn_local_l3vni_add_zrecv, TRACE_INFO)
+
+TRACEPOINT_EVENT(
+ frr_bgp,
+ evpn_local_l3vni_del_zrecv,
+ TP_ARGS(vni_t, vni, vrf_id_t, vrf),
+ TP_FIELDS(
+ ctf_integer(vni_t, vni, vni)
+ ctf_integer(int, vrf, vrf)
+ )
+)
+TRACEPOINT_LOGLEVEL(frr_bgp, evpn_local_l3vni_del_zrecv, TRACE_INFO)
+/* clang-format on */
+
+#include <lttng/tracepoint-event.h>
+
+#endif /* HAVE_LTTNG */
+
+#endif /* _BGP_TRACE_H */
diff --git a/bgpd/bgp_updgrp.c b/bgpd/bgp_updgrp.c
new file mode 100644
index 0000000..0219535
--- /dev/null
+++ b/bgpd/bgp_updgrp.c
@@ -0,0 +1,2052 @@
+/**
+ * bgp_updgrp.c: BGP update group structures
+ *
+ * @copyright Copyright (C) 2014 Cumulus Networks, Inc.
+ *
+ * @author Avneesh Sachdev <avneesh@sproute.net>
+ * @author Rajesh Varadarajan <rajesh@sproute.net>
+ * @author Pradosh Mohapatra <pradosh@sproute.net>
+ *
+ * This file is part of GNU Zebra.
+ *
+ * GNU Zebra is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License as published by the
+ * Free Software Foundation; either version 2, or (at your option) any
+ * later version.
+ *
+ * GNU Zebra is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program; see the file COPYING; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
+ */
+
+#include <zebra.h>
+
+#include "prefix.h"
+#include "thread.h"
+#include "buffer.h"
+#include "stream.h"
+#include "command.h"
+#include "sockunion.h"
+#include "network.h"
+#include "memory.h"
+#include "filter.h"
+#include "routemap.h"
+#include "log.h"
+#include "plist.h"
+#include "linklist.h"
+#include "workqueue.h"
+#include "hash.h"
+#include "jhash.h"
+#include "queue.h"
+
+#include "bgpd/bgpd.h"
+#include "bgpd/bgp_table.h"
+#include "bgpd/bgp_debug.h"
+#include "bgpd/bgp_errors.h"
+#include "bgpd/bgp_fsm.h"
+#include "bgpd/bgp_addpath.h"
+#include "bgpd/bgp_advertise.h"
+#include "bgpd/bgp_packet.h"
+#include "bgpd/bgp_updgrp.h"
+#include "bgpd/bgp_route.h"
+#include "bgpd/bgp_filter.h"
+#include "bgpd/bgp_io.h"
+
+/********************
+ * PRIVATE FUNCTIONS
+ ********************/
+
+/**
+ * assign a unique ID to update group and subgroup. Mostly for display/
+ * debugging purposes. It's a 64-bit space - used leisurely without a
+ * worry about its wrapping and about filling gaps. While at it, timestamp
+ * the creation.
+ */
+static void update_group_checkin(struct update_group *updgrp)
+{
+ updgrp->id = ++bm->updgrp_idspace;
+ updgrp->uptime = monotime(NULL);
+}
+
+static void update_subgroup_checkin(struct update_subgroup *subgrp,
+ struct update_group *updgrp)
+{
+ subgrp->id = ++bm->subgrp_idspace;
+ subgrp->uptime = monotime(NULL);
+}
+
+static void sync_init(struct update_subgroup *subgrp,
+ struct update_group *updgrp)
+{
+ struct peer *peer = UPDGRP_PEER(updgrp);
+
+ subgrp->sync =
+ XCALLOC(MTYPE_BGP_SYNCHRONISE, sizeof(struct bgp_synchronize));
+ bgp_adv_fifo_init(&subgrp->sync->update);
+ bgp_adv_fifo_init(&subgrp->sync->withdraw);
+ bgp_adv_fifo_init(&subgrp->sync->withdraw_low);
+ subgrp->hash =
+ hash_create(bgp_advertise_attr_hash_key,
+ bgp_advertise_attr_hash_cmp, "BGP SubGroup Hash");
+
+ /* We use a larger buffer for subgrp->work in the event that:
+ * - We RX a BGP_UPDATE where the attributes alone are just
+ * under 4096 or 65535 (if Extended Message capability negotiated).
+ * - The user configures an outbound route-map that does many as-path
+ * prepends or adds many communities. At most they can have
+ * CMD_ARGC_MAX
+ * args in a route-map so there is a finite limit on how large they
+ * can
+ * make the attributes.
+ *
+ * Having a buffer with BGP_MAX_PACKET_SIZE_OVERFLOW allows us to avoid
+ * bounds
+ * checking for every single attribute as we construct an UPDATE.
+ */
+ subgrp->work = stream_new(peer->max_packet_size
+ + BGP_MAX_PACKET_SIZE_OVERFLOW);
+ subgrp->scratch = stream_new(peer->max_packet_size);
+}
+
+static void sync_delete(struct update_subgroup *subgrp)
+{
+ XFREE(MTYPE_BGP_SYNCHRONISE, subgrp->sync);
+ if (subgrp->hash) {
+ hash_clean(subgrp->hash,
+ (void (*)(void *))bgp_advertise_attr_free);
+ hash_free(subgrp->hash);
+ }
+ subgrp->hash = NULL;
+ if (subgrp->work)
+ stream_free(subgrp->work);
+ subgrp->work = NULL;
+ if (subgrp->scratch)
+ stream_free(subgrp->scratch);
+ subgrp->scratch = NULL;
+}
+
+/**
+ * conf_copy
+ *
+ * copy only those fields that are relevant to update group match
+ */
+static void conf_copy(struct peer *dst, struct peer *src, afi_t afi,
+ safi_t safi)
+{
+ struct bgp_filter *srcfilter;
+ struct bgp_filter *dstfilter;
+
+ srcfilter = &src->filter[afi][safi];
+ dstfilter = &dst->filter[afi][safi];
+
+ dst->bgp = src->bgp;
+ dst->sort = src->sort;
+ dst->as = src->as;
+ dst->v_routeadv = src->v_routeadv;
+ dst->flags = src->flags;
+ dst->af_flags[afi][safi] = src->af_flags[afi][safi];
+ dst->pmax_out[afi][safi] = src->pmax_out[afi][safi];
+ dst->max_packet_size = src->max_packet_size;
+ XFREE(MTYPE_BGP_PEER_HOST, dst->host);
+
+ dst->host = XSTRDUP(MTYPE_BGP_PEER_HOST, src->host);
+ dst->cap = src->cap;
+ dst->af_cap[afi][safi] = src->af_cap[afi][safi];
+ dst->afc_nego[afi][safi] = src->afc_nego[afi][safi];
+ dst->orf_plist[afi][safi] = src->orf_plist[afi][safi];
+ dst->addpath_type[afi][safi] = src->addpath_type[afi][safi];
+ dst->local_as = src->local_as;
+ dst->change_local_as = src->change_local_as;
+ dst->shared_network = src->shared_network;
+ dst->local_role = src->local_role;
+
+ if (src->soo[afi][safi]) {
+ ecommunity_free(&dst->soo[afi][safi]);
+ dst->soo[afi][safi] = ecommunity_dup(src->soo[afi][safi]);
+ }
+
+ memcpy(&(dst->nexthop), &(src->nexthop), sizeof(struct bgp_nexthop));
+
+ dst->group = src->group;
+
+ if (src->default_rmap[afi][safi].name) {
+ dst->default_rmap[afi][safi].name =
+ XSTRDUP(MTYPE_ROUTE_MAP_NAME,
+ src->default_rmap[afi][safi].name);
+ dst->default_rmap[afi][safi].map =
+ src->default_rmap[afi][safi].map;
+ }
+
+ if (DISTRIBUTE_OUT_NAME(srcfilter)) {
+ DISTRIBUTE_OUT_NAME(dstfilter) = XSTRDUP(
+ MTYPE_BGP_FILTER_NAME, DISTRIBUTE_OUT_NAME(srcfilter));
+ DISTRIBUTE_OUT(dstfilter) = DISTRIBUTE_OUT(srcfilter);
+ }
+
+ if (PREFIX_LIST_OUT_NAME(srcfilter)) {
+ PREFIX_LIST_OUT_NAME(dstfilter) = XSTRDUP(
+ MTYPE_BGP_FILTER_NAME, PREFIX_LIST_OUT_NAME(srcfilter));
+ PREFIX_LIST_OUT(dstfilter) = PREFIX_LIST_OUT(srcfilter);
+ }
+
+ if (FILTER_LIST_OUT_NAME(srcfilter)) {
+ FILTER_LIST_OUT_NAME(dstfilter) = XSTRDUP(
+ MTYPE_BGP_FILTER_NAME, FILTER_LIST_OUT_NAME(srcfilter));
+ FILTER_LIST_OUT(dstfilter) = FILTER_LIST_OUT(srcfilter);
+ }
+
+ if (ROUTE_MAP_OUT_NAME(srcfilter)) {
+ ROUTE_MAP_OUT_NAME(dstfilter) = XSTRDUP(
+ MTYPE_BGP_FILTER_NAME, ROUTE_MAP_OUT_NAME(srcfilter));
+ ROUTE_MAP_OUT(dstfilter) = ROUTE_MAP_OUT(srcfilter);
+ }
+
+ if (UNSUPPRESS_MAP_NAME(srcfilter)) {
+ UNSUPPRESS_MAP_NAME(dstfilter) = XSTRDUP(
+ MTYPE_BGP_FILTER_NAME, UNSUPPRESS_MAP_NAME(srcfilter));
+ UNSUPPRESS_MAP(dstfilter) = UNSUPPRESS_MAP(srcfilter);
+ }
+
+ if (ADVERTISE_MAP_NAME(srcfilter)) {
+ ADVERTISE_MAP_NAME(dstfilter) = XSTRDUP(
+ MTYPE_BGP_FILTER_NAME, ADVERTISE_MAP_NAME(srcfilter));
+ ADVERTISE_MAP(dstfilter) = ADVERTISE_MAP(srcfilter);
+ ADVERTISE_CONDITION(dstfilter) = ADVERTISE_CONDITION(srcfilter);
+ }
+
+ if (CONDITION_MAP_NAME(srcfilter)) {
+ CONDITION_MAP_NAME(dstfilter) = XSTRDUP(
+ MTYPE_BGP_FILTER_NAME, CONDITION_MAP_NAME(srcfilter));
+ CONDITION_MAP(dstfilter) = CONDITION_MAP(srcfilter);
+ }
+
+ dstfilter->advmap.update_type = srcfilter->advmap.update_type;
+}
+
+/**
+ * since we did a bunch of XSTRDUP's in conf_copy, time to free them up
+ */
+static void conf_release(struct peer *src, afi_t afi, safi_t safi)
+{
+ struct bgp_filter *srcfilter;
+
+ srcfilter = &src->filter[afi][safi];
+
+ XFREE(MTYPE_ROUTE_MAP_NAME, src->default_rmap[afi][safi].name);
+
+ XFREE(MTYPE_BGP_FILTER_NAME, srcfilter->dlist[FILTER_OUT].name);
+
+ XFREE(MTYPE_BGP_FILTER_NAME, srcfilter->plist[FILTER_OUT].name);
+
+ XFREE(MTYPE_BGP_FILTER_NAME, srcfilter->aslist[FILTER_OUT].name);
+
+ XFREE(MTYPE_BGP_FILTER_NAME, srcfilter->map[RMAP_OUT].name);
+
+ XFREE(MTYPE_BGP_FILTER_NAME, srcfilter->usmap.name);
+
+ XFREE(MTYPE_BGP_FILTER_NAME, srcfilter->advmap.aname);
+
+ XFREE(MTYPE_BGP_FILTER_NAME, srcfilter->advmap.cname);
+
+ XFREE(MTYPE_BGP_PEER_HOST, src->host);
+
+ ecommunity_free(&src->soo[afi][safi]);
+}
+
+static void peer2_updgrp_copy(struct update_group *updgrp, struct peer_af *paf)
+{
+ struct peer *src;
+ struct peer *dst;
+
+ if (!updgrp || !paf)
+ return;
+
+ src = paf->peer;
+ dst = updgrp->conf;
+ if (!src || !dst)
+ return;
+
+ updgrp->afi = paf->afi;
+ updgrp->safi = paf->safi;
+ updgrp->afid = paf->afid;
+ updgrp->bgp = src->bgp;
+
+ conf_copy(dst, src, paf->afi, paf->safi);
+}
+
+/**
+ * auxiliary functions to maintain the hash table.
+ * - updgrp_hash_alloc - to create a new entry, passed to hash_get
+ * - updgrp_hash_key_make - makes the key for update group search
+ * - updgrp_hash_cmp - compare two update groups.
+ */
+static void *updgrp_hash_alloc(void *p)
+{
+ struct update_group *updgrp;
+ const struct update_group *in;
+
+ in = (const struct update_group *)p;
+ updgrp = XCALLOC(MTYPE_BGP_UPDGRP, sizeof(struct update_group));
+ memcpy(updgrp, in, sizeof(struct update_group));
+ updgrp->conf = XCALLOC(MTYPE_BGP_PEER, sizeof(struct peer));
+ conf_copy(updgrp->conf, in->conf, in->afi, in->safi);
+ return updgrp;
+}
+
+/**
+ * The hash value for a peer is computed from the following variables:
+ * v = f(
+ * 1. IBGP (1) or EBGP (2)
+ * 2. FLAGS based on configuration:
+ * LOCAL_AS_NO_PREPEND
+ * LOCAL_AS_REPLACE_AS
+ * 3. AF_FLAGS based on configuration:
+ * Refer to definition in bgp_updgrp.h
+ * 4. (AF-independent) Capability flags:
+ * AS4_RCV capability
+ * 5. (AF-dependent) Capability flags:
+ * ORF_PREFIX_SM_RCV (peer can send prefix ORF)
+ * 6. MRAI
+ * 7. peer-group name
+ * 8. Outbound route-map name (neighbor route-map <> out)
+ * 9. Outbound distribute-list name (neighbor distribute-list <> out)
+ * 10. Outbound prefix-list name (neighbor prefix-list <> out)
+ * 11. Outbound as-list name (neighbor filter-list <> out)
+ * 12. Unsuppress map name (neighbor unsuppress-map <>)
+ * 13. default rmap name (neighbor default-originate route-map <>)
+ * 14. encoding both global and link-local nexthop?
+ * 15. If peer is configured to be a lonesoul, peer ip address
+ * 16. Local-as should match, if configured.
+ * 17. maximum-prefix-out
+ * 18. Local-role should also match, if configured.
+ * )
+ */
+static unsigned int updgrp_hash_key_make(const void *p)
+{
+ const struct update_group *updgrp;
+ const struct peer *peer;
+ const struct bgp_filter *filter;
+ uint32_t flags;
+ uint32_t key;
+ afi_t afi;
+ safi_t safi;
+
+#define SEED1 999331
+#define SEED2 2147483647
+
+ updgrp = p;
+ peer = updgrp->conf;
+ afi = updgrp->afi;
+ safi = updgrp->safi;
+ flags = peer->af_flags[afi][safi];
+ filter = &peer->filter[afi][safi];
+
+ key = 0;
+
+ key = jhash_1word(peer->sort, key); /* EBGP or IBGP */
+ key = jhash_1word((peer->flags & PEER_UPDGRP_FLAGS), key);
+ key = jhash_1word((flags & PEER_UPDGRP_AF_FLAGS), key);
+ key = jhash_1word((uint32_t)peer->addpath_type[afi][safi], key);
+ key = jhash_1word((peer->cap & PEER_UPDGRP_CAP_FLAGS), key);
+ key = jhash_1word((peer->af_cap[afi][safi] & PEER_UPDGRP_AF_CAP_FLAGS),
+ key);
+ key = jhash_1word(peer->v_routeadv, key);
+ key = jhash_1word(peer->change_local_as, key);
+ key = jhash_1word(peer->max_packet_size, key);
+ key = jhash_1word(peer->pmax_out[afi][safi], key);
+
+ if (peer->group)
+ key = jhash_1word(jhash(peer->group->name,
+ strlen(peer->group->name), SEED1),
+ key);
+
+ if (filter->map[RMAP_OUT].name)
+ key = jhash_1word(jhash(filter->map[RMAP_OUT].name,
+ strlen(filter->map[RMAP_OUT].name),
+ SEED1),
+ key);
+
+ if (filter->dlist[FILTER_OUT].name)
+ key = jhash_1word(jhash(filter->dlist[FILTER_OUT].name,
+ strlen(filter->dlist[FILTER_OUT].name),
+ SEED1),
+ key);
+
+ if (filter->plist[FILTER_OUT].name)
+ key = jhash_1word(jhash(filter->plist[FILTER_OUT].name,
+ strlen(filter->plist[FILTER_OUT].name),
+ SEED1),
+ key);
+
+ if (filter->aslist[FILTER_OUT].name)
+ key = jhash_1word(jhash(filter->aslist[FILTER_OUT].name,
+ strlen(filter->aslist[FILTER_OUT].name),
+ SEED1),
+ key);
+
+ if (filter->usmap.name)
+ key = jhash_1word(jhash(filter->usmap.name,
+ strlen(filter->usmap.name), SEED1),
+ key);
+
+ if (filter->advmap.aname)
+ key = jhash_1word(jhash(filter->advmap.aname,
+ strlen(filter->advmap.aname), SEED1),
+ key);
+
+ if (filter->advmap.update_type)
+ key = jhash_1word(filter->advmap.update_type, key);
+
+ if (peer->default_rmap[afi][safi].name)
+ key = jhash_1word(
+ jhash(peer->default_rmap[afi][safi].name,
+ strlen(peer->default_rmap[afi][safi].name),
+ SEED1),
+ key);
+
+ /* If peer is on a shared network and is exchanging IPv6 prefixes,
+ * it needs to include link-local address. That's different from
+ * non-shared-network peers (nexthop encoded with 32 bytes vs 16
+ * bytes). We create different update groups to take care of that.
+ */
+ key = jhash_1word(
+ (peer->shared_network && peer_afi_active_nego(peer, AFI_IP6)),
+ key);
+ /*
+ * There are certain peers that must get their own update-group:
+ * - lonesoul peers
+ * - peers that negotiated ORF
+ * - maximum-prefix-out is set
+ */
+ if (CHECK_FLAG(peer->flags, PEER_FLAG_LONESOUL)
+ || CHECK_FLAG(peer->af_cap[afi][safi], PEER_CAP_ORF_PREFIX_SM_RCV)
+ || CHECK_FLAG(peer->af_cap[afi][safi],
+ PEER_CAP_ORF_PREFIX_SM_OLD_RCV)
+ || CHECK_FLAG(peer->af_flags[afi][safi], PEER_FLAG_MAX_PREFIX_OUT))
+ key = jhash_1word(jhash(peer->host, strlen(peer->host), SEED2),
+ key);
+ /*
+ * Multiple sessions with the same neighbor should get their own
+ * update-group if they have different roles.
+ */
+ key = jhash_1word(peer->local_role, key);
+
+ if (peer->soo[afi][safi]) {
+ char *soo_str = ecommunity_str(peer->soo[afi][safi]);
+
+ key = jhash_1word(jhash(soo_str, strlen(soo_str), SEED1), key);
+ }
+
+ if (bgp_debug_neighbor_events(peer)) {
+ zlog_debug(
+ "%pBP Update Group Hash: sort: %d UpdGrpFlags: %ju UpdGrpAFFlags: %ju",
+ peer, peer->sort,
+ (intmax_t)CHECK_FLAG(peer->flags, PEER_UPDGRP_FLAGS),
+ (intmax_t)CHECK_FLAG(flags, PEER_UPDGRP_AF_FLAGS));
+ zlog_debug(
+ "%pBP Update Group Hash: addpath: %u UpdGrpCapFlag: %u UpdGrpCapAFFlag: %u route_adv: %u change local as: %u",
+ peer, (uint32_t)peer->addpath_type[afi][safi],
+ CHECK_FLAG(peer->cap, PEER_UPDGRP_CAP_FLAGS),
+ CHECK_FLAG(peer->af_cap[afi][safi],
+ PEER_UPDGRP_AF_CAP_FLAGS),
+ peer->v_routeadv, peer->change_local_as);
+ zlog_debug(
+ "%pBP Update Group Hash: max packet size: %u pmax_out: %u Peer Group: %s rmap out: %s",
+ peer, peer->max_packet_size, peer->pmax_out[afi][safi],
+ peer->group ? peer->group->name : "(NONE)",
+ ROUTE_MAP_OUT_NAME(filter) ? ROUTE_MAP_OUT_NAME(filter)
+ : "(NONE)");
+ zlog_debug(
+ "%pBP Update Group Hash: dlist out: %s plist out: %s aslist out: %s usmap out: %s advmap: %s",
+ peer,
+ DISTRIBUTE_OUT_NAME(filter)
+ ? DISTRIBUTE_OUT_NAME(filter)
+ : "(NONE)",
+ PREFIX_LIST_OUT_NAME(filter)
+ ? PREFIX_LIST_OUT_NAME(filter)
+ : "(NONE)",
+ FILTER_LIST_OUT_NAME(filter)
+ ? FILTER_LIST_OUT_NAME(filter)
+ : "(NONE)",
+ UNSUPPRESS_MAP_NAME(filter)
+ ? UNSUPPRESS_MAP_NAME(filter)
+ : "(NONE)",
+ ADVERTISE_MAP_NAME(filter) ? ADVERTISE_MAP_NAME(filter)
+ : "(NONE)");
+ zlog_debug(
+ "%pBP Update Group Hash: default rmap: %s shared network and afi active network: %d",
+ peer,
+ peer->default_rmap[afi][safi].name
+ ? peer->default_rmap[afi][safi].name
+ : "(NONE)",
+ peer->shared_network &&
+ peer_afi_active_nego(peer, AFI_IP6));
+ zlog_debug(
+ "%pBP Update Group Hash: Lonesoul: %d ORF prefix: %u ORF old: %u max prefix out: %ju",
+ peer, !!CHECK_FLAG(peer->flags, PEER_FLAG_LONESOUL),
+ CHECK_FLAG(peer->af_cap[afi][safi],
+ PEER_CAP_ORF_PREFIX_SM_RCV),
+ CHECK_FLAG(peer->af_cap[afi][safi],
+ PEER_CAP_ORF_PREFIX_SM_OLD_RCV),
+ (intmax_t)CHECK_FLAG(peer->af_flags[afi][safi],
+ PEER_FLAG_MAX_PREFIX_OUT));
+ zlog_debug("%pBP Update Group Hash key: %u", peer, key);
+ }
+ return key;
+}
+
+static bool updgrp_hash_cmp(const void *p1, const void *p2)
+{
+ const struct update_group *grp1;
+ const struct update_group *grp2;
+ const struct peer *pe1;
+ const struct peer *pe2;
+ uint32_t flags1;
+ uint32_t flags2;
+ const struct bgp_filter *fl1;
+ const struct bgp_filter *fl2;
+ afi_t afi;
+ safi_t safi;
+
+ if (!p1 || !p2)
+ return false;
+
+ grp1 = p1;
+ grp2 = p2;
+ pe1 = grp1->conf;
+ pe2 = grp2->conf;
+ afi = grp1->afi;
+ safi = grp1->safi;
+ flags1 = pe1->af_flags[afi][safi];
+ flags2 = pe2->af_flags[afi][safi];
+ fl1 = &pe1->filter[afi][safi];
+ fl2 = &pe2->filter[afi][safi];
+
+ /* put EBGP and IBGP peers in different update groups */
+ if (pe1->sort != pe2->sort)
+ return false;
+
+ /* check peer flags */
+ if ((pe1->flags & PEER_UPDGRP_FLAGS)
+ != (pe2->flags & PEER_UPDGRP_FLAGS))
+ return false;
+
+ /* If there is 'local-as' configured, it should match. */
+ if (pe1->change_local_as != pe2->change_local_as)
+ return false;
+
+ if (pe1->pmax_out[afi][safi] != pe2->pmax_out[afi][safi])
+ return false;
+
+ /* flags like route reflector client */
+ if ((flags1 & PEER_UPDGRP_AF_FLAGS) != (flags2 & PEER_UPDGRP_AF_FLAGS))
+ return false;
+
+ if (pe1->addpath_type[afi][safi] != pe2->addpath_type[afi][safi])
+ return false;
+
+ if ((pe1->cap & PEER_UPDGRP_CAP_FLAGS)
+ != (pe2->cap & PEER_UPDGRP_CAP_FLAGS))
+ return false;
+
+ if ((pe1->af_cap[afi][safi] & PEER_UPDGRP_AF_CAP_FLAGS)
+ != (pe2->af_cap[afi][safi] & PEER_UPDGRP_AF_CAP_FLAGS))
+ return false;
+
+ if (pe1->v_routeadv != pe2->v_routeadv)
+ return false;
+
+ if (pe1->group != pe2->group)
+ return false;
+
+ /* Roles can affect filtering */
+ if (pe1->local_role != pe2->local_role)
+ return false;
+
+ /* route-map names should be the same */
+ if ((fl1->map[RMAP_OUT].name && !fl2->map[RMAP_OUT].name)
+ || (!fl1->map[RMAP_OUT].name && fl2->map[RMAP_OUT].name)
+ || (fl1->map[RMAP_OUT].name && fl2->map[RMAP_OUT].name
+ && strcmp(fl1->map[RMAP_OUT].name, fl2->map[RMAP_OUT].name)))
+ return false;
+
+ if ((fl1->dlist[FILTER_OUT].name && !fl2->dlist[FILTER_OUT].name)
+ || (!fl1->dlist[FILTER_OUT].name && fl2->dlist[FILTER_OUT].name)
+ || (fl1->dlist[FILTER_OUT].name && fl2->dlist[FILTER_OUT].name
+ && strcmp(fl1->dlist[FILTER_OUT].name,
+ fl2->dlist[FILTER_OUT].name)))
+ return false;
+
+ if ((fl1->plist[FILTER_OUT].name && !fl2->plist[FILTER_OUT].name)
+ || (!fl1->plist[FILTER_OUT].name && fl2->plist[FILTER_OUT].name)
+ || (fl1->plist[FILTER_OUT].name && fl2->plist[FILTER_OUT].name
+ && strcmp(fl1->plist[FILTER_OUT].name,
+ fl2->plist[FILTER_OUT].name)))
+ return false;
+
+ if ((fl1->aslist[FILTER_OUT].name && !fl2->aslist[FILTER_OUT].name)
+ || (!fl1->aslist[FILTER_OUT].name && fl2->aslist[FILTER_OUT].name)
+ || (fl1->aslist[FILTER_OUT].name && fl2->aslist[FILTER_OUT].name
+ && strcmp(fl1->aslist[FILTER_OUT].name,
+ fl2->aslist[FILTER_OUT].name)))
+ return false;
+
+ if ((fl1->usmap.name && !fl2->usmap.name)
+ || (!fl1->usmap.name && fl2->usmap.name)
+ || (fl1->usmap.name && fl2->usmap.name
+ && strcmp(fl1->usmap.name, fl2->usmap.name)))
+ return false;
+
+ if ((fl1->advmap.aname && !fl2->advmap.aname)
+ || (!fl1->advmap.aname && fl2->advmap.aname)
+ || (fl1->advmap.aname && fl2->advmap.aname
+ && strcmp(fl1->advmap.aname, fl2->advmap.aname)))
+ return false;
+
+ if (fl1->advmap.update_type != fl2->advmap.update_type)
+ return false;
+
+ if ((pe1->default_rmap[afi][safi].name
+ && !pe2->default_rmap[afi][safi].name)
+ || (!pe1->default_rmap[afi][safi].name
+ && pe2->default_rmap[afi][safi].name)
+ || (pe1->default_rmap[afi][safi].name
+ && pe2->default_rmap[afi][safi].name
+ && strcmp(pe1->default_rmap[afi][safi].name,
+ pe2->default_rmap[afi][safi].name)))
+ return false;
+
+ if ((afi == AFI_IP6) && (pe1->shared_network != pe2->shared_network))
+ return false;
+
+ if ((CHECK_FLAG(pe1->flags, PEER_FLAG_LONESOUL)
+ || CHECK_FLAG(pe1->af_cap[afi][safi], PEER_CAP_ORF_PREFIX_SM_RCV)
+ || CHECK_FLAG(pe1->af_cap[afi][safi],
+ PEER_CAP_ORF_PREFIX_SM_OLD_RCV))
+ && !sockunion_same(&pe1->su, &pe2->su))
+ return false;
+
+ return true;
+}
+
+static void peer_lonesoul_or_not(struct peer *peer, int set)
+{
+ /* no change in status? */
+ if (set == (CHECK_FLAG(peer->flags, PEER_FLAG_LONESOUL) > 0))
+ return;
+
+ if (set)
+ SET_FLAG(peer->flags, PEER_FLAG_LONESOUL);
+ else
+ UNSET_FLAG(peer->flags, PEER_FLAG_LONESOUL);
+
+ update_group_adjust_peer_afs(peer);
+}
+
+/*
+ * subgroup_total_packets_enqueued
+ *
+ * Returns the total number of packets enqueued to a subgroup.
+ */
+static unsigned int
+subgroup_total_packets_enqueued(struct update_subgroup *subgrp)
+{
+ struct bpacket *pkt;
+
+ pkt = bpacket_queue_last(SUBGRP_PKTQ(subgrp));
+
+ return pkt->ver - 1;
+}
+
+static int update_group_show_walkcb(struct update_group *updgrp, void *arg)
+{
+ struct updwalk_context *ctx = arg;
+ struct vty *vty;
+ struct update_subgroup *subgrp;
+ struct peer_af *paf;
+ struct bgp_filter *filter;
+ struct peer *peer = UPDGRP_PEER(updgrp);
+ int match = 0;
+
+ if (!ctx)
+ return CMD_SUCCESS;
+
+ if (ctx->subgrp_id) {
+ UPDGRP_FOREACH_SUBGRP (updgrp, subgrp) {
+ if (ctx->subgrp_id && (ctx->subgrp_id != subgrp->id))
+ continue;
+ else {
+ match = 1;
+ break;
+ }
+ }
+ } else {
+ match = 1;
+ }
+
+ if (!match) {
+ /* Since this routine is invoked from a walk, we cannot signal
+ * any */
+ /* error here, can only return. */
+ return CMD_SUCCESS;
+ }
+
+ vty = ctx->vty;
+
+ vty_out(vty, "Update-group %" PRIu64 ":\n", updgrp->id);
+ vty_out(vty, " Created: %s", timestamp_string(updgrp->uptime));
+ filter = &updgrp->conf->filter[updgrp->afi][updgrp->safi];
+ if (filter->map[RMAP_OUT].name)
+ vty_out(vty, " Outgoing route map: %s\n",
+ filter->map[RMAP_OUT].name);
+ vty_out(vty, " MRAI value (seconds): %d\n", updgrp->conf->v_routeadv);
+ if (updgrp->conf->change_local_as)
+ vty_out(vty, " Local AS %u%s%s\n",
+ updgrp->conf->change_local_as,
+ CHECK_FLAG(updgrp->conf->flags,
+ PEER_FLAG_LOCAL_AS_NO_PREPEND)
+ ? " no-prepend"
+ : "",
+ CHECK_FLAG(updgrp->conf->flags,
+ PEER_FLAG_LOCAL_AS_REPLACE_AS)
+ ? " replace-as"
+ : "");
+
+ UPDGRP_FOREACH_SUBGRP (updgrp, subgrp) {
+ if (ctx->subgrp_id && (ctx->subgrp_id != subgrp->id))
+ continue;
+ vty_out(vty, "\n");
+ vty_out(vty, " Update-subgroup %" PRIu64 ":\n", subgrp->id);
+ vty_out(vty, " Created: %s",
+ timestamp_string(subgrp->uptime));
+
+ if (subgrp->split_from.update_group_id
+ || subgrp->split_from.subgroup_id) {
+ vty_out(vty, " Split from group id: %" PRIu64 "\n",
+ subgrp->split_from.update_group_id);
+ vty_out(vty,
+ " Split from subgroup id: %" PRIu64 "\n",
+ subgrp->split_from.subgroup_id);
+ }
+
+ vty_out(vty, " Join events: %u\n", subgrp->join_events);
+ vty_out(vty, " Prune events: %u\n", subgrp->prune_events);
+ vty_out(vty, " Merge events: %u\n", subgrp->merge_events);
+ vty_out(vty, " Split events: %u\n", subgrp->split_events);
+ vty_out(vty, " Update group switch events: %u\n",
+ subgrp->updgrp_switch_events);
+ vty_out(vty, " Peer refreshes combined: %u\n",
+ subgrp->peer_refreshes_combined);
+ vty_out(vty, " Merge checks triggered: %u\n",
+ subgrp->merge_checks_triggered);
+ vty_out(vty, " Coalesce Time: %u%s\n",
+ (UPDGRP_INST(subgrp->update_group))->coalesce_time,
+ subgrp->t_coalesce ? "(Running)" : "");
+ vty_out(vty, " Version: %" PRIu64 "\n", subgrp->version);
+ vty_out(vty, " Packet queue length: %d\n",
+ bpacket_queue_length(SUBGRP_PKTQ(subgrp)));
+ vty_out(vty, " Total packets enqueued: %u\n",
+ subgroup_total_packets_enqueued(subgrp));
+ vty_out(vty, " Packet queue high watermark: %d\n",
+ bpacket_queue_hwm_length(SUBGRP_PKTQ(subgrp)));
+ vty_out(vty, " Adj-out list count: %u\n", subgrp->adj_count);
+ vty_out(vty, " Advertise list: %s\n",
+ advertise_list_is_empty(subgrp) ? "empty"
+ : "not empty");
+ vty_out(vty, " Flags: %s\n",
+ CHECK_FLAG(subgrp->flags, SUBGRP_FLAG_NEEDS_REFRESH)
+ ? "R"
+ : "");
+ if (peer)
+ vty_out(vty, " Max packet size: %d\n",
+ peer->max_packet_size);
+ if (subgrp->peer_count > 0) {
+ vty_out(vty, " Peers:\n");
+ SUBGRP_FOREACH_PEER (subgrp, paf)
+ vty_out(vty, " - %s\n", paf->peer->host);
+ }
+ }
+ return UPDWALK_CONTINUE;
+}
+
+/*
+ * Helper function to show the packet queue for each subgroup of update group.
+ * Will be constrained to a particular subgroup id if id !=0
+ */
+static int updgrp_show_packet_queue_walkcb(struct update_group *updgrp,
+ void *arg)
+{
+ struct updwalk_context *ctx = arg;
+ struct update_subgroup *subgrp;
+ struct vty *vty;
+
+ vty = ctx->vty;
+ UPDGRP_FOREACH_SUBGRP (updgrp, subgrp) {
+ if (ctx->subgrp_id && (ctx->subgrp_id != subgrp->id))
+ continue;
+ vty_out(vty, "update group %" PRIu64 ", subgroup %" PRIu64 "\n",
+ updgrp->id, subgrp->id);
+ bpacket_queue_show_vty(SUBGRP_PKTQ(subgrp), vty);
+ }
+ return UPDWALK_CONTINUE;
+}
+
+/*
+ * Show the packet queue for each subgroup of update group. Will be
+ * constrained to a particular subgroup id if id !=0
+ */
+void update_group_show_packet_queue(struct bgp *bgp, afi_t afi, safi_t safi,
+ struct vty *vty, uint64_t id)
+{
+ struct updwalk_context ctx;
+
+ memset(&ctx, 0, sizeof(ctx));
+ ctx.vty = vty;
+ ctx.subgrp_id = id;
+ ctx.flags = 0;
+ update_group_af_walk(bgp, afi, safi, updgrp_show_packet_queue_walkcb,
+ &ctx);
+}
+
+static struct update_group *update_group_find(struct peer_af *paf)
+{
+ struct update_group *updgrp;
+ struct update_group tmp;
+ struct peer tmp_conf;
+
+ if (!peer_established(PAF_PEER(paf)))
+ return NULL;
+
+ memset(&tmp, 0, sizeof(tmp));
+ memset(&tmp_conf, 0, sizeof(tmp_conf));
+ tmp.conf = &tmp_conf;
+ peer2_updgrp_copy(&tmp, paf);
+
+ updgrp = hash_lookup(paf->peer->bgp->update_groups[paf->afid], &tmp);
+ conf_release(&tmp_conf, paf->afi, paf->safi);
+ return updgrp;
+}
+
+static struct update_group *update_group_create(struct peer_af *paf)
+{
+ struct update_group *updgrp;
+ struct update_group tmp;
+ struct peer tmp_conf;
+
+ memset(&tmp, 0, sizeof(tmp));
+ memset(&tmp_conf, 0, sizeof(tmp_conf));
+ tmp.conf = &tmp_conf;
+ peer2_updgrp_copy(&tmp, paf);
+
+ updgrp = hash_get(paf->peer->bgp->update_groups[paf->afid], &tmp,
+ updgrp_hash_alloc);
+ update_group_checkin(updgrp);
+
+ if (BGP_DEBUG(update_groups, UPDATE_GROUPS))
+ zlog_debug("create update group %" PRIu64, updgrp->id);
+
+ UPDGRP_GLOBAL_STAT(updgrp, updgrps_created) += 1;
+
+ conf_release(&tmp_conf, paf->afi, paf->safi);
+ return updgrp;
+}
+
+static void update_group_delete(struct update_group *updgrp)
+{
+ if (BGP_DEBUG(update_groups, UPDATE_GROUPS))
+ zlog_debug("delete update group %" PRIu64, updgrp->id);
+
+ UPDGRP_GLOBAL_STAT(updgrp, updgrps_deleted) += 1;
+
+ hash_release(updgrp->bgp->update_groups[updgrp->afid], updgrp);
+ conf_release(updgrp->conf, updgrp->afi, updgrp->safi);
+
+ XFREE(MTYPE_BGP_PEER_HOST, updgrp->conf->host);
+
+ XFREE(MTYPE_BGP_PEER_IFNAME, updgrp->conf->ifname);
+
+ XFREE(MTYPE_BGP_PEER, updgrp->conf);
+ XFREE(MTYPE_BGP_UPDGRP, updgrp);
+}
+
+static void update_group_add_subgroup(struct update_group *updgrp,
+ struct update_subgroup *subgrp)
+{
+ if (!updgrp || !subgrp)
+ return;
+
+ LIST_INSERT_HEAD(&(updgrp->subgrps), subgrp, updgrp_train);
+ subgrp->update_group = updgrp;
+}
+
+static void update_group_remove_subgroup(struct update_group *updgrp,
+ struct update_subgroup *subgrp)
+{
+ if (!updgrp || !subgrp)
+ return;
+
+ LIST_REMOVE(subgrp, updgrp_train);
+ subgrp->update_group = NULL;
+ if (LIST_EMPTY(&(updgrp->subgrps)))
+ update_group_delete(updgrp);
+}
+
+static struct update_subgroup *
+update_subgroup_create(struct update_group *updgrp)
+{
+ struct update_subgroup *subgrp;
+
+ subgrp = XCALLOC(MTYPE_BGP_UPD_SUBGRP, sizeof(struct update_subgroup));
+ update_subgroup_checkin(subgrp, updgrp);
+ subgrp->v_coalesce = (UPDGRP_INST(updgrp))->coalesce_time;
+ sync_init(subgrp, updgrp);
+ bpacket_queue_init(SUBGRP_PKTQ(subgrp));
+ bpacket_queue_add(SUBGRP_PKTQ(subgrp), NULL, NULL);
+ TAILQ_INIT(&(subgrp->adjq));
+ if (BGP_DEBUG(update_groups, UPDATE_GROUPS))
+ zlog_debug("create subgroup u%" PRIu64 ":s%" PRIu64, updgrp->id,
+ subgrp->id);
+
+ update_group_add_subgroup(updgrp, subgrp);
+
+ UPDGRP_INCR_STAT(updgrp, subgrps_created);
+
+ return subgrp;
+}
+
+static void update_subgroup_delete(struct update_subgroup *subgrp)
+{
+ if (!subgrp)
+ return;
+
+ if (subgrp->update_group)
+ UPDGRP_INCR_STAT(subgrp->update_group, subgrps_deleted);
+
+ THREAD_OFF(subgrp->t_merge_check);
+ THREAD_OFF(subgrp->t_coalesce);
+
+ bpacket_queue_cleanup(SUBGRP_PKTQ(subgrp));
+ subgroup_clear_table(subgrp);
+
+ sync_delete(subgrp);
+
+ if (BGP_DEBUG(update_groups, UPDATE_GROUPS) && subgrp->update_group)
+ zlog_debug("delete subgroup u%" PRIu64 ":s%" PRIu64,
+ subgrp->update_group->id, subgrp->id);
+
+ update_group_remove_subgroup(subgrp->update_group, subgrp);
+
+ XFREE(MTYPE_BGP_UPD_SUBGRP, subgrp);
+}
+
+void update_subgroup_inherit_info(struct update_subgroup *to,
+ struct update_subgroup *from)
+{
+ if (!to || !from)
+ return;
+
+ to->sflags = from->sflags;
+}
+
+/*
+ * update_subgroup_check_delete
+ *
+ * Delete a subgroup if it is ready to be deleted.
+ *
+ * Returns true if the subgroup was deleted.
+ */
+static bool update_subgroup_check_delete(struct update_subgroup *subgrp)
+{
+ if (!subgrp)
+ return false;
+
+ if (!LIST_EMPTY(&(subgrp->peers)))
+ return false;
+
+ update_subgroup_delete(subgrp);
+
+ return true;
+}
+
+/*
+ * update_subgroup_add_peer
+ *
+ * @param send_enqueued_packets If true all currently enqueued packets will
+ * also be sent to the peer.
+ */
+static void update_subgroup_add_peer(struct update_subgroup *subgrp,
+ struct peer_af *paf,
+ int send_enqueued_pkts)
+{
+ struct bpacket *pkt;
+
+ if (!subgrp || !paf)
+ return;
+
+ LIST_INSERT_HEAD(&(subgrp->peers), paf, subgrp_train);
+ paf->subgroup = subgrp;
+ subgrp->peer_count++;
+
+ if (bgp_debug_peer_updout_enabled(paf->peer->host)) {
+ UPDGRP_PEER_DBG_EN(subgrp->update_group);
+ }
+
+ SUBGRP_INCR_STAT(subgrp, join_events);
+
+ if (send_enqueued_pkts) {
+ pkt = bpacket_queue_first(SUBGRP_PKTQ(subgrp));
+ } else {
+
+ /*
+ * Hang the peer off of the last, placeholder, packet in the
+ * queue. This means it won't see any of the packets that are
+ * currently the queue.
+ */
+ pkt = bpacket_queue_last(SUBGRP_PKTQ(subgrp));
+ assert(pkt->buffer == NULL);
+ }
+
+ bpacket_add_peer(pkt, paf);
+
+ if (BGP_DEBUG(update_groups, UPDATE_GROUPS))
+ zlog_debug("peer %s added to subgroup s%" PRIu64,
+ paf->peer->host, subgrp->id);
+}
+
+/*
+ * update_subgroup_remove_peer_internal
+ *
+ * Internal function that removes a peer from a subgroup, but does not
+ * delete the subgroup. A call to this function must almost always be
+ * followed by a call to update_subgroup_check_delete().
+ *
+ * @see update_subgroup_remove_peer
+ */
+static void update_subgroup_remove_peer_internal(struct update_subgroup *subgrp,
+ struct peer_af *paf)
+{
+ assert(subgrp && paf && subgrp->update_group);
+
+ if (bgp_debug_peer_updout_enabled(paf->peer->host)) {
+ UPDGRP_PEER_DBG_DIS(subgrp->update_group);
+ }
+
+ bpacket_queue_remove_peer(paf);
+ LIST_REMOVE(paf, subgrp_train);
+ paf->subgroup = NULL;
+ subgrp->peer_count--;
+
+ if (BGP_DEBUG(update_groups, UPDATE_GROUPS))
+ zlog_debug("peer %s deleted from subgroup s%"
+ PRIu64 " peer cnt %d",
+ paf->peer->host, subgrp->id, subgrp->peer_count);
+ SUBGRP_INCR_STAT(subgrp, prune_events);
+}
+
+/*
+ * update_subgroup_remove_peer
+ */
+void update_subgroup_remove_peer(struct update_subgroup *subgrp,
+ struct peer_af *paf)
+{
+ if (!subgrp || !paf)
+ return;
+
+ update_subgroup_remove_peer_internal(subgrp, paf);
+
+ if (update_subgroup_check_delete(subgrp))
+ return;
+
+ /*
+ * The deletion of the peer may have caused some packets to be
+ * deleted from the subgroup packet queue. Check if the subgroup can
+ * be merged now.
+ */
+ update_subgroup_check_merge(subgrp, "removed peer from subgroup");
+}
+
+static struct update_subgroup *update_subgroup_find(struct update_group *updgrp,
+ struct peer_af *paf)
+{
+ struct update_subgroup *subgrp = NULL;
+ uint64_t version;
+
+ if (paf->subgroup) {
+ assert(0);
+ return NULL;
+ } else
+ version = 0;
+
+ if (!peer_established(PAF_PEER(paf)))
+ return NULL;
+
+ UPDGRP_FOREACH_SUBGRP (updgrp, subgrp) {
+ if (subgrp->version != version
+ || CHECK_FLAG(subgrp->sflags,
+ SUBGRP_STATUS_DEFAULT_ORIGINATE))
+ continue;
+
+ /*
+ * The version number is not meaningful on a subgroup that needs
+ * a refresh.
+ */
+ if (update_subgroup_needs_refresh(subgrp))
+ continue;
+
+ break;
+ }
+
+ return subgrp;
+}
+
+/*
+ * update_subgroup_ready_for_merge
+ *
+ * Returns true if this subgroup is in a state that allows it to be
+ * merged into another subgroup.
+ */
+static bool update_subgroup_ready_for_merge(struct update_subgroup *subgrp)
+{
+
+ /*
+ * Not ready if there are any encoded packets waiting to be written
+ * out to peers.
+ */
+ if (!bpacket_queue_is_empty(SUBGRP_PKTQ(subgrp)))
+ return false;
+
+ /*
+ * Not ready if there enqueued updates waiting to be encoded.
+ */
+ if (!advertise_list_is_empty(subgrp))
+ return false;
+
+ /*
+ * Don't attempt to merge a subgroup that needs a refresh. For one,
+ * we can't determine if the adj_out of such a group matches that of
+ * another group.
+ */
+ if (update_subgroup_needs_refresh(subgrp))
+ return false;
+
+ return true;
+}
+
+/*
+ * update_subgrp_can_merge_into
+ *
+ * Returns true if the first subgroup can merge into the second
+ * subgroup.
+ */
+static int update_subgroup_can_merge_into(struct update_subgroup *subgrp,
+ struct update_subgroup *target)
+{
+
+ if (subgrp == target)
+ return 0;
+
+ /*
+ * Both must have processed the BRIB to the same point in order to
+ * be merged.
+ */
+ if (subgrp->version != target->version)
+ return 0;
+
+ if (CHECK_FLAG(subgrp->sflags, SUBGRP_STATUS_DEFAULT_ORIGINATE)
+ != CHECK_FLAG(target->sflags, SUBGRP_STATUS_DEFAULT_ORIGINATE))
+ return 0;
+
+ if (subgrp->adj_count != target->adj_count)
+ return 0;
+
+ return update_subgroup_ready_for_merge(target);
+}
+
+/*
+ * update_subgroup_merge
+ *
+ * Merge the first subgroup into the second one.
+ */
+static void update_subgroup_merge(struct update_subgroup *subgrp,
+ struct update_subgroup *target,
+ const char *reason)
+{
+ struct peer_af *paf;
+ int result;
+ int peer_count;
+
+ assert(subgrp->adj_count == target->adj_count);
+
+ peer_count = subgrp->peer_count;
+
+ while (1) {
+ paf = LIST_FIRST(&subgrp->peers);
+ if (!paf)
+ break;
+
+ update_subgroup_remove_peer_internal(subgrp, paf);
+
+ /*
+ * Add the peer to the target subgroup, while making sure that
+ * any currently enqueued packets won't be sent to it. Enqueued
+ * packets could, for example, result in an unnecessary withdraw
+ * followed by an advertise.
+ */
+ update_subgroup_add_peer(target, paf, 0);
+ }
+
+ SUBGRP_INCR_STAT(target, merge_events);
+
+ if (BGP_DEBUG(update_groups, UPDATE_GROUPS))
+ zlog_debug("u%" PRIu64 ":s%" PRIu64" (%d peers) merged into u%" PRIu64 ":s%" PRIu64", trigger: %s",
+ subgrp->update_group->id, subgrp->id, peer_count,
+ target->update_group->id, target->id,
+ reason ? reason : "unknown");
+
+ result = update_subgroup_check_delete(subgrp);
+ assert(result);
+}
+
+/*
+ * update_subgroup_check_merge
+ *
+ * Merge this subgroup into another subgroup if possible.
+ *
+ * Returns true if the subgroup has been merged. The subgroup pointer
+ * should not be accessed in this case.
+ */
+bool update_subgroup_check_merge(struct update_subgroup *subgrp,
+ const char *reason)
+{
+ struct update_subgroup *target;
+
+ if (!update_subgroup_ready_for_merge(subgrp))
+ return false;
+
+ /*
+ * Look for a subgroup to merge into.
+ */
+ UPDGRP_FOREACH_SUBGRP (subgrp->update_group, target) {
+ if (update_subgroup_can_merge_into(subgrp, target))
+ break;
+ }
+
+ if (!target)
+ return false;
+
+ update_subgroup_merge(subgrp, target, reason);
+ return true;
+}
+
+/*
+* update_subgroup_merge_check_thread_cb
+*/
+static void update_subgroup_merge_check_thread_cb(struct thread *thread)
+{
+ struct update_subgroup *subgrp;
+
+ subgrp = THREAD_ARG(thread);
+
+ subgrp->t_merge_check = NULL;
+
+ update_subgroup_check_merge(subgrp, "triggered merge check");
+}
+
+/*
+ * update_subgroup_trigger_merge_check
+ *
+ * Triggers a call to update_subgroup_check_merge() on a clean context.
+ *
+ * @param force If true, the merge check will be triggered even if the
+ * subgroup doesn't currently look ready for a merge.
+ *
+ * Returns true if a merge check will be performed shortly.
+ */
+bool update_subgroup_trigger_merge_check(struct update_subgroup *subgrp,
+ int force)
+{
+ if (subgrp->t_merge_check)
+ return true;
+
+ if (!force && !update_subgroup_ready_for_merge(subgrp))
+ return false;
+
+ subgrp->t_merge_check = NULL;
+ thread_add_timer_msec(bm->master, update_subgroup_merge_check_thread_cb,
+ subgrp, 0, &subgrp->t_merge_check);
+
+ SUBGRP_INCR_STAT(subgrp, merge_checks_triggered);
+
+ return true;
+}
+
+/*
+ * update_subgroup_copy_adj_out
+ *
+ * Helper function that clones the adj out (state about advertised
+ * routes) from one subgroup to another. It assumes that the adj out
+ * of the target subgroup is empty.
+ */
+static void update_subgroup_copy_adj_out(struct update_subgroup *source,
+ struct update_subgroup *dest)
+{
+ struct bgp_adj_out *aout, *aout_copy;
+
+ SUBGRP_FOREACH_ADJ (source, aout) {
+ /*
+ * Copy the adj out.
+ */
+ aout_copy = bgp_adj_out_alloc(dest, aout->dest,
+ aout->addpath_tx_id);
+ aout_copy->attr =
+ aout->attr ? bgp_attr_intern(aout->attr) : NULL;
+ }
+
+ dest->scount = source->scount;
+}
+
+/*
+ * update_subgroup_copy_packets
+ *
+ * Copy packets after and including the given packet to the subgroup
+ * 'dest'.
+ *
+ * Returns the number of packets copied.
+ */
+static int update_subgroup_copy_packets(struct update_subgroup *dest,
+ struct bpacket *pkt)
+{
+ int count;
+
+ count = 0;
+ while (pkt && pkt->buffer) {
+ bpacket_queue_add(SUBGRP_PKTQ(dest), stream_dup(pkt->buffer),
+ &pkt->arr);
+ count++;
+ pkt = bpacket_next(pkt);
+ }
+
+ return count;
+}
+
+static bool updgrp_prefix_list_update(struct update_group *updgrp,
+ const char *name)
+{
+ struct peer *peer;
+ struct bgp_filter *filter;
+
+ peer = UPDGRP_PEER(updgrp);
+ filter = &peer->filter[UPDGRP_AFI(updgrp)][UPDGRP_SAFI(updgrp)];
+
+ if (PREFIX_LIST_OUT_NAME(filter)
+ && (strcmp(name, PREFIX_LIST_OUT_NAME(filter)) == 0)) {
+ PREFIX_LIST_OUT(filter) = prefix_list_lookup(
+ UPDGRP_AFI(updgrp), PREFIX_LIST_OUT_NAME(filter));
+ return true;
+ }
+ return false;
+}
+
+static bool updgrp_filter_list_update(struct update_group *updgrp,
+ const char *name)
+{
+ struct peer *peer;
+ struct bgp_filter *filter;
+
+ peer = UPDGRP_PEER(updgrp);
+ filter = &peer->filter[UPDGRP_AFI(updgrp)][UPDGRP_SAFI(updgrp)];
+
+ if (FILTER_LIST_OUT_NAME(filter)
+ && (strcmp(name, FILTER_LIST_OUT_NAME(filter)) == 0)) {
+ FILTER_LIST_OUT(filter) =
+ as_list_lookup(FILTER_LIST_OUT_NAME(filter));
+ return true;
+ }
+ return false;
+}
+
+static bool updgrp_distribute_list_update(struct update_group *updgrp,
+ const char *name)
+{
+ struct peer *peer;
+ struct bgp_filter *filter;
+
+ peer = UPDGRP_PEER(updgrp);
+ filter = &peer->filter[UPDGRP_AFI(updgrp)][UPDGRP_SAFI(updgrp)];
+
+ if (DISTRIBUTE_OUT_NAME(filter)
+ && (strcmp(name, DISTRIBUTE_OUT_NAME(filter)) == 0)) {
+ DISTRIBUTE_OUT(filter) = access_list_lookup(
+ UPDGRP_AFI(updgrp), DISTRIBUTE_OUT_NAME(filter));
+ return true;
+ }
+ return false;
+}
+
+static int updgrp_route_map_update(struct update_group *updgrp,
+ const char *name, int *def_rmap_changed)
+{
+ struct peer *peer;
+ struct bgp_filter *filter;
+ int changed = 0;
+ afi_t afi;
+ safi_t safi;
+
+ peer = UPDGRP_PEER(updgrp);
+ afi = UPDGRP_AFI(updgrp);
+ safi = UPDGRP_SAFI(updgrp);
+ filter = &peer->filter[afi][safi];
+
+ if (ROUTE_MAP_OUT_NAME(filter)
+ && (strcmp(name, ROUTE_MAP_OUT_NAME(filter)) == 0)) {
+ ROUTE_MAP_OUT(filter) = route_map_lookup_by_name(name);
+
+ changed = 1;
+ }
+
+ if (UNSUPPRESS_MAP_NAME(filter)
+ && (strcmp(name, UNSUPPRESS_MAP_NAME(filter)) == 0)) {
+ UNSUPPRESS_MAP(filter) = route_map_lookup_by_name(name);
+ changed = 1;
+ }
+
+ /* process default-originate route-map */
+ if (peer->default_rmap[afi][safi].name
+ && (strcmp(name, peer->default_rmap[afi][safi].name) == 0)) {
+ peer->default_rmap[afi][safi].map =
+ route_map_lookup_by_name(name);
+ if (def_rmap_changed)
+ *def_rmap_changed = 1;
+ }
+ return changed;
+}
+
+/*
+ * hash iteration callback function to process a policy change for an
+ * update group. Check if the changed policy matches the updgrp's
+ * outbound route-map or unsuppress-map or default-originate map or
+ * filter-list or prefix-list or distribute-list.
+ * Trigger update generation accordingly.
+ */
+static int updgrp_policy_update_walkcb(struct update_group *updgrp, void *arg)
+{
+ struct updwalk_context *ctx = arg;
+ struct update_subgroup *subgrp;
+ int changed = 0;
+ int def_changed = 0;
+
+ if (!updgrp || !ctx || !ctx->policy_name)
+ return UPDWALK_CONTINUE;
+
+ switch (ctx->policy_type) {
+ case BGP_POLICY_ROUTE_MAP:
+ changed = updgrp_route_map_update(updgrp, ctx->policy_name,
+ &def_changed);
+ break;
+ case BGP_POLICY_FILTER_LIST:
+ changed = updgrp_filter_list_update(updgrp, ctx->policy_name);
+ break;
+ case BGP_POLICY_PREFIX_LIST:
+ changed = updgrp_prefix_list_update(updgrp, ctx->policy_name);
+ break;
+ case BGP_POLICY_DISTRIBUTE_LIST:
+ changed =
+ updgrp_distribute_list_update(updgrp, ctx->policy_name);
+ break;
+ default:
+ break;
+ }
+
+ /* If not doing route update, return after updating "config" */
+ if (!ctx->policy_route_update)
+ return UPDWALK_CONTINUE;
+
+ /* If nothing has changed, return after updating "config" */
+ if (!changed && !def_changed)
+ return UPDWALK_CONTINUE;
+
+ /*
+ * If something has changed, at the beginning of a route-map
+ * modification
+ * event, mark each subgroup's needs-refresh bit. For one, it signals to
+ * whoever that the subgroup needs a refresh. Second, it prevents
+ * premature
+ * merge of this subgroup with another before a complete (outbound)
+ * refresh.
+ */
+ if (ctx->policy_event_start_flag) {
+ UPDGRP_FOREACH_SUBGRP (updgrp, subgrp) {
+ update_subgroup_set_needs_refresh(subgrp, 1);
+ }
+ return UPDWALK_CONTINUE;
+ }
+
+ UPDGRP_FOREACH_SUBGRP (updgrp, subgrp) {
+ /* Avoid supressing duplicate routes later
+ * when processing in subgroup_announce_table().
+ */
+ SET_FLAG(subgrp->sflags, SUBGRP_STATUS_FORCE_UPDATES);
+
+ if (changed) {
+ if (bgp_debug_update(NULL, NULL, updgrp, 0))
+ zlog_debug(
+ "u%" PRIu64 ":s%" PRIu64" announcing routes upon policy %s (type %d) change",
+ updgrp->id, subgrp->id,
+ ctx->policy_name, ctx->policy_type);
+ subgroup_announce_route(subgrp);
+ }
+ if (def_changed) {
+ if (bgp_debug_update(NULL, NULL, updgrp, 0))
+ zlog_debug(
+ "u%" PRIu64 ":s%" PRIu64" announcing default upon default routemap %s change",
+ updgrp->id, subgrp->id,
+ ctx->policy_name);
+ if (route_map_lookup_by_name(ctx->policy_name)) {
+ /*
+ * When there is change in routemap, this flow
+ * is triggered. the routemap is still present
+ * in lib, hence its a update flow. The flag
+ * needs to be unset.
+ */
+ UNSET_FLAG(subgrp->sflags,
+ SUBGRP_STATUS_DEFAULT_ORIGINATE);
+ subgroup_default_originate(subgrp, 0);
+ } else {
+ /*
+ * This is a explicit withdraw, since the
+ * routemap is not present in routemap lib. need
+ * to pass 1 for withdraw arg.
+ */
+ subgroup_default_originate(subgrp, 1);
+ }
+ }
+ update_subgroup_set_needs_refresh(subgrp, 0);
+ }
+ return UPDWALK_CONTINUE;
+}
+
+static int update_group_walkcb(struct hash_bucket *bucket, void *arg)
+{
+ struct update_group *updgrp = bucket->data;
+ struct updwalk_context *wctx = arg;
+ int ret = (*wctx->cb)(updgrp, wctx->context);
+ return ret;
+}
+
+static int update_group_periodic_merge_walkcb(struct update_group *updgrp,
+ void *arg)
+{
+ struct update_subgroup *subgrp;
+ struct update_subgroup *tmp_subgrp;
+ const char *reason = arg;
+
+ UPDGRP_FOREACH_SUBGRP_SAFE (updgrp, subgrp, tmp_subgrp)
+ update_subgroup_check_merge(subgrp, reason);
+ return UPDWALK_CONTINUE;
+}
+
+/********************
+ * PUBLIC FUNCTIONS
+ ********************/
+
+/*
+ * trigger function when a policy (route-map/filter-list/prefix-list/
+ * distribute-list etc.) content changes. Go through all the
+ * update groups and process the change.
+ *
+ * bgp: the bgp instance
+ * ptype: the type of policy that got modified, see bgpd.h
+ * pname: name of the policy
+ * route_update: flag to control if an automatic update generation should
+ * occur
+ * start_event: flag that indicates if it's the beginning of the change.
+ * Esp. when the user is changing the content interactively
+ * over multiple statements. Useful to set dirty flag on
+ * update groups.
+ */
+void update_group_policy_update(struct bgp *bgp, enum bgp_policy_type ptype,
+ const char *pname, bool route_update,
+ int start_event)
+{
+ struct updwalk_context ctx;
+
+ memset(&ctx, 0, sizeof(ctx));
+ ctx.policy_type = ptype;
+ ctx.policy_name = pname;
+ ctx.policy_route_update = route_update;
+ ctx.policy_event_start_flag = start_event;
+ ctx.flags = 0;
+
+ update_group_walk(bgp, updgrp_policy_update_walkcb, &ctx);
+}
+
+/*
+ * update_subgroup_split_peer
+ *
+ * Ensure that the given peer is in a subgroup of its own in the
+ * specified update group.
+ */
+void update_subgroup_split_peer(struct peer_af *paf,
+ struct update_group *updgrp)
+{
+ struct update_subgroup *old_subgrp, *subgrp;
+ uint64_t old_id;
+
+
+ old_subgrp = paf->subgroup;
+
+ if (!updgrp)
+ updgrp = old_subgrp->update_group;
+
+ /*
+ * If the peer is alone in its subgroup, reuse the existing
+ * subgroup.
+ */
+ if (old_subgrp->peer_count == 1) {
+ if (updgrp == old_subgrp->update_group)
+ return;
+
+ subgrp = old_subgrp;
+ old_id = old_subgrp->update_group->id;
+
+ if (bgp_debug_peer_updout_enabled(paf->peer->host)) {
+ UPDGRP_PEER_DBG_DIS(old_subgrp->update_group);
+ }
+
+ update_group_remove_subgroup(old_subgrp->update_group,
+ old_subgrp);
+ update_group_add_subgroup(updgrp, subgrp);
+
+ if (bgp_debug_peer_updout_enabled(paf->peer->host)) {
+ UPDGRP_PEER_DBG_EN(updgrp);
+ }
+ if (BGP_DEBUG(update_groups, UPDATE_GROUPS))
+ zlog_debug("u%" PRIu64 ":s%" PRIu64" peer %s moved to u%" PRIu64 ":s%" PRIu64,
+ old_id, subgrp->id, paf->peer->host,
+ updgrp->id, subgrp->id);
+
+ /*
+ * The state of the subgroup (adj_out, advs, packet queue etc)
+ * is consistent internally, but may not be identical to other
+ * subgroups in the new update group even if the version number
+ * matches up. Make sure a full refresh is done before the
+ * subgroup is merged with another.
+ */
+ update_subgroup_set_needs_refresh(subgrp, 1);
+
+ SUBGRP_INCR_STAT(subgrp, updgrp_switch_events);
+ return;
+ }
+
+ /*
+ * Create a new subgroup under the specified update group, and copy
+ * over relevant state to it.
+ */
+ subgrp = update_subgroup_create(updgrp);
+ update_subgroup_inherit_info(subgrp, old_subgrp);
+
+ subgrp->split_from.update_group_id = old_subgrp->update_group->id;
+ subgrp->split_from.subgroup_id = old_subgrp->id;
+
+ /*
+ * Copy out relevant state from the old subgroup.
+ */
+ update_subgroup_copy_adj_out(paf->subgroup, subgrp);
+ update_subgroup_copy_packets(subgrp, paf->next_pkt_to_send);
+
+ if (BGP_DEBUG(update_groups, UPDATE_GROUPS))
+ zlog_debug("u%" PRIu64 ":s%" PRIu64" peer %s split and moved into u%" PRIu64":s%" PRIu64,
+ paf->subgroup->update_group->id, paf->subgroup->id,
+ paf->peer->host, updgrp->id, subgrp->id);
+
+ SUBGRP_INCR_STAT(paf->subgroup, split_events);
+
+ /*
+ * Since queued advs were left behind, this new subgroup needs a
+ * refresh.
+ */
+ update_subgroup_set_needs_refresh(subgrp, 1);
+
+ /*
+ * Remove peer from old subgroup, and add it to the new one.
+ */
+ update_subgroup_remove_peer(paf->subgroup, paf);
+
+ update_subgroup_add_peer(subgrp, paf, 1);
+}
+
+void update_bgp_group_init(struct bgp *bgp)
+{
+ int afid;
+
+ AF_FOREACH (afid)
+ bgp->update_groups[afid] =
+ hash_create(updgrp_hash_key_make, updgrp_hash_cmp,
+ "BGP Update Group Hash");
+}
+
+void update_bgp_group_free(struct bgp *bgp)
+{
+ int afid;
+
+ AF_FOREACH (afid) {
+ if (bgp->update_groups[afid]) {
+ hash_free(bgp->update_groups[afid]);
+ bgp->update_groups[afid] = NULL;
+ }
+ }
+}
+
+void update_group_show(struct bgp *bgp, afi_t afi, safi_t safi, struct vty *vty,
+ uint64_t subgrp_id)
+{
+ struct updwalk_context ctx;
+ memset(&ctx, 0, sizeof(ctx));
+ ctx.vty = vty;
+ ctx.subgrp_id = subgrp_id;
+
+ update_group_af_walk(bgp, afi, safi, update_group_show_walkcb, &ctx);
+}
+
+/*
+ * update_group_show_stats
+ *
+ * Show global statistics about update groups.
+ */
+void update_group_show_stats(struct bgp *bgp, struct vty *vty)
+{
+ vty_out(vty, "Update groups created: %u\n",
+ bgp->update_group_stats.updgrps_created);
+ vty_out(vty, "Update groups deleted: %u\n",
+ bgp->update_group_stats.updgrps_deleted);
+ vty_out(vty, "Update subgroups created: %u\n",
+ bgp->update_group_stats.subgrps_created);
+ vty_out(vty, "Update subgroups deleted: %u\n",
+ bgp->update_group_stats.subgrps_deleted);
+ vty_out(vty, "Join events: %u\n", bgp->update_group_stats.join_events);
+ vty_out(vty, "Prune events: %u\n",
+ bgp->update_group_stats.prune_events);
+ vty_out(vty, "Merge events: %u\n",
+ bgp->update_group_stats.merge_events);
+ vty_out(vty, "Split events: %u\n",
+ bgp->update_group_stats.split_events);
+ vty_out(vty, "Update group switch events: %u\n",
+ bgp->update_group_stats.updgrp_switch_events);
+ vty_out(vty, "Peer route refreshes combined: %u\n",
+ bgp->update_group_stats.peer_refreshes_combined);
+ vty_out(vty, "Merge checks triggered: %u\n",
+ bgp->update_group_stats.merge_checks_triggered);
+}
+
+/*
+ * update_group_adjust_peer
+ */
+void update_group_adjust_peer(struct peer_af *paf)
+{
+ struct update_group *updgrp;
+ struct update_subgroup *subgrp, *old_subgrp;
+ struct peer *peer;
+
+ if (!paf)
+ return;
+
+ peer = PAF_PEER(paf);
+ if (!peer_established(peer)) {
+ return;
+ }
+
+ if (!CHECK_FLAG(peer->flags, PEER_FLAG_CONFIG_NODE)) {
+ return;
+ }
+
+ if (!peer->afc_nego[paf->afi][paf->safi]) {
+ return;
+ }
+
+ updgrp = update_group_find(paf);
+ if (!updgrp) {
+ updgrp = update_group_create(paf);
+ if (!updgrp) {
+ flog_err(EC_BGP_UPDGRP_CREATE,
+ "couldn't create update group for peer %s",
+ paf->peer->host);
+ return;
+ }
+ }
+
+ old_subgrp = paf->subgroup;
+
+ if (old_subgrp) {
+
+ /*
+ * If the update group of the peer is unchanged, the peer can
+ * stay
+ * in its existing subgroup and we're done.
+ */
+ if (old_subgrp->update_group == updgrp)
+ return;
+
+ /*
+ * The peer is switching between update groups. Put it in its
+ * own subgroup under the new update group.
+ */
+ update_subgroup_split_peer(paf, updgrp);
+ return;
+ }
+
+ subgrp = update_subgroup_find(updgrp, paf);
+ if (!subgrp) {
+ subgrp = update_subgroup_create(updgrp);
+ if (!subgrp)
+ return;
+ }
+
+ update_subgroup_add_peer(subgrp, paf, 1);
+ if (BGP_DEBUG(update_groups, UPDATE_GROUPS))
+ zlog_debug("u%" PRIu64 ":s%" PRIu64 " add peer %s", updgrp->id,
+ subgrp->id, paf->peer->host);
+
+ return;
+}
+
+int update_group_adjust_soloness(struct peer *peer, int set)
+{
+ struct peer_group *group;
+ struct listnode *node, *nnode;
+
+ if (!CHECK_FLAG(peer->sflags, PEER_STATUS_GROUP)) {
+ peer_lonesoul_or_not(peer, set);
+ if (peer_established(peer))
+ bgp_announce_route_all(peer);
+ } else {
+ group = peer->group;
+ for (ALL_LIST_ELEMENTS(group->peer, node, nnode, peer)) {
+ peer_lonesoul_or_not(peer, set);
+ if (peer_established(peer))
+ bgp_announce_route_all(peer);
+ }
+ }
+ return 0;
+}
+
+/*
+ * update_subgroup_rib
+ */
+struct bgp_table *update_subgroup_rib(struct update_subgroup *subgrp)
+{
+ struct bgp *bgp;
+
+ bgp = SUBGRP_INST(subgrp);
+ if (!bgp)
+ return NULL;
+
+ return bgp->rib[SUBGRP_AFI(subgrp)][SUBGRP_SAFI(subgrp)];
+}
+
+void update_group_af_walk(struct bgp *bgp, afi_t afi, safi_t safi,
+ updgrp_walkcb cb, void *ctx)
+{
+ struct updwalk_context wctx;
+ int afid;
+
+ if (!bgp)
+ return;
+ afid = afindex(afi, safi);
+ if (afid >= BGP_AF_MAX)
+ return;
+
+ memset(&wctx, 0, sizeof(wctx));
+ wctx.cb = cb;
+ wctx.context = ctx;
+
+ if (bgp->update_groups[afid])
+ hash_walk(bgp->update_groups[afid], update_group_walkcb, &wctx);
+}
+
+void update_group_walk(struct bgp *bgp, updgrp_walkcb cb, void *ctx)
+{
+ afi_t afi;
+ safi_t safi;
+
+ FOREACH_AFI_SAFI (afi, safi) {
+ update_group_af_walk(bgp, afi, safi, cb, ctx);
+ }
+}
+
+void update_group_periodic_merge(struct bgp *bgp)
+{
+ char reason[] = "periodic merge check";
+
+ update_group_walk(bgp, update_group_periodic_merge_walkcb,
+ (void *)reason);
+}
+
+static int
+update_group_default_originate_route_map_walkcb(struct update_group *updgrp,
+ void *arg)
+{
+ struct update_subgroup *subgrp;
+ struct peer *peer;
+ afi_t afi;
+ safi_t safi;
+
+ UPDGRP_FOREACH_SUBGRP (updgrp, subgrp) {
+ peer = SUBGRP_PEER(subgrp);
+ afi = SUBGRP_AFI(subgrp);
+ safi = SUBGRP_SAFI(subgrp);
+
+ if (peer->default_rmap[afi][safi].name) {
+ /*
+ * When there is change in routemap this flow will
+ * be triggered. We need to unset the Flag to ensure
+ * the update flow gets triggered.
+ */
+ UNSET_FLAG(subgrp->sflags,
+ SUBGRP_STATUS_DEFAULT_ORIGINATE);
+ subgroup_default_originate(subgrp, 0);
+ }
+ }
+
+ return UPDWALK_CONTINUE;
+}
+
+void update_group_refresh_default_originate_route_map(struct thread *thread)
+{
+ struct bgp *bgp;
+ char reason[] = "refresh default-originate route-map";
+
+ bgp = THREAD_ARG(thread);
+ update_group_walk(bgp, update_group_default_originate_route_map_walkcb,
+ reason);
+ THREAD_OFF(bgp->t_rmap_def_originate_eval);
+ bgp_unlock(bgp);
+}
+
+/*
+ * peer_af_announce_route
+ *
+ * Refreshes routes out to a peer_af immediately.
+ *
+ * If the combine parameter is true, then this function will try to
+ * gather other peers in the subgroup for which a route announcement
+ * is pending and efficently announce routes to all of them.
+ *
+ * For now, the 'combine' option has an effect only if all peers in
+ * the subgroup have a route announcement pending.
+ */
+void peer_af_announce_route(struct peer_af *paf, int combine)
+{
+ struct update_subgroup *subgrp;
+ struct peer_af *cur_paf;
+ int all_pending;
+
+ subgrp = paf->subgroup;
+ all_pending = 0;
+
+ if (combine) {
+ /*
+ * If there are other peers in the old subgroup that also need
+ * routes to be announced, pull them into the peer's new
+ * subgroup.
+ * Combine route announcement with other peers if possible.
+ *
+ * For now, we combine only if all peers in the subgroup have an
+ * announcement pending.
+ */
+ all_pending = 1;
+
+ SUBGRP_FOREACH_PEER (subgrp, cur_paf) {
+ if (cur_paf == paf)
+ continue;
+
+ if (cur_paf->t_announce_route)
+ continue;
+
+ all_pending = 0;
+ break;
+ }
+ }
+ /*
+ * Announce to the peer alone if we were not asked to combine peers,
+ * or if some peers don't have a route annoucement pending.
+ */
+ if (!combine || !all_pending) {
+ update_subgroup_split_peer(paf, NULL);
+ subgrp = paf->subgroup;
+
+ assert(subgrp && subgrp->update_group);
+ if (bgp_debug_update(paf->peer, NULL, subgrp->update_group, 0))
+ zlog_debug("u%" PRIu64 ":s%" PRIu64" %s announcing routes",
+ subgrp->update_group->id, subgrp->id,
+ paf->peer->host);
+
+ subgroup_announce_route(paf->subgroup);
+ return;
+ }
+
+ /*
+ * We will announce routes the entire subgroup.
+ *
+ * First stop refresh timers on all the other peers.
+ */
+ SUBGRP_FOREACH_PEER (subgrp, cur_paf) {
+ if (cur_paf == paf)
+ continue;
+
+ bgp_stop_announce_route_timer(cur_paf);
+ }
+
+ if (bgp_debug_update(paf->peer, NULL, subgrp->update_group, 0))
+ zlog_debug("u%" PRIu64 ":s%" PRIu64" announcing routes to %s, combined into %d peers",
+ subgrp->update_group->id, subgrp->id,
+ paf->peer->host, subgrp->peer_count);
+
+ subgroup_announce_route(subgrp);
+
+ SUBGRP_INCR_STAT_BY(subgrp, peer_refreshes_combined,
+ subgrp->peer_count - 1);
+}
+
+void subgroup_trigger_write(struct update_subgroup *subgrp)
+{
+ struct peer_af *paf;
+
+ /*
+ * For each peer in the subgroup, schedule a job to pull packets from
+ * the subgroup output queue into their own output queue. This action
+ * will trigger a write job on the I/O thread.
+ */
+ SUBGRP_FOREACH_PEER (subgrp, paf)
+ if (peer_established(paf->peer))
+ thread_add_timer_msec(
+ bm->master, bgp_generate_updgrp_packets,
+ paf->peer, 0,
+ &paf->peer->t_generate_updgrp_packets);
+}
+
+int update_group_clear_update_dbg(struct update_group *updgrp, void *arg)
+{
+ UPDGRP_PEER_DBG_OFF(updgrp);
+ return UPDWALK_CONTINUE;
+}
+
+/* Return true if we should addpath encode NLRI to this peer */
+bool bgp_addpath_encode_tx(struct peer *peer, afi_t afi, safi_t safi)
+{
+ return (CHECK_FLAG(peer->af_cap[afi][safi], PEER_CAP_ADDPATH_AF_TX_ADV)
+ && CHECK_FLAG(peer->af_cap[afi][safi],
+ PEER_CAP_ADDPATH_AF_RX_RCV));
+}
+
+bool bgp_check_selected(struct bgp_path_info *bpi, struct peer *peer,
+ bool addpath_capable, afi_t afi, safi_t safi)
+{
+ return (CHECK_FLAG(bpi->flags, BGP_PATH_SELECTED) ||
+ (addpath_capable &&
+ bgp_addpath_tx_path(peer->addpath_type[afi][safi], bpi)));
+}
diff --git a/bgpd/bgp_updgrp.h b/bgpd/bgp_updgrp.h
new file mode 100644
index 0000000..56289d3
--- /dev/null
+++ b/bgpd/bgp_updgrp.h
@@ -0,0 +1,606 @@
+/**
+ * bgp_updgrp.c: BGP update group structures
+ *
+ * @copyright Copyright (C) 2014 Cumulus Networks, Inc.
+ *
+ * @author Avneesh Sachdev <avneesh@sproute.net>
+ * @author Rajesh Varadarajan <rajesh@sproute.net>
+ * @author Pradosh Mohapatra <pradosh@sproute.net>
+ *
+ * This file is part of GNU Zebra.
+ *
+ * GNU Zebra is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License as published by the
+ * Free Software Foundation; either version 2, or (at your option) any
+ * later version.
+ *
+ * GNU Zebra is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program; see the file COPYING; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
+ */
+
+#ifndef _QUAGGA_BGP_UPDGRP_H
+#define _QUAGGA_BGP_UPDGRP_H
+
+#include "bgp_advertise.h"
+
+/*
+ * The following three heuristic constants determine how long advertisement to
+ * a subgroup will be delayed after it is created. The intent is to allow
+ * transient changes in peer state (primarily session establishment) to settle,
+ * so that more peers can be grouped together and benefit from sharing
+ * advertisement computations with the subgroup.
+ *
+ * These values have a very large impact on initial convergence time; any
+ * changes should be accompanied by careful performance testing at all scales.
+ *
+ * The coalesce time 'C' for a new subgroup within a particular BGP instance
+ * 'B' with total number of known peers 'P', established or not, is computed as
+ * follows:
+ *
+ * C = MIN(BGP_MAX_SUBGROUP_COALESCE_TIME,
+ * BGP_DEFAULT_SUBGROUP_COALESCE_TIME +
+ * (P*BGP_PEER_ADJUST_SUBGROUP_COALESCE_TIME))
+ */
+#define BGP_DEFAULT_SUBGROUP_COALESCE_TIME 1000
+#define BGP_MAX_SUBGROUP_COALESCE_TIME 10000
+#define BGP_PEER_ADJUST_SUBGROUP_COALESCE_TIME 50
+
+#define PEER_UPDGRP_FLAGS \
+ (PEER_FLAG_LOCAL_AS_NO_PREPEND | PEER_FLAG_LOCAL_AS_REPLACE_AS)
+
+#define PEER_UPDGRP_AF_FLAGS \
+ (PEER_FLAG_SEND_COMMUNITY | PEER_FLAG_SEND_EXT_COMMUNITY \
+ | PEER_FLAG_SEND_LARGE_COMMUNITY \
+ | PEER_FLAG_DEFAULT_ORIGINATE | PEER_FLAG_REFLECTOR_CLIENT \
+ | PEER_FLAG_RSERVER_CLIENT | PEER_FLAG_NEXTHOP_SELF \
+ | PEER_FLAG_NEXTHOP_UNCHANGED | PEER_FLAG_FORCE_NEXTHOP_SELF \
+ | PEER_FLAG_AS_PATH_UNCHANGED | PEER_FLAG_MED_UNCHANGED \
+ | PEER_FLAG_NEXTHOP_LOCAL_UNCHANGED | PEER_FLAG_REMOVE_PRIVATE_AS \
+ | PEER_FLAG_REMOVE_PRIVATE_AS_ALL \
+ | PEER_FLAG_REMOVE_PRIVATE_AS_REPLACE \
+ | PEER_FLAG_REMOVE_PRIVATE_AS_ALL_REPLACE \
+ | PEER_FLAG_AS_OVERRIDE)
+
+#define PEER_UPDGRP_CAP_FLAGS (PEER_CAP_AS4_RCV)
+
+#define PEER_UPDGRP_AF_CAP_FLAGS \
+ (PEER_CAP_ORF_PREFIX_SM_RCV | PEER_CAP_ORF_PREFIX_SM_OLD_RCV \
+ | PEER_CAP_ADDPATH_AF_TX_ADV | PEER_CAP_ADDPATH_AF_RX_RCV \
+ | PEER_CAP_ENHE_AF_NEGO)
+
+enum bpacket_attr_vec_type { BGP_ATTR_VEC_NH = 0, BGP_ATTR_VEC_MAX };
+
+typedef struct {
+ uint32_t flags;
+ unsigned long offset;
+} bpacket_attr_vec;
+
+#define BPKT_ATTRVEC_FLAGS_UPDATED (1 << 0)
+#define BPKT_ATTRVEC_FLAGS_RMAP_NH_PEER_ADDRESS (1 << 1)
+#define BPKT_ATTRVEC_FLAGS_REFLECTED (1 << 2)
+#define BPKT_ATTRVEC_FLAGS_RMAP_NH_UNCHANGED (1 << 3)
+#define BPKT_ATTRVEC_FLAGS_RMAP_IPV4_NH_CHANGED (1 << 4)
+#define BPKT_ATTRVEC_FLAGS_RMAP_IPV6_GNH_CHANGED (1 << 5)
+#define BPKT_ATTRVEC_FLAGS_RMAP_IPV6_LNH_CHANGED (1 << 6)
+
+typedef struct bpacket_attr_vec_arr {
+ bpacket_attr_vec entries[BGP_ATTR_VEC_MAX];
+} bpacket_attr_vec_arr;
+
+struct bpacket {
+ /* for being part of an update subgroup's message list */
+ TAILQ_ENTRY(bpacket) pkt_train;
+
+ /* list of peers (well, peer_afs) that the packet needs to be sent to */
+ LIST_HEAD(pkt_peer_list, peer_af) peers;
+
+ struct stream *buffer;
+ bpacket_attr_vec_arr arr;
+
+ unsigned int ver;
+};
+
+struct bpacket_queue {
+ TAILQ_HEAD(pkt_queue, bpacket) pkts;
+
+ unsigned int conf_max_count;
+ unsigned int curr_count;
+ unsigned int hwm_count;
+ unsigned int max_count_reached_count;
+};
+
+struct update_group {
+ /* back pointer to the BGP instance */
+ struct bgp *bgp;
+
+ /* list of subgroups that belong to the update group */
+ LIST_HEAD(subgrp_list, update_subgroup) subgrps;
+
+ /* lazy way to store configuration common to all peers
+ hash function will compute from this data */
+ struct peer *conf;
+
+ afi_t afi;
+ safi_t safi;
+ int afid;
+
+ uint64_t id;
+ time_t uptime;
+
+ uint32_t join_events;
+ uint32_t prune_events;
+ uint32_t merge_events;
+ uint32_t updgrp_switch_events;
+ uint32_t peer_refreshes_combined;
+ uint32_t adj_count;
+ uint32_t split_events;
+ uint32_t merge_checks_triggered;
+
+ uint32_t subgrps_created;
+ uint32_t subgrps_deleted;
+
+ uint32_t num_dbg_en_peers;
+};
+
+/*
+ * Shorthand for a global statistics counter.
+ */
+#define UPDGRP_GLOBAL_STAT(updgrp, stat) \
+ ((updgrp)->bgp->update_group_stats.stat)
+
+/*
+ * Add the given value to a counter on an update group and the bgp
+ * instance.
+ */
+#define UPDGRP_INCR_STAT_BY(updgrp, stat, value) \
+ do { \
+ (updgrp)->stat += (value); \
+ UPDGRP_GLOBAL_STAT(updgrp, stat) += (value); \
+ } while (0)
+
+/*
+ * Increment a counter on a update group and its parent structures.
+ */
+#define UPDGRP_INCR_STAT(subgrp, stat) UPDGRP_INCR_STAT_BY(subgrp, stat, 1)
+
+struct update_subgroup {
+ /* back pointer to the parent update group */
+ struct update_group *update_group;
+
+ /* list of peers that belong to the subgroup */
+ LIST_HEAD(peer_list, peer_af) peers;
+ int peer_count;
+
+ /* for being part of an update group's subgroup list */
+ LIST_ENTRY(update_subgroup) updgrp_train;
+
+ struct bpacket_queue pkt_queue;
+
+ /*
+ * List of adj-out structures for this subgroup.
+ * It essentially represents the snapshot of every prefix that
+ * has been advertised to the members of the subgroup
+ */
+ TAILQ_HEAD(adjout_queue, bgp_adj_out) adjq;
+
+ /* packet buffer for update generation */
+ struct stream *work;
+
+ /* We use a separate stream to encode MP_REACH_NLRI for efficient
+ * NLRI packing. peer->obuf_work stores all the other attributes. The
+ * actual packet is then constructed by concatenating the two.
+ */
+ struct stream *scratch;
+
+ /* synchronization list and time */
+ struct bgp_synchronize *sync;
+
+ /* send prefix count */
+ uint32_t scount;
+
+ /* send prefix count prior to packet update */
+ uint32_t pscount;
+
+ /* announcement attribute hash */
+ struct hash *hash;
+
+ struct thread *t_coalesce;
+ uint32_t v_coalesce;
+
+ struct thread *t_merge_check;
+
+ /* table version that the subgroup has caught up to. */
+ uint64_t version;
+
+ /* version maintained to record adj changes */
+ uint64_t adj_version;
+
+ time_t uptime;
+
+ /*
+ * Identifying information about the subgroup that this subgroup was
+ * split
+ * from, if any.
+ */
+ struct {
+ uint64_t update_group_id;
+ uint64_t subgroup_id;
+ } split_from;
+
+ uint32_t join_events;
+ uint32_t prune_events;
+
+ /*
+ * This is bumped up when another subgroup merges into this one.
+ */
+ uint32_t merge_events;
+ uint32_t updgrp_switch_events;
+ uint32_t peer_refreshes_combined;
+ uint32_t adj_count;
+ uint32_t split_events;
+ uint32_t merge_checks_triggered;
+
+ uint64_t id;
+
+ uint16_t sflags;
+#define SUBGRP_STATUS_DEFAULT_ORIGINATE (1 << 0)
+#define SUBGRP_STATUS_FORCE_UPDATES (1 << 1)
+#define SUBGRP_STATUS_TABLE_REPARSING (1 << 2)
+/*
+ * This flag has been added to ensure that the SNT counters
+ * gets incremented and decremented only during the creation
+ * and deletion workflows of default originate,
+ * not during the update workflow.
+ */
+#define SUBGRP_STATUS_PEER_DEFAULT_ORIGINATED (1 << 3)
+
+ uint16_t flags;
+#define SUBGRP_FLAG_NEEDS_REFRESH (1 << 0)
+};
+
+/*
+ * Add the given value to the specified counter on a subgroup and its
+ * parent structures.
+ */
+#define SUBGRP_INCR_STAT_BY(subgrp, stat, value) \
+ do { \
+ (subgrp)->stat += (value); \
+ if ((subgrp)->update_group) \
+ UPDGRP_INCR_STAT_BY((subgrp)->update_group, stat, \
+ value); \
+ } while (0)
+
+/*
+ * Increment a counter on a subgroup and its parent structures.
+ */
+#define SUBGRP_INCR_STAT(subgrp, stat) SUBGRP_INCR_STAT_BY(subgrp, stat, 1)
+
+/*
+ * Decrement a counter on a subgroup and its parent structures.
+ */
+#define SUBGRP_DECR_STAT(subgrp, stat) SUBGRP_INCR_STAT_BY(subgrp, stat, -1)
+
+typedef int (*updgrp_walkcb)(struct update_group *updgrp, void *ctx);
+
+/* really a private structure */
+struct updwalk_context {
+ struct vty *vty;
+ struct bgp_dest *dest;
+ struct bgp_path_info *pi;
+ uint64_t updgrp_id;
+ uint64_t subgrp_id;
+ enum bgp_policy_type policy_type;
+ const char *policy_name;
+ int policy_event_start_flag;
+ bool policy_route_update;
+ updgrp_walkcb cb;
+ void *context;
+ uint8_t flags;
+
+#define UPDWALK_FLAGS_ADVQUEUE (1 << 0)
+#define UPDWALK_FLAGS_ADVERTISED (1 << 1)
+};
+
+#define UPDWALK_CONTINUE HASHWALK_CONTINUE
+#define UPDWALK_ABORT HASHWALK_ABORT
+
+#define PAF_PEER(p) ((p)->peer)
+#define PAF_SUBGRP(p) ((p)->subgroup)
+#define PAF_UPDGRP(p) ((p)->subgroup->update_group)
+#define PAF_PKTQ(f) SUBGRP_PKTQ((f)->subgroup)
+
+#define UPDGRP_PEER(u) ((u)->conf)
+#define UPDGRP_AFI(u) ((u)->afi)
+#define UPDGRP_SAFI(u) ((u)->safi)
+#define UPDGRP_INST(u) ((u)->bgp)
+#define UPDGRP_AFFLAGS(u) ((u)->conf->af_flags[UPDGRP_AFI(u)][UPDGRP_SAFI(u)])
+#define UPDGRP_DBG_ON(u) ((u)->num_dbg_en_peers)
+#define UPDGRP_PEER_DBG_EN(u) (((u)->num_dbg_en_peers)++)
+#define UPDGRP_PEER_DBG_DIS(u) (((u)->num_dbg_en_peers)--)
+#define UPDGRP_PEER_DBG_OFF(u) (u)->num_dbg_en_peers = 0
+
+#define SUBGRP_AFI(s) UPDGRP_AFI((s)->update_group)
+#define SUBGRP_SAFI(s) UPDGRP_SAFI((s)->update_group)
+#define SUBGRP_PEER(s) UPDGRP_PEER((s)->update_group)
+#define SUBGRP_PCOUNT(s) ((s)->peer_count)
+#define SUBGRP_PFIRST(s) LIST_FIRST(&((s)->peers))
+#define SUBGRP_PKTQ(s) &((s)->pkt_queue)
+#define SUBGRP_INST(s) UPDGRP_INST((s)->update_group)
+#define SUBGRP_AFFLAGS(s) UPDGRP_AFFLAGS((s)->update_group)
+#define SUBGRP_UPDGRP(s) ((s)->update_group)
+
+/*
+ * Walk all subgroups in an update group.
+ */
+#define UPDGRP_FOREACH_SUBGRP(updgrp, subgrp) \
+ LIST_FOREACH (subgrp, &((updgrp)->subgrps), updgrp_train)
+
+#define UPDGRP_FOREACH_SUBGRP_SAFE(updgrp, subgrp, tmp_subgrp) \
+ LIST_FOREACH_SAFE (subgrp, &((updgrp)->subgrps), updgrp_train, \
+ tmp_subgrp)
+
+#define SUBGRP_FOREACH_PEER(subgrp, paf) \
+ LIST_FOREACH (paf, &(subgrp->peers), subgrp_train)
+
+#define SUBGRP_FOREACH_PEER_SAFE(subgrp, paf, temp_paf) \
+ LIST_FOREACH_SAFE (paf, &(subgrp->peers), subgrp_train, temp_paf)
+
+#define SUBGRP_FOREACH_ADJ(subgrp, adj) \
+ TAILQ_FOREACH (adj, &(subgrp->adjq), subgrp_adj_train)
+
+#define SUBGRP_FOREACH_ADJ_SAFE(subgrp, adj, adj_temp) \
+ TAILQ_FOREACH_SAFE (adj, &(subgrp->adjq), subgrp_adj_train, adj_temp)
+
+/* Prototypes. */
+/* bgp_updgrp.c */
+extern void update_bgp_group_init(struct bgp *);
+extern void udpate_bgp_group_free(struct bgp *);
+
+extern void update_group_show(struct bgp *bgp, afi_t afi, safi_t safi,
+ struct vty *vty, uint64_t subgrp_id);
+extern void update_group_show_stats(struct bgp *bgp, struct vty *vty);
+extern void update_group_adjust_peer(struct peer_af *paf);
+extern int update_group_adjust_soloness(struct peer *peer, int set);
+
+extern void update_subgroup_remove_peer(struct update_subgroup *,
+ struct peer_af *);
+extern struct bgp_table *update_subgroup_rib(struct update_subgroup *);
+extern void update_subgroup_split_peer(struct peer_af *, struct update_group *);
+extern bool update_subgroup_check_merge(struct update_subgroup *, const char *);
+extern bool update_subgroup_trigger_merge_check(struct update_subgroup *,
+ int force);
+extern void update_group_policy_update(struct bgp *bgp,
+ enum bgp_policy_type ptype,
+ const char *pname, bool route_update,
+ int start_event);
+extern void update_group_af_walk(struct bgp *bgp, afi_t afi, safi_t safi,
+ updgrp_walkcb cb, void *ctx);
+extern void update_group_walk(struct bgp *bgp, updgrp_walkcb cb, void *ctx);
+extern void update_group_periodic_merge(struct bgp *bgp);
+extern void
+update_group_refresh_default_originate_route_map(struct thread *thread);
+extern void update_group_start_advtimer(struct bgp *bgp);
+
+extern void update_subgroup_inherit_info(struct update_subgroup *to,
+ struct update_subgroup *from);
+
+/* bgp_updgrp_packet.c */
+extern struct bpacket *bpacket_alloc(void);
+extern void bpacket_free(struct bpacket *pkt);
+extern void bpacket_queue_init(struct bpacket_queue *q);
+extern void bpacket_queue_cleanup(struct bpacket_queue *q);
+extern void bpacket_queue_sanity_check(struct bpacket_queue *q);
+extern struct bpacket *bpacket_queue_add(struct bpacket_queue *q,
+ struct stream *s,
+ struct bpacket_attr_vec_arr *vecarr);
+struct bpacket *bpacket_queue_remove(struct bpacket_queue *q);
+extern struct bpacket *bpacket_queue_first(struct bpacket_queue *q);
+struct bpacket *bpacket_queue_last(struct bpacket_queue *q);
+unsigned int bpacket_queue_length(struct bpacket_queue *q);
+unsigned int bpacket_queue_hwm_length(struct bpacket_queue *q);
+bool bpacket_queue_is_full(struct bgp *bgp, struct bpacket_queue *q);
+extern void bpacket_queue_advance_peer(struct peer_af *paf);
+extern void bpacket_queue_remove_peer(struct peer_af *paf);
+extern void bpacket_add_peer(struct bpacket *pkt, struct peer_af *paf);
+unsigned int bpacket_queue_virtual_length(struct peer_af *paf);
+extern void bpacket_queue_show_vty(struct bpacket_queue *q, struct vty *vty);
+bool subgroup_packets_to_build(struct update_subgroup *subgrp);
+extern struct bpacket *subgroup_update_packet(struct update_subgroup *s);
+extern struct bpacket *subgroup_withdraw_packet(struct update_subgroup *s);
+extern struct stream *bpacket_reformat_for_peer(struct bpacket *pkt,
+ struct peer_af *paf);
+extern void bpacket_attr_vec_arr_reset(struct bpacket_attr_vec_arr *vecarr);
+extern void bpacket_attr_vec_arr_set_vec(struct bpacket_attr_vec_arr *vecarr,
+ enum bpacket_attr_vec_type type,
+ struct stream *s, struct attr *attr);
+extern void subgroup_default_update_packet(struct update_subgroup *subgrp,
+ struct attr *attr,
+ struct peer *from);
+extern void subgroup_default_withdraw_packet(struct update_subgroup *subgrp);
+
+/* bgp_updgrp_adv.c */
+extern struct bgp_advertise *
+bgp_advertise_clean_subgroup(struct update_subgroup *subgrp,
+ struct bgp_adj_out *adj);
+extern void update_group_show_adj_queue(struct bgp *bgp, afi_t afi, safi_t safi,
+ struct vty *vty, uint64_t id);
+extern void update_group_show_advertised(struct bgp *bgp, afi_t afi,
+ safi_t safi, struct vty *vty,
+ uint64_t id);
+extern void update_group_show_packet_queue(struct bgp *bgp, afi_t afi,
+ safi_t safi, struct vty *vty,
+ uint64_t id);
+extern void subgroup_announce_route(struct update_subgroup *subgrp);
+extern void subgroup_announce_all(struct update_subgroup *subgrp);
+
+extern void subgroup_default_originate(struct update_subgroup *subgrp,
+ int withdraw);
+extern void group_announce_route(struct bgp *bgp, afi_t afi, safi_t safi,
+ struct bgp_dest *dest,
+ struct bgp_path_info *pi);
+extern void subgroup_clear_table(struct update_subgroup *subgrp);
+extern void update_group_announce(struct bgp *bgp);
+extern void update_group_announce_rrclients(struct bgp *bgp);
+extern void peer_af_announce_route(struct peer_af *paf, int combine);
+extern struct bgp_adj_out *bgp_adj_out_alloc(struct update_subgroup *subgrp,
+ struct bgp_dest *dest,
+ uint32_t addpath_tx_id);
+extern void bgp_adj_out_remove_subgroup(struct bgp_dest *dest,
+ struct bgp_adj_out *adj,
+ struct update_subgroup *subgrp);
+extern void bgp_adj_out_set_subgroup(struct bgp_dest *dest,
+ struct update_subgroup *subgrp,
+ struct attr *attr,
+ struct bgp_path_info *path);
+extern void bgp_adj_out_unset_subgroup(struct bgp_dest *dest,
+ struct update_subgroup *subgrp,
+ char withdraw, uint32_t addpath_tx_id);
+void subgroup_announce_table(struct update_subgroup *subgrp,
+ struct bgp_table *table);
+extern void subgroup_trigger_write(struct update_subgroup *subgrp);
+
+extern int update_group_clear_update_dbg(struct update_group *updgrp,
+ void *arg);
+
+extern void update_bgp_group_free(struct bgp *bgp);
+extern bool bgp_addpath_encode_tx(struct peer *peer, afi_t afi, safi_t safi);
+extern bool bgp_check_selected(struct bgp_path_info *bpi, struct peer *peer,
+ bool addpath_capable, afi_t afi, safi_t safi);
+
+/*
+ * Inline functions
+ */
+
+/*
+ * bpacket_queue_is_empty
+ */
+static inline int bpacket_queue_is_empty(struct bpacket_queue *queue)
+{
+
+ /*
+ * The packet queue is empty if it only contains a sentinel.
+ */
+ if (queue->curr_count != 1)
+ return 0;
+
+ assert(bpacket_queue_first(queue)->buffer == NULL);
+ return 1;
+}
+
+/*
+ * bpacket_next
+ *
+ * Returns the packet after the given packet in a bpacket queue.
+ */
+static inline struct bpacket *bpacket_next(struct bpacket *pkt)
+{
+ return TAILQ_NEXT(pkt, pkt_train);
+}
+
+/*
+ * update_group_adjust_peer_afs
+ *
+ * Adjust all peer_af structures for the given peer.
+ */
+static inline void update_group_adjust_peer_afs(struct peer *peer)
+{
+ struct peer_af *paf;
+ int afidx;
+
+ for (afidx = BGP_AF_START; afidx < BGP_AF_MAX; afidx++) {
+ paf = peer->peer_af_array[afidx];
+ if (paf != NULL)
+ update_group_adjust_peer(paf);
+ }
+}
+
+/*
+ * update_group_remove_peer_afs
+ *
+ * Remove all peer_af structures for the given peer from their subgroups.
+ */
+static inline void update_group_remove_peer_afs(struct peer *peer)
+{
+ struct peer_af *paf;
+ int afidx;
+
+ for (afidx = BGP_AF_START; afidx < BGP_AF_MAX; afidx++) {
+ paf = peer->peer_af_array[afidx];
+ if (paf != NULL)
+ update_subgroup_remove_peer(PAF_SUBGRP(paf), paf);
+ }
+}
+
+/*
+ * update_subgroup_needs_refresh
+ */
+static inline int
+update_subgroup_needs_refresh(const struct update_subgroup *subgrp)
+{
+ if (CHECK_FLAG(subgrp->flags, SUBGRP_FLAG_NEEDS_REFRESH))
+ return 1;
+ else
+ return 0;
+}
+
+/*
+ * update_subgroup_set_needs_refresh
+ */
+static inline void
+update_subgroup_set_needs_refresh(struct update_subgroup *subgrp, int value)
+{
+ if (value)
+ SET_FLAG(subgrp->flags, SUBGRP_FLAG_NEEDS_REFRESH);
+ else
+ UNSET_FLAG(subgrp->flags, SUBGRP_FLAG_NEEDS_REFRESH);
+}
+
+static inline struct update_subgroup *peer_subgroup(struct peer *peer,
+ afi_t afi, safi_t safi)
+{
+ struct peer_af *paf;
+
+ paf = peer_af_find(peer, afi, safi);
+ if (paf)
+ return PAF_SUBGRP(paf);
+ return NULL;
+}
+
+/*
+ * update_group_adjust_peer_afs
+ *
+ * Adjust all peer_af structures for the given peer.
+ */
+static inline void bgp_announce_peer(struct peer *peer)
+{
+ struct peer_af *paf;
+ int afidx;
+
+ for (afidx = BGP_AF_START; afidx < BGP_AF_MAX; afidx++) {
+ paf = peer->peer_af_array[afidx];
+ if (paf != NULL)
+ subgroup_announce_all(PAF_SUBGRP(paf));
+ }
+}
+
+/**
+ * advertise_list_is_empty
+ */
+static inline int advertise_list_is_empty(struct update_subgroup *subgrp)
+{
+ if (bgp_adv_fifo_count(&subgrp->sync->update)
+ || bgp_adv_fifo_count(&subgrp->sync->withdraw)
+ || bgp_adv_fifo_count(&subgrp->sync->withdraw_low)) {
+ return 0;
+ }
+
+ return 1;
+}
+
+#endif /* _QUAGGA_BGP_UPDGRP_H */
diff --git a/bgpd/bgp_updgrp_adv.c b/bgpd/bgp_updgrp_adv.c
new file mode 100644
index 0000000..b301899
--- /dev/null
+++ b/bgpd/bgp_updgrp_adv.c
@@ -0,0 +1,1066 @@
+/**
+ * bgp_updgrp_adv.c: BGP update group advertisement and adjacency
+ * maintenance
+ *
+ *
+ * @copyright Copyright (C) 2014 Cumulus Networks, Inc.
+ *
+ * @author Avneesh Sachdev <avneesh@sproute.net>
+ * @author Rajesh Varadarajan <rajesh@sproute.net>
+ * @author Pradosh Mohapatra <pradosh@sproute.net>
+ *
+ * This file is part of GNU Zebra.
+ *
+ * GNU Zebra is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License as published by the
+ * Free Software Foundation; either version 2, or (at your option) any
+ * later version.
+ *
+ * GNU Zebra is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program; see the file COPYING; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
+ */
+
+#include <zebra.h>
+
+#include "command.h"
+#include "memory.h"
+#include "prefix.h"
+#include "hash.h"
+#include "thread.h"
+#include "queue.h"
+#include "routemap.h"
+#include "filter.h"
+
+#include "bgpd/bgpd.h"
+#include "bgpd/bgp_table.h"
+#include "bgpd/bgp_debug.h"
+#include "bgpd/bgp_route.h"
+#include "bgpd/bgp_advertise.h"
+#include "bgpd/bgp_attr.h"
+#include "bgpd/bgp_aspath.h"
+#include "bgpd/bgp_packet.h"
+#include "bgpd/bgp_fsm.h"
+#include "bgpd/bgp_mplsvpn.h"
+#include "bgpd/bgp_updgrp.h"
+#include "bgpd/bgp_advertise.h"
+#include "bgpd/bgp_addpath.h"
+
+
+/********************
+ * PRIVATE FUNCTIONS
+ ********************/
+static int bgp_adj_out_compare(const struct bgp_adj_out *o1,
+ const struct bgp_adj_out *o2)
+{
+ if (o1->subgroup < o2->subgroup)
+ return -1;
+
+ if (o1->subgroup > o2->subgroup)
+ return 1;
+
+ if (o1->addpath_tx_id < o2->addpath_tx_id)
+ return -1;
+
+ if (o1->addpath_tx_id > o2->addpath_tx_id)
+ return 1;
+
+ return 0;
+}
+RB_GENERATE(bgp_adj_out_rb, bgp_adj_out, adj_entry, bgp_adj_out_compare);
+
+static inline struct bgp_adj_out *adj_lookup(struct bgp_dest *dest,
+ struct update_subgroup *subgrp,
+ uint32_t addpath_tx_id)
+{
+ struct bgp_adj_out lookup;
+
+ if (!dest || !subgrp)
+ return NULL;
+
+ /* update-groups that do not support addpath will pass 0 for
+ * addpath_tx_id. */
+ lookup.subgroup = subgrp;
+ lookup.addpath_tx_id = addpath_tx_id;
+
+ return RB_FIND(bgp_adj_out_rb, &dest->adj_out, &lookup);
+}
+
+static void adj_free(struct bgp_adj_out *adj)
+{
+ TAILQ_REMOVE(&(adj->subgroup->adjq), adj, subgrp_adj_train);
+ SUBGRP_DECR_STAT(adj->subgroup, adj_count);
+
+ RB_REMOVE(bgp_adj_out_rb, &adj->dest->adj_out, adj);
+ bgp_dest_unlock_node(adj->dest);
+
+ XFREE(MTYPE_BGP_ADJ_OUT, adj);
+}
+
+static void subgrp_withdraw_stale_addpath(struct updwalk_context *ctx,
+ struct update_subgroup *subgrp)
+{
+ struct bgp_adj_out *adj, *adj_next;
+ uint32_t id;
+ struct bgp_path_info *pi;
+ afi_t afi = SUBGRP_AFI(subgrp);
+ safi_t safi = SUBGRP_SAFI(subgrp);
+ struct peer *peer = SUBGRP_PEER(subgrp);
+
+ /* Look through all of the paths we have advertised for this rn and send
+ * a withdraw for the ones that are no longer present */
+ RB_FOREACH_SAFE (adj, bgp_adj_out_rb, &ctx->dest->adj_out, adj_next) {
+ if (adj->subgroup != subgrp)
+ continue;
+
+ for (pi = bgp_dest_get_bgp_path_info(ctx->dest); pi;
+ pi = pi->next) {
+ id = bgp_addpath_id_for_peer(peer, afi, safi,
+ &pi->tx_addpath);
+
+ if (id == adj->addpath_tx_id) {
+ break;
+ }
+ }
+
+ if (!pi) {
+ subgroup_process_announce_selected(
+ subgrp, NULL, ctx->dest, adj->addpath_tx_id);
+ }
+ }
+}
+
+static int group_announce_route_walkcb(struct update_group *updgrp, void *arg)
+{
+ struct updwalk_context *ctx = arg;
+ struct update_subgroup *subgrp;
+ struct bgp_path_info *pi;
+ afi_t afi;
+ safi_t safi;
+ struct peer *peer;
+ struct bgp_adj_out *adj, *adj_next;
+ bool addpath_capable;
+
+ afi = UPDGRP_AFI(updgrp);
+ safi = UPDGRP_SAFI(updgrp);
+ peer = UPDGRP_PEER(updgrp);
+ addpath_capable = bgp_addpath_encode_tx(peer, afi, safi);
+
+ if (BGP_DEBUG(update, UPDATE_OUT))
+ zlog_debug("%s: afi=%s, safi=%s, p=%pRN", __func__,
+ afi2str(afi), safi2str(safi),
+ bgp_dest_to_rnode(ctx->dest));
+
+ UPDGRP_FOREACH_SUBGRP (updgrp, subgrp) {
+
+ /*
+ * Skip the subgroups that have coalesce timer running. We will
+ * walk the entire prefix table for those subgroups when the
+ * coalesce timer fires.
+ */
+ if (!subgrp->t_coalesce) {
+
+ /* An update-group that uses addpath */
+ if (addpath_capable) {
+ subgrp_withdraw_stale_addpath(ctx, subgrp);
+
+ for (pi = bgp_dest_get_bgp_path_info(ctx->dest);
+ pi; pi = pi->next) {
+ /* Skip the bestpath for now */
+ if (pi == ctx->pi)
+ continue;
+
+ subgroup_process_announce_selected(
+ subgrp, pi, ctx->dest,
+ bgp_addpath_id_for_peer(
+ peer, afi, safi,
+ &pi->tx_addpath));
+ }
+
+ /* Process the bestpath last so the "show [ip]
+ * bgp neighbor x.x.x.x advertised"
+ * output shows the attributes from the bestpath
+ */
+ if (ctx->pi)
+ subgroup_process_announce_selected(
+ subgrp, ctx->pi, ctx->dest,
+ bgp_addpath_id_for_peer(
+ peer, afi, safi,
+ &ctx->pi->tx_addpath));
+ }
+ /* An update-group that does not use addpath */
+ else {
+ if (ctx->pi) {
+ subgroup_process_announce_selected(
+ subgrp, ctx->pi, ctx->dest,
+ bgp_addpath_id_for_peer(
+ peer, afi, safi,
+ &ctx->pi->tx_addpath));
+ } else {
+ /* Find the addpath_tx_id of the path we
+ * had advertised and
+ * send a withdraw */
+ RB_FOREACH_SAFE (adj, bgp_adj_out_rb,
+ &ctx->dest->adj_out,
+ adj_next) {
+ if (adj->subgroup == subgrp) {
+ subgroup_process_announce_selected(
+ subgrp, NULL,
+ ctx->dest,
+ adj->addpath_tx_id);
+ }
+ }
+ }
+ }
+ }
+
+ /* Notify BGP Conditional advertisement */
+ bgp_notify_conditional_adv_scanner(subgrp);
+ }
+
+ return UPDWALK_CONTINUE;
+}
+
+static void subgrp_show_adjq_vty(struct update_subgroup *subgrp,
+ struct vty *vty, uint8_t flags)
+{
+ struct bgp_table *table;
+ struct bgp_adj_out *adj;
+ unsigned long output_count;
+ struct bgp_dest *dest;
+ int header1 = 1;
+ struct bgp *bgp;
+ int header2 = 1;
+
+ bgp = SUBGRP_INST(subgrp);
+ if (!bgp)
+ return;
+
+ table = bgp->rib[SUBGRP_AFI(subgrp)][SUBGRP_SAFI(subgrp)];
+
+ output_count = 0;
+
+ for (dest = bgp_table_top(table); dest; dest = bgp_route_next(dest)) {
+ const struct prefix *dest_p = bgp_dest_get_prefix(dest);
+
+ RB_FOREACH (adj, bgp_adj_out_rb, &dest->adj_out) {
+ if (adj->subgroup != subgrp)
+ continue;
+
+ if (header1) {
+ vty_out(vty,
+ "BGP table version is %" PRIu64
+ ", local router ID is %pI4\n",
+ table->version, &bgp->router_id);
+ vty_out(vty, BGP_SHOW_SCODE_HEADER);
+ vty_out(vty, BGP_SHOW_OCODE_HEADER);
+ header1 = 0;
+ }
+ if (header2) {
+ vty_out(vty, BGP_SHOW_HEADER);
+ header2 = 0;
+ }
+ if ((flags & UPDWALK_FLAGS_ADVQUEUE) && adj->adv &&
+ adj->adv->baa) {
+ route_vty_out_tmp(
+ vty, dest, dest_p, adj->adv->baa->attr,
+ SUBGRP_SAFI(subgrp), 0, NULL, false);
+ output_count++;
+ }
+ if ((flags & UPDWALK_FLAGS_ADVERTISED) && adj->attr) {
+ route_vty_out_tmp(vty, dest, dest_p, adj->attr,
+ SUBGRP_SAFI(subgrp), 0, NULL,
+ false);
+ output_count++;
+ }
+ }
+ }
+ if (output_count != 0)
+ vty_out(vty, "\nTotal number of prefixes %ld\n", output_count);
+}
+
+static int updgrp_show_adj_walkcb(struct update_group *updgrp, void *arg)
+{
+ struct updwalk_context *ctx = arg;
+ struct update_subgroup *subgrp;
+ struct vty *vty;
+
+ vty = ctx->vty;
+ UPDGRP_FOREACH_SUBGRP (updgrp, subgrp) {
+ if (ctx->subgrp_id && (ctx->subgrp_id != subgrp->id))
+ continue;
+ vty_out(vty, "update group %" PRIu64 ", subgroup %" PRIu64 "\n",
+ updgrp->id, subgrp->id);
+ subgrp_show_adjq_vty(subgrp, vty, ctx->flags);
+ }
+ return UPDWALK_CONTINUE;
+}
+
+static void updgrp_show_adj(struct bgp *bgp, afi_t afi, safi_t safi,
+ struct vty *vty, uint64_t id, uint8_t flags)
+{
+ struct updwalk_context ctx;
+ memset(&ctx, 0, sizeof(ctx));
+ ctx.vty = vty;
+ ctx.subgrp_id = id;
+ ctx.flags = flags;
+
+ update_group_af_walk(bgp, afi, safi, updgrp_show_adj_walkcb, &ctx);
+}
+
+static void subgroup_coalesce_timer(struct thread *thread)
+{
+ struct update_subgroup *subgrp;
+ struct bgp *bgp;
+
+ subgrp = THREAD_ARG(thread);
+ if (bgp_debug_update(NULL, NULL, subgrp->update_group, 0))
+ zlog_debug("u%" PRIu64 ":s%" PRIu64" announcing routes upon coalesce timer expiry(%u ms)",
+ (SUBGRP_UPDGRP(subgrp))->id, subgrp->id,
+ subgrp->v_coalesce);
+ subgrp->t_coalesce = NULL;
+ subgrp->v_coalesce = 0;
+ bgp = SUBGRP_INST(subgrp);
+ subgroup_announce_route(subgrp);
+
+
+ /* While the announce_route() may kick off the route advertisement timer
+ * for
+ * the members of the subgroup, we'd like to send the initial updates
+ * much
+ * faster (i.e., without enforcing MRAI). Also, if there were no routes
+ * to
+ * announce, this is the method currently employed to trigger the EOR.
+ */
+ if (!bgp_update_delay_active(SUBGRP_INST(subgrp)) &&
+ !(BGP_SUPPRESS_FIB_ENABLED(bgp))) {
+ struct peer_af *paf;
+ struct peer *peer;
+
+ SUBGRP_FOREACH_PEER (subgrp, paf) {
+ peer = PAF_PEER(paf);
+ THREAD_OFF(peer->t_routeadv);
+ BGP_TIMER_ON(peer->t_routeadv, bgp_routeadv_timer, 0);
+ }
+ }
+}
+
+static int update_group_announce_walkcb(struct update_group *updgrp, void *arg)
+{
+ struct update_subgroup *subgrp;
+
+ UPDGRP_FOREACH_SUBGRP (updgrp, subgrp) {
+ subgroup_announce_all(subgrp);
+ }
+
+ return UPDWALK_CONTINUE;
+}
+
+static int update_group_announce_rrc_walkcb(struct update_group *updgrp,
+ void *arg)
+{
+ struct update_subgroup *subgrp;
+ afi_t afi;
+ safi_t safi;
+ struct peer *peer;
+
+ afi = UPDGRP_AFI(updgrp);
+ safi = UPDGRP_SAFI(updgrp);
+ peer = UPDGRP_PEER(updgrp);
+
+ /* Only announce if this is a group of route-reflector-clients */
+ if (CHECK_FLAG(peer->af_flags[afi][safi], PEER_FLAG_REFLECTOR_CLIENT)) {
+ UPDGRP_FOREACH_SUBGRP (updgrp, subgrp) {
+ subgroup_announce_all(subgrp);
+ }
+ }
+
+ return UPDWALK_CONTINUE;
+}
+
+/********************
+ * PUBLIC FUNCTIONS
+ ********************/
+
+/**
+ * Allocate an adj-out object. Do proper initialization of its fields,
+ * primarily its association with the subgroup and the prefix.
+ */
+struct bgp_adj_out *bgp_adj_out_alloc(struct update_subgroup *subgrp,
+ struct bgp_dest *dest,
+ uint32_t addpath_tx_id)
+{
+ struct bgp_adj_out *adj;
+
+ adj = XCALLOC(MTYPE_BGP_ADJ_OUT, sizeof(struct bgp_adj_out));
+ adj->subgroup = subgrp;
+ adj->addpath_tx_id = addpath_tx_id;
+
+ RB_INSERT(bgp_adj_out_rb, &dest->adj_out, adj);
+ bgp_dest_lock_node(dest);
+ adj->dest = dest;
+
+ TAILQ_INSERT_TAIL(&(subgrp->adjq), adj, subgrp_adj_train);
+ SUBGRP_INCR_STAT(subgrp, adj_count);
+ return adj;
+}
+
+
+struct bgp_advertise *
+bgp_advertise_clean_subgroup(struct update_subgroup *subgrp,
+ struct bgp_adj_out *adj)
+{
+ struct bgp_advertise *adv;
+ struct bgp_advertise_attr *baa;
+ struct bgp_advertise *next;
+ struct bgp_adv_fifo_head *fhead;
+
+ adv = adj->adv;
+ baa = adv->baa;
+ next = NULL;
+
+ if (baa) {
+ fhead = &subgrp->sync->update;
+
+ /* Unlink myself from advertise attribute FIFO. */
+ bgp_advertise_delete(baa, adv);
+
+ /* Fetch next advertise candidate. */
+ next = baa->adv;
+
+ /* Unintern BGP advertise attribute. */
+ bgp_advertise_attr_unintern(subgrp->hash, baa);
+ } else
+ fhead = &subgrp->sync->withdraw;
+
+
+ /* Unlink myself from advertisement FIFO. */
+ bgp_adv_fifo_del(fhead, adv);
+
+ /* Free memory. */
+ bgp_advertise_free(adj->adv);
+ adj->adv = NULL;
+
+ return next;
+}
+
+void bgp_adj_out_set_subgroup(struct bgp_dest *dest,
+ struct update_subgroup *subgrp, struct attr *attr,
+ struct bgp_path_info *path)
+{
+ struct bgp_adj_out *adj = NULL;
+ struct bgp_advertise *adv;
+ struct peer *peer;
+ afi_t afi;
+ safi_t safi;
+ struct peer *adv_peer;
+ struct peer_af *paf;
+ struct bgp *bgp;
+ uint32_t attr_hash = attrhash_key_make(attr);
+
+ peer = SUBGRP_PEER(subgrp);
+ afi = SUBGRP_AFI(subgrp);
+ safi = SUBGRP_SAFI(subgrp);
+ bgp = SUBGRP_INST(subgrp);
+
+ if (DISABLE_BGP_ANNOUNCE)
+ return;
+
+ /* Look for adjacency information. */
+ adj = adj_lookup(
+ dest, subgrp,
+ bgp_addpath_id_for_peer(peer, afi, safi, &path->tx_addpath));
+
+ if (adj) {
+ if (CHECK_FLAG(subgrp->sflags, SUBGRP_STATUS_TABLE_REPARSING))
+ subgrp->pscount++;
+ } else {
+ adj = bgp_adj_out_alloc(
+ subgrp, dest,
+ bgp_addpath_id_for_peer(peer, afi, safi,
+ &path->tx_addpath));
+ if (!adj)
+ return;
+
+ subgrp->pscount++;
+ }
+
+ /* Check if we are sending the same route. This is needed to
+ * avoid duplicate UPDATES. For instance, filtering communities
+ * at egress, neighbors will see duplicate UPDATES despite
+ * the route wasn't changed actually.
+ * Do not suppress BGP UPDATES for route-refresh.
+ */
+ if (CHECK_FLAG(bgp->flags, BGP_FLAG_SUPPRESS_DUPLICATES)
+ && !CHECK_FLAG(subgrp->sflags, SUBGRP_STATUS_FORCE_UPDATES)
+ && adj->attr_hash == attr_hash) {
+ if (BGP_DEBUG(update, UPDATE_OUT)) {
+ char attr_str[BUFSIZ] = {0};
+
+ bgp_dump_attr(attr, attr_str, sizeof(attr_str));
+
+ zlog_debug("%s suppress UPDATE w/ attr: %s", peer->host,
+ attr_str);
+ }
+
+ /*
+ * If BGP is skipping sending this value to it's peers
+ * the version number should be updated just like it
+ * would if it sent the data. Why? Because update
+ * groups will not be coalesced until such time that
+ * the version numbers are the same.
+ *
+ * Imagine a scenario with say 2 peers and they come
+ * up and are placed in the same update group. Then
+ * a new peer comes up a bit later. Then a prefix is
+ * flapped that we decide for the first 2 peers are
+ * mapped to and we decide not to send the data to
+ * it. Then unless more network changes happen we
+ * will never be able to coalesce the 3rd peer down
+ */
+ subgrp->version = MAX(subgrp->version, dest->version);
+ return;
+ }
+
+ if (adj->adv)
+ bgp_advertise_clean_subgroup(subgrp, adj);
+ adj->adv = bgp_advertise_new();
+
+ adv = adj->adv;
+ adv->dest = dest;
+ assert(adv->pathi == NULL);
+ /* bgp_path_info adj_out reference */
+ adv->pathi = bgp_path_info_lock(path);
+
+ adv->baa = bgp_advertise_attr_intern(subgrp->hash, attr);
+ adv->adj = adj;
+ adj->attr_hash = attr_hash;
+
+ /* Add new advertisement to advertisement attribute list. */
+ bgp_advertise_add(adv->baa, adv);
+
+ /*
+ * If the update adv list is empty, trigger the member peers'
+ * mrai timers so the socket writes can happen.
+ */
+ if (!bgp_adv_fifo_count(&subgrp->sync->update)) {
+ SUBGRP_FOREACH_PEER (subgrp, paf) {
+ /* If there are no routes in the withdraw list, set
+ * the flag PEER_STATUS_ADV_DELAY which will allow
+ * more routes to be sent in the update message
+ */
+ if (BGP_SUPPRESS_FIB_ENABLED(bgp)) {
+ adv_peer = PAF_PEER(paf);
+ if (!bgp_adv_fifo_count(
+ &subgrp->sync->withdraw))
+ SET_FLAG(adv_peer->thread_flags,
+ PEER_THREAD_SUBGRP_ADV_DELAY);
+ else
+ UNSET_FLAG(adv_peer->thread_flags,
+ PEER_THREAD_SUBGRP_ADV_DELAY);
+ }
+ bgp_adjust_routeadv(PAF_PEER(paf));
+ }
+ }
+
+ bgp_adv_fifo_add_tail(&subgrp->sync->update, adv);
+
+ subgrp->version = MAX(subgrp->version, dest->version);
+}
+
+/* The only time 'withdraw' will be false is if we are sending
+ * the "neighbor x.x.x.x default-originate" default and need to clear
+ * bgp_adj_out for the 0.0.0.0/0 route in the BGP table.
+ */
+void bgp_adj_out_unset_subgroup(struct bgp_dest *dest,
+ struct update_subgroup *subgrp, char withdraw,
+ uint32_t addpath_tx_id)
+{
+ struct bgp_adj_out *adj;
+ struct bgp_advertise *adv;
+ bool trigger_write;
+
+ if (DISABLE_BGP_ANNOUNCE)
+ return;
+
+ /* Lookup existing adjacency */
+ adj = adj_lookup(dest, subgrp, addpath_tx_id);
+ if (adj != NULL) {
+ /* Clean up previous advertisement. */
+ if (adj->adv)
+ bgp_advertise_clean_subgroup(subgrp, adj);
+
+ /* If default originate is enabled and the route is default
+ * route, do not send withdraw. This will prevent deletion of
+ * the default route at the peer.
+ */
+ if (CHECK_FLAG(subgrp->sflags, SUBGRP_STATUS_DEFAULT_ORIGINATE)
+ && is_default_prefix(bgp_dest_get_prefix(dest)))
+ return;
+
+ if (adj->attr && withdraw) {
+ /* We need advertisement structure. */
+ adj->adv = bgp_advertise_new();
+ adv = adj->adv;
+ adv->dest = dest;
+ adv->adj = adj;
+
+ /* Note if we need to trigger a packet write */
+ trigger_write =
+ !bgp_adv_fifo_count(&subgrp->sync->withdraw);
+
+ /* Add to synchronization entry for withdraw
+ * announcement. */
+ bgp_adv_fifo_add_tail(&subgrp->sync->withdraw, adv);
+
+ if (trigger_write)
+ subgroup_trigger_write(subgrp);
+ } else {
+ /* Free allocated information. */
+ adj_free(adj);
+ }
+ if (!CHECK_FLAG(subgrp->sflags, SUBGRP_STATUS_TABLE_REPARSING))
+ subgrp->pscount--;
+ }
+
+ subgrp->version = MAX(subgrp->version, dest->version);
+}
+
+void bgp_adj_out_remove_subgroup(struct bgp_dest *dest, struct bgp_adj_out *adj,
+ struct update_subgroup *subgrp)
+{
+ if (adj->attr)
+ bgp_attr_unintern(&adj->attr);
+
+ if (adj->adv)
+ bgp_advertise_clean_subgroup(subgrp, adj);
+
+ adj_free(adj);
+}
+
+/*
+ * Go through all the routes and clean up the adj/adv structures corresponding
+ * to the subgroup.
+ */
+void subgroup_clear_table(struct update_subgroup *subgrp)
+{
+ struct bgp_adj_out *aout, *taout;
+
+ SUBGRP_FOREACH_ADJ_SAFE (subgrp, aout, taout)
+ bgp_adj_out_remove_subgroup(aout->dest, aout, subgrp);
+}
+
+/*
+ * subgroup_announce_table
+ */
+void subgroup_announce_table(struct update_subgroup *subgrp,
+ struct bgp_table *table)
+{
+ struct bgp_dest *dest;
+ struct bgp_path_info *ri;
+ struct attr attr;
+ struct peer *peer;
+ afi_t afi;
+ safi_t safi;
+ bool addpath_capable;
+ struct bgp *bgp;
+ bool advertise;
+
+ peer = SUBGRP_PEER(subgrp);
+ afi = SUBGRP_AFI(subgrp);
+ safi = SUBGRP_SAFI(subgrp);
+ bgp = SUBGRP_INST(subgrp);
+ addpath_capable = bgp_addpath_encode_tx(peer, afi, safi);
+
+ if (safi == SAFI_LABELED_UNICAST)
+ safi = SAFI_UNICAST;
+
+ if (!table)
+ table = peer->bgp->rib[afi][safi];
+
+ if (safi != SAFI_MPLS_VPN && safi != SAFI_ENCAP && safi != SAFI_EVPN
+ && CHECK_FLAG(peer->af_flags[afi][safi],
+ PEER_FLAG_DEFAULT_ORIGINATE))
+ subgroup_default_originate(subgrp, 0);
+
+ subgrp->pscount = 0;
+ SET_FLAG(subgrp->sflags, SUBGRP_STATUS_TABLE_REPARSING);
+
+ for (dest = bgp_table_top(table); dest; dest = bgp_route_next(dest)) {
+ const struct prefix *dest_p = bgp_dest_get_prefix(dest);
+
+ /* Check if the route can be advertised */
+ advertise = bgp_check_advertise(bgp, dest);
+
+ for (ri = bgp_dest_get_bgp_path_info(dest); ri; ri = ri->next) {
+
+ if (!bgp_check_selected(ri, peer, addpath_capable, afi,
+ safi))
+ continue;
+
+ if (subgroup_announce_check(dest, ri, subgrp, dest_p,
+ &attr, NULL)) {
+ /* Check if route can be advertised */
+ if (advertise) {
+ if (!bgp_check_withdrawal(bgp, dest))
+ bgp_adj_out_set_subgroup(
+ dest, subgrp, &attr,
+ ri);
+ else
+ bgp_adj_out_unset_subgroup(
+ dest, subgrp, 1,
+ bgp_addpath_id_for_peer(
+ peer, afi, safi,
+ &ri->tx_addpath));
+ }
+ } else {
+ /* If default originate is enabled for
+ * the peer, do not send explicit
+ * withdraw. This will prevent deletion
+ * of default route advertised through
+ * default originate
+ */
+ if (CHECK_FLAG(peer->af_flags[afi][safi],
+ PEER_FLAG_DEFAULT_ORIGINATE) &&
+ is_default_prefix(
+ bgp_dest_get_prefix(dest)))
+ break;
+
+ bgp_adj_out_unset_subgroup(
+ dest, subgrp, 1,
+ bgp_addpath_id_for_peer(
+ peer, afi, safi,
+ &ri->tx_addpath));
+ }
+ }
+ }
+ UNSET_FLAG(subgrp->sflags, SUBGRP_STATUS_TABLE_REPARSING);
+
+ /*
+ * We walked through the whole table -- make sure our version number
+ * is consistent with the one on the table. This should allow
+ * subgroups to merge sooner if a peer comes up when the route node
+ * with the largest version is no longer in the table. This also
+ * covers the pathological case where all routes in the table have
+ * now been deleted.
+ */
+ subgrp->version = MAX(subgrp->version, table->version);
+
+ /*
+ * Start a task to merge the subgroup if necessary.
+ */
+ update_subgroup_trigger_merge_check(subgrp, 0);
+}
+
+/*
+ * subgroup_announce_route
+ *
+ * Refresh all routes out to a subgroup.
+ */
+void subgroup_announce_route(struct update_subgroup *subgrp)
+{
+ struct bgp_dest *dest;
+ struct bgp_table *table;
+ struct peer *onlypeer;
+
+ if (update_subgroup_needs_refresh(subgrp)) {
+ update_subgroup_set_needs_refresh(subgrp, 0);
+ }
+
+ /*
+ * First update is deferred until ORF or ROUTE-REFRESH is received
+ */
+ onlypeer = ((SUBGRP_PCOUNT(subgrp) == 1) ? (SUBGRP_PFIRST(subgrp))->peer
+ : NULL);
+ if (onlypeer && CHECK_FLAG(onlypeer->af_sflags[SUBGRP_AFI(subgrp)]
+ [SUBGRP_SAFI(subgrp)],
+ PEER_STATUS_ORF_WAIT_REFRESH))
+ return;
+
+ if (SUBGRP_SAFI(subgrp) != SAFI_MPLS_VPN
+ && SUBGRP_SAFI(subgrp) != SAFI_ENCAP
+ && SUBGRP_SAFI(subgrp) != SAFI_EVPN)
+ subgroup_announce_table(subgrp, NULL);
+ else
+ for (dest = bgp_table_top(update_subgroup_rib(subgrp)); dest;
+ dest = bgp_route_next(dest)) {
+ table = bgp_dest_get_bgp_table_info(dest);
+ if (!table)
+ continue;
+ subgroup_announce_table(subgrp, table);
+ }
+}
+
+void subgroup_default_originate(struct update_subgroup *subgrp, int withdraw)
+{
+ struct bgp *bgp;
+ struct attr attr;
+ struct attr *new_attr = &attr;
+ struct prefix p;
+ struct peer *from;
+ struct bgp_dest *dest;
+ struct bgp_path_info *pi;
+ struct peer *peer;
+ struct bgp_adj_out *adj;
+ route_map_result_t ret = RMAP_DENYMATCH;
+ route_map_result_t new_ret = RMAP_DENYMATCH;
+ afi_t afi;
+ safi_t safi;
+ int pref = 65536;
+ int new_pref = 0;
+
+ if (!subgrp)
+ return;
+
+ peer = SUBGRP_PEER(subgrp);
+ afi = SUBGRP_AFI(subgrp);
+ safi = SUBGRP_SAFI(subgrp);
+
+ if (!(afi == AFI_IP || afi == AFI_IP6))
+ return;
+
+ bgp = peer->bgp;
+ from = bgp->peer_self;
+
+ bgp_attr_default_set(&attr, bgp, BGP_ORIGIN_IGP);
+
+ /* make coverity happy */
+ assert(attr.aspath);
+
+ attr.med = 0;
+ attr.flag |= ATTR_FLAG_BIT(BGP_ATTR_MULTI_EXIT_DISC);
+
+ if ((afi == AFI_IP6) || peer_cap_enhe(peer, afi, safi)) {
+ /* IPv6 global nexthop must be included. */
+ attr.mp_nexthop_len = BGP_ATTR_NHLEN_IPV6_GLOBAL;
+
+ /* If the peer is on shared nextwork and we have link-local
+ nexthop set it. */
+ if (peer->shared_network
+ && !IN6_IS_ADDR_UNSPECIFIED(&peer->nexthop.v6_local))
+ attr.mp_nexthop_len = BGP_ATTR_NHLEN_IPV6_GLOBAL_AND_LL;
+ }
+
+ if (peer->default_rmap[afi][safi].name) {
+ struct bgp_path_info tmp_pi = {0};
+
+ tmp_pi.peer = bgp->peer_self;
+
+ SET_FLAG(bgp->peer_self->rmap_type, PEER_RMAP_TYPE_DEFAULT);
+
+ /* Iterate over the RIB to see if we can announce
+ * the default route. We announce the default
+ * route only if route-map has a match.
+ */
+ for (dest = bgp_table_top(bgp->rib[afi][safi]); dest;
+ dest = bgp_route_next(dest)) {
+ if (!bgp_dest_has_bgp_path_info_data(dest))
+ continue;
+
+ for (pi = bgp_dest_get_bgp_path_info(dest); pi;
+ pi = pi->next) {
+ struct attr tmp_attr = attr;
+
+ tmp_pi.attr = &tmp_attr;
+
+ new_ret = route_map_apply_ext(
+ peer->default_rmap[afi][safi].map,
+ bgp_dest_get_prefix(dest), pi, &tmp_pi,
+ &new_pref);
+
+ if (new_ret == RMAP_PERMITMATCH) {
+ if (new_pref < pref) {
+ pref = new_pref;
+ bgp_attr_flush(new_attr);
+ new_attr = bgp_attr_intern(
+ tmp_pi.attr);
+ bgp_attr_flush(tmp_pi.attr);
+ }
+ subgroup_announce_reset_nhop(
+ (peer_cap_enhe(peer, afi, safi)
+ ? AF_INET6
+ : AF_INET),
+ new_attr);
+ ret = new_ret;
+ } else
+ bgp_attr_flush(&tmp_attr);
+ }
+ }
+ bgp->peer_self->rmap_type = 0;
+
+ if (ret == RMAP_DENYMATCH) {
+ /*
+ * If its a implicit withdraw due to routemap
+ * deny operation need to set the flag back.
+ * This is a convertion of update flow to
+ * withdraw flow.
+ */
+ if (!withdraw &&
+ (!CHECK_FLAG(subgrp->sflags,
+ SUBGRP_STATUS_DEFAULT_ORIGINATE)))
+ SET_FLAG(subgrp->sflags,
+ SUBGRP_STATUS_DEFAULT_ORIGINATE);
+ withdraw = 1;
+ }
+ }
+
+ /* Check if the default route is in local BGP RIB which is
+ * installed through redistribute or network command
+ */
+ memset(&p, 0, sizeof(p));
+ p.family = afi2family(afi);
+ p.prefixlen = 0;
+ dest = bgp_afi_node_lookup(bgp->rib[afi][safi], afi, safi, &p, NULL);
+
+ if (withdraw) {
+ /* Withdraw the default route advertised using default
+ * originate
+ */
+ if (CHECK_FLAG(subgrp->sflags, SUBGRP_STATUS_DEFAULT_ORIGINATE))
+ subgroup_default_withdraw_packet(subgrp);
+ UNSET_FLAG(subgrp->sflags, SUBGRP_STATUS_DEFAULT_ORIGINATE);
+
+ /* If default route is present in the local RIB, advertise the
+ * route
+ */
+ if (dest) {
+ for (pi = bgp_dest_get_bgp_path_info(dest); pi;
+ pi = pi->next) {
+ if (CHECK_FLAG(pi->flags, BGP_PATH_SELECTED))
+ if (subgroup_announce_check(
+ dest, pi, subgrp,
+ bgp_dest_get_prefix(dest),
+ &attr, NULL)) {
+ struct attr *default_attr =
+ bgp_attr_intern(&attr);
+
+ bgp_adj_out_set_subgroup(
+ dest, subgrp,
+ default_attr, pi);
+ }
+ }
+ bgp_dest_unlock_node(dest);
+ }
+ } else {
+ if (!CHECK_FLAG(subgrp->sflags,
+ SUBGRP_STATUS_DEFAULT_ORIGINATE)) {
+
+ /* The 'neighbor x.x.x.x default-originate' default will
+ * act as an
+ * implicit withdraw for any previous UPDATEs sent for
+ * 0.0.0.0/0 so
+ * clear adj_out for the 0.0.0.0/0 prefix in the BGP
+ * table.
+ */
+ if (dest) {
+ /* Remove the adjacency for the previously
+ * advertised default route
+ */
+ adj = adj_lookup(
+ dest, subgrp,
+ BGP_ADDPATH_TX_ID_FOR_DEFAULT_ORIGINATE);
+ if (adj != NULL) {
+ /* Clean up previous advertisement. */
+ if (adj->adv)
+ bgp_advertise_clean_subgroup(
+ subgrp, adj);
+
+ /* Free allocated information. */
+ adj_free(adj);
+ }
+ bgp_dest_unlock_node(dest);
+ }
+
+ /* Advertise the default route */
+ if (bgp_in_graceful_shutdown(bgp))
+ bgp_attr_add_gshut_community(new_attr);
+
+ SET_FLAG(subgrp->sflags,
+ SUBGRP_STATUS_DEFAULT_ORIGINATE);
+ subgroup_default_update_packet(subgrp, new_attr, from);
+ }
+ }
+
+ aspath_unintern(&attr.aspath);
+}
+
+/*
+ * Announce the BGP table to a subgroup.
+ *
+ * At startup, we try to optimize route announcement by coalescing the
+ * peer-up events. This is done only the first time - from then on,
+ * subgrp->v_coalesce will be set to zero and the normal logic
+ * prevails.
+ */
+void subgroup_announce_all(struct update_subgroup *subgrp)
+{
+ if (!subgrp)
+ return;
+
+ /*
+ * If coalesce timer value is not set, announce routes immediately.
+ */
+ if (!subgrp->v_coalesce) {
+ if (bgp_debug_update(NULL, NULL, subgrp->update_group, 0))
+ zlog_debug("u%" PRIu64 ":s%" PRIu64" announcing all routes",
+ subgrp->update_group->id, subgrp->id);
+ subgroup_announce_route(subgrp);
+ return;
+ }
+
+ /*
+ * We should wait for the coalesce timer. Arm the timer if not done.
+ */
+ if (!subgrp->t_coalesce) {
+ thread_add_timer_msec(bm->master, subgroup_coalesce_timer,
+ subgrp, subgrp->v_coalesce,
+ &subgrp->t_coalesce);
+ }
+}
+
+/*
+ * Go through all update subgroups and set up the adv queue for the
+ * input route.
+ */
+void group_announce_route(struct bgp *bgp, afi_t afi, safi_t safi,
+ struct bgp_dest *dest, struct bgp_path_info *pi)
+{
+ struct updwalk_context ctx;
+ ctx.pi = pi;
+ ctx.dest = dest;
+
+ /* If suppress fib is enabled, the route will be advertised when
+ * FIB status is received
+ */
+ if (!bgp_check_advertise(bgp, dest))
+ return;
+
+ update_group_af_walk(bgp, afi, safi, group_announce_route_walkcb, &ctx);
+}
+
+void update_group_show_adj_queue(struct bgp *bgp, afi_t afi, safi_t safi,
+ struct vty *vty, uint64_t id)
+{
+ updgrp_show_adj(bgp, afi, safi, vty, id, UPDWALK_FLAGS_ADVQUEUE);
+}
+
+void update_group_show_advertised(struct bgp *bgp, afi_t afi, safi_t safi,
+ struct vty *vty, uint64_t id)
+{
+ updgrp_show_adj(bgp, afi, safi, vty, id, UPDWALK_FLAGS_ADVERTISED);
+}
+
+void update_group_announce(struct bgp *bgp)
+{
+ update_group_walk(bgp, update_group_announce_walkcb, NULL);
+}
+
+void update_group_announce_rrclients(struct bgp *bgp)
+{
+ update_group_walk(bgp, update_group_announce_rrc_walkcb, NULL);
+}
diff --git a/bgpd/bgp_updgrp_packet.c b/bgpd/bgp_updgrp_packet.c
new file mode 100644
index 0000000..344aea1
--- /dev/null
+++ b/bgpd/bgp_updgrp_packet.c
@@ -0,0 +1,1315 @@
+/**
+ * bgp_updgrp_packet.c: BGP update group packet handling routines
+ *
+ * @copyright Copyright (C) 2014 Cumulus Networks, Inc.
+ *
+ * @author Avneesh Sachdev <avneesh@sproute.net>
+ * @author Rajesh Varadarajan <rajesh@sproute.net>
+ * @author Pradosh Mohapatra <pradosh@sproute.net>
+ *
+ * This file is part of GNU Zebra.
+ *
+ * GNU Zebra is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License as published by the
+ * Free Software Foundation; either version 2, or (at your option) any
+ * later version.
+ *
+ * GNU Zebra is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program; see the file COPYING; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
+ */
+
+#include <zebra.h>
+
+#include "prefix.h"
+#include "thread.h"
+#include "buffer.h"
+#include "stream.h"
+#include "command.h"
+#include "sockunion.h"
+#include "network.h"
+#include "memory.h"
+#include "filter.h"
+#include "routemap.h"
+#include "log.h"
+#include "plist.h"
+#include "linklist.h"
+#include "workqueue.h"
+#include "hash.h"
+#include "queue.h"
+#include "mpls.h"
+
+#include "bgpd/bgpd.h"
+#include "bgpd/bgp_debug.h"
+#include "bgpd/bgp_errors.h"
+#include "bgpd/bgp_fsm.h"
+#include "bgpd/bgp_route.h"
+#include "bgpd/bgp_packet.h"
+#include "bgpd/bgp_advertise.h"
+#include "bgpd/bgp_updgrp.h"
+#include "bgpd/bgp_nexthop.h"
+#include "bgpd/bgp_nht.h"
+#include "bgpd/bgp_mplsvpn.h"
+#include "bgpd/bgp_label.h"
+#include "bgpd/bgp_addpath.h"
+
+/********************
+ * PRIVATE FUNCTIONS
+ ********************/
+
+/********************
+ * PUBLIC FUNCTIONS
+ ********************/
+struct bpacket *bpacket_alloc(void)
+{
+ struct bpacket *pkt;
+
+ pkt = XCALLOC(MTYPE_BGP_PACKET, sizeof(struct bpacket));
+
+ return pkt;
+}
+
+void bpacket_free(struct bpacket *pkt)
+{
+ if (pkt->buffer)
+ stream_free(pkt->buffer);
+ pkt->buffer = NULL;
+ XFREE(MTYPE_BGP_PACKET, pkt);
+}
+
+void bpacket_queue_init(struct bpacket_queue *q)
+{
+ TAILQ_INIT(&(q->pkts));
+}
+
+/*
+ * bpacket_queue_add_packet
+ *
+ * Internal function of bpacket_queue - and adds a
+ * packet entry to the end of the list.
+ *
+ * Users of bpacket_queue should use bpacket_queue_add instead.
+ */
+static void bpacket_queue_add_packet(struct bpacket_queue *q,
+ struct bpacket *pkt)
+{
+ struct bpacket *last_pkt;
+
+ if (TAILQ_EMPTY(&(q->pkts)))
+ TAILQ_INSERT_TAIL(&(q->pkts), pkt, pkt_train);
+ else {
+ last_pkt = bpacket_queue_last(q);
+ TAILQ_INSERT_AFTER(&(q->pkts), last_pkt, pkt, pkt_train);
+ }
+ q->curr_count++;
+ if (q->hwm_count < q->curr_count)
+ q->hwm_count = q->curr_count;
+}
+
+/*
+ * Adds a packet to the bpacket_queue.
+ *
+ * The stream passed is consumed by this function. So, the caller should
+ * not free or use the stream after
+ * invoking this function.
+ */
+struct bpacket *bpacket_queue_add(struct bpacket_queue *q, struct stream *s,
+ struct bpacket_attr_vec_arr *vecarrp)
+{
+ struct bpacket *pkt;
+ struct bpacket *last_pkt;
+
+
+ pkt = bpacket_alloc();
+ if (TAILQ_EMPTY(&(q->pkts))) {
+ pkt->ver = 1;
+ pkt->buffer = s;
+ if (vecarrp)
+ memcpy(&pkt->arr, vecarrp,
+ sizeof(struct bpacket_attr_vec_arr));
+ else
+ bpacket_attr_vec_arr_reset(&pkt->arr);
+ bpacket_queue_add_packet(q, pkt);
+ return pkt;
+ }
+
+ /*
+ * Fill in the new information into the current sentinel and create a
+ * new sentinel.
+ */
+ last_pkt = bpacket_queue_last(q);
+ assert(last_pkt->buffer == NULL);
+ last_pkt->buffer = s;
+ if (vecarrp)
+ memcpy(&last_pkt->arr, vecarrp,
+ sizeof(struct bpacket_attr_vec_arr));
+ else
+ bpacket_attr_vec_arr_reset(&last_pkt->arr);
+
+ pkt->ver = last_pkt->ver;
+ pkt->ver++;
+ bpacket_queue_add_packet(q, pkt);
+
+ return last_pkt;
+}
+
+struct bpacket *bpacket_queue_first(struct bpacket_queue *q)
+{
+ return (TAILQ_FIRST(&(q->pkts)));
+}
+
+struct bpacket *bpacket_queue_last(struct bpacket_queue *q)
+{
+ return TAILQ_LAST(&(q->pkts), pkt_queue);
+}
+
+struct bpacket *bpacket_queue_remove(struct bpacket_queue *q)
+{
+ struct bpacket *first;
+
+ first = bpacket_queue_first(q);
+ if (first) {
+ TAILQ_REMOVE(&(q->pkts), first, pkt_train);
+ q->curr_count--;
+ }
+ return first;
+}
+
+unsigned int bpacket_queue_length(struct bpacket_queue *q)
+{
+ return q->curr_count - 1;
+}
+
+unsigned int bpacket_queue_hwm_length(struct bpacket_queue *q)
+{
+ return q->hwm_count - 1;
+}
+
+bool bpacket_queue_is_full(struct bgp *bgp, struct bpacket_queue *q)
+{
+ if (q->curr_count >= bgp->default_subgroup_pkt_queue_max)
+ return true;
+ return false;
+}
+
+void bpacket_add_peer(struct bpacket *pkt, struct peer_af *paf)
+{
+ if (!pkt || !paf)
+ return;
+
+ LIST_INSERT_HEAD(&(pkt->peers), paf, pkt_train);
+ paf->next_pkt_to_send = pkt;
+}
+
+/*
+ * bpacket_queue_cleanup
+ */
+void bpacket_queue_cleanup(struct bpacket_queue *q)
+{
+ struct bpacket *pkt;
+
+ while ((pkt = bpacket_queue_remove(q))) {
+ bpacket_free(pkt);
+ }
+}
+
+/*
+ * bpacket_queue_compact
+ *
+ * Delete packets that do not need to be transmitted to any peer from
+ * the queue.
+ *
+ * @return the number of packets deleted.
+ */
+static int bpacket_queue_compact(struct bpacket_queue *q)
+{
+ int num_deleted;
+ struct bpacket *pkt, *removed_pkt;
+
+ num_deleted = 0;
+
+ while (1) {
+ pkt = bpacket_queue_first(q);
+ if (!pkt)
+ break;
+
+ /*
+ * Don't delete the sentinel.
+ */
+ if (!pkt->buffer)
+ break;
+
+ if (!LIST_EMPTY(&(pkt->peers)))
+ break;
+
+ removed_pkt = bpacket_queue_remove(q);
+ assert(pkt == removed_pkt);
+ bpacket_free(removed_pkt);
+
+ num_deleted++;
+ }
+
+ return num_deleted;
+}
+
+void bpacket_queue_advance_peer(struct peer_af *paf)
+{
+ struct bpacket *pkt;
+ struct bpacket *old_pkt;
+
+ old_pkt = paf->next_pkt_to_send;
+ if (old_pkt->buffer == NULL)
+ /* Already at end of list */
+ return;
+
+ LIST_REMOVE(paf, pkt_train);
+ pkt = TAILQ_NEXT(old_pkt, pkt_train);
+ bpacket_add_peer(pkt, paf);
+
+ if (!bpacket_queue_compact(PAF_PKTQ(paf)))
+ return;
+
+ /*
+ * Deleted one or more packets. Check if we can now merge this
+ * peer's subgroup into another subgroup.
+ */
+ update_subgroup_check_merge(paf->subgroup, "advanced peer in queue");
+}
+
+/*
+ * bpacket_queue_remove_peer
+ *
+ * Remove the peer from the packet queue of the subgroup it belongs
+ * to.
+ */
+void bpacket_queue_remove_peer(struct peer_af *paf)
+{
+ struct bpacket_queue *q;
+
+ q = PAF_PKTQ(paf);
+ assert(q);
+
+ LIST_REMOVE(paf, pkt_train);
+ paf->next_pkt_to_send = NULL;
+
+ bpacket_queue_compact(q);
+}
+
+unsigned int bpacket_queue_virtual_length(struct peer_af *paf)
+{
+ struct bpacket *pkt;
+ struct bpacket *last;
+ struct bpacket_queue *q;
+
+ pkt = paf->next_pkt_to_send;
+ if (!pkt || (pkt->buffer == NULL))
+ /* Already at end of list */
+ return 0;
+
+ q = PAF_PKTQ(paf);
+ if (TAILQ_EMPTY(&(q->pkts)))
+ return 0;
+
+ last = TAILQ_LAST(&(q->pkts), pkt_queue);
+ if (last->ver >= pkt->ver)
+ return last->ver - pkt->ver;
+
+ /* sequence # rolled over */
+ return (UINT_MAX - pkt->ver + 1) + last->ver;
+}
+
+/*
+ * Dump the bpacket queue
+ */
+void bpacket_queue_show_vty(struct bpacket_queue *q, struct vty *vty)
+{
+ struct bpacket *pkt;
+ struct peer_af *paf;
+
+ pkt = bpacket_queue_first(q);
+ while (pkt) {
+ vty_out(vty, " Packet %p ver %u buffer %p\n", pkt, pkt->ver,
+ pkt->buffer);
+
+ LIST_FOREACH (paf, &(pkt->peers), pkt_train) {
+ vty_out(vty, " - %s\n", paf->peer->host);
+ }
+ pkt = bpacket_next(pkt);
+ }
+ return;
+}
+
+struct stream *bpacket_reformat_for_peer(struct bpacket *pkt,
+ struct peer_af *paf)
+{
+ struct stream *s = NULL;
+ bpacket_attr_vec *vec;
+ struct peer *peer;
+ struct bgp_filter *filter;
+
+ s = stream_dup(pkt->buffer);
+ peer = PAF_PEER(paf);
+
+ vec = &pkt->arr.entries[BGP_ATTR_VEC_NH];
+
+ if (!CHECK_FLAG(vec->flags, BPKT_ATTRVEC_FLAGS_UPDATED))
+ return s;
+
+ uint8_t nhlen;
+ afi_t nhafi;
+ int route_map_sets_nh;
+
+ nhlen = stream_getc_from(s, vec->offset);
+ filter = &peer->filter[paf->afi][paf->safi];
+
+ if (peer_cap_enhe(peer, paf->afi, paf->safi))
+ nhafi = AFI_IP6;
+ else
+ nhafi = BGP_NEXTHOP_AFI_FROM_NHLEN(nhlen);
+
+ if (nhafi == AFI_IP) {
+ struct in_addr v4nh, *mod_v4nh;
+ int nh_modified = 0;
+ size_t offset_nh = vec->offset + 1;
+
+ route_map_sets_nh =
+ (CHECK_FLAG(vec->flags,
+ BPKT_ATTRVEC_FLAGS_RMAP_IPV4_NH_CHANGED)
+ || CHECK_FLAG(
+ vec->flags,
+ BPKT_ATTRVEC_FLAGS_RMAP_NH_PEER_ADDRESS));
+
+ switch (nhlen) {
+ case BGP_ATTR_NHLEN_IPV4:
+ break;
+ case BGP_ATTR_NHLEN_VPNV4:
+ offset_nh += 8;
+ break;
+ default:
+ /* TODO: handle IPv6 nexthops */
+ flog_warn(
+ EC_BGP_INVALID_NEXTHOP_LENGTH,
+ "%s: %s: invalid MP nexthop length (AFI IP): %u",
+ __func__, peer->host, nhlen);
+ stream_free(s);
+ return NULL;
+ }
+
+ stream_get_from(&v4nh, s, offset_nh, IPV4_MAX_BYTELEN);
+ mod_v4nh = &v4nh;
+
+ /*
+ * If route-map has set the nexthop, that is normally
+ * used; if it is specified as peer-address, the peering
+ * address is picked up. Otherwise, if NH is unavailable
+ * from attribute, the peering addr is picked up; the
+ * "NH unavailable" case also covers next-hop-self and
+ * some other scenarios - see subgroup_announce_check().
+ * In all other cases, use the nexthop carried in the
+ * attribute unless it is EBGP non-multiaccess and there
+ * is no next-hop-unchanged setting or the peer is EBGP
+ * and the route-map that changed the next-hop value
+ * was applied inbound rather than outbound. Updates to
+ * an EBGP peer should only modify the next-hop if it
+ * was set in an outbound route-map to that peer.
+ * Note: It is assumed route-map cannot set the nexthop
+ * to an invalid value.
+ */
+ if (route_map_sets_nh
+ && ((peer->sort != BGP_PEER_EBGP)
+ || ROUTE_MAP_OUT(filter))) {
+ if (CHECK_FLAG(
+ vec->flags,
+ BPKT_ATTRVEC_FLAGS_RMAP_NH_PEER_ADDRESS)) {
+ mod_v4nh = &peer->nexthop.v4;
+ nh_modified = 1;
+ }
+ } else if (v4nh.s_addr == INADDR_ANY) {
+ mod_v4nh = &peer->nexthop.v4;
+ nh_modified = 1;
+ } else if (peer->sort == BGP_PEER_EBGP
+ && (bgp_multiaccess_check_v4(v4nh, peer) == 0)
+ && !CHECK_FLAG(vec->flags,
+ BPKT_ATTRVEC_FLAGS_RMAP_NH_UNCHANGED)
+ && !peer_af_flag_check(
+ peer, paf->afi, paf->safi,
+ PEER_FLAG_NEXTHOP_UNCHANGED)) {
+ /* NOTE: not handling case where NH has new AFI
+ */
+ mod_v4nh = &peer->nexthop.v4;
+ nh_modified = 1;
+ }
+
+ if (nh_modified) /* allow for VPN RD */
+ stream_put_in_addr_at(s, offset_nh, mod_v4nh);
+
+ if (bgp_debug_update(peer, NULL, NULL, 0))
+ zlog_debug("u%" PRIu64 ":s%" PRIu64
+ " %s send UPDATE w/ nexthop %pI4%s",
+ PAF_SUBGRP(paf)->update_group->id,
+ PAF_SUBGRP(paf)->id, peer->host, mod_v4nh,
+ (nhlen == BGP_ATTR_NHLEN_VPNV4 ? " and RD"
+ : ""));
+ } else if (nhafi == AFI_IP6) {
+ struct in6_addr v6nhglobal, *mod_v6nhg;
+ struct in6_addr v6nhlocal, *mod_v6nhl;
+ int gnh_modified, lnh_modified;
+ size_t offset_nhglobal = vec->offset + 1;
+ size_t offset_nhlocal = vec->offset + 1;
+
+ gnh_modified = lnh_modified = 0;
+ mod_v6nhg = &v6nhglobal;
+ mod_v6nhl = &v6nhlocal;
+
+ route_map_sets_nh =
+ (CHECK_FLAG(vec->flags,
+ BPKT_ATTRVEC_FLAGS_RMAP_IPV6_GNH_CHANGED)
+ || CHECK_FLAG(
+ vec->flags,
+ BPKT_ATTRVEC_FLAGS_RMAP_NH_PEER_ADDRESS));
+
+ /*
+ * The logic here is rather similar to that for IPv4, the
+ * additional work being to handle 1 or 2 nexthops.
+ * Also, 3rd party nexthop is not propagated for EBGP
+ * right now.
+ */
+ switch (nhlen) {
+ case BGP_ATTR_NHLEN_IPV6_GLOBAL:
+ break;
+ case BGP_ATTR_NHLEN_IPV6_GLOBAL_AND_LL:
+ offset_nhlocal += IPV6_MAX_BYTELEN;
+ break;
+ case BGP_ATTR_NHLEN_VPNV6_GLOBAL:
+ offset_nhglobal += 8;
+ break;
+ case BGP_ATTR_NHLEN_VPNV6_GLOBAL_AND_LL:
+ offset_nhglobal += 8;
+ offset_nhlocal += 8 * 2 + IPV6_MAX_BYTELEN;
+ break;
+ default:
+ /* TODO: handle IPv4 nexthops */
+ flog_warn(
+ EC_BGP_INVALID_NEXTHOP_LENGTH,
+ "%s: %s: invalid MP nexthop length (AFI IP6): %u",
+ __func__, peer->host, nhlen);
+ stream_free(s);
+ return NULL;
+ }
+
+ stream_get_from(&v6nhglobal, s, offset_nhglobal,
+ IPV6_MAX_BYTELEN);
+
+ /*
+ * Updates to an EBGP peer should only modify the
+ * next-hop if it was set in an outbound route-map
+ * to that peer.
+ */
+ if (route_map_sets_nh
+ && ((peer->sort != BGP_PEER_EBGP)
+ || ROUTE_MAP_OUT(filter))) {
+ if (CHECK_FLAG(
+ vec->flags,
+ BPKT_ATTRVEC_FLAGS_RMAP_NH_PEER_ADDRESS)) {
+ mod_v6nhg = &peer->nexthop.v6_global;
+ gnh_modified = 1;
+ }
+ } else if (IN6_IS_ADDR_UNSPECIFIED(&v6nhglobal)) {
+ mod_v6nhg = &peer->nexthop.v6_global;
+ gnh_modified = 1;
+ } else if ((peer->sort == BGP_PEER_EBGP)
+ && (!bgp_multiaccess_check_v6(v6nhglobal, peer))
+ && !CHECK_FLAG(vec->flags,
+ BPKT_ATTRVEC_FLAGS_RMAP_NH_UNCHANGED)
+ && !peer_af_flag_check(
+ peer, paf->afi, paf->safi,
+ PEER_FLAG_NEXTHOP_UNCHANGED)) {
+ /* NOTE: not handling case where NH has new AFI
+ */
+ mod_v6nhg = &peer->nexthop.v6_global;
+ gnh_modified = 1;
+ }
+
+ if (IN6_IS_ADDR_UNSPECIFIED(mod_v6nhg)) {
+ if (peer->nexthop.v4.s_addr != INADDR_ANY) {
+ ipv4_to_ipv4_mapped_ipv6(mod_v6nhg,
+ peer->nexthop.v4);
+ }
+ }
+
+ if (IS_MAPPED_IPV6(&peer->nexthop.v6_global)) {
+ mod_v6nhg = &peer->nexthop.v6_global;
+ gnh_modified = 1;
+ }
+
+ if (nhlen == BGP_ATTR_NHLEN_IPV6_GLOBAL_AND_LL
+ || nhlen == BGP_ATTR_NHLEN_VPNV6_GLOBAL_AND_LL) {
+ stream_get_from(&v6nhlocal, s, offset_nhlocal,
+ IPV6_MAX_BYTELEN);
+ if (IN6_IS_ADDR_UNSPECIFIED(&v6nhlocal)) {
+ mod_v6nhl = &peer->nexthop.v6_local;
+ lnh_modified = 1;
+ }
+ }
+
+ if (gnh_modified)
+ stream_put_in6_addr_at(s, offset_nhglobal, mod_v6nhg);
+ if (lnh_modified)
+ stream_put_in6_addr_at(s, offset_nhlocal, mod_v6nhl);
+
+ if (bgp_debug_update(peer, NULL, NULL, 0)) {
+ if (nhlen == BGP_ATTR_NHLEN_IPV6_GLOBAL_AND_LL
+ || nhlen == BGP_ATTR_NHLEN_VPNV6_GLOBAL_AND_LL)
+ zlog_debug(
+ "u%" PRIu64 ":s%" PRIu64
+ " %s send UPDATE w/ mp_nexthops %pI6, %pI6%s",
+ PAF_SUBGRP(paf)->update_group->id,
+ PAF_SUBGRP(paf)->id, peer->host,
+ mod_v6nhg, mod_v6nhl,
+ (nhlen == BGP_ATTR_NHLEN_VPNV6_GLOBAL_AND_LL
+ ? " and RD"
+ : ""));
+ else
+ zlog_debug(
+ "u%" PRIu64 ":s%" PRIu64
+ " %s send UPDATE w/ mp_nexthop %pI6%s",
+ PAF_SUBGRP(paf)->update_group->id,
+ PAF_SUBGRP(paf)->id, peer->host,
+ mod_v6nhg,
+ (nhlen == BGP_ATTR_NHLEN_VPNV6_GLOBAL
+ ? " and RD"
+ : ""));
+ }
+ } else if (paf->afi == AFI_L2VPN) {
+ struct in_addr v4nh, *mod_v4nh;
+ int nh_modified = 0;
+
+ stream_get_from(&v4nh, s, vec->offset + 1, 4);
+ mod_v4nh = &v4nh;
+
+ /* No route-map changes allowed for EVPN nexthops. */
+ if (v4nh.s_addr == INADDR_ANY) {
+ mod_v4nh = &peer->nexthop.v4;
+ nh_modified = 1;
+ }
+
+ if (nh_modified)
+ stream_put_in_addr_at(s, vec->offset + 1, mod_v4nh);
+
+ if (bgp_debug_update(peer, NULL, NULL, 0))
+ zlog_debug("u%" PRIu64 ":s%" PRIu64
+ " %s send UPDATE w/ nexthop %pI4",
+ PAF_SUBGRP(paf)->update_group->id,
+ PAF_SUBGRP(paf)->id, peer->host, mod_v4nh);
+ }
+
+ return s;
+}
+
+/*
+ * Update the vecarr offsets to go beyond 'pos' bytes, i.e. add 'pos'
+ * to each offset.
+ */
+static void bpacket_attr_vec_arr_update(struct bpacket_attr_vec_arr *vecarr,
+ size_t pos)
+{
+ int i;
+
+ if (!vecarr)
+ return;
+
+ for (i = 0; i < BGP_ATTR_VEC_MAX; i++)
+ vecarr->entries[i].offset += pos;
+}
+
+/*
+ * Return if there are packets to build for this subgroup.
+ */
+bool subgroup_packets_to_build(struct update_subgroup *subgrp)
+{
+ struct bgp_advertise *adv;
+
+ if (!subgrp)
+ return false;
+
+ adv = bgp_adv_fifo_first(&subgrp->sync->withdraw);
+ if (adv)
+ return true;
+
+ adv = bgp_adv_fifo_first(&subgrp->sync->update);
+ if (adv)
+ return true;
+
+ return false;
+}
+
+/* Make BGP update packet. */
+struct bpacket *subgroup_update_packet(struct update_subgroup *subgrp)
+{
+ struct bpacket_attr_vec_arr vecarr;
+ struct bpacket *pkt;
+ struct peer *peer;
+ struct stream *s;
+ struct stream *snlri;
+ struct stream *packet;
+ struct bgp_adj_out *adj;
+ struct bgp_advertise *adv;
+ struct bgp_dest *dest = NULL;
+ struct bgp_path_info *path = NULL;
+ bgp_size_t total_attr_len = 0;
+ unsigned long attrlen_pos = 0;
+ size_t mpattrlen_pos = 0;
+ size_t mpattr_pos = 0;
+ afi_t afi;
+ safi_t safi;
+ int space_remaining = 0;
+ int space_needed = 0;
+ char send_attr_str[BUFSIZ];
+ int send_attr_printed = 0;
+ int num_pfx = 0;
+ bool addpath_capable = false;
+ int addpath_overhead = 0;
+ uint32_t addpath_tx_id = 0;
+ struct prefix_rd *prd = NULL;
+ mpls_label_t label = MPLS_INVALID_LABEL, *label_pnt = NULL;
+ uint32_t num_labels = 0;
+
+ if (!subgrp)
+ return NULL;
+
+ if (bpacket_queue_is_full(SUBGRP_INST(subgrp), SUBGRP_PKTQ(subgrp)))
+ return NULL;
+
+ peer = SUBGRP_PEER(subgrp);
+ afi = SUBGRP_AFI(subgrp);
+ safi = SUBGRP_SAFI(subgrp);
+ s = subgrp->work;
+ stream_reset(s);
+ snlri = subgrp->scratch;
+ stream_reset(snlri);
+
+ bpacket_attr_vec_arr_reset(&vecarr);
+
+ addpath_capable = bgp_addpath_encode_tx(peer, afi, safi);
+ addpath_overhead = addpath_capable ? BGP_ADDPATH_ID_LEN : 0;
+
+ adv = bgp_adv_fifo_first(&subgrp->sync->update);
+ while (adv) {
+ const struct prefix *dest_p;
+
+ assert(adv->dest);
+ dest = adv->dest;
+ dest_p = bgp_dest_get_prefix(dest);
+ adj = adv->adj;
+ addpath_tx_id = adj->addpath_tx_id;
+ path = adv->pathi;
+
+ space_remaining = STREAM_CONCAT_REMAIN(s, snlri, STREAM_SIZE(s))
+ - BGP_MAX_PACKET_SIZE_OVERFLOW;
+ space_needed =
+ BGP_NLRI_LENGTH + addpath_overhead
+ + bgp_packet_mpattr_prefix_size(afi, safi, dest_p);
+
+ /* When remaining space can't include NLRI and it's length. */
+ if (space_remaining < space_needed)
+ break;
+
+ /* If packet is empty, set attribute. */
+ if (stream_empty(s)) {
+ struct peer *from = NULL;
+
+ if (path)
+ from = path->peer;
+
+ /* 1: Write the BGP message header - 16 bytes marker, 2
+ * bytes length,
+ * one byte message type.
+ */
+ bgp_packet_set_marker(s, BGP_MSG_UPDATE);
+
+ /* 2: withdrawn routes length */
+ stream_putw(s, 0);
+
+ /* 3: total attributes length - attrlen_pos stores the
+ * position */
+ attrlen_pos = stream_get_endp(s);
+ stream_putw(s, 0);
+
+ /* 4: if there is MP_REACH_NLRI attribute, that should
+ * be the first
+ * attribute, according to
+ * draft-ietf-idr-error-handling. Save the
+ * position.
+ */
+ mpattr_pos = stream_get_endp(s);
+
+ /* 5: Encode all the attributes, except MP_REACH_NLRI
+ * attr. */
+ total_attr_len = bgp_packet_attribute(
+ NULL, peer, s, adv->baa->attr, &vecarr, NULL,
+ afi, safi, from, NULL, NULL, 0, 0, 0);
+
+ space_remaining =
+ STREAM_CONCAT_REMAIN(s, snlri, STREAM_SIZE(s))
+ - BGP_MAX_PACKET_SIZE_OVERFLOW;
+ space_needed = BGP_NLRI_LENGTH + addpath_overhead
+ + bgp_packet_mpattr_prefix_size(
+ afi, safi, dest_p);
+
+ /* If the attributes alone do not leave any room for
+ * NLRI then
+ * return */
+ if (space_remaining < space_needed) {
+ flog_err(
+ EC_BGP_UPDGRP_ATTR_LEN,
+ "u%" PRIu64 ":s%" PRIu64" attributes too long, cannot send UPDATE",
+ subgrp->update_group->id, subgrp->id);
+
+ /* Flush the FIFO update queue */
+ while (adv)
+ adv = bgp_advertise_clean_subgroup(
+ subgrp, adj);
+ return NULL;
+ }
+
+ if (BGP_DEBUG(update, UPDATE_OUT)
+ || BGP_DEBUG(update, UPDATE_PREFIX)) {
+ memset(send_attr_str, 0, BUFSIZ);
+ send_attr_printed = 0;
+ bgp_dump_attr(adv->baa->attr, send_attr_str,
+ sizeof(send_attr_str));
+ }
+ }
+
+ if ((afi == AFI_IP && safi == SAFI_UNICAST)
+ && !peer_cap_enhe(peer, afi, safi))
+ stream_put_prefix_addpath(s, dest_p, addpath_capable,
+ addpath_tx_id);
+ else {
+ /* Encode the prefix in MP_REACH_NLRI attribute */
+ if (dest->pdest)
+ prd = (struct prefix_rd *)bgp_dest_get_prefix(
+ dest->pdest);
+
+ if (safi == SAFI_LABELED_UNICAST) {
+ label = bgp_adv_label(dest, path, peer, afi,
+ safi);
+ label_pnt = &label;
+ num_labels = 1;
+ } else if (path && path->extra) {
+ label_pnt = &path->extra->label[0];
+ num_labels = path->extra->num_labels;
+ }
+
+ if (stream_empty(snlri))
+ mpattrlen_pos = bgp_packet_mpattr_start(
+ snlri, peer, afi, safi, &vecarr,
+ adv->baa->attr);
+
+ bgp_packet_mpattr_prefix(snlri, afi, safi, dest_p, prd,
+ label_pnt, num_labels,
+ addpath_capable, addpath_tx_id,
+ adv->baa->attr);
+ }
+
+ num_pfx++;
+
+ if (bgp_debug_update(NULL, dest_p, subgrp->update_group, 0)) {
+ char pfx_buf[BGP_PRD_PATH_STRLEN];
+
+ if (!send_attr_printed) {
+ zlog_debug("u%" PRIu64 ":s%" PRIu64" send UPDATE w/ attr: %s",
+ subgrp->update_group->id, subgrp->id,
+ send_attr_str);
+ if (!stream_empty(snlri)) {
+ iana_afi_t pkt_afi;
+ iana_safi_t pkt_safi;
+
+ pkt_afi = afi_int2iana(afi);
+ pkt_safi = safi_int2iana(safi);
+ zlog_debug(
+ "u%" PRIu64 ":s%" PRIu64
+ " send MP_REACH for afi/safi %s/%s",
+ subgrp->update_group->id,
+ subgrp->id,
+ iana_afi2str(pkt_afi),
+ iana_safi2str(pkt_safi));
+ }
+
+ send_attr_printed = 1;
+ }
+
+ bgp_debug_rdpfxpath2str(afi, safi, prd, dest_p,
+ label_pnt, num_labels,
+ addpath_capable, addpath_tx_id,
+ &adv->baa->attr->evpn_overlay,
+ pfx_buf, sizeof(pfx_buf));
+ zlog_debug("u%" PRIu64 ":s%" PRIu64 " send UPDATE %s",
+ subgrp->update_group->id, subgrp->id,
+ pfx_buf);
+ }
+
+ /* Synchnorize attribute. */
+ if (adj->attr)
+ bgp_attr_unintern(&adj->attr);
+ else
+ subgrp->scount++;
+
+ adj->attr = bgp_attr_intern(adv->baa->attr);
+ adv = bgp_advertise_clean_subgroup(subgrp, adj);
+ }
+
+ if (!stream_empty(s)) {
+ if (!stream_empty(snlri)) {
+ bgp_packet_mpattr_end(snlri, mpattrlen_pos);
+ total_attr_len += stream_get_endp(snlri);
+ }
+
+ /* set the total attribute length correctly */
+ stream_putw_at(s, attrlen_pos, total_attr_len);
+
+ if (!stream_empty(snlri)) {
+ packet = stream_dupcat(s, snlri, mpattr_pos);
+ bpacket_attr_vec_arr_update(&vecarr, mpattr_pos);
+ } else
+ packet = stream_dup(s);
+ bgp_packet_set_size(packet);
+ if (bgp_debug_update(NULL, NULL, subgrp->update_group, 0))
+ zlog_debug(
+ "u%" PRIu64 ":s%" PRIu64
+ " send UPDATE len %zd (max message len: %hu) numpfx %d",
+ subgrp->update_group->id, subgrp->id,
+ (stream_get_endp(packet)
+ - stream_get_getp(packet)),
+ peer->max_packet_size, num_pfx);
+ pkt = bpacket_queue_add(SUBGRP_PKTQ(subgrp), packet, &vecarr);
+ stream_reset(s);
+ stream_reset(snlri);
+ return pkt;
+ }
+ return NULL;
+}
+
+/* Make BGP withdraw packet. */
+/* For ipv4 unicast:
+ 16-octet marker | 2-octet length | 1-octet type |
+ 2-octet withdrawn route length | withdrawn prefixes | 2-octet attrlen (=0)
+*/
+/* For other afi/safis:
+ 16-octet marker | 2-octet length | 1-octet type |
+ 2-octet withdrawn route length (=0) | 2-octet attrlen |
+ mp_unreach attr type | attr len | afi | safi | withdrawn prefixes
+*/
+struct bpacket *subgroup_withdraw_packet(struct update_subgroup *subgrp)
+{
+ struct bpacket *pkt;
+ struct stream *s;
+ struct bgp_adj_out *adj;
+ struct bgp_advertise *adv;
+ struct peer *peer;
+ struct bgp_dest *dest;
+ bgp_size_t unfeasible_len;
+ bgp_size_t total_attr_len;
+ size_t mp_start = 0;
+ size_t attrlen_pos = 0;
+ size_t mplen_pos = 0;
+ uint8_t first_time = 1;
+ afi_t afi;
+ safi_t safi;
+ int space_remaining = 0;
+ int space_needed = 0;
+ int num_pfx = 0;
+ bool addpath_capable = false;
+ int addpath_overhead = 0;
+ uint32_t addpath_tx_id = 0;
+ const struct prefix_rd *prd = NULL;
+
+
+ if (!subgrp)
+ return NULL;
+
+ if (bpacket_queue_is_full(SUBGRP_INST(subgrp), SUBGRP_PKTQ(subgrp)))
+ return NULL;
+
+ peer = SUBGRP_PEER(subgrp);
+ afi = SUBGRP_AFI(subgrp);
+ safi = SUBGRP_SAFI(subgrp);
+ s = subgrp->work;
+ stream_reset(s);
+ addpath_capable = bgp_addpath_encode_tx(peer, afi, safi);
+ addpath_overhead = addpath_capable ? BGP_ADDPATH_ID_LEN : 0;
+
+ while ((adv = bgp_adv_fifo_first(&subgrp->sync->withdraw)) != NULL) {
+ const struct prefix *dest_p;
+
+ assert(adv->dest);
+ adj = adv->adj;
+ dest = adv->dest;
+ dest_p = bgp_dest_get_prefix(dest);
+ addpath_tx_id = adj->addpath_tx_id;
+
+ space_remaining =
+ STREAM_WRITEABLE(s) - BGP_MAX_PACKET_SIZE_OVERFLOW;
+ space_needed =
+ BGP_NLRI_LENGTH + addpath_overhead + BGP_TOTAL_ATTR_LEN
+ + bgp_packet_mpattr_prefix_size(afi, safi, dest_p);
+
+ if (space_remaining < space_needed)
+ break;
+
+ if (stream_empty(s)) {
+ bgp_packet_set_marker(s, BGP_MSG_UPDATE);
+ stream_putw(s, 0); /* unfeasible routes length */
+ } else
+ first_time = 0;
+
+ if (afi == AFI_IP && safi == SAFI_UNICAST
+ && !peer_cap_enhe(peer, afi, safi))
+ stream_put_prefix_addpath(s, dest_p, addpath_capable,
+ addpath_tx_id);
+ else {
+ if (dest->pdest)
+ prd = (struct prefix_rd *)bgp_dest_get_prefix(
+ dest->pdest);
+
+ /* If first time, format the MP_UNREACH header
+ */
+ if (first_time) {
+ iana_afi_t pkt_afi;
+ iana_safi_t pkt_safi;
+
+ pkt_afi = afi_int2iana(afi);
+ pkt_safi = safi_int2iana(safi);
+
+ attrlen_pos = stream_get_endp(s);
+ /* total attr length = 0 for now.
+ * reevaluate later */
+ stream_putw(s, 0);
+ mp_start = stream_get_endp(s);
+ mplen_pos = bgp_packet_mpunreach_start(s, afi,
+ safi);
+ if (bgp_debug_update(NULL, NULL,
+ subgrp->update_group, 0))
+ zlog_debug(
+ "u%" PRIu64 ":s%" PRIu64
+ " send MP_UNREACH for afi/safi %s/%s",
+ subgrp->update_group->id,
+ subgrp->id,
+ iana_afi2str(pkt_afi),
+ iana_safi2str(pkt_safi));
+ }
+
+ bgp_packet_mpunreach_prefix(s, dest_p, afi, safi, prd,
+ NULL, 0, addpath_capable,
+ addpath_tx_id, NULL);
+ }
+
+ num_pfx++;
+
+ if (bgp_debug_update(NULL, dest_p, subgrp->update_group, 0)) {
+ char pfx_buf[BGP_PRD_PATH_STRLEN];
+
+ bgp_debug_rdpfxpath2str(afi, safi, prd, dest_p, NULL, 0,
+ addpath_capable, addpath_tx_id,
+ NULL, pfx_buf, sizeof(pfx_buf));
+ zlog_debug("u%" PRIu64 ":s%" PRIu64" send UPDATE %s -- unreachable",
+ subgrp->update_group->id, subgrp->id,
+ pfx_buf);
+ }
+
+ subgrp->scount--;
+
+ bgp_adj_out_remove_subgroup(dest, adj, subgrp);
+ }
+
+ if (!stream_empty(s)) {
+ if (afi == AFI_IP && safi == SAFI_UNICAST
+ && !peer_cap_enhe(peer, afi, safi)) {
+ unfeasible_len = stream_get_endp(s) - BGP_HEADER_SIZE
+ - BGP_UNFEASIBLE_LEN;
+ stream_putw_at(s, BGP_HEADER_SIZE, unfeasible_len);
+ stream_putw(s, 0);
+ } else {
+ /* Set the mp_unreach attr's length */
+ bgp_packet_mpunreach_end(s, mplen_pos);
+
+ /* Set total path attribute length. */
+ total_attr_len = stream_get_endp(s) - mp_start;
+ stream_putw_at(s, attrlen_pos, total_attr_len);
+ }
+ bgp_packet_set_size(s);
+ if (bgp_debug_update(NULL, NULL, subgrp->update_group, 0))
+ zlog_debug("u%" PRIu64 ":s%" PRIu64" send UPDATE (withdraw) len %zd numpfx %d",
+ subgrp->update_group->id, subgrp->id,
+ (stream_get_endp(s) - stream_get_getp(s)),
+ num_pfx);
+ pkt = bpacket_queue_add(SUBGRP_PKTQ(subgrp), stream_dup(s),
+ NULL);
+ stream_reset(s);
+ return pkt;
+ }
+
+ return NULL;
+}
+
+void subgroup_default_update_packet(struct update_subgroup *subgrp,
+ struct attr *attr, struct peer *from)
+{
+ struct stream *s;
+ struct peer *peer;
+ struct prefix p;
+ unsigned long pos;
+ bgp_size_t total_attr_len;
+ afi_t afi;
+ safi_t safi;
+ struct bpacket_attr_vec_arr vecarr;
+ bool addpath_capable = false;
+
+ if (DISABLE_BGP_ANNOUNCE)
+ return;
+
+ if (!subgrp)
+ return;
+
+ peer = SUBGRP_PEER(subgrp);
+ afi = SUBGRP_AFI(subgrp);
+ safi = SUBGRP_SAFI(subgrp);
+ bpacket_attr_vec_arr_reset(&vecarr);
+ addpath_capable = bgp_addpath_encode_tx(peer, afi, safi);
+
+ memset(&p, 0, sizeof(p));
+ p.family = afi2family(afi);
+ p.prefixlen = 0;
+
+ /* Logging the attribute. */
+ if (bgp_debug_update(NULL, &p, subgrp->update_group, 0)) {
+ char attrstr[BUFSIZ];
+ /* ' with addpath ID ' 17
+ * max strlen of uint32 + 10
+ * +/- (just in case) + 1
+ * null terminator + 1
+ * ============================ 29 */
+ char tx_id_buf[30];
+
+ attrstr[0] = '\0';
+
+ bgp_dump_attr(attr, attrstr, sizeof(attrstr));
+
+ if (addpath_capable)
+ snprintf(tx_id_buf, sizeof(tx_id_buf),
+ " with addpath ID %u",
+ BGP_ADDPATH_TX_ID_FOR_DEFAULT_ORIGINATE);
+ else
+ tx_id_buf[0] = '\0';
+
+ zlog_debug("u%" PRIu64 ":s%" PRIu64 " send UPDATE %pFX%s %s",
+ (SUBGRP_UPDGRP(subgrp))->id, subgrp->id, &p,
+ tx_id_buf, attrstr);
+ }
+
+ s = stream_new(peer->max_packet_size);
+
+ /* Make BGP update packet. */
+ bgp_packet_set_marker(s, BGP_MSG_UPDATE);
+
+ /* Unfeasible Routes Length. */
+ stream_putw(s, 0);
+
+ /* Make place for total attribute length. */
+ pos = stream_get_endp(s);
+ stream_putw(s, 0);
+ total_attr_len = bgp_packet_attribute(
+ NULL, peer, s, attr, &vecarr, &p, afi, safi, from, NULL, NULL,
+ 0, addpath_capable, BGP_ADDPATH_TX_ID_FOR_DEFAULT_ORIGINATE);
+
+ /* Set Total Path Attribute Length. */
+ stream_putw_at(s, pos, total_attr_len);
+
+ /* NLRI set. */
+ if (p.family == AF_INET && safi == SAFI_UNICAST
+ && !peer_cap_enhe(peer, afi, safi))
+ stream_put_prefix_addpath(
+ s, &p, addpath_capable,
+ BGP_ADDPATH_TX_ID_FOR_DEFAULT_ORIGINATE);
+
+ /* Set size. */
+ bgp_packet_set_size(s);
+
+ (void)bpacket_queue_add(SUBGRP_PKTQ(subgrp), s, &vecarr);
+ subgroup_trigger_write(subgrp);
+
+ if (!CHECK_FLAG(subgrp->sflags,
+ SUBGRP_STATUS_PEER_DEFAULT_ORIGINATED)) {
+ subgrp->scount++;
+ SET_FLAG(subgrp->sflags, SUBGRP_STATUS_PEER_DEFAULT_ORIGINATED);
+ }
+}
+
+void subgroup_default_withdraw_packet(struct update_subgroup *subgrp)
+{
+ struct peer *peer;
+ struct stream *s;
+ struct prefix p;
+ unsigned long attrlen_pos = 0;
+ unsigned long cp;
+ bgp_size_t unfeasible_len;
+ bgp_size_t total_attr_len = 0;
+ size_t mp_start = 0;
+ size_t mplen_pos = 0;
+ afi_t afi;
+ safi_t safi;
+ bool addpath_capable = false;
+
+ if (DISABLE_BGP_ANNOUNCE)
+ return;
+
+ peer = SUBGRP_PEER(subgrp);
+ afi = SUBGRP_AFI(subgrp);
+ safi = SUBGRP_SAFI(subgrp);
+ addpath_capable = bgp_addpath_encode_tx(peer, afi, safi);
+
+ memset(&p, 0, sizeof(p));
+ p.family = afi2family(afi);
+ p.prefixlen = 0;
+
+ if (bgp_debug_update(NULL, &p, subgrp->update_group, 0)) {
+ /* ' with addpath ID ' 17
+ * max strlen of uint32 + 10
+ * +/- (just in case) + 1
+ * null terminator + 1
+ * ============================ 29 */
+ char tx_id_buf[30];
+
+ if (addpath_capable)
+ snprintf(tx_id_buf, sizeof(tx_id_buf),
+ " with addpath ID %u",
+ BGP_ADDPATH_TX_ID_FOR_DEFAULT_ORIGINATE);
+
+ zlog_debug("u%" PRIu64 ":s%" PRIu64
+ " send UPDATE %pFX%s -- unreachable",
+ (SUBGRP_UPDGRP(subgrp))->id, subgrp->id, &p,
+ tx_id_buf);
+ }
+
+ s = stream_new(peer->max_packet_size);
+
+ /* Make BGP update packet. */
+ bgp_packet_set_marker(s, BGP_MSG_UPDATE);
+
+ /* Unfeasible Routes Length. */;
+ cp = stream_get_endp(s);
+ stream_putw(s, 0);
+
+ /* Withdrawn Routes. */
+ if (p.family == AF_INET && safi == SAFI_UNICAST
+ && !peer_cap_enhe(peer, afi, safi)) {
+ stream_put_prefix_addpath(
+ s, &p, addpath_capable,
+ BGP_ADDPATH_TX_ID_FOR_DEFAULT_ORIGINATE);
+
+ unfeasible_len = stream_get_endp(s) - cp - 2;
+
+ /* Set unfeasible len. */
+ stream_putw_at(s, cp, unfeasible_len);
+
+ /* Set total path attribute length. */
+ stream_putw(s, 0);
+ } else {
+ attrlen_pos = stream_get_endp(s);
+ stream_putw(s, 0);
+ mp_start = stream_get_endp(s);
+ mplen_pos = bgp_packet_mpunreach_start(s, afi, safi);
+ bgp_packet_mpunreach_prefix(
+ s, &p, afi, safi, NULL, NULL, 0, addpath_capable,
+ BGP_ADDPATH_TX_ID_FOR_DEFAULT_ORIGINATE, NULL);
+
+ /* Set the mp_unreach attr's length */
+ bgp_packet_mpunreach_end(s, mplen_pos);
+
+ /* Set total path attribute length. */
+ total_attr_len = stream_get_endp(s) - mp_start;
+ stream_putw_at(s, attrlen_pos, total_attr_len);
+ }
+
+ bgp_packet_set_size(s);
+
+ (void)bpacket_queue_add(SUBGRP_PKTQ(subgrp), s, NULL);
+ subgroup_trigger_write(subgrp);
+
+ if (CHECK_FLAG(subgrp->sflags, SUBGRP_STATUS_PEER_DEFAULT_ORIGINATED)) {
+ subgrp->scount--;
+ UNSET_FLAG(subgrp->sflags,
+ SUBGRP_STATUS_PEER_DEFAULT_ORIGINATED);
+ }
+}
+
+static void
+bpacket_vec_arr_inherit_attr_flags(struct bpacket_attr_vec_arr *vecarr,
+ enum bpacket_attr_vec_type type,
+ struct attr *attr)
+{
+ if (CHECK_FLAG(attr->rmap_change_flags,
+ BATTR_RMAP_NEXTHOP_PEER_ADDRESS))
+ SET_FLAG(vecarr->entries[BGP_ATTR_VEC_NH].flags,
+ BPKT_ATTRVEC_FLAGS_RMAP_NH_PEER_ADDRESS);
+
+ if (CHECK_FLAG(attr->rmap_change_flags, BATTR_REFLECTED))
+ SET_FLAG(vecarr->entries[BGP_ATTR_VEC_NH].flags,
+ BPKT_ATTRVEC_FLAGS_REFLECTED);
+
+ if (CHECK_FLAG(attr->rmap_change_flags, BATTR_RMAP_NEXTHOP_UNCHANGED))
+ SET_FLAG(vecarr->entries[BGP_ATTR_VEC_NH].flags,
+ BPKT_ATTRVEC_FLAGS_RMAP_NH_UNCHANGED);
+
+ if (CHECK_FLAG(attr->rmap_change_flags, BATTR_RMAP_IPV4_NHOP_CHANGED))
+ SET_FLAG(vecarr->entries[BGP_ATTR_VEC_NH].flags,
+ BPKT_ATTRVEC_FLAGS_RMAP_IPV4_NH_CHANGED);
+
+ if (CHECK_FLAG(attr->rmap_change_flags,
+ BATTR_RMAP_IPV6_GLOBAL_NHOP_CHANGED))
+ SET_FLAG(vecarr->entries[BGP_ATTR_VEC_NH].flags,
+ BPKT_ATTRVEC_FLAGS_RMAP_IPV6_GNH_CHANGED);
+
+ if (CHECK_FLAG(attr->rmap_change_flags,
+ BATTR_RMAP_IPV6_LL_NHOP_CHANGED))
+ SET_FLAG(vecarr->entries[BGP_ATTR_VEC_NH].flags,
+ BPKT_ATTRVEC_FLAGS_RMAP_IPV6_LNH_CHANGED);
+}
+
+/* Reset the Attributes vector array. The vector array is used to override
+ * certain output parameters in the packet for a particular peer
+ */
+void bpacket_attr_vec_arr_reset(struct bpacket_attr_vec_arr *vecarr)
+{
+ int i;
+
+ if (!vecarr)
+ return;
+
+ i = 0;
+ while (i < BGP_ATTR_VEC_MAX) {
+ vecarr->entries[i].flags = 0;
+ vecarr->entries[i].offset = 0;
+ i++;
+ }
+}
+
+/* Setup a particular node entry in the vecarr */
+void bpacket_attr_vec_arr_set_vec(struct bpacket_attr_vec_arr *vecarr,
+ enum bpacket_attr_vec_type type,
+ struct stream *s, struct attr *attr)
+{
+ if (!vecarr)
+ return;
+ assert(type < BGP_ATTR_VEC_MAX);
+
+ SET_FLAG(vecarr->entries[type].flags, BPKT_ATTRVEC_FLAGS_UPDATED);
+ vecarr->entries[type].offset = stream_get_endp(s);
+ if (attr)
+ bpacket_vec_arr_inherit_attr_flags(vecarr, type, attr);
+}
diff --git a/bgpd/bgp_vnc_types.h b/bgpd/bgp_vnc_types.h
new file mode 100644
index 0000000..04847ce
--- /dev/null
+++ b/bgpd/bgp_vnc_types.h
@@ -0,0 +1,29 @@
+/*
+ * Copyright 2015-2016, LabN Consulting, L.L.C.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program; see the file COPYING; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
+ */
+
+#ifndef _QUAGGA_BGP_VNC_TYPES_H
+#define _QUAGGA_BGP_VNC_TYPES_H
+
+#ifdef ENABLE_BGP_VNC
+typedef enum {
+ BGP_VNC_SUBTLV_TYPE_LIFETIME = 1,
+ BGP_VNC_SUBTLV_TYPE_RFPOPTION = 2, /* deprecated */
+} bgp_vnc_subtlv_types;
+
+#endif /* ENABLE_BGP_VNC */
+#endif /* _QUAGGA_BGP_VNC_TYPES_H */
diff --git a/bgpd/bgp_vpn.c b/bgpd/bgp_vpn.c
new file mode 100644
index 0000000..6308936
--- /dev/null
+++ b/bgpd/bgp_vpn.c
@@ -0,0 +1,247 @@
+/* VPN Related functions
+ * Copyright (C) 2017 6WIND
+ *
+ * This file is part of FRRouting
+ *
+ * FRRouting is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License as published by the
+ * Free Software Foundation; either version 2, or (at your option) any
+ * later version.
+ *
+ * FRRouting is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program; see the file COPYING; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
+ */
+
+#include <zebra.h>
+#include "command.h"
+#include "prefix.h"
+#include "lib/json.h"
+#include "lib/printfrr.h"
+
+#include "bgpd/bgpd.h"
+#include "bgpd/bgp_route.h"
+#include "bgpd/bgp_table.h"
+#include "bgpd/bgp_attr.h"
+#include "bgpd/bgp_mplsvpn.h"
+#include "bgpd/bgp_vpn.h"
+#include "bgpd/bgp_updgrp.h"
+
+int show_adj_route_vpn(struct vty *vty, struct peer *peer,
+ struct prefix_rd *prd, afi_t afi, safi_t safi,
+ bool use_json)
+{
+ struct bgp *bgp;
+ struct bgp_table *table;
+ struct bgp_dest *dest;
+ struct bgp_dest *rm;
+ int rd_header;
+ int header = 1;
+ json_object *json = NULL;
+ json_object *json_scode = NULL;
+ json_object *json_ocode = NULL;
+ json_object *json_adv = NULL;
+ json_object *json_routes = NULL;
+ char rd_str[BUFSIZ];
+ unsigned long output_count = 0;
+
+ 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;
+ }
+
+ if (use_json) {
+ json_scode = json_object_new_object();
+ json_ocode = json_object_new_object();
+ json = json_object_new_object();
+ json_adv = json_object_new_object();
+
+ 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, "internal", "i");
+
+ json_object_string_add(json_ocode, "igp", "i");
+ json_object_string_add(json_ocode, "egp", "e");
+ json_object_string_add(json_ocode, "incomplete", "?");
+ }
+
+ for (dest = bgp_table_top(bgp->rib[afi][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 == NULL)
+ continue;
+
+ /*
+ * Initialize variables for each RD
+ * All prefixes under an RD is aggregated within "json_routes"
+ */
+ rd_header = 1;
+ memset(rd_str, 0, sizeof(rd_str));
+ json_routes = NULL;
+
+ for (rm = bgp_table_top(table); rm; rm = bgp_route_next(rm)) {
+ struct bgp_adj_out *adj = NULL;
+ struct attr *attr = NULL;
+ struct peer_af *paf = NULL;
+
+ RB_FOREACH (adj, bgp_adj_out_rb, &rm->adj_out)
+ SUBGRP_FOREACH_PEER (adj->subgroup, paf) {
+ if (paf->peer != peer || !adj->attr)
+ continue;
+
+ attr = adj->attr;
+ break;
+ }
+
+ if (bgp_dest_get_bgp_path_info(rm) == NULL)
+ continue;
+
+ if (!attr)
+ continue;
+
+ if (header) {
+ if (use_json) {
+ json_object_int_add(
+ json, "bgpTableVersion", 0);
+ 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",
+ bgp->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 0, local router ID is %pI4\n",
+ &bgp->router_id);
+ vty_out(vty, "Default local pref %u, ",
+ bgp->default_local_pref);
+ vty_out(vty, "local AS %u\n", bgp->as);
+ vty_out(vty,
+ "Status codes: s suppressed, d damped, h history, * valid, > best, i - internal\n");
+ vty_out(vty,
+ "Origin codes: i - IGP, e - EGP, ? - incomplete\n\n");
+ vty_out(vty, V4_HEADER);
+ }
+ header = 0;
+ }
+
+ if (rd_header) {
+ uint16_t type;
+ struct rd_as rd_as = {0};
+ struct rd_ip rd_ip = {0};
+#ifdef ENABLE_BGP_VNC
+ struct rd_vnc_eth rd_vnc_eth = {0};
+#endif
+ const uint8_t *pnt;
+
+ pnt = dest_p->u.val;
+
+ /* Decode RD type. */
+ type = decode_rd_type(pnt);
+ /* Decode RD value. */
+ if (type == RD_TYPE_AS)
+ decode_rd_as(pnt + 2, &rd_as);
+ else if (type == RD_TYPE_AS4)
+ decode_rd_as4(pnt + 2, &rd_as);
+ else if (type == RD_TYPE_IP)
+ decode_rd_ip(pnt + 2, &rd_ip);
+#ifdef ENABLE_BGP_VNC
+ else if (type == RD_TYPE_VNC_ETH)
+ decode_rd_vnc_eth(pnt, &rd_vnc_eth);
+#endif
+ if (use_json) {
+ json_routes = json_object_new_object();
+
+ if (type == RD_TYPE_AS
+ || type == RD_TYPE_AS4)
+ snprintf(rd_str, sizeof(rd_str),
+ "%u:%d", rd_as.as,
+ rd_as.val);
+ else if (type == RD_TYPE_IP)
+ snprintfrr(rd_str,
+ sizeof(rd_str),
+ "%pI4:%d", &rd_ip.ip,
+ rd_ip.val);
+ json_object_string_add(
+ json_routes,
+ "rd", rd_str);
+ } else {
+ vty_out(vty, "Route Distinguisher: ");
+
+ if (type == RD_TYPE_AS
+ || type == RD_TYPE_AS4)
+ vty_out(vty, "%u:%d", rd_as.as,
+ rd_as.val);
+ else if (type == RD_TYPE_IP)
+ vty_out(vty, "%pI4:%d",
+ &rd_ip.ip, rd_ip.val);
+#ifdef ENABLE_BGP_VNC
+ else if (type == RD_TYPE_VNC_ETH)
+ vty_out(vty,
+ "%u:%02x:%02x:%02x:%02x:%02x:%02x",
+ rd_vnc_eth.local_nve_id,
+ rd_vnc_eth.macaddr
+ .octet[0],
+ rd_vnc_eth.macaddr
+ .octet[1],
+ rd_vnc_eth.macaddr
+ .octet[2],
+ rd_vnc_eth.macaddr
+ .octet[3],
+ rd_vnc_eth.macaddr
+ .octet[4],
+ rd_vnc_eth.macaddr
+ .octet[5]);
+#endif
+
+ vty_out(vty, "\n");
+ }
+ rd_header = 0;
+ }
+ route_vty_out_tmp(vty, rm, bgp_dest_get_prefix(rm),
+ attr, safi, use_json, json_routes,
+ false);
+ output_count++;
+ }
+
+ if (use_json && json_routes)
+ json_object_object_add(json_adv, rd_str, json_routes);
+ }
+
+ if (use_json) {
+ json_object_object_add(json, "advertisedRoutes", json_adv);
+ json_object_int_add(json,
+ "totalPrefixCounter", output_count);
+ vty_json(vty, json);
+ } else
+ vty_out(vty, "\nTotal number of prefixes %ld\n", output_count);
+
+ return CMD_SUCCESS;
+}
diff --git a/bgpd/bgp_vpn.h b/bgpd/bgp_vpn.h
new file mode 100644
index 0000000..a2e8647
--- /dev/null
+++ b/bgpd/bgp_vpn.h
@@ -0,0 +1,30 @@
+/* VPN common functions to MP-BGP
+ * Copyright (C) 2017 6WIND
+ *
+ * This file is part of FRRouting.
+ *
+ * FRRouting is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License as published by the
+ * Free Software Foundation; either version 2, or (at your option) any
+ * later version.
+ *
+ * FRRouting is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program; see the file COPYING; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
+ */
+
+#ifndef _FRR_BGP_VPN_H
+#define _FRR_BGP_VPN_H
+
+#include <zebra.h>
+
+extern int show_adj_route_vpn(struct vty *vty, struct peer *peer,
+ struct prefix_rd *prd, afi_t afi, safi_t safi,
+ bool use_json);
+
+#endif /* _QUAGGA_BGP_VPN_H */
diff --git a/bgpd/bgp_vty.c b/bgpd/bgp_vty.c
new file mode 100644
index 0000000..b80d5f6
--- /dev/null
+++ b/bgpd/bgp_vty.c
@@ -0,0 +1,20867 @@
+/* BGP VTY interface.
+ * Copyright (C) 1996, 97, 98, 99, 2000 Kunihiro Ishiguro
+ *
+ * This file is part of GNU Zebra.
+ *
+ * GNU Zebra is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License as published by the
+ * Free Software Foundation; either version 2, or (at your option) any
+ * later version.
+ *
+ * GNU Zebra is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program; see the file COPYING; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
+ */
+
+#include <zebra.h>
+
+#include "command.h"
+#include "lib/json.h"
+#include "lib/sockopt.h"
+#include "lib_errors.h"
+#include "lib/zclient.h"
+#include "lib/printfrr.h"
+#include "prefix.h"
+#include "plist.h"
+#include "buffer.h"
+#include "linklist.h"
+#include "stream.h"
+#include "thread.h"
+#include "log.h"
+#include "memory.h"
+#include "lib_vty.h"
+#include "hash.h"
+#include "queue.h"
+#include "filter.h"
+#include "frrstr.h"
+
+#include "bgpd/bgpd.h"
+#include "bgpd/bgp_attr_evpn.h"
+#include "bgpd/bgp_advertise.h"
+#include "bgpd/bgp_attr.h"
+#include "bgpd/bgp_aspath.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_damp.h"
+#include "bgpd/bgp_debug.h"
+#include "bgpd/bgp_errors.h"
+#include "bgpd/bgp_fsm.h"
+#include "bgpd/bgp_nht.h"
+#include "bgpd/bgp_nexthop.h"
+#include "bgpd/bgp_network.h"
+#include "bgpd/bgp_open.h"
+#include "bgpd/bgp_regex.h"
+#include "bgpd/bgp_route.h"
+#include "bgpd/bgp_mplsvpn.h"
+#include "bgpd/bgp_zebra.h"
+#include "bgpd/bgp_table.h"
+#include "bgpd/bgp_vty.h"
+#include "bgpd/bgp_mpath.h"
+#include "bgpd/bgp_packet.h"
+#include "bgpd/bgp_updgrp.h"
+#include "bgpd/bgp_bfd.h"
+#include "bgpd/bgp_io.h"
+#include "bgpd/bgp_evpn.h"
+#include "bgpd/bgp_evpn_vty.h"
+#include "bgpd/bgp_evpn_mh.h"
+#include "bgpd/bgp_addpath.h"
+#include "bgpd/bgp_mac.h"
+#include "bgpd/bgp_flowspec.h"
+#include "bgpd/bgp_conditional_adv.h"
+#ifdef ENABLE_BGP_VNC
+#include "bgpd/rfapi/bgp_rfapi_cfg.h"
+#endif
+
+FRR_CFG_DEFAULT_BOOL(BGP_IMPORT_CHECK,
+ {
+ .val_bool = false,
+ .match_profile = "traditional",
+ .match_version = "< 7.4",
+ },
+ { .val_bool = true },
+);
+FRR_CFG_DEFAULT_BOOL(BGP_SHOW_HOSTNAME,
+ { .val_bool = true, .match_profile = "datacenter", },
+ { .val_bool = false },
+);
+FRR_CFG_DEFAULT_BOOL(BGP_SHOW_NEXTHOP_HOSTNAME,
+ { .val_bool = true, .match_profile = "datacenter", },
+ { .val_bool = false },
+);
+FRR_CFG_DEFAULT_BOOL(BGP_LOG_NEIGHBOR_CHANGES,
+ { .val_bool = true, .match_profile = "datacenter", },
+ { .val_bool = false },
+);
+FRR_CFG_DEFAULT_BOOL(BGP_DETERMINISTIC_MED,
+ { .val_bool = true, .match_profile = "datacenter", },
+ { .val_bool = false },
+);
+FRR_CFG_DEFAULT_ULONG(BGP_CONNECT_RETRY,
+ { .val_ulong = 10, .match_profile = "datacenter", },
+ { .val_ulong = 120 },
+);
+FRR_CFG_DEFAULT_ULONG(BGP_HOLDTIME,
+ { .val_ulong = 9, .match_profile = "datacenter", },
+ { .val_ulong = 180 },
+);
+FRR_CFG_DEFAULT_ULONG(BGP_KEEPALIVE,
+ { .val_ulong = 3, .match_profile = "datacenter", },
+ { .val_ulong = 60 },
+);
+FRR_CFG_DEFAULT_BOOL(BGP_EBGP_REQUIRES_POLICY,
+ { .val_bool = false, .match_profile = "datacenter", },
+ { .val_bool = false, .match_version = "< 7.4", },
+ { .val_bool = true },
+);
+FRR_CFG_DEFAULT_BOOL(BGP_SUPPRESS_DUPLICATES,
+ { .val_bool = false, .match_version = "< 7.6", },
+ { .val_bool = true },
+);
+FRR_CFG_DEFAULT_BOOL(BGP_GRACEFUL_NOTIFICATION,
+ { .val_bool = false, .match_version = "< 8.3", },
+ { .val_bool = true },
+);
+FRR_CFG_DEFAULT_BOOL(BGP_HARD_ADMIN_RESET,
+ { .val_bool = false, .match_version = "< 8.3", },
+ { .val_bool = true },
+);
+
+DEFINE_HOOK(bgp_inst_config_write,
+ (struct bgp *bgp, struct vty *vty),
+ (bgp, vty));
+DEFINE_HOOK(bgp_snmp_update_last_changed, (struct bgp *bgp), (bgp));
+DEFINE_HOOK(bgp_snmp_init_stats, (struct bgp *bgp), (bgp));
+
+static struct peer_group *listen_range_exists(struct bgp *bgp,
+ struct prefix *range, int exact);
+
+/* Show BGP peer's information. */
+enum show_type {
+ show_all,
+ show_peer,
+ show_ipv4_all,
+ show_ipv6_all,
+ show_ipv4_peer,
+ show_ipv6_peer
+};
+
+static struct peer_group *listen_range_exists(struct bgp *bgp,
+ struct prefix *range, int exact);
+
+static void bgp_show_global_graceful_restart_mode_vty(struct vty *vty,
+ struct bgp *bgp,
+ bool use_json,
+ json_object *json);
+
+static int bgp_show_neighbor_graceful_restart_afi_all(struct vty *vty,
+ enum show_type type,
+ const char *ip_str,
+ afi_t afi, bool use_json);
+
+static enum node_type bgp_node_type(afi_t afi, safi_t safi)
+{
+ switch (afi) {
+ case AFI_IP:
+ switch (safi) {
+ case SAFI_UNICAST:
+ return BGP_IPV4_NODE;
+ case SAFI_MULTICAST:
+ return BGP_IPV4M_NODE;
+ case SAFI_LABELED_UNICAST:
+ return BGP_IPV4L_NODE;
+ case SAFI_MPLS_VPN:
+ return BGP_VPNV4_NODE;
+ case SAFI_FLOWSPEC:
+ return BGP_FLOWSPECV4_NODE;
+ default:
+ /* not expected */
+ return BGP_IPV4_NODE;
+ }
+ case AFI_IP6:
+ switch (safi) {
+ case SAFI_UNICAST:
+ return BGP_IPV6_NODE;
+ case SAFI_MULTICAST:
+ return BGP_IPV6M_NODE;
+ case SAFI_LABELED_UNICAST:
+ return BGP_IPV6L_NODE;
+ case SAFI_MPLS_VPN:
+ return BGP_VPNV6_NODE;
+ case SAFI_FLOWSPEC:
+ return BGP_FLOWSPECV6_NODE;
+ default:
+ /* not expected */
+ return BGP_IPV4_NODE;
+ }
+ case AFI_L2VPN:
+ return BGP_EVPN_NODE;
+ case AFI_UNSPEC:
+ case AFI_MAX:
+ // We should never be here but to clarify the switch statement..
+ return BGP_IPV4_NODE;
+ }
+
+ // Impossible to happen
+ return BGP_IPV4_NODE;
+}
+
+static const char *get_afi_safi_vty_str(afi_t afi, safi_t safi)
+{
+ if (afi == AFI_IP) {
+ if (safi == SAFI_UNICAST)
+ return "IPv4 Unicast";
+ if (safi == SAFI_MULTICAST)
+ return "IPv4 Multicast";
+ if (safi == SAFI_LABELED_UNICAST)
+ return "IPv4 Labeled Unicast";
+ if (safi == SAFI_MPLS_VPN)
+ return "IPv4 VPN";
+ if (safi == SAFI_ENCAP)
+ return "IPv4 Encap";
+ if (safi == SAFI_FLOWSPEC)
+ return "IPv4 Flowspec";
+ } else if (afi == AFI_IP6) {
+ if (safi == SAFI_UNICAST)
+ return "IPv6 Unicast";
+ if (safi == SAFI_MULTICAST)
+ return "IPv6 Multicast";
+ if (safi == SAFI_LABELED_UNICAST)
+ return "IPv6 Labeled Unicast";
+ if (safi == SAFI_MPLS_VPN)
+ return "IPv6 VPN";
+ if (safi == SAFI_ENCAP)
+ return "IPv6 Encap";
+ if (safi == SAFI_FLOWSPEC)
+ return "IPv6 Flowspec";
+ } else if (afi == AFI_L2VPN) {
+ if (safi == SAFI_EVPN)
+ return "L2VPN EVPN";
+ }
+
+ return "Unknown";
+}
+
+/*
+ * Please note that we have intentionally camelCased
+ * the return strings here. So if you want
+ * to use this function, please ensure you
+ * are doing this within json output
+ */
+static const char *get_afi_safi_json_str(afi_t afi, safi_t safi)
+{
+ if (afi == AFI_IP) {
+ if (safi == SAFI_UNICAST)
+ return "ipv4Unicast";
+ if (safi == SAFI_MULTICAST)
+ return "ipv4Multicast";
+ if (safi == SAFI_LABELED_UNICAST)
+ return "ipv4LabeledUnicast";
+ if (safi == SAFI_MPLS_VPN)
+ return "ipv4Vpn";
+ if (safi == SAFI_ENCAP)
+ return "ipv4Encap";
+ if (safi == SAFI_FLOWSPEC)
+ return "ipv4Flowspec";
+ } else if (afi == AFI_IP6) {
+ if (safi == SAFI_UNICAST)
+ return "ipv6Unicast";
+ if (safi == SAFI_MULTICAST)
+ return "ipv6Multicast";
+ if (safi == SAFI_LABELED_UNICAST)
+ return "ipv6LabeledUnicast";
+ if (safi == SAFI_MPLS_VPN)
+ return "ipv6Vpn";
+ if (safi == SAFI_ENCAP)
+ return "ipv6Encap";
+ if (safi == SAFI_FLOWSPEC)
+ return "ipv6Flowspec";
+ } else if (afi == AFI_L2VPN) {
+ if (safi == SAFI_EVPN)
+ return "l2VpnEvpn";
+ }
+
+ return "Unknown";
+}
+
+/* unset srv6 locator */
+static int bgp_srv6_locator_unset(struct bgp *bgp)
+{
+ int ret;
+ struct listnode *node, *nnode;
+ struct srv6_locator_chunk *chunk;
+ struct bgp_srv6_function *func;
+ struct bgp *bgp_vrf;
+ struct in6_addr *tovpn_sid;
+
+ /* release chunk notification via ZAPI */
+ ret = bgp_zebra_srv6_manager_release_locator_chunk(
+ bgp->srv6_locator_name);
+ if (ret < 0)
+ return -1;
+
+ /* refresh chunks */
+ for (ALL_LIST_ELEMENTS(bgp->srv6_locator_chunks, node, nnode, chunk)) {
+ listnode_delete(bgp->srv6_locator_chunks, chunk);
+ srv6_locator_chunk_free(chunk);
+ }
+
+ /* refresh functions */
+ for (ALL_LIST_ELEMENTS(bgp->srv6_functions, node, nnode, func)) {
+ listnode_delete(bgp->srv6_functions, func);
+ XFREE(MTYPE_BGP_SRV6_FUNCTION, func);
+ }
+
+ /* refresh tovpn_sid */
+ for (ALL_LIST_ELEMENTS_RO(bm->bgp, node, bgp_vrf)) {
+ if (bgp_vrf->inst_type != BGP_INSTANCE_TYPE_VRF)
+ continue;
+
+ /* refresh vpnv4 tovpn_sid */
+ tovpn_sid = bgp_vrf->vpn_policy[AFI_IP].tovpn_sid;
+ if (tovpn_sid)
+ XFREE(MTYPE_BGP_SRV6_SID,
+ bgp_vrf->vpn_policy[AFI_IP].tovpn_sid);
+
+ /* refresh vpnv6 tovpn_sid */
+ tovpn_sid = bgp_vrf->vpn_policy[AFI_IP6].tovpn_sid;
+ if (tovpn_sid)
+ XFREE(MTYPE_BGP_SRV6_SID,
+ bgp_vrf->vpn_policy[AFI_IP6].tovpn_sid);
+ }
+
+ /* update vpn bgp processes */
+ vpn_leak_postchange_all();
+
+ /* refresh tovpn_sid_locator */
+ for (ALL_LIST_ELEMENTS_RO(bm->bgp, node, bgp_vrf)) {
+ if (bgp_vrf->inst_type != BGP_INSTANCE_TYPE_VRF)
+ continue;
+
+ /* refresh vpnv4 tovpn_sid_locator */
+ XFREE(MTYPE_BGP_SRV6_SID,
+ bgp_vrf->vpn_policy[AFI_IP].tovpn_sid_locator);
+
+ /* refresh vpnv6 tovpn_sid_locator */
+ XFREE(MTYPE_BGP_SRV6_SID,
+ bgp_vrf->vpn_policy[AFI_IP6].tovpn_sid_locator);
+ }
+
+ /* clear locator name */
+ memset(bgp->srv6_locator_name, 0, sizeof(bgp->srv6_locator_name));
+
+ return 0;
+}
+
+/* Utility function to get address family from current node. */
+afi_t bgp_node_afi(struct vty *vty)
+{
+ afi_t afi;
+ switch (vty->node) {
+ case BGP_IPV6_NODE:
+ case BGP_IPV6M_NODE:
+ case BGP_IPV6L_NODE:
+ case BGP_VPNV6_NODE:
+ case BGP_FLOWSPECV6_NODE:
+ afi = AFI_IP6;
+ break;
+ case BGP_EVPN_NODE:
+ afi = AFI_L2VPN;
+ break;
+ default:
+ afi = AFI_IP;
+ break;
+ }
+ return afi;
+}
+
+/* Utility function to get subsequent address family from current
+ node. */
+safi_t bgp_node_safi(struct vty *vty)
+{
+ safi_t safi;
+ switch (vty->node) {
+ case BGP_VPNV4_NODE:
+ case BGP_VPNV6_NODE:
+ safi = SAFI_MPLS_VPN;
+ break;
+ case BGP_IPV4M_NODE:
+ case BGP_IPV6M_NODE:
+ safi = SAFI_MULTICAST;
+ break;
+ case BGP_EVPN_NODE:
+ safi = SAFI_EVPN;
+ break;
+ case BGP_IPV4L_NODE:
+ case BGP_IPV6L_NODE:
+ safi = SAFI_LABELED_UNICAST;
+ break;
+ case BGP_FLOWSPECV4_NODE:
+ case BGP_FLOWSPECV6_NODE:
+ safi = SAFI_FLOWSPEC;
+ break;
+ default:
+ safi = SAFI_UNICAST;
+ break;
+ }
+ return safi;
+}
+
+/**
+ * Converts an AFI in string form to afi_t
+ *
+ * @param afi string, one of
+ * - "ipv4"
+ * - "ipv6"
+ * - "l2vpn"
+ * @return the corresponding afi_t
+ */
+afi_t bgp_vty_afi_from_str(const char *afi_str)
+{
+ afi_t afi = AFI_MAX; /* unknown */
+ if (strmatch(afi_str, "ipv4"))
+ afi = AFI_IP;
+ else if (strmatch(afi_str, "ipv6"))
+ afi = AFI_IP6;
+ else if (strmatch(afi_str, "l2vpn"))
+ afi = AFI_L2VPN;
+ return afi;
+}
+
+int argv_find_and_parse_afi(struct cmd_token **argv, int argc, int *index,
+ afi_t *afi)
+{
+ int ret = 0;
+ if (argv_find(argv, argc, "ipv4", index)) {
+ ret = 1;
+ if (afi)
+ *afi = AFI_IP;
+ } else if (argv_find(argv, argc, "ipv6", index)) {
+ ret = 1;
+ if (afi)
+ *afi = AFI_IP6;
+ } else if (argv_find(argv, argc, "l2vpn", index)) {
+ ret = 1;
+ if (afi)
+ *afi = AFI_L2VPN;
+ }
+ return ret;
+}
+
+/* supports <unicast|multicast|vpn|labeled-unicast> */
+safi_t bgp_vty_safi_from_str(const char *safi_str)
+{
+ safi_t safi = SAFI_MAX; /* unknown */
+ if (strmatch(safi_str, "multicast"))
+ safi = SAFI_MULTICAST;
+ else if (strmatch(safi_str, "unicast"))
+ safi = SAFI_UNICAST;
+ else if (strmatch(safi_str, "vpn"))
+ safi = SAFI_MPLS_VPN;
+ else if (strmatch(safi_str, "evpn"))
+ safi = SAFI_EVPN;
+ else if (strmatch(safi_str, "labeled-unicast"))
+ safi = SAFI_LABELED_UNICAST;
+ else if (strmatch(safi_str, "flowspec"))
+ safi = SAFI_FLOWSPEC;
+ return safi;
+}
+
+int argv_find_and_parse_safi(struct cmd_token **argv, int argc, int *index,
+ safi_t *safi)
+{
+ int ret = 0;
+ if (argv_find(argv, argc, "unicast", index)) {
+ ret = 1;
+ if (safi)
+ *safi = SAFI_UNICAST;
+ } else if (argv_find(argv, argc, "multicast", index)) {
+ ret = 1;
+ if (safi)
+ *safi = SAFI_MULTICAST;
+ } else if (argv_find(argv, argc, "labeled-unicast", index)) {
+ ret = 1;
+ if (safi)
+ *safi = SAFI_LABELED_UNICAST;
+ } else if (argv_find(argv, argc, "vpn", index)) {
+ ret = 1;
+ if (safi)
+ *safi = SAFI_MPLS_VPN;
+ } else if (argv_find(argv, argc, "evpn", index)) {
+ ret = 1;
+ if (safi)
+ *safi = SAFI_EVPN;
+ } else if (argv_find(argv, argc, "flowspec", index)) {
+ ret = 1;
+ if (safi)
+ *safi = SAFI_FLOWSPEC;
+ }
+ return ret;
+}
+
+/*
+ * Convert an afi_t/safi_t pair to matching BGP_DEFAULT_AF* flag.
+ *
+ * afi
+ * address-family identifier
+ *
+ * safi
+ * subsequent address-family identifier
+ *
+ * Returns:
+ * default_af string corresponding to the supplied afi/safi pair.
+ * If afi/safi is invalid or if flag for afi/safi doesn't exist,
+ * return -1.
+ */
+static const char *get_bgp_default_af_flag(afi_t afi, safi_t safi)
+{
+ switch (afi) {
+ case AFI_IP:
+ switch (safi) {
+ case SAFI_UNICAST:
+ return "ipv4-unicast";
+ case SAFI_MULTICAST:
+ return "ipv4-multicast";
+ case SAFI_MPLS_VPN:
+ return "ipv4-vpn";
+ case SAFI_ENCAP:
+ return "ipv4-encap";
+ case SAFI_LABELED_UNICAST:
+ return "ipv4-labeled-unicast";
+ case SAFI_FLOWSPEC:
+ return "ipv4-flowspec";
+ default:
+ return "unknown-afi/safi";
+ }
+ break;
+ case AFI_IP6:
+ switch (safi) {
+ case SAFI_UNICAST:
+ return "ipv6-unicast";
+ case SAFI_MULTICAST:
+ return "ipv6-multicast";
+ case SAFI_MPLS_VPN:
+ return "ipv6-vpn";
+ case SAFI_ENCAP:
+ return "ipv6-encap";
+ case SAFI_LABELED_UNICAST:
+ return "ipv6-labeled-unicast";
+ case SAFI_FLOWSPEC:
+ return "ipv6-flowspec";
+ default:
+ return "unknown-afi/safi";
+ }
+ break;
+ case AFI_L2VPN:
+ switch (safi) {
+ case SAFI_EVPN:
+ return "l2vpn-evpn";
+ default:
+ return "unknown-afi/safi";
+ }
+ case AFI_UNSPEC:
+ case AFI_MAX:
+ return "unknown-afi/safi";
+ }
+ /* all AFIs are accounted for above, so this shouldn't happen */
+ return "unknown-afi/safi";
+}
+
+int bgp_get_vty(struct bgp **bgp, as_t *as, const char *name,
+ enum bgp_instance_type inst_type)
+{
+ int ret = bgp_get(bgp, as, name, inst_type);
+
+ if (ret == BGP_CREATED) {
+ bgp_timers_set(*bgp, DFLT_BGP_KEEPALIVE, DFLT_BGP_HOLDTIME,
+ DFLT_BGP_CONNECT_RETRY, BGP_DEFAULT_DELAYOPEN);
+
+ if (DFLT_BGP_IMPORT_CHECK)
+ SET_FLAG((*bgp)->flags, BGP_FLAG_IMPORT_CHECK);
+ if (DFLT_BGP_SHOW_HOSTNAME)
+ SET_FLAG((*bgp)->flags, BGP_FLAG_SHOW_HOSTNAME);
+ if (DFLT_BGP_SHOW_NEXTHOP_HOSTNAME)
+ SET_FLAG((*bgp)->flags, BGP_FLAG_SHOW_NEXTHOP_HOSTNAME);
+ if (DFLT_BGP_LOG_NEIGHBOR_CHANGES)
+ SET_FLAG((*bgp)->flags, BGP_FLAG_LOG_NEIGHBOR_CHANGES);
+ if (DFLT_BGP_DETERMINISTIC_MED)
+ SET_FLAG((*bgp)->flags, BGP_FLAG_DETERMINISTIC_MED);
+ if (DFLT_BGP_EBGP_REQUIRES_POLICY)
+ SET_FLAG((*bgp)->flags, BGP_FLAG_EBGP_REQUIRES_POLICY);
+ if (DFLT_BGP_SUPPRESS_DUPLICATES)
+ SET_FLAG((*bgp)->flags, BGP_FLAG_SUPPRESS_DUPLICATES);
+ if (DFLT_BGP_GRACEFUL_NOTIFICATION)
+ SET_FLAG((*bgp)->flags, BGP_FLAG_GRACEFUL_NOTIFICATION);
+ if (DFLT_BGP_HARD_ADMIN_RESET)
+ SET_FLAG((*bgp)->flags, BGP_FLAG_HARD_ADMIN_RESET);
+
+ ret = BGP_SUCCESS;
+ }
+ return ret;
+}
+
+/*
+ * bgp_vty_find_and_parse_afi_safi_bgp
+ *
+ * For a given 'show ...' command, correctly parse the afi/safi/bgp out from it
+ * This function *assumes* that the calling function pre-sets the afi/safi/bgp
+ * to appropriate values for the calling function. This is to allow the
+ * calling function to make decisions appropriate for the show command
+ * that is being parsed.
+ *
+ * The show commands are generally of the form:
+ * "show [ip] bgp [<view|vrf> VIEWVRFNAME] [<ipv4|ipv6>
+ * [<unicast|multicast|vpn|labeled-unicast>]] ..."
+ *
+ * Since we use argv_find if the show command in particular doesn't have:
+ * [ip]
+ * [<view|vrf> VIEWVRFNAME]
+ * [<ipv4|ipv6> [<unicast|multicast|vpn|labeled-unicast>]]
+ * The command parsing should still be ok.
+ *
+ * vty -> The vty for the command so we can output some useful data in
+ * the event of a parse error in the vrf.
+ * argv -> The command tokens
+ * argc -> How many command tokens we have
+ * idx -> The current place in the command, generally should be 0 for this
+ * function
+ * afi -> The parsed afi if it was included in the show command, returned here
+ * safi -> The parsed safi if it was included in the show command, returned here
+ * bgp -> Pointer to the bgp data structure we need to fill in.
+ * use_json -> json is configured or not
+ *
+ * The function returns the correct location in the parse tree for the
+ * last token found.
+ *
+ * Returns 0 for failure to parse correctly, else the idx position of where
+ * it found the last token.
+ */
+int bgp_vty_find_and_parse_afi_safi_bgp(struct vty *vty,
+ struct cmd_token **argv, int argc,
+ int *idx, afi_t *afi, safi_t *safi,
+ struct bgp **bgp, bool use_json)
+{
+ char *vrf_name = NULL;
+
+ assert(afi);
+ assert(safi);
+ assert(bgp);
+
+ if (argv_find(argv, argc, "ip", idx))
+ *afi = AFI_IP;
+
+ if (argv_find(argv, argc, "view", idx))
+ vrf_name = argv[*idx + 1]->arg;
+ else if (argv_find(argv, argc, "vrf", idx)) {
+ vrf_name = argv[*idx + 1]->arg;
+ if (strmatch(vrf_name, VRF_DEFAULT_NAME))
+ vrf_name = NULL;
+ }
+ if (vrf_name) {
+ if (strmatch(vrf_name, "all"))
+ *bgp = NULL;
+ else {
+ *bgp = bgp_lookup_by_name(vrf_name);
+ if (!*bgp) {
+ if (use_json) {
+ json_object *json = NULL;
+ json = json_object_new_object();
+ json_object_string_add(
+ json, "warning",
+ "View/Vrf is unknown");
+ vty_json(vty, json);
+ }
+ else
+ vty_out(vty, "View/Vrf %s is unknown\n",
+ vrf_name);
+ *idx = 0;
+ return 0;
+ }
+ }
+ } else {
+ *bgp = bgp_get_default();
+ if (!*bgp) {
+ if (use_json) {
+ json_object *json = NULL;
+ json = json_object_new_object();
+ json_object_string_add(
+ json, "warning",
+ "Default BGP instance not found");
+ vty_json(vty, json);
+ }
+ else
+ vty_out(vty,
+ "Default BGP instance not found\n");
+ *idx = 0;
+ return 0;
+ }
+ }
+
+ if (argv_find_and_parse_afi(argv, argc, idx, afi))
+ argv_find_and_parse_safi(argv, argc, idx, safi);
+
+ *idx += 1;
+ return *idx;
+}
+
+static bool peer_address_self_check(struct bgp *bgp, union sockunion *su)
+{
+ struct interface *ifp = NULL;
+ struct listnode *node;
+ struct bgp_listener *listener;
+ union sockunion all_su;
+
+ if (su->sa.sa_family == AF_INET) {
+ (void)str2sockunion("0.0.0.0", &all_su);
+ ifp = if_lookup_by_ipv4_exact(&su->sin.sin_addr, bgp->vrf_id);
+ } else if (su->sa.sa_family == AF_INET6) {
+ (void)str2sockunion("::", &all_su);
+ ifp = if_lookup_by_ipv6_exact(&su->sin6.sin6_addr,
+ su->sin6.sin6_scope_id,
+ bgp->vrf_id);
+ }
+
+ if (ifp) {
+ for (ALL_LIST_ELEMENTS_RO(bm->listen_sockets, node, listener)) {
+ if (sockunion_family(su) !=
+ sockunion_family(&listener->su))
+ continue;
+
+ /* If 0.0.0.0/:: is a listener, then treat as self and
+ * reject.
+ */
+ if (!sockunion_cmp(&listener->su, su) ||
+ !sockunion_cmp(&listener->su, &all_su))
+ return true;
+ }
+ }
+
+ return false;
+}
+
+/* Utility function for looking up peer from VTY. */
+/* This is used only for configuration, so disallow if attempted on
+ * a dynamic neighbor.
+ */
+static struct peer *peer_lookup_vty(struct vty *vty, const char *ip_str)
+{
+ struct bgp *bgp = VTY_GET_CONTEXT(bgp);
+ int ret;
+ union sockunion su;
+ struct peer *peer;
+
+ if (!bgp) {
+ return NULL;
+ }
+
+ ret = str2sockunion(ip_str, &su);
+ if (ret < 0) {
+ peer = peer_lookup_by_conf_if(bgp, ip_str);
+ if (!peer) {
+ if ((peer = peer_lookup_by_hostname(bgp, ip_str))
+ == NULL) {
+ vty_out(vty,
+ "%% Malformed address or name: %s\n",
+ ip_str);
+ return NULL;
+ }
+ }
+ } else {
+ peer = peer_lookup(bgp, &su);
+ if (!peer) {
+ vty_out(vty,
+ "%% Specify remote-as or peer-group commands first\n");
+ return NULL;
+ }
+ if (peer_dynamic_neighbor(peer)) {
+ vty_out(vty,
+ "%% Operation not allowed on a dynamic neighbor\n");
+ return NULL;
+ }
+ }
+ return peer;
+}
+
+/* Utility function for looking up peer or peer group. */
+/* This is used only for configuration, so disallow if attempted on
+ * a dynamic neighbor.
+ */
+struct peer *peer_and_group_lookup_vty(struct vty *vty, const char *peer_str)
+{
+ struct bgp *bgp = VTY_GET_CONTEXT(bgp);
+ int ret;
+ union sockunion su;
+ struct peer *peer = NULL;
+ struct peer_group *group = NULL;
+
+ if (!bgp) {
+ return NULL;
+ }
+
+ ret = str2sockunion(peer_str, &su);
+ if (ret == 0) {
+ /* IP address, locate peer. */
+ peer = peer_lookup(bgp, &su);
+ } else {
+ /* Not IP, could match either peer configured on interface or a
+ * group. */
+ peer = peer_lookup_by_conf_if(bgp, peer_str);
+ if (!peer)
+ group = peer_group_lookup(bgp, peer_str);
+ }
+
+ if (peer) {
+ if (peer_dynamic_neighbor(peer)) {
+ vty_out(vty,
+ "%% Operation not allowed on a dynamic neighbor\n");
+ return NULL;
+ }
+
+ return peer;
+ }
+
+ if (group)
+ return group->conf;
+
+ vty_out(vty, "%% Specify remote-as or peer-group commands first\n");
+
+ return NULL;
+}
+
+int bgp_vty_return(struct vty *vty, enum bgp_create_error_code ret)
+{
+ const char *str = NULL;
+
+ switch (ret) {
+ case BGP_SUCCESS:
+ case BGP_CREATED:
+ case BGP_GR_NO_OPERATION:
+ break;
+ case BGP_ERR_INVALID_VALUE:
+ str = "Invalid value";
+ break;
+ case BGP_ERR_INVALID_FLAG:
+ str = "Invalid flag";
+ break;
+ case BGP_ERR_PEER_GROUP_SHUTDOWN:
+ str = "Peer-group has been shutdown. Activate the peer-group first";
+ break;
+ case BGP_ERR_PEER_FLAG_CONFLICT:
+ str = "Can't set override-capability and strict-capability-match at the same time";
+ break;
+ case BGP_ERR_PEER_GROUP_NO_REMOTE_AS:
+ str = "Specify remote-as or peer-group remote AS first";
+ break;
+ case BGP_ERR_PEER_GROUP_CANT_CHANGE:
+ str = "Cannot change the peer-group. Deconfigure first";
+ break;
+ case BGP_ERR_PEER_GROUP_MISMATCH:
+ str = "Peer is not a member of this peer-group";
+ break;
+ case BGP_ERR_PEER_FILTER_CONFLICT:
+ str = "Prefix/distribute list can not co-exist";
+ break;
+ case BGP_ERR_NOT_INTERNAL_PEER:
+ str = "Invalid command. Not an internal neighbor";
+ break;
+ case BGP_ERR_REMOVE_PRIVATE_AS:
+ str = "remove-private-AS cannot be configured for IBGP peers";
+ break;
+ case BGP_ERR_LOCAL_AS_ALLOWED_ONLY_FOR_EBGP:
+ str = "Local-AS allowed only for EBGP peers";
+ break;
+ case BGP_ERR_CANNOT_HAVE_LOCAL_AS_SAME_AS:
+ str = "Cannot have local-as same as BGP AS number";
+ break;
+ case BGP_ERR_TCPSIG_FAILED:
+ str = "Error while applying TCP-Sig to session(s)";
+ break;
+ case BGP_ERR_NO_EBGP_MULTIHOP_WITH_TTLHACK:
+ str = "ebgp-multihop and ttl-security cannot be configured together";
+ break;
+ case BGP_ERR_NO_IBGP_WITH_TTLHACK:
+ str = "ttl-security only allowed for EBGP peers";
+ break;
+ case BGP_ERR_AS_OVERRIDE:
+ str = "as-override cannot be configured for IBGP peers";
+ break;
+ case BGP_ERR_INVALID_DYNAMIC_NEIGHBORS_LIMIT:
+ str = "Invalid limit for number of dynamic neighbors";
+ break;
+ case BGP_ERR_DYNAMIC_NEIGHBORS_RANGE_EXISTS:
+ str = "Dynamic neighbor listen range already exists";
+ break;
+ case BGP_ERR_INVALID_FOR_DYNAMIC_PEER:
+ str = "Operation not allowed on a dynamic neighbor";
+ break;
+ case BGP_ERR_INVALID_FOR_DIRECT_PEER:
+ str = "Operation not allowed on a directly connected neighbor";
+ break;
+ case BGP_ERR_PEER_SAFI_CONFLICT:
+ str = "Cannot activate peer for both 'ipv4 unicast' and 'ipv4 labeled-unicast'";
+ break;
+ case BGP_ERR_GR_INVALID_CMD:
+ str = "The Graceful Restart command used is not valid at this moment.";
+ break;
+ case BGP_ERR_GR_OPERATION_FAILED:
+ str = "The Graceful Restart Operation failed due to an err.";
+ break;
+ case BGP_ERR_PEER_GROUP_MEMBER:
+ str = "Peer-group member cannot override remote-as of peer-group.";
+ break;
+ case BGP_ERR_PEER_GROUP_PEER_TYPE_DIFFERENT:
+ str = "Peer-group members must be all internal or all external.";
+ break;
+ case BGP_ERR_DYNAMIC_NEIGHBORS_RANGE_NOT_FOUND:
+ str = "Range specified cannot be deleted because it is not part of current config.";
+ break;
+ case BGP_ERR_INSTANCE_MISMATCH:
+ str = "Instance specified does not match the current instance.";
+ break;
+ case BGP_ERR_NO_INTERFACE_CONFIG:
+ str = "Interface specified is not being used for interface based peer.";
+ break;
+ case BGP_ERR_SOFT_RECONFIG_UNCONFIGURED:
+ str = "No configuration already specified for soft reconfiguration.";
+ break;
+ case BGP_ERR_AS_MISMATCH:
+ str = "BGP is already running.";
+ break;
+ case BGP_ERR_AF_UNCONFIGURED:
+ str = "AFI/SAFI specified is not currently configured.";
+ break;
+ case BGP_ERR_CANNOT_HAVE_LOCAL_AS_SAME_AS_REMOTE_AS:
+ str = "AS specified for local as is the same as the remote as and this is not allowed.";
+ break;
+ case BGP_ERR_INVALID_AS:
+ str = "Confederation AS specified is the same AS as our AS.";
+ break;
+ case BGP_ERR_INVALID_ROLE_NAME:
+ str = "Invalid role name";
+ break;
+ case BGP_ERR_INVALID_INTERNAL_ROLE:
+ str = "External roles can be set only on eBGP session";
+ break;
+ }
+ if (str) {
+ vty_out(vty, "%% %s\n", str);
+ return CMD_WARNING_CONFIG_FAILED;
+ }
+ return CMD_SUCCESS;
+}
+
+/* BGP clear sort. */
+enum clear_sort {
+ clear_all,
+ clear_peer,
+ clear_group,
+ clear_external,
+ clear_as
+};
+
+static void bgp_clear_vty_error(struct vty *vty, struct peer *peer, afi_t afi,
+ safi_t safi, int error)
+{
+ switch (error) {
+ case BGP_ERR_AF_UNCONFIGURED:
+ if (vty)
+ vty_out(vty,
+ "%% BGP: Enable %s address family for the neighbor %s\n",
+ get_afi_safi_str(afi, safi, false), peer->host);
+ else
+ zlog_warn(
+ "%% BGP: Enable %s address family for the neighbor %s",
+ get_afi_safi_str(afi, safi, false), peer->host);
+ break;
+ case BGP_ERR_SOFT_RECONFIG_UNCONFIGURED:
+ if (vty)
+ vty_out(vty,
+ "%% BGP: Inbound soft reconfig for %s not possible as it\n has neither refresh capability, nor inbound soft reconfig\n",
+ peer->host);
+ else
+ zlog_warn(
+ "%% BGP: Inbound soft reconfig for %s not possible as it has neither refresh capability, nor inbound soft reconfig",
+ peer->host);
+ break;
+ default:
+ break;
+ }
+}
+
+static int bgp_peer_clear(struct peer *peer, afi_t afi, safi_t safi,
+ struct listnode **nnode, enum bgp_clear_type stype)
+{
+ int ret = 0;
+ struct peer_af *paf;
+
+ /* if afi/.safi not specified, spin thru all of them */
+ if ((afi == AFI_UNSPEC) && (safi == SAFI_UNSPEC)) {
+ afi_t tmp_afi;
+ safi_t tmp_safi;
+ enum bgp_af_index index;
+
+ for (index = BGP_AF_START; index < BGP_AF_MAX; index++) {
+ paf = peer->peer_af_array[index];
+ if (!paf)
+ continue;
+
+ if (paf && paf->subgroup)
+ SET_FLAG(paf->subgroup->sflags,
+ SUBGRP_STATUS_FORCE_UPDATES);
+
+ tmp_afi = paf->afi;
+ tmp_safi = paf->safi;
+ if (!peer->afc[tmp_afi][tmp_safi])
+ continue;
+
+ if (stype == BGP_CLEAR_SOFT_NONE)
+ ret = peer_clear(peer, nnode);
+ else
+ ret = peer_clear_soft(peer, tmp_afi, tmp_safi,
+ stype);
+ }
+ /* if afi specified and safi not, spin thru safis on this afi */
+ } else if (safi == SAFI_UNSPEC) {
+ safi_t tmp_safi;
+
+ for (tmp_safi = SAFI_UNICAST;
+ tmp_safi < SAFI_MAX; tmp_safi++) {
+ if (!peer->afc[afi][tmp_safi])
+ continue;
+
+ paf = peer_af_find(peer, afi, tmp_safi);
+ if (paf && paf->subgroup)
+ SET_FLAG(paf->subgroup->sflags,
+ SUBGRP_STATUS_FORCE_UPDATES);
+
+ if (stype == BGP_CLEAR_SOFT_NONE)
+ ret = peer_clear(peer, nnode);
+ else
+ ret = peer_clear_soft(peer, afi,
+ tmp_safi, stype);
+ }
+ /* both afi/safi specified, let the caller know if not defined */
+ } else {
+ if (!peer->afc[afi][safi])
+ return 1;
+
+ paf = peer_af_find(peer, afi, safi);
+ if (paf && paf->subgroup)
+ SET_FLAG(paf->subgroup->sflags,
+ SUBGRP_STATUS_FORCE_UPDATES);
+
+ if (stype == BGP_CLEAR_SOFT_NONE)
+ ret = peer_clear(peer, nnode);
+ else
+ ret = peer_clear_soft(peer, afi, safi, stype);
+ }
+
+ return ret;
+}
+
+/* `clear ip bgp' functions. */
+static int bgp_clear(struct vty *vty, struct bgp *bgp, afi_t afi, safi_t safi,
+ enum clear_sort sort, enum bgp_clear_type stype,
+ const char *arg)
+{
+ int ret = 0;
+ bool found = false;
+ struct peer *peer;
+
+ VTY_BGP_GR_DEFINE_LOOP_VARIABLE;
+
+ /* Clear all neighbors. */
+ /*
+ * Pass along pointer to next node to peer_clear() when walking all
+ * nodes on the BGP instance as that may get freed if it is a
+ * doppelganger
+ */
+ if (sort == clear_all) {
+ for (ALL_LIST_ELEMENTS(bgp->peer, node, nnode, peer)) {
+
+ bgp_peer_gr_flags_update(peer);
+
+ if (CHECK_FLAG(peer->flags, PEER_FLAG_GRACEFUL_RESTART))
+ gr_router_detected = true;
+
+ ret = bgp_peer_clear(peer, afi, safi, &nnode,
+ stype);
+
+ if (ret < 0)
+ bgp_clear_vty_error(vty, peer, afi, safi, ret);
+ }
+
+ if (gr_router_detected
+ && bgp->present_zebra_gr_state == ZEBRA_GR_DISABLE) {
+ bgp_zebra_send_capabilities(bgp, false);
+ } else if (!gr_router_detected
+ && bgp->present_zebra_gr_state == ZEBRA_GR_ENABLE) {
+ bgp_zebra_send_capabilities(bgp, true);
+ }
+
+ /* This is to apply read-only mode on this clear. */
+ if (stype == BGP_CLEAR_SOFT_NONE)
+ bgp->update_delay_over = 0;
+
+ return CMD_SUCCESS;
+ }
+
+ /* Clear specified neighbor. */
+ if (sort == clear_peer) {
+ union sockunion su;
+
+ /* Make sockunion for lookup. */
+ ret = str2sockunion(arg, &su);
+ if (ret < 0) {
+ peer = peer_lookup_by_conf_if(bgp, arg);
+ if (!peer) {
+ peer = peer_lookup_by_hostname(bgp, arg);
+ if (!peer) {
+ vty_out(vty,
+ "Malformed address or name: %s\n",
+ arg);
+ return CMD_WARNING;
+ }
+ }
+ } else {
+ peer = peer_lookup(bgp, &su);
+ if (!peer) {
+ vty_out(vty,
+ "%% BGP: Unknown neighbor - \"%s\"\n",
+ arg);
+ return CMD_WARNING;
+ }
+ }
+
+ VTY_BGP_GR_ROUTER_DETECT(bgp, peer, peer->bgp->peer);
+ VTY_SEND_BGP_GR_CAPABILITY_TO_ZEBRA(peer->bgp, ret);
+
+ ret = bgp_peer_clear(peer, afi, safi, NULL, stype);
+
+ /* if afi/safi not defined for this peer, let caller know */
+ if (ret == 1)
+ ret = BGP_ERR_AF_UNCONFIGURED;
+
+ if (ret < 0)
+ bgp_clear_vty_error(vty, peer, afi, safi, ret);
+
+ return CMD_SUCCESS;
+ }
+
+ /* Clear all neighbors belonging to a specific peer-group. */
+ if (sort == clear_group) {
+ struct peer_group *group;
+
+ group = peer_group_lookup(bgp, arg);
+ if (!group) {
+ vty_out(vty, "%% BGP: No such peer-group %s\n", arg);
+ return CMD_WARNING;
+ }
+
+ for (ALL_LIST_ELEMENTS(group->peer, node, nnode, peer)) {
+ ret = bgp_peer_clear(peer, afi, safi, &nnode, stype);
+
+ if (ret < 0)
+ bgp_clear_vty_error(vty, peer, afi, safi, ret);
+ else
+ found = true;
+ }
+
+ if (!found)
+ vty_out(vty,
+ "%% BGP: No %s peer belonging to peer-group %s is configured\n",
+ get_afi_safi_str(afi, safi, false), arg);
+
+ return CMD_SUCCESS;
+ }
+
+ /* Clear all external (eBGP) neighbors. */
+ if (sort == clear_external) {
+ for (ALL_LIST_ELEMENTS(bgp->peer, node, nnode, peer)) {
+ if (peer->sort == BGP_PEER_IBGP)
+ continue;
+
+ bgp_peer_gr_flags_update(peer);
+
+ if (CHECK_FLAG(peer->flags, PEER_FLAG_GRACEFUL_RESTART))
+ gr_router_detected = true;
+
+ ret = bgp_peer_clear(peer, afi, safi, &nnode, stype);
+
+ if (ret < 0)
+ bgp_clear_vty_error(vty, peer, afi, safi, ret);
+ else
+ found = true;
+ }
+
+ if (gr_router_detected
+ && bgp->present_zebra_gr_state == ZEBRA_GR_DISABLE) {
+ bgp_zebra_send_capabilities(bgp, false);
+ } else if (!gr_router_detected
+ && bgp->present_zebra_gr_state == ZEBRA_GR_ENABLE) {
+ bgp_zebra_send_capabilities(bgp, true);
+ }
+
+ if (!found)
+ vty_out(vty,
+ "%% BGP: No external %s peer is configured\n",
+ get_afi_safi_str(afi, safi, false));
+
+ return CMD_SUCCESS;
+ }
+
+ /* Clear all neighbors belonging to a specific AS. */
+ if (sort == clear_as) {
+ as_t as = strtoul(arg, NULL, 10);
+
+ for (ALL_LIST_ELEMENTS(bgp->peer, node, nnode, peer)) {
+ if (peer->as != as)
+ continue;
+
+ bgp_peer_gr_flags_update(peer);
+
+ if (CHECK_FLAG(peer->flags, PEER_FLAG_GRACEFUL_RESTART))
+ gr_router_detected = true;
+
+ ret = bgp_peer_clear(peer, afi, safi, &nnode, stype);
+
+ if (ret < 0)
+ bgp_clear_vty_error(vty, peer, afi, safi, ret);
+ else
+ found = true;
+ }
+
+ if (gr_router_detected
+ && bgp->present_zebra_gr_state == ZEBRA_GR_DISABLE) {
+ bgp_zebra_send_capabilities(bgp, false);
+ } else if (!gr_router_detected
+ && bgp->present_zebra_gr_state == ZEBRA_GR_ENABLE) {
+ bgp_zebra_send_capabilities(bgp, true);
+ }
+
+ if (!found)
+ vty_out(vty,
+ "%% BGP: No %s peer is configured with AS %s\n",
+ get_afi_safi_str(afi, safi, false), arg);
+
+ return CMD_SUCCESS;
+ }
+
+ return CMD_SUCCESS;
+}
+
+static int bgp_clear_vty(struct vty *vty, const char *name, afi_t afi,
+ safi_t safi, enum clear_sort sort,
+ enum bgp_clear_type stype, const char *arg)
+{
+ struct bgp *bgp;
+
+ /* BGP structure lookup. */
+ if (name) {
+ bgp = bgp_lookup_by_name(name);
+ if (bgp == NULL) {
+ vty_out(vty, "Can't find BGP instance %s\n", name);
+ return CMD_WARNING;
+ }
+ } else {
+ bgp = bgp_get_default();
+ if (bgp == NULL) {
+ vty_out(vty, "No BGP process is configured\n");
+ return CMD_WARNING;
+ }
+ }
+
+ return bgp_clear(vty, bgp, afi, safi, sort, stype, arg);
+}
+
+/* clear soft inbound */
+static void bgp_clear_star_soft_in(struct vty *vty, const char *name)
+{
+ afi_t afi;
+ safi_t safi;
+
+ FOREACH_AFI_SAFI (afi, safi)
+ bgp_clear_vty(vty, name, afi, safi, clear_all,
+ BGP_CLEAR_SOFT_IN, NULL);
+}
+
+/* clear soft outbound */
+static void bgp_clear_star_soft_out(struct vty *vty, const char *name)
+{
+ afi_t afi;
+ safi_t safi;
+
+ FOREACH_AFI_SAFI (afi, safi)
+ bgp_clear_vty(vty, name, afi, safi, clear_all,
+ BGP_CLEAR_SOFT_OUT, NULL);
+}
+
+
+void bgp_clear_soft_in(struct bgp *bgp, afi_t afi, safi_t safi)
+{
+ bgp_clear(NULL, bgp, afi, safi, clear_all, BGP_CLEAR_SOFT_IN, NULL);
+}
+
+#ifndef VTYSH_EXTRACT_PL
+#include "bgpd/bgp_vty_clippy.c"
+#endif
+
+DEFUN_HIDDEN (bgp_local_mac,
+ bgp_local_mac_cmd,
+ "bgp local-mac vni " CMD_VNI_RANGE " mac WORD seq (0-4294967295)",
+ BGP_STR
+ "Local MAC config\n"
+ "VxLAN Network Identifier\n"
+ "VNI number\n"
+ "local mac\n"
+ "mac address\n"
+ "mac-mobility sequence\n"
+ "seq number\n")
+{
+ int rv;
+ vni_t vni;
+ struct ethaddr mac;
+ struct ipaddr ip;
+ uint32_t seq;
+ struct bgp *bgp;
+
+ vni = strtoul(argv[3]->arg, NULL, 10);
+ if (!prefix_str2mac(argv[5]->arg, &mac)) {
+ vty_out(vty, "%% Malformed MAC address\n");
+ return CMD_WARNING;
+ }
+ memset(&ip, 0, sizeof(ip));
+ seq = strtoul(argv[7]->arg, NULL, 10);
+
+ bgp = bgp_get_default();
+ if (!bgp) {
+ vty_out(vty, "Default BGP instance is not there\n");
+ return CMD_WARNING;
+ }
+
+ rv = bgp_evpn_local_macip_add(bgp, vni, &mac, &ip, 0 /* flags */, seq,
+ zero_esi);
+ if (rv < 0) {
+ vty_out(vty, "Internal error\n");
+ return CMD_WARNING;
+ }
+
+ return CMD_SUCCESS;
+}
+
+DEFUN_HIDDEN (no_bgp_local_mac,
+ no_bgp_local_mac_cmd,
+ "no bgp local-mac vni " CMD_VNI_RANGE " mac WORD",
+ NO_STR
+ BGP_STR
+ "Local MAC config\n"
+ "VxLAN Network Identifier\n"
+ "VNI number\n"
+ "local mac\n"
+ "mac address\n")
+{
+ int rv;
+ vni_t vni;
+ struct ethaddr mac;
+ struct ipaddr ip;
+ struct bgp *bgp;
+
+ vni = strtoul(argv[4]->arg, NULL, 10);
+ if (!prefix_str2mac(argv[6]->arg, &mac)) {
+ vty_out(vty, "%% Malformed MAC address\n");
+ return CMD_WARNING;
+ }
+ memset(&ip, 0, sizeof(ip));
+
+ bgp = bgp_get_default();
+ if (!bgp) {
+ vty_out(vty, "Default BGP instance is not there\n");
+ return CMD_WARNING;
+ }
+
+ rv = bgp_evpn_local_macip_del(bgp, vni, &mac, &ip, ZEBRA_NEIGH_ACTIVE);
+ if (rv < 0) {
+ vty_out(vty, "Internal error\n");
+ return CMD_WARNING;
+ }
+
+ return CMD_SUCCESS;
+}
+
+DEFUN (no_synchronization,
+ no_synchronization_cmd,
+ "no synchronization",
+ NO_STR
+ "Perform IGP synchronization\n")
+{
+ return CMD_SUCCESS;
+}
+
+DEFUN (no_auto_summary,
+ no_auto_summary_cmd,
+ "no auto-summary",
+ NO_STR
+ "Enable automatic network number summarization\n")
+{
+ return CMD_SUCCESS;
+}
+
+/* "router bgp" commands. */
+DEFUN_NOSH (router_bgp,
+ router_bgp_cmd,
+ "router bgp [(1-4294967295)$instasn [<view|vrf> VIEWVRFNAME]]",
+ ROUTER_STR
+ BGP_STR
+ AS_STR
+ BGP_INSTANCE_HELP_STR)
+{
+ int idx_asn = 2;
+ int idx_view_vrf = 3;
+ int idx_vrf = 4;
+ int is_new_bgp = 0;
+ int ret;
+ as_t as;
+ struct bgp *bgp;
+ const char *name = NULL;
+ enum bgp_instance_type inst_type;
+
+ // "router bgp" without an ASN
+ if (argc == 2) {
+ // Pending: Make VRF option available for ASN less config
+ bgp = bgp_get_default();
+
+ if (bgp == NULL) {
+ vty_out(vty, "%% No BGP process is configured\n");
+ return CMD_WARNING_CONFIG_FAILED;
+ }
+
+ if (listcount(bm->bgp) > 1) {
+ vty_out(vty, "%% Please specify ASN and VRF\n");
+ return CMD_WARNING_CONFIG_FAILED;
+ }
+ }
+
+ // "router bgp X"
+ else {
+ as = strtoul(argv[idx_asn]->arg, NULL, 10);
+
+ if (as == BGP_PRIVATE_AS_MAX || as == BGP_AS4_MAX)
+ vty_out(vty, "Reserved AS used (%u|%u); AS is %u\n",
+ BGP_PRIVATE_AS_MAX, BGP_AS4_MAX, as);
+
+ inst_type = BGP_INSTANCE_TYPE_DEFAULT;
+ if (argc > 3) {
+ name = argv[idx_vrf]->arg;
+
+ if (!strcmp(argv[idx_view_vrf]->text, "vrf")) {
+ if (strmatch(name, VRF_DEFAULT_NAME))
+ name = NULL;
+ else
+ inst_type = BGP_INSTANCE_TYPE_VRF;
+ } else if (!strcmp(argv[idx_view_vrf]->text, "view"))
+ inst_type = BGP_INSTANCE_TYPE_VIEW;
+ }
+
+ if (inst_type == BGP_INSTANCE_TYPE_DEFAULT)
+ is_new_bgp = (bgp_lookup(as, name) == NULL);
+
+ ret = bgp_get_vty(&bgp, &as, name, inst_type);
+ switch (ret) {
+ case BGP_ERR_AS_MISMATCH:
+ vty_out(vty, "BGP is already running; AS is %u\n", as);
+ return CMD_WARNING_CONFIG_FAILED;
+ case BGP_ERR_INSTANCE_MISMATCH:
+ vty_out(vty,
+ "BGP instance name and AS number mismatch\n");
+ vty_out(vty,
+ "BGP instance is already running; AS is %u\n",
+ as);
+ return CMD_WARNING_CONFIG_FAILED;
+ }
+
+ /*
+ * If we just instantiated the default instance, complete
+ * any pending VRF-VPN leaking that was configured via
+ * earlier "router bgp X vrf FOO" blocks.
+ */
+ if (is_new_bgp && inst_type == BGP_INSTANCE_TYPE_DEFAULT)
+ vpn_leak_postchange_all();
+
+ if (inst_type == BGP_INSTANCE_TYPE_VRF)
+ bgp_vpn_leak_export(bgp);
+ /* Pending: handle when user tries to change a view to vrf n vv.
+ */
+ }
+
+ /* unset the auto created flag as the user config is now present */
+ UNSET_FLAG(bgp->vrf_flags, BGP_VRF_AUTO);
+ VTY_PUSH_CONTEXT(BGP_NODE, bgp);
+
+ return CMD_SUCCESS;
+}
+
+/* "no router bgp" commands. */
+DEFUN (no_router_bgp,
+ no_router_bgp_cmd,
+ "no router bgp [(1-4294967295)$instasn [<view|vrf> VIEWVRFNAME]]",
+ NO_STR
+ ROUTER_STR
+ BGP_STR
+ AS_STR
+ BGP_INSTANCE_HELP_STR)
+{
+ int idx_asn = 3;
+ int idx_vrf = 5;
+ as_t as;
+ struct bgp *bgp;
+ const char *name = NULL;
+
+ // "no router bgp" without an ASN
+ if (argc == 3) {
+ // Pending: Make VRF option available for ASN less config
+ bgp = bgp_get_default();
+
+ if (bgp == NULL) {
+ vty_out(vty, "%% No BGP process is configured\n");
+ return CMD_WARNING_CONFIG_FAILED;
+ }
+
+ if (listcount(bm->bgp) > 1) {
+ vty_out(vty, "%% Please specify ASN and VRF\n");
+ return CMD_WARNING_CONFIG_FAILED;
+ }
+
+ if (bgp->l3vni) {
+ vty_out(vty, "%% Please unconfigure l3vni %u\n",
+ bgp->l3vni);
+ return CMD_WARNING_CONFIG_FAILED;
+ }
+ } else {
+ as = strtoul(argv[idx_asn]->arg, NULL, 10);
+
+ if (argc > 4) {
+ name = argv[idx_vrf]->arg;
+ if (strmatch(argv[idx_vrf - 1]->text, "vrf")
+ && strmatch(name, VRF_DEFAULT_NAME))
+ name = NULL;
+ }
+
+ /* Lookup bgp structure. */
+ bgp = bgp_lookup(as, name);
+ if (!bgp) {
+ vty_out(vty, "%% Can't find BGP instance\n");
+ return CMD_WARNING_CONFIG_FAILED;
+ }
+
+ if (bgp->l3vni) {
+ vty_out(vty, "%% Please unconfigure l3vni %u\n",
+ bgp->l3vni);
+ return CMD_WARNING_CONFIG_FAILED;
+ }
+
+ /* Cannot delete default instance if vrf instances exist */
+ if (bgp->inst_type == BGP_INSTANCE_TYPE_DEFAULT) {
+ struct listnode *node;
+ struct bgp *tmp_bgp;
+
+ for (ALL_LIST_ELEMENTS_RO(bm->bgp, node, tmp_bgp)) {
+ if (tmp_bgp->inst_type != BGP_INSTANCE_TYPE_VRF)
+ continue;
+ if (CHECK_FLAG(
+ tmp_bgp->af_flags[AFI_IP]
+ [SAFI_UNICAST],
+ BGP_CONFIG_MPLSVPN_TO_VRF_IMPORT) ||
+ CHECK_FLAG(
+ tmp_bgp->af_flags[AFI_IP6]
+ [SAFI_UNICAST],
+ BGP_CONFIG_MPLSVPN_TO_VRF_IMPORT) ||
+ CHECK_FLAG(
+ tmp_bgp->af_flags[AFI_IP]
+ [SAFI_UNICAST],
+ BGP_CONFIG_VRF_TO_MPLSVPN_EXPORT) ||
+ CHECK_FLAG(
+ tmp_bgp->af_flags[AFI_IP6]
+ [SAFI_UNICAST],
+ BGP_CONFIG_VRF_TO_MPLSVPN_EXPORT) ||
+ CHECK_FLAG(tmp_bgp->af_flags[AFI_IP]
+ [SAFI_UNICAST],
+ BGP_CONFIG_VRF_TO_VRF_EXPORT) ||
+ CHECK_FLAG(tmp_bgp->af_flags[AFI_IP6]
+ [SAFI_UNICAST],
+ BGP_CONFIG_VRF_TO_VRF_EXPORT) ||
+ (bgp == bgp_get_evpn() &&
+ (CHECK_FLAG(
+ tmp_bgp->af_flags[AFI_L2VPN]
+ [SAFI_EVPN],
+ BGP_L2VPN_EVPN_ADV_IPV4_UNICAST) ||
+ CHECK_FLAG(
+ tmp_bgp->af_flags[AFI_L2VPN]
+ [SAFI_EVPN],
+ BGP_L2VPN_EVPN_ADV_IPV4_UNICAST_GW_IP) ||
+ CHECK_FLAG(
+ tmp_bgp->af_flags[AFI_L2VPN]
+ [SAFI_EVPN],
+ BGP_L2VPN_EVPN_ADV_IPV6_UNICAST) ||
+ CHECK_FLAG(
+ tmp_bgp->af_flags[AFI_L2VPN]
+ [SAFI_EVPN],
+ BGP_L2VPN_EVPN_ADV_IPV6_UNICAST_GW_IP))) ||
+ (tmp_bgp->l3vni)) {
+ vty_out(vty,
+ "%% Cannot delete default BGP instance. Dependent VRF instances exist\n");
+ return CMD_WARNING_CONFIG_FAILED;
+ }
+ }
+ }
+ }
+
+ bgp_delete(bgp);
+
+ return CMD_SUCCESS;
+}
+
+/* bgp session-dscp */
+
+DEFPY (bgp_session_dscp,
+ bgp_session_dscp_cmd,
+ "bgp session-dscp (0-63)$dscp",
+ BGP_STR
+ "Override default (C6) bgp TCP session DSCP value\n"
+ "Manually configured dscp parameter\n")
+{
+ bm->tcp_dscp = dscp << 2;
+
+ return CMD_SUCCESS;
+}
+
+DEFPY (no_bgp_session_dscp,
+ no_bgp_session_dscp_cmd,
+ "no bgp session-dscp [(0-63)]",
+ NO_STR
+ BGP_STR
+ "Override default (C6) bgp TCP session DSCP value\n"
+ "Manually configured dscp parameter\n")
+{
+ bm->tcp_dscp = IPTOS_PREC_INTERNETCONTROL;
+
+ return CMD_SUCCESS;
+}
+
+/* BGP router-id. */
+
+DEFPY (bgp_router_id,
+ bgp_router_id_cmd,
+ "bgp router-id A.B.C.D",
+ BGP_STR
+ "Override configured router identifier\n"
+ "Manually configured router identifier\n")
+{
+ VTY_DECLVAR_CONTEXT(bgp, bgp);
+ bgp_router_id_static_set(bgp, router_id);
+ return CMD_SUCCESS;
+}
+
+DEFPY (no_bgp_router_id,
+ no_bgp_router_id_cmd,
+ "no bgp router-id [A.B.C.D]",
+ NO_STR
+ BGP_STR
+ "Override configured router identifier\n"
+ "Manually configured router identifier\n")
+{
+ VTY_DECLVAR_CONTEXT(bgp, bgp);
+
+ if (router_id_str) {
+ if (!IPV4_ADDR_SAME(&bgp->router_id_static, &router_id)) {
+ vty_out(vty, "%% BGP router-id doesn't match\n");
+ return CMD_WARNING_CONFIG_FAILED;
+ }
+ }
+
+ router_id.s_addr = 0;
+ bgp_router_id_static_set(bgp, router_id);
+
+ return CMD_SUCCESS;
+}
+
+DEFPY(bgp_community_alias, bgp_community_alias_cmd,
+ "[no$no] bgp community alias WORD$community ALIAS_NAME$alias_name",
+ NO_STR BGP_STR
+ "Add community specific parameters\n"
+ "Create an alias for a community\n"
+ "Community (AA:BB or AA:BB:CC)\n"
+ "Alias name\n")
+{
+ struct community_alias ca = {};
+ struct community_alias *lookup_community;
+ struct community_alias *lookup_alias;
+ struct community *comm;
+ struct lcommunity *lcomm;
+ uint8_t invalid = 0;
+
+ comm = community_str2com(community);
+ if (!comm)
+ invalid++;
+ community_free(&comm);
+
+ lcomm = lcommunity_str2com(community);
+ if (!lcomm)
+ invalid++;
+ lcommunity_free(&lcomm);
+
+ if (invalid > 1) {
+ vty_out(vty, "Invalid community format\n");
+ return CMD_WARNING;
+ }
+
+ strlcpy(ca.community, community, sizeof(ca.community));
+ strlcpy(ca.alias, alias_name, sizeof(ca.alias));
+
+ lookup_community = bgp_ca_community_lookup(&ca);
+ lookup_alias = bgp_ca_alias_lookup(&ca);
+
+ if (no) {
+ bgp_ca_alias_delete(&ca);
+ bgp_ca_community_delete(&ca);
+ } else {
+ if (lookup_alias) {
+ /* Lookup if community hash table has an item
+ * with the same alias name.
+ */
+ strlcpy(ca.community, lookup_alias->community,
+ sizeof(ca.community));
+ if (bgp_ca_community_lookup(&ca)) {
+ vty_out(vty,
+ "community (%s) already has this alias (%s)\n",
+ lookup_alias->community,
+ lookup_alias->alias);
+ return CMD_WARNING;
+ }
+ bgp_ca_alias_delete(&ca);
+ }
+
+ if (lookup_community) {
+ /* Lookup if alias hash table has an item
+ * with the same community.
+ */
+ strlcpy(ca.alias, lookup_community->alias,
+ sizeof(ca.alias));
+ if (bgp_ca_alias_lookup(&ca)) {
+ vty_out(vty,
+ "alias (%s) already has this community (%s)\n",
+ lookup_community->alias,
+ lookup_community->community);
+ return CMD_WARNING;
+ }
+ bgp_ca_community_delete(&ca);
+ }
+
+ bgp_ca_alias_insert(&ca);
+ bgp_ca_community_insert(&ca);
+ }
+
+ return CMD_SUCCESS;
+}
+
+DEFPY (bgp_global_suppress_fib_pending,
+ bgp_global_suppress_fib_pending_cmd,
+ "[no] bgp suppress-fib-pending",
+ NO_STR
+ BGP_STR
+ "Advertise only routes that are programmed in kernel to peers globally\n")
+{
+ bm_wait_for_fib_set(!no);
+
+ return CMD_SUCCESS;
+}
+
+DEFPY (bgp_suppress_fib_pending,
+ bgp_suppress_fib_pending_cmd,
+ "[no] bgp suppress-fib-pending",
+ NO_STR
+ BGP_STR
+ "Advertise only routes that are programmed in kernel to peers\n")
+{
+ VTY_DECLVAR_CONTEXT(bgp, bgp);
+
+ bgp_suppress_fib_pending_set(bgp, !no);
+ return CMD_SUCCESS;
+}
+
+
+/* BGP Cluster ID. */
+DEFUN (bgp_cluster_id,
+ bgp_cluster_id_cmd,
+ "bgp cluster-id <A.B.C.D|(1-4294967295)>",
+ BGP_STR
+ "Configure Route-Reflector Cluster-id\n"
+ "Route-Reflector Cluster-id in IP address format\n"
+ "Route-Reflector Cluster-id as 32 bit quantity\n")
+{
+ VTY_DECLVAR_CONTEXT(bgp, bgp);
+ int idx_ipv4 = 2;
+ int ret;
+ struct in_addr cluster;
+
+ ret = inet_aton(argv[idx_ipv4]->arg, &cluster);
+ if (!ret) {
+ vty_out(vty, "%% Malformed bgp cluster identifier\n");
+ return CMD_WARNING_CONFIG_FAILED;
+ }
+
+ bgp_cluster_id_set(bgp, &cluster);
+ bgp_clear_star_soft_out(vty, bgp->name);
+
+ return CMD_SUCCESS;
+}
+
+DEFUN (no_bgp_cluster_id,
+ no_bgp_cluster_id_cmd,
+ "no bgp cluster-id [<A.B.C.D|(1-4294967295)>]",
+ NO_STR
+ BGP_STR
+ "Configure Route-Reflector Cluster-id\n"
+ "Route-Reflector Cluster-id in IP address format\n"
+ "Route-Reflector Cluster-id as 32 bit quantity\n")
+{
+ VTY_DECLVAR_CONTEXT(bgp, bgp);
+ bgp_cluster_id_unset(bgp);
+ bgp_clear_star_soft_out(vty, bgp->name);
+
+ return CMD_SUCCESS;
+}
+
+DEFPY (bgp_norib,
+ bgp_norib_cmd,
+ "bgp no-rib",
+ BGP_STR
+ "Disable BGP route installation to RIB (Zebra)\n")
+{
+ if (bgp_option_check(BGP_OPT_NO_FIB)) {
+ vty_out(vty,
+ "%% No-RIB option is already set, nothing to do here.\n");
+ return CMD_SUCCESS;
+ }
+
+ bgp_option_norib_set_runtime();
+
+ return CMD_SUCCESS;
+}
+
+DEFPY (no_bgp_norib,
+ no_bgp_norib_cmd,
+ "no bgp no-rib",
+ NO_STR
+ BGP_STR
+ "Disable BGP route installation to RIB (Zebra)\n")
+{
+ if (!bgp_option_check(BGP_OPT_NO_FIB)) {
+ vty_out(vty,
+ "%% No-RIB option is not set, nothing to do here.\n");
+ return CMD_SUCCESS;
+ }
+
+ bgp_option_norib_unset_runtime();
+
+ return CMD_SUCCESS;
+}
+
+DEFPY (no_bgp_send_extra_data,
+ no_bgp_send_extra_data_cmd,
+ "[no] bgp send-extra-data zebra",
+ NO_STR
+ BGP_STR
+ "Extra data to Zebra for display/use\n"
+ "To zebra\n")
+{
+ if (no)
+ UNSET_FLAG(bm->flags, BM_FLAG_SEND_EXTRA_DATA_TO_ZEBRA);
+ else
+ SET_FLAG(bm->flags, BM_FLAG_SEND_EXTRA_DATA_TO_ZEBRA);
+
+ return CMD_SUCCESS;
+}
+
+DEFUN (bgp_confederation_identifier,
+ bgp_confederation_identifier_cmd,
+ "bgp confederation identifier (1-4294967295)",
+ BGP_STR
+ "AS confederation parameters\n"
+ "AS number\n"
+ "Set routing domain confederation AS\n")
+{
+ VTY_DECLVAR_CONTEXT(bgp, bgp);
+ int idx_number = 3;
+ as_t as;
+
+ as = strtoul(argv[idx_number]->arg, NULL, 10);
+
+ bgp_confederation_id_set(bgp, as);
+
+ return CMD_SUCCESS;
+}
+
+DEFUN (no_bgp_confederation_identifier,
+ no_bgp_confederation_identifier_cmd,
+ "no bgp confederation identifier [(1-4294967295)]",
+ NO_STR
+ BGP_STR
+ "AS confederation parameters\n"
+ "AS number\n"
+ "Set routing domain confederation AS\n")
+{
+ VTY_DECLVAR_CONTEXT(bgp, bgp);
+ bgp_confederation_id_unset(bgp);
+
+ return CMD_SUCCESS;
+}
+
+DEFUN (bgp_confederation_peers,
+ bgp_confederation_peers_cmd,
+ "bgp confederation peers (1-4294967295)...",
+ BGP_STR
+ "AS confederation parameters\n"
+ "Peer ASs in BGP confederation\n"
+ AS_STR)
+{
+ VTY_DECLVAR_CONTEXT(bgp, bgp);
+ int idx_asn = 3;
+ as_t as;
+ int i;
+
+ for (i = idx_asn; i < argc; i++) {
+ as = strtoul(argv[i]->arg, NULL, 10);
+
+ if (bgp->as == as) {
+ vty_out(vty,
+ "%% Local member-AS not allowed in confed peer list\n");
+ continue;
+ }
+
+ bgp_confederation_peers_add(bgp, as);
+ }
+ return CMD_SUCCESS;
+}
+
+DEFUN (no_bgp_confederation_peers,
+ no_bgp_confederation_peers_cmd,
+ "no bgp confederation peers (1-4294967295)...",
+ NO_STR
+ BGP_STR
+ "AS confederation parameters\n"
+ "Peer ASs in BGP confederation\n"
+ AS_STR)
+{
+ VTY_DECLVAR_CONTEXT(bgp, bgp);
+ int idx_asn = 4;
+ as_t as;
+ int i;
+
+ for (i = idx_asn; i < argc; i++) {
+ as = strtoul(argv[i]->arg, NULL, 10);
+
+ bgp_confederation_peers_remove(bgp, as);
+ }
+ return CMD_SUCCESS;
+}
+
+/**
+ * Central routine for maximum-paths configuration.
+ * @peer_type: BGP_PEER_EBGP or BGP_PEER_IBGP
+ * @set: 1 for setting values, 0 for removing the max-paths config.
+ */
+static int bgp_maxpaths_config_vty(struct vty *vty, int peer_type,
+ const char *mpaths, uint16_t options,
+ int set)
+{
+ VTY_DECLVAR_CONTEXT(bgp, bgp);
+ uint16_t maxpaths = 0;
+ int ret;
+ afi_t afi;
+ safi_t safi;
+
+ afi = bgp_node_afi(vty);
+ safi = bgp_node_safi(vty);
+
+ if (set) {
+ maxpaths = strtol(mpaths, NULL, 10);
+ if (maxpaths > multipath_num) {
+ vty_out(vty,
+ "%% Maxpaths Specified: %d is > than multipath num specified on bgp command line %d",
+ maxpaths, multipath_num);
+ return CMD_WARNING_CONFIG_FAILED;
+ }
+ ret = bgp_maximum_paths_set(bgp, afi, safi, peer_type, maxpaths,
+ options);
+ } else
+ ret = bgp_maximum_paths_unset(bgp, afi, safi, peer_type);
+
+ if (ret < 0) {
+ vty_out(vty,
+ "%% Failed to %sset maximum-paths %s %u for afi %u, safi %u\n",
+ (set == 1) ? "" : "un",
+ (peer_type == BGP_PEER_EBGP) ? "ebgp" : "ibgp",
+ maxpaths, afi, safi);
+ return CMD_WARNING_CONFIG_FAILED;
+ }
+
+ bgp_recalculate_all_bestpaths(bgp);
+
+ return CMD_SUCCESS;
+}
+
+DEFUN (bgp_maxmed_admin,
+ bgp_maxmed_admin_cmd,
+ "bgp max-med administrative ",
+ BGP_STR
+ "Advertise routes with max-med\n"
+ "Administratively applied, for an indefinite period\n")
+{
+ VTY_DECLVAR_CONTEXT(bgp, bgp);
+
+ bgp->v_maxmed_admin = 1;
+ bgp->maxmed_admin_value = BGP_MAXMED_VALUE_DEFAULT;
+
+ bgp_maxmed_update(bgp);
+
+ return CMD_SUCCESS;
+}
+
+DEFUN (bgp_maxmed_admin_medv,
+ bgp_maxmed_admin_medv_cmd,
+ "bgp max-med administrative (0-4294967295)",
+ BGP_STR
+ "Advertise routes with max-med\n"
+ "Administratively applied, for an indefinite period\n"
+ "Max MED value to be used\n")
+{
+ VTY_DECLVAR_CONTEXT(bgp, bgp);
+ int idx_number = 3;
+
+ bgp->v_maxmed_admin = 1;
+ bgp->maxmed_admin_value = strtoul(argv[idx_number]->arg, NULL, 10);
+
+ bgp_maxmed_update(bgp);
+
+ return CMD_SUCCESS;
+}
+
+DEFUN (no_bgp_maxmed_admin,
+ no_bgp_maxmed_admin_cmd,
+ "no bgp max-med administrative [(0-4294967295)]",
+ NO_STR
+ BGP_STR
+ "Advertise routes with max-med\n"
+ "Administratively applied, for an indefinite period\n"
+ "Max MED value to be used\n")
+{
+ VTY_DECLVAR_CONTEXT(bgp, bgp);
+ bgp->v_maxmed_admin = BGP_MAXMED_ADMIN_UNCONFIGURED;
+ bgp->maxmed_admin_value = BGP_MAXMED_VALUE_DEFAULT;
+ bgp_maxmed_update(bgp);
+
+ return CMD_SUCCESS;
+}
+
+DEFUN (bgp_maxmed_onstartup,
+ bgp_maxmed_onstartup_cmd,
+ "bgp max-med on-startup (5-86400) [(0-4294967295)]",
+ BGP_STR
+ "Advertise routes with max-med\n"
+ "Effective on a startup\n"
+ "Time (seconds) period for max-med\n"
+ "Max MED value to be used\n")
+{
+ VTY_DECLVAR_CONTEXT(bgp, bgp);
+ int idx = 0;
+
+ if (argv_find(argv, argc, "(5-86400)", &idx))
+ bgp->v_maxmed_onstartup = strtoul(argv[idx]->arg, NULL, 10);
+ if (argv_find(argv, argc, "(0-4294967295)", &idx))
+ bgp->maxmed_onstartup_value = strtoul(argv[idx]->arg, NULL, 10);
+ else
+ bgp->maxmed_onstartup_value = BGP_MAXMED_VALUE_DEFAULT;
+
+ bgp_maxmed_update(bgp);
+
+ return CMD_SUCCESS;
+}
+
+DEFUN (no_bgp_maxmed_onstartup,
+ no_bgp_maxmed_onstartup_cmd,
+ "no bgp max-med on-startup [(5-86400) [(0-4294967295)]]",
+ NO_STR
+ BGP_STR
+ "Advertise routes with max-med\n"
+ "Effective on a startup\n"
+ "Time (seconds) period for max-med\n"
+ "Max MED value to be used\n")
+{
+ VTY_DECLVAR_CONTEXT(bgp, bgp);
+
+ /* Cancel max-med onstartup if its on */
+ if (bgp->t_maxmed_onstartup) {
+ THREAD_OFF(bgp->t_maxmed_onstartup);
+ bgp->maxmed_onstartup_over = 1;
+ }
+
+ bgp->v_maxmed_onstartup = BGP_MAXMED_ONSTARTUP_UNCONFIGURED;
+ bgp->maxmed_onstartup_value = BGP_MAXMED_VALUE_DEFAULT;
+
+ bgp_maxmed_update(bgp);
+
+ return CMD_SUCCESS;
+}
+
+static int bgp_global_update_delay_config_vty(struct vty *vty,
+ uint16_t update_delay,
+ uint16_t establish_wait)
+{
+ struct listnode *node, *nnode;
+ struct bgp *bgp;
+ bool vrf_cfg = false;
+
+ /*
+ * See if update-delay is set per-vrf and warn user to delete it
+ * Note that we only need to check this if this is the first time
+ * setting the global config.
+ */
+ if (bm->v_update_delay == BGP_UPDATE_DELAY_DEF) {
+ for (ALL_LIST_ELEMENTS(bm->bgp, node, nnode, bgp)) {
+ if (bgp->v_update_delay != BGP_UPDATE_DELAY_DEF) {
+ vty_out(vty,
+ "%% update-delay configuration found in vrf %s\n",
+ bgp->inst_type == BGP_INSTANCE_TYPE_DEFAULT
+ ? VRF_DEFAULT_NAME
+ : bgp->name);
+ vrf_cfg = true;
+ }
+ }
+ }
+
+ if (vrf_cfg) {
+ vty_out(vty,
+ "%%Failed: global update-delay config not permitted\n");
+ return CMD_WARNING;
+ }
+
+ if (!establish_wait) { /* update-delay <delay> */
+ bm->v_update_delay = update_delay;
+ bm->v_establish_wait = bm->v_update_delay;
+ } else {
+ /* update-delay <delay> <establish-wait> */
+ if (update_delay < establish_wait) {
+ vty_out(vty,
+ "%%Failed: update-delay less than the establish-wait!\n");
+ return CMD_WARNING_CONFIG_FAILED;
+ }
+
+ bm->v_update_delay = update_delay;
+ bm->v_establish_wait = establish_wait;
+ }
+
+ for (ALL_LIST_ELEMENTS(bm->bgp, node, nnode, bgp)) {
+ bgp->v_update_delay = bm->v_update_delay;
+ bgp->v_establish_wait = bm->v_establish_wait;
+ }
+
+ return CMD_SUCCESS;
+}
+
+static int bgp_global_update_delay_deconfig_vty(struct vty *vty)
+{
+ struct listnode *node, *nnode;
+ struct bgp *bgp;
+
+ bm->v_update_delay = BGP_UPDATE_DELAY_DEF;
+ bm->v_establish_wait = bm->v_update_delay;
+
+ for (ALL_LIST_ELEMENTS(bm->bgp, node, nnode, bgp)) {
+ bgp->v_update_delay = bm->v_update_delay;
+ bgp->v_establish_wait = bm->v_establish_wait;
+ }
+
+ return CMD_SUCCESS;
+}
+
+static int bgp_update_delay_config_vty(struct vty *vty, uint16_t update_delay,
+ uint16_t establish_wait)
+{
+ VTY_DECLVAR_CONTEXT(bgp, bgp);
+
+ /* if configured globally, per-instance config is not allowed */
+ if (bm->v_update_delay) {
+ vty_out(vty,
+ "%%Failed: per-vrf update-delay config not permitted with global update-delay\n");
+ return CMD_WARNING_CONFIG_FAILED;
+ }
+
+
+ if (!establish_wait) /* update-delay <delay> */
+ {
+ bgp->v_update_delay = update_delay;
+ bgp->v_establish_wait = bgp->v_update_delay;
+ return CMD_SUCCESS;
+ }
+
+ /* update-delay <delay> <establish-wait> */
+ if (update_delay < establish_wait) {
+ vty_out(vty,
+ "%%Failed: update-delay less than the establish-wait!\n");
+ return CMD_WARNING_CONFIG_FAILED;
+ }
+
+ bgp->v_update_delay = update_delay;
+ bgp->v_establish_wait = establish_wait;
+
+ return CMD_SUCCESS;
+}
+
+static int bgp_update_delay_deconfig_vty(struct vty *vty)
+{
+ VTY_DECLVAR_CONTEXT(bgp, bgp);
+
+ /* If configured globally, cannot remove from one bgp instance */
+ if (bm->v_update_delay) {
+ vty_out(vty,
+ "%%Failed: bgp update-delay configured globally. Delete per-vrf not permitted\n");
+ return CMD_WARNING_CONFIG_FAILED;
+ }
+ bgp->v_update_delay = BGP_UPDATE_DELAY_DEF;
+ bgp->v_establish_wait = bgp->v_update_delay;
+
+ return CMD_SUCCESS;
+}
+
+void bgp_config_write_update_delay(struct vty *vty, struct bgp *bgp)
+{
+ /* If configured globally, no need to display per-instance value */
+ if (bgp->v_update_delay != bm->v_update_delay) {
+ vty_out(vty, " update-delay %d", bgp->v_update_delay);
+ if (bgp->v_update_delay != bgp->v_establish_wait)
+ vty_out(vty, " %d", bgp->v_establish_wait);
+ vty_out(vty, "\n");
+ }
+}
+
+/* Global update-delay configuration */
+DEFPY (bgp_global_update_delay,
+ bgp_global_update_delay_cmd,
+ "bgp update-delay (0-3600)$delay [(1-3600)$wait]",
+ BGP_STR
+ "Force initial delay for best-path and updates for all bgp instances\n"
+ "Max delay in seconds\n"
+ "Establish wait in seconds\n")
+{
+ return bgp_global_update_delay_config_vty(vty, delay, wait);
+}
+
+/* Global update-delay deconfiguration */
+DEFPY (no_bgp_global_update_delay,
+ no_bgp_global_update_delay_cmd,
+ "no bgp update-delay [(0-3600) [(1-3600)]]",
+ NO_STR
+ BGP_STR
+ "Force initial delay for best-path and updates\n"
+ "Max delay in seconds\n"
+ "Establish wait in seconds\n")
+{
+ return bgp_global_update_delay_deconfig_vty(vty);
+}
+
+/* Update-delay configuration */
+
+DEFPY (bgp_update_delay,
+ bgp_update_delay_cmd,
+ "update-delay (0-3600)$delay [(1-3600)$wait]",
+ "Force initial delay for best-path and updates\n"
+ "Max delay in seconds\n"
+ "Establish wait in seconds\n")
+{
+ return bgp_update_delay_config_vty(vty, delay, wait);
+}
+
+/* Update-delay deconfiguration */
+DEFPY (no_bgp_update_delay,
+ no_bgp_update_delay_cmd,
+ "no update-delay [(0-3600) [(1-3600)]]",
+ NO_STR
+ "Force initial delay for best-path and updates\n"
+ "Max delay in seconds\n"
+ "Establish wait in seconds\n")
+{
+ return bgp_update_delay_deconfig_vty(vty);
+}
+
+
+static int bgp_wpkt_quanta_config_vty(struct vty *vty, uint32_t quanta,
+ bool set)
+{
+ VTY_DECLVAR_CONTEXT(bgp, bgp);
+
+ quanta = set ? quanta : BGP_WRITE_PACKET_MAX;
+ atomic_store_explicit(&bgp->wpkt_quanta, quanta, memory_order_relaxed);
+
+ return CMD_SUCCESS;
+}
+
+static int bgp_rpkt_quanta_config_vty(struct vty *vty, uint32_t quanta,
+ bool set)
+{
+ VTY_DECLVAR_CONTEXT(bgp, bgp);
+
+ quanta = set ? quanta : BGP_READ_PACKET_MAX;
+ atomic_store_explicit(&bgp->rpkt_quanta, quanta, memory_order_relaxed);
+
+ return CMD_SUCCESS;
+}
+
+void bgp_config_write_wpkt_quanta(struct vty *vty, struct bgp *bgp)
+{
+ uint32_t quanta =
+ atomic_load_explicit(&bgp->wpkt_quanta, memory_order_relaxed);
+ if (quanta != BGP_WRITE_PACKET_MAX)
+ vty_out(vty, " write-quanta %d\n", quanta);
+}
+
+void bgp_config_write_rpkt_quanta(struct vty *vty, struct bgp *bgp)
+{
+ uint32_t quanta =
+ atomic_load_explicit(&bgp->rpkt_quanta, memory_order_relaxed);
+ if (quanta != BGP_READ_PACKET_MAX)
+ vty_out(vty, " read-quanta %d\n", quanta);
+}
+
+/* Packet quanta configuration
+ *
+ * XXX: The value set here controls the size of a stack buffer in the IO
+ * thread. When changing these limits be careful to prevent stack overflow.
+ *
+ * Furthermore, the maximums used here should correspond to
+ * BGP_WRITE_PACKET_MAX and BGP_READ_PACKET_MAX.
+ */
+DEFPY (bgp_wpkt_quanta,
+ bgp_wpkt_quanta_cmd,
+ "[no] write-quanta (1-64)$quanta",
+ NO_STR
+ "How many packets to write to peer socket per run\n"
+ "Number of packets\n")
+{
+ return bgp_wpkt_quanta_config_vty(vty, quanta, !no);
+}
+
+DEFPY (bgp_rpkt_quanta,
+ bgp_rpkt_quanta_cmd,
+ "[no] read-quanta (1-10)$quanta",
+ NO_STR
+ "How many packets to read from peer socket per I/O cycle\n"
+ "Number of packets\n")
+{
+ return bgp_rpkt_quanta_config_vty(vty, quanta, !no);
+}
+
+void bgp_config_write_coalesce_time(struct vty *vty, struct bgp *bgp)
+{
+ if (!bgp->heuristic_coalesce)
+ vty_out(vty, " coalesce-time %u\n", bgp->coalesce_time);
+}
+
+
+DEFUN (bgp_coalesce_time,
+ bgp_coalesce_time_cmd,
+ "coalesce-time (0-4294967295)",
+ "Subgroup coalesce timer\n"
+ "Subgroup coalesce timer value (in ms)\n")
+{
+ VTY_DECLVAR_CONTEXT(bgp, bgp);
+
+ int idx = 0;
+
+ bgp->heuristic_coalesce = false;
+
+ if (argv_find(argv, argc, "(0-4294967295)", &idx))
+ bgp->coalesce_time = strtoul(argv[idx]->arg, NULL, 10);
+
+ return CMD_SUCCESS;
+}
+
+DEFUN (no_bgp_coalesce_time,
+ no_bgp_coalesce_time_cmd,
+ "no coalesce-time (0-4294967295)",
+ NO_STR
+ "Subgroup coalesce timer\n"
+ "Subgroup coalesce timer value (in ms)\n")
+{
+ VTY_DECLVAR_CONTEXT(bgp, bgp);
+
+ bgp->heuristic_coalesce = true;
+ bgp->coalesce_time = BGP_DEFAULT_SUBGROUP_COALESCE_TIME;
+ return CMD_SUCCESS;
+}
+
+/* Maximum-paths configuration */
+DEFUN (bgp_maxpaths,
+ bgp_maxpaths_cmd,
+ "maximum-paths " CMD_RANGE_STR(1, MULTIPATH_NUM),
+ "Forward packets over multiple paths\n"
+ "Number of paths\n")
+{
+ int idx_number = 1;
+ return bgp_maxpaths_config_vty(vty, BGP_PEER_EBGP,
+ argv[idx_number]->arg, 0, 1);
+}
+
+ALIAS_HIDDEN(bgp_maxpaths, bgp_maxpaths_hidden_cmd,
+ "maximum-paths " CMD_RANGE_STR(1, MULTIPATH_NUM),
+ "Forward packets over multiple paths\n"
+ "Number of paths\n")
+
+DEFUN (bgp_maxpaths_ibgp,
+ bgp_maxpaths_ibgp_cmd,
+ "maximum-paths ibgp " CMD_RANGE_STR(1, MULTIPATH_NUM),
+ "Forward packets over multiple paths\n"
+ "iBGP-multipath\n"
+ "Number of paths\n")
+{
+ int idx_number = 2;
+ return bgp_maxpaths_config_vty(vty, BGP_PEER_IBGP,
+ argv[idx_number]->arg, 0, 1);
+}
+
+ALIAS_HIDDEN(bgp_maxpaths_ibgp, bgp_maxpaths_ibgp_hidden_cmd,
+ "maximum-paths ibgp " CMD_RANGE_STR(1, MULTIPATH_NUM),
+ "Forward packets over multiple paths\n"
+ "iBGP-multipath\n"
+ "Number of paths\n")
+
+DEFUN (bgp_maxpaths_ibgp_cluster,
+ bgp_maxpaths_ibgp_cluster_cmd,
+ "maximum-paths ibgp " CMD_RANGE_STR(1, MULTIPATH_NUM) " equal-cluster-length",
+ "Forward packets over multiple paths\n"
+ "iBGP-multipath\n"
+ "Number of paths\n"
+ "Match the cluster length\n")
+{
+ int idx_number = 2;
+ return bgp_maxpaths_config_vty(vty, BGP_PEER_IBGP,
+ argv[idx_number]->arg, true, 1);
+}
+
+ALIAS_HIDDEN(bgp_maxpaths_ibgp_cluster, bgp_maxpaths_ibgp_cluster_hidden_cmd,
+ "maximum-paths ibgp " CMD_RANGE_STR(
+ 1, MULTIPATH_NUM) " equal-cluster-length",
+ "Forward packets over multiple paths\n"
+ "iBGP-multipath\n"
+ "Number of paths\n"
+ "Match the cluster length\n")
+
+DEFUN (no_bgp_maxpaths,
+ no_bgp_maxpaths_cmd,
+ "no maximum-paths [" CMD_RANGE_STR(1, MULTIPATH_NUM) "]",
+ NO_STR
+ "Forward packets over multiple paths\n"
+ "Number of paths\n")
+{
+ return bgp_maxpaths_config_vty(vty, BGP_PEER_EBGP, NULL, 0, 0);
+}
+
+ALIAS_HIDDEN(no_bgp_maxpaths, no_bgp_maxpaths_hidden_cmd,
+ "no maximum-paths [" CMD_RANGE_STR(1, MULTIPATH_NUM) "]", NO_STR
+ "Forward packets over multiple paths\n"
+ "Number of paths\n")
+
+DEFUN (no_bgp_maxpaths_ibgp,
+ no_bgp_maxpaths_ibgp_cmd,
+ "no maximum-paths ibgp [" CMD_RANGE_STR(1, MULTIPATH_NUM) " [equal-cluster-length]]",
+ NO_STR
+ "Forward packets over multiple paths\n"
+ "iBGP-multipath\n"
+ "Number of paths\n"
+ "Match the cluster length\n")
+{
+ return bgp_maxpaths_config_vty(vty, BGP_PEER_IBGP, NULL, 0, 0);
+}
+
+ALIAS_HIDDEN(no_bgp_maxpaths_ibgp, no_bgp_maxpaths_ibgp_hidden_cmd,
+ "no maximum-paths ibgp [" CMD_RANGE_STR(
+ 1, MULTIPATH_NUM) " [equal-cluster-length]]",
+ NO_STR
+ "Forward packets over multiple paths\n"
+ "iBGP-multipath\n"
+ "Number of paths\n"
+ "Match the cluster length\n")
+
+static void bgp_config_write_maxpaths(struct vty *vty, struct bgp *bgp,
+ afi_t afi, safi_t safi)
+{
+ if (bgp->maxpaths[afi][safi].maxpaths_ebgp != multipath_num) {
+ vty_out(vty, " maximum-paths %d\n",
+ bgp->maxpaths[afi][safi].maxpaths_ebgp);
+ }
+
+ if (bgp->maxpaths[afi][safi].maxpaths_ibgp != multipath_num) {
+ vty_out(vty, " maximum-paths ibgp %d",
+ bgp->maxpaths[afi][safi].maxpaths_ibgp);
+ if (bgp->maxpaths[afi][safi].same_clusterlen)
+ vty_out(vty, " equal-cluster-length");
+ vty_out(vty, "\n");
+ }
+}
+
+/* BGP timers. */
+
+DEFUN (bgp_timers,
+ bgp_timers_cmd,
+ "timers bgp (0-65535) (0-65535)",
+ "Adjust routing timers\n"
+ "BGP timers\n"
+ "Keepalive interval\n"
+ "Holdtime\n")
+{
+ VTY_DECLVAR_CONTEXT(bgp, bgp);
+ int idx_number = 2;
+ int idx_number_2 = 3;
+ unsigned long keepalive = 0;
+ unsigned long holdtime = 0;
+
+ keepalive = strtoul(argv[idx_number]->arg, NULL, 10);
+ holdtime = strtoul(argv[idx_number_2]->arg, NULL, 10);
+
+ /* Holdtime value check. */
+ if (holdtime < 3 && holdtime != 0) {
+ vty_out(vty,
+ "%% hold time value must be either 0 or greater than 3\n");
+ return CMD_WARNING_CONFIG_FAILED;
+ }
+
+ bgp_timers_set(bgp, keepalive, holdtime, DFLT_BGP_CONNECT_RETRY,
+ BGP_DEFAULT_DELAYOPEN);
+
+ return CMD_SUCCESS;
+}
+
+DEFUN (no_bgp_timers,
+ no_bgp_timers_cmd,
+ "no timers bgp [(0-65535) (0-65535)]",
+ NO_STR
+ "Adjust routing timers\n"
+ "BGP timers\n"
+ "Keepalive interval\n"
+ "Holdtime\n")
+{
+ VTY_DECLVAR_CONTEXT(bgp, bgp);
+ bgp_timers_set(bgp, DFLT_BGP_KEEPALIVE, DFLT_BGP_HOLDTIME,
+ DFLT_BGP_CONNECT_RETRY, BGP_DEFAULT_DELAYOPEN);
+
+ return CMD_SUCCESS;
+}
+
+/* BGP minimum holdtime. */
+
+DEFUN(bgp_minimum_holdtime, bgp_minimum_holdtime_cmd,
+ "bgp minimum-holdtime (1-65535)",
+ "BGP specific commands\n"
+ "BGP minimum holdtime\n"
+ "Seconds\n")
+{
+ VTY_DECLVAR_CONTEXT(bgp, bgp);
+ int idx_number = 2;
+ unsigned long min_holdtime;
+
+ min_holdtime = strtoul(argv[idx_number]->arg, NULL, 10);
+
+ bgp->default_min_holdtime = min_holdtime;
+
+ return CMD_SUCCESS;
+}
+
+DEFUN(no_bgp_minimum_holdtime, no_bgp_minimum_holdtime_cmd,
+ "no bgp minimum-holdtime [(1-65535)]",
+ NO_STR
+ "BGP specific commands\n"
+ "BGP minimum holdtime\n"
+ "Seconds\n")
+{
+ VTY_DECLVAR_CONTEXT(bgp, bgp);
+
+ bgp->default_min_holdtime = 0;
+
+ return CMD_SUCCESS;
+}
+
+DEFUN (bgp_client_to_client_reflection,
+ bgp_client_to_client_reflection_cmd,
+ "bgp client-to-client reflection",
+ BGP_STR
+ "Configure client to client route reflection\n"
+ "reflection of routes allowed\n")
+{
+ VTY_DECLVAR_CONTEXT(bgp, bgp);
+ UNSET_FLAG(bgp->flags, BGP_FLAG_NO_CLIENT_TO_CLIENT);
+ bgp_clear_star_soft_out(vty, bgp->name);
+
+ return CMD_SUCCESS;
+}
+
+DEFUN (no_bgp_client_to_client_reflection,
+ no_bgp_client_to_client_reflection_cmd,
+ "no bgp client-to-client reflection",
+ NO_STR
+ BGP_STR
+ "Configure client to client route reflection\n"
+ "reflection of routes allowed\n")
+{
+ VTY_DECLVAR_CONTEXT(bgp, bgp);
+ SET_FLAG(bgp->flags, BGP_FLAG_NO_CLIENT_TO_CLIENT);
+ bgp_clear_star_soft_out(vty, bgp->name);
+
+ return CMD_SUCCESS;
+}
+
+/* "bgp always-compare-med" configuration. */
+DEFUN (bgp_always_compare_med,
+ bgp_always_compare_med_cmd,
+ "bgp always-compare-med",
+ BGP_STR
+ "Allow comparing MED from different neighbors\n")
+{
+ VTY_DECLVAR_CONTEXT(bgp, bgp);
+ SET_FLAG(bgp->flags, BGP_FLAG_ALWAYS_COMPARE_MED);
+ bgp_recalculate_all_bestpaths(bgp);
+
+ return CMD_SUCCESS;
+}
+
+DEFUN (no_bgp_always_compare_med,
+ no_bgp_always_compare_med_cmd,
+ "no bgp always-compare-med",
+ NO_STR
+ BGP_STR
+ "Allow comparing MED from different neighbors\n")
+{
+ VTY_DECLVAR_CONTEXT(bgp, bgp);
+ UNSET_FLAG(bgp->flags, BGP_FLAG_ALWAYS_COMPARE_MED);
+ bgp_recalculate_all_bestpaths(bgp);
+
+ return CMD_SUCCESS;
+}
+
+
+DEFUN(bgp_ebgp_requires_policy, bgp_ebgp_requires_policy_cmd,
+ "bgp ebgp-requires-policy",
+ BGP_STR
+ "Require in and out policy for eBGP peers (RFC8212)\n")
+{
+ VTY_DECLVAR_CONTEXT(bgp, bgp);
+ SET_FLAG(bgp->flags, BGP_FLAG_EBGP_REQUIRES_POLICY);
+ return CMD_SUCCESS;
+}
+
+DEFUN(no_bgp_ebgp_requires_policy, no_bgp_ebgp_requires_policy_cmd,
+ "no bgp ebgp-requires-policy",
+ NO_STR
+ BGP_STR
+ "Require in and out policy for eBGP peers (RFC8212)\n")
+{
+ VTY_DECLVAR_CONTEXT(bgp, bgp);
+ UNSET_FLAG(bgp->flags, BGP_FLAG_EBGP_REQUIRES_POLICY);
+ return CMD_SUCCESS;
+}
+
+DEFUN(bgp_suppress_duplicates, bgp_suppress_duplicates_cmd,
+ "bgp suppress-duplicates",
+ BGP_STR
+ "Suppress duplicate updates if the route actually not changed\n")
+{
+ VTY_DECLVAR_CONTEXT(bgp, bgp);
+ SET_FLAG(bgp->flags, BGP_FLAG_SUPPRESS_DUPLICATES);
+ return CMD_SUCCESS;
+}
+
+DEFUN(no_bgp_suppress_duplicates, no_bgp_suppress_duplicates_cmd,
+ "no bgp suppress-duplicates",
+ NO_STR
+ BGP_STR
+ "Suppress duplicate updates if the route actually not changed\n")
+{
+ VTY_DECLVAR_CONTEXT(bgp, bgp);
+ UNSET_FLAG(bgp->flags, BGP_FLAG_SUPPRESS_DUPLICATES);
+ return CMD_SUCCESS;
+}
+
+DEFUN(bgp_reject_as_sets, bgp_reject_as_sets_cmd,
+ "bgp reject-as-sets",
+ BGP_STR
+ "Reject routes with AS_SET or AS_CONFED_SET flag\n")
+{
+ VTY_DECLVAR_CONTEXT(bgp, bgp);
+ struct listnode *node, *nnode;
+ struct peer *peer;
+
+ bgp->reject_as_sets = true;
+
+ /* Reset existing BGP sessions to reject routes
+ * with aspath containing AS_SET or AS_CONFED_SET.
+ */
+ for (ALL_LIST_ELEMENTS(bgp->peer, node, nnode, peer)) {
+ if (BGP_IS_VALID_STATE_FOR_NOTIF(peer->status)) {
+ peer->last_reset = PEER_DOWN_AS_SETS_REJECT;
+ bgp_notify_send(peer, BGP_NOTIFY_CEASE,
+ BGP_NOTIFY_CEASE_CONFIG_CHANGE);
+ }
+ }
+
+ return CMD_SUCCESS;
+}
+
+DEFUN(no_bgp_reject_as_sets, no_bgp_reject_as_sets_cmd,
+ "no bgp reject-as-sets",
+ NO_STR
+ BGP_STR
+ "Reject routes with AS_SET or AS_CONFED_SET flag\n")
+{
+ VTY_DECLVAR_CONTEXT(bgp, bgp);
+ struct listnode *node, *nnode;
+ struct peer *peer;
+
+ bgp->reject_as_sets = false;
+
+ /* Reset existing BGP sessions to reject routes
+ * with aspath containing AS_SET or AS_CONFED_SET.
+ */
+ for (ALL_LIST_ELEMENTS(bgp->peer, node, nnode, peer)) {
+ if (BGP_IS_VALID_STATE_FOR_NOTIF(peer->status)) {
+ peer->last_reset = PEER_DOWN_AS_SETS_REJECT;
+ bgp_notify_send(peer, BGP_NOTIFY_CEASE,
+ BGP_NOTIFY_CEASE_CONFIG_CHANGE);
+ }
+ }
+
+ return CMD_SUCCESS;
+}
+
+/* "bgp deterministic-med" configuration. */
+DEFUN (bgp_deterministic_med,
+ bgp_deterministic_med_cmd,
+ "bgp deterministic-med",
+ BGP_STR
+ "Pick the best-MED path among paths advertised from the neighboring AS\n")
+{
+ VTY_DECLVAR_CONTEXT(bgp, bgp);
+
+ if (!CHECK_FLAG(bgp->flags, BGP_FLAG_DETERMINISTIC_MED)) {
+ SET_FLAG(bgp->flags, BGP_FLAG_DETERMINISTIC_MED);
+ bgp_recalculate_all_bestpaths(bgp);
+ }
+
+ return CMD_SUCCESS;
+}
+
+DEFUN (no_bgp_deterministic_med,
+ no_bgp_deterministic_med_cmd,
+ "no bgp deterministic-med",
+ NO_STR
+ BGP_STR
+ "Pick the best-MED path among paths advertised from the neighboring AS\n")
+{
+ VTY_DECLVAR_CONTEXT(bgp, bgp);
+ int bestpath_per_as_used;
+ afi_t afi;
+ safi_t safi;
+ struct peer *peer;
+ struct listnode *node, *nnode;
+
+ if (CHECK_FLAG(bgp->flags, BGP_FLAG_DETERMINISTIC_MED)) {
+ bestpath_per_as_used = 0;
+
+ for (ALL_LIST_ELEMENTS(bgp->peer, node, nnode, peer)) {
+ FOREACH_AFI_SAFI (afi, safi)
+ if (bgp_addpath_dmed_required(
+ peer->addpath_type[afi][safi])) {
+ bestpath_per_as_used = 1;
+ break;
+ }
+
+ if (bestpath_per_as_used)
+ break;
+ }
+
+ if (bestpath_per_as_used) {
+ vty_out(vty,
+ "bgp deterministic-med cannot be disabled while addpath-tx-bestpath-per-AS is in use\n");
+ return CMD_WARNING_CONFIG_FAILED;
+ } else {
+ UNSET_FLAG(bgp->flags, BGP_FLAG_DETERMINISTIC_MED);
+ bgp_recalculate_all_bestpaths(bgp);
+ }
+ }
+
+ return CMD_SUCCESS;
+}
+
+/* "bgp graceful-restart mode" configuration. */
+DEFUN (bgp_graceful_restart,
+ bgp_graceful_restart_cmd,
+ "bgp graceful-restart",
+ BGP_STR
+ GR_CMD
+ )
+{
+ int ret = BGP_GR_FAILURE;
+
+ if (BGP_DEBUG(graceful_restart, GRACEFUL_RESTART))
+ zlog_debug("[BGP_GR] bgp_graceful_restart_cmd : START ");
+
+ VTY_DECLVAR_CONTEXT(bgp, bgp);
+
+ ret = bgp_gr_update_all(bgp, GLOBAL_GR_CMD);
+
+ VTY_BGP_GR_ROUTER_DETECT_AND_SEND_CAPABILITY_TO_ZEBRA(bgp, bgp->peer,
+ ret);
+
+ if (BGP_DEBUG(graceful_restart, GRACEFUL_RESTART))
+ zlog_debug("[BGP_GR] bgp_graceful_restart_cmd : END ");
+ vty_out(vty,
+ "Graceful restart configuration changed, reset all peers to take effect\n");
+ return bgp_vty_return(vty, ret);
+}
+
+DEFUN (no_bgp_graceful_restart,
+ no_bgp_graceful_restart_cmd,
+ "no bgp graceful-restart",
+ NO_STR
+ BGP_STR
+ NO_GR_CMD
+ )
+{
+ VTY_DECLVAR_CONTEXT(bgp, bgp);
+
+ if (BGP_DEBUG(graceful_restart, GRACEFUL_RESTART))
+ zlog_debug("[BGP_GR] no_bgp_graceful_restart_cmd : START ");
+
+ int ret = BGP_GR_FAILURE;
+
+ ret = bgp_gr_update_all(bgp, NO_GLOBAL_GR_CMD);
+
+ VTY_BGP_GR_ROUTER_DETECT_AND_SEND_CAPABILITY_TO_ZEBRA(bgp, bgp->peer,
+ ret);
+
+ if (BGP_DEBUG(graceful_restart, GRACEFUL_RESTART))
+ zlog_debug("[BGP_GR] no_bgp_graceful_restart_cmd : END ");
+ vty_out(vty,
+ "Graceful restart configuration changed, reset all peers to take effect\n");
+
+ return bgp_vty_return(vty, ret);
+}
+
+DEFUN (bgp_graceful_restart_stalepath_time,
+ bgp_graceful_restart_stalepath_time_cmd,
+ "bgp graceful-restart stalepath-time (1-4095)",
+ BGP_STR
+ "Graceful restart capability parameters\n"
+ "Set the max time to hold onto restarting peer's stale paths\n"
+ "Delay value (seconds)\n")
+{
+ VTY_DECLVAR_CONTEXT(bgp, bgp);
+ int idx_number = 3;
+ uint32_t stalepath;
+
+ stalepath = strtoul(argv[idx_number]->arg, NULL, 10);
+ bgp->stalepath_time = stalepath;
+ return CMD_SUCCESS;
+}
+
+DEFUN (bgp_graceful_restart_restart_time,
+ bgp_graceful_restart_restart_time_cmd,
+ "bgp graceful-restart restart-time (0-4095)",
+ BGP_STR
+ "Graceful restart capability parameters\n"
+ "Set the time to wait to delete stale routes before a BGP open message is received\n"
+ "Delay value (seconds)\n")
+{
+ VTY_DECLVAR_CONTEXT(bgp, bgp);
+ int idx_number = 3;
+ uint32_t restart;
+
+ restart = strtoul(argv[idx_number]->arg, NULL, 10);
+ bgp->restart_time = restart;
+ return CMD_SUCCESS;
+}
+
+DEFUN (bgp_graceful_restart_select_defer_time,
+ bgp_graceful_restart_select_defer_time_cmd,
+ "bgp graceful-restart select-defer-time (0-3600)",
+ BGP_STR
+ "Graceful restart capability parameters\n"
+ "Set the time to defer the BGP route selection after restart\n"
+ "Delay value (seconds, 0 - disable)\n")
+{
+ VTY_DECLVAR_CONTEXT(bgp, bgp);
+ int idx_number = 3;
+ uint32_t defer_time;
+
+ defer_time = strtoul(argv[idx_number]->arg, NULL, 10);
+ bgp->select_defer_time = defer_time;
+ if (defer_time == 0)
+ SET_FLAG(bgp->flags, BGP_FLAG_SELECT_DEFER_DISABLE);
+ else
+ UNSET_FLAG(bgp->flags, BGP_FLAG_SELECT_DEFER_DISABLE);
+
+ return CMD_SUCCESS;
+}
+
+DEFUN (no_bgp_graceful_restart_stalepath_time,
+ no_bgp_graceful_restart_stalepath_time_cmd,
+ "no bgp graceful-restart stalepath-time [(1-4095)]",
+ NO_STR
+ BGP_STR
+ "Graceful restart capability parameters\n"
+ "Set the max time to hold onto restarting peer's stale paths\n"
+ "Delay value (seconds)\n")
+{
+ VTY_DECLVAR_CONTEXT(bgp, bgp);
+
+ bgp->stalepath_time = BGP_DEFAULT_STALEPATH_TIME;
+ return CMD_SUCCESS;
+}
+
+DEFUN (no_bgp_graceful_restart_restart_time,
+ no_bgp_graceful_restart_restart_time_cmd,
+ "no bgp graceful-restart restart-time [(0-4095)]",
+ NO_STR
+ BGP_STR
+ "Graceful restart capability parameters\n"
+ "Set the time to wait to delete stale routes before a BGP open message is received\n"
+ "Delay value (seconds)\n")
+{
+ VTY_DECLVAR_CONTEXT(bgp, bgp);
+
+ bgp->restart_time = BGP_DEFAULT_RESTART_TIME;
+ return CMD_SUCCESS;
+}
+
+DEFUN (no_bgp_graceful_restart_select_defer_time,
+ no_bgp_graceful_restart_select_defer_time_cmd,
+ "no bgp graceful-restart select-defer-time [(0-3600)]",
+ NO_STR
+ BGP_STR
+ "Graceful restart capability parameters\n"
+ "Set the time to defer the BGP route selection after restart\n"
+ "Delay value (seconds)\n")
+{
+ VTY_DECLVAR_CONTEXT(bgp, bgp);
+
+ bgp->select_defer_time = BGP_DEFAULT_SELECT_DEFERRAL_TIME;
+ UNSET_FLAG(bgp->flags, BGP_FLAG_SELECT_DEFER_DISABLE);
+
+ return CMD_SUCCESS;
+}
+
+DEFUN (bgp_graceful_restart_preserve_fw,
+ bgp_graceful_restart_preserve_fw_cmd,
+ "bgp graceful-restart preserve-fw-state",
+ BGP_STR
+ "Graceful restart capability parameters\n"
+ "Sets F-bit indication that fib is preserved while doing Graceful Restart\n")
+{
+ VTY_DECLVAR_CONTEXT(bgp, bgp);
+ SET_FLAG(bgp->flags, BGP_FLAG_GR_PRESERVE_FWD);
+ return CMD_SUCCESS;
+}
+
+DEFUN (no_bgp_graceful_restart_preserve_fw,
+ no_bgp_graceful_restart_preserve_fw_cmd,
+ "no bgp graceful-restart preserve-fw-state",
+ NO_STR
+ BGP_STR
+ "Graceful restart capability parameters\n"
+ "Unsets F-bit indication that fib is preserved while doing Graceful Restart\n")
+{
+ VTY_DECLVAR_CONTEXT(bgp, bgp);
+ UNSET_FLAG(bgp->flags, BGP_FLAG_GR_PRESERVE_FWD);
+ return CMD_SUCCESS;
+}
+
+DEFPY (bgp_graceful_restart_notification,
+ bgp_graceful_restart_notification_cmd,
+ "[no$no] bgp graceful-restart notification",
+ NO_STR
+ BGP_STR
+ "Graceful restart capability parameters\n"
+ "Indicate Graceful Restart support for BGP NOTIFICATION messages\n")
+{
+ VTY_DECLVAR_CONTEXT(bgp, bgp);
+
+ if (no)
+ UNSET_FLAG(bgp->flags, BGP_FLAG_GRACEFUL_NOTIFICATION);
+ else
+ SET_FLAG(bgp->flags, BGP_FLAG_GRACEFUL_NOTIFICATION);
+
+ return CMD_SUCCESS;
+}
+
+DEFPY (bgp_administrative_reset,
+ bgp_administrative_reset_cmd,
+ "[no$no] bgp hard-administrative-reset",
+ NO_STR
+ BGP_STR
+ "Send Hard Reset CEASE Notification for 'Administrative Reset'\n")
+{
+ VTY_DECLVAR_CONTEXT(bgp, bgp);
+
+ if (no)
+ UNSET_FLAG(bgp->flags, BGP_FLAG_HARD_ADMIN_RESET);
+ else
+ SET_FLAG(bgp->flags, BGP_FLAG_HARD_ADMIN_RESET);
+
+ return CMD_SUCCESS;
+}
+
+DEFUN (bgp_graceful_restart_disable,
+ bgp_graceful_restart_disable_cmd,
+ "bgp graceful-restart-disable",
+ BGP_STR
+ GR_DISABLE)
+{
+ int ret = BGP_GR_FAILURE;
+
+ if (BGP_DEBUG(graceful_restart, GRACEFUL_RESTART))
+ zlog_debug(
+ "[BGP_GR] bgp_graceful_restart_disable_cmd : START ");
+
+ VTY_DECLVAR_CONTEXT(bgp, bgp);
+
+ ret = bgp_gr_update_all(bgp, GLOBAL_DISABLE_CMD);
+
+ VTY_BGP_GR_ROUTER_DETECT_AND_SEND_CAPABILITY_TO_ZEBRA(bgp,
+ bgp->peer, ret);
+
+ if (BGP_DEBUG(graceful_restart, GRACEFUL_RESTART))
+ zlog_debug(
+ "[BGP_GR] bgp_graceful_restart_disable_cmd : END ");
+ vty_out(vty,
+ "Graceful restart configuration changed, reset all peers to take effect\n");
+
+ return bgp_vty_return(vty, ret);
+}
+
+DEFUN (no_bgp_graceful_restart_disable,
+ no_bgp_graceful_restart_disable_cmd,
+ "no bgp graceful-restart-disable",
+ NO_STR
+ BGP_STR
+ NO_GR_DISABLE
+ )
+{
+ VTY_DECLVAR_CONTEXT(bgp, bgp);
+
+ if (BGP_DEBUG(graceful_restart, GRACEFUL_RESTART))
+ zlog_debug(
+ "[BGP_GR] no_bgp_graceful_restart_disable_cmd : START ");
+
+ int ret = BGP_GR_FAILURE;
+
+ ret = bgp_gr_update_all(bgp, NO_GLOBAL_DISABLE_CMD);
+
+ VTY_BGP_GR_ROUTER_DETECT_AND_SEND_CAPABILITY_TO_ZEBRA(bgp, bgp->peer,
+ ret);
+
+ if (BGP_DEBUG(graceful_restart, GRACEFUL_RESTART))
+ zlog_debug(
+ "[BGP_GR] no_bgp_graceful_restart_disable_cmd : END ");
+ vty_out(vty,
+ "Graceful restart configuration changed, reset all peers to take effect\n");
+
+ return bgp_vty_return(vty, ret);
+}
+
+DEFUN (bgp_neighbor_graceful_restart_set,
+ bgp_neighbor_graceful_restart_set_cmd,
+ "neighbor <A.B.C.D|X:X::X:X|WORD> graceful-restart",
+ NEIGHBOR_STR
+ NEIGHBOR_ADDR_STR2
+ GR_NEIGHBOR_CMD
+ )
+{
+ int idx_peer = 1;
+ struct peer *peer;
+ int ret = BGP_GR_FAILURE;
+
+ VTY_BGP_GR_DEFINE_LOOP_VARIABLE;
+
+ if (BGP_DEBUG(graceful_restart, GRACEFUL_RESTART))
+ zlog_debug(
+ "[BGP_GR] bgp_neighbor_graceful_restart_set_cmd : START ");
+
+ peer = peer_and_group_lookup_vty(vty, argv[idx_peer]->arg);
+ if (!peer)
+ return CMD_WARNING_CONFIG_FAILED;
+
+ ret = bgp_neighbor_graceful_restart(peer, PEER_GR_CMD);
+
+ VTY_BGP_GR_ROUTER_DETECT(bgp, peer, peer->bgp->peer);
+ VTY_SEND_BGP_GR_CAPABILITY_TO_ZEBRA(peer->bgp, ret);
+
+ if (BGP_DEBUG(graceful_restart, GRACEFUL_RESTART))
+ zlog_debug(
+ "[BGP_GR] bgp_neighbor_graceful_restart_set_cmd : END ");
+ vty_out(vty,
+ "Graceful restart configuration changed, reset this peer to take effect\n");
+
+ return bgp_vty_return(vty, ret);
+}
+
+DEFUN (no_bgp_neighbor_graceful_restart,
+ no_bgp_neighbor_graceful_restart_set_cmd,
+ "no neighbor <A.B.C.D|X:X::X:X|WORD> graceful-restart",
+ NO_STR
+ NEIGHBOR_STR
+ NEIGHBOR_ADDR_STR2
+ NO_GR_NEIGHBOR_CMD
+ )
+{
+ int idx_peer = 2;
+ int ret = BGP_GR_FAILURE;
+ struct peer *peer;
+
+ VTY_BGP_GR_DEFINE_LOOP_VARIABLE;
+
+ peer = peer_and_group_lookup_vty(vty, argv[idx_peer]->arg);
+ if (!peer)
+ return CMD_WARNING_CONFIG_FAILED;
+
+ if (BGP_DEBUG(graceful_restart, GRACEFUL_RESTART))
+ zlog_debug(
+ "[BGP_GR] no_bgp_neighbor_graceful_restart_set_cmd : START ");
+
+ ret = bgp_neighbor_graceful_restart(peer, NO_PEER_GR_CMD);
+
+ VTY_BGP_GR_ROUTER_DETECT(bgp, peer, peer->bgp->peer);
+ VTY_SEND_BGP_GR_CAPABILITY_TO_ZEBRA(peer->bgp, ret);
+
+ if (BGP_DEBUG(graceful_restart, GRACEFUL_RESTART))
+ zlog_debug(
+ "[BGP_GR] no_bgp_neighbor_graceful_restart_set_cmd : END ");
+ vty_out(vty,
+ "Graceful restart configuration changed, reset this peer to take effect\n");
+
+ return bgp_vty_return(vty, ret);
+}
+
+DEFUN (bgp_neighbor_graceful_restart_helper_set,
+ bgp_neighbor_graceful_restart_helper_set_cmd,
+ "neighbor <A.B.C.D|X:X::X:X|WORD> graceful-restart-helper",
+ NEIGHBOR_STR
+ NEIGHBOR_ADDR_STR2
+ GR_NEIGHBOR_HELPER_CMD
+ )
+{
+ int idx_peer = 1;
+ struct peer *peer;
+ int ret = BGP_GR_FAILURE;
+
+ VTY_BGP_GR_DEFINE_LOOP_VARIABLE;
+
+ if (BGP_DEBUG(graceful_restart, GRACEFUL_RESTART))
+ zlog_debug(
+ "[BGP_GR] bgp_neighbor_graceful_restart_helper_set_cmd : START ");
+
+ peer = peer_and_group_lookup_vty(vty, argv[idx_peer]->arg);
+
+ if (!peer)
+ return CMD_WARNING_CONFIG_FAILED;
+
+
+ ret = bgp_neighbor_graceful_restart(peer, PEER_HELPER_CMD);
+
+ VTY_BGP_GR_ROUTER_DETECT(bgp, peer, peer->bgp->peer);
+ VTY_SEND_BGP_GR_CAPABILITY_TO_ZEBRA(peer->bgp, ret);
+
+ if (BGP_DEBUG(graceful_restart, GRACEFUL_RESTART))
+ zlog_debug(
+ "[BGP_GR] bgp_neighbor_graceful_restart_helper_set_cmd : END ");
+ vty_out(vty,
+ "Graceful restart configuration changed, reset this peer to take effect\n");
+
+ return bgp_vty_return(vty, ret);
+}
+
+DEFUN (no_bgp_neighbor_graceful_restart_helper,
+ no_bgp_neighbor_graceful_restart_helper_set_cmd,
+ "no neighbor <A.B.C.D|X:X::X:X|WORD> graceful-restart-helper",
+ NO_STR
+ NEIGHBOR_STR
+ NEIGHBOR_ADDR_STR2
+ NO_GR_NEIGHBOR_HELPER_CMD
+ )
+{
+ int idx_peer = 2;
+ int ret = BGP_GR_FAILURE;
+ struct peer *peer;
+
+ VTY_BGP_GR_DEFINE_LOOP_VARIABLE;
+
+ peer = peer_and_group_lookup_vty(vty, argv[idx_peer]->arg);
+ if (!peer)
+ return CMD_WARNING_CONFIG_FAILED;
+
+ if (BGP_DEBUG(graceful_restart, GRACEFUL_RESTART))
+ zlog_debug(
+ "[BGP_GR] no_bgp_neighbor_graceful_restart_helper_set_cmd : START ");
+
+ ret = bgp_neighbor_graceful_restart(peer, NO_PEER_HELPER_CMD);
+
+ VTY_BGP_GR_ROUTER_DETECT(bgp, peer, peer->bgp->peer);
+ VTY_SEND_BGP_GR_CAPABILITY_TO_ZEBRA(peer->bgp, ret);
+
+ if (BGP_DEBUG(graceful_restart, GRACEFUL_RESTART))
+ zlog_debug(
+ "[BGP_GR] no_bgp_neighbor_graceful_restart_helper_set_cmd : END ");
+ vty_out(vty,
+ "Graceful restart configuration changed, reset this peer to take effect\n");
+
+ return bgp_vty_return(vty, ret);
+}
+
+DEFUN (bgp_neighbor_graceful_restart_disable_set,
+ bgp_neighbor_graceful_restart_disable_set_cmd,
+ "neighbor <A.B.C.D|X:X::X:X|WORD> graceful-restart-disable",
+ NEIGHBOR_STR
+ NEIGHBOR_ADDR_STR2
+ GR_NEIGHBOR_DISABLE_CMD
+ )
+{
+ int idx_peer = 1;
+ struct peer *peer;
+ int ret = BGP_GR_FAILURE;
+
+ VTY_BGP_GR_DEFINE_LOOP_VARIABLE;
+
+ if (BGP_DEBUG(graceful_restart, GRACEFUL_RESTART))
+ zlog_debug(
+ "[BGP_GR] bgp_neighbor_graceful_restart_disable_set_cmd : START ");
+
+ peer = peer_and_group_lookup_vty(vty, argv[idx_peer]->arg);
+ if (!peer)
+ return CMD_WARNING_CONFIG_FAILED;
+
+ ret = bgp_neighbor_graceful_restart(peer, PEER_DISABLE_CMD);
+
+ if (peer->bgp->t_startup)
+ bgp_peer_gr_flags_update(peer);
+
+ VTY_BGP_GR_ROUTER_DETECT(bgp, peer, peer->bgp->peer);
+ VTY_SEND_BGP_GR_CAPABILITY_TO_ZEBRA(peer->bgp, ret);
+
+ if (BGP_DEBUG(graceful_restart, GRACEFUL_RESTART))
+ zlog_debug(
+ "[BGP_GR]bgp_neighbor_graceful_restart_disable_set_cmd : END ");
+ vty_out(vty,
+ "Graceful restart configuration changed, reset this peer to take effect\n");
+
+ return bgp_vty_return(vty, ret);
+}
+
+DEFUN (no_bgp_neighbor_graceful_restart_disable,
+ no_bgp_neighbor_graceful_restart_disable_set_cmd,
+ "no neighbor <A.B.C.D|X:X::X:X|WORD> graceful-restart-disable",
+ NO_STR
+ NEIGHBOR_STR
+ NEIGHBOR_ADDR_STR2
+ NO_GR_NEIGHBOR_DISABLE_CMD
+ )
+{
+ int idx_peer = 2;
+ int ret = BGP_GR_FAILURE;
+ struct peer *peer;
+
+ VTY_BGP_GR_DEFINE_LOOP_VARIABLE;
+
+ peer = peer_and_group_lookup_vty(vty, argv[idx_peer]->arg);
+ if (!peer)
+ return CMD_WARNING_CONFIG_FAILED;
+
+ if (BGP_DEBUG(graceful_restart, GRACEFUL_RESTART))
+ zlog_debug(
+ "[BGP_GR] no_bgp_neighbor_graceful_restart_disable_set_cmd : START ");
+
+ ret = bgp_neighbor_graceful_restart(peer, NO_PEER_DISABLE_CMD);
+
+ VTY_BGP_GR_ROUTER_DETECT(bgp, peer, peer->bgp->peer);
+ VTY_SEND_BGP_GR_CAPABILITY_TO_ZEBRA(peer->bgp, ret);
+
+ if (BGP_DEBUG(graceful_restart, GRACEFUL_RESTART))
+ zlog_debug(
+ "[BGP_GR] no_bgp_neighbor_graceful_restart_disable_set_cmd : END ");
+ vty_out(vty,
+ "Graceful restart configuration changed, reset this peer to take effect\n");
+
+ return bgp_vty_return(vty, ret);
+}
+
+DEFUN_HIDDEN (bgp_graceful_restart_disable_eor,
+ bgp_graceful_restart_disable_eor_cmd,
+ "bgp graceful-restart disable-eor",
+ BGP_STR
+ "Graceful restart configuration parameters\n"
+ "Disable EOR Check\n")
+{
+ VTY_DECLVAR_CONTEXT(bgp, bgp);
+ SET_FLAG(bgp->flags, BGP_FLAG_GR_DISABLE_EOR);
+
+ return CMD_SUCCESS;
+}
+
+DEFUN_HIDDEN (no_bgp_graceful_restart_disable_eor,
+ no_bgp_graceful_restart_disable_eor_cmd,
+ "no bgp graceful-restart disable-eor",
+ NO_STR
+ BGP_STR
+ "Graceful restart configuration parameters\n"
+ "Disable EOR Check\n")
+{
+ VTY_DECLVAR_CONTEXT(bgp, bgp);
+ UNSET_FLAG(bgp->flags, BGP_FLAG_GR_DISABLE_EOR);
+
+ return CMD_SUCCESS;
+}
+
+DEFUN (bgp_graceful_restart_rib_stale_time,
+ bgp_graceful_restart_rib_stale_time_cmd,
+ "bgp graceful-restart rib-stale-time (1-3600)",
+ BGP_STR
+ "Graceful restart configuration parameters\n"
+ "Specify the stale route removal timer in rib\n"
+ "Delay value (seconds)\n")
+{
+ VTY_DECLVAR_CONTEXT(bgp, bgp);
+ int idx_number = 3;
+ uint32_t stale_time;
+
+ stale_time = strtoul(argv[idx_number]->arg, NULL, 10);
+ bgp->rib_stale_time = stale_time;
+ /* Send the stale timer update message to RIB */
+ if (bgp_zebra_stale_timer_update(bgp))
+ return CMD_WARNING;
+
+ return CMD_SUCCESS;
+}
+
+DEFUN (no_bgp_graceful_restart_rib_stale_time,
+ no_bgp_graceful_restart_rib_stale_time_cmd,
+ "no bgp graceful-restart rib-stale-time [(1-3600)]",
+ NO_STR
+ BGP_STR
+ "Graceful restart configuration parameters\n"
+ "Specify the stale route removal timer in rib\n"
+ "Delay value (seconds)\n")
+{
+ VTY_DECLVAR_CONTEXT(bgp, bgp);
+
+ bgp->rib_stale_time = BGP_DEFAULT_RIB_STALE_TIME;
+ /* Send the stale timer update message to RIB */
+ if (bgp_zebra_stale_timer_update(bgp))
+ return CMD_WARNING;
+
+ return CMD_SUCCESS;
+}
+
+DEFUN(bgp_llgr_stalepath_time, bgp_llgr_stalepath_time_cmd,
+ "bgp long-lived-graceful-restart stale-time (1-4294967295)",
+ BGP_STR
+ "Enable Long-lived Graceful Restart\n"
+ "Specifies maximum time to wait before purging long-lived stale routes\n"
+ "Stale time value (seconds)\n")
+{
+ VTY_DECLVAR_CONTEXT(bgp, bgp);
+
+ uint32_t llgr_stale_time;
+
+ llgr_stale_time = strtoul(argv[3]->arg, NULL, 10);
+ bgp->llgr_stale_time = llgr_stale_time;
+
+ return CMD_SUCCESS;
+}
+
+DEFUN(no_bgp_llgr_stalepath_time, no_bgp_llgr_stalepath_time_cmd,
+ "no bgp long-lived-graceful-restart stale-time [(1-4294967295)]",
+ NO_STR BGP_STR
+ "Enable Long-lived Graceful Restart\n"
+ "Specifies maximum time to wait before purging long-lived stale routes\n"
+ "Stale time value (seconds)\n")
+{
+ VTY_DECLVAR_CONTEXT(bgp, bgp);
+
+ bgp->llgr_stale_time = BGP_DEFAULT_LLGR_STALE_TIME;
+
+ return CMD_SUCCESS;
+}
+
+static inline void bgp_initiate_graceful_shut_unshut(struct vty *vty,
+ struct bgp *bgp)
+{
+ bgp_static_redo_import_check(bgp);
+ bgp_redistribute_redo(bgp);
+ bgp_clear_star_soft_out(vty, bgp->name);
+ bgp_clear_star_soft_in(vty, bgp->name);
+}
+
+static int bgp_global_graceful_shutdown_config_vty(struct vty *vty)
+{
+ struct listnode *node, *nnode;
+ struct bgp *bgp;
+ bool vrf_cfg = false;
+
+ if (CHECK_FLAG(bm->flags, BM_FLAG_GRACEFUL_SHUTDOWN))
+ return CMD_SUCCESS;
+
+ /* See if graceful-shutdown is set per-vrf and warn user to delete */
+ for (ALL_LIST_ELEMENTS(bm->bgp, node, nnode, bgp)) {
+ if (CHECK_FLAG(bgp->flags, BGP_FLAG_GRACEFUL_SHUTDOWN)) {
+ vty_out(vty,
+ "%% graceful-shutdown configuration found in vrf %s\n",
+ bgp->inst_type == BGP_INSTANCE_TYPE_DEFAULT ?
+ VRF_DEFAULT_NAME : bgp->name);
+ vrf_cfg = true;
+ }
+ }
+
+ if (vrf_cfg) {
+ vty_out(vty,
+ "%%Failed: global graceful-shutdown not permitted\n");
+ return CMD_WARNING;
+ }
+
+ /* Set flag globally */
+ SET_FLAG(bm->flags, BM_FLAG_GRACEFUL_SHUTDOWN);
+
+ /* Initiate processing for all BGP instances. */
+ for (ALL_LIST_ELEMENTS(bm->bgp, node, nnode, bgp))
+ bgp_initiate_graceful_shut_unshut(vty, bgp);
+
+ return CMD_SUCCESS;
+}
+
+static int bgp_global_graceful_shutdown_deconfig_vty(struct vty *vty)
+{
+ struct listnode *node, *nnode;
+ struct bgp *bgp;
+
+ if (!CHECK_FLAG(bm->flags, BM_FLAG_GRACEFUL_SHUTDOWN))
+ return CMD_SUCCESS;
+
+ /* Unset flag globally */
+ UNSET_FLAG(bm->flags, BM_FLAG_GRACEFUL_SHUTDOWN);
+
+ /* Initiate processing for all BGP instances. */
+ for (ALL_LIST_ELEMENTS(bm->bgp, node, nnode, bgp))
+ bgp_initiate_graceful_shut_unshut(vty, bgp);
+
+ return CMD_SUCCESS;
+}
+
+/* "bgp graceful-shutdown" configuration */
+DEFUN (bgp_graceful_shutdown,
+ bgp_graceful_shutdown_cmd,
+ "bgp graceful-shutdown",
+ BGP_STR
+ "Graceful shutdown parameters\n")
+{
+ if (vty->node == CONFIG_NODE)
+ return bgp_global_graceful_shutdown_config_vty(vty);
+
+ VTY_DECLVAR_CONTEXT(bgp, bgp);
+
+ /* if configured globally, per-instance config is not allowed */
+ if (CHECK_FLAG(bm->flags, BM_FLAG_GRACEFUL_SHUTDOWN)) {
+ vty_out(vty,
+ "%%Failed: per-vrf graceful-shutdown config not permitted with global graceful-shutdown\n");
+ return CMD_WARNING_CONFIG_FAILED;
+ }
+
+ if (!CHECK_FLAG(bgp->flags, BGP_FLAG_GRACEFUL_SHUTDOWN)) {
+ SET_FLAG(bgp->flags, BGP_FLAG_GRACEFUL_SHUTDOWN);
+ bgp_initiate_graceful_shut_unshut(vty, bgp);
+ }
+
+ return CMD_SUCCESS;
+}
+
+DEFUN (no_bgp_graceful_shutdown,
+ no_bgp_graceful_shutdown_cmd,
+ "no bgp graceful-shutdown",
+ NO_STR
+ BGP_STR
+ "Graceful shutdown parameters\n")
+{
+ if (vty->node == CONFIG_NODE)
+ return bgp_global_graceful_shutdown_deconfig_vty(vty);
+
+ VTY_DECLVAR_CONTEXT(bgp, bgp);
+
+ /* If configured globally, cannot remove from one bgp instance */
+ if (CHECK_FLAG(bm->flags, BM_FLAG_GRACEFUL_SHUTDOWN)) {
+ vty_out(vty,
+ "%%Failed: bgp graceful-shutdown configured globally. Delete per-vrf not permitted\n");
+ return CMD_WARNING_CONFIG_FAILED;
+ }
+
+ if (CHECK_FLAG(bgp->flags, BGP_FLAG_GRACEFUL_SHUTDOWN)) {
+ UNSET_FLAG(bgp->flags, BGP_FLAG_GRACEFUL_SHUTDOWN);
+ bgp_initiate_graceful_shut_unshut(vty, bgp);
+ }
+
+ return CMD_SUCCESS;
+}
+
+/* "bgp fast-external-failover" configuration. */
+DEFUN (bgp_fast_external_failover,
+ bgp_fast_external_failover_cmd,
+ "bgp fast-external-failover",
+ BGP_STR
+ "Immediately reset session if a link to a directly connected external peer goes down\n")
+{
+ VTY_DECLVAR_CONTEXT(bgp, bgp);
+ UNSET_FLAG(bgp->flags, BGP_FLAG_NO_FAST_EXT_FAILOVER);
+ return CMD_SUCCESS;
+}
+
+DEFUN (no_bgp_fast_external_failover,
+ no_bgp_fast_external_failover_cmd,
+ "no bgp fast-external-failover",
+ NO_STR
+ BGP_STR
+ "Immediately reset session if a link to a directly connected external peer goes down\n")
+{
+ VTY_DECLVAR_CONTEXT(bgp, bgp);
+ SET_FLAG(bgp->flags, BGP_FLAG_NO_FAST_EXT_FAILOVER);
+ return CMD_SUCCESS;
+}
+
+/* "bgp bestpath compare-routerid" configuration. */
+DEFUN (bgp_bestpath_compare_router_id,
+ bgp_bestpath_compare_router_id_cmd,
+ "bgp bestpath compare-routerid",
+ BGP_STR
+ "Change the default bestpath selection\n"
+ "Compare router-id for identical EBGP paths\n")
+{
+ VTY_DECLVAR_CONTEXT(bgp, bgp);
+ SET_FLAG(bgp->flags, BGP_FLAG_COMPARE_ROUTER_ID);
+ bgp_recalculate_all_bestpaths(bgp);
+
+ return CMD_SUCCESS;
+}
+
+DEFUN (no_bgp_bestpath_compare_router_id,
+ no_bgp_bestpath_compare_router_id_cmd,
+ "no bgp bestpath compare-routerid",
+ NO_STR
+ BGP_STR
+ "Change the default bestpath selection\n"
+ "Compare router-id for identical EBGP paths\n")
+{
+ VTY_DECLVAR_CONTEXT(bgp, bgp);
+ UNSET_FLAG(bgp->flags, BGP_FLAG_COMPARE_ROUTER_ID);
+ bgp_recalculate_all_bestpaths(bgp);
+
+ return CMD_SUCCESS;
+}
+
+/* "bgp bestpath as-path ignore" configuration. */
+DEFUN (bgp_bestpath_aspath_ignore,
+ bgp_bestpath_aspath_ignore_cmd,
+ "bgp bestpath as-path ignore",
+ BGP_STR
+ "Change the default bestpath selection\n"
+ "AS-path attribute\n"
+ "Ignore as-path length in selecting a route\n")
+{
+ VTY_DECLVAR_CONTEXT(bgp, bgp);
+ SET_FLAG(bgp->flags, BGP_FLAG_ASPATH_IGNORE);
+ bgp_recalculate_all_bestpaths(bgp);
+
+ return CMD_SUCCESS;
+}
+
+DEFUN (no_bgp_bestpath_aspath_ignore,
+ no_bgp_bestpath_aspath_ignore_cmd,
+ "no bgp bestpath as-path ignore",
+ NO_STR
+ BGP_STR
+ "Change the default bestpath selection\n"
+ "AS-path attribute\n"
+ "Ignore as-path length in selecting a route\n")
+{
+ VTY_DECLVAR_CONTEXT(bgp, bgp);
+ UNSET_FLAG(bgp->flags, BGP_FLAG_ASPATH_IGNORE);
+ bgp_recalculate_all_bestpaths(bgp);
+
+ return CMD_SUCCESS;
+}
+
+/* "bgp bestpath as-path confed" configuration. */
+DEFUN (bgp_bestpath_aspath_confed,
+ bgp_bestpath_aspath_confed_cmd,
+ "bgp bestpath as-path confed",
+ BGP_STR
+ "Change the default bestpath selection\n"
+ "AS-path attribute\n"
+ "Compare path lengths including confederation sets & sequences in selecting a route\n")
+{
+ VTY_DECLVAR_CONTEXT(bgp, bgp);
+ SET_FLAG(bgp->flags, BGP_FLAG_ASPATH_CONFED);
+ bgp_recalculate_all_bestpaths(bgp);
+
+ return CMD_SUCCESS;
+}
+
+DEFUN (no_bgp_bestpath_aspath_confed,
+ no_bgp_bestpath_aspath_confed_cmd,
+ "no bgp bestpath as-path confed",
+ NO_STR
+ BGP_STR
+ "Change the default bestpath selection\n"
+ "AS-path attribute\n"
+ "Compare path lengths including confederation sets & sequences in selecting a route\n")
+{
+ VTY_DECLVAR_CONTEXT(bgp, bgp);
+ UNSET_FLAG(bgp->flags, BGP_FLAG_ASPATH_CONFED);
+ bgp_recalculate_all_bestpaths(bgp);
+
+ return CMD_SUCCESS;
+}
+
+/* "bgp bestpath as-path multipath-relax" configuration. */
+DEFUN (bgp_bestpath_aspath_multipath_relax,
+ bgp_bestpath_aspath_multipath_relax_cmd,
+ "bgp bestpath as-path multipath-relax [<as-set|no-as-set>]",
+ BGP_STR
+ "Change the default bestpath selection\n"
+ "AS-path attribute\n"
+ "Allow load sharing across routes that have different AS paths (but same length)\n"
+ "Generate an AS_SET\n"
+ "Do not generate an AS_SET\n")
+{
+ VTY_DECLVAR_CONTEXT(bgp, bgp);
+ int idx = 0;
+ SET_FLAG(bgp->flags, BGP_FLAG_ASPATH_MULTIPATH_RELAX);
+
+ /* no-as-set is now the default behavior so we can silently
+ * ignore it */
+ if (argv_find(argv, argc, "as-set", &idx))
+ SET_FLAG(bgp->flags, BGP_FLAG_MULTIPATH_RELAX_AS_SET);
+ else
+ UNSET_FLAG(bgp->flags, BGP_FLAG_MULTIPATH_RELAX_AS_SET);
+
+ bgp_recalculate_all_bestpaths(bgp);
+
+ return CMD_SUCCESS;
+}
+
+DEFUN (no_bgp_bestpath_aspath_multipath_relax,
+ no_bgp_bestpath_aspath_multipath_relax_cmd,
+ "no bgp bestpath as-path multipath-relax [<as-set|no-as-set>]",
+ NO_STR
+ BGP_STR
+ "Change the default bestpath selection\n"
+ "AS-path attribute\n"
+ "Allow load sharing across routes that have different AS paths (but same length)\n"
+ "Generate an AS_SET\n"
+ "Do not generate an AS_SET\n")
+{
+ VTY_DECLVAR_CONTEXT(bgp, bgp);
+ UNSET_FLAG(bgp->flags, BGP_FLAG_ASPATH_MULTIPATH_RELAX);
+ UNSET_FLAG(bgp->flags, BGP_FLAG_MULTIPATH_RELAX_AS_SET);
+ bgp_recalculate_all_bestpaths(bgp);
+
+ return CMD_SUCCESS;
+}
+
+/* "bgp bestpath peer-type multipath-relax" configuration. */
+DEFUN(bgp_bestpath_peer_type_multipath_relax,
+ bgp_bestpath_peer_type_multipath_relax_cmd,
+ "bgp bestpath peer-type multipath-relax",
+ BGP_STR
+ "Change the default bestpath selection\n"
+ "Peer type\n"
+ "Allow load sharing across routes learned from different peer types\n")
+{
+ VTY_DECLVAR_CONTEXT(bgp, bgp);
+ SET_FLAG(bgp->flags, BGP_FLAG_PEERTYPE_MULTIPATH_RELAX);
+ bgp_recalculate_all_bestpaths(bgp);
+
+ return CMD_SUCCESS;
+}
+
+DEFUN(no_bgp_bestpath_peer_type_multipath_relax,
+ no_bgp_bestpath_peer_type_multipath_relax_cmd,
+ "no bgp bestpath peer-type multipath-relax",
+ NO_STR BGP_STR
+ "Change the default bestpath selection\n"
+ "Peer type\n"
+ "Allow load sharing across routes learned from different peer types\n")
+{
+ VTY_DECLVAR_CONTEXT(bgp, bgp);
+ UNSET_FLAG(bgp->flags, BGP_FLAG_PEERTYPE_MULTIPATH_RELAX);
+ bgp_recalculate_all_bestpaths(bgp);
+
+ return CMD_SUCCESS;
+}
+
+/* "bgp log-neighbor-changes" configuration. */
+DEFUN (bgp_log_neighbor_changes,
+ bgp_log_neighbor_changes_cmd,
+ "bgp log-neighbor-changes",
+ BGP_STR
+ "Log neighbor up/down and reset reason\n")
+{
+ VTY_DECLVAR_CONTEXT(bgp, bgp);
+ SET_FLAG(bgp->flags, BGP_FLAG_LOG_NEIGHBOR_CHANGES);
+ return CMD_SUCCESS;
+}
+
+DEFUN (no_bgp_log_neighbor_changes,
+ no_bgp_log_neighbor_changes_cmd,
+ "no bgp log-neighbor-changes",
+ NO_STR
+ BGP_STR
+ "Log neighbor up/down and reset reason\n")
+{
+ VTY_DECLVAR_CONTEXT(bgp, bgp);
+ UNSET_FLAG(bgp->flags, BGP_FLAG_LOG_NEIGHBOR_CHANGES);
+ return CMD_SUCCESS;
+}
+
+/* "bgp bestpath med" configuration. */
+DEFUN (bgp_bestpath_med,
+ bgp_bestpath_med_cmd,
+ "bgp bestpath med <confed [missing-as-worst]|missing-as-worst [confed]>",
+ BGP_STR
+ "Change the default bestpath selection\n"
+ "MED attribute\n"
+ "Compare MED among confederation paths\n"
+ "Treat missing MED as the least preferred one\n"
+ "Treat missing MED as the least preferred one\n"
+ "Compare MED among confederation paths\n")
+{
+ VTY_DECLVAR_CONTEXT(bgp, bgp);
+
+ int idx = 0;
+ if (argv_find(argv, argc, "confed", &idx))
+ SET_FLAG(bgp->flags, BGP_FLAG_MED_CONFED);
+ idx = 0;
+ if (argv_find(argv, argc, "missing-as-worst", &idx))
+ SET_FLAG(bgp->flags, BGP_FLAG_MED_MISSING_AS_WORST);
+
+ bgp_recalculate_all_bestpaths(bgp);
+
+ return CMD_SUCCESS;
+}
+
+DEFUN (no_bgp_bestpath_med,
+ no_bgp_bestpath_med_cmd,
+ "no bgp bestpath med <confed [missing-as-worst]|missing-as-worst [confed]>",
+ NO_STR
+ BGP_STR
+ "Change the default bestpath selection\n"
+ "MED attribute\n"
+ "Compare MED among confederation paths\n"
+ "Treat missing MED as the least preferred one\n"
+ "Treat missing MED as the least preferred one\n"
+ "Compare MED among confederation paths\n")
+{
+ VTY_DECLVAR_CONTEXT(bgp, bgp);
+
+ int idx = 0;
+ if (argv_find(argv, argc, "confed", &idx))
+ UNSET_FLAG(bgp->flags, BGP_FLAG_MED_CONFED);
+ idx = 0;
+ if (argv_find(argv, argc, "missing-as-worst", &idx))
+ UNSET_FLAG(bgp->flags, BGP_FLAG_MED_MISSING_AS_WORST);
+
+ bgp_recalculate_all_bestpaths(bgp);
+
+ return CMD_SUCCESS;
+}
+
+/* "bgp bestpath bandwidth" configuration. */
+DEFPY (bgp_bestpath_bw,
+ bgp_bestpath_bw_cmd,
+ "bgp bestpath bandwidth <ignore|skip-missing|default-weight-for-missing>$bw_cfg",
+ BGP_STR
+ "Change the default bestpath selection\n"
+ "Link Bandwidth attribute\n"
+ "Ignore link bandwidth (i.e., do regular ECMP, not weighted)\n"
+ "Ignore paths without link bandwidth for ECMP (if other paths have it)\n"
+ "Assign a low default weight (value 1) to paths not having link bandwidth\n")
+{
+ VTY_DECLVAR_CONTEXT(bgp, bgp);
+ afi_t afi;
+ safi_t safi;
+
+ if (!bw_cfg) {
+ vty_out(vty, "%% Bandwidth configuration must be specified\n");
+ return CMD_ERR_INCOMPLETE;
+ }
+ if (!strcmp(bw_cfg, "ignore"))
+ bgp->lb_handling = BGP_LINK_BW_IGNORE_BW;
+ else if (!strcmp(bw_cfg, "skip-missing"))
+ bgp->lb_handling = BGP_LINK_BW_SKIP_MISSING;
+ else if (!strcmp(bw_cfg, "default-weight-for-missing"))
+ bgp->lb_handling = BGP_LINK_BW_DEFWT_4_MISSING;
+ else
+ return CMD_ERR_NO_MATCH;
+
+ /* This config is used in route install, so redo that. */
+ FOREACH_AFI_SAFI (afi, safi) {
+ if (!bgp_fibupd_safi(safi))
+ continue;
+ bgp_zebra_announce_table(bgp, afi, safi);
+ }
+
+ return CMD_SUCCESS;
+}
+
+DEFPY (no_bgp_bestpath_bw,
+ no_bgp_bestpath_bw_cmd,
+ "no bgp bestpath bandwidth [<ignore|skip-missing|default-weight-for-missing>$bw_cfg]",
+ NO_STR
+ BGP_STR
+ "Change the default bestpath selection\n"
+ "Link Bandwidth attribute\n"
+ "Ignore link bandwidth (i.e., do regular ECMP, not weighted)\n"
+ "Ignore paths without link bandwidth for ECMP (if other paths have it)\n"
+ "Assign a low default weight (value 1) to paths not having link bandwidth\n")
+{
+ VTY_DECLVAR_CONTEXT(bgp, bgp);
+ afi_t afi;
+ safi_t safi;
+
+ bgp->lb_handling = BGP_LINK_BW_ECMP;
+
+ /* This config is used in route install, so redo that. */
+ FOREACH_AFI_SAFI (afi, safi) {
+ if (!bgp_fibupd_safi(safi))
+ continue;
+ bgp_zebra_announce_table(bgp, afi, safi);
+ }
+ return CMD_SUCCESS;
+}
+
+DEFPY(bgp_default_afi_safi, bgp_default_afi_safi_cmd,
+ "[no] bgp default <ipv4-unicast|"
+ "ipv4-multicast|"
+ "ipv4-vpn|"
+ "ipv4-labeled-unicast|"
+ "ipv4-flowspec|"
+ "ipv6-unicast|"
+ "ipv6-multicast|"
+ "ipv6-vpn|"
+ "ipv6-labeled-unicast|"
+ "ipv6-flowspec|"
+ "l2vpn-evpn>$afi_safi",
+ NO_STR
+ BGP_STR
+ "Configure BGP defaults\n"
+ "Activate ipv4-unicast for a peer by default\n"
+ "Activate ipv4-multicast for a peer by default\n"
+ "Activate ipv4-vpn for a peer by default\n"
+ "Activate ipv4-labeled-unicast for a peer by default\n"
+ "Activate ipv4-flowspec for a peer by default\n"
+ "Activate ipv6-unicast for a peer by default\n"
+ "Activate ipv6-multicast for a peer by default\n"
+ "Activate ipv6-vpn for a peer by default\n"
+ "Activate ipv6-labeled-unicast for a peer by default\n"
+ "Activate ipv6-flowspec for a peer by default\n"
+ "Activate l2vpn-evpn for a peer by default\n")
+{
+ VTY_DECLVAR_CONTEXT(bgp, bgp);
+ char afi_safi_str[strlen(afi_safi) + 1];
+ char *afi_safi_str_tok;
+
+ strlcpy(afi_safi_str, afi_safi, sizeof(afi_safi_str));
+ char *afi_str = strtok_r(afi_safi_str, "-", &afi_safi_str_tok);
+ char *safi_str = strtok_r(NULL, "-", &afi_safi_str_tok);
+ afi_t afi = bgp_vty_afi_from_str(afi_str);
+ safi_t safi;
+
+ /*
+ * Impossible situation but making coverity happy
+ */
+ assert(afi != AFI_MAX);
+
+ if (strmatch(safi_str, "labeled"))
+ safi = bgp_vty_safi_from_str("labeled-unicast");
+ else
+ safi = bgp_vty_safi_from_str(safi_str);
+
+ assert(safi != SAFI_MAX);
+ if (no)
+ bgp->default_af[afi][safi] = false;
+ else {
+ if ((safi == SAFI_LABELED_UNICAST
+ && bgp->default_af[afi][SAFI_UNICAST])
+ || (safi == SAFI_UNICAST
+ && bgp->default_af[afi][SAFI_LABELED_UNICAST]))
+ bgp_vty_return(vty, BGP_ERR_PEER_SAFI_CONFLICT);
+ else
+ bgp->default_af[afi][safi] = true;
+ }
+
+ return CMD_SUCCESS;
+}
+
+/* Display hostname in certain command outputs */
+DEFUN (bgp_default_show_hostname,
+ bgp_default_show_hostname_cmd,
+ "bgp default show-hostname",
+ BGP_STR
+ "Configure BGP defaults\n"
+ "Show hostname in certain command outputs\n")
+{
+ VTY_DECLVAR_CONTEXT(bgp, bgp);
+ SET_FLAG(bgp->flags, BGP_FLAG_SHOW_HOSTNAME);
+ return CMD_SUCCESS;
+}
+
+DEFUN (no_bgp_default_show_hostname,
+ no_bgp_default_show_hostname_cmd,
+ "no bgp default show-hostname",
+ NO_STR
+ BGP_STR
+ "Configure BGP defaults\n"
+ "Show hostname in certain command outputs\n")
+{
+ VTY_DECLVAR_CONTEXT(bgp, bgp);
+ UNSET_FLAG(bgp->flags, BGP_FLAG_SHOW_HOSTNAME);
+ return CMD_SUCCESS;
+}
+
+/* Display hostname in certain command outputs */
+DEFUN (bgp_default_show_nexthop_hostname,
+ bgp_default_show_nexthop_hostname_cmd,
+ "bgp default show-nexthop-hostname",
+ BGP_STR
+ "Configure BGP defaults\n"
+ "Show hostname for nexthop in certain command outputs\n")
+{
+ VTY_DECLVAR_CONTEXT(bgp, bgp);
+ SET_FLAG(bgp->flags, BGP_FLAG_SHOW_NEXTHOP_HOSTNAME);
+ return CMD_SUCCESS;
+}
+
+DEFUN (no_bgp_default_show_nexthop_hostname,
+ no_bgp_default_show_nexthop_hostname_cmd,
+ "no bgp default show-nexthop-hostname",
+ NO_STR
+ BGP_STR
+ "Configure BGP defaults\n"
+ "Show hostname for nexthop in certain command outputs\n")
+{
+ VTY_DECLVAR_CONTEXT(bgp, bgp);
+ UNSET_FLAG(bgp->flags, BGP_FLAG_SHOW_NEXTHOP_HOSTNAME);
+ return CMD_SUCCESS;
+}
+
+/* "bgp network import-check" configuration. */
+DEFUN (bgp_network_import_check,
+ bgp_network_import_check_cmd,
+ "bgp network import-check",
+ BGP_STR
+ "BGP network command\n"
+ "Check BGP network route exists in IGP\n")
+{
+ VTY_DECLVAR_CONTEXT(bgp, bgp);
+ if (!CHECK_FLAG(bgp->flags, BGP_FLAG_IMPORT_CHECK)) {
+ SET_FLAG(bgp->flags, BGP_FLAG_IMPORT_CHECK);
+ bgp_static_redo_import_check(bgp);
+ }
+
+ return CMD_SUCCESS;
+}
+
+ALIAS_HIDDEN(bgp_network_import_check, bgp_network_import_check_exact_cmd,
+ "bgp network import-check exact",
+ BGP_STR
+ "BGP network command\n"
+ "Check BGP network route exists in IGP\n"
+ "Match route precisely\n")
+
+DEFUN (no_bgp_network_import_check,
+ no_bgp_network_import_check_cmd,
+ "no bgp network import-check",
+ NO_STR
+ BGP_STR
+ "BGP network command\n"
+ "Check BGP network route exists in IGP\n")
+{
+ VTY_DECLVAR_CONTEXT(bgp, bgp);
+ if (CHECK_FLAG(bgp->flags, BGP_FLAG_IMPORT_CHECK)) {
+ UNSET_FLAG(bgp->flags, BGP_FLAG_IMPORT_CHECK);
+ bgp_static_redo_import_check(bgp);
+ }
+
+ return CMD_SUCCESS;
+}
+
+DEFUN (bgp_default_local_preference,
+ bgp_default_local_preference_cmd,
+ "bgp default local-preference (0-4294967295)",
+ BGP_STR
+ "Configure BGP defaults\n"
+ "local preference (higher=more preferred)\n"
+ "Configure default local preference value\n")
+{
+ VTY_DECLVAR_CONTEXT(bgp, bgp);
+ int idx_number = 3;
+ uint32_t local_pref;
+
+ local_pref = strtoul(argv[idx_number]->arg, NULL, 10);
+
+ bgp_default_local_preference_set(bgp, local_pref);
+ bgp_clear_star_soft_in(vty, bgp->name);
+
+ return CMD_SUCCESS;
+}
+
+DEFUN (no_bgp_default_local_preference,
+ no_bgp_default_local_preference_cmd,
+ "no bgp default local-preference [(0-4294967295)]",
+ NO_STR
+ BGP_STR
+ "Configure BGP defaults\n"
+ "local preference (higher=more preferred)\n"
+ "Configure default local preference value\n")
+{
+ VTY_DECLVAR_CONTEXT(bgp, bgp);
+ bgp_default_local_preference_unset(bgp);
+ bgp_clear_star_soft_in(vty, bgp->name);
+
+ return CMD_SUCCESS;
+}
+
+
+DEFUN (bgp_default_subgroup_pkt_queue_max,
+ bgp_default_subgroup_pkt_queue_max_cmd,
+ "bgp default subgroup-pkt-queue-max (20-100)",
+ BGP_STR
+ "Configure BGP defaults\n"
+ "subgroup-pkt-queue-max\n"
+ "Configure subgroup packet queue max\n")
+{
+ VTY_DECLVAR_CONTEXT(bgp, bgp);
+ int idx_number = 3;
+ uint32_t max_size;
+
+ max_size = strtoul(argv[idx_number]->arg, NULL, 10);
+
+ bgp_default_subgroup_pkt_queue_max_set(bgp, max_size);
+
+ return CMD_SUCCESS;
+}
+
+DEFUN (no_bgp_default_subgroup_pkt_queue_max,
+ no_bgp_default_subgroup_pkt_queue_max_cmd,
+ "no bgp default subgroup-pkt-queue-max [(20-100)]",
+ NO_STR
+ BGP_STR
+ "Configure BGP defaults\n"
+ "subgroup-pkt-queue-max\n"
+ "Configure subgroup packet queue max\n")
+{
+ VTY_DECLVAR_CONTEXT(bgp, bgp);
+ bgp_default_subgroup_pkt_queue_max_unset(bgp);
+ return CMD_SUCCESS;
+}
+
+
+DEFUN (bgp_rr_allow_outbound_policy,
+ bgp_rr_allow_outbound_policy_cmd,
+ "bgp route-reflector allow-outbound-policy",
+ BGP_STR
+ "Allow modifications made by out route-map\n"
+ "on ibgp neighbors\n")
+{
+ VTY_DECLVAR_CONTEXT(bgp, bgp);
+
+ if (!CHECK_FLAG(bgp->flags, BGP_FLAG_RR_ALLOW_OUTBOUND_POLICY)) {
+ SET_FLAG(bgp->flags, BGP_FLAG_RR_ALLOW_OUTBOUND_POLICY);
+ update_group_announce_rrclients(bgp);
+ bgp_clear_star_soft_out(vty, bgp->name);
+ }
+
+ return CMD_SUCCESS;
+}
+
+DEFUN (no_bgp_rr_allow_outbound_policy,
+ no_bgp_rr_allow_outbound_policy_cmd,
+ "no bgp route-reflector allow-outbound-policy",
+ NO_STR
+ BGP_STR
+ "Allow modifications made by out route-map\n"
+ "on ibgp neighbors\n")
+{
+ VTY_DECLVAR_CONTEXT(bgp, bgp);
+
+ if (CHECK_FLAG(bgp->flags, BGP_FLAG_RR_ALLOW_OUTBOUND_POLICY)) {
+ UNSET_FLAG(bgp->flags, BGP_FLAG_RR_ALLOW_OUTBOUND_POLICY);
+ update_group_announce_rrclients(bgp);
+ bgp_clear_star_soft_out(vty, bgp->name);
+ }
+
+ return CMD_SUCCESS;
+}
+
+DEFUN (bgp_listen_limit,
+ bgp_listen_limit_cmd,
+ "bgp listen limit (1-65535)",
+ BGP_STR
+ "BGP Dynamic Neighbors listen commands\n"
+ "Maximum number of BGP Dynamic Neighbors that can be created\n"
+ "Configure Dynamic Neighbors listen limit value\n")
+{
+ VTY_DECLVAR_CONTEXT(bgp, bgp);
+ int idx_number = 3;
+ int listen_limit;
+
+ listen_limit = strtoul(argv[idx_number]->arg, NULL, 10);
+
+ bgp_listen_limit_set(bgp, listen_limit);
+
+ return CMD_SUCCESS;
+}
+
+DEFUN (no_bgp_listen_limit,
+ no_bgp_listen_limit_cmd,
+ "no bgp listen limit [(1-65535)]",
+ NO_STR
+ BGP_STR
+ "BGP Dynamic Neighbors listen commands\n"
+ "Maximum number of BGP Dynamic Neighbors that can be created\n"
+ "Configure Dynamic Neighbors listen limit value\n")
+{
+ VTY_DECLVAR_CONTEXT(bgp, bgp);
+ bgp_listen_limit_unset(bgp);
+ return CMD_SUCCESS;
+}
+
+
+/*
+ * Check if this listen range is already configured. Check for exact
+ * match or overlap based on input.
+ */
+static struct peer_group *listen_range_exists(struct bgp *bgp,
+ struct prefix *range, int exact)
+{
+ struct listnode *node, *nnode;
+ struct listnode *node1, *nnode1;
+ struct peer_group *group;
+ struct prefix *lr;
+ afi_t afi;
+ int match;
+
+ afi = family2afi(range->family);
+ for (ALL_LIST_ELEMENTS(bgp->group, node, nnode, group)) {
+ for (ALL_LIST_ELEMENTS(group->listen_range[afi], node1, nnode1,
+ lr)) {
+ if (exact)
+ match = prefix_same(range, lr);
+ else
+ match = (prefix_match(range, lr)
+ || prefix_match(lr, range));
+ if (match)
+ return group;
+ }
+ }
+
+ return NULL;
+}
+
+DEFUN (bgp_listen_range,
+ bgp_listen_range_cmd,
+ "bgp listen range <A.B.C.D/M|X:X::X:X/M> peer-group PGNAME",
+ BGP_STR
+ "Configure BGP dynamic neighbors listen range\n"
+ "Configure BGP dynamic neighbors listen range\n"
+ NEIGHBOR_ADDR_STR
+ "Member of the peer-group\n"
+ "Peer-group name\n")
+{
+ VTY_DECLVAR_CONTEXT(bgp, bgp);
+ struct prefix range;
+ struct peer_group *group, *existing_group;
+ afi_t afi;
+ int ret;
+ int idx = 0;
+
+ argv_find(argv, argc, "A.B.C.D/M", &idx);
+ argv_find(argv, argc, "X:X::X:X/M", &idx);
+ char *prefix = argv[idx]->arg;
+ argv_find(argv, argc, "PGNAME", &idx);
+ char *peergroup = argv[idx]->arg;
+
+ /* Convert IP prefix string to struct prefix. */
+ ret = str2prefix(prefix, &range);
+ if (!ret) {
+ vty_out(vty, "%% Malformed listen range\n");
+ return CMD_WARNING_CONFIG_FAILED;
+ }
+
+ afi = family2afi(range.family);
+
+ if (afi == AFI_IP6 && IN6_IS_ADDR_LINKLOCAL(&range.u.prefix6)) {
+ vty_out(vty,
+ "%% Malformed listen range (link-local address)\n");
+ return CMD_WARNING_CONFIG_FAILED;
+ }
+
+ apply_mask(&range);
+
+ /* Check if same listen range is already configured. */
+ existing_group = listen_range_exists(bgp, &range, 1);
+ if (existing_group) {
+ if (strcmp(existing_group->name, peergroup) == 0)
+ return CMD_SUCCESS;
+ else {
+ vty_out(vty,
+ "%% Same listen range is attached to peer-group %s\n",
+ existing_group->name);
+ return CMD_WARNING_CONFIG_FAILED;
+ }
+ }
+
+ /* Check if an overlapping listen range exists. */
+ if (listen_range_exists(bgp, &range, 0)) {
+ vty_out(vty,
+ "%% Listen range overlaps with existing listen range\n");
+ return CMD_WARNING_CONFIG_FAILED;
+ }
+
+ group = peer_group_lookup(bgp, peergroup);
+ if (!group) {
+ vty_out(vty, "%% Configure the peer-group first\n");
+ return CMD_WARNING_CONFIG_FAILED;
+ }
+
+ ret = peer_group_listen_range_add(group, &range);
+ return bgp_vty_return(vty, ret);
+}
+
+DEFUN (no_bgp_listen_range,
+ no_bgp_listen_range_cmd,
+ "no bgp listen range <A.B.C.D/M|X:X::X:X/M> peer-group PGNAME",
+ NO_STR
+ BGP_STR
+ "Unconfigure BGP dynamic neighbors listen range\n"
+ "Unconfigure BGP dynamic neighbors listen range\n"
+ NEIGHBOR_ADDR_STR
+ "Member of the peer-group\n"
+ "Peer-group name\n")
+{
+ VTY_DECLVAR_CONTEXT(bgp, bgp);
+ struct prefix range;
+ struct peer_group *group;
+ afi_t afi;
+ int ret;
+ int idx = 0;
+
+ argv_find(argv, argc, "A.B.C.D/M", &idx);
+ argv_find(argv, argc, "X:X::X:X/M", &idx);
+ char *prefix = argv[idx]->arg;
+ argv_find(argv, argc, "PGNAME", &idx);
+ char *peergroup = argv[idx]->arg;
+
+ /* Convert IP prefix string to struct prefix. */
+ ret = str2prefix(prefix, &range);
+ if (!ret) {
+ vty_out(vty, "%% Malformed listen range\n");
+ return CMD_WARNING_CONFIG_FAILED;
+ }
+
+ afi = family2afi(range.family);
+
+ if (afi == AFI_IP6 && IN6_IS_ADDR_LINKLOCAL(&range.u.prefix6)) {
+ vty_out(vty,
+ "%% Malformed listen range (link-local address)\n");
+ return CMD_WARNING_CONFIG_FAILED;
+ }
+
+ apply_mask(&range);
+
+ group = peer_group_lookup(bgp, peergroup);
+ if (!group) {
+ vty_out(vty, "%% Peer-group does not exist\n");
+ return CMD_WARNING_CONFIG_FAILED;
+ }
+
+ ret = peer_group_listen_range_del(group, &range);
+ return bgp_vty_return(vty, ret);
+}
+
+void bgp_config_write_listen(struct vty *vty, struct bgp *bgp)
+{
+ struct peer_group *group;
+ struct listnode *node, *nnode, *rnode, *nrnode;
+ struct prefix *range;
+ afi_t afi;
+
+ if (bgp->dynamic_neighbors_limit != BGP_DYNAMIC_NEIGHBORS_LIMIT_DEFAULT)
+ vty_out(vty, " bgp listen limit %d\n",
+ bgp->dynamic_neighbors_limit);
+
+ for (ALL_LIST_ELEMENTS(bgp->group, node, nnode, group)) {
+ for (afi = AFI_IP; afi < AFI_MAX; afi++) {
+ for (ALL_LIST_ELEMENTS(group->listen_range[afi], rnode,
+ nrnode, range)) {
+ vty_out(vty,
+ " bgp listen range %pFX peer-group %s\n",
+ range, group->name);
+ }
+ }
+ }
+}
+
+
+DEFUN (bgp_disable_connected_route_check,
+ bgp_disable_connected_route_check_cmd,
+ "bgp disable-ebgp-connected-route-check",
+ BGP_STR
+ "Disable checking if nexthop is connected on ebgp sessions\n")
+{
+ VTY_DECLVAR_CONTEXT(bgp, bgp);
+ SET_FLAG(bgp->flags, BGP_FLAG_DISABLE_NH_CONNECTED_CHK);
+ bgp_clear_star_soft_in(vty, bgp->name);
+
+ return CMD_SUCCESS;
+}
+
+DEFUN (no_bgp_disable_connected_route_check,
+ no_bgp_disable_connected_route_check_cmd,
+ "no bgp disable-ebgp-connected-route-check",
+ NO_STR
+ BGP_STR
+ "Disable checking if nexthop is connected on ebgp sessions\n")
+{
+ VTY_DECLVAR_CONTEXT(bgp, bgp);
+ UNSET_FLAG(bgp->flags, BGP_FLAG_DISABLE_NH_CONNECTED_CHK);
+ bgp_clear_star_soft_in(vty, bgp->name);
+
+ return CMD_SUCCESS;
+}
+
+
+static int peer_remote_as_vty(struct vty *vty, const char *peer_str,
+ const char *as_str)
+{
+ VTY_DECLVAR_CONTEXT(bgp, bgp);
+ int ret;
+ as_t as;
+ int as_type = AS_SPECIFIED;
+ union sockunion su;
+
+ if (as_str[0] == 'i') {
+ as = 0;
+ as_type = AS_INTERNAL;
+ } else if (as_str[0] == 'e') {
+ as = 0;
+ as_type = AS_EXTERNAL;
+ } else {
+ /* Get AS number. */
+ as = strtoul(as_str, NULL, 10);
+ }
+
+ /* If peer is peer group or interface peer, call proper function. */
+ ret = str2sockunion(peer_str, &su);
+ if (ret < 0) {
+ struct peer *peer;
+
+ /* Check if existing interface peer */
+ peer = peer_lookup_by_conf_if(bgp, peer_str);
+
+ ret = peer_remote_as(bgp, NULL, peer_str, &as, as_type);
+
+ /* if not interface peer, check peer-group settings */
+ if (ret < 0 && !peer) {
+ ret = peer_group_remote_as(bgp, peer_str, &as, as_type);
+ if (ret < 0) {
+ vty_out(vty,
+ "%% Create the peer-group or interface first\n");
+ return CMD_WARNING_CONFIG_FAILED;
+ }
+ return CMD_SUCCESS;
+ }
+ } else {
+ if (peer_address_self_check(bgp, &su)) {
+ vty_out(vty,
+ "%% Can not configure the local system as neighbor\n");
+ return CMD_WARNING_CONFIG_FAILED;
+ }
+ ret = peer_remote_as(bgp, &su, NULL, &as, as_type);
+ }
+
+ return bgp_vty_return(vty, ret);
+}
+
+DEFUN (bgp_default_shutdown,
+ bgp_default_shutdown_cmd,
+ "[no] bgp default shutdown",
+ NO_STR
+ BGP_STR
+ "Configure BGP defaults\n"
+ "Apply administrative shutdown to newly configured peers\n")
+{
+ VTY_DECLVAR_CONTEXT(bgp, bgp);
+ bgp->autoshutdown = !strmatch(argv[0]->text, "no");
+ return CMD_SUCCESS;
+}
+
+DEFPY(bgp_shutdown_msg, bgp_shutdown_msg_cmd, "bgp shutdown message MSG...",
+ BGP_STR
+ "Administrative shutdown of the BGP instance\n"
+ "Add a shutdown message (RFC 8203)\n"
+ "Shutdown message\n")
+{
+ char *msgstr = NULL;
+
+ VTY_DECLVAR_CONTEXT(bgp, bgp);
+
+ if (argc > 3)
+ msgstr = argv_concat(argv, argc, 3);
+
+ if (msgstr && strlen(msgstr) > BGP_ADMIN_SHUTDOWN_MSG_LEN) {
+ vty_out(vty, "%% Shutdown message size exceeded %d\n",
+ BGP_ADMIN_SHUTDOWN_MSG_LEN);
+ return CMD_WARNING_CONFIG_FAILED;
+ }
+
+ bgp_shutdown_enable(bgp, msgstr);
+ XFREE(MTYPE_TMP, msgstr);
+
+ return CMD_SUCCESS;
+}
+
+DEFPY(bgp_shutdown, bgp_shutdown_cmd, "bgp shutdown",
+ BGP_STR "Administrative shutdown of the BGP instance\n")
+{
+ VTY_DECLVAR_CONTEXT(bgp, bgp);
+
+ bgp_shutdown_enable(bgp, NULL);
+
+ return CMD_SUCCESS;
+}
+
+DEFPY(no_bgp_shutdown, no_bgp_shutdown_cmd, "no bgp shutdown",
+ NO_STR BGP_STR "Administrative shutdown of the BGP instance\n")
+{
+ VTY_DECLVAR_CONTEXT(bgp, bgp);
+
+ bgp_shutdown_disable(bgp);
+
+ return CMD_SUCCESS;
+}
+
+ALIAS(no_bgp_shutdown, no_bgp_shutdown_msg_cmd,
+ "no bgp shutdown message MSG...", NO_STR BGP_STR
+ "Administrative shutdown of the BGP instance\n"
+ "Add a shutdown message (RFC 8203)\n" "Shutdown message\n")
+
+DEFUN (neighbor_remote_as,
+ neighbor_remote_as_cmd,
+ "neighbor <A.B.C.D|X:X::X:X|WORD> remote-as <(1-4294967295)|internal|external>",
+ NEIGHBOR_STR
+ NEIGHBOR_ADDR_STR2
+ "Specify a BGP neighbor\n"
+ AS_STR
+ "Internal BGP peer\n"
+ "External BGP peer\n")
+{
+ int idx_peer = 1;
+ int idx_remote_as = 3;
+ return peer_remote_as_vty(vty, argv[idx_peer]->arg,
+ argv[idx_remote_as]->arg);
+}
+
+DEFPY (bgp_allow_martian,
+ bgp_allow_martian_cmd,
+ "[no]$no bgp allow-martian-nexthop",
+ NO_STR
+ BGP_STR
+ "Allow Martian nexthops to be received in the NLRI from a peer\n")
+{
+ VTY_DECLVAR_CONTEXT(bgp, bgp);
+
+ if (no)
+ bgp->allow_martian = false;
+ else
+ bgp->allow_martian = true;
+
+ return CMD_SUCCESS;
+}
+
+/* Enable fast convergence of bgp sessions. If this is enabled, bgp
+ * sessions do not wait for hold timer expiry to bring down the sessions
+ * when nexthop becomes unreachable
+ */
+DEFUN(bgp_fast_convergence, bgp_fast_convergence_cmd, "bgp fast-convergence",
+ BGP_STR "Fast convergence for bgp sessions\n")
+{
+ VTY_DECLVAR_CONTEXT(bgp, bgp);
+ bgp->fast_convergence = true;
+
+ return CMD_SUCCESS;
+}
+
+DEFUN(no_bgp_fast_convergence, no_bgp_fast_convergence_cmd,
+ "no bgp fast-convergence",
+ NO_STR BGP_STR "Fast convergence for bgp sessions\n")
+{
+ VTY_DECLVAR_CONTEXT(bgp, bgp);
+ bgp->fast_convergence = false;
+
+ return CMD_SUCCESS;
+}
+
+static int peer_conf_interface_get(struct vty *vty, const char *conf_if,
+ int v6only,
+ const char *peer_group_name,
+ const char *as_str)
+{
+ VTY_DECLVAR_CONTEXT(bgp, bgp);
+ as_t as = 0;
+ int as_type = AS_UNSPECIFIED;
+ struct peer *peer;
+ struct peer_group *group;
+ int ret = 0;
+
+ group = peer_group_lookup(bgp, conf_if);
+
+ if (group) {
+ vty_out(vty, "%% Name conflict with peer-group \n");
+ return CMD_WARNING_CONFIG_FAILED;
+ }
+
+ if (as_str) {
+ if (as_str[0] == 'i') {
+ as_type = AS_INTERNAL;
+ } else if (as_str[0] == 'e') {
+ as_type = AS_EXTERNAL;
+ } else {
+ /* Get AS number. */
+ as = strtoul(as_str, NULL, 10);
+ as_type = AS_SPECIFIED;
+ }
+ }
+
+ peer = peer_lookup_by_conf_if(bgp, conf_if);
+ if (peer) {
+ if (as_str)
+ ret = peer_remote_as(bgp, NULL, conf_if, &as, as_type);
+ } else {
+ peer = peer_create(NULL, conf_if, bgp, bgp->as, as, as_type,
+ NULL);
+
+ if (!peer) {
+ vty_out(vty, "%% BGP failed to create peer\n");
+ return CMD_WARNING_CONFIG_FAILED;
+ }
+
+ if (v6only)
+ peer_flag_set(peer, PEER_FLAG_IFPEER_V6ONLY);
+
+ /* Request zebra to initiate IPv6 RAs on this interface. We do
+ * this
+ * any unnumbered peer in order to not worry about run-time
+ * transitions
+ * (e.g., peering is initially IPv4, but the IPv4 /30 or /31
+ * address
+ * gets deleted later etc.)
+ */
+ if (peer->ifp)
+ bgp_zebra_initiate_radv(bgp, peer);
+ }
+
+ if ((v6only && !CHECK_FLAG(peer->flags, PEER_FLAG_IFPEER_V6ONLY))
+ || (!v6only && CHECK_FLAG(peer->flags, PEER_FLAG_IFPEER_V6ONLY))) {
+ if (v6only)
+ peer_flag_set(peer, PEER_FLAG_IFPEER_V6ONLY);
+ else
+ peer_flag_unset(peer, PEER_FLAG_IFPEER_V6ONLY);
+
+ /* v6only flag changed. Reset bgp seesion */
+ if (BGP_IS_VALID_STATE_FOR_NOTIF(peer->status)) {
+ peer->last_reset = PEER_DOWN_V6ONLY_CHANGE;
+ bgp_notify_send(peer, BGP_NOTIFY_CEASE,
+ BGP_NOTIFY_CEASE_CONFIG_CHANGE);
+ } else
+ bgp_session_reset(peer);
+ }
+
+ if (!CHECK_FLAG(peer->flags_invert, PEER_FLAG_CAPABILITY_ENHE)) {
+ SET_FLAG(peer->flags, PEER_FLAG_CAPABILITY_ENHE);
+ SET_FLAG(peer->flags_invert, PEER_FLAG_CAPABILITY_ENHE);
+ SET_FLAG(peer->flags_override, PEER_FLAG_CAPABILITY_ENHE);
+ }
+
+ if (peer_group_name) {
+ group = peer_group_lookup(bgp, peer_group_name);
+ if (!group) {
+ vty_out(vty, "%% Configure the peer-group first\n");
+ return CMD_WARNING_CONFIG_FAILED;
+ }
+
+ ret = peer_group_bind(bgp, NULL, peer, group, &as);
+ }
+
+ return bgp_vty_return(vty, ret);
+}
+
+DEFUN (neighbor_interface_config,
+ neighbor_interface_config_cmd,
+ "neighbor WORD interface [peer-group PGNAME]",
+ NEIGHBOR_STR
+ "Interface name or neighbor tag\n"
+ "Enable BGP on interface\n"
+ "Member of the peer-group\n"
+ "Peer-group name\n")
+{
+ int idx_word = 1;
+ int idx_peer_group_word = 4;
+
+ if (argc > idx_peer_group_word)
+ return peer_conf_interface_get(
+ vty, argv[idx_word]->arg, 0,
+ argv[idx_peer_group_word]->arg, NULL);
+ else
+ return peer_conf_interface_get(vty, argv[idx_word]->arg, 0,
+ NULL, NULL);
+}
+
+DEFUN (neighbor_interface_config_v6only,
+ neighbor_interface_config_v6only_cmd,
+ "neighbor WORD interface v6only [peer-group PGNAME]",
+ NEIGHBOR_STR
+ "Interface name or neighbor tag\n"
+ "Enable BGP on interface\n"
+ "Enable BGP with v6 link-local only\n"
+ "Member of the peer-group\n"
+ "Peer-group name\n")
+{
+ int idx_word = 1;
+ int idx_peer_group_word = 5;
+
+ if (argc > idx_peer_group_word)
+ return peer_conf_interface_get(
+ vty, argv[idx_word]->arg, 1,
+ argv[idx_peer_group_word]->arg, NULL);
+
+ return peer_conf_interface_get(vty, argv[idx_word]->arg, 1, NULL, NULL);
+}
+
+
+DEFUN (neighbor_interface_config_remote_as,
+ neighbor_interface_config_remote_as_cmd,
+ "neighbor WORD interface remote-as <(1-4294967295)|internal|external>",
+ NEIGHBOR_STR
+ "Interface name or neighbor tag\n"
+ "Enable BGP on interface\n"
+ "Specify a BGP neighbor\n"
+ AS_STR
+ "Internal BGP peer\n"
+ "External BGP peer\n")
+{
+ int idx_word = 1;
+ int idx_remote_as = 4;
+ return peer_conf_interface_get(vty, argv[idx_word]->arg, 0, NULL,
+ argv[idx_remote_as]->arg);
+}
+
+DEFUN (neighbor_interface_v6only_config_remote_as,
+ neighbor_interface_v6only_config_remote_as_cmd,
+ "neighbor WORD interface v6only remote-as <(1-4294967295)|internal|external>",
+ NEIGHBOR_STR
+ "Interface name or neighbor tag\n"
+ "Enable BGP with v6 link-local only\n"
+ "Enable BGP on interface\n"
+ "Specify a BGP neighbor\n"
+ AS_STR
+ "Internal BGP peer\n"
+ "External BGP peer\n")
+{
+ int idx_word = 1;
+ int idx_remote_as = 5;
+ return peer_conf_interface_get(vty, argv[idx_word]->arg, 1, NULL,
+ argv[idx_remote_as]->arg);
+}
+
+DEFUN (neighbor_peer_group,
+ neighbor_peer_group_cmd,
+ "neighbor WORD peer-group",
+ NEIGHBOR_STR
+ "Interface name or neighbor tag\n"
+ "Configure peer-group\n")
+{
+ VTY_DECLVAR_CONTEXT(bgp, bgp);
+ int idx_word = 1;
+ struct peer *peer;
+ struct peer_group *group;
+
+ peer = peer_lookup_by_conf_if(bgp, argv[idx_word]->arg);
+ if (peer) {
+ vty_out(vty, "%% Name conflict with interface: \n");
+ return CMD_WARNING_CONFIG_FAILED;
+ }
+
+ group = peer_group_get(bgp, argv[idx_word]->arg);
+ if (!group) {
+ vty_out(vty, "%% BGP failed to find or create peer-group\n");
+ return CMD_WARNING_CONFIG_FAILED;
+ }
+
+ return CMD_SUCCESS;
+}
+
+DEFUN (no_neighbor,
+ no_neighbor_cmd,
+ "no neighbor <WORD|<A.B.C.D|X:X::X:X> [remote-as <(1-4294967295)|internal|external>]>",
+ NO_STR
+ NEIGHBOR_STR
+ NEIGHBOR_ADDR_STR2
+ "Specify a BGP neighbor\n"
+ AS_STR
+ "Internal BGP peer\n"
+ "External BGP peer\n")
+{
+ VTY_DECLVAR_CONTEXT(bgp, bgp);
+ int idx_peer = 2;
+ int ret;
+ union sockunion su;
+ struct peer_group *group;
+ struct peer *peer;
+ struct peer *other;
+
+ ret = str2sockunion(argv[idx_peer]->arg, &su);
+ if (ret < 0) {
+ /* look up for neighbor by interface name config. */
+ peer = peer_lookup_by_conf_if(bgp, argv[idx_peer]->arg);
+ if (peer) {
+ /* Request zebra to terminate IPv6 RAs on this
+ * interface. */
+ if (peer->ifp)
+ bgp_zebra_terminate_radv(peer->bgp, peer);
+ peer_notify_unconfig(peer);
+ peer_delete(peer);
+ return CMD_SUCCESS;
+ }
+
+ group = peer_group_lookup(bgp, argv[idx_peer]->arg);
+ if (group) {
+ peer_group_notify_unconfig(group);
+ peer_group_delete(group);
+ } else {
+ vty_out(vty, "%% Create the peer-group first\n");
+ return CMD_WARNING_CONFIG_FAILED;
+ }
+ } else {
+ peer = peer_lookup(bgp, &su);
+ if (peer) {
+ if (peer_dynamic_neighbor(peer)) {
+ vty_out(vty,
+ "%% Operation not allowed on a dynamic neighbor\n");
+ return CMD_WARNING_CONFIG_FAILED;
+ }
+
+ other = peer->doppelganger;
+
+ if (CHECK_FLAG(peer->flags, PEER_FLAG_CAPABILITY_ENHE))
+ bgp_zebra_terminate_radv(peer->bgp, peer);
+
+ peer_notify_unconfig(peer);
+ peer_delete(peer);
+ if (other && other->status != Deleted) {
+ peer_notify_unconfig(other);
+ peer_delete(other);
+ }
+ }
+ }
+
+ return CMD_SUCCESS;
+}
+
+DEFUN (no_neighbor_interface_config,
+ no_neighbor_interface_config_cmd,
+ "no neighbor WORD interface [v6only] [peer-group PGNAME] [remote-as <(1-4294967295)|internal|external>]",
+ NO_STR
+ NEIGHBOR_STR
+ "Interface name\n"
+ "Configure BGP on interface\n"
+ "Enable BGP with v6 link-local only\n"
+ "Member of the peer-group\n"
+ "Peer-group name\n"
+ "Specify a BGP neighbor\n"
+ AS_STR
+ "Internal BGP peer\n"
+ "External BGP peer\n")
+{
+ VTY_DECLVAR_CONTEXT(bgp, bgp);
+ int idx_word = 2;
+ struct peer *peer;
+
+ /* look up for neighbor by interface name config. */
+ peer = peer_lookup_by_conf_if(bgp, argv[idx_word]->arg);
+ if (peer) {
+ /* Request zebra to terminate IPv6 RAs on this interface. */
+ if (peer->ifp)
+ bgp_zebra_terminate_radv(peer->bgp, peer);
+ peer_notify_unconfig(peer);
+ peer_delete(peer);
+ } else {
+ vty_out(vty, "%% Create the bgp interface first\n");
+ return CMD_WARNING_CONFIG_FAILED;
+ }
+ return CMD_SUCCESS;
+}
+
+DEFUN (no_neighbor_peer_group,
+ no_neighbor_peer_group_cmd,
+ "no neighbor WORD peer-group",
+ NO_STR
+ NEIGHBOR_STR
+ "Neighbor tag\n"
+ "Configure peer-group\n")
+{
+ VTY_DECLVAR_CONTEXT(bgp, bgp);
+ int idx_word = 2;
+ struct peer_group *group;
+
+ group = peer_group_lookup(bgp, argv[idx_word]->arg);
+ if (group) {
+ peer_group_notify_unconfig(group);
+ peer_group_delete(group);
+ } else {
+ vty_out(vty, "%% Create the peer-group first\n");
+ return CMD_WARNING_CONFIG_FAILED;
+ }
+ return CMD_SUCCESS;
+}
+
+DEFUN (no_neighbor_interface_peer_group_remote_as,
+ no_neighbor_interface_peer_group_remote_as_cmd,
+ "no neighbor WORD remote-as <(1-4294967295)|internal|external>",
+ NO_STR
+ NEIGHBOR_STR
+ "Interface name or neighbor tag\n"
+ "Specify a BGP neighbor\n"
+ AS_STR
+ "Internal BGP peer\n"
+ "External BGP peer\n")
+{
+ VTY_DECLVAR_CONTEXT(bgp, bgp);
+ int idx_word = 2;
+ struct peer_group *group;
+ struct peer *peer;
+
+ /* look up for neighbor by interface name config. */
+ peer = peer_lookup_by_conf_if(bgp, argv[idx_word]->arg);
+ if (peer) {
+ peer_as_change(peer, 0, AS_UNSPECIFIED);
+ return CMD_SUCCESS;
+ }
+
+ group = peer_group_lookup(bgp, argv[idx_word]->arg);
+ if (group)
+ peer_group_remote_as_delete(group);
+ else {
+ vty_out(vty, "%% Create the peer-group or interface first\n");
+ return CMD_WARNING_CONFIG_FAILED;
+ }
+ return CMD_SUCCESS;
+}
+
+DEFUN (neighbor_local_as,
+ neighbor_local_as_cmd,
+ "neighbor <A.B.C.D|X:X::X:X|WORD> local-as (1-4294967295)",
+ NEIGHBOR_STR
+ NEIGHBOR_ADDR_STR2
+ "Specify a local-as number\n"
+ "AS number used as local AS\n")
+{
+ int idx_peer = 1;
+ int idx_number = 3;
+ struct peer *peer;
+ int ret;
+ as_t as;
+
+ peer = peer_and_group_lookup_vty(vty, argv[idx_peer]->arg);
+ if (!peer)
+ return CMD_WARNING_CONFIG_FAILED;
+
+ as = strtoul(argv[idx_number]->arg, NULL, 10);
+ ret = peer_local_as_set(peer, as, 0, 0);
+ return bgp_vty_return(vty, ret);
+}
+
+DEFUN (neighbor_local_as_no_prepend,
+ neighbor_local_as_no_prepend_cmd,
+ "neighbor <A.B.C.D|X:X::X:X|WORD> local-as (1-4294967295) no-prepend",
+ NEIGHBOR_STR
+ NEIGHBOR_ADDR_STR2
+ "Specify a local-as number\n"
+ "AS number used as local AS\n"
+ "Do not prepend local-as to updates from ebgp peers\n")
+{
+ int idx_peer = 1;
+ int idx_number = 3;
+ struct peer *peer;
+ int ret;
+ as_t as;
+
+ peer = peer_and_group_lookup_vty(vty, argv[idx_peer]->arg);
+ if (!peer)
+ return CMD_WARNING_CONFIG_FAILED;
+
+ as = strtoul(argv[idx_number]->arg, NULL, 10);
+ ret = peer_local_as_set(peer, as, 1, 0);
+ return bgp_vty_return(vty, ret);
+}
+
+DEFUN (neighbor_local_as_no_prepend_replace_as,
+ neighbor_local_as_no_prepend_replace_as_cmd,
+ "neighbor <A.B.C.D|X:X::X:X|WORD> local-as (1-4294967295) no-prepend replace-as",
+ NEIGHBOR_STR
+ NEIGHBOR_ADDR_STR2
+ "Specify a local-as number\n"
+ "AS number used as local AS\n"
+ "Do not prepend local-as to updates from ebgp peers\n"
+ "Do not prepend local-as to updates from ibgp peers\n")
+{
+ int idx_peer = 1;
+ int idx_number = 3;
+ struct peer *peer;
+ int ret;
+ as_t as;
+
+ peer = peer_and_group_lookup_vty(vty, argv[idx_peer]->arg);
+ if (!peer)
+ return CMD_WARNING_CONFIG_FAILED;
+
+ as = strtoul(argv[idx_number]->arg, NULL, 10);
+ ret = peer_local_as_set(peer, as, 1, 1);
+ return bgp_vty_return(vty, ret);
+}
+
+DEFUN (no_neighbor_local_as,
+ no_neighbor_local_as_cmd,
+ "no neighbor <A.B.C.D|X:X::X:X|WORD> local-as [(1-4294967295) [no-prepend [replace-as]]]",
+ NO_STR
+ NEIGHBOR_STR
+ NEIGHBOR_ADDR_STR2
+ "Specify a local-as number\n"
+ "AS number used as local AS\n"
+ "Do not prepend local-as to updates from ebgp peers\n"
+ "Do not prepend local-as to updates from ibgp peers\n")
+{
+ int idx_peer = 2;
+ struct peer *peer;
+ int ret;
+
+ peer = peer_and_group_lookup_vty(vty, argv[idx_peer]->arg);
+ if (!peer)
+ return CMD_WARNING_CONFIG_FAILED;
+
+ ret = peer_local_as_unset(peer);
+ return bgp_vty_return(vty, ret);
+}
+
+
+DEFUN (neighbor_solo,
+ neighbor_solo_cmd,
+ "neighbor <A.B.C.D|X:X::X:X|WORD> solo",
+ NEIGHBOR_STR
+ NEIGHBOR_ADDR_STR2
+ "Solo peer - part of its own update group\n")
+{
+ int idx_peer = 1;
+ struct peer *peer;
+ int ret;
+
+ peer = peer_and_group_lookup_vty(vty, argv[idx_peer]->arg);
+ if (!peer)
+ return CMD_WARNING_CONFIG_FAILED;
+
+ ret = update_group_adjust_soloness(peer, 1);
+ return bgp_vty_return(vty, ret);
+}
+
+DEFUN (no_neighbor_solo,
+ no_neighbor_solo_cmd,
+ "no neighbor <A.B.C.D|X:X::X:X|WORD> solo",
+ NO_STR
+ NEIGHBOR_STR
+ NEIGHBOR_ADDR_STR2
+ "Solo peer - part of its own update group\n")
+{
+ int idx_peer = 2;
+ struct peer *peer;
+ int ret;
+
+ peer = peer_and_group_lookup_vty(vty, argv[idx_peer]->arg);
+ if (!peer)
+ return CMD_WARNING_CONFIG_FAILED;
+
+ ret = update_group_adjust_soloness(peer, 0);
+ return bgp_vty_return(vty, ret);
+}
+
+DEFUN (neighbor_password,
+ neighbor_password_cmd,
+ "neighbor <A.B.C.D|X:X::X:X|WORD> password LINE",
+ NEIGHBOR_STR
+ NEIGHBOR_ADDR_STR2
+ "Set a password\n"
+ "The password\n")
+{
+ int idx_peer = 1;
+ int idx_line = 3;
+ struct peer *peer;
+ int ret;
+
+ peer = peer_and_group_lookup_vty(vty, argv[idx_peer]->arg);
+ if (!peer)
+ return CMD_WARNING_CONFIG_FAILED;
+
+ ret = peer_password_set(peer, argv[idx_line]->arg);
+ return bgp_vty_return(vty, ret);
+}
+
+DEFUN (no_neighbor_password,
+ no_neighbor_password_cmd,
+ "no neighbor <A.B.C.D|X:X::X:X|WORD> password [LINE]",
+ NO_STR
+ NEIGHBOR_STR
+ NEIGHBOR_ADDR_STR2
+ "Set a password\n"
+ "The password\n")
+{
+ int idx_peer = 2;
+ struct peer *peer;
+ int ret;
+
+ peer = peer_and_group_lookup_vty(vty, argv[idx_peer]->arg);
+ if (!peer)
+ return CMD_WARNING_CONFIG_FAILED;
+
+ ret = peer_password_unset(peer);
+ return bgp_vty_return(vty, ret);
+}
+
+DEFUN (neighbor_activate,
+ neighbor_activate_cmd,
+ "neighbor <A.B.C.D|X:X::X:X|WORD> activate",
+ NEIGHBOR_STR
+ NEIGHBOR_ADDR_STR2
+ "Enable the Address Family for this Neighbor\n")
+{
+ int idx_peer = 1;
+ int ret;
+ struct peer *peer;
+
+ peer = peer_and_group_lookup_vty(vty, argv[idx_peer]->arg);
+ if (!peer)
+ return CMD_WARNING_CONFIG_FAILED;
+
+ ret = peer_activate(peer, bgp_node_afi(vty), bgp_node_safi(vty));
+ return bgp_vty_return(vty, ret);
+}
+
+ALIAS_HIDDEN(neighbor_activate, neighbor_activate_hidden_cmd,
+ "neighbor <A.B.C.D|X:X::X:X|WORD> activate",
+ NEIGHBOR_STR NEIGHBOR_ADDR_STR2
+ "Enable the Address Family for this Neighbor\n")
+
+DEFUN (no_neighbor_activate,
+ no_neighbor_activate_cmd,
+ "no neighbor <A.B.C.D|X:X::X:X|WORD> activate",
+ NO_STR
+ NEIGHBOR_STR
+ NEIGHBOR_ADDR_STR2
+ "Enable the Address Family for this Neighbor\n")
+{
+ int idx_peer = 2;
+ int ret;
+ struct peer *peer;
+
+ /* Lookup peer. */
+ peer = peer_and_group_lookup_vty(vty, argv[idx_peer]->arg);
+ if (!peer)
+ return CMD_WARNING_CONFIG_FAILED;
+
+ ret = peer_deactivate(peer, bgp_node_afi(vty), bgp_node_safi(vty));
+ return bgp_vty_return(vty, ret);
+}
+
+ALIAS_HIDDEN(no_neighbor_activate, no_neighbor_activate_hidden_cmd,
+ "no neighbor <A.B.C.D|X:X::X:X|WORD> activate",
+ NO_STR NEIGHBOR_STR NEIGHBOR_ADDR_STR2
+ "Enable the Address Family for this Neighbor\n")
+
+DEFUN (neighbor_set_peer_group,
+ neighbor_set_peer_group_cmd,
+ "neighbor <A.B.C.D|X:X::X:X|WORD> peer-group PGNAME",
+ NEIGHBOR_STR
+ NEIGHBOR_ADDR_STR2
+ "Member of the peer-group\n"
+ "Peer-group name\n")
+{
+ VTY_DECLVAR_CONTEXT(bgp, bgp);
+ int idx_peer = 1;
+ int idx_word = 3;
+ int ret;
+ as_t as;
+ union sockunion su;
+ struct peer *peer;
+ struct peer_group *group;
+
+ ret = str2sockunion(argv[idx_peer]->arg, &su);
+ if (ret < 0) {
+ peer = peer_lookup_by_conf_if(bgp, argv[idx_peer]->arg);
+ if (!peer) {
+ vty_out(vty, "%% Malformed address or name: %s\n",
+ argv[idx_peer]->arg);
+ return CMD_WARNING_CONFIG_FAILED;
+ }
+ } else {
+ if (peer_address_self_check(bgp, &su)) {
+ vty_out(vty,
+ "%% Can not configure the local system as neighbor\n");
+ return CMD_WARNING_CONFIG_FAILED;
+ }
+
+ /* Disallow for dynamic neighbor. */
+ peer = peer_lookup(bgp, &su);
+ if (peer && peer_dynamic_neighbor(peer)) {
+ vty_out(vty,
+ "%% Operation not allowed on a dynamic neighbor\n");
+ return CMD_WARNING_CONFIG_FAILED;
+ }
+ }
+
+ group = peer_group_lookup(bgp, argv[idx_word]->arg);
+ if (!group) {
+ vty_out(vty, "%% Configure the peer-group first\n");
+ return CMD_WARNING_CONFIG_FAILED;
+ }
+
+ ret = peer_group_bind(bgp, &su, peer, group, &as);
+
+ return bgp_vty_return(vty, ret);
+}
+
+ALIAS_HIDDEN(neighbor_set_peer_group, neighbor_set_peer_group_hidden_cmd,
+ "neighbor <A.B.C.D|X:X::X:X|WORD> peer-group PGNAME",
+ NEIGHBOR_STR NEIGHBOR_ADDR_STR2
+ "Member of the peer-group\n"
+ "Peer-group name\n")
+
+DEFUN (no_neighbor_set_peer_group,
+ no_neighbor_set_peer_group_cmd,
+ "no neighbor <A.B.C.D|X:X::X:X|WORD> peer-group PGNAME",
+ NO_STR
+ NEIGHBOR_STR
+ NEIGHBOR_ADDR_STR2
+ "Member of the peer-group\n"
+ "Peer-group name\n")
+{
+ VTY_DECLVAR_CONTEXT(bgp, bgp);
+ int idx_peer = 2;
+ int idx_word = 4;
+ int ret;
+ struct peer *peer;
+ struct peer_group *group;
+
+ peer = peer_lookup_vty(vty, argv[idx_peer]->arg);
+ if (!peer)
+ return CMD_WARNING_CONFIG_FAILED;
+
+ group = peer_group_lookup(bgp, argv[idx_word]->arg);
+ if (!group) {
+ vty_out(vty, "%% Configure the peer-group first\n");
+ return CMD_WARNING_CONFIG_FAILED;
+ }
+
+ if (CHECK_FLAG(peer->flags, PEER_FLAG_CAPABILITY_ENHE))
+ bgp_zebra_terminate_radv(peer->bgp, peer);
+
+ peer_notify_unconfig(peer);
+ ret = peer_delete(peer);
+
+ return bgp_vty_return(vty, ret);
+}
+
+ALIAS_HIDDEN(no_neighbor_set_peer_group, no_neighbor_set_peer_group_hidden_cmd,
+ "no neighbor <A.B.C.D|X:X::X:X|WORD> peer-group PGNAME",
+ NO_STR NEIGHBOR_STR NEIGHBOR_ADDR_STR2
+ "Member of the peer-group\n"
+ "Peer-group name\n")
+
+static int peer_flag_modify_vty(struct vty *vty, const char *ip_str,
+ uint64_t flag, int set)
+{
+ int ret;
+ struct peer *peer;
+
+ peer = peer_and_group_lookup_vty(vty, ip_str);
+ if (!peer)
+ return CMD_WARNING_CONFIG_FAILED;
+
+ /*
+ * If 'neighbor <interface>', then this is for directly connected peers,
+ * we should not accept disable-connected-check.
+ */
+ if (peer->conf_if && (flag == PEER_FLAG_DISABLE_CONNECTED_CHECK)) {
+ vty_out(vty,
+ "%s is directly connected peer, cannot accept disable-connected-check\n",
+ ip_str);
+ return CMD_WARNING_CONFIG_FAILED;
+ }
+
+ if (!set && flag == PEER_FLAG_SHUTDOWN)
+ peer_tx_shutdown_message_unset(peer);
+
+ if (set)
+ ret = peer_flag_set(peer, flag);
+ else
+ ret = peer_flag_unset(peer, flag);
+
+ return bgp_vty_return(vty, ret);
+}
+
+static int peer_flag_set_vty(struct vty *vty, const char *ip_str, uint64_t flag)
+{
+ return peer_flag_modify_vty(vty, ip_str, flag, 1);
+}
+
+static int peer_flag_unset_vty(struct vty *vty, const char *ip_str,
+ uint64_t flag)
+{
+ return peer_flag_modify_vty(vty, ip_str, flag, 0);
+}
+
+/* neighbor passive. */
+DEFUN (neighbor_passive,
+ neighbor_passive_cmd,
+ "neighbor <A.B.C.D|X:X::X:X|WORD> passive",
+ NEIGHBOR_STR
+ NEIGHBOR_ADDR_STR2
+ "Don't send open messages to this neighbor\n")
+{
+ int idx_peer = 1;
+ return peer_flag_set_vty(vty, argv[idx_peer]->arg, PEER_FLAG_PASSIVE);
+}
+
+DEFUN (no_neighbor_passive,
+ no_neighbor_passive_cmd,
+ "no neighbor <A.B.C.D|X:X::X:X|WORD> passive",
+ NO_STR
+ NEIGHBOR_STR
+ NEIGHBOR_ADDR_STR2
+ "Don't send open messages to this neighbor\n")
+{
+ int idx_peer = 2;
+ return peer_flag_unset_vty(vty, argv[idx_peer]->arg, PEER_FLAG_PASSIVE);
+}
+
+/* neighbor shutdown. */
+DEFUN (neighbor_shutdown_msg,
+ neighbor_shutdown_msg_cmd,
+ "neighbor <A.B.C.D|X:X::X:X|WORD> shutdown message MSG...",
+ NEIGHBOR_STR
+ NEIGHBOR_ADDR_STR2
+ "Administratively shut down this neighbor\n"
+ "Add a shutdown message (RFC 8203)\n"
+ "Shutdown message\n")
+{
+ int idx_peer = 1;
+
+ if (argc >= 5) {
+ struct peer *peer =
+ peer_and_group_lookup_vty(vty, argv[idx_peer]->arg);
+ char *message;
+
+ if (!peer)
+ return CMD_WARNING_CONFIG_FAILED;
+ message = argv_concat(argv, argc, 4);
+ peer_tx_shutdown_message_set(peer, message);
+ XFREE(MTYPE_TMP, message);
+ }
+
+ return peer_flag_set_vty(vty, argv[idx_peer]->arg, PEER_FLAG_SHUTDOWN);
+}
+
+ALIAS(neighbor_shutdown_msg, neighbor_shutdown_cmd,
+ "neighbor <A.B.C.D|X:X::X:X|WORD> shutdown",
+ NEIGHBOR_STR NEIGHBOR_ADDR_STR2
+ "Administratively shut down this neighbor\n")
+
+DEFUN (no_neighbor_shutdown_msg,
+ no_neighbor_shutdown_msg_cmd,
+ "no neighbor <A.B.C.D|X:X::X:X|WORD> shutdown message MSG...",
+ NO_STR
+ NEIGHBOR_STR
+ NEIGHBOR_ADDR_STR2
+ "Administratively shut down this neighbor\n"
+ "Remove a shutdown message (RFC 8203)\n"
+ "Shutdown message\n")
+{
+ int idx_peer = 2;
+
+ return peer_flag_unset_vty(vty, argv[idx_peer]->arg,
+ PEER_FLAG_SHUTDOWN);
+}
+
+ALIAS(no_neighbor_shutdown_msg, no_neighbor_shutdown_cmd,
+ "no neighbor <A.B.C.D|X:X::X:X|WORD> shutdown",
+ NO_STR NEIGHBOR_STR NEIGHBOR_ADDR_STR2
+ "Administratively shut down this neighbor\n")
+
+DEFUN(neighbor_shutdown_rtt,
+ neighbor_shutdown_rtt_cmd,
+ "neighbor <A.B.C.D|X:X::X:X|WORD> shutdown rtt (1-65535) [count (1-255)]",
+ NEIGHBOR_STR
+ NEIGHBOR_ADDR_STR2
+ "Administratively shut down this neighbor\n"
+ "Shutdown if round-trip-time is higher than expected\n"
+ "Round-trip-time in milliseconds\n"
+ "Specify the number of keepalives before shutdown\n"
+ "The number of keepalives with higher RTT to shutdown\n")
+{
+ int idx_peer = 1;
+ int idx_rtt = 4;
+ int idx_count = 0;
+ struct peer *peer;
+
+ peer = peer_and_group_lookup_vty(vty, argv[idx_peer]->arg);
+
+ if (!peer)
+ return CMD_WARNING_CONFIG_FAILED;
+
+ peer->rtt_expected = strtol(argv[idx_rtt]->arg, NULL, 10);
+
+ if (argv_find(argv, argc, "count", &idx_count))
+ peer->rtt_keepalive_conf =
+ strtol(argv[idx_count + 1]->arg, NULL, 10);
+
+ return peer_flag_set_vty(vty, argv[idx_peer]->arg,
+ PEER_FLAG_RTT_SHUTDOWN);
+}
+
+DEFUN(no_neighbor_shutdown_rtt,
+ no_neighbor_shutdown_rtt_cmd,
+ "no neighbor <A.B.C.D|X:X::X:X|WORD> shutdown rtt [(1-65535) [count (1-255)]]",
+ NO_STR
+ NEIGHBOR_STR
+ NEIGHBOR_ADDR_STR2
+ "Administratively shut down this neighbor\n"
+ "Shutdown if round-trip-time is higher than expected\n"
+ "Round-trip-time in milliseconds\n"
+ "Specify the number of keepalives before shutdown\n"
+ "The number of keepalives with higher RTT to shutdown\n")
+{
+ int idx_peer = 2;
+ struct peer *peer;
+
+ peer = peer_and_group_lookup_vty(vty, argv[idx_peer]->arg);
+
+ if (!peer)
+ return CMD_WARNING_CONFIG_FAILED;
+
+ peer->rtt_expected = 0;
+ peer->rtt_keepalive_conf = 1;
+
+ return peer_flag_unset_vty(vty, argv[idx_peer]->arg,
+ PEER_FLAG_RTT_SHUTDOWN);
+}
+
+/* neighbor capability dynamic. */
+DEFUN (neighbor_capability_dynamic,
+ neighbor_capability_dynamic_cmd,
+ "neighbor <A.B.C.D|X:X::X:X|WORD> capability dynamic",
+ NEIGHBOR_STR
+ NEIGHBOR_ADDR_STR2
+ "Advertise capability to the peer\n"
+ "Advertise dynamic capability to this neighbor\n")
+{
+ int idx_peer = 1;
+ return peer_flag_set_vty(vty, argv[idx_peer]->arg,
+ PEER_FLAG_DYNAMIC_CAPABILITY);
+}
+
+DEFUN (no_neighbor_capability_dynamic,
+ no_neighbor_capability_dynamic_cmd,
+ "no neighbor <A.B.C.D|X:X::X:X|WORD> capability dynamic",
+ NO_STR
+ NEIGHBOR_STR
+ NEIGHBOR_ADDR_STR2
+ "Advertise capability to the peer\n"
+ "Advertise dynamic capability to this neighbor\n")
+{
+ int idx_peer = 2;
+ return peer_flag_unset_vty(vty, argv[idx_peer]->arg,
+ PEER_FLAG_DYNAMIC_CAPABILITY);
+}
+
+/* neighbor dont-capability-negotiate */
+DEFUN (neighbor_dont_capability_negotiate,
+ neighbor_dont_capability_negotiate_cmd,
+ "neighbor <A.B.C.D|X:X::X:X|WORD> dont-capability-negotiate",
+ NEIGHBOR_STR
+ NEIGHBOR_ADDR_STR2
+ "Do not perform capability negotiation\n")
+{
+ int idx_peer = 1;
+ return peer_flag_set_vty(vty, argv[idx_peer]->arg,
+ PEER_FLAG_DONT_CAPABILITY);
+}
+
+DEFUN (no_neighbor_dont_capability_negotiate,
+ no_neighbor_dont_capability_negotiate_cmd,
+ "no neighbor <A.B.C.D|X:X::X:X|WORD> dont-capability-negotiate",
+ NO_STR
+ NEIGHBOR_STR
+ NEIGHBOR_ADDR_STR2
+ "Do not perform capability negotiation\n")
+{
+ int idx_peer = 2;
+ return peer_flag_unset_vty(vty, argv[idx_peer]->arg,
+ PEER_FLAG_DONT_CAPABILITY);
+}
+
+/* neighbor capability extended next hop encoding */
+DEFUN (neighbor_capability_enhe,
+ neighbor_capability_enhe_cmd,
+ "neighbor <A.B.C.D|X:X::X:X|WORD> capability extended-nexthop",
+ NEIGHBOR_STR
+ NEIGHBOR_ADDR_STR2
+ "Advertise capability to the peer\n"
+ "Advertise extended next-hop capability to the peer\n")
+{
+ int idx_peer = 1;
+ struct peer *peer;
+
+ peer = peer_and_group_lookup_vty(vty, argv[idx_peer]->arg);
+ if (peer && peer->conf_if)
+ return CMD_SUCCESS;
+
+ return peer_flag_set_vty(vty, argv[idx_peer]->arg,
+ PEER_FLAG_CAPABILITY_ENHE);
+}
+
+DEFUN (no_neighbor_capability_enhe,
+ no_neighbor_capability_enhe_cmd,
+ "no neighbor <A.B.C.D|X:X::X:X|WORD> capability extended-nexthop",
+ NO_STR
+ NEIGHBOR_STR
+ NEIGHBOR_ADDR_STR2
+ "Advertise capability to the peer\n"
+ "Advertise extended next-hop capability to the peer\n")
+{
+ int idx_peer = 2;
+ struct peer *peer;
+
+ peer = peer_and_group_lookup_vty(vty, argv[idx_peer]->arg);
+ if (peer && peer->conf_if) {
+ vty_out(vty,
+ "Peer %s cannot have capability extended-nexthop turned off\n",
+ argv[idx_peer]->arg);
+ return CMD_WARNING_CONFIG_FAILED;
+ }
+
+ return peer_flag_unset_vty(vty, argv[idx_peer]->arg,
+ PEER_FLAG_CAPABILITY_ENHE);
+}
+
+static int peer_af_flag_modify_vty(struct vty *vty, const char *peer_str,
+ afi_t afi, safi_t safi, uint32_t flag,
+ int set)
+{
+ int ret;
+ struct peer *peer;
+
+ peer = peer_and_group_lookup_vty(vty, peer_str);
+ if (!peer)
+ return CMD_WARNING_CONFIG_FAILED;
+
+ if (set)
+ ret = peer_af_flag_set(peer, afi, safi, flag);
+ else
+ ret = peer_af_flag_unset(peer, afi, safi, flag);
+
+ return bgp_vty_return(vty, ret);
+}
+
+static int peer_af_flag_set_vty(struct vty *vty, const char *peer_str,
+ afi_t afi, safi_t safi, uint32_t flag)
+{
+ return peer_af_flag_modify_vty(vty, peer_str, afi, safi, flag, 1);
+}
+
+static int peer_af_flag_unset_vty(struct vty *vty, const char *peer_str,
+ afi_t afi, safi_t safi, uint32_t flag)
+{
+ return peer_af_flag_modify_vty(vty, peer_str, afi, safi, flag, 0);
+}
+
+/* neighbor capability orf prefix-list. */
+DEFUN (neighbor_capability_orf_prefix,
+ neighbor_capability_orf_prefix_cmd,
+ "neighbor <A.B.C.D|X:X::X:X|WORD> capability orf prefix-list <both|send|receive>",
+ NEIGHBOR_STR
+ NEIGHBOR_ADDR_STR2
+ "Advertise capability to the peer\n"
+ "Advertise ORF capability to the peer\n"
+ "Advertise prefixlist ORF capability to this neighbor\n"
+ "Capability to SEND and RECEIVE the ORF to/from this neighbor\n"
+ "Capability to RECEIVE the ORF from this neighbor\n"
+ "Capability to SEND the ORF to this neighbor\n")
+{
+ int idx_send_recv = 5;
+ char *peer_str = argv[1]->arg;
+ struct peer *peer;
+ afi_t afi = bgp_node_afi(vty);
+ safi_t safi = bgp_node_safi(vty);
+
+ peer = peer_and_group_lookup_vty(vty, peer_str);
+ if (!peer)
+ return CMD_WARNING_CONFIG_FAILED;
+
+ if (strmatch(argv[idx_send_recv]->text, "send"))
+ return peer_af_flag_set_vty(vty, peer_str, afi, safi,
+ PEER_FLAG_ORF_PREFIX_SM);
+
+ if (strmatch(argv[idx_send_recv]->text, "receive"))
+ return peer_af_flag_set_vty(vty, peer_str, afi, safi,
+ PEER_FLAG_ORF_PREFIX_RM);
+
+ if (strmatch(argv[idx_send_recv]->text, "both"))
+ return peer_af_flag_set_vty(vty, peer_str, afi, safi,
+ PEER_FLAG_ORF_PREFIX_SM)
+ | peer_af_flag_set_vty(vty, peer_str, afi, safi,
+ PEER_FLAG_ORF_PREFIX_RM);
+
+ return CMD_WARNING_CONFIG_FAILED;
+}
+
+ALIAS_HIDDEN(
+ neighbor_capability_orf_prefix,
+ neighbor_capability_orf_prefix_hidden_cmd,
+ "neighbor <A.B.C.D|X:X::X:X|WORD> capability orf prefix-list <both|send|receive>",
+ NEIGHBOR_STR NEIGHBOR_ADDR_STR2
+ "Advertise capability to the peer\n"
+ "Advertise ORF capability to the peer\n"
+ "Advertise prefixlist ORF capability to this neighbor\n"
+ "Capability to SEND and RECEIVE the ORF to/from this neighbor\n"
+ "Capability to RECEIVE the ORF from this neighbor\n"
+ "Capability to SEND the ORF to this neighbor\n")
+
+DEFUN (no_neighbor_capability_orf_prefix,
+ no_neighbor_capability_orf_prefix_cmd,
+ "no neighbor <A.B.C.D|X:X::X:X|WORD> capability orf prefix-list <both|send|receive>",
+ NO_STR
+ NEIGHBOR_STR
+ NEIGHBOR_ADDR_STR2
+ "Advertise capability to the peer\n"
+ "Advertise ORF capability to the peer\n"
+ "Advertise prefixlist ORF capability to this neighbor\n"
+ "Capability to SEND and RECEIVE the ORF to/from this neighbor\n"
+ "Capability to RECEIVE the ORF from this neighbor\n"
+ "Capability to SEND the ORF to this neighbor\n")
+{
+ int idx_send_recv = 6;
+ char *peer_str = argv[2]->arg;
+ struct peer *peer;
+ afi_t afi = bgp_node_afi(vty);
+ safi_t safi = bgp_node_safi(vty);
+
+ peer = peer_and_group_lookup_vty(vty, peer_str);
+ if (!peer)
+ return CMD_WARNING_CONFIG_FAILED;
+
+ if (strmatch(argv[idx_send_recv]->text, "send"))
+ return peer_af_flag_unset_vty(vty, peer_str, afi, safi,
+ PEER_FLAG_ORF_PREFIX_SM);
+
+ if (strmatch(argv[idx_send_recv]->text, "receive"))
+ return peer_af_flag_unset_vty(vty, peer_str, afi, safi,
+ PEER_FLAG_ORF_PREFIX_RM);
+
+ if (strmatch(argv[idx_send_recv]->text, "both"))
+ return peer_af_flag_unset_vty(vty, peer_str, afi, safi,
+ PEER_FLAG_ORF_PREFIX_SM)
+ | peer_af_flag_unset_vty(vty, peer_str, afi, safi,
+ PEER_FLAG_ORF_PREFIX_RM);
+
+ return CMD_WARNING_CONFIG_FAILED;
+}
+
+ALIAS_HIDDEN(
+ no_neighbor_capability_orf_prefix,
+ no_neighbor_capability_orf_prefix_hidden_cmd,
+ "no neighbor <A.B.C.D|X:X::X:X|WORD> capability orf prefix-list <both|send|receive>",
+ NO_STR NEIGHBOR_STR NEIGHBOR_ADDR_STR2
+ "Advertise capability to the peer\n"
+ "Advertise ORF capability to the peer\n"
+ "Advertise prefixlist ORF capability to this neighbor\n"
+ "Capability to SEND and RECEIVE the ORF to/from this neighbor\n"
+ "Capability to RECEIVE the ORF from this neighbor\n"
+ "Capability to SEND the ORF to this neighbor\n")
+
+/* neighbor next-hop-self. */
+DEFUN (neighbor_nexthop_self,
+ neighbor_nexthop_self_cmd,
+ "neighbor <A.B.C.D|X:X::X:X|WORD> next-hop-self",
+ NEIGHBOR_STR
+ NEIGHBOR_ADDR_STR2
+ "Disable the next hop calculation for this neighbor\n")
+{
+ int idx_peer = 1;
+ return peer_af_flag_set_vty(vty, argv[idx_peer]->arg, bgp_node_afi(vty),
+ bgp_node_safi(vty), PEER_FLAG_NEXTHOP_SELF);
+}
+
+ALIAS_HIDDEN(neighbor_nexthop_self, neighbor_nexthop_self_hidden_cmd,
+ "neighbor <A.B.C.D|X:X::X:X|WORD> next-hop-self",
+ NEIGHBOR_STR NEIGHBOR_ADDR_STR2
+ "Disable the next hop calculation for this neighbor\n")
+
+/* neighbor next-hop-self. */
+DEFUN (neighbor_nexthop_self_force,
+ neighbor_nexthop_self_force_cmd,
+ "neighbor <A.B.C.D|X:X::X:X|WORD> next-hop-self force",
+ NEIGHBOR_STR
+ NEIGHBOR_ADDR_STR2
+ "Disable the next hop calculation for this neighbor\n"
+ "Set the next hop to self for reflected routes\n")
+{
+ int idx_peer = 1;
+ return peer_af_flag_set_vty(vty, argv[idx_peer]->arg, bgp_node_afi(vty),
+ bgp_node_safi(vty),
+ PEER_FLAG_FORCE_NEXTHOP_SELF);
+}
+
+ALIAS_HIDDEN(neighbor_nexthop_self_force,
+ neighbor_nexthop_self_force_hidden_cmd,
+ "neighbor <A.B.C.D|X:X::X:X|WORD> next-hop-self force",
+ NEIGHBOR_STR NEIGHBOR_ADDR_STR2
+ "Disable the next hop calculation for this neighbor\n"
+ "Set the next hop to self for reflected routes\n")
+
+ALIAS_HIDDEN(neighbor_nexthop_self_force,
+ neighbor_nexthop_self_all_hidden_cmd,
+ "neighbor <A.B.C.D|X:X::X:X|WORD> next-hop-self all",
+ NEIGHBOR_STR NEIGHBOR_ADDR_STR2
+ "Disable the next hop calculation for this neighbor\n"
+ "Set the next hop to self for reflected routes\n")
+
+DEFUN (no_neighbor_nexthop_self,
+ no_neighbor_nexthop_self_cmd,
+ "no neighbor <A.B.C.D|X:X::X:X|WORD> next-hop-self",
+ NO_STR
+ NEIGHBOR_STR
+ NEIGHBOR_ADDR_STR2
+ "Disable the next hop calculation for this neighbor\n")
+{
+ int idx_peer = 2;
+ return peer_af_flag_unset_vty(vty, argv[idx_peer]->arg,
+ bgp_node_afi(vty), bgp_node_safi(vty),
+ PEER_FLAG_NEXTHOP_SELF);
+}
+
+ALIAS_HIDDEN(no_neighbor_nexthop_self, no_neighbor_nexthop_self_hidden_cmd,
+ "no neighbor <A.B.C.D|X:X::X:X|WORD> next-hop-self",
+ NO_STR NEIGHBOR_STR NEIGHBOR_ADDR_STR2
+ "Disable the next hop calculation for this neighbor\n")
+
+DEFUN (no_neighbor_nexthop_self_force,
+ no_neighbor_nexthop_self_force_cmd,
+ "no neighbor <A.B.C.D|X:X::X:X|WORD> next-hop-self force",
+ NO_STR
+ NEIGHBOR_STR
+ NEIGHBOR_ADDR_STR2
+ "Disable the next hop calculation for this neighbor\n"
+ "Set the next hop to self for reflected routes\n")
+{
+ int idx_peer = 2;
+ return peer_af_flag_unset_vty(vty, argv[idx_peer]->arg,
+ bgp_node_afi(vty), bgp_node_safi(vty),
+ PEER_FLAG_FORCE_NEXTHOP_SELF);
+}
+
+ALIAS_HIDDEN(no_neighbor_nexthop_self_force,
+ no_neighbor_nexthop_self_force_hidden_cmd,
+ "no neighbor <A.B.C.D|X:X::X:X|WORD> next-hop-self force",
+ NO_STR NEIGHBOR_STR NEIGHBOR_ADDR_STR2
+ "Disable the next hop calculation for this neighbor\n"
+ "Set the next hop to self for reflected routes\n")
+
+ALIAS_HIDDEN(no_neighbor_nexthop_self_force,
+ no_neighbor_nexthop_self_all_hidden_cmd,
+ "no neighbor <A.B.C.D|X:X::X:X|WORD> next-hop-self all",
+ NO_STR NEIGHBOR_STR NEIGHBOR_ADDR_STR2
+ "Disable the next hop calculation for this neighbor\n"
+ "Set the next hop to self for reflected routes\n")
+
+/* neighbor as-override */
+DEFUN (neighbor_as_override,
+ neighbor_as_override_cmd,
+ "neighbor <A.B.C.D|X:X::X:X|WORD> as-override",
+ NEIGHBOR_STR
+ NEIGHBOR_ADDR_STR2
+ "Override ASNs in outbound updates if aspath equals remote-as\n")
+{
+ int idx_peer = 1;
+ return peer_af_flag_set_vty(vty, argv[idx_peer]->arg, bgp_node_afi(vty),
+ bgp_node_safi(vty), PEER_FLAG_AS_OVERRIDE);
+}
+
+ALIAS_HIDDEN(neighbor_as_override, neighbor_as_override_hidden_cmd,
+ "neighbor <A.B.C.D|X:X::X:X|WORD> as-override",
+ NEIGHBOR_STR NEIGHBOR_ADDR_STR2
+ "Override ASNs in outbound updates if aspath equals remote-as\n")
+
+DEFUN (no_neighbor_as_override,
+ no_neighbor_as_override_cmd,
+ "no neighbor <A.B.C.D|X:X::X:X|WORD> as-override",
+ NO_STR
+ NEIGHBOR_STR
+ NEIGHBOR_ADDR_STR2
+ "Override ASNs in outbound updates if aspath equals remote-as\n")
+{
+ int idx_peer = 2;
+ return peer_af_flag_unset_vty(vty, argv[idx_peer]->arg,
+ bgp_node_afi(vty), bgp_node_safi(vty),
+ PEER_FLAG_AS_OVERRIDE);
+}
+
+ALIAS_HIDDEN(no_neighbor_as_override, no_neighbor_as_override_hidden_cmd,
+ "no neighbor <A.B.C.D|X:X::X:X|WORD> as-override",
+ NO_STR NEIGHBOR_STR NEIGHBOR_ADDR_STR2
+ "Override ASNs in outbound updates if aspath equals remote-as\n")
+
+/* neighbor remove-private-AS. */
+DEFUN (neighbor_remove_private_as,
+ neighbor_remove_private_as_cmd,
+ "neighbor <A.B.C.D|X:X::X:X|WORD> remove-private-AS",
+ NEIGHBOR_STR
+ NEIGHBOR_ADDR_STR2
+ "Remove private ASNs in outbound updates\n")
+{
+ int idx_peer = 1;
+ return peer_af_flag_set_vty(vty, argv[idx_peer]->arg, bgp_node_afi(vty),
+ bgp_node_safi(vty),
+ PEER_FLAG_REMOVE_PRIVATE_AS);
+}
+
+ALIAS_HIDDEN(neighbor_remove_private_as, neighbor_remove_private_as_hidden_cmd,
+ "neighbor <A.B.C.D|X:X::X:X|WORD> remove-private-AS",
+ NEIGHBOR_STR NEIGHBOR_ADDR_STR2
+ "Remove private ASNs in outbound updates\n")
+
+DEFUN (neighbor_remove_private_as_all,
+ neighbor_remove_private_as_all_cmd,
+ "neighbor <A.B.C.D|X:X::X:X|WORD> remove-private-AS all",
+ NEIGHBOR_STR
+ NEIGHBOR_ADDR_STR2
+ "Remove private ASNs in outbound updates\n"
+ "Apply to all AS numbers\n")
+{
+ int idx_peer = 1;
+ return peer_af_flag_set_vty(vty, argv[idx_peer]->arg, bgp_node_afi(vty),
+ bgp_node_safi(vty),
+ PEER_FLAG_REMOVE_PRIVATE_AS_ALL);
+}
+
+ALIAS_HIDDEN(neighbor_remove_private_as_all,
+ neighbor_remove_private_as_all_hidden_cmd,
+ "neighbor <A.B.C.D|X:X::X:X|WORD> remove-private-AS all",
+ NEIGHBOR_STR NEIGHBOR_ADDR_STR2
+ "Remove private ASNs in outbound updates\n"
+ "Apply to all AS numbers")
+
+DEFUN (neighbor_remove_private_as_replace_as,
+ neighbor_remove_private_as_replace_as_cmd,
+ "neighbor <A.B.C.D|X:X::X:X|WORD> remove-private-AS replace-AS",
+ NEIGHBOR_STR
+ NEIGHBOR_ADDR_STR2
+ "Remove private ASNs in outbound updates\n"
+ "Replace private ASNs with our ASN in outbound updates\n")
+{
+ int idx_peer = 1;
+ return peer_af_flag_set_vty(vty, argv[idx_peer]->arg, bgp_node_afi(vty),
+ bgp_node_safi(vty),
+ PEER_FLAG_REMOVE_PRIVATE_AS_REPLACE);
+}
+
+ALIAS_HIDDEN(neighbor_remove_private_as_replace_as,
+ neighbor_remove_private_as_replace_as_hidden_cmd,
+ "neighbor <A.B.C.D|X:X::X:X|WORD> remove-private-AS replace-AS",
+ NEIGHBOR_STR NEIGHBOR_ADDR_STR2
+ "Remove private ASNs in outbound updates\n"
+ "Replace private ASNs with our ASN in outbound updates\n")
+
+DEFUN (neighbor_remove_private_as_all_replace_as,
+ neighbor_remove_private_as_all_replace_as_cmd,
+ "neighbor <A.B.C.D|X:X::X:X|WORD> remove-private-AS all replace-AS",
+ NEIGHBOR_STR
+ NEIGHBOR_ADDR_STR2
+ "Remove private ASNs in outbound updates\n"
+ "Apply to all AS numbers\n"
+ "Replace private ASNs with our ASN in outbound updates\n")
+{
+ int idx_peer = 1;
+ return peer_af_flag_set_vty(vty, argv[idx_peer]->arg, bgp_node_afi(vty),
+ bgp_node_safi(vty),
+ PEER_FLAG_REMOVE_PRIVATE_AS_ALL_REPLACE);
+}
+
+ALIAS_HIDDEN(
+ neighbor_remove_private_as_all_replace_as,
+ neighbor_remove_private_as_all_replace_as_hidden_cmd,
+ "neighbor <A.B.C.D|X:X::X:X|WORD> remove-private-AS all replace-AS",
+ NEIGHBOR_STR NEIGHBOR_ADDR_STR2
+ "Remove private ASNs in outbound updates\n"
+ "Apply to all AS numbers\n"
+ "Replace private ASNs with our ASN in outbound updates\n")
+
+DEFUN (no_neighbor_remove_private_as,
+ no_neighbor_remove_private_as_cmd,
+ "no neighbor <A.B.C.D|X:X::X:X|WORD> remove-private-AS",
+ NO_STR
+ NEIGHBOR_STR
+ NEIGHBOR_ADDR_STR2
+ "Remove private ASNs in outbound updates\n")
+{
+ int idx_peer = 2;
+ return peer_af_flag_unset_vty(vty, argv[idx_peer]->arg,
+ bgp_node_afi(vty), bgp_node_safi(vty),
+ PEER_FLAG_REMOVE_PRIVATE_AS);
+}
+
+ALIAS_HIDDEN(no_neighbor_remove_private_as,
+ no_neighbor_remove_private_as_hidden_cmd,
+ "no neighbor <A.B.C.D|X:X::X:X|WORD> remove-private-AS",
+ NO_STR NEIGHBOR_STR NEIGHBOR_ADDR_STR2
+ "Remove private ASNs in outbound updates\n")
+
+DEFUN (no_neighbor_remove_private_as_all,
+ no_neighbor_remove_private_as_all_cmd,
+ "no neighbor <A.B.C.D|X:X::X:X|WORD> remove-private-AS all",
+ NO_STR
+ NEIGHBOR_STR
+ NEIGHBOR_ADDR_STR2
+ "Remove private ASNs in outbound updates\n"
+ "Apply to all AS numbers\n")
+{
+ int idx_peer = 2;
+ return peer_af_flag_unset_vty(vty, argv[idx_peer]->arg,
+ bgp_node_afi(vty), bgp_node_safi(vty),
+ PEER_FLAG_REMOVE_PRIVATE_AS_ALL);
+}
+
+ALIAS_HIDDEN(no_neighbor_remove_private_as_all,
+ no_neighbor_remove_private_as_all_hidden_cmd,
+ "no neighbor <A.B.C.D|X:X::X:X|WORD> remove-private-AS all",
+ NO_STR NEIGHBOR_STR NEIGHBOR_ADDR_STR2
+ "Remove private ASNs in outbound updates\n"
+ "Apply to all AS numbers\n")
+
+DEFUN (no_neighbor_remove_private_as_replace_as,
+ no_neighbor_remove_private_as_replace_as_cmd,
+ "no neighbor <A.B.C.D|X:X::X:X|WORD> remove-private-AS replace-AS",
+ NO_STR
+ NEIGHBOR_STR
+ NEIGHBOR_ADDR_STR2
+ "Remove private ASNs in outbound updates\n"
+ "Replace private ASNs with our ASN in outbound updates\n")
+{
+ int idx_peer = 2;
+ return peer_af_flag_unset_vty(vty, argv[idx_peer]->arg,
+ bgp_node_afi(vty), bgp_node_safi(vty),
+ PEER_FLAG_REMOVE_PRIVATE_AS_REPLACE);
+}
+
+ALIAS_HIDDEN(no_neighbor_remove_private_as_replace_as,
+ no_neighbor_remove_private_as_replace_as_hidden_cmd,
+ "no neighbor <A.B.C.D|X:X::X:X|WORD> remove-private-AS replace-AS",
+ NO_STR NEIGHBOR_STR NEIGHBOR_ADDR_STR2
+ "Remove private ASNs in outbound updates\n"
+ "Replace private ASNs with our ASN in outbound updates\n")
+
+DEFUN (no_neighbor_remove_private_as_all_replace_as,
+ no_neighbor_remove_private_as_all_replace_as_cmd,
+ "no neighbor <A.B.C.D|X:X::X:X|WORD> remove-private-AS all replace-AS",
+ NO_STR
+ NEIGHBOR_STR
+ NEIGHBOR_ADDR_STR2
+ "Remove private ASNs in outbound updates\n"
+ "Apply to all AS numbers\n"
+ "Replace private ASNs with our ASN in outbound updates\n")
+{
+ int idx_peer = 2;
+ return peer_af_flag_unset_vty(vty, argv[idx_peer]->arg,
+ bgp_node_afi(vty), bgp_node_safi(vty),
+ PEER_FLAG_REMOVE_PRIVATE_AS_ALL_REPLACE);
+}
+
+ALIAS_HIDDEN(
+ no_neighbor_remove_private_as_all_replace_as,
+ no_neighbor_remove_private_as_all_replace_as_hidden_cmd,
+ "no neighbor <A.B.C.D|X:X::X:X|WORD> remove-private-AS all replace-AS",
+ NO_STR NEIGHBOR_STR NEIGHBOR_ADDR_STR2
+ "Remove private ASNs in outbound updates\n"
+ "Apply to all AS numbers\n"
+ "Replace private ASNs with our ASN in outbound updates\n")
+
+
+/* neighbor send-community. */
+DEFUN (neighbor_send_community,
+ neighbor_send_community_cmd,
+ "neighbor <A.B.C.D|X:X::X:X|WORD> send-community",
+ NEIGHBOR_STR
+ NEIGHBOR_ADDR_STR2
+ "Send Community attribute to this neighbor\n")
+{
+ int idx_peer = 1;
+
+ return peer_af_flag_set_vty(vty, argv[idx_peer]->arg, bgp_node_afi(vty),
+ bgp_node_safi(vty),
+ PEER_FLAG_SEND_COMMUNITY);
+}
+
+ALIAS_HIDDEN(neighbor_send_community, neighbor_send_community_hidden_cmd,
+ "neighbor <A.B.C.D|X:X::X:X|WORD> send-community",
+ NEIGHBOR_STR NEIGHBOR_ADDR_STR2
+ "Send Community attribute to this neighbor\n")
+
+DEFUN (no_neighbor_send_community,
+ no_neighbor_send_community_cmd,
+ "no neighbor <A.B.C.D|X:X::X:X|WORD> send-community",
+ NO_STR
+ NEIGHBOR_STR
+ NEIGHBOR_ADDR_STR2
+ "Send Community attribute to this neighbor\n")
+{
+ int idx_peer = 2;
+
+ return peer_af_flag_unset_vty(vty, argv[idx_peer]->arg,
+ bgp_node_afi(vty), bgp_node_safi(vty),
+ PEER_FLAG_SEND_COMMUNITY);
+}
+
+ALIAS_HIDDEN(no_neighbor_send_community, no_neighbor_send_community_hidden_cmd,
+ "no neighbor <A.B.C.D|X:X::X:X|WORD> send-community",
+ NO_STR NEIGHBOR_STR NEIGHBOR_ADDR_STR2
+ "Send Community attribute to this neighbor\n")
+
+/* neighbor send-community extended. */
+DEFUN (neighbor_send_community_type,
+ neighbor_send_community_type_cmd,
+ "neighbor <A.B.C.D|X:X::X:X|WORD> send-community <both|all|extended|standard|large>",
+ NEIGHBOR_STR
+ NEIGHBOR_ADDR_STR2
+ "Send Community attribute to this neighbor\n"
+ "Send Standard and Extended Community attributes\n"
+ "Send Standard, Large and Extended Community attributes\n"
+ "Send Extended Community attributes\n"
+ "Send Standard Community attributes\n"
+ "Send Large Community attributes\n")
+{
+ const char *type = argv[argc - 1]->text;
+ char *peer_str = argv[1]->arg;
+ struct peer *peer;
+ afi_t afi = bgp_node_afi(vty);
+ safi_t safi = bgp_node_safi(vty);
+
+ peer = peer_and_group_lookup_vty(vty, peer_str);
+ if (!peer)
+ return CMD_WARNING_CONFIG_FAILED;
+
+ if (strmatch(type, "standard"))
+ return peer_af_flag_set_vty(vty, peer_str, afi, safi,
+ PEER_FLAG_SEND_COMMUNITY);
+
+ if (strmatch(type, "extended"))
+ return peer_af_flag_set_vty(vty, peer_str, afi, safi,
+ PEER_FLAG_SEND_EXT_COMMUNITY);
+
+ if (strmatch(type, "large"))
+ return peer_af_flag_set_vty(vty, peer_str, afi, safi,
+ PEER_FLAG_SEND_LARGE_COMMUNITY);
+
+ if (strmatch(type, "both")) {
+ return peer_af_flag_set_vty(vty, peer_str, afi, safi,
+ PEER_FLAG_SEND_COMMUNITY)
+ | peer_af_flag_set_vty(vty, peer_str, afi, safi,
+ PEER_FLAG_SEND_EXT_COMMUNITY);
+ }
+ return peer_af_flag_set_vty(vty, peer_str, afi, safi,
+ PEER_FLAG_SEND_COMMUNITY)
+ | peer_af_flag_set_vty(vty, peer_str, afi, safi,
+ PEER_FLAG_SEND_EXT_COMMUNITY)
+ | peer_af_flag_set_vty(vty, peer_str, afi, safi,
+ PEER_FLAG_SEND_LARGE_COMMUNITY);
+}
+
+ALIAS_HIDDEN(
+ neighbor_send_community_type, neighbor_send_community_type_hidden_cmd,
+ "neighbor <A.B.C.D|X:X::X:X|WORD> send-community <both|all|extended|standard|large>",
+ NEIGHBOR_STR NEIGHBOR_ADDR_STR2
+ "Send Community attribute to this neighbor\n"
+ "Send Standard and Extended Community attributes\n"
+ "Send Standard, Large and Extended Community attributes\n"
+ "Send Extended Community attributes\n"
+ "Send Standard Community attributes\n"
+ "Send Large Community attributes\n")
+
+DEFUN (no_neighbor_send_community_type,
+ no_neighbor_send_community_type_cmd,
+ "no neighbor <A.B.C.D|X:X::X:X|WORD> send-community <both|all|extended|standard|large>",
+ NO_STR
+ NEIGHBOR_STR
+ NEIGHBOR_ADDR_STR2
+ "Send Community attribute to this neighbor\n"
+ "Send Standard and Extended Community attributes\n"
+ "Send Standard, Large and Extended Community attributes\n"
+ "Send Extended Community attributes\n"
+ "Send Standard Community attributes\n"
+ "Send Large Community attributes\n")
+{
+ const char *type = argv[argc - 1]->text;
+ char *peer_str = argv[2]->arg;
+ struct peer *peer;
+ afi_t afi = bgp_node_afi(vty);
+ safi_t safi = bgp_node_safi(vty);
+
+ peer = peer_and_group_lookup_vty(vty, peer_str);
+ if (!peer)
+ return CMD_WARNING_CONFIG_FAILED;
+
+ if (strmatch(type, "standard"))
+ return peer_af_flag_unset_vty(vty, peer_str, afi, safi,
+ PEER_FLAG_SEND_COMMUNITY);
+
+ if (strmatch(type, "extended"))
+ return peer_af_flag_unset_vty(vty, peer_str, afi, safi,
+ PEER_FLAG_SEND_EXT_COMMUNITY);
+
+ if (strmatch(type, "large"))
+ return peer_af_flag_unset_vty(vty, peer_str, afi, safi,
+ PEER_FLAG_SEND_LARGE_COMMUNITY);
+
+ if (strmatch(type, "both")) {
+
+ return peer_af_flag_unset_vty(vty, peer_str, afi, safi,
+ PEER_FLAG_SEND_COMMUNITY)
+ | peer_af_flag_unset_vty(vty, peer_str, afi, safi,
+ PEER_FLAG_SEND_EXT_COMMUNITY);
+ }
+
+ return peer_af_flag_unset_vty(vty, peer_str, afi, safi,
+ PEER_FLAG_SEND_COMMUNITY)
+ | peer_af_flag_unset_vty(vty, peer_str, afi, safi,
+ PEER_FLAG_SEND_EXT_COMMUNITY)
+ | peer_af_flag_unset_vty(vty, peer_str, afi, safi,
+ PEER_FLAG_SEND_LARGE_COMMUNITY);
+}
+
+ALIAS_HIDDEN(
+ no_neighbor_send_community_type,
+ no_neighbor_send_community_type_hidden_cmd,
+ "no neighbor <A.B.C.D|X:X::X:X|WORD> send-community <both|all|extended|standard|large>",
+ NO_STR NEIGHBOR_STR NEIGHBOR_ADDR_STR2
+ "Send Community attribute to this neighbor\n"
+ "Send Standard and Extended Community attributes\n"
+ "Send Standard, Large and Extended Community attributes\n"
+ "Send Extended Community attributes\n"
+ "Send Standard Community attributes\n"
+ "Send Large Community attributes\n")
+
+/* neighbor soft-reconfig. */
+DEFUN (neighbor_soft_reconfiguration,
+ neighbor_soft_reconfiguration_cmd,
+ "neighbor <A.B.C.D|X:X::X:X|WORD> soft-reconfiguration inbound",
+ NEIGHBOR_STR
+ NEIGHBOR_ADDR_STR2
+ "Per neighbor soft reconfiguration\n"
+ "Allow inbound soft reconfiguration for this neighbor\n")
+{
+ int idx_peer = 1;
+ return peer_af_flag_set_vty(vty, argv[idx_peer]->arg, bgp_node_afi(vty),
+ bgp_node_safi(vty),
+ PEER_FLAG_SOFT_RECONFIG);
+}
+
+ALIAS_HIDDEN(neighbor_soft_reconfiguration,
+ neighbor_soft_reconfiguration_hidden_cmd,
+ "neighbor <A.B.C.D|X:X::X:X|WORD> soft-reconfiguration inbound",
+ NEIGHBOR_STR NEIGHBOR_ADDR_STR2
+ "Per neighbor soft reconfiguration\n"
+ "Allow inbound soft reconfiguration for this neighbor\n")
+
+DEFUN (no_neighbor_soft_reconfiguration,
+ no_neighbor_soft_reconfiguration_cmd,
+ "no neighbor <A.B.C.D|X:X::X:X|WORD> soft-reconfiguration inbound",
+ NO_STR
+ NEIGHBOR_STR
+ NEIGHBOR_ADDR_STR2
+ "Per neighbor soft reconfiguration\n"
+ "Allow inbound soft reconfiguration for this neighbor\n")
+{
+ int idx_peer = 2;
+ return peer_af_flag_unset_vty(vty, argv[idx_peer]->arg,
+ bgp_node_afi(vty), bgp_node_safi(vty),
+ PEER_FLAG_SOFT_RECONFIG);
+}
+
+ALIAS_HIDDEN(no_neighbor_soft_reconfiguration,
+ no_neighbor_soft_reconfiguration_hidden_cmd,
+ "no neighbor <A.B.C.D|X:X::X:X|WORD> soft-reconfiguration inbound",
+ NO_STR NEIGHBOR_STR NEIGHBOR_ADDR_STR2
+ "Per neighbor soft reconfiguration\n"
+ "Allow inbound soft reconfiguration for this neighbor\n")
+
+DEFUN (neighbor_route_reflector_client,
+ neighbor_route_reflector_client_cmd,
+ "neighbor <A.B.C.D|X:X::X:X|WORD> route-reflector-client",
+ NEIGHBOR_STR
+ NEIGHBOR_ADDR_STR2
+ "Configure a neighbor as Route Reflector client\n")
+{
+ int idx_peer = 1;
+ struct peer *peer;
+
+
+ peer = peer_and_group_lookup_vty(vty, argv[idx_peer]->arg);
+ if (!peer)
+ return CMD_WARNING_CONFIG_FAILED;
+
+ return peer_af_flag_set_vty(vty, argv[idx_peer]->arg, bgp_node_afi(vty),
+ bgp_node_safi(vty),
+ PEER_FLAG_REFLECTOR_CLIENT);
+}
+
+ALIAS_HIDDEN(neighbor_route_reflector_client,
+ neighbor_route_reflector_client_hidden_cmd,
+ "neighbor <A.B.C.D|X:X::X:X|WORD> route-reflector-client",
+ NEIGHBOR_STR NEIGHBOR_ADDR_STR2
+ "Configure a neighbor as Route Reflector client\n")
+
+DEFUN (no_neighbor_route_reflector_client,
+ no_neighbor_route_reflector_client_cmd,
+ "no neighbor <A.B.C.D|X:X::X:X|WORD> route-reflector-client",
+ NO_STR
+ NEIGHBOR_STR
+ NEIGHBOR_ADDR_STR2
+ "Configure a neighbor as Route Reflector client\n")
+{
+ int idx_peer = 2;
+ return peer_af_flag_unset_vty(vty, argv[idx_peer]->arg,
+ bgp_node_afi(vty), bgp_node_safi(vty),
+ PEER_FLAG_REFLECTOR_CLIENT);
+}
+
+ALIAS_HIDDEN(no_neighbor_route_reflector_client,
+ no_neighbor_route_reflector_client_hidden_cmd,
+ "no neighbor <A.B.C.D|X:X::X:X|WORD> route-reflector-client",
+ NO_STR NEIGHBOR_STR NEIGHBOR_ADDR_STR2
+ "Configure a neighbor as Route Reflector client\n")
+
+/* neighbor route-server-client. */
+DEFUN (neighbor_route_server_client,
+ neighbor_route_server_client_cmd,
+ "neighbor <A.B.C.D|X:X::X:X|WORD> route-server-client",
+ NEIGHBOR_STR
+ NEIGHBOR_ADDR_STR2
+ "Configure a neighbor as Route Server client\n")
+{
+ int idx_peer = 1;
+ struct peer *peer;
+
+ peer = peer_and_group_lookup_vty(vty, argv[idx_peer]->arg);
+ if (!peer)
+ return CMD_WARNING_CONFIG_FAILED;
+ return peer_af_flag_set_vty(vty, argv[idx_peer]->arg, bgp_node_afi(vty),
+ bgp_node_safi(vty),
+ PEER_FLAG_RSERVER_CLIENT);
+}
+
+ALIAS_HIDDEN(neighbor_route_server_client,
+ neighbor_route_server_client_hidden_cmd,
+ "neighbor <A.B.C.D|X:X::X:X|WORD> route-server-client",
+ NEIGHBOR_STR NEIGHBOR_ADDR_STR2
+ "Configure a neighbor as Route Server client\n")
+
+DEFUN (no_neighbor_route_server_client,
+ no_neighbor_route_server_client_cmd,
+ "no neighbor <A.B.C.D|X:X::X:X|WORD> route-server-client",
+ NO_STR
+ NEIGHBOR_STR
+ NEIGHBOR_ADDR_STR2
+ "Configure a neighbor as Route Server client\n")
+{
+ int idx_peer = 2;
+ return peer_af_flag_unset_vty(vty, argv[idx_peer]->arg,
+ bgp_node_afi(vty), bgp_node_safi(vty),
+ PEER_FLAG_RSERVER_CLIENT);
+}
+
+ALIAS_HIDDEN(no_neighbor_route_server_client,
+ no_neighbor_route_server_client_hidden_cmd,
+ "no neighbor <A.B.C.D|X:X::X:X|WORD> route-server-client",
+ NO_STR NEIGHBOR_STR NEIGHBOR_ADDR_STR2
+ "Configure a neighbor as Route Server client\n")
+
+DEFUN (neighbor_nexthop_local_unchanged,
+ neighbor_nexthop_local_unchanged_cmd,
+ "neighbor <A.B.C.D|X:X::X:X|WORD> nexthop-local unchanged",
+ NEIGHBOR_STR
+ NEIGHBOR_ADDR_STR2
+ "Configure treatment of outgoing link-local nexthop attribute\n"
+ "Leave link-local nexthop unchanged for this peer\n")
+{
+ int idx_peer = 1;
+ return peer_af_flag_set_vty(vty, argv[idx_peer]->arg, bgp_node_afi(vty),
+ bgp_node_safi(vty),
+ PEER_FLAG_NEXTHOP_LOCAL_UNCHANGED);
+}
+
+DEFUN (no_neighbor_nexthop_local_unchanged,
+ no_neighbor_nexthop_local_unchanged_cmd,
+ "no neighbor <A.B.C.D|X:X::X:X|WORD> nexthop-local unchanged",
+ NO_STR
+ NEIGHBOR_STR
+ NEIGHBOR_ADDR_STR2
+ "Configure treatment of outgoing link-local-nexthop attribute\n"
+ "Leave link-local nexthop unchanged for this peer\n")
+{
+ int idx_peer = 2;
+ return peer_af_flag_unset_vty(vty, argv[idx_peer]->arg,
+ bgp_node_afi(vty), bgp_node_safi(vty),
+ PEER_FLAG_NEXTHOP_LOCAL_UNCHANGED);
+}
+
+DEFUN (neighbor_attr_unchanged,
+ neighbor_attr_unchanged_cmd,
+ "neighbor <A.B.C.D|X:X::X:X|WORD> attribute-unchanged [{as-path|next-hop|med}]",
+ NEIGHBOR_STR
+ NEIGHBOR_ADDR_STR2
+ "BGP attribute is propagated unchanged to this neighbor\n"
+ "As-path attribute\n"
+ "Nexthop attribute\n"
+ "Med attribute\n")
+{
+ int idx = 0;
+ char *peer_str = argv[1]->arg;
+ struct peer *peer;
+ bool aspath = false;
+ bool nexthop = false;
+ bool med = false;
+ afi_t afi = bgp_node_afi(vty);
+ safi_t safi = bgp_node_safi(vty);
+ int ret = 0;
+
+ peer = peer_and_group_lookup_vty(vty, peer_str);
+ if (!peer)
+ return CMD_WARNING_CONFIG_FAILED;
+
+ if (argv_find(argv, argc, "as-path", &idx))
+ aspath = true;
+
+ idx = 0;
+ if (argv_find(argv, argc, "next-hop", &idx))
+ nexthop = true;
+
+ idx = 0;
+ if (argv_find(argv, argc, "med", &idx))
+ med = true;
+
+ /* no flags means all of them! */
+ if (!aspath && !nexthop && !med) {
+ ret = peer_af_flag_set_vty(vty, peer_str, afi, safi,
+ PEER_FLAG_AS_PATH_UNCHANGED);
+ ret |= peer_af_flag_set_vty(vty, peer_str, afi, safi,
+ PEER_FLAG_NEXTHOP_UNCHANGED);
+ ret |= peer_af_flag_set_vty(vty, peer_str, afi, safi,
+ PEER_FLAG_MED_UNCHANGED);
+ } else {
+ if (!aspath) {
+ if (peer_af_flag_check(peer, afi, safi,
+ PEER_FLAG_AS_PATH_UNCHANGED)) {
+ ret |= peer_af_flag_unset_vty(
+ vty, peer_str, afi, safi,
+ PEER_FLAG_AS_PATH_UNCHANGED);
+ }
+ } else
+ ret |= peer_af_flag_set_vty(
+ vty, peer_str, afi, safi,
+ PEER_FLAG_AS_PATH_UNCHANGED);
+
+ if (!nexthop) {
+ if (peer_af_flag_check(peer, afi, safi,
+ PEER_FLAG_NEXTHOP_UNCHANGED)) {
+ ret |= peer_af_flag_unset_vty(
+ vty, peer_str, afi, safi,
+ PEER_FLAG_NEXTHOP_UNCHANGED);
+ }
+ } else
+ ret |= peer_af_flag_set_vty(
+ vty, peer_str, afi, safi,
+ PEER_FLAG_NEXTHOP_UNCHANGED);
+
+ if (!med) {
+ if (peer_af_flag_check(peer, afi, safi,
+ PEER_FLAG_MED_UNCHANGED)) {
+ ret |= peer_af_flag_unset_vty(
+ vty, peer_str, afi, safi,
+ PEER_FLAG_MED_UNCHANGED);
+ }
+ } else
+ ret |= peer_af_flag_set_vty(vty, peer_str, afi, safi,
+ PEER_FLAG_MED_UNCHANGED);
+ }
+
+ return ret;
+}
+
+ALIAS_HIDDEN(
+ neighbor_attr_unchanged, neighbor_attr_unchanged_hidden_cmd,
+ "neighbor <A.B.C.D|X:X::X:X|WORD> attribute-unchanged [{as-path|next-hop|med}]",
+ NEIGHBOR_STR NEIGHBOR_ADDR_STR2
+ "BGP attribute is propagated unchanged to this neighbor\n"
+ "As-path attribute\n"
+ "Nexthop attribute\n"
+ "Med attribute\n")
+
+DEFUN (no_neighbor_attr_unchanged,
+ no_neighbor_attr_unchanged_cmd,
+ "no neighbor <A.B.C.D|X:X::X:X|WORD> attribute-unchanged [{as-path|next-hop|med}]",
+ NO_STR
+ NEIGHBOR_STR
+ NEIGHBOR_ADDR_STR2
+ "BGP attribute is propagated unchanged to this neighbor\n"
+ "As-path attribute\n"
+ "Nexthop attribute\n"
+ "Med attribute\n")
+{
+ int idx = 0;
+ char *peer_str = argv[2]->arg;
+ struct peer *peer;
+ bool aspath = false;
+ bool nexthop = false;
+ bool med = false;
+ afi_t afi = bgp_node_afi(vty);
+ safi_t safi = bgp_node_safi(vty);
+ int ret = 0;
+
+ peer = peer_and_group_lookup_vty(vty, peer_str);
+ if (!peer)
+ return CMD_WARNING_CONFIG_FAILED;
+
+ if (argv_find(argv, argc, "as-path", &idx))
+ aspath = true;
+
+ idx = 0;
+ if (argv_find(argv, argc, "next-hop", &idx))
+ nexthop = true;
+
+ idx = 0;
+ if (argv_find(argv, argc, "med", &idx))
+ med = true;
+
+ if (!aspath && !nexthop && !med) // no flags means all of them!
+ return peer_af_flag_unset_vty(vty, peer_str, afi, safi,
+ PEER_FLAG_AS_PATH_UNCHANGED)
+ | peer_af_flag_unset_vty(vty, peer_str, afi, safi,
+ PEER_FLAG_NEXTHOP_UNCHANGED)
+ | peer_af_flag_unset_vty(vty, peer_str, afi, safi,
+ PEER_FLAG_MED_UNCHANGED);
+
+ if (aspath)
+ ret |= peer_af_flag_unset_vty(vty, peer_str, afi, safi,
+ PEER_FLAG_AS_PATH_UNCHANGED);
+
+ if (nexthop)
+ ret |= peer_af_flag_unset_vty(vty, peer_str, afi, safi,
+ PEER_FLAG_NEXTHOP_UNCHANGED);
+
+ if (med)
+ ret |= peer_af_flag_unset_vty(vty, peer_str, afi, safi,
+ PEER_FLAG_MED_UNCHANGED);
+
+ return ret;
+}
+
+ALIAS_HIDDEN(
+ no_neighbor_attr_unchanged, no_neighbor_attr_unchanged_hidden_cmd,
+ "no neighbor <A.B.C.D|X:X::X:X|WORD> attribute-unchanged [{as-path|next-hop|med}]",
+ NO_STR NEIGHBOR_STR NEIGHBOR_ADDR_STR2
+ "BGP attribute is propagated unchanged to this neighbor\n"
+ "As-path attribute\n"
+ "Nexthop attribute\n"
+ "Med attribute\n")
+
+/* EBGP multihop configuration. */
+static int peer_ebgp_multihop_set_vty(struct vty *vty, const char *ip_str,
+ const char *ttl_str)
+{
+ struct peer *peer;
+ unsigned int ttl;
+
+ peer = peer_and_group_lookup_vty(vty, ip_str);
+ if (!peer)
+ return CMD_WARNING_CONFIG_FAILED;
+
+ if (peer->conf_if)
+ return bgp_vty_return(vty, BGP_ERR_INVALID_FOR_DIRECT_PEER);
+
+ if (!ttl_str)
+ ttl = MAXTTL;
+ else
+ ttl = strtoul(ttl_str, NULL, 10);
+
+ return bgp_vty_return(vty, peer_ebgp_multihop_set(peer, ttl));
+}
+
+static int peer_ebgp_multihop_unset_vty(struct vty *vty, const char *ip_str)
+{
+ struct peer *peer;
+
+ peer = peer_and_group_lookup_vty(vty, ip_str);
+ if (!peer)
+ return CMD_WARNING_CONFIG_FAILED;
+
+ return bgp_vty_return(vty, peer_ebgp_multihop_unset(peer));
+}
+
+/* neighbor ebgp-multihop. */
+DEFUN (neighbor_ebgp_multihop,
+ neighbor_ebgp_multihop_cmd,
+ "neighbor <A.B.C.D|X:X::X:X|WORD> ebgp-multihop",
+ NEIGHBOR_STR
+ NEIGHBOR_ADDR_STR2
+ "Allow EBGP neighbors not on directly connected networks\n")
+{
+ int idx_peer = 1;
+ return peer_ebgp_multihop_set_vty(vty, argv[idx_peer]->arg, NULL);
+}
+
+DEFUN (neighbor_ebgp_multihop_ttl,
+ neighbor_ebgp_multihop_ttl_cmd,
+ "neighbor <A.B.C.D|X:X::X:X|WORD> ebgp-multihop (1-255)",
+ NEIGHBOR_STR
+ NEIGHBOR_ADDR_STR2
+ "Allow EBGP neighbors not on directly connected networks\n"
+ "maximum hop count\n")
+{
+ int idx_peer = 1;
+ int idx_number = 3;
+ return peer_ebgp_multihop_set_vty(vty, argv[idx_peer]->arg,
+ argv[idx_number]->arg);
+}
+
+DEFUN (no_neighbor_ebgp_multihop,
+ no_neighbor_ebgp_multihop_cmd,
+ "no neighbor <A.B.C.D|X:X::X:X|WORD> ebgp-multihop [(1-255)]",
+ NO_STR
+ NEIGHBOR_STR
+ NEIGHBOR_ADDR_STR2
+ "Allow EBGP neighbors not on directly connected networks\n"
+ "maximum hop count\n")
+{
+ int idx_peer = 2;
+ return peer_ebgp_multihop_unset_vty(vty, argv[idx_peer]->arg);
+}
+
+static uint8_t get_role_by_name(const char *role_str)
+{
+ if (strncmp(role_str, "peer", 2) == 0)
+ return ROLE_PEER;
+ if (strncmp(role_str, "provider", 2) == 0)
+ return ROLE_PROVIDER;
+ if (strncmp(role_str, "customer", 2) == 0)
+ return ROLE_CUSTOMER;
+ if (strncmp(role_str, "rs-server", 4) == 0)
+ return ROLE_RS_SERVER;
+ if (strncmp(role_str, "rs-client", 4) == 0)
+ return ROLE_RS_CLIENT;
+ return ROLE_UNDEFINED;
+}
+
+static int peer_role_set_vty(struct vty *vty, const char *ip_str,
+ const char *role_str, bool strict_mode)
+{
+ struct peer *peer;
+
+ peer = peer_and_group_lookup_vty(vty, ip_str);
+ if (!peer)
+ return CMD_WARNING_CONFIG_FAILED;
+ uint8_t role = get_role_by_name(role_str);
+
+ if (role == ROLE_UNDEFINED)
+ return bgp_vty_return(vty, BGP_ERR_INVALID_ROLE_NAME);
+ return bgp_vty_return(vty, peer_role_set(peer, role, strict_mode));
+}
+
+static int peer_role_unset_vty(struct vty *vty, const char *ip_str)
+{
+ struct peer *peer;
+
+ peer = peer_and_group_lookup_vty(vty, ip_str);
+ if (!peer)
+ return CMD_WARNING_CONFIG_FAILED;
+ return bgp_vty_return(vty, peer_role_unset(peer));
+}
+
+DEFPY(neighbor_role,
+ neighbor_role_cmd,
+ "neighbor <A.B.C.D|X:X::X:X|WORD> local-role <provider|rs-server|rs-client|customer|peer>",
+ NEIGHBOR_STR
+ NEIGHBOR_ADDR_STR2
+ "Set session role\n"
+ ROLE_STR)
+{
+ int idx_peer = 1;
+ int idx_role = 3;
+
+ return peer_role_set_vty(vty, argv[idx_peer]->arg, argv[idx_role]->arg,
+ false);
+}
+
+DEFPY(neighbor_role_strict,
+ neighbor_role_strict_cmd,
+ "neighbor <A.B.C.D|X:X::X:X|WORD> local-role <provider|rs-server|rs-client|customer|peer> strict-mode",
+ NEIGHBOR_STR
+ NEIGHBOR_ADDR_STR2
+ "Set session role\n"
+ ROLE_STR
+ "Use additional restriction on peer\n")
+{
+ int idx_peer = 1;
+ int idx_role = 3;
+
+ return peer_role_set_vty(vty, argv[idx_peer]->arg, argv[idx_role]->arg,
+ true);
+}
+
+DEFPY(no_neighbor_role,
+ no_neighbor_role_cmd,
+ "no neighbor <A.B.C.D|X:X::X:X|WORD> local-role <provider|rs-server|rs-client|customer|peer> [strict-mode]",
+ NO_STR
+ NEIGHBOR_STR
+ NEIGHBOR_ADDR_STR2
+ "Set session role\n"
+ ROLE_STR
+ "Use additional restriction on peer\n")
+{
+ int idx_peer = 2;
+
+ return peer_role_unset_vty(vty, argv[idx_peer]->arg);
+}
+
+/* disable-connected-check */
+DEFUN (neighbor_disable_connected_check,
+ neighbor_disable_connected_check_cmd,
+ "neighbor <A.B.C.D|X:X::X:X|WORD> <disable-connected-check|enforce-multihop>",
+ NEIGHBOR_STR
+ NEIGHBOR_ADDR_STR2
+ "one-hop away EBGP peer using loopback address\n"
+ "Enforce EBGP neighbors perform multihop\n")
+{
+ int idx_peer = 1;
+ return peer_flag_set_vty(vty, argv[idx_peer]->arg,
+ PEER_FLAG_DISABLE_CONNECTED_CHECK);
+}
+
+DEFUN (no_neighbor_disable_connected_check,
+ no_neighbor_disable_connected_check_cmd,
+ "no neighbor <A.B.C.D|X:X::X:X|WORD> <disable-connected-check|enforce-multihop>",
+ NO_STR
+ NEIGHBOR_STR
+ NEIGHBOR_ADDR_STR2
+ "one-hop away EBGP peer using loopback address\n"
+ "Enforce EBGP neighbors perform multihop\n")
+{
+ int idx_peer = 2;
+ return peer_flag_unset_vty(vty, argv[idx_peer]->arg,
+ PEER_FLAG_DISABLE_CONNECTED_CHECK);
+}
+
+/* disable-link-bw-encoding-ieee */
+DEFUN(neighbor_disable_link_bw_encoding_ieee,
+ neighbor_disable_link_bw_encoding_ieee_cmd,
+ "neighbor <A.B.C.D|X:X::X:X|WORD> disable-link-bw-encoding-ieee",
+ NEIGHBOR_STR NEIGHBOR_ADDR_STR2
+ "Disable IEEE floating-point encoding for extended community bandwidth\n")
+{
+ int idx_peer = 1;
+
+ return peer_flag_set_vty(vty, argv[idx_peer]->arg,
+ PEER_FLAG_DISABLE_LINK_BW_ENCODING_IEEE);
+}
+
+DEFUN(no_neighbor_disable_link_bw_encoding_ieee,
+ no_neighbor_disable_link_bw_encoding_ieee_cmd,
+ "no neighbor <A.B.C.D|X:X::X:X|WORD> disable-link-bw-encoding-ieee",
+ NO_STR NEIGHBOR_STR NEIGHBOR_ADDR_STR2
+ "Disable IEEE floating-point encoding for extended community bandwidth\n")
+{
+ int idx_peer = 2;
+
+ return peer_flag_unset_vty(vty, argv[idx_peer]->arg,
+ PEER_FLAG_DISABLE_LINK_BW_ENCODING_IEEE);
+}
+
+/* extended-optional-parameters */
+DEFUN(neighbor_extended_optional_parameters,
+ neighbor_extended_optional_parameters_cmd,
+ "neighbor <A.B.C.D|X:X::X:X|WORD> extended-optional-parameters",
+ NEIGHBOR_STR NEIGHBOR_ADDR_STR2
+ "Force the extended optional parameters format for OPEN messages\n")
+{
+ int idx_peer = 1;
+
+ return peer_flag_set_vty(vty, argv[idx_peer]->arg,
+ PEER_FLAG_EXTENDED_OPT_PARAMS);
+}
+
+DEFUN(no_neighbor_extended_optional_parameters,
+ no_neighbor_extended_optional_parameters_cmd,
+ "no neighbor <A.B.C.D|X:X::X:X|WORD> extended-optional-parameters",
+ NO_STR NEIGHBOR_STR NEIGHBOR_ADDR_STR2
+ "Force the extended optional parameters format for OPEN messages\n")
+{
+ int idx_peer = 2;
+
+ return peer_flag_unset_vty(vty, argv[idx_peer]->arg,
+ PEER_FLAG_EXTENDED_OPT_PARAMS);
+}
+
+/* enforce-first-as */
+DEFUN (neighbor_enforce_first_as,
+ neighbor_enforce_first_as_cmd,
+ "neighbor <A.B.C.D|X:X::X:X|WORD> enforce-first-as",
+ NEIGHBOR_STR
+ NEIGHBOR_ADDR_STR2
+ "Enforce the first AS for EBGP routes\n")
+{
+ int idx_peer = 1;
+
+ return peer_flag_set_vty(vty, argv[idx_peer]->arg,
+ PEER_FLAG_ENFORCE_FIRST_AS);
+}
+
+DEFUN (no_neighbor_enforce_first_as,
+ no_neighbor_enforce_first_as_cmd,
+ "no neighbor <A.B.C.D|X:X::X:X|WORD> enforce-first-as",
+ NO_STR
+ NEIGHBOR_STR
+ NEIGHBOR_ADDR_STR2
+ "Enforce the first AS for EBGP routes\n")
+{
+ int idx_peer = 2;
+
+ return peer_flag_unset_vty(vty, argv[idx_peer]->arg,
+ PEER_FLAG_ENFORCE_FIRST_AS);
+}
+
+
+DEFUN (neighbor_description,
+ neighbor_description_cmd,
+ "neighbor <A.B.C.D|X:X::X:X|WORD> description LINE...",
+ NEIGHBOR_STR
+ NEIGHBOR_ADDR_STR2
+ "Neighbor specific description\n"
+ "Up to 80 characters describing this neighbor\n")
+{
+ int idx_peer = 1;
+ int idx_line = 3;
+ struct peer *peer;
+ char *str;
+
+ peer = peer_and_group_lookup_vty(vty, argv[idx_peer]->arg);
+ if (!peer)
+ return CMD_WARNING_CONFIG_FAILED;
+
+ str = argv_concat(argv, argc, idx_line);
+
+ peer_description_set(peer, str);
+
+ XFREE(MTYPE_TMP, str);
+
+ return CMD_SUCCESS;
+}
+
+DEFUN (no_neighbor_description,
+ no_neighbor_description_cmd,
+ "no neighbor <A.B.C.D|X:X::X:X|WORD> description",
+ NO_STR
+ NEIGHBOR_STR
+ NEIGHBOR_ADDR_STR2
+ "Neighbor specific description\n")
+{
+ int idx_peer = 2;
+ struct peer *peer;
+
+ peer = peer_and_group_lookup_vty(vty, argv[idx_peer]->arg);
+ if (!peer)
+ return CMD_WARNING_CONFIG_FAILED;
+
+ peer_description_unset(peer);
+
+ return CMD_SUCCESS;
+}
+
+ALIAS(no_neighbor_description, no_neighbor_description_comment_cmd,
+ "no neighbor <A.B.C.D|X:X::X:X|WORD> description LINE...",
+ NO_STR NEIGHBOR_STR NEIGHBOR_ADDR_STR2
+ "Neighbor specific description\n"
+ "Up to 80 characters describing this neighbor\n")
+
+/* Neighbor update-source. */
+static int peer_update_source_vty(struct vty *vty, const char *peer_str,
+ const char *source_str)
+{
+ struct peer *peer;
+ struct prefix p;
+ union sockunion su;
+
+ peer = peer_and_group_lookup_vty(vty, peer_str);
+ if (!peer)
+ return CMD_WARNING_CONFIG_FAILED;
+
+ if (peer->conf_if)
+ return CMD_WARNING;
+
+ if (source_str) {
+ if (str2sockunion(source_str, &su) == 0)
+ peer_update_source_addr_set(peer, &su);
+ else {
+ if (str2prefix(source_str, &p)) {
+ vty_out(vty,
+ "%% Invalid update-source, remove prefix length \n");
+ return CMD_WARNING_CONFIG_FAILED;
+ } else
+ peer_update_source_if_set(peer, source_str);
+ }
+ } else
+ peer_update_source_unset(peer);
+
+ return CMD_SUCCESS;
+}
+
+#define BGP_UPDATE_SOURCE_HELP_STR \
+ "IPv4 address\n" \
+ "IPv6 address\n" \
+ "Interface name (requires zebra to be running)\n"
+
+DEFUN (neighbor_update_source,
+ neighbor_update_source_cmd,
+ "neighbor <A.B.C.D|X:X::X:X|WORD> update-source <A.B.C.D|X:X::X:X|WORD>",
+ NEIGHBOR_STR
+ NEIGHBOR_ADDR_STR2
+ "Source of routing updates\n"
+ BGP_UPDATE_SOURCE_HELP_STR)
+{
+ int idx_peer = 1;
+ int idx_peer_2 = 3;
+ return peer_update_source_vty(vty, argv[idx_peer]->arg,
+ argv[idx_peer_2]->arg);
+}
+
+DEFUN (no_neighbor_update_source,
+ no_neighbor_update_source_cmd,
+ "no neighbor <A.B.C.D|X:X::X:X|WORD> update-source [<A.B.C.D|X:X::X:X|WORD>]",
+ NO_STR
+ NEIGHBOR_STR
+ NEIGHBOR_ADDR_STR2
+ "Source of routing updates\n"
+ BGP_UPDATE_SOURCE_HELP_STR)
+{
+ int idx_peer = 2;
+ return peer_update_source_vty(vty, argv[idx_peer]->arg, NULL);
+}
+
+static int peer_default_originate_set_vty(struct vty *vty, const char *peer_str,
+ afi_t afi, safi_t safi,
+ const char *rmap, int set)
+{
+ int ret;
+ struct peer *peer;
+ struct route_map *route_map = NULL;
+
+ peer = peer_and_group_lookup_vty(vty, peer_str);
+ if (!peer)
+ return CMD_WARNING_CONFIG_FAILED;
+
+ if (set) {
+ if (rmap)
+ route_map = route_map_lookup_warn_noexist(vty, rmap);
+ ret = peer_default_originate_set(peer, afi, safi,
+ rmap, route_map);
+ } else
+ ret = peer_default_originate_unset(peer, afi, safi);
+
+ return bgp_vty_return(vty, ret);
+}
+
+/* neighbor default-originate. */
+DEFUN (neighbor_default_originate,
+ neighbor_default_originate_cmd,
+ "neighbor <A.B.C.D|X:X::X:X|WORD> default-originate",
+ NEIGHBOR_STR
+ NEIGHBOR_ADDR_STR2
+ "Originate default route to this neighbor\n")
+{
+ int idx_peer = 1;
+ return peer_default_originate_set_vty(vty, argv[idx_peer]->arg,
+ bgp_node_afi(vty),
+ bgp_node_safi(vty), NULL, 1);
+}
+
+ALIAS_HIDDEN(neighbor_default_originate, neighbor_default_originate_hidden_cmd,
+ "neighbor <A.B.C.D|X:X::X:X|WORD> default-originate",
+ NEIGHBOR_STR NEIGHBOR_ADDR_STR2
+ "Originate default route to this neighbor\n")
+
+DEFUN (neighbor_default_originate_rmap,
+ neighbor_default_originate_rmap_cmd,
+ "neighbor <A.B.C.D|X:X::X:X|WORD> default-originate route-map RMAP_NAME",
+ NEIGHBOR_STR
+ NEIGHBOR_ADDR_STR2
+ "Originate default route to this neighbor\n"
+ "Route-map to specify criteria to originate default\n"
+ "route-map name\n")
+{
+ int idx_peer = 1;
+ int idx_word = 4;
+ return peer_default_originate_set_vty(
+ vty, argv[idx_peer]->arg, bgp_node_afi(vty), bgp_node_safi(vty),
+ argv[idx_word]->arg, 1);
+}
+
+ALIAS_HIDDEN(
+ neighbor_default_originate_rmap,
+ neighbor_default_originate_rmap_hidden_cmd,
+ "neighbor <A.B.C.D|X:X::X:X|WORD> default-originate route-map RMAP_NAME",
+ NEIGHBOR_STR NEIGHBOR_ADDR_STR2
+ "Originate default route to this neighbor\n"
+ "Route-map to specify criteria to originate default\n"
+ "route-map name\n")
+
+DEFUN (no_neighbor_default_originate,
+ no_neighbor_default_originate_cmd,
+ "no neighbor <A.B.C.D|X:X::X:X|WORD> default-originate [route-map RMAP_NAME]",
+ NO_STR
+ NEIGHBOR_STR
+ NEIGHBOR_ADDR_STR2
+ "Originate default route to this neighbor\n"
+ "Route-map to specify criteria to originate default\n"
+ "route-map name\n")
+{
+ int idx_peer = 2;
+ return peer_default_originate_set_vty(vty, argv[idx_peer]->arg,
+ bgp_node_afi(vty),
+ bgp_node_safi(vty), NULL, 0);
+}
+
+ALIAS_HIDDEN(
+ no_neighbor_default_originate, no_neighbor_default_originate_hidden_cmd,
+ "no neighbor <A.B.C.D|X:X::X:X|WORD> default-originate [route-map RMAP_NAME]",
+ NO_STR NEIGHBOR_STR NEIGHBOR_ADDR_STR2
+ "Originate default route to this neighbor\n"
+ "Route-map to specify criteria to originate default\n"
+ "route-map name\n")
+
+
+/* Set neighbor's BGP port. */
+static int peer_port_vty(struct vty *vty, const char *ip_str, int afi,
+ const char *port_str)
+{
+ struct peer *peer;
+ uint16_t port;
+ struct servent *sp;
+
+ peer = peer_and_group_lookup_vty(vty, ip_str);
+ if (!peer)
+ return CMD_WARNING_CONFIG_FAILED;
+
+ if (!port_str) {
+ sp = getservbyname("bgp", "tcp");
+ port = (sp == NULL) ? BGP_PORT_DEFAULT : ntohs(sp->s_port);
+ } else {
+ port = strtoul(port_str, NULL, 10);
+ }
+
+ peer_port_set(peer, port);
+
+ return CMD_SUCCESS;
+}
+
+/* Set specified peer's BGP port. */
+DEFUN (neighbor_port,
+ neighbor_port_cmd,
+ "neighbor <A.B.C.D|X:X::X:X|WORD> port (0-65535)",
+ NEIGHBOR_STR
+ NEIGHBOR_ADDR_STR2
+ "Neighbor's BGP port\n"
+ "TCP port number\n")
+{
+ int idx_ip = 1;
+ int idx_number = 3;
+ return peer_port_vty(vty, argv[idx_ip]->arg, AFI_IP,
+ argv[idx_number]->arg);
+}
+
+DEFUN (no_neighbor_port,
+ no_neighbor_port_cmd,
+ "no neighbor <A.B.C.D|X:X::X:X|WORD> port [(0-65535)]",
+ NO_STR
+ NEIGHBOR_STR
+ NEIGHBOR_ADDR_STR2
+ "Neighbor's BGP port\n"
+ "TCP port number\n")
+{
+ int idx_ip = 2;
+ return peer_port_vty(vty, argv[idx_ip]->arg, AFI_IP, NULL);
+}
+
+
+/* neighbor weight. */
+static int peer_weight_set_vty(struct vty *vty, const char *ip_str, afi_t afi,
+ safi_t safi, const char *weight_str)
+{
+ int ret;
+ struct peer *peer;
+ unsigned long weight;
+
+ peer = peer_and_group_lookup_vty(vty, ip_str);
+ if (!peer)
+ return CMD_WARNING_CONFIG_FAILED;
+
+ weight = strtoul(weight_str, NULL, 10);
+
+ ret = peer_weight_set(peer, afi, safi, weight);
+ return bgp_vty_return(vty, ret);
+}
+
+static int peer_weight_unset_vty(struct vty *vty, const char *ip_str, afi_t afi,
+ safi_t safi)
+{
+ int ret;
+ struct peer *peer;
+
+ peer = peer_and_group_lookup_vty(vty, ip_str);
+ if (!peer)
+ return CMD_WARNING_CONFIG_FAILED;
+
+ ret = peer_weight_unset(peer, afi, safi);
+ return bgp_vty_return(vty, ret);
+}
+
+DEFUN (neighbor_weight,
+ neighbor_weight_cmd,
+ "neighbor <A.B.C.D|X:X::X:X|WORD> weight (0-65535)",
+ NEIGHBOR_STR
+ NEIGHBOR_ADDR_STR2
+ "Set default weight for routes from this neighbor\n"
+ "default weight\n")
+{
+ int idx_peer = 1;
+ int idx_number = 3;
+ return peer_weight_set_vty(vty, argv[idx_peer]->arg, bgp_node_afi(vty),
+ bgp_node_safi(vty), argv[idx_number]->arg);
+}
+
+ALIAS_HIDDEN(neighbor_weight, neighbor_weight_hidden_cmd,
+ "neighbor <A.B.C.D|X:X::X:X|WORD> weight (0-65535)",
+ NEIGHBOR_STR NEIGHBOR_ADDR_STR2
+ "Set default weight for routes from this neighbor\n"
+ "default weight\n")
+
+DEFUN (no_neighbor_weight,
+ no_neighbor_weight_cmd,
+ "no neighbor <A.B.C.D|X:X::X:X|WORD> weight [(0-65535)]",
+ NO_STR
+ NEIGHBOR_STR
+ NEIGHBOR_ADDR_STR2
+ "Set default weight for routes from this neighbor\n"
+ "default weight\n")
+{
+ int idx_peer = 2;
+ return peer_weight_unset_vty(vty, argv[idx_peer]->arg,
+ bgp_node_afi(vty), bgp_node_safi(vty));
+}
+
+ALIAS_HIDDEN(no_neighbor_weight, no_neighbor_weight_hidden_cmd,
+ "no neighbor <A.B.C.D|X:X::X:X|WORD> weight [(0-65535)]",
+ NO_STR NEIGHBOR_STR NEIGHBOR_ADDR_STR2
+ "Set default weight for routes from this neighbor\n"
+ "default weight\n")
+
+
+/* Override capability negotiation. */
+DEFUN (neighbor_override_capability,
+ neighbor_override_capability_cmd,
+ "neighbor <A.B.C.D|X:X::X:X|WORD> override-capability",
+ NEIGHBOR_STR
+ NEIGHBOR_ADDR_STR2
+ "Override capability negotiation result\n")
+{
+ int idx_peer = 1;
+ return peer_flag_set_vty(vty, argv[idx_peer]->arg,
+ PEER_FLAG_OVERRIDE_CAPABILITY);
+}
+
+DEFUN (no_neighbor_override_capability,
+ no_neighbor_override_capability_cmd,
+ "no neighbor <A.B.C.D|X:X::X:X|WORD> override-capability",
+ NO_STR
+ NEIGHBOR_STR
+ NEIGHBOR_ADDR_STR2
+ "Override capability negotiation result\n")
+{
+ int idx_peer = 2;
+ return peer_flag_unset_vty(vty, argv[idx_peer]->arg,
+ PEER_FLAG_OVERRIDE_CAPABILITY);
+}
+
+DEFUN (neighbor_strict_capability,
+ neighbor_strict_capability_cmd,
+ "neighbor <A.B.C.D|X:X::X:X|WORD> strict-capability-match",
+ NEIGHBOR_STR
+ NEIGHBOR_ADDR_STR2
+ "Strict capability negotiation match\n")
+{
+ int idx_peer = 1;
+
+ return peer_flag_set_vty(vty, argv[idx_peer]->arg,
+ PEER_FLAG_STRICT_CAP_MATCH);
+}
+
+DEFUN (no_neighbor_strict_capability,
+ no_neighbor_strict_capability_cmd,
+ "no neighbor <A.B.C.D|X:X::X:X|WORD> strict-capability-match",
+ NO_STR
+ NEIGHBOR_STR
+ NEIGHBOR_ADDR_STR2
+ "Strict capability negotiation match\n")
+{
+ int idx_peer = 2;
+
+ return peer_flag_unset_vty(vty, argv[idx_peer]->arg,
+ PEER_FLAG_STRICT_CAP_MATCH);
+}
+
+static int peer_timers_set_vty(struct vty *vty, const char *ip_str,
+ const char *keep_str, const char *hold_str)
+{
+ int ret;
+ struct peer *peer;
+ uint32_t keepalive;
+ uint32_t holdtime;
+
+ peer = peer_and_group_lookup_vty(vty, ip_str);
+ if (!peer)
+ return CMD_WARNING_CONFIG_FAILED;
+
+ keepalive = strtoul(keep_str, NULL, 10);
+ holdtime = strtoul(hold_str, NULL, 10);
+
+ ret = peer_timers_set(peer, keepalive, holdtime);
+
+ return bgp_vty_return(vty, ret);
+}
+
+static int peer_timers_unset_vty(struct vty *vty, const char *ip_str)
+{
+ int ret;
+ struct peer *peer;
+
+ peer = peer_and_group_lookup_vty(vty, ip_str);
+ if (!peer)
+ return CMD_WARNING_CONFIG_FAILED;
+
+ ret = peer_timers_unset(peer);
+
+ return bgp_vty_return(vty, ret);
+}
+
+DEFUN (neighbor_timers,
+ neighbor_timers_cmd,
+ "neighbor <A.B.C.D|X:X::X:X|WORD> timers (0-65535) (0-65535)",
+ NEIGHBOR_STR
+ NEIGHBOR_ADDR_STR2
+ "BGP per neighbor timers\n"
+ "Keepalive interval\n"
+ "Holdtime\n")
+{
+ int idx_peer = 1;
+ int idx_number = 3;
+ int idx_number_2 = 4;
+ return peer_timers_set_vty(vty, argv[idx_peer]->arg,
+ argv[idx_number]->arg,
+ argv[idx_number_2]->arg);
+}
+
+DEFUN (no_neighbor_timers,
+ no_neighbor_timers_cmd,
+ "no neighbor <A.B.C.D|X:X::X:X|WORD> timers [(0-65535) (0-65535)]",
+ NO_STR
+ NEIGHBOR_STR
+ NEIGHBOR_ADDR_STR2
+ "BGP per neighbor timers\n"
+ "Keepalive interval\n"
+ "Holdtime\n")
+{
+ int idx_peer = 2;
+ return peer_timers_unset_vty(vty, argv[idx_peer]->arg);
+}
+
+
+static int peer_timers_connect_set_vty(struct vty *vty, const char *ip_str,
+ const char *time_str)
+{
+ int ret;
+ struct peer *peer;
+ uint32_t connect;
+
+ peer = peer_and_group_lookup_vty(vty, ip_str);
+ if (!peer)
+ return CMD_WARNING_CONFIG_FAILED;
+
+ connect = strtoul(time_str, NULL, 10);
+
+ ret = peer_timers_connect_set(peer, connect);
+
+ return bgp_vty_return(vty, ret);
+}
+
+static int peer_timers_connect_unset_vty(struct vty *vty, const char *ip_str)
+{
+ int ret;
+ struct peer *peer;
+
+ peer = peer_and_group_lookup_vty(vty, ip_str);
+ if (!peer)
+ return CMD_WARNING_CONFIG_FAILED;
+
+ ret = peer_timers_connect_unset(peer);
+
+ return bgp_vty_return(vty, ret);
+}
+
+DEFUN (neighbor_timers_connect,
+ neighbor_timers_connect_cmd,
+ "neighbor <A.B.C.D|X:X::X:X|WORD> timers connect (1-65535)",
+ NEIGHBOR_STR
+ NEIGHBOR_ADDR_STR2
+ "BGP per neighbor timers\n"
+ "BGP connect timer\n"
+ "Connect timer\n")
+{
+ int idx_peer = 1;
+ int idx_number = 4;
+ return peer_timers_connect_set_vty(vty, argv[idx_peer]->arg,
+ argv[idx_number]->arg);
+}
+
+DEFUN (no_neighbor_timers_connect,
+ no_neighbor_timers_connect_cmd,
+ "no neighbor <A.B.C.D|X:X::X:X|WORD> timers connect [(1-65535)]",
+ NO_STR
+ NEIGHBOR_STR
+ NEIGHBOR_ADDR_STR2
+ "BGP per neighbor timers\n"
+ "BGP connect timer\n"
+ "Connect timer\n")
+{
+ int idx_peer = 2;
+ return peer_timers_connect_unset_vty(vty, argv[idx_peer]->arg);
+}
+
+DEFPY (neighbor_timers_delayopen,
+ neighbor_timers_delayopen_cmd,
+ "neighbor <A.B.C.D|X:X::X:X|WORD>$neighbor timers delayopen (1-240)$interval",
+ NEIGHBOR_STR
+ NEIGHBOR_ADDR_STR2
+ "BGP per neighbor timers\n"
+ "RFC 4271 DelayOpenTimer\n"
+ "DelayOpenTime timer interval\n")
+{
+ struct peer *peer;
+
+ peer = peer_and_group_lookup_vty(vty, neighbor);
+ if (!peer)
+ return CMD_WARNING_CONFIG_FAILED;
+
+ if (!interval) {
+ if (peer_timers_delayopen_unset(peer))
+ return CMD_WARNING_CONFIG_FAILED;
+ } else {
+ if (peer_timers_delayopen_set(peer, interval))
+ return CMD_WARNING_CONFIG_FAILED;
+ }
+
+ return CMD_SUCCESS;
+}
+
+DEFPY (no_neighbor_timers_delayopen,
+ no_neighbor_timers_delayopen_cmd,
+ "no neighbor <A.B.C.D|X:X::X:X|WORD>$neighbor timers delayopen [(0-65535)]",
+ NO_STR
+ NEIGHBOR_STR
+ NEIGHBOR_ADDR_STR2
+ "BGP per neighbor timers\n"
+ "RFC 4271 DelayOpenTimer\n"
+ "DelayOpenTime timer interval\n")
+{
+ struct peer *peer;
+
+ peer = peer_and_group_lookup_vty(vty, neighbor);
+ if (!peer)
+ return CMD_WARNING_CONFIG_FAILED;
+
+ if (peer_timers_delayopen_unset(peer))
+ return CMD_WARNING_CONFIG_FAILED;
+
+ return CMD_SUCCESS;
+}
+
+static int peer_advertise_interval_vty(struct vty *vty, const char *ip_str,
+ const char *time_str, int set)
+{
+ int ret;
+ struct peer *peer;
+ uint32_t routeadv = 0;
+
+ peer = peer_and_group_lookup_vty(vty, ip_str);
+ if (!peer)
+ return CMD_WARNING_CONFIG_FAILED;
+
+ if (time_str)
+ routeadv = strtoul(time_str, NULL, 10);
+
+ if (set)
+ ret = peer_advertise_interval_set(peer, routeadv);
+ else
+ ret = peer_advertise_interval_unset(peer);
+
+ return bgp_vty_return(vty, ret);
+}
+
+DEFUN (neighbor_advertise_interval,
+ neighbor_advertise_interval_cmd,
+ "neighbor <A.B.C.D|X:X::X:X|WORD> advertisement-interval (0-600)",
+ NEIGHBOR_STR
+ NEIGHBOR_ADDR_STR2
+ "Minimum interval between sending BGP routing updates\n"
+ "time in seconds\n")
+{
+ int idx_peer = 1;
+ int idx_number = 3;
+ return peer_advertise_interval_vty(vty, argv[idx_peer]->arg,
+ argv[idx_number]->arg, 1);
+}
+
+DEFUN (no_neighbor_advertise_interval,
+ no_neighbor_advertise_interval_cmd,
+ "no neighbor <A.B.C.D|X:X::X:X|WORD> advertisement-interval [(0-600)]",
+ NO_STR
+ NEIGHBOR_STR
+ NEIGHBOR_ADDR_STR2
+ "Minimum interval between sending BGP routing updates\n"
+ "time in seconds\n")
+{
+ int idx_peer = 2;
+ return peer_advertise_interval_vty(vty, argv[idx_peer]->arg, NULL, 0);
+}
+
+
+/* Time to wait before processing route-map updates */
+DEFUN (bgp_set_route_map_delay_timer,
+ bgp_set_route_map_delay_timer_cmd,
+ "bgp route-map delay-timer (0-600)",
+ SET_STR
+ "BGP route-map delay timer\n"
+ "Time in secs to wait before processing route-map changes\n"
+ "0 disables the timer, no route updates happen when route-maps change\n")
+{
+ int idx_number = 3;
+ uint32_t rmap_delay_timer;
+
+ if (argv[idx_number]->arg) {
+ rmap_delay_timer = strtoul(argv[idx_number]->arg, NULL, 10);
+ bm->rmap_update_timer = rmap_delay_timer;
+
+ /* if the dynamic update handling is being disabled, and a timer
+ * is
+ * running, stop the timer and act as if the timer has already
+ * fired.
+ */
+ if (!rmap_delay_timer && bm->t_rmap_update) {
+ THREAD_OFF(bm->t_rmap_update);
+ thread_execute(bm->master, bgp_route_map_update_timer,
+ NULL, 0);
+ }
+ return CMD_SUCCESS;
+ } else {
+ vty_out(vty, "%% BGP invalid route-map delay-timer\n");
+ return CMD_WARNING_CONFIG_FAILED;
+ }
+}
+
+DEFUN (no_bgp_set_route_map_delay_timer,
+ no_bgp_set_route_map_delay_timer_cmd,
+ "no bgp route-map delay-timer [(0-600)]",
+ NO_STR
+ BGP_STR
+ "Default BGP route-map delay timer\n"
+ "Reset to default time to wait for processing route-map changes\n"
+ "0 disables the timer, no route updates happen when route-maps change\n")
+{
+
+ bm->rmap_update_timer = RMAP_DEFAULT_UPDATE_TIMER;
+
+ return CMD_SUCCESS;
+}
+
+/* neighbor interface */
+static int peer_interface_vty(struct vty *vty, const char *ip_str,
+ const char *str)
+{
+ struct peer *peer;
+
+ peer = peer_lookup_vty(vty, ip_str);
+ if (!peer || peer->conf_if) {
+ vty_out(vty, "%% BGP invalid peer %s\n", ip_str);
+ return CMD_WARNING_CONFIG_FAILED;
+ }
+
+ if (str)
+ peer_interface_set(peer, str);
+ else
+ peer_interface_unset(peer);
+
+ return CMD_SUCCESS;
+}
+
+DEFUN (neighbor_interface,
+ neighbor_interface_cmd,
+ "neighbor <A.B.C.D|X:X::X:X> interface WORD",
+ NEIGHBOR_STR
+ NEIGHBOR_ADDR_STR
+ "Interface\n"
+ "Interface name\n")
+{
+ int idx_ip = 1;
+ int idx_word = 3;
+
+ return peer_interface_vty(vty, argv[idx_ip]->arg, argv[idx_word]->arg);
+}
+
+DEFUN (no_neighbor_interface,
+ no_neighbor_interface_cmd,
+ "no neighbor <A.B.C.D|X:X::X:X> interface WORD",
+ NO_STR
+ NEIGHBOR_STR
+ NEIGHBOR_ADDR_STR
+ "Interface\n"
+ "Interface name\n")
+{
+ int idx_peer = 2;
+
+ return peer_interface_vty(vty, argv[idx_peer]->arg, NULL);
+}
+
+DEFUN (neighbor_distribute_list,
+ neighbor_distribute_list_cmd,
+ "neighbor <A.B.C.D|X:X::X:X|WORD> distribute-list ACCESSLIST_NAME <in|out>",
+ NEIGHBOR_STR
+ NEIGHBOR_ADDR_STR2
+ "Filter updates to/from this neighbor\n"
+ "IP Access-list name\n"
+ "Filter incoming updates\n"
+ "Filter outgoing updates\n")
+{
+ int idx_peer = 1;
+ int idx_acl = 3;
+ int direct, ret;
+ struct peer *peer;
+
+ const char *pstr = argv[idx_peer]->arg;
+ const char *acl = argv[idx_acl]->arg;
+ const char *inout = argv[argc - 1]->text;
+
+ peer = peer_and_group_lookup_vty(vty, pstr);
+ if (!peer)
+ return CMD_WARNING_CONFIG_FAILED;
+
+ /* Check filter direction. */
+ direct = strmatch(inout, "in") ? FILTER_IN : FILTER_OUT;
+ ret = peer_distribute_set(peer, bgp_node_afi(vty), bgp_node_safi(vty),
+ direct, acl);
+
+ return bgp_vty_return(vty, ret);
+}
+
+ALIAS_HIDDEN(
+ neighbor_distribute_list, neighbor_distribute_list_hidden_cmd,
+ "neighbor <A.B.C.D|X:X::X:X|WORD> distribute-list ACCESSLIST_NAME <in|out>",
+ NEIGHBOR_STR NEIGHBOR_ADDR_STR2
+ "Filter updates to/from this neighbor\n"
+ "IP Access-list name\n"
+ "Filter incoming updates\n"
+ "Filter outgoing updates\n")
+
+DEFUN (no_neighbor_distribute_list,
+ no_neighbor_distribute_list_cmd,
+ "no neighbor <A.B.C.D|X:X::X:X|WORD> distribute-list ACCESSLIST_NAME <in|out>",
+ NO_STR
+ NEIGHBOR_STR
+ NEIGHBOR_ADDR_STR2
+ "Filter updates to/from this neighbor\n"
+ "IP Access-list name\n"
+ "Filter incoming updates\n"
+ "Filter outgoing updates\n")
+{
+ int idx_peer = 2;
+ int direct, ret;
+ struct peer *peer;
+
+ const char *pstr = argv[idx_peer]->arg;
+ const char *inout = argv[argc - 1]->text;
+
+ peer = peer_and_group_lookup_vty(vty, pstr);
+ if (!peer)
+ return CMD_WARNING_CONFIG_FAILED;
+
+ /* Check filter direction. */
+ direct = strmatch(inout, "in") ? FILTER_IN : FILTER_OUT;
+ ret = peer_distribute_unset(peer, bgp_node_afi(vty), bgp_node_safi(vty),
+ direct);
+
+ return bgp_vty_return(vty, ret);
+}
+
+ALIAS_HIDDEN(
+ no_neighbor_distribute_list, no_neighbor_distribute_list_hidden_cmd,
+ "no neighbor <A.B.C.D|X:X::X:X|WORD> distribute-list ACCESSLIST_NAME <in|out>",
+ NO_STR NEIGHBOR_STR NEIGHBOR_ADDR_STR2
+ "Filter updates to/from this neighbor\n"
+ "IP Access-list name\n"
+ "Filter incoming updates\n"
+ "Filter outgoing updates\n")
+
+/* Set prefix list to the peer. */
+static int peer_prefix_list_set_vty(struct vty *vty, const char *ip_str,
+ afi_t afi, safi_t safi,
+ const char *name_str,
+ const char *direct_str)
+{
+ int ret;
+ int direct = FILTER_IN;
+ struct peer *peer;
+
+ peer = peer_and_group_lookup_vty(vty, ip_str);
+ if (!peer)
+ return CMD_WARNING_CONFIG_FAILED;
+
+ /* Check filter direction. */
+ if (strncmp(direct_str, "i", 1) == 0)
+ direct = FILTER_IN;
+ else if (strncmp(direct_str, "o", 1) == 0)
+ direct = FILTER_OUT;
+
+ ret = peer_prefix_list_set(peer, afi, safi, direct, name_str);
+
+ return bgp_vty_return(vty, ret);
+}
+
+static int peer_prefix_list_unset_vty(struct vty *vty, const char *ip_str,
+ afi_t afi, safi_t safi,
+ const char *direct_str)
+{
+ int ret;
+ struct peer *peer;
+ int direct = FILTER_IN;
+
+ peer = peer_and_group_lookup_vty(vty, ip_str);
+ if (!peer)
+ return CMD_WARNING_CONFIG_FAILED;
+
+ /* Check filter direction. */
+ if (strncmp(direct_str, "i", 1) == 0)
+ direct = FILTER_IN;
+ else if (strncmp(direct_str, "o", 1) == 0)
+ direct = FILTER_OUT;
+
+ ret = peer_prefix_list_unset(peer, afi, safi, direct);
+
+ return bgp_vty_return(vty, ret);
+}
+
+DEFUN (neighbor_prefix_list,
+ neighbor_prefix_list_cmd,
+ "neighbor <A.B.C.D|X:X::X:X|WORD> prefix-list WORD <in|out>",
+ NEIGHBOR_STR
+ NEIGHBOR_ADDR_STR2
+ "Filter updates to/from this neighbor\n"
+ "Name of a prefix list\n"
+ "Filter incoming updates\n"
+ "Filter outgoing updates\n")
+{
+ int idx_peer = 1;
+ int idx_word = 3;
+ int idx_in_out = 4;
+ return peer_prefix_list_set_vty(
+ vty, argv[idx_peer]->arg, bgp_node_afi(vty), bgp_node_safi(vty),
+ argv[idx_word]->arg, argv[idx_in_out]->arg);
+}
+
+ALIAS_HIDDEN(neighbor_prefix_list, neighbor_prefix_list_hidden_cmd,
+ "neighbor <A.B.C.D|X:X::X:X|WORD> prefix-list WORD <in|out>",
+ NEIGHBOR_STR NEIGHBOR_ADDR_STR2
+ "Filter updates to/from this neighbor\n"
+ "Name of a prefix list\n"
+ "Filter incoming updates\n"
+ "Filter outgoing updates\n")
+
+DEFUN (no_neighbor_prefix_list,
+ no_neighbor_prefix_list_cmd,
+ "no neighbor <A.B.C.D|X:X::X:X|WORD> prefix-list WORD <in|out>",
+ NO_STR
+ NEIGHBOR_STR
+ NEIGHBOR_ADDR_STR2
+ "Filter updates to/from this neighbor\n"
+ "Name of a prefix list\n"
+ "Filter incoming updates\n"
+ "Filter outgoing updates\n")
+{
+ int idx_peer = 2;
+ int idx_in_out = 5;
+ return peer_prefix_list_unset_vty(vty, argv[idx_peer]->arg,
+ bgp_node_afi(vty), bgp_node_safi(vty),
+ argv[idx_in_out]->arg);
+}
+
+ALIAS_HIDDEN(no_neighbor_prefix_list, no_neighbor_prefix_list_hidden_cmd,
+ "no neighbor <A.B.C.D|X:X::X:X|WORD> prefix-list WORD <in|out>",
+ NO_STR NEIGHBOR_STR NEIGHBOR_ADDR_STR2
+ "Filter updates to/from this neighbor\n"
+ "Name of a prefix list\n"
+ "Filter incoming updates\n"
+ "Filter outgoing updates\n")
+
+static int peer_aslist_set_vty(struct vty *vty, const char *ip_str, afi_t afi,
+ safi_t safi, const char *name_str,
+ const char *direct_str)
+{
+ int ret;
+ struct peer *peer;
+ int direct = FILTER_IN;
+
+ peer = peer_and_group_lookup_vty(vty, ip_str);
+ if (!peer)
+ return CMD_WARNING_CONFIG_FAILED;
+
+ /* Check filter direction. */
+ if (strncmp(direct_str, "i", 1) == 0)
+ direct = FILTER_IN;
+ else if (strncmp(direct_str, "o", 1) == 0)
+ direct = FILTER_OUT;
+
+ ret = peer_aslist_set(peer, afi, safi, direct, name_str);
+
+ return bgp_vty_return(vty, ret);
+}
+
+static int peer_aslist_unset_vty(struct vty *vty, const char *ip_str, afi_t afi,
+ safi_t safi, const char *direct_str)
+{
+ int ret;
+ struct peer *peer;
+ int direct = FILTER_IN;
+
+ peer = peer_and_group_lookup_vty(vty, ip_str);
+ if (!peer)
+ return CMD_WARNING_CONFIG_FAILED;
+
+ /* Check filter direction. */
+ if (strncmp(direct_str, "i", 1) == 0)
+ direct = FILTER_IN;
+ else if (strncmp(direct_str, "o", 1) == 0)
+ direct = FILTER_OUT;
+
+ ret = peer_aslist_unset(peer, afi, safi, direct);
+
+ return bgp_vty_return(vty, ret);
+}
+
+DEFUN (neighbor_filter_list,
+ neighbor_filter_list_cmd,
+ "neighbor <A.B.C.D|X:X::X:X|WORD> filter-list AS_PATH_FILTER_NAME <in|out>",
+ NEIGHBOR_STR
+ NEIGHBOR_ADDR_STR2
+ "Establish BGP filters\n"
+ "AS path access-list name\n"
+ "Filter incoming routes\n"
+ "Filter outgoing routes\n")
+{
+ int idx_peer = 1;
+ int idx_word = 3;
+ int idx_in_out = 4;
+ return peer_aslist_set_vty(vty, argv[idx_peer]->arg, bgp_node_afi(vty),
+ bgp_node_safi(vty), argv[idx_word]->arg,
+ argv[idx_in_out]->arg);
+}
+
+ALIAS_HIDDEN(neighbor_filter_list, neighbor_filter_list_hidden_cmd,
+ "neighbor <A.B.C.D|X:X::X:X|WORD> filter-list AS_PATH_FILTER_NAME <in|out>",
+ NEIGHBOR_STR NEIGHBOR_ADDR_STR2
+ "Establish BGP filters\n"
+ "AS path access-list name\n"
+ "Filter incoming routes\n"
+ "Filter outgoing routes\n")
+
+DEFUN (no_neighbor_filter_list,
+ no_neighbor_filter_list_cmd,
+ "no neighbor <A.B.C.D|X:X::X:X|WORD> filter-list AS_PATH_FILTER_NAME <in|out>",
+ NO_STR
+ NEIGHBOR_STR
+ NEIGHBOR_ADDR_STR2
+ "Establish BGP filters\n"
+ "AS path access-list name\n"
+ "Filter incoming routes\n"
+ "Filter outgoing routes\n")
+{
+ int idx_peer = 2;
+ int idx_in_out = 5;
+ return peer_aslist_unset_vty(vty, argv[idx_peer]->arg,
+ bgp_node_afi(vty), bgp_node_safi(vty),
+ argv[idx_in_out]->arg);
+}
+
+ALIAS_HIDDEN(no_neighbor_filter_list, no_neighbor_filter_list_hidden_cmd,
+ "no neighbor <A.B.C.D|X:X::X:X|WORD> filter-list AS_PATH_FILTER_NAME <in|out>",
+ NO_STR NEIGHBOR_STR NEIGHBOR_ADDR_STR2
+ "Establish BGP filters\n"
+ "AS path access-list name\n"
+ "Filter incoming routes\n"
+ "Filter outgoing routes\n")
+
+/* Set advertise-map to the peer. */
+static int peer_advertise_map_set_vty(struct vty *vty, const char *ip_str,
+ afi_t afi, safi_t safi,
+ const char *advertise_str,
+ const char *condition_str, bool condition,
+ bool set)
+{
+ int ret = CMD_WARNING_CONFIG_FAILED;
+ struct peer *peer;
+ struct route_map *advertise_map;
+ struct route_map *condition_map;
+
+ peer = peer_and_group_lookup_vty(vty, ip_str);
+ if (!peer)
+ return ret;
+
+ condition_map = route_map_lookup_warn_noexist(vty, condition_str);
+ advertise_map = route_map_lookup_warn_noexist(vty, advertise_str);
+
+ if (set)
+ ret = peer_advertise_map_set(peer, afi, safi, advertise_str,
+ advertise_map, condition_str,
+ condition_map, condition);
+ else
+ ret = peer_advertise_map_unset(peer, afi, safi, advertise_str,
+ advertise_map, condition_str,
+ condition_map, condition);
+
+ return bgp_vty_return(vty, ret);
+}
+
+DEFPY (bgp_condadv_period,
+ bgp_condadv_period_cmd,
+ "[no$no] bgp conditional-advertisement timer (5-240)$period",
+ NO_STR
+ BGP_STR
+ "Conditional advertisement settings\n"
+ "Set period to rescan BGP table to check if condition is met\n"
+ "Period between BGP table scans, in seconds; default 60\n")
+{
+ VTY_DECLVAR_CONTEXT(bgp, bgp);
+
+ bgp->condition_check_period =
+ no ? DEFAULT_CONDITIONAL_ROUTES_POLL_TIME : period;
+
+ return CMD_SUCCESS;
+}
+
+DEFPY (neighbor_advertise_map,
+ neighbor_advertise_map_cmd,
+ "[no$no] neighbor <A.B.C.D|X:X::X:X|WORD>$neighbor advertise-map RMAP_NAME$advertise_str <exist-map|non-exist-map>$exist RMAP_NAME$condition_str",
+ NO_STR
+ NEIGHBOR_STR
+ NEIGHBOR_ADDR_STR2
+ "Route-map to conditionally advertise routes\n"
+ "Name of advertise map\n"
+ "Advertise routes only if prefixes in exist-map are installed in BGP table\n"
+ "Advertise routes only if prefixes in non-exist-map are not installed in BGP table\n"
+ "Name of the exist or non exist map\n")
+{
+ bool condition = CONDITION_EXIST;
+
+ if (!strcmp(exist, "non-exist-map"))
+ condition = CONDITION_NON_EXIST;
+
+ return peer_advertise_map_set_vty(vty, neighbor, bgp_node_afi(vty),
+ bgp_node_safi(vty), advertise_str,
+ condition_str, condition, !no);
+}
+
+ALIAS_HIDDEN(neighbor_advertise_map, neighbor_advertise_map_hidden_cmd,
+ "[no$no] neighbor <A.B.C.D|X:X::X:X|WORD>$neighbor advertise-map RMAP_NAME$advertise_str <exist-map|non-exist-map>$exist RMAP_NAME$condition_str",
+ NO_STR NEIGHBOR_STR NEIGHBOR_ADDR_STR2
+ "Route-map to conditionally advertise routes\n"
+ "Name of advertise map\n"
+ "Advertise routes only if prefixes in exist-map are installed in BGP table\n"
+ "Advertise routes only if prefixes in non-exist-map are not installed in BGP table\n"
+ "Name of the exist or non exist map\n")
+
+/* Set route-map to the peer. */
+static int peer_route_map_set_vty(struct vty *vty, const char *ip_str,
+ afi_t afi, safi_t safi, const char *name_str,
+ const char *direct_str)
+{
+ int ret;
+ struct peer *peer;
+ int direct = RMAP_IN;
+ struct route_map *route_map;
+
+ peer = peer_and_group_lookup_vty(vty, ip_str);
+ if (!peer)
+ return CMD_WARNING_CONFIG_FAILED;
+
+ /* Check filter direction. */
+ if (strncmp(direct_str, "in", 2) == 0)
+ direct = RMAP_IN;
+ else if (strncmp(direct_str, "o", 1) == 0)
+ direct = RMAP_OUT;
+
+ route_map = route_map_lookup_warn_noexist(vty, name_str);
+ ret = peer_route_map_set(peer, afi, safi, direct, name_str, route_map);
+
+ return bgp_vty_return(vty, ret);
+}
+
+static int peer_route_map_unset_vty(struct vty *vty, const char *ip_str,
+ afi_t afi, safi_t safi,
+ const char *direct_str)
+{
+ int ret;
+ struct peer *peer;
+ int direct = RMAP_IN;
+
+ peer = peer_and_group_lookup_vty(vty, ip_str);
+ if (!peer)
+ return CMD_WARNING_CONFIG_FAILED;
+
+ /* Check filter direction. */
+ if (strncmp(direct_str, "in", 2) == 0)
+ direct = RMAP_IN;
+ else if (strncmp(direct_str, "o", 1) == 0)
+ direct = RMAP_OUT;
+
+ ret = peer_route_map_unset(peer, afi, safi, direct);
+
+ return bgp_vty_return(vty, ret);
+}
+
+DEFUN (neighbor_route_map,
+ neighbor_route_map_cmd,
+ "neighbor <A.B.C.D|X:X::X:X|WORD> route-map RMAP_NAME <in|out>",
+ NEIGHBOR_STR
+ NEIGHBOR_ADDR_STR2
+ "Apply route map to neighbor\n"
+ "Name of route map\n"
+ "Apply map to incoming routes\n"
+ "Apply map to outbound routes\n")
+{
+ int idx_peer = 1;
+ int idx_word = 3;
+ int idx_in_out = 4;
+ return peer_route_map_set_vty(
+ vty, argv[idx_peer]->arg, bgp_node_afi(vty), bgp_node_safi(vty),
+ argv[idx_word]->arg, argv[idx_in_out]->arg);
+}
+
+ALIAS_HIDDEN(neighbor_route_map, neighbor_route_map_hidden_cmd,
+ "neighbor <A.B.C.D|X:X::X:X|WORD> route-map RMAP_NAME <in|out>",
+ NEIGHBOR_STR NEIGHBOR_ADDR_STR2
+ "Apply route map to neighbor\n"
+ "Name of route map\n"
+ "Apply map to incoming routes\n"
+ "Apply map to outbound routes\n")
+
+DEFUN (no_neighbor_route_map,
+ no_neighbor_route_map_cmd,
+ "no neighbor <A.B.C.D|X:X::X:X|WORD> route-map RMAP_NAME <in|out>",
+ NO_STR
+ NEIGHBOR_STR
+ NEIGHBOR_ADDR_STR2
+ "Apply route map to neighbor\n"
+ "Name of route map\n"
+ "Apply map to incoming routes\n"
+ "Apply map to outbound routes\n")
+{
+ int idx_peer = 2;
+ int idx_in_out = 5;
+ return peer_route_map_unset_vty(vty, argv[idx_peer]->arg,
+ bgp_node_afi(vty), bgp_node_safi(vty),
+ argv[idx_in_out]->arg);
+}
+
+ALIAS_HIDDEN(no_neighbor_route_map, no_neighbor_route_map_hidden_cmd,
+ "no neighbor <A.B.C.D|X:X::X:X|WORD> route-map RMAP_NAME <in|out>",
+ NO_STR NEIGHBOR_STR NEIGHBOR_ADDR_STR2
+ "Apply route map to neighbor\n"
+ "Name of route map\n"
+ "Apply map to incoming routes\n"
+ "Apply map to outbound routes\n")
+
+/* Set unsuppress-map to the peer. */
+static int peer_unsuppress_map_set_vty(struct vty *vty, const char *ip_str,
+ afi_t afi, safi_t safi,
+ const char *name_str)
+{
+ int ret;
+ struct peer *peer;
+ struct route_map *route_map;
+
+ peer = peer_and_group_lookup_vty(vty, ip_str);
+ if (!peer)
+ return CMD_WARNING_CONFIG_FAILED;
+
+ route_map = route_map_lookup_warn_noexist(vty, name_str);
+ ret = peer_unsuppress_map_set(peer, afi, safi, name_str, route_map);
+
+ return bgp_vty_return(vty, ret);
+}
+
+/* Unset route-map from the peer. */
+static int peer_unsuppress_map_unset_vty(struct vty *vty, const char *ip_str,
+ afi_t afi, safi_t safi)
+{
+ int ret;
+ struct peer *peer;
+
+ peer = peer_and_group_lookup_vty(vty, ip_str);
+ if (!peer)
+ return CMD_WARNING_CONFIG_FAILED;
+
+ ret = peer_unsuppress_map_unset(peer, afi, safi);
+
+ return bgp_vty_return(vty, ret);
+}
+
+DEFUN (neighbor_unsuppress_map,
+ neighbor_unsuppress_map_cmd,
+ "neighbor <A.B.C.D|X:X::X:X|WORD> unsuppress-map WORD",
+ NEIGHBOR_STR
+ NEIGHBOR_ADDR_STR2
+ "Route-map to selectively unsuppress suppressed routes\n"
+ "Name of route map\n")
+{
+ int idx_peer = 1;
+ int idx_word = 3;
+ return peer_unsuppress_map_set_vty(
+ vty, argv[idx_peer]->arg, bgp_node_afi(vty), bgp_node_safi(vty),
+ argv[idx_word]->arg);
+}
+
+ALIAS_HIDDEN(neighbor_unsuppress_map, neighbor_unsuppress_map_hidden_cmd,
+ "neighbor <A.B.C.D|X:X::X:X|WORD> unsuppress-map WORD",
+ NEIGHBOR_STR NEIGHBOR_ADDR_STR2
+ "Route-map to selectively unsuppress suppressed routes\n"
+ "Name of route map\n")
+
+DEFUN (no_neighbor_unsuppress_map,
+ no_neighbor_unsuppress_map_cmd,
+ "no neighbor <A.B.C.D|X:X::X:X|WORD> unsuppress-map WORD",
+ NO_STR
+ NEIGHBOR_STR
+ NEIGHBOR_ADDR_STR2
+ "Route-map to selectively unsuppress suppressed routes\n"
+ "Name of route map\n")
+{
+ int idx_peer = 2;
+ return peer_unsuppress_map_unset_vty(vty, argv[idx_peer]->arg,
+ bgp_node_afi(vty),
+ bgp_node_safi(vty));
+}
+
+ALIAS_HIDDEN(no_neighbor_unsuppress_map, no_neighbor_unsuppress_map_hidden_cmd,
+ "no neighbor <A.B.C.D|X:X::X:X|WORD> unsuppress-map WORD",
+ NO_STR NEIGHBOR_STR NEIGHBOR_ADDR_STR2
+ "Route-map to selectively unsuppress suppressed routes\n"
+ "Name of route map\n")
+
+static int peer_maximum_prefix_set_vty(struct vty *vty, const char *ip_str,
+ afi_t afi, safi_t safi,
+ const char *num_str,
+ const char *threshold_str, int warning,
+ const char *restart_str,
+ const char *force_str)
+{
+ int ret;
+ struct peer *peer;
+ uint32_t max;
+ uint8_t threshold;
+ uint16_t restart;
+
+ peer = peer_and_group_lookup_vty(vty, ip_str);
+ if (!peer)
+ return CMD_WARNING_CONFIG_FAILED;
+
+ max = strtoul(num_str, NULL, 10);
+ if (threshold_str)
+ threshold = atoi(threshold_str);
+ else
+ threshold = MAXIMUM_PREFIX_THRESHOLD_DEFAULT;
+
+ if (restart_str)
+ restart = atoi(restart_str);
+ else
+ restart = 0;
+
+ ret = peer_maximum_prefix_set(peer, afi, safi, max, threshold, warning,
+ restart, force_str ? true : false);
+
+ return bgp_vty_return(vty, ret);
+}
+
+static int peer_maximum_prefix_unset_vty(struct vty *vty, const char *ip_str,
+ afi_t afi, safi_t safi)
+{
+ int ret;
+ struct peer *peer;
+
+ peer = peer_and_group_lookup_vty(vty, ip_str);
+ if (!peer)
+ return CMD_WARNING_CONFIG_FAILED;
+
+ ret = peer_maximum_prefix_unset(peer, afi, safi);
+
+ return bgp_vty_return(vty, ret);
+}
+
+/* Maximum number of prefix to be sent to the neighbor. */
+DEFUN(neighbor_maximum_prefix_out,
+ neighbor_maximum_prefix_out_cmd,
+ "neighbor <A.B.C.D|X:X::X:X|WORD> maximum-prefix-out (1-4294967295)",
+ NEIGHBOR_STR
+ NEIGHBOR_ADDR_STR2
+ "Maximum number of prefixes to be sent to this peer\n"
+ "Maximum no. of prefix limit\n")
+{
+ int ret;
+ int idx_peer = 1;
+ int idx_number = 3;
+ struct peer *peer;
+ uint32_t max;
+ afi_t afi = bgp_node_afi(vty);
+ safi_t safi = bgp_node_safi(vty);
+
+ peer = peer_and_group_lookup_vty(vty, argv[idx_peer]->arg);
+ if (!peer)
+ return CMD_WARNING_CONFIG_FAILED;
+
+ max = strtoul(argv[idx_number]->arg, NULL, 10);
+
+ ret = peer_maximum_prefix_out_set(peer, afi, safi, max);
+
+ return bgp_vty_return(vty, ret);
+}
+
+DEFUN(no_neighbor_maximum_prefix_out,
+ no_neighbor_maximum_prefix_out_cmd,
+ "no neighbor <A.B.C.D|X:X::X:X|WORD> maximum-prefix-out [(1-4294967295)]",
+ NO_STR
+ NEIGHBOR_STR
+ NEIGHBOR_ADDR_STR2
+ "Maximum number of prefixes to be sent to this peer\n"
+ "Maximum no. of prefix limit\n")
+{
+ int ret;
+ int idx_peer = 2;
+ struct peer *peer;
+ afi_t afi = bgp_node_afi(vty);
+ safi_t safi = bgp_node_safi(vty);
+
+ peer = peer_and_group_lookup_vty(vty, argv[idx_peer]->arg);
+ if (!peer)
+ return CMD_WARNING_CONFIG_FAILED;
+
+ ret = peer_maximum_prefix_out_unset(peer, afi, safi);
+
+ return bgp_vty_return(vty, ret);
+}
+
+/* Maximum number of prefix configuration. Prefix count is different
+ for each peer configuration. So this configuration can be set for
+ each peer configuration. */
+DEFUN (neighbor_maximum_prefix,
+ neighbor_maximum_prefix_cmd,
+ "neighbor <A.B.C.D|X:X::X:X|WORD> maximum-prefix (1-4294967295) [force]",
+ NEIGHBOR_STR
+ NEIGHBOR_ADDR_STR2
+ "Maximum number of prefix accept from this peer\n"
+ "maximum no. of prefix limit\n"
+ "Force checking all received routes not only accepted\n")
+{
+ int idx_peer = 1;
+ int idx_number = 3;
+ int idx_force = 0;
+ char *force = NULL;
+
+ if (argv_find(argv, argc, "force", &idx_force))
+ force = argv[idx_force]->arg;
+
+ return peer_maximum_prefix_set_vty(
+ vty, argv[idx_peer]->arg, bgp_node_afi(vty), bgp_node_safi(vty),
+ argv[idx_number]->arg, NULL, 0, NULL, force);
+}
+
+ALIAS_HIDDEN(neighbor_maximum_prefix, neighbor_maximum_prefix_hidden_cmd,
+ "neighbor <A.B.C.D|X:X::X:X|WORD> maximum-prefix (1-4294967295) [force]",
+ NEIGHBOR_STR NEIGHBOR_ADDR_STR2
+ "Maximum number of prefix accept from this peer\n"
+ "maximum no. of prefix limit\n"
+ "Force checking all received routes not only accepted\n")
+
+DEFUN (neighbor_maximum_prefix_threshold,
+ neighbor_maximum_prefix_threshold_cmd,
+ "neighbor <A.B.C.D|X:X::X:X|WORD> maximum-prefix (1-4294967295) (1-100) [force]",
+ NEIGHBOR_STR
+ NEIGHBOR_ADDR_STR2
+ "Maximum number of prefix accept from this peer\n"
+ "maximum no. of prefix limit\n"
+ "Threshold value (%) at which to generate a warning msg\n"
+ "Force checking all received routes not only accepted\n")
+{
+ int idx_peer = 1;
+ int idx_number = 3;
+ int idx_number_2 = 4;
+ int idx_force = 0;
+ char *force = NULL;
+
+ if (argv_find(argv, argc, "force", &idx_force))
+ force = argv[idx_force]->arg;
+
+ return peer_maximum_prefix_set_vty(
+ vty, argv[idx_peer]->arg, bgp_node_afi(vty), bgp_node_safi(vty),
+ argv[idx_number]->arg, argv[idx_number_2]->arg, 0, NULL, force);
+}
+
+ALIAS_HIDDEN(
+ neighbor_maximum_prefix_threshold,
+ neighbor_maximum_prefix_threshold_hidden_cmd,
+ "neighbor <A.B.C.D|X:X::X:X|WORD> maximum-prefix (1-4294967295) (1-100) [force]",
+ NEIGHBOR_STR NEIGHBOR_ADDR_STR2
+ "Maximum number of prefix accept from this peer\n"
+ "maximum no. of prefix limit\n"
+ "Threshold value (%) at which to generate a warning msg\n"
+ "Force checking all received routes not only accepted\n")
+
+DEFUN (neighbor_maximum_prefix_warning,
+ neighbor_maximum_prefix_warning_cmd,
+ "neighbor <A.B.C.D|X:X::X:X|WORD> maximum-prefix (1-4294967295) warning-only [force]",
+ NEIGHBOR_STR
+ NEIGHBOR_ADDR_STR2
+ "Maximum number of prefix accept from this peer\n"
+ "maximum no. of prefix limit\n"
+ "Only give warning message when limit is exceeded\n"
+ "Force checking all received routes not only accepted\n")
+{
+ int idx_peer = 1;
+ int idx_number = 3;
+ int idx_force = 0;
+ char *force = NULL;
+
+ if (argv_find(argv, argc, "force", &idx_force))
+ force = argv[idx_force]->arg;
+
+ return peer_maximum_prefix_set_vty(
+ vty, argv[idx_peer]->arg, bgp_node_afi(vty), bgp_node_safi(vty),
+ argv[idx_number]->arg, NULL, 1, NULL, force);
+}
+
+ALIAS_HIDDEN(
+ neighbor_maximum_prefix_warning,
+ neighbor_maximum_prefix_warning_hidden_cmd,
+ "neighbor <A.B.C.D|X:X::X:X|WORD> maximum-prefix (1-4294967295) warning-only [force]",
+ NEIGHBOR_STR NEIGHBOR_ADDR_STR2
+ "Maximum number of prefix accept from this peer\n"
+ "maximum no. of prefix limit\n"
+ "Only give warning message when limit is exceeded\n"
+ "Force checking all received routes not only accepted\n")
+
+DEFUN (neighbor_maximum_prefix_threshold_warning,
+ neighbor_maximum_prefix_threshold_warning_cmd,
+ "neighbor <A.B.C.D|X:X::X:X|WORD> maximum-prefix (1-4294967295) (1-100) warning-only [force]",
+ NEIGHBOR_STR
+ NEIGHBOR_ADDR_STR2
+ "Maximum number of prefix accept from this peer\n"
+ "maximum no. of prefix limit\n"
+ "Threshold value (%) at which to generate a warning msg\n"
+ "Only give warning message when limit is exceeded\n"
+ "Force checking all received routes not only accepted\n")
+{
+ int idx_peer = 1;
+ int idx_number = 3;
+ int idx_number_2 = 4;
+ int idx_force = 0;
+ char *force = NULL;
+
+ if (argv_find(argv, argc, "force", &idx_force))
+ force = argv[idx_force]->arg;
+
+ return peer_maximum_prefix_set_vty(
+ vty, argv[idx_peer]->arg, bgp_node_afi(vty), bgp_node_safi(vty),
+ argv[idx_number]->arg, argv[idx_number_2]->arg, 1, NULL, force);
+}
+
+ALIAS_HIDDEN(
+ neighbor_maximum_prefix_threshold_warning,
+ neighbor_maximum_prefix_threshold_warning_hidden_cmd,
+ "neighbor <A.B.C.D|X:X::X:X|WORD> maximum-prefix (1-4294967295) (1-100) warning-only [force]",
+ NEIGHBOR_STR NEIGHBOR_ADDR_STR2
+ "Maximum number of prefix accept from this peer\n"
+ "maximum no. of prefix limit\n"
+ "Threshold value (%) at which to generate a warning msg\n"
+ "Only give warning message when limit is exceeded\n"
+ "Force checking all received routes not only accepted\n")
+
+DEFUN (neighbor_maximum_prefix_restart,
+ neighbor_maximum_prefix_restart_cmd,
+ "neighbor <A.B.C.D|X:X::X:X|WORD> maximum-prefix (1-4294967295) restart (1-65535) [force]",
+ NEIGHBOR_STR
+ NEIGHBOR_ADDR_STR2
+ "Maximum number of prefix accept from this peer\n"
+ "maximum no. of prefix limit\n"
+ "Restart bgp connection after limit is exceeded\n"
+ "Restart interval in minutes\n"
+ "Force checking all received routes not only accepted\n")
+{
+ int idx_peer = 1;
+ int idx_number = 3;
+ int idx_number_2 = 5;
+ int idx_force = 0;
+ char *force = NULL;
+
+ if (argv_find(argv, argc, "force", &idx_force))
+ force = argv[idx_force]->arg;
+
+ return peer_maximum_prefix_set_vty(
+ vty, argv[idx_peer]->arg, bgp_node_afi(vty), bgp_node_safi(vty),
+ argv[idx_number]->arg, NULL, 0, argv[idx_number_2]->arg, force);
+}
+
+ALIAS_HIDDEN(
+ neighbor_maximum_prefix_restart,
+ neighbor_maximum_prefix_restart_hidden_cmd,
+ "neighbor <A.B.C.D|X:X::X:X|WORD> maximum-prefix (1-4294967295) restart (1-65535) [force]",
+ NEIGHBOR_STR NEIGHBOR_ADDR_STR2
+ "Maximum number of prefix accept from this peer\n"
+ "maximum no. of prefix limit\n"
+ "Restart bgp connection after limit is exceeded\n"
+ "Restart interval in minutes\n"
+ "Force checking all received routes not only accepted\n")
+
+DEFUN (neighbor_maximum_prefix_threshold_restart,
+ neighbor_maximum_prefix_threshold_restart_cmd,
+ "neighbor <A.B.C.D|X:X::X:X|WORD> maximum-prefix (1-4294967295) (1-100) restart (1-65535) [force]",
+ NEIGHBOR_STR
+ NEIGHBOR_ADDR_STR2
+ "Maximum number of prefixes to accept from this peer\n"
+ "maximum no. of prefix limit\n"
+ "Threshold value (%) at which to generate a warning msg\n"
+ "Restart bgp connection after limit is exceeded\n"
+ "Restart interval in minutes\n"
+ "Force checking all received routes not only accepted\n")
+{
+ int idx_peer = 1;
+ int idx_number = 3;
+ int idx_number_2 = 4;
+ int idx_number_3 = 6;
+ int idx_force = 0;
+ char *force = NULL;
+
+ if (argv_find(argv, argc, "force", &idx_force))
+ force = argv[idx_force]->arg;
+
+ return peer_maximum_prefix_set_vty(
+ vty, argv[idx_peer]->arg, bgp_node_afi(vty), bgp_node_safi(vty),
+ argv[idx_number]->arg, argv[idx_number_2]->arg, 0,
+ argv[idx_number_3]->arg, force);
+}
+
+ALIAS_HIDDEN(
+ neighbor_maximum_prefix_threshold_restart,
+ neighbor_maximum_prefix_threshold_restart_hidden_cmd,
+ "neighbor <A.B.C.D|X:X::X:X|WORD> maximum-prefix (1-4294967295) (1-100) restart (1-65535) [force]",
+ NEIGHBOR_STR NEIGHBOR_ADDR_STR2
+ "Maximum number of prefixes to accept from this peer\n"
+ "maximum no. of prefix limit\n"
+ "Threshold value (%) at which to generate a warning msg\n"
+ "Restart bgp connection after limit is exceeded\n"
+ "Restart interval in minutes\n"
+ "Force checking all received routes not only accepted\n")
+
+DEFUN (no_neighbor_maximum_prefix,
+ no_neighbor_maximum_prefix_cmd,
+ "no neighbor <A.B.C.D|X:X::X:X|WORD> maximum-prefix [(1-4294967295) [(1-100)] [restart (1-65535)] [warning-only] [force]]",
+ NO_STR
+ NEIGHBOR_STR
+ NEIGHBOR_ADDR_STR2
+ "Maximum number of prefixes to accept from this peer\n"
+ "maximum no. of prefix limit\n"
+ "Threshold value (%) at which to generate a warning msg\n"
+ "Restart bgp connection after limit is exceeded\n"
+ "Restart interval in minutes\n"
+ "Only give warning message when limit is exceeded\n"
+ "Force checking all received routes not only accepted\n")
+{
+ int idx_peer = 2;
+ return peer_maximum_prefix_unset_vty(vty, argv[idx_peer]->arg,
+ bgp_node_afi(vty),
+ bgp_node_safi(vty));
+}
+
+ALIAS_HIDDEN(
+ no_neighbor_maximum_prefix, no_neighbor_maximum_prefix_hidden_cmd,
+ "no neighbor <A.B.C.D|X:X::X:X|WORD> maximum-prefix [(1-4294967295) [(1-100)] [restart (1-65535)] [warning-only] [force]]",
+ NO_STR NEIGHBOR_STR NEIGHBOR_ADDR_STR2
+ "Maximum number of prefixes to accept from this peer\n"
+ "maximum no. of prefix limit\n"
+ "Threshold value (%) at which to generate a warning msg\n"
+ "Restart bgp connection after limit is exceeded\n"
+ "Restart interval in minutes\n"
+ "Only give warning message when limit is exceeded\n"
+ "Force checking all received routes not only accepted\n")
+
+/* "neighbor soo" */
+DEFPY (neighbor_soo,
+ neighbor_soo_cmd,
+ "neighbor <A.B.C.D|X:X::X:X|WORD>$neighbor soo ASN:NN_OR_IP-ADDRESS:NN$soo",
+ NEIGHBOR_STR
+ NEIGHBOR_ADDR_STR2
+ "Set the Site-of-Origin (SoO) extended community\n"
+ "VPN extended community\n")
+{
+ struct peer *peer;
+ afi_t afi = bgp_node_afi(vty);
+ safi_t safi = bgp_node_safi(vty);
+ struct ecommunity *ecomm_soo;
+
+ peer = peer_and_group_lookup_vty(vty, neighbor);
+ if (!peer)
+ return CMD_WARNING_CONFIG_FAILED;
+
+ ecomm_soo = ecommunity_str2com(soo, ECOMMUNITY_SITE_ORIGIN, 0);
+ if (!ecomm_soo) {
+ vty_out(vty, "%% Malformed SoO extended community\n");
+ return CMD_WARNING;
+ }
+ ecommunity_str(ecomm_soo);
+
+ if (!ecommunity_match(peer->soo[afi][safi], ecomm_soo)) {
+ ecommunity_free(&peer->soo[afi][safi]);
+ peer->soo[afi][safi] = ecomm_soo;
+ peer_af_flag_unset(peer, afi, safi, PEER_FLAG_SOO);
+ }
+
+ return bgp_vty_return(vty,
+ peer_af_flag_set(peer, afi, safi, PEER_FLAG_SOO));
+}
+
+DEFPY (no_neighbor_soo,
+ no_neighbor_soo_cmd,
+ "no neighbor <A.B.C.D|X:X::X:X|WORD>$neighbor soo [ASN:NN_OR_IP-ADDRESS:NN$soo]",
+ NO_STR
+ NEIGHBOR_STR
+ NEIGHBOR_ADDR_STR2
+ "Set the Site-of-Origin (SoO) extended community\n"
+ "VPN extended community\n")
+{
+ struct peer *peer;
+ afi_t afi = bgp_node_afi(vty);
+ safi_t safi = bgp_node_safi(vty);
+
+ peer = peer_and_group_lookup_vty(vty, neighbor);
+ if (!peer)
+ return CMD_WARNING_CONFIG_FAILED;
+
+ ecommunity_free(&peer->soo[afi][safi]);
+
+ return bgp_vty_return(
+ vty, peer_af_flag_unset(peer, afi, safi, PEER_FLAG_SOO));
+}
+
+/* "neighbor allowas-in" */
+DEFUN (neighbor_allowas_in,
+ neighbor_allowas_in_cmd,
+ "neighbor <A.B.C.D|X:X::X:X|WORD> allowas-in [<(1-10)|origin>]",
+ NEIGHBOR_STR
+ NEIGHBOR_ADDR_STR2
+ "Accept as-path with my AS present in it\n"
+ "Number of occurrences of AS number\n"
+ "Only accept my AS in the as-path if the route was originated in my AS\n")
+{
+ int idx_peer = 1;
+ int idx_number_origin = 3;
+ int ret;
+ int origin = 0;
+ struct peer *peer;
+ int allow_num = 0;
+
+ peer = peer_and_group_lookup_vty(vty, argv[idx_peer]->arg);
+ if (!peer)
+ return CMD_WARNING_CONFIG_FAILED;
+
+ if (argc <= idx_number_origin)
+ allow_num = 3;
+ else {
+ if (argv[idx_number_origin]->type == WORD_TKN)
+ origin = 1;
+ else
+ allow_num = atoi(argv[idx_number_origin]->arg);
+ }
+
+ ret = peer_allowas_in_set(peer, bgp_node_afi(vty), bgp_node_safi(vty),
+ allow_num, origin);
+
+ return bgp_vty_return(vty, ret);
+}
+
+ALIAS_HIDDEN(
+ neighbor_allowas_in, neighbor_allowas_in_hidden_cmd,
+ "neighbor <A.B.C.D|X:X::X:X|WORD> allowas-in [<(1-10)|origin>]",
+ NEIGHBOR_STR NEIGHBOR_ADDR_STR2
+ "Accept as-path with my AS present in it\n"
+ "Number of occurrences of AS number\n"
+ "Only accept my AS in the as-path if the route was originated in my AS\n")
+
+DEFUN (no_neighbor_allowas_in,
+ no_neighbor_allowas_in_cmd,
+ "no neighbor <A.B.C.D|X:X::X:X|WORD> allowas-in [<(1-10)|origin>]",
+ NO_STR
+ NEIGHBOR_STR
+ NEIGHBOR_ADDR_STR2
+ "allow local ASN appears in aspath attribute\n"
+ "Number of occurrences of AS number\n"
+ "Only accept my AS in the as-path if the route was originated in my AS\n")
+{
+ int idx_peer = 2;
+ int ret;
+ struct peer *peer;
+
+ peer = peer_and_group_lookup_vty(vty, argv[idx_peer]->arg);
+ if (!peer)
+ return CMD_WARNING_CONFIG_FAILED;
+
+ ret = peer_allowas_in_unset(peer, bgp_node_afi(vty),
+ bgp_node_safi(vty));
+
+ return bgp_vty_return(vty, ret);
+}
+
+ALIAS_HIDDEN(
+ no_neighbor_allowas_in, no_neighbor_allowas_in_hidden_cmd,
+ "no neighbor <A.B.C.D|X:X::X:X|WORD> allowas-in [<(1-10)|origin>]",
+ NO_STR NEIGHBOR_STR NEIGHBOR_ADDR_STR2
+ "allow local ASN appears in aspath attribute\n"
+ "Number of occurrences of AS number\n"
+ "Only accept my AS in the as-path if the route was originated in my AS\n")
+
+DEFUN (neighbor_ttl_security,
+ neighbor_ttl_security_cmd,
+ "neighbor <A.B.C.D|X:X::X:X|WORD> ttl-security hops (1-254)",
+ NEIGHBOR_STR
+ NEIGHBOR_ADDR_STR2
+ "BGP ttl-security parameters\n"
+ "Specify the maximum number of hops to the BGP peer\n"
+ "Number of hops to BGP peer\n")
+{
+ int idx_peer = 1;
+ int idx_number = 4;
+ struct peer *peer;
+ int gtsm_hops;
+
+ peer = peer_and_group_lookup_vty(vty, argv[idx_peer]->arg);
+ if (!peer)
+ return CMD_WARNING_CONFIG_FAILED;
+
+ gtsm_hops = strtoul(argv[idx_number]->arg, NULL, 10);
+
+ /*
+ * If 'neighbor swpX', then this is for directly connected peers,
+ * we should not accept a ttl-security hops value greater than 1.
+ */
+ if (peer->conf_if && (gtsm_hops > BGP_GTSM_HOPS_CONNECTED)) {
+ vty_out(vty,
+ "%s is directly connected peer, hops cannot exceed 1\n",
+ argv[idx_peer]->arg);
+ return CMD_WARNING_CONFIG_FAILED;
+ }
+
+ return bgp_vty_return(vty, peer_ttl_security_hops_set(peer, gtsm_hops));
+}
+
+DEFUN (no_neighbor_ttl_security,
+ no_neighbor_ttl_security_cmd,
+ "no neighbor <A.B.C.D|X:X::X:X|WORD> ttl-security hops (1-254)",
+ NO_STR
+ NEIGHBOR_STR
+ NEIGHBOR_ADDR_STR2
+ "BGP ttl-security parameters\n"
+ "Specify the maximum number of hops to the BGP peer\n"
+ "Number of hops to BGP peer\n")
+{
+ int idx_peer = 2;
+ struct peer *peer;
+
+ peer = peer_and_group_lookup_vty(vty, argv[idx_peer]->arg);
+ if (!peer)
+ return CMD_WARNING_CONFIG_FAILED;
+
+ return bgp_vty_return(vty, peer_ttl_security_hops_unset(peer));
+}
+
+/* disable-addpath-rx */
+DEFUN(neighbor_disable_addpath_rx,
+ neighbor_disable_addpath_rx_cmd,
+ "neighbor <A.B.C.D|X:X::X:X|WORD> disable-addpath-rx",
+ NEIGHBOR_STR
+ NEIGHBOR_ADDR_STR2
+ "Do not accept additional paths\n")
+{
+ char *peer_str = argv[1]->arg;
+ struct peer *peer;
+ afi_t afi = bgp_node_afi(vty);
+ safi_t safi = bgp_node_safi(vty);
+
+ peer = peer_and_group_lookup_vty(vty, peer_str);
+ if (!peer)
+ return CMD_WARNING_CONFIG_FAILED;
+
+ return peer_af_flag_set_vty(vty, peer_str, afi, safi,
+ PEER_FLAG_DISABLE_ADDPATH_RX);
+}
+
+DEFUN(no_neighbor_disable_addpath_rx,
+ no_neighbor_disable_addpath_rx_cmd,
+ "no neighbor <A.B.C.D|X:X::X:X|WORD> disable-addpath-rx",
+ NO_STR
+ NEIGHBOR_STR
+ NEIGHBOR_ADDR_STR2
+ "Do not accept additional paths\n")
+{
+ char *peer_str = argv[2]->arg;
+ struct peer *peer;
+ afi_t afi = bgp_node_afi(vty);
+ safi_t safi = bgp_node_safi(vty);
+
+ peer = peer_and_group_lookup_vty(vty, peer_str);
+ if (!peer)
+ return CMD_WARNING_CONFIG_FAILED;
+
+ return peer_af_flag_unset_vty(vty, peer_str, afi, safi,
+ PEER_FLAG_DISABLE_ADDPATH_RX);
+}
+
+DEFUN (neighbor_addpath_tx_all_paths,
+ neighbor_addpath_tx_all_paths_cmd,
+ "neighbor <A.B.C.D|X:X::X:X|WORD> addpath-tx-all-paths",
+ NEIGHBOR_STR
+ NEIGHBOR_ADDR_STR2
+ "Use addpath to advertise all paths to a neighbor\n")
+{
+ int idx_peer = 1;
+ struct peer *peer;
+
+ peer = peer_and_group_lookup_vty(vty, argv[idx_peer]->arg);
+ if (!peer)
+ return CMD_WARNING_CONFIG_FAILED;
+
+ bgp_addpath_set_peer_type(peer, bgp_node_afi(vty), bgp_node_safi(vty),
+ BGP_ADDPATH_ALL);
+ return CMD_SUCCESS;
+}
+
+ALIAS_HIDDEN(neighbor_addpath_tx_all_paths,
+ neighbor_addpath_tx_all_paths_hidden_cmd,
+ "neighbor <A.B.C.D|X:X::X:X|WORD> addpath-tx-all-paths",
+ NEIGHBOR_STR NEIGHBOR_ADDR_STR2
+ "Use addpath to advertise all paths to a neighbor\n")
+
+DEFUN (no_neighbor_addpath_tx_all_paths,
+ no_neighbor_addpath_tx_all_paths_cmd,
+ "no neighbor <A.B.C.D|X:X::X:X|WORD> addpath-tx-all-paths",
+ NO_STR
+ NEIGHBOR_STR
+ NEIGHBOR_ADDR_STR2
+ "Use addpath to advertise all paths to a neighbor\n")
+{
+ int idx_peer = 2;
+ struct peer *peer;
+
+ peer = peer_and_group_lookup_vty(vty, argv[idx_peer]->arg);
+ if (!peer)
+ return CMD_WARNING_CONFIG_FAILED;
+
+ if (peer->addpath_type[bgp_node_afi(vty)][bgp_node_safi(vty)]
+ != BGP_ADDPATH_ALL) {
+ vty_out(vty,
+ "%% Peer not currently configured to transmit all paths.");
+ return CMD_WARNING_CONFIG_FAILED;
+ }
+
+ bgp_addpath_set_peer_type(peer, bgp_node_afi(vty), bgp_node_safi(vty),
+ BGP_ADDPATH_NONE);
+
+ return CMD_SUCCESS;
+}
+
+ALIAS_HIDDEN(no_neighbor_addpath_tx_all_paths,
+ no_neighbor_addpath_tx_all_paths_hidden_cmd,
+ "no neighbor <A.B.C.D|X:X::X:X|WORD> addpath-tx-all-paths",
+ NO_STR NEIGHBOR_STR NEIGHBOR_ADDR_STR2
+ "Use addpath to advertise all paths to a neighbor\n")
+
+DEFUN (neighbor_addpath_tx_bestpath_per_as,
+ neighbor_addpath_tx_bestpath_per_as_cmd,
+ "neighbor <A.B.C.D|X:X::X:X|WORD> addpath-tx-bestpath-per-AS",
+ NEIGHBOR_STR
+ NEIGHBOR_ADDR_STR2
+ "Use addpath to advertise the bestpath per each neighboring AS\n")
+{
+ int idx_peer = 1;
+ struct peer *peer;
+
+ peer = peer_and_group_lookup_vty(vty, argv[idx_peer]->arg);
+ if (!peer)
+ return CMD_WARNING_CONFIG_FAILED;
+
+ bgp_addpath_set_peer_type(peer, bgp_node_afi(vty), bgp_node_safi(vty),
+ BGP_ADDPATH_BEST_PER_AS);
+
+ return CMD_SUCCESS;
+}
+
+ALIAS_HIDDEN(neighbor_addpath_tx_bestpath_per_as,
+ neighbor_addpath_tx_bestpath_per_as_hidden_cmd,
+ "neighbor <A.B.C.D|X:X::X:X|WORD> addpath-tx-bestpath-per-AS",
+ NEIGHBOR_STR NEIGHBOR_ADDR_STR2
+ "Use addpath to advertise the bestpath per each neighboring AS\n")
+
+DEFUN (no_neighbor_addpath_tx_bestpath_per_as,
+ no_neighbor_addpath_tx_bestpath_per_as_cmd,
+ "no neighbor <A.B.C.D|X:X::X:X|WORD> addpath-tx-bestpath-per-AS",
+ NO_STR
+ NEIGHBOR_STR
+ NEIGHBOR_ADDR_STR2
+ "Use addpath to advertise the bestpath per each neighboring AS\n")
+{
+ int idx_peer = 2;
+ struct peer *peer;
+
+ peer = peer_and_group_lookup_vty(vty, argv[idx_peer]->arg);
+ if (!peer)
+ return CMD_WARNING_CONFIG_FAILED;
+
+ if (peer->addpath_type[bgp_node_afi(vty)][bgp_node_safi(vty)]
+ != BGP_ADDPATH_BEST_PER_AS) {
+ vty_out(vty,
+ "%% Peer not currently configured to transmit all best path per as.");
+ return CMD_WARNING_CONFIG_FAILED;
+ }
+
+ bgp_addpath_set_peer_type(peer, bgp_node_afi(vty), bgp_node_safi(vty),
+ BGP_ADDPATH_NONE);
+
+ return CMD_SUCCESS;
+}
+
+ALIAS_HIDDEN(no_neighbor_addpath_tx_bestpath_per_as,
+ no_neighbor_addpath_tx_bestpath_per_as_hidden_cmd,
+ "no neighbor <A.B.C.D|X:X::X:X|WORD> addpath-tx-bestpath-per-AS",
+ NO_STR NEIGHBOR_STR NEIGHBOR_ADDR_STR2
+ "Use addpath to advertise the bestpath per each neighboring AS\n")
+
+DEFPY(
+ neighbor_aspath_loop_detection, neighbor_aspath_loop_detection_cmd,
+ "neighbor <A.B.C.D|X:X::X:X|WORD>$neighbor sender-as-path-loop-detection",
+ NEIGHBOR_STR
+ NEIGHBOR_ADDR_STR2
+ "Detect AS loops before sending to neighbor\n")
+{
+ struct peer *peer;
+
+ peer = peer_and_group_lookup_vty(vty, neighbor);
+ if (!peer)
+ return CMD_WARNING_CONFIG_FAILED;
+
+ peer->as_path_loop_detection = true;
+
+ return CMD_SUCCESS;
+}
+
+DEFPY(
+ no_neighbor_aspath_loop_detection,
+ no_neighbor_aspath_loop_detection_cmd,
+ "no neighbor <A.B.C.D|X:X::X:X|WORD>$neighbor sender-as-path-loop-detection",
+ NO_STR
+ NEIGHBOR_STR
+ NEIGHBOR_ADDR_STR2
+ "Detect AS loops before sending to neighbor\n")
+{
+ struct peer *peer;
+
+ peer = peer_and_group_lookup_vty(vty, neighbor);
+ if (!peer)
+ return CMD_WARNING_CONFIG_FAILED;
+
+ peer->as_path_loop_detection = false;
+
+ return CMD_SUCCESS;
+}
+
+static int set_ecom_list(struct vty *vty, int argc, struct cmd_token **argv,
+ struct ecommunity **list, bool is_rt6)
+{
+ struct ecommunity *ecom = NULL;
+ struct ecommunity *ecomadd;
+
+ for (; argc; --argc, ++argv) {
+ if (is_rt6)
+ ecomadd = ecommunity_str2com_ipv6(argv[0]->arg,
+ ECOMMUNITY_ROUTE_TARGET,
+ 0);
+ else
+ ecomadd = ecommunity_str2com(argv[0]->arg,
+ ECOMMUNITY_ROUTE_TARGET,
+ 0);
+ if (!ecomadd) {
+ vty_out(vty, "Malformed community-list value\n");
+ if (ecom)
+ ecommunity_free(&ecom);
+ return CMD_WARNING_CONFIG_FAILED;
+ }
+
+ if (ecom) {
+ ecommunity_merge(ecom, ecomadd);
+ ecommunity_free(&ecomadd);
+ } else {
+ ecom = ecomadd;
+ }
+ }
+
+ if (*list) {
+ ecommunity_free(&*list);
+ }
+ *list = ecom;
+
+ return CMD_SUCCESS;
+}
+
+/*
+ * v2vimport is true if we are handling a `import vrf ...` command
+ */
+static afi_t vpn_policy_getafi(struct vty *vty, struct bgp *bgp, bool v2vimport)
+{
+ afi_t afi;
+
+ switch (vty->node) {
+ case BGP_IPV4_NODE:
+ afi = AFI_IP;
+ break;
+ case BGP_IPV6_NODE:
+ afi = AFI_IP6;
+ break;
+ default:
+ vty_out(vty,
+ "%% context error: valid only in address-family <ipv4|ipv6> unicast block\n");
+ return AFI_MAX;
+ }
+
+ if (!v2vimport) {
+ if (CHECK_FLAG(bgp->af_flags[afi][SAFI_UNICAST],
+ BGP_CONFIG_VRF_TO_VRF_IMPORT)
+ || CHECK_FLAG(bgp->af_flags[afi][SAFI_UNICAST],
+ BGP_CONFIG_VRF_TO_VRF_EXPORT)) {
+ vty_out(vty,
+ "%% error: Please unconfigure import vrf commands before using vpn commands\n");
+ return AFI_MAX;
+ }
+ } else {
+ if (CHECK_FLAG(bgp->af_flags[afi][SAFI_UNICAST],
+ BGP_CONFIG_VRF_TO_MPLSVPN_EXPORT)
+ || CHECK_FLAG(bgp->af_flags[afi][SAFI_UNICAST],
+ BGP_CONFIG_MPLSVPN_TO_VRF_IMPORT)) {
+ vty_out(vty,
+ "%% error: Please unconfigure vpn to vrf commands before using import vrf commands\n");
+ return AFI_MAX;
+ }
+ }
+ return afi;
+}
+
+DEFPY (af_rd_vpn_export,
+ af_rd_vpn_export_cmd,
+ "[no] rd vpn export ASN:NN_OR_IP-ADDRESS:NN$rd_str",
+ NO_STR
+ "Specify route distinguisher\n"
+ "Between current address-family and vpn\n"
+ "For routes leaked from current address-family to vpn\n"
+ "Route Distinguisher (<as-number>:<number> | <ip-address>:<number>)\n")
+{
+ VTY_DECLVAR_CONTEXT(bgp, bgp);
+ struct prefix_rd prd;
+ int ret;
+ afi_t afi;
+ int idx = 0;
+ bool yes = true;
+
+ if (argv_find(argv, argc, "no", &idx))
+ yes = false;
+
+ if (yes) {
+ ret = str2prefix_rd(rd_str, &prd);
+ if (!ret) {
+ vty_out(vty, "%% Malformed rd\n");
+ return CMD_WARNING_CONFIG_FAILED;
+ }
+ }
+
+ afi = vpn_policy_getafi(vty, bgp, false);
+ if (afi == AFI_MAX)
+ return CMD_WARNING_CONFIG_FAILED;
+
+ /*
+ * pre-change: un-export vpn routes (vpn->vrf routes unaffected)
+ */
+ vpn_leak_prechange(BGP_VPN_POLICY_DIR_TOVPN, afi,
+ bgp_get_default(), bgp);
+
+ if (yes) {
+ bgp->vpn_policy[afi].tovpn_rd = prd;
+ SET_FLAG(bgp->vpn_policy[afi].flags,
+ BGP_VPN_POLICY_TOVPN_RD_SET);
+ } else {
+ UNSET_FLAG(bgp->vpn_policy[afi].flags,
+ BGP_VPN_POLICY_TOVPN_RD_SET);
+ }
+
+ /* post-change: re-export vpn routes */
+ vpn_leak_postchange(BGP_VPN_POLICY_DIR_TOVPN, afi,
+ bgp_get_default(), bgp);
+
+ return CMD_SUCCESS;
+}
+
+ALIAS (af_rd_vpn_export,
+ af_no_rd_vpn_export_cmd,
+ "no rd vpn export",
+ NO_STR
+ "Specify route distinguisher\n"
+ "Between current address-family and vpn\n"
+ "For routes leaked from current address-family to vpn\n")
+
+DEFPY (af_label_vpn_export,
+ af_label_vpn_export_cmd,
+ "[no] label vpn export <(0-1048575)$label_val|auto$label_auto>",
+ NO_STR
+ "label value for VRF\n"
+ "Between current address-family and vpn\n"
+ "For routes leaked from current address-family to vpn\n"
+ "Label Value <0-1048575>\n"
+ "Automatically assign a label\n")
+{
+ VTY_DECLVAR_CONTEXT(bgp, bgp);
+ mpls_label_t label = MPLS_LABEL_NONE;
+ afi_t afi;
+ int idx = 0;
+ bool yes = true;
+
+ if (argv_find(argv, argc, "no", &idx))
+ yes = false;
+
+ /* If "no ...", squash trailing parameter */
+ if (!yes)
+ label_auto = NULL;
+
+ if (yes) {
+ if (!label_auto)
+ label = label_val; /* parser should force unsigned */
+ }
+
+ afi = vpn_policy_getafi(vty, bgp, false);
+ if (afi == AFI_MAX)
+ return CMD_WARNING_CONFIG_FAILED;
+
+
+ if (label_auto && CHECK_FLAG(bgp->vpn_policy[afi].flags,
+ BGP_VPN_POLICY_TOVPN_LABEL_AUTO))
+ /* no change */
+ return CMD_SUCCESS;
+
+ /*
+ * pre-change: un-export vpn routes (vpn->vrf routes unaffected)
+ */
+ vpn_leak_prechange(BGP_VPN_POLICY_DIR_TOVPN, afi,
+ bgp_get_default(), bgp);
+
+ if (!label_auto && CHECK_FLAG(bgp->vpn_policy[afi].flags,
+ BGP_VPN_POLICY_TOVPN_LABEL_AUTO)) {
+
+ if (bgp->vpn_policy[afi].tovpn_label != MPLS_LABEL_NONE) {
+
+ /*
+ * label has previously been automatically
+ * assigned by labelpool: release it
+ *
+ * NB if tovpn_label == MPLS_LABEL_NONE it
+ * means the automatic assignment is in flight
+ * and therefore the labelpool callback must
+ * detect that the auto label is not needed.
+ */
+
+ bgp_lp_release(LP_TYPE_VRF,
+ &bgp->vpn_policy[afi],
+ bgp->vpn_policy[afi].tovpn_label);
+ }
+ UNSET_FLAG(bgp->vpn_policy[afi].flags,
+ BGP_VPN_POLICY_TOVPN_LABEL_AUTO);
+ }
+
+ bgp->vpn_policy[afi].tovpn_label = label;
+ if (label_auto) {
+ SET_FLAG(bgp->vpn_policy[afi].flags,
+ BGP_VPN_POLICY_TOVPN_LABEL_AUTO);
+ bgp_lp_get(LP_TYPE_VRF, &bgp->vpn_policy[afi],
+ vpn_leak_label_callback);
+ }
+
+ /* post-change: re-export vpn routes */
+ vpn_leak_postchange(BGP_VPN_POLICY_DIR_TOVPN, afi,
+ bgp_get_default(), bgp);
+
+ hook_call(bgp_snmp_update_last_changed, bgp);
+ return CMD_SUCCESS;
+}
+
+DEFPY (af_sid_vpn_export,
+ af_sid_vpn_export_cmd,
+ "[no] sid vpn export <(1-255)$sid_idx|auto$sid_auto>",
+ NO_STR
+ "sid value for VRF\n"
+ "Between current address-family and vpn\n"
+ "For routes leaked from current address-family to vpn\n"
+ "Sid allocation index\n"
+ "Automatically assign a label\n")
+{
+ VTY_DECLVAR_CONTEXT(bgp, bgp);
+ afi_t afi;
+ int debug = 0;
+ int idx = 0;
+ bool yes = true;
+
+ if (argv_find(argv, argc, "no", &idx))
+ yes = false;
+ debug = (BGP_DEBUG(vpn, VPN_LEAK_TO_VRF) |
+ BGP_DEBUG(vpn, VPN_LEAK_FROM_VRF));
+
+ afi = vpn_policy_getafi(vty, bgp, false);
+ if (afi == AFI_MAX)
+ return CMD_WARNING_CONFIG_FAILED;
+
+ if (!yes) {
+ /* implement me */
+ vty_out(vty, "It's not implemented\n");
+ return CMD_WARNING_CONFIG_FAILED;
+ }
+
+ /* skip when it's already configured */
+ if ((sid_idx != 0 && bgp->vpn_policy[afi].tovpn_sid_index != 0)
+ || (sid_auto && CHECK_FLAG(bgp->vpn_policy[afi].flags,
+ BGP_VPN_POLICY_TOVPN_SID_AUTO)))
+ return CMD_SUCCESS;
+
+ /*
+ * mode change between sid_idx and sid_auto isn't supported.
+ * user must negate sid vpn export when they want to change the mode
+ */
+ if ((sid_auto && bgp->vpn_policy[afi].tovpn_sid_index != 0)
+ || (sid_idx != 0 && CHECK_FLAG(bgp->vpn_policy[afi].flags,
+ BGP_VPN_POLICY_TOVPN_SID_AUTO))) {
+ vty_out(vty, "it's already configured as %s.\n",
+ sid_auto ? "auto-mode" : "idx-mode");
+ return CMD_WARNING_CONFIG_FAILED;
+ }
+
+ /* pre-change */
+ vpn_leak_prechange(BGP_VPN_POLICY_DIR_TOVPN, afi,
+ bgp_get_default(), bgp);
+
+ if (sid_auto) {
+ /* SID allocation auto-mode */
+ if (debug)
+ zlog_debug("%s: auto sid alloc.", __func__);
+ SET_FLAG(bgp->vpn_policy[afi].flags,
+ BGP_VPN_POLICY_TOVPN_SID_AUTO);
+ } else {
+ /* SID allocation index-mode */
+ if (debug)
+ zlog_debug("%s: idx %ld sid alloc.", __func__, sid_idx);
+ bgp->vpn_policy[afi].tovpn_sid_index = sid_idx;
+ }
+
+ /* post-change */
+ vpn_leak_postchange(BGP_VPN_POLICY_DIR_TOVPN, afi,
+ bgp_get_default(), bgp);
+ return CMD_SUCCESS;
+}
+
+ALIAS (af_label_vpn_export,
+ af_no_label_vpn_export_cmd,
+ "no label vpn export",
+ NO_STR
+ "label value for VRF\n"
+ "Between current address-family and vpn\n"
+ "For routes leaked from current address-family to vpn\n")
+
+DEFPY (af_nexthop_vpn_export,
+ af_nexthop_vpn_export_cmd,
+ "[no] nexthop vpn export [<A.B.C.D|X:X::X:X>$nexthop_su]",
+ NO_STR
+ "Specify next hop to use for VRF advertised prefixes\n"
+ "Between current address-family and vpn\n"
+ "For routes leaked from current address-family to vpn\n"
+ "IPv4 prefix\n"
+ "IPv6 prefix\n")
+{
+ VTY_DECLVAR_CONTEXT(bgp, bgp);
+ afi_t afi;
+ struct prefix p;
+
+ if (!no) {
+ if (!nexthop_su) {
+ vty_out(vty, "%% Nexthop required\n");
+ return CMD_WARNING_CONFIG_FAILED;
+ }
+ if (!sockunion2hostprefix(nexthop_su, &p))
+ return CMD_WARNING_CONFIG_FAILED;
+ }
+
+ afi = vpn_policy_getafi(vty, bgp, false);
+ if (afi == AFI_MAX)
+ return CMD_WARNING_CONFIG_FAILED;
+
+ /*
+ * pre-change: un-export vpn routes (vpn->vrf routes unaffected)
+ */
+ vpn_leak_prechange(BGP_VPN_POLICY_DIR_TOVPN, afi,
+ bgp_get_default(), bgp);
+
+ if (!no) {
+ bgp->vpn_policy[afi].tovpn_nexthop = p;
+ SET_FLAG(bgp->vpn_policy[afi].flags,
+ BGP_VPN_POLICY_TOVPN_NEXTHOP_SET);
+ } else {
+ UNSET_FLAG(bgp->vpn_policy[afi].flags,
+ BGP_VPN_POLICY_TOVPN_NEXTHOP_SET);
+ }
+
+ /* post-change: re-export vpn routes */
+ vpn_leak_postchange(BGP_VPN_POLICY_DIR_TOVPN, afi,
+ bgp_get_default(), bgp);
+
+ return CMD_SUCCESS;
+}
+
+static int vpn_policy_getdirs(struct vty *vty, const char *dstr, int *dodir)
+{
+ if (!strcmp(dstr, "import")) {
+ dodir[BGP_VPN_POLICY_DIR_FROMVPN] = 1;
+ } else if (!strcmp(dstr, "export")) {
+ dodir[BGP_VPN_POLICY_DIR_TOVPN] = 1;
+ } else if (!strcmp(dstr, "both")) {
+ dodir[BGP_VPN_POLICY_DIR_FROMVPN] = 1;
+ dodir[BGP_VPN_POLICY_DIR_TOVPN] = 1;
+ } else {
+ vty_out(vty, "%% direction parse error\n");
+ return CMD_WARNING_CONFIG_FAILED;
+ }
+ return CMD_SUCCESS;
+}
+
+DEFPY (af_rt_vpn_imexport,
+ af_rt_vpn_imexport_cmd,
+ "[no] <rt|route-target> vpn <import|export|both>$direction_str RTLIST...",
+ NO_STR
+ "Specify route target list\n"
+ "Specify route target list\n"
+ "Between current address-family and vpn\n"
+ "For routes leaked from vpn to current address-family: match any\n"
+ "For routes leaked from current address-family to vpn: set\n"
+ "both import: match any and export: set\n"
+ "Space separated route target list (A.B.C.D:MN|EF:OPQR|GHJK:MN)\n")
+{
+ VTY_DECLVAR_CONTEXT(bgp, bgp);
+ int ret;
+ struct ecommunity *ecom = NULL;
+ int dodir[BGP_VPN_POLICY_DIR_MAX] = {0};
+ enum vpn_policy_direction dir;
+ afi_t afi;
+ int idx = 0;
+ bool yes = true;
+
+ if (argv_find(argv, argc, "no", &idx))
+ yes = false;
+
+ afi = vpn_policy_getafi(vty, bgp, false);
+ if (afi == AFI_MAX)
+ return CMD_WARNING_CONFIG_FAILED;
+
+ ret = vpn_policy_getdirs(vty, direction_str, dodir);
+ if (ret != CMD_SUCCESS)
+ return ret;
+
+ if (yes) {
+ if (!argv_find(argv, argc, "RTLIST", &idx)) {
+ vty_out(vty, "%% Missing RTLIST\n");
+ return CMD_WARNING_CONFIG_FAILED;
+ }
+ ret = set_ecom_list(vty, argc - idx, argv + idx, &ecom, false);
+ if (ret != CMD_SUCCESS) {
+ return ret;
+ }
+ }
+
+ for (dir = 0; dir < BGP_VPN_POLICY_DIR_MAX; ++dir) {
+ if (!dodir[dir])
+ continue;
+
+ vpn_leak_prechange(dir, afi, bgp_get_default(), bgp);
+
+ if (yes) {
+ if (bgp->vpn_policy[afi].rtlist[dir])
+ ecommunity_free(
+ &bgp->vpn_policy[afi].rtlist[dir]);
+ bgp->vpn_policy[afi].rtlist[dir] =
+ ecommunity_dup(ecom);
+ } else {
+ if (bgp->vpn_policy[afi].rtlist[dir])
+ ecommunity_free(
+ &bgp->vpn_policy[afi].rtlist[dir]);
+ bgp->vpn_policy[afi].rtlist[dir] = NULL;
+ }
+
+ vpn_leak_postchange(dir, afi, bgp_get_default(), bgp);
+ }
+
+ if (ecom)
+ ecommunity_free(&ecom);
+
+ return CMD_SUCCESS;
+}
+
+ALIAS (af_rt_vpn_imexport,
+ af_no_rt_vpn_imexport_cmd,
+ "no <rt|route-target> vpn <import|export|both>$direction_str",
+ NO_STR
+ "Specify route target list\n"
+ "Specify route target list\n"
+ "Between current address-family and vpn\n"
+ "For routes leaked from vpn to current address-family\n"
+ "For routes leaked from current address-family to vpn\n"
+ "both import and export\n")
+
+DEFPY (af_route_map_vpn_imexport,
+ af_route_map_vpn_imexport_cmd,
+/* future: "route-map <vpn|evpn|vrf NAME> <import|export> RMAP" */
+ "[no] route-map vpn <import|export>$direction_str RMAP$rmap_str",
+ NO_STR
+ "Specify route map\n"
+ "Between current address-family and vpn\n"
+ "For routes leaked from vpn to current address-family\n"
+ "For routes leaked from current address-family to vpn\n"
+ "name of route-map\n")
+{
+ VTY_DECLVAR_CONTEXT(bgp, bgp);
+ int ret;
+ int dodir[BGP_VPN_POLICY_DIR_MAX] = {0};
+ enum vpn_policy_direction dir;
+ afi_t afi;
+ int idx = 0;
+ bool yes = true;
+
+ if (argv_find(argv, argc, "no", &idx))
+ yes = false;
+
+ afi = vpn_policy_getafi(vty, bgp, false);
+ if (afi == AFI_MAX)
+ return CMD_WARNING_CONFIG_FAILED;
+
+ ret = vpn_policy_getdirs(vty, direction_str, dodir);
+ if (ret != CMD_SUCCESS)
+ return ret;
+
+ for (dir = 0; dir < BGP_VPN_POLICY_DIR_MAX; ++dir) {
+ if (!dodir[dir])
+ continue;
+
+ vpn_leak_prechange(dir, afi, bgp_get_default(), bgp);
+
+ if (yes) {
+ if (bgp->vpn_policy[afi].rmap_name[dir])
+ XFREE(MTYPE_ROUTE_MAP_NAME,
+ bgp->vpn_policy[afi].rmap_name[dir]);
+ bgp->vpn_policy[afi].rmap_name[dir] = XSTRDUP(
+ MTYPE_ROUTE_MAP_NAME, rmap_str);
+ bgp->vpn_policy[afi].rmap[dir] =
+ route_map_lookup_warn_noexist(vty, rmap_str);
+ if (!bgp->vpn_policy[afi].rmap[dir])
+ return CMD_SUCCESS;
+ } else {
+ if (bgp->vpn_policy[afi].rmap_name[dir])
+ XFREE(MTYPE_ROUTE_MAP_NAME,
+ bgp->vpn_policy[afi].rmap_name[dir]);
+ bgp->vpn_policy[afi].rmap_name[dir] = NULL;
+ bgp->vpn_policy[afi].rmap[dir] = NULL;
+ }
+
+ vpn_leak_postchange(dir, afi, bgp_get_default(), bgp);
+ }
+
+ return CMD_SUCCESS;
+}
+
+ALIAS (af_route_map_vpn_imexport,
+ af_no_route_map_vpn_imexport_cmd,
+ "no route-map vpn <import|export>$direction_str",
+ NO_STR
+ "Specify route map\n"
+ "Between current address-family and vpn\n"
+ "For routes leaked from vpn to current address-family\n"
+ "For routes leaked from current address-family to vpn\n")
+
+DEFPY(af_import_vrf_route_map, af_import_vrf_route_map_cmd,
+ "import vrf route-map RMAP$rmap_str",
+ "Import routes from another VRF\n"
+ "Vrf routes being filtered\n"
+ "Specify route map\n"
+ "name of route-map\n")
+{
+ VTY_DECLVAR_CONTEXT(bgp, bgp);
+ enum vpn_policy_direction dir = BGP_VPN_POLICY_DIR_FROMVPN;
+ afi_t afi;
+ struct bgp *bgp_default;
+
+ afi = vpn_policy_getafi(vty, bgp, true);
+ if (afi == AFI_MAX)
+ return CMD_WARNING_CONFIG_FAILED;
+
+ bgp_default = bgp_get_default();
+ if (!bgp_default) {
+ int32_t ret;
+ as_t as = bgp->as;
+
+ /* Auto-create assuming the same AS */
+ ret = bgp_get_vty(&bgp_default, &as, NULL,
+ BGP_INSTANCE_TYPE_DEFAULT);
+
+ if (ret) {
+ vty_out(vty,
+ "VRF default is not configured as a bgp instance\n");
+ return CMD_WARNING;
+ }
+ }
+
+ vpn_leak_prechange(dir, afi, bgp_get_default(), bgp);
+
+ if (bgp->vpn_policy[afi].rmap_name[dir])
+ XFREE(MTYPE_ROUTE_MAP_NAME,
+ bgp->vpn_policy[afi].rmap_name[dir]);
+ bgp->vpn_policy[afi].rmap_name[dir] =
+ XSTRDUP(MTYPE_ROUTE_MAP_NAME, rmap_str);
+ bgp->vpn_policy[afi].rmap[dir] =
+ route_map_lookup_warn_noexist(vty, rmap_str);
+ if (!bgp->vpn_policy[afi].rmap[dir])
+ return CMD_SUCCESS;
+
+ SET_FLAG(bgp->af_flags[afi][SAFI_UNICAST],
+ BGP_CONFIG_VRF_TO_VRF_IMPORT);
+
+ vpn_leak_postchange(dir, afi, bgp_get_default(), bgp);
+
+ return CMD_SUCCESS;
+}
+
+DEFPY(af_no_import_vrf_route_map, af_no_import_vrf_route_map_cmd,
+ "no import vrf route-map [RMAP$rmap_str]",
+ NO_STR
+ "Import routes from another VRF\n"
+ "Vrf routes being filtered\n"
+ "Specify route map\n"
+ "name of route-map\n")
+{
+ VTY_DECLVAR_CONTEXT(bgp, bgp);
+ enum vpn_policy_direction dir = BGP_VPN_POLICY_DIR_FROMVPN;
+ afi_t afi;
+
+ afi = vpn_policy_getafi(vty, bgp, true);
+ if (afi == AFI_MAX)
+ return CMD_WARNING_CONFIG_FAILED;
+
+ vpn_leak_prechange(dir, afi, bgp_get_default(), bgp);
+
+ if (bgp->vpn_policy[afi].rmap_name[dir])
+ XFREE(MTYPE_ROUTE_MAP_NAME,
+ bgp->vpn_policy[afi].rmap_name[dir]);
+ bgp->vpn_policy[afi].rmap_name[dir] = NULL;
+ bgp->vpn_policy[afi].rmap[dir] = NULL;
+
+ if (bgp->vpn_policy[afi].import_vrf->count == 0)
+ UNSET_FLAG(bgp->af_flags[afi][SAFI_UNICAST],
+ BGP_CONFIG_VRF_TO_VRF_IMPORT);
+
+ vpn_leak_postchange(dir, afi, bgp_get_default(), bgp);
+
+ return CMD_SUCCESS;
+}
+
+DEFPY(bgp_imexport_vrf, bgp_imexport_vrf_cmd,
+ "[no] import vrf VIEWVRFNAME$import_name",
+ NO_STR
+ "Import routes from another VRF\n"
+ "VRF to import from\n"
+ "The name of the VRF\n")
+{
+ VTY_DECLVAR_CONTEXT(bgp, bgp);
+ struct listnode *node;
+ struct bgp *vrf_bgp, *bgp_default;
+ int32_t ret = 0;
+ as_t as = bgp->as;
+ bool remove = false;
+ int32_t idx = 0;
+ char *vname;
+ enum bgp_instance_type bgp_type = BGP_INSTANCE_TYPE_VRF;
+ safi_t safi;
+ afi_t afi;
+
+ if (import_name == NULL) {
+ vty_out(vty, "%% Missing import name\n");
+ return CMD_WARNING;
+ }
+
+ if (strcmp(import_name, "route-map") == 0) {
+ vty_out(vty, "%% Must include route-map name\n");
+ return CMD_WARNING;
+ }
+
+ if (argv_find(argv, argc, "no", &idx))
+ remove = true;
+
+ afi = vpn_policy_getafi(vty, bgp, true);
+ if (afi == AFI_MAX)
+ return CMD_WARNING_CONFIG_FAILED;
+
+ safi = bgp_node_safi(vty);
+
+ if (((BGP_INSTANCE_TYPE_DEFAULT == bgp->inst_type)
+ && (strcmp(import_name, VRF_DEFAULT_NAME) == 0))
+ || (bgp->name && (strcmp(import_name, bgp->name) == 0))) {
+ vty_out(vty, "%% Cannot %s vrf %s into itself\n",
+ remove ? "unimport" : "import", import_name);
+ return CMD_WARNING;
+ }
+
+ bgp_default = bgp_get_default();
+ if (!bgp_default) {
+ /* Auto-create assuming the same AS */
+ ret = bgp_get_vty(&bgp_default, &as, NULL,
+ BGP_INSTANCE_TYPE_DEFAULT);
+
+ if (ret) {
+ vty_out(vty,
+ "VRF default is not configured as a bgp instance\n");
+ return CMD_WARNING;
+ }
+ }
+
+ vrf_bgp = bgp_lookup_by_name(import_name);
+ if (!vrf_bgp) {
+ if (strcmp(import_name, VRF_DEFAULT_NAME) == 0)
+ vrf_bgp = bgp_default;
+ else
+ /* Auto-create assuming the same AS */
+ ret = bgp_get_vty(&vrf_bgp, &as, import_name, bgp_type);
+
+ if (ret) {
+ vty_out(vty,
+ "VRF %s is not configured as a bgp instance\n",
+ import_name);
+ return CMD_WARNING;
+ }
+ }
+
+ if (remove) {
+ vrf_unimport_from_vrf(bgp, vrf_bgp, afi, safi);
+ } else {
+ /* Already importing from "import_vrf"? */
+ for (ALL_LIST_ELEMENTS_RO(bgp->vpn_policy[afi].import_vrf, node,
+ vname)) {
+ if (strcmp(vname, import_name) == 0)
+ return CMD_WARNING;
+ }
+
+ vrf_import_from_vrf(bgp, vrf_bgp, afi, safi);
+ }
+
+ return CMD_SUCCESS;
+}
+
+/* This command is valid only in a bgp vrf instance or the default instance */
+DEFPY (bgp_imexport_vpn,
+ bgp_imexport_vpn_cmd,
+ "[no] <import|export>$direction_str vpn",
+ NO_STR
+ "Import routes to this address-family\n"
+ "Export routes from this address-family\n"
+ "to/from default instance VPN RIB\n")
+{
+ VTY_DECLVAR_CONTEXT(bgp, bgp);
+ int previous_state;
+ afi_t afi;
+ safi_t safi;
+ int idx = 0;
+ bool yes = true;
+ int flag;
+ enum vpn_policy_direction dir;
+
+ if (argv_find(argv, argc, "no", &idx))
+ yes = false;
+
+ if (BGP_INSTANCE_TYPE_VRF != bgp->inst_type &&
+ BGP_INSTANCE_TYPE_DEFAULT != bgp->inst_type) {
+
+ vty_out(vty, "%% import|export vpn valid only for bgp vrf or default instance\n");
+ return CMD_WARNING_CONFIG_FAILED;
+ }
+
+ afi = bgp_node_afi(vty);
+ safi = bgp_node_safi(vty);
+ if ((SAFI_UNICAST != safi) || ((AFI_IP != afi) && (AFI_IP6 != afi))) {
+ vty_out(vty, "%% import|export vpn valid only for unicast ipv4|ipv6\n");
+ return CMD_WARNING_CONFIG_FAILED;
+ }
+
+ if (!strcmp(direction_str, "import")) {
+ flag = BGP_CONFIG_MPLSVPN_TO_VRF_IMPORT;
+ dir = BGP_VPN_POLICY_DIR_FROMVPN;
+ } else if (!strcmp(direction_str, "export")) {
+ flag = BGP_CONFIG_VRF_TO_MPLSVPN_EXPORT;
+ dir = BGP_VPN_POLICY_DIR_TOVPN;
+ } else {
+ vty_out(vty, "%% unknown direction %s\n", direction_str);
+ return CMD_WARNING_CONFIG_FAILED;
+ }
+
+ previous_state = CHECK_FLAG(bgp->af_flags[afi][safi], flag);
+
+ if (yes) {
+ SET_FLAG(bgp->af_flags[afi][safi], flag);
+ if (!previous_state) {
+ /* trigger export current vrf */
+ vpn_leak_postchange(dir, afi, bgp_get_default(), bgp);
+ }
+ } else {
+ if (previous_state) {
+ /* trigger un-export current vrf */
+ vpn_leak_prechange(dir, afi, bgp_get_default(), bgp);
+ }
+ UNSET_FLAG(bgp->af_flags[afi][safi], flag);
+ }
+
+ hook_call(bgp_snmp_init_stats, bgp);
+
+ return CMD_SUCCESS;
+}
+
+DEFPY (af_routetarget_import,
+ af_routetarget_import_cmd,
+ "[no] <rt|route-target|route-target6|rt6> redirect import RTLIST...",
+ NO_STR
+ "Specify route target list\n"
+ "Specify route target list\n"
+ "Specify route target list\n"
+ "Specify route target list\n"
+ "Flow-spec redirect type route target\n"
+ "Import routes to this address-family\n"
+ "Space separated route target list (A.B.C.D:MN|EF:OPQR|GHJK:MN|IPV6:MN)\n")
+{
+ VTY_DECLVAR_CONTEXT(bgp, bgp);
+ int ret;
+ struct ecommunity *ecom = NULL;
+ afi_t afi;
+ int idx = 0, idx_unused = 0;
+ bool yes = true;
+ bool rt6 = false;
+
+ if (argv_find(argv, argc, "no", &idx))
+ yes = false;
+
+ if (argv_find(argv, argc, "rt6", &idx_unused) ||
+ argv_find(argv, argc, "route-target6", &idx_unused))
+ rt6 = true;
+
+ afi = vpn_policy_getafi(vty, bgp, false);
+ if (afi == AFI_MAX)
+ return CMD_WARNING_CONFIG_FAILED;
+
+ if (rt6 && afi != AFI_IP6)
+ return CMD_WARNING_CONFIG_FAILED;
+
+ if (yes) {
+ if (!argv_find(argv, argc, "RTLIST", &idx)) {
+ vty_out(vty, "%% Missing RTLIST\n");
+ return CMD_WARNING_CONFIG_FAILED;
+ }
+ ret = set_ecom_list(vty, argc - idx, argv + idx, &ecom, rt6);
+ if (ret != CMD_SUCCESS)
+ return ret;
+ }
+
+ if (yes) {
+ if (bgp->vpn_policy[afi].import_redirect_rtlist)
+ ecommunity_free(&bgp->vpn_policy[afi]
+ .import_redirect_rtlist);
+ bgp->vpn_policy[afi].import_redirect_rtlist =
+ ecommunity_dup(ecom);
+ } else {
+ if (bgp->vpn_policy[afi].import_redirect_rtlist)
+ ecommunity_free(&bgp->vpn_policy[afi]
+ .import_redirect_rtlist);
+ bgp->vpn_policy[afi].import_redirect_rtlist = NULL;
+ }
+
+ if (ecom)
+ ecommunity_free(&ecom);
+
+ return CMD_SUCCESS;
+}
+
+DEFUN_NOSH (address_family_ipv4_safi,
+ address_family_ipv4_safi_cmd,
+ "address-family ipv4 [<unicast|multicast|vpn|labeled-unicast|flowspec>]",
+ "Enter Address Family command mode\n"
+ BGP_AF_STR
+ BGP_SAFI_WITH_LABEL_HELP_STR)
+{
+
+ if (argc == 3) {
+ VTY_DECLVAR_CONTEXT(bgp, bgp);
+ safi_t safi = bgp_vty_safi_from_str(argv[2]->text);
+ if (bgp->inst_type != BGP_INSTANCE_TYPE_DEFAULT
+ && safi != SAFI_UNICAST && safi != SAFI_MULTICAST
+ && safi != SAFI_EVPN) {
+ vty_out(vty,
+ "Only Unicast/Multicast/EVPN SAFIs supported in non-core instances.\n");
+ return CMD_WARNING_CONFIG_FAILED;
+ }
+ vty->node = bgp_node_type(AFI_IP, safi);
+ } else
+ vty->node = BGP_IPV4_NODE;
+
+ return CMD_SUCCESS;
+}
+
+DEFUN_NOSH (address_family_ipv6_safi,
+ address_family_ipv6_safi_cmd,
+ "address-family ipv6 [<unicast|multicast|vpn|labeled-unicast|flowspec>]",
+ "Enter Address Family command mode\n"
+ BGP_AF_STR
+ BGP_SAFI_WITH_LABEL_HELP_STR)
+{
+ if (argc == 3) {
+ VTY_DECLVAR_CONTEXT(bgp, bgp);
+ safi_t safi = bgp_vty_safi_from_str(argv[2]->text);
+ if (bgp->inst_type != BGP_INSTANCE_TYPE_DEFAULT
+ && safi != SAFI_UNICAST && safi != SAFI_MULTICAST
+ && safi != SAFI_EVPN) {
+ vty_out(vty,
+ "Only Unicast/Multicast/EVPN SAFIs supported in non-core instances.\n");
+ return CMD_WARNING_CONFIG_FAILED;
+ }
+ vty->node = bgp_node_type(AFI_IP6, safi);
+ } else
+ vty->node = BGP_IPV6_NODE;
+
+ return CMD_SUCCESS;
+}
+
+#ifdef KEEP_OLD_VPN_COMMANDS
+DEFUN_NOSH (address_family_vpnv4,
+ address_family_vpnv4_cmd,
+ "address-family vpnv4 [unicast]",
+ "Enter Address Family command mode\n"
+ BGP_AF_STR
+ BGP_AF_MODIFIER_STR)
+{
+ vty->node = BGP_VPNV4_NODE;
+ return CMD_SUCCESS;
+}
+
+DEFUN_NOSH (address_family_vpnv6,
+ address_family_vpnv6_cmd,
+ "address-family vpnv6 [unicast]",
+ "Enter Address Family command mode\n"
+ BGP_AF_STR
+ BGP_AF_MODIFIER_STR)
+{
+ vty->node = BGP_VPNV6_NODE;
+ return CMD_SUCCESS;
+}
+#endif /* KEEP_OLD_VPN_COMMANDS */
+
+DEFUN_NOSH (address_family_evpn,
+ address_family_evpn_cmd,
+ "address-family l2vpn evpn",
+ "Enter Address Family command mode\n"
+ BGP_AF_STR
+ BGP_AF_MODIFIER_STR)
+{
+ VTY_DECLVAR_CONTEXT(bgp, bgp);
+ vty->node = BGP_EVPN_NODE;
+ return CMD_SUCCESS;
+}
+
+DEFUN_NOSH (bgp_segment_routing_srv6,
+ bgp_segment_routing_srv6_cmd,
+ "segment-routing srv6",
+ "Segment-Routing configuration\n"
+ "Segment-Routing SRv6 configuration\n")
+{
+ VTY_DECLVAR_CONTEXT(bgp, bgp);
+ bgp->srv6_enabled = true;
+ vty->node = BGP_SRV6_NODE;
+ return CMD_SUCCESS;
+}
+
+DEFUN (no_bgp_segment_routing_srv6,
+ no_bgp_segment_routing_srv6_cmd,
+ "no segment-routing srv6",
+ NO_STR
+ "Segment-Routing configuration\n"
+ "Segment-Routing SRv6 configuration\n")
+{
+ VTY_DECLVAR_CONTEXT(bgp, bgp);
+
+ if (strlen(bgp->srv6_locator_name) > 0)
+ if (bgp_srv6_locator_unset(bgp) < 0)
+ return CMD_WARNING_CONFIG_FAILED;
+
+ bgp->srv6_enabled = false;
+ return CMD_SUCCESS;
+}
+
+DEFPY (bgp_srv6_locator,
+ bgp_srv6_locator_cmd,
+ "locator NAME$name",
+ "Specify SRv6 locator\n"
+ "Specify SRv6 locator\n")
+{
+ VTY_DECLVAR_CONTEXT(bgp, bgp);
+ int ret;
+
+ if (strlen(bgp->srv6_locator_name) > 0
+ && strcmp(name, bgp->srv6_locator_name) != 0) {
+ vty_out(vty, "srv6 locator is already configured\n");
+ return CMD_WARNING_CONFIG_FAILED;
+ }
+
+ snprintf(bgp->srv6_locator_name,
+ sizeof(bgp->srv6_locator_name), "%s", name);
+
+ ret = bgp_zebra_srv6_manager_get_locator_chunk(name);
+ if (ret < 0)
+ return CMD_WARNING_CONFIG_FAILED;
+
+ return CMD_SUCCESS;
+}
+
+DEFPY (no_bgp_srv6_locator,
+ no_bgp_srv6_locator_cmd,
+ "no locator NAME$name",
+ NO_STR
+ "Specify SRv6 locator\n"
+ "Specify SRv6 locator\n")
+{
+ VTY_DECLVAR_CONTEXT(bgp, bgp);
+
+ /* when locator isn't configured, do nothing */
+ if (strlen(bgp->srv6_locator_name) < 1)
+ return CMD_SUCCESS;
+
+ /* name validation */
+ if (strcmp(name, bgp->srv6_locator_name) != 0) {
+ vty_out(vty, "%% No srv6 locator is configured\n");
+ return CMD_WARNING_CONFIG_FAILED;
+ }
+
+ /* unset locator */
+ if (bgp_srv6_locator_unset(bgp) < 0)
+ return CMD_WARNING_CONFIG_FAILED;
+
+ return CMD_SUCCESS;
+}
+
+DEFPY (show_bgp_srv6,
+ show_bgp_srv6_cmd,
+ "show bgp segment-routing srv6",
+ SHOW_STR
+ BGP_STR
+ "BGP Segment Routing\n"
+ "BGP Segment Routing SRv6\n")
+{
+ struct bgp *bgp;
+ struct listnode *node;
+ struct srv6_locator_chunk *chunk;
+ struct bgp_srv6_function *func;
+ struct in6_addr *tovpn4_sid;
+ struct in6_addr *tovpn6_sid;
+ char buf[256];
+ char buf_tovpn4_sid[256];
+ char buf_tovpn6_sid[256];
+
+ bgp = bgp_get_default();
+ if (!bgp)
+ return CMD_SUCCESS;
+
+ vty_out(vty, "locator_name: %s\n", bgp->srv6_locator_name);
+ vty_out(vty, "locator_chunks:\n");
+ for (ALL_LIST_ELEMENTS_RO(bgp->srv6_locator_chunks, node, chunk))
+ vty_out(vty, "- %pFX\n", &chunk->prefix);
+
+ vty_out(vty, "functions:\n");
+ for (ALL_LIST_ELEMENTS_RO(bgp->srv6_functions, node, func)) {
+ inet_ntop(AF_INET6, &func->sid, buf, sizeof(buf));
+ vty_out(vty, "- sid: %s\n", buf);
+ vty_out(vty, " locator: %s\n", func->locator_name);
+ }
+
+ vty_out(vty, "bgps:\n");
+ for (ALL_LIST_ELEMENTS_RO(bm->bgp, node, bgp)) {
+ vty_out(vty, "- name: %s\n",
+ bgp->name ? bgp->name : "default");
+
+ tovpn4_sid = bgp->vpn_policy[AFI_IP].tovpn_sid;
+ tovpn6_sid = bgp->vpn_policy[AFI_IP6].tovpn_sid;
+ if (tovpn4_sid)
+ inet_ntop(AF_INET6, tovpn4_sid, buf_tovpn4_sid,
+ sizeof(buf_tovpn4_sid));
+ if (tovpn6_sid)
+ inet_ntop(AF_INET6, tovpn6_sid, buf_tovpn6_sid,
+ sizeof(buf_tovpn6_sid));
+
+ vty_out(vty, " vpn_policy[AFI_IP].tovpn_sid: %s\n",
+ tovpn4_sid ? buf_tovpn4_sid : "none");
+ vty_out(vty, " vpn_policy[AFI_IP6].tovpn_sid: %s\n",
+ tovpn6_sid ? buf_tovpn6_sid : "none");
+ }
+
+ return CMD_SUCCESS;
+}
+
+DEFUN_NOSH (exit_address_family,
+ exit_address_family_cmd,
+ "exit-address-family",
+ "Exit from Address Family configuration mode\n")
+{
+ if (vty->node == BGP_IPV4_NODE || vty->node == BGP_IPV4M_NODE
+ || vty->node == BGP_IPV4L_NODE || vty->node == BGP_VPNV4_NODE
+ || vty->node == BGP_IPV6_NODE || vty->node == BGP_IPV6M_NODE
+ || vty->node == BGP_IPV6L_NODE || vty->node == BGP_VPNV6_NODE
+ || vty->node == BGP_EVPN_NODE
+ || vty->node == BGP_FLOWSPECV4_NODE
+ || vty->node == BGP_FLOWSPECV6_NODE)
+ vty->node = BGP_NODE;
+ return CMD_SUCCESS;
+}
+
+/* Recalculate bestpath and re-advertise a prefix */
+static int bgp_clear_prefix(struct vty *vty, const char *view_name,
+ const char *ip_str, afi_t afi, safi_t safi,
+ struct prefix_rd *prd)
+{
+ int ret;
+ struct prefix match;
+ struct bgp_dest *dest;
+ struct bgp_dest *rm;
+ struct bgp *bgp;
+ struct bgp_table *table;
+ struct bgp_table *rib;
+
+ /* 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);
+ rib = bgp->rib[afi][safi];
+
+ if (safi == SAFI_MPLS_VPN) {
+ 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 == NULL)
+ continue;
+
+ rm = bgp_node_match(table, &match);
+ if (rm != NULL) {
+ const struct prefix *rm_p =
+ bgp_dest_get_prefix(rm);
+
+ if (rm_p->prefixlen == match.prefixlen) {
+ SET_FLAG(rm->flags,
+ BGP_NODE_USER_CLEAR);
+ bgp_process(bgp, rm, afi, safi);
+ }
+ bgp_dest_unlock_node(rm);
+ }
+ }
+ } else {
+ dest = bgp_node_match(rib, &match);
+ if (dest != NULL) {
+ const struct prefix *dest_p = bgp_dest_get_prefix(dest);
+
+ if (dest_p->prefixlen == match.prefixlen) {
+ SET_FLAG(dest->flags, BGP_NODE_USER_CLEAR);
+ bgp_process(bgp, dest, afi, safi);
+ }
+ bgp_dest_unlock_node(dest);
+ }
+ }
+
+ return CMD_SUCCESS;
+}
+
+/* one clear bgp command to rule them all */
+DEFUN (clear_ip_bgp_all,
+ clear_ip_bgp_all_cmd,
+ "clear [ip] bgp [<view|vrf> VIEWVRFNAME] [<ipv4|ipv6|l2vpn> [<unicast|multicast|vpn|labeled-unicast|flowspec|evpn>]] <*|A.B.C.D$neighbor|X:X::X:X$neighbor|WORD$neighbor|(1-4294967295)|external|peer-group PGNAME> [<soft [<in|out>]|in [prefix-filter]|out|message-stats>]",
+ CLEAR_STR
+ IP_STR
+ BGP_STR
+ BGP_INSTANCE_HELP_STR
+ BGP_AFI_HELP_STR
+ BGP_AF_STR
+ BGP_SAFI_WITH_LABEL_HELP_STR
+ BGP_AF_MODIFIER_STR
+ "Clear all peers\n"
+ "BGP IPv4 neighbor to clear\n"
+ "BGP IPv6 neighbor to clear\n"
+ "BGP neighbor on interface to clear\n"
+ "Clear peers with the AS number\n"
+ "Clear all external peers\n"
+ "Clear all members of peer-group\n"
+ "BGP peer-group name\n"
+ BGP_SOFT_STR
+ BGP_SOFT_IN_STR
+ BGP_SOFT_OUT_STR
+ BGP_SOFT_IN_STR
+ "Push out prefix-list ORF and do inbound soft reconfig\n"
+ BGP_SOFT_OUT_STR
+ "Reset message statistics\n")
+{
+ char *vrf = NULL;
+
+ afi_t afi = AFI_UNSPEC;
+ safi_t safi = SAFI_UNSPEC;
+ enum clear_sort clr_sort = clear_peer;
+ enum bgp_clear_type clr_type;
+ char *clr_arg = NULL;
+
+ int idx = 0;
+
+ /* clear [ip] bgp */
+ if (argv_find(argv, argc, "ip", &idx))
+ afi = AFI_IP;
+
+ /* [<vrf> VIEWVRFNAME] */
+ if (argv_find(argv, argc, "vrf", &idx)) {
+ vrf = argv[idx + 1]->arg;
+ idx += 2;
+ if (vrf && strmatch(vrf, VRF_DEFAULT_NAME))
+ vrf = NULL;
+ } else if (argv_find(argv, argc, "view", &idx)) {
+ /* [<view> VIEWVRFNAME] */
+ vrf = argv[idx + 1]->arg;
+ idx += 2;
+ }
+ /* ["BGP_AFI_CMD_STR" ["BGP_SAFI_CMD_STR"]] */
+ if (argv_find_and_parse_afi(argv, argc, &idx, &afi))
+ argv_find_and_parse_safi(argv, argc, &idx, &safi);
+
+ /* <*|A.B.C.D|X:X::X:X|WORD|(1-4294967295)|external|peer-group PGNAME> */
+ if (argv_find(argv, argc, "*", &idx)) {
+ clr_sort = clear_all;
+ } else if (argv_find(argv, argc, "A.B.C.D", &idx)) {
+ clr_sort = clear_peer;
+ clr_arg = argv[idx]->arg;
+ } else if (argv_find(argv, argc, "X:X::X:X", &idx)) {
+ clr_sort = clear_peer;
+ clr_arg = argv[idx]->arg;
+ } else if (argv_find(argv, argc, "peer-group", &idx)) {
+ clr_sort = clear_group;
+ idx++;
+ clr_arg = argv[idx]->arg;
+ } else if (argv_find(argv, argc, "PGNAME", &idx)) {
+ clr_sort = clear_peer;
+ clr_arg = argv[idx]->arg;
+ } else if (argv_find(argv, argc, "WORD", &idx)) {
+ clr_sort = clear_peer;
+ clr_arg = argv[idx]->arg;
+ } else if (argv_find(argv, argc, "(1-4294967295)", &idx)) {
+ clr_sort = clear_as;
+ clr_arg = argv[idx]->arg;
+ } else if (argv_find(argv, argc, "external", &idx)) {
+ clr_sort = clear_external;
+ }
+
+ /* [<soft [<in|out>]|in [prefix-filter]|out|message-stats>] */
+ if (argv_find(argv, argc, "soft", &idx)) {
+ if (argv_find(argv, argc, "in", &idx)
+ || argv_find(argv, argc, "out", &idx))
+ clr_type = strmatch(argv[idx]->text, "in")
+ ? BGP_CLEAR_SOFT_IN
+ : BGP_CLEAR_SOFT_OUT;
+ else
+ clr_type = BGP_CLEAR_SOFT_BOTH;
+ } else if (argv_find(argv, argc, "in", &idx)) {
+ clr_type = argv_find(argv, argc, "prefix-filter", &idx)
+ ? BGP_CLEAR_SOFT_IN_ORF_PREFIX
+ : BGP_CLEAR_SOFT_IN;
+ } else if (argv_find(argv, argc, "out", &idx)) {
+ clr_type = BGP_CLEAR_SOFT_OUT;
+ } else if (argv_find(argv, argc, "message-stats", &idx)) {
+ clr_type = BGP_CLEAR_MESSAGE_STATS;
+ } else
+ clr_type = BGP_CLEAR_SOFT_NONE;
+
+ return bgp_clear_vty(vty, vrf, afi, safi, clr_sort, clr_type, clr_arg);
+}
+
+DEFUN (clear_ip_bgp_prefix,
+ clear_ip_bgp_prefix_cmd,
+ "clear [ip] bgp [<view|vrf> VIEWVRFNAME] prefix A.B.C.D/M",
+ CLEAR_STR
+ IP_STR
+ BGP_STR
+ BGP_INSTANCE_HELP_STR
+ "Clear bestpath and re-advertise\n"
+ "IPv4 prefix\n")
+{
+ char *vrf = NULL;
+ char *prefix = NULL;
+
+ int idx = 0;
+
+ /* [<view|vrf> VIEWVRFNAME] */
+ if (argv_find(argv, argc, "vrf", &idx)) {
+ vrf = argv[idx + 1]->arg;
+ idx += 2;
+ if (vrf && strmatch(vrf, VRF_DEFAULT_NAME))
+ vrf = NULL;
+ } else if (argv_find(argv, argc, "view", &idx)) {
+ /* [<view> VIEWVRFNAME] */
+ vrf = argv[idx + 1]->arg;
+ idx += 2;
+ }
+
+ prefix = argv[argc - 1]->arg;
+
+ return bgp_clear_prefix(vty, vrf, prefix, AFI_IP, SAFI_UNICAST, NULL);
+}
+
+DEFUN (clear_bgp_ipv6_safi_prefix,
+ clear_bgp_ipv6_safi_prefix_cmd,
+ "clear [ip] bgp ipv6 "BGP_SAFI_CMD_STR" prefix X:X::X:X/M",
+ CLEAR_STR
+ IP_STR
+ BGP_STR
+ BGP_AF_STR
+ BGP_SAFI_HELP_STR
+ "Clear bestpath and re-advertise\n"
+ "IPv6 prefix\n")
+{
+ int idx_safi = 0;
+ int idx_ipv6_prefix = 0;
+ safi_t safi = SAFI_UNICAST;
+ char *prefix = argv_find(argv, argc, "X:X::X:X/M", &idx_ipv6_prefix) ?
+ argv[idx_ipv6_prefix]->arg : NULL;
+
+ argv_find_and_parse_safi(argv, argc, &idx_safi, &safi);
+ return bgp_clear_prefix(
+ vty, NULL, prefix, AFI_IP6,
+ safi, NULL);
+}
+
+DEFUN (clear_bgp_instance_ipv6_safi_prefix,
+ clear_bgp_instance_ipv6_safi_prefix_cmd,
+ "clear [ip] bgp <view|vrf> VIEWVRFNAME ipv6 "BGP_SAFI_CMD_STR" prefix X:X::X:X/M",
+ CLEAR_STR
+ IP_STR
+ BGP_STR
+ BGP_INSTANCE_HELP_STR
+ BGP_AF_STR
+ BGP_SAFI_HELP_STR
+ "Clear bestpath and re-advertise\n"
+ "IPv6 prefix\n")
+{
+ int idx_safi = 0;
+ int idx_vrfview = 0;
+ int idx_ipv6_prefix = 0;
+ safi_t safi = SAFI_UNICAST;
+ char *prefix = argv_find(argv, argc, "X:X::X:X/M", &idx_ipv6_prefix) ?
+ argv[idx_ipv6_prefix]->arg : NULL;
+ char *vrfview = NULL;
+
+ /* [<view|vrf> VIEWVRFNAME] */
+ if (argv_find(argv, argc, "vrf", &idx_vrfview)) {
+ vrfview = argv[idx_vrfview + 1]->arg;
+ if (vrfview && strmatch(vrfview, VRF_DEFAULT_NAME))
+ vrfview = NULL;
+ } else if (argv_find(argv, argc, "view", &idx_vrfview)) {
+ /* [<view> VIEWVRFNAME] */
+ vrfview = argv[idx_vrfview + 1]->arg;
+ }
+ argv_find_and_parse_safi(argv, argc, &idx_safi, &safi);
+
+ return bgp_clear_prefix(
+ vty, vrfview, prefix,
+ AFI_IP6, safi, NULL);
+}
+
+DEFUN (show_bgp_views,
+ show_bgp_views_cmd,
+ "show [ip] bgp views",
+ SHOW_STR
+ IP_STR
+ BGP_STR
+ "Show the defined BGP views\n")
+{
+ struct list *inst = bm->bgp;
+ struct listnode *node;
+ struct bgp *bgp;
+
+ vty_out(vty, "Defined BGP views:\n");
+ for (ALL_LIST_ELEMENTS_RO(inst, node, bgp)) {
+ /* Skip VRFs. */
+ if (bgp->inst_type == BGP_INSTANCE_TYPE_VRF)
+ continue;
+ vty_out(vty, "\t%s (AS%u)\n", bgp->name ? bgp->name : "(null)",
+ bgp->as);
+ }
+
+ return CMD_SUCCESS;
+}
+
+DEFUN (show_bgp_vrfs,
+ show_bgp_vrfs_cmd,
+ "show [ip] bgp vrfs [json]",
+ SHOW_STR
+ IP_STR
+ BGP_STR
+ "Show BGP VRFs\n"
+ JSON_STR)
+{
+ char buf[ETHER_ADDR_STRLEN];
+ struct list *inst = bm->bgp;
+ struct listnode *node;
+ struct bgp *bgp;
+ bool uj = use_json(argc, argv);
+ json_object *json = NULL;
+ json_object *json_vrfs = NULL;
+ int count = 0;
+
+ if (uj) {
+ json = json_object_new_object();
+ json_vrfs = json_object_new_object();
+ }
+
+ for (ALL_LIST_ELEMENTS_RO(inst, node, bgp)) {
+ const char *name, *type;
+ struct peer *peer;
+ struct listnode *node2, *nnode2;
+ int peers_cfg, peers_estb;
+ json_object *json_vrf = NULL;
+
+ /* Skip Views. */
+ if (bgp->inst_type == BGP_INSTANCE_TYPE_VIEW)
+ continue;
+
+ count++;
+ if (!uj && count == 1) {
+ vty_out(vty,
+ "%4s %-5s %-16s %9s %10s %-37s\n",
+ "Type", "Id", "routerId", "#PeersCfg",
+ "#PeersEstb", "Name");
+ vty_out(vty, "%11s %-16s %-21s %-6s\n", " ",
+ "L3-VNI", "RouterMAC", "Interface");
+ }
+
+ peers_cfg = peers_estb = 0;
+ if (uj)
+ json_vrf = json_object_new_object();
+
+
+ for (ALL_LIST_ELEMENTS(bgp->peer, node2, nnode2, peer)) {
+ if (!CHECK_FLAG(peer->flags, PEER_FLAG_CONFIG_NODE))
+ continue;
+ peers_cfg++;
+ if (peer_established(peer))
+ peers_estb++;
+ }
+
+ if (bgp->inst_type == BGP_INSTANCE_TYPE_DEFAULT) {
+ name = VRF_DEFAULT_NAME;
+ type = "DFLT";
+ } else {
+ name = bgp->name;
+ type = "VRF";
+ }
+
+
+ if (uj) {
+ int64_t vrf_id_ui = (bgp->vrf_id == VRF_UNKNOWN)
+ ? -1
+ : (int64_t)bgp->vrf_id;
+ char buf[BUFSIZ] = {0};
+
+ json_object_string_add(json_vrf, "type", type);
+ json_object_int_add(json_vrf, "vrfId", vrf_id_ui);
+ json_object_string_addf(json_vrf, "routerId", "%pI4",
+ &bgp->router_id);
+ json_object_int_add(json_vrf, "numConfiguredPeers",
+ peers_cfg);
+ json_object_int_add(json_vrf, "numEstablishedPeers",
+ peers_estb);
+
+ json_object_int_add(json_vrf, "l3vni", bgp->l3vni);
+ json_object_string_add(
+ json_vrf, "rmac",
+ prefix_mac2str(&bgp->rmac, buf, sizeof(buf)));
+ json_object_string_add(json_vrf, "interface",
+ ifindex2ifname(bgp->l3vni_svi_ifindex,
+ bgp->vrf_id));
+ json_object_object_add(json_vrfs, name, json_vrf);
+ } else {
+ vty_out(vty, "%4s %-5d %-16pI4 %-9u %-10u %-37s\n",
+ type,
+ bgp->vrf_id == VRF_UNKNOWN ? -1
+ : (int)bgp->vrf_id,
+ &bgp->router_id, peers_cfg, peers_estb, name);
+ vty_out(vty,"%11s %-16u %-21s %-20s\n", " ",
+ bgp->l3vni,
+ prefix_mac2str(&bgp->rmac, buf, sizeof(buf)),
+ ifindex2ifname(bgp->l3vni_svi_ifindex,
+ bgp->vrf_id));
+ }
+ }
+
+ if (uj) {
+ json_object_object_add(json, "vrfs", json_vrfs);
+
+ json_object_int_add(json, "totalVrfs", count);
+
+ vty_json(vty, json);
+ } else {
+ if (count)
+ vty_out(vty,
+ "\nTotal number of VRFs (including default): %d\n",
+ count);
+ }
+
+ return CMD_SUCCESS;
+}
+
+DEFUN (show_bgp_mac_hash,
+ show_bgp_mac_hash_cmd,
+ "show bgp mac hash",
+ SHOW_STR
+ BGP_STR
+ "Mac Address\n"
+ "Mac Address database\n")
+{
+ bgp_mac_dump_table(vty);
+
+ return CMD_SUCCESS;
+}
+
+static void show_tip_entry(struct hash_bucket *bucket, void *args)
+{
+ struct vty *vty = (struct vty *)args;
+ struct tip_addr *tip = (struct tip_addr *)bucket->data;
+
+ vty_out(vty, "addr: %pI4, count: %d\n", &tip->addr, tip->refcnt);
+}
+
+static void bgp_show_martian_nexthops(struct vty *vty, struct bgp *bgp)
+{
+ vty_out(vty, "self nexthop database:\n");
+ bgp_nexthop_show_address_hash(vty, bgp);
+
+ vty_out(vty, "Tunnel-ip database:\n");
+ hash_iterate(bgp->tip_hash,
+ (void (*)(struct hash_bucket *, void *))show_tip_entry,
+ vty);
+}
+
+DEFUN(show_bgp_martian_nexthop_db, show_bgp_martian_nexthop_db_cmd,
+ "show bgp [<view|vrf> VIEWVRFNAME] martian next-hop",
+ SHOW_STR BGP_STR BGP_INSTANCE_HELP_STR
+ "martian next-hops\n"
+ "martian next-hop database\n")
+{
+ struct bgp *bgp = NULL;
+ int idx = 0;
+ char *name = NULL;
+
+ /* [<vrf> VIEWVRFNAME] */
+ if (argv_find(argv, argc, "vrf", &idx)) {
+ name = argv[idx + 1]->arg;
+ if (name && strmatch(name, VRF_DEFAULT_NAME))
+ name = NULL;
+ } else if (argv_find(argv, argc, "view", &idx))
+ /* [<view> VIEWVRFNAME] */
+ name = argv[idx + 1]->arg;
+ if (name)
+ bgp = bgp_lookup_by_name(name);
+ else
+ bgp = bgp_get_default();
+
+ if (!bgp) {
+ vty_out(vty, "%% No BGP process is configured\n");
+ return CMD_WARNING;
+ }
+ bgp_show_martian_nexthops(vty, bgp);
+
+ return CMD_SUCCESS;
+}
+
+DEFUN (show_bgp_memory,
+ show_bgp_memory_cmd,
+ "show [ip] bgp memory",
+ SHOW_STR
+ IP_STR
+ BGP_STR
+ "Global BGP memory statistics\n")
+{
+ char memstrbuf[MTYPE_MEMSTR_LEN];
+ unsigned long count;
+
+ /* RIB related usage stats */
+ count = mtype_stats_alloc(MTYPE_BGP_NODE);
+ vty_out(vty, "%ld RIB nodes, using %s of memory\n", count,
+ mtype_memstr(memstrbuf, sizeof(memstrbuf),
+ count * sizeof(struct bgp_dest)));
+
+ count = mtype_stats_alloc(MTYPE_BGP_ROUTE);
+ vty_out(vty, "%ld BGP routes, using %s of memory\n", count,
+ mtype_memstr(memstrbuf, sizeof(memstrbuf),
+ count * sizeof(struct bgp_path_info)));
+ if ((count = mtype_stats_alloc(MTYPE_BGP_ROUTE_EXTRA)))
+ vty_out(vty, "%ld BGP route ancillaries, using %s of memory\n",
+ count,
+ mtype_memstr(
+ memstrbuf, sizeof(memstrbuf),
+ count * sizeof(struct bgp_path_info_extra)));
+
+ if ((count = mtype_stats_alloc(MTYPE_BGP_STATIC)))
+ vty_out(vty, "%ld Static routes, using %s of memory\n", count,
+ mtype_memstr(memstrbuf, sizeof(memstrbuf),
+ count * sizeof(struct bgp_static)));
+
+ if ((count = mtype_stats_alloc(MTYPE_BGP_PACKET)))
+ vty_out(vty, "%ld Packets, using %s of memory\n", count,
+ mtype_memstr(memstrbuf, sizeof(memstrbuf),
+ count * sizeof(struct bpacket)));
+
+ /* Adj-In/Out */
+ if ((count = mtype_stats_alloc(MTYPE_BGP_ADJ_IN)))
+ vty_out(vty, "%ld Adj-In entries, using %s of memory\n", count,
+ mtype_memstr(memstrbuf, sizeof(memstrbuf),
+ count * sizeof(struct bgp_adj_in)));
+ if ((count = mtype_stats_alloc(MTYPE_BGP_ADJ_OUT)))
+ vty_out(vty, "%ld Adj-Out entries, using %s of memory\n", count,
+ mtype_memstr(memstrbuf, sizeof(memstrbuf),
+ count * sizeof(struct bgp_adj_out)));
+
+ if ((count = mtype_stats_alloc(MTYPE_BGP_NEXTHOP_CACHE)))
+ vty_out(vty, "%ld Nexthop cache entries, using %s of memory\n",
+ count,
+ mtype_memstr(memstrbuf, sizeof(memstrbuf),
+ count * sizeof(struct bgp_nexthop_cache)));
+
+ if ((count = mtype_stats_alloc(MTYPE_BGP_DAMP_INFO)))
+ vty_out(vty, "%ld Dampening entries, using %s of memory\n",
+ count,
+ mtype_memstr(memstrbuf, sizeof(memstrbuf),
+ count * sizeof(struct bgp_damp_info)));
+
+ /* Attributes */
+ count = attr_count();
+ vty_out(vty, "%ld BGP attributes, using %s of memory\n", count,
+ mtype_memstr(memstrbuf, sizeof(memstrbuf),
+ count * sizeof(struct attr)));
+
+ if ((count = attr_unknown_count()))
+ vty_out(vty, "%ld unknown attributes\n", count);
+
+ /* AS_PATH attributes */
+ count = aspath_count();
+ vty_out(vty, "%ld BGP AS-PATH entries, using %s of memory\n", count,
+ mtype_memstr(memstrbuf, sizeof(memstrbuf),
+ count * sizeof(struct aspath)));
+
+ count = mtype_stats_alloc(MTYPE_AS_SEG);
+ vty_out(vty, "%ld BGP AS-PATH segments, using %s of memory\n", count,
+ mtype_memstr(memstrbuf, sizeof(memstrbuf),
+ count * sizeof(struct assegment)));
+
+ /* Other attributes */
+ if ((count = community_count()))
+ vty_out(vty, "%ld BGP community entries, using %s of memory\n",
+ count, mtype_memstr(memstrbuf, sizeof(memstrbuf),
+ count * sizeof(struct community)));
+ if ((count = mtype_stats_alloc(MTYPE_ECOMMUNITY)))
+ vty_out(vty,
+ "%ld BGP ext-community entries, using %s of memory\n",
+ count,
+ mtype_memstr(memstrbuf, sizeof(memstrbuf),
+ count * sizeof(struct ecommunity)));
+ if ((count = mtype_stats_alloc(MTYPE_LCOMMUNITY)))
+ vty_out(vty,
+ "%ld BGP large-community entries, using %s of memory\n",
+ count, mtype_memstr(memstrbuf, sizeof(memstrbuf),
+ count * sizeof(struct lcommunity)));
+
+ if ((count = mtype_stats_alloc(MTYPE_CLUSTER)))
+ vty_out(vty, "%ld Cluster lists, using %s of memory\n", count,
+ mtype_memstr(memstrbuf, sizeof(memstrbuf),
+ count * sizeof(struct cluster_list)));
+
+ /* Peer related usage */
+ count = mtype_stats_alloc(MTYPE_BGP_PEER);
+ vty_out(vty, "%ld peers, using %s of memory\n", count,
+ mtype_memstr(memstrbuf, sizeof(memstrbuf),
+ count * sizeof(struct peer)));
+
+ if ((count = mtype_stats_alloc(MTYPE_PEER_GROUP)))
+ vty_out(vty, "%ld peer groups, using %s of memory\n", count,
+ mtype_memstr(memstrbuf, sizeof(memstrbuf),
+ count * sizeof(struct peer_group)));
+
+ /* Other */
+ if ((count = mtype_stats_alloc(MTYPE_BGP_REGEXP)))
+ vty_out(vty, "%ld compiled regexes, using %s of memory\n",
+ count, mtype_memstr(memstrbuf, sizeof(memstrbuf),
+ count * sizeof(regex_t)));
+ return CMD_SUCCESS;
+}
+
+static void bgp_show_bestpath_json(struct bgp *bgp, json_object *json)
+{
+ json_object *bestpath = json_object_new_object();
+
+ if (CHECK_FLAG(bgp->flags, BGP_FLAG_ASPATH_IGNORE))
+ json_object_string_add(bestpath, "asPath", "ignore");
+
+ if (CHECK_FLAG(bgp->flags, BGP_FLAG_ASPATH_CONFED))
+ json_object_string_add(bestpath, "asPath", "confed");
+
+ if (CHECK_FLAG(bgp->flags, BGP_FLAG_ASPATH_MULTIPATH_RELAX)) {
+ if (CHECK_FLAG(bgp->flags, BGP_FLAG_MULTIPATH_RELAX_AS_SET))
+ json_object_string_add(bestpath, "multiPathRelax",
+ "as-set");
+ else
+ json_object_string_add(bestpath, "multiPathRelax",
+ "true");
+ } else
+ json_object_string_add(bestpath, "multiPathRelax", "false");
+
+ if (CHECK_FLAG(bgp->flags, BGP_FLAG_PEERTYPE_MULTIPATH_RELAX))
+ json_object_boolean_true_add(bestpath, "peerTypeRelax");
+
+ if (CHECK_FLAG(bgp->flags, BGP_FLAG_COMPARE_ROUTER_ID))
+ json_object_string_add(bestpath, "compareRouterId", "true");
+ if (CHECK_FLAG(bgp->flags, BGP_FLAG_MED_CONFED)
+ || CHECK_FLAG(bgp->flags, BGP_FLAG_MED_MISSING_AS_WORST)) {
+ if (CHECK_FLAG(bgp->flags, BGP_FLAG_MED_CONFED))
+ json_object_string_add(bestpath, "med", "confed");
+ if (CHECK_FLAG(bgp->flags, BGP_FLAG_MED_MISSING_AS_WORST))
+ json_object_string_add(bestpath, "med",
+ "missing-as-worst");
+ else
+ json_object_string_add(bestpath, "med", "true");
+ }
+
+ json_object_object_add(json, "bestPath", bestpath);
+}
+
+/* Print the error code/subcode for why the peer is down */
+static void bgp_show_peer_reset(struct vty * vty, struct peer *peer,
+ json_object *json_peer, bool use_json)
+{
+ const char *code_str;
+ const char *subcode_str;
+
+ if (use_json) {
+ if (peer->last_reset == PEER_DOWN_NOTIFY_SEND
+ || peer->last_reset == PEER_DOWN_NOTIFY_RECEIVED) {
+ char errorcodesubcode_hexstr[5];
+ char errorcodesubcode_str[256];
+
+ code_str = bgp_notify_code_str(peer->notify.code);
+ subcode_str = bgp_notify_subcode_str(
+ peer->notify.code,
+ peer->notify.subcode);
+
+ snprintf(errorcodesubcode_hexstr,
+ sizeof(errorcodesubcode_hexstr), "%02X%02X",
+ peer->notify.code, peer->notify.subcode);
+ json_object_string_add(json_peer,
+ "lastErrorCodeSubcode",
+ errorcodesubcode_hexstr);
+ snprintf(errorcodesubcode_str, 255, "%s%s",
+ code_str, subcode_str);
+ json_object_string_add(json_peer,
+ "lastNotificationReason",
+ errorcodesubcode_str);
+ json_object_boolean_add(json_peer,
+ "lastNotificationHardReset",
+ peer->notify.hard_reset);
+ if (peer->last_reset == PEER_DOWN_NOTIFY_RECEIVED
+ && peer->notify.code == BGP_NOTIFY_CEASE
+ && (peer->notify.subcode
+ == BGP_NOTIFY_CEASE_ADMIN_SHUTDOWN
+ || peer->notify.subcode
+ == BGP_NOTIFY_CEASE_ADMIN_RESET)
+ && peer->notify.length) {
+ char msgbuf[1024];
+ const char *msg_str;
+
+ msg_str = bgp_notify_admin_message(
+ msgbuf, sizeof(msgbuf),
+ (uint8_t *)peer->notify.data,
+ peer->notify.length);
+ if (msg_str)
+ json_object_string_add(
+ json_peer,
+ "lastShutdownDescription",
+ msg_str);
+ }
+
+ }
+ json_object_string_add(json_peer, "lastResetDueTo",
+ peer_down_str[(int)peer->last_reset]);
+ json_object_int_add(json_peer, "lastResetCode",
+ peer->last_reset);
+ } else {
+ if (peer->last_reset == PEER_DOWN_NOTIFY_SEND
+ || peer->last_reset == PEER_DOWN_NOTIFY_RECEIVED) {
+ code_str = bgp_notify_code_str(peer->notify.code);
+ subcode_str =
+ bgp_notify_subcode_str(peer->notify.code,
+ peer->notify.subcode);
+ vty_out(vty, " Notification %s (%s%s%s)\n",
+ peer->last_reset == PEER_DOWN_NOTIFY_SEND
+ ? "sent"
+ : "received",
+ code_str, subcode_str,
+ peer->notify.hard_reset
+ ? bgp_notify_subcode_str(
+ BGP_NOTIFY_CEASE,
+ BGP_NOTIFY_CEASE_HARD_RESET)
+ : "");
+ } else {
+ vty_out(vty, " %s\n",
+ peer_down_str[(int)peer->last_reset]);
+ }
+ }
+}
+
+static inline bool bgp_has_peer_failed(struct peer *peer, afi_t afi,
+ safi_t safi)
+{
+ return ((!peer_established(peer)) || !peer->afc_recv[afi][safi]);
+}
+
+static void bgp_show_failed_summary(struct vty *vty, struct bgp *bgp,
+ struct peer *peer, json_object *json_peer,
+ int max_neighbor_width, bool use_json)
+{
+ char timebuf[BGP_UPTIME_LEN], dn_flag[2];
+ int len;
+
+ if (use_json) {
+ if (peer_dynamic_neighbor(peer))
+ json_object_boolean_true_add(json_peer,
+ "dynamicPeer");
+ if (peer->hostname)
+ json_object_string_add(json_peer, "hostname",
+ peer->hostname);
+
+ if (peer->domainname)
+ json_object_string_add(json_peer, "domainname",
+ peer->domainname);
+ json_object_int_add(json_peer, "connectionsEstablished",
+ peer->established);
+ json_object_int_add(json_peer, "connectionsDropped",
+ peer->dropped);
+ peer_uptime(peer->uptime, timebuf, BGP_UPTIME_LEN,
+ use_json, json_peer);
+ if (peer_established(peer))
+ json_object_string_add(json_peer, "lastResetDueTo",
+ "AFI/SAFI Not Negotiated");
+ else
+ bgp_show_peer_reset(NULL, peer, json_peer, true);
+ } else {
+ dn_flag[1] = '\0';
+ dn_flag[0] = peer_dynamic_neighbor(peer) ? '*' : '\0';
+ if (peer->hostname
+ && CHECK_FLAG(bgp->flags, BGP_FLAG_SHOW_HOSTNAME))
+ len = vty_out(vty, "%s%s(%s)", dn_flag,
+ peer->hostname, peer->host);
+ else
+ len = vty_out(vty, "%s%s", dn_flag, peer->host);
+
+ /* pad the neighbor column with spaces */
+ if (len < max_neighbor_width)
+ vty_out(vty, "%*s", max_neighbor_width - len,
+ " ");
+ vty_out(vty, "%7d %7d %9s", peer->established,
+ peer->dropped,
+ peer_uptime(peer->uptime, timebuf,
+ BGP_UPTIME_LEN, 0, NULL));
+ if (peer_established(peer))
+ vty_out(vty, " AFI/SAFI Not Negotiated\n");
+ else
+ bgp_show_peer_reset(vty, peer, NULL,
+ false);
+ }
+}
+
+/* Strip peer's description to the given size. */
+static char *bgp_peer_description_stripped(char *desc, uint32_t size)
+{
+ static char stripped[BUFSIZ];
+ uint32_t i = 0;
+ uint32_t last_space = 0;
+
+ while (i < size) {
+ if (*(desc + i) == 0) {
+ stripped[i] = '\0';
+ return stripped;
+ }
+ if (i != 0 && *(desc + i) == ' ' && last_space != i - 1)
+ last_space = i;
+ stripped[i] = *(desc + i);
+ i++;
+ }
+
+ if (last_space > size)
+ stripped[size + 1] = '\0';
+ else
+ stripped[last_space] = '\0';
+
+ return stripped;
+}
+
+/* Determine whether var peer should be filtered out of the summary. */
+static bool bgp_show_summary_is_peer_filtered(struct peer *peer,
+ struct peer *fpeer, int as_type,
+ as_t as)
+{
+
+ /* filter neighbor XXXX */
+ if (fpeer && fpeer != peer)
+ return true;
+
+ /* filter remote-as (internal|external) */
+ if (as_type != AS_UNSPECIFIED) {
+ if (peer->as_type == AS_SPECIFIED) {
+ if (as_type == AS_INTERNAL) {
+ if (peer->as != peer->local_as)
+ return true;
+ } else if (peer->as == peer->local_as)
+ return true;
+ } else if (as_type != peer->as_type)
+ return true;
+ } else if (as && as != peer->as) /* filter remote-as XXX */
+ return true;
+
+ return false;
+}
+
+/* Show BGP peer's summary information.
+ *
+ * Peer's description is stripped according to if `wide` option is given
+ * or not.
+ *
+ * When adding new columns to `show bgp summary` output, please make
+ * sure `Desc` is the lastest column to show because it can contain
+ * whitespaces and the whole output will be tricky.
+ */
+static int bgp_show_summary(struct vty *vty, struct bgp *bgp, int afi, int safi,
+ struct peer *fpeer, int as_type, as_t as,
+ uint16_t show_flags)
+{
+ struct peer *peer;
+ struct listnode *node, *nnode;
+ unsigned int count = 0, dn_count = 0;
+ char timebuf[BGP_UPTIME_LEN], dn_flag[2];
+ char neighbor_buf[VTY_BUFSIZ];
+ int neighbor_col_default_width = 16;
+ int len, failed_count = 0;
+ unsigned int filtered_count = 0;
+ int max_neighbor_width = 0;
+ int pfx_rcd_safi;
+ json_object *json = NULL;
+ json_object *json_peer = NULL;
+ json_object *json_peers = NULL;
+ struct peer_af *paf;
+ struct bgp_filter *filter;
+ bool use_json = CHECK_FLAG(show_flags, BGP_SHOW_OPT_JSON);
+ bool show_failed = CHECK_FLAG(show_flags, BGP_SHOW_OPT_FAILED);
+ bool show_established =
+ CHECK_FLAG(show_flags, BGP_SHOW_OPT_ESTABLISHED);
+ bool show_wide = CHECK_FLAG(show_flags, BGP_SHOW_OPT_WIDE);
+ bool show_terse = CHECK_FLAG(show_flags, BGP_SHOW_OPT_TERSE);
+
+ /* labeled-unicast routes are installed in the unicast table so in order
+ * to
+ * display the correct PfxRcd value we must look at SAFI_UNICAST
+ */
+
+ if (safi == SAFI_LABELED_UNICAST)
+ pfx_rcd_safi = SAFI_UNICAST;
+ else
+ pfx_rcd_safi = safi;
+
+ if (use_json) {
+ json = json_object_new_object();
+ json_peers = json_object_new_object();
+ for (ALL_LIST_ELEMENTS(bgp->peer, node, nnode, peer)) {
+ if (bgp_show_summary_is_peer_filtered(peer, fpeer,
+ as_type, as)) {
+ filtered_count++;
+ count++;
+ continue;
+ }
+
+ if (!CHECK_FLAG(peer->flags, PEER_FLAG_CONFIG_NODE))
+ continue;
+
+ if (peer->afc[afi][safi]) {
+ /* See if we have at least a single failed peer */
+ if (bgp_has_peer_failed(peer, afi, safi))
+ failed_count++;
+ count++;
+ }
+ if (peer_dynamic_neighbor(peer))
+ dn_count++;
+ }
+
+ } else {
+ /* Loop over all neighbors that will be displayed to determine
+ * how many
+ * characters are needed for the Neighbor column
+ */
+ for (ALL_LIST_ELEMENTS(bgp->peer, node, nnode, peer)) {
+ if (bgp_show_summary_is_peer_filtered(peer, fpeer,
+ as_type, as)) {
+ filtered_count++;
+ count++;
+ continue;
+ }
+
+ if (!CHECK_FLAG(peer->flags, PEER_FLAG_CONFIG_NODE))
+ continue;
+
+ if (peer->afc[afi][safi]) {
+ memset(dn_flag, '\0', sizeof(dn_flag));
+ if (peer_dynamic_neighbor(peer))
+ dn_flag[0] = '*';
+
+ if (peer->hostname
+ && CHECK_FLAG(bgp->flags,
+ BGP_FLAG_SHOW_HOSTNAME))
+ snprintf(neighbor_buf,
+ sizeof(neighbor_buf),
+ "%s%s(%s) ", dn_flag,
+ peer->hostname, peer->host);
+ else
+ snprintf(neighbor_buf,
+ sizeof(neighbor_buf), "%s%s ",
+ dn_flag, peer->host);
+
+ len = strlen(neighbor_buf);
+
+ if (len > max_neighbor_width)
+ max_neighbor_width = len;
+
+ /* See if we have at least a single failed peer */
+ if (bgp_has_peer_failed(peer, afi, safi))
+ failed_count++;
+ count++;
+ }
+ }
+
+ /* Originally we displayed the Neighbor column as 16
+ * characters wide so make that the default
+ */
+ if (max_neighbor_width < neighbor_col_default_width)
+ max_neighbor_width = neighbor_col_default_width;
+ }
+
+ if (show_failed && !failed_count) {
+ if (use_json) {
+ json_object_int_add(json, "failedPeersCount", 0);
+ json_object_int_add(json, "dynamicPeers", dn_count);
+ json_object_int_add(json, "totalPeers", count);
+
+ vty_json(vty, json);
+ } else {
+ vty_out(vty, "%% No failed BGP neighbors found\n");
+ }
+ return CMD_SUCCESS;
+ }
+
+ count = 0; /* Reset the value as its used again */
+ filtered_count = 0;
+ dn_count = 0;
+ for (ALL_LIST_ELEMENTS(bgp->peer, node, nnode, peer)) {
+ if (!CHECK_FLAG(peer->flags, PEER_FLAG_CONFIG_NODE))
+ continue;
+
+ if (!peer->afc[afi][safi])
+ continue;
+
+ if (!count) {
+ unsigned long ents;
+ char memstrbuf[MTYPE_MEMSTR_LEN];
+ int64_t vrf_id_ui;
+
+ vrf_id_ui = (bgp->vrf_id == VRF_UNKNOWN)
+ ? -1
+ : (int64_t)bgp->vrf_id;
+
+ /* Usage summary and header */
+ if (use_json) {
+ json_object_string_addf(json, "routerId",
+ "%pI4",
+ &bgp->router_id);
+ json_object_int_add(json, "as", bgp->as);
+ json_object_int_add(json, "vrfId", vrf_id_ui);
+ json_object_string_add(
+ json, "vrfName",
+ (bgp->inst_type
+ == BGP_INSTANCE_TYPE_DEFAULT)
+ ? VRF_DEFAULT_NAME
+ : bgp->name);
+ } else {
+ vty_out(vty,
+ "BGP router identifier %pI4, local AS number %u vrf-id %d",
+ &bgp->router_id, bgp->as,
+ bgp->vrf_id == VRF_UNKNOWN
+ ? -1
+ : (int)bgp->vrf_id);
+ vty_out(vty, "\n");
+ }
+
+ if (bgp_update_delay_configured(bgp)) {
+ if (use_json) {
+ json_object_int_add(
+ json, "updateDelayLimit",
+ bgp->v_update_delay);
+
+ if (bgp->v_update_delay
+ != bgp->v_establish_wait)
+ json_object_int_add(
+ json,
+ "updateDelayEstablishWait",
+ bgp->v_establish_wait);
+
+ if (bgp_update_delay_active(bgp)) {
+ json_object_string_add(
+ json,
+ "updateDelayFirstNeighbor",
+ bgp->update_delay_begin_time);
+ json_object_boolean_true_add(
+ json,
+ "updateDelayInProgress");
+ } else {
+ if (bgp->update_delay_over) {
+ json_object_string_add(
+ json,
+ "updateDelayFirstNeighbor",
+ bgp->update_delay_begin_time);
+ json_object_string_add(
+ json,
+ "updateDelayBestpathResumed",
+ bgp->update_delay_end_time);
+ json_object_string_add(
+ json,
+ "updateDelayZebraUpdateResume",
+ bgp->update_delay_zebra_resume_time);
+ json_object_string_add(
+ json,
+ "updateDelayPeerUpdateResume",
+ bgp->update_delay_peers_resume_time);
+ }
+ }
+ } else {
+ vty_out(vty,
+ "Read-only mode update-delay limit: %d seconds\n",
+ bgp->v_update_delay);
+ if (bgp->v_update_delay
+ != bgp->v_establish_wait)
+ vty_out(vty,
+ " Establish wait: %d seconds\n",
+ bgp->v_establish_wait);
+
+ if (bgp_update_delay_active(bgp)) {
+ vty_out(vty,
+ " First neighbor established: %s\n",
+ bgp->update_delay_begin_time);
+ vty_out(vty,
+ " Delay in progress\n");
+ } else {
+ if (bgp->update_delay_over) {
+ vty_out(vty,
+ " First neighbor established: %s\n",
+ bgp->update_delay_begin_time);
+ vty_out(vty,
+ " Best-paths resumed: %s\n",
+ bgp->update_delay_end_time);
+ vty_out(vty,
+ " zebra update resumed: %s\n",
+ bgp->update_delay_zebra_resume_time);
+ vty_out(vty,
+ " peers update resumed: %s\n",
+ bgp->update_delay_peers_resume_time);
+ }
+ }
+ }
+ }
+
+ if (use_json) {
+ if (bgp_maxmed_onstartup_configured(bgp)
+ && bgp->maxmed_active)
+ json_object_boolean_true_add(
+ json, "maxMedOnStartup");
+ if (bgp->v_maxmed_admin)
+ json_object_boolean_true_add(
+ json, "maxMedAdministrative");
+
+ json_object_int_add(
+ json, "tableVersion",
+ bgp_table_version(bgp->rib[afi][safi]));
+
+ ents = bgp_table_count(bgp->rib[afi][safi]);
+ json_object_int_add(json, "ribCount", ents);
+ json_object_int_add(
+ json, "ribMemory",
+ ents * sizeof(struct bgp_dest));
+
+ ents = bgp->af_peer_count[afi][safi];
+ json_object_int_add(json, "peerCount", ents);
+ json_object_int_add(json, "peerMemory",
+ ents * sizeof(struct peer));
+
+ if ((ents = listcount(bgp->group))) {
+ json_object_int_add(
+ json, "peerGroupCount", ents);
+ json_object_int_add(
+ json, "peerGroupMemory",
+ ents * sizeof(struct
+ peer_group));
+ }
+
+ if (CHECK_FLAG(bgp->af_flags[afi][safi],
+ BGP_CONFIG_DAMPENING))
+ json_object_boolean_true_add(
+ json, "dampeningEnabled");
+ } else {
+ if (!show_terse) {
+ if (bgp_maxmed_onstartup_configured(bgp)
+ && bgp->maxmed_active)
+ vty_out(vty,
+ "Max-med on-startup active\n");
+ if (bgp->v_maxmed_admin)
+ vty_out(vty,
+ "Max-med administrative active\n");
+
+ vty_out(vty,
+ "BGP table version %" PRIu64
+ "\n",
+ bgp_table_version(
+ bgp->rib[afi][safi]));
+
+ ents = bgp_table_count(
+ bgp->rib[afi][safi]);
+ vty_out(vty,
+ "RIB entries %ld, using %s of memory\n",
+ ents,
+ mtype_memstr(
+ memstrbuf,
+ sizeof(memstrbuf),
+ ents
+ * sizeof(
+ struct
+ bgp_dest)));
+
+ /* Peer related usage */
+ ents = bgp->af_peer_count[afi][safi];
+ vty_out(vty,
+ "Peers %ld, using %s of memory\n",
+ ents,
+ mtype_memstr(
+ memstrbuf,
+ sizeof(memstrbuf),
+ ents
+ * sizeof(
+ struct
+ peer)));
+
+ if ((ents = listcount(bgp->group)))
+ vty_out(vty,
+ "Peer groups %ld, using %s of memory\n",
+ ents,
+ mtype_memstr(
+ memstrbuf,
+ sizeof(memstrbuf),
+ ents
+ * sizeof(
+ struct
+ peer_group)));
+
+ if (CHECK_FLAG(bgp->af_flags[afi][safi],
+ BGP_CONFIG_DAMPENING))
+ vty_out(vty,
+ "Dampening enabled.\n");
+ }
+ if (show_failed) {
+ vty_out(vty, "\n");
+
+ /* Subtract 8 here because 'Neighbor' is
+ * 8 characters */
+ vty_out(vty, "Neighbor");
+ vty_out(vty, "%*s",
+ max_neighbor_width - 8, " ");
+ vty_out(vty,
+ BGP_SHOW_SUMMARY_HEADER_FAILED);
+ }
+ }
+ }
+
+ paf = peer_af_find(peer, afi, safi);
+ filter = &peer->filter[afi][safi];
+
+ count++;
+ /* Works for both failed & successful cases */
+ if (peer_dynamic_neighbor(peer))
+ dn_count++;
+
+ if (use_json) {
+ json_peer = NULL;
+ if (bgp_show_summary_is_peer_filtered(peer, fpeer,
+ as_type, as)) {
+ filtered_count++;
+ continue;
+ }
+ if (show_failed &&
+ bgp_has_peer_failed(peer, afi, safi)) {
+ json_peer = json_object_new_object();
+ bgp_show_failed_summary(vty, bgp, peer,
+ json_peer, 0, use_json);
+ } else if (!show_failed) {
+ if (show_established
+ && bgp_has_peer_failed(peer, afi, safi)) {
+ filtered_count++;
+ continue;
+ }
+
+ json_peer = json_object_new_object();
+ if (peer_dynamic_neighbor(peer)) {
+ json_object_boolean_true_add(json_peer,
+ "dynamicPeer");
+ }
+
+ if (peer->hostname)
+ json_object_string_add(json_peer, "hostname",
+ peer->hostname);
+
+ if (peer->domainname)
+ json_object_string_add(json_peer, "domainname",
+ peer->domainname);
+
+ json_object_int_add(json_peer, "remoteAs", peer->as);
+ json_object_int_add(
+ json_peer, "localAs",
+ peer->change_local_as
+ ? peer->change_local_as
+ : peer->local_as);
+ json_object_int_add(json_peer, "version", 4);
+ json_object_int_add(json_peer, "msgRcvd",
+ PEER_TOTAL_RX(peer));
+ json_object_int_add(json_peer, "msgSent",
+ PEER_TOTAL_TX(peer));
+
+ atomic_size_t outq_count, inq_count;
+ outq_count = atomic_load_explicit(
+ &peer->obuf->count,
+ memory_order_relaxed);
+ inq_count = atomic_load_explicit(
+ &peer->ibuf->count,
+ memory_order_relaxed);
+
+ json_object_int_add(json_peer, "tableVersion",
+ peer->version[afi][safi]);
+ json_object_int_add(json_peer, "outq",
+ outq_count);
+ json_object_int_add(json_peer, "inq",
+ inq_count);
+ peer_uptime(peer->uptime, timebuf, BGP_UPTIME_LEN,
+ use_json, json_peer);
+
+ json_object_int_add(json_peer, "pfxRcd",
+ peer->pcount[afi][pfx_rcd_safi]);
+
+ if (paf && PAF_SUBGRP(paf))
+ json_object_int_add(
+ json_peer, "pfxSnt",
+ (PAF_SUBGRP(paf))->scount);
+ else
+ json_object_int_add(json_peer, "pfxSnt",
+ 0);
+
+ /* BGP FSM state */
+ if (CHECK_FLAG(peer->flags, PEER_FLAG_SHUTDOWN)
+ || CHECK_FLAG(peer->bgp->flags,
+ BGP_FLAG_SHUTDOWN))
+ json_object_string_add(json_peer,
+ "state",
+ "Idle (Admin)");
+ else if (peer->afc_recv[afi][safi])
+ json_object_string_add(
+ json_peer, "state",
+ lookup_msg(bgp_status_msg,
+ peer->status, NULL));
+ else if (CHECK_FLAG(
+ peer->sflags,
+ PEER_STATUS_PREFIX_OVERFLOW))
+ json_object_string_add(json_peer,
+ "state",
+ "Idle (PfxCt)");
+ else
+ json_object_string_add(
+ json_peer, "state",
+ lookup_msg(bgp_status_msg,
+ peer->status, NULL));
+
+ /* BGP peer state */
+ if (CHECK_FLAG(peer->flags, PEER_FLAG_SHUTDOWN)
+ || CHECK_FLAG(peer->bgp->flags,
+ BGP_FLAG_SHUTDOWN))
+ json_object_string_add(json_peer,
+ "peerState",
+ "Admin");
+ else if (CHECK_FLAG(
+ peer->sflags,
+ PEER_STATUS_PREFIX_OVERFLOW))
+ json_object_string_add(json_peer,
+ "peerState",
+ "PfxCt");
+ else if (CHECK_FLAG(peer->flags,
+ PEER_FLAG_PASSIVE))
+ json_object_string_add(json_peer,
+ "peerState",
+ "Passive");
+ else if (CHECK_FLAG(peer->sflags,
+ PEER_STATUS_NSF_WAIT))
+ json_object_string_add(json_peer,
+ "peerState",
+ "NSF passive");
+ else if (CHECK_FLAG(
+ peer->bgp->flags,
+ BGP_FLAG_EBGP_REQUIRES_POLICY)
+ && (!bgp_inbound_policy_exists(peer,
+ filter)
+ || !bgp_outbound_policy_exists(
+ peer, filter)))
+ json_object_string_add(json_peer,
+ "peerState",
+ "Policy");
+ else
+ json_object_string_add(
+ json_peer, "peerState", "OK");
+
+ json_object_int_add(json_peer, "connectionsEstablished",
+ peer->established);
+ json_object_int_add(json_peer, "connectionsDropped",
+ peer->dropped);
+ if (peer->desc)
+ json_object_string_add(
+ json_peer, "desc", peer->desc);
+ }
+ /* Avoid creating empty peer dicts in JSON */
+ if (json_peer == NULL)
+ continue;
+
+ if (peer->conf_if)
+ json_object_string_add(json_peer, "idType",
+ "interface");
+ else if (peer->su.sa.sa_family == AF_INET)
+ json_object_string_add(json_peer, "idType",
+ "ipv4");
+ else if (peer->su.sa.sa_family == AF_INET6)
+ json_object_string_add(json_peer, "idType",
+ "ipv6");
+ json_object_object_add(json_peers, peer->host,
+ json_peer);
+ } else {
+ if (bgp_show_summary_is_peer_filtered(peer, fpeer,
+ as_type, as)) {
+ filtered_count++;
+ continue;
+ }
+ if (show_failed &&
+ bgp_has_peer_failed(peer, afi, safi)) {
+ bgp_show_failed_summary(vty, bgp, peer, NULL,
+ max_neighbor_width,
+ use_json);
+ } else if (!show_failed) {
+ if (show_established
+ && bgp_has_peer_failed(peer, afi, safi)) {
+ filtered_count++;
+ continue;
+ }
+
+ if ((count - filtered_count) == 1) {
+ /* display headline before the first
+ * neighbor line */
+ vty_out(vty, "\n");
+
+ /* Subtract 8 here because 'Neighbor' is
+ * 8 characters */
+ vty_out(vty, "Neighbor");
+ vty_out(vty, "%*s",
+ max_neighbor_width - 8, " ");
+ vty_out(vty,
+ show_wide
+ ? BGP_SHOW_SUMMARY_HEADER_ALL_WIDE
+ : BGP_SHOW_SUMMARY_HEADER_ALL);
+ }
+
+ memset(dn_flag, '\0', sizeof(dn_flag));
+ if (peer_dynamic_neighbor(peer)) {
+ dn_flag[0] = '*';
+ }
+
+ if (peer->hostname
+ && CHECK_FLAG(bgp->flags,
+ BGP_FLAG_SHOW_HOSTNAME))
+ len = vty_out(vty, "%s%s(%s)", dn_flag,
+ peer->hostname,
+ peer->host);
+ else
+ len = vty_out(vty, "%s%s", dn_flag, peer->host);
+
+ /* pad the neighbor column with spaces */
+ if (len < max_neighbor_width)
+ vty_out(vty, "%*s", max_neighbor_width - len,
+ " ");
+
+ atomic_size_t outq_count, inq_count;
+ outq_count = atomic_load_explicit(
+ &peer->obuf->count,
+ memory_order_relaxed);
+ inq_count = atomic_load_explicit(
+ &peer->ibuf->count,
+ memory_order_relaxed);
+
+ if (show_wide)
+ vty_out(vty,
+ "4 %10u %10u %9u %9u %8" PRIu64
+ " %4zu %4zu %8s",
+ peer->as,
+ peer->change_local_as
+ ? peer->change_local_as
+ : peer->local_as,
+ PEER_TOTAL_RX(peer),
+ PEER_TOTAL_TX(peer),
+ peer->version[afi][safi],
+ inq_count, outq_count,
+ peer_uptime(peer->uptime,
+ timebuf,
+ BGP_UPTIME_LEN, 0,
+ NULL));
+ else
+ vty_out(vty, "4 %10u %9u %9u %8" PRIu64
+ " %4zu %4zu %8s",
+ peer->as, PEER_TOTAL_RX(peer),
+ PEER_TOTAL_TX(peer),
+ peer->version[afi][safi],
+ inq_count, outq_count,
+ peer_uptime(peer->uptime,
+ timebuf,
+ BGP_UPTIME_LEN, 0,
+ NULL));
+
+ if (peer_established(peer)) {
+ if (peer->afc_recv[afi][safi]) {
+ if (CHECK_FLAG(
+ bgp->flags,
+ BGP_FLAG_EBGP_REQUIRES_POLICY)
+ && !bgp_inbound_policy_exists(
+ peer, filter))
+ vty_out(vty, " %12s",
+ "(Policy)");
+ else
+ vty_out(vty,
+ " %12u",
+ peer->pcount
+ [afi]
+ [pfx_rcd_safi]);
+ } else {
+ vty_out(vty, " NoNeg");
+ }
+
+ if (paf && PAF_SUBGRP(paf)) {
+ if (CHECK_FLAG(
+ bgp->flags,
+ BGP_FLAG_EBGP_REQUIRES_POLICY)
+ && !bgp_outbound_policy_exists(
+ peer, filter))
+ vty_out(vty, " %8s",
+ "(Policy)");
+ else
+ vty_out(vty,
+ " %8u",
+ (PAF_SUBGRP(
+ paf))
+ ->scount);
+ } else {
+ vty_out(vty, " NoNeg");
+ }
+ } else {
+ if (CHECK_FLAG(peer->flags,
+ PEER_FLAG_SHUTDOWN)
+ || CHECK_FLAG(peer->bgp->flags,
+ BGP_FLAG_SHUTDOWN))
+ vty_out(vty, " Idle (Admin)");
+ else if (CHECK_FLAG(
+ peer->sflags,
+ PEER_STATUS_PREFIX_OVERFLOW))
+ vty_out(vty, " Idle (PfxCt)");
+ else
+ vty_out(vty, " %12s",
+ lookup_msg(bgp_status_msg,
+ peer->status, NULL));
+
+ vty_out(vty, " %8u", 0);
+ }
+ /* Make sure `Desc` column is the lastest in
+ * the output.
+ */
+ if (peer->desc)
+ vty_out(vty, " %s",
+ bgp_peer_description_stripped(
+ peer->desc,
+ show_wide ? 64 : 20));
+ else
+ vty_out(vty, " N/A");
+ vty_out(vty, "\n");
+ }
+
+ }
+ }
+
+ if (use_json) {
+ json_object_object_add(json, "peers", json_peers);
+ json_object_int_add(json, "failedPeers", failed_count);
+ json_object_int_add(json, "displayedPeers",
+ count - filtered_count);
+ json_object_int_add(json, "totalPeers", count);
+ json_object_int_add(json, "dynamicPeers", dn_count);
+
+ if (!show_failed)
+ bgp_show_bestpath_json(bgp, json);
+
+ vty_json(vty, json);
+ } else {
+ if (count) {
+ if (filtered_count == count)
+ vty_out(vty, "\n%% No matching neighbor\n");
+ else {
+ if (show_failed)
+ vty_out(vty, "\nDisplayed neighbors %d",
+ failed_count);
+ else if (as_type != AS_UNSPECIFIED || as
+ || fpeer || show_established)
+ vty_out(vty, "\nDisplayed neighbors %d",
+ count - filtered_count);
+
+ vty_out(vty, "\nTotal number of neighbors %d\n",
+ count);
+ }
+ } else {
+ vty_out(vty, "No %s neighbor is configured\n",
+ get_afi_safi_str(afi, safi, false));
+ }
+
+ if (dn_count) {
+ vty_out(vty, "* - dynamic neighbor\n");
+ vty_out(vty, "%d dynamic neighbor(s), limit %d\n",
+ dn_count, bgp->dynamic_neighbors_limit);
+ }
+ }
+
+ return CMD_SUCCESS;
+}
+
+static void bgp_show_summary_afi_safi(struct vty *vty, struct bgp *bgp, int afi,
+ int safi, struct peer *fpeer, int as_type,
+ as_t as, uint16_t show_flags)
+{
+ int is_first = 1;
+ int afi_wildcard = (afi == AFI_MAX);
+ int safi_wildcard = (safi == SAFI_MAX);
+ int is_wildcard = (afi_wildcard || safi_wildcard);
+ bool nbr_output = false;
+ bool use_json = CHECK_FLAG(show_flags, BGP_SHOW_OPT_JSON);
+
+ if (use_json && is_wildcard)
+ vty_out(vty, "{\n");
+ if (afi_wildcard)
+ afi = 1; /* AFI_IP */
+ while (afi < AFI_MAX) {
+ if (safi_wildcard)
+ safi = 1; /* SAFI_UNICAST */
+ while (safi < SAFI_MAX) {
+ if (bgp_afi_safi_peer_exists(bgp, afi, safi)) {
+ nbr_output = true;
+
+ if (is_wildcard) {
+ /*
+ * So limit output to those afi/safi
+ * pairs that
+ * actualy have something interesting in
+ * them
+ */
+ if (use_json) {
+ if (!is_first)
+ vty_out(vty, ",\n");
+ else
+ is_first = 0;
+
+ vty_out(vty, "\"%s\":",
+ get_afi_safi_str(afi,
+ safi,
+ true));
+ } else {
+ vty_out(vty,
+ "\n%s Summary (%s):\n",
+ get_afi_safi_str(afi,
+ safi,
+ false),
+ bgp->name_pretty);
+ }
+ }
+ bgp_show_summary(vty, bgp, afi, safi, fpeer,
+ as_type, as, show_flags);
+ }
+ safi++;
+ if (!safi_wildcard)
+ safi = SAFI_MAX;
+ }
+ afi++;
+ if (!afi_wildcard)
+ afi = AFI_MAX;
+ }
+
+ if (use_json && is_wildcard)
+ vty_out(vty, "}\n");
+ else if (!nbr_output) {
+ if (use_json)
+ vty_out(vty, "{}\n");
+ else
+ vty_out(vty, "%% No BGP neighbors found in %s\n",
+ bgp->name_pretty);
+ }
+}
+
+static void bgp_show_all_instances_summary_vty(struct vty *vty, afi_t afi,
+ safi_t safi,
+ const char *neighbor,
+ int as_type, as_t as,
+ uint16_t show_flags)
+{
+ struct listnode *node, *nnode;
+ struct bgp *bgp;
+ struct peer *fpeer = NULL;
+ int is_first = 1;
+ bool nbr_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)) {
+ nbr_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);
+ }
+ if (neighbor) {
+ fpeer = peer_lookup_in_view(vty, bgp, neighbor,
+ use_json);
+ if (!fpeer)
+ continue;
+ }
+ bgp_show_summary_afi_safi(vty, bgp, afi, safi, fpeer, as_type,
+ as, show_flags);
+ }
+
+ if (use_json)
+ vty_out(vty, "}\n");
+ else if (!nbr_output)
+ vty_out(vty, "%% BGP instance not found\n");
+}
+
+int bgp_show_summary_vty(struct vty *vty, const char *name, afi_t afi,
+ safi_t safi, const char *neighbor, int as_type,
+ as_t as, uint16_t show_flags)
+{
+ struct bgp *bgp;
+ bool use_json = CHECK_FLAG(show_flags, BGP_SHOW_OPT_JSON);
+ struct peer *fpeer = NULL;
+
+ if (name) {
+ if (strmatch(name, "all")) {
+ bgp_show_all_instances_summary_vty(vty, afi, safi,
+ neighbor, as_type,
+ as, show_flags);
+ return CMD_SUCCESS;
+ } else {
+ bgp = bgp_lookup_by_name(name);
+
+ if (!bgp) {
+ if (use_json)
+ vty_out(vty, "{}\n");
+ else
+ vty_out(vty,
+ "%% BGP instance not found\n");
+ return CMD_WARNING;
+ }
+
+ if (neighbor) {
+ fpeer = peer_lookup_in_view(vty, bgp, neighbor,
+ use_json);
+ if (!fpeer)
+ return CMD_WARNING;
+ }
+ bgp_show_summary_afi_safi(vty, bgp, afi, safi, fpeer,
+ as_type, as, show_flags);
+ return CMD_SUCCESS;
+ }
+ }
+
+ bgp = bgp_get_default();
+
+ if (bgp) {
+ if (neighbor) {
+ fpeer = peer_lookup_in_view(vty, bgp, neighbor,
+ use_json);
+ if (!fpeer)
+ return CMD_WARNING;
+ }
+ bgp_show_summary_afi_safi(vty, bgp, afi, safi, fpeer, as_type,
+ as, show_flags);
+ } else {
+ if (use_json)
+ vty_out(vty, "{}\n");
+ else
+ vty_out(vty, "%% BGP instance not found\n");
+ return CMD_WARNING;
+ }
+
+ return CMD_SUCCESS;
+}
+
+/* `show [ip] bgp summary' commands. */
+DEFPY(show_ip_bgp_summary, show_ip_bgp_summary_cmd,
+ "show [ip] bgp [<view|vrf> VIEWVRFNAME] [" BGP_AFI_CMD_STR
+ " [" BGP_SAFI_WITH_LABEL_CMD_STR
+ "]] [all$all] summary [established|failed] [<neighbor <A.B.C.D|X:X::X:X|WORD>|remote-as <(1-4294967295)|internal|external>>] [terse] [wide] [json$uj]",
+ 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"
+ "Summary of BGP neighbor status\n"
+ "Show only sessions in Established state\n"
+ "Show only sessions not in Established state\n"
+ "Show only the specified neighbor session\n"
+ "Neighbor to display information about\n"
+ "Neighbor to display information about\n"
+ "Neighbor on BGP configured interface\n"
+ "Show only the specified remote AS sessions\n"
+ "AS number\n"
+ "Internal (iBGP) AS sessions\n"
+ "External (eBGP) AS sessions\n"
+ "Shorten the information on BGP instances\n"
+ "Increase table width for longer output\n" JSON_STR)
+{
+ char *vrf = NULL;
+ afi_t afi = AFI_MAX;
+ safi_t safi = SAFI_MAX;
+ as_t as = 0; /* 0 means AS filter not set */
+ int as_type = AS_UNSPECIFIED;
+ uint16_t show_flags = 0;
+
+ int idx = 0;
+
+ /* show [ip] bgp */
+ if (!all && argv_find(argv, argc, "ip", &idx))
+ afi = AFI_IP;
+ /* [<vrf> VIEWVRFNAME] */
+ if (argv_find(argv, argc, "vrf", &idx)) {
+ vrf = argv[idx + 1]->arg;
+ if (vrf && strmatch(vrf, VRF_DEFAULT_NAME))
+ vrf = NULL;
+ } else if (argv_find(argv, argc, "view", &idx))
+ /* [<view> VIEWVRFNAME] */
+ vrf = argv[idx + 1]->arg;
+ /* ["BGP_AFI_CMD_STR" ["BGP_SAFI_CMD_STR"]] */
+ if (argv_find_and_parse_afi(argv, argc, &idx, &afi)) {
+ argv_find_and_parse_safi(argv, argc, &idx, &safi);
+ }
+
+ if (argv_find(argv, argc, "failed", &idx))
+ SET_FLAG(show_flags, BGP_SHOW_OPT_FAILED);
+
+ if (argv_find(argv, argc, "established", &idx))
+ SET_FLAG(show_flags, BGP_SHOW_OPT_ESTABLISHED);
+
+ if (argv_find(argv, argc, "remote-as", &idx)) {
+ if (argv[idx + 1]->arg[0] == 'i')
+ as_type = AS_INTERNAL;
+ else if (argv[idx + 1]->arg[0] == 'e')
+ as_type = AS_EXTERNAL;
+ else
+ as = (as_t)atoi(argv[idx + 1]->arg);
+ }
+
+ if (argv_find(argv, argc, "terse", &idx))
+ SET_FLAG(show_flags, BGP_SHOW_OPT_TERSE);
+
+ if (argv_find(argv, argc, "wide", &idx))
+ SET_FLAG(show_flags, BGP_SHOW_OPT_WIDE);
+
+ if (argv_find(argv, argc, "json", &idx))
+ SET_FLAG(show_flags, BGP_SHOW_OPT_JSON);
+
+ return bgp_show_summary_vty(vty, vrf, afi, safi, neighbor, as_type, as,
+ show_flags);
+}
+
+const char *get_afi_safi_str(afi_t afi, safi_t safi, bool for_json)
+{
+ if (for_json)
+ return get_afi_safi_json_str(afi, safi);
+ else
+ return get_afi_safi_vty_str(afi, safi);
+}
+
+
+static void bgp_show_peer_afi_orf_cap(struct vty *vty, struct peer *p,
+ afi_t afi, safi_t safi,
+ uint16_t adv_smcap, uint16_t adv_rmcap,
+ uint16_t rcv_smcap, uint16_t rcv_rmcap,
+ bool use_json, json_object *json_pref)
+{
+ /* Send-Mode */
+ if (CHECK_FLAG(p->af_cap[afi][safi], adv_smcap)
+ || CHECK_FLAG(p->af_cap[afi][safi], rcv_smcap)) {
+ if (use_json) {
+ if (CHECK_FLAG(p->af_cap[afi][safi], adv_smcap)
+ && CHECK_FLAG(p->af_cap[afi][safi], rcv_smcap))
+ json_object_string_add(json_pref, "sendMode",
+ "advertisedAndReceived");
+ else if (CHECK_FLAG(p->af_cap[afi][safi], adv_smcap))
+ json_object_string_add(json_pref, "sendMode",
+ "advertised");
+ else if (CHECK_FLAG(p->af_cap[afi][safi], rcv_smcap))
+ json_object_string_add(json_pref, "sendMode",
+ "received");
+ } else {
+ vty_out(vty, " Send-mode: ");
+ if (CHECK_FLAG(p->af_cap[afi][safi], adv_smcap))
+ vty_out(vty, "advertised");
+ if (CHECK_FLAG(p->af_cap[afi][safi], rcv_smcap))
+ vty_out(vty, "%sreceived",
+ CHECK_FLAG(p->af_cap[afi][safi],
+ adv_smcap)
+ ? ", "
+ : "");
+ vty_out(vty, "\n");
+ }
+ }
+
+ /* Receive-Mode */
+ if (CHECK_FLAG(p->af_cap[afi][safi], adv_rmcap)
+ || CHECK_FLAG(p->af_cap[afi][safi], rcv_rmcap)) {
+ if (use_json) {
+ if (CHECK_FLAG(p->af_cap[afi][safi], adv_rmcap)
+ && CHECK_FLAG(p->af_cap[afi][safi], rcv_rmcap))
+ json_object_string_add(json_pref, "recvMode",
+ "advertisedAndReceived");
+ else if (CHECK_FLAG(p->af_cap[afi][safi], adv_rmcap))
+ json_object_string_add(json_pref, "recvMode",
+ "advertised");
+ else if (CHECK_FLAG(p->af_cap[afi][safi], rcv_rmcap))
+ json_object_string_add(json_pref, "recvMode",
+ "received");
+ } else {
+ vty_out(vty, " Receive-mode: ");
+ if (CHECK_FLAG(p->af_cap[afi][safi], adv_rmcap))
+ vty_out(vty, "advertised");
+ if (CHECK_FLAG(p->af_cap[afi][safi], rcv_rmcap))
+ vty_out(vty, "%sreceived",
+ CHECK_FLAG(p->af_cap[afi][safi],
+ adv_rmcap)
+ ? ", "
+ : "");
+ vty_out(vty, "\n");
+ }
+ }
+}
+
+static void bgp_show_neighnor_graceful_restart_flags(struct vty *vty,
+ struct peer *p,
+ bool use_json,
+ json_object *json)
+{
+ bool rbit = false;
+ bool nbit = false;
+
+ if (CHECK_FLAG(p->cap, PEER_CAP_RESTART_ADV)
+ && (CHECK_FLAG(p->cap, PEER_CAP_RESTART_RCV))
+ && (peer_established(p))) {
+ rbit = CHECK_FLAG(p->cap, PEER_CAP_GRACEFUL_RESTART_R_BIT_RCV);
+ nbit = CHECK_FLAG(p->cap, PEER_CAP_GRACEFUL_RESTART_N_BIT_RCV);
+ }
+
+ if (use_json) {
+ json_object_boolean_add(json, "rBit", rbit);
+ json_object_boolean_add(json, "nBit", nbit);
+ } else {
+ vty_out(vty, "\n R bit: %s", rbit ? "True" : "False");
+ vty_out(vty, "\n N bit: %s\n", nbit ? "True" : "False");
+ }
+}
+
+static void bgp_show_neighbor_graceful_restart_remote_mode(struct vty *vty,
+ struct peer *peer,
+ bool use_json,
+ json_object *json)
+{
+ const char *mode = "NotApplicable";
+
+ if (!use_json)
+ vty_out(vty, "\n Remote GR Mode: ");
+
+ if (CHECK_FLAG(peer->cap, PEER_CAP_RESTART_ADV)
+ && (peer_established(peer))) {
+
+ if ((peer->nsf_af_count == 0)
+ && !CHECK_FLAG(peer->cap, PEER_CAP_RESTART_RCV)) {
+
+ mode = "Disable";
+
+ } else if (peer->nsf_af_count == 0
+ && CHECK_FLAG(peer->cap, PEER_CAP_RESTART_RCV)) {
+
+ mode = "Helper";
+
+ } else if (peer->nsf_af_count != 0
+ && CHECK_FLAG(peer->cap, PEER_CAP_RESTART_RCV)) {
+
+ mode = "Restart";
+ }
+ }
+
+ if (use_json) {
+ json_object_string_add(json, "remoteGrMode", mode);
+ } else
+ vty_out(vty, mode, "\n");
+}
+
+static void bgp_show_neighbor_graceful_restart_local_mode(struct vty *vty,
+ struct peer *p,
+ bool use_json,
+ json_object *json)
+{
+ const char *mode = "Invalid";
+
+ if (!use_json)
+ vty_out(vty, " Local GR Mode: ");
+
+ if (bgp_peer_gr_mode_get(p) == PEER_HELPER)
+ mode = "Helper";
+ else if (bgp_peer_gr_mode_get(p) == PEER_GR)
+ mode = "Restart";
+ else if (bgp_peer_gr_mode_get(p) == PEER_DISABLE)
+ mode = "Disable";
+ else if (bgp_peer_gr_mode_get(p) == PEER_GLOBAL_INHERIT) {
+ if (bgp_global_gr_mode_get(p->bgp) == GLOBAL_HELPER)
+ mode = "Helper*";
+ else if (bgp_global_gr_mode_get(p->bgp) == GLOBAL_GR)
+ mode = "Restart*";
+ else if (bgp_global_gr_mode_get(p->bgp) == GLOBAL_DISABLE)
+ mode = "Disable*";
+ else
+ mode = "Invalid*";
+ }
+
+ if (use_json) {
+ json_object_string_add(json, "localGrMode", mode);
+ } else {
+ vty_out(vty, mode, "\n");
+ }
+}
+
+static void bgp_show_neighbor_graceful_restart_capability_per_afi_safi(
+ struct vty *vty, struct peer *peer, bool use_json, json_object *json)
+{
+ afi_t afi;
+ safi_t safi;
+ json_object *json_afi_safi = NULL;
+ json_object *json_timer = NULL;
+ json_object *json_endofrib_status = NULL;
+ bool eor_flag = false;
+
+ FOREACH_AFI_SAFI_NSF (afi, safi) {
+ if (!peer->afc[afi][safi])
+ continue;
+
+ if (!CHECK_FLAG(peer->cap, PEER_CAP_RESTART_ADV) ||
+ !CHECK_FLAG(peer->cap, PEER_CAP_RESTART_RCV))
+ continue;
+
+ if (use_json) {
+ json_afi_safi = json_object_new_object();
+ json_endofrib_status = json_object_new_object();
+ json_timer = json_object_new_object();
+ }
+
+ if (peer->eor_stime[afi][safi] >= peer->pkt_stime[afi][safi])
+ eor_flag = true;
+ else
+ eor_flag = false;
+
+ if (!use_json) {
+ vty_out(vty, " %s:\n",
+ get_afi_safi_str(afi, safi, false));
+
+ vty_out(vty, " F bit: ");
+ }
+
+ if (peer->nsf[afi][safi] &&
+ CHECK_FLAG(peer->af_cap[afi][safi],
+ PEER_CAP_RESTART_AF_PRESERVE_RCV)) {
+
+ if (use_json) {
+ json_object_boolean_true_add(json_afi_safi,
+ "fBit");
+ } else
+ vty_out(vty, "True\n");
+ } else {
+ if (use_json)
+ json_object_boolean_false_add(json_afi_safi,
+ "fBit");
+ else
+ vty_out(vty, "False\n");
+ }
+
+ if (!use_json)
+ vty_out(vty, " End-of-RIB sent: ");
+
+ if (CHECK_FLAG(peer->af_sflags[afi][safi],
+ PEER_STATUS_EOR_SEND)) {
+ if (use_json) {
+ json_object_boolean_true_add(
+ json_endofrib_status, "endOfRibSend");
+
+ PRINT_EOR_JSON(eor_flag);
+ } else {
+ vty_out(vty, "Yes\n");
+ vty_out(vty,
+ " End-of-RIB sent after update: ");
+
+ PRINT_EOR(eor_flag);
+ }
+ } else {
+ if (use_json) {
+ json_object_boolean_false_add(
+ json_endofrib_status, "endOfRibSend");
+ json_object_boolean_false_add(
+ json_endofrib_status,
+ "endOfRibSentAfterUpdate");
+ } else {
+ vty_out(vty, "No\n");
+ vty_out(vty,
+ " End-of-RIB sent after update: ");
+ vty_out(vty, "No\n");
+ }
+ }
+
+ if (!use_json)
+ vty_out(vty, " End-of-RIB received: ");
+
+ if (CHECK_FLAG(peer->af_sflags[afi][safi],
+ PEER_STATUS_EOR_RECEIVED)) {
+ if (use_json)
+ json_object_boolean_true_add(
+ json_endofrib_status, "endOfRibRecv");
+ else
+ vty_out(vty, "Yes\n");
+ } else {
+ if (use_json)
+ json_object_boolean_false_add(
+ json_endofrib_status, "endOfRibRecv");
+ else
+ vty_out(vty, "No\n");
+ }
+
+ if (use_json) {
+ json_object_int_add(json_timer, "stalePathTimer",
+ peer->bgp->stalepath_time);
+
+ if (peer->t_gr_stale != NULL) {
+ json_object_int_add(json_timer,
+ "stalePathTimerRemaining",
+ thread_timer_remain_second(
+ peer->t_gr_stale));
+ }
+
+ /* Display Configured Selection
+ * Deferral only when when
+ * Gr mode is enabled.
+ */
+ if (CHECK_FLAG(peer->flags,
+ PEER_FLAG_GRACEFUL_RESTART)) {
+ json_object_int_add(json_timer,
+ "selectionDeferralTimer",
+ peer->bgp->stalepath_time);
+ }
+
+ if (peer->bgp->gr_info[afi][safi].t_select_deferral !=
+ NULL) {
+
+ json_object_int_add(
+ json_timer,
+ "selectionDeferralTimerRemaining",
+ thread_timer_remain_second(
+ peer->bgp->gr_info[afi][safi]
+ .t_select_deferral));
+ }
+ } else {
+ vty_out(vty, " Timers:\n");
+ vty_out(vty,
+ " Configured Stale Path Time(sec): %u\n",
+ peer->bgp->stalepath_time);
+
+ if (peer->t_gr_stale != NULL)
+ vty_out(vty,
+ " Stale Path Remaining(sec): %ld\n",
+ thread_timer_remain_second(
+ peer->t_gr_stale));
+ /* Display Configured Selection
+ * Deferral only when when
+ * Gr mode is enabled.
+ */
+ if (CHECK_FLAG(peer->flags, PEER_FLAG_GRACEFUL_RESTART))
+ vty_out(vty,
+ " Configured Selection Deferral Time(sec): %u\n",
+ peer->bgp->select_defer_time);
+
+ if (peer->bgp->gr_info[afi][safi].t_select_deferral !=
+ NULL)
+ vty_out(vty,
+ " Selection Deferral Time Remaining(sec): %ld\n",
+ thread_timer_remain_second(
+ peer->bgp->gr_info[afi][safi]
+ .t_select_deferral));
+ }
+ if (use_json) {
+ json_object_object_add(json_afi_safi, "endOfRibStatus",
+ json_endofrib_status);
+ json_object_object_add(json_afi_safi, "timers",
+ json_timer);
+ json_object_object_add(
+ json, get_afi_safi_str(afi, safi, true),
+ json_afi_safi);
+ }
+ }
+}
+
+static void bgp_show_neighbor_graceful_restart_time(struct vty *vty,
+ struct peer *p,
+ bool use_json,
+ json_object *json)
+{
+ if (use_json) {
+ json_object *json_timer = NULL;
+
+ json_timer = json_object_new_object();
+
+ json_object_int_add(json_timer, "configuredRestartTimer",
+ p->bgp->restart_time);
+
+ json_object_int_add(json_timer, "receivedRestartTimer",
+ p->v_gr_restart);
+
+ if (p->t_gr_restart != NULL)
+ json_object_int_add(
+ json_timer, "restartTimerRemaining",
+ thread_timer_remain_second(p->t_gr_restart));
+
+ json_object_object_add(json, "timers", json_timer);
+ } else {
+
+ vty_out(vty, " Timers:\n");
+ vty_out(vty, " Configured Restart Time(sec): %u\n",
+ p->bgp->restart_time);
+
+ vty_out(vty, " Received Restart Time(sec): %u\n",
+ p->v_gr_restart);
+ if (p->t_gr_restart != NULL)
+ vty_out(vty, " Restart Time Remaining(sec): %ld\n",
+ thread_timer_remain_second(p->t_gr_restart));
+ if (p->t_gr_restart != NULL) {
+ vty_out(vty, " Restart Time Remaining(sec): %ld\n",
+ thread_timer_remain_second(p->t_gr_restart));
+ }
+ }
+}
+
+static void bgp_show_peer_gr_status(struct vty *vty, struct peer *p,
+ bool use_json, json_object *json)
+{
+ char dn_flag[2] = {0};
+ /* '*' + v6 address of neighbor */
+ char neighborAddr[INET6_ADDRSTRLEN + 1] = {0};
+
+ if (!p->conf_if && peer_dynamic_neighbor(p))
+ dn_flag[0] = '*';
+
+ if (p->conf_if) {
+ if (use_json)
+ json_object_string_addf(json, "neighborAddr", "%pSU",
+ &p->su);
+ else
+ vty_out(vty, "BGP neighbor on %s: %pSU\n", p->conf_if,
+ &p->su);
+ } else {
+ snprintf(neighborAddr, sizeof(neighborAddr), "%s%s", dn_flag,
+ p->host);
+
+ if (use_json)
+ json_object_string_add(json, "neighborAddr",
+ neighborAddr);
+ else
+ vty_out(vty, "BGP neighbor is %s\n", neighborAddr);
+ }
+
+ /* more gr info in new format */
+ BGP_SHOW_PEER_GR_CAPABILITY(vty, p, use_json, json);
+}
+
+static void bgp_show_peer_afi(struct vty *vty, struct peer *p, afi_t afi,
+ safi_t safi, bool use_json,
+ json_object *json_neigh)
+{
+ struct bgp_filter *filter;
+ struct peer_af *paf;
+ char orf_pfx_name[BUFSIZ];
+ int orf_pfx_count;
+ json_object *json_af = NULL;
+ json_object *json_prefA = NULL;
+ json_object *json_prefB = NULL;
+ json_object *json_addr = NULL;
+ json_object *json_advmap = NULL;
+
+ if (use_json) {
+ json_addr = json_object_new_object();
+ json_af = json_object_new_object();
+ filter = &p->filter[afi][safi];
+
+ if (peer_group_active(p))
+ json_object_string_add(json_addr, "peerGroupMember",
+ p->group->name);
+
+ paf = peer_af_find(p, afi, safi);
+ if (paf && PAF_SUBGRP(paf)) {
+ json_object_int_add(json_addr, "updateGroupId",
+ PAF_UPDGRP(paf)->id);
+ json_object_int_add(json_addr, "subGroupId",
+ PAF_SUBGRP(paf)->id);
+ json_object_int_add(json_addr, "packetQueueLength",
+ bpacket_queue_virtual_length(paf));
+ }
+
+ if (CHECK_FLAG(p->af_cap[afi][safi], PEER_CAP_ORF_PREFIX_SM_ADV)
+ || CHECK_FLAG(p->af_cap[afi][safi],
+ PEER_CAP_ORF_PREFIX_SM_RCV)
+ || CHECK_FLAG(p->af_cap[afi][safi],
+ PEER_CAP_ORF_PREFIX_RM_ADV)
+ || CHECK_FLAG(p->af_cap[afi][safi],
+ PEER_CAP_ORF_PREFIX_RM_RCV)) {
+ json_object_int_add(json_af, "orfType",
+ ORF_TYPE_PREFIX);
+ json_prefA = json_object_new_object();
+ bgp_show_peer_afi_orf_cap(vty, p, afi, safi,
+ PEER_CAP_ORF_PREFIX_SM_ADV,
+ PEER_CAP_ORF_PREFIX_RM_ADV,
+ PEER_CAP_ORF_PREFIX_SM_RCV,
+ PEER_CAP_ORF_PREFIX_RM_RCV,
+ use_json, json_prefA);
+ json_object_object_add(json_af, "orfPrefixList",
+ json_prefA);
+ }
+
+ if (CHECK_FLAG(p->af_cap[afi][safi], PEER_CAP_ORF_PREFIX_SM_ADV)
+ || CHECK_FLAG(p->af_cap[afi][safi],
+ PEER_CAP_ORF_PREFIX_SM_OLD_RCV)
+ || CHECK_FLAG(p->af_cap[afi][safi],
+ PEER_CAP_ORF_PREFIX_RM_ADV)
+ || CHECK_FLAG(p->af_cap[afi][safi],
+ PEER_CAP_ORF_PREFIX_RM_OLD_RCV)) {
+ json_object_int_add(json_af, "orfOldType",
+ ORF_TYPE_PREFIX_OLD);
+ json_prefB = json_object_new_object();
+ bgp_show_peer_afi_orf_cap(
+ vty, p, afi, safi, PEER_CAP_ORF_PREFIX_SM_ADV,
+ PEER_CAP_ORF_PREFIX_RM_ADV,
+ PEER_CAP_ORF_PREFIX_SM_OLD_RCV,
+ PEER_CAP_ORF_PREFIX_RM_OLD_RCV, use_json,
+ json_prefB);
+ json_object_object_add(json_af, "orfOldPrefixList",
+ json_prefB);
+ }
+
+ if (CHECK_FLAG(p->af_cap[afi][safi], PEER_CAP_ORF_PREFIX_SM_ADV)
+ || CHECK_FLAG(p->af_cap[afi][safi],
+ PEER_CAP_ORF_PREFIX_SM_RCV)
+ || CHECK_FLAG(p->af_cap[afi][safi],
+ PEER_CAP_ORF_PREFIX_SM_OLD_RCV)
+ || CHECK_FLAG(p->af_cap[afi][safi],
+ PEER_CAP_ORF_PREFIX_RM_ADV)
+ || CHECK_FLAG(p->af_cap[afi][safi],
+ PEER_CAP_ORF_PREFIX_RM_RCV)
+ || CHECK_FLAG(p->af_cap[afi][safi],
+ PEER_CAP_ORF_PREFIX_RM_OLD_RCV))
+ json_object_object_add(json_addr, "afDependentCap",
+ json_af);
+ else
+ json_object_free(json_af);
+
+ snprintf(orf_pfx_name, sizeof(orf_pfx_name), "%s.%d.%d",
+ p->host, afi, safi);
+ orf_pfx_count = prefix_bgp_show_prefix_list(
+ NULL, afi, orf_pfx_name, use_json);
+
+ if (CHECK_FLAG(p->af_sflags[afi][safi],
+ PEER_STATUS_ORF_PREFIX_SEND)
+ || orf_pfx_count) {
+ if (CHECK_FLAG(p->af_sflags[afi][safi],
+ PEER_STATUS_ORF_PREFIX_SEND))
+ json_object_boolean_true_add(json_neigh,
+ "orfSent");
+ if (orf_pfx_count)
+ json_object_int_add(json_addr, "orfRecvCounter",
+ orf_pfx_count);
+ }
+ if (CHECK_FLAG(p->af_sflags[afi][safi],
+ PEER_STATUS_ORF_WAIT_REFRESH))
+ json_object_string_add(
+ json_addr, "orfFirstUpdate",
+ "deferredUntilORFOrRouteRefreshRecvd");
+
+ if (CHECK_FLAG(p->af_flags[afi][safi],
+ PEER_FLAG_REFLECTOR_CLIENT))
+ json_object_boolean_true_add(json_addr,
+ "routeReflectorClient");
+ if (CHECK_FLAG(p->af_flags[afi][safi],
+ PEER_FLAG_RSERVER_CLIENT))
+ json_object_boolean_true_add(json_addr,
+ "routeServerClient");
+ if (CHECK_FLAG(p->af_flags[afi][safi], PEER_FLAG_SOFT_RECONFIG))
+ json_object_boolean_true_add(json_addr,
+ "inboundSoftConfigPermit");
+
+ if (CHECK_FLAG(p->af_flags[afi][safi],
+ PEER_FLAG_REMOVE_PRIVATE_AS_ALL_REPLACE))
+ json_object_boolean_true_add(
+ json_addr,
+ "privateAsNumsAllReplacedInUpdatesToNbr");
+ else if (CHECK_FLAG(p->af_flags[afi][safi],
+ PEER_FLAG_REMOVE_PRIVATE_AS_REPLACE))
+ json_object_boolean_true_add(
+ json_addr,
+ "privateAsNumsReplacedInUpdatesToNbr");
+ else if (CHECK_FLAG(p->af_flags[afi][safi],
+ PEER_FLAG_REMOVE_PRIVATE_AS_ALL))
+ json_object_boolean_true_add(
+ json_addr,
+ "privateAsNumsAllRemovedInUpdatesToNbr");
+ else if (CHECK_FLAG(p->af_flags[afi][safi],
+ PEER_FLAG_REMOVE_PRIVATE_AS))
+ json_object_boolean_true_add(
+ json_addr,
+ "privateAsNumsRemovedInUpdatesToNbr");
+
+ if (p->addpath_type[afi][safi] != BGP_ADDPATH_NONE)
+ json_object_boolean_true_add(
+ json_addr,
+ bgp_addpath_names(p->addpath_type[afi][safi])
+ ->type_json_name);
+
+ if (CHECK_FLAG(p->af_flags[afi][safi], PEER_FLAG_AS_OVERRIDE))
+ json_object_string_add(json_addr,
+ "overrideASNsInOutboundUpdates",
+ "ifAspathEqualRemoteAs");
+
+ if (CHECK_FLAG(p->af_flags[afi][safi], PEER_FLAG_NEXTHOP_SELF)
+ || CHECK_FLAG(p->af_flags[afi][safi],
+ PEER_FLAG_FORCE_NEXTHOP_SELF))
+ json_object_boolean_true_add(json_addr,
+ "routerAlwaysNextHop");
+ if (CHECK_FLAG(p->af_flags[afi][safi],
+ PEER_FLAG_AS_PATH_UNCHANGED))
+ json_object_boolean_true_add(
+ json_addr, "unchangedAsPathPropogatedToNbr");
+ if (CHECK_FLAG(p->af_flags[afi][safi],
+ PEER_FLAG_NEXTHOP_UNCHANGED))
+ json_object_boolean_true_add(
+ json_addr, "unchangedNextHopPropogatedToNbr");
+ if (CHECK_FLAG(p->af_flags[afi][safi], PEER_FLAG_MED_UNCHANGED))
+ json_object_boolean_true_add(
+ json_addr, "unchangedMedPropogatedToNbr");
+ if (CHECK_FLAG(p->af_flags[afi][safi], PEER_FLAG_SEND_COMMUNITY)
+ || CHECK_FLAG(p->af_flags[afi][safi],
+ PEER_FLAG_SEND_EXT_COMMUNITY)) {
+ if (CHECK_FLAG(p->af_flags[afi][safi],
+ PEER_FLAG_SEND_COMMUNITY)
+ && CHECK_FLAG(p->af_flags[afi][safi],
+ PEER_FLAG_SEND_EXT_COMMUNITY))
+ json_object_string_add(json_addr,
+ "commAttriSentToNbr",
+ "extendedAndStandard");
+ else if (CHECK_FLAG(p->af_flags[afi][safi],
+ PEER_FLAG_SEND_EXT_COMMUNITY))
+ json_object_string_add(json_addr,
+ "commAttriSentToNbr",
+ "extended");
+ else
+ json_object_string_add(json_addr,
+ "commAttriSentToNbr",
+ "standard");
+ }
+ if (CHECK_FLAG(p->af_flags[afi][safi],
+ PEER_FLAG_DEFAULT_ORIGINATE)) {
+ if (p->default_rmap[afi][safi].name)
+ json_object_string_add(
+ json_addr, "defaultRouteMap",
+ p->default_rmap[afi][safi].name);
+
+ if (paf && PAF_SUBGRP(paf)
+ && CHECK_FLAG(PAF_SUBGRP(paf)->sflags,
+ SUBGRP_STATUS_DEFAULT_ORIGINATE))
+ json_object_boolean_true_add(json_addr,
+ "defaultSent");
+ else
+ json_object_boolean_true_add(json_addr,
+ "defaultNotSent");
+ }
+
+ if (afi == AFI_L2VPN && safi == SAFI_EVPN) {
+ if (is_evpn_enabled())
+ json_object_boolean_true_add(
+ json_addr, "advertiseAllVnis");
+ }
+
+ if (filter->plist[FILTER_IN].name
+ || filter->dlist[FILTER_IN].name
+ || filter->aslist[FILTER_IN].name
+ || filter->map[RMAP_IN].name)
+ json_object_boolean_true_add(json_addr,
+ "inboundPathPolicyConfig");
+ if (filter->plist[FILTER_OUT].name
+ || filter->dlist[FILTER_OUT].name
+ || filter->aslist[FILTER_OUT].name
+ || filter->map[RMAP_OUT].name || filter->usmap.name)
+ json_object_boolean_true_add(
+ json_addr, "outboundPathPolicyConfig");
+
+ /* prefix-list */
+ if (filter->plist[FILTER_IN].name)
+ json_object_string_add(json_addr,
+ "incomingUpdatePrefixFilterList",
+ filter->plist[FILTER_IN].name);
+ if (filter->plist[FILTER_OUT].name)
+ json_object_string_add(json_addr,
+ "outgoingUpdatePrefixFilterList",
+ filter->plist[FILTER_OUT].name);
+
+ /* distribute-list */
+ if (filter->dlist[FILTER_IN].name)
+ json_object_string_add(
+ json_addr, "incomingUpdateNetworkFilterList",
+ filter->dlist[FILTER_IN].name);
+ if (filter->dlist[FILTER_OUT].name)
+ json_object_string_add(
+ json_addr, "outgoingUpdateNetworkFilterList",
+ filter->dlist[FILTER_OUT].name);
+
+ /* filter-list. */
+ if (filter->aslist[FILTER_IN].name)
+ json_object_string_add(json_addr,
+ "incomingUpdateAsPathFilterList",
+ filter->aslist[FILTER_IN].name);
+ if (filter->aslist[FILTER_OUT].name)
+ json_object_string_add(json_addr,
+ "outgoingUpdateAsPathFilterList",
+ filter->aslist[FILTER_OUT].name);
+
+ /* route-map. */
+ if (filter->map[RMAP_IN].name)
+ json_object_string_add(
+ json_addr, "routeMapForIncomingAdvertisements",
+ filter->map[RMAP_IN].name);
+ if (filter->map[RMAP_OUT].name)
+ json_object_string_add(
+ json_addr, "routeMapForOutgoingAdvertisements",
+ filter->map[RMAP_OUT].name);
+
+ /* ebgp-requires-policy (inbound) */
+ if (CHECK_FLAG(p->bgp->flags, BGP_FLAG_EBGP_REQUIRES_POLICY)
+ && !bgp_inbound_policy_exists(p, filter))
+ json_object_string_add(
+ json_addr, "inboundEbgpRequiresPolicy",
+ "Inbound updates discarded due to missing policy");
+
+ /* ebgp-requires-policy (outbound) */
+ if (CHECK_FLAG(p->bgp->flags, BGP_FLAG_EBGP_REQUIRES_POLICY)
+ && (!bgp_outbound_policy_exists(p, filter)))
+ json_object_string_add(
+ json_addr, "outboundEbgpRequiresPolicy",
+ "Outbound updates discarded due to missing policy");
+
+ /* unsuppress-map */
+ if (filter->usmap.name)
+ json_object_string_add(json_addr,
+ "selectiveUnsuppressRouteMap",
+ filter->usmap.name);
+
+ /* advertise-map */
+ if (filter->advmap.aname) {
+ json_advmap = json_object_new_object();
+ json_object_string_add(json_advmap, "condition",
+ filter->advmap.condition
+ ? "EXIST"
+ : "NON_EXIST");
+ json_object_string_add(json_advmap, "conditionMap",
+ filter->advmap.cname);
+ json_object_string_add(json_advmap, "advertiseMap",
+ filter->advmap.aname);
+ json_object_string_add(
+ json_advmap, "advertiseStatus",
+ filter->advmap.update_type ==
+ UPDATE_TYPE_ADVERTISE
+ ? "Advertise"
+ : "Withdraw");
+ json_object_object_add(json_addr, "advertiseMap",
+ json_advmap);
+ }
+
+ /* Receive prefix count */
+ json_object_int_add(json_addr, "acceptedPrefixCounter",
+ p->pcount[afi][safi]);
+ if (paf && PAF_SUBGRP(paf))
+ json_object_int_add(json_addr, "sentPrefixCounter",
+ (PAF_SUBGRP(paf))->scount);
+
+ /* Maximum prefix */
+ if (CHECK_FLAG(p->af_flags[afi][safi], PEER_FLAG_MAX_PREFIX_OUT))
+ json_object_int_add(json_addr, "prefixOutAllowedMax",
+ p->pmax_out[afi][safi]);
+
+ /* Maximum prefix */
+ if (CHECK_FLAG(p->af_flags[afi][safi], PEER_FLAG_MAX_PREFIX)) {
+ json_object_int_add(json_addr, "prefixAllowedMax",
+ p->pmax[afi][safi]);
+ if (CHECK_FLAG(p->af_flags[afi][safi],
+ PEER_FLAG_MAX_PREFIX_WARNING))
+ json_object_boolean_true_add(
+ json_addr, "prefixAllowedMaxWarning");
+ json_object_int_add(json_addr,
+ "prefixAllowedWarningThresh",
+ p->pmax_threshold[afi][safi]);
+ if (p->pmax_restart[afi][safi])
+ json_object_int_add(
+ json_addr,
+ "prefixAllowedRestartIntervalMsecs",
+ p->pmax_restart[afi][safi] * 60000);
+ }
+ json_object_object_add(json_neigh,
+ get_afi_safi_str(afi, safi, true),
+ json_addr);
+
+ } else {
+ filter = &p->filter[afi][safi];
+
+ vty_out(vty, " For address family: %s\n",
+ get_afi_safi_str(afi, safi, false));
+
+ if (peer_group_active(p))
+ vty_out(vty, " %s peer-group member\n",
+ p->group->name);
+
+ paf = peer_af_find(p, afi, safi);
+ if (paf && PAF_SUBGRP(paf)) {
+ vty_out(vty, " Update group %" PRIu64", subgroup %" PRIu64 "\n",
+ PAF_UPDGRP(paf)->id, PAF_SUBGRP(paf)->id);
+ vty_out(vty, " Packet Queue length %d\n",
+ bpacket_queue_virtual_length(paf));
+ } else {
+ vty_out(vty, " Not part of any update group\n");
+ }
+ if (CHECK_FLAG(p->af_cap[afi][safi], PEER_CAP_ORF_PREFIX_SM_ADV)
+ || CHECK_FLAG(p->af_cap[afi][safi],
+ PEER_CAP_ORF_PREFIX_SM_RCV)
+ || CHECK_FLAG(p->af_cap[afi][safi],
+ PEER_CAP_ORF_PREFIX_SM_OLD_RCV)
+ || CHECK_FLAG(p->af_cap[afi][safi],
+ PEER_CAP_ORF_PREFIX_RM_ADV)
+ || CHECK_FLAG(p->af_cap[afi][safi],
+ PEER_CAP_ORF_PREFIX_RM_RCV)
+ || CHECK_FLAG(p->af_cap[afi][safi],
+ PEER_CAP_ORF_PREFIX_RM_OLD_RCV))
+ vty_out(vty, " AF-dependant capabilities:\n");
+
+ if (CHECK_FLAG(p->af_cap[afi][safi], PEER_CAP_ORF_PREFIX_SM_ADV)
+ || CHECK_FLAG(p->af_cap[afi][safi],
+ PEER_CAP_ORF_PREFIX_SM_RCV)
+ || CHECK_FLAG(p->af_cap[afi][safi],
+ PEER_CAP_ORF_PREFIX_RM_ADV)
+ || CHECK_FLAG(p->af_cap[afi][safi],
+ PEER_CAP_ORF_PREFIX_RM_RCV)) {
+ vty_out(vty,
+ " Outbound Route Filter (ORF) type (%d) Prefix-list:\n",
+ ORF_TYPE_PREFIX);
+ bgp_show_peer_afi_orf_cap(
+ vty, p, afi, safi, PEER_CAP_ORF_PREFIX_SM_ADV,
+ PEER_CAP_ORF_PREFIX_RM_ADV,
+ PEER_CAP_ORF_PREFIX_SM_RCV,
+ PEER_CAP_ORF_PREFIX_RM_RCV, use_json, NULL);
+ }
+ if (CHECK_FLAG(p->af_cap[afi][safi], PEER_CAP_ORF_PREFIX_SM_ADV)
+ || CHECK_FLAG(p->af_cap[afi][safi],
+ PEER_CAP_ORF_PREFIX_SM_OLD_RCV)
+ || CHECK_FLAG(p->af_cap[afi][safi],
+ PEER_CAP_ORF_PREFIX_RM_ADV)
+ || CHECK_FLAG(p->af_cap[afi][safi],
+ PEER_CAP_ORF_PREFIX_RM_OLD_RCV)) {
+ vty_out(vty,
+ " Outbound Route Filter (ORF) type (%d) Prefix-list:\n",
+ ORF_TYPE_PREFIX_OLD);
+ bgp_show_peer_afi_orf_cap(
+ vty, p, afi, safi, PEER_CAP_ORF_PREFIX_SM_ADV,
+ PEER_CAP_ORF_PREFIX_RM_ADV,
+ PEER_CAP_ORF_PREFIX_SM_OLD_RCV,
+ PEER_CAP_ORF_PREFIX_RM_OLD_RCV, use_json, NULL);
+ }
+
+ snprintf(orf_pfx_name, sizeof(orf_pfx_name), "%s.%d.%d",
+ p->host, afi, safi);
+ orf_pfx_count = prefix_bgp_show_prefix_list(
+ NULL, afi, orf_pfx_name, use_json);
+
+ if (CHECK_FLAG(p->af_sflags[afi][safi],
+ PEER_STATUS_ORF_PREFIX_SEND)
+ || orf_pfx_count) {
+ vty_out(vty, " Outbound Route Filter (ORF):");
+ if (CHECK_FLAG(p->af_sflags[afi][safi],
+ PEER_STATUS_ORF_PREFIX_SEND))
+ vty_out(vty, " sent;");
+ if (orf_pfx_count)
+ vty_out(vty, " received (%d entries)",
+ orf_pfx_count);
+ vty_out(vty, "\n");
+ }
+ if (CHECK_FLAG(p->af_sflags[afi][safi],
+ PEER_STATUS_ORF_WAIT_REFRESH))
+ vty_out(vty,
+ " First update is deferred until ORF or ROUTE-REFRESH is received\n");
+
+ if (CHECK_FLAG(p->af_flags[afi][safi],
+ PEER_FLAG_REFLECTOR_CLIENT))
+ vty_out(vty, " Route-Reflector Client\n");
+ if (CHECK_FLAG(p->af_flags[afi][safi],
+ PEER_FLAG_RSERVER_CLIENT))
+ vty_out(vty, " Route-Server Client\n");
+ if (CHECK_FLAG(p->af_flags[afi][safi], PEER_FLAG_SOFT_RECONFIG))
+ vty_out(vty,
+ " Inbound soft reconfiguration allowed\n");
+
+ if (CHECK_FLAG(p->af_flags[afi][safi],
+ PEER_FLAG_REMOVE_PRIVATE_AS_ALL_REPLACE))
+ vty_out(vty,
+ " Private AS numbers (all) replaced in updates to this neighbor\n");
+ else if (CHECK_FLAG(p->af_flags[afi][safi],
+ PEER_FLAG_REMOVE_PRIVATE_AS_REPLACE))
+ vty_out(vty,
+ " Private AS numbers replaced in updates to this neighbor\n");
+ else if (CHECK_FLAG(p->af_flags[afi][safi],
+ PEER_FLAG_REMOVE_PRIVATE_AS_ALL))
+ vty_out(vty,
+ " Private AS numbers (all) removed in updates to this neighbor\n");
+ else if (CHECK_FLAG(p->af_flags[afi][safi],
+ PEER_FLAG_REMOVE_PRIVATE_AS))
+ vty_out(vty,
+ " Private AS numbers removed in updates to this neighbor\n");
+
+ if (p->addpath_type[afi][safi] != BGP_ADDPATH_NONE)
+ vty_out(vty, " %s\n",
+ bgp_addpath_names(p->addpath_type[afi][safi])
+ ->human_description);
+
+ if (CHECK_FLAG(p->af_flags[afi][safi], PEER_FLAG_AS_OVERRIDE))
+ vty_out(vty,
+ " Override ASNs in outbound updates if aspath equals remote-as\n");
+
+ if (CHECK_FLAG(p->af_flags[afi][safi], PEER_FLAG_NEXTHOP_SELF)
+ || CHECK_FLAG(p->af_flags[afi][safi],
+ PEER_FLAG_FORCE_NEXTHOP_SELF))
+ vty_out(vty, " NEXT_HOP is always this router\n");
+ if (CHECK_FLAG(p->af_flags[afi][safi],
+ PEER_FLAG_AS_PATH_UNCHANGED))
+ vty_out(vty,
+ " AS_PATH is propagated unchanged to this neighbor\n");
+ if (CHECK_FLAG(p->af_flags[afi][safi],
+ PEER_FLAG_NEXTHOP_UNCHANGED))
+ vty_out(vty,
+ " NEXT_HOP is propagated unchanged to this neighbor\n");
+ if (CHECK_FLAG(p->af_flags[afi][safi], PEER_FLAG_MED_UNCHANGED))
+ vty_out(vty,
+ " MED is propagated unchanged to this neighbor\n");
+ if (CHECK_FLAG(p->af_flags[afi][safi], PEER_FLAG_SEND_COMMUNITY)
+ || CHECK_FLAG(p->af_flags[afi][safi],
+ PEER_FLAG_SEND_EXT_COMMUNITY)
+ || CHECK_FLAG(p->af_flags[afi][safi],
+ PEER_FLAG_SEND_LARGE_COMMUNITY)) {
+ vty_out(vty,
+ " Community attribute sent to this neighbor");
+ if (CHECK_FLAG(p->af_flags[afi][safi],
+ PEER_FLAG_SEND_COMMUNITY)
+ && CHECK_FLAG(p->af_flags[afi][safi],
+ PEER_FLAG_SEND_EXT_COMMUNITY)
+ && CHECK_FLAG(p->af_flags[afi][safi],
+ PEER_FLAG_SEND_LARGE_COMMUNITY))
+ vty_out(vty, "(all)\n");
+ else if (CHECK_FLAG(p->af_flags[afi][safi],
+ PEER_FLAG_SEND_LARGE_COMMUNITY))
+ vty_out(vty, "(large)\n");
+ else if (CHECK_FLAG(p->af_flags[afi][safi],
+ PEER_FLAG_SEND_EXT_COMMUNITY))
+ vty_out(vty, "(extended)\n");
+ else
+ vty_out(vty, "(standard)\n");
+ }
+ if (CHECK_FLAG(p->af_flags[afi][safi],
+ PEER_FLAG_DEFAULT_ORIGINATE)) {
+ vty_out(vty, " Default information originate,");
+
+ if (p->default_rmap[afi][safi].name)
+ vty_out(vty, " default route-map %s%s,",
+ p->default_rmap[afi][safi].map ? "*"
+ : "",
+ p->default_rmap[afi][safi].name);
+ if (paf && PAF_SUBGRP(paf)
+ && CHECK_FLAG(PAF_SUBGRP(paf)->sflags,
+ SUBGRP_STATUS_DEFAULT_ORIGINATE))
+ vty_out(vty, " default sent\n");
+ else
+ vty_out(vty, " default not sent\n");
+ }
+
+ /* advertise-vni-all */
+ if (afi == AFI_L2VPN && safi == SAFI_EVPN) {
+ if (is_evpn_enabled())
+ vty_out(vty, " advertise-all-vni\n");
+ }
+
+ if (filter->plist[FILTER_IN].name
+ || filter->dlist[FILTER_IN].name
+ || filter->aslist[FILTER_IN].name
+ || filter->map[RMAP_IN].name)
+ vty_out(vty, " Inbound path policy configured\n");
+ if (filter->plist[FILTER_OUT].name
+ || filter->dlist[FILTER_OUT].name
+ || filter->aslist[FILTER_OUT].name
+ || filter->map[RMAP_OUT].name || filter->usmap.name)
+ vty_out(vty, " Outbound path policy configured\n");
+
+ /* prefix-list */
+ if (filter->plist[FILTER_IN].name)
+ vty_out(vty,
+ " Incoming update prefix filter list is %s%s\n",
+ filter->plist[FILTER_IN].plist ? "*" : "",
+ filter->plist[FILTER_IN].name);
+ if (filter->plist[FILTER_OUT].name)
+ vty_out(vty,
+ " Outgoing update prefix filter list is %s%s\n",
+ filter->plist[FILTER_OUT].plist ? "*" : "",
+ filter->plist[FILTER_OUT].name);
+
+ /* distribute-list */
+ if (filter->dlist[FILTER_IN].name)
+ vty_out(vty,
+ " Incoming update network filter list is %s%s\n",
+ filter->dlist[FILTER_IN].alist ? "*" : "",
+ filter->dlist[FILTER_IN].name);
+ if (filter->dlist[FILTER_OUT].name)
+ vty_out(vty,
+ " Outgoing update network filter list is %s%s\n",
+ filter->dlist[FILTER_OUT].alist ? "*" : "",
+ filter->dlist[FILTER_OUT].name);
+
+ /* filter-list. */
+ if (filter->aslist[FILTER_IN].name)
+ vty_out(vty,
+ " Incoming update AS path filter list is %s%s\n",
+ filter->aslist[FILTER_IN].aslist ? "*" : "",
+ filter->aslist[FILTER_IN].name);
+ if (filter->aslist[FILTER_OUT].name)
+ vty_out(vty,
+ " Outgoing update AS path filter list is %s%s\n",
+ filter->aslist[FILTER_OUT].aslist ? "*" : "",
+ filter->aslist[FILTER_OUT].name);
+
+ /* route-map. */
+ if (filter->map[RMAP_IN].name)
+ vty_out(vty,
+ " Route map for incoming advertisements is %s%s\n",
+ filter->map[RMAP_IN].map ? "*" : "",
+ filter->map[RMAP_IN].name);
+ if (filter->map[RMAP_OUT].name)
+ vty_out(vty,
+ " Route map for outgoing advertisements is %s%s\n",
+ filter->map[RMAP_OUT].map ? "*" : "",
+ filter->map[RMAP_OUT].name);
+
+ /* ebgp-requires-policy (inbound) */
+ if (CHECK_FLAG(p->bgp->flags, BGP_FLAG_EBGP_REQUIRES_POLICY)
+ && !bgp_inbound_policy_exists(p, filter))
+ vty_out(vty,
+ " Inbound updates discarded due to missing policy\n");
+
+ /* ebgp-requires-policy (outbound) */
+ if (CHECK_FLAG(p->bgp->flags, BGP_FLAG_EBGP_REQUIRES_POLICY)
+ && !bgp_outbound_policy_exists(p, filter))
+ vty_out(vty,
+ " Outbound updates discarded due to missing policy\n");
+
+ /* unsuppress-map */
+ if (filter->usmap.name)
+ vty_out(vty,
+ " Route map for selective unsuppress is %s%s\n",
+ filter->usmap.map ? "*" : "",
+ filter->usmap.name);
+
+ /* advertise-map */
+ if (filter->advmap.aname && filter->advmap.cname)
+ vty_out(vty,
+ " Condition %s, Condition-map %s%s, Advertise-map %s%s, status: %s\n",
+ filter->advmap.condition ? "EXIST"
+ : "NON_EXIST",
+ filter->advmap.cmap ? "*" : "",
+ filter->advmap.cname,
+ filter->advmap.amap ? "*" : "",
+ filter->advmap.aname,
+ filter->advmap.update_type ==
+ UPDATE_TYPE_ADVERTISE
+ ? "Advertise"
+ : "Withdraw");
+
+ /* Receive prefix count */
+ vty_out(vty, " %u accepted prefixes\n",
+ p->pcount[afi][safi]);
+
+ /* maximum-prefix-out */
+ if (CHECK_FLAG(p->af_flags[afi][safi],
+ PEER_FLAG_MAX_PREFIX_OUT))
+ vty_out(vty,
+ " Maximum allowed prefixes sent %u\n",
+ p->pmax_out[afi][safi]);
+
+ /* Maximum prefix */
+ if (CHECK_FLAG(p->af_flags[afi][safi], PEER_FLAG_MAX_PREFIX)) {
+ vty_out(vty,
+ " Maximum prefixes allowed %u%s\n",
+ p->pmax[afi][safi],
+ CHECK_FLAG(p->af_flags[afi][safi],
+ PEER_FLAG_MAX_PREFIX_WARNING)
+ ? " (warning-only)"
+ : "");
+ vty_out(vty, " Threshold for warning message %d%%",
+ p->pmax_threshold[afi][safi]);
+ if (p->pmax_restart[afi][safi])
+ vty_out(vty, ", restart interval %d min",
+ p->pmax_restart[afi][safi]);
+ vty_out(vty, "\n");
+ }
+
+ vty_out(vty, "\n");
+ }
+}
+
+static void bgp_show_peer(struct vty *vty, struct peer *p, bool use_json,
+ json_object *json)
+{
+ struct bgp *bgp;
+ char buf1[PREFIX2STR_BUFFER];
+ char timebuf[BGP_UPTIME_LEN];
+ char dn_flag[2];
+ afi_t afi;
+ safi_t safi;
+ uint16_t i;
+ uint8_t *msg;
+ json_object *json_neigh = NULL;
+ time_t epoch_tbuf;
+ uint32_t sync_tcp_mss;
+
+ bgp = p->bgp;
+
+ if (use_json)
+ json_neigh = json_object_new_object();
+
+ memset(dn_flag, '\0', sizeof(dn_flag));
+ if (!p->conf_if && peer_dynamic_neighbor(p))
+ dn_flag[0] = '*';
+
+ if (!use_json) {
+ if (p->conf_if) /* Configured interface name. */
+ vty_out(vty, "BGP neighbor on %s: %pSU, ", p->conf_if,
+ &p->su);
+ else /* Configured IP address. */
+ vty_out(vty, "BGP neighbor is %s%s, ", dn_flag,
+ p->host);
+ }
+
+ if (use_json) {
+ if (p->conf_if && BGP_PEER_SU_UNSPEC(p))
+ json_object_string_add(json_neigh, "bgpNeighborAddr",
+ "none");
+ else if (p->conf_if && !BGP_PEER_SU_UNSPEC(p))
+ json_object_string_addf(json_neigh, "bgpNeighborAddr",
+ "%pSU", &p->su);
+
+ json_object_int_add(json_neigh, "remoteAs", p->as);
+
+ if (p->change_local_as)
+ json_object_int_add(json_neigh, "localAs",
+ p->change_local_as);
+ else
+ json_object_int_add(json_neigh, "localAs", p->local_as);
+
+ if (CHECK_FLAG(p->flags, PEER_FLAG_LOCAL_AS_NO_PREPEND))
+ json_object_boolean_true_add(json_neigh,
+ "localAsNoPrepend");
+
+ if (CHECK_FLAG(p->flags, PEER_FLAG_LOCAL_AS_REPLACE_AS))
+ json_object_boolean_true_add(json_neigh,
+ "localAsReplaceAs");
+ } else {
+ if ((p->as_type == AS_SPECIFIED) || (p->as_type == AS_EXTERNAL)
+ || (p->as_type == AS_INTERNAL))
+ vty_out(vty, "remote AS %u, ", p->as);
+ else
+ vty_out(vty, "remote AS Unspecified, ");
+ vty_out(vty, "local AS %u%s%s, ",
+ p->change_local_as ? p->change_local_as : p->local_as,
+ CHECK_FLAG(p->flags, PEER_FLAG_LOCAL_AS_NO_PREPEND)
+ ? " no-prepend"
+ : "",
+ CHECK_FLAG(p->flags, PEER_FLAG_LOCAL_AS_REPLACE_AS)
+ ? " replace-as"
+ : "");
+ }
+ /* peer type internal or confed-internal */
+ if ((p->as == p->local_as) || (p->as_type == AS_INTERNAL)) {
+ if (use_json) {
+ if (CHECK_FLAG(bgp->config, BGP_CONFIG_CONFEDERATION))
+ json_object_boolean_true_add(
+ json_neigh, "nbrConfedInternalLink");
+ else
+ json_object_boolean_true_add(json_neigh,
+ "nbrInternalLink");
+ } else {
+ if (CHECK_FLAG(bgp->config, BGP_CONFIG_CONFEDERATION))
+ vty_out(vty, "confed-internal link\n");
+ else
+ vty_out(vty, "internal link\n");
+ }
+ /* peer type external or confed-external */
+ } else if (p->as || (p->as_type == AS_EXTERNAL)) {
+ if (use_json) {
+ if (CHECK_FLAG(bgp->config, BGP_CONFIG_CONFEDERATION))
+ json_object_boolean_true_add(
+ json_neigh, "nbrConfedExternalLink");
+ else
+ json_object_boolean_true_add(json_neigh,
+ "nbrExternalLink");
+ } else {
+ if (bgp_confederation_peers_check(bgp, p->as))
+ vty_out(vty, "confed-external link\n");
+ else
+ vty_out(vty, "external link\n");
+ }
+ } else {
+ if (use_json)
+ json_object_boolean_true_add(json_neigh,
+ "nbrUnspecifiedLink");
+ else
+ vty_out(vty, "unspecified link\n");
+ }
+
+ /* Roles */
+ if (use_json) {
+ json_object_string_add(json_neigh, "localRole",
+ bgp_get_name_by_role(p->local_role));
+ json_object_string_add(json_neigh, "remoteRole",
+ bgp_get_name_by_role(p->remote_role));
+ } else {
+ vty_out(vty, " Local Role: %s\n",
+ bgp_get_name_by_role(p->local_role));
+ vty_out(vty, " Remote Role: %s\n",
+ bgp_get_name_by_role(p->remote_role));
+ }
+
+
+ /* Description. */
+ if (p->desc) {
+ if (use_json)
+ json_object_string_add(json_neigh, "nbrDesc", p->desc);
+ else
+ vty_out(vty, " Description: %s\n", p->desc);
+ }
+
+ if (p->hostname) {
+ if (use_json) {
+ if (p->hostname)
+ json_object_string_add(json_neigh, "hostname",
+ p->hostname);
+
+ if (p->domainname)
+ json_object_string_add(json_neigh, "domainname",
+ p->domainname);
+ } else {
+ if (p->domainname && (p->domainname[0] != '\0'))
+ vty_out(vty, "Hostname: %s.%s\n", p->hostname,
+ p->domainname);
+ else
+ vty_out(vty, "Hostname: %s\n", p->hostname);
+ }
+ }
+
+ /* Peer-group */
+ if (p->group) {
+ if (use_json) {
+ json_object_string_add(json_neigh, "peerGroup",
+ p->group->name);
+
+ if (dn_flag[0]) {
+ struct prefix prefix, *range = NULL;
+
+ if (sockunion2hostprefix(&(p->su), &prefix))
+ range = peer_group_lookup_dynamic_neighbor_range(
+ p->group, &prefix);
+
+ if (range) {
+ json_object_string_addf(
+ json_neigh,
+ "peerSubnetRangeGroup", "%pFX",
+ range);
+ }
+ }
+ } else {
+ vty_out(vty,
+ " Member of peer-group %s for session parameters\n",
+ p->group->name);
+
+ if (dn_flag[0]) {
+ struct prefix prefix, *range = NULL;
+
+ if (sockunion2hostprefix(&(p->su), &prefix))
+ range = peer_group_lookup_dynamic_neighbor_range(
+ p->group, &prefix);
+
+ if (range) {
+ vty_out(vty,
+ " Belongs to the subnet range group: %pFX\n",
+ range);
+ }
+ }
+ }
+ }
+
+ if (use_json) {
+ /* Administrative shutdown. */
+ if (CHECK_FLAG(p->flags, PEER_FLAG_SHUTDOWN)
+ || CHECK_FLAG(p->bgp->flags, BGP_FLAG_SHUTDOWN))
+ json_object_boolean_true_add(json_neigh,
+ "adminShutDown");
+
+ /* BGP Version. */
+ json_object_int_add(json_neigh, "bgpVersion", 4);
+ json_object_string_addf(json_neigh, "remoteRouterId", "%pI4",
+ &p->remote_id);
+ json_object_string_addf(json_neigh, "localRouterId", "%pI4",
+ &bgp->router_id);
+
+ /* Confederation */
+ if (CHECK_FLAG(bgp->config, BGP_CONFIG_CONFEDERATION)
+ && bgp_confederation_peers_check(bgp, p->as))
+ json_object_boolean_true_add(json_neigh,
+ "nbrCommonAdmin");
+
+ /* Status. */
+ json_object_string_add(
+ json_neigh, "bgpState",
+ lookup_msg(bgp_status_msg, p->status, NULL));
+
+ if (peer_established(p)) {
+ time_t uptime;
+
+ uptime = monotime(NULL);
+ uptime -= p->uptime;
+ epoch_tbuf = time(NULL) - uptime;
+
+ json_object_int_add(json_neigh, "bgpTimerUpMsec",
+ uptime * 1000);
+ json_object_string_add(json_neigh, "bgpTimerUpString",
+ peer_uptime(p->uptime, timebuf,
+ BGP_UPTIME_LEN, 0,
+ NULL));
+ json_object_int_add(json_neigh,
+ "bgpTimerUpEstablishedEpoch",
+ epoch_tbuf);
+ }
+
+ else if (p->status == Active) {
+ if (CHECK_FLAG(p->flags, PEER_FLAG_PASSIVE))
+ json_object_string_add(json_neigh, "bgpStateIs",
+ "passive");
+ else if (CHECK_FLAG(p->sflags, PEER_STATUS_NSF_WAIT))
+ json_object_string_add(json_neigh, "bgpStateIs",
+ "passiveNSF");
+ }
+
+ /* read timer */
+ time_t uptime;
+ struct tm tm;
+
+ uptime = monotime(NULL);
+ uptime -= p->readtime;
+ gmtime_r(&uptime, &tm);
+
+ json_object_int_add(json_neigh, "bgpTimerLastRead",
+ (tm.tm_sec * 1000) + (tm.tm_min * 60000)
+ + (tm.tm_hour * 3600000));
+
+ uptime = monotime(NULL);
+ uptime -= p->last_write;
+ gmtime_r(&uptime, &tm);
+
+ json_object_int_add(json_neigh, "bgpTimerLastWrite",
+ (tm.tm_sec * 1000) + (tm.tm_min * 60000)
+ + (tm.tm_hour * 3600000));
+
+ uptime = monotime(NULL);
+ uptime -= p->update_time;
+ gmtime_r(&uptime, &tm);
+
+ json_object_int_add(json_neigh, "bgpInUpdateElapsedTimeMsecs",
+ (tm.tm_sec * 1000) + (tm.tm_min * 60000)
+ + (tm.tm_hour * 3600000));
+
+ /* Configured timer values. */
+ json_object_int_add(json_neigh,
+ "bgpTimerConfiguredHoldTimeMsecs",
+ CHECK_FLAG(p->flags, PEER_FLAG_TIMER)
+ ? p->holdtime * 1000
+ : bgp->default_holdtime * 1000);
+ json_object_int_add(json_neigh,
+ "bgpTimerConfiguredKeepAliveIntervalMsecs",
+ CHECK_FLAG(p->flags, PEER_FLAG_TIMER)
+ ? p->keepalive * 1000
+ : bgp->default_keepalive * 1000);
+ json_object_int_add(json_neigh, "bgpTimerHoldTimeMsecs",
+ p->v_holdtime * 1000);
+ json_object_int_add(json_neigh,
+ "bgpTimerKeepAliveIntervalMsecs",
+ p->v_keepalive * 1000);
+ if (CHECK_FLAG(p->flags, PEER_FLAG_TIMER_DELAYOPEN)) {
+ json_object_int_add(json_neigh,
+ "bgpTimerDelayOpenTimeMsecs",
+ p->v_delayopen * 1000);
+ }
+
+ /* Configured and Synced tcp-mss value for peer */
+ if (CHECK_FLAG(p->flags, PEER_FLAG_TCP_MSS)) {
+ sync_tcp_mss = sockopt_tcp_mss_get(p->fd);
+ json_object_int_add(json_neigh, "bgpTcpMssConfigured",
+ p->tcp_mss);
+ json_object_int_add(json_neigh, "bgpTcpMssSynced",
+ sync_tcp_mss);
+ }
+
+ /* Extended Optional Parameters Length for BGP OPEN Message */
+ if (BGP_OPEN_EXT_OPT_PARAMS_CAPABLE(p))
+ json_object_boolean_true_add(
+ json_neigh, "extendedOptionalParametersLength");
+ else
+ json_object_boolean_false_add(
+ json_neigh, "extendedOptionalParametersLength");
+
+ /* Conditional advertisements */
+ json_object_int_add(
+ json_neigh,
+ "bgpTimerConfiguredConditionalAdvertisementsSec",
+ bgp->condition_check_period);
+ if (thread_is_scheduled(bgp->t_condition_check))
+ json_object_int_add(
+ json_neigh,
+ "bgpTimerUntilConditionalAdvertisementsSec",
+ thread_timer_remain_second(
+ bgp->t_condition_check));
+ } else {
+ /* Administrative shutdown. */
+ if (CHECK_FLAG(p->flags, PEER_FLAG_SHUTDOWN)
+ || CHECK_FLAG(p->bgp->flags, BGP_FLAG_SHUTDOWN))
+ vty_out(vty, " Administratively shut down\n");
+
+ /* BGP Version. */
+ vty_out(vty, " BGP version 4");
+ vty_out(vty, ", remote router ID %s",
+ inet_ntop(AF_INET, &p->remote_id, buf1, sizeof(buf1)));
+ vty_out(vty, ", local router ID %s\n",
+ inet_ntop(AF_INET, &bgp->router_id, buf1,
+ sizeof(buf1)));
+
+ /* Confederation */
+ if (CHECK_FLAG(bgp->config, BGP_CONFIG_CONFEDERATION)
+ && bgp_confederation_peers_check(bgp, p->as))
+ vty_out(vty,
+ " Neighbor under common administration\n");
+
+ /* Status. */
+ vty_out(vty, " BGP state = %s",
+ lookup_msg(bgp_status_msg, p->status, NULL));
+
+ if (peer_established(p))
+ vty_out(vty, ", up for %8s",
+ peer_uptime(p->uptime, timebuf, BGP_UPTIME_LEN,
+ 0, NULL));
+
+ else if (p->status == Active) {
+ if (CHECK_FLAG(p->flags, PEER_FLAG_PASSIVE))
+ vty_out(vty, " (passive)");
+ else if (CHECK_FLAG(p->sflags, PEER_STATUS_NSF_WAIT))
+ vty_out(vty, " (NSF passive)");
+ }
+ vty_out(vty, "\n");
+
+ /* read timer */
+ vty_out(vty, " Last read %s",
+ peer_uptime(p->readtime, timebuf, BGP_UPTIME_LEN, 0,
+ NULL));
+ vty_out(vty, ", Last write %s\n",
+ peer_uptime(p->last_write, timebuf, BGP_UPTIME_LEN, 0,
+ NULL));
+
+ /* Configured timer values. */
+ vty_out(vty,
+ " Hold time is %d seconds, keepalive interval is %d seconds\n",
+ p->v_holdtime, p->v_keepalive);
+ vty_out(vty, " Configured hold time is %d seconds",
+ CHECK_FLAG(p->flags, PEER_FLAG_TIMER)
+ ? p->holdtime
+ : bgp->default_holdtime);
+ vty_out(vty, ", keepalive interval is %d seconds\n",
+ CHECK_FLAG(p->flags, PEER_FLAG_TIMER)
+ ? p->keepalive
+ : bgp->default_keepalive);
+ if (CHECK_FLAG(p->flags, PEER_FLAG_TIMER_DELAYOPEN))
+ vty_out(vty,
+ " Configured DelayOpenTime is %d seconds\n",
+ p->delayopen);
+
+ /* Configured and synced tcp-mss value for peer */
+ if (CHECK_FLAG(p->flags, PEER_FLAG_TCP_MSS)) {
+ sync_tcp_mss = sockopt_tcp_mss_get(p->fd);
+ vty_out(vty, " Configured tcp-mss is %d", p->tcp_mss);
+ vty_out(vty, ", synced tcp-mss is %d\n", sync_tcp_mss);
+ }
+
+ /* Extended Optional Parameters Length for BGP OPEN Message */
+ if (BGP_OPEN_EXT_OPT_PARAMS_CAPABLE(p))
+ vty_out(vty,
+ " Extended Optional Parameters Length is enabled\n");
+
+ /* Conditional advertisements */
+ vty_out(vty,
+ " Configured conditional advertisements interval is %d seconds\n",
+ bgp->condition_check_period);
+ if (thread_is_scheduled(bgp->t_condition_check))
+ vty_out(vty,
+ " Time until conditional advertisements begin is %lu seconds\n",
+ thread_timer_remain_second(
+ bgp->t_condition_check));
+ }
+ /* Capability. */
+ if (peer_established(p) &&
+ (p->cap || peer_afc_advertised(p) || peer_afc_received(p))) {
+ if (use_json) {
+ json_object *json_cap = NULL;
+
+ json_cap = json_object_new_object();
+
+ /* AS4 */
+ if (CHECK_FLAG(p->cap, PEER_CAP_AS4_RCV) ||
+ CHECK_FLAG(p->cap, PEER_CAP_AS4_ADV)) {
+ if (CHECK_FLAG(p->cap, PEER_CAP_AS4_ADV) &&
+ CHECK_FLAG(p->cap, PEER_CAP_AS4_RCV))
+ json_object_string_add(
+ json_cap, "4byteAs",
+ "advertisedAndReceived");
+ else if (CHECK_FLAG(p->cap, PEER_CAP_AS4_ADV))
+ json_object_string_add(json_cap,
+ "4byteAs",
+ "advertised");
+ else if (CHECK_FLAG(p->cap, PEER_CAP_AS4_RCV))
+ json_object_string_add(json_cap,
+ "4byteAs",
+ "received");
+ }
+
+ /* Extended Message Support */
+ if (CHECK_FLAG(p->cap, PEER_CAP_EXTENDED_MESSAGE_ADV) &&
+ CHECK_FLAG(p->cap, PEER_CAP_EXTENDED_MESSAGE_RCV))
+ json_object_string_add(json_cap,
+ "extendedMessage",
+ "advertisedAndReceived");
+ else if (CHECK_FLAG(p->cap,
+ PEER_CAP_EXTENDED_MESSAGE_ADV))
+ json_object_string_add(json_cap,
+ "extendedMessage",
+ "advertised");
+ else if (CHECK_FLAG(p->cap,
+ PEER_CAP_EXTENDED_MESSAGE_RCV))
+ json_object_string_add(json_cap,
+ "extendedMessage",
+ "received");
+
+ /* AddPath */
+ if (CHECK_FLAG(p->cap, PEER_CAP_ADDPATH_RCV) ||
+ CHECK_FLAG(p->cap, PEER_CAP_ADDPATH_ADV)) {
+ json_object *json_add = NULL;
+ const char *print_store;
+
+ json_add = json_object_new_object();
+
+ FOREACH_AFI_SAFI (afi, safi) {
+ json_object *json_sub = NULL;
+ json_sub = json_object_new_object();
+ print_store = get_afi_safi_str(
+ afi, safi, true);
+
+ if (CHECK_FLAG(
+ p->af_cap[afi][safi],
+ PEER_CAP_ADDPATH_AF_TX_ADV) ||
+ CHECK_FLAG(
+ p->af_cap[afi][safi],
+ PEER_CAP_ADDPATH_AF_TX_RCV)) {
+ if (CHECK_FLAG(
+ p->af_cap[afi]
+ [safi],
+ PEER_CAP_ADDPATH_AF_TX_ADV) &&
+ CHECK_FLAG(
+ p->af_cap[afi]
+ [safi],
+ PEER_CAP_ADDPATH_AF_TX_RCV))
+ json_object_boolean_true_add(
+ json_sub,
+ "txAdvertisedAndReceived");
+ else if (
+ CHECK_FLAG(
+ p->af_cap[afi]
+ [safi],
+ PEER_CAP_ADDPATH_AF_TX_ADV))
+ json_object_boolean_true_add(
+ json_sub,
+ "txAdvertised");
+ else if (
+ CHECK_FLAG(
+ p->af_cap[afi]
+ [safi],
+ PEER_CAP_ADDPATH_AF_TX_RCV))
+ json_object_boolean_true_add(
+ json_sub,
+ "txReceived");
+ }
+
+ if (CHECK_FLAG(
+ p->af_cap[afi][safi],
+ PEER_CAP_ADDPATH_AF_RX_ADV) ||
+ CHECK_FLAG(
+ p->af_cap[afi][safi],
+ PEER_CAP_ADDPATH_AF_RX_RCV)) {
+ if (CHECK_FLAG(
+ p->af_cap[afi]
+ [safi],
+ PEER_CAP_ADDPATH_AF_RX_ADV) &&
+ CHECK_FLAG(
+ p->af_cap[afi]
+ [safi],
+ PEER_CAP_ADDPATH_AF_RX_RCV))
+ json_object_boolean_true_add(
+ json_sub,
+ "rxAdvertisedAndReceived");
+ else if (
+ CHECK_FLAG(
+ p->af_cap[afi]
+ [safi],
+ PEER_CAP_ADDPATH_AF_RX_ADV))
+ json_object_boolean_true_add(
+ json_sub,
+ "rxAdvertised");
+ else if (
+ CHECK_FLAG(
+ p->af_cap[afi]
+ [safi],
+ PEER_CAP_ADDPATH_AF_RX_RCV))
+ json_object_boolean_true_add(
+ json_sub,
+ "rxReceived");
+ }
+
+ if (CHECK_FLAG(
+ p->af_cap[afi][safi],
+ PEER_CAP_ADDPATH_AF_TX_ADV) ||
+ CHECK_FLAG(
+ p->af_cap[afi][safi],
+ PEER_CAP_ADDPATH_AF_TX_RCV) ||
+ CHECK_FLAG(
+ p->af_cap[afi][safi],
+ PEER_CAP_ADDPATH_AF_RX_ADV) ||
+ CHECK_FLAG(
+ p->af_cap[afi][safi],
+ PEER_CAP_ADDPATH_AF_RX_RCV))
+ json_object_object_add(
+ json_add, print_store,
+ json_sub);
+ else
+ json_object_free(json_sub);
+ }
+
+ json_object_object_add(json_cap, "addPath",
+ json_add);
+ }
+
+ /* Dynamic */
+ if (CHECK_FLAG(p->cap, PEER_CAP_DYNAMIC_RCV) ||
+ CHECK_FLAG(p->cap, PEER_CAP_DYNAMIC_ADV)) {
+ if (CHECK_FLAG(p->cap, PEER_CAP_DYNAMIC_ADV) &&
+ CHECK_FLAG(p->cap, PEER_CAP_DYNAMIC_RCV))
+ json_object_string_add(
+ json_cap, "dynamic",
+ "advertisedAndReceived");
+ else if (CHECK_FLAG(p->cap,
+ PEER_CAP_DYNAMIC_ADV))
+ json_object_string_add(json_cap,
+ "dynamic",
+ "advertised");
+ else if (CHECK_FLAG(p->cap,
+ PEER_CAP_DYNAMIC_RCV))
+ json_object_string_add(json_cap,
+ "dynamic",
+ "received");
+ }
+
+ /* Role */
+ if (CHECK_FLAG(p->cap, PEER_CAP_ROLE_RCV) ||
+ CHECK_FLAG(p->cap, PEER_CAP_ROLE_ADV)) {
+ if (CHECK_FLAG(p->cap, PEER_CAP_ROLE_ADV) &&
+ CHECK_FLAG(p->cap, PEER_CAP_ROLE_RCV))
+ json_object_string_add(
+ json_cap, "role",
+ "advertisedAndReceived");
+ else if (CHECK_FLAG(p->cap, PEER_CAP_ROLE_ADV))
+ json_object_string_add(json_cap, "role",
+ "advertised");
+ else if (CHECK_FLAG(p->cap, PEER_CAP_ROLE_RCV))
+ json_object_string_add(json_cap, "role",
+ "received");
+ }
+
+ /* Extended nexthop */
+ if (CHECK_FLAG(p->cap, PEER_CAP_ENHE_RCV) ||
+ CHECK_FLAG(p->cap, PEER_CAP_ENHE_ADV)) {
+ json_object *json_nxt = NULL;
+ const char *print_store;
+
+
+ if (CHECK_FLAG(p->cap, PEER_CAP_ENHE_ADV) &&
+ CHECK_FLAG(p->cap, PEER_CAP_ENHE_RCV))
+ json_object_string_add(
+ json_cap, "extendedNexthop",
+ "advertisedAndReceived");
+ else if (CHECK_FLAG(p->cap, PEER_CAP_ENHE_ADV))
+ json_object_string_add(
+ json_cap, "extendedNexthop",
+ "advertised");
+ else if (CHECK_FLAG(p->cap, PEER_CAP_ENHE_RCV))
+ json_object_string_add(
+ json_cap, "extendedNexthop",
+ "received");
+
+ if (CHECK_FLAG(p->cap, PEER_CAP_ENHE_RCV)) {
+ json_nxt = json_object_new_object();
+
+ for (safi = SAFI_UNICAST;
+ safi < SAFI_MAX; safi++) {
+ if (CHECK_FLAG(
+ p->af_cap[AFI_IP]
+ [safi],
+ PEER_CAP_ENHE_AF_RCV)) {
+ print_store =
+ get_afi_safi_str(
+ AFI_IP,
+ safi,
+ true);
+ json_object_string_add(
+ json_nxt,
+ print_store,
+ "recieved"); /* misspelled for compatibility */
+ }
+ }
+ json_object_object_add(
+ json_cap,
+ "extendedNexthopFamililesByPeer",
+ json_nxt);
+ }
+ }
+
+ /* Long-lived Graceful Restart */
+ if (CHECK_FLAG(p->cap, PEER_CAP_LLGR_RCV) ||
+ CHECK_FLAG(p->cap, PEER_CAP_LLGR_ADV)) {
+ json_object *json_llgr = NULL;
+ const char *afi_safi_str;
+
+ if (CHECK_FLAG(p->cap, PEER_CAP_LLGR_ADV) &&
+ CHECK_FLAG(p->cap, PEER_CAP_LLGR_RCV))
+ json_object_string_add(
+ json_cap,
+ "longLivedGracefulRestart",
+ "advertisedAndReceived");
+ else if (CHECK_FLAG(p->cap, PEER_CAP_LLGR_ADV))
+ json_object_string_add(
+ json_cap,
+ "longLivedGracefulRestart",
+ "advertised");
+ else if (CHECK_FLAG(p->cap, PEER_CAP_LLGR_RCV))
+ json_object_string_add(
+ json_cap,
+ "longLivedGracefulRestart",
+ "received");
+
+ if (CHECK_FLAG(p->cap, PEER_CAP_LLGR_RCV)) {
+ json_llgr = json_object_new_object();
+
+ FOREACH_AFI_SAFI (afi, safi) {
+ if (CHECK_FLAG(
+ p->af_cap[afi]
+ [safi],
+ PEER_CAP_ENHE_AF_RCV)) {
+ afi_safi_str =
+ get_afi_safi_str(
+ afi,
+ safi,
+ true);
+ json_object_string_add(
+ json_llgr,
+ afi_safi_str,
+ "received");
+ }
+ }
+ json_object_object_add(
+ json_cap,
+ "longLivedGracefulRestartByPeer",
+ json_llgr);
+ }
+ }
+
+ /* Route Refresh */
+ if (CHECK_FLAG(p->cap, PEER_CAP_REFRESH_ADV) ||
+ CHECK_FLAG(p->cap, PEER_CAP_REFRESH_NEW_RCV) ||
+ CHECK_FLAG(p->cap, PEER_CAP_REFRESH_OLD_RCV)) {
+ if (CHECK_FLAG(p->cap, PEER_CAP_REFRESH_ADV) &&
+ (CHECK_FLAG(p->cap,
+ PEER_CAP_REFRESH_NEW_RCV) ||
+ CHECK_FLAG(p->cap,
+ PEER_CAP_REFRESH_OLD_RCV))) {
+ if (CHECK_FLAG(
+ p->cap,
+ PEER_CAP_REFRESH_OLD_RCV) &&
+ CHECK_FLAG(
+ p->cap,
+ PEER_CAP_REFRESH_NEW_RCV))
+ json_object_string_add(
+ json_cap,
+ "routeRefresh",
+ "advertisedAndReceivedOldNew");
+ else {
+ if (CHECK_FLAG(
+ p->cap,
+ PEER_CAP_REFRESH_OLD_RCV))
+ json_object_string_add(
+ json_cap,
+ "routeRefresh",
+ "advertisedAndReceivedOld");
+ else
+ json_object_string_add(
+ json_cap,
+ "routeRefresh",
+ "advertisedAndReceivedNew");
+ }
+ } else if (CHECK_FLAG(p->cap,
+ PEER_CAP_REFRESH_ADV))
+ json_object_string_add(json_cap,
+ "routeRefresh",
+ "advertised");
+ else if (CHECK_FLAG(p->cap,
+ PEER_CAP_REFRESH_NEW_RCV) ||
+ CHECK_FLAG(p->cap,
+ PEER_CAP_REFRESH_OLD_RCV))
+ json_object_string_add(json_cap,
+ "routeRefresh",
+ "received");
+ }
+
+ /* Enhanced Route Refresh */
+ if (CHECK_FLAG(p->cap, PEER_CAP_ENHANCED_RR_ADV) ||
+ CHECK_FLAG(p->cap, PEER_CAP_ENHANCED_RR_RCV)) {
+ if (CHECK_FLAG(p->cap,
+ PEER_CAP_ENHANCED_RR_ADV) &&
+ CHECK_FLAG(p->cap,
+ PEER_CAP_ENHANCED_RR_RCV))
+ json_object_string_add(
+ json_cap,
+ "enhancedRouteRefresh",
+ "advertisedAndReceived");
+ else if (CHECK_FLAG(p->cap,
+ PEER_CAP_ENHANCED_RR_ADV))
+ json_object_string_add(
+ json_cap,
+ "enhancedRouteRefresh",
+ "advertised");
+ else if (CHECK_FLAG(p->cap,
+ PEER_CAP_ENHANCED_RR_RCV))
+ json_object_string_add(
+ json_cap,
+ "enhancedRouteRefresh",
+ "received");
+ }
+
+ /* Multiprotocol Extensions */
+ json_object *json_multi = NULL;
+
+ json_multi = json_object_new_object();
+
+ FOREACH_AFI_SAFI (afi, safi) {
+ if (p->afc_adv[afi][safi] ||
+ p->afc_recv[afi][safi]) {
+ json_object *json_exten = NULL;
+ json_exten = json_object_new_object();
+
+ if (p->afc_adv[afi][safi] &&
+ p->afc_recv[afi][safi])
+ json_object_boolean_true_add(
+ json_exten,
+ "advertisedAndReceived");
+ else if (p->afc_adv[afi][safi])
+ json_object_boolean_true_add(
+ json_exten,
+ "advertised");
+ else if (p->afc_recv[afi][safi])
+ json_object_boolean_true_add(
+ json_exten, "received");
+
+ json_object_object_add(
+ json_multi,
+ get_afi_safi_str(afi, safi,
+ true),
+ json_exten);
+ }
+ }
+ json_object_object_add(json_cap,
+ "multiprotocolExtensions",
+ json_multi);
+
+ /* Hostname capabilities */
+ json_object *json_hname = NULL;
+
+ json_hname = json_object_new_object();
+
+ if (CHECK_FLAG(p->cap, PEER_CAP_HOSTNAME_ADV)) {
+ json_object_string_add(
+ json_hname, "advHostName",
+ bgp->peer_self->hostname
+ ? bgp->peer_self->hostname
+ : "n/a");
+ json_object_string_add(
+ json_hname, "advDomainName",
+ bgp->peer_self->domainname
+ ? bgp->peer_self->domainname
+ : "n/a");
+ }
+
+
+ if (CHECK_FLAG(p->cap, PEER_CAP_HOSTNAME_RCV)) {
+ json_object_string_add(
+ json_hname, "rcvHostName",
+ p->hostname ? p->hostname : "n/a");
+ json_object_string_add(
+ json_hname, "rcvDomainName",
+ p->domainname ? p->domainname : "n/a");
+ }
+
+ json_object_object_add(json_cap, "hostName",
+ json_hname);
+
+ /* Graceful Restart */
+ if (CHECK_FLAG(p->cap, PEER_CAP_RESTART_RCV) ||
+ CHECK_FLAG(p->cap, PEER_CAP_RESTART_ADV)) {
+ if (CHECK_FLAG(p->cap, PEER_CAP_RESTART_ADV) &&
+ CHECK_FLAG(p->cap, PEER_CAP_RESTART_RCV))
+ json_object_string_add(
+ json_cap, "gracefulRestart",
+ "advertisedAndReceived");
+ else if (CHECK_FLAG(p->cap,
+ PEER_CAP_RESTART_ADV))
+ json_object_string_add(
+ json_cap,
+ "gracefulRestartCapability",
+ "advertised");
+ else if (CHECK_FLAG(p->cap,
+ PEER_CAP_RESTART_RCV))
+ json_object_string_add(
+ json_cap,
+ "gracefulRestartCapability",
+ "received");
+
+ if (CHECK_FLAG(p->cap, PEER_CAP_RESTART_RCV)) {
+ int restart_af_count = 0;
+ json_object *json_restart = NULL;
+ json_restart = json_object_new_object();
+
+ json_object_int_add(
+ json_cap,
+ "gracefulRestartRemoteTimerMsecs",
+ p->v_gr_restart * 1000);
+
+ FOREACH_AFI_SAFI (afi, safi) {
+ if (CHECK_FLAG(
+ p->af_cap[afi]
+ [safi],
+ PEER_CAP_RESTART_AF_RCV)) {
+ json_object *json_sub =
+ NULL;
+ json_sub =
+ json_object_new_object();
+
+ if (CHECK_FLAG(
+ p->af_cap
+ [afi]
+ [safi],
+ PEER_CAP_RESTART_AF_PRESERVE_RCV))
+ json_object_boolean_true_add(
+ json_sub,
+ "preserved");
+ restart_af_count++;
+ json_object_object_add(
+ json_restart,
+ get_afi_safi_str(
+ afi,
+ safi,
+ true),
+ json_sub);
+ }
+ }
+ if (!restart_af_count) {
+ json_object_string_add(
+ json_cap,
+ "addressFamiliesByPeer",
+ "none");
+ json_object_free(json_restart);
+ } else
+ json_object_object_add(
+ json_cap,
+ "addressFamiliesByPeer",
+ json_restart);
+ }
+ }
+ json_object_object_add(
+ json_neigh, "neighborCapabilities", json_cap);
+ } else {
+ vty_out(vty, " Neighbor capabilities:\n");
+
+ /* AS4 */
+ if (CHECK_FLAG(p->cap, PEER_CAP_AS4_RCV) ||
+ CHECK_FLAG(p->cap, PEER_CAP_AS4_ADV)) {
+ vty_out(vty, " 4 Byte AS:");
+ if (CHECK_FLAG(p->cap, PEER_CAP_AS4_ADV))
+ vty_out(vty, " advertised");
+ if (CHECK_FLAG(p->cap, PEER_CAP_AS4_RCV))
+ vty_out(vty, " %sreceived",
+ CHECK_FLAG(p->cap,
+ PEER_CAP_AS4_ADV)
+ ? "and "
+ : "");
+ vty_out(vty, "\n");
+ }
+
+ /* Extended Message Support */
+ if (CHECK_FLAG(p->cap, PEER_CAP_EXTENDED_MESSAGE_RCV) ||
+ CHECK_FLAG(p->cap, PEER_CAP_EXTENDED_MESSAGE_ADV)) {
+ vty_out(vty, " Extended Message:");
+ if (CHECK_FLAG(p->cap,
+ PEER_CAP_EXTENDED_MESSAGE_ADV))
+ vty_out(vty, " advertised");
+ if (CHECK_FLAG(p->cap,
+ PEER_CAP_EXTENDED_MESSAGE_RCV))
+ vty_out(vty, " %sreceived",
+ CHECK_FLAG(
+ p->cap,
+ PEER_CAP_EXTENDED_MESSAGE_ADV)
+ ? "and "
+ : "");
+ vty_out(vty, "\n");
+ }
+
+ /* AddPath */
+ if (CHECK_FLAG(p->cap, PEER_CAP_ADDPATH_RCV) ||
+ CHECK_FLAG(p->cap, PEER_CAP_ADDPATH_ADV)) {
+ vty_out(vty, " AddPath:\n");
+
+ FOREACH_AFI_SAFI (afi, safi) {
+ if (CHECK_FLAG(
+ p->af_cap[afi][safi],
+ PEER_CAP_ADDPATH_AF_TX_ADV) ||
+ CHECK_FLAG(
+ p->af_cap[afi][safi],
+ PEER_CAP_ADDPATH_AF_TX_RCV)) {
+ vty_out(vty, " %s: TX ",
+ get_afi_safi_str(
+ afi, safi,
+ false));
+
+ if (CHECK_FLAG(
+ p->af_cap[afi]
+ [safi],
+ PEER_CAP_ADDPATH_AF_TX_ADV))
+ vty_out(vty,
+ "advertised");
+
+ if (CHECK_FLAG(
+ p->af_cap[afi]
+ [safi],
+ PEER_CAP_ADDPATH_AF_TX_RCV))
+ vty_out(vty,
+ "%sreceived",
+ CHECK_FLAG(
+ p->af_cap
+ [afi]
+ [safi],
+ PEER_CAP_ADDPATH_AF_TX_ADV)
+ ? " and "
+ : "");
+
+ vty_out(vty, "\n");
+ }
+
+ if (CHECK_FLAG(
+ p->af_cap[afi][safi],
+ PEER_CAP_ADDPATH_AF_RX_ADV) ||
+ CHECK_FLAG(
+ p->af_cap[afi][safi],
+ PEER_CAP_ADDPATH_AF_RX_RCV)) {
+ vty_out(vty, " %s: RX ",
+ get_afi_safi_str(
+ afi, safi,
+ false));
+
+ if (CHECK_FLAG(
+ p->af_cap[afi]
+ [safi],
+ PEER_CAP_ADDPATH_AF_RX_ADV))
+ vty_out(vty,
+ "advertised");
+
+ if (CHECK_FLAG(
+ p->af_cap[afi]
+ [safi],
+ PEER_CAP_ADDPATH_AF_RX_RCV))
+ vty_out(vty,
+ "%sreceived",
+ CHECK_FLAG(
+ p->af_cap
+ [afi]
+ [safi],
+ PEER_CAP_ADDPATH_AF_RX_ADV)
+ ? " and "
+ : "");
+
+ vty_out(vty, "\n");
+ }
+ }
+ }
+
+ /* Dynamic */
+ if (CHECK_FLAG(p->cap, PEER_CAP_DYNAMIC_RCV) ||
+ CHECK_FLAG(p->cap, PEER_CAP_DYNAMIC_ADV)) {
+ vty_out(vty, " Dynamic:");
+ if (CHECK_FLAG(p->cap, PEER_CAP_DYNAMIC_ADV))
+ vty_out(vty, " advertised");
+ if (CHECK_FLAG(p->cap, PEER_CAP_DYNAMIC_RCV))
+ vty_out(vty, " %sreceived",
+ CHECK_FLAG(p->cap,
+ PEER_CAP_DYNAMIC_ADV)
+ ? "and "
+ : "");
+ vty_out(vty, "\n");
+ }
+
+ /* Role */
+ if (CHECK_FLAG(p->cap, PEER_CAP_ROLE_RCV) ||
+ CHECK_FLAG(p->cap, PEER_CAP_ROLE_ADV)) {
+ vty_out(vty, " Role:");
+ if (CHECK_FLAG(p->cap, PEER_CAP_ROLE_ADV))
+ vty_out(vty, " advertised");
+ if (CHECK_FLAG(p->cap, PEER_CAP_ROLE_RCV))
+ vty_out(vty, " %sreceived",
+ CHECK_FLAG(p->cap,
+ PEER_CAP_ROLE_ADV)
+ ? "and "
+ : "");
+ vty_out(vty, "\n");
+ }
+
+ /* Extended nexthop */
+ if (CHECK_FLAG(p->cap, PEER_CAP_ENHE_RCV) ||
+ CHECK_FLAG(p->cap, PEER_CAP_ENHE_ADV)) {
+ vty_out(vty, " Extended nexthop:");
+ if (CHECK_FLAG(p->cap, PEER_CAP_ENHE_ADV))
+ vty_out(vty, " advertised");
+ if (CHECK_FLAG(p->cap, PEER_CAP_ENHE_RCV))
+ vty_out(vty, " %sreceived",
+ CHECK_FLAG(p->cap,
+ PEER_CAP_ENHE_ADV)
+ ? "and "
+ : "");
+ vty_out(vty, "\n");
+
+ if (CHECK_FLAG(p->cap, PEER_CAP_ENHE_RCV)) {
+ vty_out(vty,
+ " Address families by peer:\n ");
+ for (safi = SAFI_UNICAST;
+ safi < SAFI_MAX; safi++)
+ if (CHECK_FLAG(
+ p->af_cap[AFI_IP]
+ [safi],
+ PEER_CAP_ENHE_AF_RCV))
+ vty_out(vty,
+ " %s\n",
+ get_afi_safi_str(
+ AFI_IP,
+ safi,
+ false));
+ }
+ }
+
+ /* Long-lived Graceful Restart */
+ if (CHECK_FLAG(p->cap, PEER_CAP_LLGR_RCV) ||
+ CHECK_FLAG(p->cap, PEER_CAP_LLGR_ADV)) {
+ vty_out(vty,
+ " Long-lived Graceful Restart:");
+ if (CHECK_FLAG(p->cap, PEER_CAP_LLGR_ADV))
+ vty_out(vty, " advertised");
+ if (CHECK_FLAG(p->cap, PEER_CAP_LLGR_RCV))
+ vty_out(vty, " %sreceived",
+ CHECK_FLAG(p->cap,
+ PEER_CAP_LLGR_ADV)
+ ? "and "
+ : "");
+ vty_out(vty, "\n");
+
+ if (CHECK_FLAG(p->cap, PEER_CAP_LLGR_RCV)) {
+ vty_out(vty,
+ " Address families by peer:\n");
+ FOREACH_AFI_SAFI (afi, safi)
+ if (CHECK_FLAG(
+ p->af_cap[afi]
+ [safi],
+ PEER_CAP_LLGR_AF_RCV))
+ vty_out(vty,
+ " %s\n",
+ get_afi_safi_str(
+ afi,
+ safi,
+ false));
+ }
+ }
+
+ /* Route Refresh */
+ if (CHECK_FLAG(p->cap, PEER_CAP_REFRESH_ADV) ||
+ CHECK_FLAG(p->cap, PEER_CAP_REFRESH_NEW_RCV) ||
+ CHECK_FLAG(p->cap, PEER_CAP_REFRESH_OLD_RCV)) {
+ vty_out(vty, " Route refresh:");
+ if (CHECK_FLAG(p->cap, PEER_CAP_REFRESH_ADV))
+ vty_out(vty, " advertised");
+ if (CHECK_FLAG(p->cap,
+ PEER_CAP_REFRESH_NEW_RCV) ||
+ CHECK_FLAG(p->cap,
+ PEER_CAP_REFRESH_OLD_RCV))
+ vty_out(vty, " %sreceived(%s)",
+ CHECK_FLAG(p->cap,
+ PEER_CAP_REFRESH_ADV)
+ ? "and "
+ : "",
+ (CHECK_FLAG(
+ p->cap,
+ PEER_CAP_REFRESH_OLD_RCV) &&
+ CHECK_FLAG(
+ p->cap,
+ PEER_CAP_REFRESH_NEW_RCV))
+ ? "old & new"
+ : CHECK_FLAG(
+ p->cap,
+ PEER_CAP_REFRESH_OLD_RCV)
+ ? "old"
+ : "new");
+
+ vty_out(vty, "\n");
+ }
+
+ /* Enhanced Route Refresh */
+ if (CHECK_FLAG(p->cap, PEER_CAP_ENHANCED_RR_ADV) ||
+ CHECK_FLAG(p->cap, PEER_CAP_ENHANCED_RR_RCV)) {
+ vty_out(vty, " Enhanced Route Refresh:");
+ if (CHECK_FLAG(p->cap,
+ PEER_CAP_ENHANCED_RR_ADV))
+ vty_out(vty, " advertised");
+ if (CHECK_FLAG(p->cap,
+ PEER_CAP_ENHANCED_RR_RCV))
+ vty_out(vty, " %sreceived",
+ CHECK_FLAG(p->cap,
+ PEER_CAP_REFRESH_ADV)
+ ? "and "
+ : "");
+ vty_out(vty, "\n");
+ }
+
+ /* Multiprotocol Extensions */
+ FOREACH_AFI_SAFI (afi, safi)
+ if (p->afc_adv[afi][safi] ||
+ p->afc_recv[afi][safi]) {
+ vty_out(vty, " Address Family %s:",
+ get_afi_safi_str(afi, safi,
+ false));
+ if (p->afc_adv[afi][safi])
+ vty_out(vty, " advertised");
+ if (p->afc_recv[afi][safi])
+ vty_out(vty, " %sreceived",
+ p->afc_adv[afi][safi]
+ ? "and "
+ : "");
+ vty_out(vty, "\n");
+ }
+
+ /* Hostname capability */
+ vty_out(vty, " Hostname Capability:");
+
+ if (CHECK_FLAG(p->cap, PEER_CAP_HOSTNAME_ADV)) {
+ vty_out(vty,
+ " advertised (name: %s,domain name: %s)",
+ bgp->peer_self->hostname
+ ? bgp->peer_self->hostname
+ : "n/a",
+ bgp->peer_self->domainname
+ ? bgp->peer_self->domainname
+ : "n/a");
+ } else {
+ vty_out(vty, " not advertised");
+ }
+
+ if (CHECK_FLAG(p->cap, PEER_CAP_HOSTNAME_RCV)) {
+ vty_out(vty,
+ " received (name: %s,domain name: %s)",
+ p->hostname ? p->hostname : "n/a",
+ p->domainname ? p->domainname : "n/a");
+ } else {
+ vty_out(vty, " not received");
+ }
+
+ vty_out(vty, "\n");
+
+ /* Graceful Restart */
+ if (CHECK_FLAG(p->cap, PEER_CAP_RESTART_RCV) ||
+ CHECK_FLAG(p->cap, PEER_CAP_RESTART_ADV)) {
+ vty_out(vty,
+ " Graceful Restart Capability:");
+ if (CHECK_FLAG(p->cap, PEER_CAP_RESTART_ADV))
+ vty_out(vty, " advertised");
+ if (CHECK_FLAG(p->cap, PEER_CAP_RESTART_RCV))
+ vty_out(vty, " %sreceived",
+ CHECK_FLAG(p->cap,
+ PEER_CAP_RESTART_ADV)
+ ? "and "
+ : "");
+ vty_out(vty, "\n");
+
+ if (CHECK_FLAG(p->cap, PEER_CAP_RESTART_RCV)) {
+ int restart_af_count = 0;
+
+ vty_out(vty,
+ " Remote Restart timer is %d seconds\n",
+ p->v_gr_restart);
+ vty_out(vty,
+ " Address families by peer:\n ");
+
+ FOREACH_AFI_SAFI (afi, safi)
+ if (CHECK_FLAG(
+ p->af_cap[afi]
+ [safi],
+ PEER_CAP_RESTART_AF_RCV)) {
+ vty_out(vty, "%s%s(%s)",
+ restart_af_count
+ ? ", "
+ : "",
+ get_afi_safi_str(
+ afi,
+ safi,
+ false),
+ CHECK_FLAG(
+ p->af_cap
+ [afi]
+ [safi],
+ PEER_CAP_RESTART_AF_PRESERVE_RCV)
+ ? "preserved"
+ : "not preserved");
+ restart_af_count++;
+ }
+ if (!restart_af_count)
+ vty_out(vty, "none");
+ vty_out(vty, "\n");
+ }
+ } /* Graceful Restart */
+ }
+ }
+
+ /* graceful restart information */
+ json_object *json_grace = NULL;
+ json_object *json_grace_send = NULL;
+ json_object *json_grace_recv = NULL;
+ int eor_send_af_count = 0;
+ int eor_receive_af_count = 0;
+
+ if (use_json) {
+ json_grace = json_object_new_object();
+ json_grace_send = json_object_new_object();
+ json_grace_recv = json_object_new_object();
+
+ if ((peer_established(p)) &&
+ CHECK_FLAG(p->cap, PEER_CAP_RESTART_RCV)) {
+ FOREACH_AFI_SAFI (afi, safi) {
+ if (CHECK_FLAG(p->af_sflags[afi][safi],
+ PEER_STATUS_EOR_SEND)) {
+ json_object_boolean_true_add(
+ json_grace_send,
+ get_afi_safi_str(afi, safi,
+ true));
+ eor_send_af_count++;
+ }
+ }
+ FOREACH_AFI_SAFI (afi, safi) {
+ if (CHECK_FLAG(p->af_sflags[afi][safi],
+ PEER_STATUS_EOR_RECEIVED)) {
+ json_object_boolean_true_add(
+ json_grace_recv,
+ get_afi_safi_str(afi, safi,
+ true));
+ eor_receive_af_count++;
+ }
+ }
+ }
+ json_object_object_add(json_grace, "endOfRibSend",
+ json_grace_send);
+ json_object_object_add(json_grace, "endOfRibRecv",
+ json_grace_recv);
+
+
+ if (p->t_gr_restart)
+ json_object_int_add(
+ json_grace, "gracefulRestartTimerMsecs",
+ thread_timer_remain_second(p->t_gr_restart) *
+ 1000);
+
+ if (p->t_gr_stale)
+ json_object_int_add(
+ json_grace, "gracefulStalepathTimerMsecs",
+ thread_timer_remain_second(p->t_gr_stale) *
+ 1000);
+ /* more gr info in new format */
+ BGP_SHOW_PEER_GR_CAPABILITY(vty, p, use_json, json_grace);
+ json_object_object_add(json_neigh, "gracefulRestartInfo",
+ json_grace);
+ } else {
+ vty_out(vty, " Graceful restart information:\n");
+ if ((peer_established(p)) &&
+ CHECK_FLAG(p->cap, PEER_CAP_RESTART_RCV)) {
+
+ vty_out(vty, " End-of-RIB send: ");
+ FOREACH_AFI_SAFI (afi, safi) {
+ if (CHECK_FLAG(p->af_sflags[afi][safi],
+ PEER_STATUS_EOR_SEND)) {
+ vty_out(vty, "%s%s",
+ eor_send_af_count ? ", " : "",
+ get_afi_safi_str(afi, safi,
+ false));
+ eor_send_af_count++;
+ }
+ }
+ vty_out(vty, "\n");
+ vty_out(vty, " End-of-RIB received: ");
+ FOREACH_AFI_SAFI (afi, safi) {
+ if (CHECK_FLAG(p->af_sflags[afi][safi],
+ PEER_STATUS_EOR_RECEIVED)) {
+ vty_out(vty, "%s%s",
+ eor_receive_af_count ? ", "
+ : "",
+ get_afi_safi_str(afi, safi,
+ false));
+ eor_receive_af_count++;
+ }
+ }
+ vty_out(vty, "\n");
+ }
+
+ if (p->t_gr_restart)
+ vty_out(vty,
+ " The remaining time of restart timer is %ld\n",
+ thread_timer_remain_second(p->t_gr_restart));
+
+ if (p->t_gr_stale)
+ vty_out(vty,
+ " The remaining time of stalepath timer is %ld\n",
+ thread_timer_remain_second(p->t_gr_stale));
+
+ /* more gr info in new format */
+ BGP_SHOW_PEER_GR_CAPABILITY(vty, p, use_json, NULL);
+ }
+
+ if (use_json) {
+ json_object *json_stat = NULL;
+ json_stat = json_object_new_object();
+ /* Packet counts. */
+
+ atomic_size_t outq_count, inq_count;
+ outq_count = atomic_load_explicit(&p->obuf->count,
+ memory_order_relaxed);
+ inq_count = atomic_load_explicit(&p->ibuf->count,
+ memory_order_relaxed);
+
+ json_object_int_add(json_stat, "depthInq",
+ (unsigned long)inq_count);
+ json_object_int_add(json_stat, "depthOutq",
+ (unsigned long)outq_count);
+ json_object_int_add(json_stat, "opensSent",
+ atomic_load_explicit(&p->open_out,
+ memory_order_relaxed));
+ json_object_int_add(json_stat, "opensRecv",
+ atomic_load_explicit(&p->open_in,
+ memory_order_relaxed));
+ json_object_int_add(json_stat, "notificationsSent",
+ atomic_load_explicit(&p->notify_out,
+ memory_order_relaxed));
+ json_object_int_add(json_stat, "notificationsRecv",
+ atomic_load_explicit(&p->notify_in,
+ memory_order_relaxed));
+ json_object_int_add(json_stat, "updatesSent",
+ atomic_load_explicit(&p->update_out,
+ memory_order_relaxed));
+ json_object_int_add(json_stat, "updatesRecv",
+ atomic_load_explicit(&p->update_in,
+ memory_order_relaxed));
+ json_object_int_add(json_stat, "keepalivesSent",
+ atomic_load_explicit(&p->keepalive_out,
+ memory_order_relaxed));
+ json_object_int_add(json_stat, "keepalivesRecv",
+ atomic_load_explicit(&p->keepalive_in,
+ memory_order_relaxed));
+ json_object_int_add(json_stat, "routeRefreshSent",
+ atomic_load_explicit(&p->refresh_out,
+ memory_order_relaxed));
+ json_object_int_add(json_stat, "routeRefreshRecv",
+ atomic_load_explicit(&p->refresh_in,
+ memory_order_relaxed));
+ json_object_int_add(json_stat, "capabilitySent",
+ atomic_load_explicit(&p->dynamic_cap_out,
+ memory_order_relaxed));
+ json_object_int_add(json_stat, "capabilityRecv",
+ atomic_load_explicit(&p->dynamic_cap_in,
+ memory_order_relaxed));
+ json_object_int_add(json_stat, "totalSent", PEER_TOTAL_TX(p));
+ json_object_int_add(json_stat, "totalRecv", PEER_TOTAL_RX(p));
+ json_object_object_add(json_neigh, "messageStats", json_stat);
+ } else {
+ atomic_size_t outq_count, inq_count, open_out, open_in,
+ notify_out, notify_in, update_out, update_in,
+ keepalive_out, keepalive_in, refresh_out, refresh_in,
+ dynamic_cap_out, dynamic_cap_in;
+ outq_count = atomic_load_explicit(&p->obuf->count,
+ memory_order_relaxed);
+ inq_count = atomic_load_explicit(&p->ibuf->count,
+ memory_order_relaxed);
+ open_out = atomic_load_explicit(&p->open_out,
+ memory_order_relaxed);
+ open_in =
+ atomic_load_explicit(&p->open_in, memory_order_relaxed);
+ notify_out = atomic_load_explicit(&p->notify_out,
+ memory_order_relaxed);
+ notify_in = atomic_load_explicit(&p->notify_in,
+ memory_order_relaxed);
+ update_out = atomic_load_explicit(&p->update_out,
+ memory_order_relaxed);
+ update_in = atomic_load_explicit(&p->update_in,
+ memory_order_relaxed);
+ keepalive_out = atomic_load_explicit(&p->keepalive_out,
+ memory_order_relaxed);
+ keepalive_in = atomic_load_explicit(&p->keepalive_in,
+ memory_order_relaxed);
+ refresh_out = atomic_load_explicit(&p->refresh_out,
+ memory_order_relaxed);
+ refresh_in = atomic_load_explicit(&p->refresh_in,
+ memory_order_relaxed);
+ dynamic_cap_out = atomic_load_explicit(&p->dynamic_cap_out,
+ memory_order_relaxed);
+ dynamic_cap_in = atomic_load_explicit(&p->dynamic_cap_in,
+ memory_order_relaxed);
+
+ /* Packet counts. */
+ vty_out(vty, " Message statistics:\n");
+ vty_out(vty, " Inq depth is %zu\n", inq_count);
+ vty_out(vty, " Outq depth is %zu\n", outq_count);
+ vty_out(vty, " Sent Rcvd\n");
+ vty_out(vty, " Opens: %10zu %10zu\n", open_out,
+ open_in);
+ vty_out(vty, " Notifications: %10zu %10zu\n", notify_out,
+ notify_in);
+ vty_out(vty, " Updates: %10zu %10zu\n", update_out,
+ update_in);
+ vty_out(vty, " Keepalives: %10zu %10zu\n", keepalive_out,
+ keepalive_in);
+ vty_out(vty, " Route Refresh: %10zu %10zu\n", refresh_out,
+ refresh_in);
+ vty_out(vty, " Capability: %10zu %10zu\n",
+ dynamic_cap_out, dynamic_cap_in);
+ vty_out(vty, " Total: %10u %10u\n",
+ (uint32_t)PEER_TOTAL_TX(p), (uint32_t)PEER_TOTAL_RX(p));
+ }
+
+ if (use_json) {
+ /* advertisement-interval */
+ json_object_int_add(json_neigh,
+ "minBtwnAdvertisementRunsTimerMsecs",
+ p->v_routeadv * 1000);
+
+ /* Update-source. */
+ if (p->update_if || p->update_source) {
+ if (p->update_if)
+ json_object_string_add(json_neigh,
+ "updateSource",
+ p->update_if);
+ else if (p->update_source)
+ json_object_string_addf(json_neigh,
+ "updateSource", "%pSU",
+ p->update_source);
+ }
+ } else {
+ /* advertisement-interval */
+ vty_out(vty,
+ " Minimum time between advertisement runs is %d seconds\n",
+ p->v_routeadv);
+
+ /* Update-source. */
+ if (p->update_if || p->update_source) {
+ vty_out(vty, " Update source is ");
+ if (p->update_if)
+ vty_out(vty, "%s", p->update_if);
+ else if (p->update_source)
+ vty_out(vty, "%pSU", p->update_source);
+ vty_out(vty, "\n");
+ }
+
+ vty_out(vty, "\n");
+ }
+
+ /* Address Family Information */
+ json_object *json_hold = NULL;
+
+ if (use_json)
+ json_hold = json_object_new_object();
+
+ FOREACH_AFI_SAFI (afi, safi)
+ if (p->afc[afi][safi])
+ bgp_show_peer_afi(vty, p, afi, safi, use_json,
+ json_hold);
+
+ if (use_json) {
+ json_object_object_add(json_neigh, "addressFamilyInfo",
+ json_hold);
+ json_object_int_add(json_neigh, "connectionsEstablished",
+ p->established);
+ json_object_int_add(json_neigh, "connectionsDropped",
+ p->dropped);
+ } else
+ vty_out(vty, " Connections established %d; dropped %d\n",
+ p->established, p->dropped);
+
+ if (!p->last_reset) {
+ if (use_json)
+ json_object_string_add(json_neigh, "lastReset",
+ "never");
+ else
+ vty_out(vty, " Last reset never\n");
+ } else {
+ if (use_json) {
+ time_t uptime;
+ struct tm tm;
+
+ uptime = monotime(NULL);
+ uptime -= p->resettime;
+ gmtime_r(&uptime, &tm);
+
+ json_object_int_add(json_neigh, "lastResetTimerMsecs",
+ (tm.tm_sec * 1000)
+ + (tm.tm_min * 60000)
+ + (tm.tm_hour * 3600000));
+ bgp_show_peer_reset(NULL, p, json_neigh, true);
+ } else {
+ vty_out(vty, " Last reset %s, ",
+ peer_uptime(p->resettime, timebuf,
+ BGP_UPTIME_LEN, 0, NULL));
+
+ bgp_show_peer_reset(vty, p, NULL, false);
+ if (p->last_reset_cause_size) {
+ msg = p->last_reset_cause;
+ vty_out(vty,
+ " Message received that caused BGP to send a NOTIFICATION:\n ");
+ for (i = 1; i <= p->last_reset_cause_size;
+ i++) {
+ vty_out(vty, "%02X", *msg++);
+
+ if (i != p->last_reset_cause_size) {
+ if (i % 16 == 0) {
+ vty_out(vty, "\n ");
+ } else if (i % 4 == 0) {
+ vty_out(vty, " ");
+ }
+ }
+ }
+ vty_out(vty, "\n");
+ }
+ }
+ }
+
+ if (CHECK_FLAG(p->sflags, PEER_STATUS_PREFIX_OVERFLOW)) {
+ if (use_json)
+ json_object_boolean_true_add(json_neigh,
+ "prefixesConfigExceedMax");
+ else
+ vty_out(vty,
+ " Peer had exceeded the max. no. of prefixes configured.\n");
+
+ if (p->t_pmax_restart) {
+ if (use_json) {
+ json_object_boolean_true_add(
+ json_neigh, "reducePrefixNumFrom");
+ json_object_int_add(json_neigh,
+ "restartInTimerMsec",
+ thread_timer_remain_second(
+ p->t_pmax_restart)
+ * 1000);
+ } else
+ vty_out(vty,
+ " Reduce the no. of prefix from %s, will restart in %ld seconds\n",
+ p->host, thread_timer_remain_second(
+ p->t_pmax_restart));
+ } else {
+ if (use_json)
+ json_object_boolean_true_add(
+ json_neigh,
+ "reducePrefixNumAndClearIpBgp");
+ else
+ vty_out(vty,
+ " Reduce the no. of prefix and clear ip bgp %s to restore peering\n",
+ p->host);
+ }
+ }
+
+ /* EBGP Multihop and GTSM */
+ if (p->sort != BGP_PEER_IBGP) {
+ if (use_json) {
+ if (p->gtsm_hops > BGP_GTSM_HOPS_DISABLED)
+ json_object_int_add(json_neigh,
+ "externalBgpNbrMaxHopsAway",
+ p->gtsm_hops);
+ else
+ json_object_int_add(json_neigh,
+ "externalBgpNbrMaxHopsAway",
+ p->ttl);
+ } else {
+ if (p->gtsm_hops > BGP_GTSM_HOPS_DISABLED)
+ vty_out(vty,
+ " External BGP neighbor may be up to %d hops away.\n",
+ p->gtsm_hops);
+ else
+ vty_out(vty,
+ " External BGP neighbor may be up to %d hops away.\n",
+ p->ttl);
+ }
+ } else {
+ if (use_json) {
+ if (p->gtsm_hops > BGP_GTSM_HOPS_DISABLED)
+ json_object_int_add(json_neigh,
+ "internalBgpNbrMaxHopsAway",
+ p->gtsm_hops);
+ else
+ json_object_int_add(json_neigh,
+ "internalBgpNbrMaxHopsAway",
+ p->ttl);
+ } else {
+ if (p->gtsm_hops > BGP_GTSM_HOPS_DISABLED)
+ vty_out(vty,
+ " Internal BGP neighbor may be up to %d hops away.\n",
+ p->gtsm_hops);
+ else
+ vty_out(vty,
+ " Internal BGP neighbor may be up to %d hops away.\n",
+ p->ttl);
+ }
+ }
+
+ /* Local address. */
+ if (p->su_local) {
+ if (use_json) {
+ json_object_string_addf(json_neigh, "hostLocal", "%pSU",
+ p->su_local);
+ json_object_int_add(json_neigh, "portLocal",
+ ntohs(p->su_local->sin.sin_port));
+ } else
+ vty_out(vty, "Local host: %pSU, Local port: %d\n",
+ p->su_local, ntohs(p->su_local->sin.sin_port));
+ } else {
+ if (use_json) {
+ json_object_string_add(json_neigh, "hostLocal",
+ "Unknown");
+ json_object_int_add(json_neigh, "portLocal", -1);
+ }
+ }
+
+ /* Remote address. */
+ if (p->su_remote) {
+ if (use_json) {
+ json_object_string_addf(json_neigh, "hostForeign",
+ "%pSU", p->su_remote);
+ json_object_int_add(json_neigh, "portForeign",
+ ntohs(p->su_remote->sin.sin_port));
+ } else
+ vty_out(vty, "Foreign host: %pSU, Foreign port: %d\n",
+ p->su_remote,
+ ntohs(p->su_remote->sin.sin_port));
+ } else {
+ if (use_json) {
+ json_object_string_add(json_neigh, "hostForeign",
+ "Unknown");
+ json_object_int_add(json_neigh, "portForeign", -1);
+ }
+ }
+
+ /* Nexthop display. */
+ if (p->su_local) {
+ if (use_json) {
+ json_object_string_addf(json_neigh, "nexthop", "%pI4",
+ &p->nexthop.v4);
+ json_object_string_addf(json_neigh, "nexthopGlobal",
+ "%pI6", &p->nexthop.v6_global);
+ json_object_string_addf(json_neigh, "nexthopLocal",
+ "%pI6", &p->nexthop.v6_local);
+ if (p->shared_network)
+ json_object_string_add(json_neigh,
+ "bgpConnection",
+ "sharedNetwork");
+ else
+ json_object_string_add(json_neigh,
+ "bgpConnection",
+ "nonSharedNetwork");
+ } else {
+ vty_out(vty, "Nexthop: %s\n",
+ inet_ntop(AF_INET, &p->nexthop.v4, buf1,
+ sizeof(buf1)));
+ vty_out(vty, "Nexthop global: %s\n",
+ inet_ntop(AF_INET6, &p->nexthop.v6_global, buf1,
+ sizeof(buf1)));
+ vty_out(vty, "Nexthop local: %s\n",
+ inet_ntop(AF_INET6, &p->nexthop.v6_local, buf1,
+ sizeof(buf1)));
+ vty_out(vty, "BGP connection: %s\n",
+ p->shared_network ? "shared network"
+ : "non shared network");
+ }
+ }
+
+ /* Timer information. */
+ if (use_json) {
+ json_object_int_add(json_neigh, "connectRetryTimer",
+ p->v_connect);
+ if (peer_established(p) && p->rtt)
+ json_object_int_add(json_neigh, "estimatedRttInMsecs",
+ p->rtt);
+ if (p->t_start)
+ json_object_int_add(
+ json_neigh, "nextStartTimerDueInMsecs",
+ thread_timer_remain_second(p->t_start) * 1000);
+ if (p->t_connect)
+ json_object_int_add(
+ json_neigh, "nextConnectTimerDueInMsecs",
+ thread_timer_remain_second(p->t_connect)
+ * 1000);
+ if (p->t_routeadv) {
+ json_object_int_add(json_neigh, "mraiInterval",
+ p->v_routeadv);
+ json_object_int_add(
+ json_neigh, "mraiTimerExpireInMsecs",
+ thread_timer_remain_second(p->t_routeadv)
+ * 1000);
+ }
+ if (p->password)
+ json_object_int_add(json_neigh, "authenticationEnabled",
+ 1);
+
+ if (p->t_read)
+ json_object_string_add(json_neigh, "readThread", "on");
+ else
+ json_object_string_add(json_neigh, "readThread", "off");
+
+ if (CHECK_FLAG(p->thread_flags, PEER_THREAD_WRITES_ON))
+ json_object_string_add(json_neigh, "writeThread", "on");
+ else
+ json_object_string_add(json_neigh, "writeThread",
+ "off");
+ } else {
+ vty_out(vty, "BGP Connect Retry Timer in Seconds: %d\n",
+ p->v_connect);
+ if (peer_established(p) && p->rtt)
+ vty_out(vty, "Estimated round trip time: %d ms\n",
+ p->rtt);
+ if (p->t_start)
+ vty_out(vty, "Next start timer due in %ld seconds\n",
+ thread_timer_remain_second(p->t_start));
+ if (p->t_connect)
+ vty_out(vty, "Next connect timer due in %ld seconds\n",
+ thread_timer_remain_second(p->t_connect));
+ if (p->t_routeadv)
+ vty_out(vty,
+ "MRAI (interval %u) timer expires in %ld seconds\n",
+ p->v_routeadv,
+ thread_timer_remain_second(p->t_routeadv));
+ if (p->password)
+ vty_out(vty, "Peer Authentication Enabled\n");
+
+ vty_out(vty, "Read thread: %s Write thread: %s FD used: %d\n",
+ p->t_read ? "on" : "off",
+ CHECK_FLAG(p->thread_flags, PEER_THREAD_WRITES_ON)
+ ? "on"
+ : "off", p->fd);
+ }
+
+ if (p->notify.code == BGP_NOTIFY_OPEN_ERR
+ && p->notify.subcode == BGP_NOTIFY_OPEN_UNSUP_CAPBL)
+ bgp_capability_vty_out(vty, p, use_json, json_neigh);
+
+ if (!use_json)
+ vty_out(vty, "\n");
+
+ /* BFD information. */
+ if (p->bfd_config)
+ bgp_bfd_show_info(vty, p, json_neigh);
+
+ if (use_json) {
+ if (p->conf_if) /* Configured interface name. */
+ json_object_object_add(json, p->conf_if, json_neigh);
+ else /* Configured IP address. */
+ json_object_object_add(json, p->host, json_neigh);
+ }
+}
+
+static int bgp_show_neighbor_graceful_restart(struct vty *vty, struct bgp *bgp,
+ enum show_type type,
+ union sockunion *su,
+ const char *conf_if, afi_t afi,
+ bool use_json)
+{
+ struct listnode *node, *nnode;
+ struct peer *peer;
+ int find = 0;
+ safi_t safi = SAFI_UNICAST;
+ json_object *json = NULL;
+ json_object *json_neighbor = NULL;
+
+ if (use_json) {
+ json = json_object_new_object();
+ json_neighbor = json_object_new_object();
+ }
+
+ for (ALL_LIST_ELEMENTS(bgp->peer, node, nnode, peer)) {
+
+ if (!CHECK_FLAG(peer->flags, PEER_FLAG_CONFIG_NODE))
+ continue;
+
+ if ((peer->afc[afi][safi]) == 0)
+ continue;
+
+ if (type == show_all) {
+ bgp_show_peer_gr_status(vty, peer, use_json,
+ json_neighbor);
+
+ if (use_json) {
+ json_object_object_add(json, peer->host,
+ json_neighbor);
+ json_neighbor = NULL;
+ }
+
+ } else if (type == show_peer) {
+ if (conf_if) {
+ if ((peer->conf_if
+ && !strcmp(peer->conf_if, conf_if))
+ || (peer->hostname
+ && !strcmp(peer->hostname, conf_if))) {
+ find = 1;
+ bgp_show_peer_gr_status(vty, peer,
+ use_json,
+ json_neighbor);
+ }
+ } else {
+ if (sockunion_same(&peer->su, su)) {
+ find = 1;
+ bgp_show_peer_gr_status(vty, peer,
+ use_json,
+ json_neighbor);
+ }
+ }
+ if (use_json && find)
+ json_object_object_add(json, peer->host,
+ json_neighbor);
+ }
+
+ if (find) {
+ json_neighbor = NULL;
+ break;
+ }
+ }
+
+ if (type == show_peer && !find) {
+ if (use_json)
+ json_object_boolean_true_add(json, "bgpNoSuchNeighbor");
+ else
+ vty_out(vty, "%% No such neighbor\n");
+ }
+ if (use_json) {
+ if (json_neighbor)
+ json_object_free(json_neighbor);
+ vty_json(vty, json);
+ } else {
+ vty_out(vty, "\n");
+ }
+
+ return CMD_SUCCESS;
+}
+
+static int bgp_show_neighbor(struct vty *vty, struct bgp *bgp,
+ enum show_type type, union sockunion *su,
+ const char *conf_if, bool use_json,
+ json_object *json)
+{
+ struct listnode *node, *nnode;
+ struct peer *peer;
+ int find = 0;
+ bool nbr_output = false;
+ afi_t afi = AFI_MAX;
+ safi_t safi = SAFI_MAX;
+
+ if (type == show_ipv4_peer || type == show_ipv4_all) {
+ afi = AFI_IP;
+ } else if (type == show_ipv6_peer || type == show_ipv6_all) {
+ afi = AFI_IP6;
+ }
+
+ for (ALL_LIST_ELEMENTS(bgp->peer, node, nnode, peer)) {
+ if (!CHECK_FLAG(peer->flags, PEER_FLAG_CONFIG_NODE))
+ continue;
+
+ switch (type) {
+ case show_all:
+ bgp_show_peer(vty, peer, use_json, json);
+ nbr_output = true;
+ break;
+ case show_peer:
+ if (conf_if) {
+ if ((peer->conf_if
+ && !strcmp(peer->conf_if, conf_if))
+ || (peer->hostname
+ && !strcmp(peer->hostname, conf_if))) {
+ find = 1;
+ bgp_show_peer(vty, peer, use_json,
+ json);
+ }
+ } else {
+ if (sockunion_same(&peer->su, su)) {
+ find = 1;
+ bgp_show_peer(vty, peer, use_json,
+ json);
+ }
+ }
+ break;
+ case show_ipv4_peer:
+ case show_ipv6_peer:
+ FOREACH_SAFI (safi) {
+ if (peer->afc[afi][safi]) {
+ if (conf_if) {
+ if ((peer->conf_if
+ && !strcmp(peer->conf_if, conf_if))
+ || (peer->hostname
+ && !strcmp(peer->hostname, conf_if))) {
+ find = 1;
+ bgp_show_peer(vty, peer, use_json,
+ json);
+ break;
+ }
+ } else {
+ if (sockunion_same(&peer->su, su)) {
+ find = 1;
+ bgp_show_peer(vty, peer, use_json,
+ json);
+ break;
+ }
+ }
+ }
+ }
+ break;
+ case show_ipv4_all:
+ case show_ipv6_all:
+ FOREACH_SAFI (safi) {
+ if (peer->afc[afi][safi]) {
+ bgp_show_peer(vty, peer, use_json, json);
+ nbr_output = true;
+ break;
+ }
+ }
+ break;
+ }
+ }
+
+ if ((type == show_peer || type == show_ipv4_peer ||
+ type == show_ipv6_peer) && !find) {
+ if (use_json)
+ json_object_boolean_true_add(json, "bgpNoSuchNeighbor");
+ else
+ vty_out(vty, "%% No such neighbor in this view/vrf\n");
+ }
+
+ if (type != show_peer && type != show_ipv4_peer &&
+ type != show_ipv6_peer && !nbr_output && !use_json)
+ vty_out(vty, "%% No BGP neighbors found\n");
+
+ if (use_json) {
+ vty_out(vty, "%s\n", json_object_to_json_string_ext(
+ json, JSON_C_TO_STRING_PRETTY));
+ } else {
+ vty_out(vty, "\n");
+ }
+
+ return CMD_SUCCESS;
+}
+
+static void bgp_show_neighbor_graceful_restart_vty(struct vty *vty,
+ enum show_type type,
+ const char *ip_str,
+ afi_t afi, bool use_json)
+{
+
+ int ret;
+ struct bgp *bgp;
+ union sockunion su;
+
+ bgp = bgp_get_default();
+
+ if (!bgp)
+ return;
+
+ if (!use_json)
+ bgp_show_global_graceful_restart_mode_vty(vty, bgp, use_json,
+ NULL);
+
+ if (ip_str) {
+ ret = str2sockunion(ip_str, &su);
+ if (ret < 0)
+ bgp_show_neighbor_graceful_restart(
+ vty, bgp, type, NULL, ip_str, afi, use_json);
+ else
+ bgp_show_neighbor_graceful_restart(vty, bgp, type, &su,
+ NULL, afi, use_json);
+ } else
+ bgp_show_neighbor_graceful_restart(vty, bgp, type, NULL, NULL,
+ afi, use_json);
+}
+
+static void bgp_show_all_instances_neighbors_vty(struct vty *vty,
+ enum show_type type,
+ const char *ip_str,
+ bool use_json)
+{
+ struct listnode *node, *nnode;
+ struct bgp *bgp;
+ union sockunion su;
+ json_object *json = NULL;
+ int ret, is_first = 1;
+ bool nbr_output = false;
+
+ if (use_json)
+ vty_out(vty, "{\n");
+
+ for (ALL_LIST_ELEMENTS(bm->bgp, node, nnode, bgp)) {
+ nbr_output = true;
+ if (use_json) {
+ if (!(json = json_object_new_object())) {
+ flog_err(
+ EC_BGP_JSON_MEM_ERROR,
+ "Unable to allocate memory for JSON object");
+ vty_out(vty,
+ "{\"error\": {\"message:\": \"Unable to allocate memory for JSON object\"}}}\n");
+ return;
+ }
+
+ json_object_int_add(json, "vrfId",
+ (bgp->vrf_id == VRF_UNKNOWN)
+ ? -1
+ : (int64_t)bgp->vrf_id);
+ json_object_string_add(
+ json, "vrfName",
+ (bgp->inst_type == BGP_INSTANCE_TYPE_DEFAULT)
+ ? VRF_DEFAULT_NAME
+ : bgp->name);
+
+ 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);
+ }
+
+ if (type == show_peer || type == show_ipv4_peer ||
+ type == show_ipv6_peer) {
+ ret = str2sockunion(ip_str, &su);
+ if (ret < 0)
+ bgp_show_neighbor(vty, bgp, type, NULL, ip_str,
+ use_json, json);
+ else
+ bgp_show_neighbor(vty, bgp, type, &su, NULL,
+ use_json, json);
+ } else {
+ bgp_show_neighbor(vty, bgp, type, NULL, NULL,
+ use_json, json);
+ }
+ json_object_free(json);
+ json = NULL;
+ }
+
+ if (use_json)
+ vty_out(vty, "}\n");
+ else if (!nbr_output)
+ vty_out(vty, "%% BGP instance not found\n");
+}
+
+static int bgp_show_neighbor_vty(struct vty *vty, const char *name,
+ enum show_type type, const char *ip_str,
+ bool use_json)
+{
+ int ret;
+ struct bgp *bgp;
+ union sockunion su;
+ json_object *json = NULL;
+
+ if (name) {
+ if (strmatch(name, "all")) {
+ bgp_show_all_instances_neighbors_vty(vty, type, ip_str,
+ use_json);
+ return CMD_SUCCESS;
+ } else {
+ bgp = bgp_lookup_by_name(name);
+ if (!bgp) {
+ if (use_json) {
+ json = json_object_new_object();
+ vty_json(vty, json);
+ } else
+ vty_out(vty,
+ "%% BGP instance not found\n");
+
+ return CMD_WARNING;
+ }
+ }
+ } else {
+ bgp = bgp_get_default();
+ }
+
+ if (bgp) {
+ json = json_object_new_object();
+ if (ip_str) {
+ ret = str2sockunion(ip_str, &su);
+ if (ret < 0)
+ bgp_show_neighbor(vty, bgp, type, NULL, ip_str,
+ use_json, json);
+ else
+ bgp_show_neighbor(vty, bgp, type, &su, NULL,
+ use_json, json);
+ } else {
+ bgp_show_neighbor(vty, bgp, type, NULL, NULL, use_json,
+ json);
+ }
+ json_object_free(json);
+ } else {
+ if (use_json)
+ vty_out(vty, "{}\n");
+ else
+ vty_out(vty, "%% BGP instance not found\n");
+ }
+
+ return CMD_SUCCESS;
+}
+
+
+
+/* "show [ip] bgp neighbors graceful-restart" commands. */
+DEFUN (show_ip_bgp_neighbors_graceful_restart,
+ show_ip_bgp_neighbors_graceful_restart_cmd,
+ "show bgp [<ipv4|ipv6>] neighbors [<A.B.C.D|X:X::X:X|WORD>] graceful-restart [json]",
+ SHOW_STR
+ BGP_STR
+ IP_STR
+ IPV6_STR
+ NEIGHBOR_STR
+ "Neighbor to display information about\n"
+ "Neighbor to display information about\n"
+ "Neighbor on BGP configured interface\n"
+ GR_SHOW
+ JSON_STR)
+{
+ char *sh_arg = NULL;
+ enum show_type sh_type;
+ int idx = 0;
+ afi_t afi = AFI_MAX;
+ bool uj = use_json(argc, argv);
+
+ if (!argv_find_and_parse_afi(argv, argc, &idx, &afi))
+ afi = AFI_MAX;
+
+ idx++;
+
+ if (argv_find(argv, argc, "A.B.C.D", &idx)
+ || argv_find(argv, argc, "X:X::X:X", &idx)
+ || argv_find(argv, argc, "WORD", &idx)) {
+ sh_type = show_peer;
+ sh_arg = argv[idx]->arg;
+ } else
+ sh_type = show_all;
+
+ if (!argv_find(argv, argc, "graceful-restart", &idx))
+ return CMD_SUCCESS;
+
+
+ return bgp_show_neighbor_graceful_restart_afi_all(vty, sh_type, sh_arg,
+ afi, uj);
+}
+
+/* "show [ip] bgp neighbors" commands. */
+DEFUN (show_ip_bgp_neighbors,
+ show_ip_bgp_neighbors_cmd,
+ "show [ip] bgp [<view|vrf> VIEWVRFNAME] [<ipv4|ipv6>] neighbors [<A.B.C.D|X:X::X:X|WORD>] [json]",
+ SHOW_STR
+ IP_STR
+ BGP_STR
+ BGP_INSTANCE_HELP_STR
+ BGP_AF_STR
+ BGP_AF_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"
+ JSON_STR)
+{
+ char *vrf = NULL;
+ char *sh_arg = NULL;
+ enum show_type sh_type;
+ afi_t afi = AFI_MAX;
+
+ bool uj = use_json(argc, argv);
+
+ int idx = 0;
+
+ /* [<vrf> VIEWVRFNAME] */
+ if (argv_find(argv, argc, "vrf", &idx)) {
+ vrf = argv[idx + 1]->arg;
+ if (vrf && strmatch(vrf, VRF_DEFAULT_NAME))
+ vrf = NULL;
+ } else if (argv_find(argv, argc, "view", &idx))
+ /* [<view> VIEWVRFNAME] */
+ vrf = argv[idx + 1]->arg;
+
+ idx++;
+
+ if (argv_find(argv, argc, "ipv4", &idx)) {
+ sh_type = show_ipv4_all;
+ afi = AFI_IP;
+ } else if (argv_find(argv, argc, "ipv6", &idx)) {
+ sh_type = show_ipv6_all;
+ afi = AFI_IP6;
+ } else {
+ sh_type = show_all;
+ }
+
+ if (argv_find(argv, argc, "A.B.C.D", &idx)
+ || argv_find(argv, argc, "X:X::X:X", &idx)
+ || argv_find(argv, argc, "WORD", &idx)) {
+ sh_type = show_peer;
+ sh_arg = argv[idx]->arg;
+ }
+
+ if (sh_type == show_peer && afi == AFI_IP) {
+ sh_type = show_ipv4_peer;
+ } else if (sh_type == show_peer && afi == AFI_IP6) {
+ sh_type = show_ipv6_peer;
+ }
+
+ return bgp_show_neighbor_vty(vty, vrf, sh_type, sh_arg, uj);
+}
+
+/* Show BGP's AS paths internal data. There are both `show [ip] bgp
+ paths' and `show ip mbgp paths'. Those functions results are the
+ same.*/
+DEFUN (show_ip_bgp_paths,
+ show_ip_bgp_paths_cmd,
+ "show [ip] bgp ["BGP_SAFI_CMD_STR"] paths",
+ SHOW_STR
+ IP_STR
+ BGP_STR
+ BGP_SAFI_HELP_STR
+ "Path information\n")
+{
+ vty_out(vty, "Address Refcnt Path\n");
+ aspath_print_all_vty(vty);
+ return CMD_SUCCESS;
+}
+
+#include "hash.h"
+
+static void community_show_all_iterator(struct hash_bucket *bucket,
+ struct vty *vty)
+{
+ struct community *com;
+
+ com = (struct community *)bucket->data;
+ vty_out(vty, "[%p] (%ld) %s\n", (void *)com, com->refcnt,
+ community_str(com, false, false));
+}
+
+/* Show BGP's community internal data. */
+DEFUN (show_ip_bgp_community_info,
+ show_ip_bgp_community_info_cmd,
+ "show [ip] bgp community-info",
+ SHOW_STR
+ IP_STR
+ BGP_STR
+ "List all bgp community information\n")
+{
+ vty_out(vty, "Address Refcnt Community\n");
+
+ hash_iterate(community_hash(),
+ (void (*)(struct hash_bucket *,
+ void *))community_show_all_iterator,
+ vty);
+
+ return CMD_SUCCESS;
+}
+
+static void lcommunity_show_all_iterator(struct hash_bucket *bucket,
+ struct vty *vty)
+{
+ struct lcommunity *lcom;
+
+ lcom = (struct lcommunity *)bucket->data;
+ vty_out(vty, "[%p] (%ld) %s\n", (void *)lcom, lcom->refcnt,
+ lcommunity_str(lcom, false, false));
+}
+
+/* Show BGP's community internal data. */
+DEFUN (show_ip_bgp_lcommunity_info,
+ show_ip_bgp_lcommunity_info_cmd,
+ "show ip bgp large-community-info",
+ SHOW_STR
+ IP_STR
+ BGP_STR
+ "List all bgp large-community information\n")
+{
+ vty_out(vty, "Address Refcnt Large-community\n");
+
+ hash_iterate(lcommunity_hash(),
+ (void (*)(struct hash_bucket *,
+ void *))lcommunity_show_all_iterator,
+ vty);
+
+ return CMD_SUCCESS;
+}
+/* Graceful Restart */
+
+static void bgp_show_global_graceful_restart_mode_vty(struct vty *vty,
+ struct bgp *bgp,
+ bool use_json,
+ json_object *json)
+{
+
+
+ vty_out(vty, "\n%s", SHOW_GR_HEADER);
+
+ enum global_mode bgp_global_gr_mode = bgp_global_gr_mode_get(bgp);
+
+ switch (bgp_global_gr_mode) {
+
+ case GLOBAL_HELPER:
+ vty_out(vty, "Global BGP GR Mode : Helper\n");
+ break;
+
+ case GLOBAL_GR:
+ vty_out(vty, "Global BGP GR Mode : Restart\n");
+ break;
+
+ case GLOBAL_DISABLE:
+ vty_out(vty, "Global BGP GR Mode : Disable\n");
+ break;
+
+ case GLOBAL_INVALID:
+ vty_out(vty,
+ "Global BGP GR Mode Invalid\n");
+ break;
+ }
+ vty_out(vty, "\n");
+}
+
+static int bgp_show_neighbor_graceful_restart_afi_all(struct vty *vty,
+ enum show_type type,
+ const char *ip_str,
+ afi_t afi, bool use_json)
+{
+ if ((afi == AFI_MAX) && (ip_str == NULL)) {
+ afi = AFI_IP;
+
+ while ((afi != AFI_L2VPN) && (afi < AFI_MAX)) {
+
+ bgp_show_neighbor_graceful_restart_vty(
+ vty, type, ip_str, afi, use_json);
+ afi++;
+ }
+ } else if (afi != AFI_MAX) {
+ bgp_show_neighbor_graceful_restart_vty(vty, type, ip_str, afi,
+ use_json);
+ } else {
+ return CMD_ERR_INCOMPLETE;
+ }
+
+ return CMD_SUCCESS;
+}
+/* Graceful Restart */
+
+DEFUN (show_ip_bgp_attr_info,
+ show_ip_bgp_attr_info_cmd,
+ "show [ip] bgp attribute-info",
+ SHOW_STR
+ IP_STR
+ BGP_STR
+ "List all bgp attribute information\n")
+{
+ attr_show_all(vty);
+ return CMD_SUCCESS;
+}
+
+static int bgp_show_route_leak_vty(struct vty *vty, const char *name,
+ afi_t afi, safi_t safi,
+ bool use_json, json_object *json)
+{
+ struct bgp *bgp;
+ struct listnode *node;
+ char *vname;
+ char *ecom_str;
+ enum vpn_policy_direction dir;
+
+ if (json) {
+ json_object *json_import_vrfs = NULL;
+ json_object *json_export_vrfs = NULL;
+
+ bgp = name ? bgp_lookup_by_name(name) : bgp_get_default();
+
+ if (!bgp) {
+ vty_json(vty, json);
+
+ return CMD_WARNING;
+ }
+
+ /* Provide context for the block */
+ json_object_string_add(json, "vrf", name ? name : "default");
+ json_object_string_add(json, "afiSafi",
+ get_afi_safi_str(afi, safi, true));
+
+ if (!CHECK_FLAG(bgp->af_flags[afi][safi],
+ BGP_CONFIG_VRF_TO_VRF_IMPORT)) {
+ json_object_string_add(json, "importFromVrfs", "none");
+ json_object_string_add(json, "importRts", "none");
+ } else {
+ json_import_vrfs = json_object_new_array();
+
+ for (ALL_LIST_ELEMENTS_RO(
+ bgp->vpn_policy[afi].import_vrf,
+ node, vname))
+ json_object_array_add(json_import_vrfs,
+ json_object_new_string(vname));
+
+ json_object_object_add(json, "importFromVrfs",
+ json_import_vrfs);
+ dir = BGP_VPN_POLICY_DIR_FROMVPN;
+ if (bgp->vpn_policy[afi].rtlist[dir]) {
+ ecom_str = ecommunity_ecom2str(
+ bgp->vpn_policy[afi].rtlist[dir],
+ ECOMMUNITY_FORMAT_ROUTE_MAP, 0);
+ json_object_string_add(json, "importRts",
+ ecom_str);
+ XFREE(MTYPE_ECOMMUNITY_STR, ecom_str);
+ } else
+ json_object_string_add(json, "importRts",
+ "none");
+ }
+
+ if (!CHECK_FLAG(bgp->af_flags[afi][safi],
+ BGP_CONFIG_VRF_TO_VRF_EXPORT)) {
+ json_object_string_add(json, "exportToVrfs", "none");
+ json_object_string_add(json, "routeDistinguisher",
+ "none");
+ json_object_string_add(json, "exportRts", "none");
+ } else {
+ json_export_vrfs = json_object_new_array();
+
+ for (ALL_LIST_ELEMENTS_RO(
+ bgp->vpn_policy[afi].export_vrf,
+ node, vname))
+ json_object_array_add(json_export_vrfs,
+ json_object_new_string(vname));
+ json_object_object_add(json, "exportToVrfs",
+ json_export_vrfs);
+ json_object_string_addf(json, "routeDistinguisher",
+ "%pRD",
+ &bgp->vpn_policy[afi].tovpn_rd);
+
+ dir = BGP_VPN_POLICY_DIR_TOVPN;
+ if (bgp->vpn_policy[afi].rtlist[dir]) {
+ ecom_str = ecommunity_ecom2str(
+ bgp->vpn_policy[afi].rtlist[dir],
+ ECOMMUNITY_FORMAT_ROUTE_MAP, 0);
+ json_object_string_add(json, "exportRts",
+ ecom_str);
+ XFREE(MTYPE_ECOMMUNITY_STR, ecom_str);
+ } else
+ json_object_string_add(json, "exportRts",
+ "none");
+ }
+
+ if (use_json) {
+ vty_json(vty, json);
+ }
+ } else {
+ bgp = name ? bgp_lookup_by_name(name) : bgp_get_default();
+
+ if (!bgp) {
+ vty_out(vty, "%% No such BGP instance exist\n");
+ return CMD_WARNING;
+ }
+
+ if (!CHECK_FLAG(bgp->af_flags[afi][safi],
+ BGP_CONFIG_VRF_TO_VRF_IMPORT))
+ vty_out(vty,
+ "This VRF is not importing %s routes from any other VRF\n",
+ get_afi_safi_str(afi, safi, false));
+ else {
+ vty_out(vty,
+ "This VRF is importing %s routes from the following VRFs:\n",
+ get_afi_safi_str(afi, safi, false));
+
+ for (ALL_LIST_ELEMENTS_RO(
+ bgp->vpn_policy[afi].import_vrf,
+ node, vname))
+ vty_out(vty, " %s\n", vname);
+
+ dir = BGP_VPN_POLICY_DIR_FROMVPN;
+ ecom_str = NULL;
+ if (bgp->vpn_policy[afi].rtlist[dir]) {
+ ecom_str = ecommunity_ecom2str(
+ bgp->vpn_policy[afi].rtlist[dir],
+ ECOMMUNITY_FORMAT_ROUTE_MAP, 0);
+ vty_out(vty, "Import RT(s): %s\n", ecom_str);
+
+ XFREE(MTYPE_ECOMMUNITY_STR, ecom_str);
+ } else
+ vty_out(vty, "Import RT(s):\n");
+ }
+
+ if (!CHECK_FLAG(bgp->af_flags[afi][safi],
+ BGP_CONFIG_VRF_TO_VRF_EXPORT))
+ vty_out(vty,
+ "This VRF is not exporting %s routes to any other VRF\n",
+ get_afi_safi_str(afi, safi, false));
+ else {
+ vty_out(vty,
+ "This VRF is exporting %s routes to the following VRFs:\n",
+ get_afi_safi_str(afi, safi, false));
+
+ for (ALL_LIST_ELEMENTS_RO(
+ bgp->vpn_policy[afi].export_vrf,
+ node, vname))
+ vty_out(vty, " %s\n", vname);
+
+ vty_out(vty, "RD: %pRD\n",
+ &bgp->vpn_policy[afi].tovpn_rd);
+
+ dir = BGP_VPN_POLICY_DIR_TOVPN;
+ if (bgp->vpn_policy[afi].rtlist[dir]) {
+ ecom_str = ecommunity_ecom2str(
+ bgp->vpn_policy[afi].rtlist[dir],
+ ECOMMUNITY_FORMAT_ROUTE_MAP, 0);
+ vty_out(vty, "Export RT: %s\n", ecom_str);
+ XFREE(MTYPE_ECOMMUNITY_STR, ecom_str);
+ } else
+ vty_out(vty, "Import RT(s):\n");
+ }
+ }
+
+ return CMD_SUCCESS;
+}
+
+static int bgp_show_all_instance_route_leak_vty(struct vty *vty, afi_t afi,
+ safi_t safi, bool use_json)
+{
+ struct listnode *node, *nnode;
+ struct bgp *bgp;
+ char *vrf_name = NULL;
+ json_object *json = NULL;
+ json_object *json_vrf = NULL;
+ json_object *json_vrfs = NULL;
+
+ if (use_json) {
+ json = json_object_new_object();
+ json_vrfs = json_object_new_object();
+ }
+
+ for (ALL_LIST_ELEMENTS(bm->bgp, node, nnode, bgp)) {
+
+ if (bgp->inst_type != BGP_INSTANCE_TYPE_DEFAULT)
+ vrf_name = bgp->name;
+
+ if (use_json) {
+ json_vrf = json_object_new_object();
+ } else {
+ vty_out(vty, "\nInstance %s:\n",
+ (bgp->inst_type == BGP_INSTANCE_TYPE_DEFAULT)
+ ? VRF_DEFAULT_NAME : bgp->name);
+ }
+ bgp_show_route_leak_vty(vty, vrf_name, afi, safi, 0, json_vrf);
+ if (use_json) {
+ if (bgp->inst_type == BGP_INSTANCE_TYPE_DEFAULT)
+ json_object_object_add(json_vrfs,
+ VRF_DEFAULT_NAME, json_vrf);
+ else
+ json_object_object_add(json_vrfs, vrf_name,
+ json_vrf);
+ }
+ }
+
+ if (use_json) {
+ json_object_object_add(json, "vrfs", json_vrfs);
+ vty_json(vty, json);
+ }
+
+ return CMD_SUCCESS;
+}
+
+/* "show [ip] bgp route-leak" command. */
+DEFUN (show_ip_bgp_route_leak,
+ show_ip_bgp_route_leak_cmd,
+ "show [ip] bgp [<view|vrf> VIEWVRFNAME] ["BGP_AFI_CMD_STR" ["BGP_SAFI_CMD_STR"]] route-leak [json]",
+ SHOW_STR
+ IP_STR
+ BGP_STR
+ BGP_INSTANCE_HELP_STR
+ BGP_AFI_HELP_STR
+ BGP_SAFI_HELP_STR
+ "Route leaking information\n"
+ JSON_STR)
+{
+ char *vrf = NULL;
+ afi_t afi = AFI_MAX;
+ safi_t safi = SAFI_MAX;
+
+ bool uj = use_json(argc, argv);
+ int idx = 0;
+ json_object *json = NULL;
+
+ /* show [ip] bgp */
+ if (argv_find(argv, argc, "ip", &idx)) {
+ afi = AFI_IP;
+ safi = SAFI_UNICAST;
+ }
+ /* [vrf VIEWVRFNAME] */
+ if (argv_find(argv, argc, "view", &idx)) {
+ vty_out(vty,
+ "%% This command is not applicable to BGP views\n");
+ return CMD_WARNING;
+ }
+
+ if (argv_find(argv, argc, "vrf", &idx)) {
+ vrf = argv[idx + 1]->arg;
+ if (vrf && strmatch(vrf, VRF_DEFAULT_NAME))
+ vrf = NULL;
+ }
+ /* ["BGP_AFI_CMD_STR" ["BGP_SAFI_CMD_STR"]] */
+ if (argv_find_and_parse_afi(argv, argc, &idx, &afi))
+ argv_find_and_parse_safi(argv, argc, &idx, &safi);
+
+ if (!((afi == AFI_IP || afi == AFI_IP6) && safi == SAFI_UNICAST)) {
+ vty_out(vty,
+ "%% This command is applicable only for unicast ipv4|ipv6\n");
+ return CMD_WARNING;
+ }
+
+ if (vrf && strmatch(vrf, "all"))
+ return bgp_show_all_instance_route_leak_vty(vty, afi, safi, uj);
+
+ if (uj)
+ json = json_object_new_object();
+
+ return bgp_show_route_leak_vty(vty, vrf, afi, safi, uj, json);
+}
+
+static void bgp_show_all_instances_updgrps_vty(struct vty *vty, afi_t afi,
+ safi_t safi)
+{
+ struct listnode *node, *nnode;
+ struct bgp *bgp;
+
+ for (ALL_LIST_ELEMENTS(bm->bgp, node, nnode, bgp)) {
+ vty_out(vty, "\nInstance %s:\n",
+ (bgp->inst_type == BGP_INSTANCE_TYPE_DEFAULT)
+ ? VRF_DEFAULT_NAME
+ : bgp->name);
+ update_group_show(bgp, afi, safi, vty, 0);
+ }
+}
+
+static int bgp_show_update_groups(struct vty *vty, const char *name, int afi,
+ int safi, uint64_t subgrp_id)
+{
+ struct bgp *bgp;
+
+ if (name) {
+ if (strmatch(name, "all")) {
+ bgp_show_all_instances_updgrps_vty(vty, afi, safi);
+ return CMD_SUCCESS;
+ } else {
+ bgp = bgp_lookup_by_name(name);
+ }
+ } else {
+ bgp = bgp_get_default();
+ }
+
+ if (bgp)
+ update_group_show(bgp, afi, safi, vty, subgrp_id);
+ return CMD_SUCCESS;
+}
+
+DEFUN (show_ip_bgp_updgrps,
+ show_ip_bgp_updgrps_cmd,
+ "show [ip] bgp [<view|vrf> VIEWVRFNAME] ["BGP_AFI_CMD_STR" ["BGP_SAFI_WITH_LABEL_CMD_STR"]] update-groups [SUBGROUP-ID]",
+ SHOW_STR
+ IP_STR
+ BGP_STR
+ BGP_INSTANCE_HELP_STR
+ BGP_AFI_HELP_STR
+ BGP_SAFI_WITH_LABEL_HELP_STR
+ "Detailed info about dynamic update groups\n"
+ "Specific subgroup to display detailed info for\n")
+{
+ char *vrf = NULL;
+ afi_t afi = AFI_IP6;
+ safi_t safi = SAFI_UNICAST;
+ uint64_t subgrp_id = 0;
+
+ int idx = 0;
+
+ /* show [ip] bgp */
+ if (argv_find(argv, argc, "ip", &idx))
+ afi = AFI_IP;
+ /* [<vrf> VIEWVRFNAME] */
+ if (argv_find(argv, argc, "vrf", &idx)) {
+ vrf = argv[idx + 1]->arg;
+ if (vrf && strmatch(vrf, VRF_DEFAULT_NAME))
+ vrf = NULL;
+ } else if (argv_find(argv, argc, "view", &idx))
+ /* [<view> VIEWVRFNAME] */
+ vrf = argv[idx + 1]->arg;
+ /* ["BGP_AFI_CMD_STR" ["BGP_SAFI_CMD_STR"]] */
+ if (argv_find_and_parse_afi(argv, argc, &idx, &afi)) {
+ argv_find_and_parse_safi(argv, argc, &idx, &safi);
+ }
+
+ /* get subgroup id, if provided */
+ idx = argc - 1;
+ if (argv[idx]->type == VARIABLE_TKN)
+ subgrp_id = strtoull(argv[idx]->arg, NULL, 10);
+
+ return (bgp_show_update_groups(vty, vrf, afi, safi, subgrp_id));
+}
+
+DEFUN (show_bgp_instance_all_ipv6_updgrps,
+ show_bgp_instance_all_ipv6_updgrps_cmd,
+ "show [ip] bgp <view|vrf> all update-groups",
+ SHOW_STR
+ IP_STR
+ BGP_STR
+ BGP_INSTANCE_ALL_HELP_STR
+ "Detailed info about dynamic update groups\n")
+{
+ bgp_show_all_instances_updgrps_vty(vty, AFI_IP6, SAFI_UNICAST);
+ return CMD_SUCCESS;
+}
+
+DEFUN (show_bgp_l2vpn_evpn_updgrps,
+ show_bgp_l2vpn_evpn_updgrps_cmd,
+ "show [ip] bgp l2vpn evpn update-groups",
+ SHOW_STR
+ IP_STR
+ BGP_STR
+ "l2vpn address family\n"
+ "evpn sub-address family\n"
+ "Detailed info about dynamic update groups\n")
+{
+ char *vrf = NULL;
+ uint64_t subgrp_id = 0;
+
+ bgp_show_update_groups(vty, vrf, AFI_L2VPN, SAFI_EVPN, subgrp_id);
+ return CMD_SUCCESS;
+}
+
+DEFUN (show_bgp_updgrps_stats,
+ show_bgp_updgrps_stats_cmd,
+ "show [ip] bgp update-groups statistics",
+ SHOW_STR
+ IP_STR
+ BGP_STR
+ "Detailed info about dynamic update groups\n"
+ "Statistics\n")
+{
+ struct bgp *bgp;
+
+ bgp = bgp_get_default();
+ if (bgp)
+ update_group_show_stats(bgp, vty);
+
+ return CMD_SUCCESS;
+}
+
+DEFUN (show_bgp_instance_updgrps_stats,
+ show_bgp_instance_updgrps_stats_cmd,
+ "show [ip] bgp <view|vrf> VIEWVRFNAME update-groups statistics",
+ SHOW_STR
+ IP_STR
+ BGP_STR
+ BGP_INSTANCE_HELP_STR
+ "Detailed info about dynamic update groups\n"
+ "Statistics\n")
+{
+ int idx_word = 3;
+ struct bgp *bgp;
+
+ bgp = bgp_lookup_by_name(argv[idx_word]->arg);
+ if (bgp)
+ update_group_show_stats(bgp, vty);
+
+ return CMD_SUCCESS;
+}
+
+static void show_bgp_updgrps_adj_info_aux(struct vty *vty, const char *name,
+ afi_t afi, safi_t safi,
+ const char *what, uint64_t subgrp_id)
+{
+ struct bgp *bgp;
+
+ if (name)
+ bgp = bgp_lookup_by_name(name);
+ else
+ bgp = bgp_get_default();
+
+ if (bgp) {
+ if (!strcmp(what, "advertise-queue"))
+ update_group_show_adj_queue(bgp, afi, safi, vty,
+ subgrp_id);
+ else if (!strcmp(what, "advertised-routes"))
+ update_group_show_advertised(bgp, afi, safi, vty,
+ subgrp_id);
+ else if (!strcmp(what, "packet-queue"))
+ update_group_show_packet_queue(bgp, afi, safi, vty,
+ subgrp_id);
+ }
+}
+
+DEFPY(show_ip_bgp_instance_updgrps_adj_s,
+ show_ip_bgp_instance_updgrps_adj_s_cmd,
+ "show [ip]$ip bgp [<view|vrf> VIEWVRFNAME$vrf] [<ipv4|ipv6>$afi <unicast|multicast|vpn>$safi] update-groups [SUBGROUP-ID]$sgid <advertise-queue|advertised-routes|packet-queue>$rtq",
+ SHOW_STR IP_STR BGP_STR BGP_INSTANCE_HELP_STR BGP_AFI_HELP_STR
+ BGP_SAFI_HELP_STR
+ "Detailed info about dynamic update groups\n"
+ "Specific subgroup to display info for\n"
+ "Advertisement queue\n"
+ "Announced routes\n"
+ "Packet queue\n")
+{
+ uint64_t subgrp_id = 0;
+ afi_t afiz;
+ safi_t safiz;
+ if (sgid)
+ subgrp_id = strtoull(sgid, NULL, 10);
+
+ if (!ip && !afi)
+ afiz = AFI_IP6;
+ if (!ip && afi)
+ afiz = bgp_vty_afi_from_str(afi);
+ if (ip && !afi)
+ afiz = AFI_IP;
+ if (ip && afi) {
+ afiz = bgp_vty_afi_from_str(afi);
+ if (afiz != AFI_IP)
+ vty_out(vty,
+ "%% Cannot specify both 'ip' and 'ipv6'\n");
+ return CMD_WARNING;
+ }
+
+ safiz = safi ? bgp_vty_safi_from_str(safi) : SAFI_UNICAST;
+
+ show_bgp_updgrps_adj_info_aux(vty, vrf, afiz, safiz, rtq, subgrp_id);
+ return CMD_SUCCESS;
+}
+
+static int bgp_show_one_peer_group(struct vty *vty, struct peer_group *group,
+ json_object *json)
+{
+ struct listnode *node, *nnode;
+ struct prefix *range;
+ struct peer *conf;
+ struct peer *peer;
+ afi_t afi;
+ safi_t safi;
+ const char *peer_status;
+ int lr_count;
+ int dynamic;
+ bool af_cfgd;
+ json_object *json_peer_group = NULL;
+ json_object *json_peer_group_afc = NULL;
+ json_object *json_peer_group_members = NULL;
+ json_object *json_peer_group_dynamic = NULL;
+ json_object *json_peer_group_dynamic_af = NULL;
+ json_object *json_peer_group_ranges = NULL;
+
+ conf = group->conf;
+
+ if (json) {
+ json_peer_group = json_object_new_object();
+ json_peer_group_afc = json_object_new_array();
+ }
+
+ if (conf->as_type == AS_SPECIFIED || conf->as_type == AS_EXTERNAL) {
+ if (json)
+ json_object_int_add(json_peer_group, "remoteAs",
+ conf->as);
+ else
+ vty_out(vty, "\nBGP peer-group %s, remote AS %u\n",
+ group->name, conf->as);
+ } else if (conf->as_type == AS_INTERNAL) {
+ if (json)
+ json_object_int_add(json_peer_group, "remoteAs",
+ group->bgp->as);
+ else
+ vty_out(vty, "\nBGP peer-group %s, remote AS %u\n",
+ group->name, group->bgp->as);
+ } else {
+ if (!json)
+ vty_out(vty, "\nBGP peer-group %s\n", group->name);
+ }
+
+ if ((group->bgp->as == conf->as) || (conf->as_type == AS_INTERNAL)) {
+ if (json)
+ json_object_string_add(json_peer_group, "type",
+ "internal");
+ else
+ vty_out(vty, " Peer-group type is internal\n");
+ } else {
+ if (json)
+ json_object_string_add(json_peer_group, "type",
+ "external");
+ else
+ vty_out(vty, " Peer-group type is external\n");
+ }
+
+ /* Display AFs configured. */
+ if (!json)
+ vty_out(vty, " Configured address-families:");
+
+ FOREACH_AFI_SAFI (afi, safi) {
+ if (conf->afc[afi][safi]) {
+ af_cfgd = true;
+ if (json)
+ json_object_array_add(
+ json_peer_group_afc,
+ json_object_new_string(get_afi_safi_str(
+ afi, safi, false)));
+ else
+ vty_out(vty, " %s;",
+ get_afi_safi_str(afi, safi, false));
+ }
+ }
+
+ if (json) {
+ json_object_object_add(json_peer_group,
+ "addressFamiliesConfigured",
+ json_peer_group_afc);
+ } else {
+ if (!af_cfgd)
+ vty_out(vty, " none\n");
+ else
+ vty_out(vty, "\n");
+ }
+
+ /* Display listen ranges (for dynamic neighbors), if any */
+ for (afi = AFI_IP; afi < AFI_MAX; afi++) {
+ lr_count = listcount(group->listen_range[afi]);
+ if (lr_count) {
+ if (json) {
+ if (!json_peer_group_dynamic)
+ json_peer_group_dynamic =
+ json_object_new_object();
+
+ json_peer_group_dynamic_af =
+ json_object_new_object();
+ json_peer_group_ranges =
+ json_object_new_array();
+ json_object_int_add(json_peer_group_dynamic_af,
+ "count", lr_count);
+ } else {
+ vty_out(vty, " %d %s listen range(s)\n",
+ lr_count, afi2str(afi));
+ }
+
+ for (ALL_LIST_ELEMENTS(group->listen_range[afi], node,
+ nnode, range)) {
+ if (json) {
+ char buf[BUFSIZ];
+
+ snprintfrr(buf, sizeof(buf), "%pFX",
+ range);
+
+ json_object_array_add(
+ json_peer_group_ranges,
+ json_object_new_string(buf));
+ } else {
+ vty_out(vty, " %pFX\n", range);
+ }
+ }
+
+ if (json) {
+ json_object_object_add(
+ json_peer_group_dynamic_af, "ranges",
+ json_peer_group_ranges);
+
+ json_object_object_add(
+ json_peer_group_dynamic, afi2str(afi),
+ json_peer_group_dynamic_af);
+ }
+ }
+ }
+
+ if (json_peer_group_dynamic)
+ json_object_object_add(json_peer_group, "dynamicRanges",
+ json_peer_group_dynamic);
+
+ /* Display group members and their status */
+ if (listcount(group->peer)) {
+ if (json)
+ json_peer_group_members = json_object_new_object();
+ else
+ vty_out(vty, " Peer-group members:\n");
+ for (ALL_LIST_ELEMENTS(group->peer, node, nnode, peer)) {
+ if (CHECK_FLAG(peer->flags, PEER_FLAG_SHUTDOWN)
+ || CHECK_FLAG(peer->bgp->flags, BGP_FLAG_SHUTDOWN))
+ peer_status = "Idle (Admin)";
+ else if (CHECK_FLAG(peer->sflags,
+ PEER_STATUS_PREFIX_OVERFLOW))
+ peer_status = "Idle (PfxCt)";
+ else
+ peer_status = lookup_msg(bgp_status_msg,
+ peer->status, NULL);
+
+ dynamic = peer_dynamic_neighbor(peer);
+
+ if (json) {
+ json_object *json_peer_group_member =
+ json_object_new_object();
+
+ json_object_string_add(json_peer_group_member,
+ "status", peer_status);
+
+ if (dynamic)
+ json_object_boolean_true_add(
+ json_peer_group_member,
+ "dynamic");
+
+ json_object_object_add(json_peer_group_members,
+ peer->host,
+ json_peer_group_member);
+ } else {
+ vty_out(vty, " %s %s %s \n", peer->host,
+ dynamic ? "(dynamic)" : "",
+ peer_status);
+ }
+ }
+ if (json)
+ json_object_object_add(json_peer_group, "members",
+ json_peer_group_members);
+ }
+
+ if (json)
+ json_object_object_add(json, group->name, json_peer_group);
+
+ return CMD_SUCCESS;
+}
+
+static int bgp_show_peer_group_vty(struct vty *vty, const char *name,
+ const char *group_name, bool uj)
+{
+ struct bgp *bgp;
+ struct listnode *node, *nnode;
+ struct peer_group *group;
+ bool found = false;
+ json_object *json = NULL;
+
+ if (uj)
+ json = json_object_new_object();
+
+ bgp = name ? bgp_lookup_by_name(name) : bgp_get_default();
+
+ if (!bgp) {
+ if (uj)
+ vty_json(vty, json);
+ else
+ vty_out(vty, "%% BGP instance not found\n");
+
+ return CMD_WARNING;
+ }
+
+ for (ALL_LIST_ELEMENTS(bgp->group, node, nnode, group)) {
+ if (group_name) {
+ if (strmatch(group->name, group_name)) {
+ bgp_show_one_peer_group(vty, group, json);
+ found = true;
+ break;
+ }
+ } else {
+ bgp_show_one_peer_group(vty, group, json);
+ }
+ }
+
+ if (group_name && !found && !uj)
+ vty_out(vty, "%% No such peer-group\n");
+
+ if (uj)
+ vty_json(vty, json);
+
+ return CMD_SUCCESS;
+}
+
+DEFUN(show_ip_bgp_peer_groups, show_ip_bgp_peer_groups_cmd,
+ "show [ip] bgp [<view|vrf> VIEWVRFNAME] peer-group [PGNAME] [json]",
+ SHOW_STR IP_STR BGP_STR BGP_INSTANCE_HELP_STR
+ "Detailed information on BGP peer groups\n"
+ "Peer group name\n" JSON_STR)
+{
+ char *vrf, *pg;
+ int idx = 0;
+ bool uj = use_json(argc, argv);
+
+ vrf = argv_find(argv, argc, "VIEWVRFNAME", &idx) ? argv[idx]->arg
+ : NULL;
+ pg = argv_find(argv, argc, "PGNAME", &idx) ? argv[idx]->arg : NULL;
+
+ return bgp_show_peer_group_vty(vty, vrf, pg, uj);
+}
+
+
+/* Redistribute VTY commands. */
+
+DEFUN (bgp_redistribute_ipv4,
+ bgp_redistribute_ipv4_cmd,
+ "redistribute " FRR_IP_REDIST_STR_BGPD,
+ "Redistribute information from another routing protocol\n"
+ FRR_IP_REDIST_HELP_STR_BGPD)
+{
+ VTY_DECLVAR_CONTEXT(bgp, bgp);
+ int idx_protocol = 1;
+ int type;
+
+ type = proto_redistnum(AFI_IP, argv[idx_protocol]->text);
+ if (type < 0) {
+ vty_out(vty, "%% Invalid route type\n");
+ return CMD_WARNING_CONFIG_FAILED;
+ }
+
+ bgp_redist_add(bgp, AFI_IP, type, 0);
+ return bgp_redistribute_set(bgp, AFI_IP, type, 0, false);
+}
+
+ALIAS_HIDDEN(
+ bgp_redistribute_ipv4, bgp_redistribute_ipv4_hidden_cmd,
+ "redistribute " FRR_IP_REDIST_STR_BGPD,
+ "Redistribute information from another routing protocol\n" FRR_IP_REDIST_HELP_STR_BGPD)
+
+DEFUN (bgp_redistribute_ipv4_rmap,
+ bgp_redistribute_ipv4_rmap_cmd,
+ "redistribute " FRR_IP_REDIST_STR_BGPD " route-map RMAP_NAME",
+ "Redistribute information from another routing protocol\n"
+ FRR_IP_REDIST_HELP_STR_BGPD
+ "Route map reference\n"
+ "Pointer to route-map entries\n")
+{
+ VTY_DECLVAR_CONTEXT(bgp, bgp);
+ int idx_protocol = 1;
+ int idx_word = 3;
+ int type;
+ struct bgp_redist *red;
+ bool changed;
+ struct route_map *route_map = route_map_lookup_warn_noexist(
+ vty, argv[idx_word]->arg);
+
+ type = proto_redistnum(AFI_IP, argv[idx_protocol]->text);
+ if (type < 0) {
+ vty_out(vty, "%% Invalid route type\n");
+ return CMD_WARNING_CONFIG_FAILED;
+ }
+
+ red = bgp_redist_add(bgp, AFI_IP, type, 0);
+ changed =
+ bgp_redistribute_rmap_set(red, argv[idx_word]->arg, route_map);
+ return bgp_redistribute_set(bgp, AFI_IP, type, 0, changed);
+}
+
+ALIAS_HIDDEN(
+ bgp_redistribute_ipv4_rmap, bgp_redistribute_ipv4_rmap_hidden_cmd,
+ "redistribute " FRR_IP_REDIST_STR_BGPD " route-map RMAP_NAME",
+ "Redistribute information from another routing protocol\n" FRR_IP_REDIST_HELP_STR_BGPD
+ "Route map reference\n"
+ "Pointer to route-map entries\n")
+
+DEFUN (bgp_redistribute_ipv4_metric,
+ bgp_redistribute_ipv4_metric_cmd,
+ "redistribute " FRR_IP_REDIST_STR_BGPD " metric (0-4294967295)",
+ "Redistribute information from another routing protocol\n"
+ FRR_IP_REDIST_HELP_STR_BGPD
+ "Metric for redistributed routes\n"
+ "Default metric\n")
+{
+ VTY_DECLVAR_CONTEXT(bgp, bgp);
+ int idx_protocol = 1;
+ int idx_number = 3;
+ int type;
+ uint32_t metric;
+ struct bgp_redist *red;
+ bool changed;
+
+ type = proto_redistnum(AFI_IP, argv[idx_protocol]->text);
+ if (type < 0) {
+ vty_out(vty, "%% Invalid route type\n");
+ return CMD_WARNING_CONFIG_FAILED;
+ }
+ metric = strtoul(argv[idx_number]->arg, NULL, 10);
+
+ red = bgp_redist_add(bgp, AFI_IP, type, 0);
+ changed = bgp_redistribute_metric_set(bgp, red, AFI_IP, type, metric);
+ return bgp_redistribute_set(bgp, AFI_IP, type, 0, changed);
+}
+
+ALIAS_HIDDEN(
+ bgp_redistribute_ipv4_metric, bgp_redistribute_ipv4_metric_hidden_cmd,
+ "redistribute " FRR_IP_REDIST_STR_BGPD " metric (0-4294967295)",
+ "Redistribute information from another routing protocol\n" FRR_IP_REDIST_HELP_STR_BGPD
+ "Metric for redistributed routes\n"
+ "Default metric\n")
+
+DEFUN (bgp_redistribute_ipv4_rmap_metric,
+ bgp_redistribute_ipv4_rmap_metric_cmd,
+ "redistribute " FRR_IP_REDIST_STR_BGPD " route-map RMAP_NAME metric (0-4294967295)",
+ "Redistribute information from another routing protocol\n"
+ FRR_IP_REDIST_HELP_STR_BGPD
+ "Route map reference\n"
+ "Pointer to route-map entries\n"
+ "Metric for redistributed routes\n"
+ "Default metric\n")
+{
+ VTY_DECLVAR_CONTEXT(bgp, bgp);
+ int idx_protocol = 1;
+ int idx_word = 3;
+ int idx_number = 5;
+ int type;
+ uint32_t metric;
+ struct bgp_redist *red;
+ bool changed;
+ struct route_map *route_map =
+ route_map_lookup_warn_noexist(vty, argv[idx_word]->arg);
+
+ type = proto_redistnum(AFI_IP, argv[idx_protocol]->text);
+ if (type < 0) {
+ vty_out(vty, "%% Invalid route type\n");
+ return CMD_WARNING_CONFIG_FAILED;
+ }
+ metric = strtoul(argv[idx_number]->arg, NULL, 10);
+
+ red = bgp_redist_add(bgp, AFI_IP, type, 0);
+ changed =
+ bgp_redistribute_rmap_set(red, argv[idx_word]->arg, route_map);
+ changed |= bgp_redistribute_metric_set(bgp, red, AFI_IP, type, metric);
+ return bgp_redistribute_set(bgp, AFI_IP, type, 0, changed);
+}
+
+ALIAS_HIDDEN(
+ bgp_redistribute_ipv4_rmap_metric,
+ bgp_redistribute_ipv4_rmap_metric_hidden_cmd,
+ "redistribute " FRR_IP_REDIST_STR_BGPD
+ " route-map RMAP_NAME metric (0-4294967295)",
+ "Redistribute information from another routing protocol\n" FRR_IP_REDIST_HELP_STR_BGPD
+ "Route map reference\n"
+ "Pointer to route-map entries\n"
+ "Metric for redistributed routes\n"
+ "Default metric\n")
+
+DEFUN (bgp_redistribute_ipv4_metric_rmap,
+ bgp_redistribute_ipv4_metric_rmap_cmd,
+ "redistribute " FRR_IP_REDIST_STR_BGPD " metric (0-4294967295) route-map RMAP_NAME",
+ "Redistribute information from another routing protocol\n"
+ FRR_IP_REDIST_HELP_STR_BGPD
+ "Metric for redistributed routes\n"
+ "Default metric\n"
+ "Route map reference\n"
+ "Pointer to route-map entries\n")
+{
+ VTY_DECLVAR_CONTEXT(bgp, bgp);
+ int idx_protocol = 1;
+ int idx_number = 3;
+ int idx_word = 5;
+ int type;
+ uint32_t metric;
+ struct bgp_redist *red;
+ bool changed;
+ struct route_map *route_map =
+ route_map_lookup_warn_noexist(vty, argv[idx_word]->arg);
+
+ type = proto_redistnum(AFI_IP, argv[idx_protocol]->text);
+ if (type < 0) {
+ vty_out(vty, "%% Invalid route type\n");
+ return CMD_WARNING_CONFIG_FAILED;
+ }
+ metric = strtoul(argv[idx_number]->arg, NULL, 10);
+
+ red = bgp_redist_add(bgp, AFI_IP, type, 0);
+ changed = bgp_redistribute_metric_set(bgp, red, AFI_IP, type, metric);
+ changed |=
+ bgp_redistribute_rmap_set(red, argv[idx_word]->arg, route_map);
+ return bgp_redistribute_set(bgp, AFI_IP, type, 0, changed);
+}
+
+ALIAS_HIDDEN(
+ bgp_redistribute_ipv4_metric_rmap,
+ bgp_redistribute_ipv4_metric_rmap_hidden_cmd,
+ "redistribute " FRR_IP_REDIST_STR_BGPD
+ " metric (0-4294967295) route-map RMAP_NAME",
+ "Redistribute information from another routing protocol\n" FRR_IP_REDIST_HELP_STR_BGPD
+ "Metric for redistributed routes\n"
+ "Default metric\n"
+ "Route map reference\n"
+ "Pointer to route-map entries\n")
+
+DEFUN (bgp_redistribute_ipv4_ospf,
+ bgp_redistribute_ipv4_ospf_cmd,
+ "redistribute <ospf|table> (1-65535)",
+ "Redistribute information from another routing protocol\n"
+ "Open Shortest Path First (OSPFv2)\n"
+ "Non-main Kernel Routing Table\n"
+ "Instance ID/Table ID\n")
+{
+ VTY_DECLVAR_CONTEXT(bgp, bgp);
+ int idx_ospf_table = 1;
+ int idx_number = 2;
+ unsigned short instance;
+ unsigned short protocol;
+
+ instance = strtoul(argv[idx_number]->arg, NULL, 10);
+
+ if (strncmp(argv[idx_ospf_table]->arg, "o", 1) == 0)
+ protocol = ZEBRA_ROUTE_OSPF;
+ else
+ protocol = ZEBRA_ROUTE_TABLE;
+
+ bgp_redist_add(bgp, AFI_IP, protocol, instance);
+ return bgp_redistribute_set(bgp, AFI_IP, protocol, instance, false);
+}
+
+ALIAS_HIDDEN(bgp_redistribute_ipv4_ospf, bgp_redistribute_ipv4_ospf_hidden_cmd,
+ "redistribute <ospf|table> (1-65535)",
+ "Redistribute information from another routing protocol\n"
+ "Open Shortest Path First (OSPFv2)\n"
+ "Non-main Kernel Routing Table\n"
+ "Instance ID/Table ID\n")
+
+DEFUN (bgp_redistribute_ipv4_ospf_rmap,
+ bgp_redistribute_ipv4_ospf_rmap_cmd,
+ "redistribute <ospf|table> (1-65535) route-map RMAP_NAME",
+ "Redistribute information from another routing protocol\n"
+ "Open Shortest Path First (OSPFv2)\n"
+ "Non-main Kernel Routing Table\n"
+ "Instance ID/Table ID\n"
+ "Route map reference\n"
+ "Pointer to route-map entries\n")
+{
+ VTY_DECLVAR_CONTEXT(bgp, bgp);
+ int idx_ospf_table = 1;
+ int idx_number = 2;
+ int idx_word = 4;
+ struct bgp_redist *red;
+ unsigned short instance;
+ int protocol;
+ bool changed;
+ struct route_map *route_map =
+ route_map_lookup_warn_noexist(vty, argv[idx_word]->arg);
+
+ if (strncmp(argv[idx_ospf_table]->arg, "o", 1) == 0)
+ protocol = ZEBRA_ROUTE_OSPF;
+ else
+ protocol = ZEBRA_ROUTE_TABLE;
+
+ instance = strtoul(argv[idx_number]->arg, NULL, 10);
+ red = bgp_redist_add(bgp, AFI_IP, protocol, instance);
+ changed =
+ bgp_redistribute_rmap_set(red, argv[idx_word]->arg, route_map);
+ return bgp_redistribute_set(bgp, AFI_IP, protocol, instance, changed);
+}
+
+ALIAS_HIDDEN(bgp_redistribute_ipv4_ospf_rmap,
+ bgp_redistribute_ipv4_ospf_rmap_hidden_cmd,
+ "redistribute <ospf|table> (1-65535) route-map RMAP_NAME",
+ "Redistribute information from another routing protocol\n"
+ "Open Shortest Path First (OSPFv2)\n"
+ "Non-main Kernel Routing Table\n"
+ "Instance ID/Table ID\n"
+ "Route map reference\n"
+ "Pointer to route-map entries\n")
+
+DEFUN (bgp_redistribute_ipv4_ospf_metric,
+ bgp_redistribute_ipv4_ospf_metric_cmd,
+ "redistribute <ospf|table> (1-65535) metric (0-4294967295)",
+ "Redistribute information from another routing protocol\n"
+ "Open Shortest Path First (OSPFv2)\n"
+ "Non-main Kernel Routing Table\n"
+ "Instance ID/Table ID\n"
+ "Metric for redistributed routes\n"
+ "Default metric\n")
+{
+ VTY_DECLVAR_CONTEXT(bgp, bgp);
+ int idx_ospf_table = 1;
+ int idx_number = 2;
+ int idx_number_2 = 4;
+ uint32_t metric;
+ struct bgp_redist *red;
+ unsigned short instance;
+ int protocol;
+ bool changed;
+
+ if (strncmp(argv[idx_ospf_table]->arg, "o", 1) == 0)
+ protocol = ZEBRA_ROUTE_OSPF;
+ else
+ protocol = ZEBRA_ROUTE_TABLE;
+
+ instance = strtoul(argv[idx_number]->arg, NULL, 10);
+ metric = strtoul(argv[idx_number_2]->arg, NULL, 10);
+
+ red = bgp_redist_add(bgp, AFI_IP, protocol, instance);
+ changed = bgp_redistribute_metric_set(bgp, red, AFI_IP, protocol,
+ metric);
+ return bgp_redistribute_set(bgp, AFI_IP, protocol, instance, changed);
+}
+
+ALIAS_HIDDEN(bgp_redistribute_ipv4_ospf_metric,
+ bgp_redistribute_ipv4_ospf_metric_hidden_cmd,
+ "redistribute <ospf|table> (1-65535) metric (0-4294967295)",
+ "Redistribute information from another routing protocol\n"
+ "Open Shortest Path First (OSPFv2)\n"
+ "Non-main Kernel Routing Table\n"
+ "Instance ID/Table ID\n"
+ "Metric for redistributed routes\n"
+ "Default metric\n")
+
+DEFUN (bgp_redistribute_ipv4_ospf_rmap_metric,
+ bgp_redistribute_ipv4_ospf_rmap_metric_cmd,
+ "redistribute <ospf|table> (1-65535) route-map RMAP_NAME metric (0-4294967295)",
+ "Redistribute information from another routing protocol\n"
+ "Open Shortest Path First (OSPFv2)\n"
+ "Non-main Kernel Routing Table\n"
+ "Instance ID/Table ID\n"
+ "Route map reference\n"
+ "Pointer to route-map entries\n"
+ "Metric for redistributed routes\n"
+ "Default metric\n")
+{
+ VTY_DECLVAR_CONTEXT(bgp, bgp);
+ int idx_ospf_table = 1;
+ int idx_number = 2;
+ int idx_word = 4;
+ int idx_number_2 = 6;
+ uint32_t metric;
+ struct bgp_redist *red;
+ unsigned short instance;
+ int protocol;
+ bool changed;
+ struct route_map *route_map =
+ route_map_lookup_warn_noexist(vty, argv[idx_word]->arg);
+
+ if (strncmp(argv[idx_ospf_table]->arg, "o", 1) == 0)
+ protocol = ZEBRA_ROUTE_OSPF;
+ else
+ protocol = ZEBRA_ROUTE_TABLE;
+
+ instance = strtoul(argv[idx_number]->arg, NULL, 10);
+ metric = strtoul(argv[idx_number_2]->arg, NULL, 10);
+
+ red = bgp_redist_add(bgp, AFI_IP, protocol, instance);
+ changed =
+ bgp_redistribute_rmap_set(red, argv[idx_word]->arg, route_map);
+ changed |= bgp_redistribute_metric_set(bgp, red, AFI_IP, protocol,
+ metric);
+ return bgp_redistribute_set(bgp, AFI_IP, protocol, instance, changed);
+}
+
+ALIAS_HIDDEN(
+ bgp_redistribute_ipv4_ospf_rmap_metric,
+ bgp_redistribute_ipv4_ospf_rmap_metric_hidden_cmd,
+ "redistribute <ospf|table> (1-65535) route-map RMAP_NAME metric (0-4294967295)",
+ "Redistribute information from another routing protocol\n"
+ "Open Shortest Path First (OSPFv2)\n"
+ "Non-main Kernel Routing Table\n"
+ "Instance ID/Table ID\n"
+ "Route map reference\n"
+ "Pointer to route-map entries\n"
+ "Metric for redistributed routes\n"
+ "Default metric\n")
+
+DEFUN (bgp_redistribute_ipv4_ospf_metric_rmap,
+ bgp_redistribute_ipv4_ospf_metric_rmap_cmd,
+ "redistribute <ospf|table> (1-65535) metric (0-4294967295) route-map RMAP_NAME",
+ "Redistribute information from another routing protocol\n"
+ "Open Shortest Path First (OSPFv2)\n"
+ "Non-main Kernel Routing Table\n"
+ "Instance ID/Table ID\n"
+ "Metric for redistributed routes\n"
+ "Default metric\n"
+ "Route map reference\n"
+ "Pointer to route-map entries\n")
+{
+ VTY_DECLVAR_CONTEXT(bgp, bgp);
+ int idx_ospf_table = 1;
+ int idx_number = 2;
+ int idx_number_2 = 4;
+ int idx_word = 6;
+ uint32_t metric;
+ struct bgp_redist *red;
+ unsigned short instance;
+ int protocol;
+ bool changed;
+ struct route_map *route_map =
+ route_map_lookup_warn_noexist(vty, argv[idx_word]->arg);
+
+ if (strncmp(argv[idx_ospf_table]->arg, "o", 1) == 0)
+ protocol = ZEBRA_ROUTE_OSPF;
+ else
+ protocol = ZEBRA_ROUTE_TABLE;
+
+ instance = strtoul(argv[idx_number]->arg, NULL, 10);
+ metric = strtoul(argv[idx_number_2]->arg, NULL, 10);
+
+ red = bgp_redist_add(bgp, AFI_IP, protocol, instance);
+ changed = bgp_redistribute_metric_set(bgp, red, AFI_IP, protocol,
+ metric);
+ changed |=
+ bgp_redistribute_rmap_set(red, argv[idx_word]->arg, route_map);
+ return bgp_redistribute_set(bgp, AFI_IP, protocol, instance, changed);
+}
+
+ALIAS_HIDDEN(
+ bgp_redistribute_ipv4_ospf_metric_rmap,
+ bgp_redistribute_ipv4_ospf_metric_rmap_hidden_cmd,
+ "redistribute <ospf|table> (1-65535) metric (0-4294967295) route-map RMAP_NAME",
+ "Redistribute information from another routing protocol\n"
+ "Open Shortest Path First (OSPFv2)\n"
+ "Non-main Kernel Routing Table\n"
+ "Instance ID/Table ID\n"
+ "Metric for redistributed routes\n"
+ "Default metric\n"
+ "Route map reference\n"
+ "Pointer to route-map entries\n")
+
+DEFUN (no_bgp_redistribute_ipv4_ospf,
+ no_bgp_redistribute_ipv4_ospf_cmd,
+ "no redistribute <ospf|table> (1-65535) [{metric (0-4294967295)|route-map RMAP_NAME}]",
+ NO_STR
+ "Redistribute information from another routing protocol\n"
+ "Open Shortest Path First (OSPFv2)\n"
+ "Non-main Kernel Routing Table\n"
+ "Instance ID/Table ID\n"
+ "Metric for redistributed routes\n"
+ "Default metric\n"
+ "Route map reference\n"
+ "Pointer to route-map entries\n")
+{
+ VTY_DECLVAR_CONTEXT(bgp, bgp);
+ int idx_ospf_table = 2;
+ int idx_number = 3;
+ unsigned short instance;
+ int protocol;
+
+ if (strncmp(argv[idx_ospf_table]->arg, "o", 1) == 0)
+ protocol = ZEBRA_ROUTE_OSPF;
+ else
+ protocol = ZEBRA_ROUTE_TABLE;
+
+ instance = strtoul(argv[idx_number]->arg, NULL, 10);
+ return bgp_redistribute_unset(bgp, AFI_IP, protocol, instance);
+}
+
+ALIAS_HIDDEN(
+ no_bgp_redistribute_ipv4_ospf, no_bgp_redistribute_ipv4_ospf_hidden_cmd,
+ "no redistribute <ospf|table> (1-65535) [{metric (0-4294967295)|route-map RMAP_NAME}]",
+ NO_STR
+ "Redistribute information from another routing protocol\n"
+ "Open Shortest Path First (OSPFv2)\n"
+ "Non-main Kernel Routing Table\n"
+ "Instance ID/Table ID\n"
+ "Metric for redistributed routes\n"
+ "Default metric\n"
+ "Route map reference\n"
+ "Pointer to route-map entries\n")
+
+DEFUN (no_bgp_redistribute_ipv4,
+ no_bgp_redistribute_ipv4_cmd,
+ "no redistribute " FRR_IP_REDIST_STR_BGPD " [{metric (0-4294967295)|route-map RMAP_NAME}]",
+ NO_STR
+ "Redistribute information from another routing protocol\n"
+ FRR_IP_REDIST_HELP_STR_BGPD
+ "Metric for redistributed routes\n"
+ "Default metric\n"
+ "Route map reference\n"
+ "Pointer to route-map entries\n")
+{
+ VTY_DECLVAR_CONTEXT(bgp, bgp);
+ int idx_protocol = 2;
+ int type;
+
+ type = proto_redistnum(AFI_IP, argv[idx_protocol]->text);
+ if (type < 0) {
+ vty_out(vty, "%% Invalid route type\n");
+ return CMD_WARNING_CONFIG_FAILED;
+ }
+ return bgp_redistribute_unset(bgp, AFI_IP, type, 0);
+}
+
+ALIAS_HIDDEN(
+ no_bgp_redistribute_ipv4, no_bgp_redistribute_ipv4_hidden_cmd,
+ "no redistribute " FRR_IP_REDIST_STR_BGPD
+ " [{metric (0-4294967295)|route-map RMAP_NAME}]",
+ NO_STR
+ "Redistribute information from another routing protocol\n" FRR_IP_REDIST_HELP_STR_BGPD
+ "Metric for redistributed routes\n"
+ "Default metric\n"
+ "Route map reference\n"
+ "Pointer to route-map entries\n")
+
+DEFUN (bgp_redistribute_ipv6,
+ bgp_redistribute_ipv6_cmd,
+ "redistribute " FRR_IP6_REDIST_STR_BGPD,
+ "Redistribute information from another routing protocol\n"
+ FRR_IP6_REDIST_HELP_STR_BGPD)
+{
+ VTY_DECLVAR_CONTEXT(bgp, bgp);
+ int idx_protocol = 1;
+ int type;
+
+ type = proto_redistnum(AFI_IP6, argv[idx_protocol]->text);
+ if (type < 0) {
+ vty_out(vty, "%% Invalid route type\n");
+ return CMD_WARNING_CONFIG_FAILED;
+ }
+
+ bgp_redist_add(bgp, AFI_IP6, type, 0);
+ return bgp_redistribute_set(bgp, AFI_IP6, type, 0, false);
+}
+
+DEFUN (bgp_redistribute_ipv6_rmap,
+ bgp_redistribute_ipv6_rmap_cmd,
+ "redistribute " FRR_IP6_REDIST_STR_BGPD " route-map RMAP_NAME",
+ "Redistribute information from another routing protocol\n"
+ FRR_IP6_REDIST_HELP_STR_BGPD
+ "Route map reference\n"
+ "Pointer to route-map entries\n")
+{
+ VTY_DECLVAR_CONTEXT(bgp, bgp);
+ int idx_protocol = 1;
+ int idx_word = 3;
+ int type;
+ struct bgp_redist *red;
+ bool changed;
+ struct route_map *route_map =
+ route_map_lookup_warn_noexist(vty, argv[idx_word]->arg);
+
+ type = proto_redistnum(AFI_IP6, argv[idx_protocol]->text);
+ if (type < 0) {
+ vty_out(vty, "%% Invalid route type\n");
+ return CMD_WARNING_CONFIG_FAILED;
+ }
+
+ red = bgp_redist_add(bgp, AFI_IP6, type, 0);
+ changed =
+ bgp_redistribute_rmap_set(red, argv[idx_word]->arg, route_map);
+ return bgp_redistribute_set(bgp, AFI_IP6, type, 0, changed);
+}
+
+DEFUN (bgp_redistribute_ipv6_metric,
+ bgp_redistribute_ipv6_metric_cmd,
+ "redistribute " FRR_IP6_REDIST_STR_BGPD " metric (0-4294967295)",
+ "Redistribute information from another routing protocol\n"
+ FRR_IP6_REDIST_HELP_STR_BGPD
+ "Metric for redistributed routes\n"
+ "Default metric\n")
+{
+ VTY_DECLVAR_CONTEXT(bgp, bgp);
+ int idx_protocol = 1;
+ int idx_number = 3;
+ int type;
+ uint32_t metric;
+ struct bgp_redist *red;
+ bool changed;
+
+ type = proto_redistnum(AFI_IP6, argv[idx_protocol]->text);
+ if (type < 0) {
+ vty_out(vty, "%% Invalid route type\n");
+ return CMD_WARNING_CONFIG_FAILED;
+ }
+ metric = strtoul(argv[idx_number]->arg, NULL, 10);
+
+ red = bgp_redist_add(bgp, AFI_IP6, type, 0);
+ changed = bgp_redistribute_metric_set(bgp, red, AFI_IP6, type, metric);
+ return bgp_redistribute_set(bgp, AFI_IP6, type, 0, changed);
+}
+
+DEFUN (bgp_redistribute_ipv6_rmap_metric,
+ bgp_redistribute_ipv6_rmap_metric_cmd,
+ "redistribute " FRR_IP6_REDIST_STR_BGPD " route-map RMAP_NAME metric (0-4294967295)",
+ "Redistribute information from another routing protocol\n"
+ FRR_IP6_REDIST_HELP_STR_BGPD
+ "Route map reference\n"
+ "Pointer to route-map entries\n"
+ "Metric for redistributed routes\n"
+ "Default metric\n")
+{
+ VTY_DECLVAR_CONTEXT(bgp, bgp);
+ int idx_protocol = 1;
+ int idx_word = 3;
+ int idx_number = 5;
+ int type;
+ uint32_t metric;
+ struct bgp_redist *red;
+ bool changed;
+ struct route_map *route_map =
+ route_map_lookup_warn_noexist(vty, argv[idx_word]->arg);
+
+ type = proto_redistnum(AFI_IP6, argv[idx_protocol]->text);
+ if (type < 0) {
+ vty_out(vty, "%% Invalid route type\n");
+ return CMD_WARNING_CONFIG_FAILED;
+ }
+ metric = strtoul(argv[idx_number]->arg, NULL, 10);
+
+ red = bgp_redist_add(bgp, AFI_IP6, type, 0);
+ changed =
+ bgp_redistribute_rmap_set(red, argv[idx_word]->arg, route_map);
+ changed |= bgp_redistribute_metric_set(bgp, red, AFI_IP6, type,
+ metric);
+ return bgp_redistribute_set(bgp, AFI_IP6, type, 0, changed);
+}
+
+DEFUN (bgp_redistribute_ipv6_metric_rmap,
+ bgp_redistribute_ipv6_metric_rmap_cmd,
+ "redistribute " FRR_IP6_REDIST_STR_BGPD " metric (0-4294967295) route-map RMAP_NAME",
+ "Redistribute information from another routing protocol\n"
+ FRR_IP6_REDIST_HELP_STR_BGPD
+ "Metric for redistributed routes\n"
+ "Default metric\n"
+ "Route map reference\n"
+ "Pointer to route-map entries\n")
+{
+ VTY_DECLVAR_CONTEXT(bgp, bgp);
+ int idx_protocol = 1;
+ int idx_number = 3;
+ int idx_word = 5;
+ int type;
+ uint32_t metric;
+ struct bgp_redist *red;
+ bool changed;
+ struct route_map *route_map =
+ route_map_lookup_warn_noexist(vty, argv[idx_word]->arg);
+
+ type = proto_redistnum(AFI_IP6, argv[idx_protocol]->text);
+ if (type < 0) {
+ vty_out(vty, "%% Invalid route type\n");
+ return CMD_WARNING_CONFIG_FAILED;
+ }
+ metric = strtoul(argv[idx_number]->arg, NULL, 10);
+
+ red = bgp_redist_add(bgp, AFI_IP6, type, 0);
+ changed = bgp_redistribute_metric_set(bgp, red, AFI_IP6, SAFI_UNICAST,
+ metric);
+ changed |=
+ bgp_redistribute_rmap_set(red, argv[idx_word]->arg, route_map);
+ return bgp_redistribute_set(bgp, AFI_IP6, type, 0, changed);
+}
+
+DEFUN (no_bgp_redistribute_ipv6,
+ no_bgp_redistribute_ipv6_cmd,
+ "no redistribute " FRR_IP6_REDIST_STR_BGPD " [{metric (0-4294967295)|route-map RMAP_NAME}]",
+ NO_STR
+ "Redistribute information from another routing protocol\n"
+ FRR_IP6_REDIST_HELP_STR_BGPD
+ "Metric for redistributed routes\n"
+ "Default metric\n"
+ "Route map reference\n"
+ "Pointer to route-map entries\n")
+{
+ VTY_DECLVAR_CONTEXT(bgp, bgp);
+ int idx_protocol = 2;
+ int type;
+
+ type = proto_redistnum(AFI_IP6, argv[idx_protocol]->text);
+ if (type < 0) {
+ vty_out(vty, "%% Invalid route type\n");
+ return CMD_WARNING_CONFIG_FAILED;
+ }
+
+ return bgp_redistribute_unset(bgp, AFI_IP6, type, 0);
+}
+
+/* Neighbor update tcp-mss. */
+static int peer_tcp_mss_vty(struct vty *vty, const char *peer_str,
+ const char *tcp_mss_str)
+{
+ struct peer *peer;
+ uint32_t tcp_mss_val = 0;
+
+ peer = peer_and_group_lookup_vty(vty, peer_str);
+ if (!peer)
+ return CMD_WARNING_CONFIG_FAILED;
+
+ if (tcp_mss_str) {
+ tcp_mss_val = strtoul(tcp_mss_str, NULL, 10);
+ peer_tcp_mss_set(peer, tcp_mss_val);
+ } else {
+ peer_tcp_mss_unset(peer);
+ }
+
+ return CMD_SUCCESS;
+}
+
+DEFUN(neighbor_tcp_mss, neighbor_tcp_mss_cmd,
+ "neighbor <A.B.C.D|X:X::X:X|WORD> tcp-mss (1-65535)",
+ NEIGHBOR_STR NEIGHBOR_ADDR_STR2
+ "TCP max segment size\n"
+ "TCP MSS value\n")
+{
+ int peer_index = 1;
+ int mss_index = 3;
+
+ vty_out(vty,
+ " Warning: Reset BGP session for tcp-mss value to take effect\n");
+ return peer_tcp_mss_vty(vty, argv[peer_index]->arg,
+ argv[mss_index]->arg);
+}
+
+DEFUN(no_neighbor_tcp_mss, no_neighbor_tcp_mss_cmd,
+ "no neighbor <A.B.C.D|X:X::X:X|WORD> tcp-mss [(1-65535)]",
+ NO_STR NEIGHBOR_STR NEIGHBOR_ADDR_STR2
+ "TCP max segment size\n"
+ "TCP MSS value\n")
+{
+ int peer_index = 2;
+
+ vty_out(vty,
+ " Warning: Reset BGP session for tcp-mss value to take effect\n");
+ return peer_tcp_mss_vty(vty, argv[peer_index]->arg, NULL);
+}
+
+DEFPY(bgp_retain_route_target, bgp_retain_route_target_cmd,
+ "[no$no] bgp retain route-target all",
+ NO_STR BGP_STR
+ "Retain BGP updates\n"
+ "Retain BGP updates based on route-target values\n"
+ "Retain all BGP updates\n")
+{
+ bool check;
+ struct bgp *bgp = VTY_GET_CONTEXT(bgp);
+
+ check = CHECK_FLAG(bgp->af_flags[bgp_node_afi(vty)][bgp_node_safi(vty)],
+ BGP_VPNVX_RETAIN_ROUTE_TARGET_ALL);
+ if (check != !no) {
+ if (!no)
+ SET_FLAG(bgp->af_flags[bgp_node_afi(vty)]
+ [bgp_node_safi(vty)],
+ BGP_VPNVX_RETAIN_ROUTE_TARGET_ALL);
+ else
+ UNSET_FLAG(bgp->af_flags[bgp_node_afi(vty)]
+ [bgp_node_safi(vty)],
+ BGP_VPNVX_RETAIN_ROUTE_TARGET_ALL);
+ /* trigger a flush to re-sync with ADJ-RIB-in */
+ bgp_clear(vty, bgp, bgp_node_afi(vty), bgp_node_safi(vty),
+ clear_all, BGP_CLEAR_SOFT_IN, NULL);
+ }
+ return CMD_SUCCESS;
+}
+
+static void bgp_config_write_redistribute(struct vty *vty, struct bgp *bgp,
+ afi_t afi, safi_t safi)
+{
+ int i;
+
+ /* Unicast redistribution only. */
+ if (safi != SAFI_UNICAST)
+ return;
+
+ for (i = 0; i < ZEBRA_ROUTE_MAX; i++) {
+ /* Redistribute BGP does not make sense. */
+ if (i != ZEBRA_ROUTE_BGP) {
+ struct list *red_list;
+ struct listnode *node;
+ struct bgp_redist *red;
+
+ red_list = bgp->redist[afi][i];
+ if (!red_list)
+ continue;
+
+ for (ALL_LIST_ELEMENTS_RO(red_list, node, red)) {
+ /* "redistribute" configuration. */
+ vty_out(vty, " redistribute %s",
+ zebra_route_string(i));
+ if (red->instance)
+ vty_out(vty, " %d", red->instance);
+ if (red->redist_metric_flag)
+ vty_out(vty, " metric %u",
+ red->redist_metric);
+ if (red->rmap.name)
+ vty_out(vty, " route-map %s",
+ red->rmap.name);
+ vty_out(vty, "\n");
+ }
+ }
+ }
+}
+
+/* peer-group helpers for config-write */
+
+static bool peergroup_flag_check(struct peer *peer, uint64_t flag)
+{
+ if (!peer_group_active(peer)) {
+ if (CHECK_FLAG(peer->flags_invert, flag))
+ return !CHECK_FLAG(peer->flags, flag);
+ else
+ return !!CHECK_FLAG(peer->flags, flag);
+ }
+
+ return !!CHECK_FLAG(peer->flags_override, flag);
+}
+
+static bool peergroup_af_flag_check(struct peer *peer, afi_t afi, safi_t safi,
+ uint64_t flag)
+{
+ if (!peer_group_active(peer)) {
+ if (CHECK_FLAG(peer->af_flags_invert[afi][safi], flag))
+ return !peer_af_flag_check(peer, afi, safi, flag);
+ else
+ return !!peer_af_flag_check(peer, afi, safi, flag);
+ }
+
+ return !!CHECK_FLAG(peer->af_flags_override[afi][safi], flag);
+}
+
+static bool peergroup_filter_check(struct peer *peer, afi_t afi, safi_t safi,
+ uint8_t type, int direct)
+{
+ struct bgp_filter *filter;
+
+ if (peer_group_active(peer))
+ return !!CHECK_FLAG(peer->filter_override[afi][safi][direct],
+ type);
+
+ filter = &peer->filter[afi][safi];
+ switch (type) {
+ case PEER_FT_DISTRIBUTE_LIST:
+ return !!(filter->dlist[direct].name);
+ case PEER_FT_FILTER_LIST:
+ return !!(filter->aslist[direct].name);
+ case PEER_FT_PREFIX_LIST:
+ return !!(filter->plist[direct].name);
+ case PEER_FT_ROUTE_MAP:
+ return !!(filter->map[direct].name);
+ case PEER_FT_UNSUPPRESS_MAP:
+ return !!(filter->usmap.name);
+ case PEER_FT_ADVERTISE_MAP:
+ return !!(filter->advmap.aname
+ && ((filter->advmap.condition == direct)
+ && filter->advmap.cname));
+ default:
+ return false;
+ }
+}
+
+/* Return true if the addpath type is set for peer and different from
+ * peer-group.
+ */
+static bool peergroup_af_addpath_check(struct peer *peer, afi_t afi,
+ safi_t safi)
+{
+ enum bgp_addpath_strat type, g_type;
+
+ type = peer->addpath_type[afi][safi];
+
+ if (type != BGP_ADDPATH_NONE) {
+ if (peer_group_active(peer)) {
+ g_type = peer->group->conf->addpath_type[afi][safi];
+
+ if (type != g_type)
+ return true;
+ else
+ return false;
+ }
+
+ return true;
+ }
+
+ return false;
+}
+
+/* This is part of the address-family block (unicast only) */
+static void bgp_vpn_policy_config_write_afi(struct vty *vty, struct bgp *bgp,
+ afi_t afi)
+{
+ int indent = 2;
+ uint32_t tovpn_sid_index = 0;
+
+ if (bgp->vpn_policy[afi].rmap_name[BGP_VPN_POLICY_DIR_FROMVPN]) {
+ if (CHECK_FLAG(bgp->af_flags[afi][SAFI_UNICAST],
+ BGP_CONFIG_VRF_TO_VRF_IMPORT))
+ vty_out(vty, "%*simport vrf route-map %s\n", indent, "",
+ bgp->vpn_policy[afi]
+ .rmap_name[BGP_VPN_POLICY_DIR_FROMVPN]);
+ else
+ vty_out(vty, "%*sroute-map vpn import %s\n", indent, "",
+ bgp->vpn_policy[afi]
+ .rmap_name[BGP_VPN_POLICY_DIR_FROMVPN]);
+ }
+ if (CHECK_FLAG(bgp->af_flags[afi][SAFI_UNICAST],
+ BGP_CONFIG_VRF_TO_VRF_IMPORT)
+ || CHECK_FLAG(bgp->af_flags[afi][SAFI_UNICAST],
+ BGP_CONFIG_VRF_TO_VRF_EXPORT))
+ return;
+
+ if (CHECK_FLAG(bgp->vpn_policy[afi].flags,
+ BGP_VPN_POLICY_TOVPN_LABEL_AUTO)) {
+
+ vty_out(vty, "%*slabel vpn export %s\n", indent, "", "auto");
+
+ } else {
+ if (bgp->vpn_policy[afi].tovpn_label != MPLS_LABEL_NONE) {
+ vty_out(vty, "%*slabel vpn export %u\n", indent, "",
+ bgp->vpn_policy[afi].tovpn_label);
+ }
+ }
+
+ tovpn_sid_index = bgp->vpn_policy[afi].tovpn_sid_index;
+ if (CHECK_FLAG(bgp->vpn_policy[afi].flags,
+ BGP_VPN_POLICY_TOVPN_SID_AUTO)) {
+ vty_out(vty, "%*ssid vpn export %s\n", indent, "", "auto");
+ } else if (tovpn_sid_index != 0) {
+ vty_out(vty, "%*ssid vpn export %d\n", indent, "",
+ tovpn_sid_index);
+ }
+
+ if (CHECK_FLAG(bgp->vpn_policy[afi].flags, BGP_VPN_POLICY_TOVPN_RD_SET))
+ vty_out(vty, "%*srd vpn export %pRD\n", indent, "",
+ &bgp->vpn_policy[afi].tovpn_rd);
+
+ if (CHECK_FLAG(bgp->vpn_policy[afi].flags,
+ BGP_VPN_POLICY_TOVPN_NEXTHOP_SET)) {
+
+ char buf[PREFIX_STRLEN];
+ if (inet_ntop(bgp->vpn_policy[afi].tovpn_nexthop.family,
+ &bgp->vpn_policy[afi].tovpn_nexthop.u.prefix, buf,
+ sizeof(buf))) {
+
+ vty_out(vty, "%*snexthop vpn export %s\n",
+ indent, "", buf);
+ }
+ }
+ if (bgp->vpn_policy[afi].rtlist[BGP_VPN_POLICY_DIR_FROMVPN]
+ && bgp->vpn_policy[afi].rtlist[BGP_VPN_POLICY_DIR_TOVPN]
+ && ecommunity_cmp(
+ bgp->vpn_policy[afi].rtlist[BGP_VPN_POLICY_DIR_FROMVPN],
+ bgp->vpn_policy[afi].rtlist[BGP_VPN_POLICY_DIR_TOVPN])) {
+
+ char *b = ecommunity_ecom2str(
+ bgp->vpn_policy[afi].rtlist[BGP_VPN_POLICY_DIR_TOVPN],
+ ECOMMUNITY_FORMAT_ROUTE_MAP, ECOMMUNITY_ROUTE_TARGET);
+ vty_out(vty, "%*srt vpn both %s\n", indent, "", b);
+ XFREE(MTYPE_ECOMMUNITY_STR, b);
+ } else {
+ if (bgp->vpn_policy[afi].rtlist[BGP_VPN_POLICY_DIR_FROMVPN]) {
+ char *b = ecommunity_ecom2str(
+ bgp->vpn_policy[afi]
+ .rtlist[BGP_VPN_POLICY_DIR_FROMVPN],
+ ECOMMUNITY_FORMAT_ROUTE_MAP,
+ ECOMMUNITY_ROUTE_TARGET);
+ vty_out(vty, "%*srt vpn import %s\n", indent, "", b);
+ XFREE(MTYPE_ECOMMUNITY_STR, b);
+ }
+ if (bgp->vpn_policy[afi].rtlist[BGP_VPN_POLICY_DIR_TOVPN]) {
+ char *b = ecommunity_ecom2str(
+ bgp->vpn_policy[afi]
+ .rtlist[BGP_VPN_POLICY_DIR_TOVPN],
+ ECOMMUNITY_FORMAT_ROUTE_MAP,
+ ECOMMUNITY_ROUTE_TARGET);
+ vty_out(vty, "%*srt vpn export %s\n", indent, "", b);
+ XFREE(MTYPE_ECOMMUNITY_STR, b);
+ }
+ }
+
+ if (bgp->vpn_policy[afi].rmap_name[BGP_VPN_POLICY_DIR_TOVPN])
+ vty_out(vty, "%*sroute-map vpn export %s\n", indent, "",
+ bgp->vpn_policy[afi]
+ .rmap_name[BGP_VPN_POLICY_DIR_TOVPN]);
+
+ if (bgp->vpn_policy[afi].import_redirect_rtlist) {
+ char *b = ecommunity_ecom2str(
+ bgp->vpn_policy[afi]
+ .import_redirect_rtlist,
+ ECOMMUNITY_FORMAT_ROUTE_MAP,
+ ECOMMUNITY_ROUTE_TARGET);
+
+ if (bgp->vpn_policy[afi].import_redirect_rtlist->unit_size
+ != ECOMMUNITY_SIZE)
+ vty_out(vty, "%*srt6 redirect import %s\n",
+ indent, "", b);
+ else
+ vty_out(vty, "%*srt redirect import %s\n",
+ indent, "", b);
+ XFREE(MTYPE_ECOMMUNITY_STR, b);
+ }
+}
+
+static void bgp_config_write_filter(struct vty *vty, struct peer *peer,
+ afi_t afi, safi_t safi)
+{
+ struct bgp_filter *filter;
+ char *addr;
+
+ addr = peer->host;
+ filter = &peer->filter[afi][safi];
+
+ /* distribute-list. */
+ if (peergroup_filter_check(peer, afi, safi, PEER_FT_DISTRIBUTE_LIST,
+ FILTER_IN))
+ vty_out(vty, " neighbor %s distribute-list %s in\n", addr,
+ filter->dlist[FILTER_IN].name);
+
+ if (peergroup_filter_check(peer, afi, safi, PEER_FT_DISTRIBUTE_LIST,
+ FILTER_OUT))
+ vty_out(vty, " neighbor %s distribute-list %s out\n", addr,
+ filter->dlist[FILTER_OUT].name);
+
+ /* prefix-list. */
+ if (peergroup_filter_check(peer, afi, safi, PEER_FT_PREFIX_LIST,
+ FILTER_IN))
+ vty_out(vty, " neighbor %s prefix-list %s in\n", addr,
+ filter->plist[FILTER_IN].name);
+
+ if (peergroup_filter_check(peer, afi, safi, PEER_FT_PREFIX_LIST,
+ FILTER_OUT))
+ vty_out(vty, " neighbor %s prefix-list %s out\n", addr,
+ filter->plist[FILTER_OUT].name);
+
+ /* route-map. */
+ if (peergroup_filter_check(peer, afi, safi, PEER_FT_ROUTE_MAP, RMAP_IN))
+ vty_out(vty, " neighbor %s route-map %s in\n", addr,
+ filter->map[RMAP_IN].name);
+
+ if (peergroup_filter_check(peer, afi, safi, PEER_FT_ROUTE_MAP,
+ RMAP_OUT))
+ vty_out(vty, " neighbor %s route-map %s out\n", addr,
+ filter->map[RMAP_OUT].name);
+
+ /* unsuppress-map */
+ if (peergroup_filter_check(peer, afi, safi, PEER_FT_UNSUPPRESS_MAP, 0))
+ vty_out(vty, " neighbor %s unsuppress-map %s\n", addr,
+ filter->usmap.name);
+
+ /* advertise-map : always applied in OUT direction*/
+ if (peergroup_filter_check(peer, afi, safi, PEER_FT_ADVERTISE_MAP,
+ CONDITION_NON_EXIST))
+ vty_out(vty,
+ " neighbor %s advertise-map %s non-exist-map %s\n",
+ addr, filter->advmap.aname, filter->advmap.cname);
+
+ if (peergroup_filter_check(peer, afi, safi, PEER_FT_ADVERTISE_MAP,
+ CONDITION_EXIST))
+ vty_out(vty, " neighbor %s advertise-map %s exist-map %s\n",
+ addr, filter->advmap.aname, filter->advmap.cname);
+
+ /* filter-list. */
+ if (peergroup_filter_check(peer, afi, safi, PEER_FT_FILTER_LIST,
+ FILTER_IN))
+ vty_out(vty, " neighbor %s filter-list %s in\n", addr,
+ filter->aslist[FILTER_IN].name);
+
+ if (peergroup_filter_check(peer, afi, safi, PEER_FT_FILTER_LIST,
+ FILTER_OUT))
+ vty_out(vty, " neighbor %s filter-list %s out\n", addr,
+ filter->aslist[FILTER_OUT].name);
+}
+
+/* BGP peer configuration display function. */
+static void bgp_config_write_peer_global(struct vty *vty, struct bgp *bgp,
+ struct peer *peer)
+{
+ struct peer *g_peer = NULL;
+ char *addr;
+ int if_pg_printed = false;
+ int if_ras_printed = false;
+
+ /* Skip dynamic neighbors. */
+ if (peer_dynamic_neighbor(peer))
+ return;
+
+ if (peer->conf_if)
+ addr = peer->conf_if;
+ else
+ addr = peer->host;
+
+ /************************************
+ ****** Global to the neighbor ******
+ ************************************/
+ if (peer->conf_if) {
+ if (CHECK_FLAG(peer->flags, PEER_FLAG_IFPEER_V6ONLY))
+ vty_out(vty, " neighbor %s interface v6only", addr);
+ else
+ vty_out(vty, " neighbor %s interface", addr);
+
+ if (peer_group_active(peer)) {
+ vty_out(vty, " peer-group %s", peer->group->name);
+ if_pg_printed = true;
+ } else if (peer->as_type == AS_SPECIFIED) {
+ vty_out(vty, " remote-as %u", peer->as);
+ if_ras_printed = true;
+ } else if (peer->as_type == AS_INTERNAL) {
+ vty_out(vty, " remote-as internal");
+ if_ras_printed = true;
+ } else if (peer->as_type == AS_EXTERNAL) {
+ vty_out(vty, " remote-as external");
+ if_ras_printed = true;
+ }
+
+ vty_out(vty, "\n");
+ }
+
+ /* remote-as and peer-group */
+ /* peer is a member of a peer-group */
+ if (peer_group_active(peer)) {
+ g_peer = peer->group->conf;
+
+ if (g_peer->as_type == AS_UNSPECIFIED && !if_ras_printed) {
+ if (peer->as_type == AS_SPECIFIED) {
+ vty_out(vty, " neighbor %s remote-as %u\n",
+ addr, peer->as);
+ } else if (peer->as_type == AS_INTERNAL) {
+ vty_out(vty,
+ " neighbor %s remote-as internal\n",
+ addr);
+ } else if (peer->as_type == AS_EXTERNAL) {
+ vty_out(vty,
+ " neighbor %s remote-as external\n",
+ addr);
+ }
+ }
+
+ /* For swpX peers we displayed the peer-group
+ * via 'neighbor swpX interface peer-group PGNAME' */
+ if (!if_pg_printed)
+ vty_out(vty, " neighbor %s peer-group %s\n", addr,
+ peer->group->name);
+ }
+
+ /* peer is NOT a member of a peer-group */
+ else {
+ /* peer is a peer-group, declare the peer-group */
+ if (CHECK_FLAG(peer->sflags, PEER_STATUS_GROUP)) {
+ vty_out(vty, " neighbor %s peer-group\n", addr);
+ }
+
+ if (!if_ras_printed) {
+ if (peer->as_type == AS_SPECIFIED) {
+ vty_out(vty, " neighbor %s remote-as %u\n",
+ addr, peer->as);
+ } else if (peer->as_type == AS_INTERNAL) {
+ vty_out(vty,
+ " neighbor %s remote-as internal\n",
+ addr);
+ } else if (peer->as_type == AS_EXTERNAL) {
+ vty_out(vty,
+ " neighbor %s remote-as external\n",
+ addr);
+ }
+ }
+ }
+
+ /* local-as */
+ if (peergroup_flag_check(peer, PEER_FLAG_LOCAL_AS)) {
+ vty_out(vty, " neighbor %s local-as %u", addr,
+ peer->change_local_as);
+ if (peergroup_flag_check(peer, PEER_FLAG_LOCAL_AS_NO_PREPEND))
+ vty_out(vty, " no-prepend");
+ if (peergroup_flag_check(peer, PEER_FLAG_LOCAL_AS_REPLACE_AS))
+ vty_out(vty, " replace-as");
+ vty_out(vty, "\n");
+ }
+
+ /* description */
+ if (peer->desc) {
+ vty_out(vty, " neighbor %s description %s\n", addr, peer->desc);
+ }
+
+ /* shutdown */
+ if (peergroup_flag_check(peer, PEER_FLAG_SHUTDOWN)) {
+ if (peer->tx_shutdown_message)
+ vty_out(vty, " neighbor %s shutdown message %s\n", addr,
+ peer->tx_shutdown_message);
+ else
+ vty_out(vty, " neighbor %s shutdown\n", addr);
+ }
+
+ if (peergroup_flag_check(peer, PEER_FLAG_RTT_SHUTDOWN))
+ vty_out(vty, " neighbor %s shutdown rtt %u count %u\n", addr,
+ peer->rtt_expected, peer->rtt_keepalive_conf);
+
+ /* bfd */
+ if (peer->bfd_config)
+ bgp_bfd_peer_config_write(vty, peer, addr);
+
+ /* password */
+ if (peergroup_flag_check(peer, PEER_FLAG_PASSWORD))
+ vty_out(vty, " neighbor %s password %s\n", addr,
+ peer->password);
+
+ /* neighbor solo */
+ if (CHECK_FLAG(peer->flags, PEER_FLAG_LONESOUL)) {
+ if (!peer_group_active(peer)) {
+ vty_out(vty, " neighbor %s solo\n", addr);
+ }
+ }
+
+ /* BGP port */
+ if (peer->port != BGP_PORT_DEFAULT) {
+ vty_out(vty, " neighbor %s port %d\n", addr, peer->port);
+ }
+
+ /* Local interface name */
+ if (peer->ifname) {
+ vty_out(vty, " neighbor %s interface %s\n", addr, peer->ifname);
+ }
+
+ /* TCP max segment size */
+ if (CHECK_FLAG(peer->flags, PEER_FLAG_TCP_MSS))
+ vty_out(vty, " neighbor %s tcp-mss %d\n", addr, peer->tcp_mss);
+
+ /* passive */
+ if (peergroup_flag_check(peer, PEER_FLAG_PASSIVE))
+ vty_out(vty, " neighbor %s passive\n", addr);
+
+ /* ebgp-multihop */
+ if (peer->sort != BGP_PEER_IBGP && peer->ttl != BGP_DEFAULT_TTL
+ && !(peer->gtsm_hops != BGP_GTSM_HOPS_DISABLED
+ && peer->ttl == MAXTTL)) {
+ if (!peer_group_active(peer) || g_peer->ttl != peer->ttl) {
+ vty_out(vty, " neighbor %s ebgp-multihop %d\n", addr,
+ peer->ttl);
+ }
+ }
+
+ /* role */
+ if (peergroup_flag_check(peer, PEER_FLAG_ROLE) &&
+ peer->local_role != ROLE_UNDEFINED)
+ vty_out(vty, " neighbor %s local-role %s%s\n", addr,
+ bgp_get_name_by_role(peer->local_role),
+ CHECK_FLAG(peer->flags, PEER_FLAG_ROLE_STRICT_MODE)
+ ? " strict-mode"
+ : "");
+
+ /* ttl-security hops */
+ if (peer->gtsm_hops != BGP_GTSM_HOPS_DISABLED) {
+ if (!peer_group_active(peer)
+ || g_peer->gtsm_hops != peer->gtsm_hops) {
+ vty_out(vty, " neighbor %s ttl-security hops %d\n",
+ addr, peer->gtsm_hops);
+ }
+ }
+
+ /* disable-connected-check */
+ if (peergroup_flag_check(peer, PEER_FLAG_DISABLE_CONNECTED_CHECK))
+ vty_out(vty, " neighbor %s disable-connected-check\n", addr);
+
+ /* link-bw-encoding-ieee */
+ if (peergroup_flag_check(peer, PEER_FLAG_DISABLE_LINK_BW_ENCODING_IEEE))
+ vty_out(vty, " neighbor %s disable-link-bw-encoding-ieee\n",
+ addr);
+
+ /* extended-optional-parameters */
+ if (peergroup_flag_check(peer, PEER_FLAG_EXTENDED_OPT_PARAMS))
+ vty_out(vty, " neighbor %s extended-optional-parameters\n",
+ addr);
+
+ /* enforce-first-as */
+ if (peergroup_flag_check(peer, PEER_FLAG_ENFORCE_FIRST_AS))
+ vty_out(vty, " neighbor %s enforce-first-as\n", addr);
+
+ /* update-source */
+ if (peergroup_flag_check(peer, PEER_FLAG_UPDATE_SOURCE)) {
+ if (peer->update_source)
+ vty_out(vty, " neighbor %s update-source %pSU\n", addr,
+ peer->update_source);
+ else if (peer->update_if)
+ vty_out(vty, " neighbor %s update-source %s\n", addr,
+ peer->update_if);
+ }
+
+ /* advertisement-interval */
+ if (peergroup_flag_check(peer, PEER_FLAG_ROUTEADV))
+ vty_out(vty, " neighbor %s advertisement-interval %u\n", addr,
+ peer->routeadv);
+
+ /* timers */
+ if (peergroup_flag_check(peer, PEER_FLAG_TIMER))
+ vty_out(vty, " neighbor %s timers %u %u\n", addr,
+ peer->keepalive, peer->holdtime);
+
+ /* timers connect */
+ if (peergroup_flag_check(peer, PEER_FLAG_TIMER_CONNECT))
+ vty_out(vty, " neighbor %s timers connect %u\n", addr,
+ peer->connect);
+ /* need special-case handling for changed default values due to
+ * config profile / version (because there is no "timers bgp connect"
+ * command, we need to save this per-peer :/)
+ */
+ else if (!peer_group_active(peer) && !peer->connect &&
+ peer->bgp->default_connect_retry != SAVE_BGP_CONNECT_RETRY)
+ vty_out(vty, " neighbor %s timers connect %u\n", addr,
+ peer->bgp->default_connect_retry);
+
+ /* timers delayopen */
+ if (peergroup_flag_check(peer, PEER_FLAG_TIMER_DELAYOPEN))
+ vty_out(vty, " neighbor %s timers delayopen %u\n", addr,
+ peer->delayopen);
+ /* Save config even though flag is not set if default values have been
+ * changed
+ */
+ else if (!peer_group_active(peer) && !peer->delayopen
+ && peer->bgp->default_delayopen != BGP_DEFAULT_DELAYOPEN)
+ vty_out(vty, " neighbor %s timers delayopen %u\n", addr,
+ peer->bgp->default_delayopen);
+
+ /* capability dynamic */
+ if (peergroup_flag_check(peer, PEER_FLAG_DYNAMIC_CAPABILITY))
+ vty_out(vty, " neighbor %s capability dynamic\n", addr);
+
+ /* capability extended-nexthop */
+ if (peergroup_flag_check(peer, PEER_FLAG_CAPABILITY_ENHE)) {
+ if (CHECK_FLAG(peer->flags_invert, PEER_FLAG_CAPABILITY_ENHE) &&
+ !peer->conf_if)
+ vty_out(vty,
+ " no neighbor %s capability extended-nexthop\n",
+ addr);
+ else if (!peer->conf_if)
+ vty_out(vty,
+ " neighbor %s capability extended-nexthop\n",
+ addr);
+ }
+
+ /* dont-capability-negotiation */
+ if (peergroup_flag_check(peer, PEER_FLAG_DONT_CAPABILITY))
+ vty_out(vty, " neighbor %s dont-capability-negotiate\n", addr);
+
+ /* override-capability */
+ if (peergroup_flag_check(peer, PEER_FLAG_OVERRIDE_CAPABILITY))
+ vty_out(vty, " neighbor %s override-capability\n", addr);
+
+ /* strict-capability-match */
+ if (peergroup_flag_check(peer, PEER_FLAG_STRICT_CAP_MATCH))
+ vty_out(vty, " neighbor %s strict-capability-match\n", addr);
+
+ /* Sender side AS path loop detection. */
+ if (peer->as_path_loop_detection)
+ vty_out(vty, " neighbor %s sender-as-path-loop-detection\n",
+ addr);
+
+ if (!CHECK_FLAG(peer->peer_gr_new_status_flag,
+ PEER_GRACEFUL_RESTART_NEW_STATE_INHERIT)) {
+
+ if (CHECK_FLAG(peer->peer_gr_new_status_flag,
+ PEER_GRACEFUL_RESTART_NEW_STATE_HELPER)) {
+ vty_out(vty,
+ " neighbor %s graceful-restart-helper\n", addr);
+ } else if (CHECK_FLAG(
+ peer->peer_gr_new_status_flag,
+ PEER_GRACEFUL_RESTART_NEW_STATE_RESTART)) {
+ vty_out(vty,
+ " neighbor %s graceful-restart\n", addr);
+ } else if (
+ (!(CHECK_FLAG(peer->peer_gr_new_status_flag,
+ PEER_GRACEFUL_RESTART_NEW_STATE_HELPER))
+ && !(CHECK_FLAG(
+ peer->peer_gr_new_status_flag,
+ PEER_GRACEFUL_RESTART_NEW_STATE_RESTART)))) {
+ vty_out(vty, " neighbor %s graceful-restart-disable\n",
+ addr);
+ }
+ }
+}
+
+/* BGP peer configuration display function. */
+static void bgp_config_write_peer_af(struct vty *vty, struct bgp *bgp,
+ struct peer *peer, afi_t afi, safi_t safi)
+{
+ struct peer *g_peer = NULL;
+ char *addr;
+ bool flag_scomm, flag_secomm, flag_slcomm;
+
+ /* Skip dynamic neighbors. */
+ if (peer_dynamic_neighbor(peer))
+ return;
+
+ if (peer->conf_if)
+ addr = peer->conf_if;
+ else
+ addr = peer->host;
+
+ /************************************
+ ****** Per AF to the neighbor ******
+ ************************************/
+ if (peer_group_active(peer)) {
+ g_peer = peer->group->conf;
+
+ /* If the peer-group is active but peer is not, print a 'no
+ * activate' */
+ if (g_peer->afc[afi][safi] && !peer->afc[afi][safi]) {
+ vty_out(vty, " no neighbor %s activate\n", addr);
+ }
+
+ /* If the peer-group is not active but peer is, print an
+ 'activate' */
+ else if (!g_peer->afc[afi][safi] && peer->afc[afi][safi]) {
+ vty_out(vty, " neighbor %s activate\n", addr);
+ }
+ } else {
+ if (peer->afc[afi][safi]) {
+ if (safi == SAFI_ENCAP)
+ vty_out(vty, " neighbor %s activate\n", addr);
+ else if (!bgp->default_af[afi][safi])
+ vty_out(vty, " neighbor %s activate\n", addr);
+ } else {
+ if (bgp->default_af[afi][safi])
+ vty_out(vty, " no neighbor %s activate\n",
+ addr);
+ }
+ }
+
+ /* addpath TX knobs */
+ if (peergroup_af_addpath_check(peer, afi, safi)) {
+ switch (peer->addpath_type[afi][safi]) {
+ case BGP_ADDPATH_ALL:
+ vty_out(vty, " neighbor %s addpath-tx-all-paths\n",
+ addr);
+ break;
+ case BGP_ADDPATH_BEST_PER_AS:
+ vty_out(vty,
+ " neighbor %s addpath-tx-bestpath-per-AS\n",
+ addr);
+ break;
+ case BGP_ADDPATH_MAX:
+ case BGP_ADDPATH_NONE:
+ break;
+ }
+ }
+
+ if (CHECK_FLAG(peer->af_flags[afi][safi], PEER_FLAG_DISABLE_ADDPATH_RX))
+ vty_out(vty, " neighbor %s disable-addpath-rx\n", addr);
+
+ /* ORF capability. */
+ if (peergroup_af_flag_check(peer, afi, safi, PEER_FLAG_ORF_PREFIX_SM)
+ || peergroup_af_flag_check(peer, afi, safi,
+ PEER_FLAG_ORF_PREFIX_RM)) {
+ vty_out(vty, " neighbor %s capability orf prefix-list", addr);
+
+ if (peergroup_af_flag_check(peer, afi, safi,
+ PEER_FLAG_ORF_PREFIX_SM)
+ && peergroup_af_flag_check(peer, afi, safi,
+ PEER_FLAG_ORF_PREFIX_RM))
+ vty_out(vty, " both");
+ else if (peergroup_af_flag_check(peer, afi, safi,
+ PEER_FLAG_ORF_PREFIX_SM))
+ vty_out(vty, " send");
+ else
+ vty_out(vty, " receive");
+ vty_out(vty, "\n");
+ }
+
+ /* Route reflector client. */
+ if (peergroup_af_flag_check(peer, afi, safi,
+ PEER_FLAG_REFLECTOR_CLIENT)) {
+ vty_out(vty, " neighbor %s route-reflector-client\n", addr);
+ }
+
+ /* next-hop-self force */
+ if (peergroup_af_flag_check(peer, afi, safi,
+ PEER_FLAG_FORCE_NEXTHOP_SELF)) {
+ vty_out(vty, " neighbor %s next-hop-self force\n", addr);
+ }
+
+ /* next-hop-self */
+ if (peergroup_af_flag_check(peer, afi, safi, PEER_FLAG_NEXTHOP_SELF)) {
+ vty_out(vty, " neighbor %s next-hop-self\n", addr);
+ }
+
+ /* remove-private-AS */
+ if (peergroup_af_flag_check(peer, afi, safi,
+ PEER_FLAG_REMOVE_PRIVATE_AS_ALL_REPLACE)) {
+ vty_out(vty, " neighbor %s remove-private-AS all replace-AS\n",
+ addr);
+ }
+
+ else if (peergroup_af_flag_check(peer, afi, safi,
+ PEER_FLAG_REMOVE_PRIVATE_AS_REPLACE)) {
+ vty_out(vty, " neighbor %s remove-private-AS replace-AS\n",
+ addr);
+ }
+
+ else if (peergroup_af_flag_check(peer, afi, safi,
+ PEER_FLAG_REMOVE_PRIVATE_AS_ALL)) {
+ vty_out(vty, " neighbor %s remove-private-AS all\n", addr);
+ }
+
+ else if (peergroup_af_flag_check(peer, afi, safi,
+ PEER_FLAG_REMOVE_PRIVATE_AS)) {
+ vty_out(vty, " neighbor %s remove-private-AS\n", addr);
+ }
+
+ /* as-override */
+ if (peergroup_af_flag_check(peer, afi, safi, PEER_FLAG_AS_OVERRIDE)) {
+ vty_out(vty, " neighbor %s as-override\n", addr);
+ }
+
+ /* send-community print. */
+ flag_scomm = peergroup_af_flag_check(peer, afi, safi,
+ PEER_FLAG_SEND_COMMUNITY);
+ flag_secomm = peergroup_af_flag_check(peer, afi, safi,
+ PEER_FLAG_SEND_EXT_COMMUNITY);
+ flag_slcomm = peergroup_af_flag_check(peer, afi, safi,
+ PEER_FLAG_SEND_LARGE_COMMUNITY);
+
+ if (flag_scomm && flag_secomm && flag_slcomm) {
+ vty_out(vty, " no neighbor %s send-community all\n", addr);
+ } else {
+ if (flag_scomm)
+ vty_out(vty, " no neighbor %s send-community\n", addr);
+ if (flag_secomm)
+ vty_out(vty,
+ " no neighbor %s send-community extended\n",
+ addr);
+
+ if (flag_slcomm)
+ vty_out(vty, " no neighbor %s send-community large\n",
+ addr);
+ }
+
+ /* Default information */
+ if (peergroup_af_flag_check(peer, afi, safi,
+ PEER_FLAG_DEFAULT_ORIGINATE)) {
+ vty_out(vty, " neighbor %s default-originate", addr);
+
+ if (peer->default_rmap[afi][safi].name)
+ vty_out(vty, " route-map %s",
+ peer->default_rmap[afi][safi].name);
+
+ vty_out(vty, "\n");
+ }
+
+ /* Soft reconfiguration inbound. */
+ if (peergroup_af_flag_check(peer, afi, safi, PEER_FLAG_SOFT_RECONFIG)) {
+ vty_out(vty, " neighbor %s soft-reconfiguration inbound\n",
+ addr);
+ }
+
+ /* maximum-prefix. */
+ if (peergroup_af_flag_check(peer, afi, safi, PEER_FLAG_MAX_PREFIX)) {
+ vty_out(vty, " neighbor %s maximum-prefix %u", addr,
+ peer->pmax[afi][safi]);
+
+ if (peer->pmax_threshold[afi][safi]
+ != MAXIMUM_PREFIX_THRESHOLD_DEFAULT)
+ vty_out(vty, " %u", peer->pmax_threshold[afi][safi]);
+ if (peer_af_flag_check(peer, afi, safi,
+ PEER_FLAG_MAX_PREFIX_WARNING))
+ vty_out(vty, " warning-only");
+ if (peer->pmax_restart[afi][safi])
+ vty_out(vty, " restart %u",
+ peer->pmax_restart[afi][safi]);
+ if (peer_af_flag_check(peer, afi, safi,
+ PEER_FLAG_MAX_PREFIX_FORCE))
+ vty_out(vty, " force");
+
+ vty_out(vty, "\n");
+ }
+
+ /* maximum-prefix-out */
+ if (peergroup_af_flag_check(peer, afi, safi, PEER_FLAG_MAX_PREFIX_OUT))
+ vty_out(vty, " neighbor %s maximum-prefix-out %u\n",
+ addr, peer->pmax_out[afi][safi]);
+
+ /* Route server client. */
+ if (peergroup_af_flag_check(peer, afi, safi,
+ PEER_FLAG_RSERVER_CLIENT)) {
+ vty_out(vty, " neighbor %s route-server-client\n", addr);
+ }
+
+ /* Nexthop-local unchanged. */
+ if (peergroup_af_flag_check(peer, afi, safi,
+ PEER_FLAG_NEXTHOP_LOCAL_UNCHANGED)) {
+ vty_out(vty, " neighbor %s nexthop-local unchanged\n", addr);
+ }
+
+ /* allowas-in <1-10> */
+ if (peergroup_af_flag_check(peer, afi, safi, PEER_FLAG_ALLOWAS_IN)) {
+ if (peer_af_flag_check(peer, afi, safi,
+ PEER_FLAG_ALLOWAS_IN_ORIGIN)) {
+ vty_out(vty, " neighbor %s allowas-in origin\n", addr);
+ } else if (peer->allowas_in[afi][safi] == 3) {
+ vty_out(vty, " neighbor %s allowas-in\n", addr);
+ } else {
+ vty_out(vty, " neighbor %s allowas-in %d\n", addr,
+ peer->allowas_in[afi][safi]);
+ }
+ }
+
+ /* soo */
+ if (peergroup_af_flag_check(peer, afi, safi, PEER_FLAG_SOO)) {
+ char *soo_str = ecommunity_ecom2str(
+ peer->soo[afi][safi], ECOMMUNITY_FORMAT_ROUTE_MAP, 0);
+
+ vty_out(vty, " neighbor %s soo %s\n", addr, soo_str);
+ XFREE(MTYPE_ECOMMUNITY_STR, soo_str);
+ }
+
+ /* weight */
+ if (peergroup_af_flag_check(peer, afi, safi, PEER_FLAG_WEIGHT))
+ vty_out(vty, " neighbor %s weight %lu\n", addr,
+ peer->weight[afi][safi]);
+
+ /* Filter. */
+ bgp_config_write_filter(vty, peer, afi, safi);
+
+ /* atribute-unchanged. */
+ if (peer_af_flag_check(peer, afi, safi, PEER_FLAG_AS_PATH_UNCHANGED)
+ || (safi != SAFI_EVPN
+ && peer_af_flag_check(peer, afi, safi,
+ PEER_FLAG_NEXTHOP_UNCHANGED))
+ || peer_af_flag_check(peer, afi, safi, PEER_FLAG_MED_UNCHANGED)) {
+
+ if (!peer_group_active(peer)
+ || peergroup_af_flag_check(peer, afi, safi,
+ PEER_FLAG_AS_PATH_UNCHANGED)
+ || peergroup_af_flag_check(peer, afi, safi,
+ PEER_FLAG_NEXTHOP_UNCHANGED)
+ || peergroup_af_flag_check(peer, afi, safi,
+ PEER_FLAG_MED_UNCHANGED)) {
+
+ vty_out(vty,
+ " neighbor %s attribute-unchanged%s%s%s\n",
+ addr,
+ peer_af_flag_check(peer, afi, safi,
+ PEER_FLAG_AS_PATH_UNCHANGED)
+ ? " as-path"
+ : "",
+ peer_af_flag_check(peer, afi, safi,
+ PEER_FLAG_NEXTHOP_UNCHANGED)
+ ? " next-hop"
+ : "",
+ peer_af_flag_check(peer, afi, safi,
+ PEER_FLAG_MED_UNCHANGED)
+ ? " med"
+ : "");
+ }
+ }
+}
+
+static void bgp_vpn_config_write(struct vty *vty, struct bgp *bgp, afi_t afi,
+ safi_t safi)
+{
+ if (!CHECK_FLAG(bgp->af_flags[afi][safi],
+ BGP_VPNVX_RETAIN_ROUTE_TARGET_ALL))
+ vty_out(vty, " no bgp retain route-target all\n");
+}
+
+/* Address family based peer configuration display. */
+static void bgp_config_write_family(struct vty *vty, struct bgp *bgp, afi_t afi,
+ safi_t safi)
+{
+ struct peer *peer;
+ struct peer_group *group;
+ struct listnode *node, *nnode;
+
+
+ vty_frame(vty, " !\n address-family ");
+ if (afi == AFI_IP) {
+ if (safi == SAFI_UNICAST)
+ vty_frame(vty, "ipv4 unicast");
+ else if (safi == SAFI_LABELED_UNICAST)
+ vty_frame(vty, "ipv4 labeled-unicast");
+ else if (safi == SAFI_MULTICAST)
+ vty_frame(vty, "ipv4 multicast");
+ else if (safi == SAFI_MPLS_VPN)
+ vty_frame(vty, "ipv4 vpn");
+ else if (safi == SAFI_ENCAP)
+ vty_frame(vty, "ipv4 encap");
+ else if (safi == SAFI_FLOWSPEC)
+ vty_frame(vty, "ipv4 flowspec");
+ } else if (afi == AFI_IP6) {
+ if (safi == SAFI_UNICAST)
+ vty_frame(vty, "ipv6 unicast");
+ else if (safi == SAFI_LABELED_UNICAST)
+ vty_frame(vty, "ipv6 labeled-unicast");
+ else if (safi == SAFI_MULTICAST)
+ vty_frame(vty, "ipv6 multicast");
+ else if (safi == SAFI_MPLS_VPN)
+ vty_frame(vty, "ipv6 vpn");
+ else if (safi == SAFI_ENCAP)
+ vty_frame(vty, "ipv6 encap");
+ else if (safi == SAFI_FLOWSPEC)
+ vty_frame(vty, "ipv6 flowspec");
+ } else if (afi == AFI_L2VPN) {
+ if (safi == SAFI_EVPN)
+ vty_frame(vty, "l2vpn evpn");
+ }
+ vty_frame(vty, "\n");
+
+ bgp_config_write_distance(vty, bgp, afi, safi);
+
+ bgp_config_write_network(vty, bgp, afi, safi);
+
+ bgp_config_write_redistribute(vty, bgp, afi, safi);
+
+ /* BGP flag dampening. */
+ if (CHECK_FLAG(bgp->af_flags[afi][safi], BGP_CONFIG_DAMPENING))
+ bgp_config_write_damp(vty, afi, safi);
+
+ for (ALL_LIST_ELEMENTS(bgp->group, node, nnode, group))
+ bgp_config_write_peer_af(vty, bgp, group->conf, afi, safi);
+
+ for (ALL_LIST_ELEMENTS(bgp->peer, node, nnode, peer)) {
+ /* Do not display doppelganger peers */
+ if (CHECK_FLAG(peer->flags, PEER_FLAG_CONFIG_NODE))
+ bgp_config_write_peer_af(vty, bgp, peer, afi, safi);
+ }
+
+ bgp_config_write_maxpaths(vty, bgp, afi, safi);
+ bgp_config_write_table_map(vty, bgp, afi, safi);
+
+ if (safi == SAFI_EVPN)
+ bgp_config_write_evpn_info(vty, bgp, afi, safi);
+
+ if (safi == SAFI_FLOWSPEC)
+ bgp_fs_config_write_pbr(vty, bgp, afi, safi);
+
+ if (safi == SAFI_MPLS_VPN)
+ bgp_vpn_config_write(vty, bgp, afi, safi);
+
+ if (safi == SAFI_UNICAST) {
+ bgp_vpn_policy_config_write_afi(vty, bgp, afi);
+ if (CHECK_FLAG(bgp->af_flags[afi][safi],
+ BGP_CONFIG_VRF_TO_MPLSVPN_EXPORT)) {
+
+ vty_out(vty, " export vpn\n");
+ }
+ if (CHECK_FLAG(bgp->af_flags[afi][safi],
+ BGP_CONFIG_MPLSVPN_TO_VRF_IMPORT)) {
+
+ vty_out(vty, " import vpn\n");
+ }
+ if (CHECK_FLAG(bgp->af_flags[afi][safi],
+ BGP_CONFIG_VRF_TO_VRF_IMPORT)) {
+ char *name;
+
+ for (ALL_LIST_ELEMENTS_RO(
+ bgp->vpn_policy[afi].import_vrf, node,
+ name))
+ vty_out(vty, " import vrf %s\n", name);
+ }
+ }
+
+ vty_endframe(vty, " exit-address-family\n");
+}
+
+int bgp_config_write(struct vty *vty)
+{
+ struct bgp *bgp;
+ struct peer_group *group;
+ struct peer *peer;
+ struct listnode *node, *nnode;
+ struct listnode *mnode, *mnnode;
+ afi_t afi;
+ safi_t safi;
+
+ if (bm->rmap_update_timer != RMAP_DEFAULT_UPDATE_TIMER)
+ vty_out(vty, "bgp route-map delay-timer %u\n",
+ bm->rmap_update_timer);
+
+ if (bm->v_update_delay != BGP_UPDATE_DELAY_DEF) {
+ vty_out(vty, "bgp update-delay %d", bm->v_update_delay);
+ if (bm->v_update_delay != bm->v_establish_wait)
+ vty_out(vty, " %d", bm->v_establish_wait);
+ vty_out(vty, "\n");
+ }
+
+ if (bm->wait_for_fib)
+ vty_out(vty, "bgp suppress-fib-pending\n");
+
+ if (CHECK_FLAG(bm->flags, BM_FLAG_GRACEFUL_SHUTDOWN))
+ vty_out(vty, "bgp graceful-shutdown\n");
+
+ /* No-RIB (Zebra) option flag configuration */
+ if (bgp_option_check(BGP_OPT_NO_FIB))
+ vty_out(vty, "bgp no-rib\n");
+
+ if (CHECK_FLAG(bm->flags, BM_FLAG_SEND_EXTRA_DATA_TO_ZEBRA))
+ vty_out(vty, "bgp send-extra-data zebra\n");
+
+ /* BGP session DSCP value */
+ if (bm->tcp_dscp != IPTOS_PREC_INTERNETCONTROL)
+ vty_out(vty, "bgp session-dscp %u\n", bm->tcp_dscp >> 2);
+
+ /* BGP configuration. */
+ for (ALL_LIST_ELEMENTS(bm->bgp, mnode, mnnode, bgp)) {
+
+ /* skip all auto created vrf as they dont have user config */
+ if (CHECK_FLAG(bgp->vrf_flags, BGP_VRF_AUTO))
+ continue;
+
+ /* Router bgp ASN */
+ vty_out(vty, "router bgp %u", bgp->as);
+
+ if (bgp->name)
+ vty_out(vty, " %s %s",
+ (bgp->inst_type == BGP_INSTANCE_TYPE_VIEW)
+ ? "view" : "vrf", bgp->name);
+ vty_out(vty, "\n");
+
+ /* BGP fast-external-failover. */
+ if (CHECK_FLAG(bgp->flags, BGP_FLAG_NO_FAST_EXT_FAILOVER))
+ vty_out(vty, " no bgp fast-external-failover\n");
+
+ /* BGP router ID. */
+ if (bgp->router_id_static.s_addr != INADDR_ANY)
+ vty_out(vty, " bgp router-id %pI4\n",
+ &bgp->router_id_static);
+
+ /* Suppress fib pending */
+ if (CHECK_FLAG(bgp->flags, BGP_FLAG_SUPPRESS_FIB_PENDING))
+ vty_out(vty, " bgp suppress-fib-pending\n");
+
+ /* BGP log-neighbor-changes. */
+ if (!!CHECK_FLAG(bgp->flags, BGP_FLAG_LOG_NEIGHBOR_CHANGES)
+ != SAVE_BGP_LOG_NEIGHBOR_CHANGES)
+ vty_out(vty, " %sbgp log-neighbor-changes\n",
+ CHECK_FLAG(bgp->flags,
+ BGP_FLAG_LOG_NEIGHBOR_CHANGES)
+ ? ""
+ : "no ");
+
+ /* BGP configuration. */
+ if (CHECK_FLAG(bgp->flags, BGP_FLAG_ALWAYS_COMPARE_MED))
+ vty_out(vty, " bgp always-compare-med\n");
+
+ /* RFC8212 default eBGP policy. */
+ if (!!CHECK_FLAG(bgp->flags, BGP_FLAG_EBGP_REQUIRES_POLICY)
+ != SAVE_BGP_EBGP_REQUIRES_POLICY)
+ vty_out(vty, " %sbgp ebgp-requires-policy\n",
+ CHECK_FLAG(bgp->flags,
+ BGP_FLAG_EBGP_REQUIRES_POLICY)
+ ? ""
+ : "no ");
+
+ /* draft-ietf-idr-deprecate-as-set-confed-set */
+ if (bgp->reject_as_sets)
+ vty_out(vty, " bgp reject-as-sets\n");
+
+ /* Suppress duplicate updates if the route actually not changed
+ */
+ if (!!CHECK_FLAG(bgp->flags, BGP_FLAG_SUPPRESS_DUPLICATES)
+ != SAVE_BGP_SUPPRESS_DUPLICATES)
+ vty_out(vty, " %sbgp suppress-duplicates\n",
+ CHECK_FLAG(bgp->flags,
+ BGP_FLAG_SUPPRESS_DUPLICATES)
+ ? ""
+ : "no ");
+
+ /* Send Hard Reset CEASE Notification for 'Administrative Reset'
+ */
+ if (!!CHECK_FLAG(bgp->flags, BGP_FLAG_HARD_ADMIN_RESET) !=
+ SAVE_BGP_HARD_ADMIN_RESET)
+ vty_out(vty, " %sbgp hard-administrative-reset\n",
+ CHECK_FLAG(bgp->flags,
+ BGP_FLAG_HARD_ADMIN_RESET)
+ ? ""
+ : "no ");
+
+ /* BGP default <afi>-<safi> */
+ FOREACH_AFI_SAFI (afi, safi) {
+ if (afi == AFI_IP && safi == SAFI_UNICAST) {
+ if (!bgp->default_af[afi][safi])
+ vty_out(vty, " no bgp default %s\n",
+ get_bgp_default_af_flag(afi,
+ safi));
+ } else if (bgp->default_af[afi][safi])
+ vty_out(vty, " bgp default %s\n",
+ get_bgp_default_af_flag(afi, safi));
+ }
+
+ /* BGP default local-preference. */
+ if (bgp->default_local_pref != BGP_DEFAULT_LOCAL_PREF)
+ vty_out(vty, " bgp default local-preference %u\n",
+ bgp->default_local_pref);
+
+ /* BGP default show-hostname */
+ if (!!CHECK_FLAG(bgp->flags, BGP_FLAG_SHOW_HOSTNAME)
+ != SAVE_BGP_SHOW_HOSTNAME)
+ vty_out(vty, " %sbgp default show-hostname\n",
+ CHECK_FLAG(bgp->flags, BGP_FLAG_SHOW_HOSTNAME)
+ ? ""
+ : "no ");
+
+ /* BGP default show-nexthop-hostname */
+ if (!!CHECK_FLAG(bgp->flags, BGP_FLAG_SHOW_NEXTHOP_HOSTNAME)
+ != SAVE_BGP_SHOW_HOSTNAME)
+ vty_out(vty, " %sbgp default show-nexthop-hostname\n",
+ CHECK_FLAG(bgp->flags,
+ BGP_FLAG_SHOW_NEXTHOP_HOSTNAME)
+ ? ""
+ : "no ");
+
+ /* BGP default subgroup-pkt-queue-max. */
+ if (bgp->default_subgroup_pkt_queue_max
+ != BGP_DEFAULT_SUBGROUP_PKT_QUEUE_MAX)
+ vty_out(vty, " bgp default subgroup-pkt-queue-max %u\n",
+ bgp->default_subgroup_pkt_queue_max);
+
+ /* BGP client-to-client reflection. */
+ if (CHECK_FLAG(bgp->flags, BGP_FLAG_NO_CLIENT_TO_CLIENT))
+ vty_out(vty, " no bgp client-to-client reflection\n");
+
+ /* BGP cluster ID. */
+ if (CHECK_FLAG(bgp->config, BGP_CONFIG_CLUSTER_ID))
+ vty_out(vty, " bgp cluster-id %pI4\n",
+ &bgp->cluster_id);
+
+ /* Disable ebgp connected nexthop check */
+ if (CHECK_FLAG(bgp->flags, BGP_FLAG_DISABLE_NH_CONNECTED_CHK))
+ vty_out(vty,
+ " bgp disable-ebgp-connected-route-check\n");
+
+ /* Confederation identifier*/
+ if (CHECK_FLAG(bgp->config, BGP_CONFIG_CONFEDERATION))
+ vty_out(vty, " bgp confederation identifier %u\n",
+ bgp->confed_id);
+
+ /* Confederation peer */
+ if (bgp->confed_peers_cnt > 0) {
+ int i;
+
+ vty_out(vty, " bgp confederation peers");
+
+ for (i = 0; i < bgp->confed_peers_cnt; i++)
+ vty_out(vty, " %u", bgp->confed_peers[i]);
+
+ vty_out(vty, "\n");
+ }
+
+ /* BGP deterministic-med. */
+ if (!!CHECK_FLAG(bgp->flags, BGP_FLAG_DETERMINISTIC_MED)
+ != SAVE_BGP_DETERMINISTIC_MED)
+ vty_out(vty, " %sbgp deterministic-med\n",
+ CHECK_FLAG(bgp->flags,
+ BGP_FLAG_DETERMINISTIC_MED)
+ ? ""
+ : "no ");
+
+ /* BGP update-delay. */
+ bgp_config_write_update_delay(vty, bgp);
+
+ if (bgp->v_maxmed_onstartup
+ != BGP_MAXMED_ONSTARTUP_UNCONFIGURED) {
+ vty_out(vty, " bgp max-med on-startup %u",
+ bgp->v_maxmed_onstartup);
+ if (bgp->maxmed_onstartup_value
+ != BGP_MAXMED_VALUE_DEFAULT)
+ vty_out(vty, " %u",
+ bgp->maxmed_onstartup_value);
+ vty_out(vty, "\n");
+ }
+ if (bgp->v_maxmed_admin != BGP_MAXMED_ADMIN_UNCONFIGURED) {
+ vty_out(vty, " bgp max-med administrative");
+ if (bgp->maxmed_admin_value != BGP_MAXMED_VALUE_DEFAULT)
+ vty_out(vty, " %u", bgp->maxmed_admin_value);
+ vty_out(vty, "\n");
+ }
+
+ /* write quanta */
+ bgp_config_write_wpkt_quanta(vty, bgp);
+ /* read quanta */
+ bgp_config_write_rpkt_quanta(vty, bgp);
+
+ /* coalesce time */
+ bgp_config_write_coalesce_time(vty, bgp);
+
+ /* BGP per-instance graceful-shutdown */
+ /* BGP-wide settings and per-instance settings are mutually
+ * exclusive.
+ */
+ if (!CHECK_FLAG(bm->flags, BM_FLAG_GRACEFUL_SHUTDOWN))
+ if (CHECK_FLAG(bgp->flags, BGP_FLAG_GRACEFUL_SHUTDOWN))
+ vty_out(vty, " bgp graceful-shutdown\n");
+
+ /* Long-lived Graceful Restart */
+ if (bgp->llgr_stale_time != BGP_DEFAULT_LLGR_STALE_TIME)
+ vty_out(vty,
+ " bgp long-lived-graceful-restart stale-time %u\n",
+ bgp->llgr_stale_time);
+
+ /* BGP graceful-restart. */
+ if (bgp->stalepath_time != BGP_DEFAULT_STALEPATH_TIME)
+ vty_out(vty,
+ " bgp graceful-restart stalepath-time %u\n",
+ bgp->stalepath_time);
+
+ if (bgp->restart_time != BGP_DEFAULT_RESTART_TIME)
+ vty_out(vty, " bgp graceful-restart restart-time %u\n",
+ bgp->restart_time);
+
+ if (!!CHECK_FLAG(bgp->flags, BGP_FLAG_GRACEFUL_NOTIFICATION) !=
+ SAVE_BGP_GRACEFUL_NOTIFICATION)
+ vty_out(vty, " %sbgp graceful-restart notification\n",
+ CHECK_FLAG(bgp->flags,
+ BGP_FLAG_GRACEFUL_NOTIFICATION)
+ ? ""
+ : "no ");
+
+ if (bgp->select_defer_time != BGP_DEFAULT_SELECT_DEFERRAL_TIME)
+ vty_out(vty,
+ " bgp graceful-restart select-defer-time %u\n",
+ bgp->select_defer_time);
+
+ if (bgp_global_gr_mode_get(bgp) == GLOBAL_GR)
+ vty_out(vty, " bgp graceful-restart\n");
+
+ if (bgp_global_gr_mode_get(bgp) == GLOBAL_DISABLE)
+ vty_out(vty, " bgp graceful-restart-disable\n");
+
+ /* BGP graceful-restart Preserve State F bit. */
+ if (CHECK_FLAG(bgp->flags, BGP_FLAG_GR_PRESERVE_FWD))
+ vty_out(vty,
+ " bgp graceful-restart preserve-fw-state\n");
+
+ /* Stale timer for RIB */
+ if (bgp->rib_stale_time != BGP_DEFAULT_RIB_STALE_TIME)
+ vty_out(vty,
+ " bgp graceful-restart rib-stale-time %u\n",
+ bgp->rib_stale_time);
+
+ /* BGP bestpath method. */
+ if (CHECK_FLAG(bgp->flags, BGP_FLAG_ASPATH_IGNORE))
+ vty_out(vty, " bgp bestpath as-path ignore\n");
+ if (CHECK_FLAG(bgp->flags, BGP_FLAG_ASPATH_CONFED))
+ vty_out(vty, " bgp bestpath as-path confed\n");
+
+ if (CHECK_FLAG(bgp->flags, BGP_FLAG_ASPATH_MULTIPATH_RELAX)) {
+ if (CHECK_FLAG(bgp->flags,
+ BGP_FLAG_MULTIPATH_RELAX_AS_SET)) {
+ vty_out(vty,
+ " bgp bestpath as-path multipath-relax as-set\n");
+ } else {
+ vty_out(vty,
+ " bgp bestpath as-path multipath-relax\n");
+ }
+ }
+
+ if (CHECK_FLAG(bgp->flags, BGP_FLAG_RR_ALLOW_OUTBOUND_POLICY)) {
+ vty_out(vty,
+ " bgp route-reflector allow-outbound-policy\n");
+ }
+ if (CHECK_FLAG(bgp->flags, BGP_FLAG_COMPARE_ROUTER_ID))
+ vty_out(vty, " bgp bestpath compare-routerid\n");
+ if (CHECK_FLAG(bgp->flags, BGP_FLAG_MED_CONFED)
+ || CHECK_FLAG(bgp->flags, BGP_FLAG_MED_MISSING_AS_WORST)) {
+ vty_out(vty, " bgp bestpath med");
+ if (CHECK_FLAG(bgp->flags, BGP_FLAG_MED_CONFED))
+ vty_out(vty, " confed");
+ if (CHECK_FLAG(bgp->flags,
+ BGP_FLAG_MED_MISSING_AS_WORST))
+ vty_out(vty, " missing-as-worst");
+ vty_out(vty, "\n");
+ }
+
+ if (CHECK_FLAG(bgp->flags, BGP_FLAG_PEERTYPE_MULTIPATH_RELAX))
+ vty_out(vty,
+ " bgp bestpath peer-type multipath-relax\n");
+
+ /* Link bandwidth handling. */
+ if (bgp->lb_handling == BGP_LINK_BW_IGNORE_BW)
+ vty_out(vty, " bgp bestpath bandwidth ignore\n");
+ else if (bgp->lb_handling == BGP_LINK_BW_SKIP_MISSING)
+ vty_out(vty, " bgp bestpath bandwidth skip-missing\n");
+ else if (bgp->lb_handling == BGP_LINK_BW_DEFWT_4_MISSING)
+ vty_out(vty, " bgp bestpath bandwidth default-weight-for-missing\n");
+
+ /* BGP network import check. */
+ if (!!CHECK_FLAG(bgp->flags, BGP_FLAG_IMPORT_CHECK)
+ != SAVE_BGP_IMPORT_CHECK)
+ vty_out(vty, " %sbgp network import-check\n",
+ CHECK_FLAG(bgp->flags, BGP_FLAG_IMPORT_CHECK)
+ ? ""
+ : "no ");
+
+ /* BGP timers configuration. */
+ if (bgp->default_keepalive != SAVE_BGP_KEEPALIVE
+ || bgp->default_holdtime != SAVE_BGP_HOLDTIME)
+ vty_out(vty, " timers bgp %u %u\n",
+ bgp->default_keepalive, bgp->default_holdtime);
+
+ /* BGP minimum holdtime configuration. */
+ if (bgp->default_min_holdtime != SAVE_BGP_HOLDTIME
+ && bgp->default_min_holdtime != 0)
+ vty_out(vty, " bgp minimum-holdtime %u\n",
+ bgp->default_min_holdtime);
+
+ /* Conditional advertisement timer configuration */
+ if (bgp->condition_check_period
+ != DEFAULT_CONDITIONAL_ROUTES_POLL_TIME)
+ vty_out(vty,
+ " bgp conditional-advertisement timer %u\n",
+ bgp->condition_check_period);
+
+ /* peer-group */
+ for (ALL_LIST_ELEMENTS(bgp->group, node, nnode, group)) {
+ bgp_config_write_peer_global(vty, bgp, group->conf);
+ }
+
+ /* Normal neighbor configuration. */
+ for (ALL_LIST_ELEMENTS(bgp->peer, node, nnode, peer)) {
+ if (CHECK_FLAG(peer->flags, PEER_FLAG_CONFIG_NODE))
+ bgp_config_write_peer_global(vty, bgp, peer);
+ }
+
+ /* listen range and limit for dynamic BGP neighbors */
+ bgp_config_write_listen(vty, bgp);
+
+ /*
+ * BGP default autoshutdown neighbors
+ *
+ * This must be placed after any peer and peer-group
+ * configuration, to avoid setting all peers to shutdown after
+ * a daemon restart, which is undesired behavior. (see #2286)
+ */
+ if (bgp->autoshutdown)
+ vty_out(vty, " bgp default shutdown\n");
+
+ /* BGP instance administrative shutdown */
+ if (CHECK_FLAG(bgp->flags, BGP_FLAG_SHUTDOWN))
+ vty_out(vty, " bgp shutdown\n");
+
+ if (bgp->allow_martian)
+ vty_out(vty, " bgp allow-martian-nexthop\n");
+
+ if (bgp->fast_convergence)
+ vty_out(vty, " bgp fast-convergence\n");
+
+ if (bgp->srv6_enabled) {
+ vty_frame(vty, " !\n segment-routing srv6\n");
+ if (strlen(bgp->srv6_locator_name))
+ vty_out(vty, " locator %s\n",
+ bgp->srv6_locator_name);
+ vty_endframe(vty, " exit\n");
+ }
+
+
+ /* IPv4 unicast configuration. */
+ bgp_config_write_family(vty, bgp, AFI_IP, SAFI_UNICAST);
+
+ /* IPv4 multicast configuration. */
+ bgp_config_write_family(vty, bgp, AFI_IP, SAFI_MULTICAST);
+
+ /* IPv4 labeled-unicast configuration. */
+ bgp_config_write_family(vty, bgp, AFI_IP, SAFI_LABELED_UNICAST);
+
+ /* IPv4 VPN configuration. */
+ bgp_config_write_family(vty, bgp, AFI_IP, SAFI_MPLS_VPN);
+
+ /* ENCAPv4 configuration. */
+ bgp_config_write_family(vty, bgp, AFI_IP, SAFI_ENCAP);
+
+ /* FLOWSPEC v4 configuration. */
+ bgp_config_write_family(vty, bgp, AFI_IP, SAFI_FLOWSPEC);
+
+ /* IPv6 unicast configuration. */
+ bgp_config_write_family(vty, bgp, AFI_IP6, SAFI_UNICAST);
+
+ /* IPv6 multicast configuration. */
+ bgp_config_write_family(vty, bgp, AFI_IP6, SAFI_MULTICAST);
+
+ /* IPv6 labeled-unicast configuration. */
+ bgp_config_write_family(vty, bgp, AFI_IP6,
+ SAFI_LABELED_UNICAST);
+
+ /* IPv6 VPN configuration. */
+ bgp_config_write_family(vty, bgp, AFI_IP6, SAFI_MPLS_VPN);
+
+ /* ENCAPv6 configuration. */
+ bgp_config_write_family(vty, bgp, AFI_IP6, SAFI_ENCAP);
+
+ /* FLOWSPEC v6 configuration. */
+ bgp_config_write_family(vty, bgp, AFI_IP6, SAFI_FLOWSPEC);
+
+ /* EVPN configuration. */
+ bgp_config_write_family(vty, bgp, AFI_L2VPN, SAFI_EVPN);
+
+ hook_call(bgp_inst_config_write, bgp, vty);
+
+#ifdef ENABLE_BGP_VNC
+ bgp_rfapi_cfg_write(vty, bgp);
+#endif
+
+ vty_out(vty, "exit\n");
+ vty_out(vty, "!\n");
+ }
+ return 0;
+}
+
+
+/* BGP node structure. */
+static struct cmd_node bgp_node = {
+ .name = "bgp",
+ .node = BGP_NODE,
+ .parent_node = CONFIG_NODE,
+ .prompt = "%s(config-router)# ",
+ .config_write = bgp_config_write,
+};
+
+static struct cmd_node bgp_ipv4_unicast_node = {
+ .name = "bgp ipv4 unicast",
+ .node = BGP_IPV4_NODE,
+ .parent_node = BGP_NODE,
+ .prompt = "%s(config-router-af)# ",
+ .no_xpath = true,
+};
+
+static struct cmd_node bgp_ipv4_multicast_node = {
+ .name = "bgp ipv4 multicast",
+ .node = BGP_IPV4M_NODE,
+ .parent_node = BGP_NODE,
+ .prompt = "%s(config-router-af)# ",
+ .no_xpath = true,
+};
+
+static struct cmd_node bgp_ipv4_labeled_unicast_node = {
+ .name = "bgp ipv4 labeled unicast",
+ .node = BGP_IPV4L_NODE,
+ .parent_node = BGP_NODE,
+ .prompt = "%s(config-router-af)# ",
+ .no_xpath = true,
+};
+
+static struct cmd_node bgp_ipv6_unicast_node = {
+ .name = "bgp ipv6 unicast",
+ .node = BGP_IPV6_NODE,
+ .parent_node = BGP_NODE,
+ .prompt = "%s(config-router-af)# ",
+ .no_xpath = true,
+};
+
+static struct cmd_node bgp_ipv6_multicast_node = {
+ .name = "bgp ipv6 multicast",
+ .node = BGP_IPV6M_NODE,
+ .parent_node = BGP_NODE,
+ .prompt = "%s(config-router-af)# ",
+ .no_xpath = true,
+};
+
+static struct cmd_node bgp_ipv6_labeled_unicast_node = {
+ .name = "bgp ipv6 labeled unicast",
+ .node = BGP_IPV6L_NODE,
+ .parent_node = BGP_NODE,
+ .prompt = "%s(config-router-af)# ",
+ .no_xpath = true,
+};
+
+static struct cmd_node bgp_vpnv4_node = {
+ .name = "bgp vpnv4",
+ .node = BGP_VPNV4_NODE,
+ .parent_node = BGP_NODE,
+ .prompt = "%s(config-router-af)# ",
+ .no_xpath = true,
+};
+
+static struct cmd_node bgp_vpnv6_node = {
+ .name = "bgp vpnv6",
+ .node = BGP_VPNV6_NODE,
+ .parent_node = BGP_NODE,
+ .prompt = "%s(config-router-af-vpnv6)# ",
+ .no_xpath = true,
+};
+
+static struct cmd_node bgp_evpn_node = {
+ .name = "bgp evpn",
+ .node = BGP_EVPN_NODE,
+ .parent_node = BGP_NODE,
+ .prompt = "%s(config-router-evpn)# ",
+ .no_xpath = true,
+};
+
+static struct cmd_node bgp_evpn_vni_node = {
+ .name = "bgp evpn vni",
+ .node = BGP_EVPN_VNI_NODE,
+ .parent_node = BGP_EVPN_NODE,
+ .prompt = "%s(config-router-af-vni)# ",
+};
+
+static struct cmd_node bgp_flowspecv4_node = {
+ .name = "bgp ipv4 flowspec",
+ .node = BGP_FLOWSPECV4_NODE,
+ .parent_node = BGP_NODE,
+ .prompt = "%s(config-router-af)# ",
+ .no_xpath = true,
+};
+
+static struct cmd_node bgp_flowspecv6_node = {
+ .name = "bgp ipv6 flowspec",
+ .node = BGP_FLOWSPECV6_NODE,
+ .parent_node = BGP_NODE,
+ .prompt = "%s(config-router-af-vpnv6)# ",
+ .no_xpath = true,
+};
+
+static struct cmd_node bgp_srv6_node = {
+ .name = "bgp srv6",
+ .node = BGP_SRV6_NODE,
+ .parent_node = BGP_NODE,
+ .prompt = "%s(config-router-srv6)# ",
+};
+
+static void community_list_vty(void);
+
+static void bgp_ac_peergroup(vector comps, struct cmd_token *token)
+{
+ struct bgp *bgp;
+ struct peer_group *group;
+ struct listnode *lnbgp, *lnpeer;
+
+ for (ALL_LIST_ELEMENTS_RO(bm->bgp, lnbgp, bgp)) {
+ for (ALL_LIST_ELEMENTS_RO(bgp->group, lnpeer, group))
+ vector_set(comps,
+ XSTRDUP(MTYPE_COMPLETION, group->name));
+ }
+}
+
+static void bgp_ac_peer(vector comps, struct cmd_token *token)
+{
+ struct bgp *bgp;
+ struct peer *peer;
+ struct listnode *lnbgp, *lnpeer;
+
+ for (ALL_LIST_ELEMENTS_RO(bm->bgp, lnbgp, bgp)) {
+ for (ALL_LIST_ELEMENTS_RO(bgp->peer, lnpeer, peer)) {
+ /* only provide suggestions on the appropriate input
+ * token type,
+ * they'll otherwise show up multiple times */
+ enum cmd_token_type match_type;
+ char *name = peer->host;
+
+ if (peer->conf_if) {
+ match_type = VARIABLE_TKN;
+ name = peer->conf_if;
+ } else if (strchr(peer->host, ':'))
+ match_type = IPV6_TKN;
+ else
+ match_type = IPV4_TKN;
+
+ if (token->type != match_type)
+ continue;
+
+ vector_set(comps, XSTRDUP(MTYPE_COMPLETION, name));
+ }
+ }
+}
+
+static void bgp_ac_neighbor(vector comps, struct cmd_token *token)
+{
+ bgp_ac_peer(comps, token);
+
+ if (token->type == VARIABLE_TKN)
+ bgp_ac_peergroup(comps, token);
+}
+
+static const struct cmd_variable_handler bgp_var_neighbor[] = {
+ {.varname = "neighbor", .completions = bgp_ac_neighbor},
+ {.varname = "neighbors", .completions = bgp_ac_neighbor},
+ {.varname = "peer", .completions = bgp_ac_neighbor},
+ {.completions = NULL}};
+
+static const struct cmd_variable_handler bgp_var_peergroup[] = {
+ {.tokenname = "PGNAME", .completions = bgp_ac_peergroup},
+ {.completions = NULL} };
+
+DEFINE_HOOK(bgp_config_end, (struct bgp *bgp), (bgp));
+
+static struct thread *t_bgp_cfg;
+
+bool bgp_config_inprocess(void)
+{
+ return thread_is_scheduled(t_bgp_cfg);
+}
+
+static void bgp_config_finish(struct thread *t)
+{
+ struct listnode *node;
+ struct bgp *bgp;
+
+ for (ALL_LIST_ELEMENTS_RO(bm->bgp, node, bgp))
+ hook_call(bgp_config_end, bgp);
+}
+
+static void bgp_config_start(void)
+{
+#define BGP_PRE_CONFIG_MAX_WAIT_SECONDS 600
+ THREAD_OFF(t_bgp_cfg);
+ thread_add_timer(bm->master, bgp_config_finish, NULL,
+ BGP_PRE_CONFIG_MAX_WAIT_SECONDS, &t_bgp_cfg);
+}
+
+/* When we receive a hook the configuration is read,
+ * we start a timer to make sure we postpone sending
+ * EoR before route-maps are processed.
+ * This is especially valid if using `bgp route-map delay-timer`.
+ */
+static void bgp_config_end(void)
+{
+#define BGP_POST_CONFIG_DELAY_SECONDS 1
+ uint32_t bgp_post_config_delay =
+ thread_is_scheduled(bm->t_rmap_update)
+ ? thread_timer_remain_second(bm->t_rmap_update)
+ : BGP_POST_CONFIG_DELAY_SECONDS;
+
+ /* If BGP config processing thread isn't running, then
+ * we can return and rely it's properly handled.
+ */
+ if (!bgp_config_inprocess())
+ return;
+
+ THREAD_OFF(t_bgp_cfg);
+
+ /* Start a new timer to make sure we don't send EoR
+ * before route-maps are processed.
+ */
+ thread_add_timer(bm->master, bgp_config_finish, NULL,
+ bgp_post_config_delay, &t_bgp_cfg);
+}
+
+static int config_write_interface_one(struct vty *vty, struct vrf *vrf)
+{
+ int write = 0;
+ struct interface *ifp;
+ struct bgp_interface *iifp;
+
+ FOR_ALL_INTERFACES (vrf, ifp) {
+ iifp = ifp->info;
+ if (!iifp)
+ continue;
+
+ if_vty_config_start(vty, ifp);
+
+ if (CHECK_FLAG(iifp->flags,
+ BGP_INTERFACE_MPLS_BGP_FORWARDING)) {
+ vty_out(vty, " mpls bgp forwarding\n");
+ write++;
+ }
+
+ if_vty_config_end(vty);
+ }
+
+ return write;
+}
+
+/* Configuration write function for bgpd. */
+static int config_write_interface(struct vty *vty)
+{
+ int write = 0;
+ struct vrf *vrf = NULL;
+
+ /* Display all VRF aware OSPF interface configuration */
+ RB_FOREACH (vrf, vrf_name_head, &vrfs_by_name) {
+ write += config_write_interface_one(vty, vrf);
+ }
+
+ return write;
+}
+
+DEFPY(mpls_bgp_forwarding, mpls_bgp_forwarding_cmd,
+ "[no$no] mpls bgp forwarding",
+ NO_STR MPLS_STR BGP_STR
+ "Enable MPLS forwarding for eBGP directly connected peers\n")
+{
+ bool check;
+ struct bgp_interface *iifp;
+
+ VTY_DECLVAR_CONTEXT(interface, ifp);
+ iifp = ifp->info;
+ if (!iifp) {
+ vty_out(vty, "Interface %s not available\n", ifp->name);
+ return CMD_WARNING_CONFIG_FAILED;
+ }
+ check = CHECK_FLAG(iifp->flags, BGP_INTERFACE_MPLS_BGP_FORWARDING);
+ if (check != !no) {
+ if (no)
+ UNSET_FLAG(iifp->flags,
+ BGP_INTERFACE_MPLS_BGP_FORWARDING);
+ else
+ SET_FLAG(iifp->flags,
+ BGP_INTERFACE_MPLS_BGP_FORWARDING);
+ /* trigger a nht update on eBGP sessions */
+ if (if_is_operative(ifp))
+ bgp_nht_ifp_up(ifp);
+ }
+ return CMD_SUCCESS;
+}
+
+/* Initialization of BGP interface. */
+static void bgp_vty_if_init(void)
+{
+ /* Install interface node. */
+ if_cmd_init(config_write_interface);
+
+ /* "mpls bgp forwarding" commands. */
+ install_element(INTERFACE_NODE, &mpls_bgp_forwarding_cmd);
+}
+
+void bgp_vty_init(void)
+{
+ cmd_variable_handler_register(bgp_var_neighbor);
+ cmd_variable_handler_register(bgp_var_peergroup);
+
+ cmd_init_config_callbacks(bgp_config_start, bgp_config_end);
+
+ /* Install bgp top node. */
+ install_node(&bgp_node);
+ install_node(&bgp_ipv4_unicast_node);
+ install_node(&bgp_ipv4_multicast_node);
+ install_node(&bgp_ipv4_labeled_unicast_node);
+ install_node(&bgp_ipv6_unicast_node);
+ install_node(&bgp_ipv6_multicast_node);
+ install_node(&bgp_ipv6_labeled_unicast_node);
+ install_node(&bgp_vpnv4_node);
+ install_node(&bgp_vpnv6_node);
+ install_node(&bgp_evpn_node);
+ install_node(&bgp_evpn_vni_node);
+ install_node(&bgp_flowspecv4_node);
+ install_node(&bgp_flowspecv6_node);
+ install_node(&bgp_srv6_node);
+
+ /* Install default VTY commands to new nodes. */
+ install_default(BGP_NODE);
+ install_default(BGP_IPV4_NODE);
+ install_default(BGP_IPV4M_NODE);
+ install_default(BGP_IPV4L_NODE);
+ install_default(BGP_IPV6_NODE);
+ install_default(BGP_IPV6M_NODE);
+ install_default(BGP_IPV6L_NODE);
+ install_default(BGP_VPNV4_NODE);
+ install_default(BGP_VPNV6_NODE);
+ install_default(BGP_FLOWSPECV4_NODE);
+ install_default(BGP_FLOWSPECV6_NODE);
+ install_default(BGP_EVPN_NODE);
+ install_default(BGP_EVPN_VNI_NODE);
+ install_default(BGP_SRV6_NODE);
+
+ /* "bgp local-mac" hidden commands. */
+ install_element(CONFIG_NODE, &bgp_local_mac_cmd);
+ install_element(CONFIG_NODE, &no_bgp_local_mac_cmd);
+
+ /* "bgp suppress-fib-pending" global */
+ install_element(CONFIG_NODE, &bgp_global_suppress_fib_pending_cmd);
+
+ /* bgp route-map delay-timer commands. */
+ install_element(CONFIG_NODE, &bgp_set_route_map_delay_timer_cmd);
+ install_element(CONFIG_NODE, &no_bgp_set_route_map_delay_timer_cmd);
+
+ install_element(BGP_NODE, &bgp_allow_martian_cmd);
+
+ /* bgp fast-convergence command */
+ install_element(BGP_NODE, &bgp_fast_convergence_cmd);
+ install_element(BGP_NODE, &no_bgp_fast_convergence_cmd);
+
+ /* global bgp update-delay command */
+ install_element(CONFIG_NODE, &bgp_global_update_delay_cmd);
+ install_element(CONFIG_NODE, &no_bgp_global_update_delay_cmd);
+
+ /* global bgp graceful-shutdown command */
+ install_element(CONFIG_NODE, &bgp_graceful_shutdown_cmd);
+ install_element(CONFIG_NODE, &no_bgp_graceful_shutdown_cmd);
+
+ /* Dummy commands (Currently not supported) */
+ install_element(BGP_NODE, &no_synchronization_cmd);
+ install_element(BGP_NODE, &no_auto_summary_cmd);
+
+ /* "router bgp" commands. */
+ install_element(CONFIG_NODE, &router_bgp_cmd);
+
+ /* "no router bgp" commands. */
+ install_element(CONFIG_NODE, &no_router_bgp_cmd);
+
+ /* "bgp session-dscp command */
+ install_element(CONFIG_NODE, &bgp_session_dscp_cmd);
+ install_element(CONFIG_NODE, &no_bgp_session_dscp_cmd);
+
+ /* "bgp router-id" commands. */
+ install_element(BGP_NODE, &bgp_router_id_cmd);
+ install_element(BGP_NODE, &no_bgp_router_id_cmd);
+
+ /* "bgp suppress-fib-pending" command */
+ install_element(BGP_NODE, &bgp_suppress_fib_pending_cmd);
+
+ /* "bgp cluster-id" commands. */
+ install_element(BGP_NODE, &bgp_cluster_id_cmd);
+ install_element(BGP_NODE, &no_bgp_cluster_id_cmd);
+
+ /* "bgp no-rib" commands. */
+ install_element(CONFIG_NODE, &bgp_norib_cmd);
+ install_element(CONFIG_NODE, &no_bgp_norib_cmd);
+
+ install_element(CONFIG_NODE, &no_bgp_send_extra_data_cmd);
+
+ /* "bgp confederation" commands. */
+ install_element(BGP_NODE, &bgp_confederation_identifier_cmd);
+ install_element(BGP_NODE, &no_bgp_confederation_identifier_cmd);
+
+ /* "bgp confederation peers" commands. */
+ install_element(BGP_NODE, &bgp_confederation_peers_cmd);
+ install_element(BGP_NODE, &no_bgp_confederation_peers_cmd);
+
+ /* bgp max-med command */
+ install_element(BGP_NODE, &bgp_maxmed_admin_cmd);
+ install_element(BGP_NODE, &no_bgp_maxmed_admin_cmd);
+ install_element(BGP_NODE, &bgp_maxmed_admin_medv_cmd);
+ install_element(BGP_NODE, &bgp_maxmed_onstartup_cmd);
+ install_element(BGP_NODE, &no_bgp_maxmed_onstartup_cmd);
+
+ /* "neighbor role" commands. */
+ install_element(BGP_NODE, &neighbor_role_cmd);
+ install_element(BGP_NODE, &neighbor_role_strict_cmd);
+ install_element(BGP_NODE, &no_neighbor_role_cmd);
+
+ /* bgp disable-ebgp-connected-nh-check */
+ install_element(BGP_NODE, &bgp_disable_connected_route_check_cmd);
+ install_element(BGP_NODE, &no_bgp_disable_connected_route_check_cmd);
+
+ /* bgp update-delay command */
+ install_element(BGP_NODE, &bgp_update_delay_cmd);
+ install_element(BGP_NODE, &no_bgp_update_delay_cmd);
+
+ install_element(BGP_NODE, &bgp_wpkt_quanta_cmd);
+ install_element(BGP_NODE, &bgp_rpkt_quanta_cmd);
+
+ install_element(BGP_NODE, &bgp_coalesce_time_cmd);
+ install_element(BGP_NODE, &no_bgp_coalesce_time_cmd);
+
+ /* "maximum-paths" commands. */
+ install_element(BGP_NODE, &bgp_maxpaths_hidden_cmd);
+ install_element(BGP_NODE, &no_bgp_maxpaths_hidden_cmd);
+ install_element(BGP_IPV4_NODE, &bgp_maxpaths_cmd);
+ install_element(BGP_IPV4_NODE, &no_bgp_maxpaths_cmd);
+ install_element(BGP_IPV6_NODE, &bgp_maxpaths_cmd);
+ install_element(BGP_IPV6_NODE, &no_bgp_maxpaths_cmd);
+ install_element(BGP_NODE, &bgp_maxpaths_ibgp_hidden_cmd);
+ install_element(BGP_NODE, &bgp_maxpaths_ibgp_cluster_hidden_cmd);
+ install_element(BGP_NODE, &no_bgp_maxpaths_ibgp_hidden_cmd);
+ install_element(BGP_IPV4_NODE, &bgp_maxpaths_ibgp_cmd);
+ install_element(BGP_IPV4_NODE, &bgp_maxpaths_ibgp_cluster_cmd);
+ install_element(BGP_IPV4_NODE, &no_bgp_maxpaths_ibgp_cmd);
+ install_element(BGP_IPV6_NODE, &bgp_maxpaths_ibgp_cmd);
+ install_element(BGP_IPV6_NODE, &bgp_maxpaths_ibgp_cluster_cmd);
+ install_element(BGP_IPV6_NODE, &no_bgp_maxpaths_ibgp_cmd);
+
+ install_element(BGP_IPV4L_NODE, &bgp_maxpaths_cmd);
+ install_element(BGP_IPV4L_NODE, &no_bgp_maxpaths_cmd);
+ install_element(BGP_IPV4L_NODE, &bgp_maxpaths_ibgp_cmd);
+ install_element(BGP_IPV4L_NODE, &bgp_maxpaths_ibgp_cluster_cmd);
+ install_element(BGP_IPV4L_NODE, &no_bgp_maxpaths_ibgp_cmd);
+ install_element(BGP_IPV6L_NODE, &bgp_maxpaths_cmd);
+ install_element(BGP_IPV6L_NODE, &no_bgp_maxpaths_cmd);
+ install_element(BGP_IPV6L_NODE, &bgp_maxpaths_ibgp_cmd);
+ install_element(BGP_IPV6L_NODE, &bgp_maxpaths_ibgp_cluster_cmd);
+ install_element(BGP_IPV6L_NODE, &no_bgp_maxpaths_ibgp_cmd);
+
+ /* "timers bgp" commands. */
+ install_element(BGP_NODE, &bgp_timers_cmd);
+ install_element(BGP_NODE, &no_bgp_timers_cmd);
+
+ /* "minimum-holdtime" commands. */
+ install_element(BGP_NODE, &bgp_minimum_holdtime_cmd);
+ install_element(BGP_NODE, &no_bgp_minimum_holdtime_cmd);
+
+ /* route-map delay-timer commands - per instance for backwards compat.
+ */
+ install_element(BGP_NODE, &bgp_set_route_map_delay_timer_cmd);
+ install_element(BGP_NODE, &no_bgp_set_route_map_delay_timer_cmd);
+
+ /* "bgp client-to-client reflection" commands */
+ install_element(BGP_NODE, &no_bgp_client_to_client_reflection_cmd);
+ install_element(BGP_NODE, &bgp_client_to_client_reflection_cmd);
+
+ /* "bgp always-compare-med" commands */
+ install_element(BGP_NODE, &bgp_always_compare_med_cmd);
+ install_element(BGP_NODE, &no_bgp_always_compare_med_cmd);
+
+ /* bgp ebgp-requires-policy */
+ install_element(BGP_NODE, &bgp_ebgp_requires_policy_cmd);
+ install_element(BGP_NODE, &no_bgp_ebgp_requires_policy_cmd);
+
+ /* bgp suppress-duplicates */
+ install_element(BGP_NODE, &bgp_suppress_duplicates_cmd);
+ install_element(BGP_NODE, &no_bgp_suppress_duplicates_cmd);
+
+ /* bgp reject-as-sets */
+ install_element(BGP_NODE, &bgp_reject_as_sets_cmd);
+ install_element(BGP_NODE, &no_bgp_reject_as_sets_cmd);
+
+ /* "bgp deterministic-med" commands */
+ install_element(BGP_NODE, &bgp_deterministic_med_cmd);
+ install_element(BGP_NODE, &no_bgp_deterministic_med_cmd);
+
+ /* "bgp graceful-restart" command */
+ install_element(BGP_NODE, &bgp_graceful_restart_cmd);
+ install_element(BGP_NODE, &no_bgp_graceful_restart_cmd);
+
+ /* "bgp graceful-restart-disable" command */
+ install_element(BGP_NODE, &bgp_graceful_restart_disable_cmd);
+ install_element(BGP_NODE, &no_bgp_graceful_restart_disable_cmd);
+
+ /* "neighbor a:b:c:d graceful-restart" command */
+ install_element(BGP_NODE, &bgp_neighbor_graceful_restart_set_cmd);
+ install_element(BGP_NODE, &no_bgp_neighbor_graceful_restart_set_cmd);
+
+ /* "neighbor a:b:c:d graceful-restart-disable" command */
+ install_element(BGP_NODE,
+ &bgp_neighbor_graceful_restart_disable_set_cmd);
+ install_element(BGP_NODE,
+ &no_bgp_neighbor_graceful_restart_disable_set_cmd);
+
+ /* "neighbor a:b:c:d graceful-restart-helper" command */
+ install_element(BGP_NODE,
+ &bgp_neighbor_graceful_restart_helper_set_cmd);
+ install_element(BGP_NODE,
+ &no_bgp_neighbor_graceful_restart_helper_set_cmd);
+
+ install_element(BGP_NODE, &bgp_graceful_restart_stalepath_time_cmd);
+ install_element(BGP_NODE, &no_bgp_graceful_restart_stalepath_time_cmd);
+ install_element(BGP_NODE, &bgp_graceful_restart_restart_time_cmd);
+ install_element(BGP_NODE, &no_bgp_graceful_restart_restart_time_cmd);
+ install_element(BGP_NODE, &bgp_graceful_restart_select_defer_time_cmd);
+ install_element(BGP_NODE,
+ &no_bgp_graceful_restart_select_defer_time_cmd);
+ install_element(BGP_NODE, &bgp_graceful_restart_preserve_fw_cmd);
+ install_element(BGP_NODE, &no_bgp_graceful_restart_preserve_fw_cmd);
+ install_element(BGP_NODE, &bgp_graceful_restart_notification_cmd);
+
+ install_element(BGP_NODE, &bgp_graceful_restart_disable_eor_cmd);
+ install_element(BGP_NODE, &no_bgp_graceful_restart_disable_eor_cmd);
+ install_element(BGP_NODE, &bgp_graceful_restart_rib_stale_time_cmd);
+ install_element(BGP_NODE, &no_bgp_graceful_restart_rib_stale_time_cmd);
+
+ /* "bgp graceful-shutdown" commands */
+ install_element(BGP_NODE, &bgp_graceful_shutdown_cmd);
+ install_element(BGP_NODE, &no_bgp_graceful_shutdown_cmd);
+
+ /* "bgp hard-administrative-reset" commands */
+ install_element(BGP_NODE, &bgp_administrative_reset_cmd);
+
+ /* "bgp long-lived-graceful-restart" commands */
+ install_element(BGP_NODE, &bgp_llgr_stalepath_time_cmd);
+ install_element(BGP_NODE, &no_bgp_llgr_stalepath_time_cmd);
+
+ /* "bgp fast-external-failover" commands */
+ install_element(BGP_NODE, &bgp_fast_external_failover_cmd);
+ install_element(BGP_NODE, &no_bgp_fast_external_failover_cmd);
+
+ /* "bgp bestpath compare-routerid" commands */
+ install_element(BGP_NODE, &bgp_bestpath_compare_router_id_cmd);
+ install_element(BGP_NODE, &no_bgp_bestpath_compare_router_id_cmd);
+
+ /* "bgp bestpath as-path ignore" commands */
+ install_element(BGP_NODE, &bgp_bestpath_aspath_ignore_cmd);
+ install_element(BGP_NODE, &no_bgp_bestpath_aspath_ignore_cmd);
+
+ /* "bgp bestpath as-path confed" commands */
+ install_element(BGP_NODE, &bgp_bestpath_aspath_confed_cmd);
+ install_element(BGP_NODE, &no_bgp_bestpath_aspath_confed_cmd);
+
+ /* "bgp bestpath as-path multipath-relax" commands */
+ install_element(BGP_NODE, &bgp_bestpath_aspath_multipath_relax_cmd);
+ install_element(BGP_NODE, &no_bgp_bestpath_aspath_multipath_relax_cmd);
+
+ /* "bgp bestpath peer-type multipath-relax" commands */
+ install_element(BGP_NODE, &bgp_bestpath_peer_type_multipath_relax_cmd);
+ install_element(BGP_NODE,
+ &no_bgp_bestpath_peer_type_multipath_relax_cmd);
+
+ /* "bgp log-neighbor-changes" commands */
+ install_element(BGP_NODE, &bgp_log_neighbor_changes_cmd);
+ install_element(BGP_NODE, &no_bgp_log_neighbor_changes_cmd);
+
+ /* "bgp bestpath med" commands */
+ install_element(BGP_NODE, &bgp_bestpath_med_cmd);
+ install_element(BGP_NODE, &no_bgp_bestpath_med_cmd);
+
+ /* "bgp bestpath bandwidth" commands */
+ install_element(BGP_NODE, &bgp_bestpath_bw_cmd);
+ install_element(BGP_NODE, &no_bgp_bestpath_bw_cmd);
+
+ /* "no bgp default <afi>-<safi>" commands. */
+ install_element(BGP_NODE, &bgp_default_afi_safi_cmd);
+
+ /* "bgp network import-check" commands. */
+ install_element(BGP_NODE, &bgp_network_import_check_cmd);
+ install_element(BGP_NODE, &bgp_network_import_check_exact_cmd);
+ install_element(BGP_NODE, &no_bgp_network_import_check_cmd);
+
+ /* "bgp default local-preference" commands. */
+ install_element(BGP_NODE, &bgp_default_local_preference_cmd);
+ install_element(BGP_NODE, &no_bgp_default_local_preference_cmd);
+
+ /* bgp default show-hostname */
+ install_element(BGP_NODE, &bgp_default_show_hostname_cmd);
+ install_element(BGP_NODE, &no_bgp_default_show_hostname_cmd);
+
+ /* bgp default show-nexthop-hostname */
+ install_element(BGP_NODE, &bgp_default_show_nexthop_hostname_cmd);
+ install_element(BGP_NODE, &no_bgp_default_show_nexthop_hostname_cmd);
+
+ /* "bgp default subgroup-pkt-queue-max" commands. */
+ install_element(BGP_NODE, &bgp_default_subgroup_pkt_queue_max_cmd);
+ install_element(BGP_NODE, &no_bgp_default_subgroup_pkt_queue_max_cmd);
+
+ /* bgp ibgp-allow-policy-mods command */
+ install_element(BGP_NODE, &bgp_rr_allow_outbound_policy_cmd);
+ install_element(BGP_NODE, &no_bgp_rr_allow_outbound_policy_cmd);
+
+ /* "bgp listen limit" commands. */
+ install_element(BGP_NODE, &bgp_listen_limit_cmd);
+ install_element(BGP_NODE, &no_bgp_listen_limit_cmd);
+
+ /* "bgp listen range" commands. */
+ install_element(BGP_NODE, &bgp_listen_range_cmd);
+ install_element(BGP_NODE, &no_bgp_listen_range_cmd);
+
+ /* "bgp default shutdown" command */
+ install_element(BGP_NODE, &bgp_default_shutdown_cmd);
+
+ /* "bgp shutdown" commands */
+ install_element(BGP_NODE, &bgp_shutdown_cmd);
+ install_element(BGP_NODE, &bgp_shutdown_msg_cmd);
+ install_element(BGP_NODE, &no_bgp_shutdown_cmd);
+ install_element(BGP_NODE, &no_bgp_shutdown_msg_cmd);
+
+ /* "neighbor remote-as" commands. */
+ install_element(BGP_NODE, &neighbor_remote_as_cmd);
+ install_element(BGP_NODE, &neighbor_interface_config_cmd);
+ install_element(BGP_NODE, &neighbor_interface_config_v6only_cmd);
+ install_element(BGP_NODE, &neighbor_interface_config_remote_as_cmd);
+ install_element(BGP_NODE,
+ &neighbor_interface_v6only_config_remote_as_cmd);
+ install_element(BGP_NODE, &no_neighbor_cmd);
+ install_element(BGP_NODE, &no_neighbor_interface_config_cmd);
+
+ /* "neighbor peer-group" commands. */
+ install_element(BGP_NODE, &neighbor_peer_group_cmd);
+ install_element(BGP_NODE, &no_neighbor_peer_group_cmd);
+ install_element(BGP_NODE,
+ &no_neighbor_interface_peer_group_remote_as_cmd);
+
+ /* "neighbor local-as" commands. */
+ install_element(BGP_NODE, &neighbor_local_as_cmd);
+ install_element(BGP_NODE, &neighbor_local_as_no_prepend_cmd);
+ install_element(BGP_NODE, &neighbor_local_as_no_prepend_replace_as_cmd);
+ install_element(BGP_NODE, &no_neighbor_local_as_cmd);
+
+ /* "neighbor solo" commands. */
+ install_element(BGP_NODE, &neighbor_solo_cmd);
+ install_element(BGP_NODE, &no_neighbor_solo_cmd);
+
+ /* "neighbor password" commands. */
+ install_element(BGP_NODE, &neighbor_password_cmd);
+ install_element(BGP_NODE, &no_neighbor_password_cmd);
+
+ /* "neighbor activate" commands. */
+ install_element(BGP_NODE, &neighbor_activate_hidden_cmd);
+ install_element(BGP_IPV4_NODE, &neighbor_activate_cmd);
+ install_element(BGP_IPV4M_NODE, &neighbor_activate_cmd);
+ install_element(BGP_IPV4L_NODE, &neighbor_activate_cmd);
+ install_element(BGP_IPV6_NODE, &neighbor_activate_cmd);
+ install_element(BGP_IPV6M_NODE, &neighbor_activate_cmd);
+ install_element(BGP_IPV6L_NODE, &neighbor_activate_cmd);
+ install_element(BGP_VPNV4_NODE, &neighbor_activate_cmd);
+ install_element(BGP_VPNV6_NODE, &neighbor_activate_cmd);
+ install_element(BGP_FLOWSPECV4_NODE, &neighbor_activate_cmd);
+ install_element(BGP_FLOWSPECV6_NODE, &neighbor_activate_cmd);
+ install_element(BGP_EVPN_NODE, &neighbor_activate_cmd);
+
+ /* "no neighbor activate" commands. */
+ install_element(BGP_NODE, &no_neighbor_activate_hidden_cmd);
+ install_element(BGP_IPV4_NODE, &no_neighbor_activate_cmd);
+ install_element(BGP_IPV4M_NODE, &no_neighbor_activate_cmd);
+ install_element(BGP_IPV4L_NODE, &no_neighbor_activate_cmd);
+ install_element(BGP_IPV6_NODE, &no_neighbor_activate_cmd);
+ install_element(BGP_IPV6M_NODE, &no_neighbor_activate_cmd);
+ install_element(BGP_IPV6L_NODE, &no_neighbor_activate_cmd);
+ install_element(BGP_VPNV4_NODE, &no_neighbor_activate_cmd);
+ install_element(BGP_VPNV6_NODE, &no_neighbor_activate_cmd);
+ install_element(BGP_FLOWSPECV4_NODE, &no_neighbor_activate_cmd);
+ install_element(BGP_FLOWSPECV6_NODE, &no_neighbor_activate_cmd);
+ install_element(BGP_EVPN_NODE, &no_neighbor_activate_cmd);
+
+ /* "neighbor peer-group" set commands. */
+ install_element(BGP_NODE, &neighbor_set_peer_group_cmd);
+ install_element(BGP_IPV4_NODE, &neighbor_set_peer_group_hidden_cmd);
+ install_element(BGP_IPV4M_NODE, &neighbor_set_peer_group_hidden_cmd);
+ install_element(BGP_IPV6_NODE, &neighbor_set_peer_group_hidden_cmd);
+ install_element(BGP_IPV6M_NODE, &neighbor_set_peer_group_hidden_cmd);
+ install_element(BGP_IPV6L_NODE, &neighbor_set_peer_group_hidden_cmd);
+ install_element(BGP_VPNV4_NODE, &neighbor_set_peer_group_hidden_cmd);
+ install_element(BGP_VPNV6_NODE, &neighbor_set_peer_group_hidden_cmd);
+ install_element(BGP_FLOWSPECV4_NODE,
+ &neighbor_set_peer_group_hidden_cmd);
+ install_element(BGP_FLOWSPECV6_NODE,
+ &neighbor_set_peer_group_hidden_cmd);
+
+ /* "no neighbor peer-group unset" commands. */
+ install_element(BGP_NODE, &no_neighbor_set_peer_group_cmd);
+ install_element(BGP_IPV4_NODE, &no_neighbor_set_peer_group_hidden_cmd);
+ install_element(BGP_IPV4M_NODE, &no_neighbor_set_peer_group_hidden_cmd);
+ install_element(BGP_IPV6_NODE, &no_neighbor_set_peer_group_hidden_cmd);
+ install_element(BGP_IPV6M_NODE, &no_neighbor_set_peer_group_hidden_cmd);
+ install_element(BGP_IPV6L_NODE, &no_neighbor_set_peer_group_hidden_cmd);
+ install_element(BGP_VPNV4_NODE, &no_neighbor_set_peer_group_hidden_cmd);
+ install_element(BGP_VPNV6_NODE, &no_neighbor_set_peer_group_hidden_cmd);
+ install_element(BGP_FLOWSPECV4_NODE,
+ &no_neighbor_set_peer_group_hidden_cmd);
+ install_element(BGP_FLOWSPECV6_NODE,
+ &no_neighbor_set_peer_group_hidden_cmd);
+
+ /* "neighbor softreconfiguration inbound" commands.*/
+ install_element(BGP_NODE, &neighbor_soft_reconfiguration_hidden_cmd);
+ install_element(BGP_NODE, &no_neighbor_soft_reconfiguration_hidden_cmd);
+ install_element(BGP_IPV4_NODE, &neighbor_soft_reconfiguration_cmd);
+ install_element(BGP_IPV4_NODE, &no_neighbor_soft_reconfiguration_cmd);
+ install_element(BGP_IPV4L_NODE, &neighbor_soft_reconfiguration_cmd);
+ install_element(BGP_IPV4L_NODE, &no_neighbor_soft_reconfiguration_cmd);
+ install_element(BGP_IPV4M_NODE, &neighbor_soft_reconfiguration_cmd);
+ install_element(BGP_IPV4M_NODE, &no_neighbor_soft_reconfiguration_cmd);
+ install_element(BGP_IPV6_NODE, &neighbor_soft_reconfiguration_cmd);
+ install_element(BGP_IPV6_NODE, &no_neighbor_soft_reconfiguration_cmd);
+ install_element(BGP_IPV6M_NODE, &neighbor_soft_reconfiguration_cmd);
+ install_element(BGP_IPV6M_NODE, &no_neighbor_soft_reconfiguration_cmd);
+ install_element(BGP_IPV6L_NODE, &neighbor_soft_reconfiguration_cmd);
+ install_element(BGP_IPV6L_NODE, &no_neighbor_soft_reconfiguration_cmd);
+ install_element(BGP_VPNV4_NODE, &neighbor_soft_reconfiguration_cmd);
+ install_element(BGP_VPNV4_NODE, &no_neighbor_soft_reconfiguration_cmd);
+ install_element(BGP_VPNV6_NODE, &neighbor_soft_reconfiguration_cmd);
+ install_element(BGP_VPNV6_NODE, &no_neighbor_soft_reconfiguration_cmd);
+ install_element(BGP_FLOWSPECV4_NODE,
+ &neighbor_soft_reconfiguration_cmd);
+ install_element(BGP_FLOWSPECV4_NODE,
+ &no_neighbor_soft_reconfiguration_cmd);
+ install_element(BGP_FLOWSPECV6_NODE,
+ &neighbor_soft_reconfiguration_cmd);
+ install_element(BGP_FLOWSPECV6_NODE,
+ &no_neighbor_soft_reconfiguration_cmd);
+ install_element(BGP_EVPN_NODE, &neighbor_soft_reconfiguration_cmd);
+ install_element(BGP_EVPN_NODE, &no_neighbor_soft_reconfiguration_cmd);
+
+ /* "neighbor attribute-unchanged" commands. */
+ install_element(BGP_NODE, &neighbor_attr_unchanged_hidden_cmd);
+ install_element(BGP_NODE, &no_neighbor_attr_unchanged_hidden_cmd);
+ install_element(BGP_IPV4_NODE, &neighbor_attr_unchanged_cmd);
+ install_element(BGP_IPV4_NODE, &no_neighbor_attr_unchanged_cmd);
+ install_element(BGP_IPV4M_NODE, &neighbor_attr_unchanged_cmd);
+ install_element(BGP_IPV4M_NODE, &no_neighbor_attr_unchanged_cmd);
+ install_element(BGP_IPV4L_NODE, &neighbor_attr_unchanged_cmd);
+ install_element(BGP_IPV4L_NODE, &no_neighbor_attr_unchanged_cmd);
+ install_element(BGP_IPV6_NODE, &neighbor_attr_unchanged_cmd);
+ install_element(BGP_IPV6_NODE, &no_neighbor_attr_unchanged_cmd);
+ install_element(BGP_IPV6M_NODE, &neighbor_attr_unchanged_cmd);
+ install_element(BGP_IPV6M_NODE, &no_neighbor_attr_unchanged_cmd);
+ install_element(BGP_IPV6L_NODE, &neighbor_attr_unchanged_cmd);
+ install_element(BGP_IPV6L_NODE, &no_neighbor_attr_unchanged_cmd);
+ install_element(BGP_VPNV4_NODE, &neighbor_attr_unchanged_cmd);
+ install_element(BGP_VPNV4_NODE, &no_neighbor_attr_unchanged_cmd);
+ install_element(BGP_VPNV6_NODE, &neighbor_attr_unchanged_cmd);
+ install_element(BGP_VPNV6_NODE, &no_neighbor_attr_unchanged_cmd);
+
+ install_element(BGP_EVPN_NODE, &neighbor_attr_unchanged_cmd);
+ install_element(BGP_EVPN_NODE, &no_neighbor_attr_unchanged_cmd);
+
+ install_element(BGP_FLOWSPECV4_NODE, &neighbor_attr_unchanged_cmd);
+ install_element(BGP_FLOWSPECV4_NODE, &no_neighbor_attr_unchanged_cmd);
+ install_element(BGP_FLOWSPECV6_NODE, &neighbor_attr_unchanged_cmd);
+ install_element(BGP_FLOWSPECV6_NODE, &no_neighbor_attr_unchanged_cmd);
+
+ /* "nexthop-local unchanged" commands */
+ install_element(BGP_IPV6_NODE, &neighbor_nexthop_local_unchanged_cmd);
+ install_element(BGP_IPV6_NODE,
+ &no_neighbor_nexthop_local_unchanged_cmd);
+
+ /* "neighbor next-hop-self" commands. */
+ install_element(BGP_NODE, &neighbor_nexthop_self_hidden_cmd);
+ install_element(BGP_NODE, &no_neighbor_nexthop_self_hidden_cmd);
+ install_element(BGP_IPV4_NODE, &neighbor_nexthop_self_cmd);
+ install_element(BGP_IPV4_NODE, &no_neighbor_nexthop_self_cmd);
+ install_element(BGP_IPV4M_NODE, &neighbor_nexthop_self_cmd);
+ install_element(BGP_IPV4M_NODE, &no_neighbor_nexthop_self_cmd);
+ install_element(BGP_IPV4L_NODE, &neighbor_nexthop_self_cmd);
+ install_element(BGP_IPV4L_NODE, &no_neighbor_nexthop_self_cmd);
+ install_element(BGP_IPV6_NODE, &neighbor_nexthop_self_cmd);
+ install_element(BGP_IPV6_NODE, &no_neighbor_nexthop_self_cmd);
+ install_element(BGP_IPV6M_NODE, &neighbor_nexthop_self_cmd);
+ install_element(BGP_IPV6M_NODE, &no_neighbor_nexthop_self_cmd);
+ install_element(BGP_IPV6L_NODE, &neighbor_nexthop_self_cmd);
+ install_element(BGP_IPV6L_NODE, &no_neighbor_nexthop_self_cmd);
+ install_element(BGP_VPNV4_NODE, &neighbor_nexthop_self_cmd);
+ install_element(BGP_VPNV4_NODE, &no_neighbor_nexthop_self_cmd);
+ install_element(BGP_VPNV6_NODE, &neighbor_nexthop_self_cmd);
+ install_element(BGP_VPNV6_NODE, &no_neighbor_nexthop_self_cmd);
+ install_element(BGP_EVPN_NODE, &neighbor_nexthop_self_cmd);
+ install_element(BGP_EVPN_NODE, &no_neighbor_nexthop_self_cmd);
+
+ /* "neighbor next-hop-self force" commands. */
+ install_element(BGP_NODE, &neighbor_nexthop_self_force_hidden_cmd);
+ install_element(BGP_NODE, &no_neighbor_nexthop_self_force_hidden_cmd);
+ install_element(BGP_NODE, &neighbor_nexthop_self_all_hidden_cmd);
+ install_element(BGP_NODE, &no_neighbor_nexthop_self_all_hidden_cmd);
+ install_element(BGP_IPV4_NODE, &neighbor_nexthop_self_force_cmd);
+ install_element(BGP_IPV4_NODE, &no_neighbor_nexthop_self_force_cmd);
+ install_element(BGP_IPV4_NODE, &neighbor_nexthop_self_all_hidden_cmd);
+ install_element(BGP_IPV4_NODE,
+ &no_neighbor_nexthop_self_all_hidden_cmd);
+ install_element(BGP_IPV4M_NODE, &neighbor_nexthop_self_force_cmd);
+ install_element(BGP_IPV4M_NODE, &no_neighbor_nexthop_self_force_cmd);
+ install_element(BGP_IPV4M_NODE, &neighbor_nexthop_self_all_hidden_cmd);
+ install_element(BGP_IPV4M_NODE,
+ &no_neighbor_nexthop_self_all_hidden_cmd);
+ install_element(BGP_IPV4L_NODE, &neighbor_nexthop_self_force_cmd);
+ install_element(BGP_IPV4L_NODE, &no_neighbor_nexthop_self_force_cmd);
+ install_element(BGP_IPV4L_NODE, &neighbor_nexthop_self_all_hidden_cmd);
+ install_element(BGP_IPV4L_NODE,
+ &no_neighbor_nexthop_self_all_hidden_cmd);
+ install_element(BGP_IPV6_NODE, &neighbor_nexthop_self_force_cmd);
+ install_element(BGP_IPV6_NODE, &no_neighbor_nexthop_self_force_cmd);
+ install_element(BGP_IPV6_NODE, &neighbor_nexthop_self_all_hidden_cmd);
+ install_element(BGP_IPV6_NODE,
+ &no_neighbor_nexthop_self_all_hidden_cmd);
+ install_element(BGP_IPV6M_NODE, &neighbor_nexthop_self_force_cmd);
+ install_element(BGP_IPV6M_NODE, &no_neighbor_nexthop_self_force_cmd);
+ install_element(BGP_IPV6M_NODE, &neighbor_nexthop_self_all_hidden_cmd);
+ install_element(BGP_IPV6M_NODE,
+ &no_neighbor_nexthop_self_all_hidden_cmd);
+ install_element(BGP_IPV6L_NODE, &neighbor_nexthop_self_force_cmd);
+ install_element(BGP_IPV6L_NODE, &no_neighbor_nexthop_self_force_cmd);
+ install_element(BGP_IPV6L_NODE, &neighbor_nexthop_self_all_hidden_cmd);
+ install_element(BGP_IPV6L_NODE,
+ &no_neighbor_nexthop_self_all_hidden_cmd);
+ install_element(BGP_VPNV4_NODE, &neighbor_nexthop_self_force_cmd);
+ install_element(BGP_VPNV4_NODE, &no_neighbor_nexthop_self_force_cmd);
+ install_element(BGP_VPNV4_NODE, &neighbor_nexthop_self_all_hidden_cmd);
+ install_element(BGP_VPNV4_NODE,
+ &no_neighbor_nexthop_self_all_hidden_cmd);
+ install_element(BGP_VPNV6_NODE, &neighbor_nexthop_self_force_cmd);
+ install_element(BGP_VPNV6_NODE, &no_neighbor_nexthop_self_force_cmd);
+ install_element(BGP_VPNV6_NODE, &neighbor_nexthop_self_all_hidden_cmd);
+ install_element(BGP_VPNV6_NODE,
+ &no_neighbor_nexthop_self_all_hidden_cmd);
+ install_element(BGP_EVPN_NODE, &neighbor_nexthop_self_force_cmd);
+ install_element(BGP_EVPN_NODE, &no_neighbor_nexthop_self_force_cmd);
+
+ /* "neighbor as-override" commands. */
+ install_element(BGP_NODE, &neighbor_as_override_hidden_cmd);
+ install_element(BGP_NODE, &no_neighbor_as_override_hidden_cmd);
+ install_element(BGP_IPV4_NODE, &neighbor_as_override_cmd);
+ install_element(BGP_IPV4_NODE, &no_neighbor_as_override_cmd);
+ install_element(BGP_IPV4M_NODE, &neighbor_as_override_cmd);
+ install_element(BGP_IPV4M_NODE, &no_neighbor_as_override_cmd);
+ install_element(BGP_IPV4L_NODE, &neighbor_as_override_cmd);
+ install_element(BGP_IPV4L_NODE, &no_neighbor_as_override_cmd);
+ install_element(BGP_IPV6_NODE, &neighbor_as_override_cmd);
+ install_element(BGP_IPV6_NODE, &no_neighbor_as_override_cmd);
+ install_element(BGP_IPV6M_NODE, &neighbor_as_override_cmd);
+ install_element(BGP_IPV6M_NODE, &no_neighbor_as_override_cmd);
+ install_element(BGP_IPV6L_NODE, &neighbor_as_override_cmd);
+ install_element(BGP_IPV6L_NODE, &no_neighbor_as_override_cmd);
+ install_element(BGP_VPNV4_NODE, &neighbor_as_override_cmd);
+ install_element(BGP_VPNV4_NODE, &no_neighbor_as_override_cmd);
+ install_element(BGP_VPNV6_NODE, &neighbor_as_override_cmd);
+ install_element(BGP_VPNV6_NODE, &no_neighbor_as_override_cmd);
+
+ /* "neighbor remove-private-AS" commands. */
+ install_element(BGP_NODE, &neighbor_remove_private_as_hidden_cmd);
+ install_element(BGP_NODE, &no_neighbor_remove_private_as_hidden_cmd);
+ install_element(BGP_NODE, &neighbor_remove_private_as_all_hidden_cmd);
+ install_element(BGP_NODE,
+ &no_neighbor_remove_private_as_all_hidden_cmd);
+ install_element(BGP_NODE,
+ &neighbor_remove_private_as_replace_as_hidden_cmd);
+ install_element(BGP_NODE,
+ &no_neighbor_remove_private_as_replace_as_hidden_cmd);
+ install_element(BGP_NODE,
+ &neighbor_remove_private_as_all_replace_as_hidden_cmd);
+ install_element(
+ BGP_NODE,
+ &no_neighbor_remove_private_as_all_replace_as_hidden_cmd);
+ install_element(BGP_IPV4_NODE, &neighbor_remove_private_as_cmd);
+ install_element(BGP_IPV4_NODE, &no_neighbor_remove_private_as_cmd);
+ install_element(BGP_IPV4_NODE, &neighbor_remove_private_as_all_cmd);
+ install_element(BGP_IPV4_NODE, &no_neighbor_remove_private_as_all_cmd);
+ install_element(BGP_IPV4_NODE,
+ &neighbor_remove_private_as_replace_as_cmd);
+ install_element(BGP_IPV4_NODE,
+ &no_neighbor_remove_private_as_replace_as_cmd);
+ install_element(BGP_IPV4_NODE,
+ &neighbor_remove_private_as_all_replace_as_cmd);
+ install_element(BGP_IPV4_NODE,
+ &no_neighbor_remove_private_as_all_replace_as_cmd);
+ install_element(BGP_IPV4M_NODE, &neighbor_remove_private_as_cmd);
+ install_element(BGP_IPV4M_NODE, &no_neighbor_remove_private_as_cmd);
+ install_element(BGP_IPV4M_NODE, &neighbor_remove_private_as_all_cmd);
+ install_element(BGP_IPV4M_NODE, &no_neighbor_remove_private_as_all_cmd);
+ install_element(BGP_IPV4M_NODE,
+ &neighbor_remove_private_as_replace_as_cmd);
+ install_element(BGP_IPV4M_NODE,
+ &no_neighbor_remove_private_as_replace_as_cmd);
+ install_element(BGP_IPV4M_NODE,
+ &neighbor_remove_private_as_all_replace_as_cmd);
+ install_element(BGP_IPV4M_NODE,
+ &no_neighbor_remove_private_as_all_replace_as_cmd);
+ install_element(BGP_IPV4L_NODE, &neighbor_remove_private_as_cmd);
+ install_element(BGP_IPV4L_NODE, &no_neighbor_remove_private_as_cmd);
+ install_element(BGP_IPV4L_NODE, &neighbor_remove_private_as_all_cmd);
+ install_element(BGP_IPV4L_NODE, &no_neighbor_remove_private_as_all_cmd);
+ install_element(BGP_IPV4L_NODE,
+ &neighbor_remove_private_as_replace_as_cmd);
+ install_element(BGP_IPV4L_NODE,
+ &no_neighbor_remove_private_as_replace_as_cmd);
+ install_element(BGP_IPV4L_NODE,
+ &neighbor_remove_private_as_all_replace_as_cmd);
+ install_element(BGP_IPV4L_NODE,
+ &no_neighbor_remove_private_as_all_replace_as_cmd);
+ install_element(BGP_IPV6_NODE, &neighbor_remove_private_as_cmd);
+ install_element(BGP_IPV6_NODE, &no_neighbor_remove_private_as_cmd);
+ install_element(BGP_IPV6_NODE, &neighbor_remove_private_as_all_cmd);
+ install_element(BGP_IPV6_NODE, &no_neighbor_remove_private_as_all_cmd);
+ install_element(BGP_IPV6_NODE,
+ &neighbor_remove_private_as_replace_as_cmd);
+ install_element(BGP_IPV6_NODE,
+ &no_neighbor_remove_private_as_replace_as_cmd);
+ install_element(BGP_IPV6_NODE,
+ &neighbor_remove_private_as_all_replace_as_cmd);
+ install_element(BGP_IPV6_NODE,
+ &no_neighbor_remove_private_as_all_replace_as_cmd);
+ install_element(BGP_IPV6M_NODE, &neighbor_remove_private_as_cmd);
+ install_element(BGP_IPV6M_NODE, &no_neighbor_remove_private_as_cmd);
+ install_element(BGP_IPV6M_NODE, &neighbor_remove_private_as_all_cmd);
+ install_element(BGP_IPV6M_NODE, &no_neighbor_remove_private_as_all_cmd);
+ install_element(BGP_IPV6M_NODE,
+ &neighbor_remove_private_as_replace_as_cmd);
+ install_element(BGP_IPV6M_NODE,
+ &no_neighbor_remove_private_as_replace_as_cmd);
+ install_element(BGP_IPV6M_NODE,
+ &neighbor_remove_private_as_all_replace_as_cmd);
+ install_element(BGP_IPV6M_NODE,
+ &no_neighbor_remove_private_as_all_replace_as_cmd);
+ install_element(BGP_IPV6L_NODE, &neighbor_remove_private_as_cmd);
+ install_element(BGP_IPV6L_NODE, &no_neighbor_remove_private_as_cmd);
+ install_element(BGP_IPV6L_NODE, &neighbor_remove_private_as_all_cmd);
+ install_element(BGP_IPV6L_NODE, &no_neighbor_remove_private_as_all_cmd);
+ install_element(BGP_IPV6L_NODE,
+ &neighbor_remove_private_as_replace_as_cmd);
+ install_element(BGP_IPV6L_NODE,
+ &no_neighbor_remove_private_as_replace_as_cmd);
+ install_element(BGP_IPV6L_NODE,
+ &neighbor_remove_private_as_all_replace_as_cmd);
+ install_element(BGP_IPV6L_NODE,
+ &no_neighbor_remove_private_as_all_replace_as_cmd);
+ install_element(BGP_VPNV4_NODE, &neighbor_remove_private_as_cmd);
+ install_element(BGP_VPNV4_NODE, &no_neighbor_remove_private_as_cmd);
+ install_element(BGP_VPNV4_NODE, &neighbor_remove_private_as_all_cmd);
+ install_element(BGP_VPNV4_NODE, &no_neighbor_remove_private_as_all_cmd);
+ install_element(BGP_VPNV4_NODE,
+ &neighbor_remove_private_as_replace_as_cmd);
+ install_element(BGP_VPNV4_NODE,
+ &no_neighbor_remove_private_as_replace_as_cmd);
+ install_element(BGP_VPNV4_NODE,
+ &neighbor_remove_private_as_all_replace_as_cmd);
+ install_element(BGP_VPNV4_NODE,
+ &no_neighbor_remove_private_as_all_replace_as_cmd);
+ install_element(BGP_VPNV6_NODE, &neighbor_remove_private_as_cmd);
+ install_element(BGP_VPNV6_NODE, &no_neighbor_remove_private_as_cmd);
+ install_element(BGP_VPNV6_NODE, &neighbor_remove_private_as_all_cmd);
+ install_element(BGP_VPNV6_NODE, &no_neighbor_remove_private_as_all_cmd);
+ install_element(BGP_VPNV6_NODE,
+ &neighbor_remove_private_as_replace_as_cmd);
+ install_element(BGP_VPNV6_NODE,
+ &no_neighbor_remove_private_as_replace_as_cmd);
+ install_element(BGP_VPNV6_NODE,
+ &neighbor_remove_private_as_all_replace_as_cmd);
+ install_element(BGP_VPNV6_NODE,
+ &no_neighbor_remove_private_as_all_replace_as_cmd);
+
+ /* "neighbor send-community" commands.*/
+ install_element(BGP_NODE, &neighbor_send_community_hidden_cmd);
+ install_element(BGP_NODE, &neighbor_send_community_type_hidden_cmd);
+ install_element(BGP_NODE, &no_neighbor_send_community_hidden_cmd);
+ install_element(BGP_NODE, &no_neighbor_send_community_type_hidden_cmd);
+ install_element(BGP_IPV4_NODE, &neighbor_send_community_cmd);
+ install_element(BGP_IPV4_NODE, &neighbor_send_community_type_cmd);
+ install_element(BGP_IPV4_NODE, &no_neighbor_send_community_cmd);
+ install_element(BGP_IPV4_NODE, &no_neighbor_send_community_type_cmd);
+ install_element(BGP_IPV4M_NODE, &neighbor_send_community_cmd);
+ install_element(BGP_IPV4M_NODE, &neighbor_send_community_type_cmd);
+ install_element(BGP_IPV4M_NODE, &no_neighbor_send_community_cmd);
+ install_element(BGP_IPV4M_NODE, &no_neighbor_send_community_type_cmd);
+ install_element(BGP_IPV4L_NODE, &neighbor_send_community_cmd);
+ install_element(BGP_IPV4L_NODE, &neighbor_send_community_type_cmd);
+ install_element(BGP_IPV4L_NODE, &no_neighbor_send_community_cmd);
+ install_element(BGP_IPV4L_NODE, &no_neighbor_send_community_type_cmd);
+ install_element(BGP_IPV6_NODE, &neighbor_send_community_cmd);
+ install_element(BGP_IPV6_NODE, &neighbor_send_community_type_cmd);
+ install_element(BGP_IPV6_NODE, &no_neighbor_send_community_cmd);
+ install_element(BGP_IPV6_NODE, &no_neighbor_send_community_type_cmd);
+ install_element(BGP_IPV6M_NODE, &neighbor_send_community_cmd);
+ install_element(BGP_IPV6M_NODE, &neighbor_send_community_type_cmd);
+ install_element(BGP_IPV6M_NODE, &no_neighbor_send_community_cmd);
+ install_element(BGP_IPV6M_NODE, &no_neighbor_send_community_type_cmd);
+ install_element(BGP_IPV6L_NODE, &neighbor_send_community_cmd);
+ install_element(BGP_IPV6L_NODE, &neighbor_send_community_type_cmd);
+ install_element(BGP_IPV6L_NODE, &no_neighbor_send_community_cmd);
+ install_element(BGP_IPV6L_NODE, &no_neighbor_send_community_type_cmd);
+ install_element(BGP_VPNV4_NODE, &neighbor_send_community_cmd);
+ install_element(BGP_VPNV4_NODE, &neighbor_send_community_type_cmd);
+ install_element(BGP_VPNV4_NODE, &no_neighbor_send_community_cmd);
+ install_element(BGP_VPNV4_NODE, &no_neighbor_send_community_type_cmd);
+ install_element(BGP_VPNV6_NODE, &neighbor_send_community_cmd);
+ install_element(BGP_VPNV6_NODE, &neighbor_send_community_type_cmd);
+ install_element(BGP_VPNV6_NODE, &no_neighbor_send_community_cmd);
+ install_element(BGP_VPNV6_NODE, &no_neighbor_send_community_type_cmd);
+
+ /* "neighbor route-reflector" commands.*/
+ install_element(BGP_NODE, &neighbor_route_reflector_client_hidden_cmd);
+ install_element(BGP_NODE,
+ &no_neighbor_route_reflector_client_hidden_cmd);
+ install_element(BGP_IPV4_NODE, &neighbor_route_reflector_client_cmd);
+ install_element(BGP_IPV4_NODE, &no_neighbor_route_reflector_client_cmd);
+ install_element(BGP_IPV4M_NODE, &neighbor_route_reflector_client_cmd);
+ install_element(BGP_IPV4M_NODE,
+ &no_neighbor_route_reflector_client_cmd);
+ install_element(BGP_IPV4L_NODE, &neighbor_route_reflector_client_cmd);
+ install_element(BGP_IPV4L_NODE,
+ &no_neighbor_route_reflector_client_cmd);
+ install_element(BGP_IPV6_NODE, &neighbor_route_reflector_client_cmd);
+ install_element(BGP_IPV6_NODE, &no_neighbor_route_reflector_client_cmd);
+ install_element(BGP_IPV6M_NODE, &neighbor_route_reflector_client_cmd);
+ install_element(BGP_IPV6M_NODE,
+ &no_neighbor_route_reflector_client_cmd);
+ install_element(BGP_IPV6L_NODE, &neighbor_route_reflector_client_cmd);
+ install_element(BGP_IPV6L_NODE,
+ &no_neighbor_route_reflector_client_cmd);
+ install_element(BGP_VPNV4_NODE, &neighbor_route_reflector_client_cmd);
+ install_element(BGP_VPNV4_NODE,
+ &no_neighbor_route_reflector_client_cmd);
+ install_element(BGP_VPNV6_NODE, &neighbor_route_reflector_client_cmd);
+ install_element(BGP_VPNV6_NODE,
+ &no_neighbor_route_reflector_client_cmd);
+ install_element(BGP_FLOWSPECV4_NODE,
+ &neighbor_route_reflector_client_cmd);
+ install_element(BGP_FLOWSPECV4_NODE,
+ &no_neighbor_route_reflector_client_cmd);
+ install_element(BGP_FLOWSPECV6_NODE,
+ &neighbor_route_reflector_client_cmd);
+ install_element(BGP_FLOWSPECV6_NODE,
+ &no_neighbor_route_reflector_client_cmd);
+ install_element(BGP_EVPN_NODE, &neighbor_route_reflector_client_cmd);
+ install_element(BGP_EVPN_NODE, &no_neighbor_route_reflector_client_cmd);
+
+ /* "neighbor route-server" commands.*/
+ install_element(BGP_NODE, &neighbor_route_server_client_hidden_cmd);
+ install_element(BGP_NODE, &no_neighbor_route_server_client_hidden_cmd);
+ install_element(BGP_IPV4_NODE, &neighbor_route_server_client_cmd);
+ install_element(BGP_IPV4_NODE, &no_neighbor_route_server_client_cmd);
+ install_element(BGP_IPV4M_NODE, &neighbor_route_server_client_cmd);
+ install_element(BGP_IPV4M_NODE, &no_neighbor_route_server_client_cmd);
+ install_element(BGP_IPV4L_NODE, &neighbor_route_server_client_cmd);
+ install_element(BGP_IPV4L_NODE, &no_neighbor_route_server_client_cmd);
+ install_element(BGP_IPV6_NODE, &neighbor_route_server_client_cmd);
+ install_element(BGP_IPV6_NODE, &no_neighbor_route_server_client_cmd);
+ install_element(BGP_IPV6M_NODE, &neighbor_route_server_client_cmd);
+ install_element(BGP_IPV6M_NODE, &no_neighbor_route_server_client_cmd);
+ install_element(BGP_IPV6L_NODE, &neighbor_route_server_client_cmd);
+ install_element(BGP_IPV6L_NODE, &no_neighbor_route_server_client_cmd);
+ install_element(BGP_VPNV4_NODE, &neighbor_route_server_client_cmd);
+ install_element(BGP_VPNV4_NODE, &no_neighbor_route_server_client_cmd);
+ install_element(BGP_VPNV6_NODE, &neighbor_route_server_client_cmd);
+ install_element(BGP_VPNV6_NODE, &no_neighbor_route_server_client_cmd);
+ install_element(BGP_EVPN_NODE, &neighbor_route_server_client_cmd);
+ install_element(BGP_EVPN_NODE, &no_neighbor_route_server_client_cmd);
+ install_element(BGP_FLOWSPECV4_NODE, &neighbor_route_server_client_cmd);
+ install_element(BGP_FLOWSPECV4_NODE,
+ &no_neighbor_route_server_client_cmd);
+ install_element(BGP_FLOWSPECV6_NODE, &neighbor_route_server_client_cmd);
+ install_element(BGP_FLOWSPECV6_NODE,
+ &no_neighbor_route_server_client_cmd);
+
+ /* "neighbor disable-addpath-rx" commands. */
+ install_element(BGP_IPV4_NODE, &neighbor_disable_addpath_rx_cmd);
+ install_element(BGP_IPV4_NODE, &no_neighbor_disable_addpath_rx_cmd);
+ install_element(BGP_IPV4M_NODE, &neighbor_disable_addpath_rx_cmd);
+ install_element(BGP_IPV4M_NODE, &no_neighbor_disable_addpath_rx_cmd);
+ install_element(BGP_IPV4L_NODE, &neighbor_disable_addpath_rx_cmd);
+ install_element(BGP_IPV4L_NODE, &no_neighbor_disable_addpath_rx_cmd);
+ install_element(BGP_IPV6_NODE, &neighbor_disable_addpath_rx_cmd);
+ install_element(BGP_IPV6_NODE, &no_neighbor_disable_addpath_rx_cmd);
+ install_element(BGP_IPV6M_NODE, &neighbor_disable_addpath_rx_cmd);
+ install_element(BGP_IPV6M_NODE, &no_neighbor_disable_addpath_rx_cmd);
+ install_element(BGP_IPV6L_NODE, &neighbor_disable_addpath_rx_cmd);
+ install_element(BGP_IPV6L_NODE, &no_neighbor_disable_addpath_rx_cmd);
+ install_element(BGP_VPNV4_NODE, &neighbor_disable_addpath_rx_cmd);
+ install_element(BGP_VPNV4_NODE, &no_neighbor_disable_addpath_rx_cmd);
+ install_element(BGP_VPNV6_NODE, &neighbor_disable_addpath_rx_cmd);
+ install_element(BGP_VPNV6_NODE, &no_neighbor_disable_addpath_rx_cmd);
+
+ /* "neighbor addpath-tx-all-paths" commands.*/
+ install_element(BGP_NODE, &neighbor_addpath_tx_all_paths_hidden_cmd);
+ install_element(BGP_NODE, &no_neighbor_addpath_tx_all_paths_hidden_cmd);
+ install_element(BGP_IPV4_NODE, &neighbor_addpath_tx_all_paths_cmd);
+ install_element(BGP_IPV4_NODE, &no_neighbor_addpath_tx_all_paths_cmd);
+ install_element(BGP_IPV4M_NODE, &neighbor_addpath_tx_all_paths_cmd);
+ install_element(BGP_IPV4M_NODE, &no_neighbor_addpath_tx_all_paths_cmd);
+ install_element(BGP_IPV4L_NODE, &neighbor_addpath_tx_all_paths_cmd);
+ install_element(BGP_IPV4L_NODE, &no_neighbor_addpath_tx_all_paths_cmd);
+ install_element(BGP_IPV6_NODE, &neighbor_addpath_tx_all_paths_cmd);
+ install_element(BGP_IPV6_NODE, &no_neighbor_addpath_tx_all_paths_cmd);
+ install_element(BGP_IPV6M_NODE, &neighbor_addpath_tx_all_paths_cmd);
+ install_element(BGP_IPV6M_NODE, &no_neighbor_addpath_tx_all_paths_cmd);
+ install_element(BGP_IPV6L_NODE, &neighbor_addpath_tx_all_paths_cmd);
+ install_element(BGP_IPV6L_NODE, &no_neighbor_addpath_tx_all_paths_cmd);
+ install_element(BGP_VPNV4_NODE, &neighbor_addpath_tx_all_paths_cmd);
+ install_element(BGP_VPNV4_NODE, &no_neighbor_addpath_tx_all_paths_cmd);
+ install_element(BGP_VPNV6_NODE, &neighbor_addpath_tx_all_paths_cmd);
+ install_element(BGP_VPNV6_NODE, &no_neighbor_addpath_tx_all_paths_cmd);
+
+ /* "neighbor addpath-tx-bestpath-per-AS" commands.*/
+ install_element(BGP_NODE,
+ &neighbor_addpath_tx_bestpath_per_as_hidden_cmd);
+ install_element(BGP_NODE,
+ &no_neighbor_addpath_tx_bestpath_per_as_hidden_cmd);
+ install_element(BGP_IPV4_NODE,
+ &neighbor_addpath_tx_bestpath_per_as_cmd);
+ install_element(BGP_IPV4_NODE,
+ &no_neighbor_addpath_tx_bestpath_per_as_cmd);
+ install_element(BGP_IPV4M_NODE,
+ &neighbor_addpath_tx_bestpath_per_as_cmd);
+ install_element(BGP_IPV4M_NODE,
+ &no_neighbor_addpath_tx_bestpath_per_as_cmd);
+ install_element(BGP_IPV4L_NODE,
+ &neighbor_addpath_tx_bestpath_per_as_cmd);
+ install_element(BGP_IPV4L_NODE,
+ &no_neighbor_addpath_tx_bestpath_per_as_cmd);
+ install_element(BGP_IPV6_NODE,
+ &neighbor_addpath_tx_bestpath_per_as_cmd);
+ install_element(BGP_IPV6_NODE,
+ &no_neighbor_addpath_tx_bestpath_per_as_cmd);
+ install_element(BGP_IPV6M_NODE,
+ &neighbor_addpath_tx_bestpath_per_as_cmd);
+ install_element(BGP_IPV6M_NODE,
+ &no_neighbor_addpath_tx_bestpath_per_as_cmd);
+ install_element(BGP_IPV6L_NODE,
+ &neighbor_addpath_tx_bestpath_per_as_cmd);
+ install_element(BGP_IPV6L_NODE,
+ &no_neighbor_addpath_tx_bestpath_per_as_cmd);
+ install_element(BGP_VPNV4_NODE,
+ &neighbor_addpath_tx_bestpath_per_as_cmd);
+ install_element(BGP_VPNV4_NODE,
+ &no_neighbor_addpath_tx_bestpath_per_as_cmd);
+ install_element(BGP_VPNV6_NODE,
+ &neighbor_addpath_tx_bestpath_per_as_cmd);
+ install_element(BGP_VPNV6_NODE,
+ &no_neighbor_addpath_tx_bestpath_per_as_cmd);
+
+ /* "neighbor sender-as-path-loop-detection" commands. */
+ install_element(BGP_NODE, &neighbor_aspath_loop_detection_cmd);
+ install_element(BGP_NODE, &no_neighbor_aspath_loop_detection_cmd);
+
+ /* "neighbor passive" commands. */
+ install_element(BGP_NODE, &neighbor_passive_cmd);
+ install_element(BGP_NODE, &no_neighbor_passive_cmd);
+
+
+ /* "neighbor shutdown" commands. */
+ install_element(BGP_NODE, &neighbor_shutdown_cmd);
+ install_element(BGP_NODE, &no_neighbor_shutdown_cmd);
+ install_element(BGP_NODE, &neighbor_shutdown_msg_cmd);
+ install_element(BGP_NODE, &no_neighbor_shutdown_msg_cmd);
+ install_element(BGP_NODE, &neighbor_shutdown_rtt_cmd);
+ install_element(BGP_NODE, &no_neighbor_shutdown_rtt_cmd);
+
+ /* "neighbor capability extended-nexthop" commands.*/
+ install_element(BGP_NODE, &neighbor_capability_enhe_cmd);
+ install_element(BGP_NODE, &no_neighbor_capability_enhe_cmd);
+
+ /* "neighbor capability orf prefix-list" commands.*/
+ install_element(BGP_NODE, &neighbor_capability_orf_prefix_hidden_cmd);
+ install_element(BGP_NODE,
+ &no_neighbor_capability_orf_prefix_hidden_cmd);
+ install_element(BGP_IPV4_NODE, &neighbor_capability_orf_prefix_cmd);
+ install_element(BGP_IPV4_NODE, &no_neighbor_capability_orf_prefix_cmd);
+ install_element(BGP_IPV4M_NODE, &neighbor_capability_orf_prefix_cmd);
+ install_element(BGP_IPV4M_NODE, &no_neighbor_capability_orf_prefix_cmd);
+ install_element(BGP_IPV4L_NODE, &neighbor_capability_orf_prefix_cmd);
+ install_element(BGP_IPV4L_NODE, &no_neighbor_capability_orf_prefix_cmd);
+ install_element(BGP_IPV6_NODE, &neighbor_capability_orf_prefix_cmd);
+ install_element(BGP_IPV6_NODE, &no_neighbor_capability_orf_prefix_cmd);
+ install_element(BGP_IPV6M_NODE, &neighbor_capability_orf_prefix_cmd);
+ install_element(BGP_IPV6M_NODE, &no_neighbor_capability_orf_prefix_cmd);
+ install_element(BGP_IPV6L_NODE, &neighbor_capability_orf_prefix_cmd);
+ install_element(BGP_IPV6L_NODE, &no_neighbor_capability_orf_prefix_cmd);
+
+ /* "neighbor capability dynamic" commands.*/
+ install_element(BGP_NODE, &neighbor_capability_dynamic_cmd);
+ install_element(BGP_NODE, &no_neighbor_capability_dynamic_cmd);
+
+ /* "neighbor dont-capability-negotiate" commands. */
+ install_element(BGP_NODE, &neighbor_dont_capability_negotiate_cmd);
+ install_element(BGP_NODE, &no_neighbor_dont_capability_negotiate_cmd);
+
+ /* "neighbor ebgp-multihop" commands. */
+ install_element(BGP_NODE, &neighbor_ebgp_multihop_cmd);
+ install_element(BGP_NODE, &neighbor_ebgp_multihop_ttl_cmd);
+ install_element(BGP_NODE, &no_neighbor_ebgp_multihop_cmd);
+
+ /* "neighbor disable-connected-check" commands. */
+ install_element(BGP_NODE, &neighbor_disable_connected_check_cmd);
+ install_element(BGP_NODE, &no_neighbor_disable_connected_check_cmd);
+
+ /* "neighbor disable-link-bw-encoding-ieee" commands. */
+ install_element(BGP_NODE, &neighbor_disable_link_bw_encoding_ieee_cmd);
+ install_element(BGP_NODE,
+ &no_neighbor_disable_link_bw_encoding_ieee_cmd);
+
+ /* "neighbor extended-optional-parameters" commands. */
+ install_element(BGP_NODE, &neighbor_extended_optional_parameters_cmd);
+ install_element(BGP_NODE,
+ &no_neighbor_extended_optional_parameters_cmd);
+
+ /* "neighbor enforce-first-as" commands. */
+ install_element(BGP_NODE, &neighbor_enforce_first_as_cmd);
+ install_element(BGP_NODE, &no_neighbor_enforce_first_as_cmd);
+
+ /* "neighbor description" commands. */
+ install_element(BGP_NODE, &neighbor_description_cmd);
+ install_element(BGP_NODE, &no_neighbor_description_cmd);
+ install_element(BGP_NODE, &no_neighbor_description_comment_cmd);
+
+ /* "neighbor update-source" commands. "*/
+ install_element(BGP_NODE, &neighbor_update_source_cmd);
+ install_element(BGP_NODE, &no_neighbor_update_source_cmd);
+
+ /* "neighbor default-originate" commands. */
+ install_element(BGP_NODE, &neighbor_default_originate_hidden_cmd);
+ install_element(BGP_NODE, &neighbor_default_originate_rmap_hidden_cmd);
+ install_element(BGP_NODE, &no_neighbor_default_originate_hidden_cmd);
+ install_element(BGP_IPV4_NODE, &neighbor_default_originate_cmd);
+ install_element(BGP_IPV4_NODE, &neighbor_default_originate_rmap_cmd);
+ install_element(BGP_IPV4_NODE, &no_neighbor_default_originate_cmd);
+ install_element(BGP_IPV4M_NODE, &neighbor_default_originate_cmd);
+ install_element(BGP_IPV4M_NODE, &neighbor_default_originate_rmap_cmd);
+ install_element(BGP_IPV4M_NODE, &no_neighbor_default_originate_cmd);
+ install_element(BGP_IPV4L_NODE, &neighbor_default_originate_cmd);
+ install_element(BGP_IPV4L_NODE, &neighbor_default_originate_rmap_cmd);
+ install_element(BGP_IPV4L_NODE, &no_neighbor_default_originate_cmd);
+ install_element(BGP_IPV6_NODE, &neighbor_default_originate_cmd);
+ install_element(BGP_IPV6_NODE, &neighbor_default_originate_rmap_cmd);
+ install_element(BGP_IPV6_NODE, &no_neighbor_default_originate_cmd);
+ install_element(BGP_IPV6M_NODE, &neighbor_default_originate_cmd);
+ install_element(BGP_IPV6M_NODE, &neighbor_default_originate_rmap_cmd);
+ install_element(BGP_IPV6M_NODE, &no_neighbor_default_originate_cmd);
+ install_element(BGP_IPV6L_NODE, &neighbor_default_originate_cmd);
+ install_element(BGP_IPV6L_NODE, &neighbor_default_originate_rmap_cmd);
+ install_element(BGP_IPV6L_NODE, &no_neighbor_default_originate_cmd);
+
+ /* "neighbor port" commands. */
+ install_element(BGP_NODE, &neighbor_port_cmd);
+ install_element(BGP_NODE, &no_neighbor_port_cmd);
+
+ /* "neighbor weight" commands. */
+ install_element(BGP_NODE, &neighbor_weight_hidden_cmd);
+ install_element(BGP_NODE, &no_neighbor_weight_hidden_cmd);
+
+ install_element(BGP_IPV4_NODE, &neighbor_weight_cmd);
+ install_element(BGP_IPV4_NODE, &no_neighbor_weight_cmd);
+ install_element(BGP_IPV4M_NODE, &neighbor_weight_cmd);
+ install_element(BGP_IPV4M_NODE, &no_neighbor_weight_cmd);
+ install_element(BGP_IPV4L_NODE, &neighbor_weight_cmd);
+ install_element(BGP_IPV4L_NODE, &no_neighbor_weight_cmd);
+ install_element(BGP_IPV6_NODE, &neighbor_weight_cmd);
+ install_element(BGP_IPV6_NODE, &no_neighbor_weight_cmd);
+ install_element(BGP_IPV6M_NODE, &neighbor_weight_cmd);
+ install_element(BGP_IPV6M_NODE, &no_neighbor_weight_cmd);
+ install_element(BGP_IPV6L_NODE, &neighbor_weight_cmd);
+ install_element(BGP_IPV6L_NODE, &no_neighbor_weight_cmd);
+ install_element(BGP_VPNV4_NODE, &neighbor_weight_cmd);
+ install_element(BGP_VPNV4_NODE, &no_neighbor_weight_cmd);
+ install_element(BGP_VPNV6_NODE, &neighbor_weight_cmd);
+ install_element(BGP_VPNV6_NODE, &no_neighbor_weight_cmd);
+
+ /* "neighbor override-capability" commands. */
+ install_element(BGP_NODE, &neighbor_override_capability_cmd);
+ install_element(BGP_NODE, &no_neighbor_override_capability_cmd);
+
+ /* "neighbor strict-capability-match" commands. */
+ install_element(BGP_NODE, &neighbor_strict_capability_cmd);
+ install_element(BGP_NODE, &no_neighbor_strict_capability_cmd);
+
+ /* "neighbor timers" commands. */
+ install_element(BGP_NODE, &neighbor_timers_cmd);
+ install_element(BGP_NODE, &no_neighbor_timers_cmd);
+
+ /* "neighbor timers connect" commands. */
+ install_element(BGP_NODE, &neighbor_timers_connect_cmd);
+ install_element(BGP_NODE, &no_neighbor_timers_connect_cmd);
+
+ /* "neighbor timers delayopen" commands. */
+ install_element(BGP_NODE, &neighbor_timers_delayopen_cmd);
+ install_element(BGP_NODE, &no_neighbor_timers_delayopen_cmd);
+
+ /* "neighbor advertisement-interval" commands. */
+ install_element(BGP_NODE, &neighbor_advertise_interval_cmd);
+ install_element(BGP_NODE, &no_neighbor_advertise_interval_cmd);
+
+ /* "neighbor interface" commands. */
+ install_element(BGP_NODE, &neighbor_interface_cmd);
+ install_element(BGP_NODE, &no_neighbor_interface_cmd);
+
+ /* "neighbor distribute" commands. */
+ install_element(BGP_NODE, &neighbor_distribute_list_hidden_cmd);
+ install_element(BGP_NODE, &no_neighbor_distribute_list_hidden_cmd);
+ install_element(BGP_IPV4_NODE, &neighbor_distribute_list_cmd);
+ install_element(BGP_IPV4_NODE, &no_neighbor_distribute_list_cmd);
+ install_element(BGP_IPV4M_NODE, &neighbor_distribute_list_cmd);
+ install_element(BGP_IPV4M_NODE, &no_neighbor_distribute_list_cmd);
+ install_element(BGP_IPV4L_NODE, &neighbor_distribute_list_cmd);
+ install_element(BGP_IPV4L_NODE, &no_neighbor_distribute_list_cmd);
+ install_element(BGP_IPV6_NODE, &neighbor_distribute_list_cmd);
+ install_element(BGP_IPV6_NODE, &no_neighbor_distribute_list_cmd);
+ install_element(BGP_IPV6M_NODE, &neighbor_distribute_list_cmd);
+ install_element(BGP_IPV6M_NODE, &no_neighbor_distribute_list_cmd);
+ install_element(BGP_IPV6L_NODE, &neighbor_distribute_list_cmd);
+ install_element(BGP_IPV6L_NODE, &no_neighbor_distribute_list_cmd);
+ install_element(BGP_VPNV4_NODE, &neighbor_distribute_list_cmd);
+ install_element(BGP_VPNV4_NODE, &no_neighbor_distribute_list_cmd);
+ install_element(BGP_VPNV6_NODE, &neighbor_distribute_list_cmd);
+ install_element(BGP_VPNV6_NODE, &no_neighbor_distribute_list_cmd);
+
+ /* "neighbor prefix-list" commands. */
+ install_element(BGP_NODE, &neighbor_prefix_list_hidden_cmd);
+ install_element(BGP_NODE, &no_neighbor_prefix_list_hidden_cmd);
+ install_element(BGP_IPV4_NODE, &neighbor_prefix_list_cmd);
+ install_element(BGP_IPV4_NODE, &no_neighbor_prefix_list_cmd);
+ install_element(BGP_IPV4M_NODE, &neighbor_prefix_list_cmd);
+ install_element(BGP_IPV4M_NODE, &no_neighbor_prefix_list_cmd);
+ install_element(BGP_IPV4L_NODE, &neighbor_prefix_list_cmd);
+ install_element(BGP_IPV4L_NODE, &no_neighbor_prefix_list_cmd);
+ install_element(BGP_IPV6_NODE, &neighbor_prefix_list_cmd);
+ install_element(BGP_IPV6_NODE, &no_neighbor_prefix_list_cmd);
+ install_element(BGP_IPV6M_NODE, &neighbor_prefix_list_cmd);
+ install_element(BGP_IPV6M_NODE, &no_neighbor_prefix_list_cmd);
+ install_element(BGP_IPV6L_NODE, &neighbor_prefix_list_cmd);
+ install_element(BGP_IPV6L_NODE, &no_neighbor_prefix_list_cmd);
+ install_element(BGP_VPNV4_NODE, &neighbor_prefix_list_cmd);
+ install_element(BGP_VPNV4_NODE, &no_neighbor_prefix_list_cmd);
+ install_element(BGP_VPNV6_NODE, &neighbor_prefix_list_cmd);
+ install_element(BGP_VPNV6_NODE, &no_neighbor_prefix_list_cmd);
+ install_element(BGP_FLOWSPECV4_NODE, &neighbor_prefix_list_cmd);
+ install_element(BGP_FLOWSPECV4_NODE, &no_neighbor_prefix_list_cmd);
+ install_element(BGP_FLOWSPECV6_NODE, &neighbor_prefix_list_cmd);
+ install_element(BGP_FLOWSPECV6_NODE, &no_neighbor_prefix_list_cmd);
+
+ /* "neighbor filter-list" commands. */
+ install_element(BGP_NODE, &neighbor_filter_list_hidden_cmd);
+ install_element(BGP_NODE, &no_neighbor_filter_list_hidden_cmd);
+ install_element(BGP_IPV4_NODE, &neighbor_filter_list_cmd);
+ install_element(BGP_IPV4_NODE, &no_neighbor_filter_list_cmd);
+ install_element(BGP_IPV4M_NODE, &neighbor_filter_list_cmd);
+ install_element(BGP_IPV4M_NODE, &no_neighbor_filter_list_cmd);
+ install_element(BGP_IPV4L_NODE, &neighbor_filter_list_cmd);
+ install_element(BGP_IPV4L_NODE, &no_neighbor_filter_list_cmd);
+ install_element(BGP_IPV6_NODE, &neighbor_filter_list_cmd);
+ install_element(BGP_IPV6_NODE, &no_neighbor_filter_list_cmd);
+ install_element(BGP_IPV6M_NODE, &neighbor_filter_list_cmd);
+ install_element(BGP_IPV6M_NODE, &no_neighbor_filter_list_cmd);
+ install_element(BGP_IPV6L_NODE, &neighbor_filter_list_cmd);
+ install_element(BGP_IPV6L_NODE, &no_neighbor_filter_list_cmd);
+ install_element(BGP_VPNV4_NODE, &neighbor_filter_list_cmd);
+ install_element(BGP_VPNV4_NODE, &no_neighbor_filter_list_cmd);
+ install_element(BGP_VPNV6_NODE, &neighbor_filter_list_cmd);
+ install_element(BGP_VPNV6_NODE, &no_neighbor_filter_list_cmd);
+ install_element(BGP_FLOWSPECV4_NODE, &neighbor_filter_list_cmd);
+ install_element(BGP_FLOWSPECV4_NODE, &no_neighbor_filter_list_cmd);
+ install_element(BGP_FLOWSPECV6_NODE, &neighbor_filter_list_cmd);
+ install_element(BGP_FLOWSPECV6_NODE, &no_neighbor_filter_list_cmd);
+
+ /* "neighbor route-map" commands. */
+ install_element(BGP_NODE, &neighbor_route_map_hidden_cmd);
+ install_element(BGP_NODE, &no_neighbor_route_map_hidden_cmd);
+ install_element(BGP_IPV4_NODE, &neighbor_route_map_cmd);
+ install_element(BGP_IPV4_NODE, &no_neighbor_route_map_cmd);
+ install_element(BGP_IPV4M_NODE, &neighbor_route_map_cmd);
+ install_element(BGP_IPV4M_NODE, &no_neighbor_route_map_cmd);
+ install_element(BGP_IPV4L_NODE, &neighbor_route_map_cmd);
+ install_element(BGP_IPV4L_NODE, &no_neighbor_route_map_cmd);
+ install_element(BGP_IPV6_NODE, &neighbor_route_map_cmd);
+ install_element(BGP_IPV6_NODE, &no_neighbor_route_map_cmd);
+ install_element(BGP_IPV6M_NODE, &neighbor_route_map_cmd);
+ install_element(BGP_IPV6M_NODE, &no_neighbor_route_map_cmd);
+ install_element(BGP_IPV6L_NODE, &neighbor_route_map_cmd);
+ install_element(BGP_IPV6L_NODE, &no_neighbor_route_map_cmd);
+ install_element(BGP_VPNV4_NODE, &neighbor_route_map_cmd);
+ install_element(BGP_VPNV4_NODE, &no_neighbor_route_map_cmd);
+ install_element(BGP_VPNV6_NODE, &neighbor_route_map_cmd);
+ install_element(BGP_VPNV6_NODE, &no_neighbor_route_map_cmd);
+ install_element(BGP_FLOWSPECV4_NODE, &neighbor_route_map_cmd);
+ install_element(BGP_FLOWSPECV4_NODE, &no_neighbor_route_map_cmd);
+ install_element(BGP_FLOWSPECV6_NODE, &neighbor_route_map_cmd);
+ install_element(BGP_FLOWSPECV6_NODE, &no_neighbor_route_map_cmd);
+ install_element(BGP_EVPN_NODE, &neighbor_route_map_cmd);
+ install_element(BGP_EVPN_NODE, &no_neighbor_route_map_cmd);
+
+ /* "neighbor unsuppress-map" commands. */
+ install_element(BGP_NODE, &neighbor_unsuppress_map_hidden_cmd);
+ install_element(BGP_NODE, &no_neighbor_unsuppress_map_hidden_cmd);
+ install_element(BGP_IPV4_NODE, &neighbor_unsuppress_map_cmd);
+ install_element(BGP_IPV4_NODE, &no_neighbor_unsuppress_map_cmd);
+ install_element(BGP_IPV4M_NODE, &neighbor_unsuppress_map_cmd);
+ install_element(BGP_IPV4M_NODE, &no_neighbor_unsuppress_map_cmd);
+ install_element(BGP_IPV4L_NODE, &neighbor_unsuppress_map_cmd);
+ install_element(BGP_IPV4L_NODE, &no_neighbor_unsuppress_map_cmd);
+ install_element(BGP_IPV6_NODE, &neighbor_unsuppress_map_cmd);
+ install_element(BGP_IPV6_NODE, &no_neighbor_unsuppress_map_cmd);
+ install_element(BGP_IPV6M_NODE, &neighbor_unsuppress_map_cmd);
+ install_element(BGP_IPV6M_NODE, &no_neighbor_unsuppress_map_cmd);
+ install_element(BGP_IPV6L_NODE, &neighbor_unsuppress_map_cmd);
+ install_element(BGP_IPV6L_NODE, &no_neighbor_unsuppress_map_cmd);
+ install_element(BGP_VPNV4_NODE, &neighbor_unsuppress_map_cmd);
+ install_element(BGP_VPNV4_NODE, &no_neighbor_unsuppress_map_cmd);
+ install_element(BGP_VPNV6_NODE, &neighbor_unsuppress_map_cmd);
+ install_element(BGP_VPNV6_NODE, &no_neighbor_unsuppress_map_cmd);
+
+ /* "neighbor advertise-map" commands. */
+ install_element(BGP_NODE, &bgp_condadv_period_cmd);
+ install_element(BGP_NODE, &neighbor_advertise_map_hidden_cmd);
+ install_element(BGP_IPV4_NODE, &neighbor_advertise_map_cmd);
+ install_element(BGP_IPV4M_NODE, &neighbor_advertise_map_cmd);
+ install_element(BGP_IPV4L_NODE, &neighbor_advertise_map_cmd);
+ install_element(BGP_IPV6_NODE, &neighbor_advertise_map_cmd);
+ install_element(BGP_IPV6M_NODE, &neighbor_advertise_map_cmd);
+ install_element(BGP_IPV6L_NODE, &neighbor_advertise_map_cmd);
+ install_element(BGP_VPNV4_NODE, &neighbor_advertise_map_cmd);
+ install_element(BGP_VPNV6_NODE, &neighbor_advertise_map_cmd);
+
+ /* neighbor maximum-prefix-out commands. */
+ install_element(BGP_NODE, &neighbor_maximum_prefix_out_cmd);
+ install_element(BGP_NODE, &no_neighbor_maximum_prefix_out_cmd);
+ install_element(BGP_IPV4_NODE, &neighbor_maximum_prefix_out_cmd);
+ install_element(BGP_IPV4_NODE, &no_neighbor_maximum_prefix_out_cmd);
+ install_element(BGP_IPV4M_NODE, &neighbor_maximum_prefix_out_cmd);
+ install_element(BGP_IPV4M_NODE, &no_neighbor_maximum_prefix_out_cmd);
+ install_element(BGP_IPV4L_NODE, &neighbor_maximum_prefix_out_cmd);
+ install_element(BGP_IPV4L_NODE, &no_neighbor_maximum_prefix_out_cmd);
+ install_element(BGP_IPV6_NODE, &neighbor_maximum_prefix_out_cmd);
+ install_element(BGP_IPV6_NODE, &no_neighbor_maximum_prefix_out_cmd);
+ install_element(BGP_IPV6M_NODE, &neighbor_maximum_prefix_out_cmd);
+ install_element(BGP_IPV6M_NODE, &no_neighbor_maximum_prefix_out_cmd);
+ install_element(BGP_IPV6L_NODE, &neighbor_maximum_prefix_out_cmd);
+ install_element(BGP_IPV6L_NODE, &no_neighbor_maximum_prefix_out_cmd);
+ install_element(BGP_VPNV4_NODE, &neighbor_maximum_prefix_out_cmd);
+ install_element(BGP_VPNV4_NODE, &no_neighbor_maximum_prefix_out_cmd);
+ install_element(BGP_VPNV6_NODE, &neighbor_maximum_prefix_out_cmd);
+ install_element(BGP_VPNV6_NODE, &no_neighbor_maximum_prefix_out_cmd);
+
+ /* "neighbor maximum-prefix" commands. */
+ install_element(BGP_NODE, &neighbor_maximum_prefix_hidden_cmd);
+ install_element(BGP_NODE,
+ &neighbor_maximum_prefix_threshold_hidden_cmd);
+ install_element(BGP_NODE, &neighbor_maximum_prefix_warning_hidden_cmd);
+ install_element(BGP_NODE,
+ &neighbor_maximum_prefix_threshold_warning_hidden_cmd);
+ install_element(BGP_NODE, &neighbor_maximum_prefix_restart_hidden_cmd);
+ install_element(BGP_NODE,
+ &neighbor_maximum_prefix_threshold_restart_hidden_cmd);
+ install_element(BGP_NODE, &no_neighbor_maximum_prefix_hidden_cmd);
+ install_element(BGP_IPV4_NODE, &neighbor_maximum_prefix_cmd);
+ install_element(BGP_IPV4_NODE, &neighbor_maximum_prefix_threshold_cmd);
+ install_element(BGP_IPV4_NODE, &neighbor_maximum_prefix_warning_cmd);
+ install_element(BGP_IPV4_NODE,
+ &neighbor_maximum_prefix_threshold_warning_cmd);
+ install_element(BGP_IPV4_NODE, &neighbor_maximum_prefix_restart_cmd);
+ install_element(BGP_IPV4_NODE,
+ &neighbor_maximum_prefix_threshold_restart_cmd);
+ install_element(BGP_IPV4_NODE, &no_neighbor_maximum_prefix_cmd);
+ install_element(BGP_IPV4M_NODE, &neighbor_maximum_prefix_cmd);
+ install_element(BGP_IPV4M_NODE, &neighbor_maximum_prefix_threshold_cmd);
+ install_element(BGP_IPV4M_NODE, &neighbor_maximum_prefix_warning_cmd);
+ install_element(BGP_IPV4M_NODE,
+ &neighbor_maximum_prefix_threshold_warning_cmd);
+ install_element(BGP_IPV4M_NODE, &neighbor_maximum_prefix_restart_cmd);
+ install_element(BGP_IPV4M_NODE,
+ &neighbor_maximum_prefix_threshold_restart_cmd);
+ install_element(BGP_IPV4M_NODE, &no_neighbor_maximum_prefix_cmd);
+ install_element(BGP_IPV4L_NODE, &neighbor_maximum_prefix_cmd);
+ install_element(BGP_IPV4L_NODE, &neighbor_maximum_prefix_threshold_cmd);
+ install_element(BGP_IPV4L_NODE, &neighbor_maximum_prefix_warning_cmd);
+ install_element(BGP_IPV4L_NODE,
+ &neighbor_maximum_prefix_threshold_warning_cmd);
+ install_element(BGP_IPV4L_NODE, &neighbor_maximum_prefix_restart_cmd);
+ install_element(BGP_IPV4L_NODE,
+ &neighbor_maximum_prefix_threshold_restart_cmd);
+ install_element(BGP_IPV4L_NODE, &no_neighbor_maximum_prefix_cmd);
+ install_element(BGP_IPV6_NODE, &neighbor_maximum_prefix_cmd);
+ install_element(BGP_IPV6_NODE, &neighbor_maximum_prefix_threshold_cmd);
+ install_element(BGP_IPV6_NODE, &neighbor_maximum_prefix_warning_cmd);
+ install_element(BGP_IPV6_NODE,
+ &neighbor_maximum_prefix_threshold_warning_cmd);
+ install_element(BGP_IPV6_NODE, &neighbor_maximum_prefix_restart_cmd);
+ install_element(BGP_IPV6_NODE,
+ &neighbor_maximum_prefix_threshold_restart_cmd);
+ install_element(BGP_IPV6_NODE, &no_neighbor_maximum_prefix_cmd);
+ install_element(BGP_IPV6M_NODE, &neighbor_maximum_prefix_cmd);
+ install_element(BGP_IPV6M_NODE, &neighbor_maximum_prefix_threshold_cmd);
+ install_element(BGP_IPV6M_NODE, &neighbor_maximum_prefix_warning_cmd);
+ install_element(BGP_IPV6M_NODE,
+ &neighbor_maximum_prefix_threshold_warning_cmd);
+ install_element(BGP_IPV6M_NODE, &neighbor_maximum_prefix_restart_cmd);
+ install_element(BGP_IPV6M_NODE,
+ &neighbor_maximum_prefix_threshold_restart_cmd);
+ install_element(BGP_IPV6M_NODE, &no_neighbor_maximum_prefix_cmd);
+ install_element(BGP_IPV6L_NODE, &neighbor_maximum_prefix_cmd);
+ install_element(BGP_IPV6L_NODE, &neighbor_maximum_prefix_threshold_cmd);
+ install_element(BGP_IPV6L_NODE, &neighbor_maximum_prefix_warning_cmd);
+ install_element(BGP_IPV6L_NODE,
+ &neighbor_maximum_prefix_threshold_warning_cmd);
+ install_element(BGP_IPV6L_NODE, &neighbor_maximum_prefix_restart_cmd);
+ install_element(BGP_IPV6L_NODE,
+ &neighbor_maximum_prefix_threshold_restart_cmd);
+ install_element(BGP_IPV6L_NODE, &no_neighbor_maximum_prefix_cmd);
+ install_element(BGP_VPNV4_NODE, &neighbor_maximum_prefix_cmd);
+ install_element(BGP_VPNV4_NODE, &neighbor_maximum_prefix_threshold_cmd);
+ install_element(BGP_VPNV4_NODE, &neighbor_maximum_prefix_warning_cmd);
+ install_element(BGP_VPNV4_NODE,
+ &neighbor_maximum_prefix_threshold_warning_cmd);
+ install_element(BGP_VPNV4_NODE, &neighbor_maximum_prefix_restart_cmd);
+ install_element(BGP_VPNV4_NODE,
+ &neighbor_maximum_prefix_threshold_restart_cmd);
+ install_element(BGP_VPNV4_NODE, &no_neighbor_maximum_prefix_cmd);
+ install_element(BGP_VPNV6_NODE, &neighbor_maximum_prefix_cmd);
+ install_element(BGP_VPNV6_NODE, &neighbor_maximum_prefix_threshold_cmd);
+ install_element(BGP_VPNV6_NODE, &neighbor_maximum_prefix_warning_cmd);
+ install_element(BGP_VPNV6_NODE,
+ &neighbor_maximum_prefix_threshold_warning_cmd);
+ install_element(BGP_VPNV6_NODE, &neighbor_maximum_prefix_restart_cmd);
+ install_element(BGP_VPNV6_NODE,
+ &neighbor_maximum_prefix_threshold_restart_cmd);
+ install_element(BGP_VPNV6_NODE, &no_neighbor_maximum_prefix_cmd);
+
+ /* "neighbor allowas-in" */
+ install_element(BGP_NODE, &neighbor_allowas_in_hidden_cmd);
+ install_element(BGP_NODE, &no_neighbor_allowas_in_hidden_cmd);
+ install_element(BGP_IPV4_NODE, &neighbor_allowas_in_cmd);
+ install_element(BGP_IPV4_NODE, &no_neighbor_allowas_in_cmd);
+ install_element(BGP_IPV4M_NODE, &neighbor_allowas_in_cmd);
+ install_element(BGP_IPV4M_NODE, &no_neighbor_allowas_in_cmd);
+ install_element(BGP_IPV4L_NODE, &neighbor_allowas_in_cmd);
+ install_element(BGP_IPV4L_NODE, &no_neighbor_allowas_in_cmd);
+ install_element(BGP_IPV6_NODE, &neighbor_allowas_in_cmd);
+ install_element(BGP_IPV6_NODE, &no_neighbor_allowas_in_cmd);
+ install_element(BGP_IPV6M_NODE, &neighbor_allowas_in_cmd);
+ install_element(BGP_IPV6M_NODE, &no_neighbor_allowas_in_cmd);
+ install_element(BGP_IPV6L_NODE, &neighbor_allowas_in_cmd);
+ install_element(BGP_IPV6L_NODE, &no_neighbor_allowas_in_cmd);
+ install_element(BGP_VPNV4_NODE, &neighbor_allowas_in_cmd);
+ install_element(BGP_VPNV4_NODE, &no_neighbor_allowas_in_cmd);
+ install_element(BGP_VPNV6_NODE, &neighbor_allowas_in_cmd);
+ install_element(BGP_VPNV6_NODE, &no_neighbor_allowas_in_cmd);
+ install_element(BGP_EVPN_NODE, &neighbor_allowas_in_cmd);
+ install_element(BGP_EVPN_NODE, &no_neighbor_allowas_in_cmd);
+
+ /* "neighbor soo" */
+ install_element(BGP_IPV4_NODE, &neighbor_soo_cmd);
+ install_element(BGP_IPV4_NODE, &no_neighbor_soo_cmd);
+ install_element(BGP_IPV4M_NODE, &neighbor_soo_cmd);
+ install_element(BGP_IPV4M_NODE, &no_neighbor_soo_cmd);
+ install_element(BGP_IPV4L_NODE, &neighbor_soo_cmd);
+ install_element(BGP_IPV4L_NODE, &no_neighbor_soo_cmd);
+ install_element(BGP_IPV6_NODE, &neighbor_soo_cmd);
+ install_element(BGP_IPV6_NODE, &no_neighbor_soo_cmd);
+ install_element(BGP_IPV6M_NODE, &neighbor_soo_cmd);
+ install_element(BGP_IPV6M_NODE, &no_neighbor_soo_cmd);
+ install_element(BGP_IPV6L_NODE, &neighbor_soo_cmd);
+ install_element(BGP_IPV6L_NODE, &no_neighbor_soo_cmd);
+ install_element(BGP_VPNV4_NODE, &neighbor_soo_cmd);
+ install_element(BGP_VPNV4_NODE, &no_neighbor_soo_cmd);
+ install_element(BGP_VPNV6_NODE, &neighbor_soo_cmd);
+ install_element(BGP_VPNV6_NODE, &no_neighbor_soo_cmd);
+ install_element(BGP_EVPN_NODE, &neighbor_soo_cmd);
+ install_element(BGP_EVPN_NODE, &no_neighbor_soo_cmd);
+
+ /* address-family commands. */
+ install_element(BGP_NODE, &address_family_ipv4_safi_cmd);
+ install_element(BGP_NODE, &address_family_ipv6_safi_cmd);
+#ifdef KEEP_OLD_VPN_COMMANDS
+ install_element(BGP_NODE, &address_family_vpnv4_cmd);
+ install_element(BGP_NODE, &address_family_vpnv6_cmd);
+#endif /* KEEP_OLD_VPN_COMMANDS */
+
+ install_element(BGP_NODE, &address_family_evpn_cmd);
+
+ /* "exit-address-family" command. */
+ install_element(BGP_IPV4_NODE, &exit_address_family_cmd);
+ install_element(BGP_IPV4M_NODE, &exit_address_family_cmd);
+ install_element(BGP_IPV4L_NODE, &exit_address_family_cmd);
+ install_element(BGP_IPV6_NODE, &exit_address_family_cmd);
+ install_element(BGP_IPV6M_NODE, &exit_address_family_cmd);
+ install_element(BGP_IPV6L_NODE, &exit_address_family_cmd);
+ install_element(BGP_VPNV4_NODE, &exit_address_family_cmd);
+ install_element(BGP_VPNV6_NODE, &exit_address_family_cmd);
+ install_element(BGP_FLOWSPECV4_NODE, &exit_address_family_cmd);
+ install_element(BGP_FLOWSPECV6_NODE, &exit_address_family_cmd);
+ install_element(BGP_EVPN_NODE, &exit_address_family_cmd);
+
+ /* BGP retain all route-target */
+ install_element(BGP_VPNV4_NODE, &bgp_retain_route_target_cmd);
+ install_element(BGP_VPNV6_NODE, &bgp_retain_route_target_cmd);
+
+ /* "clear ip bgp commands" */
+ install_element(ENABLE_NODE, &clear_ip_bgp_all_cmd);
+
+ /* clear ip bgp prefix */
+ install_element(ENABLE_NODE, &clear_ip_bgp_prefix_cmd);
+ install_element(ENABLE_NODE, &clear_bgp_ipv6_safi_prefix_cmd);
+ install_element(ENABLE_NODE, &clear_bgp_instance_ipv6_safi_prefix_cmd);
+
+ /* "show [ip] bgp summary" commands. */
+ install_element(VIEW_NODE, &show_bgp_instance_all_ipv6_updgrps_cmd);
+ install_element(VIEW_NODE, &show_bgp_l2vpn_evpn_updgrps_cmd);
+ install_element(VIEW_NODE, &show_bgp_instance_updgrps_stats_cmd);
+ install_element(VIEW_NODE, &show_bgp_updgrps_stats_cmd);
+ install_element(VIEW_NODE, &show_ip_bgp_instance_updgrps_adj_s_cmd);
+ install_element(VIEW_NODE, &show_ip_bgp_summary_cmd);
+ install_element(VIEW_NODE, &show_ip_bgp_updgrps_cmd);
+
+ /* "show [ip] bgp neighbors" commands. */
+ install_element(VIEW_NODE, &show_ip_bgp_neighbors_cmd);
+
+ install_element(VIEW_NODE, &show_ip_bgp_neighbors_graceful_restart_cmd);
+
+ /* "show [ip] bgp peer-group" commands. */
+ install_element(VIEW_NODE, &show_ip_bgp_peer_groups_cmd);
+
+ /* "show [ip] bgp paths" commands. */
+ install_element(VIEW_NODE, &show_ip_bgp_paths_cmd);
+
+ /* "show [ip] bgp community" commands. */
+ install_element(VIEW_NODE, &show_ip_bgp_community_info_cmd);
+
+ /* "show ip bgp large-community" commands. */
+ install_element(VIEW_NODE, &show_ip_bgp_lcommunity_info_cmd);
+ /* "show [ip] bgp attribute-info" commands. */
+ install_element(VIEW_NODE, &show_ip_bgp_attr_info_cmd);
+ /* "show [ip] bgp route-leak" command */
+ install_element(VIEW_NODE, &show_ip_bgp_route_leak_cmd);
+
+ /* "redistribute" commands. */
+ install_element(BGP_NODE, &bgp_redistribute_ipv4_hidden_cmd);
+ install_element(BGP_NODE, &no_bgp_redistribute_ipv4_hidden_cmd);
+ install_element(BGP_NODE, &bgp_redistribute_ipv4_rmap_hidden_cmd);
+ install_element(BGP_NODE, &bgp_redistribute_ipv4_metric_hidden_cmd);
+ install_element(BGP_NODE,
+ &bgp_redistribute_ipv4_rmap_metric_hidden_cmd);
+ install_element(BGP_NODE,
+ &bgp_redistribute_ipv4_metric_rmap_hidden_cmd);
+ install_element(BGP_NODE, &bgp_redistribute_ipv4_ospf_hidden_cmd);
+ install_element(BGP_NODE, &no_bgp_redistribute_ipv4_ospf_hidden_cmd);
+ install_element(BGP_NODE, &bgp_redistribute_ipv4_ospf_rmap_hidden_cmd);
+ install_element(BGP_NODE,
+ &bgp_redistribute_ipv4_ospf_metric_hidden_cmd);
+ install_element(BGP_NODE,
+ &bgp_redistribute_ipv4_ospf_rmap_metric_hidden_cmd);
+ install_element(BGP_NODE,
+ &bgp_redistribute_ipv4_ospf_metric_rmap_hidden_cmd);
+ install_element(BGP_IPV4_NODE, &bgp_redistribute_ipv4_cmd);
+ install_element(BGP_IPV4_NODE, &no_bgp_redistribute_ipv4_cmd);
+ install_element(BGP_IPV4_NODE, &bgp_redistribute_ipv4_rmap_cmd);
+ install_element(BGP_IPV4_NODE, &bgp_redistribute_ipv4_metric_cmd);
+ install_element(BGP_IPV4_NODE, &bgp_redistribute_ipv4_rmap_metric_cmd);
+ install_element(BGP_IPV4_NODE, &bgp_redistribute_ipv4_metric_rmap_cmd);
+ install_element(BGP_IPV4_NODE, &bgp_redistribute_ipv4_ospf_cmd);
+ install_element(BGP_IPV4_NODE, &no_bgp_redistribute_ipv4_ospf_cmd);
+ install_element(BGP_IPV4_NODE, &bgp_redistribute_ipv4_ospf_rmap_cmd);
+ install_element(BGP_IPV4_NODE, &bgp_redistribute_ipv4_ospf_metric_cmd);
+ install_element(BGP_IPV4_NODE,
+ &bgp_redistribute_ipv4_ospf_rmap_metric_cmd);
+ install_element(BGP_IPV4_NODE,
+ &bgp_redistribute_ipv4_ospf_metric_rmap_cmd);
+ install_element(BGP_IPV6_NODE, &bgp_redistribute_ipv6_cmd);
+ install_element(BGP_IPV6_NODE, &no_bgp_redistribute_ipv6_cmd);
+ install_element(BGP_IPV6_NODE, &bgp_redistribute_ipv6_rmap_cmd);
+ install_element(BGP_IPV6_NODE, &bgp_redistribute_ipv6_metric_cmd);
+ install_element(BGP_IPV6_NODE, &bgp_redistribute_ipv6_rmap_metric_cmd);
+ install_element(BGP_IPV6_NODE, &bgp_redistribute_ipv6_metric_rmap_cmd);
+
+ /* import|export vpn [route-map RMAP_NAME] */
+ install_element(BGP_IPV4_NODE, &bgp_imexport_vpn_cmd);
+ install_element(BGP_IPV6_NODE, &bgp_imexport_vpn_cmd);
+
+ install_element(BGP_IPV4_NODE, &bgp_imexport_vrf_cmd);
+ install_element(BGP_IPV6_NODE, &bgp_imexport_vrf_cmd);
+
+ /* ttl_security commands */
+ install_element(BGP_NODE, &neighbor_ttl_security_cmd);
+ install_element(BGP_NODE, &no_neighbor_ttl_security_cmd);
+
+ /* "show [ip] bgp memory" commands. */
+ install_element(VIEW_NODE, &show_bgp_memory_cmd);
+
+ /* "show bgp martian next-hop" */
+ install_element(VIEW_NODE, &show_bgp_martian_nexthop_db_cmd);
+
+ install_element(VIEW_NODE, &show_bgp_mac_hash_cmd);
+
+ /* "show [ip] bgp views" commands. */
+ install_element(VIEW_NODE, &show_bgp_views_cmd);
+
+ /* "show [ip] bgp vrfs" commands. */
+ install_element(VIEW_NODE, &show_bgp_vrfs_cmd);
+
+ /* Community-list. */
+ community_list_vty();
+
+ community_alias_vty();
+
+ /* vpn-policy commands */
+ install_element(BGP_IPV4_NODE, &af_rd_vpn_export_cmd);
+ install_element(BGP_IPV6_NODE, &af_rd_vpn_export_cmd);
+ install_element(BGP_IPV4_NODE, &af_label_vpn_export_cmd);
+ install_element(BGP_IPV6_NODE, &af_label_vpn_export_cmd);
+ install_element(BGP_IPV4_NODE, &af_nexthop_vpn_export_cmd);
+ install_element(BGP_IPV6_NODE, &af_nexthop_vpn_export_cmd);
+ install_element(BGP_IPV4_NODE, &af_rt_vpn_imexport_cmd);
+ install_element(BGP_IPV6_NODE, &af_rt_vpn_imexport_cmd);
+ install_element(BGP_IPV4_NODE, &af_route_map_vpn_imexport_cmd);
+ install_element(BGP_IPV6_NODE, &af_route_map_vpn_imexport_cmd);
+ install_element(BGP_IPV4_NODE, &af_import_vrf_route_map_cmd);
+ install_element(BGP_IPV6_NODE, &af_import_vrf_route_map_cmd);
+
+ install_element(BGP_IPV4_NODE, &af_routetarget_import_cmd);
+ install_element(BGP_IPV6_NODE, &af_routetarget_import_cmd);
+
+ install_element(BGP_IPV4_NODE, &af_no_rd_vpn_export_cmd);
+ install_element(BGP_IPV6_NODE, &af_no_rd_vpn_export_cmd);
+ install_element(BGP_IPV4_NODE, &af_no_label_vpn_export_cmd);
+ install_element(BGP_IPV6_NODE, &af_no_label_vpn_export_cmd);
+ install_element(BGP_IPV4_NODE, &af_no_rt_vpn_imexport_cmd);
+ install_element(BGP_IPV6_NODE, &af_no_rt_vpn_imexport_cmd);
+ install_element(BGP_IPV4_NODE, &af_no_route_map_vpn_imexport_cmd);
+ install_element(BGP_IPV6_NODE, &af_no_route_map_vpn_imexport_cmd);
+ install_element(BGP_IPV4_NODE, &af_no_import_vrf_route_map_cmd);
+ install_element(BGP_IPV6_NODE, &af_no_import_vrf_route_map_cmd);
+
+ /* tcp-mss command */
+ install_element(BGP_NODE, &neighbor_tcp_mss_cmd);
+ install_element(BGP_NODE, &no_neighbor_tcp_mss_cmd);
+
+ /* srv6 commands */
+ install_element(VIEW_NODE, &show_bgp_srv6_cmd);
+ install_element(BGP_NODE, &bgp_segment_routing_srv6_cmd);
+ install_element(BGP_NODE, &no_bgp_segment_routing_srv6_cmd);
+ install_element(BGP_SRV6_NODE, &bgp_srv6_locator_cmd);
+ install_element(BGP_SRV6_NODE, &no_bgp_srv6_locator_cmd);
+ install_element(BGP_IPV4_NODE, &af_sid_vpn_export_cmd);
+ install_element(BGP_IPV6_NODE, &af_sid_vpn_export_cmd);
+
+ bgp_vty_if_init();
+}
+
+#include "memory.h"
+#include "bgp_regex.h"
+#include "bgp_clist.h"
+#include "bgp_ecommunity.h"
+
+/* VTY functions. */
+
+/* Direction value to string conversion. */
+static const char *community_direct_str(int direct)
+{
+ switch (direct) {
+ case COMMUNITY_DENY:
+ return "deny";
+ case COMMUNITY_PERMIT:
+ return "permit";
+ default:
+ return "unknown";
+ }
+}
+
+/* Display error string. */
+static void community_list_perror(struct vty *vty, int ret)
+{
+ switch (ret) {
+ case COMMUNITY_LIST_ERR_CANT_FIND_LIST:
+ vty_out(vty, "%% Can't find community-list\n");
+ break;
+ case COMMUNITY_LIST_ERR_MALFORMED_VAL:
+ vty_out(vty, "%% Malformed community-list value\n");
+ break;
+ case COMMUNITY_LIST_ERR_STANDARD_CONFLICT:
+ vty_out(vty,
+ "%% Community name conflict, previously defined as standard community\n");
+ break;
+ case COMMUNITY_LIST_ERR_EXPANDED_CONFLICT:
+ vty_out(vty,
+ "%% Community name conflict, previously defined as expanded community\n");
+ break;
+ }
+}
+
+/* "community-list" keyword help string. */
+#define COMMUNITY_LIST_STR "Add a community list entry\n"
+
+/*community-list standard */
+DEFUN (community_list_standard,
+ bgp_community_list_standard_cmd,
+ "bgp community-list <(1-99)|standard COMMUNITY_LIST_NAME> [seq (0-4294967295)] <deny|permit> AA:NN...",
+ BGP_STR
+ COMMUNITY_LIST_STR
+ "Community list number (standard)\n"
+ "Add an standard community-list entry\n"
+ "Community list name\n"
+ "Sequence number of an entry\n"
+ "Sequence number\n"
+ "Specify community to reject\n"
+ "Specify community to accept\n"
+ COMMUNITY_VAL_STR)
+{
+ char *cl_name_or_number = NULL;
+ char *seq = NULL;
+ int direct = 0;
+ int style = COMMUNITY_LIST_STANDARD;
+ int idx = 0;
+
+ if (argv_find(argv, argc, "(0-4294967295)", &idx))
+ seq = argv[idx]->arg;
+
+ idx = 0;
+ argv_find(argv, argc, "(1-99)", &idx);
+ argv_find(argv, argc, "COMMUNITY_LIST_NAME", &idx);
+ cl_name_or_number = argv[idx]->arg;
+ direct = argv_find(argv, argc, "permit", &idx) ? COMMUNITY_PERMIT
+ : COMMUNITY_DENY;
+ argv_find(argv, argc, "AA:NN", &idx);
+ char *str = argv_concat(argv, argc, idx);
+
+ int ret = community_list_set(bgp_clist, cl_name_or_number, str, seq,
+ direct, style);
+
+ XFREE(MTYPE_TMP, str);
+
+ if (ret < 0) {
+ /* Display error string. */
+ community_list_perror(vty, ret);
+ return CMD_WARNING_CONFIG_FAILED;
+ }
+
+ return CMD_SUCCESS;
+}
+
+DEFUN (no_community_list_standard_all,
+ no_bgp_community_list_standard_all_cmd,
+ "no bgp community-list <(1-99)|standard COMMUNITY_LIST_NAME> [seq (0-4294967295)] <deny|permit> AA:NN...",
+ NO_STR
+ BGP_STR
+ COMMUNITY_LIST_STR
+ "Community list number (standard)\n"
+ "Add an standard community-list entry\n"
+ "Community list name\n"
+ "Sequence number of an entry\n"
+ "Sequence number\n"
+ "Specify community to reject\n"
+ "Specify community to accept\n"
+ COMMUNITY_VAL_STR)
+{
+ char *cl_name_or_number = NULL;
+ char *str = NULL;
+ int direct = 0;
+ int style = COMMUNITY_LIST_STANDARD;
+ char *seq = NULL;
+ int idx = 0;
+
+ if (argv_find(argv, argc, "(0-4294967295)", &idx))
+ seq = argv[idx]->arg;
+
+ idx = 0;
+ argv_find(argv, argc, "permit", &idx);
+ argv_find(argv, argc, "deny", &idx);
+
+ if (idx) {
+ direct = argv_find(argv, argc, "permit", &idx)
+ ? COMMUNITY_PERMIT
+ : COMMUNITY_DENY;
+
+ idx = 0;
+ argv_find(argv, argc, "AA:NN", &idx);
+ str = argv_concat(argv, argc, idx);
+ }
+
+ idx = 0;
+ argv_find(argv, argc, "(1-99)", &idx);
+ argv_find(argv, argc, "COMMUNITY_LIST_NAME", &idx);
+ cl_name_or_number = argv[idx]->arg;
+
+ int ret = community_list_unset(bgp_clist, cl_name_or_number, str, seq,
+ direct, style);
+
+ XFREE(MTYPE_TMP, str);
+
+ if (ret < 0) {
+ community_list_perror(vty, ret);
+ return CMD_WARNING_CONFIG_FAILED;
+ }
+
+ return CMD_SUCCESS;
+}
+
+ALIAS(no_community_list_standard_all, no_bgp_community_list_standard_all_list_cmd,
+ "no bgp community-list <(1-99)|standard COMMUNITY_LIST_NAME>",
+ NO_STR BGP_STR COMMUNITY_LIST_STR
+ "Community list number (standard)\n"
+ "Add an standard community-list entry\n"
+ "Community list name\n")
+
+/*community-list expanded */
+DEFUN (community_list_expanded_all,
+ bgp_community_list_expanded_all_cmd,
+ "bgp community-list <(100-500)|expanded COMMUNITY_LIST_NAME> [seq (0-4294967295)] <deny|permit> AA:NN...",
+ BGP_STR
+ COMMUNITY_LIST_STR
+ "Community list number (expanded)\n"
+ "Add an expanded community-list entry\n"
+ "Community list name\n"
+ "Sequence number of an entry\n"
+ "Sequence number\n"
+ "Specify community to reject\n"
+ "Specify community to accept\n"
+ COMMUNITY_VAL_STR)
+{
+ char *cl_name_or_number = NULL;
+ char *seq = NULL;
+ int direct = 0;
+ int style = COMMUNITY_LIST_EXPANDED;
+ int idx = 0;
+
+ if (argv_find(argv, argc, "(0-4294967295)", &idx))
+ seq = argv[idx]->arg;
+
+ idx = 0;
+
+ argv_find(argv, argc, "(100-500)", &idx);
+ argv_find(argv, argc, "COMMUNITY_LIST_NAME", &idx);
+ cl_name_or_number = argv[idx]->arg;
+ direct = argv_find(argv, argc, "permit", &idx) ? COMMUNITY_PERMIT
+ : COMMUNITY_DENY;
+ argv_find(argv, argc, "AA:NN", &idx);
+ char *str = argv_concat(argv, argc, idx);
+
+ int ret = community_list_set(bgp_clist, cl_name_or_number, str, seq,
+ direct, style);
+
+ XFREE(MTYPE_TMP, str);
+
+ if (ret < 0) {
+ /* Display error string. */
+ community_list_perror(vty, ret);
+ return CMD_WARNING_CONFIG_FAILED;
+ }
+
+ return CMD_SUCCESS;
+}
+
+DEFUN (no_community_list_expanded_all,
+ no_bgp_community_list_expanded_all_cmd,
+ "no bgp community-list <(100-500)|expanded COMMUNITY_LIST_NAME> [seq (0-4294967295)] <deny|permit> AA:NN...",
+ NO_STR
+ BGP_STR
+ COMMUNITY_LIST_STR
+ "Community list number (expanded)\n"
+ "Add an expanded community-list entry\n"
+ "Community list name\n"
+ "Sequence number of an entry\n"
+ "Sequence number\n"
+ "Specify community to reject\n"
+ "Specify community to accept\n"
+ COMMUNITY_VAL_STR)
+{
+ char *cl_name_or_number = NULL;
+ char *seq = NULL;
+ char *str = NULL;
+ int direct = 0;
+ int style = COMMUNITY_LIST_EXPANDED;
+ int idx = 0;
+
+ if (argv_find(argv, argc, "(0-4294967295)", &idx))
+ seq = argv[idx]->arg;
+
+ idx = 0;
+ argv_find(argv, argc, "permit", &idx);
+ argv_find(argv, argc, "deny", &idx);
+
+ if (idx) {
+ direct = argv_find(argv, argc, "permit", &idx)
+ ? COMMUNITY_PERMIT
+ : COMMUNITY_DENY;
+
+ idx = 0;
+ argv_find(argv, argc, "AA:NN", &idx);
+ str = argv_concat(argv, argc, idx);
+ }
+
+ idx = 0;
+ argv_find(argv, argc, "(100-500)", &idx);
+ argv_find(argv, argc, "COMMUNITY_LIST_NAME", &idx);
+ cl_name_or_number = argv[idx]->arg;
+
+ int ret = community_list_unset(bgp_clist, cl_name_or_number, str, seq,
+ direct, style);
+
+ XFREE(MTYPE_TMP, str);
+
+ if (ret < 0) {
+ community_list_perror(vty, ret);
+ return CMD_WARNING_CONFIG_FAILED;
+ }
+
+ return CMD_SUCCESS;
+}
+
+ALIAS(no_community_list_expanded_all,
+ no_bgp_community_list_expanded_all_list_cmd,
+ "no bgp community-list <(100-500)|expanded COMMUNITY_LIST_NAME>",
+ NO_STR BGP_STR COMMUNITY_LIST_STR
+ "Community list number (expanded)\n"
+ "Add an expanded community-list entry\n"
+ "Community list name\n")
+
+/* Return configuration string of community-list entry. */
+static const char *community_list_config_str(struct community_entry *entry)
+{
+ const char *str;
+
+ if (entry->any)
+ str = "";
+ else {
+ if (entry->style == COMMUNITY_LIST_STANDARD)
+ str = community_str(entry->u.com, false, false);
+ else if (entry->style == LARGE_COMMUNITY_LIST_STANDARD)
+ str = lcommunity_str(entry->u.lcom, false, false);
+ else
+ str = entry->config;
+ }
+ return str;
+}
+
+static void community_list_show(struct vty *vty, struct community_list *list)
+{
+ struct community_entry *entry;
+
+ for (entry = list->head; entry; entry = entry->next) {
+ if (entry == list->head) {
+ if (all_digit(list->name))
+ vty_out(vty, "Community %s list %s\n",
+ entry->style == COMMUNITY_LIST_STANDARD
+ ? "standard"
+ : "(expanded) access",
+ list->name);
+ else
+ vty_out(vty, "Named Community %s list %s\n",
+ entry->style == COMMUNITY_LIST_STANDARD
+ ? "standard"
+ : "expanded",
+ list->name);
+ }
+ if (entry->any)
+ vty_out(vty, " %s\n",
+ community_direct_str(entry->direct));
+ else
+ vty_out(vty, " %s %s\n",
+ community_direct_str(entry->direct),
+ community_list_config_str(entry));
+ }
+}
+
+DEFUN (show_community_list,
+ show_bgp_community_list_cmd,
+ "show bgp community-list",
+ SHOW_STR
+ BGP_STR
+ "List community-list\n")
+{
+ struct community_list *list;
+ struct community_list_master *cm;
+
+ cm = community_list_master_lookup(bgp_clist, COMMUNITY_LIST_MASTER);
+ if (!cm)
+ return CMD_SUCCESS;
+
+ for (list = cm->num.head; list; list = list->next)
+ community_list_show(vty, list);
+
+ for (list = cm->str.head; list; list = list->next)
+ community_list_show(vty, list);
+
+ return CMD_SUCCESS;
+}
+
+DEFUN (show_community_list_arg,
+ show_bgp_community_list_arg_cmd,
+ "show bgp community-list <(1-500)|COMMUNITY_LIST_NAME> detail",
+ SHOW_STR
+ BGP_STR
+ "List community-list\n"
+ "Community-list number\n"
+ "Community-list name\n"
+ "Detailed information on community-list\n")
+{
+ int idx_comm_list = 3;
+ struct community_list *list;
+
+ list = community_list_lookup(bgp_clist, argv[idx_comm_list]->arg, 0,
+ COMMUNITY_LIST_MASTER);
+ if (!list) {
+ vty_out(vty, "%% Can't find community-list\n");
+ return CMD_WARNING;
+ }
+
+ community_list_show(vty, list);
+
+ return CMD_SUCCESS;
+}
+
+/*
+ * Large Community code.
+ */
+static int lcommunity_list_set_vty(struct vty *vty, int argc,
+ struct cmd_token **argv, int style,
+ int reject_all_digit_name)
+{
+ int ret;
+ int direct;
+ char *str;
+ int idx = 0;
+ char *cl_name;
+ char *seq = NULL;
+
+ if (argv_find(argv, argc, "(0-4294967295)", &idx))
+ seq = argv[idx]->arg;
+
+ idx = 0;
+ direct = argv_find(argv, argc, "permit", &idx) ? COMMUNITY_PERMIT
+ : COMMUNITY_DENY;
+
+ /* All digit name check. */
+ idx = 0;
+ argv_find(argv, argc, "LCOMMUNITY_LIST_NAME", &idx);
+ argv_find(argv, argc, "(1-99)", &idx);
+ argv_find(argv, argc, "(100-500)", &idx);
+ cl_name = argv[idx]->arg;
+ if (reject_all_digit_name && all_digit(cl_name)) {
+ vty_out(vty, "%% Community name cannot have all digits\n");
+ return CMD_WARNING_CONFIG_FAILED;
+ }
+
+ idx = 0;
+ argv_find(argv, argc, "AA:BB:CC", &idx);
+ argv_find(argv, argc, "LINE", &idx);
+ /* Concat community string argument. */
+ if (idx)
+ str = argv_concat(argv, argc, idx);
+ else
+ str = NULL;
+
+ ret = lcommunity_list_set(bgp_clist, cl_name, str, seq, direct, style);
+
+ /* Free temporary community list string allocated by
+ argv_concat(). */
+ XFREE(MTYPE_TMP, str);
+
+ if (ret < 0) {
+ community_list_perror(vty, ret);
+ return CMD_WARNING_CONFIG_FAILED;
+ }
+ return CMD_SUCCESS;
+}
+
+static int lcommunity_list_unset_vty(struct vty *vty, int argc,
+ struct cmd_token **argv, int style)
+{
+ int ret;
+ int direct = 0;
+ char *str = NULL;
+ int idx = 0;
+ char *seq = NULL;
+
+ if (argv_find(argv, argc, "(0-4294967295)", &idx))
+ seq = argv[idx]->arg;
+
+ idx = 0;
+ argv_find(argv, argc, "permit", &idx);
+ argv_find(argv, argc, "deny", &idx);
+
+ if (idx) {
+ /* Check the list direct. */
+ if (strncmp(argv[idx]->arg, "p", 1) == 0)
+ direct = COMMUNITY_PERMIT;
+ else
+ direct = COMMUNITY_DENY;
+
+ idx = 0;
+ argv_find(argv, argc, "LINE", &idx);
+ argv_find(argv, argc, "AA:AA:NN", &idx);
+ /* Concat community string argument. */
+ str = argv_concat(argv, argc, idx);
+ }
+
+ idx = 0;
+ argv_find(argv, argc, "(1-99)", &idx);
+ argv_find(argv, argc, "(100-500)", &idx);
+ argv_find(argv, argc, "LCOMMUNITY_LIST_NAME", &idx);
+
+ /* Unset community list. */
+ ret = lcommunity_list_unset(bgp_clist, argv[idx]->arg, str, seq, direct,
+ style);
+
+ /* Free temporary community list string allocated by
+ argv_concat(). */
+ XFREE(MTYPE_TMP, str);
+
+ if (ret < 0) {
+ community_list_perror(vty, ret);
+ return CMD_WARNING_CONFIG_FAILED;
+ }
+
+ return CMD_SUCCESS;
+}
+
+/* "large-community-list" keyword help string. */
+#define LCOMMUNITY_LIST_STR "Add a large community list entry\n"
+#define LCOMMUNITY_VAL_STR "large community in 'aa:bb:cc' format\n"
+
+DEFUN (lcommunity_list_standard,
+ bgp_lcommunity_list_standard_cmd,
+ "bgp large-community-list (1-99) [seq (0-4294967295)] <deny|permit> AA:BB:CC...",
+ BGP_STR
+ LCOMMUNITY_LIST_STR
+ "Large Community list number (standard)\n"
+ "Sequence number of an entry\n"
+ "Sequence number\n"
+ "Specify large community to reject\n"
+ "Specify large community to accept\n"
+ LCOMMUNITY_VAL_STR)
+{
+ return lcommunity_list_set_vty(vty, argc, argv,
+ LARGE_COMMUNITY_LIST_STANDARD, 0);
+}
+
+DEFUN (lcommunity_list_expanded,
+ bgp_lcommunity_list_expanded_cmd,
+ "bgp large-community-list (100-500) [seq (0-4294967295)] <deny|permit> LINE...",
+ BGP_STR
+ LCOMMUNITY_LIST_STR
+ "Large Community list number (expanded)\n"
+ "Sequence number of an entry\n"
+ "Sequence number\n"
+ "Specify large community to reject\n"
+ "Specify large community to accept\n"
+ "An ordered list as a regular-expression\n")
+{
+ return lcommunity_list_set_vty(vty, argc, argv,
+ LARGE_COMMUNITY_LIST_EXPANDED, 0);
+}
+
+DEFUN (lcommunity_list_name_standard,
+ bgp_lcommunity_list_name_standard_cmd,
+ "bgp large-community-list standard LCOMMUNITY_LIST_NAME [seq (0-4294967295)] <deny|permit> AA:BB:CC...",
+ BGP_STR
+ LCOMMUNITY_LIST_STR
+ "Specify standard large-community-list\n"
+ "Large Community list name\n"
+ "Sequence number of an entry\n"
+ "Sequence number\n"
+ "Specify large community to reject\n"
+ "Specify large community to accept\n"
+ LCOMMUNITY_VAL_STR)
+{
+ return lcommunity_list_set_vty(vty, argc, argv,
+ LARGE_COMMUNITY_LIST_STANDARD, 1);
+}
+
+DEFUN (lcommunity_list_name_expanded,
+ bgp_lcommunity_list_name_expanded_cmd,
+ "bgp large-community-list expanded LCOMMUNITY_LIST_NAME [seq (0-4294967295)] <deny|permit> LINE...",
+ BGP_STR
+ LCOMMUNITY_LIST_STR
+ "Specify expanded large-community-list\n"
+ "Large Community list name\n"
+ "Sequence number of an entry\n"
+ "Sequence number\n"
+ "Specify large community to reject\n"
+ "Specify large community to accept\n"
+ "An ordered list as a regular-expression\n")
+{
+ return lcommunity_list_set_vty(vty, argc, argv,
+ LARGE_COMMUNITY_LIST_EXPANDED, 1);
+}
+
+DEFUN (no_lcommunity_list_all,
+ no_bgp_lcommunity_list_all_cmd,
+ "no bgp large-community-list <(1-99)|(100-500)|LCOMMUNITY_LIST_NAME>",
+ NO_STR
+ BGP_STR
+ LCOMMUNITY_LIST_STR
+ "Large Community list number (standard)\n"
+ "Large Community list number (expanded)\n"
+ "Large Community list name\n")
+{
+ return lcommunity_list_unset_vty(vty, argc, argv,
+ LARGE_COMMUNITY_LIST_STANDARD);
+}
+
+DEFUN (no_lcommunity_list_name_standard_all,
+ no_bgp_lcommunity_list_name_standard_all_cmd,
+ "no bgp large-community-list standard LCOMMUNITY_LIST_NAME",
+ NO_STR
+ BGP_STR
+ LCOMMUNITY_LIST_STR
+ "Specify standard large-community-list\n"
+ "Large Community list name\n")
+{
+ return lcommunity_list_unset_vty(vty, argc, argv,
+ LARGE_COMMUNITY_LIST_STANDARD);
+}
+
+DEFUN (no_lcommunity_list_name_expanded_all,
+ no_bgp_lcommunity_list_name_expanded_all_cmd,
+ "no bgp large-community-list expanded LCOMMUNITY_LIST_NAME",
+ NO_STR
+ BGP_STR
+ LCOMMUNITY_LIST_STR
+ "Specify expanded large-community-list\n"
+ "Large Community list name\n")
+{
+ return lcommunity_list_unset_vty(vty, argc, argv,
+ LARGE_COMMUNITY_LIST_EXPANDED);
+}
+
+DEFUN (no_lcommunity_list_standard,
+ no_bgp_lcommunity_list_standard_cmd,
+ "no bgp large-community-list (1-99) [seq (0-4294967295)] <deny|permit> AA:AA:NN...",
+ NO_STR
+ BGP_STR
+ LCOMMUNITY_LIST_STR
+ "Large Community list number (standard)\n"
+ "Sequence number of an entry\n"
+ "Sequence number\n"
+ "Specify large community to reject\n"
+ "Specify large community to accept\n"
+ LCOMMUNITY_VAL_STR)
+{
+ return lcommunity_list_unset_vty(vty, argc, argv,
+ LARGE_COMMUNITY_LIST_STANDARD);
+}
+
+DEFUN (no_lcommunity_list_expanded,
+ no_bgp_lcommunity_list_expanded_cmd,
+ "no bgp large-community-list (100-500) [seq (0-4294967295)] <deny|permit> LINE...",
+ NO_STR
+ BGP_STR
+ LCOMMUNITY_LIST_STR
+ "Large Community list number (expanded)\n"
+ "Sequence number of an entry\n"
+ "Sequence number\n"
+ "Specify large community to reject\n"
+ "Specify large community to accept\n"
+ "An ordered list as a regular-expression\n")
+{
+ return lcommunity_list_unset_vty(vty, argc, argv,
+ LARGE_COMMUNITY_LIST_EXPANDED);
+}
+
+DEFUN (no_lcommunity_list_name_standard,
+ no_bgp_lcommunity_list_name_standard_cmd,
+ "no bgp large-community-list standard LCOMMUNITY_LIST_NAME [seq (0-4294967295)] <deny|permit> AA:AA:NN...",
+ NO_STR
+ BGP_STR
+ LCOMMUNITY_LIST_STR
+ "Specify standard large-community-list\n"
+ "Large Community list name\n"
+ "Sequence number of an entry\n"
+ "Sequence number\n"
+ "Specify large community to reject\n"
+ "Specify large community to accept\n"
+ LCOMMUNITY_VAL_STR)
+{
+ return lcommunity_list_unset_vty(vty, argc, argv,
+ LARGE_COMMUNITY_LIST_STANDARD);
+}
+
+DEFUN (no_lcommunity_list_name_expanded,
+ no_bgp_lcommunity_list_name_expanded_cmd,
+ "no bgp large-community-list expanded LCOMMUNITY_LIST_NAME [seq (0-4294967295)] <deny|permit> LINE...",
+ NO_STR
+ BGP_STR
+ LCOMMUNITY_LIST_STR
+ "Specify expanded large-community-list\n"
+ "Large community list name\n"
+ "Sequence number of an entry\n"
+ "Sequence number\n"
+ "Specify large community to reject\n"
+ "Specify large community to accept\n"
+ "An ordered list as a regular-expression\n")
+{
+ return lcommunity_list_unset_vty(vty, argc, argv,
+ LARGE_COMMUNITY_LIST_EXPANDED);
+}
+
+static void lcommunity_list_show(struct vty *vty, struct community_list *list)
+{
+ struct community_entry *entry;
+
+ for (entry = list->head; entry; entry = entry->next) {
+ if (entry == list->head) {
+ if (all_digit(list->name))
+ vty_out(vty, "Large community %s list %s\n",
+ entry->style ==
+ LARGE_COMMUNITY_LIST_STANDARD
+ ? "standard"
+ : "(expanded) access",
+ list->name);
+ else
+ vty_out(vty,
+ "Named large community %s list %s\n",
+ entry->style ==
+ LARGE_COMMUNITY_LIST_STANDARD
+ ? "standard"
+ : "expanded",
+ list->name);
+ }
+ if (entry->any)
+ vty_out(vty, " %s\n",
+ community_direct_str(entry->direct));
+ else
+ vty_out(vty, " %s %s\n",
+ community_direct_str(entry->direct),
+ community_list_config_str(entry));
+ }
+}
+
+DEFUN (show_lcommunity_list,
+ show_bgp_lcommunity_list_cmd,
+ "show bgp large-community-list",
+ SHOW_STR
+ BGP_STR
+ "List large-community list\n")
+{
+ struct community_list *list;
+ struct community_list_master *cm;
+
+ cm = community_list_master_lookup(bgp_clist,
+ LARGE_COMMUNITY_LIST_MASTER);
+ if (!cm)
+ return CMD_SUCCESS;
+
+ for (list = cm->num.head; list; list = list->next)
+ lcommunity_list_show(vty, list);
+
+ for (list = cm->str.head; list; list = list->next)
+ lcommunity_list_show(vty, list);
+
+ return CMD_SUCCESS;
+}
+
+DEFUN (show_lcommunity_list_arg,
+ show_bgp_lcommunity_list_arg_cmd,
+ "show bgp large-community-list <(1-500)|LCOMMUNITY_LIST_NAME> detail",
+ SHOW_STR
+ BGP_STR
+ "List large-community list\n"
+ "Large-community-list number\n"
+ "Large-community-list name\n"
+ "Detailed information on large-community-list\n")
+{
+ struct community_list *list;
+
+ list = community_list_lookup(bgp_clist, argv[3]->arg, 0,
+ LARGE_COMMUNITY_LIST_MASTER);
+ if (!list) {
+ vty_out(vty, "%% Can't find large-community-list\n");
+ return CMD_WARNING;
+ }
+
+ lcommunity_list_show(vty, list);
+
+ return CMD_SUCCESS;
+}
+
+/* "extcommunity-list" keyword help string. */
+#define EXTCOMMUNITY_LIST_STR "Add a extended community list entry\n"
+#define EXTCOMMUNITY_VAL_STR "Extended community attribute in 'rt aa:nn_or_IPaddr:nn' OR 'soo aa:nn_or_IPaddr:nn' format\n"
+
+DEFUN (extcommunity_list_standard,
+ bgp_extcommunity_list_standard_cmd,
+ "bgp extcommunity-list <(1-99)|standard EXTCOMMUNITY_LIST_NAME> [seq (0-4294967295)] <deny|permit> AA:NN...",
+ BGP_STR
+ EXTCOMMUNITY_LIST_STR
+ "Extended Community list number (standard)\n"
+ "Specify standard extcommunity-list\n"
+ "Community list name\n"
+ "Sequence number of an entry\n"
+ "Sequence number\n"
+ "Specify community to reject\n"
+ "Specify community to accept\n"
+ EXTCOMMUNITY_VAL_STR)
+{
+ int style = EXTCOMMUNITY_LIST_STANDARD;
+ int direct = 0;
+ char *cl_number_or_name = NULL;
+ char *seq = NULL;
+
+ int idx = 0;
+
+ argv_find(argv, argc, "(1-99)", &idx);
+ argv_find(argv, argc, "EXTCOMMUNITY_LIST_NAME", &idx);
+ cl_number_or_name = argv[idx]->arg;
+
+ if (argv_find(argv, argc, "(0-4294967295)", &idx))
+ seq = argv[idx]->arg;
+
+ direct = argv_find(argv, argc, "permit", &idx) ? COMMUNITY_PERMIT
+ : COMMUNITY_DENY;
+ argv_find(argv, argc, "AA:NN", &idx);
+ char *str = argv_concat(argv, argc, idx);
+
+ int ret = extcommunity_list_set(bgp_clist, cl_number_or_name, str, seq,
+ direct, style);
+
+ XFREE(MTYPE_TMP, str);
+
+ if (ret < 0) {
+ community_list_perror(vty, ret);
+ return CMD_WARNING_CONFIG_FAILED;
+ }
+
+ return CMD_SUCCESS;
+}
+
+DEFUN (extcommunity_list_name_expanded,
+ bgp_extcommunity_list_name_expanded_cmd,
+ "bgp extcommunity-list <(100-500)|expanded EXTCOMMUNITY_LIST_NAME> [seq (0-4294967295)] <deny|permit> LINE...",
+ BGP_STR
+ EXTCOMMUNITY_LIST_STR
+ "Extended Community list number (expanded)\n"
+ "Specify expanded extcommunity-list\n"
+ "Extended Community list name\n"
+ "Sequence number of an entry\n"
+ "Sequence number\n"
+ "Specify community to reject\n"
+ "Specify community to accept\n"
+ "An ordered list as a regular-expression\n")
+{
+ int style = EXTCOMMUNITY_LIST_EXPANDED;
+ int direct = 0;
+ char *cl_number_or_name = NULL;
+ char *seq = NULL;
+ int idx = 0;
+
+ argv_find(argv, argc, "(100-500)", &idx);
+ argv_find(argv, argc, "EXTCOMMUNITY_LIST_NAME", &idx);
+ cl_number_or_name = argv[idx]->arg;
+
+ if (argv_find(argv, argc, "(0-4294967295)", &idx))
+ seq = argv[idx]->arg;
+
+ direct = argv_find(argv, argc, "permit", &idx) ? COMMUNITY_PERMIT
+ : COMMUNITY_DENY;
+ argv_find(argv, argc, "LINE", &idx);
+ char *str = argv_concat(argv, argc, idx);
+
+ int ret = extcommunity_list_set(bgp_clist, cl_number_or_name, str, seq,
+ direct, style);
+
+ XFREE(MTYPE_TMP, str);
+
+ if (ret < 0) {
+ community_list_perror(vty, ret);
+ return CMD_WARNING_CONFIG_FAILED;
+ }
+
+ return CMD_SUCCESS;
+}
+
+DEFUN (no_extcommunity_list_standard_all,
+ no_bgp_extcommunity_list_standard_all_cmd,
+ "no bgp extcommunity-list <(1-99)|standard EXTCOMMUNITY_LIST_NAME> [seq (0-4294967295)] <deny|permit> AA:NN...",
+ NO_STR
+ BGP_STR
+ EXTCOMMUNITY_LIST_STR
+ "Extended Community list number (standard)\n"
+ "Specify standard extcommunity-list\n"
+ "Community list name\n"
+ "Sequence number of an entry\n"
+ "Sequence number\n"
+ "Specify community to reject\n"
+ "Specify community to accept\n"
+ EXTCOMMUNITY_VAL_STR)
+{
+ int style = EXTCOMMUNITY_LIST_STANDARD;
+ int direct = 0;
+ char *cl_number_or_name = NULL;
+ char *str = NULL;
+ char *seq = NULL;
+ int idx = 0;
+
+ if (argv_find(argv, argc, "(0-4294967295)", &idx))
+ seq = argv[idx]->arg;
+
+ idx = 0;
+ argv_find(argv, argc, "permit", &idx);
+ argv_find(argv, argc, "deny", &idx);
+ if (idx) {
+ direct = argv_find(argv, argc, "permit", &idx)
+ ? COMMUNITY_PERMIT
+ : COMMUNITY_DENY;
+
+ idx = 0;
+ argv_find(argv, argc, "AA:NN", &idx);
+ str = argv_concat(argv, argc, idx);
+ }
+
+ idx = 0;
+ argv_find(argv, argc, "(1-99)", &idx);
+ argv_find(argv, argc, "EXTCOMMUNITY_LIST_NAME", &idx);
+ cl_number_or_name = argv[idx]->arg;
+
+ int ret = extcommunity_list_unset(bgp_clist, cl_number_or_name, str,
+ seq, direct, style);
+
+ XFREE(MTYPE_TMP, str);
+
+ if (ret < 0) {
+ community_list_perror(vty, ret);
+ return CMD_WARNING_CONFIG_FAILED;
+ }
+
+ return CMD_SUCCESS;
+}
+
+ALIAS(no_extcommunity_list_standard_all,
+ no_bgp_extcommunity_list_standard_all_list_cmd,
+ "no bgp extcommunity-list <(1-99)|standard EXTCOMMUNITY_LIST_NAME>",
+ NO_STR BGP_STR EXTCOMMUNITY_LIST_STR
+ "Extended Community list number (standard)\n"
+ "Specify standard extcommunity-list\n"
+ "Community list name\n")
+
+DEFUN (no_extcommunity_list_expanded_all,
+ no_bgp_extcommunity_list_expanded_all_cmd,
+ "no bgp extcommunity-list <(100-500)|expanded EXTCOMMUNITY_LIST_NAME> [seq (0-4294967295)] <deny|permit> LINE...",
+ NO_STR
+ BGP_STR
+ EXTCOMMUNITY_LIST_STR
+ "Extended Community list number (expanded)\n"
+ "Specify expanded extcommunity-list\n"
+ "Extended Community list name\n"
+ "Sequence number of an entry\n"
+ "Sequence number\n"
+ "Specify community to reject\n"
+ "Specify community to accept\n"
+ "An ordered list as a regular-expression\n")
+{
+ int style = EXTCOMMUNITY_LIST_EXPANDED;
+ int direct = 0;
+ char *cl_number_or_name = NULL;
+ char *str = NULL;
+ char *seq = NULL;
+ int idx = 0;
+
+ if (argv_find(argv, argc, "(0-4294967295)", &idx))
+ seq = argv[idx]->arg;
+
+ idx = 0;
+ argv_find(argv, argc, "permit", &idx);
+ argv_find(argv, argc, "deny", &idx);
+
+ if (idx) {
+ direct = argv_find(argv, argc, "permit", &idx)
+ ? COMMUNITY_PERMIT
+ : COMMUNITY_DENY;
+
+ idx = 0;
+ argv_find(argv, argc, "LINE", &idx);
+ str = argv_concat(argv, argc, idx);
+ }
+
+ idx = 0;
+ argv_find(argv, argc, "(100-500)", &idx);
+ argv_find(argv, argc, "EXTCOMMUNITY_LIST_NAME", &idx);
+ cl_number_or_name = argv[idx]->arg;
+
+ int ret = extcommunity_list_unset(bgp_clist, cl_number_or_name, str,
+ seq, direct, style);
+
+ XFREE(MTYPE_TMP, str);
+
+ if (ret < 0) {
+ community_list_perror(vty, ret);
+ return CMD_WARNING_CONFIG_FAILED;
+ }
+
+ return CMD_SUCCESS;
+}
+
+ALIAS(no_extcommunity_list_expanded_all,
+ no_bgp_extcommunity_list_expanded_all_list_cmd,
+ "no bgp extcommunity-list <(100-500)|expanded EXTCOMMUNITY_LIST_NAME>",
+ NO_STR BGP_STR EXTCOMMUNITY_LIST_STR
+ "Extended Community list number (expanded)\n"
+ "Specify expanded extcommunity-list\n"
+ "Extended Community list name\n")
+
+static void extcommunity_list_show(struct vty *vty, struct community_list *list)
+{
+ struct community_entry *entry;
+
+ for (entry = list->head; entry; entry = entry->next) {
+ if (entry == list->head) {
+ if (all_digit(list->name))
+ vty_out(vty, "Extended community %s list %s\n",
+ entry->style == EXTCOMMUNITY_LIST_STANDARD
+ ? "standard"
+ : "(expanded) access",
+ list->name);
+ else
+ vty_out(vty,
+ "Named extended community %s list %s\n",
+ entry->style == EXTCOMMUNITY_LIST_STANDARD
+ ? "standard"
+ : "expanded",
+ list->name);
+ }
+ if (entry->any)
+ vty_out(vty, " %s\n",
+ community_direct_str(entry->direct));
+ else
+ vty_out(vty, " %s %s\n",
+ community_direct_str(entry->direct),
+ community_list_config_str(entry));
+ }
+}
+
+DEFUN (show_extcommunity_list,
+ show_bgp_extcommunity_list_cmd,
+ "show bgp extcommunity-list",
+ SHOW_STR
+ BGP_STR
+ "List extended-community list\n")
+{
+ struct community_list *list;
+ struct community_list_master *cm;
+
+ cm = community_list_master_lookup(bgp_clist, EXTCOMMUNITY_LIST_MASTER);
+ if (!cm)
+ return CMD_SUCCESS;
+
+ for (list = cm->num.head; list; list = list->next)
+ extcommunity_list_show(vty, list);
+
+ for (list = cm->str.head; list; list = list->next)
+ extcommunity_list_show(vty, list);
+
+ return CMD_SUCCESS;
+}
+
+DEFUN (show_extcommunity_list_arg,
+ show_bgp_extcommunity_list_arg_cmd,
+ "show bgp extcommunity-list <(1-500)|EXTCOMMUNITY_LIST_NAME> detail",
+ SHOW_STR
+ BGP_STR
+ "List extended-community list\n"
+ "Extcommunity-list number\n"
+ "Extcommunity-list name\n"
+ "Detailed information on extcommunity-list\n")
+{
+ int idx_comm_list = 3;
+ struct community_list *list;
+
+ list = community_list_lookup(bgp_clist, argv[idx_comm_list]->arg, 0,
+ EXTCOMMUNITY_LIST_MASTER);
+ if (!list) {
+ vty_out(vty, "%% Can't find extcommunity-list\n");
+ return CMD_WARNING;
+ }
+
+ extcommunity_list_show(vty, list);
+
+ return CMD_SUCCESS;
+}
+
+/* Display community-list and extcommunity-list configuration. */
+static int community_list_config_write(struct vty *vty)
+{
+ struct community_list *list;
+ struct community_entry *entry;
+ struct community_list_master *cm;
+ int write = 0;
+
+ /* Community-list. */
+ cm = community_list_master_lookup(bgp_clist, COMMUNITY_LIST_MASTER);
+
+ for (list = cm->num.head; list; list = list->next)
+ for (entry = list->head; entry; entry = entry->next) {
+ vty_out(vty,
+ "bgp community-list %s seq %" PRId64 " %s %s\n",
+ list->name, entry->seq,
+ community_direct_str(entry->direct),
+ community_list_config_str(entry));
+ write++;
+ }
+ for (list = cm->str.head; list; list = list->next)
+ for (entry = list->head; entry; entry = entry->next) {
+ vty_out(vty,
+ "bgp community-list %s %s seq %" PRId64 " %s %s\n",
+ entry->style == COMMUNITY_LIST_STANDARD
+ ? "standard"
+ : "expanded",
+ list->name, entry->seq,
+ community_direct_str(entry->direct),
+ community_list_config_str(entry));
+ write++;
+ }
+
+ /* Extcommunity-list. */
+ cm = community_list_master_lookup(bgp_clist, EXTCOMMUNITY_LIST_MASTER);
+
+ for (list = cm->num.head; list; list = list->next)
+ for (entry = list->head; entry; entry = entry->next) {
+ vty_out(vty,
+ "bgp extcommunity-list %s seq %" PRId64 " %s %s\n",
+ list->name, entry->seq,
+ community_direct_str(entry->direct),
+ community_list_config_str(entry));
+ write++;
+ }
+ for (list = cm->str.head; list; list = list->next)
+ for (entry = list->head; entry; entry = entry->next) {
+ vty_out(vty,
+ "bgp extcommunity-list %s %s seq %" PRId64" %s %s\n",
+ entry->style == EXTCOMMUNITY_LIST_STANDARD
+ ? "standard"
+ : "expanded",
+ list->name, entry->seq,
+ community_direct_str(entry->direct),
+ community_list_config_str(entry));
+ write++;
+ }
+
+
+ /* lcommunity-list. */
+ cm = community_list_master_lookup(bgp_clist,
+ LARGE_COMMUNITY_LIST_MASTER);
+
+ for (list = cm->num.head; list; list = list->next)
+ for (entry = list->head; entry; entry = entry->next) {
+ vty_out(vty,
+ "bgp large-community-list %s seq %" PRId64" %s %s\n",
+ list->name, entry->seq,
+ community_direct_str(entry->direct),
+ community_list_config_str(entry));
+ write++;
+ }
+ for (list = cm->str.head; list; list = list->next)
+ for (entry = list->head; entry; entry = entry->next) {
+ vty_out(vty,
+ "bgp large-community-list %s %s seq %" PRId64" %s %s\n",
+
+ entry->style == LARGE_COMMUNITY_LIST_STANDARD
+ ? "standard"
+ : "expanded",
+ list->name, entry->seq, community_direct_str(entry->direct),
+ community_list_config_str(entry));
+ write++;
+ }
+
+ return write;
+}
+
+static int community_list_config_write(struct vty *vty);
+static struct cmd_node community_list_node = {
+ .name = "community list",
+ .node = COMMUNITY_LIST_NODE,
+ .prompt = "",
+ .config_write = community_list_config_write,
+};
+
+static void community_list_vty(void)
+{
+ install_node(&community_list_node);
+
+ /* Community-list. */
+ install_element(CONFIG_NODE, &bgp_community_list_standard_cmd);
+ install_element(CONFIG_NODE, &bgp_community_list_expanded_all_cmd);
+ install_element(CONFIG_NODE, &no_bgp_community_list_standard_all_cmd);
+ install_element(CONFIG_NODE, &no_bgp_community_list_standard_all_list_cmd);
+ install_element(CONFIG_NODE, &no_bgp_community_list_expanded_all_cmd);
+ install_element(CONFIG_NODE, &no_bgp_community_list_expanded_all_list_cmd);
+ install_element(VIEW_NODE, &show_bgp_community_list_cmd);
+ install_element(VIEW_NODE, &show_bgp_community_list_arg_cmd);
+
+ /* Extcommunity-list. */
+ install_element(CONFIG_NODE, &bgp_extcommunity_list_standard_cmd);
+ install_element(CONFIG_NODE, &bgp_extcommunity_list_name_expanded_cmd);
+ install_element(CONFIG_NODE, &no_bgp_extcommunity_list_standard_all_cmd);
+ install_element(CONFIG_NODE,
+ &no_bgp_extcommunity_list_standard_all_list_cmd);
+ install_element(CONFIG_NODE, &no_bgp_extcommunity_list_expanded_all_cmd);
+ install_element(CONFIG_NODE,
+ &no_bgp_extcommunity_list_expanded_all_list_cmd);
+ install_element(VIEW_NODE, &show_bgp_extcommunity_list_cmd);
+ install_element(VIEW_NODE, &show_bgp_extcommunity_list_arg_cmd);
+
+ /* Large Community List */
+ install_element(CONFIG_NODE, &bgp_lcommunity_list_standard_cmd);
+ install_element(CONFIG_NODE, &bgp_lcommunity_list_expanded_cmd);
+ install_element(CONFIG_NODE, &bgp_lcommunity_list_name_standard_cmd);
+ install_element(CONFIG_NODE, &bgp_lcommunity_list_name_expanded_cmd);
+ install_element(CONFIG_NODE, &no_bgp_lcommunity_list_all_cmd);
+ install_element(CONFIG_NODE,
+ &no_bgp_lcommunity_list_name_standard_all_cmd);
+ install_element(CONFIG_NODE,
+ &no_bgp_lcommunity_list_name_expanded_all_cmd);
+ install_element(CONFIG_NODE, &no_bgp_lcommunity_list_standard_cmd);
+ install_element(CONFIG_NODE, &no_bgp_lcommunity_list_expanded_cmd);
+ install_element(CONFIG_NODE, &no_bgp_lcommunity_list_name_standard_cmd);
+ install_element(CONFIG_NODE, &no_bgp_lcommunity_list_name_expanded_cmd);
+ install_element(VIEW_NODE, &show_bgp_lcommunity_list_cmd);
+ install_element(VIEW_NODE, &show_bgp_lcommunity_list_arg_cmd);
+
+ bgp_community_list_command_completion_setup();
+}
+
+static struct cmd_node community_alias_node = {
+ .name = "community alias",
+ .node = COMMUNITY_ALIAS_NODE,
+ .prompt = "",
+ .config_write = bgp_community_alias_write,
+};
+
+void community_alias_vty(void)
+{
+ install_node(&community_alias_node);
+
+ /* Community-list. */
+ install_element(CONFIG_NODE, &bgp_community_alias_cmd);
+
+ bgp_community_alias_command_completion_setup();
+}
diff --git a/bgpd/bgp_vty.h b/bgpd/bgp_vty.h
new file mode 100644
index 0000000..9526b50
--- /dev/null
+++ b/bgpd/bgp_vty.h
@@ -0,0 +1,189 @@
+/* BGP VTY interface.
+ * Copyright (C) 1996, 97, 98, 99, 2000 Kunihiro Ishiguro
+ *
+ * This file is part of GNU Zebra.
+ *
+ * GNU Zebra is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License as published by the
+ * Free Software Foundation; either version 2, or (at your option) any
+ * later version.
+ *
+ * GNU Zebra is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program; see the file COPYING; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
+ */
+
+#ifndef _QUAGGA_BGP_VTY_H
+#define _QUAGGA_BGP_VTY_H
+
+#include "bgpd/bgpd.h"
+#include "stream.h"
+struct bgp;
+
+#define BGP_INSTANCE_HELP_STR "BGP view\nBGP VRF\nView/VRF name\n"
+#define BGP_INSTANCE_ALL_HELP_STR "BGP view\nBGP VRF\nAll Views/VRFs\n"
+
+#define BGP_AF_STR "Address Family\n"
+#define BGP_AF_MODIFIER_STR "Address Family modifier\n"
+#define BGP_AFI_CMD_STR "<ipv4|ipv6>"
+#define BGP_AFI_HELP_STR BGP_AF_STR BGP_AF_STR
+#define BGP_SAFI_CMD_STR "<unicast|multicast|vpn>"
+#define BGP_SAFI_HELP_STR \
+ BGP_AF_MODIFIER_STR BGP_AF_MODIFIER_STR BGP_AF_MODIFIER_STR
+#define BGP_AFI_SAFI_CMD_STR BGP_AFI_CMD_STR" "BGP_SAFI_CMD_STR
+#define BGP_AFI_SAFI_HELP_STR BGP_AFI_HELP_STR BGP_SAFI_HELP_STR
+
+#define BGP_SAFI_WITH_LABEL_CMD_STR "<unicast|multicast|vpn|labeled-unicast|flowspec>"
+#define BGP_SAFI_WITH_LABEL_HELP_STR \
+ BGP_AF_MODIFIER_STR BGP_AF_MODIFIER_STR BGP_AF_MODIFIER_STR \
+ BGP_AF_MODIFIER_STR BGP_AF_MODIFIER_STR
+
+#define SHOW_GR_HEADER \
+ "Codes: GR - Graceful Restart," \
+ " * - Inheriting Global GR Config,\n" \
+ " Restart - GR Mode-Restarting," \
+ " Helper - GR Mode-Helper,\n" \
+ " Disable - GR Mode-Disable.\n\n"
+
+#define BGP_SHOW_SUMMARY_HEADER_ALL \
+ "V AS MsgRcvd MsgSent TblVer InQ OutQ Up/Down State/PfxRcd PfxSnt Desc\n"
+#define BGP_SHOW_SUMMARY_HEADER_ALL_WIDE \
+ "V AS LocalAS MsgRcvd MsgSent TblVer InQ OutQ Up/Down State/PfxRcd PfxSnt Desc\n"
+#define BGP_SHOW_SUMMARY_HEADER_FAILED "EstdCnt DropCnt ResetTime Reason\n"
+
+#define BGP_SHOW_PEER_GR_CAPABILITY(vty, p, use_json, json) \
+ do { \
+ bgp_show_neighbor_graceful_restart_local_mode(vty, p, \
+ use_json, json); \
+ bgp_show_neighbor_graceful_restart_remote_mode( \
+ vty, p, use_json, json); \
+ bgp_show_neighnor_graceful_restart_flags(vty, p, use_json, \
+ json); \
+ bgp_show_neighbor_graceful_restart_time(vty, p, use_json, \
+ json); \
+ bgp_show_neighbor_graceful_restart_capability_per_afi_safi( \
+ vty, p, use_json, json); \
+ } while (0)
+
+#define VTY_BGP_GR_DEFINE_LOOP_VARIABLE \
+ struct peer *peer_loop = NULL; \
+ struct listnode *node = NULL; \
+ struct listnode *nnode = NULL; \
+ bool gr_router_detected = false
+
+#define VTY_BGP_GR_ROUTER_DETECT(_bgp, _peer, _peer_list) \
+ do { \
+ if (_peer->bgp->t_startup) \
+ bgp_peer_gr_flags_update(_peer); \
+ for (ALL_LIST_ELEMENTS(_peer_list, node, nnode, peer_loop)) { \
+ if (CHECK_FLAG(peer_loop->flags, \
+ PEER_FLAG_GRACEFUL_RESTART)) \
+ gr_router_detected = true; \
+ } \
+ } while (0)
+
+
+#define VTY_SEND_BGP_GR_CAPABILITY_TO_ZEBRA(_bgp, _ret) \
+ do { \
+ if (gr_router_detected \
+ && _bgp->present_zebra_gr_state == ZEBRA_GR_DISABLE) { \
+ if (bgp_zebra_send_capabilities(_bgp, false)) \
+ _ret = BGP_ERR_INVALID_VALUE; \
+ } else if (!gr_router_detected \
+ && _bgp->present_zebra_gr_state \
+ == ZEBRA_GR_ENABLE) { \
+ if (bgp_zebra_send_capabilities(_bgp, true)) \
+ _ret = BGP_ERR_INVALID_VALUE; \
+ } \
+ } while (0)
+
+#define VTY_BGP_GR_ROUTER_DETECT_AND_SEND_CAPABILITY_TO_ZEBRA( \
+ _bgp, _peer_list, _ret) \
+ do { \
+ struct peer *peer_loop; \
+ bool gr_router_detected = false; \
+ struct listnode *node = {0}; \
+ struct listnode *nnode = {0}; \
+ for (ALL_LIST_ELEMENTS(_peer_list, node, nnode, peer_loop)) { \
+ if (peer_loop->bgp->t_startup) \
+ bgp_peer_gr_flags_update(peer_loop); \
+ if (CHECK_FLAG(peer_loop->flags, \
+ PEER_FLAG_GRACEFUL_RESTART)) \
+ gr_router_detected = true; \
+ } \
+ if (gr_router_detected \
+ && _bgp->present_zebra_gr_state == ZEBRA_GR_DISABLE) { \
+ if (bgp_zebra_send_capabilities(_bgp, false)) \
+ _ret = BGP_ERR_INVALID_VALUE; \
+ } else if (!gr_router_detected \
+ && _bgp->present_zebra_gr_state \
+ == ZEBRA_GR_ENABLE) { \
+ if (bgp_zebra_send_capabilities(_bgp, true)) \
+ _ret = BGP_ERR_INVALID_VALUE; \
+ } \
+ } while (0)
+
+
+#define PRINT_EOR(_eor_flag) \
+ do { \
+ if (eor_flag) \
+ vty_out(vty, "Yes\n"); \
+ else \
+ vty_out(vty, "No\n"); \
+ } while (0)
+
+#define PRINT_EOR_JSON(_eor_flag) \
+ do { \
+ if (eor_flag) \
+ json_object_boolean_true_add( \
+ json_endofrib_status, \
+ "endOfRibSentAfterUpdate"); \
+ else \
+ json_object_boolean_false_add( \
+ json_endofrib_status, \
+ "endOfRibSentAfterUpdate"); \
+ } while (0)
+
+extern void bgp_clear_soft_in(struct bgp *bgp, afi_t afi, safi_t safi);
+extern void bgp_vty_init(void);
+extern void community_alias_vty(void);
+extern const char *get_afi_safi_str(afi_t afi, safi_t safi, bool for_json);
+extern int bgp_get_vty(struct bgp **bgp, as_t *as, const char *name,
+ enum bgp_instance_type inst_type);
+extern void bgp_config_write_update_delay(struct vty *vty, struct bgp *bgp);
+extern void bgp_config_write_wpkt_quanta(struct vty *vty, struct bgp *bgp);
+extern void bgp_config_write_rpkt_quanta(struct vty *vty, struct bgp *bgp);
+extern void bgp_config_write_listen(struct vty *vty, struct bgp *bgp);
+extern void bgp_config_write_coalesce_time(struct vty *vty, struct bgp *bgp);
+extern int bgp_vty_return(struct vty *vty, int ret);
+extern bool bgp_config_inprocess(void);
+extern struct peer *peer_and_group_lookup_vty(struct vty *vty,
+ const char *peer_str);
+
+extern afi_t bgp_vty_afi_from_str(const char *afi_str);
+
+extern safi_t bgp_vty_safi_from_str(const char *safi_str);
+
+extern int argv_find_and_parse_afi(struct cmd_token **argv, int argc,
+ int *index, afi_t *afi);
+
+extern int argv_find_and_parse_safi(struct cmd_token **argv, int argc,
+ int *index, safi_t *safi);
+
+extern int bgp_vty_find_and_parse_afi_safi_bgp(struct vty *vty,
+ struct cmd_token **argv,
+ int argc, int *idx, afi_t *afi,
+ safi_t *safi, struct bgp **bgp,
+ bool use_json);
+int bgp_vty_find_and_parse_bgp(struct vty *vty, struct cmd_token **argv,
+ int argc, struct bgp **bgp, bool use_json);
+extern int bgp_show_summary_vty(struct vty *vty, const char *name, afi_t afi,
+ safi_t safi, const char *neighbor, int as_type,
+ as_t as, uint16_t show_flags);
+
+#endif /* _QUAGGA_BGP_VTY_H */
diff --git a/bgpd/bgp_zebra.c b/bgpd/bgp_zebra.c
new file mode 100644
index 0000000..57a859c
--- /dev/null
+++ b/bgpd/bgp_zebra.c
@@ -0,0 +1,3838 @@
+/* zebra client
+ * Copyright (C) 1997, 98, 99 Kunihiro Ishiguro
+ *
+ * This file is part of GNU Zebra.
+ *
+ * GNU Zebra is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License as published by the
+ * Free Software Foundation; either version 2, or (at your option) any
+ * later version.
+ *
+ * GNU Zebra is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program; see the file COPYING; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
+ */
+
+#include <zebra.h>
+
+#include "command.h"
+#include "stream.h"
+#include "network.h"
+#include "prefix.h"
+#include "log.h"
+#include "sockunion.h"
+#include "zclient.h"
+#include "routemap.h"
+#include "thread.h"
+#include "queue.h"
+#include "memory.h"
+#include "lib/json.h"
+#include "lib/bfd.h"
+#include "lib/route_opaque.h"
+#include "filter.h"
+#include "mpls.h"
+#include "vxlan.h"
+#include "pbr.h"
+
+#include "bgpd/bgpd.h"
+#include "bgpd/bgp_route.h"
+#include "bgpd/bgp_attr.h"
+#include "bgpd/bgp_aspath.h"
+#include "bgpd/bgp_nexthop.h"
+#include "bgpd/bgp_zebra.h"
+#include "bgpd/bgp_fsm.h"
+#include "bgpd/bgp_debug.h"
+#include "bgpd/bgp_errors.h"
+#include "bgpd/bgp_mpath.h"
+#include "bgpd/bgp_nexthop.h"
+#include "bgpd/bgp_nht.h"
+#include "bgpd/bgp_bfd.h"
+#include "bgpd/bgp_label.h"
+#ifdef ENABLE_BGP_VNC
+#include "bgpd/rfapi/rfapi_backend.h"
+#include "bgpd/rfapi/vnc_export_bgp.h"
+#endif
+#include "bgpd/bgp_evpn.h"
+#include "bgpd/bgp_mplsvpn.h"
+#include "bgpd/bgp_labelpool.h"
+#include "bgpd/bgp_pbr.h"
+#include "bgpd/bgp_evpn_private.h"
+#include "bgpd/bgp_evpn_mh.h"
+#include "bgpd/bgp_mac.h"
+#include "bgpd/bgp_trace.h"
+#include "bgpd/bgp_community.h"
+#include "bgpd/bgp_lcommunity.h"
+
+/* All information about zebra. */
+struct zclient *zclient = NULL;
+
+/* hook to indicate vrf status change for SNMP */
+DEFINE_HOOK(bgp_vrf_status_changed, (struct bgp *bgp, struct interface *ifp),
+ (bgp, ifp));
+
+DEFINE_MTYPE_STATIC(BGPD, BGP_IF_INFO, "BGP interface context");
+
+/* Can we install into zebra? */
+static inline bool bgp_install_info_to_zebra(struct bgp *bgp)
+{
+ if (zclient->sock <= 0)
+ return false;
+
+ if (!IS_BGP_INST_KNOWN_TO_ZEBRA(bgp)) {
+ zlog_debug(
+ "%s: No zebra instance to talk to, not installing information",
+ __func__);
+ return false;
+ }
+
+ return true;
+}
+
+int zclient_num_connects;
+
+/* Router-id update message from zebra. */
+static int bgp_router_id_update(ZAPI_CALLBACK_ARGS)
+{
+ struct prefix router_id;
+
+ zebra_router_id_update_read(zclient->ibuf, &router_id);
+
+ if (BGP_DEBUG(zebra, ZEBRA))
+ zlog_debug("Rx Router Id update VRF %u Id %pFX", vrf_id,
+ &router_id);
+
+ bgp_router_id_zebra_bump(vrf_id, &router_id);
+ return 0;
+}
+
+/* Nexthop update message from zebra. */
+static int bgp_read_nexthop_update(ZAPI_CALLBACK_ARGS)
+{
+ bgp_parse_nexthop_update(cmd, vrf_id);
+ return 0;
+}
+
+/* Set or clear interface on which unnumbered neighbor is configured. This
+ * would in turn cause BGP to initiate or turn off IPv6 RAs on this
+ * interface.
+ */
+static void bgp_update_interface_nbrs(struct bgp *bgp, struct interface *ifp,
+ struct interface *upd_ifp)
+{
+ struct listnode *node, *nnode;
+ struct peer *peer;
+
+ for (ALL_LIST_ELEMENTS(bgp->peer, node, nnode, peer)) {
+ if (peer->conf_if && (strcmp(peer->conf_if, ifp->name) == 0)) {
+ if (upd_ifp) {
+ peer->ifp = upd_ifp;
+ bgp_zebra_initiate_radv(bgp, peer);
+ } else {
+ bgp_zebra_terminate_radv(bgp, peer);
+ peer->ifp = upd_ifp;
+ }
+ }
+ }
+}
+
+static int bgp_read_fec_update(ZAPI_CALLBACK_ARGS)
+{
+ bgp_parse_fec_update();
+ return 0;
+}
+
+static void bgp_start_interface_nbrs(struct bgp *bgp, struct interface *ifp)
+{
+ struct listnode *node, *nnode;
+ struct peer *peer;
+
+ for (ALL_LIST_ELEMENTS(bgp->peer, node, nnode, peer)) {
+ if (peer->conf_if && (strcmp(peer->conf_if, ifp->name) == 0)
+ && !peer_established(peer)) {
+ if (peer_active(peer))
+ BGP_EVENT_ADD(peer, BGP_Stop);
+ BGP_EVENT_ADD(peer, BGP_Start);
+ }
+ }
+}
+
+static void bgp_nbr_connected_add(struct bgp *bgp, struct nbr_connected *ifc)
+{
+ struct listnode *node;
+ struct connected *connected;
+ struct interface *ifp;
+ struct prefix *p;
+
+ /* Kick-off the FSM for any relevant peers only if there is a
+ * valid local address on the interface.
+ */
+ ifp = ifc->ifp;
+ for (ALL_LIST_ELEMENTS_RO(ifp->connected, node, connected)) {
+ p = connected->address;
+ if (p->family == AF_INET6
+ && IN6_IS_ADDR_LINKLOCAL(&p->u.prefix6))
+ break;
+ }
+ if (!connected)
+ return;
+
+ bgp_start_interface_nbrs(bgp, ifp);
+}
+
+static void bgp_nbr_connected_delete(struct bgp *bgp, struct nbr_connected *ifc,
+ int del)
+{
+ struct listnode *node, *nnode;
+ struct peer *peer;
+ struct interface *ifp;
+
+ for (ALL_LIST_ELEMENTS(bgp->peer, node, nnode, peer)) {
+ if (peer->conf_if
+ && (strcmp(peer->conf_if, ifc->ifp->name) == 0)) {
+ peer->last_reset = PEER_DOWN_NBR_ADDR_DEL;
+ BGP_EVENT_ADD(peer, BGP_Stop);
+ }
+ }
+ /* Free neighbor also, if we're asked to. */
+ if (del) {
+ ifp = ifc->ifp;
+ listnode_delete(ifp->nbr_connected, ifc);
+ nbr_connected_free(ifc);
+ }
+}
+
+static int bgp_ifp_destroy(struct interface *ifp)
+{
+ struct bgp *bgp;
+
+ bgp = ifp->vrf->info;
+
+ if (BGP_DEBUG(zebra, ZEBRA))
+ zlog_debug("Rx Intf del VRF %u IF %s", ifp->vrf->vrf_id,
+ ifp->name);
+
+ if (bgp) {
+ bgp_update_interface_nbrs(bgp, ifp, NULL);
+ hook_call(bgp_vrf_status_changed, bgp, ifp);
+ }
+
+ bgp_mac_del_mac_entry(ifp);
+
+ return 0;
+}
+
+static int bgp_ifp_up(struct interface *ifp)
+{
+ struct connected *c;
+ struct nbr_connected *nc;
+ struct listnode *node, *nnode;
+ struct bgp *bgp;
+
+ bgp = ifp->vrf->info;
+
+ bgp_mac_add_mac_entry(ifp);
+
+ if (BGP_DEBUG(zebra, ZEBRA))
+ zlog_debug("Rx Intf up VRF %u IF %s", ifp->vrf->vrf_id,
+ ifp->name);
+
+ if (!bgp)
+ return 0;
+
+ for (ALL_LIST_ELEMENTS(ifp->connected, node, nnode, c))
+ bgp_connected_add(bgp, c);
+
+ for (ALL_LIST_ELEMENTS(ifp->nbr_connected, node, nnode, nc))
+ bgp_nbr_connected_add(bgp, nc);
+
+ hook_call(bgp_vrf_status_changed, bgp, ifp);
+ bgp_nht_ifp_up(ifp);
+
+ return 0;
+}
+
+static int bgp_ifp_down(struct interface *ifp)
+{
+ struct connected *c;
+ struct nbr_connected *nc;
+ struct listnode *node, *nnode;
+ struct bgp *bgp;
+ struct peer *peer;
+
+ bgp = ifp->vrf->info;
+
+ bgp_mac_del_mac_entry(ifp);
+
+ if (BGP_DEBUG(zebra, ZEBRA))
+ zlog_debug("Rx Intf down VRF %u IF %s", ifp->vrf->vrf_id,
+ ifp->name);
+
+ if (!bgp)
+ return 0;
+
+ for (ALL_LIST_ELEMENTS(ifp->connected, node, nnode, c))
+ bgp_connected_delete(bgp, c);
+
+ for (ALL_LIST_ELEMENTS(ifp->nbr_connected, node, nnode, nc))
+ bgp_nbr_connected_delete(bgp, nc, 1);
+
+ /* Fast external-failover */
+ if (!CHECK_FLAG(bgp->flags, BGP_FLAG_NO_FAST_EXT_FAILOVER)) {
+
+ for (ALL_LIST_ELEMENTS(bgp->peer, node, nnode, peer)) {
+ /* Take down directly connected peers. */
+ if ((peer->ttl != BGP_DEFAULT_TTL)
+ && (peer->gtsm_hops != BGP_GTSM_HOPS_CONNECTED))
+ continue;
+
+ if (ifp == peer->nexthop.ifp) {
+ BGP_EVENT_ADD(peer, BGP_Stop);
+ peer->last_reset = PEER_DOWN_IF_DOWN;
+ }
+ }
+ }
+
+ hook_call(bgp_vrf_status_changed, bgp, ifp);
+ bgp_nht_ifp_down(ifp);
+
+ return 0;
+}
+
+static int bgp_interface_address_add(ZAPI_CALLBACK_ARGS)
+{
+ struct connected *ifc;
+ struct bgp *bgp;
+ struct peer *peer;
+ struct prefix *addr;
+ struct listnode *node, *nnode;
+ afi_t afi;
+ safi_t safi;
+
+ bgp = bgp_lookup_by_vrf_id(vrf_id);
+
+ ifc = zebra_interface_address_read(cmd, zclient->ibuf, vrf_id);
+
+ if (ifc == NULL)
+ return 0;
+
+ if (bgp_debug_zebra(ifc->address))
+ zlog_debug("Rx Intf address add VRF %u IF %s addr %pFX", vrf_id,
+ ifc->ifp->name, ifc->address);
+
+ if (!bgp)
+ return 0;
+
+ if (if_is_operative(ifc->ifp)) {
+ bgp_connected_add(bgp, ifc);
+
+ /* If we have learnt of any neighbors on this interface,
+ * check to kick off any BGP interface-based neighbors,
+ * but only if this is a link-local address.
+ */
+ if (IN6_IS_ADDR_LINKLOCAL(&ifc->address->u.prefix6)
+ && !list_isempty(ifc->ifp->nbr_connected))
+ bgp_start_interface_nbrs(bgp, ifc->ifp);
+ else {
+ addr = ifc->address;
+
+ for (ALL_LIST_ELEMENTS(bgp->peer, node, nnode, peer)) {
+ if (addr->family == AF_INET)
+ continue;
+
+ /*
+ * If the Peer's interface name matches the
+ * interface name for which BGP received the
+ * update and if the received interface address
+ * is a globalV6 and if the peer is currently
+ * using a v4-mapped-v6 addr or a link local
+ * address, then copy the Rxed global v6 addr
+ * into peer's v6_global and send updates out
+ * with new nexthop addr.
+ */
+ if ((peer->conf_if &&
+ (strcmp(peer->conf_if, ifc->ifp->name) ==
+ 0)) &&
+ !IN6_IS_ADDR_LINKLOCAL(&addr->u.prefix6) &&
+ ((IS_MAPPED_IPV6(
+ &peer->nexthop.v6_global)) ||
+ IN6_IS_ADDR_LINKLOCAL(
+ &peer->nexthop.v6_global))) {
+
+ if (bgp_debug_zebra(ifc->address)) {
+ zlog_debug(
+ "Update peer %pBP's current intf addr %pI6 and send updates",
+ peer,
+ &peer->nexthop
+ .v6_global);
+ }
+ memcpy(&peer->nexthop.v6_global,
+ &addr->u.prefix6,
+ IPV6_MAX_BYTELEN);
+ FOREACH_AFI_SAFI (afi, safi)
+ bgp_announce_route(peer, afi,
+ safi, true);
+ }
+ }
+ }
+ }
+
+ return 0;
+}
+
+static int bgp_interface_address_delete(ZAPI_CALLBACK_ARGS)
+{
+ struct listnode *node, *nnode;
+ struct connected *ifc;
+ struct peer *peer;
+ struct bgp *bgp;
+ struct prefix *addr;
+
+ bgp = bgp_lookup_by_vrf_id(vrf_id);
+
+ ifc = zebra_interface_address_read(cmd, zclient->ibuf, vrf_id);
+
+ if (ifc == NULL)
+ return 0;
+
+ if (bgp_debug_zebra(ifc->address))
+ zlog_debug("Rx Intf address del VRF %u IF %s addr %pFX", vrf_id,
+ ifc->ifp->name, ifc->address);
+
+ if (bgp && if_is_operative(ifc->ifp)) {
+ bgp_connected_delete(bgp, ifc);
+ }
+
+ addr = ifc->address;
+
+ if (bgp) {
+ /*
+ * When we are using the v6 global as part of the peering
+ * nexthops and we are removing it, then we need to
+ * clear the peer data saved for that nexthop and
+ * cause a re-announcement of the route. Since
+ * we do not want the peering to bounce.
+ */
+ for (ALL_LIST_ELEMENTS(bgp->peer, node, nnode, peer)) {
+ afi_t afi;
+ safi_t safi;
+
+ if (addr->family == AF_INET)
+ continue;
+
+ if (!IN6_IS_ADDR_LINKLOCAL(&addr->u.prefix6)
+ && memcmp(&peer->nexthop.v6_global,
+ &addr->u.prefix6, 16)
+ == 0) {
+ memset(&peer->nexthop.v6_global, 0, 16);
+ FOREACH_AFI_SAFI (afi, safi)
+ bgp_announce_route(peer, afi, safi,
+ true);
+ }
+ }
+ }
+
+ connected_free(&ifc);
+
+ return 0;
+}
+
+static int bgp_interface_nbr_address_add(ZAPI_CALLBACK_ARGS)
+{
+ struct nbr_connected *ifc = NULL;
+ struct bgp *bgp;
+
+ ifc = zebra_interface_nbr_address_read(cmd, zclient->ibuf, vrf_id);
+
+ if (ifc == NULL)
+ return 0;
+
+ if (bgp_debug_zebra(ifc->address))
+ zlog_debug("Rx Intf neighbor add VRF %u IF %s addr %pFX",
+ vrf_id, ifc->ifp->name, ifc->address);
+
+ if (if_is_operative(ifc->ifp)) {
+ bgp = bgp_lookup_by_vrf_id(vrf_id);
+ if (bgp)
+ bgp_nbr_connected_add(bgp, ifc);
+ }
+
+ return 0;
+}
+
+static int bgp_interface_nbr_address_delete(ZAPI_CALLBACK_ARGS)
+{
+ struct nbr_connected *ifc = NULL;
+ struct bgp *bgp;
+
+ ifc = zebra_interface_nbr_address_read(cmd, zclient->ibuf, vrf_id);
+
+ if (ifc == NULL)
+ return 0;
+
+ if (bgp_debug_zebra(ifc->address))
+ zlog_debug("Rx Intf neighbor del VRF %u IF %s addr %pFX",
+ vrf_id, ifc->ifp->name, ifc->address);
+
+ if (if_is_operative(ifc->ifp)) {
+ bgp = bgp_lookup_by_vrf_id(vrf_id);
+ if (bgp)
+ bgp_nbr_connected_delete(bgp, ifc, 0);
+ }
+
+ nbr_connected_free(ifc);
+
+ return 0;
+}
+
+/* VRF update for an interface. */
+static int bgp_interface_vrf_update(ZAPI_CALLBACK_ARGS)
+{
+ struct interface *ifp;
+ vrf_id_t new_vrf_id;
+ struct connected *c;
+ struct nbr_connected *nc;
+ struct listnode *node, *nnode;
+ struct bgp *bgp;
+ struct peer *peer;
+
+ ifp = zebra_interface_vrf_update_read(zclient->ibuf, vrf_id,
+ &new_vrf_id);
+ if (!ifp)
+ return 0;
+
+ if (BGP_DEBUG(zebra, ZEBRA))
+ zlog_debug("Rx Intf VRF change VRF %u IF %s NewVRF %u", vrf_id,
+ ifp->name, new_vrf_id);
+
+ bgp = bgp_lookup_by_vrf_id(vrf_id);
+
+ if (bgp) {
+ for (ALL_LIST_ELEMENTS(ifp->connected, node, nnode, c))
+ bgp_connected_delete(bgp, c);
+
+ for (ALL_LIST_ELEMENTS(ifp->nbr_connected, node, nnode, nc))
+ bgp_nbr_connected_delete(bgp, nc, 1);
+
+ /* Fast external-failover */
+ if (!CHECK_FLAG(bgp->flags, BGP_FLAG_NO_FAST_EXT_FAILOVER)) {
+ for (ALL_LIST_ELEMENTS(bgp->peer, node, nnode, peer)) {
+ if ((peer->ttl != BGP_DEFAULT_TTL)
+ && (peer->gtsm_hops
+ != BGP_GTSM_HOPS_CONNECTED))
+ continue;
+
+ if (ifp == peer->nexthop.ifp)
+ BGP_EVENT_ADD(peer, BGP_Stop);
+ }
+ }
+ }
+
+ if_update_to_new_vrf(ifp, new_vrf_id);
+
+ bgp = bgp_lookup_by_vrf_id(new_vrf_id);
+ if (!bgp)
+ return 0;
+
+ for (ALL_LIST_ELEMENTS(ifp->connected, node, nnode, c))
+ bgp_connected_add(bgp, c);
+
+ for (ALL_LIST_ELEMENTS(ifp->nbr_connected, node, nnode, nc))
+ bgp_nbr_connected_add(bgp, nc);
+
+ hook_call(bgp_vrf_status_changed, bgp, ifp);
+ return 0;
+}
+
+/* Zebra route add and delete treatment. */
+static int zebra_read_route(ZAPI_CALLBACK_ARGS)
+{
+ enum nexthop_types_t nhtype;
+ enum blackhole_type bhtype = BLACKHOLE_UNSPEC;
+ struct zapi_route api;
+ union g_addr nexthop = {};
+ ifindex_t ifindex;
+ int add, i;
+ struct bgp *bgp;
+
+ bgp = bgp_lookup_by_vrf_id(vrf_id);
+ if (!bgp)
+ return 0;
+
+ if (zapi_route_decode(zclient->ibuf, &api) < 0)
+ return -1;
+
+ /* we completely ignore srcdest routes for now. */
+ if (CHECK_FLAG(api.message, ZAPI_MESSAGE_SRCPFX))
+ return 0;
+
+ /* ignore link-local address. */
+ if (api.prefix.family == AF_INET6
+ && IN6_IS_ADDR_LINKLOCAL(&api.prefix.u.prefix6))
+ return 0;
+
+ ifindex = api.nexthops[0].ifindex;
+ nhtype = api.nexthops[0].type;
+
+ /* api_nh structure has union of gate and bh_type */
+ if (nhtype == NEXTHOP_TYPE_BLACKHOLE) {
+ /* bh_type is only applicable if NEXTHOP_TYPE_BLACKHOLE*/
+ bhtype = api.nexthops[0].bh_type;
+ } else
+ nexthop = api.nexthops[0].gate;
+
+ add = (cmd == ZEBRA_REDISTRIBUTE_ROUTE_ADD);
+ if (add) {
+ /*
+ * The ADD message is actually an UPDATE and there is no
+ * explicit DEL
+ * for a prior redistributed route, if any. So, perform an
+ * implicit
+ * DEL processing for the same redistributed route from any
+ * other
+ * source type.
+ */
+ for (i = 0; i < ZEBRA_ROUTE_MAX; i++) {
+ if (i != api.type)
+ bgp_redistribute_delete(bgp, &api.prefix, i,
+ api.instance);
+ }
+
+ /* Now perform the add/update. */
+ bgp_redistribute_add(bgp, &api.prefix, &nexthop, ifindex,
+ nhtype, bhtype, api.distance, api.metric,
+ api.type, api.instance, api.tag);
+ } else {
+ bgp_redistribute_delete(bgp, &api.prefix, api.type,
+ api.instance);
+ }
+
+ if (bgp_debug_zebra(&api.prefix)) {
+ char buf[PREFIX_STRLEN];
+
+ if (add) {
+ inet_ntop(api.prefix.family, &nexthop, buf,
+ sizeof(buf));
+ zlog_debug(
+ "Rx route ADD VRF %u %s[%d] %pFX nexthop %s (type %d if %u) metric %u distance %u tag %" ROUTE_TAG_PRI,
+ vrf_id, zebra_route_string(api.type),
+ api.instance, &api.prefix, buf, nhtype, ifindex,
+ api.metric, api.distance, api.tag);
+ } else {
+ zlog_debug("Rx route DEL VRF %u %s[%d] %pFX", vrf_id,
+ zebra_route_string(api.type), api.instance,
+ &api.prefix);
+ }
+ }
+
+ return 0;
+}
+
+struct interface *if_lookup_by_ipv4(struct in_addr *addr, vrf_id_t vrf_id)
+{
+ struct vrf *vrf;
+ struct listnode *cnode;
+ struct interface *ifp;
+ struct connected *connected;
+ struct prefix_ipv4 p;
+ struct prefix *cp;
+
+ vrf = vrf_lookup_by_id(vrf_id);
+ if (!vrf)
+ return NULL;
+
+ p.family = AF_INET;
+ p.prefix = *addr;
+ p.prefixlen = IPV4_MAX_BITLEN;
+
+ FOR_ALL_INTERFACES (vrf, ifp) {
+ for (ALL_LIST_ELEMENTS_RO(ifp->connected, cnode, connected)) {
+ cp = connected->address;
+
+ if (cp->family == AF_INET)
+ if (prefix_match(cp, (struct prefix *)&p))
+ return ifp;
+ }
+ }
+ return NULL;
+}
+
+struct interface *if_lookup_by_ipv4_exact(struct in_addr *addr, vrf_id_t vrf_id)
+{
+ struct vrf *vrf;
+ struct listnode *cnode;
+ struct interface *ifp;
+ struct connected *connected;
+ struct prefix *cp;
+
+ vrf = vrf_lookup_by_id(vrf_id);
+ if (!vrf)
+ return NULL;
+
+ FOR_ALL_INTERFACES (vrf, ifp) {
+ for (ALL_LIST_ELEMENTS_RO(ifp->connected, cnode, connected)) {
+ cp = connected->address;
+
+ if (cp->family == AF_INET)
+ if (IPV4_ADDR_SAME(&cp->u.prefix4, addr))
+ return ifp;
+ }
+ }
+ return NULL;
+}
+
+struct interface *if_lookup_by_ipv6(struct in6_addr *addr, ifindex_t ifindex,
+ vrf_id_t vrf_id)
+{
+ struct vrf *vrf;
+ struct listnode *cnode;
+ struct interface *ifp;
+ struct connected *connected;
+ struct prefix_ipv6 p;
+ struct prefix *cp;
+
+ vrf = vrf_lookup_by_id(vrf_id);
+ if (!vrf)
+ return NULL;
+
+ p.family = AF_INET6;
+ p.prefix = *addr;
+ p.prefixlen = IPV6_MAX_BITLEN;
+
+ FOR_ALL_INTERFACES (vrf, ifp) {
+ for (ALL_LIST_ELEMENTS_RO(ifp->connected, cnode, connected)) {
+ cp = connected->address;
+
+ if (cp->family == AF_INET6)
+ if (prefix_match(cp, (struct prefix *)&p)) {
+ if (IN6_IS_ADDR_LINKLOCAL(
+ &cp->u.prefix6)) {
+ if (ifindex == ifp->ifindex)
+ return ifp;
+ } else
+ return ifp;
+ }
+ }
+ }
+ return NULL;
+}
+
+struct interface *if_lookup_by_ipv6_exact(struct in6_addr *addr,
+ ifindex_t ifindex, vrf_id_t vrf_id)
+{
+ struct vrf *vrf;
+ struct listnode *cnode;
+ struct interface *ifp;
+ struct connected *connected;
+ struct prefix *cp;
+
+ vrf = vrf_lookup_by_id(vrf_id);
+ if (!vrf)
+ return NULL;
+
+ FOR_ALL_INTERFACES (vrf, ifp) {
+ for (ALL_LIST_ELEMENTS_RO(ifp->connected, cnode, connected)) {
+ cp = connected->address;
+
+ if (cp->family == AF_INET6)
+ if (IPV6_ADDR_SAME(&cp->u.prefix6, addr)) {
+ if (IN6_IS_ADDR_LINKLOCAL(
+ &cp->u.prefix6)) {
+ if (ifindex == ifp->ifindex)
+ return ifp;
+ } else
+ return ifp;
+ }
+ }
+ }
+ return NULL;
+}
+
+static int if_get_ipv6_global(struct interface *ifp, struct in6_addr *addr)
+{
+ struct listnode *cnode;
+ struct connected *connected;
+ struct prefix *cp;
+
+ for (ALL_LIST_ELEMENTS_RO(ifp->connected, cnode, connected)) {
+ cp = connected->address;
+
+ if (cp->family == AF_INET6)
+ if (!IN6_IS_ADDR_LINKLOCAL(&cp->u.prefix6)) {
+ memcpy(addr, &cp->u.prefix6, IPV6_MAX_BYTELEN);
+ return 1;
+ }
+ }
+ return 0;
+}
+
+static bool if_get_ipv6_local(struct interface *ifp, struct in6_addr *addr)
+{
+ struct listnode *cnode;
+ struct connected *connected;
+ struct prefix *cp;
+
+ for (ALL_LIST_ELEMENTS_RO(ifp->connected, cnode, connected)) {
+ cp = connected->address;
+
+ if (cp->family == AF_INET6)
+ if (IN6_IS_ADDR_LINKLOCAL(&cp->u.prefix6)) {
+ memcpy(addr, &cp->u.prefix6, IPV6_MAX_BYTELEN);
+ return true;
+ }
+ }
+ return false;
+}
+
+static int if_get_ipv4_address(struct interface *ifp, struct in_addr *addr)
+{
+ struct listnode *cnode;
+ struct connected *connected;
+ struct prefix *cp;
+
+ for (ALL_LIST_ELEMENTS_RO(ifp->connected, cnode, connected)) {
+ cp = connected->address;
+ if ((cp->family == AF_INET)
+ && !ipv4_martian(&(cp->u.prefix4))) {
+ *addr = cp->u.prefix4;
+ return 1;
+ }
+ }
+ return 0;
+}
+
+
+bool bgp_zebra_nexthop_set(union sockunion *local, union sockunion *remote,
+ struct bgp_nexthop *nexthop, struct peer *peer)
+{
+ int ret = 0;
+ struct interface *ifp = NULL;
+ bool v6_ll_avail = true;
+
+ memset(nexthop, 0, sizeof(struct bgp_nexthop));
+
+ if (!local)
+ return false;
+ if (!remote)
+ return false;
+
+ if (local->sa.sa_family == AF_INET) {
+ nexthop->v4 = local->sin.sin_addr;
+ if (peer->update_if)
+ ifp = if_lookup_by_name(peer->update_if,
+ peer->bgp->vrf_id);
+ else
+ ifp = if_lookup_by_ipv4_exact(&local->sin.sin_addr,
+ peer->bgp->vrf_id);
+ }
+ if (local->sa.sa_family == AF_INET6) {
+ memcpy(&nexthop->v6_global, &local->sin6.sin6_addr, IPV6_MAX_BYTELEN);
+ if (IN6_IS_ADDR_LINKLOCAL(&local->sin6.sin6_addr)) {
+ if (peer->conf_if || peer->ifname)
+ ifp = if_lookup_by_name(peer->conf_if
+ ? peer->conf_if
+ : peer->ifname,
+ peer->bgp->vrf_id);
+ else if (peer->update_if)
+ ifp = if_lookup_by_name(peer->update_if,
+ peer->bgp->vrf_id);
+ } else if (peer->update_if)
+ ifp = if_lookup_by_name(peer->update_if,
+ peer->bgp->vrf_id);
+ else
+ ifp = if_lookup_by_ipv6_exact(&local->sin6.sin6_addr,
+ local->sin6.sin6_scope_id,
+ peer->bgp->vrf_id);
+ }
+
+ if (!ifp) {
+ /*
+ * BGP views do not currently get proper data
+ * from zebra( when attached ) to be able to
+ * properly resolve nexthops, so give this
+ * instance type a pass.
+ */
+ if (peer->bgp->inst_type == BGP_INSTANCE_TYPE_VIEW)
+ return true;
+ /*
+ * If we have no interface data but we have established
+ * some connection w/ zebra than something has gone
+ * terribly terribly wrong here, so say this failed
+ * If we do not any zebra connection then not
+ * having a ifp pointer is ok.
+ */
+ return zclient_num_connects ? false : true;
+ }
+
+ nexthop->ifp = ifp;
+
+ /* IPv4 connection, fetch and store IPv6 local address(es) if any. */
+ if (local->sa.sa_family == AF_INET) {
+ /* IPv6 nexthop*/
+ ret = if_get_ipv6_global(ifp, &nexthop->v6_global);
+
+ if (!ret) {
+ /* There is no global nexthop. Use link-local address as
+ * both the
+ * global and link-local nexthop. In this scenario, the
+ * expectation
+ * for interop is that the network admin would use a
+ * route-map to
+ * specify the global IPv6 nexthop.
+ */
+ v6_ll_avail =
+ if_get_ipv6_local(ifp, &nexthop->v6_global);
+ memcpy(&nexthop->v6_local, &nexthop->v6_global,
+ IPV6_MAX_BYTELEN);
+ } else
+ v6_ll_avail =
+ if_get_ipv6_local(ifp, &nexthop->v6_local);
+
+ /*
+ * If we are a v4 connection and we are not doing unnumbered
+ * not having a v6 LL address is ok
+ */
+ if (!v6_ll_avail && !peer->conf_if)
+ v6_ll_avail = true;
+ if (if_lookup_by_ipv4(&remote->sin.sin_addr, peer->bgp->vrf_id))
+ peer->shared_network = 1;
+ else
+ peer->shared_network = 0;
+ }
+
+ /* IPv6 connection, fetch and store IPv4 local address if any. */
+ if (local->sa.sa_family == AF_INET6) {
+ struct interface *direct = NULL;
+
+ /* IPv4 nexthop. */
+ ret = if_get_ipv4_address(ifp, &nexthop->v4);
+ if (!ret && peer->local_id.s_addr != INADDR_ANY)
+ nexthop->v4 = peer->local_id;
+
+ /* Global address*/
+ if (!IN6_IS_ADDR_LINKLOCAL(&local->sin6.sin6_addr)) {
+ memcpy(&nexthop->v6_global, &local->sin6.sin6_addr,
+ IPV6_MAX_BYTELEN);
+
+ /* If directory connected set link-local address. */
+ direct = if_lookup_by_ipv6(&remote->sin6.sin6_addr,
+ remote->sin6.sin6_scope_id,
+ peer->bgp->vrf_id);
+ if (direct)
+ v6_ll_avail = if_get_ipv6_local(
+ ifp, &nexthop->v6_local);
+ /*
+ * It's fine to not have a v6 LL when using
+ * update-source loopback/vrf
+ */
+ if (!v6_ll_avail && if_is_loopback(ifp))
+ v6_ll_avail = true;
+ else {
+ flog_warn(
+ EC_BGP_NO_LL_ADDRESS_AVAILABLE,
+ "Interface: %s does not have a v6 LL address associated with it, waiting until one is created for it",
+ ifp->name);
+ }
+ } else
+ /* Link-local address. */
+ {
+ ret = if_get_ipv6_global(ifp, &nexthop->v6_global);
+
+ /* If there is no global address. Set link-local
+ address as
+ global. I know this break RFC specification... */
+ /* In this scenario, the expectation for interop is that
+ * the
+ * network admin would use a route-map to specify the
+ * global
+ * IPv6 nexthop.
+ */
+ if (!ret)
+ memcpy(&nexthop->v6_global,
+ &local->sin6.sin6_addr,
+ IPV6_MAX_BYTELEN);
+ /* Always set the link-local address */
+ memcpy(&nexthop->v6_local, &local->sin6.sin6_addr,
+ IPV6_MAX_BYTELEN);
+ }
+
+ if (IN6_IS_ADDR_LINKLOCAL(&local->sin6.sin6_addr)
+ || if_lookup_by_ipv6(&remote->sin6.sin6_addr,
+ remote->sin6.sin6_scope_id,
+ peer->bgp->vrf_id))
+ peer->shared_network = 1;
+ else
+ peer->shared_network = 0;
+ }
+
+/* KAME stack specific treatment. */
+#ifdef KAME
+ if (IN6_IS_ADDR_LINKLOCAL(&nexthop->v6_global)
+ && IN6_LINKLOCAL_IFINDEX(nexthop->v6_global)) {
+ SET_IN6_LINKLOCAL_IFINDEX(nexthop->v6_global, 0);
+ }
+ if (IN6_IS_ADDR_LINKLOCAL(&nexthop->v6_local)
+ && IN6_LINKLOCAL_IFINDEX(nexthop->v6_local)) {
+ SET_IN6_LINKLOCAL_IFINDEX(nexthop->v6_local, 0);
+ }
+#endif /* KAME */
+
+ /* If we have identified the local interface, there is no error for now.
+ */
+ return v6_ll_avail;
+}
+
+static struct in6_addr *
+bgp_path_info_to_ipv6_nexthop(struct bgp_path_info *path, ifindex_t *ifindex)
+{
+ struct in6_addr *nexthop = NULL;
+
+ /* Only global address nexthop exists. */
+ if (path->attr->mp_nexthop_len == BGP_ATTR_NHLEN_IPV6_GLOBAL
+ || path->attr->mp_nexthop_len == BGP_ATTR_NHLEN_VPNV6_GLOBAL) {
+ nexthop = &path->attr->mp_nexthop_global;
+ if (IN6_IS_ADDR_LINKLOCAL(nexthop))
+ *ifindex = path->attr->nh_ifindex;
+ }
+
+ /* If both global and link-local address present. */
+ if (path->attr->mp_nexthop_len == BGP_ATTR_NHLEN_IPV6_GLOBAL_AND_LL
+ || path->attr->mp_nexthop_len
+ == BGP_ATTR_NHLEN_VPNV6_GLOBAL_AND_LL) {
+ /* Check if route-map is set to prefer global over link-local */
+ if (path->attr->mp_nexthop_prefer_global) {
+ nexthop = &path->attr->mp_nexthop_global;
+ if (IN6_IS_ADDR_LINKLOCAL(nexthop))
+ *ifindex = path->attr->nh_ifindex;
+ } else {
+ /* Workaround for Cisco's nexthop bug. */
+ if (IN6_IS_ADDR_UNSPECIFIED(
+ &path->attr->mp_nexthop_global)
+ && path->peer->su_remote
+ && path->peer->su_remote->sa.sa_family
+ == AF_INET6) {
+ nexthop =
+ &path->peer->su_remote->sin6.sin6_addr;
+ if (IN6_IS_ADDR_LINKLOCAL(nexthop))
+ *ifindex = path->peer->nexthop.ifp
+ ->ifindex;
+ } else {
+ nexthop = &path->attr->mp_nexthop_local;
+ if (IN6_IS_ADDR_LINKLOCAL(nexthop))
+ *ifindex = path->attr->nh_lla_ifindex;
+ }
+ }
+ }
+
+ return nexthop;
+}
+
+static bool bgp_table_map_apply(struct route_map *map, const struct prefix *p,
+ struct bgp_path_info *path)
+{
+ route_map_result_t ret;
+
+ ret = route_map_apply(map, p, path);
+ bgp_attr_flush(path->attr);
+
+ if (ret != RMAP_DENYMATCH)
+ return true;
+
+ if (bgp_debug_zebra(p)) {
+ if (p->family == AF_INET) {
+ zlog_debug(
+ "Zebra rmap deny: IPv4 route %pFX nexthop %pI4",
+ p, &path->attr->nexthop);
+ }
+ if (p->family == AF_INET6) {
+ ifindex_t ifindex;
+ struct in6_addr *nexthop;
+
+ nexthop = bgp_path_info_to_ipv6_nexthop(path, &ifindex);
+ zlog_debug(
+ "Zebra rmap deny: IPv6 route %pFX nexthop %pI6",
+ p, nexthop);
+ }
+ }
+ return false;
+}
+
+static struct thread *bgp_tm_thread_connect;
+static bool bgp_tm_status_connected;
+static bool bgp_tm_chunk_obtained;
+#define BGP_FLOWSPEC_TABLE_CHUNK 100000
+static uint32_t bgp_tm_min, bgp_tm_max, bgp_tm_chunk_size;
+struct bgp *bgp_tm_bgp;
+
+static void bgp_zebra_tm_connect(struct thread *t)
+{
+ struct zclient *zclient;
+ int delay = 10, ret = 0;
+
+ zclient = THREAD_ARG(t);
+ if (bgp_tm_status_connected && zclient->sock > 0)
+ delay = 60;
+ else {
+ bgp_tm_status_connected = false;
+ ret = tm_table_manager_connect(zclient);
+ }
+ if (ret < 0) {
+ zlog_info("Error connecting to table manager!");
+ bgp_tm_status_connected = false;
+ } else {
+ if (!bgp_tm_status_connected)
+ zlog_debug("Connecting to table manager. Success");
+ bgp_tm_status_connected = true;
+ if (!bgp_tm_chunk_obtained) {
+ if (bgp_zebra_get_table_range(bgp_tm_chunk_size,
+ &bgp_tm_min,
+ &bgp_tm_max) >= 0) {
+ bgp_tm_chunk_obtained = true;
+ /* parse non installed entries */
+ bgp_zebra_announce_table(bgp_tm_bgp, AFI_IP, SAFI_FLOWSPEC);
+ }
+ }
+ }
+ thread_add_timer(bm->master, bgp_zebra_tm_connect, zclient, delay,
+ &bgp_tm_thread_connect);
+}
+
+bool bgp_zebra_tm_chunk_obtained(void)
+{
+ return bgp_tm_chunk_obtained;
+}
+
+uint32_t bgp_zebra_tm_get_id(void)
+{
+ static int table_id;
+
+ if (!bgp_tm_chunk_obtained)
+ return ++table_id;
+ return bgp_tm_min++;
+}
+
+void bgp_zebra_init_tm_connect(struct bgp *bgp)
+{
+ int delay = 1;
+
+ /* if already set, do nothing
+ */
+ if (bgp_tm_thread_connect != NULL)
+ return;
+ bgp_tm_status_connected = false;
+ bgp_tm_chunk_obtained = false;
+ bgp_tm_min = bgp_tm_max = 0;
+ bgp_tm_chunk_size = BGP_FLOWSPEC_TABLE_CHUNK;
+ bgp_tm_bgp = bgp;
+ thread_add_timer(bm->master, bgp_zebra_tm_connect, zclient, delay,
+ &bgp_tm_thread_connect);
+}
+
+int bgp_zebra_get_table_range(uint32_t chunk_size,
+ uint32_t *start, uint32_t *end)
+{
+ int ret;
+
+ if (!bgp_tm_status_connected)
+ return -1;
+ ret = tm_get_table_chunk(zclient, chunk_size, start, end);
+ if (ret < 0) {
+ flog_err(EC_BGP_TABLE_CHUNK,
+ "BGP: Error getting table chunk %u", chunk_size);
+ return -1;
+ }
+ zlog_info("BGP: Table Manager returns range from chunk %u is [%u %u]",
+ chunk_size, *start, *end);
+ return 0;
+}
+
+static bool update_ipv4nh_for_route_install(int nh_othervrf, struct bgp *nh_bgp,
+ struct in_addr *nexthop,
+ struct attr *attr, bool is_evpn,
+ struct zapi_nexthop *api_nh)
+{
+ api_nh->gate.ipv4 = *nexthop;
+ api_nh->vrf_id = nh_bgp->vrf_id;
+
+ /* Need to set fields appropriately for EVPN routes imported into
+ * a VRF (which are programmed as onlink on l3-vni SVI) as well as
+ * connected routes leaked into a VRF.
+ */
+ if (attr->nh_type == NEXTHOP_TYPE_BLACKHOLE) {
+ api_nh->type = attr->nh_type;
+ api_nh->bh_type = attr->bh_type;
+ } else if (is_evpn) {
+ /*
+ * If the nexthop is EVPN overlay index gateway IP,
+ * treat the nexthop as NEXTHOP_TYPE_IPV4
+ * Else, mark the nexthop as onlink.
+ */
+ if (attr->evpn_overlay.type == OVERLAY_INDEX_GATEWAY_IP)
+ api_nh->type = NEXTHOP_TYPE_IPV4;
+ else {
+ api_nh->type = NEXTHOP_TYPE_IPV4_IFINDEX;
+ SET_FLAG(api_nh->flags, ZAPI_NEXTHOP_FLAG_EVPN);
+ SET_FLAG(api_nh->flags, ZAPI_NEXTHOP_FLAG_ONLINK);
+ api_nh->ifindex = nh_bgp->l3vni_svi_ifindex;
+ }
+ } else if (nh_othervrf && api_nh->gate.ipv4.s_addr == INADDR_ANY) {
+ api_nh->type = NEXTHOP_TYPE_IFINDEX;
+ api_nh->ifindex = attr->nh_ifindex;
+ } else
+ api_nh->type = NEXTHOP_TYPE_IPV4;
+
+ return true;
+}
+
+static bool update_ipv6nh_for_route_install(int nh_othervrf, struct bgp *nh_bgp,
+ struct in6_addr *nexthop,
+ ifindex_t ifindex,
+ struct bgp_path_info *pi,
+ struct bgp_path_info *best_pi,
+ bool is_evpn,
+ struct zapi_nexthop *api_nh)
+{
+ struct attr *attr;
+
+ attr = pi->attr;
+ api_nh->vrf_id = nh_bgp->vrf_id;
+
+ if (attr->nh_type == NEXTHOP_TYPE_BLACKHOLE) {
+ api_nh->type = attr->nh_type;
+ api_nh->bh_type = attr->bh_type;
+ } else if (is_evpn) {
+ /*
+ * If the nexthop is EVPN overlay index gateway IP,
+ * treat the nexthop as NEXTHOP_TYPE_IPV4
+ * Else, mark the nexthop as onlink.
+ */
+ if (attr->evpn_overlay.type == OVERLAY_INDEX_GATEWAY_IP)
+ api_nh->type = NEXTHOP_TYPE_IPV6;
+ else {
+ api_nh->type = NEXTHOP_TYPE_IPV6_IFINDEX;
+ SET_FLAG(api_nh->flags, ZAPI_NEXTHOP_FLAG_EVPN);
+ SET_FLAG(api_nh->flags, ZAPI_NEXTHOP_FLAG_ONLINK);
+ api_nh->ifindex = nh_bgp->l3vni_svi_ifindex;
+ }
+ } else if (nh_othervrf) {
+ if (IN6_IS_ADDR_UNSPECIFIED(nexthop)) {
+ api_nh->type = NEXTHOP_TYPE_IFINDEX;
+ api_nh->ifindex = attr->nh_ifindex;
+ } else if (IN6_IS_ADDR_LINKLOCAL(nexthop)) {
+ if (ifindex == 0)
+ return false;
+ api_nh->type = NEXTHOP_TYPE_IPV6_IFINDEX;
+ api_nh->ifindex = ifindex;
+ } else {
+ api_nh->type = NEXTHOP_TYPE_IPV6;
+ api_nh->ifindex = 0;
+ }
+ } else {
+ if (IN6_IS_ADDR_LINKLOCAL(nexthop)) {
+ if (pi == best_pi
+ && attr->mp_nexthop_len
+ == BGP_ATTR_NHLEN_IPV6_GLOBAL_AND_LL)
+ if (pi->peer->nexthop.ifp)
+ ifindex =
+ pi->peer->nexthop.ifp->ifindex;
+ if (!ifindex) {
+ if (pi->peer->conf_if)
+ ifindex = pi->peer->ifp->ifindex;
+ else if (pi->peer->ifname)
+ ifindex = ifname2ifindex(
+ pi->peer->ifname,
+ pi->peer->bgp->vrf_id);
+ else if (pi->peer->nexthop.ifp)
+ ifindex =
+ pi->peer->nexthop.ifp->ifindex;
+ }
+
+ if (ifindex == 0)
+ return false;
+ api_nh->type = NEXTHOP_TYPE_IPV6_IFINDEX;
+ api_nh->ifindex = ifindex;
+ } else {
+ api_nh->type = NEXTHOP_TYPE_IPV6;
+ api_nh->ifindex = 0;
+ }
+ }
+ /* api_nh structure has union of gate and bh_type */
+ if (nexthop && api_nh->type != NEXTHOP_TYPE_BLACKHOLE)
+ api_nh->gate.ipv6 = *nexthop;
+
+ return true;
+}
+
+static bool bgp_zebra_use_nhop_weighted(struct bgp *bgp, struct attr *attr,
+ uint64_t tot_bw, uint32_t *nh_weight)
+{
+ uint32_t bw;
+ uint64_t tmp;
+
+ bw = attr->link_bw;
+ /* zero link-bandwidth and link-bandwidth not present are treated
+ * as the same situation.
+ */
+ if (!bw) {
+ /* the only situations should be if we're either told
+ * to skip or use default weight.
+ */
+ if (bgp->lb_handling == BGP_LINK_BW_SKIP_MISSING)
+ return false;
+ *nh_weight = BGP_ZEBRA_DEFAULT_NHOP_WEIGHT;
+ } else {
+ tmp = (uint64_t)bw * 100;
+ *nh_weight = ((uint32_t)(tmp / tot_bw));
+ }
+
+ return true;
+}
+
+void bgp_zebra_announce(struct bgp_dest *dest, const struct prefix *p,
+ struct bgp_path_info *info, struct bgp *bgp, afi_t afi,
+ safi_t safi)
+{
+ struct zapi_route api = { 0 };
+ struct zapi_nexthop *api_nh;
+ int nh_family;
+ unsigned int valid_nh_count = 0;
+ bool allow_recursion = false;
+ uint8_t distance;
+ struct peer *peer;
+ struct bgp_path_info *mpinfo;
+ struct bgp *bgp_orig;
+ uint32_t metric;
+ struct attr local_attr;
+ struct bgp_path_info local_info;
+ struct bgp_path_info *mpinfo_cp = &local_info;
+ route_tag_t tag;
+ mpls_label_t label;
+ struct bgp_sid_info *sid_info;
+ int nh_othervrf = 0;
+ bool nh_updated = false;
+ bool do_wt_ecmp;
+ uint64_t cum_bw = 0;
+ uint32_t nhg_id = 0;
+ bool is_add;
+ uint32_t ttl = 0;
+ uint32_t bos = 0;
+ uint32_t exp = 0;
+
+ /* Don't try to install if we're not connected to Zebra or Zebra doesn't
+ * know of this instance.
+ */
+ if (!bgp_install_info_to_zebra(bgp))
+ return;
+
+ if (bgp->main_zebra_update_hold)
+ return;
+
+ if (safi == SAFI_FLOWSPEC) {
+ bgp_pbr_update_entry(bgp, bgp_dest_get_prefix(dest), info, afi,
+ safi, true);
+ return;
+ }
+
+ /*
+ * vrf leaking support (will have only one nexthop)
+ */
+ if (info->extra && info->extra->bgp_orig)
+ nh_othervrf = 1;
+
+ /* Make Zebra API structure. */
+ api.vrf_id = bgp->vrf_id;
+ api.type = ZEBRA_ROUTE_BGP;
+ api.safi = safi;
+ api.prefix = *p;
+ SET_FLAG(api.message, ZAPI_MESSAGE_NEXTHOP);
+
+ peer = info->peer;
+
+ if (info->type == ZEBRA_ROUTE_BGP
+ && info->sub_type == BGP_ROUTE_IMPORTED) {
+
+ /* Obtain peer from parent */
+ if (info->extra && info->extra->parent)
+ peer = ((struct bgp_path_info *)(info->extra->parent))
+ ->peer;
+ }
+
+ tag = info->attr->tag;
+
+ if (peer->sort == BGP_PEER_IBGP || peer->sort == BGP_PEER_CONFED
+ || info->sub_type == BGP_ROUTE_AGGREGATE) {
+ SET_FLAG(api.flags, ZEBRA_FLAG_IBGP);
+ SET_FLAG(api.flags, ZEBRA_FLAG_ALLOW_RECURSION);
+ }
+
+ if ((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))
+
+ allow_recursion = true;
+
+ if (info->attr->rmap_table_id) {
+ SET_FLAG(api.message, ZAPI_MESSAGE_TABLEID);
+ api.tableid = info->attr->rmap_table_id;
+ }
+
+ if (CHECK_FLAG(info->attr->flag, ATTR_FLAG_BIT(BGP_ATTR_SRTE_COLOR)))
+ SET_FLAG(api.message, ZAPI_MESSAGE_SRTE);
+
+ /* Metric is currently based on the best-path only */
+ metric = info->attr->med;
+
+ /* Determine if we're doing weighted ECMP or not */
+ do_wt_ecmp = bgp_path_info_mpath_chkwtd(bgp, info);
+ if (do_wt_ecmp)
+ cum_bw = bgp_path_info_mpath_cumbw(info);
+
+ /* EVPN MAC-IP routes are installed with a L3 NHG id */
+ if (bgp_evpn_path_es_use_nhg(bgp, info, &nhg_id)) {
+ mpinfo = NULL;
+ api.nhgid = nhg_id;
+ if (nhg_id)
+ SET_FLAG(api.message, ZAPI_MESSAGE_NHG);
+ } else {
+ mpinfo = info;
+ }
+
+ for (; mpinfo; mpinfo = bgp_path_info_mpath_next(mpinfo)) {
+ uint32_t nh_weight;
+ bool is_evpn;
+
+ if (valid_nh_count >= multipath_num)
+ break;
+
+ *mpinfo_cp = *mpinfo;
+ nh_weight = 0;
+
+ /* Get nexthop address-family */
+ if (p->family == AF_INET &&
+ !BGP_ATTR_MP_NEXTHOP_LEN_IP6(mpinfo_cp->attr))
+ nh_family = AF_INET;
+ else if (p->family == AF_INET6 ||
+ (p->family == AF_INET &&
+ BGP_ATTR_MP_NEXTHOP_LEN_IP6(mpinfo_cp->attr)))
+ nh_family = AF_INET6;
+ else
+ continue;
+
+ /* If processing for weighted ECMP, determine the next hop's
+ * weight. Based on user setting, we may skip the next hop
+ * in some situations.
+ */
+ if (do_wt_ecmp) {
+ if (!bgp_zebra_use_nhop_weighted(bgp, mpinfo->attr,
+ cum_bw, &nh_weight))
+ continue;
+ }
+ api_nh = &api.nexthops[valid_nh_count];
+
+ if (CHECK_FLAG(info->attr->flag,
+ ATTR_FLAG_BIT(BGP_ATTR_SRTE_COLOR)))
+ api_nh->srte_color = info->attr->srte_color;
+
+ if (bgp_debug_zebra(&api.prefix)) {
+ if (mpinfo->extra) {
+ zlog_debug("%s: p=%pFX, bgp_is_valid_label: %d",
+ __func__, p,
+ bgp_is_valid_label(
+ &mpinfo->extra->label[0]));
+ } else {
+ zlog_debug(
+ "%s: p=%pFX, extra is NULL, no label",
+ __func__, p);
+ }
+ }
+
+ if (bgp->table_map[afi][safi].name) {
+ /* Copy info and attributes, so the route-map
+ apply doesn't modify the BGP route info. */
+ local_attr = *mpinfo->attr;
+ mpinfo_cp->attr = &local_attr;
+ if (!bgp_table_map_apply(bgp->table_map[afi][safi].map,
+ p, mpinfo_cp))
+ continue;
+
+ /* metric/tag is only allowed to be
+ * overridden on 1st nexthop */
+ if (mpinfo == info) {
+ metric = mpinfo_cp->attr->med;
+ tag = mpinfo_cp->attr->tag;
+ }
+ }
+
+ BGP_ORIGINAL_UPDATE(bgp_orig, mpinfo, bgp);
+
+ if (nh_family == AF_INET) {
+ is_evpn = is_route_parent_evpn(mpinfo);
+
+ nh_updated = update_ipv4nh_for_route_install(
+ nh_othervrf, bgp_orig,
+ &mpinfo_cp->attr->nexthop, mpinfo_cp->attr,
+ is_evpn, api_nh);
+ } else {
+ ifindex_t ifindex = IFINDEX_INTERNAL;
+ struct in6_addr *nexthop;
+
+ nexthop = bgp_path_info_to_ipv6_nexthop(mpinfo_cp,
+ &ifindex);
+
+ is_evpn = is_route_parent_evpn(mpinfo);
+
+ if (!nexthop)
+ nh_updated = update_ipv4nh_for_route_install(
+ nh_othervrf, bgp_orig,
+ &mpinfo_cp->attr->nexthop,
+ mpinfo_cp->attr, is_evpn, api_nh);
+ else
+ nh_updated = update_ipv6nh_for_route_install(
+ nh_othervrf, bgp_orig, nexthop, ifindex,
+ mpinfo, info, is_evpn, api_nh);
+ }
+
+ /* Did we get proper nexthop info to update zebra? */
+ if (!nh_updated)
+ continue;
+
+ /* Allow recursion if it is a multipath group with both
+ * eBGP and iBGP paths.
+ */
+ if (!allow_recursion
+ && CHECK_FLAG(bgp->flags, BGP_FLAG_PEERTYPE_MULTIPATH_RELAX)
+ && (mpinfo->peer->sort == BGP_PEER_IBGP
+ || mpinfo->peer->sort == BGP_PEER_CONFED))
+ allow_recursion = true;
+
+ if (mpinfo->extra &&
+ bgp_is_valid_label(&mpinfo->extra->label[0]) &&
+ !CHECK_FLAG(api_nh->flags, ZAPI_NEXTHOP_FLAG_EVPN)) {
+ mpls_lse_decode(mpinfo->extra->label[0], &label, &ttl,
+ &exp, &bos);
+
+ SET_FLAG(api_nh->flags, ZAPI_NEXTHOP_FLAG_LABEL);
+
+ api_nh->label_num = 1;
+ api_nh->labels[0] = label;
+ }
+
+ if (is_evpn
+ && mpinfo->attr->evpn_overlay.type
+ != OVERLAY_INDEX_GATEWAY_IP)
+ memcpy(&api_nh->rmac, &(mpinfo->attr->rmac),
+ sizeof(struct ethaddr));
+
+ api_nh->weight = nh_weight;
+
+ if (mpinfo->extra && !sid_zero(&mpinfo->extra->sid[0].sid) &&
+ !CHECK_FLAG(api_nh->flags, ZAPI_NEXTHOP_FLAG_EVPN)) {
+ sid_info = &mpinfo->extra->sid[0];
+
+ memcpy(&api_nh->seg6_segs, &sid_info->sid,
+ sizeof(api_nh->seg6_segs));
+
+ if (sid_info->transposition_len != 0) {
+ if (!bgp_is_valid_label(
+ &mpinfo->extra->label[0]))
+ continue;
+
+ mpls_lse_decode(mpinfo->extra->label[0], &label,
+ &ttl, &exp, &bos);
+ transpose_sid(&api_nh->seg6_segs, label,
+ sid_info->transposition_offset,
+ sid_info->transposition_len);
+ }
+
+ SET_FLAG(api_nh->flags, ZAPI_NEXTHOP_FLAG_SEG6);
+ }
+
+ valid_nh_count++;
+ }
+
+ is_add = (valid_nh_count || nhg_id) ? true : false;
+
+ if (is_add && CHECK_FLAG(bm->flags, BM_FLAG_SEND_EXTRA_DATA_TO_ZEBRA)) {
+ struct bgp_zebra_opaque bzo = {};
+ const char *reason =
+ bgp_path_selection_reason2str(dest->reason);
+
+ strlcpy(bzo.aspath, info->attr->aspath->str,
+ sizeof(bzo.aspath));
+
+ if (info->attr->flag & ATTR_FLAG_BIT(BGP_ATTR_COMMUNITIES))
+ strlcpy(bzo.community,
+ bgp_attr_get_community(info->attr)->str,
+ sizeof(bzo.community));
+
+ if (info->attr->flag
+ & ATTR_FLAG_BIT(BGP_ATTR_LARGE_COMMUNITIES))
+ strlcpy(bzo.lcommunity,
+ bgp_attr_get_lcommunity(info->attr)->str,
+ sizeof(bzo.lcommunity));
+
+ strlcpy(bzo.selection_reason, reason,
+ sizeof(bzo.selection_reason));
+
+ SET_FLAG(api.message, ZAPI_MESSAGE_OPAQUE);
+ api.opaque.length = MIN(sizeof(struct bgp_zebra_opaque),
+ ZAPI_MESSAGE_OPAQUE_LENGTH);
+ memcpy(api.opaque.data, &bzo, api.opaque.length);
+ }
+
+ if (allow_recursion)
+ SET_FLAG(api.flags, ZEBRA_FLAG_ALLOW_RECURSION);
+
+ /*
+ * When we create an aggregate route we must also
+ * install a Null0 route in the RIB, so overwrite
+ * what was written into api with a blackhole route
+ */
+ if (info->sub_type == BGP_ROUTE_AGGREGATE)
+ zapi_route_set_blackhole(&api, BLACKHOLE_NULL);
+ else
+ api.nexthop_num = valid_nh_count;
+
+ SET_FLAG(api.message, ZAPI_MESSAGE_METRIC);
+ api.metric = metric;
+
+ if (tag) {
+ SET_FLAG(api.message, ZAPI_MESSAGE_TAG);
+ api.tag = tag;
+ }
+
+ distance = bgp_distance_apply(p, info, afi, safi, bgp);
+ if (distance) {
+ SET_FLAG(api.message, ZAPI_MESSAGE_DISTANCE);
+ api.distance = distance;
+ }
+
+ if (bgp_debug_zebra(p)) {
+ char nh_buf[INET6_ADDRSTRLEN];
+ char eth_buf[ETHER_ADDR_STRLEN + 7] = {'\0'};
+ char buf1[ETHER_ADDR_STRLEN];
+ char label_buf[20];
+ char sid_buf[20];
+ char segs_buf[256];
+ int i;
+
+ zlog_debug(
+ "Tx route %s VRF %u %pFX metric %u tag %" ROUTE_TAG_PRI
+ " count %d nhg %d",
+ valid_nh_count ? "add" : "delete", bgp->vrf_id,
+ &api.prefix, api.metric, api.tag, api.nexthop_num,
+ nhg_id);
+ for (i = 0; i < api.nexthop_num; i++) {
+ api_nh = &api.nexthops[i];
+
+ switch (api_nh->type) {
+ case NEXTHOP_TYPE_IFINDEX:
+ nh_buf[0] = '\0';
+ break;
+ case NEXTHOP_TYPE_IPV4:
+ case NEXTHOP_TYPE_IPV4_IFINDEX:
+ nh_family = AF_INET;
+ inet_ntop(nh_family, &api_nh->gate, nh_buf,
+ sizeof(nh_buf));
+ break;
+ case NEXTHOP_TYPE_IPV6:
+ case NEXTHOP_TYPE_IPV6_IFINDEX:
+ nh_family = AF_INET6;
+ inet_ntop(nh_family, &api_nh->gate, nh_buf,
+ sizeof(nh_buf));
+ break;
+ case NEXTHOP_TYPE_BLACKHOLE:
+ strlcpy(nh_buf, "blackhole", sizeof(nh_buf));
+ break;
+ default:
+ /* Note: add new nexthop case */
+ assert(0);
+ break;
+ }
+
+ label_buf[0] = '\0';
+ eth_buf[0] = '\0';
+ segs_buf[0] = '\0';
+ if (CHECK_FLAG(api_nh->flags,
+ ZAPI_NEXTHOP_FLAG_LABEL) &&
+ !CHECK_FLAG(api_nh->flags, ZAPI_NEXTHOP_FLAG_EVPN))
+ snprintf(label_buf, sizeof(label_buf),
+ "label %u", api_nh->labels[0]);
+ if (CHECK_FLAG(api_nh->flags, ZAPI_NEXTHOP_FLAG_SEG6) &&
+ !CHECK_FLAG(api_nh->flags,
+ ZAPI_NEXTHOP_FLAG_EVPN)) {
+ inet_ntop(AF_INET6, &api_nh->seg6_segs,
+ sid_buf, sizeof(sid_buf));
+ snprintf(segs_buf, sizeof(segs_buf), "segs %s",
+ sid_buf);
+ }
+ if (CHECK_FLAG(api_nh->flags, ZAPI_NEXTHOP_FLAG_EVPN) &&
+ !is_zero_mac(&api_nh->rmac))
+ snprintf(eth_buf, sizeof(eth_buf), " RMAC %s",
+ prefix_mac2str(&api_nh->rmac,
+ buf1, sizeof(buf1)));
+ zlog_debug(" nhop [%d]: %s if %u VRF %u wt %u %s %s %s",
+ i + 1, nh_buf, api_nh->ifindex,
+ api_nh->vrf_id, api_nh->weight,
+ label_buf, segs_buf, eth_buf);
+ }
+
+ int recursion_flag = 0;
+
+ if (CHECK_FLAG(api.flags, ZEBRA_FLAG_ALLOW_RECURSION))
+ recursion_flag = 1;
+
+ zlog_debug("%s: %pFX: announcing to zebra (recursion %sset)",
+ __func__, p, (recursion_flag ? "" : "NOT "));
+ }
+ zclient_route_send(is_add ? ZEBRA_ROUTE_ADD : ZEBRA_ROUTE_DELETE,
+ zclient, &api);
+}
+
+/* Announce all routes of a table to zebra */
+void bgp_zebra_announce_table(struct bgp *bgp, afi_t afi, safi_t safi)
+{
+ struct bgp_dest *dest;
+ struct bgp_table *table;
+ struct bgp_path_info *pi;
+
+ /* Don't try to install if we're not connected to Zebra or Zebra doesn't
+ * know of this instance.
+ */
+ if (!bgp_install_info_to_zebra(bgp))
+ return;
+
+ table = bgp->rib[afi][safi];
+ if (!table)
+ return;
+
+ 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 (CHECK_FLAG(pi->flags, BGP_PATH_SELECTED) &&
+
+ (pi->type == ZEBRA_ROUTE_BGP
+ && (pi->sub_type == BGP_ROUTE_NORMAL
+ || pi->sub_type == BGP_ROUTE_IMPORTED)))
+
+ bgp_zebra_announce(dest,
+ bgp_dest_get_prefix(dest),
+ pi, bgp, afi, safi);
+}
+
+/* Announce routes of any bgp subtype of a table to zebra */
+void bgp_zebra_announce_table_all_subtypes(struct bgp *bgp, afi_t afi,
+ safi_t safi)
+{
+ struct bgp_dest *dest;
+ struct bgp_table *table;
+ struct bgp_path_info *pi;
+
+ if (!bgp_install_info_to_zebra(bgp))
+ return;
+
+ table = bgp->rib[afi][safi];
+ if (!table)
+ return;
+
+ 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 (CHECK_FLAG(pi->flags, BGP_PATH_SELECTED) &&
+ pi->type == ZEBRA_ROUTE_BGP)
+ bgp_zebra_announce(dest,
+ bgp_dest_get_prefix(dest),
+ pi, bgp, afi, safi);
+}
+
+void bgp_zebra_withdraw(const struct prefix *p, struct bgp_path_info *info,
+ struct bgp *bgp, safi_t safi)
+{
+ struct zapi_route api;
+ struct peer *peer;
+
+ /* Don't try to install if we're not connected to Zebra or Zebra doesn't
+ * know of this instance.
+ */
+ if (!bgp_install_info_to_zebra(bgp))
+ return;
+
+ if (safi == SAFI_FLOWSPEC) {
+ peer = info->peer;
+ bgp_pbr_update_entry(peer->bgp, p, info, AFI_IP, safi, false);
+ return;
+ }
+
+ memset(&api, 0, sizeof(api));
+ api.vrf_id = bgp->vrf_id;
+ api.type = ZEBRA_ROUTE_BGP;
+ api.safi = safi;
+ api.prefix = *p;
+
+ if (info->attr->rmap_table_id) {
+ SET_FLAG(api.message, ZAPI_MESSAGE_TABLEID);
+ api.tableid = info->attr->rmap_table_id;
+ }
+
+ if (bgp_debug_zebra(p))
+ zlog_debug("Tx route delete VRF %u %pFX", bgp->vrf_id,
+ &api.prefix);
+
+ zclient_route_send(ZEBRA_ROUTE_DELETE, zclient, &api);
+}
+
+/* Withdraw all entries in a BGP instances RIB table from Zebra */
+void bgp_zebra_withdraw_table_all_subtypes(struct bgp *bgp, afi_t afi, safi_t safi)
+{
+ struct bgp_dest *dest;
+ struct bgp_table *table;
+ struct bgp_path_info *pi;
+
+ if (!bgp_install_info_to_zebra(bgp))
+ return;
+
+ table = bgp->rib[afi][safi];
+ if (!table)
+ return;
+
+ 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 (CHECK_FLAG(pi->flags, BGP_PATH_SELECTED)
+ && (pi->type == ZEBRA_ROUTE_BGP))
+ bgp_zebra_withdraw(bgp_dest_get_prefix(dest),
+ pi, bgp, safi);
+ }
+ }
+}
+
+struct bgp_redist *bgp_redist_lookup(struct bgp *bgp, afi_t afi, uint8_t type,
+ unsigned short instance)
+{
+ struct list *red_list;
+ struct listnode *node;
+ struct bgp_redist *red;
+
+ red_list = bgp->redist[afi][type];
+ if (!red_list)
+ return (NULL);
+
+ for (ALL_LIST_ELEMENTS_RO(red_list, node, red))
+ if (red->instance == instance)
+ return red;
+
+ return NULL;
+}
+
+struct bgp_redist *bgp_redist_add(struct bgp *bgp, afi_t afi, uint8_t type,
+ unsigned short instance)
+{
+ struct list *red_list;
+ struct bgp_redist *red;
+
+ red = bgp_redist_lookup(bgp, afi, type, instance);
+ if (red)
+ return red;
+
+ if (!bgp->redist[afi][type])
+ bgp->redist[afi][type] = list_new();
+
+ red_list = bgp->redist[afi][type];
+ red = XCALLOC(MTYPE_BGP_REDIST, sizeof(struct bgp_redist));
+ red->instance = instance;
+
+ listnode_add(red_list, red);
+
+ return red;
+}
+
+static void bgp_redist_del(struct bgp *bgp, afi_t afi, uint8_t type,
+ unsigned short instance)
+{
+ struct bgp_redist *red;
+
+ red = bgp_redist_lookup(bgp, afi, type, instance);
+
+ if (red) {
+ listnode_delete(bgp->redist[afi][type], red);
+ XFREE(MTYPE_BGP_REDIST, red);
+ if (!bgp->redist[afi][type]->count)
+ list_delete(&bgp->redist[afi][type]);
+ }
+}
+
+/* Other routes redistribution into BGP. */
+int bgp_redistribute_set(struct bgp *bgp, afi_t afi, int type,
+ unsigned short instance, bool changed)
+{
+ /* If redistribute options are changed call
+ * bgp_redistribute_unreg() to reset the option and withdraw
+ * the routes
+ */
+ if (changed)
+ bgp_redistribute_unreg(bgp, afi, type, instance);
+
+ /* Return if already redistribute flag is set. */
+ if (instance) {
+ if (redist_check_instance(&zclient->mi_redist[afi][type],
+ instance))
+ return CMD_WARNING;
+
+ redist_add_instance(&zclient->mi_redist[afi][type], instance);
+ } else {
+ if (vrf_bitmap_check(zclient->redist[afi][type], bgp->vrf_id))
+ return CMD_WARNING;
+
+#ifdef ENABLE_BGP_VNC
+ if (EVPN_ENABLED(bgp) && type == ZEBRA_ROUTE_VNC_DIRECT) {
+ vnc_export_bgp_enable(
+ bgp, afi); /* only enables if mode bits cfg'd */
+ }
+#endif
+
+ vrf_bitmap_set(zclient->redist[afi][type], bgp->vrf_id);
+ }
+
+ /*
+ * Don't try to register if we're not connected to Zebra or Zebra
+ * doesn't know of this instance.
+ *
+ * When we come up later well resend if needed.
+ */
+ if (!bgp_install_info_to_zebra(bgp))
+ return CMD_SUCCESS;
+
+ if (BGP_DEBUG(zebra, ZEBRA))
+ zlog_debug("Tx redistribute add VRF %u afi %d %s %d",
+ bgp->vrf_id, afi, zebra_route_string(type),
+ instance);
+
+ /* Send distribute add message to zebra. */
+ zebra_redistribute_send(ZEBRA_REDISTRIBUTE_ADD, zclient, afi, type,
+ instance, bgp->vrf_id);
+
+ return CMD_SUCCESS;
+}
+
+int bgp_redistribute_resend(struct bgp *bgp, afi_t afi, int type,
+ unsigned short instance)
+{
+ /* Don't try to send if we're not connected to Zebra or Zebra doesn't
+ * know of this instance.
+ */
+ if (!bgp_install_info_to_zebra(bgp))
+ return -1;
+
+ if (BGP_DEBUG(zebra, ZEBRA))
+ zlog_debug("Tx redistribute del/add VRF %u afi %d %s %d",
+ bgp->vrf_id, afi, zebra_route_string(type),
+ instance);
+
+ /* Send distribute add message to zebra. */
+ zebra_redistribute_send(ZEBRA_REDISTRIBUTE_DELETE, zclient, afi, type,
+ instance, bgp->vrf_id);
+ zebra_redistribute_send(ZEBRA_REDISTRIBUTE_ADD, zclient, afi, type,
+ instance, bgp->vrf_id);
+
+ return 0;
+}
+
+/* Redistribute with route-map specification. */
+bool bgp_redistribute_rmap_set(struct bgp_redist *red, const char *name,
+ struct route_map *route_map)
+{
+ if (red->rmap.name && (strcmp(red->rmap.name, name) == 0))
+ return false;
+
+ XFREE(MTYPE_ROUTE_MAP_NAME, red->rmap.name);
+ /* Decrement the count for existing routemap and
+ * increment the count for new route map.
+ */
+ route_map_counter_decrement(red->rmap.map);
+ red->rmap.name = XSTRDUP(MTYPE_ROUTE_MAP_NAME, name);
+ red->rmap.map = route_map;
+ route_map_counter_increment(red->rmap.map);
+
+ return true;
+}
+
+/* Redistribute with metric specification. */
+bool bgp_redistribute_metric_set(struct bgp *bgp, struct bgp_redist *red,
+ afi_t afi, int type, uint32_t metric)
+{
+ struct bgp_dest *dest;
+ struct bgp_path_info *pi;
+
+ if (red->redist_metric_flag && red->redist_metric == metric)
+ return false;
+
+ red->redist_metric_flag = 1;
+ red->redist_metric = metric;
+
+ for (dest = bgp_table_top(bgp->rib[afi][SAFI_UNICAST]); dest;
+ dest = bgp_route_next(dest)) {
+ for (pi = bgp_dest_get_bgp_path_info(dest); pi; pi = pi->next) {
+ if (pi->sub_type == BGP_ROUTE_REDISTRIBUTE
+ && pi->type == type
+ && pi->instance == red->instance) {
+ struct attr *old_attr;
+ struct attr new_attr;
+
+ new_attr = *pi->attr;
+ new_attr.med = red->redist_metric;
+ old_attr = pi->attr;
+ pi->attr = bgp_attr_intern(&new_attr);
+ bgp_attr_unintern(&old_attr);
+
+ bgp_path_info_set_flag(dest, pi,
+ BGP_PATH_ATTR_CHANGED);
+ bgp_process(bgp, dest, afi, SAFI_UNICAST);
+ }
+ }
+ }
+
+ return true;
+}
+
+/* Unset redistribution. */
+int bgp_redistribute_unreg(struct bgp *bgp, afi_t afi, int type,
+ unsigned short instance)
+{
+ struct bgp_redist *red;
+
+ red = bgp_redist_lookup(bgp, afi, type, instance);
+ if (!red)
+ return CMD_SUCCESS;
+
+ /* Return if zebra connection is disabled. */
+ if (instance) {
+ if (!redist_check_instance(&zclient->mi_redist[afi][type],
+ instance))
+ return CMD_WARNING;
+ redist_del_instance(&zclient->mi_redist[afi][type], instance);
+ } else {
+ if (!vrf_bitmap_check(zclient->redist[afi][type], bgp->vrf_id))
+ return CMD_WARNING;
+ vrf_bitmap_unset(zclient->redist[afi][type], bgp->vrf_id);
+ }
+
+ if (bgp_install_info_to_zebra(bgp)) {
+ /* Send distribute delete message to zebra. */
+ if (BGP_DEBUG(zebra, ZEBRA))
+ zlog_debug("Tx redistribute del VRF %u afi %d %s %d",
+ bgp->vrf_id, afi, zebra_route_string(type),
+ instance);
+ zebra_redistribute_send(ZEBRA_REDISTRIBUTE_DELETE, zclient, afi,
+ type, instance, bgp->vrf_id);
+ }
+
+ /* Withdraw redistributed routes from current BGP's routing table. */
+ bgp_redistribute_withdraw(bgp, afi, type, instance);
+
+ return CMD_SUCCESS;
+}
+
+/* Unset redistribution. */
+int bgp_redistribute_unset(struct bgp *bgp, afi_t afi, int type,
+ unsigned short instance)
+{
+ struct bgp_redist *red;
+
+/*
+ * vnc and vpn->vrf checks must be before red check because
+ * they operate within bgpd irrespective of zebra connection
+ * status. red lookup fails if there is no zebra connection.
+ */
+#ifdef ENABLE_BGP_VNC
+ if (EVPN_ENABLED(bgp) && type == ZEBRA_ROUTE_VNC_DIRECT) {
+ vnc_export_bgp_disable(bgp, afi);
+ }
+#endif
+
+ red = bgp_redist_lookup(bgp, afi, type, instance);
+ if (!red)
+ return CMD_SUCCESS;
+
+ bgp_redistribute_unreg(bgp, afi, type, instance);
+
+ /* Unset route-map. */
+ XFREE(MTYPE_ROUTE_MAP_NAME, red->rmap.name);
+ route_map_counter_decrement(red->rmap.map);
+ red->rmap.map = NULL;
+
+ /* Unset metric. */
+ red->redist_metric_flag = 0;
+ red->redist_metric = 0;
+
+ bgp_redist_del(bgp, afi, type, instance);
+
+ return CMD_SUCCESS;
+}
+
+void bgp_redistribute_redo(struct bgp *bgp)
+{
+ afi_t afi;
+ int i;
+ struct list *red_list;
+ struct listnode *node;
+ struct bgp_redist *red;
+
+ for (afi = AFI_IP; afi < AFI_MAX; afi++) {
+ for (i = 0; i < ZEBRA_ROUTE_MAX; i++) {
+
+ red_list = bgp->redist[afi][i];
+ if (!red_list)
+ continue;
+
+ for (ALL_LIST_ELEMENTS_RO(red_list, node, red)) {
+ bgp_redistribute_resend(bgp, afi, i,
+ red->instance);
+ }
+ }
+ }
+}
+
+void bgp_zclient_reset(void)
+{
+ zclient_reset(zclient);
+}
+
+/* Register this instance with Zebra. Invoked upon connect (for
+ * default instance) and when other VRFs are learnt (or created and
+ * already learnt).
+ */
+void bgp_zebra_instance_register(struct bgp *bgp)
+{
+ /* Don't try to register if we're not connected to Zebra */
+ if (!zclient || zclient->sock < 0)
+ return;
+
+ if (BGP_DEBUG(zebra, ZEBRA))
+ zlog_debug("Registering VRF %u", bgp->vrf_id);
+
+ /* Register for router-id, interfaces, redistributed routes. */
+ zclient_send_reg_requests(zclient, bgp->vrf_id);
+
+ /* For EVPN instance, register to learn about VNIs, if appropriate. */
+ if (bgp->advertise_all_vni)
+ bgp_zebra_advertise_all_vni(bgp, 1);
+
+ bgp_nht_register_nexthops(bgp);
+}
+
+/* Deregister this instance with Zebra. Invoked upon the instance
+ * being deleted (default or VRF) and it is already registered.
+ */
+void bgp_zebra_instance_deregister(struct bgp *bgp)
+{
+ /* Don't try to deregister if we're not connected to Zebra */
+ if (zclient->sock < 0)
+ return;
+
+ if (BGP_DEBUG(zebra, ZEBRA))
+ zlog_debug("Deregistering VRF %u", bgp->vrf_id);
+
+ /* For EVPN instance, unregister learning about VNIs, if appropriate. */
+ if (bgp->advertise_all_vni)
+ bgp_zebra_advertise_all_vni(bgp, 0);
+
+ /* Deregister for router-id, interfaces, redistributed routes. */
+ zclient_send_dereg_requests(zclient, bgp->vrf_id);
+}
+
+void bgp_zebra_initiate_radv(struct bgp *bgp, struct peer *peer)
+{
+ uint32_t ra_interval = BGP_UNNUM_DEFAULT_RA_INTERVAL;
+
+ /* Don't try to initiate if we're not connected to Zebra */
+ if (zclient->sock < 0)
+ return;
+
+ if (BGP_DEBUG(zebra, ZEBRA))
+ zlog_debug("%u: Initiating RA for peer %s", bgp->vrf_id,
+ peer->host);
+
+ /*
+ * If unnumbered peer (peer->ifp) call thru zapi to start RAs.
+ * If we don't have an ifp pointer, call function to find the
+ * ifps for a numbered enhe peer to turn RAs on.
+ */
+ peer->ifp ? zclient_send_interface_radv_req(zclient, bgp->vrf_id,
+ peer->ifp, 1, ra_interval)
+ : bgp_nht_reg_enhe_cap_intfs(peer);
+}
+
+void bgp_zebra_terminate_radv(struct bgp *bgp, struct peer *peer)
+{
+ /* Don't try to terminate if we're not connected to Zebra */
+ if (zclient->sock < 0)
+ return;
+
+ if (BGP_DEBUG(zebra, ZEBRA))
+ zlog_debug("%u: Terminating RA for peer %s", bgp->vrf_id,
+ peer->host);
+
+ /*
+ * If unnumbered peer (peer->ifp) call thru zapi to stop RAs.
+ * If we don't have an ifp pointer, call function to find the
+ * ifps for a numbered enhe peer to turn RAs off.
+ */
+ peer->ifp ? zclient_send_interface_radv_req(zclient, bgp->vrf_id,
+ peer->ifp, 0, 0)
+ : bgp_nht_dereg_enhe_cap_intfs(peer);
+}
+
+int bgp_zebra_advertise_subnet(struct bgp *bgp, int advertise, vni_t vni)
+{
+ struct stream *s = NULL;
+
+ /* Check socket. */
+ if (!zclient || zclient->sock < 0)
+ return 0;
+
+ /* Don't try to register if Zebra doesn't know of this instance. */
+ if (!IS_BGP_INST_KNOWN_TO_ZEBRA(bgp)) {
+ if (BGP_DEBUG(zebra, ZEBRA))
+ zlog_debug(
+ "%s: No zebra instance to talk to, cannot advertise subnet",
+ __func__);
+ return 0;
+ }
+
+ s = zclient->obuf;
+ stream_reset(s);
+
+ zclient_create_header(s, ZEBRA_ADVERTISE_SUBNET, bgp->vrf_id);
+ stream_putc(s, advertise);
+ stream_put3(s, vni);
+ stream_putw_at(s, 0, stream_get_endp(s));
+
+ return zclient_send_message(zclient);
+}
+
+int bgp_zebra_advertise_svi_macip(struct bgp *bgp, int advertise, vni_t vni)
+{
+ struct stream *s = NULL;
+
+ /* Check socket. */
+ if (!zclient || zclient->sock < 0)
+ return 0;
+
+ /* Don't try to register if Zebra doesn't know of this instance. */
+ if (!IS_BGP_INST_KNOWN_TO_ZEBRA(bgp))
+ return 0;
+
+ s = zclient->obuf;
+ stream_reset(s);
+
+ zclient_create_header(s, ZEBRA_ADVERTISE_SVI_MACIP, bgp->vrf_id);
+ stream_putc(s, advertise);
+ stream_putl(s, vni);
+ stream_putw_at(s, 0, stream_get_endp(s));
+
+ return zclient_send_message(zclient);
+}
+
+int bgp_zebra_advertise_gw_macip(struct bgp *bgp, int advertise, vni_t vni)
+{
+ struct stream *s = NULL;
+
+ /* Check socket. */
+ if (!zclient || zclient->sock < 0)
+ return 0;
+
+ /* Don't try to register if Zebra doesn't know of this instance. */
+ if (!IS_BGP_INST_KNOWN_TO_ZEBRA(bgp)) {
+ if (BGP_DEBUG(zebra, ZEBRA))
+ zlog_debug(
+ "%s: No zebra instance to talk to, not installing gw_macip",
+ __func__);
+ return 0;
+ }
+
+ s = zclient->obuf;
+ stream_reset(s);
+
+ zclient_create_header(s, ZEBRA_ADVERTISE_DEFAULT_GW, bgp->vrf_id);
+ stream_putc(s, advertise);
+ stream_putl(s, vni);
+ stream_putw_at(s, 0, stream_get_endp(s));
+
+ return zclient_send_message(zclient);
+}
+
+int bgp_zebra_vxlan_flood_control(struct bgp *bgp,
+ enum vxlan_flood_control flood_ctrl)
+{
+ struct stream *s;
+
+ /* Check socket. */
+ if (!zclient || zclient->sock < 0)
+ return 0;
+
+ /* Don't try to register if Zebra doesn't know of this instance. */
+ if (!IS_BGP_INST_KNOWN_TO_ZEBRA(bgp)) {
+ if (BGP_DEBUG(zebra, ZEBRA))
+ zlog_debug(
+ "%s: No zebra instance to talk to, not installing all vni",
+ __func__);
+ return 0;
+ }
+
+ s = zclient->obuf;
+ stream_reset(s);
+
+ zclient_create_header(s, ZEBRA_VXLAN_FLOOD_CONTROL, bgp->vrf_id);
+ stream_putc(s, flood_ctrl);
+ stream_putw_at(s, 0, stream_get_endp(s));
+
+ return zclient_send_message(zclient);
+}
+
+int bgp_zebra_advertise_all_vni(struct bgp *bgp, int advertise)
+{
+ struct stream *s;
+
+ /* Check socket. */
+ if (!zclient || zclient->sock < 0)
+ return 0;
+
+ /* Don't try to register if Zebra doesn't know of this instance. */
+ if (!IS_BGP_INST_KNOWN_TO_ZEBRA(bgp))
+ return 0;
+
+ s = zclient->obuf;
+ stream_reset(s);
+
+ zclient_create_header(s, ZEBRA_ADVERTISE_ALL_VNI, bgp->vrf_id);
+ stream_putc(s, advertise);
+ /* Also inform current BUM handling setting. This is really
+ * relevant only when 'advertise' is set.
+ */
+ stream_putc(s, bgp->vxlan_flood_ctrl);
+ stream_putw_at(s, 0, stream_get_endp(s));
+
+ return zclient_send_message(zclient);
+}
+
+int bgp_zebra_dup_addr_detection(struct bgp *bgp)
+{
+ struct stream *s;
+
+ /* Check socket. */
+ if (!zclient || zclient->sock < 0)
+ return 0;
+
+ /* Don't try to register if Zebra doesn't know of this instance. */
+ if (!IS_BGP_INST_KNOWN_TO_ZEBRA(bgp))
+ return 0;
+
+ if (BGP_DEBUG(zebra, ZEBRA))
+ zlog_debug("dup addr detect %s max_moves %u time %u freeze %s freeze_time %u",
+ bgp->evpn_info->dup_addr_detect ?
+ "enable" : "disable",
+ bgp->evpn_info->dad_max_moves,
+ bgp->evpn_info->dad_time,
+ bgp->evpn_info->dad_freeze ?
+ "enable" : "disable",
+ bgp->evpn_info->dad_freeze_time);
+
+ s = zclient->obuf;
+ stream_reset(s);
+ zclient_create_header(s, ZEBRA_DUPLICATE_ADDR_DETECTION,
+ bgp->vrf_id);
+ stream_putl(s, bgp->evpn_info->dup_addr_detect);
+ stream_putl(s, bgp->evpn_info->dad_time);
+ stream_putl(s, bgp->evpn_info->dad_max_moves);
+ stream_putl(s, bgp->evpn_info->dad_freeze);
+ stream_putl(s, bgp->evpn_info->dad_freeze_time);
+ stream_putw_at(s, 0, stream_get_endp(s));
+
+ return zclient_send_message(zclient);
+}
+
+static int rule_notify_owner(ZAPI_CALLBACK_ARGS)
+{
+ uint32_t seqno, priority, unique;
+ enum zapi_rule_notify_owner note;
+ struct bgp_pbr_action *bgp_pbra;
+ struct bgp_pbr_rule *bgp_pbr = NULL;
+ char ifname[INTERFACE_NAMSIZ + 1];
+
+ if (!zapi_rule_notify_decode(zclient->ibuf, &seqno, &priority, &unique,
+ ifname, &note))
+ return -1;
+
+ bgp_pbra = bgp_pbr_action_rule_lookup(vrf_id, unique);
+ if (!bgp_pbra) {
+ /* look in bgp pbr rule */
+ bgp_pbr = bgp_pbr_rule_lookup(vrf_id, unique);
+ if (!bgp_pbr && note != ZAPI_RULE_REMOVED) {
+ if (BGP_DEBUG(zebra, ZEBRA))
+ zlog_debug("%s: Fail to look BGP rule (%u)",
+ __func__, unique);
+ return 0;
+ }
+ }
+
+ switch (note) {
+ case ZAPI_RULE_FAIL_INSTALL:
+ if (BGP_DEBUG(zebra, ZEBRA))
+ zlog_debug("%s: Received RULE_FAIL_INSTALL", __func__);
+ if (bgp_pbra) {
+ bgp_pbra->installed = false;
+ bgp_pbra->install_in_progress = false;
+ } else {
+ bgp_pbr->installed = false;
+ bgp_pbr->install_in_progress = false;
+ }
+ break;
+ case ZAPI_RULE_INSTALLED:
+ if (bgp_pbra) {
+ bgp_pbra->installed = true;
+ bgp_pbra->install_in_progress = false;
+ } else {
+ struct bgp_path_info *path;
+ struct bgp_path_info_extra *extra;
+
+ bgp_pbr->installed = true;
+ bgp_pbr->install_in_progress = false;
+ bgp_pbr->action->refcnt++;
+ /* link bgp_info to bgp_pbr */
+ path = (struct bgp_path_info *)bgp_pbr->path;
+ extra = bgp_path_info_extra_get(path);
+ listnode_add_force(&extra->bgp_fs_iprule,
+ bgp_pbr);
+ }
+ if (BGP_DEBUG(zebra, ZEBRA))
+ zlog_debug("%s: Received RULE_INSTALLED", __func__);
+ break;
+ case ZAPI_RULE_FAIL_REMOVE:
+ case ZAPI_RULE_REMOVED:
+ if (BGP_DEBUG(zebra, ZEBRA))
+ zlog_debug("%s: Received RULE REMOVED", __func__);
+ break;
+ }
+
+ return 0;
+}
+
+static int ipset_notify_owner(ZAPI_CALLBACK_ARGS)
+{
+ uint32_t unique;
+ enum zapi_ipset_notify_owner note;
+ struct bgp_pbr_match *bgp_pbim;
+
+ if (!zapi_ipset_notify_decode(zclient->ibuf,
+ &unique,
+ &note))
+ return -1;
+
+ bgp_pbim = bgp_pbr_match_ipset_lookup(vrf_id, unique);
+ if (!bgp_pbim) {
+ if (BGP_DEBUG(zebra, ZEBRA))
+ zlog_debug("%s: Fail to look BGP match ( %u, ID %u)",
+ __func__, note, unique);
+ return 0;
+ }
+
+ switch (note) {
+ case ZAPI_IPSET_FAIL_INSTALL:
+ if (BGP_DEBUG(zebra, ZEBRA))
+ zlog_debug("%s: Received IPSET_FAIL_INSTALL", __func__);
+ bgp_pbim->installed = false;
+ bgp_pbim->install_in_progress = false;
+ break;
+ case ZAPI_IPSET_INSTALLED:
+ bgp_pbim->installed = true;
+ bgp_pbim->install_in_progress = false;
+ if (BGP_DEBUG(zebra, ZEBRA))
+ zlog_debug("%s: Received IPSET_INSTALLED", __func__);
+ break;
+ case ZAPI_IPSET_FAIL_REMOVE:
+ case ZAPI_IPSET_REMOVED:
+ if (BGP_DEBUG(zebra, ZEBRA))
+ zlog_debug("%s: Received IPSET REMOVED", __func__);
+ break;
+ }
+
+ return 0;
+}
+
+static int ipset_entry_notify_owner(ZAPI_CALLBACK_ARGS)
+{
+ uint32_t unique;
+ char ipset_name[ZEBRA_IPSET_NAME_SIZE];
+ enum zapi_ipset_entry_notify_owner note;
+ struct bgp_pbr_match_entry *bgp_pbime;
+
+ if (!zapi_ipset_entry_notify_decode(
+ zclient->ibuf,
+ &unique,
+ ipset_name,
+ &note))
+ return -1;
+ bgp_pbime = bgp_pbr_match_ipset_entry_lookup(vrf_id,
+ ipset_name,
+ unique);
+ if (!bgp_pbime) {
+ if (BGP_DEBUG(zebra, ZEBRA))
+ zlog_debug(
+ "%s: Fail to look BGP match entry (%u, ID %u)",
+ __func__, note, unique);
+ return 0;
+ }
+
+ switch (note) {
+ case ZAPI_IPSET_ENTRY_FAIL_INSTALL:
+ if (BGP_DEBUG(zebra, ZEBRA))
+ zlog_debug("%s: Received IPSET_ENTRY_FAIL_INSTALL",
+ __func__);
+ bgp_pbime->installed = false;
+ bgp_pbime->install_in_progress = false;
+ break;
+ case ZAPI_IPSET_ENTRY_INSTALLED:
+ {
+ struct bgp_path_info *path;
+ struct bgp_path_info_extra *extra;
+
+ bgp_pbime->installed = true;
+ bgp_pbime->install_in_progress = false;
+ if (BGP_DEBUG(zebra, ZEBRA))
+ zlog_debug("%s: Received IPSET_ENTRY_INSTALLED",
+ __func__);
+ /* link bgp_path_info to bpme */
+ path = (struct bgp_path_info *)bgp_pbime->path;
+ extra = bgp_path_info_extra_get(path);
+ listnode_add_force(&extra->bgp_fs_pbr, bgp_pbime);
+ }
+ break;
+ case ZAPI_IPSET_ENTRY_FAIL_REMOVE:
+ case ZAPI_IPSET_ENTRY_REMOVED:
+ if (BGP_DEBUG(zebra, ZEBRA))
+ zlog_debug("%s: Received IPSET_ENTRY_REMOVED",
+ __func__);
+ break;
+ }
+ return 0;
+}
+
+static int iptable_notify_owner(ZAPI_CALLBACK_ARGS)
+{
+ uint32_t unique;
+ enum zapi_iptable_notify_owner note;
+ struct bgp_pbr_match *bgpm;
+
+ if (!zapi_iptable_notify_decode(
+ zclient->ibuf,
+ &unique,
+ &note))
+ return -1;
+ bgpm = bgp_pbr_match_iptable_lookup(vrf_id, unique);
+ if (!bgpm) {
+ if (BGP_DEBUG(zebra, ZEBRA))
+ zlog_debug("%s: Fail to look BGP iptable (%u %u)",
+ __func__, note, unique);
+ return 0;
+ }
+ switch (note) {
+ case ZAPI_IPTABLE_FAIL_INSTALL:
+ if (BGP_DEBUG(zebra, ZEBRA))
+ zlog_debug("%s: Received IPTABLE_FAIL_INSTALL",
+ __func__);
+ bgpm->installed_in_iptable = false;
+ bgpm->install_iptable_in_progress = false;
+ break;
+ case ZAPI_IPTABLE_INSTALLED:
+ bgpm->installed_in_iptable = true;
+ bgpm->install_iptable_in_progress = false;
+ if (BGP_DEBUG(zebra, ZEBRA))
+ zlog_debug("%s: Received IPTABLE_INSTALLED", __func__);
+ bgpm->action->refcnt++;
+ break;
+ case ZAPI_IPTABLE_FAIL_REMOVE:
+ case ZAPI_IPTABLE_REMOVED:
+ if (BGP_DEBUG(zebra, ZEBRA))
+ zlog_debug("%s: Received IPTABLE REMOVED", __func__);
+ break;
+ }
+ return 0;
+}
+
+/* Process route notification messages from RIB */
+static int bgp_zebra_route_notify_owner(int command, struct zclient *zclient,
+ zebra_size_t length, vrf_id_t vrf_id)
+{
+ struct prefix p;
+ enum zapi_route_notify_owner note;
+ uint32_t table_id;
+ afi_t afi;
+ safi_t safi;
+ struct bgp_dest *dest;
+ struct bgp *bgp;
+ struct bgp_path_info *pi, *new_select;
+
+ if (!zapi_route_notify_decode(zclient->ibuf, &p, &table_id, &note,
+ &afi, &safi)) {
+ zlog_err("%s : error in msg decode", __func__);
+ return -1;
+ }
+
+ /* Get the bgp instance */
+ bgp = bgp_lookup_by_vrf_id(vrf_id);
+ if (!bgp) {
+ flog_err(EC_BGP_INVALID_BGP_INSTANCE,
+ "%s : bgp instance not found vrf %d", __func__,
+ vrf_id);
+ return -1;
+ }
+
+ /* Find the bgp route node */
+ dest = bgp_afi_node_lookup(bgp->rib[afi][safi], afi, safi, &p,
+ &bgp->vrf_prd);
+ if (!dest)
+ return -1;
+
+ switch (note) {
+ case ZAPI_ROUTE_INSTALLED:
+ new_select = NULL;
+ /* Clear the flags so that route can be processed */
+ UNSET_FLAG(dest->flags, BGP_NODE_FIB_INSTALL_PENDING);
+ SET_FLAG(dest->flags, BGP_NODE_FIB_INSTALLED);
+ if (BGP_DEBUG(zebra, ZEBRA))
+ zlog_debug("route %pRN : INSTALLED", dest);
+ /* Find the best route */
+ for (pi = dest->info; pi; pi = pi->next) {
+ /* Process aggregate route */
+ bgp_aggregate_increment(bgp, &p, pi, afi, safi);
+ if (CHECK_FLAG(pi->flags, BGP_PATH_SELECTED))
+ new_select = pi;
+ }
+ /* Advertise the route */
+ if (new_select)
+ group_announce_route(bgp, afi, safi, dest, new_select);
+ else {
+ flog_err(EC_BGP_INVALID_ROUTE,
+ "selected route %pRN not found", dest);
+
+ bgp_dest_unlock_node(dest);
+ return -1;
+ }
+ break;
+ case ZAPI_ROUTE_REMOVED:
+ /* Route deleted from dataplane, reset the installed flag
+ * so that route can be reinstalled when client sends
+ * route add later
+ */
+ UNSET_FLAG(dest->flags, BGP_NODE_FIB_INSTALLED);
+ if (BGP_DEBUG(zebra, ZEBRA))
+ zlog_debug("route %pRN: Removed from Fib", dest);
+ break;
+ case ZAPI_ROUTE_FAIL_INSTALL:
+ new_select = NULL;
+ if (BGP_DEBUG(zebra, ZEBRA))
+ zlog_debug("route: %pRN Failed to Install into Fib",
+ dest);
+ UNSET_FLAG(dest->flags, BGP_NODE_FIB_INSTALL_PENDING);
+ UNSET_FLAG(dest->flags, BGP_NODE_FIB_INSTALLED);
+ for (pi = bgp_dest_get_bgp_path_info(dest); pi; pi = pi->next) {
+ if (CHECK_FLAG(pi->flags, BGP_PATH_SELECTED))
+ new_select = pi;
+ }
+ if (new_select)
+ group_announce_route(bgp, afi, safi, dest, new_select);
+ /* Error will be logged by zebra module */
+ break;
+ case ZAPI_ROUTE_BETTER_ADMIN_WON:
+ if (BGP_DEBUG(zebra, ZEBRA))
+ zlog_debug("route: %pRN removed due to better admin won",
+ dest);
+ new_select = NULL;
+ UNSET_FLAG(dest->flags, BGP_NODE_FIB_INSTALL_PENDING);
+ UNSET_FLAG(dest->flags, BGP_NODE_FIB_INSTALLED);
+ for (pi = bgp_dest_get_bgp_path_info(dest); pi; pi = pi->next) {
+ bgp_aggregate_decrement(bgp, &p, pi, afi, safi);
+ if (CHECK_FLAG(pi->flags, BGP_PATH_SELECTED))
+ new_select = pi;
+ }
+ if (new_select)
+ group_announce_route(bgp, afi, safi, dest, new_select);
+ /* No action required */
+ break;
+ case ZAPI_ROUTE_REMOVE_FAIL:
+ zlog_warn("%s: Route %pRN failure to remove",
+ __func__, dest);
+ break;
+ }
+
+ bgp_dest_unlock_node(dest);
+ return 0;
+}
+
+/* this function is used to forge ip rule,
+ * - either for iptable/ipset using fwmark id
+ * - or for sample ip rule cmd
+ */
+static void bgp_encode_pbr_rule_action(struct stream *s,
+ struct bgp_pbr_action *pbra,
+ struct bgp_pbr_rule *pbr)
+{
+ struct prefix pfx;
+ uint8_t fam = AF_INET;
+ char ifname[INTERFACE_NAMSIZ];
+
+ if (pbra->nh.type == NEXTHOP_TYPE_IPV6)
+ fam = AF_INET6;
+ stream_putl(s, 0); /* seqno unused */
+ if (pbr)
+ stream_putl(s, pbr->priority);
+ else
+ stream_putl(s, 0);
+ /* ruleno unused - priority change
+ * ruleno permits distinguishing various FS PBR entries
+ * - FS PBR entries based on ipset/iptables
+ * - FS PBR entries based on iprule
+ * the latter may contain default routing information injected by FS
+ */
+ if (pbr)
+ stream_putl(s, pbr->unique);
+ else
+ stream_putl(s, pbra->unique);
+ stream_putc(s, 0); /* ip protocol being used */
+ if (pbr && pbr->flags & MATCH_IP_SRC_SET)
+ memcpy(&pfx, &(pbr->src), sizeof(struct prefix));
+ else {
+ memset(&pfx, 0, sizeof(pfx));
+ pfx.family = fam;
+ }
+ stream_putc(s, pfx.family);
+ stream_putc(s, pfx.prefixlen);
+ stream_put(s, &pfx.u.prefix, prefix_blen(&pfx));
+
+ stream_putw(s, 0); /* src port */
+
+ if (pbr && pbr->flags & MATCH_IP_DST_SET)
+ memcpy(&pfx, &(pbr->dst), sizeof(struct prefix));
+ else {
+ memset(&pfx, 0, sizeof(pfx));
+ pfx.family = fam;
+ }
+ stream_putc(s, pfx.family);
+ stream_putc(s, pfx.prefixlen);
+ stream_put(s, &pfx.u.prefix, prefix_blen(&pfx));
+
+ stream_putw(s, 0); /* dst port */
+ stream_putc(s, 0); /* dsfield */
+ /* if pbr present, fwmark is not used */
+ if (pbr)
+ stream_putl(s, 0);
+ else
+ stream_putl(s, pbra->fwmark); /* fwmark */
+
+ stream_putl(s, 0); /* queue id */
+ stream_putw(s, 0); /* vlan_id */
+ stream_putw(s, 0); /* vlan_flags */
+ stream_putw(s, 0); /* pcp */
+
+ stream_putl(s, pbra->table_id);
+
+ memset(ifname, 0, sizeof(ifname));
+ stream_put(s, ifname, INTERFACE_NAMSIZ); /* ifname unused */
+}
+
+static void bgp_encode_pbr_ipset_match(struct stream *s,
+ struct bgp_pbr_match *pbim)
+{
+ stream_putl(s, pbim->unique);
+ stream_putl(s, pbim->type);
+ stream_putc(s, pbim->family);
+ stream_put(s, pbim->ipset_name,
+ ZEBRA_IPSET_NAME_SIZE);
+}
+
+static void bgp_encode_pbr_ipset_entry_match(struct stream *s,
+ struct bgp_pbr_match_entry *pbime)
+{
+ stream_putl(s, pbime->unique);
+ /* check that back pointer is not null */
+ stream_put(s, pbime->backpointer->ipset_name,
+ ZEBRA_IPSET_NAME_SIZE);
+
+ stream_putc(s, pbime->src.family);
+ stream_putc(s, pbime->src.prefixlen);
+ stream_put(s, &pbime->src.u.prefix, prefix_blen(&pbime->src));
+
+ stream_putc(s, pbime->dst.family);
+ stream_putc(s, pbime->dst.prefixlen);
+ stream_put(s, &pbime->dst.u.prefix, prefix_blen(&pbime->dst));
+
+ stream_putw(s, pbime->src_port_min);
+ stream_putw(s, pbime->src_port_max);
+ stream_putw(s, pbime->dst_port_min);
+ stream_putw(s, pbime->dst_port_max);
+ stream_putc(s, pbime->proto);
+}
+
+static void bgp_encode_pbr_iptable_match(struct stream *s,
+ struct bgp_pbr_action *bpa,
+ struct bgp_pbr_match *pbm)
+{
+ stream_putl(s, pbm->unique2);
+
+ stream_putl(s, pbm->type);
+
+ stream_putl(s, pbm->flags);
+
+ /* TODO: correlate with what is contained
+ * into bgp_pbr_action.
+ * currently only forward supported
+ */
+ if (bpa->nh.type == NEXTHOP_TYPE_BLACKHOLE)
+ stream_putl(s, ZEBRA_IPTABLES_DROP);
+ else
+ stream_putl(s, ZEBRA_IPTABLES_FORWARD);
+ stream_putl(s, bpa->fwmark);
+ stream_put(s, pbm->ipset_name,
+ ZEBRA_IPSET_NAME_SIZE);
+ stream_putc(s, pbm->family);
+ stream_putw(s, pbm->pkt_len_min);
+ stream_putw(s, pbm->pkt_len_max);
+ stream_putw(s, pbm->tcp_flags);
+ stream_putw(s, pbm->tcp_mask_flags);
+ stream_putc(s, pbm->dscp_value);
+ stream_putc(s, pbm->fragment);
+ stream_putc(s, pbm->protocol);
+ stream_putw(s, pbm->flow_label);
+}
+
+/* BGP has established connection with Zebra. */
+static void bgp_zebra_connected(struct zclient *zclient)
+{
+ struct bgp *bgp;
+
+ zclient_num_connects++; /* increment even if not responding */
+
+ /* Send the client registration */
+ bfd_client_sendmsg(zclient, ZEBRA_BFD_CLIENT_REGISTER, VRF_DEFAULT);
+
+ /* At this point, we may or may not have BGP instances configured, but
+ * we're only interested in the default VRF (others wouldn't have learnt
+ * the VRF from Zebra yet.)
+ */
+ bgp = bgp_get_default();
+ if (!bgp)
+ return;
+
+ bgp_zebra_instance_register(bgp);
+
+ /* tell label pool that zebra is connected */
+ bgp_lp_event_zebra_up();
+
+ /* TODO - What if we have peers and networks configured, do we have to
+ * kick-start them?
+ */
+ BGP_GR_ROUTER_DETECT_AND_SEND_CAPABILITY_TO_ZEBRA(bgp, bgp->peer);
+}
+
+static int bgp_zebra_process_local_es_add(ZAPI_CALLBACK_ARGS)
+{
+ esi_t esi;
+ struct bgp *bgp = NULL;
+ struct stream *s = NULL;
+ char buf[ESI_STR_LEN];
+ struct in_addr originator_ip;
+ uint8_t active;
+ uint8_t bypass;
+ uint16_t df_pref;
+
+ bgp = bgp_lookup_by_vrf_id(vrf_id);
+ if (!bgp)
+ return 0;
+
+ s = zclient->ibuf;
+ stream_get(&esi, s, sizeof(esi_t));
+ originator_ip.s_addr = stream_get_ipv4(s);
+ active = stream_getc(s);
+ df_pref = stream_getw(s);
+ bypass = stream_getc(s);
+
+ if (BGP_DEBUG(zebra, ZEBRA))
+ zlog_debug(
+ "Rx add ESI %s originator-ip %pI4 active %u df_pref %u %s",
+ esi_to_str(&esi, buf, sizeof(buf)), &originator_ip,
+ active, df_pref, bypass ? "bypass" : "");
+
+ frrtrace(5, frr_bgp, evpn_mh_local_es_add_zrecv, &esi, originator_ip,
+ active, bypass, df_pref);
+
+ bgp_evpn_local_es_add(bgp, &esi, originator_ip, active, df_pref,
+ !!bypass);
+
+ return 0;
+}
+
+static int bgp_zebra_process_local_es_del(ZAPI_CALLBACK_ARGS)
+{
+ esi_t esi;
+ struct bgp *bgp = NULL;
+ struct stream *s = NULL;
+ char buf[ESI_STR_LEN];
+
+ memset(&esi, 0, sizeof(esi_t));
+ bgp = bgp_lookup_by_vrf_id(vrf_id);
+ if (!bgp)
+ return 0;
+
+ s = zclient->ibuf;
+ stream_get(&esi, s, sizeof(esi_t));
+
+ if (BGP_DEBUG(zebra, ZEBRA))
+ zlog_debug("Rx del ESI %s",
+ esi_to_str(&esi, buf, sizeof(buf)));
+
+ frrtrace(1, frr_bgp, evpn_mh_local_es_del_zrecv, &esi);
+
+ bgp_evpn_local_es_del(bgp, &esi);
+
+ return 0;
+}
+
+static int bgp_zebra_process_local_es_evi(ZAPI_CALLBACK_ARGS)
+{
+ esi_t esi;
+ vni_t vni;
+ struct bgp *bgp;
+ struct stream *s;
+ char buf[ESI_STR_LEN];
+
+ bgp = bgp_lookup_by_vrf_id(vrf_id);
+ if (!bgp)
+ return 0;
+
+ s = zclient->ibuf;
+ stream_get(&esi, s, sizeof(esi_t));
+ vni = stream_getl(s);
+
+ if (BGP_DEBUG(zebra, ZEBRA))
+ zlog_debug("Rx %s ESI %s VNI %u",
+ (cmd == ZEBRA_VNI_ADD) ? "add" : "del",
+ esi_to_str(&esi, buf, sizeof(buf)), vni);
+
+ if (cmd == ZEBRA_LOCAL_ES_EVI_ADD) {
+ frrtrace(2, frr_bgp, evpn_mh_local_es_evi_add_zrecv, &esi, vni);
+
+ bgp_evpn_local_es_evi_add(bgp, &esi, vni);
+ } else {
+ frrtrace(2, frr_bgp, evpn_mh_local_es_evi_del_zrecv, &esi, vni);
+
+ bgp_evpn_local_es_evi_del(bgp, &esi, vni);
+ }
+
+ return 0;
+}
+
+static int bgp_zebra_process_local_l3vni(ZAPI_CALLBACK_ARGS)
+{
+ int filter = 0;
+ vni_t l3vni = 0;
+ struct ethaddr svi_rmac, vrr_rmac = {.octet = {0} };
+ struct in_addr originator_ip;
+ struct stream *s;
+ ifindex_t svi_ifindex;
+ bool is_anycast_mac = false;
+
+ memset(&svi_rmac, 0, sizeof(svi_rmac));
+ memset(&originator_ip, 0, sizeof(originator_ip));
+ s = zclient->ibuf;
+ l3vni = stream_getl(s);
+ if (cmd == ZEBRA_L3VNI_ADD) {
+ stream_get(&svi_rmac, s, sizeof(struct ethaddr));
+ originator_ip.s_addr = stream_get_ipv4(s);
+ stream_get(&filter, s, sizeof(int));
+ svi_ifindex = stream_getl(s);
+ stream_get(&vrr_rmac, s, sizeof(struct ethaddr));
+ is_anycast_mac = stream_getl(s);
+
+ if (BGP_DEBUG(zebra, ZEBRA))
+ zlog_debug(
+ "Rx L3-VNI ADD VRF %s VNI %u RMAC svi-mac %pEA vrr-mac %pEA filter %s svi-if %u",
+ vrf_id_to_name(vrf_id), l3vni, &svi_rmac,
+ &vrr_rmac,
+ filter ? "prefix-routes-only" : "none",
+ svi_ifindex);
+
+ frrtrace(8, frr_bgp, evpn_local_l3vni_add_zrecv, l3vni, vrf_id,
+ &svi_rmac, &vrr_rmac, filter, originator_ip,
+ svi_ifindex, is_anycast_mac);
+
+ bgp_evpn_local_l3vni_add(l3vni, vrf_id, &svi_rmac, &vrr_rmac,
+ originator_ip, filter, svi_ifindex,
+ is_anycast_mac);
+ } else {
+ if (BGP_DEBUG(zebra, ZEBRA))
+ zlog_debug("Rx L3-VNI DEL VRF %s VNI %u",
+ vrf_id_to_name(vrf_id), l3vni);
+
+ frrtrace(2, frr_bgp, evpn_local_l3vni_del_zrecv, l3vni, vrf_id);
+
+ bgp_evpn_local_l3vni_del(l3vni, vrf_id);
+ }
+
+ return 0;
+}
+
+static int bgp_zebra_process_local_vni(ZAPI_CALLBACK_ARGS)
+{
+ struct stream *s;
+ vni_t vni;
+ struct bgp *bgp;
+ struct in_addr vtep_ip = {INADDR_ANY};
+ vrf_id_t tenant_vrf_id = VRF_DEFAULT;
+ struct in_addr mcast_grp = {INADDR_ANY};
+ ifindex_t svi_ifindex = 0;
+
+ s = zclient->ibuf;
+ vni = stream_getl(s);
+ if (cmd == ZEBRA_VNI_ADD) {
+ vtep_ip.s_addr = stream_get_ipv4(s);
+ stream_get(&tenant_vrf_id, s, sizeof(vrf_id_t));
+ mcast_grp.s_addr = stream_get_ipv4(s);
+ stream_get(&svi_ifindex, s, sizeof(ifindex_t));
+ }
+
+ bgp = bgp_lookup_by_vrf_id(vrf_id);
+ if (!bgp)
+ return 0;
+
+ if (BGP_DEBUG(zebra, ZEBRA))
+ zlog_debug(
+ "Rx VNI %s VRF %s VNI %u tenant-vrf %s SVI ifindex %u",
+ (cmd == ZEBRA_VNI_ADD) ? "add" : "del",
+ vrf_id_to_name(vrf_id), vni,
+ vrf_id_to_name(tenant_vrf_id), svi_ifindex);
+
+ if (cmd == ZEBRA_VNI_ADD) {
+ frrtrace(4, frr_bgp, evpn_local_vni_add_zrecv, vni, vtep_ip,
+ tenant_vrf_id, mcast_grp);
+
+ return bgp_evpn_local_vni_add(
+ bgp, vni,
+ vtep_ip.s_addr != INADDR_ANY ? vtep_ip : bgp->router_id,
+ tenant_vrf_id, mcast_grp, svi_ifindex);
+ } else {
+ frrtrace(1, frr_bgp, evpn_local_vni_del_zrecv, vni);
+
+ return bgp_evpn_local_vni_del(bgp, vni);
+ }
+}
+
+static int bgp_zebra_process_local_macip(ZAPI_CALLBACK_ARGS)
+{
+ struct stream *s;
+ vni_t vni;
+ struct bgp *bgp;
+ struct ethaddr mac;
+ struct ipaddr ip;
+ int ipa_len;
+ uint8_t flags = 0;
+ uint32_t seqnum = 0;
+ int state = 0;
+ char buf2[ESI_STR_LEN];
+ esi_t esi;
+
+ memset(&ip, 0, sizeof(ip));
+ s = zclient->ibuf;
+ vni = stream_getl(s);
+ stream_get(&mac.octet, s, ETH_ALEN);
+ ipa_len = stream_getl(s);
+ if (ipa_len != 0 && ipa_len != IPV4_MAX_BYTELEN
+ && ipa_len != IPV6_MAX_BYTELEN) {
+ flog_err(EC_BGP_MACIP_LEN,
+ "%u:Recv MACIP %s with invalid IP addr length %d",
+ vrf_id, (cmd == ZEBRA_MACIP_ADD) ? "Add" : "Del",
+ ipa_len);
+ return -1;
+ }
+
+ if (ipa_len) {
+ ip.ipa_type =
+ (ipa_len == IPV4_MAX_BYTELEN) ? IPADDR_V4 : IPADDR_V6;
+ stream_get(&ip.ip.addr, s, ipa_len);
+ }
+ if (cmd == ZEBRA_MACIP_ADD) {
+ flags = stream_getc(s);
+ seqnum = stream_getl(s);
+ stream_get(&esi, s, sizeof(esi_t));
+ } else {
+ state = stream_getl(s);
+ memset(&esi, 0, sizeof(esi_t));
+ }
+
+ bgp = bgp_lookup_by_vrf_id(vrf_id);
+ if (!bgp)
+ return 0;
+
+ if (BGP_DEBUG(zebra, ZEBRA))
+ zlog_debug(
+ "%u:Recv MACIP %s f 0x%x MAC %pEA IP %pIA VNI %u seq %u state %d ESI %s",
+ vrf_id, (cmd == ZEBRA_MACIP_ADD) ? "Add" : "Del", flags,
+ &mac, &ip, vni, seqnum, state,
+ esi_to_str(&esi, buf2, sizeof(buf2)));
+
+ if (cmd == ZEBRA_MACIP_ADD) {
+ frrtrace(6, frr_bgp, evpn_local_macip_add_zrecv, vni, &mac, &ip,
+ flags, seqnum, &esi);
+
+ return bgp_evpn_local_macip_add(bgp, vni, &mac, &ip,
+ flags, seqnum, &esi);
+ } else {
+ frrtrace(4, frr_bgp, evpn_local_macip_del_zrecv, vni, &mac, &ip,
+ state);
+
+ return bgp_evpn_local_macip_del(bgp, vni, &mac, &ip, state);
+ }
+}
+
+static int bgp_zebra_process_local_ip_prefix(ZAPI_CALLBACK_ARGS)
+{
+ struct stream *s = NULL;
+ struct bgp *bgp_vrf = NULL;
+ struct prefix p;
+
+ memset(&p, 0, sizeof(p));
+ s = zclient->ibuf;
+ stream_get(&p, s, sizeof(struct prefix));
+
+ bgp_vrf = bgp_lookup_by_vrf_id(vrf_id);
+ if (!bgp_vrf)
+ return 0;
+
+ if (BGP_DEBUG(zebra, ZEBRA))
+ zlog_debug("Recv prefix %pFX %s on vrf %s", &p,
+ (cmd == ZEBRA_IP_PREFIX_ROUTE_ADD) ? "ADD" : "DEL",
+ vrf_id_to_name(vrf_id));
+
+ if (cmd == ZEBRA_IP_PREFIX_ROUTE_ADD) {
+
+ if (p.family == AF_INET)
+ bgp_evpn_advertise_type5_route(bgp_vrf, &p, NULL,
+ AFI_IP, SAFI_UNICAST);
+ else
+ bgp_evpn_advertise_type5_route(bgp_vrf, &p, NULL,
+ AFI_IP6, SAFI_UNICAST);
+
+ } else {
+ if (p.family == AF_INET)
+ bgp_evpn_withdraw_type5_route(bgp_vrf, &p, AFI_IP,
+ SAFI_UNICAST);
+ else
+ bgp_evpn_withdraw_type5_route(bgp_vrf, &p, AFI_IP6,
+ SAFI_UNICAST);
+ }
+ return 0;
+}
+
+static int bgp_zebra_process_label_chunk(ZAPI_CALLBACK_ARGS)
+{
+ struct stream *s = NULL;
+ uint8_t response_keep;
+ uint32_t first;
+ uint32_t last;
+ uint8_t proto;
+ unsigned short instance;
+
+ s = zclient->ibuf;
+ STREAM_GETC(s, proto);
+ STREAM_GETW(s, instance);
+ STREAM_GETC(s, response_keep);
+ STREAM_GETL(s, first);
+ STREAM_GETL(s, last);
+
+ if (zclient->redist_default != proto) {
+ flog_err(EC_BGP_LM_ERROR, "Got LM msg with wrong proto %u",
+ proto);
+ return 0;
+ }
+ if (zclient->instance != instance) {
+ flog_err(EC_BGP_LM_ERROR, "Got LM msg with wrong instance %u",
+ proto);
+ return 0;
+ }
+
+ if (first > last ||
+ first < MPLS_LABEL_UNRESERVED_MIN ||
+ last > MPLS_LABEL_UNRESERVED_MAX) {
+
+ flog_err(EC_BGP_LM_ERROR, "%s: Invalid Label chunk: %u - %u",
+ __func__, first, last);
+ return 0;
+ }
+ if (BGP_DEBUG(zebra, ZEBRA)) {
+ zlog_debug("Label Chunk assign: %u - %u (%u) ",
+ first, last, response_keep);
+ }
+
+ bgp_lp_event_chunk(response_keep, first, last);
+
+ return 0;
+
+stream_failure: /* for STREAM_GETX */
+ return -1;
+}
+
+extern struct zebra_privs_t bgpd_privs;
+
+static int bgp_ifp_create(struct interface *ifp)
+{
+ struct bgp *bgp;
+
+ if (BGP_DEBUG(zebra, ZEBRA))
+ zlog_debug("Rx Intf add VRF %u IF %s", ifp->vrf->vrf_id,
+ ifp->name);
+
+ bgp = ifp->vrf->info;
+ if (!bgp)
+ return 0;
+
+ bgp_mac_add_mac_entry(ifp);
+
+ bgp_update_interface_nbrs(bgp, ifp, ifp);
+ hook_call(bgp_vrf_status_changed, bgp, ifp);
+ return 0;
+}
+
+static int bgp_zebra_process_srv6_locator_chunk(ZAPI_CALLBACK_ARGS)
+{
+ struct stream *s = NULL;
+ struct bgp *bgp = bgp_get_default();
+ struct listnode *node;
+ struct srv6_locator_chunk *c;
+ struct srv6_locator_chunk *chunk = srv6_locator_chunk_alloc();
+
+ s = zclient->ibuf;
+ zapi_srv6_locator_chunk_decode(s, chunk);
+
+ if (strcmp(bgp->srv6_locator_name, chunk->locator_name) != 0) {
+ zlog_err("%s: Locator name unmatch %s:%s", __func__,
+ bgp->srv6_locator_name, chunk->locator_name);
+ srv6_locator_chunk_free(chunk);
+ return 0;
+ }
+
+ for (ALL_LIST_ELEMENTS_RO(bgp->srv6_locator_chunks, node, c)) {
+ if (!prefix_cmp(&c->prefix, &chunk->prefix)) {
+ srv6_locator_chunk_free(chunk);
+ return 0;
+ }
+ }
+
+ listnode_add(bgp->srv6_locator_chunks, chunk);
+ vpn_leak_postchange_all();
+ return 0;
+}
+
+static int bgp_zebra_process_srv6_locator_add(ZAPI_CALLBACK_ARGS)
+{
+ struct srv6_locator loc = {};
+ struct bgp *bgp = bgp_get_default();
+ const char *loc_name = bgp->srv6_locator_name;
+
+ if (zapi_srv6_locator_decode(zclient->ibuf, &loc) < 0)
+ return -1;
+
+ if (!bgp || !bgp->srv6_enabled)
+ return 0;
+
+ if (bgp_zebra_srv6_manager_get_locator_chunk(loc_name) < 0)
+ return -1;
+
+ return 0;
+}
+
+static int bgp_zebra_process_srv6_locator_delete(ZAPI_CALLBACK_ARGS)
+{
+ struct srv6_locator loc = {};
+ struct bgp *bgp = bgp_get_default();
+ struct listnode *node, *nnode;
+ struct srv6_locator_chunk *chunk;
+ struct bgp_srv6_function *func;
+ struct bgp *bgp_vrf;
+ struct in6_addr *tovpn_sid, *tovpn_sid_locator;
+ struct prefix_ipv6 tmp_prefi;
+
+ if (zapi_srv6_locator_decode(zclient->ibuf, &loc) < 0)
+ return -1;
+
+ // refresh chunks
+ for (ALL_LIST_ELEMENTS(bgp->srv6_locator_chunks, node, nnode, chunk))
+ if (prefix_match((struct prefix *)&loc.prefix,
+ (struct prefix *)&chunk->prefix)) {
+ listnode_delete(bgp->srv6_locator_chunks, chunk);
+ srv6_locator_chunk_free(chunk);
+ }
+
+ // refresh functions
+ for (ALL_LIST_ELEMENTS(bgp->srv6_functions, node, nnode, func)) {
+ tmp_prefi.family = AF_INET6;
+ tmp_prefi.prefixlen = 128;
+ tmp_prefi.prefix = func->sid;
+ if (prefix_match((struct prefix *)&loc.prefix,
+ (struct prefix *)&tmp_prefi)) {
+ listnode_delete(bgp->srv6_functions, func);
+ XFREE(MTYPE_BGP_SRV6_FUNCTION, func);
+ }
+ }
+
+ // refresh tovpn_sid
+ for (ALL_LIST_ELEMENTS_RO(bm->bgp, node, bgp_vrf)) {
+ if (bgp_vrf->inst_type != BGP_INSTANCE_TYPE_VRF)
+ continue;
+
+ // refresh vpnv4 tovpn_sid
+ tovpn_sid = bgp_vrf->vpn_policy[AFI_IP].tovpn_sid;
+ if (tovpn_sid) {
+ tmp_prefi.family = AF_INET6;
+ tmp_prefi.prefixlen = 128;
+ tmp_prefi.prefix = *tovpn_sid;
+ if (prefix_match((struct prefix *)&loc.prefix,
+ (struct prefix *)&tmp_prefi))
+ XFREE(MTYPE_BGP_SRV6_SID,
+ bgp_vrf->vpn_policy[AFI_IP].tovpn_sid);
+ }
+
+ // refresh vpnv6 tovpn_sid
+ tovpn_sid = bgp_vrf->vpn_policy[AFI_IP6].tovpn_sid;
+ if (tovpn_sid) {
+ tmp_prefi.family = AF_INET6;
+ tmp_prefi.prefixlen = 128;
+ tmp_prefi.prefix = *tovpn_sid;
+ if (prefix_match((struct prefix *)&loc.prefix,
+ (struct prefix *)&tmp_prefi))
+ XFREE(MTYPE_BGP_SRV6_SID,
+ bgp_vrf->vpn_policy[AFI_IP6].tovpn_sid);
+ }
+ }
+
+ vpn_leak_postchange_all();
+
+ /* refresh tovpn_sid_locator */
+ for (ALL_LIST_ELEMENTS_RO(bm->bgp, node, bgp_vrf)) {
+ if (bgp_vrf->inst_type != BGP_INSTANCE_TYPE_VRF)
+ continue;
+
+ /* refresh vpnv4 tovpn_sid_locator */
+ tovpn_sid_locator =
+ bgp_vrf->vpn_policy[AFI_IP].tovpn_sid_locator;
+ if (tovpn_sid_locator) {
+ tmp_prefi.family = AF_INET6;
+ tmp_prefi.prefixlen = IPV6_MAX_BITLEN;
+ tmp_prefi.prefix = *tovpn_sid_locator;
+ if (prefix_match((struct prefix *)&loc.prefix,
+ (struct prefix *)&tmp_prefi))
+ XFREE(MTYPE_BGP_SRV6_SID, tovpn_sid_locator);
+ }
+
+ /* refresh vpnv6 tovpn_sid_locator */
+ tovpn_sid_locator =
+ bgp_vrf->vpn_policy[AFI_IP6].tovpn_sid_locator;
+ if (tovpn_sid_locator) {
+ tmp_prefi.family = AF_INET6;
+ tmp_prefi.prefixlen = IPV6_MAX_BITLEN;
+ tmp_prefi.prefix = *tovpn_sid_locator;
+ if (prefix_match((struct prefix *)&loc.prefix,
+ (struct prefix *)&tmp_prefi))
+ XFREE(MTYPE_BGP_SRV6_SID, tovpn_sid_locator);
+ }
+ }
+
+ return 0;
+}
+
+static zclient_handler *const bgp_handlers[] = {
+ [ZEBRA_ROUTER_ID_UPDATE] = bgp_router_id_update,
+ [ZEBRA_INTERFACE_ADDRESS_ADD] = bgp_interface_address_add,
+ [ZEBRA_INTERFACE_ADDRESS_DELETE] = bgp_interface_address_delete,
+ [ZEBRA_INTERFACE_NBR_ADDRESS_ADD] = bgp_interface_nbr_address_add,
+ [ZEBRA_INTERFACE_NBR_ADDRESS_DELETE] = bgp_interface_nbr_address_delete,
+ [ZEBRA_INTERFACE_VRF_UPDATE] = bgp_interface_vrf_update,
+ [ZEBRA_REDISTRIBUTE_ROUTE_ADD] = zebra_read_route,
+ [ZEBRA_REDISTRIBUTE_ROUTE_DEL] = zebra_read_route,
+ [ZEBRA_NEXTHOP_UPDATE] = bgp_read_nexthop_update,
+ [ZEBRA_FEC_UPDATE] = bgp_read_fec_update,
+ [ZEBRA_LOCAL_ES_ADD] = bgp_zebra_process_local_es_add,
+ [ZEBRA_LOCAL_ES_DEL] = bgp_zebra_process_local_es_del,
+ [ZEBRA_VNI_ADD] = bgp_zebra_process_local_vni,
+ [ZEBRA_LOCAL_ES_EVI_ADD] = bgp_zebra_process_local_es_evi,
+ [ZEBRA_LOCAL_ES_EVI_DEL] = bgp_zebra_process_local_es_evi,
+ [ZEBRA_VNI_DEL] = bgp_zebra_process_local_vni,
+ [ZEBRA_MACIP_ADD] = bgp_zebra_process_local_macip,
+ [ZEBRA_MACIP_DEL] = bgp_zebra_process_local_macip,
+ [ZEBRA_L3VNI_ADD] = bgp_zebra_process_local_l3vni,
+ [ZEBRA_L3VNI_DEL] = bgp_zebra_process_local_l3vni,
+ [ZEBRA_IP_PREFIX_ROUTE_ADD] = bgp_zebra_process_local_ip_prefix,
+ [ZEBRA_IP_PREFIX_ROUTE_DEL] = bgp_zebra_process_local_ip_prefix,
+ [ZEBRA_GET_LABEL_CHUNK] = bgp_zebra_process_label_chunk,
+ [ZEBRA_RULE_NOTIFY_OWNER] = rule_notify_owner,
+ [ZEBRA_IPSET_NOTIFY_OWNER] = ipset_notify_owner,
+ [ZEBRA_IPSET_ENTRY_NOTIFY_OWNER] = ipset_entry_notify_owner,
+ [ZEBRA_IPTABLE_NOTIFY_OWNER] = iptable_notify_owner,
+ [ZEBRA_ROUTE_NOTIFY_OWNER] = bgp_zebra_route_notify_owner,
+ [ZEBRA_SRV6_LOCATOR_ADD] = bgp_zebra_process_srv6_locator_add,
+ [ZEBRA_SRV6_LOCATOR_DELETE] = bgp_zebra_process_srv6_locator_delete,
+ [ZEBRA_SRV6_MANAGER_GET_LOCATOR_CHUNK] =
+ bgp_zebra_process_srv6_locator_chunk,
+};
+
+static int bgp_if_new_hook(struct interface *ifp)
+{
+ struct bgp_interface *iifp;
+
+ if (ifp->info)
+ return 0;
+ iifp = XCALLOC(MTYPE_BGP_IF_INFO, sizeof(struct bgp_interface));
+ ifp->info = iifp;
+
+ return 0;
+}
+
+static int bgp_if_delete_hook(struct interface *ifp)
+{
+ XFREE(MTYPE_BGP_IF_INFO, ifp->info);
+ return 0;
+}
+
+void bgp_if_init(void)
+{
+ /* Initialize Zebra interface data structure. */
+ hook_register_prio(if_add, 0, bgp_if_new_hook);
+ hook_register_prio(if_del, 0, bgp_if_delete_hook);
+}
+
+void bgp_zebra_init(struct thread_master *master, unsigned short instance)
+{
+ zclient_num_connects = 0;
+
+ if_zapi_callbacks(bgp_ifp_create, bgp_ifp_up,
+ bgp_ifp_down, bgp_ifp_destroy);
+
+ /* Set default values. */
+ zclient = zclient_new(master, &zclient_options_default, bgp_handlers,
+ array_size(bgp_handlers));
+ zclient_init(zclient, ZEBRA_ROUTE_BGP, 0, &bgpd_privs);
+ zclient->zebra_connected = bgp_zebra_connected;
+ zclient->instance = instance;
+}
+
+void bgp_zebra_destroy(void)
+{
+ if (zclient == NULL)
+ return;
+ zclient_stop(zclient);
+ zclient_free(zclient);
+ zclient = NULL;
+}
+
+int bgp_zebra_num_connects(void)
+{
+ return zclient_num_connects;
+}
+
+void bgp_send_pbr_rule_action(struct bgp_pbr_action *pbra,
+ struct bgp_pbr_rule *pbr,
+ bool install)
+{
+ struct stream *s;
+
+ if (pbra->install_in_progress && !pbr)
+ return;
+ if (pbr && pbr->install_in_progress)
+ return;
+ if (BGP_DEBUG(zebra, ZEBRA)) {
+ if (pbr)
+ zlog_debug("%s: table %d (ip rule) %d", __func__,
+ pbra->table_id, install);
+ else
+ zlog_debug("%s: table %d fwmark %d %d", __func__,
+ pbra->table_id, pbra->fwmark, install);
+ }
+ s = zclient->obuf;
+ stream_reset(s);
+
+ zclient_create_header(s,
+ install ? ZEBRA_RULE_ADD : ZEBRA_RULE_DELETE,
+ VRF_DEFAULT);
+ stream_putl(s, 1); /* send one pbr action */
+
+ bgp_encode_pbr_rule_action(s, pbra, pbr);
+
+ stream_putw_at(s, 0, stream_get_endp(s));
+ if ((zclient_send_message(zclient) != ZCLIENT_SEND_FAILURE)
+ && install) {
+ if (!pbr)
+ pbra->install_in_progress = true;
+ else
+ pbr->install_in_progress = true;
+ }
+}
+
+void bgp_send_pbr_ipset_match(struct bgp_pbr_match *pbrim, bool install)
+{
+ struct stream *s;
+
+ if (pbrim->install_in_progress)
+ return;
+ if (BGP_DEBUG(zebra, ZEBRA))
+ zlog_debug("%s: name %s type %d %d, ID %u", __func__,
+ pbrim->ipset_name, pbrim->type, install,
+ pbrim->unique);
+ s = zclient->obuf;
+ stream_reset(s);
+
+ zclient_create_header(s,
+ install ? ZEBRA_IPSET_CREATE :
+ ZEBRA_IPSET_DESTROY,
+ VRF_DEFAULT);
+
+ stream_putl(s, 1); /* send one pbr action */
+
+ bgp_encode_pbr_ipset_match(s, pbrim);
+
+ stream_putw_at(s, 0, stream_get_endp(s));
+ if ((zclient_send_message(zclient) != ZCLIENT_SEND_FAILURE) && install)
+ pbrim->install_in_progress = true;
+}
+
+void bgp_send_pbr_ipset_entry_match(struct bgp_pbr_match_entry *pbrime,
+ bool install)
+{
+ struct stream *s;
+
+ if (pbrime->install_in_progress)
+ return;
+ if (BGP_DEBUG(zebra, ZEBRA))
+ zlog_debug("%s: name %s %d %d, ID %u", __func__,
+ pbrime->backpointer->ipset_name, pbrime->unique,
+ install, pbrime->unique);
+ s = zclient->obuf;
+ stream_reset(s);
+
+ zclient_create_header(s,
+ install ? ZEBRA_IPSET_ENTRY_ADD :
+ ZEBRA_IPSET_ENTRY_DELETE,
+ VRF_DEFAULT);
+
+ stream_putl(s, 1); /* send one pbr action */
+
+ bgp_encode_pbr_ipset_entry_match(s, pbrime);
+
+ stream_putw_at(s, 0, stream_get_endp(s));
+ if ((zclient_send_message(zclient) != ZCLIENT_SEND_FAILURE) && install)
+ pbrime->install_in_progress = true;
+}
+
+static void bgp_encode_pbr_interface_list(struct bgp *bgp, struct stream *s,
+ uint8_t family)
+{
+ struct bgp_pbr_config *bgp_pbr_cfg = bgp->bgp_pbr_cfg;
+ struct bgp_pbr_interface_head *head;
+ struct bgp_pbr_interface *pbr_if;
+ struct interface *ifp;
+
+ if (!bgp_pbr_cfg)
+ return;
+ if (family == AF_INET)
+ head = &(bgp_pbr_cfg->ifaces_by_name_ipv4);
+ else
+ head = &(bgp_pbr_cfg->ifaces_by_name_ipv6);
+ RB_FOREACH (pbr_if, bgp_pbr_interface_head, head) {
+ ifp = if_lookup_by_name(pbr_if->name, bgp->vrf_id);
+ if (ifp)
+ stream_putl(s, ifp->ifindex);
+ }
+}
+
+static int bgp_pbr_get_ifnumber(struct bgp *bgp, uint8_t family)
+{
+ struct bgp_pbr_config *bgp_pbr_cfg = bgp->bgp_pbr_cfg;
+ struct bgp_pbr_interface_head *head;
+ struct bgp_pbr_interface *pbr_if;
+ int cnt = 0;
+
+ if (!bgp_pbr_cfg)
+ return 0;
+ if (family == AF_INET)
+ head = &(bgp_pbr_cfg->ifaces_by_name_ipv4);
+ else
+ head = &(bgp_pbr_cfg->ifaces_by_name_ipv6);
+ RB_FOREACH (pbr_if, bgp_pbr_interface_head, head) {
+ if (if_lookup_by_name(pbr_if->name, bgp->vrf_id))
+ cnt++;
+ }
+ return cnt;
+}
+
+void bgp_send_pbr_iptable(struct bgp_pbr_action *pba,
+ struct bgp_pbr_match *pbm,
+ bool install)
+{
+ struct stream *s;
+ int ret = 0;
+ int nb_interface;
+
+ if (pbm->install_iptable_in_progress)
+ return;
+ if (BGP_DEBUG(zebra, ZEBRA))
+ zlog_debug("%s: name %s type %d mark %d %d, ID %u", __func__,
+ pbm->ipset_name, pbm->type, pba->fwmark, install,
+ pbm->unique2);
+ s = zclient->obuf;
+ stream_reset(s);
+
+ zclient_create_header(s,
+ install ? ZEBRA_IPTABLE_ADD :
+ ZEBRA_IPTABLE_DELETE,
+ VRF_DEFAULT);
+
+ bgp_encode_pbr_iptable_match(s, pba, pbm);
+ nb_interface = bgp_pbr_get_ifnumber(pba->bgp, pbm->family);
+ stream_putl(s, nb_interface);
+ if (nb_interface)
+ bgp_encode_pbr_interface_list(pba->bgp, s, pbm->family);
+ stream_putw_at(s, 0, stream_get_endp(s));
+ ret = zclient_send_message(zclient);
+ if (install) {
+ if (ret != ZCLIENT_SEND_FAILURE)
+ pba->refcnt++;
+ else
+ pbm->install_iptable_in_progress = true;
+ }
+}
+
+/* inject in table <table_id> a default route to:
+ * - if nexthop IP is present : to this nexthop
+ * - if vrf is different from local : to the matching VRF
+ */
+void bgp_zebra_announce_default(struct bgp *bgp, struct nexthop *nh,
+ afi_t afi, uint32_t table_id, bool announce)
+{
+ struct zapi_nexthop *api_nh;
+ struct zapi_route api;
+ struct prefix p;
+
+ if (!nh || (nh->type != NEXTHOP_TYPE_IPV4
+ && nh->type != NEXTHOP_TYPE_IPV6)
+ || nh->vrf_id == VRF_UNKNOWN)
+ return;
+
+ /* in vrf-lite, no default route has to be announced
+ * the table id of vrf is directly used to divert traffic
+ */
+ if (!vrf_is_backend_netns() && bgp->vrf_id != nh->vrf_id)
+ return;
+
+ memset(&p, 0, sizeof(p));
+ if (afi != AFI_IP && afi != AFI_IP6)
+ return;
+ p.family = afi2family(afi);
+ memset(&api, 0, sizeof(api));
+ api.vrf_id = bgp->vrf_id;
+ api.type = ZEBRA_ROUTE_BGP;
+ api.safi = SAFI_UNICAST;
+ api.prefix = p;
+ api.tableid = table_id;
+ api.nexthop_num = 1;
+ SET_FLAG(api.message, ZAPI_MESSAGE_TABLEID);
+ SET_FLAG(api.message, ZAPI_MESSAGE_NEXTHOP);
+ api_nh = &api.nexthops[0];
+
+ api.distance = ZEBRA_EBGP_DISTANCE_DEFAULT;
+ SET_FLAG(api.message, ZAPI_MESSAGE_DISTANCE);
+
+ /* redirect IP */
+ if (afi == AFI_IP && nh->gate.ipv4.s_addr != INADDR_ANY) {
+ char buff[PREFIX_STRLEN];
+
+ api_nh->vrf_id = nh->vrf_id;
+ api_nh->gate.ipv4 = nh->gate.ipv4;
+ api_nh->type = NEXTHOP_TYPE_IPV4;
+
+ inet_ntop(AF_INET, &(nh->gate.ipv4), buff, INET_ADDRSTRLEN);
+ if (BGP_DEBUG(zebra, ZEBRA))
+ zlog_debug("BGP: %s default route to %s table %d (redirect IP)",
+ announce ? "adding" : "withdrawing",
+ buff, table_id);
+ zclient_route_send(announce ? ZEBRA_ROUTE_ADD
+ : ZEBRA_ROUTE_DELETE,
+ zclient, &api);
+ } else if (afi == AFI_IP6 &&
+ memcmp(&nh->gate.ipv6,
+ &in6addr_any, sizeof(struct in6_addr))) {
+ char buff[PREFIX_STRLEN];
+
+ api_nh->vrf_id = nh->vrf_id;
+ memcpy(&api_nh->gate.ipv6, &nh->gate.ipv6,
+ sizeof(struct in6_addr));
+ api_nh->type = NEXTHOP_TYPE_IPV6;
+
+ inet_ntop(AF_INET6, &(nh->gate.ipv6), buff, INET_ADDRSTRLEN);
+ if (BGP_DEBUG(zebra, ZEBRA))
+ zlog_debug("BGP: %s default route to %s table %d (redirect IP)",
+ announce ? "adding" : "withdrawing",
+ buff, table_id);
+ zclient_route_send(announce ? ZEBRA_ROUTE_ADD
+ : ZEBRA_ROUTE_DELETE,
+ zclient, &api);
+ } else if (nh->vrf_id != bgp->vrf_id) {
+ struct vrf *vrf;
+ struct interface *ifp;
+
+ vrf = vrf_lookup_by_id(nh->vrf_id);
+ if (!vrf)
+ return;
+ /* create default route with interface <VRF>
+ * with nexthop-vrf <VRF>
+ */
+ ifp = if_lookup_by_name_vrf(vrf->name, vrf);
+ if (!ifp)
+ return;
+ api_nh->vrf_id = nh->vrf_id;
+ api_nh->type = NEXTHOP_TYPE_IFINDEX;
+ api_nh->ifindex = ifp->ifindex;
+ if (BGP_DEBUG(zebra, ZEBRA))
+ zlog_info("BGP: %s default route to %s table %d (redirect VRF)",
+ announce ? "adding" : "withdrawing",
+ vrf->name, table_id);
+ zclient_route_send(announce ? ZEBRA_ROUTE_ADD
+ : ZEBRA_ROUTE_DELETE,
+ zclient, &api);
+ return;
+ }
+}
+
+/* Send capabilities to RIB */
+int bgp_zebra_send_capabilities(struct bgp *bgp, bool disable)
+{
+ struct zapi_cap api;
+ int ret = BGP_GR_SUCCESS;
+
+ if (zclient == NULL) {
+ if (BGP_DEBUG(zebra, ZEBRA))
+ zlog_debug("zclient invalid");
+ return BGP_GR_FAILURE;
+ }
+
+ /* Check if the client is connected */
+ if ((zclient->sock < 0) || (zclient->t_connect)) {
+ if (BGP_DEBUG(zebra, ZEBRA))
+ zlog_debug("client not connected");
+ return BGP_GR_FAILURE;
+ }
+
+ /* Check if capability is already sent. If the flag force is set
+ * send the capability since this can be initial bgp configuration
+ */
+ memset(&api, 0, sizeof(api));
+ if (disable) {
+ api.cap = ZEBRA_CLIENT_GR_DISABLE;
+ api.vrf_id = bgp->vrf_id;
+ } else {
+ api.cap = ZEBRA_CLIENT_GR_CAPABILITIES;
+ api.stale_removal_time = bgp->rib_stale_time;
+ api.vrf_id = bgp->vrf_id;
+ }
+
+ if (zclient_capabilities_send(ZEBRA_CLIENT_CAPABILITIES, zclient, &api)
+ == ZCLIENT_SEND_FAILURE) {
+ zlog_err("error sending capability");
+ ret = BGP_GR_FAILURE;
+ } else {
+ if (disable)
+ bgp->present_zebra_gr_state = ZEBRA_GR_DISABLE;
+ else
+ bgp->present_zebra_gr_state = ZEBRA_GR_ENABLE;
+
+ if (BGP_DEBUG(zebra, ZEBRA))
+ zlog_debug("send capabilty success");
+ ret = BGP_GR_SUCCESS;
+ }
+ return ret;
+}
+
+/* Send route update pesding or completed status to RIB for the
+ * specific AFI, SAFI
+ */
+int bgp_zebra_update(afi_t afi, safi_t safi, vrf_id_t vrf_id, int type)
+{
+ struct zapi_cap api = {0};
+
+ if (zclient == NULL) {
+ if (BGP_DEBUG(zebra, ZEBRA))
+ zlog_debug("zclient == NULL, invalid");
+ return BGP_GR_FAILURE;
+ }
+
+ /* Check if the client is connected */
+ if ((zclient->sock < 0) || (zclient->t_connect)) {
+ if (BGP_DEBUG(zebra, ZEBRA))
+ zlog_debug("client not connected");
+ return BGP_GR_FAILURE;
+ }
+
+ api.afi = afi;
+ api.safi = safi;
+ api.vrf_id = vrf_id;
+ api.cap = type;
+
+ if (zclient_capabilities_send(ZEBRA_CLIENT_CAPABILITIES, zclient, &api)
+ == ZCLIENT_SEND_FAILURE) {
+ if (BGP_DEBUG(zebra, ZEBRA))
+ zlog_debug("error sending capability");
+ return BGP_GR_FAILURE;
+ }
+ return BGP_GR_SUCCESS;
+}
+
+
+/* Send RIB stale timer update */
+int bgp_zebra_stale_timer_update(struct bgp *bgp)
+{
+ struct zapi_cap api;
+
+ if (zclient == NULL) {
+ if (BGP_DEBUG(zebra, ZEBRA))
+ zlog_debug("zclient invalid");
+ return BGP_GR_FAILURE;
+ }
+
+ /* Check if the client is connected */
+ if ((zclient->sock < 0) || (zclient->t_connect)) {
+ if (BGP_DEBUG(zebra, ZEBRA))
+ zlog_debug("client not connected");
+ return BGP_GR_FAILURE;
+ }
+
+ memset(&api, 0, sizeof(api));
+ api.cap = ZEBRA_CLIENT_RIB_STALE_TIME;
+ api.stale_removal_time = bgp->rib_stale_time;
+ api.vrf_id = bgp->vrf_id;
+ if (zclient_capabilities_send(ZEBRA_CLIENT_CAPABILITIES, zclient, &api)
+ == ZCLIENT_SEND_FAILURE) {
+ if (BGP_DEBUG(zebra, ZEBRA))
+ zlog_debug("error sending capability");
+ return BGP_GR_FAILURE;
+ }
+ if (BGP_DEBUG(zebra, ZEBRA))
+ zlog_debug("send capabilty success");
+ return BGP_GR_SUCCESS;
+}
+
+int bgp_zebra_srv6_manager_get_locator_chunk(const char *name)
+{
+ return srv6_manager_get_locator_chunk(zclient, name);
+}
+
+int bgp_zebra_srv6_manager_release_locator_chunk(const char *name)
+{
+ return srv6_manager_release_locator_chunk(zclient, name);
+}
diff --git a/bgpd/bgp_zebra.h b/bgpd/bgp_zebra.h
new file mode 100644
index 0000000..0a41069
--- /dev/null
+++ b/bgpd/bgp_zebra.h
@@ -0,0 +1,136 @@
+/* zebra connection and redistribute fucntions.
+ * Copyright (C) 1999 Kunihiro Ishiguro
+ *
+ * This file is part of GNU Zebra.
+ *
+ * GNU Zebra is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License as published by the
+ * Free Software Foundation; either version 2, or (at your option) any
+ * later version.
+ *
+ * GNU Zebra is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program; see the file COPYING; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
+ */
+
+#ifndef _QUAGGA_BGP_ZEBRA_H
+#define _QUAGGA_BGP_ZEBRA_H
+
+#include "vxlan.h"
+
+/* Macro to update bgp_original based on bpg_path_info */
+#define BGP_ORIGINAL_UPDATE(_bgp_orig, _mpinfo, _bgp) \
+ ((_mpinfo->extra && _mpinfo->extra->bgp_orig \
+ && _mpinfo->sub_type == BGP_ROUTE_IMPORTED) \
+ ? (_bgp_orig = _mpinfo->extra->bgp_orig) \
+ : (_bgp_orig = _bgp))
+
+/* Default weight for next hop, if doing weighted ECMP. */
+#define BGP_ZEBRA_DEFAULT_NHOP_WEIGHT 1
+
+extern void bgp_zebra_init(struct thread_master *master,
+ unsigned short instance);
+extern void bgp_if_init(void);
+extern void bgp_zebra_init_tm_connect(struct bgp *bgp);
+extern uint32_t bgp_zebra_tm_get_id(void);
+extern bool bgp_zebra_tm_chunk_obtained(void);
+extern void bgp_zebra_destroy(void);
+extern int bgp_zebra_get_table_range(uint32_t chunk_size,
+ uint32_t *start, uint32_t *end);
+extern int bgp_if_update_all(void);
+extern void bgp_zebra_announce(struct bgp_dest *dest, const struct prefix *p,
+ struct bgp_path_info *path, struct bgp *bgp,
+ afi_t afi, safi_t safi);
+extern void bgp_zebra_announce_table(struct bgp *bgp, afi_t afi, safi_t safi);
+extern void bgp_zebra_withdraw(const struct prefix *p,
+ struct bgp_path_info *path, struct bgp *bgp,
+ safi_t safi);
+
+/* Announce routes of any bgp subtype of a table to zebra */
+extern void bgp_zebra_announce_table_all_subtypes(struct bgp *bgp, afi_t afi,
+ safi_t safi);
+
+/* Withdraw all entries of any subtype in a BGP instances RIB table from Zebra */
+extern void bgp_zebra_withdraw_table_all_subtypes(struct bgp *bgp, afi_t afi,
+ safi_t safi);
+
+extern void bgp_zebra_initiate_radv(struct bgp *bgp, struct peer *peer);
+extern void bgp_zebra_terminate_radv(struct bgp *bgp, struct peer *peer);
+
+extern void bgp_zebra_instance_register(struct bgp *bgp);
+extern void bgp_zebra_instance_deregister(struct bgp *bgp);
+
+extern void bgp_redistribute_redo(struct bgp *bgp);
+extern struct bgp_redist *bgp_redist_lookup(struct bgp *bgp, afi_t afi,
+ uint8_t type,
+ unsigned short instance);
+extern struct bgp_redist *bgp_redist_add(struct bgp *bgp, afi_t afi,
+ uint8_t type, unsigned short instance);
+extern int bgp_redistribute_set(struct bgp *bgp, afi_t afi, int type,
+ unsigned short instance, bool changed);
+extern int bgp_redistribute_resend(struct bgp *bgp, afi_t afi, int type,
+ unsigned short instance);
+extern bool bgp_redistribute_rmap_set(struct bgp_redist *red, const char *name,
+ struct route_map *route_map);
+extern bool bgp_redistribute_metric_set(struct bgp *bgp, struct bgp_redist *red,
+ afi_t afi, int type, uint32_t metric);
+extern int bgp_redistribute_unset(struct bgp *bgp, afi_t afi, int type,
+ unsigned short instance);
+extern int bgp_redistribute_unreg(struct bgp *bgp, afi_t afi, int type,
+ unsigned short instance);
+
+extern struct interface *if_lookup_by_ipv4(struct in_addr *addr,
+ vrf_id_t vrf_id);
+extern struct interface *if_lookup_by_ipv4_exact(struct in_addr *addr,
+ vrf_id_t vrf_id);
+extern struct interface *if_lookup_by_ipv6(struct in6_addr *addr,
+ ifindex_t ifindex, vrf_id_t vrf_id);
+extern struct interface *if_lookup_by_ipv6_exact(struct in6_addr *addr,
+ ifindex_t ifindex,
+ vrf_id_t vrf_id);
+extern int bgp_zebra_advertise_subnet(struct bgp *bgp, int advertise,
+ vni_t vni);
+extern int bgp_zebra_advertise_gw_macip(struct bgp *bgp, int advertise,
+ vni_t vni);
+extern int bgp_zebra_advertise_svi_macip(struct bgp *bgp, int advertise,
+ vni_t vni);
+extern int bgp_zebra_advertise_all_vni(struct bgp *bgp, int advertise);
+extern int bgp_zebra_dup_addr_detection(struct bgp *bgp);
+extern int bgp_zebra_vxlan_flood_control(struct bgp *bgp,
+ enum vxlan_flood_control flood_ctrl);
+
+extern int bgp_zebra_num_connects(void);
+
+extern bool bgp_zebra_nexthop_set(union sockunion *local,
+ union sockunion *remote,
+ struct bgp_nexthop *nexthop,
+ struct peer *peer);
+struct bgp_pbr_action;
+struct bgp_pbr_match;
+struct bgp_pbr_rule;
+struct bgp_pbr_match_entry;
+
+extern void bgp_send_pbr_rule_action(struct bgp_pbr_action *pbra,
+ struct bgp_pbr_rule *pbr,
+ bool install);
+extern void bgp_send_pbr_ipset_match(struct bgp_pbr_match *pbrim,
+ bool install);
+extern void bgp_send_pbr_ipset_entry_match(struct bgp_pbr_match_entry *pbrime,
+ bool install);
+extern void bgp_send_pbr_iptable(struct bgp_pbr_action *pba,
+ struct bgp_pbr_match *pbm,
+ bool install);
+
+extern void bgp_zebra_announce_default(struct bgp *bgp, struct nexthop *nh,
+ afi_t afi, uint32_t table_id, bool announce);
+extern int bgp_zebra_send_capabilities(struct bgp *bgp, bool disable);
+extern int bgp_zebra_update(afi_t afi, safi_t safi, vrf_id_t vrf_id, int type);
+extern int bgp_zebra_stale_timer_update(struct bgp *bgp);
+extern int bgp_zebra_srv6_manager_get_locator_chunk(const char *name);
+extern int bgp_zebra_srv6_manager_release_locator_chunk(const char *name);
+#endif /* _QUAGGA_BGP_ZEBRA_H */
diff --git a/bgpd/bgpd.c b/bgpd/bgpd.c
new file mode 100644
index 0000000..3dbd373
--- /dev/null
+++ b/bgpd/bgpd.c
@@ -0,0 +1,8393 @@
+/* BGP-4, BGP-4+ daemon program
+ * Copyright (C) 1996, 97, 98, 99, 2000 Kunihiro Ishiguro
+ *
+ * This file is part of GNU Zebra.
+ *
+ * GNU Zebra is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License as published by the
+ * Free Software Foundation; either version 2, or (at your option) any
+ * later version.
+ *
+ * GNU Zebra is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program; see the file COPYING; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
+ */
+
+#include <zebra.h>
+
+#include "prefix.h"
+#include "thread.h"
+#include "buffer.h"
+#include "stream.h"
+#include "ringbuf.h"
+#include "command.h"
+#include "sockunion.h"
+#include "sockopt.h"
+#include "network.h"
+#include "memory.h"
+#include "filter.h"
+#include "routemap.h"
+#include "log.h"
+#include "plist.h"
+#include "linklist.h"
+#include "workqueue.h"
+#include "queue.h"
+#include "zclient.h"
+#include "bfd.h"
+#include "hash.h"
+#include "jhash.h"
+#include "table.h"
+#include "lib/json.h"
+#include "lib/sockopt.h"
+#include "frr_pthread.h"
+#include "bitfield.h"
+
+#include "bgpd/bgpd.h"
+#include "bgpd/bgp_table.h"
+#include "bgpd/bgp_aspath.h"
+#include "bgpd/bgp_route.h"
+#include "bgpd/bgp_dump.h"
+#include "bgpd/bgp_debug.h"
+#include "bgpd/bgp_errors.h"
+#include "bgpd/bgp_community.h"
+#include "bgpd/bgp_community_alias.h"
+#include "bgpd/bgp_conditional_adv.h"
+#include "bgpd/bgp_attr.h"
+#include "bgpd/bgp_regex.h"
+#include "bgpd/bgp_clist.h"
+#include "bgpd/bgp_fsm.h"
+#include "bgpd/bgp_packet.h"
+#include "bgpd/bgp_zebra.h"
+#include "bgpd/bgp_open.h"
+#include "bgpd/bgp_filter.h"
+#include "bgpd/bgp_nexthop.h"
+#include "bgpd/bgp_damp.h"
+#include "bgpd/bgp_mplsvpn.h"
+#ifdef ENABLE_BGP_VNC
+#include "bgpd/rfapi/bgp_rfapi_cfg.h"
+#include "bgpd/rfapi/rfapi_backend.h"
+#endif
+#include "bgpd/bgp_evpn.h"
+#include "bgpd/bgp_advertise.h"
+#include "bgpd/bgp_network.h"
+#include "bgpd/bgp_vty.h"
+#include "bgpd/bgp_mpath.h"
+#include "bgpd/bgp_nht.h"
+#include "bgpd/bgp_updgrp.h"
+#include "bgpd/bgp_bfd.h"
+#include "bgpd/bgp_memory.h"
+#include "bgpd/bgp_evpn_vty.h"
+#include "bgpd/bgp_keepalives.h"
+#include "bgpd/bgp_io.h"
+#include "bgpd/bgp_ecommunity.h"
+#include "bgpd/bgp_flowspec.h"
+#include "bgpd/bgp_labelpool.h"
+#include "bgpd/bgp_pbr.h"
+#include "bgpd/bgp_addpath.h"
+#include "bgpd/bgp_evpn_private.h"
+#include "bgpd/bgp_evpn_mh.h"
+#include "bgpd/bgp_mac.h"
+
+DEFINE_MTYPE_STATIC(BGPD, PEER_TX_SHUTDOWN_MSG, "Peer shutdown message (TX)");
+DEFINE_MTYPE_STATIC(BGPD, BGP_EVPN_INFO, "BGP EVPN instance information");
+DEFINE_QOBJ_TYPE(bgp_master);
+DEFINE_QOBJ_TYPE(bgp);
+DEFINE_QOBJ_TYPE(peer);
+DEFINE_HOOK(bgp_inst_delete, (struct bgp *bgp), (bgp));
+
+/* BGP process wide configuration. */
+static struct bgp_master bgp_master;
+
+/* BGP process wide configuration pointer to export. */
+struct bgp_master *bm;
+
+/* BGP community-list. */
+struct community_list_handler *bgp_clist;
+
+unsigned int multipath_num = MULTIPATH_NUM;
+
+/* Number of bgp instances configured for suppress fib config */
+unsigned int bgp_suppress_fib_count;
+
+static void bgp_if_finish(struct bgp *bgp);
+static void peer_drop_dynamic_neighbor(struct peer *peer);
+
+extern struct zclient *zclient;
+
+/* handle main socket creation or deletion */
+static int bgp_check_main_socket(bool create, struct bgp *bgp)
+{
+ static int bgp_server_main_created;
+ struct listnode *node;
+ char *address;
+
+ if (create) {
+ if (bgp_server_main_created)
+ return 0;
+ if (list_isempty(bm->addresses)) {
+ if (bgp_socket(bgp, bm->port, NULL) < 0)
+ return BGP_ERR_INVALID_VALUE;
+ } else {
+ for (ALL_LIST_ELEMENTS_RO(bm->addresses, node, address))
+ if (bgp_socket(bgp, bm->port, address) < 0)
+ return BGP_ERR_INVALID_VALUE;
+ }
+ bgp_server_main_created = 1;
+ return 0;
+ }
+ if (!bgp_server_main_created)
+ return 0;
+ bgp_close();
+ bgp_server_main_created = 0;
+ return 0;
+}
+
+void bgp_session_reset(struct peer *peer)
+{
+ if (peer->doppelganger && (peer->doppelganger->status != Deleted)
+ && !(CHECK_FLAG(peer->doppelganger->flags, PEER_FLAG_CONFIG_NODE)))
+ peer_delete(peer->doppelganger);
+
+ BGP_EVENT_ADD(peer, BGP_Stop);
+}
+
+/*
+ * During session reset, we may delete the doppelganger peer, which would
+ * be the next node to the current node. If the session reset was invoked
+ * during walk of peer list, we would end up accessing the freed next
+ * node. This function moves the next node along.
+ */
+static void bgp_session_reset_safe(struct peer *peer, struct listnode **nnode)
+{
+ struct listnode *n;
+ struct peer *npeer;
+
+ n = (nnode) ? *nnode : NULL;
+ npeer = (n) ? listgetdata(n) : NULL;
+
+ if (peer->doppelganger && (peer->doppelganger->status != Deleted)
+ && !(CHECK_FLAG(peer->doppelganger->flags,
+ PEER_FLAG_CONFIG_NODE))) {
+ if (peer->doppelganger == npeer)
+ /* nnode and *nnode are confirmed to be non-NULL here */
+ *nnode = (*nnode)->next;
+ peer_delete(peer->doppelganger);
+ }
+
+ BGP_EVENT_ADD(peer, BGP_Stop);
+}
+
+/* BGP global flag manipulation. */
+int bgp_option_set(int flag)
+{
+ switch (flag) {
+ case BGP_OPT_NO_FIB:
+ case BGP_OPT_NO_LISTEN:
+ case BGP_OPT_NO_ZEBRA:
+ SET_FLAG(bm->options, flag);
+ break;
+ default:
+ return BGP_ERR_INVALID_FLAG;
+ }
+ return 0;
+}
+
+int bgp_option_unset(int flag)
+{
+ switch (flag) {
+ /* Fall through. */
+ case BGP_OPT_NO_ZEBRA:
+ case BGP_OPT_NO_FIB:
+ UNSET_FLAG(bm->options, flag);
+ break;
+ default:
+ return BGP_ERR_INVALID_FLAG;
+ }
+ return 0;
+}
+
+int bgp_option_check(int flag)
+{
+ return CHECK_FLAG(bm->options, flag);
+}
+
+/* set the bgp no-rib option during runtime and remove installed routes */
+void bgp_option_norib_set_runtime(void)
+{
+ struct bgp *bgp;
+ struct listnode *node;
+ afi_t afi;
+ safi_t safi;
+
+ if (bgp_option_check(BGP_OPT_NO_FIB))
+ return;
+
+ bgp_option_set(BGP_OPT_NO_FIB);
+
+ zlog_info("Disabled BGP route installation to RIB (Zebra)");
+
+ for (ALL_LIST_ELEMENTS_RO(bm->bgp, node, bgp)) {
+ FOREACH_AFI_SAFI (afi, safi) {
+ /*
+ * Stop a crash, more work is needed
+ * here to properly add/remove these types of
+ * routes from zebra.
+ */
+ if (!bgp_fibupd_safi(safi))
+ continue;
+
+ bgp_zebra_withdraw_table_all_subtypes(bgp, afi, safi);
+ }
+ }
+
+ zlog_info("All routes have been withdrawn from RIB (Zebra)");
+}
+
+/* unset the bgp no-rib option during runtime and announce routes to Zebra */
+void bgp_option_norib_unset_runtime(void)
+{
+ struct bgp *bgp;
+ struct listnode *node;
+ afi_t afi;
+ safi_t safi;
+
+ if (!bgp_option_check(BGP_OPT_NO_FIB))
+ return;
+
+ bgp_option_unset(BGP_OPT_NO_FIB);
+
+ zlog_info("Enabled BGP route installation to RIB (Zebra)");
+
+ for (ALL_LIST_ELEMENTS_RO(bm->bgp, node, bgp)) {
+ FOREACH_AFI_SAFI (afi, safi) {
+ /*
+ * Stop a crash, more work is needed
+ * here to properly add/remove these types
+ * of routes from zebra
+ */
+ if (!bgp_fibupd_safi(safi))
+ continue;
+
+ bgp_zebra_announce_table_all_subtypes(bgp, afi, safi);
+ }
+ }
+
+ zlog_info("All routes have been installed in RIB (Zebra)");
+}
+
+/* Internal function to set BGP structure configureation flag. */
+static void bgp_config_set(struct bgp *bgp, int config)
+{
+ SET_FLAG(bgp->config, config);
+}
+
+static void bgp_config_unset(struct bgp *bgp, int config)
+{
+ UNSET_FLAG(bgp->config, config);
+}
+
+static int bgp_config_check(struct bgp *bgp, int config)
+{
+ return CHECK_FLAG(bgp->config, config);
+}
+
+/* Set BGP router identifier; distinguish between explicit config and other
+ * cases.
+ */
+static int bgp_router_id_set(struct bgp *bgp, const struct in_addr *id,
+ bool is_config)
+{
+ struct peer *peer;
+ struct listnode *node, *nnode;
+
+ if (IPV4_ADDR_SAME(&bgp->router_id, id))
+ return 0;
+
+ /* EVPN uses router id in RD, withdraw them */
+ if (is_evpn_enabled())
+ bgp_evpn_handle_router_id_update(bgp, true);
+
+ vpn_handle_router_id_update(bgp, true, is_config);
+
+ IPV4_ADDR_COPY(&bgp->router_id, id);
+
+ /* Set all peer's local identifier with this value. */
+ for (ALL_LIST_ELEMENTS(bgp->peer, node, nnode, peer)) {
+ IPV4_ADDR_COPY(&peer->local_id, id);
+
+ if (BGP_IS_VALID_STATE_FOR_NOTIF(peer->status)) {
+ peer->last_reset = PEER_DOWN_RID_CHANGE;
+ bgp_notify_send(peer, BGP_NOTIFY_CEASE,
+ BGP_NOTIFY_CEASE_CONFIG_CHANGE);
+ }
+ }
+
+ /* EVPN uses router id in RD, update them */
+ if (is_evpn_enabled())
+ bgp_evpn_handle_router_id_update(bgp, false);
+
+ vpn_handle_router_id_update(bgp, false, is_config);
+
+ return 0;
+}
+
+void bgp_router_id_zebra_bump(vrf_id_t vrf_id, const struct prefix *router_id)
+{
+ struct listnode *node, *nnode;
+ struct bgp *bgp;
+ struct in_addr *addr = NULL;
+
+ if (router_id != NULL)
+ addr = (struct in_addr *)&(router_id->u.prefix4);
+
+ if (vrf_id == VRF_DEFAULT) {
+ /* Router-id change for default VRF has to also update all
+ * views. */
+ for (ALL_LIST_ELEMENTS(bm->bgp, node, nnode, bgp)) {
+ if (bgp->inst_type == BGP_INSTANCE_TYPE_VRF)
+ continue;
+
+ if (addr)
+ bgp->router_id_zebra = *addr;
+ else
+ addr = &bgp->router_id_zebra;
+
+ if (!bgp->router_id_static.s_addr) {
+ /* Router ID is updated if there are no active
+ * peer sessions
+ */
+ if (bgp->established_peers == 0) {
+ if (BGP_DEBUG(zebra, ZEBRA))
+ zlog_debug(
+ "RID change : vrf %s(%u), RTR ID %pI4",
+ bgp->name_pretty,
+ bgp->vrf_id, addr);
+ /*
+ * if old router-id was 0x0, set flag
+ * to use this new value
+ */
+ bgp_router_id_set(bgp, addr,
+ (bgp->router_id.s_addr
+ == INADDR_ANY)
+ ? true
+ : false);
+ }
+ }
+ }
+ } else {
+ bgp = bgp_lookup_by_vrf_id(vrf_id);
+ if (bgp) {
+ if (addr)
+ bgp->router_id_zebra = *addr;
+ else
+ addr = &bgp->router_id_zebra;
+
+ if (!bgp->router_id_static.s_addr) {
+ /* Router ID is updated if there are no active
+ * peer sessions
+ */
+ if (bgp->established_peers == 0) {
+ if (BGP_DEBUG(zebra, ZEBRA))
+ zlog_debug(
+ "RID change : vrf %s(%u), RTR ID %pI4",
+ bgp->name_pretty,
+ bgp->vrf_id, addr);
+ /*
+ * if old router-id was 0x0, set flag
+ * to use this new value
+ */
+ bgp_router_id_set(bgp, addr,
+ (bgp->router_id.s_addr
+ == INADDR_ANY)
+ ? true
+ : false);
+ }
+ }
+
+ }
+ }
+}
+
+void bgp_router_id_static_set(struct bgp *bgp, struct in_addr id)
+{
+ bgp->router_id_static = id;
+ bgp_router_id_set(bgp,
+ id.s_addr != INADDR_ANY ? &id : &bgp->router_id_zebra,
+ true /* is config */);
+}
+
+void bm_wait_for_fib_set(bool set)
+{
+ bool send_msg = false;
+
+ if (bm->wait_for_fib == set)
+ return;
+
+ bm->wait_for_fib = set;
+ if (set) {
+ if (bgp_suppress_fib_count == 0)
+ send_msg = true;
+ bgp_suppress_fib_count++;
+ } else {
+ bgp_suppress_fib_count--;
+ if (bgp_suppress_fib_count == 0)
+ send_msg = true;
+ }
+
+ if (send_msg && zclient)
+ zebra_route_notify_send(ZEBRA_ROUTE_NOTIFY_REQUEST,
+ zclient, set);
+}
+
+/* Set the suppress fib pending for the bgp configuration */
+void bgp_suppress_fib_pending_set(struct bgp *bgp, bool set)
+{
+ bool send_msg = false;
+
+ if (bgp->inst_type == BGP_INSTANCE_TYPE_VIEW)
+ return;
+
+ if (set) {
+ SET_FLAG(bgp->flags, BGP_FLAG_SUPPRESS_FIB_PENDING);
+ /* Send msg to zebra for the first instance of bgp enabled
+ * with suppress fib
+ */
+ if (bgp_suppress_fib_count == 0)
+ send_msg = true;
+ bgp_suppress_fib_count++;
+ } else {
+ UNSET_FLAG(bgp->flags, BGP_FLAG_SUPPRESS_FIB_PENDING);
+ bgp_suppress_fib_count--;
+
+ /* Send msg to zebra if there are no instances enabled
+ * with suppress fib
+ */
+ if (bgp_suppress_fib_count == 0)
+ send_msg = true;
+ }
+ /* Send route notify request to RIB */
+ if (send_msg) {
+ if (BGP_DEBUG(zebra, ZEBRA))
+ zlog_debug("Sending ZEBRA_ROUTE_NOTIFY_REQUEST");
+
+ if (zclient)
+ zebra_route_notify_send(ZEBRA_ROUTE_NOTIFY_REQUEST,
+ zclient, set);
+ }
+}
+
+/* BGP's cluster-id control. */
+void bgp_cluster_id_set(struct bgp *bgp, struct in_addr *cluster_id)
+{
+ struct peer *peer;
+ struct listnode *node, *nnode;
+
+ if (bgp_config_check(bgp, BGP_CONFIG_CLUSTER_ID)
+ && IPV4_ADDR_SAME(&bgp->cluster_id, cluster_id))
+ return;
+
+ IPV4_ADDR_COPY(&bgp->cluster_id, cluster_id);
+ bgp_config_set(bgp, BGP_CONFIG_CLUSTER_ID);
+
+ /* Clear all IBGP peer. */
+ for (ALL_LIST_ELEMENTS(bgp->peer, node, nnode, peer)) {
+ if (peer->sort != BGP_PEER_IBGP)
+ continue;
+
+ if (BGP_IS_VALID_STATE_FOR_NOTIF(peer->status)) {
+ peer->last_reset = PEER_DOWN_CLID_CHANGE;
+ bgp_notify_send(peer, BGP_NOTIFY_CEASE,
+ BGP_NOTIFY_CEASE_CONFIG_CHANGE);
+ }
+ }
+}
+
+void bgp_cluster_id_unset(struct bgp *bgp)
+{
+ struct peer *peer;
+ struct listnode *node, *nnode;
+
+ if (!bgp_config_check(bgp, BGP_CONFIG_CLUSTER_ID))
+ return;
+
+ bgp->cluster_id.s_addr = 0;
+ bgp_config_unset(bgp, BGP_CONFIG_CLUSTER_ID);
+
+ /* Clear all IBGP peer. */
+ for (ALL_LIST_ELEMENTS(bgp->peer, node, nnode, peer)) {
+ if (peer->sort != BGP_PEER_IBGP)
+ continue;
+
+ if (BGP_IS_VALID_STATE_FOR_NOTIF(peer->status)) {
+ peer->last_reset = PEER_DOWN_CLID_CHANGE;
+ bgp_notify_send(peer, BGP_NOTIFY_CEASE,
+ BGP_NOTIFY_CEASE_CONFIG_CHANGE);
+ }
+ }
+}
+
+/* BGP timer configuration. */
+void bgp_timers_set(struct bgp *bgp, uint32_t keepalive, uint32_t holdtime,
+ uint32_t connect_retry, uint32_t delayopen)
+{
+ bgp->default_keepalive =
+ (keepalive < holdtime / 3 ? keepalive : holdtime / 3);
+ bgp->default_holdtime = holdtime;
+ bgp->default_connect_retry = connect_retry;
+ bgp->default_delayopen = delayopen;
+}
+
+/* mostly for completeness - CLI uses its own defaults */
+void bgp_timers_unset(struct bgp *bgp)
+{
+ bgp->default_keepalive = BGP_DEFAULT_KEEPALIVE;
+ bgp->default_holdtime = BGP_DEFAULT_HOLDTIME;
+ bgp->default_connect_retry = BGP_DEFAULT_CONNECT_RETRY;
+ bgp->default_delayopen = BGP_DEFAULT_DELAYOPEN;
+}
+
+/* BGP confederation configuration. */
+void bgp_confederation_id_set(struct bgp *bgp, as_t as)
+{
+ struct peer *peer;
+ struct listnode *node, *nnode;
+ int already_confed;
+
+ if (as == 0)
+ return;
+
+ /* Remember - were we doing confederation before? */
+ already_confed = bgp_config_check(bgp, BGP_CONFIG_CONFEDERATION);
+ bgp->confed_id = as;
+ bgp_config_set(bgp, BGP_CONFIG_CONFEDERATION);
+
+ /* If we were doing confederation already, this is just an external
+ AS change. Just Reset EBGP sessions, not CONFED sessions. If we
+ were not doing confederation before, reset all EBGP sessions. */
+ for (ALL_LIST_ELEMENTS(bgp->peer, node, nnode, peer)) {
+ enum bgp_peer_sort ptype = peer_sort(peer);
+
+ /* We're looking for peers who's AS is not local or part of our
+ confederation. */
+ if (already_confed) {
+ if (ptype == BGP_PEER_EBGP) {
+ peer->local_as = as;
+ if (BGP_IS_VALID_STATE_FOR_NOTIF(
+ peer->status)) {
+ peer->last_reset =
+ PEER_DOWN_CONFED_ID_CHANGE;
+ bgp_notify_send(
+ peer, BGP_NOTIFY_CEASE,
+ BGP_NOTIFY_CEASE_CONFIG_CHANGE);
+ } else
+ bgp_session_reset_safe(peer, &nnode);
+ }
+ } else {
+ /* Not doign confederation before, so reset every
+ non-local
+ session */
+ if (ptype != BGP_PEER_IBGP) {
+ /* Reset the local_as to be our EBGP one */
+ if (ptype == BGP_PEER_EBGP)
+ peer->local_as = as;
+ if (BGP_IS_VALID_STATE_FOR_NOTIF(
+ peer->status)) {
+ peer->last_reset =
+ PEER_DOWN_CONFED_ID_CHANGE;
+ bgp_notify_send(
+ peer, BGP_NOTIFY_CEASE,
+ BGP_NOTIFY_CEASE_CONFIG_CHANGE);
+ } else
+ bgp_session_reset_safe(peer, &nnode);
+ }
+ }
+ }
+ return;
+}
+
+void bgp_confederation_id_unset(struct bgp *bgp)
+{
+ struct peer *peer;
+ struct listnode *node, *nnode;
+
+ bgp->confed_id = 0;
+ bgp_config_unset(bgp, BGP_CONFIG_CONFEDERATION);
+
+ for (ALL_LIST_ELEMENTS(bgp->peer, node, nnode, peer)) {
+ /* We're looking for peers who's AS is not local */
+ if (peer_sort(peer) != BGP_PEER_IBGP) {
+ peer->local_as = bgp->as;
+ if (BGP_IS_VALID_STATE_FOR_NOTIF(peer->status)) {
+ peer->last_reset = PEER_DOWN_CONFED_ID_CHANGE;
+ bgp_notify_send(peer, BGP_NOTIFY_CEASE,
+ BGP_NOTIFY_CEASE_CONFIG_CHANGE);
+ }
+
+ else
+ bgp_session_reset_safe(peer, &nnode);
+ }
+ }
+}
+
+/* Is an AS part of the confed or not? */
+bool bgp_confederation_peers_check(struct bgp *bgp, as_t as)
+{
+ int i;
+
+ if (!bgp)
+ return false;
+
+ for (i = 0; i < bgp->confed_peers_cnt; i++)
+ if (bgp->confed_peers[i] == as)
+ return true;
+
+ return false;
+}
+
+/* Add an AS to the confederation set. */
+void bgp_confederation_peers_add(struct bgp *bgp, as_t as)
+{
+ struct peer *peer;
+ struct listnode *node, *nnode;
+
+ if (bgp->as == as)
+ return;
+
+ if (bgp_confederation_peers_check(bgp, as))
+ return;
+
+ bgp->confed_peers =
+ XREALLOC(MTYPE_BGP_CONFED_LIST, bgp->confed_peers,
+ (bgp->confed_peers_cnt + 1) * sizeof(as_t));
+
+ bgp->confed_peers[bgp->confed_peers_cnt] = as;
+ bgp->confed_peers_cnt++;
+
+ if (bgp_config_check(bgp, BGP_CONFIG_CONFEDERATION)) {
+ for (ALL_LIST_ELEMENTS(bgp->peer, node, nnode, peer)) {
+ if (peer->as == as) {
+ (void)peer_sort(peer);
+ peer->local_as = bgp->as;
+ if (BGP_IS_VALID_STATE_FOR_NOTIF(
+ peer->status)) {
+ peer->last_reset =
+ PEER_DOWN_CONFED_PEER_CHANGE;
+ bgp_notify_send(
+ peer, BGP_NOTIFY_CEASE,
+ BGP_NOTIFY_CEASE_CONFIG_CHANGE);
+ } else
+ bgp_session_reset_safe(peer, &nnode);
+ }
+ }
+ }
+}
+
+/* Delete an AS from the confederation set. */
+void bgp_confederation_peers_remove(struct bgp *bgp, as_t as)
+{
+ int i;
+ int j;
+ struct peer *peer;
+ struct listnode *node, *nnode;
+
+ if (!bgp)
+ return;
+
+ if (!bgp_confederation_peers_check(bgp, as))
+ return;
+
+ for (i = 0; i < bgp->confed_peers_cnt; i++)
+ if (bgp->confed_peers[i] == as)
+ for (j = i + 1; j < bgp->confed_peers_cnt; j++)
+ bgp->confed_peers[j - 1] = bgp->confed_peers[j];
+
+ bgp->confed_peers_cnt--;
+
+ if (bgp->confed_peers_cnt == 0) {
+ if (bgp->confed_peers)
+ XFREE(MTYPE_BGP_CONFED_LIST, bgp->confed_peers);
+ bgp->confed_peers = NULL;
+ } else
+ bgp->confed_peers =
+ XREALLOC(MTYPE_BGP_CONFED_LIST, bgp->confed_peers,
+ bgp->confed_peers_cnt * sizeof(as_t));
+
+ /* Now reset any peer who's remote AS has just been removed from the
+ CONFED */
+ if (bgp_config_check(bgp, BGP_CONFIG_CONFEDERATION)) {
+ for (ALL_LIST_ELEMENTS(bgp->peer, node, nnode, peer)) {
+ if (peer->as == as) {
+ (void)peer_sort(peer);
+ peer->local_as = bgp->confed_id;
+ if (BGP_IS_VALID_STATE_FOR_NOTIF(
+ peer->status)) {
+ peer->last_reset =
+ PEER_DOWN_CONFED_PEER_CHANGE;
+ bgp_notify_send(
+ peer, BGP_NOTIFY_CEASE,
+ BGP_NOTIFY_CEASE_CONFIG_CHANGE);
+ } else
+ bgp_session_reset_safe(peer, &nnode);
+ }
+ }
+ }
+}
+
+/* Local preference configuration. */
+void bgp_default_local_preference_set(struct bgp *bgp, uint32_t local_pref)
+{
+ if (!bgp)
+ return;
+
+ bgp->default_local_pref = local_pref;
+}
+
+void bgp_default_local_preference_unset(struct bgp *bgp)
+{
+ if (!bgp)
+ return;
+
+ bgp->default_local_pref = BGP_DEFAULT_LOCAL_PREF;
+}
+
+/* Local preference configuration. */
+void bgp_default_subgroup_pkt_queue_max_set(struct bgp *bgp,
+ uint32_t queue_size)
+{
+ if (!bgp)
+ return;
+
+ bgp->default_subgroup_pkt_queue_max = queue_size;
+}
+
+void bgp_default_subgroup_pkt_queue_max_unset(struct bgp *bgp)
+{
+ if (!bgp)
+ return;
+ bgp->default_subgroup_pkt_queue_max =
+ BGP_DEFAULT_SUBGROUP_PKT_QUEUE_MAX;
+}
+
+/* Listen limit configuration. */
+void bgp_listen_limit_set(struct bgp *bgp, int listen_limit)
+{
+ if (!bgp)
+ return;
+
+ bgp->dynamic_neighbors_limit = listen_limit;
+}
+
+void bgp_listen_limit_unset(struct bgp *bgp)
+{
+ if (!bgp)
+ return;
+
+ bgp->dynamic_neighbors_limit = BGP_DYNAMIC_NEIGHBORS_LIMIT_DEFAULT;
+}
+
+int bgp_map_afi_safi_iana2int(iana_afi_t pkt_afi, iana_safi_t pkt_safi,
+ afi_t *afi, safi_t *safi)
+{
+ /* Map from IANA values to internal values, return error if
+ * values are unrecognized.
+ */
+ *afi = afi_iana2int(pkt_afi);
+ *safi = safi_iana2int(pkt_safi);
+ if (*afi == AFI_MAX || *safi == SAFI_MAX)
+ return -1;
+
+ return 0;
+}
+
+int bgp_map_afi_safi_int2iana(afi_t afi, safi_t safi, iana_afi_t *pkt_afi,
+ iana_safi_t *pkt_safi)
+{
+ /* Map from internal values to IANA values, return error if
+ * internal values are bad (unexpected).
+ */
+ if (afi == AFI_MAX || safi == SAFI_MAX)
+ return -1;
+ *pkt_afi = afi_int2iana(afi);
+ *pkt_safi = safi_int2iana(safi);
+ return 0;
+}
+
+struct peer_af *peer_af_create(struct peer *peer, afi_t afi, safi_t safi)
+{
+ struct peer_af *af;
+ int afid;
+ struct bgp *bgp;
+
+ if (!peer)
+ return NULL;
+
+ afid = afindex(afi, safi);
+ if (afid >= BGP_AF_MAX)
+ return NULL;
+
+ bgp = peer->bgp;
+ assert(peer->peer_af_array[afid] == NULL);
+
+ /* Allocate new peer af */
+ af = XCALLOC(MTYPE_BGP_PEER_AF, sizeof(struct peer_af));
+
+ peer->peer_af_array[afid] = af;
+ af->afi = afi;
+ af->safi = safi;
+ af->afid = afid;
+ af->peer = peer;
+ bgp->af_peer_count[afi][safi]++;
+
+ return af;
+}
+
+struct peer_af *peer_af_find(struct peer *peer, afi_t afi, safi_t safi)
+{
+ int afid;
+
+ if (!peer)
+ return NULL;
+
+ afid = afindex(afi, safi);
+ if (afid >= BGP_AF_MAX)
+ return NULL;
+
+ return peer->peer_af_array[afid];
+}
+
+int peer_af_delete(struct peer *peer, afi_t afi, safi_t safi)
+{
+ struct peer_af *af;
+ int afid;
+ struct bgp *bgp;
+
+ if (!peer)
+ return -1;
+
+ afid = afindex(afi, safi);
+ if (afid >= BGP_AF_MAX)
+ return -1;
+
+ af = peer->peer_af_array[afid];
+ if (!af)
+ return -1;
+
+ bgp = peer->bgp;
+ bgp_soft_reconfig_table_task_cancel(bgp, bgp->rib[afi][safi], peer);
+
+ bgp_stop_announce_route_timer(af);
+
+ if (PAF_SUBGRP(af)) {
+ if (BGP_DEBUG(update_groups, UPDATE_GROUPS))
+ zlog_debug("u%" PRIu64 ":s%" PRIu64 " remove peer %s",
+ af->subgroup->update_group->id,
+ af->subgroup->id, peer->host);
+ }
+
+
+ update_subgroup_remove_peer(af->subgroup, af);
+
+ if (bgp->af_peer_count[afi][safi])
+ bgp->af_peer_count[afi][safi]--;
+
+ peer->peer_af_array[afid] = NULL;
+ XFREE(MTYPE_BGP_PEER_AF, af);
+ return 0;
+}
+
+/* Peer comparison function for sorting. */
+int peer_cmp(struct peer *p1, struct peer *p2)
+{
+ if (p1->group && !p2->group)
+ return -1;
+
+ if (!p1->group && p2->group)
+ return 1;
+
+ if (p1->group == p2->group) {
+ if (p1->conf_if && !p2->conf_if)
+ return -1;
+
+ if (!p1->conf_if && p2->conf_if)
+ return 1;
+
+ if (p1->conf_if && p2->conf_if)
+ return if_cmp_name_func(p1->conf_if, p2->conf_if);
+ } else
+ return strcmp(p1->group->name, p2->group->name);
+
+ return sockunion_cmp(&p1->su, &p2->su);
+}
+
+static unsigned int peer_hash_key_make(const void *p)
+{
+ const struct peer *peer = p;
+ return sockunion_hash(&peer->su);
+}
+
+static bool peer_hash_same(const void *p1, const void *p2)
+{
+ const struct peer *peer1 = p1;
+ const struct peer *peer2 = p2;
+ return (sockunion_same(&peer1->su, &peer2->su)
+ && CHECK_FLAG(peer1->flags, PEER_FLAG_CONFIG_NODE)
+ == CHECK_FLAG(peer2->flags, PEER_FLAG_CONFIG_NODE));
+}
+
+void peer_flag_inherit(struct peer *peer, uint64_t flag)
+{
+ bool group_val;
+
+ /* Skip if peer is not a peer-group member. */
+ if (!peer_group_active(peer))
+ return;
+
+ /* Unset override flag to signal inheritance from peer-group. */
+ UNSET_FLAG(peer->flags_override, flag);
+
+ /*
+ * Inherit flag state from peer-group. If the flag of the peer-group is
+ * not being inverted, the peer must inherit the inverse of the current
+ * peer-group flag state.
+ */
+ group_val = CHECK_FLAG(peer->group->conf->flags, flag);
+ if (!CHECK_FLAG(peer->group->conf->flags_invert, flag)
+ && CHECK_FLAG(peer->flags_invert, flag))
+ COND_FLAG(peer->flags, flag, !group_val);
+ else
+ COND_FLAG(peer->flags, flag, group_val);
+}
+
+int peer_af_flag_check(struct peer *peer, afi_t afi, safi_t safi, uint32_t flag)
+{
+ return CHECK_FLAG(peer->af_flags[afi][safi], flag);
+}
+
+void peer_af_flag_inherit(struct peer *peer, afi_t afi, safi_t safi,
+ uint64_t flag)
+{
+ bool group_val;
+
+ /* Skip if peer is not a peer-group member. */
+ if (!peer_group_active(peer))
+ return;
+
+ /* Unset override flag to signal inheritance from peer-group. */
+ UNSET_FLAG(peer->af_flags_override[afi][safi], flag);
+
+ /*
+ * Inherit flag state from peer-group. If the flag of the peer-group is
+ * not being inverted, the peer must inherit the inverse of the current
+ * peer-group flag state.
+ */
+ group_val = CHECK_FLAG(peer->group->conf->af_flags[afi][safi], flag);
+ if (!CHECK_FLAG(peer->group->conf->af_flags_invert[afi][safi], flag)
+ && CHECK_FLAG(peer->af_flags_invert[afi][safi], flag))
+ COND_FLAG(peer->af_flags[afi][safi], flag, !group_val);
+ else
+ COND_FLAG(peer->af_flags[afi][safi], flag, group_val);
+}
+
+/* Check peer's AS number and determines if this peer is IBGP or EBGP */
+static inline enum bgp_peer_sort peer_calc_sort(struct peer *peer)
+{
+ struct bgp *bgp;
+
+ bgp = peer->bgp;
+
+ /* Peer-group */
+ if (CHECK_FLAG(peer->sflags, PEER_STATUS_GROUP)) {
+ if (peer->as_type == AS_INTERNAL)
+ return BGP_PEER_IBGP;
+
+ else if (peer->as_type == AS_EXTERNAL)
+ return BGP_PEER_EBGP;
+
+ else if (peer->as_type == AS_SPECIFIED && peer->as) {
+ assert(bgp);
+ return (bgp->as == peer->as ? BGP_PEER_IBGP
+ : BGP_PEER_EBGP);
+ }
+
+ else {
+ struct peer *peer1;
+
+ assert(peer->group);
+ peer1 = listnode_head(peer->group->peer);
+
+ if (peer1)
+ return peer1->sort;
+ }
+ return BGP_PEER_INTERNAL;
+ }
+
+ /* Normal peer */
+ if (bgp && CHECK_FLAG(bgp->config, BGP_CONFIG_CONFEDERATION)) {
+ if (peer->local_as == 0)
+ return BGP_PEER_INTERNAL;
+
+ if (peer->local_as == peer->as) {
+ if (bgp->as == bgp->confed_id) {
+ if (peer->local_as == bgp->as)
+ return BGP_PEER_IBGP;
+ else
+ return BGP_PEER_EBGP;
+ } else {
+ if (peer->local_as == bgp->confed_id)
+ return BGP_PEER_EBGP;
+ else
+ return BGP_PEER_IBGP;
+ }
+ }
+
+ if (bgp_confederation_peers_check(bgp, peer->as))
+ return BGP_PEER_CONFED;
+
+ return BGP_PEER_EBGP;
+ } else {
+ if (peer->as_type == AS_UNSPECIFIED) {
+ /* check if in peer-group with AS information */
+ if (peer->group
+ && (peer->group->conf->as_type != AS_UNSPECIFIED)) {
+ if (peer->group->conf->as_type
+ == AS_SPECIFIED) {
+ if (peer->local_as
+ == peer->group->conf->as)
+ return BGP_PEER_IBGP;
+ else
+ return BGP_PEER_EBGP;
+ } else if (peer->group->conf->as_type
+ == AS_INTERNAL)
+ return BGP_PEER_IBGP;
+ else
+ return BGP_PEER_EBGP;
+ }
+ /* no AS information anywhere, let caller know */
+ return BGP_PEER_UNSPECIFIED;
+ } else if (peer->as_type != AS_SPECIFIED)
+ return (peer->as_type == AS_INTERNAL ? BGP_PEER_IBGP
+ : BGP_PEER_EBGP);
+
+ return (peer->local_as == 0
+ ? BGP_PEER_INTERNAL
+ : peer->local_as == peer->as ? BGP_PEER_IBGP
+ : BGP_PEER_EBGP);
+ }
+}
+
+/* Calculate and cache the peer "sort" */
+enum bgp_peer_sort peer_sort(struct peer *peer)
+{
+ peer->sort = peer_calc_sort(peer);
+ return peer->sort;
+}
+
+enum bgp_peer_sort peer_sort_lookup(struct peer *peer)
+{
+ return peer->sort;
+}
+
+static void peer_free(struct peer *peer)
+{
+ afi_t afi;
+ safi_t safi;
+
+ assert(peer->status == Deleted);
+
+ QOBJ_UNREG(peer);
+
+ /* this /ought/ to have been done already through bgp_stop earlier,
+ * but just to be sure..
+ */
+ bgp_timer_set(peer);
+ bgp_reads_off(peer);
+ bgp_writes_off(peer);
+ assert(!peer->t_write);
+ assert(!peer->t_read);
+ BGP_EVENT_FLUSH(peer);
+
+ pthread_mutex_destroy(&peer->io_mtx);
+
+ /* Free connected nexthop, if present */
+ if (CHECK_FLAG(peer->flags, PEER_FLAG_CONFIG_NODE)
+ && !peer_dynamic_neighbor(peer))
+ bgp_delete_connected_nexthop(family2afi(peer->su.sa.sa_family),
+ peer);
+
+ FOREACH_AFI_SAFI (afi, safi) {
+ if (peer->filter[afi][safi].advmap.aname)
+ XFREE(MTYPE_BGP_FILTER_NAME,
+ peer->filter[afi][safi].advmap.aname);
+ if (peer->filter[afi][safi].advmap.cname)
+ XFREE(MTYPE_BGP_FILTER_NAME,
+ peer->filter[afi][safi].advmap.cname);
+ }
+
+ XFREE(MTYPE_PEER_TX_SHUTDOWN_MSG, peer->tx_shutdown_message);
+
+ XFREE(MTYPE_PEER_DESC, peer->desc);
+ XFREE(MTYPE_BGP_PEER_HOST, peer->host);
+ XFREE(MTYPE_BGP_PEER_HOST, peer->domainname);
+ XFREE(MTYPE_BGP_PEER_IFNAME, peer->ifname);
+
+ /* Update source configuration. */
+ if (peer->update_source) {
+ sockunion_free(peer->update_source);
+ peer->update_source = NULL;
+ }
+
+ XFREE(MTYPE_PEER_UPDATE_SOURCE, peer->update_if);
+
+ XFREE(MTYPE_BGP_NOTIFICATION, peer->notify.data);
+ memset(&peer->notify, 0, sizeof(struct bgp_notify));
+
+ if (peer->clear_node_queue)
+ work_queue_free_and_null(&peer->clear_node_queue);
+
+ bgp_sync_delete(peer);
+
+ XFREE(MTYPE_PEER_CONF_IF, peer->conf_if);
+
+ /* Remove BFD configuration. */
+ if (peer->bfd_config)
+ bgp_peer_remove_bfd_config(peer);
+
+ FOREACH_AFI_SAFI (afi, safi)
+ bgp_addpath_set_peer_type(peer, afi, safi, BGP_ADDPATH_NONE);
+
+ bgp_unlock(peer->bgp);
+
+ memset(peer, 0, sizeof(struct peer));
+
+ XFREE(MTYPE_BGP_PEER, peer);
+}
+
+/* increase reference count on a struct peer */
+struct peer *peer_lock_with_caller(const char *name, struct peer *peer)
+{
+ assert(peer && (peer->lock >= 0));
+
+ peer->lock++;
+
+ return peer;
+}
+
+/* decrease reference count on a struct peer
+ * struct peer is freed and NULL returned if last reference
+ */
+struct peer *peer_unlock_with_caller(const char *name, struct peer *peer)
+{
+ assert(peer && (peer->lock > 0));
+
+ peer->lock--;
+
+ if (peer->lock == 0) {
+ peer_free(peer);
+ return NULL;
+ }
+
+ return peer;
+}
+/* BGP GR changes */
+
+int bgp_global_gr_init(struct bgp *bgp)
+{
+ if (BGP_DEBUG(graceful_restart, GRACEFUL_RESTART))
+ zlog_debug("%s called ..", __func__);
+
+ int local_GLOBAL_GR_FSM[BGP_GLOBAL_GR_MODE][BGP_GLOBAL_GR_EVENT_CMD] = {
+ /* GLOBAL_HELPER Mode */
+ {
+ /*Event -> */
+ /*GLOBAL_GR_cmd*/ /*no_Global_GR_cmd*/
+ GLOBAL_GR, GLOBAL_INVALID,
+ /*GLOBAL_DISABLE_cmd*/ /*no_Global_Disable_cmd*/
+ GLOBAL_DISABLE, GLOBAL_INVALID
+ },
+ /* GLOBAL_GR Mode */
+ {
+ /*Event -> */
+ /*GLOBAL_GR_cmd*/ /*no_Global_GR_cmd*/
+ GLOBAL_GR, GLOBAL_HELPER,
+ /*GLOBAL_DISABLE_cmd*/ /*no_Global_Disable_cmd*/
+ GLOBAL_DISABLE, GLOBAL_INVALID
+ },
+ /* GLOBAL_DISABLE Mode */
+ {
+ /*Event -> */
+ /*GLOBAL_GR_cmd */ /*no_Global_GR_cmd*/
+ GLOBAL_GR, GLOBAL_INVALID,
+ /*GLOBAL_DISABLE_cmd*//*no_Global_Disable_cmd*/
+ GLOBAL_INVALID, GLOBAL_HELPER
+ },
+ /* GLOBAL_INVALID Mode */
+ {
+ /*Event -> */
+ /*GLOBAL_GR_cmd*/ /*no_Global_GR_cmd*/
+ GLOBAL_INVALID, GLOBAL_INVALID,
+ /*GLOBAL_DISABLE_cmd*/ /*no_Global_Disable_cmd*/
+ GLOBAL_INVALID, GLOBAL_INVALID
+ }
+ };
+ memcpy(bgp->GLOBAL_GR_FSM, local_GLOBAL_GR_FSM,
+ sizeof(local_GLOBAL_GR_FSM));
+
+ bgp->global_gr_present_state = GLOBAL_HELPER;
+ bgp->present_zebra_gr_state = ZEBRA_GR_DISABLE;
+
+ return BGP_GR_SUCCESS;
+}
+
+int bgp_peer_gr_init(struct peer *peer)
+{
+ if (BGP_DEBUG(graceful_restart, GRACEFUL_RESTART))
+ zlog_debug("%s called ..", __func__);
+
+ struct bgp_peer_gr local_Peer_GR_FSM[BGP_PEER_GR_MODE]
+ [BGP_PEER_GR_EVENT_CMD] = {
+ {
+ /* PEER_HELPER Mode */
+ /* Event-> */ /* PEER_GR_CMD */ /* NO_PEER_GR_CMD */
+ { PEER_GR, bgp_peer_gr_action }, {PEER_INVALID, NULL },
+ /* Event-> */ /* PEER_DISABLE_CMD */ /* NO_PEER_DISABLE_CMD */
+ {PEER_DISABLE, bgp_peer_gr_action }, {PEER_INVALID, NULL },
+ /* Event-> */ /* PEER_HELPER_cmd */ /* NO_PEER_HELPER_CMD */
+ { PEER_INVALID, NULL }, {PEER_GLOBAL_INHERIT,
+ bgp_peer_gr_action }
+ },
+ {
+ /* PEER_GR Mode */
+ /* Event-> */ /* PEER_GR_CMD */ /* NO_PEER_GR_CMD */
+ { PEER_INVALID, NULL }, { PEER_GLOBAL_INHERIT,
+ bgp_peer_gr_action },
+ /* Event-> */ /* PEER_DISABLE_CMD */ /* NO_PEER_DISABLE_CMD */
+ {PEER_DISABLE, bgp_peer_gr_action }, { PEER_INVALID, NULL },
+ /* Event-> */ /* PEER_HELPER_cmd */ /* NO_PEER_HELPER_CMD */
+ { PEER_HELPER, bgp_peer_gr_action }, { PEER_INVALID, NULL }
+ },
+ {
+ /* PEER_DISABLE Mode */
+ /* Event-> */ /* PEER_GR_CMD */ /* NO_PEER_GR_CMD */
+ { PEER_GR, bgp_peer_gr_action }, { PEER_INVALID, NULL },
+ /* Event-> */ /* PEER_DISABLE_CMD */ /* NO_PEER_DISABLE_CMD */
+ { PEER_INVALID, NULL }, { PEER_GLOBAL_INHERIT,
+ bgp_peer_gr_action },
+ /* Event-> */ /* PEER_HELPER_cmd */ /* NO_PEER_HELPER_CMD */
+ { PEER_HELPER, bgp_peer_gr_action }, { PEER_INVALID, NULL }
+ },
+ {
+ /* PEER_INVALID Mode */
+ /* Event-> */ /* PEER_GR_CMD */ /* NO_PEER_GR_CMD */
+ { PEER_INVALID, NULL }, { PEER_INVALID, NULL },
+ /* Event-> */ /* PEER_DISABLE_CMD */ /* NO_PEER_DISABLE_CMD */
+ { PEER_INVALID, NULL }, { PEER_INVALID, NULL },
+ /* Event-> */ /* PEER_HELPER_cmd */ /* NO_PEER_HELPER_CMD */
+ { PEER_INVALID, NULL }, { PEER_INVALID, NULL },
+ },
+ {
+ /* PEER_GLOBAL_INHERIT Mode */
+ /* Event-> */ /* PEER_GR_CMD */ /* NO_PEER_GR_CMD */
+ { PEER_GR, bgp_peer_gr_action }, { PEER_INVALID, NULL },
+ /* Event-> */ /* PEER_DISABLE_CMD */ /* NO_PEER_DISABLE_CMD */
+ { PEER_DISABLE, bgp_peer_gr_action}, { PEER_INVALID, NULL },
+ /* Event-> */ /* PEER_HELPER_cmd */ /* NO_PEER_HELPER_CMD */
+ { PEER_HELPER, bgp_peer_gr_action }, { PEER_INVALID, NULL }
+ }
+ };
+ memcpy(&peer->PEER_GR_FSM, local_Peer_GR_FSM,
+ sizeof(local_Peer_GR_FSM));
+ peer->peer_gr_present_state = PEER_GLOBAL_INHERIT;
+ bgp_peer_move_to_gr_mode(peer, PEER_GLOBAL_INHERIT);
+
+ return BGP_GR_SUCCESS;
+}
+
+static void bgp_srv6_init(struct bgp *bgp)
+{
+ bgp->srv6_enabled = false;
+ memset(bgp->srv6_locator_name, 0, sizeof(bgp->srv6_locator_name));
+ bgp->srv6_locator_chunks = list_new();
+ bgp->srv6_functions = list_new();
+}
+
+static void bgp_srv6_cleanup(struct bgp *bgp)
+{
+ if (bgp->srv6_locator_chunks)
+ list_delete(&bgp->srv6_locator_chunks);
+ if (bgp->srv6_functions)
+ list_delete(&bgp->srv6_functions);
+}
+
+/* Allocate new peer object, implicitely locked. */
+struct peer *peer_new(struct bgp *bgp)
+{
+ afi_t afi;
+ safi_t safi;
+ struct peer *peer;
+ struct servent *sp;
+
+ /* bgp argument is absolutely required */
+ assert(bgp);
+
+ /* Allocate new peer. */
+ peer = XCALLOC(MTYPE_BGP_PEER, sizeof(struct peer));
+
+ /* Set default value. */
+ peer->fd = -1;
+ peer->v_start = BGP_INIT_START_TIMER;
+ peer->v_connect = bgp->default_connect_retry;
+ peer->status = Idle;
+ peer->ostatus = Idle;
+ peer->cur_event = peer->last_event = peer->last_major_event = 0;
+ peer->bgp = bgp_lock(bgp);
+ peer = peer_lock(peer); /* initial reference */
+ peer->local_role = ROLE_UNDEFINED;
+ peer->remote_role = ROLE_UNDEFINED;
+ peer->password = NULL;
+ peer->max_packet_size = BGP_STANDARD_MESSAGE_MAX_PACKET_SIZE;
+
+ /* Set default flags. */
+ FOREACH_AFI_SAFI (afi, safi) {
+ SET_FLAG(peer->af_flags[afi][safi], PEER_FLAG_SEND_COMMUNITY);
+ SET_FLAG(peer->af_flags[afi][safi],
+ PEER_FLAG_SEND_EXT_COMMUNITY);
+ SET_FLAG(peer->af_flags[afi][safi],
+ PEER_FLAG_SEND_LARGE_COMMUNITY);
+
+ SET_FLAG(peer->af_flags_invert[afi][safi],
+ PEER_FLAG_SEND_COMMUNITY);
+ SET_FLAG(peer->af_flags_invert[afi][safi],
+ PEER_FLAG_SEND_EXT_COMMUNITY);
+ SET_FLAG(peer->af_flags_invert[afi][safi],
+ PEER_FLAG_SEND_LARGE_COMMUNITY);
+ peer->addpath_type[afi][safi] = BGP_ADDPATH_NONE;
+ peer->soo[afi][safi] = NULL;
+ }
+
+ /* set nexthop-unchanged for l2vpn evpn by default */
+ SET_FLAG(peer->af_flags[AFI_L2VPN][SAFI_EVPN],
+ PEER_FLAG_NEXTHOP_UNCHANGED);
+
+ SET_FLAG(peer->sflags, PEER_STATUS_CAPABILITY_OPEN);
+
+ /* Initialize per peer bgp GR FSM */
+ bgp_peer_gr_init(peer);
+
+ /* Create buffers. */
+ peer->ibuf = stream_fifo_new();
+ peer->obuf = stream_fifo_new();
+ pthread_mutex_init(&peer->io_mtx, NULL);
+
+ /* We use a larger buffer for peer->obuf_work in the event that:
+ * - We RX a BGP_UPDATE where the attributes alone are just
+ * under BGP_EXTENDED_MESSAGE_MAX_PACKET_SIZE.
+ * - The user configures an outbound route-map that does many as-path
+ * prepends or adds many communities. At most they can have
+ * CMD_ARGC_MAX args in a route-map so there is a finite limit on how
+ * large they can make the attributes.
+ *
+ * Having a buffer with BGP_MAX_PACKET_SIZE_OVERFLOW allows us to avoid
+ * bounds checking for every single attribute as we construct an
+ * UPDATE.
+ */
+ peer->obuf_work =
+ stream_new(BGP_MAX_PACKET_SIZE + BGP_MAX_PACKET_SIZE_OVERFLOW);
+ peer->ibuf_work =
+ ringbuf_new(BGP_MAX_PACKET_SIZE * BGP_READ_PACKET_MAX);
+
+ peer->scratch = stream_new(BGP_MAX_PACKET_SIZE);
+
+ bgp_sync_init(peer);
+
+ /* Get service port number. */
+ sp = getservbyname("bgp", "tcp");
+ peer->port = (sp == NULL) ? BGP_PORT_DEFAULT : ntohs(sp->s_port);
+
+ QOBJ_REG(peer, peer);
+ return peer;
+}
+
+/*
+ * This function is invoked when a duplicate peer structure associated with
+ * a neighbor is being deleted. If this about-to-be-deleted structure is
+ * the one with all the config, then we have to copy over the info.
+ */
+void peer_xfer_config(struct peer *peer_dst, struct peer *peer_src)
+{
+ struct peer_af *paf;
+ afi_t afi;
+ safi_t safi;
+ int afidx;
+
+ assert(peer_src);
+ assert(peer_dst);
+
+ /* The following function is used by both peer group config copy to
+ * individual peer and when we transfer config
+ */
+ if (peer_src->change_local_as)
+ peer_dst->change_local_as = peer_src->change_local_as;
+
+ /* peer flags apply */
+ peer_dst->flags = peer_src->flags;
+
+ peer_dst->peer_gr_present_state = peer_src->peer_gr_present_state;
+ peer_dst->peer_gr_new_status_flag = peer_src->peer_gr_new_status_flag;
+
+ peer_dst->local_as = peer_src->local_as;
+ peer_dst->port = peer_src->port;
+ /* copy tcp_mss value */
+ peer_dst->tcp_mss = peer_src->tcp_mss;
+ (void)peer_sort(peer_dst);
+ peer_dst->rmap_type = peer_src->rmap_type;
+ peer_dst->local_role = peer_src->local_role;
+
+ peer_dst->max_packet_size = peer_src->max_packet_size;
+
+ /* Timers */
+ peer_dst->holdtime = peer_src->holdtime;
+ peer_dst->keepalive = peer_src->keepalive;
+ peer_dst->connect = peer_src->connect;
+ peer_dst->delayopen = peer_src->delayopen;
+ peer_dst->v_holdtime = peer_src->v_holdtime;
+ peer_dst->v_keepalive = peer_src->v_keepalive;
+ peer_dst->routeadv = peer_src->routeadv;
+ peer_dst->v_routeadv = peer_src->v_routeadv;
+ peer_dst->v_delayopen = peer_src->v_delayopen;
+
+ /* password apply */
+ if (peer_src->password) {
+ XFREE(MTYPE_PEER_PASSWORD, peer_dst->password);
+ peer_dst->password =
+ XSTRDUP(MTYPE_PEER_PASSWORD, peer_src->password);
+ }
+
+ FOREACH_AFI_SAFI (afi, safi) {
+ peer_dst->afc[afi][safi] = peer_src->afc[afi][safi];
+ peer_dst->af_flags[afi][safi] = peer_src->af_flags[afi][safi];
+ peer_dst->allowas_in[afi][safi] =
+ peer_src->allowas_in[afi][safi];
+ peer_dst->weight[afi][safi] = peer_src->weight[afi][safi];
+ peer_dst->addpath_type[afi][safi] =
+ peer_src->addpath_type[afi][safi];
+ }
+
+ for (afidx = BGP_AF_START; afidx < BGP_AF_MAX; afidx++) {
+ paf = peer_src->peer_af_array[afidx];
+ if (paf != NULL) {
+ if (!peer_af_find(peer_dst, paf->afi, paf->safi))
+ peer_af_create(peer_dst, paf->afi, paf->safi);
+ }
+ }
+
+ /* update-source apply */
+ if (peer_src->update_source) {
+ if (peer_dst->update_source)
+ sockunion_free(peer_dst->update_source);
+ XFREE(MTYPE_PEER_UPDATE_SOURCE, peer_dst->update_if);
+ peer_dst->update_source =
+ sockunion_dup(peer_src->update_source);
+ } else if (peer_src->update_if) {
+ XFREE(MTYPE_PEER_UPDATE_SOURCE, peer_dst->update_if);
+ if (peer_dst->update_source) {
+ sockunion_free(peer_dst->update_source);
+ peer_dst->update_source = NULL;
+ }
+ peer_dst->update_if =
+ XSTRDUP(MTYPE_PEER_UPDATE_SOURCE, peer_src->update_if);
+ }
+
+ if (peer_src->ifname) {
+ XFREE(MTYPE_BGP_PEER_IFNAME, peer_dst->ifname);
+
+ peer_dst->ifname =
+ XSTRDUP(MTYPE_BGP_PEER_IFNAME, peer_src->ifname);
+ }
+}
+
+static int bgp_peer_conf_if_to_su_update_v4(struct peer *peer,
+ struct interface *ifp)
+{
+ struct connected *ifc;
+ struct prefix p;
+ uint32_t addr;
+ struct listnode *node;
+
+ /* If our IPv4 address on the interface is /30 or /31, we can derive the
+ * IPv4 address of the other end.
+ */
+ for (ALL_LIST_ELEMENTS_RO(ifp->connected, node, ifc)) {
+ if (ifc->address && (ifc->address->family == AF_INET)) {
+ prefix_copy(&p, CONNECTED_PREFIX(ifc));
+ if (p.prefixlen == 30) {
+ peer->su.sa.sa_family = AF_INET;
+ addr = ntohl(p.u.prefix4.s_addr);
+ if (addr % 4 == 1)
+ peer->su.sin.sin_addr.s_addr =
+ htonl(addr + 1);
+ else if (addr % 4 == 2)
+ peer->su.sin.sin_addr.s_addr =
+ htonl(addr - 1);
+#ifdef HAVE_STRUCT_SOCKADDR_IN_SIN_LEN
+ peer->su.sin.sin_len =
+ sizeof(struct sockaddr_in);
+#endif /* HAVE_STRUCT_SOCKADDR_IN_SIN_LEN */
+ return 1;
+ } else if (p.prefixlen == 31) {
+ peer->su.sa.sa_family = AF_INET;
+ addr = ntohl(p.u.prefix4.s_addr);
+ if (addr % 2 == 0)
+ peer->su.sin.sin_addr.s_addr =
+ htonl(addr + 1);
+ else
+ peer->su.sin.sin_addr.s_addr =
+ htonl(addr - 1);
+#ifdef HAVE_STRUCT_SOCKADDR_IN_SIN_LEN
+ peer->su.sin.sin_len =
+ sizeof(struct sockaddr_in);
+#endif /* HAVE_STRUCT_SOCKADDR_IN_SIN_LEN */
+ return 1;
+ } else if (bgp_debug_neighbor_events(peer))
+ zlog_debug(
+ "%s: IPv4 interface address is not /30 or /31, v4 session not started",
+ peer->conf_if);
+ }
+ }
+
+ return 0;
+}
+
+static bool bgp_peer_conf_if_to_su_update_v6(struct peer *peer,
+ struct interface *ifp)
+{
+ struct nbr_connected *ifc_nbr;
+
+ /* Have we learnt the peer's IPv6 link-local address? */
+ if (ifp->nbr_connected
+ && (ifc_nbr = listnode_head(ifp->nbr_connected))) {
+ peer->su.sa.sa_family = AF_INET6;
+ memcpy(&peer->su.sin6.sin6_addr, &ifc_nbr->address->u.prefix,
+ sizeof(struct in6_addr));
+#ifdef SIN6_LEN
+ peer->su.sin6.sin6_len = sizeof(struct sockaddr_in6);
+#endif
+ peer->su.sin6.sin6_scope_id = ifp->ifindex;
+ return true;
+ }
+
+ return false;
+}
+
+/*
+ * Set or reset the peer address socketunion structure based on the
+ * learnt/derived peer address. If the address has changed, update the
+ * password on the listen socket, if needed.
+ */
+void bgp_peer_conf_if_to_su_update(struct peer *peer)
+{
+ struct interface *ifp;
+ int prev_family;
+ int peer_addr_updated = 0;
+
+ if (!peer->conf_if)
+ return;
+
+ /*
+ * Our peer structure is stored in the bgp->peerhash
+ * release it before we modify anything.
+ */
+ hash_release(peer->bgp->peerhash, peer);
+
+ prev_family = peer->su.sa.sa_family;
+ if ((ifp = if_lookup_by_name(peer->conf_if, peer->bgp->vrf_id))) {
+ peer->ifp = ifp;
+ /* If BGP unnumbered is not "v6only", we first see if we can
+ * derive the
+ * peer's IPv4 address.
+ */
+ if (!CHECK_FLAG(peer->flags, PEER_FLAG_IFPEER_V6ONLY))
+ peer_addr_updated =
+ bgp_peer_conf_if_to_su_update_v4(peer, ifp);
+
+ /* If "v6only" or we can't derive peer's IPv4 address, see if
+ * we've
+ * learnt the peer's IPv6 link-local address. This is from the
+ * source
+ * IPv6 address in router advertisement.
+ */
+ if (!peer_addr_updated)
+ peer_addr_updated =
+ bgp_peer_conf_if_to_su_update_v6(peer, ifp);
+ }
+ /* If we could derive the peer address, we may need to install the
+ * password
+ * configured for the peer, if any, on the listen socket. Otherwise,
+ * mark
+ * that peer's address is not available and uninstall the password, if
+ * needed.
+ */
+ if (peer_addr_updated) {
+ if (CHECK_FLAG(peer->flags, PEER_FLAG_PASSWORD)
+ && prev_family == AF_UNSPEC)
+ bgp_md5_set(peer);
+ } else {
+ if (CHECK_FLAG(peer->flags, PEER_FLAG_PASSWORD)
+ && prev_family != AF_UNSPEC)
+ bgp_md5_unset(peer);
+ peer->su.sa.sa_family = AF_UNSPEC;
+ memset(&peer->su.sin6.sin6_addr, 0, sizeof(struct in6_addr));
+ }
+
+ /*
+ * Since our su changed we need to del/add peer to the peerhash
+ */
+ (void)hash_get(peer->bgp->peerhash, peer, hash_alloc_intern);
+}
+
+void bgp_recalculate_afi_safi_bestpaths(struct bgp *bgp, afi_t afi, safi_t safi)
+{
+ struct bgp_dest *dest, *ndest;
+ struct bgp_table *table;
+
+ 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) {
+ /* Special handling for 2-level routing
+ * tables. */
+ if (safi == SAFI_MPLS_VPN || safi == SAFI_ENCAP
+ || safi == SAFI_EVPN) {
+ for (ndest = bgp_table_top(table); ndest;
+ ndest = bgp_route_next(ndest))
+ bgp_process(bgp, ndest, afi, safi);
+ } else
+ bgp_process(bgp, dest, afi, safi);
+ }
+ }
+}
+
+/* Force a bestpath recalculation for all prefixes. This is used
+ * when 'bgp bestpath' commands are entered.
+ */
+void bgp_recalculate_all_bestpaths(struct bgp *bgp)
+{
+ afi_t afi;
+ safi_t safi;
+
+ FOREACH_AFI_SAFI (afi, safi) {
+ bgp_recalculate_afi_safi_bestpaths(bgp, afi, safi);
+ }
+}
+
+/*
+ * Create new BGP peer.
+ *
+ * conf_if and su are mutually exclusive if configuring from the cli.
+ * If we are handing a doppelganger, then we *must* pass in both
+ * the original peer's su and conf_if, so that we can appropriately
+ * track the bgp->peerhash( ie we don't want to remove the current
+ * one from the config ).
+ */
+struct peer *peer_create(union sockunion *su, const char *conf_if,
+ struct bgp *bgp, as_t local_as, as_t remote_as,
+ int as_type, struct peer_group *group)
+{
+ int active;
+ struct peer *peer;
+ char buf[SU_ADDRSTRLEN];
+ afi_t afi;
+ safi_t safi;
+
+ peer = peer_new(bgp);
+ if (conf_if) {
+ peer->conf_if = XSTRDUP(MTYPE_PEER_CONF_IF, conf_if);
+ if (su)
+ peer->su = *su;
+ else
+ bgp_peer_conf_if_to_su_update(peer);
+ XFREE(MTYPE_BGP_PEER_HOST, peer->host);
+ peer->host = XSTRDUP(MTYPE_BGP_PEER_HOST, conf_if);
+ } else if (su) {
+ peer->su = *su;
+ sockunion2str(su, buf, SU_ADDRSTRLEN);
+ XFREE(MTYPE_BGP_PEER_HOST, peer->host);
+ peer->host = XSTRDUP(MTYPE_BGP_PEER_HOST, buf);
+ }
+ peer->local_as = local_as;
+ peer->as = remote_as;
+ peer->as_type = as_type;
+ peer->local_id = bgp->router_id;
+ peer->v_holdtime = bgp->default_holdtime;
+ peer->v_keepalive = bgp->default_keepalive;
+ peer->v_routeadv = (peer_sort(peer) == BGP_PEER_IBGP)
+ ? BGP_DEFAULT_IBGP_ROUTEADV
+ : BGP_DEFAULT_EBGP_ROUTEADV;
+ if (bgp_config_inprocess())
+ peer->shut_during_cfg = true;
+
+ peer = peer_lock(peer); /* bgp peer list reference */
+ peer->group = group;
+ listnode_add_sort(bgp->peer, peer);
+ (void)hash_get(bgp->peerhash, peer, hash_alloc_intern);
+
+ /* Adjust update-group coalesce timer heuristics for # peers. */
+ if (bgp->heuristic_coalesce) {
+ long ct = BGP_DEFAULT_SUBGROUP_COALESCE_TIME
+ + (bgp->peer->count
+ * BGP_PEER_ADJUST_SUBGROUP_COALESCE_TIME);
+ bgp->coalesce_time = MIN(BGP_MAX_SUBGROUP_COALESCE_TIME, ct);
+ }
+
+ active = peer_active(peer);
+ if (!active) {
+ if (peer->su.sa.sa_family == AF_UNSPEC)
+ peer->last_reset = PEER_DOWN_NBR_ADDR;
+ else
+ peer->last_reset = PEER_DOWN_NOAFI_ACTIVATED;
+ }
+
+ /* Last read and reset time set */
+ peer->readtime = peer->resettime = monotime(NULL);
+
+ /* Default TTL set. */
+ peer->ttl = (peer->sort == BGP_PEER_IBGP) ? MAXTTL : BGP_DEFAULT_TTL;
+
+ /* Default configured keepalives count for shutdown rtt command */
+ peer->rtt_keepalive_conf = 1;
+
+ SET_FLAG(peer->flags, PEER_FLAG_CONFIG_NODE);
+
+ /* If 'bgp default <afi>-<safi>' is configured, then activate the
+ * neighbor for the corresponding address family. IPv4 Unicast is
+ * the only address family enabled by default without expliict
+ * configuration.
+ */
+ FOREACH_AFI_SAFI (afi, safi) {
+ if (bgp->default_af[afi][safi]) {
+ peer->afc[afi][safi] = 1;
+ peer_af_create(peer, afi, safi);
+ }
+ }
+
+ /* auto shutdown if configured */
+ if (bgp->autoshutdown)
+ peer_flag_set(peer, PEER_FLAG_SHUTDOWN);
+ /* Set up peer's events and timers. */
+ else if (!active && peer_active(peer))
+ bgp_timer_set(peer);
+
+ bgp_peer_gr_flags_update(peer);
+ BGP_GR_ROUTER_DETECT_AND_SEND_CAPABILITY_TO_ZEBRA(bgp, bgp->peer);
+
+ return peer;
+}
+
+/* Make accept BGP peer. This function is only called from the test code */
+struct peer *peer_create_accept(struct bgp *bgp)
+{
+ struct peer *peer;
+
+ peer = peer_new(bgp);
+
+ peer = peer_lock(peer); /* bgp peer list reference */
+ listnode_add_sort(bgp->peer, peer);
+
+ return peer;
+}
+
+/*
+ * Return true if we have a peer configured to use this afi/safi
+ */
+bool bgp_afi_safi_peer_exists(struct bgp *bgp, afi_t afi, safi_t safi)
+{
+ struct listnode *node;
+ struct peer *peer;
+
+ for (ALL_LIST_ELEMENTS_RO(bgp->peer, node, peer)) {
+ if (!CHECK_FLAG(peer->flags, PEER_FLAG_CONFIG_NODE))
+ continue;
+
+ if (peer->afc[afi][safi])
+ return true;
+ }
+
+ return false;
+}
+
+/* Change peer's AS number. */
+void peer_as_change(struct peer *peer, as_t as, int as_specified)
+{
+ enum bgp_peer_sort origtype, newtype;
+
+ /* Stop peer. */
+ if (!CHECK_FLAG(peer->sflags, PEER_STATUS_GROUP)) {
+ if (BGP_IS_VALID_STATE_FOR_NOTIF(peer->status)) {
+ peer->last_reset = PEER_DOWN_REMOTE_AS_CHANGE;
+ bgp_notify_send(peer, BGP_NOTIFY_CEASE,
+ BGP_NOTIFY_CEASE_CONFIG_CHANGE);
+ } else
+ bgp_session_reset(peer);
+ }
+ origtype = peer_sort_lookup(peer);
+ peer->as = as;
+ peer->as_type = as_specified;
+
+ if (bgp_config_check(peer->bgp, BGP_CONFIG_CONFEDERATION)
+ && !bgp_confederation_peers_check(peer->bgp, as)
+ && peer->bgp->as != as)
+ peer->local_as = peer->bgp->confed_id;
+ else
+ peer->local_as = peer->bgp->as;
+
+ newtype = peer_sort(peer);
+ /* Advertisement-interval reset */
+ if (!CHECK_FLAG(peer->flags, PEER_FLAG_ROUTEADV)) {
+ peer->v_routeadv = (newtype == BGP_PEER_IBGP)
+ ? BGP_DEFAULT_IBGP_ROUTEADV
+ : BGP_DEFAULT_EBGP_ROUTEADV;
+ }
+
+ /* TTL reset */
+ if (newtype == BGP_PEER_IBGP)
+ peer->ttl = MAXTTL;
+ else if (origtype == BGP_PEER_IBGP)
+ peer->ttl = BGP_DEFAULT_TTL;
+
+ /* reflector-client reset */
+ if (newtype != BGP_PEER_IBGP) {
+ UNSET_FLAG(peer->af_flags[AFI_IP][SAFI_UNICAST],
+ PEER_FLAG_REFLECTOR_CLIENT);
+ UNSET_FLAG(peer->af_flags[AFI_IP][SAFI_MULTICAST],
+ PEER_FLAG_REFLECTOR_CLIENT);
+ UNSET_FLAG(peer->af_flags[AFI_IP][SAFI_LABELED_UNICAST],
+ PEER_FLAG_REFLECTOR_CLIENT);
+ UNSET_FLAG(peer->af_flags[AFI_IP][SAFI_MPLS_VPN],
+ PEER_FLAG_REFLECTOR_CLIENT);
+ UNSET_FLAG(peer->af_flags[AFI_IP][SAFI_ENCAP],
+ PEER_FLAG_REFLECTOR_CLIENT);
+ UNSET_FLAG(peer->af_flags[AFI_IP][SAFI_FLOWSPEC],
+ PEER_FLAG_REFLECTOR_CLIENT);
+ UNSET_FLAG(peer->af_flags[AFI_IP6][SAFI_UNICAST],
+ PEER_FLAG_REFLECTOR_CLIENT);
+ UNSET_FLAG(peer->af_flags[AFI_IP6][SAFI_MULTICAST],
+ PEER_FLAG_REFLECTOR_CLIENT);
+ UNSET_FLAG(peer->af_flags[AFI_IP6][SAFI_LABELED_UNICAST],
+ PEER_FLAG_REFLECTOR_CLIENT);
+ UNSET_FLAG(peer->af_flags[AFI_IP6][SAFI_MPLS_VPN],
+ PEER_FLAG_REFLECTOR_CLIENT);
+ UNSET_FLAG(peer->af_flags[AFI_IP6][SAFI_ENCAP],
+ PEER_FLAG_REFLECTOR_CLIENT);
+ UNSET_FLAG(peer->af_flags[AFI_IP6][SAFI_FLOWSPEC],
+ PEER_FLAG_REFLECTOR_CLIENT);
+ UNSET_FLAG(peer->af_flags[AFI_L2VPN][SAFI_EVPN],
+ PEER_FLAG_REFLECTOR_CLIENT);
+ }
+
+ /* local-as reset */
+ if (newtype != BGP_PEER_EBGP) {
+ peer->change_local_as = 0;
+ peer_flag_unset(peer, PEER_FLAG_LOCAL_AS);
+ peer_flag_unset(peer, PEER_FLAG_LOCAL_AS_NO_PREPEND);
+ peer_flag_unset(peer, PEER_FLAG_LOCAL_AS_REPLACE_AS);
+ }
+}
+
+/* If peer does not exist, create new one. If peer already exists,
+ set AS number to the peer. */
+int peer_remote_as(struct bgp *bgp, union sockunion *su, const char *conf_if,
+ as_t *as, int as_type)
+{
+ struct peer *peer;
+ as_t local_as;
+
+ if (conf_if)
+ peer = peer_lookup_by_conf_if(bgp, conf_if);
+ else
+ peer = peer_lookup(bgp, su);
+
+ if (peer) {
+ /* Not allowed for a dynamic peer. */
+ if (peer_dynamic_neighbor(peer)) {
+ *as = peer->as;
+ return BGP_ERR_INVALID_FOR_DYNAMIC_PEER;
+ }
+
+ /* When this peer is a member of peer-group. */
+ if (peer->group) {
+ /* peer-group already has AS number/internal/external */
+ if (peer->group->conf->as
+ || peer->group->conf->as_type) {
+ /* Return peer group's AS number. */
+ *as = peer->group->conf->as;
+ return BGP_ERR_PEER_GROUP_MEMBER;
+ }
+
+ enum bgp_peer_sort peer_sort_type =
+ peer_sort(peer->group->conf);
+
+ /* Explicit AS numbers used, compare AS numbers */
+ if (as_type == AS_SPECIFIED) {
+ if (((peer_sort_type == BGP_PEER_IBGP)
+ && (bgp->as != *as))
+ || ((peer_sort_type == BGP_PEER_EBGP)
+ && (bgp->as == *as))) {
+ *as = peer->as;
+ return BGP_ERR_PEER_GROUP_PEER_TYPE_DIFFERENT;
+ }
+ } else {
+ /* internal/external used, compare as-types */
+ if (((peer_sort_type == BGP_PEER_IBGP)
+ && (as_type != AS_INTERNAL))
+ || ((peer_sort_type == BGP_PEER_EBGP)
+ && (as_type != AS_EXTERNAL))) {
+ *as = peer->as;
+ return BGP_ERR_PEER_GROUP_PEER_TYPE_DIFFERENT;
+ }
+ }
+ }
+
+ /* Existing peer's AS number change. */
+ if (((peer->as_type == AS_SPECIFIED) && peer->as != *as)
+ || (peer->as_type != as_type))
+ peer_as_change(peer, *as, as_type);
+ } else {
+ if (conf_if)
+ return BGP_ERR_NO_INTERFACE_CONFIG;
+
+ /* If the peer is not part of our confederation, and its not an
+ iBGP peer then spoof the source AS */
+ if (bgp_config_check(bgp, BGP_CONFIG_CONFEDERATION) &&
+ !bgp_confederation_peers_check(bgp, *as) && *as &&
+ bgp->as != *as)
+ local_as = bgp->confed_id;
+ else
+ local_as = bgp->as;
+
+ peer_create(su, conf_if, bgp, local_as, *as, as_type, NULL);
+ }
+
+ return 0;
+}
+
+const char *bgp_get_name_by_role(uint8_t role)
+{
+ switch (role) {
+ case ROLE_PROVIDER:
+ return "provider";
+ case ROLE_RS_SERVER:
+ return "rs-server";
+ case ROLE_RS_CLIENT:
+ return "rs-client";
+ case ROLE_CUSTOMER:
+ return "customer";
+ case ROLE_PEER:
+ return "peer";
+ case ROLE_UNDEFINED:
+ return "undefined";
+ }
+ return "unknown";
+}
+
+static void peer_group2peer_config_copy_af(struct peer_group *group,
+ struct peer *peer, afi_t afi,
+ safi_t safi)
+{
+ int in = FILTER_IN;
+ int out = FILTER_OUT;
+ uint64_t flags_tmp;
+ uint64_t pflags_ovrd;
+ uint8_t *pfilter_ovrd;
+ struct peer *conf;
+
+ conf = group->conf;
+ pflags_ovrd = peer->af_flags_override[afi][safi];
+ pfilter_ovrd = &peer->filter_override[afi][safi][in];
+
+ /* peer af_flags apply */
+ flags_tmp = conf->af_flags[afi][safi] & ~pflags_ovrd;
+ flags_tmp ^= conf->af_flags_invert[afi][safi]
+ ^ peer->af_flags_invert[afi][safi];
+ flags_tmp &= ~pflags_ovrd;
+
+ UNSET_FLAG(peer->af_flags[afi][safi], ~pflags_ovrd);
+ SET_FLAG(peer->af_flags[afi][safi], flags_tmp);
+ SET_FLAG(peer->af_flags_invert[afi][safi],
+ conf->af_flags_invert[afi][safi]);
+
+ /* maximum-prefix */
+ if (!CHECK_FLAG(pflags_ovrd, PEER_FLAG_MAX_PREFIX)) {
+ PEER_ATTR_INHERIT(peer, group, pmax[afi][safi]);
+ PEER_ATTR_INHERIT(peer, group, pmax_threshold[afi][safi]);
+ PEER_ATTR_INHERIT(peer, group, pmax_restart[afi][safi]);
+ }
+
+ /* maximum-prefix-out */
+ if (!CHECK_FLAG(pflags_ovrd, PEER_FLAG_MAX_PREFIX_OUT))
+ PEER_ATTR_INHERIT(peer, group, pmax_out[afi][safi]);
+
+ /* allowas-in */
+ if (!CHECK_FLAG(pflags_ovrd, PEER_FLAG_ALLOWAS_IN))
+ PEER_ATTR_INHERIT(peer, group, allowas_in[afi][safi]);
+
+ /* soo */
+ if (!CHECK_FLAG(pflags_ovrd, PEER_FLAG_SOO))
+ PEER_ATTR_INHERIT(peer, group, soo[afi][safi]);
+
+ /* weight */
+ if (!CHECK_FLAG(pflags_ovrd, PEER_FLAG_WEIGHT))
+ PEER_ATTR_INHERIT(peer, group, weight[afi][safi]);
+
+ /* default-originate route-map */
+ if (!CHECK_FLAG(pflags_ovrd, PEER_FLAG_DEFAULT_ORIGINATE)) {
+ PEER_STR_ATTR_INHERIT(peer, group, default_rmap[afi][safi].name,
+ MTYPE_ROUTE_MAP_NAME);
+ PEER_ATTR_INHERIT(peer, group, default_rmap[afi][safi].map);
+ }
+
+ /* inbound filter apply */
+ if (!CHECK_FLAG(pfilter_ovrd[in], PEER_FT_DISTRIBUTE_LIST)) {
+ PEER_STR_ATTR_INHERIT(peer, group,
+ filter[afi][safi].dlist[in].name,
+ MTYPE_BGP_FILTER_NAME);
+ PEER_ATTR_INHERIT(peer, group,
+ filter[afi][safi].dlist[in].alist);
+ }
+
+ if (!CHECK_FLAG(pfilter_ovrd[in], PEER_FT_PREFIX_LIST)) {
+ PEER_STR_ATTR_INHERIT(peer, group,
+ filter[afi][safi].plist[in].name,
+ MTYPE_BGP_FILTER_NAME);
+ PEER_ATTR_INHERIT(peer, group,
+ filter[afi][safi].plist[in].plist);
+ }
+
+ if (!CHECK_FLAG(pfilter_ovrd[in], PEER_FT_FILTER_LIST)) {
+ PEER_STR_ATTR_INHERIT(peer, group,
+ filter[afi][safi].aslist[in].name,
+ MTYPE_BGP_FILTER_NAME);
+ PEER_ATTR_INHERIT(peer, group,
+ filter[afi][safi].aslist[in].aslist);
+ }
+
+ if (!CHECK_FLAG(pfilter_ovrd[RMAP_IN], PEER_FT_ROUTE_MAP)) {
+ PEER_STR_ATTR_INHERIT(peer, group,
+ filter[afi][safi].map[in].name,
+ MTYPE_BGP_FILTER_NAME);
+ PEER_ATTR_INHERIT(peer, group,
+ filter[afi][safi].map[RMAP_IN].map);
+ }
+
+ /* outbound filter apply */
+ if (!CHECK_FLAG(pfilter_ovrd[out], PEER_FT_DISTRIBUTE_LIST)) {
+ PEER_STR_ATTR_INHERIT(peer, group,
+ filter[afi][safi].dlist[out].name,
+ MTYPE_BGP_FILTER_NAME);
+ PEER_ATTR_INHERIT(peer, group,
+ filter[afi][safi].dlist[out].alist);
+ }
+
+ if (!CHECK_FLAG(pfilter_ovrd[out], PEER_FT_PREFIX_LIST)) {
+ PEER_STR_ATTR_INHERIT(peer, group,
+ filter[afi][safi].plist[out].name,
+ MTYPE_BGP_FILTER_NAME);
+ PEER_ATTR_INHERIT(peer, group,
+ filter[afi][safi].plist[out].plist);
+ }
+
+ if (!CHECK_FLAG(pfilter_ovrd[out], PEER_FT_FILTER_LIST)) {
+ PEER_STR_ATTR_INHERIT(peer, group,
+ filter[afi][safi].aslist[out].name,
+ MTYPE_BGP_FILTER_NAME);
+ PEER_ATTR_INHERIT(peer, group,
+ filter[afi][safi].aslist[out].aslist);
+ }
+
+ if (!CHECK_FLAG(pfilter_ovrd[RMAP_OUT], PEER_FT_ROUTE_MAP)) {
+ PEER_STR_ATTR_INHERIT(peer, group,
+ filter[afi][safi].map[RMAP_OUT].name,
+ MTYPE_BGP_FILTER_NAME);
+ PEER_ATTR_INHERIT(peer, group,
+ filter[afi][safi].map[RMAP_OUT].map);
+ }
+
+ /* nondirectional filter apply */
+ if (!CHECK_FLAG(pfilter_ovrd[0], PEER_FT_UNSUPPRESS_MAP)) {
+ PEER_STR_ATTR_INHERIT(peer, group, filter[afi][safi].usmap.name,
+ MTYPE_BGP_FILTER_NAME);
+ PEER_ATTR_INHERIT(peer, group, filter[afi][safi].usmap.map);
+ }
+
+ /* Conditional Advertisements */
+ if (!CHECK_FLAG(pfilter_ovrd[RMAP_OUT], PEER_FT_ADVERTISE_MAP)) {
+ PEER_STR_ATTR_INHERIT(peer, group,
+ filter[afi][safi].advmap.aname,
+ MTYPE_BGP_FILTER_NAME);
+ PEER_ATTR_INHERIT(peer, group, filter[afi][safi].advmap.amap);
+ PEER_STR_ATTR_INHERIT(peer, group,
+ filter[afi][safi].advmap.cname,
+ MTYPE_BGP_FILTER_NAME);
+ PEER_ATTR_INHERIT(peer, group, filter[afi][safi].advmap.cmap);
+ PEER_ATTR_INHERIT(peer, group,
+ filter[afi][safi].advmap.condition);
+ }
+
+ if (peer->addpath_type[afi][safi] == BGP_ADDPATH_NONE) {
+ peer->addpath_type[afi][safi] = conf->addpath_type[afi][safi];
+ bgp_addpath_type_changed(conf->bgp);
+ }
+}
+
+static int peer_activate_af(struct peer *peer, afi_t afi, safi_t safi)
+{
+ int active;
+ struct peer *other;
+
+ if (CHECK_FLAG(peer->sflags, PEER_STATUS_GROUP)) {
+ flog_err(EC_BGP_PEER_GROUP, "%s was called for peer-group %s",
+ __func__, peer->host);
+ return 1;
+ }
+
+ /* Do not activate a peer for both SAFI_UNICAST and SAFI_LABELED_UNICAST
+ */
+ if ((safi == SAFI_UNICAST && peer->afc[afi][SAFI_LABELED_UNICAST])
+ || (safi == SAFI_LABELED_UNICAST && peer->afc[afi][SAFI_UNICAST]))
+ return BGP_ERR_PEER_SAFI_CONFLICT;
+
+ /* Nothing to do if we've already activated this peer */
+ if (peer->afc[afi][safi])
+ return 0;
+
+ if (peer_af_create(peer, afi, safi) == NULL)
+ return 1;
+
+ active = peer_active(peer);
+ peer->afc[afi][safi] = 1;
+
+ if (peer->group)
+ peer_group2peer_config_copy_af(peer->group, peer, afi, safi);
+
+ if (!active && peer_active(peer)) {
+ bgp_timer_set(peer);
+ } else {
+ if (peer_established(peer)) {
+ if (CHECK_FLAG(peer->cap, PEER_CAP_DYNAMIC_RCV)) {
+ peer->afc_adv[afi][safi] = 1;
+ bgp_capability_send(peer, afi, safi,
+ CAPABILITY_CODE_MP,
+ CAPABILITY_ACTION_SET);
+ if (peer->afc_recv[afi][safi]) {
+ peer->afc_nego[afi][safi] = 1;
+ bgp_announce_route(peer, afi, safi,
+ false);
+ }
+ } else {
+ peer->last_reset = PEER_DOWN_AF_ACTIVATE;
+ bgp_notify_send(peer, BGP_NOTIFY_CEASE,
+ BGP_NOTIFY_CEASE_CONFIG_CHANGE);
+ }
+ }
+ if (peer->status == OpenSent || peer->status == OpenConfirm) {
+ peer->last_reset = PEER_DOWN_AF_ACTIVATE;
+ bgp_notify_send(peer, BGP_NOTIFY_CEASE,
+ BGP_NOTIFY_CEASE_CONFIG_CHANGE);
+ }
+ /*
+ * If we are turning on a AFI/SAFI locally and we've
+ * started bringing a peer up, we need to tell
+ * the other peer to restart because we might loose
+ * configuration here because when the doppelganger
+ * gets to a established state due to how
+ * we resolve we could just overwrite the afi/safi
+ * activation.
+ */
+ other = peer->doppelganger;
+ if (other
+ && (other->status == OpenSent
+ || other->status == OpenConfirm)) {
+ other->last_reset = PEER_DOWN_AF_ACTIVATE;
+ bgp_notify_send(other, BGP_NOTIFY_CEASE,
+ BGP_NOTIFY_CEASE_CONFIG_CHANGE);
+ }
+ }
+
+ return 0;
+}
+
+/* Activate the peer or peer group for specified AFI and SAFI. */
+int peer_activate(struct peer *peer, afi_t afi, safi_t safi)
+{
+ int ret = 0;
+ struct peer_group *group;
+ struct listnode *node, *nnode;
+ struct peer *tmp_peer;
+ struct bgp *bgp;
+
+ /* Nothing to do if we've already activated this peer */
+ if (peer->afc[afi][safi])
+ return ret;
+
+ bgp = peer->bgp;
+
+ /* This is a peer-group so activate all of the members of the
+ * peer-group as well */
+ if (CHECK_FLAG(peer->sflags, PEER_STATUS_GROUP)) {
+
+ /* Do not activate a peer for both SAFI_UNICAST and
+ * SAFI_LABELED_UNICAST */
+ if ((safi == SAFI_UNICAST
+ && peer->afc[afi][SAFI_LABELED_UNICAST])
+ || (safi == SAFI_LABELED_UNICAST
+ && peer->afc[afi][SAFI_UNICAST]))
+ return BGP_ERR_PEER_SAFI_CONFLICT;
+
+ peer->afc[afi][safi] = 1;
+ group = peer->group;
+
+ for (ALL_LIST_ELEMENTS(group->peer, node, nnode, tmp_peer)) {
+ ret |= peer_activate_af(tmp_peer, afi, safi);
+ }
+ } else {
+ ret |= peer_activate_af(peer, afi, safi);
+ }
+
+ /* If this is the first peer to be activated for this
+ * afi/labeled-unicast recalc bestpaths to trigger label allocation */
+ if (ret != BGP_ERR_PEER_SAFI_CONFLICT && safi == SAFI_LABELED_UNICAST
+ && !bgp->allocate_mpls_labels[afi][SAFI_UNICAST]) {
+
+ if (BGP_DEBUG(zebra, ZEBRA))
+ zlog_debug(
+ "peer(s) are now active for labeled-unicast, allocate MPLS labels");
+
+ bgp->allocate_mpls_labels[afi][SAFI_UNICAST] = 1;
+ bgp_recalculate_afi_safi_bestpaths(bgp, afi, SAFI_UNICAST);
+ }
+
+ if (safi == SAFI_FLOWSPEC) {
+ /* connect to table manager */
+ bgp_zebra_init_tm_connect(bgp);
+ }
+ return ret;
+}
+
+static bool non_peergroup_deactivate_af(struct peer *peer, afi_t afi,
+ safi_t safi)
+{
+ if (CHECK_FLAG(peer->sflags, PEER_STATUS_GROUP)) {
+ flog_err(EC_BGP_PEER_GROUP, "%s was called for peer-group %s",
+ __func__, peer->host);
+ return true;
+ }
+
+ /* Nothing to do if we've already deactivated this peer */
+ if (!peer->afc[afi][safi])
+ return false;
+
+ /* De-activate the address family configuration. */
+ peer->afc[afi][safi] = 0;
+
+ if (peer_af_delete(peer, afi, safi) != 0) {
+ flog_err(EC_BGP_PEER_DELETE,
+ "couldn't delete af structure for peer %s(%s, %s)",
+ peer->host, afi2str(afi), safi2str(safi));
+ return true;
+ }
+
+ if (peer_established(peer)) {
+ if (CHECK_FLAG(peer->cap, PEER_CAP_DYNAMIC_RCV)) {
+ peer->afc_adv[afi][safi] = 0;
+ peer->afc_nego[afi][safi] = 0;
+
+ if (peer_active_nego(peer)) {
+ bgp_capability_send(peer, afi, safi,
+ CAPABILITY_CODE_MP,
+ CAPABILITY_ACTION_UNSET);
+ bgp_clear_route(peer, afi, safi);
+ peer->pcount[afi][safi] = 0;
+ } else {
+ peer->last_reset = PEER_DOWN_NEIGHBOR_DELETE;
+ bgp_notify_send(peer, BGP_NOTIFY_CEASE,
+ BGP_NOTIFY_CEASE_CONFIG_CHANGE);
+ }
+ } else {
+ peer->last_reset = PEER_DOWN_NEIGHBOR_DELETE;
+ bgp_notify_send(peer, BGP_NOTIFY_CEASE,
+ BGP_NOTIFY_CEASE_CONFIG_CHANGE);
+ }
+ }
+
+ return false;
+}
+
+int peer_deactivate(struct peer *peer, afi_t afi, safi_t safi)
+{
+ int ret = 0;
+ struct peer_group *group;
+ struct peer *tmp_peer;
+ struct listnode *node, *nnode;
+ struct bgp *bgp;
+
+ /* Nothing to do if we've already de-activated this peer */
+ if (!peer->afc[afi][safi])
+ return ret;
+
+ /* This is a peer-group so de-activate all of the members of the
+ * peer-group as well */
+ if (CHECK_FLAG(peer->sflags, PEER_STATUS_GROUP)) {
+ peer->afc[afi][safi] = 0;
+ group = peer->group;
+
+ for (ALL_LIST_ELEMENTS(group->peer, node, nnode, tmp_peer)) {
+ ret |= non_peergroup_deactivate_af(tmp_peer, afi, safi);
+ }
+ } else {
+ ret |= non_peergroup_deactivate_af(peer, afi, safi);
+ }
+
+ bgp = peer->bgp;
+
+ /* If this is the last peer to be deactivated for this
+ * afi/labeled-unicast recalc bestpaths to trigger label deallocation */
+ if (safi == SAFI_LABELED_UNICAST
+ && bgp->allocate_mpls_labels[afi][SAFI_UNICAST]
+ && !bgp_afi_safi_peer_exists(bgp, afi, safi)) {
+
+ if (BGP_DEBUG(zebra, ZEBRA))
+ zlog_debug(
+ "peer(s) are no longer active for labeled-unicast, deallocate MPLS labels");
+
+ bgp->allocate_mpls_labels[afi][SAFI_UNICAST] = 0;
+ bgp_recalculate_afi_safi_bestpaths(bgp, afi, SAFI_UNICAST);
+ }
+ return ret;
+}
+
+void peer_nsf_stop(struct peer *peer)
+{
+ afi_t afi;
+ safi_t safi;
+
+ UNSET_FLAG(peer->sflags, PEER_STATUS_NSF_WAIT);
+ UNSET_FLAG(peer->sflags, PEER_STATUS_NSF_MODE);
+
+ FOREACH_AFI_SAFI_NSF (afi, safi) {
+ peer->nsf[afi][safi] = 0;
+ THREAD_OFF(peer->t_llgr_stale[afi][safi]);
+ }
+
+ if (peer->t_gr_restart) {
+ THREAD_OFF(peer->t_gr_restart);
+ if (bgp_debug_neighbor_events(peer))
+ zlog_debug("%pBP graceful restart timer stopped", peer);
+ }
+ if (peer->t_gr_stale) {
+ THREAD_OFF(peer->t_gr_stale);
+ if (bgp_debug_neighbor_events(peer))
+ zlog_debug(
+ "%pBP graceful restart stalepath timer stopped",
+ peer);
+ }
+ bgp_clear_route_all(peer);
+}
+
+/* Delete peer from confguration.
+ *
+ * The peer is moved to a dead-end "Deleted" neighbour-state, to allow
+ * it to "cool off" and refcounts to hit 0, at which state it is freed.
+ *
+ * This function /should/ take care to be idempotent, to guard against
+ * it being called multiple times through stray events that come in
+ * that happen to result in this function being called again. That
+ * said, getting here for a "Deleted" peer is a bug in the neighbour
+ * FSM.
+ */
+int peer_delete(struct peer *peer)
+{
+ int i;
+ afi_t afi;
+ safi_t safi;
+ struct bgp *bgp;
+ struct bgp_filter *filter;
+ struct listnode *pn;
+ int accept_peer;
+
+ assert(peer->status != Deleted);
+
+ bgp = peer->bgp;
+ accept_peer = CHECK_FLAG(peer->sflags, PEER_STATUS_ACCEPT_PEER);
+
+ bgp_soft_reconfig_table_task_cancel(bgp, NULL, peer);
+
+ bgp_keepalives_off(peer);
+ bgp_reads_off(peer);
+ bgp_writes_off(peer);
+ assert(!CHECK_FLAG(peer->thread_flags, PEER_THREAD_WRITES_ON));
+ assert(!CHECK_FLAG(peer->thread_flags, PEER_THREAD_READS_ON));
+ assert(!CHECK_FLAG(peer->thread_flags, PEER_THREAD_KEEPALIVES_ON));
+
+ if (CHECK_FLAG(peer->sflags, PEER_STATUS_NSF_WAIT))
+ peer_nsf_stop(peer);
+
+ SET_FLAG(peer->flags, PEER_FLAG_DELETE);
+
+ /* Remove BFD settings. */
+ if (peer->bfd_config)
+ bgp_peer_remove_bfd_config(peer);
+
+ /* If this peer belongs to peer group, clear up the
+ relationship. */
+ if (peer->group) {
+ if (peer_dynamic_neighbor(peer))
+ peer_drop_dynamic_neighbor(peer);
+
+ if ((pn = listnode_lookup(peer->group->peer, peer))) {
+ peer = peer_unlock(
+ peer); /* group->peer list reference */
+ list_delete_node(peer->group->peer, pn);
+ }
+ peer->group = NULL;
+ }
+
+ /* Withdraw all information from routing table. We can not use
+ * BGP_EVENT_ADD (peer, BGP_Stop) at here. Because the event is
+ * executed after peer structure is deleted.
+ */
+ peer->last_reset = PEER_DOWN_NEIGHBOR_DELETE;
+ bgp_stop(peer);
+ UNSET_FLAG(peer->flags, PEER_FLAG_DELETE);
+
+ if (peer->doppelganger) {
+ peer->doppelganger->doppelganger = NULL;
+ peer->doppelganger = NULL;
+ }
+
+ UNSET_FLAG(peer->sflags, PEER_STATUS_ACCEPT_PEER);
+ bgp_fsm_change_status(peer, Deleted);
+
+ /* Remove from NHT */
+ if (CHECK_FLAG(peer->flags, PEER_FLAG_CONFIG_NODE))
+ bgp_unlink_nexthop_by_peer(peer);
+
+ /* Password configuration */
+ if (CHECK_FLAG(peer->flags, PEER_FLAG_PASSWORD)) {
+ XFREE(MTYPE_PEER_PASSWORD, peer->password);
+ if (!accept_peer && !BGP_PEER_SU_UNSPEC(peer)
+ && !CHECK_FLAG(peer->sflags, PEER_STATUS_GROUP)
+ && !CHECK_FLAG(peer->flags, PEER_FLAG_DYNAMIC_NEIGHBOR))
+ bgp_md5_unset(peer);
+ }
+
+ bgp_timer_set(peer); /* stops all timers for Deleted */
+
+ /* Delete from all peer list. */
+ if (!CHECK_FLAG(peer->sflags, PEER_STATUS_GROUP)
+ && (pn = listnode_lookup(bgp->peer, peer))) {
+ peer_unlock(peer); /* bgp peer list reference */
+ list_delete_node(bgp->peer, pn);
+ hash_release(bgp->peerhash, peer);
+ }
+
+ /* Buffers. */
+ if (peer->ibuf) {
+ stream_fifo_free(peer->ibuf);
+ peer->ibuf = NULL;
+ }
+
+ if (peer->obuf) {
+ stream_fifo_free(peer->obuf);
+ peer->obuf = NULL;
+ }
+
+ if (peer->ibuf_work) {
+ ringbuf_del(peer->ibuf_work);
+ peer->ibuf_work = NULL;
+ }
+
+ if (peer->obuf_work) {
+ stream_free(peer->obuf_work);
+ peer->obuf_work = NULL;
+ }
+
+ if (peer->scratch) {
+ stream_free(peer->scratch);
+ peer->scratch = NULL;
+ }
+
+ /* Local and remote addresses. */
+ if (peer->su_local) {
+ sockunion_free(peer->su_local);
+ peer->su_local = NULL;
+ }
+
+ if (peer->su_remote) {
+ sockunion_free(peer->su_remote);
+ peer->su_remote = NULL;
+ }
+
+ /* Free filter related memory. */
+ FOREACH_AFI_SAFI (afi, safi) {
+ filter = &peer->filter[afi][safi];
+
+ for (i = FILTER_IN; i < FILTER_MAX; i++) {
+ XFREE(MTYPE_BGP_FILTER_NAME, filter->dlist[i].name);
+ XFREE(MTYPE_BGP_FILTER_NAME, filter->plist[i].name);
+ XFREE(MTYPE_BGP_FILTER_NAME, filter->aslist[i].name);
+ }
+
+ for (i = RMAP_IN; i < RMAP_MAX; i++) {
+ XFREE(MTYPE_BGP_FILTER_NAME, filter->map[i].name);
+ }
+
+ XFREE(MTYPE_BGP_FILTER_NAME, filter->usmap.name);
+ XFREE(MTYPE_ROUTE_MAP_NAME, peer->default_rmap[afi][safi].name);
+ ecommunity_free(&peer->soo[afi][safi]);
+ }
+
+ FOREACH_AFI_SAFI (afi, safi)
+ peer_af_delete(peer, afi, safi);
+
+ XFREE(MTYPE_BGP_PEER_HOST, peer->hostname);
+ XFREE(MTYPE_BGP_PEER_HOST, peer->domainname);
+
+ peer_unlock(peer); /* initial reference */
+
+ return 0;
+}
+
+static int peer_group_cmp(struct peer_group *g1, struct peer_group *g2)
+{
+ return strcmp(g1->name, g2->name);
+}
+
+/* Peer group cofiguration. */
+static struct peer_group *peer_group_new(void)
+{
+ return XCALLOC(MTYPE_PEER_GROUP, sizeof(struct peer_group));
+}
+
+static void peer_group_free(struct peer_group *group)
+{
+ XFREE(MTYPE_PEER_GROUP, group);
+}
+
+struct peer_group *peer_group_lookup(struct bgp *bgp, const char *name)
+{
+ struct peer_group *group;
+ struct listnode *node, *nnode;
+
+ for (ALL_LIST_ELEMENTS(bgp->group, node, nnode, group)) {
+ if (strcmp(group->name, name) == 0)
+ return group;
+ }
+ return NULL;
+}
+
+struct peer_group *peer_group_get(struct bgp *bgp, const char *name)
+{
+ struct peer_group *group;
+ afi_t afi;
+ safi_t safi;
+
+ group = peer_group_lookup(bgp, name);
+ if (group)
+ return group;
+
+ group = peer_group_new();
+ group->bgp = bgp;
+ XFREE(MTYPE_PEER_GROUP_HOST, group->name);
+ group->name = XSTRDUP(MTYPE_PEER_GROUP_HOST, name);
+ group->peer = list_new();
+ for (afi = AFI_IP; afi < AFI_MAX; afi++)
+ group->listen_range[afi] = list_new();
+ group->conf = peer_new(bgp);
+ FOREACH_AFI_SAFI (afi, safi) {
+ if (bgp->default_af[afi][safi])
+ group->conf->afc[afi][safi] = 1;
+ }
+ XFREE(MTYPE_BGP_PEER_HOST, group->conf->host);
+ group->conf->host = XSTRDUP(MTYPE_BGP_PEER_HOST, name);
+ group->conf->group = group;
+ group->conf->as = 0;
+ group->conf->ttl = BGP_DEFAULT_TTL;
+ group->conf->gtsm_hops = BGP_GTSM_HOPS_DISABLED;
+ group->conf->v_routeadv = BGP_DEFAULT_EBGP_ROUTEADV;
+ SET_FLAG(group->conf->sflags, PEER_STATUS_GROUP);
+ listnode_add_sort(bgp->group, group);
+
+ return group;
+}
+
+static void peer_group2peer_config_copy(struct peer_group *group,
+ struct peer *peer)
+{
+ uint32_t flags_tmp;
+ struct peer *conf;
+
+ conf = group->conf;
+
+ /* remote-as */
+ if (conf->as)
+ peer->as = conf->as;
+
+ /* local-as */
+ if (!CHECK_FLAG(peer->flags_override, PEER_FLAG_LOCAL_AS))
+ peer->change_local_as = conf->change_local_as;
+
+ /* If peer-group has configured TTL then override it */
+ if (conf->ttl != BGP_DEFAULT_TTL)
+ peer->ttl = conf->ttl;
+
+ /* GTSM hops */
+ peer->gtsm_hops = conf->gtsm_hops;
+
+ /* peer flags apply */
+ flags_tmp = conf->flags & ~peer->flags_override;
+ flags_tmp ^= conf->flags_invert ^ peer->flags_invert;
+ flags_tmp &= ~peer->flags_override;
+
+ UNSET_FLAG(peer->flags, ~peer->flags_override);
+ SET_FLAG(peer->flags, flags_tmp);
+ SET_FLAG(peer->flags_invert, conf->flags_invert);
+
+ /* peer timers apply */
+ if (!CHECK_FLAG(peer->flags_override, PEER_FLAG_TIMER)) {
+ PEER_ATTR_INHERIT(peer, group, holdtime);
+ PEER_ATTR_INHERIT(peer, group, keepalive);
+ }
+
+ if (!CHECK_FLAG(peer->flags_override, PEER_FLAG_TIMER_CONNECT)) {
+ PEER_ATTR_INHERIT(peer, group, connect);
+ if (CHECK_FLAG(conf->flags, PEER_FLAG_TIMER_CONNECT))
+ peer->v_connect = conf->connect;
+ else
+ peer->v_connect = peer->bgp->default_connect_retry;
+ }
+
+ if (!CHECK_FLAG(peer->flags_override, PEER_FLAG_TIMER_DELAYOPEN)) {
+ PEER_ATTR_INHERIT(peer, group, delayopen);
+ if (CHECK_FLAG(conf->flags, PEER_FLAG_TIMER_DELAYOPEN))
+ peer->v_delayopen = conf->delayopen;
+ else
+ peer->v_delayopen = peer->bgp->default_delayopen;
+ }
+
+ /* advertisement-interval apply */
+ if (!CHECK_FLAG(peer->flags_override, PEER_FLAG_ROUTEADV)) {
+ PEER_ATTR_INHERIT(peer, group, routeadv);
+ if (CHECK_FLAG(conf->flags, PEER_FLAG_ROUTEADV))
+ peer->v_routeadv = conf->routeadv;
+ else
+ peer->v_routeadv = (peer_sort(peer) == BGP_PEER_IBGP)
+ ? BGP_DEFAULT_IBGP_ROUTEADV
+ : BGP_DEFAULT_EBGP_ROUTEADV;
+ }
+
+ /* capability extended-nexthop apply */
+ if (!CHECK_FLAG(peer->flags_override, PEER_FLAG_CAPABILITY_ENHE))
+ if (CHECK_FLAG(conf->flags, PEER_FLAG_CAPABILITY_ENHE))
+ SET_FLAG(peer->flags, PEER_FLAG_CAPABILITY_ENHE);
+
+ /* password apply */
+ if (!CHECK_FLAG(peer->flags_override, PEER_FLAG_PASSWORD))
+ PEER_STR_ATTR_INHERIT(peer, group, password,
+ MTYPE_PEER_PASSWORD);
+
+ if (!BGP_PEER_SU_UNSPEC(peer))
+ bgp_md5_set(peer);
+
+ /* update-source apply */
+ if (!CHECK_FLAG(peer->flags_override, PEER_FLAG_UPDATE_SOURCE)) {
+ if (conf->update_source) {
+ XFREE(MTYPE_PEER_UPDATE_SOURCE, peer->update_if);
+ PEER_SU_ATTR_INHERIT(peer, group, update_source);
+ } else if (conf->update_if) {
+ sockunion_free(peer->update_source);
+ PEER_STR_ATTR_INHERIT(peer, group, update_if,
+ MTYPE_PEER_UPDATE_SOURCE);
+ }
+ }
+
+ /* role */
+ PEER_ATTR_INHERIT(peer, group, local_role);
+
+ /* Update GR flags for the peer. */
+ bgp_peer_gr_flags_update(peer);
+
+ /* Apply BFD settings from group to peer if it exists. */
+ if (conf->bfd_config) {
+ bgp_peer_configure_bfd(peer, false);
+ bgp_peer_config_apply(peer, group);
+ }
+}
+
+/* Peer group's remote AS configuration. */
+int peer_group_remote_as(struct bgp *bgp, const char *group_name, as_t *as,
+ int as_type)
+{
+ struct peer_group *group;
+ struct peer *peer;
+ struct listnode *node, *nnode;
+
+ group = peer_group_lookup(bgp, group_name);
+ if (!group)
+ return -1;
+
+ if ((as_type == group->conf->as_type) && (group->conf->as == *as))
+ return 0;
+
+
+ /* When we setup peer-group AS number all peer group member's AS
+ number must be updated to same number. */
+ peer_as_change(group->conf, *as, as_type);
+
+ for (ALL_LIST_ELEMENTS(group->peer, node, nnode, peer)) {
+ if (((peer->as_type == AS_SPECIFIED) && peer->as != *as)
+ || (peer->as_type != as_type))
+ peer_as_change(peer, *as, as_type);
+ }
+
+ return 0;
+}
+
+void peer_notify_unconfig(struct peer *peer)
+{
+ if (BGP_IS_VALID_STATE_FOR_NOTIF(peer->status))
+ bgp_notify_send(peer, BGP_NOTIFY_CEASE,
+ BGP_NOTIFY_CEASE_PEER_UNCONFIG);
+}
+
+static void peer_notify_shutdown(struct peer *peer)
+{
+ if (BGP_PEER_GRACEFUL_RESTART_CAPABLE(peer)) {
+ if (bgp_debug_neighbor_events(peer))
+ zlog_debug(
+ "%pBP configured Graceful-Restart, skipping shutdown notification",
+ peer);
+ return;
+ }
+
+ if (BGP_IS_VALID_STATE_FOR_NOTIF(peer->status))
+ bgp_notify_send(peer, BGP_NOTIFY_CEASE,
+ BGP_NOTIFY_CEASE_ADMIN_SHUTDOWN);
+}
+
+void peer_group_notify_unconfig(struct peer_group *group)
+{
+ struct peer *peer, *other;
+ struct listnode *node, *nnode;
+
+ for (ALL_LIST_ELEMENTS(group->peer, node, nnode, peer)) {
+ other = peer->doppelganger;
+ if (other && other->status != Deleted) {
+ other->group = NULL;
+ peer_notify_unconfig(other);
+ } else
+ peer_notify_unconfig(peer);
+ }
+}
+
+int peer_group_delete(struct peer_group *group)
+{
+ struct bgp *bgp;
+ struct peer *peer;
+ struct prefix *prefix;
+ struct peer *other;
+ struct listnode *node, *nnode;
+ afi_t afi;
+
+ bgp = group->bgp;
+
+ for (ALL_LIST_ELEMENTS(group->peer, node, nnode, peer)) {
+ other = peer->doppelganger;
+
+ if (CHECK_FLAG(peer->flags, PEER_FLAG_CAPABILITY_ENHE))
+ bgp_zebra_terminate_radv(bgp, peer);
+
+ peer_delete(peer);
+ if (other && other->status != Deleted) {
+ other->group = NULL;
+ peer_delete(other);
+ }
+ }
+ list_delete(&group->peer);
+
+ for (afi = AFI_IP; afi < AFI_MAX; afi++) {
+ for (ALL_LIST_ELEMENTS(group->listen_range[afi], node, nnode,
+ prefix)) {
+ prefix_free(&prefix);
+ }
+ list_delete(&group->listen_range[afi]);
+ }
+
+ XFREE(MTYPE_PEER_GROUP_HOST, group->name);
+ group->name = NULL;
+
+ if (group->conf->bfd_config)
+ bgp_peer_remove_bfd_config(group->conf);
+
+ group->conf->group = NULL;
+ peer_delete(group->conf);
+
+ /* Delete from all peer_group list. */
+ listnode_delete(bgp->group, group);
+
+ peer_group_free(group);
+
+ return 0;
+}
+
+int peer_group_remote_as_delete(struct peer_group *group)
+{
+ struct peer *peer, *other;
+ struct listnode *node, *nnode;
+
+ if ((group->conf->as_type == AS_UNSPECIFIED)
+ || ((!group->conf->as) && (group->conf->as_type == AS_SPECIFIED)))
+ return 0;
+
+ for (ALL_LIST_ELEMENTS(group->peer, node, nnode, peer)) {
+ other = peer->doppelganger;
+
+ if (CHECK_FLAG(peer->flags, PEER_FLAG_CAPABILITY_ENHE))
+ bgp_zebra_terminate_radv(peer->bgp, peer);
+
+ peer_delete(peer);
+
+ if (other && other->status != Deleted) {
+ other->group = NULL;
+ peer_delete(other);
+ }
+ }
+ list_delete_all_node(group->peer);
+
+ group->conf->as = 0;
+ group->conf->as_type = AS_UNSPECIFIED;
+
+ return 0;
+}
+
+int peer_group_listen_range_add(struct peer_group *group, struct prefix *range)
+{
+ struct prefix *prefix;
+ struct listnode *node, *nnode;
+ afi_t afi;
+
+ afi = family2afi(range->family);
+
+ /* Group needs remote AS configured. */
+ if (group->conf->as_type == AS_UNSPECIFIED)
+ return BGP_ERR_PEER_GROUP_NO_REMOTE_AS;
+
+ /* Ensure no duplicates. Currently we don't care about overlaps. */
+ for (ALL_LIST_ELEMENTS(group->listen_range[afi], node, nnode, prefix)) {
+ if (prefix_same(range, prefix))
+ return 0;
+ }
+
+ prefix = prefix_new();
+ prefix_copy(prefix, range);
+ listnode_add(group->listen_range[afi], prefix);
+
+ /* Update passwords for new ranges */
+ if (group->conf->password)
+ bgp_md5_set_prefix(group->bgp, prefix, group->conf->password);
+
+ return 0;
+}
+
+int peer_group_listen_range_del(struct peer_group *group, struct prefix *range)
+{
+ struct prefix *prefix, prefix2;
+ struct listnode *node, *nnode;
+ struct peer *peer;
+ afi_t afi;
+
+ afi = family2afi(range->family);
+
+ /* Identify the listen range. */
+ for (ALL_LIST_ELEMENTS(group->listen_range[afi], node, nnode, prefix)) {
+ if (prefix_same(range, prefix))
+ break;
+ }
+
+ if (!prefix)
+ return BGP_ERR_DYNAMIC_NEIGHBORS_RANGE_NOT_FOUND;
+
+ /* Dispose off any dynamic neighbors that exist due to this listen range
+ */
+ for (ALL_LIST_ELEMENTS(group->peer, node, nnode, peer)) {
+ if (!peer_dynamic_neighbor(peer))
+ continue;
+
+ if (sockunion2hostprefix(&peer->su, &prefix2)
+ && prefix_match(prefix, &prefix2)) {
+ if (bgp_debug_neighbor_events(peer))
+ zlog_debug(
+ "Deleting dynamic neighbor %s group %s upon delete of listen range %pFX",
+ peer->host, group->name, prefix);
+ peer_delete(peer);
+ }
+ }
+
+ /* Get rid of the listen range */
+ listnode_delete(group->listen_range[afi], prefix);
+
+ /* Remove passwords for deleted ranges */
+ if (group->conf->password)
+ bgp_md5_unset_prefix(group->bgp, prefix);
+
+ return 0;
+}
+
+/* Bind specified peer to peer group. */
+int peer_group_bind(struct bgp *bgp, union sockunion *su, struct peer *peer,
+ struct peer_group *group, as_t *as)
+{
+ int first_member = 0;
+ afi_t afi;
+ safi_t safi;
+ enum bgp_peer_sort ptype, gtype;
+
+ /* Lookup the peer. */
+ if (!peer)
+ peer = peer_lookup(bgp, su);
+
+ /* The peer exist, bind it to the peer-group */
+ if (peer) {
+ /* When the peer already belongs to a peer-group, check the
+ * consistency. */
+ if (peer_group_active(peer)) {
+
+ /* The peer is already bound to the peer-group,
+ * nothing to do
+ */
+ if (strcmp(peer->group->name, group->name) == 0)
+ return 0;
+ else
+ return BGP_ERR_PEER_GROUP_CANT_CHANGE;
+ }
+
+ /* The peer has not specified a remote-as, inherit it from the
+ * peer-group */
+ if (peer->as_type == AS_UNSPECIFIED) {
+ peer->as_type = group->conf->as_type;
+ peer->as = group->conf->as;
+ peer->sort = group->conf->sort;
+ }
+
+ ptype = peer_sort(peer);
+ if (!group->conf->as && ptype != BGP_PEER_UNSPECIFIED) {
+ gtype = peer_sort(group->conf);
+ if ((gtype != BGP_PEER_INTERNAL) && (gtype != ptype)) {
+ if (as)
+ *as = peer->as;
+ return BGP_ERR_PEER_GROUP_PEER_TYPE_DIFFERENT;
+ }
+
+ if (gtype == BGP_PEER_INTERNAL)
+ first_member = 1;
+ }
+
+ peer_group2peer_config_copy(group, peer);
+
+ FOREACH_AFI_SAFI (afi, safi) {
+ if (group->conf->afc[afi][safi]) {
+ peer->afc[afi][safi] = 1;
+
+ if (peer_af_find(peer, afi, safi)
+ || peer_af_create(peer, afi, safi)) {
+ peer_group2peer_config_copy_af(
+ group, peer, afi, safi);
+ }
+ } else if (peer->afc[afi][safi])
+ peer_deactivate(peer, afi, safi);
+ }
+
+ if (peer->group) {
+ assert(group && peer->group == group);
+ } else {
+ listnode_delete(bgp->peer, peer);
+
+ peer->group = group;
+ listnode_add_sort(bgp->peer, peer);
+
+ peer = peer_lock(peer); /* group->peer list reference */
+ listnode_add(group->peer, peer);
+ }
+
+ if (first_member) {
+ gtype = peer_sort(group->conf);
+ /* Advertisement-interval reset */
+ if (!CHECK_FLAG(group->conf->flags,
+ PEER_FLAG_ROUTEADV)) {
+ group->conf->v_routeadv =
+ (gtype == BGP_PEER_IBGP)
+ ? BGP_DEFAULT_IBGP_ROUTEADV
+ : BGP_DEFAULT_EBGP_ROUTEADV;
+ }
+
+ /* ebgp-multihop reset */
+ if (gtype == BGP_PEER_IBGP)
+ group->conf->ttl = MAXTTL;
+
+ /* local-as reset */
+ if (gtype != BGP_PEER_EBGP) {
+ group->conf->change_local_as = 0;
+ peer_flag_unset(group->conf,
+ PEER_FLAG_LOCAL_AS);
+ peer_flag_unset(group->conf,
+ PEER_FLAG_LOCAL_AS_NO_PREPEND);
+ peer_flag_unset(group->conf,
+ PEER_FLAG_LOCAL_AS_REPLACE_AS);
+ }
+ }
+
+ SET_FLAG(peer->flags, PEER_FLAG_CONFIG_NODE);
+
+ if (BGP_IS_VALID_STATE_FOR_NOTIF(peer->status)) {
+ peer->last_reset = PEER_DOWN_RMAP_BIND;
+ bgp_notify_send(peer, BGP_NOTIFY_CEASE,
+ BGP_NOTIFY_CEASE_CONFIG_CHANGE);
+ } else {
+ bgp_session_reset(peer);
+ }
+ }
+
+ /* Create a new peer. */
+ else {
+ if ((group->conf->as_type == AS_SPECIFIED)
+ && (!group->conf->as)) {
+ return BGP_ERR_PEER_GROUP_NO_REMOTE_AS;
+ }
+
+ peer = peer_create(su, NULL, bgp, bgp->as, group->conf->as,
+ group->conf->as_type, group);
+
+ peer = peer_lock(peer); /* group->peer list reference */
+ listnode_add(group->peer, peer);
+
+ peer_group2peer_config_copy(group, peer);
+
+ /* If the peer-group is active for this afi/safi then activate
+ * for this peer */
+ FOREACH_AFI_SAFI (afi, safi) {
+ if (group->conf->afc[afi][safi]) {
+ peer->afc[afi][safi] = 1;
+
+ if (!peer_af_find(peer, afi, safi))
+ peer_af_create(peer, afi, safi);
+
+ peer_group2peer_config_copy_af(group, peer, afi,
+ safi);
+ } else if (peer->afc[afi][safi])
+ peer_deactivate(peer, afi, safi);
+ }
+
+ SET_FLAG(peer->flags, PEER_FLAG_CONFIG_NODE);
+
+ /* Set up peer's events and timers. */
+ if (peer_active(peer))
+ bgp_timer_set(peer);
+ }
+
+ return 0;
+}
+
+static void bgp_startup_timer_expire(struct thread *thread)
+{
+ struct bgp *bgp;
+
+ bgp = THREAD_ARG(thread);
+ bgp->t_startup = NULL;
+}
+
+/*
+ * On shutdown we call the cleanup function which
+ * does a free of the link list nodes, free up
+ * the data we are pointing at too.
+ */
+static void bgp_vrf_string_name_delete(void *data)
+{
+ char *vname = data;
+
+ XFREE(MTYPE_TMP, vname);
+}
+
+/* BGP instance creation by `router bgp' commands. */
+static struct bgp *bgp_create(as_t *as, const char *name,
+ enum bgp_instance_type inst_type)
+{
+ struct bgp *bgp;
+ afi_t afi;
+ safi_t safi;
+
+ bgp = XCALLOC(MTYPE_BGP, sizeof(struct bgp));
+
+ if (BGP_DEBUG(zebra, ZEBRA)) {
+ if (inst_type == BGP_INSTANCE_TYPE_DEFAULT)
+ zlog_debug("Creating Default VRF, AS %u", *as);
+ else
+ zlog_debug("Creating %s %s, AS %u",
+ (inst_type == BGP_INSTANCE_TYPE_VRF)
+ ? "VRF"
+ : "VIEW",
+ name, *as);
+ }
+
+ /* Default the EVPN VRF to the default one */
+ if (inst_type == BGP_INSTANCE_TYPE_DEFAULT && !bgp_master.bgp_evpn) {
+ bgp_lock(bgp);
+ bm->bgp_evpn = bgp;
+ }
+
+ bgp_lock(bgp);
+
+ bgp->allow_martian = false;
+ bgp_process_queue_init(bgp);
+ bgp->heuristic_coalesce = true;
+ bgp->inst_type = inst_type;
+ bgp->vrf_id = (inst_type == BGP_INSTANCE_TYPE_DEFAULT) ? VRF_DEFAULT
+ : VRF_UNKNOWN;
+ bgp->peer_self = peer_new(bgp);
+ XFREE(MTYPE_BGP_PEER_HOST, bgp->peer_self->host);
+ bgp->peer_self->host =
+ XSTRDUP(MTYPE_BGP_PEER_HOST, "Static announcement");
+ XFREE(MTYPE_BGP_PEER_HOST, bgp->peer_self->hostname);
+ if (cmd_hostname_get())
+ bgp->peer_self->hostname =
+ XSTRDUP(MTYPE_BGP_PEER_HOST, cmd_hostname_get());
+
+ XFREE(MTYPE_BGP_PEER_HOST, bgp->peer_self->domainname);
+ if (cmd_domainname_get())
+ bgp->peer_self->domainname =
+ XSTRDUP(MTYPE_BGP_PEER_HOST, cmd_domainname_get());
+ bgp->peer = list_new();
+ bgp->peer->cmp = (int (*)(void *, void *))peer_cmp;
+ bgp->peerhash = hash_create(peer_hash_key_make, peer_hash_same,
+ "BGP Peer Hash");
+ bgp->peerhash->max_size = BGP_PEER_MAX_HASH_SIZE;
+
+ bgp->group = list_new();
+ bgp->group->cmp = (int (*)(void *, void *))peer_group_cmp;
+
+ FOREACH_AFI_SAFI (afi, safi) {
+ bgp->route[afi][safi] = bgp_table_init(bgp, afi, safi);
+ bgp->aggregate[afi][safi] = bgp_table_init(bgp, afi, safi);
+ bgp->rib[afi][safi] = bgp_table_init(bgp, afi, safi);
+
+ /* Enable maximum-paths */
+ bgp_maximum_paths_set(bgp, afi, safi, BGP_PEER_EBGP,
+ multipath_num, 0);
+ bgp_maximum_paths_set(bgp, afi, safi, BGP_PEER_IBGP,
+ multipath_num, 0);
+ /* Initialize graceful restart info */
+ bgp->gr_info[afi][safi].eor_required = 0;
+ bgp->gr_info[afi][safi].eor_received = 0;
+ bgp->gr_info[afi][safi].t_select_deferral = NULL;
+ bgp->gr_info[afi][safi].t_route_select = NULL;
+ bgp->gr_info[afi][safi].gr_deferred = 0;
+ }
+
+ bgp->v_update_delay = bm->v_update_delay;
+ bgp->v_establish_wait = bm->v_establish_wait;
+ bgp->default_local_pref = BGP_DEFAULT_LOCAL_PREF;
+ bgp->default_subgroup_pkt_queue_max =
+ BGP_DEFAULT_SUBGROUP_PKT_QUEUE_MAX;
+ bgp_timers_unset(bgp);
+ bgp->default_min_holdtime = 0;
+ bgp->restart_time = BGP_DEFAULT_RESTART_TIME;
+ bgp->stalepath_time = BGP_DEFAULT_STALEPATH_TIME;
+ bgp->select_defer_time = BGP_DEFAULT_SELECT_DEFERRAL_TIME;
+ bgp->rib_stale_time = BGP_DEFAULT_RIB_STALE_TIME;
+ bgp->dynamic_neighbors_limit = BGP_DYNAMIC_NEIGHBORS_LIMIT_DEFAULT;
+ bgp->dynamic_neighbors_count = 0;
+ bgp->lb_ref_bw = BGP_LINK_BW_REF_BW;
+ bgp->lb_handling = BGP_LINK_BW_ECMP;
+ bgp->reject_as_sets = false;
+ bgp->condition_check_period = DEFAULT_CONDITIONAL_ROUTES_POLL_TIME;
+ bgp_addpath_init_bgp_data(&bgp->tx_addpath);
+ bgp->fast_convergence = false;
+ bgp->as = *as;
+ bgp->llgr_stale_time = BGP_DEFAULT_LLGR_STALE_TIME;
+
+#ifdef ENABLE_BGP_VNC
+ if (inst_type != BGP_INSTANCE_TYPE_VRF) {
+ bgp->rfapi = bgp_rfapi_new(bgp);
+ assert(bgp->rfapi);
+ assert(bgp->rfapi_cfg);
+ }
+#endif /* ENABLE_BGP_VNC */
+
+ for (afi = AFI_IP; afi < AFI_MAX; afi++) {
+ bgp->vpn_policy[afi].bgp = bgp;
+ bgp->vpn_policy[afi].afi = afi;
+ bgp->vpn_policy[afi].tovpn_label = MPLS_LABEL_NONE;
+ bgp->vpn_policy[afi].tovpn_zebra_vrf_label_last_sent =
+ MPLS_LABEL_NONE;
+
+ bgp->vpn_policy[afi].import_vrf = list_new();
+ bgp->vpn_policy[afi].import_vrf->del =
+ bgp_vrf_string_name_delete;
+ bgp->vpn_policy[afi].export_vrf = list_new();
+ bgp->vpn_policy[afi].export_vrf->del =
+ bgp_vrf_string_name_delete;
+ SET_FLAG(bgp->af_flags[afi][SAFI_MPLS_VPN],
+ BGP_VPNVX_RETAIN_ROUTE_TARGET_ALL);
+ }
+ if (name)
+ bgp->name = XSTRDUP(MTYPE_BGP, name);
+
+ thread_add_timer(bm->master, bgp_startup_timer_expire, bgp,
+ bgp->restart_time, &bgp->t_startup);
+
+ /* printable name we can use in debug messages */
+ if (inst_type == BGP_INSTANCE_TYPE_DEFAULT) {
+ bgp->name_pretty = XSTRDUP(MTYPE_BGP, "VRF default");
+ } else {
+ const char *n;
+ int len;
+
+ if (bgp->name)
+ n = bgp->name;
+ else
+ n = "?";
+
+ len = 4 + 1 + strlen(n) + 1; /* "view foo\0" */
+
+ bgp->name_pretty = XCALLOC(MTYPE_BGP, len);
+ snprintf(bgp->name_pretty, len, "%s %s",
+ (bgp->inst_type == BGP_INSTANCE_TYPE_VRF)
+ ? "VRF"
+ : "VIEW",
+ n);
+ }
+
+ atomic_store_explicit(&bgp->wpkt_quanta, BGP_WRITE_PACKET_MAX,
+ memory_order_relaxed);
+ atomic_store_explicit(&bgp->rpkt_quanta, BGP_READ_PACKET_MAX,
+ memory_order_relaxed);
+ bgp->coalesce_time = BGP_DEFAULT_SUBGROUP_COALESCE_TIME;
+ bgp->default_af[AFI_IP][SAFI_UNICAST] = true;
+
+ QOBJ_REG(bgp, bgp);
+
+ update_bgp_group_init(bgp);
+
+ /* assign a unique rd id for auto derivation of vrf's RD */
+ bf_assign_index(bm->rd_idspace, bgp->vrf_rd_id);
+
+ bgp->evpn_info = XCALLOC(MTYPE_BGP_EVPN_INFO,
+ sizeof(struct bgp_evpn_info));
+ bgp_evpn_init(bgp);
+ bgp_evpn_vrf_es_init(bgp);
+ bgp_pbr_init(bgp);
+ bgp_srv6_init(bgp);
+
+ /*initilize global GR FSM */
+ bgp_global_gr_init(bgp);
+
+ memset(&bgp->ebgprequirespolicywarning, 0,
+ sizeof(bgp->ebgprequirespolicywarning));
+
+ return bgp;
+}
+
+/* Return the "default VRF" instance of BGP. */
+struct bgp *bgp_get_default(void)
+{
+ struct bgp *bgp;
+ struct listnode *node, *nnode;
+
+ for (ALL_LIST_ELEMENTS(bm->bgp, node, nnode, bgp))
+ if (bgp->inst_type == BGP_INSTANCE_TYPE_DEFAULT)
+ return bgp;
+ return NULL;
+}
+
+/* Lookup BGP entry. */
+struct bgp *bgp_lookup(as_t as, const char *name)
+{
+ struct bgp *bgp;
+ struct listnode *node, *nnode;
+
+ for (ALL_LIST_ELEMENTS(bm->bgp, node, nnode, bgp))
+ if (bgp->as == as
+ && ((bgp->name == NULL && name == NULL)
+ || (bgp->name && name && strcmp(bgp->name, name) == 0)))
+ return bgp;
+ return NULL;
+}
+
+/* Lookup BGP structure by view name. */
+struct bgp *bgp_lookup_by_name(const char *name)
+{
+ struct bgp *bgp;
+ struct listnode *node, *nnode;
+
+ for (ALL_LIST_ELEMENTS(bm->bgp, node, nnode, bgp))
+ if ((bgp->name == NULL && name == NULL)
+ || (bgp->name && name && strcmp(bgp->name, name) == 0))
+ return bgp;
+ return NULL;
+}
+
+/* Lookup BGP instance based on VRF id. */
+/* Note: Only to be used for incoming messages from Zebra. */
+struct bgp *bgp_lookup_by_vrf_id(vrf_id_t vrf_id)
+{
+ struct vrf *vrf;
+
+ /* Lookup VRF (in tree) and follow link. */
+ vrf = vrf_lookup_by_id(vrf_id);
+ if (!vrf)
+ return NULL;
+ return (vrf->info) ? (struct bgp *)vrf->info : NULL;
+}
+
+/* Sets the BGP instance where EVPN is enabled */
+void bgp_set_evpn(struct bgp *bgp)
+{
+ if (bm->bgp_evpn == bgp)
+ return;
+
+ /* First, release the reference count we hold on the instance */
+ if (bm->bgp_evpn)
+ bgp_unlock(bm->bgp_evpn);
+
+ bm->bgp_evpn = bgp;
+
+ /* Increase the reference count on this new VRF */
+ if (bm->bgp_evpn)
+ bgp_lock(bm->bgp_evpn);
+}
+
+/* Returns the BGP instance where EVPN is enabled, if any */
+struct bgp *bgp_get_evpn(void)
+{
+ return bm->bgp_evpn;
+}
+
+/* handle socket creation or deletion, if necessary
+ * this is called for all new BGP instances
+ */
+int bgp_handle_socket(struct bgp *bgp, struct vrf *vrf, vrf_id_t old_vrf_id,
+ bool create)
+{
+ struct listnode *node;
+ char *address;
+
+ /* Create BGP server socket, if listen mode not disabled */
+ if (!bgp || bgp_option_check(BGP_OPT_NO_LISTEN))
+ return 0;
+ if (bgp->inst_type == BGP_INSTANCE_TYPE_VRF) {
+ /*
+ * suppress vrf socket
+ */
+ if (!create) {
+ bgp_close_vrf_socket(bgp);
+ return 0;
+ }
+ if (vrf == NULL)
+ return BGP_ERR_INVALID_VALUE;
+ /* do nothing
+ * if vrf_id did not change
+ */
+ if (vrf->vrf_id == old_vrf_id)
+ return 0;
+ if (old_vrf_id != VRF_UNKNOWN) {
+ /* look for old socket. close it. */
+ bgp_close_vrf_socket(bgp);
+ }
+ /* if backend is not yet identified ( VRF_UNKNOWN) then
+ * creation will be done later
+ */
+ if (vrf->vrf_id == VRF_UNKNOWN)
+ return 0;
+ if (list_isempty(bm->addresses)) {
+ if (bgp_socket(bgp, bm->port, NULL) < 0)
+ return BGP_ERR_INVALID_VALUE;
+ } else {
+ for (ALL_LIST_ELEMENTS_RO(bm->addresses, node, address))
+ if (bgp_socket(bgp, bm->port, address) < 0)
+ return BGP_ERR_INVALID_VALUE;
+ }
+ return 0;
+ } else
+ return bgp_check_main_socket(create, bgp);
+}
+
+int bgp_lookup_by_as_name_type(struct bgp **bgp_val, as_t *as, const char *name,
+ enum bgp_instance_type inst_type)
+{
+ struct bgp *bgp;
+
+ /* Multiple instance check. */
+ if (name)
+ bgp = bgp_lookup_by_name(name);
+ else
+ bgp = bgp_get_default();
+
+ if (bgp) {
+ *bgp_val = bgp;
+ if (bgp->as != *as) {
+ *as = bgp->as;
+ return BGP_ERR_AS_MISMATCH;
+ }
+ if (bgp->inst_type != inst_type)
+ return BGP_ERR_INSTANCE_MISMATCH;
+ return BGP_SUCCESS;
+ }
+ *bgp_val = NULL;
+
+ return BGP_SUCCESS;
+}
+
+/* Called from VTY commands. */
+int bgp_get(struct bgp **bgp_val, as_t *as, const char *name,
+ enum bgp_instance_type inst_type)
+{
+ struct bgp *bgp;
+ struct vrf *vrf = NULL;
+ int ret = 0;
+
+ ret = bgp_lookup_by_as_name_type(bgp_val, as, name, inst_type);
+ if (ret || *bgp_val)
+ return ret;
+
+ bgp = bgp_create(as, name, inst_type);
+
+ /*
+ * view instances will never work inside of a vrf
+ * as such they must always be in the VRF_DEFAULT
+ * Also we must set this to something useful because
+ * of the vrf socket code needing an actual useful
+ * default value to send to the underlying OS.
+ *
+ * This code is currently ignoring vrf based
+ * code using the -Z option( and that is probably
+ * best addressed elsewhere in the code )
+ */
+ if (inst_type == BGP_INSTANCE_TYPE_VIEW)
+ bgp->vrf_id = VRF_DEFAULT;
+
+ bgp_router_id_set(bgp, &bgp->router_id_zebra, true);
+ bgp_address_init(bgp);
+ bgp_tip_hash_init(bgp);
+ bgp_scan_init(bgp);
+ *bgp_val = bgp;
+
+ bgp->t_rmap_def_originate_eval = NULL;
+
+ /* If Default instance or VRF, link to the VRF structure, if present. */
+ if (bgp->inst_type == BGP_INSTANCE_TYPE_DEFAULT
+ || bgp->inst_type == BGP_INSTANCE_TYPE_VRF) {
+ vrf = bgp_vrf_lookup_by_instance_type(bgp);
+ if (vrf)
+ bgp_vrf_link(bgp, vrf);
+ }
+ /* BGP server socket already processed if BGP instance
+ * already part of the list
+ */
+ bgp_handle_socket(bgp, vrf, VRF_UNKNOWN, true);
+ listnode_add(bm->bgp, bgp);
+
+ if (IS_BGP_INST_KNOWN_TO_ZEBRA(bgp)) {
+ if (BGP_DEBUG(zebra, ZEBRA))
+ zlog_debug("%s: Registering BGP instance %s to zebra",
+ __func__, name);
+ bgp_zebra_instance_register(bgp);
+ }
+
+ return BGP_CREATED;
+}
+
+static void bgp_zclient_set_redist(afi_t afi, int type, unsigned short instance,
+ vrf_id_t vrf_id, bool set)
+{
+ if (instance) {
+ if (set)
+ redist_add_instance(&zclient->mi_redist[afi][type],
+ instance);
+ else
+ redist_del_instance(&zclient->mi_redist[afi][type],
+ instance);
+ } else {
+ if (set)
+ vrf_bitmap_set(zclient->redist[afi][type], vrf_id);
+ else
+ vrf_bitmap_unset(zclient->redist[afi][type], vrf_id);
+ }
+}
+
+static void bgp_set_redist_vrf_bitmaps(struct bgp *bgp, bool set)
+{
+ afi_t afi;
+ int i;
+ struct list *red_list;
+ struct listnode *node;
+ struct bgp_redist *red;
+
+ for (afi = AFI_IP; afi < AFI_MAX; afi++) {
+ for (i = 0; i < ZEBRA_ROUTE_MAX; i++) {
+
+ red_list = bgp->redist[afi][i];
+ if (!red_list)
+ continue;
+
+ for (ALL_LIST_ELEMENTS_RO(red_list, node, red))
+ bgp_zclient_set_redist(afi, i, red->instance,
+ bgp->vrf_id, set);
+ }
+ }
+}
+
+/*
+ * Make BGP instance "up". Applies only to VRFs (non-default) and
+ * implies the VRF has been learnt from Zebra.
+ */
+void bgp_instance_up(struct bgp *bgp)
+{
+ struct peer *peer;
+ struct listnode *node, *next;
+
+ bgp_set_redist_vrf_bitmaps(bgp, true);
+
+ /* Register with zebra. */
+ bgp_zebra_instance_register(bgp);
+
+ /* Kick off any peers that may have been configured. */
+ for (ALL_LIST_ELEMENTS(bgp->peer, node, next, peer)) {
+ if (!BGP_PEER_START_SUPPRESSED(peer))
+ BGP_EVENT_ADD(peer, BGP_Start);
+ }
+
+ /* Process any networks that have been configured. */
+ bgp_static_add(bgp);
+}
+
+/*
+ * Make BGP instance "down". Applies only to VRFs (non-default) and
+ * implies the VRF has been deleted by Zebra.
+ */
+void bgp_instance_down(struct bgp *bgp)
+{
+ struct peer *peer;
+ struct listnode *node;
+ struct listnode *next;
+
+ /* Stop timers. */
+ if (bgp->t_rmap_def_originate_eval) {
+ THREAD_OFF(bgp->t_rmap_def_originate_eval);
+ bgp_unlock(bgp); /* TODO - This timer is started with a lock -
+ why? */
+ }
+
+ /* Bring down peers, so corresponding routes are purged. */
+ for (ALL_LIST_ELEMENTS(bgp->peer, node, next, peer)) {
+ if (BGP_IS_VALID_STATE_FOR_NOTIF(peer->status))
+ bgp_notify_send(peer, BGP_NOTIFY_CEASE,
+ BGP_NOTIFY_CEASE_ADMIN_SHUTDOWN);
+ else
+ bgp_session_reset(peer);
+ }
+
+ /* Purge network and redistributed routes. */
+ bgp_purge_static_redist_routes(bgp);
+
+ /* Cleanup registered nexthops (flags) */
+ bgp_cleanup_nexthops(bgp);
+
+ bgp_zebra_instance_deregister(bgp);
+
+ bgp_set_redist_vrf_bitmaps(bgp, false);
+}
+
+/* Delete BGP instance. */
+int bgp_delete(struct bgp *bgp)
+{
+ struct peer *peer;
+ struct peer_group *group;
+ struct listnode *node, *next;
+ struct vrf *vrf;
+ afi_t afi;
+ safi_t safi;
+ int i;
+ struct graceful_restart_info *gr_info;
+
+ assert(bgp);
+
+ bgp_soft_reconfig_table_task_cancel(bgp, NULL, NULL);
+
+ /* make sure we withdraw any exported routes */
+ vpn_leak_prechange(BGP_VPN_POLICY_DIR_TOVPN, AFI_IP, bgp_get_default(),
+ bgp);
+ vpn_leak_prechange(BGP_VPN_POLICY_DIR_TOVPN, AFI_IP6, bgp_get_default(),
+ bgp);
+
+ bgp_vpn_leak_unimport(bgp);
+
+ hook_call(bgp_inst_delete, bgp);
+
+ THREAD_OFF(bgp->t_condition_check);
+ THREAD_OFF(bgp->t_startup);
+ THREAD_OFF(bgp->t_maxmed_onstartup);
+ THREAD_OFF(bgp->t_update_delay);
+ THREAD_OFF(bgp->t_establish_wait);
+
+ /* Set flag indicating bgp instance delete in progress */
+ SET_FLAG(bgp->flags, BGP_FLAG_DELETE_IN_PROGRESS);
+
+ /* Delete the graceful restart info */
+ FOREACH_AFI_SAFI (afi, safi) {
+ struct thread *t;
+
+ gr_info = &bgp->gr_info[afi][safi];
+ if (!gr_info)
+ continue;
+ t = gr_info->t_select_deferral;
+ if (t) {
+ void *info = THREAD_ARG(t);
+
+ XFREE(MTYPE_TMP, info);
+ }
+ THREAD_OFF(gr_info->t_select_deferral);
+
+ t = gr_info->t_route_select;
+ if (t) {
+ void *info = THREAD_ARG(t);
+
+ XFREE(MTYPE_TMP, info);
+ }
+ THREAD_OFF(gr_info->t_route_select);
+ }
+
+ if (BGP_DEBUG(zebra, ZEBRA)) {
+ if (bgp->inst_type == BGP_INSTANCE_TYPE_DEFAULT)
+ zlog_debug("Deleting Default VRF");
+ else
+ zlog_debug("Deleting %s %s",
+ (bgp->inst_type == BGP_INSTANCE_TYPE_VRF)
+ ? "VRF"
+ : "VIEW",
+ bgp->name);
+ }
+
+ /* unmap from RT list */
+ bgp_evpn_vrf_delete(bgp);
+
+ /* unmap bgp vrf label */
+ vpn_leak_zebra_vrf_label_withdraw(bgp, AFI_IP);
+ vpn_leak_zebra_vrf_label_withdraw(bgp, AFI_IP6);
+
+ /* Stop timers. */
+ if (bgp->t_rmap_def_originate_eval) {
+ THREAD_OFF(bgp->t_rmap_def_originate_eval);
+ bgp_unlock(bgp); /* TODO - This timer is started with a lock -
+ why? */
+ }
+
+ /* Inform peers we're going down. */
+ for (ALL_LIST_ELEMENTS(bgp->peer, node, next, peer))
+ peer_notify_shutdown(peer);
+
+ /* Delete static routes (networks). */
+ bgp_static_delete(bgp);
+
+ /* Unset redistribution. */
+ for (afi = AFI_IP; afi < AFI_MAX; afi++)
+ for (i = 0; i < ZEBRA_ROUTE_MAX; i++)
+ if (i != ZEBRA_ROUTE_BGP)
+ bgp_redistribute_unset(bgp, afi, i, 0);
+
+ /* Free peers and peer-groups. */
+ for (ALL_LIST_ELEMENTS(bgp->group, node, next, group))
+ peer_group_delete(group);
+
+ for (ALL_LIST_ELEMENTS(bgp->peer, node, next, peer))
+ peer_delete(peer);
+
+ if (bgp->peer_self) {
+ peer_delete(bgp->peer_self);
+ bgp->peer_self = NULL;
+ }
+
+ update_bgp_group_free(bgp);
+
+/* TODO - Other memory may need to be freed - e.g., NHT */
+
+#ifdef ENABLE_BGP_VNC
+ rfapi_delete(bgp);
+#endif
+
+ /* Free memory allocated with aggregate address configuration. */
+ FOREACH_AFI_SAFI (afi, safi) {
+ struct bgp_aggregate *aggregate = NULL;
+
+ for (struct bgp_dest *dest =
+ bgp_table_top(bgp->aggregate[afi][safi]);
+ dest; dest = bgp_route_next(dest)) {
+ aggregate = bgp_dest_get_bgp_aggregate_info(dest);
+ if (aggregate == NULL)
+ continue;
+
+ bgp_dest_set_bgp_aggregate_info(dest, NULL);
+ bgp_free_aggregate_info(aggregate);
+ }
+ }
+
+ bgp_cleanup_routes(bgp);
+
+ for (afi = 0; afi < AFI_MAX; ++afi) {
+ if (!bgp->vpn_policy[afi].import_redirect_rtlist)
+ continue;
+ ecommunity_free(
+ &bgp->vpn_policy[afi]
+ .import_redirect_rtlist);
+ bgp->vpn_policy[afi].import_redirect_rtlist = NULL;
+ }
+
+ /* Free any memory allocated to holding routemap references */
+ for (afi = 0; afi < AFI_MAX; ++afi) {
+ for (enum vpn_policy_direction dir = 0;
+ dir < BGP_VPN_POLICY_DIR_MAX; ++dir) {
+ if (bgp->vpn_policy[afi].rmap_name[dir])
+ XFREE(MTYPE_ROUTE_MAP_NAME,
+ bgp->vpn_policy[afi].rmap_name[dir]);
+ bgp->vpn_policy[afi].rmap[dir] = NULL;
+ }
+ }
+
+ /* Deregister from Zebra, if needed */
+ if (IS_BGP_INST_KNOWN_TO_ZEBRA(bgp)) {
+ if (BGP_DEBUG(zebra, ZEBRA))
+ zlog_debug(
+ "%s: deregistering this bgp %s instance from zebra",
+ __func__, bgp->name);
+ bgp_zebra_instance_deregister(bgp);
+ }
+
+ /* Remove visibility via the master list - there may however still be
+ * routes to be processed still referencing the struct bgp.
+ */
+ listnode_delete(bm->bgp, bgp);
+
+ /* Free interfaces in this instance. */
+ bgp_if_finish(bgp);
+
+ vrf = bgp_vrf_lookup_by_instance_type(bgp);
+ bgp_handle_socket(bgp, vrf, VRF_UNKNOWN, false);
+ if (vrf)
+ bgp_vrf_unlink(bgp, vrf);
+
+ /* Update EVPN VRF pointer */
+ if (bm->bgp_evpn == bgp) {
+ if (bgp->inst_type == BGP_INSTANCE_TYPE_DEFAULT)
+ bgp_set_evpn(NULL);
+ else
+ bgp_set_evpn(bgp_get_default());
+ }
+
+ if (bgp->process_queue)
+ work_queue_free_and_null(&bgp->process_queue);
+
+ thread_master_free_unused(bm->master);
+ bgp_unlock(bgp); /* initial reference */
+
+ return 0;
+}
+
+void bgp_free(struct bgp *bgp)
+{
+ afi_t afi;
+ safi_t safi;
+ struct bgp_table *table;
+ struct bgp_dest *dest;
+ struct bgp_rmap *rmap;
+
+ QOBJ_UNREG(bgp);
+
+ list_delete(&bgp->group);
+ list_delete(&bgp->peer);
+
+ if (bgp->peerhash) {
+ hash_free(bgp->peerhash);
+ bgp->peerhash = NULL;
+ }
+
+ FOREACH_AFI_SAFI (afi, safi) {
+ /* Special handling for 2-level routing tables. */
+ if (safi == SAFI_MPLS_VPN || safi == SAFI_ENCAP
+ || safi == SAFI_EVPN) {
+ for (dest = bgp_table_top(bgp->rib[afi][safi]); dest;
+ dest = bgp_route_next(dest)) {
+ table = bgp_dest_get_bgp_table_info(dest);
+ bgp_table_finish(&table);
+ }
+ }
+ if (bgp->route[afi][safi])
+ bgp_table_finish(&bgp->route[afi][safi]);
+ if (bgp->aggregate[afi][safi])
+ bgp_table_finish(&bgp->aggregate[afi][safi]);
+ if (bgp->rib[afi][safi])
+ bgp_table_finish(&bgp->rib[afi][safi]);
+ rmap = &bgp->table_map[afi][safi];
+ XFREE(MTYPE_ROUTE_MAP_NAME, rmap->name);
+ }
+
+ bgp_scan_finish(bgp);
+ bgp_address_destroy(bgp);
+ bgp_tip_hash_destroy(bgp);
+
+ /* release the auto RD id */
+ bf_release_index(bm->rd_idspace, bgp->vrf_rd_id);
+
+ bgp_evpn_cleanup(bgp);
+ bgp_pbr_cleanup(bgp);
+ bgp_srv6_cleanup(bgp);
+ XFREE(MTYPE_BGP_EVPN_INFO, bgp->evpn_info);
+
+ for (afi = AFI_IP; afi < AFI_MAX; afi++) {
+ enum vpn_policy_direction dir;
+
+ if (bgp->vpn_policy[afi].import_vrf)
+ list_delete(&bgp->vpn_policy[afi].import_vrf);
+ if (bgp->vpn_policy[afi].export_vrf)
+ list_delete(&bgp->vpn_policy[afi].export_vrf);
+
+ dir = BGP_VPN_POLICY_DIR_FROMVPN;
+ if (bgp->vpn_policy[afi].rtlist[dir])
+ ecommunity_free(&bgp->vpn_policy[afi].rtlist[dir]);
+ dir = BGP_VPN_POLICY_DIR_TOVPN;
+ if (bgp->vpn_policy[afi].rtlist[dir])
+ ecommunity_free(&bgp->vpn_policy[afi].rtlist[dir]);
+ }
+
+ XFREE(MTYPE_BGP, bgp->name);
+ XFREE(MTYPE_BGP, bgp->name_pretty);
+ XFREE(MTYPE_BGP, bgp->snmp_stats);
+
+ XFREE(MTYPE_BGP, bgp);
+}
+
+struct peer *peer_lookup_by_conf_if(struct bgp *bgp, const char *conf_if)
+{
+ struct peer *peer;
+ struct listnode *node, *nnode;
+
+ if (!conf_if)
+ return NULL;
+
+ if (bgp != NULL) {
+ for (ALL_LIST_ELEMENTS(bgp->peer, node, nnode, peer))
+ if (peer->conf_if && !strcmp(peer->conf_if, conf_if)
+ && !CHECK_FLAG(peer->sflags,
+ PEER_STATUS_ACCEPT_PEER))
+ return peer;
+ } else if (bm->bgp != NULL) {
+ struct listnode *bgpnode, *nbgpnode;
+
+ for (ALL_LIST_ELEMENTS(bm->bgp, bgpnode, nbgpnode, bgp))
+ for (ALL_LIST_ELEMENTS(bgp->peer, node, nnode, peer))
+ if (peer->conf_if
+ && !strcmp(peer->conf_if, conf_if)
+ && !CHECK_FLAG(peer->sflags,
+ PEER_STATUS_ACCEPT_PEER))
+ return peer;
+ }
+ return NULL;
+}
+
+struct peer *peer_lookup_by_hostname(struct bgp *bgp, const char *hostname)
+{
+ struct peer *peer;
+ struct listnode *node, *nnode;
+
+ if (!hostname)
+ return NULL;
+
+ if (bgp != NULL) {
+ for (ALL_LIST_ELEMENTS(bgp->peer, node, nnode, peer))
+ if (peer->hostname && !strcmp(peer->hostname, hostname)
+ && !CHECK_FLAG(peer->sflags,
+ PEER_STATUS_ACCEPT_PEER))
+ return peer;
+ } else if (bm->bgp != NULL) {
+ struct listnode *bgpnode, *nbgpnode;
+
+ for (ALL_LIST_ELEMENTS(bm->bgp, bgpnode, nbgpnode, bgp))
+ for (ALL_LIST_ELEMENTS(bgp->peer, node, nnode, peer))
+ if (peer->hostname
+ && !strcmp(peer->hostname, hostname)
+ && !CHECK_FLAG(peer->sflags,
+ PEER_STATUS_ACCEPT_PEER))
+ return peer;
+ }
+ return NULL;
+}
+
+struct peer *peer_lookup(struct bgp *bgp, union sockunion *su)
+{
+ struct peer *peer = NULL;
+ struct peer tmp_peer;
+
+ memset(&tmp_peer, 0, sizeof(struct peer));
+
+ /*
+ * We do not want to find the doppelganger peer so search for the peer
+ * in
+ * the hash that has PEER_FLAG_CONFIG_NODE
+ */
+ SET_FLAG(tmp_peer.flags, PEER_FLAG_CONFIG_NODE);
+
+ tmp_peer.su = *su;
+
+ if (bgp != NULL) {
+ peer = hash_lookup(bgp->peerhash, &tmp_peer);
+ } else if (bm->bgp != NULL) {
+ struct listnode *bgpnode, *nbgpnode;
+
+ for (ALL_LIST_ELEMENTS(bm->bgp, bgpnode, nbgpnode, bgp)) {
+ peer = hash_lookup(bgp->peerhash, &tmp_peer);
+ if (peer)
+ break;
+ }
+ }
+
+ return peer;
+}
+
+struct peer *peer_create_bind_dynamic_neighbor(struct bgp *bgp,
+ union sockunion *su,
+ struct peer_group *group)
+{
+ struct peer *peer;
+ afi_t afi;
+ safi_t safi;
+
+ /* Create peer first; we've already checked group config is valid. */
+ peer = peer_create(su, NULL, bgp, bgp->as, group->conf->as,
+ group->conf->as_type, group);
+ if (!peer)
+ return NULL;
+
+ /* Link to group */
+ peer = peer_lock(peer);
+ listnode_add(group->peer, peer);
+
+ peer_group2peer_config_copy(group, peer);
+
+ /*
+ * Bind peer for all AFs configured for the group. We don't call
+ * peer_group_bind as that is sub-optimal and does some stuff we don't
+ * want.
+ */
+ FOREACH_AFI_SAFI (afi, safi) {
+ if (!group->conf->afc[afi][safi])
+ continue;
+ peer->afc[afi][safi] = 1;
+
+ if (!peer_af_find(peer, afi, safi))
+ peer_af_create(peer, afi, safi);
+
+ peer_group2peer_config_copy_af(group, peer, afi, safi);
+ }
+
+ /* Mark as dynamic, but also as a "config node" for other things to
+ * work. */
+ SET_FLAG(peer->flags, PEER_FLAG_DYNAMIC_NEIGHBOR);
+ SET_FLAG(peer->flags, PEER_FLAG_CONFIG_NODE);
+
+ return peer;
+}
+
+struct prefix *
+peer_group_lookup_dynamic_neighbor_range(struct peer_group *group,
+ struct prefix *prefix)
+{
+ struct listnode *node, *nnode;
+ struct prefix *range;
+ afi_t afi;
+
+ afi = family2afi(prefix->family);
+
+ if (group->listen_range[afi])
+ for (ALL_LIST_ELEMENTS(group->listen_range[afi], node, nnode,
+ range))
+ if (prefix_match(range, prefix))
+ return range;
+
+ return NULL;
+}
+
+struct peer_group *
+peer_group_lookup_dynamic_neighbor(struct bgp *bgp, struct prefix *prefix,
+ struct prefix **listen_range)
+{
+ struct prefix *range = NULL;
+ struct peer_group *group = NULL;
+ struct listnode *node, *nnode;
+
+ *listen_range = NULL;
+ if (bgp != NULL) {
+ for (ALL_LIST_ELEMENTS(bgp->group, node, nnode, group))
+ if ((range = peer_group_lookup_dynamic_neighbor_range(
+ group, prefix)))
+ break;
+ } else if (bm->bgp != NULL) {
+ struct listnode *bgpnode, *nbgpnode;
+
+ for (ALL_LIST_ELEMENTS(bm->bgp, bgpnode, nbgpnode, bgp))
+ for (ALL_LIST_ELEMENTS(bgp->group, node, nnode, group))
+ if ((range = peer_group_lookup_dynamic_neighbor_range(
+ group, prefix)))
+ goto found_range;
+ }
+
+found_range:
+ *listen_range = range;
+ return (group && range) ? group : NULL;
+}
+
+struct peer *peer_lookup_dynamic_neighbor(struct bgp *bgp, union sockunion *su)
+{
+ struct peer_group *group;
+ struct bgp *gbgp;
+ struct peer *peer;
+ struct prefix prefix;
+ struct prefix *listen_range;
+ int dncount;
+
+ if (!sockunion2hostprefix(su, &prefix))
+ return NULL;
+
+ /* See if incoming connection matches a configured listen range. */
+ group = peer_group_lookup_dynamic_neighbor(bgp, &prefix, &listen_range);
+
+ if (!group)
+ return NULL;
+
+
+ gbgp = group->bgp;
+
+ if (!gbgp)
+ return NULL;
+
+ if (bgp_debug_neighbor_events(NULL))
+ zlog_debug(
+ "Dynamic Neighbor %pFX matches group %s listen range %pFX",
+ &prefix, group->name, listen_range);
+
+ /* Are we within the listen limit? */
+ dncount = gbgp->dynamic_neighbors_count;
+
+ if (dncount >= gbgp->dynamic_neighbors_limit) {
+ if (bgp_debug_neighbor_events(NULL))
+ zlog_debug(
+ "Dynamic Neighbor %pFX rejected - at limit %d",
+ &prefix, gbgp->dynamic_neighbors_limit);
+ return NULL;
+ }
+
+ /* Ensure group is not disabled. */
+ if (CHECK_FLAG(group->conf->flags, PEER_FLAG_SHUTDOWN)) {
+ if (bgp_debug_neighbor_events(NULL))
+ zlog_debug(
+ "Dynamic Neighbor %pFX rejected - group %s disabled",
+ &prefix, group->name);
+ return NULL;
+ }
+
+ /* Check that at least one AF is activated for the group. */
+ if (!peer_group_af_configured(group)) {
+ if (bgp_debug_neighbor_events(NULL))
+ zlog_debug(
+ "Dynamic Neighbor %pFX rejected - no AF activated for group %s",
+ &prefix, group->name);
+ return NULL;
+ }
+
+ /* Create dynamic peer and bind to associated group. */
+ peer = peer_create_bind_dynamic_neighbor(gbgp, su, group);
+ assert(peer);
+
+ gbgp->dynamic_neighbors_count = ++dncount;
+
+ if (bgp_debug_neighbor_events(peer))
+ zlog_debug("%s Dynamic Neighbor added, group %s count %d",
+ peer->host, group->name, dncount);
+
+ return peer;
+}
+
+static void peer_drop_dynamic_neighbor(struct peer *peer)
+{
+ int dncount = -1;
+ if (peer->group->bgp) {
+ dncount = peer->group->bgp->dynamic_neighbors_count;
+ if (dncount)
+ peer->group->bgp->dynamic_neighbors_count = --dncount;
+ }
+ if (bgp_debug_neighbor_events(peer))
+ zlog_debug("%s dropped from group %s, count %d", peer->host,
+ peer->group->name, dncount);
+}
+
+/* If peer is configured at least one address family return 1. */
+bool peer_active(struct peer *peer)
+{
+ if (BGP_PEER_SU_UNSPEC(peer))
+ return false;
+ if (peer->afc[AFI_IP][SAFI_UNICAST] || peer->afc[AFI_IP][SAFI_MULTICAST]
+ || peer->afc[AFI_IP][SAFI_LABELED_UNICAST]
+ || peer->afc[AFI_IP][SAFI_MPLS_VPN] || peer->afc[AFI_IP][SAFI_ENCAP]
+ || peer->afc[AFI_IP][SAFI_FLOWSPEC]
+ || peer->afc[AFI_IP6][SAFI_UNICAST]
+ || peer->afc[AFI_IP6][SAFI_MULTICAST]
+ || peer->afc[AFI_IP6][SAFI_LABELED_UNICAST]
+ || peer->afc[AFI_IP6][SAFI_MPLS_VPN]
+ || peer->afc[AFI_IP6][SAFI_ENCAP]
+ || peer->afc[AFI_IP6][SAFI_FLOWSPEC]
+ || peer->afc[AFI_L2VPN][SAFI_EVPN])
+ return true;
+ return false;
+}
+
+/* If peer is negotiated at least one address family return 1. */
+bool peer_active_nego(struct peer *peer)
+{
+ if (peer->afc_nego[AFI_IP][SAFI_UNICAST]
+ || peer->afc_nego[AFI_IP][SAFI_MULTICAST]
+ || peer->afc_nego[AFI_IP][SAFI_LABELED_UNICAST]
+ || peer->afc_nego[AFI_IP][SAFI_MPLS_VPN]
+ || peer->afc_nego[AFI_IP][SAFI_ENCAP]
+ || peer->afc_nego[AFI_IP][SAFI_FLOWSPEC]
+ || peer->afc_nego[AFI_IP6][SAFI_UNICAST]
+ || peer->afc_nego[AFI_IP6][SAFI_MULTICAST]
+ || peer->afc_nego[AFI_IP6][SAFI_LABELED_UNICAST]
+ || peer->afc_nego[AFI_IP6][SAFI_MPLS_VPN]
+ || peer->afc_nego[AFI_IP6][SAFI_ENCAP]
+ || peer->afc_nego[AFI_IP6][SAFI_FLOWSPEC]
+ || peer->afc_nego[AFI_L2VPN][SAFI_EVPN])
+ return true;
+ return false;
+}
+
+/* If peer received at least one address family MP, return true */
+bool peer_afc_received(struct peer *peer)
+{
+ afi_t afi;
+ safi_t safi;
+
+ FOREACH_AFI_SAFI (afi, safi)
+ if (peer->afc_recv[afi][safi])
+ return true;
+
+ return false;
+}
+
+/* If peer advertised at least one address family MP, return true */
+bool peer_afc_advertised(struct peer *peer)
+{
+ afi_t afi;
+ safi_t safi;
+
+ FOREACH_AFI_SAFI (afi, safi)
+ if (peer->afc_adv[afi][safi])
+ return true;
+
+ return false;
+}
+
+void peer_change_action(struct peer *peer, afi_t afi, safi_t safi,
+ enum peer_change_type type)
+{
+ struct peer_af *paf;
+
+ if (CHECK_FLAG(peer->sflags, PEER_STATUS_GROUP))
+ return;
+
+ if (!peer_established(peer))
+ return;
+
+ if (type == peer_change_reset) {
+ /* If we're resetting session, we've to delete both peer struct
+ */
+ if ((peer->doppelganger)
+ && (peer->doppelganger->status != Deleted)
+ && (!CHECK_FLAG(peer->doppelganger->flags,
+ PEER_FLAG_CONFIG_NODE)))
+ peer_delete(peer->doppelganger);
+
+ bgp_notify_send(peer, BGP_NOTIFY_CEASE,
+ BGP_NOTIFY_CEASE_CONFIG_CHANGE);
+ } else if (type == peer_change_reset_in) {
+ if (CHECK_FLAG(peer->cap, PEER_CAP_REFRESH_OLD_RCV)
+ || CHECK_FLAG(peer->cap, PEER_CAP_REFRESH_NEW_RCV))
+ bgp_route_refresh_send(peer, afi, safi, 0, 0, 0,
+ BGP_ROUTE_REFRESH_NORMAL);
+ else {
+ if ((peer->doppelganger)
+ && (peer->doppelganger->status != Deleted)
+ && (!CHECK_FLAG(peer->doppelganger->flags,
+ PEER_FLAG_CONFIG_NODE)))
+ peer_delete(peer->doppelganger);
+
+ bgp_notify_send(peer, BGP_NOTIFY_CEASE,
+ BGP_NOTIFY_CEASE_CONFIG_CHANGE);
+ }
+ } else if (type == peer_change_reset_out) {
+ paf = peer_af_find(peer, afi, safi);
+ if (paf && paf->subgroup)
+ SET_FLAG(paf->subgroup->sflags,
+ SUBGRP_STATUS_FORCE_UPDATES);
+
+ update_group_adjust_peer(paf);
+ bgp_announce_route(peer, afi, safi, false);
+ }
+}
+
+struct peer_flag_action {
+ /* Peer's flag. */
+ uint64_t flag;
+
+ /* This flag can be set for peer-group member. */
+ uint8_t not_for_member;
+
+ /* Action when the flag is changed. */
+ enum peer_change_type type;
+};
+
+static const struct peer_flag_action peer_flag_action_list[] = {
+ {PEER_FLAG_PASSIVE, 0, peer_change_reset},
+ {PEER_FLAG_SHUTDOWN, 0, peer_change_reset},
+ {PEER_FLAG_RTT_SHUTDOWN, 0, peer_change_none},
+ {PEER_FLAG_DONT_CAPABILITY, 0, peer_change_none},
+ {PEER_FLAG_OVERRIDE_CAPABILITY, 0, peer_change_none},
+ {PEER_FLAG_STRICT_CAP_MATCH, 0, peer_change_none},
+ {PEER_FLAG_DYNAMIC_CAPABILITY, 0, peer_change_reset},
+ {PEER_FLAG_DISABLE_CONNECTED_CHECK, 0, peer_change_reset},
+ {PEER_FLAG_CAPABILITY_ENHE, 0, peer_change_reset},
+ {PEER_FLAG_ENFORCE_FIRST_AS, 0, peer_change_reset_in},
+ {PEER_FLAG_IFPEER_V6ONLY, 0, peer_change_reset},
+ {PEER_FLAG_ROUTEADV, 0, peer_change_none},
+ {PEER_FLAG_TIMER, 0, peer_change_none},
+ {PEER_FLAG_TIMER_CONNECT, 0, peer_change_none},
+ {PEER_FLAG_TIMER_DELAYOPEN, 0, peer_change_none},
+ {PEER_FLAG_PASSWORD, 0, peer_change_none},
+ {PEER_FLAG_LOCAL_AS, 0, peer_change_none},
+ {PEER_FLAG_LOCAL_AS_NO_PREPEND, 0, peer_change_none},
+ {PEER_FLAG_LOCAL_AS_REPLACE_AS, 0, peer_change_none},
+ {PEER_FLAG_UPDATE_SOURCE, 0, peer_change_none},
+ {PEER_FLAG_DISABLE_LINK_BW_ENCODING_IEEE, 0, peer_change_none},
+ {PEER_FLAG_EXTENDED_OPT_PARAMS, 0, peer_change_reset},
+ {PEER_FLAG_ROLE_STRICT_MODE, 0, peer_change_reset},
+ {PEER_FLAG_ROLE, 0, peer_change_reset},
+ {PEER_FLAG_PORT, 0, peer_change_reset},
+ {0, 0, 0}};
+
+static const struct peer_flag_action peer_af_flag_action_list[] = {
+ {PEER_FLAG_SEND_COMMUNITY, 1, peer_change_reset_out},
+ {PEER_FLAG_SEND_EXT_COMMUNITY, 1, peer_change_reset_out},
+ {PEER_FLAG_SEND_LARGE_COMMUNITY, 1, peer_change_reset_out},
+ {PEER_FLAG_NEXTHOP_SELF, 1, peer_change_reset_out},
+ {PEER_FLAG_REFLECTOR_CLIENT, 1, peer_change_reset},
+ {PEER_FLAG_RSERVER_CLIENT, 1, peer_change_reset},
+ {PEER_FLAG_SOFT_RECONFIG, 0, peer_change_reset_in},
+ {PEER_FLAG_AS_PATH_UNCHANGED, 1, peer_change_reset_out},
+ {PEER_FLAG_NEXTHOP_UNCHANGED, 1, peer_change_reset_out},
+ {PEER_FLAG_MED_UNCHANGED, 1, peer_change_reset_out},
+ {PEER_FLAG_DEFAULT_ORIGINATE, 0, peer_change_none},
+ {PEER_FLAG_REMOVE_PRIVATE_AS, 1, peer_change_reset_out},
+ {PEER_FLAG_ALLOWAS_IN, 0, peer_change_reset_in},
+ {PEER_FLAG_ALLOWAS_IN_ORIGIN, 0, peer_change_reset_in},
+ {PEER_FLAG_ORF_PREFIX_SM, 1, peer_change_reset},
+ {PEER_FLAG_ORF_PREFIX_RM, 1, peer_change_reset},
+ {PEER_FLAG_MAX_PREFIX, 0, peer_change_none},
+ {PEER_FLAG_MAX_PREFIX_WARNING, 0, peer_change_none},
+ {PEER_FLAG_MAX_PREFIX_FORCE, 0, peer_change_none},
+ {PEER_FLAG_MAX_PREFIX_OUT, 0, peer_change_none},
+ {PEER_FLAG_NEXTHOP_LOCAL_UNCHANGED, 0, peer_change_reset_out},
+ {PEER_FLAG_FORCE_NEXTHOP_SELF, 1, peer_change_reset_out},
+ {PEER_FLAG_REMOVE_PRIVATE_AS_ALL, 1, peer_change_reset_out},
+ {PEER_FLAG_REMOVE_PRIVATE_AS_REPLACE, 1, peer_change_reset_out},
+ {PEER_FLAG_AS_OVERRIDE, 1, peer_change_reset_out},
+ {PEER_FLAG_REMOVE_PRIVATE_AS_ALL_REPLACE, 1, peer_change_reset_out},
+ {PEER_FLAG_WEIGHT, 0, peer_change_reset_in},
+ {PEER_FLAG_DISABLE_ADDPATH_RX, 0, peer_change_reset},
+ {PEER_FLAG_SOO, 0, peer_change_reset},
+ {0, 0, 0}};
+
+/* Proper action set. */
+static int peer_flag_action_set(const struct peer_flag_action *action_list,
+ int size, struct peer_flag_action *action,
+ uint64_t flag)
+{
+ int i;
+ int found = 0;
+ int reset_in = 0;
+ int reset_out = 0;
+ const struct peer_flag_action *match = NULL;
+
+ /* Check peer's frag action. */
+ for (i = 0; i < size; i++) {
+ match = &action_list[i];
+
+ if (match->flag == 0)
+ break;
+
+ if (match->flag & flag) {
+ found = 1;
+
+ if (match->type == peer_change_reset_in)
+ reset_in = 1;
+ if (match->type == peer_change_reset_out)
+ reset_out = 1;
+ if (match->type == peer_change_reset) {
+ reset_in = 1;
+ reset_out = 1;
+ }
+ if (match->not_for_member)
+ action->not_for_member = 1;
+ }
+ }
+
+ /* Set peer clear type. */
+ if (reset_in && reset_out)
+ action->type = peer_change_reset;
+ else if (reset_in)
+ action->type = peer_change_reset_in;
+ else if (reset_out)
+ action->type = peer_change_reset_out;
+ else
+ action->type = peer_change_none;
+
+ return found;
+}
+
+static void peer_flag_modify_action(struct peer *peer, uint32_t flag)
+{
+ if (flag == PEER_FLAG_SHUTDOWN) {
+ if (CHECK_FLAG(peer->flags, flag)) {
+ if (CHECK_FLAG(peer->sflags, PEER_STATUS_NSF_WAIT))
+ peer_nsf_stop(peer);
+
+ UNSET_FLAG(peer->sflags, PEER_STATUS_PREFIX_OVERFLOW);
+
+ if (peer->t_pmax_restart) {
+ THREAD_OFF(peer->t_pmax_restart);
+ if (bgp_debug_neighbor_events(peer))
+ zlog_debug(
+ "%pBP Maximum-prefix restart timer canceled",
+ peer);
+ }
+
+ if (BGP_IS_VALID_STATE_FOR_NOTIF(peer->status)) {
+ char *msg = peer->tx_shutdown_message;
+ size_t msglen;
+ uint8_t msgbuf[BGP_ADMIN_SHUTDOWN_MSG_LEN + 1];
+
+ if (!msg && peer_group_active(peer))
+ msg = peer->group->conf
+ ->tx_shutdown_message;
+ msglen = msg ? strlen(msg) : 0;
+ if (msglen > BGP_ADMIN_SHUTDOWN_MSG_LEN)
+ msglen = BGP_ADMIN_SHUTDOWN_MSG_LEN;
+
+ if (msglen) {
+ msgbuf[0] = msglen;
+ memcpy(msgbuf + 1, msg, msglen);
+
+ bgp_notify_send_with_data(
+ peer, BGP_NOTIFY_CEASE,
+ BGP_NOTIFY_CEASE_ADMIN_SHUTDOWN,
+ msgbuf, msglen + 1);
+ } else
+ bgp_notify_send(
+ peer, BGP_NOTIFY_CEASE,
+ BGP_NOTIFY_CEASE_ADMIN_SHUTDOWN);
+ } else
+ bgp_session_reset(peer);
+ } else {
+ peer->v_start = BGP_INIT_START_TIMER;
+ BGP_EVENT_ADD(peer, BGP_Stop);
+ }
+ } else if (BGP_IS_VALID_STATE_FOR_NOTIF(peer->status)) {
+ if (flag == PEER_FLAG_DYNAMIC_CAPABILITY)
+ peer->last_reset = PEER_DOWN_CAPABILITY_CHANGE;
+ else if (flag == PEER_FLAG_PASSIVE)
+ peer->last_reset = PEER_DOWN_PASSIVE_CHANGE;
+ else if (flag == PEER_FLAG_DISABLE_CONNECTED_CHECK)
+ peer->last_reset = PEER_DOWN_MULTIHOP_CHANGE;
+
+ bgp_notify_send(peer, BGP_NOTIFY_CEASE,
+ BGP_NOTIFY_CEASE_CONFIG_CHANGE);
+ } else
+ bgp_session_reset(peer);
+}
+
+/* Enable global administrative shutdown of all peers of BGP instance */
+void bgp_shutdown_enable(struct bgp *bgp, const char *msg)
+{
+ struct peer *peer;
+ struct listnode *node;
+ /* length(1) + message(N) */
+ uint8_t data[BGP_ADMIN_SHUTDOWN_MSG_LEN + 1];
+
+ /* do nothing if already shut down */
+ if (CHECK_FLAG(bgp->flags, BGP_FLAG_SHUTDOWN))
+ return;
+
+ /* informational log message */
+ zlog_info("Enabled administrative shutdown on BGP instance AS %u",
+ bgp->as);
+
+ /* iterate through peers of BGP instance */
+ for (ALL_LIST_ELEMENTS_RO(bgp->peer, node, peer)) {
+ /* continue, if peer is already in administrative shutdown. */
+ if (CHECK_FLAG(peer->flags, PEER_FLAG_SHUTDOWN))
+ continue;
+
+ /* send a RFC 4486 notification message if necessary */
+ if (BGP_IS_VALID_STATE_FOR_NOTIF(peer->status)) {
+ if (msg) {
+ size_t datalen = strlen(msg);
+
+ if (datalen > BGP_ADMIN_SHUTDOWN_MSG_LEN)
+ datalen = BGP_ADMIN_SHUTDOWN_MSG_LEN;
+
+ data[0] = datalen;
+ memcpy(data + 1, msg, datalen);
+
+ bgp_notify_send_with_data(
+ peer, BGP_NOTIFY_CEASE,
+ BGP_NOTIFY_CEASE_ADMIN_SHUTDOWN, data,
+ datalen + 1);
+ } else {
+ bgp_notify_send(
+ peer, BGP_NOTIFY_CEASE,
+ BGP_NOTIFY_CEASE_ADMIN_SHUTDOWN);
+ }
+ }
+
+ /* reset start timer to initial value */
+ peer->v_start = BGP_INIT_START_TIMER;
+
+ /* trigger a RFC 4271 ManualStop event */
+ BGP_EVENT_ADD(peer, BGP_Stop);
+ }
+
+ /* set the BGP instances shutdown flag */
+ SET_FLAG(bgp->flags, BGP_FLAG_SHUTDOWN);
+}
+
+/* Disable global administrative shutdown of all peers of BGP instance */
+void bgp_shutdown_disable(struct bgp *bgp)
+{
+ /* do nothing if not shut down. */
+ if (!CHECK_FLAG(bgp->flags, BGP_FLAG_SHUTDOWN))
+ return;
+
+ /* informational log message */
+ zlog_info("Disabled administrative shutdown on BGP instance AS %u",
+ bgp->as);
+
+ /* clear the BGP instances shutdown flag */
+ UNSET_FLAG(bgp->flags, BGP_FLAG_SHUTDOWN);
+}
+
+/* Change specified peer flag. */
+static int peer_flag_modify(struct peer *peer, uint64_t flag, int set)
+{
+ int found;
+ int size;
+ bool invert, member_invert;
+ struct peer *member;
+ struct listnode *node, *nnode;
+ struct peer_flag_action action;
+
+ memset(&action, 0, sizeof(struct peer_flag_action));
+ size = sizeof(peer_flag_action_list) / sizeof(struct peer_flag_action);
+
+ invert = CHECK_FLAG(peer->flags_invert, flag);
+ found = peer_flag_action_set(peer_flag_action_list, size, &action,
+ flag);
+
+ /* Abort if no flag action exists. */
+ if (!found)
+ return BGP_ERR_INVALID_FLAG;
+
+ /* Check for flag conflict: STRICT_CAP_MATCH && OVERRIDE_CAPABILITY */
+ if (set && CHECK_FLAG(peer->flags | flag, PEER_FLAG_STRICT_CAP_MATCH)
+ && CHECK_FLAG(peer->flags | flag, PEER_FLAG_OVERRIDE_CAPABILITY))
+ return BGP_ERR_PEER_FLAG_CONFLICT;
+
+ /* Handle flag updates where desired state matches current state. */
+ if (!CHECK_FLAG(peer->sflags, PEER_STATUS_GROUP)) {
+ if (set && CHECK_FLAG(peer->flags, flag)) {
+ COND_FLAG(peer->flags_override, flag, !invert);
+ return 0;
+ }
+
+ if (!set && !CHECK_FLAG(peer->flags, flag)) {
+ COND_FLAG(peer->flags_override, flag, invert);
+ return 0;
+ }
+ }
+
+ /* Inherit from peer-group or set/unset flags accordingly. */
+ if (peer_group_active(peer) && set == invert)
+ peer_flag_inherit(peer, flag);
+ else
+ COND_FLAG(peer->flags, flag, set);
+
+ /* Check if handling a regular peer. */
+ if (!CHECK_FLAG(peer->sflags, PEER_STATUS_GROUP)) {
+ /* Update flag override state accordingly. */
+ COND_FLAG(peer->flags_override, flag, set != invert);
+
+ /*
+ * For the extended next-hop encoding flag we need to turn RAs
+ * on if flag is being set, but only turn RAs off if the flag
+ * is being unset on this peer and if this peer is a member of a
+ * peer-group, the peer-group also doesn't have the flag set.
+ */
+ if (flag == PEER_FLAG_CAPABILITY_ENHE) {
+ if (set) {
+ bgp_zebra_initiate_radv(peer->bgp, peer);
+ } else if (peer_group_active(peer)) {
+ if (!CHECK_FLAG(peer->group->conf->flags,
+ flag) &&
+ !peer->conf_if)
+ bgp_zebra_terminate_radv(peer->bgp,
+ peer);
+ } else
+ bgp_zebra_terminate_radv(peer->bgp, peer);
+ }
+
+ /* Execute flag action on peer. */
+ if (action.type == peer_change_reset)
+ peer_flag_modify_action(peer, flag);
+
+ /* Skip peer-group mechanics for regular peers. */
+ return 0;
+ }
+
+ /*
+ * Update peer-group members, unless they are explicitly overriding
+ * peer-group configuration.
+ */
+ for (ALL_LIST_ELEMENTS(peer->group->peer, node, nnode, member)) {
+ /* Skip peers with overridden configuration. */
+ if (CHECK_FLAG(member->flags_override, flag))
+ continue;
+
+ /* Check if only member without group is inverted. */
+ member_invert =
+ CHECK_FLAG(member->flags_invert, flag) && !invert;
+
+ /* Skip peers with equivalent configuration. */
+ if (set != member_invert && CHECK_FLAG(member->flags, flag))
+ continue;
+
+ if (set == member_invert && !CHECK_FLAG(member->flags, flag))
+ continue;
+
+ /* Update flag on peer-group member. */
+ COND_FLAG(member->flags, flag, set != member_invert);
+
+ if (flag == PEER_FLAG_CAPABILITY_ENHE && !member->conf_if)
+ set ? bgp_zebra_initiate_radv(member->bgp, member)
+ : bgp_zebra_terminate_radv(member->bgp, member);
+
+ /* Execute flag action on peer-group member. */
+ if (action.type == peer_change_reset)
+ peer_flag_modify_action(member, flag);
+ }
+
+ return 0;
+}
+
+int peer_flag_set(struct peer *peer, uint64_t flag)
+{
+ return peer_flag_modify(peer, flag, 1);
+}
+
+int peer_flag_unset(struct peer *peer, uint64_t flag)
+{
+ return peer_flag_modify(peer, flag, 0);
+}
+
+static int peer_af_flag_modify(struct peer *peer, afi_t afi, safi_t safi,
+ uint64_t flag, bool set)
+{
+ int found;
+ int size;
+ bool invert, member_invert;
+ struct peer *member;
+ struct listnode *node, *nnode;
+ struct peer_flag_action action;
+ enum bgp_peer_sort ptype;
+
+ memset(&action, 0, sizeof(struct peer_flag_action));
+ size = sizeof(peer_af_flag_action_list)
+ / sizeof(struct peer_flag_action);
+
+ invert = CHECK_FLAG(peer->af_flags_invert[afi][safi], flag);
+ found = peer_flag_action_set(peer_af_flag_action_list, size, &action,
+ flag);
+
+ /* Abort if flag action exists. */
+ if (!found)
+ return BGP_ERR_INVALID_FLAG;
+
+ ptype = peer_sort(peer);
+ /* Special check for reflector client. */
+ if (flag & PEER_FLAG_REFLECTOR_CLIENT && ptype != BGP_PEER_IBGP)
+ return BGP_ERR_NOT_INTERNAL_PEER;
+
+ /* Special check for remove-private-AS. */
+ if (flag & PEER_FLAG_REMOVE_PRIVATE_AS && ptype == BGP_PEER_IBGP)
+ return BGP_ERR_REMOVE_PRIVATE_AS;
+
+ /* as-override is not allowed for IBGP peers */
+ if (flag & PEER_FLAG_AS_OVERRIDE && ptype == BGP_PEER_IBGP)
+ return BGP_ERR_AS_OVERRIDE;
+
+ /* Handle flag updates where desired state matches current state. */
+ if (!CHECK_FLAG(peer->sflags, PEER_STATUS_GROUP)) {
+ if (set && CHECK_FLAG(peer->af_flags[afi][safi], flag)) {
+ COND_FLAG(peer->af_flags_override[afi][safi], flag,
+ !invert);
+ return 0;
+ }
+
+ if (!set && !CHECK_FLAG(peer->af_flags[afi][safi], flag)) {
+ COND_FLAG(peer->af_flags_override[afi][safi], flag,
+ invert);
+ return 0;
+ }
+ }
+
+ /*
+ * For EVPN we implicitly set the NEXTHOP_UNCHANGED flag,
+ * if we are setting/unsetting flags which conflict with this flag
+ * handle accordingly
+ */
+ if (afi == AFI_L2VPN && safi == SAFI_EVPN) {
+ if (set) {
+
+ /*
+ * if we are setting NEXTHOP_SELF, we need to unset the
+ * NEXTHOP_UNCHANGED flag
+ */
+ if (CHECK_FLAG(flag, PEER_FLAG_NEXTHOP_SELF) ||
+ CHECK_FLAG(flag, PEER_FLAG_FORCE_NEXTHOP_SELF))
+ UNSET_FLAG(peer->af_flags[afi][safi],
+ PEER_FLAG_NEXTHOP_UNCHANGED);
+ } else {
+
+ /*
+ * if we are unsetting NEXTHOP_SELF, we need to set the
+ * NEXTHOP_UNCHANGED flag to reset the defaults for EVPN
+ */
+ if (CHECK_FLAG(flag, PEER_FLAG_NEXTHOP_SELF) ||
+ CHECK_FLAG(flag, PEER_FLAG_FORCE_NEXTHOP_SELF))
+ SET_FLAG(peer->af_flags[afi][safi],
+ PEER_FLAG_NEXTHOP_UNCHANGED);
+ }
+ }
+
+ /*
+ * If the peer is a route server client let's not
+ * muck with the nexthop on the way out the door
+ */
+ if (flag & PEER_FLAG_RSERVER_CLIENT) {
+ if (set)
+ SET_FLAG(peer->af_flags[afi][safi],
+ PEER_FLAG_NEXTHOP_UNCHANGED);
+ else
+ UNSET_FLAG(peer->af_flags[afi][safi],
+ PEER_FLAG_NEXTHOP_UNCHANGED);
+ }
+
+ /* Inherit from peer-group or set/unset flags accordingly. */
+ if (peer_group_active(peer) && set == invert)
+ peer_af_flag_inherit(peer, afi, safi, flag);
+ else
+ COND_FLAG(peer->af_flags[afi][safi], flag, set);
+
+ /* Execute action when peer is established. */
+ if (!CHECK_FLAG(peer->sflags, PEER_STATUS_GROUP)
+ && peer_established(peer)) {
+ if (!set && flag == PEER_FLAG_SOFT_RECONFIG)
+ bgp_clear_adj_in(peer, afi, safi);
+ else {
+ if (flag == PEER_FLAG_REFLECTOR_CLIENT)
+ peer->last_reset = PEER_DOWN_RR_CLIENT_CHANGE;
+ else if (flag == PEER_FLAG_RSERVER_CLIENT)
+ peer->last_reset = PEER_DOWN_RS_CLIENT_CHANGE;
+ else if (flag == PEER_FLAG_ORF_PREFIX_SM)
+ peer->last_reset = PEER_DOWN_CAPABILITY_CHANGE;
+ else if (flag == PEER_FLAG_ORF_PREFIX_RM)
+ peer->last_reset = PEER_DOWN_CAPABILITY_CHANGE;
+
+ peer_change_action(peer, afi, safi, action.type);
+ }
+ }
+
+ /* Check if handling a regular peer. */
+ if (!CHECK_FLAG(peer->sflags, PEER_STATUS_GROUP)) {
+ COND_FLAG(peer->af_flags_override[afi][safi], flag,
+ set != invert);
+ } else {
+ /*
+ * Update peer-group members, unless they are explicitly
+ * overriding peer-group configuration.
+ */
+ for (ALL_LIST_ELEMENTS(peer->group->peer, node, nnode,
+ member)) {
+ /* Skip peers with overridden configuration. */
+ if (CHECK_FLAG(member->af_flags_override[afi][safi],
+ flag))
+ continue;
+
+ /* Check if only member without group is inverted. */
+ member_invert =
+ CHECK_FLAG(member->af_flags_invert[afi][safi],
+ flag)
+ && !invert;
+
+ /* Skip peers with equivalent configuration. */
+ if (set != member_invert
+ && CHECK_FLAG(member->af_flags[afi][safi], flag))
+ continue;
+
+ if (set == member_invert
+ && !CHECK_FLAG(member->af_flags[afi][safi], flag))
+ continue;
+
+ /* Update flag on peer-group member. */
+ COND_FLAG(member->af_flags[afi][safi], flag,
+ set != member_invert);
+
+ /* Execute flag action on peer-group member. */
+ if (peer_established(member)) {
+ if (!set && flag == PEER_FLAG_SOFT_RECONFIG)
+ bgp_clear_adj_in(member, afi, safi);
+ else {
+ if (flag == PEER_FLAG_REFLECTOR_CLIENT)
+ member->last_reset =
+ PEER_DOWN_RR_CLIENT_CHANGE;
+ else if (flag
+ == PEER_FLAG_RSERVER_CLIENT)
+ member->last_reset =
+ PEER_DOWN_RS_CLIENT_CHANGE;
+ else if (flag
+ == PEER_FLAG_ORF_PREFIX_SM)
+ member->last_reset =
+ PEER_DOWN_CAPABILITY_CHANGE;
+ else if (flag
+ == PEER_FLAG_ORF_PREFIX_RM)
+ member->last_reset =
+ PEER_DOWN_CAPABILITY_CHANGE;
+
+ peer_change_action(member, afi, safi,
+ action.type);
+ }
+ }
+ }
+ }
+
+ return 0;
+}
+
+int peer_af_flag_set(struct peer *peer, afi_t afi, safi_t safi, uint64_t flag)
+{
+ return peer_af_flag_modify(peer, afi, safi, flag, 1);
+}
+
+int peer_af_flag_unset(struct peer *peer, afi_t afi, safi_t safi, uint64_t flag)
+{
+ return peer_af_flag_modify(peer, afi, safi, flag, 0);
+}
+
+
+void peer_tx_shutdown_message_set(struct peer *peer, const char *msg)
+{
+ XFREE(MTYPE_PEER_TX_SHUTDOWN_MSG, peer->tx_shutdown_message);
+ peer->tx_shutdown_message =
+ msg ? XSTRDUP(MTYPE_PEER_TX_SHUTDOWN_MSG, msg) : NULL;
+}
+
+void peer_tx_shutdown_message_unset(struct peer *peer)
+{
+ XFREE(MTYPE_PEER_TX_SHUTDOWN_MSG, peer->tx_shutdown_message);
+}
+
+
+/* EBGP multihop configuration. */
+int peer_ebgp_multihop_set(struct peer *peer, int ttl)
+{
+ struct peer_group *group;
+ struct listnode *node, *nnode;
+ struct peer *peer1;
+
+ if (peer->sort == BGP_PEER_IBGP || peer->conf_if)
+ return 0;
+
+ /* is there anything to do? */
+ if (peer->ttl == ttl)
+ return 0;
+
+ /* see comment in peer_ttl_security_hops_set() */
+ if (ttl != MAXTTL) {
+ if (CHECK_FLAG(peer->sflags, PEER_STATUS_GROUP)) {
+ group = peer->group;
+ if (group->conf->gtsm_hops != BGP_GTSM_HOPS_DISABLED)
+ return BGP_ERR_NO_EBGP_MULTIHOP_WITH_TTLHACK;
+
+ for (ALL_LIST_ELEMENTS(group->peer, node, nnode,
+ peer1)) {
+ if (peer1->sort == BGP_PEER_IBGP)
+ continue;
+
+ if (peer1->gtsm_hops != BGP_GTSM_HOPS_DISABLED)
+ return BGP_ERR_NO_EBGP_MULTIHOP_WITH_TTLHACK;
+ }
+ } else {
+ if (peer->gtsm_hops != BGP_GTSM_HOPS_DISABLED)
+ return BGP_ERR_NO_EBGP_MULTIHOP_WITH_TTLHACK;
+ }
+ }
+
+ peer->ttl = ttl;
+
+ if (!CHECK_FLAG(peer->sflags, PEER_STATUS_GROUP)) {
+ if (peer->sort != BGP_PEER_IBGP) {
+ if (BGP_IS_VALID_STATE_FOR_NOTIF(peer->status))
+ bgp_notify_send(peer, BGP_NOTIFY_CEASE,
+ BGP_NOTIFY_CEASE_CONFIG_CHANGE);
+ else
+ bgp_session_reset(peer);
+
+ /* Reconfigure BFD peer with new TTL. */
+ if (peer->bfd_config)
+ bgp_peer_bfd_update_source(peer);
+ }
+ } else {
+ group = peer->group;
+ for (ALL_LIST_ELEMENTS(group->peer, node, nnode, peer)) {
+ if (peer->sort == BGP_PEER_IBGP)
+ continue;
+
+ peer->ttl = group->conf->ttl;
+
+ if (BGP_IS_VALID_STATE_FOR_NOTIF(peer->status))
+ bgp_notify_send(peer, BGP_NOTIFY_CEASE,
+ BGP_NOTIFY_CEASE_CONFIG_CHANGE);
+ else
+ bgp_session_reset(peer);
+
+ /* Reconfigure BFD peer with new TTL. */
+ if (peer->bfd_config)
+ bgp_peer_bfd_update_source(peer);
+ }
+ }
+ return 0;
+}
+
+int peer_ebgp_multihop_unset(struct peer *peer)
+{
+ struct peer_group *group;
+ struct listnode *node, *nnode;
+ int ttl;
+
+ if (peer->sort == BGP_PEER_IBGP)
+ return 0;
+
+ if (peer->gtsm_hops != BGP_GTSM_HOPS_DISABLED && peer->ttl != MAXTTL)
+ return BGP_ERR_NO_EBGP_MULTIHOP_WITH_TTLHACK;
+
+ if (peer_group_active(peer))
+ ttl = peer->group->conf->ttl;
+ else
+ ttl = BGP_DEFAULT_TTL;
+
+ if (ttl == peer->ttl)
+ return 0;
+
+ peer->ttl = ttl;
+
+ if (!CHECK_FLAG(peer->sflags, PEER_STATUS_GROUP)) {
+ if (BGP_IS_VALID_STATE_FOR_NOTIF(peer->status))
+ bgp_notify_send(peer, BGP_NOTIFY_CEASE,
+ BGP_NOTIFY_CEASE_CONFIG_CHANGE);
+ else
+ bgp_session_reset(peer);
+
+ /* Reconfigure BFD peer with new TTL. */
+ if (peer->bfd_config)
+ bgp_peer_bfd_update_source(peer);
+ } else {
+ group = peer->group;
+ for (ALL_LIST_ELEMENTS(group->peer, node, nnode, peer)) {
+ if (peer->sort == BGP_PEER_IBGP)
+ continue;
+
+ peer->ttl = BGP_DEFAULT_TTL;
+
+ if (peer->fd >= 0) {
+ if (BGP_IS_VALID_STATE_FOR_NOTIF(peer->status))
+ bgp_notify_send(
+ peer, BGP_NOTIFY_CEASE,
+ BGP_NOTIFY_CEASE_CONFIG_CHANGE);
+ else
+ bgp_session_reset(peer);
+ }
+
+ /* Reconfigure BFD peer with new TTL. */
+ if (peer->bfd_config)
+ bgp_peer_bfd_update_source(peer);
+ }
+ }
+ return 0;
+}
+
+/* Set Open Policy Role and check its correctness */
+int peer_role_set(struct peer *peer, uint8_t role, bool strict_mode)
+{
+ struct peer *member;
+ struct listnode *node, *nnode;
+
+ peer_flag_set(peer, PEER_FLAG_ROLE);
+
+ if (!CHECK_FLAG(peer->sflags, PEER_STATUS_GROUP)) {
+ if (peer->sort != BGP_PEER_EBGP)
+ return BGP_ERR_INVALID_INTERNAL_ROLE;
+
+ if (peer->local_role == role) {
+ if (CHECK_FLAG(peer->flags,
+ PEER_FLAG_ROLE_STRICT_MODE) &&
+ !strict_mode)
+ /* TODO: Is session restart needed if it was
+ * down?
+ */
+ UNSET_FLAG(peer->flags,
+ PEER_FLAG_ROLE_STRICT_MODE);
+ if (!CHECK_FLAG(peer->flags,
+ PEER_FLAG_ROLE_STRICT_MODE) &&
+ strict_mode) {
+ SET_FLAG(peer->flags,
+ PEER_FLAG_ROLE_STRICT_MODE);
+ /* Restart session to throw Role Mismatch
+ * Notification
+ */
+ if (peer->remote_role == ROLE_UNDEFINED)
+ bgp_session_reset(peer);
+ }
+ } else {
+ peer->local_role = role;
+ if (strict_mode)
+ SET_FLAG(peer->flags,
+ PEER_FLAG_ROLE_STRICT_MODE);
+ else
+ UNSET_FLAG(peer->flags,
+ PEER_FLAG_ROLE_STRICT_MODE);
+ bgp_session_reset(peer);
+ }
+
+ return CMD_SUCCESS;
+ }
+
+ peer->local_role = role;
+ for (ALL_LIST_ELEMENTS(peer->group->peer, node, nnode, member)) {
+ if (member->sort != BGP_PEER_EBGP)
+ return BGP_ERR_INVALID_INTERNAL_ROLE;
+
+ if (member->local_role == role) {
+ if (CHECK_FLAG(member->flags,
+ PEER_FLAG_ROLE_STRICT_MODE) &&
+ !strict_mode)
+ /* TODO: Is session restart needed if it was
+ * down?
+ */
+ UNSET_FLAG(member->flags,
+ PEER_FLAG_ROLE_STRICT_MODE);
+ if (!CHECK_FLAG(member->flags,
+ PEER_FLAG_ROLE_STRICT_MODE) &&
+ strict_mode) {
+ SET_FLAG(peer->flags,
+ PEER_FLAG_ROLE_STRICT_MODE);
+ SET_FLAG(member->flags,
+ PEER_FLAG_ROLE_STRICT_MODE);
+ /* Restart session to throw Role Mismatch
+ * Notification
+ */
+ if (member->remote_role == ROLE_UNDEFINED)
+ bgp_session_reset(member);
+ }
+ } else {
+ member->local_role = role;
+
+ if (strict_mode) {
+ SET_FLAG(peer->flags,
+ PEER_FLAG_ROLE_STRICT_MODE);
+ SET_FLAG(member->flags,
+ PEER_FLAG_ROLE_STRICT_MODE);
+ } else {
+ UNSET_FLAG(member->flags,
+ PEER_FLAG_ROLE_STRICT_MODE);
+ }
+ bgp_session_reset(member);
+ }
+ }
+
+ return CMD_SUCCESS;
+}
+
+int peer_role_unset(struct peer *peer)
+{
+ struct peer *member;
+ struct listnode *node, *nnode;
+
+ peer_flag_unset(peer, PEER_FLAG_ROLE);
+
+ if (!CHECK_FLAG(peer->sflags, PEER_STATUS_GROUP))
+ return peer_role_set(peer, ROLE_UNDEFINED, 0);
+
+ for (ALL_LIST_ELEMENTS(peer->group->peer, node, nnode, member))
+ peer_role_set(member, ROLE_UNDEFINED, 0);
+
+ return CMD_SUCCESS;
+}
+
+/* Neighbor description. */
+void peer_description_set(struct peer *peer, const char *desc)
+{
+ XFREE(MTYPE_PEER_DESC, peer->desc);
+
+ peer->desc = XSTRDUP(MTYPE_PEER_DESC, desc);
+}
+
+void peer_description_unset(struct peer *peer)
+{
+ XFREE(MTYPE_PEER_DESC, peer->desc);
+}
+
+/* Neighbor update-source. */
+int peer_update_source_if_set(struct peer *peer, const char *ifname)
+{
+ struct peer *member;
+ struct listnode *node, *nnode;
+
+ /* Set flag and configuration on peer. */
+ peer_flag_set(peer, PEER_FLAG_UPDATE_SOURCE);
+ if (peer->update_if) {
+ if (strcmp(peer->update_if, ifname) == 0)
+ return 0;
+ XFREE(MTYPE_PEER_UPDATE_SOURCE, peer->update_if);
+ }
+ peer->update_if = XSTRDUP(MTYPE_PEER_UPDATE_SOURCE, ifname);
+ sockunion_free(peer->update_source);
+ peer->update_source = NULL;
+
+ /* Check if handling a regular peer. */
+ if (!CHECK_FLAG(peer->sflags, PEER_STATUS_GROUP)) {
+ /* Send notification or reset peer depending on state. */
+ if (BGP_IS_VALID_STATE_FOR_NOTIF(peer->status)) {
+ peer->last_reset = PEER_DOWN_UPDATE_SOURCE_CHANGE;
+ bgp_notify_send(peer, BGP_NOTIFY_CEASE,
+ BGP_NOTIFY_CEASE_CONFIG_CHANGE);
+ } else
+ bgp_session_reset(peer);
+
+ /* Apply new source configuration to BFD session. */
+ if (peer->bfd_config)
+ bgp_peer_bfd_update_source(peer);
+
+ /* Skip peer-group mechanics for regular peers. */
+ return 0;
+ }
+
+ /*
+ * Set flag and configuration on all peer-group members, unless they are
+ * explicitly overriding peer-group configuration.
+ */
+ for (ALL_LIST_ELEMENTS(peer->group->peer, node, nnode, member)) {
+ /* Skip peers with overridden configuration. */
+ if (CHECK_FLAG(member->flags_override, PEER_FLAG_UPDATE_SOURCE))
+ continue;
+
+ /* Skip peers with the same configuration. */
+ if (member->update_if) {
+ if (strcmp(member->update_if, ifname) == 0)
+ continue;
+ XFREE(MTYPE_PEER_UPDATE_SOURCE, member->update_if);
+ }
+
+ /* Set flag and configuration on peer-group member. */
+ SET_FLAG(member->flags, PEER_FLAG_UPDATE_SOURCE);
+ member->update_if = XSTRDUP(MTYPE_PEER_UPDATE_SOURCE, ifname);
+ sockunion_free(member->update_source);
+ member->update_source = NULL;
+
+ /* Send notification or reset peer depending on state. */
+ if (BGP_IS_VALID_STATE_FOR_NOTIF(member->status)) {
+ member->last_reset = PEER_DOWN_UPDATE_SOURCE_CHANGE;
+ bgp_notify_send(member, BGP_NOTIFY_CEASE,
+ BGP_NOTIFY_CEASE_CONFIG_CHANGE);
+ } else
+ bgp_session_reset(member);
+
+ /* Apply new source configuration to BFD session. */
+ if (member->bfd_config)
+ bgp_peer_bfd_update_source(member);
+ }
+
+ return 0;
+}
+
+void peer_update_source_addr_set(struct peer *peer, const union sockunion *su)
+{
+ struct peer *member;
+ struct listnode *node, *nnode;
+
+ /* Set flag and configuration on peer. */
+ peer_flag_set(peer, PEER_FLAG_UPDATE_SOURCE);
+ if (peer->update_source) {
+ if (sockunion_cmp(peer->update_source, su) == 0)
+ return;
+ sockunion_free(peer->update_source);
+ }
+ peer->update_source = sockunion_dup(su);
+ XFREE(MTYPE_PEER_UPDATE_SOURCE, peer->update_if);
+
+ /* Check if handling a regular peer. */
+ if (!CHECK_FLAG(peer->sflags, PEER_STATUS_GROUP)) {
+ /* Send notification or reset peer depending on state. */
+ if (BGP_IS_VALID_STATE_FOR_NOTIF(peer->status)) {
+ peer->last_reset = PEER_DOWN_UPDATE_SOURCE_CHANGE;
+ bgp_notify_send(peer, BGP_NOTIFY_CEASE,
+ BGP_NOTIFY_CEASE_CONFIG_CHANGE);
+ } else
+ bgp_session_reset(peer);
+
+ /* Apply new source configuration to BFD session. */
+ if (peer->bfd_config)
+ bgp_peer_bfd_update_source(peer);
+
+ /* Skip peer-group mechanics for regular peers. */
+ return;
+ }
+
+ /*
+ * Set flag and configuration on all peer-group members, unless they are
+ * explicitly overriding peer-group configuration.
+ */
+ for (ALL_LIST_ELEMENTS(peer->group->peer, node, nnode, member)) {
+ /* Skip peers with overridden configuration. */
+ if (CHECK_FLAG(member->flags_override, PEER_FLAG_UPDATE_SOURCE))
+ continue;
+
+ /* Skip peers with the same configuration. */
+ if (member->update_source) {
+ if (sockunion_cmp(member->update_source, su) == 0)
+ continue;
+ sockunion_free(member->update_source);
+ }
+
+ /* Set flag and configuration on peer-group member. */
+ SET_FLAG(member->flags, PEER_FLAG_UPDATE_SOURCE);
+ member->update_source = sockunion_dup(su);
+ XFREE(MTYPE_PEER_UPDATE_SOURCE, member->update_if);
+
+ /* Send notification or reset peer depending on state. */
+ if (BGP_IS_VALID_STATE_FOR_NOTIF(member->status)) {
+ member->last_reset = PEER_DOWN_UPDATE_SOURCE_CHANGE;
+ bgp_notify_send(member, BGP_NOTIFY_CEASE,
+ BGP_NOTIFY_CEASE_CONFIG_CHANGE);
+ } else
+ bgp_session_reset(member);
+
+ /* Apply new source configuration to BFD session. */
+ if (member->bfd_config)
+ bgp_peer_bfd_update_source(member);
+ }
+}
+
+void peer_update_source_unset(struct peer *peer)
+{
+ struct peer *member;
+ struct listnode *node, *nnode;
+
+ if (!CHECK_FLAG(peer->flags, PEER_FLAG_UPDATE_SOURCE))
+ return;
+
+ /* Inherit configuration from peer-group if peer is member. */
+ if (peer_group_active(peer)) {
+ peer_flag_inherit(peer, PEER_FLAG_UPDATE_SOURCE);
+ PEER_SU_ATTR_INHERIT(peer, peer->group, update_source);
+ PEER_STR_ATTR_INHERIT(peer, peer->group, update_if,
+ MTYPE_PEER_UPDATE_SOURCE);
+ } else {
+ /* Otherwise remove flag and configuration from peer. */
+ peer_flag_unset(peer, PEER_FLAG_UPDATE_SOURCE);
+ sockunion_free(peer->update_source);
+ peer->update_source = NULL;
+ XFREE(MTYPE_PEER_UPDATE_SOURCE, peer->update_if);
+ }
+
+ /* Check if handling a regular peer. */
+ if (!CHECK_FLAG(peer->sflags, PEER_STATUS_GROUP)) {
+ /* Send notification or reset peer depending on state. */
+ if (BGP_IS_VALID_STATE_FOR_NOTIF(peer->status)) {
+ peer->last_reset = PEER_DOWN_UPDATE_SOURCE_CHANGE;
+ bgp_notify_send(peer, BGP_NOTIFY_CEASE,
+ BGP_NOTIFY_CEASE_CONFIG_CHANGE);
+ } else
+ bgp_session_reset(peer);
+
+ /* Apply new source configuration to BFD session. */
+ if (peer->bfd_config)
+ bgp_peer_bfd_update_source(peer);
+
+ /* Skip peer-group mechanics for regular peers. */
+ return;
+ }
+
+ /*
+ * Set flag and configuration on all peer-group members, unless they are
+ * explicitly overriding peer-group configuration.
+ */
+ for (ALL_LIST_ELEMENTS(peer->group->peer, node, nnode, member)) {
+ /* Skip peers with overridden configuration. */
+ if (CHECK_FLAG(member->flags_override, PEER_FLAG_UPDATE_SOURCE))
+ continue;
+
+ /* Skip peers with the same configuration. */
+ if (!CHECK_FLAG(member->flags, PEER_FLAG_UPDATE_SOURCE)
+ && !member->update_source && !member->update_if)
+ continue;
+
+ /* Remove flag and configuration on peer-group member. */
+ UNSET_FLAG(member->flags, PEER_FLAG_UPDATE_SOURCE);
+ sockunion_free(member->update_source);
+ member->update_source = NULL;
+ XFREE(MTYPE_PEER_UPDATE_SOURCE, member->update_if);
+
+ /* Send notification or reset peer depending on state. */
+ if (BGP_IS_VALID_STATE_FOR_NOTIF(member->status)) {
+ member->last_reset = PEER_DOWN_UPDATE_SOURCE_CHANGE;
+ bgp_notify_send(member, BGP_NOTIFY_CEASE,
+ BGP_NOTIFY_CEASE_CONFIG_CHANGE);
+ } else
+ bgp_session_reset(member);
+
+ /* Apply new source configuration to BFD session. */
+ if (member->bfd_config)
+ bgp_peer_bfd_update_source(member);
+ }
+}
+
+int peer_default_originate_set(struct peer *peer, afi_t afi, safi_t safi,
+ const char *rmap, struct route_map *route_map)
+{
+ struct peer *member;
+ struct listnode *node, *nnode;
+ struct update_subgroup *subgrp;
+
+ /* Set flag and configuration on peer. */
+ peer_af_flag_set(peer, afi, safi, PEER_FLAG_DEFAULT_ORIGINATE);
+
+ subgrp = peer_subgroup(peer, afi, safi);
+
+ if (rmap) {
+ if (!peer->default_rmap[afi][safi].name
+ || strcmp(rmap, peer->default_rmap[afi][safi].name) != 0) {
+ if (peer->default_rmap[afi][safi].name)
+ XFREE(MTYPE_ROUTE_MAP_NAME,
+ peer->default_rmap[afi][safi].name);
+
+ /*
+ * When there is a change in route-map policy,
+ * this flow gets triggered. Since, the default
+ * route is already originated, the flag is set.
+ * The flag should be unset here,
+ * to trigger the flow of sending update message.
+ */
+ if (subgrp)
+ UNSET_FLAG(subgrp->sflags,
+ SUBGRP_STATUS_DEFAULT_ORIGINATE);
+
+ route_map_counter_decrement(peer->default_rmap[afi][safi].map);
+ peer->default_rmap[afi][safi].name =
+ XSTRDUP(MTYPE_ROUTE_MAP_NAME, rmap);
+ peer->default_rmap[afi][safi].map = route_map;
+ route_map_counter_increment(route_map);
+ }
+ } else if (!rmap) {
+ if (peer->default_rmap[afi][safi].name)
+ XFREE(MTYPE_ROUTE_MAP_NAME,
+ peer->default_rmap[afi][safi].name);
+
+ /*
+ * This is triggered in case of route-map deletion.
+ * The flag needs to be unset, to trigger the flow
+ * of sending an update message.
+ */
+ if (subgrp)
+ UNSET_FLAG(subgrp->sflags,
+ SUBGRP_STATUS_DEFAULT_ORIGINATE);
+
+ route_map_counter_decrement(peer->default_rmap[afi][safi].map);
+ peer->default_rmap[afi][safi].name = NULL;
+ peer->default_rmap[afi][safi].map = NULL;
+ }
+
+ /* Check if handling a regular peer. */
+ if (!CHECK_FLAG(peer->sflags, PEER_STATUS_GROUP)) {
+ /* Update peer route announcements. */
+ if (peer_established(peer) && peer->afc_nego[afi][safi]) {
+ update_group_adjust_peer(peer_af_find(peer, afi, safi));
+ bgp_default_originate(peer, afi, safi, 0);
+ bgp_announce_route(peer, afi, safi, false);
+ }
+
+ /* Skip peer-group mechanics for regular peers. */
+ return 0;
+ }
+
+ /*
+ * Set flag and configuration on all peer-group members, unless they are
+ * explicitly overriding peer-group configuration.
+ */
+ for (ALL_LIST_ELEMENTS(peer->group->peer, node, nnode, member)) {
+ /* Skip peers with overridden configuration. */
+ if (CHECK_FLAG(member->af_flags_override[afi][safi],
+ PEER_FLAG_DEFAULT_ORIGINATE))
+ continue;
+
+ /* Set flag and configuration on peer-group member. */
+ SET_FLAG(member->af_flags[afi][safi],
+ PEER_FLAG_DEFAULT_ORIGINATE);
+ if (rmap) {
+ if (member->default_rmap[afi][safi].name)
+ XFREE(MTYPE_ROUTE_MAP_NAME,
+ member->default_rmap[afi][safi].name);
+ route_map_counter_decrement(
+ member->default_rmap[afi][safi].map);
+ member->default_rmap[afi][safi].name =
+ XSTRDUP(MTYPE_ROUTE_MAP_NAME, rmap);
+ member->default_rmap[afi][safi].map = route_map;
+ route_map_counter_increment(route_map);
+ }
+
+ /* Update peer route announcements. */
+ if (peer_established(member) && member->afc_nego[afi][safi]) {
+ update_group_adjust_peer(
+ peer_af_find(member, afi, safi));
+ bgp_default_originate(member, afi, safi, 0);
+ bgp_announce_route(member, afi, safi, false);
+ }
+ }
+
+ return 0;
+}
+
+int peer_default_originate_unset(struct peer *peer, afi_t afi, safi_t safi)
+{
+ struct peer *member;
+ struct listnode *node, *nnode;
+
+ /* Inherit configuration from peer-group if peer is member. */
+ if (peer_group_active(peer)) {
+ peer_af_flag_inherit(peer, afi, safi,
+ PEER_FLAG_DEFAULT_ORIGINATE);
+ PEER_STR_ATTR_INHERIT(peer, peer->group,
+ default_rmap[afi][safi].name,
+ MTYPE_ROUTE_MAP_NAME);
+ PEER_ATTR_INHERIT(peer, peer->group,
+ default_rmap[afi][safi].map);
+ } else {
+ /* Otherwise remove flag and configuration from peer. */
+ peer_af_flag_unset(peer, afi, safi,
+ PEER_FLAG_DEFAULT_ORIGINATE);
+ if (peer->default_rmap[afi][safi].name)
+ XFREE(MTYPE_ROUTE_MAP_NAME,
+ peer->default_rmap[afi][safi].name);
+ route_map_counter_decrement(peer->default_rmap[afi][safi].map);
+ peer->default_rmap[afi][safi].name = NULL;
+ peer->default_rmap[afi][safi].map = NULL;
+ }
+
+ /* Check if handling a regular peer. */
+ if (!CHECK_FLAG(peer->sflags, PEER_STATUS_GROUP)) {
+ /* Update peer route announcements. */
+ if (peer_established(peer) && peer->afc_nego[afi][safi]) {
+ update_group_adjust_peer(peer_af_find(peer, afi, safi));
+ bgp_default_originate(peer, afi, safi, 1);
+ bgp_announce_route(peer, afi, safi, false);
+ }
+
+ /* Skip peer-group mechanics for regular peers. */
+ return 0;
+ }
+
+ /*
+ * Remove flag and configuration from all peer-group members, unless
+ * they are explicitly overriding peer-group configuration.
+ */
+ for (ALL_LIST_ELEMENTS(peer->group->peer, node, nnode, member)) {
+ /* Skip peers with overridden configuration. */
+ if (CHECK_FLAG(member->af_flags_override[afi][safi],
+ PEER_FLAG_DEFAULT_ORIGINATE))
+ continue;
+
+ /* Remove flag and configuration on peer-group member. */
+ UNSET_FLAG(member->af_flags[afi][safi],
+ PEER_FLAG_DEFAULT_ORIGINATE);
+ if (member->default_rmap[afi][safi].name)
+ XFREE(MTYPE_ROUTE_MAP_NAME,
+ member->default_rmap[afi][safi].name);
+ route_map_counter_decrement(member->default_rmap[afi][safi].map);
+ member->default_rmap[afi][safi].name = NULL;
+ member->default_rmap[afi][safi].map = NULL;
+
+ /* Update peer route announcements. */
+ if (peer_established(member) && member->afc_nego[afi][safi]) {
+ update_group_adjust_peer(peer_af_find(member, afi, safi));
+ bgp_default_originate(member, afi, safi, 1);
+ bgp_announce_route(member, afi, safi, false);
+ }
+ }
+
+ return 0;
+}
+
+void peer_port_set(struct peer *peer, uint16_t port)
+{
+ peer->port = port;
+ peer_flag_set(peer, PEER_FLAG_PORT);
+}
+
+void peer_port_unset(struct peer *peer)
+{
+ peer->port = BGP_PORT_DEFAULT;
+ peer_flag_unset(peer, PEER_FLAG_PORT);
+}
+
+/* Set the TCP-MSS value in the peer structure,
+ * This gets applied only after connection reset
+ * So this value will be used in bgp_connect.
+ */
+void peer_tcp_mss_set(struct peer *peer, uint32_t tcp_mss)
+{
+ peer->tcp_mss = tcp_mss;
+ SET_FLAG(peer->flags, PEER_FLAG_TCP_MSS);
+}
+
+/* Reset the TCP-MSS value in the peer structure,
+ * This gets applied only after connection reset
+ * So this value will be used in bgp_connect.
+ */
+void peer_tcp_mss_unset(struct peer *peer)
+{
+ UNSET_FLAG(peer->flags, PEER_FLAG_TCP_MSS);
+ peer->tcp_mss = 0;
+}
+
+/*
+ * Helper function that is called after the name of the policy
+ * being used by a peer has changed (AF specific). Automatically
+ * initiates inbound or outbound processing as needed.
+ */
+static void peer_on_policy_change(struct peer *peer, afi_t afi, safi_t safi,
+ int outbound)
+{
+ if (outbound) {
+ update_group_adjust_peer(peer_af_find(peer, afi, safi));
+ if (peer_established(peer))
+ bgp_announce_route(peer, afi, safi, false);
+ } else {
+ if (!peer_established(peer))
+ return;
+
+ if (CHECK_FLAG(peer->af_flags[afi][safi],
+ PEER_FLAG_SOFT_RECONFIG))
+ bgp_soft_reconfig_in(peer, afi, safi);
+ else if (CHECK_FLAG(peer->cap, PEER_CAP_REFRESH_OLD_RCV) ||
+ CHECK_FLAG(peer->cap, PEER_CAP_REFRESH_NEW_RCV))
+ bgp_route_refresh_send(peer, afi, safi, 0, 0, 0,
+ BGP_ROUTE_REFRESH_NORMAL);
+ }
+}
+
+
+/* neighbor weight. */
+int peer_weight_set(struct peer *peer, afi_t afi, safi_t safi, uint16_t weight)
+{
+ struct peer *member;
+ struct listnode *node, *nnode;
+
+ /* Set flag and configuration on peer. */
+ peer_af_flag_set(peer, afi, safi, PEER_FLAG_WEIGHT);
+ if (peer->weight[afi][safi] != weight) {
+ peer->weight[afi][safi] = weight;
+ peer_on_policy_change(peer, afi, safi, 0);
+ }
+
+ /* Skip peer-group mechanics for regular peers. */
+ if (!CHECK_FLAG(peer->sflags, PEER_STATUS_GROUP))
+ return 0;
+
+ /*
+ * Set flag and configuration on all peer-group members, unless they are
+ * explicitly overriding peer-group configuration.
+ */
+ for (ALL_LIST_ELEMENTS(peer->group->peer, node, nnode, member)) {
+ /* Skip peers with overridden configuration. */
+ if (CHECK_FLAG(member->af_flags_override[afi][safi],
+ PEER_FLAG_WEIGHT))
+ continue;
+
+ /* Set flag and configuration on peer-group member. */
+ SET_FLAG(member->af_flags[afi][safi], PEER_FLAG_WEIGHT);
+ if (member->weight[afi][safi] != weight) {
+ member->weight[afi][safi] = weight;
+ peer_on_policy_change(member, afi, safi, 0);
+ }
+ }
+
+ return 0;
+}
+
+int peer_weight_unset(struct peer *peer, afi_t afi, safi_t safi)
+{
+ struct peer *member;
+ struct listnode *node, *nnode;
+
+ if (!CHECK_FLAG(peer->af_flags[afi][safi], PEER_FLAG_WEIGHT))
+ return 0;
+
+ /* Inherit configuration from peer-group if peer is member. */
+ if (peer_group_active(peer)) {
+ peer_af_flag_inherit(peer, afi, safi, PEER_FLAG_WEIGHT);
+ PEER_ATTR_INHERIT(peer, peer->group, weight[afi][safi]);
+
+ peer_on_policy_change(peer, afi, safi, 0);
+ return 0;
+ }
+
+ /* Remove flag and configuration from peer. */
+ peer_af_flag_unset(peer, afi, safi, PEER_FLAG_WEIGHT);
+ peer->weight[afi][safi] = 0;
+ peer_on_policy_change(peer, afi, safi, 0);
+
+ /* Skip peer-group mechanics for regular peers. */
+ if (!CHECK_FLAG(peer->sflags, PEER_STATUS_GROUP))
+ return 0;
+
+ /*
+ * Remove flag and configuration from all peer-group members, unless
+ * they are explicitly overriding peer-group configuration.
+ */
+ for (ALL_LIST_ELEMENTS(peer->group->peer, node, nnode, member)) {
+ /* Skip peers with overridden configuration. */
+ if (CHECK_FLAG(member->af_flags_override[afi][safi],
+ PEER_FLAG_WEIGHT))
+ continue;
+
+ /* Skip peers where flag is already disabled. */
+ if (!CHECK_FLAG(member->af_flags[afi][safi], PEER_FLAG_WEIGHT))
+ continue;
+
+ /* Remove flag and configuration on peer-group member. */
+ UNSET_FLAG(member->af_flags[afi][safi], PEER_FLAG_WEIGHT);
+ member->weight[afi][safi] = 0;
+ peer_on_policy_change(member, afi, safi, 0);
+ }
+
+ return 0;
+}
+
+int peer_timers_set(struct peer *peer, uint32_t keepalive, uint32_t holdtime)
+{
+ struct peer *member;
+ struct listnode *node, *nnode;
+
+ if (keepalive > UINT16_MAX)
+ return BGP_ERR_INVALID_VALUE;
+
+ if (holdtime > UINT16_MAX)
+ return BGP_ERR_INVALID_VALUE;
+
+ if (holdtime < 3 && holdtime != 0)
+ return BGP_ERR_INVALID_VALUE;
+
+ /* Set flag and configuration on peer. */
+ peer_flag_set(peer, PEER_FLAG_TIMER);
+ peer->holdtime = holdtime;
+ peer->keepalive = (keepalive < holdtime / 3 ? keepalive : holdtime / 3);
+
+ /* Skip peer-group mechanics for regular peers. */
+ if (!CHECK_FLAG(peer->sflags, PEER_STATUS_GROUP))
+ return 0;
+
+ /*
+ * Set flag and configuration on all peer-group members, unless they are
+ * explicitly overriding peer-group configuration.
+ */
+ for (ALL_LIST_ELEMENTS(peer->group->peer, node, nnode, member)) {
+ /* Skip peers with overridden configuration. */
+ if (CHECK_FLAG(member->flags_override, PEER_FLAG_TIMER))
+ continue;
+
+ /* Set flag and configuration on peer-group member. */
+ SET_FLAG(member->flags, PEER_FLAG_TIMER);
+ PEER_ATTR_INHERIT(member, peer->group, holdtime);
+ PEER_ATTR_INHERIT(member, peer->group, keepalive);
+ }
+
+ return 0;
+}
+
+int peer_timers_unset(struct peer *peer)
+{
+ struct peer *member;
+ struct listnode *node, *nnode;
+
+ /* Inherit configuration from peer-group if peer is member. */
+ if (peer_group_active(peer)) {
+ peer_flag_inherit(peer, PEER_FLAG_TIMER);
+ PEER_ATTR_INHERIT(peer, peer->group, holdtime);
+ PEER_ATTR_INHERIT(peer, peer->group, keepalive);
+ } else {
+ /* Otherwise remove flag and configuration from peer. */
+ peer_flag_unset(peer, PEER_FLAG_TIMER);
+ peer->holdtime = 0;
+ peer->keepalive = 0;
+ }
+
+ /* Skip peer-group mechanics for regular peers. */
+ if (!CHECK_FLAG(peer->sflags, PEER_STATUS_GROUP))
+ return 0;
+
+ /*
+ * Remove flag and configuration from all peer-group members, unless
+ * they are explicitly overriding peer-group configuration.
+ */
+ for (ALL_LIST_ELEMENTS(peer->group->peer, node, nnode, member)) {
+ /* Skip peers with overridden configuration. */
+ if (CHECK_FLAG(member->flags_override, PEER_FLAG_TIMER))
+ continue;
+
+ /* Remove flag and configuration on peer-group member. */
+ UNSET_FLAG(member->flags, PEER_FLAG_TIMER);
+ member->holdtime = 0;
+ member->keepalive = 0;
+ }
+
+ return 0;
+}
+
+int peer_timers_connect_set(struct peer *peer, uint32_t connect)
+{
+ struct peer *member;
+ struct listnode *node, *nnode;
+
+ if (connect > UINT16_MAX)
+ return BGP_ERR_INVALID_VALUE;
+
+ /* Set flag and configuration on peer. */
+ peer_flag_set(peer, PEER_FLAG_TIMER_CONNECT);
+ peer->connect = connect;
+ peer->v_connect = connect;
+
+ /* Skip peer-group mechanics for regular peers. */
+ if (!CHECK_FLAG(peer->sflags, PEER_STATUS_GROUP)) {
+ if (!peer_established(peer)) {
+ if (peer_active(peer))
+ BGP_EVENT_ADD(peer, BGP_Stop);
+ BGP_EVENT_ADD(peer, BGP_Start);
+ }
+ return 0;
+ }
+ /*
+ * Set flag and configuration on all peer-group members, unless they are
+ * explicitly overriding peer-group configuration.
+ */
+ for (ALL_LIST_ELEMENTS(peer->group->peer, node, nnode, member)) {
+ /* Skip peers with overridden configuration. */
+ if (CHECK_FLAG(member->flags_override, PEER_FLAG_TIMER_CONNECT))
+ continue;
+
+ /* Set flag and configuration on peer-group member. */
+ SET_FLAG(member->flags, PEER_FLAG_TIMER_CONNECT);
+ member->connect = connect;
+ member->v_connect = connect;
+
+ if (!peer_established(member)) {
+ if (peer_active(member))
+ BGP_EVENT_ADD(member, BGP_Stop);
+ BGP_EVENT_ADD(member, BGP_Start);
+ }
+ }
+
+ return 0;
+}
+
+int peer_timers_connect_unset(struct peer *peer)
+{
+ struct peer *member;
+ struct listnode *node, *nnode;
+
+ /* Inherit configuration from peer-group if peer is member. */
+ if (peer_group_active(peer)) {
+ peer_flag_inherit(peer, PEER_FLAG_TIMER_CONNECT);
+ PEER_ATTR_INHERIT(peer, peer->group, connect);
+ } else {
+ /* Otherwise remove flag and configuration from peer. */
+ peer_flag_unset(peer, PEER_FLAG_TIMER_CONNECT);
+ peer->connect = 0;
+ }
+
+ /* Set timer with fallback to default value. */
+ if (peer->connect)
+ peer->v_connect = peer->connect;
+ else
+ peer->v_connect = peer->bgp->default_connect_retry;
+
+ /* Skip peer-group mechanics for regular peers. */
+ if (!CHECK_FLAG(peer->sflags, PEER_STATUS_GROUP)) {
+ if (!peer_established(peer)) {
+ if (peer_active(peer))
+ BGP_EVENT_ADD(peer, BGP_Stop);
+ BGP_EVENT_ADD(peer, BGP_Start);
+ }
+ return 0;
+ }
+ /*
+ * Remove flag and configuration from all peer-group members, unless
+ * they are explicitly overriding peer-group configuration.
+ */
+ for (ALL_LIST_ELEMENTS(peer->group->peer, node, nnode, member)) {
+ /* Skip peers with overridden configuration. */
+ if (CHECK_FLAG(member->flags_override, PEER_FLAG_TIMER_CONNECT))
+ continue;
+
+ /* Remove flag and configuration on peer-group member. */
+ UNSET_FLAG(member->flags, PEER_FLAG_TIMER_CONNECT);
+ member->connect = 0;
+ member->v_connect = peer->bgp->default_connect_retry;
+
+ if (!peer_established(member)) {
+ if (peer_active(member))
+ BGP_EVENT_ADD(member, BGP_Stop);
+ BGP_EVENT_ADD(member, BGP_Start);
+ }
+ }
+
+ return 0;
+}
+
+int peer_advertise_interval_set(struct peer *peer, uint32_t routeadv)
+{
+ struct peer *member;
+ struct listnode *node, *nnode;
+
+ if (routeadv > 600)
+ return BGP_ERR_INVALID_VALUE;
+
+ /* Set flag and configuration on peer. */
+ peer_flag_set(peer, PEER_FLAG_ROUTEADV);
+ peer->routeadv = routeadv;
+ peer->v_routeadv = routeadv;
+
+ /* Check if handling a regular peer. */
+ if (!CHECK_FLAG(peer->sflags, PEER_STATUS_GROUP)) {
+ /* Update peer route announcements. */
+ update_group_adjust_peer_afs(peer);
+ if (peer_established(peer))
+ bgp_announce_route_all(peer);
+
+ /* Skip peer-group mechanics for regular peers. */
+ return 0;
+ }
+
+ /*
+ * Set flag and configuration on all peer-group members, unless they are
+ * explicitly overriding peer-group configuration.
+ */
+ for (ALL_LIST_ELEMENTS(peer->group->peer, node, nnode, member)) {
+ /* Skip peers with overridden configuration. */
+ if (CHECK_FLAG(member->flags_override, PEER_FLAG_ROUTEADV))
+ continue;
+
+ /* Set flag and configuration on peer-group member. */
+ SET_FLAG(member->flags, PEER_FLAG_ROUTEADV);
+ member->routeadv = routeadv;
+ member->v_routeadv = routeadv;
+
+ /* Update peer route announcements. */
+ update_group_adjust_peer_afs(member);
+ if (peer_established(member))
+ bgp_announce_route_all(member);
+ }
+
+ return 0;
+}
+
+int peer_advertise_interval_unset(struct peer *peer)
+{
+ struct peer *member;
+ struct listnode *node, *nnode;
+
+ /* Inherit configuration from peer-group if peer is member. */
+ if (peer_group_active(peer)) {
+ peer_flag_inherit(peer, PEER_FLAG_ROUTEADV);
+ PEER_ATTR_INHERIT(peer, peer->group, routeadv);
+ } else {
+ /* Otherwise remove flag and configuration from peer. */
+ peer_flag_unset(peer, PEER_FLAG_ROUTEADV);
+ peer->routeadv = 0;
+ }
+
+ /* Set timer with fallback to default value. */
+ if (peer->routeadv)
+ peer->v_routeadv = peer->routeadv;
+ else
+ peer->v_routeadv = (peer->sort == BGP_PEER_IBGP)
+ ? BGP_DEFAULT_IBGP_ROUTEADV
+ : BGP_DEFAULT_EBGP_ROUTEADV;
+
+ /* Check if handling a regular peer. */
+ if (!CHECK_FLAG(peer->sflags, PEER_STATUS_GROUP)) {
+ /* Update peer route announcements. */
+ update_group_adjust_peer_afs(peer);
+ if (peer_established(peer))
+ bgp_announce_route_all(peer);
+
+ /* Skip peer-group mechanics for regular peers. */
+ return 0;
+ }
+
+ /*
+ * Remove flag and configuration from all peer-group members, unless
+ * they are explicitly overriding peer-group configuration.
+ */
+ for (ALL_LIST_ELEMENTS(peer->group->peer, node, nnode, member)) {
+ /* Skip peers with overridden configuration. */
+ if (CHECK_FLAG(member->flags_override, PEER_FLAG_ROUTEADV))
+ continue;
+
+ /* Remove flag and configuration on peer-group member. */
+ UNSET_FLAG(member->flags, PEER_FLAG_ROUTEADV);
+ member->routeadv = 0;
+ member->v_routeadv = (member->sort == BGP_PEER_IBGP)
+ ? BGP_DEFAULT_IBGP_ROUTEADV
+ : BGP_DEFAULT_EBGP_ROUTEADV;
+
+ /* Update peer route announcements. */
+ update_group_adjust_peer_afs(member);
+ if (peer_established(member))
+ bgp_announce_route_all(member);
+ }
+
+ return 0;
+}
+
+/* set the peers RFC 4271 DelayOpen session attribute flag and DelayOpenTimer
+ * interval
+ */
+int peer_timers_delayopen_set(struct peer *peer, uint32_t delayopen)
+{
+ struct peer *member;
+ struct listnode *node;
+
+ /* Set peers session attribute flag and timer interval. */
+ peer_flag_set(peer, PEER_FLAG_TIMER_DELAYOPEN);
+ peer->delayopen = delayopen;
+ peer->v_delayopen = delayopen;
+
+ /* Skip group mechanics for regular peers. */
+ if (!CHECK_FLAG(peer->sflags, PEER_STATUS_GROUP))
+ return 0;
+
+ /* Set flag and configuration on all peer-group members, unless they are
+ * explicitly overriding peer-group configuration.
+ */
+ for (ALL_LIST_ELEMENTS_RO(peer->group->peer, node, member)) {
+ /* Skip peers with overridden configuration. */
+ if (CHECK_FLAG(member->flags_override,
+ PEER_FLAG_TIMER_DELAYOPEN))
+ continue;
+
+ /* Set session attribute flag and timer intervals on peer-group
+ * member.
+ */
+ SET_FLAG(member->flags, PEER_FLAG_TIMER_DELAYOPEN);
+ member->delayopen = delayopen;
+ member->v_delayopen = delayopen;
+ }
+
+ return 0;
+}
+
+/* unset the peers RFC 4271 DelayOpen session attribute flag and reset the
+ * DelayOpenTimer interval to the default value.
+ */
+int peer_timers_delayopen_unset(struct peer *peer)
+{
+ struct peer *member;
+ struct listnode *node;
+
+ /* Inherit configuration from peer-group if peer is member. */
+ if (peer_group_active(peer)) {
+ peer_flag_inherit(peer, PEER_FLAG_TIMER_DELAYOPEN);
+ PEER_ATTR_INHERIT(peer, peer->group, delayopen);
+ } else {
+ /* Otherwise remove session attribute flag and set timer
+ * interval to default value.
+ */
+ peer_flag_unset(peer, PEER_FLAG_TIMER_DELAYOPEN);
+ peer->delayopen = peer->bgp->default_delayopen;
+ }
+
+ /* Set timer value to zero */
+ peer->v_delayopen = 0;
+
+ /* Skip peer-group mechanics for regular peers. */
+ if (!CHECK_FLAG(peer->sflags, PEER_STATUS_GROUP))
+ return 0;
+
+ /* Remove flag and configuration from all peer-group members, unless
+ * they are explicitly overriding peer-group configuration.
+ */
+ for (ALL_LIST_ELEMENTS_RO(peer->group->peer, node, member)) {
+ /* Skip peers with overridden configuration. */
+ if (CHECK_FLAG(member->flags_override,
+ PEER_FLAG_TIMER_DELAYOPEN))
+ continue;
+
+ /* Remove session attribute flag, reset the timer interval to
+ * the default value and set the timer value to zero.
+ */
+ UNSET_FLAG(member->flags, PEER_FLAG_TIMER_DELAYOPEN);
+ member->delayopen = peer->bgp->default_delayopen;
+ member->v_delayopen = 0;
+ }
+
+ return 0;
+}
+
+/* neighbor interface */
+void peer_interface_set(struct peer *peer, const char *str)
+{
+ XFREE(MTYPE_BGP_PEER_IFNAME, peer->ifname);
+ peer->ifname = XSTRDUP(MTYPE_BGP_PEER_IFNAME, str);
+}
+
+void peer_interface_unset(struct peer *peer)
+{
+ XFREE(MTYPE_BGP_PEER_IFNAME, peer->ifname);
+}
+
+/* Allow-as in. */
+int peer_allowas_in_set(struct peer *peer, afi_t afi, safi_t safi,
+ int allow_num, int origin)
+{
+ struct peer *member;
+ struct listnode *node, *nnode;
+
+ if (!origin && (allow_num < 1 || allow_num > 10))
+ return BGP_ERR_INVALID_VALUE;
+
+ /* Set flag and configuration on peer. */
+ peer_af_flag_set(peer, afi, safi, PEER_FLAG_ALLOWAS_IN);
+ if (origin) {
+ if (peer->allowas_in[afi][safi] != 0
+ || !CHECK_FLAG(peer->af_flags[afi][safi],
+ PEER_FLAG_ALLOWAS_IN_ORIGIN)) {
+ peer_af_flag_set(peer, afi, safi,
+ PEER_FLAG_ALLOWAS_IN_ORIGIN);
+ peer->allowas_in[afi][safi] = 0;
+ peer_on_policy_change(peer, afi, safi, 0);
+ }
+ } else {
+ if (peer->allowas_in[afi][safi] != allow_num
+ || CHECK_FLAG(peer->af_flags[afi][safi],
+ PEER_FLAG_ALLOWAS_IN_ORIGIN)) {
+
+ peer_af_flag_unset(peer, afi, safi,
+ PEER_FLAG_ALLOWAS_IN_ORIGIN);
+ peer->allowas_in[afi][safi] = allow_num;
+ peer_on_policy_change(peer, afi, safi, 0);
+ }
+ }
+
+ /* Skip peer-group mechanics for regular peers. */
+ if (!CHECK_FLAG(peer->sflags, PEER_STATUS_GROUP))
+ return 0;
+
+ /*
+ * Set flag and configuration on all peer-group members, unless
+ * they are explicitly overriding peer-group configuration.
+ */
+ for (ALL_LIST_ELEMENTS(peer->group->peer, node, nnode, member)) {
+ /* Skip peers with overridden configuration. */
+ if (CHECK_FLAG(member->af_flags_override[afi][safi],
+ PEER_FLAG_ALLOWAS_IN))
+ continue;
+
+ /* Set flag and configuration on peer-group member. */
+ SET_FLAG(member->af_flags[afi][safi], PEER_FLAG_ALLOWAS_IN);
+ if (origin) {
+ if (member->allowas_in[afi][safi] != 0
+ || !CHECK_FLAG(member->af_flags[afi][safi],
+ PEER_FLAG_ALLOWAS_IN_ORIGIN)) {
+ SET_FLAG(member->af_flags[afi][safi],
+ PEER_FLAG_ALLOWAS_IN_ORIGIN);
+ member->allowas_in[afi][safi] = 0;
+ peer_on_policy_change(peer, afi, safi, 0);
+ }
+ } else {
+ if (member->allowas_in[afi][safi] != allow_num
+ || CHECK_FLAG(member->af_flags[afi][safi],
+ PEER_FLAG_ALLOWAS_IN_ORIGIN)) {
+ UNSET_FLAG(member->af_flags[afi][safi],
+ PEER_FLAG_ALLOWAS_IN_ORIGIN);
+ member->allowas_in[afi][safi] = allow_num;
+ peer_on_policy_change(peer, afi, safi, 0);
+ }
+ }
+ }
+
+ return 0;
+}
+
+int peer_allowas_in_unset(struct peer *peer, afi_t afi, safi_t safi)
+{
+ struct peer *member;
+ struct listnode *node, *nnode;
+
+ /* Skip peer if flag is already disabled. */
+ if (!CHECK_FLAG(peer->af_flags[afi][safi], PEER_FLAG_ALLOWAS_IN))
+ return 0;
+
+ /* Inherit configuration from peer-group if peer is member. */
+ if (peer_group_active(peer)) {
+ peer_af_flag_inherit(peer, afi, safi, PEER_FLAG_ALLOWAS_IN);
+ peer_af_flag_inherit(peer, afi, safi,
+ PEER_FLAG_ALLOWAS_IN_ORIGIN);
+ PEER_ATTR_INHERIT(peer, peer->group, allowas_in[afi][safi]);
+ peer_on_policy_change(peer, afi, safi, 0);
+
+ return 0;
+ }
+
+ /* Remove flag and configuration from peer. */
+ peer_af_flag_unset(peer, afi, safi, PEER_FLAG_ALLOWAS_IN);
+ peer_af_flag_unset(peer, afi, safi, PEER_FLAG_ALLOWAS_IN_ORIGIN);
+ peer->allowas_in[afi][safi] = 0;
+ peer_on_policy_change(peer, afi, safi, 0);
+
+ /* Skip peer-group mechanics if handling a regular peer. */
+ if (!CHECK_FLAG(peer->sflags, PEER_STATUS_GROUP))
+ return 0;
+
+ /*
+ * Remove flags and configuration from all peer-group members, unless
+ * they are explicitly overriding peer-group configuration.
+ */
+ for (ALL_LIST_ELEMENTS(peer->group->peer, node, nnode, member)) {
+ /* Skip peers with overridden configuration. */
+ if (CHECK_FLAG(member->af_flags_override[afi][safi],
+ PEER_FLAG_ALLOWAS_IN))
+ continue;
+
+ /* Remove flags and configuration on peer-group member. */
+ UNSET_FLAG(member->af_flags[afi][safi], PEER_FLAG_ALLOWAS_IN);
+ UNSET_FLAG(member->af_flags[afi][safi],
+ PEER_FLAG_ALLOWAS_IN_ORIGIN);
+ member->allowas_in[afi][safi] = 0;
+ peer_on_policy_change(member, afi, safi, 0);
+ }
+
+ return 0;
+}
+
+int peer_local_as_set(struct peer *peer, as_t as, bool no_prepend,
+ bool replace_as)
+{
+ bool old_no_prepend, old_replace_as;
+ struct bgp *bgp = peer->bgp;
+ struct peer *member;
+ struct listnode *node, *nnode;
+ enum bgp_peer_sort ptype = peer_sort(peer);
+
+ if (ptype != BGP_PEER_EBGP && ptype != BGP_PEER_INTERNAL)
+ return BGP_ERR_LOCAL_AS_ALLOWED_ONLY_FOR_EBGP;
+
+ if (bgp->as == as)
+ return BGP_ERR_CANNOT_HAVE_LOCAL_AS_SAME_AS;
+
+ if (peer->as == as)
+ return BGP_ERR_CANNOT_HAVE_LOCAL_AS_SAME_AS_REMOTE_AS;
+
+ /* Save previous flag states. */
+ old_no_prepend =
+ !!CHECK_FLAG(peer->flags, PEER_FLAG_LOCAL_AS_NO_PREPEND);
+ old_replace_as =
+ !!CHECK_FLAG(peer->flags, PEER_FLAG_LOCAL_AS_REPLACE_AS);
+
+ /* Set flag and configuration on peer. */
+ peer_flag_set(peer, PEER_FLAG_LOCAL_AS);
+ peer_flag_modify(peer, PEER_FLAG_LOCAL_AS_NO_PREPEND, no_prepend);
+ peer_flag_modify(peer, PEER_FLAG_LOCAL_AS_REPLACE_AS, replace_as);
+
+ if (peer->change_local_as == as && old_no_prepend == no_prepend
+ && old_replace_as == replace_as)
+ return 0;
+ peer->change_local_as = as;
+
+ /* Check if handling a regular peer. */
+ if (!CHECK_FLAG(peer->sflags, PEER_STATUS_GROUP)) {
+ /* Send notification or reset peer depending on state. */
+ if (BGP_IS_VALID_STATE_FOR_NOTIF(peer->status)) {
+ peer->last_reset = PEER_DOWN_LOCAL_AS_CHANGE;
+ bgp_notify_send(peer, BGP_NOTIFY_CEASE,
+ BGP_NOTIFY_CEASE_CONFIG_CHANGE);
+ } else
+ bgp_session_reset(peer);
+
+ /* Skip peer-group mechanics for regular peers. */
+ return 0;
+ }
+
+ /*
+ * Set flag and configuration on all peer-group members, unless they are
+ * explicitly overriding peer-group configuration.
+ */
+ for (ALL_LIST_ELEMENTS(peer->group->peer, node, nnode, member)) {
+ /* Skip peers with overridden configuration. */
+ if (CHECK_FLAG(member->flags_override, PEER_FLAG_LOCAL_AS))
+ continue;
+
+ /* Skip peers with the same configuration. */
+ old_no_prepend = CHECK_FLAG(member->flags,
+ PEER_FLAG_LOCAL_AS_NO_PREPEND);
+ old_replace_as = CHECK_FLAG(member->flags,
+ PEER_FLAG_LOCAL_AS_REPLACE_AS);
+ if (member->change_local_as == as
+ && CHECK_FLAG(member->flags, PEER_FLAG_LOCAL_AS)
+ && old_no_prepend == no_prepend
+ && old_replace_as == replace_as)
+ continue;
+
+ /* Set flag and configuration on peer-group member. */
+ SET_FLAG(member->flags, PEER_FLAG_LOCAL_AS);
+ COND_FLAG(member->flags, PEER_FLAG_LOCAL_AS_NO_PREPEND,
+ no_prepend);
+ COND_FLAG(member->flags, PEER_FLAG_LOCAL_AS_REPLACE_AS,
+ replace_as);
+ member->change_local_as = as;
+
+ /* Send notification or stop peer depending on state. */
+ if (BGP_IS_VALID_STATE_FOR_NOTIF(member->status)) {
+ member->last_reset = PEER_DOWN_LOCAL_AS_CHANGE;
+ bgp_notify_send(member, BGP_NOTIFY_CEASE,
+ BGP_NOTIFY_CEASE_CONFIG_CHANGE);
+ } else
+ BGP_EVENT_ADD(member, BGP_Stop);
+ }
+
+ return 0;
+}
+
+int peer_local_as_unset(struct peer *peer)
+{
+ struct peer *member;
+ struct listnode *node, *nnode;
+
+ if (!CHECK_FLAG(peer->flags, PEER_FLAG_LOCAL_AS))
+ return 0;
+
+ /* Inherit configuration from peer-group if peer is member. */
+ if (peer_group_active(peer)) {
+ peer_flag_inherit(peer, PEER_FLAG_LOCAL_AS);
+ peer_flag_inherit(peer, PEER_FLAG_LOCAL_AS_NO_PREPEND);
+ peer_flag_inherit(peer, PEER_FLAG_LOCAL_AS_REPLACE_AS);
+ PEER_ATTR_INHERIT(peer, peer->group, change_local_as);
+ } else {
+ /* Otherwise remove flag and configuration from peer. */
+ peer_flag_unset(peer, PEER_FLAG_LOCAL_AS);
+ peer_flag_unset(peer, PEER_FLAG_LOCAL_AS_NO_PREPEND);
+ peer_flag_unset(peer, PEER_FLAG_LOCAL_AS_REPLACE_AS);
+ peer->change_local_as = 0;
+ }
+
+ /* Check if handling a regular peer. */
+ if (!CHECK_FLAG(peer->sflags, PEER_STATUS_GROUP)) {
+ /* Send notification or stop peer depending on state. */
+ if (BGP_IS_VALID_STATE_FOR_NOTIF(peer->status)) {
+ peer->last_reset = PEER_DOWN_LOCAL_AS_CHANGE;
+ bgp_notify_send(peer, BGP_NOTIFY_CEASE,
+ BGP_NOTIFY_CEASE_CONFIG_CHANGE);
+ } else
+ BGP_EVENT_ADD(peer, BGP_Stop);
+
+ /* Skip peer-group mechanics for regular peers. */
+ return 0;
+ }
+
+ /*
+ * Remove flag and configuration from all peer-group members, unless
+ * they are explicitly overriding peer-group configuration.
+ */
+ for (ALL_LIST_ELEMENTS(peer->group->peer, node, nnode, member)) {
+ /* Skip peers with overridden configuration. */
+ if (CHECK_FLAG(member->flags_override, PEER_FLAG_LOCAL_AS))
+ continue;
+
+ /* Remove flag and configuration on peer-group member. */
+ UNSET_FLAG(member->flags, PEER_FLAG_LOCAL_AS);
+ UNSET_FLAG(member->flags, PEER_FLAG_LOCAL_AS_NO_PREPEND);
+ UNSET_FLAG(member->flags, PEER_FLAG_LOCAL_AS_REPLACE_AS);
+ member->change_local_as = 0;
+
+ /* Send notification or stop peer depending on state. */
+ if (BGP_IS_VALID_STATE_FOR_NOTIF(member->status)) {
+ member->last_reset = PEER_DOWN_LOCAL_AS_CHANGE;
+ bgp_notify_send(member, BGP_NOTIFY_CEASE,
+ BGP_NOTIFY_CEASE_CONFIG_CHANGE);
+ } else
+ bgp_session_reset(member);
+ }
+
+ return 0;
+}
+
+/* Set password for authenticating with the peer. */
+int peer_password_set(struct peer *peer, const char *password)
+{
+ struct peer *member;
+ struct listnode *node, *nnode;
+ int len = password ? strlen(password) : 0;
+ int ret = BGP_SUCCESS;
+
+ if ((len < PEER_PASSWORD_MINLEN) || (len > PEER_PASSWORD_MAXLEN))
+ return BGP_ERR_INVALID_VALUE;
+
+ /* Set flag and configuration on peer. */
+ peer_flag_set(peer, PEER_FLAG_PASSWORD);
+ if (peer->password && strcmp(peer->password, password) == 0)
+ return 0;
+ XFREE(MTYPE_PEER_PASSWORD, peer->password);
+ peer->password = XSTRDUP(MTYPE_PEER_PASSWORD, password);
+
+ /* Check if handling a regular peer. */
+ if (!CHECK_FLAG(peer->sflags, PEER_STATUS_GROUP)) {
+ /* Send notification or reset peer depending on state. */
+ if (BGP_IS_VALID_STATE_FOR_NOTIF(peer->status))
+ bgp_notify_send(peer, BGP_NOTIFY_CEASE,
+ BGP_NOTIFY_CEASE_CONFIG_CHANGE);
+ else
+ bgp_session_reset(peer);
+
+ /*
+ * Attempt to install password on socket and skip peer-group
+ * mechanics.
+ */
+ if (BGP_PEER_SU_UNSPEC(peer))
+ return BGP_SUCCESS;
+ return (bgp_md5_set(peer) >= 0) ? BGP_SUCCESS
+ : BGP_ERR_TCPSIG_FAILED;
+ }
+
+ /*
+ * Set flag and configuration on all peer-group members, unless they are
+ * explicitly overriding peer-group configuration.
+ */
+ for (ALL_LIST_ELEMENTS(peer->group->peer, node, nnode, member)) {
+ /* Skip peers with overridden configuration. */
+ if (CHECK_FLAG(member->flags_override, PEER_FLAG_PASSWORD))
+ continue;
+
+ /* Skip peers with the same password. */
+ if (member->password && strcmp(member->password, password) == 0)
+ continue;
+
+ /* Set flag and configuration on peer-group member. */
+ SET_FLAG(member->flags, PEER_FLAG_PASSWORD);
+ if (member->password)
+ XFREE(MTYPE_PEER_PASSWORD, member->password);
+ member->password = XSTRDUP(MTYPE_PEER_PASSWORD, password);
+
+ /* Send notification or reset peer depending on state. */
+ if (BGP_IS_VALID_STATE_FOR_NOTIF(member->status))
+ bgp_notify_send(member, BGP_NOTIFY_CEASE,
+ BGP_NOTIFY_CEASE_CONFIG_CHANGE);
+ else
+ bgp_session_reset(member);
+
+ /* Attempt to install password on socket. */
+ if (!BGP_PEER_SU_UNSPEC(member) && bgp_md5_set(member) < 0)
+ ret = BGP_ERR_TCPSIG_FAILED;
+ }
+
+ /* Set flag and configuration on all peer-group listen ranges */
+ struct listnode *ln;
+ struct prefix *lr;
+
+ for (ALL_LIST_ELEMENTS_RO(peer->group->listen_range[AFI_IP], ln, lr))
+ bgp_md5_set_prefix(peer->bgp, lr, password);
+ for (ALL_LIST_ELEMENTS_RO(peer->group->listen_range[AFI_IP6], ln, lr))
+ bgp_md5_set_prefix(peer->bgp, lr, password);
+
+ return ret;
+}
+
+int peer_password_unset(struct peer *peer)
+{
+ struct peer *member;
+ struct listnode *node, *nnode;
+
+ if (!CHECK_FLAG(peer->flags, PEER_FLAG_PASSWORD))
+ return 0;
+
+ /* Inherit configuration from peer-group if peer is member. */
+ if (peer_group_active(peer)) {
+ peer_flag_inherit(peer, PEER_FLAG_PASSWORD);
+ PEER_STR_ATTR_INHERIT(peer, peer->group, password,
+ MTYPE_PEER_PASSWORD);
+ } else {
+ /* Otherwise remove flag and configuration from peer. */
+ peer_flag_unset(peer, PEER_FLAG_PASSWORD);
+ XFREE(MTYPE_PEER_PASSWORD, peer->password);
+ }
+
+ /* Check if handling a regular peer. */
+ if (!CHECK_FLAG(peer->sflags, PEER_STATUS_GROUP)) {
+ /* Send notification or reset peer depending on state. */
+ if (BGP_IS_VALID_STATE_FOR_NOTIF(peer->status))
+ bgp_notify_send(peer, BGP_NOTIFY_CEASE,
+ BGP_NOTIFY_CEASE_CONFIG_CHANGE);
+ else
+ bgp_session_reset(peer);
+
+ /* Attempt to uninstall password on socket. */
+ if (!BGP_PEER_SU_UNSPEC(peer))
+ bgp_md5_unset(peer);
+ /* Skip peer-group mechanics for regular peers. */
+ return 0;
+ }
+
+ /*
+ * Remove flag and configuration from all peer-group members, unless
+ * they are explicitly overriding peer-group configuration.
+ */
+ for (ALL_LIST_ELEMENTS(peer->group->peer, node, nnode, member)) {
+ /* Skip peers with overridden configuration. */
+ if (CHECK_FLAG(member->flags_override, PEER_FLAG_PASSWORD))
+ continue;
+
+ /* Remove flag and configuration on peer-group member. */
+ UNSET_FLAG(member->flags, PEER_FLAG_PASSWORD);
+ XFREE(MTYPE_PEER_PASSWORD, member->password);
+
+ /* Send notification or reset peer depending on state. */
+ if (BGP_IS_VALID_STATE_FOR_NOTIF(member->status))
+ bgp_notify_send(member, BGP_NOTIFY_CEASE,
+ BGP_NOTIFY_CEASE_CONFIG_CHANGE);
+ else
+ bgp_session_reset(member);
+
+ /* Attempt to uninstall password on socket. */
+ if (!BGP_PEER_SU_UNSPEC(member))
+ bgp_md5_unset(member);
+ }
+
+ /* Set flag and configuration on all peer-group listen ranges */
+ struct listnode *ln;
+ struct prefix *lr;
+
+ for (ALL_LIST_ELEMENTS_RO(peer->group->listen_range[AFI_IP], ln, lr))
+ bgp_md5_unset_prefix(peer->bgp, lr);
+ for (ALL_LIST_ELEMENTS_RO(peer->group->listen_range[AFI_IP6], ln, lr))
+ bgp_md5_unset_prefix(peer->bgp, lr);
+
+ return 0;
+}
+
+
+/* Set distribute list to the peer. */
+int peer_distribute_set(struct peer *peer, afi_t afi, safi_t safi, int direct,
+ const char *name)
+{
+ struct peer *member;
+ struct bgp_filter *filter;
+ struct listnode *node, *nnode;
+
+ if (direct != FILTER_IN && direct != FILTER_OUT)
+ return BGP_ERR_INVALID_VALUE;
+
+ /* Set configuration on peer. */
+ filter = &peer->filter[afi][safi];
+ if (filter->plist[direct].name)
+ return BGP_ERR_PEER_FILTER_CONFLICT;
+ if (filter->dlist[direct].name)
+ XFREE(MTYPE_BGP_FILTER_NAME, filter->dlist[direct].name);
+ filter->dlist[direct].name = XSTRDUP(MTYPE_BGP_FILTER_NAME, name);
+ filter->dlist[direct].alist = access_list_lookup(afi, name);
+
+ /* Check if handling a regular peer. */
+ if (!CHECK_FLAG(peer->sflags, PEER_STATUS_GROUP)) {
+ /* Set override-flag and process peer route updates. */
+ SET_FLAG(peer->filter_override[afi][safi][direct],
+ PEER_FT_DISTRIBUTE_LIST);
+ peer_on_policy_change(peer, afi, safi,
+ (direct == FILTER_OUT) ? 1 : 0);
+
+ /* Skip peer-group mechanics for regular peers. */
+ return 0;
+ }
+
+ /*
+ * Set configuration on all peer-group members, un less they are
+ * explicitly overriding peer-group configuration.
+ */
+ for (ALL_LIST_ELEMENTS(peer->group->peer, node, nnode, member)) {
+ /* Skip peers with overridden configuration. */
+ if (CHECK_FLAG(member->filter_override[afi][safi][direct],
+ PEER_FT_DISTRIBUTE_LIST))
+ continue;
+
+ /* Set configuration on peer-group member. */
+ filter = &member->filter[afi][safi];
+ if (filter->dlist[direct].name)
+ XFREE(MTYPE_BGP_FILTER_NAME,
+ filter->dlist[direct].name);
+ filter->dlist[direct].name =
+ XSTRDUP(MTYPE_BGP_FILTER_NAME, name);
+ filter->dlist[direct].alist = access_list_lookup(afi, name);
+
+ /* Process peer route updates. */
+ peer_on_policy_change(member, afi, safi,
+ (direct == FILTER_OUT) ? 1 : 0);
+ }
+
+ return 0;
+}
+
+int peer_distribute_unset(struct peer *peer, afi_t afi, safi_t safi, int direct)
+{
+ struct peer *member;
+ struct bgp_filter *filter;
+ struct listnode *node, *nnode;
+
+ if (direct != FILTER_IN && direct != FILTER_OUT)
+ return BGP_ERR_INVALID_VALUE;
+
+ /* Unset override-flag unconditionally. */
+ UNSET_FLAG(peer->filter_override[afi][safi][direct],
+ PEER_FT_DISTRIBUTE_LIST);
+
+ /* Inherit configuration from peer-group if peer is member. */
+ if (peer_group_active(peer)) {
+ PEER_STR_ATTR_INHERIT(peer, peer->group,
+ filter[afi][safi].dlist[direct].name,
+ MTYPE_BGP_FILTER_NAME);
+ PEER_ATTR_INHERIT(peer, peer->group,
+ filter[afi][safi].dlist[direct].alist);
+ } else {
+ /* Otherwise remove configuration from peer. */
+ filter = &peer->filter[afi][safi];
+ if (filter->dlist[direct].name)
+ XFREE(MTYPE_BGP_FILTER_NAME,
+ filter->dlist[direct].name);
+ filter->dlist[direct].name = NULL;
+ filter->dlist[direct].alist = NULL;
+ }
+
+ /* Check if handling a regular peer. */
+ if (!CHECK_FLAG(peer->sflags, PEER_STATUS_GROUP)) {
+ /* Process peer route updates. */
+ peer_on_policy_change(peer, afi, safi,
+ (direct == FILTER_OUT) ? 1 : 0);
+
+ /* Skip peer-group mechanics for regular peers. */
+ return 0;
+ }
+
+ /*
+ * Remove configuration on all peer-group members, unless they are
+ * explicitly overriding peer-group configuration.
+ */
+ for (ALL_LIST_ELEMENTS(peer->group->peer, node, nnode, member)) {
+ /* Skip peers with overridden configuration. */
+ if (CHECK_FLAG(member->filter_override[afi][safi][direct],
+ PEER_FT_DISTRIBUTE_LIST))
+ continue;
+
+ /* Remove configuration on peer-group member. */
+ filter = &member->filter[afi][safi];
+ if (filter->dlist[direct].name)
+ XFREE(MTYPE_BGP_FILTER_NAME,
+ filter->dlist[direct].name);
+ filter->dlist[direct].name = NULL;
+ filter->dlist[direct].alist = NULL;
+
+ /* Process peer route updates. */
+ peer_on_policy_change(member, afi, safi,
+ (direct == FILTER_OUT) ? 1 : 0);
+ }
+
+ return 0;
+}
+
+/* Update distribute list. */
+static void peer_distribute_update(struct access_list *access)
+{
+ afi_t afi;
+ safi_t safi;
+ int direct;
+ struct listnode *mnode, *mnnode;
+ struct listnode *node, *nnode;
+ struct bgp *bgp;
+ struct peer *peer;
+ struct peer_group *group;
+ struct bgp_filter *filter;
+
+ for (ALL_LIST_ELEMENTS(bm->bgp, mnode, mnnode, bgp)) {
+ if (access->name)
+ update_group_policy_update(bgp,
+ BGP_POLICY_DISTRIBUTE_LIST,
+ access->name, true, 0);
+ for (ALL_LIST_ELEMENTS(bgp->peer, node, nnode, peer)) {
+ FOREACH_AFI_SAFI (afi, safi) {
+ filter = &peer->filter[afi][safi];
+
+ for (direct = FILTER_IN; direct < FILTER_MAX;
+ direct++) {
+ if (filter->dlist[direct].name)
+ filter->dlist[direct]
+ .alist = access_list_lookup(
+ afi,
+ filter->dlist[direct]
+ .name);
+ else
+ filter->dlist[direct].alist =
+ NULL;
+ }
+ }
+ }
+ for (ALL_LIST_ELEMENTS(bgp->group, node, nnode, group)) {
+ FOREACH_AFI_SAFI (afi, safi) {
+ filter = &group->conf->filter[afi][safi];
+
+ for (direct = FILTER_IN; direct < FILTER_MAX;
+ direct++) {
+ if (filter->dlist[direct].name)
+ filter->dlist[direct]
+ .alist = access_list_lookup(
+ afi,
+ filter->dlist[direct]
+ .name);
+ else
+ filter->dlist[direct].alist =
+ NULL;
+ }
+ }
+ }
+#ifdef ENABLE_BGP_VNC
+ vnc_prefix_list_update(bgp);
+#endif
+ }
+}
+
+/* Set prefix list to the peer. */
+int peer_prefix_list_set(struct peer *peer, afi_t afi, safi_t safi, int direct,
+ const char *name)
+{
+ struct peer *member;
+ struct bgp_filter *filter;
+ struct listnode *node, *nnode;
+
+ if (direct != FILTER_IN && direct != FILTER_OUT)
+ return BGP_ERR_INVALID_VALUE;
+
+ /* Set configuration on peer. */
+ filter = &peer->filter[afi][safi];
+ if (filter->dlist[direct].name)
+ return BGP_ERR_PEER_FILTER_CONFLICT;
+ if (filter->plist[direct].name)
+ XFREE(MTYPE_BGP_FILTER_NAME, filter->plist[direct].name);
+ filter->plist[direct].name = XSTRDUP(MTYPE_BGP_FILTER_NAME, name);
+ filter->plist[direct].plist = prefix_list_lookup(afi, name);
+
+ /* Check if handling a regular peer. */
+ if (!CHECK_FLAG(peer->sflags, PEER_STATUS_GROUP)) {
+ /* Set override-flag and process peer route updates. */
+ SET_FLAG(peer->filter_override[afi][safi][direct],
+ PEER_FT_PREFIX_LIST);
+ peer_on_policy_change(peer, afi, safi,
+ (direct == FILTER_OUT) ? 1 : 0);
+
+ /* Skip peer-group mechanics for regular peers. */
+ return 0;
+ }
+
+ /*
+ * Set configuration on all peer-group members, unless they are
+ * explicitly overriding peer-group configuration.
+ */
+ for (ALL_LIST_ELEMENTS(peer->group->peer, node, nnode, member)) {
+ /* Skip peers with overridden configuration. */
+ if (CHECK_FLAG(member->filter_override[afi][safi][direct],
+ PEER_FT_PREFIX_LIST))
+ continue;
+
+ /* Set configuration on peer-group member. */
+ filter = &member->filter[afi][safi];
+ if (filter->plist[direct].name)
+ XFREE(MTYPE_BGP_FILTER_NAME,
+ filter->plist[direct].name);
+ filter->plist[direct].name =
+ XSTRDUP(MTYPE_BGP_FILTER_NAME, name);
+ filter->plist[direct].plist = prefix_list_lookup(afi, name);
+
+ /* Process peer route updates. */
+ peer_on_policy_change(member, afi, safi,
+ (direct == FILTER_OUT) ? 1 : 0);
+ }
+
+ return 0;
+}
+
+int peer_prefix_list_unset(struct peer *peer, afi_t afi, safi_t safi,
+ int direct)
+{
+ struct peer *member;
+ struct bgp_filter *filter;
+ struct listnode *node, *nnode;
+
+ if (direct != FILTER_IN && direct != FILTER_OUT)
+ return BGP_ERR_INVALID_VALUE;
+
+ /* Unset override-flag unconditionally. */
+ UNSET_FLAG(peer->filter_override[afi][safi][direct],
+ PEER_FT_PREFIX_LIST);
+
+ /* Inherit configuration from peer-group if peer is member. */
+ if (peer_group_active(peer)) {
+ PEER_STR_ATTR_INHERIT(peer, peer->group,
+ filter[afi][safi].plist[direct].name,
+ MTYPE_BGP_FILTER_NAME);
+ PEER_ATTR_INHERIT(peer, peer->group,
+ filter[afi][safi].plist[direct].plist);
+ } else {
+ /* Otherwise remove configuration from peer. */
+ filter = &peer->filter[afi][safi];
+ if (filter->plist[direct].name)
+ XFREE(MTYPE_BGP_FILTER_NAME,
+ filter->plist[direct].name);
+ filter->plist[direct].name = NULL;
+ filter->plist[direct].plist = NULL;
+ }
+
+ /* Check if handling a regular peer. */
+ if (!CHECK_FLAG(peer->sflags, PEER_STATUS_GROUP)) {
+ /* Process peer route updates. */
+ peer_on_policy_change(peer, afi, safi,
+ (direct == FILTER_OUT) ? 1 : 0);
+
+ /* Skip peer-group mechanics for regular peers. */
+ return 0;
+ }
+
+ /*
+ * Remove configuration on all peer-group members, unless they are
+ * explicitly overriding peer-group configuration.
+ */
+ for (ALL_LIST_ELEMENTS(peer->group->peer, node, nnode, member)) {
+ /* Skip peers with overridden configuration. */
+ if (CHECK_FLAG(member->filter_override[afi][safi][direct],
+ PEER_FT_PREFIX_LIST))
+ continue;
+
+ /* Remove configuration on peer-group member. */
+ filter = &member->filter[afi][safi];
+ if (filter->plist[direct].name)
+ XFREE(MTYPE_BGP_FILTER_NAME,
+ filter->plist[direct].name);
+ filter->plist[direct].name = NULL;
+ filter->plist[direct].plist = NULL;
+
+ /* Process peer route updates. */
+ peer_on_policy_change(member, afi, safi,
+ (direct == FILTER_OUT) ? 1 : 0);
+ }
+
+ return 0;
+}
+
+/* Update prefix-list list. */
+static void peer_prefix_list_update(struct prefix_list *plist)
+{
+ struct listnode *mnode, *mnnode;
+ struct listnode *node, *nnode;
+ struct bgp *bgp;
+ struct peer *peer;
+ struct peer_group *group;
+ struct bgp_filter *filter;
+ afi_t afi;
+ safi_t safi;
+ int direct;
+
+ for (ALL_LIST_ELEMENTS(bm->bgp, mnode, mnnode, bgp)) {
+
+ /*
+ * Update the prefix-list on update groups.
+ */
+ update_group_policy_update(
+ bgp, BGP_POLICY_PREFIX_LIST,
+ plist ? prefix_list_name(plist) : NULL, true, 0);
+
+ for (ALL_LIST_ELEMENTS(bgp->peer, node, nnode, peer)) {
+ FOREACH_AFI_SAFI (afi, safi) {
+ filter = &peer->filter[afi][safi];
+
+ for (direct = FILTER_IN; direct < FILTER_MAX;
+ direct++) {
+ if (filter->plist[direct].name)
+ filter->plist[direct]
+ .plist = prefix_list_lookup(
+ afi,
+ filter->plist[direct]
+ .name);
+ else
+ filter->plist[direct].plist =
+ NULL;
+ }
+
+ /* If we touch prefix-list, we need to process
+ * new updates. This is important for ORF to
+ * work correctly.
+ */
+ if (CHECK_FLAG(peer->af_cap[afi][safi],
+ PEER_CAP_ORF_PREFIX_SM_ADV) &&
+ (CHECK_FLAG(peer->af_cap[afi][safi],
+ PEER_CAP_ORF_PREFIX_RM_RCV) ||
+ CHECK_FLAG(
+ peer->af_cap[afi][safi],
+ PEER_CAP_ORF_PREFIX_RM_OLD_RCV)))
+ peer_clear_soft(
+ peer, afi, safi,
+ BGP_CLEAR_SOFT_IN_ORF_PREFIX);
+ }
+ }
+ for (ALL_LIST_ELEMENTS(bgp->group, node, nnode, group)) {
+ FOREACH_AFI_SAFI (afi, safi) {
+ filter = &group->conf->filter[afi][safi];
+
+ for (direct = FILTER_IN; direct < FILTER_MAX;
+ direct++) {
+ if (filter->plist[direct].name)
+ filter->plist[direct]
+ .plist = prefix_list_lookup(
+ afi,
+ filter->plist[direct]
+ .name);
+ else
+ filter->plist[direct].plist =
+ NULL;
+ }
+ }
+ }
+ }
+}
+
+int peer_aslist_set(struct peer *peer, afi_t afi, safi_t safi, int direct,
+ const char *name)
+{
+ struct peer *member;
+ struct bgp_filter *filter;
+ struct listnode *node, *nnode;
+
+ if (direct != FILTER_IN && direct != FILTER_OUT)
+ return BGP_ERR_INVALID_VALUE;
+
+ /* Set configuration on peer. */
+ filter = &peer->filter[afi][safi];
+ if (filter->aslist[direct].name)
+ XFREE(MTYPE_BGP_FILTER_NAME, filter->aslist[direct].name);
+ filter->aslist[direct].name = XSTRDUP(MTYPE_BGP_FILTER_NAME, name);
+ filter->aslist[direct].aslist = as_list_lookup(name);
+
+ /* Check if handling a regular peer. */
+ if (!CHECK_FLAG(peer->sflags, PEER_STATUS_GROUP)) {
+ /* Set override-flag and process peer route updates. */
+ SET_FLAG(peer->filter_override[afi][safi][direct],
+ PEER_FT_FILTER_LIST);
+ peer_on_policy_change(peer, afi, safi,
+ (direct == FILTER_OUT) ? 1 : 0);
+
+ /* Skip peer-group mechanics for regular peers. */
+ return 0;
+ }
+
+ /*
+ * Set configuration on all peer-group members, unless they are
+ * explicitly overriding peer-group configuration.
+ */
+ for (ALL_LIST_ELEMENTS(peer->group->peer, node, nnode, member)) {
+ /* Skip peers with overridden configuration. */
+ if (CHECK_FLAG(member->filter_override[afi][safi][direct],
+ PEER_FT_FILTER_LIST))
+ continue;
+
+ /* Set configuration on peer-group member. */
+ filter = &member->filter[afi][safi];
+ if (filter->aslist[direct].name)
+ XFREE(MTYPE_BGP_FILTER_NAME,
+ filter->aslist[direct].name);
+ filter->aslist[direct].name =
+ XSTRDUP(MTYPE_BGP_FILTER_NAME, name);
+ filter->aslist[direct].aslist = as_list_lookup(name);
+
+ /* Process peer route updates. */
+ peer_on_policy_change(member, afi, safi,
+ (direct == FILTER_OUT) ? 1 : 0);
+ }
+
+ return 0;
+}
+
+int peer_aslist_unset(struct peer *peer, afi_t afi, safi_t safi, int direct)
+{
+ struct peer *member;
+ struct bgp_filter *filter;
+ struct listnode *node, *nnode;
+
+ if (direct != FILTER_IN && direct != FILTER_OUT)
+ return BGP_ERR_INVALID_VALUE;
+
+ /* Unset override-flag unconditionally. */
+ UNSET_FLAG(peer->filter_override[afi][safi][direct],
+ PEER_FT_FILTER_LIST);
+
+ /* Inherit configuration from peer-group if peer is member. */
+ if (peer_group_active(peer)) {
+ PEER_STR_ATTR_INHERIT(peer, peer->group,
+ filter[afi][safi].aslist[direct].name,
+ MTYPE_BGP_FILTER_NAME);
+ PEER_ATTR_INHERIT(peer, peer->group,
+ filter[afi][safi].aslist[direct].aslist);
+ } else {
+ /* Otherwise remove configuration from peer. */
+ filter = &peer->filter[afi][safi];
+ if (filter->aslist[direct].name)
+ XFREE(MTYPE_BGP_FILTER_NAME,
+ filter->aslist[direct].name);
+ filter->aslist[direct].name = NULL;
+ filter->aslist[direct].aslist = NULL;
+ }
+
+ /* Check if handling a regular peer. */
+ if (!CHECK_FLAG(peer->sflags, PEER_STATUS_GROUP)) {
+ /* Process peer route updates. */
+ peer_on_policy_change(peer, afi, safi,
+ (direct == FILTER_OUT) ? 1 : 0);
+
+ /* Skip peer-group mechanics for regular peers. */
+ return 0;
+ }
+
+ /*
+ * Remove configuration on all peer-group members, unless they are
+ * explicitly overriding peer-group configuration.
+ */
+ for (ALL_LIST_ELEMENTS(peer->group->peer, node, nnode, member)) {
+ /* Skip peers with overridden configuration. */
+ if (CHECK_FLAG(member->filter_override[afi][safi][direct],
+ PEER_FT_FILTER_LIST))
+ continue;
+
+ /* Remove configuration on peer-group member. */
+ filter = &member->filter[afi][safi];
+ if (filter->aslist[direct].name)
+ XFREE(MTYPE_BGP_FILTER_NAME,
+ filter->aslist[direct].name);
+ filter->aslist[direct].name = NULL;
+ filter->aslist[direct].aslist = NULL;
+
+ /* Process peer route updates. */
+ peer_on_policy_change(member, afi, safi,
+ (direct == FILTER_OUT) ? 1 : 0);
+ }
+
+ return 0;
+}
+
+static void peer_aslist_update(const char *aslist_name)
+{
+ afi_t afi;
+ safi_t safi;
+ int direct;
+ struct listnode *mnode, *mnnode;
+ struct listnode *node, *nnode;
+ struct bgp *bgp;
+ struct peer *peer;
+ struct peer_group *group;
+ struct bgp_filter *filter;
+
+ for (ALL_LIST_ELEMENTS(bm->bgp, mnode, mnnode, bgp)) {
+ update_group_policy_update(bgp, BGP_POLICY_FILTER_LIST,
+ aslist_name, true, 0);
+
+ for (ALL_LIST_ELEMENTS(bgp->peer, node, nnode, peer)) {
+ FOREACH_AFI_SAFI (afi, safi) {
+ filter = &peer->filter[afi][safi];
+
+ for (direct = FILTER_IN; direct < FILTER_MAX;
+ direct++) {
+ if (filter->aslist[direct].name)
+ filter->aslist[direct]
+ .aslist = as_list_lookup(
+ filter->aslist[direct]
+ .name);
+ else
+ filter->aslist[direct].aslist =
+ NULL;
+ }
+ }
+ }
+ for (ALL_LIST_ELEMENTS(bgp->group, node, nnode, group)) {
+ FOREACH_AFI_SAFI (afi, safi) {
+ filter = &group->conf->filter[afi][safi];
+
+ for (direct = FILTER_IN; direct < FILTER_MAX;
+ direct++) {
+ if (filter->aslist[direct].name)
+ filter->aslist[direct]
+ .aslist = as_list_lookup(
+ filter->aslist[direct]
+ .name);
+ else
+ filter->aslist[direct].aslist =
+ NULL;
+ }
+ }
+ }
+ }
+}
+
+static void peer_aslist_add(char *aslist_name)
+{
+ peer_aslist_update(aslist_name);
+ route_map_notify_dependencies(aslist_name, RMAP_EVENT_ASLIST_ADDED);
+}
+
+static void peer_aslist_del(const char *aslist_name)
+{
+ peer_aslist_update(aslist_name);
+ route_map_notify_dependencies(aslist_name, RMAP_EVENT_ASLIST_DELETED);
+}
+
+
+int peer_route_map_set(struct peer *peer, afi_t afi, safi_t safi, int direct,
+ const char *name, struct route_map *route_map)
+{
+ struct peer *member;
+ struct bgp_filter *filter;
+ struct listnode *node, *nnode;
+
+ if (direct != RMAP_IN && direct != RMAP_OUT)
+ return BGP_ERR_INVALID_VALUE;
+
+ /* Set configuration on peer. */
+ filter = &peer->filter[afi][safi];
+ if (filter->map[direct].name) {
+ /* If the neighbor is configured with the same route-map
+ * again then, ignore the duplicate configuration.
+ */
+ if (strcmp(filter->map[direct].name, name) == 0)
+ return 0;
+
+ XFREE(MTYPE_BGP_FILTER_NAME, filter->map[direct].name);
+ }
+ route_map_counter_decrement(filter->map[direct].map);
+ filter->map[direct].name = XSTRDUP(MTYPE_BGP_FILTER_NAME, name);
+ filter->map[direct].map = route_map;
+ route_map_counter_increment(route_map);
+
+ /* Check if handling a regular peer. */
+ if (!CHECK_FLAG(peer->sflags, PEER_STATUS_GROUP)) {
+ /* Set override-flag and process peer route updates. */
+ SET_FLAG(peer->filter_override[afi][safi][direct],
+ PEER_FT_ROUTE_MAP);
+ peer_on_policy_change(peer, afi, safi,
+ (direct == RMAP_OUT) ? 1 : 0);
+
+ /* Skip peer-group mechanics for regular peers. */
+ return 0;
+ }
+
+ /*
+ * Set configuration on all peer-group members, unless they are
+ * explicitly overriding peer-group configuration.
+ */
+ for (ALL_LIST_ELEMENTS(peer->group->peer, node, nnode, member)) {
+ /* Skip peers with overridden configuration. */
+ if (CHECK_FLAG(member->filter_override[afi][safi][direct],
+ PEER_FT_ROUTE_MAP))
+ continue;
+
+ /* Set configuration on peer-group member. */
+ filter = &member->filter[afi][safi];
+ if (filter->map[direct].name)
+ XFREE(MTYPE_BGP_FILTER_NAME, filter->map[direct].name);
+ route_map_counter_decrement(filter->map[direct].map);
+ filter->map[direct].name = XSTRDUP(MTYPE_BGP_FILTER_NAME, name);
+ filter->map[direct].map = route_map;
+ route_map_counter_increment(route_map);
+
+ /* Process peer route updates. */
+ peer_on_policy_change(member, afi, safi,
+ (direct == RMAP_OUT) ? 1 : 0);
+ }
+ return 0;
+}
+
+/* Unset route-map from the peer. */
+int peer_route_map_unset(struct peer *peer, afi_t afi, safi_t safi, int direct)
+{
+ struct peer *member;
+ struct bgp_filter *filter;
+ struct listnode *node, *nnode;
+
+ if (direct != RMAP_IN && direct != RMAP_OUT)
+ return BGP_ERR_INVALID_VALUE;
+
+ /* Unset override-flag unconditionally. */
+ UNSET_FLAG(peer->filter_override[afi][safi][direct], PEER_FT_ROUTE_MAP);
+
+ /* Inherit configuration from peer-group if peer is member. */
+ if (peer_group_active(peer)) {
+ PEER_STR_ATTR_INHERIT(peer, peer->group,
+ filter[afi][safi].map[direct].name,
+ MTYPE_BGP_FILTER_NAME);
+ PEER_ATTR_INHERIT(peer, peer->group,
+ filter[afi][safi].map[direct].map);
+ } else {
+ /* Otherwise remove configuration from peer. */
+ filter = &peer->filter[afi][safi];
+ if (filter->map[direct].name)
+ XFREE(MTYPE_BGP_FILTER_NAME, filter->map[direct].name);
+ route_map_counter_decrement(filter->map[direct].map);
+ filter->map[direct].name = NULL;
+ filter->map[direct].map = NULL;
+ }
+
+ /* Check if handling a regular peer. */
+ if (!CHECK_FLAG(peer->sflags, PEER_STATUS_GROUP)) {
+ /* Process peer route updates. */
+ peer_on_policy_change(peer, afi, safi,
+ (direct == RMAP_OUT) ? 1 : 0);
+
+ /* Skip peer-group mechanics for regular peers. */
+ return 0;
+ }
+
+ /*
+ * Remove configuration on all peer-group members, unless they are
+ * explicitly overriding peer-group configuration.
+ */
+ for (ALL_LIST_ELEMENTS(peer->group->peer, node, nnode, member)) {
+ /* Skip peers with overridden configuration. */
+ if (CHECK_FLAG(member->filter_override[afi][safi][direct],
+ PEER_FT_ROUTE_MAP))
+ continue;
+
+ /* Remove configuration on peer-group member. */
+ filter = &member->filter[afi][safi];
+ if (filter->map[direct].name)
+ XFREE(MTYPE_BGP_FILTER_NAME, filter->map[direct].name);
+ route_map_counter_decrement(filter->map[direct].map);
+ filter->map[direct].name = NULL;
+ filter->map[direct].map = NULL;
+
+ /* Process peer route updates. */
+ peer_on_policy_change(member, afi, safi,
+ (direct == RMAP_OUT) ? 1 : 0);
+ }
+
+ return 0;
+}
+
+/* Set unsuppress-map to the peer. */
+int peer_unsuppress_map_set(struct peer *peer, afi_t afi, safi_t safi,
+ const char *name, struct route_map *route_map)
+{
+ struct peer *member;
+ struct bgp_filter *filter;
+ struct listnode *node, *nnode;
+
+ /* Set configuration on peer. */
+ filter = &peer->filter[afi][safi];
+ if (filter->usmap.name)
+ XFREE(MTYPE_BGP_FILTER_NAME, filter->usmap.name);
+ route_map_counter_decrement(filter->usmap.map);
+ filter->usmap.name = XSTRDUP(MTYPE_BGP_FILTER_NAME, name);
+ filter->usmap.map = route_map;
+ route_map_counter_increment(route_map);
+
+ /* Check if handling a regular peer. */
+ if (!CHECK_FLAG(peer->sflags, PEER_STATUS_GROUP)) {
+ /* Set override-flag and process peer route updates. */
+ SET_FLAG(peer->filter_override[afi][safi][0],
+ PEER_FT_UNSUPPRESS_MAP);
+ peer_on_policy_change(peer, afi, safi, 1);
+
+ /* Skip peer-group mechanics for regular peers. */
+ return 0;
+ }
+
+ /*
+ * Set configuration on all peer-group members, unless they are
+ * explicitly overriding peer-group configuration.
+ */
+ for (ALL_LIST_ELEMENTS(peer->group->peer, node, nnode, member)) {
+ /* Skip peers with overridden configuration. */
+ if (CHECK_FLAG(member->filter_override[afi][safi][0],
+ PEER_FT_UNSUPPRESS_MAP))
+ continue;
+
+ /* Set configuration on peer-group member. */
+ filter = &member->filter[afi][safi];
+ if (filter->usmap.name)
+ XFREE(MTYPE_BGP_FILTER_NAME, filter->usmap.name);
+ route_map_counter_decrement(filter->usmap.map);
+ filter->usmap.name = XSTRDUP(MTYPE_BGP_FILTER_NAME, name);
+ filter->usmap.map = route_map;
+ route_map_counter_increment(route_map);
+
+ /* Process peer route updates. */
+ peer_on_policy_change(member, afi, safi, 1);
+ }
+
+ return 0;
+}
+
+/* Unset route-map from the peer. */
+int peer_unsuppress_map_unset(struct peer *peer, afi_t afi, safi_t safi)
+{
+ struct peer *member;
+ struct bgp_filter *filter;
+ struct listnode *node, *nnode;
+
+ /* Unset override-flag unconditionally. */
+ UNSET_FLAG(peer->filter_override[afi][safi][0], PEER_FT_UNSUPPRESS_MAP);
+
+ /* Inherit configuration from peer-group if peer is member. */
+ if (peer_group_active(peer)) {
+ PEER_STR_ATTR_INHERIT(peer, peer->group,
+ filter[afi][safi].usmap.name,
+ MTYPE_BGP_FILTER_NAME);
+ PEER_ATTR_INHERIT(peer, peer->group,
+ filter[afi][safi].usmap.map);
+ } else {
+ /* Otherwise remove configuration from peer. */
+ filter = &peer->filter[afi][safi];
+ if (filter->usmap.name)
+ XFREE(MTYPE_BGP_FILTER_NAME, filter->usmap.name);
+ route_map_counter_decrement(filter->usmap.map);
+ filter->usmap.name = NULL;
+ filter->usmap.map = NULL;
+ }
+
+ /* Check if handling a regular peer. */
+ if (!CHECK_FLAG(peer->sflags, PEER_STATUS_GROUP)) {
+ /* Process peer route updates. */
+ peer_on_policy_change(peer, afi, safi, 1);
+
+ /* Skip peer-group mechanics for regular peers. */
+ return 0;
+ }
+
+ /*
+ * Remove configuration on all peer-group members, unless they are
+ * explicitly overriding peer-group configuration.
+ */
+ for (ALL_LIST_ELEMENTS(peer->group->peer, node, nnode, member)) {
+ /* Skip peers with overridden configuration. */
+ if (CHECK_FLAG(member->filter_override[afi][safi][0],
+ PEER_FT_UNSUPPRESS_MAP))
+ continue;
+
+ /* Remove configuration on peer-group member. */
+ filter = &member->filter[afi][safi];
+ if (filter->usmap.name)
+ XFREE(MTYPE_BGP_FILTER_NAME, filter->usmap.name);
+ route_map_counter_decrement(filter->usmap.map);
+ filter->usmap.name = NULL;
+ filter->usmap.map = NULL;
+
+ /* Process peer route updates. */
+ peer_on_policy_change(member, afi, safi, 1);
+ }
+
+ return 0;
+}
+
+static void peer_advertise_map_filter_update(struct peer *peer, afi_t afi,
+ safi_t safi, const char *amap_name,
+ struct route_map *amap,
+ const char *cmap_name,
+ struct route_map *cmap,
+ bool condition, bool set)
+{
+ struct bgp_filter *filter;
+ bool filter_exists = false;
+
+ filter = &peer->filter[afi][safi];
+
+ /* advertise-map is already configured. */
+ if (filter->advmap.aname) {
+ filter_exists = true;
+ XFREE(MTYPE_BGP_FILTER_NAME, filter->advmap.aname);
+ XFREE(MTYPE_BGP_FILTER_NAME, filter->advmap.cname);
+ }
+
+ route_map_counter_decrement(filter->advmap.amap);
+
+ /* Removed advertise-map configuration */
+ if (!set) {
+ memset(&filter->advmap, 0, sizeof(filter->advmap));
+
+ /* decrement condition_filter_count delete timer if
+ * this is the last advertise-map to be removed.
+ */
+ if (filter_exists)
+ bgp_conditional_adv_disable(peer, afi, safi);
+
+ /* Process peer route updates. */
+ peer_on_policy_change(peer, afi, safi, 1);
+
+ return;
+ }
+
+ /* Update filter data with newly configured values. */
+ filter->advmap.aname = XSTRDUP(MTYPE_BGP_FILTER_NAME, amap_name);
+ filter->advmap.cname = XSTRDUP(MTYPE_BGP_FILTER_NAME, cmap_name);
+ filter->advmap.amap = amap;
+ filter->advmap.cmap = cmap;
+ filter->advmap.condition = condition;
+ route_map_counter_increment(filter->advmap.amap);
+ peer->advmap_config_change[afi][safi] = true;
+
+ /* Increment condition_filter_count and/or create timer. */
+ if (!filter_exists) {
+ filter->advmap.update_type = UPDATE_TYPE_ADVERTISE;
+ bgp_conditional_adv_enable(peer, afi, safi);
+ }
+
+ /* Process peer route updates. */
+ peer_on_policy_change(peer, afi, safi, 1);
+}
+
+/* Set advertise-map to the peer. */
+int peer_advertise_map_set(struct peer *peer, afi_t afi, safi_t safi,
+ const char *advertise_name,
+ struct route_map *advertise_map,
+ const char *condition_name,
+ struct route_map *condition_map, bool condition)
+{
+ struct peer *member;
+ struct listnode *node, *nnode;
+
+ /* Set configuration on peer. */
+ peer_advertise_map_filter_update(peer, afi, safi, advertise_name,
+ advertise_map, condition_name,
+ condition_map, condition, true);
+
+ /* Check if handling a regular peer & Skip peer-group mechanics. */
+ if (!CHECK_FLAG(peer->sflags, PEER_STATUS_GROUP)) {
+ /* Set override-flag and process peer route updates. */
+ SET_FLAG(peer->filter_override[afi][safi][RMAP_OUT],
+ PEER_FT_ADVERTISE_MAP);
+ return 0;
+ }
+
+ /*
+ * Set configuration on all peer-group members, unless they are
+ * explicitly overriding peer-group configuration.
+ */
+ for (ALL_LIST_ELEMENTS(peer->group->peer, node, nnode, member)) {
+ /* Skip peers with overridden configuration. */
+ if (CHECK_FLAG(member->filter_override[afi][safi][RMAP_OUT],
+ PEER_FT_ADVERTISE_MAP))
+ continue;
+
+ /* Set configuration on peer-group member. */
+ peer_advertise_map_filter_update(
+ member, afi, safi, advertise_name, advertise_map,
+ condition_name, condition_map, condition, true);
+ }
+
+ return 0;
+}
+
+/* Unset advertise-map from the peer. */
+int peer_advertise_map_unset(struct peer *peer, afi_t afi, safi_t safi,
+ const char *advertise_name,
+ struct route_map *advertise_map,
+ const char *condition_name,
+ struct route_map *condition_map, bool condition)
+{
+ struct peer *member;
+ struct listnode *node, *nnode;
+
+ /* advertise-map is not configured */
+ if (!peer->filter[afi][safi].advmap.aname)
+ return 0;
+
+ /* Unset override-flag unconditionally. */
+ UNSET_FLAG(peer->filter_override[afi][safi][RMAP_OUT],
+ PEER_FT_ADVERTISE_MAP);
+
+ /* Inherit configuration from peer-group if peer is member. */
+ if (peer_group_active(peer)) {
+ PEER_STR_ATTR_INHERIT(peer, peer->group,
+ filter[afi][safi].advmap.aname,
+ MTYPE_BGP_FILTER_NAME);
+ PEER_ATTR_INHERIT(peer, peer->group,
+ filter[afi][safi].advmap.amap);
+ } else
+ peer_advertise_map_filter_update(
+ peer, afi, safi, advertise_name, advertise_map,
+ condition_name, condition_map, condition, false);
+
+ /* Check if handling a regular peer and skip peer-group mechanics. */
+ if (!CHECK_FLAG(peer->sflags, PEER_STATUS_GROUP)) {
+ /* Process peer route updates. */
+ if (BGP_DEBUG(update, UPDATE_OUT))
+ zlog_debug("%s: Send normal update to %s for %s",
+ __func__, peer->host,
+ get_afi_safi_str(afi, safi, false));
+
+ return 0;
+ }
+
+ /*
+ * Remove configuration on all peer-group members, unless they are
+ * explicitly overriding peer-group configuration.
+ */
+ for (ALL_LIST_ELEMENTS(peer->group->peer, node, nnode, member)) {
+ /* Skip peers with overridden configuration. */
+ if (CHECK_FLAG(member->filter_override[afi][safi][RMAP_OUT],
+ PEER_FT_ADVERTISE_MAP))
+ continue;
+ /* Remove configuration on peer-group member. */
+ peer_advertise_map_filter_update(
+ member, afi, safi, advertise_name, advertise_map,
+ condition_name, condition_map, condition, false);
+
+ /* Process peer route updates. */
+ if (BGP_DEBUG(update, UPDATE_OUT))
+ zlog_debug("%s: Send normal update to %s for %s ",
+ __func__, member->host,
+ get_afi_safi_str(afi, safi, false));
+ }
+
+ return 0;
+}
+
+static bool peer_maximum_prefix_clear_overflow(struct peer *peer)
+{
+ if (!CHECK_FLAG(peer->sflags, PEER_STATUS_PREFIX_OVERFLOW))
+ return false;
+
+ UNSET_FLAG(peer->sflags, PEER_STATUS_PREFIX_OVERFLOW);
+ if (peer->t_pmax_restart) {
+ THREAD_OFF(peer->t_pmax_restart);
+ if (bgp_debug_neighbor_events(peer))
+ zlog_debug(
+ "%pBP Maximum-prefix restart timer cancelled",
+ peer);
+ }
+ BGP_EVENT_ADD(peer, BGP_Start);
+ return true;
+}
+
+int peer_maximum_prefix_set(struct peer *peer, afi_t afi, safi_t safi,
+ uint32_t max, uint8_t threshold, int warning,
+ uint16_t restart, bool force)
+{
+ struct peer *member;
+ struct listnode *node, *nnode;
+
+ /* Set flags and configuration on peer. */
+ peer_af_flag_set(peer, afi, safi, PEER_FLAG_MAX_PREFIX);
+
+ if (force)
+ peer_af_flag_set(peer, afi, safi, PEER_FLAG_MAX_PREFIX_FORCE);
+ else
+ peer_af_flag_unset(peer, afi, safi, PEER_FLAG_MAX_PREFIX_FORCE);
+
+ if (warning)
+ peer_af_flag_set(peer, afi, safi, PEER_FLAG_MAX_PREFIX_WARNING);
+ else
+ peer_af_flag_unset(peer, afi, safi,
+ PEER_FLAG_MAX_PREFIX_WARNING);
+
+ peer->pmax[afi][safi] = max;
+ peer->pmax_threshold[afi][safi] = threshold;
+ peer->pmax_restart[afi][safi] = restart;
+
+ /* Check if handling a regular peer. */
+ if (!CHECK_FLAG(peer->sflags, PEER_STATUS_GROUP)) {
+ /* Re-check if peer violates maximum-prefix. */
+ if ((peer_established(peer)) && (peer->afc[afi][safi]))
+ bgp_maximum_prefix_overflow(peer, afi, safi, 1);
+
+ /* Skip peer-group mechanics for regular peers. */
+ return 0;
+ }
+
+ /*
+ * Set flags and configuration on all peer-group members, unless they
+ * are explicitly overriding peer-group configuration.
+ */
+ for (ALL_LIST_ELEMENTS(peer->group->peer, node, nnode, member)) {
+ /* Skip peers with overridden configuration. */
+ if (CHECK_FLAG(member->af_flags_override[afi][safi],
+ PEER_FLAG_MAX_PREFIX))
+ continue;
+
+ /* Set flag and configuration on peer-group member. */
+ member->pmax[afi][safi] = max;
+ member->pmax_threshold[afi][safi] = threshold;
+ member->pmax_restart[afi][safi] = restart;
+
+ if (force)
+ SET_FLAG(member->af_flags[afi][safi],
+ PEER_FLAG_MAX_PREFIX_FORCE);
+ else
+ UNSET_FLAG(member->af_flags[afi][safi],
+ PEER_FLAG_MAX_PREFIX_FORCE);
+
+ if (warning)
+ SET_FLAG(member->af_flags[afi][safi],
+ PEER_FLAG_MAX_PREFIX_WARNING);
+ else
+ UNSET_FLAG(member->af_flags[afi][safi],
+ PEER_FLAG_MAX_PREFIX_WARNING);
+
+ /* Re-check if peer violates maximum-prefix. */
+ if ((peer_established(member)) && (member->afc[afi][safi]))
+ bgp_maximum_prefix_overflow(member, afi, safi, 1);
+ }
+
+ return 0;
+}
+
+int peer_maximum_prefix_unset(struct peer *peer, afi_t afi, safi_t safi)
+{
+ /* Inherit configuration from peer-group if peer is member. */
+ if (peer_group_active(peer)) {
+ peer_af_flag_inherit(peer, afi, safi, PEER_FLAG_MAX_PREFIX);
+ peer_af_flag_inherit(peer, afi, safi,
+ PEER_FLAG_MAX_PREFIX_FORCE);
+ peer_af_flag_inherit(peer, afi, safi,
+ PEER_FLAG_MAX_PREFIX_WARNING);
+ PEER_ATTR_INHERIT(peer, peer->group, pmax[afi][safi]);
+ PEER_ATTR_INHERIT(peer, peer->group, pmax_threshold[afi][safi]);
+ PEER_ATTR_INHERIT(peer, peer->group, pmax_restart[afi][safi]);
+
+ return 0;
+ }
+
+ /* Remove flags and configuration from peer. */
+ peer_af_flag_unset(peer, afi, safi, PEER_FLAG_MAX_PREFIX);
+ peer_af_flag_unset(peer, afi, safi, PEER_FLAG_MAX_PREFIX_FORCE);
+ peer_af_flag_unset(peer, afi, safi, PEER_FLAG_MAX_PREFIX_WARNING);
+ peer->pmax[afi][safi] = 0;
+ peer->pmax_threshold[afi][safi] = 0;
+ peer->pmax_restart[afi][safi] = 0;
+
+ /*
+ * Remove flags and configuration from all peer-group members, unless
+ * they are explicitly overriding peer-group configuration.
+ */
+ if (CHECK_FLAG(peer->sflags, PEER_STATUS_GROUP)) {
+ struct peer *member;
+ struct listnode *node;
+
+ for (ALL_LIST_ELEMENTS_RO(peer->group->peer, node, member)) {
+ /* Skip peers with overridden configuration. */
+ if (CHECK_FLAG(member->af_flags_override[afi][safi],
+ PEER_FLAG_MAX_PREFIX))
+ continue;
+
+ /* Remove flag and configuration on peer-group member.
+ */
+ UNSET_FLAG(member->af_flags[afi][safi],
+ PEER_FLAG_MAX_PREFIX);
+ UNSET_FLAG(member->af_flags[afi][safi],
+ PEER_FLAG_MAX_PREFIX_FORCE);
+ UNSET_FLAG(member->af_flags[afi][safi],
+ PEER_FLAG_MAX_PREFIX_WARNING);
+ member->pmax[afi][safi] = 0;
+ member->pmax_threshold[afi][safi] = 0;
+ member->pmax_restart[afi][safi] = 0;
+
+ peer_maximum_prefix_clear_overflow(member);
+ }
+ } else {
+ peer_maximum_prefix_clear_overflow(peer);
+ }
+
+ return 0;
+}
+
+void peer_maximum_prefix_out_refresh_routes(struct peer *peer, afi_t afi,
+ safi_t safi)
+{
+ update_group_adjust_peer(peer_af_find(peer, afi, safi));
+
+ if (peer_established(peer))
+ bgp_announce_route(peer, afi, safi, false);
+}
+
+int peer_maximum_prefix_out_set(struct peer *peer, afi_t afi, safi_t safi,
+ uint32_t max)
+{
+ struct peer *member;
+ struct listnode *node, *nnode;
+
+ /* Set flag on peer and peer-group member if any */
+ peer_af_flag_set(peer, afi, safi, PEER_FLAG_MAX_PREFIX_OUT);
+ /* Set configuration on peer. */
+ peer->pmax_out[afi][safi] = max;
+
+ /* Check if handling a regular peer. */
+ if (!CHECK_FLAG(peer->sflags, PEER_STATUS_GROUP)) {
+ /* Skip peer-group mechanics for regular peers. */
+ peer_maximum_prefix_out_refresh_routes(peer, afi, safi);
+ return 0;
+ }
+
+ /*
+ * Set flag and configuration on all peer-group members, unless they
+ * are explicitly overriding peer-group configuration.
+ */
+ for (ALL_LIST_ELEMENTS(peer->group->peer, node, nnode, member)) {
+ /* Skip peers with overridden configuration. */
+ if (CHECK_FLAG(member->af_flags_override[afi][safi],
+ PEER_FLAG_MAX_PREFIX_OUT))
+ continue;
+
+ /* Set configuration on peer-group member. */
+ member->pmax_out[afi][safi] = max;
+
+ peer_maximum_prefix_out_refresh_routes(member, afi, safi);
+ }
+ return 0;
+}
+
+int peer_maximum_prefix_out_unset(struct peer *peer, afi_t afi, safi_t safi)
+{
+ struct peer *member;
+ struct listnode *node;
+ /* Inherit configuration from peer-group if peer is member. */
+ if (peer_group_active(peer)) {
+ peer_af_flag_inherit(peer, afi, safi, PEER_FLAG_MAX_PREFIX_OUT);
+ PEER_ATTR_INHERIT(peer, peer->group, pmax_out[afi][safi]);
+
+ peer_maximum_prefix_out_refresh_routes(peer, afi, safi);
+ return 0;
+ }
+
+ /* Remove flag and configuration from peer. */
+ peer_af_flag_unset(peer, afi, safi, PEER_FLAG_MAX_PREFIX_OUT);
+ peer->pmax_out[afi][safi] = 0;
+
+ /* Check if handling a regular peer. */
+ if (!CHECK_FLAG(peer->sflags, PEER_STATUS_GROUP)) {
+ /* Skip peer-group mechanics for regular peers. */
+ peer_maximum_prefix_out_refresh_routes(peer, afi, safi);
+ return 0;
+ }
+
+ /*
+ * Remove flag and configuration from all peer-group members, unless
+ * they are explicitly overriding peer-group configuration.
+ */
+ for (ALL_LIST_ELEMENTS_RO(peer->group->peer, node, member)) {
+ /* Skip peers with overridden configuration. */
+ if (CHECK_FLAG(member->af_flags_override[afi][safi],
+ PEER_FLAG_MAX_PREFIX_OUT))
+ continue;
+
+ /* Remove flag and configuration on peer-group member.
+ */
+ UNSET_FLAG(member->af_flags[afi][safi],
+ PEER_FLAG_MAX_PREFIX_OUT);
+ member->pmax_out[afi][safi] = 0;
+
+ peer_maximum_prefix_out_refresh_routes(member, afi, safi);
+ }
+ return 0;
+}
+
+int is_ebgp_multihop_configured(struct peer *peer)
+{
+ struct peer_group *group;
+ struct listnode *node, *nnode;
+ struct peer *peer1;
+
+ if (CHECK_FLAG(peer->sflags, PEER_STATUS_GROUP)) {
+ group = peer->group;
+ if ((peer_sort(peer) != BGP_PEER_IBGP)
+ && (group->conf->ttl != BGP_DEFAULT_TTL))
+ return 1;
+
+ for (ALL_LIST_ELEMENTS(group->peer, node, nnode, peer1)) {
+ if ((peer_sort(peer1) != BGP_PEER_IBGP)
+ && (peer1->ttl != BGP_DEFAULT_TTL))
+ return 1;
+ }
+ } else {
+ if ((peer_sort(peer) != BGP_PEER_IBGP)
+ && (peer->ttl != BGP_DEFAULT_TTL))
+ return 1;
+ }
+ return 0;
+}
+
+/* Set # of hops between us and BGP peer. */
+int peer_ttl_security_hops_set(struct peer *peer, int gtsm_hops)
+{
+ struct peer_group *group;
+ struct peer *gpeer;
+ struct listnode *node, *nnode;
+ int ret;
+
+ zlog_debug("%s: set gtsm_hops to %d for %s", __func__, gtsm_hops,
+ peer->host);
+
+ /* We cannot configure ttl-security hops when ebgp-multihop is already
+ set. For non peer-groups, the check is simple. For peer-groups,
+ it's
+ slightly messy, because we need to check both the peer-group
+ structure
+ and all peer-group members for any trace of ebgp-multihop
+ configuration
+ before actually applying the ttl-security rules. Cisco really made a
+ mess of this configuration parameter, and OpenBGPD got it right.
+ */
+
+ if ((peer->gtsm_hops == BGP_GTSM_HOPS_DISABLED)
+ && (peer->sort != BGP_PEER_IBGP)) {
+ if (is_ebgp_multihop_configured(peer))
+ return BGP_ERR_NO_EBGP_MULTIHOP_WITH_TTLHACK;
+
+ if (!CHECK_FLAG(peer->sflags, PEER_STATUS_GROUP)) {
+ peer->gtsm_hops = gtsm_hops;
+
+ /* Calling ebgp multihop also resets the session.
+ * On restart, NHT will get setup correctly as will the
+ * min & max ttls on the socket. The return value is
+ * irrelevant.
+ */
+ ret = peer_ebgp_multihop_set(peer, MAXTTL);
+
+ if (ret != 0)
+ return ret;
+ } else {
+ group = peer->group;
+ group->conf->gtsm_hops = gtsm_hops;
+ for (ALL_LIST_ELEMENTS(group->peer, node, nnode,
+ gpeer)) {
+ gpeer->gtsm_hops = group->conf->gtsm_hops;
+
+ /* Calling ebgp multihop also resets the
+ * session.
+ * On restart, NHT will get setup correctly as
+ * will the
+ * min & max ttls on the socket. The return
+ * value is
+ * irrelevant.
+ */
+ peer_ebgp_multihop_set(gpeer, MAXTTL);
+ }
+ }
+ } else {
+ /* Post the first gtsm setup or if its ibgp, maxttl setting
+ * isn't
+ * necessary, just set the minttl.
+ */
+ if (!CHECK_FLAG(peer->sflags, PEER_STATUS_GROUP)) {
+ peer->gtsm_hops = gtsm_hops;
+
+ if (peer->fd >= 0)
+ sockopt_minttl(peer->su.sa.sa_family, peer->fd,
+ MAXTTL + 1 - gtsm_hops);
+ if ((peer->status < Established) && peer->doppelganger
+ && (peer->doppelganger->fd >= 0))
+ sockopt_minttl(peer->su.sa.sa_family,
+ peer->doppelganger->fd,
+ MAXTTL + 1 - gtsm_hops);
+ } else {
+ group = peer->group;
+ group->conf->gtsm_hops = gtsm_hops;
+ for (ALL_LIST_ELEMENTS(group->peer, node, nnode,
+ gpeer)) {
+ gpeer->gtsm_hops = group->conf->gtsm_hops;
+
+ /* Change setting of existing peer
+ * established then change value (may break
+ * connectivity)
+ * not established yet (teardown session and
+ * restart)
+ * no session then do nothing (will get
+ * handled by next connection)
+ */
+ if (gpeer->fd >= 0
+ && gpeer->gtsm_hops
+ != BGP_GTSM_HOPS_DISABLED)
+ sockopt_minttl(
+ gpeer->su.sa.sa_family,
+ gpeer->fd,
+ MAXTTL + 1 - gpeer->gtsm_hops);
+ if ((gpeer->status < Established)
+ && gpeer->doppelganger
+ && (gpeer->doppelganger->fd >= 0))
+ sockopt_minttl(gpeer->su.sa.sa_family,
+ gpeer->doppelganger->fd,
+ MAXTTL + 1 - gtsm_hops);
+ }
+ }
+ }
+
+ return 0;
+}
+
+int peer_ttl_security_hops_unset(struct peer *peer)
+{
+ struct peer_group *group;
+ struct listnode *node, *nnode;
+ int ret = 0;
+
+ zlog_debug("%s: set gtsm_hops to zero for %s", __func__, peer->host);
+
+ /* if a peer-group member, then reset to peer-group default rather than
+ * 0 */
+ if (peer_group_active(peer))
+ peer->gtsm_hops = peer->group->conf->gtsm_hops;
+ else
+ peer->gtsm_hops = BGP_GTSM_HOPS_DISABLED;
+
+ if (!CHECK_FLAG(peer->sflags, PEER_STATUS_GROUP)) {
+ /* Invoking ebgp_multihop_set will set the TTL back to the
+ * original
+ * value as well as restting the NHT and such. The session is
+ * reset.
+ */
+ if (peer->sort == BGP_PEER_EBGP)
+ ret = peer_ebgp_multihop_unset(peer);
+ else {
+ if (peer->fd >= 0)
+ sockopt_minttl(peer->su.sa.sa_family, peer->fd,
+ 0);
+
+ if ((peer->status < Established) && peer->doppelganger
+ && (peer->doppelganger->fd >= 0))
+ sockopt_minttl(peer->su.sa.sa_family,
+ peer->doppelganger->fd, 0);
+ }
+ } else {
+ group = peer->group;
+ for (ALL_LIST_ELEMENTS(group->peer, node, nnode, peer)) {
+ peer->gtsm_hops = BGP_GTSM_HOPS_DISABLED;
+ if (peer->sort == BGP_PEER_EBGP)
+ ret = peer_ebgp_multihop_unset(peer);
+ else {
+ if (peer->fd >= 0)
+ sockopt_minttl(peer->su.sa.sa_family,
+ peer->fd, 0);
+
+ if ((peer->status < Established)
+ && peer->doppelganger
+ && (peer->doppelganger->fd >= 0))
+ sockopt_minttl(peer->su.sa.sa_family,
+ peer->doppelganger->fd,
+ 0);
+ }
+ }
+ }
+
+ return ret;
+}
+
+static void peer_reset_message_stats(struct peer *peer)
+{
+ if (peer) {
+ atomic_store_explicit(&peer->open_in, 0, memory_order_relaxed);
+ atomic_store_explicit(&peer->open_out, 0, memory_order_relaxed);
+ atomic_store_explicit(&peer->update_in, 0,
+ memory_order_relaxed);
+ atomic_store_explicit(&peer->update_out, 0,
+ memory_order_relaxed);
+ atomic_store_explicit(&peer->keepalive_in, 0,
+ memory_order_relaxed);
+ atomic_store_explicit(&peer->keepalive_out, 0,
+ memory_order_relaxed);
+ atomic_store_explicit(&peer->notify_in, 0,
+ memory_order_relaxed);
+ atomic_store_explicit(&peer->notify_out, 0,
+ memory_order_relaxed);
+ atomic_store_explicit(&peer->refresh_in, 0,
+ memory_order_relaxed);
+ atomic_store_explicit(&peer->refresh_out, 0,
+ memory_order_relaxed);
+ atomic_store_explicit(&peer->dynamic_cap_in, 0,
+ memory_order_relaxed);
+ atomic_store_explicit(&peer->dynamic_cap_out, 0,
+ memory_order_relaxed);
+ }
+}
+
+/*
+ * If peer clear is invoked in a loop for all peers on the BGP instance,
+ * it may end up freeing the doppelganger, and if this was the next node
+ * to the current node, we would end up accessing the freed next node.
+ * Pass along additional parameter which can be updated if next node
+ * is freed; only required when walking the peer list on BGP instance.
+ */
+int peer_clear(struct peer *peer, struct listnode **nnode)
+{
+ if (!CHECK_FLAG(peer->flags, PEER_FLAG_SHUTDOWN)
+ || !CHECK_FLAG(peer->bgp->flags, BGP_FLAG_SHUTDOWN)) {
+ if (peer_maximum_prefix_clear_overflow(peer))
+ return 0;
+
+ peer->v_start = BGP_INIT_START_TIMER;
+ if (BGP_IS_VALID_STATE_FOR_NOTIF(peer->status))
+ bgp_notify_send(peer, BGP_NOTIFY_CEASE,
+ BGP_NOTIFY_CEASE_ADMIN_RESET);
+ else
+ bgp_session_reset_safe(peer, nnode);
+ }
+ return 0;
+}
+
+int peer_clear_soft(struct peer *peer, afi_t afi, safi_t safi,
+ enum bgp_clear_type stype)
+{
+ struct peer_af *paf;
+
+ if (!peer_established(peer))
+ return 0;
+
+ if (!peer->afc[afi][safi])
+ return BGP_ERR_AF_UNCONFIGURED;
+
+ peer->rtt = sockopt_tcp_rtt(peer->fd);
+
+ if (stype == BGP_CLEAR_SOFT_OUT || stype == BGP_CLEAR_SOFT_BOTH) {
+ /* Clear the "neighbor x.x.x.x default-originate" flag */
+ paf = peer_af_find(peer, afi, safi);
+ if (paf && paf->subgroup
+ && CHECK_FLAG(paf->subgroup->sflags,
+ SUBGRP_STATUS_DEFAULT_ORIGINATE))
+ UNSET_FLAG(paf->subgroup->sflags,
+ SUBGRP_STATUS_DEFAULT_ORIGINATE);
+
+ bgp_announce_route(peer, afi, safi, false);
+ }
+
+ if (stype == BGP_CLEAR_SOFT_IN_ORF_PREFIX) {
+ if (CHECK_FLAG(peer->af_cap[afi][safi],
+ PEER_CAP_ORF_PREFIX_SM_ADV)
+ && (CHECK_FLAG(peer->af_cap[afi][safi],
+ PEER_CAP_ORF_PREFIX_RM_RCV)
+ || CHECK_FLAG(peer->af_cap[afi][safi],
+ PEER_CAP_ORF_PREFIX_RM_OLD_RCV))) {
+ struct bgp_filter *filter = &peer->filter[afi][safi];
+ uint8_t prefix_type;
+
+ if (CHECK_FLAG(peer->af_cap[afi][safi],
+ PEER_CAP_ORF_PREFIX_RM_RCV))
+ prefix_type = ORF_TYPE_PREFIX;
+ else
+ prefix_type = ORF_TYPE_PREFIX_OLD;
+
+ if (filter->plist[FILTER_IN].plist) {
+ if (CHECK_FLAG(peer->af_sflags[afi][safi],
+ PEER_STATUS_ORF_PREFIX_SEND))
+ bgp_route_refresh_send(
+ peer, afi, safi, prefix_type,
+ REFRESH_DEFER, 1,
+ BGP_ROUTE_REFRESH_NORMAL);
+ bgp_route_refresh_send(
+ peer, afi, safi, prefix_type,
+ REFRESH_IMMEDIATE, 0,
+ BGP_ROUTE_REFRESH_NORMAL);
+ } else {
+ if (CHECK_FLAG(peer->af_sflags[afi][safi],
+ PEER_STATUS_ORF_PREFIX_SEND))
+ bgp_route_refresh_send(
+ peer, afi, safi, prefix_type,
+ REFRESH_IMMEDIATE, 1,
+ BGP_ROUTE_REFRESH_NORMAL);
+ else
+ bgp_route_refresh_send(
+ peer, afi, safi, 0, 0, 0,
+ BGP_ROUTE_REFRESH_NORMAL);
+ }
+ return 0;
+ }
+ }
+
+ if (stype == BGP_CLEAR_SOFT_IN || stype == BGP_CLEAR_SOFT_BOTH
+ || stype == BGP_CLEAR_SOFT_IN_ORF_PREFIX) {
+ /* If neighbor has soft reconfiguration inbound flag.
+ Use Adj-RIB-In database. */
+ if (CHECK_FLAG(peer->af_flags[afi][safi],
+ PEER_FLAG_SOFT_RECONFIG))
+ bgp_soft_reconfig_in(peer, afi, safi);
+ else {
+ /* If neighbor has route refresh capability, send route
+ refresh
+ message to the peer. */
+ if (CHECK_FLAG(peer->cap, PEER_CAP_REFRESH_OLD_RCV)
+ || CHECK_FLAG(peer->cap, PEER_CAP_REFRESH_NEW_RCV))
+ bgp_route_refresh_send(
+ peer, afi, safi, 0, 0, 0,
+ BGP_ROUTE_REFRESH_NORMAL);
+ else
+ return BGP_ERR_SOFT_RECONFIG_UNCONFIGURED;
+ }
+ }
+
+ if (stype == BGP_CLEAR_MESSAGE_STATS)
+ peer_reset_message_stats(peer);
+
+ return 0;
+}
+
+/* Display peer uptime.*/
+char *peer_uptime(time_t uptime2, char *buf, size_t len, bool use_json,
+ json_object *json)
+{
+ time_t uptime1, epoch_tbuf;
+ struct tm tm;
+
+ /* If there is no connection has been done before print `never'. */
+ if (uptime2 == 0) {
+ if (use_json) {
+ json_object_string_add(json, "peerUptime", "never");
+ json_object_int_add(json, "peerUptimeMsec", 0);
+ } else
+ snprintf(buf, len, "never");
+ return buf;
+ }
+
+ /* Get current time. */
+ uptime1 = monotime(NULL);
+ uptime1 -= uptime2;
+ gmtime_r(&uptime1, &tm);
+
+ if (uptime1 < ONE_DAY_SECOND)
+ snprintf(buf, len, "%02d:%02d:%02d", tm.tm_hour, tm.tm_min,
+ tm.tm_sec);
+ else if (uptime1 < ONE_WEEK_SECOND)
+ snprintf(buf, len, "%dd%02dh%02dm", tm.tm_yday, tm.tm_hour,
+ tm.tm_min);
+ else if (uptime1 < ONE_YEAR_SECOND)
+ snprintf(buf, len, "%02dw%dd%02dh", tm.tm_yday / 7,
+ tm.tm_yday - ((tm.tm_yday / 7) * 7), tm.tm_hour);
+ else
+ snprintf(buf, len, "%02dy%02dw%dd", tm.tm_year - 70,
+ tm.tm_yday / 7,
+ tm.tm_yday - ((tm.tm_yday / 7) * 7));
+
+ if (use_json) {
+ epoch_tbuf = time(NULL) - uptime1;
+ json_object_string_add(json, "peerUptime", buf);
+ json_object_int_add(json, "peerUptimeMsec", uptime1 * 1000);
+ json_object_int_add(json, "peerUptimeEstablishedEpoch",
+ epoch_tbuf);
+ }
+
+ return buf;
+}
+
+void bgp_master_init(struct thread_master *master, const int buffer_size,
+ struct list *addresses)
+{
+ qobj_init();
+
+ memset(&bgp_master, 0, sizeof(bgp_master));
+
+ bm = &bgp_master;
+ bm->bgp = list_new();
+ bm->listen_sockets = list_new();
+ bm->port = BGP_PORT_DEFAULT;
+ bm->addresses = addresses;
+ bm->master = master;
+ bm->start_time = monotime(NULL);
+ bm->t_rmap_update = NULL;
+ bm->rmap_update_timer = RMAP_DEFAULT_UPDATE_TIMER;
+ bm->v_update_delay = BGP_UPDATE_DELAY_DEF;
+ bm->v_establish_wait = BGP_UPDATE_DELAY_DEF;
+ bm->terminating = false;
+ bm->socket_buffer = buffer_size;
+ bm->wait_for_fib = false;
+ bm->tcp_dscp = IPTOS_PREC_INTERNETCONTROL;
+
+ bgp_mac_init();
+ /* init the rd id space.
+ assign 0th index in the bitfield,
+ so that we start with id 1
+ */
+ bf_init(bm->rd_idspace, UINT16_MAX);
+ bf_assign_zero_index(bm->rd_idspace);
+
+ /* mpls label dynamic allocation pool */
+ bgp_lp_init(bm->master, &bm->labelpool);
+
+ bgp_l3nhg_init();
+ bgp_evpn_mh_init();
+ QOBJ_REG(bm, bgp_master);
+}
+
+/*
+ * Free up connected routes and interfaces for a BGP instance. Invoked upon
+ * instance delete (non-default only) or BGP exit.
+ */
+static void bgp_if_finish(struct bgp *bgp)
+{
+ struct vrf *vrf;
+ struct interface *ifp;
+
+ vrf = bgp_vrf_lookup_by_instance_type(bgp);
+
+ if (bgp->inst_type == BGP_INSTANCE_TYPE_VIEW || !vrf)
+ return;
+
+ FOR_ALL_INTERFACES (vrf, ifp) {
+ struct listnode *c_node, *c_nnode;
+ struct connected *c;
+
+ for (ALL_LIST_ELEMENTS(ifp->connected, c_node, c_nnode, c))
+ bgp_connected_delete(bgp, c);
+ }
+}
+
+static void bgp_viewvrf_autocomplete(vector comps, struct cmd_token *token)
+{
+ struct vrf *vrf = NULL;
+ struct listnode *next;
+ struct bgp *bgp;
+
+ RB_FOREACH (vrf, vrf_name_head, &vrfs_by_name)
+ vector_set(comps, XSTRDUP(MTYPE_COMPLETION, vrf->name));
+
+ for (ALL_LIST_ELEMENTS_RO(bm->bgp, next, bgp)) {
+ if (bgp->inst_type != BGP_INSTANCE_TYPE_VIEW)
+ continue;
+
+ vector_set(comps, XSTRDUP(MTYPE_COMPLETION, bgp->name));
+ }
+}
+
+static void bgp_instasn_autocomplete(vector comps, struct cmd_token *token)
+{
+ struct listnode *next, *next2;
+ struct bgp *bgp, *bgp2;
+ char buf[11];
+
+ for (ALL_LIST_ELEMENTS_RO(bm->bgp, next, bgp)) {
+ /* deduplicate */
+ for (ALL_LIST_ELEMENTS_RO(bm->bgp, next2, bgp2)) {
+ if (bgp2->as == bgp->as)
+ break;
+ if (bgp2 == bgp)
+ break;
+ }
+ if (bgp2 != bgp)
+ continue;
+
+ snprintf(buf, sizeof(buf), "%u", bgp->as);
+ vector_set(comps, XSTRDUP(MTYPE_COMPLETION, buf));
+ }
+}
+
+static const struct cmd_variable_handler bgp_viewvrf_var_handlers[] = {
+ {.tokenname = "VIEWVRFNAME", .completions = bgp_viewvrf_autocomplete},
+ {.varname = "instasn", .completions = bgp_instasn_autocomplete},
+ {.completions = NULL},
+};
+
+struct frr_pthread *bgp_pth_io;
+struct frr_pthread *bgp_pth_ka;
+
+static void bgp_pthreads_init(void)
+{
+ assert(!bgp_pth_io);
+ assert(!bgp_pth_ka);
+
+ struct frr_pthread_attr io = {
+ .start = frr_pthread_attr_default.start,
+ .stop = frr_pthread_attr_default.stop,
+ };
+ struct frr_pthread_attr ka = {
+ .start = bgp_keepalives_start,
+ .stop = bgp_keepalives_stop,
+ };
+ bgp_pth_io = frr_pthread_new(&io, "BGP I/O thread", "bgpd_io");
+ bgp_pth_ka = frr_pthread_new(&ka, "BGP Keepalives thread", "bgpd_ka");
+}
+
+void bgp_pthreads_run(void)
+{
+ frr_pthread_run(bgp_pth_io, NULL);
+ frr_pthread_run(bgp_pth_ka, NULL);
+
+ /* Wait until threads are ready. */
+ frr_pthread_wait_running(bgp_pth_io);
+ frr_pthread_wait_running(bgp_pth_ka);
+}
+
+void bgp_pthreads_finish(void)
+{
+ frr_pthread_stop_all();
+}
+
+static int peer_unshut_after_cfg(struct bgp *bgp)
+{
+ struct listnode *node;
+ struct peer *peer;
+
+ for (ALL_LIST_ELEMENTS_RO(bgp->peer, node, peer)) {
+ if (!peer->shut_during_cfg)
+ continue;
+
+ if (bgp_debug_neighbor_events(peer))
+ zlog_debug("%s: released from config-pending hold",
+ peer->host);
+
+ peer->shut_during_cfg = false;
+ if (peer_active(peer) && peer->status != Established) {
+ if (peer->status != Idle)
+ BGP_EVENT_ADD(peer, BGP_Stop);
+ BGP_EVENT_ADD(peer, BGP_Start);
+ }
+ }
+
+ return 0;
+}
+
+void bgp_init(unsigned short instance)
+{
+ hook_register(bgp_config_end, peer_unshut_after_cfg);
+
+ /* allocates some vital data structures used by peer commands in
+ * vty_init */
+
+ /* pre-init pthreads */
+ bgp_pthreads_init();
+
+ /* Init zebra. */
+ bgp_zebra_init(bm->master, instance);
+
+#ifdef ENABLE_BGP_VNC
+ vnc_zebra_init(bm->master);
+#endif
+
+ /* BGP VTY commands installation. */
+ bgp_vty_init();
+
+ /* BGP inits. */
+ bgp_attr_init();
+ bgp_debug_init();
+ bgp_community_alias_init();
+ bgp_dump_init();
+ bgp_route_init();
+ bgp_route_map_init();
+ bgp_scan_vty_init();
+ bgp_mplsvpn_init();
+#ifdef ENABLE_BGP_VNC
+ rfapi_init();
+#endif
+ bgp_ethernetvpn_init();
+ bgp_flowspec_vty_init();
+
+ /* Access list initialize. */
+ access_list_init();
+ access_list_add_hook(peer_distribute_update);
+ access_list_delete_hook(peer_distribute_update);
+
+ /* Filter list initialize. */
+ bgp_filter_init();
+ as_list_add_hook(peer_aslist_add);
+ as_list_delete_hook(peer_aslist_del);
+
+ /* Prefix list initialize.*/
+ prefix_list_init();
+ prefix_list_add_hook(peer_prefix_list_update);
+ prefix_list_delete_hook(peer_prefix_list_update);
+
+ /* Community list initialize. */
+ bgp_clist = community_list_init();
+
+ /* BFD init */
+ bgp_bfd_init(bm->master);
+
+ bgp_lp_vty_init();
+
+ cmd_variable_handler_register(bgp_viewvrf_var_handlers);
+}
+
+void bgp_terminate(void)
+{
+ struct bgp *bgp;
+ struct peer *peer;
+ struct listnode *node, *nnode;
+ struct listnode *mnode, *mnnode;
+
+ QOBJ_UNREG(bm);
+
+ /* Close the listener sockets first as this prevents peers from
+ * attempting
+ * to reconnect on receiving the peer unconfig message. In the presence
+ * of a large number of peers this will ensure that no peer is left with
+ * a dangling connection
+ */
+
+ bgp_close();
+ /* reverse bgp_master_init */
+ for (ALL_LIST_ELEMENTS(bm->bgp, mnode, mnnode, bgp)) {
+ bgp_close_vrf_socket(bgp);
+ for (ALL_LIST_ELEMENTS(bgp->peer, node, nnode, peer)) {
+ if (BGP_PEER_GRACEFUL_RESTART_CAPABLE(peer)) {
+ if (bgp_debug_neighbor_events(peer))
+ zlog_debug(
+ "%pBP configured Graceful-Restart, skipping unconfig notification",
+ peer);
+ continue;
+ }
+ if (BGP_IS_VALID_STATE_FOR_NOTIF(peer->status))
+ bgp_notify_send(peer, BGP_NOTIFY_CEASE,
+ BGP_NOTIFY_CEASE_PEER_UNCONFIG);
+ }
+ }
+
+ if (bm->listen_sockets)
+ list_delete(&bm->listen_sockets);
+
+ THREAD_OFF(bm->t_rmap_update);
+
+ bgp_mac_finish();
+}
+
+struct peer *peer_lookup_in_view(struct vty *vty, struct bgp *bgp,
+ const char *ip_str, bool use_json)
+{
+ int ret;
+ struct peer *peer;
+ union sockunion su;
+
+ /* Get peer sockunion. */
+ ret = str2sockunion(ip_str, &su);
+ if (ret < 0) {
+ peer = peer_lookup_by_conf_if(bgp, ip_str);
+ if (!peer) {
+ peer = peer_lookup_by_hostname(bgp, ip_str);
+
+ if (!peer) {
+ if (use_json) {
+ json_object *json_no = NULL;
+ json_no = json_object_new_object();
+ json_object_string_add(
+ json_no,
+ "malformedAddressOrName",
+ ip_str);
+ vty_json(vty, json_no);
+ } else
+ vty_out(vty,
+ "%% Malformed address or name: %s\n",
+ ip_str);
+ return NULL;
+ }
+ }
+ return peer;
+ }
+
+ /* Peer structure lookup. */
+ peer = peer_lookup(bgp, &su);
+ if (!peer) {
+ if (use_json) {
+ json_object *json_no = NULL;
+ json_no = json_object_new_object();
+ json_object_string_add(json_no, "warning",
+ "No such neighbor in this view/vrf");
+ vty_json(vty, json_no);
+ } else
+ vty_out(vty, "No such neighbor in this view/vrf\n");
+ return NULL;
+ }
+
+ return peer;
+}
+
+void bgp_gr_apply_running_config(void)
+{
+ struct peer *peer = NULL;
+ struct bgp *bgp = NULL;
+ struct listnode *node, *nnode;
+ bool gr_router_detected = false;
+
+ if (BGP_DEBUG(graceful_restart, GRACEFUL_RESTART))
+ zlog_debug("[BGP_GR] %s called !", __func__);
+
+ for (ALL_LIST_ELEMENTS(bm->bgp, node, nnode, bgp)) {
+ for (ALL_LIST_ELEMENTS(bgp->peer, node, nnode, peer)) {
+ bgp_peer_gr_flags_update(peer);
+ if (CHECK_FLAG(peer->flags, PEER_FLAG_GRACEFUL_RESTART))
+ gr_router_detected = true;
+ }
+
+ if (gr_router_detected
+ && bgp->present_zebra_gr_state == ZEBRA_GR_DISABLE) {
+ bgp_zebra_send_capabilities(bgp, true);
+ } else if (!gr_router_detected
+ && bgp->present_zebra_gr_state == ZEBRA_GR_ENABLE) {
+ bgp_zebra_send_capabilities(bgp, false);
+ }
+
+ gr_router_detected = false;
+ }
+}
+
+printfrr_ext_autoreg_p("BP", printfrr_bp);
+static ssize_t printfrr_bp(struct fbuf *buf, struct printfrr_eargs *ea,
+ const void *ptr)
+{
+ const struct peer *peer = ptr;
+
+ if (!peer)
+ return bputs(buf, "(null)");
+
+ return bprintfrr(buf, "%s(%s)", peer->host,
+ peer->hostname ? peer->hostname : "Unknown");
+}
diff --git a/bgpd/bgpd.h b/bgpd/bgpd.h
new file mode 100644
index 0000000..f6162f3
--- /dev/null
+++ b/bgpd/bgpd.h
@@ -0,0 +1,2586 @@
+/* BGP message definition header.
+ * Copyright (C) 1996, 97, 98, 99, 2000 Kunihiro Ishiguro
+ *
+ * This file is part of GNU Zebra.
+ *
+ * GNU Zebra is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License as published by the
+ * Free Software Foundation; either version 2, or (at your option) any
+ * later version.
+ *
+ * GNU Zebra is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program; see the file COPYING; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
+ */
+
+#ifndef _QUAGGA_BGPD_H
+#define _QUAGGA_BGPD_H
+
+#include "qobj.h"
+#include <pthread.h>
+
+#include "hook.h"
+#include "frr_pthread.h"
+#include "lib/json.h"
+#include "vrf.h"
+#include "vty.h"
+#include "srv6.h"
+#include "iana_afi.h"
+
+/* For union sockunion. */
+#include "queue.h"
+#include "sockunion.h"
+#include "routemap.h"
+#include "linklist.h"
+#include "defaults.h"
+#include "bgp_memory.h"
+#include "bitfield.h"
+#include "vxlan.h"
+#include "bgp_labelpool.h"
+#include "bgp_addpath_types.h"
+#include "bgp_nexthop.h"
+#include "bgp_io.h"
+
+#include "lib/bfd.h"
+
+#define BGP_MAX_HOSTNAME 64 /* Linux max, is larger than most other sys */
+#define BGP_PEER_MAX_HASH_SIZE 16384
+
+/* Default interval for IPv6 RAs when triggered by BGP unnumbered neighbor. */
+#define BGP_UNNUM_DEFAULT_RA_INTERVAL 10
+
+struct update_subgroup;
+struct bpacket;
+struct bgp_pbr_config;
+
+/*
+ * Allow the neighbor XXXX remote-as to take internal or external
+ * AS_SPECIFIED is zero to auto-inherit original non-feature/enhancement
+ * behavior
+ * in the system.
+ */
+enum { AS_UNSPECIFIED = 0,
+ AS_SPECIFIED,
+ AS_INTERNAL,
+ AS_EXTERNAL,
+};
+
+/* Zebra Gracaful Restart states */
+enum zebra_gr_mode {
+ ZEBRA_GR_DISABLE = 0,
+ ZEBRA_GR_ENABLE
+};
+
+/* Typedef BGP specific types. */
+typedef uint32_t as_t;
+typedef uint16_t as16_t; /* we may still encounter 16 Bit asnums */
+typedef uint16_t bgp_size_t;
+
+enum bgp_af_index {
+ BGP_AF_START,
+ BGP_AF_IPV4_UNICAST = BGP_AF_START,
+ BGP_AF_IPV4_MULTICAST,
+ BGP_AF_IPV4_VPN,
+ BGP_AF_IPV6_UNICAST,
+ BGP_AF_IPV6_MULTICAST,
+ BGP_AF_IPV6_VPN,
+ BGP_AF_IPV4_ENCAP,
+ BGP_AF_IPV6_ENCAP,
+ BGP_AF_L2VPN_EVPN,
+ BGP_AF_IPV4_LBL_UNICAST,
+ BGP_AF_IPV6_LBL_UNICAST,
+ BGP_AF_IPV4_FLOWSPEC,
+ BGP_AF_IPV6_FLOWSPEC,
+ BGP_AF_MAX
+};
+
+#define AF_FOREACH(af) for ((af) = BGP_AF_START; (af) < BGP_AF_MAX; (af)++)
+
+#define FOREACH_SAFI(safi) \
+ for (safi = SAFI_UNICAST; safi < SAFI_MAX; safi++)
+
+extern struct frr_pthread *bgp_pth_io;
+extern struct frr_pthread *bgp_pth_ka;
+
+/* BGP master for system wide configurations and variables. */
+struct bgp_master {
+ /* BGP instance list. */
+ struct list *bgp;
+
+ /* BGP thread master. */
+ struct thread_master *master;
+
+ /* Listening sockets */
+ struct list *listen_sockets;
+
+ /* BGP port number. */
+ uint16_t port;
+
+ /* Listener addresses */
+ struct list *addresses;
+
+ /* The Mac table */
+ struct hash *self_mac_hash;
+
+ /* BGP start time. */
+ time_t start_time;
+
+ /* Various BGP global configuration. */
+ uint8_t options;
+#define BGP_OPT_NO_FIB (1 << 0)
+#define BGP_OPT_NO_LISTEN (1 << 1)
+#define BGP_OPT_NO_ZEBRA (1 << 2)
+
+ uint64_t updgrp_idspace;
+ uint64_t subgrp_idspace;
+
+ /* timer to dampen route map changes */
+ struct thread *t_rmap_update; /* Handle route map updates */
+ uint32_t rmap_update_timer; /* Route map update timer */
+#define RMAP_DEFAULT_UPDATE_TIMER 5 /* disabled by default */
+
+ /* Id space for automatic RD derivation for an EVI/VRF */
+ bitfield_t rd_idspace;
+
+ /* dynamic mpls label allocation pool */
+ struct labelpool labelpool;
+
+ /* BGP-EVPN VRF ID. Defaults to default VRF (if any) */
+ struct bgp* bgp_evpn;
+
+ /* How big should we set the socket buffer size */
+ uint32_t socket_buffer;
+
+ /* Should we do wait for fib install globally? */
+ bool wait_for_fib;
+
+ /* EVPN multihoming */
+ struct bgp_evpn_mh_info *mh_info;
+
+ /* global update-delay timer values */
+ uint16_t v_update_delay;
+ uint16_t v_establish_wait;
+
+ uint32_t flags;
+#define BM_FLAG_GRACEFUL_SHUTDOWN (1 << 0)
+#define BM_FLAG_SEND_EXTRA_DATA_TO_ZEBRA (1 << 1)
+
+ bool terminating; /* global flag that sigint terminate seen */
+
+ /* DSCP value for TCP sessions */
+ uint8_t tcp_dscp;
+
+ QOBJ_FIELDS;
+};
+DECLARE_QOBJ_TYPE(bgp_master);
+
+/* BGP route-map structure. */
+struct bgp_rmap {
+ char *name;
+ struct route_map *map;
+};
+
+struct bgp_redist {
+ unsigned short instance;
+
+ /* BGP redistribute metric configuration. */
+ uint8_t redist_metric_flag;
+ uint32_t redist_metric;
+
+ /* BGP redistribute route-map. */
+ struct bgp_rmap rmap;
+};
+
+enum vpn_policy_direction {
+ BGP_VPN_POLICY_DIR_FROMVPN = 0,
+ BGP_VPN_POLICY_DIR_TOVPN = 1,
+ BGP_VPN_POLICY_DIR_MAX = 2
+};
+
+struct vpn_policy {
+ struct bgp *bgp; /* parent */
+ afi_t afi;
+ struct ecommunity *rtlist[BGP_VPN_POLICY_DIR_MAX];
+ struct ecommunity *import_redirect_rtlist;
+ char *rmap_name[BGP_VPN_POLICY_DIR_MAX];
+ struct route_map *rmap[BGP_VPN_POLICY_DIR_MAX];
+
+ /* should be mpls_label_t? */
+ uint32_t tovpn_label; /* may be MPLS_LABEL_NONE */
+ uint32_t tovpn_zebra_vrf_label_last_sent;
+ struct prefix_rd tovpn_rd;
+ struct prefix tovpn_nexthop; /* unset => set to 0 */
+ uint32_t flags;
+#define BGP_VPN_POLICY_TOVPN_LABEL_AUTO (1 << 0)
+#define BGP_VPN_POLICY_TOVPN_RD_SET (1 << 1)
+#define BGP_VPN_POLICY_TOVPN_NEXTHOP_SET (1 << 2)
+#define BGP_VPN_POLICY_TOVPN_SID_AUTO (1 << 3)
+
+ /*
+ * If we are importing another vrf into us keep a list of
+ * vrf names that are being imported into us.
+ */
+ struct list *import_vrf;
+
+ /*
+ * if we are being exported to another vrf keep a list of
+ * vrf names that we are being exported to.
+ */
+ struct list *export_vrf;
+
+ /*
+ * Segment-Routing SRv6 Mode
+ */
+ uint32_t tovpn_sid_index; /* unset => set to 0 */
+ struct in6_addr *tovpn_sid;
+ struct in6_addr *tovpn_sid_locator;
+ uint32_t tovpn_sid_transpose_label;
+ struct in6_addr *tovpn_zebra_vrf_sid_last_sent;
+};
+
+/*
+ * Type of 'struct bgp'.
+ * - Default: The default instance
+ * - VRF: A specific (non-default) VRF
+ * - View: An instance used for route exchange
+ * The "default" instance is treated separately to simplify the code. Note
+ * that if deployed in a Multi-VRF environment, it may not exist.
+ */
+enum bgp_instance_type {
+ BGP_INSTANCE_TYPE_DEFAULT,
+ BGP_INSTANCE_TYPE_VRF,
+ BGP_INSTANCE_TYPE_VIEW
+};
+
+#define BGP_SEND_EOR(bgp, afi, safi) \
+ (!CHECK_FLAG(bgp->flags, BGP_FLAG_GR_DISABLE_EOR) \
+ && ((bgp->gr_info[afi][safi].t_select_deferral == NULL) \
+ || (bgp->gr_info[afi][safi].eor_required \
+ == bgp->gr_info[afi][safi].eor_received)))
+
+/* BGP GR Global ds */
+
+#define BGP_GLOBAL_GR_MODE 4
+#define BGP_GLOBAL_GR_EVENT_CMD 4
+
+/* Graceful restart selection deferral timer info */
+struct graceful_restart_info {
+ /* Count of EOR message expected */
+ uint32_t eor_required;
+ /* Count of EOR received */
+ uint32_t eor_received;
+ /* Deferral Timer */
+ struct thread *t_select_deferral;
+ /* Routes Deferred */
+ uint32_t gr_deferred;
+ /* Best route select */
+ struct thread *t_route_select;
+ /* AFI, SAFI enabled */
+ bool af_enabled[AFI_MAX][SAFI_MAX];
+ /* Route update completed */
+ bool route_sync[AFI_MAX][SAFI_MAX];
+};
+
+enum global_mode {
+ GLOBAL_HELPER = 0, /* This is the default mode */
+ GLOBAL_GR,
+ GLOBAL_DISABLE,
+ GLOBAL_INVALID
+};
+
+enum global_gr_command {
+ GLOBAL_GR_CMD = 0,
+ NO_GLOBAL_GR_CMD,
+ GLOBAL_DISABLE_CMD,
+ NO_GLOBAL_DISABLE_CMD
+};
+
+#define BGP_GR_SUCCESS 0
+#define BGP_GR_FAILURE 1
+
+/* Handling of BGP link bandwidth (LB) on receiver - whether and how to
+ * do weighted ECMP. Note: This applies after multipath computation.
+ */
+enum bgp_link_bw_handling {
+ /* Do ECMP if some paths don't have LB - default */
+ BGP_LINK_BW_ECMP,
+ /* Completely ignore LB, just do regular ECMP */
+ BGP_LINK_BW_IGNORE_BW,
+ /* Skip paths without LB, do wECMP on others */
+ BGP_LINK_BW_SKIP_MISSING,
+ /* Do wECMP with default weight for paths not having LB */
+ BGP_LINK_BW_DEFWT_4_MISSING
+};
+
+RB_HEAD(bgp_es_vrf_rb_head, bgp_evpn_es_vrf);
+RB_PROTOTYPE(bgp_es_vrf_rb_head, bgp_evpn_es_vrf, rb_node, bgp_es_vrf_rb_cmp);
+
+struct bgp_snmp_stats {
+ /* SNMP variables for mplsL3Vpn*/
+ time_t creation_time;
+ time_t modify_time;
+ bool active;
+ uint32_t routes_added;
+ uint32_t routes_deleted;
+};
+
+struct bgp_srv6_function {
+ struct in6_addr sid;
+ char locator_name[SRV6_LOCNAME_SIZE];
+};
+
+/* BGP instance structure. */
+struct bgp {
+ /* AS number of this BGP instance. */
+ as_t as;
+
+ /* Name of this BGP instance. */
+ char *name;
+ char *name_pretty; /* printable "VRF|VIEW name|default" */
+
+ /* Type of instance and VRF id. */
+ enum bgp_instance_type inst_type;
+ vrf_id_t vrf_id;
+
+ /* Reference count to allow peer_delete to finish after bgp_delete */
+ int lock;
+
+ /* Self peer. */
+ struct peer *peer_self;
+
+ /* BGP peer. */
+ struct list *peer;
+ struct hash *peerhash;
+
+ /* BGP peer group. */
+ struct list *group;
+
+ /* The maximum number of BGP dynamic neighbors that can be created */
+ int dynamic_neighbors_limit;
+
+ /* The current number of BGP dynamic neighbors */
+ int dynamic_neighbors_count;
+
+ struct hash *update_groups[BGP_AF_MAX];
+
+ /*
+ * Global statistics for update groups.
+ */
+ struct {
+ uint32_t join_events;
+ uint32_t prune_events;
+ uint32_t merge_events;
+ uint32_t split_events;
+ uint32_t updgrp_switch_events;
+ uint32_t peer_refreshes_combined;
+ uint32_t adj_count;
+ uint32_t merge_checks_triggered;
+
+ uint32_t updgrps_created;
+ uint32_t updgrps_deleted;
+ uint32_t subgrps_created;
+ uint32_t subgrps_deleted;
+ } update_group_stats;
+
+ struct bgp_snmp_stats *snmp_stats;
+
+ /* BGP configuration. */
+ uint16_t config;
+#define BGP_CONFIG_CLUSTER_ID (1 << 0)
+#define BGP_CONFIG_CONFEDERATION (1 << 1)
+
+ /* BGP router identifier. */
+ struct in_addr router_id;
+ struct in_addr router_id_static;
+ struct in_addr router_id_zebra;
+
+ /* BGP route reflector cluster ID. */
+ struct in_addr cluster_id;
+
+ /* BGP confederation information. */
+ as_t confed_id;
+ as_t *confed_peers;
+ int confed_peers_cnt;
+
+ struct thread
+ *t_startup; /* start-up timer on only once at the beginning */
+
+ uint32_t v_maxmed_onstartup; /* Duration of max-med on start-up */
+#define BGP_MAXMED_ONSTARTUP_UNCONFIGURED 0 /* 0 means off, its the default */
+ uint32_t maxmed_onstartup_value; /* Max-med value when active on
+ start-up */
+ struct thread
+ *t_maxmed_onstartup; /* non-null when max-med onstartup is on */
+ uint8_t maxmed_onstartup_over; /* Flag to make it effective only once */
+
+ bool v_maxmed_admin; /* true/false if max-med administrative is on/off
+ */
+#define BGP_MAXMED_ADMIN_UNCONFIGURED false /* Off by default */
+ uint32_t maxmed_admin_value; /* Max-med value when administrative in on
+ */
+#define BGP_MAXMED_VALUE_DEFAULT 4294967294 /* Maximum by default */
+
+ uint8_t maxmed_active; /* 1/0 if max-med is active or not */
+ uint32_t maxmed_value; /* Max-med value when its active */
+
+ /* BGP update delay on startup */
+ struct thread *t_update_delay;
+ struct thread *t_establish_wait;
+ uint8_t update_delay_over;
+ uint8_t main_zebra_update_hold;
+ uint8_t main_peers_update_hold;
+ uint16_t v_update_delay;
+ uint16_t v_establish_wait;
+ char update_delay_begin_time[64];
+ char update_delay_end_time[64];
+ char update_delay_zebra_resume_time[64];
+ char update_delay_peers_resume_time[64];
+ uint32_t established;
+ uint32_t restarted_peers;
+ uint32_t implicit_eors;
+ uint32_t explicit_eors;
+#define BGP_UPDATE_DELAY_DEF 0
+#define BGP_UPDATE_DELAY_MIN 0
+#define BGP_UPDATE_DELAY_MAX 3600
+
+ /* Reference bandwidth for BGP link-bandwidth. Used when
+ * the LB value has to be computed based on some other
+ * factor (e.g., number of multipaths for the prefix)
+ * Value is in Mbps
+ */
+ uint32_t lb_ref_bw;
+#define BGP_LINK_BW_REF_BW 1
+
+ /* BGP flags. */
+ uint64_t flags;
+#define BGP_FLAG_ALWAYS_COMPARE_MED (1 << 0)
+#define BGP_FLAG_DETERMINISTIC_MED (1 << 1)
+#define BGP_FLAG_MED_MISSING_AS_WORST (1 << 2)
+#define BGP_FLAG_MED_CONFED (1 << 3)
+#define BGP_FLAG_NO_CLIENT_TO_CLIENT (1 << 4)
+#define BGP_FLAG_COMPARE_ROUTER_ID (1 << 5)
+#define BGP_FLAG_ASPATH_IGNORE (1 << 6)
+#define BGP_FLAG_IMPORT_CHECK (1 << 7)
+#define BGP_FLAG_NO_FAST_EXT_FAILOVER (1 << 8)
+#define BGP_FLAG_LOG_NEIGHBOR_CHANGES (1 << 9)
+
+/* This flag is set when we have full BGP Graceful-Restart mode enable */
+#define BGP_FLAG_GRACEFUL_RESTART (1 << 10)
+
+#define BGP_FLAG_ASPATH_CONFED (1 << 11)
+#define BGP_FLAG_ASPATH_MULTIPATH_RELAX (1 << 12)
+#define BGP_FLAG_RR_ALLOW_OUTBOUND_POLICY (1 << 13)
+#define BGP_FLAG_DISABLE_NH_CONNECTED_CHK (1 << 14)
+#define BGP_FLAG_MULTIPATH_RELAX_AS_SET (1 << 15)
+#define BGP_FLAG_FORCE_STATIC_PROCESS (1 << 16)
+#define BGP_FLAG_SHOW_HOSTNAME (1 << 17)
+#define BGP_FLAG_GR_PRESERVE_FWD (1 << 18)
+#define BGP_FLAG_GRACEFUL_SHUTDOWN (1 << 19)
+#define BGP_FLAG_DELETE_IN_PROGRESS (1 << 20)
+#define BGP_FLAG_SELECT_DEFER_DISABLE (1 << 21)
+#define BGP_FLAG_GR_DISABLE_EOR (1 << 22)
+#define BGP_FLAG_EBGP_REQUIRES_POLICY (1 << 23)
+#define BGP_FLAG_SHOW_NEXTHOP_HOSTNAME (1 << 24)
+
+/* This flag is set if the instance is in administrative shutdown */
+#define BGP_FLAG_SHUTDOWN (1 << 25)
+#define BGP_FLAG_SUPPRESS_FIB_PENDING (1 << 26)
+#define BGP_FLAG_SUPPRESS_DUPLICATES (1 << 27)
+#define BGP_FLAG_PEERTYPE_MULTIPATH_RELAX (1 << 29)
+/* Indicate Graceful Restart support for BGP NOTIFICATION messages */
+#define BGP_FLAG_GRACEFUL_NOTIFICATION (1 << 30)
+/* Send Hard Reset CEASE Notification for 'Administrative Reset' */
+#define BGP_FLAG_HARD_ADMIN_RESET (1 << 31)
+
+ /* BGP default address-families.
+ * New peers inherit enabled afi/safis from bgp instance.
+ */
+ uint16_t default_af[AFI_MAX][SAFI_MAX];
+
+ enum global_mode GLOBAL_GR_FSM[BGP_GLOBAL_GR_MODE]
+ [BGP_GLOBAL_GR_EVENT_CMD];
+ enum global_mode global_gr_present_state;
+
+ /* This variable stores the current Graceful Restart state of Zebra
+ * - ZEBRA_GR_ENABLE / ZEBRA_GR_DISABLE
+ */
+ enum zebra_gr_mode present_zebra_gr_state;
+
+ /* BGP Per AF flags */
+ uint16_t af_flags[AFI_MAX][SAFI_MAX];
+#define BGP_CONFIG_DAMPENING (1 << 0)
+/* l2vpn evpn flags - 1 << 0 is used for DAMPENNG */
+#define BGP_L2VPN_EVPN_ADV_IPV4_UNICAST (1 << 1)
+#define BGP_L2VPN_EVPN_ADV_IPV4_UNICAST_GW_IP (1 << 2)
+#define BGP_L2VPN_EVPN_ADV_IPV6_UNICAST (1 << 3)
+#define BGP_L2VPN_EVPN_ADV_IPV6_UNICAST_GW_IP (1 << 4)
+#define BGP_L2VPN_EVPN_DEFAULT_ORIGINATE_IPV4 (1 << 5)
+#define BGP_L2VPN_EVPN_DEFAULT_ORIGINATE_IPV6 (1 << 6)
+/* import/export between address families */
+#define BGP_CONFIG_VRF_TO_MPLSVPN_EXPORT (1 << 7)
+#define BGP_CONFIG_MPLSVPN_TO_VRF_IMPORT (1 << 8)
+/* vrf-route leaking flags */
+#define BGP_CONFIG_VRF_TO_VRF_IMPORT (1 << 9)
+#define BGP_CONFIG_VRF_TO_VRF_EXPORT (1 << 10)
+/* vpnvx retain flag */
+#define BGP_VPNVX_RETAIN_ROUTE_TARGET_ALL (1 << 11)
+
+ /* BGP per AF peer count */
+ uint32_t af_peer_count[AFI_MAX][SAFI_MAX];
+
+ /* Tree for next-hop lookup cache. */
+ struct bgp_nexthop_cache_head nexthop_cache_table[AFI_MAX];
+
+ /* Tree for import-check */
+ struct bgp_nexthop_cache_head import_check_table[AFI_MAX];
+
+ struct bgp_table *connected_table[AFI_MAX];
+
+ struct hash *address_hash;
+
+ /* DB for all local tunnel-ips - used mainly for martian checks
+ Currently it only has all VxLan tunnel IPs*/
+ struct hash *tip_hash;
+
+ /* Static route configuration. */
+ struct bgp_table *route[AFI_MAX][SAFI_MAX];
+
+ /* Aggregate address configuration. */
+ struct bgp_table *aggregate[AFI_MAX][SAFI_MAX];
+
+ /* BGP routing information base. */
+ struct bgp_table *rib[AFI_MAX][SAFI_MAX];
+
+ /* BGP table route-map. */
+ struct bgp_rmap table_map[AFI_MAX][SAFI_MAX];
+
+ /* BGP redistribute configuration. */
+ struct list *redist[AFI_MAX][ZEBRA_ROUTE_MAX];
+
+ /* Allocate MPLS labels */
+ uint8_t allocate_mpls_labels[AFI_MAX][SAFI_MAX];
+
+ /* Allocate hash entries to store policy routing information
+ * The hash are used to host pbr rules somewhere.
+ * Actually, pbr will only be used by flowspec
+ * those hash elements will have relationship together as
+ * illustrated in below diagram:
+ *
+ * pbr_action a <----- pbr_match i <--- pbr_match_entry 1..n
+ * <----- pbr_match j <--- pbr_match_entry 1..m
+ * <----- pbr_rule k
+ *
+ * - here in BGP structure, the list of match and actions will
+ * stand for the list of ipset sets, and table_ids in the kernel
+ * - the arrow above between pbr_match and pbr_action indicate
+ * that a backpointer permits match to find the action
+ * - the arrow betwen match_entry and match is a hash list
+ * contained in match, that lists the whole set of entries
+ */
+ struct hash *pbr_match_hash;
+ struct hash *pbr_rule_hash;
+ struct hash *pbr_action_hash;
+
+ /* timer to re-evaluate neighbor default-originate route-maps */
+ struct thread *t_rmap_def_originate_eval;
+#define RMAP_DEFAULT_ORIGINATE_EVAL_TIMER 5
+
+ /* BGP distance configuration. */
+ uint8_t distance_ebgp[AFI_MAX][SAFI_MAX];
+ uint8_t distance_ibgp[AFI_MAX][SAFI_MAX];
+ uint8_t distance_local[AFI_MAX][SAFI_MAX];
+
+ /* BGP default local-preference. */
+ uint32_t default_local_pref;
+
+ /* BGP default subgroup pkt queue max */
+ uint32_t default_subgroup_pkt_queue_max;
+
+ /* BGP default timer. */
+ uint32_t default_holdtime;
+ uint32_t default_keepalive;
+ uint32_t default_connect_retry;
+ uint32_t default_delayopen;
+
+ /* BGP minimum holdtime. */
+ uint16_t default_min_holdtime;
+
+ /* BGP graceful restart */
+ uint32_t restart_time;
+ uint32_t stalepath_time;
+ uint32_t select_defer_time;
+ struct graceful_restart_info gr_info[AFI_MAX][SAFI_MAX];
+ uint32_t rib_stale_time;
+
+ /* BGP Long-lived Graceful Restart */
+ uint32_t llgr_stale_time;
+
+#define BGP_ROUTE_SELECT_DELAY 1
+#define BGP_MAX_BEST_ROUTE_SELECT 10000
+ /* Maximum-paths configuration */
+ struct bgp_maxpaths_cfg {
+ uint16_t maxpaths_ebgp;
+ uint16_t maxpaths_ibgp;
+ bool same_clusterlen;
+ } maxpaths[AFI_MAX][SAFI_MAX];
+
+ _Atomic uint32_t wpkt_quanta; // max # packets to write per i/o cycle
+ _Atomic uint32_t rpkt_quanta; // max # packets to read per i/o cycle
+
+ /* Automatic coalesce adjust on/off */
+ bool heuristic_coalesce;
+ /* Actual coalesce time */
+ uint32_t coalesce_time;
+
+ /* Auto-shutdown new peers */
+ bool autoshutdown;
+
+ struct bgp_addpath_bgp_data tx_addpath;
+
+#ifdef ENABLE_BGP_VNC
+ struct rfapi_cfg *rfapi_cfg;
+ struct rfapi *rfapi;
+#endif
+
+ /* EVPN related information */
+
+ /* EVI hash table */
+ struct hash *vnihash;
+
+ /*
+ * VNI hash table based on SVI ifindex as its key.
+ * We use SVI ifindex as key to lookup a VNI table for gateway IP
+ * overlay index recursive lookup.
+ * For this purpose, a hashtable is added which optimizes this lookup.
+ */
+ struct hash *vni_svi_hash;
+
+ /* EVPN enable - advertise gateway macip routes */
+ int advertise_gw_macip;
+
+ /* EVPN enable - advertise local VNIs and their MACs etc. */
+ int advertise_all_vni;
+
+ /* draft-ietf-idr-deprecate-as-set-confed-set
+ * Reject aspaths with AS_SET and/or AS_CONFED_SET.
+ */
+ bool reject_as_sets;
+
+ struct bgp_evpn_info *evpn_info;
+
+ /* EVPN - use RFC 8365 to auto-derive RT */
+ int advertise_autort_rfc8365;
+
+ /*
+ * Flooding mechanism for BUM packets for VxLAN-EVPN.
+ */
+ enum vxlan_flood_control vxlan_flood_ctrl;
+
+ /* Hash table of Import RTs to EVIs */
+ struct hash *import_rt_hash;
+
+ /* Hash table of VRF import RTs to VRFs */
+ struct hash *vrf_import_rt_hash;
+
+ /* L3-VNI corresponding to this vrf */
+ vni_t l3vni;
+
+ /* router-mac to be used in mac-ip routes for this vrf */
+ struct ethaddr rmac;
+
+ /* originator ip - to be used as NH for type-5 routes */
+ struct in_addr originator_ip;
+
+ /* SVI associated with the L3-VNI corresponding to this vrf */
+ ifindex_t l3vni_svi_ifindex;
+
+ /* RB tree of ES-VRFs */
+ struct bgp_es_vrf_rb_head es_vrf_rb_tree;
+
+ /* Hash table of EVPN nexthops maintained per-tenant-VRF */
+ struct hash *evpn_nh_table;
+
+ /*
+ * Flag resolve_overlay_index is used for recursive resolution
+ * procedures for EVPN type-5 route's gateway IP overlay index.
+ * When this flag is set, we build remote-ip-hash for
+ * all L2VNIs and resolve overlay index nexthops using this hash.
+ * Overlay index nexthops remain unresolved if this flag is not set.
+ */
+ bool resolve_overlay_index;
+
+ /* vrf flags */
+ uint32_t vrf_flags;
+#define BGP_VRF_AUTO (1 << 0)
+#define BGP_VRF_IMPORT_RT_CFGD (1 << 1)
+#define BGP_VRF_EXPORT_RT_CFGD (1 << 2)
+#define BGP_VRF_RD_CFGD (1 << 3)
+#define BGP_VRF_L3VNI_PREFIX_ROUTES_ONLY (1 << 4)
+
+ /* unique ID for auto derivation of RD for this vrf */
+ uint16_t vrf_rd_id;
+
+ /* Automatically derived RD for this VRF */
+ struct prefix_rd vrf_prd_auto;
+
+ /* RD for this VRF */
+ struct prefix_rd vrf_prd;
+
+ /* import rt list for the vrf instance */
+ struct list *vrf_import_rtl;
+
+ /* export rt list for the vrf instance */
+ struct list *vrf_export_rtl;
+
+ /* list of corresponding l2vnis (struct bgpevpn) */
+ struct list *l2vnis;
+
+ /* route map for advertise ipv4/ipv6 unicast (type-5 routes) */
+ struct bgp_rmap adv_cmd_rmap[AFI_MAX][SAFI_MAX];
+
+ struct vpn_policy vpn_policy[AFI_MAX];
+
+ struct bgp_pbr_config *bgp_pbr_cfg;
+
+ /* Count of peers in established state */
+ uint32_t established_peers;
+
+ /* Weighted ECMP related config. */
+ enum bgp_link_bw_handling lb_handling;
+
+ /* Process Queue for handling routes */
+ struct work_queue *process_queue;
+
+ bool fast_convergence;
+
+ /* BGP Conditional advertisement */
+ uint32_t condition_check_period;
+ uint32_t condition_filter_count;
+ struct thread *t_condition_check;
+
+ /* BGP VPN SRv6 backend */
+ bool srv6_enabled;
+ char srv6_locator_name[SRV6_LOCNAME_SIZE];
+ struct list *srv6_locator_chunks;
+ struct list *srv6_functions;
+
+ struct timeval ebgprequirespolicywarning;
+#define FIFTEENMINUTE2USEC (int64_t)15 * 60 * 1000000
+
+ bool allow_martian;
+
+ QOBJ_FIELDS;
+};
+DECLARE_QOBJ_TYPE(bgp);
+
+struct bgp_interface {
+#define BGP_INTERFACE_MPLS_BGP_FORWARDING (1 << 0)
+ uint32_t flags;
+};
+
+DECLARE_HOOK(bgp_inst_delete, (struct bgp *bgp), (bgp));
+DECLARE_HOOK(bgp_inst_config_write,
+ (struct bgp *bgp, struct vty *vty),
+ (bgp, vty));
+DECLARE_HOOK(bgp_config_end, (struct bgp *bgp), (bgp));
+
+/* Thread callback information */
+struct afi_safi_info {
+ afi_t afi;
+ safi_t safi;
+ struct bgp *bgp;
+};
+
+#define BGP_ROUTE_ADV_HOLD(bgp) (bgp->main_peers_update_hold)
+
+#define IS_BGP_INST_KNOWN_TO_ZEBRA(bgp) \
+ (bgp->inst_type == BGP_INSTANCE_TYPE_DEFAULT \
+ || (bgp->inst_type == BGP_INSTANCE_TYPE_VRF \
+ && bgp->vrf_id != VRF_UNKNOWN))
+
+#define BGP_SELECT_DEFER_DISABLE(bgp) \
+ (CHECK_FLAG(bgp->flags, BGP_FLAG_SELECT_DEFER_DISABLE))
+
+#define BGP_SUPPRESS_FIB_ENABLED(bgp) \
+ (CHECK_FLAG(bgp->flags, BGP_FLAG_SUPPRESS_FIB_PENDING) \
+ || bm->wait_for_fib)
+
+/* BGP peer-group support. */
+struct peer_group {
+ /* Name of the peer-group. */
+ char *name;
+
+ /* Pointer to BGP. */
+ struct bgp *bgp;
+
+ /* Peer-group client list. */
+ struct list *peer;
+
+ /** Dynamic neighbor listening ranges */
+ struct list *listen_range[AFI_MAX];
+
+ /* Peer-group config */
+ struct peer *conf;
+};
+
+/* BGP Notify message format. */
+struct bgp_notify {
+ uint8_t code;
+ uint8_t subcode;
+ char *data;
+ bgp_size_t length;
+ uint8_t *raw_data;
+ bool hard_reset;
+};
+
+/* Next hop self address. */
+struct bgp_nexthop {
+ struct interface *ifp;
+ struct in_addr v4;
+ struct in6_addr v6_global;
+ struct in6_addr v6_local;
+};
+
+/* BGP addpath values */
+#define BGP_ADDPATH_RX 1
+#define BGP_ADDPATH_TX 2
+#define BGP_ADDPATH_ID_LEN 4
+
+#define BGP_ADDPATH_TX_ID_FOR_DEFAULT_ORIGINATE 1
+
+/* Route map direction */
+#define RMAP_IN 0
+#define RMAP_OUT 1
+#define RMAP_MAX 2
+
+#define BGP_DEFAULT_TTL 1
+#define BGP_GTSM_HOPS_DISABLED 0
+#define BGP_GTSM_HOPS_CONNECTED 1
+
+/* Advertise map */
+#define CONDITION_NON_EXIST false
+#define CONDITION_EXIST true
+
+enum update_type { UPDATE_TYPE_WITHDRAW, UPDATE_TYPE_ADVERTISE };
+
+#include "filter.h"
+
+/* BGP filter structure. */
+struct bgp_filter {
+ /* Distribute-list. */
+ struct {
+ char *name;
+ struct access_list *alist;
+ } dlist[FILTER_MAX];
+
+ /* Prefix-list. */
+ struct {
+ char *name;
+ struct prefix_list *plist;
+ } plist[FILTER_MAX];
+
+ /* Filter-list. */
+ struct {
+ char *name;
+ struct as_list *aslist;
+ } aslist[FILTER_MAX];
+
+ /* Route-map. */
+ struct {
+ char *name;
+ struct route_map *map;
+ } map[RMAP_MAX];
+
+ /* Unsuppress-map. */
+ struct {
+ char *name;
+ struct route_map *map;
+ } usmap;
+
+ /* Advertise-map */
+ struct {
+ char *aname;
+ struct route_map *amap;
+
+ bool condition;
+
+ char *cname;
+ struct route_map *cmap;
+
+ enum update_type update_type;
+ } advmap;
+};
+
+/* IBGP/EBGP identifier. We also have a CONFED peer, which is to say,
+ a peer who's AS is part of our Confederation. */
+enum bgp_peer_sort {
+ BGP_PEER_UNSPECIFIED,
+ BGP_PEER_IBGP,
+ BGP_PEER_EBGP,
+ BGP_PEER_INTERNAL,
+ BGP_PEER_CONFED,
+};
+
+/* BGP message header and packet size. */
+#define BGP_MARKER_SIZE 16
+#define BGP_HEADER_SIZE 19
+#define BGP_STANDARD_MESSAGE_MAX_PACKET_SIZE 4096
+#define BGP_EXTENDED_MESSAGE_MAX_PACKET_SIZE 65535
+#define BGP_MAX_PACKET_SIZE BGP_EXTENDED_MESSAGE_MAX_PACKET_SIZE
+#define BGP_MAX_PACKET_SIZE_OVERFLOW 1024
+
+/*
+ * Trigger delay for bgp_announce_route().
+ */
+#define BGP_ANNOUNCE_ROUTE_SHORT_DELAY_MS 100
+#define BGP_ANNOUNCE_ROUTE_DELAY_MS 500
+
+struct peer_af {
+ /* back pointer to the peer */
+ struct peer *peer;
+
+ /* which subgroup the peer_af belongs to */
+ struct update_subgroup *subgroup;
+
+ /* for being part of an update subgroup's peer list */
+ LIST_ENTRY(peer_af) subgrp_train;
+
+ /* for being part of a packet's peer list */
+ LIST_ENTRY(peer_af) pkt_train;
+
+ struct bpacket *next_pkt_to_send;
+
+ /*
+ * Trigger timer for bgp_announce_route().
+ */
+ struct thread *t_announce_route;
+
+ afi_t afi;
+ safi_t safi;
+ int afid;
+};
+/* BGP GR per peer ds */
+
+#define BGP_PEER_GR_MODE 5
+#define BGP_PEER_GR_EVENT_CMD 6
+
+enum peer_mode {
+ PEER_HELPER = 0,
+ PEER_GR,
+ PEER_DISABLE,
+ PEER_INVALID,
+ PEER_GLOBAL_INHERIT /* This is the default mode */
+
+};
+
+enum peer_gr_command {
+ PEER_GR_CMD = 0,
+ NO_PEER_GR_CMD,
+ PEER_DISABLE_CMD,
+ NO_PEER_DISABLE_CMD,
+ PEER_HELPER_CMD,
+ NO_PEER_HELPER_CMD
+};
+
+typedef unsigned int (*bgp_peer_gr_action_ptr)(struct peer *, int, int);
+
+struct bgp_peer_gr {
+ enum peer_mode next_state;
+ bgp_peer_gr_action_ptr action_fun;
+};
+
+/*
+ * BGP FSM event codes, per RFC 4271 ss. 8.1
+ */
+enum bgp_fsm_rfc_codes {
+ BGP_FSM_ManualStart = 1,
+ BGP_FSM_ManualStop = 2,
+ BGP_FSM_AutomaticStart = 3,
+ BGP_FSM_ManualStart_with_PassiveTcpEstablishment = 4,
+ BGP_FSM_AutomaticStart_with_PassiveTcpEstablishment = 5,
+ BGP_FSM_AutomaticStart_with_DampPeerOscillations = 6,
+ BGP_FSM_AutomaticStart_with_DampPeerOscillations_and_PassiveTcpEstablishment =
+ 7,
+ BGP_FSM_AutomaticStop = 8,
+ BGP_FSM_ConnectRetryTimer_Expires = 9,
+ BGP_FSM_HoldTimer_Expires = 10,
+ BGP_FSM_KeepaliveTimer_Expires = 11,
+ BGP_FSM_DelayOpenTimer_Expires = 12,
+ BGP_FSM_IdleHoldTimer_Expires = 13,
+ BGP_FSM_TcpConnection_Valid = 14,
+ BGP_FSM_Tcp_CR_Invalid = 15,
+ BGP_FSM_Tcp_CR_Acked = 16,
+ BGP_FSM_TcpConnectionConfirmed = 17,
+ BGP_FSM_TcpConnectionFails = 18,
+ BGP_FSM_BGPOpen = 19,
+ BGP_FSM_BGPOpen_with_DelayOpenTimer_running = 20,
+ BGP_FSM_BGPHeaderErr = 21,
+ BGP_FSM_BGPOpenMsgErr = 22,
+ BGP_FSM_OpenCollisionDump = 23,
+ BGP_FSM_NotifMsgVerErr = 24,
+ BGP_FSM_NotifMsg = 25,
+ BGP_FSM_KeepAliveMsg = 26,
+ BGP_FSM_UpdateMsg = 27,
+ BGP_FSM_UpdateMsgErr = 28
+};
+
+/*
+ * BGP finite state machine events
+ *
+ * Note: these do not correspond to RFC-defined event codes. Those are
+ * defined elsewhere.
+ */
+enum bgp_fsm_events {
+ BGP_Start = 1,
+ BGP_Stop,
+ TCP_connection_open,
+ TCP_connection_open_w_delay,
+ TCP_connection_closed,
+ TCP_connection_open_failed,
+ TCP_fatal_error,
+ ConnectRetry_timer_expired,
+ Hold_Timer_expired,
+ KeepAlive_timer_expired,
+ DelayOpen_timer_expired,
+ Receive_OPEN_message,
+ Receive_KEEPALIVE_message,
+ Receive_UPDATE_message,
+ Receive_NOTIFICATION_message,
+ Clearing_Completed,
+ BGP_EVENTS_MAX,
+};
+
+/* BGP finite state machine status. */
+enum bgp_fsm_status {
+ Idle = 1,
+ Connect,
+ Active,
+ OpenSent,
+ OpenConfirm,
+ Established,
+ Clearing,
+ Deleted,
+ BGP_STATUS_MAX,
+};
+
+#define PEER_HOSTNAME(peer) ((peer)->host ? (peer)->host : "(unknown peer)")
+
+struct llgr_info {
+ uint32_t stale_time;
+ uint8_t flags;
+};
+
+/* BGP neighbor structure. */
+struct peer {
+ /* BGP structure. */
+ struct bgp *bgp;
+
+ /* reference count, primarily to allow bgp_process'ing of route_node's
+ * to be done after a struct peer is deleted.
+ *
+ * named 'lock' for hysterical reasons within Quagga.
+ */
+ int lock;
+
+ /* BGP peer group. */
+ struct peer_group *group;
+ uint64_t version[AFI_MAX][SAFI_MAX];
+
+ /* BGP peer_af structures, per configured AF on this peer */
+ struct peer_af *peer_af_array[BGP_AF_MAX];
+
+ /* Peer's remote AS number. */
+ int as_type;
+ as_t as;
+
+ /* Peer's local AS number. */
+ as_t local_as;
+
+ enum bgp_peer_sort sort;
+
+ /* Peer's Change local AS number. */
+ as_t change_local_as;
+
+ /* Remote router ID. */
+ struct in_addr remote_id;
+
+ /* Local router ID. */
+ struct in_addr local_id;
+
+ /* Packet receive and send buffer. */
+ pthread_mutex_t io_mtx; // guards ibuf, obuf
+ struct stream_fifo *ibuf; // packets waiting to be processed
+ struct stream_fifo *obuf; // packets waiting to be written
+
+ /* used as a block to deposit raw wire data to */
+ uint8_t ibuf_scratch[BGP_EXTENDED_MESSAGE_MAX_PACKET_SIZE
+ * BGP_READ_PACKET_MAX];
+ struct ringbuf *ibuf_work; // WiP buffer used by bgp_read() only
+ struct stream *obuf_work; // WiP buffer used to construct packets
+
+ struct stream *curr; // the current packet being parsed
+
+ /* We use a separate stream to encode MP_REACH_NLRI for efficient
+ * NLRI packing. peer->obuf_work stores all the other attributes. The
+ * actual packet is then constructed by concatenating the two.
+ */
+ struct stream *scratch;
+
+ /* the doppelganger peer structure, due to dual TCP conn setup */
+ struct peer *doppelganger;
+
+ /* Status of the peer. */
+ enum bgp_fsm_status status;
+ enum bgp_fsm_status ostatus;
+
+ /* FSM events, stored for debug purposes.
+ * Note: uchar used for reduced memory usage.
+ */
+ enum bgp_fsm_events cur_event;
+ enum bgp_fsm_events last_event;
+ enum bgp_fsm_events last_major_event;
+
+ /* Peer index, used for dumping TABLE_DUMP_V2 format */
+ uint16_t table_dump_index;
+
+ /* Peer information */
+ int fd; /* File descriptor */
+ int ttl; /* TTL of TCP connection to the peer. */
+ int rtt; /* Estimated round-trip-time from TCP_INFO */
+ int rtt_expected; /* Expected round-trip-time for a peer */
+ uint8_t rtt_keepalive_rcv; /* Received count for RTT shutdown */
+ uint8_t rtt_keepalive_conf; /* Configured count for RTT shutdown */
+ int gtsm_hops; /* minimum hopcount to peer */
+ char *desc; /* Description of the peer. */
+ unsigned short port; /* Destination port for peer */
+ char *host; /* Printable address of the peer. */
+ union sockunion su; /* Sockunion address of the peer. */
+#define BGP_PEER_SU_UNSPEC(peer) (peer->su.sa.sa_family == AF_UNSPEC)
+ time_t uptime; /* Last Up/Down time */
+ time_t readtime; /* Last read time */
+ time_t resettime; /* Last reset time */
+
+ char *conf_if; /* neighbor interface config name. */
+ struct interface *ifp; /* corresponding interface */
+ char *ifname; /* bind interface name. */
+ char *update_if;
+ union sockunion *update_source;
+
+ union sockunion *su_local; /* Sockunion of local address. */
+ union sockunion *su_remote; /* Sockunion of remote address. */
+ int shared_network; /* Is this peer shared same network. */
+ struct bgp_nexthop nexthop; /* Nexthop */
+
+ /* Roles in bgp session */
+ uint8_t local_role;
+ uint8_t remote_role;
+#define ROLE_PROVIDER 0
+#define ROLE_RS_SERVER 1
+#define ROLE_RS_CLIENT 2
+#define ROLE_CUSTOMER 3
+#define ROLE_PEER 4
+#define ROLE_UNDEFINED 255
+
+#define ROLE_NAME_MAX_LEN 20
+
+ /* Peer address family configuration. */
+ uint8_t afc[AFI_MAX][SAFI_MAX];
+ uint8_t afc_nego[AFI_MAX][SAFI_MAX];
+ uint8_t afc_adv[AFI_MAX][SAFI_MAX];
+ uint8_t afc_recv[AFI_MAX][SAFI_MAX];
+
+ /* Capability flags (reset in bgp_stop) */
+ uint32_t cap;
+#define PEER_CAP_REFRESH_ADV (1U << 0) /* refresh advertised */
+#define PEER_CAP_REFRESH_OLD_RCV (1U << 1) /* refresh old received */
+#define PEER_CAP_REFRESH_NEW_RCV (1U << 2) /* refresh rfc received */
+#define PEER_CAP_DYNAMIC_ADV (1U << 3) /* dynamic advertised */
+#define PEER_CAP_DYNAMIC_RCV (1U << 4) /* dynamic received */
+#define PEER_CAP_RESTART_ADV (1U << 5) /* restart advertised */
+#define PEER_CAP_RESTART_RCV (1U << 6) /* restart received */
+#define PEER_CAP_AS4_ADV (1U << 7) /* as4 advertised */
+#define PEER_CAP_AS4_RCV (1U << 8) /* as4 received */
+/* sent graceful-restart restart (R) bit */
+#define PEER_CAP_GRACEFUL_RESTART_R_BIT_ADV (1U << 9)
+/* received graceful-restart restart (R) bit */
+#define PEER_CAP_GRACEFUL_RESTART_R_BIT_RCV (1U << 10)
+#define PEER_CAP_ADDPATH_ADV (1U << 11) /* addpath advertised */
+#define PEER_CAP_ADDPATH_RCV (1U << 12) /* addpath received */
+#define PEER_CAP_ENHE_ADV (1U << 13) /* Extended nexthop advertised */
+#define PEER_CAP_ENHE_RCV (1U << 14) /* Extended nexthop received */
+#define PEER_CAP_HOSTNAME_ADV (1U << 15) /* hostname advertised */
+#define PEER_CAP_HOSTNAME_RCV (1U << 16) /* hostname received */
+#define PEER_CAP_ENHANCED_RR_ADV (1U << 17) /* enhanced rr advertised */
+#define PEER_CAP_ENHANCED_RR_RCV (1U << 18) /* enhanced rr received */
+#define PEER_CAP_EXTENDED_MESSAGE_ADV (1U << 19)
+#define PEER_CAP_EXTENDED_MESSAGE_RCV (1U << 20)
+#define PEER_CAP_LLGR_ADV (1U << 21)
+#define PEER_CAP_LLGR_RCV (1U << 22)
+/* sent graceful-restart notification (N) bit */
+#define PEER_CAP_GRACEFUL_RESTART_N_BIT_ADV (1U << 23)
+/* received graceful-restart notification (N) bit */
+#define PEER_CAP_GRACEFUL_RESTART_N_BIT_RCV (1U << 24)
+#define PEER_CAP_ROLE_ADV (1U << 25) /* role advertised */
+#define PEER_CAP_ROLE_RCV (1U << 26) /* role received */
+
+ /* Capability flags (reset in bgp_stop) */
+ uint32_t af_cap[AFI_MAX][SAFI_MAX];
+#define PEER_CAP_ORF_PREFIX_SM_ADV (1U << 0) /* send-mode advertised */
+#define PEER_CAP_ORF_PREFIX_RM_ADV (1U << 1) /* receive-mode advertised */
+#define PEER_CAP_ORF_PREFIX_SM_RCV (1U << 2) /* send-mode received */
+#define PEER_CAP_ORF_PREFIX_RM_RCV (1U << 3) /* receive-mode received */
+#define PEER_CAP_ORF_PREFIX_SM_OLD_RCV (1U << 4) /* send-mode received */
+#define PEER_CAP_ORF_PREFIX_RM_OLD_RCV (1U << 5) /* receive-mode received */
+#define PEER_CAP_RESTART_AF_RCV (1U << 6) /* graceful restart afi/safi received */
+#define PEER_CAP_RESTART_AF_PRESERVE_RCV (1U << 7) /* graceful restart afi/safi F-bit received */
+#define PEER_CAP_ADDPATH_AF_TX_ADV (1U << 8) /* addpath tx advertised */
+#define PEER_CAP_ADDPATH_AF_TX_RCV (1U << 9) /* addpath tx received */
+#define PEER_CAP_ADDPATH_AF_RX_ADV (1U << 10) /* addpath rx advertised */
+#define PEER_CAP_ADDPATH_AF_RX_RCV (1U << 11) /* addpath rx received */
+#define PEER_CAP_ENHE_AF_ADV (1U << 12) /* Extended nexthopi afi/safi advertised */
+#define PEER_CAP_ENHE_AF_RCV (1U << 13) /* Extended nexthop afi/safi received */
+#define PEER_CAP_ENHE_AF_NEGO (1U << 14) /* Extended nexthop afi/safi negotiated */
+#define PEER_CAP_LLGR_AF_ADV (1U << 15)
+#define PEER_CAP_LLGR_AF_RCV (1U << 16)
+
+ /* Global configuration flags. */
+ /*
+ * Parallel array to flags that indicates whether each flag originates
+ * from a peer-group or if it is config that is specific to this
+ * individual peer. If a flag is set independent of the peer-group, the
+ * same bit should be set here. If this peer is a peer-group, this
+ * memory region should be all zeros.
+ *
+ * The assumption is that the default state for all flags is unset,
+ * so if a flag is unset, the corresponding override flag is unset too.
+ * However if a flag is set, the corresponding override flag is set.
+ */
+ uint32_t flags_override;
+ /*
+ * Parallel array to flags that indicates whether the default behavior
+ * of *flags_override* should be inverted. If a flag is unset and the
+ * corresponding invert flag is set, the corresponding override flag
+ * would be set. However if a flag is set and the corresponding invert
+ * flag is unset, the corresponding override flag would be unset.
+ *
+ * This can be used for attributes like *send-community*, which are
+ * implicitely enabled and have to be disabled explicitely, compared to
+ * 'normal' attributes like *next-hop-self* which are implicitely set.
+ *
+ * All operations dealing with flags should apply the following boolean
+ * logic to keep the internal flag system in a sane state:
+ *
+ * value=0 invert=0 Inherit flag if member, otherwise unset flag
+ * value=0 invert=1 Unset flag unconditionally
+ * value=1 invert=0 Set flag unconditionally
+ * value=1 invert=1 Inherit flag if member, otherwise set flag
+ *
+ * Contrary to the implementation of *flags_override*, the flag
+ * inversion state can be set either on the peer OR the peer *and* the
+ * peer-group. This was done on purpose, as the inversion state of a
+ * flag can be determined on either the peer or the peer-group.
+ *
+ * Example: Enabling the cisco configuration mode inverts all flags
+ * related to *send-community* unconditionally for both peer-groups and
+ * peers.
+ *
+ * This behavior is different for interface peers though, which enable
+ * the *extended-nexthop* flag by default, which regular peers do not.
+ * As the peer-group can contain both regular and interface peers, the
+ * flag inversion state must be set on the peer only.
+ *
+ * When a peer inherits the configuration from a peer-group and the
+ * inversion state of the flag differs between peer and peer-group, the
+ * newly set value must equal to the inverted state of the peer-group.
+ */
+ uint32_t flags_invert;
+ /*
+ * Effective array for storing the peer/peer-group flags. In case of a
+ * peer-group, the peer-specific overrides (see flags_override and
+ * flags_invert) must be respected.
+ */
+ uint64_t flags;
+#define PEER_FLAG_PASSIVE (1ULL << 0) /* passive mode */
+#define PEER_FLAG_SHUTDOWN (1ULL << 1) /* shutdown */
+#define PEER_FLAG_DONT_CAPABILITY (1ULL << 2) /* dont-capability */
+#define PEER_FLAG_OVERRIDE_CAPABILITY (1ULL << 3) /* override-capability */
+#define PEER_FLAG_STRICT_CAP_MATCH (1ULL << 4) /* strict-match */
+#define PEER_FLAG_DYNAMIC_CAPABILITY (1ULL << 5) /* dynamic capability */
+#define PEER_FLAG_DISABLE_CONNECTED_CHECK (1ULL << 6) /* disable-connected-check */
+#define PEER_FLAG_LOCAL_AS_NO_PREPEND (1ULL << 7) /* local-as no-prepend */
+#define PEER_FLAG_LOCAL_AS_REPLACE_AS (1ULL << 8) /* local-as no-prepend replace-as */
+#define PEER_FLAG_DELETE (1ULL << 9) /* mark the peer for deleting */
+#define PEER_FLAG_CONFIG_NODE (1ULL << 10) /* the node to update configs on */
+#define PEER_FLAG_LONESOUL (1ULL << 11)
+#define PEER_FLAG_DYNAMIC_NEIGHBOR (1ULL << 12) /* dynamic neighbor */
+#define PEER_FLAG_CAPABILITY_ENHE (1ULL << 13) /* Extended next-hop (rfc 5549)*/
+#define PEER_FLAG_IFPEER_V6ONLY (1ULL << 14) /* if-based peer is v6 only */
+#define PEER_FLAG_IS_RFAPI_HD (1ULL << 15) /* attached to rfapi HD */
+#define PEER_FLAG_ENFORCE_FIRST_AS (1ULL << 16) /* enforce-first-as */
+#define PEER_FLAG_ROUTEADV (1ULL << 17) /* route advertise */
+#define PEER_FLAG_TIMER (1ULL << 18) /* keepalive & holdtime */
+#define PEER_FLAG_TIMER_CONNECT (1ULL << 19) /* connect timer */
+#define PEER_FLAG_PASSWORD (1ULL << 20) /* password */
+#define PEER_FLAG_LOCAL_AS (1ULL << 21) /* local-as */
+#define PEER_FLAG_UPDATE_SOURCE (1ULL << 22) /* update-source */
+
+ /* BGP-GR Peer related flags */
+#define PEER_FLAG_GRACEFUL_RESTART_HELPER (1ULL << 23) /* Helper */
+#define PEER_FLAG_GRACEFUL_RESTART (1ULL << 24) /* Graceful Restart */
+#define PEER_FLAG_GRACEFUL_RESTART_GLOBAL_INHERIT (1ULL << 25) /* Global-Inherit */
+#define PEER_FLAG_RTT_SHUTDOWN (1ULL << 26) /* shutdown rtt */
+#define PEER_FLAG_TIMER_DELAYOPEN (1ULL << 27) /* delayopen timer */
+#define PEER_FLAG_TCP_MSS (1ULL << 28) /* tcp-mss */
+/* Disable IEEE floating-point link bandwidth encoding in
+ * extended communities.
+ */
+#define PEER_FLAG_DISABLE_LINK_BW_ENCODING_IEEE (1ULL << 29)
+/* force the extended format for Optional Parameters in OPEN message */
+#define PEER_FLAG_EXTENDED_OPT_PARAMS (1ULL << 30)
+
+ /* BGP Open Policy flags.
+ * Enforce using roles on both sides:
+ * `local-role ROLE strict-mode` configured.
+ */
+#define PEER_FLAG_ROLE_STRICT_MODE (1ULL << 31)
+ /* `local-role` configured */
+#define PEER_FLAG_ROLE (1ULL << 32)
+#define PEER_FLAG_PORT (1ULL << 33)
+
+ /*
+ *GR-Disabled mode means unset PEER_FLAG_GRACEFUL_RESTART
+ *& PEER_FLAG_GRACEFUL_RESTART_HELPER
+ *and PEER_FLAG_GRACEFUL_RESTART_GLOBAL_INHERIT
+ */
+
+ struct bgp_peer_gr PEER_GR_FSM[BGP_PEER_GR_MODE][BGP_PEER_GR_EVENT_CMD];
+ enum peer_mode peer_gr_present_state;
+ /* Non stop forwarding afi-safi count for BGP gr feature*/
+ uint8_t nsf_af_count;
+
+ uint8_t peer_gr_new_status_flag;
+#define PEER_GRACEFUL_RESTART_NEW_STATE_HELPER (1U << 0)
+#define PEER_GRACEFUL_RESTART_NEW_STATE_RESTART (1U << 1)
+#define PEER_GRACEFUL_RESTART_NEW_STATE_INHERIT (1U << 2)
+
+ /* outgoing message sent in CEASE_ADMIN_SHUTDOWN notify */
+ char *tx_shutdown_message;
+
+ /* NSF mode (graceful restart) */
+ uint8_t nsf[AFI_MAX][SAFI_MAX];
+ /* EOR Send time */
+ time_t eor_stime[AFI_MAX][SAFI_MAX];
+ /* Last update packet sent time */
+ time_t pkt_stime[AFI_MAX][SAFI_MAX];
+
+ /* Peer Per AF flags */
+ /*
+ * Please consult the comments for *flags_override*, *flags_invert* and
+ * *flags* to understand what these three arrays do. The address-family
+ * specific attributes are being treated the exact same way as global
+ * peer attributes.
+ */
+ uint64_t af_flags_override[AFI_MAX][SAFI_MAX];
+ uint64_t af_flags_invert[AFI_MAX][SAFI_MAX];
+ uint64_t af_flags[AFI_MAX][SAFI_MAX];
+#define PEER_FLAG_SEND_COMMUNITY (1ULL << 0)
+#define PEER_FLAG_SEND_EXT_COMMUNITY (1ULL << 1)
+#define PEER_FLAG_NEXTHOP_SELF (1ULL << 2)
+#define PEER_FLAG_REFLECTOR_CLIENT (1ULL << 3)
+#define PEER_FLAG_RSERVER_CLIENT (1ULL << 4)
+#define PEER_FLAG_SOFT_RECONFIG (1ULL << 5)
+#define PEER_FLAG_AS_PATH_UNCHANGED (1ULL << 6)
+#define PEER_FLAG_NEXTHOP_UNCHANGED (1ULL << 7)
+#define PEER_FLAG_MED_UNCHANGED (1ULL << 8)
+#define PEER_FLAG_DEFAULT_ORIGINATE (1ULL << 9)
+#define PEER_FLAG_REMOVE_PRIVATE_AS (1ULL << 10)
+#define PEER_FLAG_ALLOWAS_IN (1ULL << 11)
+#define PEER_FLAG_ORF_PREFIX_SM (1ULL << 12)
+#define PEER_FLAG_ORF_PREFIX_RM (1ULL << 13)
+#define PEER_FLAG_MAX_PREFIX (1ULL << 14)
+#define PEER_FLAG_MAX_PREFIX_WARNING (1ULL << 15)
+#define PEER_FLAG_NEXTHOP_LOCAL_UNCHANGED (1ULL << 16)
+#define PEER_FLAG_FORCE_NEXTHOP_SELF (1ULL << 17)
+#define PEER_FLAG_REMOVE_PRIVATE_AS_ALL (1ULL << 18)
+#define PEER_FLAG_REMOVE_PRIVATE_AS_REPLACE (1ULL << 19)
+#define PEER_FLAG_AS_OVERRIDE (1ULL << 20)
+#define PEER_FLAG_REMOVE_PRIVATE_AS_ALL_REPLACE (1ULL << 21)
+#define PEER_FLAG_WEIGHT (1ULL << 24)
+#define PEER_FLAG_ALLOWAS_IN_ORIGIN (1ULL << 25)
+#define PEER_FLAG_SEND_LARGE_COMMUNITY (1ULL << 26)
+#define PEER_FLAG_MAX_PREFIX_OUT (1ULL << 27)
+#define PEER_FLAG_MAX_PREFIX_FORCE (1ULL << 28)
+#define PEER_FLAG_DISABLE_ADDPATH_RX (1ULL << 29)
+#define PEER_FLAG_SOO (1ULL << 30)
+
+ enum bgp_addpath_strat addpath_type[AFI_MAX][SAFI_MAX];
+
+ /* MD5 password */
+ char *password;
+
+ /* default-originate route-map. */
+ struct {
+ char *name;
+ struct route_map *map;
+ } default_rmap[AFI_MAX][SAFI_MAX];
+
+ /* Peer status flags. */
+ uint16_t sflags;
+#define PEER_STATUS_ACCEPT_PEER (1U << 0) /* accept peer */
+#define PEER_STATUS_PREFIX_OVERFLOW (1U << 1) /* prefix-overflow */
+#define PEER_STATUS_CAPABILITY_OPEN (1U << 2) /* capability open send */
+#define PEER_STATUS_HAVE_ACCEPT (1U << 3) /* accept peer's parent */
+#define PEER_STATUS_GROUP (1U << 4) /* peer-group conf */
+#define PEER_STATUS_NSF_MODE (1U << 5) /* NSF aware peer */
+#define PEER_STATUS_NSF_WAIT (1U << 6) /* wait comeback peer */
+/* received extended format encoding for OPEN message */
+#define PEER_STATUS_EXT_OPT_PARAMS_LENGTH (1U << 7)
+
+ /* Peer status af flags (reset in bgp_stop) */
+ uint16_t af_sflags[AFI_MAX][SAFI_MAX];
+#define PEER_STATUS_ORF_PREFIX_SEND (1U << 0) /* prefix-list send peer */
+#define PEER_STATUS_ORF_WAIT_REFRESH (1U << 1) /* wait refresh received peer */
+#define PEER_STATUS_PREFIX_THRESHOLD (1U << 2) /* exceed prefix-threshold */
+#define PEER_STATUS_PREFIX_LIMIT (1U << 3) /* exceed prefix-limit */
+#define PEER_STATUS_EOR_SEND (1U << 4) /* end-of-rib send to peer */
+#define PEER_STATUS_EOR_RECEIVED (1U << 5) /* end-of-rib received from peer */
+#define PEER_STATUS_ENHANCED_REFRESH (1U << 6) /* Enhanced Route Refresh */
+#define PEER_STATUS_BORR_SEND (1U << 7) /* BoRR send to peer */
+#define PEER_STATUS_BORR_RECEIVED (1U << 8) /* BoRR received from peer */
+#define PEER_STATUS_EORR_SEND (1U << 9) /* EoRR send to peer */
+#define PEER_STATUS_EORR_RECEIVED (1U << 10) /* EoRR received from peer */
+/* LLGR aware peer */
+#define PEER_STATUS_LLGR_WAIT (1U << 11)
+#define PEER_STATUS_REFRESH_PENDING (1U << 12) /* refresh request from peer */
+
+ /* Configured timer values. */
+ _Atomic uint32_t holdtime;
+ _Atomic uint32_t keepalive;
+ _Atomic uint32_t connect;
+ _Atomic uint32_t routeadv;
+ _Atomic uint32_t delayopen;
+
+ /* Timer values. */
+ _Atomic uint32_t v_start;
+ _Atomic uint32_t v_connect;
+ _Atomic uint32_t v_holdtime;
+ _Atomic uint32_t v_keepalive;
+ _Atomic uint32_t v_routeadv;
+ _Atomic uint32_t v_delayopen;
+ _Atomic uint32_t v_pmax_restart;
+ _Atomic uint32_t v_gr_restart;
+
+ /* Threads. */
+ struct thread *t_read;
+ struct thread *t_write;
+ struct thread *t_start;
+ struct thread *t_connect_check_r;
+ struct thread *t_connect_check_w;
+ struct thread *t_connect;
+ struct thread *t_holdtime;
+ struct thread *t_routeadv;
+ struct thread *t_delayopen;
+ struct thread *t_pmax_restart;
+ struct thread *t_gr_restart;
+ struct thread *t_gr_stale;
+ struct thread *t_llgr_stale[AFI_MAX][SAFI_MAX];
+ struct thread *t_generate_updgrp_packets;
+ struct thread *t_process_packet;
+ struct thread *t_process_packet_error;
+ struct thread *t_refresh_stalepath;
+
+ /* Thread flags. */
+ _Atomic uint32_t thread_flags;
+#define PEER_THREAD_WRITES_ON (1U << 0)
+#define PEER_THREAD_READS_ON (1U << 1)
+#define PEER_THREAD_KEEPALIVES_ON (1U << 2)
+#define PEER_THREAD_SUBGRP_ADV_DELAY (1U << 3)
+
+ /* workqueues */
+ struct work_queue *clear_node_queue;
+
+#define PEER_TOTAL_RX(peer) \
+ atomic_load_explicit(&peer->open_in, memory_order_relaxed) \
+ + atomic_load_explicit(&peer->update_in, memory_order_relaxed) \
+ + atomic_load_explicit(&peer->notify_in, memory_order_relaxed) \
+ + atomic_load_explicit(&peer->refresh_in, \
+ memory_order_relaxed) \
+ + atomic_load_explicit(&peer->keepalive_in, \
+ memory_order_relaxed) \
+ + atomic_load_explicit(&peer->dynamic_cap_in, \
+ memory_order_relaxed)
+
+#define PEER_TOTAL_TX(peer) \
+ atomic_load_explicit(&peer->open_out, memory_order_relaxed) \
+ + atomic_load_explicit(&peer->update_out, \
+ memory_order_relaxed) \
+ + atomic_load_explicit(&peer->notify_out, \
+ memory_order_relaxed) \
+ + atomic_load_explicit(&peer->refresh_out, \
+ memory_order_relaxed) \
+ + atomic_load_explicit(&peer->keepalive_out, \
+ memory_order_relaxed) \
+ + atomic_load_explicit(&peer->dynamic_cap_out, \
+ memory_order_relaxed)
+
+ /* Statistics field */
+ _Atomic uint32_t open_in; /* Open message input count */
+ _Atomic uint32_t open_out; /* Open message output count */
+ _Atomic uint32_t update_in; /* Update message input count */
+ _Atomic uint32_t update_out; /* Update message ouput count */
+ _Atomic time_t update_time; /* Update message received time. */
+ _Atomic uint32_t keepalive_in; /* Keepalive input count */
+ _Atomic uint32_t keepalive_out; /* Keepalive output count */
+ _Atomic uint32_t notify_in; /* Notify input count */
+ _Atomic uint32_t notify_out; /* Notify output count */
+ _Atomic uint32_t refresh_in; /* Route Refresh input count */
+ _Atomic uint32_t refresh_out; /* Route Refresh output count */
+ _Atomic uint32_t dynamic_cap_in; /* Dynamic Capability input count. */
+ _Atomic uint32_t dynamic_cap_out; /* Dynamic Capability output count. */
+
+ uint32_t stat_pfx_filter;
+ uint32_t stat_pfx_aspath_loop;
+ uint32_t stat_pfx_originator_loop;
+ uint32_t stat_pfx_cluster_loop;
+ uint32_t stat_pfx_nh_invalid;
+ uint32_t stat_pfx_dup_withdraw;
+ uint32_t stat_upd_7606; /* RFC7606: treat-as-withdraw */
+
+ /* BGP state count */
+ uint32_t established; /* Established */
+ uint32_t dropped; /* Dropped */
+
+ /* Update delay related fields */
+ uint8_t update_delay_over; /* When this is set, BGP is no more waiting
+ for EOR */
+
+ /* Syncronization list and time. */
+ struct bgp_synchronize *sync[AFI_MAX][SAFI_MAX];
+ time_t synctime;
+ /* timestamp when the last UPDATE msg was written */
+ _Atomic time_t last_write;
+ /* timestamp when the last msg was written */
+ _Atomic time_t last_update;
+
+ /* only updated under io_mtx.
+ * last_sendq_warn is only for ratelimiting log warning messages.
+ */
+ time_t last_sendq_ok, last_sendq_warn;
+
+ /* Notify data. */
+ struct bgp_notify notify;
+
+ /* Filter structure. */
+ struct bgp_filter filter[AFI_MAX][SAFI_MAX];
+
+ /*
+ * Parallel array to filter that indicates whether each filter
+ * originates from a peer-group or if it is config that is specific to
+ * this individual peer. If a filter is set independent of the
+ * peer-group the appropriate bit should be set here. If this peer is a
+ * peer-group, this memory region should be all zeros. The assumption
+ * is that the default state for all flags is unset. Due to filters
+ * having a direction (e.g. in/out/...), this array has a third
+ * dimension for storing the overrides independently per direction.
+ *
+ * Notes:
+ * - if a filter for an individual peer is unset, the corresponding
+ * override flag is unset and the peer is considered to be back in
+ * sync with the peer-group.
+ * - This does *not* contain the filter values, rather it contains
+ * whether the filter in filter (struct bgp_filter) is peer-specific.
+ */
+ uint8_t filter_override[AFI_MAX][SAFI_MAX][FILTER_MAX];
+#define PEER_FT_DISTRIBUTE_LIST (1U << 0) /* distribute-list */
+#define PEER_FT_FILTER_LIST (1U << 1) /* filter-list */
+#define PEER_FT_PREFIX_LIST (1U << 2) /* prefix-list */
+#define PEER_FT_ROUTE_MAP (1U << 3) /* route-map */
+#define PEER_FT_UNSUPPRESS_MAP (1U << 4) /* unsuppress-map */
+#define PEER_FT_ADVERTISE_MAP (1U << 5) /* advertise-map */
+
+ /* ORF Prefix-list */
+ struct prefix_list *orf_plist[AFI_MAX][SAFI_MAX];
+
+ /* Text description of last attribute rcvd */
+ char rcvd_attr_str[BUFSIZ];
+
+ /* Track if we printed the attribute in debugs */
+ int rcvd_attr_printed;
+
+ /* Accepted prefix count */
+ uint32_t pcount[AFI_MAX][SAFI_MAX];
+
+ /* Max prefix count. */
+ uint32_t pmax[AFI_MAX][SAFI_MAX];
+ uint8_t pmax_threshold[AFI_MAX][SAFI_MAX];
+ uint16_t pmax_restart[AFI_MAX][SAFI_MAX];
+#define MAXIMUM_PREFIX_THRESHOLD_DEFAULT 75
+
+ /* Send prefix count. */
+ uint32_t pmax_out[AFI_MAX][SAFI_MAX];
+
+ /* allowas-in. */
+ char allowas_in[AFI_MAX][SAFI_MAX];
+
+ /* soo */
+ struct ecommunity *soo[AFI_MAX][SAFI_MAX];
+
+ /* weight */
+ unsigned long weight[AFI_MAX][SAFI_MAX];
+
+ /* peer reset cause */
+ uint8_t last_reset;
+#define PEER_DOWN_RID_CHANGE 1U /* bgp router-id command */
+#define PEER_DOWN_REMOTE_AS_CHANGE 2U /* neighbor remote-as command */
+#define PEER_DOWN_LOCAL_AS_CHANGE 3U /* neighbor local-as command */
+#define PEER_DOWN_CLID_CHANGE 4U /* bgp cluster-id command */
+#define PEER_DOWN_CONFED_ID_CHANGE 5U /* bgp confederation id command */
+#define PEER_DOWN_CONFED_PEER_CHANGE 6U /* bgp confederation peer command */
+#define PEER_DOWN_RR_CLIENT_CHANGE 7U /* neighbor rr-client command */
+#define PEER_DOWN_RS_CLIENT_CHANGE 8U /* neighbor rs-client command */
+#define PEER_DOWN_UPDATE_SOURCE_CHANGE 9U /* neighbor update-source command */
+#define PEER_DOWN_AF_ACTIVATE 10U /* neighbor activate command */
+#define PEER_DOWN_USER_SHUTDOWN 11U /* neighbor shutdown command */
+#define PEER_DOWN_USER_RESET 12U /* clear ip bgp command */
+#define PEER_DOWN_NOTIFY_RECEIVED 13U /* notification received */
+#define PEER_DOWN_NOTIFY_SEND 14U /* notification send */
+#define PEER_DOWN_CLOSE_SESSION 15U /* tcp session close */
+#define PEER_DOWN_NEIGHBOR_DELETE 16U /* neghbor delete */
+#define PEER_DOWN_RMAP_BIND 17U /* neghbor peer-group command */
+#define PEER_DOWN_RMAP_UNBIND 18U /* no neighbor peer-group command */
+#define PEER_DOWN_CAPABILITY_CHANGE 19U /* neighbor capability command */
+#define PEER_DOWN_PASSIVE_CHANGE 20U /* neighbor passive command */
+#define PEER_DOWN_MULTIHOP_CHANGE 21U /* neighbor multihop command */
+#define PEER_DOWN_NSF_CLOSE_SESSION 22U /* NSF tcp session close */
+#define PEER_DOWN_V6ONLY_CHANGE 23U /* if-based peering v6only toggled */
+#define PEER_DOWN_BFD_DOWN 24U /* BFD down */
+#define PEER_DOWN_IF_DOWN 25U /* Interface down */
+#define PEER_DOWN_NBR_ADDR_DEL 26U /* Peer address lost */
+#define PEER_DOWN_WAITING_NHT 27U /* Waiting for NHT to resolve */
+#define PEER_DOWN_NBR_ADDR 28U /* Waiting for peer IPv6 IP Addr */
+#define PEER_DOWN_VRF_UNINIT 29U /* Associated VRF is not init yet */
+#define PEER_DOWN_NOAFI_ACTIVATED 30U /* No AFI/SAFI activated for peer */
+#define PEER_DOWN_AS_SETS_REJECT 31U /* Reject routes with AS_SET */
+#define PEER_DOWN_WAITING_OPEN 32U /* Waiting for open to succeed */
+#define PEER_DOWN_PFX_COUNT 33U /* Reached received prefix count */
+#define PEER_DOWN_SOCKET_ERROR 34U /* Some socket error happened */
+ /*
+ * Remember to update peer_down_str in bgp_fsm.c when you add
+ * a new value to the last_reset reason
+ */
+
+ uint16_t last_reset_cause_size;
+ uint8_t last_reset_cause[BGP_MAX_PACKET_SIZE];
+
+ /* The kind of route-map Flags.*/
+ uint16_t rmap_type;
+#define PEER_RMAP_TYPE_IN (1U << 0) /* neighbor route-map in */
+#define PEER_RMAP_TYPE_OUT (1U << 1) /* neighbor route-map out */
+#define PEER_RMAP_TYPE_NETWORK (1U << 2) /* network route-map */
+#define PEER_RMAP_TYPE_REDISTRIBUTE (1U << 3) /* redistribute route-map */
+#define PEER_RMAP_TYPE_DEFAULT (1U << 4) /* default-originate route-map */
+#define PEER_RMAP_TYPE_NOSET (1U << 5) /* not allow to set commands */
+#define PEER_RMAP_TYPE_IMPORT (1U << 6) /* neighbor route-map import */
+#define PEER_RMAP_TYPE_EXPORT (1U << 7) /* neighbor route-map export */
+#define PEER_RMAP_TYPE_AGGREGATE (1U << 8) /* aggregate-address route-map */
+
+ /** Peer overwrite configuration. */
+ struct bfd_session_config {
+ /**
+ * Manual configuration bit.
+ *
+ * This flag only makes sense for real peers (and not groups),
+ * it keeps track if the user explicitly configured BFD for a
+ * peer.
+ */
+ bool manual;
+ /** Control Plane Independent. */
+ bool cbit;
+ /** Detection multiplier. */
+ uint8_t detection_multiplier;
+ /** Minimum required RX interval. */
+ uint32_t min_rx;
+ /** Minimum required TX interval. */
+ uint32_t min_tx;
+ /** Profile name. */
+ char profile[BFD_PROFILE_NAME_LEN];
+ /** Peer BFD session */
+ struct bfd_session_params *session;
+ } * bfd_config;
+
+ /* hostname and domainname advertised by host */
+ char *hostname;
+ char *domainname;
+
+ /* Sender side AS path loop detection. */
+ bool as_path_loop_detection;
+
+ /* Extended Message Support */
+ uint16_t max_packet_size;
+
+ /* Conditional advertisement */
+ bool advmap_config_change[AFI_MAX][SAFI_MAX];
+ bool advmap_table_change;
+
+ /* set TCP max segment size */
+ uint32_t tcp_mss;
+
+ /* Long-lived Graceful Restart */
+ struct llgr_info llgr[AFI_MAX][SAFI_MAX];
+
+ bool shut_during_cfg;
+
+ QOBJ_FIELDS;
+};
+DECLARE_QOBJ_TYPE(peer);
+
+/* Inherit peer attribute from peer-group. */
+#define PEER_ATTR_INHERIT(peer, group, attr) \
+ ((peer)->attr = (group)->conf->attr)
+#define PEER_STR_ATTR_INHERIT(peer, group, attr, mt) \
+ do { \
+ XFREE(mt, (peer)->attr); \
+ if ((group)->conf->attr) \
+ (peer)->attr = XSTRDUP(mt, (group)->conf->attr); \
+ else \
+ (peer)->attr = NULL; \
+ } while (0)
+#define PEER_SU_ATTR_INHERIT(peer, group, attr) \
+ do { \
+ if ((peer)->attr) \
+ sockunion_free((peer)->attr); \
+ if ((group)->conf->attr) \
+ (peer)->attr = sockunion_dup((group)->conf->attr); \
+ else \
+ (peer)->attr = NULL; \
+ } while (0)
+
+/* Check if suppress start/restart of sessions to peer. */
+#define BGP_PEER_START_SUPPRESSED(P) \
+ (CHECK_FLAG((P)->flags, PEER_FLAG_SHUTDOWN) || \
+ CHECK_FLAG((P)->sflags, PEER_STATUS_PREFIX_OVERFLOW) || \
+ CHECK_FLAG((P)->bgp->flags, BGP_FLAG_SHUTDOWN) || \
+ (P)->shut_during_cfg)
+
+#define PEER_ROUTE_ADV_DELAY(peer) \
+ (CHECK_FLAG(peer->thread_flags, PEER_THREAD_SUBGRP_ADV_DELAY))
+
+#define PEER_PASSWORD_MINLEN (1)
+#define PEER_PASSWORD_MAXLEN (80)
+
+/* This structure's member directly points incoming packet data
+ stream. */
+struct bgp_nlri {
+ /* AFI. */
+ uint16_t afi; /* iana_afi_t */
+
+ /* SAFI. */
+ uint8_t safi; /* iana_safi_t */
+
+ /* Pointer to NLRI byte stream. */
+ uint8_t *nlri;
+
+ /* Length of whole NLRI. */
+ bgp_size_t length;
+};
+
+/* BGP versions. */
+#define BGP_VERSION_4 4
+
+/* Default BGP port number. */
+#define BGP_PORT_DEFAULT 179
+
+/* Extended BGP Administrative Shutdown Communication */
+#define BGP_ADMIN_SHUTDOWN_MSG_LEN 255
+
+/* BGP minimum message size. */
+#define BGP_MSG_OPEN_MIN_SIZE (BGP_HEADER_SIZE + 10)
+#define BGP_MSG_UPDATE_MIN_SIZE (BGP_HEADER_SIZE + 4)
+#define BGP_MSG_NOTIFY_MIN_SIZE (BGP_HEADER_SIZE + 2)
+#define BGP_MSG_KEEPALIVE_MIN_SIZE (BGP_HEADER_SIZE + 0)
+#define BGP_MSG_ROUTE_REFRESH_MIN_SIZE (BGP_HEADER_SIZE + 4)
+#define BGP_MSG_CAPABILITY_MIN_SIZE (BGP_HEADER_SIZE + 3)
+
+/* BGP message types. */
+#define BGP_MSG_OPEN 1
+#define BGP_MSG_UPDATE 2
+#define BGP_MSG_NOTIFY 3
+#define BGP_MSG_KEEPALIVE 4
+#define BGP_MSG_ROUTE_REFRESH_NEW 5
+#define BGP_MSG_CAPABILITY 6
+#define BGP_MSG_ROUTE_REFRESH_OLD 128
+
+/* BGP open optional parameter. */
+#define BGP_OPEN_OPT_AUTH 1
+#define BGP_OPEN_OPT_CAP 2
+
+/* BGP4 attribute type codes. */
+#define BGP_ATTR_ORIGIN 1
+#define BGP_ATTR_AS_PATH 2
+#define BGP_ATTR_NEXT_HOP 3
+#define BGP_ATTR_MULTI_EXIT_DISC 4
+#define BGP_ATTR_LOCAL_PREF 5
+#define BGP_ATTR_ATOMIC_AGGREGATE 6
+#define BGP_ATTR_AGGREGATOR 7
+#define BGP_ATTR_COMMUNITIES 8
+#define BGP_ATTR_ORIGINATOR_ID 9
+#define BGP_ATTR_CLUSTER_LIST 10
+#define BGP_ATTR_MP_REACH_NLRI 14
+#define BGP_ATTR_MP_UNREACH_NLRI 15
+#define BGP_ATTR_EXT_COMMUNITIES 16
+#define BGP_ATTR_AS4_PATH 17
+#define BGP_ATTR_AS4_AGGREGATOR 18
+#define BGP_ATTR_AS_PATHLIMIT 21
+#define BGP_ATTR_PMSI_TUNNEL 22
+#define BGP_ATTR_ENCAP 23
+#define BGP_ATTR_IPV6_EXT_COMMUNITIES 25
+#define BGP_ATTR_LARGE_COMMUNITIES 32
+#define BGP_ATTR_OTC 35
+#define BGP_ATTR_PREFIX_SID 40
+#define BGP_ATTR_SRTE_COLOR 51
+#ifdef ENABLE_BGP_VNC_ATTR
+#define BGP_ATTR_VNC 255
+#endif
+
+/* BGP update origin. */
+#define BGP_ORIGIN_IGP 0
+#define BGP_ORIGIN_EGP 1
+#define BGP_ORIGIN_INCOMPLETE 2
+#define BGP_ORIGIN_UNSPECIFIED 255
+
+/* BGP notify message codes. */
+#define BGP_NOTIFY_HEADER_ERR 1
+#define BGP_NOTIFY_OPEN_ERR 2
+#define BGP_NOTIFY_UPDATE_ERR 3
+#define BGP_NOTIFY_HOLD_ERR 4
+#define BGP_NOTIFY_FSM_ERR 5
+#define BGP_NOTIFY_CEASE 6
+#define BGP_NOTIFY_ROUTE_REFRESH_ERR 7
+
+/* Subcodes for BGP Finite State Machine Error */
+#define BGP_NOTIFY_FSM_ERR_SUBCODE_UNSPECIFIC 0
+#define BGP_NOTIFY_FSM_ERR_SUBCODE_OPENSENT 1
+#define BGP_NOTIFY_FSM_ERR_SUBCODE_OPENCONFIRM 2
+#define BGP_NOTIFY_FSM_ERR_SUBCODE_ESTABLISHED 3
+
+#define BGP_NOTIFY_SUBCODE_UNSPECIFIC 0
+
+/* BGP_NOTIFY_HEADER_ERR sub codes. */
+#define BGP_NOTIFY_HEADER_NOT_SYNC 1
+#define BGP_NOTIFY_HEADER_BAD_MESLEN 2
+#define BGP_NOTIFY_HEADER_BAD_MESTYPE 3
+
+/* BGP_NOTIFY_OPEN_ERR sub codes. */
+#define BGP_NOTIFY_OPEN_MALFORMED_ATTR 0
+#define BGP_NOTIFY_OPEN_UNSUP_VERSION 1
+#define BGP_NOTIFY_OPEN_BAD_PEER_AS 2
+#define BGP_NOTIFY_OPEN_BAD_BGP_IDENT 3
+#define BGP_NOTIFY_OPEN_UNSUP_PARAM 4
+#define BGP_NOTIFY_OPEN_AUTH_FAILURE 5
+#define BGP_NOTIFY_OPEN_UNACEP_HOLDTIME 6
+#define BGP_NOTIFY_OPEN_UNSUP_CAPBL 7
+#define BGP_NOTIFY_OPEN_ROLE_MISMATCH 11
+
+/* BGP_NOTIFY_UPDATE_ERR sub codes. */
+#define BGP_NOTIFY_UPDATE_MAL_ATTR 1
+#define BGP_NOTIFY_UPDATE_UNREC_ATTR 2
+#define BGP_NOTIFY_UPDATE_MISS_ATTR 3
+#define BGP_NOTIFY_UPDATE_ATTR_FLAG_ERR 4
+#define BGP_NOTIFY_UPDATE_ATTR_LENG_ERR 5
+#define BGP_NOTIFY_UPDATE_INVAL_ORIGIN 6
+#define BGP_NOTIFY_UPDATE_AS_ROUTE_LOOP 7
+#define BGP_NOTIFY_UPDATE_INVAL_NEXT_HOP 8
+#define BGP_NOTIFY_UPDATE_OPT_ATTR_ERR 9
+#define BGP_NOTIFY_UPDATE_INVAL_NETWORK 10
+#define BGP_NOTIFY_UPDATE_MAL_AS_PATH 11
+
+/* BGP_NOTIFY_CEASE sub codes (RFC 4486). */
+#define BGP_NOTIFY_CEASE_MAX_PREFIX 1
+#define BGP_NOTIFY_CEASE_ADMIN_SHUTDOWN 2
+#define BGP_NOTIFY_CEASE_PEER_UNCONFIG 3
+#define BGP_NOTIFY_CEASE_ADMIN_RESET 4
+#define BGP_NOTIFY_CEASE_CONNECT_REJECT 5
+#define BGP_NOTIFY_CEASE_CONFIG_CHANGE 6
+#define BGP_NOTIFY_CEASE_COLLISION_RESOLUTION 7
+#define BGP_NOTIFY_CEASE_OUT_OF_RESOURCE 8
+#define BGP_NOTIFY_CEASE_HARD_RESET 9
+#define BGP_NOTIFY_CEASE_BFD_DOWN 10
+
+/* BGP_NOTIFY_ROUTE_REFRESH_ERR sub codes (RFC 7313). */
+#define BGP_NOTIFY_ROUTE_REFRESH_INVALID_MSG_LEN 1
+
+/* BGP route refresh optional subtypes. */
+#define BGP_ROUTE_REFRESH_NORMAL 0
+#define BGP_ROUTE_REFRESH_BORR 1
+#define BGP_ROUTE_REFRESH_EORR 2
+
+/* BGP timers default value. */
+#define BGP_INIT_START_TIMER 1
+/* The following 3 are RFC defaults that are overridden in bgp_vty.c with
+ * version-/profile-specific values. The values here do not matter, they only
+ * exist to provide a clear layering separation between core and CLI.
+ */
+#define BGP_DEFAULT_HOLDTIME 180
+#define BGP_DEFAULT_KEEPALIVE 60
+#define BGP_DEFAULT_CONNECT_RETRY 120
+
+#define BGP_DEFAULT_EBGP_ROUTEADV 0
+#define BGP_DEFAULT_IBGP_ROUTEADV 0
+
+/* BGP RFC 4271 DelayOpenTime default value */
+#define BGP_DEFAULT_DELAYOPEN 120
+
+/* BGP default local preference. */
+#define BGP_DEFAULT_LOCAL_PREF 100
+
+/* BGP local-preference to send when 'bgp graceful-shutdown'
+ * is configured */
+#define BGP_GSHUT_LOCAL_PREF 0
+
+/* BGP default subgroup packet queue max . */
+#define BGP_DEFAULT_SUBGROUP_PKT_QUEUE_MAX 40
+
+/* BGP graceful restart */
+#define BGP_DEFAULT_RESTART_TIME 120
+#define BGP_DEFAULT_STALEPATH_TIME 360
+#define BGP_DEFAULT_SELECT_DEFERRAL_TIME 360
+#define BGP_DEFAULT_RIB_STALE_TIME 500
+#define BGP_DEFAULT_UPDATE_ADVERTISEMENT_TIME 1
+
+/* BGP Long-lived Graceful Restart */
+#define BGP_DEFAULT_LLGR_STALE_TIME 0
+
+/* BGP uptime string length. */
+#define BGP_UPTIME_LEN 25
+
+/* Default configuration settings for bgpd. */
+#define BGP_VTY_PORT 2605
+#define BGP_DEFAULT_CONFIG "bgpd.conf"
+
+/* BGP Dynamic Neighbors feature */
+#define BGP_DYNAMIC_NEIGHBORS_LIMIT_DEFAULT 100
+#define BGP_DYNAMIC_NEIGHBORS_LIMIT_MIN 1
+#define BGP_DYNAMIC_NEIGHBORS_LIMIT_MAX 65535
+
+/* Flag for peer_clear_soft(). */
+enum bgp_clear_type {
+ BGP_CLEAR_SOFT_NONE,
+ BGP_CLEAR_SOFT_OUT,
+ BGP_CLEAR_SOFT_IN,
+ BGP_CLEAR_SOFT_BOTH,
+ BGP_CLEAR_SOFT_IN_ORF_PREFIX,
+ BGP_CLEAR_MESSAGE_STATS
+};
+
+/* Macros. */
+#define BGP_INPUT(P) ((P)->curr)
+#define BGP_INPUT_PNT(P) (stream_pnt(BGP_INPUT(P)))
+#define BGP_IS_VALID_STATE_FOR_NOTIF(S) \
+ (((S) == OpenSent) || ((S) == OpenConfirm) || ((S) == Established))
+
+/* BGP error codes. */
+enum bgp_create_error_code {
+ BGP_SUCCESS = 0,
+ BGP_CREATED = 1,
+ BGP_ERR_INVALID_VALUE = -1,
+ BGP_ERR_INVALID_FLAG = -2,
+ BGP_ERR_INVALID_AS = -3,
+ BGP_ERR_PEER_GROUP_MEMBER = -4,
+ BGP_ERR_PEER_GROUP_NO_REMOTE_AS = -5,
+ BGP_ERR_PEER_GROUP_CANT_CHANGE = -6,
+ BGP_ERR_PEER_GROUP_MISMATCH = -7,
+ BGP_ERR_PEER_GROUP_PEER_TYPE_DIFFERENT = -8,
+ BGP_ERR_AS_MISMATCH = -9,
+ BGP_ERR_PEER_FLAG_CONFLICT = -10,
+ BGP_ERR_PEER_GROUP_SHUTDOWN = -11,
+ BGP_ERR_PEER_FILTER_CONFLICT = -12,
+ BGP_ERR_NOT_INTERNAL_PEER = -13,
+ BGP_ERR_REMOVE_PRIVATE_AS = -14,
+ BGP_ERR_AF_UNCONFIGURED = -15,
+ BGP_ERR_SOFT_RECONFIG_UNCONFIGURED = -16,
+ BGP_ERR_INSTANCE_MISMATCH = -17,
+ BGP_ERR_LOCAL_AS_ALLOWED_ONLY_FOR_EBGP = -18,
+ BGP_ERR_CANNOT_HAVE_LOCAL_AS_SAME_AS = -19,
+ BGP_ERR_TCPSIG_FAILED = -20,
+ BGP_ERR_NO_EBGP_MULTIHOP_WITH_TTLHACK = -21,
+ BGP_ERR_NO_IBGP_WITH_TTLHACK = -22,
+ BGP_ERR_NO_INTERFACE_CONFIG = -23,
+ BGP_ERR_CANNOT_HAVE_LOCAL_AS_SAME_AS_REMOTE_AS = -24,
+ BGP_ERR_AS_OVERRIDE = -25,
+ BGP_ERR_INVALID_DYNAMIC_NEIGHBORS_LIMIT = -26,
+ BGP_ERR_DYNAMIC_NEIGHBORS_RANGE_EXISTS = -27,
+ BGP_ERR_DYNAMIC_NEIGHBORS_RANGE_NOT_FOUND = -28,
+ BGP_ERR_INVALID_FOR_DYNAMIC_PEER = -29,
+ BGP_ERR_INVALID_FOR_DIRECT_PEER = -30,
+ BGP_ERR_PEER_SAFI_CONFLICT = -31,
+
+ /* BGP GR ERRORS */
+ BGP_ERR_GR_INVALID_CMD = -32,
+ BGP_ERR_GR_OPERATION_FAILED = -33,
+ BGP_GR_NO_OPERATION = -34,
+
+ /*BGP Open Policy ERRORS */
+ BGP_ERR_INVALID_ROLE_NAME = -35,
+ BGP_ERR_INVALID_INTERNAL_ROLE = -36
+};
+
+/*
+ * Enumeration of different policy kinds a peer can be configured with.
+ */
+enum bgp_policy_type {
+ BGP_POLICY_ROUTE_MAP,
+ BGP_POLICY_FILTER_LIST,
+ BGP_POLICY_PREFIX_LIST,
+ BGP_POLICY_DISTRIBUTE_LIST,
+};
+
+/* peer_flag_change_type. */
+enum peer_change_type {
+ peer_change_none,
+ peer_change_reset,
+ peer_change_reset_in,
+ peer_change_reset_out,
+};
+
+extern struct bgp_master *bm;
+extern unsigned int multipath_num;
+
+/* Prototypes. */
+extern void bgp_terminate(void);
+extern void bgp_reset(void);
+extern void bgp_zclient_reset(void);
+extern struct bgp *bgp_get_default(void);
+extern struct bgp *bgp_lookup(as_t, const char *);
+extern struct bgp *bgp_lookup_by_name(const char *);
+extern struct bgp *bgp_lookup_by_vrf_id(vrf_id_t);
+extern struct bgp *bgp_get_evpn(void);
+extern void bgp_set_evpn(struct bgp *bgp);
+extern struct peer *peer_lookup(struct bgp *, union sockunion *);
+extern struct peer *peer_lookup_by_conf_if(struct bgp *, const char *);
+extern struct peer *peer_lookup_by_hostname(struct bgp *, const char *);
+extern void bgp_peer_conf_if_to_su_update(struct peer *);
+extern int peer_group_listen_range_del(struct peer_group *, struct prefix *);
+extern struct peer_group *peer_group_lookup(struct bgp *, const char *);
+extern struct peer_group *peer_group_get(struct bgp *, const char *);
+extern struct peer *peer_create_bind_dynamic_neighbor(struct bgp *,
+ union sockunion *,
+ struct peer_group *);
+extern struct prefix *
+peer_group_lookup_dynamic_neighbor_range(struct peer_group *, struct prefix *);
+extern struct peer_group *peer_group_lookup_dynamic_neighbor(struct bgp *,
+ struct prefix *,
+ struct prefix **);
+extern struct peer *peer_lookup_dynamic_neighbor(struct bgp *,
+ union sockunion *);
+
+/*
+ * Peers are incredibly easy to memory leak
+ * due to the various ways that they are actually used
+ * Provide some functionality to debug locks and unlocks
+ */
+extern struct peer *peer_lock_with_caller(const char *, struct peer *);
+extern struct peer *peer_unlock_with_caller(const char *, struct peer *);
+#define peer_unlock(A) peer_unlock_with_caller(__FUNCTION__, (A))
+#define peer_lock(B) peer_lock_with_caller(__FUNCTION__, (B))
+
+extern enum bgp_peer_sort peer_sort(struct peer *peer);
+extern enum bgp_peer_sort peer_sort_lookup(struct peer *peer);
+
+extern bool peer_active(struct peer *);
+extern bool peer_active_nego(struct peer *);
+extern bool peer_afc_received(struct peer *peer);
+extern bool peer_afc_advertised(struct peer *peer);
+extern void bgp_recalculate_all_bestpaths(struct bgp *bgp);
+extern struct peer *peer_create(union sockunion *, const char *, struct bgp *,
+ as_t, as_t, int, struct peer_group *);
+extern struct peer *peer_create_accept(struct bgp *);
+extern void peer_xfer_config(struct peer *dst, struct peer *src);
+extern char *peer_uptime(time_t uptime2, char *buf, size_t len, bool use_json,
+ json_object *json);
+
+extern int bgp_config_write(struct vty *);
+
+extern void bgp_master_init(struct thread_master *master, const int buffer_size,
+ struct list *addresses);
+
+extern void bgp_init(unsigned short instance);
+extern void bgp_pthreads_run(void);
+extern void bgp_pthreads_finish(void);
+extern void bgp_route_map_init(void);
+extern void bgp_session_reset(struct peer *);
+
+extern int bgp_option_set(int);
+extern int bgp_option_unset(int);
+extern int bgp_option_check(int);
+
+/* set the bgp no-rib option during runtime and remove installed routes */
+extern void bgp_option_norib_set_runtime(void);
+
+/* unset the bgp no-rib option during runtime and reset all peers */
+extern void bgp_option_norib_unset_runtime(void);
+
+extern int bgp_get(struct bgp **, as_t *, const char *, enum bgp_instance_type);
+extern void bgp_instance_up(struct bgp *);
+extern void bgp_instance_down(struct bgp *);
+extern int bgp_delete(struct bgp *);
+
+extern int bgp_handle_socket(struct bgp *bgp, struct vrf *vrf,
+ vrf_id_t old_vrf_id, bool create);
+
+extern void bgp_router_id_zebra_bump(vrf_id_t, const struct prefix *);
+extern void bgp_router_id_static_set(struct bgp *, struct in_addr);
+
+extern void bm_wait_for_fib_set(bool set);
+extern void bgp_suppress_fib_pending_set(struct bgp *bgp, bool set);
+extern void bgp_cluster_id_set(struct bgp *bgp, struct in_addr *cluster_id);
+extern void bgp_cluster_id_unset(struct bgp *bgp);
+
+extern void bgp_confederation_id_set(struct bgp *bgp, as_t as);
+extern void bgp_confederation_id_unset(struct bgp *bgp);
+extern bool bgp_confederation_peers_check(struct bgp *, as_t);
+
+extern void bgp_confederation_peers_add(struct bgp *bgp, as_t as);
+extern void bgp_confederation_peers_remove(struct bgp *bgp, as_t as);
+
+extern void bgp_timers_set(struct bgp *, uint32_t keepalive, uint32_t holdtime,
+ uint32_t connect_retry, uint32_t delayopen);
+extern void bgp_timers_unset(struct bgp *);
+
+extern void bgp_default_local_preference_set(struct bgp *bgp,
+ uint32_t local_pref);
+extern void bgp_default_local_preference_unset(struct bgp *bgp);
+
+extern void bgp_default_subgroup_pkt_queue_max_set(struct bgp *bgp,
+ uint32_t queue_size);
+extern void bgp_default_subgroup_pkt_queue_max_unset(struct bgp *bgp);
+
+extern void bgp_listen_limit_set(struct bgp *bgp, int listen_limit);
+extern void bgp_listen_limit_unset(struct bgp *bgp);
+
+extern bool bgp_update_delay_active(struct bgp *);
+extern bool bgp_update_delay_configured(struct bgp *);
+extern bool bgp_afi_safi_peer_exists(struct bgp *bgp, afi_t afi, safi_t safi);
+extern void peer_as_change(struct peer *, as_t, int);
+extern int peer_remote_as(struct bgp *, union sockunion *, const char *, as_t *,
+ int);
+extern int peer_group_remote_as(struct bgp *, const char *, as_t *, int);
+extern int peer_delete(struct peer *peer);
+extern void peer_notify_unconfig(struct peer *peer);
+extern int peer_group_delete(struct peer_group *);
+extern int peer_group_remote_as_delete(struct peer_group *);
+extern int peer_group_listen_range_add(struct peer_group *, struct prefix *);
+extern void peer_group_notify_unconfig(struct peer_group *group);
+
+extern int peer_activate(struct peer *, afi_t, safi_t);
+extern int peer_deactivate(struct peer *, afi_t, safi_t);
+
+extern int peer_group_bind(struct bgp *, union sockunion *, struct peer *,
+ struct peer_group *, as_t *);
+
+extern int peer_flag_set(struct peer *peer, uint64_t flag);
+extern int peer_flag_unset(struct peer *peer, uint64_t flag);
+extern void peer_flag_inherit(struct peer *peer, uint64_t flag);
+
+extern int peer_af_flag_set(struct peer *peer, afi_t afi, safi_t safi,
+ uint64_t flag);
+extern int peer_af_flag_unset(struct peer *peer, afi_t afi, safi_t safi,
+ uint64_t flag);
+extern int peer_af_flag_check(struct peer *, afi_t, safi_t, uint32_t);
+extern void peer_af_flag_inherit(struct peer *peer, afi_t afi, safi_t safi,
+ uint64_t flag);
+extern void peer_change_action(struct peer *peer, afi_t afi, safi_t safi,
+ enum peer_change_type type);
+
+extern int peer_ebgp_multihop_set(struct peer *, int);
+extern int peer_ebgp_multihop_unset(struct peer *);
+extern int is_ebgp_multihop_configured(struct peer *peer);
+
+extern int peer_role_set(struct peer *peer, uint8_t role, bool strict_mode);
+extern int peer_role_unset(struct peer *peer);
+
+extern void peer_description_set(struct peer *, const char *);
+extern void peer_description_unset(struct peer *);
+
+extern int peer_update_source_if_set(struct peer *, const char *);
+extern void peer_update_source_addr_set(struct peer *peer,
+ const union sockunion *su);
+extern void peer_update_source_unset(struct peer *peer);
+
+extern int peer_default_originate_set(struct peer *peer, afi_t afi, safi_t safi,
+ const char *rmap,
+ struct route_map *route_map);
+extern int peer_default_originate_unset(struct peer *, afi_t, safi_t);
+
+extern void peer_port_set(struct peer *, uint16_t);
+extern void peer_port_unset(struct peer *);
+
+extern int peer_weight_set(struct peer *, afi_t, safi_t, uint16_t);
+extern int peer_weight_unset(struct peer *, afi_t, safi_t);
+
+extern int peer_timers_set(struct peer *, uint32_t keepalive,
+ uint32_t holdtime);
+extern int peer_timers_unset(struct peer *);
+
+extern int peer_timers_connect_set(struct peer *, uint32_t);
+extern int peer_timers_connect_unset(struct peer *);
+
+extern int peer_advertise_interval_set(struct peer *, uint32_t);
+extern int peer_advertise_interval_unset(struct peer *);
+
+extern int peer_timers_delayopen_set(struct peer *peer, uint32_t delayopen);
+extern int peer_timers_delayopen_unset(struct peer *peer);
+
+extern void peer_interface_set(struct peer *, const char *);
+extern void peer_interface_unset(struct peer *);
+
+extern int peer_distribute_set(struct peer *, afi_t, safi_t, int, const char *);
+extern int peer_distribute_unset(struct peer *, afi_t, safi_t, int);
+
+extern int peer_allowas_in_set(struct peer *, afi_t, safi_t, int, int);
+extern int peer_allowas_in_unset(struct peer *, afi_t, safi_t);
+
+extern int peer_local_as_set(struct peer *, as_t, bool no_prepend,
+ bool replace_as);
+extern int peer_local_as_unset(struct peer *);
+
+extern int peer_prefix_list_set(struct peer *, afi_t, safi_t, int,
+ const char *);
+extern int peer_prefix_list_unset(struct peer *, afi_t, safi_t, int);
+
+extern int peer_aslist_set(struct peer *, afi_t, safi_t, int, const char *);
+extern int peer_aslist_unset(struct peer *, afi_t, safi_t, int);
+
+extern int peer_route_map_set(struct peer *peer, afi_t afi, safi_t safi, int,
+ const char *name, struct route_map *route_map);
+extern int peer_route_map_unset(struct peer *, afi_t, safi_t, int);
+
+extern int peer_unsuppress_map_set(struct peer *peer, afi_t afi, safi_t safi,
+ const char *name,
+ struct route_map *route_map);
+
+extern int peer_advertise_map_set(struct peer *peer, afi_t afi, safi_t safi,
+ const char *advertise_name,
+ struct route_map *advertise_map,
+ const char *condition_name,
+ struct route_map *condition_map,
+ bool condition);
+
+extern int peer_password_set(struct peer *, const char *);
+extern int peer_password_unset(struct peer *);
+
+extern int peer_unsuppress_map_unset(struct peer *, afi_t, safi_t);
+
+extern int peer_advertise_map_unset(struct peer *peer, afi_t afi, safi_t safi,
+ const char *advertise_name,
+ struct route_map *advertise_map,
+ const char *condition_name,
+ struct route_map *condition_map,
+ bool condition);
+
+extern int peer_maximum_prefix_set(struct peer *, afi_t, safi_t, uint32_t,
+ uint8_t, int, uint16_t, bool force);
+extern int peer_maximum_prefix_unset(struct peer *, afi_t, safi_t);
+
+extern void peer_maximum_prefix_out_refresh_routes(struct peer *peer, afi_t afi,
+ safi_t safi);
+extern int peer_maximum_prefix_out_set(struct peer *peer, afi_t afi,
+ safi_t safi, uint32_t max);
+extern int peer_maximum_prefix_out_unset(struct peer *peer, afi_t afi,
+ safi_t safi);
+
+extern int peer_clear(struct peer *, struct listnode **);
+extern int peer_clear_soft(struct peer *, afi_t, safi_t, enum bgp_clear_type);
+
+extern int peer_ttl_security_hops_set(struct peer *, int);
+extern int peer_ttl_security_hops_unset(struct peer *);
+
+extern void peer_tx_shutdown_message_set(struct peer *, const char *msg);
+extern void peer_tx_shutdown_message_unset(struct peer *);
+
+extern void bgp_route_map_update_timer(struct thread *thread);
+extern const char *bgp_get_name_by_role(uint8_t role);
+
+extern void bgp_route_map_terminate(void);
+
+extern int peer_cmp(struct peer *p1, struct peer *p2);
+
+extern int bgp_map_afi_safi_iana2int(iana_afi_t pkt_afi, iana_safi_t pkt_safi,
+ afi_t *afi, safi_t *safi);
+extern int bgp_map_afi_safi_int2iana(afi_t afi, safi_t safi,
+ iana_afi_t *pkt_afi,
+ iana_safi_t *pkt_safi);
+
+extern struct peer_af *peer_af_create(struct peer *, afi_t, safi_t);
+extern struct peer_af *peer_af_find(struct peer *, afi_t, safi_t);
+extern int peer_af_delete(struct peer *, afi_t, safi_t);
+
+extern void bgp_shutdown_enable(struct bgp *bgp, const char *msg);
+extern void bgp_shutdown_disable(struct bgp *bgp);
+
+extern void bgp_close(void);
+extern void bgp_free(struct bgp *);
+void bgp_gr_apply_running_config(void);
+
+/* BGP GR */
+int bgp_global_gr_init(struct bgp *bgp);
+int bgp_peer_gr_init(struct peer *peer);
+
+
+#define BGP_GR_ROUTER_DETECT_AND_SEND_CAPABILITY_TO_ZEBRA(_bgp, _peer_list) \
+ do { \
+ struct peer *peer_loop; \
+ bool gr_router_detected = false; \
+ struct listnode *node = {0}; \
+ for (ALL_LIST_ELEMENTS_RO(_peer_list, node, peer_loop)) { \
+ if (CHECK_FLAG(peer_loop->flags, \
+ PEER_FLAG_GRACEFUL_RESTART)) \
+ gr_router_detected = true; \
+ } \
+ if (gr_router_detected \
+ && _bgp->present_zebra_gr_state == ZEBRA_GR_DISABLE) { \
+ bgp_zebra_send_capabilities(_bgp, false); \
+ } else if (!gr_router_detected \
+ && _bgp->present_zebra_gr_state \
+ == ZEBRA_GR_ENABLE) { \
+ bgp_zebra_send_capabilities(_bgp, true); \
+ } \
+ } while (0)
+
+static inline struct bgp *bgp_lock(struct bgp *bgp)
+{
+ bgp->lock++;
+ return bgp;
+}
+
+static inline void bgp_unlock(struct bgp *bgp)
+{
+ assert(bgp->lock > 0);
+ if (--bgp->lock == 0)
+ bgp_free(bgp);
+}
+
+static inline int afindex(afi_t afi, safi_t safi)
+{
+ switch (afi) {
+ case AFI_IP:
+ switch (safi) {
+ case SAFI_UNICAST:
+ return BGP_AF_IPV4_UNICAST;
+ case SAFI_MULTICAST:
+ return BGP_AF_IPV4_MULTICAST;
+ case SAFI_LABELED_UNICAST:
+ return BGP_AF_IPV4_LBL_UNICAST;
+ case SAFI_MPLS_VPN:
+ return BGP_AF_IPV4_VPN;
+ case SAFI_ENCAP:
+ return BGP_AF_IPV4_ENCAP;
+ case SAFI_FLOWSPEC:
+ return BGP_AF_IPV4_FLOWSPEC;
+ default:
+ return BGP_AF_MAX;
+ }
+ break;
+ case AFI_IP6:
+ switch (safi) {
+ case SAFI_UNICAST:
+ return BGP_AF_IPV6_UNICAST;
+ case SAFI_MULTICAST:
+ return BGP_AF_IPV6_MULTICAST;
+ case SAFI_LABELED_UNICAST:
+ return BGP_AF_IPV6_LBL_UNICAST;
+ case SAFI_MPLS_VPN:
+ return BGP_AF_IPV6_VPN;
+ case SAFI_ENCAP:
+ return BGP_AF_IPV6_ENCAP;
+ case SAFI_FLOWSPEC:
+ return BGP_AF_IPV6_FLOWSPEC;
+ default:
+ return BGP_AF_MAX;
+ }
+ break;
+ case AFI_L2VPN:
+ switch (safi) {
+ case SAFI_EVPN:
+ return BGP_AF_L2VPN_EVPN;
+ default:
+ return BGP_AF_MAX;
+ }
+ default:
+ return BGP_AF_MAX;
+ }
+}
+
+/* If the peer is not a peer-group but is bound to a peer-group return 1 */
+static inline int peer_group_active(struct peer *peer)
+{
+ if (!CHECK_FLAG(peer->sflags, PEER_STATUS_GROUP) && peer->group)
+ return 1;
+ return 0;
+}
+
+/* If peer is negotiated at least one address family return 1. */
+static inline int peer_afi_active_nego(const struct peer *peer, afi_t afi)
+{
+ if (peer->afc_nego[afi][SAFI_UNICAST]
+ || peer->afc_nego[afi][SAFI_MULTICAST]
+ || peer->afc_nego[afi][SAFI_LABELED_UNICAST]
+ || peer->afc_nego[afi][SAFI_MPLS_VPN]
+ || peer->afc_nego[afi][SAFI_ENCAP]
+ || peer->afc_nego[afi][SAFI_FLOWSPEC]
+ || peer->afc_nego[afi][SAFI_EVPN])
+ return 1;
+ return 0;
+}
+
+/* If at least one address family activated for group, return 1. */
+static inline int peer_group_af_configured(struct peer_group *group)
+{
+ struct peer *peer = group->conf;
+
+ if (peer->afc[AFI_IP][SAFI_UNICAST] || peer->afc[AFI_IP][SAFI_MULTICAST]
+ || peer->afc[AFI_IP][SAFI_LABELED_UNICAST]
+ || peer->afc[AFI_IP][SAFI_FLOWSPEC]
+ || peer->afc[AFI_IP][SAFI_MPLS_VPN] || peer->afc[AFI_IP][SAFI_ENCAP]
+ || peer->afc[AFI_IP6][SAFI_UNICAST]
+ || peer->afc[AFI_IP6][SAFI_MULTICAST]
+ || peer->afc[AFI_IP6][SAFI_LABELED_UNICAST]
+ || peer->afc[AFI_IP6][SAFI_MPLS_VPN]
+ || peer->afc[AFI_IP6][SAFI_ENCAP]
+ || peer->afc[AFI_IP6][SAFI_FLOWSPEC]
+ || peer->afc[AFI_L2VPN][SAFI_EVPN])
+ return 1;
+ return 0;
+}
+
+static inline char *timestamp_string(time_t ts)
+{
+ time_t tbuf;
+ tbuf = time(NULL) - (monotime(NULL) - ts);
+ return ctime(&tbuf);
+}
+
+static inline bool peer_established(struct peer *peer)
+{
+ return peer->status == Established;
+}
+
+static inline bool peer_dynamic_neighbor(struct peer *peer)
+{
+ return CHECK_FLAG(peer->flags, PEER_FLAG_DYNAMIC_NEIGHBOR);
+}
+
+static inline bool peer_dynamic_neighbor_no_nsf(struct peer *peer)
+{
+ return (peer_dynamic_neighbor(peer) &&
+ !CHECK_FLAG(peer->sflags, PEER_STATUS_NSF_WAIT));
+}
+
+static inline int peer_cap_enhe(struct peer *peer, afi_t afi, safi_t safi)
+{
+ return (CHECK_FLAG(peer->af_cap[afi][safi], PEER_CAP_ENHE_AF_NEGO));
+}
+
+/* Lookup VRF for BGP instance based on its type. */
+static inline struct vrf *bgp_vrf_lookup_by_instance_type(struct bgp *bgp)
+{
+ struct vrf *vrf;
+
+ if (bgp->inst_type == BGP_INSTANCE_TYPE_DEFAULT)
+ vrf = vrf_lookup_by_id(VRF_DEFAULT);
+ else if (bgp->inst_type == BGP_INSTANCE_TYPE_VRF)
+ vrf = vrf_lookup_by_name(bgp->name);
+ else
+ vrf = NULL;
+
+ return vrf;
+}
+
+static inline uint32_t bgp_vrf_interfaces(struct bgp *bgp, bool active)
+{
+ struct vrf *vrf;
+ struct interface *ifp;
+ uint32_t count = 0;
+
+ /* if there is one interface in the vrf which is up then it is deemed
+ * active
+ */
+ vrf = bgp_vrf_lookup_by_instance_type(bgp);
+ if (vrf == NULL)
+ return 0;
+ RB_FOREACH (ifp, if_name_head, &vrf->ifaces_by_name) {
+ if (strcmp(ifp->name, bgp->name) == 0)
+ continue;
+ if (!active || if_is_up(ifp))
+ count++;
+ }
+ return count;
+}
+
+/* Link BGP instance to VRF. */
+static inline void bgp_vrf_link(struct bgp *bgp, struct vrf *vrf)
+{
+ bgp->vrf_id = vrf->vrf_id;
+ if (vrf->info != (void *)bgp)
+ vrf->info = (void *)bgp_lock(bgp);
+}
+
+/* Unlink BGP instance from VRF. */
+static inline void bgp_vrf_unlink(struct bgp *bgp, struct vrf *vrf)
+{
+ if (vrf->info == (void *)bgp) {
+ vrf->info = NULL;
+ bgp_unlock(bgp);
+ }
+ bgp->vrf_id = VRF_UNKNOWN;
+}
+
+static inline bool bgp_in_graceful_shutdown(struct bgp *bgp)
+{
+ /* True if either set for this instance or globally */
+ return (!!CHECK_FLAG(bgp->flags, BGP_FLAG_GRACEFUL_SHUTDOWN) ||
+ !!CHECK_FLAG(bm->flags, BM_FLAG_GRACEFUL_SHUTDOWN));
+}
+
+/* For benefit of rfapi */
+extern struct peer *peer_new(struct bgp *bgp);
+
+extern struct peer *peer_lookup_in_view(struct vty *vty, struct bgp *bgp,
+ const char *ip_str, bool use_json);
+extern int bgp_lookup_by_as_name_type(struct bgp **bgp_val, as_t *as,
+ const char *name,
+ enum bgp_instance_type inst_type);
+
+/* Hooks */
+DECLARE_HOOK(bgp_vrf_status_changed, (struct bgp *bgp, struct interface *ifp),
+ (bgp, ifp));
+DECLARE_HOOK(peer_status_changed, (struct peer *peer), (peer));
+DECLARE_HOOK(bgp_snmp_init_stats, (struct bgp *bgp), (bgp));
+DECLARE_HOOK(bgp_snmp_update_last_changed, (struct bgp *bgp), (bgp));
+DECLARE_HOOK(bgp_snmp_update_stats,
+ (struct bgp_node *rn, struct bgp_path_info *pi, bool added),
+ (rn, pi, added));
+DECLARE_HOOK(bgp_rpki_prefix_status,
+ (struct peer * peer, struct attr *attr,
+ const struct prefix *prefix),
+ (peer, attr, prefix));
+
+void peer_nsf_stop(struct peer *peer);
+
+void peer_tcp_mss_set(struct peer *peer, uint32_t tcp_mss);
+void peer_tcp_mss_unset(struct peer *peer);
+
+extern void bgp_recalculate_afi_safi_bestpaths(struct bgp *bgp, afi_t afi,
+ safi_t safi);
+
+#ifdef _FRR_ATTRIBUTE_PRINTFRR
+/* clang-format off */
+#pragma FRR printfrr_ext "%pBP" (struct peer *)
+/* clang-format on */
+#endif
+
+#endif /* _QUAGGA_BGPD_H */
diff --git a/bgpd/rfapi/bgp_rfapi_cfg.c b/bgpd/rfapi/bgp_rfapi_cfg.c
new file mode 100644
index 0000000..b65d90e
--- /dev/null
+++ b/bgpd/rfapi/bgp_rfapi_cfg.c
@@ -0,0 +1,4637 @@
+/*
+ *
+ * Copyright 2009-2016, LabN Consulting, L.L.C.
+ *
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program; see the file COPYING; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
+ */
+#include "lib/zebra.h"
+
+#include "lib/command.h"
+#include "lib/prefix.h"
+#include "lib/memory.h"
+#include "lib/linklist.h"
+#include "lib/agg_table.h"
+#include "lib/plist.h"
+#include "lib/routemap.h"
+
+#include "bgpd/bgpd.h"
+#include "bgpd/bgp_attr.h"
+#include "bgpd/bgp_route.h"
+#include "bgpd/bgp_mplsvpn.h"
+
+#include "bgpd/bgp_vty.h"
+#include "bgpd/bgp_ecommunity.h"
+#include "bgpd/rfapi/rfapi.h"
+#include "bgpd/rfapi/bgp_rfapi_cfg.h"
+#include "bgpd/rfapi/rfapi_backend.h"
+#include "bgpd/rfapi/rfapi_import.h"
+#include "bgpd/rfapi/rfapi_private.h"
+#include "bgpd/rfapi/rfapi_monitor.h"
+#include "bgpd/rfapi/vnc_zebra.h"
+#include "bgpd/rfapi/vnc_export_bgp.h"
+#include "bgpd/rfapi/vnc_export_bgp_p.h"
+#include "bgpd/rfapi/rfapi_vty.h"
+#include "bgpd/rfapi/vnc_import_bgp.h"
+#include "bgpd/rfapi/vnc_debug.h"
+
+#ifdef ENABLE_BGP_VNC
+
+#undef BGP_VNC_DEBUG_MATCH_GROUP
+
+
+DEFINE_MGROUP(RFAPI, "rfapi");
+DEFINE_MTYPE(RFAPI, RFAPI_CFG, "NVE Configuration");
+DEFINE_MTYPE(RFAPI, RFAPI_GROUP_CFG, "NVE Group Configuration");
+DEFINE_MTYPE(RFAPI, RFAPI_L2_CFG, "RFAPI L2 Group Configuration");
+DEFINE_MTYPE(RFAPI, RFAPI_RFP_GROUP_CFG, "RFAPI RFP Group Configuration");
+DEFINE_MTYPE(RFAPI, RFAPI, "RFAPI Generic");
+DEFINE_MTYPE(RFAPI, RFAPI_DESC, "RFAPI Descriptor");
+DEFINE_MTYPE(RFAPI, RFAPI_IMPORTTABLE, "RFAPI Import Table");
+DEFINE_MTYPE(RFAPI, RFAPI_MONITOR, "RFAPI Monitor VPN");
+DEFINE_MTYPE(RFAPI, RFAPI_MONITOR_ENCAP, "RFAPI Monitor Encap");
+DEFINE_MTYPE(RFAPI, RFAPI_NEXTHOP, "RFAPI Next Hop");
+DEFINE_MTYPE(RFAPI, RFAPI_VN_OPTION, "RFAPI VN Option");
+DEFINE_MTYPE(RFAPI, RFAPI_UN_OPTION, "RFAPI UN Option");
+DEFINE_MTYPE(RFAPI, RFAPI_WITHDRAW, "RFAPI Withdraw");
+DEFINE_MTYPE(RFAPI, RFAPI_RFG_NAME, "RFAPI RFGName");
+DEFINE_MTYPE(RFAPI, RFAPI_ADB, "RFAPI Advertisement Data");
+DEFINE_MTYPE(RFAPI, RFAPI_ETI, "RFAPI Export Table Info");
+DEFINE_MTYPE(RFAPI, RFAPI_NVE_ADDR, "RFAPI NVE Address");
+DEFINE_MTYPE(RFAPI, RFAPI_PREFIX_BAG, "RFAPI Prefix Bag");
+DEFINE_MTYPE(RFAPI, RFAPI_IT_EXTRA, "RFAPI IT Extra");
+DEFINE_MTYPE(RFAPI, RFAPI_INFO, "RFAPI Info");
+DEFINE_MTYPE(RFAPI, RFAPI_ADDR, "RFAPI Addr");
+DEFINE_MTYPE(RFAPI, RFAPI_UPDATED_RESPONSE_QUEUE, "RFAPI Updated Rsp Queue");
+DEFINE_MTYPE(RFAPI, RFAPI_RECENT_DELETE, "RFAPI Recently Deleted Route");
+DEFINE_MTYPE(RFAPI, RFAPI_L2ADDR_OPT, "RFAPI L2 Address Option");
+DEFINE_MTYPE(RFAPI, RFAPI_AP, "RFAPI Advertised Prefix");
+DEFINE_MTYPE(RFAPI, RFAPI_MONITOR_ETH, "RFAPI Monitor Ethernet");
+
+DEFINE_QOBJ_TYPE(rfapi_nve_group_cfg);
+DEFINE_QOBJ_TYPE(rfapi_l2_group_cfg);
+/***********************************************************************
+ * RFAPI Support
+ ***********************************************************************/
+
+
+/*
+ * compaitibility to old quagga_time call
+ * time_t value in terms of stabilised absolute time.
+ * replacement for POSIX time()
+ */
+time_t rfapi_time(time_t *t)
+{
+ time_t clock = monotime(NULL);
+ if (t)
+ *t = clock;
+ return clock;
+}
+
+void nve_group_to_nve_list(struct rfapi_nve_group_cfg *rfg, struct list **nves,
+ uint8_t family) /* AF_INET, AF_INET6 */
+{
+ struct listnode *hln;
+ struct rfapi_descriptor *rfd;
+
+ /*
+ * loop over nves in this grp, add to list
+ */
+ for (ALL_LIST_ELEMENTS_RO(rfg->nves, hln, rfd)) {
+ if (rfd->vn_addr.addr_family == family) {
+ if (!*nves)
+ *nves = list_new();
+ listnode_add(*nves, rfd);
+ }
+ }
+}
+
+
+struct rfapi_nve_group_cfg *bgp_rfapi_cfg_match_group(struct rfapi_cfg *hc,
+ struct prefix *vn,
+ struct prefix *un)
+{
+ struct rfapi_nve_group_cfg *rfg_vn = NULL;
+ struct rfapi_nve_group_cfg *rfg_un = NULL;
+
+ struct agg_table *rt_vn;
+ struct agg_table *rt_un;
+ struct agg_node *rn_vn;
+ struct agg_node *rn_un;
+
+ struct rfapi_nve_group_cfg *rfg;
+ struct listnode *node, *nnode;
+
+ switch (vn->family) {
+ case AF_INET:
+ rt_vn = hc->nve_groups_vn[AFI_IP];
+ break;
+ case AF_INET6:
+ rt_vn = hc->nve_groups_vn[AFI_IP6];
+ break;
+ default:
+ return NULL;
+ }
+
+ switch (un->family) {
+ case AF_INET:
+ rt_un = hc->nve_groups_un[AFI_IP];
+ break;
+ case AF_INET6:
+ rt_un = hc->nve_groups_un[AFI_IP6];
+ break;
+ default:
+ return NULL;
+ }
+
+ rn_vn = agg_node_match(rt_vn, vn); /* NB locks node */
+ if (rn_vn) {
+ rfg_vn = rn_vn->info;
+ agg_unlock_node(rn_vn);
+ }
+
+ rn_un = agg_node_match(rt_un, un); /* NB locks node */
+ if (rn_un) {
+ rfg_un = rn_un->info;
+ agg_unlock_node(rn_un);
+ }
+
+#ifdef BGP_VNC_DEBUG_MATCH_GROUP
+ {
+ vnc_zlog_debug_verbose("%s: vn prefix: %pFX", __func__, vn);
+ vnc_zlog_debug_verbose("%s: un prefix: %pFX", __func__, un);
+ vnc_zlog_debug_verbose(
+ "%s: rn_vn=%p, rn_un=%p, rfg_vn=%p, rfg_un=%p",
+ __func__, rn_vn, rn_un, rfg_vn, rfg_un);
+ }
+#endif
+
+
+ if (rfg_un == rfg_vn) /* same group */
+ return rfg_un;
+ if (!rfg_un) /* un doesn't match, return vn-matched grp */
+ return rfg_vn;
+ if (!rfg_vn) /* vn doesn't match, return un-matched grp */
+ return rfg_un;
+
+ /*
+ * Two different nve groups match: the group configured earlier wins.
+ * For now, just walk the sequential list and pick the first one.
+ * If this approach is too slow, then store serial numbers in the
+ * nve group structures as they are defined and just compare
+ * serial numbers.
+ */
+ for (ALL_LIST_ELEMENTS(hc->nve_groups_sequential, node, nnode, rfg)) {
+ if ((rfg == rfg_un) || (rfg == rfg_vn)) {
+ return rfg;
+ }
+ }
+ vnc_zlog_debug_verbose(
+ "%s: shouldn't happen, returning NULL when un and vn match",
+ __func__);
+ return NULL; /* shouldn't happen */
+}
+
+/*------------------------------------------
+ * rfapi_get_rfp_start_val
+ *
+ * Returns value passed to rfapi on rfp_start
+ *
+ * input:
+ * void * bgp structure
+ *
+ * returns:
+ * void *
+ *------------------------------------------*/
+void *rfapi_get_rfp_start_val(void *bgpv)
+{
+ struct bgp *bgp = bgpv;
+ if (bgp == NULL || bgp->rfapi == NULL)
+ return NULL;
+ return bgp->rfapi->rfp;
+}
+
+/*------------------------------------------
+ * bgp_rfapi_is_vnc_configured
+ *
+ * Returns if VNC is configured
+ *
+ * input:
+ * bgp NULL (=use default instance)
+ *
+ * output:
+ *
+ * return value: If VNC is configured for the bgpd instance
+ * 0 Success
+ * EPERM Not Default instance (VNC operations not allowed)
+ * ENXIO VNC not configured
+ --------------------------------------------*/
+int bgp_rfapi_is_vnc_configured(struct bgp *bgp)
+{
+ if (bgp == NULL)
+ bgp = bgp_get_default();
+
+ if (bgp && bgp->inst_type != BGP_INSTANCE_TYPE_DEFAULT)
+ return EPERM;
+
+ if (bgp && bgp->rfapi_cfg)
+ return 0;
+ return ENXIO;
+}
+
+/***********************************************************************
+ * VNC Configuration/CLI
+ ***********************************************************************/
+#define VNC_VTY_CONFIG_CHECK(bgp) \
+ { \
+ switch (bgp_rfapi_is_vnc_configured(bgp)) { \
+ case EPERM: \
+ vty_out(vty, \
+ "VNC operations only permitted on default BGP instance.\n"); \
+ return CMD_WARNING_CONFIG_FAILED; \
+ break; \
+ case ENXIO: \
+ vty_out(vty, "VNC not configured.\n"); \
+ return CMD_WARNING_CONFIG_FAILED; \
+ break; \
+ default: \
+ break; \
+ } \
+ }
+
+DEFUN (vnc_advertise_un_method,
+ vnc_advertise_un_method_cmd,
+ "vnc advertise-un-method encap-attr",
+ VNC_CONFIG_STR
+ "Method of advertising UN addresses\n"
+ "Via Tunnel Encap attribute (in VPN SAFI)\n")
+{
+ VTY_DECLVAR_CONTEXT(bgp, bgp);
+ VNC_VTY_CONFIG_CHECK(bgp);
+
+ if (!strncmp(argv[2]->arg, "encap-safi", 7)) {
+ bgp->rfapi_cfg->flags |= BGP_VNC_CONFIG_ADV_UN_METHOD_ENCAP;
+ } else {
+ bgp->rfapi_cfg->flags &= ~BGP_VNC_CONFIG_ADV_UN_METHOD_ENCAP;
+ }
+
+ return CMD_SUCCESS;
+}
+
+/*-------------------------------------------------------------------------
+ * RFG defaults
+ *-----------------------------------------------------------------------*/
+
+
+DEFUN_NOSH (vnc_defaults,
+ vnc_defaults_cmd,
+ "vnc defaults", VNC_CONFIG_STR "Configure default NVE group\n")
+{
+ VTY_DECLVAR_CONTEXT(bgp, bgp);
+ VNC_VTY_CONFIG_CHECK(bgp);
+ if (bgp->inst_type != BGP_INSTANCE_TYPE_DEFAULT) {
+ vty_out(vty, "Malformed community-list value\n");
+ return CMD_WARNING_CONFIG_FAILED;
+ }
+ vty->node = BGP_VNC_DEFAULTS_NODE;
+ return CMD_SUCCESS;
+}
+
+static int set_ecom_list(struct vty *vty, int argc, struct cmd_token **argv,
+ struct ecommunity **list)
+{
+ struct ecommunity *ecom = NULL;
+ struct ecommunity *ecomadd;
+
+ for (; argc; --argc, ++argv) {
+
+ ecomadd = ecommunity_str2com(argv[0]->arg,
+ ECOMMUNITY_ROUTE_TARGET, 0);
+ if (!ecomadd) {
+ vty_out(vty, "Malformed community-list value\n");
+ if (ecom)
+ ecommunity_free(&ecom);
+ return CMD_WARNING_CONFIG_FAILED;
+ }
+
+ if (ecom) {
+ ecommunity_merge(ecom, ecomadd);
+ ecommunity_free(&ecomadd);
+ } else {
+ ecom = ecomadd;
+ }
+ }
+
+ if (*list) {
+ ecommunity_free(&*list);
+ }
+ *list = ecom;
+
+ return CMD_SUCCESS;
+}
+
+DEFUN (vnc_defaults_rt_import,
+ vnc_defaults_rt_import_cmd,
+ "rt import RTLIST...",
+ "Specify default route targets\n"
+ "Import filter\n"
+ "Space separated route target list (A.B.C.D:MN|EF:OPQR|GHJK:MN)\n")
+{
+ VTY_DECLVAR_CONTEXT(bgp, bgp);
+ return set_ecom_list(vty, argc - 2, argv + 2,
+ &bgp->rfapi_cfg->default_rt_import_list);
+}
+
+DEFUN (vnc_defaults_rt_export,
+ vnc_defaults_rt_export_cmd,
+ "rt export RTLIST...",
+ "Configure default route targets\n"
+ "Export filter\n"
+ "Space separated route target list (A.B.C.D:MN|EF:OPQR|GHJK:MN)\n")
+{
+ VTY_DECLVAR_CONTEXT(bgp, bgp);
+ return set_ecom_list(vty, argc - 2, argv + 2,
+ &bgp->rfapi_cfg->default_rt_export_list);
+}
+
+DEFUN (vnc_defaults_rt_both,
+ vnc_defaults_rt_both_cmd,
+ "rt both RTLIST...",
+ "Configure default route targets\n"
+ "Export+import filters\n"
+ "Space separated route target list (A.B.C.D:MN|EF:OPQR|GHJK:MN)\n")
+{
+ VTY_DECLVAR_CONTEXT(bgp, bgp);
+ int rc;
+
+ rc = set_ecom_list(vty, argc - 2, argv + 2,
+ &bgp->rfapi_cfg->default_rt_import_list);
+ if (rc != CMD_SUCCESS)
+ return rc;
+ return set_ecom_list(vty, argc - 2, argv + 2,
+ &bgp->rfapi_cfg->default_rt_export_list);
+}
+
+DEFUN (vnc_defaults_rd,
+ vnc_defaults_rd_cmd,
+ "rd ASN:NN_OR_IP-ADDRESS:NN",
+ "Specify default route distinguisher\n"
+ "Route Distinguisher (<as-number>:<number> | <ip-address>:<number> | auto:vn:<number> )\n")
+{
+ VTY_DECLVAR_CONTEXT(bgp, bgp);
+ int ret;
+ struct prefix_rd prd;
+
+ if (!strncmp(argv[1]->arg, "auto:vn:", 8)) {
+ /*
+ * use AF_UNIX to designate automatically-assigned RD
+ * auto:vn:nn where nn is a 2-octet quantity
+ */
+ char *end = NULL;
+ uint32_t value32 = strtoul(argv[1]->arg + 8, &end, 10);
+ uint16_t value = value32 & 0xffff;
+
+ if (!argv[1]->arg[8] || *end) {
+ vty_out(vty, "%% Malformed rd\n");
+ return CMD_WARNING_CONFIG_FAILED;
+ }
+ if (value32 > 0xffff) {
+ vty_out(vty, "%% Malformed rd (must be less than %u\n",
+ 0x0ffff);
+ return CMD_WARNING_CONFIG_FAILED;
+ }
+
+ memset(&prd, 0, sizeof(prd));
+ prd.family = AF_UNIX;
+ prd.prefixlen = 64;
+ prd.val[0] = (RD_TYPE_IP >> 8) & 0x0ff;
+ prd.val[1] = RD_TYPE_IP & 0x0ff;
+ prd.val[6] = (value >> 8) & 0x0ff;
+ prd.val[7] = value & 0x0ff;
+
+ } else {
+
+ ret = str2prefix_rd(argv[1]->arg, &prd);
+ if (!ret) {
+ vty_out(vty, "%% Malformed rd\n");
+ return CMD_WARNING_CONFIG_FAILED;
+ }
+ }
+
+ bgp->rfapi_cfg->default_rd = prd;
+ return CMD_SUCCESS;
+}
+
+DEFUN (vnc_defaults_l2rd,
+ vnc_defaults_l2rd_cmd,
+ "l2rd <(1-255)|auto-vn>",
+ "Specify default Local Nve ID value to use in RD for L2 routes\n"
+ "Fixed value 1-255\n"
+ "use the low-order octet of the NVE's VN address\n")
+{
+ VTY_DECLVAR_CONTEXT(bgp, bgp);
+ uint8_t value = 0;
+
+ if (strmatch(argv[1]->text, "auto-vn")) {
+ value = 0;
+ } else {
+ char *end = NULL;
+ unsigned long value_l = strtoul(argv[1]->arg, &end, 10);
+
+ value = value_l & 0xff;
+ if (!argv[1]->arg[0] || *end) {
+ vty_out(vty, "%% Malformed l2 nve ID \"%s\"\n",
+ argv[1]->arg);
+ return CMD_WARNING_CONFIG_FAILED;
+ }
+ if ((value_l < 1) || (value_l > 0xff)) {
+ vty_out(vty,
+ "%% Malformed l2 nve id (must be greater than 0 and less than %u\n",
+ 0x100);
+ return CMD_WARNING_CONFIG_FAILED;
+ }
+ }
+ bgp->rfapi_cfg->flags |= BGP_VNC_CONFIG_L2RD;
+ bgp->rfapi_cfg->default_l2rd = value;
+
+ return CMD_SUCCESS;
+}
+
+DEFUN (vnc_defaults_no_l2rd,
+ vnc_defaults_no_l2rd_cmd,
+ "no l2rd",
+ NO_STR
+ "Specify default Local Nve ID value to use in RD for L2 routes\n")
+{
+ VTY_DECLVAR_CONTEXT(bgp, bgp);
+
+ bgp->rfapi_cfg->default_l2rd = 0;
+ bgp->rfapi_cfg->flags &= ~BGP_VNC_CONFIG_L2RD;
+
+ return CMD_SUCCESS;
+}
+
+DEFUN (vnc_defaults_responselifetime,
+ vnc_defaults_responselifetime_cmd,
+ "response-lifetime <LIFETIME|infinite>",
+ "Specify default response lifetime\n"
+ "Response lifetime in seconds\n" "Infinite response lifetime\n")
+{
+ VTY_DECLVAR_CONTEXT(bgp, bgp);
+ uint32_t rspint;
+ struct rfapi *h = NULL;
+ struct listnode *hdnode;
+ struct rfapi_descriptor *rfd;
+
+ h = bgp->rfapi;
+ if (!h)
+ return CMD_WARNING_CONFIG_FAILED;
+
+ if (strmatch(argv[1]->text, "infinite")) {
+ rspint = RFAPI_INFINITE_LIFETIME;
+ } else {
+ rspint = strtoul(argv[1]->arg, NULL, 10);
+ if (rspint > INT32_MAX)
+ rspint = INT32_MAX; /* is really an int, not an unsigned
+ int */
+ }
+
+ bgp->rfapi_cfg->default_response_lifetime = rspint;
+
+ for (ALL_LIST_ELEMENTS_RO(&h->descriptors, hdnode, rfd))
+ if (rfd->rfg
+ && !(rfd->rfg->flags & RFAPI_RFG_RESPONSE_LIFETIME))
+ rfd->response_lifetime = rfd->rfg->response_lifetime =
+ rspint;
+
+ return CMD_SUCCESS;
+}
+
+struct rfapi_nve_group_cfg *
+bgp_rfapi_cfg_match_byname(struct bgp *bgp, const char *name,
+ rfapi_group_cfg_type_t type) /* _MAX = any */
+{
+ struct rfapi_nve_group_cfg *rfg;
+ struct listnode *node, *nnode;
+
+ for (ALL_LIST_ELEMENTS(bgp->rfapi_cfg->nve_groups_sequential, node,
+ nnode, rfg)) {
+ if ((type == RFAPI_GROUP_CFG_MAX || type == rfg->type)
+ && !strcmp(rfg->name, name))
+ return rfg;
+ }
+ return NULL;
+}
+
+static struct rfapi_nve_group_cfg *
+rfapi_group_new(struct bgp *bgp, rfapi_group_cfg_type_t type, const char *name)
+{
+ struct rfapi_nve_group_cfg *rfg;
+
+ rfg = XCALLOC(MTYPE_RFAPI_GROUP_CFG,
+ sizeof(struct rfapi_nve_group_cfg));
+ rfg->type = type;
+ rfg->name = strdup(name);
+ /* add to tail of list */
+ listnode_add(bgp->rfapi_cfg->nve_groups_sequential, rfg);
+ rfg->label = MPLS_LABEL_NONE;
+
+ QOBJ_REG(rfg, rfapi_nve_group_cfg);
+
+ return rfg;
+}
+
+static struct rfapi_l2_group_cfg *rfapi_l2_group_lookup_byname(struct bgp *bgp,
+ const char *name)
+{
+ struct rfapi_l2_group_cfg *rfg;
+ struct listnode *node, *nnode;
+
+ if (bgp->rfapi_cfg->l2_groups == NULL) /* not the best place for this */
+ bgp->rfapi_cfg->l2_groups = list_new();
+
+ for (ALL_LIST_ELEMENTS(bgp->rfapi_cfg->l2_groups, node, nnode, rfg)) {
+ if (!strcmp(rfg->name, name))
+ return rfg;
+ }
+ return NULL;
+}
+
+static struct rfapi_l2_group_cfg *rfapi_l2_group_new(void)
+{
+ struct rfapi_l2_group_cfg *rfg;
+
+ rfg = XCALLOC(MTYPE_RFAPI_L2_CFG, sizeof(struct rfapi_l2_group_cfg));
+ QOBJ_REG(rfg, rfapi_l2_group_cfg);
+
+ return rfg;
+}
+
+static void rfapi_l2_group_del(struct rfapi_l2_group_cfg *rfg)
+{
+ QOBJ_UNREG(rfg);
+ XFREE(MTYPE_RFAPI_L2_CFG, rfg);
+}
+
+static int rfapi_str2route_type(const char *l3str, const char *pstr, afi_t *afi,
+ int *type)
+{
+ if (!l3str || !pstr)
+ return EINVAL;
+
+ if (!strcmp(l3str, "ipv4")) {
+ *afi = AFI_IP;
+ } else {
+ if (!strcmp(l3str, "ipv6"))
+ *afi = AFI_IP6;
+ else
+ return ENOENT;
+ }
+
+ if (!strcmp(pstr, "connected"))
+ *type = ZEBRA_ROUTE_CONNECT;
+ if (!strcmp(pstr, "kernel"))
+ *type = ZEBRA_ROUTE_KERNEL;
+ if (!strcmp(pstr, "static"))
+ *type = ZEBRA_ROUTE_STATIC;
+ if (!strcmp(pstr, "bgp"))
+ *type = ZEBRA_ROUTE_BGP;
+ if (!strcmp(pstr, "bgp-direct"))
+ *type = ZEBRA_ROUTE_BGP_DIRECT;
+ if (!strcmp(pstr, "bgp-direct-to-nve-groups"))
+ *type = ZEBRA_ROUTE_BGP_DIRECT_EXT;
+
+ if (!strcmp(pstr, "rip")) {
+ if (*afi == AFI_IP)
+ *type = ZEBRA_ROUTE_RIP;
+ else
+ *type = ZEBRA_ROUTE_RIPNG;
+ }
+
+ if (!strcmp(pstr, "ripng")) {
+ if (*afi == AFI_IP)
+ return EAFNOSUPPORT;
+ *type = ZEBRA_ROUTE_RIPNG;
+ }
+
+ if (!strcmp(pstr, "ospf")) {
+ if (*afi == AFI_IP)
+ *type = ZEBRA_ROUTE_OSPF;
+ else
+ *type = ZEBRA_ROUTE_OSPF6;
+ }
+
+ if (!strcmp(pstr, "ospf6")) {
+ if (*afi == AFI_IP)
+ return EAFNOSUPPORT;
+ *type = ZEBRA_ROUTE_OSPF6;
+ }
+
+ return 0;
+}
+
+/*-------------------------------------------------------------------------
+ * redistribute
+ *-----------------------------------------------------------------------*/
+
+#define VNC_REDIST_ENABLE(bgp, afi, type) \
+ do { \
+ switch (type) { \
+ case ZEBRA_ROUTE_BGP_DIRECT: \
+ vnc_import_bgp_redist_enable((bgp), (afi)); \
+ break; \
+ case ZEBRA_ROUTE_BGP_DIRECT_EXT: \
+ vnc_import_bgp_exterior_redist_enable((bgp), (afi)); \
+ break; \
+ default: \
+ if ((type) < ZEBRA_ROUTE_MAX) \
+ vnc_redistribute_set((bgp), (afi), (type)); \
+ break; \
+ } \
+ } while (0)
+
+#define VNC_REDIST_DISABLE(bgp, afi, type) \
+ do { \
+ switch (type) { \
+ case ZEBRA_ROUTE_BGP_DIRECT: \
+ vnc_import_bgp_redist_disable((bgp), (afi)); \
+ break; \
+ case ZEBRA_ROUTE_BGP_DIRECT_EXT: \
+ vnc_import_bgp_exterior_redist_disable((bgp), (afi)); \
+ break; \
+ default: \
+ if ((type) < ZEBRA_ROUTE_MAX) \
+ vnc_redistribute_unset((bgp), (afi), (type)); \
+ break; \
+ } \
+ } while (0)
+
+static uint8_t redist_was_enabled[AFI_MAX][ZEBRA_ROUTE_MAX];
+
+static void vnc_redistribute_prechange(struct bgp *bgp)
+{
+ afi_t afi;
+ int type;
+
+ vnc_zlog_debug_verbose("%s: entry", __func__);
+ memset(redist_was_enabled, 0, sizeof(redist_was_enabled));
+
+ /*
+ * Look to see if we have any redistribution enabled. If so, flush
+ * the corresponding routes and turn off redistribution temporarily.
+ * We need to do it because the RD's used for the redistributed
+ * routes depend on the nve group.
+ */
+ for (afi = AFI_IP; afi < AFI_MAX; ++afi) {
+ for (type = 0; type < ZEBRA_ROUTE_MAX; ++type) {
+ if (bgp->rfapi_cfg->redist[afi][type]) {
+ redist_was_enabled[afi][type] = 1;
+ VNC_REDIST_DISABLE(bgp, afi, type);
+ }
+ }
+ }
+ vnc_zlog_debug_verbose("%s: return", __func__);
+}
+
+static void vnc_redistribute_postchange(struct bgp *bgp)
+{
+ afi_t afi;
+ int type;
+
+ vnc_zlog_debug_verbose("%s: entry", __func__);
+ /*
+ * If we turned off redistribution above, turn it back on. Doing so
+ * will tell zebra to resend the routes to us
+ */
+ for (afi = AFI_IP; afi < AFI_MAX; ++afi) {
+ for (type = 0; type < ZEBRA_ROUTE_MAX; ++type) {
+ if (redist_was_enabled[afi][type]) {
+ VNC_REDIST_ENABLE(bgp, afi, type);
+ }
+ }
+ }
+ vnc_zlog_debug_verbose("%s: return", __func__);
+}
+
+DEFUN (vnc_redistribute_rh_roo_localadmin,
+ vnc_redistribute_rh_roo_localadmin_cmd,
+ "vnc redistribute resolve-nve roo-ec-local-admin (0-65535)",
+ VNC_CONFIG_STR
+ "Redistribute routes into VNC\n"
+ "Resolve-NVE mode\n"
+ "Route Origin Extended Community Local Admin Field\n" "Field value\n")
+{
+ VTY_DECLVAR_CONTEXT(bgp, bgp);
+ uint32_t localadmin;
+ char *endptr;
+
+ VNC_VTY_CONFIG_CHECK(bgp);
+
+ localadmin = strtoul(argv[4]->arg, &endptr, 0);
+ if (!argv[4]->arg[0] || *endptr) {
+ vty_out(vty, "%% Malformed value\n");
+ return CMD_WARNING_CONFIG_FAILED;
+ }
+
+ if (localadmin > 0xffff) {
+ vty_out(vty, "%% Value out of range (0-%d)\n", 0xffff);
+ return CMD_WARNING_CONFIG_FAILED;
+ }
+
+ if (bgp->rfapi_cfg->resolve_nve_roo_local_admin == localadmin)
+ return CMD_SUCCESS;
+
+ if ((bgp->rfapi_cfg->flags & BGP_VNC_CONFIG_EXPORT_BGP_MODE_BITS)
+ == BGP_VNC_CONFIG_EXPORT_BGP_MODE_CE) {
+
+ vnc_export_bgp_prechange(bgp);
+ }
+ vnc_redistribute_prechange(bgp);
+
+ bgp->rfapi_cfg->resolve_nve_roo_local_admin = localadmin;
+
+ if ((bgp->rfapi_cfg->flags & BGP_VNC_CONFIG_EXPORT_BGP_MODE_BITS)
+ == BGP_VNC_CONFIG_EXPORT_BGP_MODE_CE) {
+
+ vnc_export_bgp_postchange(bgp);
+ }
+ vnc_redistribute_postchange(bgp);
+
+ return CMD_SUCCESS;
+}
+
+
+DEFUN (vnc_redistribute_mode,
+ vnc_redistribute_mode_cmd,
+ "vnc redistribute mode <nve-group|plain|resolve-nve>",
+ VNC_CONFIG_STR
+ "Redistribute routes into VNC\n"
+ "Redistribution mode\n"
+ "Based on redistribute nve-group\n"
+ "Unmodified\n" "Resolve each nexthop to connected NVEs\n")
+{
+ VTY_DECLVAR_CONTEXT(bgp, bgp);
+ vnc_redist_mode_t newmode;
+
+ VNC_VTY_CONFIG_CHECK(bgp);
+
+ switch (argv[3]->arg[0]) {
+ case 'n':
+ newmode = VNC_REDIST_MODE_RFG;
+ break;
+
+ case 'p':
+ newmode = VNC_REDIST_MODE_PLAIN;
+ break;
+
+ case 'r':
+ newmode = VNC_REDIST_MODE_RESOLVE_NVE;
+ break;
+
+ default:
+ vty_out(vty, "unknown redistribute mode\n");
+ return CMD_WARNING_CONFIG_FAILED;
+ }
+
+ if (newmode != bgp->rfapi_cfg->redist_mode) {
+ vnc_redistribute_prechange(bgp);
+ bgp->rfapi_cfg->redist_mode = newmode;
+ vnc_redistribute_postchange(bgp);
+ }
+
+ return CMD_SUCCESS;
+}
+
+DEFUN (vnc_redistribute_protocol,
+ vnc_redistribute_protocol_cmd,
+ "vnc redistribute <ipv4|ipv6> <bgp|bgp-direct|bgp-direct-to-nve-groups|connected|kernel|ospf|rip|static>",
+ VNC_CONFIG_STR
+ "Redistribute routes into VNC\n"
+ "IPv4 routes\n"
+ "IPv6 routes\n"
+ "From BGP\n"
+ "From BGP without Zebra\n"
+ "From BGP without Zebra, only to configured NVE groups\n"
+ "Connected interfaces\n"
+ "From kernel routes\n"
+ "From Open Shortest Path First (OSPF)\n"
+ "From Routing Information Protocol (RIP)\n" "From Static routes\n")
+{
+ VTY_DECLVAR_CONTEXT(bgp, bgp);
+ int type = ZEBRA_ROUTE_MAX; /* init to bogus value */
+ afi_t afi;
+
+ VNC_VTY_CONFIG_CHECK(bgp);
+
+ if (rfapi_str2route_type(argv[2]->arg, argv[3]->arg, &afi, &type)) {
+ vty_out(vty, "%% Invalid route type\n");
+ return CMD_WARNING_CONFIG_FAILED;
+ }
+
+ if (type == ZEBRA_ROUTE_BGP_DIRECT_EXT) {
+ if (bgp->rfapi_cfg->redist_bgp_exterior_view_name) {
+ VNC_REDIST_DISABLE(bgp, afi,
+ type); /* disabled view implicitly */
+ free(bgp->rfapi_cfg->redist_bgp_exterior_view_name);
+ bgp->rfapi_cfg->redist_bgp_exterior_view_name = NULL;
+ }
+ bgp->rfapi_cfg->redist_bgp_exterior_view = bgp;
+ }
+
+ VNC_REDIST_ENABLE(bgp, afi, type);
+
+ return CMD_SUCCESS;
+}
+
+DEFUN (vnc_no_redistribute_protocol,
+ vnc_no_redistribute_protocol_cmd,
+ "no vnc redistribute <ipv4|ipv6> <bgp|bgp-direct|bgp-direct-to-nve-groups|connected|kernel|ospf|rip|static>",
+ NO_STR
+ VNC_CONFIG_STR
+ "Redistribute from other protocol\n"
+ "IPv4 routes\n"
+ "IPv6 routes\n"
+ "From BGP\n"
+ "From BGP without Zebra\n"
+ "From BGP without Zebra, only to configured NVE groups\n"
+ "Connected interfaces\n"
+ "From kernel routes\n"
+ "From Open Shortest Path First (OSPF)\n"
+ "From Routing Information Protocol (RIP)\n" "From Static routes\n")
+{
+ VTY_DECLVAR_CONTEXT(bgp, bgp);
+ int type;
+ afi_t afi;
+
+ VNC_VTY_CONFIG_CHECK(bgp);
+
+ if (rfapi_str2route_type(argv[3]->arg, argv[4]->arg, &afi, &type)) {
+ vty_out(vty, "%% Invalid route type\n");
+ return CMD_WARNING_CONFIG_FAILED;
+ }
+
+ VNC_REDIST_DISABLE(bgp, afi, type);
+
+ if (type == ZEBRA_ROUTE_BGP_DIRECT_EXT) {
+ if (bgp->rfapi_cfg->redist_bgp_exterior_view_name) {
+ free(bgp->rfapi_cfg->redist_bgp_exterior_view_name);
+ bgp->rfapi_cfg->redist_bgp_exterior_view_name = NULL;
+ }
+ bgp->rfapi_cfg->redist_bgp_exterior_view = NULL;
+ }
+
+ return CMD_SUCCESS;
+}
+
+DEFUN (vnc_redistribute_bgp_exterior,
+ vnc_redistribute_bgp_exterior_cmd,
+ "vnc redistribute <ipv4|ipv6> bgp-direct-to-nve-groups view NAME",
+ VNC_CONFIG_STR
+ "Redistribute routes into VNC\n"
+ "IPv4 routes\n"
+ "IPv6 routes\n"
+ "From BGP without Zebra, only to configured NVE groups\n"
+ "From BGP view\n" "BGP view name\n")
+{
+ VTY_DECLVAR_CONTEXT(bgp, bgp);
+ int type;
+ afi_t afi;
+
+ VNC_VTY_CONFIG_CHECK(bgp);
+
+ if (rfapi_str2route_type(argv[2]->arg, "bgp-direct-to-nve-groups", &afi,
+ &type)) {
+ vty_out(vty, "%% Invalid route type\n");
+ return CMD_WARNING_CONFIG_FAILED;
+ }
+
+ if (bgp->rfapi_cfg->redist_bgp_exterior_view_name)
+ free(bgp->rfapi_cfg->redist_bgp_exterior_view_name);
+ bgp->rfapi_cfg->redist_bgp_exterior_view_name = strdup(argv[5]->arg);
+ /* could be NULL if name is not defined yet */
+ bgp->rfapi_cfg->redist_bgp_exterior_view =
+ bgp_lookup_by_name(argv[5]->arg);
+
+ VNC_REDIST_ENABLE(bgp, afi, type);
+
+ return CMD_SUCCESS;
+}
+
+DEFUN (vnc_redistribute_nvegroup,
+ vnc_redistribute_nvegroup_cmd,
+ "vnc redistribute nve-group NAME",
+ VNC_CONFIG_STR
+ "Assign a NVE group to routes redistributed from another routing protocol\n"
+ "NVE group\n" "Group name\n")
+{
+ VTY_DECLVAR_CONTEXT(bgp, bgp);
+ VNC_VTY_CONFIG_CHECK(bgp);
+
+ vnc_redistribute_prechange(bgp);
+
+ /*
+ * OK if nve group doesn't exist yet; we'll set the pointer
+ * when the group is defined later
+ */
+ bgp->rfapi_cfg->rfg_redist = bgp_rfapi_cfg_match_byname(
+ bgp, argv[3]->arg, RFAPI_GROUP_CFG_NVE);
+ if (bgp->rfapi_cfg->rfg_redist_name)
+ free(bgp->rfapi_cfg->rfg_redist_name);
+ bgp->rfapi_cfg->rfg_redist_name = strdup(argv[3]->arg);
+
+ vnc_redistribute_postchange(bgp);
+
+ return CMD_SUCCESS;
+}
+
+DEFUN (vnc_redistribute_no_nvegroup,
+ vnc_redistribute_no_nvegroup_cmd,
+ "no vnc redistribute nve-group",
+ NO_STR
+ VNC_CONFIG_STR
+ "Redistribute from other protocol\n"
+ "Assign a NVE group to routes redistributed from another routing protocol\n")
+{
+ VTY_DECLVAR_CONTEXT(bgp, bgp);
+
+ VNC_VTY_CONFIG_CHECK(bgp);
+
+ vnc_redistribute_prechange(bgp);
+
+ bgp->rfapi_cfg->rfg_redist = NULL;
+ if (bgp->rfapi_cfg->rfg_redist_name)
+ free(bgp->rfapi_cfg->rfg_redist_name);
+ bgp->rfapi_cfg->rfg_redist_name = NULL;
+
+ vnc_redistribute_postchange(bgp);
+
+ return CMD_SUCCESS;
+}
+
+
+DEFUN (vnc_redistribute_lifetime,
+ vnc_redistribute_lifetime_cmd,
+ "vnc redistribute lifetime <LIFETIME|infinite>",
+ VNC_CONFIG_STR
+ "Redistribute\n"
+ "Assign a lifetime to routes redistributed from another routing protocol\n"
+ "lifetime value (32 bit)\n"
+ "Allow lifetime to never expire\n")
+{
+ VTY_DECLVAR_CONTEXT(bgp, bgp);
+ VNC_VTY_CONFIG_CHECK(bgp);
+
+ vnc_redistribute_prechange(bgp);
+
+ if (strmatch(argv[3]->text, "infinite")) {
+ bgp->rfapi_cfg->redist_lifetime = RFAPI_INFINITE_LIFETIME;
+ } else {
+ bgp->rfapi_cfg->redist_lifetime =
+ strtoul(argv[3]->arg, NULL, 10);
+ }
+
+ vnc_redistribute_postchange(bgp);
+
+ return CMD_SUCCESS;
+}
+
+/*-- redist policy, non-nvegroup start --*/
+
+DEFUN (vnc_redist_bgpdirect_no_prefixlist,
+ vnc_redist_bgpdirect_no_prefixlist_cmd,
+ "no vnc redistribute <bgp-direct|bgp-direct-to-nve-groups> <ipv4|ipv6> prefix-list",
+ NO_STR
+ VNC_CONFIG_STR
+ "Redistribute from other protocol\n"
+ "Redistribute from BGP directly\n"
+ "Redistribute from BGP without Zebra, only to configured NVE groups\n"
+ "IPv4 routes\n"
+ "IPv6 routes\n" "Prefix-list for filtering redistributed routes\n")
+{
+ VTY_DECLVAR_CONTEXT(bgp, bgp);
+ afi_t afi;
+ struct rfapi_cfg *hc;
+ uint8_t route_type = 0;
+
+ VNC_VTY_CONFIG_CHECK(bgp);
+ hc = bgp->rfapi_cfg;
+
+ if (strmatch(argv[3]->text, "bgp-direct")) {
+ route_type = ZEBRA_ROUTE_BGP_DIRECT;
+ } else {
+ route_type = ZEBRA_ROUTE_BGP_DIRECT_EXT;
+ }
+
+ if (strmatch(argv[4]->text, "ipv4")) {
+ afi = AFI_IP;
+ } else {
+ afi = AFI_IP6;
+ }
+
+ vnc_redistribute_prechange(bgp);
+
+ if (hc->plist_redist_name[route_type][afi])
+ free(hc->plist_redist_name[route_type][afi]);
+ hc->plist_redist_name[route_type][afi] = NULL;
+ hc->plist_redist[route_type][afi] = NULL;
+
+ vnc_redistribute_postchange(bgp);
+
+ return CMD_SUCCESS;
+}
+
+DEFUN (vnc_redist_bgpdirect_prefixlist,
+ vnc_redist_bgpdirect_prefixlist_cmd,
+ "vnc redistribute <bgp-direct|bgp-direct-to-nve-groups> <ipv4|ipv6> prefix-list NAME",
+ VNC_CONFIG_STR
+ "Redistribute from other protocol\n"
+ "Redistribute from BGP directly\n"
+ "Redistribute from BGP without Zebra, only to configured NVE groups\n"
+ "IPv4 routes\n"
+ "IPv6 routes\n"
+ "Prefix-list for filtering redistributed routes\n"
+ "prefix list name\n")
+{
+ VTY_DECLVAR_CONTEXT(bgp, bgp);
+ struct rfapi_cfg *hc;
+ afi_t afi;
+ uint8_t route_type = 0;
+
+ VNC_VTY_CONFIG_CHECK(bgp);
+ hc = bgp->rfapi_cfg;
+
+ if (strmatch(argv[2]->text, "bgp-direct")) {
+ route_type = ZEBRA_ROUTE_BGP_DIRECT;
+ } else {
+ route_type = ZEBRA_ROUTE_BGP_DIRECT_EXT;
+ }
+
+ if (strmatch(argv[3]->text, "ipv4")) {
+ afi = AFI_IP;
+ } else {
+ afi = AFI_IP6;
+ }
+
+ vnc_redistribute_prechange(bgp);
+
+ if (hc->plist_redist_name[route_type][afi])
+ free(hc->plist_redist_name[route_type][afi]);
+ hc->plist_redist_name[route_type][afi] = strdup(argv[5]->arg);
+ hc->plist_redist[route_type][afi] =
+ prefix_list_lookup(afi, argv[5]->arg);
+
+ vnc_redistribute_postchange(bgp);
+
+ return CMD_SUCCESS;
+}
+
+DEFUN (vnc_redist_bgpdirect_no_routemap,
+ vnc_redist_bgpdirect_no_routemap_cmd,
+ "no vnc redistribute <bgp-direct|bgp-direct-to-nve-groups> route-map",
+ NO_STR
+ VNC_CONFIG_STR
+ "Redistribute from other protocols\n"
+ "Redistribute from BGP directly\n"
+ "Redistribute from BGP without Zebra, only to configured NVE groups\n"
+ "Route-map for filtering redistributed routes\n")
+{
+ VTY_DECLVAR_CONTEXT(bgp, bgp);
+ struct rfapi_cfg *hc;
+ uint8_t route_type = 0;
+
+ VNC_VTY_CONFIG_CHECK(bgp);
+ hc = bgp->rfapi_cfg;
+
+ if (strmatch(argv[3]->text, "bgp-direct")) {
+ route_type = ZEBRA_ROUTE_BGP_DIRECT;
+ } else {
+ route_type = ZEBRA_ROUTE_BGP_DIRECT_EXT;
+ }
+
+ vnc_redistribute_prechange(bgp);
+
+ if (hc->routemap_redist_name[route_type])
+ free(hc->routemap_redist_name[route_type]);
+ hc->routemap_redist_name[route_type] = NULL;
+ hc->routemap_redist[route_type] = NULL;
+
+ vnc_redistribute_postchange(bgp);
+
+ return CMD_SUCCESS;
+}
+
+DEFUN (vnc_redist_bgpdirect_routemap,
+ vnc_redist_bgpdirect_routemap_cmd,
+ "vnc redistribute <bgp-direct|bgp-direct-to-nve-groups> route-map NAME",
+ VNC_CONFIG_STR
+ "Redistribute from other protocols\n"
+ "Redistribute from BGP directly\n"
+ "Redistribute from BGP without Zebra, only to configured NVE groups\n"
+ "Route-map for filtering exported routes\n" "route map name\n")
+{
+ VTY_DECLVAR_CONTEXT(bgp, bgp);
+ struct rfapi_cfg *hc;
+ uint8_t route_type = 0;
+
+ VNC_VTY_CONFIG_CHECK(bgp);
+ hc = bgp->rfapi_cfg;
+
+ if (strmatch(argv[2]->text, "bgp-direct")) {
+ route_type = ZEBRA_ROUTE_BGP_DIRECT;
+ } else {
+ route_type = ZEBRA_ROUTE_BGP_DIRECT_EXT;
+ }
+
+ vnc_redistribute_prechange(bgp);
+
+ if (hc->routemap_redist_name[route_type])
+ free(hc->routemap_redist_name[route_type]);
+
+ /* If the old route map config overwrite with new
+ * route map config , old routemap counter have to be
+ * reduced.
+ */
+ route_map_counter_decrement(hc->routemap_redist[route_type]);
+ hc->routemap_redist_name[route_type] = strdup(argv[4]->arg);
+ hc->routemap_redist[route_type] =
+ route_map_lookup_by_name(argv[4]->arg);
+ route_map_counter_increment(hc->routemap_redist[route_type]);
+
+ vnc_redistribute_postchange(bgp);
+
+ return CMD_SUCCESS;
+}
+
+/*-- redist policy, non-nvegroup end --*/
+
+/*-- redist policy, nvegroup start --*/
+
+DEFUN (vnc_nve_group_redist_bgpdirect_no_prefixlist,
+ vnc_nve_group_redist_bgpdirect_no_prefixlist_cmd,
+ "no redistribute bgp-direct <ipv4|ipv6> prefix-list",
+ NO_STR
+ "Redistribute from other protocol\n"
+ "Redistribute from BGP directly\n"
+ "IPv4 routes\n"
+ "IPv6 routes\n"
+ "Prefix-list for filtering redistributed routes\n")
+{
+ VTY_DECLVAR_CONTEXT(bgp, bgp);
+ VTY_DECLVAR_CONTEXT_SUB(rfapi_nve_group_cfg, rfg)
+ afi_t afi;
+
+ VNC_VTY_CONFIG_CHECK(bgp);
+
+ /* make sure it's still in list */
+ if (!listnode_lookup(bgp->rfapi_cfg->nve_groups_sequential, rfg)) {
+ /* Not in list anymore */
+ vty_out(vty, "Current NVE group no longer exists\n");
+ return CMD_WARNING_CONFIG_FAILED;
+ }
+
+ if (strmatch(argv[3]->text, "ipv4")) {
+ afi = AFI_IP;
+ } else {
+ afi = AFI_IP6;
+ }
+
+ vnc_redistribute_prechange(bgp);
+
+ if (rfg->plist_redist_name[ZEBRA_ROUTE_BGP_DIRECT][afi])
+ free(rfg->plist_redist_name[ZEBRA_ROUTE_BGP_DIRECT][afi]);
+ rfg->plist_redist_name[ZEBRA_ROUTE_BGP_DIRECT][afi] = NULL;
+ rfg->plist_redist[ZEBRA_ROUTE_BGP_DIRECT][afi] = NULL;
+
+ vnc_redistribute_postchange(bgp);
+
+ return CMD_SUCCESS;
+}
+
+DEFUN (vnc_nve_group_redist_bgpdirect_prefixlist,
+ vnc_nve_group_redist_bgpdirect_prefixlist_cmd,
+ "redistribute bgp-direct <ipv4|ipv6> prefix-list NAME",
+ "Redistribute from other protocol\n"
+ "Redistribute from BGP directly\n"
+ "IPv4 routes\n"
+ "IPv6 routes\n"
+ "Prefix-list for filtering redistributed routes\n"
+ "prefix list name\n")
+{
+ VTY_DECLVAR_CONTEXT(bgp, bgp);
+ VTY_DECLVAR_CONTEXT_SUB(rfapi_nve_group_cfg, rfg);
+ afi_t afi;
+
+ VNC_VTY_CONFIG_CHECK(bgp);
+
+ /* make sure it's still in list */
+ if (!listnode_lookup(bgp->rfapi_cfg->nve_groups_sequential, rfg)) {
+ /* Not in list anymore */
+ vty_out(vty, "Current NVE group no longer exists\n");
+ return CMD_WARNING_CONFIG_FAILED;
+ }
+
+ if (strmatch(argv[2]->text, "ipv4")) {
+ afi = AFI_IP;
+ } else {
+ afi = AFI_IP6;
+ }
+
+ vnc_redistribute_prechange(bgp);
+
+ if (rfg->plist_redist_name[ZEBRA_ROUTE_BGP_DIRECT][afi])
+ free(rfg->plist_redist_name[ZEBRA_ROUTE_BGP_DIRECT][afi]);
+ rfg->plist_redist_name[ZEBRA_ROUTE_BGP_DIRECT][afi] =
+ strdup(argv[4]->arg);
+ rfg->plist_redist[ZEBRA_ROUTE_BGP_DIRECT][afi] =
+ prefix_list_lookup(afi, argv[4]->arg);
+
+ vnc_redistribute_postchange(bgp);
+
+ return CMD_SUCCESS;
+}
+
+DEFUN (vnc_nve_group_redist_bgpdirect_no_routemap,
+ vnc_nve_group_redist_bgpdirect_no_routemap_cmd,
+ "no redistribute bgp-direct route-map",
+ NO_STR
+ "Redistribute from other protocols\n"
+ "Redistribute from BGP directly\n"
+ "Route-map for filtering redistributed routes\n")
+{
+ VTY_DECLVAR_CONTEXT(bgp, bgp);
+ VTY_DECLVAR_CONTEXT_SUB(rfapi_nve_group_cfg, rfg);
+
+ VNC_VTY_CONFIG_CHECK(bgp);
+
+ /* make sure it's still in list */
+ if (!listnode_lookup(bgp->rfapi_cfg->nve_groups_sequential, rfg)) {
+ /* Not in list anymore */
+ vty_out(vty, "Current NVE group no longer exists\n");
+ return CMD_WARNING_CONFIG_FAILED;
+ }
+
+ vnc_redistribute_prechange(bgp);
+
+ if (rfg->routemap_redist_name[ZEBRA_ROUTE_BGP_DIRECT])
+ free(rfg->routemap_redist_name[ZEBRA_ROUTE_BGP_DIRECT]);
+ route_map_counter_decrement(
+ rfg->routemap_redist[ZEBRA_ROUTE_BGP_DIRECT]);
+ rfg->routemap_redist_name[ZEBRA_ROUTE_BGP_DIRECT] = NULL;
+ rfg->routemap_redist[ZEBRA_ROUTE_BGP_DIRECT] = NULL;
+
+ vnc_redistribute_postchange(bgp);
+
+ return CMD_SUCCESS;
+}
+
+DEFUN (vnc_nve_group_redist_bgpdirect_routemap,
+ vnc_nve_group_redist_bgpdirect_routemap_cmd,
+ "redistribute bgp-direct route-map NAME",
+ "Redistribute from other protocols\n"
+ "Redistribute from BGP directly\n"
+ "Route-map for filtering exported routes\n" "route map name\n")
+{
+ VTY_DECLVAR_CONTEXT(bgp, bgp);
+ VTY_DECLVAR_CONTEXT_SUB(rfapi_nve_group_cfg, rfg);
+
+ VNC_VTY_CONFIG_CHECK(bgp);
+
+ /* make sure it's still in list */
+ if (!listnode_lookup(bgp->rfapi_cfg->nve_groups_sequential, rfg)) {
+ /* Not in list anymore */
+ vty_out(vty, "Current NVE group no longer exists\n");
+ return CMD_WARNING_CONFIG_FAILED;
+ }
+
+ vnc_redistribute_prechange(bgp);
+
+ if (rfg->routemap_redist_name[ZEBRA_ROUTE_BGP_DIRECT])
+ free(rfg->routemap_redist_name[ZEBRA_ROUTE_BGP_DIRECT]);
+ route_map_counter_decrement(
+ rfg->routemap_redist[ZEBRA_ROUTE_BGP_DIRECT]);
+ rfg->routemap_redist_name[ZEBRA_ROUTE_BGP_DIRECT] =
+ strdup(argv[3]->arg);
+ rfg->routemap_redist[ZEBRA_ROUTE_BGP_DIRECT] =
+ route_map_lookup_by_name(argv[3]->arg);
+ route_map_counter_increment(
+ rfg->routemap_redist[ZEBRA_ROUTE_BGP_DIRECT]);
+
+ vnc_redistribute_postchange(bgp);
+
+ return CMD_SUCCESS;
+}
+
+/*-- redist policy, nvegroup end --*/
+
+/*-------------------------------------------------------------------------
+ * export
+ *-----------------------------------------------------------------------*/
+
+DEFUN (vnc_export_mode,
+ vnc_export_mode_cmd,
+ "vnc export <bgp|zebra> mode <group-nve|ce|none|registering-nve>",
+ VNC_CONFIG_STR
+ "Export to other protocols\n"
+ "Export to BGP\n"
+ "Export to Zebra (experimental)\n"
+ "Select export mode\n"
+ "Export routes with nve-group next-hops\n"
+ "Export routes with NVE connected router next-hops\n"
+ "Disable export\n" "Export routes with registering NVE as next-hop\n")
+{
+ VTY_DECLVAR_CONTEXT(bgp, bgp);
+ uint32_t oldmode = 0;
+ uint32_t newmode = 0;
+
+ VNC_VTY_CONFIG_CHECK(bgp);
+
+ if (argv[2]->arg[0] == 'b') {
+ oldmode = bgp->rfapi_cfg->flags
+ & BGP_VNC_CONFIG_EXPORT_BGP_MODE_BITS;
+ switch (argv[4]->arg[0]) {
+ case 'g':
+ newmode = BGP_VNC_CONFIG_EXPORT_BGP_MODE_GRP;
+ break;
+ case 'c':
+ newmode = BGP_VNC_CONFIG_EXPORT_BGP_MODE_CE;
+ break;
+ case 'n':
+ newmode = 0;
+ break;
+ case 'r':
+ newmode = BGP_VNC_CONFIG_EXPORT_BGP_MODE_RH;
+ break;
+ default:
+ vty_out(vty, "Invalid mode specified\n");
+ return CMD_WARNING_CONFIG_FAILED;
+ }
+
+ if (newmode == oldmode) {
+ vty_out(vty, "Mode unchanged\n");
+ return CMD_SUCCESS;
+ }
+
+ vnc_export_bgp_prechange(bgp);
+
+ bgp->rfapi_cfg->flags &= ~BGP_VNC_CONFIG_EXPORT_BGP_MODE_BITS;
+ bgp->rfapi_cfg->flags |= newmode;
+
+ vnc_export_bgp_postchange(bgp);
+
+
+ } else {
+ /*
+ * export to zebra with RH mode is not yet implemented
+ */
+ vty_out(vty,
+ "Changing modes for zebra export not implemented yet\n");
+ return CMD_WARNING_CONFIG_FAILED;
+ }
+
+ return CMD_SUCCESS;
+}
+
+static struct rfapi_rfg_name *rfgn_new(void)
+{
+ return XCALLOC(MTYPE_RFAPI_RFG_NAME, sizeof(struct rfapi_rfg_name));
+}
+
+static void rfgn_free(struct rfapi_rfg_name *rfgn)
+{
+ XFREE(MTYPE_RFAPI_RFG_NAME, rfgn);
+}
+
+DEFUN (vnc_export_nvegroup,
+ vnc_export_nvegroup_cmd,
+ "vnc export <bgp|zebra> group-nve group NAME",
+ VNC_CONFIG_STR
+ "Export to other protocols\n"
+ "Export to BGP\n"
+ "Export to Zebra (experimental)\n"
+ "NVE group, used in 'group-nve' export mode\n"
+ "NVE group\n" "Group name\n")
+{
+ VTY_DECLVAR_CONTEXT(bgp, bgp);
+ struct rfapi_nve_group_cfg *rfg_new;
+
+ VNC_VTY_CONFIG_CHECK(bgp);
+
+ rfg_new = bgp_rfapi_cfg_match_byname(bgp, argv[5]->arg,
+ RFAPI_GROUP_CFG_NVE);
+ if (rfg_new == NULL) {
+ rfg_new = bgp_rfapi_cfg_match_byname(bgp, argv[5]->arg,
+ RFAPI_GROUP_CFG_VRF);
+ if (rfg_new)
+ vnc_add_vrf_opener(bgp, rfg_new);
+ }
+
+ if (rfg_new == NULL) {
+ vty_out(vty, "Can't find group named \"%s\".\n", argv[5]->arg);
+ return CMD_WARNING_CONFIG_FAILED;
+ }
+
+ if (argv[2]->arg[0] == 'b') {
+
+ struct listnode *node;
+ struct rfapi_rfg_name *rfgn;
+
+ /*
+ * Set group for export to BGP Direct
+ */
+
+ /* see if group is already included in export list */
+ for (ALL_LIST_ELEMENTS_RO(
+ bgp->rfapi_cfg->rfg_export_direct_bgp_l, node,
+ rfgn)) {
+
+ if (!strcmp(rfgn->name, argv[5]->arg)) {
+ /* already in the list: we're done */
+ return CMD_SUCCESS;
+ }
+ }
+
+ rfgn = rfgn_new();
+ rfgn->name = strdup(argv[5]->arg);
+ rfgn->rfg = rfg_new; /* OK if not set yet */
+
+ listnode_add(bgp->rfapi_cfg->rfg_export_direct_bgp_l, rfgn);
+
+ vnc_zlog_debug_verbose("%s: testing rfg_new", __func__);
+ if (rfg_new) {
+ vnc_zlog_debug_verbose(
+ "%s: testing bgp grp mode enabled", __func__);
+ if (VNC_EXPORT_BGP_GRP_ENABLED(bgp->rfapi_cfg))
+ vnc_zlog_debug_verbose(
+ "%s: calling vnc_direct_bgp_add_group",
+ __func__);
+ vnc_direct_bgp_add_group(bgp, rfg_new);
+ }
+
+ } else {
+
+ struct listnode *node;
+ struct rfapi_rfg_name *rfgn;
+
+ /*
+ * Set group for export to Zebra
+ */
+
+ /* see if group is already included in export list */
+ for (ALL_LIST_ELEMENTS_RO(bgp->rfapi_cfg->rfg_export_zebra_l,
+ node, rfgn)) {
+
+ if (!strcmp(rfgn->name, argv[5]->arg)) {
+ /* already in the list: we're done */
+ return CMD_SUCCESS;
+ }
+ }
+
+ rfgn = rfgn_new();
+ rfgn->name = strdup(argv[5]->arg);
+ rfgn->rfg = rfg_new; /* OK if not set yet */
+
+ listnode_add(bgp->rfapi_cfg->rfg_export_zebra_l, rfgn);
+
+ if (rfg_new) {
+ if (VNC_EXPORT_ZEBRA_GRP_ENABLED(bgp->rfapi_cfg))
+ vnc_zebra_add_group(bgp, rfg_new);
+ }
+ }
+
+ return CMD_SUCCESS;
+}
+
+/*
+ * This command applies to routes exported from VNC to BGP directly
+ * without going though zebra
+ */
+DEFUN (vnc_no_export_nvegroup,
+ vnc_no_export_nvegroup_cmd,
+ "vnc export <bgp|zebra> group-nve no group NAME",
+ VNC_CONFIG_STR
+ "Export to other protocols\n"
+ "Export to BGP\n"
+ "Export to Zebra (experimental)\n"
+ "NVE group, used in 'group-nve' export mode\n"
+ "Disable export of VNC routes\n" "NVE group\n" "Group name\n")
+{
+ VTY_DECLVAR_CONTEXT(bgp, bgp);
+ struct listnode *node, *nnode;
+ struct rfapi_rfg_name *rfgn;
+
+ VNC_VTY_CONFIG_CHECK(bgp);
+
+ if (argv[2]->arg[0] == 'b') {
+ for (ALL_LIST_ELEMENTS(bgp->rfapi_cfg->rfg_export_direct_bgp_l,
+ node, nnode, rfgn)) {
+
+ if (rfgn->name && !strcmp(rfgn->name, argv[6]->arg)) {
+ vnc_zlog_debug_verbose("%s: matched \"%s\"",
+ __func__, rfgn->name);
+ if (rfgn->rfg)
+ vnc_direct_bgp_del_group(bgp,
+ rfgn->rfg);
+ free(rfgn->name);
+ list_delete_node(
+ bgp->rfapi_cfg->rfg_export_direct_bgp_l,
+ node);
+ rfgn_free(rfgn);
+ break;
+ }
+ }
+ } else {
+ for (ALL_LIST_ELEMENTS(bgp->rfapi_cfg->rfg_export_zebra_l, node,
+ nnode, rfgn)) {
+
+ vnc_zlog_debug_verbose("does rfg \"%s\" match?",
+ rfgn->name);
+ if (rfgn->name && !strcmp(rfgn->name, argv[6]->arg)) {
+ if (rfgn->rfg)
+ vnc_zebra_del_group(bgp, rfgn->rfg);
+ free(rfgn->name);
+ list_delete_node(
+ bgp->rfapi_cfg->rfg_export_zebra_l,
+ node);
+ rfgn_free(rfgn);
+ break;
+ }
+ }
+ }
+ return CMD_SUCCESS;
+}
+
+DEFUN (vnc_nve_group_export_no_prefixlist,
+ vnc_nve_group_export_no_prefixlist_cmd,
+ "no export <bgp|zebra> <ipv4|ipv6> prefix-list [NAME]",
+ NO_STR
+ "Export to other protocols\n"
+ "Export to BGP\n"
+ "Export to Zebra (experimental)\n"
+ "IPv4 routes\n"
+ "IPv6 routes\n"
+ "Prefix-list for filtering exported routes\n" "prefix list name\n")
+{
+ VTY_DECLVAR_CONTEXT(bgp, bgp);
+ VTY_DECLVAR_CONTEXT_SUB(rfapi_nve_group_cfg, rfg);
+ int idx = 0;
+ int is_bgp = 1;
+ afi_t afi;
+
+ VNC_VTY_CONFIG_CHECK(bgp);
+
+ /* make sure it's still in list */
+ if (!listnode_lookup(bgp->rfapi_cfg->nve_groups_sequential, rfg)) {
+ /* Not in list anymore */
+ vty_out(vty, "Current NVE group no longer exists\n");
+ return CMD_WARNING_CONFIG_FAILED;
+ }
+
+ if (!argv_find_and_parse_afi(argv, argc, &idx, &afi)) {
+ vty_out(vty, "%% Malformed Address Family\n");
+ return CMD_WARNING_CONFIG_FAILED;
+ }
+
+ if (argv[idx - 1]->text[0] == 'z')
+ is_bgp = 0;
+ idx += 2; /* skip afi and keyword */
+
+ if (is_bgp) {
+ if (idx == argc
+ || (rfg->plist_export_bgp_name[afi]
+ && strmatch(argv[idx]->arg,
+ rfg->plist_export_bgp_name[afi]))) {
+ if (rfg->plist_export_bgp_name[afi])
+ free(rfg->plist_export_bgp_name[afi]);
+ rfg->plist_export_bgp_name[afi] = NULL;
+ rfg->plist_export_bgp[afi] = NULL;
+
+ vnc_direct_bgp_reexport_group_afi(bgp, rfg, afi);
+ }
+ } else {
+ if (idx == argc
+ || (rfg->plist_export_zebra_name[afi]
+ && strmatch(argv[idx]->arg,
+ rfg->plist_export_zebra_name[afi]))) {
+ if (rfg->plist_export_zebra_name[afi])
+ free(rfg->plist_export_zebra_name[afi]);
+ rfg->plist_export_zebra_name[afi] = NULL;
+ rfg->plist_export_zebra[afi] = NULL;
+
+ vnc_zebra_reexport_group_afi(bgp, rfg, afi);
+ }
+ }
+ return CMD_SUCCESS;
+}
+
+ALIAS (vnc_nve_group_export_no_prefixlist,
+ vnc_vrf_policy_export_no_prefixlist_cmd,
+ "no export <ipv4|ipv6> prefix-list [NAME]",
+ NO_STR
+ "Export to VRF\n"
+ "IPv4 routes\n"
+ "IPv6 routes\n"
+ "Prefix-list for filtering exported routes\n" "prefix list name\n")
+
+DEFUN (vnc_nve_group_export_prefixlist,
+ vnc_nve_group_export_prefixlist_cmd,
+ "export <bgp|zebra> <ipv4|ipv6> prefix-list NAME",
+ "Export to other protocols\n"
+ "Export to BGP\n"
+ "Export to Zebra (experimental)\n"
+ "IPv4 routes\n"
+ "IPv6 routes\n"
+ "Prefix-list for filtering exported routes\n" "prefix list name\n")
+{
+ VTY_DECLVAR_CONTEXT(bgp, bgp);
+ VTY_DECLVAR_CONTEXT_SUB(rfapi_nve_group_cfg, rfg);
+ int idx = 0;
+ int is_bgp = 1;
+ afi_t afi;
+
+ VNC_VTY_CONFIG_CHECK(bgp);
+
+ /* make sure it's still in list */
+ if (!listnode_lookup(bgp->rfapi_cfg->nve_groups_sequential, rfg)) {
+ /* Not in list anymore */
+ vty_out(vty, "Current NVE group no longer exists\n");
+ return CMD_WARNING_CONFIG_FAILED;
+ }
+
+ if (!argv_find_and_parse_afi(argv, argc, &idx, &afi)) {
+ vty_out(vty, "%% Malformed Address Family\n");
+ return CMD_WARNING_CONFIG_FAILED;
+ }
+
+ if (argv[idx - 1]->text[0] == 'z')
+ is_bgp = 0;
+ idx = argc - 1;
+
+ if (is_bgp) {
+ if (rfg->plist_export_bgp_name[afi])
+ free(rfg->plist_export_bgp_name[afi]);
+ rfg->plist_export_bgp_name[afi] = strdup(argv[idx]->arg);
+ rfg->plist_export_bgp[afi] =
+ prefix_list_lookup(afi, argv[idx]->arg);
+
+ vnc_direct_bgp_reexport_group_afi(bgp, rfg, afi);
+
+ } else {
+ if (rfg->plist_export_zebra_name[afi])
+ free(rfg->plist_export_zebra_name[afi]);
+ rfg->plist_export_zebra_name[afi] = strdup(argv[idx]->arg);
+ rfg->plist_export_zebra[afi] =
+ prefix_list_lookup(afi, argv[idx]->arg);
+
+ vnc_zebra_reexport_group_afi(bgp, rfg, afi);
+ }
+ return CMD_SUCCESS;
+}
+
+ALIAS (vnc_nve_group_export_prefixlist,
+ vnc_vrf_policy_export_prefixlist_cmd,
+ "export <ipv4|ipv6> prefix-list NAME",
+ "Export to VRF\n"
+ "IPv4 routes\n"
+ "IPv6 routes\n"
+ "Prefix-list for filtering exported routes\n" "prefix list name\n")
+
+DEFUN (vnc_nve_group_export_no_routemap,
+ vnc_nve_group_export_no_routemap_cmd,
+ "no export <bgp|zebra> route-map [NAME]",
+ NO_STR
+ "Export to other protocols\n"
+ "Export to BGP\n"
+ "Export to Zebra (experimental)\n"
+ "Route-map for filtering exported routes\n" "route map name\n")
+{
+ VTY_DECLVAR_CONTEXT(bgp, bgp);
+ VTY_DECLVAR_CONTEXT_SUB(rfapi_nve_group_cfg, rfg);
+ int idx = 2;
+ int is_bgp = 1;
+
+ VNC_VTY_CONFIG_CHECK(bgp);
+
+ /* make sure it's still in list */
+ if (!listnode_lookup(bgp->rfapi_cfg->nve_groups_sequential, rfg)) {
+ /* Not in list anymore */
+ vty_out(vty, "Current NVE group no longer exists\n");
+ return CMD_WARNING_CONFIG_FAILED;
+ }
+ switch (argv[idx]->text[0]) {
+ case 'z':
+ is_bgp = 0;
+ /* fall thru */
+ case 'b':
+ idx += 2;
+ break;
+ default: /* route-map */
+ idx++;
+ break;
+ }
+
+ if (is_bgp) {
+ if (idx == argc
+ || (rfg->routemap_export_bgp_name
+ && strmatch(argv[idx]->arg,
+ rfg->routemap_export_bgp_name))) {
+ if (rfg->routemap_export_bgp_name)
+ free(rfg->routemap_export_bgp_name);
+ route_map_counter_decrement(rfg->routemap_export_bgp);
+ rfg->routemap_export_bgp_name = NULL;
+ rfg->routemap_export_bgp = NULL;
+
+ vnc_direct_bgp_reexport_group_afi(bgp, rfg, AFI_IP);
+ vnc_direct_bgp_reexport_group_afi(bgp, rfg, AFI_IP6);
+ }
+ } else {
+ if (idx == argc
+ || (rfg->routemap_export_zebra_name
+ && strmatch(argv[idx]->arg,
+ rfg->routemap_export_zebra_name))) {
+ if (rfg->routemap_export_zebra_name)
+ free(rfg->routemap_export_zebra_name);
+ route_map_counter_decrement(rfg->routemap_export_zebra);
+ rfg->routemap_export_zebra_name = NULL;
+ rfg->routemap_export_zebra = NULL;
+
+ vnc_zebra_reexport_group_afi(bgp, rfg, AFI_IP);
+ vnc_zebra_reexport_group_afi(bgp, rfg, AFI_IP6);
+ }
+ }
+ return CMD_SUCCESS;
+}
+
+ALIAS (vnc_nve_group_export_no_routemap,
+ vnc_vrf_policy_export_no_routemap_cmd,
+ "no export route-map [NAME]",
+ NO_STR
+ "Export to VRF\n"
+ "Route-map for filtering exported routes\n" "route map name\n")
+
+DEFUN (vnc_nve_group_export_routemap,
+ vnc_nve_group_export_routemap_cmd,
+ "export <bgp|zebra> route-map NAME",
+ "Export to other protocols\n"
+ "Export to BGP\n"
+ "Export to Zebra (experimental)\n"
+ "Route-map for filtering exported routes\n" "route map name\n")
+{
+ VTY_DECLVAR_CONTEXT(bgp, bgp);
+ VTY_DECLVAR_CONTEXT_SUB(rfapi_nve_group_cfg, rfg);
+ int idx = 0;
+ int is_bgp = 1;
+
+ VNC_VTY_CONFIG_CHECK(bgp);
+
+ /* make sure it's still in list */
+ if (!listnode_lookup(bgp->rfapi_cfg->nve_groups_sequential, rfg)) {
+ /* Not in list anymore */
+ vty_out(vty, "Current NVE group no longer exists\n");
+ return CMD_WARNING_CONFIG_FAILED;
+ }
+
+ if (argv[1]->text[0] == 'z')
+ is_bgp = 0;
+ idx = argc - 1;
+
+ if (is_bgp) {
+ if (rfg->routemap_export_bgp_name)
+ free(rfg->routemap_export_bgp_name);
+ route_map_counter_decrement(rfg->routemap_export_bgp);
+ rfg->routemap_export_bgp_name = strdup(argv[idx]->arg);
+ rfg->routemap_export_bgp =
+ route_map_lookup_by_name(argv[idx]->arg);
+ route_map_counter_increment(rfg->routemap_export_bgp);
+ vnc_direct_bgp_reexport_group_afi(bgp, rfg, AFI_IP);
+ vnc_direct_bgp_reexport_group_afi(bgp, rfg, AFI_IP6);
+ } else {
+ if (rfg->routemap_export_zebra_name)
+ free(rfg->routemap_export_zebra_name);
+ route_map_counter_decrement(rfg->routemap_export_zebra);
+ rfg->routemap_export_zebra_name = strdup(argv[idx]->arg);
+ rfg->routemap_export_zebra =
+ route_map_lookup_by_name(argv[idx]->arg);
+ route_map_counter_increment(rfg->routemap_export_zebra);
+ vnc_zebra_reexport_group_afi(bgp, rfg, AFI_IP);
+ vnc_zebra_reexport_group_afi(bgp, rfg, AFI_IP6);
+ }
+ return CMD_SUCCESS;
+}
+
+ALIAS (vnc_nve_group_export_routemap,
+ vnc_vrf_policy_export_routemap_cmd,
+ "export route-map NAME",
+ "Export to VRF\n"
+ "Route-map for filtering exported routes\n" "route map name\n")
+
+DEFUN (vnc_nve_export_no_prefixlist,
+ vnc_nve_export_no_prefixlist_cmd,
+ "no vnc export <bgp|zebra> <ipv4|ipv6> prefix-list [NAME]",
+ NO_STR
+ VNC_CONFIG_STR
+ "Export to other protocols\n"
+ "Export to BGP\n"
+ "Export to Zebra (experimental)\n"
+ "IPv4 prefixes\n"
+ "IPv6 prefixes\n"
+ "Prefix-list for filtering exported routes\n" "Prefix list name\n")
+{
+ VTY_DECLVAR_CONTEXT(bgp, bgp);
+ struct rfapi_cfg *hc;
+ afi_t afi;
+
+ VNC_VTY_CONFIG_CHECK(bgp);
+ hc = bgp->rfapi_cfg;
+
+ if (strmatch(argv[4]->text, "ipv4")) {
+ afi = AFI_IP;
+ } else {
+ afi = AFI_IP6;
+ }
+
+ if (argv[3]->arg[0] == 'b') {
+ if (((argc > 6) && hc->plist_export_bgp_name[afi]
+ && strmatch(argv[6]->text, hc->plist_export_bgp_name[afi]))
+ || (argc <= 6)) {
+
+ free(hc->plist_export_bgp_name[afi]);
+ hc->plist_export_bgp_name[afi] = NULL;
+ hc->plist_export_bgp[afi] = NULL;
+ vnc_direct_bgp_reexport(bgp, afi);
+ }
+ } else {
+ if (((argc > 6) && hc->plist_export_zebra_name[afi]
+ && strmatch(argv[6]->text,
+ hc->plist_export_zebra_name[afi]))
+ || (argc <= 6)) {
+
+ free(hc->plist_export_zebra_name[afi]);
+ hc->plist_export_zebra_name[afi] = NULL;
+ hc->plist_export_zebra[afi] = NULL;
+ /* TBD vnc_zebra_rh_reexport(bgp, afi); */
+ }
+ }
+ return CMD_SUCCESS;
+}
+
+DEFUN (vnc_nve_export_prefixlist,
+ vnc_nve_export_prefixlist_cmd,
+ "vnc export <bgp|zebra> <ipv4|ipv6> prefix-list NAME",
+ VNC_CONFIG_STR
+ "Export to other protocols\n"
+ "Export to BGP\n"
+ "Export to Zebra (experimental)\n"
+ "IPv4 prefixes\n"
+ "IPv6 prefixes\n"
+ "Prefix-list for filtering exported routes\n" "Prefix list name\n")
+{
+ VTY_DECLVAR_CONTEXT(bgp, bgp);
+ struct rfapi_cfg *hc;
+ afi_t afi;
+
+ VNC_VTY_CONFIG_CHECK(bgp);
+ hc = bgp->rfapi_cfg;
+
+ if (strmatch(argv[3]->text, "ipv4")) {
+ afi = AFI_IP;
+ } else {
+ afi = AFI_IP6;
+ }
+
+ if (argv[2]->arg[0] == 'b') {
+ if (hc->plist_export_bgp_name[afi])
+ free(hc->plist_export_bgp_name[afi]);
+ hc->plist_export_bgp_name[afi] = strdup(argv[5]->arg);
+ hc->plist_export_bgp[afi] =
+ prefix_list_lookup(afi, argv[5]->arg);
+ vnc_direct_bgp_reexport(bgp, afi);
+ } else {
+ if (hc->plist_export_zebra_name[afi])
+ free(hc->plist_export_zebra_name[afi]);
+ hc->plist_export_zebra_name[afi] = strdup(argv[5]->arg);
+ hc->plist_export_zebra[afi] =
+ prefix_list_lookup(afi, argv[5]->arg);
+ /* TBD vnc_zebra_rh_reexport(bgp, afi); */
+ }
+ return CMD_SUCCESS;
+}
+
+DEFUN (vnc_nve_export_no_routemap,
+ vnc_nve_export_no_routemap_cmd,
+ "no vnc export <bgp|zebra> route-map [NAME]",
+ NO_STR
+ VNC_CONFIG_STR
+ "Export to other protocols\n"
+ "Export to BGP\n"
+ "Export to Zebra (experimental)\n"
+ "Route-map for filtering exported routes\n" "Route map name\n")
+{
+ VTY_DECLVAR_CONTEXT(bgp, bgp);
+ struct rfapi_cfg *hc;
+
+ VNC_VTY_CONFIG_CHECK(bgp);
+ hc = bgp->rfapi_cfg;
+
+ if (argv[3]->arg[0] == 'b') {
+ if (((argc > 5) && hc->routemap_export_bgp_name
+ && strmatch(argv[5]->text, hc->routemap_export_bgp_name))
+ || (argc <= 5)) {
+
+ free(hc->routemap_export_bgp_name);
+ route_map_counter_decrement(hc->routemap_export_bgp);
+ hc->routemap_export_bgp_name = NULL;
+ hc->routemap_export_bgp = NULL;
+ vnc_direct_bgp_reexport(bgp, AFI_IP);
+ vnc_direct_bgp_reexport(bgp, AFI_IP6);
+ }
+ } else {
+ if (((argc > 5) && hc->routemap_export_zebra_name
+ && strmatch(argv[5]->text, hc->routemap_export_zebra_name))
+ || (argc <= 5)) {
+
+ free(hc->routemap_export_zebra_name);
+ route_map_counter_decrement(hc->routemap_export_zebra);
+ hc->routemap_export_zebra_name = NULL;
+ hc->routemap_export_zebra = NULL;
+ /* TBD vnc_zebra_rh_reexport(bgp, AFI_IP); */
+ /* TBD vnc_zebra_rh_reexport(bgp, AFI_IP6); */
+ }
+ }
+ return CMD_SUCCESS;
+}
+
+DEFUN (vnc_nve_export_routemap,
+ vnc_nve_export_routemap_cmd,
+ "vnc export <bgp|zebra> route-map NAME",
+ VNC_CONFIG_STR
+ "Export to other protocols\n"
+ "Export to BGP\n"
+ "Export to Zebra (experimental)\n"
+ "Route-map for filtering exported routes\n" "Route map name\n")
+{
+ VTY_DECLVAR_CONTEXT(bgp, bgp);
+ struct rfapi_cfg *hc;
+
+ VNC_VTY_CONFIG_CHECK(bgp);
+ hc = bgp->rfapi_cfg;
+
+ if (argv[2]->arg[0] == 'b') {
+ if (hc->routemap_export_bgp_name)
+ free(hc->routemap_export_bgp_name);
+ route_map_counter_decrement(hc->routemap_export_bgp);
+ hc->routemap_export_bgp_name = strdup(argv[4]->arg);
+ hc->routemap_export_bgp =
+ route_map_lookup_by_name(argv[4]->arg);
+ route_map_counter_increment(hc->routemap_export_bgp);
+ vnc_direct_bgp_reexport(bgp, AFI_IP);
+ vnc_direct_bgp_reexport(bgp, AFI_IP6);
+ } else {
+ if (hc->routemap_export_zebra_name)
+ free(hc->routemap_export_zebra_name);
+ route_map_counter_decrement(hc->routemap_export_zebra);
+ hc->routemap_export_zebra_name = strdup(argv[4]->arg);
+ hc->routemap_export_zebra =
+ route_map_lookup_by_name(argv[4]->arg);
+ route_map_counter_increment(hc->routemap_export_zebra);
+ /* TBD vnc_zebra_rh_reexport(bgp, AFI_IP); */
+ /* TBD vnc_zebra_rh_reexport(bgp, AFI_IP6); */
+ }
+ return CMD_SUCCESS;
+}
+
+
+/*
+ * respond to changes in the global prefix list configuration
+ */
+void vnc_prefix_list_update(struct bgp *bgp)
+{
+ afi_t afi;
+ struct listnode *n;
+ struct rfapi_nve_group_cfg *rfg;
+ struct rfapi_cfg *hc;
+ int i;
+
+ if (!bgp) {
+ vnc_zlog_debug_verbose("%s: No BGP process is configured",
+ __func__);
+ return;
+ }
+
+ if (!(hc = bgp->rfapi_cfg)) {
+ vnc_zlog_debug_verbose("%s: rfapi not configured", __func__);
+ return;
+ }
+
+ for (afi = AFI_IP; afi < AFI_MAX; afi++) {
+ /*
+ * Loop over nve groups
+ */
+ for (ALL_LIST_ELEMENTS_RO(bgp->rfapi_cfg->nve_groups_sequential,
+ n, rfg)) {
+
+ if (rfg->plist_export_bgp_name[afi]) {
+ rfg->plist_export_bgp[afi] = prefix_list_lookup(
+ afi, rfg->plist_export_bgp_name[afi]);
+ }
+ if (rfg->plist_export_zebra_name[afi]) {
+ rfg->plist_export_zebra
+ [afi] = prefix_list_lookup(
+ afi, rfg->plist_export_zebra_name[afi]);
+ }
+ for (i = 0; i < ZEBRA_ROUTE_MAX; ++i) {
+ if (rfg->plist_redist_name[i][afi]) {
+ rfg->plist_redist
+ [i][afi] = prefix_list_lookup(
+ afi,
+ rfg->plist_redist_name[i][afi]);
+ }
+ }
+
+ vnc_direct_bgp_reexport_group_afi(bgp, rfg, afi);
+ /* TBD vnc_zebra_reexport_group_afi(bgp, rfg, afi); */
+ }
+
+ /*
+ * RH config, too
+ */
+ if (hc->plist_export_bgp_name[afi]) {
+ hc->plist_export_bgp[afi] = prefix_list_lookup(
+ afi, hc->plist_export_bgp_name[afi]);
+ }
+ if (hc->plist_export_zebra_name[afi]) {
+ hc->plist_export_zebra[afi] = prefix_list_lookup(
+ afi, hc->plist_export_zebra_name[afi]);
+ }
+
+ for (i = 0; i < ZEBRA_ROUTE_MAX; ++i) {
+ if (hc->plist_redist_name[i][afi]) {
+ hc->plist_redist[i][afi] = prefix_list_lookup(
+ afi, hc->plist_redist_name[i][afi]);
+ }
+ }
+ }
+
+ vnc_direct_bgp_reexport(bgp, AFI_IP);
+ vnc_direct_bgp_reexport(bgp, AFI_IP6);
+
+ /* TBD vnc_zebra_rh_reexport(bgp, AFI_IP); */
+ /* TBD vnc_zebra_rh_reexport(bgp, AFI_IP6); */
+
+ vnc_redistribute_prechange(bgp);
+ vnc_redistribute_postchange(bgp);
+}
+
+/*
+ * respond to changes in the global route map configuration
+ */
+void vnc_routemap_update(struct bgp *bgp, const char *unused)
+{
+ struct listnode *n;
+ struct rfapi_nve_group_cfg *rfg;
+ struct rfapi_cfg *hc;
+ int i;
+ struct route_map *old = NULL;
+
+ vnc_zlog_debug_verbose("%s(arg=%s)", __func__, unused);
+
+ if (!bgp) {
+ vnc_zlog_debug_verbose("%s: No BGP process is configured",
+ __func__);
+ return;
+ }
+
+ if (!(hc = bgp->rfapi_cfg)) {
+ vnc_zlog_debug_verbose("%s: rfapi not configured", __func__);
+ return;
+ }
+
+ /*
+ * Loop over nve groups
+ */
+ for (ALL_LIST_ELEMENTS_RO(bgp->rfapi_cfg->nve_groups_sequential, n,
+ rfg)) {
+
+ if (rfg->routemap_export_bgp_name) {
+ old = rfg->routemap_export_bgp;
+ rfg->routemap_export_bgp = route_map_lookup_by_name(
+ rfg->routemap_export_bgp_name);
+ /* old is NULL. i.e Route map creation event.
+ * So update applied_counter.
+ * If Old is not NULL, i.e It may be routemap
+ * updation or deletion.
+ * So no need to update the counter.
+ */
+ if (!old)
+ route_map_counter_increment(
+ rfg->routemap_export_bgp);
+ }
+ if (rfg->routemap_export_zebra_name) {
+ old = rfg->routemap_export_bgp;
+ rfg->routemap_export_bgp = route_map_lookup_by_name(
+ rfg->routemap_export_zebra_name);
+ if (!old)
+ route_map_counter_increment(
+ rfg->routemap_export_bgp);
+ }
+ for (i = 0; i < ZEBRA_ROUTE_MAX; ++i) {
+ if (rfg->routemap_redist_name[i]) {
+ old = rfg->routemap_redist[i];
+ rfg->routemap_redist[i] =
+ route_map_lookup_by_name(
+ rfg->routemap_redist_name[i]);
+ if (!old)
+ route_map_counter_increment(
+ rfg->routemap_redist[i]);
+ }
+ }
+
+ vnc_direct_bgp_reexport_group_afi(bgp, rfg, AFI_IP);
+ vnc_direct_bgp_reexport_group_afi(bgp, rfg, AFI_IP6);
+ /* TBD vnc_zebra_reexport_group_afi(bgp, rfg, afi); */
+ }
+
+ /*
+ * RH config, too
+ */
+ if (hc->routemap_export_bgp_name) {
+ old = hc->routemap_export_bgp;
+ hc->routemap_export_bgp =
+ route_map_lookup_by_name(hc->routemap_export_bgp_name);
+ if (!old)
+ route_map_counter_increment(hc->routemap_export_bgp);
+ }
+ if (hc->routemap_export_zebra_name) {
+ old = hc->routemap_export_bgp;
+ hc->routemap_export_bgp = route_map_lookup_by_name(
+ hc->routemap_export_zebra_name);
+ if (!old)
+ route_map_counter_increment(hc->routemap_export_bgp);
+ }
+ for (i = 0; i < ZEBRA_ROUTE_MAX; ++i) {
+ if (hc->routemap_redist_name[i]) {
+ old = hc->routemap_redist[i];
+ hc->routemap_redist[i] = route_map_lookup_by_name(
+ hc->routemap_redist_name[i]);
+ if (!old)
+ route_map_counter_increment(
+ hc->routemap_redist[i]);
+ }
+ }
+
+ vnc_direct_bgp_reexport(bgp, AFI_IP);
+ vnc_direct_bgp_reexport(bgp, AFI_IP6);
+
+ /* TBD vnc_zebra_rh_reexport(bgp, AFI_IP); */
+ /* TBD vnc_zebra_rh_reexport(bgp, AFI_IP6); */
+
+ vnc_redistribute_prechange(bgp);
+ vnc_redistribute_postchange(bgp);
+
+ vnc_zlog_debug_verbose("%s done", __func__);
+}
+
+/*-------------------------------------------------------------------------
+ * nve-group
+ *-----------------------------------------------------------------------*/
+
+
+DEFUN_NOSH (vnc_nve_group,
+ vnc_nve_group_cmd,
+ "vnc nve-group NAME",
+ VNC_CONFIG_STR "Configure a NVE group\n" "Group name\n")
+{
+ VTY_DECLVAR_CONTEXT(bgp, bgp);
+ struct rfapi_nve_group_cfg *rfg;
+ struct listnode *node, *nnode;
+ struct rfapi_rfg_name *rfgn;
+
+ VNC_VTY_CONFIG_CHECK(bgp);
+
+ /* Search for name */
+ rfg = bgp_rfapi_cfg_match_byname(bgp, argv[2]->arg,
+ RFAPI_GROUP_CFG_NVE);
+
+ if (!rfg) {
+ rfg = rfapi_group_new(bgp, RFAPI_GROUP_CFG_NVE, argv[2]->arg);
+ if (!rfg) {
+ /* Error out of memory */
+ vty_out(vty, "Can't allocate memory for NVE group\n");
+ return CMD_WARNING_CONFIG_FAILED;
+ }
+
+ /* Copy defaults from struct rfapi_cfg */
+ rfg->rd = bgp->rfapi_cfg->default_rd;
+ if (bgp->rfapi_cfg->flags & BGP_VNC_CONFIG_L2RD) {
+ rfg->l2rd = bgp->rfapi_cfg->default_l2rd;
+ rfg->flags |= RFAPI_RFG_L2RD;
+ }
+ rfg->rd = bgp->rfapi_cfg->default_rd;
+ rfg->response_lifetime =
+ bgp->rfapi_cfg->default_response_lifetime;
+
+ if (bgp->rfapi_cfg->default_rt_export_list) {
+ rfg->rt_export_list = ecommunity_dup(
+ bgp->rfapi_cfg->default_rt_export_list);
+ }
+
+ if (bgp->rfapi_cfg->default_rt_import_list) {
+ rfg->rt_import_list = ecommunity_dup(
+ bgp->rfapi_cfg->default_rt_import_list);
+ rfg->rfapi_import_table = rfapiImportTableRefAdd(
+ bgp, rfg->rt_import_list, rfg);
+ }
+
+ /*
+ * If a redist nve group was named but the group was not
+ * defined,
+ * make the linkage now
+ */
+ if (!bgp->rfapi_cfg->rfg_redist) {
+ if (bgp->rfapi_cfg->rfg_redist_name
+ && !strcmp(bgp->rfapi_cfg->rfg_redist_name,
+ rfg->name)) {
+
+ vnc_redistribute_prechange(bgp);
+ bgp->rfapi_cfg->rfg_redist = rfg;
+ vnc_redistribute_postchange(bgp);
+ }
+ }
+
+ /*
+ * Same treatment for bgp-direct export group
+ */
+ for (ALL_LIST_ELEMENTS(bgp->rfapi_cfg->rfg_export_direct_bgp_l,
+ node, nnode, rfgn)) {
+
+ if (!strcmp(rfgn->name, rfg->name)) {
+ rfgn->rfg = rfg;
+ vnc_direct_bgp_add_group(bgp, rfg);
+ break;
+ }
+ }
+
+ /*
+ * Same treatment for zebra export group
+ */
+ for (ALL_LIST_ELEMENTS(bgp->rfapi_cfg->rfg_export_zebra_l, node,
+ nnode, rfgn)) {
+
+ vnc_zlog_debug_verbose(
+ "%s: ezport zebra: checking if \"%s\" == \"%s\"",
+ __func__, rfgn->name, rfg->name);
+ if (!strcmp(rfgn->name, rfg->name)) {
+ rfgn->rfg = rfg;
+ vnc_zebra_add_group(bgp, rfg);
+ break;
+ }
+ }
+ }
+
+ /*
+ * XXX subsequent calls will need to make sure this item is still
+ * in the linked list and has the same name
+ */
+ VTY_PUSH_CONTEXT_SUB(BGP_VNC_NVE_GROUP_NODE, rfg);
+
+ return CMD_SUCCESS;
+}
+
+static void bgp_rfapi_delete_nve_group(struct vty *vty, /* NULL = no output */
+ struct bgp *bgp,
+ struct rfapi_nve_group_cfg *rfg)
+{
+ struct list *orphaned_nves = NULL;
+ struct listnode *node, *nnode;
+
+ /*
+ * If there are currently-open NVEs that belong to this group,
+ * zero out their references to this group structure.
+ */
+ if (rfg->nves) {
+ struct rfapi_descriptor *rfd;
+ orphaned_nves = list_new();
+ while ((rfd = listnode_head(rfg->nves))) {
+ rfd->rfg = NULL;
+ listnode_delete(rfg->nves, rfd);
+ listnode_add(orphaned_nves, rfd);
+ }
+ list_delete(&rfg->nves);
+ }
+
+ /* delete it */
+ free(rfg->name);
+ if (rfg->rfapi_import_table)
+ rfapiImportTableRefDelByIt(bgp, rfg->rfapi_import_table);
+ if (rfg->rt_import_list)
+ ecommunity_free(&rfg->rt_import_list);
+ if (rfg->rt_export_list)
+ ecommunity_free(&rfg->rt_export_list);
+
+ if (rfg->vn_node) {
+ rfg->vn_node->info = NULL;
+ agg_unlock_node(rfg->vn_node); /* frees */
+ }
+ if (rfg->un_node) {
+ rfg->un_node->info = NULL;
+ agg_unlock_node(rfg->un_node); /* frees */
+ }
+ if (rfg->rfp_cfg)
+ XFREE(MTYPE_RFAPI_RFP_GROUP_CFG, rfg->rfp_cfg);
+ listnode_delete(bgp->rfapi_cfg->nve_groups_sequential, rfg);
+
+ QOBJ_UNREG(rfg);
+ XFREE(MTYPE_RFAPI_GROUP_CFG, rfg);
+
+ /*
+ * Attempt to reassign the orphaned nves to a new group. If
+ * a NVE can not be reassigned, its rfd->rfg will remain NULL
+ * and it will become a zombie until released by rfapi_close().
+ */
+ if (orphaned_nves) {
+ struct rfapi_descriptor *rfd;
+
+ for (ALL_LIST_ELEMENTS(orphaned_nves, node, nnode, rfd)) {
+ /*
+ * 1. rfapi_close() equivalent except:
+ * a. don't free original descriptor
+ * b. remember query list
+ * c. remember advertised route list
+ * 2. rfapi_open() equivalent except:
+ * a. reuse original descriptor
+ * 3. rfapi_register() on remembered advertised route
+ * list
+ * 4. rfapi_query on rememebred query list
+ */
+
+ int rc;
+
+ rc = rfapi_reopen(rfd, bgp);
+
+ if (!rc) {
+ list_delete_node(orphaned_nves, node);
+ if (vty)
+ vty_out(vty,
+ "WARNING: reassigned NVE vn=");
+ rfapiPrintRfapiIpAddr(vty, &rfd->vn_addr);
+ if (vty)
+ vty_out(vty, " un=");
+ rfapiPrintRfapiIpAddr(vty, &rfd->un_addr);
+ if (vty)
+ vty_out(vty, " to new group \"%s\"\n",
+ rfd->rfg->name);
+ }
+ }
+
+ for (ALL_LIST_ELEMENTS_RO(orphaned_nves, node, rfd)) {
+ if (vty)
+ vty_out(vty, "WARNING: orphaned NVE vn=");
+ rfapiPrintRfapiIpAddr(vty, &rfd->vn_addr);
+ if (vty)
+ vty_out(vty, " un=");
+ rfapiPrintRfapiIpAddr(vty, &rfd->un_addr);
+ if (vty)
+ vty_out(vty, "\n");
+ }
+ list_delete(&orphaned_nves);
+ }
+}
+
+static int
+bgp_rfapi_delete_named_nve_group(struct vty *vty, /* NULL = no output */
+ struct bgp *bgp,
+ const char *rfg_name, /* NULL = any */
+ rfapi_group_cfg_type_t type) /* _MAX = any */
+{
+ struct rfapi_nve_group_cfg *rfg = NULL;
+ struct listnode *node, *nnode;
+ struct rfapi_rfg_name *rfgn;
+
+ /* Search for name */
+ if (rfg_name) {
+ rfg = bgp_rfapi_cfg_match_byname(bgp, rfg_name, type);
+ if (!rfg) {
+ if (vty)
+ vty_out(vty, "No NVE group named \"%s\"\n",
+ rfg_name);
+ return CMD_WARNING_CONFIG_FAILED;
+ }
+ }
+
+ /*
+ * If this group is the redist nve group, unlink it
+ */
+ if (rfg_name == NULL || bgp->rfapi_cfg->rfg_redist == rfg) {
+ vnc_redistribute_prechange(bgp);
+ bgp->rfapi_cfg->rfg_redist = NULL;
+ vnc_redistribute_postchange(bgp);
+ }
+
+
+ /*
+ * remove reference from bgp direct export list
+ */
+ for (ALL_LIST_ELEMENTS_RO(bgp->rfapi_cfg->rfg_export_direct_bgp_l, node,
+ rfgn)) {
+ if (rfgn->rfg == rfg) {
+ rfgn->rfg = NULL;
+ /* remove exported routes from this group */
+ vnc_direct_bgp_del_group(bgp, rfg);
+ break;
+ }
+ }
+
+ /*
+ * remove reference from zebra export list
+ */
+ for (ALL_LIST_ELEMENTS_RO(bgp->rfapi_cfg->rfg_export_zebra_l, node,
+ rfgn)) {
+ if (rfgn->rfg == rfg) {
+ rfgn->rfg = NULL;
+ /* remove exported routes from this group */
+ vnc_zebra_del_group(bgp, rfg);
+ break;
+ }
+ }
+ if (rfg) {
+ if (rfg->rfd)
+ clear_vnc_vrf_closer(rfg);
+ bgp_rfapi_delete_nve_group(vty, bgp, rfg);
+ } else /* must be delete all */
+ for (ALL_LIST_ELEMENTS(bgp->rfapi_cfg->nve_groups_sequential,
+ node, nnode, rfg)) {
+ if (rfg->rfd)
+ clear_vnc_vrf_closer(rfg);
+ bgp_rfapi_delete_nve_group(vty, bgp, rfg);
+ }
+ return CMD_SUCCESS;
+}
+
+DEFUN (vnc_no_nve_group,
+ vnc_no_nve_group_cmd,
+ "no vnc nve-group NAME",
+ NO_STR
+ VNC_CONFIG_STR
+ "Configure a NVE group\n"
+ "Group name\n")
+{
+ VTY_DECLVAR_CONTEXT(bgp, bgp);
+
+ return bgp_rfapi_delete_named_nve_group(vty, bgp, argv[3]->arg,
+ RFAPI_GROUP_CFG_NVE);
+}
+
+DEFUN (vnc_nve_group_prefix,
+ vnc_nve_group_prefix_cmd,
+ "prefix <vn|un> <A.B.C.D/M|X:X::X:X/M>",
+ "Specify prefixes matching NVE VN or UN interfaces\n"
+ "VN prefix\n"
+ "UN prefix\n"
+ "IPv4 prefix\n"
+ "IPv6 prefix\n")
+{
+ VTY_DECLVAR_CONTEXT(bgp, bgp);
+ VTY_DECLVAR_CONTEXT_SUB(rfapi_nve_group_cfg, rfg);
+ struct prefix p;
+ afi_t afi;
+ struct agg_table *rt;
+ struct agg_node *rn;
+ int is_un_prefix = 0;
+
+ /* make sure it's still in list */
+ if (!listnode_lookup(bgp->rfapi_cfg->nve_groups_sequential, rfg)) {
+ /* Not in list anymore */
+ vty_out(vty, "Current NVE group no longer exists\n");
+ return CMD_WARNING_CONFIG_FAILED;
+ }
+
+ if (!str2prefix(argv[2]->arg, &p)) {
+ vty_out(vty, "Malformed prefix \"%s\"\n", argv[2]->arg);
+ return CMD_WARNING_CONFIG_FAILED;
+ }
+
+ afi = family2afi(p.family);
+ if (!afi) {
+ vty_out(vty, "Unsupported address family\n");
+ return CMD_WARNING_CONFIG_FAILED;
+ }
+
+ if (argv[1]->arg[0] == 'u') {
+ rt = bgp->rfapi_cfg->nve_groups_un[afi];
+ is_un_prefix = 1;
+ } else {
+ rt = bgp->rfapi_cfg->nve_groups_vn[afi];
+ }
+
+ rn = agg_node_get(rt, &p); /* NB locks node */
+ if (rn->info) {
+ /*
+ * There is already a group with this prefix
+ */
+ agg_unlock_node(rn);
+ if (rn->info != rfg) {
+ /*
+ * different group name: fail
+ */
+ vty_out(vty,
+ "nve group \"%s\" already has \"%s\" prefix %s\n",
+ ((struct rfapi_nve_group_cfg *)(rn->info))
+ ->name,
+ argv[1]->arg, argv[2]->arg);
+ return CMD_WARNING_CONFIG_FAILED;
+ } else {
+ /*
+ * same group name: it's already in the correct place
+ * in the table, so we're done.
+ *
+ * Implies rfg->(vn|un)_prefix is already correct.
+ */
+ return CMD_SUCCESS;
+ }
+ }
+
+ if (bgp->rfapi_cfg->rfg_redist == rfg) {
+ vnc_redistribute_prechange(bgp);
+ }
+
+ /* New prefix, new node */
+
+ if (is_un_prefix) {
+
+ /* detach rfg from previous route table location */
+ if (rfg->un_node) {
+ rfg->un_node->info = NULL;
+ agg_unlock_node(rfg->un_node); /* frees */
+ }
+ rfg->un_node = rn; /* back ref */
+ rfg->un_prefix = p;
+
+ } else {
+
+ /* detach rfg from previous route table location */
+ if (rfg->vn_node) {
+ rfg->vn_node->info = NULL;
+ agg_unlock_node(rfg->vn_node); /* frees */
+ }
+ rfg->vn_node = rn; /* back ref */
+ rfg->vn_prefix = p;
+ }
+
+ /* attach */
+ rn->info = rfg;
+
+ if (bgp->rfapi_cfg->rfg_redist == rfg) {
+ vnc_redistribute_postchange(bgp);
+ }
+
+ return CMD_SUCCESS;
+}
+
+DEFUN (vnc_nve_group_rt_import,
+ vnc_nve_group_rt_import_cmd,
+ "rt import RTLIST...",
+ "Specify route targets\n"
+ "Import filter\n"
+ "Space separated route target list (A.B.C.D:MN|EF:OPQR|GHJK:MN)\n")
+{
+ VTY_DECLVAR_CONTEXT(bgp, bgp);
+ VTY_DECLVAR_CONTEXT_SUB(rfapi_nve_group_cfg, rfg);
+ int rc;
+ struct listnode *node;
+ struct rfapi_rfg_name *rfgn;
+ int is_export_bgp = 0;
+ int is_export_zebra = 0;
+
+ /* make sure it's still in list */
+ if (!listnode_lookup(bgp->rfapi_cfg->nve_groups_sequential, rfg)) {
+ /* Not in list anymore */
+ vty_out(vty, "Current NVE group no longer exists\n");
+ return CMD_WARNING_CONFIG_FAILED;
+ }
+
+ rc = set_ecom_list(vty, argc - 2, argv + 2, &rfg->rt_import_list);
+ if (rc != CMD_SUCCESS)
+ return rc;
+
+ for (ALL_LIST_ELEMENTS_RO(bgp->rfapi_cfg->rfg_export_direct_bgp_l, node,
+ rfgn)) {
+
+ if (rfgn->rfg == rfg) {
+ is_export_bgp = 1;
+ break;
+ }
+ }
+
+ if (is_export_bgp)
+ vnc_direct_bgp_del_group(bgp, rfg);
+
+ for (ALL_LIST_ELEMENTS_RO(bgp->rfapi_cfg->rfg_export_zebra_l, node,
+ rfgn)) {
+
+ if (rfgn->rfg == rfg) {
+ is_export_zebra = 1;
+ break;
+ }
+ }
+
+ if (is_export_zebra)
+ vnc_zebra_del_group(bgp, rfg);
+
+ /*
+ * stop referencing old import table, now reference new one
+ */
+ if (rfg->rfapi_import_table)
+ rfapiImportTableRefDelByIt(bgp, rfg->rfapi_import_table);
+ rfg->rfapi_import_table =
+ rfapiImportTableRefAdd(bgp, rfg->rt_import_list, rfg);
+
+ if (is_export_bgp)
+ vnc_direct_bgp_add_group(bgp, rfg);
+
+ if (is_export_zebra)
+ vnc_zebra_add_group(bgp, rfg);
+
+ return CMD_SUCCESS;
+}
+
+DEFUN (vnc_nve_group_rt_export,
+ vnc_nve_group_rt_export_cmd,
+ "rt export RTLIST...",
+ "Specify route targets\n"
+ "Export filter\n"
+ "Space separated route target list (A.B.C.D:MN|EF:OPQR|GHJK:MN)\n")
+{
+ VTY_DECLVAR_CONTEXT(bgp, bgp);
+ VTY_DECLVAR_CONTEXT_SUB(rfapi_nve_group_cfg, rfg);
+ int rc;
+
+ /* make sure it's still in list */
+ if (!listnode_lookup(bgp->rfapi_cfg->nve_groups_sequential, rfg)) {
+ /* Not in list anymore */
+ vty_out(vty, "Current NVE group no longer exists\n");
+ return CMD_WARNING_CONFIG_FAILED;
+ }
+
+ if (bgp->rfapi_cfg->rfg_redist == rfg) {
+ vnc_redistribute_prechange(bgp);
+ }
+
+ rc = set_ecom_list(vty, argc - 2, argv + 2, &rfg->rt_export_list);
+
+ if (bgp->rfapi_cfg->rfg_redist == rfg) {
+ vnc_redistribute_postchange(bgp);
+ }
+
+ return rc;
+}
+
+DEFUN (vnc_nve_group_rt_both,
+ vnc_nve_group_rt_both_cmd,
+ "rt both RTLIST...",
+ "Specify route targets\n"
+ "Export+import filters\n"
+ "Space separated route target list (A.B.C.D:MN|EF:OPQR|GHJK:MN)\n")
+{
+ VTY_DECLVAR_CONTEXT(bgp, bgp);
+ VTY_DECLVAR_CONTEXT_SUB(rfapi_nve_group_cfg, rfg);
+ int rc;
+ int is_export_bgp = 0;
+ int is_export_zebra = 0;
+ struct listnode *node;
+ struct rfapi_rfg_name *rfgn;
+
+ /* make sure it's still in list */
+ if (!listnode_lookup(bgp->rfapi_cfg->nve_groups_sequential, rfg)) {
+ /* Not in list anymore */
+ vty_out(vty, "Current NVE group no longer exists\n");
+ return CMD_WARNING_CONFIG_FAILED;
+ }
+
+ rc = set_ecom_list(vty, argc - 2, argv + 2, &rfg->rt_import_list);
+ if (rc != CMD_SUCCESS)
+ return rc;
+
+ for (ALL_LIST_ELEMENTS_RO(bgp->rfapi_cfg->rfg_export_direct_bgp_l, node,
+ rfgn)) {
+
+ if (rfgn->rfg == rfg) {
+ is_export_bgp = 1;
+ break;
+ }
+ }
+
+ if (is_export_bgp)
+ vnc_direct_bgp_del_group(bgp, rfg);
+
+ for (ALL_LIST_ELEMENTS_RO(bgp->rfapi_cfg->rfg_export_zebra_l, node,
+ rfgn)) {
+
+ if (rfgn->rfg == rfg) {
+ is_export_zebra = 1;
+ break;
+ }
+ }
+
+ if (is_export_zebra) {
+ vnc_zlog_debug_verbose("%s: is_export_zebra", __func__);
+ vnc_zebra_del_group(bgp, rfg);
+ }
+
+ /*
+ * stop referencing old import table, now reference new one
+ */
+ if (rfg->rfapi_import_table)
+ rfapiImportTableRefDelByIt(bgp, rfg->rfapi_import_table);
+ rfg->rfapi_import_table =
+ rfapiImportTableRefAdd(bgp, rfg->rt_import_list, rfg);
+
+ if (is_export_bgp)
+ vnc_direct_bgp_add_group(bgp, rfg);
+
+ if (is_export_zebra)
+ vnc_zebra_add_group(bgp, rfg);
+
+ if (bgp->rfapi_cfg->rfg_redist == rfg) {
+ vnc_redistribute_prechange(bgp);
+ }
+
+ rc = set_ecom_list(vty, argc - 2, argv + 2, &rfg->rt_export_list);
+
+ if (bgp->rfapi_cfg->rfg_redist == rfg) {
+ vnc_redistribute_postchange(bgp);
+ }
+
+ return rc;
+}
+
+DEFUN (vnc_nve_group_l2rd,
+ vnc_nve_group_l2rd_cmd,
+ "l2rd <(1-255)|auto-vn>",
+ "Specify default Local Nve ID value to use in RD for L2 routes\n"
+ "Fixed value 1-255\n"
+ "use the low-order octet of the NVE's VN address\n")
+{
+ VTY_DECLVAR_CONTEXT(bgp, bgp);
+ VTY_DECLVAR_CONTEXT_SUB(rfapi_nve_group_cfg, rfg);
+
+ /* make sure it's still in list */
+ if (!listnode_lookup(bgp->rfapi_cfg->nve_groups_sequential, rfg)) {
+ /* Not in list anymore */
+ vty_out(vty, "Current NVE group no longer exists\n");
+ return CMD_WARNING_CONFIG_FAILED;
+ }
+
+ if (strmatch(argv[1]->text, "auto:vn")) {
+ rfg->l2rd = 0;
+ } else {
+ char *end = NULL;
+ unsigned long value_l = strtoul(argv[1]->arg, &end, 10);
+ uint8_t value = value_l & 0xff;
+
+ if (!argv[1]->arg[0] || *end) {
+ vty_out(vty, "%% Malformed l2 nve ID \"%s\"\n",
+ argv[1]->arg);
+ return CMD_WARNING_CONFIG_FAILED;
+ }
+ if ((value_l < 1) || (value_l > 0xff)) {
+ vty_out(vty,
+ "%% Malformed l2 nve id (must be greater than 0 and less than %u\n",
+ 0x100);
+ return CMD_WARNING_CONFIG_FAILED;
+ }
+
+ rfg->l2rd = value;
+ }
+ rfg->flags |= RFAPI_RFG_L2RD;
+
+ return CMD_SUCCESS;
+}
+
+DEFUN (vnc_nve_group_no_l2rd,
+ vnc_nve_group_no_l2rd_cmd,
+ "no l2rd",
+ NO_STR
+ "Specify default Local Nve ID value to use in RD for L2 routes\n")
+{
+ VTY_DECLVAR_CONTEXT(bgp, bgp);
+ VTY_DECLVAR_CONTEXT_SUB(rfapi_nve_group_cfg, rfg);
+
+ /* make sure it's still in list */
+ if (!listnode_lookup(bgp->rfapi_cfg->nve_groups_sequential, rfg)) {
+ /* Not in list anymore */
+ vty_out(vty, "Current NVE group no longer exists\n");
+ return CMD_WARNING_CONFIG_FAILED;
+ }
+
+ rfg->l2rd = 0;
+ rfg->flags &= ~RFAPI_RFG_L2RD;
+
+ return CMD_SUCCESS;
+}
+
+DEFUN (vnc_nve_group_rd,
+ vnc_nve_group_rd_cmd,
+ "rd ASN:NN_OR_IP-ADDRESS:NN",
+ "Specify route distinguisher\n"
+ "Route Distinguisher (<as-number>:<number> | <ip-address>:<number> | auto:vn:<number> )\n")
+{
+ VTY_DECLVAR_CONTEXT(bgp, bgp);
+ int ret;
+ struct prefix_rd prd;
+ VTY_DECLVAR_CONTEXT_SUB(rfapi_nve_group_cfg, rfg);
+
+ /* make sure it's still in list */
+ if (!listnode_lookup(bgp->rfapi_cfg->nve_groups_sequential, rfg)) {
+ /* Not in list anymore */
+ vty_out(vty, "Current NVE group no longer exists\n");
+ return CMD_WARNING_CONFIG_FAILED;
+ }
+
+ if (!strncmp(argv[1]->arg, "auto:vn:", 8)) {
+ /*
+ * use AF_UNIX to designate automatically-assigned RD
+ * auto:vn:nn where nn is a 2-octet quantity
+ */
+ char *end = NULL;
+ uint32_t value32 = strtoul(argv[1]->arg + 8, &end, 10);
+ uint16_t value = value32 & 0xffff;
+
+ if (!argv[1]->arg[8] || *end) {
+ vty_out(vty, "%% Malformed rd\n");
+ return CMD_WARNING_CONFIG_FAILED;
+ }
+ if (value32 > 0xffff) {
+ vty_out(vty, "%% Malformed rd (must be less than %u\n",
+ 0x0ffff);
+ return CMD_WARNING_CONFIG_FAILED;
+ }
+
+ memset(&prd, 0, sizeof(prd));
+ prd.family = AF_UNIX;
+ prd.prefixlen = 64;
+ prd.val[0] = (RD_TYPE_IP >> 8) & 0x0ff;
+ prd.val[1] = RD_TYPE_IP & 0x0ff;
+ prd.val[6] = (value >> 8) & 0x0ff;
+ prd.val[7] = value & 0x0ff;
+
+ } else {
+
+ ret = str2prefix_rd(argv[1]->arg, &prd);
+ if (!ret) {
+ vty_out(vty, "%% Malformed rd\n");
+ return CMD_WARNING_CONFIG_FAILED;
+ }
+ }
+
+ if (bgp->rfapi_cfg->rfg_redist == rfg) {
+ vnc_redistribute_prechange(bgp);
+ }
+
+ rfg->rd = prd;
+
+ if (bgp->rfapi_cfg->rfg_redist == rfg) {
+ vnc_redistribute_postchange(bgp);
+ }
+ return CMD_SUCCESS;
+}
+
+DEFUN (vnc_nve_group_responselifetime,
+ vnc_nve_group_responselifetime_cmd,
+ "response-lifetime <LIFETIME|infinite>",
+ "Specify response lifetime\n"
+ "Response lifetime in seconds\n" "Infinite response lifetime\n")
+{
+ VTY_DECLVAR_CONTEXT(bgp, bgp);
+ unsigned int rspint;
+ VTY_DECLVAR_CONTEXT_SUB(rfapi_nve_group_cfg, rfg);
+ struct rfapi_descriptor *rfd;
+ struct listnode *hdnode;
+
+ /* make sure it's still in list */
+ if (!listnode_lookup(bgp->rfapi_cfg->nve_groups_sequential, rfg)) {
+ /* Not in list anymore */
+ vty_out(vty, "Current NVE group no longer exists\n");
+ return CMD_WARNING_CONFIG_FAILED;
+ }
+
+ if (strmatch(argv[1]->text, "infinite")) {
+ rspint = RFAPI_INFINITE_LIFETIME;
+ } else {
+ rspint = strtoul(argv[1]->arg, NULL, 10);
+ }
+
+ rfg->response_lifetime = rspint;
+ rfg->flags |= RFAPI_RFG_RESPONSE_LIFETIME;
+ if (rfg->nves)
+ for (ALL_LIST_ELEMENTS_RO(rfg->nves, hdnode, rfd))
+ rfd->response_lifetime = rspint;
+ return CMD_SUCCESS;
+}
+
+/*
+ * Sigh. This command, like exit-address-family, is a hack to deal
+ * with the lack of rigorous level control in the command handler.
+ * TBD fix command handler.
+ */
+DEFUN_NOSH (exit_vnc,
+ exit_vnc_cmd,
+ "exit-vnc",
+ "Exit VNC configuration mode\n")
+{
+ if (vty->node == BGP_VNC_DEFAULTS_NODE
+ || vty->node == BGP_VNC_NVE_GROUP_NODE
+ || vty->node == BGP_VNC_L2_GROUP_NODE) {
+
+ vty->node = BGP_NODE;
+ }
+ return CMD_SUCCESS;
+}
+
+static struct cmd_node bgp_vnc_defaults_node = {
+ .name = "bgp vnc defaults",
+ .node = BGP_VNC_DEFAULTS_NODE,
+ .parent_node = BGP_NODE,
+ .prompt = "%s(config-router-vnc-defaults)# ",
+};
+
+static struct cmd_node bgp_vnc_nve_group_node = {
+ .name = "bgp vnc nve",
+ .node = BGP_VNC_NVE_GROUP_NODE,
+ .parent_node = BGP_NODE,
+ .prompt = "%s(config-router-vnc-nve-group)# ",
+};
+
+/*-------------------------------------------------------------------------
+ * VNC nve-group
+ * Note there are two types of NVEs, one for VPNs one for RFP NVEs
+ *-----------------------------------------------------------------------*/
+
+DEFUN_NOSH (vnc_vrf_policy,
+ vnc_vrf_policy_cmd,
+ "vrf-policy NAME",
+ "Configure a VRF policy group\n"
+ "VRF name\n")
+{
+ struct rfapi_nve_group_cfg *rfg;
+ VTY_DECLVAR_CONTEXT(bgp, bgp);
+
+ if (bgp->inst_type == BGP_INSTANCE_TYPE_VRF) {
+ vty_out(vty,
+ "Can't configure vrf-policy within a BGP VRF instance\n");
+ return CMD_WARNING_CONFIG_FAILED;
+ }
+
+ /* Search for name */
+ rfg = bgp_rfapi_cfg_match_byname(bgp, argv[1]->arg,
+ RFAPI_GROUP_CFG_VRF);
+
+ if (!rfg) {
+ rfg = rfapi_group_new(bgp, RFAPI_GROUP_CFG_VRF, argv[1]->arg);
+ if (!rfg) {
+ /* Error out of memory */
+ vty_out(vty, "Can't allocate memory for NVE group\n");
+ return CMD_WARNING_CONFIG_FAILED;
+ }
+ }
+ /*
+ * XXX subsequent calls will need to make sure this item is still
+ * in the linked list and has the same name
+ */
+ VTY_PUSH_CONTEXT_SUB(BGP_VRF_POLICY_NODE, rfg);
+
+ return CMD_SUCCESS;
+}
+
+DEFUN (vnc_no_vrf_policy,
+ vnc_no_vrf_policy_cmd,
+ "no vrf-policy NAME",
+ NO_STR
+ "Remove a VRF policy group\n"
+ "VRF name\n")
+{
+ VTY_DECLVAR_CONTEXT(bgp, bgp);
+
+ /* silently return */
+ if (bgp->inst_type == BGP_INSTANCE_TYPE_VRF)
+ return CMD_SUCCESS;
+
+ return bgp_rfapi_delete_named_nve_group(vty, bgp, argv[2]->arg,
+ RFAPI_GROUP_CFG_VRF);
+}
+
+DEFUN (vnc_vrf_policy_label,
+ vnc_vrf_policy_label_cmd,
+ "label (0-1048575)",
+ "Default label value for VRF\n"
+ "Label Value <0-1048575>\n")
+{
+ VTY_DECLVAR_CONTEXT_SUB(rfapi_nve_group_cfg, rfg);
+
+ uint32_t label;
+ VTY_DECLVAR_CONTEXT(bgp, bgp);
+
+ /* make sure it's still in list */
+ if (!listnode_lookup(bgp->rfapi_cfg->nve_groups_sequential, rfg)) {
+ /* Not in list anymore */
+ vty_out(vty, "Current NVE group no longer exists\n");
+ return CMD_WARNING_CONFIG_FAILED;
+ }
+
+ label = strtoul(argv[1]->arg, NULL, 10);
+
+ if (bgp->rfapi_cfg->rfg_redist == rfg) {
+ vnc_redistribute_prechange(bgp);
+ }
+
+ rfg->label = label;
+
+ if (bgp->rfapi_cfg->rfg_redist == rfg) {
+ vnc_redistribute_postchange(bgp);
+ }
+ return CMD_SUCCESS;
+}
+
+DEFUN (vnc_vrf_policy_no_label,
+ vnc_vrf_policy_no_label_cmd,
+ "no label",
+ NO_STR
+ "Remove VRF default label\n")
+{
+ VTY_DECLVAR_CONTEXT_SUB(rfapi_nve_group_cfg, rfg);
+ VTY_DECLVAR_CONTEXT(bgp, bgp);
+
+ /* make sure it's still in list */
+ if (!listnode_lookup(bgp->rfapi_cfg->nve_groups_sequential, rfg)) {
+ /* Not in list anymore */
+ vty_out(vty, "Current VRF group no longer exists\n");
+ return CMD_WARNING_CONFIG_FAILED;
+ }
+
+ if (bgp->rfapi_cfg->rfg_redist == rfg) {
+ vnc_redistribute_prechange(bgp);
+ }
+
+ rfg->label = MPLS_LABEL_NONE;
+
+ if (bgp->rfapi_cfg->rfg_redist == rfg) {
+ vnc_redistribute_postchange(bgp);
+ }
+ return CMD_SUCCESS;
+}
+
+DEFUN (vnc_vrf_policy_nexthop,
+ vnc_vrf_policy_nexthop_cmd,
+ "nexthop <A.B.C.D|X:X::X:X|self>",
+ "Specify next hop to use for VRF advertised prefixes\n"
+ "IPv4 prefix\n"
+ "IPv6 prefix\n"
+ "Use configured router-id (default)\n")
+{
+ VTY_DECLVAR_CONTEXT_SUB(rfapi_nve_group_cfg, rfg);
+ struct prefix p;
+
+ VTY_DECLVAR_CONTEXT(bgp, bgp);
+
+ /* make sure it's still in list */
+ if (!listnode_lookup(bgp->rfapi_cfg->nve_groups_sequential, rfg)) {
+ /* Not in list anymore */
+ vty_out(vty, "Current VRF no longer exists\n");
+ return CMD_WARNING_CONFIG_FAILED;
+ }
+
+ if (bgp->rfapi_cfg->rfg_redist == rfg) {
+ vnc_redistribute_prechange(bgp);
+ }
+
+ if (!str2prefix(argv[1]->arg, &p) && p.family) {
+ // vty_out (vty, "Nexthop set to self\n");
+ SET_FLAG(rfg->flags, RFAPI_RFG_VPN_NH_SELF);
+ memset(&rfg->vn_prefix, 0, sizeof(struct prefix));
+ } else {
+ UNSET_FLAG(rfg->flags, RFAPI_RFG_VPN_NH_SELF);
+ rfg->vn_prefix = p;
+ rfg->un_prefix = p;
+ }
+
+ /* TBD handle router-id/ nexthop changes when have advertised prefixes
+ */
+
+ if (bgp->rfapi_cfg->rfg_redist == rfg) {
+ vnc_redistribute_postchange(bgp);
+ }
+
+ return CMD_SUCCESS;
+}
+
+/* The RT code should be refactored/simplified with above... */
+DEFUN (vnc_vrf_policy_rt_import,
+ vnc_vrf_policy_rt_import_cmd,
+ "rt import RTLIST...",
+ "Specify route targets\n"
+ "Import filter\n"
+ "Space separated route target list (A.B.C.D:MN|EF:OPQR|GHJK:MN)\n")
+{
+ VTY_DECLVAR_CONTEXT_SUB(rfapi_nve_group_cfg, rfg);
+ VTY_DECLVAR_CONTEXT(bgp, bgp);
+ int rc;
+ struct listnode *node;
+ struct rfapi_rfg_name *rfgn;
+ int is_export_bgp = 0;
+ int is_export_zebra = 0;
+
+ /* make sure it's still in list */
+ if (!listnode_lookup(bgp->rfapi_cfg->nve_groups_sequential, rfg)) {
+ /* Not in list anymore */
+ vty_out(vty, "Current NVE group no longer exists\n");
+ return CMD_WARNING_CONFIG_FAILED;
+ }
+
+ rc = set_ecom_list(vty, argc - 2, argv + 2, &rfg->rt_import_list);
+ if (rc != CMD_SUCCESS)
+ return rc;
+
+ for (ALL_LIST_ELEMENTS_RO(bgp->rfapi_cfg->rfg_export_direct_bgp_l, node,
+ rfgn)) {
+
+ if (rfgn->rfg == rfg) {
+ is_export_bgp = 1;
+ break;
+ }
+ }
+
+ if (is_export_bgp)
+ vnc_direct_bgp_del_group(bgp, rfg);
+
+ for (ALL_LIST_ELEMENTS_RO(bgp->rfapi_cfg->rfg_export_zebra_l, node,
+ rfgn)) {
+
+ if (rfgn->rfg == rfg) {
+ is_export_zebra = 1;
+ break;
+ }
+ }
+
+ if (is_export_zebra)
+ vnc_zebra_del_group(bgp, rfg);
+
+ /*
+ * stop referencing old import table, now reference new one
+ */
+ if (rfg->rfapi_import_table)
+ rfapiImportTableRefDelByIt(bgp, rfg->rfapi_import_table);
+ rfg->rfapi_import_table =
+ rfapiImportTableRefAdd(bgp, rfg->rt_import_list, rfg);
+
+ if (is_export_bgp)
+ vnc_direct_bgp_add_group(bgp, rfg);
+
+ if (is_export_zebra)
+ vnc_zebra_add_group(bgp, rfg);
+
+ return CMD_SUCCESS;
+}
+
+DEFUN (vnc_vrf_policy_rt_export,
+ vnc_vrf_policy_rt_export_cmd,
+ "rt export RTLIST...",
+ "Specify route targets\n"
+ "Export filter\n"
+ "Space separated route target list (A.B.C.D:MN|EF:OPQR|GHJK:MN)\n")
+{
+ VTY_DECLVAR_CONTEXT_SUB(rfapi_nve_group_cfg, rfg);
+ VTY_DECLVAR_CONTEXT(bgp, bgp);
+ int rc;
+
+ /* make sure it's still in list */
+ if (!listnode_lookup(bgp->rfapi_cfg->nve_groups_sequential, rfg)) {
+ /* Not in list anymore */
+ vty_out(vty, "Current NVE group no longer exists\n");
+ return CMD_WARNING_CONFIG_FAILED;
+ }
+
+ if (bgp->rfapi_cfg->rfg_redist == rfg) {
+ vnc_redistribute_prechange(bgp);
+ }
+
+ rc = set_ecom_list(vty, argc - 2, argv + 2, &rfg->rt_export_list);
+
+ if (bgp->rfapi_cfg->rfg_redist == rfg) {
+ vnc_redistribute_postchange(bgp);
+ }
+
+ return rc;
+}
+
+DEFUN (vnc_vrf_policy_rt_both,
+ vnc_vrf_policy_rt_both_cmd,
+ "rt both RTLIST...",
+ "Specify route targets\n"
+ "Export+import filters\n"
+ "Space separated route target list (A.B.C.D:MN|EF:OPQR|GHJK:MN)\n")
+{
+ VTY_DECLVAR_CONTEXT_SUB(rfapi_nve_group_cfg, rfg);
+ VTY_DECLVAR_CONTEXT(bgp, bgp);
+ int rc;
+ int is_export_bgp = 0;
+ int is_export_zebra = 0;
+ struct listnode *node;
+ struct rfapi_rfg_name *rfgn;
+
+ /* make sure it's still in list */
+ if (!listnode_lookup(bgp->rfapi_cfg->nve_groups_sequential, rfg)) {
+ /* Not in list anymore */
+ vty_out(vty, "Current NVE group no longer exists\n");
+ return CMD_WARNING_CONFIG_FAILED;
+ }
+
+ rc = set_ecom_list(vty, argc - 2, argv + 2, &rfg->rt_import_list);
+ if (rc != CMD_SUCCESS)
+ return rc;
+
+ for (ALL_LIST_ELEMENTS_RO(bgp->rfapi_cfg->rfg_export_direct_bgp_l, node,
+ rfgn)) {
+
+ if (rfgn->rfg == rfg) {
+ is_export_bgp = 1;
+ break;
+ }
+ }
+
+ if (is_export_bgp)
+ vnc_direct_bgp_del_group(bgp, rfg);
+
+ for (ALL_LIST_ELEMENTS_RO(bgp->rfapi_cfg->rfg_export_zebra_l, node,
+ rfgn)) {
+
+ if (rfgn->rfg == rfg) {
+ is_export_zebra = 1;
+ break;
+ }
+ }
+
+ if (is_export_zebra) {
+ vnc_zlog_debug_verbose("%s: is_export_zebra", __func__);
+ vnc_zebra_del_group(bgp, rfg);
+ }
+
+ /*
+ * stop referencing old import table, now reference new one
+ */
+ if (rfg->rfapi_import_table)
+ rfapiImportTableRefDelByIt(bgp, rfg->rfapi_import_table);
+ rfg->rfapi_import_table =
+ rfapiImportTableRefAdd(bgp, rfg->rt_import_list, rfg);
+
+ if (is_export_bgp)
+ vnc_direct_bgp_add_group(bgp, rfg);
+
+ if (is_export_zebra)
+ vnc_zebra_add_group(bgp, rfg);
+
+ if (bgp->rfapi_cfg->rfg_redist == rfg) {
+ vnc_redistribute_prechange(bgp);
+ }
+
+ rc = set_ecom_list(vty, argc - 2, argv + 2, &rfg->rt_export_list);
+
+ if (bgp->rfapi_cfg->rfg_redist == rfg) {
+ vnc_redistribute_postchange(bgp);
+ }
+
+ return rc;
+}
+
+DEFUN (vnc_vrf_policy_rd,
+ vnc_vrf_policy_rd_cmd,
+ "rd ASN:NN_OR_IP-ADDRESS:NN",
+ "Specify default VRF route distinguisher\n"
+ "Route Distinguisher (<as-number>:<number> | <ip-address>:<number> | auto:nh:<number> )\n")
+{
+ int ret;
+ struct prefix_rd prd;
+ VTY_DECLVAR_CONTEXT_SUB(rfapi_nve_group_cfg, rfg);
+ VTY_DECLVAR_CONTEXT(bgp, bgp);
+
+ /* make sure it's still in list */
+ if (!listnode_lookup(bgp->rfapi_cfg->nve_groups_sequential, rfg)) {
+ /* Not in list anymore */
+ vty_out(vty, "Current NVE group no longer exists\n");
+ return CMD_WARNING_CONFIG_FAILED;
+ }
+
+ if (!strncmp(argv[1]->arg, "auto:nh:", 8)) {
+ /*
+ * use AF_UNIX to designate automatically-assigned RD
+ * auto:vn:nn where nn is a 2-octet quantity
+ */
+ char *end = NULL;
+ uint32_t value32 = strtoul(argv[1]->arg + 8, &end, 10);
+ uint16_t value = value32 & 0xffff;
+
+ if (!*(argv[1]->arg + 5) || *end) {
+ vty_out(vty, "%% Malformed rd\n");
+ return CMD_WARNING_CONFIG_FAILED;
+ }
+ if (value32 > 0xffff) {
+ vty_out(vty, "%% Malformed rd (must be less than %u\n",
+ 0x0ffff);
+ return CMD_WARNING_CONFIG_FAILED;
+ }
+
+ memset(&prd, 0, sizeof(prd));
+ prd.family = AF_UNIX;
+ prd.prefixlen = 64;
+ prd.val[0] = (RD_TYPE_IP >> 8) & 0x0ff;
+ prd.val[1] = RD_TYPE_IP & 0x0ff;
+ prd.val[6] = (value >> 8) & 0x0ff;
+ prd.val[7] = value & 0x0ff;
+
+ } else {
+
+ ret = str2prefix_rd(argv[1]->arg, &prd);
+ if (!ret) {
+ vty_out(vty, "%% Malformed rd\n");
+ return CMD_WARNING_CONFIG_FAILED;
+ }
+ }
+
+ if (bgp->rfapi_cfg->rfg_redist == rfg) {
+ vnc_redistribute_prechange(bgp);
+ }
+
+ rfg->rd = prd;
+
+ if (bgp->rfapi_cfg->rfg_redist == rfg) {
+ vnc_redistribute_postchange(bgp);
+ }
+ return CMD_SUCCESS;
+}
+
+DEFUN_NOSH (exit_vrf_policy,
+ exit_vrf_policy_cmd,
+ "exit-vrf-policy",
+ "Exit VRF policy configuration mode\n")
+{
+ if (vty->node == BGP_VRF_POLICY_NODE) {
+ vty->node = BGP_NODE;
+ }
+ return CMD_SUCCESS;
+}
+
+static struct cmd_node bgp_vrf_policy_node = {
+ .name = "bgp vrf policy",
+ .node = BGP_VRF_POLICY_NODE,
+ .parent_node = BGP_NODE,
+ .prompt = "%s(config-router-vrf-policy)# ",
+};
+
+/*-------------------------------------------------------------------------
+ * vnc-l2-group
+ *-----------------------------------------------------------------------*/
+
+
+DEFUN_NOSH (vnc_l2_group,
+ vnc_l2_group_cmd,
+ "vnc l2-group NAME",
+ VNC_CONFIG_STR "Configure a L2 group\n" "Group name\n")
+{
+ struct rfapi_l2_group_cfg *rfg;
+ VTY_DECLVAR_CONTEXT(bgp, bgp);
+ VNC_VTY_CONFIG_CHECK(bgp);
+
+ /* Search for name */
+ rfg = rfapi_l2_group_lookup_byname(bgp, argv[2]->arg);
+
+ if (!rfg) {
+ rfg = rfapi_l2_group_new();
+ if (!rfg) {
+ /* Error out of memory */
+ vty_out(vty, "Can't allocate memory for L2 group\n");
+ return CMD_WARNING_CONFIG_FAILED;
+ }
+ rfg->name = strdup(argv[2]->arg);
+ /* add to tail of list */
+ listnode_add(bgp->rfapi_cfg->l2_groups, rfg);
+ }
+
+ /*
+ * XXX subsequent calls will need to make sure this item is still
+ * in the linked list and has the same name
+ */
+ VTY_PUSH_CONTEXT_SUB(BGP_VNC_L2_GROUP_NODE, rfg);
+ return CMD_SUCCESS;
+}
+
+static void bgp_rfapi_delete_l2_group(struct vty *vty, /* NULL = no output */
+ struct bgp *bgp,
+ struct rfapi_l2_group_cfg *rfg)
+{
+ /* delete it */
+ free(rfg->name);
+ if (rfg->rt_import_list)
+ ecommunity_free(&rfg->rt_import_list);
+ if (rfg->rt_export_list)
+ ecommunity_free(&rfg->rt_export_list);
+ if (rfg->labels)
+ list_delete(&rfg->labels);
+ XFREE(MTYPE_RFAPI_RFP_GROUP_CFG, rfg->rfp_cfg);
+ listnode_delete(bgp->rfapi_cfg->l2_groups, rfg);
+
+ rfapi_l2_group_del(rfg);
+}
+
+static int
+bgp_rfapi_delete_named_l2_group(struct vty *vty, /* NULL = no output */
+ struct bgp *bgp,
+ const char *rfg_name) /* NULL = any */
+{
+ struct rfapi_l2_group_cfg *rfg = NULL;
+ struct listnode *node, *nnode;
+
+ /* Search for name */
+ if (rfg_name) {
+ rfg = rfapi_l2_group_lookup_byname(bgp, rfg_name);
+ if (!rfg) {
+ if (vty)
+ vty_out(vty, "No L2 group named \"%s\"\n",
+ rfg_name);
+ return CMD_WARNING_CONFIG_FAILED;
+ }
+ }
+
+ if (rfg)
+ bgp_rfapi_delete_l2_group(vty, bgp, rfg);
+ else /* must be delete all */
+ for (ALL_LIST_ELEMENTS(bgp->rfapi_cfg->l2_groups, node, nnode,
+ rfg))
+ bgp_rfapi_delete_l2_group(vty, bgp, rfg);
+ return CMD_SUCCESS;
+}
+
+DEFUN (vnc_no_l2_group,
+ vnc_no_l2_group_cmd,
+ "no vnc l2-group NAME",
+ NO_STR
+ VNC_CONFIG_STR
+ "Configure a L2 group\n"
+ "Group name\n")
+{
+ VTY_DECLVAR_CONTEXT(bgp, bgp);
+
+ return bgp_rfapi_delete_named_l2_group(vty, bgp, argv[3]->arg);
+}
+
+
+DEFUN (vnc_l2_group_lni,
+ vnc_l2_group_lni_cmd,
+ "logical-network-id (0-4294967295)",
+ "Specify Logical Network ID associated with group\n"
+ "value\n")
+{
+ VTY_DECLVAR_CONTEXT_SUB(rfapi_l2_group_cfg, rfg);
+ VTY_DECLVAR_CONTEXT(bgp, bgp);
+
+ /* make sure it's still in list */
+ if (!listnode_lookup(bgp->rfapi_cfg->l2_groups, rfg)) {
+ /* Not in list anymore */
+ vty_out(vty, "Current L2 group no longer exists\n");
+ return CMD_WARNING_CONFIG_FAILED;
+ }
+
+ rfg->logical_net_id = strtoul(argv[1]->arg, NULL, 10);
+
+ return CMD_SUCCESS;
+}
+
+DEFUN (vnc_l2_group_labels,
+ vnc_l2_group_labels_cmd,
+ "labels (0-1048575)...",
+ "Specify label values associated with group\n"
+ "Space separated list of label values <0-1048575>\n")
+{
+ VTY_DECLVAR_CONTEXT_SUB(rfapi_l2_group_cfg, rfg);
+ VTY_DECLVAR_CONTEXT(bgp, bgp);
+ struct list *ll;
+
+ /* make sure it's still in list */
+ if (!listnode_lookup(bgp->rfapi_cfg->l2_groups, rfg)) {
+ /* Not in list anymore */
+ vty_out(vty, "Current L2 group no longer exists\n");
+ return CMD_WARNING_CONFIG_FAILED;
+ }
+
+ ll = rfg->labels;
+ if (ll == NULL) {
+ ll = list_new();
+ rfg->labels = ll;
+ }
+ argc--;
+ argv++;
+ for (; argc; --argc, ++argv) {
+ uint32_t label;
+ label = strtoul(argv[0]->arg, NULL, 10);
+ if (!listnode_lookup(ll, (void *)(uintptr_t)label))
+ listnode_add(ll, (void *)(uintptr_t)label);
+ }
+
+ return CMD_SUCCESS;
+}
+
+DEFUN (vnc_l2_group_no_labels,
+ vnc_l2_group_no_labels_cmd,
+ "no labels (0-1048575)...",
+ NO_STR
+ "Specify label values associated with L2 group\n"
+ "Space separated list of label values <0-1048575>\n")
+{
+ VTY_DECLVAR_CONTEXT_SUB(rfapi_l2_group_cfg, rfg);
+ VTY_DECLVAR_CONTEXT(bgp, bgp);
+ struct list *ll;
+
+ /* make sure it's still in list */
+ if (!listnode_lookup(bgp->rfapi_cfg->l2_groups, rfg)) {
+ /* Not in list anymore */
+ vty_out(vty, "Current L2 group no longer exists\n");
+ return CMD_WARNING_CONFIG_FAILED;
+ }
+
+ ll = rfg->labels;
+ if (ll == NULL) {
+ vty_out(vty, "Label no longer associated with group\n");
+ return CMD_WARNING_CONFIG_FAILED;
+ }
+
+ argc -= 2;
+ argv += 2;
+ for (; argc; --argc, ++argv) {
+ uint32_t label;
+ label = strtoul(argv[0]->arg, NULL, 10);
+ listnode_delete(ll, (void *)(uintptr_t)label);
+ }
+
+ return CMD_SUCCESS;
+}
+
+DEFUN (vnc_l2_group_rt,
+ vnc_l2_group_rt_cmd,
+ "rt <both|export|import> ASN:NN_OR_IP-ADDRESS:NN",
+ "Specify route targets\n"
+ "Export+import filters\n"
+ "Export filters\n"
+ "Import filters\n"
+ "A route target\n")
+{
+ VTY_DECLVAR_CONTEXT_SUB(rfapi_l2_group_cfg, rfg);
+ VTY_DECLVAR_CONTEXT(bgp, bgp);
+ int rc = CMD_SUCCESS;
+ int do_import = 0;
+ int do_export = 0;
+
+ switch (argv[1]->arg[0]) {
+ case 'b':
+ do_export = 1; /* fall through */
+ case 'i':
+ do_import = 1;
+ break;
+ case 'e':
+ do_export = 1;
+ break;
+ default:
+ vty_out(vty, "Unknown option, %s\n", argv[1]->arg);
+ return CMD_ERR_NO_MATCH;
+ }
+
+ /* make sure it's still in list */
+ if (!listnode_lookup(bgp->rfapi_cfg->l2_groups, rfg)) {
+ /* Not in list anymore */
+ vty_out(vty, "Current L2 group no longer exists\n");
+ return CMD_WARNING_CONFIG_FAILED;
+ }
+
+ if (do_import)
+ rc = set_ecom_list(vty, argc - 2, argv + 2,
+ &rfg->rt_import_list);
+ if (rc == CMD_SUCCESS && do_export)
+ rc = set_ecom_list(vty, argc - 2, argv + 2,
+ &rfg->rt_export_list);
+ return rc;
+}
+
+
+static struct cmd_node bgp_vnc_l2_group_node = {
+ .name = "bgp vnc l2",
+ .node = BGP_VNC_L2_GROUP_NODE,
+ .parent_node = BGP_NODE,
+ .prompt = "%s(config-router-vnc-l2-group)# ",
+};
+
+struct rfapi_l2_group_cfg *
+bgp_rfapi_get_group_by_lni_label(struct bgp *bgp, uint32_t logical_net_id,
+ uint32_t label)
+{
+ struct rfapi_l2_group_cfg *rfg;
+ struct listnode *node;
+
+ if (bgp->rfapi_cfg->l2_groups == NULL) /* not the best place for this */
+ return NULL;
+
+ label = label & 0xfffff; /* label is 20 bits! */
+
+ for (ALL_LIST_ELEMENTS_RO(bgp->rfapi_cfg->l2_groups, node, rfg)) {
+ if (rfg->logical_net_id == logical_net_id) {
+ struct listnode *lnode;
+ void *data;
+ for (ALL_LIST_ELEMENTS_RO(rfg->labels, lnode, data))
+ if (((uint32_t)((uintptr_t)data))
+ == label) { /* match! */
+ return rfg;
+ }
+ }
+ }
+ return NULL;
+}
+
+struct list *bgp_rfapi_get_labellist_by_lni_label(struct bgp *bgp,
+ uint32_t logical_net_id,
+ uint32_t label)
+{
+ struct rfapi_l2_group_cfg *rfg;
+ rfg = bgp_rfapi_get_group_by_lni_label(bgp, logical_net_id, label);
+ if (rfg) {
+ return rfg->labels;
+ }
+ return NULL;
+}
+
+struct ecommunity *
+bgp_rfapi_get_ecommunity_by_lni_label(struct bgp *bgp, uint32_t is_import,
+ uint32_t logical_net_id, uint32_t label)
+{
+ struct rfapi_l2_group_cfg *rfg;
+ rfg = bgp_rfapi_get_group_by_lni_label(bgp, logical_net_id, label);
+ if (rfg) {
+ if (is_import)
+ return rfg->rt_import_list;
+ else
+ return rfg->rt_export_list;
+ }
+ return NULL;
+}
+
+void bgp_rfapi_cfg_init(void)
+{
+ install_node(&bgp_vnc_defaults_node);
+ install_node(&bgp_vnc_nve_group_node);
+ install_node(&bgp_vrf_policy_node);
+ install_node(&bgp_vnc_l2_group_node);
+ install_default(BGP_VRF_POLICY_NODE);
+ install_default(BGP_VNC_DEFAULTS_NODE);
+ install_default(BGP_VNC_NVE_GROUP_NODE);
+ install_default(BGP_VNC_L2_GROUP_NODE);
+
+ /*
+ * Add commands
+ */
+ install_element(BGP_NODE, &vnc_defaults_cmd);
+ install_element(BGP_NODE, &vnc_nve_group_cmd);
+ install_element(BGP_NODE, &vnc_no_nve_group_cmd);
+ install_element(BGP_NODE, &vnc_vrf_policy_cmd);
+ install_element(BGP_NODE, &vnc_no_vrf_policy_cmd);
+ install_element(BGP_NODE, &vnc_l2_group_cmd);
+ install_element(BGP_NODE, &vnc_no_l2_group_cmd);
+ install_element(BGP_NODE, &vnc_advertise_un_method_cmd);
+ install_element(BGP_NODE, &vnc_export_mode_cmd);
+
+ install_element(BGP_VNC_DEFAULTS_NODE, &vnc_defaults_rt_import_cmd);
+ install_element(BGP_VNC_DEFAULTS_NODE, &vnc_defaults_rt_export_cmd);
+ install_element(BGP_VNC_DEFAULTS_NODE, &vnc_defaults_rt_both_cmd);
+ install_element(BGP_VNC_DEFAULTS_NODE, &vnc_defaults_rd_cmd);
+ install_element(BGP_VNC_DEFAULTS_NODE, &vnc_defaults_l2rd_cmd);
+ install_element(BGP_VNC_DEFAULTS_NODE, &vnc_defaults_no_l2rd_cmd);
+ install_element(BGP_VNC_DEFAULTS_NODE,
+ &vnc_defaults_responselifetime_cmd);
+ install_element(BGP_VNC_DEFAULTS_NODE, &exit_vnc_cmd);
+
+ install_element(BGP_NODE, &vnc_redistribute_protocol_cmd);
+ install_element(BGP_NODE, &vnc_no_redistribute_protocol_cmd);
+ install_element(BGP_NODE, &vnc_redistribute_nvegroup_cmd);
+ install_element(BGP_NODE, &vnc_redistribute_no_nvegroup_cmd);
+ install_element(BGP_NODE, &vnc_redistribute_lifetime_cmd);
+ install_element(BGP_NODE, &vnc_redistribute_rh_roo_localadmin_cmd);
+ install_element(BGP_NODE, &vnc_redistribute_mode_cmd);
+ install_element(BGP_NODE, &vnc_redistribute_bgp_exterior_cmd);
+
+ install_element(BGP_NODE, &vnc_redist_bgpdirect_no_prefixlist_cmd);
+ install_element(BGP_NODE, &vnc_redist_bgpdirect_prefixlist_cmd);
+ install_element(BGP_NODE, &vnc_redist_bgpdirect_no_routemap_cmd);
+ install_element(BGP_NODE, &vnc_redist_bgpdirect_routemap_cmd);
+
+ install_element(BGP_VNC_NVE_GROUP_NODE,
+ &vnc_nve_group_redist_bgpdirect_no_prefixlist_cmd);
+ install_element(BGP_VNC_NVE_GROUP_NODE,
+ &vnc_nve_group_redist_bgpdirect_prefixlist_cmd);
+ install_element(BGP_VNC_NVE_GROUP_NODE,
+ &vnc_nve_group_redist_bgpdirect_no_routemap_cmd);
+ install_element(BGP_VNC_NVE_GROUP_NODE,
+ &vnc_nve_group_redist_bgpdirect_routemap_cmd);
+
+ install_element(BGP_NODE, &vnc_export_nvegroup_cmd);
+ install_element(BGP_NODE, &vnc_no_export_nvegroup_cmd);
+ install_element(BGP_NODE, &vnc_nve_export_prefixlist_cmd);
+ install_element(BGP_NODE, &vnc_nve_export_routemap_cmd);
+ install_element(BGP_NODE, &vnc_nve_export_no_prefixlist_cmd);
+ install_element(BGP_NODE, &vnc_nve_export_no_routemap_cmd);
+
+ install_element(BGP_VNC_NVE_GROUP_NODE, &vnc_nve_group_l2rd_cmd);
+ install_element(BGP_VNC_NVE_GROUP_NODE, &vnc_nve_group_no_l2rd_cmd);
+ install_element(BGP_VNC_NVE_GROUP_NODE, &vnc_nve_group_prefix_cmd);
+ install_element(BGP_VNC_NVE_GROUP_NODE, &vnc_nve_group_rt_import_cmd);
+ install_element(BGP_VNC_NVE_GROUP_NODE, &vnc_nve_group_rt_export_cmd);
+ install_element(BGP_VNC_NVE_GROUP_NODE, &vnc_nve_group_rt_both_cmd);
+ install_element(BGP_VNC_NVE_GROUP_NODE, &vnc_nve_group_rd_cmd);
+ install_element(BGP_VNC_NVE_GROUP_NODE,
+ &vnc_nve_group_responselifetime_cmd);
+ install_element(BGP_VNC_NVE_GROUP_NODE,
+ &vnc_nve_group_export_prefixlist_cmd);
+ install_element(BGP_VNC_NVE_GROUP_NODE,
+ &vnc_nve_group_export_routemap_cmd);
+ install_element(BGP_VNC_NVE_GROUP_NODE,
+ &vnc_nve_group_export_no_prefixlist_cmd);
+ install_element(BGP_VNC_NVE_GROUP_NODE,
+ &vnc_nve_group_export_no_routemap_cmd);
+ install_element(BGP_VNC_NVE_GROUP_NODE, &exit_vnc_cmd);
+
+ install_element(BGP_VRF_POLICY_NODE, &vnc_vrf_policy_label_cmd);
+ install_element(BGP_VRF_POLICY_NODE, &vnc_vrf_policy_no_label_cmd);
+ // Reenable to support VRF controller use case and testing
+ install_element(BGP_VRF_POLICY_NODE, &vnc_vrf_policy_nexthop_cmd);
+ install_element(BGP_VRF_POLICY_NODE, &vnc_vrf_policy_rt_import_cmd);
+ install_element(BGP_VRF_POLICY_NODE, &vnc_vrf_policy_rt_export_cmd);
+ install_element(BGP_VRF_POLICY_NODE, &vnc_vrf_policy_rt_both_cmd);
+ install_element(BGP_VRF_POLICY_NODE, &vnc_vrf_policy_rd_cmd);
+ install_element(BGP_VRF_POLICY_NODE,
+ &vnc_vrf_policy_export_prefixlist_cmd);
+ install_element(BGP_VRF_POLICY_NODE,
+ &vnc_vrf_policy_export_routemap_cmd);
+ install_element(BGP_VRF_POLICY_NODE,
+ &vnc_vrf_policy_export_no_prefixlist_cmd);
+ install_element(BGP_VRF_POLICY_NODE,
+ &vnc_vrf_policy_export_no_routemap_cmd);
+ install_element(BGP_VRF_POLICY_NODE, &exit_vrf_policy_cmd);
+
+ install_element(BGP_VNC_L2_GROUP_NODE, &vnc_l2_group_lni_cmd);
+ install_element(BGP_VNC_L2_GROUP_NODE, &vnc_l2_group_labels_cmd);
+ install_element(BGP_VNC_L2_GROUP_NODE, &vnc_l2_group_no_labels_cmd);
+ install_element(BGP_VNC_L2_GROUP_NODE, &vnc_l2_group_rt_cmd);
+ install_element(BGP_VNC_L2_GROUP_NODE, &exit_vnc_cmd);
+}
+
+struct rfapi_cfg *bgp_rfapi_cfg_new(struct rfapi_rfp_cfg *cfg)
+{
+ struct rfapi_cfg *h;
+ afi_t afi;
+
+ h = XCALLOC(MTYPE_RFAPI_CFG, sizeof(struct rfapi_cfg));
+ assert(h);
+
+ h->nve_groups_sequential = list_new();
+ assert(h->nve_groups_sequential);
+ for (afi = AFI_IP; afi < AFI_MAX; afi++) {
+ h->nve_groups_vn[afi] = agg_table_init();
+ h->nve_groups_un[afi] = agg_table_init();
+ }
+ h->default_response_lifetime =
+ BGP_VNC_DEFAULT_RESPONSE_LIFETIME_DEFAULT;
+ h->rfg_export_direct_bgp_l = list_new();
+ h->rfg_export_zebra_l = list_new();
+ h->resolve_nve_roo_local_admin =
+ BGP_VNC_CONFIG_RESOLVE_NVE_ROO_LOCAL_ADMIN_DEFAULT;
+
+ SET_FLAG(h->flags, BGP_VNC_CONFIG_FLAGS_DEFAULT);
+
+ if (cfg == NULL) {
+ h->rfp_cfg.download_type = RFAPI_RFP_DOWNLOAD_PARTIAL;
+ h->rfp_cfg.ftd_advertisement_interval =
+ RFAPI_RFP_CFG_DEFAULT_FTD_ADVERTISEMENT_INTERVAL;
+ h->rfp_cfg.holddown_factor =
+ RFAPI_RFP_CFG_DEFAULT_HOLDDOWN_FACTOR;
+ h->rfp_cfg.use_updated_response = 0;
+ h->rfp_cfg.use_removes = 0;
+ } else {
+ h->rfp_cfg.download_type = cfg->download_type;
+ h->rfp_cfg.ftd_advertisement_interval =
+ cfg->ftd_advertisement_interval;
+ h->rfp_cfg.holddown_factor = cfg->holddown_factor;
+ h->rfp_cfg.use_updated_response = cfg->use_updated_response;
+ h->rfp_cfg.use_removes = cfg->use_removes;
+ if (cfg->use_updated_response)
+ h->flags &= ~BGP_VNC_CONFIG_CALLBACK_DISABLE;
+ else
+ h->flags |= BGP_VNC_CONFIG_CALLBACK_DISABLE;
+ if (cfg->use_removes)
+ h->flags &= ~BGP_VNC_CONFIG_RESPONSE_REMOVAL_DISABLE;
+ else
+ h->flags |= BGP_VNC_CONFIG_RESPONSE_REMOVAL_DISABLE;
+ }
+ return h;
+}
+
+static void bgp_rfapi_rfgn_list_delete(void *data)
+{
+ struct rfapi_rfg_name *rfgn = data;
+ free(rfgn->name);
+ rfgn_free(rfgn);
+}
+
+void bgp_rfapi_cfg_destroy(struct bgp *bgp, struct rfapi_cfg *h)
+{
+ afi_t afi;
+ if (h == NULL)
+ return;
+
+ bgp_rfapi_delete_named_nve_group(NULL, bgp, NULL, RFAPI_GROUP_CFG_MAX);
+ bgp_rfapi_delete_named_l2_group(NULL, bgp, NULL);
+ if (h->l2_groups != NULL)
+ list_delete(&h->l2_groups);
+ list_delete(&h->nve_groups_sequential);
+
+ h->rfg_export_direct_bgp_l->del = bgp_rfapi_rfgn_list_delete;
+ list_delete(&h->rfg_export_direct_bgp_l);
+
+ h->rfg_export_zebra_l->del = bgp_rfapi_rfgn_list_delete;
+ list_delete(&h->rfg_export_zebra_l);
+
+ if (h->default_rt_export_list)
+ ecommunity_free(&h->default_rt_export_list);
+ if (h->default_rt_import_list)
+ ecommunity_free(&h->default_rt_import_list);
+ XFREE(MTYPE_RFAPI_RFP_GROUP_CFG, h->default_rfp_cfg);
+ for (afi = AFI_IP; afi < AFI_MAX; afi++) {
+ agg_table_finish(h->nve_groups_vn[afi]);
+ agg_table_finish(h->nve_groups_un[afi]);
+ }
+ XFREE(MTYPE_RFAPI_CFG, h);
+}
+
+int bgp_rfapi_cfg_write(struct vty *vty, struct bgp *bgp)
+{
+ struct listnode *node, *nnode;
+ struct rfapi_nve_group_cfg *rfg;
+ struct rfapi_cfg *hc = bgp->rfapi_cfg;
+ struct rfapi_rfg_name *rfgn;
+ int write = 0;
+ afi_t afi;
+ int type;
+ if (bgp->rfapi == NULL || hc == NULL)
+ return write;
+
+ vty_out(vty, "!\n");
+ for (ALL_LIST_ELEMENTS(hc->nve_groups_sequential, node, nnode, rfg))
+ if (rfg->type == RFAPI_GROUP_CFG_VRF) {
+ ++write;
+ vty_out(vty, " vrf-policy %s\n", rfg->name);
+ if (rfg->label <= MPLS_LABEL_MAX) {
+ vty_out(vty, " label %u\n", rfg->label);
+ }
+ if (CHECK_FLAG(rfg->flags, RFAPI_RFG_VPN_NH_SELF)) {
+ vty_out(vty, " nexthop self\n");
+
+ } else {
+ if (rfg->vn_prefix.family) {
+ char buf[BUFSIZ];
+ buf[0] = buf[BUFSIZ - 1] = 0;
+ inet_ntop(rfg->vn_prefix.family,
+ &rfg->vn_prefix.u.prefix, buf,
+ sizeof(buf));
+ if (!buf[0] || buf[BUFSIZ - 1]) {
+ // vty_out (vty, "nexthop
+ // self\n");
+ } else {
+ vty_out(vty, " nexthop %s\n",
+ buf);
+ }
+ }
+ }
+
+ if (rfg->rd.prefixlen) {
+ if (AF_UNIX == rfg->rd.family) {
+
+ uint16_t value = 0;
+
+ value = ((rfg->rd.val[6] << 8)
+ & 0x0ff00)
+ | (rfg->rd.val[7] & 0x0ff);
+
+ vty_out(vty, " rd auto:nh:%d\n",
+ value);
+
+ } else
+ vty_out(vty, " rd %pRD\n", &rfg->rd);
+ }
+
+ if (rfg->rt_import_list && rfg->rt_export_list
+ && ecommunity_cmp(rfg->rt_import_list,
+ rfg->rt_export_list)) {
+ char *b = ecommunity_ecom2str(
+ rfg->rt_import_list,
+ ECOMMUNITY_FORMAT_ROUTE_MAP,
+ ECOMMUNITY_ROUTE_TARGET);
+ vty_out(vty, " rt both %s\n", b);
+ XFREE(MTYPE_ECOMMUNITY_STR, b);
+ } else {
+ if (rfg->rt_import_list) {
+ char *b = ecommunity_ecom2str(
+ rfg->rt_import_list,
+ ECOMMUNITY_FORMAT_ROUTE_MAP,
+ ECOMMUNITY_ROUTE_TARGET);
+ vty_out(vty, " rt import %s\n", b);
+ XFREE(MTYPE_ECOMMUNITY_STR, b);
+ }
+ if (rfg->rt_export_list) {
+ char *b = ecommunity_ecom2str(
+ rfg->rt_export_list,
+ ECOMMUNITY_FORMAT_ROUTE_MAP,
+ ECOMMUNITY_ROUTE_TARGET);
+ vty_out(vty, " rt export %s\n", b);
+ XFREE(MTYPE_ECOMMUNITY_STR, b);
+ }
+ }
+
+ /*
+ * route filtering: prefix-lists and route-maps
+ */
+ for (afi = AFI_IP; afi < AFI_MAX; ++afi) {
+
+ const char *afistr =
+ (afi == AFI_IP) ? "ipv4" : "ipv6";
+
+ if (rfg->plist_export_bgp_name[afi]) {
+ vty_out(vty,
+ " export %s%s prefix-list %s\n",
+ (rfg->type == RFAPI_GROUP_CFG_VRF
+ ? ""
+ : "bgp "),
+ afistr,
+ rfg->plist_export_bgp_name
+ [afi]);
+ }
+ if (rfg->plist_export_zebra_name[afi]) {
+ vty_out(vty,
+ " export %s%s prefix-list %s\n",
+ (rfg->type == RFAPI_GROUP_CFG_VRF
+ ? ""
+ : "zebra "),
+ afistr,
+ rfg->plist_export_zebra_name
+ [afi]);
+ }
+ /*
+ * currently we only support redist plists for
+ * bgp-direct.
+ * If we later add plist support for
+ * redistributing other
+ * protocols, we'll need to loop over protocols
+ * here
+ */
+ if (rfg->plist_redist_name
+ [ZEBRA_ROUTE_BGP_DIRECT][afi]) {
+ vty_out(vty,
+ " redistribute bgp-direct %s prefix-list %s\n",
+ afistr,
+ rfg->plist_redist_name
+ [ZEBRA_ROUTE_BGP_DIRECT]
+ [afi]);
+ }
+ if (rfg->plist_redist_name
+ [ZEBRA_ROUTE_BGP_DIRECT_EXT][afi]) {
+ vty_out(vty,
+ " redistribute bgp-direct-to-nve-groups %s prefix-list %s\n",
+ afistr,
+ rfg->plist_redist_name
+ [ZEBRA_ROUTE_BGP_DIRECT_EXT]
+ [afi]);
+ }
+ }
+
+ if (rfg->routemap_export_bgp_name) {
+ vty_out(vty, " export %sroute-map %s\n",
+ (rfg->type == RFAPI_GROUP_CFG_VRF
+ ? ""
+ : "bgp "),
+ rfg->routemap_export_bgp_name);
+ }
+ if (rfg->routemap_export_zebra_name) {
+ vty_out(vty, " export %sroute-map %s\n",
+ (rfg->type == RFAPI_GROUP_CFG_VRF
+ ? ""
+ : "zebra "),
+ rfg->routemap_export_zebra_name);
+ }
+ if (rfg->routemap_redist_name[ZEBRA_ROUTE_BGP_DIRECT]) {
+ vty_out(vty,
+ " redistribute bgp-direct route-map %s\n",
+ rfg->routemap_redist_name
+ [ZEBRA_ROUTE_BGP_DIRECT]);
+ }
+ if (rfg->routemap_redist_name
+ [ZEBRA_ROUTE_BGP_DIRECT_EXT]) {
+ vty_out(vty,
+ " redistribute bgp-direct-to-nve-groups route-map %s\n",
+ rfg->routemap_redist_name
+ [ZEBRA_ROUTE_BGP_DIRECT_EXT]);
+ }
+ vty_out(vty, " exit-vrf-policy\n");
+ vty_out(vty, "!\n");
+ }
+ if (hc->flags & BGP_VNC_CONFIG_ADV_UN_METHOD_ENCAP) {
+ vty_out(vty, " vnc advertise-un-method encap-safi\n");
+ write++;
+ }
+
+ { /* was based on listen ports */
+ /* for now allow both old and new */
+ if (bgp->rfapi->rfp_methods.cfg_cb)
+ write += (bgp->rfapi->rfp_methods.cfg_cb)(
+ vty, bgp->rfapi->rfp);
+
+ if (write)
+ vty_out(vty, "!\n");
+
+ if (hc->l2_groups) {
+ struct rfapi_l2_group_cfg *rfgc = NULL;
+ struct listnode *gnode;
+ for (ALL_LIST_ELEMENTS_RO(hc->l2_groups, gnode, rfgc)) {
+ struct listnode *lnode;
+ void *data;
+ ++write;
+ vty_out(vty, " vnc l2-group %s\n", rfgc->name);
+ if (rfgc->logical_net_id != 0)
+ vty_out(vty,
+ " logical-network-id %u\n",
+ rfgc->logical_net_id);
+ if (rfgc->labels != NULL
+ && listhead(rfgc->labels) != NULL) {
+ vty_out(vty, " labels ");
+ for (ALL_LIST_ELEMENTS_RO(rfgc->labels,
+ lnode,
+ data)) {
+ vty_out(vty, "%hu ",
+ (uint16_t)(
+ (uintptr_t)
+ data));
+ }
+ vty_out(vty, "\n");
+ }
+
+ if (rfgc->rt_import_list && rfgc->rt_export_list
+ && ecommunity_cmp(rfgc->rt_import_list,
+ rfgc->rt_export_list)) {
+ char *b = ecommunity_ecom2str(
+ rfgc->rt_import_list,
+ ECOMMUNITY_FORMAT_ROUTE_MAP,
+ ECOMMUNITY_ROUTE_TARGET);
+ vty_out(vty, " rt both %s\n", b);
+ XFREE(MTYPE_ECOMMUNITY_STR, b);
+ } else {
+ if (rfgc->rt_import_list) {
+ char *b = ecommunity_ecom2str(
+ rfgc->rt_import_list,
+ ECOMMUNITY_FORMAT_ROUTE_MAP,
+ ECOMMUNITY_ROUTE_TARGET);
+ vty_out(vty, " rt import %s\n",
+ b);
+ XFREE(MTYPE_ECOMMUNITY_STR, b);
+ }
+ if (rfgc->rt_export_list) {
+ char *b = ecommunity_ecom2str(
+ rfgc->rt_export_list,
+ ECOMMUNITY_FORMAT_ROUTE_MAP,
+ ECOMMUNITY_ROUTE_TARGET);
+ vty_out(vty, " rt export %s\n",
+ b);
+ XFREE(MTYPE_ECOMMUNITY_STR, b);
+ }
+ }
+ if (bgp->rfapi->rfp_methods.cfg_group_cb)
+ write += (bgp->rfapi->rfp_methods
+ .cfg_group_cb)(
+ vty, bgp->rfapi->rfp,
+ RFAPI_RFP_CFG_GROUP_L2,
+ rfgc->name, rfgc->rfp_cfg);
+ vty_out(vty, " exit-vnc\n");
+ vty_out(vty, "!\n");
+ }
+ }
+
+ if (hc->default_rd.prefixlen
+ || hc->default_response_lifetime
+ != BGP_VNC_DEFAULT_RESPONSE_LIFETIME_DEFAULT
+ || hc->default_rt_import_list || hc->default_rt_export_list
+ || hc->nve_groups_sequential->count) {
+
+
+ ++write;
+ vty_out(vty, " vnc defaults\n");
+
+ if (hc->default_rd.prefixlen) {
+ if (AF_UNIX == hc->default_rd.family) {
+ uint16_t value = 0;
+
+ value = ((hc->default_rd.val[6] << 8)
+ & 0x0ff00)
+ | (hc->default_rd.val[7]
+ & 0x0ff);
+
+ vty_out(vty, " rd auto:vn:%d\n",
+ value);
+
+ } else
+ vty_out(vty, " rd %pRD\n",
+ &hc->default_rd);
+ }
+ if (hc->default_response_lifetime
+ != BGP_VNC_DEFAULT_RESPONSE_LIFETIME_DEFAULT) {
+ vty_out(vty, " response-lifetime ");
+ if (hc->default_response_lifetime != UINT32_MAX)
+ vty_out(vty, "%d",
+ hc->default_response_lifetime);
+ else
+ vty_out(vty, "infinite");
+ vty_out(vty, "\n");
+ }
+ if (hc->default_rt_import_list
+ && hc->default_rt_export_list
+ && ecommunity_cmp(hc->default_rt_import_list,
+ hc->default_rt_export_list)) {
+ char *b = ecommunity_ecom2str(
+ hc->default_rt_import_list,
+ ECOMMUNITY_FORMAT_ROUTE_MAP,
+ ECOMMUNITY_ROUTE_TARGET);
+ vty_out(vty, " rt both %s\n", b);
+ XFREE(MTYPE_ECOMMUNITY_STR, b);
+ } else {
+ if (hc->default_rt_import_list) {
+ char *b = ecommunity_ecom2str(
+ hc->default_rt_import_list,
+ ECOMMUNITY_FORMAT_ROUTE_MAP,
+ ECOMMUNITY_ROUTE_TARGET);
+ vty_out(vty, " rt import %s\n", b);
+ XFREE(MTYPE_ECOMMUNITY_STR, b);
+ }
+ if (hc->default_rt_export_list) {
+ char *b = ecommunity_ecom2str(
+ hc->default_rt_export_list,
+ ECOMMUNITY_FORMAT_ROUTE_MAP,
+ ECOMMUNITY_ROUTE_TARGET);
+ vty_out(vty, " rt export %s\n", b);
+ XFREE(MTYPE_ECOMMUNITY_STR, b);
+ }
+ }
+ if (bgp->rfapi->rfp_methods.cfg_group_cb)
+ write += (bgp->rfapi->rfp_methods.cfg_group_cb)(
+ vty, bgp->rfapi->rfp,
+ RFAPI_RFP_CFG_GROUP_DEFAULT, NULL,
+ bgp->rfapi_cfg->default_rfp_cfg);
+ vty_out(vty, " exit-vnc\n");
+ vty_out(vty, "!\n");
+ }
+
+ for (ALL_LIST_ELEMENTS(hc->nve_groups_sequential, node, nnode,
+ rfg))
+ if (rfg->type == RFAPI_GROUP_CFG_NVE) {
+ ++write;
+ vty_out(vty, " vnc nve-group %s\n", rfg->name);
+
+ if (rfg->vn_prefix.family && rfg->vn_node)
+ vty_out(vty, " prefix %s %pFX\n", "vn",
+ &rfg->vn_prefix);
+
+ if (rfg->un_prefix.family && rfg->un_node)
+ vty_out(vty, " prefix %s %pFX\n", "un",
+ &rfg->un_prefix);
+
+
+ if (rfg->rd.prefixlen) {
+ if (AF_UNIX == rfg->rd.family) {
+
+ uint16_t value = 0;
+
+ value = ((rfg->rd.val[6] << 8)
+ & 0x0ff00)
+ | (rfg->rd.val[7]
+ & 0x0ff);
+
+ vty_out(vty,
+ " rd auto:vn:%d\n",
+ value);
+
+ } else
+ vty_out(vty, " rd %pRD\n",
+ &rfg->rd);
+ }
+ if (rfg->flags & RFAPI_RFG_RESPONSE_LIFETIME) {
+ vty_out(vty, " response-lifetime ");
+ if (rfg->response_lifetime
+ != UINT32_MAX)
+ vty_out(vty, "%d",
+ rfg->response_lifetime);
+ else
+ vty_out(vty, "infinite");
+ vty_out(vty, "\n");
+ }
+
+ if (rfg->rt_import_list && rfg->rt_export_list
+ && ecommunity_cmp(rfg->rt_import_list,
+ rfg->rt_export_list)) {
+ char *b = ecommunity_ecom2str(
+ rfg->rt_import_list,
+ ECOMMUNITY_FORMAT_ROUTE_MAP,
+ ECOMMUNITY_ROUTE_TARGET);
+ vty_out(vty, " rt both %s\n", b);
+ XFREE(MTYPE_ECOMMUNITY_STR, b);
+ } else {
+ if (rfg->rt_import_list) {
+ char *b = ecommunity_ecom2str(
+ rfg->rt_import_list,
+ ECOMMUNITY_FORMAT_ROUTE_MAP,
+ ECOMMUNITY_ROUTE_TARGET);
+ vty_out(vty, " rt import %s\n",
+ b);
+ XFREE(MTYPE_ECOMMUNITY_STR, b);
+ }
+ if (rfg->rt_export_list) {
+ char *b = ecommunity_ecom2str(
+ rfg->rt_export_list,
+ ECOMMUNITY_FORMAT_ROUTE_MAP,
+ ECOMMUNITY_ROUTE_TARGET);
+ vty_out(vty, " rt export %s\n",
+ b);
+ XFREE(MTYPE_ECOMMUNITY_STR, b);
+ }
+ }
+
+ /*
+ * route filtering: prefix-lists and route-maps
+ */
+ for (afi = AFI_IP; afi < AFI_MAX; ++afi) {
+
+ const char *afistr = (afi == AFI_IP)
+ ? "ipv4"
+ : "ipv6";
+
+ if (rfg->plist_export_bgp_name[afi]) {
+ vty_out(vty,
+ " export bgp %s prefix-list %s\n",
+ afistr,
+ rfg->plist_export_bgp_name
+ [afi]);
+ }
+ if (rfg->plist_export_zebra_name[afi]) {
+ vty_out(vty,
+ " export zebra %s prefix-list %s\n",
+ afistr,
+ rfg->plist_export_zebra_name
+ [afi]);
+ }
+ /*
+ * currently we only support redist
+ * plists for bgp-direct.
+ * If we later add plist support for
+ * redistributing other
+ * protocols, we'll need to loop over
+ * protocols here
+ */
+ if (rfg->plist_redist_name
+ [ZEBRA_ROUTE_BGP_DIRECT]
+ [afi]) {
+ vty_out(vty,
+ " redistribute bgp-direct %s prefix-list %s\n",
+ afistr,
+ rfg->plist_redist_name
+ [ZEBRA_ROUTE_BGP_DIRECT]
+ [afi]);
+ }
+ if (rfg->plist_redist_name
+ [ZEBRA_ROUTE_BGP_DIRECT_EXT]
+ [afi]) {
+ vty_out(vty,
+ " redistribute bgp-direct-to-nve-groups %s prefix-list %s\n",
+ afistr,
+ rfg->plist_redist_name
+ [ZEBRA_ROUTE_BGP_DIRECT_EXT]
+ [afi]);
+ }
+ }
+
+ if (rfg->routemap_export_bgp_name) {
+ vty_out(vty,
+ " export bgp route-map %s\n",
+ rfg->routemap_export_bgp_name);
+ }
+ if (rfg->routemap_export_zebra_name) {
+ vty_out(vty,
+ " export zebra route-map %s\n",
+ rfg->routemap_export_zebra_name);
+ }
+ if (rfg->routemap_redist_name
+ [ZEBRA_ROUTE_BGP_DIRECT]) {
+ vty_out(vty,
+ " redistribute bgp-direct route-map %s\n",
+ rfg->routemap_redist_name
+ [ZEBRA_ROUTE_BGP_DIRECT]);
+ }
+ if (rfg->routemap_redist_name
+ [ZEBRA_ROUTE_BGP_DIRECT_EXT]) {
+ vty_out(vty,
+ " redistribute bgp-direct-to-nve-groups route-map %s\n",
+ rfg->routemap_redist_name
+ [ZEBRA_ROUTE_BGP_DIRECT_EXT]);
+ }
+ if (bgp->rfapi->rfp_methods.cfg_group_cb)
+ write += (bgp->rfapi->rfp_methods
+ .cfg_group_cb)(
+ vty, bgp->rfapi->rfp,
+ RFAPI_RFP_CFG_GROUP_NVE,
+ rfg->name, rfg->rfp_cfg);
+ vty_out(vty, " exit-vnc\n");
+ vty_out(vty, "!\n");
+ }
+ } /* have listen ports */
+
+ /*
+ * route export to other protocols
+ */
+ if (VNC_EXPORT_BGP_GRP_ENABLED(hc)) {
+ vty_out(vty, " vnc export bgp mode group-nve\n");
+ } else if (VNC_EXPORT_BGP_RH_ENABLED(hc)) {
+ vty_out(vty, " vnc export bgp mode registering-nve\n");
+ } else if (VNC_EXPORT_BGP_CE_ENABLED(hc)) {
+ vty_out(vty, " vnc export bgp mode ce\n");
+ }
+
+ if (VNC_EXPORT_ZEBRA_GRP_ENABLED(hc)) {
+ vty_out(vty, " vnc export zebra mode group-nve\n");
+ } else if (VNC_EXPORT_ZEBRA_RH_ENABLED(hc)) {
+ vty_out(vty, " vnc export zebra mode registering-nve\n");
+ }
+
+ if (hc->rfg_export_direct_bgp_l) {
+ for (ALL_LIST_ELEMENTS(hc->rfg_export_direct_bgp_l, node, nnode,
+ rfgn)) {
+
+ vty_out(vty, " vnc export bgp group-nve group %s\n",
+ rfgn->name);
+ }
+ }
+
+ if (hc->rfg_export_zebra_l) {
+ for (ALL_LIST_ELEMENTS(hc->rfg_export_zebra_l, node, nnode,
+ rfgn)) {
+
+ vty_out(vty, " vnc export zebra group-nve group %s\n",
+ rfgn->name);
+ }
+ }
+
+
+ if (hc->rfg_redist_name) {
+ vty_out(vty, " vnc redistribute nve-group %s\n",
+ hc->rfg_redist_name);
+ }
+ if (hc->redist_lifetime) {
+ vty_out(vty, " vnc redistribute lifetime %d\n",
+ hc->redist_lifetime);
+ }
+ if (hc->resolve_nve_roo_local_admin
+ != BGP_VNC_CONFIG_RESOLVE_NVE_ROO_LOCAL_ADMIN_DEFAULT) {
+
+ vty_out(vty,
+ " vnc redistribute resolve-nve roo-ec-local-admin %d\n",
+ hc->resolve_nve_roo_local_admin);
+ }
+
+ if (hc->redist_mode) /* ! default */
+ {
+ const char *s = "";
+
+ switch (hc->redist_mode) {
+ case VNC_REDIST_MODE_PLAIN:
+ s = "plain";
+ break;
+ case VNC_REDIST_MODE_RFG:
+ s = "nve-group";
+ break;
+ case VNC_REDIST_MODE_RESOLVE_NVE:
+ s = "resolve-nve";
+ break;
+ }
+ if (s) {
+ vty_out(vty, " vnc redistribute mode %s\n", s);
+ }
+ }
+
+ /*
+ * route filtering: prefix-lists and route-maps
+ */
+ for (afi = AFI_IP; afi < AFI_MAX; ++afi) {
+
+ const char *afistr = (afi == AFI_IP) ? "ipv4" : "ipv6";
+
+ if (hc->plist_export_bgp_name[afi]) {
+ vty_out(vty, " vnc export bgp %s prefix-list %s\n",
+ afistr, hc->plist_export_bgp_name[afi]);
+ }
+ if (hc->plist_export_zebra_name[afi]) {
+ vty_out(vty, " vnc export zebra %s prefix-list %s\n",
+ afistr, hc->plist_export_zebra_name[afi]);
+ }
+ if (hc->plist_redist_name[ZEBRA_ROUTE_BGP_DIRECT][afi]) {
+ vty_out(vty,
+ " vnc redistribute bgp-direct %s prefix-list %s\n",
+ afistr,
+ hc->plist_redist_name[ZEBRA_ROUTE_BGP_DIRECT]
+ [afi]);
+ }
+ }
+
+ if (hc->routemap_export_bgp_name) {
+ vty_out(vty, " vnc export bgp route-map %s\n",
+ hc->routemap_export_bgp_name);
+ }
+ if (hc->routemap_export_zebra_name) {
+ vty_out(vty, " vnc export zebra route-map %s\n",
+ hc->routemap_export_zebra_name);
+ }
+ if (hc->routemap_redist_name[ZEBRA_ROUTE_BGP_DIRECT]) {
+ vty_out(vty, " vnc redistribute bgp-direct route-map %s\n",
+ hc->routemap_redist_name[ZEBRA_ROUTE_BGP_DIRECT]);
+ }
+
+ for (afi = AFI_IP; afi < AFI_MAX; ++afi) {
+ for (type = 0; type < ZEBRA_ROUTE_MAX; ++type) {
+ if (hc->redist[afi][type]) {
+ if (type == ZEBRA_ROUTE_BGP_DIRECT_EXT
+ && hc->redist_bgp_exterior_view_name) {
+ vty_out(vty,
+ " vnc redistribute %s %s view %s\n",
+ ((afi == AFI_IP) ? "ipv4"
+ : "ipv6"),
+ zebra_route_string(type),
+ hc->redist_bgp_exterior_view_name);
+ } else {
+ vty_out(vty,
+ " vnc redistribute %s %s\n",
+ ((afi == AFI_IP) ? "ipv4"
+ : "ipv6"),
+ zebra_route_string(type));
+ }
+ }
+ }
+ }
+ return write;
+}
+
+void bgp_rfapi_show_summary(struct bgp *bgp, struct vty *vty)
+{
+ struct rfapi_cfg *hc = bgp->rfapi_cfg;
+ afi_t afi;
+ int type, redist = 0;
+ char tmp[40];
+ if (hc == NULL)
+ return;
+
+ vty_out(vty, "%-39s %-19s %s\n", "VNC Advertise method:",
+ (hc->flags & BGP_VNC_CONFIG_ADV_UN_METHOD_ENCAP
+ ? "Encapsulation SAFI"
+ : "Tunnel Encap attribute"),
+ ((hc->flags & BGP_VNC_CONFIG_ADV_UN_METHOD_ENCAP)
+ == (BGP_VNC_CONFIG_ADV_UN_METHOD_ENCAP
+ & BGP_VNC_CONFIG_FLAGS_DEFAULT)
+ ? "(default)"
+ : ""));
+ /* export */
+ vty_out(vty, "%-39s ", "Export from VNC:");
+ /*
+ * route export to other protocols
+ */
+ if (VNC_EXPORT_BGP_GRP_ENABLED(hc)) {
+ redist++;
+ vty_out(vty, "ToBGP Groups={");
+ if (hc->rfg_export_direct_bgp_l) {
+ int cnt = 0;
+ struct listnode *node, *nnode;
+ struct rfapi_rfg_name *rfgn;
+ for (ALL_LIST_ELEMENTS(hc->rfg_export_direct_bgp_l,
+ node, nnode, rfgn)) {
+ if (cnt++ != 0)
+ vty_out(vty, ",");
+
+ vty_out(vty, "%s", rfgn->name);
+ }
+ }
+ vty_out(vty, "}");
+ } else if (VNC_EXPORT_BGP_RH_ENABLED(hc)) {
+ redist++;
+ vty_out(vty, "ToBGP {Registering NVE}");
+ /* note filters, route-maps not shown */
+ } else if (VNC_EXPORT_BGP_CE_ENABLED(hc)) {
+ redist++;
+ vty_out(vty, "ToBGP {NVE connected router:%d}",
+ hc->resolve_nve_roo_local_admin);
+ /* note filters, route-maps not shown */
+ }
+
+ if (VNC_EXPORT_ZEBRA_GRP_ENABLED(hc)) {
+ redist++;
+ vty_out(vty, "%sToZebra Groups={", (redist == 1 ? "" : " "));
+ if (hc->rfg_export_zebra_l) {
+ int cnt = 0;
+ struct listnode *node, *nnode;
+ struct rfapi_rfg_name *rfgn;
+ for (ALL_LIST_ELEMENTS(hc->rfg_export_zebra_l, node,
+ nnode, rfgn)) {
+ if (cnt++ != 0)
+ vty_out(vty, ",");
+ vty_out(vty, "%s", rfgn->name);
+ }
+ }
+ vty_out(vty, "}");
+ } else if (VNC_EXPORT_ZEBRA_RH_ENABLED(hc)) {
+ redist++;
+ vty_out(vty, "%sToZebra {Registering NVE}",
+ (redist == 1 ? "" : " "));
+ /* note filters, route-maps not shown */
+ }
+ vty_out(vty, "%-19s %s\n", (redist ? "" : "Off"),
+ (redist ? "" : "(default)"));
+
+ /* Redistribution */
+ redist = 0;
+ vty_out(vty, "%-39s ", "Redistribution into VNC:");
+ for (afi = AFI_IP; afi < AFI_MAX; ++afi) {
+ for (type = 0; type < ZEBRA_ROUTE_MAX; ++type) {
+ if (hc->redist[afi][type]) {
+ vty_out(vty, "{%s,%s} ",
+ ((afi == AFI_IP) ? "ipv4" : "ipv6"),
+ zebra_route_string(type));
+ redist++;
+ }
+ }
+ }
+ vty_out(vty, "%-19s %s\n", (redist ? "" : "Off"),
+ (redist ? "" : "(default)"));
+
+ vty_out(vty, "%-39s %3u%-16s %s\n",
+ "RFP Registration Hold-Down Factor:",
+ hc->rfp_cfg.holddown_factor, "%",
+ (hc->rfp_cfg.holddown_factor
+ == RFAPI_RFP_CFG_DEFAULT_HOLDDOWN_FACTOR
+ ? "(default)"
+ : ""));
+ vty_out(vty, "%-39s %-19s %s\n", "RFP Updated responses:",
+ (hc->rfp_cfg.use_updated_response == 0 ? "Off" : "On"),
+ (hc->rfp_cfg.use_updated_response == 0 ? "(default)" : ""));
+ vty_out(vty, "%-39s %-19s %s\n", "RFP Removal responses:",
+ (hc->rfp_cfg.use_removes == 0 ? "Off" : "On"),
+ (hc->rfp_cfg.use_removes == 0 ? "(default)" : ""));
+ vty_out(vty, "%-39s %-19s %s\n", "RFP Full table download:",
+ (hc->rfp_cfg.download_type == RFAPI_RFP_DOWNLOAD_FULL ? "On"
+ : "Off"),
+ (hc->rfp_cfg.download_type == RFAPI_RFP_DOWNLOAD_PARTIAL
+ ? "(default)"
+ : ""));
+ snprintf(tmp, sizeof(tmp), "%u seconds",
+ hc->rfp_cfg.ftd_advertisement_interval);
+ vty_out(vty, "%-39s %-19s %s\n", " Advertisement Interval:", tmp,
+ (hc->rfp_cfg.ftd_advertisement_interval
+ == RFAPI_RFP_CFG_DEFAULT_FTD_ADVERTISEMENT_INTERVAL
+ ? "(default)"
+ : ""));
+ vty_out(vty, "%-39s %d seconds\n", "Default RFP response lifetime:",
+ hc->default_response_lifetime);
+ vty_out(vty, "\n");
+ return;
+}
+
+struct rfapi_cfg *bgp_rfapi_get_config(struct bgp *bgp)
+{
+ struct rfapi_cfg *hc = NULL;
+ if (bgp == NULL)
+ bgp = bgp_get_default();
+ if (bgp != NULL)
+ hc = bgp->rfapi_cfg;
+ return hc;
+}
+
+#endif /* ENABLE_BGP_VNC */
diff --git a/bgpd/rfapi/bgp_rfapi_cfg.h b/bgpd/rfapi/bgp_rfapi_cfg.h
new file mode 100644
index 0000000..ef97419
--- /dev/null
+++ b/bgpd/rfapi/bgp_rfapi_cfg.h
@@ -0,0 +1,315 @@
+/*
+ *
+ * Copyright 2009-2016, LabN Consulting, L.L.C.
+ *
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program; see the file COPYING; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
+ */
+
+#ifndef _QUAGGA_BGP_RFAPI_CFG_H
+#define _QUAGGA_BGP_RFAPI_CFG_H
+
+#include "lib/table.h"
+#include "lib/routemap.h"
+
+#ifdef ENABLE_BGP_VNC
+#include "rfapi.h"
+
+struct rfapi_l2_group_cfg {
+ char *name;
+ uint32_t logical_net_id;
+ struct list *labels; /* list of uint32_t */
+ struct ecommunity *rt_import_list;
+ struct ecommunity *rt_export_list;
+ void *rfp_cfg; /* rfp owned group config */
+
+ QOBJ_FIELDS;
+};
+DECLARE_QOBJ_TYPE(rfapi_l2_group_cfg);
+
+typedef enum {
+ RFAPI_GROUP_CFG_NVE = 1,
+ RFAPI_GROUP_CFG_VRF,
+ RFAPI_GROUP_CFG_L2,
+ RFAPI_GROUP_CFG_MAX
+} rfapi_group_cfg_type_t;
+
+struct rfapi_nve_group_cfg {
+ struct agg_node *vn_node; /* backref */
+ struct agg_node *un_node; /* backref */
+
+ rfapi_group_cfg_type_t type; /* NVE|VPN */
+ char *name; /* unique by type! */
+ struct prefix vn_prefix;
+ struct prefix un_prefix;
+
+ struct prefix_rd rd;
+ uint8_t l2rd; /* 0 = VN addr LSB */
+ uint32_t response_lifetime;
+ uint32_t flags;
+#define RFAPI_RFG_RESPONSE_LIFETIME 0x01 /* bits */
+#define RFAPI_RFG_L2RD 0x02
+#define RFAPI_RFG_VPN_NH_SELF 0x04
+ struct ecommunity *rt_import_list;
+ struct ecommunity *rt_export_list;
+ struct rfapi_import_table *rfapi_import_table;
+
+ void *rfp_cfg; /* rfp owned group config */
+ /*
+ * List of NVE descriptors that are assigned to this NVE group
+ *
+ * Currently (Mar 2010) this list is used only by the route
+ * export code to generate per-NVE nexthops for each route.
+ *
+ * The nve descriptors listed here have pointers back to
+ * this nve group config structure to enable them to delete
+ * their own list entries when they are closed. Consequently,
+ * if an instance of this nve group config structure is deleted,
+ * we must first set the nve descriptor references to it to NULL.
+ */
+ struct list *nves;
+
+ /*
+ * Route filtering
+ *
+ * Prefix lists are segregated by afi (part of the base plist code)
+ * Route-maps are not segregated
+ */
+ char *plist_export_bgp_name[AFI_MAX];
+ struct prefix_list *plist_export_bgp[AFI_MAX];
+
+ char *plist_export_zebra_name[AFI_MAX];
+ struct prefix_list *plist_export_zebra[AFI_MAX];
+
+ char *plist_redist_name[ZEBRA_ROUTE_MAX][AFI_MAX];
+ struct prefix_list *plist_redist[ZEBRA_ROUTE_MAX][AFI_MAX];
+
+ char *routemap_export_bgp_name;
+ struct route_map *routemap_export_bgp;
+
+ char *routemap_export_zebra_name;
+ struct route_map *routemap_export_zebra;
+
+ char *routemap_redist_name[ZEBRA_ROUTE_MAX];
+ struct route_map *routemap_redist[ZEBRA_ROUTE_MAX];
+
+ /* for VRF type groups */
+ uint32_t label;
+ struct rfapi_descriptor *rfd;
+ QOBJ_FIELDS;
+};
+DECLARE_QOBJ_TYPE(rfapi_nve_group_cfg);
+
+struct rfapi_rfg_name {
+ struct rfapi_nve_group_cfg *rfg;
+ char *name;
+};
+
+typedef enum {
+ VNC_REDIST_MODE_PLAIN = 0, /* 0 = default */
+ VNC_REDIST_MODE_RFG,
+ VNC_REDIST_MODE_RESOLVE_NVE
+} vnc_redist_mode_t;
+
+struct rfapi_cfg {
+ struct prefix_rd default_rd;
+ uint8_t default_l2rd;
+ struct ecommunity *default_rt_import_list;
+ struct ecommunity *default_rt_export_list;
+ uint32_t default_response_lifetime;
+#define BGP_VNC_DEFAULT_RESPONSE_LIFETIME_DEFAULT 3600
+ void *default_rfp_cfg; /* rfp owned group config */
+
+ struct list *l2_groups; /* rfapi_l2_group_cfg list */
+ /* three views into the same collection of rfapi_nve_group_cfg */
+ struct list *nve_groups_sequential;
+ struct agg_table *nve_groups_vn[AFI_MAX];
+ struct agg_table *nve_groups_un[AFI_MAX];
+
+ /*
+ * For Single VRF export to ordinary routing protocols. This is
+ * the nve-group that the ordinary protocols belong to. We use it
+ * to set the RD when sending unicast Zebra routes to VNC
+ */
+ uint8_t redist[AFI_MAX][ZEBRA_ROUTE_MAX];
+ uint32_t redist_lifetime;
+ vnc_redist_mode_t redist_mode;
+
+ /*
+ * view name of BGP unicast instance that holds
+ * exterior routes
+ */
+ char *redist_bgp_exterior_view_name;
+ struct bgp *redist_bgp_exterior_view;
+
+ /*
+ * nve group for redistribution of routes from zebra to VNC
+ * (which is probably not useful for production networks)
+ */
+ char *rfg_redist_name;
+ struct rfapi_nve_group_cfg *rfg_redist;
+
+ /*
+ * List of NVE groups on whose behalf we will export VNC
+ * routes to zebra. ((NB: it's actually a list of <struct
+ * rfapi_rfg_name>)
+ * This list is used when BGP_VNC_CONFIG_EXPORT_ZEBRA_MODE_BITS is
+ * BGP_VNC_CONFIG_EXPORT_ZEBRA_MODE_GRP
+ */
+ struct list *rfg_export_zebra_l;
+
+ /*
+ * List of NVE groups on whose behalf we will export VNC
+ * routes directly to the bgp unicast RIB. (NB: it's actually
+ * a list of <struct rfapi_rfg_name>)
+ * This list is used when BGP_VNC_CONFIG_EXPORT_BGP_MODE_BITS is
+ * BGP_VNC_CONFIG_EXPORT_BGP_MODE_GRP
+ */
+ struct list *rfg_export_direct_bgp_l;
+
+ /*
+ * Exported Route filtering
+ *
+ * Prefix lists are segregated by afi (part of the base plist code)
+ * Route-maps are not segregated
+ */
+ char *plist_export_bgp_name[AFI_MAX];
+ struct prefix_list *plist_export_bgp[AFI_MAX];
+
+ char *plist_export_zebra_name[AFI_MAX];
+ struct prefix_list *plist_export_zebra[AFI_MAX];
+
+ char *routemap_export_bgp_name;
+ struct route_map *routemap_export_bgp;
+
+ char *routemap_export_zebra_name;
+ struct route_map *routemap_export_zebra;
+
+ /*
+ * Redistributed route filtering (routes from other
+ * protocols into VNC)
+ */
+ char *plist_redist_name[ZEBRA_ROUTE_MAX][AFI_MAX];
+ struct prefix_list *plist_redist[ZEBRA_ROUTE_MAX][AFI_MAX];
+
+ char *routemap_redist_name[ZEBRA_ROUTE_MAX];
+ struct route_map *routemap_redist[ZEBRA_ROUTE_MAX];
+
+ /*
+ * For importing bgp unicast routes to VNC, we encode the CE
+ * (route nexthop) in a Route Origin extended community. The
+ * local part (16-bit) is user-configurable.
+ */
+ uint16_t resolve_nve_roo_local_admin;
+#define BGP_VNC_CONFIG_RESOLVE_NVE_ROO_LOCAL_ADMIN_DEFAULT 5226
+
+ uint32_t flags;
+#define BGP_VNC_CONFIG_ADV_UN_METHOD_ENCAP 0x00000001
+#define BGP_VNC_CONFIG_CALLBACK_DISABLE 0x00000002
+#define BGP_VNC_CONFIG_RESPONSE_REMOVAL_DISABLE 0x00000004
+
+#define BGP_VNC_CONFIG_EXPORT_BGP_MODE_BITS 0x000000f0
+#define BGP_VNC_CONFIG_EXPORT_ZEBRA_MODE_BITS 0x00000f00
+
+#define BGP_VNC_CONFIG_EXPORT_BGP_MODE_NONE 0x00000000
+#define BGP_VNC_CONFIG_EXPORT_BGP_MODE_GRP 0x00000010
+#define BGP_VNC_CONFIG_EXPORT_BGP_MODE_RH 0x00000020 /* registerd nve */
+#define BGP_VNC_CONFIG_EXPORT_BGP_MODE_CE 0x00000040
+
+#define BGP_VNC_CONFIG_EXPORT_ZEBRA_MODE_NONE 0x00000000
+#define BGP_VNC_CONFIG_EXPORT_ZEBRA_MODE_GRP 0x00000100
+#define BGP_VNC_CONFIG_EXPORT_ZEBRA_MODE_RH 0x00000200
+
+#define BGP_VNC_CONFIG_FILTER_SELF_FROM_RSP 0x00001000
+#define BGP_VNC_CONFIG_L2RD 0x00002000
+
+/* Use new NVE RIB to filter callback routes */
+/* Filter querying NVE's registrations from responses */
+/* Default to updated-responses off */
+/* Default to removal-responses off */
+#define BGP_VNC_CONFIG_FLAGS_DEFAULT \
+ (BGP_VNC_CONFIG_FILTER_SELF_FROM_RSP | BGP_VNC_CONFIG_CALLBACK_DISABLE \
+ | BGP_VNC_CONFIG_RESPONSE_REMOVAL_DISABLE)
+
+ struct rfapi_rfp_cfg rfp_cfg; /* rfp related configuration */
+};
+
+#define VNC_EXPORT_ZEBRA_GRP_ENABLED(hc) \
+ (((hc)->flags & BGP_VNC_CONFIG_EXPORT_ZEBRA_MODE_BITS) \
+ == BGP_VNC_CONFIG_EXPORT_ZEBRA_MODE_GRP)
+
+#define VNC_EXPORT_ZEBRA_RH_ENABLED(hc) \
+ (((hc)->flags & BGP_VNC_CONFIG_EXPORT_ZEBRA_MODE_BITS) \
+ == BGP_VNC_CONFIG_EXPORT_ZEBRA_MODE_RH)
+
+#define VNC_EXPORT_BGP_GRP_ENABLED(hc) \
+ (((hc)->flags & BGP_VNC_CONFIG_EXPORT_BGP_MODE_BITS) \
+ == BGP_VNC_CONFIG_EXPORT_BGP_MODE_GRP)
+
+#define VNC_EXPORT_BGP_RH_ENABLED(hc) \
+ (((hc)->flags & BGP_VNC_CONFIG_EXPORT_BGP_MODE_BITS) \
+ == BGP_VNC_CONFIG_EXPORT_BGP_MODE_RH)
+
+#define VNC_EXPORT_BGP_CE_ENABLED(hc) \
+ (((hc)->flags & BGP_VNC_CONFIG_EXPORT_BGP_MODE_BITS) \
+ == BGP_VNC_CONFIG_EXPORT_BGP_MODE_CE)
+
+
+void bgp_rfapi_cfg_init(void);
+
+struct rfapi_cfg *bgp_rfapi_cfg_new(struct rfapi_rfp_cfg *cfg);
+
+void bgp_rfapi_cfg_destroy(struct bgp *bgp, struct rfapi_cfg *h);
+
+int bgp_rfapi_cfg_write(struct vty *vty, struct bgp *bgp);
+
+extern int bgp_rfapi_is_vnc_configured(struct bgp *bgp);
+
+extern void nve_group_to_nve_list(struct rfapi_nve_group_cfg *rfg,
+ struct list **nves,
+ uint8_t family); /* AF_INET, AF_INET6 */
+
+struct rfapi_nve_group_cfg *bgp_rfapi_cfg_match_group(struct rfapi_cfg *hc,
+ struct prefix *vn,
+ struct prefix *un);
+
+struct rfapi_nve_group_cfg *
+bgp_rfapi_cfg_match_byname(struct bgp *bgp, const char *name,
+ rfapi_group_cfg_type_t type); /* _MAX = any */
+
+extern void vnc_prefix_list_update(struct bgp *bgp);
+
+extern void vnc_routemap_update(struct bgp *bgp, const char *unused);
+
+extern void bgp_rfapi_show_summary(struct bgp *bgp, struct vty *vty);
+
+extern struct rfapi_cfg *bgp_rfapi_get_config(struct bgp *bgp);
+
+extern struct rfapi_l2_group_cfg *
+bgp_rfapi_get_group_by_lni_label(struct bgp *bgp, uint32_t logical_net_id,
+ uint32_t label);
+
+extern struct ecommunity *
+bgp_rfapi_get_ecommunity_by_lni_label(struct bgp *bgp, uint32_t is_import,
+ uint32_t logical_net_id,
+ uint32_t label); /* note, 20bit label! */
+
+extern struct list *
+bgp_rfapi_get_labellist_by_lni_label(struct bgp *bgp, uint32_t logical_net_id,
+ uint32_t label); /* note, 20bit label! */
+
+#endif /* ENABLE_BGP_VNC */
+
+#endif /* _QUAGGA_BGP_RFAPI_CFG_H */
diff --git a/bgpd/rfapi/rfapi.c b/bgpd/rfapi/rfapi.c
new file mode 100644
index 0000000..ed0714c
--- /dev/null
+++ b/bgpd/rfapi/rfapi.c
@@ -0,0 +1,4062 @@
+/*
+ *
+ * Copyright 2009-2016, LabN Consulting, L.L.C.
+ *
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program; see the file COPYING; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
+ */
+
+#include "lib/zebra.h"
+#include "lib/prefix.h"
+#include "lib/agg_table.h"
+#include "lib/vty.h"
+#include "lib/memory.h"
+#include "lib/routemap.h"
+#include "lib/log.h"
+#include "lib/linklist.h"
+#include "lib/command.h"
+#include "lib/stream.h"
+#include "lib/ringbuf.h"
+#include "lib/lib_errors.h"
+
+#include "bgpd/bgpd.h"
+#include "bgpd/bgp_ecommunity.h"
+#include "bgpd/bgp_attr.h"
+
+#include "bgpd/rfapi/bgp_rfapi_cfg.h"
+#include "bgpd/rfapi/rfapi.h"
+#include "bgpd/rfapi/rfapi_backend.h"
+
+#include "bgpd/bgp_route.h"
+#include "bgpd/bgp_mplsvpn.h"
+#include "bgpd/bgp_aspath.h"
+#include "bgpd/bgp_advertise.h"
+#include "bgpd/bgp_vnc_types.h"
+#include "bgpd/bgp_zebra.h"
+
+#include "bgpd/rfapi/rfapi_import.h"
+#include "bgpd/rfapi/rfapi_private.h"
+#include "bgpd/rfapi/rfapi_monitor.h"
+#include "bgpd/rfapi/rfapi_vty.h"
+#include "bgpd/rfapi/vnc_export_bgp.h"
+#include "bgpd/rfapi/vnc_export_bgp_p.h"
+#include "bgpd/rfapi/vnc_zebra.h"
+#include "bgpd/rfapi/vnc_import_bgp.h"
+#include "bgpd/rfapi/rfapi_rib.h"
+#include "bgpd/rfapi/rfapi_ap.h"
+#include "bgpd/rfapi/rfapi_encap_tlv.h"
+#include "bgpd/rfapi/vnc_debug.h"
+
+#ifdef HAVE_GLIBC_BACKTRACE
+/* for backtrace and friends */
+#include <execinfo.h>
+#endif /* HAVE_GLIBC_BACKTRACE */
+
+struct ethaddr rfapi_ethaddr0 = {{0}};
+
+#define DEBUG_RFAPI_STR "RF API debugging/testing command\n"
+
+const char *rfapi_error_str(int code)
+{
+ switch (code) {
+ case 0:
+ return "Success";
+ case ENXIO:
+ return "BGP or VNC not configured";
+ case ENOENT:
+ return "No match";
+ case EEXIST:
+ return "Handle already open";
+ case ENOMSG:
+ return "Incomplete configuration";
+ case EAFNOSUPPORT:
+ return "Invalid address family";
+ case EDEADLK:
+ return "Called from within a callback procedure";
+ case EBADF:
+ return "Invalid handle";
+ case EINVAL:
+ return "Invalid argument";
+ case ESTALE:
+ return "Stale descriptor";
+ default:
+ return "Unknown error";
+ }
+}
+
+/*------------------------------------------
+ * rfapi_get_response_lifetime_default
+ *
+ * Returns the default lifetime for a response.
+ * rfp_start_val value returned by rfp_start or
+ * NULL (=use default instance)
+ *
+ * input:
+ * None
+ *
+ * output:
+ *
+ * return value: The bgp instance default lifetime for a response.
+ --------------------------------------------*/
+int rfapi_get_response_lifetime_default(void *rfp_start_val)
+{
+ struct bgp *bgp = rfapi_bgp_lookup_by_rfp(rfp_start_val);
+ if (bgp)
+ return bgp->rfapi_cfg->default_response_lifetime;
+ return BGP_VNC_DEFAULT_RESPONSE_LIFETIME_DEFAULT;
+}
+
+/*------------------------------------------
+ * rfapi_is_vnc_configured
+ *
+ * Returns if VNC is configured
+ *
+ * input:
+ * rfp_start_val value returned by rfp_start or
+ * NULL (=use default instance)
+ *
+ * output:
+ *
+ * return value: If VNC is configured for the bgpd instance
+ * 0 Success
+ * ENXIO VNC not configured
+ --------------------------------------------*/
+int rfapi_is_vnc_configured(void *rfp_start_val)
+{
+ struct bgp *bgp = rfapi_bgp_lookup_by_rfp(rfp_start_val);
+ if (bgp_rfapi_is_vnc_configured(bgp) == 0)
+ return 0;
+ return ENXIO;
+}
+
+
+/*------------------------------------------
+ * rfapi_get_vn_addr
+ *
+ * Get the virtual network address used by an NVE based on it's RFD
+ *
+ * input:
+ * rfd: rfapi descriptor returned by rfapi_open or rfapi_create_generic
+ *
+ * output:
+ *
+ * return value:
+ * vn NVE virtual network address
+ *------------------------------------------*/
+struct rfapi_ip_addr *rfapi_get_vn_addr(void *rfd)
+{
+ struct rfapi_descriptor *rrfd = (struct rfapi_descriptor *)rfd;
+ return &rrfd->vn_addr;
+}
+
+/*------------------------------------------
+ * rfapi_get_un_addr
+ *
+ * Get the underlay network address used by an NVE based on it's RFD
+ *
+ * input:
+ * rfd: rfapi descriptor returned by rfapi_open or rfapi_create_generic
+ *
+ * output:
+ *
+ * return value:
+ * un NVE underlay network address
+ *------------------------------------------*/
+struct rfapi_ip_addr *rfapi_get_un_addr(void *rfd)
+{
+ struct rfapi_descriptor *rrfd = (struct rfapi_descriptor *)rfd;
+ return &rrfd->un_addr;
+}
+
+int rfapi_ip_addr_cmp(struct rfapi_ip_addr *a1, struct rfapi_ip_addr *a2)
+{
+ if (a1->addr_family != a2->addr_family)
+ return a1->addr_family - a2->addr_family;
+
+ if (a1->addr_family == AF_INET) {
+ return IPV4_ADDR_CMP(&a1->addr.v4, &a2->addr.v4);
+ }
+
+ if (a1->addr_family == AF_INET6) {
+ return IPV6_ADDR_CMP(&a1->addr.v6, &a2->addr.v6);
+ }
+
+ assert(1);
+ /* NOTREACHED */
+ return 1;
+}
+
+static int rfapi_find_node(struct bgp *bgp, struct rfapi_ip_addr *vn_addr,
+ struct rfapi_ip_addr *un_addr,
+ struct agg_node **node)
+{
+ struct rfapi *h;
+ struct prefix p;
+ struct agg_node *rn;
+ int rc;
+ afi_t afi;
+
+ if (!bgp) {
+ return ENXIO;
+ }
+
+ h = bgp->rfapi;
+ if (!h) {
+ return ENXIO;
+ }
+
+ afi = family2afi(un_addr->addr_family);
+ if (!afi) {
+ return EAFNOSUPPORT;
+ }
+
+ if ((rc = rfapiRaddr2Qprefix(un_addr, &p)))
+ return rc;
+
+ rn = agg_node_lookup(h->un[afi], &p);
+
+ if (!rn)
+ return ENOENT;
+
+ agg_unlock_node(rn);
+
+ *node = rn;
+
+ return 0;
+}
+
+
+int rfapi_find_rfd(struct bgp *bgp, struct rfapi_ip_addr *vn_addr,
+ struct rfapi_ip_addr *un_addr, struct rfapi_descriptor **rfd)
+{
+ struct agg_node *rn;
+ int rc;
+
+ rc = rfapi_find_node(bgp, vn_addr, un_addr, &rn);
+
+ if (rc)
+ return rc;
+
+ for (*rfd = (struct rfapi_descriptor *)(rn->info); *rfd;
+ *rfd = (*rfd)->next) {
+ if (!rfapi_ip_addr_cmp(&(*rfd)->vn_addr, vn_addr))
+ break;
+ }
+
+ if (!*rfd)
+ return ENOENT;
+
+ return 0;
+}
+
+/*------------------------------------------
+ * rfapi_find_handle
+ *
+ * input:
+ * un underlay network address
+ * vn virtual network address
+ *
+ * output:
+ * pHandle pointer to location to store handle
+ *
+ * return value:
+ * 0 Success
+ * ENOENT no matching handle
+ * ENXIO BGP or VNC not configured
+ *------------------------------------------*/
+static int rfapi_find_handle(struct bgp *bgp, struct rfapi_ip_addr *vn_addr,
+ struct rfapi_ip_addr *un_addr,
+ rfapi_handle *handle)
+{
+ struct rfapi_descriptor **rfd;
+
+ rfd = (struct rfapi_descriptor **)handle;
+
+ return rfapi_find_rfd(bgp, vn_addr, un_addr, rfd);
+}
+
+static int rfapi_find_handle_vty(struct vty *vty, struct rfapi_ip_addr *vn_addr,
+ struct rfapi_ip_addr *un_addr,
+ rfapi_handle *handle)
+{
+ struct bgp *bgp;
+ struct rfapi_descriptor **rfd;
+
+ bgp = bgp_get_default(); /* assume 1 instance for now */
+
+ rfd = (struct rfapi_descriptor **)handle;
+
+ return rfapi_find_rfd(bgp, vn_addr, un_addr, rfd);
+}
+
+static int is_valid_rfd(struct rfapi_descriptor *rfd)
+{
+ rfapi_handle hh;
+
+ if (!rfd || rfd->bgp == NULL)
+ return 0;
+
+ if (CHECK_FLAG(
+ rfd->flags,
+ RFAPI_HD_FLAG_IS_VRF)) /* assume VRF/internal are valid */
+ return 1;
+
+ if (rfapi_find_handle(rfd->bgp, &rfd->vn_addr, &rfd->un_addr, &hh))
+ return 0;
+
+ if (rfd != hh)
+ return 0;
+
+ return 1;
+}
+
+/*
+ * check status of descriptor
+ */
+int rfapi_check(void *handle)
+{
+ struct rfapi_descriptor *rfd = (struct rfapi_descriptor *)handle;
+ rfapi_handle hh;
+ int rc;
+
+ if (!rfd || rfd->bgp == NULL)
+ return EINVAL;
+
+ if (CHECK_FLAG(
+ rfd->flags,
+ RFAPI_HD_FLAG_IS_VRF)) /* assume VRF/internal are valid */
+ return 0;
+
+ if ((rc = rfapi_find_handle(rfd->bgp, &rfd->vn_addr, &rfd->un_addr,
+ &hh)))
+ return rc;
+
+ if (rfd != hh)
+ return ENOENT;
+
+ if (!rfd->rfg)
+ return ESTALE;
+
+ return 0;
+}
+
+
+void del_vnc_route(struct rfapi_descriptor *rfd,
+ struct peer *peer, /* rfd->peer for RFP regs */
+ struct bgp *bgp, safi_t safi, const struct prefix *p,
+ struct prefix_rd *prd, uint8_t type, uint8_t sub_type,
+ struct rfapi_nexthop *lnh, int kill)
+{
+ afi_t afi; /* of the VN address */
+ struct bgp_dest *bn;
+ struct bgp_path_info *bpi;
+ struct prefix_rd prd0;
+
+ afi = family2afi(p->family);
+ assert(afi == AFI_IP || afi == AFI_IP6);
+
+ if (safi == SAFI_ENCAP) {
+ memset(&prd0, 0, sizeof(prd0));
+ prd0.family = AF_UNSPEC;
+ prd0.prefixlen = 64;
+ prd = &prd0;
+ }
+ bn = bgp_afi_node_get(bgp->rib[afi][safi], afi, safi, p, prd);
+
+ vnc_zlog_debug_verbose(
+ "%s: peer=%p, prefix=%pFX, prd=%pRD afi=%d, safi=%d bn=%p, bn->info=%p",
+ __func__, peer, p, prd, afi, safi, bn,
+ (bn ? bgp_dest_get_bgp_path_info(bn) : NULL));
+
+ for (bpi = (bn ? bgp_dest_get_bgp_path_info(bn) : NULL); bpi;
+ bpi = bpi->next) {
+
+ vnc_zlog_debug_verbose(
+ "%s: trying bpi=%p, bpi->peer=%p, bpi->type=%d, bpi->sub_type=%d, bpi->extra->vnc.export.rfapi_handle=%p, local_pref=%" PRIu64,
+ __func__, bpi, bpi->peer, bpi->type, bpi->sub_type,
+ (bpi->extra ? bpi->extra->vnc.export.rfapi_handle
+ : NULL),
+ CHECK_FLAG(bpi->attr->flag,
+ ATTR_FLAG_BIT(BGP_ATTR_LOCAL_PREF)
+ ? bpi->attr->local_pref : 0));
+
+ if (bpi->peer == peer && bpi->type == type
+ && bpi->sub_type == sub_type && bpi->extra
+ && bpi->extra->vnc.export.rfapi_handle == (void *)rfd) {
+
+ vnc_zlog_debug_verbose("%s: matched it", __func__);
+
+ break;
+ }
+ }
+
+ if (lnh) {
+ /*
+ * lnh set means to JUST delete the local nexthop from this
+ * route. Leave the route itself in place.
+ * TBD add return code reporting of success/failure
+ */
+ if (!bpi || !bpi->extra
+ || !bpi->extra->vnc.export.local_nexthops) {
+ /*
+ * no local nexthops
+ */
+ vnc_zlog_debug_verbose(
+ "%s: lnh list already empty at prefix %pFX",
+ __func__, p);
+ goto done;
+ }
+
+ /*
+ * look for it
+ */
+ struct listnode *node;
+ struct rfapi_nexthop *pLnh = NULL;
+
+ for (ALL_LIST_ELEMENTS_RO(bpi->extra->vnc.export.local_nexthops,
+ node, pLnh)) {
+
+ if (prefix_same(&pLnh->addr, &lnh->addr)) {
+ break;
+ }
+ }
+
+ if (pLnh) {
+ listnode_delete(bpi->extra->vnc.export.local_nexthops,
+ pLnh);
+
+ /* silly rabbit, listnode_delete doesn't invoke
+ * list->del on data */
+ rfapi_nexthop_free(pLnh);
+ } else {
+ vnc_zlog_debug_verbose("%s: desired lnh not found %pFX",
+ __func__, p);
+ }
+ goto done;
+ }
+
+ /*
+ * loop back to import tables
+ * Do this before removing from BGP RIB because rfapiProcessWithdraw
+ * might refer to it
+ */
+ rfapiProcessWithdraw(peer, rfd, p, prd, NULL, afi, safi, type, kill);
+
+ if (bpi) {
+ vnc_zlog_debug_verbose(
+ "%s: Found route (safi=%d) to delete at prefix %pFX",
+ __func__, safi, p);
+
+ 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);
+ table = bgp_dest_get_bgp_table_info(pdest);
+ if (table)
+ vnc_import_bgp_del_vnc_host_route_mode_resolve_nve(
+ bgp, prd, table, p, bpi);
+ bgp_dest_unlock_node(pdest);
+ }
+
+ /*
+ * Delete local_nexthops list
+ */
+ if (bpi->extra && bpi->extra->vnc.export.local_nexthops)
+ list_delete(&bpi->extra->vnc.export.local_nexthops);
+
+ bgp_aggregate_decrement(bgp, p, bpi, afi, safi);
+ bgp_path_info_delete(bn, bpi);
+ bgp_process(bgp, bn, afi, safi);
+ } else {
+ vnc_zlog_debug_verbose(
+ "%s: Couldn't find route (safi=%d) at prefix %pFX",
+ __func__, safi, p);
+ }
+done:
+ bgp_dest_unlock_node(bn);
+}
+
+struct rfapi_nexthop *rfapi_nexthop_new(struct rfapi_nexthop *copyme)
+{
+ struct rfapi_nexthop *new =
+ XCALLOC(MTYPE_RFAPI_NEXTHOP, sizeof(struct rfapi_nexthop));
+ if (copyme)
+ *new = *copyme;
+ return new;
+}
+
+void rfapi_nexthop_free(void *p)
+{
+ struct rfapi_nexthop *goner = p;
+ XFREE(MTYPE_RFAPI_NEXTHOP, goner);
+}
+
+struct rfapi_vn_option *rfapi_vn_options_dup(struct rfapi_vn_option *existing)
+{
+ struct rfapi_vn_option *p;
+ struct rfapi_vn_option *head = NULL;
+ struct rfapi_vn_option *tail = NULL;
+
+ for (p = existing; p; p = p->next) {
+ struct rfapi_vn_option *new;
+
+ new = XCALLOC(MTYPE_RFAPI_VN_OPTION,
+ sizeof(struct rfapi_vn_option));
+ *new = *p;
+ new->next = NULL;
+ if (tail)
+ (tail)->next = new;
+ tail = new;
+ if (!head) {
+ head = new;
+ }
+ }
+ return head;
+}
+
+void rfapi_un_options_free(struct rfapi_un_option *p)
+{
+ struct rfapi_un_option *next;
+
+ while (p) {
+ next = p->next;
+ XFREE(MTYPE_RFAPI_UN_OPTION, p);
+ p = next;
+ }
+}
+
+void rfapi_vn_options_free(struct rfapi_vn_option *p)
+{
+ struct rfapi_vn_option *next;
+
+ while (p) {
+ next = p->next;
+ XFREE(MTYPE_RFAPI_VN_OPTION, p);
+ p = next;
+ }
+}
+
+/* Based on bgp_redistribute_add() */
+void add_vnc_route(struct rfapi_descriptor *rfd, /* cookie, VPN UN addr, peer */
+ struct bgp *bgp, int safi, const struct prefix *p,
+ struct prefix_rd *prd, struct rfapi_ip_addr *nexthop,
+ uint32_t *local_pref,
+ uint32_t *lifetime, /* NULL => dont send lifetime */
+ struct bgp_tea_options *rfp_options,
+ struct rfapi_un_option *options_un,
+ struct rfapi_vn_option *options_vn,
+ struct ecommunity *rt_export_list, /* Copied, not consumed */
+ uint32_t *med, /* NULL => don't set med */
+ uint32_t *label, /* low order 3 bytes */
+ uint8_t type, uint8_t sub_type, /* RFP, NORMAL or REDIST */
+ int flags)
+{
+ afi_t afi; /* of the VN address */
+ struct bgp_path_info *new;
+ struct bgp_path_info *bpi;
+ struct bgp_dest *bn;
+
+ struct attr attr = {0};
+ struct attr *new_attr;
+ uint32_t label_val;
+
+ struct bgp_attr_encap_subtlv *encaptlv;
+ char buf[PREFIX_STRLEN];
+
+ struct rfapi_nexthop *lnh = NULL; /* local nexthop */
+ struct rfapi_vn_option *vo;
+ struct rfapi_l2address_option *l2o = NULL;
+ struct rfapi_ip_addr *un_addr = &rfd->un_addr;
+
+ bgp_encap_types TunnelType = BGP_ENCAP_TYPE_RESERVED;
+ struct bgp_redist *red;
+
+ if (safi == SAFI_ENCAP
+ && !(bgp->rfapi_cfg->flags & BGP_VNC_CONFIG_ADV_UN_METHOD_ENCAP)) {
+
+ /*
+ * Encap mode not enabled. UN addresses will be communicated
+ * via VNC Tunnel subtlv instead.
+ */
+ vnc_zlog_debug_verbose(
+ "%s: encap mode not enabled, not adding SAFI_ENCAP route",
+ __func__);
+ return;
+ }
+
+ for (vo = options_vn; vo; vo = vo->next) {
+ if (RFAPI_VN_OPTION_TYPE_L2ADDR == vo->type) {
+ l2o = &vo->v.l2addr;
+ if (RFAPI_0_ETHERADDR(&l2o->macaddr))
+ l2o = NULL; /* not MAC resolution */
+ }
+ if (RFAPI_VN_OPTION_TYPE_LOCAL_NEXTHOP == vo->type) {
+ lnh = &vo->v.local_nexthop;
+ }
+ }
+
+ if (label)
+ label_val = *label;
+ else
+ label_val = MPLS_LABEL_IMPLICIT_NULL;
+
+ afi = family2afi(p->family);
+ assert(afi == AFI_IP || afi == AFI_IP6);
+
+ vnc_zlog_debug_verbose("%s: afi=%s, safi=%s", __func__, afi2str(afi),
+ safi2str(safi));
+
+ /* Make default attribute. Produces already-interned attr.aspath */
+ /* Cripes, the memory management of attributes is byzantine */
+
+ bgp_attr_default_set(&attr, bgp, BGP_ORIGIN_INCOMPLETE);
+
+ /*
+ * At this point:
+ * attr: static
+ * extra: dynamically allocated, owned by attr
+ * aspath: points to interned hash from aspath hash table
+ */
+
+
+ /*
+ * Route-specific un_options get added to the VPN SAFI
+ * advertisement tunnel encap attribute. (the per-NVE
+ * "default" un_options are put into the 1-per-NVE ENCAP
+ * SAFI advertisement). The VPN SAFI also gets the
+ * default un_options if there are no route-specific options.
+ */
+ if (options_un) {
+ struct rfapi_un_option *uo;
+
+ for (uo = options_un; uo; uo = uo->next) {
+ if (RFAPI_UN_OPTION_TYPE_TUNNELTYPE == uo->type) {
+ TunnelType = rfapi_tunneltype_option_to_tlv(
+ bgp, un_addr, &uo->v.tunnel, &attr,
+ l2o != NULL);
+ }
+ }
+ } else {
+ /*
+ * Add encap attr
+ * These are the NVE-specific "default" un_options which are
+ * put into the 1-per-NVE ENCAP advertisement.
+ */
+ if (rfd->default_tunneltype_option.type) {
+ TunnelType = rfapi_tunneltype_option_to_tlv(
+ bgp, un_addr, &rfd->default_tunneltype_option,
+ &attr, l2o != NULL);
+ } else /* create default for local addse */
+ if (type == ZEBRA_ROUTE_BGP
+ && sub_type == BGP_ROUTE_RFP)
+ TunnelType = rfapi_tunneltype_option_to_tlv(
+ bgp, un_addr, NULL, &attr, l2o != NULL);
+ }
+
+ if (TunnelType == BGP_ENCAP_TYPE_MPLS) {
+ if (safi == SAFI_ENCAP) {
+ /* Encap SAFI not used with MPLS */
+ vnc_zlog_debug_verbose(
+ "%s: mpls tunnel type, encap safi omitted",
+ __func__);
+ aspath_unintern(&attr.aspath); /* Unintern original. */
+ return;
+ }
+ }
+
+ if (local_pref) {
+ attr.local_pref = *local_pref;
+ attr.flag |= ATTR_FLAG_BIT(BGP_ATTR_LOCAL_PREF);
+ }
+
+ if (med) {
+ attr.med = *med;
+ attr.flag |= ATTR_FLAG_BIT(BGP_ATTR_MULTI_EXIT_DISC);
+ }
+
+ /* override default weight assigned by bgp_attr_default_set() */
+ attr.weight = rfd->peer ? rfd->peer->weight[afi][safi] : 0;
+
+ /*
+ * NB: ticket 81: do not reset attr.aspath here because it would
+ * cause iBGP peers to drop route
+ */
+
+ /*
+ * Set originator ID for routes imported from BGP directly.
+ * These routes could be synthetic, and therefore could
+ * reuse the peer pointers of the routes they are derived
+ * from. Setting the originator ID to "us" prevents the
+ * wrong originator ID from being sent when this route is
+ * sent from a route reflector.
+ */
+ if (type == ZEBRA_ROUTE_BGP_DIRECT
+ || type == ZEBRA_ROUTE_BGP_DIRECT_EXT) {
+ attr.flag |= ATTR_FLAG_BIT(BGP_ATTR_ORIGINATOR_ID);
+ attr.originator_id = bgp->router_id;
+ }
+
+
+ /* Set up vnc attribute (sub-tlv for Prefix Lifetime) */
+ if (lifetime && *lifetime != RFAPI_INFINITE_LIFETIME) {
+ uint32_t lt;
+
+ encaptlv = XCALLOC(MTYPE_ENCAP_TLV,
+ sizeof(struct bgp_attr_encap_subtlv) + 4);
+ encaptlv->type =
+ BGP_VNC_SUBTLV_TYPE_LIFETIME; /* prefix lifetime */
+ encaptlv->length = 4;
+ lt = htonl(*lifetime);
+ memcpy(encaptlv->value, &lt, 4);
+ bgp_attr_set_vnc_subtlvs(&attr, encaptlv);
+ vnc_zlog_debug_verbose(
+ "%s: set Encap Attr Prefix Lifetime to %d", __func__,
+ *lifetime);
+ }
+
+ /* add rfp options to vnc attr */
+ if (rfp_options) {
+
+ if (flags & RFAPI_AHR_RFPOPT_IS_VNCTLV) {
+ struct bgp_attr_encap_subtlv *vnc_subtlvs =
+ bgp_attr_get_vnc_subtlvs(&attr);
+ /*
+ * this flag means we're passing a pointer to an
+ * existing encap tlv chain which we should copy.
+ * It's a hack to avoid adding yet another argument
+ * to add_vnc_route()
+ */
+ encaptlv = encap_tlv_dup(
+ (struct bgp_attr_encap_subtlv *)rfp_options);
+ if (vnc_subtlvs)
+ vnc_subtlvs->next = encaptlv;
+ else
+ bgp_attr_set_vnc_subtlvs(&attr, encaptlv);
+ } else {
+ struct bgp_tea_options *hop;
+ /* XXX max of one tlv present so far from above code */
+ struct bgp_attr_encap_subtlv *tail =
+ bgp_attr_get_vnc_subtlvs(&attr);
+
+ for (hop = rfp_options; hop; hop = hop->next) {
+
+ /*
+ * Construct subtlv
+ */
+ encaptlv = XCALLOC(
+ MTYPE_ENCAP_TLV,
+ sizeof(struct bgp_attr_encap_subtlv) + 2
+ + hop->length);
+ encaptlv->type =
+ BGP_VNC_SUBTLV_TYPE_RFPOPTION; /* RFP
+ option
+ */
+ encaptlv->length = 2 + hop->length;
+ *((uint8_t *)(encaptlv->value) + 0) = hop->type;
+ *((uint8_t *)(encaptlv->value) + 1) =
+ hop->length;
+ memcpy(((uint8_t *)encaptlv->value) + 2,
+ hop->value, hop->length);
+
+ /*
+ * add to end of subtlv chain
+ */
+ if (tail)
+ tail->next = encaptlv;
+ else
+ bgp_attr_set_vnc_subtlvs(&attr,
+ encaptlv);
+ tail = encaptlv;
+ }
+ }
+ }
+
+ /*
+ * At this point:
+ * attr: static
+ * extra: dynamically allocated, owned by attr
+ * vnc_subtlvs: dynamic chain, length 1
+ * aspath: points to interned hash from aspath hash table
+ */
+
+
+ bgp_attr_set_ecommunity(&attr, ecommunity_new());
+ assert(bgp_attr_get_ecommunity(&attr));
+
+ if (TunnelType != BGP_ENCAP_TYPE_MPLS
+ && TunnelType != BGP_ENCAP_TYPE_RESERVED) {
+ /*
+ * Add BGP Encapsulation Extended Community. Format described in
+ * section 4.5 of RFC 5512.
+ * Always include when not MPLS type, to disambiguate this case.
+ */
+ struct ecommunity_val beec;
+
+ memset(&beec, 0, sizeof(beec));
+ beec.val[0] = ECOMMUNITY_ENCODE_OPAQUE;
+ beec.val[1] = ECOMMUNITY_OPAQUE_SUBTYPE_ENCAP;
+ beec.val[6] = ((TunnelType) >> 8) & 0xff;
+ beec.val[7] = (TunnelType)&0xff;
+ ecommunity_add_val(bgp_attr_get_ecommunity(&attr), &beec, false,
+ false);
+ }
+
+ /*
+ * Add extended community attributes to match rt export list
+ */
+ if (rt_export_list) {
+ bgp_attr_set_ecommunity(
+ &attr, ecommunity_merge(bgp_attr_get_ecommunity(&attr),
+ rt_export_list));
+ }
+
+ struct ecommunity *ecomm = bgp_attr_get_ecommunity(&attr);
+
+ if (!ecomm->size) {
+ ecommunity_free(&ecomm);
+ bgp_attr_set_ecommunity(&attr, NULL);
+ }
+ vnc_zlog_debug_verbose("%s: attr.ecommunity=%p", __func__, ecomm);
+
+
+ /*
+ * At this point:
+ * attr: static
+ * extra: dynamically allocated, owned by attr
+ * vnc_subtlvs: dynamic chain, length 1
+ * ecommunity: dynamic 2-part
+ * aspath: points to interned hash from aspath hash table
+ */
+
+ /* stuff nexthop in attr_extra; which field depends on IPv4 or IPv6 */
+ switch (nexthop->addr_family) {
+ case AF_INET:
+ /*
+ * set this field to prevent bgp_route.c code from setting
+ * mp_nexthop_global_in to self
+ */
+ attr.nexthop.s_addr = nexthop->addr.v4.s_addr;
+
+ attr.mp_nexthop_global_in = nexthop->addr.v4;
+ attr.mp_nexthop_len = BGP_ATTR_NHLEN_IPV4;
+ break;
+
+ case AF_INET6:
+ attr.mp_nexthop_global = nexthop->addr.v6;
+ attr.mp_nexthop_len = BGP_ATTR_NHLEN_IPV6_GLOBAL;
+ break;
+
+ default:
+ assert(0);
+ }
+
+
+ prefix2str(p, buf, sizeof(buf));
+
+ /*
+ * At this point:
+ *
+ * attr: static
+ * extra: dynamically allocated, owned by attr
+ * vnc_subtlvs: dynamic chain, length 1
+ * ecommunity: dynamic 2-part
+ * aspath: points to interned hash from aspath hash table
+ */
+
+ red = bgp_redist_lookup(bgp, afi, type, 0);
+
+ if (red && red->redist_metric_flag) {
+ attr.med = red->redist_metric;
+ attr.flag |= ATTR_FLAG_BIT(BGP_ATTR_MULTI_EXIT_DISC);
+ }
+
+ bn = bgp_afi_node_get(bgp->rib[afi][safi], afi, safi, p, prd);
+
+ /*
+ * bgp_attr_intern creates a new reference to a cached
+ * attribute, but leaves the following bits of trash:
+ * - old attr
+ * - old attr->extra (free via bgp_attr_extra_free(attr))
+ *
+ * Note that it frees the original attr->extra->ecommunity
+ * but leaves the new attribute pointing to the ORIGINAL
+ * vnc options (which therefore we needn't free from the
+ * static attr)
+ */
+ new_attr = bgp_attr_intern(&attr);
+
+ aspath_unintern(&attr.aspath); /* Unintern original. */
+
+ /*
+ * At this point:
+ *
+ * attr: static
+ * extra: dynamically allocated, owned by attr
+ * vnc_subtlvs: dynamic chain, length 1
+ * ecommunity: POINTS TO INTERNED ecom, THIS REF NOT COUNTED
+ *
+ * new_attr: an attr that is part of the hash table, distinct
+ * from attr which is static.
+ * extra: dynamically allocated, owned by new_attr (in hash table)
+ * vnc_subtlvs: POINTS TO SAME dynamic chain AS attr
+ * ecommunity: POINTS TO interned/refcounted dynamic 2-part AS attr
+ * aspath: POINTS TO interned/refcounted hashed block
+ */
+ for (bpi = bgp_dest_get_bgp_path_info(bn); bpi; bpi = bpi->next) {
+ /* probably only need to check
+ * bpi->extra->vnc.export.rfapi_handle */
+ if (bpi->peer == rfd->peer && bpi->type == type
+ && bpi->sub_type == sub_type && bpi->extra
+ && bpi->extra->vnc.export.rfapi_handle == (void *)rfd) {
+
+ break;
+ }
+ }
+
+ if (bpi) {
+
+ /*
+ * Adding new local_nexthop, which does not by itself change
+ * what is advertised via BGP
+ */
+ if (lnh) {
+ if (!bpi->extra->vnc.export.local_nexthops) {
+ /* TBD make arrangements to free when needed */
+ bpi->extra->vnc.export.local_nexthops =
+ list_new();
+ bpi->extra->vnc.export.local_nexthops->del =
+ rfapi_nexthop_free;
+ }
+
+ /*
+ * already present?
+ */
+ struct listnode *node;
+ struct rfapi_nexthop *pLnh = NULL;
+
+ for (ALL_LIST_ELEMENTS_RO(
+ bpi->extra->vnc.export.local_nexthops,
+ node, pLnh)) {
+
+ if (prefix_same(&pLnh->addr, &lnh->addr)) {
+ break;
+ }
+ }
+
+ /*
+ * Not present, add new one
+ */
+ if (!pLnh) {
+ pLnh = rfapi_nexthop_new(lnh);
+ listnode_add(
+ bpi->extra->vnc.export.local_nexthops,
+ pLnh);
+ }
+ }
+
+ if (attrhash_cmp(bpi->attr, new_attr)
+ && !CHECK_FLAG(bpi->flags, BGP_PATH_REMOVED)) {
+ bgp_attr_unintern(&new_attr);
+ bgp_dest_unlock_node(bn);
+
+ vnc_zlog_debug_any(
+ "%s: Found route (safi=%d) at prefix %s, no change",
+ __func__, safi, buf);
+
+ goto done;
+ } else {
+ /* The attribute is changed. */
+ bgp_path_info_set_flag(bn, bpi, BGP_PATH_ATTR_CHANGED);
+
+ 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);
+ table = bgp_dest_get_bgp_table_info(pdest);
+ if (table)
+ vnc_import_bgp_del_vnc_host_route_mode_resolve_nve(
+ bgp, prd, table, p, bpi);
+ bgp_dest_unlock_node(pdest);
+ }
+
+ /* 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);
+ bgp_attr_unintern(&bpi->attr);
+ bpi->attr = new_attr;
+ bpi->uptime = monotime(NULL);
+
+
+ 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);
+ table = bgp_dest_get_bgp_table_info(pdest);
+ if (table)
+ vnc_import_bgp_add_vnc_host_route_mode_resolve_nve(
+ bgp, prd, table, p, bpi);
+ bgp_dest_unlock_node(pdest);
+ }
+
+ /* Process change. */
+ bgp_aggregate_increment(bgp, p, bpi, afi, safi);
+ bgp_process(bgp, bn, afi, safi);
+ bgp_dest_unlock_node(bn);
+
+ vnc_zlog_debug_any(
+ "%s: Found route (safi=%d) at prefix %s, changed attr",
+ __func__, safi, buf);
+
+ goto done;
+ }
+ }
+
+ new = info_make(type, sub_type, 0, rfd->peer, new_attr, NULL);
+ SET_FLAG(new->flags, BGP_PATH_VALID);
+
+ /* save backref to rfapi handle */
+ bgp_path_info_extra_get(new);
+ new->extra->vnc.export.rfapi_handle = (void *)rfd;
+ encode_label(label_val, &new->extra->label[0]);
+
+ /* debug */
+
+ if (VNC_DEBUG(VERBOSE)) {
+ vnc_zlog_debug_verbose("%s: printing BPI", __func__);
+ rfapiPrintBi(NULL, new);
+ }
+
+ bgp_aggregate_increment(bgp, p, new, afi, safi);
+ bgp_path_info_add(bn, new);
+
+ 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);
+ table = bgp_dest_get_bgp_table_info(pdest);
+ if (table)
+ vnc_import_bgp_add_vnc_host_route_mode_resolve_nve(
+ bgp, prd, table, p, new);
+ bgp_dest_unlock_node(pdest);
+ encode_label(label_val, &bn->local_label);
+ }
+
+ bgp_dest_unlock_node(bn);
+ bgp_process(bgp, bn, afi, safi);
+
+ vnc_zlog_debug_any(
+ "%s: Added route (safi=%s) at prefix %s (bn=%p, prd=%pRD)",
+ __func__, safi2str(safi), buf, bn, prd);
+
+done:
+ /* Loop back to import tables */
+ rfapiProcessUpdate(rfd->peer, rfd, p, prd, new_attr, afi, safi, type,
+ sub_type, &label_val);
+ vnc_zlog_debug_verbose("%s: looped back import route (safi=%d)",
+ __func__, safi);
+}
+
+uint32_t rfp_cost_to_localpref(uint8_t cost)
+{
+ return 255 - cost;
+}
+
+static void rfapiTunnelRouteAnnounce(struct bgp *bgp,
+ struct rfapi_descriptor *rfd,
+ uint32_t *pLifetime)
+{
+ struct prefix_rd prd;
+ struct prefix pfx_vn;
+ int rc;
+ uint32_t local_pref = rfp_cost_to_localpref(0);
+
+ rc = rfapiRaddr2Qprefix(&(rfd->vn_addr), &pfx_vn);
+ assert(!rc);
+
+ /*
+ * Construct route distinguisher = 0
+ */
+ memset(&prd, 0, sizeof(prd));
+ prd.family = AF_UNSPEC;
+ prd.prefixlen = 64;
+
+ add_vnc_route(rfd, /* rfapi descr, for export list & backref */
+ bgp, /* which bgp instance */
+ SAFI_ENCAP, /* which SAFI */
+ &pfx_vn, /* prefix to advertise */
+ &prd, /* route distinguisher to use */
+ &rfd->un_addr, /* nexthop */
+ &local_pref,
+ pLifetime, /* max lifetime of child VPN routes */
+ NULL, /* no rfp options for ENCAP safi */
+ NULL, /* rfp un options */
+ NULL, /* rfp vn options */
+ rfd->rt_export_list, NULL, /* med */
+ NULL, /* label: default */
+ ZEBRA_ROUTE_BGP, BGP_ROUTE_RFP, 0);
+}
+
+
+/***********************************************************************
+ * RFP processing behavior configuration
+ ***********************************************************************/
+
+/*------------------------------------------
+ * rfapi_rfp_set_configuration
+ *
+ * This is used to change rfapi's processing behavior based on
+ * RFP requirements.
+ *
+ * input:
+ * rfp_start_val value returned by rfp_start
+ * rfapi_rfp_cfg Pointer to configuration structure
+ *
+ * output:
+ * none
+ *
+ * return value:
+ * 0 Success
+ * ENXIO Unabled to locate configured BGP/VNC
+--------------------------------------------*/
+int rfapi_rfp_set_configuration(void *rfp_start_val, struct rfapi_rfp_cfg *new)
+{
+ struct rfapi_rfp_cfg *rcfg;
+ struct bgp *bgp;
+
+ bgp = rfapi_bgp_lookup_by_rfp(rfp_start_val);
+
+ if (!new || !bgp || !bgp->rfapi_cfg)
+ return ENXIO;
+
+ rcfg = &bgp->rfapi_cfg->rfp_cfg;
+ rcfg->download_type = new->download_type;
+ rcfg->ftd_advertisement_interval = new->ftd_advertisement_interval;
+ rcfg->holddown_factor = new->holddown_factor;
+
+ if (rcfg->use_updated_response != new->use_updated_response) {
+ rcfg->use_updated_response = new->use_updated_response;
+ if (rcfg->use_updated_response)
+ rfapiMonitorCallbacksOn(bgp);
+ else
+ rfapiMonitorCallbacksOff(bgp);
+ }
+ if (rcfg->use_removes != new->use_removes) {
+ rcfg->use_removes = new->use_removes;
+ if (rcfg->use_removes)
+ rfapiMonitorResponseRemovalOn(bgp);
+ else
+ rfapiMonitorResponseRemovalOff(bgp);
+ }
+ return 0;
+}
+
+/*------------------------------------------
+ * rfapi_rfp_set_cb_methods
+ *
+ * Change registered callback functions for asynchronous notifications
+ * from RFAPI to the RFP client.
+ *
+ * input:
+ * rfp_start_val value returned by rfp_start
+ * methods Pointer to struct rfapi_rfp_cb_methods containing
+ * pointers to callback methods as described above
+ *
+ * return value:
+ * 0 Success
+ * ENXIO BGP or VNC not configured
+ *------------------------------------------*/
+int rfapi_rfp_set_cb_methods(void *rfp_start_val,
+ struct rfapi_rfp_cb_methods *methods)
+{
+ struct rfapi *h;
+ struct bgp *bgp;
+
+ bgp = rfapi_bgp_lookup_by_rfp(rfp_start_val);
+ if (!bgp)
+ return ENXIO;
+
+ h = bgp->rfapi;
+ if (!h)
+ return ENXIO;
+
+ h->rfp_methods = *methods;
+
+ return 0;
+}
+
+/***********************************************************************
+ * NVE Sessions
+ ***********************************************************************/
+/*
+ * Caller must supply an already-allocated rfd with the "caller"
+ * fields already set (vn_addr, un_addr, callback, cookie)
+ * The advertised_prefixes[] array elements should be NULL to
+ * have this function set them to newly-allocated radix trees.
+ */
+static int rfapi_open_inner(struct rfapi_descriptor *rfd, struct bgp *bgp,
+ struct rfapi *h, struct rfapi_nve_group_cfg *rfg)
+{
+ int ret;
+
+ if (h->flags & RFAPI_INCALLBACK)
+ return EDEADLK;
+
+ /*
+ * Fill in configured fields
+ */
+
+ /*
+ * If group's RD is specified as "auto", then fill in based
+ * on NVE's VN address
+ */
+ rfd->rd = rfg->rd;
+
+ if (rfd->rd.family == AF_UNIX) {
+ ret = rfapi_set_autord_from_vn(&rfd->rd, &rfd->vn_addr);
+ if (ret != 0)
+ return ret;
+ }
+ rfd->rt_export_list = (rfg->rt_export_list)
+ ? ecommunity_dup(rfg->rt_export_list)
+ : NULL;
+ rfd->response_lifetime = rfg->response_lifetime;
+ rfd->rfg = rfg;
+
+ /*
+ * Fill in BGP peer structure
+ */
+ rfd->peer = peer_new(bgp);
+ rfd->peer->status = Established; /* keep bgp core happy */
+ bgp_sync_delete(rfd->peer); /* don't need these */
+
+ /*
+ * since this peer is not on the I/O thread, this lock is not strictly
+ * necessary, but serves as a reminder to those who may meddle...
+ */
+ frr_with_mutex (&rfd->peer->io_mtx) {
+ // we don't need any I/O related facilities
+ if (rfd->peer->ibuf)
+ stream_fifo_free(rfd->peer->ibuf);
+ if (rfd->peer->obuf)
+ stream_fifo_free(rfd->peer->obuf);
+
+ if (rfd->peer->ibuf_work)
+ ringbuf_del(rfd->peer->ibuf_work);
+ if (rfd->peer->obuf_work)
+ stream_free(rfd->peer->obuf_work);
+
+ rfd->peer->ibuf = NULL;
+ rfd->peer->obuf = NULL;
+ rfd->peer->obuf_work = NULL;
+ rfd->peer->ibuf_work = NULL;
+ }
+
+ { /* base code assumes have valid host pointer */
+ char buf[BUFSIZ];
+ buf[0] = 0;
+
+ if (rfd->vn_addr.addr_family == AF_INET) {
+ inet_ntop(AF_INET, &rfd->vn_addr.addr.v4, buf, BUFSIZ);
+ } else if (rfd->vn_addr.addr_family == AF_INET6) {
+ inet_ntop(AF_INET6, &rfd->vn_addr.addr.v6, buf, BUFSIZ);
+ }
+ rfd->peer->host = XSTRDUP(MTYPE_BGP_PEER_HOST, buf);
+ }
+ /* Mark peer as belonging to HD */
+ SET_FLAG(rfd->peer->flags, PEER_FLAG_IS_RFAPI_HD);
+
+ /*
+ * Set min prefix lifetime to max value so it will get set
+ * upon first rfapi_register()
+ */
+ rfd->min_prefix_lifetime = UINT32_MAX;
+
+/*
+ * Allocate response tables if needed
+ */
+#define RFD_RTINIT_AFI(rh, ary, afi) \
+ do { \
+ if (!ary[afi]) { \
+ ary[afi] = agg_table_init(); \
+ agg_set_table_info(ary[afi], rh); \
+ } \
+ } while (0)
+
+#define RFD_RTINIT(rh, ary) \
+ do { \
+ RFD_RTINIT_AFI(rh, ary, AFI_IP); \
+ RFD_RTINIT_AFI(rh, ary, AFI_IP6); \
+ RFD_RTINIT_AFI(rh, ary, AFI_L2VPN); \
+ } while (0)
+
+ RFD_RTINIT(rfd, rfd->rib);
+ RFD_RTINIT(rfd, rfd->rib_pending);
+ RFD_RTINIT(rfd, rfd->rsp_times);
+
+ /*
+ * Link to Import Table
+ */
+ rfd->import_table = rfg->rfapi_import_table;
+ rfd->import_table->refcount += 1;
+
+ rfapiApInit(&rfd->advertised);
+
+ /*
+ * add this NVE descriptor to the list of NVEs in the NVE group
+ */
+ if (!rfg->nves) {
+ rfg->nves = list_new();
+ }
+ listnode_add(rfg->nves, rfd);
+
+ vnc_direct_bgp_add_nve(bgp, rfd);
+ vnc_zebra_add_nve(bgp, rfd);
+
+ return 0;
+}
+
+/* moved from rfapi_register */
+int rfapi_init_and_open(struct bgp *bgp, struct rfapi_descriptor *rfd,
+ struct rfapi_nve_group_cfg *rfg)
+{
+ struct rfapi *h = bgp->rfapi;
+ char buf_vn[BUFSIZ];
+ char buf_un[BUFSIZ];
+ afi_t afi_vn, afi_un;
+ struct prefix pfx_un;
+ struct agg_node *rn;
+
+ rfd->open_time = monotime(NULL);
+
+ if (rfg->type == RFAPI_GROUP_CFG_VRF)
+ SET_FLAG(rfd->flags, RFAPI_HD_FLAG_IS_VRF);
+
+ rfapiRfapiIpAddr2Str(&rfd->vn_addr, buf_vn, BUFSIZ);
+ rfapiRfapiIpAddr2Str(&rfd->un_addr, buf_un, BUFSIZ);
+
+ vnc_zlog_debug_verbose("%s: new RFD with VN=%s UN=%s cookie=%p",
+ __func__, buf_vn, buf_un, rfd->cookie);
+
+ if (rfg->type != RFAPI_GROUP_CFG_VRF) /* unclear if needed for VRF */
+ {
+ listnode_add(&h->descriptors, rfd);
+ if (h->descriptors.count > h->stat.max_descriptors) {
+ h->stat.max_descriptors = h->descriptors.count;
+ }
+
+ /*
+ * attach to UN radix tree
+ */
+ afi_vn = family2afi(rfd->vn_addr.addr_family);
+ afi_un = family2afi(rfd->un_addr.addr_family);
+ assert(afi_vn && afi_un);
+ assert(!rfapiRaddr2Qprefix(&rfd->un_addr, &pfx_un));
+
+ rn = agg_node_get(h->un[afi_un], &pfx_un);
+ assert(rn);
+ rfd->next = rn->info;
+ rn->info = rfd;
+ rfd->un_node = rn;
+ }
+ return rfapi_open_inner(rfd, bgp, h, rfg);
+}
+
+struct rfapi_vn_option *rfapiVnOptionsDup(struct rfapi_vn_option *orig)
+{
+ struct rfapi_vn_option *head = NULL;
+ struct rfapi_vn_option *tail = NULL;
+ struct rfapi_vn_option *vo = NULL;
+
+ for (vo = orig; vo; vo = vo->next) {
+ struct rfapi_vn_option *new;
+
+ new = XCALLOC(MTYPE_RFAPI_VN_OPTION,
+ sizeof(struct rfapi_vn_option));
+ memcpy(new, vo, sizeof(struct rfapi_vn_option));
+ new->next = NULL;
+
+ if (tail) {
+ tail->next = new;
+ } else {
+ head = tail = new;
+ }
+ }
+ return head;
+}
+
+struct rfapi_un_option *rfapiUnOptionsDup(struct rfapi_un_option *orig)
+{
+ struct rfapi_un_option *head = NULL;
+ struct rfapi_un_option *tail = NULL;
+ struct rfapi_un_option *uo = NULL;
+
+ for (uo = orig; uo; uo = uo->next) {
+ struct rfapi_un_option *new;
+
+ new = XCALLOC(MTYPE_RFAPI_UN_OPTION,
+ sizeof(struct rfapi_un_option));
+ memcpy(new, uo, sizeof(struct rfapi_un_option));
+ new->next = NULL;
+
+ if (tail) {
+ tail->next = new;
+ } else {
+ head = tail = new;
+ }
+ }
+ return head;
+}
+
+struct bgp_tea_options *rfapiOptionsDup(struct bgp_tea_options *orig)
+{
+ struct bgp_tea_options *head = NULL;
+ struct bgp_tea_options *tail = NULL;
+ struct bgp_tea_options *hop = NULL;
+
+ for (hop = orig; hop; hop = hop->next) {
+ struct bgp_tea_options *new;
+
+ new = XCALLOC(MTYPE_BGP_TEA_OPTIONS,
+ sizeof(struct bgp_tea_options));
+ memcpy(new, hop, sizeof(struct bgp_tea_options));
+ new->next = NULL;
+ if (hop->value) {
+ new->value = XCALLOC(MTYPE_BGP_TEA_OPTIONS_VALUE,
+ hop->length);
+ memcpy(new->value, hop->value, hop->length);
+ }
+ if (tail) {
+ tail->next = new;
+ } else {
+ head = tail = new;
+ }
+ }
+ return head;
+}
+
+void rfapiFreeBgpTeaOptionChain(struct bgp_tea_options *p)
+{
+ struct bgp_tea_options *next;
+
+ while (p) {
+ next = p->next;
+
+ XFREE(MTYPE_BGP_TEA_OPTIONS_VALUE, p->value);
+ XFREE(MTYPE_BGP_TEA_OPTIONS, p);
+
+ p = next;
+ }
+}
+
+void rfapiAdbFree(struct rfapi_adb *adb)
+{
+ XFREE(MTYPE_RFAPI_ADB, adb);
+}
+
+static int
+rfapi_query_inner(void *handle, struct rfapi_ip_addr *target,
+ struct rfapi_l2address_option *l2o, /* may be NULL */
+ struct rfapi_next_hop_entry **ppNextHopEntry)
+{
+ afi_t afi;
+ struct prefix p;
+ struct prefix p_original;
+ struct agg_node *rn;
+ struct rfapi_descriptor *rfd = (struct rfapi_descriptor *)handle;
+ struct bgp *bgp = rfd->bgp;
+ struct rfapi_next_hop_entry *pNHE = NULL;
+ struct rfapi_ip_addr *self_vn_addr = NULL;
+ int eth_is_0 = 0;
+ int use_eth_resolution = 0;
+ struct rfapi_next_hop_entry *i_nhe;
+
+ /* preemptive */
+ if (!bgp) {
+ vnc_zlog_debug_verbose("%s: No BGP instance, returning ENXIO",
+ __func__);
+ return ENXIO;
+ }
+ if (!bgp->rfapi) {
+ vnc_zlog_debug_verbose("%s: No RFAPI instance, returning ENXIO",
+ __func__);
+ return ENXIO;
+ }
+ if (bgp->rfapi->flags & RFAPI_INCALLBACK) {
+ vnc_zlog_debug_verbose(
+ "%s: Called during calback, returning EDEADLK",
+ __func__);
+ return EDEADLK;
+ }
+
+ if (!is_valid_rfd(rfd)) {
+ vnc_zlog_debug_verbose("%s: invalid handle, returning EBADF",
+ __func__);
+ return EBADF;
+ }
+
+ rfd->rsp_counter++; /* dedup: identify this generation */
+ rfd->rsp_time = monotime(NULL); /* response content dedup */
+ rfd->ftd_last_allowed_time =
+ monotime(NULL) -
+ bgp->rfapi_cfg->rfp_cfg.ftd_advertisement_interval;
+
+ if (l2o) {
+ if (!memcmp(l2o->macaddr.octet, rfapi_ethaddr0.octet,
+ ETH_ALEN)) {
+ eth_is_0 = 1;
+ }
+ /* per t/c Paul/Lou 151022 */
+ if (!eth_is_0 || l2o->logical_net_id) {
+ use_eth_resolution = 1;
+ }
+ }
+
+ if (ppNextHopEntry)
+ *ppNextHopEntry = NULL;
+
+ /*
+ * Save original target in prefix form. In case of L2-based queries,
+ * p_original will be modified to reflect the L2 target
+ */
+ assert(!rfapiRaddr2Qprefix(target, &p_original));
+
+ if (bgp->rfapi_cfg->rfp_cfg.download_type == RFAPI_RFP_DOWNLOAD_FULL) {
+ /* convert query to 0/0 when full-table download is enabled */
+ memset((char *)&p, 0, sizeof(p));
+ p.family = target->addr_family;
+ } else {
+ p = p_original;
+ }
+
+ {
+ char *s;
+
+ vnc_zlog_debug_verbose("%s(rfd=%p, target=%pFX, ppNextHop=%p)",
+ __func__, rfd, &p, ppNextHopEntry);
+
+ s = ecommunity_ecom2str(rfd->import_table->rt_import_list,
+ ECOMMUNITY_FORMAT_ROUTE_MAP, 0);
+ vnc_zlog_debug_verbose(
+ "%s rfd->import_table=%p, rfd->import_table->rt_import_list: %s",
+ __func__, rfd->import_table, s);
+ XFREE(MTYPE_ECOMMUNITY_STR, s);
+ }
+
+ afi = family2afi(p.family);
+ assert(afi);
+
+ if (CHECK_FLAG(bgp->rfapi_cfg->flags,
+ BGP_VNC_CONFIG_FILTER_SELF_FROM_RSP)) {
+ self_vn_addr = &rfd->vn_addr;
+ }
+
+ if (use_eth_resolution) {
+ uint32_t logical_net_id = l2o->logical_net_id;
+ struct ecommunity *l2com;
+
+ /*
+ * fix up p_original to contain L2 address
+ */
+ rfapiL2o2Qprefix(l2o, &p_original);
+
+ l2com = bgp_rfapi_get_ecommunity_by_lni_label(
+ bgp, 1, logical_net_id, l2o->label);
+ if (l2com) {
+ uint8_t *v = l2com->val;
+ logical_net_id = (v[5] << 16) + (v[6] << 8) + (v[7]);
+ }
+ /*
+ * Ethernet/L2-based lookup
+ *
+ * Always returns IT node corresponding to route
+ */
+
+ if (RFAPI_RFP_DOWNLOAD_FULL
+ == bgp->rfapi_cfg->rfp_cfg.download_type) {
+ eth_is_0 = 1;
+ }
+
+ rn = rfapiMonitorEthAdd(
+ bgp, rfd, (eth_is_0 ? &rfapi_ethaddr0 : &l2o->macaddr),
+ logical_net_id);
+
+ if (eth_is_0) {
+ struct rfapi_ip_prefix rprefix;
+
+ memset(&rprefix, 0, sizeof(rprefix));
+ rprefix.prefix.addr_family = target->addr_family;
+ if (target->addr_family == AF_INET) {
+ rprefix.length = IPV4_MAX_BITLEN;
+ } else {
+ rprefix.length = IPV6_MAX_BITLEN;
+ }
+
+ pNHE = rfapiEthRouteTable2NextHopList(
+ logical_net_id, &rprefix,
+ rfd->response_lifetime, self_vn_addr,
+ rfd->rib[afi], &p_original);
+ goto done;
+ }
+
+ } else {
+
+ /*
+ * IP-based lookup
+ */
+
+ rn = rfapiMonitorAdd(bgp, rfd, &p);
+
+ /*
+ * If target address is 0, this request is special: means to
+ * return ALL routes in the table
+ *
+ * Monitors for All-Routes queries get put on a special list,
+ * not in the VPN tree
+ */
+ if (RFAPI_0_PREFIX(&p)) {
+
+ vnc_zlog_debug_verbose("%s: 0-prefix", __func__);
+
+ /*
+ * Generate nexthop list for caller
+ */
+ pNHE = rfapiRouteTable2NextHopList(
+ rfd->import_table->imported_vpn[afi],
+ rfd->response_lifetime, self_vn_addr,
+ rfd->rib[afi], &p_original);
+ goto done;
+ }
+
+ if (rn) {
+ agg_lock_node(rn); /* so we can unlock below */
+ } else {
+ /*
+ * returns locked node. Don't unlock yet because the
+ * unlock
+ * might free it before we're done with it. This
+ * situation
+ * could occur when rfapiMonitorGetAttachNode() returns
+ * a
+ * newly-created default node.
+ */
+ rn = rfapiMonitorGetAttachNode(rfd, &p);
+ }
+ }
+
+ assert(rn);
+ if (!rn->info) {
+ agg_unlock_node(rn);
+ vnc_zlog_debug_verbose(
+ "%s: VPN route not found, returning ENOENT", __func__);
+ return ENOENT;
+ }
+
+ if (VNC_DEBUG(RFAPI_QUERY)) {
+ rfapiShowImportTable(NULL, "query",
+ rfd->import_table->imported_vpn[afi], 1);
+ }
+
+ if (use_eth_resolution) {
+
+ struct rfapi_ip_prefix rprefix;
+
+ memset(&rprefix, 0, sizeof(rprefix));
+ rprefix.prefix.addr_family = target->addr_family;
+ if (target->addr_family == AF_INET) {
+ rprefix.length = IPV4_MAX_BITLEN;
+ } else {
+ rprefix.length = IPV6_MAX_BITLEN;
+ }
+
+ pNHE = rfapiEthRouteNode2NextHopList(
+ rn, &rprefix, rfd->response_lifetime, self_vn_addr,
+ rfd->rib[afi], &p_original);
+
+
+ } else {
+ /*
+ * Generate answer to query
+ */
+ pNHE = rfapiRouteNode2NextHopList(rn, rfd->response_lifetime,
+ self_vn_addr, rfd->rib[afi],
+ &p_original);
+ }
+
+ agg_unlock_node(rn);
+
+done:
+ if (ppNextHopEntry) {
+ /* only count if caller gets it */
+ ++bgp->rfapi->response_immediate_count;
+ }
+
+ if (!pNHE) {
+ vnc_zlog_debug_verbose("%s: NO NHEs, returning ENOENT",
+ __func__);
+ return ENOENT;
+ }
+
+ /*
+ * count nexthops for statistics
+ */
+ for (i_nhe = pNHE; i_nhe; i_nhe = i_nhe->next) {
+ ++rfd->stat_count_nh_reachable;
+ }
+
+ if (ppNextHopEntry) {
+ *ppNextHopEntry = pNHE;
+ } else {
+ rfapi_free_next_hop_list(pNHE);
+ }
+
+ vnc_zlog_debug_verbose("%s: success", __func__);
+ return 0;
+}
+
+/*
+ * support on-the-fly reassignment of an already-open nve to a new
+ * nve-group in the event that its original nve-group is
+ * administratively deleted.
+ */
+static int rfapi_open_rfd(struct rfapi_descriptor *rfd, struct bgp *bgp)
+{
+ struct prefix pfx_vn;
+ struct prefix pfx_un;
+ struct rfapi_nve_group_cfg *rfg;
+ struct rfapi *h;
+ struct rfapi_cfg *hc;
+ int rc;
+
+ h = bgp->rfapi;
+ if (!h)
+ return ENXIO;
+
+ hc = bgp->rfapi_cfg;
+ if (!hc)
+ return ENXIO;
+
+ rc = rfapiRaddr2Qprefix(&rfd->vn_addr, &pfx_vn);
+ assert(!rc);
+
+ rc = rfapiRaddr2Qprefix(&rfd->un_addr, &pfx_un);
+ assert(!rc);
+
+ /*
+ * Find the matching nve group config block
+ */
+ rfg = bgp_rfapi_cfg_match_group(hc, &pfx_vn, &pfx_un);
+ if (!rfg) {
+ return ENOENT;
+ }
+
+ /*
+ * check nve group config block for required values
+ */
+ if (!rfg->rt_export_list || !rfg->rfapi_import_table) {
+
+ return ENOMSG;
+ }
+
+ rc = rfapi_open_inner(rfd, bgp, h, rfg);
+ if (rc) {
+ return rc;
+ }
+
+ /*
+ * re-advertise registered routes, this time as part of new NVE-group
+ */
+ rfapiApReadvertiseAll(bgp, rfd);
+
+ /*
+ * re-attach callbacks to import table
+ */
+ if (!(bgp->rfapi_cfg->flags & BGP_VNC_CONFIG_CALLBACK_DISABLE)) {
+ rfapiMonitorAttachImportHd(rfd);
+ }
+
+ return 0;
+}
+
+/*------------------------------------------
+ * rfapi_open
+ *
+ * This function initializes a NVE record and associates it with
+ * the specified VN and underlay network addresses
+ *
+ * input:
+ * rfp_start_val value returned by rfp_start
+ * vn NVE virtual network address
+ *
+ * un NVE underlay network address
+ *
+ * default_options Default options to use on registrations.
+ * For now only tunnel type is supported.
+ * May be overridden per-prefix in rfapi_register().
+ * Caller owns (rfapi_open() does not free)
+ *
+ * response_cb Pointer to next hop list update callback function or
+ * NULL when no callbacks are desired.
+ *
+ * userdata Passed to subsequent response_cb invocations.
+ *
+ * output:
+ * response_lifetime The length of time that responses sent to this
+ * NVE are valid.
+ *
+ * pHandle pointer to location to store rfapi handle. The
+ * handle must be passed on subsequent rfapi_ calls.
+ *
+ *
+ * return value:
+ * 0 Success
+ * EEXIST NVE with this {vn,un} already open
+ * ENOENT No matching nve group config
+ * ENOMSG Matched nve group config was incomplete
+ * ENXIO BGP or VNC not configured
+ * EAFNOSUPPORT Matched nve group specifies auto-assignment of RD,
+ * but underlay network address is not IPv4
+ * EDEADLK Called from within a callback procedure
+ *------------------------------------------*/
+int rfapi_open(void *rfp_start_val, struct rfapi_ip_addr *vn,
+ struct rfapi_ip_addr *un,
+ struct rfapi_un_option *default_options,
+ uint32_t *response_lifetime,
+ void *userdata, /* callback cookie */
+ rfapi_handle *pHandle)
+{
+ struct bgp *bgp;
+ struct rfapi *h;
+ struct rfapi_descriptor *rfd;
+ struct rfapi_cfg *hc;
+ struct rfapi_nve_group_cfg *rfg;
+
+ struct prefix pfx_vn;
+ struct prefix pfx_un;
+
+ int rc;
+ rfapi_handle hh = NULL;
+ int reusing_provisional = 0;
+
+ {
+ char buf[2][INET_ADDRSTRLEN];
+ vnc_zlog_debug_verbose(
+ "%s: VN=%s UN=%s", __func__,
+ rfapiRfapiIpAddr2Str(vn, buf[0], INET_ADDRSTRLEN),
+ rfapiRfapiIpAddr2Str(un, buf[1], INET_ADDRSTRLEN));
+ }
+
+ assert(pHandle);
+ *pHandle = NULL;
+
+ bgp = rfapi_bgp_lookup_by_rfp(rfp_start_val);
+ if (!bgp)
+ return ENXIO;
+
+ h = bgp->rfapi;
+ if (!h)
+ return ENXIO;
+
+ hc = bgp->rfapi_cfg;
+ if (!hc)
+ return ENXIO;
+
+ if (h->flags & RFAPI_INCALLBACK)
+ return EDEADLK;
+
+ rc = rfapiRaddr2Qprefix(vn, &pfx_vn);
+ assert(!rc);
+
+ rc = rfapiRaddr2Qprefix(un, &pfx_un);
+ assert(!rc);
+
+ /*
+ * already have a descriptor with VN and UN?
+ */
+ if (!rfapi_find_handle(bgp, vn, un, &hh)) {
+ /*
+ * we might have set up a handle for static routes before
+ * this NVE was opened. In that case, reuse the handle
+ */
+ rfd = hh;
+ if (!CHECK_FLAG(rfd->flags, RFAPI_HD_FLAG_PROVISIONAL)) {
+ return EEXIST;
+ }
+
+ /*
+ * reuse provisional descriptor
+ * hh is not NULL
+ */
+ reusing_provisional = 1;
+ }
+
+ /*
+ * Find the matching nve group config block
+ */
+ rfg = bgp_rfapi_cfg_match_group(hc, &pfx_vn, &pfx_un);
+ if (!rfg) {
+ ++h->stat.count_unknown_nves;
+ {
+ char buf[2][INET_ADDRSTRLEN];
+ zlog_notice("%s: no matching group VN=%s UN=%s",
+ __func__,
+ rfapiRfapiIpAddr2Str(vn, buf[0],
+ INET_ADDRSTRLEN),
+ rfapiRfapiIpAddr2Str(un, buf[1],
+ INET_ADDRSTRLEN));
+ }
+ return ENOENT;
+ }
+
+ /*
+ * check nve group config block for required values
+ */
+ if (!rfg->rt_export_list || !rfg->rfapi_import_table) {
+
+ ++h->stat.count_unknown_nves;
+ return ENOMSG;
+ }
+
+ /*
+ * If group config specifies auto-rd assignment, check that
+ * VN address is IPv4|v6 so we don't fail in rfapi_open_inner().
+ * Check here so we don't need to unwind memory allocations, &c.
+ */
+ if ((rfg->rd.family == AF_UNIX) && (vn->addr_family != AF_INET)
+ && (vn->addr_family != AF_INET6)) {
+ return EAFNOSUPPORT;
+ }
+
+ if (hh) {
+ /*
+ * reusing provisional rfd
+ */
+ rfd = hh;
+ } else {
+ rfd = XCALLOC(MTYPE_RFAPI_DESC,
+ sizeof(struct rfapi_descriptor));
+ }
+
+ rfd->bgp = bgp;
+ if (default_options) {
+ struct rfapi_un_option *p;
+
+ for (p = default_options; p; p = p->next) {
+ if ((RFAPI_UN_OPTION_TYPE_PROVISIONAL == p->type)) {
+ rfd->flags |= RFAPI_HD_FLAG_PROVISIONAL;
+ }
+ if ((RFAPI_UN_OPTION_TYPE_TUNNELTYPE == p->type)) {
+ rfd->default_tunneltype_option = p->v.tunnel;
+ }
+ }
+ }
+
+ /*
+ * Fill in caller fields
+ */
+ rfd->vn_addr = *vn;
+ rfd->un_addr = *un;
+ rfd->cookie = userdata;
+
+ if (!reusing_provisional) {
+ rc = rfapi_init_and_open(bgp, rfd, rfg);
+ /*
+ * This can fail only if the VN address is IPv6 and the group
+ * specified auto-assignment of RDs, which only works for v4,
+ * and the check above should catch it.
+ *
+ * Another failure possibility is that we were called
+ * during an rfapi callback. Also checked above.
+ */
+ assert(!rc);
+ }
+
+ if (response_lifetime)
+ *response_lifetime = rfd->response_lifetime;
+ *pHandle = rfd;
+ return 0;
+}
+
+/*
+ * For use with debug functions
+ */
+static int rfapi_set_response_cb(struct rfapi_descriptor *rfd,
+ rfapi_response_cb_t *response_cb)
+{
+ if (!is_valid_rfd(rfd))
+ return EBADF;
+ rfd->response_cb = response_cb;
+ return 0;
+}
+
+/*
+ * rfapi_close_inner
+ *
+ * Does almost all the work of rfapi_close, except:
+ * 1. preserves the descriptor (doesn't free it)
+ * 2. preserves the prefix query list (i.e., rfd->mon list)
+ * 3. preserves the advertised prefix list (rfd->advertised)
+ * 4. preserves the rib and rib_pending tables
+ *
+ * The purpose of organizing it this way is to support on-the-fly
+ * reassignment of an already-open nve to a new nve-group in the
+ * event that its original nve-group is administratively deleted.
+ */
+static int rfapi_close_inner(struct rfapi_descriptor *rfd, struct bgp *bgp)
+{
+ int rc;
+ struct prefix pfx_vn;
+ struct prefix_rd prd; /* currently always 0 for VN->UN */
+
+ if (!is_valid_rfd(rfd))
+ return EBADF;
+
+ rc = rfapiRaddr2Qprefix(&rfd->vn_addr, &pfx_vn);
+ assert(!rc); /* should never have bad AF in stored vn address */
+
+ /*
+ * update exported routes to reflect disappearance of this NVE as
+ * nexthop
+ */
+ vnc_direct_bgp_del_nve(bgp, rfd);
+ vnc_zebra_del_nve(bgp, rfd);
+
+ /*
+ * unlink this HD's monitors from import table
+ */
+ rfapiMonitorDetachImportHd(rfd);
+
+ /*
+ * Unlink from Import Table
+ * NB rfd->import_table will be NULL if we are closing a stale
+ * descriptor
+ */
+ if (rfd->import_table)
+ rfapiImportTableRefDelByIt(bgp, rfd->import_table);
+ rfd->import_table = NULL;
+
+ /*
+ * Construct route distinguisher
+ */
+ memset(&prd, 0, sizeof(prd));
+ prd = rfd->rd;
+ prd.family = AF_UNSPEC;
+ prd.prefixlen = 64;
+
+ /*
+ * withdraw tunnel
+ */
+ del_vnc_route(rfd, rfd->peer, bgp, SAFI_ENCAP,
+ &pfx_vn, /* prefix being advertised */
+ &prd, /* route distinguisher to use (0 for ENCAP) */
+ ZEBRA_ROUTE_BGP, BGP_ROUTE_RFP, NULL, 0); /* no kill */
+
+ /*
+ * Construct route distinguisher for VPN routes
+ */
+ prd = rfd->rd;
+ prd.family = AF_UNSPEC;
+ prd.prefixlen = 64;
+
+ /*
+ * find all VPN routes associated with this rfd and delete them, too
+ */
+ rfapiApWithdrawAll(bgp, rfd);
+
+ /*
+ * remove this nve descriptor from the list of nves
+ * associated with the nve group
+ */
+ if (rfd->rfg) {
+ listnode_delete(rfd->rfg->nves, rfd);
+ rfd->rfg = NULL; /* XXX mark as orphaned/stale */
+ }
+
+ if (rfd->rt_export_list)
+ ecommunity_free(&rfd->rt_export_list);
+ rfd->rt_export_list = NULL;
+
+ /*
+ * free peer structure (possibly delayed until its
+ * refcount reaches zero)
+ */
+ if (rfd->peer) {
+ vnc_zlog_debug_verbose("%s: calling peer_delete(%p), #%d",
+ __func__, rfd->peer, rfd->peer->lock);
+ peer_delete(rfd->peer);
+ }
+ rfd->peer = NULL;
+
+ return 0;
+}
+
+int rfapi_close(void *handle)
+{
+ struct rfapi_descriptor *rfd = (struct rfapi_descriptor *)handle;
+ int rc;
+ struct agg_node *node;
+ struct bgp *bgp;
+ struct rfapi *h;
+
+ vnc_zlog_debug_verbose("%s: rfd=%p", __func__, rfd);
+
+#ifdef RFAPI_WHO_IS_CALLING_ME
+#ifdef HAVE_GLIBC_BACKTRACE
+#define RFAPI_DEBUG_BACKTRACE_NENTRIES 5
+ {
+ void *buf[RFAPI_DEBUG_BACKTRACE_NENTRIES];
+ char **syms;
+ int i;
+ size_t size;
+
+ size = backtrace(buf, RFAPI_DEBUG_BACKTRACE_NENTRIES);
+ syms = backtrace_symbols(buf, size);
+ for (i = 0; i < size && i < RFAPI_DEBUG_BACKTRACE_NENTRIES;
+ ++i) {
+ vnc_zlog_debug_verbose("backtrace[%2d]: %s", i,
+ syms[i]);
+ }
+ free(syms);
+ }
+#endif
+#endif
+
+ bgp = rfd->bgp;
+ if (!bgp)
+ return ENXIO;
+
+ h = bgp->rfapi;
+ if (!h)
+ return ENXIO;
+
+ if (!is_valid_rfd(rfd))
+ return EBADF;
+
+ if (h->flags & RFAPI_INCALLBACK) {
+ /*
+ * Queue these close requests for processing after callback
+ * is finished
+ */
+ if (!CHECK_FLAG(rfd->flags,
+ RFAPI_HD_FLAG_CLOSING_ADMINISTRATIVELY)) {
+ work_queue_add(h->deferred_close_q, handle);
+ vnc_zlog_debug_verbose(
+ "%s: added handle %p to deferred close queue",
+ __func__, handle);
+ }
+ return 0;
+ }
+
+ if (CHECK_FLAG(rfd->flags, RFAPI_HD_FLAG_CLOSING_ADMINISTRATIVELY)) {
+
+ vnc_zlog_debug_verbose("%s administrative close rfd=%p",
+ __func__, rfd);
+
+ if (h->rfp_methods.close_cb) {
+ vnc_zlog_debug_verbose(
+ "%s calling close callback rfd=%p", __func__,
+ rfd);
+
+ /*
+ * call the callback fairly early so that it can still
+ * lookup un/vn
+ * from handle, etc.
+ *
+ * NB RFAPI_INCALLBACK is tested above, so if we reach
+ * this point
+ * we are not already in the context of a callback.
+ */
+ h->flags |= RFAPI_INCALLBACK;
+ (*h->rfp_methods.close_cb)(handle, EIDRM);
+ h->flags &= ~RFAPI_INCALLBACK;
+ }
+ }
+
+ if (rfd->rfg) {
+ /*
+ * Orphaned descriptors have already done this part, so do
+ * only for non-orphaned descriptors.
+ */
+ if ((rc = rfapi_close_inner(rfd, bgp)))
+ return rc;
+ }
+
+ /*
+ * Remove descriptor from UN index
+ * (remove from chain at node)
+ */
+ rc = rfapi_find_node(bgp, &rfd->vn_addr, &rfd->un_addr, &node);
+ if (!rc) {
+ struct rfapi_descriptor *hh;
+
+ if (node->info == rfd) {
+ node->info = rfd->next;
+ } else {
+
+ for (hh = node->info; hh; hh = hh->next) {
+ if (hh->next == rfd) {
+ hh->next = rfd->next;
+ break;
+ }
+ }
+ }
+ agg_unlock_node(node);
+ }
+
+ /*
+ * remove from descriptor list
+ */
+ listnode_delete(&h->descriptors, rfd);
+
+ /*
+ * Delete monitor list items and free monitor structures
+ */
+ (void)rfapiMonitorDelHd(rfd);
+
+ /*
+ * release advertised prefix data
+ */
+ rfapiApRelease(&rfd->advertised);
+
+ /*
+ * Release RFP callback RIB
+ */
+ rfapiRibFree(rfd);
+
+ /*
+ * free descriptor
+ */
+ memset(rfd, 0, sizeof(struct rfapi_descriptor));
+ XFREE(MTYPE_RFAPI_DESC, rfd);
+
+ return 0;
+}
+
+/*
+ * Reopen a nve descriptor. If the descriptor's NVE-group
+ * does not exist (e.g., if it has been administratively removed),
+ * reassignment to a new NVE-group is attempted.
+ *
+ * If NVE-group reassignment fails, the descriptor becomes "stale"
+ * (rfd->rfg == NULL implies "stale:). The only permissible API operation
+ * on a stale descriptor is rfapi_close(). Any other rfapi_* API operation
+ * on the descriptor will return ESTALE.
+ *
+ * Reopening a descriptor is a potentially expensive operation, because
+ * it involves withdrawing any routes advertised by the NVE, withdrawing
+ * the NVE's route queries, and then re-adding them all after a new
+ * NVE-group is assigned. There are also possible route-export affects
+ * caused by deleting and then adding the NVE: advertised prefixes
+ * and nexthop lists for exported routes can turn over.
+ */
+int rfapi_reopen(struct rfapi_descriptor *rfd, struct bgp *bgp)
+{
+ struct rfapi *h;
+ int rc;
+
+ if ((rc = rfapi_close_inner(rfd, bgp))) {
+ return rc;
+ }
+ if ((rc = rfapi_open_rfd(rfd, bgp))) {
+
+ h = bgp->rfapi;
+
+ assert(h != NULL && !CHECK_FLAG(h->flags, RFAPI_INCALLBACK));
+
+ if (CHECK_FLAG(rfd->flags,
+ RFAPI_HD_FLAG_CLOSING_ADMINISTRATIVELY)
+ && h && h->rfp_methods.close_cb) {
+
+ /*
+ * NB RFAPI_INCALLBACK is tested above, so if we reach
+ * this point
+ * we are not already in the context of a callback.
+ */
+ h->flags |= RFAPI_INCALLBACK;
+ (*h->rfp_methods.close_cb)((rfapi_handle)rfd, ESTALE);
+ h->flags &= ~RFAPI_INCALLBACK;
+ }
+ return rc;
+ }
+ return 0;
+}
+
+/***********************************************************************
+ * NVE Routes
+ ***********************************************************************/
+/*
+ * Announce reachability to this prefix via the NVE
+ */
+int rfapi_register(void *handle, struct rfapi_ip_prefix *prefix,
+ uint32_t lifetime, /* host byte order */
+ struct rfapi_un_option *options_un,
+ struct rfapi_vn_option *options_vn,
+ rfapi_register_action action)
+{
+ struct rfapi_descriptor *rfd = (struct rfapi_descriptor *)handle;
+ struct bgp *bgp;
+ struct prefix p;
+ struct prefix *pfx_ip = NULL;
+ struct prefix_rd prd;
+ afi_t afi;
+ struct prefix pfx_mac_buf;
+ struct prefix *pfx_mac = NULL;
+ struct prefix pfx_vn_buf;
+ const char *action_str = NULL;
+ uint32_t *label = NULL;
+ struct rfapi_vn_option *vo;
+ struct rfapi_l2address_option *l2o = NULL;
+ struct prefix_rd *prd_override = NULL;
+
+ switch (action) {
+ case RFAPI_REGISTER_ADD:
+ action_str = "add";
+ break;
+ case RFAPI_REGISTER_WITHDRAW:
+ action_str = "withdraw";
+ break;
+ case RFAPI_REGISTER_KILL:
+ action_str = "kill";
+ break;
+ default:
+ assert(0);
+ break;
+ }
+
+ /*
+ * Inspect VN options
+ */
+ for (vo = options_vn; vo; vo = vo->next) {
+ if (RFAPI_VN_OPTION_TYPE_L2ADDR == vo->type) {
+ l2o = &vo->v.l2addr;
+ }
+ if (RFAPI_VN_OPTION_TYPE_INTERNAL_RD == vo->type) {
+ prd_override = &vo->v.internal_rd;
+ }
+ }
+
+ /*********************************************************************
+ * advertise prefix
+ *********************************************************************/
+
+ /*
+ * set <p> based on <prefix>
+ */
+ assert(!rfapiRprefix2Qprefix(prefix, &p));
+
+ afi = family2afi(prefix->prefix.addr_family);
+ assert(afi);
+
+ vnc_zlog_debug_verbose(
+ "%s(rfd=%p, pfx=%pFX, lifetime=%d, opts_un=%p, opts_vn=%p, action=%s)",
+ __func__, rfd, &p, lifetime, options_un, options_vn,
+ action_str);
+
+ /*
+ * These tests come after the prefix conversion so that we can
+ * print the prefix in a debug message before failing
+ */
+
+ bgp = rfd->bgp;
+ if (!bgp) {
+ vnc_zlog_debug_verbose("%s: no BGP instance: returning ENXIO",
+ __func__);
+ return ENXIO;
+ }
+ if (!bgp->rfapi) {
+ vnc_zlog_debug_verbose("%s: no RFAPI instance: returning ENXIO",
+ __func__);
+ return ENXIO;
+ }
+ if (!rfd->rfg) {
+ if (RFAPI_REGISTER_ADD == action) {
+ ++bgp->rfapi->stat.count_registrations_failed;
+ }
+ vnc_zlog_debug_verbose(
+ "%s: rfd=%p, no RF GRP instance: returning ESTALE",
+ __func__, rfd);
+ return ESTALE;
+ }
+
+ if (bgp->rfapi->flags & RFAPI_INCALLBACK) {
+ if (RFAPI_REGISTER_ADD == action) {
+ ++bgp->rfapi->stat.count_registrations_failed;
+ }
+ vnc_zlog_debug_verbose("%s: in callback: returning EDEADLK",
+ __func__);
+ return EDEADLK;
+ }
+
+ if (!is_valid_rfd(rfd)) {
+ if (RFAPI_REGISTER_ADD == action) {
+ ++bgp->rfapi->stat.count_registrations_failed;
+ }
+ vnc_zlog_debug_verbose("%s: invalid handle: returning EBADF",
+ __func__);
+ return EBADF;
+ }
+
+ /*
+ * Is there a MAC address in this registration?
+ */
+ if (l2o && !RFAPI_0_ETHERADDR(&l2o->macaddr)) {
+ rfapiL2o2Qprefix(l2o, &pfx_mac_buf);
+ pfx_mac = &pfx_mac_buf;
+ }
+
+ /*
+ * Is there an IP prefix in this registration?
+ */
+ if (!(RFAPI_0_PREFIX(&p) && RFAPI_HOST_PREFIX(&p))) {
+ pfx_ip = &p;
+ } else {
+ if (!pfx_mac) {
+ vnc_zlog_debug_verbose(
+ "%s: missing mac addr that is required for host 0 pfx",
+ __func__);
+ if (RFAPI_REGISTER_ADD == action) {
+ ++bgp->rfapi->stat.count_registrations_failed;
+ }
+ return EINVAL;
+ }
+ if (rfapiRaddr2Qprefix(&rfd->vn_addr, &pfx_vn_buf)) {
+ vnc_zlog_debug_verbose(
+ "%s: handle has bad vn_addr: returning EBADF",
+ __func__);
+ if (RFAPI_REGISTER_ADD == action) {
+ ++bgp->rfapi->stat.count_registrations_failed;
+ }
+ return EBADF;
+ }
+ }
+
+ if (RFAPI_REGISTER_ADD == action) {
+ ++bgp->rfapi->stat.count_registrations;
+ }
+
+ /*
+ * Figure out if this registration is missing an IP address
+ *
+ * MAC-addr based:
+ *
+ * In RFAPI, we use prefixes in family AF_LINK to store
+ * the MAC addresses. These prefixes are used for the
+ * list of advertised prefixes and in the RFAPI import
+ * tables.
+ *
+ * In BGP proper, we use the prefix matching the NVE's
+ * VN address with a host prefix-length (i.e., 32 or 128).
+ *
+ */
+ if (l2o && l2o->logical_net_id && RFAPI_0_PREFIX(&p)
+ && RFAPI_HOST_PREFIX(&p)) {
+
+ rfapiL2o2Qprefix(l2o, &pfx_mac_buf);
+ pfx_mac = &pfx_mac_buf;
+ }
+
+ /*
+ * Construct route distinguisher
+ */
+ if (prd_override) {
+ prd = *prd_override;
+ } else {
+ memset(&prd, 0, sizeof(prd));
+ if (pfx_mac) {
+ prd.family = AF_UNSPEC;
+ prd.prefixlen = 64;
+ encode_rd_type(RD_TYPE_VNC_ETH, prd.val);
+ if (l2o->local_nve_id
+ || !(rfd->rfg->flags & RFAPI_RFG_L2RD)) {
+ /*
+ * If Local NVE ID is specified in message, use
+ * it.
+ * (if no local default configured, also use it
+ * even if 0)
+ */
+ prd.val[1] = l2o->local_nve_id;
+ } else {
+ if (rfd->rfg->l2rd) {
+ /*
+ * locally-configured literal value
+ */
+ prd.val[1] = rfd->rfg->l2rd;
+ } else {
+ /*
+ * 0 means auto:vn, which means use LSB
+ * of VN addr
+ */
+ if (rfd->vn_addr.addr_family
+ == AF_INET) {
+ prd.val[1] =
+ *(((char *)&rfd->vn_addr
+ .addr.v4
+ .s_addr)
+ + 3);
+ } else {
+ prd.val[1] =
+ *(((char *)&rfd->vn_addr
+ .addr.v6
+ .s6_addr)
+ + 15);
+ }
+ }
+ }
+ memcpy(prd.val + 2, pfx_mac->u.prefix_eth.octet, 6);
+ } else {
+ prd = rfd->rd;
+ prd.family = AF_UNSPEC;
+ prd.prefixlen = 64;
+ }
+ }
+
+
+ if (action == RFAPI_REGISTER_WITHDRAW
+ || action == RFAPI_REGISTER_KILL) {
+
+ int adv_tunnel = 0;
+
+ /*
+ * withdraw previous advertisement
+ */
+ del_vnc_route(
+ rfd, rfd->peer, bgp, SAFI_MPLS_VPN,
+ pfx_ip ? pfx_ip
+ : &pfx_vn_buf, /* prefix being advertised */
+ &prd, /* route distinguisher (0 for ENCAP) */
+ ZEBRA_ROUTE_BGP, BGP_ROUTE_RFP, NULL,
+ action == RFAPI_REGISTER_KILL);
+
+ if (0 == rfapiApDelete(bgp, rfd, &p, pfx_mac, &prd,
+ &adv_tunnel)) {
+ if (adv_tunnel)
+ rfapiTunnelRouteAnnounce(
+ bgp, rfd, &rfd->max_prefix_lifetime);
+ }
+
+ } else {
+
+ int adv_tunnel = 0;
+ uint32_t local_pref;
+ struct ecommunity *rtlist = NULL;
+ struct ecommunity_val ecom_value;
+
+ if (!rfapiApCount(rfd)) {
+ /*
+ * make sure we advertise tunnel route upon adding the
+ * first VPN route
+ */
+ adv_tunnel = 1;
+ }
+
+ if (rfapiApAdd(bgp, rfd, &p, pfx_mac, &prd, lifetime,
+ prefix->cost, l2o)) {
+ adv_tunnel = 1;
+ }
+
+ vnc_zlog_debug_verbose("%s: adv_tunnel = %d", __func__,
+ adv_tunnel);
+ if (adv_tunnel) {
+ vnc_zlog_debug_verbose("%s: announcing tunnel route",
+ __func__);
+ rfapiTunnelRouteAnnounce(bgp, rfd,
+ &rfd->max_prefix_lifetime);
+ }
+
+ vnc_zlog_debug_verbose("%s: calling add_vnc_route", __func__);
+
+ local_pref = rfp_cost_to_localpref(prefix->cost);
+
+ if (l2o && l2o->label)
+ label = &l2o->label;
+
+ if (pfx_mac) {
+ struct ecommunity *l2com = NULL;
+
+ if (label) {
+ l2com = bgp_rfapi_get_ecommunity_by_lni_label(
+ bgp, 1, l2o->logical_net_id, *label);
+ }
+ if (l2com) {
+ rtlist = ecommunity_dup(l2com);
+ } else {
+ /*
+ * If mac address is set, add an RT based on the
+ * registered LNI
+ */
+ memset((char *)&ecom_value, 0,
+ sizeof(ecom_value));
+ ecom_value.val[1] = ECOMMUNITY_ROUTE_TARGET;
+ ecom_value.val[5] =
+ (l2o->logical_net_id >> 16) & 0xff;
+ ecom_value.val[6] =
+ (l2o->logical_net_id >> 8) & 0xff;
+ ecom_value.val[7] =
+ (l2o->logical_net_id >> 0) & 0xff;
+ rtlist = ecommunity_new();
+ ecommunity_add_val(rtlist, &ecom_value,
+ false, false);
+ }
+ if (l2o->tag_id) {
+ as_t as = bgp->as;
+ uint16_t val = l2o->tag_id;
+ memset((char *)&ecom_value, 0,
+ sizeof(ecom_value));
+ ecom_value.val[1] = ECOMMUNITY_ROUTE_TARGET;
+ if (as > BGP_AS_MAX) {
+ ecom_value.val[0] =
+ ECOMMUNITY_ENCODE_AS4;
+ ecom_value.val[2] = (as >> 24) & 0xff;
+ ecom_value.val[3] = (as >> 16) & 0xff;
+ ecom_value.val[4] = (as >> 8) & 0xff;
+ ecom_value.val[5] = as & 0xff;
+ } else {
+ ecom_value.val[0] =
+ ECOMMUNITY_ENCODE_AS;
+ ecom_value.val[2] = (as >> 8) & 0xff;
+ ecom_value.val[3] = as & 0xff;
+ }
+ ecom_value.val[6] = (val >> 8) & 0xff;
+ ecom_value.val[7] = val & 0xff;
+ if (rtlist == NULL)
+ rtlist = ecommunity_new();
+ ecommunity_add_val(rtlist, &ecom_value,
+ false, false);
+ }
+ }
+
+ /*
+ * advertise prefix via tunnel endpoint
+ */
+ add_vnc_route(
+ rfd, /* rfapi descr, for export list & backref */
+ bgp, /* which bgp instance */
+ SAFI_MPLS_VPN, /* which SAFI */
+ (pfx_ip ? pfx_ip
+ : &pfx_vn_buf), /* prefix being advertised */
+ &prd, /* route distinguisher to use (0 for ENCAP) */
+ &rfd->vn_addr, /* nexthop */
+ &local_pref,
+ &lifetime, /* prefix lifetime -> Tunnel Encap attr */
+ NULL, options_un, /* rfapi un options */
+ options_vn, /* rfapi vn options */
+ (rtlist ? rtlist : rfd->rt_export_list), NULL, /* med */
+ label, /* label: default */
+ ZEBRA_ROUTE_BGP, BGP_ROUTE_RFP, 0);
+
+ if (rtlist)
+ ecommunity_free(&rtlist); /* sets rtlist = NULL */
+ }
+
+ vnc_zlog_debug_verbose("%s: success", __func__);
+ return 0;
+}
+
+int rfapi_query(void *handle, struct rfapi_ip_addr *target,
+ struct rfapi_l2address_option *l2o, /* may be NULL */
+ struct rfapi_next_hop_entry **ppNextHopEntry)
+{
+ struct rfapi_descriptor *rfd = (struct rfapi_descriptor *)handle;
+ struct bgp *bgp = rfd->bgp;
+ int rc;
+
+ assert(ppNextHopEntry);
+ *ppNextHopEntry = NULL;
+
+ if (bgp && bgp->rfapi) {
+ bgp->rfapi->stat.count_queries++;
+ }
+
+ if (!rfd->rfg) {
+ if (bgp && bgp->rfapi)
+ ++bgp->rfapi->stat.count_queries_failed;
+ return ESTALE;
+ }
+
+ if ((rc = rfapi_query_inner(handle, target, l2o, ppNextHopEntry))) {
+ if (bgp && bgp->rfapi)
+ ++bgp->rfapi->stat.count_queries_failed;
+ }
+ return rc;
+}
+
+int rfapi_query_done(rfapi_handle handle, struct rfapi_ip_addr *target)
+{
+ struct prefix p;
+ int rc;
+ struct rfapi_descriptor *rfd = (struct rfapi_descriptor *)handle;
+ struct bgp *bgp = rfd->bgp;
+
+ if (!rfd->rfg)
+ return ESTALE;
+
+ assert(target);
+ rc = rfapiRaddr2Qprefix(target, &p);
+ assert(!rc);
+
+ if (!is_valid_rfd(rfd))
+ return EBADF;
+
+ /* preemptive */
+ if (!bgp || !bgp->rfapi)
+ return ENXIO;
+
+ if (bgp->rfapi->flags & RFAPI_INCALLBACK)
+ return EDEADLK;
+
+ rfapiMonitorDel(bgp, rfd, &p);
+
+ return 0;
+}
+
+int rfapi_query_done_all(rfapi_handle handle, int *count)
+{
+ struct rfapi_descriptor *rfd = (struct rfapi_descriptor *)handle;
+ struct bgp *bgp = rfd->bgp;
+ ;
+ int num;
+
+ if (!rfd->rfg)
+ return ESTALE;
+
+ if (!is_valid_rfd(rfd))
+ return EBADF;
+
+ /* preemptive */
+ if (!bgp || !bgp->rfapi)
+ return ENXIO;
+
+ if (bgp->rfapi->flags & RFAPI_INCALLBACK)
+ return EDEADLK;
+
+ num = rfapiMonitorDelHd(rfd);
+
+ if (count)
+ *count = num;
+
+ return 0;
+}
+
+void rfapi_free_next_hop_list(struct rfapi_next_hop_entry *list)
+{
+ struct rfapi_next_hop_entry *nh;
+ struct rfapi_next_hop_entry *next;
+
+ for (nh = list; nh; nh = next) {
+ next = nh->next;
+ rfapi_un_options_free(nh->un_options);
+ nh->un_options = NULL;
+ rfapi_vn_options_free(nh->vn_options);
+ nh->vn_options = NULL;
+ XFREE(MTYPE_RFAPI_NEXTHOP, nh);
+ }
+}
+
+/*
+ * NULL handle => return total count across all nves
+ */
+uint32_t rfapi_monitor_count(void *handle)
+{
+ struct bgp *bgp = bgp_get_default();
+ uint32_t count;
+
+ if (handle) {
+ struct rfapi_descriptor *rfd =
+ (struct rfapi_descriptor *)handle;
+ count = rfd->monitor_count;
+ } else {
+
+ if (!bgp || !bgp->rfapi)
+ return 0;
+
+ count = bgp->rfapi->monitor_count;
+ }
+
+ return count;
+}
+
+/***********************************************************************
+ * CLI/CONFIG
+ ***********************************************************************/
+
+DEFUN (debug_rfapi_show_nves,
+ debug_rfapi_show_nves_cmd,
+ "debug rfapi-dev show nves",
+ DEBUG_STR
+ DEBUG_RFAPI_STR
+ SHOW_STR
+ "NVE Information\n")
+{
+ rfapiPrintMatchingDescriptors(vty, NULL, NULL);
+ return CMD_SUCCESS;
+}
+
+DEFUN (
+ debug_rfapi_show_nves_vn_un,
+ debug_rfapi_show_nves_vn_un_cmd,
+ "debug rfapi-dev show nves <vn|un> <A.B.C.D|X:X::X:X>", /* prefix also ok */
+ DEBUG_STR
+ DEBUG_RFAPI_STR
+ SHOW_STR
+ "NVE Information\n"
+ "Specify virtual network\n"
+ "Specify underlay network interface\n"
+ "IPv4 address\n"
+ "IPv6 address\n")
+{
+ struct prefix pfx;
+
+ if (!str2prefix(argv[5]->arg, &pfx)) {
+ vty_out(vty, "Malformed address \"%s\"\n", argv[5]->arg);
+ return CMD_WARNING_CONFIG_FAILED;
+ }
+ if (pfx.family != AF_INET && pfx.family != AF_INET6) {
+ vty_out(vty, "Invalid address \"%s\"\n", argv[5]->arg);
+ return CMD_WARNING_CONFIG_FAILED;
+ }
+
+ if (argv[4]->arg[0] == 'u') {
+ rfapiPrintMatchingDescriptors(vty, NULL, &pfx);
+ } else {
+ rfapiPrintMatchingDescriptors(vty, &pfx, NULL);
+ }
+ return CMD_SUCCESS;
+}
+
+/*
+ * Note: this function does not flush vty output, so if it is called
+ * with a stream pointing to a vty, the user will have to type something
+ * before the callback output shows up
+ */
+static void test_nexthops_callback(
+ // struct rfapi_ip_addr *target,
+ struct rfapi_next_hop_entry *next_hops, void *userdata)
+{
+ void *stream = userdata;
+
+ int (*fp)(void *, const char *, ...);
+ struct vty *vty;
+ void *out;
+ const char *vty_newline;
+
+ if (rfapiStream2Vty(stream, &fp, &vty, &out, &vty_newline) == 0)
+ return;
+
+ fp(out, "Nexthops Callback, Target=(");
+ // rfapiPrintRfapiIpAddr(stream, target);
+ fp(out, ")\n");
+
+ rfapiPrintNhl(stream, next_hops);
+
+ fp(out, "\n");
+
+ rfapi_free_next_hop_list(next_hops);
+}
+
+DEFUN (debug_rfapi_open,
+ debug_rfapi_open_cmd,
+ "debug rfapi-dev open vn <A.B.C.D|X:X::X:X> un <A.B.C.D|X:X::X:X>",
+ DEBUG_STR
+ DEBUG_RFAPI_STR
+ "rfapi_open\n"
+ "indicate vn addr follows\n"
+ "virtual network interface IPv4 address\n"
+ "virtual network interface IPv6 address\n"
+ "indicate xt addr follows\n"
+ "underlay network interface IPv4 address\n"
+ "underlay network interface IPv6 address\n")
+{
+ struct rfapi_ip_addr vn;
+ struct rfapi_ip_addr un;
+ uint32_t lifetime = 0;
+ int rc;
+ rfapi_handle handle;
+
+ /*
+ * Get VN addr
+ */
+ if ((rc = rfapiCliGetRfapiIpAddr(vty, argv[4]->arg, &vn)))
+ return rc;
+
+ /*
+ * Get UN addr
+ */
+ if ((rc = rfapiCliGetRfapiIpAddr(vty, argv[6]->arg, &un)))
+ return rc;
+
+ rc = rfapi_open(rfapi_get_rfp_start_val_by_bgp(bgp_get_default()), &vn,
+ &un, /*&uo */ NULL, &lifetime, NULL, &handle);
+
+ vty_out(vty, "rfapi_open: status %d, handle %p, lifetime %d\n", rc,
+ handle, lifetime);
+
+ rc = rfapi_set_response_cb(handle, test_nexthops_callback);
+
+ vty_out(vty, "rfapi_set_response_cb: status %d\n", rc);
+
+ return CMD_SUCCESS;
+}
+
+
+DEFUN (debug_rfapi_close_vn_un,
+ debug_rfapi_close_vn_un_cmd,
+ "debug rfapi-dev close vn <A.B.C.D|X:X::X:X> un <A.B.C.D|X:X::X:X>",
+ DEBUG_STR
+ DEBUG_RFAPI_STR
+ "rfapi_close\n"
+ "indicate vn addr follows\n"
+ "virtual network interface IPv4 address\n"
+ "virtual network interface IPv6 address\n"
+ "indicate xt addr follows\n"
+ "underlay network interface IPv4 address\n"
+ "underlay network interface IPv6 address\n")
+{
+ struct rfapi_ip_addr vn;
+ struct rfapi_ip_addr un;
+ rfapi_handle handle;
+ int rc;
+
+ /*
+ * Get VN addr
+ */
+ if ((rc = rfapiCliGetRfapiIpAddr(vty, argv[4]->arg, &vn)))
+ return rc;
+
+
+ /*
+ * Get UN addr
+ */
+ if ((rc = rfapiCliGetRfapiIpAddr(vty, argv[6]->arg, &un)))
+ return rc;
+
+
+ if (rfapi_find_handle_vty(vty, &vn, &un, &handle)) {
+ vty_out(vty, "can't locate handle matching vn=%s, un=%s\n",
+ argv[4]->arg, argv[6]->arg);
+ return CMD_WARNING_CONFIG_FAILED;
+ }
+
+ rc = rfapi_close(handle);
+
+ vty_out(vty, "rfapi_close(handle=%p): status %d\n", handle, rc);
+
+ return CMD_SUCCESS;
+}
+
+DEFUN (debug_rfapi_close_rfd,
+ debug_rfapi_close_rfd_cmd,
+ "debug rfapi-dev close rfd HANDLE",
+ DEBUG_STR
+ DEBUG_RFAPI_STR
+ "rfapi_close\n"
+ "indicate handle follows\n" "rfapi handle in hexadecimal\n")
+{
+ rfapi_handle handle;
+ int rc;
+ char *endptr = NULL;
+
+ handle = (rfapi_handle)(uintptr_t)(strtoull(argv[4]->arg, &endptr, 16));
+
+ if (*endptr != '\0' || (uintptr_t)handle == UINTPTR_MAX) {
+ vty_out(vty, "Invalid value: %s\n", argv[4]->arg);
+ return CMD_WARNING_CONFIG_FAILED;
+ }
+
+ rc = rfapi_close(handle);
+
+ vty_out(vty, "rfapi_close(handle=%p): status %d\n", handle, rc);
+
+ return CMD_SUCCESS;
+}
+
+DEFUN (debug_rfapi_register_vn_un,
+ debug_rfapi_register_vn_un_cmd,
+ "debug rfapi-dev register vn <A.B.C.D|X:X::X:X> un <A.B.C.D|X:X::X:X> prefix <A.B.C.D/M|X:X::X:X/M> lifetime SECONDS [cost (0-255)]",
+ DEBUG_STR
+ DEBUG_RFAPI_STR
+ "rfapi_register\n"
+ "indicate vn addr follows\n"
+ "virtual network IPv4 interface address\n"
+ "virtual network IPv6 interface address\n"
+ "indicate un addr follows\n"
+ "underlay network IPv4 interface address\n"
+ "underlay network IPv6 interface address\n"
+ "indicate prefix follows\n"
+ "IPv4 prefix\n"
+ "IPv6 prefix\n"
+ "indicate lifetime follows\n"
+ "lifetime\n"
+ "Cost (localpref = 255-cost)\n"
+ "0-255\n")
+{
+ struct rfapi_ip_addr vn;
+ struct rfapi_ip_addr un;
+ rfapi_handle handle;
+ struct prefix pfx;
+ uint32_t lifetime;
+ struct rfapi_ip_prefix hpfx;
+ int rc;
+ uint8_t cost = 100;
+
+ /*
+ * Get VN addr
+ */
+ if ((rc = rfapiCliGetRfapiIpAddr(vty, argv[4]->arg, &vn)))
+ return rc;
+
+
+ /*
+ * Get UN addr
+ */
+ if ((rc = rfapiCliGetRfapiIpAddr(vty, argv[6]->arg, &un)))
+ return rc;
+
+
+ if (rfapi_find_handle_vty(vty, &vn, &un, &handle)) {
+ vty_out(vty, "can't locate handle matching vn=%s, un=%s\n",
+ argv[4]->arg, argv[6]->arg);
+ return CMD_WARNING_CONFIG_FAILED;
+ }
+
+ /*
+ * Get prefix to advertise
+ */
+ if (!str2prefix(argv[8]->arg, &pfx)) {
+ vty_out(vty, "Malformed prefix \"%s\"\n", argv[8]->arg);
+ return CMD_WARNING_CONFIG_FAILED;
+ }
+ if (pfx.family != AF_INET && pfx.family != AF_INET6) {
+ vty_out(vty, "Bad family for prefix \"%s\"\n", argv[8]->arg);
+ return CMD_WARNING_CONFIG_FAILED;
+ }
+ rfapiQprefix2Rprefix(&pfx, &hpfx);
+
+ if (strmatch(argv[10]->text, "infinite")) {
+ lifetime = RFAPI_INFINITE_LIFETIME;
+ } else {
+ lifetime = strtoul(argv[10]->arg, NULL, 10);
+ }
+
+ if (argc >= 13)
+ cost = (uint8_t) strtoul(argv[12]->arg, NULL, 10);
+ hpfx.cost = cost;
+
+ rc = rfapi_register(handle, &hpfx, lifetime, NULL, NULL,
+ RFAPI_REGISTER_ADD);
+ if (rc) {
+ vty_out(vty, "rfapi_register failed with rc=%d (%s)\n", rc,
+ strerror(rc));
+ }
+
+ return CMD_SUCCESS;
+}
+
+DEFUN (debug_rfapi_register_vn_un_l2o,
+ debug_rfapi_register_vn_un_l2o_cmd,
+ "debug rfapi-dev register vn <A.B.C.D|X:X::X:X> un <A.B.C.D|X:X::X:X> prefix <A.B.C.D/M|X:X::X:X/M> lifetime SECONDS macaddr X:X:X:X:X:X lni (0-16777215)",
+ DEBUG_STR
+ DEBUG_RFAPI_STR
+ "rfapi_register\n"
+ "indicate vn addr follows\n"
+ "virtual network IPv4 interface address\n"
+ "virtual network IPv6 interface address\n"
+ "indicate un addr follows\n"
+ "underlay network IPv4 interface address\n"
+ "underlay network IPv6 interface address\n"
+ "indicate prefix follows\n"
+ "IPv4 prefix\n"
+ "IPv6 prefix\n"
+ "indicate lifetime follows\n"
+ "Seconds of lifetime\n"
+ "indicate MAC address follows\n"
+ "MAC address\n"
+ "indicate lni follows\n"
+ "lni value range\n")
+{
+ struct rfapi_ip_addr vn;
+ struct rfapi_ip_addr un;
+ rfapi_handle handle;
+ struct prefix pfx;
+ uint32_t lifetime;
+ struct rfapi_ip_prefix hpfx;
+ int rc;
+ struct rfapi_vn_option optary[10]; /* XXX must be big enough */
+ struct rfapi_vn_option *opt = NULL;
+ int opt_next = 0;
+
+ /*
+ * Get VN addr
+ */
+ if ((rc = rfapiCliGetRfapiIpAddr(vty, argv[4]->arg, &vn)))
+ return rc;
+
+
+ /*
+ * Get UN addr
+ */
+ if ((rc = rfapiCliGetRfapiIpAddr(vty, argv[6]->arg, &un)))
+ return rc;
+
+
+ if (rfapi_find_handle_vty(vty, &vn, &un, &handle)) {
+ vty_out(vty, "can't locate handle matching vn=%s, un=%s\n",
+ argv[4]->arg, argv[6]->arg);
+ return CMD_WARNING_CONFIG_FAILED;
+ }
+
+ /*
+ * Get prefix to advertise
+ */
+ if (!str2prefix(argv[8]->arg, &pfx)) {
+ vty_out(vty, "Malformed prefix \"%s\"\n", argv[8]->arg);
+ return CMD_WARNING_CONFIG_FAILED;
+ }
+ if (pfx.family != AF_INET && pfx.family != AF_INET6) {
+ vty_out(vty, "Bad family for prefix \"%s\"\n", argv[8]->arg);
+ return CMD_WARNING_CONFIG_FAILED;
+ }
+ rfapiQprefix2Rprefix(&pfx, &hpfx);
+
+ if (strmatch(argv[10]->text, "infinite")) {
+ lifetime = RFAPI_INFINITE_LIFETIME;
+ } else {
+ lifetime = strtoul(argv[10]->arg, NULL, 10);
+ }
+
+ /* L2 option parsing START */
+ memset(optary, 0, sizeof(optary));
+ optary[opt_next].v.l2addr.logical_net_id =
+ strtoul(argv[14]->arg, NULL, 10);
+ if (rfapiStr2EthAddr(argv[12]->arg,
+ &optary[opt_next].v.l2addr.macaddr)) {
+ vty_out(vty, "Bad mac address \"%s\"\n", argv[12]->arg);
+ return CMD_WARNING_CONFIG_FAILED;
+ }
+ optary[opt_next].type = RFAPI_VN_OPTION_TYPE_L2ADDR;
+ opt = optary;
+
+ /* L2 option parsing END */
+
+ /* TBD fixme */
+ rc = rfapi_register(handle, &hpfx, lifetime, NULL /* &uo */, opt,
+ RFAPI_REGISTER_ADD);
+ if (rc) {
+ vty_out(vty, "rfapi_register failed with rc=%d (%s)\n", rc,
+ strerror(rc));
+ }
+
+ return CMD_SUCCESS;
+}
+
+
+DEFUN (debug_rfapi_unregister_vn_un,
+ debug_rfapi_unregister_vn_un_cmd,
+ "debug rfapi-dev unregister vn <A.B.C.D|X:X::X:X> un <A.B.C.D|X:X::X:X> prefix <A.B.C.D/M|X:X::X:X/M> [kill]",
+ DEBUG_STR
+ DEBUG_RFAPI_STR
+ "rfapi_register\n"
+ "indicate vn addr follows\n"
+ "virtual network interface address\n"
+ "indicate xt addr follows\n"
+ "underlay network interface address\n"
+ "prefix to remove\n"
+ "Remove without holddown")
+{
+ struct rfapi_ip_addr vn;
+ struct rfapi_ip_addr un;
+ rfapi_handle handle;
+ struct prefix pfx;
+ struct rfapi_ip_prefix hpfx;
+ int rc;
+
+ /*
+ * Get VN addr
+ */
+ if ((rc = rfapiCliGetRfapiIpAddr(vty, argv[4]->arg, &vn)))
+ return rc;
+
+
+ /*
+ * Get UN addr
+ */
+ if ((rc = rfapiCliGetRfapiIpAddr(vty, argv[6]->arg, &un)))
+ return rc;
+
+
+ if (rfapi_find_handle_vty(vty, &vn, &un, &handle)) {
+ vty_out(vty, "can't locate handle matching vn=%s, un=%s\n",
+ argv[4]->arg, argv[6]->arg);
+ return CMD_WARNING_CONFIG_FAILED;
+ }
+
+ /*
+ * Get prefix to advertise
+ */
+ if (!str2prefix(argv[8]->arg, &pfx)) {
+ vty_out(vty, "Malformed prefix \"%s\"\n", argv[8]->arg);
+ return CMD_WARNING_CONFIG_FAILED;
+ }
+ if (pfx.family != AF_INET && pfx.family != AF_INET6) {
+ vty_out(vty, "Bad family for prefix \"%s\"\n", argv[8]->arg);
+ return CMD_WARNING_CONFIG_FAILED;
+ }
+ rfapiQprefix2Rprefix(&pfx, &hpfx);
+
+ rfapi_register(handle, &hpfx, 0, NULL, NULL,
+ (argc == 10 ?
+ RFAPI_REGISTER_KILL : RFAPI_REGISTER_WITHDRAW));
+
+ return CMD_SUCCESS;
+}
+
+DEFUN (debug_rfapi_query_vn_un,
+ debug_rfapi_query_vn_un_cmd,
+ "debug rfapi-dev query vn <A.B.C.D|X:X::X:X> un <A.B.C.D|X:X::X:X> target <A.B.C.D|X:X::X:X>",
+ DEBUG_STR
+ DEBUG_RFAPI_STR
+ "rfapi_query\n"
+ "indicate vn addr follows\n"
+ "virtual network interface IPv4 address\n"
+ "virtual network interface IPv6 address\n"
+ "indicate un addr follows\n"
+ "IPv4 un address\n"
+ "IPv6 un address\n"
+ "indicate target follows\n"
+ "target IPv4 address\n"
+ "target IPv6 address\n")
+{
+ struct rfapi_ip_addr vn;
+ struct rfapi_ip_addr un;
+ struct rfapi_ip_addr target;
+ rfapi_handle handle;
+ int rc;
+ struct rfapi_next_hop_entry *pNextHopEntry;
+
+ /*
+ * Get VN addr
+ */
+ if ((rc = rfapiCliGetRfapiIpAddr(vty, argv[4]->arg, &vn)))
+ return rc;
+
+
+ /*
+ * Get UN addr
+ */
+ if ((rc = rfapiCliGetRfapiIpAddr(vty, argv[6]->arg, &un)))
+ return rc;
+
+
+ /*
+ * Get target addr
+ */
+ if ((rc = rfapiCliGetRfapiIpAddr(vty, argv[8]->arg, &target)))
+ return rc;
+
+
+ if (rfapi_find_handle_vty(vty, &vn, &un, &handle)) {
+ vty_out(vty, "can't locate handle matching vn=%s, un=%s\n",
+ argv[4]->arg, argv[6]->arg);
+ return CMD_WARNING_CONFIG_FAILED;
+ }
+
+ /*
+ * options parameter not used? Set to NULL for now
+ */
+ rc = rfapi_query(handle, &target, NULL, &pNextHopEntry);
+
+ if (rc) {
+ vty_out(vty, "rfapi_query failed with rc=%d (%s)\n", rc,
+ strerror(rc));
+ } else {
+ /*
+ * print nexthop list
+ */
+ test_nexthops_callback(/*&target, */ pNextHopEntry,
+ vty); /* frees nh list! */
+ }
+
+ return CMD_SUCCESS;
+}
+
+
+DEFUN (debug_rfapi_query_vn_un_l2o,
+ debug_rfapi_query_vn_un_l2o_cmd,
+ "debug rfapi-dev query vn <A.B.C.D|X:X::X:X> un <A.B.C.D|X:X::X:X> lni LNI target X:X:X:X:X:X",
+ DEBUG_STR
+ DEBUG_RFAPI_STR
+ "rfapi_query\n"
+ "indicate vn addr follows\n"
+ "virtual network interface IPv4 address\n"
+ "virtual network interface IPv6 address\n"
+ "indicate xt addr follows\n"
+ "underlay network interface IPv4 address\n"
+ "underlay network interface IPv6 address\n"
+ "logical network ID follows\n"
+ "logical network ID\n"
+ "indicate target MAC addr follows\n"
+ "target MAC addr\n")
+{
+ struct rfapi_ip_addr vn;
+ struct rfapi_ip_addr un;
+ int rc;
+
+ /*
+ * Get VN addr
+ */
+ if ((rc = rfapiCliGetRfapiIpAddr(vty, argv[4]->arg, &vn)))
+ return rc;
+
+
+ /*
+ * Get UN addr
+ */
+ if ((rc = rfapiCliGetRfapiIpAddr(vty, argv[6]->arg, &un)))
+ return rc;
+
+ vty_out(vty, "%% This command is broken.\n");
+ return CMD_WARNING_CONFIG_FAILED;
+}
+
+
+DEFUN (debug_rfapi_query_done_vn_un,
+ debug_rfapi_query_vn_un_done_cmd,
+ "debug rfapi-dev query done vn <A.B.C.D|X:X::X:X> un <A.B.C.D|X:X::X:X> target <A.B.C.D|X:X::X:X>",
+ DEBUG_STR
+ DEBUG_RFAPI_STR
+ "rfapi_query_done\n"
+ "rfapi_query_done\n"
+ "indicate vn addr follows\n"
+ "virtual network interface IPv4 address\n"
+ "virtual network interface IPv6 address\n"
+ "indicate xt addr follows\n"
+ "underlay network interface IPv4 address\n"
+ "underlay network interface IPv6 address\n"
+ "indicate target follows\n"
+ "Target IPv4 address\n"
+ "Target IPv6 address\n")
+{
+ struct rfapi_ip_addr vn;
+ struct rfapi_ip_addr un;
+ struct rfapi_ip_addr target;
+ rfapi_handle handle;
+ int rc;
+
+ /*
+ * Get VN addr
+ */
+ if ((rc = rfapiCliGetRfapiIpAddr(vty, argv[5]->arg, &vn)))
+ return rc;
+
+
+ /*
+ * Get UN addr
+ */
+ if ((rc = rfapiCliGetRfapiIpAddr(vty, argv[7]->arg, &un)))
+ return rc;
+
+
+ /*
+ * Get target addr
+ */
+ if ((rc = rfapiCliGetRfapiIpAddr(vty, argv[9]->arg, &target)))
+ return rc;
+
+
+ if (rfapi_find_handle_vty(vty, &vn, &un, &handle)) {
+ vty_out(vty, "can't locate handle matching vn=%s, un=%s\n",
+ argv[5]->arg, argv[7]->arg);
+ return CMD_WARNING_CONFIG_FAILED;
+ }
+
+ /*
+ * options parameter not used? Set to NULL for now
+ */
+ rc = rfapi_query_done(handle, &target);
+
+ vty_out(vty, "rfapi_query_done returned %d\n", rc);
+
+ return CMD_SUCCESS;
+}
+
+DEFUN (debug_rfapi_show_import,
+ debug_rfapi_show_import_cmd,
+ "debug rfapi-dev show import",
+ DEBUG_STR
+ DEBUG_RFAPI_STR
+ SHOW_STR
+ "import\n")
+{
+ struct bgp *bgp;
+ struct rfapi *h;
+ struct rfapi_import_table *it;
+ char *s;
+ int first_l2 = 1;
+
+ /*
+ * Show all import tables
+ */
+
+ bgp = bgp_get_default(); /* assume 1 instance for now */
+ if (!bgp) {
+ vty_out(vty, "No BGP instance\n");
+ return CMD_WARNING_CONFIG_FAILED;
+ }
+
+ h = bgp->rfapi;
+ if (!h) {
+ vty_out(vty, "No RFAPI instance\n");
+ return CMD_WARNING_CONFIG_FAILED;
+ }
+
+ /*
+ * Iterate over all import tables; do a filtered import
+ * for the afi/safi combination
+ */
+
+
+ for (it = h->imports; it; it = it->next) {
+ s = ecommunity_ecom2str(it->rt_import_list,
+ ECOMMUNITY_FORMAT_ROUTE_MAP, 0);
+ vty_out(vty, "Import Table %p, RTs: %s\n", it, s);
+ XFREE(MTYPE_ECOMMUNITY_STR, s);
+
+ rfapiShowImportTable(vty, "IP VPN", it->imported_vpn[AFI_IP],
+ 1);
+ rfapiShowImportTable(vty, "IP ENCAP",
+ it->imported_encap[AFI_IP], 0);
+ rfapiShowImportTable(vty, "IP6 VPN", it->imported_vpn[AFI_IP6],
+ 1);
+ rfapiShowImportTable(vty, "IP6 ENCAP",
+ it->imported_encap[AFI_IP6], 0);
+ }
+
+ if (h->import_mac) {
+ void *cursor = NULL;
+ uint32_t lni;
+ uintptr_t lni_as_ptr;
+ int rc;
+ char buf[BUFSIZ];
+
+ for (rc = skiplist_next(h->import_mac, (void **)&lni_as_ptr,
+ (void **)&it, &cursor);
+ !rc;
+ rc = skiplist_next(h->import_mac, (void **)&lni_as_ptr,
+ (void **)&it, &cursor)) {
+
+ if (it->imported_vpn[AFI_L2VPN]) {
+ lni = lni_as_ptr;
+ if (first_l2) {
+ vty_out(vty,
+ "\nLNI-based Ethernet Tables:\n");
+ first_l2 = 0;
+ }
+ snprintf(buf, sizeof(buf), "L2VPN LNI=%u", lni);
+ rfapiShowImportTable(
+ vty, buf, it->imported_vpn[AFI_L2VPN],
+ 1);
+ }
+ }
+ }
+
+ rfapiShowImportTable(vty, "CE IT - IP VPN",
+ h->it_ce->imported_vpn[AFI_IP], 1);
+
+ return CMD_SUCCESS;
+}
+
+DEFUN (debug_rfapi_show_import_vn_un,
+ debug_rfapi_show_import_vn_un_cmd,
+ "debug rfapi-dev show import vn <A.B.C.D|X:X::X:X> un <A.B.C.D|X:X::X:X>",
+ DEBUG_STR
+ DEBUG_RFAPI_STR
+ SHOW_STR
+ "import\n"
+ "indicate vn addr follows\n"
+ "virtual network interface IPv4 address\n"
+ "virtual network interface IPv6 address\n"
+ "indicate xt addr follows\n"
+ "underlay network interface IPv4 address\n"
+ "underlay network interface IPv6 address\n")
+{
+ struct rfapi_ip_addr vn;
+ struct rfapi_ip_addr un;
+ rfapi_handle handle;
+ int rc;
+ struct rfapi_descriptor *rfd;
+
+ /*
+ * Get VN addr
+ */
+ if ((rc = rfapiCliGetRfapiIpAddr(vty, argv[5]->arg, &vn)))
+ return rc;
+
+
+ /*
+ * Get UN addr
+ */
+ if ((rc = rfapiCliGetRfapiIpAddr(vty, argv[7]->arg, &un)))
+ return rc;
+
+
+ if (rfapi_find_handle_vty(vty, &vn, &un, &handle)) {
+ vty_out(vty, "can't locate handle matching vn=%s, un=%s\n",
+ argv[5]->arg, argv[7]->arg);
+ return CMD_WARNING_CONFIG_FAILED;
+ }
+
+ rfd = (struct rfapi_descriptor *)handle;
+
+ rfapiShowImportTable(vty, "IP VPN",
+ rfd->import_table->imported_vpn[AFI_IP], 1);
+ rfapiShowImportTable(vty, "IP ENCAP",
+ rfd->import_table->imported_encap[AFI_IP], 0);
+ rfapiShowImportTable(vty, "IP6 VPN",
+ rfd->import_table->imported_vpn[AFI_IP6], 1);
+ rfapiShowImportTable(vty, "IP6 ENCAP",
+ rfd->import_table->imported_encap[AFI_IP6], 0);
+
+ return CMD_SUCCESS;
+}
+
+DEFUN (debug_rfapi_response_omit_self,
+ debug_rfapi_response_omit_self_cmd,
+ "debug rfapi-dev response-omit-self <on|off>",
+ DEBUG_STR
+ DEBUG_RFAPI_STR
+ "Omit self in RFP responses\n"
+ "filter out self from responses\n" "leave self in responses\n")
+{
+ struct bgp *bgp = bgp_get_default();
+
+ if (!bgp) {
+ vty_out(vty, "No BGP process is configured\n");
+ return CMD_WARNING_CONFIG_FAILED;
+ }
+ if (!bgp->rfapi_cfg) {
+ vty_out(vty, "VNC not configured\n");
+ return CMD_WARNING_CONFIG_FAILED;
+ }
+
+ if (strmatch(argv[3]->text, "on"))
+ SET_FLAG(bgp->rfapi_cfg->flags,
+ BGP_VNC_CONFIG_FILTER_SELF_FROM_RSP);
+ else
+ UNSET_FLAG(bgp->rfapi_cfg->flags,
+ BGP_VNC_CONFIG_FILTER_SELF_FROM_RSP);
+
+ return CMD_SUCCESS;
+}
+
+
+#ifdef RFAPI_DEBUG_SKIPLIST_CLI
+
+#include "lib/skiplist.h"
+DEFUN (skiplist_test_cli,
+ skiplist_test_cli_cmd,
+ "skiplist test",
+ "skiplist command\n"
+ "test\n")
+{
+ skiplist_test(vty);
+
+ return CMD_SUCCESS;
+}
+
+DEFUN (skiplist_debug_cli,
+ skiplist_debug_cli_cmd,
+ "skiplist debug",
+ "skiplist command\n"
+ "debug\n")
+{
+ skiplist_debug(vty, NULL);
+ return CMD_SUCCESS;
+}
+
+#endif /* RFAPI_DEBUG_SKIPLIST_CLI */
+
+void rfapi_init(void)
+{
+ bgp_rfapi_cfg_init();
+ vnc_debug_init();
+
+ install_element(ENABLE_NODE, &debug_rfapi_show_import_cmd);
+ install_element(ENABLE_NODE, &debug_rfapi_show_import_vn_un_cmd);
+
+ install_element(ENABLE_NODE, &debug_rfapi_open_cmd);
+ install_element(ENABLE_NODE, &debug_rfapi_close_vn_un_cmd);
+ install_element(ENABLE_NODE, &debug_rfapi_close_rfd_cmd);
+ install_element(ENABLE_NODE, &debug_rfapi_register_vn_un_cmd);
+ install_element(ENABLE_NODE, &debug_rfapi_unregister_vn_un_cmd);
+ install_element(ENABLE_NODE, &debug_rfapi_query_vn_un_cmd);
+ install_element(ENABLE_NODE, &debug_rfapi_query_vn_un_done_cmd);
+ install_element(ENABLE_NODE, &debug_rfapi_query_vn_un_l2o_cmd);
+
+ install_element(ENABLE_NODE, &debug_rfapi_response_omit_self_cmd);
+
+ /* Need the following show commands for gpz test scripts */
+ install_element(ENABLE_NODE, &debug_rfapi_show_nves_cmd);
+ install_element(ENABLE_NODE, &debug_rfapi_show_nves_vn_un_cmd);
+ install_element(ENABLE_NODE, &debug_rfapi_register_vn_un_l2o_cmd);
+
+#ifdef RFAPI_DEBUG_SKIPLIST_CLI
+ install_element(ENABLE_NODE, &skiplist_test_cli_cmd);
+ install_element(ENABLE_NODE, &skiplist_debug_cli_cmd);
+#endif
+
+ rfapi_vty_init();
+}
+
+#ifdef DEBUG_RFAPI
+static void rfapi_print_exported(struct bgp *bgp)
+{
+ struct bgp_dest *destn;
+ struct bgp_dest *dest;
+ struct bgp_path_info *bpi;
+
+ if (!bgp)
+ return;
+
+ for (destn = bgp_table_top(bgp->rib[AFI_IP][SAFI_MPLS_VPN]); destn;
+ destn = bgp_route_next(destn)) {
+ struct bgp_table *table;
+
+ table = bgp_dest_get_bgp_table_info(destn);
+ if (!table)
+ continue;
+ fprintf(stderr, "%s: vpn destn=%p\n", __func__, destn);
+ for (dest = bgp_table_top(table); dest;
+ dest = bgp_route_next(dest)) {
+ bpi = bgp_dest_get_bgp_path_info(dest);
+
+ if (!bpi)
+ continue;
+ fprintf(stderr, "%s: dest=%p\n", __func__, dest);
+ for (; bpi; bpi = bpi->next) {
+ rfapiPrintBi((void *)2, bpi); /* 2 => stderr */
+ }
+ }
+ }
+ for (destn = bgp_table_top(bgp->rib[AFI_IP][SAFI_ENCAP]); destn;
+ destn = bgp_route_next(destn)) {
+ struct bgp_table *table;
+
+ table = bgp_dest_get_bgp_table_info(destn);
+ if (!table)
+ continue;
+ fprintf(stderr, "%s: encap destn=%p\n", __func__, destn);
+ for (dest = bgp_table_top(table); dest;
+ dest = bgp_route_next(dest)) {
+ bpi = bgp_dest_get_bgp_path_info(dest);
+ if (!bpi)
+ continue;
+ fprintf(stderr, "%s: dest=%p\n", __func__, dest);
+ for (; bpi; bpi = bpi->next) {
+ rfapiPrintBi((void *)2, bpi); /* 2 => stderr */
+ }
+ }
+ }
+}
+#endif /* defined(DEBUG_RFAPI) */
+
+/*
+ * Free all memory to prepare for clean exit as seen by valgrind memcheck
+ */
+void rfapi_delete(struct bgp *bgp)
+{
+ extern void rfp_clear_vnc_nve_all(void); /* can't fix correctly yet */
+
+ /*
+ * This clears queries and registered routes, and closes nves
+ */
+ if (bgp->rfapi)
+ rfp_clear_vnc_nve_all();
+ bgp_rfapi_cfg_destroy(bgp, bgp->rfapi_cfg);
+ bgp->rfapi_cfg = NULL;
+ bgp_rfapi_destroy(bgp, bgp->rfapi);
+ bgp->rfapi = NULL;
+#ifdef DEBUG_RFAPI
+ /*
+ * show what's left in the BGP MPLSVPN RIB
+ */
+ rfapi_print_exported(bgp);
+#endif
+}
+
+int rfapi_set_autord_from_vn(struct prefix_rd *rd, struct rfapi_ip_addr *vn)
+{
+ vnc_zlog_debug_verbose("%s: auto-assigning RD", __func__);
+ if (vn->addr_family != AF_INET && vn->addr_family != AF_INET6) {
+ vnc_zlog_debug_verbose(
+ "%s: can't auto-assign RD, VN addr family is not IPv4|v6",
+ __func__);
+ return EAFNOSUPPORT;
+ }
+ rd->family = AF_UNSPEC;
+ rd->prefixlen = 64;
+ rd->val[1] = RD_TYPE_IP;
+ if (vn->addr_family == AF_INET) {
+ memcpy(rd->val + 2, &vn->addr.v4.s_addr, 4);
+ } else { /* is v6 */
+ memcpy(rd->val + 2, &vn->addr.v6.s6_addr32[3],
+ 4); /* low order 4 bytes */
+ }
+ vnc_zlog_debug_verbose("%s: auto-RD is set to %pRD", __func__, rd);
+ return 0;
+}
+
+/*------------------------------------------
+ * rfapi_bgp_lookup_by_rfp
+ *
+ * Find bgp instance pointer based on value returned by rfp_start
+ *
+ * input:
+ * rfp_start_val value returned by rfp_startor
+ * NULL (=get default instance)
+ *
+ * output:
+ * none
+ *
+ * return value:
+ * bgp bgp instance pointer
+ * NULL = not found
+ *
+ --------------------------------------------*/
+struct bgp *rfapi_bgp_lookup_by_rfp(void *rfp_start_val)
+{
+ struct bgp *bgp = NULL;
+ struct listnode *node, *nnode;
+
+ if (rfp_start_val == NULL)
+ bgp = bgp_get_default();
+ else
+ for (ALL_LIST_ELEMENTS(bm->bgp, node, nnode, bgp))
+ if (bgp->rfapi != NULL
+ && bgp->rfapi->rfp == rfp_start_val)
+ return bgp;
+ return bgp;
+}
+
+/*------------------------------------------
+ * rfapi_get_rfp_start_val_by_bgp
+ *
+ * Find bgp instance pointer based on value returned by rfp_start
+ *
+ * input:
+ * bgp bgp instance pointer
+ *
+ * output:
+ * none
+ *
+ * return value:
+ * rfp_start_val
+ * NULL = not found
+ *
+ --------------------------------------------*/
+void *rfapi_get_rfp_start_val_by_bgp(struct bgp *bgp)
+{
+ if (!bgp || !bgp->rfapi)
+ return NULL;
+ return bgp->rfapi->rfp;
+}
+
+/***********************************************************************
+ * RFP group specific configuration
+ ***********************************************************************/
+static void *rfapi_rfp_get_or_init_group_config_default(struct rfapi_cfg *rfc,
+ struct vty *vty,
+ uint32_t size)
+{
+ if (rfc->default_rfp_cfg == NULL && size > 0) {
+ rfc->default_rfp_cfg = XCALLOC(MTYPE_RFAPI_RFP_GROUP_CFG, size);
+ vnc_zlog_debug_verbose("%s: allocated, size=%d", __func__,
+ size);
+ }
+ return rfc->default_rfp_cfg;
+}
+
+static void *rfapi_rfp_get_or_init_group_config_nve(struct rfapi_cfg *rfc,
+ struct vty *vty,
+ uint32_t size)
+{
+ struct rfapi_nve_group_cfg *rfg =
+ VTY_GET_CONTEXT_SUB(rfapi_nve_group_cfg);
+
+ /* make sure group is still in list */
+ if (!rfg || !listnode_lookup(rfc->nve_groups_sequential, rfg)) {
+ /* Not in list anymore */
+ vty_out(vty, "Current NVE group no longer exists\n");
+ return NULL;
+ }
+
+ if (rfg->rfp_cfg == NULL && size > 0) {
+ rfg->rfp_cfg = XCALLOC(MTYPE_RFAPI_RFP_GROUP_CFG, size);
+ vnc_zlog_debug_verbose("%s: allocated, size=%d", __func__,
+ size);
+ }
+ return rfg->rfp_cfg;
+}
+
+static void *rfapi_rfp_get_or_init_group_config_l2(struct rfapi_cfg *rfc,
+ struct vty *vty,
+ uint32_t size)
+{
+ struct rfapi_l2_group_cfg *rfg =
+ VTY_GET_CONTEXT_SUB(rfapi_l2_group_cfg);
+
+ /* make sure group is still in list */
+ if (!rfg || !listnode_lookup(rfc->l2_groups, rfg)) {
+ /* Not in list anymore */
+ vty_out(vty, "Current L2 group no longer exists\n");
+ return NULL;
+ }
+ if (rfg->rfp_cfg == NULL && size > 0) {
+ rfg->rfp_cfg = XCALLOC(MTYPE_RFAPI_RFP_GROUP_CFG, size);
+ vnc_zlog_debug_verbose("%s: allocated, size=%d", __func__,
+ size);
+ }
+ return rfg->rfp_cfg;
+}
+
+/*------------------------------------------
+ * rfapi_rfp_init_group_config_ptr_vty
+ *
+ * This is used to init or return a previously init'ed group specific
+ * configuration pointer. Group is identified by vty context.
+ * NOTE: size is ignored when a previously init'ed value is returned.
+ * RFAPI frees rfp_cfg_group when group is deleted during reconfig,
+ * bgp restart or shutdown.
+ *
+ * input:
+ * rfp_start_val value returned by rfp_start
+ * type group type
+ * vty quagga vty context
+ * size number of bytes to allocation
+ *
+ * output:
+ * none
+ *
+ * return value:
+ * rfp_cfg_group NULL or Pointer to configuration structure
+--------------------------------------------*/
+void *rfapi_rfp_init_group_config_ptr_vty(void *rfp_start_val,
+ rfapi_rfp_cfg_group_type type,
+ struct vty *vty, uint32_t size)
+{
+ struct bgp *bgp;
+ void *ret = NULL;
+
+ if (rfp_start_val == NULL || vty == NULL)
+ return NULL;
+
+ bgp = rfapi_bgp_lookup_by_rfp(rfp_start_val);
+ if (!bgp || !bgp->rfapi_cfg)
+ return NULL;
+
+ switch (type) {
+ case RFAPI_RFP_CFG_GROUP_DEFAULT:
+ ret = rfapi_rfp_get_or_init_group_config_default(bgp->rfapi_cfg,
+ vty, size);
+ break;
+ case RFAPI_RFP_CFG_GROUP_NVE:
+ ret = rfapi_rfp_get_or_init_group_config_nve(bgp->rfapi_cfg,
+ vty, size);
+ break;
+ case RFAPI_RFP_CFG_GROUP_L2:
+ ret = rfapi_rfp_get_or_init_group_config_l2(bgp->rfapi_cfg, vty,
+ size);
+ break;
+ default:
+ flog_err(EC_LIB_DEVELOPMENT, "%s: Unknown group type=%d",
+ __func__, type);
+ /* should never happen */
+ assert("Unknown type" == NULL);
+ break;
+ }
+ return ret;
+}
+
+/*------------------------------------------
+ * rfapi_rfp_get_group_config_ptr_vty
+ *
+ * This is used to get group specific configuration pointer.
+ * Group is identified by type and vty context.
+ * RFAPI frees rfp_cfg_group when group is deleted during reconfig,
+ * bgp restart or shutdown.
+ *
+ * input:
+ * rfp_start_val value returned by rfp_start
+ * type group type
+ * vty quagga vty context
+ *
+ * output:
+ * none
+ *
+ * return value:
+ * rfp_cfg_group Pointer to configuration structure
+--------------------------------------------*/
+void *rfapi_rfp_get_group_config_ptr_vty(void *rfp_start_val,
+ rfapi_rfp_cfg_group_type type,
+ struct vty *vty)
+{
+ return rfapi_rfp_init_group_config_ptr_vty(rfp_start_val, type, vty, 0);
+}
+
+static void *
+rfapi_rfp_get_group_config_name_nve(struct rfapi_cfg *rfc, const char *name,
+ void *criteria,
+ rfp_group_config_search_cb_t *search_cb)
+{
+ struct rfapi_nve_group_cfg *rfg;
+ struct listnode *node;
+
+ for (ALL_LIST_ELEMENTS_RO(rfc->nve_groups_sequential, node, rfg)) {
+ if (!strcmp(rfg->name, name) && /* name match */
+ (search_cb == NULL || !search_cb(criteria, rfg->rfp_cfg)))
+ return rfg->rfp_cfg;
+ }
+ return NULL;
+}
+
+static void *
+rfapi_rfp_get_group_config_name_l2(struct rfapi_cfg *rfc, const char *name,
+ void *criteria,
+ rfp_group_config_search_cb_t *search_cb)
+{
+ struct rfapi_l2_group_cfg *rfg;
+ struct listnode *node;
+
+ for (ALL_LIST_ELEMENTS_RO(rfc->l2_groups, node, rfg)) {
+ if (!strcmp(rfg->name, name) && /* name match */
+ (search_cb == NULL || !search_cb(criteria, rfg->rfp_cfg)))
+ return rfg->rfp_cfg;
+ }
+ return NULL;
+}
+
+/*------------------------------------------
+ * rfapi_rfp_get_group_config_ptr_name
+ *
+ * This is used to get group specific configuration pointer.
+ * Group is identified by type and name context.
+ * RFAPI frees rfp_cfg_group when group is deleted during reconfig,
+ * bgp restart or shutdown.
+ *
+ * input:
+ * rfp_start_val value returned by rfp_start
+ * type group type
+ * name group name
+ * criteria RFAPI caller provided search criteria
+ * search_cb optional rfp_group_config_search_cb_t
+ *
+ * output:
+ * none
+ *
+ * return value:
+ * rfp_cfg_group Pointer to configuration structure
+--------------------------------------------*/
+void *rfapi_rfp_get_group_config_ptr_name(
+ void *rfp_start_val, rfapi_rfp_cfg_group_type type, const char *name,
+ void *criteria, rfp_group_config_search_cb_t *search_cb)
+{
+ struct bgp *bgp;
+ void *ret = NULL;
+
+ if (rfp_start_val == NULL || name == NULL)
+ return NULL;
+
+ bgp = rfapi_bgp_lookup_by_rfp(rfp_start_val);
+ if (!bgp || !bgp->rfapi_cfg)
+ return NULL;
+
+ switch (type) {
+ case RFAPI_RFP_CFG_GROUP_DEFAULT:
+ ret = bgp->rfapi_cfg->default_rfp_cfg;
+ break;
+ case RFAPI_RFP_CFG_GROUP_NVE:
+ ret = rfapi_rfp_get_group_config_name_nve(bgp->rfapi_cfg, name,
+ criteria, search_cb);
+ break;
+ case RFAPI_RFP_CFG_GROUP_L2:
+ ret = rfapi_rfp_get_group_config_name_l2(bgp->rfapi_cfg, name,
+ criteria, search_cb);
+ break;
+ default:
+ flog_err(EC_LIB_DEVELOPMENT, "%s: Unknown group type=%d",
+ __func__, type);
+ /* should never happen */
+ assert("Unknown type" == NULL);
+ break;
+ }
+ return ret;
+}
+
+/*------------------------------------------
+ * rfapi_rfp_get_l2_group_config_ptr_lni
+ *
+ * This is used to get group specific configuration pointer.
+ * Group is identified by type and logical network identifier.
+ * RFAPI frees rfp_cfg_group when group is deleted during reconfig,
+ * bgp restart or shutdown.
+ *
+ * input:
+ * rfp_start_val value returned by rfp_start
+ * type group type
+ * logical_net_id group logical network identifier
+ * criteria RFAPI caller provided search criteria
+ * search_cb optional rfp_group_config_search_cb_t
+ *
+ * output:
+ * none
+ *
+ * return value:
+ * rfp_cfg_group Pointer to configuration structure
+--------------------------------------------*/
+void *
+rfapi_rfp_get_l2_group_config_ptr_lni(void *rfp_start_val,
+ uint32_t logical_net_id, void *criteria,
+ rfp_group_config_search_cb_t *search_cb)
+{
+ struct bgp *bgp;
+ struct rfapi_l2_group_cfg *rfg;
+ struct listnode *node;
+
+ if (rfp_start_val == NULL)
+ return NULL;
+
+ bgp = rfapi_bgp_lookup_by_rfp(rfp_start_val);
+ if (!bgp || !bgp->rfapi_cfg)
+ return NULL;
+
+ for (ALL_LIST_ELEMENTS_RO(bgp->rfapi_cfg->l2_groups, node, rfg)) {
+ if (rfg->logical_net_id == logical_net_id
+ && (search_cb == NULL
+ || !search_cb(criteria, rfg->rfp_cfg))) {
+ if (rfg->rfp_cfg == NULL)
+ vnc_zlog_debug_verbose(
+ "%s: returning rfp group config for lni=0",
+ __func__);
+ return rfg->rfp_cfg;
+ }
+ }
+ return NULL;
+}
diff --git a/bgpd/rfapi/rfapi.h b/bgpd/rfapi/rfapi.h
new file mode 100644
index 0000000..b2079fb
--- /dev/null
+++ b/bgpd/rfapi/rfapi.h
@@ -0,0 +1,914 @@
+/*
+ *
+ * Copyright 2009-2016, LabN Consulting, L.L.C.
+ *
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program; see the file COPYING; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
+ */
+
+#ifndef _QUAGGA_BGP_RFAPI_H
+#define _QUAGGA_BGP_RFAPI_H
+
+#ifdef ENABLE_BGP_VNC
+
+#include <stdint.h>
+#include <netinet/in.h>
+#include "lib/zebra.h"
+#include "lib/vty.h"
+#include "lib/prefix.h"
+#include "bgpd/bgpd.h"
+#include "bgpd/bgp_encap_types.h"
+
+/* probably ought to have a field-specific define in config.h */
+#ifndef s6_addr32 /* for solaris/bsd */
+# define s6_addr32 __u6_addr.__u6_addr32
+#endif
+
+#define RFAPI_V4_ADDR 0x04
+#define RFAPI_V6_ADDR 0x06
+#define RFAPI_SHOW_STR "VNC information\n"
+
+struct rfapi_ip_addr {
+ uint8_t addr_family; /* AF_INET | AF_INET6 */
+ union {
+ struct in_addr v4; /* in network order */
+ struct in6_addr v6; /* in network order */
+ } addr;
+};
+
+struct rfapi_ip_prefix {
+ uint8_t length;
+ uint8_t cost; /* bgp local pref = 255 - cost */
+ struct rfapi_ip_addr prefix;
+};
+
+struct rfapi_nexthop {
+ struct prefix addr;
+ uint8_t cost;
+};
+
+struct rfapi_next_hop_entry {
+ struct rfapi_next_hop_entry *next;
+ struct rfapi_ip_prefix prefix;
+ uint32_t lifetime;
+ struct rfapi_ip_addr un_address;
+ struct rfapi_ip_addr vn_address;
+ struct rfapi_vn_option *vn_options;
+ struct rfapi_un_option *un_options;
+};
+
+#define RFAPI_REMOVE_RESPONSE_LIFETIME 0
+#define RFAPI_INFINITE_LIFETIME 0xFFFFFFFF
+
+struct rfapi_l2address_option {
+ struct ethaddr macaddr; /* use 0 to assign label to IP prefix */
+ uint32_t label; /* 20bit label in low bits, no TC, S, or TTL */
+ uint32_t logical_net_id; /* ~= EVPN Ethernet Segment Id,
+ must not be zero for mac regis. */
+ uint8_t local_nve_id;
+ uint16_t tag_id; /* EVPN Ethernet Tag ID, 0 = none */
+};
+
+typedef enum {
+ RFAPI_UN_OPTION_TYPE_PROVISIONAL, /* internal use only */
+ RFAPI_UN_OPTION_TYPE_TUNNELTYPE,
+} rfapi_un_option_type;
+
+struct rfapi_tunneltype_option {
+ bgp_encap_types type;
+ union {
+ struct bgp_encap_type_reserved reserved;
+ struct bgp_encap_type_l2tpv3_over_ip l2tpv3_ip;
+ struct bgp_encap_type_gre gre;
+ struct bgp_encap_type_transmit_tunnel_endpoint
+ transmit_tunnel_endpoint;
+ struct bgp_encap_type_ipsec_in_tunnel_mode ipsec_tunnel;
+ struct bgp_encap_type_ip_in_ip_tunnel_with_ipsec_transport_mode
+ ip_ipsec;
+ struct bgp_encap_type_mpls_in_ip_tunnel_with_ipsec_transport_mode
+ mpls_ipsec;
+ struct bgp_encap_type_ip_in_ip ip_ip;
+ struct bgp_encap_type_vxlan vxlan;
+ struct bgp_encap_type_nvgre nvgre;
+ struct bgp_encap_type_mpls mpls;
+ struct bgp_encap_type_mpls_in_gre mpls_gre;
+ struct bgp_encap_type_vxlan_gpe vxlan_gpe;
+ struct bgp_encap_type_mpls_in_udp mpls_udp;
+ struct bgp_encap_type_pbb pbb;
+ } bgpinfo;
+};
+
+struct rfapi_un_option {
+ struct rfapi_un_option *next;
+ rfapi_un_option_type type;
+ union {
+ struct rfapi_tunneltype_option tunnel;
+ } v;
+};
+
+typedef enum {
+ RFAPI_VN_OPTION_TYPE_L2ADDR =
+ 3, /* Layer 2 address, 3 for legacy compatibility */
+ RFAPI_VN_OPTION_TYPE_LOCAL_NEXTHOP, /* for static routes */
+ RFAPI_VN_OPTION_TYPE_INTERNAL_RD, /* internal use only */
+} rfapi_vn_option_type;
+
+struct rfapi_vn_option {
+ struct rfapi_vn_option *next;
+
+ rfapi_vn_option_type type;
+
+ union {
+ struct rfapi_l2address_option l2addr;
+
+ /*
+ * If this option is present, the next hop is local to the
+ * client NVE (i.e., not via a tunnel).
+ */
+ struct rfapi_nexthop local_nexthop;
+
+ /*
+ * For rfapi internal use only
+ */
+ struct prefix_rd internal_rd;
+ } v;
+};
+
+struct rfapi_l2address_option_match {
+ struct rfapi_l2address_option o;
+ uint32_t flags;
+
+#define RFAPI_L2O_MACADDR 0x00000001
+#define RFAPI_L2O_LABEL 0x00000002
+#define RFAPI_L2O_LNI 0x00000004
+#define RFAPI_L2O_LHI 0x00000008
+};
+
+#define VNC_CONFIG_STR "VNC/RFP related configuration\n"
+
+typedef void *rfapi_handle;
+
+/***********************************************************************
+ * RFP Callbacks
+ ***********************************************************************/
+/*------------------------------------------
+ * rfapi_response_cb_t (callback typedef)
+ *
+ * Callbacks of this type are used to provide asynchronous
+ * route updates from RFAPI to the RFP client.
+ *
+ * response_cb
+ * called to notify the rfp client that a next hop list
+ * that has previously been provided in response to an
+ * rfapi_query call has been updated. Deleted routes are indicated
+ * with lifetime==RFAPI_REMOVE_RESPONSE_LIFETIME.
+ *
+ * By default, the routes an NVE receives via this callback include
+ * its own routes (that it has registered). However, these may be
+ * filtered out if the global BGP_VNC_CONFIG_FILTER_SELF_FROM_RSP
+ * flag is set.
+ *
+ * local_cb
+ * called to notify the rfp client that a local route
+ * has been added or deleted. Deleted routes are indicated
+ * with lifetime==RFAPI_REMOVE_RESPONSE_LIFETIME.
+ *
+ * input:
+ * next_hops a list of possible next hops.
+ * This is a linked list allocated within the
+ * rfapi. The response_cb callback function is responsible
+ * for freeing this memory via rfapi_free_next_hop_list()
+ * in order to avoid memory leaks.
+ *
+ * userdata value (cookie) originally specified in call to
+ * rfapi_open()
+ *
+ *------------------------------------------*/
+typedef void(rfapi_response_cb_t)(struct rfapi_next_hop_entry *next_hops,
+ void *userdata);
+
+/*------------------------------------------
+ * rfapi_nve_close_cb_t (callback typedef)
+ *
+ * Callbacks of this type are used to provide asynchronous
+ * notification that an rfapi_handle was invalidated
+ *
+ * input:
+ * pHandle Firmerly valid rfapi_handle returned to
+ * client via rfapi_open().
+ *
+ * reason EIDRM handle administratively closed (clear nve ...)
+ * ESTALE handle invalidated by configuration change
+ *
+ *------------------------------------------*/
+typedef void(rfapi_nve_close_cb_t)(rfapi_handle pHandle, int reason);
+
+/*------------------------------------------
+ * rfp_cfg_write_cb_t (callback typedef)
+ *
+ * This callback is used to generate output for any config parameters
+ * that may supported by RFP via RFP defined vty commands at the bgp
+ * level. See loglevel as an example.
+ *
+ * input:
+ * vty -- quagga vty context
+ * rfp_start_val -- value returned by rfp_start
+ *
+ * output:
+ * to vty, rfp related configuration
+ *
+ * return value:
+ * lines written
+--------------------------------------------*/
+typedef int(rfp_cfg_write_cb_t)(struct vty *vty, void *rfp_start_val);
+
+/*------------------------------------------
+ * rfp_cfg_group_write_cb_t (callback typedef)
+ *
+ * This callback is used to generate output for any config parameters
+ * that may supported by RFP via RFP defined vty commands at the
+ * L2 or NVE level. See loglevel as an example.
+ *
+ * input:
+ * vty quagga vty context
+ * rfp_start_val value returned by rfp_start
+ * type group type
+ * name group name
+ * rfp_cfg_group Pointer to configuration structure
+ *
+ * output:
+ * to vty, rfp related configuration
+ *
+ * return value:
+ * lines written
+--------------------------------------------*/
+typedef enum {
+ RFAPI_RFP_CFG_GROUP_DEFAULT,
+ RFAPI_RFP_CFG_GROUP_NVE,
+ RFAPI_RFP_CFG_GROUP_L2
+} rfapi_rfp_cfg_group_type;
+
+typedef int(rfp_cfg_group_write_cb_t)(struct vty *vty, void *rfp_start_val,
+ rfapi_rfp_cfg_group_type type,
+ const char *name, void *rfp_cfg_group);
+
+/***********************************************************************
+ * Configuration related defines and structures
+ ***********************************************************************/
+
+struct rfapi_rfp_cb_methods {
+ rfp_cfg_write_cb_t *cfg_cb; /* show top level config */
+ rfp_cfg_group_write_cb_t *cfg_group_cb; /* show group level config */
+ rfapi_response_cb_t *response_cb; /* unsolicited responses */
+ rfapi_response_cb_t *local_cb; /* local route add/delete */
+ rfapi_nve_close_cb_t *close_cb; /* handle closed */
+};
+
+/*
+ * If a route with infinite lifetime is withdrawn, this is
+ * how long (in seconds) to wait before expiring it (because
+ * RFAPI_LIFETIME_MULTIPLIER_PCT * infinity is too long to wait)
+ */
+#define RFAPI_LIFETIME_INFINITE_WITHDRAW_DELAY (60*120)
+
+/*
+ * the factor that should be applied to a prefix's <lifetime> value
+ * before using it to expire a withdrawn prefix, expressed as a percent.
+ * Thus, a value of 100 means to use the exact value of <lifetime>,
+ * a value of 200 means to use twice the value of <lifetime>, etc.
+ */
+#define RFAPI_RFP_CFG_DEFAULT_HOLDDOWN_FACTOR 150
+
+/*
+ * This is used by rfapi to determine if RFP is using/supports
+ * a partial (i.e., cache) or full table download approach for
+ * mapping information. When full table download approach is
+ * used all information is passed to RFP after an initial
+ * rfapi_query. When partial table download is used, only
+ * information matching a query is passed.
+ */
+typedef enum {
+ RFAPI_RFP_DOWNLOAD_PARTIAL = 0,
+ RFAPI_RFP_DOWNLOAD_FULL
+} rfapi_rfp_download_type;
+
+#define RFAPI_RFP_CFG_DEFAULT_FTD_ADVERTISEMENT_INTERVAL 1
+
+struct rfapi_rfp_cfg {
+ /* partial or full table download */
+ rfapi_rfp_download_type download_type; /* default=partial */
+ /*
+ * When full-table-download is enabled, this is the minimum
+ * number of seconds between times a non-queried prefix will
+ * be updated to a particular NVE.
+ * default: RFAPI_RFP_CFG_DEFAULT_FTD_ADVERTISEMENT_INTERVAL
+ */
+ uint32_t ftd_advertisement_interval;
+ /*
+ * percentage of registration lifetime to continue to use information
+ * post soft-state refresh timeout
+ default: RFAPI_RFP_CFG_DEFAULT_HOLDDOWN_FACTOR
+ */
+ uint32_t holddown_factor;
+ /* Control generation of updated RFP responses */
+ uint8_t use_updated_response; /* default=0/no */
+ /* when use_updated_response, also generate remove responses */
+ uint8_t use_removes; /* default=0/no */
+};
+
+/***********************************************************************
+ * Process related functions -- MUST be provided by the RFAPI user <<===
+ ***********************************************************************/
+
+/*------------------------------------------
+ * rfp_start
+ *
+ * This function will start the RFP code
+ *
+ * input:
+ * master quagga thread_master to tie into bgpd threads
+ *
+ * output:
+ * cfgp Pointer to rfapi_rfp_cfg (null = use defaults),
+ * copied by caller, updated via rfp_set_configuration
+ * cbmp Pointer to rfapi_rfp_cb_methods, may be null
+ * copied by caller, updated via rfapi_rfp_set_cb_methods
+ * return value:
+ * rfp_start_val rfp returned value passed on rfp_stop and other rfapi calls
+--------------------------------------------*/
+extern void *rfp_start(struct thread_master *master,
+ struct rfapi_rfp_cfg **cfgp,
+ struct rfapi_rfp_cb_methods **cbmp);
+
+/*------------------------------------------
+ * rfp_stop
+ *
+ * This function is called on shutdown to trigger RFP cleanup
+ *
+ * input:
+ * rfp_start_val
+ *
+ * output:
+ * none
+ *
+ * return value:
+--------------------------------------------*/
+extern void rfp_stop(void *rfp_start_val);
+
+/***********************************************************************
+ * RFP processing behavior configuration
+ ***********************************************************************/
+
+/*------------------------------------------
+ * rfapi_rfp_set_configuration
+ *
+ * This is used to change rfapi's processing behavior based on
+ * RFP requirements.
+ *
+ * input:
+ * rfp_start_val value returned by rfp_start
+ * rfapi_rfp_cfg Pointer to configuration structure
+ *
+ * output:
+ * none
+ *
+ * return value:
+ * 0 Success
+ * ENXIO Unabled to locate configured BGP/VNC
+--------------------------------------------*/
+extern int rfapi_rfp_set_configuration(void *rfp_start_val,
+ struct rfapi_rfp_cfg *rfp_cfg);
+
+/*------------------------------------------
+ * rfapi_rfp_set_cb_methods
+ *
+ * Change registered callback functions for asynchronous notifications
+ * from RFAPI to the RFP client.
+ *
+ * input:
+ * rfp_start_val value by rfp_start
+ * methods Pointer to struct rfapi_rfp_cb_methods containing
+ * pointers to callback methods as described above
+ *
+ * return value:
+ * 0 Success
+ * ENXIO BGP or VNC not configured
+ *------------------------------------------*/
+extern int rfapi_rfp_set_cb_methods(void *rfp_start_val,
+ struct rfapi_rfp_cb_methods *methods);
+
+/***********************************************************************
+ * RFP group specific configuration
+ ***********************************************************************/
+
+/*------------------------------------------
+ * rfapi_rfp_init_group_config_ptr_vty
+ *
+ * This is used to init or return a previously init'ed group specific
+ * configuration pointer. Group is identified by vty context.
+ * NOTE: size is ignored when a previously init'ed value is returned.
+ * RFAPI frees rfp_cfg_group when group is deleted during reconfig,
+ * bgp restart or shutdown.
+ *
+ * input:
+ * rfp_start_val value returned by rfp_start
+ * type group type
+ * vty quagga vty context
+ * size number of bytes to allocation
+ *
+ * output:
+ * none
+ *
+ * return value:
+ * rfp_cfg_group NULL or Pointer to configuration structure
+--------------------------------------------*/
+extern void *rfapi_rfp_init_group_config_ptr_vty(void *rfp_start_val,
+ rfapi_rfp_cfg_group_type type,
+ struct vty *vty,
+ uint32_t size);
+
+/*------------------------------------------
+ * rfapi_rfp_get_group_config_ptr_vty
+ *
+ * This is used to get group specific configuration pointer.
+ * Group is identified by type and vty context.
+ * RFAPI frees rfp_cfg_group when group is deleted during reconfig,
+ * bgp restart or shutdown.
+ *
+ * input:
+ * rfp_start_val value returned by rfp_start
+ * type group type
+ * vty quagga vty context
+ *
+ * output:
+ * none
+ *
+ * return value:
+ * rfp_cfg_group Pointer to configuration structure
+--------------------------------------------*/
+extern void *rfapi_rfp_get_group_config_ptr_vty(void *rfp_start_val,
+ rfapi_rfp_cfg_group_type type,
+ struct vty *vty);
+
+/*------------------------------------------
+ * rfp_group_config_search_cb_t (callback typedef)
+ *
+ * This callback is used to called from within a
+ * rfapi_rfp_get_group_config_ptr to check if the rfp_cfg_group
+ * matches the search criteria
+ *
+ * input:
+ * criteria RFAPI caller provided serach criteria
+ * rfp_cfg_group Pointer to configuration structure | NULL
+ *
+ * output:
+ *
+ * return value:
+ * 0 Match/Success
+ * ENOENT No matching
+--------------------------------------------*/
+typedef int(rfp_group_config_search_cb_t)(void *criteria, void *rfp_cfg_group);
+
+/*------------------------------------------
+ * rfapi_rfp_get_group_config_ptr_name
+ *
+ * This is used to get group specific configuration pointer.
+ * Group is identified by type and name context.
+ * RFAPI frees rfp_cfg_group when group is deleted during reconfig,
+ * bgp restart or shutdown.
+ *
+ * input:
+ * rfp_start_val value returned by rfp_start
+ * type group type
+ * name group name
+ * criteria RFAPI caller provided serach criteria
+ * search_cb optional rfp_group_config_search_cb_t
+ *
+ * output:
+ * none
+ *
+ * return value:
+ * rfp_cfg_group Pointer to configuration structure
+--------------------------------------------*/
+extern void *rfapi_rfp_get_group_config_ptr_name(
+ void *rfp_start_val, rfapi_rfp_cfg_group_type type, const char *name,
+ void *criteria, rfp_group_config_search_cb_t *search_cb);
+
+/*------------------------------------------
+ * rfapi_rfp_get_l2_group_config_ptr_lni
+ *
+ * This is used to get group specific configuration pointer.
+ * Group is identified by type and logical network identifier.
+ * RFAPI frees rfp_cfg_group when group is deleted during reconfig,
+ * bgp restart or shutdown.
+ *
+ * input:
+ * rfp_start_val value returned by rfp_start
+ * logical_net_id group logical network identifier
+ * criteria RFAPI caller provided serach criteria
+ * search_cb optional rfp_group_config_search_cb_t
+ *
+ * output:
+ * none
+ *
+ * return value:
+ * rfp_cfg_group Pointer to configuration structure
+--------------------------------------------*/
+extern void *
+rfapi_rfp_get_l2_group_config_ptr_lni(void *rfp_start_val,
+ uint32_t logical_net_id, void *criteria,
+ rfp_group_config_search_cb_t *search_cb);
+
+/***********************************************************************
+ * NVE Sessions
+ ***********************************************************************/
+
+/*------------------------------------------
+ * rfapi_open
+ *
+ * This function initializes a NVE record and associates it with
+ * the specified VN and underlay network addresses
+ *
+ * input:
+ * rfp_start_val value returned by rfp_start
+ * vn NVE virtual network address
+ *
+ * un NVE underlay network address
+ *
+ * default_options Default options to use on registrations.
+ * For now only tunnel type is supported.
+ * May be overridden per-prefix in rfapi_register().
+ * Caller owns (rfapi_open() does not free)
+ *
+ * response_cb Pointer to next hop list update callback function or
+ * NULL when no callbacks are desired.
+ *
+ * userdata Passed to subsequent response_cb invocations.
+ *
+ * output:
+ * response_lifetime The length of time that responses sent to this
+ * NVE are valid.
+ *
+ * pHandle pointer to location to store rfapi handle. The
+ * handle must be passed on subsequent rfapi_ calls.
+ *
+ *
+ * return value:
+ * 0 Success
+ * EEXIST NVE with this {vn,un} already open
+ * ENOENT No matching nve group config
+ * ENOMSG Matched nve group config was incomplete
+ * ENXIO BGP or VNC not configured
+ * EAFNOSUPPORT Matched nve group specifies auto-assignment of RD,
+ * but underlay network address is not IPv4
+ * EDEADLK Called from within a callback procedure
+ *------------------------------------------*/
+extern int rfapi_open(void *rfp_start_val, struct rfapi_ip_addr *vn,
+ struct rfapi_ip_addr *un,
+ struct rfapi_un_option *default_options,
+ uint32_t *response_lifetime, void *userdata,
+ rfapi_handle *pHandle);
+
+
+/*------------------------------------------
+ * rfapi_close
+ *
+ * Shut down NVE session and release associated data. Calling
+ * from within a rfapi callback procedure is permitted (the close
+ * will be completed asynchronously after the callback finishes).
+ *
+ * input:
+ * rfd: rfapi descriptor returned by rfapi_open
+ *
+ * output:
+ *
+ * return value:
+ * 0 Success
+ * EBADF invalid handle
+ * ENXIO BGP or VNC not configured
+ *------------------------------------------*/
+extern int rfapi_close(rfapi_handle rfd);
+
+/*------------------------------------------
+ * rfapi_check
+ *
+ * Test rfapi descriptor
+ *
+ * input:
+ * rfd: rfapi descriptor returned by rfapi_open
+ *
+ * output:
+ *
+ * return value:
+ * 0 Success: handle is valid and usable
+ * EINVAL null argument
+ * ESTALE formerly valid handle invalidated by config, needs close
+ * EBADF invalid handle
+ * ENXIO BGP or VNC not configured
+ * EAFNOSUPPORT Internal addressing error
+ *------------------------------------------*/
+extern int rfapi_check(rfapi_handle rfd);
+
+/***********************************************************************
+ * NVE Routes
+ ***********************************************************************/
+
+/*------------------------------------------
+ * rfapi_query
+ *
+ * This function queries the RIB for a
+ * particular route. Note that this call may result in subsequent
+ * callbacks to response_cb. Response callbacks can be cancelled
+ * by calling rfapi_query_done. A duplicate query using the same target
+ * will result in only one callback per change in next_hops. (i.e.,
+ * cancel/replace the prior query results.)
+ *
+ * input:
+ * rfd: rfapi descriptor returned by rfapi_open
+ * target: the destination address
+ * l2o ptr to L2 Options struct, NULL if not present in query
+ *
+ * output:
+ * ppNextHopEntry pointer to a location to store a pointer
+ * to the returned list of nexthops. It is the
+ * caller's responsibility to free this list
+ * via rfapi_free_next_hop_list().
+ *
+ *
+ * return value:
+ * 0 Success
+ * EBADF invalid handle
+ * ENOENT no valid route
+ * ENXIO BGP or VNC not configured
+ * ESTALE descriptor is no longer usable; should be closed
+ * EDEADLK Called from within a callback procedure
+--------------------------------------------*/
+extern int rfapi_query(rfapi_handle rfd, struct rfapi_ip_addr *target,
+ struct rfapi_l2address_option *l2o,
+ struct rfapi_next_hop_entry **ppNextHopEntry);
+
+/*------------------------------------------
+ * rfapi_query_done
+ *
+ * Notifies the rfapi that the user is no longer interested
+ * in the specified target.
+ *
+ * input:
+ * rfd: rfapi descriptor returned by rfapi_open
+ * target: the destination address
+ *
+ * output:
+ *
+ * return value:
+ * 0 Success
+ * EBADF invalid handle
+ * ENOENT no match found for target
+ * ENXIO BGP or VNC not configured
+ * ESTALE descriptor is no longer usable; should be closed
+ * EDEADLK Called from within a callback procedure
+--------------------------------------------*/
+extern int rfapi_query_done(rfapi_handle rfd, struct rfapi_ip_addr *target);
+
+/*------------------------------------------
+ * rfapi_query_done_all
+ *
+ * Notifies the rfapi that the user is no longer interested
+ * in any target.
+ *
+ * input:
+ * rfd: rfapi descriptor returned by rfapi_open
+ *
+ * output:
+ * count: number of queries cleared
+ *
+ * return value:
+ * 0 Success
+ * EBADF invalid handle
+ * ENXIO BGP or VNC not configured
+ * ESTALE descriptor is no longer usable; should be closed
+ * EDEADLK Called from within a callback procedure
+--------------------------------------------*/
+extern int rfapi_query_done_all(rfapi_handle rfd, int *count);
+
+/*------------------------------------------
+ * rfapi_register
+ *
+ * Requests that reachability to the indicated prefix via this NVE
+ * be advertised by BGP. If <unregister> is non-zero, then the previously-
+ * advertised prefix should be withdrawn.
+ *
+ * (This function should NOT be called if the rfapi_open() function
+ * returns NULL)
+ *
+ * input:
+ * rfd: rfapi descriptor returned by rfapi_open
+ * prefix: A prefix to be registered or deregistered
+ * lifetime Prefix lifetime in seconds, host byte order
+ * options_un underlay netowrk options, may include tunnel-type
+ * Caller owns (rfapi_register() does not free).
+ * options_vn virtual network options, may include layer 2 address
+ * option and local-nexthop option
+ * Caller owns (rfapi_register() does not free).
+ *
+ * action: RFAPI_REGISTER_ADD add the route
+ * RFAPI_REGISTER_WITHDRAW withdraw route
+ * RFAPI_REGISTER_KILL withdraw without holddown
+ *
+ * return value:
+ * 0 Success
+ * EBADF invalid handle
+ * ENXIO BGP or VNC not configured
+ * ESTALE descriptor is no longer usable; should be closed
+ * EDEADLK Called from within a callback procedure
+ --------------------------------------------*/
+
+typedef enum {
+ RFAPI_REGISTER_ADD,
+ RFAPI_REGISTER_WITHDRAW,
+ RFAPI_REGISTER_KILL
+} rfapi_register_action;
+
+extern int rfapi_register(rfapi_handle rfd, struct rfapi_ip_prefix *prefix,
+ uint32_t lifetime, struct rfapi_un_option *options_un,
+ struct rfapi_vn_option *options_vn,
+ rfapi_register_action action);
+
+/***********************************************************************
+ * Helper / Utility functions
+ ***********************************************************************/
+
+/*------------------------------------------
+ * rfapi_get_vn_addr
+ *
+ * Get the virtual network address used by an NVE based on it's RFD
+ *
+ * input:
+ * rfd: rfapi descriptor returned by rfapi_open or rfapi_create_generic
+ *
+ * output:
+ *
+ * return value:
+ * vn NVE virtual network address
+ *------------------------------------------*/
+extern struct rfapi_ip_addr *rfapi_get_vn_addr(void *);
+
+/*------------------------------------------
+ * rfapi_get_un_addr
+ *
+ * Get the underlay network address used by an NVE based on it's RFD
+ *
+ * input:
+ * rfd: rfapi descriptor returned by rfapi_open or rfapi_create_generic
+ *
+ * output:
+ *
+ * return value:
+ * un NVE underlay network address
+ *------------------------------------------*/
+extern struct rfapi_ip_addr *rfapi_get_un_addr(void *);
+
+/*------------------------------------------
+ * rfapi_error_str
+ *
+ * Returns a string describing the rfapi error code.
+ *
+ * input:
+ *
+ * code Error code returned by rfapi function
+ *
+ * returns:
+ *
+ * const char * String
+ *------------------------------------------*/
+extern const char *rfapi_error_str(int code);
+
+/*------------------------------------------
+ * rfapi_get_rfp_start_val
+ *
+ * Returns value passed to rfapi on rfp_start
+ *
+ * input:
+ * void * bgp structure
+ *
+ * returns:
+ * void *
+ *------------------------------------------*/
+extern void *rfapi_get_rfp_start_val(void *bgpv);
+
+/*------------------------------------------
+ * rfapi_compare_rfds
+ *
+ * Compare two generic rfapi descriptors.
+ *
+ * input:
+ * rfd1: rfapi descriptor returned by rfapi_open or rfapi_create_generic
+ * rfd2: rfapi descriptor returned by rfapi_open or rfapi_create_generic
+ *
+ * output:
+ *
+ * return value:
+ * 0 Mismatch
+ * 1 Match
+ *------------------------------------------*/
+extern int rfapi_compare_rfds(void *rfd1, void *rfd2);
+
+/*------------------------------------------
+ * rfapi_free_next_hop_list
+ *
+ * Frees a next_hop_list returned by a rfapi_query invocation
+ *
+ * input:
+ * list: a pointer to a response list (as a
+ * struct rfapi_next_hop_entry) to free.
+ *
+ * output:
+ *
+ * return value: None
+ --------------------------------------------*/
+extern void rfapi_free_next_hop_list(struct rfapi_next_hop_entry *list);
+
+/*------------------------------------------
+ * rfapi_get_response_lifetime_default
+ *
+ * Returns the default lifetime for a response.
+ * rfp_start_val value returned by rfp_start or
+ * NULL (=use default instance)
+ *
+ * input:
+ * None
+ *
+ * output:
+ *
+ * return value: The bgp instance default lifetime for a response.
+ --------------------------------------------*/
+extern int rfapi_get_response_lifetime_default(void *rfp_start_val);
+
+/*------------------------------------------
+ * rfapi_is_vnc_configured
+ *
+ * Returns if VNC is configured
+ *
+ * input:
+ * rfp_start_val value returned by rfp_start or
+ * NULL (=use default instance)
+ *
+ * output:
+ *
+ * return value: If VNC is configured for the bgpd instance
+ * 0 Success
+ * ENXIO VNC not configured
+ --------------------------------------------*/
+extern int rfapi_is_vnc_configured(void *rfp_start_val);
+
+/*------------------------------------------
+ * rfapi_bgp_lookup_by_rfp
+ *
+ * Find bgp instance pointer based on value returned by rfp_start
+ *
+ * input:
+ * rfp_start_val value returned by rfp_startor
+ * NULL (=get default instance)
+ *
+ * output:
+ * none
+ *
+ * return value:
+ * bgp bgp instance pointer
+ * NULL = not found
+ *
+ --------------------------------------------*/
+extern struct bgp *rfapi_bgp_lookup_by_rfp(void *rfp_start_val);
+
+/*------------------------------------------
+ * rfapi_get_rfp_start_val_by_bgp
+ *
+ * Find bgp instance pointer based on value returned by rfp_start
+ *
+ * input:
+ * bgp bgp instance pointer
+ *
+ * output:
+ * none
+ *
+ * return value:
+ * rfp_start_val
+ * NULL = not found
+ *
+ --------------------------------------------*/
+extern void *rfapi_get_rfp_start_val_by_bgp(struct bgp *bgp);
+
+#endif /* ENABLE_BGP_VNC */
+
+#endif /* _QUAGGA_BGP_RFAPI_H */
diff --git a/bgpd/rfapi/rfapi_ap.c b/bgpd/rfapi/rfapi_ap.c
new file mode 100644
index 0000000..fcc6168
--- /dev/null
+++ b/bgpd/rfapi/rfapi_ap.c
@@ -0,0 +1,555 @@
+/*
+ *
+ * Copyright 2009-2016, LabN Consulting, L.L.C.
+ *
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program; see the file COPYING; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
+ */
+
+#include "lib/zebra.h"
+#include "lib/prefix.h"
+#include "lib/agg_table.h"
+#include "lib/vty.h"
+#include "lib/memory.h"
+#include "lib/routemap.h"
+#include "lib/log.h"
+#include "lib/linklist.h"
+#include "lib/command.h"
+#include "lib/stream.h"
+
+#include "bgpd/bgpd.h"
+#include "bgpd/bgp_ecommunity.h"
+#include "bgpd/bgp_attr.h"
+
+#include "bgpd/rfapi/bgp_rfapi_cfg.h"
+#include "bgpd/rfapi/rfapi.h"
+#include "bgpd/rfapi/rfapi_backend.h"
+
+#include "bgpd/bgp_route.h"
+#include "bgpd/bgp_mplsvpn.h"
+#include "bgpd/bgp_aspath.h"
+#include "bgpd/bgp_advertise.h"
+
+#include "bgpd/rfapi/rfapi_import.h"
+#include "bgpd/rfapi/rfapi_private.h"
+#include "bgpd/rfapi/rfapi_monitor.h"
+#include "bgpd/rfapi/rfapi_vty.h"
+#include "bgpd/rfapi/vnc_export_bgp.h"
+#include "bgpd/rfapi/vnc_export_bgp_p.h"
+#include "bgpd/rfapi/vnc_zebra.h"
+#include "bgpd/rfapi/vnc_import_bgp.h"
+#include "bgpd/rfapi/rfapi_rib.h"
+
+#include "bgpd/rfapi/rfapi_ap.h"
+#include "bgpd/rfapi/vnc_debug.h"
+
+/*
+ * Per-NVE Advertised prefixes
+ *
+ * We maintain a list of prefixes advertised by each NVE.
+ * There are two indices: by prefix and by lifetime.
+ *
+ * BY-PREFIX skiplist
+ *
+ * key: ptr to struct prefix (when storing, point to prefix that
+ * is part of rfapi_adb).
+ *
+ * value: ptr to struct rfapi_adb
+ *
+ * BY-LIFETIME skiplist
+ *
+ * key: ptr to struct rfapi_adb
+ * value: ptr to struct rfapi_adb
+ *
+ */
+
+/*
+ * Skiplist sort function that sorts first according to lifetime
+ * and then according to adb pointer value. The adb pointer
+ * is used to spread out the sort for adbs with the same lifetime
+ * and thereby make the skip list operations more efficient.
+ */
+static int sl_adb_lifetime_cmp(const void *adb1, const void *adb2)
+{
+ const struct rfapi_adb *a1 = adb1;
+ const struct rfapi_adb *a2 = adb2;
+
+ if (a1->lifetime < a2->lifetime)
+ return -1;
+ if (a1->lifetime > a2->lifetime)
+ return 1;
+
+ if (a1 < a2)
+ return -1;
+ if (a1 > a2)
+ return 1;
+
+ return 0;
+}
+
+void rfapiApInit(struct rfapi_advertised_prefixes *ap)
+{
+ ap->ipN_by_prefix = skiplist_new(0, rfapi_rib_key_cmp, NULL);
+ ap->ip0_by_ether = skiplist_new(0, rfapi_rib_key_cmp, NULL);
+ ap->by_lifetime = skiplist_new(0, sl_adb_lifetime_cmp, NULL);
+}
+
+void rfapiApRelease(struct rfapi_advertised_prefixes *ap)
+{
+ struct rfapi_adb *adb;
+
+ /* Free ADBs and lifetime items */
+ while (0 == skiplist_first(ap->by_lifetime, NULL, (void **)&adb)) {
+ rfapiAdbFree(adb);
+ skiplist_delete_first(ap->by_lifetime);
+ }
+
+ while (0 == skiplist_delete_first(ap->ipN_by_prefix))
+ ;
+ while (0 == skiplist_delete_first(ap->ip0_by_ether))
+ ;
+
+ /* Free lists */
+ skiplist_free(ap->ipN_by_prefix);
+ skiplist_free(ap->ip0_by_ether);
+ skiplist_free(ap->by_lifetime);
+
+ ap->ipN_by_prefix = NULL;
+ ap->ip0_by_ether = NULL;
+ ap->by_lifetime = NULL;
+}
+
+int rfapiApCount(struct rfapi_descriptor *rfd)
+{
+ if (!rfd->advertised.by_lifetime)
+ return 0;
+
+ return skiplist_count(rfd->advertised.by_lifetime);
+}
+
+int rfapiApCountAll(struct bgp *bgp)
+{
+ struct rfapi *h;
+ struct listnode *node;
+ struct rfapi_descriptor *rfd;
+ int total = 0;
+
+ h = bgp->rfapi;
+ if (h) {
+ for (ALL_LIST_ELEMENTS_RO(&h->descriptors, node, rfd)) {
+ total += rfapiApCount(rfd);
+ }
+ }
+ return total;
+}
+
+
+void rfapiApReadvertiseAll(struct bgp *bgp, struct rfapi_descriptor *rfd)
+{
+ struct rfapi_adb *adb;
+ void *cursor = NULL;
+ int rc;
+
+ for (rc = skiplist_next(rfd->advertised.by_lifetime, NULL,
+ (void **)&adb, &cursor);
+ rc == 0; rc = skiplist_next(rfd->advertised.by_lifetime, NULL,
+ (void **)&adb, &cursor)) {
+
+ struct prefix_rd prd;
+ uint32_t local_pref = rfp_cost_to_localpref(adb->cost);
+
+ prd = rfd->rd;
+ prd.family = AF_UNSPEC;
+ prd.prefixlen = 64;
+
+ /*
+ * TBD this is not quite right. When pfx_ip is 0/32 or 0/128,
+ * we need to substitute the VN address as the prefix
+ */
+ add_vnc_route(rfd, bgp, SAFI_MPLS_VPN, &adb->u.s.prefix_ip,
+ &prd, /* RD to use (0 for ENCAP) */
+ &rfd->vn_addr, /* nexthop */
+ &local_pref, &adb->lifetime, NULL,
+ NULL, /* struct rfapi_un_option */
+ NULL, /* struct rfapi_vn_option */
+ rfd->rt_export_list, NULL, /* med */
+ NULL, ZEBRA_ROUTE_BGP, BGP_ROUTE_RFP, 0);
+ }
+}
+
+void rfapiApWithdrawAll(struct bgp *bgp, struct rfapi_descriptor *rfd)
+{
+ struct rfapi_adb *adb;
+ void *cursor;
+ int rc;
+
+
+ cursor = NULL;
+ for (rc = skiplist_next(rfd->advertised.by_lifetime, NULL,
+ (void **)&adb, &cursor);
+ rc == 0; rc = skiplist_next(rfd->advertised.by_lifetime, NULL,
+ (void **)&adb, &cursor)) {
+
+ struct prefix pfx_vn_buf;
+ struct prefix *pfx_ip;
+
+ if (!(RFAPI_0_PREFIX(&adb->u.s.prefix_ip)
+ && RFAPI_HOST_PREFIX(&adb->u.s.prefix_ip))) {
+
+ pfx_ip = &adb->u.s.prefix_ip;
+
+ } else {
+
+ pfx_ip = NULL;
+
+ /*
+ * 0/32 or 0/128 => mac advertisement
+ */
+ if (rfapiRaddr2Qprefix(&rfd->vn_addr, &pfx_vn_buf)) {
+ /*
+ * Bad: it means we can't delete the route
+ */
+ vnc_zlog_debug_verbose(
+ "%s: BAD: handle has bad vn_addr: skipping",
+ __func__);
+ continue;
+ }
+ }
+
+ del_vnc_route(rfd, rfd->peer, bgp, SAFI_MPLS_VPN,
+ pfx_ip ? pfx_ip : &pfx_vn_buf,
+ &adb->u.s.prd, /* RD to use (0 for ENCAP) */
+ ZEBRA_ROUTE_BGP, BGP_ROUTE_RFP, NULL, 0);
+ }
+}
+
+/*
+ * returns nonzero if tunnel readvertisement is needed, 0 otherwise
+ */
+static int rfapiApAdjustLifetimeStats(
+ struct rfapi_descriptor *rfd,
+ uint32_t *old_lifetime, /* set if removing/replacing */
+ uint32_t *new_lifetime) /* set if replacing/adding */
+{
+ int advertise = 0;
+ int find_max = 0;
+ int find_min = 0;
+
+ vnc_zlog_debug_verbose("%s: rfd=%p, pOldLife=%p, pNewLife=%p", __func__,
+ rfd, old_lifetime, new_lifetime);
+ if (old_lifetime)
+ vnc_zlog_debug_verbose("%s: OldLife=%d", __func__,
+ *old_lifetime);
+ if (new_lifetime)
+ vnc_zlog_debug_verbose("%s: NewLife=%d", __func__,
+ *new_lifetime);
+
+ if (new_lifetime) {
+ /*
+ * Adding new lifetime
+ */
+ if (old_lifetime) {
+ /*
+ * replacing existing lifetime
+ */
+
+
+ /* old and new are same */
+ if (*old_lifetime == *new_lifetime)
+ return 0;
+
+ if (*old_lifetime == rfd->min_prefix_lifetime) {
+ find_min = 1;
+ }
+ if (*old_lifetime == rfd->max_prefix_lifetime) {
+ find_max = 1;
+ }
+
+ /* no need to search if new value is at or equals
+ * min|max */
+ if (*new_lifetime <= rfd->min_prefix_lifetime) {
+ rfd->min_prefix_lifetime = *new_lifetime;
+ find_min = 0;
+ }
+ if (*new_lifetime >= rfd->max_prefix_lifetime) {
+ rfd->max_prefix_lifetime = *new_lifetime;
+ advertise = 1;
+ find_max = 0;
+ }
+
+ } else {
+ /*
+ * Just adding new lifetime
+ */
+ if (*new_lifetime < rfd->min_prefix_lifetime) {
+ rfd->min_prefix_lifetime = *new_lifetime;
+ }
+ if (*new_lifetime > rfd->max_prefix_lifetime) {
+ advertise = 1;
+ rfd->max_prefix_lifetime = *new_lifetime;
+ }
+ }
+ } else {
+ /*
+ * Deleting
+ */
+
+ /*
+ * See if the max prefix lifetime for this NVE has decreased.
+ * The easy optimization: track min & max; walk the table only
+ * if they are different.
+ * The general optimization: index the advertised_prefixes
+ * table by lifetime.
+ *
+ * Note: for a given nve_descriptor, only one of the
+ * advertised_prefixes[] tables will be used: viz., the
+ * address family that matches the VN address.
+ *
+ */
+ if (rfd->max_prefix_lifetime == rfd->min_prefix_lifetime) {
+
+ /*
+ * Common case: all lifetimes are the same. Only
+ * thing we need to do here is check if there are
+ * no exported routes left. In that case, reinitialize
+ * the max and min values.
+ */
+ if (!rfapiApCount(rfd)) {
+ rfd->max_prefix_lifetime = 0;
+ rfd->min_prefix_lifetime = UINT32_MAX;
+ }
+
+
+ } else {
+ if (old_lifetime) {
+ if (*old_lifetime == rfd->min_prefix_lifetime) {
+ find_min = 1;
+ }
+ if (*old_lifetime == rfd->max_prefix_lifetime) {
+ find_max = 1;
+ }
+ }
+ }
+ }
+
+ if (find_min || find_max) {
+ uint32_t min = UINT32_MAX;
+ uint32_t max = 0;
+
+ struct rfapi_adb *adb_min;
+ struct rfapi_adb *adb_max;
+
+ if (!skiplist_first(rfd->advertised.by_lifetime,
+ (void **)&adb_min, NULL)
+ && !skiplist_last(rfd->advertised.by_lifetime,
+ (void **)&adb_max, NULL)) {
+
+ /*
+ * This should always work
+ */
+ min = adb_min->lifetime;
+ max = adb_max->lifetime;
+
+ } else {
+
+ void *cursor;
+ struct rfapi_rib_key rk;
+ struct rfapi_adb *adb;
+ int rc;
+
+ vnc_zlog_debug_verbose(
+ "%s: walking to find new min/max", __func__);
+
+ cursor = NULL;
+ for (rc = skiplist_next(rfd->advertised.ipN_by_prefix,
+ (void **)&rk, (void **)&adb,
+ &cursor);
+ !rc;
+ rc = skiplist_next(rfd->advertised.ipN_by_prefix,
+ (void **)&rk, (void **)&adb,
+ &cursor)) {
+
+ uint32_t lt = adb->lifetime;
+
+ if (lt > max)
+ max = lt;
+ if (lt < min)
+ min = lt;
+ }
+ cursor = NULL;
+ for (rc = skiplist_next(rfd->advertised.ip0_by_ether,
+ (void **)&rk, (void **)&adb,
+ &cursor);
+ !rc;
+ rc = skiplist_next(rfd->advertised.ip0_by_ether,
+ (void **)&rk, (void **)&adb,
+ &cursor)) {
+
+ uint32_t lt = adb->lifetime;
+
+ if (lt > max)
+ max = lt;
+ if (lt < min)
+ min = lt;
+ }
+ }
+
+ /*
+ * trigger tunnel route update
+ * but only if we found a VPN route and it had
+ * a lifetime greater than 0
+ */
+ if (max && rfd->max_prefix_lifetime != max)
+ advertise = 1;
+ rfd->max_prefix_lifetime = max;
+ rfd->min_prefix_lifetime = min;
+ }
+
+ vnc_zlog_debug_verbose("%s: returning advertise=%d, min=%d, max=%d",
+ __func__, advertise, rfd->min_prefix_lifetime,
+ rfd->max_prefix_lifetime);
+
+ return (advertise != 0);
+}
+
+/*
+ * Return Value
+ *
+ * 0 No need to advertise tunnel route
+ * non-0 advertise tunnel route
+ */
+int rfapiApAdd(struct bgp *bgp, struct rfapi_descriptor *rfd,
+ struct prefix *pfx_ip, struct prefix *pfx_eth,
+ struct prefix_rd *prd, uint32_t lifetime, uint8_t cost,
+ struct rfapi_l2address_option *l2o) /* other options TBD */
+{
+ int rc;
+ struct rfapi_adb *adb;
+ uint32_t old_lifetime = 0;
+ int use_ip0 = 0;
+ struct rfapi_rib_key rk;
+
+ rfapi_rib_key_init(pfx_ip, prd, pfx_eth, &rk);
+ if (RFAPI_0_PREFIX(pfx_ip) && RFAPI_HOST_PREFIX(pfx_ip)) {
+ use_ip0 = 1;
+ assert(pfx_eth);
+ rc = skiplist_search(rfd->advertised.ip0_by_ether, &rk,
+ (void **)&adb);
+
+ } else {
+
+ /* find prefix in advertised prefixes list */
+ rc = skiplist_search(rfd->advertised.ipN_by_prefix, &rk,
+ (void **)&adb);
+ }
+
+
+ if (rc) {
+ /* Not found */
+ adb = XCALLOC(MTYPE_RFAPI_ADB, sizeof(struct rfapi_adb));
+ adb->lifetime = lifetime;
+ adb->u.key = rk;
+
+ if (use_ip0) {
+ assert(pfx_eth);
+ skiplist_insert(rfd->advertised.ip0_by_ether,
+ &adb->u.key, adb);
+ } else {
+ skiplist_insert(rfd->advertised.ipN_by_prefix,
+ &adb->u.key, adb);
+ }
+
+ skiplist_insert(rfd->advertised.by_lifetime, adb, adb);
+ } else {
+ old_lifetime = adb->lifetime;
+ if (old_lifetime != lifetime) {
+ assert(!skiplist_delete(rfd->advertised.by_lifetime,
+ adb, NULL));
+ adb->lifetime = lifetime;
+ assert(!skiplist_insert(rfd->advertised.by_lifetime,
+ adb, adb));
+ }
+ }
+ adb->cost = cost;
+ if (l2o)
+ adb->l2o = *l2o;
+ else
+ memset(&adb->l2o, 0, sizeof(struct rfapi_l2address_option));
+
+ if (rfapiApAdjustLifetimeStats(rfd, (rc ? NULL : &old_lifetime),
+ &lifetime))
+ return 1;
+
+ return 0;
+}
+
+/*
+ * After this function returns successfully, caller should call
+ * rfapiAdjustLifetimeStats() and possibly rfapiTunnelRouteAnnounce()
+ */
+int rfapiApDelete(struct bgp *bgp, struct rfapi_descriptor *rfd,
+ struct prefix *pfx_ip, struct prefix *pfx_eth,
+ struct prefix_rd *prd, int *advertise_tunnel) /* out */
+{
+ int rc;
+ struct rfapi_adb *adb;
+ uint32_t old_lifetime;
+ int use_ip0 = 0;
+ struct rfapi_rib_key rk;
+
+ if (advertise_tunnel)
+ *advertise_tunnel = 0;
+
+ rfapi_rib_key_init(pfx_ip, prd, pfx_eth, &rk);
+ /* find prefix in advertised prefixes list */
+ if (RFAPI_0_PREFIX(pfx_ip) && RFAPI_HOST_PREFIX(pfx_ip)) {
+ use_ip0 = 1;
+ assert(pfx_eth);
+
+ rc = skiplist_search(rfd->advertised.ip0_by_ether, &rk,
+ (void **)&adb);
+
+ } else {
+
+ /* find prefix in advertised prefixes list */
+ rc = skiplist_search(rfd->advertised.ipN_by_prefix, &rk,
+ (void **)&adb);
+ }
+
+ if (rc) {
+ return ENOENT;
+ }
+
+ old_lifetime = adb->lifetime;
+
+ if (use_ip0) {
+ rc = skiplist_delete(rfd->advertised.ip0_by_ether, &rk, NULL);
+ } else {
+ rc = skiplist_delete(rfd->advertised.ipN_by_prefix, &rk, NULL);
+ }
+ assert(!rc);
+
+ rc = skiplist_delete(rfd->advertised.by_lifetime, adb, NULL);
+ assert(!rc);
+
+ rfapiAdbFree(adb);
+
+ if (rfapiApAdjustLifetimeStats(rfd, &old_lifetime, NULL)) {
+ if (advertise_tunnel)
+ *advertise_tunnel = 1;
+ }
+
+ return 0;
+}
diff --git a/bgpd/rfapi/rfapi_ap.h b/bgpd/rfapi/rfapi_ap.h
new file mode 100644
index 0000000..56977bd
--- /dev/null
+++ b/bgpd/rfapi/rfapi_ap.h
@@ -0,0 +1,85 @@
+/*
+ *
+ * Copyright 2009-2016, LabN Consulting, L.L.C.
+ *
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program; see the file COPYING; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
+ */
+#ifndef _QUAGGA_BGP_RFAPI_AP_H
+#define _QUAGGA_BGP_RFAPI_AP_H
+
+/* TBD delete some of these #includes */
+
+#include <errno.h>
+
+#include "lib/zebra.h"
+#include "lib/prefix.h"
+#include "lib/table.h"
+#include "lib/vty.h"
+#include "lib/memory.h"
+#include "lib/routemap.h"
+#include "lib/log.h"
+#include "lib/linklist.h"
+#include "lib/command.h"
+#include "lib/stream.h"
+
+#include "bgpd/bgpd.h"
+
+#include "bgp_rfapi_cfg.h"
+#include "rfapi.h"
+#include "rfapi_backend.h"
+
+#include "bgpd/bgp_route.h"
+#include "bgpd/bgp_aspath.h"
+#include "bgpd/bgp_advertise.h"
+
+#include "rfapi_import.h"
+#include "rfapi_private.h"
+#include "rfapi_monitor.h"
+#include "rfapi_vty.h"
+#include "vnc_export_bgp.h"
+#include "vnc_export_bgp_p.h"
+#include "vnc_zebra.h"
+#include "vnc_import_bgp.h"
+#include "rfapi_rib.h"
+
+
+extern void rfapiApInit(struct rfapi_advertised_prefixes *ap);
+
+extern void rfapiApRelease(struct rfapi_advertised_prefixes *ap);
+
+extern int rfapiApCount(struct rfapi_descriptor *rfd);
+
+
+extern int rfapiApCountAll(struct bgp *bgp);
+
+extern void rfapiApReadvertiseAll(struct bgp *bgp,
+ struct rfapi_descriptor *rfd);
+
+extern void rfapiApWithdrawAll(struct bgp *bgp, struct rfapi_descriptor *rfd);
+
+extern int
+rfapiApAdd(struct bgp *bgp, struct rfapi_descriptor *rfd, struct prefix *pfx_ip,
+ struct prefix *pfx_eth, struct prefix_rd *prd, uint32_t lifetime,
+ uint8_t cost,
+ struct rfapi_l2address_option *l2o); /* other options TBD */
+
+extern int rfapiApDelete(struct bgp *bgp, struct rfapi_descriptor *rfd,
+ struct prefix *pfx_ip, struct prefix *pfx_eth,
+ struct prefix_rd *prd,
+ int *advertise_tunnel); /* out */
+
+
+#endif /* _QUAGGA_BGP_RFAPI_AP_H */
diff --git a/bgpd/rfapi/rfapi_backend.h b/bgpd/rfapi/rfapi_backend.h
new file mode 100644
index 0000000..4d2ae0b
--- /dev/null
+++ b/bgpd/rfapi/rfapi_backend.h
@@ -0,0 +1,73 @@
+/*
+ *
+ * Copyright 2009-2016, LabN Consulting, L.L.C.
+ *
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program; see the file COPYING; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
+ */
+
+#ifndef _QUAGGA_BGP_RFAPI_BACKEND_H
+#define _QUAGGA_BGP_RFAPI_BACKEND_H
+
+#ifdef ENABLE_BGP_VNC
+
+#include "bgpd/bgp_route.h"
+#include "bgpd/bgp_nexthop.h"
+
+extern void rfapi_init(void);
+extern void vnc_zebra_init(struct thread_master *master);
+extern void vnc_zebra_destroy(void);
+
+extern void rfapi_delete(struct bgp *);
+
+struct rfapi *bgp_rfapi_new(struct bgp *bgp);
+void bgp_rfapi_destroy(struct bgp *bgp, struct rfapi *h);
+
+extern void rfapiProcessUpdate(struct peer *peer, void *rfd,
+ const struct prefix *p, struct prefix_rd *prd,
+ struct attr *attr, afi_t afi, safi_t safi,
+ uint8_t type, uint8_t sub_type, uint32_t *label);
+
+
+extern void rfapiProcessWithdraw(struct peer *peer, void *rfd,
+ const struct prefix *p, struct prefix_rd *prd,
+ struct attr *attr, afi_t afi, safi_t safi,
+ uint8_t type, int kill);
+
+extern void rfapiProcessPeerDown(struct peer *peer);
+
+extern void vnc_zebra_announce(struct prefix *p,
+ struct bgp_path_info *new_select,
+ struct bgp *bgp);
+
+extern void vnc_zebra_withdraw(struct prefix *p,
+ struct bgp_path_info *old_select);
+
+
+extern void rfapi_vty_out_vncinfo(struct vty *vty, const struct prefix *p,
+ struct bgp_path_info *bpi, safi_t safi);
+
+
+extern void vnc_direct_bgp_vpn_enable(struct bgp *bgp, afi_t afi);
+
+extern void vnc_direct_bgp_vpn_disable(struct bgp *bgp, afi_t afi);
+
+extern void vnc_direct_bgp_rh_vpn_enable(struct bgp *bgp, afi_t afi);
+
+extern void vnc_direct_bgp_rh_vpn_disable(struct bgp *bgp, afi_t afi);
+
+#endif /* ENABLE_BGP_VNC */
+
+#endif /* _QUAGGA_BGP_RFAPI_BACKEND_H */
diff --git a/bgpd/rfapi/rfapi_descriptor_rfp_utils.c b/bgpd/rfapi/rfapi_descriptor_rfp_utils.c
new file mode 100644
index 0000000..ce5acf0
--- /dev/null
+++ b/bgpd/rfapi/rfapi_descriptor_rfp_utils.c
@@ -0,0 +1,123 @@
+/*
+ *
+ * Copyright 2009-2016, LabN Consulting, L.L.C.
+ *
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program; see the file COPYING; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
+ */
+
+#include "lib/zebra.h"
+#include "lib/prefix.h"
+#include "lib/table.h"
+#include "lib/vty.h"
+#include "lib/memory.h"
+#include "lib/log.h"
+
+#include "bgpd/bgpd.h"
+
+#include "bgpd/rfapi/rfapi.h"
+#include "bgpd/rfapi/rfapi_private.h"
+#include "bgpd/rfapi/rfapi_descriptor_rfp_utils.h"
+#include "bgpd/rfapi/vnc_debug.h"
+
+
+void *rfapi_create_generic(struct rfapi_ip_addr *vn, struct rfapi_ip_addr *un)
+{
+ struct rfapi_descriptor *rfd;
+ rfd = XCALLOC(MTYPE_RFAPI_DESC, sizeof(struct rfapi_descriptor));
+ vnc_zlog_debug_verbose("%s: rfd=%p", __func__, rfd);
+ rfd->vn_addr = *vn;
+ rfd->un_addr = *un;
+ return (void *)rfd;
+}
+
+/*------------------------------------------
+ * rfapi_free_generic
+ *
+ * Compare two generic rfapi descriptors.
+ *
+ * input:
+ * grfd: rfapi descriptor returned by rfapi_open or rfapi_create_generic
+ *
+ * output:
+ *
+ * return value:
+ *
+ *------------------------------------------*/
+void rfapi_free_generic(void *grfd)
+{
+ struct rfapi_descriptor *rfd;
+ rfd = (struct rfapi_descriptor *)grfd;
+ XFREE(MTYPE_RFAPI_DESC, rfd);
+}
+
+
+/*------------------------------------------
+ * rfapi_compare_rfds
+ *
+ * Compare two generic rfapi descriptors.
+ *
+ * input:
+ * rfd1: rfapi descriptor returned by rfapi_open or rfapi_create_generic
+ * rfd2: rfapi descriptor returned by rfapi_open or rfapi_create_generic
+ *
+ * output:
+ *
+ * return value:
+ * 0 Mismatch
+ * 1 Match
+ *------------------------------------------*/
+int rfapi_compare_rfds(void *rfd1, void *rfd2)
+{
+ struct rfapi_descriptor *rrfd1, *rrfd2;
+ int match = 0;
+
+ rrfd1 = (struct rfapi_descriptor *)rfd1;
+ rrfd2 = (struct rfapi_descriptor *)rfd2;
+
+ if (rrfd1->vn_addr.addr_family == rrfd2->vn_addr.addr_family) {
+ if (rrfd1->vn_addr.addr_family == AF_INET)
+ match = IPV4_ADDR_SAME(&(rrfd1->vn_addr.addr.v4),
+ &(rrfd2->vn_addr.addr.v4));
+ else
+ match = IPV6_ADDR_SAME(&(rrfd1->vn_addr.addr.v6),
+ &(rrfd2->vn_addr.addr.v6));
+ }
+
+ /*
+ * If the VN addresses don't match in all forms,
+ * give up.
+ */
+ if (!match)
+ return 0;
+
+ /*
+ * do the process again for the UN addresses.
+ */
+ match = 0;
+ if (rrfd1->un_addr.addr_family == rrfd2->un_addr.addr_family) {
+ /* VN addresses match
+ * UN address families match
+ * now check the actual UN addresses
+ */
+ if (rrfd1->un_addr.addr_family == AF_INET)
+ match = IPV4_ADDR_SAME(&(rrfd1->un_addr.addr.v4),
+ &(rrfd2->un_addr.addr.v4));
+ else
+ match = IPV6_ADDR_SAME(&(rrfd1->un_addr.addr.v6),
+ &(rrfd2->un_addr.addr.v6));
+ }
+ return match;
+}
diff --git a/bgpd/rfapi/rfapi_descriptor_rfp_utils.h b/bgpd/rfapi/rfapi_descriptor_rfp_utils.h
new file mode 100644
index 0000000..dcc1979
--- /dev/null
+++ b/bgpd/rfapi/rfapi_descriptor_rfp_utils.h
@@ -0,0 +1,38 @@
+/*
+ *
+ * Copyright 2009-2016, LabN Consulting, L.L.C.
+ *
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program; see the file COPYING; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
+ */
+
+
+extern void *rfapi_create_generic(struct rfapi_ip_addr *vn,
+ struct rfapi_ip_addr *un);
+
+/*------------------------------------------
+ * rfapi_free_generic
+ *
+ * Compare two generic rfapi descriptors.
+ *
+ * input:
+ * grfd: rfapi descriptor returned by rfapi_open or rfapi_create_generic
+ *
+ * output:
+ *
+ * return value:
+ *
+ *------------------------------------------*/
+extern void rfapi_free_generic(void *grfd);
diff --git a/bgpd/rfapi/rfapi_encap_tlv.c b/bgpd/rfapi/rfapi_encap_tlv.c
new file mode 100644
index 0000000..d4e875d
--- /dev/null
+++ b/bgpd/rfapi/rfapi_encap_tlv.c
@@ -0,0 +1,743 @@
+/*
+ * Copyright 2015-2016, LabN Consulting, L.L.C.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program; see the file COPYING; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
+ */
+
+#include "lib/zebra.h"
+
+#include "lib/memory.h"
+#include "lib/prefix.h"
+#include "lib/table.h"
+#include "lib/vty.h"
+
+#include "bgpd/bgpd.h"
+#include "bgpd/bgp_attr.h"
+
+#include "bgpd/bgp_encap_types.h"
+#include "bgpd/bgp_encap_tlv.h"
+
+#include "bgpd/rfapi/rfapi.h"
+#include "bgpd/rfapi/rfapi_encap_tlv.h"
+#include "bgpd/rfapi/rfapi_private.h"
+#include "bgpd/rfapi/rfapi_monitor.h"
+#include "bgpd/rfapi/rfapi_vty.h"
+#include "bgpd/rfapi/bgp_rfapi_cfg.h"
+#include "bgpd/rfapi/vnc_debug.h"
+
+static void rfapi_add_endpoint_address_to_subtlv(
+ struct bgp *bgp, struct rfapi_ip_addr *ea,
+ struct bgp_tea_subtlv_remote_endpoint *subtlv)
+{
+ subtlv->family = ea->addr_family;
+ if (subtlv->family == AF_INET)
+ subtlv->ip_address.v4 = ea->addr.v4;
+ else
+ subtlv->ip_address.v6 = ea->addr.v6;
+ subtlv->as4 = htonl(bgp->as);
+}
+
+bgp_encap_types
+rfapi_tunneltype_option_to_tlv(struct bgp *bgp, struct rfapi_ip_addr *ea,
+ struct rfapi_tunneltype_option *tto,
+ struct attr *attr, int always_add)
+{
+
+#define _RTTO_MAYBE_ADD_ENDPOINT_ADDRESS(ttype) \
+ if ((always_add \
+ || (bgp->rfapi_cfg \
+ && !CHECK_FLAG(bgp->rfapi_cfg->flags, \
+ BGP_VNC_CONFIG_ADV_UN_METHOD_ENCAP))) \
+ && ea \
+ && !CHECK_SUBTLV_FLAG(&tto->bgpinfo.ttype, \
+ BGP_TEA_SUBTLV_REMOTE_ENDPOINT)) { \
+ rfapi_add_endpoint_address_to_subtlv( \
+ bgp, ea, &tto->bgpinfo.ttype.st_endpoint); \
+ SET_SUBTLV_FLAG(&tto->bgpinfo.ttype, \
+ BGP_TEA_SUBTLV_REMOTE_ENDPOINT); \
+ }
+
+ struct rfapi_tunneltype_option dto;
+ if (tto == NULL) { /* create default type */
+ tto = &dto;
+ memset(tto, 0, sizeof(dto));
+ tto->type = RFAPI_BGP_ENCAP_TYPE_DEFAULT;
+ }
+ switch (tto->type) {
+ case BGP_ENCAP_TYPE_L2TPV3_OVER_IP:
+ _RTTO_MAYBE_ADD_ENDPOINT_ADDRESS(l2tpv3_ip);
+ bgp_encap_type_l2tpv3overip_to_tlv(&tto->bgpinfo.l2tpv3_ip,
+ attr);
+ break;
+
+ case BGP_ENCAP_TYPE_GRE:
+ _RTTO_MAYBE_ADD_ENDPOINT_ADDRESS(gre);
+ bgp_encap_type_gre_to_tlv(&tto->bgpinfo.gre, attr);
+ break;
+
+ case BGP_ENCAP_TYPE_TRANSMIT_TUNNEL_ENDPOINT:
+ _RTTO_MAYBE_ADD_ENDPOINT_ADDRESS(transmit_tunnel_endpoint);
+ bgp_encap_type_transmit_tunnel_endpoint(
+ &tto->bgpinfo.transmit_tunnel_endpoint, attr);
+ break;
+
+ case BGP_ENCAP_TYPE_IPSEC_IN_TUNNEL_MODE:
+ _RTTO_MAYBE_ADD_ENDPOINT_ADDRESS(ipsec_tunnel);
+ bgp_encap_type_ipsec_in_tunnel_mode_to_tlv(
+ &tto->bgpinfo.ipsec_tunnel, attr);
+ break;
+
+ case BGP_ENCAP_TYPE_IP_IN_IP_TUNNEL_WITH_IPSEC_TRANSPORT_MODE:
+ _RTTO_MAYBE_ADD_ENDPOINT_ADDRESS(ip_ipsec);
+ bgp_encap_type_ip_in_ip_tunnel_with_ipsec_transport_mode_to_tlv(
+ &tto->bgpinfo.ip_ipsec, attr);
+ break;
+
+ case BGP_ENCAP_TYPE_MPLS_IN_IP_TUNNEL_WITH_IPSEC_TRANSPORT_MODE:
+ _RTTO_MAYBE_ADD_ENDPOINT_ADDRESS(mpls_ipsec);
+ bgp_encap_type_mpls_in_ip_tunnel_with_ipsec_transport_mode_to_tlv(
+ &tto->bgpinfo.mpls_ipsec, attr);
+ break;
+
+ case BGP_ENCAP_TYPE_IP_IN_IP:
+ _RTTO_MAYBE_ADD_ENDPOINT_ADDRESS(ip_ip);
+ bgp_encap_type_ip_in_ip_to_tlv(&tto->bgpinfo.ip_ip, attr);
+ break;
+
+ case BGP_ENCAP_TYPE_VXLAN:
+ _RTTO_MAYBE_ADD_ENDPOINT_ADDRESS(vxlan);
+ bgp_encap_type_vxlan_to_tlv(&tto->bgpinfo.vxlan, attr);
+ break;
+
+ case BGP_ENCAP_TYPE_NVGRE:
+ _RTTO_MAYBE_ADD_ENDPOINT_ADDRESS(nvgre);
+ bgp_encap_type_nvgre_to_tlv(&tto->bgpinfo.nvgre, attr);
+ break;
+
+ case BGP_ENCAP_TYPE_MPLS:
+ /* nothing to do for MPLS */
+ break;
+
+ case BGP_ENCAP_TYPE_MPLS_IN_GRE:
+ _RTTO_MAYBE_ADD_ENDPOINT_ADDRESS(mpls_gre);
+ bgp_encap_type_mpls_in_gre_to_tlv(&tto->bgpinfo.mpls_gre, attr);
+ break;
+
+ case BGP_ENCAP_TYPE_VXLAN_GPE:
+ _RTTO_MAYBE_ADD_ENDPOINT_ADDRESS(vxlan_gpe);
+ bgp_encap_type_vxlan_gpe_to_tlv(&tto->bgpinfo.vxlan_gpe, attr);
+ break;
+
+ case BGP_ENCAP_TYPE_MPLS_IN_UDP:
+ _RTTO_MAYBE_ADD_ENDPOINT_ADDRESS(mpls_udp);
+ bgp_encap_type_mpls_in_udp_to_tlv(&tto->bgpinfo.mpls_udp, attr);
+ break;
+
+ case BGP_ENCAP_TYPE_PBB:
+ _RTTO_MAYBE_ADD_ENDPOINT_ADDRESS(pbb);
+ bgp_encap_type_pbb_to_tlv(&tto->bgpinfo.pbb, attr);
+ break;
+
+ default:
+ assert(0);
+ }
+ return tto->type;
+}
+
+struct rfapi_un_option *rfapi_encap_tlv_to_un_option(struct attr *attr)
+{
+ struct rfapi_un_option *uo = NULL;
+ struct rfapi_tunneltype_option *tto;
+ int rc;
+ struct bgp_attr_encap_subtlv *stlv;
+
+ /* no tunnel encap attr stored */
+ if (!attr->encap_tunneltype)
+ return NULL;
+
+ stlv = attr->encap_subtlvs;
+
+ uo = XCALLOC(MTYPE_RFAPI_UN_OPTION, sizeof(struct rfapi_un_option));
+ uo->type = RFAPI_UN_OPTION_TYPE_TUNNELTYPE;
+ uo->v.tunnel.type = attr->encap_tunneltype;
+ tto = &uo->v.tunnel;
+
+ switch (attr->encap_tunneltype) {
+ case BGP_ENCAP_TYPE_L2TPV3_OVER_IP:
+ rc = tlv_to_bgp_encap_type_l2tpv3overip(
+ stlv, &tto->bgpinfo.l2tpv3_ip);
+ break;
+
+ case BGP_ENCAP_TYPE_GRE:
+ rc = tlv_to_bgp_encap_type_gre(stlv, &tto->bgpinfo.gre);
+ break;
+
+ case BGP_ENCAP_TYPE_TRANSMIT_TUNNEL_ENDPOINT:
+ rc = tlv_to_bgp_encap_type_transmit_tunnel_endpoint(
+ stlv, &tto->bgpinfo.transmit_tunnel_endpoint);
+ break;
+
+ case BGP_ENCAP_TYPE_IPSEC_IN_TUNNEL_MODE:
+ rc = tlv_to_bgp_encap_type_ipsec_in_tunnel_mode(
+ stlv, &tto->bgpinfo.ipsec_tunnel);
+ break;
+
+ case BGP_ENCAP_TYPE_IP_IN_IP_TUNNEL_WITH_IPSEC_TRANSPORT_MODE:
+ rc = tlv_to_bgp_encap_type_ip_in_ip_tunnel_with_ipsec_transport_mode(
+ stlv, &tto->bgpinfo.ip_ipsec);
+ break;
+
+ case BGP_ENCAP_TYPE_MPLS_IN_IP_TUNNEL_WITH_IPSEC_TRANSPORT_MODE:
+ rc = tlv_to_bgp_encap_type_mpls_in_ip_tunnel_with_ipsec_transport_mode(
+ stlv, &tto->bgpinfo.mpls_ipsec);
+ break;
+
+ case BGP_ENCAP_TYPE_IP_IN_IP:
+ rc = tlv_to_bgp_encap_type_ip_in_ip(stlv, &tto->bgpinfo.ip_ip);
+ break;
+
+ case BGP_ENCAP_TYPE_VXLAN:
+ rc = tlv_to_bgp_encap_type_vxlan(stlv, &tto->bgpinfo.vxlan);
+ break;
+
+ case BGP_ENCAP_TYPE_NVGRE:
+ rc = tlv_to_bgp_encap_type_nvgre(stlv, &tto->bgpinfo.nvgre);
+ break;
+
+ case BGP_ENCAP_TYPE_MPLS:
+ rc = tlv_to_bgp_encap_type_mpls(stlv, &tto->bgpinfo.mpls);
+ break;
+
+ case BGP_ENCAP_TYPE_MPLS_IN_GRE:
+ rc = tlv_to_bgp_encap_type_mpls_in_gre(stlv,
+ &tto->bgpinfo.mpls_gre);
+ break;
+
+ case BGP_ENCAP_TYPE_VXLAN_GPE:
+ rc = tlv_to_bgp_encap_type_vxlan_gpe(stlv,
+ &tto->bgpinfo.vxlan_gpe);
+ break;
+
+ case BGP_ENCAP_TYPE_MPLS_IN_UDP:
+ rc = tlv_to_bgp_encap_type_mpls_in_udp(stlv,
+ &tto->bgpinfo.mpls_udp);
+ break;
+
+ case BGP_ENCAP_TYPE_PBB:
+ rc = tlv_to_bgp_encap_type_pbb(stlv, &tto->bgpinfo.pbb);
+ break;
+
+ default:
+ vnc_zlog_debug_verbose("%s: unknown tunnel type %d", __func__,
+ attr->encap_tunneltype);
+ rc = -1;
+ break;
+ }
+ if (rc) {
+ XFREE(MTYPE_RFAPI_UN_OPTION, uo);
+ }
+ return uo;
+}
+
+/***********************************************************************
+ * SUBTLV PRINT
+ ***********************************************************************/
+
+static void subtlv_print_encap_l2tpv3_over_ip(
+ void *stream, int column_offset,
+ struct bgp_tea_subtlv_encap_l2tpv3_over_ip *st)
+{
+ int (*fp)(void *, const char *, ...);
+ struct vty *vty;
+ void *out;
+ const char *vty_newline;
+
+ if (rfapiStream2Vty(stream, &fp, &vty, &out, &vty_newline) == 0)
+ return;
+ if (!st)
+ return;
+
+ fp(out, "%*s%s%s", column_offset, "", "SubTLV: Encap(L2TPv3 over IP)",
+ vty_newline);
+ fp(out, "%*s SessionID: %d%s", column_offset, "", st->sessionid,
+ vty_newline);
+ fp(out, "%*s Cookie: (length %d)%s", column_offset, "",
+ st->cookie_length, vty_newline);
+}
+
+static void subtlv_print_encap_gre(void *stream, int column_offset,
+ struct bgp_tea_subtlv_encap_gre_key *st)
+{
+ int (*fp)(void *, const char *, ...);
+ struct vty *vty;
+ void *out;
+ const char *vty_newline;
+
+ if (rfapiStream2Vty(stream, &fp, &vty, &out, &vty_newline) == 0)
+ return;
+ if (!st)
+ return;
+
+ fp(out, "%*s%s%s", column_offset, "", "SubTLV: Encap(GRE)",
+ vty_newline);
+ fp(out, "%*s GRE key: %d (0x%x)%s", column_offset, "", st->gre_key,
+ st->gre_key, vty_newline);
+}
+
+static void subtlv_print_encap_pbb(void *stream, int column_offset,
+ struct bgp_tea_subtlv_encap_pbb *st)
+{
+ int (*fp)(void *, const char *, ...);
+ struct vty *vty;
+ void *out;
+ const char *vty_newline;
+
+ if (rfapiStream2Vty(stream, &fp, &vty, &out, &vty_newline) == 0)
+ return;
+ if (!st)
+ return;
+
+ fp(out, "%*s%s%s", column_offset, "", "SubTLV: Encap(PBB)",
+ vty_newline);
+ if (st->flag_isid) {
+ fp(out, "%*s ISID: %d (0x%x)%s", column_offset, "", st->isid,
+ st->isid, vty_newline);
+ }
+ if (st->flag_vid) {
+ fp(out, "%*s VID: %d (0x%x)%s", column_offset, "", st->vid,
+ st->vid, vty_newline);
+ }
+ fp(out, "%*s MACADDR %02x:%02x:%02x:%02x:%02x:%02x%s", column_offset,
+ "", st->macaddr[0], st->macaddr[1], st->macaddr[2], st->macaddr[3],
+ st->macaddr[4], st->macaddr[5], vty_newline);
+}
+
+static void subtlv_print_proto_type(void *stream, int column_offset,
+ struct bgp_tea_subtlv_proto_type *st)
+{
+ int (*fp)(void *, const char *, ...);
+ struct vty *vty;
+ void *out;
+ const char *vty_newline;
+
+ if (rfapiStream2Vty(stream, &fp, &vty, &out, &vty_newline) == 0)
+ return;
+ if (!st)
+ return;
+
+ fp(out, "%*s%s%s", column_offset, "", "SubTLV: Encap(Proto Type)",
+ vty_newline);
+ fp(out, "%*s Proto %d (0x%x)%s", column_offset, "", st->proto,
+ st->proto, vty_newline);
+}
+
+static void subtlv_print_color(void *stream, int column_offset,
+ struct bgp_tea_subtlv_color *st)
+{
+ int (*fp)(void *, const char *, ...);
+ struct vty *vty;
+ void *out;
+ const char *vty_newline;
+
+ if (rfapiStream2Vty(stream, &fp, &vty, &out, &vty_newline) == 0)
+ return;
+ if (!st)
+ return;
+
+ fp(out, "%*s%s%s", column_offset, "", "SubTLV: Color", vty_newline);
+ fp(out, "%*s Color: %d (0x%x)", column_offset, "", st->color,
+ st->color, vty_newline);
+}
+
+static void subtlv_print_ipsec_ta(void *stream, int column_offset,
+ struct bgp_tea_subtlv_ipsec_ta *st)
+{
+ int (*fp)(void *, const char *, ...);
+ struct vty *vty;
+ void *out;
+ const char *vty_newline;
+
+ if (rfapiStream2Vty(stream, &fp, &vty, &out, &vty_newline) == 0)
+ return;
+ if (!st)
+ return;
+
+ fp(out, "%*s%s%s", column_offset, "", "SubTLV: IPSEC TA", vty_newline);
+ fp(out, "%*s Authenticator Type: %d (0x%x)", column_offset, "",
+ st->authenticator_type, st->authenticator_type, vty_newline);
+ fp(out, "%*s Authenticator: (length %d)", column_offset, "",
+ st->authenticator_length, vty_newline);
+}
+
+/***********************************************************************
+ * TLV PRINT
+ ***********************************************************************/
+
+static void
+print_encap_type_l2tpv3overip(void *stream, int column_offset,
+ struct bgp_encap_type_l2tpv3_over_ip *bet)
+{
+ const char *type = "L2TPv3 over IP";
+ int (*fp)(void *, const char *, ...);
+ struct vty *vty;
+ void *out;
+ const char *vty_newline;
+
+ if (rfapiStream2Vty(stream, &fp, &vty, &out, &vty_newline) == 0)
+ return;
+ if (!bet)
+ return;
+
+ fp(out, "%*sTEA type %s%s", column_offset, "", type, vty_newline);
+
+ subtlv_print_encap_l2tpv3_over_ip(stream, column_offset + 2,
+ &bet->st_encap);
+ subtlv_print_proto_type(stream, column_offset + 2, &bet->st_proto);
+ subtlv_print_color(stream, column_offset + 2, &bet->st_color);
+}
+
+static void print_encap_type_gre(void *stream, int column_offset,
+ struct bgp_encap_type_gre *bet)
+{
+ const char *type = "GRE";
+ int (*fp)(void *, const char *, ...);
+ struct vty *vty;
+ void *out;
+ const char *vty_newline;
+
+ if (rfapiStream2Vty(stream, &fp, &vty, &out, &vty_newline) == 0)
+ return;
+ if (!bet)
+ return;
+
+ fp(out, "%*sTEA type %s%s", column_offset, "", type, vty_newline);
+
+ subtlv_print_encap_gre(stream, column_offset + 2, &bet->st_encap);
+ subtlv_print_proto_type(stream, column_offset + 2, &bet->st_proto);
+ subtlv_print_color(stream, column_offset + 2, &bet->st_color);
+}
+
+static void print_encap_type_ip_in_ip(void *stream, int column_offset,
+ struct bgp_encap_type_ip_in_ip *bet)
+{
+ const char *type = "IP in IP";
+ int (*fp)(void *, const char *, ...);
+ struct vty *vty;
+ void *out;
+ const char *vty_newline;
+
+ if (rfapiStream2Vty(stream, &fp, &vty, &out, &vty_newline) == 0)
+ return;
+ if (!bet)
+ return;
+
+ fp(out, "%*sTEA type %s%s", column_offset, "", type, vty_newline);
+
+ subtlv_print_proto_type(stream, column_offset + 2, &bet->st_proto);
+ subtlv_print_color(stream, column_offset + 2, &bet->st_color);
+}
+
+static void print_encap_type_transmit_tunnel_endpoint(
+ void *stream, int column_offset,
+ struct bgp_encap_type_transmit_tunnel_endpoint *bet)
+{
+ const char *type = "Transmit Tunnel Endpoint";
+ int (*fp)(void *, const char *, ...);
+ struct vty *vty;
+ void *out;
+ const char *vty_newline;
+
+ if (rfapiStream2Vty(stream, &fp, &vty, &out, &vty_newline) == 0)
+ return;
+ if (!bet)
+ return;
+
+ fp(out, "%*sTEA type %s%s", column_offset, "", type, vty_newline);
+
+ /* no subtlvs for this type */
+}
+
+static void print_encap_type_ipsec_in_tunnel_mode(
+ void *stream, int column_offset,
+ struct bgp_encap_type_ipsec_in_tunnel_mode *bet)
+{
+ const char *type = "IPSEC in Tunnel mode";
+ int (*fp)(void *, const char *, ...);
+ struct vty *vty;
+ void *out;
+ const char *vty_newline;
+
+ if (rfapiStream2Vty(stream, &fp, &vty, &out, &vty_newline) == 0)
+ return;
+ if (!bet)
+ return;
+
+ fp(out, "%*sTEA type %s%s", column_offset, "", type, vty_newline);
+ subtlv_print_ipsec_ta(stream, column_offset + 2, &bet->st_ipsec_ta);
+}
+
+static void print_encap_type_ip_in_ip_tunnel_with_ipsec_transport_mode(
+ void *stream, int column_offset,
+ struct bgp_encap_type_ip_in_ip_tunnel_with_ipsec_transport_mode *bet)
+{
+ const char *type = "IP in IP Tunnel with IPSEC transport mode";
+ int (*fp)(void *, const char *, ...);
+ struct vty *vty;
+ void *out;
+ const char *vty_newline;
+
+ if (rfapiStream2Vty(stream, &fp, &vty, &out, &vty_newline) == 0)
+ return;
+ if (!bet)
+ return;
+
+ fp(out, "%*sTEA type %s%s", column_offset, "", type, vty_newline);
+
+ subtlv_print_ipsec_ta(stream, column_offset + 2, &bet->st_ipsec_ta);
+}
+
+static void print_encap_type_mpls_in_ip_tunnel_with_ipsec_transport_mode(
+ void *stream, int column_offset,
+ struct bgp_encap_type_mpls_in_ip_tunnel_with_ipsec_transport_mode *bet)
+{
+ const char *type = "MPLS in IP Tunnel with IPSEC transport mode";
+ int (*fp)(void *, const char *, ...);
+ struct vty *vty;
+ void *out;
+ const char *vty_newline;
+
+ if (rfapiStream2Vty(stream, &fp, &vty, &out, &vty_newline) == 0)
+ return;
+ if (!bet)
+ return;
+
+ fp(out, "%*sTEA type %s%s", column_offset, "", type, vty_newline);
+
+ subtlv_print_ipsec_ta(stream, column_offset + 2, &bet->st_ipsec_ta);
+}
+
+
+static void print_encap_type_pbb(void *stream, int column_offset,
+ struct bgp_encap_type_pbb *bet)
+{
+ const char *type = "PBB";
+ int (*fp)(void *, const char *, ...);
+ struct vty *vty;
+ void *out;
+ const char *vty_newline;
+
+ if (rfapiStream2Vty(stream, &fp, &vty, &out, &vty_newline) == 0)
+ return;
+ if (!bet)
+ return;
+
+ fp(out, "%*sTEA type %s%s", column_offset, "", type, vty_newline);
+
+ subtlv_print_encap_pbb(stream, column_offset + 2, &bet->st_encap);
+}
+
+
+static void print_encap_type_vxlan(void *stream, int column_offset,
+ struct bgp_encap_type_vxlan *bet)
+{
+ const char *type = "VXLAN";
+ int (*fp)(void *, const char *, ...);
+ struct vty *vty;
+ void *out;
+ const char *vty_newline;
+
+ if (rfapiStream2Vty(stream, &fp, &vty, &out, &vty_newline) == 0)
+ return;
+ if (!bet)
+ return;
+
+ fp(out, "%*sTEA type %s%s", column_offset, "", type, vty_newline);
+
+ /* no subtlvs for this type */
+}
+
+
+static void print_encap_type_nvgre(void *stream, int column_offset,
+ struct bgp_encap_type_nvgre *bet)
+{
+ const char *type = "NVGRE";
+ int (*fp)(void *, const char *, ...);
+ struct vty *vty;
+ void *out;
+ const char *vty_newline;
+
+ if (rfapiStream2Vty(stream, &fp, &vty, &out, &vty_newline) == 0)
+ return;
+ if (!bet)
+ return;
+
+ fp(out, "%*sTEA type %s%s", column_offset, "", type, vty_newline);
+
+ /* no subtlvs for this type */
+}
+
+static void print_encap_type_mpls(void *stream, int column_offset,
+ struct bgp_encap_type_mpls *bet)
+{
+ const char *type = "MPLS";
+ int (*fp)(void *, const char *, ...);
+ struct vty *vty;
+ void *out;
+ const char *vty_newline;
+
+ if (rfapiStream2Vty(stream, &fp, &vty, &out, &vty_newline) == 0)
+ return;
+ if (!bet)
+ return;
+
+ fp(out, "%*sTEA type %s%s", column_offset, "", type, vty_newline);
+
+ /* no subtlvs for this type */
+}
+
+static void print_encap_type_mpls_in_gre(void *stream, int column_offset,
+ struct bgp_encap_type_mpls_in_gre *bet)
+{
+ const char *type = "MPLS in GRE";
+ int (*fp)(void *, const char *, ...);
+ struct vty *vty;
+ void *out;
+ const char *vty_newline;
+
+ if (rfapiStream2Vty(stream, &fp, &vty, &out, &vty_newline) == 0)
+ return;
+ if (!bet)
+ return;
+
+ fp(out, "%*sTEA type %s%s", column_offset, "", type, vty_newline);
+
+ /* no subtlvs for this type */
+}
+
+static void print_encap_type_vxlan_gpe(void *stream, int column_offset,
+ struct bgp_encap_type_vxlan_gpe *bet)
+{
+ const char *type = "VXLAN GPE";
+ int (*fp)(void *, const char *, ...);
+ struct vty *vty;
+ void *out;
+ const char *vty_newline;
+
+ if (rfapiStream2Vty(stream, &fp, &vty, &out, &vty_newline) == 0)
+ return;
+ if (!bet)
+ return;
+
+ fp(out, "%*sTEA type %s%s", column_offset, "", type, vty_newline);
+
+ /* no subtlvs for this type */
+}
+
+static void print_encap_type_mpls_in_udp(void *stream, int column_offset,
+ struct bgp_encap_type_mpls_in_udp *bet)
+{
+ const char *type = "MPLS in UDP";
+ int (*fp)(void *, const char *, ...);
+ struct vty *vty;
+ void *out;
+ const char *vty_newline;
+
+ if (rfapiStream2Vty(stream, &fp, &vty, &out, &vty_newline) == 0)
+ return;
+ if (!bet)
+ return;
+
+ fp(out, "%*sTEA type %s%s", column_offset, "", type, vty_newline);
+
+ /* no subtlvs for this type */
+}
+
+void rfapi_print_tunneltype_option(void *stream, int column_offset,
+ struct rfapi_tunneltype_option *tto)
+{
+ switch (tto->type) {
+ case BGP_ENCAP_TYPE_L2TPV3_OVER_IP:
+ print_encap_type_l2tpv3overip(stream, column_offset,
+ &tto->bgpinfo.l2tpv3_ip);
+ break;
+
+ case BGP_ENCAP_TYPE_GRE:
+ print_encap_type_gre(stream, column_offset, &tto->bgpinfo.gre);
+ break;
+
+ case BGP_ENCAP_TYPE_TRANSMIT_TUNNEL_ENDPOINT:
+ print_encap_type_transmit_tunnel_endpoint(
+ stream, column_offset,
+ &tto->bgpinfo.transmit_tunnel_endpoint);
+ break;
+
+ case BGP_ENCAP_TYPE_IPSEC_IN_TUNNEL_MODE:
+ print_encap_type_ipsec_in_tunnel_mode(
+ stream, column_offset, &tto->bgpinfo.ipsec_tunnel);
+ break;
+
+ case BGP_ENCAP_TYPE_IP_IN_IP_TUNNEL_WITH_IPSEC_TRANSPORT_MODE:
+ print_encap_type_ip_in_ip_tunnel_with_ipsec_transport_mode(
+ stream, column_offset, &tto->bgpinfo.ip_ipsec);
+ break;
+
+ case BGP_ENCAP_TYPE_MPLS_IN_IP_TUNNEL_WITH_IPSEC_TRANSPORT_MODE:
+ print_encap_type_mpls_in_ip_tunnel_with_ipsec_transport_mode(
+ stream, column_offset, &tto->bgpinfo.mpls_ipsec);
+ break;
+
+ case BGP_ENCAP_TYPE_IP_IN_IP:
+ print_encap_type_ip_in_ip(stream, column_offset,
+ &tto->bgpinfo.ip_ip);
+ break;
+
+ case BGP_ENCAP_TYPE_VXLAN:
+ print_encap_type_vxlan(stream, column_offset,
+ &tto->bgpinfo.vxlan);
+ break;
+
+ case BGP_ENCAP_TYPE_NVGRE:
+ print_encap_type_nvgre(stream, column_offset,
+ &tto->bgpinfo.nvgre);
+ break;
+
+ case BGP_ENCAP_TYPE_MPLS:
+ print_encap_type_mpls(stream, column_offset,
+ &tto->bgpinfo.mpls);
+ break;
+
+ case BGP_ENCAP_TYPE_MPLS_IN_GRE:
+ print_encap_type_mpls_in_gre(stream, column_offset,
+ &tto->bgpinfo.mpls_gre);
+ break;
+
+ case BGP_ENCAP_TYPE_VXLAN_GPE:
+ print_encap_type_vxlan_gpe(stream, column_offset,
+ &tto->bgpinfo.vxlan_gpe);
+ break;
+
+ case BGP_ENCAP_TYPE_MPLS_IN_UDP:
+ print_encap_type_mpls_in_udp(stream, column_offset,
+ &tto->bgpinfo.mpls_udp);
+ break;
+
+ case BGP_ENCAP_TYPE_PBB:
+ print_encap_type_pbb(stream, column_offset, &tto->bgpinfo.pbb);
+ break;
+
+ default:
+ assert(0);
+ }
+}
diff --git a/bgpd/rfapi/rfapi_encap_tlv.h b/bgpd/rfapi/rfapi_encap_tlv.h
new file mode 100644
index 0000000..57e1b5e
--- /dev/null
+++ b/bgpd/rfapi/rfapi_encap_tlv.h
@@ -0,0 +1,35 @@
+/*
+ * Copyright 2015-2016, LabN Consulting, L.L.C.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program; see the file COPYING; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
+ */
+
+#ifndef _QUAGGA_BGP_RFAPI_ENCAP_TLV_H
+#define _QUAGGA_BGP_RFAPI_ENCAP_TLV_H
+
+#define RFAPI_BGP_ENCAP_TYPE_DEFAULT BGP_ENCAP_TYPE_IP_IN_IP
+
+extern bgp_encap_types
+rfapi_tunneltype_option_to_tlv(struct bgp *bgp, struct rfapi_ip_addr *ea,
+ struct rfapi_tunneltype_option *tto,
+ struct attr *attr, int always_add);
+
+extern struct rfapi_un_option *rfapi_encap_tlv_to_un_option(struct attr *attr);
+
+extern void rfapi_print_tunneltype_option(void *stream, int column_offset,
+ struct rfapi_tunneltype_option *tto);
+
+
+#endif /* _QUAGGA_BGP_RFAPI_ENCAP_TLV_H */
diff --git a/bgpd/rfapi/rfapi_import.c b/bgpd/rfapi/rfapi_import.c
new file mode 100644
index 0000000..e897cf6
--- /dev/null
+++ b/bgpd/rfapi/rfapi_import.c
@@ -0,0 +1,4810 @@
+/*
+*
+* Copyright 2009-2016, LabN Consulting, L.L.C.
+*
+*
+* This program is free software; you can redistribute it and/or
+* modify it under the terms of the GNU General Public License
+* as published by the Free Software Foundation; either version 2
+* of the License, or (at your option) any later version.
+*
+* This program is distributed in the hope that it will be useful,
+* but WITHOUT ANY WARRANTY; without even the implied warranty of
+* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+* GNU General Public License for more details.
+*
+* You should have received a copy of the GNU General Public License along
+* with this program; see the file COPYING; if not, write to the Free Software
+* Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
+*/
+
+/*
+ * File: rfapi_import.c
+ * Purpose: Handle import of routes from BGP to RFAPI
+ */
+
+#include "lib/zebra.h"
+#include "lib/prefix.h"
+#include "lib/agg_table.h"
+#include "lib/vty.h"
+#include "lib/memory.h"
+#include "lib/log.h"
+#include "lib/skiplist.h"
+#include "lib/thread.h"
+#include "lib/stream.h"
+#include "lib/lib_errors.h"
+
+#include "bgpd/bgpd.h"
+#include "bgpd/bgp_ecommunity.h"
+#include "bgpd/bgp_attr.h"
+#include "bgpd/bgp_route.h"
+#include "bgpd/bgp_mplsvpn.h" /* prefix_rd2str() */
+#include "bgpd/bgp_vnc_types.h"
+#include "bgpd/bgp_rd.h"
+
+#include "bgpd/rfapi/rfapi.h"
+#include "bgpd/rfapi/bgp_rfapi_cfg.h"
+#include "bgpd/rfapi/rfapi_backend.h"
+#include "bgpd/rfapi/rfapi_import.h"
+#include "bgpd/rfapi/rfapi_private.h"
+#include "bgpd/rfapi/rfapi_monitor.h"
+#include "bgpd/rfapi/rfapi_nve_addr.h"
+#include "bgpd/rfapi/rfapi_vty.h"
+#include "bgpd/rfapi/vnc_export_bgp.h"
+#include "bgpd/rfapi/vnc_export_bgp_p.h"
+#include "bgpd/rfapi/vnc_zebra.h"
+#include "bgpd/rfapi/vnc_import_bgp.h"
+#include "bgpd/rfapi/vnc_import_bgp_p.h"
+#include "bgpd/rfapi/rfapi_rib.h"
+#include "bgpd/rfapi/rfapi_encap_tlv.h"
+#include "bgpd/rfapi/vnc_debug.h"
+
+#ifdef HAVE_GLIBC_BACKTRACE
+/* for backtrace and friends */
+#include <execinfo.h>
+#endif /* HAVE_GLIBC_BACKTRACE */
+
+#undef DEBUG_MONITOR_MOVE_SHORTER
+#undef DEBUG_RETURNED_NHL
+#undef DEBUG_ROUTE_COUNTERS
+#undef DEBUG_ENCAP_MONITOR
+#undef DEBUG_L2_EXTRA
+#undef DEBUG_IT_NODES
+#undef DEBUG_BI_SEARCH
+
+/*
+ * Allocated for each withdraw timer instance; freed when the timer
+ * expires or is canceled
+ */
+struct rfapi_withdraw {
+ struct rfapi_import_table *import_table;
+ struct agg_node *node;
+ struct bgp_path_info *info;
+ safi_t safi; /* used only for bulk operations */
+ /*
+ * For import table node reference count checking (i.e., debugging).
+ * Normally when a timer expires, lockoffset should be 0. However, if
+ * the timer expiration function is called directly (e.g.,
+ * rfapiExpireVpnNow), the node could be locked by a preceding
+ * agg_route_top() or agg_route_next() in a loop, so we need to pass
+ * this value in.
+ */
+ int lockoffset;
+};
+
+/*
+ * DEBUG FUNCTION
+ * It's evil and fiendish. It's compiler-dependent.
+ * ? Might need LDFLAGS -rdynamic to produce all function names
+ */
+void rfapiDebugBacktrace(void)
+{
+#ifdef HAVE_GLIBC_BACKTRACE
+#define RFAPI_DEBUG_BACKTRACE_NENTRIES 200
+ void *buf[RFAPI_DEBUG_BACKTRACE_NENTRIES];
+ char **syms;
+ size_t i;
+ size_t size;
+
+ size = backtrace(buf, RFAPI_DEBUG_BACKTRACE_NENTRIES);
+ syms = backtrace_symbols(buf, size);
+
+ for (i = 0; i < size && i < RFAPI_DEBUG_BACKTRACE_NENTRIES; ++i) {
+ vnc_zlog_debug_verbose("backtrace[%2zu]: %s", i, syms[i]);
+ }
+
+ free(syms);
+#else
+#endif
+}
+
+/*
+ * DEBUG FUNCTION
+ * Count remote routes and compare with actively-maintained values.
+ * Abort if they disagree.
+ */
+void rfapiCheckRouteCount(void)
+{
+ struct bgp *bgp = bgp_get_default();
+ struct rfapi *h;
+ struct rfapi_import_table *it;
+ afi_t afi;
+
+ assert(bgp);
+
+ h = bgp->rfapi;
+ assert(h);
+
+ for (it = h->imports; it; it = it->next) {
+ for (afi = AFI_IP; afi < AFI_MAX; ++afi) {
+
+ struct agg_table *rt;
+ struct agg_node *rn;
+
+ int holddown_count = 0;
+ int local_count = 0;
+ int imported_count = 0;
+ int remote_count = 0;
+
+ rt = it->imported_vpn[afi];
+
+ for (rn = agg_route_top(rt); rn;
+ rn = agg_route_next(rn)) {
+ struct bgp_path_info *bpi;
+ struct bgp_path_info *next;
+
+ for (bpi = rn->info; bpi; bpi = next) {
+ next = bpi->next;
+
+ if (CHECK_FLAG(bpi->flags,
+ BGP_PATH_REMOVED)) {
+ ++holddown_count;
+
+ } else {
+ if (RFAPI_LOCAL_BI(bpi)) {
+ ++local_count;
+ } else {
+ if (RFAPI_DIRECT_IMPORT_BI(
+ bpi)) {
+ ++imported_count;
+ } else {
+ ++remote_count;
+ }
+ }
+ }
+ }
+ }
+
+ if (it->holddown_count[afi] != holddown_count) {
+ vnc_zlog_debug_verbose(
+ "%s: it->holddown_count %d != holddown_count %d",
+ __func__, it->holddown_count[afi],
+ holddown_count);
+ assert(0);
+ }
+ if (it->remote_count[afi] != remote_count) {
+ vnc_zlog_debug_verbose(
+ "%s: it->remote_count %d != remote_count %d",
+ __func__, it->remote_count[afi],
+ remote_count);
+ assert(0);
+ }
+ if (it->imported_count[afi] != imported_count) {
+ vnc_zlog_debug_verbose(
+ "%s: it->imported_count %d != imported_count %d",
+ __func__, it->imported_count[afi],
+ imported_count);
+ assert(0);
+ }
+ }
+ }
+}
+
+#ifdef DEBUG_ROUTE_COUNTERS
+#define VNC_ITRCCK do {rfapiCheckRouteCount();} while (0)
+#else
+#define VNC_ITRCCK
+#endif
+
+/*
+ * Validate reference count for a node in an import table
+ *
+ * Normally lockoffset is 0 for nodes in quiescent state. However,
+ * agg_unlock_node will delete the node if it is called when
+ * node->lock == 1, and we have to validate the refcount before
+ * the node is deleted. In this case, we specify lockoffset 1.
+ */
+void rfapiCheckRefcount(struct agg_node *rn, safi_t safi, int lockoffset)
+{
+ unsigned int count_bpi = 0;
+ unsigned int count_monitor = 0;
+ struct bgp_path_info *bpi;
+ struct rfapi_monitor_encap *hme;
+ struct rfapi_monitor_vpn *hmv;
+
+ for (bpi = rn->info; bpi; bpi = bpi->next)
+ ++count_bpi;
+
+
+ if (rn->aggregate) {
+ ++count_monitor; /* rfapi_it_extra */
+
+ switch (safi) {
+ void *cursor;
+ int rc;
+
+ case SAFI_ENCAP:
+ for (hme = RFAPI_MONITOR_ENCAP(rn); hme;
+ hme = hme->next)
+ ++count_monitor;
+ break;
+
+ case SAFI_MPLS_VPN:
+
+ for (hmv = RFAPI_MONITOR_VPN(rn); hmv; hmv = hmv->next)
+ ++count_monitor;
+
+ if (RFAPI_MONITOR_EXTERIOR(rn)->source) {
+ ++count_monitor; /* sl */
+ cursor = NULL;
+ for (rc = skiplist_next(
+ RFAPI_MONITOR_EXTERIOR(rn)->source,
+ NULL, NULL, &cursor);
+ !rc;
+ rc = skiplist_next(
+ RFAPI_MONITOR_EXTERIOR(rn)->source,
+ NULL, NULL, &cursor)) {
+
+ ++count_monitor; /* sl entry */
+ }
+ }
+ break;
+
+ default:
+ assert(0);
+ }
+ }
+
+ if (count_bpi + count_monitor + lockoffset
+ != agg_node_get_lock_count(rn)) {
+ vnc_zlog_debug_verbose(
+ "%s: count_bpi=%d, count_monitor=%d, lockoffset=%d, rn->lock=%d",
+ __func__, count_bpi, count_monitor, lockoffset,
+ agg_node_get_lock_count(rn));
+ assert(0);
+ }
+}
+
+/*
+ * Perform deferred rfapi_close operations that were queued
+ * during callbacks.
+ */
+static wq_item_status rfapi_deferred_close_workfunc(struct work_queue *q,
+ void *data)
+{
+ struct rfapi_descriptor *rfd = data;
+ struct rfapi *h = q->spec.data;
+
+ assert(!(h->flags & RFAPI_INCALLBACK));
+ rfapi_close(rfd);
+ vnc_zlog_debug_verbose("%s: completed deferred close on handle %p",
+ __func__, rfd);
+ return WQ_SUCCESS;
+}
+
+/*
+ * Extract layer 2 option from Encap TLVS in BGP attrs
+ */
+int rfapiGetL2o(struct attr *attr, struct rfapi_l2address_option *l2o)
+{
+ if (attr) {
+ struct bgp_attr_encap_subtlv *pEncap;
+
+ for (pEncap = bgp_attr_get_vnc_subtlvs(attr); pEncap;
+ pEncap = pEncap->next) {
+
+ if (pEncap->type == BGP_VNC_SUBTLV_TYPE_RFPOPTION) {
+ if (pEncap->value[0]
+ == RFAPI_VN_OPTION_TYPE_L2ADDR) {
+
+ if (pEncap->value[1] == 14) {
+ memcpy(l2o->macaddr.octet,
+ pEncap->value + 2,
+ ETH_ALEN);
+ l2o->label =
+ ((pEncap->value[10]
+ >> 4)
+ & 0x0f)
+ + ((pEncap->value[9]
+ << 4)
+ & 0xff0)
+ + ((pEncap->value[8]
+ << 12)
+ & 0xff000);
+
+ l2o->local_nve_id =
+ pEncap->value[12];
+
+ l2o->logical_net_id =
+ (pEncap->value[15]
+ & 0xff)
+ + ((pEncap->value[14]
+ << 8)
+ & 0xff00)
+ + ((pEncap->value[13]
+ << 16)
+ & 0xff0000);
+ }
+
+ return 0;
+ }
+ }
+ }
+ }
+
+ return ENOENT;
+}
+
+/*
+ * Extract the lifetime from the Tunnel Encap attribute of a route in
+ * an import table
+ */
+int rfapiGetVncLifetime(struct attr *attr, uint32_t *lifetime)
+{
+ struct bgp_attr_encap_subtlv *pEncap;
+
+ *lifetime = RFAPI_INFINITE_LIFETIME; /* default to infinite */
+
+ if (attr) {
+
+ for (pEncap = bgp_attr_get_vnc_subtlvs(attr); pEncap;
+ pEncap = pEncap->next) {
+
+ if (pEncap->type
+ == BGP_VNC_SUBTLV_TYPE_LIFETIME) { /* lifetime */
+ if (pEncap->length == 4) {
+ memcpy(lifetime, pEncap->value, 4);
+ *lifetime = ntohl(*lifetime);
+ return 0;
+ }
+ }
+ }
+ }
+
+ return ENOENT;
+}
+
+/*
+ * Look for UN address in Encap attribute
+ */
+int rfapiGetVncTunnelUnAddr(struct attr *attr, struct prefix *p)
+{
+ struct bgp_attr_encap_subtlv *pEncap;
+ bgp_encap_types tun_type = BGP_ENCAP_TYPE_MPLS;/*Default tunnel type*/
+
+ bgp_attr_extcom_tunnel_type(attr, &tun_type);
+ if (tun_type == BGP_ENCAP_TYPE_MPLS) {
+ if (!p)
+ return 0;
+ /* MPLS carries UN address in next hop */
+ rfapiNexthop2Prefix(attr, p);
+ if (p->family != AF_UNSPEC)
+ return 0;
+
+ return ENOENT;
+ }
+ if (attr) {
+ for (pEncap = attr->encap_subtlvs; pEncap;
+ pEncap = pEncap->next) {
+
+ if (pEncap->type
+ == BGP_ENCAP_SUBTLV_TYPE_REMOTE_ENDPOINT) { /* un
+ addr
+ */
+ switch (pEncap->length) {
+ case 8:
+ if (p) {
+ p->family = AF_INET;
+ p->prefixlen = IPV4_MAX_BITLEN;
+ memcpy(p->u.val, pEncap->value,
+ 4);
+ }
+ return 0;
+
+ case 20:
+ if (p) {
+ p->family = AF_INET6;
+ p->prefixlen = IPV6_MAX_BITLEN;
+ memcpy(p->u.val, pEncap->value,
+ 16);
+ }
+ return 0;
+ }
+ }
+ }
+ }
+
+ return ENOENT;
+}
+
+/*
+ * Get UN address wherever it might be
+ */
+int rfapiGetUnAddrOfVpnBi(struct bgp_path_info *bpi, struct prefix *p)
+{
+ /* If it's in this route's VNC attribute, we're done */
+ if (!rfapiGetVncTunnelUnAddr(bpi->attr, p))
+ return 0;
+ /*
+ * Otherwise, see if it's cached from a corresponding ENCAP SAFI
+ * advertisement
+ */
+ if (bpi->extra) {
+ switch (bpi->extra->vnc.import.un_family) {
+ case AF_INET:
+ if (p) {
+ p->family = bpi->extra->vnc.import.un_family;
+ p->u.prefix4 = bpi->extra->vnc.import.un.addr4;
+ p->prefixlen = IPV4_MAX_BITLEN;
+ }
+ return 0;
+ case AF_INET6:
+ if (p) {
+ p->family = bpi->extra->vnc.import.un_family;
+ p->u.prefix6 = bpi->extra->vnc.import.un.addr6;
+ p->prefixlen = IPV6_MAX_BITLEN;
+ }
+ return 0;
+ default:
+ if (p)
+ p->family = AF_UNSPEC;
+#ifdef DEBUG_ENCAP_MONITOR
+ vnc_zlog_debug_verbose(
+ "%s: bpi->extra->vnc.import.un_family is 0, no UN addr",
+ __func__);
+#endif
+ break;
+ }
+ }
+
+ return ENOENT;
+}
+
+
+/*
+ * Make a new bgp_path_info from gathered parameters
+ */
+static struct bgp_path_info *rfapiBgpInfoCreate(struct attr *attr,
+ struct peer *peer, void *rfd,
+ struct prefix_rd *prd,
+ uint8_t type, uint8_t sub_type,
+ uint32_t *label)
+{
+ struct bgp_path_info *new;
+
+ new = info_make(type, sub_type, 0, peer, attr, NULL);
+
+ new->attr = bgp_attr_intern(attr);
+
+ bgp_path_info_extra_get(new);
+ if (prd) {
+ new->extra->vnc.import.rd = *prd;
+ new->extra->vnc.import.create_time = monotime(NULL);
+ }
+ if (label)
+ encode_label(*label, &new->extra->label[0]);
+
+ peer_lock(peer);
+
+ return new;
+}
+
+/*
+ * Frees bgp_path_info as used in import tables (parts are not
+ * allocated exactly the way they are in the main RIBs)
+ */
+static void rfapiBgpInfoFree(struct bgp_path_info *goner)
+{
+ if (!goner)
+ return;
+
+ if (goner->peer) {
+ vnc_zlog_debug_verbose("%s: calling peer_unlock(%p), #%d",
+ __func__, goner->peer,
+ goner->peer->lock);
+ peer_unlock(goner->peer);
+ }
+
+ bgp_attr_unintern(&goner->attr);
+
+ if (goner->extra)
+ bgp_path_info_extra_free(&goner->extra);
+ XFREE(MTYPE_BGP_ROUTE, goner);
+}
+
+struct rfapi_import_table *rfapiMacImportTableGetNoAlloc(struct bgp *bgp,
+ uint32_t lni)
+{
+ struct rfapi *h;
+ struct rfapi_import_table *it = NULL;
+ uintptr_t lni_as_ptr = lni;
+
+ h = bgp->rfapi;
+ if (!h)
+ return NULL;
+
+ if (!h->import_mac)
+ return NULL;
+
+ if (skiplist_search(h->import_mac, (void *)lni_as_ptr, (void **)&it))
+ return NULL;
+
+ return it;
+}
+
+struct rfapi_import_table *rfapiMacImportTableGet(struct bgp *bgp, uint32_t lni)
+{
+ struct rfapi *h;
+ struct rfapi_import_table *it = NULL;
+ uintptr_t lni_as_ptr = lni;
+
+ h = bgp->rfapi;
+ assert(h);
+
+ if (!h->import_mac) {
+ /* default cmp is good enough for LNI */
+ h->import_mac = skiplist_new(0, NULL, NULL);
+ }
+
+ if (skiplist_search(h->import_mac, (void *)lni_as_ptr, (void **)&it)) {
+
+ struct ecommunity *enew;
+ struct ecommunity_val eval;
+ afi_t afi;
+
+ it = XCALLOC(MTYPE_RFAPI_IMPORTTABLE,
+ sizeof(struct rfapi_import_table));
+ /* set RT list of new import table based on LNI */
+ memset((char *)&eval, 0, sizeof(eval));
+ eval.val[0] = 0; /* VNC L2VPN */
+ eval.val[1] = 2; /* VNC L2VPN */
+ eval.val[5] = (lni >> 16) & 0xff;
+ eval.val[6] = (lni >> 8) & 0xff;
+ eval.val[7] = (lni >> 0) & 0xff;
+
+ enew = ecommunity_new();
+ ecommunity_add_val(enew, &eval, false, false);
+ it->rt_import_list = enew;
+
+ for (afi = AFI_IP; afi < AFI_MAX; ++afi) {
+ it->imported_vpn[afi] = agg_table_init();
+ it->imported_encap[afi] = agg_table_init();
+ }
+
+ it->l2_logical_net_id = lni;
+
+ skiplist_insert(h->import_mac, (void *)lni_as_ptr, it);
+ }
+
+ assert(it);
+ return it;
+}
+
+/*
+ * Implement MONITOR_MOVE_SHORTER(original_node) from
+ * RFAPI-Import-Event-Handling.txt
+ *
+ * Returns pointer to the list of moved monitors
+ */
+static struct rfapi_monitor_vpn *
+rfapiMonitorMoveShorter(struct agg_node *original_vpn_node, int lockoffset)
+{
+ struct bgp_path_info *bpi;
+ struct agg_node *par;
+ struct rfapi_monitor_vpn *m;
+ struct rfapi_monitor_vpn *mlast;
+ struct rfapi_monitor_vpn *moved;
+ int movecount = 0;
+ int parent_already_refcounted = 0;
+
+ RFAPI_CHECK_REFCOUNT(original_vpn_node, SAFI_MPLS_VPN, lockoffset);
+
+#ifdef DEBUG_MONITOR_MOVE_SHORTER
+ {
+ vnc_zlog_debug_verbose("%s: called with node pfx=%pFX",
+ __func__, &original_vpn_node->p);
+ }
+#endif
+
+ /*
+ * 1. If there is at least one bpi (either regular route or
+ * route marked as withdrawn, with a pending timer) at
+ * original_node with a valid UN address, we're done. Return.
+ */
+ for (bpi = original_vpn_node->info; bpi; bpi = bpi->next) {
+ struct prefix pfx;
+
+ if (!rfapiGetUnAddrOfVpnBi(bpi, &pfx)) {
+#ifdef DEBUG_MONITOR_MOVE_SHORTER
+ vnc_zlog_debug_verbose(
+ "%s: have valid UN at original node, no change",
+ __func__);
+#endif
+ return NULL;
+ }
+ }
+
+ /*
+ * 2. Travel up the tree (toward less-specific prefixes) from
+ * original_node to find the first node that has at least
+ * one route (even if it is only a withdrawn route) with a
+ * valid UN address. Call this node "Node P."
+ */
+ for (par = agg_node_parent(original_vpn_node); par;
+ par = agg_node_parent(par)) {
+ for (bpi = par->info; bpi; bpi = bpi->next) {
+ struct prefix pfx;
+ if (!rfapiGetUnAddrOfVpnBi(bpi, &pfx)) {
+ break;
+ }
+ }
+ if (bpi)
+ break;
+ }
+
+ if (par) {
+ RFAPI_CHECK_REFCOUNT(par, SAFI_MPLS_VPN, 0);
+ }
+
+ /*
+ * If no less-specific routes, try to use the 0/0 node
+ */
+ if (!par) {
+ const struct prefix *p;
+ /* this isn't necessarily 0/0 */
+ par = agg_route_table_top(original_vpn_node);
+
+ if (par)
+ p = agg_node_get_prefix(par);
+ /*
+ * If we got the top node but it wasn't 0/0,
+ * ignore it
+ */
+ if (par && p->prefixlen) {
+ agg_unlock_node(par); /* maybe free */
+ par = NULL;
+ }
+
+ if (par) {
+ ++parent_already_refcounted;
+ }
+ }
+
+ /*
+ * Create 0/0 node if it isn't there
+ */
+ if (!par) {
+ struct prefix pfx_default;
+ const struct prefix *p = agg_node_get_prefix(original_vpn_node);
+
+ memset(&pfx_default, 0, sizeof(pfx_default));
+ pfx_default.family = p->family;
+
+ /* creates default node if none exists */
+ par = agg_node_get(agg_get_table(original_vpn_node),
+ &pfx_default);
+ ++parent_already_refcounted;
+ }
+
+ /*
+ * 3. Move each of the monitors found at original_node to Node P.
+ * These are "Moved Monitors."
+ *
+ */
+
+ /*
+ * Attach at end so that the list pointer we return points
+ * only to the moved routes
+ */
+ for (m = RFAPI_MONITOR_VPN(par), mlast = NULL; m;
+ mlast = m, m = m->next)
+ ;
+
+ if (mlast) {
+ moved = mlast->next = RFAPI_MONITOR_VPN(original_vpn_node);
+ } else {
+ moved = RFAPI_MONITOR_VPN_W_ALLOC(par) =
+ RFAPI_MONITOR_VPN(original_vpn_node);
+ }
+ if (RFAPI_MONITOR_VPN(
+ original_vpn_node)) /* check agg, so not allocated */
+ RFAPI_MONITOR_VPN_W_ALLOC(original_vpn_node) = NULL;
+
+ /*
+ * update the node pointers on the monitors
+ */
+ for (m = moved; m; m = m->next) {
+ ++movecount;
+ m->node = par;
+ }
+
+ RFAPI_CHECK_REFCOUNT(par, SAFI_MPLS_VPN,
+ parent_already_refcounted - movecount);
+ while (movecount > parent_already_refcounted) {
+ agg_lock_node(par);
+ ++parent_already_refcounted;
+ }
+ while (movecount < parent_already_refcounted) {
+ /* unlikely, but code defensively */
+ agg_unlock_node(par);
+ --parent_already_refcounted;
+ }
+ RFAPI_CHECK_REFCOUNT(original_vpn_node, SAFI_MPLS_VPN,
+ movecount + lockoffset);
+ while (movecount--) {
+ agg_unlock_node(original_vpn_node);
+ }
+
+#ifdef DEBUG_MONITOR_MOVE_SHORTER
+ {
+ vnc_zlog_debug_verbose("%s: moved to node pfx=%pFX", __func__,
+ &par->p);
+ }
+#endif
+
+
+ return moved;
+}
+
+/*
+ * Implement MONITOR_MOVE_LONGER(new_node) from
+ * RFAPI-Import-Event-Handling.txt
+ */
+static void rfapiMonitorMoveLonger(struct agg_node *new_vpn_node)
+{
+ struct rfapi_monitor_vpn *monitor;
+ struct rfapi_monitor_vpn *mlast;
+ struct bgp_path_info *bpi;
+ struct agg_node *par;
+ const struct prefix *new_vpn_node_p = agg_node_get_prefix(new_vpn_node);
+
+ RFAPI_CHECK_REFCOUNT(new_vpn_node, SAFI_MPLS_VPN, 0);
+
+ /*
+ * Make sure we have at least one valid route at the new node
+ */
+ for (bpi = new_vpn_node->info; bpi; bpi = bpi->next) {
+ struct prefix pfx;
+ if (!rfapiGetUnAddrOfVpnBi(bpi, &pfx))
+ break;
+ }
+
+ if (!bpi) {
+ vnc_zlog_debug_verbose(
+ "%s: no valid routes at node %p, so not attempting moves",
+ __func__, new_vpn_node);
+ return;
+ }
+
+ /*
+ * Find first parent node that has monitors
+ */
+ for (par = agg_node_parent(new_vpn_node); par;
+ par = agg_node_parent(par)) {
+ if (RFAPI_MONITOR_VPN(par))
+ break;
+ }
+
+ if (!par) {
+ vnc_zlog_debug_verbose(
+ "%s: no parent nodes with monitors, done", __func__);
+ return;
+ }
+
+ /*
+ * Check each of these monitors to see of their longest-match
+ * is now the updated node. Move any such monitors to the more-
+ * specific updated node
+ */
+ for (mlast = NULL, monitor = RFAPI_MONITOR_VPN(par); monitor;) {
+ /*
+ * If new longest match for monitor prefix is the new
+ * route's prefix, move monitor to new route's prefix
+ */
+ if (prefix_match(new_vpn_node_p, &monitor->p)) {
+ /* detach */
+ if (mlast) {
+ mlast->next = monitor->next;
+ } else {
+ RFAPI_MONITOR_VPN_W_ALLOC(par) = monitor->next;
+ }
+
+
+ /* attach */
+ monitor->next = RFAPI_MONITOR_VPN(new_vpn_node);
+ RFAPI_MONITOR_VPN_W_ALLOC(new_vpn_node) = monitor;
+ monitor->node = new_vpn_node;
+
+ agg_lock_node(new_vpn_node); /* incr refcount */
+
+ monitor = mlast ? mlast->next : RFAPI_MONITOR_VPN(par);
+
+ RFAPI_CHECK_REFCOUNT(par, SAFI_MPLS_VPN, 1);
+ /* decr refcount after we're done with par as this might
+ * free it */
+ agg_unlock_node(par);
+
+ continue;
+ }
+ mlast = monitor;
+ monitor = monitor->next;
+ }
+
+ RFAPI_CHECK_REFCOUNT(new_vpn_node, SAFI_MPLS_VPN, 0);
+}
+
+
+static void rfapiBgpInfoChainFree(struct bgp_path_info *bpi)
+{
+ struct bgp_path_info *next;
+
+ while (bpi) {
+
+ /*
+ * If there is a timer waiting to delete this bpi, cancel
+ * the timer and delete immediately
+ */
+ if (CHECK_FLAG(bpi->flags, BGP_PATH_REMOVED)
+ && bpi->extra->vnc.import.timer) {
+ struct rfapi_withdraw *wcb =
+ THREAD_ARG(bpi->extra->vnc.import.timer);
+
+ XFREE(MTYPE_RFAPI_WITHDRAW, wcb);
+ THREAD_OFF(bpi->extra->vnc.import.timer);
+ }
+
+ next = bpi->next;
+ bpi->next = NULL;
+ rfapiBgpInfoFree(bpi);
+ bpi = next;
+ }
+}
+
+static void rfapiImportTableFlush(struct rfapi_import_table *it)
+{
+ afi_t afi;
+
+ /*
+ * Free ecommunity
+ */
+ ecommunity_free(&it->rt_import_list);
+ it->rt_import_list = NULL;
+
+ for (afi = AFI_IP; afi < AFI_MAX; ++afi) {
+
+ struct agg_node *rn;
+
+ for (rn = agg_route_top(it->imported_vpn[afi]); rn;
+ rn = agg_route_next(rn)) {
+ /*
+ * Each route_node has:
+ * aggregate: points to rfapi_it_extra with monitor
+ * chain(s)
+ * info: points to chain of bgp_path_info
+ */
+ /* free bgp_path_info and its children */
+ rfapiBgpInfoChainFree(rn->info);
+ rn->info = NULL;
+
+ rfapiMonitorExtraFlush(SAFI_MPLS_VPN, rn);
+ }
+
+ for (rn = agg_route_top(it->imported_encap[afi]); rn;
+ rn = agg_route_next(rn)) {
+ /* free bgp_path_info and its children */
+ rfapiBgpInfoChainFree(rn->info);
+ rn->info = NULL;
+
+ rfapiMonitorExtraFlush(SAFI_ENCAP, rn);
+ }
+
+ agg_table_finish(it->imported_vpn[afi]);
+ agg_table_finish(it->imported_encap[afi]);
+ }
+ if (it->monitor_exterior_orphans) {
+ skiplist_free(it->monitor_exterior_orphans);
+ }
+}
+
+void rfapiImportTableRefDelByIt(struct bgp *bgp,
+ struct rfapi_import_table *it_target)
+{
+ struct rfapi *h;
+ struct rfapi_import_table *it;
+ struct rfapi_import_table *prev = NULL;
+
+ assert(it_target);
+
+ h = bgp->rfapi;
+ assert(h);
+
+ for (it = h->imports; it; prev = it, it = it->next) {
+ if (it == it_target)
+ break;
+ }
+
+ assert(it);
+ assert(it->refcount);
+
+ it->refcount -= 1;
+
+ if (!it->refcount) {
+ if (prev) {
+ prev->next = it->next;
+ } else {
+ h->imports = it->next;
+ }
+ rfapiImportTableFlush(it);
+ XFREE(MTYPE_RFAPI_IMPORTTABLE, it);
+ }
+}
+
+#ifdef RFAPI_REQUIRE_ENCAP_BEEC
+/*
+ * Look for magic BGP Encapsulation Extended Community value
+ * Format in RFC 5512 Sect. 4.5
+ */
+static int rfapiEcommunitiesMatchBeec(struct ecommunity *ecom,
+ bgp_encap_types type)
+{
+ int i;
+
+ if (!ecom)
+ return 0;
+
+ for (i = 0; i < (ecom->size * ECOMMUNITY_SIZE); i += ECOMMUNITY_SIZE) {
+
+ uint8_t *ep;
+
+ ep = ecom->val + i;
+
+ if (ep[0] == ECOMMUNITY_ENCODE_OPAQUE
+ && ep[1] == ECOMMUNITY_OPAQUE_SUBTYPE_ENCAP
+ && ep[6] == ((type && 0xff00) >> 8)
+ && ep[7] == (type & 0xff)) {
+
+ return 1;
+ }
+ }
+ return 0;
+}
+#endif
+
+int rfapiEcommunitiesIntersect(struct ecommunity *e1, struct ecommunity *e2)
+{
+ uint32_t i, j;
+
+ if (!e1 || !e2)
+ return 0;
+
+ {
+ char *s1, *s2;
+ s1 = ecommunity_ecom2str(e1, ECOMMUNITY_FORMAT_DISPLAY, 0);
+ s2 = ecommunity_ecom2str(e2, ECOMMUNITY_FORMAT_DISPLAY, 0);
+ vnc_zlog_debug_verbose("%s: e1[%s], e2[%s]", __func__, s1, s2);
+ XFREE(MTYPE_ECOMMUNITY_STR, s1);
+ XFREE(MTYPE_ECOMMUNITY_STR, s2);
+ }
+
+ for (i = 0; i < e1->size; ++i) {
+ for (j = 0; j < e2->size; ++j) {
+ if (!memcmp(e1->val + (i * ECOMMUNITY_SIZE),
+ e2->val + (j * ECOMMUNITY_SIZE),
+ ECOMMUNITY_SIZE)) {
+
+ return 1;
+ }
+ }
+ }
+ return 0;
+}
+
+int rfapiEcommunityGetLNI(struct ecommunity *ecom, uint32_t *lni)
+{
+ if (ecom) {
+ uint32_t i;
+
+ for (i = 0; i < ecom->size; ++i) {
+ uint8_t *p = ecom->val + (i * ECOMMUNITY_SIZE);
+
+ if ((*(p + 0) == 0x00) && (*(p + 1) == 0x02)) {
+
+ *lni = (*(p + 5) << 16) | (*(p + 6) << 8)
+ | (*(p + 7));
+ return 0;
+ }
+ }
+ }
+ return ENOENT;
+}
+
+int rfapiEcommunityGetEthernetTag(struct ecommunity *ecom, uint16_t *tag_id)
+{
+ struct bgp *bgp = bgp_get_default();
+ *tag_id = 0; /* default to untagged */
+ if (ecom) {
+ uint32_t i;
+
+ for (i = 0; i < ecom->size; ++i) {
+ as_t as = 0;
+ int encode = 0;
+ const uint8_t *p = ecom->val + (i * ECOMMUNITY_SIZE);
+
+ /* High-order octet of type. */
+ encode = *p++;
+
+ if (*p++ == ECOMMUNITY_ROUTE_TARGET) {
+ if (encode == ECOMMUNITY_ENCODE_AS4) {
+ p = ptr_get_be32(p, &as);
+ } else if (encode == ECOMMUNITY_ENCODE_AS) {
+ as = (*p++ << 8);
+ as |= (*p++);
+ p += 2; /* skip next two, tag/vid
+ always in lowest bytes */
+ }
+ if (as == bgp->as) {
+ *tag_id = *p++ << 8;
+ *tag_id |= (*p++);
+ return 0;
+ }
+ }
+ }
+ }
+ return ENOENT;
+}
+
+static int rfapiVpnBiNhEqualsPt(struct bgp_path_info *bpi,
+ struct rfapi_ip_addr *hpt)
+{
+ uint8_t family;
+
+ if (!hpt || !bpi)
+ return 0;
+
+ family = BGP_MP_NEXTHOP_FAMILY(bpi->attr->mp_nexthop_len);
+
+ if (hpt->addr_family != family)
+ return 0;
+
+ switch (family) {
+ case AF_INET:
+ if (bpi->attr->mp_nexthop_global_in.s_addr
+ != hpt->addr.v4.s_addr)
+ return 0;
+ break;
+
+ case AF_INET6:
+ if (IPV6_ADDR_CMP(&bpi->attr->mp_nexthop_global, &hpt->addr.v6))
+ return 0;
+ break;
+
+ default:
+ return 0;
+ }
+
+ return 1;
+}
+
+
+/*
+ * Compare 2 VPN BIs. Return true if they have the same VN and UN addresses
+ */
+static int rfapiVpnBiSamePtUn(struct bgp_path_info *bpi1,
+ struct bgp_path_info *bpi2)
+{
+ struct prefix pfx_un1;
+ struct prefix pfx_un2;
+
+ if (!bpi1 || !bpi2)
+ return 0;
+
+ /*
+ * VN address comparisons
+ */
+
+ if (BGP_MP_NEXTHOP_FAMILY(bpi1->attr->mp_nexthop_len)
+ != BGP_MP_NEXTHOP_FAMILY(bpi2->attr->mp_nexthop_len)) {
+ return 0;
+ }
+
+ switch (BGP_MP_NEXTHOP_FAMILY(bpi1->attr->mp_nexthop_len)) {
+ case AF_INET:
+ if (bpi1->attr->mp_nexthop_global_in.s_addr
+ != bpi2->attr->mp_nexthop_global_in.s_addr)
+ return 0;
+ break;
+
+ case AF_INET6:
+ if (IPV6_ADDR_CMP(&bpi1->attr->mp_nexthop_global,
+ &bpi2->attr->mp_nexthop_global))
+ return 0;
+ break;
+
+ default:
+ return 0;
+ }
+
+ memset(&pfx_un1, 0, sizeof(pfx_un1));
+ memset(&pfx_un2, 0, sizeof(pfx_un2));
+
+ /*
+ * UN address comparisons
+ */
+ if (rfapiGetVncTunnelUnAddr(bpi1->attr, &pfx_un1)) {
+ if (bpi1->extra) {
+ pfx_un1.family = bpi1->extra->vnc.import.un_family;
+ switch (bpi1->extra->vnc.import.un_family) {
+ case AF_INET:
+ pfx_un1.u.prefix4 =
+ bpi1->extra->vnc.import.un.addr4;
+ break;
+ case AF_INET6:
+ pfx_un1.u.prefix6 =
+ bpi1->extra->vnc.import.un.addr6;
+ break;
+ default:
+ pfx_un1.family = AF_UNSPEC;
+ break;
+ }
+ }
+ }
+
+ if (rfapiGetVncTunnelUnAddr(bpi2->attr, &pfx_un2)) {
+ if (bpi2->extra) {
+ pfx_un2.family = bpi2->extra->vnc.import.un_family;
+ switch (bpi2->extra->vnc.import.un_family) {
+ case AF_INET:
+ pfx_un2.u.prefix4 =
+ bpi2->extra->vnc.import.un.addr4;
+ break;
+ case AF_INET6:
+ pfx_un2.u.prefix6 =
+ bpi2->extra->vnc.import.un.addr6;
+ break;
+ default:
+ pfx_un2.family = AF_UNSPEC;
+ break;
+ }
+ }
+ }
+
+ if (pfx_un1.family == AF_UNSPEC || pfx_un2.family == AF_UNSPEC)
+ return 0;
+
+ if (pfx_un1.family != pfx_un2.family)
+ return 0;
+
+ switch (pfx_un1.family) {
+ case AF_INET:
+ if (!IPV4_ADDR_SAME(&pfx_un1.u.prefix4, &pfx_un2.u.prefix4))
+ return 0;
+ break;
+ case AF_INET6:
+ if (!IPV6_ADDR_SAME(&pfx_un1.u.prefix6, &pfx_un2.u.prefix6))
+ return 0;
+ break;
+ }
+
+
+ return 1;
+}
+
+uint8_t rfapiRfpCost(struct attr *attr)
+{
+ if (attr->flag & ATTR_FLAG_BIT(BGP_ATTR_LOCAL_PREF)) {
+ if (attr->local_pref > 255) {
+ return 0;
+ }
+ return 255 - attr->local_pref;
+ }
+
+ return 255;
+}
+
+/*------------------------------------------
+ * rfapi_extract_l2o
+ *
+ * Find Layer 2 options in an option chain
+ *
+ * input:
+ * pHop option chain
+ *
+ * output:
+ * l2o layer 2 options extracted
+ *
+ * return value:
+ * 0 OK
+ * 1 no options found
+ *
+ --------------------------------------------*/
+int rfapi_extract_l2o(
+ struct bgp_tea_options *pHop, /* chain of options */
+ struct rfapi_l2address_option *l2o) /* return extracted value */
+{
+ struct bgp_tea_options *p;
+
+ for (p = pHop; p; p = p->next) {
+ if ((p->type == RFAPI_VN_OPTION_TYPE_L2ADDR)
+ && (p->length >= 8)) {
+
+ char *v = p->value;
+
+ memcpy(&l2o->macaddr, v, 6);
+
+ l2o->label = ((v[6] << 12) & 0xff000)
+ + ((v[7] << 4) & 0xff0)
+ + ((v[8] >> 4) & 0xf);
+
+ l2o->local_nve_id = (uint8_t)v[10];
+
+ l2o->logical_net_id =
+ (v[11] << 16) + (v[12] << 8) + (v[13] << 0);
+
+ return 0;
+ }
+ }
+ return 1;
+}
+
+static struct rfapi_next_hop_entry *
+rfapiRouteInfo2NextHopEntry(struct rfapi_ip_prefix *rprefix,
+ struct bgp_path_info *bpi, /* route to encode */
+ uint32_t lifetime, /* use this in nhe */
+ struct agg_node *rn) /* req for L2 eth addr */
+{
+ struct rfapi_next_hop_entry *new;
+ int have_vnc_tunnel_un = 0;
+ const struct prefix *p = agg_node_get_prefix(rn);
+
+#ifdef DEBUG_ENCAP_MONITOR
+ vnc_zlog_debug_verbose("%s: entry, bpi %p, rn %p", __func__, bpi, rn);
+#endif
+
+ new = XCALLOC(MTYPE_RFAPI_NEXTHOP, sizeof(struct rfapi_next_hop_entry));
+
+ new->prefix = *rprefix;
+
+ if (bpi->extra
+ && decode_rd_type(bpi->extra->vnc.import.rd.val)
+ == RD_TYPE_VNC_ETH) {
+ /* ethernet */
+
+ struct rfapi_vn_option *vo;
+
+ vo = XCALLOC(MTYPE_RFAPI_VN_OPTION,
+ sizeof(struct rfapi_vn_option));
+
+ vo->type = RFAPI_VN_OPTION_TYPE_L2ADDR;
+
+ memcpy(&vo->v.l2addr.macaddr, &p->u.prefix_eth.octet, ETH_ALEN);
+ /* only low 3 bytes of this are significant */
+ (void)rfapiEcommunityGetLNI(bgp_attr_get_ecommunity(bpi->attr),
+ &vo->v.l2addr.logical_net_id);
+ (void)rfapiEcommunityGetEthernetTag(
+ bgp_attr_get_ecommunity(bpi->attr),
+ &vo->v.l2addr.tag_id);
+
+ /* local_nve_id comes from lower byte of RD type */
+ vo->v.l2addr.local_nve_id = bpi->extra->vnc.import.rd.val[1];
+
+ /* label comes from MP_REACH_NLRI label */
+ vo->v.l2addr.label = decode_label(&bpi->extra->label[0]);
+
+ new->vn_options = vo;
+
+ /*
+ * If there is an auxiliary prefix (i.e., host IP address),
+ * use it as the nexthop prefix instead of the query prefix
+ */
+ if (bpi->extra->vnc.import.aux_prefix.family) {
+ rfapiQprefix2Rprefix(&bpi->extra->vnc.import.aux_prefix,
+ &new->prefix);
+ }
+ }
+
+ bgp_encap_types tun_type = BGP_ENCAP_TYPE_MPLS; /*Default*/
+ new->prefix.cost = rfapiRfpCost(bpi->attr);
+
+ struct bgp_attr_encap_subtlv *pEncap;
+
+ switch (BGP_MP_NEXTHOP_FAMILY(bpi->attr->mp_nexthop_len)) {
+ case AF_INET:
+ new->vn_address.addr_family = AF_INET;
+ new->vn_address.addr.v4 = bpi->attr->mp_nexthop_global_in;
+ break;
+
+ case AF_INET6:
+ new->vn_address.addr_family = AF_INET6;
+ new->vn_address.addr.v6 = bpi->attr->mp_nexthop_global;
+ break;
+
+ default:
+ zlog_warn("%s: invalid vpn nexthop length: %d", __func__,
+ bpi->attr->mp_nexthop_len);
+ rfapi_free_next_hop_list(new);
+ return NULL;
+ }
+
+ for (pEncap = bgp_attr_get_vnc_subtlvs(bpi->attr); pEncap;
+ pEncap = pEncap->next) {
+ switch (pEncap->type) {
+ case BGP_VNC_SUBTLV_TYPE_LIFETIME:
+ /* use configured lifetime, not attr lifetime */
+ break;
+
+ default:
+ zlog_warn("%s: unknown VNC option type %d", __func__,
+ pEncap->type);
+
+ break;
+ }
+ }
+
+ bgp_attr_extcom_tunnel_type(bpi->attr, &tun_type);
+ if (tun_type == BGP_ENCAP_TYPE_MPLS) {
+ struct prefix p;
+ /* MPLS carries UN address in next hop */
+ rfapiNexthop2Prefix(bpi->attr, &p);
+ if (p.family != AF_UNSPEC) {
+ rfapiQprefix2Raddr(&p, &new->un_address);
+ have_vnc_tunnel_un = 1;
+ }
+ }
+
+ for (pEncap = bpi->attr->encap_subtlvs; pEncap; pEncap = pEncap->next) {
+ switch (pEncap->type) {
+ case BGP_ENCAP_SUBTLV_TYPE_REMOTE_ENDPOINT:
+ /*
+ * Overrides ENCAP UN address, if any
+ */
+ switch (pEncap->length) {
+
+ case 8:
+ new->un_address.addr_family = AF_INET;
+ memcpy(&new->un_address.addr.v4, pEncap->value,
+ 4);
+ have_vnc_tunnel_un = 1;
+ break;
+
+ case 20:
+ new->un_address.addr_family = AF_INET6;
+ memcpy(&new->un_address.addr.v6, pEncap->value,
+ 16);
+ have_vnc_tunnel_un = 1;
+ break;
+
+ default:
+ zlog_warn(
+ "%s: invalid tunnel subtlv UN addr length (%d) for bpi %p",
+ __func__, pEncap->length, bpi);
+ }
+ break;
+
+ default:
+ zlog_warn("%s: unknown Encap Attribute option type %d",
+ __func__, pEncap->type);
+ break;
+ }
+ }
+
+ new->un_options = rfapi_encap_tlv_to_un_option(bpi->attr);
+
+#ifdef DEBUG_ENCAP_MONITOR
+ vnc_zlog_debug_verbose("%s: line %d: have_vnc_tunnel_un=%d", __func__,
+ __LINE__, have_vnc_tunnel_un);
+#endif
+
+ if (!have_vnc_tunnel_un && bpi->extra) {
+ /*
+ * use cached UN address from ENCAP route
+ */
+ new->un_address.addr_family = bpi->extra->vnc.import.un_family;
+ switch (new->un_address.addr_family) {
+ case AF_INET:
+ new->un_address.addr.v4 =
+ bpi->extra->vnc.import.un.addr4;
+ break;
+ case AF_INET6:
+ new->un_address.addr.v6 =
+ bpi->extra->vnc.import.un.addr6;
+ break;
+ default:
+ zlog_warn("%s: invalid UN addr family (%d) for bpi %p",
+ __func__, new->un_address.addr_family, bpi);
+ rfapi_free_next_hop_list(new);
+ return NULL;
+ }
+ }
+
+ new->lifetime = lifetime;
+ return new;
+}
+
+int rfapiHasNonRemovedRoutes(struct agg_node *rn)
+{
+ struct bgp_path_info *bpi;
+
+ for (bpi = rn->info; bpi; bpi = bpi->next) {
+ struct prefix pfx;
+
+ if (!CHECK_FLAG(bpi->flags, BGP_PATH_REMOVED)
+ && (bpi->extra && !rfapiGetUnAddrOfVpnBi(bpi, &pfx))) {
+
+ return 1;
+ }
+ }
+ return 0;
+}
+
+#ifdef DEBUG_IT_NODES
+/*
+ * DEBUG FUNCTION
+ */
+void rfapiDumpNode(struct agg_node *rn)
+{
+ struct bgp_path_info *bpi;
+
+ vnc_zlog_debug_verbose("%s: rn=%p", __func__, rn);
+ for (bpi = rn->info; bpi; bpi = bpi->next) {
+ struct prefix pfx;
+ int ctrc = rfapiGetUnAddrOfVpnBi(bpi, &pfx);
+ int nr;
+
+ if (!CHECK_FLAG(bpi->flags, BGP_PATH_REMOVED)
+ && (bpi->extra && !ctrc)) {
+
+ nr = 1;
+ } else {
+ nr = 0;
+ }
+
+ vnc_zlog_debug_verbose(
+ " bpi=%p, nr=%d, flags=0x%x, extra=%p, ctrc=%d", bpi,
+ nr, bpi->flags, bpi->extra, ctrc);
+ }
+}
+#endif
+
+static int rfapiNhlAddNodeRoutes(
+ struct agg_node *rn, /* in */
+ struct rfapi_ip_prefix *rprefix, /* in */
+ uint32_t lifetime, /* in */
+ int removed, /* in */
+ struct rfapi_next_hop_entry **head, /* in/out */
+ struct rfapi_next_hop_entry **tail, /* in/out */
+ struct rfapi_ip_addr *exclude_vnaddr, /* omit routes to same NVE */
+ struct agg_node *rfd_rib_node, /* preload this NVE rib node */
+ struct prefix *pfx_target_original) /* query target */
+{
+ struct bgp_path_info *bpi;
+ struct rfapi_next_hop_entry *new;
+ struct prefix pfx_un;
+ struct skiplist *seen_nexthops;
+ int count = 0;
+ const struct prefix *p = agg_node_get_prefix(rn);
+ int is_l2 = (p->family == AF_ETHERNET);
+
+ if (rfd_rib_node) {
+ struct agg_table *atable = agg_get_table(rfd_rib_node);
+ struct rfapi_descriptor *rfd;
+
+ if (atable) {
+ rfd = agg_get_table_info(atable);
+
+ if (rfapiRibFTDFilterRecentPrefix(rfd, rn,
+ pfx_target_original))
+ return 0;
+ }
+ }
+
+ seen_nexthops =
+ skiplist_new(0, vnc_prefix_cmp, prefix_free_lists);
+
+ for (bpi = rn->info; bpi; bpi = bpi->next) {
+
+ struct prefix pfx_vn;
+ struct prefix *newpfx;
+
+ if (removed && !CHECK_FLAG(bpi->flags, BGP_PATH_REMOVED)) {
+#ifdef DEBUG_RETURNED_NHL
+ vnc_zlog_debug_verbose(
+ "%s: want holddown, this route not holddown, skip",
+ __func__);
+#endif
+ continue;
+ }
+ if (!removed && CHECK_FLAG(bpi->flags, BGP_PATH_REMOVED)) {
+ continue;
+ }
+
+ if (!bpi->extra) {
+ continue;
+ }
+
+ /*
+ * Check for excluded VN address
+ */
+ if (rfapiVpnBiNhEqualsPt(bpi, exclude_vnaddr))
+ continue;
+
+ /*
+ * Check for VN address (nexthop) copied already
+ */
+ if (is_l2) {
+ /* L2 routes: semantic nexthop in aux_prefix; VN addr
+ * ain't it */
+ pfx_vn = bpi->extra->vnc.import.aux_prefix;
+ } else {
+ rfapiNexthop2Prefix(bpi->attr, &pfx_vn);
+ }
+ if (!skiplist_search(seen_nexthops, &pfx_vn, NULL)) {
+#ifdef DEBUG_RETURNED_NHL
+ vnc_zlog_debug_verbose(
+ "%s: already put VN/nexthop %pFX, skip",
+ __func__, &pfx_vn);
+#endif
+ continue;
+ }
+
+ if (rfapiGetUnAddrOfVpnBi(bpi, &pfx_un)) {
+#ifdef DEBUG_ENCAP_MONITOR
+ vnc_zlog_debug_verbose(
+ "%s: failed to get UN address of this VPN bpi",
+ __func__);
+#endif
+ continue;
+ }
+
+ newpfx = prefix_new();
+ *newpfx = pfx_vn;
+ skiplist_insert(seen_nexthops, newpfx, newpfx);
+
+ new = rfapiRouteInfo2NextHopEntry(rprefix, bpi, lifetime, rn);
+ if (new) {
+ if (rfapiRibPreloadBi(rfd_rib_node, &pfx_vn, &pfx_un,
+ lifetime, bpi)) {
+ /* duplicate filtered by RIB */
+ rfapi_free_next_hop_list(new);
+ new = NULL;
+ }
+ }
+
+ if (new) {
+ if (*tail) {
+ (*tail)->next = new;
+ } else {
+ *head = new;
+ }
+ *tail = new;
+ ++count;
+ }
+ }
+
+ skiplist_free(seen_nexthops);
+
+ return count;
+}
+
+
+/*
+ * Breadth-first
+ *
+ * omit_node is meant for the situation where we are adding a subtree
+ * of a parent of some original requested node. The response already
+ * contains the original requested node, and we don't want to duplicate
+ * its routes in the list, so we skip it if the right or left node
+ * matches (of course, we still travel down its child subtrees).
+ */
+static int rfapiNhlAddSubtree(
+ struct agg_node *rn, /* in */
+ uint32_t lifetime, /* in */
+ struct rfapi_next_hop_entry **head, /* in/out */
+ struct rfapi_next_hop_entry **tail, /* in/out */
+ struct agg_node *omit_node, /* in */
+ struct rfapi_ip_addr *exclude_vnaddr, /* omit routes to same NVE */
+ struct agg_table *rfd_rib_table, /* preload here */
+ struct prefix *pfx_target_original) /* query target */
+{
+ struct rfapi_ip_prefix rprefix;
+ int rcount = 0;
+
+ /* FIXME: need to find a better way here to work without sticking our
+ * hands in node->link */
+ if (agg_node_left(rn) && agg_node_left(rn) != omit_node) {
+ if (agg_node_left(rn)->info) {
+ const struct prefix *p =
+ agg_node_get_prefix(agg_node_left(rn));
+ int count = 0;
+ struct agg_node *rib_rn = NULL;
+
+ rfapiQprefix2Rprefix(p, &rprefix);
+ if (rfd_rib_table)
+ rib_rn = agg_node_get(rfd_rib_table, p);
+
+ count = rfapiNhlAddNodeRoutes(
+ agg_node_left(rn), &rprefix, lifetime, 0, head,
+ tail, exclude_vnaddr, rib_rn,
+ pfx_target_original);
+ if (!count) {
+ count = rfapiNhlAddNodeRoutes(
+ agg_node_left(rn), &rprefix, lifetime,
+ 1, head, tail, exclude_vnaddr, rib_rn,
+ pfx_target_original);
+ }
+ rcount += count;
+ if (rib_rn)
+ agg_unlock_node(rib_rn);
+ }
+ }
+
+ if (agg_node_right(rn) && agg_node_right(rn) != omit_node) {
+ if (agg_node_right(rn)->info) {
+ const struct prefix *p =
+ agg_node_get_prefix(agg_node_right(rn));
+ int count = 0;
+ struct agg_node *rib_rn = NULL;
+
+ rfapiQprefix2Rprefix(p, &rprefix);
+ if (rfd_rib_table)
+ rib_rn = agg_node_get(rfd_rib_table, p);
+
+ count = rfapiNhlAddNodeRoutes(
+ agg_node_right(rn), &rprefix, lifetime, 0, head,
+ tail, exclude_vnaddr, rib_rn,
+ pfx_target_original);
+ if (!count) {
+ count = rfapiNhlAddNodeRoutes(
+ agg_node_right(rn), &rprefix, lifetime,
+ 1, head, tail, exclude_vnaddr, rib_rn,
+ pfx_target_original);
+ }
+ rcount += count;
+ if (rib_rn)
+ agg_unlock_node(rib_rn);
+ }
+ }
+
+ if (agg_node_left(rn)) {
+ rcount += rfapiNhlAddSubtree(
+ agg_node_left(rn), lifetime, head, tail, omit_node,
+ exclude_vnaddr, rfd_rib_table, pfx_target_original);
+ }
+ if (agg_node_right(rn)) {
+ rcount += rfapiNhlAddSubtree(
+ agg_node_right(rn), lifetime, head, tail, omit_node,
+ exclude_vnaddr, rfd_rib_table, pfx_target_original);
+ }
+
+ return rcount;
+}
+
+/*
+ * Implementation of ROUTE_LIST(node) from RFAPI-Import-Event-Handling.txt
+ *
+ * Construct an rfapi nexthop list based on the routes attached to
+ * the specified node.
+ *
+ * If there are any routes that do NOT have BGP_PATH_REMOVED set,
+ * return those only. If there are ONLY routes with BGP_PATH_REMOVED,
+ * then return those, and also include all the non-removed routes from the
+ * next less-specific node (i.e., this node's parent) at the end.
+ */
+struct rfapi_next_hop_entry *rfapiRouteNode2NextHopList(
+ struct agg_node *rn, uint32_t lifetime, /* put into nexthop entries */
+ struct rfapi_ip_addr *exclude_vnaddr, /* omit routes to same NVE */
+ struct agg_table *rfd_rib_table, /* preload here */
+ struct prefix *pfx_target_original) /* query target */
+{
+ struct rfapi_ip_prefix rprefix;
+ struct rfapi_next_hop_entry *answer = NULL;
+ struct rfapi_next_hop_entry *last = NULL;
+ struct agg_node *parent;
+ const struct prefix *p = agg_node_get_prefix(rn);
+ int count = 0;
+ struct agg_node *rib_rn;
+
+#ifdef DEBUG_RETURNED_NHL
+ vnc_zlog_debug_verbose("%s: called with node pfx=%rRN", __func__, rn);
+ rfapiDebugBacktrace();
+#endif
+
+ rfapiQprefix2Rprefix(p, &rprefix);
+
+ rib_rn = rfd_rib_table ? agg_node_get(rfd_rib_table, p) : NULL;
+
+ /*
+ * Add non-withdrawn routes at this node
+ */
+ count = rfapiNhlAddNodeRoutes(rn, &rprefix, lifetime, 0, &answer, &last,
+ exclude_vnaddr, rib_rn,
+ pfx_target_original);
+
+ /*
+ * If the list has at least one entry, it's finished
+ */
+ if (count) {
+ count += rfapiNhlAddSubtree(rn, lifetime, &answer, &last, NULL,
+ exclude_vnaddr, rfd_rib_table,
+ pfx_target_original);
+ vnc_zlog_debug_verbose("%s: %d nexthops, answer=%p", __func__,
+ count, answer);
+#ifdef DEBUG_RETURNED_NHL
+ rfapiPrintNhl(NULL, answer);
+#endif
+ if (rib_rn)
+ agg_unlock_node(rib_rn);
+ return answer;
+ }
+
+ /*
+ * Add withdrawn routes at this node
+ */
+ count = rfapiNhlAddNodeRoutes(rn, &rprefix, lifetime, 1, &answer, &last,
+ exclude_vnaddr, rib_rn,
+ pfx_target_original);
+ if (rib_rn)
+ agg_unlock_node(rib_rn);
+
+ // rfapiPrintNhl(NULL, answer);
+
+ /*
+ * walk up the tree until we find a node with non-deleted
+ * routes, then add them
+ */
+ for (parent = agg_node_parent(rn); parent;
+ parent = agg_node_parent(parent)) {
+ if (rfapiHasNonRemovedRoutes(parent)) {
+ break;
+ }
+ }
+
+ /*
+ * Add non-withdrawn routes from less-specific prefix
+ */
+ if (parent) {
+ const struct prefix *p = agg_node_get_prefix(parent);
+
+ rib_rn = rfd_rib_table ? agg_node_get(rfd_rib_table, p) : NULL;
+ rfapiQprefix2Rprefix(p, &rprefix);
+ count += rfapiNhlAddNodeRoutes(parent, &rprefix, lifetime, 0,
+ &answer, &last, exclude_vnaddr,
+ rib_rn, pfx_target_original);
+ count += rfapiNhlAddSubtree(parent, lifetime, &answer, &last,
+ rn, exclude_vnaddr, rfd_rib_table,
+ pfx_target_original);
+ if (rib_rn)
+ agg_unlock_node(rib_rn);
+ } else {
+ /*
+ * There is no parent with non-removed routes. Still need to
+ * add subtree of original node if it contributed routes to the
+ * answer.
+ */
+ if (count)
+ count += rfapiNhlAddSubtree(rn, lifetime, &answer,
+ &last, rn, exclude_vnaddr,
+ rfd_rib_table,
+ pfx_target_original);
+ }
+
+ vnc_zlog_debug_verbose("%s: %d nexthops, answer=%p", __func__, count,
+ answer);
+#ifdef DEBUG_RETURNED_NHL
+ rfapiPrintNhl(NULL, answer);
+#endif
+ return answer;
+}
+
+/*
+ * Construct nexthop list of all routes in table
+ */
+struct rfapi_next_hop_entry *rfapiRouteTable2NextHopList(
+ struct agg_table *rt, uint32_t lifetime, /* put into nexthop entries */
+ struct rfapi_ip_addr *exclude_vnaddr, /* omit routes to same NVE */
+ struct agg_table *rfd_rib_table, /* preload this NVE rib table */
+ struct prefix *pfx_target_original) /* query target */
+{
+ struct agg_node *rn;
+ struct rfapi_next_hop_entry *biglist = NULL;
+ struct rfapi_next_hop_entry *nhl;
+ struct rfapi_next_hop_entry *tail = NULL;
+ int count = 0;
+
+ for (rn = agg_route_top(rt); rn; rn = agg_route_next(rn)) {
+
+ nhl = rfapiRouteNode2NextHopList(rn, lifetime, exclude_vnaddr,
+ rfd_rib_table,
+ pfx_target_original);
+ if (!tail) {
+ tail = biglist = nhl;
+ if (tail)
+ count = 1;
+ } else {
+ tail->next = nhl;
+ }
+ if (tail) {
+ while (tail->next) {
+ ++count;
+ tail = tail->next;
+ }
+ }
+ }
+
+ vnc_zlog_debug_verbose("%s: returning %d routes", __func__, count);
+ return biglist;
+}
+
+struct rfapi_next_hop_entry *rfapiEthRouteNode2NextHopList(
+ struct agg_node *rn, struct rfapi_ip_prefix *rprefix,
+ uint32_t lifetime, /* put into nexthop entries */
+ struct rfapi_ip_addr *exclude_vnaddr, /* omit routes to same NVE */
+ struct agg_table *rfd_rib_table, /* preload NVE rib table */
+ struct prefix *pfx_target_original) /* query target */
+{
+ int count = 0;
+ struct rfapi_next_hop_entry *answer = NULL;
+ struct rfapi_next_hop_entry *last = NULL;
+ struct agg_node *rib_rn;
+
+ rib_rn = rfd_rib_table
+ ? agg_node_get(rfd_rib_table, agg_node_get_prefix(rn))
+ : NULL;
+
+ count = rfapiNhlAddNodeRoutes(rn, rprefix, lifetime, 0, &answer, &last,
+ NULL, rib_rn, pfx_target_original);
+
+#ifdef DEBUG_ENCAP_MONITOR
+ vnc_zlog_debug_verbose("%s: node %p: %d non-holddown routes", __func__,
+ rn, count);
+#endif
+
+ if (!count) {
+ count = rfapiNhlAddNodeRoutes(rn, rprefix, lifetime, 1, &answer,
+ &last, exclude_vnaddr, rib_rn,
+ pfx_target_original);
+ vnc_zlog_debug_verbose("%s: node %p: %d holddown routes",
+ __func__, rn, count);
+ }
+
+ if (rib_rn)
+ agg_unlock_node(rib_rn);
+
+#ifdef DEBUG_RETURNED_NHL
+ rfapiPrintNhl(NULL, answer);
+#endif
+
+ return answer;
+}
+
+
+/*
+ * Construct nexthop list of all routes in table
+ */
+struct rfapi_next_hop_entry *rfapiEthRouteTable2NextHopList(
+ uint32_t logical_net_id, struct rfapi_ip_prefix *rprefix,
+ uint32_t lifetime, /* put into nexthop entries */
+ struct rfapi_ip_addr *exclude_vnaddr, /* omit routes to same NVE */
+ struct agg_table *rfd_rib_table, /* preload NVE rib node */
+ struct prefix *pfx_target_original) /* query target */
+{
+ struct rfapi_import_table *it;
+ struct bgp *bgp = bgp_get_default();
+ struct agg_table *rt;
+ struct agg_node *rn;
+ struct rfapi_next_hop_entry *biglist = NULL;
+ struct rfapi_next_hop_entry *nhl;
+ struct rfapi_next_hop_entry *tail = NULL;
+ int count = 0;
+
+
+ it = rfapiMacImportTableGet(bgp, logical_net_id);
+ rt = it->imported_vpn[AFI_L2VPN];
+
+ for (rn = agg_route_top(rt); rn; rn = agg_route_next(rn)) {
+
+ nhl = rfapiEthRouteNode2NextHopList(
+ rn, rprefix, lifetime, exclude_vnaddr, rfd_rib_table,
+ pfx_target_original);
+ if (!tail) {
+ tail = biglist = nhl;
+ if (tail)
+ count = 1;
+ } else {
+ tail->next = nhl;
+ }
+ if (tail) {
+ while (tail->next) {
+ ++count;
+ tail = tail->next;
+ }
+ }
+ }
+
+ vnc_zlog_debug_verbose("%s: returning %d routes", __func__, count);
+ return biglist;
+}
+
+/*
+ * Insert a new bpi to the imported route table node,
+ * keeping the list of BPIs sorted best route first
+ */
+static void rfapiBgpInfoAttachSorted(struct agg_node *rn,
+ struct bgp_path_info *info_new, afi_t afi,
+ safi_t safi)
+{
+ struct bgp *bgp;
+ struct bgp_path_info *prev;
+ struct bgp_path_info *next;
+ char pfx_buf[PREFIX2STR_BUFFER];
+
+
+ bgp = bgp_get_default(); /* assume 1 instance for now */
+
+ if (VNC_DEBUG(IMPORT_BI_ATTACH)) {
+ vnc_zlog_debug_verbose("%s: info_new->peer=%p", __func__,
+ info_new->peer);
+ vnc_zlog_debug_verbose("%s: info_new->peer->su_remote=%p",
+ __func__, info_new->peer->su_remote);
+ }
+
+ for (prev = NULL, next = rn->info; next;
+ prev = next, next = next->next) {
+ enum bgp_path_selection_reason reason;
+
+ if (!bgp
+ || (!CHECK_FLAG(info_new->flags, BGP_PATH_REMOVED)
+ && CHECK_FLAG(next->flags, BGP_PATH_REMOVED))
+ || bgp_path_info_cmp_compatible(bgp, info_new, next,
+ pfx_buf, afi, safi,
+ &reason)
+ == -1) { /* -1 if 1st is better */
+ break;
+ }
+ }
+ vnc_zlog_debug_verbose("%s: prev=%p, next=%p", __func__, prev, next);
+ if (prev) {
+ prev->next = info_new;
+ } else {
+ rn->info = info_new;
+ }
+ info_new->prev = prev;
+ info_new->next = next;
+ if (next)
+ next->prev = info_new;
+ bgp_attr_intern(info_new->attr);
+}
+
+static void rfapiBgpInfoDetach(struct agg_node *rn, struct bgp_path_info *bpi)
+{
+ /*
+ * Remove the route (doubly-linked)
+ */
+ // bgp_attr_unintern (&bpi->attr);
+ if (bpi->next)
+ bpi->next->prev = bpi->prev;
+ if (bpi->prev)
+ bpi->prev->next = bpi->next;
+ else
+ rn->info = bpi->next;
+}
+
+/*
+ * For L3-indexed import tables
+ */
+static int rfapi_bi_peer_rd_cmp(const void *b1, const void *b2)
+{
+ const struct bgp_path_info *bpi1 = b1;
+ const struct bgp_path_info *bpi2 = b2;
+
+ /*
+ * Compare peers
+ */
+ if (bpi1->peer < bpi2->peer)
+ return -1;
+ if (bpi1->peer > bpi2->peer)
+ return 1;
+
+ /*
+ * compare RDs
+ */
+ return vnc_prefix_cmp(
+ (const struct prefix *)&bpi1->extra->vnc.import.rd,
+ (const struct prefix *)&bpi2->extra->vnc.import.rd);
+}
+
+/*
+ * For L2-indexed import tables
+ * The BPIs in these tables should ALWAYS have an aux_prefix set because
+ * they arrive via IPv4 or IPv6 advertisements.
+ */
+static int rfapi_bi_peer_rd_aux_cmp(const void *b1, const void *b2)
+{
+ const struct bgp_path_info *bpi1 = b1;
+ const struct bgp_path_info *bpi2 = b2;
+ int rc;
+
+ /*
+ * Compare peers
+ */
+ if (bpi1->peer < bpi2->peer)
+ return -1;
+ if (bpi1->peer > bpi2->peer)
+ return 1;
+
+ /*
+ * compare RDs
+ */
+ rc = vnc_prefix_cmp((struct prefix *)&bpi1->extra->vnc.import.rd,
+ (struct prefix *)&bpi2->extra->vnc.import.rd);
+ if (rc) {
+ return rc;
+ }
+
+ /*
+ * L2 import tables can have multiple entries with the
+ * same MAC address, same RD, but different L3 addresses.
+ *
+ * Use presence of aux_prefix with AF=ethernet and prefixlen=1
+ * as magic value to signify explicit wildcarding of the aux_prefix.
+ * This magic value will not appear in bona fide bpi entries in
+ * the import table, but is allowed in the "fake" bpi used to
+ * probe the table when searching. (We have to test both b1 and b2
+ * because there is no guarantee of the order the test key and
+ * the real key will be passed)
+ */
+ if ((bpi1->extra->vnc.import.aux_prefix.family == AF_ETHERNET
+ && (bpi1->extra->vnc.import.aux_prefix.prefixlen == 1))
+ || (bpi2->extra->vnc.import.aux_prefix.family == AF_ETHERNET
+ && (bpi2->extra->vnc.import.aux_prefix.prefixlen == 1))) {
+
+ /*
+ * wildcard aux address specified
+ */
+ return 0;
+ }
+
+ return vnc_prefix_cmp(&bpi1->extra->vnc.import.aux_prefix,
+ &bpi2->extra->vnc.import.aux_prefix);
+}
+
+
+/*
+ * Index on RD and Peer
+ */
+static void rfapiItBiIndexAdd(struct agg_node *rn, /* Import table VPN node */
+ struct bgp_path_info *bpi) /* new BPI */
+{
+ struct skiplist *sl;
+ const struct prefix *p;
+
+ assert(rn);
+ assert(bpi);
+ assert(bpi->extra);
+
+ vnc_zlog_debug_verbose("%s: bpi %p, peer %p, rd %pRD", __func__, bpi,
+ bpi->peer, &bpi->extra->vnc.import.rd);
+
+ sl = RFAPI_RDINDEX_W_ALLOC(rn);
+ if (!sl) {
+ p = agg_node_get_prefix(rn);
+ if (AF_ETHERNET == p->family) {
+ sl = skiplist_new(0, rfapi_bi_peer_rd_aux_cmp, NULL);
+ } else {
+ sl = skiplist_new(0, rfapi_bi_peer_rd_cmp, NULL);
+ }
+ RFAPI_IT_EXTRA_GET(rn)->u.vpn.idx_rd = sl;
+ agg_lock_node(rn); /* for skiplist */
+ }
+ assert(!skiplist_insert(sl, (void *)bpi, (void *)bpi));
+ agg_lock_node(rn); /* for skiplist entry */
+
+ /* NB: BPIs in import tables are not refcounted */
+}
+
+static void rfapiItBiIndexDump(struct agg_node *rn)
+{
+ struct skiplist *sl;
+ void *cursor = NULL;
+ struct bgp_path_info *k;
+ struct bgp_path_info *v;
+ int rc;
+
+ sl = RFAPI_RDINDEX(rn);
+ if (!sl)
+ return;
+
+ for (rc = skiplist_next(sl, (void **)&k, (void **)&v, &cursor); !rc;
+ rc = skiplist_next(sl, (void **)&k, (void **)&v, &cursor)) {
+
+ char buf[RD_ADDRSTRLEN];
+ char buf_aux_pfx[PREFIX_STRLEN];
+
+ prefix_rd2str(&k->extra->vnc.import.rd, buf, sizeof(buf));
+ if (k->extra->vnc.import.aux_prefix.family) {
+ prefix2str(&k->extra->vnc.import.aux_prefix,
+ buf_aux_pfx, sizeof(buf_aux_pfx));
+ } else
+ strlcpy(buf_aux_pfx, "(none)", sizeof(buf_aux_pfx));
+
+ vnc_zlog_debug_verbose("bpi %p, peer %p, rd %s, aux_prefix %s",
+ k, k->peer, buf, buf_aux_pfx);
+ }
+}
+
+static struct bgp_path_info *rfapiItBiIndexSearch(
+ struct agg_node *rn, /* Import table VPN node */
+ struct prefix_rd *prd, struct peer *peer,
+ const struct prefix *aux_prefix) /* optional L3 addr for L2 ITs */
+{
+ struct skiplist *sl;
+ int rc;
+ struct bgp_path_info bpi_fake = {0};
+ struct bgp_path_info_extra bpi_extra = {0};
+ struct bgp_path_info *bpi_result;
+
+ sl = RFAPI_RDINDEX(rn);
+ if (!sl)
+ return NULL;
+
+#ifdef DEBUG_BI_SEARCH
+ {
+ char buf_aux_pfx[PREFIX_STRLEN];
+
+ if (aux_prefix) {
+ prefix2str(aux_prefix, buf_aux_pfx,
+ sizeof(buf_aux_pfx));
+ } else
+ strlcpy(buf_aux_pfx, "(nil)", sizeof(buf_aux_pfx));
+
+ vnc_zlog_debug_verbose(
+ "%s want prd=%pRD, peer=%p, aux_prefix=%s", __func__,
+ prd, peer, buf_aux_pfx);
+ rfapiItBiIndexDump(rn);
+ }
+#endif
+
+ /* threshold is a WAG */
+ if (sl->count < 3) {
+#ifdef DEBUG_BI_SEARCH
+ vnc_zlog_debug_verbose("%s: short list algorithm", __func__);
+#endif
+ /* if short list, linear search might be faster */
+ for (bpi_result = rn->info; bpi_result;
+ bpi_result = bpi_result->next) {
+#ifdef DEBUG_BI_SEARCH
+ vnc_zlog_debug_verbose(
+ "%s: bpi has prd=%pRD, peer=%p", __func__,
+ &bpi_result->extra->vnc.import.rd,
+ bpi_result->peer);
+#endif
+ if (peer == bpi_result->peer
+ && !prefix_cmp((struct prefix *)&bpi_result->extra
+ ->vnc.import.rd,
+ (struct prefix *)prd)) {
+
+#ifdef DEBUG_BI_SEARCH
+ vnc_zlog_debug_verbose(
+ "%s: peer and RD same, doing aux_prefix check",
+ __func__);
+#endif
+ if (!aux_prefix
+ || !prefix_cmp(
+ aux_prefix,
+ &bpi_result->extra->vnc.import
+ .aux_prefix)) {
+
+#ifdef DEBUG_BI_SEARCH
+ vnc_zlog_debug_verbose("%s: match",
+ __func__);
+#endif
+ break;
+ }
+ }
+ }
+ return bpi_result;
+ }
+
+ bpi_fake.peer = peer;
+ bpi_fake.extra = &bpi_extra;
+ bpi_fake.extra->vnc.import.rd = *prd;
+ if (aux_prefix) {
+ bpi_fake.extra->vnc.import.aux_prefix = *aux_prefix;
+ } else {
+ /* wildcard */
+ bpi_fake.extra->vnc.import.aux_prefix.family = AF_ETHERNET;
+ bpi_fake.extra->vnc.import.aux_prefix.prefixlen = 1;
+ }
+
+ rc = skiplist_search(sl, (void *)&bpi_fake, (void *)&bpi_result);
+
+ if (rc) {
+#ifdef DEBUG_BI_SEARCH
+ vnc_zlog_debug_verbose("%s: no match", __func__);
+#endif
+ return NULL;
+ }
+
+#ifdef DEBUG_BI_SEARCH
+ vnc_zlog_debug_verbose("%s: matched bpi=%p", __func__, bpi_result);
+#endif
+
+ return bpi_result;
+}
+
+static void rfapiItBiIndexDel(struct agg_node *rn, /* Import table VPN node */
+ struct bgp_path_info *bpi) /* old BPI */
+{
+ struct skiplist *sl;
+ int rc;
+
+ vnc_zlog_debug_verbose("%s: bpi %p, peer %p, rd %pRD", __func__, bpi,
+ bpi->peer, &bpi->extra->vnc.import.rd);
+
+ sl = RFAPI_RDINDEX(rn);
+ assert(sl);
+
+ rc = skiplist_delete(sl, (void *)(bpi), (void *)bpi);
+ if (rc) {
+ rfapiItBiIndexDump(rn);
+ }
+ assert(!rc);
+
+ agg_unlock_node(rn); /* for skiplist entry */
+
+ /* NB: BPIs in import tables are not refcounted */
+}
+
+/*
+ * Add a backreference at the ENCAP node to the VPN route that
+ * refers to it
+ */
+static void
+rfapiMonitorEncapAdd(struct rfapi_import_table *import_table,
+ struct prefix *p, /* VN address */
+ struct agg_node *vpn_rn, /* VPN node */
+ struct bgp_path_info *vpn_bpi) /* VPN bpi/route */
+{
+ afi_t afi = family2afi(p->family);
+ struct agg_node *rn;
+ struct rfapi_monitor_encap *m;
+
+ assert(afi);
+ rn = agg_node_get(import_table->imported_encap[afi], p); /* locks rn */
+ assert(rn);
+
+ m = XCALLOC(MTYPE_RFAPI_MONITOR_ENCAP,
+ sizeof(struct rfapi_monitor_encap));
+
+ m->node = vpn_rn;
+ m->bpi = vpn_bpi;
+ m->rn = rn;
+
+ /* insert to encap node's list */
+ m->next = RFAPI_MONITOR_ENCAP(rn);
+ if (m->next)
+ m->next->prev = m;
+ RFAPI_MONITOR_ENCAP_W_ALLOC(rn) = m;
+
+ /* for easy lookup when deleting vpn route */
+ vpn_bpi->extra->vnc.import.hme = m;
+
+ vnc_zlog_debug_verbose(
+ "%s: it=%p, vpn_bpi=%p, afi=%d, encap rn=%p, setting vpn_bpi->extra->vnc.import.hme=%p",
+ __func__, import_table, vpn_bpi, afi, rn, m);
+
+ RFAPI_CHECK_REFCOUNT(rn, SAFI_ENCAP, 0);
+ bgp_attr_intern(vpn_bpi->attr);
+}
+
+static void rfapiMonitorEncapDelete(struct bgp_path_info *vpn_bpi)
+{
+ /*
+ * Remove encap monitor
+ */
+ vnc_zlog_debug_verbose("%s: vpn_bpi=%p", __func__, vpn_bpi);
+ if (vpn_bpi->extra) {
+ struct rfapi_monitor_encap *hme =
+ vpn_bpi->extra->vnc.import.hme;
+
+ if (hme) {
+
+ vnc_zlog_debug_verbose("%s: hme=%p", __func__, hme);
+
+ /* Refcount checking takes too long here */
+ // RFAPI_CHECK_REFCOUNT(hme->rn, SAFI_ENCAP, 0);
+ if (hme->next)
+ hme->next->prev = hme->prev;
+ if (hme->prev)
+ hme->prev->next = hme->next;
+ else
+ RFAPI_MONITOR_ENCAP_W_ALLOC(hme->rn) =
+ hme->next;
+ /* Refcount checking takes too long here */
+ // RFAPI_CHECK_REFCOUNT(hme->rn, SAFI_ENCAP, 1);
+
+ /* see if the struct rfapi_it_extra is empty and can be
+ * freed */
+ rfapiMonitorExtraPrune(SAFI_ENCAP, hme->rn);
+
+ agg_unlock_node(hme->rn); /* decr ref count */
+ XFREE(MTYPE_RFAPI_MONITOR_ENCAP, hme);
+ vpn_bpi->extra->vnc.import.hme = NULL;
+ }
+ }
+}
+
+/*
+ * Timer callback for withdraw
+ */
+static void rfapiWithdrawTimerVPN(struct thread *t)
+{
+ struct rfapi_withdraw *wcb = THREAD_ARG(t);
+ struct bgp_path_info *bpi = wcb->info;
+ struct bgp *bgp = bgp_get_default();
+ const struct prefix *p;
+ struct rfapi_monitor_vpn *moved;
+ afi_t afi;
+ bool early_exit = false;
+
+ if (bgp == NULL) {
+ vnc_zlog_debug_verbose(
+ "%s: NULL BGP pointer, assume shutdown race condition!!!",
+ __func__);
+ early_exit = true;
+ }
+ if (bgp && CHECK_FLAG(bgp->flags, BGP_FLAG_DELETE_IN_PROGRESS)) {
+ vnc_zlog_debug_verbose(
+ "%s: BGP delete in progress, assume shutdown race condition!!!",
+ __func__);
+ early_exit = true;
+ }
+
+ /* This callback is responsible for the withdraw object's memory */
+ if (early_exit) {
+ XFREE(MTYPE_RFAPI_WITHDRAW, wcb);
+ return;
+ }
+
+ assert(wcb->node);
+ assert(bpi);
+ assert(wcb->import_table);
+ assert(bpi->extra);
+
+ RFAPI_CHECK_REFCOUNT(wcb->node, SAFI_MPLS_VPN, wcb->lockoffset);
+
+ vnc_zlog_debug_verbose("%s: removing bpi %p at prefix %pRN", __func__,
+ bpi, wcb->node);
+
+ /*
+ * Remove the route (doubly-linked)
+ */
+ if (CHECK_FLAG(bpi->flags, BGP_PATH_VALID)
+ && VALID_INTERIOR_TYPE(bpi->type))
+ RFAPI_MONITOR_EXTERIOR(wcb->node)->valid_interior_count--;
+
+ p = agg_node_get_prefix(wcb->node);
+ afi = family2afi(p->family);
+ wcb->import_table->holddown_count[afi] -= 1; /* keep count consistent */
+ rfapiItBiIndexDel(wcb->node, bpi);
+ rfapiBgpInfoDetach(wcb->node, bpi); /* with removed bpi */
+
+ vnc_import_bgp_exterior_del_route_interior(bgp, wcb->import_table,
+ wcb->node, bpi);
+
+
+ /*
+ * If VNC is configured to send response remove messages, AND
+ * if the removed route had a UN address, do response removal
+ * processing.
+ */
+ if (!(bgp->rfapi_cfg->flags
+ & BGP_VNC_CONFIG_RESPONSE_REMOVAL_DISABLE)) {
+
+ int has_valid_duplicate = 0;
+ struct bgp_path_info *bpii;
+
+ /*
+ * First check if there are any OTHER routes at this node
+ * that have the same nexthop and a valid UN address. If
+ * there are (e.g., from other peers), then the route isn't
+ * really gone, so skip sending a response removal message.
+ */
+ for (bpii = wcb->node->info; bpii; bpii = bpii->next) {
+ if (rfapiVpnBiSamePtUn(bpi, bpii)) {
+ has_valid_duplicate = 1;
+ break;
+ }
+ }
+
+ vnc_zlog_debug_verbose("%s: has_valid_duplicate=%d", __func__,
+ has_valid_duplicate);
+
+ if (!has_valid_duplicate) {
+ rfapiRibPendingDeleteRoute(bgp, wcb->import_table, afi,
+ wcb->node);
+ }
+ }
+
+ rfapiMonitorEncapDelete(bpi);
+
+ /*
+ * If there are no VPN monitors at this VPN Node A,
+ * we are done
+ */
+ if (!RFAPI_MONITOR_VPN(wcb->node)) {
+ vnc_zlog_debug_verbose("%s: no VPN monitors at this node",
+ __func__);
+ goto done;
+ }
+
+ /*
+ * rfapiMonitorMoveShorter only moves monitors if there are
+ * no remaining valid routes at the current node
+ */
+ moved = rfapiMonitorMoveShorter(wcb->node, 1);
+
+ if (moved) {
+ rfapiMonitorMovedUp(wcb->import_table, wcb->node, moved->node,
+ moved);
+ }
+
+done:
+ /*
+ * Free VPN bpi
+ */
+ rfapiBgpInfoFree(bpi);
+ wcb->info = NULL;
+
+ /*
+ * If route count at this node has gone to 0, withdraw exported prefix
+ */
+ if (!wcb->node->info) {
+ /* see if the struct rfapi_it_extra is empty and can be freed */
+ rfapiMonitorExtraPrune(SAFI_MPLS_VPN, wcb->node);
+ vnc_direct_bgp_del_prefix(bgp, wcb->import_table, wcb->node);
+ vnc_zebra_del_prefix(bgp, wcb->import_table, wcb->node);
+ } else {
+ /*
+ * nexthop change event
+ * vnc_direct_bgp_add_prefix() will recompute the VN addr
+ * ecommunity
+ */
+ vnc_direct_bgp_add_prefix(bgp, wcb->import_table, wcb->node);
+ }
+
+ RFAPI_CHECK_REFCOUNT(wcb->node, SAFI_MPLS_VPN, 1 + wcb->lockoffset);
+ agg_unlock_node(wcb->node); /* decr ref count */
+ XFREE(MTYPE_RFAPI_WITHDRAW, wcb);
+}
+
+/*
+ * This works for multiprotocol extension, but not for plain ol'
+ * unicast IPv4 because that nexthop is stored in attr->nexthop
+ */
+void rfapiNexthop2Prefix(struct attr *attr, struct prefix *p)
+{
+ assert(p);
+ assert(attr);
+
+ memset(p, 0, sizeof(struct prefix));
+
+ switch (p->family = BGP_MP_NEXTHOP_FAMILY(attr->mp_nexthop_len)) {
+ case AF_INET:
+ p->u.prefix4 = attr->mp_nexthop_global_in;
+ p->prefixlen = IPV4_MAX_BITLEN;
+ break;
+
+ case AF_INET6:
+ p->u.prefix6 = attr->mp_nexthop_global;
+ p->prefixlen = IPV6_MAX_BITLEN;
+ break;
+
+ default:
+ vnc_zlog_debug_verbose("%s: Family is unknown = %d", __func__,
+ p->family);
+ }
+}
+
+void rfapiUnicastNexthop2Prefix(afi_t afi, struct attr *attr, struct prefix *p)
+{
+ if (afi == AFI_IP) {
+ p->family = AF_INET;
+ p->prefixlen = IPV4_MAX_BITLEN;
+ p->u.prefix4 = attr->nexthop;
+ } else {
+ rfapiNexthop2Prefix(attr, p);
+ }
+}
+
+static int rfapiAttrNexthopAddrDifferent(struct prefix *p1, struct prefix *p2)
+{
+ if (!p1 || !p2) {
+ vnc_zlog_debug_verbose("%s: p1 or p2 is NULL", __func__);
+ return 1;
+ }
+
+ /*
+ * Are address families the same?
+ */
+ if (p1->family != p2->family) {
+ return 1;
+ }
+
+ switch (p1->family) {
+ case AF_INET:
+ if (IPV4_ADDR_SAME(&p1->u.prefix4, &p2->u.prefix4))
+ return 0;
+ break;
+
+ case AF_INET6:
+ if (IPV6_ADDR_SAME(&p1->u.prefix6, &p2->u.prefix6))
+ return 0;
+ break;
+
+ default:
+ assert(1);
+ }
+
+ return 1;
+}
+
+static void rfapiCopyUnEncap2VPN(struct bgp_path_info *encap_bpi,
+ struct bgp_path_info *vpn_bpi)
+{
+ if (!vpn_bpi || !vpn_bpi->extra) {
+ zlog_warn("%s: no vpn bpi attr/extra, can't copy UN address",
+ __func__);
+ return;
+ }
+
+ switch (BGP_MP_NEXTHOP_FAMILY(encap_bpi->attr->mp_nexthop_len)) {
+ case AF_INET:
+
+ /*
+ * instrumentation to debug segfault of 091127
+ */
+ vnc_zlog_debug_verbose("%s: vpn_bpi=%p", __func__, vpn_bpi);
+ vnc_zlog_debug_verbose("%s: vpn_bpi->extra=%p", __func__,
+ vpn_bpi->extra);
+
+ vpn_bpi->extra->vnc.import.un_family = AF_INET;
+ vpn_bpi->extra->vnc.import.un.addr4 =
+ encap_bpi->attr->mp_nexthop_global_in;
+ break;
+
+ case AF_INET6:
+ vpn_bpi->extra->vnc.import.un_family = AF_INET6;
+ vpn_bpi->extra->vnc.import.un.addr6 =
+ encap_bpi->attr->mp_nexthop_global;
+ break;
+
+ default:
+ zlog_warn("%s: invalid encap nexthop length: %d", __func__,
+ encap_bpi->attr->mp_nexthop_len);
+ vpn_bpi->extra->vnc.import.un_family = AF_UNSPEC;
+ break;
+ }
+}
+
+/*
+ * returns 0 on success, nonzero on error
+ */
+static int
+rfapiWithdrawEncapUpdateCachedUn(struct rfapi_import_table *import_table,
+ struct bgp_path_info *encap_bpi,
+ struct agg_node *vpn_rn,
+ struct bgp_path_info *vpn_bpi)
+{
+ if (!encap_bpi) {
+
+ /*
+ * clear cached UN address
+ */
+ if (!vpn_bpi || !vpn_bpi->extra) {
+ zlog_warn(
+ "%s: missing VPN bpi/extra, can't clear UN addr",
+ __func__);
+ return 1;
+ }
+ vpn_bpi->extra->vnc.import.un_family = AF_UNSPEC;
+ memset(&vpn_bpi->extra->vnc.import.un, 0,
+ sizeof(vpn_bpi->extra->vnc.import.un));
+ if (CHECK_FLAG(vpn_bpi->flags, BGP_PATH_VALID)) {
+ if (rfapiGetVncTunnelUnAddr(vpn_bpi->attr, NULL)) {
+ UNSET_FLAG(vpn_bpi->flags, BGP_PATH_VALID);
+ if (VALID_INTERIOR_TYPE(vpn_bpi->type))
+ RFAPI_MONITOR_EXTERIOR(vpn_rn)
+ ->valid_interior_count--;
+ /* signal interior route withdrawal to
+ * import-exterior */
+ vnc_import_bgp_exterior_del_route_interior(
+ bgp_get_default(), import_table, vpn_rn,
+ vpn_bpi);
+ }
+ }
+
+ } else {
+ if (!vpn_bpi) {
+ zlog_warn("%s: missing VPN bpi, can't clear UN addr",
+ __func__);
+ return 1;
+ }
+ rfapiCopyUnEncap2VPN(encap_bpi, vpn_bpi);
+ if (!CHECK_FLAG(vpn_bpi->flags, BGP_PATH_VALID)) {
+ SET_FLAG(vpn_bpi->flags, BGP_PATH_VALID);
+ if (VALID_INTERIOR_TYPE(vpn_bpi->type))
+ RFAPI_MONITOR_EXTERIOR(vpn_rn)
+ ->valid_interior_count++;
+ /* signal interior route withdrawal to import-exterior
+ */
+ vnc_import_bgp_exterior_add_route_interior(
+ bgp_get_default(), import_table, vpn_rn,
+ vpn_bpi);
+ }
+ }
+ return 0;
+}
+
+static void rfapiWithdrawTimerEncap(struct thread *t)
+{
+ struct rfapi_withdraw *wcb = THREAD_ARG(t);
+ struct bgp_path_info *bpi = wcb->info;
+ int was_first_route = 0;
+ struct rfapi_monitor_encap *em;
+ struct skiplist *vpn_node_sl = skiplist_new(0, NULL, NULL);
+
+ assert(wcb->node);
+ assert(bpi);
+ assert(wcb->import_table);
+
+ RFAPI_CHECK_REFCOUNT(wcb->node, SAFI_ENCAP, 0);
+
+ if (wcb->node->info == bpi)
+ was_first_route = 1;
+
+ /*
+ * Remove the route/bpi and free it
+ */
+ rfapiBgpInfoDetach(wcb->node, bpi);
+ rfapiBgpInfoFree(bpi);
+
+ if (!was_first_route)
+ goto done;
+
+ for (em = RFAPI_MONITOR_ENCAP(wcb->node); em; em = em->next) {
+
+ /*
+ * Update monitoring VPN BPIs with new encap info at the
+ * head of the encap bpi chain (which could be NULL after
+ * removing the expiring bpi above)
+ */
+ if (rfapiWithdrawEncapUpdateCachedUn(wcb->import_table,
+ wcb->node->info, em->node,
+ em->bpi))
+ continue;
+
+ /*
+ * Build a list of unique VPN nodes referenced by these
+ * monitors.
+ * Use a skiplist for speed.
+ */
+ skiplist_insert(vpn_node_sl, em->node, em->node);
+ }
+
+
+ /*
+ * for each VPN node referenced in the ENCAP monitors:
+ */
+ struct agg_node *rn;
+ while (!skiplist_first(vpn_node_sl, (void **)&rn, NULL)) {
+ if (!wcb->node->info) {
+ struct rfapi_monitor_vpn *moved;
+
+ moved = rfapiMonitorMoveShorter(rn, 0);
+ if (moved) {
+ // rfapiDoRouteCallback(wcb->import_table,
+ // moved->node, moved);
+ rfapiMonitorMovedUp(wcb->import_table, rn,
+ moved->node, moved);
+ }
+ } else {
+ // rfapiDoRouteCallback(wcb->import_table, rn, NULL);
+ rfapiMonitorItNodeChanged(wcb->import_table, rn, NULL);
+ }
+ skiplist_delete_first(vpn_node_sl);
+ }
+
+done:
+ RFAPI_CHECK_REFCOUNT(wcb->node, SAFI_ENCAP, 1);
+ agg_unlock_node(wcb->node); /* decr ref count */
+ XFREE(MTYPE_RFAPI_WITHDRAW, wcb);
+ skiplist_free(vpn_node_sl);
+}
+
+
+/*
+ * Works for both VPN and ENCAP routes; timer_service_func is different
+ * in each case
+ */
+static void
+rfapiBiStartWithdrawTimer(struct rfapi_import_table *import_table,
+ struct agg_node *rn, struct bgp_path_info *bpi,
+ afi_t afi, safi_t safi,
+ void (*timer_service_func)(struct thread *))
+{
+ uint32_t lifetime;
+ struct rfapi_withdraw *wcb;
+
+ if (CHECK_FLAG(bpi->flags, BGP_PATH_REMOVED)) {
+ /*
+ * Already on the path to being withdrawn,
+ * should already have a timer set up to
+ * delete it.
+ */
+ vnc_zlog_debug_verbose(
+ "%s: already being withdrawn, do nothing", __func__);
+ return;
+ }
+
+ rfapiGetVncLifetime(bpi->attr, &lifetime);
+ vnc_zlog_debug_verbose("%s: VNC lifetime is %u", __func__, lifetime);
+
+ /*
+ * withdrawn routes get to hang around for a while
+ */
+ SET_FLAG(bpi->flags, BGP_PATH_REMOVED);
+
+ /* set timer to remove the route later */
+ lifetime = rfapiGetHolddownFromLifetime(lifetime);
+ vnc_zlog_debug_verbose("%s: using timeout %u", __func__, lifetime);
+
+ /*
+ * Stash import_table, node, and info for use by timer
+ * service routine, which is supposed to free the wcb.
+ */
+ wcb = XCALLOC(MTYPE_RFAPI_WITHDRAW, sizeof(struct rfapi_withdraw));
+ wcb->node = rn;
+ wcb->info = bpi;
+ wcb->import_table = import_table;
+ bgp_attr_intern(bpi->attr);
+
+ if (VNC_DEBUG(VERBOSE)) {
+ vnc_zlog_debug_verbose(
+ "%s: wcb values: node=%p, info=%p, import_table=%p (bpi follows)",
+ __func__, wcb->node, wcb->info, wcb->import_table);
+ rfapiPrintBi(NULL, bpi);
+ }
+
+
+ assert(bpi->extra);
+ if (lifetime > UINT32_MAX / 1001) {
+ /* sub-optimal case, but will probably never happen */
+ bpi->extra->vnc.import.timer = NULL;
+ thread_add_timer(bm->master, timer_service_func, wcb, lifetime,
+ &bpi->extra->vnc.import.timer);
+ } else {
+ static uint32_t jitter;
+ uint32_t lifetime_msec;
+
+ /*
+ * the goal here is to spread out the timers so they are
+ * sortable in the skip list
+ */
+ if (++jitter >= 1000)
+ jitter = 0;
+
+ lifetime_msec = (lifetime * 1000) + jitter;
+
+ bpi->extra->vnc.import.timer = NULL;
+ thread_add_timer_msec(bm->master, timer_service_func, wcb,
+ lifetime_msec,
+ &bpi->extra->vnc.import.timer);
+ }
+
+ /* re-sort route list (BGP_PATH_REMOVED routes are last) */
+ if (((struct bgp_path_info *)rn->info)->next) {
+ rfapiBgpInfoDetach(rn, bpi);
+ rfapiBgpInfoAttachSorted(rn, bpi, afi, safi);
+ }
+}
+
+
+typedef void(rfapi_bi_filtered_import_f)(struct rfapi_import_table *table,
+ int action, struct peer *peer,
+ void *rfd, const struct prefix *prefix,
+ const struct prefix *aux_prefix,
+ afi_t afi, struct prefix_rd *prd,
+ struct attr *attr, uint8_t type,
+ uint8_t sub_type, uint32_t *label);
+
+
+static void rfapiExpireEncapNow(struct rfapi_import_table *it,
+ struct agg_node *rn, struct bgp_path_info *bpi)
+{
+ struct rfapi_withdraw *wcb;
+ struct thread t;
+
+ /*
+ * pretend we're an expiring timer
+ */
+ wcb = XCALLOC(MTYPE_RFAPI_WITHDRAW, sizeof(struct rfapi_withdraw));
+ wcb->info = bpi;
+ wcb->node = rn;
+ wcb->import_table = it;
+ memset(&t, 0, sizeof(t));
+ t.arg = wcb;
+ rfapiWithdrawTimerEncap(&t); /* frees wcb */
+}
+
+static int rfapiGetNexthop(struct attr *attr, struct prefix *prefix)
+{
+ switch (BGP_MP_NEXTHOP_FAMILY(attr->mp_nexthop_len)) {
+ case AF_INET:
+ prefix->family = AF_INET;
+ prefix->prefixlen = IPV4_MAX_BITLEN;
+ prefix->u.prefix4 = attr->mp_nexthop_global_in;
+ break;
+ case AF_INET6:
+ prefix->family = AF_INET6;
+ prefix->prefixlen = IPV6_MAX_BITLEN;
+ prefix->u.prefix6 = attr->mp_nexthop_global;
+ break;
+ default:
+ vnc_zlog_debug_verbose("%s: unknown attr->mp_nexthop_len %d",
+ __func__, attr->mp_nexthop_len);
+ return EINVAL;
+ }
+ return 0;
+}
+
+/*
+ * import a bgp_path_info if its route target list intersects with the
+ * import table's route target list
+ */
+static void rfapiBgpInfoFilteredImportEncap(
+ struct rfapi_import_table *import_table, int action, struct peer *peer,
+ void *rfd, /* set for looped back routes */
+ const struct prefix *p,
+ const struct prefix *aux_prefix, /* Unused for encap routes */
+ afi_t afi, struct prefix_rd *prd,
+ struct attr *attr, /* part of bgp_path_info */
+ uint8_t type, /* part of bgp_path_info */
+ uint8_t sub_type, /* part of bgp_path_info */
+ uint32_t *label) /* part of bgp_path_info */
+{
+ struct agg_table *rt = NULL;
+ struct agg_node *rn;
+ struct bgp_path_info *info_new;
+ struct bgp_path_info *bpi;
+ struct bgp_path_info *next;
+ char buf[BUFSIZ];
+
+ struct prefix p_firstbpi_old;
+ struct prefix p_firstbpi_new;
+ int replacing = 0;
+ const char *action_str = NULL;
+ struct prefix un_prefix;
+
+ struct bgp *bgp;
+ bgp = bgp_get_default(); /* assume 1 instance for now */
+
+ switch (action) {
+ case FIF_ACTION_UPDATE:
+ action_str = "update";
+ break;
+ case FIF_ACTION_WITHDRAW:
+ action_str = "withdraw";
+ break;
+ case FIF_ACTION_KILL:
+ action_str = "kill";
+ break;
+ default:
+ assert(0);
+ break;
+ }
+
+ vnc_zlog_debug_verbose(
+ "%s: entry: %s: prefix %s/%d", __func__, action_str,
+ inet_ntop(p->family, &p->u.prefix, buf, BUFSIZ), p->prefixlen);
+
+ memset(&p_firstbpi_old, 0, sizeof(p_firstbpi_old));
+ memset(&p_firstbpi_new, 0, sizeof(p_firstbpi_new));
+
+ if (action == FIF_ACTION_UPDATE) {
+ /*
+ * Compare rt lists. If no intersection, don't import this route
+ * On a withdraw, peer and RD are sufficient to determine if
+ * we should act.
+ */
+ if (!attr || !bgp_attr_get_ecommunity(attr)) {
+
+ vnc_zlog_debug_verbose(
+ "%s: attr, extra, or ecommunity missing, not importing",
+ __func__);
+ return;
+ }
+#ifdef RFAPI_REQUIRE_ENCAP_BEEC
+ if (!rfapiEcommunitiesMatchBeec(
+ bgp_attr_get_ecommunity(attr))) {
+ vnc_zlog_debug_verbose(
+ "%s: it=%p: no match for BGP Encapsulation ecommunity",
+ __func__, import_table);
+ return;
+ }
+#endif
+ if (!rfapiEcommunitiesIntersect(
+ import_table->rt_import_list,
+ bgp_attr_get_ecommunity(attr))) {
+
+ vnc_zlog_debug_verbose(
+ "%s: it=%p: no ecommunity intersection",
+ __func__, import_table);
+ return;
+ }
+
+ /*
+ * Updates must also have a nexthop address
+ */
+ memset(&un_prefix, 0,
+ sizeof(un_prefix)); /* keep valgrind happy */
+ if (rfapiGetNexthop(attr, &un_prefix)) {
+ vnc_zlog_debug_verbose("%s: missing nexthop address",
+ __func__);
+ return;
+ }
+ }
+
+ /*
+ * Figure out which radix tree the route would go into
+ */
+ switch (afi) {
+ case AFI_IP:
+ case AFI_IP6:
+ rt = import_table->imported_encap[afi];
+ break;
+
+ default:
+ flog_err(EC_LIB_DEVELOPMENT, "%s: bad afi %d", __func__, afi);
+ return;
+ }
+
+ /*
+ * agg_node_lookup returns a node only if there is at least
+ * one route attached.
+ */
+ rn = agg_node_lookup(rt, p);
+
+#ifdef DEBUG_ENCAP_MONITOR
+ vnc_zlog_debug_verbose("%s: initial encap lookup(it=%p) rn=%p",
+ __func__, import_table, rn);
+#endif
+
+ if (rn) {
+
+ RFAPI_CHECK_REFCOUNT(rn, SAFI_ENCAP, 1);
+ agg_unlock_node(rn); /* undo lock in agg_node_lookup */
+
+
+ /*
+ * capture nexthop of first bpi
+ */
+ if (rn->info) {
+ rfapiNexthop2Prefix(
+ ((struct bgp_path_info *)(rn->info))->attr,
+ &p_firstbpi_old);
+ }
+
+ for (bpi = rn->info; bpi; bpi = bpi->next) {
+
+ /*
+ * Does this bgp_path_info refer to the same route
+ * as we are trying to add?
+ */
+ vnc_zlog_debug_verbose("%s: comparing BPI %p", __func__,
+ bpi);
+
+
+ /*
+ * Compare RDs
+ *
+ * RD of import table bpi is in
+ * bpi->extra->vnc.import.rd RD of info_orig is in prd
+ */
+ if (!bpi->extra) {
+ vnc_zlog_debug_verbose("%s: no bpi->extra",
+ __func__);
+ continue;
+ }
+ if (prefix_cmp(
+ (struct prefix *)&bpi->extra->vnc.import.rd,
+ (struct prefix *)prd)) {
+
+ vnc_zlog_debug_verbose("%s: prd does not match",
+ __func__);
+ continue;
+ }
+
+ /*
+ * Compare peers
+ */
+ if (bpi->peer != peer) {
+ vnc_zlog_debug_verbose(
+ "%s: peer does not match", __func__);
+ continue;
+ }
+
+ vnc_zlog_debug_verbose("%s: found matching bpi",
+ __func__);
+
+ /* Same route. Delete this bpi, replace with new one */
+
+ if (action == FIF_ACTION_WITHDRAW) {
+
+ vnc_zlog_debug_verbose(
+ "%s: withdrawing at prefix %pRN",
+ __func__, rn);
+
+ rfapiBiStartWithdrawTimer(
+ import_table, rn, bpi, afi, SAFI_ENCAP,
+ rfapiWithdrawTimerEncap);
+
+ } else {
+ vnc_zlog_debug_verbose(
+ "%s: %s at prefix %pRN", __func__,
+ ((action == FIF_ACTION_KILL)
+ ? "killing"
+ : "replacing"),
+ rn);
+
+ /*
+ * If this route is waiting to be deleted
+ * because of
+ * a previous withdraw, we must cancel its
+ * timer.
+ */
+ if (CHECK_FLAG(bpi->flags, BGP_PATH_REMOVED)
+ && bpi->extra->vnc.import.timer) {
+ struct rfapi_withdraw *wcb = THREAD_ARG(
+ bpi->extra->vnc.import.timer);
+
+ XFREE(MTYPE_RFAPI_WITHDRAW, wcb);
+ THREAD_OFF(
+ bpi->extra->vnc.import.timer);
+ }
+
+ if (action == FIF_ACTION_UPDATE) {
+ rfapiBgpInfoDetach(rn, bpi);
+ rfapiBgpInfoFree(bpi);
+ replacing = 1;
+ } else {
+ /*
+ * Kill: do export stuff when removing
+ * bpi
+ */
+ struct rfapi_withdraw *wcb;
+ struct thread t;
+
+ /*
+ * pretend we're an expiring timer
+ */
+ wcb = XCALLOC(
+ MTYPE_RFAPI_WITHDRAW,
+ sizeof(struct rfapi_withdraw));
+ wcb->info = bpi;
+ wcb->node = rn;
+ wcb->import_table = import_table;
+ memset(&t, 0, sizeof(t));
+ t.arg = wcb;
+ rfapiWithdrawTimerEncap(
+ &t); /* frees wcb */
+ }
+ }
+
+ break;
+ }
+ }
+
+ if (rn)
+ RFAPI_CHECK_REFCOUNT(rn, SAFI_ENCAP, replacing ? 1 : 0);
+
+ if (action == FIF_ACTION_WITHDRAW || action == FIF_ACTION_KILL)
+ return;
+
+ info_new =
+ rfapiBgpInfoCreate(attr, peer, rfd, prd, type, sub_type, NULL);
+
+ if (rn) {
+ if (!replacing)
+ agg_lock_node(rn); /* incr ref count for new BPI */
+ } else {
+ rn = agg_node_get(rt, p);
+ }
+
+ vnc_zlog_debug_verbose("%s: (afi=%d, rn=%p) inserting at prefix %pRN",
+ __func__, afi, rn, rn);
+
+ rfapiBgpInfoAttachSorted(rn, info_new, afi, SAFI_ENCAP);
+
+ /*
+ * Delete holddown routes from same NVE. See details in
+ * rfapiBgpInfoFilteredImportVPN()
+ */
+ for (bpi = info_new->next; bpi; bpi = next) {
+
+ struct prefix pfx_un;
+ int un_match = 0;
+
+ next = bpi->next;
+ if (!CHECK_FLAG(bpi->flags, BGP_PATH_REMOVED))
+ continue;
+
+ /*
+ * We already match the VN address (it is the prefix
+ * of the route node)
+ */
+
+ if (!rfapiGetNexthop(bpi->attr, &pfx_un)
+ && prefix_same(&pfx_un, &un_prefix)) {
+
+ un_match = 1;
+ }
+
+ if (!un_match)
+ continue;
+
+ vnc_zlog_debug_verbose(
+ "%s: removing holddown bpi matching NVE of new route",
+ __func__);
+ if (bpi->extra->vnc.import.timer) {
+ struct rfapi_withdraw *wcb =
+ THREAD_ARG(bpi->extra->vnc.import.timer);
+
+ XFREE(MTYPE_RFAPI_WITHDRAW, wcb);
+ THREAD_OFF(bpi->extra->vnc.import.timer);
+ }
+ rfapiExpireEncapNow(import_table, rn, bpi);
+ }
+
+ rfapiNexthop2Prefix(((struct bgp_path_info *)(rn->info))->attr,
+ &p_firstbpi_new);
+
+ /*
+ * If the nexthop address of the selected Encap route (i.e.,
+ * the UN address) has changed, then we must update the VPN
+ * routes that refer to this Encap route and possibly force
+ * rfapi callbacks.
+ */
+ if (rfapiAttrNexthopAddrDifferent(&p_firstbpi_old, &p_firstbpi_new)) {
+
+ struct rfapi_monitor_encap *m;
+ struct rfapi_monitor_encap *mnext;
+
+ struct agg_node *referenced_vpn_prefix;
+
+ /*
+ * Optimized approach: build radix tree on the fly to
+ * hold list of VPN nodes referenced by the ENCAP monitors
+ *
+ * The nodes in this table correspond to prefixes of VPN routes.
+ * The "info" pointer of the node points to a chain of
+ * struct rfapi_monitor_encap, each of which refers to a
+ * specific VPN node.
+ */
+ struct agg_table *referenced_vpn_table;
+
+ referenced_vpn_table = agg_table_init();
+
+/*
+ * iterate over the set of monitors at this ENCAP node.
+ */
+#ifdef DEBUG_ENCAP_MONITOR
+ vnc_zlog_debug_verbose("%s: examining monitors at rn=%p",
+ __func__, rn);
+#endif
+ for (m = RFAPI_MONITOR_ENCAP(rn); m; m = m->next) {
+ const struct prefix *p;
+
+ /*
+ * For each referenced bpi/route, copy the ENCAP route's
+ * nexthop to the VPN route's cached UN address field
+ * and set
+ * the address family of the cached UN address field.
+ */
+ rfapiCopyUnEncap2VPN(info_new, m->bpi);
+ if (!CHECK_FLAG(m->bpi->flags, BGP_PATH_VALID)) {
+ SET_FLAG(m->bpi->flags, BGP_PATH_VALID);
+ if (VALID_INTERIOR_TYPE(m->bpi->type))
+ RFAPI_MONITOR_EXTERIOR(m->node)
+ ->valid_interior_count++;
+ vnc_import_bgp_exterior_add_route_interior(
+ bgp, import_table, m->node, m->bpi);
+ }
+
+ /*
+ * Build a list of unique VPN nodes referenced by these
+ * monitors
+ *
+ * There could be more than one VPN node here with a
+ * given
+ * prefix. Those are currently in an unsorted linear
+ * list
+ * per prefix.
+ */
+ p = agg_node_get_prefix(m->node);
+ referenced_vpn_prefix =
+ agg_node_get(referenced_vpn_table, p);
+ assert(referenced_vpn_prefix);
+ for (mnext = referenced_vpn_prefix->info; mnext;
+ mnext = mnext->next) {
+
+ if (mnext->node == m->node)
+ break;
+ }
+
+ if (mnext) {
+ /*
+ * already have an entry for this VPN node
+ */
+ agg_unlock_node(referenced_vpn_prefix);
+ } else {
+ mnext = XCALLOC(
+ MTYPE_RFAPI_MONITOR_ENCAP,
+ sizeof(struct rfapi_monitor_encap));
+ mnext->node = m->node;
+ mnext->next = referenced_vpn_prefix->info;
+ referenced_vpn_prefix->info = mnext;
+ }
+ }
+
+ /*
+ * for each VPN node referenced in the ENCAP monitors:
+ */
+ for (referenced_vpn_prefix =
+ agg_route_top(referenced_vpn_table);
+ referenced_vpn_prefix;
+ referenced_vpn_prefix =
+ agg_route_next(referenced_vpn_prefix)) {
+
+ while ((m = referenced_vpn_prefix->info)) {
+
+ struct agg_node *n;
+
+ rfapiMonitorMoveLonger(m->node);
+ for (n = m->node; n; n = agg_node_parent(n)) {
+ // rfapiDoRouteCallback(import_table, n,
+ // NULL);
+ }
+ rfapiMonitorItNodeChanged(import_table, m->node,
+ NULL);
+
+ referenced_vpn_prefix->info = m->next;
+ agg_unlock_node(referenced_vpn_prefix);
+ XFREE(MTYPE_RFAPI_MONITOR_ENCAP, m);
+ }
+ }
+ agg_table_finish(referenced_vpn_table);
+ }
+
+ RFAPI_CHECK_REFCOUNT(rn, SAFI_ENCAP, 0);
+}
+
+static void rfapiExpireVpnNow(struct rfapi_import_table *it,
+ struct agg_node *rn, struct bgp_path_info *bpi,
+ int lockoffset)
+{
+ struct rfapi_withdraw *wcb;
+ struct thread t;
+
+ /*
+ * pretend we're an expiring timer
+ */
+ wcb = XCALLOC(MTYPE_RFAPI_WITHDRAW, sizeof(struct rfapi_withdraw));
+ wcb->info = bpi;
+ wcb->node = rn;
+ wcb->import_table = it;
+ wcb->lockoffset = lockoffset;
+ memset(&t, 0, sizeof(t));
+ t.arg = wcb;
+ rfapiWithdrawTimerVPN(&t); /* frees wcb */
+}
+
+
+/*
+ * import a bgp_path_info if its route target list intersects with the
+ * import table's route target list
+ */
+void rfapiBgpInfoFilteredImportVPN(
+ struct rfapi_import_table *import_table, int action, struct peer *peer,
+ void *rfd, /* set for looped back routes */
+ const struct prefix *p,
+ const struct prefix *aux_prefix, /* AFI_L2VPN: optional IP */
+ afi_t afi, struct prefix_rd *prd,
+ struct attr *attr, /* part of bgp_path_info */
+ uint8_t type, /* part of bgp_path_info */
+ uint8_t sub_type, /* part of bgp_path_info */
+ uint32_t *label) /* part of bgp_path_info */
+{
+ struct agg_table *rt = NULL;
+ struct agg_node *rn;
+ struct agg_node *n;
+ struct bgp_path_info *info_new;
+ struct bgp_path_info *bpi;
+ struct bgp_path_info *next;
+ char buf[BUFSIZ];
+ struct prefix vn_prefix;
+ struct prefix un_prefix;
+ int un_prefix_valid = 0;
+ struct agg_node *ern;
+ int replacing = 0;
+ int original_had_routes = 0;
+ struct prefix original_nexthop;
+ const char *action_str = NULL;
+ int is_it_ce = 0;
+
+ struct bgp *bgp;
+ bgp = bgp_get_default(); /* assume 1 instance for now */
+
+ switch (action) {
+ case FIF_ACTION_UPDATE:
+ action_str = "update";
+ break;
+ case FIF_ACTION_WITHDRAW:
+ action_str = "withdraw";
+ break;
+ case FIF_ACTION_KILL:
+ action_str = "kill";
+ break;
+ default:
+ assert(0);
+ break;
+ }
+
+ if (import_table == bgp->rfapi->it_ce)
+ is_it_ce = 1;
+
+ vnc_zlog_debug_verbose("%s: entry: %s%s: prefix %s/%d: it %p, afi %s",
+ __func__, (is_it_ce ? "CE-IT " : ""), action_str,
+ rfapi_ntop(p->family, &p->u.prefix, buf, BUFSIZ),
+ p->prefixlen, import_table, afi2str(afi));
+
+ VNC_ITRCCK;
+
+ /*
+ * Compare rt lists. If no intersection, don't import this route
+ * On a withdraw, peer and RD are sufficient to determine if
+ * we should act.
+ */
+ if (action == FIF_ACTION_UPDATE) {
+ if (!attr || !bgp_attr_get_ecommunity(attr)) {
+
+ vnc_zlog_debug_verbose(
+ "%s: attr, extra, or ecommunity missing, not importing",
+ __func__);
+ return;
+ }
+ if ((import_table != bgp->rfapi->it_ce) &&
+ !rfapiEcommunitiesIntersect(
+ import_table->rt_import_list,
+ bgp_attr_get_ecommunity(attr))) {
+
+ vnc_zlog_debug_verbose(
+ "%s: it=%p: no ecommunity intersection",
+ __func__, import_table);
+ return;
+ }
+
+ memset(&vn_prefix, 0,
+ sizeof(vn_prefix)); /* keep valgrind happy */
+ if (rfapiGetNexthop(attr, &vn_prefix)) {
+ /* missing nexthop address would be a bad, bad thing */
+ vnc_zlog_debug_verbose("%s: missing nexthop", __func__);
+ return;
+ }
+ }
+
+ /*
+ * Figure out which radix tree the route would go into
+ */
+ switch (afi) {
+ case AFI_IP:
+ case AFI_IP6:
+ case AFI_L2VPN:
+ rt = import_table->imported_vpn[afi];
+ break;
+
+ default:
+ flog_err(EC_LIB_DEVELOPMENT, "%s: bad afi %d", __func__, afi);
+ return;
+ }
+
+ /* clear it */
+ memset(&original_nexthop, 0, sizeof(original_nexthop));
+
+ /*
+ * agg_node_lookup returns a node only if there is at least
+ * one route attached.
+ */
+ rn = agg_node_lookup(rt, p);
+
+ vnc_zlog_debug_verbose("%s: rn=%p", __func__, rn);
+
+ if (rn) {
+
+ RFAPI_CHECK_REFCOUNT(rn, SAFI_MPLS_VPN, 1);
+ agg_unlock_node(rn); /* undo lock in agg_node_lookup */
+
+ if (rn->info)
+ original_had_routes = 1;
+
+ if (VNC_DEBUG(VERBOSE)) {
+ vnc_zlog_debug_verbose("%s: showing IT node on entry",
+ __func__);
+ rfapiShowItNode(NULL, rn); /* debug */
+ }
+
+ /*
+ * Look for same route (will have same RD and peer)
+ */
+ bpi = rfapiItBiIndexSearch(rn, prd, peer, aux_prefix);
+
+ if (bpi) {
+
+ /*
+ * This was an old test when we iterated over the
+ * BPIs linearly. Since we're now looking up with
+ * RD and peer, comparing types should not be
+ * needed. Changed to assertion.
+ *
+ * Compare types. Doing so prevents a RFP-originated
+ * route from matching an imported route, for example.
+ */
+ if (VNC_DEBUG(VERBOSE) && bpi->type != type)
+ /* should be handled by RDs, but warn for now */
+ zlog_warn("%s: type mismatch! (bpi=%d, arg=%d)",
+ __func__, bpi->type, type);
+
+ vnc_zlog_debug_verbose("%s: found matching bpi",
+ __func__);
+
+ /*
+ * In the special CE table, withdrawals occur without
+ * holddown
+ */
+ if (import_table == bgp->rfapi->it_ce) {
+ vnc_direct_bgp_del_route_ce(bgp, rn, bpi);
+ if (action == FIF_ACTION_WITHDRAW)
+ action = FIF_ACTION_KILL;
+ }
+
+ if (action == FIF_ACTION_WITHDRAW) {
+
+ int washolddown = CHECK_FLAG(bpi->flags,
+ BGP_PATH_REMOVED);
+
+ vnc_zlog_debug_verbose(
+ "%s: withdrawing at prefix %pRN%s",
+ __func__, rn,
+ (washolddown
+ ? " (already being withdrawn)"
+ : ""));
+
+ VNC_ITRCCK;
+ if (!washolddown) {
+ rfapiBiStartWithdrawTimer(
+ import_table, rn, bpi, afi,
+ SAFI_MPLS_VPN,
+ rfapiWithdrawTimerVPN);
+
+ RFAPI_UPDATE_ITABLE_COUNT(
+ bpi, import_table, afi, -1);
+ import_table->holddown_count[afi] += 1;
+ }
+ VNC_ITRCCK;
+ } else {
+ vnc_zlog_debug_verbose(
+ "%s: %s at prefix %pRN", __func__,
+ ((action == FIF_ACTION_KILL)
+ ? "killing"
+ : "replacing"),
+ rn);
+
+ /*
+ * If this route is waiting to be deleted
+ * because of
+ * a previous withdraw, we must cancel its
+ * timer.
+ */
+ if (CHECK_FLAG(bpi->flags, BGP_PATH_REMOVED)
+ && bpi->extra->vnc.import.timer) {
+ struct rfapi_withdraw *wcb = THREAD_ARG(
+ bpi->extra->vnc.import.timer);
+
+ XFREE(MTYPE_RFAPI_WITHDRAW, wcb);
+ THREAD_OFF(
+ bpi->extra->vnc.import.timer);
+
+ import_table->holddown_count[afi] -= 1;
+ RFAPI_UPDATE_ITABLE_COUNT(
+ bpi, import_table, afi, 1);
+ }
+ /*
+ * decrement remote count (if route is remote)
+ * because
+ * we are going to remove it below
+ */
+ RFAPI_UPDATE_ITABLE_COUNT(bpi, import_table,
+ afi, -1);
+ if (action == FIF_ACTION_UPDATE) {
+ replacing = 1;
+
+ /*
+ * make copy of original nexthop so we
+ * can see if it changed
+ */
+ rfapiGetNexthop(bpi->attr,
+ &original_nexthop);
+
+ /*
+ * remove bpi without doing any export
+ * processing
+ */
+ if (CHECK_FLAG(bpi->flags,
+ BGP_PATH_VALID)
+ && VALID_INTERIOR_TYPE(bpi->type))
+ RFAPI_MONITOR_EXTERIOR(rn)
+ ->valid_interior_count--;
+ rfapiItBiIndexDel(rn, bpi);
+ rfapiBgpInfoDetach(rn, bpi);
+ rfapiMonitorEncapDelete(bpi);
+ vnc_import_bgp_exterior_del_route_interior(
+ bgp, import_table, rn, bpi);
+ rfapiBgpInfoFree(bpi);
+ } else {
+ /* Kill */
+ /*
+ * remove bpi and do export processing
+ */
+ import_table->holddown_count[afi] += 1;
+ rfapiExpireVpnNow(import_table, rn, bpi,
+ 0);
+ }
+ }
+ }
+ }
+
+ if (rn)
+ RFAPI_CHECK_REFCOUNT(rn, SAFI_MPLS_VPN, replacing ? 1 : 0);
+
+ if (action == FIF_ACTION_WITHDRAW || action == FIF_ACTION_KILL) {
+ VNC_ITRCCK;
+ return;
+ }
+
+ info_new =
+ rfapiBgpInfoCreate(attr, peer, rfd, prd, type, sub_type, label);
+
+ /*
+ * lookup un address in encap table
+ */
+ ern = agg_node_match(import_table->imported_encap[afi], &vn_prefix);
+ if (ern) {
+ rfapiCopyUnEncap2VPN(ern->info, info_new);
+ agg_unlock_node(ern); /* undo lock in route_note_match */
+ } else {
+ /* Not a big deal, just means VPN route got here first */
+ vnc_zlog_debug_verbose("%s: no encap route for vn addr %pFX",
+ __func__, &vn_prefix);
+ info_new->extra->vnc.import.un_family = AF_UNSPEC;
+ }
+
+ if (rn) {
+ if (!replacing)
+ agg_lock_node(rn);
+ } else {
+ /*
+ * No need to increment reference count, so only "get"
+ * if the node is not there already
+ */
+ rn = agg_node_get(rt, p);
+ }
+
+ /*
+ * For ethernet routes, if there is an accompanying IP address,
+ * save it in the bpi
+ */
+ if ((AFI_L2VPN == afi) && aux_prefix) {
+
+ vnc_zlog_debug_verbose("%s: setting BPI's aux_prefix",
+ __func__);
+ info_new->extra->vnc.import.aux_prefix = *aux_prefix;
+ }
+
+ vnc_zlog_debug_verbose("%s: inserting bpi %p at prefix %pRN #%d",
+ __func__, info_new, rn,
+ agg_node_get_lock_count(rn));
+
+ rfapiBgpInfoAttachSorted(rn, info_new, afi, SAFI_MPLS_VPN);
+ rfapiItBiIndexAdd(rn, info_new);
+ if (!rfapiGetUnAddrOfVpnBi(info_new, NULL)) {
+ if (VALID_INTERIOR_TYPE(info_new->type))
+ RFAPI_MONITOR_EXTERIOR(rn)->valid_interior_count++;
+ SET_FLAG(info_new->flags, BGP_PATH_VALID);
+ }
+ RFAPI_UPDATE_ITABLE_COUNT(info_new, import_table, afi, 1);
+ vnc_import_bgp_exterior_add_route_interior(bgp, import_table, rn,
+ info_new);
+
+ if (import_table == bgp->rfapi->it_ce)
+ vnc_direct_bgp_add_route_ce(bgp, rn, info_new);
+
+ if (VNC_DEBUG(VERBOSE)) {
+ vnc_zlog_debug_verbose("%s: showing IT node", __func__);
+ rfapiShowItNode(NULL, rn); /* debug */
+ }
+
+ rfapiMonitorEncapAdd(import_table, &vn_prefix, rn, info_new);
+
+ if (!rfapiGetUnAddrOfVpnBi(info_new, &un_prefix)) {
+
+ /*
+ * if we have a valid UN address (either via Encap route
+ * or via tunnel attribute), then we should attempt
+ * to move any monitors at less-specific nodes to this node
+ */
+ rfapiMonitorMoveLonger(rn);
+
+ un_prefix_valid = 1;
+ }
+
+ /*
+ * 101129 Enhancement: if we add a route (implication: it is not
+ * in holddown), delete all other routes from this nve at this
+ * node that are in holddown, regardless of peer.
+ *
+ * Reasons it's OK to do that:
+ *
+ * - if the holddown route being deleted originally came from BGP VPN,
+ * it is already gone from BGP (implication of holddown), so there
+ * won't be any added inconsistency with the BGP RIB.
+ *
+ * - once a fresh route is added at a prefix, any routes in holddown
+ * at that prefix will not show up in RFP responses, so deleting
+ * the holddown routes won't affect the contents of responses.
+ *
+ * - lifetimes are supposed to be consistent, so there should not
+ * be a case where the fresh route has a shorter lifetime than
+ * the holddown route, so we don't expect the fresh route to
+ * disappear and complete its holddown time before the existing
+ * holddown routes time out. Therefore, we won't have a situation
+ * where we expect the existing holddown routes to be hidden and
+ * then to reappear sometime later (as holddown routes) in a
+ * RFP response.
+ *
+ * Among other things, this would enable us to skirt the problem
+ * of local holddown routes that refer to NVE descriptors that
+ * have already been closed (if the same NVE triggers a subsequent
+ * rfapi_open(), the new peer is different and doesn't match the
+ * peer of the holddown route, so the stale holddown route still
+ * hangs around until it times out instead of just being replaced
+ * by the fresh route).
+ */
+ /*
+ * We know that the new bpi will have been inserted before any routes
+ * in holddown, so we can skip any that came before it
+ */
+ for (bpi = info_new->next; bpi; bpi = next) {
+
+ struct prefix pfx_vn;
+ struct prefix pfx_un;
+ int un_match = 0;
+ int remote_peer_match = 0;
+
+ next = bpi->next;
+
+ /*
+ * Must be holddown
+ */
+ if (!CHECK_FLAG(bpi->flags, BGP_PATH_REMOVED))
+ continue;
+
+ /*
+ * Must match VN address (nexthop of VPN route)
+ */
+ if (rfapiGetNexthop(bpi->attr, &pfx_vn))
+ continue;
+ if (!prefix_same(&pfx_vn, &vn_prefix))
+ continue;
+
+ if (un_prefix_valid && /* new route UN addr */
+ !rfapiGetUnAddrOfVpnBi(bpi, &pfx_un)
+ && /* old route UN addr */
+ prefix_same(&pfx_un, &un_prefix)) { /* compare */
+ un_match = 1;
+ }
+ if (!RFAPI_LOCAL_BI(bpi) && !RFAPI_LOCAL_BI(info_new)
+ && sockunion_same(&bpi->peer->su, &info_new->peer->su)) {
+ /* old & new are both remote, same peer */
+ remote_peer_match = 1;
+ }
+
+ if (!un_match && !remote_peer_match)
+ continue;
+
+ vnc_zlog_debug_verbose(
+ "%s: removing holddown bpi matching NVE of new route",
+ __func__);
+ if (bpi->extra->vnc.import.timer) {
+ struct rfapi_withdraw *wcb =
+ THREAD_ARG(bpi->extra->vnc.import.timer);
+
+ XFREE(MTYPE_RFAPI_WITHDRAW, wcb);
+ THREAD_OFF(bpi->extra->vnc.import.timer);
+ }
+ rfapiExpireVpnNow(import_table, rn, bpi, 0);
+ }
+
+ if (!original_had_routes) {
+ /*
+ * We went from 0 usable routes to 1 usable route. Perform the
+ * "Adding a Route" export process.
+ */
+ vnc_direct_bgp_add_prefix(bgp, import_table, rn);
+ vnc_zebra_add_prefix(bgp, import_table, rn);
+ } else {
+ /*
+ * Check for nexthop change event
+ * Note: the prefix_same() test below detects two situations:
+ * 1. route is replaced, new route has different nexthop
+ * 2. new route is added (original_nexthop is 0)
+ */
+ struct prefix new_nexthop;
+
+ rfapiGetNexthop(attr, &new_nexthop);
+ if (!prefix_same(&original_nexthop, &new_nexthop)) {
+ /*
+ * nexthop change event
+ * vnc_direct_bgp_add_prefix() will recompute VN addr
+ * ecommunity
+ */
+ vnc_direct_bgp_add_prefix(bgp, import_table, rn);
+ }
+ }
+
+ if (!(bgp->rfapi_cfg->flags & BGP_VNC_CONFIG_CALLBACK_DISABLE)) {
+ for (n = rn; n; n = agg_node_parent(n)) {
+ // rfapiDoRouteCallback(import_table, n, NULL);
+ }
+ rfapiMonitorItNodeChanged(import_table, rn, NULL);
+ }
+ RFAPI_CHECK_REFCOUNT(rn, SAFI_MPLS_VPN, 0);
+ VNC_ITRCCK;
+}
+
+static void rfapiBgpInfoFilteredImportBadSafi(
+ struct rfapi_import_table *import_table, int action, struct peer *peer,
+ void *rfd, /* set for looped back routes */
+ const struct prefix *p,
+ const struct prefix *aux_prefix, /* AFI_L2VPN: optional IP */
+ afi_t afi, struct prefix_rd *prd,
+ struct attr *attr, /* part of bgp_path_info */
+ uint8_t type, /* part of bgp_path_info */
+ uint8_t sub_type, /* part of bgp_path_info */
+ uint32_t *label) /* part of bgp_path_info */
+{
+ vnc_zlog_debug_verbose("%s: Error, bad safi", __func__);
+}
+
+static rfapi_bi_filtered_import_f *
+rfapiBgpInfoFilteredImportFunction(safi_t safi)
+{
+ switch (safi) {
+ case SAFI_MPLS_VPN:
+ return rfapiBgpInfoFilteredImportVPN;
+
+ case SAFI_ENCAP:
+ return rfapiBgpInfoFilteredImportEncap;
+
+ default:
+ /* not expected */
+ flog_err(EC_LIB_DEVELOPMENT, "%s: bad safi %d", __func__, safi);
+ return rfapiBgpInfoFilteredImportBadSafi;
+ }
+}
+
+void rfapiProcessUpdate(struct peer *peer,
+ void *rfd, /* set when looped from RFP/RFAPI */
+ const struct prefix *p, struct prefix_rd *prd,
+ struct attr *attr, afi_t afi, safi_t safi, uint8_t type,
+ uint8_t sub_type, uint32_t *label)
+{
+ struct bgp *bgp;
+ struct rfapi *h;
+ struct rfapi_import_table *it;
+ int has_ip_route = 1;
+ uint32_t lni = 0;
+
+ bgp = bgp_get_default(); /* assume 1 instance for now */
+ assert(bgp);
+
+ h = bgp->rfapi;
+ assert(h);
+
+ /*
+ * look at high-order byte of RD. FF means MAC
+ * address is present (VNC L2VPN)
+ */
+ if ((safi == SAFI_MPLS_VPN)
+ && (decode_rd_type(prd->val) == RD_TYPE_VNC_ETH)) {
+ struct prefix pfx_mac_buf;
+ struct prefix pfx_nexthop_buf;
+ int rc;
+
+ /*
+ * Set flag if prefix and nexthop are the same - don't
+ * add the route to normal IP-based import tables
+ */
+ if (!rfapiGetNexthop(attr, &pfx_nexthop_buf)) {
+ if (!prefix_cmp(&pfx_nexthop_buf, p)) {
+ has_ip_route = 0;
+ }
+ }
+
+ memset(&pfx_mac_buf, 0, sizeof(pfx_mac_buf));
+ pfx_mac_buf.family = AF_ETHERNET;
+ pfx_mac_buf.prefixlen = 48;
+ memcpy(&pfx_mac_buf.u.prefix_eth.octet, prd->val + 2, 6);
+
+ /*
+ * Find rt containing LNI (Logical Network ID), which
+ * _should_ always be present when mac address is present
+ */
+ rc = rfapiEcommunityGetLNI(bgp_attr_get_ecommunity(attr), &lni);
+
+ vnc_zlog_debug_verbose(
+ "%s: rfapiEcommunityGetLNI returned %d, lni=%d, attr=%p",
+ __func__, rc, lni, attr);
+ if (!rc) {
+ it = rfapiMacImportTableGet(bgp, lni);
+
+ rfapiBgpInfoFilteredImportVPN(
+ it, FIF_ACTION_UPDATE, peer, rfd,
+ &pfx_mac_buf, /* prefix */
+ p, /* aux prefix: IP addr */
+ AFI_L2VPN, prd, attr, type, sub_type, label);
+ }
+ }
+
+ if (!has_ip_route)
+ return;
+
+ /*
+ * Iterate over all import tables; do a filtered import
+ * for the afi/safi combination
+ */
+ for (it = h->imports; it; it = it->next) {
+ (*rfapiBgpInfoFilteredImportFunction(safi))(
+ it, FIF_ACTION_UPDATE, peer, rfd, p, /* prefix */
+ NULL, afi, prd, attr, type, sub_type, label);
+ }
+
+ if (safi == SAFI_MPLS_VPN) {
+ vnc_direct_bgp_rh_add_route(bgp, afi, p, peer, attr);
+ rfapiBgpInfoFilteredImportVPN(
+ bgp->rfapi->it_ce, FIF_ACTION_UPDATE, peer, rfd,
+ p, /* prefix */
+ NULL, afi, prd, attr, type, sub_type, label);
+ }
+}
+
+
+void rfapiProcessWithdraw(struct peer *peer, void *rfd, const struct prefix *p,
+ struct prefix_rd *prd, struct attr *attr, afi_t afi,
+ safi_t safi, uint8_t type, int kill)
+{
+ struct bgp *bgp;
+ struct rfapi *h;
+ struct rfapi_import_table *it;
+
+ bgp = bgp_get_default(); /* assume 1 instance for now */
+ assert(bgp);
+
+ h = bgp->rfapi;
+ assert(h);
+
+ /*
+ * look at high-order byte of RD. FF means MAC
+ * address is present (VNC L2VPN)
+ */
+ if (h->import_mac != NULL && safi == SAFI_MPLS_VPN
+ && decode_rd_type(prd->val) == RD_TYPE_VNC_ETH) {
+ struct prefix pfx_mac_buf;
+ void *cursor = NULL;
+ int rc;
+
+ memset(&pfx_mac_buf, 0, sizeof(pfx_mac_buf));
+ pfx_mac_buf.family = AF_ETHERNET;
+ pfx_mac_buf.prefixlen = 48;
+ memcpy(&pfx_mac_buf.u.prefix_eth, prd->val + 2, 6);
+
+ /*
+ * withdraw does not contain attrs, so we don't have
+ * access to the route's LNI, which would ordinarily
+ * select the specific mac-based import table. Instead,
+ * we must iterate over all mac-based tables and rely
+ * on the RD to match.
+ *
+ * If this approach is too slow, add an index where
+ * key is {RD, peer} and value is the import table
+ */
+ for (rc = skiplist_next(h->import_mac, NULL, (void **)&it,
+ &cursor);
+ rc == 0; rc = skiplist_next(h->import_mac, NULL,
+ (void **)&it, &cursor)) {
+
+#ifdef DEBUG_L2_EXTRA
+ vnc_zlog_debug_verbose(
+ "%s: calling rfapiBgpInfoFilteredImportVPN(it=%p, afi=AFI_L2VPN)",
+ __func__, it);
+#endif
+
+ rfapiBgpInfoFilteredImportVPN(
+ it,
+ (kill ? FIF_ACTION_KILL : FIF_ACTION_WITHDRAW),
+ peer, rfd, &pfx_mac_buf, /* prefix */
+ p, /* aux_prefix: IP */
+ AFI_L2VPN, prd, attr, type, 0,
+ NULL); /* sub_type & label unused for withdraw
+ */
+ }
+ }
+
+ /*
+ * XXX For the case where the withdraw involves an L2
+ * route with no IP information, we rely on the lack
+ * of RT-list intersection to filter out the withdraw
+ * from the IP-based import tables below
+ */
+
+ /*
+ * Iterate over all import tables; do a filtered import
+ * for the afi/safi combination
+ */
+
+ for (it = h->imports; it; it = it->next) {
+ (*rfapiBgpInfoFilteredImportFunction(safi))(
+ it, (kill ? FIF_ACTION_KILL : FIF_ACTION_WITHDRAW),
+ peer, rfd, p, /* prefix */
+ NULL, afi, prd, attr, type, 0,
+ NULL); /* sub_type & label unused for withdraw */
+ }
+
+ /* TBD the deletion should happen after the lifetime expires */
+ if (safi == SAFI_MPLS_VPN)
+ vnc_direct_bgp_rh_del_route(bgp, afi, p, peer);
+
+ if (safi == SAFI_MPLS_VPN) {
+ rfapiBgpInfoFilteredImportVPN(
+ bgp->rfapi->it_ce,
+ (kill ? FIF_ACTION_KILL : FIF_ACTION_WITHDRAW), peer,
+ rfd, p, /* prefix */
+ NULL, afi, prd, attr, type, 0,
+ NULL); /* sub_type & label unused for withdraw */
+ }
+}
+
+/*
+ * TBD optimized withdraw timer algorithm for case of many
+ * routes expiring at the same time due to peer drop.
+ */
+/*
+ * 1. Visit all BPIs in all ENCAP import tables.
+ *
+ * a. If a bpi's peer is the failed peer, remove the bpi.
+ * b. If the removed ENCAP bpi was first in the list of
+ * BPIs at this ENCAP node, loop over all monitors
+ * at this node:
+ *
+ * (1) for each ENCAP monitor, loop over all its
+ * VPN node monitors and set their RFAPI_MON_FLAG_NEEDCALLBACK
+ * flags.
+ *
+ * 2. Visit all BPIs in all VPN import tables.
+ * a. If a bpi's peer is the failed peer, remove the bpi.
+ * b. loop over all the VPN node monitors and set their
+ * RFAPI_MON_FLAG_NEEDCALLBACK flags
+ * c. If there are no BPIs left at this VPN node,
+ *
+ */
+
+
+/* surprise, this gets called from peer_delete(), from rfapi_close() */
+static void rfapiProcessPeerDownRt(struct peer *peer,
+ struct rfapi_import_table *import_table,
+ afi_t afi, safi_t safi)
+{
+ struct agg_node *rn;
+ struct bgp_path_info *bpi;
+ struct agg_table *rt;
+ void (*timer_service_func)(struct thread *);
+
+ assert(afi == AFI_IP || afi == AFI_IP6);
+
+ VNC_ITRCCK;
+
+ switch (safi) {
+ case SAFI_MPLS_VPN:
+ rt = import_table->imported_vpn[afi];
+ timer_service_func = rfapiWithdrawTimerVPN;
+ break;
+ case SAFI_ENCAP:
+ rt = import_table->imported_encap[afi];
+ timer_service_func = rfapiWithdrawTimerEncap;
+ break;
+ default:
+ /* Suppress uninitialized variable warning */
+ rt = NULL;
+ timer_service_func = NULL;
+ assert(0);
+ }
+
+
+ for (rn = agg_route_top(rt); rn; rn = agg_route_next(rn)) {
+ for (bpi = rn->info; bpi; bpi = bpi->next) {
+ if (bpi->peer == peer) {
+
+ if (CHECK_FLAG(bpi->flags, BGP_PATH_REMOVED)) {
+ /* already in holddown, skip */
+ continue;
+ }
+
+ if (safi == SAFI_MPLS_VPN) {
+ RFAPI_UPDATE_ITABLE_COUNT(
+ bpi, import_table, afi, -1);
+ import_table->holddown_count[afi] += 1;
+ }
+ rfapiBiStartWithdrawTimer(import_table, rn, bpi,
+ afi, safi,
+ timer_service_func);
+ }
+ }
+ }
+ VNC_ITRCCK;
+}
+
+/*
+ * This gets called when a peer connection drops. We have to remove
+ * all the routes from this peer.
+ *
+ * Current approach is crude. TBD Optimize by setting fewer timers and
+ * grouping withdrawn routes so we can generate callbacks more
+ * efficiently.
+ */
+void rfapiProcessPeerDown(struct peer *peer)
+{
+ struct bgp *bgp;
+ struct rfapi *h;
+ struct rfapi_import_table *it;
+
+ /*
+ * If this peer is a "dummy" peer structure atached to a RFAPI
+ * nve_descriptor, we don't need to walk the import tables
+ * because the routes are already withdrawn by rfapi_close()
+ */
+ if (CHECK_FLAG(peer->flags, PEER_FLAG_IS_RFAPI_HD))
+ return;
+
+ /*
+ * 1. Visit all BPIs in all ENCAP import tables.
+ * Start withdraw timer on the BPIs that match peer.
+ *
+ * 2. Visit All BPIs in all VPN import tables.
+ * Start withdraw timer on the BPIs that match peer.
+ */
+
+ bgp = bgp_get_default(); /* assume 1 instance for now */
+ if (!bgp)
+ return;
+
+ h = bgp->rfapi;
+ assert(h);
+
+ for (it = h->imports; it; it = it->next) {
+ rfapiProcessPeerDownRt(peer, it, AFI_IP, SAFI_ENCAP);
+ rfapiProcessPeerDownRt(peer, it, AFI_IP6, SAFI_ENCAP);
+ rfapiProcessPeerDownRt(peer, it, AFI_IP, SAFI_MPLS_VPN);
+ rfapiProcessPeerDownRt(peer, it, AFI_IP6, SAFI_MPLS_VPN);
+ }
+
+ if (h->it_ce) {
+ rfapiProcessPeerDownRt(peer, h->it_ce, AFI_IP, SAFI_MPLS_VPN);
+ rfapiProcessPeerDownRt(peer, h->it_ce, AFI_IP6, SAFI_MPLS_VPN);
+ }
+}
+
+/*
+ * Import an entire RIB (for an afi/safi) to an import table RIB,
+ * filtered according to the import table's RT list
+ *
+ * TBD: does this function need additions to match rfapiProcessUpdate()
+ * for, e.g., L2 handling?
+ */
+static void rfapiBgpTableFilteredImport(struct bgp *bgp,
+ struct rfapi_import_table *it,
+ afi_t afi, safi_t safi)
+{
+ struct bgp_dest *dest1;
+ struct bgp_dest *dest2;
+
+ /* Only these SAFIs have 2-level RIBS */
+ assert(safi == SAFI_MPLS_VPN || safi == SAFI_ENCAP);
+
+ /*
+ * Now visit all the rd nodes and the nodes of all the
+ * route tables attached to them, and import the routes
+ * if they have matching route targets
+ */
+ for (dest1 = bgp_table_top(bgp->rib[afi][safi]); dest1;
+ dest1 = bgp_route_next(dest1)) {
+
+ if (bgp_dest_has_bgp_path_info_data(dest1)) {
+
+ for (dest2 = bgp_table_top(
+ bgp_dest_get_bgp_table_info(dest1));
+ dest2; dest2 = bgp_route_next(dest2)) {
+
+ struct bgp_path_info *bpi;
+
+ for (bpi = bgp_dest_get_bgp_path_info(dest2);
+ bpi; bpi = bpi->next) {
+ uint32_t label = 0;
+
+ if (CHECK_FLAG(bpi->flags,
+ BGP_PATH_REMOVED))
+ continue;
+
+ if (bpi->extra)
+ label = decode_label(
+ &bpi->extra->label[0]);
+ (*rfapiBgpInfoFilteredImportFunction(
+ safi))(
+ it, /* which import table */
+ FIF_ACTION_UPDATE, bpi->peer,
+ NULL,
+ bgp_dest_get_prefix(dest2),
+ NULL, afi,
+ (struct prefix_rd *)
+ bgp_dest_get_prefix(
+ dest1),
+ bpi->attr, bpi->type,
+ bpi->sub_type, &label);
+ }
+ }
+ }
+ }
+}
+
+
+/* per-bgp-instance rfapi data */
+struct rfapi *bgp_rfapi_new(struct bgp *bgp)
+{
+ struct rfapi *h;
+ afi_t afi;
+ struct rfapi_rfp_cfg *cfg = NULL;
+ struct rfapi_rfp_cb_methods *cbm = NULL;
+
+ assert(bgp->rfapi_cfg == NULL);
+
+ h = XCALLOC(MTYPE_RFAPI, sizeof(struct rfapi));
+
+ for (afi = AFI_IP; afi < AFI_MAX; afi++) {
+ h->un[afi] = agg_table_init();
+ }
+
+ /*
+ * initialize the ce import table
+ */
+ h->it_ce = XCALLOC(MTYPE_RFAPI_IMPORTTABLE,
+ sizeof(struct rfapi_import_table));
+ h->it_ce->imported_vpn[AFI_IP] = agg_table_init();
+ h->it_ce->imported_vpn[AFI_IP6] = agg_table_init();
+ h->it_ce->imported_encap[AFI_IP] = agg_table_init();
+ h->it_ce->imported_encap[AFI_IP6] = agg_table_init();
+ rfapiBgpTableFilteredImport(bgp, h->it_ce, AFI_IP, SAFI_MPLS_VPN);
+ rfapiBgpTableFilteredImport(bgp, h->it_ce, AFI_IP6, SAFI_MPLS_VPN);
+
+ /*
+ * Set up work queue for deferred rfapi_close operations
+ */
+ h->deferred_close_q =
+ work_queue_new(bm->master, "rfapi deferred close");
+ h->deferred_close_q->spec.workfunc = rfapi_deferred_close_workfunc;
+ h->deferred_close_q->spec.data = h;
+
+ h->rfp = rfp_start(bm->master, &cfg, &cbm);
+ bgp->rfapi_cfg = bgp_rfapi_cfg_new(cfg);
+ if (cbm != NULL) {
+ h->rfp_methods = *cbm;
+ }
+ return h;
+}
+
+void bgp_rfapi_destroy(struct bgp *bgp, struct rfapi *h)
+{
+ afi_t afi;
+
+ if (bgp == NULL || h == NULL)
+ return;
+
+ if (h->resolve_nve_nexthop) {
+ skiplist_free(h->resolve_nve_nexthop);
+ h->resolve_nve_nexthop = NULL;
+ }
+
+ agg_table_finish(h->it_ce->imported_vpn[AFI_IP]);
+ agg_table_finish(h->it_ce->imported_vpn[AFI_IP6]);
+ agg_table_finish(h->it_ce->imported_encap[AFI_IP]);
+ agg_table_finish(h->it_ce->imported_encap[AFI_IP6]);
+
+ if (h->import_mac) {
+ struct rfapi_import_table *it;
+ void *cursor;
+ int rc;
+
+ for (cursor = NULL,
+ rc = skiplist_next(h->import_mac, NULL, (void **)&it,
+ &cursor);
+ !rc; rc = skiplist_next(h->import_mac, NULL, (void **)&it,
+ &cursor)) {
+
+ rfapiImportTableFlush(it);
+ XFREE(MTYPE_RFAPI_IMPORTTABLE, it);
+ }
+ skiplist_free(h->import_mac);
+ h->import_mac = NULL;
+ }
+
+ work_queue_free_and_null(&h->deferred_close_q);
+
+ if (h->rfp != NULL)
+ rfp_stop(h->rfp);
+
+ for (afi = AFI_IP; afi < AFI_MAX; afi++) {
+ agg_table_finish(h->un[afi]);
+ }
+
+ XFREE(MTYPE_RFAPI_IMPORTTABLE, h->it_ce);
+ XFREE(MTYPE_RFAPI, h);
+}
+
+struct rfapi_import_table *
+rfapiImportTableRefAdd(struct bgp *bgp, struct ecommunity *rt_import_list,
+ struct rfapi_nve_group_cfg *rfg)
+{
+ struct rfapi *h;
+ struct rfapi_import_table *it;
+ afi_t afi;
+
+ h = bgp->rfapi;
+ assert(h);
+
+ for (it = h->imports; it; it = it->next) {
+ if (ecommunity_cmp(it->rt_import_list, rt_import_list))
+ break;
+ }
+
+ vnc_zlog_debug_verbose("%s: matched it=%p", __func__, it);
+
+ if (!it) {
+ it = XCALLOC(MTYPE_RFAPI_IMPORTTABLE,
+ sizeof(struct rfapi_import_table));
+ it->next = h->imports;
+ h->imports = it;
+
+ it->rt_import_list = ecommunity_dup(rt_import_list);
+ it->rfg = rfg;
+ it->monitor_exterior_orphans =
+ skiplist_new(0, NULL, prefix_free_lists);
+
+ /*
+ * fill import route tables from RIBs
+ *
+ * Potential area for optimization. If this occurs when
+ * tables are large (e.g., the operator adds a nve group
+ * with a new RT list to a running system), it could take
+ * a while.
+ *
+ */
+ for (afi = AFI_IP; afi < AFI_MAX; ++afi) {
+
+ it->imported_vpn[afi] = agg_table_init();
+ it->imported_encap[afi] = agg_table_init();
+
+ rfapiBgpTableFilteredImport(bgp, it, afi,
+ SAFI_MPLS_VPN);
+ rfapiBgpTableFilteredImport(bgp, it, afi, SAFI_ENCAP);
+
+ vnc_import_bgp_exterior_redist_enable_it(bgp, afi, it);
+ }
+ }
+
+ it->refcount += 1;
+
+ return it;
+}
+
+/*
+ * skiplist element free function
+ */
+static void delete_rem_pfx_na_free(void *na)
+{
+ uint32_t *pCounter = ((struct rfapi_nve_addr *)na)->info;
+
+ *pCounter += 1;
+ XFREE(MTYPE_RFAPI_NVE_ADDR, na);
+}
+
+/*
+ * Common deleter for IP and MAC import tables
+ */
+static void rfapiDeleteRemotePrefixesIt(
+ struct bgp *bgp, struct rfapi_import_table *it, struct prefix *un,
+ struct prefix *vn, struct prefix *p, int delete_active,
+ int delete_holddown, uint32_t *pARcount, uint32_t *pAHcount,
+ uint32_t *pHRcount, uint32_t *pHHcount,
+ struct skiplist *uniq_active_nves, struct skiplist *uniq_holddown_nves)
+{
+ afi_t afi;
+
+#ifdef DEBUG_L2_EXTRA
+ {
+ char buf_pfx[PREFIX_STRLEN];
+
+ if (p) {
+ prefix2str(p, buf_pfx, sizeof(buf_pfx));
+ } else {
+ buf_pfx[0] = '*';
+ buf_pfx[1] = 0;
+ }
+
+ vnc_zlog_debug_verbose(
+ "%s: entry, p=%s, delete_active=%d, delete_holddown=%d",
+ __func__, buf_pfx, delete_active, delete_holddown);
+ }
+#endif
+
+ for (afi = AFI_IP; afi < AFI_MAX; ++afi) {
+
+ struct agg_table *rt;
+ struct agg_node *rn;
+
+ if (p && (family2afi(p->family) != afi)) {
+ continue;
+ }
+
+ rt = it->imported_vpn[afi];
+ if (!rt)
+ continue;
+
+ vnc_zlog_debug_verbose("%s: scanning rt for afi=%d", __func__,
+ afi);
+
+ for (rn = agg_route_top(rt); rn; rn = agg_route_next(rn)) {
+ struct bgp_path_info *bpi;
+ struct bgp_path_info *next;
+ const struct prefix *rn_p = agg_node_get_prefix(rn);
+
+ if (p && VNC_DEBUG(IMPORT_DEL_REMOTE))
+ vnc_zlog_debug_any("%s: want %pFX, have %pRN",
+ __func__, p, rn);
+
+ if (p && prefix_cmp(p, rn_p))
+ continue;
+
+ vnc_zlog_debug_verbose("%s: rn pfx=%pRN", __func__, rn);
+
+ /* TBD is this valid for afi == AFI_L2VPN? */
+ RFAPI_CHECK_REFCOUNT(rn, SAFI_MPLS_VPN, 1);
+
+ for (bpi = rn->info; bpi; bpi = next) {
+ next = bpi->next;
+
+ struct prefix qpt;
+ struct prefix qct;
+ int qpt_valid = 0;
+ int qct_valid = 0;
+ int is_active = 0;
+
+ vnc_zlog_debug_verbose("%s: examining bpi %p",
+ __func__, bpi);
+
+ if (!rfapiGetNexthop(bpi->attr, &qpt))
+ qpt_valid = 1;
+
+ if (vn) {
+ if (!qpt_valid
+ || !prefix_match(vn, &qpt)) {
+#ifdef DEBUG_L2_EXTRA
+ vnc_zlog_debug_verbose(
+ "%s: continue at vn && !qpt_valid || !prefix_match(vn, &qpt)",
+ __func__);
+#endif
+ continue;
+ }
+ }
+
+ if (!rfapiGetUnAddrOfVpnBi(bpi, &qct))
+ qct_valid = 1;
+
+ if (un) {
+ if (!qct_valid
+ || !prefix_match(un, &qct)) {
+#ifdef DEBUG_L2_EXTRA
+ vnc_zlog_debug_verbose(
+ "%s: continue at un && !qct_valid || !prefix_match(un, &qct)",
+ __func__);
+#endif
+ continue;
+ }
+ }
+
+
+ /*
+ * Blow bpi away
+ */
+ /*
+ * If this route is waiting to be deleted
+ * because of
+ * a previous withdraw, we must cancel its
+ * timer.
+ */
+ if (CHECK_FLAG(bpi->flags, BGP_PATH_REMOVED)) {
+ if (!delete_holddown)
+ continue;
+ if (bpi->extra->vnc.import.timer) {
+ struct rfapi_withdraw *wcb =
+ THREAD_ARG(
+ bpi->extra->vnc
+ .import
+ .timer);
+
+ wcb->import_table
+ ->holddown_count[afi] -=
+ 1;
+ RFAPI_UPDATE_ITABLE_COUNT(
+ bpi, wcb->import_table,
+ afi, 1);
+ XFREE(MTYPE_RFAPI_WITHDRAW,
+ wcb);
+ THREAD_OFF(
+ bpi->extra->vnc.import
+ .timer);
+ }
+ } else {
+ if (!delete_active)
+ continue;
+ is_active = 1;
+ }
+
+ vnc_zlog_debug_verbose(
+ "%s: deleting bpi %p (qct_valid=%d, qpt_valid=%d, delete_holddown=%d, delete_active=%d)",
+ __func__, bpi, qct_valid, qpt_valid,
+ delete_holddown, delete_active);
+
+
+ /*
+ * add nve to list
+ */
+ if (qct_valid && qpt_valid) {
+
+ struct rfapi_nve_addr na;
+ struct rfapi_nve_addr *nap;
+
+ memset(&na, 0, sizeof(na));
+ assert(!rfapiQprefix2Raddr(&qct,
+ &na.un));
+ assert(!rfapiQprefix2Raddr(&qpt,
+ &na.vn));
+
+ if (skiplist_search(
+ (is_active
+ ? uniq_active_nves
+ : uniq_holddown_nves),
+ &na, (void **)&nap)) {
+ char line[BUFSIZ];
+
+ nap = XCALLOC(
+ MTYPE_RFAPI_NVE_ADDR,
+ sizeof(struct
+ rfapi_nve_addr));
+ *nap = na;
+ nap->info = is_active
+ ? pAHcount
+ : pHHcount;
+ skiplist_insert(
+ (is_active
+ ? uniq_active_nves
+ : uniq_holddown_nves),
+ nap, nap);
+
+ rfapiNveAddr2Str(nap, line,
+ BUFSIZ);
+ }
+ }
+
+ vnc_direct_bgp_rh_del_route(bgp, afi, rn_p,
+ bpi->peer);
+
+ RFAPI_UPDATE_ITABLE_COUNT(bpi, it, afi, -1);
+ it->holddown_count[afi] += 1;
+ rfapiExpireVpnNow(it, rn, bpi, 1);
+
+ vnc_zlog_debug_verbose(
+ "%s: incrementing count (is_active=%d)",
+ __func__, is_active);
+
+ if (is_active)
+ ++*pARcount;
+ else
+ ++*pHRcount;
+ }
+ }
+ }
+}
+
+
+/*
+ * For use by the "clear vnc prefixes" command
+ */
+/*------------------------------------------
+ * rfapiDeleteRemotePrefixes
+ *
+ * UI helper: For use by the "clear vnc prefixes" command
+ *
+ * input:
+ * un if set, tunnel must match this prefix
+ * vn if set, nexthop prefix must match this prefix
+ * p if set, prefix must match this prefix
+ * it if set, only look in this import table
+ *
+ * output
+ * pARcount number of active routes deleted
+ * pAHcount number of active nves deleted
+ * pHRcount number of holddown routes deleted
+ * pHHcount number of holddown nves deleted
+ *
+ * return value:
+ * void
+ --------------------------------------------*/
+void rfapiDeleteRemotePrefixes(struct prefix *un, struct prefix *vn,
+ struct prefix *p,
+ struct rfapi_import_table *arg_it,
+ int delete_active, int delete_holddown,
+ uint32_t *pARcount, uint32_t *pAHcount,
+ uint32_t *pHRcount, uint32_t *pHHcount)
+{
+ struct bgp *bgp;
+ struct rfapi *h;
+ struct rfapi_import_table *it;
+ uint32_t deleted_holddown_route_count = 0;
+ uint32_t deleted_active_route_count = 0;
+ uint32_t deleted_holddown_nve_count = 0;
+ uint32_t deleted_active_nve_count = 0;
+ struct skiplist *uniq_holddown_nves;
+ struct skiplist *uniq_active_nves;
+
+ VNC_ITRCCK;
+
+ bgp = bgp_get_default(); /* assume 1 instance for now */
+ /* If no bgp instantiated yet, no vnc prefixes exist */
+ if (!bgp)
+ return;
+
+ h = bgp->rfapi;
+ assert(h);
+
+ uniq_holddown_nves =
+ skiplist_new(0, rfapi_nve_addr_cmp, delete_rem_pfx_na_free);
+ uniq_active_nves =
+ skiplist_new(0, rfapi_nve_addr_cmp, delete_rem_pfx_na_free);
+
+ /*
+ * Iterate over all import tables; do a filtered import
+ * for the afi/safi combination
+ */
+
+ if (arg_it)
+ it = arg_it;
+ else
+ it = h->imports;
+ for (; it;) {
+
+ vnc_zlog_debug_verbose(
+ "%s: calling rfapiDeleteRemotePrefixesIt() on (IP) import %p",
+ __func__, it);
+
+ rfapiDeleteRemotePrefixesIt(
+ bgp, it, un, vn, p, delete_active, delete_holddown,
+ &deleted_active_route_count, &deleted_active_nve_count,
+ &deleted_holddown_route_count,
+ &deleted_holddown_nve_count, uniq_active_nves,
+ uniq_holddown_nves);
+
+ if (arg_it)
+ it = NULL;
+ else
+ it = it->next;
+ }
+
+ /*
+ * Now iterate over L2 import tables
+ */
+ if (h->import_mac && !(p && (p->family != AF_ETHERNET))) {
+
+ void *cursor = NULL;
+ int rc;
+
+ for (cursor = NULL,
+ rc = skiplist_next(h->import_mac, NULL, (void **)&it,
+ &cursor);
+ !rc; rc = skiplist_next(h->import_mac, NULL, (void **)&it,
+ &cursor)) {
+
+ vnc_zlog_debug_verbose(
+ "%s: calling rfapiDeleteRemotePrefixesIt() on import_mac %p",
+ __func__, it);
+
+ rfapiDeleteRemotePrefixesIt(
+ bgp, it, un, vn, p, delete_active,
+ delete_holddown, &deleted_active_route_count,
+ &deleted_active_nve_count,
+ &deleted_holddown_route_count,
+ &deleted_holddown_nve_count, uniq_active_nves,
+ uniq_holddown_nves);
+ }
+ }
+
+ /*
+ * our custom element freeing function above counts as it deletes
+ */
+ skiplist_free(uniq_holddown_nves);
+ skiplist_free(uniq_active_nves);
+
+ if (pARcount)
+ *pARcount = deleted_active_route_count;
+ if (pAHcount)
+ *pAHcount = deleted_active_nve_count;
+ if (pHRcount)
+ *pHRcount = deleted_holddown_route_count;
+ if (pHHcount)
+ *pHHcount = deleted_holddown_nve_count;
+
+ VNC_ITRCCK;
+}
+
+/*------------------------------------------
+ * rfapiCountRemoteRoutes
+ *
+ * UI helper: count VRF routes from BGP side
+ *
+ * input:
+ *
+ * output
+ * pALRcount count of active local routes
+ * pARRcount count of active remote routes
+ * pHRcount count of holddown routes
+ * pIRcount count of direct imported routes
+ *
+ * return value:
+ * void
+ --------------------------------------------*/
+void rfapiCountAllItRoutes(int *pALRcount, /* active local routes */
+ int *pARRcount, /* active remote routes */
+ int *pHRcount, /* holddown routes */
+ int *pIRcount) /* imported routes */
+{
+ struct bgp *bgp;
+ struct rfapi *h;
+ struct rfapi_import_table *it;
+ afi_t afi;
+
+ int total_active_local = 0;
+ int total_active_remote = 0;
+ int total_holddown = 0;
+ int total_imported = 0;
+
+ bgp = bgp_get_default(); /* assume 1 instance for now */
+ assert(bgp);
+
+ h = bgp->rfapi;
+ assert(h);
+
+ /*
+ * Iterate over all import tables; do a filtered import
+ * for the afi/safi combination
+ */
+
+ for (it = h->imports; it; it = it->next) {
+
+ for (afi = AFI_IP; afi < AFI_MAX; ++afi) {
+
+ total_active_local += it->local_count[afi];
+ total_active_remote += it->remote_count[afi];
+ total_holddown += it->holddown_count[afi];
+ total_imported += it->imported_count[afi];
+ }
+ }
+
+ void *cursor;
+ int rc;
+
+ if (h->import_mac) {
+ for (cursor = NULL,
+ rc = skiplist_next(h->import_mac, NULL, (void **)&it,
+ &cursor);
+ !rc; rc = skiplist_next(h->import_mac, NULL, (void **)&it,
+ &cursor)) {
+
+ total_active_local += it->local_count[AFI_L2VPN];
+ total_active_remote += it->remote_count[AFI_L2VPN];
+ total_holddown += it->holddown_count[AFI_L2VPN];
+ total_imported += it->imported_count[AFI_L2VPN];
+ }
+ }
+
+
+ if (pALRcount) {
+ *pALRcount = total_active_local;
+ }
+ if (pARRcount) {
+ *pARRcount = total_active_remote;
+ }
+ if (pHRcount) {
+ *pHRcount = total_holddown;
+ }
+ if (pIRcount) {
+ *pIRcount = total_imported;
+ }
+}
+
+/*------------------------------------------
+ * rfapiGetHolddownFromLifetime
+ *
+ * calculate holddown value based on lifetime
+ *
+ * input:
+ * lifetime lifetime
+ *
+ * return value:
+ * Holddown value based on lifetime, holddown_factor,
+ * and RFAPI_LIFETIME_INFINITE_WITHDRAW_DELAY
+ *
+ --------------------------------------------*/
+/* hold down time maxes out at RFAPI_LIFETIME_INFINITE_WITHDRAW_DELAY */
+uint32_t rfapiGetHolddownFromLifetime(uint32_t lifetime)
+{
+ uint32_t factor;
+ struct bgp *bgp;
+
+ bgp = bgp_get_default();
+ if (bgp && bgp->rfapi_cfg)
+ factor = bgp->rfapi_cfg->rfp_cfg.holddown_factor;
+ else
+ factor = RFAPI_RFP_CFG_DEFAULT_HOLDDOWN_FACTOR;
+
+ if (factor < 100 || lifetime < RFAPI_LIFETIME_INFINITE_WITHDRAW_DELAY)
+ lifetime = lifetime * factor / 100;
+ if (lifetime < RFAPI_LIFETIME_INFINITE_WITHDRAW_DELAY)
+ return lifetime;
+ else
+ return RFAPI_LIFETIME_INFINITE_WITHDRAW_DELAY;
+}
diff --git a/bgpd/rfapi/rfapi_import.h b/bgpd/rfapi/rfapi_import.h
new file mode 100644
index 0000000..387e6c4
--- /dev/null
+++ b/bgpd/rfapi/rfapi_import.h
@@ -0,0 +1,243 @@
+/*
+ *
+ * Copyright 2009-2016, LabN Consulting, L.L.C.
+ *
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program; see the file COPYING; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
+ */
+
+/*
+ * File: rfapi_import.h
+ * Purpose: Handle import of routes from BGP to RFAPI
+ */
+
+#ifndef QUAGGA_HGP_RFAPI_IMPORT_H
+#define QUAGGA_HGP_RFAPI_IMPORT_H
+
+#include "lib/thread.h"
+
+/*
+ * These are per-rt-import-list
+ *
+ * routes are not segregated by RD - the RD is stored in bgp_path_info_extra
+ * and is needed to determine if two prefixes are the same.
+ */
+struct rfapi_import_table {
+ struct rfapi_import_table *next;
+ struct rfapi_nve_group_cfg *rfg;
+ struct ecommunity *rt_import_list; /* copied from nve grp */
+ int refcount; /* nve grps and nves */
+ uint32_t l2_logical_net_id; /* L2 only: EVPN Eth Seg Id */
+ struct agg_table *imported_vpn[AFI_MAX];
+ struct rfapi_monitor_vpn *vpn0_queries[AFI_MAX];
+ struct rfapi_monitor_eth *eth0_queries;
+ struct agg_table *imported_encap[AFI_MAX];
+ struct skiplist *monitor_exterior_orphans;
+ int local_count[AFI_MAX];
+ int remote_count[AFI_MAX];
+ int holddown_count[AFI_MAX];
+ int imported_count[AFI_MAX];
+};
+
+#define RFAPI_LOCAL_BI(bpi) \
+ (((bpi)->type == ZEBRA_ROUTE_BGP) && ((bpi)->sub_type == BGP_ROUTE_RFP))
+
+#define RFAPI_DIRECT_IMPORT_BI(bpi) \
+ (((bpi)->type == ZEBRA_ROUTE_BGP_DIRECT) \
+ || ((bpi)->type == ZEBRA_ROUTE_BGP_DIRECT_EXT))
+
+#define RFAPI_UPDATE_ITABLE_COUNT(bpi, itable, afi, cnt) \
+ if (RFAPI_LOCAL_BI(bpi)) { \
+ (itable)->local_count[(afi)] += (cnt); \
+ } else { \
+ if (RFAPI_DIRECT_IMPORT_BI(bpi)) \
+ (itable)->imported_count[(afi)] += (cnt); \
+ else \
+ (itable)->remote_count[(afi)] += (cnt); \
+ }
+
+extern uint8_t rfapiRfpCost(struct attr *attr);
+
+extern void rfapiDebugBacktrace(void);
+
+extern void rfapiCheckRouteCount(void);
+
+/*
+ * Print BPI in an Import Table
+ */
+extern void rfapiPrintBi(void *stream, struct bgp_path_info *bpi);
+
+extern void rfapiShowImportTable(void *stream, const char *label,
+ struct agg_table *rt, int isvpn);
+
+extern struct rfapi_import_table *
+rfapiImportTableRefAdd(struct bgp *bgp, struct ecommunity *rt_import_list,
+ struct rfapi_nve_group_cfg *rfg);
+
+extern void rfapiImportTableRefDelByIt(struct bgp *bgp,
+ struct rfapi_import_table *it_target);
+
+
+/*
+ * Construct an rfapi nexthop list based on the routes attached to
+ * the specified node.
+ *
+ * If there are any routes that do NOT have BGP_PATH_REMOVED set,
+ * return those only. If there are ONLY routes with BGP_INFO_REMOVED,
+ * then return those, and also include all the non-removed routes from the
+ * next less-specific node (i.e., this node's parent) at the end.
+ */
+extern struct rfapi_next_hop_entry *rfapiRouteNode2NextHopList(
+ struct agg_node *rn, uint32_t lifetime, /* put into nexthop entries */
+ struct rfapi_ip_addr *exclude_vnaddr, /* omit routes to same NVE */
+ struct agg_table *rfd_rib_table, /* preload this NVE rib table */
+ struct prefix *pfx_target_original); /* query target */
+
+extern struct rfapi_next_hop_entry *rfapiRouteTable2NextHopList(
+ struct agg_table *rt, uint32_t lifetime, /* put into nexthop entries */
+ struct rfapi_ip_addr *exclude_vnaddr, /* omit routes to same NVE */
+ struct agg_table *rfd_rib_table, /* preload this NVE rib table */
+ struct prefix *pfx_target_original); /* query target */
+
+extern struct rfapi_next_hop_entry *rfapiEthRouteTable2NextHopList(
+ uint32_t logical_net_id, struct rfapi_ip_prefix *rprefix,
+ uint32_t lifetime, /* put into nexthop entries */
+ struct rfapi_ip_addr *exclude_vnaddr, /* omit routes to same NVE */
+ struct agg_table *rib_route_table, /* preload NVE rib node */
+ struct prefix *pfx_target_original); /* query target */
+
+extern int rfapiEcommunitiesIntersect(struct ecommunity *e1,
+ struct ecommunity *e2);
+
+extern void rfapiCheckRefcount(struct agg_node *rn, safi_t safi,
+ int lockoffset);
+
+extern int rfapiHasNonRemovedRoutes(struct agg_node *rn);
+
+extern int rfapiGetUnAddrOfVpnBi(struct bgp_path_info *bpi, struct prefix *p);
+
+extern void rfapiNexthop2Prefix(struct attr *attr, struct prefix *p);
+
+extern void rfapiUnicastNexthop2Prefix(afi_t afi, struct attr *attr,
+ struct prefix *p);
+
+/* Filtered Import Function actions */
+#define FIF_ACTION_UPDATE 0
+#define FIF_ACTION_WITHDRAW 1
+#define FIF_ACTION_KILL 2
+
+extern void rfapiBgpInfoFilteredImportVPN(
+ struct rfapi_import_table *import_table, int action, struct peer *peer,
+ void *rfd, /* set for looped back routes */
+ const struct prefix *p,
+ const struct prefix *aux_prefix, /* AFI_ETHER: optional IP */
+ afi_t afi, struct prefix_rd *prd,
+ struct attr *attr, /* part of bgp_path_info */
+ uint8_t type, /* part of bgp_path_info */
+ uint8_t sub_type, /* part of bgp_path_info */
+ uint32_t *label); /* part of bgp_path_info */
+
+extern struct rfapi_next_hop_entry *rfapiEthRouteNode2NextHopList(
+ struct agg_node *rn, struct rfapi_ip_prefix *rprefix,
+ uint32_t lifetime, /* put into nexthop entries */
+ struct rfapi_ip_addr *exclude_vnaddr, /* omit routes to same NVE */
+ struct agg_table *rib_route_table, /* preload NVE rib table */
+ struct prefix *pfx_target_original); /* query target */
+
+extern struct rfapi_import_table *rfapiMacImportTableGetNoAlloc(struct bgp *bgp,
+ uint32_t lni);
+
+extern struct rfapi_import_table *rfapiMacImportTableGet(struct bgp *bgp,
+ uint32_t lni);
+
+extern int rfapiGetL2o(struct attr *attr, struct rfapi_l2address_option *l2o);
+
+extern int rfapiEcommunityGetLNI(struct ecommunity *ecom, uint32_t *lni);
+
+extern int rfapiEcommunityGetEthernetTag(struct ecommunity *ecom,
+ uint16_t *tag_id);
+
+/* enable for debugging; disable for performance */
+#if 0
+#define RFAPI_CHECK_REFCOUNT(rn, safi, lo) rfapiCheckRefcount((rn),(safi),(lo))
+#else
+#define RFAPI_CHECK_REFCOUNT(rn, safi, lo) {}
+#endif
+
+/*------------------------------------------
+ * rfapiDeleteRemotePrefixes
+ *
+ * UI helper: For use by the "clear vnc prefixes" command
+ *
+ * input:
+ * un if set, tunnel must match this prefix
+ * vn if set, nexthop prefix must match this prefix
+ * p if set, prefix must match this prefix
+ * it if set, only look in this import table
+ *
+ * output
+ * pARcount number of active routes deleted
+ * pAHcount number of active nves deleted
+ * pHRcount number of holddown routes deleted
+ * pHHcount number of holddown nves deleted
+ *
+ * return value:
+ * void
+ --------------------------------------------*/
+extern void rfapiDeleteRemotePrefixes(struct prefix *un, struct prefix *vn,
+ struct prefix *p,
+ struct rfapi_import_table *it,
+ int delete_active, int delete_holddown,
+ uint32_t *pARcount, /* active routes */
+ uint32_t *pAHcount, /* active nves */
+ uint32_t *pHRcount, /* holddown routes */
+ uint32_t *pHHcount); /* holddown nves */
+
+/*------------------------------------------
+ * rfapiCountAllItRoutes
+ *
+ * UI helper: count VRF routes from BGP side
+ *
+ * input:
+ *
+ * output
+ * pARcount count of active routes
+ * pHRcount count of holddown routes
+ * pIRcount count of holddown routes
+ *
+ * return value:
+ * void
+ --------------------------------------------*/
+extern void rfapiCountAllItRoutes(int *pALRcount, /* active local routes */
+ int *pARRcount, /* active remote routes */
+ int *pHRcount, /* holddown routes */
+ int *pIRcount); /* direct imported routes */
+
+/*------------------------------------------
+ * rfapiGetHolddownFromLifetime
+ *
+ * calculate holddown value based on lifetime
+ *
+ * input:
+ * lifetime lifetime
+ *
+ * return value:
+ * Holddown value based on lifetime, holddown_factor,
+ * and RFAPI_LIFETIME_INFINITE_WITHDRAW_DELAY
+ *
+ --------------------------------------------*/
+extern uint32_t rfapiGetHolddownFromLifetime(uint32_t lifetime);
+
+#endif /* QUAGGA_HGP_RFAPI_IMPORT_H */
diff --git a/bgpd/rfapi/rfapi_monitor.c b/bgpd/rfapi/rfapi_monitor.c
new file mode 100644
index 0000000..0e71d5d
--- /dev/null
+++ b/bgpd/rfapi/rfapi_monitor.c
@@ -0,0 +1,1558 @@
+/*
+ *
+ * Copyright 2009-2016, LabN Consulting, L.L.C.
+ *
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program; see the file COPYING; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
+ */
+
+/*
+ * File: rfapi_monitor.c
+ */
+
+/* TBD remove unneeded includes */
+
+#include "lib/zebra.h"
+#include "lib/prefix.h"
+#include "lib/agg_table.h"
+#include "lib/vty.h"
+#include "lib/memory.h"
+#include "lib/log.h"
+#include "lib/table.h"
+#include "lib/skiplist.h"
+
+#include "bgpd/bgpd.h"
+
+#include "bgpd/rfapi/bgp_rfapi_cfg.h"
+#include "bgpd/rfapi/rfapi.h"
+#include "bgpd/rfapi/rfapi_backend.h"
+
+#include "bgpd/rfapi/rfapi.h"
+#include "bgpd/rfapi/rfapi_import.h"
+#include "bgpd/rfapi/vnc_import_bgp.h"
+#include "bgpd/rfapi/rfapi_private.h"
+#include "bgpd/rfapi/rfapi_monitor.h"
+#include "bgpd/rfapi/rfapi_vty.h"
+#include "bgpd/rfapi/rfapi_rib.h"
+#include "bgpd/rfapi/vnc_debug.h"
+
+#define DEBUG_L2_EXTRA 0
+#define DEBUG_DUP_CHECK 0
+#define DEBUG_ETH_SL 0
+
+static void rfapiMonitorTimerRestart(struct rfapi_monitor_vpn *m);
+
+static void rfapiMonitorEthTimerRestart(struct rfapi_monitor_eth *m);
+
+/*
+ * Forward declarations
+ */
+static void rfapiMonitorEthDetachImport(struct bgp *bgp,
+ struct rfapi_monitor_eth *mon);
+
+#if DEBUG_ETH_SL
+/*
+ * Debug function, special case
+ */
+void rfapiMonitorEthSlCheck(struct agg_node *rn, const char *tag1,
+ const char *tag2)
+{
+ struct agg_node *rn_saved = NULL;
+ static struct skiplist *sl_saved = NULL;
+ struct skiplist *sl;
+
+ if (!rn)
+ return;
+
+ if (rn_saved && (rn != rn_saved))
+ return;
+
+ if (!rn_saved)
+ rn_saved = rn;
+
+ sl = RFAPI_MONITOR_ETH(rn);
+ if (sl || sl_saved) {
+ vnc_zlog_debug_verbose(
+ "%s[%s%s]: rn=%p, rn->lock=%d, old sl=%p, new sl=%p",
+ __func__, (tag1 ? tag1 : ""), (tag2 ? tag2 : ""), rn,
+ rn->lock, sl_saved, sl);
+ sl_saved = sl;
+ }
+}
+#endif
+
+/*
+ * Debugging function that aborts when it finds monitors whose
+ * "next" pointer * references themselves
+ */
+void rfapiMonitorLoopCheck(struct rfapi_monitor_vpn *mchain)
+{
+ struct rfapi_monitor_vpn *m;
+
+ for (m = mchain; m; m = m->next)
+ assert(m != m->next);
+}
+
+#if DEBUG_DUP_CHECK
+/*
+ * Debugging code: see if a monitor is mentioned more than once
+ * in a HD's monitor list
+ */
+void rfapiMonitorDupCheck(struct bgp *bgp)
+{
+ struct listnode *hnode;
+ struct rfapi_descriptor *rfd;
+
+ for (ALL_LIST_ELEMENTS_RO(&bgp->rfapi->descriptors, hnode, rfd)) {
+ struct agg_node *mrn;
+
+ if (!rfd->mon)
+ continue;
+
+ for (mrn = agg_route_top(rfd->mon); mrn;
+ mrn = agg_route_next(mrn)) {
+ struct rfapi_monitor_vpn *m;
+ for (m = (struct rfapi_monitor_vpn *)(mrn->info); m;
+ m = m->next)
+ m->dcount = 0;
+ }
+ }
+
+ for (ALL_LIST_ELEMENTS_RO(&bgp->rfapi->descriptors, hnode, rfd)) {
+ struct agg_node *mrn;
+
+ if (!rfd->mon)
+ continue;
+
+ for (mrn = agg_route_top(rfd->mon); mrn;
+ mrn = agg_route_next(mrn)) {
+ struct rfapi_monitor_vpn *m;
+
+ for (m = (struct rfapi_monitor_vpn *)(mrn->info); m;
+ m = m->next)
+ assert(++m->dcount == 1);
+ }
+ }
+}
+#endif
+
+/* debug */
+void rfapiMonitorCleanCheck(struct bgp *bgp)
+{
+ struct listnode *hnode;
+ struct rfapi_descriptor *rfd;
+
+ for (ALL_LIST_ELEMENTS_RO(&bgp->rfapi->descriptors, hnode, rfd)) {
+ assert(!rfd->import_table->vpn0_queries[AFI_IP]);
+ assert(!rfd->import_table->vpn0_queries[AFI_IP6]);
+
+ struct agg_node *rn;
+
+ for (rn = agg_route_top(
+ rfd->import_table->imported_vpn[AFI_IP]);
+ rn; rn = agg_route_next(rn)) {
+
+ assert(!RFAPI_MONITOR_VPN(rn));
+ }
+ for (rn = agg_route_top(
+ rfd->import_table->imported_vpn[AFI_IP6]);
+ rn; rn = agg_route_next(rn)) {
+
+ assert(!RFAPI_MONITOR_VPN(rn));
+ }
+ }
+}
+
+/* debug */
+void rfapiMonitorCheckAttachAllowed(void)
+{
+ struct bgp *bgp = bgp_get_default();
+ assert(!(bgp->rfapi_cfg->flags & BGP_VNC_CONFIG_CALLBACK_DISABLE));
+}
+
+void rfapiMonitorExtraFlush(safi_t safi, struct agg_node *rn)
+{
+ struct rfapi_it_extra *hie;
+ struct rfapi_monitor_vpn *v;
+ struct rfapi_monitor_vpn *v_next;
+ struct rfapi_monitor_encap *e = NULL;
+ struct rfapi_monitor_encap *e_next = NULL;
+
+ if (!rn)
+ return;
+
+ if (!rn->aggregate)
+ return;
+
+ hie = (struct rfapi_it_extra *)(rn->aggregate);
+
+ switch (safi) {
+ case SAFI_ENCAP:
+ for (e = hie->u.encap.e; e; e = e_next) {
+ e_next = e->next;
+ e->next = NULL;
+ XFREE(MTYPE_RFAPI_MONITOR_ENCAP, e);
+ agg_unlock_node(rn);
+ }
+ hie->u.encap.e = NULL;
+ break;
+
+ case SAFI_MPLS_VPN:
+ for (v = hie->u.vpn.v; v; v = v_next) {
+ v_next = v->next;
+ v->next = NULL;
+ XFREE(MTYPE_RFAPI_MONITOR, e);
+ agg_unlock_node(rn);
+ }
+ hie->u.vpn.v = NULL;
+ if (hie->u.vpn.e.source) {
+ while (!skiplist_delete_first(hie->u.vpn.e.source)) {
+ agg_unlock_node(rn);
+ }
+ skiplist_free(hie->u.vpn.e.source);
+ hie->u.vpn.e.source = NULL;
+ agg_unlock_node(rn);
+ }
+ if (hie->u.vpn.idx_rd) {
+ /* looping through bpi->extra->vnc.import.rd is tbd */
+ while (!skiplist_delete_first(hie->u.vpn.idx_rd)) {
+ agg_unlock_node(rn);
+ }
+ skiplist_free(hie->u.vpn.idx_rd);
+ hie->u.vpn.idx_rd = NULL;
+ agg_unlock_node(rn);
+ }
+ if (hie->u.vpn.mon_eth) {
+ while (!skiplist_delete_first(hie->u.vpn.mon_eth)) {
+ agg_unlock_node(rn);
+ }
+ skiplist_free(hie->u.vpn.mon_eth);
+ hie->u.vpn.mon_eth = NULL;
+ agg_unlock_node(rn);
+ }
+ break;
+
+ default:
+ assert(0);
+ }
+ XFREE(MTYPE_RFAPI_IT_EXTRA, hie);
+ rn->aggregate = NULL;
+ agg_unlock_node(rn);
+}
+
+/*
+ * If the child lists are empty, release the rfapi_it_extra struct
+ */
+void rfapiMonitorExtraPrune(safi_t safi, struct agg_node *rn)
+{
+ struct rfapi_it_extra *hie;
+
+ if (!rn)
+ return;
+
+ if (!rn->aggregate)
+ return;
+
+ hie = (struct rfapi_it_extra *)(rn->aggregate);
+
+ switch (safi) {
+ case SAFI_ENCAP:
+ if (hie->u.encap.e)
+ return;
+ break;
+
+ case SAFI_MPLS_VPN:
+ if (hie->u.vpn.v)
+ return;
+ if (hie->u.vpn.mon_eth) {
+ if (skiplist_count(hie->u.vpn.mon_eth))
+ return;
+ skiplist_free(hie->u.vpn.mon_eth);
+ hie->u.vpn.mon_eth = NULL;
+ agg_unlock_node(rn); /* uncount skiplist */
+ }
+ if (hie->u.vpn.e.source) {
+ if (skiplist_count(hie->u.vpn.e.source))
+ return;
+ skiplist_free(hie->u.vpn.e.source);
+ hie->u.vpn.e.source = NULL;
+ agg_unlock_node(rn);
+ }
+ if (hie->u.vpn.idx_rd) {
+ if (skiplist_count(hie->u.vpn.idx_rd))
+ return;
+ skiplist_free(hie->u.vpn.idx_rd);
+ hie->u.vpn.idx_rd = NULL;
+ agg_unlock_node(rn);
+ }
+ if (hie->u.vpn.mon_eth) {
+ if (skiplist_count(hie->u.vpn.mon_eth))
+ return;
+ skiplist_free(hie->u.vpn.mon_eth);
+ hie->u.vpn.mon_eth = NULL;
+ agg_unlock_node(rn);
+ }
+ break;
+
+ default:
+ assert(0);
+ }
+ XFREE(MTYPE_RFAPI_IT_EXTRA, hie);
+ rn->aggregate = NULL;
+ agg_unlock_node(rn);
+}
+
+/*
+ * returns locked node
+ */
+struct agg_node *rfapiMonitorGetAttachNode(struct rfapi_descriptor *rfd,
+ struct prefix *p)
+{
+ afi_t afi;
+ struct agg_node *rn;
+
+ if (RFAPI_0_PREFIX(p)) {
+ assert(1);
+ }
+
+ afi = family2afi(p->family);
+ assert(afi);
+
+ /*
+ * It's possible that even though there is a route at this node,
+ * there are no routes with valid UN addresses (i.e,. with no
+ * valid tunnel routes). Check for that and walk back up the
+ * tree if necessary.
+ *
+ * When the outer loop completes, the matched node, if any, is
+ * locked (i.e., its reference count has been incremented) to
+ * account for the VPN monitor we are about to attach.
+ *
+ * if a monitor is moved to another node, there must be
+ * corresponding unlock/locks
+ */
+ for (rn = agg_node_match(rfd->import_table->imported_vpn[afi], p);
+ rn;) {
+
+ struct bgp_path_info *bpi;
+ struct prefix pfx_dummy;
+
+ /* TBD update this code to use new valid_interior_count */
+ for (bpi = rn->info; bpi; bpi = bpi->next) {
+ /*
+ * If there is a cached ENCAP UN address, it's a usable
+ * VPN route
+ */
+ if (bpi->extra && bpi->extra->vnc.import.un_family) {
+ break;
+ }
+
+ /*
+ * Or if there is a valid Encap Attribute tunnel subtlv
+ * address,
+ * it's a usable VPN route.
+ */
+ if (!rfapiGetVncTunnelUnAddr(bpi->attr, &pfx_dummy)) {
+ break;
+ }
+ }
+ if (bpi)
+ break;
+
+ agg_unlock_node(rn);
+ if ((rn = agg_node_parent(rn))) {
+ agg_lock_node(rn);
+ }
+ }
+
+ if (!rn) {
+ struct prefix pfx_default;
+
+ memset(&pfx_default, 0, sizeof(pfx_default));
+ pfx_default.family = p->family;
+
+ /* creates default node if none exists, and increments ref count
+ */
+ rn = agg_node_get(rfd->import_table->imported_vpn[afi],
+ &pfx_default);
+ }
+
+ return rn;
+}
+
+/*
+ * If this function happens to attach the monitor to a radix tree
+ * node (as opposed to the 0-prefix list), the node pointer is
+ * returned (for the benefit of caller which might like to use it
+ * to generate an immediate query response).
+ */
+static struct agg_node *rfapiMonitorAttachImport(struct rfapi_descriptor *rfd,
+ struct rfapi_monitor_vpn *m)
+{
+ struct agg_node *rn;
+
+ rfapiMonitorCheckAttachAllowed();
+
+ if (RFAPI_0_PREFIX(&m->p)) {
+ /*
+ * Add new monitor entry to vpn0 list
+ */
+ afi_t afi;
+
+ afi = family2afi(m->p.family);
+ assert(afi);
+
+ m->next = rfd->import_table->vpn0_queries[afi];
+ rfd->import_table->vpn0_queries[afi] = m;
+ vnc_zlog_debug_verbose("%s: attached monitor %p to vpn0 list",
+ __func__, m);
+ return NULL;
+ }
+
+ /*
+ * Attach new monitor entry to import table node
+ */
+ rn = rfapiMonitorGetAttachNode(rfd, &m->p); /* returns locked rn */
+ m->node = rn;
+ m->next = RFAPI_MONITOR_VPN(rn);
+ RFAPI_MONITOR_VPN_W_ALLOC(rn) = m;
+ RFAPI_CHECK_REFCOUNT(rn, SAFI_MPLS_VPN, 0);
+ vnc_zlog_debug_verbose("%s: attached monitor %p to rn %p", __func__, m,
+ rn);
+ return rn;
+}
+
+
+/*
+ * reattach monitors for this HD to import table
+ */
+void rfapiMonitorAttachImportHd(struct rfapi_descriptor *rfd)
+{
+ struct agg_node *mrn;
+
+ if (!rfd->mon) {
+ /*
+ * No monitors for this HD
+ */
+ return;
+ }
+
+ for (mrn = agg_route_top(rfd->mon); mrn; mrn = agg_route_next(mrn)) {
+
+ if (!mrn->info)
+ continue;
+
+ (void)rfapiMonitorAttachImport(
+ rfd, (struct rfapi_monitor_vpn *)(mrn->info));
+ }
+}
+
+/*
+ * Adds a monitor for a query to the NVE descriptor's list
+ * and, if callbacks are enabled, attaches it to the import table.
+ *
+ * If we happened to locate the import table radix tree attachment
+ * point, return it so the caller can use it to generate a query
+ * response without repeating the lookup. Note that when callbacks
+ * are disabled, this function will not perform a lookup, and the
+ * caller will have to do its own lookup.
+ */
+struct agg_node *rfapiMonitorAdd(struct bgp *bgp, struct rfapi_descriptor *rfd,
+ struct prefix *p)
+{
+ struct rfapi_monitor_vpn *m;
+ struct agg_node *rn;
+
+ /*
+ * Initialize nve's monitor list if needed
+ * NB use the same radix tree for IPv4 and IPv6 targets.
+ * The prefix will always have full-length mask (/32, /128)
+ * or be 0/0 so they won't get mixed up.
+ */
+ if (!rfd->mon) {
+ rfd->mon = agg_table_init();
+ }
+ rn = agg_node_get(rfd->mon, p);
+ if (rn->info) {
+ /*
+ * received this query before, no further action needed
+ */
+ rfapiMonitorTimerRestart((struct rfapi_monitor_vpn *)rn->info);
+ agg_unlock_node(rn);
+ return NULL;
+ }
+
+ /*
+ * New query for this nve, record it in the HD
+ */
+ rn->info =
+ XCALLOC(MTYPE_RFAPI_MONITOR, sizeof(struct rfapi_monitor_vpn));
+ m = (struct rfapi_monitor_vpn *)(rn->info);
+ m->rfd = rfd;
+ prefix_copy(&m->p, p);
+
+ ++rfd->monitor_count;
+ ++bgp->rfapi->monitor_count;
+
+ rfapiMonitorTimerRestart(m);
+
+ if (bgp->rfapi_cfg->flags & BGP_VNC_CONFIG_CALLBACK_DISABLE) {
+ /*
+ * callbacks turned off, so don't attach monitor to import table
+ */
+ return NULL;
+ }
+
+
+ /*
+ * attach to import table
+ */
+ return rfapiMonitorAttachImport(rfd, m);
+}
+
+/*
+ * returns monitor pointer if found, NULL if not
+ */
+static struct rfapi_monitor_vpn *
+rfapiMonitorDetachImport(struct rfapi_monitor_vpn *m)
+{
+ struct rfapi_monitor_vpn *prev;
+ struct rfapi_monitor_vpn *this = NULL;
+
+ if (RFAPI_0_PREFIX(&m->p)) {
+ afi_t afi;
+
+ /*
+ * 0-prefix monitors are stored in a special list and not
+ * in the import VPN tree
+ */
+
+ afi = family2afi(m->p.family);
+ assert(afi);
+
+ if (m->rfd->import_table) {
+ for (prev = NULL,
+ this = m->rfd->import_table->vpn0_queries[afi];
+ this; prev = this, this = this->next) {
+
+ if (this == m)
+ break;
+ }
+ if (this) {
+ if (!prev) {
+ m->rfd->import_table
+ ->vpn0_queries[afi] =
+ this->next;
+ } else {
+ prev->next = this->next;
+ }
+ }
+ }
+ } else {
+
+ if (m->node) {
+ for (prev = NULL, this = RFAPI_MONITOR_VPN(m->node);
+ this; prev = this, this = this->next) {
+
+ if (this == m)
+ break;
+ }
+ if (this) {
+ if (prev) {
+ prev->next = this->next;
+ } else {
+ RFAPI_MONITOR_VPN_W_ALLOC(m->node) =
+ this->next;
+ }
+ RFAPI_CHECK_REFCOUNT(m->node, SAFI_MPLS_VPN, 1);
+ agg_unlock_node(m->node);
+ }
+ m->node = NULL;
+ }
+ }
+ return this;
+}
+
+
+void rfapiMonitorDetachImportHd(struct rfapi_descriptor *rfd)
+{
+ struct agg_node *rn;
+
+ if (!rfd->mon)
+ return;
+
+ for (rn = agg_route_top(rfd->mon); rn; rn = agg_route_next(rn)) {
+ if (rn->info) {
+ rfapiMonitorDetachImport(
+ (struct rfapi_monitor_vpn *)(rn->info));
+ }
+ }
+}
+
+void rfapiMonitorDel(struct bgp *bgp, struct rfapi_descriptor *rfd,
+ struct prefix *p)
+{
+ struct agg_node *rn;
+ struct rfapi_monitor_vpn *m;
+
+ assert(rfd->mon);
+ rn = agg_node_get(rfd->mon, p); /* locks node */
+ m = rn->info;
+
+ assert(m);
+
+ /*
+ * remove from import table
+ */
+ if (!(bgp->rfapi_cfg->flags & BGP_VNC_CONFIG_CALLBACK_DISABLE)) {
+ rfapiMonitorDetachImport(m);
+ }
+
+ THREAD_OFF(m->timer);
+
+ /*
+ * remove from rfd list
+ */
+ XFREE(MTYPE_RFAPI_MONITOR, m);
+ rn->info = NULL;
+ agg_unlock_node(rn); /* undo original lock when created */
+ agg_unlock_node(rn); /* undo lock in agg_node_get */
+
+ --rfd->monitor_count;
+ --bgp->rfapi->monitor_count;
+}
+
+/*
+ * returns count of monitors deleted
+ */
+int rfapiMonitorDelHd(struct rfapi_descriptor *rfd)
+{
+ struct agg_node *rn;
+ struct bgp *bgp;
+ int count = 0;
+
+ vnc_zlog_debug_verbose("%s: entry rfd=%p", __func__, rfd);
+
+ bgp = bgp_get_default();
+
+ if (rfd->mon) {
+ for (rn = agg_route_top(rfd->mon); rn;
+ rn = agg_route_next(rn)) {
+ struct rfapi_monitor_vpn *m;
+ if ((m = rn->info)) {
+ if (!(bgp->rfapi_cfg->flags
+ & BGP_VNC_CONFIG_CALLBACK_DISABLE)) {
+ rfapiMonitorDetachImport(m);
+ }
+
+ THREAD_OFF(m->timer);
+
+ XFREE(MTYPE_RFAPI_MONITOR, m);
+ rn->info = NULL;
+ agg_unlock_node(rn); /* undo original lock
+ when created */
+ ++count;
+ --rfd->monitor_count;
+ --bgp->rfapi->monitor_count;
+ }
+ }
+ agg_table_finish(rfd->mon);
+ rfd->mon = NULL;
+ }
+
+ if (rfd->mon_eth) {
+
+ struct rfapi_monitor_eth *mon_eth;
+
+ while (!skiplist_first(rfd->mon_eth, NULL, (void **)&mon_eth)) {
+
+ int rc;
+
+ if (!(bgp->rfapi_cfg->flags
+ & BGP_VNC_CONFIG_CALLBACK_DISABLE)) {
+ rfapiMonitorEthDetachImport(bgp, mon_eth);
+ } else {
+#if DEBUG_L2_EXTRA
+ vnc_zlog_debug_verbose(
+ "%s: callbacks disabled, not attempting to detach mon_eth %p",
+ __func__, mon_eth);
+#endif
+ }
+
+ THREAD_OFF(mon_eth->timer);
+
+ /*
+ * remove from rfd list
+ */
+ rc = skiplist_delete(rfd->mon_eth, mon_eth, mon_eth);
+ assert(!rc);
+
+ vnc_zlog_debug_verbose("%s: freeing mon_eth %p",
+ __func__, mon_eth);
+ XFREE(MTYPE_RFAPI_MONITOR_ETH, mon_eth);
+
+ ++count;
+ --rfd->monitor_count;
+ --bgp->rfapi->monitor_count;
+ }
+ skiplist_free(rfd->mon_eth);
+ rfd->mon_eth = NULL;
+ }
+
+ return count;
+}
+
+void rfapiMonitorResponseRemovalOff(struct bgp *bgp)
+{
+ if (bgp->rfapi_cfg->flags & BGP_VNC_CONFIG_RESPONSE_REMOVAL_DISABLE) {
+ return;
+ }
+ bgp->rfapi_cfg->flags |= BGP_VNC_CONFIG_RESPONSE_REMOVAL_DISABLE;
+}
+
+void rfapiMonitorResponseRemovalOn(struct bgp *bgp)
+{
+ if (!(bgp->rfapi_cfg->flags
+ & BGP_VNC_CONFIG_RESPONSE_REMOVAL_DISABLE)) {
+ return;
+ }
+ bgp->rfapi_cfg->flags &= ~BGP_VNC_CONFIG_RESPONSE_REMOVAL_DISABLE;
+}
+
+static void rfapiMonitorTimerExpire(struct thread *t)
+{
+ struct rfapi_monitor_vpn *m = THREAD_ARG(t);
+
+ /* forget reference to thread, it's gone */
+ m->timer = NULL;
+
+ /* delete the monitor */
+ rfapiMonitorDel(bgp_get_default(), m->rfd, &m->p);
+}
+
+static void rfapiMonitorTimerRestart(struct rfapi_monitor_vpn *m)
+{
+ unsigned long remain = thread_timer_remain_second(m->timer);
+
+ /* unexpected case, but avoid wraparound problems below */
+ if (remain > m->rfd->response_lifetime)
+ return;
+
+ /* don't restart if we just restarted recently */
+ if (m->rfd->response_lifetime - remain < 2)
+ return;
+
+ THREAD_OFF(m->timer);
+
+ {
+ char buf[BUFSIZ];
+
+ vnc_zlog_debug_verbose(
+ "%s: target %s life %u", __func__,
+ rfapi_ntop(m->p.family, m->p.u.val, buf, BUFSIZ),
+ m->rfd->response_lifetime);
+ }
+
+ thread_add_timer(bm->master, rfapiMonitorTimerExpire, m,
+ m->rfd->response_lifetime, &m->timer);
+}
+
+/*
+ * called when an updated response is sent to the NVE. Per
+ * ticket 255, restart timers for any monitors that could have
+ * been responsible for the response, i.e., any monitors for
+ * the exact prefix or a parent of it.
+ */
+void rfapiMonitorTimersRestart(struct rfapi_descriptor *rfd,
+ const struct prefix *p)
+{
+ struct agg_node *rn;
+
+ if (AF_ETHERNET == p->family) {
+ struct rfapi_monitor_eth *mon_eth;
+ int rc;
+ void *cursor;
+
+ /*
+ * XXX match any LNI
+ */
+ for (cursor = NULL,
+ rc = skiplist_next(rfd->mon_eth, NULL, (void **)&mon_eth,
+ &cursor);
+ rc == 0; rc = skiplist_next(rfd->mon_eth, NULL,
+ (void **)&mon_eth, &cursor)) {
+
+ if (!memcmp(mon_eth->macaddr.octet,
+ p->u.prefix_eth.octet, ETH_ALEN)) {
+
+ rfapiMonitorEthTimerRestart(mon_eth);
+ }
+ }
+
+ } else {
+ for (rn = agg_route_top(rfd->mon); rn;
+ rn = agg_route_next(rn)) {
+ struct rfapi_monitor_vpn *m;
+ const struct prefix *p_node;
+
+ if (!((m = rn->info)))
+ continue;
+
+ p_node = agg_node_get_prefix(m->node);
+ /* NB order of test is significant ! */
+ if (!m->node || prefix_match(p_node, p)) {
+ rfapiMonitorTimerRestart(m);
+ }
+ }
+ }
+}
+
+/*
+ * Find monitors at this node and all its parents. Call
+ * rfapiRibUpdatePendingNode with this node and all corresponding NVEs.
+ */
+void rfapiMonitorItNodeChanged(
+ struct rfapi_import_table *import_table, struct agg_node *it_node,
+ struct rfapi_monitor_vpn *monitor_list) /* for base it node, NULL=all */
+{
+ struct skiplist *nves_seen;
+ struct agg_node *rn = it_node;
+ struct bgp *bgp = bgp_get_default();
+ const struct prefix *p = agg_node_get_prefix(rn);
+ afi_t afi = family2afi(p->family);
+
+ assert(bgp);
+ assert(import_table);
+
+ nves_seen = skiplist_new(0, NULL, NULL);
+
+#if DEBUG_L2_EXTRA
+ vnc_zlog_debug_verbose("%s: it=%p, it_node=%p, it_node->prefix=%pFX",
+ __func__, import_table, it_node, &it_node->p);
+#endif
+
+ if (AFI_L2VPN == afi) {
+ struct rfapi_monitor_eth *m;
+ struct skiplist *sl;
+ void *cursor;
+ int rc;
+
+ if ((sl = RFAPI_MONITOR_ETH(rn))) {
+
+ for (cursor = NULL,
+ rc = skiplist_next(sl, NULL, (void **)&m, &cursor);
+ !rc; rc = skiplist_next(sl, NULL, (void **)&m,
+ &cursor)) {
+
+ if (skiplist_search(nves_seen, m->rfd, NULL)) {
+ /*
+ * Haven't done this NVE yet. Add to
+ * "seen" list.
+ */
+ assert(!skiplist_insert(nves_seen,
+ m->rfd, NULL));
+
+ /*
+ * update its RIB
+ */
+ rfapiRibUpdatePendingNode(
+ bgp, m->rfd, import_table,
+ it_node,
+ m->rfd->response_lifetime);
+ }
+ }
+ }
+
+ } else {
+
+ struct rfapi_monitor_vpn *m;
+
+ if (monitor_list) {
+ m = monitor_list;
+ } else {
+ m = RFAPI_MONITOR_VPN(rn);
+ }
+
+ do {
+ /*
+ * If we have reached the root node (parent==NULL) and
+ * there
+ * are no routes here (info==NULL), and the IT node that
+ * changed was not the root node (it_node->parent !=
+ * NULL),
+ * then any monitors at this node are here because they
+ * had
+ * no match at all. Therefore, do not send route updates
+ * to them
+ * because we haven't sent them an initial route.
+ */
+ if (!agg_node_parent(rn) && !rn->info
+ && it_node->parent)
+ break;
+
+ for (; m; m = m->next) {
+
+ if (RFAPI_0_PREFIX(&m->p)) {
+ /* shouldn't happen, but be safe */
+ continue;
+ }
+ if (skiplist_search(nves_seen, m->rfd, NULL)) {
+ /*
+ * Haven't done this NVE yet. Add to
+ * "seen" list.
+ */
+ assert(!skiplist_insert(nves_seen,
+ m->rfd, NULL));
+
+ vnc_zlog_debug_verbose(
+ "%s: update rfd %p attached to pfx %pRN (targ=%pFX)",
+ __func__, m->rfd, m->node,
+ &m->p);
+
+ /*
+ * update its RIB
+ */
+ rfapiRibUpdatePendingNode(
+ bgp, m->rfd, import_table,
+ it_node,
+ m->rfd->response_lifetime);
+ }
+ }
+ rn = agg_node_parent(rn);
+ if (rn)
+ m = RFAPI_MONITOR_VPN(rn);
+ } while (rn);
+ }
+
+ /*
+ * All-routes L2 monitors
+ */
+ if (AFI_L2VPN == afi) {
+ struct rfapi_monitor_eth *e;
+
+#if DEBUG_L2_EXTRA
+ vnc_zlog_debug_verbose("%s: checking L2 all-routes monitors",
+ __func__);
+#endif
+
+ for (e = import_table->eth0_queries; e; e = e->next) {
+#if DEBUG_L2_EXTRA
+ vnc_zlog_debug_verbose("%s: checking eth0 mon=%p",
+ __func__, e);
+#endif
+ if (skiplist_search(nves_seen, e->rfd, NULL)) {
+ /*
+ * Haven't done this NVE yet. Add to "seen"
+ * list.
+ */
+ assert(!skiplist_insert(nves_seen, e->rfd,
+ NULL));
+
+/*
+ * update its RIB
+ */
+#if DEBUG_L2_EXTRA
+ vnc_zlog_debug_verbose(
+ "%s: found L2 all-routes monitor %p",
+ __func__, e);
+#endif
+ rfapiRibUpdatePendingNode(
+ bgp, e->rfd, import_table, it_node,
+ e->rfd->response_lifetime);
+ }
+ }
+ } else {
+ struct rfapi_monitor_vpn *m;
+
+ /*
+ * All-routes IPv4. IPv6 monitors
+ */
+ for (m = import_table->vpn0_queries[afi]; m; m = m->next) {
+ if (skiplist_search(nves_seen, m->rfd, NULL)) {
+ /*
+ * Haven't done this NVE yet. Add to "seen"
+ * list.
+ */
+ assert(!skiplist_insert(nves_seen, m->rfd,
+ NULL));
+
+ /*
+ * update its RIB
+ */
+ rfapiRibUpdatePendingNode(
+ bgp, m->rfd, import_table, it_node,
+ m->rfd->response_lifetime);
+ }
+ }
+ }
+
+ skiplist_free(nves_seen);
+}
+
+/*
+ * For the listed monitors, update new node and its subtree, but
+ * omit old node and its subtree
+ */
+void rfapiMonitorMovedUp(struct rfapi_import_table *import_table,
+ struct agg_node *old_node, struct agg_node *new_node,
+ struct rfapi_monitor_vpn *monitor_list)
+{
+ struct bgp *bgp = bgp_get_default();
+ struct rfapi_monitor_vpn *m;
+
+ assert(new_node);
+ assert(old_node);
+ assert(new_node != old_node);
+
+ /*
+ * If new node is 0/0 and there is no route there, don't
+ * generate an update because it will not contain any
+ * routes including the target.
+ */
+ if (!new_node->parent && !new_node->info) {
+ vnc_zlog_debug_verbose(
+ "%s: new monitor at 0/0 and no routes, no updates",
+ __func__);
+ return;
+ }
+
+ for (m = monitor_list; m; m = m->next) {
+ rfapiRibUpdatePendingNode(bgp, m->rfd, import_table, new_node,
+ m->rfd->response_lifetime);
+ rfapiRibUpdatePendingNodeSubtree(bgp, m->rfd, import_table,
+ new_node, old_node,
+ m->rfd->response_lifetime);
+ }
+}
+
+static void rfapiMonitorEthTimerExpire(struct thread *t)
+{
+ struct rfapi_monitor_eth *m = THREAD_ARG(t);
+
+ /* forget reference to thread, it's gone */
+ m->timer = NULL;
+
+ /* delete the monitor */
+ rfapiMonitorEthDel(bgp_get_default(), m->rfd, &m->macaddr,
+ m->logical_net_id);
+
+}
+
+static void rfapiMonitorEthTimerRestart(struct rfapi_monitor_eth *m)
+{
+ unsigned long remain = thread_timer_remain_second(m->timer);
+
+ /* unexpected case, but avoid wraparound problems below */
+ if (remain > m->rfd->response_lifetime)
+ return;
+
+ /* don't restart if we just restarted recently */
+ if (m->rfd->response_lifetime - remain < 2)
+ return;
+
+ THREAD_OFF(m->timer);
+
+ {
+ char buf[BUFSIZ];
+
+ vnc_zlog_debug_verbose(
+ "%s: target %s life %u", __func__,
+ rfapiEthAddr2Str(&m->macaddr, buf, BUFSIZ),
+ m->rfd->response_lifetime);
+ }
+
+ thread_add_timer(bm->master, rfapiMonitorEthTimerExpire, m,
+ m->rfd->response_lifetime, &m->timer);
+}
+
+static int mon_eth_cmp(const void *a, const void *b)
+{
+ const struct rfapi_monitor_eth *m1;
+ const struct rfapi_monitor_eth *m2;
+
+ int i;
+
+ m1 = (struct rfapi_monitor_eth *)a;
+ m2 = (struct rfapi_monitor_eth *)b;
+
+ /*
+ * compare ethernet addresses
+ */
+ for (i = 0; i < ETH_ALEN; ++i) {
+ if (m1->macaddr.octet[i] != m2->macaddr.octet[i])
+ return (m1->macaddr.octet[i] - m2->macaddr.octet[i]);
+ }
+
+ /*
+ * compare LNIs
+ */
+ return (m1->logical_net_id - m2->logical_net_id);
+}
+
+static void rfapiMonitorEthAttachImport(
+ struct rfapi_import_table *it,
+ struct agg_node *rn, /* it node attach point if non-0 */
+ struct rfapi_monitor_eth *mon) /* monitor struct to attach */
+{
+ struct skiplist *sl;
+ int rc;
+
+ vnc_zlog_debug_verbose("%s: it=%p", __func__, it);
+
+ rfapiMonitorCheckAttachAllowed();
+
+ if (RFAPI_0_ETHERADDR(&mon->macaddr)) {
+ /*
+ * These go on a different list
+ */
+ mon->next = it->eth0_queries;
+ it->eth0_queries = mon;
+#if DEBUG_L2_EXTRA
+ vnc_zlog_debug_verbose("%s: attached monitor %p to eth0 list",
+ __func__, mon);
+#endif
+ return;
+ }
+
+ if (rn == NULL) {
+#if DEBUG_L2_EXTRA
+ vnc_zlog_debug_verbose("%s: rn is null!", __func__);
+#endif
+ return;
+ }
+
+ /*
+ * Get sl to attach to
+ */
+ sl = RFAPI_MONITOR_ETH_W_ALLOC(rn);
+ if (!sl) {
+ sl = RFAPI_MONITOR_ETH_W_ALLOC(rn) =
+ skiplist_new(0, NULL, NULL);
+ agg_lock_node(rn); /* count skiplist mon_eth */
+ }
+
+#if DEBUG_L2_EXTRA
+ vnc_zlog_debug_verbose(
+ "%s: rn=%p, rn->lock=%d, sl=%p, attaching eth mon %p", __func__,
+ rn, rn->lock, sl, mon);
+#endif
+
+ rc = skiplist_insert(sl, (void *)mon, (void *)mon);
+ assert(!rc);
+
+ /* count eth monitor */
+ agg_lock_node(rn);
+}
+
+/*
+ * reattach monitors for this HD to import table
+ */
+static void rfapiMonitorEthAttachImportHd(struct bgp *bgp,
+ struct rfapi_descriptor *rfd)
+{
+ void *cursor;
+ struct rfapi_monitor_eth *mon;
+ int rc;
+
+ if (!rfd->mon_eth) {
+ /*
+ * No monitors for this HD
+ */
+ return;
+ }
+
+ for (cursor = NULL,
+ rc = skiplist_next(rfd->mon_eth, NULL, (void **)&mon, &cursor);
+ rc == 0;
+ rc = skiplist_next(rfd->mon_eth, NULL, (void **)&mon, &cursor)) {
+
+ struct rfapi_import_table *it;
+ struct prefix pfx_mac_buf;
+ struct agg_node *rn;
+
+ it = rfapiMacImportTableGet(bgp, mon->logical_net_id);
+ assert(it);
+
+ memset((void *)&pfx_mac_buf, 0, sizeof(struct prefix));
+ pfx_mac_buf.family = AF_ETHERNET;
+ pfx_mac_buf.prefixlen = 48;
+ pfx_mac_buf.u.prefix_eth = mon->macaddr;
+
+ rn = agg_node_get(it->imported_vpn[AFI_L2VPN], &pfx_mac_buf);
+ assert(rn);
+
+ (void)rfapiMonitorEthAttachImport(it, rn, mon);
+ }
+}
+
+static void rfapiMonitorEthDetachImport(
+ struct bgp *bgp,
+ struct rfapi_monitor_eth *mon) /* monitor struct to detach */
+{
+ struct rfapi_import_table *it;
+ struct prefix pfx_mac_buf;
+ struct skiplist *sl;
+ struct agg_node *rn;
+ int rc;
+
+ it = rfapiMacImportTableGet(bgp, mon->logical_net_id);
+ assert(it);
+
+ if (RFAPI_0_ETHERADDR(&mon->macaddr)) {
+ struct rfapi_monitor_eth *prev;
+ struct rfapi_monitor_eth *this = NULL;
+
+ for (prev = NULL, this = it->eth0_queries; this;
+ prev = this, this = this->next) {
+
+ if (this == mon)
+ break;
+ }
+ if (this) {
+ if (!prev) {
+ it->eth0_queries = this->next;
+ } else {
+ prev->next = this->next;
+ }
+ }
+#if DEBUG_L2_EXTRA
+ vnc_zlog_debug_verbose(
+ "%s: it=%p, LNI=%d, detached eth0 mon %p", __func__, it,
+ mon->logical_net_id, mon);
+#endif
+ return;
+ }
+
+ memset((void *)&pfx_mac_buf, 0, sizeof(struct prefix));
+ pfx_mac_buf.family = AF_ETHERNET;
+ pfx_mac_buf.prefixlen = 48;
+ pfx_mac_buf.u.prefix_eth = mon->macaddr;
+
+ rn = agg_node_get(it->imported_vpn[AFI_L2VPN], &pfx_mac_buf);
+ assert(rn);
+
+ /*
+ * Get sl to detach from
+ */
+ sl = RFAPI_MONITOR_ETH(rn);
+#if DEBUG_L2_EXTRA
+ vnc_zlog_debug_verbose(
+ "%s: it=%p, rn=%p, rn->lock=%d, sl=%p, pfx=%pFX, LNI=%d, detaching eth mon %p",
+ __func__, it, rn, rn->lock, sl, agg_node_get_prefix(rn),
+ mon->logical_net_id, mon);
+#endif
+ assert(sl);
+
+
+ rc = skiplist_delete(sl, (void *)mon, (void *)mon);
+ assert(!rc);
+
+ /* uncount eth monitor */
+ agg_unlock_node(rn);
+}
+
+struct agg_node *rfapiMonitorEthAdd(struct bgp *bgp,
+ struct rfapi_descriptor *rfd,
+ struct ethaddr *macaddr,
+ uint32_t logical_net_id)
+{
+ int rc;
+ struct rfapi_monitor_eth mon_buf;
+ struct rfapi_monitor_eth *val;
+ struct rfapi_import_table *it;
+ struct agg_node *rn = NULL;
+ struct prefix pfx_mac_buf;
+
+ if (!rfd->mon_eth) {
+ rfd->mon_eth = skiplist_new(0, mon_eth_cmp, NULL);
+ }
+
+ it = rfapiMacImportTableGet(bgp, logical_net_id);
+ assert(it);
+
+ /*
+ * Get route node in import table. Here is where we attach the
+ * monitor.
+ *
+ * Look it up now because we return it to caller regardless of
+ * whether we create a new monitor or not.
+ */
+ memset((void *)&pfx_mac_buf, 0, sizeof(struct prefix));
+ pfx_mac_buf.family = AF_ETHERNET;
+ pfx_mac_buf.prefixlen = 48;
+ pfx_mac_buf.u.prefix_eth = *macaddr;
+
+ if (!RFAPI_0_ETHERADDR(macaddr)) {
+ rn = agg_node_get(it->imported_vpn[AFI_L2VPN], &pfx_mac_buf);
+ assert(rn);
+ }
+
+ memset((void *)&mon_buf, 0, sizeof(mon_buf));
+ mon_buf.rfd = rfd;
+ mon_buf.macaddr = *macaddr;
+ mon_buf.logical_net_id = logical_net_id;
+
+ {
+ char buf[BUFSIZ];
+
+ vnc_zlog_debug_verbose(
+ "%s: LNI=%d: rfd=%p, pfx=%s", __func__, logical_net_id,
+ rfd, rfapi_ntop(pfx_mac_buf.family, pfx_mac_buf.u.val,
+ buf, BUFSIZ));
+ }
+
+
+ /*
+ * look up query
+ */
+ rc = skiplist_search(rfd->mon_eth, (void *)&mon_buf, (void **)&val);
+ if (!rc) {
+ /*
+ * Found monitor - we have seen this query before
+ * restart timer
+ */
+ vnc_zlog_debug_verbose(
+ "%s: already present in rfd->mon_eth, not adding",
+ __func__);
+ rfapiMonitorEthTimerRestart(val);
+ return rn;
+ }
+
+ /*
+ * New query
+ */
+ val = XCALLOC(MTYPE_RFAPI_MONITOR_ETH,
+ sizeof(struct rfapi_monitor_eth));
+ assert(val);
+ *val = mon_buf;
+
+ ++rfd->monitor_count;
+ ++bgp->rfapi->monitor_count;
+
+ rc = skiplist_insert(rfd->mon_eth, val, val);
+
+#if DEBUG_L2_EXTRA
+ vnc_zlog_debug_verbose("%s: inserted rfd=%p mon_eth=%p, rc=%d",
+ __func__, rfd, val, rc);
+#else
+ (void)rc;
+#endif
+
+ /*
+ * start timer
+ */
+ rfapiMonitorEthTimerRestart(val);
+
+ if (bgp->rfapi_cfg->flags & BGP_VNC_CONFIG_CALLBACK_DISABLE) {
+/*
+ * callbacks turned off, so don't attach monitor to import table
+ */
+#if DEBUG_L2_EXTRA
+ vnc_zlog_debug_verbose(
+ "%s: callbacks turned off, not attaching mon_eth %p to import table",
+ __func__, val);
+#endif
+ return rn;
+ }
+
+ /*
+ * attach to import table
+ */
+ rfapiMonitorEthAttachImport(it, rn, val);
+
+ return rn;
+}
+
+void rfapiMonitorEthDel(struct bgp *bgp, struct rfapi_descriptor *rfd,
+ struct ethaddr *macaddr, uint32_t logical_net_id)
+{
+ struct rfapi_monitor_eth *val;
+ struct rfapi_monitor_eth mon_buf;
+ int rc;
+
+ vnc_zlog_debug_verbose("%s: entry rfd=%p", __func__, rfd);
+
+ assert(rfd->mon_eth);
+
+ memset((void *)&mon_buf, 0, sizeof(mon_buf));
+ mon_buf.macaddr = *macaddr;
+ mon_buf.logical_net_id = logical_net_id;
+
+ rc = skiplist_search(rfd->mon_eth, (void *)&mon_buf, (void **)&val);
+ assert(!rc);
+
+ /*
+ * remove from import table
+ */
+ if (!(bgp->rfapi_cfg->flags & BGP_VNC_CONFIG_CALLBACK_DISABLE)) {
+ rfapiMonitorEthDetachImport(bgp, val);
+ }
+
+ THREAD_OFF(val->timer);
+
+ /*
+ * remove from rfd list
+ */
+ rc = skiplist_delete(rfd->mon_eth, val, val);
+ assert(!rc);
+
+#if DEBUG_L2_EXTRA
+ vnc_zlog_debug_verbose("%s: freeing mon_eth %p", __func__, val);
+#endif
+ XFREE(MTYPE_RFAPI_MONITOR_ETH, val);
+
+ --rfd->monitor_count;
+ --bgp->rfapi->monitor_count;
+}
+
+
+void rfapiMonitorCallbacksOff(struct bgp *bgp)
+{
+ struct rfapi_import_table *it;
+ afi_t afi;
+ struct agg_table *rt;
+ struct agg_node *rn;
+ void *cursor;
+ int rc;
+ struct rfapi *h = bgp->rfapi;
+
+ if (bgp->rfapi_cfg->flags & BGP_VNC_CONFIG_CALLBACK_DISABLE) {
+ /*
+ * Already off.
+ */
+ return;
+ }
+ bgp->rfapi_cfg->flags |= BGP_VNC_CONFIG_CALLBACK_DISABLE;
+
+#if DEBUG_L2_EXTRA
+ vnc_zlog_debug_verbose("%s: turned off callbacks", __func__);
+#endif
+
+ if (h == NULL)
+ return;
+ /*
+ * detach monitors from import VPN tables. The monitors
+ * will still be linked in per-nve monitor lists.
+ */
+ for (it = h->imports; it; it = it->next) {
+ for (afi = AFI_IP; afi < AFI_MAX; ++afi) {
+
+ struct rfapi_monitor_vpn *m;
+ struct rfapi_monitor_vpn *next;
+
+ rt = it->imported_vpn[afi];
+
+ for (rn = agg_route_top(rt); rn;
+ rn = agg_route_next(rn)) {
+ m = RFAPI_MONITOR_VPN(rn);
+ if (RFAPI_MONITOR_VPN(rn))
+ RFAPI_MONITOR_VPN_W_ALLOC(rn) = NULL;
+ for (; m; m = next) {
+ next = m->next;
+ m->next =
+ NULL; /* gratuitous safeness */
+ m->node = NULL;
+ agg_unlock_node(rn); /* uncount */
+ }
+ }
+
+ for (m = it->vpn0_queries[afi]; m; m = next) {
+ next = m->next;
+ m->next = NULL; /* gratuitous safeness */
+ m->node = NULL;
+ }
+ it->vpn0_queries[afi] = NULL; /* detach first monitor */
+ }
+ }
+
+ /*
+ * detach monitors from import Eth tables. The monitors
+ * will still be linked in per-nve monitor lists.
+ */
+
+ /*
+ * Loop over ethernet import tables
+ */
+ for (cursor = NULL,
+ rc = skiplist_next(h->import_mac, NULL, (void **)&it, &cursor);
+ !rc;
+ rc = skiplist_next(h->import_mac, NULL, (void **)&it, &cursor)) {
+ struct rfapi_monitor_eth *e;
+ struct rfapi_monitor_eth *enext;
+
+ /*
+ * The actual route table
+ */
+ rt = it->imported_vpn[AFI_L2VPN];
+
+ /*
+ * Find non-0 monitors (i.e., actual addresses, not FTD
+ * monitors)
+ */
+ for (rn = agg_route_top(rt); rn; rn = agg_route_next(rn)) {
+ struct skiplist *sl;
+
+ sl = RFAPI_MONITOR_ETH(rn);
+ while (!skiplist_delete_first(sl)) {
+ agg_unlock_node(rn); /* uncount monitor */
+ }
+ }
+
+ /*
+ * Find 0-monitors (FTD queries)
+ */
+ for (e = it->eth0_queries; e; e = enext) {
+#if DEBUG_L2_EXTRA
+ vnc_zlog_debug_verbose("%s: detaching eth0 mon %p",
+ __func__, e);
+#endif
+ enext = e->next;
+ e->next = NULL; /* gratuitous safeness */
+ }
+ it->eth0_queries = NULL; /* detach first monitor */
+ }
+}
+
+void rfapiMonitorCallbacksOn(struct bgp *bgp)
+{
+ struct listnode *hnode;
+ struct rfapi_descriptor *rfd;
+
+ if (!(bgp->rfapi_cfg->flags & BGP_VNC_CONFIG_CALLBACK_DISABLE)) {
+ /*
+ * Already on. It's important that we don't try to reattach
+ * monitors that are already attached because, in the interest
+ * of performance, there is no checking at the lower level
+ * whether a monitor is already attached. It leads to
+ * corrupted chains (e.g., looped pointers)
+ */
+ return;
+ }
+ bgp->rfapi_cfg->flags &= ~BGP_VNC_CONFIG_CALLBACK_DISABLE;
+#if DEBUG_L2_EXTRA
+ vnc_zlog_debug_verbose("%s: turned on callbacks", __func__);
+#endif
+ if (bgp->rfapi == NULL)
+ return;
+
+ /*
+ * reattach monitors
+ */
+ for (ALL_LIST_ELEMENTS_RO(&bgp->rfapi->descriptors, hnode, rfd)) {
+
+ rfapiMonitorAttachImportHd(rfd);
+ rfapiMonitorEthAttachImportHd(bgp, rfd);
+ }
+}
diff --git a/bgpd/rfapi/rfapi_monitor.h b/bgpd/rfapi/rfapi_monitor.h
new file mode 100644
index 0000000..3a2248a
--- /dev/null
+++ b/bgpd/rfapi/rfapi_monitor.h
@@ -0,0 +1,190 @@
+/*
+ *
+ * Copyright 2009-2016, LabN Consulting, L.L.C.
+ *
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program; see the file COPYING; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
+ */
+
+#ifndef QUAGGA_HGP_RFAPI_MONITOR_H
+#define QUAGGA_HGP_RFAPI_MONITOR_H
+
+#include "lib/zebra.h"
+#include "lib/prefix.h"
+#include "lib/table.h"
+
+/*
+ * These get attached to the nodes in an import table (using "aggregate" ptr)
+ * to indicate which nves are interested in a prefix/target
+ */
+struct rfapi_monitor_vpn {
+ struct rfapi_monitor_vpn *next; /* chain from struct agg_node */
+ struct rfapi_descriptor *rfd; /* which NVE requested the route */
+ struct prefix p; /* constant: pfx in original request */
+ struct agg_node *node; /* node we're currently attached to */
+ uint32_t flags;
+#define RFAPI_MON_FLAG_NEEDCALLBACK 0x00000001 /* deferred callback */
+
+ // int dcount; /* debugging counter */
+ struct thread *timer;
+};
+
+struct rfapi_monitor_encap {
+ struct rfapi_monitor_encap *next;
+ struct rfapi_monitor_encap *prev;
+ struct agg_node *node; /* VPN node */
+ struct bgp_path_info *bpi; /* VPN bpi */
+ struct agg_node *rn; /* parent node */
+};
+
+struct rfapi_monitor_eth {
+ struct rfapi_monitor_eth *next; /* for use in vpn0_queries list */
+ struct rfapi_descriptor *rfd; /* which NVE requested the route */
+ struct ethaddr macaddr;
+ uint32_t logical_net_id;
+ struct thread *timer;
+};
+
+/*
+ * This is referenced by the "aggregate" field of a route node
+ * in an RFAPI import table.
+ *
+ * node lock/unlock:
+ * - one lock increment for this structure itself
+ * - one lock per chained struct rfapi_monitor_vpn
+ * - one lock for the mon_eth skiplist itself
+ * - one lock per mon_eth skiplist entry
+ * - one lock for the ext skiplist itself
+ * - one lock for each ext skiplist entry
+ * remember to free skiplist when freeing rfapi_it_extra
+ * - one lock per chained struct rfapi_monitor_encap
+ *
+ */
+struct rfapi_it_extra {
+ union {
+ struct {
+ struct rfapi_monitor_vpn *v;
+ struct skiplist *idx_rd; /* RD index */
+ struct skiplist *mon_eth; /* ether queries */
+ struct {
+ /* routes with UN addrs, either cached encap or
+ * Encap TLV */
+ int valid_interior_count;
+
+ /* unicast exterior routes, key=bpi,
+ * val=allocated prefix */
+ struct skiplist *source;
+ } e;
+ } vpn;
+ struct {
+ struct rfapi_monitor_encap *e;
+ } encap;
+ } u;
+};
+
+#define RFAPI_IT_EXTRA_GET(rn) \
+ ((struct rfapi_it_extra \
+ *)((rn)->aggregate \
+ ? (rn)->aggregate \
+ : (agg_lock_node(rn), \
+ (rn)->aggregate = XCALLOC( \
+ MTYPE_RFAPI_IT_EXTRA, \
+ sizeof(struct rfapi_it_extra)))))
+
+#define RFAPI_RDINDEX(rn) \
+ ((rn)->aggregate ? RFAPI_IT_EXTRA_GET(rn)->u.vpn.idx_rd : NULL)
+
+#define RFAPI_RDINDEX_W_ALLOC(rn) (RFAPI_IT_EXTRA_GET(rn)->u.vpn.idx_rd)
+
+#define RFAPI_MONITOR_ETH(rn) \
+ ((rn)->aggregate ? RFAPI_IT_EXTRA_GET(rn)->u.vpn.mon_eth : NULL)
+
+#define RFAPI_MONITOR_ETH_W_ALLOC(rn) (RFAPI_IT_EXTRA_GET(rn)->u.vpn.mon_eth)
+
+#define RFAPI_MONITOR_VPN(rn) \
+ ((rn)->aggregate ? RFAPI_IT_EXTRA_GET(rn)->u.vpn.v : NULL)
+
+#define RFAPI_MONITOR_VPN_W_ALLOC(rn) (RFAPI_IT_EXTRA_GET(rn)->u.vpn.v)
+
+#define RFAPI_MONITOR_ENCAP(rn) \
+ ((rn)->aggregate ? RFAPI_IT_EXTRA_GET(rn)->u.encap.e : NULL)
+
+#define RFAPI_MONITOR_ENCAP_W_ALLOC(rn) (RFAPI_IT_EXTRA_GET(rn)->u.encap.e)
+
+#define RFAPI_MONITOR_EXTERIOR(rn) (&(RFAPI_IT_EXTRA_GET(rn)->u.vpn.e))
+
+#define RFAPI_HAS_MONITOR_EXTERIOR(rn) \
+ (rn && rn->aggregate \
+ && ((struct rfapi_it_extra *)(rn->aggregate))->u.vpn.e.source \
+ && !skiplist_first(((struct rfapi_it_extra *)(rn->aggregate)) \
+ ->u.vpn.e.source, \
+ NULL, NULL))
+
+extern void rfapiMonitorLoopCheck(struct rfapi_monitor_vpn *mchain);
+
+extern void rfapiMonitorCleanCheck(struct bgp *bgp);
+
+extern void rfapiMonitorCheckAttachAllowed(void);
+
+extern void rfapiMonitorExtraFlush(safi_t safi, struct agg_node *rn);
+
+extern struct agg_node *rfapiMonitorGetAttachNode(struct rfapi_descriptor *rfd,
+ struct prefix *p);
+
+extern void rfapiMonitorAttachImportHd(struct rfapi_descriptor *rfd);
+
+extern struct agg_node *rfapiMonitorAdd(struct bgp *bgp,
+ struct rfapi_descriptor *rfd,
+ struct prefix *p);
+
+extern void rfapiMonitorDetachImportHd(struct rfapi_descriptor *rfd);
+
+extern void rfapiMonitorDel(struct bgp *bgp, struct rfapi_descriptor *rfd,
+ struct prefix *p);
+
+extern int rfapiMonitorDelHd(struct rfapi_descriptor *rfd);
+
+extern void rfapiMonitorCallbacksOff(struct bgp *bgp);
+
+extern void rfapiMonitorCallbacksOn(struct bgp *bgp);
+
+extern void rfapiMonitorResponseRemovalOff(struct bgp *bgp);
+
+extern void rfapiMonitorResponseRemovalOn(struct bgp *bgp);
+
+extern void rfapiMonitorExtraPrune(safi_t safi, struct agg_node *rn);
+
+extern void rfapiMonitorTimersRestart(struct rfapi_descriptor *rfd,
+ const struct prefix *p);
+
+extern void rfapiMonitorItNodeChanged(struct rfapi_import_table *import_table,
+ struct agg_node *it_node,
+ struct rfapi_monitor_vpn *monitor_list);
+
+extern void rfapiMonitorMovedUp(struct rfapi_import_table *import_table,
+ struct agg_node *old_node,
+ struct agg_node *new_node,
+ struct rfapi_monitor_vpn *monitor_list);
+
+extern struct agg_node *rfapiMonitorEthAdd(struct bgp *bgp,
+ struct rfapi_descriptor *rfd,
+ struct ethaddr *macaddr,
+ uint32_t logical_net_id);
+
+extern void rfapiMonitorEthDel(struct bgp *bgp, struct rfapi_descriptor *rfd,
+ struct ethaddr *macaddr,
+ uint32_t logical_net_id);
+
+#endif /* QUAGGA_HGP_RFAPI_MONITOR_H */
diff --git a/bgpd/rfapi/rfapi_nve_addr.c b/bgpd/rfapi/rfapi_nve_addr.c
new file mode 100644
index 0000000..b8193f1
--- /dev/null
+++ b/bgpd/rfapi/rfapi_nve_addr.c
@@ -0,0 +1,158 @@
+/*
+ *
+ * Copyright 2009-2016, LabN Consulting, L.L.C.
+ *
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program; see the file COPYING; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
+ */
+
+
+#include "lib/zebra.h"
+#include "lib/prefix.h"
+#include "lib/agg_table.h"
+#include "lib/vty.h"
+#include "lib/memory.h"
+#include "lib/skiplist.h"
+
+
+#include "bgpd/bgpd.h"
+
+#include "bgpd/rfapi/bgp_rfapi_cfg.h"
+#include "bgpd/rfapi/rfapi.h"
+#include "bgpd/rfapi/rfapi_backend.h"
+
+#include "bgpd/rfapi/rfapi_import.h"
+#include "bgpd/rfapi/rfapi_private.h"
+#include "bgpd/rfapi/rfapi_nve_addr.h"
+#include "bgpd/rfapi/rfapi_vty.h"
+#include "bgpd/rfapi/vnc_debug.h"
+
+#define DEBUG_NVE_ADDR 0
+
+void rfapiNveAddr2Str(struct rfapi_nve_addr *, char *, int);
+
+
+#if DEBUG_NVE_ADDR
+static void logdifferent(const char *tag, struct rfapi_nve_addr *a,
+ struct rfapi_nve_addr *b)
+{
+ char a_str[BUFSIZ];
+ char b_str[BUFSIZ];
+
+ rfapiNveAddr2Str(a, a_str, BUFSIZ);
+ rfapiNveAddr2Str(b, b_str, BUFSIZ);
+ vnc_zlog_debug_verbose("%s: [%s] [%s]", tag, a_str, b_str);
+}
+#endif
+
+
+int rfapi_nve_addr_cmp(const void *k1, const void *k2)
+{
+ const struct rfapi_nve_addr *a = (struct rfapi_nve_addr *)k1;
+ const struct rfapi_nve_addr *b = (struct rfapi_nve_addr *)k2;
+ int ret = 0;
+
+ if (!a || !b) {
+#if DEBUG_NVE_ADDR
+ vnc_zlog_debug_verbose("%s: missing address a=%p b=%p",
+ __func__, a, b);
+#endif
+ return (a - b);
+ }
+ if (a->un.addr_family != b->un.addr_family) {
+#if DEBUG_NVE_ADDR
+ vnc_zlog_debug_verbose(
+ "diff: UN addr fam a->un.af=%d, b->un.af=%d",
+ a->un.addr_family, b->un.addr_family);
+#endif
+ return (a->un.addr_family - b->un.addr_family);
+ }
+ if (a->un.addr_family == AF_INET) {
+ ret = IPV4_ADDR_CMP(&a->un.addr.v4, &b->un.addr.v4);
+ if (ret != 0) {
+#if DEBUG_NVE_ADDR
+ logdifferent("diff: UN addr", a, b);
+#endif
+ return ret;
+ }
+ } else if (a->un.addr_family == AF_INET6) {
+ ret = IPV6_ADDR_CMP(&a->un.addr.v6, &b->un.addr.v6);
+ if (ret == 0) {
+#if DEBUG_NVE_ADDR
+ logdifferent("diff: UN addr", a, b);
+#endif
+ return ret;
+ }
+ } else {
+ assert(0);
+ }
+ if (a->vn.addr_family != b->vn.addr_family) {
+#if DEBUG_NVE_ADDR
+ vnc_zlog_debug_verbose(
+ "diff: pT addr fam a->vn.af=%d, b->vn.af=%d",
+ a->vn.addr_family, b->vn.addr_family);
+#endif
+ return (a->vn.addr_family - b->vn.addr_family);
+ }
+ if (a->vn.addr_family == AF_INET) {
+ ret = IPV4_ADDR_CMP(&a->vn.addr.v4, &b->vn.addr.v4);
+ if (ret != 0) {
+#if DEBUG_NVE_ADDR
+ logdifferent("diff: VN addr", a, b);
+#endif
+ return ret;
+ }
+ } else if (a->vn.addr_family == AF_INET6) {
+ ret = IPV6_ADDR_CMP(&a->vn.addr.v6, &b->vn.addr.v6);
+ if (ret == 0) {
+#if DEBUG_NVE_ADDR
+ logdifferent("diff: VN addr", a, b);
+#endif
+ return ret;
+ }
+ } else {
+ assert(0);
+ }
+ return 0;
+}
+
+void rfapiNveAddr2Str(struct rfapi_nve_addr *na, char *buf, int bufsize)
+{
+ char *p = buf;
+ int r;
+
+#define REMAIN (bufsize - (p-buf))
+#define INCP {p += (r > REMAIN)? REMAIN: r;}
+
+ if (bufsize < 1)
+ return;
+
+ r = snprintf(p, REMAIN, "VN=");
+ INCP;
+
+ if (!rfapiRfapiIpAddr2Str(&na->vn, p, REMAIN))
+ goto done;
+
+ buf[bufsize - 1] = 0;
+ p = buf + strlen(buf);
+
+ r = snprintf(p, REMAIN, ", UN=");
+ INCP;
+
+ rfapiRfapiIpAddr2Str(&na->un, p, REMAIN);
+
+done:
+ buf[bufsize - 1] = 0;
+}
diff --git a/bgpd/rfapi/rfapi_nve_addr.h b/bgpd/rfapi/rfapi_nve_addr.h
new file mode 100644
index 0000000..7bcb3ca
--- /dev/null
+++ b/bgpd/rfapi/rfapi_nve_addr.h
@@ -0,0 +1,38 @@
+/*
+ *
+ * Copyright 2009-2016, LabN Consulting, L.L.C.
+ *
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program; see the file COPYING; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
+ */
+
+#ifndef _QUAGGA_BGP_RFAPI_NVE_ADDR_H
+#define _QUAGGA_BGP_RFAPI_NVE_ADDR_H
+
+#include "rfapi.h"
+
+struct rfapi_nve_addr {
+ struct rfapi_ip_addr vn;
+ struct rfapi_ip_addr un;
+ void *info;
+};
+
+
+extern int rfapi_nve_addr_cmp(const void *k1, const void *k2);
+
+extern void rfapiNveAddr2Str(struct rfapi_nve_addr *na, char *buf, int bufsize);
+
+
+#endif /* _QUAGGA_BGP_RFAPI_NVE_ADDR_H */
diff --git a/bgpd/rfapi/rfapi_private.h b/bgpd/rfapi/rfapi_private.h
new file mode 100644
index 0000000..8c76e1d
--- /dev/null
+++ b/bgpd/rfapi/rfapi_private.h
@@ -0,0 +1,413 @@
+/*
+ *
+ * Copyright 2009-2016, LabN Consulting, L.L.C.
+ *
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program; see the file COPYING; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
+ */
+
+/*
+ * Internal definitions for RFAPI. Not for use by other code
+ */
+
+#ifndef _QUAGGA_BGP_RFAPI_PRIVATE_H
+#define _QUAGGA_BGP_RFAPI_PRIVATE_H
+
+#include "lib/linklist.h"
+#include "lib/skiplist.h"
+#include "lib/workqueue.h"
+
+#include "bgpd/bgp_attr.h"
+#include "bgpd/bgp_route.h"
+
+#include "rfapi.h"
+
+/*
+ * Lists of rfapi_adb. Each rfapi_adb is referenced twice:
+ *
+ * 1. each is referenced in by_lifetime
+ * 2. each is referenced by exactly one of: ipN_by_prefix, ip0_by_ether
+ */
+struct rfapi_advertised_prefixes {
+ struct skiplist *ipN_by_prefix; /* all except 0/32, 0/128 */
+ struct skiplist *ip0_by_ether; /* ip prefix 0/32, 0/128 */
+ struct skiplist *by_lifetime; /* all */
+};
+
+struct rfapi_descriptor {
+ struct agg_node *un_node; /* backref to un table */
+
+ struct rfapi_descriptor *next; /* next vn_addr */
+
+ /* supplied by client */
+ struct bgp *bgp; /* from rfp_start_val */
+ struct rfapi_ip_addr vn_addr;
+ struct rfapi_ip_addr un_addr;
+ rfapi_response_cb_t *response_cb; /* override per-bgp response_cb */
+ void *cookie; /* for callbacks */
+ struct rfapi_tunneltype_option default_tunneltype_option;
+
+ /* supplied by matched configuration */
+ struct prefix_rd rd;
+ struct ecommunity *rt_export_list;
+ uint32_t response_lifetime;
+
+ /* list of prefixes currently being advertised by this nve */
+ struct rfapi_advertised_prefixes advertised;
+
+ time_t open_time;
+
+ uint32_t max_prefix_lifetime;
+ uint32_t min_prefix_lifetime;
+
+ /* reference to this nve's import table */
+ struct rfapi_import_table *import_table;
+
+ uint32_t monitor_count;
+ struct agg_table *mon; /* rfapi_monitors */
+ struct skiplist *mon_eth; /* ethernet monitors */
+
+ /*
+ * rib RIB as seen by NVE
+ * rib_pending RIB containing nodes with updated info chains
+ * rsp_times last time we sent response containing pfx
+ */
+ uint32_t rib_prefix_count; /* pfxes with routes */
+ struct agg_table *rib[AFI_MAX];
+ struct agg_table *rib_pending[AFI_MAX];
+ struct work_queue *updated_responses_queue;
+ struct agg_table *rsp_times[AFI_MAX];
+
+ uint32_t rsp_counter; /* dedup initial rsp */
+ time_t rsp_time; /* dedup initial rsp */
+ time_t ftd_last_allowed_time; /* FTD filter */
+
+ unsigned int stat_count_nh_reachable;
+ unsigned int stat_count_nh_removal;
+
+ /*
+ * points to the original nve group structure that matched
+ * when this nve_descriptor was created. We use this pointer
+ * in rfapi_close() to find the nve group structure and
+ * delete its reference back to us.
+ *
+ * If the nve group structure is deleted (via configuration
+ * change) while this nve_descriptor exists, this rfg pointer
+ * will be set to NULL.
+ */
+ struct rfapi_nve_group_cfg *rfg;
+
+ /*
+ * This ~7kB structure is here to permit multiple routes for
+ * a prefix to be injected to BGP. There are at least two
+ * situations where such conditions obtain:
+ *
+ * When an VNC route is exported to BGP on behalf of the set of
+ * NVEs that belong to the export NVE group, it is replicated
+ * so that there is one route per NVE (and the route's nexthop
+ * is the NVE's VN address).
+ *
+ * Each of these routes being injected to BGP must have a distinct
+ * peer pointer (otherwise, if they have the same peer pointer, each
+ * route will be considered an implicit waithdraw of the previous
+ * route injected from that peer, and the new route will replace
+ * rather than augment the old one(s)).
+ */
+ struct peer *peer;
+
+ uint32_t flags;
+#define RFAPI_HD_FLAG_CALLBACK_SCHEDULED_AFI_IP 0x00000001
+#define RFAPI_HD_FLAG_CALLBACK_SCHEDULED_AFI_IP6 0x00000002
+#define RFAPI_HD_FLAG_CALLBACK_SCHEDULED_AFI_L2VPN 0x00000004
+#define RFAPI_HD_FLAG_PROVISIONAL 0x00000008
+#define RFAPI_HD_FLAG_CLOSING_ADMINISTRATIVELY 0x00000010
+#define RFAPI_HD_FLAG_IS_VRF 0x00000012
+};
+
+#define RFAPI_QUEUED_FLAG(afi) \
+ (((afi) == AFI_IP) \
+ ? RFAPI_HD_FLAG_CALLBACK_SCHEDULED_AFI_IP \
+ : (((afi) == AFI_IP6) \
+ ? RFAPI_HD_FLAG_CALLBACK_SCHEDULED_AFI_IP6 \
+ : (((afi) == AFI_L2VPN) \
+ ? RFAPI_HD_FLAG_CALLBACK_SCHEDULED_AFI_L2VPN \
+ : (assert(0), 0))))
+
+
+struct rfapi_global_stats {
+ time_t last_reset;
+ unsigned int max_descriptors;
+
+ unsigned int count_unknown_nves;
+
+ unsigned int count_queries;
+ unsigned int count_queries_failed;
+
+ unsigned int max_responses; /* semantics? */
+
+ unsigned int count_registrations;
+ unsigned int count_registrations_failed;
+
+ unsigned int count_updated_response_updates;
+ unsigned int count_updated_response_deletes;
+};
+
+/*
+ * There is one of these per BGP instance.
+ *
+ * Radix tree is indexed by un address; follow chain and
+ * check vn address to get exact match.
+ */
+struct rfapi {
+ struct agg_table *un[AFI_MAX];
+ struct rfapi_import_table *imports; /* IPv4, IPv6 */
+ struct list descriptors; /* debug & resolve-nve imports */
+
+ struct rfapi_global_stats stat;
+
+ /*
+ * callbacks into RFP, set at startup time (bgp_rfapi_new() gets
+ * values from rfp_start()) or via rfapi_rfp_set_cb_methods()
+ * (otherwise NULL). Note that the response_cb method can also
+ * be overridden per-rfd (currently used only for debug/test scenarios)
+ */
+ struct rfapi_rfp_cb_methods rfp_methods;
+
+ /*
+ * Import tables for Ethernet over IPSEC
+ *
+ * The skiplist keys are LNIs. Values are pointers
+ * to struct rfapi_import_table.
+ */
+ struct skiplist *import_mac; /* L2 */
+
+ /*
+ * when exporting plain routes ("registered-nve" mode) to
+ * bgp unicast or zebra, we need to keep track of information
+ * related to expiring the routes according to the VNC lifetime
+ */
+ struct agg_table *rt_export_bgp[AFI_MAX];
+ struct agg_table *rt_export_zebra[AFI_MAX];
+
+ /*
+ * For VNC->BGP unicast exports in CE mode, we need a
+ * routing table that collects all of the VPN routes
+ * in a single tree. The VPN rib is split up according
+ * to RD first, so we can't use that. This is an import
+ * table that matches all RTs.
+ */
+ struct rfapi_import_table *it_ce;
+
+ /*
+ * when importing bgp-direct routes in resolve-nve mode,
+ * this list maps unicast route nexthops to their bgp_path_infos
+ * in the unicast table
+ */
+ struct skiplist *resolve_nve_nexthop;
+
+ /*
+ * Descriptors for which rfapi_close() was called during a callback.
+ * They will be closed after the callback finishes.
+ */
+ struct work_queue *deferred_close_q;
+
+ /*
+ * For "show vnc responses"
+ */
+ uint32_t response_immediate_count;
+ uint32_t response_updated_count;
+ uint32_t monitor_count;
+
+ uint32_t rib_prefix_count_total;
+ uint32_t rib_prefix_count_total_max;
+
+ uint32_t flags;
+#define RFAPI_INCALLBACK 0x00000001
+ void *rfp; /* from rfp_start */
+};
+
+#define RFAPI_RIB_PREFIX_COUNT_INCR(rfd, rfapi) \
+ do { \
+ ++(rfd)->rib_prefix_count; \
+ ++(rfapi)->rib_prefix_count_total; \
+ if ((rfapi)->rib_prefix_count_total \
+ > (rfapi)->rib_prefix_count_total_max) \
+ ++(rfapi)->rib_prefix_count_total_max; \
+ } while (0)
+
+#define RFAPI_RIB_PREFIX_COUNT_DECR(rfd, rfapi) \
+ do { \
+ --(rfd)->rib_prefix_count; \
+ --(rfapi)->rib_prefix_count_total; \
+ } while (0)
+
+#define RFAPI_0_PREFIX(prefix) \
+ ((((prefix)->family == AF_INET) \
+ ? (prefix)->u.prefix4.s_addr == INADDR_ANY \
+ : (((prefix)->family == AF_INET6) \
+ ? (IN6_IS_ADDR_UNSPECIFIED(&(prefix)->u.prefix6)) \
+ : 0)))
+
+#define RFAPI_0_ETHERADDR(ea) \
+ (((ea)->octet[0] | (ea)->octet[1] | (ea)->octet[2] | (ea)->octet[3] \
+ | (ea)->octet[4] | (ea)->octet[5]) \
+ == 0)
+
+#define RFAPI_HOST_PREFIX(prefix) \
+ (((prefix)->family == AF_INET) \
+ ? ((prefix)->prefixlen == IPV4_MAX_BITLEN) \
+ : (((prefix)->family == AF_INET6) \
+ ? ((prefix)->prefixlen == IPV6_MAX_BITLEN) \
+ : 0))
+
+extern int rfapi_find_rfd(struct bgp *bgp, struct rfapi_ip_addr *vn_addr,
+ struct rfapi_ip_addr *un_addr,
+ struct rfapi_descriptor **rfd);
+
+extern void
+add_vnc_route(struct rfapi_descriptor *rfd, /* cookie + UN addr for VPN */
+ struct bgp *bgp, int safi, const struct prefix *p,
+ struct prefix_rd *prd, struct rfapi_ip_addr *nexthop,
+ uint32_t *local_pref, /* host byte order */
+ uint32_t *lifetime, /* host byte order */
+ struct bgp_tea_options *rfp_options,
+ struct rfapi_un_option *options_un,
+ struct rfapi_vn_option *options_vn,
+ struct ecommunity *rt_export_list, uint32_t *med, uint32_t *label,
+ uint8_t type, uint8_t sub_type, int flags);
+#define RFAPI_AHR_NO_TUNNEL_SUBTLV 0x00000001
+#define RFAPI_AHR_RFPOPT_IS_VNCTLV 0x00000002 /* hack! */
+
+extern void del_vnc_route(struct rfapi_descriptor *rfd, struct peer *peer,
+ struct bgp *bgp, safi_t safi, const struct prefix *p,
+ struct prefix_rd *prd, uint8_t type, uint8_t sub_type,
+ struct rfapi_nexthop *lnh, int kill);
+
+extern int rfapiCliGetPrefixAddr(struct vty *vty, const char *str,
+ struct prefix *p);
+
+extern int rfapiGetVncLifetime(struct attr *attr, uint32_t *lifetime);
+
+extern int rfapiGetVncTunnelUnAddr(struct attr *attr, struct prefix *p);
+
+extern int rfapi_reopen(struct rfapi_descriptor *rfd, struct bgp *bgp);
+
+extern void vnc_import_bgp_add_rfp_host_route_mode_resolve_nve(
+ struct bgp *bgp, struct rfapi_descriptor *rfd, struct prefix *prefix);
+
+extern void vnc_import_bgp_del_rfp_host_route_mode_resolve_nve(
+ struct bgp *bgp, struct rfapi_descriptor *rfd, struct prefix *prefix);
+
+extern void rfapiFreeBgpTeaOptionChain(struct bgp_tea_options *p);
+
+extern struct rfapi_vn_option *rfapiVnOptionsDup(struct rfapi_vn_option *orig);
+
+extern struct rfapi_un_option *rfapiUnOptionsDup(struct rfapi_un_option *orig);
+
+extern struct bgp_tea_options *rfapiOptionsDup(struct bgp_tea_options *orig);
+
+extern int rfapi_ip_addr_cmp(struct rfapi_ip_addr *a1,
+ struct rfapi_ip_addr *a2);
+
+extern uint32_t rfp_cost_to_localpref(uint8_t cost);
+
+extern int rfapi_set_autord_from_vn(struct prefix_rd *rd,
+ struct rfapi_ip_addr *vn);
+
+extern struct rfapi_nexthop *rfapi_nexthop_new(struct rfapi_nexthop *copyme);
+
+extern void rfapi_nexthop_free(void *goner);
+
+extern struct rfapi_vn_option *
+rfapi_vn_options_dup(struct rfapi_vn_option *existing);
+
+extern void rfapi_un_options_free(struct rfapi_un_option *goner);
+
+extern void rfapi_vn_options_free(struct rfapi_vn_option *goner);
+
+extern void vnc_add_vrf_opener(struct bgp *bgp,
+ struct rfapi_nve_group_cfg *rfg);
+extern void clear_vnc_vrf_closer(struct rfapi_nve_group_cfg *rfg);
+/*------------------------------------------
+ * rfapi_extract_l2o
+ *
+ * Find Layer 2 options in an option chain
+ *
+ * input:
+ * pHop option chain
+ *
+ * output:
+ * l2o layer 2 options extracted
+ *
+ * return value:
+ * 0 OK
+ * 1 no options found
+ *
+ --------------------------------------------*/
+extern int rfapi_extract_l2o(
+ struct bgp_tea_options *pHop, /* chain of options */
+ struct rfapi_l2address_option *l2o); /* return extracted value */
+
+/*
+ * compaitibility to old quagga_time call
+ * time_t value in terms of stabilised absolute time.
+ * replacement for POSIX time()
+ *
+ * Please do not use this. This is kept only for
+ * Lou's CI in that that CI compiles against some
+ * private bgp code and it will just fail to compile
+ * without this. Use monotime()
+ */
+extern time_t rfapi_time(time_t *t);
+
+DECLARE_MGROUP(RFAPI);
+DECLARE_MTYPE(RFAPI_CFG);
+DECLARE_MTYPE(RFAPI_GROUP_CFG);
+DECLARE_MTYPE(RFAPI_L2_CFG);
+DECLARE_MTYPE(RFAPI_RFP_GROUP_CFG);
+DECLARE_MTYPE(RFAPI);
+DECLARE_MTYPE(RFAPI_DESC);
+DECLARE_MTYPE(RFAPI_IMPORTTABLE);
+DECLARE_MTYPE(RFAPI_MONITOR);
+DECLARE_MTYPE(RFAPI_MONITOR_ENCAP);
+DECLARE_MTYPE(RFAPI_NEXTHOP);
+DECLARE_MTYPE(RFAPI_VN_OPTION);
+DECLARE_MTYPE(RFAPI_UN_OPTION);
+DECLARE_MTYPE(RFAPI_WITHDRAW);
+DECLARE_MTYPE(RFAPI_RFG_NAME);
+DECLARE_MTYPE(RFAPI_ADB);
+DECLARE_MTYPE(RFAPI_ETI);
+DECLARE_MTYPE(RFAPI_NVE_ADDR);
+DECLARE_MTYPE(RFAPI_PREFIX_BAG);
+DECLARE_MTYPE(RFAPI_IT_EXTRA);
+DECLARE_MTYPE(RFAPI_INFO);
+DECLARE_MTYPE(RFAPI_ADDR);
+DECLARE_MTYPE(RFAPI_UPDATED_RESPONSE_QUEUE);
+DECLARE_MTYPE(RFAPI_RECENT_DELETE);
+DECLARE_MTYPE(RFAPI_L2ADDR_OPT);
+DECLARE_MTYPE(RFAPI_AP);
+DECLARE_MTYPE(RFAPI_MONITOR_ETH);
+
+
+/*
+ * Caller must supply an already-allocated rfd with the "caller"
+ * fields already set (vn_addr, un_addr, callback, cookie)
+ * The advertised_prefixes[] array elements should be NULL to
+ * have this function set them to newly-allocated radix trees.
+ */
+extern int rfapi_init_and_open(struct bgp *bgp, struct rfapi_descriptor *rfd,
+ struct rfapi_nve_group_cfg *rfg);
+
+#endif /* _QUAGGA_BGP_RFAPI_PRIVATE_H */
diff --git a/bgpd/rfapi/rfapi_rib.c b/bgpd/rfapi/rfapi_rib.c
new file mode 100644
index 0000000..50a10c3
--- /dev/null
+++ b/bgpd/rfapi/rfapi_rib.c
@@ -0,0 +1,2433 @@
+/*
+ *
+ * Copyright 2009-2016, LabN Consulting, L.L.C.
+ *
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program; see the file COPYING; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
+ */
+
+/*
+ * File: rfapi_rib.c
+ * Purpose: maintain per-nve ribs and generate change lists
+ */
+
+#include "lib/zebra.h"
+#include "lib/prefix.h"
+#include "lib/agg_table.h"
+#include "lib/vty.h"
+#include "lib/memory.h"
+#include "lib/log.h"
+#include "lib/skiplist.h"
+#include "lib/workqueue.h"
+
+#include "bgpd/bgpd.h"
+#include "bgpd/bgp_route.h"
+#include "bgpd/bgp_ecommunity.h"
+#include "bgpd/bgp_mplsvpn.h"
+#include "bgpd/bgp_vnc_types.h"
+
+#include "bgpd/rfapi/rfapi.h"
+#include "bgpd/rfapi/bgp_rfapi_cfg.h"
+#include "bgpd/rfapi/rfapi_import.h"
+#include "bgpd/rfapi/rfapi_private.h"
+#include "bgpd/rfapi/rfapi_vty.h"
+#include "bgpd/rfapi/vnc_import_bgp.h"
+#include "bgpd/rfapi/rfapi_rib.h"
+#include "bgpd/rfapi/rfapi_monitor.h"
+#include "bgpd/rfapi/rfapi_encap_tlv.h"
+#include "bgpd/rfapi/vnc_debug.h"
+
+#define DEBUG_PROCESS_PENDING_NODE 0
+#define DEBUG_PENDING_DELETE_ROUTE 0
+#define DEBUG_NHL 0
+#define DEBUG_RIB_SL_RD 0
+
+/* forward decl */
+#if DEBUG_NHL
+static void rfapiRibShowRibSl(void *stream, struct prefix *pfx,
+ struct skiplist *sl);
+#endif
+
+/*
+ * RIB
+ * ---
+ * Model of the set of routes currently in the NVE's RIB.
+ *
+ * node->info ptr to "struct skiplist".
+ * MUST be NULL if there are no routes.
+ * key = ptr to struct prefix {vn}
+ * val = ptr to struct rfapi_info
+ * skiplist.del = NULL
+ * skiplist.cmp = vnc_prefix_cmp
+ *
+ * node->aggregate ptr to "struct skiplist".
+ * key = ptr to struct prefix {vn}
+ * val = ptr to struct rfapi_info
+ * skiplist.del = rfapi_info_free
+ * skiplist.cmp = vnc_prefix_cmp
+ *
+ * This skiplist at "aggregate"
+ * contains the routes recently
+ * deleted
+ *
+ *
+ * Pending RIB
+ * -----------
+ * Sparse list of prefixes that need to be updated. Each node
+ * will have the complete set of routes for the prefix.
+ *
+ * node->info ptr to "struct list" (lib/linklist.h)
+ * "Cost List"
+ * List of routes sorted lowest cost first.
+ * This list is how the new complete set
+ * of routes should look.
+ * Set if there are updates to the prefix;
+ * MUST be NULL if there are no updates.
+ *
+ * .data = ptr to struct rfapi_info
+ * list.cmp = NULL (sorted manually)
+ * list.del = rfapi_info_free
+ *
+ * Special case: if node->info is 1, it means
+ * "delete all routes at this prefix".
+ *
+ * node->aggregate ptr to struct skiplist
+ * key = ptr to struct prefix {vn} (part of ri)
+ * val = struct rfapi_info
+ * skiplist.cmp = vnc_prefix_cmp
+ * skiplist.del = NULL
+ *
+ * ptlist is rewritten anew each time
+ * rfapiRibUpdatePendingNode() is called
+ *
+ * THE ptlist VALUES ARE REFERENCES TO THE
+ * rfapi_info STRUCTS IN THE node->info LIST.
+ */
+
+/*
+ * iterate over RIB to count responses, compare with running counters
+ */
+void rfapiRibCheckCounts(
+ int checkstats, /* validate rfd & global counts */
+ unsigned int offset) /* number of ri's held separately */
+{
+ struct rfapi_descriptor *rfd;
+ struct listnode *node;
+
+ struct bgp *bgp = bgp_get_default();
+
+ uint32_t t_pfx_active = 0;
+ uint32_t t_pfx_deleted = 0;
+
+ uint32_t t_ri_active = 0;
+ uint32_t t_ri_deleted = 0;
+ uint32_t t_ri_pend = 0;
+
+ unsigned int alloc_count;
+
+ /*
+ * loop over NVEs
+ */
+ for (ALL_LIST_ELEMENTS_RO(&bgp->rfapi->descriptors, node, rfd)) {
+
+ afi_t afi;
+ uint32_t pfx_active = 0;
+ uint32_t pfx_deleted = 0;
+
+ for (afi = AFI_IP; afi < AFI_MAX; ++afi) {
+
+ struct agg_node *rn;
+
+ for (rn = agg_route_top(rfd->rib[afi]); rn;
+ rn = agg_route_next(rn)) {
+
+ struct skiplist *sl = rn->info;
+ struct skiplist *dsl = rn->aggregate;
+ uint32_t ri_active = 0;
+ uint32_t ri_deleted = 0;
+
+ if (sl) {
+ ri_active = skiplist_count(sl);
+ assert(ri_active);
+ t_ri_active += ri_active;
+ ++pfx_active;
+ ++t_pfx_active;
+ }
+
+ if (dsl) {
+ ri_deleted = skiplist_count(dsl);
+ t_ri_deleted += ri_deleted;
+ ++pfx_deleted;
+ ++t_pfx_deleted;
+ }
+ }
+ for (rn = agg_route_top(rfd->rib_pending[afi]); rn;
+ rn = agg_route_next(rn)) {
+
+ struct list *l = rn->info; /* sorted by cost */
+ struct skiplist *sl = rn->aggregate;
+ uint32_t ri_pend_cost = 0;
+ uint32_t ri_pend_uniq = 0;
+
+ if (sl) {
+ ri_pend_uniq = skiplist_count(sl);
+ }
+
+ if (l && (l != (void *)1)) {
+ ri_pend_cost = l->count;
+ t_ri_pend += l->count;
+ }
+
+ assert(ri_pend_uniq == ri_pend_cost);
+ }
+ }
+
+ if (checkstats) {
+ if (pfx_active != rfd->rib_prefix_count) {
+ vnc_zlog_debug_verbose(
+ "%s: rfd %p actual pfx count %u != running %u",
+ __func__, rfd, pfx_active,
+ rfd->rib_prefix_count);
+ assert(0);
+ }
+ }
+ }
+
+ if (checkstats && bgp->rfapi) {
+ if (t_pfx_active != bgp->rfapi->rib_prefix_count_total) {
+ vnc_zlog_debug_verbose(
+ "%s: actual total pfx count %u != running %u",
+ __func__, t_pfx_active,
+ bgp->rfapi->rib_prefix_count_total);
+ assert(0);
+ }
+ }
+
+ /*
+ * Check against memory allocation count
+ */
+ alloc_count = mtype_stats_alloc(MTYPE_RFAPI_INFO);
+ assert(t_ri_active + t_ri_deleted + t_ri_pend + offset == alloc_count);
+}
+
+static struct rfapi_info *rfapi_info_new(void)
+{
+ return XCALLOC(MTYPE_RFAPI_INFO, sizeof(struct rfapi_info));
+}
+
+void rfapiFreeRfapiUnOptionChain(struct rfapi_un_option *p)
+{
+ while (p) {
+ struct rfapi_un_option *next;
+
+ next = p->next;
+ XFREE(MTYPE_RFAPI_UN_OPTION, p);
+ p = next;
+ }
+}
+
+void rfapiFreeRfapiVnOptionChain(struct rfapi_vn_option *p)
+{
+ while (p) {
+ struct rfapi_vn_option *next;
+
+ next = p->next;
+ XFREE(MTYPE_RFAPI_VN_OPTION, p);
+ p = next;
+ }
+}
+
+
+static void rfapi_info_free(struct rfapi_info *goner)
+{
+ if (goner) {
+ if (goner->tea_options) {
+ rfapiFreeBgpTeaOptionChain(goner->tea_options);
+ goner->tea_options = NULL;
+ }
+ if (goner->un_options) {
+ rfapiFreeRfapiUnOptionChain(goner->un_options);
+ goner->un_options = NULL;
+ }
+ if (goner->vn_options) {
+ rfapiFreeRfapiVnOptionChain(goner->vn_options);
+ goner->vn_options = NULL;
+ }
+ if (goner->timer) {
+ struct rfapi_rib_tcb *tcb;
+
+ tcb = THREAD_ARG(goner->timer);
+ THREAD_OFF(goner->timer);
+ XFREE(MTYPE_RFAPI_RECENT_DELETE, tcb);
+ }
+ XFREE(MTYPE_RFAPI_INFO, goner);
+ }
+}
+
+/*
+ * Timer control block for recently-deleted and expired routes
+ */
+struct rfapi_rib_tcb {
+ struct rfapi_descriptor *rfd;
+ struct skiplist *sl;
+ struct rfapi_info *ri;
+ struct agg_node *rn;
+ int flags;
+#define RFAPI_RIB_TCB_FLAG_DELETED 0x00000001
+};
+
+/*
+ * remove route from rib
+ */
+static void rfapiRibExpireTimer(struct thread *t)
+{
+ struct rfapi_rib_tcb *tcb = THREAD_ARG(t);
+
+ RFAPI_RIB_CHECK_COUNTS(1, 0);
+
+ /*
+ * Forget reference to thread. Otherwise rfapi_info_free() will
+ * attempt to free thread pointer as an option chain
+ */
+ tcb->ri->timer = NULL;
+
+ /* "deleted" skiplist frees ri, "active" doesn't */
+ assert(!skiplist_delete(tcb->sl, &tcb->ri->rk, NULL));
+ if (!tcb->sl->del) {
+ /*
+ * XXX in this case, skiplist has no delete function: we must
+ * therefore delete rfapi_info explicitly.
+ */
+ rfapi_info_free(tcb->ri);
+ }
+
+ if (skiplist_empty(tcb->sl)) {
+ if (CHECK_FLAG(tcb->flags, RFAPI_RIB_TCB_FLAG_DELETED))
+ tcb->rn->aggregate = NULL;
+ else {
+ struct bgp *bgp = bgp_get_default();
+ tcb->rn->info = NULL;
+ RFAPI_RIB_PREFIX_COUNT_DECR(tcb->rfd, bgp->rfapi);
+ }
+ skiplist_free(tcb->sl);
+ agg_unlock_node(tcb->rn);
+ }
+
+ XFREE(MTYPE_RFAPI_RECENT_DELETE, tcb);
+
+ RFAPI_RIB_CHECK_COUNTS(1, 0);
+}
+
+static void rfapiRibStartTimer(struct rfapi_descriptor *rfd,
+ struct rfapi_info *ri,
+ struct agg_node *rn, /* route node attached to */
+ int deleted)
+{
+ struct rfapi_rib_tcb *tcb = NULL;
+
+ if (ri->timer) {
+ tcb = THREAD_ARG(ri->timer);
+ THREAD_OFF(ri->timer);
+ } else {
+ tcb = XCALLOC(MTYPE_RFAPI_RECENT_DELETE,
+ sizeof(struct rfapi_rib_tcb));
+ }
+ tcb->rfd = rfd;
+ tcb->ri = ri;
+ tcb->rn = rn;
+ if (deleted) {
+ tcb->sl = (struct skiplist *)rn->aggregate;
+ SET_FLAG(tcb->flags, RFAPI_RIB_TCB_FLAG_DELETED);
+ } else {
+ tcb->sl = (struct skiplist *)rn->info;
+ UNSET_FLAG(tcb->flags, RFAPI_RIB_TCB_FLAG_DELETED);
+ }
+
+ vnc_zlog_debug_verbose("%s: rfd %p pfx %pRN life %u", __func__, rfd, rn,
+ ri->lifetime);
+
+ thread_add_timer(bm->master, rfapiRibExpireTimer, tcb, ri->lifetime,
+ &ri->timer);
+}
+
+extern void rfapi_rib_key_init(struct prefix *prefix, /* may be NULL */
+ struct prefix_rd *rd, /* may be NULL */
+ struct prefix *aux, /* may be NULL */
+ struct rfapi_rib_key *rk)
+
+{
+ memset((void *)rk, 0, sizeof(struct rfapi_rib_key));
+ if (prefix)
+ rk->vn = *prefix;
+ if (rd)
+ rk->rd = *rd;
+ if (aux)
+ rk->aux_prefix = *aux;
+}
+
+/*
+ * Compares two <struct rfapi_rib_key>s
+ */
+int rfapi_rib_key_cmp(const void *k1, const void *k2)
+{
+ const struct rfapi_rib_key *a = (struct rfapi_rib_key *)k1;
+ const struct rfapi_rib_key *b = (struct rfapi_rib_key *)k2;
+ int ret;
+
+ if (!a || !b)
+ return (a - b);
+
+ ret = vnc_prefix_cmp(&a->vn, &b->vn);
+ if (ret)
+ return ret;
+
+ ret = vnc_prefix_cmp(&a->rd, &b->rd);
+ if (ret)
+ return ret;
+
+ ret = vnc_prefix_cmp(&a->aux_prefix, &b->aux_prefix);
+
+ return ret;
+}
+
+
+/*
+ * Note: this function will claim that two option chains are
+ * different unless their option items are in identical order.
+ * The consequence is that RFP updated responses can be sent
+ * unnecessarily, or that they might contain nexthop items
+ * that are not strictly needed.
+ *
+ * This function could be modified to compare option chains more
+ * thoroughly, but it's not clear that the extra compuation would
+ * be worth it.
+ */
+static int bgp_tea_options_cmp(struct bgp_tea_options *a,
+ struct bgp_tea_options *b)
+{
+ int rc;
+
+ if (!a || !b) {
+ return (a - b);
+ }
+
+ if (a->type != b->type)
+ return (a->type - b->type);
+ if (a->length != b->length)
+ return (a->length = b->length);
+ if ((rc = memcmp(a->value, b->value, a->length)))
+ return rc;
+ if (!a->next != !b->next) { /* logical xor */
+ return (a->next - b->next);
+ }
+ if (a->next)
+ return bgp_tea_options_cmp(a->next, b->next);
+ return 0;
+}
+
+static int rfapi_info_cmp(struct rfapi_info *a, struct rfapi_info *b)
+{
+ int rc;
+
+ if (!a || !b)
+ return (a - b);
+
+ if ((rc = rfapi_rib_key_cmp(&a->rk, &b->rk)))
+ return rc;
+
+ if ((rc = vnc_prefix_cmp(&a->un, &b->un)))
+ return rc;
+
+ if (a->cost != b->cost)
+ return (a->cost - b->cost);
+
+ if (a->lifetime != b->lifetime)
+ return (a->lifetime - b->lifetime);
+
+ if ((rc = bgp_tea_options_cmp(a->tea_options, b->tea_options)))
+ return rc;
+
+ return 0;
+}
+
+void rfapiRibClear(struct rfapi_descriptor *rfd)
+{
+ struct bgp *bgp;
+ afi_t afi;
+
+ if (rfd->bgp)
+ bgp = rfd->bgp;
+ else
+ bgp = bgp_get_default();
+#ifdef DEBUG_L2_EXTRA
+ vnc_zlog_debug_verbose("%s: rfd=%p", __func__, rfd);
+#endif
+
+ for (afi = AFI_IP; afi < AFI_MAX; ++afi) {
+ struct agg_node *pn;
+ struct agg_node *rn;
+
+ if (rfd->rib_pending[afi]) {
+ for (pn = agg_route_top(rfd->rib_pending[afi]); pn;
+ pn = agg_route_next(pn)) {
+ if (pn->aggregate) {
+ /*
+ * free references into the rfapi_info
+ * structures before
+ * freeing the structures themselves
+ */
+ skiplist_free(
+ (struct skiplist
+ *)(pn->aggregate));
+ pn->aggregate = NULL;
+ agg_unlock_node(
+ pn); /* skiplist deleted */
+ }
+ /*
+ * free the rfapi_info structures
+ */
+ if (pn->info) {
+ if (pn->info != (void *)1) {
+ list_delete(
+ (struct list *
+ *)(&pn->info));
+ }
+ pn->info = NULL;
+ /* linklist or 1 deleted */
+ agg_unlock_node(pn);
+ }
+ }
+ }
+ if (rfd->rib[afi]) {
+ for (rn = agg_route_top(rfd->rib[afi]); rn;
+ rn = agg_route_next(rn)) {
+ if (rn->info) {
+
+ struct rfapi_info *ri;
+
+ while (0 == skiplist_first(
+ (struct skiplist *)
+ rn->info,
+ NULL,
+ (void **)&ri)) {
+
+ rfapi_info_free(ri);
+ skiplist_delete_first(
+ (struct skiplist *)
+ rn->info);
+ }
+ skiplist_free(
+ (struct skiplist *)rn->info);
+ rn->info = NULL;
+ agg_unlock_node(rn);
+ RFAPI_RIB_PREFIX_COUNT_DECR(rfd,
+ bgp->rfapi);
+ }
+ if (rn->aggregate) {
+
+ struct rfapi_info *ri_del;
+
+ /* delete skiplist & contents */
+ while (!skiplist_first(
+ (struct skiplist
+ *)(rn->aggregate),
+ NULL, (void **)&ri_del)) {
+
+ /* sl->del takes care of ri_del
+ */
+ skiplist_delete_first((
+ struct skiplist
+ *)(rn->aggregate));
+ }
+ skiplist_free(
+ (struct skiplist
+ *)(rn->aggregate));
+
+ rn->aggregate = NULL;
+ agg_unlock_node(rn);
+ }
+ }
+ }
+ }
+ if (rfd->updated_responses_queue)
+ work_queue_free_and_null(&rfd->updated_responses_queue);
+}
+
+/*
+ * Release all dynamically-allocated memory that is part of an HD's RIB
+ */
+void rfapiRibFree(struct rfapi_descriptor *rfd)
+{
+ afi_t afi;
+
+
+ /*
+ * NB rfd is typically detached from master list, so is not included
+ * in the count performed by RFAPI_RIB_CHECK_COUNTS
+ */
+
+ /*
+ * Free routes attached to radix trees
+ */
+ rfapiRibClear(rfd);
+
+ /* Now the uncounted rfapi_info's are freed, so the check should succeed
+ */
+ RFAPI_RIB_CHECK_COUNTS(1, 0);
+
+ /*
+ * Free radix trees
+ */
+ for (afi = AFI_IP; afi < AFI_MAX; ++afi) {
+ if (rfd->rib_pending[afi])
+ agg_table_finish(rfd->rib_pending[afi]);
+ rfd->rib_pending[afi] = NULL;
+
+ if (rfd->rib[afi])
+ agg_table_finish(rfd->rib[afi]);
+ rfd->rib[afi] = NULL;
+
+ /* NB agg_table_finish frees only prefix nodes, not chained
+ * info */
+ if (rfd->rsp_times[afi])
+ agg_table_finish(rfd->rsp_times[afi]);
+ rfd->rib[afi] = NULL;
+ }
+}
+
+/*
+ * Copies struct bgp_path_info to struct rfapi_info, except for rk fields and un
+ */
+static void rfapiRibBi2Ri(struct bgp_path_info *bpi, struct rfapi_info *ri,
+ uint32_t lifetime)
+{
+ struct bgp_attr_encap_subtlv *pEncap;
+
+ ri->cost = rfapiRfpCost(bpi->attr);
+ ri->lifetime = lifetime;
+
+ /* This loop based on rfapiRouteInfo2NextHopEntry() */
+ for (pEncap = bgp_attr_get_vnc_subtlvs(bpi->attr); pEncap;
+ pEncap = pEncap->next) {
+ struct bgp_tea_options *hop;
+
+ switch (pEncap->type) {
+ case BGP_VNC_SUBTLV_TYPE_LIFETIME:
+ /* use configured lifetime, not attr lifetime */
+ break;
+
+ case BGP_VNC_SUBTLV_TYPE_RFPOPTION:
+ hop = XCALLOC(MTYPE_BGP_TEA_OPTIONS,
+ sizeof(struct bgp_tea_options));
+ assert(hop);
+ hop->type = pEncap->value[0];
+ hop->length = pEncap->value[1];
+ hop->value = XCALLOC(MTYPE_BGP_TEA_OPTIONS_VALUE,
+ pEncap->length - 2);
+ assert(hop->value);
+ memcpy(hop->value, pEncap->value + 2,
+ pEncap->length - 2);
+ if (hop->length > pEncap->length - 2) {
+ zlog_warn(
+ "%s: VNC subtlv length mismatch: RFP option says %d, attr says %d (shrinking)",
+ __func__, hop->length,
+ pEncap->length - 2);
+ hop->length = pEncap->length - 2;
+ }
+ hop->next = ri->tea_options;
+ ri->tea_options = hop;
+ break;
+
+ default:
+ break;
+ }
+ }
+
+ rfapi_un_options_free(ri->un_options); /* maybe free old version */
+ ri->un_options = rfapi_encap_tlv_to_un_option(bpi->attr);
+
+ /*
+ * VN options
+ */
+ if (bpi->extra
+ && decode_rd_type(bpi->extra->vnc.import.rd.val)
+ == RD_TYPE_VNC_ETH) {
+ /* ethernet route */
+
+ struct rfapi_vn_option *vo;
+
+ vo = XCALLOC(MTYPE_RFAPI_VN_OPTION,
+ sizeof(struct rfapi_vn_option));
+ assert(vo);
+
+ vo->type = RFAPI_VN_OPTION_TYPE_L2ADDR;
+
+ /* copy from RD already stored in bpi, so we don't need it_node
+ */
+ memcpy(&vo->v.l2addr.macaddr, bpi->extra->vnc.import.rd.val + 2,
+ ETH_ALEN);
+
+ (void)rfapiEcommunityGetLNI(bgp_attr_get_ecommunity(bpi->attr),
+ &vo->v.l2addr.logical_net_id);
+ (void)rfapiEcommunityGetEthernetTag(
+ bgp_attr_get_ecommunity(bpi->attr),
+ &vo->v.l2addr.tag_id);
+
+ /* local_nve_id comes from RD */
+ vo->v.l2addr.local_nve_id = bpi->extra->vnc.import.rd.val[1];
+
+ /* label comes from MP_REACH_NLRI label */
+ vo->v.l2addr.label = decode_label(&bpi->extra->label[0]);
+
+ rfapi_vn_options_free(
+ ri->vn_options); /* maybe free old version */
+ ri->vn_options = vo;
+ }
+
+ /*
+ * If there is an auxiliary IP address (L2 can have it), copy it
+ */
+ if (bpi->extra && bpi->extra->vnc.import.aux_prefix.family) {
+ ri->rk.aux_prefix = bpi->extra->vnc.import.aux_prefix;
+ }
+}
+
+/*
+ * rfapiRibPreloadBi
+ *
+ * Install route into NVE RIB model so as to be consistent with
+ * caller's response to rfapi_query().
+ *
+ * Also: return indication to caller whether this specific route
+ * should be included in the response to the NVE according to
+ * the following tests:
+ *
+ * 1. If there were prior duplicates of this route in this same
+ * query response, don't include the route.
+ *
+ * RETURN VALUE:
+ *
+ * 0 OK to include route in response
+ * !0 do not include route in response
+ */
+int rfapiRibPreloadBi(
+ struct agg_node *rfd_rib_node, /* NULL = don't preload or filter */
+ struct prefix *pfx_vn, struct prefix *pfx_un, uint32_t lifetime,
+ struct bgp_path_info *bpi)
+{
+ struct rfapi_descriptor *rfd;
+ struct skiplist *slRibPt = NULL;
+ struct rfapi_info *ori = NULL;
+ struct rfapi_rib_key rk;
+ struct agg_node *trn;
+ afi_t afi;
+ const struct prefix *p = agg_node_get_prefix(rfd_rib_node);
+
+ if (!rfd_rib_node)
+ return 0;
+
+ afi = family2afi(p->family);
+
+ rfd = agg_get_table_info(agg_get_table(rfd_rib_node));
+
+ memset((void *)&rk, 0, sizeof(rk));
+ rk.vn = *pfx_vn;
+ rk.rd = bpi->extra->vnc.import.rd;
+
+ /*
+ * If there is an auxiliary IP address (L2 can have it), copy it
+ */
+ if (bpi->extra->vnc.import.aux_prefix.family) {
+ rk.aux_prefix = bpi->extra->vnc.import.aux_prefix;
+ }
+
+ /*
+ * is this route already in NVE's RIB?
+ */
+ slRibPt = (struct skiplist *)rfd_rib_node->info;
+
+ if (slRibPt && !skiplist_search(slRibPt, &rk, (void **)&ori)) {
+
+ if ((ori->rsp_counter == rfd->rsp_counter)
+ && (ori->last_sent_time == rfd->rsp_time)) {
+ return -1; /* duplicate in this response */
+ }
+
+ /* found: update contents of existing route in RIB */
+ ori->un = *pfx_un;
+ rfapiRibBi2Ri(bpi, ori, lifetime);
+ } else {
+ /* not found: add new route to RIB */
+ ori = rfapi_info_new();
+ ori->rk = rk;
+ ori->un = *pfx_un;
+ rfapiRibBi2Ri(bpi, ori, lifetime);
+
+ if (!slRibPt) {
+ slRibPt = skiplist_new(0, rfapi_rib_key_cmp, NULL);
+ rfd_rib_node->info = slRibPt;
+ agg_lock_node(rfd_rib_node);
+ RFAPI_RIB_PREFIX_COUNT_INCR(rfd, rfd->bgp->rfapi);
+ }
+ skiplist_insert(slRibPt, &ori->rk, ori);
+ }
+
+ ori->last_sent_time = monotime(NULL);
+
+ /*
+ * poke timer
+ */
+ RFAPI_RIB_CHECK_COUNTS(0, 0);
+ rfapiRibStartTimer(rfd, ori, rfd_rib_node, 0);
+ RFAPI_RIB_CHECK_COUNTS(0, 0);
+
+ /*
+ * Update last sent time for prefix
+ */
+ trn = agg_node_get(rfd->rsp_times[afi], p); /* locks trn */
+ trn->info = (void *)(uintptr_t)monotime(NULL);
+ if (agg_node_get_lock_count(trn) > 1)
+ agg_unlock_node(trn);
+
+ return 0;
+}
+
+/*
+ * Frees rfapi_info items at node
+ *
+ * Adjust 'rib' and 'rib_pending' as follows:
+ *
+ * If rib_pending node->info is 1 (magic value):
+ * callback: NHL = RIB NHL with lifetime = withdraw_lifetime_value
+ * RIB = remove all routes at the node
+ * DONE
+ *
+ * For each item at rib node:
+ * if not present in pending node, move RIB item to "delete list"
+ *
+ * For each item at pending rib node:
+ * if present (same vn/un) in rib node with same lifetime & options, drop
+ * matching item from pending node
+ *
+ * For each remaining item at pending rib node, add or replace item
+ * at rib node.
+ *
+ * Construct NHL as concatenation of pending list + delete list
+ *
+ * Clear pending node
+ */
+static void process_pending_node(struct bgp *bgp, struct rfapi_descriptor *rfd,
+ afi_t afi,
+ struct agg_node *pn, /* pending node */
+ struct rfapi_next_hop_entry **head,
+ struct rfapi_next_hop_entry **tail)
+{
+ struct listnode *node = NULL;
+ struct listnode *nnode = NULL;
+ struct rfapi_info *ri = NULL; /* happy valgrind */
+ struct rfapi_ip_prefix hp = {0}; /* pfx to put in NHE */
+ struct agg_node *rn = NULL;
+ struct skiplist *slRibPt = NULL; /* rib list */
+ struct skiplist *slPendPt = NULL;
+ struct list *lPendCost = NULL;
+ struct list *delete_list = NULL;
+ int printedprefix = 0;
+ int rib_node_started_nonempty = 0;
+ int sendingsomeroutes = 0;
+ const struct prefix *p;
+#if DEBUG_PROCESS_PENDING_NODE
+ unsigned int count_rib_initial = 0;
+ unsigned int count_pend_vn_initial = 0;
+ unsigned int count_pend_cost_initial = 0;
+#endif
+
+ assert(pn);
+ p = agg_node_get_prefix(pn);
+ vnc_zlog_debug_verbose("%s: afi=%d, %pRN pn->info=%p", __func__, afi,
+ pn, pn->info);
+
+ if (AFI_L2VPN != afi) {
+ rfapiQprefix2Rprefix(p, &hp);
+ }
+
+ RFAPI_RIB_CHECK_COUNTS(1, 0);
+
+ /*
+ * Find corresponding RIB node
+ */
+ rn = agg_node_get(rfd->rib[afi], p); /* locks rn */
+
+ /*
+ * RIB skiplist has key=rfapi_addr={vn,un}, val = rfapi_info,
+ * skiplist.del = NULL
+ */
+ slRibPt = (struct skiplist *)rn->info;
+ if (slRibPt)
+ rib_node_started_nonempty = 1;
+
+ slPendPt = (struct skiplist *)(pn->aggregate);
+ lPendCost = (struct list *)(pn->info);
+
+#if DEBUG_PROCESS_PENDING_NODE
+ /* debugging */
+ if (slRibPt)
+ count_rib_initial = skiplist_count(slRibPt);
+
+ if (slPendPt)
+ count_pend_vn_initial = skiplist_count(slPendPt);
+
+ if (lPendCost && lPendCost != (struct list *)1)
+ count_pend_cost_initial = lPendCost->count;
+#endif
+
+
+ /*
+ * Handle special case: delete all routes at prefix
+ */
+ if (lPendCost == (struct list *)1) {
+ vnc_zlog_debug_verbose("%s: lPendCost=1 => delete all",
+ __func__);
+ if (slRibPt && !skiplist_empty(slRibPt)) {
+ delete_list = list_new();
+ while (0
+ == skiplist_first(slRibPt, NULL, (void **)&ri)) {
+ listnode_add(delete_list, ri);
+ vnc_zlog_debug_verbose(
+ "%s: after listnode_add, delete_list->count=%d",
+ __func__, delete_list->count);
+ rfapiFreeBgpTeaOptionChain(ri->tea_options);
+ ri->tea_options = NULL;
+
+ if (ri->timer) {
+ struct rfapi_rib_tcb *tcb;
+
+ tcb = THREAD_ARG(ri->timer);
+ THREAD_OFF(ri->timer);
+ XFREE(MTYPE_RFAPI_RECENT_DELETE, tcb);
+ }
+
+ vnc_zlog_debug_verbose(
+ "%s: put dl pfx=%pRN vn=%pFX un=%pFX cost=%d life=%d vn_options=%p",
+ __func__, pn, &ri->rk.vn, &ri->un,
+ ri->cost, ri->lifetime, ri->vn_options);
+
+ skiplist_delete_first(slRibPt);
+ }
+
+ assert(skiplist_empty(slRibPt));
+
+ skiplist_free(slRibPt);
+ rn->info = slRibPt = NULL;
+ agg_unlock_node(rn);
+
+ lPendCost = pn->info = NULL;
+ agg_unlock_node(pn);
+
+ goto callback;
+ }
+ if (slRibPt) {
+ skiplist_free(slRibPt);
+ rn->info = NULL;
+ agg_unlock_node(rn);
+ }
+
+ assert(!slPendPt);
+ if (slPendPt) { /* TBD I think we can toss this block */
+ skiplist_free(slPendPt);
+ pn->aggregate = NULL;
+ agg_unlock_node(pn);
+ }
+
+ pn->info = NULL;
+ agg_unlock_node(pn);
+
+ agg_unlock_node(rn); /* agg_node_get() */
+
+ if (rib_node_started_nonempty) {
+ RFAPI_RIB_PREFIX_COUNT_DECR(rfd, bgp->rfapi);
+ }
+
+ RFAPI_RIB_CHECK_COUNTS(1, 0);
+
+ return;
+ }
+
+ vnc_zlog_debug_verbose("%s: lPendCost->count=%d, slRibPt->count=%d",
+ __func__,
+ (lPendCost ? (int)lPendCost->count : -1),
+ (slRibPt ? (int)slRibPt->count : -1));
+
+ /*
+ * Iterate over routes at RIB Node.
+ * If not found at Pending Node, delete from RIB Node and add to
+ * deletelist
+ * If found at Pending Node
+ * If identical rfapi_info, delete from Pending Node
+ */
+ if (slRibPt) {
+ void *cursor = NULL;
+ struct rfapi_info *ori;
+
+ /*
+ * Iterate over RIB List
+ *
+ */
+ while (!skiplist_next(slRibPt, NULL, (void **)&ori, &cursor)) {
+
+ if (skiplist_search(slPendPt, &ori->rk, (void **)&ri)) {
+ /*
+ * Not in Pending list, so it should be deleted
+ */
+ if (!delete_list)
+ delete_list = list_new();
+ listnode_add(delete_list, ori);
+ rfapiFreeBgpTeaOptionChain(ori->tea_options);
+ ori->tea_options = NULL;
+ if (ori->timer) {
+ struct rfapi_rib_tcb *tcb;
+
+ tcb = THREAD_ARG(ori->timer);
+ THREAD_OFF(ori->timer);
+ XFREE(MTYPE_RFAPI_RECENT_DELETE, tcb);
+ }
+
+#if DEBUG_PROCESS_PENDING_NODE
+ /* deleted from slRibPt below, after we're done
+ * iterating */
+ vnc_zlog_debug_verbose(
+ "%s: slRibPt ri %p not matched in pending list, delete",
+ __func__, ori);
+#endif
+
+ } else {
+ /*
+ * Found in pending list. If same lifetime,
+ * cost, options,
+ * then remove from pending list because the
+ * route
+ * hasn't changed.
+ */
+ if (!rfapi_info_cmp(ori, ri)) {
+ skiplist_delete(slPendPt, &ri->rk,
+ NULL);
+ assert(lPendCost);
+ if (lPendCost) {
+ /* linear walk: might need
+ * optimization */
+ listnode_delete(lPendCost,
+ ri); /* XXX
+ doesn't
+ free
+ data!
+ bug? */
+ rfapi_info_free(
+ ri); /* grr... */
+ }
+ }
+#if DEBUG_PROCESS_PENDING_NODE
+ vnc_zlog_debug_verbose(
+ "%s: slRibPt ri %p matched in pending list, %s",
+ __func__, ori,
+ (same ? "same info"
+ : "different info"));
+#endif
+ }
+ }
+ /*
+ * Go back and delete items from RIB
+ */
+ if (delete_list) {
+ for (ALL_LIST_ELEMENTS_RO(delete_list, node, ri)) {
+ vnc_zlog_debug_verbose(
+ "%s: deleting ri %p from slRibPt",
+ __func__, ri);
+ assert(!skiplist_delete(slRibPt, &ri->rk,
+ NULL));
+ }
+ if (skiplist_empty(slRibPt)) {
+ skiplist_free(slRibPt);
+ slRibPt = rn->info = NULL;
+ agg_unlock_node(rn);
+ }
+ }
+ }
+
+ RFAPI_RIB_CHECK_COUNTS(0, (delete_list ? delete_list->count : 0));
+
+ /*
+ * Iterate over routes at Pending Node
+ *
+ * If {vn} found at RIB Node, update RIB Node route contents to match PN
+ * If {vn} NOT found at RIB Node, add copy to RIB Node
+ */
+ if (lPendCost) {
+ for (ALL_LIST_ELEMENTS_RO(lPendCost, node, ri)) {
+
+ struct rfapi_info *ori;
+
+ if (slRibPt
+ && !skiplist_search(slRibPt, &ri->rk,
+ (void **)&ori)) {
+
+ /* found: update contents of existing route in
+ * RIB */
+ ori->un = ri->un;
+ ori->cost = ri->cost;
+ ori->lifetime = ri->lifetime;
+ rfapiFreeBgpTeaOptionChain(ori->tea_options);
+ ori->tea_options =
+ rfapiOptionsDup(ri->tea_options);
+ ori->last_sent_time = monotime(NULL);
+
+ rfapiFreeRfapiVnOptionChain(ori->vn_options);
+ ori->vn_options =
+ rfapiVnOptionsDup(ri->vn_options);
+
+ rfapiFreeRfapiUnOptionChain(ori->un_options);
+ ori->un_options =
+ rfapiUnOptionsDup(ri->un_options);
+
+ vnc_zlog_debug_verbose(
+ "%s: matched lPendCost item %p in slRibPt, rewrote",
+ __func__, ri);
+
+ } else {
+ /* not found: add new route to RIB */
+ ori = rfapi_info_new();
+ ori->rk = ri->rk;
+ ori->un = ri->un;
+ ori->cost = ri->cost;
+ ori->lifetime = ri->lifetime;
+ ori->tea_options =
+ rfapiOptionsDup(ri->tea_options);
+ ori->last_sent_time = monotime(NULL);
+ ori->vn_options =
+ rfapiVnOptionsDup(ri->vn_options);
+ ori->un_options =
+ rfapiUnOptionsDup(ri->un_options);
+
+ if (!slRibPt) {
+ slRibPt = skiplist_new(
+ 0, rfapi_rib_key_cmp, NULL);
+ rn->info = slRibPt;
+ agg_lock_node(rn);
+ }
+ skiplist_insert(slRibPt, &ori->rk, ori);
+
+ vnc_zlog_debug_verbose(
+ "%s: nomatch lPendCost item %p in slRibPt, added (rd=%pRD)",
+ __func__, ri, &ori->rk.rd);
+ }
+
+ /*
+ * poke timer
+ */
+ RFAPI_RIB_CHECK_COUNTS(
+ 0, (delete_list ? delete_list->count : 0));
+ rfapiRibStartTimer(rfd, ori, rn, 0);
+ RFAPI_RIB_CHECK_COUNTS(
+ 0, (delete_list ? delete_list->count : 0));
+ }
+ }
+
+
+callback:
+ /*
+ * Construct NHL as concatenation of pending list + delete list
+ */
+
+
+ RFAPI_RIB_CHECK_COUNTS(0, (delete_list ? delete_list->count : 0));
+
+ if (lPendCost) {
+
+ char buf[BUFSIZ];
+ char buf2[BUFSIZ];
+
+ vnc_zlog_debug_verbose("%s: lPendCost->count now %d", __func__,
+ lPendCost->count);
+ vnc_zlog_debug_verbose("%s: For prefix %pRN (a)", __func__, pn);
+ printedprefix = 1;
+
+ for (ALL_LIST_ELEMENTS(lPendCost, node, nnode, ri)) {
+
+ struct rfapi_next_hop_entry *new;
+ struct agg_node *trn;
+
+ new = XCALLOC(MTYPE_RFAPI_NEXTHOP,
+ sizeof(struct rfapi_next_hop_entry));
+
+ if (ri->rk.aux_prefix.family) {
+ rfapiQprefix2Rprefix(&ri->rk.aux_prefix,
+ &new->prefix);
+ } else {
+ new->prefix = hp;
+ if (AFI_L2VPN == afi) {
+ /* hp is 0; need to set length to match
+ * AF of vn */
+ new->prefix.length =
+ (ri->rk.vn.family == AF_INET)
+ ? 32
+ : 128;
+ }
+ }
+ new->prefix.cost = ri->cost;
+ new->lifetime = ri->lifetime;
+ rfapiQprefix2Raddr(&ri->rk.vn, &new->vn_address);
+ rfapiQprefix2Raddr(&ri->un, &new->un_address);
+ /* free option chain from ri */
+ rfapiFreeBgpTeaOptionChain(ri->tea_options);
+
+ ri->tea_options =
+ NULL; /* option chain was transferred to NHL */
+
+ new->vn_options = ri->vn_options;
+ ri->vn_options =
+ NULL; /* option chain was transferred to NHL */
+
+ new->un_options = ri->un_options;
+ ri->un_options =
+ NULL; /* option chain was transferred to NHL */
+
+ if (*tail)
+ (*tail)->next = new;
+ *tail = new;
+ if (!*head) {
+ *head = new;
+ }
+ sendingsomeroutes = 1;
+
+ ++rfd->stat_count_nh_reachable;
+ ++bgp->rfapi->stat.count_updated_response_updates;
+
+ /*
+ * update this NVE's timestamp for this prefix
+ */
+ trn = agg_node_get(rfd->rsp_times[afi],
+ p); /* locks trn */
+ trn->info = (void *)(uintptr_t)monotime(NULL);
+ if (agg_node_get_lock_count(trn) > 1)
+ agg_unlock_node(trn);
+
+ rfapiRfapiIpAddr2Str(&new->vn_address, buf, BUFSIZ);
+ rfapiRfapiIpAddr2Str(&new->un_address, buf2, BUFSIZ);
+ vnc_zlog_debug_verbose(
+ "%s: add vn=%s un=%s cost=%d life=%d",
+ __func__, buf, buf2, new->prefix.cost,
+ new->lifetime);
+ }
+ }
+
+ RFAPI_RIB_CHECK_COUNTS(0, (delete_list ? delete_list->count : 0));
+
+ if (delete_list) {
+
+ char buf[BUFSIZ];
+ char buf2[BUFSIZ];
+
+ if (!printedprefix) {
+ vnc_zlog_debug_verbose("%s: For prefix %pRN (d)",
+ __func__, pn);
+ }
+ vnc_zlog_debug_verbose("%s: delete_list has %d elements",
+ __func__, delete_list->count);
+
+ RFAPI_RIB_CHECK_COUNTS(0, delete_list->count);
+ if (!CHECK_FLAG(bgp->rfapi_cfg->flags,
+ BGP_VNC_CONFIG_RESPONSE_REMOVAL_DISABLE)) {
+
+ for (ALL_LIST_ELEMENTS(delete_list, node, nnode, ri)) {
+
+ struct rfapi_next_hop_entry *new;
+ struct rfapi_info *ri_del;
+
+ RFAPI_RIB_CHECK_COUNTS(0, delete_list->count);
+ new = XCALLOC(
+ MTYPE_RFAPI_NEXTHOP,
+ sizeof(struct rfapi_next_hop_entry));
+
+ if (ri->rk.aux_prefix.family) {
+ rfapiQprefix2Rprefix(&ri->rk.aux_prefix,
+ &new->prefix);
+ } else {
+ new->prefix = hp;
+ if (AFI_L2VPN == afi) {
+ /* hp is 0; need to set length
+ * to match AF of vn */
+ new->prefix.length =
+ (ri->rk.vn.family
+ == AF_INET)
+ ? 32
+ : 128;
+ }
+ }
+
+ new->prefix.cost = ri->cost;
+ new->lifetime = RFAPI_REMOVE_RESPONSE_LIFETIME;
+ rfapiQprefix2Raddr(&ri->rk.vn,
+ &new->vn_address);
+ rfapiQprefix2Raddr(&ri->un, &new->un_address);
+
+ new->vn_options = ri->vn_options;
+ ri->vn_options = NULL; /* option chain was
+ transferred to NHL */
+
+ new->un_options = ri->un_options;
+ ri->un_options = NULL; /* option chain was
+ transferred to NHL */
+
+ if (*tail)
+ (*tail)->next = new;
+ *tail = new;
+ if (!*head) {
+ *head = new;
+ }
+ ++rfd->stat_count_nh_removal;
+ ++bgp->rfapi->stat
+ .count_updated_response_deletes;
+
+ rfapiRfapiIpAddr2Str(&new->vn_address, buf,
+ BUFSIZ);
+ rfapiRfapiIpAddr2Str(&new->un_address, buf2,
+ BUFSIZ);
+ vnc_zlog_debug_verbose(
+ "%s: DEL vn=%s un=%s cost=%d life=%d",
+ __func__, buf, buf2, new->prefix.cost,
+ new->lifetime);
+
+ RFAPI_RIB_CHECK_COUNTS(0, delete_list->count);
+ /*
+ * Update/add to list of recent deletions at
+ * this prefix
+ */
+ if (!rn->aggregate) {
+ rn->aggregate = skiplist_new(
+ 0, rfapi_rib_key_cmp,
+ (void (*)(void *))
+ rfapi_info_free);
+ agg_lock_node(rn);
+ }
+ RFAPI_RIB_CHECK_COUNTS(0, delete_list->count);
+
+ /* sanity check lifetime */
+ if (ri->lifetime
+ > RFAPI_LIFETIME_INFINITE_WITHDRAW_DELAY)
+ ri->lifetime =
+ RFAPI_LIFETIME_INFINITE_WITHDRAW_DELAY;
+
+ RFAPI_RIB_CHECK_COUNTS(0, delete_list->count);
+ /* cancel normal expire timer */
+ if (ri->timer) {
+ struct rfapi_rib_tcb *tcb;
+
+ tcb = THREAD_ARG(ri->timer);
+ THREAD_OFF(ri->timer);
+ XFREE(MTYPE_RFAPI_RECENT_DELETE, tcb);
+ }
+ RFAPI_RIB_CHECK_COUNTS(0, delete_list->count);
+
+ /*
+ * Look in "recently-deleted" list
+ */
+ if (skiplist_search(
+ (struct skiplist *)(rn->aggregate),
+ &ri->rk, (void **)&ri_del)) {
+
+ int rc;
+
+ RFAPI_RIB_CHECK_COUNTS(
+ 0, delete_list->count);
+ /*
+ * NOT in "recently-deleted" list
+ */
+ list_delete_node(
+ delete_list,
+ node); /* does not free ri */
+ rc = skiplist_insert(
+ (struct skiplist
+ *)(rn->aggregate),
+ &ri->rk, ri);
+ assert(!rc);
+
+ RFAPI_RIB_CHECK_COUNTS(
+ 0, delete_list->count);
+ rfapiRibStartTimer(rfd, ri, rn, 1);
+ RFAPI_RIB_CHECK_COUNTS(
+ 0, delete_list->count);
+ ri->last_sent_time = monotime(NULL);
+#if DEBUG_RIB_SL_RD
+ vnc_zlog_debug_verbose(
+ "%s: move route to recently deleted list, rd=%pRD",
+ __func__, &ri->rk.rd);
+#endif
+
+ } else {
+ /*
+ * IN "recently-deleted" list
+ */
+ RFAPI_RIB_CHECK_COUNTS(
+ 0, delete_list->count);
+ rfapiRibStartTimer(rfd, ri_del, rn, 1);
+ RFAPI_RIB_CHECK_COUNTS(
+ 0, delete_list->count);
+ ri->last_sent_time = monotime(NULL);
+ }
+ }
+ } else {
+ vnc_zlog_debug_verbose(
+ "%s: response removal disabled, omitting removals",
+ __func__);
+ }
+
+ delete_list->del = (void (*)(void *))rfapi_info_free;
+ list_delete(&delete_list);
+ }
+
+ RFAPI_RIB_CHECK_COUNTS(0, 0);
+
+ /*
+ * Reset pending lists. The final agg_unlock_node() will probably
+ * cause the pending node to be released.
+ */
+ if (slPendPt) {
+ skiplist_free(slPendPt);
+ pn->aggregate = NULL;
+ agg_unlock_node(pn);
+ }
+ if (lPendCost) {
+ list_delete(&lPendCost);
+ pn->info = NULL;
+ agg_unlock_node(pn);
+ }
+ RFAPI_RIB_CHECK_COUNTS(0, 0);
+
+ if (rib_node_started_nonempty) {
+ if (!rn->info) {
+ RFAPI_RIB_PREFIX_COUNT_DECR(rfd, bgp->rfapi);
+ }
+ } else {
+ if (rn->info) {
+ RFAPI_RIB_PREFIX_COUNT_INCR(rfd, bgp->rfapi);
+ }
+ }
+
+ if (sendingsomeroutes)
+ rfapiMonitorTimersRestart(rfd, p);
+
+ agg_unlock_node(rn); /* agg_node_get() */
+
+ RFAPI_RIB_CHECK_COUNTS(1, 0);
+}
+
+/*
+ * regardless of targets, construct a single callback by doing
+ * only one traversal of the pending RIB
+ *
+ *
+ * Do callback
+ *
+ */
+static void rib_do_callback_onepass(struct rfapi_descriptor *rfd, afi_t afi)
+{
+ struct bgp *bgp = bgp_get_default();
+ struct rfapi_next_hop_entry *head = NULL;
+ struct rfapi_next_hop_entry *tail = NULL;
+ struct agg_node *rn;
+
+#ifdef DEBUG_L2_EXTRA
+ vnc_zlog_debug_verbose("%s: rfd=%p, afi=%d", __func__, rfd, afi);
+#endif
+
+ if (!rfd->rib_pending[afi])
+ return;
+
+ assert(bgp->rfapi);
+
+ for (rn = agg_route_top(rfd->rib_pending[afi]); rn;
+ rn = agg_route_next(rn)) {
+ process_pending_node(bgp, rfd, afi, rn, &head, &tail);
+ }
+
+ if (head) {
+ rfapi_response_cb_t *f;
+
+#if DEBUG_NHL
+ vnc_zlog_debug_verbose("%s: response callback NHL follows:",
+ __func__);
+ rfapiPrintNhl(NULL, head);
+#endif
+
+ if (rfd->response_cb)
+ f = rfd->response_cb;
+ else
+ f = bgp->rfapi->rfp_methods.response_cb;
+
+ bgp->rfapi->flags |= RFAPI_INCALLBACK;
+ vnc_zlog_debug_verbose("%s: invoking updated response callback",
+ __func__);
+ (*f)(head, rfd->cookie);
+ bgp->rfapi->flags &= ~RFAPI_INCALLBACK;
+ ++bgp->rfapi->response_updated_count;
+ }
+}
+
+static wq_item_status rfapiRibDoQueuedCallback(struct work_queue *wq,
+ void *data)
+{
+ struct rfapi_descriptor *rfd;
+ afi_t afi;
+ uint32_t queued_flag;
+
+ RFAPI_RIB_CHECK_COUNTS(1, 0);
+
+ rfd = ((struct rfapi_updated_responses_queue *)data)->rfd;
+ afi = ((struct rfapi_updated_responses_queue *)data)->afi;
+
+ /* Make sure the HD wasn't closed after the work item was scheduled */
+ if (rfapi_check(rfd))
+ return WQ_SUCCESS;
+
+ rib_do_callback_onepass(rfd, afi);
+
+ queued_flag = RFAPI_QUEUED_FLAG(afi);
+
+ UNSET_FLAG(rfd->flags, queued_flag);
+
+ RFAPI_RIB_CHECK_COUNTS(1, 0);
+
+ return WQ_SUCCESS;
+}
+
+static void rfapiRibQueueItemDelete(struct work_queue *wq, void *data)
+{
+ XFREE(MTYPE_RFAPI_UPDATED_RESPONSE_QUEUE, data);
+}
+
+static void updated_responses_queue_init(struct rfapi_descriptor *rfd)
+{
+ if (rfd->updated_responses_queue)
+ return;
+
+ rfd->updated_responses_queue =
+ work_queue_new(bm->master, "rfapi updated responses");
+ assert(rfd->updated_responses_queue);
+
+ rfd->updated_responses_queue->spec.workfunc = rfapiRibDoQueuedCallback;
+ rfd->updated_responses_queue->spec.del_item_data =
+ rfapiRibQueueItemDelete;
+ rfd->updated_responses_queue->spec.max_retries = 0;
+ rfd->updated_responses_queue->spec.hold = 1;
+}
+
+/*
+ * Called when an import table node is modified. Construct a
+ * new complete nexthop list, sorted by cost (lowest first),
+ * based on the import table node.
+ *
+ * Filter out duplicate nexthops (vn address). There should be
+ * only one UN address per VN address from the point of view of
+ * a given import table, so we can probably ignore UN addresses
+ * while filtering.
+ *
+ * Based on rfapiNhlAddNodeRoutes()
+ */
+void rfapiRibUpdatePendingNode(
+ struct bgp *bgp, struct rfapi_descriptor *rfd,
+ struct rfapi_import_table *it, /* needed for L2 */
+ struct agg_node *it_node, uint32_t lifetime)
+{
+ const struct prefix *prefix;
+ struct bgp_path_info *bpi;
+ struct agg_node *pn;
+ afi_t afi;
+ uint32_t queued_flag;
+ int count = 0;
+
+ vnc_zlog_debug_verbose("%s: entry", __func__);
+
+ if (CHECK_FLAG(bgp->rfapi_cfg->flags, BGP_VNC_CONFIG_CALLBACK_DISABLE))
+ return;
+
+ vnc_zlog_debug_verbose("%s: callbacks are not disabled", __func__);
+
+ RFAPI_RIB_CHECK_COUNTS(1, 0);
+
+ prefix = agg_node_get_prefix(it_node);
+ afi = family2afi(prefix->family);
+ vnc_zlog_debug_verbose("%s: prefix=%pFX", __func__, prefix);
+
+ pn = agg_node_get(rfd->rib_pending[afi], prefix);
+ assert(pn);
+
+ vnc_zlog_debug_verbose("%s: pn->info=%p, pn->aggregate=%p", __func__,
+ pn->info, pn->aggregate);
+
+ if (pn->aggregate) {
+ /*
+ * free references into the rfapi_info structures before
+ * freeing the structures themselves
+ */
+ skiplist_free((struct skiplist *)(pn->aggregate));
+ pn->aggregate = NULL;
+ agg_unlock_node(pn); /* skiplist deleted */
+ }
+
+
+ /*
+ * free the rfapi_info structures
+ */
+ if (pn->info) {
+ if (pn->info != (void *)1) {
+ list_delete((struct list **)(&pn->info));
+ }
+ pn->info = NULL;
+ agg_unlock_node(pn); /* linklist or 1 deleted */
+ }
+
+ /*
+ * The BPIs in the import table are already sorted by cost
+ */
+ for (bpi = it_node->info; bpi; bpi = bpi->next) {
+
+ struct rfapi_info *ri;
+ struct prefix pfx_nh;
+
+ if (!bpi->extra) {
+ /* shouldn't happen */
+ /* TBD increment error stats counter */
+ continue;
+ }
+
+ rfapiNexthop2Prefix(bpi->attr, &pfx_nh);
+
+ /*
+ * Omit route if nexthop is self
+ */
+ if (CHECK_FLAG(bgp->rfapi_cfg->flags,
+ BGP_VNC_CONFIG_FILTER_SELF_FROM_RSP)) {
+
+ struct prefix pfx_vn;
+
+ assert(!rfapiRaddr2Qprefix(&rfd->vn_addr, &pfx_vn));
+ if (prefix_same(&pfx_vn, &pfx_nh))
+ continue;
+ }
+
+ ri = rfapi_info_new();
+ ri->rk.vn = pfx_nh;
+ ri->rk.rd = bpi->extra->vnc.import.rd;
+ /*
+ * If there is an auxiliary IP address (L2 can have it), copy it
+ */
+ if (bpi->extra->vnc.import.aux_prefix.family) {
+ ri->rk.aux_prefix = bpi->extra->vnc.import.aux_prefix;
+ }
+
+ if (rfapiGetUnAddrOfVpnBi(bpi, &ri->un)) {
+ rfapi_info_free(ri);
+ continue;
+ }
+
+ if (!pn->aggregate) {
+ pn->aggregate =
+ skiplist_new(0, rfapi_rib_key_cmp, NULL);
+ agg_lock_node(pn);
+ }
+
+ /*
+ * If we have already added this nexthop, the insert will fail.
+ * Note that the skiplist key is a pointer INTO the rfapi_info
+ * structure which will be added to the "info" list.
+ * The skiplist entry VALUE is not used for anything but
+ * might be useful during debugging.
+ */
+ if (skiplist_insert((struct skiplist *)pn->aggregate, &ri->rk,
+ ri)) {
+
+ /*
+ * duplicate
+ */
+ rfapi_info_free(ri);
+ continue;
+ }
+
+ rfapiRibBi2Ri(bpi, ri, lifetime);
+
+ if (!pn->info) {
+ pn->info = list_new();
+ ((struct list *)(pn->info))->del =
+ (void (*)(void *))rfapi_info_free;
+ agg_lock_node(pn);
+ }
+
+ listnode_add((struct list *)(pn->info), ri);
+ }
+
+ if (pn->info) {
+ count = ((struct list *)(pn->info))->count;
+ }
+
+ if (!count) {
+ assert(!pn->info);
+ assert(!pn->aggregate);
+ pn->info = (void *)1; /* magic value means this node has no
+ routes */
+ agg_lock_node(pn);
+ }
+
+ agg_unlock_node(pn); /* agg_node_get */
+
+ queued_flag = RFAPI_QUEUED_FLAG(afi);
+
+ if (!CHECK_FLAG(rfd->flags, queued_flag)) {
+
+ struct rfapi_updated_responses_queue *urq;
+
+ urq = XCALLOC(MTYPE_RFAPI_UPDATED_RESPONSE_QUEUE,
+ sizeof(struct rfapi_updated_responses_queue));
+ if (!rfd->updated_responses_queue)
+ updated_responses_queue_init(rfd);
+
+ SET_FLAG(rfd->flags, queued_flag);
+ urq->rfd = rfd;
+ urq->afi = afi;
+ work_queue_add(rfd->updated_responses_queue, urq);
+ }
+ RFAPI_RIB_CHECK_COUNTS(1, 0);
+}
+
+void rfapiRibUpdatePendingNodeSubtree(
+ struct bgp *bgp, struct rfapi_descriptor *rfd,
+ struct rfapi_import_table *it, struct agg_node *it_node,
+ struct agg_node *omit_subtree, /* may be NULL */
+ uint32_t lifetime)
+{
+ /* FIXME: need to find a better way here to work without sticking our
+ * hands in node->link */
+ if (agg_node_left(it_node)
+ && (agg_node_left(it_node) != omit_subtree)) {
+ if (agg_node_left(it_node)->info)
+ rfapiRibUpdatePendingNode(
+ bgp, rfd, it, agg_node_left(it_node), lifetime);
+ rfapiRibUpdatePendingNodeSubtree(bgp, rfd, it,
+ agg_node_left(it_node),
+ omit_subtree, lifetime);
+ }
+
+ if (agg_node_right(it_node)
+ && (agg_node_right(it_node) != omit_subtree)) {
+ if (agg_node_right(it_node)->info)
+ rfapiRibUpdatePendingNode(bgp, rfd, it,
+ agg_node_right(it_node),
+ lifetime);
+ rfapiRibUpdatePendingNodeSubtree(bgp, rfd, it,
+ agg_node_right(it_node),
+ omit_subtree, lifetime);
+ }
+}
+
+/*
+ * RETURN VALUE
+ *
+ * 0 allow prefix to be included in response
+ * !0 don't allow prefix to be included in response
+ */
+int rfapiRibFTDFilterRecentPrefix(
+ struct rfapi_descriptor *rfd,
+ struct agg_node *it_rn, /* import table node */
+ struct prefix *pfx_target_original) /* query target */
+{
+ struct bgp *bgp = rfd->bgp;
+ const struct prefix *p = agg_node_get_prefix(it_rn);
+ afi_t afi = family2afi(p->family);
+ time_t prefix_time;
+ struct agg_node *trn;
+
+ /*
+ * Not in FTD mode, so allow prefix
+ */
+ if (bgp->rfapi_cfg->rfp_cfg.download_type != RFAPI_RFP_DOWNLOAD_FULL)
+ return 0;
+
+ /*
+ * TBD
+ * This matches behavior of now-obsolete rfapiRibFTDFilterRecent(),
+ * but we need to decide if that is correct.
+ */
+ if (p->family == AF_ETHERNET)
+ return 0;
+
+#ifdef DEBUG_FTD_FILTER_RECENT
+ {
+ vnc_zlog_debug_verbose("%s: prefix %pFX", __func__,
+ agg_node_get_prefix(it_rn));
+ }
+#endif
+
+ /*
+ * prefix covers target address, so allow prefix
+ */
+ if (prefix_match(p, pfx_target_original)) {
+#ifdef DEBUG_FTD_FILTER_RECENT
+ vnc_zlog_debug_verbose("%s: prefix covers target, allowed",
+ __func__);
+#endif
+ return 0;
+ }
+
+ /*
+ * check this NVE's timestamp for this prefix
+ */
+ trn = agg_node_get(rfd->rsp_times[afi], p); /* locks trn */
+ prefix_time = (time_t)trn->info;
+ if (agg_node_get_lock_count(trn) > 1)
+ agg_unlock_node(trn);
+
+#ifdef DEBUG_FTD_FILTER_RECENT
+ vnc_zlog_debug_verbose("%s: last sent time %lu, last allowed time %lu",
+ __func__, prefix_time,
+ rfd->ftd_last_allowed_time);
+#endif
+
+ /*
+ * haven't sent this prefix, which doesn't cover target address,
+ * to NVE since ftd_advertisement_interval, so OK to send now.
+ */
+ if (prefix_time <= rfd->ftd_last_allowed_time)
+ return 0;
+
+ return 1;
+}
+
+/*
+ * Call when rfapi returns from rfapi_query() so the RIB reflects
+ * the routes sent to the NVE before the first updated response
+ *
+ * Also: remove duplicates from response. Caller should use returned
+ * value of nexthop chain.
+ */
+struct rfapi_next_hop_entry *
+rfapiRibPreload(struct bgp *bgp, struct rfapi_descriptor *rfd,
+ struct rfapi_next_hop_entry *response, int use_eth_resolution)
+{
+ struct rfapi_next_hop_entry *nhp;
+ struct rfapi_next_hop_entry *nhp_next;
+ struct rfapi_next_hop_entry *head = NULL;
+ struct rfapi_next_hop_entry *tail = NULL;
+ time_t new_last_sent_time;
+
+ vnc_zlog_debug_verbose("%s: loading response=%p, use_eth_resolution=%d",
+ __func__, response, use_eth_resolution);
+
+ new_last_sent_time = monotime(NULL);
+
+ for (nhp = response; nhp; nhp = nhp_next) {
+
+ struct prefix pfx;
+ struct rfapi_rib_key rk;
+ afi_t afi;
+ struct rfapi_info *ri;
+ int need_insert;
+ struct agg_node *rn;
+ int rib_node_started_nonempty = 0;
+ struct agg_node *trn;
+ int allowed = 0;
+
+ /* save in case we delete nhp */
+ nhp_next = nhp->next;
+
+ if (nhp->lifetime == RFAPI_REMOVE_RESPONSE_LIFETIME) {
+ /*
+ * weird, shouldn't happen
+ */
+ vnc_zlog_debug_verbose(
+ "%s: got nhp->lifetime == RFAPI_REMOVE_RESPONSE_LIFETIME",
+ __func__);
+ continue;
+ }
+
+
+ if (use_eth_resolution) {
+ /* get the prefix of the ethernet address in the L2
+ * option */
+ struct rfapi_l2address_option *pL2o;
+ struct rfapi_vn_option *vo;
+
+ /*
+ * Look for VN option of type
+ * RFAPI_VN_OPTION_TYPE_L2ADDR
+ */
+ for (pL2o = NULL, vo = nhp->vn_options; vo;
+ vo = vo->next) {
+ if (RFAPI_VN_OPTION_TYPE_L2ADDR == vo->type) {
+ pL2o = &vo->v.l2addr;
+ break;
+ }
+ }
+
+ if (!pL2o) {
+ /*
+ * not supposed to happen
+ */
+ vnc_zlog_debug_verbose("%s: missing L2 info",
+ __func__);
+ continue;
+ }
+
+ afi = AFI_L2VPN;
+ rfapiL2o2Qprefix(pL2o, &pfx);
+ } else {
+ rfapiRprefix2Qprefix(&nhp->prefix, &pfx);
+ afi = family2afi(pfx.family);
+ }
+
+ /*
+ * TBD for ethernet, rib must know the right way to distinguish
+ * duplicate routes
+ *
+ * Current approach: prefix is key to radix tree; then
+ * each prefix has a set of routes with unique VN addrs
+ */
+
+ /*
+ * Look up prefix in RIB
+ */
+ rn = agg_node_get(rfd->rib[afi], &pfx); /* locks rn */
+
+ if (rn->info) {
+ rib_node_started_nonempty = 1;
+ } else {
+ rn->info = skiplist_new(0, rfapi_rib_key_cmp, NULL);
+ agg_lock_node(rn);
+ }
+
+ /*
+ * Look up route at prefix
+ */
+ need_insert = 0;
+ memset((void *)&rk, 0, sizeof(rk));
+ assert(!rfapiRaddr2Qprefix(&nhp->vn_address, &rk.vn));
+
+ if (use_eth_resolution) {
+ /* copy what came from aux_prefix to rk.aux_prefix */
+ rfapiRprefix2Qprefix(&nhp->prefix, &rk.aux_prefix);
+ if (RFAPI_0_PREFIX(&rk.aux_prefix)
+ && RFAPI_HOST_PREFIX(&rk.aux_prefix)) {
+ /* mark as "none" if nhp->prefix is 0/32 or
+ * 0/128 */
+ rk.aux_prefix.family = AF_UNSPEC;
+ }
+ }
+
+#if DEBUG_NHL
+ {
+ char str_aux_prefix[PREFIX_STRLEN];
+
+ str_aux_prefix[0] = 0;
+
+ prefix2str(&rk.aux_prefix, str_aux_prefix,
+ sizeof(str_aux_prefix));
+
+ if (!rk.aux_prefix.family) {
+ }
+ vnc_zlog_debug_verbose(
+ "%s: rk.vn=%pFX rk.aux_prefix=%s", __func__,
+ &rk.vn,
+ (rk.aux_prefix.family ? str_aux_prefix : "-"));
+ }
+ vnc_zlog_debug_verbose(
+ "%s: RIB skiplist for this prefix follows", __func__);
+ rfapiRibShowRibSl(NULL, agg_node_get_prefix(rn),
+ (struct skiplist *)rn->info);
+#endif
+
+
+ if (!skiplist_search((struct skiplist *)rn->info, &rk,
+ (void **)&ri)) {
+ /*
+ * Already have this route; make values match
+ */
+ rfapiFreeRfapiUnOptionChain(ri->un_options);
+ ri->un_options = NULL;
+ rfapiFreeRfapiVnOptionChain(ri->vn_options);
+ ri->vn_options = NULL;
+
+#if DEBUG_NHL
+ vnc_zlog_debug_verbose("%s: found in RIB", __func__);
+#endif
+
+ /*
+ * Filter duplicate routes from initial response.
+ * Check timestamps to avoid wraparound problems
+ */
+ if ((ri->rsp_counter != rfd->rsp_counter)
+ || (ri->last_sent_time != new_last_sent_time)) {
+
+#if DEBUG_NHL
+ vnc_zlog_debug_verbose(
+ "%s: allowed due to counter/timestamp diff",
+ __func__);
+#endif
+ allowed = 1;
+ }
+
+ } else {
+
+#if DEBUG_NHL
+ vnc_zlog_debug_verbose(
+ "%s: allowed due to not yet in RIB", __func__);
+#endif
+ /* not found: add new route to RIB */
+ ri = rfapi_info_new();
+ need_insert = 1;
+ allowed = 1;
+ }
+
+ ri->rk = rk;
+ assert(!rfapiRaddr2Qprefix(&nhp->un_address, &ri->un));
+ ri->cost = nhp->prefix.cost;
+ ri->lifetime = nhp->lifetime;
+ ri->vn_options = rfapiVnOptionsDup(nhp->vn_options);
+ ri->rsp_counter = rfd->rsp_counter;
+ ri->last_sent_time = monotime(NULL);
+
+ if (need_insert) {
+ int rc;
+ rc = skiplist_insert((struct skiplist *)rn->info,
+ &ri->rk, ri);
+ assert(!rc);
+ }
+
+ if (!rib_node_started_nonempty) {
+ RFAPI_RIB_PREFIX_COUNT_INCR(rfd, bgp->rfapi);
+ }
+
+ RFAPI_RIB_CHECK_COUNTS(0, 0);
+ rfapiRibStartTimer(rfd, ri, rn, 0);
+ RFAPI_RIB_CHECK_COUNTS(0, 0);
+
+ agg_unlock_node(rn);
+
+ /*
+ * update this NVE's timestamp for this prefix
+ */
+ trn = agg_node_get(rfd->rsp_times[afi], &pfx); /* locks trn */
+ trn->info = (void *)(uintptr_t)monotime(NULL);
+ if (agg_node_get_lock_count(trn) > 1)
+ agg_unlock_node(trn);
+
+ vnc_zlog_debug_verbose(
+ "%s: added pfx=%pFX nh[vn]=%pFX, cost=%u, lifetime=%u, allowed=%d",
+ __func__, &pfx, &rk.vn, nhp->prefix.cost, nhp->lifetime,
+ allowed);
+
+ if (allowed) {
+ if (tail)
+ (tail)->next = nhp;
+ tail = nhp;
+ if (!head) {
+ head = nhp;
+ }
+ } else {
+ rfapi_un_options_free(nhp->un_options);
+ nhp->un_options = NULL;
+ rfapi_vn_options_free(nhp->vn_options);
+ nhp->vn_options = NULL;
+
+ XFREE(MTYPE_RFAPI_NEXTHOP, nhp);
+ }
+ }
+
+ if (tail)
+ tail->next = NULL;
+ return head;
+}
+
+void rfapiRibPendingDeleteRoute(struct bgp *bgp, struct rfapi_import_table *it,
+ afi_t afi, struct agg_node *it_node)
+{
+ struct rfapi_descriptor *rfd;
+ struct listnode *node;
+ const struct prefix *p = agg_node_get_prefix(it_node);
+
+ vnc_zlog_debug_verbose("%s: entry, it=%p, afi=%d, it_node=%p, pfx=%pRN",
+ __func__, it, afi, it_node, it_node);
+
+ if (AFI_L2VPN == afi) {
+ /*
+ * ethernet import tables are per-LNI and each ethernet monitor
+ * identifies the rfd that owns it.
+ */
+ struct rfapi_monitor_eth *m;
+ struct agg_node *rn;
+ struct skiplist *sl;
+ void *cursor;
+ int rc;
+
+ /*
+ * route-specific monitors
+ */
+ if ((sl = RFAPI_MONITOR_ETH(it_node))) {
+
+ vnc_zlog_debug_verbose(
+ "%s: route-specific skiplist: %p", __func__,
+ sl);
+
+ for (cursor = NULL,
+ rc = skiplist_next(sl, NULL, (void **)&m, &cursor);
+ !rc; rc = skiplist_next(sl, NULL, (void **)&m,
+ &cursor)) {
+
+#if DEBUG_PENDING_DELETE_ROUTE
+ vnc_zlog_debug_verbose("%s: eth monitor rfd=%p",
+ __func__, m->rfd);
+#endif
+ /*
+ * If we have already sent a route with this
+ * prefix to this
+ * NVE, it's OK to send an update with the
+ * delete
+ */
+ if ((rn = agg_node_lookup(m->rfd->rib[afi],
+ p))) {
+ rfapiRibUpdatePendingNode(
+ bgp, m->rfd, it, it_node,
+ m->rfd->response_lifetime);
+ agg_unlock_node(rn);
+ }
+ }
+ }
+
+ /*
+ * all-routes/FTD monitors
+ */
+ for (m = it->eth0_queries; m; m = m->next) {
+#if DEBUG_PENDING_DELETE_ROUTE
+ vnc_zlog_debug_verbose("%s: eth0 monitor rfd=%p",
+ __func__, m->rfd);
+#endif
+ /*
+ * If we have already sent a route with this prefix to
+ * this
+ * NVE, it's OK to send an update with the delete
+ */
+ if ((rn = agg_node_lookup(m->rfd->rib[afi], p))) {
+ rfapiRibUpdatePendingNode(
+ bgp, m->rfd, it, it_node,
+ m->rfd->response_lifetime);
+ agg_unlock_node(rn);
+ }
+ }
+
+ } else {
+ /*
+ * Find RFDs that reference this import table
+ */
+ for (ALL_LIST_ELEMENTS_RO(&bgp->rfapi->descriptors, node,
+ rfd)) {
+
+ struct agg_node *rn;
+
+ vnc_zlog_debug_verbose(
+ "%s: comparing rfd(%p)->import_table=%p to it=%p",
+ __func__, rfd, rfd->import_table, it);
+
+ if (rfd->import_table != it)
+ continue;
+
+ vnc_zlog_debug_verbose("%s: matched rfd %p", __func__,
+ rfd);
+
+ /*
+ * If we have sent a response to this NVE with this
+ * prefix
+ * previously, we should send an updated response.
+ */
+ if ((rn = agg_node_lookup(rfd->rib[afi], p))) {
+ rfapiRibUpdatePendingNode(
+ bgp, rfd, it, it_node,
+ rfd->response_lifetime);
+ agg_unlock_node(rn);
+ }
+ }
+ }
+}
+
+void rfapiRibShowResponsesSummary(void *stream)
+{
+ int (*fp)(void *, const char *, ...);
+ struct vty *vty;
+ void *out;
+ const char *vty_newline;
+ struct bgp *bgp = bgp_get_default();
+
+ int nves = 0;
+ int nves_with_nonempty_ribs = 0;
+ struct rfapi_descriptor *rfd;
+ struct listnode *node;
+
+ if (rfapiStream2Vty(stream, &fp, &vty, &out, &vty_newline) == 0)
+ return;
+ if (!bgp) {
+ fp(out, "Unable to find default BGP instance\n");
+ return;
+ }
+
+ fp(out, "%-24s ", "Responses: (Prefixes)");
+ fp(out, "%-8s %-8u ", "Active:", bgp->rfapi->rib_prefix_count_total);
+ fp(out, "%-8s %-8u",
+ "Maximum:", bgp->rfapi->rib_prefix_count_total_max);
+ fp(out, "\n");
+
+ fp(out, "%-24s ", " (Updated)");
+ fp(out, "%-8s %-8u ",
+ "Update:", bgp->rfapi->stat.count_updated_response_updates);
+ fp(out, "%-8s %-8u",
+ "Remove:", bgp->rfapi->stat.count_updated_response_deletes);
+ fp(out, "%-8s %-8u", "Total:",
+ bgp->rfapi->stat.count_updated_response_updates
+ + bgp->rfapi->stat.count_updated_response_deletes);
+ fp(out, "\n");
+
+ fp(out, "%-24s ", " (NVEs)");
+ for (ALL_LIST_ELEMENTS_RO(&bgp->rfapi->descriptors, node, rfd)) {
+ ++nves;
+ if (rfd->rib_prefix_count)
+ ++nves_with_nonempty_ribs;
+ }
+ fp(out, "%-8s %-8u ", "Active:", nves_with_nonempty_ribs);
+ fp(out, "%-8s %-8u", "Total:", nves);
+ fp(out, "\n");
+}
+
+void rfapiRibShowResponsesSummaryClear(void)
+{
+ struct bgp *bgp = bgp_get_default();
+
+ bgp->rfapi->rib_prefix_count_total_max =
+ bgp->rfapi->rib_prefix_count_total;
+}
+
+static int print_rib_sl(int (*fp)(void *, const char *, ...), struct vty *vty,
+ void *out, struct skiplist *sl, int deleted,
+ char *str_pfx, int *printedprefix)
+{
+ struct rfapi_info *ri;
+ int rc;
+ void *cursor;
+ int routes_displayed = 0;
+
+ cursor = NULL;
+ for (rc = skiplist_next(sl, NULL, (void **)&ri, &cursor); !rc;
+ rc = skiplist_next(sl, NULL, (void **)&ri, &cursor)) {
+
+ char str_vn[PREFIX_STRLEN];
+ char str_un[PREFIX_STRLEN];
+ char str_lifetime[BUFSIZ];
+ char str_age[BUFSIZ];
+ char *p;
+
+ ++routes_displayed;
+
+ prefix2str(&ri->rk.vn, str_vn, sizeof(str_vn));
+ p = index(str_vn, '/');
+ if (p)
+ *p = 0;
+
+ prefix2str(&ri->un, str_un, sizeof(str_un));
+ p = index(str_un, '/');
+ if (p)
+ *p = 0;
+
+ rfapiFormatSeconds(ri->lifetime, str_lifetime, BUFSIZ);
+#ifdef RFAPI_REGISTRATIONS_REPORT_AGE
+ rfapiFormatAge(ri->last_sent_time, str_age, BUFSIZ);
+#else
+ {
+ time_t now = monotime(NULL);
+ time_t expire =
+ ri->last_sent_time + (time_t)ri->lifetime;
+ /* allow for delayed/async removal */
+ rfapiFormatSeconds((expire > now ? expire - now : 1),
+ str_age, BUFSIZ);
+ }
+#endif
+
+ fp(out, " %c %-20s %-15s %-15s %-4u %-8s %-8s %pRD\n",
+ deleted ? 'r' : ' ', *printedprefix ? "" : str_pfx, str_vn,
+ str_un, ri->cost, str_lifetime, str_age, &ri->rk.rd);
+
+ if (!*printedprefix)
+ *printedprefix = 1;
+ }
+ return routes_displayed;
+}
+
+#if DEBUG_NHL
+/*
+ * This one is for debugging (set stream to NULL to send output to log)
+ */
+static void rfapiRibShowRibSl(void *stream, struct prefix *pfx,
+ struct skiplist *sl)
+{
+ int (*fp)(void *, const char *, ...);
+ struct vty *vty;
+ void *out;
+ const char *vty_newline;
+
+ int nhs_displayed = 0;
+ char str_pfx[PREFIX_STRLEN];
+ int printedprefix = 0;
+
+ if (rfapiStream2Vty(stream, &fp, &vty, &out, &vty_newline) == 0)
+ return;
+
+ prefix2str(pfx, str_pfx, sizeof(str_pfx));
+
+ nhs_displayed +=
+ print_rib_sl(fp, vty, out, sl, 0, str_pfx, &printedprefix);
+}
+#endif
+
+void rfapiRibShowResponses(void *stream, struct prefix *pfx_match,
+ int show_removed)
+{
+ int (*fp)(void *, const char *, ...);
+ struct vty *vty;
+ void *out;
+ const char *vty_newline;
+
+ struct rfapi_descriptor *rfd;
+ struct listnode *node;
+
+ struct bgp *bgp = bgp_get_default();
+ int printedheader = 0;
+ int routes_total = 0;
+ int nhs_total = 0;
+ int prefixes_total = 0;
+ int prefixes_displayed = 0;
+ int nves_total = 0;
+ int nves_with_routes = 0;
+ int nves_displayed = 0;
+ int routes_displayed = 0;
+ int nhs_displayed = 0;
+
+ if (rfapiStream2Vty(stream, &fp, &vty, &out, &vty_newline) == 0)
+ return;
+ if (!bgp) {
+ fp(out, "Unable to find default BGP instance\n");
+ return;
+ }
+
+ /*
+ * loop over NVEs
+ */
+ for (ALL_LIST_ELEMENTS_RO(&bgp->rfapi->descriptors, node, rfd)) {
+
+ int printednve = 0;
+ afi_t afi;
+
+ ++nves_total;
+ if (rfd->rib_prefix_count)
+ ++nves_with_routes;
+
+ for (afi = AFI_IP; afi < AFI_MAX; ++afi) {
+
+ struct agg_node *rn;
+
+ if (!rfd->rib[afi])
+ continue;
+
+ for (rn = agg_route_top(rfd->rib[afi]); rn;
+ rn = agg_route_next(rn)) {
+ const struct prefix *p =
+ agg_node_get_prefix(rn);
+ struct skiplist *sl;
+ char str_pfx[PREFIX_STRLEN];
+ int printedprefix = 0;
+
+ if (!show_removed)
+ sl = rn->info;
+ else
+ sl = rn->aggregate;
+
+ if (!sl)
+ continue;
+
+ routes_total++;
+ nhs_total += skiplist_count(sl);
+ ++prefixes_total;
+
+ if (pfx_match && !prefix_match(pfx_match, p)
+ && !prefix_match(p, pfx_match))
+ continue;
+
+ ++prefixes_displayed;
+
+ if (!printedheader) {
+ ++printedheader;
+
+ fp(out, "\n[%s]\n",
+ show_removed ? "Removed" : "Active");
+ fp(out, "%-15s %-15s\n", "Querying VN",
+ "Querying UN");
+ fp(out,
+ " %-20s %-15s %-15s %4s %-8s %-8s\n",
+ "Prefix", "Registered VN",
+ "Registered UN", "Cost", "Lifetime",
+#ifdef RFAPI_REGISTRATIONS_REPORT_AGE
+ "Age"
+#else
+ "Remaining"
+#endif
+ );
+ }
+ if (!printednve) {
+ char str_vn[BUFSIZ];
+ char str_un[BUFSIZ];
+
+ ++printednve;
+ ++nves_displayed;
+
+ fp(out, "%-15s %-15s\n",
+ rfapiRfapiIpAddr2Str(&rfd->vn_addr,
+ str_vn, BUFSIZ),
+ rfapiRfapiIpAddr2Str(&rfd->un_addr,
+ str_un,
+ BUFSIZ));
+ }
+ prefix2str(p, str_pfx, sizeof(str_pfx));
+ // fp(out, " %s\n", buf); /* prefix */
+
+ routes_displayed++;
+ nhs_displayed += print_rib_sl(
+ fp, vty, out, sl, show_removed, str_pfx,
+ &printedprefix);
+ }
+ }
+ }
+
+ if (routes_total) {
+ fp(out, "\n");
+ fp(out, "Displayed %u NVEs, and %u out of %u %s prefixes",
+ nves_displayed, routes_displayed, routes_total,
+ show_removed ? "removed" : "active");
+ if (nhs_displayed != routes_displayed
+ || nhs_total != routes_total)
+ fp(out, " with %u out of %u next hops", nhs_displayed,
+ nhs_total);
+ fp(out, "\n");
+ }
+}
diff --git a/bgpd/rfapi/rfapi_rib.h b/bgpd/rfapi/rfapi_rib.h
new file mode 100644
index 0000000..3ad021b
--- /dev/null
+++ b/bgpd/rfapi/rfapi_rib.h
@@ -0,0 +1,154 @@
+/*
+ *
+ * Copyright 2009-2016, LabN Consulting, L.L.C.
+ *
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program; see the file COPYING; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
+ */
+
+/*
+ * File: rfapi_rib.h
+ * Purpose: per-nve rib
+ */
+
+#ifndef QUAGGA_HGP_RFAPI_RIB_H
+#define QUAGGA_HGP_RFAPI_RIB_H
+
+/*
+ * Key for indexing RIB and Pending RIB skiplists. For L3 RIBs,
+ * the VN address is sufficient because it represents the actual next hop.
+ *
+ * For L2 RIBs, it is possible to have multiple routes to a given L2
+ * prefix via a given VN address, but each route having a unique aux_prefix.
+ */
+struct rfapi_rib_key {
+ struct prefix vn;
+ struct prefix_rd rd;
+
+ /*
+ * for L2 routes: optional IP addr
+ * .family == 0 means "none"
+ */
+ struct prefix aux_prefix;
+};
+#include "rfapi.h"
+
+/*
+ * RFAPI Advertisement Data Block
+ *
+ * Holds NVE prefix advertisement information
+ */
+struct rfapi_adb {
+ union {
+ struct {
+ struct prefix prefix_ip;
+ struct prefix_rd prd;
+ struct prefix prefix_eth;
+ } s; /* mainly for legacy use */
+ struct rfapi_rib_key key;
+ } u;
+ uint32_t lifetime;
+ uint8_t cost;
+ struct rfapi_l2address_option l2o;
+};
+
+struct rfapi_info {
+ struct rfapi_rib_key rk; /* NVE VN addr + aux addr */
+ struct prefix un;
+ uint8_t cost;
+ uint32_t lifetime;
+ time_t last_sent_time;
+ uint32_t rsp_counter; /* dedup initial responses */
+ struct bgp_tea_options *tea_options;
+ struct rfapi_un_option *un_options;
+ struct rfapi_vn_option *vn_options;
+ struct thread *timer;
+};
+
+/*
+ * Work item for updated responses queue
+ */
+struct rfapi_updated_responses_queue {
+ struct rfapi_descriptor *rfd;
+ afi_t afi;
+};
+
+
+extern void rfapiRibClear(struct rfapi_descriptor *rfd);
+
+extern void rfapiRibFree(struct rfapi_descriptor *rfd);
+
+extern void rfapiRibUpdatePendingNode(struct bgp *bgp,
+ struct rfapi_descriptor *rfd,
+ struct rfapi_import_table *it,
+ struct agg_node *it_node,
+ uint32_t lifetime);
+
+extern void rfapiRibUpdatePendingNodeSubtree(struct bgp *bgp,
+ struct rfapi_descriptor *rfd,
+ struct rfapi_import_table *it,
+ struct agg_node *it_node,
+ struct agg_node *omit_subtree,
+ uint32_t lifetime);
+
+extern int rfapiRibPreloadBi(struct agg_node *rfd_rib_node,
+ struct prefix *pfx_vn, struct prefix *pfx_un,
+ uint32_t lifetime, struct bgp_path_info *bpi);
+
+extern struct rfapi_next_hop_entry *
+rfapiRibPreload(struct bgp *bgp, struct rfapi_descriptor *rfd,
+ struct rfapi_next_hop_entry *response, int use_eth_resolution);
+
+extern void rfapiRibPendingDeleteRoute(struct bgp *bgp,
+ struct rfapi_import_table *it, afi_t afi,
+ struct agg_node *it_node);
+
+extern void rfapiRibShowResponsesSummary(void *stream);
+
+extern void rfapiRibShowResponsesSummaryClear(void);
+
+extern void rfapiRibShowResponses(void *stream, struct prefix *pfx_match,
+ int show_removed);
+
+extern int rfapiRibFTDFilterRecentPrefix(
+ struct rfapi_descriptor *rfd,
+ struct agg_node *it_rn, /* import table node */
+ struct prefix *pfx_target_original); /* query target */
+
+extern void rfapiFreeRfapiUnOptionChain(struct rfapi_un_option *p);
+
+extern void rfapiFreeRfapiVnOptionChain(struct rfapi_vn_option *p);
+
+extern void
+rfapiRibCheckCounts(int checkstats, /* validate rfd & global counts */
+ unsigned int offset); /* number of ri's held separately */
+
+/* enable for debugging; disable for performance */
+#if 0
+#define RFAPI_RIB_CHECK_COUNTS(checkstats, offset) rfapiRibCheckCounts(checkstats, offset)
+#else
+#define RFAPI_RIB_CHECK_COUNTS(checkstats, offset)
+#endif
+
+extern void rfapi_rib_key_init(struct prefix *prefix, /* may be NULL */
+ struct prefix_rd *rd, /* may be NULL */
+ struct prefix *aux, /* may be NULL */
+ struct rfapi_rib_key *rk);
+
+extern int rfapi_rib_key_cmp(const void *k1, const void *k2);
+
+extern void rfapiAdbFree(struct rfapi_adb *adb);
+
+#endif /* QUAGGA_HGP_RFAPI_RIB_H */
diff --git a/bgpd/rfapi/rfapi_vty.c b/bgpd/rfapi/rfapi_vty.c
new file mode 100644
index 0000000..23f43fa
--- /dev/null
+++ b/bgpd/rfapi/rfapi_vty.c
@@ -0,0 +1,5033 @@
+/*
+ *
+ * Copyright 2009-2016, LabN Consulting, L.L.C.
+ *
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program; see the file COPYING; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
+ */
+
+#include "lib/zebra.h"
+#include "lib/prefix.h"
+#include "lib/agg_table.h"
+#include "lib/vty.h"
+#include "lib/memory.h"
+#include "lib/routemap.h"
+#include "lib/log.h"
+#include "lib/linklist.h"
+#include "lib/command.h"
+
+#include "bgpd/bgpd.h"
+#include "bgpd/bgp_ecommunity.h"
+#include "bgpd/bgp_attr.h"
+#include "bgpd/bgp_route.h"
+#include "bgpd/bgp_mplsvpn.h"
+
+#include "bgpd/rfapi/bgp_rfapi_cfg.h"
+#include "bgpd/rfapi/rfapi.h"
+#include "bgpd/rfapi/rfapi_backend.h"
+
+#include "bgpd/bgp_route.h"
+#include "bgpd/bgp_aspath.h"
+#include "bgpd/bgp_community.h"
+#include "bgpd/bgp_vnc_types.h"
+#include "bgpd/bgp_label.h"
+
+#include "bgpd/rfapi/rfapi_import.h"
+#include "bgpd/rfapi/rfapi_private.h"
+#include "bgpd/rfapi/rfapi_monitor.h"
+#include "bgpd/rfapi/rfapi_rib.h"
+#include "bgpd/rfapi/rfapi_vty.h"
+#include "bgpd/rfapi/rfapi_ap.h"
+#include "bgpd/rfapi/rfapi_encap_tlv.h"
+#include "bgpd/rfapi/vnc_debug.h"
+
+#define DEBUG_L2_EXTRA 0
+#define DEBUG_SHOW_EXTRA 0
+
+#define VNC_SHOW_STR "VNC information\n"
+
+/* format related utilies */
+
+
+#define FMT_MIN 60 /* seconds */
+#define FMT_HOUR (60 * FMT_MIN)
+#define FMT_DAY (24 * FMT_HOUR)
+#define FMT_YEAR (365 * FMT_DAY)
+
+char *rfapiFormatSeconds(uint32_t seconds, char *buf, size_t len)
+{
+ int year, day, hour, min;
+
+ if (seconds >= FMT_YEAR) {
+ year = seconds / FMT_YEAR;
+ seconds -= year * FMT_YEAR;
+ } else
+ year = 0;
+
+ if (seconds >= FMT_DAY) {
+ day = seconds / FMT_DAY;
+ seconds -= day * FMT_DAY;
+ } else
+ day = 0;
+
+ if (seconds >= FMT_HOUR) {
+ hour = seconds / FMT_HOUR;
+ seconds -= hour * FMT_HOUR;
+ } else
+ hour = 0;
+
+ if (seconds >= FMT_MIN) {
+ min = seconds / FMT_MIN;
+ seconds -= min * FMT_MIN;
+ } else
+ min = 0;
+
+ if (year > 0) {
+ snprintf(buf, len, "%dy%dd%dh", year, day, hour);
+ } else if (day > 0) {
+ snprintf(buf, len, "%dd%dh%dm", day, hour, min);
+ } else {
+ snprintf(buf, len, "%02d:%02d:%02d", hour, min, seconds);
+ }
+
+ return buf;
+}
+
+char *rfapiFormatAge(time_t age, char *buf, size_t len)
+{
+ time_t now, age_adjusted;
+
+ now = monotime(NULL);
+ age_adjusted = now - age;
+
+ return rfapiFormatSeconds(age_adjusted, buf, len);
+}
+
+
+/*
+ * Reimplementation of quagga/lib/prefix.c function, but
+ * for RFAPI-style prefixes
+ */
+void rfapiRprefixApplyMask(struct rfapi_ip_prefix *rprefix)
+{
+ uint8_t *pnt;
+ int index;
+ int offset;
+
+ static const uint8_t maskbit[] = {0x00, 0x80, 0xc0, 0xe0, 0xf0,
+ 0xf8, 0xfc, 0xfe, 0xff};
+
+ switch (rprefix->prefix.addr_family) {
+ case AF_INET:
+ index = rprefix->length / 8;
+ if (index < 4) {
+ pnt = (uint8_t *)&rprefix->prefix.addr.v4;
+ offset = rprefix->length % 8;
+ pnt[index] &= maskbit[offset];
+ index++;
+ while (index < 4)
+ pnt[index++] = 0;
+ }
+ break;
+
+ case AF_INET6:
+ index = rprefix->length / 8;
+ if (index < 16) {
+ pnt = (uint8_t *)&rprefix->prefix.addr.v6;
+ offset = rprefix->length % 8;
+ pnt[index] &= maskbit[offset];
+ index++;
+ while (index < 16)
+ pnt[index++] = 0;
+ }
+ break;
+
+ default:
+ assert(0);
+ }
+}
+
+/*
+ * translate a quagga prefix into a rfapi IP address. The
+ * prefix is REQUIRED to be 32 bits for IPv4 and 128 bits for IPv6
+ *
+ * RETURNS:
+ *
+ * 0 Success
+ * <0 Error
+ */
+int rfapiQprefix2Raddr(struct prefix *qprefix, struct rfapi_ip_addr *raddr)
+{
+ memset(raddr, 0, sizeof(struct rfapi_ip_addr));
+ raddr->addr_family = qprefix->family;
+ switch (qprefix->family) {
+ case AF_INET:
+ if (qprefix->prefixlen != IPV4_MAX_BITLEN)
+ return -1;
+ raddr->addr.v4 = qprefix->u.prefix4;
+ break;
+ case AF_INET6:
+ if (qprefix->prefixlen != IPV6_MAX_BITLEN)
+ return -1;
+ raddr->addr.v6 = qprefix->u.prefix6;
+ break;
+ default:
+ return -1;
+ }
+ return 0;
+}
+
+/*
+ * Translate Quagga prefix to RFAPI prefix
+ */
+/* rprefix->cost set to 0 */
+void rfapiQprefix2Rprefix(const struct prefix *qprefix,
+ struct rfapi_ip_prefix *rprefix)
+{
+ memset(rprefix, 0, sizeof(struct rfapi_ip_prefix));
+ rprefix->length = qprefix->prefixlen;
+ rprefix->prefix.addr_family = qprefix->family;
+ switch (qprefix->family) {
+ case AF_INET:
+ rprefix->prefix.addr.v4 = qprefix->u.prefix4;
+ break;
+ case AF_INET6:
+ rprefix->prefix.addr.v6 = qprefix->u.prefix6;
+ break;
+ default:
+ assert(0);
+ }
+}
+
+int rfapiRprefix2Qprefix(struct rfapi_ip_prefix *rprefix,
+ struct prefix *qprefix)
+{
+ memset(qprefix, 0, sizeof(struct prefix));
+ qprefix->prefixlen = rprefix->length;
+ qprefix->family = rprefix->prefix.addr_family;
+
+ switch (rprefix->prefix.addr_family) {
+ case AF_INET:
+ qprefix->u.prefix4 = rprefix->prefix.addr.v4;
+ break;
+ case AF_INET6:
+ qprefix->u.prefix6 = rprefix->prefix.addr.v6;
+ break;
+ default:
+ return EAFNOSUPPORT;
+ }
+ return 0;
+}
+
+/*
+ * returns 1 if prefixes have same addr family, prefix len, and address
+ * Note that host bits matter in this comparison!
+ *
+ * For paralellism with quagga/lib/prefix.c. if we need a comparison
+ * where host bits are ignored, call that function rfapiRprefixCmp.
+ */
+int rfapiRprefixSame(struct rfapi_ip_prefix *hp1, struct rfapi_ip_prefix *hp2)
+{
+ if (hp1->prefix.addr_family != hp2->prefix.addr_family)
+ return 0;
+ if (hp1->length != hp2->length)
+ return 0;
+ if (hp1->prefix.addr_family == AF_INET)
+ if (IPV4_ADDR_SAME(&hp1->prefix.addr.v4, &hp2->prefix.addr.v4))
+ return 1;
+ if (hp1->prefix.addr_family == AF_INET6)
+ if (IPV6_ADDR_SAME(&hp1->prefix.addr.v6, &hp2->prefix.addr.v6))
+ return 1;
+ return 0;
+}
+
+int rfapiRaddr2Qprefix(struct rfapi_ip_addr *hia, struct prefix *pfx)
+{
+ memset(pfx, 0, sizeof(struct prefix));
+ pfx->family = hia->addr_family;
+
+ switch (hia->addr_family) {
+ case AF_INET:
+ pfx->prefixlen = IPV4_MAX_BITLEN;
+ pfx->u.prefix4 = hia->addr.v4;
+ break;
+ case AF_INET6:
+ pfx->prefixlen = IPV6_MAX_BITLEN;
+ pfx->u.prefix6 = hia->addr.v6;
+ break;
+ default:
+ return EAFNOSUPPORT;
+ }
+ return 0;
+}
+
+void rfapiL2o2Qprefix(struct rfapi_l2address_option *l2o, struct prefix *pfx)
+{
+ memset(pfx, 0, sizeof(struct prefix));
+ pfx->family = AF_ETHERNET;
+ pfx->prefixlen = 48;
+ pfx->u.prefix_eth = l2o->macaddr;
+}
+
+char *rfapiEthAddr2Str(const struct ethaddr *ea, char *buf, int bufsize)
+{
+ return prefix_mac2str(ea, buf, bufsize);
+}
+
+int rfapiStr2EthAddr(const char *str, struct ethaddr *ea)
+{
+ unsigned int a[6];
+ int i;
+
+ if (sscanf(str, "%2x:%2x:%2x:%2x:%2x:%2x", a + 0, a + 1, a + 2, a + 3,
+ a + 4, a + 5)
+ != 6) {
+
+ return EINVAL;
+ }
+
+ for (i = 0; i < 6; ++i)
+ ea->octet[i] = a[i] & 0xff;
+
+ return 0;
+}
+
+const char *rfapi_ntop(int af, const void *src, char *buf, socklen_t size)
+{
+ if (af == AF_ETHERNET) {
+ return rfapiEthAddr2Str((const struct ethaddr *)src, buf, size);
+ }
+
+ return inet_ntop(af, src, buf, size);
+}
+
+int rfapiDebugPrintf(void *dummy, const char *format, ...)
+{
+ va_list args;
+ va_start(args, format);
+ vzlog(LOG_DEBUG, format, args);
+ va_end(args);
+ return 0;
+}
+
+static int rfapiStdioPrintf(void *stream, const char *format, ...)
+{
+ FILE *file = NULL;
+
+ va_list args;
+ va_start(args, format);
+
+ switch ((uintptr_t)stream) {
+ case 1:
+ file = stdout;
+ break;
+ case 2:
+ file = stderr;
+ break;
+ default:
+ assert(0);
+ }
+
+ vfprintf(file, format, args);
+ va_end(args);
+ return 0;
+}
+
+/* Fake out for debug logging */
+static struct vty vty_dummy_zlog;
+static struct vty vty_dummy_stdio;
+#define HVTYNL ((vty == &vty_dummy_zlog)? "": "\n")
+
+static const char *str_vty_newline(struct vty *vty)
+{
+ if (vty == &vty_dummy_zlog)
+ return "";
+ return "\n";
+}
+
+int rfapiStream2Vty(void *stream, /* input */
+ int (**fp)(void *, const char *, ...), /* output */
+ struct vty **vty, /* output */
+ void **outstream, /* output */
+ const char **vty_newline) /* output */
+{
+
+ if (!stream) {
+ vty_dummy_zlog.type = VTY_SHELL; /* for VTYNL */
+ *vty = &vty_dummy_zlog;
+ *fp = (int (*)(void *, const char *, ...))rfapiDebugPrintf;
+ *outstream = NULL;
+ *vty_newline = str_vty_newline(*vty);
+ return 1;
+ }
+
+ if (((uintptr_t)stream == (uintptr_t)1)
+ || ((uintptr_t)stream == (uintptr_t)2)) {
+
+ vty_dummy_stdio.type = VTY_SHELL; /* for VTYNL */
+ *vty = &vty_dummy_stdio;
+ *fp = (int (*)(void *, const char *, ...))rfapiStdioPrintf;
+ *outstream = stream;
+ *vty_newline = str_vty_newline(*vty);
+ return 1;
+ }
+
+ *vty = stream; /* VTYNL requires vty to be legit */
+ *fp = (int (*)(void *, const char *, ...))vty_out;
+ *outstream = stream;
+ *vty_newline = str_vty_newline(*vty);
+ return 1;
+}
+
+/* called from bgpd/bgp_vty.c'route_vty_out() */
+void rfapi_vty_out_vncinfo(struct vty *vty, const struct prefix *p,
+ struct bgp_path_info *bpi, safi_t safi)
+{
+ char *s;
+ uint32_t lifetime;
+
+ /*
+ * Print, on an indented line:
+ * UN address [if VPN route and VNC UN addr subtlv]
+ * EC list
+ * VNC lifetime
+ */
+ vty_out(vty, " ");
+
+ if (safi == SAFI_MPLS_VPN) {
+ struct prefix pfx_un;
+
+ if (!rfapiGetVncTunnelUnAddr(bpi->attr, &pfx_un)) {
+ char buf[BUFSIZ];
+ vty_out(vty, "UN=%s",
+ inet_ntop(pfx_un.family, pfx_un.u.val, buf,
+ BUFSIZ));
+ }
+ }
+
+ if (bgp_attr_get_ecommunity(bpi->attr)) {
+ s = ecommunity_ecom2str(bgp_attr_get_ecommunity(bpi->attr),
+ ECOMMUNITY_FORMAT_ROUTE_MAP, 0);
+ vty_out(vty, " EC{%s}", s);
+ XFREE(MTYPE_ECOMMUNITY_STR, s);
+ }
+
+ if (bpi->extra != NULL) {
+ if (bpi->extra->label[0] == BGP_PREVENT_VRF_2_VRF_LEAK)
+ vty_out(vty, " label=VRF2VRF");
+ else
+ vty_out(vty, " label=%u",
+ decode_label(&bpi->extra->label[0]));
+
+ if (bpi->extra->num_sids) {
+ char buf[BUFSIZ];
+
+ vty_out(vty, " sid=%s",
+ inet_ntop(AF_INET6, &bpi->extra->sid[0].sid,
+ buf, sizeof(buf)));
+
+ if (bpi->extra->sid[0].loc_block_len != 0) {
+ vty_out(vty, " sid_structure=[%d,%d,%d,%d]",
+ bpi->extra->sid[0].loc_block_len,
+ bpi->extra->sid[0].loc_node_len,
+ bpi->extra->sid[0].func_len,
+ bpi->extra->sid[0].arg_len);
+ }
+ }
+ }
+
+ if (!rfapiGetVncLifetime(bpi->attr, &lifetime)) {
+ vty_out(vty, " life=%d", lifetime);
+ }
+
+ vty_out(vty, " type=%s, subtype=%d", zebra_route_string(bpi->type),
+ bpi->sub_type);
+
+ vty_out(vty, "%s", HVTYNL);
+}
+
+void rfapiPrintAttrPtrs(void *stream, struct attr *attr)
+{
+ int (*fp)(void *, const char *, ...);
+ struct vty *vty;
+ void *out;
+ const char *vty_newline;
+ struct transit *transit;
+ struct cluster_list *cluster;
+ char buf[BUFSIZ];
+ struct ecommunity *ecomm;
+ struct community *comm;
+
+ if (rfapiStream2Vty(stream, &fp, &vty, &out, &vty_newline) == 0)
+ return;
+
+ fp(out, "Attr[%p]:%s", attr, HVTYNL);
+ if (!attr)
+ return;
+
+ /* IPv4 Nexthop */
+ inet_ntop(AF_INET, &attr->nexthop, buf, BUFSIZ);
+ fp(out, " nexthop=%s%s", buf, HVTYNL);
+
+ fp(out, " aspath=%p, refcnt=%d%s", attr->aspath,
+ (attr->aspath ? attr->aspath->refcnt : 0), HVTYNL);
+
+ comm = bgp_attr_get_community(attr);
+ fp(out, " community=%p, refcnt=%d%s", comm, (comm ? comm->refcnt : 0),
+ HVTYNL);
+
+ ecomm = bgp_attr_get_ecommunity(attr);
+ fp(out, " ecommunity=%p, refcnt=%d%s", ecomm,
+ (ecomm ? ecomm->refcnt : 0), HVTYNL);
+
+ cluster = bgp_attr_get_cluster(attr);
+ fp(out, " cluster=%p, refcnt=%d%s", cluster,
+ (cluster ? cluster->refcnt : 0), HVTYNL);
+
+ transit = bgp_attr_get_transit(attr);
+ fp(out, " transit=%p, refcnt=%d%s", transit,
+ (transit ? transit->refcnt : 0), HVTYNL);
+}
+
+/*
+ * Print BPI in an Import Table
+ */
+void rfapiPrintBi(void *stream, struct bgp_path_info *bpi)
+{
+ char buf[BUFSIZ];
+ char *s;
+
+ int (*fp)(void *, const char *, ...);
+ struct vty *vty;
+ void *out;
+ const char *vty_newline;
+
+ char line[BUFSIZ];
+ char *p = line;
+ int r;
+ int has_macaddr = 0;
+ struct ethaddr macaddr = {{0}};
+ struct rfapi_l2address_option l2o_buf;
+ uint8_t l2hid = 0; /* valid if has_macaddr */
+
+#define REMAIN (BUFSIZ - (p-line))
+#define INCP {p += (r > REMAIN)? REMAIN: r;}
+
+ if (rfapiStream2Vty(stream, &fp, &vty, &out, &vty_newline) == 0)
+ return;
+
+ if (!bpi)
+ return;
+
+ if (CHECK_FLAG(bpi->flags, BGP_PATH_REMOVED) && bpi->extra
+ && bpi->extra->vnc.import.timer) {
+ struct thread *t =
+ (struct thread *)bpi->extra->vnc.import.timer;
+ r = snprintf(p, REMAIN, " [%4lu] ",
+ thread_timer_remain_second(t));
+ INCP;
+
+ } else {
+ r = snprintf(p, REMAIN, " ");
+ INCP;
+ }
+
+ if (bpi->extra) {
+ /* TBD This valid only for SAFI_MPLS_VPN, but not for encap */
+ if (decode_rd_type(bpi->extra->vnc.import.rd.val)
+ == RD_TYPE_VNC_ETH) {
+ has_macaddr = 1;
+ memcpy(macaddr.octet, bpi->extra->vnc.import.rd.val + 2,
+ 6);
+ l2hid = bpi->extra->vnc.import.rd.val[1];
+ }
+ }
+
+ /*
+ * Print these items:
+ * type/subtype
+ * nexthop address
+ * lifetime
+ * RFP option sizes (they are opaque values)
+ * extended communities (RTs)
+ */
+ uint32_t lifetime;
+ int printed_1st_gol = 0;
+ struct bgp_attr_encap_subtlv *pEncap;
+ struct prefix pfx_un;
+ int af = BGP_MP_NEXTHOP_FAMILY(bpi->attr->mp_nexthop_len);
+
+ /* Nexthop */
+ if (af == AF_INET) {
+ r = snprintf(p, REMAIN, "%s",
+ inet_ntop(AF_INET,
+ &bpi->attr->mp_nexthop_global_in, buf,
+ BUFSIZ));
+ INCP;
+ } else if (af == AF_INET6) {
+ r = snprintf(p, REMAIN, "%s",
+ inet_ntop(AF_INET6, &bpi->attr->mp_nexthop_global,
+ buf, BUFSIZ));
+ INCP;
+ } else {
+ r = snprintf(p, REMAIN, "?");
+ INCP;
+ }
+
+ /*
+ * VNC tunnel subtlv, if present, contains UN address
+ */
+ if (!rfapiGetVncTunnelUnAddr(bpi->attr, &pfx_un)) {
+ r = snprintf(
+ p, REMAIN, " un=%s",
+ inet_ntop(pfx_un.family, pfx_un.u.val, buf, BUFSIZ));
+ INCP;
+ }
+
+ /* Lifetime */
+ if (rfapiGetVncLifetime(bpi->attr, &lifetime)) {
+ r = snprintf(p, REMAIN, " nolife");
+ INCP;
+ } else {
+ if (lifetime == 0xffffffff)
+ r = snprintf(p, REMAIN, " %6s", "infini");
+ else
+ r = snprintf(p, REMAIN, " %6u", lifetime);
+ INCP;
+ }
+
+ /* RFP option lengths */
+ for (pEncap = bgp_attr_get_vnc_subtlvs(bpi->attr); pEncap;
+ pEncap = pEncap->next) {
+
+ if (pEncap->type == BGP_VNC_SUBTLV_TYPE_RFPOPTION) {
+ if (printed_1st_gol) {
+ r = snprintf(p, REMAIN, ",");
+ INCP;
+ } else {
+ r = snprintf(p, REMAIN,
+ " "); /* leading space */
+ INCP;
+ }
+ r = snprintf(p, REMAIN, "%d", pEncap->length);
+ INCP;
+ printed_1st_gol = 1;
+ }
+ }
+
+ /* RT list */
+ if (bgp_attr_get_ecommunity(bpi->attr)) {
+ s = ecommunity_ecom2str(bgp_attr_get_ecommunity(bpi->attr),
+ ECOMMUNITY_FORMAT_ROUTE_MAP, 0);
+ r = snprintf(p, REMAIN, " %s", s);
+ INCP;
+ XFREE(MTYPE_ECOMMUNITY_STR, s);
+ }
+
+ r = snprintf(p, REMAIN, " bpi@%p", bpi);
+ INCP;
+
+ r = snprintf(p, REMAIN, " p@%p", bpi->peer);
+ INCP;
+
+ if (CHECK_FLAG(bpi->flags, BGP_PATH_REMOVED)) {
+ r = snprintf(p, REMAIN, " HD=yes");
+ INCP;
+ } else {
+ r = snprintf(p, REMAIN, " HD=no");
+ INCP;
+ }
+
+ if (bpi->attr->weight) {
+ r = snprintf(p, REMAIN, " W=%d", bpi->attr->weight);
+ INCP;
+ }
+
+ if (bpi->attr->flag & ATTR_FLAG_BIT(BGP_ATTR_LOCAL_PREF)) {
+ r = snprintf(p, REMAIN, " LP=%d", bpi->attr->local_pref);
+ INCP;
+ } else {
+ r = snprintf(p, REMAIN, " LP=unset");
+ INCP;
+ }
+
+ r = snprintf(p, REMAIN, " %c:%u", zebra_route_char(bpi->type),
+ bpi->sub_type);
+ INCP;
+
+ fp(out, "%s%s", line, HVTYNL);
+
+ if (has_macaddr) {
+ fp(out, " RD HID=%d ETH=%02x:%02x:%02x:%02x:%02x:%02x%s",
+ l2hid, macaddr.octet[0], macaddr.octet[1], macaddr.octet[2],
+ macaddr.octet[3], macaddr.octet[4], macaddr.octet[5],
+ HVTYNL);
+ }
+
+ if (!rfapiGetL2o(bpi->attr, &l2o_buf)) {
+ fp(out,
+ " L2O ETH=%02x:%02x:%02x:%02x:%02x:%02x LBL=%d LNI=%d LHI=%hhu%s",
+ l2o_buf.macaddr.octet[0], l2o_buf.macaddr.octet[1],
+ l2o_buf.macaddr.octet[2], l2o_buf.macaddr.octet[3],
+ l2o_buf.macaddr.octet[4], l2o_buf.macaddr.octet[5],
+ l2o_buf.label, l2o_buf.logical_net_id, l2o_buf.local_nve_id,
+ HVTYNL);
+ }
+ if (bpi->extra && bpi->extra->vnc.import.aux_prefix.family) {
+ const char *sp;
+
+ sp = rfapi_ntop(bpi->extra->vnc.import.aux_prefix.family,
+ &bpi->extra->vnc.import.aux_prefix.u.prefix,
+ buf, BUFSIZ);
+ buf[BUFSIZ - 1] = 0;
+ if (sp) {
+ fp(out, " IP: %s%s", sp, HVTYNL);
+ }
+ }
+ {
+ struct rfapi_un_option *uo =
+ rfapi_encap_tlv_to_un_option(bpi->attr);
+ if (uo) {
+ rfapi_print_tunneltype_option(stream, 8, &uo->v.tunnel);
+ rfapi_un_options_free(uo);
+ }
+ }
+}
+
+char *rfapiMonitorVpn2Str(struct rfapi_monitor_vpn *m, char *buf, int size)
+{
+ char buf_pfx[BUFSIZ];
+ char buf_vn[BUFSIZ];
+ char buf_un[BUFSIZ];
+ int rc;
+
+ rfapiRfapiIpAddr2Str(&m->rfd->un_addr, buf_vn, BUFSIZ);
+ rfapiRfapiIpAddr2Str(&m->rfd->vn_addr, buf_un, BUFSIZ);
+
+ rc = snprintf(buf, size,
+ "m=%p, next=%p, rfd=%p(vn=%s un=%s), p=%s/%d, node=%p", m,
+ m->next, m->rfd, buf_vn, buf_un,
+ inet_ntop(m->p.family, &m->p.u.prefix, buf_pfx, BUFSIZ),
+ m->p.prefixlen, m->node);
+ buf[size - 1] = 0;
+ if (rc >= size)
+ return NULL;
+ return buf;
+}
+
+static void rfapiDebugPrintMonitorVpn(void *stream, struct rfapi_monitor_vpn *m)
+{
+ char buf[BUFSIZ];
+
+ int (*fp)(void *, const char *, ...);
+ struct vty *vty;
+ void *out;
+ const char *vty_newline;
+
+ if (rfapiStream2Vty(stream, &fp, &vty, &out, &vty_newline) == 0)
+ return;
+
+ rfapiMonitorVpn2Str(m, buf, BUFSIZ);
+ fp(out, " Mon %s%s", buf, HVTYNL);
+}
+
+static void rfapiDebugPrintMonitorEncap(void *stream,
+ struct rfapi_monitor_encap *m)
+{
+ int (*fp)(void *, const char *, ...);
+ struct vty *vty;
+ void *out = NULL;
+ const char *vty_newline;
+
+ if (rfapiStream2Vty(stream, &fp, &vty, &out, &vty_newline) == 0)
+ return;
+
+ fp(out, " Mon m=%p, next=%p, node=%p, bpi=%p%s", m, m->next, m->node,
+ m->bpi, HVTYNL);
+}
+
+void rfapiShowItNode(void *stream, struct agg_node *rn)
+{
+ struct bgp_path_info *bpi;
+
+ int (*fp)(void *, const char *, ...);
+ struct vty *vty;
+ void *out;
+ const char *vty_newline;
+
+ if (rfapiStream2Vty(stream, &fp, &vty, &out, &vty_newline) == 0)
+ return;
+
+ fp(out, "%pRN @%p #%d%s", rn, rn, agg_node_get_lock_count(rn), HVTYNL);
+
+ for (bpi = rn->info; bpi; bpi = bpi->next) {
+ rfapiPrintBi(stream, bpi);
+ }
+
+ /* doesn't show montors */
+}
+
+void rfapiShowImportTable(void *stream, const char *label, struct agg_table *rt,
+ int isvpn)
+{
+ struct agg_node *rn;
+ char buf[BUFSIZ];
+
+ int (*fp)(void *, const char *, ...);
+ struct vty *vty;
+ void *out;
+ const char *vty_newline;
+
+ if (rfapiStream2Vty(stream, &fp, &vty, &out, &vty_newline) == 0)
+ return;
+
+ fp(out, "Import Table [%s]%s", label, HVTYNL);
+
+ for (rn = agg_route_top(rt); rn; rn = agg_route_next(rn)) {
+ struct bgp_path_info *bpi;
+ const struct prefix *p = agg_node_get_prefix(rn);
+
+ if (p->family == AF_ETHERNET) {
+ rfapiEthAddr2Str(&p->u.prefix_eth, buf, BUFSIZ);
+ } else {
+ inet_ntop(p->family, &p->u.prefix, buf, BUFSIZ);
+ }
+
+ fp(out, "%s/%d @%p #%d%s", buf, p->prefixlen, rn,
+ agg_node_get_lock_count(rn)
+ - 1, /* account for loop iterator locking */
+ HVTYNL);
+
+ for (bpi = rn->info; bpi; bpi = bpi->next) {
+ rfapiPrintBi(stream, bpi);
+ }
+
+ if (isvpn) {
+ struct rfapi_monitor_vpn *m;
+ for (m = RFAPI_MONITOR_VPN(rn); m; m = m->next) {
+ rfapiDebugPrintMonitorVpn(stream, m);
+ }
+ } else {
+ struct rfapi_monitor_encap *m;
+ for (m = RFAPI_MONITOR_ENCAP(rn); m; m = m->next) {
+ rfapiDebugPrintMonitorEncap(stream, m);
+ }
+ }
+ }
+}
+
+int rfapiShowVncQueries(void *stream, struct prefix *pfx_match)
+{
+ struct bgp *bgp;
+ struct rfapi *h;
+ struct listnode *node;
+ struct rfapi_descriptor *rfd;
+
+ int (*fp)(void *, const char *, ...);
+ struct vty *vty;
+ void *out;
+ const char *vty_newline;
+
+ int printedheader = 0;
+
+ int nves_total = 0;
+ int nves_with_queries = 0;
+ int nves_displayed = 0;
+
+ int queries_total = 0;
+ int queries_displayed = 0;
+
+ if (rfapiStream2Vty(stream, &fp, &vty, &out, &vty_newline) == 0)
+ return CMD_WARNING;
+
+ bgp = bgp_get_default(); /* assume 1 instance for now */
+ if (!bgp) {
+ vty_out(vty, "No BGP instance\n");
+ return CMD_WARNING;
+ }
+
+ h = bgp->rfapi;
+ if (!h) {
+ vty_out(vty, "No RFAPI instance\n");
+ return CMD_WARNING;
+ }
+
+ for (ALL_LIST_ELEMENTS_RO(&h->descriptors, node, rfd)) {
+
+ struct agg_node *rn;
+ int printedquerier = 0;
+
+
+ ++nves_total;
+
+ if (rfd->mon
+ || (rfd->mon_eth && skiplist_count(rfd->mon_eth))) {
+ ++nves_with_queries;
+ } else {
+ continue;
+ }
+
+ /*
+ * IP Queries
+ */
+ if (rfd->mon) {
+ for (rn = agg_route_top(rfd->mon); rn;
+ rn = agg_route_next(rn)) {
+ const struct prefix *p =
+ agg_node_get_prefix(rn);
+ struct rfapi_monitor_vpn *m;
+ char buf_remain[BUFSIZ];
+ char buf_pfx[BUFSIZ];
+
+ if (!rn->info)
+ continue;
+
+ m = rn->info;
+
+ ++queries_total;
+
+ if (pfx_match && !prefix_match(pfx_match, p)
+ && !prefix_match(p, pfx_match))
+ continue;
+
+ ++queries_displayed;
+
+ if (!printedheader) {
+ ++printedheader;
+ fp(out, "\n");
+ fp(out, "%-15s %-15s %-15s %-10s\n",
+ "VN Address", "UN Address", "Target",
+ "Remaining");
+ }
+
+ if (!printedquerier) {
+ char buf_vn[BUFSIZ];
+ char buf_un[BUFSIZ];
+
+ rfapiRfapiIpAddr2Str(&rfd->un_addr,
+ buf_un, BUFSIZ);
+ rfapiRfapiIpAddr2Str(&rfd->vn_addr,
+ buf_vn, BUFSIZ);
+
+ fp(out, "%-15s %-15s", buf_vn, buf_un);
+ printedquerier = 1;
+
+ ++nves_displayed;
+ } else
+ fp(out, "%-15s %-15s", "", "");
+ buf_remain[0] = 0;
+ rfapiFormatSeconds(
+ thread_timer_remain_second(m->timer),
+ buf_remain, BUFSIZ);
+ fp(out, " %-15s %-10s\n",
+ inet_ntop(m->p.family, &m->p.u.prefix,
+ buf_pfx, BUFSIZ),
+ buf_remain);
+ }
+ }
+
+ /*
+ * Ethernet Queries
+ */
+ if (rfd->mon_eth && skiplist_count(rfd->mon_eth)) {
+
+ int rc;
+ void *cursor;
+ struct rfapi_monitor_eth *mon_eth;
+
+ for (cursor = NULL,
+ rc = skiplist_next(rfd->mon_eth, NULL,
+ (void **)&mon_eth, &cursor);
+ rc == 0;
+ rc = skiplist_next(rfd->mon_eth, NULL,
+ (void **)&mon_eth, &cursor)) {
+
+ char buf_remain[BUFSIZ];
+ char buf_pfx[BUFSIZ];
+ struct prefix pfx_mac;
+
+ ++queries_total;
+
+ vnc_zlog_debug_verbose(
+ "%s: checking rfd=%p mon_eth=%p",
+ __func__, rfd, mon_eth);
+
+ memset((void *)&pfx_mac, 0,
+ sizeof(struct prefix));
+ pfx_mac.family = AF_ETHERNET;
+ pfx_mac.prefixlen = 48;
+ pfx_mac.u.prefix_eth = mon_eth->macaddr;
+
+ if (pfx_match
+ && !prefix_match(pfx_match, &pfx_mac)
+ && !prefix_match(&pfx_mac, pfx_match))
+ continue;
+
+ ++queries_displayed;
+
+ if (!printedheader) {
+ ++printedheader;
+ fp(out, "\n");
+ fp(out,
+ "%-15s %-15s %-17s %10s %-10s\n",
+ "VN Address", "UN Address", "Target",
+ "LNI", "Remaining");
+ }
+
+ if (!printedquerier) {
+ char buf_vn[BUFSIZ];
+ char buf_un[BUFSIZ];
+
+ rfapiRfapiIpAddr2Str(&rfd->un_addr,
+ buf_un, BUFSIZ);
+ rfapiRfapiIpAddr2Str(&rfd->vn_addr,
+ buf_vn, BUFSIZ);
+
+ fp(out, "%-15s %-15s", buf_vn, buf_un);
+ printedquerier = 1;
+
+ ++nves_displayed;
+ } else
+ fp(out, "%-15s %-15s", "", "");
+ buf_remain[0] = 0;
+ rfapiFormatSeconds(thread_timer_remain_second(
+ mon_eth->timer),
+ buf_remain, BUFSIZ);
+ fp(out, " %-17s %10d %-10s\n",
+ rfapi_ntop(pfx_mac.family, &pfx_mac.u.prefix,
+ buf_pfx, BUFSIZ),
+ mon_eth->logical_net_id, buf_remain);
+ }
+ }
+ }
+
+ if (queries_total) {
+ fp(out, "\n");
+ fp(out, "Displayed %d out of %d total queries\n",
+ queries_displayed, queries_total);
+ }
+ return CMD_SUCCESS;
+}
+
+static int rfapiPrintRemoteRegBi(struct bgp *bgp, void *stream,
+ struct agg_node *rn, struct bgp_path_info *bpi)
+{
+ int (*fp)(void *, const char *, ...);
+ struct vty *vty;
+ void *out;
+ const char *vty_newline;
+ struct prefix pfx_un;
+ struct prefix pfx_vn;
+ uint8_t cost;
+ uint32_t lifetime;
+ bgp_encap_types tun_type = BGP_ENCAP_TYPE_MPLS;/*Default tunnel type*/
+
+ char buf_pfx[BUFSIZ];
+ char buf_ntop[BUFSIZ];
+ char buf_un[BUFSIZ];
+ char buf_vn[BUFSIZ];
+ char buf_lifetime[BUFSIZ];
+ int nlines = 0;
+ const struct prefix *p = agg_node_get_prefix(rn);
+
+ if (!stream)
+ return 0; /* for debug log, print into buf & call output once */
+
+ if (rfapiStream2Vty(stream, &fp, &vty, &out, &vty_newline) == 0)
+ return 0;
+
+ /*
+ * Prefix
+ */
+ buf_pfx[0] = 0;
+ snprintf(buf_pfx, sizeof(buf_pfx), "%s/%d",
+ rfapi_ntop(p->family, &p->u.prefix, buf_ntop, BUFSIZ),
+ p->prefixlen);
+ buf_pfx[BUFSIZ - 1] = 0;
+ nlines++;
+
+ /*
+ * UN addr
+ */
+ buf_un[0] = 0;
+ if (!rfapiGetUnAddrOfVpnBi(bpi, &pfx_un)) {
+ snprintf(buf_un, sizeof(buf_un), "%s",
+ inet_ntop(pfx_un.family, &pfx_un.u.prefix, buf_ntop,
+ BUFSIZ));
+ }
+
+ bgp_attr_extcom_tunnel_type(bpi->attr, &tun_type);
+ /*
+ * VN addr
+ */
+ buf_vn[0] = 0;
+ rfapiNexthop2Prefix(bpi->attr, &pfx_vn);
+ if (tun_type == BGP_ENCAP_TYPE_MPLS) {
+ /* MPLS carries un in nrli next hop (same as vn for IP tunnels)
+ */
+ snprintf(buf_un, sizeof(buf_un), "%s",
+ inet_ntop(pfx_vn.family, &pfx_vn.u.prefix, buf_ntop,
+ BUFSIZ));
+ if (bpi->extra) {
+ uint32_t l = decode_label(&bpi->extra->label[0]);
+ snprintf(buf_vn, sizeof(buf_vn), "Label: %d", l);
+ } else /* should never happen */
+ {
+ snprintf(buf_vn, sizeof(buf_vn), "Label: N/A");
+ }
+ } else {
+ snprintf(buf_vn, sizeof(buf_vn), "%s",
+ inet_ntop(pfx_vn.family, &pfx_vn.u.prefix, buf_ntop,
+ BUFSIZ));
+ }
+ buf_vn[BUFSIZ - 1] = 0;
+ buf_un[BUFSIZ - 1] = 0;
+
+ /*
+ * Cost is encoded in local_pref as (255-cost)
+ * See rfapi_import.c'rfapiRouteInfo2NextHopEntry() for conversion
+ * back to cost.
+ */
+ uint32_t local_pref;
+
+ if (bpi->attr->flag & ATTR_FLAG_BIT(BGP_ATTR_LOCAL_PREF))
+ local_pref = bpi->attr->local_pref;
+ else
+ local_pref = 0;
+ cost = (local_pref > 255) ? 0 : 255 - local_pref;
+
+ fp(out, "%-20s ", buf_pfx);
+ fp(out, "%-15s ", buf_vn);
+ fp(out, "%-15s ", buf_un);
+ fp(out, "%-4d ", cost);
+
+ /* Lifetime */
+ /* NB rfapiGetVncLifetime sets infinite value when returning !0 */
+ if (rfapiGetVncLifetime(bpi->attr, &lifetime)
+ || (lifetime == RFAPI_INFINITE_LIFETIME)) {
+
+ fp(out, "%-10s ", "infinite");
+ } else {
+ time_t t_lifetime = lifetime;
+ rfapiFormatSeconds(t_lifetime, buf_lifetime, BUFSIZ);
+ fp(out, "%-10s ", buf_lifetime);
+ }
+
+ if (CHECK_FLAG(bpi->flags, BGP_PATH_REMOVED) && bpi->extra
+ && bpi->extra->vnc.import.timer) {
+
+ uint32_t remaining;
+ time_t age;
+ char buf_age[BUFSIZ];
+
+ struct thread *t =
+ (struct thread *)bpi->extra->vnc.import.timer;
+ remaining = thread_timer_remain_second(t);
+
+#ifdef RFAPI_REGISTRATIONS_REPORT_AGE
+ /*
+ * Calculate when the timer started. Doing so here saves
+ * us a timestamp field in "struct bgp_path_info".
+ *
+ * See rfapi_import.c'rfapiBiStartWithdrawTimer() for the
+ * original calculation.
+ */
+ age = rfapiGetHolddownFromLifetime(lifetime, factor)
+ - remaining;
+#else /* report remaining time */
+ age = remaining;
+#endif
+ rfapiFormatSeconds(age, buf_age, BUFSIZ);
+
+ fp(out, "%-10s ", buf_age);
+
+ } else if (RFAPI_LOCAL_BI(bpi)) {
+
+ char buf_age[BUFSIZ];
+
+ if (bpi->extra && bpi->extra->vnc.import.create_time) {
+ rfapiFormatAge(bpi->extra->vnc.import.create_time,
+ buf_age, BUFSIZ);
+ } else {
+ buf_age[0] = '?';
+ buf_age[1] = 0;
+ }
+ fp(out, "%-10s ", buf_age);
+ }
+ fp(out, "%s", HVTYNL);
+
+ if (p->family == AF_ETHERNET) {
+ /*
+ * If there is a corresponding IP address && != VN address,
+ * print that on the next line
+ */
+
+ if (bpi->extra && bpi->extra->vnc.import.aux_prefix.family) {
+ const char *sp;
+
+ sp = rfapi_ntop(
+ bpi->extra->vnc.import.aux_prefix.family,
+ &bpi->extra->vnc.import.aux_prefix.u.prefix,
+ buf_ntop, BUFSIZ);
+ buf_ntop[BUFSIZ - 1] = 0;
+
+ if (sp && strcmp(buf_vn, sp) != 0) {
+ fp(out, " IP: %s", sp);
+ if (nlines == 1)
+ nlines++;
+ }
+ }
+ }
+ if (tun_type != BGP_ENCAP_TYPE_MPLS && bpi->extra) {
+ uint32_t l = decode_label(&bpi->extra->label[0]);
+ if (!MPLS_LABEL_IS_NULL(l)) {
+ fp(out, " Label: %d", l);
+ if (nlines == 1)
+ nlines++;
+ }
+ }
+ if (nlines > 1)
+ fp(out, "%s", HVTYNL);
+
+ return 1;
+}
+
+static int rfapiShowRemoteRegistrationsIt(struct bgp *bgp, void *stream,
+ struct rfapi_import_table *it,
+ struct prefix *prefix_only,
+ int show_expiring, /* either/or */
+ int show_local, int show_remote,
+ int show_imported, /* either/or */
+ uint32_t *pLni) /* AFI_L2VPN only */
+{
+ afi_t afi;
+ int printed_rtlist_hdr = 0;
+
+ int (*fp)(void *, const char *, ...);
+ struct vty *vty;
+ void *out;
+ const char *vty_newline;
+ int total = 0;
+ int printed = 0;
+
+ if (rfapiStream2Vty(stream, &fp, &vty, &out, &vty_newline) == 0)
+ return printed;
+
+ for (afi = AFI_IP; afi < AFI_MAX; ++afi) {
+
+ struct agg_node *rn;
+
+ if (!it->imported_vpn[afi])
+ continue;
+
+ for (rn = agg_route_top(it->imported_vpn[afi]); rn;
+ rn = agg_route_next(rn)) {
+ const struct prefix *p = agg_node_get_prefix(rn);
+ struct bgp_path_info *bpi;
+ int count_only;
+
+ /* allow for wider or more narrow mask from user */
+ if (prefix_only && !prefix_match(prefix_only, p)
+ && !prefix_match(p, prefix_only))
+ count_only = 1;
+ else
+ count_only = 0;
+
+ for (bpi = rn->info; bpi; bpi = bpi->next) {
+
+ if (!show_local && RFAPI_LOCAL_BI(bpi)) {
+
+ /* local route from RFP */
+ continue;
+ }
+
+ if (!show_remote && !RFAPI_LOCAL_BI(bpi)) {
+
+ /* remote route */
+ continue;
+ }
+
+ if (show_expiring
+ && !CHECK_FLAG(bpi->flags,
+ BGP_PATH_REMOVED))
+ continue;
+
+ if (!show_expiring
+ && CHECK_FLAG(bpi->flags, BGP_PATH_REMOVED))
+ continue;
+
+ if (bpi->type == ZEBRA_ROUTE_BGP_DIRECT
+ || bpi->type
+ == ZEBRA_ROUTE_BGP_DIRECT_EXT) {
+ if (!show_imported)
+ continue;
+ } else {
+ if (show_imported)
+ continue;
+ }
+
+ total++;
+ if (count_only == 1)
+ continue;
+ if (!printed_rtlist_hdr) {
+ const char *agetype = "";
+ char *s;
+ const char *type = "";
+ if (show_imported) {
+ type = "Imported";
+ } else {
+ if (show_expiring) {
+ type = "Holddown";
+ } else {
+ if (RFAPI_LOCAL_BI(
+ bpi)) {
+ type = "Local";
+ } else {
+ type = "Remote";
+ }
+ }
+ }
+
+ s = ecommunity_ecom2str(
+ it->rt_import_list,
+ ECOMMUNITY_FORMAT_ROUTE_MAP, 0);
+
+ if (pLni) {
+ fp(out,
+ "%s[%s] L2VPN Network 0x%x (%u) RT={%s}",
+ HVTYNL, type, *pLni,
+ (*pLni & 0xfff), s);
+ } else {
+ fp(out, "%s[%s] Prefix RT={%s}",
+ HVTYNL, type, s);
+ }
+ XFREE(MTYPE_ECOMMUNITY_STR, s);
+
+ if (it->rfg && it->rfg->name) {
+ fp(out, " %s \"%s\"",
+ (it->rfg->type == RFAPI_GROUP_CFG_VRF
+ ? "VRF"
+ : "NVE group"),
+ it->rfg->name);
+ }
+ fp(out, "%s", HVTYNL);
+ if (show_expiring) {
+#ifdef RFAPI_REGISTRATIONS_REPORT_AGE
+ agetype = "Age";
+#else
+ agetype = "Remaining";
+#endif
+ } else if (show_local) {
+ agetype = "Age";
+ }
+
+ printed_rtlist_hdr = 1;
+
+ fp(out,
+ "%-20s %-15s %-15s %4s %-10s %-10s%s",
+ (pLni ? "L2 Address/IP" : "Prefix"),
+ "VN Address", "UN Address", "Cost",
+ "Lifetime", agetype, HVTYNL);
+ }
+ printed += rfapiPrintRemoteRegBi(bgp, stream,
+ rn, bpi);
+ }
+ }
+ }
+
+ if (printed > 0) {
+
+ const char *type = "prefixes";
+
+ if (show_imported) {
+ type = "imported prefixes";
+ } else {
+ if (show_expiring) {
+ type = "prefixes in holddown";
+ } else {
+ if (show_local && !show_remote) {
+ type = "locally registered prefixes";
+ } else if (!show_local && show_remote) {
+ type = "remotely registered prefixes";
+ }
+ }
+ }
+
+ fp(out, "Displayed %d out of %d %s%s", printed, total, type,
+ HVTYNL);
+#if DEBUG_SHOW_EXTRA
+ fp(out, "IT table above: it=%p%s", it, HVTYNL);
+#endif
+ }
+ return printed;
+}
+
+
+/*
+ * rfapiShowRemoteRegistrations
+ *
+ * Similar to rfapiShowImportTable() above. This function
+ * is mean to produce the "remote" portion of the output
+ * of "show vnc registrations".
+ */
+int rfapiShowRemoteRegistrations(void *stream, struct prefix *prefix_only,
+ int show_expiring, int show_local,
+ int show_remote, int show_imported)
+{
+ struct bgp *bgp;
+ struct rfapi *h;
+ struct rfapi_import_table *it;
+ int printed = 0;
+
+ bgp = bgp_get_default();
+ if (!bgp) {
+ return printed;
+ }
+
+ h = bgp->rfapi;
+ if (!h) {
+ return printed;
+ }
+
+ for (it = h->imports; it; it = it->next) {
+ printed += rfapiShowRemoteRegistrationsIt(
+ bgp, stream, it, prefix_only, show_expiring, show_local,
+ show_remote, show_imported, NULL);
+ }
+
+ if (h->import_mac) {
+ void *cursor = NULL;
+ int rc;
+ uintptr_t lni_as_ptr;
+ uint32_t lni;
+ uint32_t *pLni;
+
+ for (rc = skiplist_next(h->import_mac, (void **)&lni_as_ptr,
+ (void **)&it, &cursor);
+ !rc;
+ rc = skiplist_next(h->import_mac, (void **)&lni_as_ptr,
+ (void **)&it, &cursor)) {
+ pLni = NULL;
+ if ((lni_as_ptr & 0xffffffff) == lni_as_ptr) {
+ lni = (uint32_t)(lni_as_ptr & 0xffffffff);
+ pLni = &lni;
+ }
+
+ printed += rfapiShowRemoteRegistrationsIt(
+ bgp, stream, it, prefix_only, show_expiring,
+ show_local, show_remote, show_imported, pLni);
+ }
+ }
+
+ return printed;
+}
+
+/*------------------------------------------
+ * rfapiRfapiIpAddr2Str
+ *
+ * UI helper: generate string from rfapi_ip_addr
+ *
+ * input:
+ * a IP v4/v6 address
+ *
+ * output
+ * buf put string here
+ * bufsize max space to write
+ *
+ * return value:
+ * NULL conversion failed
+ * non-NULL pointer to buf
+ --------------------------------------------*/
+const char *rfapiRfapiIpAddr2Str(struct rfapi_ip_addr *a, char *buf,
+ int bufsize)
+{
+ const char *rc = NULL;
+
+ switch (a->addr_family) {
+ case AF_INET:
+ rc = inet_ntop(a->addr_family, &a->addr.v4, buf, bufsize);
+ break;
+ case AF_INET6:
+ rc = inet_ntop(a->addr_family, &a->addr.v6, buf, bufsize);
+ break;
+ }
+ return rc;
+}
+
+void rfapiPrintRfapiIpAddr(void *stream, struct rfapi_ip_addr *a)
+{
+ char buf[BUFSIZ];
+ const char *rc = NULL;
+
+ int (*fp)(void *, const char *, ...);
+ struct vty *vty;
+ void *out = NULL;
+ const char *vty_newline;
+
+ if (rfapiStream2Vty(stream, &fp, &vty, &out, &vty_newline) == 0)
+ return;
+
+ rc = rfapiRfapiIpAddr2Str(a, buf, BUFSIZ);
+
+ if (rc)
+ fp(out, "%s", buf);
+}
+
+const char *rfapiRfapiIpPrefix2Str(struct rfapi_ip_prefix *p, char *buf,
+ int bufsize)
+{
+ struct rfapi_ip_addr *a = &p->prefix;
+ const char *rc = NULL;
+
+ switch (a->addr_family) {
+ case AF_INET:
+ rc = inet_ntop(a->addr_family, &a->addr.v4, buf, bufsize);
+ break;
+ case AF_INET6:
+ rc = inet_ntop(a->addr_family, &a->addr.v6, buf, bufsize);
+ break;
+ }
+
+ if (rc) {
+ int alen = strlen(buf);
+ int remaining = bufsize - alen - 1;
+ int slen;
+
+ if (remaining > 0) {
+ slen = snprintf(buf + alen, remaining, "/%u",
+ p->length);
+ if (slen < remaining) /* see man page for snprintf(3) */
+ return rc;
+ }
+ }
+
+ return NULL;
+}
+
+void rfapiPrintRfapiIpPrefix(void *stream, struct rfapi_ip_prefix *p)
+{
+ char buf[BUFSIZ];
+ const char *rc;
+
+ int (*fp)(void *, const char *, ...);
+ struct vty *vty;
+ void *out = NULL;
+ const char *vty_newline;
+
+ if (rfapiStream2Vty(stream, &fp, &vty, &out, &vty_newline) == 0)
+ return;
+
+ rc = rfapiRfapiIpPrefix2Str(p, buf, BUFSIZ);
+
+ if (rc)
+ fp(out, "%s:%u", buf, p->cost);
+ else
+ fp(out, "?/?:?");
+}
+
+void rfapiPrintAdvertisedInfo(struct vty *vty, struct rfapi_descriptor *rfd,
+ safi_t safi, struct prefix *p)
+{
+ afi_t afi; /* of the VN address */
+ struct bgp_dest *bd;
+ struct bgp_path_info *bpi;
+ uint8_t type = ZEBRA_ROUTE_BGP;
+ struct bgp *bgp;
+ int printed = 0;
+ struct prefix_rd prd0;
+ struct prefix_rd *prd;
+
+ /*
+ * Find the bgp_path in the RIB corresponding to this
+ * prefix and rfd
+ */
+
+ afi = family2afi(p->family);
+ assert(afi == AFI_IP || afi == AFI_IP6);
+
+ bgp = bgp_get_default(); /* assume 1 instance for now */
+ assert(bgp);
+
+ if (safi == SAFI_ENCAP) {
+ memset(&prd0, 0, sizeof(prd0));
+ prd0.family = AF_UNSPEC;
+ prd0.prefixlen = 64;
+ prd = &prd0;
+ } else {
+ prd = &rfd->rd;
+ }
+ bd = bgp_afi_node_get(bgp->rib[afi][safi], afi, safi, p, prd);
+
+ vty_out(vty, " bd=%p%s", bd, HVTYNL);
+
+ for (bpi = bgp_dest_get_bgp_path_info(bd); bpi; bpi = bpi->next) {
+ if (bpi->peer == rfd->peer && bpi->type == type
+ && bpi->sub_type == BGP_ROUTE_RFP && bpi->extra
+ && bpi->extra->vnc.export.rfapi_handle == (void *)rfd) {
+
+ rfapiPrintBi(vty, bpi);
+ printed = 1;
+ }
+ }
+
+ if (!printed) {
+ vty_out(vty, " --?--%s", HVTYNL);
+ return;
+ }
+}
+
+void rfapiPrintDescriptor(struct vty *vty, struct rfapi_descriptor *rfd)
+{
+ /* pHD un-addr vn-addr pCB cookie rd lifetime */
+ /* RT export list */
+ /* RT import list */
+ /* list of advertised prefixes */
+ /* dump import table */
+
+ char *s;
+ void *cursor;
+ int rc;
+ afi_t afi;
+ struct rfapi_adb *adb;
+
+ vty_out(vty, "%-10p ", rfd);
+ rfapiPrintRfapiIpAddr(vty, &rfd->un_addr);
+ vty_out(vty, " ");
+ rfapiPrintRfapiIpAddr(vty, &rfd->vn_addr);
+ vty_out(vty, " %p %p ", rfd->response_cb, rfd->cookie);
+ vty_out(vty, "%pRD", &rfd->rd);
+ vty_out(vty, " %d", rfd->response_lifetime);
+ vty_out(vty, " %s", (rfd->rfg ? rfd->rfg->name : "<orphaned>"));
+ vty_out(vty, "%s", HVTYNL);
+
+ vty_out(vty, " Peer %p #%d%s", rfd->peer, rfd->peer->lock, HVTYNL);
+
+ /* export RT list */
+ if (rfd->rt_export_list) {
+ s = ecommunity_ecom2str(rfd->rt_export_list,
+ ECOMMUNITY_FORMAT_ROUTE_MAP, 0);
+ vty_out(vty, " Export %s%s", s, HVTYNL);
+ XFREE(MTYPE_ECOMMUNITY_STR, s);
+ } else {
+ vty_out(vty, " Export (nil)%s", HVTYNL);
+ }
+
+ /* import RT list */
+ if (rfd->import_table) {
+ s = ecommunity_ecom2str(rfd->import_table->rt_import_list,
+ ECOMMUNITY_FORMAT_ROUTE_MAP, 0);
+ vty_out(vty, " Import %s%s", s, HVTYNL);
+ XFREE(MTYPE_ECOMMUNITY_STR, s);
+ } else {
+ vty_out(vty, " Import (nil)%s", HVTYNL);
+ }
+
+ for (afi = AFI_IP; afi < AFI_MAX; ++afi) {
+ uint8_t family;
+
+ family = afi2family(afi);
+ if (!family)
+ continue;
+
+ cursor = NULL;
+ for (rc = skiplist_next(rfd->advertised.ipN_by_prefix, NULL,
+ (void **)&adb, &cursor);
+ rc == 0;
+ rc = skiplist_next(rfd->advertised.ipN_by_prefix, NULL,
+ (void **)&adb, &cursor)) {
+
+ /* group like family prefixes together in output */
+ if (family != adb->u.s.prefix_ip.family)
+ continue;
+
+ vty_out(vty, " Adv Pfx: %pFX%s", &adb->u.s.prefix_ip,
+ HVTYNL);
+ rfapiPrintAdvertisedInfo(vty, rfd, SAFI_MPLS_VPN,
+ &adb->u.s.prefix_ip);
+ }
+ }
+ for (rc = skiplist_next(rfd->advertised.ip0_by_ether, NULL,
+ (void **)&adb, &cursor);
+ rc == 0; rc = skiplist_next(rfd->advertised.ip0_by_ether, NULL,
+ (void **)&adb, &cursor)) {
+ vty_out(vty, " Adv Pfx: %pFX%s", &adb->u.s.prefix_eth, HVTYNL);
+
+ /* TBD update the following function to print ethernet info */
+ /* Also need to pass/use rd */
+ rfapiPrintAdvertisedInfo(vty, rfd, SAFI_MPLS_VPN,
+ &adb->u.s.prefix_ip);
+ }
+ vty_out(vty, "%s", HVTYNL);
+}
+
+/*
+ * test scripts rely on first line for each nve starting in 1st column,
+ * leading whitespace for additional detail of that nve
+ */
+void rfapiPrintMatchingDescriptors(struct vty *vty, struct prefix *vn_prefix,
+ struct prefix *un_prefix)
+{
+ struct bgp *bgp;
+ struct rfapi *h;
+ struct listnode *ln;
+ struct rfapi_descriptor *rfd;
+ int printed = 0;
+
+ bgp = bgp_get_default(); /* assume 1 instance for now */
+ if (!bgp)
+ return;
+
+ h = bgp->rfapi;
+ assert(h);
+
+ for (ln = listhead(&h->descriptors); ln; ln = listnextnode(ln)) {
+ rfd = listgetdata(ln);
+
+ struct prefix pfx;
+
+ if (vn_prefix) {
+ assert(!rfapiRaddr2Qprefix(&rfd->vn_addr, &pfx));
+ if (!prefix_match(vn_prefix, &pfx))
+ continue;
+ }
+
+ if (un_prefix) {
+ assert(!rfapiRaddr2Qprefix(&rfd->un_addr, &pfx));
+ if (!prefix_match(un_prefix, &pfx))
+ continue;
+ }
+
+ if (!printed) {
+ /* print column header */
+ vty_out(vty, "%s %s %s %s %s %s %s %s%s", "descriptor",
+ "un-addr", "vn-addr", "callback", "cookie",
+ "RD", "lifetime", "group", HVTYNL);
+ }
+ rfapiPrintDescriptor(vty, rfd);
+ printed = 1;
+ }
+}
+
+
+/*
+ * Parse an address and put into a struct prefix
+ */
+int rfapiCliGetPrefixAddr(struct vty *vty, const char *str, struct prefix *p)
+{
+ if (!str2prefix(str, p)) {
+ vty_out(vty, "Malformed address \"%s\"%s", str ? str : "null",
+ HVTYNL);
+ return CMD_WARNING;
+ }
+ switch (p->family) {
+ case AF_INET:
+ if (p->prefixlen != IPV4_MAX_BITLEN) {
+ vty_out(vty, "Not a host address: \"%s\"%s", str,
+ HVTYNL);
+ return CMD_WARNING;
+ }
+ break;
+ case AF_INET6:
+ if (p->prefixlen != IPV6_MAX_BITLEN) {
+ vty_out(vty, "Not a host address: \"%s\"%s", str,
+ HVTYNL);
+ return CMD_WARNING;
+ }
+ break;
+ default:
+ vty_out(vty, "Invalid address \"%s\"%s", str, HVTYNL);
+ return CMD_WARNING;
+ }
+ return 0;
+}
+
+int rfapiCliGetRfapiIpAddr(struct vty *vty, const char *str,
+ struct rfapi_ip_addr *hai)
+{
+ struct prefix pfx;
+ int rc;
+
+ rc = rfapiCliGetPrefixAddr(vty, str, &pfx);
+ if (rc)
+ return rc;
+
+ hai->addr_family = pfx.family;
+ if (pfx.family == AF_INET)
+ hai->addr.v4 = pfx.u.prefix4;
+ else
+ hai->addr.v6 = pfx.u.prefix6;
+
+ return 0;
+}
+
+/*
+ * Note: this function does not flush vty output, so if it is called
+ * with a stream pointing to a vty, the user will have to type something
+ * before the callback output shows up
+ */
+void rfapiPrintNhl(void *stream, struct rfapi_next_hop_entry *next_hops)
+{
+ struct rfapi_next_hop_entry *nh;
+ int count;
+
+ int (*fp)(void *, const char *, ...);
+ struct vty *vty;
+ void *out;
+ const char *vty_newline;
+
+#define REMAIN (BUFSIZ - (p-line))
+#define INCP {p += (r > REMAIN)? REMAIN: r;}
+
+ if (rfapiStream2Vty(stream, &fp, &vty, &out, &vty_newline) == 0)
+ return;
+
+ for (nh = next_hops, count = 1; nh; nh = nh->next, ++count) {
+
+ char line[BUFSIZ];
+ char *p = line;
+ int r;
+
+ r = snprintf(p, REMAIN, "%3d pfx=", count);
+ INCP;
+
+ if (rfapiRfapiIpPrefix2Str(&nh->prefix, p, REMAIN)) {
+ /* it fit, so count length */
+ r = strlen(p);
+ } else {
+ /* didn't fit */
+ goto truncate;
+ }
+ INCP;
+
+ r = snprintf(p, REMAIN, ", un=");
+ INCP;
+
+ if (rfapiRfapiIpAddr2Str(&nh->un_address, p, REMAIN)) {
+ /* it fit, so count length */
+ r = strlen(p);
+ } else {
+ /* didn't fit */
+ goto truncate;
+ }
+ INCP;
+
+ r = snprintf(p, REMAIN, ", vn=");
+ INCP;
+
+ if (rfapiRfapiIpAddr2Str(&nh->vn_address, p, REMAIN)) {
+ /* it fit, so count length */
+ r = strlen(p);
+ } else {
+ /* didn't fit */
+ goto truncate;
+ }
+ INCP;
+
+ truncate:
+ line[BUFSIZ - 1] = 0;
+ fp(out, "%s%s", line, HVTYNL);
+
+ /*
+ * options
+ */
+ if (nh->vn_options) {
+ struct rfapi_vn_option *vo;
+ char offset[] = " ";
+
+ for (vo = nh->vn_options; vo; vo = vo->next) {
+ char pbuf[100];
+
+ switch (vo->type) {
+ case RFAPI_VN_OPTION_TYPE_L2ADDR:
+ rfapiEthAddr2Str(&vo->v.l2addr.macaddr,
+ pbuf, sizeof(pbuf));
+ fp(out,
+ "%sL2 %s LBL=0x%06x NETID=0x%06x NVEID=%d%s",
+ offset, pbuf,
+ (vo->v.l2addr.label & 0x00ffffff),
+ (vo->v.l2addr.logical_net_id
+ & 0x00ffffff),
+ vo->v.l2addr.local_nve_id, HVTYNL);
+ break;
+
+ case RFAPI_VN_OPTION_TYPE_LOCAL_NEXTHOP:
+ fp(out, "%sLNH %pFX cost=%d%s", offset,
+ &vo->v.local_nexthop.addr,
+ vo->v.local_nexthop.cost, HVTYNL);
+ break;
+
+ default:
+ fp(out,
+ "%svn option type %d (unknown)%s",
+ offset, vo->type, HVTYNL);
+ break;
+ }
+ }
+ }
+ if (nh->un_options) {
+ struct rfapi_un_option *uo;
+ char offset[] = " ";
+
+ for (uo = nh->un_options; uo; uo = uo->next) {
+ switch (uo->type) {
+ case RFAPI_UN_OPTION_TYPE_TUNNELTYPE:
+ rfapi_print_tunneltype_option(
+ stream, 8, &uo->v.tunnel);
+ break;
+ default:
+ fp(out, "%sUN Option type %d%s", offset,
+ uo->type, vty_newline);
+ break;
+ }
+ }
+ }
+ }
+}
+
+/***********************************************************************
+ * STATIC ROUTES
+ ***********************************************************************/
+
+/*
+ * Add another nexthop to the NHL
+ */
+static void rfapiAddDeleteLocalRfpPrefix(struct rfapi_ip_addr *un_addr,
+ struct rfapi_ip_addr *vn_addr,
+ struct rfapi_ip_prefix *rprefix,
+ int is_add,
+ uint32_t lifetime, /* add only */
+ struct rfapi_vn_option *vn_options,
+ struct rfapi_next_hop_entry **head,
+ struct rfapi_next_hop_entry **tail)
+{
+ struct rfapi_next_hop_entry *new;
+
+ /*
+ * construct NHL
+ */
+
+ new = XCALLOC(MTYPE_RFAPI_NEXTHOP, sizeof(struct rfapi_next_hop_entry));
+ new->prefix = *rprefix;
+ new->un_address = *un_addr;
+ new->vn_address = *vn_addr;
+
+ new->vn_options = vn_options;
+ if (is_add) {
+ new->lifetime = lifetime;
+ } else {
+ new->lifetime = RFAPI_REMOVE_RESPONSE_LIFETIME;
+ }
+
+ if (*tail)
+ (*tail)->next = new;
+ *tail = new;
+ if (!*head) {
+ *head = new;
+ }
+}
+
+
+static int
+register_add(struct vty *vty, struct cmd_token *carg_prefix,
+ struct cmd_token *carg_vn, struct cmd_token *carg_un,
+ struct cmd_token *carg_cost, /* optional */
+ struct cmd_token *carg_lifetime, /* optional */
+ struct cmd_token *carg_macaddr, /* optional */
+ struct cmd_token
+ *carg_vni, /* mac present=>mandatory Virtual Network ID */
+ int argc, struct cmd_token **argv)
+{
+ const char *arg_prefix = carg_prefix ? carg_prefix->arg : NULL;
+ const char *arg_vn = carg_vn ? carg_vn->arg : NULL;
+ const char *arg_un = carg_un ? carg_un->arg : NULL;
+ const char *arg_cost = carg_cost ? carg_cost->arg : NULL;
+ const char *arg_lifetime = carg_lifetime ? carg_lifetime->arg : NULL;
+ const char *arg_macaddr = carg_macaddr ? carg_macaddr->arg : NULL;
+ const char *arg_vni = carg_vni ? carg_vni->arg : NULL;
+ struct rfapi_ip_addr vn_address;
+ struct rfapi_ip_addr un_address;
+ struct prefix pfx;
+ struct rfapi_ip_prefix rpfx;
+ uint32_t cost;
+ uint32_t lnh_cost;
+ uint32_t lifetime;
+ rfapi_handle rfd;
+ struct rfapi_vn_option optary[10]; /* XXX must be big enough */
+ struct rfapi_vn_option *opt = NULL;
+ int opt_next = 0;
+
+ int rc = CMD_WARNING_CONFIG_FAILED;
+ char *endptr;
+ struct bgp *bgp;
+ struct rfapi *h;
+ struct rfapi_cfg *rfapi_cfg;
+
+ const char *arg_lnh = NULL;
+ const char *arg_lnh_cost = NULL;
+
+ bgp = bgp_get_default(); /* assume 1 instance for now */
+ if (!bgp) {
+ if (vty)
+ vty_out(vty, "BGP not configured\n");
+ return CMD_WARNING_CONFIG_FAILED;
+ }
+
+ h = bgp->rfapi;
+ rfapi_cfg = bgp->rfapi_cfg;
+ if (!h || !rfapi_cfg) {
+ if (vty)
+ vty_out(vty, "RFAPI not configured\n");
+ return CMD_WARNING_CONFIG_FAILED;
+ }
+
+ for (; argc; --argc, ++argv) {
+ if (strmatch(argv[0]->text, "local-next-hop")) {
+ if (arg_lnh) {
+ vty_out(vty,
+ "local-next-hop specified more than once\n");
+ return CMD_WARNING_CONFIG_FAILED;
+ }
+ if (argc <= 1) {
+ vty_out(vty,
+ "Missing parameter for local-next-hop\n");
+ return CMD_WARNING_CONFIG_FAILED;
+ }
+ ++argv;
+ --argc;
+ arg_lnh = argv[0]->arg;
+ }
+ if (strmatch(argv[0]->text, "local-cost")) {
+ if (arg_lnh_cost) {
+ vty_out(vty,
+ "local-cost specified more than once\n");
+ return CMD_WARNING_CONFIG_FAILED;
+ }
+ if (argc <= 1) {
+ vty_out(vty,
+ "Missing parameter for local-cost\n");
+ return CMD_WARNING_CONFIG_FAILED;
+ }
+ ++argv;
+ --argc;
+ arg_lnh_cost = argv[0]->arg;
+ }
+ }
+
+ if ((rc = rfapiCliGetRfapiIpAddr(vty, arg_vn, &vn_address)))
+ goto fail;
+ if ((rc = rfapiCliGetRfapiIpAddr(vty, arg_un, &un_address)))
+ goto fail;
+
+ /* arg_prefix is optional if mac address is given */
+ if (arg_macaddr && !arg_prefix) {
+ /*
+ * fake up a 0/32 or 0/128 prefix
+ */
+ switch (vn_address.addr_family) {
+ case AF_INET:
+ arg_prefix = "0.0.0.0/32";
+ break;
+ case AF_INET6:
+ arg_prefix = "0::0/128";
+ break;
+ default:
+ vty_out(vty,
+ "Internal error, unknown VN address family\n");
+ return CMD_WARNING_CONFIG_FAILED;
+ }
+ }
+
+ if (!str2prefix(arg_prefix, &pfx)) {
+ vty_out(vty, "Malformed prefix \"%s\"\n", arg_prefix);
+ goto fail;
+ }
+ if (pfx.family != AF_INET && pfx.family != AF_INET6) {
+ vty_out(vty, "prefix \"%s\" has invalid address family\n",
+ arg_prefix);
+ goto fail;
+ }
+
+
+ memset(optary, 0, sizeof(optary));
+
+ if (arg_cost) {
+ endptr = NULL;
+ cost = strtoul(arg_cost, &endptr, 10);
+ if (*endptr != '\0' || cost > 255) {
+ vty_out(vty, "%% Invalid %s value\n", "cost");
+ goto fail;
+ }
+ } else {
+ cost = 255;
+ }
+
+ if (arg_lifetime) {
+ if (!strcmp(arg_lifetime, "infinite")) {
+ lifetime = RFAPI_INFINITE_LIFETIME;
+ } else {
+ endptr = NULL;
+ lifetime = strtoul(arg_lifetime, &endptr, 10);
+ if (*endptr != '\0') {
+ vty_out(vty, "%% Invalid %s value\n",
+ "lifetime");
+ goto fail;
+ }
+ }
+ } else {
+ lifetime = RFAPI_INFINITE_LIFETIME; /* default infinite */
+ }
+
+ if (arg_lnh_cost) {
+ if (!arg_lnh) {
+ vty_out(vty,
+ "%% %s may only be specified with local-next-hop\n",
+ "local-cost");
+ goto fail;
+ }
+ endptr = NULL;
+ lnh_cost = strtoul(arg_lnh_cost, &endptr, 10);
+ if (*endptr != '\0' || lnh_cost > 255) {
+ vty_out(vty, "%% Invalid %s value\n", "local-cost");
+ goto fail;
+ }
+ } else {
+ lnh_cost = 255;
+ }
+
+ if (arg_lnh) {
+ if (!arg_prefix) {
+ vty_out(vty,
+ "%% %s may only be specified with prefix\n",
+ "local-next-hop");
+ goto fail;
+ }
+ if ((rc = rfapiCliGetPrefixAddr(
+ vty, arg_lnh,
+ &optary[opt_next].v.local_nexthop.addr))) {
+
+ goto fail;
+ }
+
+ optary[opt_next].v.local_nexthop.cost = lnh_cost;
+ optary[opt_next].type = RFAPI_VN_OPTION_TYPE_LOCAL_NEXTHOP;
+
+ if (opt_next) {
+ optary[opt_next - 1].next = optary + opt_next;
+ } else {
+ opt = optary;
+ }
+ ++opt_next;
+ }
+
+ if (arg_vni && !arg_macaddr) {
+ vty_out(vty, "%% %s may only be specified with mac address\n",
+ "virtual-network-identifier");
+ goto fail;
+ }
+
+ if (arg_macaddr) {
+ if (!arg_vni) {
+ vty_out(vty,
+ "Missing \"vni\" parameter (mandatory with mac)\n");
+ return CMD_WARNING_CONFIG_FAILED;
+ }
+ optary[opt_next].v.l2addr.logical_net_id =
+ strtoul(arg_vni, NULL, 10);
+
+ if ((rc = rfapiStr2EthAddr(
+ arg_macaddr,
+ &optary[opt_next].v.l2addr.macaddr))) {
+ vty_out(vty, "Invalid %s value\n", "mac address");
+ goto fail;
+ }
+ /* TBD label, NVE ID */
+
+ optary[opt_next].type = RFAPI_VN_OPTION_TYPE_L2ADDR;
+
+ if (opt_next) {
+ optary[opt_next - 1].next = optary + opt_next;
+ } else {
+ opt = optary;
+ }
+ ++opt_next;
+ }
+
+ vnc_zlog_debug_verbose(
+ "%s: vn=%s, un=%s, prefix=%s, cost=%s, lifetime=%s, lnh=%s",
+ __func__, arg_vn, arg_un, arg_prefix,
+ (arg_cost ? arg_cost : "NULL"),
+ (arg_lifetime ? arg_lifetime : "NULL"),
+ (arg_lnh ? arg_lnh : "NULL"));
+
+ rfapiQprefix2Rprefix(&pfx, &rpfx);
+
+ rpfx.cost = cost & 255;
+
+ /* look up rf descriptor, call open if it doesn't exist */
+ rc = rfapi_find_rfd(bgp, &vn_address, &un_address,
+ (struct rfapi_descriptor **)&rfd);
+ if (rc) {
+ if (ENOENT == rc) {
+ struct rfapi_un_option uo;
+
+ /*
+ * flag descriptor as provisionally opened for static
+ * route
+ * registration so that we can fix up the other
+ * parameters
+ * when the real open comes along
+ */
+ memset(&uo, 0, sizeof(uo));
+ uo.type = RFAPI_UN_OPTION_TYPE_PROVISIONAL;
+
+ rc = rfapi_open(rfapi_get_rfp_start_val_by_bgp(bgp),
+ &vn_address, &un_address,
+ &uo, /* flags */
+ NULL, NULL, /* no userdata */
+ &rfd);
+ if (rc) {
+ vty_out(vty,
+ "Can't open session for this NVE: %s\n",
+ rfapi_error_str(rc));
+ rc = CMD_WARNING_CONFIG_FAILED;
+ goto fail;
+ }
+ } else {
+ vty_out(vty, "Can't find session for this NVE: %s\n",
+ rfapi_error_str(rc));
+ goto fail;
+ }
+ }
+
+ rc = rfapi_register(rfd, &rpfx, lifetime, NULL, opt,
+ RFAPI_REGISTER_ADD);
+ if (!rc) {
+ struct rfapi_next_hop_entry *head = NULL;
+ struct rfapi_next_hop_entry *tail = NULL;
+ struct rfapi_vn_option *vn_opt_new;
+
+ vnc_zlog_debug_verbose(
+ "%s: rfapi_register succeeded, returning 0", __func__);
+
+ if (h->rfp_methods.local_cb) {
+ struct rfapi_descriptor *r =
+ (struct rfapi_descriptor *)rfd;
+ vn_opt_new = rfapi_vn_options_dup(opt);
+
+ rfapiAddDeleteLocalRfpPrefix(&r->un_addr, &r->vn_addr,
+ &rpfx, 1, lifetime,
+ vn_opt_new, &head, &tail);
+ if (head) {
+ h->flags |= RFAPI_INCALLBACK;
+ (*h->rfp_methods.local_cb)(head, r->cookie);
+ h->flags &= ~RFAPI_INCALLBACK;
+ }
+ head = tail = NULL;
+ }
+ return 0;
+ }
+
+ vnc_zlog_debug_verbose("%s: rfapi_register failed", __func__);
+ vty_out(vty, "\n");
+ vty_out(vty, "Registration failed.\n");
+ vty_out(vty,
+ "Confirm that either the VN or UN address matches a configured NVE group.\n");
+ return CMD_WARNING_CONFIG_FAILED;
+
+fail:
+ vnc_zlog_debug_verbose("%s: fail, rc=%d", __func__, rc);
+ return rc;
+}
+
+/************************************************************************
+ * Add prefix With LNH_OPTIONS...
+ ************************************************************************/
+DEFUN (add_vnc_prefix_cost_life_lnh,
+ add_vnc_prefix_cost_life_lnh_cmd,
+ "add vnc prefix <A.B.C.D/M|X:X::X:X/M> vn <A.B.C.D|X:X::X:X> un <A.B.C.D|X:X::X:X> cost (0-255) lifetime (1-4294967295) LNH_OPTIONS...",
+ "Add registration\n"
+ "VNC Information\n"
+ "Add/modify prefix related information\n"
+ "IPv4 prefix\n"
+ "IPv6 prefix\n"
+ "VN address of NVE\n"
+ "VN IPv4 interface address\n"
+ "VN IPv6 interface address\n"
+ "UN address of NVE\n"
+ "UN IPv4 interface address\n"
+ "UN IPv6 interface address\n"
+ "Administrative cost [default: 255]\n"
+ "Administrative cost\n"
+ "Registration lifetime [default: infinite]\n"
+ "Lifetime value in seconds\n"
+ "[local-next-hop (A.B.C.D|X:X::X:X)] [local-cost <0-255>]\n")
+{
+ /* pfx vn un cost life */
+ return register_add(vty, argv[3], argv[5], argv[7], argv[9], argv[11],
+ /* mac vni */
+ NULL, NULL, argc - 12, argv + 12);
+}
+
+DEFUN (add_vnc_prefix_life_cost_lnh,
+ add_vnc_prefix_life_cost_lnh_cmd,
+ "add vnc prefix <A.B.C.D/M|X:X::X:X/M> vn <A.B.C.D|X:X::X:X> un <A.B.C.D|X:X::X:X> lifetime (1-4294967295) cost (0-255) LNH_OPTIONS...",
+ "Add registration\n"
+ "VNC Information\n"
+ "Add/modify prefix related information\n"
+ "IPv4 prefix\n"
+ "IPv6 prefix\n"
+ "VN address of NVE\n"
+ "VN IPv4 interface address\n"
+ "VN IPv6 interface address\n"
+ "UN address of NVE\n"
+ "UN IPv4 interface address\n"
+ "UN IPv6 interface address\n"
+ "Registration lifetime [default: infinite]\n"
+ "Lifetime value in seconds\n"
+ "Administrative cost [default: 255]\n"
+ "Administrative cost\n"
+ "[local-next-hop (A.B.C.D|X:X::X:X)] [local-cost <0-255>]\n")
+{
+ /* pfx vn un cost life */
+ return register_add(vty, argv[3], argv[5], argv[7], argv[11], argv[9],
+ /* mac vni */
+ NULL, NULL, argc - 12, argv + 12);
+}
+
+DEFUN (add_vnc_prefix_cost_lnh,
+ add_vnc_prefix_cost_lnh_cmd,
+ "add vnc prefix <A.B.C.D/M|X:X::X:X/M> vn <A.B.C.D|X:X::X:X> un <A.B.C.D|X:X::X:X> cost (0-255) LNH_OPTIONS...",
+ "Add registration\n"
+ "VNC Information\n"
+ "Add/modify prefix related information\n"
+ "IPv4 prefix\n"
+ "IPv6 prefix\n"
+ "VN address of NVE\n"
+ "VN IPv4 interface address\n"
+ "VN IPv6 interface address\n"
+ "UN address of NVE\n"
+ "UN IPv4 interface address\n"
+ "UN IPv6 interface address\n"
+ "Administrative cost [default: 255]\n"
+ "Administrative cost\n"
+ "[local-next-hop (A.B.C.D|X:X::X:X)] [local-cost <0-255>]\n")
+{
+ /* pfx vn un cost life */
+ return register_add(vty, argv[3], argv[5], argv[7], argv[9], NULL,
+ /* mac vni */
+ NULL, NULL, argc - 10, argv + 10);
+}
+
+DEFUN (add_vnc_prefix_life_lnh,
+ add_vnc_prefix_life_lnh_cmd,
+ "add vnc prefix <A.B.C.D/M|X:X::X:X/M> vn <A.B.C.D|X:X::X:X> un <A.B.C.D|X:X::X:X> lifetime (1-4294967295) LNH_OPTIONS...",
+ "Add registration\n"
+ "VNC Information\n"
+ "Add/modify prefix related information\n"
+ "IPv4 prefix\n"
+ "IPv6 prefix\n"
+ "VN address of NVE\n"
+ "VN IPv4 interface address\n"
+ "VN IPv6 interface address\n"
+ "UN address of NVE\n"
+ "UN IPv4 interface address\n"
+ "UN IPv6 interface address\n"
+ "Registration lifetime [default: infinite]\n"
+ "Lifetime value in seconds\n"
+ "[local-next-hop (A.B.C.D|X:X::X:X)] [local-cost <0-255>]\n")
+{
+ /* pfx vn un cost life */
+ return register_add(vty, argv[3], argv[5], argv[7], NULL, argv[9],
+ /* mac vni */
+ NULL, NULL, argc - 10, argv + 10);
+}
+
+DEFUN (add_vnc_prefix_lnh,
+ add_vnc_prefix_lnh_cmd,
+ "add vnc prefix <A.B.C.D/M|X:X::X:X/M> vn <A.B.C.D|X:X::X:X> un <A.B.C.D|X:X::X:X> LNH_OPTIONS...",
+ "Add registration\n"
+ "VNC Information\n"
+ "Add/modify prefix related information\n"
+ "IPv4 prefix\n"
+ "IPv6 prefix\n"
+ "VN address of NVE\n"
+ "VN IPv4 interface address\n"
+ "VN IPv6 interface address\n"
+ "UN address of NVE\n"
+ "UN IPv4 interface address\n"
+ "UN IPv6 interface address\n"
+ "[local-next-hop (A.B.C.D|X:X::X:X)] [local-cost <0-255>]\n")
+{
+ /* pfx vn un cost life */
+ return register_add(vty, argv[3], argv[5], argv[7], NULL, NULL,
+ /* mac vni */
+ NULL, NULL, argc - 8, argv + 8);
+}
+
+/************************************************************************
+ * Add prefix Without LNH_OPTIONS...
+ ************************************************************************/
+DEFUN (add_vnc_prefix_cost_life,
+ add_vnc_prefix_cost_life_cmd,
+ "add vnc prefix <A.B.C.D/M|X:X::X:X/M> vn <A.B.C.D|X:X::X:X> un <A.B.C.D|X:X::X:X> cost (0-255) lifetime (1-4294967295)",
+ "Add registration\n"
+ "VNC Information\n"
+ "Add/modify prefix related information\n"
+ "IPv4 prefix\n"
+ "IPv6 prefix\n"
+ "VN address of NVE\n"
+ "VN IPv4 interface address\n"
+ "VN IPv6 interface address\n"
+ "UN address of NVE\n"
+ "UN IPv4 interface address\n"
+ "UN IPv6 interface address\n"
+ "Administrative cost [default: 255]\n"
+ "Administrative cost\n"
+ "Registration lifetime [default: infinite]\n"
+ "Lifetime value in seconds\n")
+{
+ /* pfx vn un cost life */
+ return register_add(vty, argv[3], argv[5], argv[7], argv[9], argv[11],
+ /* mac vni */
+ NULL, NULL, 0, NULL);
+}
+
+DEFUN (add_vnc_prefix_life_cost,
+ add_vnc_prefix_life_cost_cmd,
+ "add vnc prefix <A.B.C.D/M|X:X::X:X/M> vn <A.B.C.D|X:X::X:X> un <A.B.C.D|X:X::X:X> lifetime (1-4294967295) cost (0-255)",
+ "Add registration\n"
+ "VNC Information\n"
+ "Add/modify prefix related information\n"
+ "IPv4 prefix\n"
+ "IPv6 prefix\n"
+ "VN address of NVE\n"
+ "VN IPv4 interface address\n"
+ "VN IPv6 interface address\n"
+ "UN address of NVE\n"
+ "UN IPv4 interface address\n"
+ "UN IPv6 interface address\n"
+ "Registration lifetime [default: infinite]\n"
+ "Lifetime value in seconds\n"
+ "Administrative cost [default: 255]\n"
+ "Administrative cost\n")
+{
+ /* pfx vn un cost life */
+ return register_add(vty, argv[3], argv[5], argv[7], argv[11], argv[9],
+ /* mac vni */
+ NULL, NULL, 0, NULL);
+}
+
+DEFUN (add_vnc_prefix_cost,
+ add_vnc_prefix_cost_cmd,
+ "add vnc prefix <A.B.C.D/M|X:X::X:X/M> vn <A.B.C.D|X:X::X:X> un <A.B.C.D|X:X::X:X> cost (0-255)",
+ "Add registration\n"
+ "VNC Information\n"
+ "Add/modify prefix related information\n"
+ "IPv4 prefix\n"
+ "IPv6 prefix\n"
+ "VN address of NVE\n"
+ "VN IPv4 interface address\n"
+ "VN IPv6 interface address\n"
+ "UN address of NVE\n"
+ "UN IPv4 interface address\n"
+ "UN IPv6 interface address\n"
+ "Administrative cost [default: 255]\n"
+ "Administrative cost\n")
+{
+ /* pfx vn un cost life */
+ return register_add(vty, argv[3], argv[5], argv[7], argv[9], NULL,
+ /* mac vni */
+ NULL, NULL, 0, NULL);
+}
+
+DEFUN (add_vnc_prefix_life,
+ add_vnc_prefix_life_cmd,
+ "add vnc prefix <A.B.C.D/M|X:X::X:X/M> vn <A.B.C.D|X:X::X:X> un <A.B.C.D|X:X::X:X> lifetime (1-4294967295)",
+ "Add registration\n"
+ "VNC Information\n"
+ "Add/modify prefix related information\n"
+ "IPv4 prefix\n"
+ "IPv6 prefix\n"
+ "VN address of NVE\n"
+ "VN IPv4 interface address\n"
+ "VN IPv6 interface address\n"
+ "UN address of NVE\n"
+ "UN IPv4 interface address\n"
+ "UN IPv6 interface address\n"
+ "Registration lifetime [default: infinite]\n"
+ "Lifetime value in seconds\n")
+{
+ /* pfx vn un cost life */
+ return register_add(vty, argv[3], argv[5], argv[7], NULL, argv[9],
+ /* mac vni */
+ NULL, NULL, 0, NULL);
+}
+
+DEFUN (add_vnc_prefix,
+ add_vnc_prefix_cmd,
+ "add vnc prefix <A.B.C.D/M|X:X::X:X/M> vn <A.B.C.D|X:X::X:X> un <A.B.C.D|X:X::X:X>",
+ "Add registration\n"
+ "VNC Information\n"
+ "Add/modify prefix related information\n"
+ "IPv4 prefix\n"
+ "IPv6 prefix\n"
+ "VN address of NVE\n"
+ "VN IPv4 interface address\n"
+ "VN IPv6 interface address\n"
+ "UN address of NVE\n"
+ "UN IPv4 interface address\n"
+ "UN IPv6 interface address\n")
+{
+ /* pfx vn un cost life */
+ return register_add(vty, argv[3], argv[5], argv[7], NULL, NULL,
+ /* mac vni */
+ NULL, NULL, 0, NULL);
+}
+
+/************************************************************************
+ * Mac address registrations
+ ************************************************************************/
+DEFUN (add_vnc_mac_vni_prefix_cost_life,
+ add_vnc_mac_vni_prefix_cost_life_cmd,
+ "add vnc mac X:X:X:X:X:X virtual-network-identifier (1-4294967295) vn <A.B.C.D|X:X::X:X> un <A.B.C.D|X:X::X:X> prefix <A.B.C.D/M|X:X::X:X/M> cost (0-255) lifetime (1-4294967295)",
+ "Add registration\n"
+ "VNC Information\n"
+ "Add/modify mac address information\n"
+ "MAC address\n"
+ "Virtual Network Identifier follows\n"
+ "Virtual Network Identifier\n"
+ "VN address of NVE\n"
+ "VN IPv4 interface address\n"
+ "VN IPv6 interface address\n"
+ "UN address of NVE\n"
+ "UN IPv4 interface address\n"
+ "UN IPv6 interface address\n"
+ "Add/modify prefix related information\n"
+ "IPv4 prefix\n"
+ "IPv6 prefix\n"
+ "Administrative cost [default: 255]\n"
+ "Administrative cost\n"
+ "Registration lifetime [default: infinite]\n"
+ "Lifetime value in seconds\n")
+{
+ /* pfx vn un cost life */
+ return register_add(vty, argv[11], argv[7], argv[9], argv[13], argv[15],
+ /* mac vni */
+ argv[3], argv[5], 0, NULL);
+}
+
+
+DEFUN (add_vnc_mac_vni_prefix_life,
+ add_vnc_mac_vni_prefix_life_cmd,
+ "add vnc mac X:X:X:X:X:X virtual-network-identifier (1-4294967295) vn <A.B.C.D|X:X::X:X> un <A.B.C.D|X:X::X:X> prefix <A.B.C.D/M|X:X::X:X/M> lifetime (1-4294967295)",
+ "Add registration\n"
+ "VNC Information\n"
+ "Add/modify mac address information\n"
+ "MAC address\n"
+ "Virtual Network Identifier follows\n"
+ "Virtual Network Identifier\n"
+ "VN address of NVE\n"
+ "VN IPv4 interface address\n"
+ "VN IPv6 interface address\n"
+ "UN address of NVE\n"
+ "UN IPv4 interface address\n"
+ "UN IPv6 interface address\n"
+ "Add/modify prefix related information\n"
+ "IPv4 prefix\n"
+ "IPv6 prefix\n"
+ "Registration lifetime [default: infinite]\n"
+ "Lifetime value in seconds\n")
+{
+ /* pfx vn un cost life */
+ return register_add(vty, argv[11], argv[7], argv[9], NULL, argv[13],
+ /* mac vni */
+ argv[3], argv[5], 0, NULL);
+}
+
+DEFUN (add_vnc_mac_vni_prefix_cost,
+ add_vnc_mac_vni_prefix_cost_cmd,
+ "add vnc mac X:X:X:X:X:X virtual-network-identifier (1-4294967295) vn <A.B.C.D|X:X::X:X> un <A.B.C.D|X:X::X:X> prefix <A.B.C.D/M|X:X::X:X/M> cost (0-255)",
+ "Add registration\n"
+ "VNC Information\n"
+ "Add/modify mac address information\n"
+ "MAC address\n"
+ "Virtual Network Identifier follows\n"
+ "Virtual Network Identifier\n"
+ "VN address of NVE\n"
+ "VN IPv4 interface address\n"
+ "VN IPv6 interface address\n"
+ "UN address of NVE\n"
+ "UN IPv4 interface address\n"
+ "UN IPv6 interface address\n"
+ "Add/modify prefix related information\n"
+ "IPv4 prefix\n"
+ "IPv6 prefix\n"
+ "Administrative cost [default: 255]\n" "Administrative cost\n")
+{
+ /* pfx vn un cost life */
+ return register_add(vty, argv[11], argv[7], argv[9], argv[13], NULL,
+ /* mac vni */
+ argv[3], argv[5], 0, NULL);
+}
+
+DEFUN (add_vnc_mac_vni_prefix,
+ add_vnc_mac_vni_prefix_cmd,
+ "add vnc mac X:X:X:X:X:X virtual-network-identifier (1-4294967295) vn <A.B.C.D|X:X::X:X> un <A.B.C.D|X:X::X:X> prefix <A.B.C.D/M|X:X::X:X/M>",
+ "Add registration\n"
+ "VNC Information\n"
+ "Add/modify mac address information\n"
+ "MAC address\n"
+ "Virtual Network Identifier follows\n"
+ "Virtual Network Identifier\n"
+ "VN address of NVE\n"
+ "VN IPv4 interface address\n"
+ "VN IPv6 interface address\n"
+ "UN address of NVE\n"
+ "UN IPv4 interface address\n"
+ "UN IPv6 interface address\n"
+ "Add/modify prefix related information\n"
+ "IPv4 prefix\n" "IPv6 prefix\n")
+{
+ /* pfx vn un cost life */
+ return register_add(vty, argv[11], argv[7], argv[9], NULL, NULL,
+ /* mac vni */
+ argv[3], argv[5], 0, NULL);
+}
+
+DEFUN (add_vnc_mac_vni_cost_life,
+ add_vnc_mac_vni_cost_life_cmd,
+ "add vnc mac X:X:X:X:X:X virtual-network-identifier (1-4294967295) vn <A.B.C.D|X:X::X:X> un <A.B.C.D|X:X::X:X> cost (0-255) lifetime (1-4294967295)",
+ "Add registration\n"
+ "VNC Information\n"
+ "Add/modify mac address information\n"
+ "MAC address\n"
+ "Virtual Network Identifier follows\n"
+ "Virtual Network Identifier\n"
+ "VN address of NVE\n"
+ "VN IPv4 interface address\n"
+ "VN IPv6 interface address\n"
+ "UN address of NVE\n"
+ "UN IPv4 interface address\n"
+ "UN IPv6 interface address\n"
+ "Administrative cost [default: 255]\n"
+ "Administrative cost\n"
+ "Registration lifetime [default: infinite]\n"
+ "Lifetime value in seconds\n")
+{
+ /* pfx vn un cost life */
+ return register_add(vty, NULL, argv[7], argv[9], argv[11], argv[13],
+ /* mac vni */
+ argv[3], argv[5], 0, NULL);
+}
+
+
+DEFUN (add_vnc_mac_vni_cost,
+ add_vnc_mac_vni_cost_cmd,
+ "add vnc mac X:X:X:X:X:X virtual-network-identifier (1-4294967295) vn <A.B.C.D|X:X::X:X> un <A.B.C.D|X:X::X:X> cost (0-255)",
+ "Add registration\n"
+ "VNC Information\n"
+ "Add/modify mac address information\n"
+ "MAC address\n"
+ "Virtual Network Identifier follows\n"
+ "Virtual Network Identifier\n"
+ "VN address of NVE\n"
+ "VN IPv4 interface address\n"
+ "VN IPv6 interface address\n"
+ "UN address of NVE\n"
+ "UN IPv4 interface address\n"
+ "UN IPv6 interface address\n"
+ "Administrative cost [default: 255]\n" "Administrative cost\n")
+{
+ /* pfx vn un cost life */
+ return register_add(vty, NULL, argv[7], argv[9], argv[11], NULL,
+ /* mac vni */
+ argv[3], argv[5], 0, NULL);
+}
+
+
+DEFUN (add_vnc_mac_vni_life,
+ add_vnc_mac_vni_life_cmd,
+ "add vnc mac X:X:X:X:X:X virtual-network-identifier (1-4294967295) vn <A.B.C.D|X:X::X:X> un <A.B.C.D|X:X::X:X> lifetime (1-4294967295)",
+ "Add registration\n"
+ "VNC Information\n"
+ "Add/modify mac address information\n"
+ "MAC address\n"
+ "Virtual Network Identifier follows\n"
+ "Virtual Network Identifier\n"
+ "VN address of NVE\n"
+ "VN IPv4 interface address\n"
+ "VN IPv6 interface address\n"
+ "UN address of NVE\n"
+ "UN IPv4 interface address\n"
+ "UN IPv6 interface address\n"
+ "Registration lifetime [default: infinite]\n"
+ "Lifetime value in seconds\n")
+{
+ /* pfx vn un cost life */
+ return register_add(vty, NULL, argv[7], argv[9], NULL, argv[11],
+ /* mac vni */
+ argv[3], argv[5], 0, NULL);
+}
+
+
+DEFUN (add_vnc_mac_vni,
+ add_vnc_mac_vni_cmd,
+ "add vnc mac X:X:X:X:X:X virtual-network-identifier (1-4294967295) vn <A.B.C.D|X:X::X:X> un <A.B.C.D|X:X::X:X>",
+ "Add registration\n"
+ "VNC Information\n"
+ "Add/modify mac address information\n"
+ "MAC address\n"
+ "Virtual Network Identifier follows\n"
+ "Virtual Network Identifier\n"
+ "VN address of NVE\n"
+ "VN IPv4 interface address\n"
+ "VN IPv6 interface address\n"
+ "UN address of NVE\n"
+ "UN IPv4 interface address\n" "UN IPv6 interface address\n")
+{
+ /* pfx vn un cost life */
+ return register_add(vty, NULL, argv[7], argv[9], NULL, NULL,
+ /* mac vni */
+ argv[3], argv[5], 0, NULL);
+}
+
+/************************************************************************
+ * Delete prefix
+ ************************************************************************/
+
+struct rfapi_local_reg_delete_arg {
+ /*
+ * match parameters
+ */
+ struct bgp *bgp;
+ struct rfapi_ip_addr un_address; /* AF==0: wildcard */
+ struct rfapi_ip_addr vn_address; /* AF==0: wildcard */
+ struct prefix prefix; /* AF==0: wildcard */
+ struct prefix_rd rd; /* plen!=64: wildcard */
+ struct rfapi_nve_group_cfg *rfg; /* NULL: wildcard */
+
+ struct rfapi_l2address_option_match l2o;
+
+ /*
+ * result parameters
+ */
+ struct vty *vty;
+ uint32_t reg_count;
+ uint32_t pfx_count;
+ uint32_t query_count;
+
+ uint32_t failed_pfx_count;
+
+ uint32_t nve_count;
+ struct skiplist *nves;
+
+ uint32_t remote_active_nve_count;
+ uint32_t remote_active_pfx_count;
+ uint32_t remote_holddown_nve_count;
+ uint32_t remote_holddown_pfx_count;
+};
+
+struct nve_addr {
+ struct rfapi_ip_addr vn;
+ struct rfapi_ip_addr un;
+ struct rfapi_descriptor *rfd;
+ struct rfapi_local_reg_delete_arg *cda;
+};
+
+static void nve_addr_free(void *hap)
+{
+ ((struct nve_addr *)hap)->cda->nve_count += 1;
+ XFREE(MTYPE_RFAPI_NVE_ADDR, hap);
+}
+
+static int nve_addr_cmp(const void *k1, const void *k2)
+{
+ const struct nve_addr *a = (struct nve_addr *)k1;
+ const struct nve_addr *b = (struct nve_addr *)k2;
+ int ret = 0;
+
+ if (!a || !b) {
+ return (a - b);
+ }
+ if (a->un.addr_family != b->un.addr_family) {
+ return (a->un.addr_family - b->un.addr_family);
+ }
+ if (a->vn.addr_family != b->vn.addr_family) {
+ return (a->vn.addr_family - b->vn.addr_family);
+ }
+ if (a->un.addr_family == AF_INET) {
+ ret = IPV4_ADDR_CMP(&a->un.addr.v4, &b->un.addr.v4);
+ if (ret != 0) {
+ return ret;
+ }
+ } else if (a->un.addr_family == AF_INET6) {
+ ret = IPV6_ADDR_CMP(&a->un.addr.v6, &b->un.addr.v6);
+ if (ret != 0) {
+ return ret;
+ }
+ } else {
+ assert(0);
+ }
+ if (a->vn.addr_family == AF_INET) {
+ ret = IPV4_ADDR_CMP(&a->vn.addr.v4, &b->vn.addr.v4);
+ if (ret != 0)
+ return ret;
+ } else if (a->vn.addr_family == AF_INET6) {
+ ret = IPV6_ADDR_CMP(&a->vn.addr.v6, &b->vn.addr.v6);
+ if (ret == 0) {
+ return ret;
+ }
+ } else {
+ assert(0);
+ }
+ return 0;
+}
+
+static int parse_deleter_args(struct vty *vty, struct bgp *bgp,
+ const char *arg_prefix, const char *arg_vn,
+ const char *arg_un, const char *arg_l2addr,
+ const char *arg_vni, const char *arg_rd,
+ struct rfapi_nve_group_cfg *arg_rfg,
+ struct rfapi_local_reg_delete_arg *rcdarg)
+{
+ int rc = CMD_WARNING;
+
+ memset(rcdarg, 0, sizeof(struct rfapi_local_reg_delete_arg));
+
+ rcdarg->vty = vty;
+ if (bgp == NULL)
+ bgp = bgp_get_default();
+ rcdarg->bgp = bgp;
+ rcdarg->rfg = arg_rfg; /* may be NULL */
+
+ if (arg_vn && strcmp(arg_vn, "*")) {
+ if ((rc = rfapiCliGetRfapiIpAddr(vty, arg_vn,
+ &rcdarg->vn_address)))
+ return rc;
+ }
+ if (arg_un && strcmp(arg_un, "*")) {
+ if ((rc = rfapiCliGetRfapiIpAddr(vty, arg_un,
+ &rcdarg->un_address)))
+ return rc;
+ }
+ if (arg_prefix && strcmp(arg_prefix, "*")) {
+
+ if (!str2prefix(arg_prefix, &rcdarg->prefix)) {
+ vty_out(vty, "Malformed prefix \"%s\"\n", arg_prefix);
+ return rc;
+ }
+ }
+
+ if (arg_l2addr) {
+ if (!arg_vni) {
+ vty_out(vty, "Missing VNI\n");
+ return rc;
+ }
+ if (strcmp(arg_l2addr, "*")) {
+ if ((rc = rfapiStr2EthAddr(arg_l2addr,
+ &rcdarg->l2o.o.macaddr))) {
+ vty_out(vty, "Malformed L2 Address \"%s\"\n",
+ arg_l2addr);
+ return rc;
+ }
+ rcdarg->l2o.flags |= RFAPI_L2O_MACADDR;
+ }
+ if (strcmp(arg_vni, "*")) {
+ rcdarg->l2o.o.logical_net_id =
+ strtoul(arg_vni, NULL, 10);
+ rcdarg->l2o.flags |= RFAPI_L2O_LNI;
+ }
+ }
+ if (arg_rd) {
+ if (!str2prefix_rd(arg_rd, &rcdarg->rd)) {
+ vty_out(vty, "Malformed RD \"%s\"\n", arg_rd);
+ return rc;
+ }
+ }
+
+ return CMD_SUCCESS;
+}
+
+static int
+parse_deleter_tokens(struct vty *vty, struct bgp *bgp,
+ struct cmd_token *carg_prefix, struct cmd_token *carg_vn,
+ struct cmd_token *carg_un, struct cmd_token *carg_l2addr,
+ struct cmd_token *carg_vni, struct cmd_token *carg_rd,
+ struct rfapi_nve_group_cfg *arg_rfg,
+ struct rfapi_local_reg_delete_arg *rcdarg)
+{
+ const char *arg_prefix = carg_prefix ? carg_prefix->arg : NULL;
+ const char *arg_vn = carg_vn ? carg_vn->arg : NULL;
+ const char *arg_un = carg_un ? carg_un->arg : NULL;
+ const char *arg_l2addr = carg_l2addr ? carg_l2addr->arg : NULL;
+ const char *arg_vni = carg_vni ? carg_vni->arg : NULL;
+ const char *arg_rd = carg_rd ? carg_rd->arg : NULL;
+ return parse_deleter_args(vty, bgp, arg_prefix, arg_vn, arg_un,
+ arg_l2addr, arg_vni, arg_rd, arg_rfg, rcdarg);
+}
+
+static void record_nve_in_cda_list(struct rfapi_local_reg_delete_arg *cda,
+ struct rfapi_ip_addr *un_address,
+ struct rfapi_ip_addr *vn_address,
+ struct rfapi_descriptor *rfd)
+{
+ struct nve_addr ha;
+ struct nve_addr *hap;
+
+ memset(&ha, 0, sizeof(ha));
+ ha.un = *un_address;
+ ha.vn = *vn_address;
+ ha.rfd = rfd;
+
+ if (!cda->nves)
+ cda->nves = skiplist_new(0, nve_addr_cmp, nve_addr_free);
+
+ if (skiplist_search(cda->nves, &ha, (void *)&hap)) {
+ hap = XCALLOC(MTYPE_RFAPI_NVE_ADDR, sizeof(struct nve_addr));
+ assert(hap);
+ ha.cda = cda;
+ *hap = ha;
+ skiplist_insert(cda->nves, hap, hap);
+ }
+}
+
+static void clear_vnc_responses(struct rfapi_local_reg_delete_arg *cda)
+{
+ struct rfapi *h;
+ struct rfapi_descriptor *rfd;
+ int query_count = 0;
+ struct listnode *node;
+ struct bgp *bgp_default = bgp_get_default();
+
+ if (cda->vn_address.addr_family && cda->un_address.addr_family) {
+ /*
+ * Single nve case
+ */
+ if (rfapi_find_rfd(bgp_default, &cda->vn_address,
+ &cda->un_address, &rfd))
+ return;
+
+ rfapiRibClear(rfd);
+ rfapi_query_done_all(rfd, &query_count);
+ cda->query_count += query_count;
+
+ /*
+ * Track unique nves seen
+ */
+ record_nve_in_cda_list(cda, &rfd->un_addr, &rfd->vn_addr, rfd);
+ return;
+ }
+
+ /*
+ * wildcard case
+ */
+
+ if (!bgp_default)
+ return; /* ENXIO */
+
+ h = bgp_default->rfapi;
+
+ if (!h)
+ return; /* ENXIO */
+
+ for (ALL_LIST_ELEMENTS_RO(&h->descriptors, node, rfd)) {
+ /*
+ * match un, vn addresses of NVEs
+ */
+ if (cda->un_address.addr_family
+ && rfapi_ip_addr_cmp(&cda->un_address, &rfd->un_addr)) {
+ continue;
+ }
+ if (cda->vn_address.addr_family
+ && rfapi_ip_addr_cmp(&cda->vn_address, &rfd->vn_addr)) {
+ continue;
+ }
+
+ rfapiRibClear(rfd);
+
+ rfapi_query_done_all(rfd, &query_count);
+ cda->query_count += query_count;
+
+ /*
+ * Track unique nves seen
+ */
+ record_nve_in_cda_list(cda, &rfd->un_addr, &rfd->vn_addr, rfd);
+ }
+}
+
+/*
+ * TBD need to count deleted prefixes and nves?
+ *
+ * ENXIO BGP or VNC not configured
+ */
+static int rfapiDeleteLocalPrefixesByRFD(struct rfapi_local_reg_delete_arg *cda,
+ struct rfapi_descriptor *rfd)
+{
+ struct rfapi_ip_addr *pUn; /* NULL = wildcard */
+ struct rfapi_ip_addr *pVn; /* NULL = wildcard */
+ struct prefix *pPrefix; /* NULL = wildcard */
+ struct prefix_rd *pPrd; /* NULL = wildcard */
+
+ struct rfapi_ip_prefix rprefix;
+ struct rfapi_next_hop_entry *head = NULL;
+ struct rfapi_next_hop_entry *tail = NULL;
+
+#if DEBUG_L2_EXTRA
+ vnc_zlog_debug_verbose("%s: entry", __func__);
+#endif
+
+ pUn = (cda->un_address.addr_family ? &cda->un_address : NULL);
+ pVn = (cda->vn_address.addr_family ? &cda->vn_address : NULL);
+ pPrefix = (cda->prefix.family ? &cda->prefix : NULL);
+ pPrd = (cda->rd.prefixlen == 64 ? &cda->rd : NULL);
+
+ if (pPrefix) {
+ rfapiQprefix2Rprefix(pPrefix, &rprefix);
+ }
+
+ do /* to preserve old code structure */
+ {
+ struct rfapi *h = cda->bgp->rfapi;
+ ;
+ struct rfapi_adb *adb;
+ int rc;
+ int deleted_from_this_nve;
+ struct nve_addr ha;
+ struct nve_addr *hap;
+
+#if DEBUG_L2_EXTRA
+ vnc_zlog_debug_verbose("%s: rfd=%p", __func__, rfd);
+#endif
+
+ /*
+ * match un, vn addresses of NVEs
+ */
+ if (pUn && (rfapi_ip_addr_cmp(pUn, &rfd->un_addr)))
+ break;
+ if (pVn && (rfapi_ip_addr_cmp(pVn, &rfd->vn_addr)))
+ break;
+
+#if DEBUG_L2_EXTRA
+ vnc_zlog_debug_verbose("%s: un, vn match", __func__);
+#endif
+
+ /*
+ * match prefix
+ */
+
+ deleted_from_this_nve = 0;
+
+ {
+ struct skiplist *sl;
+ struct rfapi_ip_prefix rp;
+ void *cursor;
+ struct list *adb_delete_list;
+
+ /*
+ * The advertisements are stored in a skiplist.
+ * Withdrawing
+ * the registration deletes the advertisement from the
+ * skiplist, which we can't do while iterating over that
+ * same skiplist using the current skiplist API.
+ *
+ * Strategy: iterate over the skiplist and build another
+ * list containing only the matching ADBs. Then delete
+ * _everything_ in that second list (which can be done
+ * using either skiplists or quagga linklists).
+ */
+ adb_delete_list = list_new();
+
+ /*
+ * Advertised IP prefixes (not 0/32 or 0/128)
+ */
+ sl = rfd->advertised.ipN_by_prefix;
+
+ for (cursor = NULL,
+ rc = skiplist_next(sl, NULL, (void **)&adb,
+ &cursor);
+ !rc; rc = skiplist_next(sl, NULL, (void **)&adb,
+ &cursor)) {
+
+ if (pPrefix) {
+ if (!prefix_same(pPrefix,
+ &adb->u.s.prefix_ip)) {
+#if DEBUG_L2_EXTRA
+ vnc_zlog_debug_verbose(
+ "%s: adb=%p, prefix doesn't match, skipping",
+ __func__, adb);
+#endif
+ continue;
+ }
+ }
+ if (pPrd) {
+ if (memcmp(pPrd->val, adb->u.s.prd.val,
+ 8)
+ != 0) {
+#if DEBUG_L2_EXTRA
+ vnc_zlog_debug_verbose(
+ "%s: adb=%p, RD doesn't match, skipping",
+ __func__, adb);
+#endif
+ continue;
+ }
+ }
+ if (CHECK_FLAG(cda->l2o.flags,
+ RFAPI_L2O_MACADDR)) {
+ if (memcmp(cda->l2o.o.macaddr.octet,
+ adb->u.s.prefix_eth.u
+ .prefix_eth.octet,
+ ETH_ALEN)) {
+#if DEBUG_L2_EXTRA
+ vnc_zlog_debug_verbose(
+ "%s: adb=%p, macaddr doesn't match, skipping",
+ __func__, adb);
+#endif
+ continue;
+ }
+ }
+
+ if (CHECK_FLAG(cda->l2o.flags, RFAPI_L2O_LNI)) {
+ if (cda->l2o.o.logical_net_id
+ != adb->l2o.logical_net_id) {
+#if DEBUG_L2_EXTRA
+ vnc_zlog_debug_verbose(
+ "%s: adb=%p, LNI doesn't match, skipping",
+ __func__, adb);
+#endif
+ continue;
+ }
+ }
+
+#if DEBUG_L2_EXTRA
+ vnc_zlog_debug_verbose(
+ "%s: ipN adding adb %p to delete list",
+ __func__, adb);
+#endif
+
+ listnode_add(adb_delete_list, adb);
+ }
+
+ struct listnode *node;
+
+ for (ALL_LIST_ELEMENTS_RO(adb_delete_list, node, adb)) {
+ int this_advertisement_prefix_count;
+ struct rfapi_vn_option optary[3];
+ struct rfapi_vn_option *opt = NULL;
+ int cur_opt = 0;
+
+ this_advertisement_prefix_count = 1;
+
+ rfapiQprefix2Rprefix(&adb->u.s.prefix_ip, &rp);
+
+ memset(optary, 0, sizeof(optary));
+
+ /* if mac addr present in advert, make l2o vn
+ * option */
+ if (adb->u.s.prefix_eth.family == AF_ETHERNET) {
+ if (opt != NULL)
+ opt->next = &optary[cur_opt];
+ opt = &optary[cur_opt++];
+ opt->type = RFAPI_VN_OPTION_TYPE_L2ADDR;
+ opt->v.l2addr.macaddr =
+ adb->u.s.prefix_eth.u
+ .prefix_eth;
+ ++this_advertisement_prefix_count;
+ }
+ /*
+ * use saved RD value instead of trying to
+ * invert
+ * complex RD computation in rfapi_register()
+ */
+ if (opt != NULL)
+ opt->next = &optary[cur_opt];
+ opt = &optary[cur_opt++];
+ opt->type = RFAPI_VN_OPTION_TYPE_INTERNAL_RD;
+ opt->v.internal_rd = adb->u.s.prd;
+
+#if DEBUG_L2_EXTRA
+ vnc_zlog_debug_verbose(
+ "%s: ipN killing reg from adb %p ",
+ __func__, adb);
+#endif
+
+ rc = rfapi_register(rfd, &rp, 0, NULL,
+ (cur_opt ? optary : NULL),
+ RFAPI_REGISTER_KILL);
+ if (!rc) {
+ cda->pfx_count +=
+ this_advertisement_prefix_count;
+ cda->reg_count += 1;
+ deleted_from_this_nve = 1;
+ }
+ if (h->rfp_methods.local_cb) {
+ rfapiAddDeleteLocalRfpPrefix(
+ &rfd->un_addr, &rfd->vn_addr,
+ &rp, 0, 0, NULL, &head, &tail);
+ }
+ }
+ list_delete_all_node(adb_delete_list);
+
+ if (!(pPrefix && !RFAPI_0_PREFIX(pPrefix))) {
+ /*
+ * Caller didn't specify a prefix, or specified
+ * (0/32 or 0/128)
+ */
+
+ /*
+ * Advertised 0/32 and 0/128 (indexed by
+ * ethernet address)
+ */
+ sl = rfd->advertised.ip0_by_ether;
+
+ for (cursor = NULL,
+ rc = skiplist_next(sl, NULL, (void **)&adb,
+ &cursor);
+ !rc;
+ rc = skiplist_next(sl, NULL, (void **)&adb,
+ &cursor)) {
+
+ if (CHECK_FLAG(cda->l2o.flags,
+ RFAPI_L2O_MACADDR)) {
+ if (memcmp(cda->l2o.o.macaddr
+ .octet,
+ adb->u.s.prefix_eth.u
+ .prefix_eth
+ .octet,
+ ETH_ALEN)) {
+
+ continue;
+ }
+ }
+ if (CHECK_FLAG(cda->l2o.flags,
+ RFAPI_L2O_LNI)) {
+ if (cda->l2o.o.logical_net_id
+ != adb->l2o.logical_net_id) {
+ continue;
+ }
+ }
+#if DEBUG_L2_EXTRA
+ vnc_zlog_debug_verbose(
+ "%s: ip0 adding adb %p to delete list",
+ __func__, adb);
+#endif
+ listnode_add(adb_delete_list, adb);
+ }
+
+
+ for (ALL_LIST_ELEMENTS_RO(adb_delete_list, node,
+ adb)) {
+
+ struct rfapi_vn_option vn;
+
+ rfapiQprefix2Rprefix(
+ &adb->u.s.prefix_ip, &rp);
+
+ memset(&vn, 0, sizeof(vn));
+ vn.type = RFAPI_VN_OPTION_TYPE_L2ADDR;
+ vn.v.l2addr = adb->l2o;
+
+#if DEBUG_L2_EXTRA
+ vnc_zlog_debug_verbose(
+ "%s: ip0 killing reg from adb %p ",
+ __func__, adb);
+#endif
+
+ rc = rfapi_register(
+ rfd, &rp, 0, NULL, &vn,
+ RFAPI_REGISTER_KILL);
+ if (!rc) {
+ cda->pfx_count += 1;
+ cda->reg_count += 1;
+ deleted_from_this_nve = 1;
+ }
+ if (h->rfp_methods.local_cb) {
+ struct rfapi_vn_option
+ *vn_opt_new;
+
+ vn_opt_new =
+ rfapi_vn_options_dup(
+ &vn);
+ rfapiAddDeleteLocalRfpPrefix(
+ &rfd->un_addr,
+ &rfd->vn_addr, &rp, 0,
+ 0, vn_opt_new, &head,
+ &tail);
+ }
+ }
+ list_delete_all_node(adb_delete_list);
+ }
+ list_delete(&adb_delete_list);
+ }
+
+
+ if (head) { /* should not be set if (NULL ==
+ rfapi_cfg->local_cb) */
+ h->flags |= RFAPI_INCALLBACK;
+ (*h->rfp_methods.local_cb)(head, rfd->cookie);
+ h->flags &= ~RFAPI_INCALLBACK;
+ head = tail = NULL;
+ }
+
+ if (deleted_from_this_nve) {
+ /*
+ * track unique NVEs seen
+ */
+ memset(&ha, 0, sizeof(ha));
+ ha.un = rfd->un_addr;
+ ha.vn = rfd->vn_addr;
+
+ if (!cda->nves)
+ cda->nves = skiplist_new(0, nve_addr_cmp,
+ nve_addr_free);
+ if (skiplist_search(cda->nves, &ha, (void **)&hap)) {
+ hap = XCALLOC(MTYPE_RFAPI_NVE_ADDR,
+ sizeof(struct nve_addr));
+ assert(hap);
+ ha.cda = cda;
+ *hap = ha;
+ skiplist_insert(cda->nves, hap, hap);
+ }
+ }
+ } while (0); /* to preserve old code structure */
+
+ return 0;
+}
+
+static int rfapiDeleteLocalPrefixes(struct rfapi_local_reg_delete_arg *cda)
+{
+ int rc = 0;
+
+ if (cda->rfg) {
+ if (cda->rfg->rfd) /* if not open, nothing to delete */
+ rc = rfapiDeleteLocalPrefixesByRFD(cda, cda->rfg->rfd);
+ } else {
+ struct bgp *bgp = cda->bgp;
+ struct rfapi *h;
+ struct rfapi_cfg *rfapi_cfg;
+
+ struct listnode *node;
+ struct rfapi_descriptor *rfd;
+ if (!bgp)
+ return ENXIO;
+ h = bgp->rfapi;
+ rfapi_cfg = bgp->rfapi_cfg;
+ if (!h || !rfapi_cfg)
+ return ENXIO;
+ vnc_zlog_debug_verbose("%s: starting descriptor loop",
+ __func__);
+ for (ALL_LIST_ELEMENTS_RO(&h->descriptors, node, rfd)) {
+ rc = rfapiDeleteLocalPrefixesByRFD(cda, rfd);
+ }
+ }
+ return rc;
+}
+
+/*
+ * clear_vnc_prefix
+ *
+ * Deletes local and remote prefixes that match
+ */
+static void clear_vnc_prefix(struct rfapi_local_reg_delete_arg *cda)
+{
+ struct prefix pfx_un;
+ struct prefix pfx_vn;
+
+ struct prefix *pUN = NULL;
+ struct prefix *pVN = NULL;
+ struct prefix *pPrefix = NULL;
+
+ struct rfapi_import_table *it = NULL;
+
+ /*
+ * Delete matching remote prefixes in holddown
+ */
+ if (cda->vn_address.addr_family) {
+ if (!rfapiRaddr2Qprefix(&cda->vn_address, &pfx_vn))
+ pVN = &pfx_vn;
+ }
+ if (cda->un_address.addr_family) {
+ if (!rfapiRaddr2Qprefix(&cda->un_address, &pfx_un))
+ pUN = &pfx_un;
+ }
+ if (cda->prefix.family) {
+ pPrefix = &cda->prefix;
+ }
+ if (cda->rfg) {
+ it = cda->rfg->rfapi_import_table;
+ }
+ rfapiDeleteRemotePrefixes(
+ pUN, pVN, pPrefix, it, 0, 1, &cda->remote_active_pfx_count,
+ &cda->remote_active_nve_count, &cda->remote_holddown_pfx_count,
+ &cda->remote_holddown_nve_count);
+
+ /*
+ * Now do local prefixes
+ */
+ rfapiDeleteLocalPrefixes(cda);
+}
+
+static void print_cleared_stats(struct rfapi_local_reg_delete_arg *cda)
+{
+ struct vty *vty = cda->vty; /* for benefit of VTYNL */
+
+ /* Our special element-deleting function counts nves */
+ if (cda->nves) {
+ skiplist_free(cda->nves);
+ cda->nves = NULL;
+ }
+ if (cda->failed_pfx_count)
+ vty_out(vty, "Failed to delete %d prefixes\n",
+ cda->failed_pfx_count);
+
+ /* left as "prefixes" even in single case for ease of machine parsing */
+ vty_out(vty,
+ "[Local] Cleared %u registrations, %u prefixes, %u responses from %d NVEs\n",
+ cda->reg_count, cda->pfx_count, cda->query_count,
+ cda->nve_count);
+
+ /*
+ * We don't currently allow deletion of active remote prefixes from
+ * the command line
+ */
+
+ vty_out(vty, "[Holddown] Cleared %u prefixes from %u NVEs\n",
+ cda->remote_holddown_pfx_count, cda->remote_holddown_nve_count);
+}
+
+/*
+ * Caller has already deleted registrations and queries for this/these
+ * NVEs. Now we just have to close their descriptors.
+ */
+static void clear_vnc_nve_closer(struct rfapi_local_reg_delete_arg *cda)
+{
+ struct skiplist *sl = cda->nves; /* contains affected NVEs */
+ struct nve_addr *pKey;
+ struct nve_addr *pValue;
+ void *cursor = NULL;
+ int rc;
+
+ if (!sl)
+ return;
+
+ for (rc = skiplist_next(sl, (void **)&pKey, (void **)&pValue, &cursor);
+ !rc; rc = skiplist_next(sl, (void **)&pKey, (void **)&pValue,
+ &cursor)) {
+
+ if (pValue->rfd) {
+ pValue->rfd->flags |=
+ RFAPI_HD_FLAG_CLOSING_ADMINISTRATIVELY;
+ rfapi_close(pValue->rfd);
+ }
+ }
+}
+
+DEFUN (clear_vnc_nve_all,
+ clear_vnc_nve_all_cmd,
+ "clear vnc nve *",
+ "clear\n"
+ "VNC Information\n"
+ "Clear per NVE information\n"
+ "For all NVEs\n")
+{
+
+ struct rfapi_local_reg_delete_arg cda;
+ int rc;
+
+ if ((rc = parse_deleter_args(vty, NULL, NULL, NULL, NULL, NULL, NULL,
+ NULL, NULL, &cda)))
+ return rc;
+
+ cda.vty = vty;
+
+ clear_vnc_responses(&cda);
+ clear_vnc_prefix(&cda);
+ clear_vnc_nve_closer(&cda);
+
+ print_cleared_stats(&cda);
+
+ return 0;
+}
+
+DEFUN (clear_vnc_nve_vn_un,
+ clear_vnc_nve_vn_un_cmd,
+ "clear vnc nve vn <*|A.B.C.D|X:X::X:X> un <*|A.B.C.D|X:X::X:X>",
+ "clear\n"
+ "VNC Information\n"
+ "Clear prefix registration information\n"
+ "VN address of NVE\n"
+ "For all NVEs\n"
+ "VN IPv4 interface address\n"
+ "VN IPv6 interface address\n"
+ "UN address of NVE\n"
+ "For all UN addresses\n"
+ "UN IPv4 interface address\n"
+ "UN IPv6 interface address\n")
+{
+ struct rfapi_local_reg_delete_arg cda;
+ int rc;
+
+ if ((rc = parse_deleter_tokens(vty, NULL, NULL, argv[4], argv[6], NULL,
+ NULL, NULL, NULL, &cda)))
+ return rc;
+
+ cda.vty = vty;
+
+ clear_vnc_responses(&cda);
+ clear_vnc_prefix(&cda);
+ clear_vnc_nve_closer(&cda);
+
+ print_cleared_stats(&cda);
+
+ return 0;
+}
+
+DEFUN (clear_vnc_nve_un_vn,
+ clear_vnc_nve_un_vn_cmd,
+ "clear vnc nve un <*|A.B.C.D|X:X::X:X> vn <*|A.B.C.D|X:X::X:X>",
+ "clear\n"
+ "VNC Information\n"
+ "Clear prefix registration information\n"
+ "UN address of NVE\n"
+ "For all un NVEs\n"
+ "UN IPv4 interface address\n"
+ "UN IPv6 interface address\n"
+ "VN address of NVE\n"
+ "For all vn NVEs\n"
+ "VN IPv4 interface address\n"
+ "VN IPv6 interface address\n")
+{
+ struct rfapi_local_reg_delete_arg cda;
+ int rc;
+
+ if ((rc = parse_deleter_tokens(vty, NULL, NULL, argv[6], argv[4], NULL,
+ NULL, NULL, NULL, &cda)))
+ return rc;
+
+ cda.vty = vty;
+
+ clear_vnc_responses(&cda);
+ clear_vnc_prefix(&cda);
+ clear_vnc_nve_closer(&cda);
+
+ print_cleared_stats(&cda);
+
+ return 0;
+}
+
+DEFUN (clear_vnc_nve_vn,
+ clear_vnc_nve_vn_cmd,
+ "clear vnc nve vn <*|A.B.C.D|X:X::X:X>",
+ "clear\n"
+ "VNC Information\n"
+ "Clear prefix registration information\n"
+ "VN address of NVE\n"
+ "All addresses\n"
+ "VN IPv4 interface address\n"
+ "VN IPv6 interface address\n")
+{
+ struct rfapi_local_reg_delete_arg cda;
+ int rc;
+
+ if ((rc = parse_deleter_tokens(vty, NULL, NULL, argv[4], NULL, NULL,
+ NULL, NULL, NULL, &cda)))
+ return rc;
+
+ cda.vty = vty;
+
+ clear_vnc_responses(&cda);
+ clear_vnc_prefix(&cda);
+ clear_vnc_nve_closer(&cda);
+
+ print_cleared_stats(&cda);
+ return 0;
+}
+
+DEFUN (clear_vnc_nve_un,
+ clear_vnc_nve_un_cmd,
+ "clear vnc nve un <*|A.B.C.D|X:X::X:X>",
+ "clear\n"
+ "VNC Information\n"
+ "Clear prefix registration information\n"
+ "UN address of NVE\n"
+ "All un nves\n"
+ "UN IPv4 interface address\n"
+ "UN IPv6 interface address\n")
+{
+ struct rfapi_local_reg_delete_arg cda;
+ int rc;
+
+ if ((rc = parse_deleter_tokens(vty, NULL, NULL, NULL, argv[4], NULL,
+ NULL, NULL, NULL, &cda)))
+ return rc;
+
+ cda.vty = vty;
+
+ clear_vnc_responses(&cda);
+ clear_vnc_prefix(&cda);
+ clear_vnc_nve_closer(&cda);
+
+ print_cleared_stats(&cda);
+ return 0;
+}
+
+/*-------------------------------------------------
+ * Clear VNC Prefix
+ *-------------------------------------------------*/
+
+/*
+ * This function is defined in this file (rather than in rfp_registration.c)
+ * because here we have access to all the task handles.
+ */
+DEFUN (clear_vnc_prefix_vn_un,
+ clear_vnc_prefix_vn_un_cmd,
+ "clear vnc prefix <*|A.B.C.D/M|X:X::X:X/M> vn <*|A.B.C.D|X:X::X:X> un <*|A.B.C.D|X:X::X:X>",
+ "clear\n"
+ "VNC Information\n"
+ "Clear prefix registration information\n"
+ "All prefixes\n"
+ "IPv4 prefix\n"
+ "IPv6 prefix\n"
+ "VN address of NVE\n"
+ "All VN addresses\n"
+ "VN IPv4 interface address\n"
+ "VN IPv6 interface address\n"
+ "UN address of NVE\n"
+ "All UN addresses\n"
+ "UN IPv4 interface address\n"
+ "UN IPv6 interface address\n")
+{
+ struct rfapi_local_reg_delete_arg cda;
+ int rc;
+
+ if ((rc = parse_deleter_tokens(vty, NULL, argv[3], argv[5], argv[7],
+ NULL, NULL, NULL, NULL, &cda)))
+ return rc;
+ cda.vty = vty;
+ clear_vnc_prefix(&cda);
+ print_cleared_stats(&cda);
+ return 0;
+}
+
+DEFUN (clear_vnc_prefix_un_vn,
+ clear_vnc_prefix_un_vn_cmd,
+ "clear vnc prefix <*|A.B.C.D/M|X:X::X:X/M> un <*|A.B.C.D|X:X::X:X> vn <*|A.B.C.D|X:X::X:X>",
+ "clear\n"
+ "VNC Information\n"
+ "Clear prefix registration information\n"
+ "All prefixes\n"
+ "IPv4 prefix\n"
+ "IPv6 prefix\n"
+ "UN address of NVE\n"
+ "All UN addresses\n"
+ "UN IPv4 interface address\n"
+ "UN IPv6 interface address\n"
+ "VN address of NVE\n"
+ "All VN addresses\n"
+ "VN IPv4 interface address\n"
+ "VN IPv6 interface address\n")
+{
+ struct rfapi_local_reg_delete_arg cda;
+ int rc;
+
+ if ((rc = parse_deleter_tokens(vty, NULL, argv[3], argv[7], argv[5],
+ NULL, NULL, NULL, NULL, &cda)))
+ return rc;
+ cda.vty = vty;
+ clear_vnc_prefix(&cda);
+ print_cleared_stats(&cda);
+ return 0;
+}
+
+DEFUN (clear_vnc_prefix_un,
+ clear_vnc_prefix_un_cmd,
+ "clear vnc prefix <*|A.B.C.D/M|X:X::X:X/M> un <*|A.B.C.D|X:X::X:X>",
+ "clear\n"
+ "VNC Information\n"
+ "Clear prefix registration information\n"
+ "All prefixes\n"
+ "IPv4 prefix\n"
+ "IPv6 prefix\n"
+ "UN address of NVE\n"
+ "All UN addresses\n"
+ "UN IPv4 interface address\n"
+ "UN IPv6 interface address\n")
+{
+ struct rfapi_local_reg_delete_arg cda;
+ int rc;
+
+ if ((rc = parse_deleter_tokens(vty, NULL, argv[3], NULL, argv[5], NULL,
+ NULL, NULL, NULL, &cda)))
+ return rc;
+ cda.vty = vty;
+ clear_vnc_prefix(&cda);
+ print_cleared_stats(&cda);
+ return 0;
+}
+
+DEFUN (clear_vnc_prefix_vn,
+ clear_vnc_prefix_vn_cmd,
+ "clear vnc prefix <*|A.B.C.D/M|X:X::X:X/M> vn <*|A.B.C.D|X:X::X:X>",
+ "clear\n"
+ "VNC Information\n"
+ "Clear prefix registration information\n"
+ "All prefixes\n"
+ "IPv4 prefix\n"
+ "IPv6 prefix\n"
+ "UN address of NVE\n"
+ "All VN addresses\n"
+ "VN IPv4 interface address\n"
+ "VN IPv6 interface address\n")
+{
+ struct rfapi_local_reg_delete_arg cda;
+ int rc;
+
+ if ((rc = parse_deleter_tokens(vty, NULL, argv[3], argv[5], NULL, NULL,
+ NULL, NULL, NULL, &cda)))
+ return rc;
+ cda.vty = vty;
+ clear_vnc_prefix(&cda);
+ print_cleared_stats(&cda);
+ return 0;
+}
+
+DEFUN (clear_vnc_prefix_all,
+ clear_vnc_prefix_all_cmd,
+ "clear vnc prefix <*|A.B.C.D/M|X:X::X:X/M> *",
+ "clear\n"
+ "VNC Information\n"
+ "Clear prefix registration information\n"
+ "All prefixes\n"
+ "IPv4 prefix\n"
+ "IPv6 prefix\n"
+ "From any NVE\n")
+{
+ struct rfapi_local_reg_delete_arg cda;
+ int rc;
+
+ if ((rc = parse_deleter_tokens(vty, NULL, argv[3], NULL, NULL, NULL,
+ NULL, NULL, NULL, &cda)))
+ return rc;
+ cda.vty = vty;
+ clear_vnc_prefix(&cda);
+ print_cleared_stats(&cda);
+ return 0;
+}
+
+/*-------------------------------------------------
+ * Clear VNC MAC
+ *-------------------------------------------------*/
+
+/*
+ * This function is defined in this file (rather than in rfp_registration.c)
+ * because here we have access to all the task handles.
+ */
+DEFUN (clear_vnc_mac_vn_un,
+ clear_vnc_mac_vn_un_cmd,
+ "clear vnc mac <*|X:X:X:X:X:X> virtual-network-identifier <*|(1-4294967295)> vn <*|A.B.C.D|X:X::X:X> un <*|A.B.C.D|X:X::X:X>",
+ "clear\n"
+ "VNC Information\n"
+ "Clear mac registration information\n"
+ "All macs\n"
+ "MAC address\n"
+ "VNI keyword\n"
+ "Any virtual network identifier\n"
+ "Virtual network identifier\n"
+ "VN address of NVE\n"
+ "All VN addresses\n"
+ "VN IPv4 interface address\n"
+ "VN IPv6 interface address\n"
+ "UN address of NVE\n"
+ "All UN addresses\n"
+ "UN IPv4 interface address\n"
+ "UN IPv6 interface address\n")
+{
+ struct rfapi_local_reg_delete_arg cda;
+ int rc;
+
+ /* pfx vn un L2 VNI */
+ if ((rc = parse_deleter_tokens(vty, NULL, NULL, argv[7], argv[9],
+ argv[3], argv[5], NULL, NULL, &cda)))
+ return rc;
+ cda.vty = vty;
+ clear_vnc_prefix(&cda);
+ print_cleared_stats(&cda);
+ return 0;
+}
+
+DEFUN (clear_vnc_mac_un_vn,
+ clear_vnc_mac_un_vn_cmd,
+ "clear vnc mac <*|X:X:X:X:X:X> virtual-network-identifier <*|(1-4294967295)> un <*|A.B.C.D|X:X::X:X> vn <*|A.B.C.D|X:X::X:X>",
+ "clear\n"
+ "VNC Information\n"
+ "Clear mac registration information\n"
+ "All macs\n"
+ "MAC address\n"
+ "VNI keyword\n"
+ "Any virtual network identifier\n"
+ "Virtual network identifier\n"
+ "UN address of NVE\n"
+ "All UN addresses\n"
+ "UN IPv4 interface address\n"
+ "UN IPv6 interface address\n"
+ "VN address of NVE\n"
+ "All VN addresses\n"
+ "VN IPv4 interface address\n"
+ "VN IPv6 interface address\n")
+{
+ struct rfapi_local_reg_delete_arg cda;
+ int rc;
+
+ /* pfx vn un L2 VNI */
+ if ((rc = parse_deleter_tokens(vty, NULL, NULL, argv[9], argv[7],
+ argv[3], argv[5], NULL, NULL, &cda)))
+ return rc;
+ cda.vty = vty;
+ clear_vnc_prefix(&cda);
+ print_cleared_stats(&cda);
+ return 0;
+}
+
+DEFUN (clear_vnc_mac_un,
+ clear_vnc_mac_un_cmd,
+ "clear vnc mac <*|X:X:X:X:X:X> virtual-network-identifier <*|(1-4294967295)> un <*|A.B.C.D|X:X::X:X>",
+ "clear\n"
+ "VNC Information\n"
+ "Clear mac registration information\n"
+ "All macs\n"
+ "MAC address\n"
+ "VNI keyword\n"
+ "Any virtual network identifier\n"
+ "Virtual network identifier\n"
+ "UN address of NVE\n"
+ "All UN addresses\n"
+ "UN IPv4 interface address\n"
+ "UN IPv6 interface address\n")
+{
+ struct rfapi_local_reg_delete_arg cda;
+ int rc;
+
+ /* pfx vn un L2 VNI */
+ if ((rc = parse_deleter_tokens(vty, NULL, NULL, NULL, argv[7], argv[3],
+ argv[5], NULL, NULL, &cda)))
+ return rc;
+ cda.vty = vty;
+ clear_vnc_prefix(&cda);
+ print_cleared_stats(&cda);
+ return 0;
+}
+
+DEFUN (clear_vnc_mac_vn,
+ clear_vnc_mac_vn_cmd,
+ "clear vnc mac <*|X:X:X:X:X:X> virtual-network-identifier <*|(1-4294967295)> vn <*|A.B.C.D|X:X::X:X>",
+ "clear\n"
+ "VNC Information\n"
+ "Clear mac registration information\n"
+ "All macs\n"
+ "MAC address\n"
+ "VNI keyword\n"
+ "Any virtual network identifier\n"
+ "Virtual network identifier\n"
+ "UN address of NVE\n"
+ "All VN addresses\n"
+ "VN IPv4 interface address\n"
+ "VN IPv6 interface address\n")
+{
+ struct rfapi_local_reg_delete_arg cda;
+ int rc;
+
+ /* pfx vn un L2 VNI */
+ if ((rc = parse_deleter_tokens(vty, NULL, NULL, argv[7], NULL, argv[3],
+ argv[5], NULL, NULL, &cda)))
+ return rc;
+ cda.vty = vty;
+ clear_vnc_prefix(&cda);
+ print_cleared_stats(&cda);
+ return 0;
+}
+
+DEFUN (clear_vnc_mac_all,
+ clear_vnc_mac_all_cmd,
+ "clear vnc mac <*|X:X:X:X:X:X> virtual-network-identifier <*|(1-4294967295)> *",
+ "clear\n"
+ "VNC Information\n"
+ "Clear mac registration information\n"
+ "All macs\n"
+ "MAC address\n"
+ "VNI keyword\n"
+ "Any virtual network identifier\n"
+ "Virtual network identifier\n"
+ "From any NVE\n")
+{
+ struct rfapi_local_reg_delete_arg cda;
+ int rc;
+
+ /* pfx vn un L2 VNI */
+ if ((rc = parse_deleter_tokens(vty, NULL, NULL, NULL, NULL, argv[3],
+ argv[5], NULL, NULL, &cda)))
+ return rc;
+ cda.vty = vty;
+ clear_vnc_prefix(&cda);
+ print_cleared_stats(&cda);
+ return 0;
+}
+
+/*-------------------------------------------------
+ * Clear VNC MAC PREFIX
+ *-------------------------------------------------*/
+
+DEFUN (clear_vnc_mac_vn_un_prefix,
+ clear_vnc_mac_vn_un_prefix_cmd,
+ "clear vnc mac <*|X:X:X:X:X:X> virtual-network-identifier <*|(1-4294967295)> vn <*|A.B.C.D|X:X::X:X> un <*|A.B.C.D|X:X::X:X> prefix <*|A.B.C.D/M|X:X::X:X/M>",
+ "clear\n"
+ "VNC Information\n"
+ "Clear mac registration information\n"
+ "All macs\n"
+ "MAC address\n"
+ "VNI keyword\n"
+ "Any virtual network identifier\n"
+ "Virtual network identifier\n"
+ "VN address of NVE\n"
+ "All VN addresses\n"
+ "VN IPv4 interface address\n"
+ "VN IPv6 interface address\n"
+ "UN address of NVE\n"
+ "All UN addresses\n"
+ "UN IPv4 interface address\n"
+ "UN IPv6 interface address\n"
+ "Clear prefix registration information\n"
+ "All prefixes\n"
+ "IPv4 prefix\n"
+ "IPv6 prefix\n")
+{
+ struct rfapi_local_reg_delete_arg cda;
+ int rc;
+
+ /* pfx vn un L2 VNI */
+ if ((rc = parse_deleter_tokens(vty, NULL, argv[11], argv[7], argv[9],
+ argv[3], argv[5], NULL, NULL, &cda)))
+ return rc;
+ cda.vty = vty;
+ clear_vnc_prefix(&cda);
+ print_cleared_stats(&cda);
+ return 0;
+}
+
+DEFUN (clear_vnc_mac_un_vn_prefix,
+ clear_vnc_mac_un_vn_prefix_cmd,
+ "clear vnc mac <*|X:X:X:X:X:X> virtual-network-identifier <*|(1-4294967295)> un <*|A.B.C.D|X:X::X:X> vn <*|A.B.C.D|X:X::X:X> prefix <*|A.B.C.D/M|X:X::X:X/M> prefix <*|A.B.C.D/M|X:X::X:X/M>",
+ "clear\n"
+ "VNC Information\n"
+ "Clear mac registration information\n"
+ "All macs\n"
+ "MAC address\n"
+ "VNI keyword\n"
+ "Any virtual network identifier\n"
+ "Virtual network identifier\n"
+ "UN address of NVE\n"
+ "All UN addresses\n"
+ "UN IPv4 interface address\n"
+ "UN IPv6 interface address\n"
+ "VN address of NVE\n"
+ "All VN addresses\n"
+ "VN IPv4 interface address\n"
+ "VN IPv6 interface address\n"
+ "Clear prefix registration information\n"
+ "All prefixes\n"
+ "IPv4 prefix\n"
+ "IPv6 prefix\n"
+ "Clear prefix registration information\n"
+ "All prefixes\n"
+ "IPv4 prefix\n"
+ "IPv6 prefix\n")
+{
+ struct rfapi_local_reg_delete_arg cda;
+ int rc;
+
+ /* pfx vn un L2 VNI */
+ if ((rc = parse_deleter_tokens(vty, NULL, argv[11], argv[9], argv[7],
+ argv[3], argv[5], NULL, NULL, &cda)))
+ return rc;
+ cda.vty = vty;
+ clear_vnc_prefix(&cda);
+ print_cleared_stats(&cda);
+ return 0;
+}
+
+DEFUN (clear_vnc_mac_un_prefix,
+ clear_vnc_mac_un_prefix_cmd,
+ "clear vnc mac <*|X:X:X:X:X:X> virtual-network-identifier <*|(1-4294967295)> un <*|A.B.C.D|X:X::X:X> prefix <*|A.B.C.D/M|X:X::X:X/M>",
+ "clear\n"
+ "VNC Information\n"
+ "Clear mac registration information\n"
+ "All macs\n"
+ "MAC address\n"
+ "VNI keyword\n"
+ "Any virtual network identifier\n"
+ "Virtual network identifier\n"
+ "UN address of NVE\n"
+ "All UN addresses\n"
+ "UN IPv4 interface address\n"
+ "UN IPv6 interface address\n"
+ "Clear prefix registration information\n"
+ "All prefixes\n"
+ "IPv4 Prefix\n"
+ "IPv6 Prefix\n")
+{
+ struct rfapi_local_reg_delete_arg cda;
+ int rc;
+
+ /* pfx vn un L2 VNI */
+ if ((rc = parse_deleter_tokens(vty, NULL, argv[9], NULL, argv[7],
+ argv[3], argv[5], NULL, NULL, &cda)))
+ return rc;
+ cda.vty = vty;
+ clear_vnc_prefix(&cda);
+ print_cleared_stats(&cda);
+ return 0;
+}
+
+DEFUN (clear_vnc_mac_vn_prefix,
+ clear_vnc_mac_vn_prefix_cmd,
+ "clear vnc mac <*|X:X:X:X:X:X> virtual-network-identifier <*|(1-4294967295)> vn <*|A.B.C.D|X:X::X:X> prefix <*|A.B.C.D/M|X:X::X:X/M>",
+ "clear\n"
+ "VNC Information\n"
+ "Clear mac registration information\n"
+ "All macs\n"
+ "MAC address\n"
+ "VNI keyword\n"
+ "Any virtual network identifier\n"
+ "Virtual network identifier\n"
+ "UN address of NVE\n"
+ "All VN addresses\n"
+ "VN IPv4 interface address\n"
+ "VN IPv6 interface address\n"
+ "Clear prefix registration information\n"
+ "All prefixes\n"
+ "IPv4 Prefix\n"
+ "IPv6 Prefix\n")
+{
+ struct rfapi_local_reg_delete_arg cda;
+ int rc;
+
+ /* pfx vn un L2 VNI */
+ if ((rc = parse_deleter_tokens(vty, NULL, argv[9], argv[7], NULL,
+ argv[3], argv[5], NULL, NULL, &cda)))
+ return rc;
+ cda.vty = vty;
+ clear_vnc_prefix(&cda);
+ print_cleared_stats(&cda);
+ return 0;
+}
+
+DEFUN (clear_vnc_mac_all_prefix,
+ clear_vnc_mac_all_prefix_cmd,
+ "clear vnc mac <*|X:X:X:X:X:X> virtual-network-identifier <*|(1-4294967295)> prefix <*|A.B.C.D/M|X:X::X:X/M>",
+ "clear\n"
+ "VNC Information\n"
+ "Clear mac registration information\n"
+ "All macs\n"
+ "MAC address\n"
+ "VNI keyword\n"
+ "Any virtual network identifier\n"
+ "Virtual network identifier\n"
+ "UN address of NVE\n"
+ "All VN addresses\n"
+ "VN IPv4 interface address\n"
+ "VN IPv6 interface address\n")
+{
+ struct rfapi_local_reg_delete_arg cda;
+ int rc;
+
+ /* pfx vn un L2 VNI */
+ if ((rc = parse_deleter_tokens(vty, NULL, argv[7], NULL, NULL, argv[3],
+ argv[5], NULL, NULL, &cda)))
+ return rc;
+ cda.vty = vty;
+ clear_vnc_prefix(&cda);
+ print_cleared_stats(&cda);
+ return 0;
+}
+
+/************************************************************************
+ * Show commands
+ ************************************************************************/
+
+
+/* copied from rfp_vty.c */
+static int check_and_display_is_vnc_running(struct vty *vty)
+{
+ if (bgp_rfapi_is_vnc_configured(NULL) == 0)
+ return 1; /* is running */
+
+ if (vty) {
+ vty_out(vty, "VNC is not configured.\n");
+ }
+ return 0; /* not running */
+}
+
+static int rfapi_vty_show_nve_summary(struct vty *vty,
+ show_nve_summary_t show_type)
+{
+ struct bgp *bgp_default = bgp_get_default();
+ struct rfapi *h;
+ int is_vnc_running = (bgp_rfapi_is_vnc_configured(bgp_default) == 0);
+
+ int active_local_routes;
+ int active_remote_routes;
+ int holddown_remote_routes;
+ int imported_remote_routes;
+
+ if (!bgp_default)
+ goto notcfg;
+
+ h = bgp_default->rfapi;
+
+ if (!h)
+ goto notcfg;
+
+ /* don't show local info if not running RFP */
+ if (is_vnc_running || show_type == SHOW_NVE_SUMMARY_REGISTERED) {
+
+ switch (show_type) {
+
+ case SHOW_NVE_SUMMARY_ACTIVE_NVES:
+ vty_out(vty, "%-24s ", "NVEs:");
+ vty_out(vty, "%-8s %-8u ",
+ "Active:", h->descriptors.count);
+ vty_out(vty, "%-8s %-8u ",
+ "Maximum:", h->stat.max_descriptors);
+ vty_out(vty, "%-8s %-8u",
+ "Unknown:", h->stat.count_unknown_nves);
+ break;
+
+ case SHOW_NVE_SUMMARY_REGISTERED:
+ /*
+ * NB: With the introduction of L2 route support, we no
+ * longer have a one-to-one correspondence between
+ * locally-originated route advertisements and routes in
+ * the import tables that have local origin. This
+ * discrepancy arises because a single advertisement
+ * may contain both an IP prefix and a MAC address.
+ * Such an advertisement results in two import table
+ * entries: one indexed by IP prefix, the other indexed
+ * by MAC address.
+ *
+ * TBD: update computation and display of registration
+ * statistics to reflect the underlying semantics.
+ */
+ if (is_vnc_running) {
+ vty_out(vty, "%-24s ", "Registrations:");
+ vty_out(vty, "%-8s %-8u ", "Active:",
+ rfapiApCountAll(bgp_default));
+ vty_out(vty, "%-8s %-8u ", "Failed:",
+ h->stat.count_registrations_failed);
+ vty_out(vty, "%-8s %-8u",
+ "Total:", h->stat.count_registrations);
+ vty_out(vty, "\n");
+ }
+ vty_out(vty, "%-24s ", "Prefixes registered:");
+ vty_out(vty, "\n");
+
+ rfapiCountAllItRoutes(&active_local_routes,
+ &active_remote_routes,
+ &holddown_remote_routes,
+ &imported_remote_routes);
+
+ /* local */
+ if (is_vnc_running) {
+ vty_out(vty, " %-20s ", "Locally:");
+ vty_out(vty, "%-8s %-8u ",
+ "Active:", active_local_routes);
+ vty_out(vty, "\n");
+ }
+
+
+ vty_out(vty, " %-20s ", "Remotely:");
+ vty_out(vty, "%-8s %-8u",
+ "Active:", active_remote_routes);
+ vty_out(vty, "\n");
+ vty_out(vty, " %-20s ", "In Holddown:");
+ vty_out(vty, "%-8s %-8u",
+ "Active:", holddown_remote_routes);
+ vty_out(vty, "\n");
+ vty_out(vty, " %-20s ", "Imported:");
+ vty_out(vty, "%-8s %-8u",
+ "Active:", imported_remote_routes);
+ break;
+
+ case SHOW_NVE_SUMMARY_QUERIES:
+ vty_out(vty, "%-24s ", "Queries:");
+ vty_out(vty, "%-8s %-8u ",
+ "Active:", rfapi_monitor_count(NULL));
+ vty_out(vty, "%-8s %-8u ",
+ "Failed:", h->stat.count_queries_failed);
+ vty_out(vty, "%-8s %-8u",
+ "Total:", h->stat.count_queries);
+ break;
+
+ case SHOW_NVE_SUMMARY_RESPONSES:
+ rfapiRibShowResponsesSummary(vty);
+
+ default:
+ break;
+ }
+ vty_out(vty, "\n");
+ }
+ return 0;
+
+notcfg:
+ vty_out(vty, "VNC is not configured.\n");
+ return CMD_WARNING;
+}
+
+static int rfapi_show_nves(struct vty *vty, struct prefix *vn_prefix,
+ struct prefix *un_prefix)
+{
+ // struct hash *rfds;
+ // struct rfp_rfapi_descriptor_param param;
+
+ struct bgp *bgp_default = bgp_get_default();
+ struct rfapi *h;
+ struct listnode *node;
+ struct rfapi_descriptor *rfd;
+
+ int total = 0;
+ int printed = 0;
+ int rc;
+
+ if (!bgp_default)
+ goto notcfg;
+
+ h = bgp_default->rfapi;
+
+ if (!h)
+ goto notcfg;
+
+ rc = rfapi_vty_show_nve_summary(vty, SHOW_NVE_SUMMARY_ACTIVE_NVES);
+ if (rc)
+ return rc;
+
+ for (ALL_LIST_ELEMENTS_RO(&h->descriptors, node, rfd)) {
+ struct prefix pfx;
+ char vn_addr_buf[INET6_ADDRSTRLEN] = {
+ 0,
+ };
+ char un_addr_buf[INET6_ADDRSTRLEN] = {
+ 0,
+ };
+ char age[10];
+
+ ++total;
+
+ if (vn_prefix) {
+ assert(!rfapiRaddr2Qprefix(&rfd->vn_addr, &pfx));
+ if (!prefix_match(vn_prefix, &pfx))
+ continue;
+ }
+
+ if (un_prefix) {
+ assert(!rfapiRaddr2Qprefix(&rfd->un_addr, &pfx));
+ if (!prefix_match(un_prefix, &pfx))
+ continue;
+ }
+
+ rfapiRfapiIpAddr2Str(&rfd->vn_addr, vn_addr_buf,
+ INET6_ADDRSTRLEN);
+ rfapiRfapiIpAddr2Str(&rfd->un_addr, un_addr_buf,
+ INET6_ADDRSTRLEN);
+
+ if (!printed) {
+ /* print out a header */
+ vty_out(vty,
+ " Active Next Hops\n");
+ vty_out(vty, "%-15s %-15s %-5s %-5s %-6s %-6s %s\n",
+ "VN Address", "UN Address", "Regis", "Resps",
+ "Reach", "Remove", "Age");
+ }
+
+ ++printed;
+
+ vty_out(vty, "%-15s %-15s %-5u %-5u %-6u %-6u %s\n",
+ vn_addr_buf, un_addr_buf, rfapiApCount(rfd),
+ rfapi_monitor_count(rfd), rfd->stat_count_nh_reachable,
+ rfd->stat_count_nh_removal,
+ rfapiFormatAge(rfd->open_time, age, 10));
+ }
+
+ if (printed > 0 || vn_prefix || un_prefix)
+ vty_out(vty, "Displayed %d out of %d active NVEs\n", printed,
+ total);
+
+ return 0;
+
+notcfg:
+ vty_out(vty, "VNC is not configured.\n");
+ return CMD_WARNING;
+}
+
+
+DEFUN (vnc_show_summary,
+ vnc_show_summary_cmd,
+ "show vnc summary",
+ SHOW_STR
+ VNC_SHOW_STR
+ "Display VNC status summary\n")
+{
+ if (!check_and_display_is_vnc_running(vty))
+ return CMD_SUCCESS;
+ bgp_rfapi_show_summary(bgp_get_default(), vty);
+ vty_out(vty, "\n");
+ rfapi_vty_show_nve_summary(vty, SHOW_NVE_SUMMARY_ACTIVE_NVES);
+ rfapi_vty_show_nve_summary(vty, SHOW_NVE_SUMMARY_QUERIES);
+ rfapi_vty_show_nve_summary(vty, SHOW_NVE_SUMMARY_RESPONSES);
+ rfapi_vty_show_nve_summary(vty, SHOW_NVE_SUMMARY_REGISTERED);
+ return CMD_SUCCESS;
+}
+
+DEFUN (vnc_show_nves,
+ vnc_show_nves_cmd,
+ "show vnc nves",
+ SHOW_STR
+ VNC_SHOW_STR
+ "List known NVEs\n")
+{
+ rfapi_show_nves(vty, NULL, NULL);
+ return CMD_SUCCESS;
+}
+
+DEFUN (vnc_show_nves_ptct,
+ vnc_show_nves_ptct_cmd,
+ "show vnc nves <vn|un> <A.B.C.D|X:X::X:X>",
+ SHOW_STR
+ VNC_SHOW_STR
+ "List known NVEs\n"
+ "VN address of NVE\n"
+ "UN address of NVE\n"
+ "IPv4 interface address\n"
+ "IPv6 interface address\n")
+{
+ struct prefix pfx;
+
+ if (!check_and_display_is_vnc_running(vty))
+ return CMD_SUCCESS;
+
+ if (!str2prefix(argv[4]->arg, &pfx)) {
+ vty_out(vty, "Malformed address \"%s\"\n", argv[4]->arg);
+ return CMD_WARNING;
+ }
+ if (pfx.family != AF_INET && pfx.family != AF_INET6) {
+ vty_out(vty, "Invalid address \"%s\"\n", argv[4]->arg);
+ return CMD_WARNING;
+ }
+
+ if (argv[3]->arg[0] == 'u') {
+ rfapi_show_nves(vty, NULL, &pfx);
+ } else {
+ rfapi_show_nves(vty, &pfx, NULL);
+ }
+
+ return CMD_SUCCESS;
+}
+
+/* adapted from rfp_registration_cache_log() */
+static void rfapi_show_registrations(struct vty *vty,
+ struct prefix *restrict_to, int show_local,
+ int show_remote, int show_holddown,
+ int show_imported)
+{
+ int printed = 0;
+
+ if (!vty)
+ return;
+
+ rfapi_vty_show_nve_summary(vty, SHOW_NVE_SUMMARY_REGISTERED);
+
+ if (show_local) {
+ /* non-expiring, local */
+ printed += rfapiShowRemoteRegistrations(vty, restrict_to, 0, 1,
+ 0, 0);
+ }
+ if (show_remote) {
+ /* non-expiring, non-local */
+ printed += rfapiShowRemoteRegistrations(vty, restrict_to, 0, 0,
+ 1, 0);
+ }
+ if (show_holddown) {
+ /* expiring, including local */
+ printed += rfapiShowRemoteRegistrations(vty, restrict_to, 1, 1,
+ 1, 0);
+ }
+ if (show_imported) {
+ /* non-expiring, non-local */
+ printed += rfapiShowRemoteRegistrations(vty, restrict_to, 0, 0,
+ 1, 1);
+ }
+ if (!printed) {
+ vty_out(vty, "\n");
+ }
+}
+
+DEFUN (vnc_show_registrations_pfx,
+ vnc_show_registrations_pfx_cmd,
+ "show vnc registrations [<A.B.C.D|A.B.C.D/M|X:X::X:X|X:X::X:X/M|X:X:X:X:X:X>]",
+ SHOW_STR
+ VNC_SHOW_STR
+ "List active prefix registrations\n"
+ "Limit output to a particualr IPV4 address\n"
+ "Limit output to a particular IPv4 prefix\n"
+ "Limit output to a particualr IPV6 address\n"
+ "Limit output to a particular IPv6 prefix\n"
+ "Limit output to a particular MAC address\n")
+{
+ struct prefix p;
+ struct prefix *p_addr = NULL;
+
+ if (argc > 3) {
+ if (!str2prefix(argv[3]->arg, &p)) {
+ vty_out(vty, "Invalid prefix: %s\n", argv[3]->arg);
+ return CMD_SUCCESS;
+ } else {
+ p_addr = &p;
+ }
+ }
+
+ rfapi_show_registrations(vty, p_addr, 1, 1, 1, 1);
+ return CMD_SUCCESS;
+}
+
+DEFUN (vnc_show_registrations_some_pfx,
+ vnc_show_registrations_some_pfx_cmd,
+ "show vnc registrations <all|holddown|imported|local|remote> [<A.B.C.D|A.B.C.D/M|X:X::X:X|X:X::X:X/M|X:X:X:X:X:X>]",
+ SHOW_STR
+ VNC_SHOW_STR
+ "List active prefix registrations\n"
+ "show all registrations\n"
+ "show only registrations in holddown\n"
+ "show only imported prefixes\n"
+ "show only local registrations\n"
+ "show only remote registrations\n"
+ "Limit output to a particualr IPV4 address\n"
+ "Limit output to a particular IPv4 prefix\n"
+ "Limit output to a particualr IPV6 address\n"
+ "Limit output to a particular IPv6 prefix\n"
+ "Limit output to a particular MAC address\n")
+{
+ struct prefix p;
+ struct prefix *p_addr = NULL;
+
+ int show_local = 0;
+ int show_remote = 0;
+ int show_holddown = 0;
+ int show_imported = 0;
+
+ if (argc > 4) {
+ if (!str2prefix(argv[4]->arg, &p)) {
+ vty_out(vty, "Invalid prefix: %s\n", argv[4]->arg);
+ return CMD_SUCCESS;
+ } else {
+ p_addr = &p;
+ }
+ }
+ switch (argv[3]->arg[0]) {
+ case 'a':
+ show_local = 1;
+ show_remote = 1;
+ show_holddown = 1;
+ show_imported = 1;
+ break;
+
+ case 'h':
+ show_holddown = 1;
+ break;
+
+ case 'i':
+ show_imported = 1;
+ break;
+
+ case 'l':
+ show_local = 1;
+ break;
+
+ case 'r':
+ show_remote = 1;
+ break;
+ }
+
+ rfapi_show_registrations(vty, p_addr, show_local, show_remote,
+ show_holddown, show_imported);
+ return CMD_SUCCESS;
+}
+
+DEFUN (vnc_show_responses_pfx,
+ vnc_show_responses_pfx_cmd,
+ "show vnc responses [<A.B.C.D|A.B.C.D/M|X:X::X:X|X:X::X:X/M|X:X:X:X:X:X>]",
+ SHOW_STR
+ VNC_SHOW_STR
+ "List recent query responses\n"
+ "Limit output to a particualr IPV4 address\n"
+ "Limit output to a particular IPv4 prefix\n"
+ "Limit output to a particualr IPV6 address\n"
+ "Limit output to a particular IPv6 prefix\n"
+ "Limit output to a particular MAC address\n" )
+{
+ struct prefix p;
+ struct prefix *p_addr = NULL;
+
+ if (argc > 3) {
+ if (!str2prefix(argv[3]->arg, &p)) {
+ vty_out(vty, "Invalid prefix: %s\n", argv[3]->arg);
+ return CMD_SUCCESS;
+ } else {
+ p_addr = &p;
+ }
+ }
+ rfapi_vty_show_nve_summary(vty, SHOW_NVE_SUMMARY_QUERIES);
+
+ rfapiRibShowResponsesSummary(vty);
+
+ rfapiRibShowResponses(vty, p_addr, 0);
+ rfapiRibShowResponses(vty, p_addr, 1);
+
+ return CMD_SUCCESS;
+}
+
+DEFUN (vnc_show_responses_some_pfx,
+ vnc_show_responses_some_pfx_cmd,
+ "show vnc responses <active|removed> [<A.B.C.D|A.B.C.D/M|X:X::X:X|X:X::X:X/M|X:X:X:X:X:X>]",
+ SHOW_STR
+ VNC_SHOW_STR
+ "List recent query responses\n"
+ "show only active query responses\n"
+ "show only removed query responses\n"
+ "Limit output to a particualr IPV4 address\n"
+ "Limit output to a particular IPv4 prefix\n"
+ "Limit output to a particualr IPV6 address\n"
+ "Limit output to a particular IPv6 prefix\n"
+ "Limit output to a particular MAC address\n")
+{
+ struct prefix p;
+ struct prefix *p_addr = NULL;
+
+ int show_active = 0;
+ int show_removed = 0;
+
+ if (!check_and_display_is_vnc_running(vty))
+ return CMD_SUCCESS;
+
+ if (argc > 4) {
+ if (!str2prefix(argv[4]->arg, &p)) {
+ vty_out(vty, "Invalid prefix: %s\n", argv[4]->arg);
+ return CMD_SUCCESS;
+ } else {
+ p_addr = &p;
+ }
+ }
+
+ switch (argv[3]->arg[0]) {
+ case 'a':
+ show_active = 1;
+ break;
+
+ case 'r':
+ show_removed = 1;
+ break;
+ }
+
+ rfapi_vty_show_nve_summary(vty, SHOW_NVE_SUMMARY_QUERIES);
+
+ rfapiRibShowResponsesSummary(vty);
+
+ if (show_active)
+ rfapiRibShowResponses(vty, p_addr, 0);
+ if (show_removed)
+ rfapiRibShowResponses(vty, p_addr, 1);
+
+ return CMD_SUCCESS;
+}
+
+DEFUN (show_vnc_queries_pfx,
+ show_vnc_queries_pfx_cmd,
+ "show vnc queries [<A.B.C.D|A.B.C.D/M|X:X::X:X|X:X::X:X/M|X:X:X:X:X:X>]",
+ SHOW_STR
+ VNC_SHOW_STR
+ "List active queries\n"
+ "Limit output to a particualr IPV4 address\n"
+ "Limit output to a particular IPv4 prefix\n"
+ "Limit output to a particualr IPV6 address\n"
+ "Limit output to a particular IPv6 prefix\n"
+ "Limit output to a particualr MAC address\n")
+{
+ struct prefix pfx;
+ struct prefix *p = NULL;
+
+ if (argc > 3) {
+ if (!str2prefix(argv[3]->arg, &pfx)) {
+ vty_out(vty, "Invalid prefix: %s\n", argv[3]->arg);
+ return CMD_WARNING;
+ }
+ p = &pfx;
+ }
+
+ rfapi_vty_show_nve_summary(vty, SHOW_NVE_SUMMARY_QUERIES);
+
+ return rfapiShowVncQueries(vty, p);
+}
+
+DEFUN (vnc_clear_counters,
+ vnc_clear_counters_cmd,
+ "clear vnc counters",
+ CLEAR_STR
+ VNC_SHOW_STR
+ "Reset VNC counters\n")
+{
+ struct bgp *bgp_default = bgp_get_default();
+ struct rfapi *h;
+ struct listnode *node;
+ struct rfapi_descriptor *rfd;
+
+ if (!bgp_default)
+ goto notcfg;
+
+ h = bgp_default->rfapi;
+
+ if (!h)
+ goto notcfg;
+
+ /* per-rfd */
+ for (ALL_LIST_ELEMENTS_RO(&h->descriptors, node, rfd)) {
+ rfd->stat_count_nh_reachable = 0;
+ rfd->stat_count_nh_removal = 0;
+ }
+
+ /* global */
+ memset(&h->stat, 0, sizeof(h->stat));
+
+ /*
+ * 151122 per bug 103, set count_registrations = number active.
+ * Do same for queries
+ */
+ h->stat.count_registrations = rfapiApCountAll(bgp_default);
+ h->stat.count_queries = rfapi_monitor_count(NULL);
+
+ rfapiRibShowResponsesSummaryClear();
+
+ return CMD_SUCCESS;
+
+notcfg:
+ vty_out(vty, "VNC is not configured.\n");
+ return CMD_WARNING;
+}
+
+/************************************************************************
+ * Add prefix with vrf
+ *
+ * add [vrf <vrf-name>] prefix <prefix>
+ * [rd <value>] [label <value>] [local-preference <0-4294967295>]
+ ************************************************************************/
+void vnc_add_vrf_opener(struct bgp *bgp, struct rfapi_nve_group_cfg *rfg)
+{
+ if (rfg->rfd == NULL) { /* need new rfapi_handle */
+ /* based on rfapi_open */
+ struct rfapi_descriptor *rfd;
+
+ rfd = XCALLOC(MTYPE_RFAPI_DESC,
+ sizeof(struct rfapi_descriptor));
+ rfd->bgp = bgp;
+ rfg->rfd = rfd;
+ /* leave most fields empty as will get from (dynamic) config
+ * when needed */
+ rfd->default_tunneltype_option.type = BGP_ENCAP_TYPE_MPLS;
+ rfd->cookie = rfg;
+ if (rfg->vn_prefix.family
+ && !CHECK_FLAG(rfg->flags, RFAPI_RFG_VPN_NH_SELF)) {
+ rfapiQprefix2Raddr(&rfg->vn_prefix, &rfd->vn_addr);
+ } else {
+ memset(&rfd->vn_addr, 0, sizeof(struct rfapi_ip_addr));
+ rfd->vn_addr.addr_family = AF_INET;
+ rfd->vn_addr.addr.v4 = bgp->router_id;
+ }
+ rfd->un_addr = rfd->vn_addr; /* sigh, need something in UN for
+ lookups */
+ vnc_zlog_debug_verbose("%s: Opening RFD for VRF %s", __func__,
+ rfg->name);
+ rfapi_init_and_open(bgp, rfd, rfg);
+ }
+}
+
+/* NOTE: this functions parallels vnc_direct_add_rn_group_rd */
+static int vnc_add_vrf_prefix(struct vty *vty, const char *arg_vrf,
+ const char *arg_prefix,
+ const char *arg_rd, /* optional */
+ const char *arg_label, /* optional */
+ const char *arg_pref) /* optional */
+{
+ struct bgp *bgp;
+ struct rfapi_nve_group_cfg *rfg;
+ struct prefix pfx;
+ struct rfapi_ip_prefix rpfx;
+ uint32_t pref = 0;
+ struct rfapi_vn_option optary[3];
+ struct rfapi_vn_option *opt = NULL;
+ int cur_opt = 0;
+
+ bgp = bgp_get_default(); /* assume main instance for now */
+ if (!bgp) {
+ vty_out(vty, "No BGP process is configured\n");
+ return CMD_WARNING_CONFIG_FAILED;
+ }
+ if (!bgp->rfapi || !bgp->rfapi_cfg) {
+ vty_out(vty, "VRF support not configured\n");
+ return CMD_WARNING_CONFIG_FAILED;
+ }
+
+ rfg = bgp_rfapi_cfg_match_byname(bgp, arg_vrf, RFAPI_GROUP_CFG_VRF);
+ /* arg checks */
+ if (!rfg) {
+ vty_out(vty, "VRF \"%s\" appears not to be configured.\n",
+ arg_vrf);
+ return CMD_WARNING_CONFIG_FAILED;
+ }
+ if (!rfg->rt_export_list || !rfg->rfapi_import_table) {
+ vty_out(vty,
+ "VRF \"%s\" is missing RT import/export RT configuration.\n",
+ arg_vrf);
+ return CMD_WARNING_CONFIG_FAILED;
+ }
+ if (!rfg->rd.prefixlen && !arg_rd) {
+ vty_out(vty,
+ "VRF \"%s\" isn't configured with an RD, so RD must be provided.\n",
+ arg_vrf);
+ return CMD_WARNING_CONFIG_FAILED;
+ }
+ if (rfg->label > MPLS_LABEL_MAX && !arg_label) {
+ vty_out(vty,
+ "VRF \"%s\" isn't configured with a default labels, so a label must be provided.\n",
+ arg_vrf);
+ return CMD_WARNING_CONFIG_FAILED;
+ }
+ if (!str2prefix(arg_prefix, &pfx)) {
+ vty_out(vty, "Malformed prefix \"%s\"\n", arg_prefix);
+ return CMD_WARNING_CONFIG_FAILED;
+ }
+ rfapiQprefix2Rprefix(&pfx, &rpfx);
+ memset(optary, 0, sizeof(optary));
+ if (arg_rd) {
+ opt = &optary[cur_opt++];
+ opt->type = RFAPI_VN_OPTION_TYPE_INTERNAL_RD;
+ if (!str2prefix_rd(arg_rd, &opt->v.internal_rd)) {
+ vty_out(vty, "Malformed RD \"%s\"\n", arg_rd);
+ return CMD_WARNING_CONFIG_FAILED;
+ }
+ }
+ if (rfg->label <= MPLS_LABEL_MAX || arg_label) {
+ struct rfapi_l2address_option *l2o;
+ if (opt != NULL)
+ opt->next = &optary[cur_opt];
+ opt = &optary[cur_opt++];
+ opt->type = RFAPI_VN_OPTION_TYPE_L2ADDR;
+ l2o = &opt->v.l2addr;
+ if (arg_label) {
+ int32_t label;
+ label = strtoul(arg_label, NULL, 10);
+ l2o->label = label;
+ } else
+ l2o->label = rfg->label;
+ }
+ if (arg_pref) {
+ char *endptr = NULL;
+ pref = strtoul(arg_pref, &endptr, 10);
+ if (*endptr != '\0') {
+ vty_out(vty,
+ "%% Invalid local-preference value \"%s\"\n",
+ arg_pref);
+ return CMD_WARNING_CONFIG_FAILED;
+ }
+ }
+ rpfx.cost = 255 - (pref & 255);
+ vnc_add_vrf_opener(bgp, rfg);
+
+ if (!rfapi_register(rfg->rfd, &rpfx, RFAPI_INFINITE_LIFETIME, NULL,
+ (cur_opt ? optary : NULL), RFAPI_REGISTER_ADD)) {
+ struct rfapi_next_hop_entry *head = NULL;
+ struct rfapi_next_hop_entry *tail = NULL;
+ struct rfapi_vn_option *vn_opt_new;
+
+ vnc_zlog_debug_verbose("%s: rfapi_register succeeded",
+ __func__);
+
+ if (bgp->rfapi->rfp_methods.local_cb) {
+ struct rfapi_descriptor *r =
+ (struct rfapi_descriptor *)rfg->rfd;
+ vn_opt_new = rfapi_vn_options_dup(opt);
+
+ rfapiAddDeleteLocalRfpPrefix(&r->un_addr, &r->vn_addr,
+ &rpfx, 1,
+ RFAPI_INFINITE_LIFETIME,
+ vn_opt_new, &head, &tail);
+ if (head) {
+ bgp->rfapi->flags |= RFAPI_INCALLBACK;
+ (*bgp->rfapi->rfp_methods.local_cb)(head,
+ r->cookie);
+ bgp->rfapi->flags &= ~RFAPI_INCALLBACK;
+ }
+ head = tail = NULL;
+ }
+ vnc_zlog_debug_verbose(
+ "%s completed, count=%d/%d", __func__,
+ rfg->rfapi_import_table->local_count[AFI_IP],
+ rfg->rfapi_import_table->local_count[AFI_IP6]);
+ return CMD_SUCCESS;
+ }
+
+ vnc_zlog_debug_verbose("%s: rfapi_register failed", __func__);
+ vty_out(vty, "Add failed.\n");
+ return CMD_WARNING_CONFIG_FAILED;
+}
+
+DEFUN (add_vrf_prefix_rd_label_pref,
+ add_vrf_prefix_rd_label_pref_cmd,
+ "add vrf NAME prefix <A.B.C.D/M|X:X::X:X/M> [{rd ASN:NN_OR_IP-ADDRESS|label (0-1048575)|preference (0-4294967295)}]",
+ "Add\n"
+ "To a VRF\n"
+ "VRF name\n"
+ "Add/modify prefix related information\n"
+ "IPv4 prefix\n"
+ "IPv6 prefix\n"
+ "Override configured VRF Route Distinguisher\n"
+ "<as-number>:<number> or <ip-address>:<number>\n"
+ "Override configured VRF label\n"
+ "Label Value <0-1048575>\n"
+ "Set advertised local preference\n"
+ "local preference (higher=more preferred)\n")
+{
+ char *arg_vrf = argv[2]->arg;
+ char *arg_prefix = argv[4]->arg;
+ char *arg_rd = NULL; /* optional */
+ char *arg_label = NULL; /* optional */
+ char *arg_pref = NULL; /* optional */
+ int pargc = 5;
+ argc--; /* don't parse argument */
+ while (pargc < argc) {
+ switch (argv[pargc++]->arg[0]) {
+ case 'r':
+ arg_rd = argv[pargc]->arg;
+ break;
+ case 'l':
+ arg_label = argv[pargc]->arg;
+ break;
+ case 'p':
+ arg_pref = argv[pargc]->arg;
+ break;
+ default:
+ break;
+ }
+ pargc++;
+ }
+
+ return vnc_add_vrf_prefix(vty, arg_vrf, arg_prefix, arg_rd, arg_label,
+ arg_pref);
+}
+
+/************************************************************************
+ * del prefix with vrf
+ *
+ * clear [vrf <vrf-name>] prefix <prefix> [rd <value>]
+ ************************************************************************/
+static int rfapi_cfg_group_it_count(struct rfapi_nve_group_cfg *rfg)
+{
+ int count = 0;
+
+ if (rfg->rfapi_import_table == NULL)
+ return 0;
+
+ afi_t afi = AFI_MAX;
+ while (afi-- > 0) {
+ count += rfg->rfapi_import_table->local_count[afi];
+ }
+ return count;
+}
+
+void clear_vnc_vrf_closer(struct rfapi_nve_group_cfg *rfg)
+{
+ struct rfapi_descriptor *rfd = rfg->rfd;
+ afi_t afi;
+
+ if (rfd == NULL)
+ return;
+ /* check if IT is empty */
+ for (afi = 0;
+ afi < AFI_MAX && rfg->rfapi_import_table->local_count[afi] == 0;
+ afi++)
+ ;
+
+ if (afi == AFI_MAX) {
+ vnc_zlog_debug_verbose("%s: closing RFD for VRF %s", __func__,
+ rfg->name);
+ rfg->rfd = NULL;
+ rfapi_close(rfd);
+ } else {
+ vnc_zlog_debug_verbose(
+ "%s: VRF %s afi=%d count=%d", __func__, rfg->name, afi,
+ rfg->rfapi_import_table->local_count[afi]);
+ }
+}
+
+static int vnc_clear_vrf(struct vty *vty, struct bgp *bgp, const char *arg_vrf,
+ const char *arg_prefix, /* NULL = all */
+ const char *arg_rd) /* optional */
+{
+ struct rfapi_nve_group_cfg *rfg;
+ struct rfapi_local_reg_delete_arg cda;
+ int rc;
+ int start_count;
+
+ if (bgp == NULL)
+ bgp = bgp_get_default(); /* assume main instance for now */
+ if (!bgp) {
+ vty_out(vty, "No BGP process is configured\n");
+ return CMD_WARNING;
+ }
+ if (!bgp->rfapi || !bgp->rfapi_cfg) {
+ vty_out(vty, "VRF support not configured\n");
+ return CMD_WARNING;
+ }
+ rfg = bgp_rfapi_cfg_match_byname(bgp, arg_vrf, RFAPI_GROUP_CFG_VRF);
+ /* arg checks */
+ if (!rfg) {
+ vty_out(vty, "VRF \"%s\" appears not to be configured.\n",
+ arg_vrf);
+ return CMD_WARNING;
+ }
+ rc = parse_deleter_args(vty, bgp, arg_prefix, NULL, NULL, NULL, NULL,
+ arg_rd, rfg, &cda);
+ if (rc != CMD_SUCCESS) /* parse error */
+ return rc;
+
+ start_count = rfapi_cfg_group_it_count(rfg);
+ clear_vnc_prefix(&cda);
+ vty_out(vty, "Cleared %u out of %d prefixes.\n", cda.pfx_count,
+ start_count);
+ return CMD_SUCCESS;
+}
+
+DEFUN (clear_vrf_prefix_rd,
+ clear_vrf_prefix_rd_cmd,
+ "clear vrf NAME [prefix <A.B.C.D/M|X:X::X:X/M>] [rd ASN:NN_OR_IP-ADDRESS]",
+ "Clear stored data\n"
+ "From a VRF\n"
+ "VRF name\n"
+ "Prefix related information\n"
+ "IPv4 prefix\n"
+ "IPv6 prefix\n"
+ "Specific VRF Route Distinguisher\n"
+ "<as-number>:<number> or <ip-address>:<number>\n")
+{
+ char *arg_vrf = argv[2]->arg;
+ char *arg_prefix = NULL; /* optional */
+ char *arg_rd = NULL; /* optional */
+ int pargc = 3;
+ argc--; /* don't check parameter */
+ while (pargc < argc) {
+ switch (argv[pargc++]->arg[0]) {
+ case 'r':
+ arg_rd = argv[pargc]->arg;
+ break;
+ case 'p':
+ arg_prefix = argv[pargc]->arg;
+ break;
+ default:
+ break;
+ }
+ pargc++;
+ }
+ return vnc_clear_vrf(vty, NULL, arg_vrf, arg_prefix, arg_rd);
+}
+
+DEFUN (clear_vrf_all,
+ clear_vrf_all_cmd,
+ "clear vrf NAME all",
+ "Clear stored data\n"
+ "From a VRF\n"
+ "VRF name\n"
+ "All prefixes\n")
+{
+ char *arg_vrf = argv[2]->arg;
+ return vnc_clear_vrf(vty, NULL, arg_vrf, NULL, NULL);
+}
+
+void rfapi_vty_init(void)
+{
+ install_element(ENABLE_NODE, &add_vnc_prefix_cost_life_lnh_cmd);
+ install_element(ENABLE_NODE, &add_vnc_prefix_life_cost_lnh_cmd);
+ install_element(ENABLE_NODE, &add_vnc_prefix_cost_lnh_cmd);
+ install_element(ENABLE_NODE, &add_vnc_prefix_life_lnh_cmd);
+ install_element(ENABLE_NODE, &add_vnc_prefix_lnh_cmd);
+
+ install_element(ENABLE_NODE, &add_vnc_prefix_cost_life_cmd);
+ install_element(ENABLE_NODE, &add_vnc_prefix_life_cost_cmd);
+ install_element(ENABLE_NODE, &add_vnc_prefix_cost_cmd);
+ install_element(ENABLE_NODE, &add_vnc_prefix_life_cmd);
+ install_element(ENABLE_NODE, &add_vnc_prefix_cmd);
+
+ install_element(ENABLE_NODE, &add_vnc_mac_vni_prefix_cost_life_cmd);
+ install_element(ENABLE_NODE, &add_vnc_mac_vni_prefix_life_cmd);
+ install_element(ENABLE_NODE, &add_vnc_mac_vni_prefix_cost_cmd);
+ install_element(ENABLE_NODE, &add_vnc_mac_vni_prefix_cmd);
+ install_element(ENABLE_NODE, &add_vnc_mac_vni_cost_life_cmd);
+ install_element(ENABLE_NODE, &add_vnc_mac_vni_cost_cmd);
+ install_element(ENABLE_NODE, &add_vnc_mac_vni_life_cmd);
+ install_element(ENABLE_NODE, &add_vnc_mac_vni_cmd);
+
+ install_element(ENABLE_NODE, &add_vrf_prefix_rd_label_pref_cmd);
+
+ install_element(ENABLE_NODE, &clear_vnc_nve_all_cmd);
+ install_element(ENABLE_NODE, &clear_vnc_nve_vn_un_cmd);
+ install_element(ENABLE_NODE, &clear_vnc_nve_un_vn_cmd);
+ install_element(ENABLE_NODE, &clear_vnc_nve_vn_cmd);
+ install_element(ENABLE_NODE, &clear_vnc_nve_un_cmd);
+
+ install_element(ENABLE_NODE, &clear_vnc_prefix_vn_un_cmd);
+ install_element(ENABLE_NODE, &clear_vnc_prefix_un_vn_cmd);
+ install_element(ENABLE_NODE, &clear_vnc_prefix_un_cmd);
+ install_element(ENABLE_NODE, &clear_vnc_prefix_vn_cmd);
+ install_element(ENABLE_NODE, &clear_vnc_prefix_all_cmd);
+
+ install_element(ENABLE_NODE, &clear_vnc_mac_vn_un_cmd);
+ install_element(ENABLE_NODE, &clear_vnc_mac_un_vn_cmd);
+ install_element(ENABLE_NODE, &clear_vnc_mac_un_cmd);
+ install_element(ENABLE_NODE, &clear_vnc_mac_vn_cmd);
+ install_element(ENABLE_NODE, &clear_vnc_mac_all_cmd);
+
+ install_element(ENABLE_NODE, &clear_vnc_mac_vn_un_prefix_cmd);
+ install_element(ENABLE_NODE, &clear_vnc_mac_un_vn_prefix_cmd);
+ install_element(ENABLE_NODE, &clear_vnc_mac_un_prefix_cmd);
+ install_element(ENABLE_NODE, &clear_vnc_mac_vn_prefix_cmd);
+ install_element(ENABLE_NODE, &clear_vnc_mac_all_prefix_cmd);
+
+ install_element(ENABLE_NODE, &clear_vrf_prefix_rd_cmd);
+ install_element(ENABLE_NODE, &clear_vrf_all_cmd);
+
+ install_element(ENABLE_NODE, &vnc_clear_counters_cmd);
+
+ install_element(VIEW_NODE, &vnc_show_summary_cmd);
+ install_element(VIEW_NODE, &vnc_show_nves_cmd);
+ install_element(VIEW_NODE, &vnc_show_nves_ptct_cmd);
+
+ install_element(VIEW_NODE, &vnc_show_registrations_pfx_cmd);
+ install_element(VIEW_NODE, &vnc_show_registrations_some_pfx_cmd);
+ install_element(VIEW_NODE, &vnc_show_responses_pfx_cmd);
+ install_element(VIEW_NODE, &vnc_show_responses_some_pfx_cmd);
+ install_element(VIEW_NODE, &show_vnc_queries_pfx_cmd);
+}
diff --git a/bgpd/rfapi/rfapi_vty.h b/bgpd/rfapi/rfapi_vty.h
new file mode 100644
index 0000000..09e1b3c
--- /dev/null
+++ b/bgpd/rfapi/rfapi_vty.h
@@ -0,0 +1,174 @@
+/*
+ *
+ * Copyright 2009-2016, LabN Consulting, L.L.C.
+ *
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program; see the file COPYING; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
+ */
+
+#ifndef RFAPI_VTY_H
+#define RFAPI_VTY_H
+
+#include "lib/vty.h"
+
+typedef enum {
+ SHOW_NVE_SUMMARY_ACTIVE_NVES,
+ SHOW_NVE_SUMMARY_UNKNOWN_NVES, /* legacy */
+ SHOW_NVE_SUMMARY_REGISTERED,
+ SHOW_NVE_SUMMARY_QUERIES,
+ SHOW_NVE_SUMMARY_RESPONSES,
+ SHOW_NVE_SUMMARY_MAX
+} show_nve_summary_t;
+
+#define VNC_SHOW_STR "VNC information\n"
+
+extern char *rfapiFormatSeconds(uint32_t seconds, char *buf, size_t len);
+
+extern char *rfapiFormatAge(time_t age, char *buf, size_t len);
+
+extern void rfapiRprefixApplyMask(struct rfapi_ip_prefix *rprefix);
+
+extern int rfapiQprefix2Raddr(struct prefix *qprefix,
+ struct rfapi_ip_addr *raddr);
+
+extern void rfapiQprefix2Rprefix(const struct prefix *qprefix,
+ struct rfapi_ip_prefix *rprefix);
+
+extern int rfapiRprefix2Qprefix(struct rfapi_ip_prefix *rprefix,
+ struct prefix *qprefix);
+
+extern int rfapiRaddr2Qprefix(struct rfapi_ip_addr *hia, struct prefix *pfx);
+
+extern int rfapiRprefixSame(struct rfapi_ip_prefix *hp1,
+ struct rfapi_ip_prefix *hp2);
+
+extern void rfapiL2o2Qprefix(struct rfapi_l2address_option *l2o,
+ struct prefix *pfx);
+
+extern int rfapiStr2EthAddr(const char *str, struct ethaddr *ea);
+
+extern const char *rfapi_ntop(int af, const void *src, char *buf,
+ socklen_t size);
+
+extern int rfapiDebugPrintf(void *dummy, const char *format, ...);
+
+extern int rfapiStream2Vty(void *stream, /* input */
+ int (**fp)(void *, const char *, ...), /* output */
+ struct vty **vty, /* output */
+ void **outstream, /* output */
+ const char **vty_newline); /* output */
+
+/*------------------------------------------
+ * rfapiRfapiIpAddr2Str
+ *
+ * UI helper: generate string from rfapi_ip_addr
+ *
+ * input:
+ * a IP v4/v6 address
+ *
+ * output
+ * buf put string here
+ * bufsize max space to write
+ *
+ * return value:
+ * NULL conversion failed
+ * non-NULL pointer to buf
+ --------------------------------------------*/
+extern const char *rfapiRfapiIpAddr2Str(struct rfapi_ip_addr *a, char *buf,
+ int bufsize);
+
+extern void rfapiPrintRfapiIpAddr(void *stream, struct rfapi_ip_addr *a);
+
+extern void rfapiPrintRfapiIpPrefix(void *stream, struct rfapi_ip_prefix *p);
+
+extern void rfapiPrintAdvertisedInfo(struct vty *vty,
+ struct rfapi_descriptor *rfd, safi_t safi,
+ struct prefix *p);
+
+extern void rfapiPrintDescriptor(struct vty *vty, struct rfapi_descriptor *rfd);
+
+extern void rfapiPrintMatchingDescriptors(struct vty *vty,
+ struct prefix *vn_prefix,
+ struct prefix *un_prefix);
+
+extern void rfapiPrintAttrPtrs(void *stream, struct attr *attr);
+
+/*
+ * Parse an address and put into a struct prefix
+ */
+extern int rfapiCliGetPrefixAddr(struct vty *vty, const char *str,
+ struct prefix *p);
+
+extern int rfapiCliGetRfapiIpAddr(struct vty *vty, const char *str,
+ struct rfapi_ip_addr *hai);
+
+extern void rfapiPrintNhl(void *stream, struct rfapi_next_hop_entry *next_hops);
+
+extern char *rfapiMonitorVpn2Str(struct rfapi_monitor_vpn *m, char *buf,
+ int size);
+
+extern const char *rfapiRfapiIpPrefix2Str(struct rfapi_ip_prefix *p, char *buf,
+ int bufsize);
+
+extern void rfapiShowItNode(void *stream, struct agg_node *rn);
+
+extern char *rfapiEthAddr2Str(const struct ethaddr *ea, char *buf, int bufsize);
+
+/* install vty commands */
+extern void rfapi_vty_init(void);
+
+/*------------------------------------------
+ * rfapiShowRemoteRegistrations
+ *
+ * UI helper: produces the "remote" portion of the output
+ * of "show vnc registrations".
+ *
+ * input:
+ * stream pointer to output stream
+ * prefix_only pointer to prefix. If non-NULL, print only registrations
+ * matching the specified prefix
+ * show_expiring if non-zero, show expiring registrations
+ * show_local if non-zero, show local registrations
+ * show_imported if non-zero, show imported registrations
+ *
+ * return value:
+ * 0 nothing printed
+ * >0 something printed
+ --------------------------------------------*/
+extern int rfapiShowRemoteRegistrations(void *stream,
+ struct prefix *prefix_only,
+ int show_expiring, int show_local,
+ int show_remote, int show_imported);
+
+/*------------------------------------------
+ * rfapi_monitor_count
+ *
+ * UI helper: count number of active monitors
+ *
+ * input:
+ * handle rfapi handle (NULL to count across
+ * all open handles)
+ *
+ * output
+ *
+ * return value:
+ * count of monitors
+ --------------------------------------------*/
+extern uint32_t rfapi_monitor_count(rfapi_handle);
+
+extern int rfapiShowVncQueries(void *stream, struct prefix *pfx_match);
+
+
+#endif
diff --git a/bgpd/rfapi/vnc_debug.c b/bgpd/rfapi/vnc_debug.c
new file mode 100644
index 0000000..5c627ef
--- /dev/null
+++ b/bgpd/rfapi/vnc_debug.c
@@ -0,0 +1,196 @@
+/*
+ *
+ * Copyright 2016, LabN Consulting, L.L.C.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program; see the file COPYING; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
+ */
+
+#include "lib/zebra.h"
+
+#include <lib/version.h>
+#include "lib/prefix.h"
+#include "lib/linklist.h"
+#include "lib/stream.h"
+#include "lib/command.h"
+#include "lib/log.h"
+#include "bgpd/rfapi/vnc_debug.h"
+
+/*
+ * debug state storage
+ */
+unsigned long conf_vnc_debug;
+unsigned long term_vnc_debug;
+
+struct vnc_debug {
+ unsigned long bit;
+ const char *name;
+};
+
+static const struct vnc_debug vncdebug[] = {
+ {VNC_DEBUG_RFAPI_QUERY, "rfapi-query"},
+ {VNC_DEBUG_IMPORT_BI_ATTACH, "import-bi-attach"},
+ {VNC_DEBUG_IMPORT_DEL_REMOTE, "import-del-remote"},
+ {VNC_DEBUG_EXPORT_BGP_GETCE, "export-bgp-getce"},
+ {VNC_DEBUG_EXPORT_BGP_DIRECT_ADD, "export-bgp-direct-add"},
+ {VNC_DEBUG_IMPORT_BGP_ADD_ROUTE, "import-bgp-add-route"},
+ {VNC_DEBUG_VERBOSE, "verbose"},
+};
+
+#define VNC_STR "VNC information\n"
+
+/***********************************************************************
+ * debug bgp vnc <foo>
+ ***********************************************************************/
+DEFUN (debug_bgp_vnc,
+ debug_bgp_vnc_cmd,
+ "debug bgp vnc <rfapi-query|import-bi-attach|import-del-remote|verbose>",
+ DEBUG_STR
+ BGP_STR
+ VNC_STR
+ "rfapi query handling\n"
+ "import BI atachment\n"
+ "import delete remote routes\n"
+ "verbose logging\n")
+{
+ size_t i;
+
+ for (i = 0; i < (sizeof(vncdebug) / sizeof(struct vnc_debug)); ++i) {
+ if (strmatch(argv[3]->text, vncdebug[i].name)) {
+ if (vty->node == CONFIG_NODE) {
+ conf_vnc_debug |= vncdebug[i].bit;
+ term_vnc_debug |= vncdebug[i].bit;
+ } else {
+ term_vnc_debug |= vncdebug[i].bit;
+ vty_out(vty, "BGP vnc %s debugging is on\n",
+ vncdebug[i].name);
+ }
+ return CMD_SUCCESS;
+ }
+ }
+ vty_out(vty, "Unknown debug flag: %s\n", argv[3]->arg);
+ return CMD_WARNING_CONFIG_FAILED;
+}
+
+DEFUN (no_debug_bgp_vnc,
+ no_debug_bgp_vnc_cmd,
+ "no debug bgp vnc <rfapi-query|import-bi-attach|import-del-remote|verbose>",
+ NO_STR
+ DEBUG_STR
+ BGP_STR
+ VNC_STR
+ "rfapi query handling\n"
+ "import BI atachment\n"
+ "import delete remote routes\n"
+ "verbose logging\n")
+{
+ size_t i;
+
+ for (i = 0; i < (sizeof(vncdebug) / sizeof(struct vnc_debug)); ++i) {
+ if (strmatch(argv[argc - 1]->text, vncdebug[i].name)) {
+ if (vty->node == CONFIG_NODE) {
+ conf_vnc_debug &= ~vncdebug[i].bit;
+ term_vnc_debug &= ~vncdebug[i].bit;
+ } else {
+ term_vnc_debug &= ~vncdebug[i].bit;
+ vty_out(vty, "BGP vnc %s debugging is off\n",
+ vncdebug[i].name);
+ }
+ return CMD_SUCCESS;
+ }
+ }
+ vty_out(vty, "Unknown debug flag: %s\n", argv[3]->arg);
+ return CMD_WARNING_CONFIG_FAILED;
+}
+
+/***********************************************************************
+ * no debug bgp vnc all
+ ***********************************************************************/
+
+DEFUN (no_debug_bgp_vnc_all,
+ no_debug_bgp_vnc_all_cmd,
+ "no debug all bgp vnc",
+ NO_STR
+ DEBUG_STR
+ "Disable all VNC debugging\n"
+ BGP_STR
+ VNC_STR)
+{
+ term_vnc_debug = 0;
+ vty_out(vty, "All possible VNC debugging has been turned off\n");
+
+ return CMD_SUCCESS;
+}
+
+/***********************************************************************
+ * show/save
+ ***********************************************************************/
+
+DEFUN_NOSH (show_debugging_bgp_vnc,
+ show_debugging_bgp_vnc_cmd,
+ "show debugging bgp vnc",
+ SHOW_STR
+ DEBUG_STR
+ BGP_STR
+ VNC_STR)
+{
+ size_t i;
+
+ vty_out(vty, "BGP VNC debugging status:\n");
+
+ for (i = 0; i < (sizeof(vncdebug) / sizeof(struct vnc_debug)); ++i) {
+ if (term_vnc_debug & vncdebug[i].bit) {
+ vty_out(vty, " BGP VNC %s debugging is on\n",
+ vncdebug[i].name);
+ }
+ }
+ vty_out(vty, "\n");
+ return CMD_SUCCESS;
+}
+
+static int bgp_vnc_config_write_debug(struct vty *vty)
+{
+ int write = 0;
+ size_t i;
+
+ for (i = 0; i < array_size(vncdebug); ++i) {
+ if (conf_vnc_debug & vncdebug[i].bit) {
+ vty_out(vty, "debug bgp vnc %s\n", vncdebug[i].name);
+ write++;
+ }
+ }
+ return write;
+}
+
+static int bgp_vnc_config_write_debug(struct vty *vty);
+static struct cmd_node debug_node = {
+ .name = "vnc debug",
+ .node = DEBUG_VNC_NODE,
+ .prompt = "",
+ .config_write = bgp_vnc_config_write_debug,
+};
+
+void vnc_debug_init(void)
+{
+ install_node(&debug_node);
+ install_element(ENABLE_NODE, &show_debugging_bgp_vnc_cmd);
+
+ install_element(ENABLE_NODE, &debug_bgp_vnc_cmd);
+ install_element(CONFIG_NODE, &debug_bgp_vnc_cmd);
+ install_element(ENABLE_NODE, &no_debug_bgp_vnc_cmd);
+ install_element(CONFIG_NODE, &no_debug_bgp_vnc_cmd);
+
+ install_element(ENABLE_NODE, &no_debug_bgp_vnc_all_cmd);
+ install_element(CONFIG_NODE, &no_debug_bgp_vnc_all_cmd);
+}
diff --git a/bgpd/rfapi/vnc_debug.h b/bgpd/rfapi/vnc_debug.h
new file mode 100644
index 0000000..c472b63
--- /dev/null
+++ b/bgpd/rfapi/vnc_debug.h
@@ -0,0 +1,51 @@
+/*
+ *
+ * Copyright 2016, LabN Consulting, L.L.C.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program; see the file COPYING; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
+ */
+
+#ifndef _QUAGGA_BGP_VNC_DEBUG_H
+#define _QUAGGA_BGP_VNC_DEBUG_H
+
+#ifdef ENABLE_BGP_VNC
+
+/*
+ * debug state storage
+ */
+extern unsigned long conf_vnc_debug;
+extern unsigned long term_vnc_debug;
+
+/*
+ * debug flag bits
+ */
+#define VNC_DEBUG_RFAPI_QUERY 0x00000001
+#define VNC_DEBUG_IMPORT_BI_ATTACH 0x00000002
+#define VNC_DEBUG_IMPORT_DEL_REMOTE 0x00000004
+#define VNC_DEBUG_EXPORT_BGP_GETCE 0x00000008
+#define VNC_DEBUG_EXPORT_BGP_DIRECT_ADD 0x00000010
+#define VNC_DEBUG_IMPORT_BGP_ADD_ROUTE 0x00000020
+#define VNC_DEBUG_VERBOSE 0x00000040
+#define VNC_DEBUG_ANY 0xFFFFFFFF
+
+#define VNC_DEBUG(bit) (term_vnc_debug & (VNC_DEBUG_ ## bit))
+#define vnc_zlog_debug_verbose if (VNC_DEBUG(VERBOSE)) zlog_debug
+#define vnc_zlog_debug_any if (VNC_DEBUG(ANY)) zlog_debug
+
+extern void vnc_debug_init(void);
+
+#endif /* ENABLE_BGP_VNC */
+
+#endif /* _QUAGGA_BGP_VNC_DEBUG_H */
diff --git a/bgpd/rfapi/vnc_export_bgp.c b/bgpd/rfapi/vnc_export_bgp.c
new file mode 100644
index 0000000..05e45bc
--- /dev/null
+++ b/bgpd/rfapi/vnc_export_bgp.c
@@ -0,0 +1,2113 @@
+/*
+ *
+ * Copyright 2009-2016, LabN Consulting, L.L.C.
+ *
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program; see the file COPYING; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
+ */
+
+/*
+ * File: vnc_export_bgp.c
+ * Purpose: Export routes to BGP directly (not via zebra)
+ */
+
+#include "lib/zebra.h"
+#include "lib/prefix.h"
+#include "lib/agg_table.h"
+#include "lib/vty.h"
+#include "lib/log.h"
+#include "lib/stream.h"
+#include "lib/memory.h"
+#include "lib/linklist.h"
+#include "lib/plist.h"
+#include "lib/routemap.h"
+#include "lib/lib_errors.h"
+
+#include "bgpd/bgpd.h"
+#include "bgpd/bgp_ecommunity.h"
+#include "bgpd/bgp_attr.h"
+#include "bgpd/bgp_aspath.h"
+
+#include "bgpd/rfapi/vnc_export_bgp.h"
+#include "bgpd/rfapi/vnc_export_bgp_p.h"
+#include "bgpd/rfapi/vnc_export_table.h"
+#include "bgpd/rfapi/bgp_rfapi_cfg.h"
+#include "bgpd/rfapi/rfapi.h"
+#include "bgpd/rfapi/rfapi_import.h"
+#include "bgpd/rfapi/rfapi_private.h"
+#include "bgpd/rfapi/rfapi_backend.h"
+#include "bgpd/rfapi/rfapi_vty.h"
+#include "bgpd/rfapi/vnc_debug.h"
+
+
+static void vnc_direct_add_rn_group_rd(struct bgp *bgp,
+ struct rfapi_nve_group_cfg *rfg,
+ struct agg_node *rn, struct attr *attr,
+ afi_t afi,
+ struct rfapi_descriptor *irfd);
+
+/***********************************************************************
+ * Export methods that set nexthop to CE (from 5226 roo EC) BEGIN
+ ***********************************************************************/
+
+/*
+ * Memory allocation approach: make a ghost attr that
+ * has non-interned parts for the modifications. ghost attr
+ * memory is allocated by caller.
+ *
+ * - extract ce (=5226) EC and use as new nexthop
+ * - strip Tunnel Encap attr
+ * - copy all ECs
+ */
+static void encap_attr_export_ce(struct attr *new, struct attr *orig,
+ struct prefix *use_nexthop)
+{
+ /*
+ * Make "new" a ghost attr copy of "orig"
+ */
+ memset(new, 0, sizeof(struct attr));
+ *new = *orig;
+
+ /*
+ * Set nexthop
+ */
+ switch (use_nexthop->family) {
+ case AF_INET:
+ new->nexthop = use_nexthop->u.prefix4;
+ new->mp_nexthop_len = BGP_ATTR_NHLEN_IPV4; /* bytes */
+ new->flag |= ATTR_FLAG_BIT(BGP_ATTR_NEXT_HOP);
+ break;
+
+ case AF_INET6:
+ new->mp_nexthop_global = use_nexthop->u.prefix6;
+ new->mp_nexthop_len = BGP_ATTR_NHLEN_IPV6_GLOBAL; /* bytes */
+ break;
+
+ default:
+ assert(0);
+ break;
+ }
+
+ /*
+ * Set MED
+ *
+ * Note that it will be deleted when BGP sends to any eBGP
+ * peer unless PEER_FLAG_MED_UNCHANGED is set:
+ *
+ * neighbor NEIGHBOR attribute-unchanged med
+ */
+ if (!CHECK_FLAG(new->flag, BGP_ATTR_MULTI_EXIT_DISC)) {
+ if (CHECK_FLAG(new->flag, BGP_ATTR_LOCAL_PREF)) {
+ if (new->local_pref > 255)
+ new->med = 0;
+ else
+ new->med = 255 - new->local_pref;
+ } else {
+ new->med = 255; /* shouldn't happen */
+ }
+ new->flag |= ATTR_FLAG_BIT(BGP_ATTR_MULTI_EXIT_DISC);
+ }
+
+ /*
+ * "new" is now a ghost attr:
+ * - it owns an "extra" struct
+ * - it owns any non-interned parts
+ * - any references to interned parts are not counted
+ *
+ * Caller should, after using the attr, call:
+ * - bgp_attr_flush() to free non-interned parts
+ */
+}
+
+static int getce(struct bgp *bgp, struct attr *attr, struct prefix *pfx_ce)
+{
+ uint8_t *ecp;
+ uint32_t i;
+ uint16_t localadmin = bgp->rfapi_cfg->resolve_nve_roo_local_admin;
+ struct ecommunity *ecomm = bgp_attr_get_ecommunity(attr);
+
+ for (ecp = ecomm->val, i = 0; i < ecomm->size;
+ ++i, ecp += ECOMMUNITY_SIZE) {
+
+ if (VNC_DEBUG(EXPORT_BGP_GETCE)) {
+ vnc_zlog_debug_any(
+ "%s: %02x %02x %02x %02x %02x %02x %02x %02x",
+ __func__, ecp[0], ecp[1], ecp[2], ecp[3],
+ ecp[4], ecp[5], ecp[6], ecp[7]);
+ }
+
+ /*
+ * is it ROO?
+ */
+ if (ecp[0] != 1 || ecp[1] != 3) {
+ continue;
+ }
+
+ /*
+ * Match local admin value?
+ */
+ if (ecp[6] != ((localadmin & 0xff00) >> 8)
+ || ecp[7] != (localadmin & 0xff))
+ continue;
+
+ memset((uint8_t *)pfx_ce, 0, sizeof(*pfx_ce));
+ memcpy(&pfx_ce->u.prefix4, ecp + 2, 4);
+ pfx_ce->family = AF_INET;
+ pfx_ce->prefixlen = IPV4_MAX_BITLEN;
+
+ return 0;
+ }
+ return -1;
+}
+
+
+void vnc_direct_bgp_add_route_ce(struct bgp *bgp, struct agg_node *rn,
+ struct bgp_path_info *bpi)
+{
+ struct attr *attr = bpi->attr;
+ struct peer *peer = bpi->peer;
+ const struct prefix *prefix = agg_node_get_prefix(rn);
+ afi_t afi = family2afi(prefix->family);
+ struct bgp_dest *udest;
+ struct bgp_path_info *ubpi;
+ struct attr hattr;
+ struct attr *iattr;
+ struct prefix ce_nexthop;
+ struct prefix post_routemap_nexthop;
+
+
+ if (!afi) {
+ flog_err(EC_LIB_DEVELOPMENT, "%s: can't get afi of route node",
+ __func__);
+ return;
+ }
+
+ if ((bpi->type != ZEBRA_ROUTE_BGP)
+ || (bpi->sub_type != BGP_ROUTE_NORMAL
+ && bpi->sub_type != BGP_ROUTE_RFP
+ && bpi->sub_type != BGP_ROUTE_STATIC)) {
+
+ vnc_zlog_debug_verbose(
+ "%s: wrong route type/sub_type for export, skipping",
+ __func__);
+ return;
+ }
+
+ /* check bgp redist flag for vnc direct ("vpn") routes */
+ if (!bgp->redist[afi][ZEBRA_ROUTE_VNC_DIRECT]) {
+ vnc_zlog_debug_verbose(
+ "%s: bgp redistribution of VNC direct routes is off",
+ __func__);
+ return;
+ }
+
+ if (!bgp->rfapi_cfg) {
+ vnc_zlog_debug_verbose("%s: bgp->rfapi_cfg is NULL, skipping",
+ __func__);
+ return;
+ }
+
+ if (!VNC_EXPORT_BGP_CE_ENABLED(bgp->rfapi_cfg)) {
+ vnc_zlog_debug_verbose(
+ "%s: export-to-bgp ce mode not enabled, skipping",
+ __func__);
+ return;
+ }
+
+ /*
+ * prefix list check
+ */
+ if (bgp->rfapi_cfg->plist_export_bgp[afi]) {
+ if (prefix_list_apply(bgp->rfapi_cfg->plist_export_bgp[afi],
+ prefix)
+ == PREFIX_DENY) {
+ vnc_zlog_debug_verbose(
+ "%s: prefix list denied, skipping", __func__);
+ return;
+ }
+ }
+
+
+ /*
+ * Extract CE
+ * This works only for IPv4 because IPv6 addresses are too big
+ * to fit in an extended community
+ */
+ if (getce(bgp, attr, &ce_nexthop)) {
+ vnc_zlog_debug_verbose("%s: EC has no encoded CE, skipping",
+ __func__);
+ return;
+ }
+
+ /*
+ * Is this route already represented in the unicast RIB?
+ * (look up prefix; compare route type, sub_type, peer, nexthop)
+ */
+ udest = bgp_afi_node_get(bgp->rib[afi][SAFI_UNICAST], afi, SAFI_UNICAST,
+ prefix, NULL);
+ for (ubpi = bgp_dest_get_bgp_path_info(udest); ubpi;
+ ubpi = ubpi->next) {
+ struct prefix unicast_nexthop;
+
+ if (CHECK_FLAG(ubpi->flags, BGP_PATH_REMOVED))
+ continue;
+
+ rfapiUnicastNexthop2Prefix(afi, ubpi->attr, &unicast_nexthop);
+
+ if (ubpi->type == ZEBRA_ROUTE_VNC_DIRECT
+ && ubpi->sub_type == BGP_ROUTE_REDISTRIBUTE
+ && ubpi->peer == peer
+ && prefix_same(&unicast_nexthop, &ce_nexthop)) {
+
+ vnc_zlog_debug_verbose(
+ "%s: already have matching exported unicast route, skipping",
+ __func__);
+ return;
+ }
+ }
+
+ /*
+ * Construct new attribute set with CE addr as
+ * nexthop and without Tunnel Encap attr
+ */
+ encap_attr_export_ce(&hattr, attr, &ce_nexthop);
+ if (bgp->rfapi_cfg->routemap_export_bgp) {
+ struct bgp_path_info info;
+ route_map_result_t ret;
+
+ memset(&info, 0, sizeof(info));
+ info.peer = peer;
+ info.attr = &hattr;
+ ret = route_map_apply(bgp->rfapi_cfg->routemap_export_bgp,
+ prefix, &info);
+ if (ret == RMAP_DENYMATCH) {
+ bgp_attr_flush(&hattr);
+ return;
+ }
+ }
+
+ iattr = bgp_attr_intern(&hattr);
+ bgp_attr_flush(&hattr);
+
+ /*
+ * Rule: disallow route-map alteration of next-hop, because it
+ * would make it too difficult to keep track of the correspondence
+ * between VPN routes and unicast routes.
+ */
+ rfapiUnicastNexthop2Prefix(afi, iattr, &post_routemap_nexthop);
+
+ if (!prefix_same(&ce_nexthop, &post_routemap_nexthop)) {
+ vnc_zlog_debug_verbose(
+ "%s: route-map modification of nexthop not allowed, skipping",
+ __func__);
+ bgp_attr_unintern(&iattr);
+ return;
+ }
+
+ bgp_update(peer, prefix, 0, /* addpath_id */
+ iattr, /* bgp_update copies this attr */
+ afi, SAFI_UNICAST, ZEBRA_ROUTE_VNC_DIRECT,
+ BGP_ROUTE_REDISTRIBUTE, NULL, /* RD not used for unicast */
+ NULL, 0, /* tag not used for unicast */
+ 0, NULL); /* EVPN not used */
+ bgp_attr_unintern(&iattr);
+}
+
+
+/*
+ * "Withdrawing a Route" export process
+ */
+void vnc_direct_bgp_del_route_ce(struct bgp *bgp, struct agg_node *rn,
+ struct bgp_path_info *bpi)
+{
+ const struct prefix *p = agg_node_get_prefix(rn);
+ afi_t afi = family2afi(p->family);
+ struct bgp_path_info *vbpi;
+ struct prefix ce_nexthop;
+
+ if (!afi) {
+ flog_err(EC_LIB_DEVELOPMENT, "%s: bad afi", __func__);
+ return;
+ }
+
+ /* check bgp redist flag for vnc direct ("vpn") routes */
+ if (!bgp->redist[afi][ZEBRA_ROUTE_VNC_DIRECT]) {
+ vnc_zlog_debug_verbose(
+ "%s: bgp redistribution of VNC direct routes is off",
+ __func__);
+ return;
+ }
+
+ if (!bgp->rfapi_cfg) {
+ vnc_zlog_debug_verbose("%s: bgp->rfapi_cfg is NULL, skipping",
+ __func__);
+ return;
+ }
+ if (!VNC_EXPORT_BGP_CE_ENABLED(bgp->rfapi_cfg)) {
+ vnc_zlog_debug_verbose(
+ "%s: export-to-bgp ce mode not enabled, skipping",
+ __func__);
+ return;
+ }
+
+ /*
+ * Extract CE
+ * This works only for IPv4 because IPv6 addresses are too big
+ * to fit in an extended community
+ */
+ if (getce(bgp, bpi->attr, &ce_nexthop)) {
+ vnc_zlog_debug_verbose("%s: EC has no encoded CE, skipping",
+ __func__);
+ return;
+ }
+
+ /*
+ * Look for other VPN routes with same prefix, same 5226 CE,
+ * same peer. If at least one is present, don't remove the
+ * route from the unicast RIB
+ */
+
+ for (vbpi = rn->info; vbpi; vbpi = vbpi->next) {
+ struct prefix ce;
+ if (bpi == vbpi)
+ continue;
+ if (bpi->peer != vbpi->peer)
+ continue;
+ if (getce(bgp, vbpi->attr, &ce))
+ continue;
+ if (prefix_same(&ce, &ce_nexthop)) {
+ vnc_zlog_debug_verbose(
+ "%s: still have a route via CE, not deleting unicast",
+ __func__);
+ return;
+ }
+ }
+
+ /*
+ * withdraw the route
+ */
+ bgp_withdraw(bpi->peer, p, 0, /* addpath_id */
+ NULL, /* attr, ignored */
+ afi, SAFI_UNICAST, ZEBRA_ROUTE_VNC_DIRECT,
+ BGP_ROUTE_REDISTRIBUTE, NULL, /* RD not used for unicast */
+ NULL, 0, NULL); /* tag not used for unicast */
+}
+
+static void vnc_direct_bgp_vpn_enable_ce(struct bgp *bgp, afi_t afi)
+{
+ struct agg_node *rn;
+ struct bgp_path_info *ri;
+
+ vnc_zlog_debug_verbose("%s: entry, afi=%d", __func__, afi);
+
+ if (!bgp)
+ return;
+
+ if (!(bgp->rfapi_cfg))
+ return;
+
+ if (!VNC_EXPORT_BGP_CE_ENABLED(bgp->rfapi_cfg)) {
+ vnc_zlog_debug_verbose(
+ "%s: export of CE routes not enabled, skipping",
+ __func__);
+ return;
+ }
+
+ if (afi != AFI_IP && afi != AFI_IP6) {
+ vnc_zlog_debug_verbose("%s: bad afi: %d", __func__, afi);
+ return;
+ }
+
+ /*
+ * Go through entire ce import table and export to BGP unicast.
+ */
+ for (rn = agg_route_top(bgp->rfapi->it_ce->imported_vpn[afi]); rn;
+ rn = agg_route_next(rn)) {
+ if (!rn->info)
+ continue;
+
+ vnc_zlog_debug_verbose("%s: checking prefix %pRN", __func__,
+ rn);
+
+ for (ri = rn->info; ri; ri = ri->next) {
+
+ vnc_zlog_debug_verbose("%s: ri->sub_type: %d", __func__,
+ ri->sub_type);
+
+ if (ri->sub_type == BGP_ROUTE_NORMAL
+ || ri->sub_type == BGP_ROUTE_RFP
+ || ri->sub_type == BGP_ROUTE_STATIC) {
+
+ vnc_direct_bgp_add_route_ce(bgp, rn, ri);
+ }
+ }
+ }
+}
+
+static void vnc_direct_bgp_vpn_disable_ce(struct bgp *bgp, afi_t afi)
+{
+ struct bgp_dest *dest;
+
+ vnc_zlog_debug_verbose("%s: entry, afi=%d", __func__, afi);
+
+ if (!bgp)
+ return;
+
+ if (afi != AFI_IP && afi != AFI_IP6) {
+ vnc_zlog_debug_verbose("%s: bad afi: %d", __func__, afi);
+ return;
+ }
+
+ /*
+ * Go through the entire BGP unicast table and remove routes that
+ * originated from us
+ */
+ for (dest = bgp_table_top(bgp->rib[afi][SAFI_UNICAST]); dest;
+ dest = bgp_route_next(dest)) {
+
+ struct bgp_path_info *ri;
+ struct bgp_path_info *next;
+
+ for (ri = bgp_dest_get_bgp_path_info(dest), next = NULL; ri;
+ ri = next) {
+
+ next = ri->next;
+
+ if (ri->type == ZEBRA_ROUTE_VNC_DIRECT
+ && ri->sub_type == BGP_ROUTE_REDISTRIBUTE) {
+
+ bgp_withdraw(
+ ri->peer, bgp_dest_get_prefix(dest),
+ 0, /* addpath_id */
+ NULL, /* ignored */
+ AFI_IP, SAFI_UNICAST,
+ ZEBRA_ROUTE_VNC_DIRECT,
+ BGP_ROUTE_REDISTRIBUTE,
+ NULL, /* RD not used for unicast */
+ NULL, 0,
+ NULL); /* tag not used for unicast */
+ }
+ }
+ }
+}
+
+/***********************************************************************
+ * Export methods that set nexthop to CE (from 5226 roo EC) END
+ ***********************************************************************/
+
+/***********************************************************************
+ * Export methods that proxy nexthop BEGIN
+ ***********************************************************************/
+
+static struct ecommunity *vnc_route_origin_ecom(struct agg_node *rn)
+{
+ struct ecommunity *new;
+ struct bgp_path_info *bpi;
+
+ if (!rn->info)
+ return NULL;
+
+ new = ecommunity_new();
+
+ for (bpi = rn->info; bpi; bpi = bpi->next) {
+
+ struct ecommunity_val roec;
+
+ switch (BGP_MP_NEXTHOP_FAMILY(bpi->attr->mp_nexthop_len)) {
+ case AF_INET:
+ memset(&roec, 0, sizeof(roec));
+ roec.val[0] = 0x01;
+ roec.val[1] = 0x03;
+ memcpy(roec.val + 2,
+ &bpi->attr->mp_nexthop_global_in.s_addr, 4);
+ roec.val[6] = 0;
+ roec.val[7] = 0;
+ ecommunity_add_val(new, &roec, false, false);
+ break;
+ case AF_INET6:
+ /* No support for IPv6 addresses in extended communities
+ */
+ break;
+ }
+ }
+
+ if (!new->size) {
+ ecommunity_free(&new);
+ new = NULL;
+ }
+
+ return new;
+}
+
+static struct ecommunity *vnc_route_origin_ecom_single(struct in_addr *origin)
+{
+ struct ecommunity *new;
+ struct ecommunity_val roec;
+
+ memset(&roec, 0, sizeof(roec));
+ roec.val[0] = 0x01;
+ roec.val[1] = 0x03;
+ memcpy(roec.val + 2, &origin->s_addr, 4);
+ roec.val[6] = 0;
+ roec.val[7] = 0;
+
+ new = ecommunity_new();
+ ecommunity_add_val(new, &roec, false, false);
+
+ if (!new->size) {
+ ecommunity_free(&new);
+ new = NULL;
+ }
+
+ return new;
+}
+
+
+/*
+ * New memory allocation approach: make a ghost attr that
+ * has non-interned parts for the modifications. ghost attr
+ * memory is allocated by caller.
+ */
+static int
+encap_attr_export(struct attr *new, struct attr *orig,
+ struct prefix *new_nexthop,
+ struct agg_node *rn) /* for VN addrs for ecom list */
+ /* if rn is 0, use route's nexthop */
+{
+ struct prefix orig_nexthop;
+ struct prefix *use_nexthop;
+ static struct ecommunity *ecom_ro;
+
+ if (new_nexthop) {
+ use_nexthop = new_nexthop;
+ } else {
+ use_nexthop = &orig_nexthop;
+ orig_nexthop.family =
+ BGP_MP_NEXTHOP_FAMILY(orig->mp_nexthop_len);
+ if (orig_nexthop.family == AF_INET) {
+ orig_nexthop.prefixlen = IPV4_MAX_BITLEN;
+ orig_nexthop.u.prefix4 = orig->mp_nexthop_global_in;
+ } else if (orig_nexthop.family == AF_INET6) {
+ orig_nexthop.prefixlen = IPV6_MAX_BITLEN;
+ orig_nexthop.u.prefix6 = orig->mp_nexthop_global;
+ } else {
+ return -1; /* FAIL - can't compute nexthop */
+ }
+ }
+
+
+ /*
+ * Make "new" a ghost attr copy of "orig"
+ */
+ memset(new, 0, sizeof(struct attr));
+ *new = *orig;
+
+ /*
+ * Set nexthop
+ */
+ switch (use_nexthop->family) {
+ case AF_INET:
+ new->nexthop = use_nexthop->u.prefix4;
+ new->mp_nexthop_len = BGP_ATTR_NHLEN_IPV4; /* bytes */
+ new->flag |= ATTR_FLAG_BIT(BGP_ATTR_NEXT_HOP);
+ break;
+
+ case AF_INET6:
+ new->mp_nexthop_global = use_nexthop->u.prefix6;
+ new->mp_nexthop_len = BGP_ATTR_NHLEN_IPV6_GLOBAL; /* bytes */
+ break;
+
+ default:
+ assert(0);
+ break;
+ }
+
+ if (rn) {
+ ecom_ro = vnc_route_origin_ecom(rn);
+ } else {
+ /* TBD use lcom for IPv6 */
+ ecom_ro = vnc_route_origin_ecom_single(&use_nexthop->u.prefix4);
+ }
+ if (bgp_attr_get_ecommunity(new)) {
+ if (ecom_ro)
+ bgp_attr_set_ecommunity(
+ new,
+ ecommunity_merge(ecom_ro,
+ bgp_attr_get_ecommunity(new)));
+ } else {
+ bgp_attr_set_ecommunity(new, ecom_ro);
+ }
+
+ /*
+ * Set MED
+ *
+ * Note that it will be deleted when BGP sends to any eBGP
+ * peer unless PEER_FLAG_MED_UNCHANGED is set:
+ *
+ * neighbor NEIGHBOR attribute-unchanged med
+ */
+ if (!CHECK_FLAG(new->flag, BGP_ATTR_MULTI_EXIT_DISC)) {
+ if (CHECK_FLAG(new->flag, BGP_ATTR_LOCAL_PREF)) {
+ if (new->local_pref > 255)
+ new->med = 0;
+ else
+ new->med = 255 - new->local_pref;
+ } else {
+ new->med = 255; /* shouldn't happen */
+ }
+ new->flag |= ATTR_FLAG_BIT(BGP_ATTR_MULTI_EXIT_DISC);
+ }
+
+ /*
+ * "new" is now a ghost attr:
+ * - it owns an "extra" struct
+ * - it owns any non-interned parts
+ * - any references to interned parts are not counted
+ *
+ * Caller should, after using the attr, call:
+ * - bgp_attr_flush() to free non-interned parts
+ */
+
+ return 0;
+}
+
+/*
+ * "Adding a Route" export process
+ */
+void vnc_direct_bgp_add_prefix(struct bgp *bgp,
+ struct rfapi_import_table *import_table,
+ struct agg_node *rn)
+{
+ struct attr attr = {0};
+ struct listnode *node, *nnode;
+ struct rfapi_rfg_name *rfgn;
+ const struct prefix *p = agg_node_get_prefix(rn);
+ afi_t afi = family2afi(p->family);
+
+ if (!afi) {
+ flog_err(EC_LIB_DEVELOPMENT, "%s: can't get afi of route node",
+ __func__);
+ return;
+ }
+
+ /* check bgp redist flag for vnc direct ("vpn") routes */
+ if (!bgp->redist[afi][ZEBRA_ROUTE_VNC_DIRECT]) {
+ vnc_zlog_debug_verbose(
+ "%s: bgp redistribution of VNC direct routes is off",
+ __func__);
+ return;
+ }
+
+ if (!bgp->rfapi_cfg) {
+ vnc_zlog_debug_verbose("%s: bgp->rfapi_cfg is NULL, skipping",
+ __func__);
+ return;
+ }
+
+ if (!VNC_EXPORT_BGP_GRP_ENABLED(bgp->rfapi_cfg)) {
+ vnc_zlog_debug_verbose(
+ "%s: export-to-bgp group mode not enabled, skipping",
+ __func__);
+ return;
+ }
+
+ if (!listcount(bgp->rfapi_cfg->rfg_export_direct_bgp_l)) {
+ vnc_zlog_debug_verbose(
+ "%s: no bgp-direct export nve group, skipping",
+ __func__);
+ return;
+ }
+
+ bgp_attr_default_set(&attr, bgp, BGP_ORIGIN_INCOMPLETE);
+ /* TBD set some configured med, see add_vnc_route() */
+
+ vnc_zlog_debug_verbose(
+ "%s: looping over nve-groups in direct-bgp export list",
+ __func__);
+
+ for (ALL_LIST_ELEMENTS(bgp->rfapi_cfg->rfg_export_direct_bgp_l, node,
+ nnode, rfgn)) {
+
+ struct listnode *ln;
+
+ /*
+ * If nve group is not defined yet, skip it
+ */
+ if (!rfgn->rfg)
+ continue;
+
+ /*
+ * If the nve group uses a different import table, skip it
+ */
+ if (import_table != rfgn->rfg->rfapi_import_table)
+ continue;
+
+ /*
+ * if no NVEs currently associated with this group, skip it
+ */
+ if (rfgn->rfg->type != RFAPI_GROUP_CFG_VRF && !rfgn->rfg->nves)
+ continue;
+
+ /*
+ * per-nve-group prefix list check
+ */
+ if (rfgn->rfg->plist_export_bgp[afi]) {
+ if (prefix_list_apply(rfgn->rfg->plist_export_bgp[afi],
+ p)
+ == PREFIX_DENY)
+
+ continue;
+ }
+
+ if (rfgn->rfg->type == RFAPI_GROUP_CFG_VRF) {
+ vnc_direct_add_rn_group_rd(bgp, rfgn->rfg, rn, &attr,
+ afi, rfgn->rfg->rfd);
+ /*
+ * yuck!
+ * - but consistent with rest of function
+ */
+ continue;
+ }
+ /*
+ * For each NVE that is assigned to the export nve group,
+ * generate
+ * a route with that NVE as its next hop
+ */
+ for (ln = listhead(rfgn->rfg->nves); ln;
+ ln = listnextnode(ln)) {
+ vnc_direct_add_rn_group_rd(bgp, rfgn->rfg, rn, &attr,
+ afi, listgetdata(ln));
+ }
+ }
+
+ aspath_unintern(&attr.aspath);
+}
+
+/*
+ * "Withdrawing a Route" export process
+ */
+void vnc_direct_bgp_del_prefix(struct bgp *bgp,
+ struct rfapi_import_table *import_table,
+ struct agg_node *rn)
+{
+ struct listnode *node, *nnode;
+ struct rfapi_rfg_name *rfgn;
+ const struct prefix *p = agg_node_get_prefix(rn);
+ afi_t afi = family2afi(p->family);
+
+ if (!afi) {
+ flog_err(EC_LIB_DEVELOPMENT, "%s: can't get afi route node",
+ __func__);
+ return;
+ }
+
+ /* check bgp redist flag for vnc direct ("vpn") routes */
+ if (!bgp->redist[afi][ZEBRA_ROUTE_VNC_DIRECT]) {
+ vnc_zlog_debug_verbose(
+ "%s: bgp redistribution of VNC direct routes is off",
+ __func__);
+ return;
+ }
+
+ if (!bgp->rfapi_cfg) {
+ vnc_zlog_debug_verbose("%s: bgp->rfapi_cfg is NULL, skipping",
+ __func__);
+ return;
+ }
+
+ if (!VNC_EXPORT_BGP_GRP_ENABLED(bgp->rfapi_cfg)) {
+ vnc_zlog_debug_verbose(
+ "%s: export-to-bgp group mode not enabled, skipping",
+ __func__);
+ return;
+ }
+
+ if (!listcount(bgp->rfapi_cfg->rfg_export_direct_bgp_l)) {
+ vnc_zlog_debug_verbose(
+ "%s: no bgp-direct export nve group, skipping",
+ __func__);
+ return;
+ }
+
+ for (ALL_LIST_ELEMENTS(bgp->rfapi_cfg->rfg_export_direct_bgp_l, node,
+ nnode, rfgn)) {
+
+ struct listnode *ln;
+
+ /*
+ * If nve group is not defined yet, skip it
+ */
+ if (!rfgn->rfg)
+ continue;
+
+ /*
+ * if no NVEs currently associated with this group, skip it
+ */
+ if (rfgn->rfg->type != RFAPI_GROUP_CFG_VRF && !rfgn->rfg->nves)
+ continue;
+
+ /*
+ * If the nve group uses a different import table,
+ * skip it
+ */
+ if (import_table != rfgn->rfg->rfapi_import_table)
+ continue;
+
+ if (rfgn->rfg->type == RFAPI_GROUP_CFG_VRF) {
+ struct prefix nhp;
+ struct rfapi_descriptor *irfd;
+
+ irfd = rfgn->rfg->rfd;
+
+ if (rfapiRaddr2Qprefix(&irfd->vn_addr, &nhp))
+ continue;
+
+ bgp_withdraw(irfd->peer, p, /* prefix */
+ 0, /* addpath_id */
+ NULL, /* attr, ignored */
+ afi, SAFI_UNICAST, ZEBRA_ROUTE_VNC_DIRECT,
+ BGP_ROUTE_REDISTRIBUTE,
+ NULL, /* RD not used for unicast */
+ NULL, 0,
+ NULL); /* tag not used for unicast */
+ /*
+ * yuck!
+ * - but consistent with rest of function
+ */
+ continue;
+ }
+ /*
+ * For each NVE that is assigned to the export nve group,
+ * generate
+ * a route with that NVE as its next hop
+ */
+ for (ln = listhead(rfgn->rfg->nves); ln;
+ ln = listnextnode(ln)) {
+
+ struct prefix nhp;
+ struct rfapi_descriptor *irfd;
+
+ irfd = listgetdata(ln);
+
+ if (rfapiRaddr2Qprefix(&irfd->vn_addr, &nhp))
+ continue;
+
+ bgp_withdraw(irfd->peer, p, /* prefix */
+ 0, /* addpath_id */
+ NULL, /* attr, ignored */
+ afi, SAFI_UNICAST, ZEBRA_ROUTE_VNC_DIRECT,
+ BGP_ROUTE_REDISTRIBUTE,
+ NULL, /* RD not used for unicast */
+ NULL, 0,
+ NULL); /* tag not used for unicast */
+ }
+ }
+}
+
+void vnc_direct_bgp_add_nve(struct bgp *bgp, struct rfapi_descriptor *rfd)
+{
+ struct listnode *node, *nnode;
+ struct rfapi_rfg_name *rfgn;
+ struct rfapi_nve_group_cfg *rfg = rfd->rfg;
+ afi_t afi = family2afi(rfd->vn_addr.addr_family);
+
+ if (!afi) {
+ flog_err(EC_LIB_DEVELOPMENT, "%s: can't get afi of nve vn addr",
+ __func__);
+ return;
+ }
+
+ if (!bgp)
+ return;
+ if (!bgp->rfapi_cfg) {
+ vnc_zlog_debug_verbose("%s: bgp->rfapi_cfg is NULL, skipping",
+ __func__);
+ return;
+ }
+ if (!VNC_EXPORT_BGP_GRP_ENABLED(bgp->rfapi_cfg)) {
+ vnc_zlog_debug_verbose(
+ "%s: export-to-bgp group mode not enabled, skipping",
+ __func__);
+ return;
+ }
+
+ if (!bgp->redist[afi][ZEBRA_ROUTE_VNC_DIRECT]) {
+ vnc_zlog_debug_verbose(
+ "%s: bgp redistribution of VNC direct routes is off",
+ __func__);
+ return;
+ }
+
+ /*
+ * Loop over the list of NVE-Groups configured for
+ * exporting to direct-bgp and see if this new NVE's
+ * group is among them.
+ */
+ for (ALL_LIST_ELEMENTS(bgp->rfapi_cfg->rfg_export_direct_bgp_l, node,
+ nnode, rfgn)) {
+
+ /*
+ * Yes, this NVE's group is configured for export to direct-bgp
+ */
+ if (rfgn->rfg == rfg) {
+
+ struct agg_table *rt = NULL;
+ struct agg_node *rn;
+ struct attr attr = {0};
+ struct rfapi_import_table *import_table;
+
+
+ import_table = rfg->rfapi_import_table;
+
+ if (afi == AFI_IP || afi == AFI_IP6) {
+ rt = import_table->imported_vpn[afi];
+ } else {
+ flog_err(EC_LIB_DEVELOPMENT, "%s: bad afi %d",
+ __func__, afi);
+ return;
+ }
+
+ bgp_attr_default_set(&attr, bgp, BGP_ORIGIN_INCOMPLETE);
+ /* TBD set some configured med, see add_vnc_route() */
+
+ /*
+ * Walk the NVE-Group's VNC Import table
+ */
+ for (rn = agg_route_top(rt); rn;
+ rn = agg_route_next(rn)) {
+
+ if (rn->info) {
+
+ struct prefix nhp;
+ struct rfapi_descriptor *irfd = rfd;
+ struct attr hattr;
+ struct attr *iattr;
+ struct bgp_path_info info;
+ const struct prefix *p =
+ agg_node_get_prefix(rn);
+
+ if (rfapiRaddr2Qprefix(&irfd->vn_addr,
+ &nhp))
+ continue;
+
+ /*
+ * per-nve-group prefix list check
+ */
+ if (rfgn->rfg->plist_export_bgp[afi]) {
+ if (prefix_list_apply(
+ rfgn->rfg->plist_export_bgp
+ [afi],
+ p)
+ == PREFIX_DENY)
+
+ continue;
+ }
+
+
+ /*
+ * Construct new attribute set with
+ * NVE's VN addr as
+ * nexthop and without Tunnel Encap attr
+ */
+ if (encap_attr_export(&hattr, &attr,
+ &nhp, rn))
+ continue;
+
+ if (rfgn->rfg->routemap_export_bgp) {
+ route_map_result_t ret;
+ info.peer = irfd->peer;
+ info.attr = &hattr;
+ ret = route_map_apply(
+ rfgn->rfg
+ ->routemap_export_bgp,
+ p, &info);
+ if (ret == RMAP_DENYMATCH) {
+ bgp_attr_flush(&hattr);
+ continue;
+ }
+ }
+
+ iattr = bgp_attr_intern(&hattr);
+ bgp_attr_flush(&hattr);
+ bgp_update(
+ irfd->peer, p, /* prefix */
+ 0, /* addpath_id */
+ iattr, /* bgp_update copies
+ it */
+ afi, SAFI_UNICAST,
+ ZEBRA_ROUTE_VNC_DIRECT,
+ BGP_ROUTE_REDISTRIBUTE, NULL,
+ /* RD not used for unicast */
+ NULL,
+ /* tag not used for unicast */
+ 0, 0, NULL); /* EVPN not used */
+
+ bgp_attr_unintern(&iattr);
+ }
+ }
+
+ aspath_unintern(&attr.aspath);
+ }
+ }
+}
+
+
+void vnc_direct_bgp_del_nve(struct bgp *bgp, struct rfapi_descriptor *rfd)
+{
+ struct listnode *node, *nnode;
+ struct rfapi_rfg_name *rfgn;
+ struct rfapi_nve_group_cfg *rfg = rfd->rfg;
+ afi_t afi = family2afi(rfd->vn_addr.addr_family);
+
+ if (!afi) {
+ flog_err(EC_LIB_DEVELOPMENT, "%s: can't get afi of nve vn addr",
+ __func__);
+ return;
+ }
+
+ if (!bgp)
+ return;
+ if (!bgp->rfapi_cfg) {
+ vnc_zlog_debug_verbose("%s: bgp->rfapi_cfg is NULL, skipping",
+ __func__);
+ return;
+ }
+ if (!VNC_EXPORT_BGP_GRP_ENABLED(bgp->rfapi_cfg)) {
+ vnc_zlog_debug_verbose(
+ "%s: export-to-bgp group mode not enabled, skipping",
+ __func__);
+ return;
+ }
+
+ if (!bgp->redist[afi][ZEBRA_ROUTE_VNC_DIRECT]) {
+ vnc_zlog_debug_verbose(
+ "%s: bgp redistribution of VNC direct routes is off",
+ __func__);
+ return;
+ }
+
+ /*
+ * Loop over the list of NVE-Groups configured for
+ * exporting to direct-bgp and see if this new NVE's
+ * group is among them.
+ */
+ for (ALL_LIST_ELEMENTS(bgp->rfapi_cfg->rfg_export_direct_bgp_l, node,
+ nnode, rfgn)) {
+
+ /*
+ * Yes, this NVE's group is configured for export to direct-bgp
+ */
+ if (rfg && rfgn->rfg == rfg) {
+
+ struct agg_table *rt = NULL;
+ struct agg_node *rn;
+ struct rfapi_import_table *import_table;
+
+ import_table = rfg->rfapi_import_table;
+
+ if (afi == AFI_IP || afi == AFI_IP6) {
+ rt = import_table->imported_vpn[afi];
+ } else {
+ flog_err(EC_LIB_DEVELOPMENT, "%s: bad afi %d",
+ __func__, afi);
+ return;
+ }
+
+ /*
+ * Walk the NVE-Group's VNC Import table
+ */
+ for (rn = agg_route_top(rt); rn;
+ rn = agg_route_next(rn)) {
+
+ if (rn->info) {
+ const struct prefix *p =
+ agg_node_get_prefix(rn);
+ struct prefix nhp;
+ struct rfapi_descriptor *irfd = rfd;
+
+ if (rfapiRaddr2Qprefix(&irfd->vn_addr,
+ &nhp))
+ continue;
+
+ bgp_withdraw(irfd->peer, p, /* prefix */
+ 0, /* addpath_id */
+ NULL, /* attr, ignored */
+ afi, SAFI_UNICAST,
+ ZEBRA_ROUTE_VNC_DIRECT,
+ BGP_ROUTE_REDISTRIBUTE,
+ NULL, /* RD not used for
+ unicast */
+ NULL, 0, NULL); /* tag not
+ used for
+ unicast */
+ }
+ }
+ }
+ }
+}
+
+static void vnc_direct_add_rn_group_rd(struct bgp *bgp,
+ struct rfapi_nve_group_cfg *rfg,
+ struct agg_node *rn, struct attr *attr,
+ afi_t afi, struct rfapi_descriptor *irfd)
+{
+ struct prefix nhp;
+ struct bgp_path_info info;
+ struct attr hattr;
+ struct attr *iattr;
+ const struct prefix *p = agg_node_get_prefix(rn);
+
+ if (irfd == NULL && rfg->type != RFAPI_GROUP_CFG_VRF) {
+ /* need new rfapi_handle, for peer strcture
+ * -- based on vnc_add_vrf_prefi */
+ assert(rfg->rfd == NULL);
+
+ if (!rfg->rt_export_list || !rfg->rfapi_import_table) {
+ vnc_zlog_debug_verbose(
+ "%s: VRF \"%s\" is missing RT import/export configuration.",
+ __func__, rfg->name);
+ return;
+ }
+ if (!rfg->rd.prefixlen) {
+ vnc_zlog_debug_verbose(
+ "%s: VRF \"%s\" is missing RD configuration.",
+ __func__, rfg->name);
+ return;
+ }
+ if (rfg->label > MPLS_LABEL_MAX) {
+ vnc_zlog_debug_verbose(
+ "%s: VRF \"%s\" is missing default label configuration.",
+ __func__, rfg->name);
+ return;
+ }
+
+ irfd = XCALLOC(MTYPE_RFAPI_DESC,
+ sizeof(struct rfapi_descriptor));
+ irfd->bgp = bgp;
+ rfg->rfd = irfd;
+ /*
+ * leave most fields empty as will get from (dynamic) config
+ * when needed
+ */
+ irfd->default_tunneltype_option.type = BGP_ENCAP_TYPE_MPLS;
+ irfd->cookie = rfg;
+ if (rfg->vn_prefix.family
+ && !CHECK_FLAG(rfg->flags, RFAPI_RFG_VPN_NH_SELF)) {
+ rfapiQprefix2Raddr(&rfg->vn_prefix, &irfd->vn_addr);
+ } else {
+ memset(&irfd->vn_addr, 0, sizeof(struct rfapi_ip_addr));
+ irfd->vn_addr.addr_family = AF_INET;
+ irfd->vn_addr.addr.v4 = bgp->router_id;
+ }
+ irfd->un_addr = irfd->vn_addr; /* sigh, need something in UN for
+ lookups */
+ vnc_zlog_debug_verbose("%s: Opening RFD for VRF %s", __func__,
+ rfg->name);
+ rfapi_init_and_open(bgp, irfd, rfg);
+ }
+
+ if (irfd == NULL || rfapiRaddr2Qprefix(&irfd->vn_addr, &nhp))
+ return;
+
+ /*
+ * Construct new attribute set with NVE's VN
+ * addr as
+ * nexthop and without Tunnel Encap attr
+ */
+ if (encap_attr_export(&hattr, attr, &nhp, rn))
+ return;
+
+ if (VNC_DEBUG(EXPORT_BGP_DIRECT_ADD)) {
+ vnc_zlog_debug_any("%s: attr follows", __func__);
+ rfapiPrintAttrPtrs(NULL, attr);
+ vnc_zlog_debug_any("%s: hattr follows", __func__);
+ rfapiPrintAttrPtrs(NULL, &hattr);
+ }
+
+ if (rfg->routemap_export_bgp) {
+ route_map_result_t ret;
+
+ info.peer = irfd->peer;
+ info.attr = &hattr;
+ ret = route_map_apply(rfg->routemap_export_bgp, p, &info);
+ if (ret == RMAP_DENYMATCH) {
+ bgp_attr_flush(&hattr);
+ vnc_zlog_debug_verbose(
+ "%s: route map says DENY, so not calling bgp_update",
+ __func__);
+ return;
+ }
+ }
+
+ if (VNC_DEBUG(EXPORT_BGP_DIRECT_ADD)) {
+ vnc_zlog_debug_any("%s: hattr after route_map_apply:",
+ __func__);
+ rfapiPrintAttrPtrs(NULL, &hattr);
+ }
+ iattr = bgp_attr_intern(&hattr);
+ bgp_attr_flush(&hattr);
+
+ bgp_update(irfd->peer, p, /* prefix */
+ 0, /* addpath_id */
+ iattr, /* bgp_update copies it */
+ afi, SAFI_UNICAST, ZEBRA_ROUTE_VNC_DIRECT,
+ BGP_ROUTE_REDISTRIBUTE, NULL, /* RD not used for unicast */
+ NULL, /* tag not used for unicast */
+ 0, 0, NULL); /* EVPN not used */
+
+ bgp_attr_unintern(&iattr);
+
+ return;
+}
+
+/*
+ * Caller is responsible for ensuring that the specified nve-group
+ * is actually part of the list of exported nve groups.
+ */
+static void vnc_direct_bgp_add_group_afi(struct bgp *bgp,
+ struct rfapi_nve_group_cfg *rfg,
+ afi_t afi)
+{
+ struct agg_table *rt = NULL;
+ struct agg_node *rn;
+ struct attr attr = {0};
+ struct rfapi_import_table *import_table;
+
+ vnc_zlog_debug_verbose("%s: entry", __func__);
+
+ import_table = rfg->rfapi_import_table;
+ if (!import_table) {
+ vnc_zlog_debug_verbose(
+ "%s: import table not defined, returning", __func__);
+ return;
+ }
+
+ if (afi == AFI_IP || afi == AFI_IP6) {
+ rt = import_table->imported_vpn[afi];
+ } else {
+ flog_err(EC_LIB_DEVELOPMENT, "%s: bad afi %d", __func__, afi);
+ return;
+ }
+
+ if (!rfg->nves && rfg->type != RFAPI_GROUP_CFG_VRF) {
+ vnc_zlog_debug_verbose("%s: no NVEs in this group", __func__);
+ return;
+ }
+
+ bgp_attr_default_set(&attr, bgp, BGP_ORIGIN_INCOMPLETE);
+ /* TBD set some configured med, see add_vnc_route() */
+
+ /*
+ * Walk the NVE-Group's VNC Import table
+ */
+ for (rn = agg_route_top(rt); rn; rn = agg_route_next(rn)) {
+
+ if (rn->info) {
+ const struct prefix *p = agg_node_get_prefix(rn);
+ struct listnode *ln;
+
+ /*
+ * per-nve-group prefix list check
+ */
+ if (rfg->plist_export_bgp[afi]) {
+ if (prefix_list_apply(
+ rfg->plist_export_bgp[afi], p)
+ == PREFIX_DENY)
+
+ continue;
+ }
+ if (rfg->type == RFAPI_GROUP_CFG_VRF) {
+ vnc_direct_add_rn_group_rd(bgp, rfg, rn, &attr,
+ afi, rfg->rfd);
+ /*
+ * yuck!
+ * - but consistent with rest of function
+ */
+ continue;
+ }
+ /*
+ * For each NVE that is assigned to the export nve
+ * group, generate
+ * a route with that NVE as its next hop
+ */
+ for (ln = listhead(rfg->nves); ln;
+ ln = listnextnode(ln)) {
+ vnc_direct_add_rn_group_rd(bgp, rfg, rn, &attr,
+ afi,
+ listgetdata(ln));
+ }
+ }
+ }
+
+ aspath_unintern(&attr.aspath);
+}
+
+
+/*
+ * Caller is responsible for ensuring that the specified nve-group
+ * is actually part of the list of exported nve groups.
+ */
+void vnc_direct_bgp_add_group(struct bgp *bgp, struct rfapi_nve_group_cfg *rfg)
+{
+ vnc_direct_bgp_add_group_afi(bgp, rfg, AFI_IP);
+ vnc_direct_bgp_add_group_afi(bgp, rfg, AFI_IP6);
+}
+
+static void vnc_direct_del_rn_group_rd(struct bgp *bgp,
+ struct rfapi_nve_group_cfg *rfg,
+ struct agg_node *rn, afi_t afi,
+ struct rfapi_descriptor *irfd)
+{
+ if (irfd == NULL)
+ return;
+
+ bgp_withdraw(irfd->peer, agg_node_get_prefix(rn), /* prefix */
+ 0, /* addpath_id */
+ NULL, /* attr, ignored */
+ afi, SAFI_UNICAST, ZEBRA_ROUTE_VNC_DIRECT,
+ BGP_ROUTE_REDISTRIBUTE, NULL, /* RD not used for unicast */
+ NULL, 0, NULL); /* tag not used for unicast */
+ return;
+}
+
+/*
+ * Caller is responsible for ensuring that the specified nve-group
+ * was actually part of the list of exported nve groups.
+ */
+static void vnc_direct_bgp_del_group_afi(struct bgp *bgp,
+ struct rfapi_nve_group_cfg *rfg,
+ afi_t afi)
+{
+ struct agg_table *rt = NULL;
+ struct agg_node *rn;
+ struct rfapi_import_table *import_table;
+
+ vnc_zlog_debug_verbose("%s: entry", __func__);
+
+ import_table = rfg->rfapi_import_table;
+ if (!import_table) {
+ vnc_zlog_debug_verbose(
+ "%s: import table not defined, returning", __func__);
+ return;
+ }
+
+ rt = import_table->imported_vpn[afi];
+
+ if (!rfg->nves && rfg->type != RFAPI_GROUP_CFG_VRF) {
+ vnc_zlog_debug_verbose("%s: no NVEs in this group", __func__);
+ return;
+ }
+
+ /*
+ * Walk the NVE-Group's VNC Import table
+ */
+ for (rn = agg_route_top(rt); rn; rn = agg_route_next(rn))
+ if (rn->info) {
+ if (rfg->type == RFAPI_GROUP_CFG_VRF)
+ vnc_direct_del_rn_group_rd(bgp, rfg, rn, afi,
+ rfg->rfd);
+ else {
+ struct listnode *ln;
+
+ /*
+ * For each NVE that is assigned to the export
+ * nve
+ * group, generate
+ * a route with that NVE as its next hop
+ */
+ for (ln = listhead(rfg->nves); ln;
+ ln = listnextnode(ln))
+ vnc_direct_del_rn_group_rd(
+ bgp, rfg, rn, afi,
+ listgetdata(ln));
+ }
+ }
+}
+
+/*
+ * Caller is responsible for ensuring that the specified nve-group
+ * was actually part of the list of exported nve groups.
+ */
+void vnc_direct_bgp_del_group(struct bgp *bgp, struct rfapi_nve_group_cfg *rfg)
+{
+ vnc_direct_bgp_del_group_afi(bgp, rfg, AFI_IP);
+ vnc_direct_bgp_del_group_afi(bgp, rfg, AFI_IP6);
+}
+
+void vnc_direct_bgp_reexport_group_afi(struct bgp *bgp,
+ struct rfapi_nve_group_cfg *rfg,
+ afi_t afi)
+{
+ struct listnode *node;
+ struct rfapi_rfg_name *rfgn;
+
+ if (VNC_EXPORT_BGP_GRP_ENABLED(bgp->rfapi_cfg)) {
+ /*
+ * look in the list of currently-exported groups
+ */
+ for (ALL_LIST_ELEMENTS_RO(
+ bgp->rfapi_cfg->rfg_export_direct_bgp_l, node,
+ rfgn)) {
+
+ if (rfgn->rfg == rfg) {
+ /*
+ * If it matches, reexport it
+ */
+ vnc_direct_bgp_del_group_afi(bgp, rfg, afi);
+ vnc_direct_bgp_add_group_afi(bgp, rfg, afi);
+ break;
+ }
+ }
+ }
+}
+
+
+static void vnc_direct_bgp_unexport_table(afi_t afi, struct agg_table *rt,
+ struct list *nve_list)
+{
+ if (nve_list) {
+
+ struct agg_node *rn;
+
+ for (rn = agg_route_top(rt); rn; rn = agg_route_next(rn)) {
+
+ if (rn->info) {
+
+ struct listnode *hln;
+ struct rfapi_descriptor *irfd;
+
+ for (ALL_LIST_ELEMENTS_RO(nve_list, hln,
+ irfd)) {
+
+ bgp_withdraw(irfd->peer,
+ agg_node_get_prefix(rn),
+ 0, /* addpath_id */
+ NULL, /* attr, ignored */
+ afi, SAFI_UNICAST,
+ ZEBRA_ROUTE_VNC_DIRECT,
+ BGP_ROUTE_REDISTRIBUTE,
+ NULL, /* RD not used for
+ unicast */
+ NULL, 0, NULL); /* tag not
+ used for
+ unicast,
+ EVPN
+ neither */
+ }
+ }
+ }
+ }
+}
+
+static void import_table_to_nve_list_direct_bgp(struct bgp *bgp,
+ struct rfapi_import_table *it,
+ struct list **nves,
+ uint8_t family)
+{
+ struct listnode *node;
+ struct rfapi_rfg_name *rfgn;
+
+ /*
+ * Loop over the list of NVE-Groups configured for
+ * exporting to direct-bgp.
+ *
+ * Build a list of NVEs that use this import table
+ */
+ *nves = NULL;
+ for (ALL_LIST_ELEMENTS_RO(bgp->rfapi_cfg->rfg_export_direct_bgp_l, node,
+ rfgn)) {
+
+ /*
+ * If this NVE-Group's import table matches the current one
+ */
+ if (rfgn->rfg && rfgn->rfg->rfapi_import_table == it) {
+ if (rfgn->rfg->nves)
+ nve_group_to_nve_list(rfgn->rfg, nves, family);
+ else if (rfgn->rfg->rfd
+ && rfgn->rfg->type == RFAPI_GROUP_CFG_VRF) {
+ if (!*nves)
+ *nves = list_new();
+ listnode_add(*nves, rfgn->rfg->rfd);
+ }
+ }
+ }
+}
+
+void vnc_direct_bgp_vpn_enable(struct bgp *bgp, afi_t afi)
+{
+ struct listnode *rfgn;
+ struct rfapi_nve_group_cfg *rfg;
+
+ if (!bgp)
+ return;
+
+ if (!VNC_EXPORT_BGP_GRP_ENABLED(bgp->rfapi_cfg)) {
+ vnc_zlog_debug_verbose(
+ "%s: export-to-bgp group mode not enabled, skipping",
+ __func__);
+ return;
+ }
+
+ if (afi != AFI_IP && afi != AFI_IP6) {
+ vnc_zlog_debug_verbose("%s: bad afi: %d", __func__, afi);
+ return;
+ }
+
+ /*
+ * Policy is applied per-nve-group, so we need to iterate
+ * over the groups to add everything.
+ */
+ for (ALL_LIST_ELEMENTS_RO(bgp->rfapi_cfg->nve_groups_sequential, rfgn,
+ rfg)) {
+
+ /*
+ * contains policy management
+ */
+ vnc_direct_bgp_add_group_afi(bgp, rfg, afi);
+ }
+}
+
+
+void vnc_direct_bgp_vpn_disable(struct bgp *bgp, afi_t afi)
+{
+ struct rfapi_import_table *it;
+ uint8_t family = afi2family(afi);
+
+ vnc_zlog_debug_verbose("%s: entry, afi=%d", __func__, afi);
+
+ if (!bgp)
+ return;
+
+ if (!bgp->rfapi) {
+ vnc_zlog_debug_verbose("%s: rfapi not initialized", __func__);
+ return;
+ }
+
+ if (!family || (afi != AFI_IP && afi != AFI_IP6)) {
+ vnc_zlog_debug_verbose("%s: bad afi: %d", __func__, afi);
+ return;
+ }
+
+ for (it = bgp->rfapi->imports; it; it = it->next) {
+
+ struct list *nve_list = NULL;
+
+ import_table_to_nve_list_direct_bgp(bgp, it, &nve_list, family);
+
+ if (nve_list) {
+ vnc_direct_bgp_unexport_table(
+ afi, it->imported_vpn[afi], nve_list);
+ list_delete(&nve_list);
+ }
+ }
+}
+
+
+/***********************************************************************
+ * Export methods that proxy nexthop END
+ ***********************************************************************/
+
+
+/***********************************************************************
+ * Export methods that preserve original nexthop BEGIN
+ * rh = "registering nve"
+ ***********************************************************************/
+
+
+/*
+ * "Adding a Route" export process
+ * TBD do we need to check bpi->type and bpi->sub_type here, or does
+ * caller do it?
+ */
+void vnc_direct_bgp_rh_add_route(struct bgp *bgp, afi_t afi,
+ const struct prefix *prefix, struct peer *peer,
+ struct attr *attr)
+{
+ struct vnc_export_info *eti;
+ struct attr hattr;
+ struct rfapi_cfg *hc;
+ struct attr *iattr;
+
+ if (!afi) {
+ flog_err(EC_LIB_DEVELOPMENT, "%s: can't get afi of route node",
+ __func__);
+ return;
+ }
+
+ /* check bgp redist flag for vnc direct ("vpn") routes */
+ if (!bgp->redist[afi][ZEBRA_ROUTE_VNC_DIRECT]) {
+ vnc_zlog_debug_verbose(
+ "%s: bgp redistribution of VNC direct routes is off",
+ __func__);
+ return;
+ }
+
+ if (!(hc = bgp->rfapi_cfg)) {
+ vnc_zlog_debug_verbose("%s: bgp->rfapi_cfg is NULL, skipping",
+ __func__);
+ return;
+ }
+
+ if (!VNC_EXPORT_BGP_RH_ENABLED(bgp->rfapi_cfg)) {
+ vnc_zlog_debug_verbose(
+ "%s: export-to-bgp RH mode not enabled, skipping",
+ __func__);
+ return;
+ }
+
+ /*
+ * prefix list check
+ */
+ if (hc->plist_export_bgp[afi]) {
+ if (prefix_list_apply(hc->plist_export_bgp[afi], prefix)
+ == PREFIX_DENY)
+ return;
+ }
+
+ /*
+ * Construct new attribute set with NVE's VN addr as
+ * nexthop and without Tunnel Encap attr
+ */
+ if (encap_attr_export(&hattr, attr, NULL, NULL))
+ return;
+ if (hc->routemap_export_bgp) {
+ struct bgp_path_info info;
+ route_map_result_t ret;
+
+ memset(&info, 0, sizeof(info));
+ info.peer = peer;
+ info.attr = &hattr;
+ ret = route_map_apply(hc->routemap_export_bgp, prefix, &info);
+ if (ret == RMAP_DENYMATCH) {
+ bgp_attr_flush(&hattr);
+ return;
+ }
+ }
+
+ iattr = bgp_attr_intern(&hattr);
+ bgp_attr_flush(&hattr);
+
+ /*
+ * record route information that we will need to expire
+ * this route
+ */
+ eti = vnc_eti_get(bgp, EXPORT_TYPE_BGP, prefix, peer,
+ ZEBRA_ROUTE_VNC_DIRECT_RH, BGP_ROUTE_REDISTRIBUTE);
+ rfapiGetVncLifetime(attr, &eti->lifetime);
+ eti->lifetime = rfapiGetHolddownFromLifetime(eti->lifetime);
+
+ /*
+ * export expiration timer is already running on
+ * this route: cancel it
+ */
+ THREAD_OFF(eti->timer);
+
+ bgp_update(peer, prefix, /* prefix */
+ 0, /* addpath_id */
+ iattr, /* bgp_update copies this attr */
+ afi, SAFI_UNICAST, ZEBRA_ROUTE_VNC_DIRECT_RH,
+ BGP_ROUTE_REDISTRIBUTE, NULL, /* RD not used for unicast */
+ NULL, /* tag not used for unicast, EVPN neither */
+ 0, 0, NULL); /* EVPN not used */
+ bgp_attr_unintern(&iattr);
+}
+
+static void vncExportWithdrawTimer(struct thread *t)
+{
+ struct vnc_export_info *eti = THREAD_ARG(t);
+ const struct prefix *p = agg_node_get_prefix(eti->node);
+
+ /*
+ * withdraw the route
+ */
+ bgp_withdraw(eti->peer, p, 0, /* addpath_id */
+ NULL, /* attr, ignored */
+ family2afi(p->family), SAFI_UNICAST, eti->type,
+ eti->subtype, NULL, /* RD not used for unicast */
+ NULL, 0,
+ NULL); /* tag not used for unicast, EVPN neither */
+
+ /*
+ * Free the eti
+ */
+ vnc_eti_delete(eti);
+}
+
+/*
+ * "Withdrawing a Route" export process
+ * TBD do we need to check bpi->type and bpi->sub_type here, or does
+ * caller do it?
+ */
+void vnc_direct_bgp_rh_del_route(struct bgp *bgp, afi_t afi,
+ const struct prefix *prefix, struct peer *peer)
+{
+ struct vnc_export_info *eti;
+
+ if (!afi) {
+ flog_err(EC_LIB_DEVELOPMENT, "%s: can't get afi route node",
+ __func__);
+ return;
+ }
+
+ /* check bgp redist flag for vnc direct ("vpn") routes */
+ if (!bgp->redist[afi][ZEBRA_ROUTE_VNC_DIRECT]) {
+ vnc_zlog_debug_verbose(
+ "%s: bgp redistribution of VNC direct routes is off",
+ __func__);
+ return;
+ }
+
+ if (!bgp->rfapi_cfg) {
+ vnc_zlog_debug_verbose("%s: bgp->rfapi_cfg is NULL, skipping",
+ __func__);
+ return;
+ }
+ if (!VNC_EXPORT_BGP_RH_ENABLED(bgp->rfapi_cfg)) {
+ vnc_zlog_debug_verbose(
+ "%s: export-to-bgp group mode not enabled, skipping",
+ __func__);
+ return;
+ }
+
+ eti = vnc_eti_get(bgp, EXPORT_TYPE_BGP, prefix, peer,
+ ZEBRA_ROUTE_VNC_DIRECT_RH, BGP_ROUTE_REDISTRIBUTE);
+
+ if (!eti->timer && eti->lifetime <= INT32_MAX) {
+ eti->timer = NULL;
+ thread_add_timer(bm->master, vncExportWithdrawTimer, eti,
+ eti->lifetime, &eti->timer);
+ vnc_zlog_debug_verbose(
+ "%s: set expiration timer for %u seconds", __func__,
+ eti->lifetime);
+ }
+}
+
+
+void vnc_direct_bgp_rh_vpn_enable(struct bgp *bgp, afi_t afi)
+{
+ struct prefix_rd prd;
+ struct bgp_dest *pdest;
+ struct rfapi_cfg *hc;
+
+ vnc_zlog_debug_verbose("%s: entry, afi=%d", __func__, afi);
+
+ if (!bgp)
+ return;
+
+ if (!(hc = bgp->rfapi_cfg))
+ return;
+
+ if (!VNC_EXPORT_BGP_RH_ENABLED(bgp->rfapi_cfg)) {
+ vnc_zlog_debug_verbose(
+ "%s: export of RH routes not enabled, skipping",
+ __func__);
+ return;
+ }
+
+ if (afi != AFI_IP && afi != AFI_IP6) {
+ vnc_zlog_debug_verbose("%s: bad afi: %d", __func__, afi);
+ return;
+ }
+
+ /*
+ * Go through the entire BGP VPN table and export to BGP unicast.
+ */
+
+ vnc_zlog_debug_verbose("%s: starting RD loop", __func__);
+
+ /* Loop over all the RDs */
+ for (pdest = bgp_table_top(bgp->rib[afi][SAFI_MPLS_VPN]); pdest;
+ pdest = bgp_route_next(pdest)) {
+
+ struct bgp_table *table;
+ struct bgp_dest *dest;
+ struct bgp_path_info *ri;
+ const struct prefix *pdest_p = bgp_dest_get_prefix(pdest);
+
+ memset(&prd, 0, sizeof(prd));
+ prd.family = AF_UNSPEC;
+ prd.prefixlen = 64;
+ memcpy(prd.val, pdest_p->u.val, 8);
+
+ /* This is the per-RD table of prefixes */
+ table = bgp_dest_get_bgp_table_info(pdest);
+
+ if (!table)
+ continue;
+
+ for (dest = bgp_table_top(table); dest;
+ dest = bgp_route_next(dest)) {
+ const struct prefix *dest_p;
+
+ /*
+ * skip prefix list check if no routes here
+ */
+ if (!bgp_dest_has_bgp_path_info_data(dest))
+ continue;
+
+ vnc_zlog_debug_verbose("%s: checking prefix %pBD",
+ __func__, dest);
+
+ dest_p = bgp_dest_get_prefix(dest);
+
+ /*
+ * prefix list check
+ */
+ if (hc->plist_export_bgp[afi]) {
+ if (prefix_list_apply(hc->plist_export_bgp[afi],
+ dest_p)
+ == PREFIX_DENY) {
+
+ vnc_zlog_debug_verbose(
+ "%s: prefix list says DENY",
+ __func__);
+ continue;
+ }
+ }
+
+ for (ri = bgp_dest_get_bgp_path_info(dest); ri;
+ ri = ri->next) {
+
+ vnc_zlog_debug_verbose("%s: ri->sub_type: %d",
+ __func__, ri->sub_type);
+
+ if (ri->sub_type == BGP_ROUTE_NORMAL
+ || ri->sub_type == BGP_ROUTE_RFP) {
+
+ struct vnc_export_info *eti;
+ struct attr hattr;
+ struct attr *iattr;
+
+ /*
+ * Construct new attribute set with
+ * NVE's VN addr as
+ * nexthop and without Tunnel Encap attr
+ */
+ if (encap_attr_export(&hattr, ri->attr,
+ NULL, NULL)) {
+ vnc_zlog_debug_verbose(
+ "%s: encap_attr_export failed",
+ __func__);
+ continue;
+ }
+
+ if (hc->routemap_export_bgp) {
+ struct bgp_path_info info;
+ route_map_result_t ret;
+
+ memset(&info, 0, sizeof(info));
+ info.peer = ri->peer;
+ info.attr = &hattr;
+ ret = route_map_apply(
+ hc->routemap_export_bgp,
+ dest_p, &info);
+ if (ret == RMAP_DENYMATCH) {
+ bgp_attr_flush(&hattr);
+ vnc_zlog_debug_verbose(
+ "%s: route map says DENY",
+ __func__);
+ continue;
+ }
+ }
+
+ iattr = bgp_attr_intern(&hattr);
+ bgp_attr_flush(&hattr);
+
+ /*
+ * record route information that we will
+ * need to expire
+ * this route
+ */
+ eti = vnc_eti_get(
+ bgp, EXPORT_TYPE_BGP, dest_p,
+ ri->peer,
+ ZEBRA_ROUTE_VNC_DIRECT_RH,
+ BGP_ROUTE_REDISTRIBUTE);
+ rfapiGetVncLifetime(ri->attr,
+ &eti->lifetime);
+
+ /*
+ * export expiration timer is
+ * already running on
+ * this route: cancel it
+ */
+ THREAD_OFF(eti->timer);
+
+ vnc_zlog_debug_verbose(
+ "%s: calling bgp_update",
+ __func__);
+
+ bgp_update(
+ ri->peer, dest_p, /* prefix */
+ 0, /* addpath_id */
+ iattr, /* bgp_update copies
+ it */
+ AFI_IP, SAFI_UNICAST,
+ ZEBRA_ROUTE_VNC_DIRECT_RH,
+ BGP_ROUTE_REDISTRIBUTE, NULL,
+ /* RD not used for unicast */
+ NULL,
+ /* tag not used for unicast,
+ or EVPN */
+ 0, 0, NULL); /* EVPN not used */
+
+ bgp_attr_unintern(&iattr);
+ }
+ }
+ }
+ }
+}
+
+void vnc_direct_bgp_rh_vpn_disable(struct bgp *bgp, afi_t afi)
+{
+ struct bgp_dest *dest;
+
+ vnc_zlog_debug_verbose("%s: entry, afi=%d", __func__, afi);
+
+ if (!bgp)
+ return;
+
+ if (afi != AFI_IP && afi != AFI_IP6) {
+ vnc_zlog_debug_verbose("%s: bad afi: %d", __func__, afi);
+ return;
+ }
+
+ /*
+ * Go through the entire BGP unicast table and remove routes that
+ * originated from us
+ */
+ for (dest = bgp_table_top(bgp->rib[afi][SAFI_UNICAST]); dest;
+ dest = bgp_route_next(dest)) {
+ const struct prefix *dest_p = bgp_dest_get_prefix(dest);
+ struct bgp_path_info *ri;
+ struct bgp_path_info *next;
+
+ for (ri = bgp_dest_get_bgp_path_info(dest), next = NULL; ri;
+ ri = next) {
+
+ next = ri->next;
+
+ if (ri->type == ZEBRA_ROUTE_VNC_DIRECT_RH
+ && ri->sub_type == BGP_ROUTE_REDISTRIBUTE) {
+
+ struct vnc_export_info *eti;
+
+ /*
+ * Delete routes immediately (no timer)
+ */
+ eti = vnc_eti_checktimer(
+ bgp, EXPORT_TYPE_BGP, dest_p, ri->peer,
+ ZEBRA_ROUTE_VNC_DIRECT_RH,
+ BGP_ROUTE_REDISTRIBUTE);
+ if (eti) {
+ THREAD_OFF(eti->timer);
+ vnc_eti_delete(eti);
+ }
+
+ bgp_withdraw(ri->peer, dest_p, /* prefix */
+ 0, /* addpath_id */
+ NULL, /* ignored */
+ AFI_IP, SAFI_UNICAST,
+ ZEBRA_ROUTE_VNC_DIRECT_RH,
+ BGP_ROUTE_REDISTRIBUTE,
+ NULL, /* RD not used for unicast */
+ NULL, 0, NULL); /* tag not used for
+ unicast, EVPN
+ neither */
+ }
+ }
+ }
+}
+
+void vnc_direct_bgp_rh_reexport(struct bgp *bgp, afi_t afi)
+{
+ if (VNC_EXPORT_BGP_RH_ENABLED(bgp->rfapi_cfg)) {
+ vnc_direct_bgp_rh_vpn_disable(bgp, afi);
+ vnc_direct_bgp_rh_vpn_enable(bgp, afi);
+ }
+}
+
+/***********************************************************************
+ * Generic Export methods
+ ***********************************************************************/
+
+/*
+ * Assumes the correct mode bits are already turned on. Thus it
+ * is OK to call this function from, e.g., bgp_redistribute_set()
+ * without caring if export is enabled or not
+ */
+void vnc_export_bgp_enable(struct bgp *bgp, afi_t afi)
+{
+ if (!bgp->rfapi_cfg)
+ return;
+
+ switch (bgp->rfapi_cfg->flags & BGP_VNC_CONFIG_EXPORT_BGP_MODE_BITS) {
+ case BGP_VNC_CONFIG_EXPORT_BGP_MODE_NONE:
+ break;
+
+ case BGP_VNC_CONFIG_EXPORT_BGP_MODE_GRP:
+ vnc_direct_bgp_vpn_enable(bgp, afi);
+ break;
+
+ case BGP_VNC_CONFIG_EXPORT_BGP_MODE_RH:
+ vnc_direct_bgp_rh_vpn_enable(bgp, afi);
+ break;
+
+ case BGP_VNC_CONFIG_EXPORT_BGP_MODE_CE:
+ vnc_direct_bgp_vpn_enable_ce(bgp, afi);
+ break;
+ }
+}
+
+void vnc_export_bgp_disable(struct bgp *bgp, afi_t afi)
+{
+ if (!bgp->rfapi_cfg)
+ return;
+
+ switch (bgp->rfapi_cfg->flags & BGP_VNC_CONFIG_EXPORT_BGP_MODE_BITS) {
+ case BGP_VNC_CONFIG_EXPORT_BGP_MODE_NONE:
+ break;
+
+ case BGP_VNC_CONFIG_EXPORT_BGP_MODE_GRP:
+ vnc_direct_bgp_vpn_disable(bgp, afi);
+ break;
+
+ case BGP_VNC_CONFIG_EXPORT_BGP_MODE_RH:
+ vnc_direct_bgp_rh_vpn_disable(bgp, afi);
+ break;
+
+ case BGP_VNC_CONFIG_EXPORT_BGP_MODE_CE:
+ vnc_direct_bgp_vpn_disable_ce(bgp, afi);
+ break;
+ }
+}
+
+void vnc_export_bgp_prechange(struct bgp *bgp)
+{
+ vnc_export_bgp_disable(bgp, AFI_IP);
+ vnc_export_bgp_disable(bgp, AFI_IP6);
+}
+
+void vnc_export_bgp_postchange(struct bgp *bgp)
+{
+ vnc_export_bgp_enable(bgp, AFI_IP);
+ vnc_export_bgp_enable(bgp, AFI_IP6);
+}
+
+void vnc_direct_bgp_reexport(struct bgp *bgp, afi_t afi)
+{
+ vnc_export_bgp_disable(bgp, afi);
+ vnc_export_bgp_enable(bgp, afi);
+}
diff --git a/bgpd/rfapi/vnc_export_bgp.h b/bgpd/rfapi/vnc_export_bgp.h
new file mode 100644
index 0000000..a6f8c82
--- /dev/null
+++ b/bgpd/rfapi/vnc_export_bgp.h
@@ -0,0 +1,41 @@
+/*
+ *
+ * Copyright 2009-2016, LabN Consulting, L.L.C.
+ *
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program; see the file COPYING; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
+ */
+
+#ifndef _QUAGGA_RFAPI_VNC_EXPORT_BGP_H_
+#define _QUAGGA_RFAPI_VNC_EXPORT_BGP_H_
+
+#include "lib/zebra.h"
+#include "lib/prefix.h"
+
+#include "bgpd/bgpd.h"
+#include "bgpd/bgp_route.h"
+
+
+extern void vnc_direct_bgp_rh_reexport(struct bgp *bgp, afi_t afi);
+
+extern void vnc_export_bgp_prechange(struct bgp *bgp);
+
+extern void vnc_export_bgp_postchange(struct bgp *bgp);
+
+extern void vnc_export_bgp_enable(struct bgp *bgp, afi_t afi);
+
+extern void vnc_export_bgp_disable(struct bgp *bgp, afi_t afi);
+
+#endif /* _QUAGGA_RFAPI_VNC_EXPORT_BGP_H_ */
diff --git a/bgpd/rfapi/vnc_export_bgp_p.h b/bgpd/rfapi/vnc_export_bgp_p.h
new file mode 100644
index 0000000..bf292ab
--- /dev/null
+++ b/bgpd/rfapi/vnc_export_bgp_p.h
@@ -0,0 +1,74 @@
+/*
+ *
+ * Copyright 2009-2016, LabN Consulting, L.L.C.
+ *
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program; see the file COPYING; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
+ */
+
+#ifndef _QUAGGA_RFAPI_VNC_EXPORT_BGP_P_H_
+#define _QUAGGA_RFAPI_VNC_EXPORT_BGP_P_H_
+
+#include "lib/zebra.h"
+#include "lib/prefix.h"
+
+#include "bgpd/bgpd.h"
+#include "bgpd/bgp_route.h"
+
+#include "rfapi_private.h"
+
+extern void vnc_direct_bgp_add_route_ce(struct bgp *bgp, struct agg_node *rn,
+ struct bgp_path_info *bpi);
+
+extern void vnc_direct_bgp_del_route_ce(struct bgp *bgp, struct agg_node *rn,
+ struct bgp_path_info *bpi);
+
+extern void vnc_direct_bgp_add_prefix(struct bgp *bgp,
+ struct rfapi_import_table *import_table,
+ struct agg_node *rn);
+
+extern void vnc_direct_bgp_del_prefix(struct bgp *bgp,
+ struct rfapi_import_table *import_table,
+ struct agg_node *rn);
+
+extern void vnc_direct_bgp_add_nve(struct bgp *bgp,
+ struct rfapi_descriptor *rfd);
+
+extern void vnc_direct_bgp_del_nve(struct bgp *bgp,
+ struct rfapi_descriptor *rfd);
+
+extern void vnc_direct_bgp_add_group(struct bgp *bgp,
+ struct rfapi_nve_group_cfg *rfg);
+
+extern void vnc_direct_bgp_del_group(struct bgp *bgp,
+ struct rfapi_nve_group_cfg *rfg);
+
+extern void vnc_direct_bgp_reexport_group_afi(struct bgp *bgp,
+ struct rfapi_nve_group_cfg *rfg,
+ afi_t afi);
+
+
+extern void vnc_direct_bgp_rh_add_route(struct bgp *bgp, afi_t afi,
+ const struct prefix *prefix,
+ struct peer *peer, struct attr *attr);
+
+
+extern void vnc_direct_bgp_rh_del_route(struct bgp *bgp, afi_t afi,
+ const struct prefix *prefix,
+ struct peer *peer);
+
+extern void vnc_direct_bgp_reexport(struct bgp *bgp, afi_t afi);
+
+#endif /* _QUAGGA_RFAPI_VNC_EXPORT_BGP_P_H_ */
diff --git a/bgpd/rfapi/vnc_export_table.c b/bgpd/rfapi/vnc_export_table.c
new file mode 100644
index 0000000..743576d
--- /dev/null
+++ b/bgpd/rfapi/vnc_export_table.c
@@ -0,0 +1,192 @@
+/*
+ *
+ * Copyright 2009-2016, LabN Consulting, L.L.C.
+ *
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program; see the file COPYING; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
+ */
+
+
+#include "lib/zebra.h"
+#include "lib/prefix.h"
+#include "lib/agg_table.h"
+#include "lib/memory.h"
+#include "lib/vty.h"
+
+#include "bgpd/bgpd.h"
+#include "bgpd/bgp_route.h"
+
+#include "bgpd/rfapi/vnc_export_table.h"
+#include "bgpd/rfapi/rfapi_private.h"
+#include "bgpd/rfapi/rfapi_import.h"
+#include "bgpd/rfapi/vnc_debug.h"
+
+struct agg_node *vnc_etn_get(struct bgp *bgp, vnc_export_type_t type,
+ const struct prefix *p)
+{
+ struct agg_table *t = NULL;
+ struct agg_node *rn = NULL;
+ afi_t afi;
+
+ if (!bgp || !bgp->rfapi)
+ return NULL;
+
+ afi = family2afi(p->family);
+ assert(afi == AFI_IP || afi == AFI_IP6);
+
+ switch (type) {
+ case EXPORT_TYPE_BGP:
+ if (!bgp->rfapi->rt_export_bgp[afi])
+ bgp->rfapi->rt_export_bgp[afi] = agg_table_init();
+ t = bgp->rfapi->rt_export_bgp[afi];
+ break;
+
+ case EXPORT_TYPE_ZEBRA:
+ if (!bgp->rfapi->rt_export_zebra[afi])
+ bgp->rfapi->rt_export_zebra[afi] = agg_table_init();
+ t = bgp->rfapi->rt_export_zebra[afi];
+ break;
+ }
+
+ if (t)
+ rn = agg_node_get(t, p);
+ return rn;
+}
+
+struct agg_node *vnc_etn_lookup(struct bgp *bgp, vnc_export_type_t type,
+ const struct prefix *p)
+{
+ struct agg_table *t = NULL;
+ struct agg_node *rn = NULL;
+ afi_t afi;
+
+ if (!bgp || !bgp->rfapi)
+ return NULL;
+
+ afi = family2afi(p->family);
+ assert(afi == AFI_IP || afi == AFI_IP6);
+
+ switch (type) {
+ case EXPORT_TYPE_BGP:
+ if (!bgp->rfapi->rt_export_bgp[afi])
+ bgp->rfapi->rt_export_bgp[afi] = agg_table_init();
+ t = bgp->rfapi->rt_export_bgp[afi];
+ break;
+
+ case EXPORT_TYPE_ZEBRA:
+ if (!bgp->rfapi->rt_export_zebra[afi])
+ bgp->rfapi->rt_export_zebra[afi] = agg_table_init();
+ t = bgp->rfapi->rt_export_zebra[afi];
+ break;
+ }
+
+ if (t)
+ rn = agg_node_lookup(t, p);
+ return rn;
+}
+
+struct vnc_export_info *vnc_eti_get(struct bgp *bgp, vnc_export_type_t etype,
+ const struct prefix *p, struct peer *peer,
+ uint8_t type, uint8_t subtype)
+{
+ struct agg_node *etn;
+ struct vnc_export_info *eti;
+
+ etn = vnc_etn_get(bgp, etype, p);
+ assert(etn);
+
+ for (eti = etn->info; eti; eti = eti->next) {
+ if (peer == eti->peer && type == eti->type
+ && subtype == eti->subtype) {
+
+ break;
+ }
+ }
+
+ if (eti) {
+ agg_unlock_node(etn);
+ } else {
+ eti = XCALLOC(MTYPE_RFAPI_ETI, sizeof(struct vnc_export_info));
+ eti->node = etn;
+ eti->peer = peer;
+ peer_lock(peer);
+ eti->type = type;
+ eti->subtype = subtype;
+ eti->next = etn->info;
+ etn->info = eti;
+ }
+
+ return eti;
+}
+
+void vnc_eti_delete(struct vnc_export_info *goner)
+{
+ struct agg_node *etn;
+ struct vnc_export_info *eti;
+ struct vnc_export_info *eti_prev = NULL;
+
+ etn = goner->node;
+
+ for (eti = etn->info; eti; eti_prev = eti, eti = eti->next) {
+ if (eti == goner)
+ break;
+ }
+
+ if (!eti) {
+ vnc_zlog_debug_verbose("%s: COULDN'T FIND ETI", __func__);
+ return;
+ }
+
+ if (eti_prev) {
+ eti_prev->next = goner->next;
+ } else {
+ etn->info = goner->next;
+ }
+
+ peer_unlock(eti->peer);
+ goner->node = NULL;
+ XFREE(MTYPE_RFAPI_ETI, goner);
+
+ agg_unlock_node(etn);
+}
+
+struct vnc_export_info *vnc_eti_checktimer(struct bgp *bgp,
+ vnc_export_type_t etype,
+ const struct prefix *p,
+ struct peer *peer, uint8_t type,
+ uint8_t subtype)
+{
+ struct agg_node *etn;
+ struct vnc_export_info *eti;
+
+ etn = vnc_etn_lookup(bgp, etype, p);
+ if (!etn)
+ return NULL;
+
+ for (eti = etn->info; eti; eti = eti->next) {
+ if (peer == eti->peer && type == eti->type
+ && subtype == eti->subtype) {
+
+ break;
+ }
+ }
+
+ agg_unlock_node(etn);
+
+ if (eti && eti->timer)
+ return eti;
+
+ return NULL;
+}
diff --git a/bgpd/rfapi/vnc_export_table.h b/bgpd/rfapi/vnc_export_table.h
new file mode 100644
index 0000000..8a1fc9a
--- /dev/null
+++ b/bgpd/rfapi/vnc_export_table.h
@@ -0,0 +1,66 @@
+/*
+ *
+ * Copyright 2009-2016, LabN Consulting, L.L.C.
+ *
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program; see the file COPYING; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
+ */
+
+#ifndef _QUAGGA_VNC_VNC_EXPORT_TABLE_H_
+#define _QUAGGA_VNC_VNC_EXPORT_TABLE_H_
+
+#include "lib/table.h"
+#include "lib/thread.h"
+#include "lib/vty.h"
+
+#include "bgpd/bgpd.h"
+
+#define VNC_EXPORT_TYPE_BGP 1
+#define VNC_EXPORT_TYPE_ZEBRA 2
+
+typedef enum vnc_export_type {
+ EXPORT_TYPE_BGP,
+ EXPORT_TYPE_ZEBRA
+} vnc_export_type_t;
+
+struct vnc_export_info {
+ struct vnc_export_info *next;
+ struct agg_node *node;
+ struct peer *peer;
+ uint8_t type;
+ uint8_t subtype;
+ uint32_t lifetime;
+ struct thread *timer;
+};
+
+extern struct agg_node *vnc_etn_get(struct bgp *bgp, vnc_export_type_t type,
+ const struct prefix *p);
+
+extern struct agg_node *vnc_etn_lookup(struct bgp *bgp, vnc_export_type_t type,
+ const struct prefix *p);
+
+extern struct vnc_export_info *
+vnc_eti_get(struct bgp *bgp, vnc_export_type_t etype, const struct prefix *p,
+ struct peer *peer, uint8_t type, uint8_t subtype);
+
+extern void vnc_eti_delete(struct vnc_export_info *goner);
+
+extern struct vnc_export_info *
+vnc_eti_checktimer(struct bgp *bgp, vnc_export_type_t etype,
+ const struct prefix *p, struct peer *peer, uint8_t type,
+ uint8_t subtype);
+
+
+#endif /* _QUAGGA_VNC_VNC_EXPORT_TABLE_H_ */
diff --git a/bgpd/rfapi/vnc_import_bgp.c b/bgpd/rfapi/vnc_import_bgp.c
new file mode 100644
index 0000000..acbc413
--- /dev/null
+++ b/bgpd/rfapi/vnc_import_bgp.c
@@ -0,0 +1,2907 @@
+/*
+ *
+ * Copyright 2009-2016, LabN Consulting, L.L.C.
+ *
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program; see the file COPYING; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
+ */
+
+/*
+ * File: vnc_import_bgp.c
+ * Purpose: Import routes from BGP unicast directly (not via zebra)
+ */
+
+#include "lib/zebra.h"
+#include "lib/prefix.h"
+#include "lib/agg_table.h"
+#include "lib/vty.h"
+#include "lib/log.h"
+#include "lib/memory.h"
+#include "lib/linklist.h"
+#include "lib/plist.h"
+#include "lib/routemap.h"
+#include "lib/lib_errors.h"
+
+#include "bgpd/bgpd.h"
+#include "bgpd/bgp_ecommunity.h"
+#include "bgpd/bgp_attr.h"
+#include "bgpd/bgp_route.h"
+#include "bgpd/bgp_mplsvpn.h" /* for RD_TYPE_IP */
+
+#include "bgpd/rfapi/vnc_export_bgp.h"
+#include "bgpd/rfapi/bgp_rfapi_cfg.h"
+#include "bgpd/rfapi/rfapi.h"
+#include "bgpd/rfapi/rfapi_import.h"
+#include "bgpd/rfapi/rfapi_private.h"
+#include "bgpd/rfapi/rfapi_monitor.h"
+#include "bgpd/rfapi/rfapi_vty.h"
+#include "bgpd/rfapi/vnc_import_bgp.h"
+#include "bgpd/rfapi/vnc_import_bgp_p.h"
+#include "bgpd/rfapi/vnc_debug.h"
+
+#define ENABLE_VNC_RHNCK
+
+#define DEBUG_RHN_LIST 0
+
+static struct rfapi_descriptor vncHDBgpDirect; /* dummy nve descriptor */
+static struct rfapi_descriptor vncHDResolveNve; /* dummy nve descriptor */
+
+/*
+ * For routes from another AS:
+ *
+ * If MED is set,
+ * LOCAL_PREF = 255 - MIN(255, MED)
+ * else
+ * LOCAL_PREF = default_local_pref
+ *
+ * For routes from the same AS:
+ *
+ * LOCAL_PREF unchanged
+ */
+uint32_t calc_local_pref(struct attr *attr, struct peer *peer)
+{
+ uint32_t local_pref = 0;
+
+ if (!attr) {
+ if (peer) {
+ return peer->bgp->default_local_pref;
+ }
+ return bgp_get_default()->default_local_pref;
+ }
+
+ if (peer && (peer->as != peer->bgp->as)) {
+ if (attr->flag & ATTR_FLAG_BIT(BGP_ATTR_MULTI_EXIT_DISC)) {
+ if (attr->med > 255) {
+ local_pref = 0;
+ } else {
+ local_pref = 255 - attr->med;
+ }
+ } else {
+ local_pref = peer->bgp->default_local_pref;
+ }
+ } else {
+ if (attr->flag & ATTR_FLAG_BIT(BGP_ATTR_LOCAL_PREF)) {
+ local_pref = attr->local_pref;
+ } else {
+ if (peer && peer->bgp) {
+ local_pref = peer->bgp->default_local_pref;
+ }
+ }
+ }
+
+ return local_pref;
+}
+
+static int is_host_prefix(const struct prefix *p)
+{
+ switch (p->family) {
+ case AF_INET:
+ return (p->prefixlen == IPV4_MAX_BITLEN);
+ case AF_INET6:
+ return (p->prefixlen == IPV6_MAX_BITLEN);
+ }
+ return 0;
+}
+
+/***********************************************************************
+ * RHN list
+ ***********************************************************************/
+
+struct prefix_bag {
+ struct prefix hpfx; /* ce address = unicast nexthop */
+ struct prefix upfx; /* unicast prefix */
+ struct bgp_path_info *ubpi; /* unicast route */
+};
+
+static const uint8_t maskbit[] = {0x00, 0x80, 0xc0, 0xe0, 0xf0,
+ 0xf8, 0xfc, 0xfe, 0xff};
+
+int vnc_prefix_cmp(const void *pfx1, const void *pfx2)
+{
+ int offset;
+ int shift;
+ uint8_t mask;
+
+ const struct prefix *p1 = pfx1;
+ const struct prefix *p2 = pfx2;
+
+ if (p1->family < p2->family)
+ return -1;
+ if (p1->family > p2->family)
+ return 1;
+
+ if (p1->prefixlen < p2->prefixlen)
+ return -1;
+ if (p1->prefixlen > p2->prefixlen)
+ return 1;
+
+ offset = p1->prefixlen / 8;
+ shift = p1->prefixlen % 8;
+ if (shift == 0 && offset) { /* catch aligned case */
+ offset--;
+ shift = 8;
+ }
+
+ /* Set both prefix's head pointer. */
+ const uint8_t *pp1 = (const uint8_t *)&p1->u.prefix;
+ const uint8_t *pp2 = (const uint8_t *)&p2->u.prefix;
+
+ while (offset--) {
+ if (*pp1 < *pp2)
+ return -1;
+ if (*pp1 > *pp2)
+ return 1;
+ ++pp1;
+ ++pp2;
+ }
+
+ mask = maskbit[shift];
+ if ((*pp1 & mask) < (*pp2 & mask))
+ return -1;
+ if ((*pp1 & mask) > (*pp2 & mask))
+ return 1;
+
+ return 0;
+}
+
+static void prefix_bag_free(void *pb)
+{
+ XFREE(MTYPE_RFAPI_PREFIX_BAG, pb);
+}
+
+#if DEBUG_RHN_LIST
+static void print_rhn_list(const char *tag1, const char *tag2)
+{
+ struct bgp *bgp;
+ struct skiplist *sl;
+ struct skiplistnode *p;
+ struct prefix_bag *pb;
+ int count = 0;
+
+ bgp = bgp_get_default();
+ if (!bgp)
+ return;
+
+ sl = bgp->frapi->resolve_nve_nexthop;
+ if (!sl) {
+ vnc_zlog_debug_verbose("%s: %s: RHN List is empty",
+ (tag1 ? tag1 : ""), (tag2 ? tag2 : ""));
+ return;
+ }
+
+ vnc_zlog_debug_verbose("%s: %s: RHN list:", (tag1 ? tag1 : ""),
+ (tag2 ? tag2 : ""));
+
+ /* XXX uses secret knowledge of skiplist structure */
+ for (p = sl->header->forward[0]; p; p = p->forward[0]) {
+ pb = p->value;
+
+ vnc_zlog_debug_verbose(
+ "RHN Entry %d (q=%p): kpfx=%pFX, upfx=%pFX, hpfx=%pFX, ubpi=%p",
+ ++count, p, p->key, &pb->upfx, &pb->hpfx, pb->ubpi);
+ }
+}
+#endif
+
+#ifdef ENABLE_VNC_RHNCK
+static void vnc_rhnck(char *tag)
+{
+ struct bgp *bgp;
+ struct skiplist *sl;
+ struct skiplistnode *p;
+
+ bgp = bgp_get_default();
+ if (!bgp)
+ return;
+ sl = bgp->rfapi->resolve_nve_nexthop;
+
+ if (!sl)
+ return;
+
+ /* XXX uses secret knowledge of skiplist structure */
+ for (p = sl->header->forward[0]; p; p = p->forward[0]) {
+ struct prefix_bag *pb;
+ struct prefix *pkey;
+ afi_t afi;
+ struct prefix pfx_orig_nexthop;
+
+ memset(&pfx_orig_nexthop, 0,
+ sizeof(pfx_orig_nexthop)); /* keep valgrind happy */
+
+ pkey = p->key;
+ pb = p->value;
+
+ afi = family2afi(pb->upfx.family);
+
+ rfapiUnicastNexthop2Prefix(afi, pb->ubpi->attr,
+ &pfx_orig_nexthop);
+
+ /* pb->hpfx, pb->ubpi nexthop, pkey should all reflect the same
+ * pfx */
+ assert(!vnc_prefix_cmp(&pb->hpfx, pkey));
+ if (vnc_prefix_cmp(&pb->hpfx, &pfx_orig_nexthop)) {
+ vnc_zlog_debug_verbose(
+ "%s: %s: FATAL: resolve_nve_nexthop list item bpi nexthop %pFX != nve pfx %pFX",
+ __func__, tag, &pfx_orig_nexthop, &pb->hpfx);
+ assert(0);
+ }
+ }
+ vnc_zlog_debug_verbose("%s: vnc_rhnck OK", tag);
+}
+
+#define VNC_RHNCK(n) \
+ do { \
+ char buf[BUFSIZ]; \
+ snprintf(buf, sizeof(buf), "%s: %s", __func__, #n); \
+ vnc_rhnck(buf); \
+ } while (0)
+
+#else
+
+#define VNC_RHNCK(n)
+
+#endif
+
+/***********************************************************************
+ * Add/Delete Unicast Route
+ ***********************************************************************/
+
+/*
+ * "Adding a Route" import process
+ */
+
+/*
+ * extract and package information from the BGP unicast route.
+ * Return code 0 means OK, non-0 means drop.
+ *
+ * If return code is 0, caller MUST release ecom
+ */
+static int process_unicast_route(struct bgp *bgp, /* in */
+ afi_t afi, /* in */
+ const struct prefix *prefix, /* in */
+ struct bgp_path_info *info, /* in */
+ struct ecommunity **ecom, /* OUT */
+ struct prefix *unicast_nexthop) /* OUT */
+{
+ struct rfapi_cfg *hc = bgp->rfapi_cfg;
+ struct peer *peer = info->peer;
+ struct attr *attr = info->attr;
+ struct attr hattr;
+ struct route_map *rmap = NULL;
+ struct prefix pfx_orig_nexthop;
+
+ memset(&pfx_orig_nexthop, 0,
+ sizeof(pfx_orig_nexthop)); /* keep valgrind happy */
+
+ /*
+ * prefix list check
+ */
+ if (hc->plist_redist[ZEBRA_ROUTE_BGP_DIRECT][afi]) {
+ vnc_zlog_debug_verbose("%s: HC prefix list is set, checking",
+ __func__);
+ if (prefix_list_apply(
+ hc->plist_redist[ZEBRA_ROUTE_BGP_DIRECT][afi],
+ prefix)
+ == PREFIX_DENY) {
+ vnc_zlog_debug_verbose(
+ "%s: prefix list returns DENY, blocking route",
+ __func__);
+ return -1;
+ }
+ vnc_zlog_debug_verbose(
+ "%s: prefix list returns PASS, allowing route",
+ __func__);
+ }
+
+ /* apply routemap, if any, later */
+ rmap = hc->routemap_redist[ZEBRA_ROUTE_BGP_DIRECT];
+
+ /*
+ * Extract original nexthop, which we expect to be a NVE connected
+ * router
+ * Note that this is the nexthop before any possible application of
+ * policy
+ */
+ /*
+ * Incoming prefix is unicast. If v6, it is in multiprotocol area,
+ * but if v4 it is in attr->nexthop
+ */
+ rfapiUnicastNexthop2Prefix(afi, attr, &pfx_orig_nexthop);
+
+ /*
+ * route map handling
+ * This code is here because it allocates an interned attr which
+ * must be freed before we return. It's easier to put it after
+ * all of the possible returns above.
+ */
+ memset(&hattr, 0, sizeof(hattr));
+ /* hattr becomes a ghost attr */
+ hattr = *attr;
+
+ if (rmap) {
+ struct bgp_path_info info;
+ route_map_result_t ret;
+
+ memset(&info, 0, sizeof(info));
+ info.peer = peer;
+ info.attr = &hattr;
+ ret = route_map_apply(rmap, prefix, &info);
+ if (ret == RMAP_DENYMATCH) {
+ bgp_attr_flush(&hattr);
+ vnc_zlog_debug_verbose(
+ "%s: route map \"%s\" says DENY, returning",
+ __func__, rmap->name);
+ return -1;
+ }
+ }
+
+ /*
+ * Get the (possibly altered by policy) unicast nexthop
+ * for later lookup in the Import Table by caller
+ */
+ rfapiUnicastNexthop2Prefix(afi, &hattr, unicast_nexthop);
+
+ if (bgp_attr_get_ecommunity(&hattr))
+ *ecom = ecommunity_dup(bgp_attr_get_ecommunity(&hattr));
+ else
+ *ecom = ecommunity_new();
+
+ /*
+ * Done with hattr, clean up
+ */
+ bgp_attr_flush(&hattr);
+
+ /*
+ * Add EC that carries original NH of iBGP route (2 bytes = magic
+ * value indicating it came from an VNC gateway; default 5226, but
+ * must be user configurable). Note that this is the nexthop before
+ * any application of policy.
+ */
+ {
+ struct ecommunity_val vnc_gateway_magic;
+ uint16_t localadmin;
+
+ /* Using route origin extended community type */
+ memset(&vnc_gateway_magic, 0, sizeof(vnc_gateway_magic));
+ vnc_gateway_magic.val[0] = 0x01;
+ vnc_gateway_magic.val[1] = 0x03;
+
+ /* Only works for IPv4 nexthops */
+ if (prefix->family == AF_INET) {
+ memcpy(vnc_gateway_magic.val + 2,
+ &unicast_nexthop->u.prefix4, 4);
+ }
+ localadmin = htons(hc->resolve_nve_roo_local_admin);
+ memcpy(vnc_gateway_magic.val + 6, (char *)&localadmin, 2);
+
+ ecommunity_add_val(*ecom, &vnc_gateway_magic, false, false);
+ }
+
+ return 0;
+}
+
+
+static void vnc_import_bgp_add_route_mode_resolve_nve_one_bi(
+ struct bgp *bgp, afi_t afi, struct bgp_path_info *bpi, /* VPN bpi */
+ struct prefix_rd *prd, /* RD */
+ const struct prefix *prefix, /* unicast route prefix */
+ uint32_t *local_pref, /* NULL = no local_pref */
+ uint32_t *med, /* NULL = no med */
+ struct ecommunity *ecom) /* generated ecoms */
+{
+ struct prefix un;
+ struct prefix nexthop;
+ struct rfapi_ip_addr nexthop_h;
+ uint32_t lifetime;
+ uint32_t *plifetime;
+ struct bgp_attr_encap_subtlv *encaptlvs;
+ uint32_t label = 0;
+
+ struct rfapi_un_option optary[3];
+ struct rfapi_un_option *opt = NULL;
+ int cur_opt = 0;
+
+ vnc_zlog_debug_verbose("%s: entry", __func__);
+
+ if (bpi->type != ZEBRA_ROUTE_BGP
+ && bpi->type != ZEBRA_ROUTE_BGP_DIRECT) {
+
+ return;
+ }
+ if (bpi->sub_type != BGP_ROUTE_NORMAL
+ && bpi->sub_type != BGP_ROUTE_STATIC
+ && bpi->sub_type != BGP_ROUTE_RFP) {
+
+ return;
+ }
+ if (CHECK_FLAG(bpi->flags, BGP_PATH_REMOVED))
+ return;
+
+ vncHDResolveNve.peer = bpi->peer;
+ if (!rfapiGetVncTunnelUnAddr(bpi->attr, &un)) {
+ if (rfapiQprefix2Raddr(&un, &vncHDResolveNve.un_addr))
+ return;
+ } else {
+ memset(&vncHDResolveNve.un_addr, 0,
+ sizeof(vncHDResolveNve.un_addr));
+ }
+
+ /* Use nexthop of VPN route as nexthop of constructed route */
+ rfapiNexthop2Prefix(bpi->attr, &nexthop);
+ rfapiQprefix2Raddr(&nexthop, &nexthop_h);
+
+ if (rfapiGetVncLifetime(bpi->attr, &lifetime)) {
+ plifetime = NULL;
+ } else {
+ plifetime = &lifetime;
+ }
+
+ encaptlvs = bgp_attr_get_vnc_subtlvs(bpi->attr);
+ if (bpi->attr->encap_tunneltype != BGP_ENCAP_TYPE_RESERVED
+ && bpi->attr->encap_tunneltype != BGP_ENCAP_TYPE_MPLS) {
+ opt = &optary[cur_opt++];
+ memset(opt, 0, sizeof(struct rfapi_un_option));
+ opt->type = RFAPI_UN_OPTION_TYPE_TUNNELTYPE;
+ opt->v.tunnel.type = bpi->attr->encap_tunneltype;
+ /* TBD parse bpi->attr->extra->encap_subtlvs */
+ }
+
+ struct ecommunity *new_ecom = ecommunity_dup(ecom);
+
+ if (bgp_attr_get_ecommunity(bpi->attr))
+ ecommunity_merge(new_ecom, bgp_attr_get_ecommunity(bpi->attr));
+
+ if (bpi->extra)
+ label = decode_label(&bpi->extra->label[0]);
+
+ add_vnc_route(&vncHDResolveNve, bgp, SAFI_MPLS_VPN,
+ prefix, /* unicast route prefix */
+ prd, &nexthop_h, /* new nexthop */
+ local_pref, plifetime,
+ (struct bgp_tea_options *)encaptlvs, /* RFP options */
+ opt, NULL, new_ecom, med, /* NULL => don't set med */
+ (label ? &label : NULL), /* NULL= default */
+ ZEBRA_ROUTE_BGP_DIRECT, BGP_ROUTE_REDISTRIBUTE,
+ RFAPI_AHR_RFPOPT_IS_VNCTLV); /* flags */
+
+ ecommunity_free(&new_ecom);
+}
+
+static void vnc_import_bgp_add_route_mode_resolve_nve_one_rd(
+ struct prefix_rd *prd, /* RD */
+ struct bgp_table *table_rd, /* per-rd VPN route table */
+ afi_t afi, struct bgp *bgp,
+ const struct prefix *prefix, /* unicast prefix */
+ struct ecommunity *ecom, /* generated ecoms */
+ uint32_t *local_pref, /* NULL = no local_pref */
+ uint32_t *med, /* NULL = no med */
+ struct prefix *ubpi_nexthop) /* unicast nexthop */
+{
+ struct bgp_dest *bd;
+ struct bgp_path_info *bpi;
+
+ if (!table_rd)
+ return;
+
+ vnc_zlog_debug_verbose("%s: ubpi_nexthop=%pFX", __func__, ubpi_nexthop);
+
+ /* exact match */
+ bd = bgp_node_lookup(table_rd, ubpi_nexthop);
+ if (!bd) {
+ vnc_zlog_debug_verbose(
+ "%s: no match in RD's table for ubpi_nexthop",
+ __func__);
+ return;
+ }
+
+ /* Iterate over bgp_info items at this node */
+ for (bpi = bgp_dest_get_bgp_path_info(bd); bpi; bpi = bpi->next) {
+
+ vnc_import_bgp_add_route_mode_resolve_nve_one_bi(
+ bgp, afi, bpi, /* VPN bpi */
+ prd, prefix, local_pref, med, ecom);
+ }
+
+ bgp_dest_unlock_node(bd);
+}
+
+static void vnc_import_bgp_add_route_mode_resolve_nve(
+ struct bgp *bgp, const struct prefix *prefix, /* unicast prefix */
+ struct bgp_path_info *info) /* unicast info */
+{
+ afi_t afi = family2afi(prefix->family);
+
+ struct prefix pfx_unicast_nexthop = {0}; /* happy valgrind */
+
+ struct ecommunity *ecom = NULL;
+ uint32_t local_pref;
+ uint32_t *med = NULL;
+
+ struct prefix_bag *pb;
+ struct bgp_dest *bdp; /* prd table node */
+
+ /*debugging */
+ if (VNC_DEBUG(VERBOSE)) {
+ char str_nh[PREFIX_STRLEN];
+ struct prefix nh;
+
+ nh.prefixlen = 0;
+ rfapiUnicastNexthop2Prefix(afi, info->attr, &nh);
+ if (nh.prefixlen) {
+ prefix2str(&nh, str_nh, sizeof(str_nh));
+ } else {
+ str_nh[0] = '?';
+ str_nh[1] = 0;
+ }
+
+ vnc_zlog_debug_verbose(
+ "%s(bgp=%p, unicast prefix=%pFX, unicast nh=%s)",
+ __func__, bgp, prefix, str_nh);
+ }
+
+ if (info->type != ZEBRA_ROUTE_BGP) {
+ vnc_zlog_debug_verbose(
+ "%s: unicast type %d=\"%s\" is not %d=%s, skipping",
+ __func__, info->type, zebra_route_string(info->type),
+ ZEBRA_ROUTE_BGP, "ZEBRA_ROUTE_BGP");
+ return;
+ }
+
+ /*
+ * Preliminary checks
+ */
+
+ if (!afi) {
+ flog_err(EC_LIB_DEVELOPMENT, "%s: can't get afi of prefix",
+ __func__);
+ return;
+ }
+
+ if (!(bgp->rfapi_cfg)) {
+ vnc_zlog_debug_verbose("%s: bgp->rfapi_cfg is NULL, skipping",
+ __func__);
+ return;
+ }
+
+ /* check vnc redist flag for bgp direct routes */
+ if (!bgp->rfapi_cfg->redist[afi][ZEBRA_ROUTE_BGP_DIRECT]) {
+ vnc_zlog_debug_verbose(
+ "%s: bgp->rfapi_cfg->redist[afi=%d][type=ZEBRA_ROUTE_BGP_DIRECT] is 0, skipping",
+ __func__, afi);
+ return;
+ }
+
+
+ if (process_unicast_route(bgp, afi, prefix, info, &ecom,
+ &pfx_unicast_nexthop)) {
+
+ vnc_zlog_debug_verbose(
+ "%s: process_unicast_route error, skipping", __func__);
+ return;
+ }
+
+ local_pref = calc_local_pref(info->attr, info->peer);
+ if (info->attr->flag & ATTR_FLAG_BIT(BGP_ATTR_MULTI_EXIT_DISC))
+ med = &info->attr->med;
+
+ /*
+ * At this point, we have allocated:
+ *
+ * ecom ecommunity ptr, union of unicast and ROO parts (no NVE part)
+ *
+ * And we have set:
+ *
+ * pfx_unicast_nexthop nexthop of uncast route
+ */
+
+ if (!bgp->rfapi->resolve_nve_nexthop) {
+ bgp->rfapi->resolve_nve_nexthop =
+ skiplist_new(SKIPLIST_FLAG_ALLOW_DUPLICATES,
+ vnc_prefix_cmp, prefix_bag_free);
+ }
+
+ pb = XCALLOC(MTYPE_RFAPI_PREFIX_BAG, sizeof(struct prefix_bag));
+ pb->hpfx = pfx_unicast_nexthop;
+ pb->ubpi = info;
+ pb->upfx = *prefix;
+
+ bgp_path_info_lock(info); /* skiplist refers to it */
+ skiplist_insert(bgp->rfapi->resolve_nve_nexthop, &pb->hpfx, pb);
+
+ /*
+ * Iterate over RDs in VPN RIB. For each RD, look up unicast nexthop
+ * (exact match, /32). If an exact match is found, call add_vnc_route.
+ */
+
+ for (bdp = bgp_table_top(bgp->rib[afi][SAFI_MPLS_VPN]); bdp;
+ bdp = bgp_route_next(bdp)) {
+
+ struct bgp_table *table;
+
+ table = bgp_dest_get_bgp_table_info(bdp);
+
+ if (!table)
+ continue;
+
+ vnc_import_bgp_add_route_mode_resolve_nve_one_rd(
+ (struct prefix_rd *)bgp_dest_get_prefix(bdp), table,
+ afi, bgp, prefix, ecom, &local_pref, med,
+ &pfx_unicast_nexthop);
+ }
+
+
+ if (ecom)
+ ecommunity_free(&ecom);
+
+ vnc_zlog_debug_verbose("%s: done", __func__);
+}
+
+
+static void vnc_import_bgp_add_route_mode_plain(struct bgp *bgp,
+ const struct prefix *prefix,
+ struct bgp_path_info *info)
+{
+ afi_t afi = family2afi(prefix->family);
+ struct peer *peer = info->peer;
+ struct attr *attr = info->attr;
+ struct attr hattr;
+ struct rfapi_cfg *hc = bgp->rfapi_cfg;
+ struct attr *iattr = NULL;
+
+ struct rfapi_ip_addr vnaddr;
+ struct prefix vn_pfx_space;
+ struct prefix *vn_pfx = NULL;
+ int ahr_flags = 0;
+ struct ecommunity *ecom = NULL;
+ struct prefix_rd prd;
+ struct route_map *rmap = NULL;
+ uint32_t local_pref;
+ uint32_t *med = NULL;
+
+ vnc_zlog_debug_verbose("%s(prefix=%pFX) entry", __func__, prefix);
+
+ if (!afi) {
+ flog_err(EC_LIB_DEVELOPMENT, "%s: can't get afi of prefix",
+ __func__);
+ return;
+ }
+
+ if (!hc) {
+ vnc_zlog_debug_verbose("%s: bgp->rfapi_cfg is NULL, skipping",
+ __func__);
+ return;
+ }
+
+ /* check vnc redist flag for bgp direct routes */
+ if (!bgp->rfapi_cfg->redist[afi][ZEBRA_ROUTE_BGP_DIRECT]) {
+ vnc_zlog_debug_verbose(
+ "%s: bgp->rfapi_cfg->redist[afi=%d][type=ZEBRA_ROUTE_BGP_DIRECT] is 0, skipping",
+ __func__, afi);
+ return;
+ }
+
+ /*
+ * mode "plain" specific code
+ */
+ {
+ vnc_zlog_debug_verbose("%s: NOT using redist RFG", __func__);
+
+ /*
+ * prefix list check
+ */
+ if (hc->plist_redist[ZEBRA_ROUTE_BGP_DIRECT][afi]) {
+ vnc_zlog_debug_verbose(
+ "%s: HC prefix list is set, checking",
+ __func__);
+ if (prefix_list_apply(
+ hc->plist_redist[ZEBRA_ROUTE_BGP_DIRECT]
+ [afi],
+ prefix)
+ == PREFIX_DENY) {
+ vnc_zlog_debug_verbose(
+ "%s: prefix list returns DENY, blocking route",
+ __func__);
+ return;
+ }
+ vnc_zlog_debug_verbose(
+ "%s: prefix list returns PASS, allowing route",
+ __func__);
+ }
+
+ /* apply routemap, if any, later */
+ rmap = hc->routemap_redist[ZEBRA_ROUTE_BGP_DIRECT];
+
+ /*
+ * Incoming prefix is unicast. If v6, it is in multiprotocol
+ * area,
+ * but if v4 it is in attr->nexthop
+ */
+ rfapiUnicastNexthop2Prefix(afi, attr, &vn_pfx_space);
+ vn_pfx = &vn_pfx_space;
+
+ /* UN address */
+ ahr_flags |= RFAPI_AHR_NO_TUNNEL_SUBTLV;
+ }
+
+ if (VNC_DEBUG(IMPORT_BGP_ADD_ROUTE))
+ vnc_zlog_debug_any("%s vn_pfx=%pFX", __func__, vn_pfx);
+
+ /*
+ * Compute VN address
+ */
+ if (rfapiQprefix2Raddr(vn_pfx, &vnaddr)) {
+ vnc_zlog_debug_verbose("%s: redist VN invalid, skipping",
+ __func__);
+ return;
+ }
+
+ /*
+ * route map handling
+ * This code is here because it allocates an interned attr which
+ * must be freed before we return. It's easier to put it after
+ * all of the possible returns above.
+ */
+ memset(&hattr, 0, sizeof(hattr));
+ /* hattr becomes a ghost attr */
+ hattr = *attr;
+
+ if (rmap) {
+ struct bgp_path_info info;
+ route_map_result_t ret;
+
+ memset(&info, 0, sizeof(info));
+ info.peer = peer;
+ info.attr = &hattr;
+ ret = route_map_apply(rmap, prefix, &info);
+ if (ret == RMAP_DENYMATCH) {
+ bgp_attr_flush(&hattr);
+ vnc_zlog_debug_verbose(
+ "%s: route map \"%s\" says DENY, returning",
+ __func__, rmap->name);
+ return;
+ }
+ }
+
+ iattr = bgp_attr_intern(&hattr);
+ bgp_attr_flush(&hattr);
+
+ /* Now iattr is an allocated interned attr */
+
+ /*
+ * Mode "plain" specific code
+ *
+ * Sets RD in dummy HD
+ * Allocates ecom
+ */
+ {
+ if (vnaddr.addr_family != AF_INET) {
+ vnc_zlog_debug_verbose(
+ "%s: can't auto-assign RD, VN AF (%d) is not IPv4, skipping",
+ __func__, vnaddr.addr_family);
+ if (iattr) {
+ bgp_attr_unintern(&iattr);
+ }
+ return;
+ }
+ memset(&prd, 0, sizeof(prd));
+ rfapi_set_autord_from_vn(&prd, &vnaddr);
+
+ if (iattr && bgp_attr_get_ecommunity(iattr))
+ ecom = ecommunity_dup(bgp_attr_get_ecommunity(iattr));
+ }
+
+ local_pref = calc_local_pref(iattr, peer);
+
+ if (iattr && (iattr->flag & ATTR_FLAG_BIT(BGP_ATTR_MULTI_EXIT_DISC))) {
+ med = &iattr->med;
+ }
+
+ if (VNC_DEBUG(IMPORT_BGP_ADD_ROUTE)) {
+ char buf[PREFIX_STRLEN];
+
+ rfapiRfapiIpAddr2Str(&vnaddr, buf, sizeof(buf));
+ vnc_zlog_debug_any("%s: setting vnaddr to %s", __func__, buf);
+ }
+
+ vncHDBgpDirect.peer = peer;
+ add_vnc_route(&vncHDBgpDirect, bgp, SAFI_MPLS_VPN, prefix, &prd,
+ &vnaddr, &local_pref, &(bgp->rfapi_cfg->redist_lifetime),
+ NULL, /* RFP options */
+ NULL, NULL, ecom, med, /* med */
+ NULL, /* label: default */
+ ZEBRA_ROUTE_BGP_DIRECT, BGP_ROUTE_REDISTRIBUTE,
+ ahr_flags);
+ vncHDBgpDirect.peer = NULL;
+
+ if (ecom)
+ ecommunity_free(&ecom);
+}
+
+static void vnc_import_bgp_add_route_mode_nvegroup(
+ struct bgp *bgp, const struct prefix *prefix,
+ struct bgp_path_info *info, struct rfapi_nve_group_cfg *rfg)
+{
+ afi_t afi = family2afi(prefix->family);
+ struct peer *peer = info->peer;
+ struct attr *attr = info->attr;
+ struct attr hattr;
+ struct attr *iattr = NULL;
+
+ struct rfapi_ip_addr vnaddr;
+ struct prefix *vn_pfx = NULL;
+ int ahr_flags = 0;
+ struct ecommunity *ecom = NULL;
+ struct prefix_rd prd;
+ struct route_map *rmap = NULL;
+ uint32_t local_pref;
+
+ vnc_zlog_debug_verbose("%s(prefix=%pFX) entry", __func__, prefix);
+
+ assert(rfg);
+
+ if (!afi) {
+ flog_err(EC_LIB_DEVELOPMENT, "%s: can't get afi of prefix",
+ __func__);
+ return;
+ }
+
+ if (!(bgp->rfapi_cfg)) {
+ vnc_zlog_debug_verbose("%s: bgp->rfapi_cfg is NULL, skipping",
+ __func__);
+ return;
+ }
+
+ /* check vnc redist flag for bgp direct routes */
+ if (!bgp->rfapi_cfg->redist[afi][ZEBRA_ROUTE_BGP_DIRECT]) {
+ vnc_zlog_debug_verbose(
+ "%s: bgp->rfapi_cfg->redist[afi=%d][type=ZEBRA_ROUTE_BGP_DIRECT] is 0, skipping",
+ __func__, afi);
+ return;
+ }
+
+
+ /*
+ * RFG-specific code
+ */
+ {
+
+ struct rfapi_ip_prefix pfx_un;
+
+ vnc_zlog_debug_verbose("%s: using redist RFG", __func__);
+
+ /*
+ * RFG prefix list check
+ */
+ if (rfg->plist_redist[ZEBRA_ROUTE_BGP_DIRECT][afi]) {
+ vnc_zlog_debug_verbose(
+ "%s: RFG prefix list is set, checking",
+ __func__);
+ if (prefix_list_apply(
+ rfg->plist_redist[ZEBRA_ROUTE_BGP_DIRECT]
+ [afi],
+ prefix)
+ == PREFIX_DENY) {
+ vnc_zlog_debug_verbose(
+ "%s: prefix list returns DENY, blocking route",
+ __func__);
+ return;
+ }
+ vnc_zlog_debug_verbose(
+ "%s: prefix list returns PASS, allowing route",
+ __func__);
+ }
+
+ /* apply routemap, if any, later */
+ rmap = rfg->routemap_redist[ZEBRA_ROUTE_BGP_DIRECT];
+
+ /*
+ * export nve group's VN addr prefix must be a /32 which
+ * will yield the VN addr to use
+ */
+ vn_pfx = &rfg->vn_prefix;
+
+ /*
+ * UN Address
+ */
+ if (!is_host_prefix(&rfg->un_prefix)) {
+ /* NB prefixlen==0 means it has not been configured */
+ vnc_zlog_debug_verbose(
+ "%s: redist RFG UN pfx not host pfx (plen=%d), skipping",
+ __func__, rfg->un_prefix.prefixlen);
+ return;
+ }
+
+ rfapiQprefix2Rprefix(&rfg->un_prefix, &pfx_un);
+
+ vncHDBgpDirect.un_addr = pfx_un.prefix;
+ }
+
+ if (VNC_DEBUG(IMPORT_BGP_ADD_ROUTE))
+ vnc_zlog_debug_any("%s vn_pfx=%pFX", __func__, vn_pfx);
+
+ /*
+ * Compute VN address
+ */
+ if (rfapiQprefix2Raddr(vn_pfx, &vnaddr)) {
+ vnc_zlog_debug_verbose("%s: redist VN invalid, skipping",
+ __func__);
+ return;
+ }
+
+ /*
+ * route map handling
+ * This code is here because it allocates an interned attr which
+ * must be freed before we return. It's easier to put it after
+ * all of the possible returns above.
+ */
+ memset(&hattr, 0, sizeof(hattr));
+ /* hattr becomes a ghost attr */
+ hattr = *attr;
+
+ if (rmap) {
+ struct bgp_path_info path;
+ route_map_result_t ret;
+
+ memset(&path, 0, sizeof(path));
+ path.peer = peer;
+ path.attr = &hattr;
+ ret = route_map_apply(rmap, prefix, &path);
+ if (ret == RMAP_DENYMATCH) {
+ bgp_attr_flush(&hattr);
+ vnc_zlog_debug_verbose(
+ "%s: route map \"%s\" says DENY, returning",
+ __func__, rmap->name);
+ return;
+ }
+ }
+
+ iattr = bgp_attr_intern(&hattr);
+ bgp_attr_flush(&hattr);
+
+ /* Now iattr is an allocated interned attr */
+
+ /*
+ * RFG-specific code
+ *
+ * Sets RD in dummy HD
+ * Allocates ecom
+ */
+ {
+
+ memset(&prd, 0, sizeof(prd));
+ prd = rfg->rd;
+ prd.family = AF_UNSPEC;
+ prd.prefixlen = 64;
+
+ if (rfg->rd.family == AF_UNIX) {
+ rfapi_set_autord_from_vn(&prd, &vnaddr);
+ }
+
+ if (rfg->rt_export_list)
+ ecom = ecommunity_dup(
+ bgp->rfapi_cfg->rfg_redist->rt_export_list);
+ else
+ ecom = ecommunity_new();
+
+ if (iattr && bgp_attr_get_ecommunity(iattr))
+ ecom = ecommunity_merge(ecom,
+ bgp_attr_get_ecommunity(iattr));
+ }
+
+ local_pref = calc_local_pref(iattr, peer);
+
+ if (VNC_DEBUG(IMPORT_BGP_ADD_ROUTE)) {
+ char buf[BUFSIZ];
+
+ buf[0] = 0;
+ rfapiRfapiIpAddr2Str(&vnaddr, buf, BUFSIZ);
+ buf[BUFSIZ - 1] = 0;
+ vnc_zlog_debug_any("%s: setting vnaddr to %s", __func__, buf);
+ }
+
+ vncHDBgpDirect.peer = peer;
+ add_vnc_route(&vncHDBgpDirect, bgp, SAFI_MPLS_VPN, prefix, &prd,
+ &vnaddr, &local_pref, &(bgp->rfapi_cfg->redist_lifetime),
+ NULL, /* RFP options */
+ NULL, NULL, ecom, NULL, /* med */
+ NULL, /* label: default */
+ ZEBRA_ROUTE_BGP_DIRECT, BGP_ROUTE_REDISTRIBUTE,
+ ahr_flags);
+ vncHDBgpDirect.peer = NULL;
+
+ if (ecom)
+ ecommunity_free(&ecom);
+}
+
+static void vnc_import_bgp_del_route_mode_plain(struct bgp *bgp,
+ const struct prefix *prefix,
+ struct bgp_path_info *info)
+{
+ struct prefix_rd prd;
+ afi_t afi = family2afi(prefix->family);
+ struct prefix *vn_pfx = NULL;
+ struct rfapi_ip_addr vnaddr;
+ struct prefix vn_pfx_space;
+
+
+ assert(afi);
+
+ /*
+ * Compute VN address
+ */
+
+ if (info) {
+ rfapiUnicastNexthop2Prefix(afi, info->attr, &vn_pfx_space);
+ } else {
+ vnc_zlog_debug_verbose("%s: no attr, can't delete route",
+ __func__);
+ return;
+ }
+ vn_pfx = &vn_pfx_space;
+
+ vnaddr.addr_family = vn_pfx->family;
+ switch (vn_pfx->family) {
+ case AF_INET:
+ if (vn_pfx->prefixlen != IPV4_MAX_BITLEN) {
+ vnc_zlog_debug_verbose(
+ "%s: redist VN plen (%d) != 32, skipping",
+ __func__, vn_pfx->prefixlen);
+ return;
+ }
+ vnaddr.addr.v4 = vn_pfx->u.prefix4;
+ break;
+
+ case AF_INET6:
+ if (vn_pfx->prefixlen != IPV6_MAX_BITLEN) {
+ vnc_zlog_debug_verbose(
+ "%s: redist VN plen (%d) != 128, skipping",
+ __func__, vn_pfx->prefixlen);
+ return;
+ }
+ vnaddr.addr.v6 = vn_pfx->u.prefix6;
+ break;
+
+ default:
+ vnc_zlog_debug_verbose(
+ "%s: no redist RFG VN host pfx configured, skipping",
+ __func__);
+ return;
+ }
+
+
+ memset(&prd, 0, sizeof(prd));
+ if (rfapi_set_autord_from_vn(&prd, &vnaddr)) {
+ vnc_zlog_debug_verbose("%s: can't auto-assign RD, skipping",
+ __func__);
+ return;
+ }
+
+ vncHDBgpDirect.peer = info->peer;
+ vnc_zlog_debug_verbose("%s: setting peer to %p", __func__,
+ vncHDBgpDirect.peer);
+ del_vnc_route(&vncHDBgpDirect, info->peer, bgp, SAFI_MPLS_VPN, prefix,
+ &prd, ZEBRA_ROUTE_BGP_DIRECT, BGP_ROUTE_REDISTRIBUTE,
+ NULL, 1);
+
+ vncHDBgpDirect.peer = NULL;
+}
+
+static void vnc_import_bgp_del_route_mode_nvegroup(struct bgp *bgp,
+ const struct prefix *prefix,
+ struct bgp_path_info *info)
+{
+ struct prefix_rd prd;
+ afi_t afi = family2afi(prefix->family);
+ struct rfapi_nve_group_cfg *rfg = NULL;
+ struct prefix *vn_pfx = NULL;
+ struct rfapi_ip_addr vnaddr;
+
+
+ assert(afi);
+
+ rfg = bgp->rfapi_cfg->rfg_redist;
+ assert(rfg);
+
+ /*
+ * Compute VN address
+ */
+
+ /*
+ * export nve group's VN addr prefix must be a /32 which
+ * will yield the VN addr to use
+ */
+ vn_pfx = &rfg->vn_prefix;
+
+
+ vnaddr.addr_family = vn_pfx->family;
+ switch (vn_pfx->family) {
+ case AF_INET:
+ if (vn_pfx->prefixlen != IPV4_MAX_BITLEN) {
+ vnc_zlog_debug_verbose(
+ "%s: redist VN plen (%d) != 32, skipping",
+ __func__, vn_pfx->prefixlen);
+ return;
+ }
+ vnaddr.addr.v4 = vn_pfx->u.prefix4;
+ break;
+
+ case AF_INET6:
+ if (vn_pfx->prefixlen != IPV6_MAX_BITLEN) {
+ vnc_zlog_debug_verbose(
+ "%s: redist VN plen (%d) != 128, skipping",
+ __func__, vn_pfx->prefixlen);
+ return;
+ }
+ vnaddr.addr.v6 = vn_pfx->u.prefix6;
+ break;
+
+ default:
+ vnc_zlog_debug_verbose(
+ "%s: no redist RFG VN host pfx configured, skipping",
+ __func__);
+ return;
+ }
+
+ memset(&prd, 0, sizeof(prd));
+ prd = rfg->rd;
+ prd.family = AF_UNSPEC;
+ prd.prefixlen = 64;
+
+ if (rfg->rd.family == AF_UNIX) {
+ /* means "auto" with VN addr */
+ if (rfapi_set_autord_from_vn(&prd, &vnaddr)) {
+ vnc_zlog_debug_verbose(
+ "%s: can't auto-assign RD, skipping", __func__);
+ return;
+ }
+ }
+
+
+ vncHDBgpDirect.peer = info->peer;
+ vnc_zlog_debug_verbose("%s: setting peer to %p", __func__,
+ vncHDBgpDirect.peer);
+ del_vnc_route(&vncHDBgpDirect, info->peer, bgp, SAFI_MPLS_VPN, prefix,
+ &prd, ZEBRA_ROUTE_BGP_DIRECT, BGP_ROUTE_REDISTRIBUTE,
+ NULL, 1);
+
+ vncHDBgpDirect.peer = NULL;
+}
+
+static void vnc_import_bgp_del_route_mode_resolve_nve_one_bi(
+ struct bgp *bgp, afi_t afi, struct bgp_path_info *bpi, /* VPN bpi */
+ struct prefix_rd *prd, /* RD */
+ const struct prefix *prefix) /* unicast route prefix */
+{
+ struct prefix un;
+
+ if (bpi->type != ZEBRA_ROUTE_BGP
+ && bpi->type != ZEBRA_ROUTE_BGP_DIRECT) {
+
+ return;
+ }
+ if (bpi->sub_type != BGP_ROUTE_NORMAL
+ && bpi->sub_type != BGP_ROUTE_STATIC
+ && bpi->sub_type != BGP_ROUTE_RFP) {
+
+ return;
+ }
+ if (CHECK_FLAG(bpi->flags, BGP_PATH_REMOVED))
+ return;
+
+ vncHDResolveNve.peer = bpi->peer;
+ if (!rfapiGetVncTunnelUnAddr(bpi->attr, &un)) {
+ if (rfapiQprefix2Raddr(&un, &vncHDResolveNve.un_addr))
+ return;
+ } else {
+ memset(&vncHDResolveNve.un_addr, 0,
+ sizeof(vncHDResolveNve.un_addr));
+ }
+
+ del_vnc_route(&vncHDResolveNve, vncHDResolveNve.peer, bgp,
+ SAFI_MPLS_VPN, prefix, /* unicast route prefix */
+ prd, ZEBRA_ROUTE_BGP_DIRECT, BGP_ROUTE_REDISTRIBUTE, NULL,
+ 0); /* flags */
+}
+
+static void vnc_import_bgp_del_route_mode_resolve_nve_one_rd(
+ struct prefix_rd *prd,
+ struct bgp_table *table_rd, /* per-rd VPN route table */
+ afi_t afi, struct bgp *bgp,
+ const struct prefix *prefix, /* unicast prefix */
+ const struct prefix *ubpi_nexthop) /* unicast bpi's nexthop */
+{
+ struct bgp_dest *bd;
+ struct bgp_path_info *bpi;
+
+ if (!table_rd)
+ return;
+
+ vnc_zlog_debug_verbose("%s: ubpi_nexthop=%pFX", __func__, ubpi_nexthop);
+
+
+ /* exact match */
+ bd = bgp_node_lookup(table_rd, ubpi_nexthop);
+ if (!bd) {
+ vnc_zlog_debug_verbose(
+ "%s: no match in RD's table for ubpi_nexthop",
+ __func__);
+ return;
+ }
+
+ /* Iterate over bgp_info items at this node */
+ for (bpi = bgp_dest_get_bgp_path_info(bd); bpi; bpi = bpi->next) {
+
+ vnc_import_bgp_del_route_mode_resolve_nve_one_bi(
+ bgp, afi, bpi, /* VPN bpi */
+ prd, /* VPN RD */
+ prefix); /* unicast route prefix */
+ }
+
+ bgp_dest_unlock_node(bd);
+}
+
+static void
+vnc_import_bgp_del_route_mode_resolve_nve(struct bgp *bgp, afi_t afi,
+ const struct prefix *prefix,
+ struct bgp_path_info *info)
+{
+ struct ecommunity *ecom = NULL;
+ struct prefix pfx_unicast_nexthop = {0}; /* happy valgrind */
+
+ // struct listnode *hnode;
+ // struct rfapi_descriptor *rfd;
+ struct prefix_bag *pb;
+ void *cursor;
+ struct skiplist *sl = bgp->rfapi->resolve_nve_nexthop;
+ int rc;
+ struct bgp_dest *bdp; /* prd table node */
+
+ if (!sl) {
+ vnc_zlog_debug_verbose("%s: no RHN entries, skipping",
+ __func__);
+ return;
+ }
+
+ if (info->type != ZEBRA_ROUTE_BGP) {
+ vnc_zlog_debug_verbose(
+ "%s: unicast type %d=\"%s\" is not %d=%s, skipping",
+ __func__, info->type, zebra_route_string(info->type),
+ ZEBRA_ROUTE_BGP, "ZEBRA_ROUTE_BGP");
+ return;
+ }
+
+ if (process_unicast_route(bgp, afi, prefix, info, &ecom,
+ &pfx_unicast_nexthop)) {
+
+ vnc_zlog_debug_verbose(
+ "%s: process_unicast_route error, skipping", __func__);
+ return;
+ }
+
+ rc = skiplist_first_value(sl, &pfx_unicast_nexthop, (void *)&pb,
+ &cursor);
+ while (!rc) {
+ if (pb->ubpi == info) {
+ skiplist_delete(sl, &pfx_unicast_nexthop, pb);
+ bgp_path_info_unlock(info);
+ break;
+ }
+ rc = skiplist_next_value(sl, &pfx_unicast_nexthop, (void *)&pb,
+ &cursor);
+ }
+
+ /*
+ * Iterate over RDs in VPN RIB. For each RD, look up unicast nexthop
+ * (exact match, /32). If an exact match is found, call add_vnc_route.
+ */
+
+ for (bdp = bgp_table_top(bgp->rib[afi][SAFI_MPLS_VPN]); bdp;
+ bdp = bgp_route_next(bdp)) {
+
+ struct bgp_table *table;
+
+ table = bgp_dest_get_bgp_table_info(bdp);
+
+ if (!table)
+ continue;
+
+ vnc_import_bgp_del_route_mode_resolve_nve_one_rd(
+ (struct prefix_rd *)bgp_dest_get_prefix(bdp), table,
+ afi, bgp, prefix, &pfx_unicast_nexthop);
+ }
+
+ if (ecom)
+ ecommunity_free(&ecom);
+}
+
+
+/***********************************************************************
+ * Add/Delete CE->NVE routes
+ ***********************************************************************/
+
+/*
+ * Should be called whan a bpi is added to VPN RIB. This function
+ * will check if it is a host route and return immediately if not.
+ */
+void vnc_import_bgp_add_vnc_host_route_mode_resolve_nve(
+ struct bgp *bgp, struct prefix_rd *prd, /* RD */
+ struct bgp_table *table_rd, /* per-rd VPN route table */
+ const struct prefix *prefix, /* VPN prefix */
+ struct bgp_path_info *bpi) /* new VPN host route */
+{
+ afi_t afi = family2afi(prefix->family);
+ struct skiplist *sl = NULL;
+ int rc;
+ struct prefix_bag *pb;
+ void *cursor;
+ struct rfapi_cfg *hc = NULL;
+
+ vnc_zlog_debug_verbose("%s: entry", __func__);
+
+ if (afi != AFI_IP && afi != AFI_IP6) {
+ vnc_zlog_debug_verbose("%s: bad afi %d, skipping", __func__,
+ afi);
+ return;
+ }
+
+ if (!(hc = bgp->rfapi_cfg)) {
+ vnc_zlog_debug_verbose("%s: bgp->rfapi_cfg is NULL, skipping",
+ __func__);
+ return;
+ }
+
+ /* check vnc redist flag for bgp direct routes */
+ if (!hc->redist[afi][ZEBRA_ROUTE_BGP_DIRECT]) {
+ vnc_zlog_debug_verbose(
+ "%s: bgp->rfapi_cfg->redist[afi=%d][type=ZEBRA_ROUTE_BGP_DIRECT] is 0, skipping",
+ __func__, afi);
+ return;
+ }
+
+ if (hc->redist_mode != VNC_REDIST_MODE_RESOLVE_NVE) {
+ vnc_zlog_debug_verbose("%s: not in resolve-nve mode, skipping",
+ __func__);
+ return;
+ }
+
+ if (bgp->rfapi)
+ sl = bgp->rfapi->resolve_nve_nexthop;
+
+ if (!sl) {
+ vnc_zlog_debug_verbose(
+ "%s: no resolve_nve_nexthop skiplist, skipping",
+ __func__);
+ return;
+ }
+
+ if (!is_host_prefix(prefix)) {
+ vnc_zlog_debug_verbose("%s: not host prefix, skipping",
+ __func__);
+ return;
+ }
+
+ rc = skiplist_first_value(sl, prefix, (void *)&pb, &cursor);
+ while (!rc) {
+ struct ecommunity *ecom;
+ struct prefix pfx_unicast_nexthop;
+ uint32_t *med = NULL;
+ uint32_t local_pref;
+
+ memset(&pfx_unicast_nexthop, 0,
+ sizeof(pfx_unicast_nexthop)); /* keep valgrind happy */
+
+ if (VNC_DEBUG(IMPORT_BGP_ADD_ROUTE))
+ vnc_zlog_debug_any(
+ "%s: examining RHN Entry (q=%p): upfx=%pFX, hpfx=%pFX, ubpi=%p",
+ __func__, cursor, &pb->upfx, &pb->hpfx,
+ pb->ubpi);
+
+ if (process_unicast_route(bgp, afi, &pb->upfx, pb->ubpi, &ecom,
+ &pfx_unicast_nexthop)) {
+
+ vnc_zlog_debug_verbose(
+ "%s: process_unicast_route error, skipping",
+ __func__);
+ continue;
+ }
+ local_pref = calc_local_pref(pb->ubpi->attr, pb->ubpi->peer);
+
+ if (pb->ubpi->attr->flag
+ & ATTR_FLAG_BIT(BGP_ATTR_MULTI_EXIT_DISC))
+ med = &pb->ubpi->attr->med;
+
+ /*
+ * Sanity check
+ */
+ if (vnc_prefix_cmp(&pfx_unicast_nexthop, prefix)) {
+ vnc_zlog_debug_verbose(
+ "%s: FATAL: resolve_nve_nexthop list item bpi nexthop %pFX != nve pfx %pFX",
+ __func__, &pfx_unicast_nexthop, prefix);
+ assert(0);
+ }
+
+ vnc_import_bgp_add_route_mode_resolve_nve_one_bi(
+ bgp, afi, bpi, /* VPN bpi */
+ prd, &pb->upfx, /* unicast prefix */
+ &local_pref, med, ecom);
+
+ if (ecom)
+ ecommunity_free(&ecom);
+
+#if DEBUG_RHN_LIST
+ /* debug */
+ {
+ vnc_zlog_debug_verbose(
+ "%s: advancing past RHN Entry (q=%p): with prefix %pFX",
+ __func__, cursor, prefix);
+ print_rhn_list(__func__, NULL); /* debug */
+ }
+#endif
+ rc = skiplist_next_value(sl, prefix, (void *)&pb, &cursor);
+ }
+ vnc_zlog_debug_verbose("%s: done", __func__);
+}
+
+
+void vnc_import_bgp_del_vnc_host_route_mode_resolve_nve(
+ struct bgp *bgp, struct prefix_rd *prd, /* RD */
+ struct bgp_table *table_rd, /* per-rd VPN route table */
+ const struct prefix *prefix, /* VPN prefix */
+ struct bgp_path_info *bpi) /* old VPN host route */
+{
+ afi_t afi = family2afi(prefix->family);
+ struct skiplist *sl = NULL;
+ struct prefix_bag *pb;
+ void *cursor;
+ struct rfapi_cfg *hc = NULL;
+ int rc;
+
+ vnc_zlog_debug_verbose("%s(bgp=%p, nve prefix=%pFX)", __func__, bgp,
+ prefix);
+
+ if (afi != AFI_IP && afi != AFI_IP6)
+ return;
+
+ if (!(hc = bgp->rfapi_cfg)) {
+ vnc_zlog_debug_verbose("%s: bgp->rfapi_cfg is NULL, skipping",
+ __func__);
+ return;
+ }
+
+ /* check vnc redist flag for bgp direct routes */
+ if (!hc->redist[afi][ZEBRA_ROUTE_BGP_DIRECT]) {
+ vnc_zlog_debug_verbose(
+ "%s: bgp->rfapi_cfg->redist[afi=%d][type=ZEBRA_ROUTE_BGP_DIRECT] is 0, skipping",
+ __func__, afi);
+ return;
+ }
+
+ if (hc->redist_mode != VNC_REDIST_MODE_RESOLVE_NVE) {
+ vnc_zlog_debug_verbose("%s: not in resolve-nve mode, skipping",
+ __func__);
+ return;
+ }
+
+ if (bgp->rfapi)
+ sl = bgp->rfapi->resolve_nve_nexthop;
+
+ if (!sl) {
+ vnc_zlog_debug_verbose("%s: no RHN entries, skipping",
+ __func__);
+ return;
+ }
+
+ if (!is_host_prefix(prefix)) {
+ vnc_zlog_debug_verbose("%s: not host route, skip", __func__);
+ return;
+ }
+
+ /*
+ * Find all entries with key == CE in the RHN list
+ */
+ rc = skiplist_first_value(sl, prefix, (void *)&pb, &cursor);
+ while (!rc) {
+
+ struct ecommunity *ecom;
+ struct prefix pfx_unicast_nexthop;
+
+ memset(&pfx_unicast_nexthop, 0,
+ sizeof(pfx_unicast_nexthop)); /* keep valgrind happy */
+
+ if (process_unicast_route(bgp, afi, &pb->upfx, pb->ubpi, &ecom,
+ &pfx_unicast_nexthop)) {
+
+ vnc_zlog_debug_verbose(
+ "%s: process_unicast_route error, skipping",
+ __func__);
+ continue;
+ }
+
+ /*
+ * Sanity check
+ */
+ if (vnc_prefix_cmp(&pfx_unicast_nexthop, prefix)) {
+ vnc_zlog_debug_verbose(
+ "%s: FATAL: resolve_nve_nexthop list item bpi nexthop %pFX != nve pfx %pFX",
+ __func__, &pfx_unicast_nexthop, prefix);
+ assert(0);
+ }
+
+ vnc_import_bgp_del_route_mode_resolve_nve_one_bi(
+ bgp, afi, bpi, prd, &pb->upfx);
+
+ if (ecom)
+ ecommunity_free(&ecom);
+
+ rc = skiplist_next_value(sl, prefix, (void *)&pb, &cursor);
+ }
+}
+
+
+/***********************************************************************
+ * Exterior Routes
+ ***********************************************************************/
+
+#define DEBUG_IS_USABLE_INTERIOR 1
+
+static int is_usable_interior_route(struct bgp_path_info *bpi_interior)
+{
+ if (!VALID_INTERIOR_TYPE(bpi_interior->type)) {
+#if DEBUG_IS_USABLE_INTERIOR
+ vnc_zlog_debug_verbose(
+ "%s: NO: type %d is not valid interior type", __func__,
+ bpi_interior->type);
+#endif
+ return 0;
+ }
+ if (!CHECK_FLAG(bpi_interior->flags, BGP_PATH_VALID)) {
+#if DEBUG_IS_USABLE_INTERIOR
+ vnc_zlog_debug_verbose("%s: NO: BGP_PATH_VALID not set",
+ __func__);
+#endif
+ return 0;
+ }
+ return 1;
+}
+
+/*
+ * There should be only one of these per prefix at a time.
+ * This should be called as a result of selection operation
+ *
+ * NB should be called espacially for bgp instances that are named,
+ * because the exterior routes will always come from one of those.
+ * We filter here on the instance name to make sure we get only the
+ * right routes.
+ */
+static void vnc_import_bgp_exterior_add_route_it(
+ struct bgp *bgp, /* exterior instance, we hope */
+ const struct prefix *prefix, /* unicast prefix */
+ struct bgp_path_info *info, /* unicast info */
+ struct rfapi_import_table *it_only) /* NULL, or limit to this IT */
+{
+ struct rfapi *h;
+ struct rfapi_cfg *hc;
+ struct prefix pfx_orig_nexthop;
+ struct rfapi_import_table *it;
+ struct bgp *bgp_default = bgp_get_default();
+ afi_t afi = family2afi(prefix->family);
+
+ if (!bgp_default)
+ return;
+
+ h = bgp_default->rfapi;
+ hc = bgp_default->rfapi_cfg;
+
+ vnc_zlog_debug_verbose("%s: entry with it=%p", __func__, it_only);
+
+ if (!h || !hc) {
+ vnc_zlog_debug_verbose(
+ "%s: rfapi or rfapi_cfg not instantiated, skipping",
+ __func__);
+ return;
+ }
+ if (!hc->redist_bgp_exterior_view) {
+ vnc_zlog_debug_verbose("%s: exterior view not set, skipping",
+ __func__);
+ return;
+ }
+ if (bgp != hc->redist_bgp_exterior_view) {
+ vnc_zlog_debug_verbose(
+ "%s: bgp %p != hc->redist_bgp_exterior_view %p, skipping",
+ __func__, bgp, hc->redist_bgp_exterior_view);
+ return;
+ }
+
+ if (!hc->redist[afi][ZEBRA_ROUTE_BGP_DIRECT_EXT]) {
+ vnc_zlog_debug_verbose(
+ "%s: redist of exterior routes not enabled, skipping",
+ __func__);
+ return;
+ }
+
+ /*
+ * Extract nexthop from exterior route
+ *
+ * Incoming prefix is unicast. If v6, it is in multiprotocol area,
+ * but if v4 it is in attr->nexthop
+ */
+ rfapiUnicastNexthop2Prefix(afi, info->attr, &pfx_orig_nexthop);
+
+ for (it = h->imports; it; it = it->next) {
+ struct agg_table *table;
+ struct agg_node *rn;
+ struct agg_node *par;
+ struct bgp_path_info *bpi_interior;
+ int have_usable_route;
+
+ vnc_zlog_debug_verbose("%s: doing it %p", __func__, it);
+
+ if (it_only && (it_only != it)) {
+ vnc_zlog_debug_verbose("%s: doesn't match it_only %p",
+ __func__, it_only);
+ continue;
+ }
+
+ table = it->imported_vpn[afi];
+
+ for (rn = agg_node_match(table, &pfx_orig_nexthop),
+ have_usable_route = 0;
+ (!have_usable_route) && rn;) {
+
+ vnc_zlog_debug_verbose("%s: it %p trying rn %p",
+ __func__, it, rn);
+
+ for (bpi_interior = rn->info; bpi_interior;
+ bpi_interior = bpi_interior->next) {
+ struct prefix_rd *prd;
+ struct attr new_attr;
+ uint32_t label = 0;
+
+ if (!is_usable_interior_route(bpi_interior))
+ continue;
+
+ vnc_zlog_debug_verbose(
+ "%s: usable: bpi_interior %p", __func__,
+ bpi_interior);
+
+ /*
+ * have a legitimate route to exterior's nexthop
+ * via NVE.
+ *
+ * Import unicast route to the import table
+ */
+ have_usable_route = 1;
+
+ if (bpi_interior->extra) {
+ prd = &bpi_interior->extra->vnc.import
+ .rd;
+ label = decode_label(
+ &bpi_interior->extra->label[0]);
+ } else
+ prd = NULL;
+
+ /* use local_pref from unicast route */
+ memset(&new_attr, 0, sizeof(new_attr));
+ new_attr = *bpi_interior->attr;
+ if (info->attr->flag
+ & ATTR_FLAG_BIT(BGP_ATTR_LOCAL_PREF)) {
+ new_attr.local_pref =
+ info->attr->local_pref;
+ new_attr.flag |= ATTR_FLAG_BIT(
+ BGP_ATTR_LOCAL_PREF);
+ }
+
+ rfapiBgpInfoFilteredImportVPN(
+ it, FIF_ACTION_UPDATE,
+ bpi_interior->peer, NULL, /* rfd */
+ prefix, NULL, afi, prd, &new_attr,
+ ZEBRA_ROUTE_BGP_DIRECT_EXT,
+ BGP_ROUTE_REDISTRIBUTE, &label);
+ }
+
+ if (have_usable_route) {
+ /*
+ * Make monitor
+ *
+ * TBD factor this out into its own function
+ */
+ struct prefix *pfx_mon = prefix_new();
+ if (!RFAPI_MONITOR_EXTERIOR(rn)->source) {
+ RFAPI_MONITOR_EXTERIOR(rn)->source =
+ skiplist_new(
+ 0, NULL,
+ prefix_free_lists);
+ agg_lock_node(rn); /* for skiplist */
+ }
+ agg_lock_node(rn); /* for skiplist entry */
+ prefix_copy(pfx_mon, prefix);
+ if (!skiplist_insert(
+ RFAPI_MONITOR_EXTERIOR(rn)->source,
+ info, pfx_mon)) {
+
+ bgp_path_info_lock(info);
+ }
+ }
+ par = agg_node_parent(rn);
+ if (par)
+ agg_lock_node(par);
+ agg_unlock_node(rn);
+ rn = par;
+ }
+ if (rn)
+ agg_unlock_node(rn);
+
+ if (!have_usable_route) {
+ struct prefix *pfx_mon = prefix_new();
+ prefix_copy(pfx_mon, prefix);
+ if (!skiplist_insert(it->monitor_exterior_orphans, info,
+ pfx_mon)) {
+
+ bgp_path_info_lock(info);
+ }
+ }
+ }
+}
+
+void vnc_import_bgp_exterior_add_route(
+ struct bgp *bgp, /* exterior instance, we hope */
+ const struct prefix *prefix, /* unicast prefix */
+ struct bgp_path_info *info) /* unicast info */
+{
+ vnc_import_bgp_exterior_add_route_it(bgp, prefix, info, NULL);
+}
+
+/*
+ * There should be only one of these per prefix at a time.
+ * This should probably be called as a result of selection operation.
+ *
+ * NB should be called espacially for bgp instances that are named,
+ * because the exterior routes will always come from one of those.
+ * We filter here on the instance name to make sure we get only the
+ * right routes.
+ */
+void vnc_import_bgp_exterior_del_route(
+ struct bgp *bgp, const struct prefix *prefix, /* unicast prefix */
+ struct bgp_path_info *info) /* unicast info */
+{
+ struct rfapi *h;
+ struct rfapi_cfg *hc;
+ struct rfapi_import_table *it;
+ struct prefix pfx_orig_nexthop;
+ afi_t afi = family2afi(prefix->family);
+ struct bgp *bgp_default = bgp_get_default();
+
+ if (!bgp_default)
+ return;
+
+ memset(&pfx_orig_nexthop, 0,
+ sizeof(pfx_orig_nexthop)); /* keep valgrind happy */
+
+ h = bgp_default->rfapi;
+ hc = bgp_default->rfapi_cfg;
+
+ if (!h || !hc) {
+ vnc_zlog_debug_verbose(
+ "%s: rfapi or rfapi_cfg not instantiated, skipping",
+ __func__);
+ return;
+ }
+ if (!hc->redist_bgp_exterior_view) {
+ vnc_zlog_debug_verbose("%s: exterior view not set, skipping",
+ __func__);
+ return;
+ }
+ if (bgp != hc->redist_bgp_exterior_view) {
+ vnc_zlog_debug_verbose(
+ "%s: bgp %p != hc->redist_bgp_exterior_view %p, skipping",
+ __func__, bgp, hc->redist_bgp_exterior_view);
+ return;
+ }
+ if (!hc->redist[afi][ZEBRA_ROUTE_BGP_DIRECT_EXT]) {
+ vnc_zlog_debug_verbose(
+ "%s: redist of exterior routes no enabled, skipping",
+ __func__);
+ return;
+ }
+
+ /*
+ * Extract nexthop from exterior route
+ *
+ * Incoming prefix is unicast. If v6, it is in multiprotocol area,
+ * but if v4 it is in attr->nexthop
+ */
+ rfapiUnicastNexthop2Prefix(afi, info->attr, &pfx_orig_nexthop);
+
+ for (it = h->imports; it; it = it->next) {
+ struct agg_table *table;
+ struct agg_node *rn;
+ struct agg_node *par;
+ struct bgp_path_info *bpi_interior;
+ int have_usable_route;
+
+ table = it->imported_vpn[afi];
+
+ for (rn = agg_node_match(table, &pfx_orig_nexthop),
+ have_usable_route = 0;
+ (!have_usable_route) && rn;) {
+
+ for (bpi_interior = rn->info; bpi_interior;
+ bpi_interior = bpi_interior->next) {
+ struct prefix_rd *prd;
+ uint32_t label = 0;
+
+ if (!is_usable_interior_route(bpi_interior))
+ continue;
+
+ /*
+ * have a legitimate route to exterior's nexthop
+ * via NVE.
+ *
+ * Import unicast route to the import table
+ */
+ have_usable_route = 1;
+
+ if (bpi_interior->extra) {
+ prd = &bpi_interior->extra->vnc.import
+ .rd;
+ label = decode_label(
+ &bpi_interior->extra->label[0]);
+ } else
+ prd = NULL;
+
+ rfapiBgpInfoFilteredImportVPN(
+ it, FIF_ACTION_KILL, bpi_interior->peer,
+ NULL, /* rfd */
+ prefix, NULL, afi, prd,
+ bpi_interior->attr,
+ ZEBRA_ROUTE_BGP_DIRECT_EXT,
+ BGP_ROUTE_REDISTRIBUTE, &label);
+
+ /*
+ * Delete monitor
+ *
+ * TBD factor this out into its own function
+ */
+ {
+ if (RFAPI_MONITOR_EXTERIOR(rn)
+ ->source) {
+ if (!skiplist_delete(
+ RFAPI_MONITOR_EXTERIOR(
+ rn)
+ ->source,
+ info, NULL)) {
+
+ bgp_path_info_unlock(
+ info);
+ agg_unlock_node(
+ rn); /* sl entry
+ */
+ }
+ if (skiplist_empty(
+ RFAPI_MONITOR_EXTERIOR(
+ rn)
+ ->source)) {
+ skiplist_free(
+ RFAPI_MONITOR_EXTERIOR(
+ rn)
+ ->source);
+ RFAPI_MONITOR_EXTERIOR(
+ rn)
+ ->source = NULL;
+ agg_unlock_node(
+ rn); /* skiplist
+ itself
+ */
+ }
+ }
+ }
+ }
+ par = agg_node_parent(rn);
+ if (par)
+ agg_lock_node(par);
+ agg_unlock_node(rn);
+ rn = par;
+ }
+ if (rn)
+ agg_unlock_node(rn);
+
+ if (!have_usable_route) {
+ if (!skiplist_delete(it->monitor_exterior_orphans, info,
+ NULL)) {
+
+ bgp_path_info_unlock(info);
+ }
+ }
+ }
+}
+
+/*
+ * This function should be called after a new interior VPN route
+ * has been added to an import_table.
+ *
+ * NB should also be called whenever an existing vpn interior route
+ * becomes valid (e.g., valid_interior_count is inremented)
+ */
+void vnc_import_bgp_exterior_add_route_interior(
+ struct bgp *bgp, struct rfapi_import_table *it,
+ struct agg_node *rn_interior, /* VPN IT node */
+ struct bgp_path_info *bpi_interior) /* VPN IT route */
+{
+ const struct prefix *p = agg_node_get_prefix(rn_interior);
+ afi_t afi = family2afi(p->family);
+ struct agg_node *par;
+ struct bgp_path_info *bpi_exterior;
+ struct prefix *pfx_exterior; /* exterior pfx */
+ void *cursor;
+ int rc;
+ struct list *list_adopted;
+
+ vnc_zlog_debug_verbose("%s: entry", __func__);
+
+ if (!is_usable_interior_route(bpi_interior)) {
+ vnc_zlog_debug_verbose(
+ "%s: not usable interior route, skipping", __func__);
+ return;
+ }
+
+ if (!bgp->rfapi_cfg->redist[afi][ZEBRA_ROUTE_BGP_DIRECT_EXT]) {
+ vnc_zlog_debug_verbose(
+ "%s: redist of exterior routes no enabled, skipping",
+ __func__);
+ return;
+ }
+
+ if (it == bgp->rfapi->it_ce) {
+ vnc_zlog_debug_verbose("%s: import table is it_ce, skipping",
+ __func__);
+ return;
+ }
+
+ /*debugging */
+ vnc_zlog_debug_verbose("%s: interior prefix=%pRN, bpi type=%d",
+ __func__, rn_interior, bpi_interior->type);
+
+ if (RFAPI_HAS_MONITOR_EXTERIOR(rn_interior)) {
+
+ int count = 0; /* debugging */
+
+ vnc_zlog_debug_verbose(
+ "%s: has exterior monitor; ext src: %p", __func__,
+ RFAPI_MONITOR_EXTERIOR(rn_interior)->source);
+
+ /*
+ * There is a monitor here already. Therefore, we do not need
+ * to do any pulldown. Just construct exterior routes based
+ * on the new interior route.
+ */
+ cursor = NULL;
+ for (rc = skiplist_next(
+ RFAPI_MONITOR_EXTERIOR(rn_interior)->source,
+ (void **)&bpi_exterior, (void **)&pfx_exterior,
+ &cursor);
+ !rc; rc = skiplist_next(
+ RFAPI_MONITOR_EXTERIOR(rn_interior)->source,
+ (void **)&bpi_exterior,
+ (void **)&pfx_exterior, &cursor)) {
+
+ struct prefix_rd *prd;
+ struct attr new_attr;
+ uint32_t label = 0;
+
+
+ ++count; /* debugging */
+
+ assert(bpi_exterior);
+ assert(pfx_exterior);
+
+ if (bpi_interior->extra) {
+ prd = &bpi_interior->extra->vnc.import.rd;
+ label = decode_label(
+ &bpi_interior->extra->label[0]);
+ } else
+ prd = NULL;
+
+ /* use local_pref from unicast route */
+ memset(&new_attr, 0, sizeof(struct attr));
+ new_attr = *bpi_interior->attr;
+ if (bpi_exterior
+ && (bpi_exterior->attr->flag
+ & ATTR_FLAG_BIT(BGP_ATTR_LOCAL_PREF))) {
+ new_attr.local_pref =
+ bpi_exterior->attr->local_pref;
+ new_attr.flag |=
+ ATTR_FLAG_BIT(BGP_ATTR_LOCAL_PREF);
+ }
+
+ rfapiBgpInfoFilteredImportVPN(
+ it, FIF_ACTION_UPDATE, bpi_interior->peer,
+ NULL, /* rfd */
+ pfx_exterior, NULL, afi, prd, &new_attr,
+ ZEBRA_ROUTE_BGP_DIRECT_EXT,
+ BGP_ROUTE_REDISTRIBUTE, &label);
+ }
+ vnc_zlog_debug_verbose(
+ "%s: finished constructing exteriors based on existing monitors",
+ __func__);
+ return;
+ }
+
+ vnc_zlog_debug_verbose("%s: no exterior monitor", __func__);
+
+ /*
+ * No monitor at this node. Is this the first valid interior
+ * route at this node?
+ */
+ if (RFAPI_MONITOR_EXTERIOR(rn_interior)->valid_interior_count > 1) {
+ vnc_zlog_debug_verbose(
+ "%s: new interior route not first valid one, skipping pulldown",
+ __func__);
+ return;
+ }
+
+ /*
+ * Look up the tree for possible pulldown candidates.
+ * Find nearest parent with an exterior route monitor
+ */
+ for (par = agg_node_parent(rn_interior); par;
+ par = agg_node_parent(par)) {
+ if (RFAPI_HAS_MONITOR_EXTERIOR(par))
+ break;
+ }
+
+ if (par) {
+
+ vnc_zlog_debug_verbose(
+ "%s: checking parent %p for possible pulldowns",
+ __func__, par);
+
+ /* check monitors at par for possible pulldown */
+ cursor = NULL;
+ for (rc = skiplist_next(RFAPI_MONITOR_EXTERIOR(par)->source,
+ (void **)&bpi_exterior,
+ (void **)&pfx_exterior, &cursor);
+ !rc;
+ rc = skiplist_next(RFAPI_MONITOR_EXTERIOR(par)->source,
+ (void **)&bpi_exterior,
+ (void **)&pfx_exterior, &cursor)) {
+
+ struct prefix pfx_nexthop;
+
+ memset(&pfx_nexthop, 0,
+ sizeof(struct prefix)); /* keep valgrind happy */
+
+ /* check original nexthop for prefix match */
+ rfapiUnicastNexthop2Prefix(afi, bpi_exterior->attr,
+ &pfx_nexthop);
+
+ if (prefix_match(p, &pfx_nexthop)) {
+
+ struct bgp_path_info *bpi;
+ struct prefix_rd *prd;
+ struct attr new_attr;
+ uint32_t label = 0;
+
+ /* do pull-down */
+
+ /*
+ * add monitor to longer prefix
+ */
+ struct prefix *pfx_mon = prefix_new();
+ prefix_copy(pfx_mon, pfx_exterior);
+ if (!RFAPI_MONITOR_EXTERIOR(rn_interior)
+ ->source) {
+ RFAPI_MONITOR_EXTERIOR(rn_interior)
+ ->source = skiplist_new(
+ 0, NULL, prefix_free_lists);
+ agg_lock_node(rn_interior);
+ }
+ skiplist_insert(
+ RFAPI_MONITOR_EXTERIOR(rn_interior)
+ ->source,
+ bpi_exterior, pfx_mon);
+ agg_lock_node(rn_interior);
+
+ /*
+ * Delete constructed exterior routes based on
+ * parent routes.
+ */
+ for (bpi = par->info; bpi; bpi = bpi->next) {
+
+ if (bpi->extra) {
+ prd = &bpi->extra->vnc.import
+ .rd;
+ label = decode_label(
+ &bpi->extra->label[0]);
+ } else
+ prd = NULL;
+
+ rfapiBgpInfoFilteredImportVPN(
+ it, FIF_ACTION_KILL, bpi->peer,
+ NULL, /* rfd */
+ pfx_exterior, NULL, afi, prd,
+ bpi->attr,
+ ZEBRA_ROUTE_BGP_DIRECT_EXT,
+ BGP_ROUTE_REDISTRIBUTE, &label);
+ }
+
+
+ /*
+ * Add constructed exterior routes based on
+ * the new interior route at longer prefix.
+ */
+ if (bpi_interior->extra) {
+ prd = &bpi_interior->extra->vnc.import
+ .rd;
+ label = decode_label(
+ &bpi_interior->extra->label[0]);
+ } else
+ prd = NULL;
+
+ /* use local_pref from unicast route */
+ memset(&new_attr, 0, sizeof(struct attr));
+ new_attr = *bpi_interior->attr;
+ if (bpi_exterior
+ && (bpi_exterior->attr->flag
+ & ATTR_FLAG_BIT(BGP_ATTR_LOCAL_PREF))) {
+ new_attr.local_pref =
+ bpi_exterior->attr->local_pref;
+ new_attr.flag |= ATTR_FLAG_BIT(
+ BGP_ATTR_LOCAL_PREF);
+ }
+
+ rfapiBgpInfoFilteredImportVPN(
+ it, FIF_ACTION_UPDATE,
+ bpi_interior->peer, NULL, /* rfd */
+ pfx_exterior, NULL, afi, prd, &new_attr,
+ ZEBRA_ROUTE_BGP_DIRECT_EXT,
+ BGP_ROUTE_REDISTRIBUTE, &label);
+ }
+ }
+
+ /*
+ * The only monitors at rn_interior are the ones we added just
+ * above, so we can use the rn_interior list to identify which
+ * monitors to delete from the parent.
+ */
+ cursor = NULL;
+ for (rc = skiplist_next(
+ RFAPI_MONITOR_EXTERIOR(rn_interior)->source,
+ (void **)&bpi_exterior, NULL, &cursor);
+ !rc; rc = skiplist_next(
+ RFAPI_MONITOR_EXTERIOR(rn_interior)->source,
+ (void **)&bpi_exterior, NULL, &cursor)) {
+
+
+ skiplist_delete(RFAPI_MONITOR_EXTERIOR(par)->source,
+ bpi_exterior, NULL);
+ agg_unlock_node(par); /* sl entry */
+ }
+ if (skiplist_empty(RFAPI_MONITOR_EXTERIOR(par)->source)) {
+ skiplist_free(RFAPI_MONITOR_EXTERIOR(par)->source);
+ RFAPI_MONITOR_EXTERIOR(par)->source = NULL;
+ agg_unlock_node(par); /* sl itself */
+ }
+ }
+
+ vnc_zlog_debug_verbose("%s: checking orphans", __func__);
+
+ /*
+ * See if any orphans can be pulled down to the current node
+ */
+ cursor = NULL;
+ list_adopted = NULL;
+ for (rc = skiplist_next(it->monitor_exterior_orphans,
+ (void **)&bpi_exterior, (void **)&pfx_exterior,
+ &cursor);
+ !rc; rc = skiplist_next(it->monitor_exterior_orphans,
+ (void **)&bpi_exterior,
+ (void **)&pfx_exterior, &cursor)) {
+
+ struct prefix pfx_nexthop;
+ afi_t afi_exterior = family2afi(pfx_exterior->family);
+
+ vnc_zlog_debug_verbose(
+ "%s: checking exterior orphan at prefix %pFX", __func__,
+ pfx_exterior);
+
+ if (afi_exterior != afi) {
+ vnc_zlog_debug_verbose(
+ "%s: exterior orphan afi %d != interior afi %d, skip",
+ __func__, afi_exterior, afi);
+ continue;
+ }
+
+ /* check original nexthop for prefix match */
+ rfapiUnicastNexthop2Prefix(afi, bpi_exterior->attr,
+ &pfx_nexthop);
+
+ if (prefix_match(p, &pfx_nexthop)) {
+
+ struct prefix_rd *prd;
+ struct attr new_attr;
+ uint32_t label = 0;
+
+ /* do pull-down */
+
+ /*
+ * add monitor to longer prefix
+ */
+
+ struct prefix *pfx_mon = prefix_new();
+ prefix_copy(pfx_mon, pfx_exterior);
+ if (!RFAPI_MONITOR_EXTERIOR(rn_interior)->source) {
+ RFAPI_MONITOR_EXTERIOR(rn_interior)->source =
+ skiplist_new(
+ 0, NULL, prefix_free_lists);
+ agg_lock_node(rn_interior); /* sl */
+ }
+ skiplist_insert(
+ RFAPI_MONITOR_EXTERIOR(rn_interior)->source,
+ bpi_exterior, pfx_mon);
+ agg_lock_node(rn_interior); /* sl entry */
+ if (!list_adopted) {
+ list_adopted = list_new();
+ }
+ listnode_add(list_adopted, bpi_exterior);
+
+ /*
+ * Add constructed exterior routes based on the
+ * new interior route at the longer prefix.
+ */
+ if (bpi_interior->extra) {
+ prd = &bpi_interior->extra->vnc.import.rd;
+ label = decode_label(
+ &bpi_interior->extra->label[0]);
+ } else
+ prd = NULL;
+
+ /* use local_pref from unicast route */
+ memset(&new_attr, 0, sizeof(struct attr));
+ new_attr = *bpi_interior->attr;
+ if (bpi_exterior
+ && (bpi_exterior->attr->flag
+ & ATTR_FLAG_BIT(BGP_ATTR_LOCAL_PREF))) {
+ new_attr.local_pref =
+ bpi_exterior->attr->local_pref;
+ new_attr.flag |=
+ ATTR_FLAG_BIT(BGP_ATTR_LOCAL_PREF);
+ }
+
+ rfapiBgpInfoFilteredImportVPN(
+ it, FIF_ACTION_UPDATE, bpi_interior->peer,
+ NULL, /* rfd */
+ pfx_exterior, NULL, afi, prd, &new_attr,
+ ZEBRA_ROUTE_BGP_DIRECT_EXT,
+ BGP_ROUTE_REDISTRIBUTE, &label);
+ }
+ }
+ if (list_adopted) {
+ struct listnode *node;
+ struct agg_node *an_bpi_exterior;
+
+ for (ALL_LIST_ELEMENTS_RO(list_adopted, node,
+ an_bpi_exterior)) {
+ skiplist_delete(it->monitor_exterior_orphans,
+ an_bpi_exterior, NULL);
+ }
+ list_delete(&list_adopted);
+ }
+}
+
+/*
+ * This function should be called after an interior VPN route
+ * has been deleted from an import_table.
+ * bpi_interior must still be valid, but it must already be detached
+ * from its route node and the route node's valid_interior_count
+ * must already be decremented.
+ *
+ * NB should also be called whenever an existing vpn interior route
+ * becomes invalid (e.g., valid_interior_count is decremented)
+ */
+void vnc_import_bgp_exterior_del_route_interior(
+ struct bgp *bgp, struct rfapi_import_table *it,
+ struct agg_node *rn_interior, /* VPN IT node */
+ struct bgp_path_info *bpi_interior) /* VPN IT route */
+{
+ const struct prefix *p = agg_node_get_prefix(rn_interior);
+ afi_t afi = family2afi(p->family);
+ struct agg_node *par;
+ struct bgp_path_info *bpi_exterior;
+ struct prefix *pfx_exterior; /* exterior pfx */
+ void *cursor;
+ int rc;
+
+ if (!VALID_INTERIOR_TYPE(bpi_interior->type)) {
+ vnc_zlog_debug_verbose(
+ "%s: type %d not valid interior type, skipping",
+ __func__, bpi_interior->type);
+ return;
+ }
+
+ if (!bgp->rfapi_cfg->redist[afi][ZEBRA_ROUTE_BGP_DIRECT_EXT]) {
+ vnc_zlog_debug_verbose(
+ "%s: redist of exterior routes no enabled, skipping",
+ __func__);
+ return;
+ }
+
+ if (it == bgp->rfapi->it_ce) {
+ vnc_zlog_debug_verbose("%s: it is it_ce, skipping", __func__);
+ return;
+ }
+
+ /* If no exterior routes depend on this prefix, nothing to do */
+ if (!RFAPI_HAS_MONITOR_EXTERIOR(rn_interior)) {
+ vnc_zlog_debug_verbose("%s: no exterior monitor, skipping",
+ __func__);
+ return;
+ }
+
+ /*debugging */
+ vnc_zlog_debug_verbose("%s: interior prefix=%pRN, bpi type=%d",
+ __func__, rn_interior, bpi_interior->type);
+
+ /*
+ * Remove constructed routes based on the deleted interior route
+ */
+ cursor = NULL;
+ for (rc = skiplist_next(RFAPI_MONITOR_EXTERIOR(rn_interior)->source,
+ (void **)&bpi_exterior, (void **)&pfx_exterior,
+ &cursor);
+ !rc;
+ rc = skiplist_next(RFAPI_MONITOR_EXTERIOR(rn_interior)->source,
+ (void **)&bpi_exterior, (void **)&pfx_exterior,
+ &cursor)) {
+
+ struct prefix_rd *prd;
+ uint32_t label = 0;
+
+ if (bpi_interior->extra) {
+ prd = &bpi_interior->extra->vnc.import.rd;
+ label = decode_label(&bpi_interior->extra->label[0]);
+ } else
+ prd = NULL;
+
+ rfapiBgpInfoFilteredImportVPN(
+ it, FIF_ACTION_KILL, bpi_interior->peer, NULL, /* rfd */
+ pfx_exterior, NULL, afi, prd, bpi_interior->attr,
+ ZEBRA_ROUTE_BGP_DIRECT_EXT, BGP_ROUTE_REDISTRIBUTE,
+ &label);
+ }
+
+ /*
+ * If there are no remaining valid interior routes at this prefix,
+ * we need to look up the tree for a possible node to move monitors to
+ */
+ if (RFAPI_MONITOR_EXTERIOR(rn_interior)->valid_interior_count) {
+ vnc_zlog_debug_verbose(
+ "%s: interior routes still present, skipping",
+ __func__);
+ return;
+ }
+
+ /*
+ * Find nearest parent with at least one valid interior route
+ * If none is found, par will end up NULL, and we will move
+ * the monitors to the orphan list for this import table
+ */
+ for (par = agg_node_parent(rn_interior); par;
+ par = agg_node_parent(par)) {
+ if (RFAPI_MONITOR_EXTERIOR(par)->valid_interior_count)
+ break;
+ }
+
+ vnc_zlog_debug_verbose("%s: par=%p, ext src: %p", __func__, par,
+ RFAPI_MONITOR_EXTERIOR(rn_interior)->source);
+
+ /* move all monitors */
+ /*
+ * We will use and delete every element of the source skiplist
+ */
+ while (!skiplist_first(RFAPI_MONITOR_EXTERIOR(rn_interior)->source,
+ (void **)&bpi_exterior,
+ (void **)&pfx_exterior)) {
+
+ struct prefix *pfx_mon = prefix_new();
+
+ prefix_copy(pfx_mon, pfx_exterior);
+
+ if (par) {
+
+ struct bgp_path_info *bpi;
+
+ /*
+ * Add monitor to parent node
+ */
+ if (!RFAPI_MONITOR_EXTERIOR(par)->source) {
+ RFAPI_MONITOR_EXTERIOR(par)->source =
+ skiplist_new(
+ 0, NULL, prefix_free_lists);
+ agg_lock_node(par); /* sl */
+ }
+ skiplist_insert(RFAPI_MONITOR_EXTERIOR(par)->source,
+ bpi_exterior, pfx_mon);
+ agg_lock_node(par); /* sl entry */
+
+ /* Add constructed exterior routes based on parent */
+ for (bpi = par->info; bpi; bpi = bpi->next) {
+
+ struct prefix_rd *prd;
+ struct attr new_attr;
+ uint32_t label = 0;
+
+ if (bpi->type == ZEBRA_ROUTE_BGP_DIRECT_EXT)
+ continue;
+
+ if (bpi->extra) {
+ prd = &bpi->extra->vnc.import.rd;
+ label = decode_label(
+ &bpi->extra->label[0]);
+ } else
+ prd = NULL;
+
+ /* use local_pref from unicast route */
+ memset(&new_attr, 0, sizeof(new_attr));
+ new_attr = *bpi->attr;
+ if (bpi_exterior
+ && (bpi_exterior->attr->flag
+ & ATTR_FLAG_BIT(BGP_ATTR_LOCAL_PREF))) {
+ new_attr.local_pref =
+ bpi_exterior->attr->local_pref;
+ new_attr.flag |= ATTR_FLAG_BIT(
+ BGP_ATTR_LOCAL_PREF);
+ }
+
+ rfapiBgpInfoFilteredImportVPN(
+ it, FIF_ACTION_UPDATE, bpi->peer,
+ NULL, /* rfd */
+ pfx_exterior, NULL, afi, prd, &new_attr,
+ ZEBRA_ROUTE_BGP_DIRECT_EXT,
+ BGP_ROUTE_REDISTRIBUTE, &label);
+ }
+
+ } else {
+
+ /*
+ * No interior route for exterior's nexthop. Save
+ * monitor
+ * in orphan list to await future route.
+ */
+ skiplist_insert(it->monitor_exterior_orphans,
+ bpi_exterior, pfx_mon);
+ }
+
+ skiplist_delete_first(
+ RFAPI_MONITOR_EXTERIOR(rn_interior)->source);
+ agg_unlock_node(rn_interior); /* sl entry */
+ }
+ if (skiplist_empty(RFAPI_MONITOR_EXTERIOR(rn_interior)->source)) {
+ skiplist_free(RFAPI_MONITOR_EXTERIOR(rn_interior)->source);
+ RFAPI_MONITOR_EXTERIOR(rn_interior)->source = NULL;
+ agg_unlock_node(rn_interior); /* sl itself */
+ }
+}
+
+/***********************************************************************
+ * Generic add/delete unicast routes
+ ***********************************************************************/
+
+void vnc_import_bgp_add_route(struct bgp *bgp, const struct prefix *prefix,
+ struct bgp_path_info *info)
+{
+ afi_t afi = family2afi(prefix->family);
+
+ if (VNC_DEBUG(VERBOSE)) {
+ struct prefix pfx_nexthop;
+
+ rfapiUnicastNexthop2Prefix(afi, info->attr, &pfx_nexthop);
+ vnc_zlog_debug_verbose("%s: pfx %pFX, nh %pFX", __func__,
+ prefix, &pfx_nexthop);
+ }
+#if DEBUG_RHN_LIST
+ print_rhn_list(__func__, "ENTER ");
+#endif
+ VNC_RHNCK(enter);
+
+ if (!afi) {
+ flog_err(EC_LIB_DEVELOPMENT, "%s: can't get afi of prefix",
+ __func__);
+ return;
+ }
+
+ if (!bgp->rfapi_cfg) {
+ vnc_zlog_debug_verbose("%s: bgp->rfapi_cfg is NULL, skipping",
+ __func__);
+ return;
+ }
+
+ /* check vnc redist flag for bgp direct routes */
+ if (!bgp->rfapi_cfg->redist[afi][ZEBRA_ROUTE_BGP_DIRECT]) {
+ vnc_zlog_debug_verbose(
+ "%s: bgp->rfapi_cfg->redist[afi=%d][type=%d=ZEBRA_ROUTE_BGP_DIRECT] is 0, skipping",
+ __func__, afi, ZEBRA_ROUTE_BGP_DIRECT);
+ return;
+ }
+
+ switch (bgp->rfapi_cfg->redist_mode) {
+ case VNC_REDIST_MODE_PLAIN:
+ vnc_import_bgp_add_route_mode_plain(bgp, prefix, info);
+ break;
+
+ case VNC_REDIST_MODE_RFG:
+ if (bgp->rfapi_cfg->rfg_redist)
+ vnc_import_bgp_add_route_mode_nvegroup(
+ bgp, prefix, info, bgp->rfapi_cfg->rfg_redist);
+ else
+ vnc_zlog_debug_verbose("%s: mode RFG but no redist RFG",
+ __func__);
+ break;
+
+ case VNC_REDIST_MODE_RESOLVE_NVE:
+ vnc_import_bgp_add_route_mode_resolve_nve(bgp, prefix, info);
+ break;
+ }
+#if DEBUG_RHN_LIST
+ print_rhn_list(__func__, "LEAVE ");
+#endif
+ VNC_RHNCK(leave);
+}
+
+/*
+ * "Withdrawing a Route" import process
+ */
+void vnc_import_bgp_del_route(struct bgp *bgp, const struct prefix *prefix,
+ struct bgp_path_info *info) /* unicast info */
+{
+ afi_t afi = family2afi(prefix->family);
+
+ assert(afi);
+
+ {
+ struct prefix pfx_nexthop;
+
+ rfapiUnicastNexthop2Prefix(afi, info->attr, &pfx_nexthop);
+ vnc_zlog_debug_verbose("%s: pfx %pFX, nh %pFX", __func__,
+ prefix, &pfx_nexthop);
+ }
+#if DEBUG_RHN_LIST
+ print_rhn_list(__func__, "ENTER ");
+#endif
+ VNC_RHNCK(enter);
+
+ if (!bgp->rfapi_cfg) {
+ vnc_zlog_debug_verbose("%s: bgp->rfapi_cfg is NULL, skipping",
+ __func__);
+ return;
+ }
+
+ /* check bgp redist flag for vnc direct ("vpn") routes */
+ if (!bgp->rfapi_cfg->redist[afi][ZEBRA_ROUTE_BGP_DIRECT]) {
+ vnc_zlog_debug_verbose(
+ "%s: bgp redistribution of afi=%d VNC direct routes is off",
+ __func__, afi);
+ return;
+ }
+
+ switch (bgp->rfapi_cfg->redist_mode) {
+ case VNC_REDIST_MODE_PLAIN:
+ vnc_import_bgp_del_route_mode_plain(bgp, prefix, info);
+ break;
+
+ case VNC_REDIST_MODE_RFG:
+ if (bgp->rfapi_cfg->rfg_redist)
+ vnc_import_bgp_del_route_mode_nvegroup(bgp, prefix,
+ info);
+ else
+ vnc_zlog_debug_verbose("%s: mode RFG but no redist RFG",
+ __func__);
+ break;
+
+ case VNC_REDIST_MODE_RESOLVE_NVE:
+ vnc_import_bgp_del_route_mode_resolve_nve(bgp, afi, prefix,
+ info);
+ break;
+ }
+#if DEBUG_RHN_LIST
+ print_rhn_list(__func__, "LEAVE ");
+#endif
+ VNC_RHNCK(leave);
+}
+
+
+/***********************************************************************
+ * Enable/Disable
+ ***********************************************************************/
+
+void vnc_import_bgp_redist_enable(struct bgp *bgp, afi_t afi)
+{
+ /* iterate over bgp unicast v4 and v6 routes, call
+ * vnc_import_bgp_add_route */
+
+ struct bgp_dest *dest;
+
+ vnc_zlog_debug_verbose("%s: entry, afi=%d", __func__, afi);
+
+ if (bgp->rfapi_cfg->redist[afi][ZEBRA_ROUTE_BGP_DIRECT]) {
+ vnc_zlog_debug_verbose(
+ "%s: already enabled for afi %d, skipping", __func__,
+ afi);
+ return;
+ }
+ bgp->rfapi_cfg->redist[afi][ZEBRA_ROUTE_BGP_DIRECT] = 1;
+
+ for (dest = bgp_table_top(bgp->rib[afi][SAFI_UNICAST]); dest;
+ dest = bgp_route_next(dest)) {
+
+ struct bgp_path_info *bpi;
+
+ for (bpi = bgp_dest_get_bgp_path_info(dest); bpi;
+ bpi = bpi->next) {
+
+ if (CHECK_FLAG(bpi->flags, BGP_PATH_REMOVED))
+ continue;
+
+ vnc_import_bgp_add_route(bgp, bgp_dest_get_prefix(dest),
+ bpi);
+ }
+ }
+ vnc_zlog_debug_verbose(
+ "%s: set redist[afi=%d][type=%d=ZEBRA_ROUTE_BGP_DIRECT] return",
+ __func__, afi, ZEBRA_ROUTE_BGP_DIRECT);
+}
+
+void vnc_import_bgp_exterior_redist_enable(struct bgp *bgp, afi_t afi)
+{
+ struct bgp *bgp_exterior;
+ struct bgp_dest *dest;
+
+ bgp_exterior = bgp->rfapi_cfg->redist_bgp_exterior_view;
+
+ if (bgp->rfapi_cfg->redist[afi][ZEBRA_ROUTE_BGP_DIRECT_EXT]) {
+ vnc_zlog_debug_verbose(
+ "%s: already enabled for afi %d, skipping", __func__,
+ afi);
+ return;
+ }
+ bgp->rfapi_cfg->redist[afi][ZEBRA_ROUTE_BGP_DIRECT_EXT] = 1;
+
+ if (!bgp_exterior) {
+ vnc_zlog_debug_verbose(
+ "%s: no exterior view set yet, no routes to import yet",
+ __func__);
+ return;
+ }
+
+ for (dest = bgp_table_top(bgp_exterior->rib[afi][SAFI_UNICAST]); dest;
+ dest = bgp_route_next(dest)) {
+
+ struct bgp_path_info *bpi;
+
+ for (bpi = bgp_dest_get_bgp_path_info(dest); bpi;
+ bpi = bpi->next) {
+
+ if (CHECK_FLAG(bpi->flags, BGP_PATH_REMOVED))
+ continue;
+
+ vnc_import_bgp_exterior_add_route(
+ bgp_exterior, bgp_dest_get_prefix(dest), bpi);
+ }
+ }
+ vnc_zlog_debug_verbose(
+ "%s: set redist[afi=%d][type=%d=ZEBRA_ROUTE_BGP_DIRECT] return",
+ __func__, afi, ZEBRA_ROUTE_BGP_DIRECT);
+}
+
+/*
+ * This function is for populating a newly-created Import Table
+ */
+void vnc_import_bgp_exterior_redist_enable_it(
+ struct bgp *bgp, afi_t afi, struct rfapi_import_table *it_only)
+{
+ struct bgp *bgp_exterior;
+ struct bgp_dest *dest;
+
+ vnc_zlog_debug_verbose("%s: entry", __func__);
+
+ bgp_exterior = bgp->rfapi_cfg->redist_bgp_exterior_view;
+
+ if (!bgp->rfapi_cfg->redist[afi][ZEBRA_ROUTE_BGP_DIRECT_EXT]) {
+ vnc_zlog_debug_verbose("%s: not enabled for afi %d, skipping",
+ __func__, afi);
+ return;
+ }
+
+ if (!bgp_exterior) {
+ vnc_zlog_debug_verbose(
+ "%s: no exterior view set yet, no routes to import yet",
+ __func__);
+ return;
+ }
+
+ for (dest = bgp_table_top(bgp_exterior->rib[afi][SAFI_UNICAST]); dest;
+ dest = bgp_route_next(dest)) {
+
+ struct bgp_path_info *bpi;
+
+ for (bpi = bgp_dest_get_bgp_path_info(dest); bpi;
+ bpi = bpi->next) {
+
+ if (CHECK_FLAG(bpi->flags, BGP_PATH_REMOVED))
+ continue;
+
+ vnc_import_bgp_exterior_add_route_it(
+ bgp_exterior, bgp_dest_get_prefix(dest), bpi,
+ it_only);
+ }
+ }
+}
+
+
+void vnc_import_bgp_redist_disable(struct bgp *bgp, afi_t afi)
+{
+ /*
+ * iterate over vpn routes, find routes of type ZEBRA_ROUTE_BGP_DIRECT,
+ * delete (call timer expire immediately)
+ */
+ struct bgp_dest *dest1;
+ struct bgp_dest *dest2;
+
+ vnc_zlog_debug_verbose("%s: entry", __func__);
+
+ if (!bgp->rfapi_cfg->redist[afi][ZEBRA_ROUTE_BGP_DIRECT]) {
+ vnc_zlog_debug_verbose(
+ "%s: already disabled for afi %d, skipping", __func__,
+ afi);
+ return;
+ }
+
+ /*
+ * Two-level table for SAFI_MPLS_VPN
+ * Be careful when changing the things we iterate over
+ */
+ for (dest1 = bgp_table_top(bgp->rib[afi][SAFI_MPLS_VPN]); dest1;
+ dest1 = bgp_route_next(dest1)) {
+ const struct prefix *dest1_p;
+
+ if (!bgp_dest_has_bgp_path_info_data(dest1))
+ continue;
+
+ dest1_p = bgp_dest_get_prefix(dest1);
+ for (dest2 = bgp_table_top(bgp_dest_get_bgp_table_info(dest1));
+ dest2; dest2 = bgp_route_next(dest2)) {
+ const struct prefix *dest2_p =
+ bgp_dest_get_prefix(dest2);
+ struct bgp_path_info *bpi;
+ struct bgp_path_info *nextbpi;
+
+ for (bpi = bgp_dest_get_bgp_path_info(dest2); bpi;
+ bpi = nextbpi) {
+
+ nextbpi = bpi->next;
+
+ if (bpi->type != ZEBRA_ROUTE_BGP_DIRECT)
+ continue;
+
+ struct rfapi_descriptor *rfd;
+ vncHDBgpDirect.peer = bpi->peer;
+
+ assert(bpi->extra);
+
+ rfd = bpi->extra->vnc.export.rfapi_handle;
+
+ vnc_zlog_debug_verbose(
+ "%s: deleting bpi=%p, bpi->peer=%p, bpi->type=%d, bpi->sub_type=%d, bpi->extra->vnc.export.rfapi_handle=%p [passing rfd=%p]",
+ __func__, bpi, bpi->peer, bpi->type,
+ bpi->sub_type,
+ (bpi->extra ? bpi->extra->vnc.export
+ .rfapi_handle
+ : NULL),
+ rfd);
+
+ del_vnc_route(rfd, bpi->peer, bgp,
+ SAFI_MPLS_VPN, dest2_p,
+ (struct prefix_rd *)dest1_p,
+ bpi->type, bpi->sub_type, NULL,
+ 1); /* kill */
+
+ vncHDBgpDirect.peer = NULL;
+ }
+ }
+ }
+ /* Clear RHN list */
+ if (bgp->rfapi->resolve_nve_nexthop) {
+ struct prefix_bag *pb;
+ struct bgp_path_info *info;
+ while (!skiplist_first(bgp->rfapi->resolve_nve_nexthop, NULL,
+ (void *)&pb)) {
+ info = pb->ubpi;
+ skiplist_delete_first(bgp->rfapi->resolve_nve_nexthop);
+ bgp_path_info_unlock(info);
+ }
+ }
+
+ bgp->rfapi_cfg->redist[afi][ZEBRA_ROUTE_BGP_DIRECT] = 0;
+ vnc_zlog_debug_verbose("%s: return", __func__);
+}
+
+
+void vnc_import_bgp_exterior_redist_disable(struct bgp *bgp, afi_t afi)
+{
+ struct rfapi_cfg *hc = bgp->rfapi_cfg;
+ struct bgp *bgp_exterior = hc->redist_bgp_exterior_view;
+
+ vnc_zlog_debug_verbose("%s: entry", __func__);
+
+ if (!hc->redist[afi][ZEBRA_ROUTE_BGP_DIRECT_EXT]) {
+ vnc_zlog_debug_verbose(
+ "%s: already disabled for afi %d, skipping", __func__,
+ afi);
+ return;
+ }
+
+ if (!bgp_exterior) {
+ vnc_zlog_debug_verbose(
+ "%s: bgp exterior view not defined, skipping",
+ __func__);
+ return;
+ }
+
+
+ {
+ struct bgp_dest *dest;
+ for (dest = bgp_table_top(bgp_exterior->rib[afi][SAFI_UNICAST]);
+ dest; dest = bgp_route_next(dest)) {
+
+ struct bgp_path_info *bpi;
+
+ for (bpi = bgp_dest_get_bgp_path_info(dest); bpi;
+ bpi = bpi->next) {
+
+ if (CHECK_FLAG(bpi->flags, BGP_PATH_REMOVED))
+ continue;
+
+ vnc_import_bgp_exterior_del_route(
+ bgp_exterior, bgp_dest_get_prefix(dest),
+ bpi);
+ }
+ }
+#if DEBUG_RHN_LIST
+ print_rhn_list(__func__, NULL);
+#endif
+ }
+
+ bgp->rfapi_cfg->redist[afi][ZEBRA_ROUTE_BGP_DIRECT_EXT] = 0;
+ vnc_zlog_debug_verbose("%s: return", __func__);
+}
diff --git a/bgpd/rfapi/vnc_import_bgp.h b/bgpd/rfapi/vnc_import_bgp.h
new file mode 100644
index 0000000..ab2ec1a
--- /dev/null
+++ b/bgpd/rfapi/vnc_import_bgp.h
@@ -0,0 +1,75 @@
+/*
+ *
+ * Copyright 2009-2016, LabN Consulting, L.L.C.
+ *
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program; see the file COPYING; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
+ */
+
+#ifndef _QUAGGA_RFAPI_VNC_IMPORT_BGP_H_
+#define _QUAGGA_RFAPI_VNC_IMPORT_BGP_H_
+
+#include "lib/zebra.h"
+#include "lib/prefix.h"
+
+#include "bgpd/bgpd.h"
+#include "bgpd/bgp_route.h"
+
+#define VALID_INTERIOR_TYPE(type) \
+ (((type) == ZEBRA_ROUTE_BGP) || ((type) == ZEBRA_ROUTE_BGP_DIRECT))
+
+extern uint32_t calc_local_pref(struct attr *attr, struct peer *peer);
+
+extern int vnc_prefix_cmp(const void *pfx1, const void *pfx2);
+
+extern void vnc_import_bgp_add_route(struct bgp *bgp,
+ const struct prefix *prefix,
+ struct bgp_path_info *info);
+
+extern void vnc_import_bgp_del_route(struct bgp *bgp,
+ const struct prefix *prefix,
+ struct bgp_path_info *info);
+
+extern void vnc_import_bgp_redist_enable(struct bgp *bgp, afi_t afi);
+
+extern void vnc_import_bgp_redist_disable(struct bgp *bgp, afi_t afi);
+
+extern void vnc_import_bgp_exterior_redist_enable(struct bgp *bgp, afi_t afi);
+
+extern void vnc_import_bgp_exterior_redist_disable(struct bgp *bgp, afi_t afi);
+
+
+extern void vnc_import_bgp_exterior_add_route(
+ struct bgp *bgp, /* exterior instance, we hope */
+ const struct prefix *prefix, /* unicast prefix */
+ struct bgp_path_info *info); /* unicast info */
+
+extern void vnc_import_bgp_exterior_del_route(
+ struct bgp *bgp, const struct prefix *prefix, /* unicast prefix */
+ struct bgp_path_info *info); /* unicast info */
+
+extern void vnc_import_bgp_add_vnc_host_route_mode_resolve_nve(
+ struct bgp *bgp, struct prefix_rd *prd, /* RD */
+ struct bgp_table *table_rd, /* per-rd VPN route table */
+ const struct prefix *prefix, /* VPN prefix */
+ struct bgp_path_info *bpi); /* new VPN host route */
+
+extern void vnc_import_bgp_del_vnc_host_route_mode_resolve_nve(
+ struct bgp *bgp, struct prefix_rd *prd, /* RD */
+ struct bgp_table *table_rd, /* per-rd VPN route table */
+ const struct prefix *prefix, /* VPN prefix */
+ struct bgp_path_info *bpi); /* old VPN host route */
+
+#endif /* _QUAGGA_RFAPI_VNC_IMPORT_BGP_H_ */
diff --git a/bgpd/rfapi/vnc_import_bgp_p.h b/bgpd/rfapi/vnc_import_bgp_p.h
new file mode 100644
index 0000000..c6627df
--- /dev/null
+++ b/bgpd/rfapi/vnc_import_bgp_p.h
@@ -0,0 +1,44 @@
+/*
+ *
+ * Copyright 2009-2016, LabN Consulting, L.L.C.
+ *
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program; see the file COPYING; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
+ */
+
+#ifndef _QUAGGA_RFAPI_VNC_IMPORT_BGP_P_H_
+#define _QUAGGA_RFAPI_VNC_IMPORT_BGP_P_H_
+
+#include "lib/zebra.h"
+#include "lib/prefix.h"
+
+#include "bgpd/bgpd.h"
+#include "bgpd/bgp_route.h"
+
+extern void vnc_import_bgp_exterior_add_route_interior(
+ struct bgp *bgp, struct rfapi_import_table *it,
+ struct agg_node *rn_interior, /* VPN IT node */
+ struct bgp_path_info *bpi_interior); /* VPN IT route */
+
+extern void vnc_import_bgp_exterior_del_route_interior(
+ struct bgp *bgp, struct rfapi_import_table *it,
+ struct agg_node *rn_interior, /* VPN IT node */
+ struct bgp_path_info *bpi_interior); /* VPN IT route */
+
+extern void
+vnc_import_bgp_exterior_redist_enable_it(struct bgp *bgp, afi_t afi,
+ struct rfapi_import_table *it_only);
+
+#endif /* _QUAGGA_RFAPI_VNC_IMPORT_BGP_P_H_ */
diff --git a/bgpd/rfapi/vnc_zebra.c b/bgpd/rfapi/vnc_zebra.c
new file mode 100644
index 0000000..fe81898
--- /dev/null
+++ b/bgpd/rfapi/vnc_zebra.c
@@ -0,0 +1,921 @@
+/*
+ *
+ * Copyright 2009-2016, LabN Consulting, L.L.C.
+ *
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program; see the file COPYING; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
+ */
+
+/*
+ * File: vnc_zebra.c
+ * Purpose: Handle exchange of routes between VNC and Zebra
+ */
+
+#include "lib/zebra.h"
+#include "lib/prefix.h"
+#include "lib/agg_table.h"
+#include "lib/log.h"
+#include "lib/command.h"
+#include "lib/zclient.h"
+#include "lib/stream.h"
+#include "lib/ringbuf.h"
+#include "lib/memory.h"
+#include "lib/lib_errors.h"
+
+#include "bgpd/bgpd.h"
+#include "bgpd/bgp_ecommunity.h"
+#include "bgpd/bgp_route.h"
+#include "bgpd/bgp_debug.h"
+#include "bgpd/bgp_advertise.h"
+
+#include "bgpd/rfapi/bgp_rfapi_cfg.h"
+#include "bgpd/rfapi/rfapi.h"
+#include "bgpd/rfapi/rfapi_import.h"
+#include "bgpd/rfapi/rfapi_private.h"
+#include "bgpd/rfapi/vnc_zebra.h"
+#include "bgpd/rfapi/rfapi_vty.h"
+#include "bgpd/rfapi/rfapi_backend.h"
+#include "bgpd/rfapi/vnc_debug.h"
+
+static struct rfapi_descriptor vncHD1VR; /* Single-VR export dummy nve descr */
+static struct zclient *zclient_vnc = NULL;
+
+/***********************************************************************
+ * REDISTRIBUTE: Zebra sends updates/withdraws to BGPD
+ ***********************************************************************/
+
+/*
+ * Routes coming from zebra get added to VNC here
+ */
+static void vnc_redistribute_add(struct prefix *p, uint32_t metric,
+ uint8_t type)
+{
+ struct bgp *bgp = bgp_get_default();
+ struct prefix_rd prd;
+ struct rfapi_ip_addr vnaddr;
+ afi_t afi;
+ uint32_t local_pref =
+ rfp_cost_to_localpref(metric > 255 ? 255 : metric);
+
+ if (!bgp)
+ return;
+
+ if (!bgp->rfapi_cfg) {
+ vnc_zlog_debug_verbose("%s: bgp->rfapi_cfg is NULL, skipping",
+ __func__);
+ return;
+ }
+
+ afi = family2afi(p->family);
+ if (!afi) {
+ vnc_zlog_debug_verbose("%s: unknown prefix address family %d",
+ __func__, p->family);
+ return;
+ }
+
+ if (!bgp->rfapi_cfg->redist[afi][type]) {
+ vnc_zlog_debug_verbose(
+ "%s: bgp->rfapi_cfg->redist[afi=%d][type=%d] is 0, skipping",
+ __func__, afi, type);
+ return;
+ }
+ if (!bgp->rfapi_cfg->rfg_redist) {
+ vnc_zlog_debug_verbose("%s: no redist nve group, skipping",
+ __func__);
+ return;
+ }
+
+ /*
+ * Assume nve group's configured VN address prefix is a host
+ * route which also happens to give the NVE VN address to use
+ * for redistributing into VNC.
+ */
+ vnaddr.addr_family = bgp->rfapi_cfg->rfg_redist->vn_prefix.family;
+ switch (bgp->rfapi_cfg->rfg_redist->vn_prefix.family) {
+ case AF_INET:
+ if (bgp->rfapi_cfg->rfg_redist->vn_prefix.prefixlen
+ != IPV4_MAX_BITLEN) {
+ vnc_zlog_debug_verbose(
+ "%s: redist nve group VN prefix len (%d) != 32, skipping",
+ __func__,
+ bgp->rfapi_cfg->rfg_redist->vn_prefix
+ .prefixlen);
+ return;
+ }
+ vnaddr.addr.v4 =
+ bgp->rfapi_cfg->rfg_redist->vn_prefix.u.prefix4;
+ break;
+ case AF_INET6:
+ if (bgp->rfapi_cfg->rfg_redist->vn_prefix.prefixlen
+ != IPV6_MAX_BITLEN) {
+ vnc_zlog_debug_verbose(
+ "%s: redist nve group VN prefix len (%d) != 128, skipping",
+ __func__,
+ bgp->rfapi_cfg->rfg_redist->vn_prefix
+ .prefixlen);
+ return;
+ }
+ vnaddr.addr.v6 =
+ bgp->rfapi_cfg->rfg_redist->vn_prefix.u.prefix6;
+ break;
+ default:
+ vnc_zlog_debug_verbose(
+ "%s: no redist nve group VN host prefix configured, skipping",
+ __func__);
+ return;
+ }
+
+ /*
+ * Assume nve group's configured UN address prefix is a host
+ * route which also happens to give the NVE UN address to use
+ * for redistributing into VNC.
+ */
+
+ /*
+ * Set UN address in dummy nve descriptor so add_vnc_route
+ * can use it in VNC tunnel SubTLV
+ */
+ {
+ struct rfapi_ip_prefix pfx_un;
+
+ rfapiQprefix2Rprefix(&bgp->rfapi_cfg->rfg_redist->un_prefix,
+ &pfx_un);
+
+ switch (pfx_un.prefix.addr_family) {
+ case AF_INET:
+ if (pfx_un.length != IPV4_MAX_BITLEN) {
+ vnc_zlog_debug_verbose(
+ "%s: redist nve group UN prefix len (%d) != 32, skipping",
+ __func__, pfx_un.length);
+ return;
+ }
+ break;
+ case AF_INET6:
+ if (pfx_un.length != IPV6_MAX_BITLEN) {
+ vnc_zlog_debug_verbose(
+ "%s: redist nve group UN prefix len (%d) != 128, skipping",
+ __func__, pfx_un.length);
+ return;
+ }
+ break;
+ default:
+ vnc_zlog_debug_verbose(
+ "%s: no redist nve group UN host prefix configured, skipping",
+ __func__);
+ return;
+ }
+
+ vncHD1VR.un_addr = pfx_un.prefix;
+
+ if (!vncHD1VR.peer) {
+ /*
+ * Same setup as in rfapi_open()
+ */
+ vncHD1VR.peer = peer_new(bgp);
+ vncHD1VR.peer->status =
+ Established; /* keep bgp core happy */
+ bgp_sync_delete(vncHD1VR.peer); /* don't need these */
+
+ /*
+ * since this peer is not on the I/O thread, this lock
+ * is not strictly necessary, but serves as a reminder
+ * to those who may meddle...
+ */
+ frr_with_mutex (&vncHD1VR.peer->io_mtx) {
+ // we don't need any I/O related facilities
+ if (vncHD1VR.peer->ibuf)
+ stream_fifo_free(vncHD1VR.peer->ibuf);
+ if (vncHD1VR.peer->obuf)
+ stream_fifo_free(vncHD1VR.peer->obuf);
+
+ if (vncHD1VR.peer->ibuf_work)
+ ringbuf_del(vncHD1VR.peer->ibuf_work);
+ if (vncHD1VR.peer->obuf_work)
+ stream_free(vncHD1VR.peer->obuf_work);
+
+ vncHD1VR.peer->ibuf = NULL;
+ vncHD1VR.peer->obuf = NULL;
+ vncHD1VR.peer->obuf_work = NULL;
+ vncHD1VR.peer->ibuf_work = NULL;
+ }
+
+ /* base code assumes have valid host pointer */
+ vncHD1VR.peer->host =
+ XSTRDUP(MTYPE_BGP_PEER_HOST, ".zebra.");
+
+ /* Mark peer as belonging to HD */
+ SET_FLAG(vncHD1VR.peer->flags, PEER_FLAG_IS_RFAPI_HD);
+ }
+ }
+
+ memset(&prd, 0, sizeof(prd));
+ prd = bgp->rfapi_cfg->rfg_redist->rd;
+ prd.family = AF_UNSPEC;
+ prd.prefixlen = 64;
+
+ add_vnc_route(&vncHD1VR, /* cookie + UN addr */
+ bgp, SAFI_MPLS_VPN, p, &prd, &vnaddr, &local_pref,
+ &(bgp->rfapi_cfg->redist_lifetime),
+ NULL, /* RFP options */
+ NULL, /* struct rfapi_un_option */
+ NULL, /* struct rfapi_vn_option */
+ bgp->rfapi_cfg->rfg_redist->rt_export_list, NULL,
+ NULL, /* label: default */
+ type, BGP_ROUTE_REDISTRIBUTE, 0); /* flags */
+}
+
+/*
+ * Route deletions from zebra propagate to VNC here
+ */
+static void vnc_redistribute_delete(struct prefix *p, uint8_t type)
+{
+ struct bgp *bgp = bgp_get_default();
+ struct prefix_rd prd;
+ afi_t afi;
+
+ if (!bgp)
+ return;
+
+ if (!bgp->rfapi_cfg) {
+ vnc_zlog_debug_verbose("%s: bgp->rfapi_cfg is NULL, skipping",
+ __func__);
+ return;
+ }
+ afi = family2afi(p->family);
+ if (!afi) {
+ vnc_zlog_debug_verbose("%s: unknown prefix address family %d",
+ __func__, p->family);
+ return;
+ }
+ if (!bgp->rfapi_cfg->redist[afi][type]) {
+ vnc_zlog_debug_verbose(
+ "%s: bgp->rfapi_cfg->redist[afi=%d][type=%d] is 0, skipping",
+ __func__, afi, type);
+ return;
+ }
+ if (!bgp->rfapi_cfg->rfg_redist) {
+ vnc_zlog_debug_verbose("%s: no redist nve group, skipping",
+ __func__);
+ return;
+ }
+
+ memset(&prd, 0, sizeof(prd));
+ prd = bgp->rfapi_cfg->rfg_redist->rd;
+ prd.family = AF_UNSPEC;
+ prd.prefixlen = 64;
+
+ del_vnc_route(&vncHD1VR, /* use dummy ptr as cookie */
+ vncHD1VR.peer, bgp, SAFI_MPLS_VPN, p, &prd, type,
+ BGP_ROUTE_REDISTRIBUTE, NULL, 0);
+}
+
+/*
+ * Flush all redistributed routes of type <type>
+ */
+static void vnc_redistribute_withdraw(struct bgp *bgp, afi_t afi, uint8_t type)
+{
+ struct prefix_rd prd;
+ struct bgp_table *table;
+ struct bgp_dest *pdest;
+ struct bgp_dest *dest;
+
+ vnc_zlog_debug_verbose("%s: entry", __func__);
+
+ if (!bgp)
+ return;
+ if (!bgp->rfapi_cfg) {
+ vnc_zlog_debug_verbose("%s: bgp->rfapi_cfg is NULL, skipping",
+ __func__);
+ return;
+ }
+
+ /*
+ * Loop over all the RDs
+ */
+ for (pdest = bgp_table_top(bgp->rib[afi][SAFI_MPLS_VPN]); pdest;
+ pdest = bgp_route_next(pdest)) {
+ const struct prefix *pdest_p = bgp_dest_get_prefix(pdest);
+
+ memset(&prd, 0, sizeof(prd));
+ prd.family = AF_UNSPEC;
+ prd.prefixlen = 64;
+ memcpy(prd.val, pdest_p->u.val, 8);
+
+ /* This is the per-RD table of prefixes */
+ table = bgp_dest_get_bgp_table_info(pdest);
+ if (!table)
+ continue;
+
+ for (dest = bgp_table_top(table); dest;
+ dest = bgp_route_next(dest)) {
+
+ struct bgp_path_info *ri;
+
+ for (ri = bgp_dest_get_bgp_path_info(dest); ri;
+ ri = ri->next) {
+ if (ri->type
+ == type) { /* has matching redist type */
+ break;
+ }
+ }
+ if (ri) {
+ del_vnc_route(
+ &vncHD1VR, /* use dummy ptr as cookie */
+ vncHD1VR.peer, bgp, SAFI_MPLS_VPN,
+ bgp_dest_get_prefix(dest), &prd, type,
+ BGP_ROUTE_REDISTRIBUTE, NULL, 0);
+ }
+ }
+ }
+ vnc_zlog_debug_verbose("%s: return", __func__);
+}
+
+/*
+ * Zebra route add and delete treatment.
+ *
+ * Assumes 1 nexthop
+ */
+static int vnc_zebra_read_route(ZAPI_CALLBACK_ARGS)
+{
+ struct zapi_route api;
+ int add;
+
+ if (zapi_route_decode(zclient->ibuf, &api) < 0)
+ return -1;
+
+ /* we completely ignore srcdest routes for now. */
+ if (CHECK_FLAG(api.message, ZAPI_MESSAGE_SRCPFX))
+ return 0;
+
+ add = (cmd == ZEBRA_REDISTRIBUTE_ROUTE_ADD);
+ if (add)
+ vnc_redistribute_add(&api.prefix, api.metric, api.type);
+ else
+ vnc_redistribute_delete(&api.prefix, api.type);
+
+ if (BGP_DEBUG(zebra, ZEBRA))
+ vnc_zlog_debug_verbose(
+ "%s: Zebra rcvd: route delete %s %pFX metric %u",
+ __func__, zebra_route_string(api.type), &api.prefix,
+ api.metric);
+
+ return 0;
+}
+
+/***********************************************************************
+ * vnc_bgp_zebra_*: VNC sends updates/withdraws to Zebra
+ ***********************************************************************/
+
+/*
+ * low-level message builder
+ */
+static void vnc_zebra_route_msg(const struct prefix *p, unsigned int nhp_count,
+ void *nhp_ary, int add) /* 1 = add, 0 = del */
+{
+ struct zapi_route api;
+ struct zapi_nexthop *api_nh;
+ int i;
+ struct in_addr **nhp_ary4 = nhp_ary;
+ struct in6_addr **nhp_ary6 = nhp_ary;
+
+ if (!nhp_count) {
+ vnc_zlog_debug_verbose("%s: empty nexthop list, skipping",
+ __func__);
+ return;
+ }
+
+ memset(&api, 0, sizeof(api));
+ api.vrf_id = VRF_DEFAULT;
+ api.type = ZEBRA_ROUTE_VNC;
+ api.safi = SAFI_UNICAST;
+ api.prefix = *p;
+
+ /* Nexthops */
+ SET_FLAG(api.message, ZAPI_MESSAGE_NEXTHOP);
+ api.nexthop_num = MIN(nhp_count, multipath_num);
+ for (i = 0; i < api.nexthop_num; i++) {
+
+ api_nh = &api.nexthops[i];
+ api_nh->vrf_id = VRF_DEFAULT;
+ switch (p->family) {
+ case AF_INET:
+ memcpy(&api_nh->gate.ipv4, nhp_ary4[i],
+ sizeof(api_nh->gate.ipv4));
+ api_nh->type = NEXTHOP_TYPE_IPV4;
+ break;
+ case AF_INET6:
+ memcpy(&api_nh->gate.ipv6, nhp_ary6[i],
+ sizeof(api_nh->gate.ipv6));
+ api_nh->type = NEXTHOP_TYPE_IPV6;
+ break;
+ }
+ }
+
+ if (BGP_DEBUG(zebra, ZEBRA))
+ vnc_zlog_debug_verbose(
+ "%s: Zebra send: route %s %pFX, nhp_count=%d", __func__,
+ (add ? "add" : "del"), &api.prefix, nhp_count);
+
+ zclient_route_send((add ? ZEBRA_ROUTE_ADD : ZEBRA_ROUTE_DELETE),
+ zclient_vnc, &api);
+}
+
+
+static void
+nve_list_to_nh_array(uint8_t family, struct list *nve_list,
+ unsigned int *nh_count_ret,
+ void **nh_ary_ret, /* returned address array */
+ void **nhp_ary_ret) /* returned pointer array */
+{
+ int nve_count = listcount(nve_list);
+
+ *nh_count_ret = 0;
+ *nh_ary_ret = NULL;
+ *nhp_ary_ret = NULL;
+
+ if (!nve_count) {
+ vnc_zlog_debug_verbose("%s: empty nve_list, skipping",
+ __func__);
+ return;
+ }
+
+ if (family == AF_INET) {
+ struct listnode *ln;
+ struct in_addr *iap;
+ struct in_addr **v;
+
+ /*
+ * Array of nexthop addresses
+ */
+ *nh_ary_ret =
+ XCALLOC(MTYPE_TMP, nve_count * sizeof(struct in_addr));
+
+ /*
+ * Array of pointers to nexthop addresses
+ */
+ *nhp_ary_ret = XCALLOC(MTYPE_TMP,
+ nve_count * sizeof(struct in_addr *));
+ iap = *nh_ary_ret;
+ v = *nhp_ary_ret;
+
+ for (ln = listhead(nve_list); ln; ln = listnextnode(ln)) {
+
+ struct rfapi_descriptor *irfd;
+ struct prefix nhp;
+
+ irfd = listgetdata(ln);
+
+ if (rfapiRaddr2Qprefix(&irfd->vn_addr, &nhp))
+ continue;
+
+ *iap = nhp.u.prefix4;
+ *v = iap;
+ vnc_zlog_debug_verbose(
+ "%s: ipadr: (%p)<-0x%x, ptr: (%p)<-%p",
+ __func__, iap, nhp.u.prefix4.s_addr, v, iap);
+
+ ++iap;
+ ++v;
+ ++*nh_count_ret;
+ }
+
+ } else if (family == AF_INET6) {
+
+ struct listnode *ln;
+
+ *nh_ary_ret =
+ XCALLOC(MTYPE_TMP, nve_count * sizeof(struct in6_addr));
+
+ *nhp_ary_ret = XCALLOC(MTYPE_TMP,
+ nve_count * sizeof(struct in6_addr *));
+
+ for (ln = listhead(nve_list); ln; ln = listnextnode(ln)) {
+
+ struct rfapi_descriptor *irfd;
+ struct in6_addr *iap = *nh_ary_ret;
+ struct in6_addr **v = *nhp_ary_ret;
+ struct prefix nhp;
+
+ irfd = listgetdata(ln);
+
+ if (rfapiRaddr2Qprefix(&irfd->vn_addr, &nhp))
+ continue;
+
+ *iap = nhp.u.prefix6;
+ *v = iap;
+
+ ++iap;
+ ++v;
+ ++*nh_count_ret;
+ }
+ }
+}
+
+static void import_table_to_nve_list_zebra(struct bgp *bgp,
+ struct rfapi_import_table *it,
+ struct list **nves, uint8_t family)
+{
+ struct listnode *node;
+ struct rfapi_rfg_name *rfgn;
+
+ /*
+ * Loop over the list of NVE-Groups configured for
+ * exporting to direct-bgp.
+ *
+ * Build a list of NVEs that use this import table
+ */
+ *nves = NULL;
+ for (ALL_LIST_ELEMENTS_RO(bgp->rfapi_cfg->rfg_export_zebra_l, node,
+ rfgn)) {
+
+ /*
+ * If this NVE-Group's import table matches the current one
+ */
+ if (rfgn->rfg && rfgn->rfg->nves
+ && rfgn->rfg->rfapi_import_table == it) {
+
+ nve_group_to_nve_list(rfgn->rfg, nves, family);
+ }
+ }
+}
+
+static void vnc_zebra_add_del_prefix(struct bgp *bgp,
+ struct rfapi_import_table *import_table,
+ struct agg_node *rn,
+ int add) /* !0 = add, 0 = del */
+{
+ struct list *nves;
+ const struct prefix *p = agg_node_get_prefix(rn);
+ unsigned int nexthop_count = 0;
+ void *nh_ary = NULL;
+ void *nhp_ary = NULL;
+
+ vnc_zlog_debug_verbose("%s: entry, add=%d", __func__, add);
+
+ if (zclient_vnc->sock < 0)
+ return;
+
+ if (p->family != AF_INET && p->family != AF_INET6) {
+ flog_err(EC_LIB_DEVELOPMENT,
+ "%s: invalid route node addr family", __func__);
+ return;
+ }
+
+ if (!vrf_bitmap_check(
+ zclient_vnc->redist[family2afi(p->family)][ZEBRA_ROUTE_VNC],
+ VRF_DEFAULT))
+ return;
+
+ if (!bgp->rfapi_cfg) {
+ vnc_zlog_debug_verbose("%s: bgp->rfapi_cfg is NULL, skipping",
+ __func__);
+ return;
+ }
+ if (!listcount(bgp->rfapi_cfg->rfg_export_zebra_l)) {
+ vnc_zlog_debug_verbose(
+ "%s: no zebra export nve group, skipping", __func__);
+ return;
+ }
+
+ import_table_to_nve_list_zebra(bgp, import_table, &nves, p->family);
+
+ if (nves) {
+ nve_list_to_nh_array(p->family, nves, &nexthop_count, &nh_ary,
+ &nhp_ary);
+
+ list_delete(&nves);
+
+ if (nexthop_count)
+ vnc_zebra_route_msg(p, nexthop_count, nhp_ary, add);
+ }
+
+ XFREE(MTYPE_TMP, nhp_ary);
+ XFREE(MTYPE_TMP, nh_ary);
+}
+
+void vnc_zebra_add_prefix(struct bgp *bgp,
+ struct rfapi_import_table *import_table,
+ struct agg_node *rn)
+{
+ vnc_zebra_add_del_prefix(bgp, import_table, rn, 1);
+}
+
+void vnc_zebra_del_prefix(struct bgp *bgp,
+ struct rfapi_import_table *import_table,
+ struct agg_node *rn)
+{
+ vnc_zebra_add_del_prefix(bgp, import_table, rn, 0);
+}
+
+
+static void vnc_zebra_add_del_nve(struct bgp *bgp, struct rfapi_descriptor *rfd,
+ int add) /* 0 = del, !0 = add */
+{
+ struct listnode *node;
+ struct rfapi_rfg_name *rfgn;
+ struct rfapi_nve_group_cfg *rfg = rfd->rfg;
+ afi_t afi = family2afi(rfd->vn_addr.addr_family);
+ struct prefix nhp;
+ void *pAddr;
+
+ vnc_zlog_debug_verbose("%s: entry, add=%d", __func__, add);
+
+ if (zclient_vnc->sock < 0)
+ return;
+
+ if (!vrf_bitmap_check(zclient_vnc->redist[afi][ZEBRA_ROUTE_VNC],
+ VRF_DEFAULT))
+ return;
+
+ if (afi != AFI_IP && afi != AFI_IP6) {
+ flog_err(EC_LIB_DEVELOPMENT, "%s: invalid vn addr family",
+ __func__);
+ return;
+ }
+
+ if (!bgp)
+ return;
+ if (!bgp->rfapi_cfg) {
+ vnc_zlog_debug_verbose("%s: bgp->rfapi_cfg is NULL, skipping",
+ __func__);
+ return;
+ }
+
+ if (rfapiRaddr2Qprefix(&rfd->vn_addr, &nhp)) {
+ vnc_zlog_debug_verbose("%s: can't convert vn address, skipping",
+ __func__);
+ return;
+ }
+
+ pAddr = &nhp.u.val;
+
+ /*
+ * Loop over the list of NVE-Groups configured for
+ * exporting to zebra and see if this new NVE's
+ * group is among them.
+ */
+ for (ALL_LIST_ELEMENTS_RO(bgp->rfapi_cfg->rfg_export_zebra_l, node,
+ rfgn)) {
+
+ /*
+ * Yes, this NVE's group is configured for export to zebra
+ */
+ if (rfgn->rfg == rfg) {
+
+ struct agg_table *rt = NULL;
+ struct agg_node *rn;
+ struct rfapi_import_table *import_table;
+ import_table = rfg->rfapi_import_table;
+
+ vnc_zlog_debug_verbose(
+ "%s: this nve's group is in zebra export list",
+ __func__);
+
+ rt = import_table->imported_vpn[afi];
+
+ /*
+ * Walk the NVE-Group's VNC Import table
+ */
+ for (rn = agg_route_top(rt); rn;
+ rn = agg_route_next(rn)) {
+ if (!rn->info)
+ continue;
+
+ vnc_zlog_debug_verbose("%s: sending %s",
+ __func__,
+ (add ? "add" : "del"));
+ vnc_zebra_route_msg(agg_node_get_prefix(rn), 1,
+ &pAddr, add);
+ }
+ }
+ }
+}
+
+void vnc_zebra_add_nve(struct bgp *bgp, struct rfapi_descriptor *rfd)
+{
+ vnc_zebra_add_del_nve(bgp, rfd, 1);
+}
+
+void vnc_zebra_del_nve(struct bgp *bgp, struct rfapi_descriptor *rfd)
+{
+ vnc_zebra_add_del_nve(bgp, rfd, 0);
+}
+
+static void vnc_zebra_add_del_group_afi(struct bgp *bgp,
+ struct rfapi_nve_group_cfg *rfg,
+ afi_t afi, int add)
+{
+ struct agg_table *rt = NULL;
+ struct agg_node *rn;
+ struct rfapi_import_table *import_table;
+ uint8_t family = afi2family(afi);
+
+ struct list *nves = NULL;
+ unsigned int nexthop_count = 0;
+ void *nh_ary = NULL;
+ void *nhp_ary = NULL;
+
+ vnc_zlog_debug_verbose("%s: entry", __func__);
+ import_table = rfg->rfapi_import_table;
+ if (!import_table) {
+ vnc_zlog_debug_verbose(
+ "%s: import table not defined, returning", __func__);
+ return;
+ }
+
+ if (afi == AFI_IP || afi == AFI_IP6) {
+ rt = import_table->imported_vpn[afi];
+ } else {
+ flog_err(EC_LIB_DEVELOPMENT, "%s: bad afi %d", __func__, afi);
+ return;
+ }
+
+ if (!family) {
+ flog_err(EC_LIB_DEVELOPMENT, "%s: computed bad family: %d",
+ __func__, family);
+ return;
+ }
+
+ if (!rfg->nves) {
+ /* avoid segfault below if list doesn't exist */
+ vnc_zlog_debug_verbose("%s: no NVEs in this group", __func__);
+ return;
+ }
+
+ nve_group_to_nve_list(rfg, &nves, family);
+ if (nves) {
+ vnc_zlog_debug_verbose("%s: have nves", __func__);
+ nve_list_to_nh_array(family, nves, &nexthop_count, &nh_ary,
+ &nhp_ary);
+
+ vnc_zlog_debug_verbose("%s: family: %d, nve count: %d",
+ __func__, family, nexthop_count);
+
+ list_delete(&nves);
+
+ if (nexthop_count) {
+ /*
+ * Walk the NVE-Group's VNC Import table
+ */
+ for (rn = agg_route_top(rt); rn;
+ rn = agg_route_next(rn)) {
+ if (rn->info) {
+ vnc_zebra_route_msg(
+ agg_node_get_prefix(rn),
+ nexthop_count, nhp_ary, add);
+ }
+ }
+ }
+ XFREE(MTYPE_TMP, nhp_ary);
+ XFREE(MTYPE_TMP, nh_ary);
+ }
+}
+
+void vnc_zebra_add_group(struct bgp *bgp, struct rfapi_nve_group_cfg *rfg)
+{
+ vnc_zebra_add_del_group_afi(bgp, rfg, AFI_IP, 1);
+ vnc_zebra_add_del_group_afi(bgp, rfg, AFI_IP6, 1);
+}
+
+void vnc_zebra_del_group(struct bgp *bgp, struct rfapi_nve_group_cfg *rfg)
+{
+ vnc_zlog_debug_verbose("%s: entry", __func__);
+ vnc_zebra_add_del_group_afi(bgp, rfg, AFI_IP, 0);
+ vnc_zebra_add_del_group_afi(bgp, rfg, AFI_IP6, 0);
+}
+
+void vnc_zebra_reexport_group_afi(struct bgp *bgp,
+ struct rfapi_nve_group_cfg *rfg, afi_t afi)
+{
+ struct listnode *node;
+ struct rfapi_rfg_name *rfgn;
+
+ for (ALL_LIST_ELEMENTS_RO(bgp->rfapi_cfg->rfg_export_zebra_l, node,
+ rfgn)) {
+
+ if (rfgn->rfg == rfg) {
+ vnc_zebra_add_del_group_afi(bgp, rfg, afi, 0);
+ vnc_zebra_add_del_group_afi(bgp, rfg, afi, 1);
+ break;
+ }
+ }
+}
+
+
+/***********************************************************************
+ * CONTROL INTERFACE
+ ***********************************************************************/
+
+
+/* Other routes redistribution into BGP. */
+int vnc_redistribute_set(struct bgp *bgp, afi_t afi, int type)
+{
+ if (!bgp->rfapi_cfg) {
+ return CMD_WARNING_CONFIG_FAILED;
+ }
+
+ /* Set flag to BGP instance. */
+ bgp->rfapi_cfg->redist[afi][type] = 1;
+
+ // bgp->redist[afi][type] = 1;
+
+ /* Return if already redistribute flag is set. */
+ if (vrf_bitmap_check(zclient_vnc->redist[afi][type], VRF_DEFAULT))
+ return CMD_WARNING_CONFIG_FAILED;
+
+ vrf_bitmap_set(zclient_vnc->redist[afi][type], VRF_DEFAULT);
+
+ // vrf_bitmap_set(zclient_vnc->redist[afi][type], VRF_DEFAULT);
+
+ /* Return if zebra connection is not established. */
+ if (zclient_vnc->sock < 0)
+ return CMD_WARNING_CONFIG_FAILED;
+
+ if (BGP_DEBUG(zebra, ZEBRA))
+ vnc_zlog_debug_verbose("Zebra send: redistribute add %s",
+ zebra_route_string(type));
+
+ /* Send distribute add message to zebra. */
+ zebra_redistribute_send(ZEBRA_REDISTRIBUTE_ADD, zclient_vnc, afi, type,
+ 0, VRF_DEFAULT);
+
+ return CMD_SUCCESS;
+}
+
+/* Unset redistribution. */
+int vnc_redistribute_unset(struct bgp *bgp, afi_t afi, int type)
+{
+ vnc_zlog_debug_verbose("%s: type=%d entry", __func__, type);
+
+ if (!bgp->rfapi_cfg) {
+ vnc_zlog_debug_verbose("%s: return (no rfapi_cfg)", __func__);
+ return CMD_WARNING_CONFIG_FAILED;
+ }
+
+ /* Unset flag from BGP instance. */
+ bgp->rfapi_cfg->redist[afi][type] = 0;
+
+ /* Return if zebra connection is disabled. */
+ if (!vrf_bitmap_check(zclient_vnc->redist[afi][type], VRF_DEFAULT))
+ return CMD_WARNING_CONFIG_FAILED;
+ vrf_bitmap_unset(zclient_vnc->redist[afi][type], VRF_DEFAULT);
+
+ if (bgp->rfapi_cfg->redist[AFI_IP][type] == 0
+ && bgp->rfapi_cfg->redist[AFI_IP6][type] == 0
+ && zclient_vnc->sock >= 0) {
+ /* Send distribute delete message to zebra. */
+ if (BGP_DEBUG(zebra, ZEBRA))
+ vnc_zlog_debug_verbose(
+ "Zebra send: redistribute delete %s",
+ zebra_route_string(type));
+ zebra_redistribute_send(ZEBRA_REDISTRIBUTE_DELETE, zclient_vnc,
+ afi, type, 0, VRF_DEFAULT);
+ }
+
+ /* Withdraw redistributed routes from current BGP's routing table. */
+ vnc_redistribute_withdraw(bgp, afi, type);
+
+ vnc_zlog_debug_verbose("%s: return", __func__);
+
+ return CMD_SUCCESS;
+}
+
+extern struct zebra_privs_t bgpd_privs;
+
+static zclient_handler *const vnc_handlers[] = {
+ [ZEBRA_REDISTRIBUTE_ROUTE_ADD] = vnc_zebra_read_route,
+ [ZEBRA_REDISTRIBUTE_ROUTE_DEL] = vnc_zebra_read_route,
+};
+
+/*
+ * Modeled after bgp_zebra.c'bgp_zebra_init()
+ * Charriere asks, "Is it possible to carry two?"
+ */
+void vnc_zebra_init(struct thread_master *master)
+{
+ /* Set default values. */
+ zclient_vnc = zclient_new(master, &zclient_options_default,
+ vnc_handlers, array_size(vnc_handlers));
+ zclient_init(zclient_vnc, ZEBRA_ROUTE_VNC, 0, &bgpd_privs);
+}
+
+void vnc_zebra_destroy(void)
+{
+ if (zclient_vnc == NULL)
+ return;
+ zclient_stop(zclient_vnc);
+ zclient_free(zclient_vnc);
+ zclient_vnc = NULL;
+}
diff --git a/bgpd/rfapi/vnc_zebra.h b/bgpd/rfapi/vnc_zebra.h
new file mode 100644
index 0000000..b8c1cb1
--- /dev/null
+++ b/bgpd/rfapi/vnc_zebra.h
@@ -0,0 +1,56 @@
+/*
+ *
+ * Copyright 2009-2016, LabN Consulting, L.L.C.
+ *
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program; see the file COPYING; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
+ */
+
+/*
+ * File: vnc_zebra.h
+ */
+
+#ifndef _QUAGGA_BGP_VNC_ZEBRA_H
+#define _QUAGGA_BGP_VNC_ZEBRA_H
+
+#include "lib/zebra.h"
+
+extern void vnc_zebra_add_prefix(struct bgp *bgp,
+ struct rfapi_import_table *import_table,
+ struct agg_node *rn);
+
+extern void vnc_zebra_del_prefix(struct bgp *bgp,
+ struct rfapi_import_table *import_table,
+ struct agg_node *rn);
+
+extern void vnc_zebra_add_nve(struct bgp *bgp, struct rfapi_descriptor *rfd);
+
+extern void vnc_zebra_del_nve(struct bgp *bgp, struct rfapi_descriptor *rfd);
+
+extern void vnc_zebra_add_group(struct bgp *bgp,
+ struct rfapi_nve_group_cfg *rfg);
+
+extern void vnc_zebra_del_group(struct bgp *bgp,
+ struct rfapi_nve_group_cfg *rfg);
+
+extern void vnc_zebra_reexport_group_afi(struct bgp *bgp,
+ struct rfapi_nve_group_cfg *rfg,
+ afi_t afi);
+
+extern int vnc_redistribute_set(struct bgp *bgp, afi_t afi, int type);
+
+extern int vnc_redistribute_unset(struct bgp *bgp, afi_t afi, int type);
+
+#endif /* _QUAGGA_BGP_VNC_ZEBRA_H */
diff --git a/bgpd/rfp-example/librfp/Makefile b/bgpd/rfp-example/librfp/Makefile
new file mode 100644
index 0000000..8deb93d
--- /dev/null
+++ b/bgpd/rfp-example/librfp/Makefile
@@ -0,0 +1,10 @@
+all: ALWAYS
+ @$(MAKE) -s -C ../../.. bgpd/rfp-example/librfp/librfp.a
+%: ALWAYS
+ @$(MAKE) -s -C ../../.. bgpd/rfp-example/librfp/$@
+
+Makefile:
+ #nothing
+ALWAYS:
+.PHONY: ALWAYS makefiles
+.SUFFIXES:
diff --git a/bgpd/rfp-example/librfp/rfp.h b/bgpd/rfp-example/librfp/rfp.h
new file mode 100644
index 0000000..ecb0c79
--- /dev/null
+++ b/bgpd/rfp-example/librfp/rfp.h
@@ -0,0 +1,30 @@
+/*
+ *
+ * Copyright 2015-2016, LabN Consulting, L.L.C.
+ *
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program; see the file COPYING; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
+ */
+
+/* Sample header file */
+#ifndef _RFP_H
+#define _RFP_H
+
+#include "bgpd/rfapi/rfapi.h"
+extern int bgp_rfp_cfg_write(void *vty, void *bgp);
+/* TO BE REMOVED */
+void rfp_clear_vnc_nve_all(void);
+
+#endif /* _RFP_H */
diff --git a/bgpd/rfp-example/librfp/rfp_example.c b/bgpd/rfp-example/librfp/rfp_example.c
new file mode 100644
index 0000000..9a4ec7d
--- /dev/null
+++ b/bgpd/rfp-example/librfp/rfp_example.c
@@ -0,0 +1,344 @@
+/*
+ *
+ * Copyright 2015-2016, LabN Consulting, L.L.C.
+ *
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program; see the file COPYING; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
+ */
+
+#ifdef HAVE_CONFIG_H
+#include "config.h"
+#endif
+
+/* stub rfp */
+#include "rfp_internal.h"
+#include "bgpd/rfapi/rfapi.h"
+#include "lib/command.h"
+
+struct rfp_instance_t {
+ struct rfapi_rfp_cfg rfapi_config;
+ struct rfapi_rfp_cb_methods rfapi_callbacks;
+ struct thread_master *master;
+ uint32_t config_var;
+};
+
+struct rfp_instance_t
+ global_rfi; /* dynamically allocate in full implementation */
+
+/***********************************************************************
+ * Sample VTY / internal function
+ **********************************************************************/
+#define RFP_SHOW_STR "RFP information\n"
+DEFUN (rfp_example_config_value,
+ rfp_example_config_value_cmd,
+ "rfp example-config-value VALUE",
+ RFP_SHOW_STR
+ "Example value to be configured\n"
+ "Value to display\n")
+{
+ uint32_t value = 0;
+ struct rfp_instance_t *rfi = NULL;
+ rfi = rfapi_get_rfp_start_val(VTY_GET_CONTEXT(bgp)); /* BGP_NODE */
+ assert(rfi != NULL);
+
+ value = strtoul(argv[2]->arg, NULL, 10);
+ if (rfi)
+ rfi->config_var = value;
+ return CMD_SUCCESS;
+}
+
+DEFUN (rfp_holddown_factor,
+ rfp_holddown_factor_cmd,
+ "rfp holddown-factor (0-4294967295)",
+ RFP_SHOW_STR
+ "Set Hold-Down Factor as a percentage of registration lifetime.\n"
+ "Percentage of registration lifetime\n")
+{
+ struct rfp_instance_t *rfi;
+ uint32_t value = 0;
+
+ value = strtoul((argv[--argc]->arg), NULL, 10);
+ rfi = rfapi_get_rfp_start_val(VTY_GET_CONTEXT(bgp)); /* BGP_NODE */
+ if (!rfi) {
+ vty_out(vty, "VNC not configured\n");
+ return CMD_WARNING;
+ }
+ rfi->rfapi_config.holddown_factor = value;
+ rfapi_rfp_set_configuration(rfi, &rfi->rfapi_config);
+ return CMD_SUCCESS;
+}
+
+
+DEFUN (rfp_full_table_download,
+ rfp_full_table_download_cmd,
+ "rfp full-table-download <on|off>",
+ RFP_SHOW_STR
+ "RFP full table download support (default=on)\n"
+ "Enable RFP full table download\n"
+ "Disable RFP full table download\n")
+{
+ struct rfp_instance_t *rfi;
+ rfapi_rfp_download_type old;
+
+ rfi = rfapi_get_rfp_start_val(VTY_GET_CONTEXT(bgp)); /* BGP_NODE */
+ if (!rfi) {
+ vty_out(vty, "VNC not configured\n");
+ return CMD_WARNING;
+ }
+ old = rfi->rfapi_config.download_type;
+ if (argv[--argc]->arg[1] == 'n' || argv[argc]->arg[1] == 'N')
+ rfi->rfapi_config.download_type = RFAPI_RFP_DOWNLOAD_FULL;
+ else
+ rfi->rfapi_config.download_type = RFAPI_RFP_DOWNLOAD_PARTIAL;
+ if (old != rfi->rfapi_config.download_type)
+ rfapi_rfp_set_configuration(rfi, &rfi->rfapi_config);
+ return CMD_SUCCESS;
+}
+
+static void rfp_vty_install(void)
+{
+ static int installed = 0;
+ if (installed) /* do this only once */
+ return;
+ installed = 1;
+ /* example of new cli command */
+ install_element(BGP_NODE, &rfp_example_config_value_cmd);
+ install_element(BGP_NODE, &rfp_holddown_factor_cmd);
+ install_element(BGP_NODE, &rfp_full_table_download_cmd);
+}
+
+/***********************************************************************
+ * RFAPI Callbacks
+ **********************************************************************/
+
+/*------------------------------------------
+ * rfp_response_cb
+ *
+ * Callbacks of this type are used to provide asynchronous
+ * route updates from RFAPI to the RFP client.
+ *
+ * response_cb
+ * called to notify the rfp client that a next hop list
+ * that has previously been provided in response to an
+ * rfapi_query call has been updated. Deleted routes are indicated
+ * with lifetime==RFAPI_REMOVE_RESPONSE_LIFETIME.
+ *
+ * By default, the routes an NVE receives via this callback include
+ * its own routes (that it has registered). However, these may be
+ * filtered out if the global BGP_VNC_CONFIG_FILTER_SELF_FROM_RSP
+ * flag is set.
+ *
+ * input:
+ * next_hops a list of possible next hops.
+ * This is a linked list allocated within the
+ * rfapi. The response_cb callback function is responsible
+ * for freeing this memory via rfapi_free_next_hop_list()
+ * in order to avoid memory leaks.
+ *
+ * userdata value (cookie) originally specified in call to
+ * rfapi_open()
+ *
+ *------------------------------------------*/
+static void rfp_response_cb(struct rfapi_next_hop_entry *next_hops,
+ void *userdata)
+{
+ /*
+ * Identify NVE based on userdata, which is a value passed
+ * to RFAPI in the rfapi_open call
+ */
+
+ /* process list of next_hops */
+
+ /* free next hops */
+ rfapi_free_next_hop_list(next_hops);
+ return;
+}
+
+/*------------------------------------------
+ * rfp_local_cb
+ *
+ * Callbacks of this type are used to provide asynchronous
+ * route updates from RFAPI to the RFP client.
+ *
+ * local_cb
+ * called to notify the rfp client that a local route
+ * has been added or deleted. Deleted routes are indicated
+ * with lifetime==RFAPI_REMOVE_RESPONSE_LIFETIME.
+ *
+ * input:
+ * next_hops a list of possible next hops.
+ * This is a linked list allocated within the
+ * rfapi. The local_cb callback function is responsible
+ * for freeing this memory via rfapi_free_next_hop_list()
+ * in order to avoid memory leaks.
+ *
+ * userdata value (cookie) originally specified in call to
+ * rfapi_open()
+ *
+ *------------------------------------------*/
+static void rfp_local_cb(struct rfapi_next_hop_entry *next_hops, void *userdata)
+{
+ /*
+ * Identify NVE based on userdata, which is a value passed
+ * to RFAPI in the rfapi_open call
+ */
+
+ /* process list of local next_hops */
+
+ /* free next hops */
+ rfapi_free_next_hop_list(next_hops);
+ return;
+}
+
+/*------------------------------------------
+ * rfp_close_cb
+ *
+ * Callbacks used to provide asynchronous
+ * notification that an rfapi_handle was invalidated
+ *
+ * input:
+ * pHandle Firmerly valid rfapi_handle returned to
+ * client via rfapi_open().
+ *
+ * reason EIDRM handle administratively closed (clear nve ...)
+ * ESTALE handle invalidated by configuration change
+ *
+ *------------------------------------------*/
+static void rfp_close_cb(rfapi_handle pHandle, int reason)
+{
+ /* close / invalidate NVE with the pHandle returned by the rfapi_open
+ * call */
+ return;
+}
+
+/*------------------------------------------
+ * rfp_cfg_write_cb
+ *
+ * This callback is used to generate output for any config parameters
+ * that may supported by RFP via RFP defined vty commands at the bgp
+ * level. See loglevel as an example.
+ *
+ * input:
+ * vty -- quagga vty context
+ * rfp_start_val -- value returned by rfp_start
+ *
+ * output:
+ * to vty, rfp related configuration
+ *
+ * return value:
+ * lines written
+--------------------------------------------*/
+static int rfp_cfg_write_cb(struct vty *vty, void *rfp_start_val)
+{
+ struct rfp_instance_t *rfi = rfp_start_val;
+ int write = 0;
+ assert(rfp_start_val != NULL);
+ if (rfi->config_var != 0) {
+ vty_out(vty, " rfp example-config-value %u", rfi->config_var);
+ vty_out(vty, "\n");
+ write++;
+ }
+ if (rfi->rfapi_config.holddown_factor != 0) {
+ vty_out(vty, " rfp holddown-factor %u\n",
+ rfi->rfapi_config.holddown_factor);
+ write++;
+ }
+ if (rfi->rfapi_config.download_type == RFAPI_RFP_DOWNLOAD_FULL) {
+ vty_out(vty, " rfp full-table-download on\n");
+ write++;
+ }
+ return write;
+}
+
+/***********************************************************************
+ * RFAPI required functions
+ **********************************************************************/
+
+/*------------------------------------------
+ * rfp_start
+ *
+ * This function will start the RFP code
+ *
+ * input:
+ * master quagga thread_master to tie into bgpd threads
+ *
+ * output:
+ * cfgp Pointer to rfapi_rfp_cfg (null = use defaults),
+ * copied by caller, updated via rfp_set_configuration
+ * cbmp Pointer to rfapi_rfp_cb_methods, may be null
+ * copied by caller, updated via rfapi_rfp_set_cb_methods
+ *
+ * return value:
+ * rfp_start_val rfp returned value passed on rfp_stop and rfp_cfg_write
+ *
+--------------------------------------------*/
+void *rfp_start(struct thread_master *master, struct rfapi_rfp_cfg **cfgp,
+ struct rfapi_rfp_cb_methods **cbmp)
+{
+ memset(&global_rfi, 0, sizeof(global_rfi));
+ global_rfi.master = master; /* for BGPD threads */
+
+ /* initilize struct rfapi_rfp_cfg, see rfapi.h */
+ global_rfi.rfapi_config.download_type =
+ RFAPI_RFP_DOWNLOAD_PARTIAL; /* default=partial */
+ global_rfi.rfapi_config.ftd_advertisement_interval =
+ RFAPI_RFP_CFG_DEFAULT_FTD_ADVERTISEMENT_INTERVAL;
+ global_rfi.rfapi_config.holddown_factor =
+ 0; /* default: RFAPI_RFP_CFG_DEFAULT_HOLDDOWN_FACTOR */
+ global_rfi.rfapi_config.use_updated_response = 1; /* 0=no */
+ global_rfi.rfapi_config.use_removes = 1; /* 0=no */
+
+
+ /* initilize structrfapi_rfp_cb_methods , see rfapi.h */
+ global_rfi.rfapi_callbacks.cfg_cb = rfp_cfg_write_cb;
+ /* no group config */
+ global_rfi.rfapi_callbacks.response_cb = rfp_response_cb;
+ global_rfi.rfapi_callbacks.local_cb = rfp_local_cb;
+ global_rfi.rfapi_callbacks.close_cb = rfp_close_cb;
+
+ if (cfgp != NULL)
+ *cfgp = &global_rfi.rfapi_config;
+ if (cbmp != NULL)
+ *cbmp = &global_rfi.rfapi_callbacks;
+
+ rfp_vty_install();
+
+ return &global_rfi;
+}
+
+/*------------------------------------------
+ * rfp_stop
+ *
+ * This function is called on shutdown to trigger RFP cleanup
+ *
+ * input:
+ * none
+ *
+ * output:
+ * none
+ *
+ * return value:
+ * rfp_start_val
+--------------------------------------------*/
+void rfp_stop(void *rfp_start_val)
+{
+ assert(rfp_start_val != NULL);
+}
+
+/* TO BE REMOVED */
+void rfp_clear_vnc_nve_all(void)
+{
+ return;
+}
diff --git a/bgpd/rfp-example/librfp/rfp_internal.h b/bgpd/rfp-example/librfp/rfp_internal.h
new file mode 100644
index 0000000..f54f40d
--- /dev/null
+++ b/bgpd/rfp-example/librfp/rfp_internal.h
@@ -0,0 +1,28 @@
+/*
+ *
+ * Copyright 2015-2016, LabN Consulting, L.L.C.
+ *
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program; see the file COPYING; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
+ */
+
+/* Sample header file */
+#ifndef _RFP_INTERNAL_H
+#define _RFP_INTERNAL_H
+#include "lib/zebra.h"
+#include "rfp.h"
+#include "bgpd/rfapi/rfapi.h"
+
+#endif /* _RFP_INTERNAL_H */
diff --git a/bgpd/rfp-example/librfp/subdir.am b/bgpd/rfp-example/librfp/subdir.am
new file mode 100644
index 0000000..254ab71
--- /dev/null
+++ b/bgpd/rfp-example/librfp/subdir.am
@@ -0,0 +1,17 @@
+#
+# librfp
+#
+
+if ENABLE_BGP_VNC
+noinst_LIBRARIES += bgpd/rfp-example/librfp/librfp.a
+RFPLDADD = bgpd/rfp-example/librfp/librfp.a
+endif
+
+bgpd_rfp_example_librfp_librfp_a_SOURCES = \
+ bgpd/rfp-example/librfp/rfp_example.c \
+ # end
+
+noinst_HEADERS += \
+ bgpd/rfp-example/librfp/rfp.h \
+ bgpd/rfp-example/librfp/rfp_internal.h \
+ # end
diff --git a/bgpd/rfp-example/rfptest/.gitignore b/bgpd/rfp-example/rfptest/.gitignore
new file mode 100644
index 0000000..d3d7c0a
--- /dev/null
+++ b/bgpd/rfp-example/rfptest/.gitignore
@@ -0,0 +1 @@
+/rfptest
diff --git a/bgpd/rfp-example/rfptest/Makefile b/bgpd/rfp-example/rfptest/Makefile
new file mode 100644
index 0000000..659a9ce
--- /dev/null
+++ b/bgpd/rfp-example/rfptest/Makefile
@@ -0,0 +1,10 @@
+all: ALWAYS
+ @$(MAKE) -s -C ../../.. bgpd/rfp-example/rfptest/rfptest
+%: ALWAYS
+ @$(MAKE) -s -C ../../.. bgpd/rfp-example/rfptest/$@
+
+Makefile:
+ #nothing
+ALWAYS:
+.PHONY: ALWAYS makefiles
+.SUFFIXES:
diff --git a/bgpd/rfp-example/rfptest/rfptest.c b/bgpd/rfp-example/rfptest/rfptest.c
new file mode 100644
index 0000000..1036829
--- /dev/null
+++ b/bgpd/rfp-example/rfptest/rfptest.c
@@ -0,0 +1,33 @@
+/*
+ *
+ * Copyright 2015-2016, LabN Consulting, L.L.C.
+ *
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program; see the file COPYING; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
+ */
+
+#ifdef HAVE_CONFIG_H
+#include "config.h"
+#endif
+
+/* dummy test program */
+#include <stdio.h>
+#include <stdlib.h>
+#include "rfptest.h"
+int main(void)
+{
+ printf("Your test code goes here.\n");
+ exit(1);
+}
diff --git a/bgpd/rfp-example/rfptest/rfptest.h b/bgpd/rfp-example/rfptest/rfptest.h
new file mode 100644
index 0000000..1f0ccb8
--- /dev/null
+++ b/bgpd/rfp-example/rfptest/rfptest.h
@@ -0,0 +1,25 @@
+/*
+ *
+ * Copyright 2015-2016, LabN Consulting, L.L.C.
+ *
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program; see the file COPYING; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
+ */
+
+/* Sample header file */
+#ifndef _RFPTEST_H
+#define _RFPTEST_H
+
+#endif /* _RFPTEST_H */
diff --git a/bgpd/rfp-example/rfptest/subdir.am b/bgpd/rfp-example/rfptest/subdir.am
new file mode 100644
index 0000000..1b5024a
--- /dev/null
+++ b/bgpd/rfp-example/rfptest/subdir.am
@@ -0,0 +1,23 @@
+#
+# libtest
+#
+
+if ENABLE_BGP_VNC
+noinst_PROGRAMS += bgpd/rfp-example/rfptest/rfptest
+endif
+
+bgpd_rfp_example_rfptest_rfptest_CFLAGS = \
+ $(AM_CFLAGS) \
+ -I$(top_srcdir)/bgpd/rfapi \
+ # end
+bgpd_rfp_example_rfptest_rfptest_SOURCES = \
+ bgpd/rfp-example/rfptest/rfptest.c \
+ # end
+noinst_HEADERS += \
+ bgpd/rfp-example/rfptest/rfptest.h \
+ # end
+
+bgpd_rfp_example_rfptest_rfptest_LDADD = \
+ lib/libfrr.la \
+ $(RFPLDADD) \
+ # end
diff --git a/bgpd/subdir.am b/bgpd/subdir.am
new file mode 100644
index 0000000..b1eeb93
--- /dev/null
+++ b/bgpd/subdir.am
@@ -0,0 +1,255 @@
+#
+# bgpd
+#
+
+if BGPD
+noinst_LIBRARIES += bgpd/libbgp.a
+sbin_PROGRAMS += bgpd/bgpd
+noinst_PROGRAMS += bgpd/bgp_btoa
+vtysh_scan += \
+ bgpd/bgp_bfd.c \
+ bgpd/bgp_debug.c \
+ bgpd/bgp_dump.c \
+ bgpd/bgp_evpn_mh.c \
+ bgpd/bgp_evpn_vty.c \
+ bgpd/bgp_filter.c \
+ bgpd/bgp_labelpool.c \
+ bgpd/bgp_mplsvpn.c \
+ bgpd/bgp_nexthop.c \
+ bgpd/bgp_route.c \
+ bgpd/bgp_routemap.c \
+ bgpd/bgp_vty.c \
+ bgpd/bgp_flowspec_vty.c \
+ # end
+
+# can be loaded as DSO - always include for vtysh
+vtysh_scan += bgpd/bgp_rpki.c
+vtysh_scan += bgpd/bgp_bmp.c
+
+vtysh_daemons += bgpd
+
+if ENABLE_BGP_VNC
+vtysh_scan += \
+ bgpd/rfapi/bgp_rfapi_cfg.c \
+ bgpd/rfapi/rfapi.c \
+ bgpd/rfapi/rfapi_vty.c \
+ bgpd/rfapi/vnc_debug.c \
+ # end
+endif
+if SNMP
+module_LTLIBRARIES += bgpd/bgpd_snmp.la
+endif
+if RPKI
+module_LTLIBRARIES += bgpd/bgpd_rpki.la
+endif
+if BGP_BMP
+module_LTLIBRARIES += bgpd/bgpd_bmp.la
+endif
+man8 += $(MANBUILD)/frr-bgpd.8
+endif
+
+bgpd_libbgp_a_SOURCES = \
+ bgpd/bgp_addpath.c \
+ bgpd/bgp_advertise.c \
+ bgpd/bgp_aspath.c \
+ bgpd/bgp_attr.c \
+ bgpd/bgp_attr_evpn.c \
+ bgpd/bgp_bfd.c \
+ bgpd/bgp_clist.c \
+ bgpd/bgp_community.c \
+ bgpd/bgp_community_alias.c \
+ bgpd/bgp_conditional_adv.c \
+ bgpd/bgp_damp.c \
+ bgpd/bgp_debug.c \
+ bgpd/bgp_dump.c \
+ bgpd/bgp_ecommunity.c \
+ bgpd/bgp_encap_tlv.c \
+ bgpd/bgp_errors.c \
+ bgpd/bgp_evpn.c \
+ bgpd/bgp_evpn_mh.c \
+ bgpd/bgp_evpn_vty.c \
+ bgpd/bgp_filter.c \
+ bgpd/bgp_flowspec.c \
+ bgpd/bgp_flowspec_util.c \
+ bgpd/bgp_flowspec_vty.c \
+ bgpd/bgp_fsm.c \
+ bgpd/bgp_io.c \
+ bgpd/bgp_keepalives.c \
+ bgpd/bgp_label.c \
+ bgpd/bgp_labelpool.c \
+ bgpd/bgp_lcommunity.c \
+ bgpd/bgp_mac.c \
+ bgpd/bgp_memory.c \
+ bgpd/bgp_mpath.c \
+ bgpd/bgp_mplsvpn.c \
+ bgpd/bgp_network.c \
+ bgpd/bgp_nexthop.c \
+ bgpd/bgp_nht.c \
+ bgpd/bgp_open.c \
+ bgpd/bgp_packet.c \
+ bgpd/bgp_pbr.c \
+ bgpd/bgp_rd.c \
+ bgpd/bgp_regex.c \
+ bgpd/bgp_route.c \
+ bgpd/bgp_routemap.c \
+ bgpd/bgp_routemap_nb.c \
+ bgpd/bgp_routemap_nb_config.c \
+ bgpd/bgp_script.c \
+ bgpd/bgp_table.c \
+ bgpd/bgp_updgrp.c \
+ bgpd/bgp_updgrp_adv.c \
+ bgpd/bgp_updgrp_packet.c \
+ bgpd/bgp_vpn.c \
+ bgpd/bgp_vty.c \
+ bgpd/bgp_zebra.c \
+ bgpd/bgpd.c \
+ bgpd/bgp_trace.c \
+ # end
+
+if ENABLE_BGP_VNC
+bgpd_libbgp_a_SOURCES += \
+ bgpd/rfapi/bgp_rfapi_cfg.c \
+ bgpd/rfapi/rfapi_import.c \
+ bgpd/rfapi/rfapi.c \
+ bgpd/rfapi/rfapi_ap.c \
+ bgpd/rfapi/rfapi_descriptor_rfp_utils.c \
+ bgpd/rfapi/rfapi_encap_tlv.c \
+ bgpd/rfapi/rfapi_nve_addr.c \
+ bgpd/rfapi/rfapi_monitor.c \
+ bgpd/rfapi/rfapi_rib.c \
+ bgpd/rfapi/rfapi_vty.c \
+ bgpd/rfapi/vnc_debug.c \
+ bgpd/rfapi/vnc_export_bgp.c \
+ bgpd/rfapi/vnc_export_table.c \
+ bgpd/rfapi/vnc_import_bgp.c \
+ bgpd/rfapi/vnc_zebra.c \
+ # end
+endif
+
+noinst_HEADERS += \
+ bgpd/bgp_addpath.h \
+ bgpd/bgp_addpath_types.h \
+ bgpd/bgp_advertise.h \
+ bgpd/bgp_aspath.h \
+ bgpd/bgp_attr.h \
+ bgpd/bgp_attr_evpn.h \
+ bgpd/bgp_bfd.h \
+ bgpd/bgp_clist.h \
+ bgpd/bgp_community.h \
+ bgpd/bgp_community_alias.h \
+ bgpd/bgp_conditional_adv.h \
+ bgpd/bgp_damp.h \
+ bgpd/bgp_debug.h \
+ bgpd/bgp_dump.h \
+ bgpd/bgp_bmp.h \
+ bgpd/bgp_ecommunity.h \
+ bgpd/bgp_encap_tlv.h \
+ bgpd/bgp_encap_types.h \
+ bgpd/bgp_errors.h \
+ bgpd/bgp_evpn.h \
+ bgpd/bgp_evpn_mh.h \
+ bgpd/bgp_evpn_private.h \
+ bgpd/bgp_evpn_vty.h \
+ bgpd/bgp_filter.h \
+ bgpd/bgp_flowspec.h \
+ bgpd/bgp_flowspec_private.h \
+ bgpd/bgp_flowspec_util.h \
+ bgpd/bgp_fsm.h \
+ bgpd/bgp_io.h \
+ bgpd/bgp_keepalives.h \
+ bgpd/bgp_label.h \
+ bgpd/bgp_labelpool.h \
+ bgpd/bgp_lcommunity.h \
+ bgpd/bgp_mac.h \
+ bgpd/bgp_memory.h \
+ bgpd/bgp_mpath.h \
+ bgpd/bgp_mplsvpn.h \
+ bgpd/bgp_mplsvpn_snmp.h \
+ bgpd/bgp_network.h \
+ bgpd/bgp_nexthop.h \
+ bgpd/bgp_nht.h \
+ bgpd/bgp_open.h \
+ bgpd/bgp_packet.h \
+ bgpd/bgp_pbr.h \
+ bgpd/bgp_rd.h \
+ bgpd/bgp_regex.h \
+ bgpd/bgp_rpki.h \
+ bgpd/bgp_route.h \
+ bgpd/bgp_routemap_nb.h \
+ bgpd/bgp_script.h \
+ bgpd/bgp_table.h \
+ bgpd/bgp_updgrp.h \
+ bgpd/bgp_vpn.h \
+ bgpd/bgp_vty.h \
+ bgpd/bgp_zebra.h \
+ bgpd/bgpd.h \
+ bgpd/bgp_trace.h \
+ \
+ bgpd/rfapi/bgp_rfapi_cfg.h \
+ bgpd/rfapi/rfapi_import.h \
+ bgpd/rfapi/rfapi.h \
+ bgpd/rfapi/rfapi_ap.h \
+ bgpd/rfapi/rfapi_backend.h \
+ bgpd/rfapi/rfapi_descriptor_rfp_utils.h \
+ bgpd/rfapi/rfapi_encap_tlv.h \
+ bgpd/rfapi/rfapi_nve_addr.h \
+ bgpd/rfapi/rfapi_monitor.h \
+ bgpd/rfapi/rfapi_private.h \
+ bgpd/rfapi/rfapi_rib.h \
+ bgpd/rfapi/rfapi_vty.h \
+ bgpd/rfapi/vnc_debug.h \
+ bgpd/rfapi/vnc_export_bgp.h \
+ bgpd/rfapi/vnc_export_table.h \
+ bgpd/rfapi/vnc_import_bgp.h \
+ bgpd/rfapi/vnc_zebra.h \
+ bgpd/rfapi/vnc_export_bgp_p.h \
+ bgpd/rfapi/vnc_import_bgp_p.h \
+ bgpd/bgp_vnc_types.h \
+ # end
+
+bgpd_bgpd_SOURCES = bgpd/bgp_main.c
+bgpd_bgp_btoa_SOURCES = bgpd/bgp_btoa.c
+
+# RFPLDADD is set in bgpd/rfp-example/librfp/subdir.am
+bgpd_bgpd_LDADD = bgpd/libbgp.a $(RFPLDADD) lib/libfrr.la $(LIBYANG_LIBS) $(LIBCAP) $(LIBM) $(UST_LIBS)
+bgpd_bgp_btoa_LDADD = bgpd/libbgp.a $(RFPLDADD) lib/libfrr.la $(LIBYANG_LIBS) $(LIBCAP) $(LIBM) $(UST_LIBS)
+
+bgpd_bgpd_snmp_la_SOURCES = bgpd/bgp_snmp.c bgpd/bgp_mplsvpn_snmp.c
+bgpd_bgpd_snmp_la_CFLAGS = $(AM_CFLAGS) $(SNMP_CFLAGS) -std=gnu11
+bgpd_bgpd_snmp_la_LDFLAGS = $(MODULE_LDFLAGS)
+bgpd_bgpd_snmp_la_LIBADD = lib/libfrrsnmp.la
+
+bgpd_bgpd_rpki_la_SOURCES = bgpd/bgp_rpki.c
+bgpd_bgpd_rpki_la_CFLAGS = $(AM_CFLAGS) $(RTRLIB_CFLAGS)
+bgpd_bgpd_rpki_la_LDFLAGS = $(MODULE_LDFLAGS)
+bgpd_bgpd_rpki_la_LIBADD = $(RTRLIB_LIBS)
+
+bgpd_bgpd_bmp_la_SOURCES = bgpd/bgp_bmp.c
+bgpd_bgpd_bmp_la_LIBADD = lib/libfrrcares.la
+bgpd_bgpd_bmp_la_LDFLAGS = $(MODULE_LDFLAGS)
+
+clippy_scan += \
+ bgpd/bgp_bmp.c \
+ bgpd/bgp_debug.c \
+ bgpd/bgp_evpn_vty.c \
+ bgpd/bgp_labelpool.c \
+ bgpd/bgp_route.c \
+ bgpd/bgp_routemap.c \
+ bgpd/bgp_rpki.c \
+ bgpd/bgp_vty.c \
+ # end
+
+nodist_bgpd_bgpd_SOURCES = \
+ yang/frr-bgp-types.yang.c \
+ yang/frr-bgp.yang.c \
+ yang/frr-bgp-common-structure.yang.c \
+ yang/frr-bgp-common.yang.c \
+ yang/frr-bgp-common-multiprotocol.yang.c \
+ yang/frr-bgp-neighbor.yang.c \
+ yang/frr-bgp-peer-group.yang.c \
+ yang/frr-bgp-bmp.yang.c \
+ yang/frr-bgp-rpki.yang.c \
+ yang/frr-deviations-bgp-datacenter.yang.c \
+ yang/frr-bgp-filter.yang.c \
+ yang/frr-bgp-route-map.yang.c \
+ # end