diff options
| author | Donald Sharp <donaldsharp72@gmail.com> | 2023-09-05 14:03:00 -0400 | 
|---|---|---|
| committer | GitHub <noreply@github.com> | 2023-09-05 14:03:00 -0400 | 
| commit | 9c36b56d4cf72f665785a7761d7c2ece3358ee59 (patch) | |
| tree | 0bc4ce98e3ceefb7c6a60c4f5ac57db516070af1 | |
| parent | 889b21788458ba0099fb74820abbd8d9eba95f5d (diff) | |
| parent | 56903202ce54c935d3940e20c3dd36d967fb49a7 (diff) | |
Merge pull request #14352 from FRRouting/mergify/bp/stable/9.0/pr-14318
zebra: Fix zebra crash when replacing NHE during shutdown (backport #14318)
| -rw-r--r-- | zebra/rib.h | 4 | ||||
| -rw-r--r-- | zebra/zebra_nhg.c | 38 | ||||
| -rw-r--r-- | zebra/zebra_rib.c | 26 | 
3 files changed, 48 insertions, 20 deletions
diff --git a/zebra/rib.h b/zebra/rib.h index 65cc1ffab9..77d06762bc 100644 --- a/zebra/rib.h +++ b/zebra/rib.h @@ -329,8 +329,8 @@ int route_entry_update_nhe(struct route_entry *re,  			   struct nhg_hash_entry *new_nhghe);  /* NHG replace has happend, we have to update route_entry pointers to new one */ -void rib_handle_nhg_replace(struct nhg_hash_entry *old_entry, -			    struct nhg_hash_entry *new_entry); +int rib_handle_nhg_replace(struct nhg_hash_entry *old_entry, +			   struct nhg_hash_entry *new_entry);  #define route_entry_dump(prefix, src, re) _route_entry_dump(__func__, prefix, src, re)  extern void _route_entry_dump(const char *func, union prefixconstptr pp, diff --git a/zebra/zebra_nhg.c b/zebra/zebra_nhg.c index 717a7a7b48..2b78b828af 100644 --- a/zebra/zebra_nhg.c +++ b/zebra/zebra_nhg.c @@ -3410,6 +3410,7 @@ struct nhg_hash_entry *zebra_nhg_proto_add(uint32_t id, int type,  	struct nhg_connected *rb_node_dep = NULL;  	struct nexthop *newhop;  	bool replace = false; +	int ret = 0;  	if (!nhg->nexthop) {  		if (IS_ZEBRA_DEBUG_NHG) @@ -3507,22 +3508,31 @@ struct nhg_hash_entry *zebra_nhg_proto_add(uint32_t id, int type,  		if (CHECK_FLAG(old->flags, NEXTHOP_GROUP_PROTO_RELEASED))  			zebra_nhg_increment_ref(old); -		rib_handle_nhg_replace(old, new); +		ret = rib_handle_nhg_replace(old, new); +		if (ret) +			/* +			 * if ret > 0, some previous re->nhe has freed the +			 * address to which old_entry is pointing. Hence mark +			 * the old NHE as NULL +			 */ +			old = NULL; +		else { +			/* We have to decrement its singletons +			 * because some might not exist in NEW. +			 */ +			if (!zebra_nhg_depends_is_empty(old)) { +				frr_each (nhg_connected_tree, &old->nhg_depends, +					  rb_node_dep) +					zebra_nhg_decrement_ref( +						rb_node_dep->nhe); +			} -		/* We have to decrement its singletons -		 * because some might not exist in NEW. -		 */ -		if (!zebra_nhg_depends_is_empty(old)) { -			frr_each (nhg_connected_tree, &old->nhg_depends, -				  rb_node_dep) -				zebra_nhg_decrement_ref(rb_node_dep->nhe); +			/* Dont call the dec API, we dont want to uninstall the ID */ +			old->refcnt = 0; +			EVENT_OFF(old->timer); +			zebra_nhg_free(old); +			old = NULL;  		} - -		/* Dont call the dec API, we dont want to uninstall the ID */ -		old->refcnt = 0; -		EVENT_OFF(old->timer); -		zebra_nhg_free(old); -		old = NULL;  	}  	if (IS_ZEBRA_DEBUG_NHG_DETAIL) diff --git a/zebra/zebra_rib.c b/zebra/zebra_rib.c index b531eb7c23..ad6bcc356c 100644 --- a/zebra/zebra_rib.c +++ b/zebra/zebra_rib.c @@ -428,18 +428,29 @@ int route_entry_update_nhe(struct route_entry *re,  done:  	/* Detach / deref previous nhg */ -	if (old_nhg) + +	if (old_nhg) { +		/* +		 * Return true if we are deleting the previous NHE +		 * Note: we dont check the return value of the function anywhere +		 * except at rib_handle_nhg_replace(). +		 */ +		if (old_nhg->refcnt == 1) +			ret = 1; +  		zebra_nhg_decrement_ref(old_nhg); +	}  	return ret;  } -void rib_handle_nhg_replace(struct nhg_hash_entry *old_entry, -			    struct nhg_hash_entry *new_entry) +int rib_handle_nhg_replace(struct nhg_hash_entry *old_entry, +			   struct nhg_hash_entry *new_entry)  {  	struct zebra_router_table *zrt;  	struct route_node *rn;  	struct route_entry *re, *next; +	int ret = 0;  	if (IS_ZEBRA_DEBUG_RIB_DETAILED || IS_ZEBRA_DEBUG_NHG_DETAIL)  		zlog_debug("%s: replacing routes nhe (%u) OLD %p NEW %p", @@ -451,10 +462,17 @@ void rib_handle_nhg_replace(struct nhg_hash_entry *old_entry,  		     rn = srcdest_route_next(rn)) {  			RNODE_FOREACH_RE_SAFE (rn, re, next) {  				if (re->nhe && re->nhe == old_entry) -					route_entry_update_nhe(re, new_entry); +					ret += route_entry_update_nhe(re, +								      new_entry);  			}  		}  	} + +	/* +	 * if ret > 0, some previous re->nhe has freed the address to which +	 * old_entry is pointing. +	 */ +	return ret;  }  struct route_entry *rib_match(afi_t afi, safi_t safi, vrf_id_t vrf_id,  | 
