diff options
Diffstat (limited to 'bgpd/rfapi/rfapi_ap.c')
-rw-r--r-- | bgpd/rfapi/rfapi_ap.c | 629 |
1 files changed, 629 insertions, 0 deletions
diff --git a/bgpd/rfapi/rfapi_ap.c b/bgpd/rfapi/rfapi_ap.c new file mode 100644 index 000000000..94b6dea8d --- /dev/null +++ b/bgpd/rfapi/rfapi_ap.c @@ -0,0 +1,629 @@ +/* + * + * Copyright 2009-2016, LabN Consulting, L.L.C. + * + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. + * + */ + +#include <errno.h> + +#include "zebra.h" +#include "prefix.h" +#include "table.h" +#include "vty.h" +#include "memory.h" +#include "routemap.h" +#include "log.h" +#include "linklist.h" +#include "command.h" +#include "stream.h" + +#include "bgpd.h" +#include "bgp_ecommunity.h" +#include "bgp_attr.h" +#include "bgp_mplsvpn.h" + +#include "bgp_rfapi_cfg.h" +#include "rfapi.h" +#include "rfapi_backend.h" + +#include "bgp_route.h" +#include "bgp_aspath.h" +#include "bgp_advertise.h" + +#include "rfapi_import.h" +#include "rfapi_private.h" +#include "rfapi_monitor.h" +#include "rfapi_vty.h" +#include "vnc_export_bgp.h" +#include "vnc_export_bgp_p.h" +#include "vnc_zebra.h" +#include "vnc_import_bgp.h" +#include "rfapi_rib.h" + +#include "rfapi_ap.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 (void *adb1, void *adb2) +{ + struct rfapi_adb *a1 = adb1; + 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, vnc_prefix_cmp, NULL); + ap->ip0_by_ether = skiplist_new (0, vnc_prefix_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; + 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->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->prefix_ip) && + RFAPI_HOST_PREFIX (&adb->prefix_ip))) + { + + pfx_ip = &adb->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 + */ + zlog_debug ("%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->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; + + zlog_debug ("%s: rfd=%p, pOldLife=%p, pNewLife=%p", + __func__, rfd, old_lifetime, new_lifetime); + if (old_lifetime) + zlog_debug ("%s: OldLife=%d", __func__, *old_lifetime); + if (new_lifetime) + zlog_debug ("%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 prefix *prefix; + struct rfapi_adb *adb; + int rc; + + zlog_debug ("%s: walking to find new min/max", __func__); + + cursor = NULL; + for (rc = skiplist_next (rfd->advertised.ipN_by_prefix, + (void **) &prefix, (void **) &adb, + &cursor); !rc; + rc = + skiplist_next (rfd->advertised.ipN_by_prefix, + (void **) &prefix, (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 **) &prefix, (void **) &adb, + &cursor); !rc; + rc = + skiplist_next (rfd->advertised.ip0_by_ether, (void **) &prefix, + (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; + } + + zlog_debug ("%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; + + 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, pfx_eth, + (void **) &adb); + + } + else + { + + /* find prefix in advertised prefixes list */ + rc = + skiplist_search (rfd->advertised.ipN_by_prefix, pfx_ip, + (void **) &adb); + } + + + if (rc) + { + /* Not found */ + adb = XCALLOC (MTYPE_RFAPI_ADB, sizeof (struct rfapi_adb)); + assert (adb); + adb->lifetime = lifetime; + adb->prefix_ip = *pfx_ip; + if (pfx_eth) + adb->prefix_eth = *pfx_eth; + + if (use_ip0) + { + assert (pfx_eth); + skiplist_insert (rfd->advertised.ip0_by_ether, &adb->prefix_eth, + adb); + } + else + { + skiplist_insert (rfd->advertised.ipN_by_prefix, &adb->prefix_ip, + 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)); + } + + if (!use_ip0 && pfx_eth && prefix_cmp (&adb->prefix_eth, pfx_eth)) + { + /* mac address changed */ + adb->prefix_eth = *pfx_eth; + } + } + adb->cost = cost; + if (l2o) + adb->l2o = *l2o; + else + memset (&adb->l2o, 0, sizeof (struct rfapi_l2address_option)); + adb->prd = *prd; + + 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, + int *advertise_tunnel) /* out */ +{ + int rc; + struct rfapi_adb *adb; + uint32_t old_lifetime; + int use_ip0 = 0; + + if (advertise_tunnel) + *advertise_tunnel = 0; + + /* 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, pfx_eth, + (void **) &adb); + + } + else + { + + /* find prefix in advertised prefixes list */ + rc = + skiplist_search (rfd->advertised.ipN_by_prefix, pfx_ip, + (void **) &adb); + } + + if (rc) + { + return ENOENT; + } + + old_lifetime = adb->lifetime; + + if (use_ip0) + { + rc = skiplist_delete (rfd->advertised.ip0_by_ether, pfx_eth, NULL); + } + else + { + rc = skiplist_delete (rfd->advertised.ipN_by_prefix, pfx_ip, 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; +} |