From 86c55361f9d47de9c4db4d03c44beb5fcb375bc2 Mon Sep 17 00:00:00 2001 From: Igor Ryzhov Date: Sun, 23 Jan 2022 20:22:42 +0300 Subject: [PATCH] zebra: fix cleanup of meta queues on vrf disable Current code treats all metaqueues as lists of route_node structures. However, some queues contain other structures that need to be cleaned up differently. Casting the elements of those queues to struct route_node and dereferencing them leads to a crash. The crash may be seen when executing bgp_multi_vrf_topo2. Fix the code by using the proper list element types. Signed-off-by: Igor Ryzhov (cherry picked from commit 0ef6eacc95c82014c04f13be3b641ff3983040ca) --- zebra/rib.h | 2 ++ zebra/zebra_rib.c | 53 +++++++++++++++++++++++++++++++++++++++++++++++ zebra/zebra_vrf.c | 34 ++---------------------------- 3 files changed, 57 insertions(+), 32 deletions(-) diff --git a/zebra/rib.h b/zebra/rib.h index a0ec1f0e4f..d5aec5d4c1 100644 --- a/zebra/rib.h +++ b/zebra/rib.h @@ -481,6 +481,8 @@ int zebra_rib_queue_evpn_rem_vtep_del(vrf_id_t vrf_id, vni_t vni, struct in_addr vtep_ip); extern void meta_queue_free(struct meta_queue *mq); +extern void rib_meta_queue_free_vrf(struct meta_queue *mq, + struct zebra_vrf *zvrf); extern int zebra_rib_labeled_unicast(struct route_entry *re); extern struct route_table *rib_table_ipv6; diff --git a/zebra/zebra_rib.c b/zebra/zebra_rib.c index 1374b932ae..96897e1128 100644 --- a/zebra/zebra_rib.c +++ b/zebra/zebra_rib.c @@ -2944,6 +2944,59 @@ void meta_queue_free(struct meta_queue *mq) XFREE(MTYPE_WORK_QUEUE, mq); } +void rib_meta_queue_free_vrf(struct meta_queue *mq, struct zebra_vrf *zvrf) +{ + vrf_id_t vrf_id = zvrf->vrf->vrf_id; + unsigned int i; + + for (i = 0; i < MQ_SIZE; i++) { + struct listnode *lnode, *nnode; + void *data; + bool del; + + for (ALL_LIST_ELEMENTS(mq->subq[i], lnode, nnode, data)) { + del = false; + + if (i == META_QUEUE_EVPN) { + struct wq_evpn_wrapper *w = data; + + if (w->vrf_id == vrf_id) { + XFREE(MTYPE_WQ_WRAPPER, w); + del = true; + } + } else if (i == + route_info[ZEBRA_ROUTE_NHG].meta_q_map) { + struct wq_nhg_wrapper *w = data; + + if (w->type == WQ_NHG_WRAPPER_TYPE_CTX && + w->u.ctx->vrf_id == vrf_id) { + nhg_ctx_free(&w->u.ctx); + XFREE(MTYPE_WQ_WRAPPER, w); + del = true; + } else if (w->type == WQ_NHG_WRAPPER_TYPE_NHG && + w->u.nhe->vrf_id == vrf_id) { + zebra_nhg_free(w->u.nhe); + XFREE(MTYPE_WQ_WRAPPER, w); + del = true; + } + } else { + struct route_node *rnode = data; + rib_dest_t *dest = rib_dest_from_rnode(rnode); + + if (dest && rib_dest_vrf(dest) == zvrf) { + route_unlock_node(rnode); + del = true; + } + } + + if (del) { + list_delete_node(mq->subq[i], lnode); + mq->size--; + } + } + } +} + /* initialise zebra rib work queue */ static void rib_queue_init(void) { diff --git a/zebra/zebra_vrf.c b/zebra/zebra_vrf.c index 842dc3f576..f88a65d952 100644 --- a/zebra/zebra_vrf.c +++ b/zebra/zebra_vrf.c @@ -177,7 +177,6 @@ static int zebra_vrf_disable(struct vrf *vrf) struct interface *ifp; afi_t afi; safi_t safi; - unsigned i; assert(zvrf); if (IS_ZEBRA_DEBUG_EVENT) @@ -222,21 +221,7 @@ static int zebra_vrf_disable(struct vrf *vrf) if_nbr_ipv6ll_to_ipv4ll_neigh_del_all(ifp); /* clean-up work queues */ - for (i = 0; i < MQ_SIZE; i++) { - struct listnode *lnode, *nnode; - struct route_node *rnode; - rib_dest_t *dest; - - for (ALL_LIST_ELEMENTS(zrouter.mq->subq[i], lnode, nnode, - rnode)) { - dest = rib_dest_from_rnode(rnode); - if (dest && rib_dest_vrf(dest) == zvrf) { - route_unlock_node(rnode); - list_delete_node(zrouter.mq->subq[i], lnode); - zrouter.mq->size--; - } - } - } + rib_meta_queue_free_vrf(zrouter.mq, zvrf); /* Cleanup (free) routing tables and NHT tables. */ for (afi = AFI_IP; afi <= AFI_IP6; afi++) { @@ -262,7 +247,6 @@ static int zebra_vrf_delete(struct vrf *vrf) { struct zebra_vrf *zvrf = vrf->info; struct other_route_table *otable; - unsigned i; assert(zvrf); if (IS_ZEBRA_DEBUG_EVENT) @@ -272,21 +256,7 @@ static int zebra_vrf_delete(struct vrf *vrf) table_manager_disable(zvrf); /* clean-up work queues */ - for (i = 0; i < MQ_SIZE; i++) { - struct listnode *lnode, *nnode; - struct route_node *rnode; - rib_dest_t *dest; - - for (ALL_LIST_ELEMENTS(zrouter.mq->subq[i], lnode, nnode, - rnode)) { - dest = rib_dest_from_rnode(rnode); - if (dest && rib_dest_vrf(dest) == zvrf) { - route_unlock_node(rnode); - list_delete_node(zrouter.mq->subq[i], lnode); - zrouter.mq->size--; - } - } - } + rib_meta_queue_free_vrf(zrouter.mq, zvrf); /* Free Vxlan and MPLS. */ zebra_vxlan_close_tables(zvrf);