diff options
| author | Igor Ryzhov <iryzhov@nfware.com> | 2022-01-23 20:22:42 +0300 | 
|---|---|---|
| committer | Igor Ryzhov <iryzhov@nfware.com> | 2022-02-01 18:20:30 +0300 | 
| commit | 0ef6eacc95c82014c04f13be3b641ff3983040ca (patch) | |
| tree | ffbd3d6951c5baa1a955beba32891a610b5ceb1a | |
| parent | 7a90d91586290d872c05960427df2d3f031cc5e5 (diff) | |
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 <iryzhov@nfware.com>
| -rw-r--r-- | zebra/rib.h | 2 | ||||
| -rw-r--r-- | zebra/zebra_rib.c | 53 | ||||
| -rw-r--r-- | 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);  | 
