summaryrefslogtreecommitdiffstats
path: root/bgpd/rfapi
diff options
context:
space:
mode:
authorDaniel Baumann <daniel.baumann@progress-linux.org>2024-04-09 13:16:35 +0000
committerDaniel Baumann <daniel.baumann@progress-linux.org>2024-04-09 13:16:35 +0000
commite2bbf175a2184bd76f6c54ccf8456babeb1a46fc (patch)
treef0b76550d6e6f500ada964a3a4ee933a45e5a6f1 /bgpd/rfapi
parentInitial commit. (diff)
downloadfrr-e2bbf175a2184bd76f6c54ccf8456babeb1a46fc.tar.xz
frr-e2bbf175a2184bd76f6c54ccf8456babeb1a46fc.zip
Adding upstream version 9.1.upstream/9.1
Signed-off-by: Daniel Baumann <daniel.baumann@progress-linux.org>
Diffstat (limited to 'bgpd/rfapi')
-rw-r--r--bgpd/rfapi/bgp_rfapi_cfg.c4627
-rw-r--r--bgpd/rfapi/bgp_rfapi_cfg.h302
-rw-r--r--bgpd/rfapi/rfapi.c4060
-rw-r--r--bgpd/rfapi/rfapi.h900
-rw-r--r--bgpd/rfapi/rfapi_ap.c542
-rw-r--r--bgpd/rfapi/rfapi_ap.h72
-rw-r--r--bgpd/rfapi/rfapi_backend.h60
-rw-r--r--bgpd/rfapi/rfapi_descriptor_rfp_utils.c110
-rw-r--r--bgpd/rfapi/rfapi_descriptor_rfp_utils.h25
-rw-r--r--bgpd/rfapi/rfapi_encap_tlv.c730
-rw-r--r--bgpd/rfapi/rfapi_encap_tlv.h22
-rw-r--r--bgpd/rfapi/rfapi_import.c4818
-rw-r--r--bgpd/rfapi/rfapi_import.h230
-rw-r--r--bgpd/rfapi/rfapi_monitor.c1557
-rw-r--r--bgpd/rfapi/rfapi_monitor.h177
-rw-r--r--bgpd/rfapi/rfapi_nve_addr.c145
-rw-r--r--bgpd/rfapi/rfapi_nve_addr.h25
-rw-r--r--bgpd/rfapi/rfapi_private.h400
-rw-r--r--bgpd/rfapi/rfapi_rib.c2424
-rw-r--r--bgpd/rfapi/rfapi_rib.h141
-rw-r--r--bgpd/rfapi/rfapi_vty.c5008
-rw-r--r--bgpd/rfapi/rfapi_vty.h162
-rw-r--r--bgpd/rfapi/vnc_debug.c182
-rw-r--r--bgpd/rfapi/vnc_debug.h38
-rw-r--r--bgpd/rfapi/vnc_export_bgp.c2091
-rw-r--r--bgpd/rfapi/vnc_export_bgp.h28
-rw-r--r--bgpd/rfapi/vnc_export_bgp_p.h61
-rw-r--r--bgpd/rfapi/vnc_export_table.c179
-rw-r--r--bgpd/rfapi/vnc_export_table.h53
-rw-r--r--bgpd/rfapi/vnc_import_bgp.c2893
-rw-r--r--bgpd/rfapi/vnc_import_bgp.h62
-rw-r--r--bgpd/rfapi/vnc_import_bgp_p.h31
-rw-r--r--bgpd/rfapi/vnc_zebra.c887
-rw-r--r--bgpd/rfapi/vnc_zebra.h43
34 files changed, 33085 insertions, 0 deletions
diff --git a/bgpd/rfapi/bgp_rfapi_cfg.c b/bgpd/rfapi/bgp_rfapi_cfg.c
new file mode 100644
index 0000000..5b6961d
--- /dev/null
+++ b/bgpd/rfapi/bgp_rfapi_cfg.c
@@ -0,0 +1,4627 @@
+// SPDX-License-Identifier: GPL-2.0-or-later
+/*
+ *
+ * Copyright 2009-2016, LabN Consulting, L.L.C.
+ *
+ */
+#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 {
+
+ /* TODO: save RD format */
+ 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 {
+
+ /* TODO: save RD format */
+ 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 {
+
+ /* TODO: save RD format */
+ 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 %pRDP\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 %pRDP\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 %pRDP\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..77549ad
--- /dev/null
+++ b/bgpd/rfapi/bgp_rfapi_cfg.h
@@ -0,0 +1,302 @@
+// SPDX-License-Identifier: GPL-2.0-or-later
+/*
+ *
+ * Copyright 2009-2016, LabN Consulting, L.L.C.
+ *
+ */
+
+#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..ff7137b
--- /dev/null
+++ b/bgpd/rfapi/rfapi.c
@@ -0,0 +1,4060 @@
+// SPDX-License-Identifier: GPL-2.0-or-later
+/*
+ *
+ * Copyright 2009-2016, LabN Consulting, L.L.C.
+ *
+ */
+
+#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 */
+
+#define DEBUG_CLEANUP 0
+
+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=%pRDP 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=%pRDP)",
+ __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->connection->status = Established; /* keep bgp core happy */
+
+ bgp_peer_connection_buffers_free(rfd->peer->connection);
+
+ { /* base code assumes have valid host pointer */
+ char buf[INET6_ADDRSTRLEN];
+ buf[0] = 0;
+
+ if (rfd->vn_addr.addr_family == AF_INET) {
+ inet_ntop(AF_INET, &rfd->vn_addr.addr.v4, buf,
+ sizeof(buf));
+ } else if (rfd->vn_addr.addr_family == AF_INET6) {
+ inet_ntop(AF_INET6, &rfd->vn_addr.addr.v6, buf,
+ sizeof(buf));
+ }
+ 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_unregister\n"
+ "indicate vn addr follows\n"
+ "virtual network interface address\n"
+ "virtual network interface address\n"
+ "indicate xt addr follows\n"
+ "underlay network interface address\n"
+ "underlay network interface address\n"
+ "prefix to remove\n"
+ "prefix to remove\n"
+ "prefix to remove\n"
+ "Remove without holddown\n")
+{
+ 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 */
+
+#if DEBUG_CLEANUP
+ zlog_debug("%s: bgp %p", __func__, bgp);
+#endif
+
+ /*
+ * This clears queries and registered routes, and closes nves
+ */
+ if (bgp->rfapi)
+ rfp_clear_vnc_nve_all();
+
+ /*
+ * close any remaining descriptors
+ */
+ struct rfapi *h = bgp->rfapi;
+
+ if (h && h->descriptors.count) {
+ struct listnode *node, *nnode;
+ struct rfapi_descriptor *rfd;
+#if DEBUG_CLEANUP
+ zlog_debug("%s: descriptor count %u", __func__,
+ h->descriptors.count);
+#endif
+ for (ALL_LIST_ELEMENTS(&h->descriptors, node, nnode, rfd)) {
+#if DEBUG_CLEANUP
+ zlog_debug("%s: closing rfd %p", __func__, rfd);
+#endif
+ (void)rfapi_close(rfd);
+ }
+ }
+
+ 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 %pRDP", __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..3f61d5b
--- /dev/null
+++ b/bgpd/rfapi/rfapi.h
@@ -0,0 +1,900 @@
+// SPDX-License-Identifier: GPL-2.0-or-later
+/*
+ *
+ * Copyright 2009-2016, LabN Consulting, L.L.C.
+ *
+ */
+
+#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 event_loop *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..fe344a5
--- /dev/null
+++ b/bgpd/rfapi/rfapi_ap.c
@@ -0,0 +1,542 @@
+// SPDX-License-Identifier: GPL-2.0-or-later
+/*
+ *
+ * Copyright 2009-2016, LabN Consulting, L.L.C.
+ *
+ */
+
+#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..7698fba
--- /dev/null
+++ b/bgpd/rfapi/rfapi_ap.h
@@ -0,0 +1,72 @@
+// SPDX-License-Identifier: GPL-2.0-or-later
+/*
+ *
+ * Copyright 2009-2016, LabN Consulting, L.L.C.
+ *
+ */
+#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..32ea0a2
--- /dev/null
+++ b/bgpd/rfapi/rfapi_backend.h
@@ -0,0 +1,60 @@
+// SPDX-License-Identifier: GPL-2.0-or-later
+/*
+ *
+ * Copyright 2009-2016, LabN Consulting, L.L.C.
+ *
+ */
+
+#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 event_loop *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..28326ab
--- /dev/null
+++ b/bgpd/rfapi/rfapi_descriptor_rfp_utils.c
@@ -0,0 +1,110 @@
+// SPDX-License-Identifier: GPL-2.0-or-later
+/*
+ *
+ * Copyright 2009-2016, LabN Consulting, L.L.C.
+ *
+ */
+
+#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..9e65a70
--- /dev/null
+++ b/bgpd/rfapi/rfapi_descriptor_rfp_utils.h
@@ -0,0 +1,25 @@
+// SPDX-License-Identifier: GPL-2.0-or-later
+/*
+ *
+ * Copyright 2009-2016, LabN Consulting, L.L.C.
+ *
+ */
+
+
+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..37a7326
--- /dev/null
+++ b/bgpd/rfapi/rfapi_encap_tlv.c
@@ -0,0 +1,730 @@
+// SPDX-License-Identifier: GPL-2.0-or-later
+/*
+ * Copyright 2015-2016, LabN Consulting, L.L.C.
+ */
+
+#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;
+
+ case BGP_ENCAP_TYPE_RESERVED:
+ assert(!"Cannot process BGP_ENCAP_TYPE_RESERVED");
+ }
+ 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;
+
+ case BGP_ENCAP_TYPE_RESERVED:
+ assert(!"Cannot process BGP_ENCAP_TYPE_RESERVED");
+ }
+}
diff --git a/bgpd/rfapi/rfapi_encap_tlv.h b/bgpd/rfapi/rfapi_encap_tlv.h
new file mode 100644
index 0000000..56131d8
--- /dev/null
+++ b/bgpd/rfapi/rfapi_encap_tlv.h
@@ -0,0 +1,22 @@
+// SPDX-License-Identifier: GPL-2.0-or-later
+/*
+ * Copyright 2015-2016, LabN Consulting, L.L.C.
+ */
+
+#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..a93e186
--- /dev/null
+++ b/bgpd/rfapi/rfapi_import.c
@@ -0,0 +1,4818 @@
+// SPDX-License-Identifier: GPL-2.0-or-later
+/*
+ * Copyright 2009-2016, LabN Consulting, L.L.C.
+ */
+
+/*
+ * 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 "frrevent.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 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)) {
+ 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;
+
+ case SAFI_UNSPEC:
+ case SAFI_UNICAST:
+ case SAFI_MULTICAST:
+ case SAFI_EVPN:
+ case SAFI_LABELED_UNICAST:
+ case SAFI_FLOWSPEC:
+ case SAFI_MAX:
+ assert(!"Passed in safi should be impossible");
+ }
+ }
+
+ 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 =
+ EVENT_ARG(bpi->extra->vnc.import.timer);
+
+ XFREE(MTYPE_RFAPI_WITHDRAW, wcb);
+ EVENT_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;
+ struct agg_table *at;
+
+ at = it->imported_vpn[afi];
+ if (at) {
+ for (rn = agg_route_top(at); 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);
+ }
+ agg_table_finish(at);
+ }
+
+ if (at) {
+ at = it->imported_encap[afi];
+ for (rn = agg_route_top(at); 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(at);
+ }
+ }
+ 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 %pRDP", __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),
+ bgp_get_asnotation(k->peer ? k->peer->bgp : NULL));
+ 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=%pRDP, 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=%pRDP, 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 %pRDP", __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 event *t)
+{
+ struct rfapi_withdraw *wcb = EVENT_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 event *t)
+{
+ struct rfapi_withdraw *wcb = EVENT_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 event *))
+{
+ 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;
+ event_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;
+ event_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 event 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, sizeof(buf)),
+ 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;
+
+ case AFI_UNSPEC:
+ case AFI_L2VPN:
+ case AFI_MAX:
+ 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 = EVENT_ARG(
+ bpi->extra->vnc.import.timer);
+
+ XFREE(MTYPE_RFAPI_WITHDRAW, wcb);
+ EVENT_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 event 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 =
+ EVENT_ARG(bpi->extra->vnc.import.timer);
+
+ XFREE(MTYPE_RFAPI_WITHDRAW, wcb);
+ EVENT_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 event 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;
+
+ case AFI_UNSPEC:
+ case AFI_MAX:
+ 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 = EVENT_ARG(
+ bpi->extra->vnc.import.timer);
+
+ XFREE(MTYPE_RFAPI_WITHDRAW, wcb);
+ EVENT_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->connection->su,
+ &info_new->peer->connection->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 =
+ EVENT_ARG(bpi->extra->vnc.import.timer);
+
+ XFREE(MTYPE_RFAPI_WITHDRAW, wcb);
+ EVENT_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;
+
+ case SAFI_UNSPEC:
+ case SAFI_UNICAST:
+ case SAFI_MULTICAST:
+ case SAFI_EVPN:
+ case SAFI_LABELED_UNICAST:
+ case SAFI_FLOWSPEC:
+ case SAFI_MAX:
+ /* not expected */
+ flog_err(EC_LIB_DEVELOPMENT, "%s: bad safi %d", __func__, safi);
+ return rfapiBgpInfoFilteredImportBadSafi;
+ }
+
+ assert(!"Reached end of function when we were not expecting to");
+}
+
+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 = NULL;
+ void (*timer_service_func)(struct event *) = NULL;
+
+ 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;
+ case SAFI_UNSPEC:
+ case SAFI_UNICAST:
+ case SAFI_MULTICAST:
+ case SAFI_EVPN:
+ case SAFI_LABELED_UNICAST:
+ case SAFI_FLOWSPEC:
+ case SAFI_MAX:
+ /* 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;
+ }
+
+ rfapiImportTableFlush(h->it_ce);
+
+ 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 =
+ EVENT_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);
+ EVENT_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..dd06afe
--- /dev/null
+++ b/bgpd/rfapi/rfapi_import.h
@@ -0,0 +1,230 @@
+// SPDX-License-Identifier: GPL-2.0-or-later
+/*
+ *
+ * Copyright 2009-2016, LabN Consulting, L.L.C.
+ *
+ */
+
+/*
+ * 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 "frrevent.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..3fe957b
--- /dev/null
+++ b/bgpd/rfapi/rfapi_monitor.c
@@ -0,0 +1,1557 @@
+// SPDX-License-Identifier: GPL-2.0-or-later
+/*
+ *
+ * Copyright 2009-2016, LabN Consulting, L.L.C.
+ *
+ */
+
+/*
+ * 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;
+
+ case SAFI_UNSPEC:
+ case SAFI_UNICAST:
+ case SAFI_MULTICAST:
+ case SAFI_EVPN:
+ case SAFI_LABELED_UNICAST:
+ case SAFI_FLOWSPEC:
+ case SAFI_MAX:
+ 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;
+
+ case SAFI_UNSPEC:
+ case SAFI_UNICAST:
+ case SAFI_MULTICAST:
+ case SAFI_EVPN:
+ case SAFI_LABELED_UNICAST:
+ case SAFI_FLOWSPEC:
+ case SAFI_MAX:
+ 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);
+ }
+
+ EVENT_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);
+ }
+
+ EVENT_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
+ }
+
+ EVENT_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 event *t)
+{
+ struct rfapi_monitor_vpn *m = EVENT_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 = event_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;
+
+ EVENT_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);
+ }
+
+ event_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 event *t)
+{
+ struct rfapi_monitor_eth *m = EVENT_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 = event_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;
+
+ EVENT_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);
+ }
+
+ event_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);
+ }
+
+ EVENT_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..3200079
--- /dev/null
+++ b/bgpd/rfapi/rfapi_monitor.h
@@ -0,0 +1,177 @@
+// SPDX-License-Identifier: GPL-2.0-or-later
+/*
+ *
+ * Copyright 2009-2016, LabN Consulting, L.L.C.
+ *
+ */
+
+#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 event *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 event *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..eabec2f
--- /dev/null
+++ b/bgpd/rfapi/rfapi_nve_addr.c
@@ -0,0 +1,145 @@
+// SPDX-License-Identifier: GPL-2.0-or-later
+/*
+ *
+ * Copyright 2009-2016, LabN Consulting, L.L.C.
+ *
+ */
+
+
+#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..49e9fc5
--- /dev/null
+++ b/bgpd/rfapi/rfapi_nve_addr.h
@@ -0,0 +1,25 @@
+// SPDX-License-Identifier: GPL-2.0-or-later
+/*
+ *
+ * Copyright 2009-2016, LabN Consulting, L.L.C.
+ *
+ */
+
+#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..2161d43
--- /dev/null
+++ b/bgpd/rfapi/rfapi_private.h
@@ -0,0 +1,400 @@
+// SPDX-License-Identifier: GPL-2.0-or-later
+/*
+ *
+ * Copyright 2009-2016, LabN Consulting, L.L.C.
+ *
+ */
+
+/*
+ * 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..5784f95
--- /dev/null
+++ b/bgpd/rfapi/rfapi_rib.c
@@ -0,0 +1,2424 @@
+// SPDX-License-Identifier: GPL-2.0-or-later
+/*
+ *
+ * Copyright 2009-2016, LabN Consulting, L.L.C.
+ *
+ */
+
+/*
+ * 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
+#define DEBUG_CLEANUP 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_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;
+
+ 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;
+ }
+ }
+ 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 = EVENT_ARG(goner->timer);
+ EVENT_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 event *t)
+{
+ struct rfapi_rib_tcb *tcb = EVENT_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 = EVENT_ARG(ri->timer);
+ EVENT_OFF(ri->timer);
+ } else {
+ tcb = XCALLOC(MTYPE_RFAPI_RECENT_DELETE,
+ sizeof(struct rfapi_rib_tcb));
+ }
+#if DEBUG_CLEANUP
+ zlog_debug("%s: rfd %p, rn %p, ri %p, tcb %p", __func__, rfd, rn, ri,
+ tcb);
+#endif
+
+ 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);
+
+ event_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)) {
+
+ if (ri->timer) {
+ struct rfapi_rib_tcb
+ *tcb;
+
+ tcb = EVENT_ARG(
+ ri->timer);
+ EVENT_OFF(ri->timer);
+ XFREE(MTYPE_RFAPI_RECENT_DELETE,
+ tcb);
+ }
+ 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;
+
+#if DEBUG_CLEANUP
+ zlog_debug("%s: rfd %p", __func__, rfd);
+#endif
+
+ /*
+ * 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 = EVENT_ARG(ri->timer);
+ EVENT_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 = EVENT_ARG(ori->timer);
+ EVENT_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=%pRDP)",
+ __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 = EVENT_ARG(ri->timer);
+ EVENT_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=%pRDP",
+ __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 %pRDP\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 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;
+
+ 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);
+
+ if (pfx_match && !prefix_match(pfx_match, p)
+ && !prefix_match(p, pfx_match))
+ continue;
+
+ 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..5fa838b
--- /dev/null
+++ b/bgpd/rfapi/rfapi_rib.h
@@ -0,0 +1,141 @@
+// SPDX-License-Identifier: GPL-2.0-or-later
+/*
+ *
+ * Copyright 2009-2016, LabN Consulting, L.L.C.
+ *
+ */
+
+/*
+ * 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 event *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..252b6d6
--- /dev/null
+++ b/bgpd/rfapi/rfapi_vty.c
@@ -0,0 +1,5008 @@
+// SPDX-License-Identifier: GPL-2.0-or-later
+/*
+ *
+ * Copyright 2009-2016, LabN Consulting, L.L.C.
+ *
+ */
+
+#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;
+}
+
+PRINTFRR(2, 3)
+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,
+ sizeof(buf)));
+ }
+ }
+
+ 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->attr->srv6_l3vpn || bpi->attr->srv6_vpn) {
+ struct in6_addr *sid_tmp =
+ bpi->attr->srv6_l3vpn
+ ? (&bpi->attr->srv6_l3vpn->sid)
+ : (&bpi->attr->srv6_vpn->sid);
+ vty_out(vty, " sid=%pI6", sid_tmp);
+
+ if (bpi->attr->srv6_l3vpn &&
+ bpi->attr->srv6_l3vpn->loc_block_len != 0) {
+ vty_out(vty, " sid_structure=[%d,%d,%d,%d]",
+ bpi->attr->srv6_l3vpn->loc_block_len,
+ bpi->attr->srv6_l3vpn->loc_node_len,
+ bpi->attr->srv6_l3vpn->func_len,
+ bpi->attr->srv6_l3vpn->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;
+ 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 */
+ fp(out, " nexthop=%pI4%s", &attr->nexthop, 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 event *t = (struct event *)bpi->extra->vnc.import.timer;
+
+ r = snprintf(p, REMAIN, " [%4lu] ",
+ event_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 = snprintfrr(p, REMAIN, "%pI4",
+ &bpi->attr->mp_nexthop_global_in);
+ INCP;
+ } else if (af == AF_INET6) {
+ r = snprintfrr(p, REMAIN, "%pI6",
+ &bpi->attr->mp_nexthop_global);
+ 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,
+ sizeof(buf)));
+ 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,
+ sizeof(buf_pfx)),
+ 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, sizeof(buf));
+ } else {
+ inet_ntop(p->family, &p->u.prefix, buf, sizeof(buf));
+ }
+
+ 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 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;
+
+ if (!rfd->mon &&
+ !(rfd->mon_eth && skiplist_count(rfd->mon_eth)))
+ 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;
+ } else
+ fp(out, "%-15s %-15s", "", "");
+ buf_remain[0] = 0;
+ rfapiFormatSeconds(
+ event_timer_remain_second(m->timer),
+ buf_remain, BUFSIZ);
+ fp(out, " %-15s %-10s\n",
+ inet_ntop(m->p.family, &m->p.u.prefix,
+ buf_pfx, sizeof(buf_pfx)),
+ 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;
+ } else
+ fp(out, "%-15s %-15s", "", "");
+ buf_remain[0] = 0;
+ rfapiFormatSeconds(event_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, sizeof(buf_ntop)),
+ 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,
+ sizeof(buf_ntop)));
+ }
+
+ 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,
+ sizeof(buf_ntop)));
+ 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,
+ sizeof(buf_ntop)));
+ }
+ 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 event *t = (struct event *)bpi->extra->vnc.import.timer;
+ remaining = event_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, "%pRDP", &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;
+
+ case RFAPI_VN_OPTION_TYPE_INTERNAL_RD:
+ 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;
+ case RFAPI_UN_OPTION_TYPE_PROVISIONAL:
+ 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);
+
+ case SHOW_NVE_SUMMARY_UNKNOWN_NVES:
+ case SHOW_NVE_SUMMARY_MAX:
+ 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;
+ /* TODO: save RD format */
+ 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);
+ print_cleared_stats(&cda); /* frees lists in cda */
+ 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..a563701
--- /dev/null
+++ b/bgpd/rfapi/rfapi_vty.h
@@ -0,0 +1,162 @@
+// SPDX-License-Identifier: GPL-2.0-or-later
+/*
+ *
+ * Copyright 2009-2016, LabN Consulting, L.L.C.
+ *
+ */
+
+#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, ...)
+ PRINTFRR(2, 3);
+
+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..eb0d861
--- /dev/null
+++ b/bgpd/rfapi/vnc_debug.c
@@ -0,0 +1,182 @@
+// SPDX-License-Identifier: GPL-2.0-or-later
+/*
+ *
+ * Copyright 2016, LabN Consulting, L.L.C.
+ */
+
+#include "lib/zebra.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..c775127
--- /dev/null
+++ b/bgpd/rfapi/vnc_debug.h
@@ -0,0 +1,38 @@
+// SPDX-License-Identifier: GPL-2.0-or-later
+/*
+ *
+ * Copyright 2016, LabN Consulting, L.L.C.
+ */
+
+#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..7decb75
--- /dev/null
+++ b/bgpd/rfapi/vnc_export_bgp.c
@@ -0,0 +1,2091 @@
+// SPDX-License-Identifier: GPL-2.0-or-later
+/*
+ *
+ * Copyright 2009-2016, LabN Consulting, L.L.C.
+ *
+ */
+
+/*
+ * 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 */
+ 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 */
+ 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 */
+ 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 */
+ 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 */
+ 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 */
+ 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 */
+ 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
+ */
+ EVENT_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 event *t)
+{
+ struct vnc_export_info *eti = EVENT_ARG(t);
+ const struct prefix *p = agg_node_get_prefix(eti->node);
+
+ /*
+ * withdraw the route
+ */
+ bgp_withdraw(eti->peer, p, 0, /* addpath_id */
+ 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;
+ event_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
+ */
+ EVENT_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) {
+ EVENT_OFF(eti->timer);
+ vnc_eti_delete(eti);
+ }
+
+ bgp_withdraw(ri->peer, dest_p, /* prefix */
+ 0, /* addpath_id */
+ 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..9ea20b1
--- /dev/null
+++ b/bgpd/rfapi/vnc_export_bgp.h
@@ -0,0 +1,28 @@
+// SPDX-License-Identifier: GPL-2.0-or-later
+/*
+ *
+ * Copyright 2009-2016, LabN Consulting, L.L.C.
+ *
+ */
+
+#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..8f5b613
--- /dev/null
+++ b/bgpd/rfapi/vnc_export_bgp_p.h
@@ -0,0 +1,61 @@
+// SPDX-License-Identifier: GPL-2.0-or-later
+/*
+ *
+ * Copyright 2009-2016, LabN Consulting, L.L.C.
+ *
+ */
+
+#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..4b6baca
--- /dev/null
+++ b/bgpd/rfapi/vnc_export_table.c
@@ -0,0 +1,179 @@
+// SPDX-License-Identifier: GPL-2.0-or-later
+/*
+ *
+ * Copyright 2009-2016, LabN Consulting, L.L.C.
+ *
+ */
+
+
+#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..f715de0
--- /dev/null
+++ b/bgpd/rfapi/vnc_export_table.h
@@ -0,0 +1,53 @@
+// SPDX-License-Identifier: GPL-2.0-or-later
+/*
+ *
+ * Copyright 2009-2016, LabN Consulting, L.L.C.
+ *
+ */
+
+#ifndef _QUAGGA_VNC_VNC_EXPORT_TABLE_H_
+#define _QUAGGA_VNC_VNC_EXPORT_TABLE_H_
+
+#include "lib/table.h"
+#include "frrevent.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 event *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..3fcc0e6
--- /dev/null
+++ b/bgpd/rfapi/vnc_import_bgp.c
@@ -0,0 +1,2893 @@
+// SPDX-License-Identifier: GPL-2.0-or-later
+/*
+ *
+ * Copyright 2009-2016, LabN Consulting, L.L.C.
+ *
+ */
+
+/*
+ * 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);
+ if (iattr)
+ bgp_attr_unintern(&iattr);
+}
+
+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);
+ if (iattr)
+ bgp_attr_unintern(&iattr);
+}
+
+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)) {
+
+ 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;
+
+ 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..c8d4170
--- /dev/null
+++ b/bgpd/rfapi/vnc_import_bgp.h
@@ -0,0 +1,62 @@
+// SPDX-License-Identifier: GPL-2.0-or-later
+/*
+ *
+ * Copyright 2009-2016, LabN Consulting, L.L.C.
+ *
+ */
+
+#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..7019796
--- /dev/null
+++ b/bgpd/rfapi/vnc_import_bgp_p.h
@@ -0,0 +1,31 @@
+// SPDX-License-Identifier: GPL-2.0-or-later
+/*
+ *
+ * Copyright 2009-2016, LabN Consulting, L.L.C.
+ *
+ */
+
+#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..c886bee
--- /dev/null
+++ b/bgpd/rfapi/vnc_zebra.c
@@ -0,0 +1,887 @@
+// SPDX-License-Identifier: GPL-2.0-or-later
+/*
+ *
+ * Copyright 2009-2016, LabN Consulting, L.L.C.
+ *
+ */
+
+/*
+ * 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->connection->status =
+ Established; /* keep bgp core happy */
+
+ bgp_peer_connection_buffers_free(
+ vncHD1VR.peer->connection);
+
+ /* 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 event_loop *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..6d7e689
--- /dev/null
+++ b/bgpd/rfapi/vnc_zebra.h
@@ -0,0 +1,43 @@
+// SPDX-License-Identifier: GPL-2.0-or-later
+/*
+ *
+ * Copyright 2009-2016, LabN Consulting, L.L.C.
+ *
+ */
+
+/*
+ * 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 */