summaryrefslogtreecommitdiffstats
path: root/bgpd/rfapi/rfapi.c
diff options
context:
space:
mode:
Diffstat (limited to 'bgpd/rfapi/rfapi.c')
-rw-r--r--bgpd/rfapi/rfapi.c4060
1 files changed, 4060 insertions, 0 deletions
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;
+}