bgpd: rfapi: track outstanding rib and import timers, free mem at exit

While here, also make "VPN SAFI clear" test wait for clear result
    (tests/topotests/bgp_rfapi_basic_sanity{,_config2})

    Original RFAPI code relied on the frr timer system to remember
    various allocations that were supposed to be freed at future times
    rather than manage a parallel database. However, if bgpd is terminated
    before the times expire, those pending allocations are marked as
    memory leaks, even though they wouldn't be leaks under normal operation.

    This change adds some hash tables to track these outstanding
    allocations that are associated with pending timers, and uses
    those tables to free the allocations when bgpd exits.

Signed-off-by: G. Paul Ziemba <paulz@labn.net>
This commit is contained in:
G. Paul Ziemba 2025-03-30 15:43:04 -07:00
parent a02ec27693
commit 1629c05924
10 changed files with 181 additions and 51 deletions

View file

@ -8846,6 +8846,9 @@ void bgp_terminate(void)
EVENT_OFF(bm->t_bgp_zebra_l3_vni); EVENT_OFF(bm->t_bgp_zebra_l3_vni);
bgp_mac_finish(); bgp_mac_finish();
#if ENABLE_BGP_VNC
rfapi_terminate();
#endif
} }
struct peer *peer_lookup_in_view(struct vty *vty, struct bgp *bgp, struct peer *peer_lookup_in_view(struct vty *vty, struct bgp *bgp,

View file

@ -3546,6 +3546,8 @@ DEFUN (skiplist_debug_cli,
void rfapi_init(void) void rfapi_init(void)
{ {
rfapi_rib_init();
rfapi_import_init();
bgp_rfapi_cfg_init(); bgp_rfapi_cfg_init();
vnc_debug_init(); vnc_debug_init();
@ -3576,6 +3578,12 @@ void rfapi_init(void)
rfapi_vty_init(); rfapi_vty_init();
} }
void rfapi_terminate(void)
{
rfapi_import_terminate();
rfapi_rib_terminate();
}
#ifdef DEBUG_RFAPI #ifdef DEBUG_RFAPI
static void rfapi_print_exported(struct bgp *bgp) static void rfapi_print_exported(struct bgp *bgp)
{ {

View file

@ -14,6 +14,7 @@
#include "bgpd/bgp_nexthop.h" #include "bgpd/bgp_nexthop.h"
extern void rfapi_init(void); extern void rfapi_init(void);
extern void rfapi_terminate(void);
extern void vnc_zebra_init(struct event_loop *master); extern void vnc_zebra_init(struct event_loop *master);
extern void vnc_zebra_destroy(void); extern void vnc_zebra_destroy(void);

View file

@ -52,14 +52,23 @@
#undef DEBUG_IT_NODES #undef DEBUG_IT_NODES
#undef DEBUG_BI_SEARCH #undef DEBUG_BI_SEARCH
/*
* Hash to keep track of outstanding timers so we can force them to
* expire at shutdown time, thus freeing their allocated memory.
*/
PREDECL_HASH(rwcb);
/* /*
* Allocated for each withdraw timer instance; freed when the timer * Allocated for each withdraw timer instance; freed when the timer
* expires or is canceled * expires or is canceled
*/ */
struct rfapi_withdraw { struct rfapi_withdraw {
struct rwcb_item rwcbi;
struct rfapi_import_table *import_table; struct rfapi_import_table *import_table;
struct agg_node *node; struct agg_node *node;
struct bgp_path_info *info; struct bgp_path_info *info;
void (*timer_service_func)(struct event *t); /* for cleanup */
safi_t safi; /* used only for bulk operations */ safi_t safi; /* used only for bulk operations */
/* /*
* For import table node reference count checking (i.e., debugging). * For import table node reference count checking (i.e., debugging).
@ -72,6 +81,19 @@ struct rfapi_withdraw {
int lockoffset; int lockoffset;
}; };
static int _rwcb_cmp(const struct rfapi_withdraw *w1, const struct rfapi_withdraw *w2)
{
return (w1 != w2);
}
static uint32_t _rwcb_hash(const struct rfapi_withdraw *w)
{
return (uintptr_t)w & 0xffffffff;
}
DECLARE_HASH(rwcb, struct rfapi_withdraw, rwcbi, _rwcb_cmp, _rwcb_hash);
static struct rwcb_head _rwcbhash;
/* /*
* DEBUG FUNCTION * DEBUG FUNCTION
* Count remote routes and compare with actively-maintained values. * Count remote routes and compare with actively-maintained values.
@ -826,6 +848,7 @@ static void rfapiBgpInfoChainFree(struct bgp_path_info *bpi)
struct rfapi_withdraw *wcb = struct rfapi_withdraw *wcb =
EVENT_ARG(bpi->extra->vnc->vnc.import.timer); EVENT_ARG(bpi->extra->vnc->vnc.import.timer);
rwcb_del(&_rwcbhash, wcb);
XFREE(MTYPE_RFAPI_WITHDRAW, wcb); XFREE(MTYPE_RFAPI_WITHDRAW, wcb);
EVENT_OFF(bpi->extra->vnc->vnc.import.timer); EVENT_OFF(bpi->extra->vnc->vnc.import.timer);
} }
@ -2349,6 +2372,7 @@ static void rfapiWithdrawTimerVPN(struct event *t)
/* This callback is responsible for the withdraw object's memory */ /* This callback is responsible for the withdraw object's memory */
if (early_exit) { if (early_exit) {
rwcb_del(&_rwcbhash, wcb);
XFREE(MTYPE_RFAPI_WITHDRAW, wcb); XFREE(MTYPE_RFAPI_WITHDRAW, wcb);
return; return;
} }
@ -2462,6 +2486,7 @@ done:
RFAPI_CHECK_REFCOUNT(wcb->node, SAFI_MPLS_VPN, 1 + wcb->lockoffset); RFAPI_CHECK_REFCOUNT(wcb->node, SAFI_MPLS_VPN, 1 + wcb->lockoffset);
agg_unlock_node(wcb->node); /* decr ref count */ agg_unlock_node(wcb->node); /* decr ref count */
rwcb_del(&_rwcbhash, wcb);
XFREE(MTYPE_RFAPI_WITHDRAW, wcb); XFREE(MTYPE_RFAPI_WITHDRAW, wcb);
} }
@ -2705,6 +2730,7 @@ static void rfapiWithdrawTimerEncap(struct event *t)
done: done:
RFAPI_CHECK_REFCOUNT(wcb->node, SAFI_ENCAP, 1); RFAPI_CHECK_REFCOUNT(wcb->node, SAFI_ENCAP, 1);
agg_unlock_node(wcb->node); /* decr ref count */ agg_unlock_node(wcb->node); /* decr ref count */
rwcb_del(&_rwcbhash, wcb);
XFREE(MTYPE_RFAPI_WITHDRAW, wcb); XFREE(MTYPE_RFAPI_WITHDRAW, wcb);
skiplist_free(vpn_node_sl); skiplist_free(vpn_node_sl);
} }
@ -2754,6 +2780,8 @@ rfapiBiStartWithdrawTimer(struct rfapi_import_table *import_table,
wcb->node = rn; wcb->node = rn;
wcb->info = bpi; wcb->info = bpi;
wcb->import_table = import_table; wcb->import_table = import_table;
wcb->timer_service_func = timer_service_func;
rwcb_add(&_rwcbhash, wcb);
bgp_attr_intern(bpi->attr); bgp_attr_intern(bpi->attr);
if (VNC_DEBUG(VERBOSE)) { if (VNC_DEBUG(VERBOSE)) {
@ -2819,6 +2847,7 @@ static void rfapiExpireEncapNow(struct rfapi_import_table *it,
wcb->info = bpi; wcb->info = bpi;
wcb->node = rn; wcb->node = rn;
wcb->import_table = it; wcb->import_table = it;
rwcb_add(&_rwcbhash, wcb);
memset(&t, 0, sizeof(t)); memset(&t, 0, sizeof(t));
t.arg = wcb; t.arg = wcb;
rfapiWithdrawTimerEncap(&t); /* frees wcb */ rfapiWithdrawTimerEncap(&t); /* frees wcb */
@ -3057,6 +3086,7 @@ static void rfapiBgpInfoFilteredImportEncap(
struct rfapi_withdraw *wcb = EVENT_ARG( struct rfapi_withdraw *wcb = EVENT_ARG(
bpi->extra->vnc->vnc.import.timer); bpi->extra->vnc->vnc.import.timer);
rwcb_del(&_rwcbhash, wcb);
XFREE(MTYPE_RFAPI_WITHDRAW, wcb); XFREE(MTYPE_RFAPI_WITHDRAW, wcb);
EVENT_OFF(bpi->extra->vnc->vnc.import EVENT_OFF(bpi->extra->vnc->vnc.import
.timer); .timer);
@ -3083,6 +3113,7 @@ static void rfapiBgpInfoFilteredImportEncap(
wcb->info = bpi; wcb->info = bpi;
wcb->node = rn; wcb->node = rn;
wcb->import_table = import_table; wcb->import_table = import_table;
rwcb_add(&_rwcbhash, wcb);
memset(&t, 0, sizeof(t)); memset(&t, 0, sizeof(t));
t.arg = wcb; t.arg = wcb;
rfapiWithdrawTimerEncap( rfapiWithdrawTimerEncap(
@ -3149,6 +3180,7 @@ static void rfapiBgpInfoFilteredImportEncap(
struct rfapi_withdraw *wcb = struct rfapi_withdraw *wcb =
EVENT_ARG(bpi->extra->vnc->vnc.import.timer); EVENT_ARG(bpi->extra->vnc->vnc.import.timer);
rwcb_del(&_rwcbhash, wcb);
XFREE(MTYPE_RFAPI_WITHDRAW, wcb); XFREE(MTYPE_RFAPI_WITHDRAW, wcb);
EVENT_OFF(bpi->extra->vnc->vnc.import.timer); EVENT_OFF(bpi->extra->vnc->vnc.import.timer);
} }
@ -3293,6 +3325,7 @@ static void rfapiExpireVpnNow(struct rfapi_import_table *it,
wcb->node = rn; wcb->node = rn;
wcb->import_table = it; wcb->import_table = it;
wcb->lockoffset = lockoffset; wcb->lockoffset = lockoffset;
rwcb_add(&_rwcbhash, wcb);
memset(&t, 0, sizeof(t)); memset(&t, 0, sizeof(t));
t.arg = wcb; t.arg = wcb;
rfapiWithdrawTimerVPN(&t); /* frees wcb */ rfapiWithdrawTimerVPN(&t); /* frees wcb */
@ -3510,6 +3543,7 @@ void rfapiBgpInfoFilteredImportVPN(
struct rfapi_withdraw *wcb = EVENT_ARG( struct rfapi_withdraw *wcb = EVENT_ARG(
bpi->extra->vnc->vnc.import.timer); bpi->extra->vnc->vnc.import.timer);
rwcb_del(&_rwcbhash, wcb);
XFREE(MTYPE_RFAPI_WITHDRAW, wcb); XFREE(MTYPE_RFAPI_WITHDRAW, wcb);
EVENT_OFF(bpi->extra->vnc->vnc.import EVENT_OFF(bpi->extra->vnc->vnc.import
.timer); .timer);
@ -3729,6 +3763,7 @@ void rfapiBgpInfoFilteredImportVPN(
struct rfapi_withdraw *wcb = struct rfapi_withdraw *wcb =
EVENT_ARG(bpi->extra->vnc->vnc.import.timer); EVENT_ARG(bpi->extra->vnc->vnc.import.timer);
rwcb_del(&_rwcbhash, wcb);
XFREE(MTYPE_RFAPI_WITHDRAW, wcb); XFREE(MTYPE_RFAPI_WITHDRAW, wcb);
EVENT_OFF(bpi->extra->vnc->vnc.import.timer); EVENT_OFF(bpi->extra->vnc->vnc.import.timer);
} }
@ -4474,6 +4509,7 @@ static void rfapiDeleteRemotePrefixesIt(
RFAPI_UPDATE_ITABLE_COUNT( RFAPI_UPDATE_ITABLE_COUNT(
bpi, wcb->import_table, bpi, wcb->import_table,
afi, 1); afi, 1);
rwcb_del(&_rwcbhash, wcb);
XFREE(MTYPE_RFAPI_WITHDRAW, XFREE(MTYPE_RFAPI_WITHDRAW,
wcb); wcb);
EVENT_OFF(bpi->extra->vnc->vnc EVENT_OFF(bpi->extra->vnc->vnc
@ -4798,3 +4834,33 @@ uint32_t rfapiGetHolddownFromLifetime(uint32_t lifetime)
else else
return RFAPI_LIFETIME_INFINITE_WITHDRAW_DELAY; return RFAPI_LIFETIME_INFINITE_WITHDRAW_DELAY;
} }
void rfapi_import_init(void)
{
rwcb_init(&_rwcbhash);
}
void rfapi_import_terminate(void)
{
struct rfapi_withdraw *wcb;
struct bgp_path_info *bpi;
void (*timer_service_func)(struct event *t);
struct event t;
vnc_zlog_debug_verbose("%s: cleaning up %zu pending timers", __func__,
rwcb_count(&_rwcbhash));
/*
* clean up memory allocations stored in pending timers
*/
while ((wcb = rwcb_pop(&_rwcbhash))) {
bpi = wcb->info;
assert(wcb == EVENT_ARG(bpi->extra->vnc->vnc.import.timer));
EVENT_OFF(bpi->extra->vnc->vnc.import.timer);
timer_service_func = wcb->timer_service_func;
memset(&t, 0, sizeof(t));
t.arg = wcb;
(*timer_service_func)(&t); /* frees wcb */
}
}

View file

@ -225,4 +225,7 @@ extern void rfapiCountAllItRoutes(int *pALRcount, /* active local routes */
--------------------------------------------*/ --------------------------------------------*/
extern uint32_t rfapiGetHolddownFromLifetime(uint32_t lifetime); extern uint32_t rfapiGetHolddownFromLifetime(uint32_t lifetime);
extern void rfapi_import_init(void);
extern void rfapi_import_terminate(void);
#endif /* QUAGGA_HGP_RFAPI_IMPORT_H */ #endif /* QUAGGA_HGP_RFAPI_IMPORT_H */

View file

@ -18,6 +18,7 @@
#include "lib/log.h" #include "lib/log.h"
#include "lib/skiplist.h" #include "lib/skiplist.h"
#include "lib/workqueue.h" #include "lib/workqueue.h"
#include <typesafe.h>
#include "bgpd/bgpd.h" #include "bgpd/bgpd.h"
#include "bgpd/bgp_route.h" #include "bgpd/bgp_route.h"
@ -44,8 +45,7 @@
/* forward decl */ /* forward decl */
#if DEBUG_NHL #if DEBUG_NHL
static void rfapiRibShowRibSl(void *stream, struct prefix *pfx, static void rfapiRibShowRibSl(void *stream, const struct prefix *pfx, struct skiplist *sl);
struct skiplist *sl);
#endif #endif
/* /*
@ -234,9 +234,45 @@ void rfapiFreeRfapiVnOptionChain(struct rfapi_vn_option *p)
} }
/*
* Hash to keep track of outstanding timers so we can force them to
* expire at shutdown time, thus freeing their allocated memory.
*/
PREDECL_HASH(rrtcb);
/*
* Timer control block for recently-deleted and expired routes
*/
struct rfapi_rib_tcb {
struct rrtcb_item tcbi;
struct rfapi_descriptor *rfd;
struct skiplist *sl;
struct rfapi_info *ri;
struct agg_node *rn;
int flags;
#define RFAPI_RIB_TCB_FLAG_DELETED 0x00000001
};
static int _rrtcb_cmp(const struct rfapi_rib_tcb *t1, const struct rfapi_rib_tcb *t2)
{
return (t1 != t2);
}
static uint32_t _rrtcb_hash(const struct rfapi_rib_tcb *t)
{
return (uintptr_t)t & 0xffffffff;
}
DECLARE_HASH(rrtcb, struct rfapi_rib_tcb, tcbi, _rrtcb_cmp, _rrtcb_hash);
static struct rrtcb_head _rrtcbhash;
static void rfapi_info_free(struct rfapi_info *goner) static void rfapi_info_free(struct rfapi_info *goner)
{ {
if (goner) { if (goner) {
#if DEBUG_CLEANUP
zlog_debug("%s: ri %p, timer %p", __func__, goner, goner->timer);
#endif
if (goner->tea_options) { if (goner->tea_options) {
rfapiFreeBgpTeaOptionChain(goner->tea_options); rfapiFreeBgpTeaOptionChain(goner->tea_options);
goner->tea_options = NULL; goner->tea_options = NULL;
@ -253,32 +289,19 @@ static void rfapi_info_free(struct rfapi_info *goner)
struct rfapi_rib_tcb *tcb; struct rfapi_rib_tcb *tcb;
tcb = EVENT_ARG(goner->timer); tcb = EVENT_ARG(goner->timer);
#if DEBUG_CLEANUP
zlog_debug("%s: ri %p, tcb %p", __func__, goner, tcb);
#endif
EVENT_OFF(goner->timer); EVENT_OFF(goner->timer);
rrtcb_del(&_rrtcbhash, tcb);
XFREE(MTYPE_RFAPI_RECENT_DELETE, tcb); XFREE(MTYPE_RFAPI_RECENT_DELETE, tcb);
} }
XFREE(MTYPE_RFAPI_INFO, goner); XFREE(MTYPE_RFAPI_INFO, goner);
} }
} }
/* static void _rfapiRibExpireTimer(struct rfapi_rib_tcb *tcb)
* Timer control block for recently-deleted and expired routes
*/
struct rfapi_rib_tcb {
struct rfapi_descriptor *rfd;
struct skiplist *sl;
struct rfapi_info *ri;
struct agg_node *rn;
int flags;
#define RFAPI_RIB_TCB_FLAG_DELETED 0x00000001
};
/*
* remove route from rib
*/
static void rfapiRibExpireTimer(struct event *t)
{ {
struct rfapi_rib_tcb *tcb = EVENT_ARG(t);
RFAPI_RIB_CHECK_COUNTS(1, 0); RFAPI_RIB_CHECK_COUNTS(1, 0);
/* /*
@ -309,11 +332,22 @@ static void rfapiRibExpireTimer(struct event *t)
agg_unlock_node(tcb->rn); agg_unlock_node(tcb->rn);
} }
rrtcb_del(&_rrtcbhash, tcb);
XFREE(MTYPE_RFAPI_RECENT_DELETE, tcb); XFREE(MTYPE_RFAPI_RECENT_DELETE, tcb);
RFAPI_RIB_CHECK_COUNTS(1, 0); RFAPI_RIB_CHECK_COUNTS(1, 0);
} }
/*
* remove route from rib
*/
static void rfapiRibExpireTimer(struct event *t)
{
struct rfapi_rib_tcb *tcb = EVENT_ARG(t);
_rfapiRibExpireTimer(tcb);
}
static void rfapiRibStartTimer(struct rfapi_descriptor *rfd, static void rfapiRibStartTimer(struct rfapi_descriptor *rfd,
struct rfapi_info *ri, struct rfapi_info *ri,
struct agg_node *rn, /* route node attached to */ struct agg_node *rn, /* route node attached to */
@ -349,6 +383,8 @@ static void rfapiRibStartTimer(struct rfapi_descriptor *rfd,
event_add_timer(bm->master, rfapiRibExpireTimer, tcb, ri->lifetime, event_add_timer(bm->master, rfapiRibExpireTimer, tcb, ri->lifetime,
&ri->timer); &ri->timer);
rrtcb_add(&_rrtcbhash, tcb);
} }
extern void rfapi_rib_key_init(struct prefix *prefix, /* may be NULL */ extern void rfapi_rib_key_init(struct prefix *prefix, /* may be NULL */
@ -519,6 +555,7 @@ void rfapiRibClear(struct rfapi_descriptor *rfd)
tcb = EVENT_ARG( tcb = EVENT_ARG(
ri->timer); ri->timer);
EVENT_OFF(ri->timer); EVENT_OFF(ri->timer);
rrtcb_del(&_rrtcbhash, tcb);
XFREE(MTYPE_RFAPI_RECENT_DELETE, XFREE(MTYPE_RFAPI_RECENT_DELETE,
tcb); tcb);
} }
@ -852,11 +889,6 @@ static void process_pending_node(struct bgp *bgp, struct rfapi_descriptor *rfd,
int rib_node_started_nonempty = 0; int rib_node_started_nonempty = 0;
int sendingsomeroutes = 0; int sendingsomeroutes = 0;
const struct prefix *p; const struct prefix *p;
#if DEBUG_PROCESS_PENDING_NODE
unsigned int count_rib_initial = 0;
unsigned int count_pend_vn_initial = 0;
unsigned int count_pend_cost_initial = 0;
#endif
assert(pn); assert(pn);
p = agg_node_get_prefix(pn); p = agg_node_get_prefix(pn);
@ -885,19 +917,6 @@ static void process_pending_node(struct bgp *bgp, struct rfapi_descriptor *rfd,
slPendPt = (struct skiplist *)(pn->aggregate); slPendPt = (struct skiplist *)(pn->aggregate);
lPendCost = (struct list *)(pn->info); lPendCost = (struct list *)(pn->info);
#if DEBUG_PROCESS_PENDING_NODE
/* debugging */
if (slRibPt)
count_rib_initial = skiplist_count(slRibPt);
if (slPendPt)
count_pend_vn_initial = skiplist_count(slPendPt);
if (lPendCost && lPendCost != (struct list *)1)
count_pend_cost_initial = lPendCost->count;
#endif
/* /*
* Handle special case: delete all routes at prefix * Handle special case: delete all routes at prefix
*/ */
@ -920,6 +939,7 @@ static void process_pending_node(struct bgp *bgp, struct rfapi_descriptor *rfd,
tcb = EVENT_ARG(ri->timer); tcb = EVENT_ARG(ri->timer);
EVENT_OFF(ri->timer); EVENT_OFF(ri->timer);
rrtcb_del(&_rrtcbhash, tcb);
XFREE(MTYPE_RFAPI_RECENT_DELETE, tcb); XFREE(MTYPE_RFAPI_RECENT_DELETE, tcb);
} }
@ -1005,6 +1025,7 @@ static void process_pending_node(struct bgp *bgp, struct rfapi_descriptor *rfd,
tcb = EVENT_ARG(ori->timer); tcb = EVENT_ARG(ori->timer);
EVENT_OFF(ori->timer); EVENT_OFF(ori->timer);
rrtcb_del(&_rrtcbhash, tcb);
XFREE(MTYPE_RFAPI_RECENT_DELETE, tcb); XFREE(MTYPE_RFAPI_RECENT_DELETE, tcb);
} }
@ -1017,6 +1038,11 @@ static void process_pending_node(struct bgp *bgp, struct rfapi_descriptor *rfd,
#endif #endif
} else { } else {
#if DEBUG_PROCESS_PENDING_NODE
vnc_zlog_debug_verbose("%s: slRibPt ri %p matched in pending list",
__func__, ori);
#endif
/* /*
* Found in pending list. If same lifetime, * Found in pending list. If same lifetime,
* cost, options, * cost, options,
@ -1040,16 +1066,12 @@ static void process_pending_node(struct bgp *bgp, struct rfapi_descriptor *rfd,
rfapi_info_free( rfapi_info_free(
ri); /* grr... */ ri); /* grr... */
} }
}
#if DEBUG_PROCESS_PENDING_NODE #if DEBUG_PROCESS_PENDING_NODE
vnc_zlog_debug_verbose( vnc_zlog_debug_verbose("%s: same info", __func__);
"%s: slRibPt ri %p matched in pending list, %s",
__func__, ori,
(same ? "same info"
: "different info"));
#endif #endif
} }
} }
}
/* /*
* Go back and delete items from RIB * Go back and delete items from RIB
*/ */
@ -1339,6 +1361,7 @@ callback:
tcb = EVENT_ARG(ri->timer); tcb = EVENT_ARG(ri->timer);
EVENT_OFF(ri->timer); EVENT_OFF(ri->timer);
rrtcb_del(&_rrtcbhash, tcb);
XFREE(MTYPE_RFAPI_RECENT_DELETE, tcb); XFREE(MTYPE_RFAPI_RECENT_DELETE, tcb);
} }
RFAPI_RIB_CHECK_COUNTS(0, delete_list->count); RFAPI_RIB_CHECK_COUNTS(0, delete_list->count);
@ -2285,8 +2308,7 @@ static int print_rib_sl(int (*fp)(void *, const char *, ...), struct vty *vty,
/* /*
* This one is for debugging (set stream to NULL to send output to log) * This one is for debugging (set stream to NULL to send output to log)
*/ */
static void rfapiRibShowRibSl(void *stream, struct prefix *pfx, static void rfapiRibShowRibSl(void *stream, const struct prefix *pfx, struct skiplist *sl)
struct skiplist *sl)
{ {
int (*fp)(void *, const char *, ...); int (*fp)(void *, const char *, ...);
struct vty *vty; struct vty *vty;
@ -2426,3 +2448,25 @@ void rfapiRibShowResponses(void *stream, struct prefix *pfx_match,
fp(out, "\n"); fp(out, "\n");
} }
} }
void rfapi_rib_init(void)
{
rrtcb_init(&_rrtcbhash);
}
void rfapi_rib_terminate(void)
{
struct rfapi_rib_tcb *tcb;
vnc_zlog_debug_verbose("%s: cleaning up %zu pending timers", __func__,
rrtcb_count(&_rrtcbhash));
/*
* Clean up memory allocations stored in pending timers
*/
while ((tcb = rrtcb_pop(&_rrtcbhash))) {
assert(tcb == EVENT_ARG(tcb->ri->timer));
EVENT_OFF(tcb->ri->timer);
_rfapiRibExpireTimer(tcb); /* deletes hash entry, frees tcb */
}
}

View file

@ -138,4 +138,7 @@ extern int rfapi_rib_key_cmp(const void *k1, const void *k2);
extern void rfapiAdbFree(struct rfapi_adb *adb); extern void rfapiAdbFree(struct rfapi_adb *adb);
extern void rfapi_rib_init(void);
extern void rfapi_rib_terminate(void);
#endif /* QUAGGA_HGP_RFAPI_RIB_H */ #endif /* QUAGGA_HGP_RFAPI_RIB_H */

View file

@ -4,6 +4,7 @@ hostname r3
password zebra password zebra
log stdout notifications log stdout notifications
log commands log commands
#debug bgp vnc verbose
router bgp 5226 router bgp 5226
bgp router-id 3.3.3.3 bgp router-id 3.3.3.3
bgp cluster-id 3.3.3.3 bgp cluster-id 3.3.3.3

View file

@ -91,10 +91,10 @@ luCommand(
) )
num = "0 exist" num = "0 exist"
luCommand("r1", 'vtysh -c "show bgp ipv4 vpn"', num, "pass", "VPN SAFI clear") luCommand("r1", 'vtysh -c "show bgp ipv4 vpn"', num, "wait", "VPN SAFI clear")
luCommand("r2", 'vtysh -c "show bgp ipv4 vpn"', num, "pass", "VPN SAFI clear") luCommand("r2", 'vtysh -c "show bgp ipv4 vpn"', num, "wait", "VPN SAFI clear")
luCommand("r3", 'vtysh -c "show bgp ipv4 vpn"', num, "pass", "VPN SAFI clear") luCommand("r3", 'vtysh -c "show bgp ipv4 vpn"', num, "wait", "VPN SAFI clear")
luCommand("r4", 'vtysh -c "show bgp ipv4 vpn"', num, "pass", "VPN SAFI clear") luCommand("r4", 'vtysh -c "show bgp ipv4 vpn"', num, "wait", "VPN SAFI clear")
luCommand( luCommand(
"r1", "r1",

View file

@ -4,6 +4,7 @@ hostname r3
password zebra password zebra
log stdout notifications log stdout notifications
log commands log commands
#debug bgp vnc verbose
router bgp 5226 router bgp 5226
bgp router-id 3.3.3.3 bgp router-id 3.3.3.3
bgp cluster-id 3.3.3.3 bgp cluster-id 3.3.3.3