diff options
author | Daniel Baumann <daniel.baumann@progress-linux.org> | 2024-04-28 09:53:30 +0000 |
---|---|---|
committer | Daniel Baumann <daniel.baumann@progress-linux.org> | 2024-04-28 09:53:30 +0000 |
commit | 2c7cac91ed6e7db0f6937923d2b57f97dbdbc337 (patch) | |
tree | c05dc0f8e6aa3accc84e3e5cffc933ed94941383 /bgpd/rfapi/vnc_import_bgp.c | |
parent | Initial commit. (diff) | |
download | frr-upstream.tar.xz frr-upstream.zip |
Adding upstream version 8.4.4.upstream/8.4.4upstream
Signed-off-by: Daniel Baumann <daniel.baumann@progress-linux.org>
Diffstat (limited to 'bgpd/rfapi/vnc_import_bgp.c')
-rw-r--r-- | bgpd/rfapi/vnc_import_bgp.c | 2907 |
1 files changed, 2907 insertions, 0 deletions
diff --git a/bgpd/rfapi/vnc_import_bgp.c b/bgpd/rfapi/vnc_import_bgp.c new file mode 100644 index 0000000..acbc413 --- /dev/null +++ b/bgpd/rfapi/vnc_import_bgp.c @@ -0,0 +1,2907 @@ +/* + * + * Copyright 2009-2016, LabN Consulting, L.L.C. + * + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License along + * with this program; see the file COPYING; if not, write to the Free Software + * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA + */ + +/* + * File: vnc_import_bgp.c + * Purpose: Import routes from BGP unicast directly (not via zebra) + */ + +#include "lib/zebra.h" +#include "lib/prefix.h" +#include "lib/agg_table.h" +#include "lib/vty.h" +#include "lib/log.h" +#include "lib/memory.h" +#include "lib/linklist.h" +#include "lib/plist.h" +#include "lib/routemap.h" +#include "lib/lib_errors.h" + +#include "bgpd/bgpd.h" +#include "bgpd/bgp_ecommunity.h" +#include "bgpd/bgp_attr.h" +#include "bgpd/bgp_route.h" +#include "bgpd/bgp_mplsvpn.h" /* for RD_TYPE_IP */ + +#include "bgpd/rfapi/vnc_export_bgp.h" +#include "bgpd/rfapi/bgp_rfapi_cfg.h" +#include "bgpd/rfapi/rfapi.h" +#include "bgpd/rfapi/rfapi_import.h" +#include "bgpd/rfapi/rfapi_private.h" +#include "bgpd/rfapi/rfapi_monitor.h" +#include "bgpd/rfapi/rfapi_vty.h" +#include "bgpd/rfapi/vnc_import_bgp.h" +#include "bgpd/rfapi/vnc_import_bgp_p.h" +#include "bgpd/rfapi/vnc_debug.h" + +#define ENABLE_VNC_RHNCK + +#define DEBUG_RHN_LIST 0 + +static struct rfapi_descriptor vncHDBgpDirect; /* dummy nve descriptor */ +static struct rfapi_descriptor vncHDResolveNve; /* dummy nve descriptor */ + +/* + * For routes from another AS: + * + * If MED is set, + * LOCAL_PREF = 255 - MIN(255, MED) + * else + * LOCAL_PREF = default_local_pref + * + * For routes from the same AS: + * + * LOCAL_PREF unchanged + */ +uint32_t calc_local_pref(struct attr *attr, struct peer *peer) +{ + uint32_t local_pref = 0; + + if (!attr) { + if (peer) { + return peer->bgp->default_local_pref; + } + return bgp_get_default()->default_local_pref; + } + + if (peer && (peer->as != peer->bgp->as)) { + if (attr->flag & ATTR_FLAG_BIT(BGP_ATTR_MULTI_EXIT_DISC)) { + if (attr->med > 255) { + local_pref = 0; + } else { + local_pref = 255 - attr->med; + } + } else { + local_pref = peer->bgp->default_local_pref; + } + } else { + if (attr->flag & ATTR_FLAG_BIT(BGP_ATTR_LOCAL_PREF)) { + local_pref = attr->local_pref; + } else { + if (peer && peer->bgp) { + local_pref = peer->bgp->default_local_pref; + } + } + } + + return local_pref; +} + +static int is_host_prefix(const struct prefix *p) +{ + switch (p->family) { + case AF_INET: + return (p->prefixlen == IPV4_MAX_BITLEN); + case AF_INET6: + return (p->prefixlen == IPV6_MAX_BITLEN); + } + return 0; +} + +/*********************************************************************** + * RHN list + ***********************************************************************/ + +struct prefix_bag { + struct prefix hpfx; /* ce address = unicast nexthop */ + struct prefix upfx; /* unicast prefix */ + struct bgp_path_info *ubpi; /* unicast route */ +}; + +static const uint8_t maskbit[] = {0x00, 0x80, 0xc0, 0xe0, 0xf0, + 0xf8, 0xfc, 0xfe, 0xff}; + +int vnc_prefix_cmp(const void *pfx1, const void *pfx2) +{ + int offset; + int shift; + uint8_t mask; + + const struct prefix *p1 = pfx1; + const struct prefix *p2 = pfx2; + + if (p1->family < p2->family) + return -1; + if (p1->family > p2->family) + return 1; + + if (p1->prefixlen < p2->prefixlen) + return -1; + if (p1->prefixlen > p2->prefixlen) + return 1; + + offset = p1->prefixlen / 8; + shift = p1->prefixlen % 8; + if (shift == 0 && offset) { /* catch aligned case */ + offset--; + shift = 8; + } + + /* Set both prefix's head pointer. */ + const uint8_t *pp1 = (const uint8_t *)&p1->u.prefix; + const uint8_t *pp2 = (const uint8_t *)&p2->u.prefix; + + while (offset--) { + if (*pp1 < *pp2) + return -1; + if (*pp1 > *pp2) + return 1; + ++pp1; + ++pp2; + } + + mask = maskbit[shift]; + if ((*pp1 & mask) < (*pp2 & mask)) + return -1; + if ((*pp1 & mask) > (*pp2 & mask)) + return 1; + + return 0; +} + +static void prefix_bag_free(void *pb) +{ + XFREE(MTYPE_RFAPI_PREFIX_BAG, pb); +} + +#if DEBUG_RHN_LIST +static void print_rhn_list(const char *tag1, const char *tag2) +{ + struct bgp *bgp; + struct skiplist *sl; + struct skiplistnode *p; + struct prefix_bag *pb; + int count = 0; + + bgp = bgp_get_default(); + if (!bgp) + return; + + sl = bgp->frapi->resolve_nve_nexthop; + if (!sl) { + vnc_zlog_debug_verbose("%s: %s: RHN List is empty", + (tag1 ? tag1 : ""), (tag2 ? tag2 : "")); + return; + } + + vnc_zlog_debug_verbose("%s: %s: RHN list:", (tag1 ? tag1 : ""), + (tag2 ? tag2 : "")); + + /* XXX uses secret knowledge of skiplist structure */ + for (p = sl->header->forward[0]; p; p = p->forward[0]) { + pb = p->value; + + vnc_zlog_debug_verbose( + "RHN Entry %d (q=%p): kpfx=%pFX, upfx=%pFX, hpfx=%pFX, ubpi=%p", + ++count, p, p->key, &pb->upfx, &pb->hpfx, pb->ubpi); + } +} +#endif + +#ifdef ENABLE_VNC_RHNCK +static void vnc_rhnck(char *tag) +{ + struct bgp *bgp; + struct skiplist *sl; + struct skiplistnode *p; + + bgp = bgp_get_default(); + if (!bgp) + return; + sl = bgp->rfapi->resolve_nve_nexthop; + + if (!sl) + return; + + /* XXX uses secret knowledge of skiplist structure */ + for (p = sl->header->forward[0]; p; p = p->forward[0]) { + struct prefix_bag *pb; + struct prefix *pkey; + afi_t afi; + struct prefix pfx_orig_nexthop; + + memset(&pfx_orig_nexthop, 0, + sizeof(pfx_orig_nexthop)); /* keep valgrind happy */ + + pkey = p->key; + pb = p->value; + + afi = family2afi(pb->upfx.family); + + rfapiUnicastNexthop2Prefix(afi, pb->ubpi->attr, + &pfx_orig_nexthop); + + /* pb->hpfx, pb->ubpi nexthop, pkey should all reflect the same + * pfx */ + assert(!vnc_prefix_cmp(&pb->hpfx, pkey)); + if (vnc_prefix_cmp(&pb->hpfx, &pfx_orig_nexthop)) { + vnc_zlog_debug_verbose( + "%s: %s: FATAL: resolve_nve_nexthop list item bpi nexthop %pFX != nve pfx %pFX", + __func__, tag, &pfx_orig_nexthop, &pb->hpfx); + assert(0); + } + } + vnc_zlog_debug_verbose("%s: vnc_rhnck OK", tag); +} + +#define VNC_RHNCK(n) \ + do { \ + char buf[BUFSIZ]; \ + snprintf(buf, sizeof(buf), "%s: %s", __func__, #n); \ + vnc_rhnck(buf); \ + } while (0) + +#else + +#define VNC_RHNCK(n) + +#endif + +/*********************************************************************** + * Add/Delete Unicast Route + ***********************************************************************/ + +/* + * "Adding a Route" import process + */ + +/* + * extract and package information from the BGP unicast route. + * Return code 0 means OK, non-0 means drop. + * + * If return code is 0, caller MUST release ecom + */ +static int process_unicast_route(struct bgp *bgp, /* in */ + afi_t afi, /* in */ + const struct prefix *prefix, /* in */ + struct bgp_path_info *info, /* in */ + struct ecommunity **ecom, /* OUT */ + struct prefix *unicast_nexthop) /* OUT */ +{ + struct rfapi_cfg *hc = bgp->rfapi_cfg; + struct peer *peer = info->peer; + struct attr *attr = info->attr; + struct attr hattr; + struct route_map *rmap = NULL; + struct prefix pfx_orig_nexthop; + + memset(&pfx_orig_nexthop, 0, + sizeof(pfx_orig_nexthop)); /* keep valgrind happy */ + + /* + * prefix list check + */ + if (hc->plist_redist[ZEBRA_ROUTE_BGP_DIRECT][afi]) { + vnc_zlog_debug_verbose("%s: HC prefix list is set, checking", + __func__); + if (prefix_list_apply( + hc->plist_redist[ZEBRA_ROUTE_BGP_DIRECT][afi], + prefix) + == PREFIX_DENY) { + vnc_zlog_debug_verbose( + "%s: prefix list returns DENY, blocking route", + __func__); + return -1; + } + vnc_zlog_debug_verbose( + "%s: prefix list returns PASS, allowing route", + __func__); + } + + /* apply routemap, if any, later */ + rmap = hc->routemap_redist[ZEBRA_ROUTE_BGP_DIRECT]; + + /* + * Extract original nexthop, which we expect to be a NVE connected + * router + * Note that this is the nexthop before any possible application of + * policy + */ + /* + * Incoming prefix is unicast. If v6, it is in multiprotocol area, + * but if v4 it is in attr->nexthop + */ + rfapiUnicastNexthop2Prefix(afi, attr, &pfx_orig_nexthop); + + /* + * route map handling + * This code is here because it allocates an interned attr which + * must be freed before we return. It's easier to put it after + * all of the possible returns above. + */ + memset(&hattr, 0, sizeof(hattr)); + /* hattr becomes a ghost attr */ + hattr = *attr; + + if (rmap) { + struct bgp_path_info info; + route_map_result_t ret; + + memset(&info, 0, sizeof(info)); + info.peer = peer; + info.attr = &hattr; + ret = route_map_apply(rmap, prefix, &info); + if (ret == RMAP_DENYMATCH) { + bgp_attr_flush(&hattr); + vnc_zlog_debug_verbose( + "%s: route map \"%s\" says DENY, returning", + __func__, rmap->name); + return -1; + } + } + + /* + * Get the (possibly altered by policy) unicast nexthop + * for later lookup in the Import Table by caller + */ + rfapiUnicastNexthop2Prefix(afi, &hattr, unicast_nexthop); + + if (bgp_attr_get_ecommunity(&hattr)) + *ecom = ecommunity_dup(bgp_attr_get_ecommunity(&hattr)); + else + *ecom = ecommunity_new(); + + /* + * Done with hattr, clean up + */ + bgp_attr_flush(&hattr); + + /* + * Add EC that carries original NH of iBGP route (2 bytes = magic + * value indicating it came from an VNC gateway; default 5226, but + * must be user configurable). Note that this is the nexthop before + * any application of policy. + */ + { + struct ecommunity_val vnc_gateway_magic; + uint16_t localadmin; + + /* Using route origin extended community type */ + memset(&vnc_gateway_magic, 0, sizeof(vnc_gateway_magic)); + vnc_gateway_magic.val[0] = 0x01; + vnc_gateway_magic.val[1] = 0x03; + + /* Only works for IPv4 nexthops */ + if (prefix->family == AF_INET) { + memcpy(vnc_gateway_magic.val + 2, + &unicast_nexthop->u.prefix4, 4); + } + localadmin = htons(hc->resolve_nve_roo_local_admin); + memcpy(vnc_gateway_magic.val + 6, (char *)&localadmin, 2); + + ecommunity_add_val(*ecom, &vnc_gateway_magic, false, false); + } + + return 0; +} + + +static void vnc_import_bgp_add_route_mode_resolve_nve_one_bi( + struct bgp *bgp, afi_t afi, struct bgp_path_info *bpi, /* VPN bpi */ + struct prefix_rd *prd, /* RD */ + const struct prefix *prefix, /* unicast route prefix */ + uint32_t *local_pref, /* NULL = no local_pref */ + uint32_t *med, /* NULL = no med */ + struct ecommunity *ecom) /* generated ecoms */ +{ + struct prefix un; + struct prefix nexthop; + struct rfapi_ip_addr nexthop_h; + uint32_t lifetime; + uint32_t *plifetime; + struct bgp_attr_encap_subtlv *encaptlvs; + uint32_t label = 0; + + struct rfapi_un_option optary[3]; + struct rfapi_un_option *opt = NULL; + int cur_opt = 0; + + vnc_zlog_debug_verbose("%s: entry", __func__); + + if (bpi->type != ZEBRA_ROUTE_BGP + && bpi->type != ZEBRA_ROUTE_BGP_DIRECT) { + + return; + } + if (bpi->sub_type != BGP_ROUTE_NORMAL + && bpi->sub_type != BGP_ROUTE_STATIC + && bpi->sub_type != BGP_ROUTE_RFP) { + + return; + } + if (CHECK_FLAG(bpi->flags, BGP_PATH_REMOVED)) + return; + + vncHDResolveNve.peer = bpi->peer; + if (!rfapiGetVncTunnelUnAddr(bpi->attr, &un)) { + if (rfapiQprefix2Raddr(&un, &vncHDResolveNve.un_addr)) + return; + } else { + memset(&vncHDResolveNve.un_addr, 0, + sizeof(vncHDResolveNve.un_addr)); + } + + /* Use nexthop of VPN route as nexthop of constructed route */ + rfapiNexthop2Prefix(bpi->attr, &nexthop); + rfapiQprefix2Raddr(&nexthop, &nexthop_h); + + if (rfapiGetVncLifetime(bpi->attr, &lifetime)) { + plifetime = NULL; + } else { + plifetime = &lifetime; + } + + encaptlvs = bgp_attr_get_vnc_subtlvs(bpi->attr); + if (bpi->attr->encap_tunneltype != BGP_ENCAP_TYPE_RESERVED + && bpi->attr->encap_tunneltype != BGP_ENCAP_TYPE_MPLS) { + opt = &optary[cur_opt++]; + memset(opt, 0, sizeof(struct rfapi_un_option)); + opt->type = RFAPI_UN_OPTION_TYPE_TUNNELTYPE; + opt->v.tunnel.type = bpi->attr->encap_tunneltype; + /* TBD parse bpi->attr->extra->encap_subtlvs */ + } + + struct ecommunity *new_ecom = ecommunity_dup(ecom); + + if (bgp_attr_get_ecommunity(bpi->attr)) + ecommunity_merge(new_ecom, bgp_attr_get_ecommunity(bpi->attr)); + + if (bpi->extra) + label = decode_label(&bpi->extra->label[0]); + + add_vnc_route(&vncHDResolveNve, bgp, SAFI_MPLS_VPN, + prefix, /* unicast route prefix */ + prd, &nexthop_h, /* new nexthop */ + local_pref, plifetime, + (struct bgp_tea_options *)encaptlvs, /* RFP options */ + opt, NULL, new_ecom, med, /* NULL => don't set med */ + (label ? &label : NULL), /* NULL= default */ + ZEBRA_ROUTE_BGP_DIRECT, BGP_ROUTE_REDISTRIBUTE, + RFAPI_AHR_RFPOPT_IS_VNCTLV); /* flags */ + + ecommunity_free(&new_ecom); +} + +static void vnc_import_bgp_add_route_mode_resolve_nve_one_rd( + struct prefix_rd *prd, /* RD */ + struct bgp_table *table_rd, /* per-rd VPN route table */ + afi_t afi, struct bgp *bgp, + const struct prefix *prefix, /* unicast prefix */ + struct ecommunity *ecom, /* generated ecoms */ + uint32_t *local_pref, /* NULL = no local_pref */ + uint32_t *med, /* NULL = no med */ + struct prefix *ubpi_nexthop) /* unicast nexthop */ +{ + struct bgp_dest *bd; + struct bgp_path_info *bpi; + + if (!table_rd) + return; + + vnc_zlog_debug_verbose("%s: ubpi_nexthop=%pFX", __func__, ubpi_nexthop); + + /* exact match */ + bd = bgp_node_lookup(table_rd, ubpi_nexthop); + if (!bd) { + vnc_zlog_debug_verbose( + "%s: no match in RD's table for ubpi_nexthop", + __func__); + return; + } + + /* Iterate over bgp_info items at this node */ + for (bpi = bgp_dest_get_bgp_path_info(bd); bpi; bpi = bpi->next) { + + vnc_import_bgp_add_route_mode_resolve_nve_one_bi( + bgp, afi, bpi, /* VPN bpi */ + prd, prefix, local_pref, med, ecom); + } + + bgp_dest_unlock_node(bd); +} + +static void vnc_import_bgp_add_route_mode_resolve_nve( + struct bgp *bgp, const struct prefix *prefix, /* unicast prefix */ + struct bgp_path_info *info) /* unicast info */ +{ + afi_t afi = family2afi(prefix->family); + + struct prefix pfx_unicast_nexthop = {0}; /* happy valgrind */ + + struct ecommunity *ecom = NULL; + uint32_t local_pref; + uint32_t *med = NULL; + + struct prefix_bag *pb; + struct bgp_dest *bdp; /* prd table node */ + + /*debugging */ + if (VNC_DEBUG(VERBOSE)) { + char str_nh[PREFIX_STRLEN]; + struct prefix nh; + + nh.prefixlen = 0; + rfapiUnicastNexthop2Prefix(afi, info->attr, &nh); + if (nh.prefixlen) { + prefix2str(&nh, str_nh, sizeof(str_nh)); + } else { + str_nh[0] = '?'; + str_nh[1] = 0; + } + + vnc_zlog_debug_verbose( + "%s(bgp=%p, unicast prefix=%pFX, unicast nh=%s)", + __func__, bgp, prefix, str_nh); + } + + if (info->type != ZEBRA_ROUTE_BGP) { + vnc_zlog_debug_verbose( + "%s: unicast type %d=\"%s\" is not %d=%s, skipping", + __func__, info->type, zebra_route_string(info->type), + ZEBRA_ROUTE_BGP, "ZEBRA_ROUTE_BGP"); + return; + } + + /* + * Preliminary checks + */ + + if (!afi) { + flog_err(EC_LIB_DEVELOPMENT, "%s: can't get afi of prefix", + __func__); + return; + } + + if (!(bgp->rfapi_cfg)) { + vnc_zlog_debug_verbose("%s: bgp->rfapi_cfg is NULL, skipping", + __func__); + return; + } + + /* check vnc redist flag for bgp direct routes */ + if (!bgp->rfapi_cfg->redist[afi][ZEBRA_ROUTE_BGP_DIRECT]) { + vnc_zlog_debug_verbose( + "%s: bgp->rfapi_cfg->redist[afi=%d][type=ZEBRA_ROUTE_BGP_DIRECT] is 0, skipping", + __func__, afi); + return; + } + + + if (process_unicast_route(bgp, afi, prefix, info, &ecom, + &pfx_unicast_nexthop)) { + + vnc_zlog_debug_verbose( + "%s: process_unicast_route error, skipping", __func__); + return; + } + + local_pref = calc_local_pref(info->attr, info->peer); + if (info->attr->flag & ATTR_FLAG_BIT(BGP_ATTR_MULTI_EXIT_DISC)) + med = &info->attr->med; + + /* + * At this point, we have allocated: + * + * ecom ecommunity ptr, union of unicast and ROO parts (no NVE part) + * + * And we have set: + * + * pfx_unicast_nexthop nexthop of uncast route + */ + + if (!bgp->rfapi->resolve_nve_nexthop) { + bgp->rfapi->resolve_nve_nexthop = + skiplist_new(SKIPLIST_FLAG_ALLOW_DUPLICATES, + vnc_prefix_cmp, prefix_bag_free); + } + + pb = XCALLOC(MTYPE_RFAPI_PREFIX_BAG, sizeof(struct prefix_bag)); + pb->hpfx = pfx_unicast_nexthop; + pb->ubpi = info; + pb->upfx = *prefix; + + bgp_path_info_lock(info); /* skiplist refers to it */ + skiplist_insert(bgp->rfapi->resolve_nve_nexthop, &pb->hpfx, pb); + + /* + * Iterate over RDs in VPN RIB. For each RD, look up unicast nexthop + * (exact match, /32). If an exact match is found, call add_vnc_route. + */ + + for (bdp = bgp_table_top(bgp->rib[afi][SAFI_MPLS_VPN]); bdp; + bdp = bgp_route_next(bdp)) { + + struct bgp_table *table; + + table = bgp_dest_get_bgp_table_info(bdp); + + if (!table) + continue; + + vnc_import_bgp_add_route_mode_resolve_nve_one_rd( + (struct prefix_rd *)bgp_dest_get_prefix(bdp), table, + afi, bgp, prefix, ecom, &local_pref, med, + &pfx_unicast_nexthop); + } + + + if (ecom) + ecommunity_free(&ecom); + + vnc_zlog_debug_verbose("%s: done", __func__); +} + + +static void vnc_import_bgp_add_route_mode_plain(struct bgp *bgp, + const struct prefix *prefix, + struct bgp_path_info *info) +{ + afi_t afi = family2afi(prefix->family); + struct peer *peer = info->peer; + struct attr *attr = info->attr; + struct attr hattr; + struct rfapi_cfg *hc = bgp->rfapi_cfg; + struct attr *iattr = NULL; + + struct rfapi_ip_addr vnaddr; + struct prefix vn_pfx_space; + struct prefix *vn_pfx = NULL; + int ahr_flags = 0; + struct ecommunity *ecom = NULL; + struct prefix_rd prd; + struct route_map *rmap = NULL; + uint32_t local_pref; + uint32_t *med = NULL; + + vnc_zlog_debug_verbose("%s(prefix=%pFX) entry", __func__, prefix); + + if (!afi) { + flog_err(EC_LIB_DEVELOPMENT, "%s: can't get afi of prefix", + __func__); + return; + } + + if (!hc) { + vnc_zlog_debug_verbose("%s: bgp->rfapi_cfg is NULL, skipping", + __func__); + return; + } + + /* check vnc redist flag for bgp direct routes */ + if (!bgp->rfapi_cfg->redist[afi][ZEBRA_ROUTE_BGP_DIRECT]) { + vnc_zlog_debug_verbose( + "%s: bgp->rfapi_cfg->redist[afi=%d][type=ZEBRA_ROUTE_BGP_DIRECT] is 0, skipping", + __func__, afi); + return; + } + + /* + * mode "plain" specific code + */ + { + vnc_zlog_debug_verbose("%s: NOT using redist RFG", __func__); + + /* + * prefix list check + */ + if (hc->plist_redist[ZEBRA_ROUTE_BGP_DIRECT][afi]) { + vnc_zlog_debug_verbose( + "%s: HC prefix list is set, checking", + __func__); + if (prefix_list_apply( + hc->plist_redist[ZEBRA_ROUTE_BGP_DIRECT] + [afi], + prefix) + == PREFIX_DENY) { + vnc_zlog_debug_verbose( + "%s: prefix list returns DENY, blocking route", + __func__); + return; + } + vnc_zlog_debug_verbose( + "%s: prefix list returns PASS, allowing route", + __func__); + } + + /* apply routemap, if any, later */ + rmap = hc->routemap_redist[ZEBRA_ROUTE_BGP_DIRECT]; + + /* + * Incoming prefix is unicast. If v6, it is in multiprotocol + * area, + * but if v4 it is in attr->nexthop + */ + rfapiUnicastNexthop2Prefix(afi, attr, &vn_pfx_space); + vn_pfx = &vn_pfx_space; + + /* UN address */ + ahr_flags |= RFAPI_AHR_NO_TUNNEL_SUBTLV; + } + + if (VNC_DEBUG(IMPORT_BGP_ADD_ROUTE)) + vnc_zlog_debug_any("%s vn_pfx=%pFX", __func__, vn_pfx); + + /* + * Compute VN address + */ + if (rfapiQprefix2Raddr(vn_pfx, &vnaddr)) { + vnc_zlog_debug_verbose("%s: redist VN invalid, skipping", + __func__); + return; + } + + /* + * route map handling + * This code is here because it allocates an interned attr which + * must be freed before we return. It's easier to put it after + * all of the possible returns above. + */ + memset(&hattr, 0, sizeof(hattr)); + /* hattr becomes a ghost attr */ + hattr = *attr; + + if (rmap) { + struct bgp_path_info info; + route_map_result_t ret; + + memset(&info, 0, sizeof(info)); + info.peer = peer; + info.attr = &hattr; + ret = route_map_apply(rmap, prefix, &info); + if (ret == RMAP_DENYMATCH) { + bgp_attr_flush(&hattr); + vnc_zlog_debug_verbose( + "%s: route map \"%s\" says DENY, returning", + __func__, rmap->name); + return; + } + } + + iattr = bgp_attr_intern(&hattr); + bgp_attr_flush(&hattr); + + /* Now iattr is an allocated interned attr */ + + /* + * Mode "plain" specific code + * + * Sets RD in dummy HD + * Allocates ecom + */ + { + if (vnaddr.addr_family != AF_INET) { + vnc_zlog_debug_verbose( + "%s: can't auto-assign RD, VN AF (%d) is not IPv4, skipping", + __func__, vnaddr.addr_family); + if (iattr) { + bgp_attr_unintern(&iattr); + } + return; + } + memset(&prd, 0, sizeof(prd)); + rfapi_set_autord_from_vn(&prd, &vnaddr); + + if (iattr && bgp_attr_get_ecommunity(iattr)) + ecom = ecommunity_dup(bgp_attr_get_ecommunity(iattr)); + } + + local_pref = calc_local_pref(iattr, peer); + + if (iattr && (iattr->flag & ATTR_FLAG_BIT(BGP_ATTR_MULTI_EXIT_DISC))) { + med = &iattr->med; + } + + if (VNC_DEBUG(IMPORT_BGP_ADD_ROUTE)) { + char buf[PREFIX_STRLEN]; + + rfapiRfapiIpAddr2Str(&vnaddr, buf, sizeof(buf)); + vnc_zlog_debug_any("%s: setting vnaddr to %s", __func__, buf); + } + + vncHDBgpDirect.peer = peer; + add_vnc_route(&vncHDBgpDirect, bgp, SAFI_MPLS_VPN, prefix, &prd, + &vnaddr, &local_pref, &(bgp->rfapi_cfg->redist_lifetime), + NULL, /* RFP options */ + NULL, NULL, ecom, med, /* med */ + NULL, /* label: default */ + ZEBRA_ROUTE_BGP_DIRECT, BGP_ROUTE_REDISTRIBUTE, + ahr_flags); + vncHDBgpDirect.peer = NULL; + + if (ecom) + ecommunity_free(&ecom); +} + +static void vnc_import_bgp_add_route_mode_nvegroup( + struct bgp *bgp, const struct prefix *prefix, + struct bgp_path_info *info, struct rfapi_nve_group_cfg *rfg) +{ + afi_t afi = family2afi(prefix->family); + struct peer *peer = info->peer; + struct attr *attr = info->attr; + struct attr hattr; + struct attr *iattr = NULL; + + struct rfapi_ip_addr vnaddr; + struct prefix *vn_pfx = NULL; + int ahr_flags = 0; + struct ecommunity *ecom = NULL; + struct prefix_rd prd; + struct route_map *rmap = NULL; + uint32_t local_pref; + + vnc_zlog_debug_verbose("%s(prefix=%pFX) entry", __func__, prefix); + + assert(rfg); + + if (!afi) { + flog_err(EC_LIB_DEVELOPMENT, "%s: can't get afi of prefix", + __func__); + return; + } + + if (!(bgp->rfapi_cfg)) { + vnc_zlog_debug_verbose("%s: bgp->rfapi_cfg is NULL, skipping", + __func__); + return; + } + + /* check vnc redist flag for bgp direct routes */ + if (!bgp->rfapi_cfg->redist[afi][ZEBRA_ROUTE_BGP_DIRECT]) { + vnc_zlog_debug_verbose( + "%s: bgp->rfapi_cfg->redist[afi=%d][type=ZEBRA_ROUTE_BGP_DIRECT] is 0, skipping", + __func__, afi); + return; + } + + + /* + * RFG-specific code + */ + { + + struct rfapi_ip_prefix pfx_un; + + vnc_zlog_debug_verbose("%s: using redist RFG", __func__); + + /* + * RFG prefix list check + */ + if (rfg->plist_redist[ZEBRA_ROUTE_BGP_DIRECT][afi]) { + vnc_zlog_debug_verbose( + "%s: RFG prefix list is set, checking", + __func__); + if (prefix_list_apply( + rfg->plist_redist[ZEBRA_ROUTE_BGP_DIRECT] + [afi], + prefix) + == PREFIX_DENY) { + vnc_zlog_debug_verbose( + "%s: prefix list returns DENY, blocking route", + __func__); + return; + } + vnc_zlog_debug_verbose( + "%s: prefix list returns PASS, allowing route", + __func__); + } + + /* apply routemap, if any, later */ + rmap = rfg->routemap_redist[ZEBRA_ROUTE_BGP_DIRECT]; + + /* + * export nve group's VN addr prefix must be a /32 which + * will yield the VN addr to use + */ + vn_pfx = &rfg->vn_prefix; + + /* + * UN Address + */ + if (!is_host_prefix(&rfg->un_prefix)) { + /* NB prefixlen==0 means it has not been configured */ + vnc_zlog_debug_verbose( + "%s: redist RFG UN pfx not host pfx (plen=%d), skipping", + __func__, rfg->un_prefix.prefixlen); + return; + } + + rfapiQprefix2Rprefix(&rfg->un_prefix, &pfx_un); + + vncHDBgpDirect.un_addr = pfx_un.prefix; + } + + if (VNC_DEBUG(IMPORT_BGP_ADD_ROUTE)) + vnc_zlog_debug_any("%s vn_pfx=%pFX", __func__, vn_pfx); + + /* + * Compute VN address + */ + if (rfapiQprefix2Raddr(vn_pfx, &vnaddr)) { + vnc_zlog_debug_verbose("%s: redist VN invalid, skipping", + __func__); + return; + } + + /* + * route map handling + * This code is here because it allocates an interned attr which + * must be freed before we return. It's easier to put it after + * all of the possible returns above. + */ + memset(&hattr, 0, sizeof(hattr)); + /* hattr becomes a ghost attr */ + hattr = *attr; + + if (rmap) { + struct bgp_path_info path; + route_map_result_t ret; + + memset(&path, 0, sizeof(path)); + path.peer = peer; + path.attr = &hattr; + ret = route_map_apply(rmap, prefix, &path); + if (ret == RMAP_DENYMATCH) { + bgp_attr_flush(&hattr); + vnc_zlog_debug_verbose( + "%s: route map \"%s\" says DENY, returning", + __func__, rmap->name); + return; + } + } + + iattr = bgp_attr_intern(&hattr); + bgp_attr_flush(&hattr); + + /* Now iattr is an allocated interned attr */ + + /* + * RFG-specific code + * + * Sets RD in dummy HD + * Allocates ecom + */ + { + + memset(&prd, 0, sizeof(prd)); + prd = rfg->rd; + prd.family = AF_UNSPEC; + prd.prefixlen = 64; + + if (rfg->rd.family == AF_UNIX) { + rfapi_set_autord_from_vn(&prd, &vnaddr); + } + + if (rfg->rt_export_list) + ecom = ecommunity_dup( + bgp->rfapi_cfg->rfg_redist->rt_export_list); + else + ecom = ecommunity_new(); + + if (iattr && bgp_attr_get_ecommunity(iattr)) + ecom = ecommunity_merge(ecom, + bgp_attr_get_ecommunity(iattr)); + } + + local_pref = calc_local_pref(iattr, peer); + + if (VNC_DEBUG(IMPORT_BGP_ADD_ROUTE)) { + char buf[BUFSIZ]; + + buf[0] = 0; + rfapiRfapiIpAddr2Str(&vnaddr, buf, BUFSIZ); + buf[BUFSIZ - 1] = 0; + vnc_zlog_debug_any("%s: setting vnaddr to %s", __func__, buf); + } + + vncHDBgpDirect.peer = peer; + add_vnc_route(&vncHDBgpDirect, bgp, SAFI_MPLS_VPN, prefix, &prd, + &vnaddr, &local_pref, &(bgp->rfapi_cfg->redist_lifetime), + NULL, /* RFP options */ + NULL, NULL, ecom, NULL, /* med */ + NULL, /* label: default */ + ZEBRA_ROUTE_BGP_DIRECT, BGP_ROUTE_REDISTRIBUTE, + ahr_flags); + vncHDBgpDirect.peer = NULL; + + if (ecom) + ecommunity_free(&ecom); +} + +static void vnc_import_bgp_del_route_mode_plain(struct bgp *bgp, + const struct prefix *prefix, + struct bgp_path_info *info) +{ + struct prefix_rd prd; + afi_t afi = family2afi(prefix->family); + struct prefix *vn_pfx = NULL; + struct rfapi_ip_addr vnaddr; + struct prefix vn_pfx_space; + + + assert(afi); + + /* + * Compute VN address + */ + + if (info) { + rfapiUnicastNexthop2Prefix(afi, info->attr, &vn_pfx_space); + } else { + vnc_zlog_debug_verbose("%s: no attr, can't delete route", + __func__); + return; + } + vn_pfx = &vn_pfx_space; + + vnaddr.addr_family = vn_pfx->family; + switch (vn_pfx->family) { + case AF_INET: + if (vn_pfx->prefixlen != IPV4_MAX_BITLEN) { + vnc_zlog_debug_verbose( + "%s: redist VN plen (%d) != 32, skipping", + __func__, vn_pfx->prefixlen); + return; + } + vnaddr.addr.v4 = vn_pfx->u.prefix4; + break; + + case AF_INET6: + if (vn_pfx->prefixlen != IPV6_MAX_BITLEN) { + vnc_zlog_debug_verbose( + "%s: redist VN plen (%d) != 128, skipping", + __func__, vn_pfx->prefixlen); + return; + } + vnaddr.addr.v6 = vn_pfx->u.prefix6; + break; + + default: + vnc_zlog_debug_verbose( + "%s: no redist RFG VN host pfx configured, skipping", + __func__); + return; + } + + + memset(&prd, 0, sizeof(prd)); + if (rfapi_set_autord_from_vn(&prd, &vnaddr)) { + vnc_zlog_debug_verbose("%s: can't auto-assign RD, skipping", + __func__); + return; + } + + vncHDBgpDirect.peer = info->peer; + vnc_zlog_debug_verbose("%s: setting peer to %p", __func__, + vncHDBgpDirect.peer); + del_vnc_route(&vncHDBgpDirect, info->peer, bgp, SAFI_MPLS_VPN, prefix, + &prd, ZEBRA_ROUTE_BGP_DIRECT, BGP_ROUTE_REDISTRIBUTE, + NULL, 1); + + vncHDBgpDirect.peer = NULL; +} + +static void vnc_import_bgp_del_route_mode_nvegroup(struct bgp *bgp, + const struct prefix *prefix, + struct bgp_path_info *info) +{ + struct prefix_rd prd; + afi_t afi = family2afi(prefix->family); + struct rfapi_nve_group_cfg *rfg = NULL; + struct prefix *vn_pfx = NULL; + struct rfapi_ip_addr vnaddr; + + + assert(afi); + + rfg = bgp->rfapi_cfg->rfg_redist; + assert(rfg); + + /* + * Compute VN address + */ + + /* + * export nve group's VN addr prefix must be a /32 which + * will yield the VN addr to use + */ + vn_pfx = &rfg->vn_prefix; + + + vnaddr.addr_family = vn_pfx->family; + switch (vn_pfx->family) { + case AF_INET: + if (vn_pfx->prefixlen != IPV4_MAX_BITLEN) { + vnc_zlog_debug_verbose( + "%s: redist VN plen (%d) != 32, skipping", + __func__, vn_pfx->prefixlen); + return; + } + vnaddr.addr.v4 = vn_pfx->u.prefix4; + break; + + case AF_INET6: + if (vn_pfx->prefixlen != IPV6_MAX_BITLEN) { + vnc_zlog_debug_verbose( + "%s: redist VN plen (%d) != 128, skipping", + __func__, vn_pfx->prefixlen); + return; + } + vnaddr.addr.v6 = vn_pfx->u.prefix6; + break; + + default: + vnc_zlog_debug_verbose( + "%s: no redist RFG VN host pfx configured, skipping", + __func__); + return; + } + + memset(&prd, 0, sizeof(prd)); + prd = rfg->rd; + prd.family = AF_UNSPEC; + prd.prefixlen = 64; + + if (rfg->rd.family == AF_UNIX) { + /* means "auto" with VN addr */ + if (rfapi_set_autord_from_vn(&prd, &vnaddr)) { + vnc_zlog_debug_verbose( + "%s: can't auto-assign RD, skipping", __func__); + return; + } + } + + + vncHDBgpDirect.peer = info->peer; + vnc_zlog_debug_verbose("%s: setting peer to %p", __func__, + vncHDBgpDirect.peer); + del_vnc_route(&vncHDBgpDirect, info->peer, bgp, SAFI_MPLS_VPN, prefix, + &prd, ZEBRA_ROUTE_BGP_DIRECT, BGP_ROUTE_REDISTRIBUTE, + NULL, 1); + + vncHDBgpDirect.peer = NULL; +} + +static void vnc_import_bgp_del_route_mode_resolve_nve_one_bi( + struct bgp *bgp, afi_t afi, struct bgp_path_info *bpi, /* VPN bpi */ + struct prefix_rd *prd, /* RD */ + const struct prefix *prefix) /* unicast route prefix */ +{ + struct prefix un; + + if (bpi->type != ZEBRA_ROUTE_BGP + && bpi->type != ZEBRA_ROUTE_BGP_DIRECT) { + + return; + } + if (bpi->sub_type != BGP_ROUTE_NORMAL + && bpi->sub_type != BGP_ROUTE_STATIC + && bpi->sub_type != BGP_ROUTE_RFP) { + + return; + } + if (CHECK_FLAG(bpi->flags, BGP_PATH_REMOVED)) + return; + + vncHDResolveNve.peer = bpi->peer; + if (!rfapiGetVncTunnelUnAddr(bpi->attr, &un)) { + if (rfapiQprefix2Raddr(&un, &vncHDResolveNve.un_addr)) + return; + } else { + memset(&vncHDResolveNve.un_addr, 0, + sizeof(vncHDResolveNve.un_addr)); + } + + del_vnc_route(&vncHDResolveNve, vncHDResolveNve.peer, bgp, + SAFI_MPLS_VPN, prefix, /* unicast route prefix */ + prd, ZEBRA_ROUTE_BGP_DIRECT, BGP_ROUTE_REDISTRIBUTE, NULL, + 0); /* flags */ +} + +static void vnc_import_bgp_del_route_mode_resolve_nve_one_rd( + struct prefix_rd *prd, + struct bgp_table *table_rd, /* per-rd VPN route table */ + afi_t afi, struct bgp *bgp, + const struct prefix *prefix, /* unicast prefix */ + const struct prefix *ubpi_nexthop) /* unicast bpi's nexthop */ +{ + struct bgp_dest *bd; + struct bgp_path_info *bpi; + + if (!table_rd) + return; + + vnc_zlog_debug_verbose("%s: ubpi_nexthop=%pFX", __func__, ubpi_nexthop); + + + /* exact match */ + bd = bgp_node_lookup(table_rd, ubpi_nexthop); + if (!bd) { + vnc_zlog_debug_verbose( + "%s: no match in RD's table for ubpi_nexthop", + __func__); + return; + } + + /* Iterate over bgp_info items at this node */ + for (bpi = bgp_dest_get_bgp_path_info(bd); bpi; bpi = bpi->next) { + + vnc_import_bgp_del_route_mode_resolve_nve_one_bi( + bgp, afi, bpi, /* VPN bpi */ + prd, /* VPN RD */ + prefix); /* unicast route prefix */ + } + + bgp_dest_unlock_node(bd); +} + +static void +vnc_import_bgp_del_route_mode_resolve_nve(struct bgp *bgp, afi_t afi, + const struct prefix *prefix, + struct bgp_path_info *info) +{ + struct ecommunity *ecom = NULL; + struct prefix pfx_unicast_nexthop = {0}; /* happy valgrind */ + + // struct listnode *hnode; + // struct rfapi_descriptor *rfd; + struct prefix_bag *pb; + void *cursor; + struct skiplist *sl = bgp->rfapi->resolve_nve_nexthop; + int rc; + struct bgp_dest *bdp; /* prd table node */ + + if (!sl) { + vnc_zlog_debug_verbose("%s: no RHN entries, skipping", + __func__); + return; + } + + if (info->type != ZEBRA_ROUTE_BGP) { + vnc_zlog_debug_verbose( + "%s: unicast type %d=\"%s\" is not %d=%s, skipping", + __func__, info->type, zebra_route_string(info->type), + ZEBRA_ROUTE_BGP, "ZEBRA_ROUTE_BGP"); + return; + } + + if (process_unicast_route(bgp, afi, prefix, info, &ecom, + &pfx_unicast_nexthop)) { + + vnc_zlog_debug_verbose( + "%s: process_unicast_route error, skipping", __func__); + return; + } + + rc = skiplist_first_value(sl, &pfx_unicast_nexthop, (void *)&pb, + &cursor); + while (!rc) { + if (pb->ubpi == info) { + skiplist_delete(sl, &pfx_unicast_nexthop, pb); + bgp_path_info_unlock(info); + break; + } + rc = skiplist_next_value(sl, &pfx_unicast_nexthop, (void *)&pb, + &cursor); + } + + /* + * Iterate over RDs in VPN RIB. For each RD, look up unicast nexthop + * (exact match, /32). If an exact match is found, call add_vnc_route. + */ + + for (bdp = bgp_table_top(bgp->rib[afi][SAFI_MPLS_VPN]); bdp; + bdp = bgp_route_next(bdp)) { + + struct bgp_table *table; + + table = bgp_dest_get_bgp_table_info(bdp); + + if (!table) + continue; + + vnc_import_bgp_del_route_mode_resolve_nve_one_rd( + (struct prefix_rd *)bgp_dest_get_prefix(bdp), table, + afi, bgp, prefix, &pfx_unicast_nexthop); + } + + if (ecom) + ecommunity_free(&ecom); +} + + +/*********************************************************************** + * Add/Delete CE->NVE routes + ***********************************************************************/ + +/* + * Should be called whan a bpi is added to VPN RIB. This function + * will check if it is a host route and return immediately if not. + */ +void vnc_import_bgp_add_vnc_host_route_mode_resolve_nve( + struct bgp *bgp, struct prefix_rd *prd, /* RD */ + struct bgp_table *table_rd, /* per-rd VPN route table */ + const struct prefix *prefix, /* VPN prefix */ + struct bgp_path_info *bpi) /* new VPN host route */ +{ + afi_t afi = family2afi(prefix->family); + struct skiplist *sl = NULL; + int rc; + struct prefix_bag *pb; + void *cursor; + struct rfapi_cfg *hc = NULL; + + vnc_zlog_debug_verbose("%s: entry", __func__); + + if (afi != AFI_IP && afi != AFI_IP6) { + vnc_zlog_debug_verbose("%s: bad afi %d, skipping", __func__, + afi); + return; + } + + if (!(hc = bgp->rfapi_cfg)) { + vnc_zlog_debug_verbose("%s: bgp->rfapi_cfg is NULL, skipping", + __func__); + return; + } + + /* check vnc redist flag for bgp direct routes */ + if (!hc->redist[afi][ZEBRA_ROUTE_BGP_DIRECT]) { + vnc_zlog_debug_verbose( + "%s: bgp->rfapi_cfg->redist[afi=%d][type=ZEBRA_ROUTE_BGP_DIRECT] is 0, skipping", + __func__, afi); + return; + } + + if (hc->redist_mode != VNC_REDIST_MODE_RESOLVE_NVE) { + vnc_zlog_debug_verbose("%s: not in resolve-nve mode, skipping", + __func__); + return; + } + + if (bgp->rfapi) + sl = bgp->rfapi->resolve_nve_nexthop; + + if (!sl) { + vnc_zlog_debug_verbose( + "%s: no resolve_nve_nexthop skiplist, skipping", + __func__); + return; + } + + if (!is_host_prefix(prefix)) { + vnc_zlog_debug_verbose("%s: not host prefix, skipping", + __func__); + return; + } + + rc = skiplist_first_value(sl, prefix, (void *)&pb, &cursor); + while (!rc) { + struct ecommunity *ecom; + struct prefix pfx_unicast_nexthop; + uint32_t *med = NULL; + uint32_t local_pref; + + memset(&pfx_unicast_nexthop, 0, + sizeof(pfx_unicast_nexthop)); /* keep valgrind happy */ + + if (VNC_DEBUG(IMPORT_BGP_ADD_ROUTE)) + vnc_zlog_debug_any( + "%s: examining RHN Entry (q=%p): upfx=%pFX, hpfx=%pFX, ubpi=%p", + __func__, cursor, &pb->upfx, &pb->hpfx, + pb->ubpi); + + if (process_unicast_route(bgp, afi, &pb->upfx, pb->ubpi, &ecom, + &pfx_unicast_nexthop)) { + + vnc_zlog_debug_verbose( + "%s: process_unicast_route error, skipping", + __func__); + continue; + } + local_pref = calc_local_pref(pb->ubpi->attr, pb->ubpi->peer); + + if (pb->ubpi->attr->flag + & ATTR_FLAG_BIT(BGP_ATTR_MULTI_EXIT_DISC)) + med = &pb->ubpi->attr->med; + + /* + * Sanity check + */ + if (vnc_prefix_cmp(&pfx_unicast_nexthop, prefix)) { + vnc_zlog_debug_verbose( + "%s: FATAL: resolve_nve_nexthop list item bpi nexthop %pFX != nve pfx %pFX", + __func__, &pfx_unicast_nexthop, prefix); + assert(0); + } + + vnc_import_bgp_add_route_mode_resolve_nve_one_bi( + bgp, afi, bpi, /* VPN bpi */ + prd, &pb->upfx, /* unicast prefix */ + &local_pref, med, ecom); + + if (ecom) + ecommunity_free(&ecom); + +#if DEBUG_RHN_LIST + /* debug */ + { + vnc_zlog_debug_verbose( + "%s: advancing past RHN Entry (q=%p): with prefix %pFX", + __func__, cursor, prefix); + print_rhn_list(__func__, NULL); /* debug */ + } +#endif + rc = skiplist_next_value(sl, prefix, (void *)&pb, &cursor); + } + vnc_zlog_debug_verbose("%s: done", __func__); +} + + +void vnc_import_bgp_del_vnc_host_route_mode_resolve_nve( + struct bgp *bgp, struct prefix_rd *prd, /* RD */ + struct bgp_table *table_rd, /* per-rd VPN route table */ + const struct prefix *prefix, /* VPN prefix */ + struct bgp_path_info *bpi) /* old VPN host route */ +{ + afi_t afi = family2afi(prefix->family); + struct skiplist *sl = NULL; + struct prefix_bag *pb; + void *cursor; + struct rfapi_cfg *hc = NULL; + int rc; + + vnc_zlog_debug_verbose("%s(bgp=%p, nve prefix=%pFX)", __func__, bgp, + prefix); + + if (afi != AFI_IP && afi != AFI_IP6) + return; + + if (!(hc = bgp->rfapi_cfg)) { + vnc_zlog_debug_verbose("%s: bgp->rfapi_cfg is NULL, skipping", + __func__); + return; + } + + /* check vnc redist flag for bgp direct routes */ + if (!hc->redist[afi][ZEBRA_ROUTE_BGP_DIRECT]) { + vnc_zlog_debug_verbose( + "%s: bgp->rfapi_cfg->redist[afi=%d][type=ZEBRA_ROUTE_BGP_DIRECT] is 0, skipping", + __func__, afi); + return; + } + + if (hc->redist_mode != VNC_REDIST_MODE_RESOLVE_NVE) { + vnc_zlog_debug_verbose("%s: not in resolve-nve mode, skipping", + __func__); + return; + } + + if (bgp->rfapi) + sl = bgp->rfapi->resolve_nve_nexthop; + + if (!sl) { + vnc_zlog_debug_verbose("%s: no RHN entries, skipping", + __func__); + return; + } + + if (!is_host_prefix(prefix)) { + vnc_zlog_debug_verbose("%s: not host route, skip", __func__); + return; + } + + /* + * Find all entries with key == CE in the RHN list + */ + rc = skiplist_first_value(sl, prefix, (void *)&pb, &cursor); + while (!rc) { + + struct ecommunity *ecom; + struct prefix pfx_unicast_nexthop; + + memset(&pfx_unicast_nexthop, 0, + sizeof(pfx_unicast_nexthop)); /* keep valgrind happy */ + + if (process_unicast_route(bgp, afi, &pb->upfx, pb->ubpi, &ecom, + &pfx_unicast_nexthop)) { + + vnc_zlog_debug_verbose( + "%s: process_unicast_route error, skipping", + __func__); + continue; + } + + /* + * Sanity check + */ + if (vnc_prefix_cmp(&pfx_unicast_nexthop, prefix)) { + vnc_zlog_debug_verbose( + "%s: FATAL: resolve_nve_nexthop list item bpi nexthop %pFX != nve pfx %pFX", + __func__, &pfx_unicast_nexthop, prefix); + assert(0); + } + + vnc_import_bgp_del_route_mode_resolve_nve_one_bi( + bgp, afi, bpi, prd, &pb->upfx); + + if (ecom) + ecommunity_free(&ecom); + + rc = skiplist_next_value(sl, prefix, (void *)&pb, &cursor); + } +} + + +/*********************************************************************** + * Exterior Routes + ***********************************************************************/ + +#define DEBUG_IS_USABLE_INTERIOR 1 + +static int is_usable_interior_route(struct bgp_path_info *bpi_interior) +{ + if (!VALID_INTERIOR_TYPE(bpi_interior->type)) { +#if DEBUG_IS_USABLE_INTERIOR + vnc_zlog_debug_verbose( + "%s: NO: type %d is not valid interior type", __func__, + bpi_interior->type); +#endif + return 0; + } + if (!CHECK_FLAG(bpi_interior->flags, BGP_PATH_VALID)) { +#if DEBUG_IS_USABLE_INTERIOR + vnc_zlog_debug_verbose("%s: NO: BGP_PATH_VALID not set", + __func__); +#endif + return 0; + } + return 1; +} + +/* + * There should be only one of these per prefix at a time. + * This should be called as a result of selection operation + * + * NB should be called espacially for bgp instances that are named, + * because the exterior routes will always come from one of those. + * We filter here on the instance name to make sure we get only the + * right routes. + */ +static void vnc_import_bgp_exterior_add_route_it( + struct bgp *bgp, /* exterior instance, we hope */ + const struct prefix *prefix, /* unicast prefix */ + struct bgp_path_info *info, /* unicast info */ + struct rfapi_import_table *it_only) /* NULL, or limit to this IT */ +{ + struct rfapi *h; + struct rfapi_cfg *hc; + struct prefix pfx_orig_nexthop; + struct rfapi_import_table *it; + struct bgp *bgp_default = bgp_get_default(); + afi_t afi = family2afi(prefix->family); + + if (!bgp_default) + return; + + h = bgp_default->rfapi; + hc = bgp_default->rfapi_cfg; + + vnc_zlog_debug_verbose("%s: entry with it=%p", __func__, it_only); + + if (!h || !hc) { + vnc_zlog_debug_verbose( + "%s: rfapi or rfapi_cfg not instantiated, skipping", + __func__); + return; + } + if (!hc->redist_bgp_exterior_view) { + vnc_zlog_debug_verbose("%s: exterior view not set, skipping", + __func__); + return; + } + if (bgp != hc->redist_bgp_exterior_view) { + vnc_zlog_debug_verbose( + "%s: bgp %p != hc->redist_bgp_exterior_view %p, skipping", + __func__, bgp, hc->redist_bgp_exterior_view); + return; + } + + if (!hc->redist[afi][ZEBRA_ROUTE_BGP_DIRECT_EXT]) { + vnc_zlog_debug_verbose( + "%s: redist of exterior routes not enabled, skipping", + __func__); + return; + } + + /* + * Extract nexthop from exterior route + * + * Incoming prefix is unicast. If v6, it is in multiprotocol area, + * but if v4 it is in attr->nexthop + */ + rfapiUnicastNexthop2Prefix(afi, info->attr, &pfx_orig_nexthop); + + for (it = h->imports; it; it = it->next) { + struct agg_table *table; + struct agg_node *rn; + struct agg_node *par; + struct bgp_path_info *bpi_interior; + int have_usable_route; + + vnc_zlog_debug_verbose("%s: doing it %p", __func__, it); + + if (it_only && (it_only != it)) { + vnc_zlog_debug_verbose("%s: doesn't match it_only %p", + __func__, it_only); + continue; + } + + table = it->imported_vpn[afi]; + + for (rn = agg_node_match(table, &pfx_orig_nexthop), + have_usable_route = 0; + (!have_usable_route) && rn;) { + + vnc_zlog_debug_verbose("%s: it %p trying rn %p", + __func__, it, rn); + + for (bpi_interior = rn->info; bpi_interior; + bpi_interior = bpi_interior->next) { + struct prefix_rd *prd; + struct attr new_attr; + uint32_t label = 0; + + if (!is_usable_interior_route(bpi_interior)) + continue; + + vnc_zlog_debug_verbose( + "%s: usable: bpi_interior %p", __func__, + bpi_interior); + + /* + * have a legitimate route to exterior's nexthop + * via NVE. + * + * Import unicast route to the import table + */ + have_usable_route = 1; + + if (bpi_interior->extra) { + prd = &bpi_interior->extra->vnc.import + .rd; + label = decode_label( + &bpi_interior->extra->label[0]); + } else + prd = NULL; + + /* use local_pref from unicast route */ + memset(&new_attr, 0, sizeof(new_attr)); + new_attr = *bpi_interior->attr; + if (info->attr->flag + & ATTR_FLAG_BIT(BGP_ATTR_LOCAL_PREF)) { + new_attr.local_pref = + info->attr->local_pref; + new_attr.flag |= ATTR_FLAG_BIT( + BGP_ATTR_LOCAL_PREF); + } + + rfapiBgpInfoFilteredImportVPN( + it, FIF_ACTION_UPDATE, + bpi_interior->peer, NULL, /* rfd */ + prefix, NULL, afi, prd, &new_attr, + ZEBRA_ROUTE_BGP_DIRECT_EXT, + BGP_ROUTE_REDISTRIBUTE, &label); + } + + if (have_usable_route) { + /* + * Make monitor + * + * TBD factor this out into its own function + */ + struct prefix *pfx_mon = prefix_new(); + if (!RFAPI_MONITOR_EXTERIOR(rn)->source) { + RFAPI_MONITOR_EXTERIOR(rn)->source = + skiplist_new( + 0, NULL, + prefix_free_lists); + agg_lock_node(rn); /* for skiplist */ + } + agg_lock_node(rn); /* for skiplist entry */ + prefix_copy(pfx_mon, prefix); + if (!skiplist_insert( + RFAPI_MONITOR_EXTERIOR(rn)->source, + info, pfx_mon)) { + + bgp_path_info_lock(info); + } + } + par = agg_node_parent(rn); + if (par) + agg_lock_node(par); + agg_unlock_node(rn); + rn = par; + } + if (rn) + agg_unlock_node(rn); + + if (!have_usable_route) { + struct prefix *pfx_mon = prefix_new(); + prefix_copy(pfx_mon, prefix); + if (!skiplist_insert(it->monitor_exterior_orphans, info, + pfx_mon)) { + + bgp_path_info_lock(info); + } + } + } +} + +void vnc_import_bgp_exterior_add_route( + struct bgp *bgp, /* exterior instance, we hope */ + const struct prefix *prefix, /* unicast prefix */ + struct bgp_path_info *info) /* unicast info */ +{ + vnc_import_bgp_exterior_add_route_it(bgp, prefix, info, NULL); +} + +/* + * There should be only one of these per prefix at a time. + * This should probably be called as a result of selection operation. + * + * NB should be called espacially for bgp instances that are named, + * because the exterior routes will always come from one of those. + * We filter here on the instance name to make sure we get only the + * right routes. + */ +void vnc_import_bgp_exterior_del_route( + struct bgp *bgp, const struct prefix *prefix, /* unicast prefix */ + struct bgp_path_info *info) /* unicast info */ +{ + struct rfapi *h; + struct rfapi_cfg *hc; + struct rfapi_import_table *it; + struct prefix pfx_orig_nexthop; + afi_t afi = family2afi(prefix->family); + struct bgp *bgp_default = bgp_get_default(); + + if (!bgp_default) + return; + + memset(&pfx_orig_nexthop, 0, + sizeof(pfx_orig_nexthop)); /* keep valgrind happy */ + + h = bgp_default->rfapi; + hc = bgp_default->rfapi_cfg; + + if (!h || !hc) { + vnc_zlog_debug_verbose( + "%s: rfapi or rfapi_cfg not instantiated, skipping", + __func__); + return; + } + if (!hc->redist_bgp_exterior_view) { + vnc_zlog_debug_verbose("%s: exterior view not set, skipping", + __func__); + return; + } + if (bgp != hc->redist_bgp_exterior_view) { + vnc_zlog_debug_verbose( + "%s: bgp %p != hc->redist_bgp_exterior_view %p, skipping", + __func__, bgp, hc->redist_bgp_exterior_view); + return; + } + if (!hc->redist[afi][ZEBRA_ROUTE_BGP_DIRECT_EXT]) { + vnc_zlog_debug_verbose( + "%s: redist of exterior routes no enabled, skipping", + __func__); + return; + } + + /* + * Extract nexthop from exterior route + * + * Incoming prefix is unicast. If v6, it is in multiprotocol area, + * but if v4 it is in attr->nexthop + */ + rfapiUnicastNexthop2Prefix(afi, info->attr, &pfx_orig_nexthop); + + for (it = h->imports; it; it = it->next) { + struct agg_table *table; + struct agg_node *rn; + struct agg_node *par; + struct bgp_path_info *bpi_interior; + int have_usable_route; + + table = it->imported_vpn[afi]; + + for (rn = agg_node_match(table, &pfx_orig_nexthop), + have_usable_route = 0; + (!have_usable_route) && rn;) { + + for (bpi_interior = rn->info; bpi_interior; + bpi_interior = bpi_interior->next) { + struct prefix_rd *prd; + uint32_t label = 0; + + if (!is_usable_interior_route(bpi_interior)) + continue; + + /* + * have a legitimate route to exterior's nexthop + * via NVE. + * + * Import unicast route to the import table + */ + have_usable_route = 1; + + if (bpi_interior->extra) { + prd = &bpi_interior->extra->vnc.import + .rd; + label = decode_label( + &bpi_interior->extra->label[0]); + } else + prd = NULL; + + rfapiBgpInfoFilteredImportVPN( + it, FIF_ACTION_KILL, bpi_interior->peer, + NULL, /* rfd */ + prefix, NULL, afi, prd, + bpi_interior->attr, + ZEBRA_ROUTE_BGP_DIRECT_EXT, + BGP_ROUTE_REDISTRIBUTE, &label); + + /* + * Delete monitor + * + * TBD factor this out into its own function + */ + { + if (RFAPI_MONITOR_EXTERIOR(rn) + ->source) { + if (!skiplist_delete( + RFAPI_MONITOR_EXTERIOR( + rn) + ->source, + info, NULL)) { + + bgp_path_info_unlock( + info); + agg_unlock_node( + rn); /* sl entry + */ + } + if (skiplist_empty( + RFAPI_MONITOR_EXTERIOR( + rn) + ->source)) { + skiplist_free( + RFAPI_MONITOR_EXTERIOR( + rn) + ->source); + RFAPI_MONITOR_EXTERIOR( + rn) + ->source = NULL; + agg_unlock_node( + rn); /* skiplist + itself + */ + } + } + } + } + par = agg_node_parent(rn); + if (par) + agg_lock_node(par); + agg_unlock_node(rn); + rn = par; + } + if (rn) + agg_unlock_node(rn); + + if (!have_usable_route) { + if (!skiplist_delete(it->monitor_exterior_orphans, info, + NULL)) { + + bgp_path_info_unlock(info); + } + } + } +} + +/* + * This function should be called after a new interior VPN route + * has been added to an import_table. + * + * NB should also be called whenever an existing vpn interior route + * becomes valid (e.g., valid_interior_count is inremented) + */ +void vnc_import_bgp_exterior_add_route_interior( + struct bgp *bgp, struct rfapi_import_table *it, + struct agg_node *rn_interior, /* VPN IT node */ + struct bgp_path_info *bpi_interior) /* VPN IT route */ +{ + const struct prefix *p = agg_node_get_prefix(rn_interior); + afi_t afi = family2afi(p->family); + struct agg_node *par; + struct bgp_path_info *bpi_exterior; + struct prefix *pfx_exterior; /* exterior pfx */ + void *cursor; + int rc; + struct list *list_adopted; + + vnc_zlog_debug_verbose("%s: entry", __func__); + + if (!is_usable_interior_route(bpi_interior)) { + vnc_zlog_debug_verbose( + "%s: not usable interior route, skipping", __func__); + return; + } + + if (!bgp->rfapi_cfg->redist[afi][ZEBRA_ROUTE_BGP_DIRECT_EXT]) { + vnc_zlog_debug_verbose( + "%s: redist of exterior routes no enabled, skipping", + __func__); + return; + } + + if (it == bgp->rfapi->it_ce) { + vnc_zlog_debug_verbose("%s: import table is it_ce, skipping", + __func__); + return; + } + + /*debugging */ + vnc_zlog_debug_verbose("%s: interior prefix=%pRN, bpi type=%d", + __func__, rn_interior, bpi_interior->type); + + if (RFAPI_HAS_MONITOR_EXTERIOR(rn_interior)) { + + int count = 0; /* debugging */ + + vnc_zlog_debug_verbose( + "%s: has exterior monitor; ext src: %p", __func__, + RFAPI_MONITOR_EXTERIOR(rn_interior)->source); + + /* + * There is a monitor here already. Therefore, we do not need + * to do any pulldown. Just construct exterior routes based + * on the new interior route. + */ + cursor = NULL; + for (rc = skiplist_next( + RFAPI_MONITOR_EXTERIOR(rn_interior)->source, + (void **)&bpi_exterior, (void **)&pfx_exterior, + &cursor); + !rc; rc = skiplist_next( + RFAPI_MONITOR_EXTERIOR(rn_interior)->source, + (void **)&bpi_exterior, + (void **)&pfx_exterior, &cursor)) { + + struct prefix_rd *prd; + struct attr new_attr; + uint32_t label = 0; + + + ++count; /* debugging */ + + assert(bpi_exterior); + assert(pfx_exterior); + + if (bpi_interior->extra) { + prd = &bpi_interior->extra->vnc.import.rd; + label = decode_label( + &bpi_interior->extra->label[0]); + } else + prd = NULL; + + /* use local_pref from unicast route */ + memset(&new_attr, 0, sizeof(struct attr)); + new_attr = *bpi_interior->attr; + if (bpi_exterior + && (bpi_exterior->attr->flag + & ATTR_FLAG_BIT(BGP_ATTR_LOCAL_PREF))) { + new_attr.local_pref = + bpi_exterior->attr->local_pref; + new_attr.flag |= + ATTR_FLAG_BIT(BGP_ATTR_LOCAL_PREF); + } + + rfapiBgpInfoFilteredImportVPN( + it, FIF_ACTION_UPDATE, bpi_interior->peer, + NULL, /* rfd */ + pfx_exterior, NULL, afi, prd, &new_attr, + ZEBRA_ROUTE_BGP_DIRECT_EXT, + BGP_ROUTE_REDISTRIBUTE, &label); + } + vnc_zlog_debug_verbose( + "%s: finished constructing exteriors based on existing monitors", + __func__); + return; + } + + vnc_zlog_debug_verbose("%s: no exterior monitor", __func__); + + /* + * No monitor at this node. Is this the first valid interior + * route at this node? + */ + if (RFAPI_MONITOR_EXTERIOR(rn_interior)->valid_interior_count > 1) { + vnc_zlog_debug_verbose( + "%s: new interior route not first valid one, skipping pulldown", + __func__); + return; + } + + /* + * Look up the tree for possible pulldown candidates. + * Find nearest parent with an exterior route monitor + */ + for (par = agg_node_parent(rn_interior); par; + par = agg_node_parent(par)) { + if (RFAPI_HAS_MONITOR_EXTERIOR(par)) + break; + } + + if (par) { + + vnc_zlog_debug_verbose( + "%s: checking parent %p for possible pulldowns", + __func__, par); + + /* check monitors at par for possible pulldown */ + cursor = NULL; + for (rc = skiplist_next(RFAPI_MONITOR_EXTERIOR(par)->source, + (void **)&bpi_exterior, + (void **)&pfx_exterior, &cursor); + !rc; + rc = skiplist_next(RFAPI_MONITOR_EXTERIOR(par)->source, + (void **)&bpi_exterior, + (void **)&pfx_exterior, &cursor)) { + + struct prefix pfx_nexthop; + + memset(&pfx_nexthop, 0, + sizeof(struct prefix)); /* keep valgrind happy */ + + /* check original nexthop for prefix match */ + rfapiUnicastNexthop2Prefix(afi, bpi_exterior->attr, + &pfx_nexthop); + + if (prefix_match(p, &pfx_nexthop)) { + + struct bgp_path_info *bpi; + struct prefix_rd *prd; + struct attr new_attr; + uint32_t label = 0; + + /* do pull-down */ + + /* + * add monitor to longer prefix + */ + struct prefix *pfx_mon = prefix_new(); + prefix_copy(pfx_mon, pfx_exterior); + if (!RFAPI_MONITOR_EXTERIOR(rn_interior) + ->source) { + RFAPI_MONITOR_EXTERIOR(rn_interior) + ->source = skiplist_new( + 0, NULL, prefix_free_lists); + agg_lock_node(rn_interior); + } + skiplist_insert( + RFAPI_MONITOR_EXTERIOR(rn_interior) + ->source, + bpi_exterior, pfx_mon); + agg_lock_node(rn_interior); + + /* + * Delete constructed exterior routes based on + * parent routes. + */ + for (bpi = par->info; bpi; bpi = bpi->next) { + + if (bpi->extra) { + prd = &bpi->extra->vnc.import + .rd; + label = decode_label( + &bpi->extra->label[0]); + } else + prd = NULL; + + rfapiBgpInfoFilteredImportVPN( + it, FIF_ACTION_KILL, bpi->peer, + NULL, /* rfd */ + pfx_exterior, NULL, afi, prd, + bpi->attr, + ZEBRA_ROUTE_BGP_DIRECT_EXT, + BGP_ROUTE_REDISTRIBUTE, &label); + } + + + /* + * Add constructed exterior routes based on + * the new interior route at longer prefix. + */ + if (bpi_interior->extra) { + prd = &bpi_interior->extra->vnc.import + .rd; + label = decode_label( + &bpi_interior->extra->label[0]); + } else + prd = NULL; + + /* use local_pref from unicast route */ + memset(&new_attr, 0, sizeof(struct attr)); + new_attr = *bpi_interior->attr; + if (bpi_exterior + && (bpi_exterior->attr->flag + & ATTR_FLAG_BIT(BGP_ATTR_LOCAL_PREF))) { + new_attr.local_pref = + bpi_exterior->attr->local_pref; + new_attr.flag |= ATTR_FLAG_BIT( + BGP_ATTR_LOCAL_PREF); + } + + rfapiBgpInfoFilteredImportVPN( + it, FIF_ACTION_UPDATE, + bpi_interior->peer, NULL, /* rfd */ + pfx_exterior, NULL, afi, prd, &new_attr, + ZEBRA_ROUTE_BGP_DIRECT_EXT, + BGP_ROUTE_REDISTRIBUTE, &label); + } + } + + /* + * The only monitors at rn_interior are the ones we added just + * above, so we can use the rn_interior list to identify which + * monitors to delete from the parent. + */ + cursor = NULL; + for (rc = skiplist_next( + RFAPI_MONITOR_EXTERIOR(rn_interior)->source, + (void **)&bpi_exterior, NULL, &cursor); + !rc; rc = skiplist_next( + RFAPI_MONITOR_EXTERIOR(rn_interior)->source, + (void **)&bpi_exterior, NULL, &cursor)) { + + + skiplist_delete(RFAPI_MONITOR_EXTERIOR(par)->source, + bpi_exterior, NULL); + agg_unlock_node(par); /* sl entry */ + } + if (skiplist_empty(RFAPI_MONITOR_EXTERIOR(par)->source)) { + skiplist_free(RFAPI_MONITOR_EXTERIOR(par)->source); + RFAPI_MONITOR_EXTERIOR(par)->source = NULL; + agg_unlock_node(par); /* sl itself */ + } + } + + vnc_zlog_debug_verbose("%s: checking orphans", __func__); + + /* + * See if any orphans can be pulled down to the current node + */ + cursor = NULL; + list_adopted = NULL; + for (rc = skiplist_next(it->monitor_exterior_orphans, + (void **)&bpi_exterior, (void **)&pfx_exterior, + &cursor); + !rc; rc = skiplist_next(it->monitor_exterior_orphans, + (void **)&bpi_exterior, + (void **)&pfx_exterior, &cursor)) { + + struct prefix pfx_nexthop; + afi_t afi_exterior = family2afi(pfx_exterior->family); + + vnc_zlog_debug_verbose( + "%s: checking exterior orphan at prefix %pFX", __func__, + pfx_exterior); + + if (afi_exterior != afi) { + vnc_zlog_debug_verbose( + "%s: exterior orphan afi %d != interior afi %d, skip", + __func__, afi_exterior, afi); + continue; + } + + /* check original nexthop for prefix match */ + rfapiUnicastNexthop2Prefix(afi, bpi_exterior->attr, + &pfx_nexthop); + + if (prefix_match(p, &pfx_nexthop)) { + + struct prefix_rd *prd; + struct attr new_attr; + uint32_t label = 0; + + /* do pull-down */ + + /* + * add monitor to longer prefix + */ + + struct prefix *pfx_mon = prefix_new(); + prefix_copy(pfx_mon, pfx_exterior); + if (!RFAPI_MONITOR_EXTERIOR(rn_interior)->source) { + RFAPI_MONITOR_EXTERIOR(rn_interior)->source = + skiplist_new( + 0, NULL, prefix_free_lists); + agg_lock_node(rn_interior); /* sl */ + } + skiplist_insert( + RFAPI_MONITOR_EXTERIOR(rn_interior)->source, + bpi_exterior, pfx_mon); + agg_lock_node(rn_interior); /* sl entry */ + if (!list_adopted) { + list_adopted = list_new(); + } + listnode_add(list_adopted, bpi_exterior); + + /* + * Add constructed exterior routes based on the + * new interior route at the longer prefix. + */ + if (bpi_interior->extra) { + prd = &bpi_interior->extra->vnc.import.rd; + label = decode_label( + &bpi_interior->extra->label[0]); + } else + prd = NULL; + + /* use local_pref from unicast route */ + memset(&new_attr, 0, sizeof(struct attr)); + new_attr = *bpi_interior->attr; + if (bpi_exterior + && (bpi_exterior->attr->flag + & ATTR_FLAG_BIT(BGP_ATTR_LOCAL_PREF))) { + new_attr.local_pref = + bpi_exterior->attr->local_pref; + new_attr.flag |= + ATTR_FLAG_BIT(BGP_ATTR_LOCAL_PREF); + } + + rfapiBgpInfoFilteredImportVPN( + it, FIF_ACTION_UPDATE, bpi_interior->peer, + NULL, /* rfd */ + pfx_exterior, NULL, afi, prd, &new_attr, + ZEBRA_ROUTE_BGP_DIRECT_EXT, + BGP_ROUTE_REDISTRIBUTE, &label); + } + } + if (list_adopted) { + struct listnode *node; + struct agg_node *an_bpi_exterior; + + for (ALL_LIST_ELEMENTS_RO(list_adopted, node, + an_bpi_exterior)) { + skiplist_delete(it->monitor_exterior_orphans, + an_bpi_exterior, NULL); + } + list_delete(&list_adopted); + } +} + +/* + * This function should be called after an interior VPN route + * has been deleted from an import_table. + * bpi_interior must still be valid, but it must already be detached + * from its route node and the route node's valid_interior_count + * must already be decremented. + * + * NB should also be called whenever an existing vpn interior route + * becomes invalid (e.g., valid_interior_count is decremented) + */ +void vnc_import_bgp_exterior_del_route_interior( + struct bgp *bgp, struct rfapi_import_table *it, + struct agg_node *rn_interior, /* VPN IT node */ + struct bgp_path_info *bpi_interior) /* VPN IT route */ +{ + const struct prefix *p = agg_node_get_prefix(rn_interior); + afi_t afi = family2afi(p->family); + struct agg_node *par; + struct bgp_path_info *bpi_exterior; + struct prefix *pfx_exterior; /* exterior pfx */ + void *cursor; + int rc; + + if (!VALID_INTERIOR_TYPE(bpi_interior->type)) { + vnc_zlog_debug_verbose( + "%s: type %d not valid interior type, skipping", + __func__, bpi_interior->type); + return; + } + + if (!bgp->rfapi_cfg->redist[afi][ZEBRA_ROUTE_BGP_DIRECT_EXT]) { + vnc_zlog_debug_verbose( + "%s: redist of exterior routes no enabled, skipping", + __func__); + return; + } + + if (it == bgp->rfapi->it_ce) { + vnc_zlog_debug_verbose("%s: it is it_ce, skipping", __func__); + return; + } + + /* If no exterior routes depend on this prefix, nothing to do */ + if (!RFAPI_HAS_MONITOR_EXTERIOR(rn_interior)) { + vnc_zlog_debug_verbose("%s: no exterior monitor, skipping", + __func__); + return; + } + + /*debugging */ + vnc_zlog_debug_verbose("%s: interior prefix=%pRN, bpi type=%d", + __func__, rn_interior, bpi_interior->type); + + /* + * Remove constructed routes based on the deleted interior route + */ + cursor = NULL; + for (rc = skiplist_next(RFAPI_MONITOR_EXTERIOR(rn_interior)->source, + (void **)&bpi_exterior, (void **)&pfx_exterior, + &cursor); + !rc; + rc = skiplist_next(RFAPI_MONITOR_EXTERIOR(rn_interior)->source, + (void **)&bpi_exterior, (void **)&pfx_exterior, + &cursor)) { + + struct prefix_rd *prd; + uint32_t label = 0; + + if (bpi_interior->extra) { + prd = &bpi_interior->extra->vnc.import.rd; + label = decode_label(&bpi_interior->extra->label[0]); + } else + prd = NULL; + + rfapiBgpInfoFilteredImportVPN( + it, FIF_ACTION_KILL, bpi_interior->peer, NULL, /* rfd */ + pfx_exterior, NULL, afi, prd, bpi_interior->attr, + ZEBRA_ROUTE_BGP_DIRECT_EXT, BGP_ROUTE_REDISTRIBUTE, + &label); + } + + /* + * If there are no remaining valid interior routes at this prefix, + * we need to look up the tree for a possible node to move monitors to + */ + if (RFAPI_MONITOR_EXTERIOR(rn_interior)->valid_interior_count) { + vnc_zlog_debug_verbose( + "%s: interior routes still present, skipping", + __func__); + return; + } + + /* + * Find nearest parent with at least one valid interior route + * If none is found, par will end up NULL, and we will move + * the monitors to the orphan list for this import table + */ + for (par = agg_node_parent(rn_interior); par; + par = agg_node_parent(par)) { + if (RFAPI_MONITOR_EXTERIOR(par)->valid_interior_count) + break; + } + + vnc_zlog_debug_verbose("%s: par=%p, ext src: %p", __func__, par, + RFAPI_MONITOR_EXTERIOR(rn_interior)->source); + + /* move all monitors */ + /* + * We will use and delete every element of the source skiplist + */ + while (!skiplist_first(RFAPI_MONITOR_EXTERIOR(rn_interior)->source, + (void **)&bpi_exterior, + (void **)&pfx_exterior)) { + + struct prefix *pfx_mon = prefix_new(); + + prefix_copy(pfx_mon, pfx_exterior); + + if (par) { + + struct bgp_path_info *bpi; + + /* + * Add monitor to parent node + */ + if (!RFAPI_MONITOR_EXTERIOR(par)->source) { + RFAPI_MONITOR_EXTERIOR(par)->source = + skiplist_new( + 0, NULL, prefix_free_lists); + agg_lock_node(par); /* sl */ + } + skiplist_insert(RFAPI_MONITOR_EXTERIOR(par)->source, + bpi_exterior, pfx_mon); + agg_lock_node(par); /* sl entry */ + + /* Add constructed exterior routes based on parent */ + for (bpi = par->info; bpi; bpi = bpi->next) { + + struct prefix_rd *prd; + struct attr new_attr; + uint32_t label = 0; + + if (bpi->type == ZEBRA_ROUTE_BGP_DIRECT_EXT) + continue; + + if (bpi->extra) { + prd = &bpi->extra->vnc.import.rd; + label = decode_label( + &bpi->extra->label[0]); + } else + prd = NULL; + + /* use local_pref from unicast route */ + memset(&new_attr, 0, sizeof(new_attr)); + new_attr = *bpi->attr; + if (bpi_exterior + && (bpi_exterior->attr->flag + & ATTR_FLAG_BIT(BGP_ATTR_LOCAL_PREF))) { + new_attr.local_pref = + bpi_exterior->attr->local_pref; + new_attr.flag |= ATTR_FLAG_BIT( + BGP_ATTR_LOCAL_PREF); + } + + rfapiBgpInfoFilteredImportVPN( + it, FIF_ACTION_UPDATE, bpi->peer, + NULL, /* rfd */ + pfx_exterior, NULL, afi, prd, &new_attr, + ZEBRA_ROUTE_BGP_DIRECT_EXT, + BGP_ROUTE_REDISTRIBUTE, &label); + } + + } else { + + /* + * No interior route for exterior's nexthop. Save + * monitor + * in orphan list to await future route. + */ + skiplist_insert(it->monitor_exterior_orphans, + bpi_exterior, pfx_mon); + } + + skiplist_delete_first( + RFAPI_MONITOR_EXTERIOR(rn_interior)->source); + agg_unlock_node(rn_interior); /* sl entry */ + } + if (skiplist_empty(RFAPI_MONITOR_EXTERIOR(rn_interior)->source)) { + skiplist_free(RFAPI_MONITOR_EXTERIOR(rn_interior)->source); + RFAPI_MONITOR_EXTERIOR(rn_interior)->source = NULL; + agg_unlock_node(rn_interior); /* sl itself */ + } +} + +/*********************************************************************** + * Generic add/delete unicast routes + ***********************************************************************/ + +void vnc_import_bgp_add_route(struct bgp *bgp, const struct prefix *prefix, + struct bgp_path_info *info) +{ + afi_t afi = family2afi(prefix->family); + + if (VNC_DEBUG(VERBOSE)) { + struct prefix pfx_nexthop; + + rfapiUnicastNexthop2Prefix(afi, info->attr, &pfx_nexthop); + vnc_zlog_debug_verbose("%s: pfx %pFX, nh %pFX", __func__, + prefix, &pfx_nexthop); + } +#if DEBUG_RHN_LIST + print_rhn_list(__func__, "ENTER "); +#endif + VNC_RHNCK(enter); + + if (!afi) { + flog_err(EC_LIB_DEVELOPMENT, "%s: can't get afi of prefix", + __func__); + return; + } + + if (!bgp->rfapi_cfg) { + vnc_zlog_debug_verbose("%s: bgp->rfapi_cfg is NULL, skipping", + __func__); + return; + } + + /* check vnc redist flag for bgp direct routes */ + if (!bgp->rfapi_cfg->redist[afi][ZEBRA_ROUTE_BGP_DIRECT]) { + vnc_zlog_debug_verbose( + "%s: bgp->rfapi_cfg->redist[afi=%d][type=%d=ZEBRA_ROUTE_BGP_DIRECT] is 0, skipping", + __func__, afi, ZEBRA_ROUTE_BGP_DIRECT); + return; + } + + switch (bgp->rfapi_cfg->redist_mode) { + case VNC_REDIST_MODE_PLAIN: + vnc_import_bgp_add_route_mode_plain(bgp, prefix, info); + break; + + case VNC_REDIST_MODE_RFG: + if (bgp->rfapi_cfg->rfg_redist) + vnc_import_bgp_add_route_mode_nvegroup( + bgp, prefix, info, bgp->rfapi_cfg->rfg_redist); + else + vnc_zlog_debug_verbose("%s: mode RFG but no redist RFG", + __func__); + break; + + case VNC_REDIST_MODE_RESOLVE_NVE: + vnc_import_bgp_add_route_mode_resolve_nve(bgp, prefix, info); + break; + } +#if DEBUG_RHN_LIST + print_rhn_list(__func__, "LEAVE "); +#endif + VNC_RHNCK(leave); +} + +/* + * "Withdrawing a Route" import process + */ +void vnc_import_bgp_del_route(struct bgp *bgp, const struct prefix *prefix, + struct bgp_path_info *info) /* unicast info */ +{ + afi_t afi = family2afi(prefix->family); + + assert(afi); + + { + struct prefix pfx_nexthop; + + rfapiUnicastNexthop2Prefix(afi, info->attr, &pfx_nexthop); + vnc_zlog_debug_verbose("%s: pfx %pFX, nh %pFX", __func__, + prefix, &pfx_nexthop); + } +#if DEBUG_RHN_LIST + print_rhn_list(__func__, "ENTER "); +#endif + VNC_RHNCK(enter); + + if (!bgp->rfapi_cfg) { + vnc_zlog_debug_verbose("%s: bgp->rfapi_cfg is NULL, skipping", + __func__); + return; + } + + /* check bgp redist flag for vnc direct ("vpn") routes */ + if (!bgp->rfapi_cfg->redist[afi][ZEBRA_ROUTE_BGP_DIRECT]) { + vnc_zlog_debug_verbose( + "%s: bgp redistribution of afi=%d VNC direct routes is off", + __func__, afi); + return; + } + + switch (bgp->rfapi_cfg->redist_mode) { + case VNC_REDIST_MODE_PLAIN: + vnc_import_bgp_del_route_mode_plain(bgp, prefix, info); + break; + + case VNC_REDIST_MODE_RFG: + if (bgp->rfapi_cfg->rfg_redist) + vnc_import_bgp_del_route_mode_nvegroup(bgp, prefix, + info); + else + vnc_zlog_debug_verbose("%s: mode RFG but no redist RFG", + __func__); + break; + + case VNC_REDIST_MODE_RESOLVE_NVE: + vnc_import_bgp_del_route_mode_resolve_nve(bgp, afi, prefix, + info); + break; + } +#if DEBUG_RHN_LIST + print_rhn_list(__func__, "LEAVE "); +#endif + VNC_RHNCK(leave); +} + + +/*********************************************************************** + * Enable/Disable + ***********************************************************************/ + +void vnc_import_bgp_redist_enable(struct bgp *bgp, afi_t afi) +{ + /* iterate over bgp unicast v4 and v6 routes, call + * vnc_import_bgp_add_route */ + + struct bgp_dest *dest; + + vnc_zlog_debug_verbose("%s: entry, afi=%d", __func__, afi); + + if (bgp->rfapi_cfg->redist[afi][ZEBRA_ROUTE_BGP_DIRECT]) { + vnc_zlog_debug_verbose( + "%s: already enabled for afi %d, skipping", __func__, + afi); + return; + } + bgp->rfapi_cfg->redist[afi][ZEBRA_ROUTE_BGP_DIRECT] = 1; + + for (dest = bgp_table_top(bgp->rib[afi][SAFI_UNICAST]); dest; + dest = bgp_route_next(dest)) { + + struct bgp_path_info *bpi; + + for (bpi = bgp_dest_get_bgp_path_info(dest); bpi; + bpi = bpi->next) { + + if (CHECK_FLAG(bpi->flags, BGP_PATH_REMOVED)) + continue; + + vnc_import_bgp_add_route(bgp, bgp_dest_get_prefix(dest), + bpi); + } + } + vnc_zlog_debug_verbose( + "%s: set redist[afi=%d][type=%d=ZEBRA_ROUTE_BGP_DIRECT] return", + __func__, afi, ZEBRA_ROUTE_BGP_DIRECT); +} + +void vnc_import_bgp_exterior_redist_enable(struct bgp *bgp, afi_t afi) +{ + struct bgp *bgp_exterior; + struct bgp_dest *dest; + + bgp_exterior = bgp->rfapi_cfg->redist_bgp_exterior_view; + + if (bgp->rfapi_cfg->redist[afi][ZEBRA_ROUTE_BGP_DIRECT_EXT]) { + vnc_zlog_debug_verbose( + "%s: already enabled for afi %d, skipping", __func__, + afi); + return; + } + bgp->rfapi_cfg->redist[afi][ZEBRA_ROUTE_BGP_DIRECT_EXT] = 1; + + if (!bgp_exterior) { + vnc_zlog_debug_verbose( + "%s: no exterior view set yet, no routes to import yet", + __func__); + return; + } + + for (dest = bgp_table_top(bgp_exterior->rib[afi][SAFI_UNICAST]); dest; + dest = bgp_route_next(dest)) { + + struct bgp_path_info *bpi; + + for (bpi = bgp_dest_get_bgp_path_info(dest); bpi; + bpi = bpi->next) { + + if (CHECK_FLAG(bpi->flags, BGP_PATH_REMOVED)) + continue; + + vnc_import_bgp_exterior_add_route( + bgp_exterior, bgp_dest_get_prefix(dest), bpi); + } + } + vnc_zlog_debug_verbose( + "%s: set redist[afi=%d][type=%d=ZEBRA_ROUTE_BGP_DIRECT] return", + __func__, afi, ZEBRA_ROUTE_BGP_DIRECT); +} + +/* + * This function is for populating a newly-created Import Table + */ +void vnc_import_bgp_exterior_redist_enable_it( + struct bgp *bgp, afi_t afi, struct rfapi_import_table *it_only) +{ + struct bgp *bgp_exterior; + struct bgp_dest *dest; + + vnc_zlog_debug_verbose("%s: entry", __func__); + + bgp_exterior = bgp->rfapi_cfg->redist_bgp_exterior_view; + + if (!bgp->rfapi_cfg->redist[afi][ZEBRA_ROUTE_BGP_DIRECT_EXT]) { + vnc_zlog_debug_verbose("%s: not enabled for afi %d, skipping", + __func__, afi); + return; + } + + if (!bgp_exterior) { + vnc_zlog_debug_verbose( + "%s: no exterior view set yet, no routes to import yet", + __func__); + return; + } + + for (dest = bgp_table_top(bgp_exterior->rib[afi][SAFI_UNICAST]); dest; + dest = bgp_route_next(dest)) { + + struct bgp_path_info *bpi; + + for (bpi = bgp_dest_get_bgp_path_info(dest); bpi; + bpi = bpi->next) { + + if (CHECK_FLAG(bpi->flags, BGP_PATH_REMOVED)) + continue; + + vnc_import_bgp_exterior_add_route_it( + bgp_exterior, bgp_dest_get_prefix(dest), bpi, + it_only); + } + } +} + + +void vnc_import_bgp_redist_disable(struct bgp *bgp, afi_t afi) +{ + /* + * iterate over vpn routes, find routes of type ZEBRA_ROUTE_BGP_DIRECT, + * delete (call timer expire immediately) + */ + struct bgp_dest *dest1; + struct bgp_dest *dest2; + + vnc_zlog_debug_verbose("%s: entry", __func__); + + if (!bgp->rfapi_cfg->redist[afi][ZEBRA_ROUTE_BGP_DIRECT]) { + vnc_zlog_debug_verbose( + "%s: already disabled for afi %d, skipping", __func__, + afi); + return; + } + + /* + * Two-level table for SAFI_MPLS_VPN + * Be careful when changing the things we iterate over + */ + for (dest1 = bgp_table_top(bgp->rib[afi][SAFI_MPLS_VPN]); dest1; + dest1 = bgp_route_next(dest1)) { + const struct prefix *dest1_p; + + if (!bgp_dest_has_bgp_path_info_data(dest1)) + continue; + + dest1_p = bgp_dest_get_prefix(dest1); + for (dest2 = bgp_table_top(bgp_dest_get_bgp_table_info(dest1)); + dest2; dest2 = bgp_route_next(dest2)) { + const struct prefix *dest2_p = + bgp_dest_get_prefix(dest2); + struct bgp_path_info *bpi; + struct bgp_path_info *nextbpi; + + for (bpi = bgp_dest_get_bgp_path_info(dest2); bpi; + bpi = nextbpi) { + + nextbpi = bpi->next; + + if (bpi->type != ZEBRA_ROUTE_BGP_DIRECT) + continue; + + struct rfapi_descriptor *rfd; + vncHDBgpDirect.peer = bpi->peer; + + assert(bpi->extra); + + rfd = bpi->extra->vnc.export.rfapi_handle; + + vnc_zlog_debug_verbose( + "%s: deleting bpi=%p, bpi->peer=%p, bpi->type=%d, bpi->sub_type=%d, bpi->extra->vnc.export.rfapi_handle=%p [passing rfd=%p]", + __func__, bpi, bpi->peer, bpi->type, + bpi->sub_type, + (bpi->extra ? bpi->extra->vnc.export + .rfapi_handle + : NULL), + rfd); + + del_vnc_route(rfd, bpi->peer, bgp, + SAFI_MPLS_VPN, dest2_p, + (struct prefix_rd *)dest1_p, + bpi->type, bpi->sub_type, NULL, + 1); /* kill */ + + vncHDBgpDirect.peer = NULL; + } + } + } + /* Clear RHN list */ + if (bgp->rfapi->resolve_nve_nexthop) { + struct prefix_bag *pb; + struct bgp_path_info *info; + while (!skiplist_first(bgp->rfapi->resolve_nve_nexthop, NULL, + (void *)&pb)) { + info = pb->ubpi; + skiplist_delete_first(bgp->rfapi->resolve_nve_nexthop); + bgp_path_info_unlock(info); + } + } + + bgp->rfapi_cfg->redist[afi][ZEBRA_ROUTE_BGP_DIRECT] = 0; + vnc_zlog_debug_verbose("%s: return", __func__); +} + + +void vnc_import_bgp_exterior_redist_disable(struct bgp *bgp, afi_t afi) +{ + struct rfapi_cfg *hc = bgp->rfapi_cfg; + struct bgp *bgp_exterior = hc->redist_bgp_exterior_view; + + vnc_zlog_debug_verbose("%s: entry", __func__); + + if (!hc->redist[afi][ZEBRA_ROUTE_BGP_DIRECT_EXT]) { + vnc_zlog_debug_verbose( + "%s: already disabled for afi %d, skipping", __func__, + afi); + return; + } + + if (!bgp_exterior) { + vnc_zlog_debug_verbose( + "%s: bgp exterior view not defined, skipping", + __func__); + return; + } + + + { + struct bgp_dest *dest; + for (dest = bgp_table_top(bgp_exterior->rib[afi][SAFI_UNICAST]); + dest; dest = bgp_route_next(dest)) { + + struct bgp_path_info *bpi; + + for (bpi = bgp_dest_get_bgp_path_info(dest); bpi; + bpi = bpi->next) { + + if (CHECK_FLAG(bpi->flags, BGP_PATH_REMOVED)) + continue; + + vnc_import_bgp_exterior_del_route( + bgp_exterior, bgp_dest_get_prefix(dest), + bpi); + } + } +#if DEBUG_RHN_LIST + print_rhn_list(__func__, NULL); +#endif + } + + bgp->rfapi_cfg->redist[afi][ZEBRA_ROUTE_BGP_DIRECT_EXT] = 0; + vnc_zlog_debug_verbose("%s: return", __func__); +} |