summaryrefslogtreecommitdiffstats
path: root/bgpd/rfapi/rfapi_ap.c
diff options
context:
space:
mode:
Diffstat (limited to 'bgpd/rfapi/rfapi_ap.c')
-rw-r--r--bgpd/rfapi/rfapi_ap.c542
1 files changed, 542 insertions, 0 deletions
diff --git a/bgpd/rfapi/rfapi_ap.c b/bgpd/rfapi/rfapi_ap.c
new file mode 100644
index 0000000..fe344a5
--- /dev/null
+++ b/bgpd/rfapi/rfapi_ap.c
@@ -0,0 +1,542 @@
+// SPDX-License-Identifier: GPL-2.0-or-later
+/*
+ *
+ * Copyright 2009-2016, LabN Consulting, L.L.C.
+ *
+ */
+
+#include "lib/zebra.h"
+#include "lib/prefix.h"
+#include "lib/agg_table.h"
+#include "lib/vty.h"
+#include "lib/memory.h"
+#include "lib/routemap.h"
+#include "lib/log.h"
+#include "lib/linklist.h"
+#include "lib/command.h"
+#include "lib/stream.h"
+
+#include "bgpd/bgpd.h"
+#include "bgpd/bgp_ecommunity.h"
+#include "bgpd/bgp_attr.h"
+
+#include "bgpd/rfapi/bgp_rfapi_cfg.h"
+#include "bgpd/rfapi/rfapi.h"
+#include "bgpd/rfapi/rfapi_backend.h"
+
+#include "bgpd/bgp_route.h"
+#include "bgpd/bgp_mplsvpn.h"
+#include "bgpd/bgp_aspath.h"
+#include "bgpd/bgp_advertise.h"
+
+#include "bgpd/rfapi/rfapi_import.h"
+#include "bgpd/rfapi/rfapi_private.h"
+#include "bgpd/rfapi/rfapi_monitor.h"
+#include "bgpd/rfapi/rfapi_vty.h"
+#include "bgpd/rfapi/vnc_export_bgp.h"
+#include "bgpd/rfapi/vnc_export_bgp_p.h"
+#include "bgpd/rfapi/vnc_zebra.h"
+#include "bgpd/rfapi/vnc_import_bgp.h"
+#include "bgpd/rfapi/rfapi_rib.h"
+
+#include "bgpd/rfapi/rfapi_ap.h"
+#include "bgpd/rfapi/vnc_debug.h"
+
+/*
+ * Per-NVE Advertised prefixes
+ *
+ * We maintain a list of prefixes advertised by each NVE.
+ * There are two indices: by prefix and by lifetime.
+ *
+ * BY-PREFIX skiplist
+ *
+ * key: ptr to struct prefix (when storing, point to prefix that
+ * is part of rfapi_adb).
+ *
+ * value: ptr to struct rfapi_adb
+ *
+ * BY-LIFETIME skiplist
+ *
+ * key: ptr to struct rfapi_adb
+ * value: ptr to struct rfapi_adb
+ *
+ */
+
+/*
+ * Skiplist sort function that sorts first according to lifetime
+ * and then according to adb pointer value. The adb pointer
+ * is used to spread out the sort for adbs with the same lifetime
+ * and thereby make the skip list operations more efficient.
+ */
+static int sl_adb_lifetime_cmp(const void *adb1, const void *adb2)
+{
+ const struct rfapi_adb *a1 = adb1;
+ const struct rfapi_adb *a2 = adb2;
+
+ if (a1->lifetime < a2->lifetime)
+ return -1;
+ if (a1->lifetime > a2->lifetime)
+ return 1;
+
+ if (a1 < a2)
+ return -1;
+ if (a1 > a2)
+ return 1;
+
+ return 0;
+}
+
+void rfapiApInit(struct rfapi_advertised_prefixes *ap)
+{
+ ap->ipN_by_prefix = skiplist_new(0, rfapi_rib_key_cmp, NULL);
+ ap->ip0_by_ether = skiplist_new(0, rfapi_rib_key_cmp, NULL);
+ ap->by_lifetime = skiplist_new(0, sl_adb_lifetime_cmp, NULL);
+}
+
+void rfapiApRelease(struct rfapi_advertised_prefixes *ap)
+{
+ struct rfapi_adb *adb;
+
+ /* Free ADBs and lifetime items */
+ while (0 == skiplist_first(ap->by_lifetime, NULL, (void **)&adb)) {
+ rfapiAdbFree(adb);
+ skiplist_delete_first(ap->by_lifetime);
+ }
+
+ while (0 == skiplist_delete_first(ap->ipN_by_prefix))
+ ;
+ while (0 == skiplist_delete_first(ap->ip0_by_ether))
+ ;
+
+ /* Free lists */
+ skiplist_free(ap->ipN_by_prefix);
+ skiplist_free(ap->ip0_by_ether);
+ skiplist_free(ap->by_lifetime);
+
+ ap->ipN_by_prefix = NULL;
+ ap->ip0_by_ether = NULL;
+ ap->by_lifetime = NULL;
+}
+
+int rfapiApCount(struct rfapi_descriptor *rfd)
+{
+ if (!rfd->advertised.by_lifetime)
+ return 0;
+
+ return skiplist_count(rfd->advertised.by_lifetime);
+}
+
+int rfapiApCountAll(struct bgp *bgp)
+{
+ struct rfapi *h;
+ struct listnode *node;
+ struct rfapi_descriptor *rfd;
+ int total = 0;
+
+ h = bgp->rfapi;
+ if (h) {
+ for (ALL_LIST_ELEMENTS_RO(&h->descriptors, node, rfd)) {
+ total += rfapiApCount(rfd);
+ }
+ }
+ return total;
+}
+
+
+void rfapiApReadvertiseAll(struct bgp *bgp, struct rfapi_descriptor *rfd)
+{
+ struct rfapi_adb *adb;
+ void *cursor = NULL;
+ int rc;
+
+ for (rc = skiplist_next(rfd->advertised.by_lifetime, NULL,
+ (void **)&adb, &cursor);
+ rc == 0; rc = skiplist_next(rfd->advertised.by_lifetime, NULL,
+ (void **)&adb, &cursor)) {
+
+ struct prefix_rd prd;
+ uint32_t local_pref = rfp_cost_to_localpref(adb->cost);
+
+ prd = rfd->rd;
+ prd.family = AF_UNSPEC;
+ prd.prefixlen = 64;
+
+ /*
+ * TBD this is not quite right. When pfx_ip is 0/32 or 0/128,
+ * we need to substitute the VN address as the prefix
+ */
+ add_vnc_route(rfd, bgp, SAFI_MPLS_VPN, &adb->u.s.prefix_ip,
+ &prd, /* RD to use (0 for ENCAP) */
+ &rfd->vn_addr, /* nexthop */
+ &local_pref, &adb->lifetime, NULL,
+ NULL, /* struct rfapi_un_option */
+ NULL, /* struct rfapi_vn_option */
+ rfd->rt_export_list, NULL, /* med */
+ NULL, ZEBRA_ROUTE_BGP, BGP_ROUTE_RFP, 0);
+ }
+}
+
+void rfapiApWithdrawAll(struct bgp *bgp, struct rfapi_descriptor *rfd)
+{
+ struct rfapi_adb *adb;
+ void *cursor;
+ int rc;
+
+
+ cursor = NULL;
+ for (rc = skiplist_next(rfd->advertised.by_lifetime, NULL,
+ (void **)&adb, &cursor);
+ rc == 0; rc = skiplist_next(rfd->advertised.by_lifetime, NULL,
+ (void **)&adb, &cursor)) {
+
+ struct prefix pfx_vn_buf;
+ struct prefix *pfx_ip;
+
+ if (!(RFAPI_0_PREFIX(&adb->u.s.prefix_ip)
+ && RFAPI_HOST_PREFIX(&adb->u.s.prefix_ip))) {
+
+ pfx_ip = &adb->u.s.prefix_ip;
+
+ } else {
+
+ pfx_ip = NULL;
+
+ /*
+ * 0/32 or 0/128 => mac advertisement
+ */
+ if (rfapiRaddr2Qprefix(&rfd->vn_addr, &pfx_vn_buf)) {
+ /*
+ * Bad: it means we can't delete the route
+ */
+ vnc_zlog_debug_verbose(
+ "%s: BAD: handle has bad vn_addr: skipping",
+ __func__);
+ continue;
+ }
+ }
+
+ del_vnc_route(rfd, rfd->peer, bgp, SAFI_MPLS_VPN,
+ pfx_ip ? pfx_ip : &pfx_vn_buf,
+ &adb->u.s.prd, /* RD to use (0 for ENCAP) */
+ ZEBRA_ROUTE_BGP, BGP_ROUTE_RFP, NULL, 0);
+ }
+}
+
+/*
+ * returns nonzero if tunnel readvertisement is needed, 0 otherwise
+ */
+static int rfapiApAdjustLifetimeStats(
+ struct rfapi_descriptor *rfd,
+ uint32_t *old_lifetime, /* set if removing/replacing */
+ uint32_t *new_lifetime) /* set if replacing/adding */
+{
+ int advertise = 0;
+ int find_max = 0;
+ int find_min = 0;
+
+ vnc_zlog_debug_verbose("%s: rfd=%p, pOldLife=%p, pNewLife=%p", __func__,
+ rfd, old_lifetime, new_lifetime);
+ if (old_lifetime)
+ vnc_zlog_debug_verbose("%s: OldLife=%d", __func__,
+ *old_lifetime);
+ if (new_lifetime)
+ vnc_zlog_debug_verbose("%s: NewLife=%d", __func__,
+ *new_lifetime);
+
+ if (new_lifetime) {
+ /*
+ * Adding new lifetime
+ */
+ if (old_lifetime) {
+ /*
+ * replacing existing lifetime
+ */
+
+
+ /* old and new are same */
+ if (*old_lifetime == *new_lifetime)
+ return 0;
+
+ if (*old_lifetime == rfd->min_prefix_lifetime) {
+ find_min = 1;
+ }
+ if (*old_lifetime == rfd->max_prefix_lifetime) {
+ find_max = 1;
+ }
+
+ /* no need to search if new value is at or equals
+ * min|max */
+ if (*new_lifetime <= rfd->min_prefix_lifetime) {
+ rfd->min_prefix_lifetime = *new_lifetime;
+ find_min = 0;
+ }
+ if (*new_lifetime >= rfd->max_prefix_lifetime) {
+ rfd->max_prefix_lifetime = *new_lifetime;
+ advertise = 1;
+ find_max = 0;
+ }
+
+ } else {
+ /*
+ * Just adding new lifetime
+ */
+ if (*new_lifetime < rfd->min_prefix_lifetime) {
+ rfd->min_prefix_lifetime = *new_lifetime;
+ }
+ if (*new_lifetime > rfd->max_prefix_lifetime) {
+ advertise = 1;
+ rfd->max_prefix_lifetime = *new_lifetime;
+ }
+ }
+ } else {
+ /*
+ * Deleting
+ */
+
+ /*
+ * See if the max prefix lifetime for this NVE has decreased.
+ * The easy optimization: track min & max; walk the table only
+ * if they are different.
+ * The general optimization: index the advertised_prefixes
+ * table by lifetime.
+ *
+ * Note: for a given nve_descriptor, only one of the
+ * advertised_prefixes[] tables will be used: viz., the
+ * address family that matches the VN address.
+ *
+ */
+ if (rfd->max_prefix_lifetime == rfd->min_prefix_lifetime) {
+
+ /*
+ * Common case: all lifetimes are the same. Only
+ * thing we need to do here is check if there are
+ * no exported routes left. In that case, reinitialize
+ * the max and min values.
+ */
+ if (!rfapiApCount(rfd)) {
+ rfd->max_prefix_lifetime = 0;
+ rfd->min_prefix_lifetime = UINT32_MAX;
+ }
+
+
+ } else {
+ if (old_lifetime) {
+ if (*old_lifetime == rfd->min_prefix_lifetime) {
+ find_min = 1;
+ }
+ if (*old_lifetime == rfd->max_prefix_lifetime) {
+ find_max = 1;
+ }
+ }
+ }
+ }
+
+ if (find_min || find_max) {
+ uint32_t min = UINT32_MAX;
+ uint32_t max = 0;
+
+ struct rfapi_adb *adb_min;
+ struct rfapi_adb *adb_max;
+
+ if (!skiplist_first(rfd->advertised.by_lifetime,
+ (void **)&adb_min, NULL)
+ && !skiplist_last(rfd->advertised.by_lifetime,
+ (void **)&adb_max, NULL)) {
+
+ /*
+ * This should always work
+ */
+ min = adb_min->lifetime;
+ max = adb_max->lifetime;
+
+ } else {
+
+ void *cursor;
+ struct rfapi_rib_key rk;
+ struct rfapi_adb *adb;
+ int rc;
+
+ vnc_zlog_debug_verbose(
+ "%s: walking to find new min/max", __func__);
+
+ cursor = NULL;
+ for (rc = skiplist_next(rfd->advertised.ipN_by_prefix,
+ (void **)&rk, (void **)&adb,
+ &cursor);
+ !rc;
+ rc = skiplist_next(rfd->advertised.ipN_by_prefix,
+ (void **)&rk, (void **)&adb,
+ &cursor)) {
+
+ uint32_t lt = adb->lifetime;
+
+ if (lt > max)
+ max = lt;
+ if (lt < min)
+ min = lt;
+ }
+ cursor = NULL;
+ for (rc = skiplist_next(rfd->advertised.ip0_by_ether,
+ (void **)&rk, (void **)&adb,
+ &cursor);
+ !rc;
+ rc = skiplist_next(rfd->advertised.ip0_by_ether,
+ (void **)&rk, (void **)&adb,
+ &cursor)) {
+
+ uint32_t lt = adb->lifetime;
+
+ if (lt > max)
+ max = lt;
+ if (lt < min)
+ min = lt;
+ }
+ }
+
+ /*
+ * trigger tunnel route update
+ * but only if we found a VPN route and it had
+ * a lifetime greater than 0
+ */
+ if (max && rfd->max_prefix_lifetime != max)
+ advertise = 1;
+ rfd->max_prefix_lifetime = max;
+ rfd->min_prefix_lifetime = min;
+ }
+
+ vnc_zlog_debug_verbose("%s: returning advertise=%d, min=%d, max=%d",
+ __func__, advertise, rfd->min_prefix_lifetime,
+ rfd->max_prefix_lifetime);
+
+ return (advertise != 0);
+}
+
+/*
+ * Return Value
+ *
+ * 0 No need to advertise tunnel route
+ * non-0 advertise tunnel route
+ */
+int rfapiApAdd(struct bgp *bgp, struct rfapi_descriptor *rfd,
+ struct prefix *pfx_ip, struct prefix *pfx_eth,
+ struct prefix_rd *prd, uint32_t lifetime, uint8_t cost,
+ struct rfapi_l2address_option *l2o) /* other options TBD */
+{
+ int rc;
+ struct rfapi_adb *adb;
+ uint32_t old_lifetime = 0;
+ int use_ip0 = 0;
+ struct rfapi_rib_key rk;
+
+ rfapi_rib_key_init(pfx_ip, prd, pfx_eth, &rk);
+ if (RFAPI_0_PREFIX(pfx_ip) && RFAPI_HOST_PREFIX(pfx_ip)) {
+ use_ip0 = 1;
+ assert(pfx_eth);
+ rc = skiplist_search(rfd->advertised.ip0_by_ether, &rk,
+ (void **)&adb);
+
+ } else {
+
+ /* find prefix in advertised prefixes list */
+ rc = skiplist_search(rfd->advertised.ipN_by_prefix, &rk,
+ (void **)&adb);
+ }
+
+
+ if (rc) {
+ /* Not found */
+ adb = XCALLOC(MTYPE_RFAPI_ADB, sizeof(struct rfapi_adb));
+ adb->lifetime = lifetime;
+ adb->u.key = rk;
+
+ if (use_ip0) {
+ assert(pfx_eth);
+ skiplist_insert(rfd->advertised.ip0_by_ether,
+ &adb->u.key, adb);
+ } else {
+ skiplist_insert(rfd->advertised.ipN_by_prefix,
+ &adb->u.key, adb);
+ }
+
+ skiplist_insert(rfd->advertised.by_lifetime, adb, adb);
+ } else {
+ old_lifetime = adb->lifetime;
+ if (old_lifetime != lifetime) {
+ assert(!skiplist_delete(rfd->advertised.by_lifetime,
+ adb, NULL));
+ adb->lifetime = lifetime;
+ assert(!skiplist_insert(rfd->advertised.by_lifetime,
+ adb, adb));
+ }
+ }
+ adb->cost = cost;
+ if (l2o)
+ adb->l2o = *l2o;
+ else
+ memset(&adb->l2o, 0, sizeof(struct rfapi_l2address_option));
+
+ if (rfapiApAdjustLifetimeStats(rfd, (rc ? NULL : &old_lifetime),
+ &lifetime))
+ return 1;
+
+ return 0;
+}
+
+/*
+ * After this function returns successfully, caller should call
+ * rfapiAdjustLifetimeStats() and possibly rfapiTunnelRouteAnnounce()
+ */
+int rfapiApDelete(struct bgp *bgp, struct rfapi_descriptor *rfd,
+ struct prefix *pfx_ip, struct prefix *pfx_eth,
+ struct prefix_rd *prd, int *advertise_tunnel) /* out */
+{
+ int rc;
+ struct rfapi_adb *adb;
+ uint32_t old_lifetime;
+ int use_ip0 = 0;
+ struct rfapi_rib_key rk;
+
+ if (advertise_tunnel)
+ *advertise_tunnel = 0;
+
+ rfapi_rib_key_init(pfx_ip, prd, pfx_eth, &rk);
+ /* find prefix in advertised prefixes list */
+ if (RFAPI_0_PREFIX(pfx_ip) && RFAPI_HOST_PREFIX(pfx_ip)) {
+ use_ip0 = 1;
+ assert(pfx_eth);
+
+ rc = skiplist_search(rfd->advertised.ip0_by_ether, &rk,
+ (void **)&adb);
+
+ } else {
+
+ /* find prefix in advertised prefixes list */
+ rc = skiplist_search(rfd->advertised.ipN_by_prefix, &rk,
+ (void **)&adb);
+ }
+
+ if (rc) {
+ return ENOENT;
+ }
+
+ old_lifetime = adb->lifetime;
+
+ if (use_ip0) {
+ rc = skiplist_delete(rfd->advertised.ip0_by_ether, &rk, NULL);
+ } else {
+ rc = skiplist_delete(rfd->advertised.ipN_by_prefix, &rk, NULL);
+ }
+ assert(!rc);
+
+ rc = skiplist_delete(rfd->advertised.by_lifetime, adb, NULL);
+ assert(!rc);
+
+ rfapiAdbFree(adb);
+
+ if (rfapiApAdjustLifetimeStats(rfd, &old_lifetime, NULL)) {
+ if (advertise_tunnel)
+ *advertise_tunnel = 1;
+ }
+
+ return 0;
+}