4801 lines
117 KiB
C
4801 lines
117 KiB
C
|
// SPDX-License-Identifier: GPL-2.0-or-later
|
||
|
/*
|
||
|
* Copyright 2009-2016, LabN Consulting, L.L.C.
|
||
|
*/
|
||
|
|
||
|
/*
|
||
|
* File: rfapi_import.c
|
||
|
* Purpose: Handle import of routes from BGP to RFAPI
|
||
|
*/
|
||
|
|
||
|
#include "lib/zebra.h"
|
||
|
#include "lib/prefix.h"
|
||
|
#include "lib/agg_table.h"
|
||
|
#include "lib/vty.h"
|
||
|
#include "lib/memory.h"
|
||
|
#include "lib/log.h"
|
||
|
#include "lib/skiplist.h"
|
||
|
#include "frrevent.h"
|
||
|
#include "lib/stream.h"
|
||
|
#include "lib/lib_errors.h"
|
||
|
|
||
|
#include "bgpd/bgpd.h"
|
||
|
#include "bgpd/bgp_ecommunity.h"
|
||
|
#include "bgpd/bgp_attr.h"
|
||
|
#include "bgpd/bgp_route.h"
|
||
|
#include "bgpd/bgp_mplsvpn.h" /* prefix_rd2str() */
|
||
|
#include "bgpd/bgp_vnc_types.h"
|
||
|
#include "bgpd/bgp_rd.h"
|
||
|
|
||
|
#include "bgpd/rfapi/rfapi.h"
|
||
|
#include "bgpd/rfapi/bgp_rfapi_cfg.h"
|
||
|
#include "bgpd/rfapi/rfapi_backend.h"
|
||
|
#include "bgpd/rfapi/rfapi_import.h"
|
||
|
#include "bgpd/rfapi/rfapi_private.h"
|
||
|
#include "bgpd/rfapi/rfapi_monitor.h"
|
||
|
#include "bgpd/rfapi/rfapi_nve_addr.h"
|
||
|
#include "bgpd/rfapi/rfapi_vty.h"
|
||
|
#include "bgpd/rfapi/vnc_export_bgp.h"
|
||
|
#include "bgpd/rfapi/vnc_export_bgp_p.h"
|
||
|
#include "bgpd/rfapi/vnc_zebra.h"
|
||
|
#include "bgpd/rfapi/vnc_import_bgp.h"
|
||
|
#include "bgpd/rfapi/vnc_import_bgp_p.h"
|
||
|
#include "bgpd/rfapi/rfapi_rib.h"
|
||
|
#include "bgpd/rfapi/rfapi_encap_tlv.h"
|
||
|
#include "bgpd/rfapi/vnc_debug.h"
|
||
|
|
||
|
#undef DEBUG_MONITOR_MOVE_SHORTER
|
||
|
#undef DEBUG_RETURNED_NHL
|
||
|
#undef DEBUG_ROUTE_COUNTERS
|
||
|
#undef DEBUG_ENCAP_MONITOR
|
||
|
#undef DEBUG_L2_EXTRA
|
||
|
#undef DEBUG_IT_NODES
|
||
|
#undef DEBUG_BI_SEARCH
|
||
|
|
||
|
/*
|
||
|
* Allocated for each withdraw timer instance; freed when the timer
|
||
|
* expires or is canceled
|
||
|
*/
|
||
|
struct rfapi_withdraw {
|
||
|
struct rfapi_import_table *import_table;
|
||
|
struct agg_node *node;
|
||
|
struct bgp_path_info *info;
|
||
|
safi_t safi; /* used only for bulk operations */
|
||
|
/*
|
||
|
* For import table node reference count checking (i.e., debugging).
|
||
|
* Normally when a timer expires, lockoffset should be 0. However, if
|
||
|
* the timer expiration function is called directly (e.g.,
|
||
|
* rfapiExpireVpnNow), the node could be locked by a preceding
|
||
|
* agg_route_top() or agg_route_next() in a loop, so we need to pass
|
||
|
* this value in.
|
||
|
*/
|
||
|
int lockoffset;
|
||
|
};
|
||
|
|
||
|
/*
|
||
|
* DEBUG FUNCTION
|
||
|
* Count remote routes and compare with actively-maintained values.
|
||
|
* Abort if they disagree.
|
||
|
*/
|
||
|
void rfapiCheckRouteCount(void)
|
||
|
{
|
||
|
struct bgp *bgp = bgp_get_default();
|
||
|
struct rfapi *h;
|
||
|
struct rfapi_import_table *it;
|
||
|
afi_t afi;
|
||
|
|
||
|
assert(bgp);
|
||
|
|
||
|
h = bgp->rfapi;
|
||
|
assert(h);
|
||
|
|
||
|
for (it = h->imports; it; it = it->next) {
|
||
|
for (afi = AFI_IP; afi < AFI_MAX; ++afi) {
|
||
|
|
||
|
struct agg_table *rt;
|
||
|
struct agg_node *rn;
|
||
|
|
||
|
int holddown_count = 0;
|
||
|
int imported_count = 0;
|
||
|
int remote_count = 0;
|
||
|
|
||
|
rt = it->imported_vpn[afi];
|
||
|
|
||
|
for (rn = agg_route_top(rt); rn;
|
||
|
rn = agg_route_next(rn)) {
|
||
|
struct bgp_path_info *bpi;
|
||
|
struct bgp_path_info *next;
|
||
|
|
||
|
for (bpi = rn->info; bpi; bpi = next) {
|
||
|
next = bpi->next;
|
||
|
|
||
|
if (CHECK_FLAG(bpi->flags,
|
||
|
BGP_PATH_REMOVED)) {
|
||
|
++holddown_count;
|
||
|
|
||
|
} else {
|
||
|
if (!RFAPI_LOCAL_BI(bpi)) {
|
||
|
if (RFAPI_DIRECT_IMPORT_BI(
|
||
|
bpi)) {
|
||
|
++imported_count;
|
||
|
} else {
|
||
|
++remote_count;
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
|
||
|
if (it->holddown_count[afi] != holddown_count) {
|
||
|
vnc_zlog_debug_verbose(
|
||
|
"%s: it->holddown_count %d != holddown_count %d",
|
||
|
__func__, it->holddown_count[afi],
|
||
|
holddown_count);
|
||
|
assert(0);
|
||
|
}
|
||
|
if (it->remote_count[afi] != remote_count) {
|
||
|
vnc_zlog_debug_verbose(
|
||
|
"%s: it->remote_count %d != remote_count %d",
|
||
|
__func__, it->remote_count[afi],
|
||
|
remote_count);
|
||
|
assert(0);
|
||
|
}
|
||
|
if (it->imported_count[afi] != imported_count) {
|
||
|
vnc_zlog_debug_verbose(
|
||
|
"%s: it->imported_count %d != imported_count %d",
|
||
|
__func__, it->imported_count[afi],
|
||
|
imported_count);
|
||
|
assert(0);
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
|
||
|
#ifdef DEBUG_ROUTE_COUNTERS
|
||
|
#define VNC_ITRCCK do {rfapiCheckRouteCount();} while (0)
|
||
|
#else
|
||
|
#define VNC_ITRCCK
|
||
|
#endif
|
||
|
|
||
|
/*
|
||
|
* Validate reference count for a node in an import table
|
||
|
*
|
||
|
* Normally lockoffset is 0 for nodes in quiescent state. However,
|
||
|
* agg_unlock_node will delete the node if it is called when
|
||
|
* node->lock == 1, and we have to validate the refcount before
|
||
|
* the node is deleted. In this case, we specify lockoffset 1.
|
||
|
*/
|
||
|
void rfapiCheckRefcount(struct agg_node *rn, safi_t safi, int lockoffset)
|
||
|
{
|
||
|
unsigned int count_bpi = 0;
|
||
|
unsigned int count_monitor = 0;
|
||
|
struct bgp_path_info *bpi;
|
||
|
struct rfapi_monitor_encap *hme;
|
||
|
struct rfapi_monitor_vpn *hmv;
|
||
|
|
||
|
for (bpi = rn->info; bpi; bpi = bpi->next)
|
||
|
++count_bpi;
|
||
|
|
||
|
|
||
|
if (rn->aggregate) {
|
||
|
++count_monitor; /* rfapi_it_extra */
|
||
|
|
||
|
switch (safi) {
|
||
|
void *cursor;
|
||
|
int rc;
|
||
|
|
||
|
case SAFI_ENCAP:
|
||
|
for (hme = RFAPI_MONITOR_ENCAP(rn); hme;
|
||
|
hme = hme->next)
|
||
|
++count_monitor;
|
||
|
break;
|
||
|
|
||
|
case SAFI_MPLS_VPN:
|
||
|
|
||
|
for (hmv = RFAPI_MONITOR_VPN(rn); hmv; hmv = hmv->next)
|
||
|
++count_monitor;
|
||
|
|
||
|
if (RFAPI_MONITOR_EXTERIOR(rn)->source) {
|
||
|
++count_monitor; /* sl */
|
||
|
cursor = NULL;
|
||
|
for (rc = skiplist_next(
|
||
|
RFAPI_MONITOR_EXTERIOR(rn)->source,
|
||
|
NULL, NULL, &cursor);
|
||
|
!rc;
|
||
|
rc = skiplist_next(
|
||
|
RFAPI_MONITOR_EXTERIOR(rn)->source,
|
||
|
NULL, NULL, &cursor)) {
|
||
|
|
||
|
++count_monitor; /* sl entry */
|
||
|
}
|
||
|
}
|
||
|
break;
|
||
|
|
||
|
case SAFI_UNSPEC:
|
||
|
case SAFI_UNICAST:
|
||
|
case SAFI_MULTICAST:
|
||
|
case SAFI_EVPN:
|
||
|
case SAFI_LABELED_UNICAST:
|
||
|
case SAFI_FLOWSPEC:
|
||
|
case SAFI_MAX:
|
||
|
assert(!"Passed in safi should be impossible");
|
||
|
}
|
||
|
}
|
||
|
|
||
|
if (count_bpi + count_monitor + lockoffset
|
||
|
!= agg_node_get_lock_count(rn)) {
|
||
|
vnc_zlog_debug_verbose(
|
||
|
"%s: count_bpi=%d, count_monitor=%d, lockoffset=%d, rn->lock=%d",
|
||
|
__func__, count_bpi, count_monitor, lockoffset,
|
||
|
agg_node_get_lock_count(rn));
|
||
|
assert(0);
|
||
|
}
|
||
|
}
|
||
|
|
||
|
/*
|
||
|
* Perform deferred rfapi_close operations that were queued
|
||
|
* during callbacks.
|
||
|
*/
|
||
|
static wq_item_status rfapi_deferred_close_workfunc(struct work_queue *q,
|
||
|
void *data)
|
||
|
{
|
||
|
struct rfapi_descriptor *rfd = data;
|
||
|
struct rfapi *h = q->spec.data;
|
||
|
|
||
|
assert(!(h->flags & RFAPI_INCALLBACK));
|
||
|
rfapi_close(rfd);
|
||
|
vnc_zlog_debug_verbose("%s: completed deferred close on handle %p",
|
||
|
__func__, rfd);
|
||
|
return WQ_SUCCESS;
|
||
|
}
|
||
|
|
||
|
/*
|
||
|
* Extract layer 2 option from Encap TLVS in BGP attrs
|
||
|
*/
|
||
|
int rfapiGetL2o(struct attr *attr, struct rfapi_l2address_option *l2o)
|
||
|
{
|
||
|
if (attr) {
|
||
|
struct bgp_attr_encap_subtlv *pEncap;
|
||
|
|
||
|
for (pEncap = bgp_attr_get_vnc_subtlvs(attr); pEncap;
|
||
|
pEncap = pEncap->next) {
|
||
|
|
||
|
if (pEncap->type == BGP_VNC_SUBTLV_TYPE_RFPOPTION) {
|
||
|
if (pEncap->value[0]
|
||
|
== RFAPI_VN_OPTION_TYPE_L2ADDR) {
|
||
|
|
||
|
if (pEncap->value[1] == 14) {
|
||
|
memcpy(l2o->macaddr.octet,
|
||
|
pEncap->value + 2,
|
||
|
ETH_ALEN);
|
||
|
l2o->label =
|
||
|
((pEncap->value[10]
|
||
|
>> 4)
|
||
|
& 0x0f)
|
||
|
+ ((pEncap->value[9]
|
||
|
<< 4)
|
||
|
& 0xff0)
|
||
|
+ ((pEncap->value[8]
|
||
|
<< 12)
|
||
|
& 0xff000);
|
||
|
|
||
|
l2o->local_nve_id =
|
||
|
pEncap->value[12];
|
||
|
|
||
|
l2o->logical_net_id =
|
||
|
(pEncap->value[15]
|
||
|
& 0xff)
|
||
|
+ ((pEncap->value[14]
|
||
|
<< 8)
|
||
|
& 0xff00)
|
||
|
+ ((pEncap->value[13]
|
||
|
<< 16)
|
||
|
& 0xff0000);
|
||
|
}
|
||
|
|
||
|
return 0;
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
|
||
|
return ENOENT;
|
||
|
}
|
||
|
|
||
|
/*
|
||
|
* Extract the lifetime from the Tunnel Encap attribute of a route in
|
||
|
* an import table
|
||
|
*/
|
||
|
int rfapiGetVncLifetime(struct attr *attr, uint32_t *lifetime)
|
||
|
{
|
||
|
struct bgp_attr_encap_subtlv *pEncap;
|
||
|
|
||
|
*lifetime = RFAPI_INFINITE_LIFETIME; /* default to infinite */
|
||
|
|
||
|
if (attr) {
|
||
|
|
||
|
for (pEncap = bgp_attr_get_vnc_subtlvs(attr); pEncap;
|
||
|
pEncap = pEncap->next) {
|
||
|
|
||
|
if (pEncap->type
|
||
|
== BGP_VNC_SUBTLV_TYPE_LIFETIME) { /* lifetime */
|
||
|
if (pEncap->length == 4) {
|
||
|
memcpy(lifetime, pEncap->value, 4);
|
||
|
*lifetime = ntohl(*lifetime);
|
||
|
return 0;
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
|
||
|
return ENOENT;
|
||
|
}
|
||
|
|
||
|
/*
|
||
|
* Look for UN address in Encap attribute
|
||
|
*/
|
||
|
int rfapiGetVncTunnelUnAddr(struct attr *attr, struct prefix *p)
|
||
|
{
|
||
|
struct bgp_attr_encap_subtlv *pEncap;
|
||
|
bgp_encap_types tun_type = BGP_ENCAP_TYPE_MPLS;/*Default tunnel type*/
|
||
|
|
||
|
bgp_attr_extcom_tunnel_type(attr, &tun_type);
|
||
|
if (tun_type == BGP_ENCAP_TYPE_MPLS) {
|
||
|
if (!p)
|
||
|
return 0;
|
||
|
/* MPLS carries UN address in next hop */
|
||
|
rfapiNexthop2Prefix(attr, p);
|
||
|
if (p->family != AF_UNSPEC)
|
||
|
return 0;
|
||
|
|
||
|
return ENOENT;
|
||
|
}
|
||
|
if (attr) {
|
||
|
for (pEncap = attr->encap_subtlvs; pEncap;
|
||
|
pEncap = pEncap->next) {
|
||
|
|
||
|
if (pEncap->type
|
||
|
== BGP_ENCAP_SUBTLV_TYPE_REMOTE_ENDPOINT) { /* un
|
||
|
addr
|
||
|
*/
|
||
|
switch (pEncap->length) {
|
||
|
case 8:
|
||
|
if (p) {
|
||
|
p->family = AF_INET;
|
||
|
p->prefixlen = IPV4_MAX_BITLEN;
|
||
|
memcpy(p->u.val, pEncap->value,
|
||
|
4);
|
||
|
}
|
||
|
return 0;
|
||
|
|
||
|
case 20:
|
||
|
if (p) {
|
||
|
p->family = AF_INET6;
|
||
|
p->prefixlen = IPV6_MAX_BITLEN;
|
||
|
memcpy(p->u.val, pEncap->value,
|
||
|
16);
|
||
|
}
|
||
|
return 0;
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
|
||
|
return ENOENT;
|
||
|
}
|
||
|
|
||
|
/*
|
||
|
* Get UN address wherever it might be
|
||
|
*/
|
||
|
int rfapiGetUnAddrOfVpnBi(struct bgp_path_info *bpi, struct prefix *p)
|
||
|
{
|
||
|
/* If it's in this route's VNC attribute, we're done */
|
||
|
if (!rfapiGetVncTunnelUnAddr(bpi->attr, p))
|
||
|
return 0;
|
||
|
/*
|
||
|
* Otherwise, see if it's cached from a corresponding ENCAP SAFI
|
||
|
* advertisement
|
||
|
*/
|
||
|
if (bpi->extra) {
|
||
|
switch (bpi->extra->vnc->vnc.import.un_family) {
|
||
|
case AF_INET:
|
||
|
if (p) {
|
||
|
p->family =
|
||
|
bpi->extra->vnc->vnc.import.un_family;
|
||
|
p->u.prefix4 =
|
||
|
bpi->extra->vnc->vnc.import.un.addr4;
|
||
|
p->prefixlen = IPV4_MAX_BITLEN;
|
||
|
}
|
||
|
return 0;
|
||
|
case AF_INET6:
|
||
|
if (p) {
|
||
|
p->family =
|
||
|
bpi->extra->vnc->vnc.import.un_family;
|
||
|
p->u.prefix6 =
|
||
|
bpi->extra->vnc->vnc.import.un.addr6;
|
||
|
p->prefixlen = IPV6_MAX_BITLEN;
|
||
|
}
|
||
|
return 0;
|
||
|
default:
|
||
|
if (p)
|
||
|
p->family = AF_UNSPEC;
|
||
|
#ifdef DEBUG_ENCAP_MONITOR
|
||
|
vnc_zlog_debug_verbose(
|
||
|
"%s: bpi->extra->vnc.import.un_family is 0, no UN addr",
|
||
|
__func__);
|
||
|
#endif
|
||
|
break;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
return ENOENT;
|
||
|
}
|
||
|
|
||
|
|
||
|
/*
|
||
|
* Make a new bgp_path_info from gathered parameters
|
||
|
*/
|
||
|
static struct bgp_path_info *rfapiBgpInfoCreate(struct attr *attr,
|
||
|
struct peer *peer, void *rfd,
|
||
|
struct prefix_rd *prd,
|
||
|
uint8_t type, uint8_t sub_type,
|
||
|
uint32_t *label)
|
||
|
{
|
||
|
struct bgp_path_info *new;
|
||
|
struct bgp_labels bgp_labels = {};
|
||
|
|
||
|
new = info_make(type, sub_type, 0, peer, attr, NULL);
|
||
|
|
||
|
new->attr = bgp_attr_intern(attr);
|
||
|
|
||
|
bgp_path_info_extra_get(new);
|
||
|
new->extra->vnc = XCALLOC(MTYPE_BGP_ROUTE_EXTRA_VNC,
|
||
|
sizeof(struct bgp_path_info_extra_vnc));
|
||
|
if (prd) {
|
||
|
new->extra->vnc->vnc.import.rd = *prd;
|
||
|
new->extra->vnc->vnc.import.create_time = monotime(NULL);
|
||
|
}
|
||
|
if (label && *label != MPLS_INVALID_LABEL) {
|
||
|
encode_label(*label, &bgp_labels.label[0]);
|
||
|
bgp_labels.num_labels = 1;
|
||
|
new->extra->labels = bgp_labels_intern(&bgp_labels);
|
||
|
}
|
||
|
|
||
|
peer_lock(peer);
|
||
|
|
||
|
return new;
|
||
|
}
|
||
|
|
||
|
/*
|
||
|
* Frees bgp_path_info as used in import tables (parts are not
|
||
|
* allocated exactly the way they are in the main RIBs)
|
||
|
*/
|
||
|
static void rfapiBgpInfoFree(struct bgp_path_info *goner)
|
||
|
{
|
||
|
if (!goner)
|
||
|
return;
|
||
|
|
||
|
if (goner->peer) {
|
||
|
vnc_zlog_debug_verbose("%s: calling peer_unlock(%p), #%d",
|
||
|
__func__, goner->peer,
|
||
|
goner->peer->lock);
|
||
|
peer_unlock(goner->peer);
|
||
|
}
|
||
|
|
||
|
bgp_attr_unintern(&goner->attr);
|
||
|
|
||
|
if (goner->extra)
|
||
|
bgp_path_info_extra_free(&goner->extra);
|
||
|
XFREE(MTYPE_BGP_ROUTE, goner);
|
||
|
}
|
||
|
|
||
|
struct rfapi_import_table *rfapiMacImportTableGetNoAlloc(struct bgp *bgp,
|
||
|
uint32_t lni)
|
||
|
{
|
||
|
struct rfapi *h;
|
||
|
struct rfapi_import_table *it = NULL;
|
||
|
uintptr_t lni_as_ptr = lni;
|
||
|
|
||
|
h = bgp->rfapi;
|
||
|
if (!h)
|
||
|
return NULL;
|
||
|
|
||
|
if (!h->import_mac)
|
||
|
return NULL;
|
||
|
|
||
|
if (skiplist_search(h->import_mac, (void *)lni_as_ptr, (void **)&it))
|
||
|
return NULL;
|
||
|
|
||
|
return it;
|
||
|
}
|
||
|
|
||
|
struct rfapi_import_table *rfapiMacImportTableGet(struct bgp *bgp, uint32_t lni)
|
||
|
{
|
||
|
struct rfapi *h;
|
||
|
struct rfapi_import_table *it = NULL;
|
||
|
uintptr_t lni_as_ptr = lni;
|
||
|
|
||
|
h = bgp->rfapi;
|
||
|
assert(h);
|
||
|
|
||
|
if (!h->import_mac) {
|
||
|
/* default cmp is good enough for LNI */
|
||
|
h->import_mac = skiplist_new(0, NULL, NULL);
|
||
|
}
|
||
|
|
||
|
if (skiplist_search(h->import_mac, (void *)lni_as_ptr, (void **)&it)) {
|
||
|
|
||
|
struct ecommunity *enew;
|
||
|
struct ecommunity_val eval;
|
||
|
afi_t afi;
|
||
|
|
||
|
it = XCALLOC(MTYPE_RFAPI_IMPORTTABLE,
|
||
|
sizeof(struct rfapi_import_table));
|
||
|
/* set RT list of new import table based on LNI */
|
||
|
memset((char *)&eval, 0, sizeof(eval));
|
||
|
eval.val[0] = 0; /* VNC L2VPN */
|
||
|
eval.val[1] = 2; /* VNC L2VPN */
|
||
|
eval.val[5] = (lni >> 16) & 0xff;
|
||
|
eval.val[6] = (lni >> 8) & 0xff;
|
||
|
eval.val[7] = (lni >> 0) & 0xff;
|
||
|
|
||
|
enew = ecommunity_new();
|
||
|
ecommunity_add_val(enew, &eval, false, false);
|
||
|
it->rt_import_list = enew;
|
||
|
|
||
|
for (afi = AFI_IP; afi < AFI_MAX; ++afi) {
|
||
|
it->imported_vpn[afi] = agg_table_init();
|
||
|
it->imported_encap[afi] = agg_table_init();
|
||
|
}
|
||
|
|
||
|
it->l2_logical_net_id = lni;
|
||
|
|
||
|
skiplist_insert(h->import_mac, (void *)lni_as_ptr, it);
|
||
|
}
|
||
|
|
||
|
assert(it);
|
||
|
return it;
|
||
|
}
|
||
|
|
||
|
/*
|
||
|
* Implement MONITOR_MOVE_SHORTER(original_node) from
|
||
|
* RFAPI-Import-Event-Handling.txt
|
||
|
*
|
||
|
* Returns pointer to the list of moved monitors
|
||
|
*/
|
||
|
static struct rfapi_monitor_vpn *
|
||
|
rfapiMonitorMoveShorter(struct agg_node *original_vpn_node, int lockoffset)
|
||
|
{
|
||
|
struct bgp_path_info *bpi;
|
||
|
struct agg_node *par;
|
||
|
struct rfapi_monitor_vpn *m;
|
||
|
struct rfapi_monitor_vpn *mlast;
|
||
|
struct rfapi_monitor_vpn *moved;
|
||
|
int movecount = 0;
|
||
|
int parent_already_refcounted = 0;
|
||
|
|
||
|
RFAPI_CHECK_REFCOUNT(original_vpn_node, SAFI_MPLS_VPN, lockoffset);
|
||
|
|
||
|
#ifdef DEBUG_MONITOR_MOVE_SHORTER
|
||
|
{
|
||
|
vnc_zlog_debug_verbose("%s: called with node pfx=%pFX",
|
||
|
__func__, &original_vpn_node->p);
|
||
|
}
|
||
|
#endif
|
||
|
|
||
|
/*
|
||
|
* 1. If there is at least one bpi (either regular route or
|
||
|
* route marked as withdrawn, with a pending timer) at
|
||
|
* original_node with a valid UN address, we're done. Return.
|
||
|
*/
|
||
|
for (bpi = original_vpn_node->info; bpi; bpi = bpi->next) {
|
||
|
struct prefix pfx;
|
||
|
|
||
|
if (!rfapiGetUnAddrOfVpnBi(bpi, &pfx)) {
|
||
|
#ifdef DEBUG_MONITOR_MOVE_SHORTER
|
||
|
vnc_zlog_debug_verbose(
|
||
|
"%s: have valid UN at original node, no change",
|
||
|
__func__);
|
||
|
#endif
|
||
|
return NULL;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
/*
|
||
|
* 2. Travel up the tree (toward less-specific prefixes) from
|
||
|
* original_node to find the first node that has at least
|
||
|
* one route (even if it is only a withdrawn route) with a
|
||
|
* valid UN address. Call this node "Node P."
|
||
|
*/
|
||
|
for (par = agg_node_parent(original_vpn_node); par;
|
||
|
par = agg_node_parent(par)) {
|
||
|
for (bpi = par->info; bpi; bpi = bpi->next) {
|
||
|
struct prefix pfx;
|
||
|
if (!rfapiGetUnAddrOfVpnBi(bpi, &pfx)) {
|
||
|
break;
|
||
|
}
|
||
|
}
|
||
|
if (bpi)
|
||
|
break;
|
||
|
}
|
||
|
|
||
|
if (par) {
|
||
|
RFAPI_CHECK_REFCOUNT(par, SAFI_MPLS_VPN, 0);
|
||
|
}
|
||
|
|
||
|
/*
|
||
|
* If no less-specific routes, try to use the 0/0 node
|
||
|
*/
|
||
|
if (!par) {
|
||
|
const struct prefix *p;
|
||
|
/* this isn't necessarily 0/0 */
|
||
|
par = agg_route_table_top(original_vpn_node);
|
||
|
|
||
|
if (par)
|
||
|
p = agg_node_get_prefix(par);
|
||
|
/*
|
||
|
* If we got the top node but it wasn't 0/0,
|
||
|
* ignore it
|
||
|
*/
|
||
|
if (par && p->prefixlen) {
|
||
|
agg_unlock_node(par); /* maybe free */
|
||
|
par = NULL;
|
||
|
}
|
||
|
|
||
|
if (par) {
|
||
|
++parent_already_refcounted;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
/*
|
||
|
* Create 0/0 node if it isn't there
|
||
|
*/
|
||
|
if (!par) {
|
||
|
struct prefix pfx_default;
|
||
|
const struct prefix *p = agg_node_get_prefix(original_vpn_node);
|
||
|
|
||
|
memset(&pfx_default, 0, sizeof(pfx_default));
|
||
|
pfx_default.family = p->family;
|
||
|
|
||
|
/* creates default node if none exists */
|
||
|
par = agg_node_get(agg_get_table(original_vpn_node),
|
||
|
&pfx_default);
|
||
|
++parent_already_refcounted;
|
||
|
}
|
||
|
|
||
|
/*
|
||
|
* 3. Move each of the monitors found at original_node to Node P.
|
||
|
* These are "Moved Monitors."
|
||
|
*
|
||
|
*/
|
||
|
|
||
|
/*
|
||
|
* Attach at end so that the list pointer we return points
|
||
|
* only to the moved routes
|
||
|
*/
|
||
|
for (m = RFAPI_MONITOR_VPN(par), mlast = NULL; m;
|
||
|
mlast = m, m = m->next)
|
||
|
;
|
||
|
|
||
|
if (mlast) {
|
||
|
moved = mlast->next = RFAPI_MONITOR_VPN(original_vpn_node);
|
||
|
} else {
|
||
|
moved = RFAPI_MONITOR_VPN_W_ALLOC(par) =
|
||
|
RFAPI_MONITOR_VPN(original_vpn_node);
|
||
|
}
|
||
|
if (RFAPI_MONITOR_VPN(
|
||
|
original_vpn_node)) /* check agg, so not allocated */
|
||
|
RFAPI_MONITOR_VPN_W_ALLOC(original_vpn_node) = NULL;
|
||
|
|
||
|
/*
|
||
|
* update the node pointers on the monitors
|
||
|
*/
|
||
|
for (m = moved; m; m = m->next) {
|
||
|
++movecount;
|
||
|
m->node = par;
|
||
|
}
|
||
|
|
||
|
RFAPI_CHECK_REFCOUNT(par, SAFI_MPLS_VPN,
|
||
|
parent_already_refcounted - movecount);
|
||
|
while (movecount > parent_already_refcounted) {
|
||
|
agg_lock_node(par);
|
||
|
++parent_already_refcounted;
|
||
|
}
|
||
|
while (movecount < parent_already_refcounted) {
|
||
|
/* unlikely, but code defensively */
|
||
|
agg_unlock_node(par);
|
||
|
--parent_already_refcounted;
|
||
|
}
|
||
|
RFAPI_CHECK_REFCOUNT(original_vpn_node, SAFI_MPLS_VPN,
|
||
|
movecount + lockoffset);
|
||
|
while (movecount--) {
|
||
|
agg_unlock_node(original_vpn_node);
|
||
|
}
|
||
|
|
||
|
#ifdef DEBUG_MONITOR_MOVE_SHORTER
|
||
|
{
|
||
|
vnc_zlog_debug_verbose("%s: moved to node pfx=%pFX", __func__,
|
||
|
&par->p);
|
||
|
}
|
||
|
#endif
|
||
|
|
||
|
|
||
|
return moved;
|
||
|
}
|
||
|
|
||
|
/*
|
||
|
* Implement MONITOR_MOVE_LONGER(new_node) from
|
||
|
* RFAPI-Import-Event-Handling.txt
|
||
|
*/
|
||
|
static void rfapiMonitorMoveLonger(struct agg_node *new_vpn_node)
|
||
|
{
|
||
|
struct rfapi_monitor_vpn *monitor;
|
||
|
struct rfapi_monitor_vpn *mlast;
|
||
|
struct bgp_path_info *bpi;
|
||
|
struct agg_node *par;
|
||
|
const struct prefix *new_vpn_node_p = agg_node_get_prefix(new_vpn_node);
|
||
|
|
||
|
RFAPI_CHECK_REFCOUNT(new_vpn_node, SAFI_MPLS_VPN, 0);
|
||
|
|
||
|
/*
|
||
|
* Make sure we have at least one valid route at the new node
|
||
|
*/
|
||
|
for (bpi = new_vpn_node->info; bpi; bpi = bpi->next) {
|
||
|
struct prefix pfx;
|
||
|
if (!rfapiGetUnAddrOfVpnBi(bpi, &pfx))
|
||
|
break;
|
||
|
}
|
||
|
|
||
|
if (!bpi) {
|
||
|
vnc_zlog_debug_verbose(
|
||
|
"%s: no valid routes at node %p, so not attempting moves",
|
||
|
__func__, new_vpn_node);
|
||
|
return;
|
||
|
}
|
||
|
|
||
|
/*
|
||
|
* Find first parent node that has monitors
|
||
|
*/
|
||
|
for (par = agg_node_parent(new_vpn_node); par;
|
||
|
par = agg_node_parent(par)) {
|
||
|
if (RFAPI_MONITOR_VPN(par))
|
||
|
break;
|
||
|
}
|
||
|
|
||
|
if (!par) {
|
||
|
vnc_zlog_debug_verbose(
|
||
|
"%s: no parent nodes with monitors, done", __func__);
|
||
|
return;
|
||
|
}
|
||
|
|
||
|
/*
|
||
|
* Check each of these monitors to see of their longest-match
|
||
|
* is now the updated node. Move any such monitors to the more-
|
||
|
* specific updated node
|
||
|
*/
|
||
|
for (mlast = NULL, monitor = RFAPI_MONITOR_VPN(par); monitor;) {
|
||
|
/*
|
||
|
* If new longest match for monitor prefix is the new
|
||
|
* route's prefix, move monitor to new route's prefix
|
||
|
*/
|
||
|
if (prefix_match(new_vpn_node_p, &monitor->p)) {
|
||
|
/* detach */
|
||
|
if (mlast) {
|
||
|
mlast->next = monitor->next;
|
||
|
} else {
|
||
|
RFAPI_MONITOR_VPN_W_ALLOC(par) = monitor->next;
|
||
|
}
|
||
|
|
||
|
|
||
|
/* attach */
|
||
|
monitor->next = RFAPI_MONITOR_VPN(new_vpn_node);
|
||
|
RFAPI_MONITOR_VPN_W_ALLOC(new_vpn_node) = monitor;
|
||
|
monitor->node = new_vpn_node;
|
||
|
|
||
|
agg_lock_node(new_vpn_node); /* incr refcount */
|
||
|
|
||
|
monitor = mlast ? mlast->next : RFAPI_MONITOR_VPN(par);
|
||
|
|
||
|
RFAPI_CHECK_REFCOUNT(par, SAFI_MPLS_VPN, 1);
|
||
|
/* decr refcount after we're done with par as this might
|
||
|
* free it */
|
||
|
agg_unlock_node(par);
|
||
|
|
||
|
continue;
|
||
|
}
|
||
|
mlast = monitor;
|
||
|
monitor = monitor->next;
|
||
|
}
|
||
|
|
||
|
RFAPI_CHECK_REFCOUNT(new_vpn_node, SAFI_MPLS_VPN, 0);
|
||
|
}
|
||
|
|
||
|
|
||
|
static void rfapiBgpInfoChainFree(struct bgp_path_info *bpi)
|
||
|
{
|
||
|
struct bgp_path_info *next;
|
||
|
|
||
|
while (bpi) {
|
||
|
|
||
|
/*
|
||
|
* If there is a timer waiting to delete this bpi, cancel
|
||
|
* the timer and delete immediately
|
||
|
*/
|
||
|
if (CHECK_FLAG(bpi->flags, BGP_PATH_REMOVED) &&
|
||
|
bpi->extra->vnc->vnc.import.timer) {
|
||
|
struct rfapi_withdraw *wcb =
|
||
|
EVENT_ARG(bpi->extra->vnc->vnc.import.timer);
|
||
|
|
||
|
XFREE(MTYPE_RFAPI_WITHDRAW, wcb);
|
||
|
EVENT_OFF(bpi->extra->vnc->vnc.import.timer);
|
||
|
}
|
||
|
|
||
|
next = bpi->next;
|
||
|
bpi->next = NULL;
|
||
|
rfapiBgpInfoFree(bpi);
|
||
|
bpi = next;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
static void rfapiImportTableFlush(struct rfapi_import_table *it)
|
||
|
{
|
||
|
afi_t afi;
|
||
|
|
||
|
/*
|
||
|
* Free ecommunity
|
||
|
*/
|
||
|
ecommunity_free(&it->rt_import_list);
|
||
|
it->rt_import_list = NULL;
|
||
|
|
||
|
for (afi = AFI_IP; afi < AFI_MAX; ++afi) {
|
||
|
|
||
|
struct agg_node *rn;
|
||
|
struct agg_table *at;
|
||
|
|
||
|
at = it->imported_vpn[afi];
|
||
|
if (at) {
|
||
|
for (rn = agg_route_top(at); rn;
|
||
|
rn = agg_route_next(rn)) {
|
||
|
/*
|
||
|
* Each route_node has:
|
||
|
* aggregate: points to rfapi_it_extra with
|
||
|
* monitor chain(s)
|
||
|
* info: points to chain of bgp_path_info
|
||
|
*/
|
||
|
/* free bgp_path_info and its children */
|
||
|
rfapiBgpInfoChainFree(rn->info);
|
||
|
rn->info = NULL;
|
||
|
|
||
|
rfapiMonitorExtraFlush(SAFI_MPLS_VPN, rn);
|
||
|
}
|
||
|
agg_table_finish(at);
|
||
|
}
|
||
|
|
||
|
if (at) {
|
||
|
at = it->imported_encap[afi];
|
||
|
for (rn = agg_route_top(at); rn;
|
||
|
rn = agg_route_next(rn)) {
|
||
|
/* free bgp_path_info and its children */
|
||
|
rfapiBgpInfoChainFree(rn->info);
|
||
|
rn->info = NULL;
|
||
|
|
||
|
rfapiMonitorExtraFlush(SAFI_ENCAP, rn);
|
||
|
}
|
||
|
agg_table_finish(at);
|
||
|
}
|
||
|
}
|
||
|
if (it->monitor_exterior_orphans) {
|
||
|
skiplist_free(it->monitor_exterior_orphans);
|
||
|
}
|
||
|
}
|
||
|
|
||
|
void rfapiImportTableRefDelByIt(struct bgp *bgp,
|
||
|
struct rfapi_import_table *it_target)
|
||
|
{
|
||
|
struct rfapi *h;
|
||
|
struct rfapi_import_table *it;
|
||
|
struct rfapi_import_table *prev = NULL;
|
||
|
|
||
|
assert(it_target);
|
||
|
|
||
|
h = bgp->rfapi;
|
||
|
assert(h);
|
||
|
|
||
|
for (it = h->imports; it; prev = it, it = it->next) {
|
||
|
if (it == it_target)
|
||
|
break;
|
||
|
}
|
||
|
|
||
|
assert(it);
|
||
|
assert(it->refcount);
|
||
|
|
||
|
it->refcount -= 1;
|
||
|
|
||
|
if (!it->refcount) {
|
||
|
if (prev) {
|
||
|
prev->next = it->next;
|
||
|
} else {
|
||
|
h->imports = it->next;
|
||
|
}
|
||
|
rfapiImportTableFlush(it);
|
||
|
XFREE(MTYPE_RFAPI_IMPORTTABLE, it);
|
||
|
}
|
||
|
}
|
||
|
|
||
|
#ifdef RFAPI_REQUIRE_ENCAP_BEEC
|
||
|
/*
|
||
|
* Look for magic BGP Encapsulation Extended Community value
|
||
|
* Format in RFC 5512 Sect. 4.5
|
||
|
*/
|
||
|
static int rfapiEcommunitiesMatchBeec(struct ecommunity *ecom,
|
||
|
bgp_encap_types type)
|
||
|
{
|
||
|
int i;
|
||
|
|
||
|
if (!ecom)
|
||
|
return 0;
|
||
|
|
||
|
for (i = 0; i < (ecom->size * ECOMMUNITY_SIZE); i += ECOMMUNITY_SIZE) {
|
||
|
|
||
|
uint8_t *ep;
|
||
|
|
||
|
ep = ecom->val + i;
|
||
|
|
||
|
if (ep[0] == ECOMMUNITY_ENCODE_OPAQUE
|
||
|
&& ep[1] == ECOMMUNITY_OPAQUE_SUBTYPE_ENCAP
|
||
|
&& ep[6] == ((type && 0xff00) >> 8)
|
||
|
&& ep[7] == (type & 0xff)) {
|
||
|
|
||
|
return 1;
|
||
|
}
|
||
|
}
|
||
|
return 0;
|
||
|
}
|
||
|
#endif
|
||
|
|
||
|
int rfapiEcommunitiesIntersect(struct ecommunity *e1, struct ecommunity *e2)
|
||
|
{
|
||
|
uint32_t i, j;
|
||
|
|
||
|
if (!e1 || !e2)
|
||
|
return 0;
|
||
|
|
||
|
{
|
||
|
char *s1, *s2;
|
||
|
s1 = ecommunity_ecom2str(e1, ECOMMUNITY_FORMAT_DISPLAY, 0);
|
||
|
s2 = ecommunity_ecom2str(e2, ECOMMUNITY_FORMAT_DISPLAY, 0);
|
||
|
vnc_zlog_debug_verbose("%s: e1[%s], e2[%s]", __func__, s1, s2);
|
||
|
XFREE(MTYPE_ECOMMUNITY_STR, s1);
|
||
|
XFREE(MTYPE_ECOMMUNITY_STR, s2);
|
||
|
}
|
||
|
|
||
|
for (i = 0; i < e1->size; ++i) {
|
||
|
for (j = 0; j < e2->size; ++j) {
|
||
|
if (!memcmp(e1->val + (i * ECOMMUNITY_SIZE),
|
||
|
e2->val + (j * ECOMMUNITY_SIZE),
|
||
|
ECOMMUNITY_SIZE)) {
|
||
|
|
||
|
return 1;
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
return 0;
|
||
|
}
|
||
|
|
||
|
int rfapiEcommunityGetLNI(struct ecommunity *ecom, uint32_t *lni)
|
||
|
{
|
||
|
if (ecom) {
|
||
|
uint32_t i;
|
||
|
|
||
|
for (i = 0; i < ecom->size; ++i) {
|
||
|
uint8_t *p = ecom->val + (i * ECOMMUNITY_SIZE);
|
||
|
|
||
|
if ((*(p + 0) == 0x00) && (*(p + 1) == 0x02)) {
|
||
|
|
||
|
*lni = (*(p + 5) << 16) | (*(p + 6) << 8)
|
||
|
| (*(p + 7));
|
||
|
return 0;
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
return ENOENT;
|
||
|
}
|
||
|
|
||
|
int rfapiEcommunityGetEthernetTag(struct ecommunity *ecom, uint16_t *tag_id)
|
||
|
{
|
||
|
struct bgp *bgp = bgp_get_default();
|
||
|
*tag_id = 0; /* default to untagged */
|
||
|
if (ecom) {
|
||
|
uint32_t i;
|
||
|
|
||
|
for (i = 0; i < ecom->size; ++i) {
|
||
|
as_t as = 0;
|
||
|
int encode = 0;
|
||
|
const uint8_t *p = ecom->val + (i * ECOMMUNITY_SIZE);
|
||
|
|
||
|
/* High-order octet of type. */
|
||
|
encode = *p++;
|
||
|
|
||
|
if (*p++ == ECOMMUNITY_ROUTE_TARGET) {
|
||
|
if (encode == ECOMMUNITY_ENCODE_AS4) {
|
||
|
p = ptr_get_be32(p, &as);
|
||
|
} else if (encode == ECOMMUNITY_ENCODE_AS) {
|
||
|
as = (*p++ << 8);
|
||
|
as |= (*p++);
|
||
|
p += 2; /* skip next two, tag/vid
|
||
|
always in lowest bytes */
|
||
|
}
|
||
|
if (as == bgp->as) {
|
||
|
*tag_id = *p++ << 8;
|
||
|
*tag_id |= (*p++);
|
||
|
return 0;
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
return ENOENT;
|
||
|
}
|
||
|
|
||
|
static int rfapiVpnBiNhEqualsPt(struct bgp_path_info *bpi,
|
||
|
struct rfapi_ip_addr *hpt)
|
||
|
{
|
||
|
uint8_t family;
|
||
|
|
||
|
if (!hpt || !bpi)
|
||
|
return 0;
|
||
|
|
||
|
family = BGP_MP_NEXTHOP_FAMILY(bpi->attr->mp_nexthop_len);
|
||
|
|
||
|
if (hpt->addr_family != family)
|
||
|
return 0;
|
||
|
|
||
|
switch (family) {
|
||
|
case AF_INET:
|
||
|
if (bpi->attr->mp_nexthop_global_in.s_addr
|
||
|
!= hpt->addr.v4.s_addr)
|
||
|
return 0;
|
||
|
break;
|
||
|
|
||
|
case AF_INET6:
|
||
|
if (IPV6_ADDR_CMP(&bpi->attr->mp_nexthop_global, &hpt->addr.v6))
|
||
|
return 0;
|
||
|
break;
|
||
|
|
||
|
default:
|
||
|
return 0;
|
||
|
}
|
||
|
|
||
|
return 1;
|
||
|
}
|
||
|
|
||
|
|
||
|
/*
|
||
|
* Compare 2 VPN BIs. Return true if they have the same VN and UN addresses
|
||
|
*/
|
||
|
static int rfapiVpnBiSamePtUn(struct bgp_path_info *bpi1,
|
||
|
struct bgp_path_info *bpi2)
|
||
|
{
|
||
|
struct prefix pfx_un1;
|
||
|
struct prefix pfx_un2;
|
||
|
|
||
|
if (!bpi1 || !bpi2)
|
||
|
return 0;
|
||
|
|
||
|
/*
|
||
|
* VN address comparisons
|
||
|
*/
|
||
|
|
||
|
if (BGP_MP_NEXTHOP_FAMILY(bpi1->attr->mp_nexthop_len)
|
||
|
!= BGP_MP_NEXTHOP_FAMILY(bpi2->attr->mp_nexthop_len)) {
|
||
|
return 0;
|
||
|
}
|
||
|
|
||
|
switch (BGP_MP_NEXTHOP_FAMILY(bpi1->attr->mp_nexthop_len)) {
|
||
|
case AF_INET:
|
||
|
if (bpi1->attr->mp_nexthop_global_in.s_addr
|
||
|
!= bpi2->attr->mp_nexthop_global_in.s_addr)
|
||
|
return 0;
|
||
|
break;
|
||
|
|
||
|
case AF_INET6:
|
||
|
if (IPV6_ADDR_CMP(&bpi1->attr->mp_nexthop_global,
|
||
|
&bpi2->attr->mp_nexthop_global))
|
||
|
return 0;
|
||
|
break;
|
||
|
|
||
|
default:
|
||
|
return 0;
|
||
|
}
|
||
|
|
||
|
memset(&pfx_un1, 0, sizeof(pfx_un1));
|
||
|
memset(&pfx_un2, 0, sizeof(pfx_un2));
|
||
|
|
||
|
/*
|
||
|
* UN address comparisons
|
||
|
*/
|
||
|
if (rfapiGetVncTunnelUnAddr(bpi1->attr, &pfx_un1)) {
|
||
|
if (bpi1->extra) {
|
||
|
pfx_un1.family = bpi1->extra->vnc->vnc.import.un_family;
|
||
|
switch (bpi1->extra->vnc->vnc.import.un_family) {
|
||
|
case AF_INET:
|
||
|
pfx_un1.u.prefix4 =
|
||
|
bpi1->extra->vnc->vnc.import.un.addr4;
|
||
|
break;
|
||
|
case AF_INET6:
|
||
|
pfx_un1.u.prefix6 =
|
||
|
bpi1->extra->vnc->vnc.import.un.addr6;
|
||
|
break;
|
||
|
default:
|
||
|
pfx_un1.family = AF_UNSPEC;
|
||
|
break;
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
|
||
|
if (rfapiGetVncTunnelUnAddr(bpi2->attr, &pfx_un2)) {
|
||
|
if (bpi2->extra) {
|
||
|
pfx_un2.family = bpi2->extra->vnc->vnc.import.un_family;
|
||
|
switch (bpi2->extra->vnc->vnc.import.un_family) {
|
||
|
case AF_INET:
|
||
|
pfx_un2.u.prefix4 =
|
||
|
bpi2->extra->vnc->vnc.import.un.addr4;
|
||
|
break;
|
||
|
case AF_INET6:
|
||
|
pfx_un2.u.prefix6 =
|
||
|
bpi2->extra->vnc->vnc.import.un.addr6;
|
||
|
break;
|
||
|
default:
|
||
|
pfx_un2.family = AF_UNSPEC;
|
||
|
break;
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
|
||
|
if (pfx_un1.family == AF_UNSPEC || pfx_un2.family == AF_UNSPEC)
|
||
|
return 0;
|
||
|
|
||
|
if (pfx_un1.family != pfx_un2.family)
|
||
|
return 0;
|
||
|
|
||
|
switch (pfx_un1.family) {
|
||
|
case AF_INET:
|
||
|
if (!IPV4_ADDR_SAME(&pfx_un1.u.prefix4, &pfx_un2.u.prefix4))
|
||
|
return 0;
|
||
|
break;
|
||
|
case AF_INET6:
|
||
|
if (!IPV6_ADDR_SAME(&pfx_un1.u.prefix6, &pfx_un2.u.prefix6))
|
||
|
return 0;
|
||
|
break;
|
||
|
}
|
||
|
|
||
|
|
||
|
return 1;
|
||
|
}
|
||
|
|
||
|
uint8_t rfapiRfpCost(struct attr *attr)
|
||
|
{
|
||
|
if (attr->flag & ATTR_FLAG_BIT(BGP_ATTR_LOCAL_PREF)) {
|
||
|
if (attr->local_pref > 255) {
|
||
|
return 0;
|
||
|
}
|
||
|
return 255 - attr->local_pref;
|
||
|
}
|
||
|
|
||
|
return 255;
|
||
|
}
|
||
|
|
||
|
/*------------------------------------------
|
||
|
* rfapi_extract_l2o
|
||
|
*
|
||
|
* Find Layer 2 options in an option chain
|
||
|
*
|
||
|
* input:
|
||
|
* pHop option chain
|
||
|
*
|
||
|
* output:
|
||
|
* l2o layer 2 options extracted
|
||
|
*
|
||
|
* return value:
|
||
|
* 0 OK
|
||
|
* 1 no options found
|
||
|
*
|
||
|
--------------------------------------------*/
|
||
|
int rfapi_extract_l2o(
|
||
|
struct bgp_tea_options *pHop, /* chain of options */
|
||
|
struct rfapi_l2address_option *l2o) /* return extracted value */
|
||
|
{
|
||
|
struct bgp_tea_options *p;
|
||
|
|
||
|
for (p = pHop; p; p = p->next) {
|
||
|
if ((p->type == RFAPI_VN_OPTION_TYPE_L2ADDR)
|
||
|
&& (p->length >= 8)) {
|
||
|
|
||
|
char *v = p->value;
|
||
|
|
||
|
memcpy(&l2o->macaddr, v, 6);
|
||
|
|
||
|
l2o->label = ((v[6] << 12) & 0xff000)
|
||
|
+ ((v[7] << 4) & 0xff0)
|
||
|
+ ((v[8] >> 4) & 0xf);
|
||
|
|
||
|
l2o->local_nve_id = (uint8_t)v[10];
|
||
|
|
||
|
l2o->logical_net_id =
|
||
|
(v[11] << 16) + (v[12] << 8) + (v[13] << 0);
|
||
|
|
||
|
return 0;
|
||
|
}
|
||
|
}
|
||
|
return 1;
|
||
|
}
|
||
|
|
||
|
static struct rfapi_next_hop_entry *
|
||
|
rfapiRouteInfo2NextHopEntry(struct rfapi_ip_prefix *rprefix,
|
||
|
struct bgp_path_info *bpi, /* route to encode */
|
||
|
uint32_t lifetime, /* use this in nhe */
|
||
|
struct agg_node *rn) /* req for L2 eth addr */
|
||
|
{
|
||
|
struct rfapi_next_hop_entry *new;
|
||
|
int have_vnc_tunnel_un = 0;
|
||
|
const struct prefix *p = agg_node_get_prefix(rn);
|
||
|
|
||
|
#ifdef DEBUG_ENCAP_MONITOR
|
||
|
vnc_zlog_debug_verbose("%s: entry, bpi %p, rn %p", __func__, bpi, rn);
|
||
|
#endif
|
||
|
|
||
|
new = XCALLOC(MTYPE_RFAPI_NEXTHOP, sizeof(struct rfapi_next_hop_entry));
|
||
|
|
||
|
new->prefix = *rprefix;
|
||
|
|
||
|
if (bpi->extra && decode_rd_type(bpi->extra->vnc->vnc.import.rd.val) ==
|
||
|
RD_TYPE_VNC_ETH) {
|
||
|
/* ethernet */
|
||
|
|
||
|
struct rfapi_vn_option *vo;
|
||
|
|
||
|
vo = XCALLOC(MTYPE_RFAPI_VN_OPTION,
|
||
|
sizeof(struct rfapi_vn_option));
|
||
|
|
||
|
vo->type = RFAPI_VN_OPTION_TYPE_L2ADDR;
|
||
|
|
||
|
memcpy(&vo->v.l2addr.macaddr, &p->u.prefix_eth.octet, ETH_ALEN);
|
||
|
/* only low 3 bytes of this are significant */
|
||
|
(void)rfapiEcommunityGetLNI(bgp_attr_get_ecommunity(bpi->attr),
|
||
|
&vo->v.l2addr.logical_net_id);
|
||
|
(void)rfapiEcommunityGetEthernetTag(
|
||
|
bgp_attr_get_ecommunity(bpi->attr),
|
||
|
&vo->v.l2addr.tag_id);
|
||
|
|
||
|
/* local_nve_id comes from lower byte of RD type */
|
||
|
vo->v.l2addr.local_nve_id =
|
||
|
bpi->extra->vnc->vnc.import.rd.val[1];
|
||
|
|
||
|
/* label comes from MP_REACH_NLRI label */
|
||
|
vo->v.l2addr.label =
|
||
|
BGP_PATH_INFO_NUM_LABELS(bpi)
|
||
|
? decode_label(&bpi->extra->labels->label[0])
|
||
|
: MPLS_INVALID_LABEL;
|
||
|
|
||
|
new->vn_options = vo;
|
||
|
|
||
|
/*
|
||
|
* If there is an auxiliary prefix (i.e., host IP address),
|
||
|
* use it as the nexthop prefix instead of the query prefix
|
||
|
*/
|
||
|
if (bpi->extra->vnc->vnc.import.aux_prefix.family) {
|
||
|
rfapiQprefix2Rprefix(&bpi->extra->vnc->vnc.import
|
||
|
.aux_prefix,
|
||
|
&new->prefix);
|
||
|
}
|
||
|
}
|
||
|
|
||
|
bgp_encap_types tun_type = BGP_ENCAP_TYPE_MPLS; /*Default*/
|
||
|
new->prefix.cost = rfapiRfpCost(bpi->attr);
|
||
|
|
||
|
struct bgp_attr_encap_subtlv *pEncap;
|
||
|
|
||
|
switch (BGP_MP_NEXTHOP_FAMILY(bpi->attr->mp_nexthop_len)) {
|
||
|
case AF_INET:
|
||
|
new->vn_address.addr_family = AF_INET;
|
||
|
new->vn_address.addr.v4 = bpi->attr->mp_nexthop_global_in;
|
||
|
break;
|
||
|
|
||
|
case AF_INET6:
|
||
|
new->vn_address.addr_family = AF_INET6;
|
||
|
new->vn_address.addr.v6 = bpi->attr->mp_nexthop_global;
|
||
|
break;
|
||
|
|
||
|
default:
|
||
|
zlog_warn("%s: invalid vpn nexthop length: %d", __func__,
|
||
|
bpi->attr->mp_nexthop_len);
|
||
|
rfapi_free_next_hop_list(new);
|
||
|
return NULL;
|
||
|
}
|
||
|
|
||
|
for (pEncap = bgp_attr_get_vnc_subtlvs(bpi->attr); pEncap;
|
||
|
pEncap = pEncap->next) {
|
||
|
switch (pEncap->type) {
|
||
|
case BGP_VNC_SUBTLV_TYPE_LIFETIME:
|
||
|
/* use configured lifetime, not attr lifetime */
|
||
|
break;
|
||
|
|
||
|
default:
|
||
|
zlog_warn("%s: unknown VNC option type %d", __func__,
|
||
|
pEncap->type);
|
||
|
|
||
|
break;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
bgp_attr_extcom_tunnel_type(bpi->attr, &tun_type);
|
||
|
if (tun_type == BGP_ENCAP_TYPE_MPLS) {
|
||
|
struct prefix p;
|
||
|
/* MPLS carries UN address in next hop */
|
||
|
rfapiNexthop2Prefix(bpi->attr, &p);
|
||
|
if (p.family != AF_UNSPEC) {
|
||
|
rfapiQprefix2Raddr(&p, &new->un_address);
|
||
|
have_vnc_tunnel_un = 1;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
for (pEncap = bpi->attr->encap_subtlvs; pEncap; pEncap = pEncap->next) {
|
||
|
switch (pEncap->type) {
|
||
|
case BGP_ENCAP_SUBTLV_TYPE_REMOTE_ENDPOINT:
|
||
|
/*
|
||
|
* Overrides ENCAP UN address, if any
|
||
|
*/
|
||
|
switch (pEncap->length) {
|
||
|
|
||
|
case 8:
|
||
|
new->un_address.addr_family = AF_INET;
|
||
|
memcpy(&new->un_address.addr.v4, pEncap->value,
|
||
|
4);
|
||
|
have_vnc_tunnel_un = 1;
|
||
|
break;
|
||
|
|
||
|
case 20:
|
||
|
new->un_address.addr_family = AF_INET6;
|
||
|
memcpy(&new->un_address.addr.v6, pEncap->value,
|
||
|
16);
|
||
|
have_vnc_tunnel_un = 1;
|
||
|
break;
|
||
|
|
||
|
default:
|
||
|
zlog_warn(
|
||
|
"%s: invalid tunnel subtlv UN addr length (%d) for bpi %p",
|
||
|
__func__, pEncap->length, bpi);
|
||
|
}
|
||
|
break;
|
||
|
|
||
|
default:
|
||
|
zlog_warn("%s: unknown Encap Attribute option type %d",
|
||
|
__func__, pEncap->type);
|
||
|
break;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
new->un_options = rfapi_encap_tlv_to_un_option(bpi->attr);
|
||
|
|
||
|
#ifdef DEBUG_ENCAP_MONITOR
|
||
|
vnc_zlog_debug_verbose("%s: line %d: have_vnc_tunnel_un=%d", __func__,
|
||
|
__LINE__, have_vnc_tunnel_un);
|
||
|
#endif
|
||
|
|
||
|
if (!have_vnc_tunnel_un && bpi->extra) {
|
||
|
/*
|
||
|
* use cached UN address from ENCAP route
|
||
|
*/
|
||
|
new->un_address.addr_family =
|
||
|
bpi->extra->vnc->vnc.import.un_family;
|
||
|
switch (new->un_address.addr_family) {
|
||
|
case AF_INET:
|
||
|
new->un_address.addr.v4 =
|
||
|
bpi->extra->vnc->vnc.import.un.addr4;
|
||
|
break;
|
||
|
case AF_INET6:
|
||
|
new->un_address.addr.v6 =
|
||
|
bpi->extra->vnc->vnc.import.un.addr6;
|
||
|
break;
|
||
|
default:
|
||
|
zlog_warn("%s: invalid UN addr family (%d) for bpi %p",
|
||
|
__func__, new->un_address.addr_family, bpi);
|
||
|
rfapi_free_next_hop_list(new);
|
||
|
return NULL;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
new->lifetime = lifetime;
|
||
|
return new;
|
||
|
}
|
||
|
|
||
|
int rfapiHasNonRemovedRoutes(struct agg_node *rn)
|
||
|
{
|
||
|
struct bgp_path_info *bpi;
|
||
|
|
||
|
for (bpi = rn->info; bpi; bpi = bpi->next) {
|
||
|
struct prefix pfx;
|
||
|
|
||
|
if (!CHECK_FLAG(bpi->flags, BGP_PATH_REMOVED)
|
||
|
&& (bpi->extra && !rfapiGetUnAddrOfVpnBi(bpi, &pfx))) {
|
||
|
|
||
|
return 1;
|
||
|
}
|
||
|
}
|
||
|
return 0;
|
||
|
}
|
||
|
|
||
|
#ifdef DEBUG_IT_NODES
|
||
|
/*
|
||
|
* DEBUG FUNCTION
|
||
|
*/
|
||
|
void rfapiDumpNode(struct agg_node *rn)
|
||
|
{
|
||
|
struct bgp_path_info *bpi;
|
||
|
|
||
|
vnc_zlog_debug_verbose("%s: rn=%p", __func__, rn);
|
||
|
for (bpi = rn->info; bpi; bpi = bpi->next) {
|
||
|
struct prefix pfx;
|
||
|
int ctrc = rfapiGetUnAddrOfVpnBi(bpi, &pfx);
|
||
|
int nr;
|
||
|
|
||
|
if (!CHECK_FLAG(bpi->flags, BGP_PATH_REMOVED)
|
||
|
&& (bpi->extra && !ctrc)) {
|
||
|
|
||
|
nr = 1;
|
||
|
} else {
|
||
|
nr = 0;
|
||
|
}
|
||
|
|
||
|
vnc_zlog_debug_verbose(
|
||
|
" bpi=%p, nr=%d, flags=0x%x, extra=%p, ctrc=%d", bpi,
|
||
|
nr, bpi->flags, bpi->extra, ctrc);
|
||
|
}
|
||
|
}
|
||
|
#endif
|
||
|
|
||
|
static int rfapiNhlAddNodeRoutes(
|
||
|
struct agg_node *rn, /* in */
|
||
|
struct rfapi_ip_prefix *rprefix, /* in */
|
||
|
uint32_t lifetime, /* in */
|
||
|
int removed, /* in */
|
||
|
struct rfapi_next_hop_entry **head, /* in/out */
|
||
|
struct rfapi_next_hop_entry **tail, /* in/out */
|
||
|
struct rfapi_ip_addr *exclude_vnaddr, /* omit routes to same NVE */
|
||
|
struct agg_node *rfd_rib_node, /* preload this NVE rib node */
|
||
|
struct prefix *pfx_target_original) /* query target */
|
||
|
{
|
||
|
struct bgp_path_info *bpi;
|
||
|
struct rfapi_next_hop_entry *new;
|
||
|
struct prefix pfx_un;
|
||
|
struct skiplist *seen_nexthops;
|
||
|
int count = 0;
|
||
|
const struct prefix *p = agg_node_get_prefix(rn);
|
||
|
int is_l2 = (p->family == AF_ETHERNET);
|
||
|
|
||
|
if (rfd_rib_node) {
|
||
|
struct agg_table *atable = agg_get_table(rfd_rib_node);
|
||
|
struct rfapi_descriptor *rfd;
|
||
|
|
||
|
if (atable) {
|
||
|
rfd = agg_get_table_info(atable);
|
||
|
|
||
|
if (rfapiRibFTDFilterRecentPrefix(rfd, rn,
|
||
|
pfx_target_original))
|
||
|
return 0;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
seen_nexthops =
|
||
|
skiplist_new(0, vnc_prefix_cmp, prefix_free_lists);
|
||
|
|
||
|
for (bpi = rn->info; bpi; bpi = bpi->next) {
|
||
|
|
||
|
struct prefix pfx_vn;
|
||
|
struct prefix *newpfx;
|
||
|
|
||
|
if (removed && !CHECK_FLAG(bpi->flags, BGP_PATH_REMOVED)) {
|
||
|
#ifdef DEBUG_RETURNED_NHL
|
||
|
vnc_zlog_debug_verbose(
|
||
|
"%s: want holddown, this route not holddown, skip",
|
||
|
__func__);
|
||
|
#endif
|
||
|
continue;
|
||
|
}
|
||
|
if (!removed && CHECK_FLAG(bpi->flags, BGP_PATH_REMOVED)) {
|
||
|
continue;
|
||
|
}
|
||
|
|
||
|
if (!bpi->extra) {
|
||
|
continue;
|
||
|
}
|
||
|
|
||
|
/*
|
||
|
* Check for excluded VN address
|
||
|
*/
|
||
|
if (rfapiVpnBiNhEqualsPt(bpi, exclude_vnaddr))
|
||
|
continue;
|
||
|
|
||
|
/*
|
||
|
* Check for VN address (nexthop) copied already
|
||
|
*/
|
||
|
if (is_l2) {
|
||
|
/* L2 routes: semantic nexthop in aux_prefix; VN addr
|
||
|
* ain't it */
|
||
|
pfx_vn = bpi->extra->vnc->vnc.import.aux_prefix;
|
||
|
} else {
|
||
|
rfapiNexthop2Prefix(bpi->attr, &pfx_vn);
|
||
|
}
|
||
|
if (!skiplist_search(seen_nexthops, &pfx_vn, NULL)) {
|
||
|
#ifdef DEBUG_RETURNED_NHL
|
||
|
vnc_zlog_debug_verbose(
|
||
|
"%s: already put VN/nexthop %pFX, skip",
|
||
|
__func__, &pfx_vn);
|
||
|
#endif
|
||
|
continue;
|
||
|
}
|
||
|
|
||
|
if (rfapiGetUnAddrOfVpnBi(bpi, &pfx_un)) {
|
||
|
#ifdef DEBUG_ENCAP_MONITOR
|
||
|
vnc_zlog_debug_verbose(
|
||
|
"%s: failed to get UN address of this VPN bpi",
|
||
|
__func__);
|
||
|
#endif
|
||
|
continue;
|
||
|
}
|
||
|
|
||
|
newpfx = prefix_new();
|
||
|
*newpfx = pfx_vn;
|
||
|
skiplist_insert(seen_nexthops, newpfx, newpfx);
|
||
|
|
||
|
new = rfapiRouteInfo2NextHopEntry(rprefix, bpi, lifetime, rn);
|
||
|
if (new) {
|
||
|
if (rfapiRibPreloadBi(rfd_rib_node, &pfx_vn, &pfx_un,
|
||
|
lifetime, bpi)) {
|
||
|
/* duplicate filtered by RIB */
|
||
|
rfapi_free_next_hop_list(new);
|
||
|
new = NULL;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
if (new) {
|
||
|
if (*tail) {
|
||
|
(*tail)->next = new;
|
||
|
} else {
|
||
|
*head = new;
|
||
|
}
|
||
|
*tail = new;
|
||
|
++count;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
skiplist_free(seen_nexthops);
|
||
|
|
||
|
return count;
|
||
|
}
|
||
|
|
||
|
|
||
|
/*
|
||
|
* Breadth-first
|
||
|
*
|
||
|
* omit_node is meant for the situation where we are adding a subtree
|
||
|
* of a parent of some original requested node. The response already
|
||
|
* contains the original requested node, and we don't want to duplicate
|
||
|
* its routes in the list, so we skip it if the right or left node
|
||
|
* matches (of course, we still travel down its child subtrees).
|
||
|
*/
|
||
|
static int rfapiNhlAddSubtree(
|
||
|
struct agg_node *rn, /* in */
|
||
|
uint32_t lifetime, /* in */
|
||
|
struct rfapi_next_hop_entry **head, /* in/out */
|
||
|
struct rfapi_next_hop_entry **tail, /* in/out */
|
||
|
struct agg_node *omit_node, /* in */
|
||
|
struct rfapi_ip_addr *exclude_vnaddr, /* omit routes to same NVE */
|
||
|
struct agg_table *rfd_rib_table, /* preload here */
|
||
|
struct prefix *pfx_target_original) /* query target */
|
||
|
{
|
||
|
struct rfapi_ip_prefix rprefix;
|
||
|
int rcount = 0;
|
||
|
|
||
|
/* FIXME: need to find a better way here to work without sticking our
|
||
|
* hands in node->link */
|
||
|
if (agg_node_left(rn) && agg_node_left(rn) != omit_node) {
|
||
|
if (agg_node_left(rn)->info) {
|
||
|
const struct prefix *p =
|
||
|
agg_node_get_prefix(agg_node_left(rn));
|
||
|
int count = 0;
|
||
|
struct agg_node *rib_rn = NULL;
|
||
|
|
||
|
rfapiQprefix2Rprefix(p, &rprefix);
|
||
|
if (rfd_rib_table)
|
||
|
rib_rn = agg_node_get(rfd_rib_table, p);
|
||
|
|
||
|
count = rfapiNhlAddNodeRoutes(
|
||
|
agg_node_left(rn), &rprefix, lifetime, 0, head,
|
||
|
tail, exclude_vnaddr, rib_rn,
|
||
|
pfx_target_original);
|
||
|
if (!count) {
|
||
|
count = rfapiNhlAddNodeRoutes(
|
||
|
agg_node_left(rn), &rprefix, lifetime,
|
||
|
1, head, tail, exclude_vnaddr, rib_rn,
|
||
|
pfx_target_original);
|
||
|
}
|
||
|
rcount += count;
|
||
|
if (rib_rn)
|
||
|
agg_unlock_node(rib_rn);
|
||
|
}
|
||
|
}
|
||
|
|
||
|
if (agg_node_right(rn) && agg_node_right(rn) != omit_node) {
|
||
|
if (agg_node_right(rn)->info) {
|
||
|
const struct prefix *p =
|
||
|
agg_node_get_prefix(agg_node_right(rn));
|
||
|
int count = 0;
|
||
|
struct agg_node *rib_rn = NULL;
|
||
|
|
||
|
rfapiQprefix2Rprefix(p, &rprefix);
|
||
|
if (rfd_rib_table)
|
||
|
rib_rn = agg_node_get(rfd_rib_table, p);
|
||
|
|
||
|
count = rfapiNhlAddNodeRoutes(
|
||
|
agg_node_right(rn), &rprefix, lifetime, 0, head,
|
||
|
tail, exclude_vnaddr, rib_rn,
|
||
|
pfx_target_original);
|
||
|
if (!count) {
|
||
|
count = rfapiNhlAddNodeRoutes(
|
||
|
agg_node_right(rn), &rprefix, lifetime,
|
||
|
1, head, tail, exclude_vnaddr, rib_rn,
|
||
|
pfx_target_original);
|
||
|
}
|
||
|
rcount += count;
|
||
|
if (rib_rn)
|
||
|
agg_unlock_node(rib_rn);
|
||
|
}
|
||
|
}
|
||
|
|
||
|
if (agg_node_left(rn)) {
|
||
|
rcount += rfapiNhlAddSubtree(
|
||
|
agg_node_left(rn), lifetime, head, tail, omit_node,
|
||
|
exclude_vnaddr, rfd_rib_table, pfx_target_original);
|
||
|
}
|
||
|
if (agg_node_right(rn)) {
|
||
|
rcount += rfapiNhlAddSubtree(
|
||
|
agg_node_right(rn), lifetime, head, tail, omit_node,
|
||
|
exclude_vnaddr, rfd_rib_table, pfx_target_original);
|
||
|
}
|
||
|
|
||
|
return rcount;
|
||
|
}
|
||
|
|
||
|
/*
|
||
|
* Implementation of ROUTE_LIST(node) from RFAPI-Import-Event-Handling.txt
|
||
|
*
|
||
|
* Construct an rfapi nexthop list based on the routes attached to
|
||
|
* the specified node.
|
||
|
*
|
||
|
* If there are any routes that do NOT have BGP_PATH_REMOVED set,
|
||
|
* return those only. If there are ONLY routes with BGP_PATH_REMOVED,
|
||
|
* then return those, and also include all the non-removed routes from the
|
||
|
* next less-specific node (i.e., this node's parent) at the end.
|
||
|
*/
|
||
|
struct rfapi_next_hop_entry *rfapiRouteNode2NextHopList(
|
||
|
struct agg_node *rn, uint32_t lifetime, /* put into nexthop entries */
|
||
|
struct rfapi_ip_addr *exclude_vnaddr, /* omit routes to same NVE */
|
||
|
struct agg_table *rfd_rib_table, /* preload here */
|
||
|
struct prefix *pfx_target_original) /* query target */
|
||
|
{
|
||
|
struct rfapi_ip_prefix rprefix;
|
||
|
struct rfapi_next_hop_entry *answer = NULL;
|
||
|
struct rfapi_next_hop_entry *last = NULL;
|
||
|
struct agg_node *parent;
|
||
|
const struct prefix *p = agg_node_get_prefix(rn);
|
||
|
int count = 0;
|
||
|
struct agg_node *rib_rn;
|
||
|
|
||
|
#ifdef DEBUG_RETURNED_NHL
|
||
|
vnc_zlog_debug_verbose("%s: called with node pfx=%rRN", __func__, rn);
|
||
|
zlog_backtrace(LOG_INFO);
|
||
|
#endif
|
||
|
|
||
|
rfapiQprefix2Rprefix(p, &rprefix);
|
||
|
|
||
|
rib_rn = rfd_rib_table ? agg_node_get(rfd_rib_table, p) : NULL;
|
||
|
|
||
|
/*
|
||
|
* Add non-withdrawn routes at this node
|
||
|
*/
|
||
|
count = rfapiNhlAddNodeRoutes(rn, &rprefix, lifetime, 0, &answer, &last,
|
||
|
exclude_vnaddr, rib_rn,
|
||
|
pfx_target_original);
|
||
|
|
||
|
/*
|
||
|
* If the list has at least one entry, it's finished
|
||
|
*/
|
||
|
if (count) {
|
||
|
count += rfapiNhlAddSubtree(rn, lifetime, &answer, &last, NULL,
|
||
|
exclude_vnaddr, rfd_rib_table,
|
||
|
pfx_target_original);
|
||
|
vnc_zlog_debug_verbose("%s: %d nexthops, answer=%p", __func__,
|
||
|
count, answer);
|
||
|
#ifdef DEBUG_RETURNED_NHL
|
||
|
rfapiPrintNhl(NULL, answer);
|
||
|
#endif
|
||
|
if (rib_rn)
|
||
|
agg_unlock_node(rib_rn);
|
||
|
return answer;
|
||
|
}
|
||
|
|
||
|
/*
|
||
|
* Add withdrawn routes at this node
|
||
|
*/
|
||
|
count = rfapiNhlAddNodeRoutes(rn, &rprefix, lifetime, 1, &answer, &last,
|
||
|
exclude_vnaddr, rib_rn,
|
||
|
pfx_target_original);
|
||
|
if (rib_rn)
|
||
|
agg_unlock_node(rib_rn);
|
||
|
|
||
|
// rfapiPrintNhl(NULL, answer);
|
||
|
|
||
|
/*
|
||
|
* walk up the tree until we find a node with non-deleted
|
||
|
* routes, then add them
|
||
|
*/
|
||
|
for (parent = agg_node_parent(rn); parent;
|
||
|
parent = agg_node_parent(parent)) {
|
||
|
if (rfapiHasNonRemovedRoutes(parent)) {
|
||
|
break;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
/*
|
||
|
* Add non-withdrawn routes from less-specific prefix
|
||
|
*/
|
||
|
if (parent) {
|
||
|
const struct prefix *p = agg_node_get_prefix(parent);
|
||
|
|
||
|
rib_rn = rfd_rib_table ? agg_node_get(rfd_rib_table, p) : NULL;
|
||
|
rfapiQprefix2Rprefix(p, &rprefix);
|
||
|
count += rfapiNhlAddNodeRoutes(parent, &rprefix, lifetime, 0,
|
||
|
&answer, &last, exclude_vnaddr,
|
||
|
rib_rn, pfx_target_original);
|
||
|
count += rfapiNhlAddSubtree(parent, lifetime, &answer, &last,
|
||
|
rn, exclude_vnaddr, rfd_rib_table,
|
||
|
pfx_target_original);
|
||
|
if (rib_rn)
|
||
|
agg_unlock_node(rib_rn);
|
||
|
} else {
|
||
|
/*
|
||
|
* There is no parent with non-removed routes. Still need to
|
||
|
* add subtree of original node if it contributed routes to the
|
||
|
* answer.
|
||
|
*/
|
||
|
if (count)
|
||
|
count += rfapiNhlAddSubtree(rn, lifetime, &answer,
|
||
|
&last, rn, exclude_vnaddr,
|
||
|
rfd_rib_table,
|
||
|
pfx_target_original);
|
||
|
}
|
||
|
|
||
|
vnc_zlog_debug_verbose("%s: %d nexthops, answer=%p", __func__, count,
|
||
|
answer);
|
||
|
#ifdef DEBUG_RETURNED_NHL
|
||
|
rfapiPrintNhl(NULL, answer);
|
||
|
#endif
|
||
|
return answer;
|
||
|
}
|
||
|
|
||
|
/*
|
||
|
* Construct nexthop list of all routes in table
|
||
|
*/
|
||
|
struct rfapi_next_hop_entry *rfapiRouteTable2NextHopList(
|
||
|
struct agg_table *rt, uint32_t lifetime, /* put into nexthop entries */
|
||
|
struct rfapi_ip_addr *exclude_vnaddr, /* omit routes to same NVE */
|
||
|
struct agg_table *rfd_rib_table, /* preload this NVE rib table */
|
||
|
struct prefix *pfx_target_original) /* query target */
|
||
|
{
|
||
|
struct agg_node *rn;
|
||
|
struct rfapi_next_hop_entry *biglist = NULL;
|
||
|
struct rfapi_next_hop_entry *nhl;
|
||
|
struct rfapi_next_hop_entry *tail = NULL;
|
||
|
int count = 0;
|
||
|
|
||
|
for (rn = agg_route_top(rt); rn; rn = agg_route_next(rn)) {
|
||
|
|
||
|
nhl = rfapiRouteNode2NextHopList(rn, lifetime, exclude_vnaddr,
|
||
|
rfd_rib_table,
|
||
|
pfx_target_original);
|
||
|
if (!tail) {
|
||
|
tail = biglist = nhl;
|
||
|
if (tail)
|
||
|
count = 1;
|
||
|
} else {
|
||
|
tail->next = nhl;
|
||
|
}
|
||
|
if (tail) {
|
||
|
while (tail->next) {
|
||
|
++count;
|
||
|
tail = tail->next;
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
|
||
|
vnc_zlog_debug_verbose("%s: returning %d routes", __func__, count);
|
||
|
return biglist;
|
||
|
}
|
||
|
|
||
|
struct rfapi_next_hop_entry *rfapiEthRouteNode2NextHopList(
|
||
|
struct agg_node *rn, struct rfapi_ip_prefix *rprefix,
|
||
|
uint32_t lifetime, /* put into nexthop entries */
|
||
|
struct rfapi_ip_addr *exclude_vnaddr, /* omit routes to same NVE */
|
||
|
struct agg_table *rfd_rib_table, /* preload NVE rib table */
|
||
|
struct prefix *pfx_target_original) /* query target */
|
||
|
{
|
||
|
int count = 0;
|
||
|
struct rfapi_next_hop_entry *answer = NULL;
|
||
|
struct rfapi_next_hop_entry *last = NULL;
|
||
|
struct agg_node *rib_rn;
|
||
|
|
||
|
rib_rn = rfd_rib_table
|
||
|
? agg_node_get(rfd_rib_table, agg_node_get_prefix(rn))
|
||
|
: NULL;
|
||
|
|
||
|
count = rfapiNhlAddNodeRoutes(rn, rprefix, lifetime, 0, &answer, &last,
|
||
|
NULL, rib_rn, pfx_target_original);
|
||
|
|
||
|
#ifdef DEBUG_ENCAP_MONITOR
|
||
|
vnc_zlog_debug_verbose("%s: node %p: %d non-holddown routes", __func__,
|
||
|
rn, count);
|
||
|
#endif
|
||
|
|
||
|
if (!count) {
|
||
|
count = rfapiNhlAddNodeRoutes(rn, rprefix, lifetime, 1, &answer,
|
||
|
&last, exclude_vnaddr, rib_rn,
|
||
|
pfx_target_original);
|
||
|
vnc_zlog_debug_verbose("%s: node %p: %d holddown routes",
|
||
|
__func__, rn, count);
|
||
|
}
|
||
|
|
||
|
if (rib_rn)
|
||
|
agg_unlock_node(rib_rn);
|
||
|
|
||
|
#ifdef DEBUG_RETURNED_NHL
|
||
|
rfapiPrintNhl(NULL, answer);
|
||
|
#endif
|
||
|
|
||
|
return answer;
|
||
|
}
|
||
|
|
||
|
|
||
|
/*
|
||
|
* Construct nexthop list of all routes in table
|
||
|
*/
|
||
|
struct rfapi_next_hop_entry *rfapiEthRouteTable2NextHopList(
|
||
|
uint32_t logical_net_id, struct rfapi_ip_prefix *rprefix,
|
||
|
uint32_t lifetime, /* put into nexthop entries */
|
||
|
struct rfapi_ip_addr *exclude_vnaddr, /* omit routes to same NVE */
|
||
|
struct agg_table *rfd_rib_table, /* preload NVE rib node */
|
||
|
struct prefix *pfx_target_original) /* query target */
|
||
|
{
|
||
|
struct rfapi_import_table *it;
|
||
|
struct bgp *bgp = bgp_get_default();
|
||
|
struct agg_table *rt;
|
||
|
struct agg_node *rn;
|
||
|
struct rfapi_next_hop_entry *biglist = NULL;
|
||
|
struct rfapi_next_hop_entry *nhl;
|
||
|
struct rfapi_next_hop_entry *tail = NULL;
|
||
|
int count = 0;
|
||
|
|
||
|
|
||
|
it = rfapiMacImportTableGet(bgp, logical_net_id);
|
||
|
rt = it->imported_vpn[AFI_L2VPN];
|
||
|
|
||
|
for (rn = agg_route_top(rt); rn; rn = agg_route_next(rn)) {
|
||
|
|
||
|
nhl = rfapiEthRouteNode2NextHopList(
|
||
|
rn, rprefix, lifetime, exclude_vnaddr, rfd_rib_table,
|
||
|
pfx_target_original);
|
||
|
if (!tail) {
|
||
|
tail = biglist = nhl;
|
||
|
if (tail)
|
||
|
count = 1;
|
||
|
} else {
|
||
|
tail->next = nhl;
|
||
|
}
|
||
|
if (tail) {
|
||
|
while (tail->next) {
|
||
|
++count;
|
||
|
tail = tail->next;
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
|
||
|
vnc_zlog_debug_verbose("%s: returning %d routes", __func__, count);
|
||
|
return biglist;
|
||
|
}
|
||
|
|
||
|
/*
|
||
|
* Insert a new bpi to the imported route table node,
|
||
|
* keeping the list of BPIs sorted best route first
|
||
|
*/
|
||
|
static void rfapiBgpInfoAttachSorted(struct agg_node *rn,
|
||
|
struct bgp_path_info *info_new, afi_t afi,
|
||
|
safi_t safi)
|
||
|
{
|
||
|
struct bgp *bgp;
|
||
|
struct bgp_path_info *prev;
|
||
|
struct bgp_path_info *next;
|
||
|
char pfx_buf[PREFIX2STR_BUFFER] = {};
|
||
|
|
||
|
|
||
|
bgp = bgp_get_default(); /* assume 1 instance for now */
|
||
|
|
||
|
if (VNC_DEBUG(IMPORT_BI_ATTACH)) {
|
||
|
vnc_zlog_debug_verbose("%s: info_new->peer=%p", __func__,
|
||
|
info_new->peer);
|
||
|
vnc_zlog_debug_verbose("%s: info_new->peer->su_remote=%p",
|
||
|
__func__, info_new->peer->su_remote);
|
||
|
}
|
||
|
|
||
|
for (prev = NULL, next = rn->info; next;
|
||
|
prev = next, next = next->next) {
|
||
|
enum bgp_path_selection_reason reason;
|
||
|
|
||
|
if (!bgp
|
||
|
|| (!CHECK_FLAG(info_new->flags, BGP_PATH_REMOVED)
|
||
|
&& CHECK_FLAG(next->flags, BGP_PATH_REMOVED))
|
||
|
|| bgp_path_info_cmp_compatible(bgp, info_new, next,
|
||
|
pfx_buf, afi, safi,
|
||
|
&reason)
|
||
|
== -1) { /* -1 if 1st is better */
|
||
|
break;
|
||
|
}
|
||
|
}
|
||
|
vnc_zlog_debug_verbose("%s: prev=%p, next=%p", __func__, prev, next);
|
||
|
if (prev) {
|
||
|
prev->next = info_new;
|
||
|
} else {
|
||
|
rn->info = info_new;
|
||
|
}
|
||
|
info_new->prev = prev;
|
||
|
info_new->next = next;
|
||
|
if (next)
|
||
|
next->prev = info_new;
|
||
|
bgp_attr_intern(info_new->attr);
|
||
|
}
|
||
|
|
||
|
static void rfapiBgpInfoDetach(struct agg_node *rn, struct bgp_path_info *bpi)
|
||
|
{
|
||
|
/*
|
||
|
* Remove the route (doubly-linked)
|
||
|
*/
|
||
|
// bgp_attr_unintern (&bpi->attr);
|
||
|
if (bpi->next)
|
||
|
bpi->next->prev = bpi->prev;
|
||
|
if (bpi->prev)
|
||
|
bpi->prev->next = bpi->next;
|
||
|
else
|
||
|
rn->info = bpi->next;
|
||
|
}
|
||
|
|
||
|
/*
|
||
|
* For L3-indexed import tables
|
||
|
*/
|
||
|
static int rfapi_bi_peer_rd_cmp(const void *b1, const void *b2)
|
||
|
{
|
||
|
const struct bgp_path_info *bpi1 = b1;
|
||
|
const struct bgp_path_info *bpi2 = b2;
|
||
|
|
||
|
/*
|
||
|
* Compare peers
|
||
|
*/
|
||
|
if (bpi1->peer < bpi2->peer)
|
||
|
return -1;
|
||
|
if (bpi1->peer > bpi2->peer)
|
||
|
return 1;
|
||
|
|
||
|
/*
|
||
|
* compare RDs
|
||
|
*/
|
||
|
return vnc_prefix_cmp((const struct prefix *)&bpi1->extra->vnc->vnc
|
||
|
.import.rd,
|
||
|
(const struct prefix *)&bpi2->extra->vnc->vnc
|
||
|
.import.rd);
|
||
|
}
|
||
|
|
||
|
/*
|
||
|
* For L2-indexed import tables
|
||
|
* The BPIs in these tables should ALWAYS have an aux_prefix set because
|
||
|
* they arrive via IPv4 or IPv6 advertisements.
|
||
|
*/
|
||
|
static int rfapi_bi_peer_rd_aux_cmp(const void *b1, const void *b2)
|
||
|
{
|
||
|
const struct bgp_path_info *bpi1 = b1;
|
||
|
const struct bgp_path_info *bpi2 = b2;
|
||
|
int rc;
|
||
|
|
||
|
/*
|
||
|
* Compare peers
|
||
|
*/
|
||
|
if (bpi1->peer < bpi2->peer)
|
||
|
return -1;
|
||
|
if (bpi1->peer > bpi2->peer)
|
||
|
return 1;
|
||
|
|
||
|
/*
|
||
|
* compare RDs
|
||
|
*/
|
||
|
rc = vnc_prefix_cmp((struct prefix *)&bpi1->extra->vnc->vnc.import.rd,
|
||
|
(struct prefix *)&bpi2->extra->vnc->vnc.import.rd);
|
||
|
if (rc) {
|
||
|
return rc;
|
||
|
}
|
||
|
|
||
|
/*
|
||
|
* L2 import tables can have multiple entries with the
|
||
|
* same MAC address, same RD, but different L3 addresses.
|
||
|
*
|
||
|
* Use presence of aux_prefix with AF=ethernet and prefixlen=1
|
||
|
* as magic value to signify explicit wildcarding of the aux_prefix.
|
||
|
* This magic value will not appear in bona fide bpi entries in
|
||
|
* the import table, but is allowed in the "fake" bpi used to
|
||
|
* probe the table when searching. (We have to test both b1 and b2
|
||
|
* because there is no guarantee of the order the test key and
|
||
|
* the real key will be passed)
|
||
|
*/
|
||
|
if ((bpi1->extra->vnc->vnc.import.aux_prefix.family == AF_ETHERNET &&
|
||
|
(bpi1->extra->vnc->vnc.import.aux_prefix.prefixlen == 1)) ||
|
||
|
(bpi2->extra->vnc->vnc.import.aux_prefix.family == AF_ETHERNET &&
|
||
|
(bpi2->extra->vnc->vnc.import.aux_prefix.prefixlen == 1))) {
|
||
|
/*
|
||
|
* wildcard aux address specified
|
||
|
*/
|
||
|
return 0;
|
||
|
}
|
||
|
|
||
|
return vnc_prefix_cmp(&bpi1->extra->vnc->vnc.import.aux_prefix,
|
||
|
&bpi2->extra->vnc->vnc.import.aux_prefix);
|
||
|
}
|
||
|
|
||
|
|
||
|
/*
|
||
|
* Index on RD and Peer
|
||
|
*/
|
||
|
static void rfapiItBiIndexAdd(struct agg_node *rn, /* Import table VPN node */
|
||
|
struct bgp_path_info *bpi) /* new BPI */
|
||
|
{
|
||
|
struct skiplist *sl;
|
||
|
const struct prefix *p;
|
||
|
|
||
|
assert(rn);
|
||
|
assert(bpi);
|
||
|
assert(bpi->extra);
|
||
|
|
||
|
vnc_zlog_debug_verbose("%s: bpi %p, peer %p, rd %pRDP", __func__, bpi,
|
||
|
bpi->peer, &bpi->extra->vnc->vnc.import.rd);
|
||
|
|
||
|
sl = RFAPI_RDINDEX_W_ALLOC(rn);
|
||
|
if (!sl) {
|
||
|
p = agg_node_get_prefix(rn);
|
||
|
if (AF_ETHERNET == p->family) {
|
||
|
sl = skiplist_new(0, rfapi_bi_peer_rd_aux_cmp, NULL);
|
||
|
} else {
|
||
|
sl = skiplist_new(0, rfapi_bi_peer_rd_cmp, NULL);
|
||
|
}
|
||
|
RFAPI_IT_EXTRA_GET(rn)->u.vpn.idx_rd = sl;
|
||
|
agg_lock_node(rn); /* for skiplist */
|
||
|
}
|
||
|
assert(!skiplist_insert(sl, (void *)bpi, (void *)bpi));
|
||
|
agg_lock_node(rn); /* for skiplist entry */
|
||
|
|
||
|
/* NB: BPIs in import tables are not refcounted */
|
||
|
}
|
||
|
|
||
|
static void rfapiItBiIndexDump(struct agg_node *rn)
|
||
|
{
|
||
|
struct skiplist *sl;
|
||
|
void *cursor = NULL;
|
||
|
struct bgp_path_info *k;
|
||
|
struct bgp_path_info *v;
|
||
|
int rc;
|
||
|
|
||
|
sl = RFAPI_RDINDEX(rn);
|
||
|
if (!sl)
|
||
|
return;
|
||
|
|
||
|
for (rc = skiplist_next(sl, (void **)&k, (void **)&v, &cursor); !rc;
|
||
|
rc = skiplist_next(sl, (void **)&k, (void **)&v, &cursor)) {
|
||
|
|
||
|
char buf[RD_ADDRSTRLEN];
|
||
|
char buf_aux_pfx[PREFIX_STRLEN];
|
||
|
|
||
|
prefix_rd2str(&k->extra->vnc->vnc.import.rd, buf, sizeof(buf),
|
||
|
bgp_get_asnotation(k->peer ? k->peer->bgp : NULL));
|
||
|
if (k->extra->vnc->vnc.import.aux_prefix.family) {
|
||
|
prefix2str(&k->extra->vnc->vnc.import.aux_prefix,
|
||
|
buf_aux_pfx, sizeof(buf_aux_pfx));
|
||
|
} else
|
||
|
strlcpy(buf_aux_pfx, "(none)", sizeof(buf_aux_pfx));
|
||
|
|
||
|
vnc_zlog_debug_verbose("bpi %p, peer %p, rd %s, aux_prefix %s",
|
||
|
k, k->peer, buf, buf_aux_pfx);
|
||
|
}
|
||
|
}
|
||
|
|
||
|
static struct bgp_path_info *rfapiItBiIndexSearch(
|
||
|
struct agg_node *rn, /* Import table VPN node */
|
||
|
struct prefix_rd *prd, struct peer *peer,
|
||
|
const struct prefix *aux_prefix) /* optional L3 addr for L2 ITs */
|
||
|
{
|
||
|
struct skiplist *sl;
|
||
|
int rc;
|
||
|
struct bgp_path_info bpi_fake = {0};
|
||
|
struct bgp_path_info_extra bpi_extra = {0};
|
||
|
struct bgp_path_info_extra_vnc bpi_extra_vnc = { 0 };
|
||
|
struct bgp_path_info *bpi_result;
|
||
|
|
||
|
sl = RFAPI_RDINDEX(rn);
|
||
|
if (!sl)
|
||
|
return NULL;
|
||
|
|
||
|
#ifdef DEBUG_BI_SEARCH
|
||
|
{
|
||
|
char buf_aux_pfx[PREFIX_STRLEN];
|
||
|
|
||
|
if (aux_prefix) {
|
||
|
prefix2str(aux_prefix, buf_aux_pfx,
|
||
|
sizeof(buf_aux_pfx));
|
||
|
} else
|
||
|
strlcpy(buf_aux_pfx, "(nil)", sizeof(buf_aux_pfx));
|
||
|
|
||
|
vnc_zlog_debug_verbose(
|
||
|
"%s want prd=%pRDP, peer=%p, aux_prefix=%s", __func__,
|
||
|
prd, peer, buf_aux_pfx);
|
||
|
rfapiItBiIndexDump(rn);
|
||
|
}
|
||
|
#endif
|
||
|
|
||
|
/* threshold is a WAG */
|
||
|
if (sl->count < 3) {
|
||
|
#ifdef DEBUG_BI_SEARCH
|
||
|
vnc_zlog_debug_verbose("%s: short list algorithm", __func__);
|
||
|
#endif
|
||
|
/* if short list, linear search might be faster */
|
||
|
for (bpi_result = rn->info; bpi_result;
|
||
|
bpi_result = bpi_result->next) {
|
||
|
#ifdef DEBUG_BI_SEARCH
|
||
|
vnc_zlog_debug_verbose("%s: bpi has prd=%pRDP, peer=%p",
|
||
|
__func__,
|
||
|
&bpi_result->extra->vnc->vnc
|
||
|
.import.rd,
|
||
|
bpi_result->peer);
|
||
|
#endif
|
||
|
if (peer == bpi_result->peer &&
|
||
|
!prefix_cmp((struct prefix *)&bpi_result->extra->vnc
|
||
|
->vnc.import.rd,
|
||
|
(struct prefix *)prd)) {
|
||
|
#ifdef DEBUG_BI_SEARCH
|
||
|
vnc_zlog_debug_verbose(
|
||
|
"%s: peer and RD same, doing aux_prefix check",
|
||
|
__func__);
|
||
|
#endif
|
||
|
if (!aux_prefix ||
|
||
|
!prefix_cmp(aux_prefix,
|
||
|
&bpi_result->extra->vnc->vnc
|
||
|
.import.aux_prefix)) {
|
||
|
#ifdef DEBUG_BI_SEARCH
|
||
|
vnc_zlog_debug_verbose("%s: match",
|
||
|
__func__);
|
||
|
#endif
|
||
|
break;
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
return bpi_result;
|
||
|
}
|
||
|
|
||
|
bpi_fake.peer = peer;
|
||
|
bpi_fake.extra = &bpi_extra;
|
||
|
bpi_fake.extra->vnc = &bpi_extra_vnc;
|
||
|
bpi_fake.extra->vnc->vnc.import.rd = *prd;
|
||
|
if (aux_prefix) {
|
||
|
bpi_fake.extra->vnc->vnc.import.aux_prefix = *aux_prefix;
|
||
|
} else {
|
||
|
/* wildcard */
|
||
|
bpi_fake.extra->vnc->vnc.import.aux_prefix.family = AF_ETHERNET;
|
||
|
bpi_fake.extra->vnc->vnc.import.aux_prefix.prefixlen = 1;
|
||
|
}
|
||
|
|
||
|
rc = skiplist_search(sl, (void *)&bpi_fake, (void *)&bpi_result);
|
||
|
|
||
|
if (rc) {
|
||
|
#ifdef DEBUG_BI_SEARCH
|
||
|
vnc_zlog_debug_verbose("%s: no match", __func__);
|
||
|
#endif
|
||
|
return NULL;
|
||
|
}
|
||
|
|
||
|
#ifdef DEBUG_BI_SEARCH
|
||
|
vnc_zlog_debug_verbose("%s: matched bpi=%p", __func__, bpi_result);
|
||
|
#endif
|
||
|
|
||
|
return bpi_result;
|
||
|
}
|
||
|
|
||
|
static void rfapiItBiIndexDel(struct agg_node *rn, /* Import table VPN node */
|
||
|
struct bgp_path_info *bpi) /* old BPI */
|
||
|
{
|
||
|
struct skiplist *sl;
|
||
|
int rc;
|
||
|
|
||
|
vnc_zlog_debug_verbose("%s: bpi %p, peer %p, rd %pRDP", __func__, bpi,
|
||
|
bpi->peer, &bpi->extra->vnc->vnc.import.rd);
|
||
|
|
||
|
sl = RFAPI_RDINDEX(rn);
|
||
|
assert(sl);
|
||
|
|
||
|
rc = skiplist_delete(sl, (void *)(bpi), (void *)bpi);
|
||
|
if (rc) {
|
||
|
rfapiItBiIndexDump(rn);
|
||
|
}
|
||
|
assert(!rc);
|
||
|
|
||
|
agg_unlock_node(rn); /* for skiplist entry */
|
||
|
|
||
|
/* NB: BPIs in import tables are not refcounted */
|
||
|
}
|
||
|
|
||
|
/*
|
||
|
* Add a backreference at the ENCAP node to the VPN route that
|
||
|
* refers to it
|
||
|
*/
|
||
|
static void
|
||
|
rfapiMonitorEncapAdd(struct rfapi_import_table *import_table,
|
||
|
struct prefix *p, /* VN address */
|
||
|
struct agg_node *vpn_rn, /* VPN node */
|
||
|
struct bgp_path_info *vpn_bpi) /* VPN bpi/route */
|
||
|
{
|
||
|
afi_t afi = family2afi(p->family);
|
||
|
struct agg_node *rn;
|
||
|
struct rfapi_monitor_encap *m;
|
||
|
|
||
|
assert(afi);
|
||
|
rn = agg_node_get(import_table->imported_encap[afi], p); /* locks rn */
|
||
|
assert(rn);
|
||
|
|
||
|
m = XCALLOC(MTYPE_RFAPI_MONITOR_ENCAP,
|
||
|
sizeof(struct rfapi_monitor_encap));
|
||
|
|
||
|
m->node = vpn_rn;
|
||
|
m->bpi = vpn_bpi;
|
||
|
m->rn = rn;
|
||
|
|
||
|
/* insert to encap node's list */
|
||
|
m->next = RFAPI_MONITOR_ENCAP(rn);
|
||
|
if (m->next)
|
||
|
m->next->prev = m;
|
||
|
RFAPI_MONITOR_ENCAP_W_ALLOC(rn) = m;
|
||
|
|
||
|
/* for easy lookup when deleting vpn route */
|
||
|
vpn_bpi->extra->vnc->vnc.import.hme = m;
|
||
|
|
||
|
vnc_zlog_debug_verbose("%s: it=%p, vpn_bpi=%p, afi=%d, encap rn=%p, setting vpn_bpi->extra->vnc->vnc.import.hme=%p",
|
||
|
__func__, import_table, vpn_bpi, afi, rn, m);
|
||
|
|
||
|
RFAPI_CHECK_REFCOUNT(rn, SAFI_ENCAP, 0);
|
||
|
bgp_attr_intern(vpn_bpi->attr);
|
||
|
}
|
||
|
|
||
|
static void rfapiMonitorEncapDelete(struct bgp_path_info *vpn_bpi)
|
||
|
{
|
||
|
/*
|
||
|
* Remove encap monitor
|
||
|
*/
|
||
|
vnc_zlog_debug_verbose("%s: vpn_bpi=%p", __func__, vpn_bpi);
|
||
|
if (vpn_bpi->extra) {
|
||
|
struct rfapi_monitor_encap *hme =
|
||
|
vpn_bpi->extra->vnc->vnc.import.hme;
|
||
|
|
||
|
if (hme) {
|
||
|
|
||
|
vnc_zlog_debug_verbose("%s: hme=%p", __func__, hme);
|
||
|
|
||
|
/* Refcount checking takes too long here */
|
||
|
// RFAPI_CHECK_REFCOUNT(hme->rn, SAFI_ENCAP, 0);
|
||
|
if (hme->next)
|
||
|
hme->next->prev = hme->prev;
|
||
|
if (hme->prev)
|
||
|
hme->prev->next = hme->next;
|
||
|
else
|
||
|
RFAPI_MONITOR_ENCAP_W_ALLOC(hme->rn) =
|
||
|
hme->next;
|
||
|
/* Refcount checking takes too long here */
|
||
|
// RFAPI_CHECK_REFCOUNT(hme->rn, SAFI_ENCAP, 1);
|
||
|
|
||
|
/* see if the struct rfapi_it_extra is empty and can be
|
||
|
* freed */
|
||
|
rfapiMonitorExtraPrune(SAFI_ENCAP, hme->rn);
|
||
|
|
||
|
agg_unlock_node(hme->rn); /* decr ref count */
|
||
|
XFREE(MTYPE_RFAPI_MONITOR_ENCAP, hme);
|
||
|
vpn_bpi->extra->vnc->vnc.import.hme = NULL;
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
|
||
|
/*
|
||
|
* Timer callback for withdraw
|
||
|
*/
|
||
|
static void rfapiWithdrawTimerVPN(struct event *t)
|
||
|
{
|
||
|
struct rfapi_withdraw *wcb = EVENT_ARG(t);
|
||
|
struct bgp_path_info *bpi = wcb->info;
|
||
|
struct bgp *bgp = bgp_get_default();
|
||
|
const struct prefix *p;
|
||
|
struct rfapi_monitor_vpn *moved;
|
||
|
afi_t afi;
|
||
|
bool early_exit = false;
|
||
|
|
||
|
if (bgp == NULL) {
|
||
|
vnc_zlog_debug_verbose(
|
||
|
"%s: NULL BGP pointer, assume shutdown race condition!!!",
|
||
|
__func__);
|
||
|
early_exit = true;
|
||
|
}
|
||
|
if (bgp && CHECK_FLAG(bgp->flags, BGP_FLAG_DELETE_IN_PROGRESS)) {
|
||
|
vnc_zlog_debug_verbose(
|
||
|
"%s: BGP delete in progress, assume shutdown race condition!!!",
|
||
|
__func__);
|
||
|
early_exit = true;
|
||
|
}
|
||
|
|
||
|
/* This callback is responsible for the withdraw object's memory */
|
||
|
if (early_exit) {
|
||
|
XFREE(MTYPE_RFAPI_WITHDRAW, wcb);
|
||
|
return;
|
||
|
}
|
||
|
|
||
|
assert(wcb->node);
|
||
|
assert(bpi);
|
||
|
assert(wcb->import_table);
|
||
|
assert(bpi->extra);
|
||
|
|
||
|
RFAPI_CHECK_REFCOUNT(wcb->node, SAFI_MPLS_VPN, wcb->lockoffset);
|
||
|
|
||
|
vnc_zlog_debug_verbose("%s: removing bpi %p at prefix %pRN", __func__,
|
||
|
bpi, wcb->node);
|
||
|
|
||
|
/*
|
||
|
* Remove the route (doubly-linked)
|
||
|
*/
|
||
|
if (CHECK_FLAG(bpi->flags, BGP_PATH_VALID)
|
||
|
&& VALID_INTERIOR_TYPE(bpi->type))
|
||
|
RFAPI_MONITOR_EXTERIOR(wcb->node)->valid_interior_count--;
|
||
|
|
||
|
p = agg_node_get_prefix(wcb->node);
|
||
|
afi = family2afi(p->family);
|
||
|
wcb->import_table->holddown_count[afi] -= 1; /* keep count consistent */
|
||
|
rfapiItBiIndexDel(wcb->node, bpi);
|
||
|
rfapiBgpInfoDetach(wcb->node, bpi); /* with removed bpi */
|
||
|
|
||
|
vnc_import_bgp_exterior_del_route_interior(bgp, wcb->import_table,
|
||
|
wcb->node, bpi);
|
||
|
|
||
|
|
||
|
/*
|
||
|
* If VNC is configured to send response remove messages, AND
|
||
|
* if the removed route had a UN address, do response removal
|
||
|
* processing.
|
||
|
*/
|
||
|
if (!(bgp->rfapi_cfg->flags
|
||
|
& BGP_VNC_CONFIG_RESPONSE_REMOVAL_DISABLE)) {
|
||
|
|
||
|
int has_valid_duplicate = 0;
|
||
|
struct bgp_path_info *bpii;
|
||
|
|
||
|
/*
|
||
|
* First check if there are any OTHER routes at this node
|
||
|
* that have the same nexthop and a valid UN address. If
|
||
|
* there are (e.g., from other peers), then the route isn't
|
||
|
* really gone, so skip sending a response removal message.
|
||
|
*/
|
||
|
for (bpii = wcb->node->info; bpii; bpii = bpii->next) {
|
||
|
if (rfapiVpnBiSamePtUn(bpi, bpii)) {
|
||
|
has_valid_duplicate = 1;
|
||
|
break;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
vnc_zlog_debug_verbose("%s: has_valid_duplicate=%d", __func__,
|
||
|
has_valid_duplicate);
|
||
|
|
||
|
if (!has_valid_duplicate) {
|
||
|
rfapiRibPendingDeleteRoute(bgp, wcb->import_table, afi,
|
||
|
wcb->node);
|
||
|
}
|
||
|
}
|
||
|
|
||
|
rfapiMonitorEncapDelete(bpi);
|
||
|
|
||
|
/*
|
||
|
* If there are no VPN monitors at this VPN Node A,
|
||
|
* we are done
|
||
|
*/
|
||
|
if (!RFAPI_MONITOR_VPN(wcb->node)) {
|
||
|
vnc_zlog_debug_verbose("%s: no VPN monitors at this node",
|
||
|
__func__);
|
||
|
goto done;
|
||
|
}
|
||
|
|
||
|
/*
|
||
|
* rfapiMonitorMoveShorter only moves monitors if there are
|
||
|
* no remaining valid routes at the current node
|
||
|
*/
|
||
|
moved = rfapiMonitorMoveShorter(wcb->node, 1);
|
||
|
|
||
|
if (moved) {
|
||
|
rfapiMonitorMovedUp(wcb->import_table, wcb->node, moved->node,
|
||
|
moved);
|
||
|
}
|
||
|
|
||
|
done:
|
||
|
/*
|
||
|
* Free VPN bpi
|
||
|
*/
|
||
|
rfapiBgpInfoFree(bpi);
|
||
|
wcb->info = NULL;
|
||
|
|
||
|
/*
|
||
|
* If route count at this node has gone to 0, withdraw exported prefix
|
||
|
*/
|
||
|
if (!wcb->node->info) {
|
||
|
/* see if the struct rfapi_it_extra is empty and can be freed */
|
||
|
rfapiMonitorExtraPrune(SAFI_MPLS_VPN, wcb->node);
|
||
|
vnc_direct_bgp_del_prefix(bgp, wcb->import_table, wcb->node);
|
||
|
vnc_zebra_del_prefix(bgp, wcb->import_table, wcb->node);
|
||
|
} else {
|
||
|
/*
|
||
|
* nexthop change event
|
||
|
* vnc_direct_bgp_add_prefix() will recompute the VN addr
|
||
|
* ecommunity
|
||
|
*/
|
||
|
vnc_direct_bgp_add_prefix(bgp, wcb->import_table, wcb->node);
|
||
|
}
|
||
|
|
||
|
RFAPI_CHECK_REFCOUNT(wcb->node, SAFI_MPLS_VPN, 1 + wcb->lockoffset);
|
||
|
agg_unlock_node(wcb->node); /* decr ref count */
|
||
|
XFREE(MTYPE_RFAPI_WITHDRAW, wcb);
|
||
|
}
|
||
|
|
||
|
/*
|
||
|
* This works for multiprotocol extension, but not for plain ol'
|
||
|
* unicast IPv4 because that nexthop is stored in attr->nexthop
|
||
|
*/
|
||
|
void rfapiNexthop2Prefix(struct attr *attr, struct prefix *p)
|
||
|
{
|
||
|
assert(p);
|
||
|
assert(attr);
|
||
|
|
||
|
memset(p, 0, sizeof(struct prefix));
|
||
|
|
||
|
switch (p->family = BGP_MP_NEXTHOP_FAMILY(attr->mp_nexthop_len)) {
|
||
|
case AF_INET:
|
||
|
p->u.prefix4 = attr->mp_nexthop_global_in;
|
||
|
p->prefixlen = IPV4_MAX_BITLEN;
|
||
|
break;
|
||
|
|
||
|
case AF_INET6:
|
||
|
p->u.prefix6 = attr->mp_nexthop_global;
|
||
|
p->prefixlen = IPV6_MAX_BITLEN;
|
||
|
break;
|
||
|
|
||
|
default:
|
||
|
vnc_zlog_debug_verbose("%s: Family is unknown = %d", __func__,
|
||
|
p->family);
|
||
|
}
|
||
|
}
|
||
|
|
||
|
void rfapiUnicastNexthop2Prefix(afi_t afi, struct attr *attr, struct prefix *p)
|
||
|
{
|
||
|
if (afi == AFI_IP) {
|
||
|
p->family = AF_INET;
|
||
|
p->prefixlen = IPV4_MAX_BITLEN;
|
||
|
p->u.prefix4 = attr->nexthop;
|
||
|
} else {
|
||
|
rfapiNexthop2Prefix(attr, p);
|
||
|
}
|
||
|
}
|
||
|
|
||
|
static int rfapiAttrNexthopAddrDifferent(struct prefix *p1, struct prefix *p2)
|
||
|
{
|
||
|
if (!p1 || !p2) {
|
||
|
vnc_zlog_debug_verbose("%s: p1 or p2 is NULL", __func__);
|
||
|
return 1;
|
||
|
}
|
||
|
|
||
|
/*
|
||
|
* Are address families the same?
|
||
|
*/
|
||
|
if (p1->family != p2->family) {
|
||
|
return 1;
|
||
|
}
|
||
|
|
||
|
switch (p1->family) {
|
||
|
case AF_INET:
|
||
|
if (IPV4_ADDR_SAME(&p1->u.prefix4, &p2->u.prefix4))
|
||
|
return 0;
|
||
|
break;
|
||
|
|
||
|
case AF_INET6:
|
||
|
if (IPV6_ADDR_SAME(&p1->u.prefix6, &p2->u.prefix6))
|
||
|
return 0;
|
||
|
break;
|
||
|
|
||
|
default:
|
||
|
assert(1);
|
||
|
}
|
||
|
|
||
|
return 1;
|
||
|
}
|
||
|
|
||
|
static void rfapiCopyUnEncap2VPN(struct bgp_path_info *encap_bpi,
|
||
|
struct bgp_path_info *vpn_bpi)
|
||
|
{
|
||
|
if (!vpn_bpi || !vpn_bpi->extra) {
|
||
|
zlog_warn("%s: no vpn bpi attr/extra, can't copy UN address",
|
||
|
__func__);
|
||
|
return;
|
||
|
}
|
||
|
|
||
|
switch (BGP_MP_NEXTHOP_FAMILY(encap_bpi->attr->mp_nexthop_len)) {
|
||
|
case AF_INET:
|
||
|
|
||
|
/*
|
||
|
* instrumentation to debug segfault of 091127
|
||
|
*/
|
||
|
vnc_zlog_debug_verbose("%s: vpn_bpi=%p", __func__, vpn_bpi);
|
||
|
vnc_zlog_debug_verbose("%s: vpn_bpi->extra=%p", __func__,
|
||
|
vpn_bpi->extra);
|
||
|
|
||
|
vpn_bpi->extra->vnc->vnc.import.un_family = AF_INET;
|
||
|
vpn_bpi->extra->vnc->vnc.import.un.addr4 =
|
||
|
encap_bpi->attr->mp_nexthop_global_in;
|
||
|
break;
|
||
|
|
||
|
case AF_INET6:
|
||
|
vpn_bpi->extra->vnc->vnc.import.un_family = AF_INET6;
|
||
|
vpn_bpi->extra->vnc->vnc.import.un.addr6 =
|
||
|
encap_bpi->attr->mp_nexthop_global;
|
||
|
break;
|
||
|
|
||
|
default:
|
||
|
zlog_warn("%s: invalid encap nexthop length: %d", __func__,
|
||
|
encap_bpi->attr->mp_nexthop_len);
|
||
|
vpn_bpi->extra->vnc->vnc.import.un_family = AF_UNSPEC;
|
||
|
break;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
/*
|
||
|
* returns 0 on success, nonzero on error
|
||
|
*/
|
||
|
static int
|
||
|
rfapiWithdrawEncapUpdateCachedUn(struct rfapi_import_table *import_table,
|
||
|
struct bgp_path_info *encap_bpi,
|
||
|
struct agg_node *vpn_rn,
|
||
|
struct bgp_path_info *vpn_bpi)
|
||
|
{
|
||
|
if (!encap_bpi) {
|
||
|
|
||
|
/*
|
||
|
* clear cached UN address
|
||
|
*/
|
||
|
if (!vpn_bpi || !vpn_bpi->extra) {
|
||
|
zlog_warn(
|
||
|
"%s: missing VPN bpi/extra, can't clear UN addr",
|
||
|
__func__);
|
||
|
return 1;
|
||
|
}
|
||
|
vpn_bpi->extra->vnc->vnc.import.un_family = AF_UNSPEC;
|
||
|
memset(&vpn_bpi->extra->vnc->vnc.import.un, 0,
|
||
|
sizeof(vpn_bpi->extra->vnc->vnc.import.un));
|
||
|
if (CHECK_FLAG(vpn_bpi->flags, BGP_PATH_VALID)) {
|
||
|
if (rfapiGetVncTunnelUnAddr(vpn_bpi->attr, NULL)) {
|
||
|
UNSET_FLAG(vpn_bpi->flags, BGP_PATH_VALID);
|
||
|
if (VALID_INTERIOR_TYPE(vpn_bpi->type))
|
||
|
RFAPI_MONITOR_EXTERIOR(vpn_rn)
|
||
|
->valid_interior_count--;
|
||
|
/* signal interior route withdrawal to
|
||
|
* import-exterior */
|
||
|
vnc_import_bgp_exterior_del_route_interior(
|
||
|
bgp_get_default(), import_table, vpn_rn,
|
||
|
vpn_bpi);
|
||
|
}
|
||
|
}
|
||
|
|
||
|
} else {
|
||
|
if (!vpn_bpi) {
|
||
|
zlog_warn("%s: missing VPN bpi, can't clear UN addr",
|
||
|
__func__);
|
||
|
return 1;
|
||
|
}
|
||
|
rfapiCopyUnEncap2VPN(encap_bpi, vpn_bpi);
|
||
|
if (!CHECK_FLAG(vpn_bpi->flags, BGP_PATH_VALID)) {
|
||
|
SET_FLAG(vpn_bpi->flags, BGP_PATH_VALID);
|
||
|
if (VALID_INTERIOR_TYPE(vpn_bpi->type))
|
||
|
RFAPI_MONITOR_EXTERIOR(vpn_rn)
|
||
|
->valid_interior_count++;
|
||
|
/* signal interior route withdrawal to import-exterior
|
||
|
*/
|
||
|
vnc_import_bgp_exterior_add_route_interior(
|
||
|
bgp_get_default(), import_table, vpn_rn,
|
||
|
vpn_bpi);
|
||
|
}
|
||
|
}
|
||
|
return 0;
|
||
|
}
|
||
|
|
||
|
static void rfapiWithdrawTimerEncap(struct event *t)
|
||
|
{
|
||
|
struct rfapi_withdraw *wcb = EVENT_ARG(t);
|
||
|
struct bgp_path_info *bpi = wcb->info;
|
||
|
int was_first_route = 0;
|
||
|
struct rfapi_monitor_encap *em;
|
||
|
struct skiplist *vpn_node_sl = skiplist_new(0, NULL, NULL);
|
||
|
|
||
|
assert(wcb->node);
|
||
|
assert(bpi);
|
||
|
assert(wcb->import_table);
|
||
|
|
||
|
RFAPI_CHECK_REFCOUNT(wcb->node, SAFI_ENCAP, 0);
|
||
|
|
||
|
if (wcb->node->info == bpi)
|
||
|
was_first_route = 1;
|
||
|
|
||
|
/*
|
||
|
* Remove the route/bpi and free it
|
||
|
*/
|
||
|
rfapiBgpInfoDetach(wcb->node, bpi);
|
||
|
rfapiBgpInfoFree(bpi);
|
||
|
|
||
|
if (!was_first_route)
|
||
|
goto done;
|
||
|
|
||
|
for (em = RFAPI_MONITOR_ENCAP(wcb->node); em; em = em->next) {
|
||
|
|
||
|
/*
|
||
|
* Update monitoring VPN BPIs with new encap info at the
|
||
|
* head of the encap bpi chain (which could be NULL after
|
||
|
* removing the expiring bpi above)
|
||
|
*/
|
||
|
if (rfapiWithdrawEncapUpdateCachedUn(wcb->import_table,
|
||
|
wcb->node->info, em->node,
|
||
|
em->bpi))
|
||
|
continue;
|
||
|
|
||
|
/*
|
||
|
* Build a list of unique VPN nodes referenced by these
|
||
|
* monitors.
|
||
|
* Use a skiplist for speed.
|
||
|
*/
|
||
|
skiplist_insert(vpn_node_sl, em->node, em->node);
|
||
|
}
|
||
|
|
||
|
|
||
|
/*
|
||
|
* for each VPN node referenced in the ENCAP monitors:
|
||
|
*/
|
||
|
struct agg_node *rn;
|
||
|
while (!skiplist_first(vpn_node_sl, (void **)&rn, NULL)) {
|
||
|
if (!wcb->node->info) {
|
||
|
struct rfapi_monitor_vpn *moved;
|
||
|
|
||
|
moved = rfapiMonitorMoveShorter(rn, 0);
|
||
|
if (moved) {
|
||
|
// rfapiDoRouteCallback(wcb->import_table,
|
||
|
// moved->node, moved);
|
||
|
rfapiMonitorMovedUp(wcb->import_table, rn,
|
||
|
moved->node, moved);
|
||
|
}
|
||
|
} else {
|
||
|
// rfapiDoRouteCallback(wcb->import_table, rn, NULL);
|
||
|
rfapiMonitorItNodeChanged(wcb->import_table, rn, NULL);
|
||
|
}
|
||
|
skiplist_delete_first(vpn_node_sl);
|
||
|
}
|
||
|
|
||
|
done:
|
||
|
RFAPI_CHECK_REFCOUNT(wcb->node, SAFI_ENCAP, 1);
|
||
|
agg_unlock_node(wcb->node); /* decr ref count */
|
||
|
XFREE(MTYPE_RFAPI_WITHDRAW, wcb);
|
||
|
skiplist_free(vpn_node_sl);
|
||
|
}
|
||
|
|
||
|
|
||
|
/*
|
||
|
* Works for both VPN and ENCAP routes; timer_service_func is different
|
||
|
* in each case
|
||
|
*/
|
||
|
static void
|
||
|
rfapiBiStartWithdrawTimer(struct rfapi_import_table *import_table,
|
||
|
struct agg_node *rn, struct bgp_path_info *bpi,
|
||
|
afi_t afi, safi_t safi,
|
||
|
void (*timer_service_func)(struct event *))
|
||
|
{
|
||
|
uint32_t lifetime;
|
||
|
struct rfapi_withdraw *wcb;
|
||
|
|
||
|
if (CHECK_FLAG(bpi->flags, BGP_PATH_REMOVED)) {
|
||
|
/*
|
||
|
* Already on the path to being withdrawn,
|
||
|
* should already have a timer set up to
|
||
|
* delete it.
|
||
|
*/
|
||
|
vnc_zlog_debug_verbose(
|
||
|
"%s: already being withdrawn, do nothing", __func__);
|
||
|
return;
|
||
|
}
|
||
|
|
||
|
rfapiGetVncLifetime(bpi->attr, &lifetime);
|
||
|
vnc_zlog_debug_verbose("%s: VNC lifetime is %u", __func__, lifetime);
|
||
|
|
||
|
/*
|
||
|
* withdrawn routes get to hang around for a while
|
||
|
*/
|
||
|
SET_FLAG(bpi->flags, BGP_PATH_REMOVED);
|
||
|
|
||
|
/* set timer to remove the route later */
|
||
|
lifetime = rfapiGetHolddownFromLifetime(lifetime);
|
||
|
vnc_zlog_debug_verbose("%s: using timeout %u", __func__, lifetime);
|
||
|
|
||
|
/*
|
||
|
* Stash import_table, node, and info for use by timer
|
||
|
* service routine, which is supposed to free the wcb.
|
||
|
*/
|
||
|
wcb = XCALLOC(MTYPE_RFAPI_WITHDRAW, sizeof(struct rfapi_withdraw));
|
||
|
wcb->node = rn;
|
||
|
wcb->info = bpi;
|
||
|
wcb->import_table = import_table;
|
||
|
bgp_attr_intern(bpi->attr);
|
||
|
|
||
|
if (VNC_DEBUG(VERBOSE)) {
|
||
|
vnc_zlog_debug_verbose(
|
||
|
"%s: wcb values: node=%p, info=%p, import_table=%p (bpi follows)",
|
||
|
__func__, wcb->node, wcb->info, wcb->import_table);
|
||
|
rfapiPrintBi(NULL, bpi);
|
||
|
}
|
||
|
|
||
|
|
||
|
assert(bpi->extra);
|
||
|
if (lifetime > UINT32_MAX / 1001) {
|
||
|
/* sub-optimal case, but will probably never happen */
|
||
|
bpi->extra->vnc->vnc.import.timer = NULL;
|
||
|
event_add_timer(bm->master, timer_service_func, wcb, lifetime,
|
||
|
&bpi->extra->vnc->vnc.import.timer);
|
||
|
} else {
|
||
|
static uint32_t jitter;
|
||
|
uint32_t lifetime_msec;
|
||
|
|
||
|
/*
|
||
|
* the goal here is to spread out the timers so they are
|
||
|
* sortable in the skip list
|
||
|
*/
|
||
|
if (++jitter >= 1000)
|
||
|
jitter = 0;
|
||
|
|
||
|
lifetime_msec = (lifetime * 1000) + jitter;
|
||
|
|
||
|
bpi->extra->vnc->vnc.import.timer = NULL;
|
||
|
event_add_timer_msec(bm->master, timer_service_func, wcb,
|
||
|
lifetime_msec,
|
||
|
&bpi->extra->vnc->vnc.import.timer);
|
||
|
}
|
||
|
|
||
|
/* re-sort route list (BGP_PATH_REMOVED routes are last) */
|
||
|
if (((struct bgp_path_info *)rn->info)->next) {
|
||
|
rfapiBgpInfoDetach(rn, bpi);
|
||
|
rfapiBgpInfoAttachSorted(rn, bpi, afi, safi);
|
||
|
}
|
||
|
}
|
||
|
|
||
|
|
||
|
typedef void(rfapi_bi_filtered_import_f)(struct rfapi_import_table *table,
|
||
|
int action, struct peer *peer,
|
||
|
void *rfd, const struct prefix *prefix,
|
||
|
const struct prefix *aux_prefix,
|
||
|
afi_t afi, struct prefix_rd *prd,
|
||
|
struct attr *attr, uint8_t type,
|
||
|
uint8_t sub_type, uint32_t *label);
|
||
|
|
||
|
|
||
|
static void rfapiExpireEncapNow(struct rfapi_import_table *it,
|
||
|
struct agg_node *rn, struct bgp_path_info *bpi)
|
||
|
{
|
||
|
struct rfapi_withdraw *wcb;
|
||
|
struct event t;
|
||
|
|
||
|
/*
|
||
|
* pretend we're an expiring timer
|
||
|
*/
|
||
|
wcb = XCALLOC(MTYPE_RFAPI_WITHDRAW, sizeof(struct rfapi_withdraw));
|
||
|
wcb->info = bpi;
|
||
|
wcb->node = rn;
|
||
|
wcb->import_table = it;
|
||
|
memset(&t, 0, sizeof(t));
|
||
|
t.arg = wcb;
|
||
|
rfapiWithdrawTimerEncap(&t); /* frees wcb */
|
||
|
}
|
||
|
|
||
|
static int rfapiGetNexthop(struct attr *attr, struct prefix *prefix)
|
||
|
{
|
||
|
switch (BGP_MP_NEXTHOP_FAMILY(attr->mp_nexthop_len)) {
|
||
|
case AF_INET:
|
||
|
prefix->family = AF_INET;
|
||
|
prefix->prefixlen = IPV4_MAX_BITLEN;
|
||
|
prefix->u.prefix4 = attr->mp_nexthop_global_in;
|
||
|
break;
|
||
|
case AF_INET6:
|
||
|
prefix->family = AF_INET6;
|
||
|
prefix->prefixlen = IPV6_MAX_BITLEN;
|
||
|
prefix->u.prefix6 = attr->mp_nexthop_global;
|
||
|
break;
|
||
|
default:
|
||
|
vnc_zlog_debug_verbose("%s: unknown attr->mp_nexthop_len %d",
|
||
|
__func__, attr->mp_nexthop_len);
|
||
|
return EINVAL;
|
||
|
}
|
||
|
return 0;
|
||
|
}
|
||
|
|
||
|
/*
|
||
|
* import a bgp_path_info if its route target list intersects with the
|
||
|
* import table's route target list
|
||
|
*/
|
||
|
static void rfapiBgpInfoFilteredImportEncap(
|
||
|
struct rfapi_import_table *import_table, int action, struct peer *peer,
|
||
|
void *rfd, /* set for looped back routes */
|
||
|
const struct prefix *p,
|
||
|
const struct prefix *aux_prefix, /* Unused for encap routes */
|
||
|
afi_t afi, struct prefix_rd *prd,
|
||
|
struct attr *attr, /* part of bgp_path_info */
|
||
|
uint8_t type, /* part of bgp_path_info */
|
||
|
uint8_t sub_type, /* part of bgp_path_info */
|
||
|
uint32_t *label) /* part of bgp_path_info */
|
||
|
{
|
||
|
struct agg_table *rt = NULL;
|
||
|
struct agg_node *rn;
|
||
|
struct bgp_path_info *info_new;
|
||
|
struct bgp_path_info *bpi;
|
||
|
struct bgp_path_info *next;
|
||
|
char buf[BUFSIZ];
|
||
|
|
||
|
struct prefix p_firstbpi_old;
|
||
|
struct prefix p_firstbpi_new;
|
||
|
int replacing = 0;
|
||
|
const char *action_str = NULL;
|
||
|
struct prefix un_prefix;
|
||
|
|
||
|
struct bgp *bgp;
|
||
|
bgp = bgp_get_default(); /* assume 1 instance for now */
|
||
|
|
||
|
switch (action) {
|
||
|
case FIF_ACTION_UPDATE:
|
||
|
action_str = "update";
|
||
|
break;
|
||
|
case FIF_ACTION_WITHDRAW:
|
||
|
action_str = "withdraw";
|
||
|
break;
|
||
|
case FIF_ACTION_KILL:
|
||
|
action_str = "kill";
|
||
|
break;
|
||
|
default:
|
||
|
assert(0);
|
||
|
break;
|
||
|
}
|
||
|
|
||
|
vnc_zlog_debug_verbose(
|
||
|
"%s: entry: %s: prefix %s/%d", __func__, action_str,
|
||
|
inet_ntop(p->family, &p->u.prefix, buf, sizeof(buf)),
|
||
|
p->prefixlen);
|
||
|
|
||
|
memset(&p_firstbpi_old, 0, sizeof(p_firstbpi_old));
|
||
|
memset(&p_firstbpi_new, 0, sizeof(p_firstbpi_new));
|
||
|
|
||
|
if (action == FIF_ACTION_UPDATE) {
|
||
|
/*
|
||
|
* Compare rt lists. If no intersection, don't import this route
|
||
|
* On a withdraw, peer and RD are sufficient to determine if
|
||
|
* we should act.
|
||
|
*/
|
||
|
if (!attr || !bgp_attr_get_ecommunity(attr)) {
|
||
|
|
||
|
vnc_zlog_debug_verbose(
|
||
|
"%s: attr, extra, or ecommunity missing, not importing",
|
||
|
__func__);
|
||
|
return;
|
||
|
}
|
||
|
#ifdef RFAPI_REQUIRE_ENCAP_BEEC
|
||
|
if (!rfapiEcommunitiesMatchBeec(
|
||
|
bgp_attr_get_ecommunity(attr))) {
|
||
|
vnc_zlog_debug_verbose(
|
||
|
"%s: it=%p: no match for BGP Encapsulation ecommunity",
|
||
|
__func__, import_table);
|
||
|
return;
|
||
|
}
|
||
|
#endif
|
||
|
if (!rfapiEcommunitiesIntersect(
|
||
|
import_table->rt_import_list,
|
||
|
bgp_attr_get_ecommunity(attr))) {
|
||
|
|
||
|
vnc_zlog_debug_verbose(
|
||
|
"%s: it=%p: no ecommunity intersection",
|
||
|
__func__, import_table);
|
||
|
return;
|
||
|
}
|
||
|
|
||
|
/*
|
||
|
* Updates must also have a nexthop address
|
||
|
*/
|
||
|
memset(&un_prefix, 0,
|
||
|
sizeof(un_prefix)); /* keep valgrind happy */
|
||
|
if (rfapiGetNexthop(attr, &un_prefix)) {
|
||
|
vnc_zlog_debug_verbose("%s: missing nexthop address",
|
||
|
__func__);
|
||
|
return;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
/*
|
||
|
* Figure out which radix tree the route would go into
|
||
|
*/
|
||
|
switch (afi) {
|
||
|
case AFI_IP:
|
||
|
case AFI_IP6:
|
||
|
rt = import_table->imported_encap[afi];
|
||
|
break;
|
||
|
|
||
|
case AFI_UNSPEC:
|
||
|
case AFI_L2VPN:
|
||
|
case AFI_MAX:
|
||
|
flog_err(EC_LIB_DEVELOPMENT, "%s: bad afi %d", __func__, afi);
|
||
|
return;
|
||
|
}
|
||
|
|
||
|
/*
|
||
|
* agg_node_lookup returns a node only if there is at least
|
||
|
* one route attached.
|
||
|
*/
|
||
|
rn = agg_node_lookup(rt, p);
|
||
|
|
||
|
#ifdef DEBUG_ENCAP_MONITOR
|
||
|
vnc_zlog_debug_verbose("%s: initial encap lookup(it=%p) rn=%p",
|
||
|
__func__, import_table, rn);
|
||
|
#endif
|
||
|
|
||
|
if (rn) {
|
||
|
|
||
|
RFAPI_CHECK_REFCOUNT(rn, SAFI_ENCAP, 1);
|
||
|
agg_unlock_node(rn); /* undo lock in agg_node_lookup */
|
||
|
|
||
|
|
||
|
/*
|
||
|
* capture nexthop of first bpi
|
||
|
*/
|
||
|
if (rn->info) {
|
||
|
rfapiNexthop2Prefix(
|
||
|
((struct bgp_path_info *)(rn->info))->attr,
|
||
|
&p_firstbpi_old);
|
||
|
}
|
||
|
|
||
|
for (bpi = rn->info; bpi; bpi = bpi->next) {
|
||
|
|
||
|
/*
|
||
|
* Does this bgp_path_info refer to the same route
|
||
|
* as we are trying to add?
|
||
|
*/
|
||
|
vnc_zlog_debug_verbose("%s: comparing BPI %p", __func__,
|
||
|
bpi);
|
||
|
|
||
|
|
||
|
/*
|
||
|
* Compare RDs
|
||
|
*
|
||
|
* RD of import table bpi is in
|
||
|
* bpi->extra->vnc->vnc.import.rd RD of info_orig is in prd
|
||
|
*/
|
||
|
if (!bpi->extra) {
|
||
|
vnc_zlog_debug_verbose("%s: no bpi->extra",
|
||
|
__func__);
|
||
|
continue;
|
||
|
}
|
||
|
if (prefix_cmp((struct prefix *)&bpi->extra->vnc->vnc
|
||
|
.import.rd,
|
||
|
(struct prefix *)prd)) {
|
||
|
vnc_zlog_debug_verbose("%s: prd does not match",
|
||
|
__func__);
|
||
|
continue;
|
||
|
}
|
||
|
|
||
|
/*
|
||
|
* Compare peers
|
||
|
*/
|
||
|
if (bpi->peer != peer) {
|
||
|
vnc_zlog_debug_verbose(
|
||
|
"%s: peer does not match", __func__);
|
||
|
continue;
|
||
|
}
|
||
|
|
||
|
vnc_zlog_debug_verbose("%s: found matching bpi",
|
||
|
__func__);
|
||
|
|
||
|
/* Same route. Delete this bpi, replace with new one */
|
||
|
|
||
|
if (action == FIF_ACTION_WITHDRAW) {
|
||
|
|
||
|
vnc_zlog_debug_verbose(
|
||
|
"%s: withdrawing at prefix %pRN",
|
||
|
__func__, rn);
|
||
|
|
||
|
rfapiBiStartWithdrawTimer(
|
||
|
import_table, rn, bpi, afi, SAFI_ENCAP,
|
||
|
rfapiWithdrawTimerEncap);
|
||
|
|
||
|
} else {
|
||
|
vnc_zlog_debug_verbose(
|
||
|
"%s: %s at prefix %pRN", __func__,
|
||
|
((action == FIF_ACTION_KILL)
|
||
|
? "killing"
|
||
|
: "replacing"),
|
||
|
rn);
|
||
|
|
||
|
/*
|
||
|
* If this route is waiting to be deleted
|
||
|
* because of
|
||
|
* a previous withdraw, we must cancel its
|
||
|
* timer.
|
||
|
*/
|
||
|
if (CHECK_FLAG(bpi->flags, BGP_PATH_REMOVED) &&
|
||
|
bpi->extra->vnc->vnc.import.timer) {
|
||
|
struct rfapi_withdraw *wcb = EVENT_ARG(
|
||
|
bpi->extra->vnc->vnc.import.timer);
|
||
|
|
||
|
XFREE(MTYPE_RFAPI_WITHDRAW, wcb);
|
||
|
EVENT_OFF(bpi->extra->vnc->vnc.import
|
||
|
.timer);
|
||
|
}
|
||
|
|
||
|
if (action == FIF_ACTION_UPDATE) {
|
||
|
rfapiBgpInfoDetach(rn, bpi);
|
||
|
rfapiBgpInfoFree(bpi);
|
||
|
replacing = 1;
|
||
|
} else {
|
||
|
/*
|
||
|
* Kill: do export stuff when removing
|
||
|
* bpi
|
||
|
*/
|
||
|
struct rfapi_withdraw *wcb;
|
||
|
struct event t;
|
||
|
|
||
|
/*
|
||
|
* pretend we're an expiring timer
|
||
|
*/
|
||
|
wcb = XCALLOC(
|
||
|
MTYPE_RFAPI_WITHDRAW,
|
||
|
sizeof(struct rfapi_withdraw));
|
||
|
wcb->info = bpi;
|
||
|
wcb->node = rn;
|
||
|
wcb->import_table = import_table;
|
||
|
memset(&t, 0, sizeof(t));
|
||
|
t.arg = wcb;
|
||
|
rfapiWithdrawTimerEncap(
|
||
|
&t); /* frees wcb */
|
||
|
}
|
||
|
}
|
||
|
|
||
|
break;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
if (rn)
|
||
|
RFAPI_CHECK_REFCOUNT(rn, SAFI_ENCAP, replacing ? 1 : 0);
|
||
|
|
||
|
if (action == FIF_ACTION_WITHDRAW || action == FIF_ACTION_KILL)
|
||
|
return;
|
||
|
|
||
|
info_new =
|
||
|
rfapiBgpInfoCreate(attr, peer, rfd, prd, type, sub_type, NULL);
|
||
|
|
||
|
if (rn) {
|
||
|
if (!replacing)
|
||
|
agg_lock_node(rn); /* incr ref count for new BPI */
|
||
|
} else {
|
||
|
rn = agg_node_get(rt, p);
|
||
|
}
|
||
|
|
||
|
vnc_zlog_debug_verbose("%s: (afi=%d, rn=%p) inserting at prefix %pRN",
|
||
|
__func__, afi, rn, rn);
|
||
|
|
||
|
rfapiBgpInfoAttachSorted(rn, info_new, afi, SAFI_ENCAP);
|
||
|
|
||
|
/*
|
||
|
* Delete holddown routes from same NVE. See details in
|
||
|
* rfapiBgpInfoFilteredImportVPN()
|
||
|
*/
|
||
|
for (bpi = info_new->next; bpi; bpi = next) {
|
||
|
|
||
|
struct prefix pfx_un;
|
||
|
int un_match = 0;
|
||
|
|
||
|
next = bpi->next;
|
||
|
if (!CHECK_FLAG(bpi->flags, BGP_PATH_REMOVED))
|
||
|
continue;
|
||
|
|
||
|
/*
|
||
|
* We already match the VN address (it is the prefix
|
||
|
* of the route node)
|
||
|
*/
|
||
|
|
||
|
if (!rfapiGetNexthop(bpi->attr, &pfx_un)
|
||
|
&& prefix_same(&pfx_un, &un_prefix)) {
|
||
|
|
||
|
un_match = 1;
|
||
|
}
|
||
|
|
||
|
if (!un_match)
|
||
|
continue;
|
||
|
|
||
|
vnc_zlog_debug_verbose(
|
||
|
"%s: removing holddown bpi matching NVE of new route",
|
||
|
__func__);
|
||
|
if (bpi->extra->vnc->vnc.import.timer) {
|
||
|
struct rfapi_withdraw *wcb =
|
||
|
EVENT_ARG(bpi->extra->vnc->vnc.import.timer);
|
||
|
|
||
|
XFREE(MTYPE_RFAPI_WITHDRAW, wcb);
|
||
|
EVENT_OFF(bpi->extra->vnc->vnc.import.timer);
|
||
|
}
|
||
|
rfapiExpireEncapNow(import_table, rn, bpi);
|
||
|
}
|
||
|
|
||
|
rfapiNexthop2Prefix(((struct bgp_path_info *)(rn->info))->attr,
|
||
|
&p_firstbpi_new);
|
||
|
|
||
|
/*
|
||
|
* If the nexthop address of the selected Encap route (i.e.,
|
||
|
* the UN address) has changed, then we must update the VPN
|
||
|
* routes that refer to this Encap route and possibly force
|
||
|
* rfapi callbacks.
|
||
|
*/
|
||
|
if (rfapiAttrNexthopAddrDifferent(&p_firstbpi_old, &p_firstbpi_new)) {
|
||
|
|
||
|
struct rfapi_monitor_encap *m;
|
||
|
struct rfapi_monitor_encap *mnext;
|
||
|
|
||
|
struct agg_node *referenced_vpn_prefix;
|
||
|
|
||
|
/*
|
||
|
* Optimized approach: build radix tree on the fly to
|
||
|
* hold list of VPN nodes referenced by the ENCAP monitors
|
||
|
*
|
||
|
* The nodes in this table correspond to prefixes of VPN routes.
|
||
|
* The "info" pointer of the node points to a chain of
|
||
|
* struct rfapi_monitor_encap, each of which refers to a
|
||
|
* specific VPN node.
|
||
|
*/
|
||
|
struct agg_table *referenced_vpn_table;
|
||
|
|
||
|
referenced_vpn_table = agg_table_init();
|
||
|
|
||
|
/*
|
||
|
* iterate over the set of monitors at this ENCAP node.
|
||
|
*/
|
||
|
#ifdef DEBUG_ENCAP_MONITOR
|
||
|
vnc_zlog_debug_verbose("%s: examining monitors at rn=%p",
|
||
|
__func__, rn);
|
||
|
#endif
|
||
|
for (m = RFAPI_MONITOR_ENCAP(rn); m; m = m->next) {
|
||
|
const struct prefix *p;
|
||
|
|
||
|
/*
|
||
|
* For each referenced bpi/route, copy the ENCAP route's
|
||
|
* nexthop to the VPN route's cached UN address field
|
||
|
* and set
|
||
|
* the address family of the cached UN address field.
|
||
|
*/
|
||
|
rfapiCopyUnEncap2VPN(info_new, m->bpi);
|
||
|
if (!CHECK_FLAG(m->bpi->flags, BGP_PATH_VALID)) {
|
||
|
SET_FLAG(m->bpi->flags, BGP_PATH_VALID);
|
||
|
if (VALID_INTERIOR_TYPE(m->bpi->type))
|
||
|
RFAPI_MONITOR_EXTERIOR(m->node)
|
||
|
->valid_interior_count++;
|
||
|
vnc_import_bgp_exterior_add_route_interior(
|
||
|
bgp, import_table, m->node, m->bpi);
|
||
|
}
|
||
|
|
||
|
/*
|
||
|
* Build a list of unique VPN nodes referenced by these
|
||
|
* monitors
|
||
|
*
|
||
|
* There could be more than one VPN node here with a
|
||
|
* given
|
||
|
* prefix. Those are currently in an unsorted linear
|
||
|
* list
|
||
|
* per prefix.
|
||
|
*/
|
||
|
p = agg_node_get_prefix(m->node);
|
||
|
referenced_vpn_prefix =
|
||
|
agg_node_get(referenced_vpn_table, p);
|
||
|
assert(referenced_vpn_prefix);
|
||
|
for (mnext = referenced_vpn_prefix->info; mnext;
|
||
|
mnext = mnext->next) {
|
||
|
|
||
|
if (mnext->node == m->node)
|
||
|
break;
|
||
|
}
|
||
|
|
||
|
if (mnext) {
|
||
|
/*
|
||
|
* already have an entry for this VPN node
|
||
|
*/
|
||
|
agg_unlock_node(referenced_vpn_prefix);
|
||
|
} else {
|
||
|
mnext = XCALLOC(
|
||
|
MTYPE_RFAPI_MONITOR_ENCAP,
|
||
|
sizeof(struct rfapi_monitor_encap));
|
||
|
mnext->node = m->node;
|
||
|
mnext->next = referenced_vpn_prefix->info;
|
||
|
referenced_vpn_prefix->info = mnext;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
/*
|
||
|
* for each VPN node referenced in the ENCAP monitors:
|
||
|
*/
|
||
|
for (referenced_vpn_prefix =
|
||
|
agg_route_top(referenced_vpn_table);
|
||
|
referenced_vpn_prefix;
|
||
|
referenced_vpn_prefix =
|
||
|
agg_route_next(referenced_vpn_prefix)) {
|
||
|
|
||
|
while ((m = referenced_vpn_prefix->info)) {
|
||
|
|
||
|
struct agg_node *n;
|
||
|
|
||
|
rfapiMonitorMoveLonger(m->node);
|
||
|
for (n = m->node; n; n = agg_node_parent(n)) {
|
||
|
// rfapiDoRouteCallback(import_table, n,
|
||
|
// NULL);
|
||
|
}
|
||
|
rfapiMonitorItNodeChanged(import_table, m->node,
|
||
|
NULL);
|
||
|
|
||
|
referenced_vpn_prefix->info = m->next;
|
||
|
agg_unlock_node(referenced_vpn_prefix);
|
||
|
XFREE(MTYPE_RFAPI_MONITOR_ENCAP, m);
|
||
|
}
|
||
|
}
|
||
|
agg_table_finish(referenced_vpn_table);
|
||
|
}
|
||
|
|
||
|
RFAPI_CHECK_REFCOUNT(rn, SAFI_ENCAP, 0);
|
||
|
}
|
||
|
|
||
|
static void rfapiExpireVpnNow(struct rfapi_import_table *it,
|
||
|
struct agg_node *rn, struct bgp_path_info *bpi,
|
||
|
int lockoffset)
|
||
|
{
|
||
|
struct rfapi_withdraw *wcb;
|
||
|
struct event t;
|
||
|
|
||
|
/*
|
||
|
* pretend we're an expiring timer
|
||
|
*/
|
||
|
wcb = XCALLOC(MTYPE_RFAPI_WITHDRAW, sizeof(struct rfapi_withdraw));
|
||
|
wcb->info = bpi;
|
||
|
wcb->node = rn;
|
||
|
wcb->import_table = it;
|
||
|
wcb->lockoffset = lockoffset;
|
||
|
memset(&t, 0, sizeof(t));
|
||
|
t.arg = wcb;
|
||
|
rfapiWithdrawTimerVPN(&t); /* frees wcb */
|
||
|
}
|
||
|
|
||
|
|
||
|
/*
|
||
|
* import a bgp_path_info if its route target list intersects with the
|
||
|
* import table's route target list
|
||
|
*/
|
||
|
void rfapiBgpInfoFilteredImportVPN(
|
||
|
struct rfapi_import_table *import_table, int action, struct peer *peer,
|
||
|
void *rfd, /* set for looped back routes */
|
||
|
const struct prefix *p,
|
||
|
const struct prefix *aux_prefix, /* AFI_L2VPN: optional IP */
|
||
|
afi_t afi, struct prefix_rd *prd,
|
||
|
struct attr *attr, /* part of bgp_path_info */
|
||
|
uint8_t type, /* part of bgp_path_info */
|
||
|
uint8_t sub_type, /* part of bgp_path_info */
|
||
|
uint32_t *label) /* part of bgp_path_info */
|
||
|
{
|
||
|
struct agg_table *rt = NULL;
|
||
|
struct agg_node *rn;
|
||
|
struct agg_node *n;
|
||
|
struct bgp_path_info *info_new;
|
||
|
struct bgp_path_info *bpi;
|
||
|
struct bgp_path_info *next;
|
||
|
char buf[BUFSIZ];
|
||
|
struct prefix vn_prefix;
|
||
|
struct prefix un_prefix;
|
||
|
int un_prefix_valid = 0;
|
||
|
struct agg_node *ern;
|
||
|
int replacing = 0;
|
||
|
int original_had_routes = 0;
|
||
|
struct prefix original_nexthop;
|
||
|
const char *action_str = NULL;
|
||
|
int is_it_ce = 0;
|
||
|
|
||
|
struct bgp *bgp;
|
||
|
bgp = bgp_get_default(); /* assume 1 instance for now */
|
||
|
|
||
|
switch (action) {
|
||
|
case FIF_ACTION_UPDATE:
|
||
|
action_str = "update";
|
||
|
break;
|
||
|
case FIF_ACTION_WITHDRAW:
|
||
|
action_str = "withdraw";
|
||
|
break;
|
||
|
case FIF_ACTION_KILL:
|
||
|
action_str = "kill";
|
||
|
break;
|
||
|
default:
|
||
|
assert(0);
|
||
|
break;
|
||
|
}
|
||
|
|
||
|
if (import_table == bgp->rfapi->it_ce)
|
||
|
is_it_ce = 1;
|
||
|
|
||
|
vnc_zlog_debug_verbose("%s: entry: %s%s: prefix %s/%d: it %p, afi %s",
|
||
|
__func__, (is_it_ce ? "CE-IT " : ""), action_str,
|
||
|
rfapi_ntop(p->family, &p->u.prefix, buf, BUFSIZ),
|
||
|
p->prefixlen, import_table, afi2str(afi));
|
||
|
|
||
|
VNC_ITRCCK;
|
||
|
|
||
|
/*
|
||
|
* Compare rt lists. If no intersection, don't import this route
|
||
|
* On a withdraw, peer and RD are sufficient to determine if
|
||
|
* we should act.
|
||
|
*/
|
||
|
if (action == FIF_ACTION_UPDATE) {
|
||
|
if (!attr || !bgp_attr_get_ecommunity(attr)) {
|
||
|
|
||
|
vnc_zlog_debug_verbose(
|
||
|
"%s: attr, extra, or ecommunity missing, not importing",
|
||
|
__func__);
|
||
|
return;
|
||
|
}
|
||
|
if ((import_table != bgp->rfapi->it_ce) &&
|
||
|
!rfapiEcommunitiesIntersect(
|
||
|
import_table->rt_import_list,
|
||
|
bgp_attr_get_ecommunity(attr))) {
|
||
|
|
||
|
vnc_zlog_debug_verbose(
|
||
|
"%s: it=%p: no ecommunity intersection",
|
||
|
__func__, import_table);
|
||
|
return;
|
||
|
}
|
||
|
|
||
|
memset(&vn_prefix, 0,
|
||
|
sizeof(vn_prefix)); /* keep valgrind happy */
|
||
|
if (rfapiGetNexthop(attr, &vn_prefix)) {
|
||
|
/* missing nexthop address would be a bad, bad thing */
|
||
|
vnc_zlog_debug_verbose("%s: missing nexthop", __func__);
|
||
|
return;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
/*
|
||
|
* Figure out which radix tree the route would go into
|
||
|
*/
|
||
|
switch (afi) {
|
||
|
case AFI_IP:
|
||
|
case AFI_IP6:
|
||
|
case AFI_L2VPN:
|
||
|
rt = import_table->imported_vpn[afi];
|
||
|
break;
|
||
|
|
||
|
case AFI_UNSPEC:
|
||
|
case AFI_MAX:
|
||
|
flog_err(EC_LIB_DEVELOPMENT, "%s: bad afi %d", __func__, afi);
|
||
|
return;
|
||
|
}
|
||
|
|
||
|
/* clear it */
|
||
|
memset(&original_nexthop, 0, sizeof(original_nexthop));
|
||
|
|
||
|
/*
|
||
|
* agg_node_lookup returns a node only if there is at least
|
||
|
* one route attached.
|
||
|
*/
|
||
|
rn = agg_node_lookup(rt, p);
|
||
|
|
||
|
vnc_zlog_debug_verbose("%s: rn=%p", __func__, rn);
|
||
|
|
||
|
if (rn) {
|
||
|
|
||
|
RFAPI_CHECK_REFCOUNT(rn, SAFI_MPLS_VPN, 1);
|
||
|
agg_unlock_node(rn); /* undo lock in agg_node_lookup */
|
||
|
|
||
|
if (rn->info)
|
||
|
original_had_routes = 1;
|
||
|
|
||
|
if (VNC_DEBUG(VERBOSE)) {
|
||
|
vnc_zlog_debug_verbose("%s: showing IT node on entry",
|
||
|
__func__);
|
||
|
rfapiShowItNode(NULL, rn); /* debug */
|
||
|
}
|
||
|
|
||
|
/*
|
||
|
* Look for same route (will have same RD and peer)
|
||
|
*/
|
||
|
bpi = rfapiItBiIndexSearch(rn, prd, peer, aux_prefix);
|
||
|
|
||
|
if (bpi) {
|
||
|
|
||
|
/*
|
||
|
* This was an old test when we iterated over the
|
||
|
* BPIs linearly. Since we're now looking up with
|
||
|
* RD and peer, comparing types should not be
|
||
|
* needed. Changed to assertion.
|
||
|
*
|
||
|
* Compare types. Doing so prevents a RFP-originated
|
||
|
* route from matching an imported route, for example.
|
||
|
*/
|
||
|
if (VNC_DEBUG(VERBOSE) && bpi->type != type)
|
||
|
/* should be handled by RDs, but warn for now */
|
||
|
zlog_warn("%s: type mismatch! (bpi=%d, arg=%d)",
|
||
|
__func__, bpi->type, type);
|
||
|
|
||
|
vnc_zlog_debug_verbose("%s: found matching bpi",
|
||
|
__func__);
|
||
|
|
||
|
/*
|
||
|
* In the special CE table, withdrawals occur without
|
||
|
* holddown
|
||
|
*/
|
||
|
if (import_table == bgp->rfapi->it_ce) {
|
||
|
vnc_direct_bgp_del_route_ce(bgp, rn, bpi);
|
||
|
if (action == FIF_ACTION_WITHDRAW)
|
||
|
action = FIF_ACTION_KILL;
|
||
|
}
|
||
|
|
||
|
if (action == FIF_ACTION_WITHDRAW) {
|
||
|
|
||
|
int washolddown = CHECK_FLAG(bpi->flags,
|
||
|
BGP_PATH_REMOVED);
|
||
|
|
||
|
vnc_zlog_debug_verbose(
|
||
|
"%s: withdrawing at prefix %pRN%s",
|
||
|
__func__, rn,
|
||
|
(washolddown
|
||
|
? " (already being withdrawn)"
|
||
|
: ""));
|
||
|
|
||
|
VNC_ITRCCK;
|
||
|
if (!washolddown) {
|
||
|
rfapiBiStartWithdrawTimer(
|
||
|
import_table, rn, bpi, afi,
|
||
|
SAFI_MPLS_VPN,
|
||
|
rfapiWithdrawTimerVPN);
|
||
|
|
||
|
RFAPI_UPDATE_ITABLE_COUNT(
|
||
|
bpi, import_table, afi, -1);
|
||
|
import_table->holddown_count[afi] += 1;
|
||
|
}
|
||
|
VNC_ITRCCK;
|
||
|
} else {
|
||
|
vnc_zlog_debug_verbose(
|
||
|
"%s: %s at prefix %pRN", __func__,
|
||
|
((action == FIF_ACTION_KILL)
|
||
|
? "killing"
|
||
|
: "replacing"),
|
||
|
rn);
|
||
|
|
||
|
/*
|
||
|
* If this route is waiting to be deleted
|
||
|
* because of
|
||
|
* a previous withdraw, we must cancel its
|
||
|
* timer.
|
||
|
*/
|
||
|
if (CHECK_FLAG(bpi->flags, BGP_PATH_REMOVED) &&
|
||
|
bpi->extra->vnc->vnc.import.timer) {
|
||
|
struct rfapi_withdraw *wcb = EVENT_ARG(
|
||
|
bpi->extra->vnc->vnc.import.timer);
|
||
|
|
||
|
XFREE(MTYPE_RFAPI_WITHDRAW, wcb);
|
||
|
EVENT_OFF(bpi->extra->vnc->vnc.import
|
||
|
.timer);
|
||
|
|
||
|
import_table->holddown_count[afi] -= 1;
|
||
|
RFAPI_UPDATE_ITABLE_COUNT(
|
||
|
bpi, import_table, afi, 1);
|
||
|
}
|
||
|
/*
|
||
|
* decrement remote count (if route is remote)
|
||
|
* because
|
||
|
* we are going to remove it below
|
||
|
*/
|
||
|
RFAPI_UPDATE_ITABLE_COUNT(bpi, import_table,
|
||
|
afi, -1);
|
||
|
if (action == FIF_ACTION_UPDATE) {
|
||
|
replacing = 1;
|
||
|
|
||
|
/*
|
||
|
* make copy of original nexthop so we
|
||
|
* can see if it changed
|
||
|
*/
|
||
|
rfapiGetNexthop(bpi->attr,
|
||
|
&original_nexthop);
|
||
|
|
||
|
/*
|
||
|
* remove bpi without doing any export
|
||
|
* processing
|
||
|
*/
|
||
|
if (CHECK_FLAG(bpi->flags,
|
||
|
BGP_PATH_VALID)
|
||
|
&& VALID_INTERIOR_TYPE(bpi->type))
|
||
|
RFAPI_MONITOR_EXTERIOR(rn)
|
||
|
->valid_interior_count--;
|
||
|
rfapiItBiIndexDel(rn, bpi);
|
||
|
rfapiBgpInfoDetach(rn, bpi);
|
||
|
rfapiMonitorEncapDelete(bpi);
|
||
|
vnc_import_bgp_exterior_del_route_interior(
|
||
|
bgp, import_table, rn, bpi);
|
||
|
rfapiBgpInfoFree(bpi);
|
||
|
} else {
|
||
|
/* Kill */
|
||
|
/*
|
||
|
* remove bpi and do export processing
|
||
|
*/
|
||
|
import_table->holddown_count[afi] += 1;
|
||
|
rfapiExpireVpnNow(import_table, rn, bpi,
|
||
|
0);
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
|
||
|
if (rn)
|
||
|
RFAPI_CHECK_REFCOUNT(rn, SAFI_MPLS_VPN, replacing ? 1 : 0);
|
||
|
|
||
|
if (action == FIF_ACTION_WITHDRAW || action == FIF_ACTION_KILL) {
|
||
|
VNC_ITRCCK;
|
||
|
return;
|
||
|
}
|
||
|
|
||
|
info_new =
|
||
|
rfapiBgpInfoCreate(attr, peer, rfd, prd, type, sub_type, label);
|
||
|
|
||
|
/*
|
||
|
* lookup un address in encap table
|
||
|
*/
|
||
|
ern = agg_node_match(import_table->imported_encap[afi], &vn_prefix);
|
||
|
if (ern) {
|
||
|
rfapiCopyUnEncap2VPN(ern->info, info_new);
|
||
|
agg_unlock_node(ern); /* undo lock in route_note_match */
|
||
|
} else {
|
||
|
/* Not a big deal, just means VPN route got here first */
|
||
|
vnc_zlog_debug_verbose("%s: no encap route for vn addr %pFX",
|
||
|
__func__, &vn_prefix);
|
||
|
info_new->extra->vnc->vnc.import.un_family = AF_UNSPEC;
|
||
|
}
|
||
|
|
||
|
if (rn) {
|
||
|
if (!replacing)
|
||
|
agg_lock_node(rn);
|
||
|
} else {
|
||
|
/*
|
||
|
* No need to increment reference count, so only "get"
|
||
|
* if the node is not there already
|
||
|
*/
|
||
|
rn = agg_node_get(rt, p);
|
||
|
}
|
||
|
|
||
|
/*
|
||
|
* For ethernet routes, if there is an accompanying IP address,
|
||
|
* save it in the bpi
|
||
|
*/
|
||
|
if ((AFI_L2VPN == afi) && aux_prefix) {
|
||
|
|
||
|
vnc_zlog_debug_verbose("%s: setting BPI's aux_prefix",
|
||
|
__func__);
|
||
|
info_new->extra->vnc->vnc.import.aux_prefix = *aux_prefix;
|
||
|
}
|
||
|
|
||
|
vnc_zlog_debug_verbose("%s: inserting bpi %p at prefix %pRN #%d",
|
||
|
__func__, info_new, rn,
|
||
|
agg_node_get_lock_count(rn));
|
||
|
|
||
|
rfapiBgpInfoAttachSorted(rn, info_new, afi, SAFI_MPLS_VPN);
|
||
|
rfapiItBiIndexAdd(rn, info_new);
|
||
|
if (!rfapiGetUnAddrOfVpnBi(info_new, NULL)) {
|
||
|
if (VALID_INTERIOR_TYPE(info_new->type))
|
||
|
RFAPI_MONITOR_EXTERIOR(rn)->valid_interior_count++;
|
||
|
SET_FLAG(info_new->flags, BGP_PATH_VALID);
|
||
|
}
|
||
|
RFAPI_UPDATE_ITABLE_COUNT(info_new, import_table, afi, 1);
|
||
|
vnc_import_bgp_exterior_add_route_interior(bgp, import_table, rn,
|
||
|
info_new);
|
||
|
|
||
|
if (import_table == bgp->rfapi->it_ce)
|
||
|
vnc_direct_bgp_add_route_ce(bgp, rn, info_new);
|
||
|
|
||
|
if (VNC_DEBUG(VERBOSE)) {
|
||
|
vnc_zlog_debug_verbose("%s: showing IT node", __func__);
|
||
|
rfapiShowItNode(NULL, rn); /* debug */
|
||
|
}
|
||
|
|
||
|
rfapiMonitorEncapAdd(import_table, &vn_prefix, rn, info_new);
|
||
|
|
||
|
if (!rfapiGetUnAddrOfVpnBi(info_new, &un_prefix)) {
|
||
|
|
||
|
/*
|
||
|
* if we have a valid UN address (either via Encap route
|
||
|
* or via tunnel attribute), then we should attempt
|
||
|
* to move any monitors at less-specific nodes to this node
|
||
|
*/
|
||
|
rfapiMonitorMoveLonger(rn);
|
||
|
|
||
|
un_prefix_valid = 1;
|
||
|
}
|
||
|
|
||
|
/*
|
||
|
* 101129 Enhancement: if we add a route (implication: it is not
|
||
|
* in holddown), delete all other routes from this nve at this
|
||
|
* node that are in holddown, regardless of peer.
|
||
|
*
|
||
|
* Reasons it's OK to do that:
|
||
|
*
|
||
|
* - if the holddown route being deleted originally came from BGP VPN,
|
||
|
* it is already gone from BGP (implication of holddown), so there
|
||
|
* won't be any added inconsistency with the BGP RIB.
|
||
|
*
|
||
|
* - once a fresh route is added at a prefix, any routes in holddown
|
||
|
* at that prefix will not show up in RFP responses, so deleting
|
||
|
* the holddown routes won't affect the contents of responses.
|
||
|
*
|
||
|
* - lifetimes are supposed to be consistent, so there should not
|
||
|
* be a case where the fresh route has a shorter lifetime than
|
||
|
* the holddown route, so we don't expect the fresh route to
|
||
|
* disappear and complete its holddown time before the existing
|
||
|
* holddown routes time out. Therefore, we won't have a situation
|
||
|
* where we expect the existing holddown routes to be hidden and
|
||
|
* then to reappear sometime later (as holddown routes) in a
|
||
|
* RFP response.
|
||
|
*
|
||
|
* Among other things, this would enable us to skirt the problem
|
||
|
* of local holddown routes that refer to NVE descriptors that
|
||
|
* have already been closed (if the same NVE triggers a subsequent
|
||
|
* rfapi_open(), the new peer is different and doesn't match the
|
||
|
* peer of the holddown route, so the stale holddown route still
|
||
|
* hangs around until it times out instead of just being replaced
|
||
|
* by the fresh route).
|
||
|
*/
|
||
|
/*
|
||
|
* We know that the new bpi will have been inserted before any routes
|
||
|
* in holddown, so we can skip any that came before it
|
||
|
*/
|
||
|
for (bpi = info_new->next; bpi; bpi = next) {
|
||
|
|
||
|
struct prefix pfx_vn;
|
||
|
struct prefix pfx_un;
|
||
|
int un_match = 0;
|
||
|
int remote_peer_match = 0;
|
||
|
|
||
|
next = bpi->next;
|
||
|
|
||
|
/*
|
||
|
* Must be holddown
|
||
|
*/
|
||
|
if (!CHECK_FLAG(bpi->flags, BGP_PATH_REMOVED))
|
||
|
continue;
|
||
|
|
||
|
/*
|
||
|
* Must match VN address (nexthop of VPN route)
|
||
|
*/
|
||
|
if (rfapiGetNexthop(bpi->attr, &pfx_vn))
|
||
|
continue;
|
||
|
if (!prefix_same(&pfx_vn, &vn_prefix))
|
||
|
continue;
|
||
|
|
||
|
if (un_prefix_valid && /* new route UN addr */
|
||
|
!rfapiGetUnAddrOfVpnBi(bpi, &pfx_un)
|
||
|
&& /* old route UN addr */
|
||
|
prefix_same(&pfx_un, &un_prefix)) { /* compare */
|
||
|
un_match = 1;
|
||
|
}
|
||
|
if (!RFAPI_LOCAL_BI(bpi) && !RFAPI_LOCAL_BI(info_new) &&
|
||
|
sockunion_same(&bpi->peer->connection->su,
|
||
|
&info_new->peer->connection->su)) {
|
||
|
/* old & new are both remote, same peer */
|
||
|
remote_peer_match = 1;
|
||
|
}
|
||
|
|
||
|
if (!un_match && !remote_peer_match)
|
||
|
continue;
|
||
|
|
||
|
vnc_zlog_debug_verbose(
|
||
|
"%s: removing holddown bpi matching NVE of new route",
|
||
|
__func__);
|
||
|
if (bpi->extra->vnc->vnc.import.timer) {
|
||
|
struct rfapi_withdraw *wcb =
|
||
|
EVENT_ARG(bpi->extra->vnc->vnc.import.timer);
|
||
|
|
||
|
XFREE(MTYPE_RFAPI_WITHDRAW, wcb);
|
||
|
EVENT_OFF(bpi->extra->vnc->vnc.import.timer);
|
||
|
}
|
||
|
rfapiExpireVpnNow(import_table, rn, bpi, 0);
|
||
|
}
|
||
|
|
||
|
if (!original_had_routes) {
|
||
|
/*
|
||
|
* We went from 0 usable routes to 1 usable route. Perform the
|
||
|
* "Adding a Route" export process.
|
||
|
*/
|
||
|
vnc_direct_bgp_add_prefix(bgp, import_table, rn);
|
||
|
vnc_zebra_add_prefix(bgp, import_table, rn);
|
||
|
} else {
|
||
|
/*
|
||
|
* Check for nexthop change event
|
||
|
* Note: the prefix_same() test below detects two situations:
|
||
|
* 1. route is replaced, new route has different nexthop
|
||
|
* 2. new route is added (original_nexthop is 0)
|
||
|
*/
|
||
|
struct prefix new_nexthop;
|
||
|
|
||
|
rfapiGetNexthop(attr, &new_nexthop);
|
||
|
if (!prefix_same(&original_nexthop, &new_nexthop)) {
|
||
|
/*
|
||
|
* nexthop change event
|
||
|
* vnc_direct_bgp_add_prefix() will recompute VN addr
|
||
|
* ecommunity
|
||
|
*/
|
||
|
vnc_direct_bgp_add_prefix(bgp, import_table, rn);
|
||
|
}
|
||
|
}
|
||
|
|
||
|
if (!(bgp->rfapi_cfg->flags & BGP_VNC_CONFIG_CALLBACK_DISABLE)) {
|
||
|
for (n = rn; n; n = agg_node_parent(n)) {
|
||
|
// rfapiDoRouteCallback(import_table, n, NULL);
|
||
|
}
|
||
|
rfapiMonitorItNodeChanged(import_table, rn, NULL);
|
||
|
}
|
||
|
RFAPI_CHECK_REFCOUNT(rn, SAFI_MPLS_VPN, 0);
|
||
|
VNC_ITRCCK;
|
||
|
}
|
||
|
|
||
|
static void rfapiBgpInfoFilteredImportBadSafi(
|
||
|
struct rfapi_import_table *import_table, int action, struct peer *peer,
|
||
|
void *rfd, /* set for looped back routes */
|
||
|
const struct prefix *p,
|
||
|
const struct prefix *aux_prefix, /* AFI_L2VPN: optional IP */
|
||
|
afi_t afi, struct prefix_rd *prd,
|
||
|
struct attr *attr, /* part of bgp_path_info */
|
||
|
uint8_t type, /* part of bgp_path_info */
|
||
|
uint8_t sub_type, /* part of bgp_path_info */
|
||
|
uint32_t *label) /* part of bgp_path_info */
|
||
|
{
|
||
|
vnc_zlog_debug_verbose("%s: Error, bad safi", __func__);
|
||
|
}
|
||
|
|
||
|
static rfapi_bi_filtered_import_f *
|
||
|
rfapiBgpInfoFilteredImportFunction(safi_t safi)
|
||
|
{
|
||
|
switch (safi) {
|
||
|
case SAFI_MPLS_VPN:
|
||
|
return rfapiBgpInfoFilteredImportVPN;
|
||
|
|
||
|
case SAFI_ENCAP:
|
||
|
return rfapiBgpInfoFilteredImportEncap;
|
||
|
|
||
|
case SAFI_UNSPEC:
|
||
|
case SAFI_UNICAST:
|
||
|
case SAFI_MULTICAST:
|
||
|
case SAFI_EVPN:
|
||
|
case SAFI_LABELED_UNICAST:
|
||
|
case SAFI_FLOWSPEC:
|
||
|
case SAFI_MAX:
|
||
|
/* not expected */
|
||
|
flog_err(EC_LIB_DEVELOPMENT, "%s: bad safi %d", __func__, safi);
|
||
|
return rfapiBgpInfoFilteredImportBadSafi;
|
||
|
}
|
||
|
|
||
|
assert(!"Reached end of function when we were not expecting to");
|
||
|
}
|
||
|
|
||
|
void rfapiProcessUpdate(struct peer *peer,
|
||
|
void *rfd, /* set when looped from RFP/RFAPI */
|
||
|
const struct prefix *p, struct prefix_rd *prd,
|
||
|
struct attr *attr, afi_t afi, safi_t safi, uint8_t type,
|
||
|
uint8_t sub_type, uint32_t *label)
|
||
|
{
|
||
|
struct bgp *bgp;
|
||
|
struct rfapi *h;
|
||
|
struct rfapi_import_table *it;
|
||
|
int has_ip_route = 1;
|
||
|
uint32_t lni = 0;
|
||
|
|
||
|
bgp = bgp_get_default(); /* assume 1 instance for now */
|
||
|
assert(bgp);
|
||
|
|
||
|
h = bgp->rfapi;
|
||
|
assert(h);
|
||
|
|
||
|
/*
|
||
|
* look at high-order byte of RD. FF means MAC
|
||
|
* address is present (VNC L2VPN)
|
||
|
*/
|
||
|
if ((safi == SAFI_MPLS_VPN)
|
||
|
&& (decode_rd_type(prd->val) == RD_TYPE_VNC_ETH)) {
|
||
|
struct prefix pfx_mac_buf;
|
||
|
struct prefix pfx_nexthop_buf;
|
||
|
int rc;
|
||
|
|
||
|
/*
|
||
|
* Set flag if prefix and nexthop are the same - don't
|
||
|
* add the route to normal IP-based import tables
|
||
|
*/
|
||
|
if (!rfapiGetNexthop(attr, &pfx_nexthop_buf)) {
|
||
|
if (!prefix_cmp(&pfx_nexthop_buf, p)) {
|
||
|
has_ip_route = 0;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
memset(&pfx_mac_buf, 0, sizeof(pfx_mac_buf));
|
||
|
pfx_mac_buf.family = AF_ETHERNET;
|
||
|
pfx_mac_buf.prefixlen = 48;
|
||
|
memcpy(&pfx_mac_buf.u.prefix_eth.octet, prd->val + 2, 6);
|
||
|
|
||
|
/*
|
||
|
* Find rt containing LNI (Logical Network ID), which
|
||
|
* _should_ always be present when mac address is present
|
||
|
*/
|
||
|
rc = rfapiEcommunityGetLNI(bgp_attr_get_ecommunity(attr), &lni);
|
||
|
|
||
|
vnc_zlog_debug_verbose(
|
||
|
"%s: rfapiEcommunityGetLNI returned %d, lni=%d, attr=%p",
|
||
|
__func__, rc, lni, attr);
|
||
|
if (!rc) {
|
||
|
it = rfapiMacImportTableGet(bgp, lni);
|
||
|
|
||
|
rfapiBgpInfoFilteredImportVPN(
|
||
|
it, FIF_ACTION_UPDATE, peer, rfd,
|
||
|
&pfx_mac_buf, /* prefix */
|
||
|
p, /* aux prefix: IP addr */
|
||
|
AFI_L2VPN, prd, attr, type, sub_type, label);
|
||
|
}
|
||
|
}
|
||
|
|
||
|
if (!has_ip_route)
|
||
|
return;
|
||
|
|
||
|
/*
|
||
|
* Iterate over all import tables; do a filtered import
|
||
|
* for the afi/safi combination
|
||
|
*/
|
||
|
for (it = h->imports; it; it = it->next) {
|
||
|
(*rfapiBgpInfoFilteredImportFunction(safi))(
|
||
|
it, FIF_ACTION_UPDATE, peer, rfd, p, /* prefix */
|
||
|
NULL, afi, prd, attr, type, sub_type, label);
|
||
|
}
|
||
|
|
||
|
if (safi == SAFI_MPLS_VPN) {
|
||
|
vnc_direct_bgp_rh_add_route(bgp, afi, p, peer, attr);
|
||
|
rfapiBgpInfoFilteredImportVPN(
|
||
|
bgp->rfapi->it_ce, FIF_ACTION_UPDATE, peer, rfd,
|
||
|
p, /* prefix */
|
||
|
NULL, afi, prd, attr, type, sub_type, label);
|
||
|
}
|
||
|
}
|
||
|
|
||
|
|
||
|
void rfapiProcessWithdraw(struct peer *peer, void *rfd, const struct prefix *p,
|
||
|
struct prefix_rd *prd, struct attr *attr, afi_t afi,
|
||
|
safi_t safi, uint8_t type, int kill)
|
||
|
{
|
||
|
struct bgp *bgp;
|
||
|
struct rfapi *h;
|
||
|
struct rfapi_import_table *it;
|
||
|
|
||
|
bgp = bgp_get_default(); /* assume 1 instance for now */
|
||
|
assert(bgp);
|
||
|
|
||
|
h = bgp->rfapi;
|
||
|
assert(h);
|
||
|
|
||
|
/*
|
||
|
* look at high-order byte of RD. FF means MAC
|
||
|
* address is present (VNC L2VPN)
|
||
|
*/
|
||
|
if (h->import_mac != NULL && safi == SAFI_MPLS_VPN
|
||
|
&& decode_rd_type(prd->val) == RD_TYPE_VNC_ETH) {
|
||
|
struct prefix pfx_mac_buf;
|
||
|
void *cursor = NULL;
|
||
|
int rc;
|
||
|
|
||
|
memset(&pfx_mac_buf, 0, sizeof(pfx_mac_buf));
|
||
|
pfx_mac_buf.family = AF_ETHERNET;
|
||
|
pfx_mac_buf.prefixlen = 48;
|
||
|
memcpy(&pfx_mac_buf.u.prefix_eth, prd->val + 2, 6);
|
||
|
|
||
|
/*
|
||
|
* withdraw does not contain attrs, so we don't have
|
||
|
* access to the route's LNI, which would ordinarily
|
||
|
* select the specific mac-based import table. Instead,
|
||
|
* we must iterate over all mac-based tables and rely
|
||
|
* on the RD to match.
|
||
|
*
|
||
|
* If this approach is too slow, add an index where
|
||
|
* key is {RD, peer} and value is the import table
|
||
|
*/
|
||
|
for (rc = skiplist_next(h->import_mac, NULL, (void **)&it,
|
||
|
&cursor);
|
||
|
rc == 0; rc = skiplist_next(h->import_mac, NULL,
|
||
|
(void **)&it, &cursor)) {
|
||
|
|
||
|
#ifdef DEBUG_L2_EXTRA
|
||
|
vnc_zlog_debug_verbose(
|
||
|
"%s: calling rfapiBgpInfoFilteredImportVPN(it=%p, afi=AFI_L2VPN)",
|
||
|
__func__, it);
|
||
|
#endif
|
||
|
|
||
|
rfapiBgpInfoFilteredImportVPN(
|
||
|
it,
|
||
|
(kill ? FIF_ACTION_KILL : FIF_ACTION_WITHDRAW),
|
||
|
peer, rfd, &pfx_mac_buf, /* prefix */
|
||
|
p, /* aux_prefix: IP */
|
||
|
AFI_L2VPN, prd, attr, type, 0,
|
||
|
NULL); /* sub_type & label unused for withdraw
|
||
|
*/
|
||
|
}
|
||
|
}
|
||
|
|
||
|
/*
|
||
|
* XXX For the case where the withdraw involves an L2
|
||
|
* route with no IP information, we rely on the lack
|
||
|
* of RT-list intersection to filter out the withdraw
|
||
|
* from the IP-based import tables below
|
||
|
*/
|
||
|
|
||
|
/*
|
||
|
* Iterate over all import tables; do a filtered import
|
||
|
* for the afi/safi combination
|
||
|
*/
|
||
|
|
||
|
for (it = h->imports; it; it = it->next) {
|
||
|
(*rfapiBgpInfoFilteredImportFunction(safi))(
|
||
|
it, (kill ? FIF_ACTION_KILL : FIF_ACTION_WITHDRAW),
|
||
|
peer, rfd, p, /* prefix */
|
||
|
NULL, afi, prd, attr, type, 0,
|
||
|
NULL); /* sub_type & label unused for withdraw */
|
||
|
}
|
||
|
|
||
|
/* TBD the deletion should happen after the lifetime expires */
|
||
|
if (safi == SAFI_MPLS_VPN)
|
||
|
vnc_direct_bgp_rh_del_route(bgp, afi, p, peer);
|
||
|
|
||
|
if (safi == SAFI_MPLS_VPN) {
|
||
|
rfapiBgpInfoFilteredImportVPN(
|
||
|
bgp->rfapi->it_ce,
|
||
|
(kill ? FIF_ACTION_KILL : FIF_ACTION_WITHDRAW), peer,
|
||
|
rfd, p, /* prefix */
|
||
|
NULL, afi, prd, attr, type, 0,
|
||
|
NULL); /* sub_type & label unused for withdraw */
|
||
|
}
|
||
|
}
|
||
|
|
||
|
/*
|
||
|
* TBD optimized withdraw timer algorithm for case of many
|
||
|
* routes expiring at the same time due to peer drop.
|
||
|
*/
|
||
|
/*
|
||
|
* 1. Visit all BPIs in all ENCAP import tables.
|
||
|
*
|
||
|
* a. If a bpi's peer is the failed peer, remove the bpi.
|
||
|
* b. If the removed ENCAP bpi was first in the list of
|
||
|
* BPIs at this ENCAP node, loop over all monitors
|
||
|
* at this node:
|
||
|
*
|
||
|
* (1) for each ENCAP monitor, loop over all its
|
||
|
* VPN node monitors and set their RFAPI_MON_FLAG_NEEDCALLBACK
|
||
|
* flags.
|
||
|
*
|
||
|
* 2. Visit all BPIs in all VPN import tables.
|
||
|
* a. If a bpi's peer is the failed peer, remove the bpi.
|
||
|
* b. loop over all the VPN node monitors and set their
|
||
|
* RFAPI_MON_FLAG_NEEDCALLBACK flags
|
||
|
* c. If there are no BPIs left at this VPN node,
|
||
|
*
|
||
|
*/
|
||
|
|
||
|
|
||
|
/* surprise, this gets called from peer_delete(), from rfapi_close() */
|
||
|
static void rfapiProcessPeerDownRt(struct peer *peer,
|
||
|
struct rfapi_import_table *import_table,
|
||
|
afi_t afi, safi_t safi)
|
||
|
{
|
||
|
struct agg_node *rn;
|
||
|
struct bgp_path_info *bpi;
|
||
|
struct agg_table *rt = NULL;
|
||
|
void (*timer_service_func)(struct event *) = NULL;
|
||
|
|
||
|
assert(afi == AFI_IP || afi == AFI_IP6);
|
||
|
|
||
|
VNC_ITRCCK;
|
||
|
|
||
|
switch (safi) {
|
||
|
case SAFI_MPLS_VPN:
|
||
|
rt = import_table->imported_vpn[afi];
|
||
|
timer_service_func = rfapiWithdrawTimerVPN;
|
||
|
break;
|
||
|
case SAFI_ENCAP:
|
||
|
rt = import_table->imported_encap[afi];
|
||
|
timer_service_func = rfapiWithdrawTimerEncap;
|
||
|
break;
|
||
|
case SAFI_UNSPEC:
|
||
|
case SAFI_UNICAST:
|
||
|
case SAFI_MULTICAST:
|
||
|
case SAFI_EVPN:
|
||
|
case SAFI_LABELED_UNICAST:
|
||
|
case SAFI_FLOWSPEC:
|
||
|
case SAFI_MAX:
|
||
|
/* Suppress uninitialized variable warning */
|
||
|
rt = NULL;
|
||
|
timer_service_func = NULL;
|
||
|
assert(0);
|
||
|
}
|
||
|
|
||
|
for (rn = agg_route_top(rt); rn; rn = agg_route_next(rn)) {
|
||
|
for (bpi = rn->info; bpi; bpi = bpi->next) {
|
||
|
if (bpi->peer == peer) {
|
||
|
|
||
|
if (CHECK_FLAG(bpi->flags, BGP_PATH_REMOVED)) {
|
||
|
/* already in holddown, skip */
|
||
|
continue;
|
||
|
}
|
||
|
|
||
|
if (safi == SAFI_MPLS_VPN) {
|
||
|
RFAPI_UPDATE_ITABLE_COUNT(
|
||
|
bpi, import_table, afi, -1);
|
||
|
import_table->holddown_count[afi] += 1;
|
||
|
}
|
||
|
rfapiBiStartWithdrawTimer(import_table, rn, bpi,
|
||
|
afi, safi,
|
||
|
timer_service_func);
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
VNC_ITRCCK;
|
||
|
}
|
||
|
|
||
|
/*
|
||
|
* This gets called when a peer connection drops. We have to remove
|
||
|
* all the routes from this peer.
|
||
|
*
|
||
|
* Current approach is crude. TBD Optimize by setting fewer timers and
|
||
|
* grouping withdrawn routes so we can generate callbacks more
|
||
|
* efficiently.
|
||
|
*/
|
||
|
void rfapiProcessPeerDown(struct peer *peer)
|
||
|
{
|
||
|
struct bgp *bgp;
|
||
|
struct rfapi *h;
|
||
|
struct rfapi_import_table *it;
|
||
|
|
||
|
/*
|
||
|
* If this peer is a "dummy" peer structure atached to a RFAPI
|
||
|
* nve_descriptor, we don't need to walk the import tables
|
||
|
* because the routes are already withdrawn by rfapi_close()
|
||
|
*/
|
||
|
if (CHECK_FLAG(peer->flags, PEER_FLAG_IS_RFAPI_HD))
|
||
|
return;
|
||
|
|
||
|
/*
|
||
|
* 1. Visit all BPIs in all ENCAP import tables.
|
||
|
* Start withdraw timer on the BPIs that match peer.
|
||
|
*
|
||
|
* 2. Visit All BPIs in all VPN import tables.
|
||
|
* Start withdraw timer on the BPIs that match peer.
|
||
|
*/
|
||
|
|
||
|
bgp = bgp_get_default(); /* assume 1 instance for now */
|
||
|
if (!bgp)
|
||
|
return;
|
||
|
|
||
|
h = bgp->rfapi;
|
||
|
assert(h);
|
||
|
|
||
|
for (it = h->imports; it; it = it->next) {
|
||
|
rfapiProcessPeerDownRt(peer, it, AFI_IP, SAFI_ENCAP);
|
||
|
rfapiProcessPeerDownRt(peer, it, AFI_IP6, SAFI_ENCAP);
|
||
|
rfapiProcessPeerDownRt(peer, it, AFI_IP, SAFI_MPLS_VPN);
|
||
|
rfapiProcessPeerDownRt(peer, it, AFI_IP6, SAFI_MPLS_VPN);
|
||
|
}
|
||
|
|
||
|
if (h->it_ce) {
|
||
|
rfapiProcessPeerDownRt(peer, h->it_ce, AFI_IP, SAFI_MPLS_VPN);
|
||
|
rfapiProcessPeerDownRt(peer, h->it_ce, AFI_IP6, SAFI_MPLS_VPN);
|
||
|
}
|
||
|
}
|
||
|
|
||
|
/*
|
||
|
* Import an entire RIB (for an afi/safi) to an import table RIB,
|
||
|
* filtered according to the import table's RT list
|
||
|
*
|
||
|
* TBD: does this function need additions to match rfapiProcessUpdate()
|
||
|
* for, e.g., L2 handling?
|
||
|
*/
|
||
|
static void rfapiBgpTableFilteredImport(struct bgp *bgp,
|
||
|
struct rfapi_import_table *it,
|
||
|
afi_t afi, safi_t safi)
|
||
|
{
|
||
|
struct bgp_dest *dest1;
|
||
|
struct bgp_dest *dest2;
|
||
|
|
||
|
/* Only these SAFIs have 2-level RIBS */
|
||
|
assert(safi == SAFI_MPLS_VPN || safi == SAFI_ENCAP);
|
||
|
|
||
|
/*
|
||
|
* Now visit all the rd nodes and the nodes of all the
|
||
|
* route tables attached to them, and import the routes
|
||
|
* if they have matching route targets
|
||
|
*/
|
||
|
for (dest1 = bgp_table_top(bgp->rib[afi][safi]); dest1;
|
||
|
dest1 = bgp_route_next(dest1)) {
|
||
|
|
||
|
if (bgp_dest_has_bgp_path_info_data(dest1)) {
|
||
|
|
||
|
for (dest2 = bgp_table_top(
|
||
|
bgp_dest_get_bgp_table_info(dest1));
|
||
|
dest2; dest2 = bgp_route_next(dest2)) {
|
||
|
|
||
|
struct bgp_path_info *bpi;
|
||
|
|
||
|
for (bpi = bgp_dest_get_bgp_path_info(dest2);
|
||
|
bpi; bpi = bpi->next) {
|
||
|
uint32_t label = MPLS_INVALID_LABEL;
|
||
|
|
||
|
if (CHECK_FLAG(bpi->flags,
|
||
|
BGP_PATH_REMOVED))
|
||
|
continue;
|
||
|
|
||
|
if (BGP_PATH_INFO_NUM_LABELS(bpi))
|
||
|
label = decode_label(
|
||
|
&bpi->extra->labels
|
||
|
->label[0]);
|
||
|
(*rfapiBgpInfoFilteredImportFunction(
|
||
|
safi))(
|
||
|
it, /* which import table */
|
||
|
FIF_ACTION_UPDATE, bpi->peer,
|
||
|
NULL,
|
||
|
bgp_dest_get_prefix(dest2),
|
||
|
NULL, afi,
|
||
|
(struct prefix_rd *)
|
||
|
bgp_dest_get_prefix(
|
||
|
dest1),
|
||
|
bpi->attr, bpi->type,
|
||
|
bpi->sub_type, &label);
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
|
||
|
|
||
|
/* per-bgp-instance rfapi data */
|
||
|
struct rfapi *bgp_rfapi_new(struct bgp *bgp)
|
||
|
{
|
||
|
struct rfapi *h;
|
||
|
afi_t afi;
|
||
|
struct rfapi_rfp_cfg *cfg = NULL;
|
||
|
struct rfapi_rfp_cb_methods *cbm = NULL;
|
||
|
|
||
|
assert(bgp->rfapi_cfg == NULL);
|
||
|
|
||
|
h = XCALLOC(MTYPE_RFAPI, sizeof(struct rfapi));
|
||
|
|
||
|
for (afi = AFI_IP; afi < AFI_MAX; afi++) {
|
||
|
h->un[afi] = agg_table_init();
|
||
|
}
|
||
|
|
||
|
/*
|
||
|
* initialize the ce import table
|
||
|
*/
|
||
|
h->it_ce = XCALLOC(MTYPE_RFAPI_IMPORTTABLE,
|
||
|
sizeof(struct rfapi_import_table));
|
||
|
h->it_ce->imported_vpn[AFI_IP] = agg_table_init();
|
||
|
h->it_ce->imported_vpn[AFI_IP6] = agg_table_init();
|
||
|
h->it_ce->imported_encap[AFI_IP] = agg_table_init();
|
||
|
h->it_ce->imported_encap[AFI_IP6] = agg_table_init();
|
||
|
rfapiBgpTableFilteredImport(bgp, h->it_ce, AFI_IP, SAFI_MPLS_VPN);
|
||
|
rfapiBgpTableFilteredImport(bgp, h->it_ce, AFI_IP6, SAFI_MPLS_VPN);
|
||
|
|
||
|
/*
|
||
|
* Set up work queue for deferred rfapi_close operations
|
||
|
*/
|
||
|
h->deferred_close_q =
|
||
|
work_queue_new(bm->master, "rfapi deferred close");
|
||
|
h->deferred_close_q->spec.workfunc = rfapi_deferred_close_workfunc;
|
||
|
h->deferred_close_q->spec.data = h;
|
||
|
|
||
|
h->rfp = rfp_start(bm->master, &cfg, &cbm);
|
||
|
bgp->rfapi_cfg = bgp_rfapi_cfg_new(cfg);
|
||
|
if (cbm != NULL) {
|
||
|
h->rfp_methods = *cbm;
|
||
|
}
|
||
|
return h;
|
||
|
}
|
||
|
|
||
|
void bgp_rfapi_destroy(struct bgp *bgp, struct rfapi *h)
|
||
|
{
|
||
|
afi_t afi;
|
||
|
|
||
|
if (bgp == NULL || h == NULL)
|
||
|
return;
|
||
|
|
||
|
if (h->resolve_nve_nexthop) {
|
||
|
skiplist_free(h->resolve_nve_nexthop);
|
||
|
h->resolve_nve_nexthop = NULL;
|
||
|
}
|
||
|
|
||
|
rfapiImportTableFlush(h->it_ce);
|
||
|
|
||
|
if (h->import_mac) {
|
||
|
struct rfapi_import_table *it;
|
||
|
void *cursor;
|
||
|
int rc;
|
||
|
|
||
|
for (cursor = NULL,
|
||
|
rc = skiplist_next(h->import_mac, NULL, (void **)&it,
|
||
|
&cursor);
|
||
|
!rc; rc = skiplist_next(h->import_mac, NULL, (void **)&it,
|
||
|
&cursor)) {
|
||
|
|
||
|
rfapiImportTableFlush(it);
|
||
|
XFREE(MTYPE_RFAPI_IMPORTTABLE, it);
|
||
|
}
|
||
|
skiplist_free(h->import_mac);
|
||
|
h->import_mac = NULL;
|
||
|
}
|
||
|
|
||
|
work_queue_free_and_null(&h->deferred_close_q);
|
||
|
|
||
|
if (h->rfp != NULL)
|
||
|
rfp_stop(h->rfp);
|
||
|
|
||
|
for (afi = AFI_IP; afi < AFI_MAX; afi++) {
|
||
|
agg_table_finish(h->un[afi]);
|
||
|
}
|
||
|
|
||
|
XFREE(MTYPE_RFAPI_IMPORTTABLE, h->it_ce);
|
||
|
XFREE(MTYPE_RFAPI, h);
|
||
|
}
|
||
|
|
||
|
struct rfapi_import_table *
|
||
|
rfapiImportTableRefAdd(struct bgp *bgp, struct ecommunity *rt_import_list,
|
||
|
struct rfapi_nve_group_cfg *rfg)
|
||
|
{
|
||
|
struct rfapi *h;
|
||
|
struct rfapi_import_table *it;
|
||
|
afi_t afi;
|
||
|
|
||
|
h = bgp->rfapi;
|
||
|
assert(h);
|
||
|
|
||
|
for (it = h->imports; it; it = it->next) {
|
||
|
if (ecommunity_cmp(it->rt_import_list, rt_import_list))
|
||
|
break;
|
||
|
}
|
||
|
|
||
|
vnc_zlog_debug_verbose("%s: matched it=%p", __func__, it);
|
||
|
|
||
|
if (!it) {
|
||
|
it = XCALLOC(MTYPE_RFAPI_IMPORTTABLE,
|
||
|
sizeof(struct rfapi_import_table));
|
||
|
it->next = h->imports;
|
||
|
h->imports = it;
|
||
|
|
||
|
it->rt_import_list = ecommunity_dup(rt_import_list);
|
||
|
it->rfg = rfg;
|
||
|
it->monitor_exterior_orphans =
|
||
|
skiplist_new(0, NULL, prefix_free_lists);
|
||
|
|
||
|
/*
|
||
|
* fill import route tables from RIBs
|
||
|
*
|
||
|
* Potential area for optimization. If this occurs when
|
||
|
* tables are large (e.g., the operator adds a nve group
|
||
|
* with a new RT list to a running system), it could take
|
||
|
* a while.
|
||
|
*
|
||
|
*/
|
||
|
for (afi = AFI_IP; afi < AFI_MAX; ++afi) {
|
||
|
|
||
|
it->imported_vpn[afi] = agg_table_init();
|
||
|
it->imported_encap[afi] = agg_table_init();
|
||
|
|
||
|
rfapiBgpTableFilteredImport(bgp, it, afi,
|
||
|
SAFI_MPLS_VPN);
|
||
|
rfapiBgpTableFilteredImport(bgp, it, afi, SAFI_ENCAP);
|
||
|
|
||
|
vnc_import_bgp_exterior_redist_enable_it(bgp, afi, it);
|
||
|
}
|
||
|
}
|
||
|
|
||
|
it->refcount += 1;
|
||
|
|
||
|
return it;
|
||
|
}
|
||
|
|
||
|
/*
|
||
|
* skiplist element free function
|
||
|
*/
|
||
|
static void delete_rem_pfx_na_free(void *na)
|
||
|
{
|
||
|
uint32_t *pCounter = ((struct rfapi_nve_addr *)na)->info;
|
||
|
|
||
|
*pCounter += 1;
|
||
|
XFREE(MTYPE_RFAPI_NVE_ADDR, na);
|
||
|
}
|
||
|
|
||
|
/*
|
||
|
* Common deleter for IP and MAC import tables
|
||
|
*/
|
||
|
static void rfapiDeleteRemotePrefixesIt(
|
||
|
struct bgp *bgp, struct rfapi_import_table *it, struct prefix *un,
|
||
|
struct prefix *vn, struct prefix *p, int delete_active,
|
||
|
int delete_holddown, uint32_t *pARcount, uint32_t *pAHcount,
|
||
|
uint32_t *pHRcount, uint32_t *pHHcount,
|
||
|
struct skiplist *uniq_active_nves, struct skiplist *uniq_holddown_nves)
|
||
|
{
|
||
|
afi_t afi;
|
||
|
|
||
|
#ifdef DEBUG_L2_EXTRA
|
||
|
{
|
||
|
char buf_pfx[PREFIX_STRLEN];
|
||
|
|
||
|
if (p) {
|
||
|
prefix2str(p, buf_pfx, sizeof(buf_pfx));
|
||
|
} else {
|
||
|
buf_pfx[0] = '*';
|
||
|
buf_pfx[1] = 0;
|
||
|
}
|
||
|
|
||
|
vnc_zlog_debug_verbose(
|
||
|
"%s: entry, p=%s, delete_active=%d, delete_holddown=%d",
|
||
|
__func__, buf_pfx, delete_active, delete_holddown);
|
||
|
}
|
||
|
#endif
|
||
|
|
||
|
for (afi = AFI_IP; afi < AFI_MAX; ++afi) {
|
||
|
|
||
|
struct agg_table *rt;
|
||
|
struct agg_node *rn;
|
||
|
|
||
|
if (p && (family2afi(p->family) != afi)) {
|
||
|
continue;
|
||
|
}
|
||
|
|
||
|
rt = it->imported_vpn[afi];
|
||
|
if (!rt)
|
||
|
continue;
|
||
|
|
||
|
vnc_zlog_debug_verbose("%s: scanning rt for afi=%d", __func__,
|
||
|
afi);
|
||
|
|
||
|
for (rn = agg_route_top(rt); rn; rn = agg_route_next(rn)) {
|
||
|
struct bgp_path_info *bpi;
|
||
|
struct bgp_path_info *next;
|
||
|
const struct prefix *rn_p = agg_node_get_prefix(rn);
|
||
|
|
||
|
if (p && VNC_DEBUG(IMPORT_DEL_REMOTE))
|
||
|
vnc_zlog_debug_any("%s: want %pFX, have %pRN",
|
||
|
__func__, p, rn);
|
||
|
|
||
|
if (p && prefix_cmp(p, rn_p))
|
||
|
continue;
|
||
|
|
||
|
vnc_zlog_debug_verbose("%s: rn pfx=%pRN", __func__, rn);
|
||
|
|
||
|
/* TBD is this valid for afi == AFI_L2VPN? */
|
||
|
RFAPI_CHECK_REFCOUNT(rn, SAFI_MPLS_VPN, 1);
|
||
|
|
||
|
for (bpi = rn->info; bpi; bpi = next) {
|
||
|
next = bpi->next;
|
||
|
|
||
|
struct prefix qpt;
|
||
|
struct prefix qct;
|
||
|
int qpt_valid = 0;
|
||
|
int qct_valid = 0;
|
||
|
int is_active = 0;
|
||
|
|
||
|
vnc_zlog_debug_verbose("%s: examining bpi %p",
|
||
|
__func__, bpi);
|
||
|
|
||
|
if (!rfapiGetNexthop(bpi->attr, &qpt))
|
||
|
qpt_valid = 1;
|
||
|
|
||
|
if (vn) {
|
||
|
if (!qpt_valid
|
||
|
|| !prefix_match(vn, &qpt)) {
|
||
|
#ifdef DEBUG_L2_EXTRA
|
||
|
vnc_zlog_debug_verbose(
|
||
|
"%s: continue at vn && !qpt_valid || !prefix_match(vn, &qpt)",
|
||
|
__func__);
|
||
|
#endif
|
||
|
continue;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
if (!rfapiGetUnAddrOfVpnBi(bpi, &qct))
|
||
|
qct_valid = 1;
|
||
|
|
||
|
if (un) {
|
||
|
if (!qct_valid
|
||
|
|| !prefix_match(un, &qct)) {
|
||
|
#ifdef DEBUG_L2_EXTRA
|
||
|
vnc_zlog_debug_verbose(
|
||
|
"%s: continue at un && !qct_valid || !prefix_match(un, &qct)",
|
||
|
__func__);
|
||
|
#endif
|
||
|
continue;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
|
||
|
/*
|
||
|
* Blow bpi away
|
||
|
*/
|
||
|
/*
|
||
|
* If this route is waiting to be deleted
|
||
|
* because of
|
||
|
* a previous withdraw, we must cancel its
|
||
|
* timer.
|
||
|
*/
|
||
|
if (CHECK_FLAG(bpi->flags, BGP_PATH_REMOVED)) {
|
||
|
if (!delete_holddown)
|
||
|
continue;
|
||
|
if (bpi->extra->vnc->vnc.import.timer) {
|
||
|
struct rfapi_withdraw *wcb = EVENT_ARG(
|
||
|
bpi->extra->vnc->vnc
|
||
|
.import.timer);
|
||
|
|
||
|
wcb->import_table
|
||
|
->holddown_count[afi] -=
|
||
|
1;
|
||
|
RFAPI_UPDATE_ITABLE_COUNT(
|
||
|
bpi, wcb->import_table,
|
||
|
afi, 1);
|
||
|
XFREE(MTYPE_RFAPI_WITHDRAW,
|
||
|
wcb);
|
||
|
EVENT_OFF(bpi->extra->vnc->vnc
|
||
|
.import.timer);
|
||
|
}
|
||
|
} else {
|
||
|
if (!delete_active)
|
||
|
continue;
|
||
|
is_active = 1;
|
||
|
}
|
||
|
|
||
|
vnc_zlog_debug_verbose(
|
||
|
"%s: deleting bpi %p (qct_valid=%d, qpt_valid=%d, delete_holddown=%d, delete_active=%d)",
|
||
|
__func__, bpi, qct_valid, qpt_valid,
|
||
|
delete_holddown, delete_active);
|
||
|
|
||
|
|
||
|
/*
|
||
|
* add nve to list
|
||
|
*/
|
||
|
if (qct_valid && qpt_valid) {
|
||
|
|
||
|
struct rfapi_nve_addr na;
|
||
|
struct rfapi_nve_addr *nap;
|
||
|
|
||
|
memset(&na, 0, sizeof(na));
|
||
|
assert(!rfapiQprefix2Raddr(&qct,
|
||
|
&na.un));
|
||
|
assert(!rfapiQprefix2Raddr(&qpt,
|
||
|
&na.vn));
|
||
|
|
||
|
if (skiplist_search(
|
||
|
(is_active
|
||
|
? uniq_active_nves
|
||
|
: uniq_holddown_nves),
|
||
|
&na, (void **)&nap)) {
|
||
|
char line[BUFSIZ];
|
||
|
|
||
|
nap = XCALLOC(
|
||
|
MTYPE_RFAPI_NVE_ADDR,
|
||
|
sizeof(struct
|
||
|
rfapi_nve_addr));
|
||
|
*nap = na;
|
||
|
nap->info = is_active
|
||
|
? pAHcount
|
||
|
: pHHcount;
|
||
|
skiplist_insert(
|
||
|
(is_active
|
||
|
? uniq_active_nves
|
||
|
: uniq_holddown_nves),
|
||
|
nap, nap);
|
||
|
|
||
|
rfapiNveAddr2Str(nap, line,
|
||
|
BUFSIZ);
|
||
|
}
|
||
|
}
|
||
|
|
||
|
vnc_direct_bgp_rh_del_route(bgp, afi, rn_p,
|
||
|
bpi->peer);
|
||
|
|
||
|
RFAPI_UPDATE_ITABLE_COUNT(bpi, it, afi, -1);
|
||
|
it->holddown_count[afi] += 1;
|
||
|
rfapiExpireVpnNow(it, rn, bpi, 1);
|
||
|
|
||
|
vnc_zlog_debug_verbose(
|
||
|
"%s: incrementing count (is_active=%d)",
|
||
|
__func__, is_active);
|
||
|
|
||
|
if (is_active)
|
||
|
++*pARcount;
|
||
|
else
|
||
|
++*pHRcount;
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
|
||
|
|
||
|
/*
|
||
|
* For use by the "clear vnc prefixes" command
|
||
|
*/
|
||
|
/*------------------------------------------
|
||
|
* rfapiDeleteRemotePrefixes
|
||
|
*
|
||
|
* UI helper: For use by the "clear vnc prefixes" command
|
||
|
*
|
||
|
* input:
|
||
|
* un if set, tunnel must match this prefix
|
||
|
* vn if set, nexthop prefix must match this prefix
|
||
|
* p if set, prefix must match this prefix
|
||
|
* it if set, only look in this import table
|
||
|
*
|
||
|
* output
|
||
|
* pARcount number of active routes deleted
|
||
|
* pAHcount number of active nves deleted
|
||
|
* pHRcount number of holddown routes deleted
|
||
|
* pHHcount number of holddown nves deleted
|
||
|
*
|
||
|
* return value:
|
||
|
* void
|
||
|
--------------------------------------------*/
|
||
|
void rfapiDeleteRemotePrefixes(struct prefix *un, struct prefix *vn,
|
||
|
struct prefix *p,
|
||
|
struct rfapi_import_table *arg_it,
|
||
|
int delete_active, int delete_holddown,
|
||
|
uint32_t *pARcount, uint32_t *pAHcount,
|
||
|
uint32_t *pHRcount, uint32_t *pHHcount)
|
||
|
{
|
||
|
struct bgp *bgp;
|
||
|
struct rfapi *h;
|
||
|
struct rfapi_import_table *it;
|
||
|
uint32_t deleted_holddown_route_count = 0;
|
||
|
uint32_t deleted_active_route_count = 0;
|
||
|
uint32_t deleted_holddown_nve_count = 0;
|
||
|
uint32_t deleted_active_nve_count = 0;
|
||
|
struct skiplist *uniq_holddown_nves;
|
||
|
struct skiplist *uniq_active_nves;
|
||
|
|
||
|
VNC_ITRCCK;
|
||
|
|
||
|
bgp = bgp_get_default(); /* assume 1 instance for now */
|
||
|
/* If no bgp instantiated yet, no vnc prefixes exist */
|
||
|
if (!bgp)
|
||
|
return;
|
||
|
|
||
|
h = bgp->rfapi;
|
||
|
assert(h);
|
||
|
|
||
|
uniq_holddown_nves =
|
||
|
skiplist_new(0, rfapi_nve_addr_cmp, delete_rem_pfx_na_free);
|
||
|
uniq_active_nves =
|
||
|
skiplist_new(0, rfapi_nve_addr_cmp, delete_rem_pfx_na_free);
|
||
|
|
||
|
/*
|
||
|
* Iterate over all import tables; do a filtered import
|
||
|
* for the afi/safi combination
|
||
|
*/
|
||
|
|
||
|
if (arg_it)
|
||
|
it = arg_it;
|
||
|
else
|
||
|
it = h->imports;
|
||
|
for (; it;) {
|
||
|
|
||
|
vnc_zlog_debug_verbose(
|
||
|
"%s: calling rfapiDeleteRemotePrefixesIt() on (IP) import %p",
|
||
|
__func__, it);
|
||
|
|
||
|
rfapiDeleteRemotePrefixesIt(
|
||
|
bgp, it, un, vn, p, delete_active, delete_holddown,
|
||
|
&deleted_active_route_count, &deleted_active_nve_count,
|
||
|
&deleted_holddown_route_count,
|
||
|
&deleted_holddown_nve_count, uniq_active_nves,
|
||
|
uniq_holddown_nves);
|
||
|
|
||
|
if (arg_it)
|
||
|
it = NULL;
|
||
|
else
|
||
|
it = it->next;
|
||
|
}
|
||
|
|
||
|
/*
|
||
|
* Now iterate over L2 import tables
|
||
|
*/
|
||
|
if (h->import_mac && !(p && (p->family != AF_ETHERNET))) {
|
||
|
|
||
|
void *cursor = NULL;
|
||
|
int rc;
|
||
|
|
||
|
for (cursor = NULL,
|
||
|
rc = skiplist_next(h->import_mac, NULL, (void **)&it,
|
||
|
&cursor);
|
||
|
!rc; rc = skiplist_next(h->import_mac, NULL, (void **)&it,
|
||
|
&cursor)) {
|
||
|
|
||
|
vnc_zlog_debug_verbose(
|
||
|
"%s: calling rfapiDeleteRemotePrefixesIt() on import_mac %p",
|
||
|
__func__, it);
|
||
|
|
||
|
rfapiDeleteRemotePrefixesIt(
|
||
|
bgp, it, un, vn, p, delete_active,
|
||
|
delete_holddown, &deleted_active_route_count,
|
||
|
&deleted_active_nve_count,
|
||
|
&deleted_holddown_route_count,
|
||
|
&deleted_holddown_nve_count, uniq_active_nves,
|
||
|
uniq_holddown_nves);
|
||
|
}
|
||
|
}
|
||
|
|
||
|
/*
|
||
|
* our custom element freeing function above counts as it deletes
|
||
|
*/
|
||
|
skiplist_free(uniq_holddown_nves);
|
||
|
skiplist_free(uniq_active_nves);
|
||
|
|
||
|
if (pARcount)
|
||
|
*pARcount = deleted_active_route_count;
|
||
|
if (pAHcount)
|
||
|
*pAHcount = deleted_active_nve_count;
|
||
|
if (pHRcount)
|
||
|
*pHRcount = deleted_holddown_route_count;
|
||
|
if (pHHcount)
|
||
|
*pHHcount = deleted_holddown_nve_count;
|
||
|
|
||
|
VNC_ITRCCK;
|
||
|
}
|
||
|
|
||
|
/*------------------------------------------
|
||
|
* rfapiCountRemoteRoutes
|
||
|
*
|
||
|
* UI helper: count VRF routes from BGP side
|
||
|
*
|
||
|
* input:
|
||
|
*
|
||
|
* output
|
||
|
* pALRcount count of active local routes
|
||
|
* pARRcount count of active remote routes
|
||
|
* pHRcount count of holddown routes
|
||
|
* pIRcount count of direct imported routes
|
||
|
*
|
||
|
* return value:
|
||
|
* void
|
||
|
--------------------------------------------*/
|
||
|
void rfapiCountAllItRoutes(int *pALRcount, /* active local routes */
|
||
|
int *pARRcount, /* active remote routes */
|
||
|
int *pHRcount, /* holddown routes */
|
||
|
int *pIRcount) /* imported routes */
|
||
|
{
|
||
|
struct bgp *bgp;
|
||
|
struct rfapi *h;
|
||
|
struct rfapi_import_table *it;
|
||
|
afi_t afi;
|
||
|
|
||
|
int total_active_local = 0;
|
||
|
int total_active_remote = 0;
|
||
|
int total_holddown = 0;
|
||
|
int total_imported = 0;
|
||
|
|
||
|
bgp = bgp_get_default(); /* assume 1 instance for now */
|
||
|
assert(bgp);
|
||
|
|
||
|
h = bgp->rfapi;
|
||
|
assert(h);
|
||
|
|
||
|
/*
|
||
|
* Iterate over all import tables; do a filtered import
|
||
|
* for the afi/safi combination
|
||
|
*/
|
||
|
|
||
|
for (it = h->imports; it; it = it->next) {
|
||
|
|
||
|
for (afi = AFI_IP; afi < AFI_MAX; ++afi) {
|
||
|
|
||
|
total_active_local += it->local_count[afi];
|
||
|
total_active_remote += it->remote_count[afi];
|
||
|
total_holddown += it->holddown_count[afi];
|
||
|
total_imported += it->imported_count[afi];
|
||
|
}
|
||
|
}
|
||
|
|
||
|
void *cursor;
|
||
|
int rc;
|
||
|
|
||
|
if (h->import_mac) {
|
||
|
for (cursor = NULL,
|
||
|
rc = skiplist_next(h->import_mac, NULL, (void **)&it,
|
||
|
&cursor);
|
||
|
!rc; rc = skiplist_next(h->import_mac, NULL, (void **)&it,
|
||
|
&cursor)) {
|
||
|
|
||
|
total_active_local += it->local_count[AFI_L2VPN];
|
||
|
total_active_remote += it->remote_count[AFI_L2VPN];
|
||
|
total_holddown += it->holddown_count[AFI_L2VPN];
|
||
|
total_imported += it->imported_count[AFI_L2VPN];
|
||
|
}
|
||
|
}
|
||
|
|
||
|
|
||
|
if (pALRcount) {
|
||
|
*pALRcount = total_active_local;
|
||
|
}
|
||
|
if (pARRcount) {
|
||
|
*pARRcount = total_active_remote;
|
||
|
}
|
||
|
if (pHRcount) {
|
||
|
*pHRcount = total_holddown;
|
||
|
}
|
||
|
if (pIRcount) {
|
||
|
*pIRcount = total_imported;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
/*------------------------------------------
|
||
|
* rfapiGetHolddownFromLifetime
|
||
|
*
|
||
|
* calculate holddown value based on lifetime
|
||
|
*
|
||
|
* input:
|
||
|
* lifetime lifetime
|
||
|
*
|
||
|
* return value:
|
||
|
* Holddown value based on lifetime, holddown_factor,
|
||
|
* and RFAPI_LIFETIME_INFINITE_WITHDRAW_DELAY
|
||
|
*
|
||
|
--------------------------------------------*/
|
||
|
/* hold down time maxes out at RFAPI_LIFETIME_INFINITE_WITHDRAW_DELAY */
|
||
|
uint32_t rfapiGetHolddownFromLifetime(uint32_t lifetime)
|
||
|
{
|
||
|
uint32_t factor;
|
||
|
struct bgp *bgp;
|
||
|
|
||
|
bgp = bgp_get_default();
|
||
|
if (bgp && bgp->rfapi_cfg)
|
||
|
factor = bgp->rfapi_cfg->rfp_cfg.holddown_factor;
|
||
|
else
|
||
|
factor = RFAPI_RFP_CFG_DEFAULT_HOLDDOWN_FACTOR;
|
||
|
|
||
|
if (factor < 100 || lifetime < RFAPI_LIFETIME_INFINITE_WITHDRAW_DELAY)
|
||
|
lifetime = lifetime * factor / 100;
|
||
|
if (lifetime < RFAPI_LIFETIME_INFINITE_WITHDRAW_DELAY)
|
||
|
return lifetime;
|
||
|
else
|
||
|
return RFAPI_LIFETIME_INFINITE_WITHDRAW_DELAY;
|
||
|
}
|