diff options
| -rw-r--r-- | lib/nexthop.c | 22 | ||||
| -rw-r--r-- | lib/nexthop.h | 3 | ||||
| -rw-r--r-- | lib/routemap.c | 130 | ||||
| -rw-r--r-- | sharpd/sharp_zebra.c | 6 | ||||
| -rw-r--r-- | staticd/static_zebra.c | 1 | ||||
| -rw-r--r-- | tests/topotests/zebra_rib/r1/v4_route_1_static_override.json | 2 | ||||
| -rw-r--r-- | zebra/rib.h | 18 | ||||
| -rw-r--r-- | zebra/zapi_msg.c | 2 | ||||
| -rw-r--r-- | zebra/zebra_dplane.c | 69 | ||||
| -rw-r--r-- | zebra/zebra_dplane.h | 14 | ||||
| -rw-r--r-- | zebra/zebra_mpls.c | 504 | ||||
| -rw-r--r-- | zebra/zebra_mpls.h | 13 | ||||
| -rw-r--r-- | zebra/zebra_nhg.c | 68 | ||||
| -rw-r--r-- | zebra/zebra_pw.c | 4 | ||||
| -rw-r--r-- | zebra/zebra_rib.c | 282 | ||||
| -rw-r--r-- | zebra/zebra_rnh.c | 156 | ||||
| -rw-r--r-- | zebra/zebra_vty.c | 418 | 
17 files changed, 1018 insertions, 694 deletions
diff --git a/lib/nexthop.c b/lib/nexthop.c index 0d239e091b..3496081d47 100644 --- a/lib/nexthop.c +++ b/lib/nexthop.c @@ -187,35 +187,41 @@ int nexthop_cmp(const struct nexthop *next1, const struct nexthop *next2)  	return ret;  } -int nexthop_same_firsthop(struct nexthop *next1, struct nexthop *next2) +bool nexthop_same_firsthop(const struct nexthop *next1, +			   const struct nexthop *next2)  { +	/* Map the TYPE_IPx types to TYPE_IPx_IFINDEX */  	int type1 = NEXTHOP_FIRSTHOPTYPE(next1->type);  	int type2 = NEXTHOP_FIRSTHOPTYPE(next2->type);  	if (type1 != type2) -		return 0; +		return false; + +	if (next1->vrf_id != next2->vrf_id) +		return false; +  	switch (type1) {  	case NEXTHOP_TYPE_IPV4_IFINDEX:  		if (!IPV4_ADDR_SAME(&next1->gate.ipv4, &next2->gate.ipv4)) -			return 0; +			return false;  		if (next1->ifindex != next2->ifindex) -			return 0; +			return false;  		break;  	case NEXTHOP_TYPE_IFINDEX:  		if (next1->ifindex != next2->ifindex) -			return 0; +			return false;  		break;  	case NEXTHOP_TYPE_IPV6_IFINDEX:  		if (!IPV6_ADDR_SAME(&next1->gate.ipv6, &next2->gate.ipv6)) -			return 0; +			return false;  		if (next1->ifindex != next2->ifindex) -			return 0; +			return false;  		break;  	default:  		/* do nothing */  		break;  	} -	return 1; +	return true;  }  /* diff --git a/lib/nexthop.h b/lib/nexthop.h index 9b71262589..eda88efc08 100644 --- a/lib/nexthop.h +++ b/lib/nexthop.h @@ -208,7 +208,8 @@ extern int nexthop_g_addr_cmp(enum nexthop_types_t type,  extern const char *nexthop_type_to_str(enum nexthop_types_t nh_type);  extern bool nexthop_labels_match(const struct nexthop *nh1,  				 const struct nexthop *nh2); -extern int nexthop_same_firsthop(struct nexthop *next1, struct nexthop *next2); +extern bool nexthop_same_firsthop(const struct nexthop *next1, +				  const struct nexthop *next2);  extern const char *nexthop2str(const struct nexthop *nexthop,  			       char *str, int size); diff --git a/lib/routemap.c b/lib/routemap.c index 3b45133450..7749ea4cc7 100644 --- a/lib/routemap.c +++ b/lib/routemap.c @@ -47,18 +47,12 @@ DEFINE_QOBJ_TYPE(route_map)  #define IPv4_PREFIX_LIST "ip address prefix-list"  #define IPv6_PREFIX_LIST "ipv6 address prefix-list" -#define IPv4_MATCH_RULE "ip " -#define IPv6_MATCH_RULE "ipv6 "  #define IS_RULE_IPv4_PREFIX_LIST(S)                                            \  	(strncmp(S, IPv4_PREFIX_LIST, strlen(IPv4_PREFIX_LIST)) == 0)  #define IS_RULE_IPv6_PREFIX_LIST(S)                                            \  	(strncmp(S, IPv6_PREFIX_LIST, strlen(IPv6_PREFIX_LIST)) == 0) -#define IS_IPv4_RULE(S)                                                        \ -	(strncmp(S, IPv4_MATCH_RULE, strlen(IPv4_MATCH_RULE)) == 0) -#define IS_IPv6_RULE(S)                                                        \ -	(strncmp(S, IPv6_MATCH_RULE, strlen(IPv6_MATCH_RULE)) == 0)  struct route_map_pentry_dep {  	struct prefix_list_entry *pentry;  	const char *plist_name; @@ -86,8 +80,6 @@ static void route_map_del_plist_entries(afi_t afi,  					struct route_map_index *index,  					const char *plist_name,  					struct prefix_list_entry *entry); -static bool route_map_is_ip_rule_present(struct route_map_index *index); -static bool route_map_is_ipv6_rule_present(struct route_map_index *index);  static struct hash *route_map_get_dep_hash(route_map_event_t event); @@ -1370,26 +1362,6 @@ enum rmap_compile_rets route_map_add_match(struct route_map_index *index,  	} else if (IS_RULE_IPv6_PREFIX_LIST(match_name)) {  		route_map_pfx_tbl_update(RMAP_EVENT_PLIST_ADDED, index, AFI_IP6,  					 match_arg); -	} else { -		/* If IPv4 match criteria has been added to the route-map -		 * index, check for IPv6 prefix-list match rule presence and -		 * remove this index from the trie node created for each of the -		 * prefix-entry within the prefix-list. If no IPv6 prefix-list -		 * match rule is present, remove this index from the IPv6 -		 * default route's trie node. -		 */ -		if (IS_IPv4_RULE(match_name)) -			route_map_del_plist_entries(AFI_IP6, index, NULL, NULL); - -		/* If IPv6 match criteria has been added to the route-map -		 * index, check for IPv4 prefix-list match rule presence and -		 * remove this index from the trie node created for each of the -		 * prefix-entry within the prefix-list. If no IPv4 prefix-list -		 * match rule is present, remove this index from the IPv4 -		 * default route's trie node. -		 */ -		else if (IS_IPv6_RULE(match_name)) -			route_map_del_plist_entries(AFI_IP, index, NULL, NULL);  	}  	/* Execute event hook. */ @@ -1441,7 +1413,7 @@ enum rmap_compile_rets route_map_delete_match(struct route_map_index *index,  			route_map_rule_delete(&index->match_list, rule);  			/* If IPv4 or IPv6 prefix-list match criteria -			 * has been delete to the route-map index, update +			 * has been delete from the route-map index, update  			 * the route-map's prefix table.  			 */  			if (IS_RULE_IPv4_PREFIX_LIST(match_name)) { @@ -1452,30 +1424,6 @@ enum rmap_compile_rets route_map_delete_match(struct route_map_index *index,  				route_map_pfx_tbl_update(  					RMAP_EVENT_PLIST_DELETED, index,  					AFI_IP6, match_arg); -			} else { -				/* If no more IPv4 match rules are present in -				 * this index, check for IPv6 prefix-list match -				 * rule presence and add this index to trie node -				 * created for each of the prefix-entry within -				 * the prefix-list. If no IPv6 prefix-list match -				 * rule is present, add this index to the IPv6 -				 * default route's trie node. -				 */ -				if (!route_map_is_ip_rule_present(index)) -					route_map_add_plist_entries( -						AFI_IP6, index, NULL, NULL); - -				/* If no more IPv6 match rules are present in -				 * this index, check for IPv4 prefix-list match -				 * rule presence and add this index to trie node -				 * created for each of the prefix-entry within -				 * the prefix-list. If no IPv6 prefix-list match -				 * rule is present, add this index to the IPv4 -				 * default route's trie node. -				 */ -				if (!route_map_is_ipv6_rule_present(index)) -					route_map_add_plist_entries( -						AFI_IP, index, NULL, NULL);  			}  			return RMAP_COMPILE_SUCCESS; @@ -1921,33 +1869,34 @@ static void route_map_pfx_table_del(struct route_table *table,  	route_unlock_node(rn);  } -/* This function checks for the presence of an IPv4 match rule - * in the given route-map index. +/* This function checks for the presence of an IPv4 prefix-list + * match rule in the given route-map index.   */ -static bool route_map_is_ip_rule_present(struct route_map_index *index) +static bool route_map_is_ip_pfx_list_rule_present(struct route_map_index *index)  {  	struct route_map_rule_list *match_list = NULL;  	struct route_map_rule *rule = NULL;  	match_list = &index->match_list;  	for (rule = match_list->head; rule; rule = rule->next) -		if (IS_IPv4_RULE(rule->cmd->str)) +		if (IS_RULE_IPv4_PREFIX_LIST(rule->cmd->str))  			return true;  	return false;  } -/* This function checks for the presence of an IPv6 match rule - * in the given route-map index. +/* This function checks for the presence of an IPv6 prefix-list + * match rule in the given route-map index.   */ -static bool route_map_is_ipv6_rule_present(struct route_map_index *index) +static bool +route_map_is_ipv6_pfx_list_rule_present(struct route_map_index *index)  {  	struct route_map_rule_list *match_list = NULL;  	struct route_map_rule *rule = NULL;  	match_list = &index->match_list;  	for (rule = match_list->head; rule; rule = rule->next) -		if (IS_IPv6_RULE(rule->cmd->str)) +		if (IS_RULE_IPv6_PREFIX_LIST(rule->cmd->str))  			return true;  	return false; @@ -2118,7 +2067,7 @@ static void route_map_trie_update(afi_t afi, route_map_event_t event,  {  	if (event == RMAP_EVENT_PLIST_ADDED) {  		if (afi == AFI_IP) { -			if (!route_map_is_ipv6_rule_present(index)) { +			if (!route_map_is_ipv6_pfx_list_rule_present(index)) {  				route_map_pfx_table_del_default(AFI_IP6, index);  				route_map_add_plist_entries(afi, index,  							    plist_name, NULL); @@ -2127,7 +2076,7 @@ static void route_map_trie_update(afi_t afi, route_map_event_t event,  							    NULL, NULL);  			}  		} else { -			if (!route_map_is_ip_rule_present(index)) { +			if (!route_map_is_ip_pfx_list_rule_present(index)) {  				route_map_pfx_table_del_default(AFI_IP, index);  				route_map_add_plist_entries(afi, index,  							    plist_name, NULL); @@ -2141,22 +2090,36 @@ static void route_map_trie_update(afi_t afi, route_map_event_t event,  			route_map_del_plist_entries(afi, index, plist_name,  						    NULL); -			if (!route_map_is_ipv6_rule_present(index)) +			/* If IPv6 prefix-list match rule is not present, +			 * add this index to the IPv4 default route's trie +			 * node. +			 * Also, add this index to the trie nodes created +			 * for each of the prefix-entries within the IPv6 +			 * prefix-list, if the IPv6 prefix-list match rule +			 * is present. Else, add this index to the IPv6 +			 * default route's trie node. +			 */ +			if (!route_map_is_ipv6_pfx_list_rule_present(index))  				route_map_pfx_table_add_default(afi, index); -			if (!route_map_is_ip_rule_present(index)) -				route_map_add_plist_entries(AFI_IP6, index, -							    NULL, NULL); +			route_map_add_plist_entries(AFI_IP6, index, NULL, NULL);  		} else {  			route_map_del_plist_entries(afi, index, plist_name,  						    NULL); -			if (!route_map_is_ip_rule_present(index)) +			/* If IPv4 prefix-list match rule is not present, +			 * add this index to the IPv6 default route's trie +			 * node. +			 * Also, add this index to the trie nodes created +			 * for each of the prefix-entries within the IPv4 +			 * prefix-list, if the IPv4 prefix-list match rule +			 * is present. Else, add this index to the IPv4 +			 * default route's trie node. +			 */ +			if (!route_map_is_ip_pfx_list_rule_present(index))  				route_map_pfx_table_add_default(afi, index); -			if (!route_map_is_ipv6_rule_present(index)) -				route_map_add_plist_entries(AFI_IP, index, NULL, -							    NULL); +			route_map_add_plist_entries(AFI_IP, index, NULL, NULL);  		}  	}  } @@ -2229,30 +2192,27 @@ static void route_map_pentry_update(route_map_event_t event,  	}  	if (event == RMAP_EVENT_PLIST_ADDED) { -		if (plist->count == 1) { -			if (afi == AFI_IP) { -				if (!route_map_is_ipv6_rule_present(index)) -					route_map_add_plist_entries( -						afi, index, plist_name, pentry); -			} else { -				if (!route_map_is_ip_rule_present(index)) -					route_map_add_plist_entries( -						afi, index, plist_name, pentry); -			} +		if (afi == AFI_IP) { +			if (!route_map_is_ipv6_pfx_list_rule_present(index)) +				route_map_add_plist_entries(afi, index, +							    plist_name, pentry);  		} else { -			route_map_add_plist_entries(afi, index, plist_name, -						    pentry); +			if (!route_map_is_ip_pfx_list_rule_present(index)) +				route_map_add_plist_entries(afi, index, +							    plist_name, pentry);  		}  	} else if (event == RMAP_EVENT_PLIST_DELETED) {  		route_map_del_plist_entries(afi, index, plist_name, pentry);  		if (plist->count == 1) {  			if (afi == AFI_IP) { -				if (!route_map_is_ipv6_rule_present(index)) +				if (!route_map_is_ipv6_pfx_list_rule_present( +					    index))  					route_map_pfx_table_add_default(afi,  									index);  			} else { -				if (!route_map_is_ip_rule_present(index)) +				if (!route_map_is_ip_pfx_list_rule_present( +					    index))  					route_map_pfx_table_add_default(afi,  									index);  			} diff --git a/sharpd/sharp_zebra.c b/sharpd/sharp_zebra.c index c47f2105cb..7ab2d6ec22 100644 --- a/sharpd/sharp_zebra.c +++ b/sharpd/sharp_zebra.c @@ -435,6 +435,12 @@ static int sharp_debug_nexthops(struct zapi_route *api)  	int i;  	char buf[PREFIX_STRLEN]; +	if (api->nexthop_num == 0) { +		zlog_debug( +			"        Not installed"); +		return 0; +	} +  	for (i = 0; i < api->nexthop_num; i++) {  		struct zapi_nexthop *znh = &api->nexthops[i]; diff --git a/staticd/static_zebra.c b/staticd/static_zebra.c index 5cadf34365..c42f632ffb 100644 --- a/staticd/static_zebra.c +++ b/staticd/static_zebra.c @@ -363,6 +363,7 @@ extern void static_zebra_route_add(struct route_node *rn,  		memcpy(&api.src_prefix, src_pp, sizeof(api.src_prefix));  	}  	SET_FLAG(api.flags, ZEBRA_FLAG_RR_USE_DISTANCE); +	SET_FLAG(api.flags, ZEBRA_FLAG_ALLOW_RECURSION);  	SET_FLAG(api.message, ZAPI_MESSAGE_NEXTHOP);  	if (si_changed->distance) {  		SET_FLAG(api.message, ZAPI_MESSAGE_DISTANCE); diff --git a/tests/topotests/zebra_rib/r1/v4_route_1_static_override.json b/tests/topotests/zebra_rib/r1/v4_route_1_static_override.json index aa9522aff6..22e199f9aa 100644 --- a/tests/topotests/zebra_rib/r1/v4_route_1_static_override.json +++ b/tests/topotests/zebra_rib/r1/v4_route_1_static_override.json @@ -10,7 +10,7 @@        "installed":true,        "table":254,        "internalStatus":16, -      "internalFlags":72, +      "internalFlags":73,        "internalNextHopNum":1,        "internalNextHopActiveNum":1,        "nexthops":[ diff --git a/zebra/rib.h b/zebra/rib.h index a024b6dfaa..ec992974fa 100644 --- a/zebra/rib.h +++ b/zebra/rib.h @@ -94,9 +94,11 @@ struct route_entry {  	struct nhg_hash_entry *nhe;  	/* Nexthop group from FIB (optional), reflecting what is actually -	 * installed in the FIB if that differs. +	 * installed in the FIB if that differs. The 'backup' group is used +	 * when backup nexthops are present in the route's nhg.  	 */  	struct nexthop_group fib_ng; +	struct nexthop_group fib_backup_ng;  	/* Nexthop group hash entry ID */  	uint32_t nhe_id; @@ -526,7 +528,7 @@ DECLARE_HOOK(rib_update, (struct route_node * rn, const char *reason),  /*   * Access active nexthop-group, either RIB or FIB version   */ -static inline struct nexthop_group *rib_active_nhg(struct route_entry *re) +static inline struct nexthop_group *rib_get_fib_nhg(struct route_entry *re)  {  	if (re->fib_ng.nexthop)  		return &(re->fib_ng); @@ -534,6 +536,18 @@ static inline struct nexthop_group *rib_active_nhg(struct route_entry *re)  		return &(re->nhe->nhg);  } +/* + * Access active nexthop-group, either RIB or FIB version + */ +static inline struct nexthop_group *rib_get_fib_backup_nhg( +	struct route_entry *re) +{ +	if (re->fib_backup_ng.nexthop) +		return &(re->fib_backup_ng); +	else +		return zebra_nhg_get_backup_nhg(re->nhe); +} +  extern void zebra_vty_init(void);  extern pid_t pid; diff --git a/zebra/zapi_msg.c b/zebra/zapi_msg.c index a40aa8b643..dc7c595d26 100644 --- a/zebra/zapi_msg.c +++ b/zebra/zapi_msg.c @@ -2485,7 +2485,7 @@ static void zread_vrf_label(ZAPI_HANDLER_ARGS)  		if (really_remove)  			mpls_lsp_uninstall(def_zvrf, ltype, zvrf->label[afi],  					   NEXTHOP_TYPE_IFINDEX, NULL, -					   ifp->ifindex); +					   ifp->ifindex, false /*backup*/);  	}  	if (nlabel != MPLS_LABEL_NONE) { diff --git a/zebra/zebra_dplane.c b/zebra/zebra_dplane.c index 64383fc81c..e34b6f23ff 100644 --- a/zebra/zebra_dplane.c +++ b/zebra/zebra_dplane.c @@ -1149,6 +1149,37 @@ void dplane_ctx_set_nexthops(struct zebra_dplane_ctx *ctx, struct nexthop *nh)  	nexthop_group_copy_nh_sorted(&(ctx->u.rinfo.zd_ng), nh);  } +/* + * Set the list of backup nexthops; their ordering is preserved (they're not + * re-sorted.) + */ +void dplane_ctx_set_backup_nhg(struct zebra_dplane_ctx *ctx, +			       const struct nexthop_group *nhg) +{ +	struct nexthop *nh, *last_nh, *nexthop; + +	DPLANE_CTX_VALID(ctx); + +	if (ctx->u.rinfo.backup_ng.nexthop) { +		nexthops_free(ctx->u.rinfo.backup_ng.nexthop); +		ctx->u.rinfo.backup_ng.nexthop = NULL; +	} + +	last_nh = NULL; + +	/* Be careful to preserve the order of the backup list */ +	for (nh = nhg->nexthop; nh; nh = nh->next) { +		nexthop = nexthop_dup(nh, NULL); + +		if (last_nh) +			NEXTHOP_APPEND(last_nh, nexthop); +		else +			ctx->u.rinfo.backup_ng.nexthop = nexthop; + +		last_nh = nexthop; +	} +} +  uint32_t dplane_ctx_get_nhg_id(const struct zebra_dplane_ctx *ctx)  {  	DPLANE_CTX_VALID(ctx); @@ -1303,7 +1334,7 @@ const struct nhlfe_list_head *dplane_ctx_get_backup_nhlfe_list(  zebra_nhlfe_t *dplane_ctx_add_nhlfe(struct zebra_dplane_ctx *ctx,  				    enum lsp_types_t lsp_type,  				    enum nexthop_types_t nh_type, -				    union g_addr *gate, +				    const union g_addr *gate,  				    ifindex_t ifindex,  				    uint8_t num_labels,  				    mpls_label_t *out_labels) @@ -1322,7 +1353,7 @@ zebra_nhlfe_t *dplane_ctx_add_nhlfe(struct zebra_dplane_ctx *ctx,  zebra_nhlfe_t *dplane_ctx_add_backup_nhlfe(struct zebra_dplane_ctx *ctx,  					   enum lsp_types_t lsp_type,  					   enum nexthop_types_t nh_type, -					   union g_addr *gate, +					   const union g_addr *gate,  					   ifindex_t ifindex,  					   uint8_t num_labels,  					   mpls_label_t *out_labels) @@ -1921,18 +1952,12 @@ done:  /*   * Capture information for an LSP update in a dplane context.   */ -static int dplane_ctx_lsp_init(struct zebra_dplane_ctx *ctx, -			       enum dplane_op_e op, -			       zebra_lsp_t *lsp) +int dplane_ctx_lsp_init(struct zebra_dplane_ctx *ctx, enum dplane_op_e op, +			zebra_lsp_t *lsp)  {  	int ret = AOK;  	zebra_nhlfe_t *nhlfe, *new_nhlfe; -	if (IS_ZEBRA_DEBUG_DPLANE_DETAIL) -		zlog_debug("init dplane ctx %s: in-label %u ecmp# %d", -			   dplane_op2str(op), lsp->ile.in_label, -			   lsp->num_ecmp); -  	ctx->zd_op = op;  	ctx->zd_status = ZEBRA_DPLANE_REQUEST_SUCCESS; @@ -1944,6 +1969,20 @@ static int dplane_ctx_lsp_init(struct zebra_dplane_ctx *ctx,  	nhlfe_list_init(&(ctx->u.lsp.nhlfe_list));  	nhlfe_list_init(&(ctx->u.lsp.backup_nhlfe_list)); + +	/* This may be called to create/init a dplane context, not necessarily +	 * to copy an lsp object. +	 */ +	if (lsp == NULL) { +		ret = AOK; +		goto done; +	} + +	if (IS_ZEBRA_DEBUG_DPLANE_DETAIL) +		zlog_debug("init dplane ctx %s: in-label %u ecmp# %d", +			   dplane_op2str(op), lsp->ile.in_label, +			   lsp->num_ecmp); +  	ctx->u.lsp.ile = lsp->ile;  	ctx->u.lsp.addr_family = lsp->addr_family;  	ctx->u.lsp.num_ecmp = lsp->num_ecmp; @@ -2012,6 +2051,7 @@ static int dplane_ctx_pw_init(struct zebra_dplane_ctx *ctx,  	struct route_table *table;  	struct route_node *rn;  	struct route_entry *re; +	const struct nexthop_group *nhg;  	if (IS_ZEBRA_DEBUG_DPLANE_DETAIL)  		zlog_debug("init dplane ctx %s: pw '%s', loc %u, rem %u", @@ -2062,10 +2102,11 @@ static int dplane_ctx_pw_init(struct zebra_dplane_ctx *ctx,  					break;  			} -			if (re) +			if (re) { +				nhg = rib_get_fib_nhg(re);  				copy_nexthops(&(ctx->u.pw.nhg.nexthop), -					      re->nhe->nhg.nexthop, NULL); - +					      nhg->nexthop, NULL); +			}  			route_unlock_node(rn);  		}  	} @@ -2442,7 +2483,7 @@ dplane_route_notif_update(struct route_node *rn,  		new_ctx->u.rinfo.zd_ng.nexthop = NULL;  		copy_nexthops(&(new_ctx->u.rinfo.zd_ng.nexthop), -			      (rib_active_nhg(re))->nexthop, NULL); +			      (rib_get_fib_nhg(re))->nexthop, NULL);  		for (ALL_NEXTHOPS(new_ctx->u.rinfo.zd_ng, nexthop))  			UNSET_FLAG(nexthop->flags, NEXTHOP_FLAG_FIB); diff --git a/zebra/zebra_dplane.h b/zebra/zebra_dplane.h index 9e07231fea..8e873886df 100644 --- a/zebra/zebra_dplane.h +++ b/zebra/zebra_dplane.h @@ -283,6 +283,8 @@ void dplane_ctx_set_distance(struct zebra_dplane_ctx *ctx, uint8_t distance);  uint8_t dplane_ctx_get_old_distance(const struct zebra_dplane_ctx *ctx);  void dplane_ctx_set_nexthops(struct zebra_dplane_ctx *ctx, struct nexthop *nh); +void dplane_ctx_set_backup_nhg(struct zebra_dplane_ctx *ctx, +			       const struct nexthop_group *nhg);  uint32_t dplane_ctx_get_nhg_id(const struct zebra_dplane_ctx *ctx);  const struct nexthop_group *dplane_ctx_get_ng( @@ -308,6 +310,14 @@ dplane_ctx_get_nhe_nh_grp(const struct zebra_dplane_ctx *ctx);  uint8_t dplane_ctx_get_nhe_nh_grp_count(const struct zebra_dplane_ctx *ctx);  /* Accessors for LSP information */ + +/* Init the internal LSP data struct - necessary before adding to it. + * If 'lsp' is non-NULL, info will be copied from it to the internal + * context data area. + */ +int dplane_ctx_lsp_init(struct zebra_dplane_ctx *ctx, enum dplane_op_e op, +			zebra_lsp_t *lsp); +  mpls_label_t dplane_ctx_get_in_label(const struct zebra_dplane_ctx *ctx);  void dplane_ctx_set_in_label(struct zebra_dplane_ctx *ctx,  			     mpls_label_t label); @@ -325,7 +335,7 @@ const struct nhlfe_list_head *dplane_ctx_get_backup_nhlfe_list(  zebra_nhlfe_t *dplane_ctx_add_nhlfe(struct zebra_dplane_ctx *ctx,  				    enum lsp_types_t lsp_type,  				    enum nexthop_types_t nh_type, -				    union g_addr *gate, +				    const union g_addr *gate,  				    ifindex_t ifindex,  				    uint8_t num_labels,  				    mpls_label_t *out_labels); @@ -333,7 +343,7 @@ zebra_nhlfe_t *dplane_ctx_add_nhlfe(struct zebra_dplane_ctx *ctx,  zebra_nhlfe_t *dplane_ctx_add_backup_nhlfe(struct zebra_dplane_ctx *ctx,  					   enum lsp_types_t lsp_type,  					   enum nexthop_types_t nh_type, -					   union g_addr *gate, +					   const union g_addr *gate,  					   ifindex_t ifindex,  					   uint8_t num_labels,  					   mpls_label_t *out_labels); diff --git a/zebra/zebra_mpls.c b/zebra/zebra_mpls.c index 8ee8601689..e741268ebb 100644 --- a/zebra/zebra_mpls.c +++ b/zebra/zebra_mpls.c @@ -110,17 +110,14 @@ static zebra_nhlfe_t *nhlfe_find(struct nhlfe_list_head *list,  static zebra_nhlfe_t *nhlfe_add(zebra_lsp_t *lsp, enum lsp_types_t lsp_type,  				enum nexthop_types_t gtype,  				const union g_addr *gate, ifindex_t ifindex, -				uint8_t num_labels, const mpls_label_t *labels); +				uint8_t num_labels, const mpls_label_t *labels, +				bool is_backup);  static int nhlfe_del(zebra_nhlfe_t *nhlfe);  static void nhlfe_free(zebra_nhlfe_t *nhlfe);  static void nhlfe_out_label_update(zebra_nhlfe_t *nhlfe,  				   struct mpls_label_stack *nh_label);  static int mpls_lsp_uninstall_all(struct hash *lsp_table, zebra_lsp_t *lsp,  				  enum lsp_types_t type); -static int lsp_backup_uninstall(struct zebra_vrf *zvrf, enum lsp_types_t type, -				mpls_label_t in_label, -				enum nexthop_types_t gtype, -				const union g_addr *gate, ifindex_t ifindex);  static int mpls_static_lsp_uninstall_all(struct zebra_vrf *zvrf,  					 mpls_label_t in_label);  static void nhlfe_print(zebra_nhlfe_t *nhlfe, struct vty *vty); @@ -167,6 +164,15 @@ static void clear_nhlfe_installed(zebra_lsp_t *lsp)  		UNSET_FLAG(nhlfe->flags, NHLFE_FLAG_INSTALLED);  		UNSET_FLAG(nexthop->flags, NEXTHOP_FLAG_FIB);  	} + +	frr_each_safe(nhlfe_list, &lsp->backup_nhlfe_list, nhlfe) { +		nexthop = nhlfe->nexthop; +		if (!nexthop) +			continue; + +		UNSET_FLAG(nhlfe->flags, NHLFE_FLAG_INSTALLED); +		UNSET_FLAG(nexthop->flags, NEXTHOP_FLAG_FIB); +	}  }  /* @@ -240,7 +246,8 @@ static int lsp_install(struct zebra_vrf *zvrf, mpls_label_t label,  			nhlfe = nhlfe_add(lsp, lsp_type, nexthop->type,  					  &nexthop->gate, nexthop->ifindex,  					  nexthop->nh_label->num_labels, -					  nexthop->nh_label->label); +					  nexthop->nh_label->label, +					  false /*backup*/);  			if (!nhlfe)  				return -1; @@ -797,8 +804,7 @@ static void lsp_select_best_nhlfe(zebra_lsp_t *lsp)  	/*  	 * First compute the best path, after checking nexthop status. We are -	 * only -	 * concerned with non-deleted NHLFEs. +	 * only concerned with non-deleted NHLFEs.  	 */  	frr_each_safe(nhlfe_list, &lsp->nhlfe_list, nhlfe) {  		/* Clear selection flags. */ @@ -816,6 +822,14 @@ static void lsp_select_best_nhlfe(zebra_lsp_t *lsp)  	if (!lsp->best_nhlfe)  		return; +	/* +	 * Check the active status of backup nhlfes also +	 */ +	frr_each_safe(nhlfe_list, &lsp->backup_nhlfe_list, nhlfe) { +		if (!CHECK_FLAG(nhlfe->flags, NHLFE_FLAG_DELETED)) +			(void)nhlfe_nexthop_active(nhlfe); +	} +  	/* Mark best NHLFE as selected. */  	SET_FLAG(lsp->best_nhlfe->flags, NHLFE_FLAG_SELECTED); @@ -910,9 +924,9 @@ static wq_item_status lsp_process(struct work_queue *wq, void *data)  	if (IS_ZEBRA_DEBUG_MPLS) {  		if (oldbest) -			nhlfe2str(oldbest, buf, BUFSIZ); +			nhlfe2str(oldbest, buf, sizeof(buf));  		if (newbest) -			nhlfe2str(newbest, buf2, BUFSIZ); +			nhlfe2str(newbest, buf2, sizeof(buf2));  		zlog_debug(  			"Process LSP in-label %u oldbest %s newbest %s "  			"flags 0x%x ecmp# %d", @@ -1310,13 +1324,14 @@ static zebra_nhlfe_t *nhlfe_alloc(zebra_lsp_t *lsp, enum lsp_types_t lsp_type,  }  /* - * Add NHLFE. Base entry must have been created and duplicate - * check done. + * Add primary or backup NHLFE. Base entry must have been created and + * duplicate check done.   */  static zebra_nhlfe_t *nhlfe_add(zebra_lsp_t *lsp, enum lsp_types_t lsp_type,  				enum nexthop_types_t gtype,  				const union g_addr *gate, ifindex_t ifindex, -				uint8_t num_labels, const mpls_label_t *labels) +				uint8_t num_labels, const mpls_label_t *labels, +				bool is_backup)  {  	zebra_nhlfe_t *nhlfe; @@ -1327,36 +1342,12 @@ static zebra_nhlfe_t *nhlfe_add(zebra_lsp_t *lsp, enum lsp_types_t lsp_type,  	nhlfe = nhlfe_alloc(lsp, lsp_type, gtype, gate, ifindex, num_labels,  			    labels); -	/* Enqueue to LSP, at head of list. */ -	nhlfe_list_add_head(&lsp->nhlfe_list, nhlfe); - -	return nhlfe; -} - -/* - * Add backup NHLFE. Base entry must have been created and duplicate - * check done. - */ -static zebra_nhlfe_t *nhlfe_backup_add(zebra_lsp_t *lsp, -				       enum lsp_types_t lsp_type, -				       enum nexthop_types_t gtype, -				       const union g_addr *gate, -				       ifindex_t ifindex, uint8_t num_labels, -				       const mpls_label_t *labels) -{ -	zebra_nhlfe_t *nhlfe; - -	if (!lsp) -		return NULL; - -	/* Allocate new object */ -	nhlfe = nhlfe_alloc(lsp, lsp_type, gtype, gate, ifindex, num_labels, -			    labels); - -	SET_FLAG(nhlfe->flags, NHLFE_FLAG_IS_BACKUP); - -	/* Enqueue to LSP, at tail of list. */ -	nhlfe_list_add_tail(&lsp->backup_nhlfe_list, nhlfe); +	/* Enqueue to LSP: primaries at head of list, backups at tail */ +	if (is_backup) { +		SET_FLAG(nhlfe->flags, NHLFE_FLAG_IS_BACKUP); +		nhlfe_list_add_tail(&lsp->backup_nhlfe_list, nhlfe); +	} else +		nhlfe_list_add_head(&lsp->nhlfe_list, nhlfe);  	return nhlfe;  } @@ -1590,6 +1581,9 @@ static void nhlfe_print(zebra_nhlfe_t *nhlfe, struct vty *vty)  		break;  	}  	vty_out(vty, "%s", +		CHECK_FLAG(nhlfe->flags, NHLFE_FLAG_IS_BACKUP) ? " (backup)" +							       : ""); +	vty_out(vty, "%s",  		CHECK_FLAG(nhlfe->flags, NHLFE_FLAG_INSTALLED) ? " (installed)"  							       : "");  	vty_out(vty, "\n"); @@ -1616,6 +1610,7 @@ static void lsp_print(struct vty *vty, zebra_lsp_t *lsp)  			/* Find backup in backup list */  			i = 0; +			backup = NULL;  			frr_each(nhlfe_list, &lsp->backup_nhlfe_list, backup) {  				if (i == nhlfe->nexthop->backup_idx)  					break; @@ -1933,23 +1928,27 @@ void zebra_mpls_lsp_dplane_result(struct zebra_dplane_ctx *ctx)  		/* TODO -- Confirm that this result is still 'current' */ -		if (status == ZEBRA_DPLANE_REQUEST_SUCCESS) { -			/* Update zebra object */ -			SET_FLAG(lsp->flags, LSP_FLAG_INSTALLED); -			frr_each(nhlfe_list, &lsp->nhlfe_list, nhlfe) { -				nexthop = nhlfe->nexthop; -				if (!nexthop) -					continue; - -				SET_FLAG(nhlfe->flags, NHLFE_FLAG_INSTALLED); -				SET_FLAG(nexthop->flags, NEXTHOP_FLAG_FIB); -			} -		} else { +		if (status != ZEBRA_DPLANE_REQUEST_SUCCESS) {  			UNSET_FLAG(lsp->flags, LSP_FLAG_INSTALLED);  			clear_nhlfe_installed(lsp);  			flog_warn(EC_ZEBRA_LSP_INSTALL_FAILURE,  				  "LSP Install Failure: in-label %u",  				  lsp->ile.in_label); +			break; +		} + +		/* Update zebra object */ +		SET_FLAG(lsp->flags, LSP_FLAG_INSTALLED); +		frr_each(nhlfe_list, &lsp->nhlfe_list, nhlfe) { +			nexthop = nhlfe->nexthop; +			if (!nexthop) +				continue; + +			if (CHECK_FLAG(nhlfe->flags, NHLFE_FLAG_SELECTED) && +			    CHECK_FLAG(nexthop->flags, NEXTHOP_FLAG_ACTIVE)) { +				SET_FLAG(nhlfe->flags, NHLFE_FLAG_INSTALLED); +				SET_FLAG(nexthop->flags, NEXTHOP_FLAG_FIB); +			}  		}  		break; @@ -1970,53 +1969,23 @@ void zebra_mpls_lsp_dplane_result(struct zebra_dplane_ctx *ctx)  }  /* - * Process async dplane notifications. + * Process LSP installation info from two sets of nhlfes: a set from + * a dplane notification, and a set from the zebra LSP object. Update + * counters of installed nexthops, and return whether the LSP has changed.   */ -void zebra_mpls_process_dplane_notify(struct zebra_dplane_ctx *ctx) +static bool compare_notif_nhlfes(const struct nhlfe_list_head *ctx_head, +				 struct nhlfe_list_head *nhlfe_head, +				 int *start_counter, int *end_counter)  { -	struct zebra_vrf *zvrf; -	zebra_ile_t tmp_ile; -	struct hash *lsp_table; -	zebra_lsp_t *lsp;  	zebra_nhlfe_t *nhlfe; -	const struct nhlfe_list_head *head;  	const zebra_nhlfe_t *ctx_nhlfe;  	struct nexthop *nexthop;  	const struct nexthop *ctx_nexthop; -	int start_count = 0, end_count = 0; /* Installed counts */ +	int start_count = 0, end_count = 0;  	bool changed_p = false;  	bool is_debug = (IS_ZEBRA_DEBUG_DPLANE | IS_ZEBRA_DEBUG_MPLS); -	if (is_debug) -		zlog_debug("LSP dplane notif, in-label %u", -			   dplane_ctx_get_in_label(ctx)); - -	/* Look for zebra LSP object */ -	zvrf = vrf_info_lookup(VRF_DEFAULT); -	if (zvrf == NULL) -		goto done; - -	lsp_table = zvrf->lsp_table; - -	tmp_ile.in_label = dplane_ctx_get_in_label(ctx); -	lsp = hash_lookup(lsp_table, &tmp_ile); -	if (lsp == NULL) { -		if (is_debug) -			zlog_debug("dplane LSP notif: in-label %u not found", -				   dplane_ctx_get_in_label(ctx)); -		goto done; -	} - -	/* -	 * The dataplane/forwarding plane is notifying zebra about the state -	 * of the nexthops associated with this LSP. First, we take a -	 * pre-scan pass to determine whether the LSP has transitioned -	 * from installed -> uninstalled. In that case, we need to have -	 * the existing state of the LSP objects available before making -	 * any changes. -	 */ -	head = dplane_ctx_get_nhlfe_list(ctx); -	frr_each_safe(nhlfe_list, &lsp->nhlfe_list, nhlfe) { +	frr_each_safe(nhlfe_list, nhlfe_head, nhlfe) {  		char buf[NEXTHOP_STRLEN];  		nexthop = nhlfe->nexthop; @@ -2026,8 +1995,9 @@ void zebra_mpls_process_dplane_notify(struct zebra_dplane_ctx *ctx)  		if (CHECK_FLAG(nexthop->flags, NEXTHOP_FLAG_FIB))  			start_count++; +		ctx_nhlfe = NULL;  		ctx_nexthop = NULL; -		frr_each(nhlfe_list_const, head, ctx_nhlfe) { +		frr_each(nhlfe_list_const, ctx_head, ctx_nhlfe) {  			ctx_nexthop = ctx_nhlfe->nexthop;  			if (!ctx_nexthop)  				continue; @@ -2085,32 +2055,39 @@ void zebra_mpls_process_dplane_notify(struct zebra_dplane_ctx *ctx)  		}  	} -	if (is_debug) -		zlog_debug("LSP dplane notif: lfib start_count %d, end_count %d%s", -			   start_count, end_count, -			   changed_p ? ", changed" : ""); +	if (start_counter) +		*start_counter += start_count; +	if (end_counter) +		*end_counter += end_count; -	/* -	 * Has the LSP become uninstalled? -	 */ -	if (start_count > 0 && end_count == 0) { -		/* Inform other lfibs */ -		dplane_lsp_notif_update(lsp, DPLANE_OP_LSP_DELETE, ctx); -	} +	return changed_p; +} -	/* -	 * Now we take a second pass and bring the zebra -	 * nexthop state into sync with the forwarding-plane state. -	 */ -	frr_each_safe(nhlfe_list, &lsp->nhlfe_list, nhlfe) { +/* + * Update an lsp nhlfe list from a dplane context, typically an async + * notification context. Update the LSP list to match the installed + * status from the context's list. + */ +static int update_nhlfes_from_ctx(struct nhlfe_list_head *nhlfe_head, +				  const struct nhlfe_list_head *ctx_head) +{ +	int ret = 0; +	zebra_nhlfe_t *nhlfe; +	const zebra_nhlfe_t *ctx_nhlfe; +	struct nexthop *nexthop; +	const struct nexthop *ctx_nexthop; +	bool is_debug = (IS_ZEBRA_DEBUG_DPLANE | IS_ZEBRA_DEBUG_MPLS); + +	frr_each_safe(nhlfe_list, nhlfe_head, nhlfe) {  		char buf[NEXTHOP_STRLEN];  		nexthop = nhlfe->nexthop;  		if (!nexthop)  			continue; +		ctx_nhlfe = NULL;  		ctx_nexthop = NULL; -		frr_each(nhlfe_list_const, head, ctx_nhlfe) { +		frr_each(nhlfe_list_const, ctx_head, ctx_nhlfe) {  			ctx_nexthop = ctx_nhlfe->nexthop;  			if (!ctx_nexthop)  				continue; @@ -2130,10 +2107,16 @@ void zebra_mpls_process_dplane_notify(struct zebra_dplane_ctx *ctx)  			/* Bring zebra nhlfe install state into sync */  			if (CHECK_FLAG(ctx_nhlfe->flags,  				       NHLFE_FLAG_INSTALLED)) { +				if (is_debug) +					zlog_debug("%s: matched lsp nhlfe %s (installed)", +						   __func__, buf);  				SET_FLAG(nhlfe->flags, NHLFE_FLAG_INSTALLED);  			} else { +				if (is_debug) +					zlog_debug("%s: matched lsp nhlfe %s (not installed)", +						   __func__, buf);  				UNSET_FLAG(nhlfe->flags, NHLFE_FLAG_INSTALLED);  			} @@ -2153,13 +2136,101 @@ void zebra_mpls_process_dplane_notify(struct zebra_dplane_ctx *ctx)  		} else {  			/* Not mentioned in lfib set -> uninstalled */ - +			if (is_debug) +				zlog_debug("%s: no match for lsp nhlfe %s", +					   __func__, buf);  			UNSET_FLAG(nhlfe->flags, NHLFE_FLAG_INSTALLED);  			UNSET_FLAG(nexthop->flags, NEXTHOP_FLAG_FIB);  			UNSET_FLAG(nexthop->flags, NEXTHOP_FLAG_ACTIVE);  		}  	} +	return ret; +} + +/* + * Process async dplane notifications. + */ +void zebra_mpls_process_dplane_notify(struct zebra_dplane_ctx *ctx) +{ +	struct zebra_vrf *zvrf; +	zebra_ile_t tmp_ile; +	struct hash *lsp_table; +	zebra_lsp_t *lsp; +	const struct nhlfe_list_head *ctx_list; +	int start_count = 0, end_count = 0; /* Installed counts */ +	bool changed_p = false; +	bool is_debug = (IS_ZEBRA_DEBUG_DPLANE | IS_ZEBRA_DEBUG_MPLS); + +	if (is_debug) +		zlog_debug("LSP dplane notif, in-label %u", +			   dplane_ctx_get_in_label(ctx)); + +	/* Look for zebra LSP object */ +	zvrf = vrf_info_lookup(VRF_DEFAULT); +	if (zvrf == NULL) +		goto done; + +	lsp_table = zvrf->lsp_table; + +	tmp_ile.in_label = dplane_ctx_get_in_label(ctx); +	lsp = hash_lookup(lsp_table, &tmp_ile); +	if (lsp == NULL) { +		if (is_debug) +			zlog_debug("dplane LSP notif: in-label %u not found", +				   dplane_ctx_get_in_label(ctx)); +		goto done; +	} + +	/* +	 * The dataplane/forwarding plane is notifying zebra about the state +	 * of the nexthops associated with this LSP. First, we take a +	 * pre-scan pass to determine whether the LSP has transitioned +	 * from installed -> uninstalled. In that case, we need to have +	 * the existing state of the LSP objects available before making +	 * any changes. +	 */ +	ctx_list = dplane_ctx_get_nhlfe_list(ctx); + +	changed_p = compare_notif_nhlfes(ctx_list, &lsp->nhlfe_list, +					 &start_count, &end_count); + +	if (is_debug) +		zlog_debug("LSP dplane notif: lfib start_count %d, end_count %d%s", +			   start_count, end_count, +			   changed_p ? ", changed" : ""); + +	ctx_list = dplane_ctx_get_backup_nhlfe_list(ctx); + +	if (compare_notif_nhlfes(ctx_list, &lsp->backup_nhlfe_list, +				 &start_count, &end_count)) +		/* Avoid accidentally setting back to 'false' */ +		changed_p = true; + +	if (is_debug) +		zlog_debug("LSP dplane notif: lfib backups, start_count %d, end_count %d%s", +			   start_count, end_count, +			   changed_p ? ", changed" : ""); + +	/* +	 * Has the LSP become uninstalled? We need the existing state of the +	 * nexthops/nhlfes at this point so we know what to delete. +	 */ +	if (start_count > 0 && end_count == 0) { +		/* Inform other lfibs */ +		dplane_lsp_notif_update(lsp, DPLANE_OP_LSP_DELETE, ctx); +	} + +	/* +	 * Now we take a second pass and bring the zebra +	 * nexthop state into sync with the forwarding-plane state. +	 */ +	ctx_list = dplane_ctx_get_nhlfe_list(ctx); +	update_nhlfes_from_ctx(&lsp->nhlfe_list, ctx_list); + +	ctx_list = dplane_ctx_get_backup_nhlfe_list(ctx); +	update_nhlfes_from_ctx(&lsp->backup_nhlfe_list, ctx_list); +  	if (end_count > 0) {  		SET_FLAG(lsp->flags, LSP_FLAG_INSTALLED); @@ -2234,14 +2305,14 @@ int zebra_mpls_lsp_uninstall(struct zebra_vrf *zvrf, struct route_node *rn,  zebra_nhlfe_t *zebra_mpls_lsp_add_nhlfe(zebra_lsp_t *lsp,  					enum lsp_types_t lsp_type,  					enum nexthop_types_t gtype, -					union g_addr *gate, +					const union g_addr *gate,  					ifindex_t ifindex,  					uint8_t num_labels,  					const mpls_label_t *out_labels)  {  	/* Just a public pass-through to the internal implementation */  	return nhlfe_add(lsp, lsp_type, gtype, gate, ifindex, num_labels, -			 out_labels); +			 out_labels, false /*backup*/);  }  /* @@ -2252,14 +2323,14 @@ zebra_nhlfe_t *zebra_mpls_lsp_add_nhlfe(zebra_lsp_t *lsp,  zebra_nhlfe_t *zebra_mpls_lsp_add_backup_nhlfe(zebra_lsp_t *lsp,  					       enum lsp_types_t lsp_type,  					       enum nexthop_types_t gtype, -					       union g_addr *gate, +					       const union g_addr *gate,  					       ifindex_t ifindex,  					       uint8_t num_labels,  					       const mpls_label_t *out_labels)  {  	/* Just a public pass-through to the internal implementation */ -	return nhlfe_backup_add(lsp, lsp_type, gtype, gate, ifindex, num_labels, -				out_labels); +	return nhlfe_add(lsp, lsp_type, gtype, gate, ifindex, num_labels, +			 out_labels, true);  }  /* @@ -2275,7 +2346,8 @@ zebra_nhlfe_t *zebra_mpls_lsp_add_nh(zebra_lsp_t *lsp,  		return NULL;  	nhlfe = nhlfe_add(lsp, lsp_type, nh->type, &nh->gate, nh->ifindex, -			  nh->nh_label->num_labels, nh->nh_label->label); +			  nh->nh_label->num_labels, nh->nh_label->label, +			  false /*backup*/);  	return nhlfe;  } @@ -2293,9 +2365,9 @@ zebra_nhlfe_t *zebra_mpls_lsp_add_backup_nh(zebra_lsp_t *lsp,  	if (nh->nh_label == NULL || nh->nh_label->num_labels == 0)  		return NULL; -	nhlfe = nhlfe_backup_add(lsp, lsp_type, nh->type, &nh->gate, +	nhlfe = nhlfe_add(lsp, lsp_type, nh->type, &nh->gate,  				 nh->ifindex, nh->nh_label->num_labels, -				 nh->nh_label->label); +				 nh->nh_label->label, true);  	return nhlfe;  } @@ -2846,6 +2918,9 @@ int mpls_ftn_uninstall(struct zebra_vrf *zvrf, enum lsp_types_t type,  	SET_FLAG(re->status, ROUTE_ENTRY_CHANGED);  	SET_FLAG(re->status, ROUTE_ENTRY_LABELS_CHANGED); +	/* This will create (or ref) a new nhe, so we will discard the local +	 * temporary nhe +	 */  	mpls_zebra_nhe_update(re, afi, new_nhe);  	zebra_nhg_free(new_nhe); @@ -3013,7 +3088,8 @@ int mpls_zapi_labels_process(bool add_p, struct zebra_vrf *zvrf,  		else  			ret = mpls_lsp_uninstall(zvrf, zl->type,  						 zl->local_label, znh->type, -						 &znh->gate, znh->ifindex); +						 &znh->gate, znh->ifindex, +						 false);  		if (ret < 0) {  			if (IS_ZEBRA_DEBUG_RECV || IS_ZEBRA_DEBUG_MPLS) {  				zapi_nexthop2str(znh, buf, sizeof(buf)); @@ -3055,10 +3131,10 @@ int mpls_zapi_labels_process(bool add_p, struct zebra_vrf *zvrf,  		if (add_p)  			ret = lsp_backup_znh_install(lsp, zl->type, znh);  		else -			ret = lsp_backup_uninstall(zvrf, zl->type, -						   zl->local_label, -						   znh->type, &znh->gate, -						   znh->ifindex); +			ret = mpls_lsp_uninstall(zvrf, zl->type, +						 zl->local_label, +						 znh->type, &znh->gate, +						 znh->ifindex, true);  		if (ret < 0) {  			if (IS_ZEBRA_DEBUG_RECV || @@ -3123,92 +3199,22 @@ static zebra_nhlfe_t *  lsp_add_nhlfe(zebra_lsp_t *lsp, enum lsp_types_t type,  	      uint8_t num_out_labels, const mpls_label_t *out_labels,  	      enum nexthop_types_t gtype, const union g_addr *gate, -	      ifindex_t ifindex) +	      ifindex_t ifindex, bool is_backup)  {  	zebra_nhlfe_t *nhlfe;  	char buf[MPLS_LABEL_STRLEN]; +	const char *backup_str; -	nhlfe = nhlfe_find(&lsp->nhlfe_list, type, gtype, gate, ifindex); -	if (nhlfe) { -		struct nexthop *nh = nhlfe->nexthop; - -		assert(nh); -		assert(nh->nh_label); - -		/* Clear deleted flag (in case it was set) */ -		UNSET_FLAG(nhlfe->flags, NHLFE_FLAG_DELETED); -		if (nh->nh_label->num_labels == num_out_labels -		    && !memcmp(nh->nh_label->label, out_labels, -			       sizeof(mpls_label_t) * num_out_labels)) -			/* No change */ -			return nhlfe; - -		if (IS_ZEBRA_DEBUG_MPLS) { -			char buf2[MPLS_LABEL_STRLEN]; -			char buf3[MPLS_LABEL_STRLEN]; - -			nhlfe2str(nhlfe, buf, sizeof(buf)); -			mpls_label2str(num_out_labels, out_labels, buf2, -				       sizeof(buf2), 0); -			mpls_label2str(nh->nh_label->num_labels, -				       nh->nh_label->label, buf3, sizeof(buf3), -				       0); - -			zlog_debug("LSP in-label %u type %d nexthop %s out-label(s) changed to %s (old %s)", -				   lsp->ile.in_label, type, buf, buf2, buf3); -		} - -		/* Update out label(s), trigger processing. */ -		if (nh->nh_label->num_labels == num_out_labels) -			memcpy(nh->nh_label->label, out_labels, -			       sizeof(mpls_label_t) * num_out_labels); -		else { -			nexthop_del_labels(nh); -			nexthop_add_labels(nh, type, num_out_labels, -					   out_labels); -		} +	if (is_backup) { +		nhlfe = nhlfe_find(&lsp->backup_nhlfe_list, type, gtype, +				   gate, ifindex); +		backup_str = "backup ";  	} else { -		/* Add LSP entry to this nexthop */ -		nhlfe = nhlfe_add(lsp, type, gtype, gate, ifindex, -				  num_out_labels, out_labels); -		if (!nhlfe) -			return NULL; - -		if (IS_ZEBRA_DEBUG_MPLS) { -			char buf2[MPLS_LABEL_STRLEN]; - -			nhlfe2str(nhlfe, buf, sizeof(buf)); -			mpls_label2str(num_out_labels, out_labels, buf2, -				       sizeof(buf2), 0); - -			zlog_debug("Add LSP in-label %u type %d nexthop %s out-label(s) %s", -				   lsp->ile.in_label, type, buf, buf2); -		} - -		lsp->addr_family = NHLFE_FAMILY(nhlfe); +		nhlfe = nhlfe_find(&lsp->nhlfe_list, type, gtype, gate, +				   ifindex); +		backup_str = "";  	} -	/* Mark NHLFE, queue LSP for processing. */ -	SET_FLAG(nhlfe->flags, NHLFE_FLAG_CHANGED); - -	return nhlfe; -} - -/* - * Install/update a NHLFE for an LSP in the forwarding table. This may be - * a new LSP entry or a new NHLFE for an existing in-label or an update of - * the out-label for an existing NHLFE (update case). - */ -static zebra_nhlfe_t * -lsp_add_backup_nhlfe(zebra_lsp_t *lsp, enum lsp_types_t type, -		     uint8_t num_out_labels, const mpls_label_t *out_labels, -		     enum nexthop_types_t gtype, const union g_addr *gate, -		     ifindex_t ifindex) -{ -	zebra_nhlfe_t *nhlfe; -	char buf[MPLS_LABEL_STRLEN]; - -	nhlfe = nhlfe_find(&lsp->backup_nhlfe_list, type, gtype, gate, ifindex);  	if (nhlfe) {  		struct nexthop *nh = nhlfe->nexthop; @@ -3234,8 +3240,9 @@ lsp_add_backup_nhlfe(zebra_lsp_t *lsp, enum lsp_types_t type,  				       nh->nh_label->label, buf3, sizeof(buf3),  				       0); -			zlog_debug("LSP in-label %u type %d backup nexthop %s out-label(s) changed to %s (old %s)", -				   lsp->ile.in_label, type, buf, buf2, buf3); +			zlog_debug("LSP in-label %u type %d %snexthop %s out-label(s) changed to %s (old %s)", +				   lsp->ile.in_label, type, backup_str, buf, +				   buf2, buf3);  		}  		/* Update out label(s), trigger processing. */ @@ -3249,8 +3256,8 @@ lsp_add_backup_nhlfe(zebra_lsp_t *lsp, enum lsp_types_t type,  		}  	} else {  		/* Add LSP entry to this nexthop */ -		nhlfe = nhlfe_backup_add(lsp, type, gtype, gate, ifindex, -					 num_out_labels, out_labels); +		nhlfe = nhlfe_add(lsp, type, gtype, gate, ifindex, +				  num_out_labels, out_labels, is_backup);  		if (!nhlfe)  			return NULL; @@ -3261,8 +3268,9 @@ lsp_add_backup_nhlfe(zebra_lsp_t *lsp, enum lsp_types_t type,  			mpls_label2str(num_out_labels, out_labels, buf2,  				       sizeof(buf2), 0); -			zlog_debug("Add LSP in-label %u type %d backup nexthop %s out-label(s) %s", -				   lsp->ile.in_label, type, buf, buf2); +			zlog_debug("Add LSP in-label %u type %d %snexthop %s out-label(s) %s", +				   lsp->ile.in_label, type, backup_str, buf, +				   buf2);  		}  		lsp->addr_family = NHLFE_FAMILY(nhlfe); @@ -3300,7 +3308,7 @@ int mpls_lsp_install(struct zebra_vrf *zvrf, enum lsp_types_t type,  		return -1;  	nhlfe = lsp_add_nhlfe(lsp, type, num_out_labels, out_labels, gtype, -			      gate, ifindex); +			      gate, ifindex, false /*backup*/);  	if (nhlfe == NULL)  		return -1; @@ -3320,7 +3328,8 @@ static int lsp_znh_install(zebra_lsp_t *lsp, enum lsp_types_t type,  	zebra_nhlfe_t *nhlfe;  	nhlfe = lsp_add_nhlfe(lsp, type, znh->label_num, znh->labels, -			      znh->type, &znh->gate, znh->ifindex); +			      znh->type, &znh->gate, znh->ifindex, +			      false /*backup*/);  	if (nhlfe == NULL)  		return -1; @@ -3345,9 +3354,9 @@ static int lsp_backup_znh_install(zebra_lsp_t *lsp, enum lsp_types_t type,  {  	zebra_nhlfe_t *nhlfe; -	nhlfe = lsp_add_backup_nhlfe(lsp, type, znh->label_num, -				     znh->labels, znh->type, &znh->gate, -				     znh->ifindex); +	nhlfe = lsp_add_nhlfe(lsp, type, znh->label_num, +			      znh->labels, znh->type, &znh->gate, +			      znh->ifindex, true /*backup*/);  	if (nhlfe == NULL) {  		if (IS_ZEBRA_DEBUG_MPLS)  			zlog_debug("%s: unable to add backup nhlfe, label: %u", @@ -3368,13 +3377,14 @@ static int lsp_backup_znh_install(zebra_lsp_t *lsp, enum lsp_types_t type,   */  int mpls_lsp_uninstall(struct zebra_vrf *zvrf, enum lsp_types_t type,  		       mpls_label_t in_label, enum nexthop_types_t gtype, -		       const union g_addr *gate, ifindex_t ifindex) +		       const union g_addr *gate, ifindex_t ifindex, +		       bool backup_p)  {  	struct hash *lsp_table;  	zebra_ile_t tmp_ile;  	zebra_lsp_t *lsp;  	zebra_nhlfe_t *nhlfe; -	char buf[BUFSIZ]; +	char buf[NEXTHOP_STRLEN];  	bool schedule_lsp = false;  	/* Lookup table. */ @@ -3387,7 +3397,13 @@ int mpls_lsp_uninstall(struct zebra_vrf *zvrf, enum lsp_types_t type,  	lsp = hash_lookup(lsp_table, &tmp_ile);  	if (!lsp)  		return 0; -	nhlfe = nhlfe_find(&lsp->nhlfe_list, type, gtype, gate, ifindex); + +	if (backup_p) +		nhlfe = nhlfe_find(&lsp->backup_nhlfe_list, type, gtype, +				   gate, ifindex); +	else +		nhlfe = nhlfe_find(&lsp->nhlfe_list, type, gtype, gate, +				   ifindex);  	if (!nhlfe)  		return 0; @@ -3420,56 +3436,6 @@ int mpls_lsp_uninstall(struct zebra_vrf *zvrf, enum lsp_types_t type,  	return 0;  } -/* - * Uninstall a particular NHLFE in the forwarding table. If this is - * the only NHLFE, the entire LSP forwarding entry has to be deleted. - */ -static int lsp_backup_uninstall(struct zebra_vrf *zvrf, enum lsp_types_t type, -				mpls_label_t in_label, -				enum nexthop_types_t gtype, -				const union g_addr *gate, ifindex_t ifindex) -{ -	struct hash *lsp_table; -	zebra_ile_t tmp_ile; -	zebra_lsp_t *lsp; -	zebra_nhlfe_t *nhlfe; -	char buf[BUFSIZ]; - -	/* Lookup table. */ -	lsp_table = zvrf->lsp_table; -	if (!lsp_table) -		return -1; - -	/* If entry is not present, exit. */ -	tmp_ile.in_label = in_label; -	lsp = hash_lookup(lsp_table, &tmp_ile); -	if (!lsp) -		return 0; -	nhlfe = nhlfe_find(&lsp->backup_nhlfe_list, type, gtype, gate, ifindex); -	if (!nhlfe) -		return 0; - -	if (IS_ZEBRA_DEBUG_MPLS) { -		nhlfe2str(nhlfe, buf, BUFSIZ); -		zlog_debug("Del backup LSP in-label %u type %d nexthop %s flags 0x%x", -			   in_label, type, buf, nhlfe->flags); -	} - -	/* Mark NHLFE for delete or directly delete, as appropriate. */ -	if (CHECK_FLAG(nhlfe->flags, NHLFE_FLAG_INSTALLED)) { -		UNSET_FLAG(nhlfe->flags, NHLFE_FLAG_CHANGED); -		SET_FLAG(nhlfe->flags, NHLFE_FLAG_DELETED); -		if (lsp_processq_add(lsp)) -			return -1; -	} else { -		nhlfe_del(nhlfe); - -		/* Free LSP entry if no other NHLFEs and not scheduled. */ -		lsp_check_free(lsp_table, &lsp); -	} -	return 0; -} -  int mpls_lsp_uninstall_all_vrf(struct zebra_vrf *zvrf, enum lsp_types_t type,  			       mpls_label_t in_label)  { @@ -3754,7 +3720,7 @@ int zebra_mpls_static_lsp_del(struct zebra_vrf *zvrf, mpls_label_t in_label,  		/* Uninstall LSP from the main table. */  		mpls_lsp_uninstall(zvrf, ZEBRA_LSP_STATIC, in_label, gtype, -				   gate, ifindex); +				   gate, ifindex, false);  		/* Delete static LSP NHLFE */  		snhlfe_del(snhlfe); diff --git a/zebra/zebra_mpls.h b/zebra/zebra_mpls.h index 9b5fb39573..07a8efeb8b 100644 --- a/zebra/zebra_mpls.h +++ b/zebra/zebra_mpls.h @@ -213,7 +213,7 @@ int zebra_mpls_lsp_uninstall(struct zebra_vrf *zvrf, struct route_node *rn,  zebra_nhlfe_t *zebra_mpls_lsp_add_nhlfe(zebra_lsp_t *lsp,  					enum lsp_types_t lsp_type,  					enum nexthop_types_t gtype, -					union g_addr *gate, +					const union g_addr *gate,  					ifindex_t ifindex,  					uint8_t num_labels,  					const mpls_label_t *out_labels); @@ -222,7 +222,7 @@ zebra_nhlfe_t *zebra_mpls_lsp_add_nhlfe(zebra_lsp_t *lsp,  zebra_nhlfe_t *zebra_mpls_lsp_add_backup_nhlfe(zebra_lsp_t *lsp,  					       enum lsp_types_t lsp_type,  					       enum nexthop_types_t gtype, -					       union g_addr *gate, +					       const union g_addr *gate,  					       ifindex_t ifindex,  					       uint8_t num_labels,  					       const mpls_label_t *out_labels); @@ -331,7 +331,8 @@ int mpls_lsp_install(struct zebra_vrf *zvrf, enum lsp_types_t type,   */  int mpls_lsp_uninstall(struct zebra_vrf *zvrf, enum lsp_types_t type,  		       mpls_label_t in_label, enum nexthop_types_t gtype, -		       const union g_addr *gate, ifindex_t ifindex); +		       const union g_addr *gate, ifindex_t ifindex, +		       bool backup_p);  /*   * Uninstall all NHLFEs for a particular LSP forwarding entry. @@ -339,12 +340,6 @@ int mpls_lsp_uninstall(struct zebra_vrf *zvrf, enum lsp_types_t type,  int mpls_lsp_uninstall_all_vrf(struct zebra_vrf *zvrf, enum lsp_types_t type,  			       mpls_label_t in_label); -/* - * Uninstall all Segment Routing NHLFEs for a particular LSP forwarding entry. - * If no other NHLFEs exist, the entry would be deleted. - */ -void mpls_sr_lsp_uninstall_all(struct hash_bucket *bucket, void *ctxt); -  #if defined(HAVE_CUMULUS)  /*   * Check that the label values used in LSP creation are consistent. The diff --git a/zebra/zebra_nhg.c b/zebra/zebra_nhg.c index 02ba69bd4d..f5e4a4e79e 100644 --- a/zebra/zebra_nhg.c +++ b/zebra/zebra_nhg.c @@ -1734,6 +1734,10 @@ static bool nexthop_valid_resolve(const struct nexthop *nexthop,  	if (CHECK_FLAG(resolved->flags, NEXTHOP_FLAG_RECURSIVE))  		return false; +	/* Must be ACTIVE */ +	if (!CHECK_FLAG(resolved->flags, NEXTHOP_FLAG_ACTIVE)) +		return false; +  	switch (nexthop->type) {  	case NEXTHOP_TYPE_IPV4_IFINDEX:  	case NEXTHOP_TYPE_IPV6_IFINDEX: @@ -1926,11 +1930,23 @@ static int nexthop_active(afi_t afi, struct route_entry *re,  			return 1;  		} else if (CHECK_FLAG(re->flags, ZEBRA_FLAG_ALLOW_RECURSION)) { +			struct nexthop_group *nhg; +  			resolved = 0; -			for (ALL_NEXTHOPS(match->nhe->nhg, newhop)) { -				if (!CHECK_FLAG(match->status, -						ROUTE_ENTRY_INSTALLED)) -					continue; + +			/* Only useful if installed */ +			if (!CHECK_FLAG(match->status, ROUTE_ENTRY_INSTALLED)) { +				if (IS_ZEBRA_DEBUG_NHG_DETAIL) +					zlog_debug("%s: match %p (%u) not installed", +						   __func__, match, +						   match->nhe->id); + +				goto done_with_match; +			} + +			/* Examine installed nexthops */ +			nhg = &match->nhe->nhg; +			for (ALL_NEXTHOPS_PTR(nhg, newhop)) {  				if (!nexthop_valid_resolve(nexthop, newhop))  					continue; @@ -1945,25 +1961,21 @@ static int nexthop_active(afi_t afi, struct route_entry *re,  				resolved = 1;  			} -			if (resolved) -				re->nexthop_mtu = match->mtu; -			else if (IS_ZEBRA_DEBUG_RIB_DETAILED) -				zlog_debug( -					"        %s: Recursion failed to find", -					__func__); +			/* Examine installed backup nexthops, if any. There +			 * are only installed backups *if* there is a +			 * dedicated fib list. +			 */ +			nhg = rib_get_fib_backup_nhg(match); +			if (nhg == NULL || +			    nhg == zebra_nhg_get_backup_nhg(match->nhe)) +				goto done_with_match; -			return resolved; -		} else if (re->type == ZEBRA_ROUTE_STATIC) { -			resolved = 0; -			for (ALL_NEXTHOPS(match->nhe->nhg, newhop)) { -				if (!CHECK_FLAG(match->status, -						ROUTE_ENTRY_INSTALLED)) -					continue; +			for (ALL_NEXTHOPS_PTR(nhg, newhop)) {  				if (!nexthop_valid_resolve(nexthop, newhop))  					continue; -				if (IS_ZEBRA_DEBUG_RIB_DETAILED) -					zlog_debug("%s: STATIC match %p (%u), newhop %pNHv", +				if (IS_ZEBRA_DEBUG_NHG_DETAIL) +					zlog_debug("%s: RECURSIVE match backup %p (%u), newhop %pNHv",  						   __func__, match,  						   match->nhe->id, newhop); @@ -1972,13 +1984,14 @@ static int nexthop_active(afi_t afi, struct route_entry *re,  				nexthop_set_resolved(afi, newhop, nexthop);  				resolved = 1;  			} +done_with_match:  			if (resolved)  				re->nexthop_mtu = match->mtu; - -			if (!resolved && IS_ZEBRA_DEBUG_RIB_DETAILED) +			else if (IS_ZEBRA_DEBUG_RIB_DETAILED)  				zlog_debug( -					"        %s: Static route unable to resolve", +					"        %s: Recursion failed to find",  					__func__); +  			return resolved;  		} else {  			if (IS_ZEBRA_DEBUG_RIB_DETAILED) { @@ -2170,17 +2183,20 @@ done:  }  /* - * Process a list of nexthops, given the head of the list, determining + * Process a list of nexthops, given an nhg, determining   * whether each one is ACTIVE/installable at this time.   */  static uint32_t nexthop_list_active_update(struct route_node *rn,  					   struct route_entry *re, -					   struct nexthop *nexthop) +					   struct nexthop_group *nhg)  {  	union g_addr prev_src;  	unsigned int prev_active, new_active;  	ifindex_t prev_index;  	uint32_t counter = 0; +	struct nexthop *nexthop; + +	nexthop = nhg->nexthop;  	/* Process nexthops one-by-one */  	for ( ; nexthop; nexthop = nexthop->next) { @@ -2263,7 +2279,7 @@ int nexthop_active_update(struct route_node *rn, struct route_entry *re)  	curr_nhe->id = 0;  	/* Process nexthops */ -	curr_active = nexthop_list_active_update(rn, re, curr_nhe->nhg.nexthop); +	curr_active = nexthop_list_active_update(rn, re, &curr_nhe->nhg);  	if (IS_ZEBRA_DEBUG_NHG_DETAIL)  		zlog_debug("%s: re %p curr_active %u", __func__, re, @@ -2274,7 +2290,7 @@ int nexthop_active_update(struct route_node *rn, struct route_entry *re)  		goto backups_done;  	backup_active = nexthop_list_active_update( -		rn, re, zebra_nhg_get_backup_nhg(curr_nhe)->nexthop); +		rn, re, zebra_nhg_get_backup_nhg(curr_nhe));  	if (IS_ZEBRA_DEBUG_NHG_DETAIL)  		zlog_debug("%s: re %p backup_active %u", __func__, re, diff --git a/zebra/zebra_pw.c b/zebra/zebra_pw.c index 8f0c964c18..2328ab650a 100644 --- a/zebra/zebra_pw.c +++ b/zebra/zebra_pw.c @@ -550,7 +550,7 @@ static void vty_show_mpls_pseudowire_detail(struct vty *vty)  		re = rib_match(family2afi(pw->af), SAFI_UNICAST, pw->vrf_id,  			       &pw->nexthop, NULL);  		if (re) { -			for (ALL_NEXTHOPS_PTR(rib_active_nhg(re), nexthop)) { +			for (ALL_NEXTHOPS_PTR(rib_get_fib_nhg(re), nexthop)) {  				snprintfrr(buf_nh, sizeof(buf_nh), "%pNHv",  					   nexthop);  				vty_out(vty, "  Next Hop: %s\n", buf_nh); @@ -604,7 +604,7 @@ static void vty_show_mpls_pseudowire(struct zebra_pw *pw, json_object *json_pws)  	re = rib_match(family2afi(pw->af), SAFI_UNICAST, pw->vrf_id,  		       &pw->nexthop, NULL);  	if (re) { -		for (ALL_NEXTHOPS_PTR(rib_active_nhg(re), nexthop)) { +		for (ALL_NEXTHOPS_PTR(rib_get_fib_nhg(re), nexthop)) {  			json_nexthop = json_object_new_object();  			snprintfrr(buf_nh, sizeof(buf_nh), "%pNHv", nexthop);  			json_object_string_add(json_nexthop, "nexthop", buf_nh); diff --git a/zebra/zebra_rib.c b/zebra/zebra_rib.c index 31582dcb3d..67b3812ed3 100644 --- a/zebra/zebra_rib.c +++ b/zebra/zebra_rib.c @@ -1342,6 +1342,92 @@ static bool rib_compare_routes(const struct route_entry *re1,  }  /* + * Compare nexthop lists from a route and a dplane context; test whether + * the list installed in the FIB matches the route's list. + * Set 'changed_p' to 'true' if there were changes to the route's + * installed nexthops. + * + * Return 'false' if any ACTIVE route nexthops are not mentioned in the FIB + * list. + */ +static bool rib_update_nhg_from_ctx(struct nexthop_group *re_nhg, +				    const struct nexthop_group *ctx_nhg, +				    bool *changed_p) +{ +	bool matched_p = true; +	struct nexthop *nexthop, *ctx_nexthop; + +	/* Get the first `installed` one to check against. +	 * If the dataplane doesn't set these to be what was actually installed, +	 * it will just be whatever was in re->nhe->nhg? +	 */ +	ctx_nexthop = ctx_nhg->nexthop; + +	if (CHECK_FLAG(ctx_nexthop->flags, NEXTHOP_FLAG_RECURSIVE) +	    || !CHECK_FLAG(ctx_nexthop->flags, NEXTHOP_FLAG_ACTIVE)) +		ctx_nexthop = nexthop_next_active_resolved(ctx_nexthop); + +	for (ALL_NEXTHOPS_PTR(re_nhg, nexthop)) { + +		if (CHECK_FLAG(nexthop->flags, NEXTHOP_FLAG_RECURSIVE)) +			continue; + +		if (!CHECK_FLAG(nexthop->flags, NEXTHOP_FLAG_ACTIVE)) +			continue; + +		/* Check for a FIB nexthop corresponding to the RIB nexthop */ +		if (nexthop_same(ctx_nexthop, nexthop) == false) { +			/* If the FIB doesn't know about the nexthop, +			 * it's not installed +			 */ +			if (IS_ZEBRA_DEBUG_RIB_DETAILED || +			    IS_ZEBRA_DEBUG_NHG_DETAIL) { +				zlog_debug("%s: no ctx match for rib nh %pNHv %s", +					   __func__, nexthop, +					   (CHECK_FLAG(nexthop->flags, +						       NEXTHOP_FLAG_FIB) ? +					    "(FIB)":"")); +			} +			matched_p = false; + +			if (CHECK_FLAG(nexthop->flags, NEXTHOP_FLAG_FIB)) +				*changed_p = true; + +			UNSET_FLAG(nexthop->flags, NEXTHOP_FLAG_FIB); + +			/* Keep checking nexthops */ +			continue; +		} + +		if (CHECK_FLAG(ctx_nexthop->flags, NEXTHOP_FLAG_FIB)) { +			if (!CHECK_FLAG(nexthop->flags, NEXTHOP_FLAG_FIB)) { +				if (IS_ZEBRA_DEBUG_NHG_DETAIL) +					zlog_debug("%s: rib nh %pNHv -> installed", +						   __func__, nexthop); + +				*changed_p = true; +			} + +			SET_FLAG(nexthop->flags, NEXTHOP_FLAG_FIB); +		} else { +			if (CHECK_FLAG(nexthop->flags, NEXTHOP_FLAG_FIB)) { +				if (IS_ZEBRA_DEBUG_NHG_DETAIL) +					zlog_debug("%s: rib nh %pNHv -> uninstalled", +						   __func__, nexthop); + +				*changed_p = true; +			} + +			UNSET_FLAG(nexthop->flags, NEXTHOP_FLAG_FIB); +		} + +		ctx_nexthop = nexthop_next_active_resolved(ctx_nexthop); +	} + +	return matched_p; +} + +/*   * Update a route from a dplane context. This consolidates common code   * that can be used in processing of results from FIB updates, and in   * async notification processing. @@ -1352,10 +1438,10 @@ static bool rib_update_re_from_ctx(struct route_entry *re,  				   struct zebra_dplane_ctx *ctx)  {  	char dest_str[PREFIX_STRLEN] = ""; -	char nh_str[NEXTHOP_STRLEN]; -	struct nexthop *nexthop, *ctx_nexthop; +	struct nexthop *nexthop;  	bool matched;  	const struct nexthop_group *ctxnhg; +	struct nexthop_group *re_nhg;  	bool is_selected = false; /* Is 're' currently the selected re? */  	bool changed_p = false; /* Change to nexthops? */  	rib_dest_t *dest; @@ -1386,10 +1472,13 @@ static bool rib_update_re_from_ctx(struct route_entry *re,  	matched = false;  	ctxnhg = dplane_ctx_get_ng(ctx); -	/* Check both fib group and notif group for equivalence. +	/* Check route's fib group and incoming notif group for equivalence.  	 *  	 * Let's assume the nexthops are ordered here to save time.  	 */ +	/* TODO -- this isn't testing or comparing the FIB flags; we should +	 * do a more explicit loop, checking the incoming notification's flags. +	 */  	if (re->fib_ng.nexthop && ctxnhg->nexthop &&  	    nexthop_group_equal(&re->fib_ng, ctxnhg))  		matched = true; @@ -1400,7 +1489,7 @@ static bool rib_update_re_from_ctx(struct route_entry *re,  			zlog_debug(  				"%s(%u):%s update_from_ctx(): existing fib nhg, no change",  				VRF_LOGNAME(vrf), re->vrf_id, dest_str); -		goto done; +		goto check_backups;  	} else if (re->fib_ng.nexthop) {  		/* @@ -1430,70 +1519,16 @@ static bool rib_update_re_from_ctx(struct route_entry *re,  	 *  	 * Assume nexthops are ordered here as well.  	 */ -	matched = true; -	ctx_nexthop = ctxnhg->nexthop; - -	/* Nothing installed - we can skip some of the checking/comparison +	/* If nothing is installed, we can skip some of the checking/comparison  	 * of nexthops.  	 */ -	if (ctx_nexthop == NULL) { +	if (ctxnhg->nexthop == NULL) {  		changed_p = true;  		goto no_nexthops;  	} -	/* Get the first `installed` one to check against. -	 * If the dataplane doesn't set these to be what was actually installed, -	 * it will just be whatever was in re->nhe->nhg? -	 */ -	if (CHECK_FLAG(ctx_nexthop->flags, NEXTHOP_FLAG_RECURSIVE) -	    || !CHECK_FLAG(ctx_nexthop->flags, NEXTHOP_FLAG_ACTIVE)) -		ctx_nexthop = nexthop_next_active_resolved(ctx_nexthop); - -	for (ALL_NEXTHOPS(re->nhe->nhg, nexthop)) { - -		if (CHECK_FLAG(nexthop->flags, NEXTHOP_FLAG_RECURSIVE)) -			continue; - -		if (!CHECK_FLAG(nexthop->flags, NEXTHOP_FLAG_ACTIVE)) -			continue; - -		/* Check for a FIB nexthop corresponding to the RIB nexthop */ -		if (nexthop_same(ctx_nexthop, nexthop) == false) { -			/* If the FIB doesn't know about the nexthop, -			 * it's not installed -			 */ -			if (IS_ZEBRA_DEBUG_RIB_DETAILED) { -				nexthop2str(nexthop, nh_str, sizeof(nh_str)); -				zlog_debug( -					"update_from_ctx: no match for rib nh %s", -					nh_str); -			} -			matched = false; - -			if (CHECK_FLAG(nexthop->flags, NEXTHOP_FLAG_FIB)) -				changed_p = true; - -			UNSET_FLAG(nexthop->flags, NEXTHOP_FLAG_FIB); - -			/* Keep checking nexthops */ -			continue; -		} - -		if (CHECK_FLAG(ctx_nexthop->flags, NEXTHOP_FLAG_FIB)) { -			if (!CHECK_FLAG(nexthop->flags, NEXTHOP_FLAG_FIB)) -				changed_p = true; - -			SET_FLAG(nexthop->flags, NEXTHOP_FLAG_FIB); -		} else { -			if (CHECK_FLAG(nexthop->flags, NEXTHOP_FLAG_FIB)) -				changed_p = true; - -			UNSET_FLAG(nexthop->flags, NEXTHOP_FLAG_FIB); -		} - -		ctx_nexthop = nexthop_next_active_resolved(ctx_nexthop); -	} +	matched = rib_update_nhg_from_ctx(&(re->nhe->nhg), ctxnhg, &changed_p);  	/* If all nexthops were processed, we're done */  	if (matched) { @@ -1502,7 +1537,7 @@ static bool rib_update_re_from_ctx(struct route_entry *re,  				"%s(%u):%s update_from_ctx(): rib nhg matched, changed '%s'",  				VRF_LOGNAME(vrf), re->vrf_id, dest_str,  				(changed_p ? "true" : "false")); -		goto done; +		goto check_backups;  	}  no_nexthops: @@ -1527,7 +1562,81 @@ no_nexthops:  		_nexthop_add(&(re->fib_ng.nexthop), nexthop);  	} +check_backups: + +	/* +	 * Check the status of the route's backup nexthops, if any. +	 * The logic for backups is somewhat different: if any backup is +	 * installed, a new fib nhg will be attached to the route. +	 */ +	re_nhg = zebra_nhg_get_backup_nhg(re->nhe); +	if (re_nhg == NULL) +		goto done;	/* No backup nexthops */ + +	/* First check the route's 'fib' list of backups, if it's present +	 * from some previous event. +	 */ +	re_nhg = &re->fib_backup_ng; +	ctxnhg = dplane_ctx_get_backup_ng(ctx); + +	matched = false; +	if (re_nhg->nexthop && ctxnhg && nexthop_group_equal(re_nhg, ctxnhg)) +		matched = true; + +	/* If the new FIB set matches an existing FIB set, we're done. */ +	if (matched) { +		if (IS_ZEBRA_DEBUG_RIB) +			zlog_debug( +				"%s(%u):%s update_from_ctx(): existing fib backup nhg, no change", +				VRF_LOGNAME(vrf), re->vrf_id, dest_str); +		goto done; + +	} else if (re->fib_backup_ng.nexthop) { +		/* +		 * Free stale fib backup list and move on to check +		 * the route's backups. +		 */ +		if (IS_ZEBRA_DEBUG_RIB) +			zlog_debug( +				"%s(%u):%s update_from_ctx(): replacing fib backup nhg", +				VRF_LOGNAME(vrf), re->vrf_id, dest_str); +		nexthops_free(re->fib_backup_ng.nexthop); +		re->fib_backup_ng.nexthop = NULL; + +		/* Note that the installed nexthops have changed */ +		changed_p = true; +	} else { +		if (IS_ZEBRA_DEBUG_RIB) +			zlog_debug("%s(%u):%s update_from_ctx(): no fib backup nhg", +				   VRF_LOGNAME(vrf), re->vrf_id, dest_str); +	} + +	/* +	 * If a FIB backup nexthop set exists: attach a copy +	 * to the route if any backup is installed +	 */ +	if (ctxnhg && ctxnhg->nexthop) { + +		for (ALL_NEXTHOPS_PTR(ctxnhg, nexthop)) { +			if (CHECK_FLAG(nexthop->flags, NEXTHOP_FLAG_FIB)) +				break; +		} + +		/* If no installed backups, we're done */ +		if (nexthop == NULL) +			goto done; + +		if (IS_ZEBRA_DEBUG_RIB) +			zlog_debug("%s(%u):%s update_from_ctx(): changed %s, adding new backup fib nhg", +				   VRF_LOGNAME(vrf), re->vrf_id, dest_str, +				   (changed_p ? "true" : "false")); + +		copy_nexthops(&(re->fib_backup_ng.nexthop), ctxnhg->nexthop, +			      NULL); +	} +  done: +  	return changed_p;  } @@ -1814,6 +1923,38 @@ done:  }  /* + * Count installed/FIB nexthops + */ +static int rib_count_installed_nh(struct route_entry *re) +{ +	int count = 0; +	struct nexthop *nexthop; +	struct nexthop_group *nhg; + +	nhg = rib_get_fib_nhg(re); + +	for (ALL_NEXTHOPS_PTR(nhg, nexthop)) { +		/* The meaningful flag depends on where the installed +		 * nexthops reside. +		 */ +		if (nhg == &(re->fib_backup_ng)) { +			if (CHECK_FLAG(nexthop->flags, NEXTHOP_FLAG_FIB)) +				count++; +		} else { +			if (CHECK_FLAG(nexthop->flags, NEXTHOP_FLAG_ACTIVE)) +				count++; +		} +	} + +	for (ALL_NEXTHOPS_PTR(rib_get_fib_backup_nhg(re), nexthop)) { +		if (CHECK_FLAG(nexthop->flags, NEXTHOP_FLAG_FIB)) +			count++; +	} + +	return count; +} + +/*   * Handle notification from async dataplane: the dataplane has detected   * some change to a route, and notifies zebra so that the control plane   * can reflect that change. @@ -1930,12 +2071,8 @@ static void rib_process_dplane_notify(struct zebra_dplane_ctx *ctx)  	 */  	start_count = 0; -	if (CHECK_FLAG(re->status, ROUTE_ENTRY_INSTALLED)) { -		for (ALL_NEXTHOPS_PTR(rib_active_nhg(re), nexthop)) { -			if (CHECK_FLAG(nexthop->flags, NEXTHOP_FLAG_FIB)) -				start_count++; -		} -	} +	if (CHECK_FLAG(re->status, ROUTE_ENTRY_INSTALLED)) +		start_count = rib_count_installed_nh(re);  	/* Update zebra's nexthop FIB flags based on the context struct's  	 * nexthops. @@ -1954,12 +2091,7 @@ static void rib_process_dplane_notify(struct zebra_dplane_ctx *ctx)  	 * Perform follow-up work if the actual status of the prefix  	 * changed.  	 */ - -	end_count = 0; -	for (ALL_NEXTHOPS_PTR(rib_active_nhg(re), nexthop)) { -		if (CHECK_FLAG(nexthop->flags, NEXTHOP_FLAG_FIB)) -			end_count++; -	} +	end_count = rib_count_installed_nh(re);  	/* Various fib transitions: changed nexthops; from installed to  	 * not-installed; or not-installed to installed. @@ -1988,7 +2120,7 @@ static void rib_process_dplane_notify(struct zebra_dplane_ctx *ctx)  		SET_FLAG(re->status, ROUTE_ENTRY_INSTALLED);  		/* Changed nexthops - update kernel/others */ -		dplane_route_notif_update(rn, re, DPLANE_OP_ROUTE_INSTALL, ctx); +		dplane_route_notif_update(rn, re, DPLANE_OP_ROUTE_UPDATE, ctx);  		/* Redistribute, lsp, and nht update */  		redistribute_update(dest_pfx, src_pfx, re, NULL); diff --git a/zebra/zebra_rnh.c b/zebra/zebra_rnh.c index 20af96a557..d1a5cf2a9d 100644 --- a/zebra/zebra_rnh.c +++ b/zebra/zebra_rnh.c @@ -419,7 +419,7 @@ static int zebra_rnh_apply_nht_rmap(afi_t afi, struct zebra_vrf *zvrf,  				at_least_one++; /* at least one valid NH */  			else {  				SET_FLAG(nexthop->flags, -						NEXTHOP_FLAG_RNH_FILTERED); +					 NEXTHOP_FLAG_RNH_FILTERED);  			}  		}  	} @@ -458,12 +458,12 @@ zebra_rnh_resolve_import_entry(struct zebra_vrf *zvrf, afi_t afi,  	if (IS_ZEBRA_DEBUG_NHT_DETAILED) {  		char buf[PREFIX_STRLEN]; -		char buf1[PREFIX_STRLEN]; +		char buf1[SRCDEST2STR_BUFFER];  		zlog_debug("%s: %u:%s Resolved Import Entry to %s", __func__,  			   rnh->vrf_id,  			   prefix2str(&rnh->node->p, buf, sizeof(buf)), -			   srcdest_rnode2str(rn, buf1, sizeof(buf))); +			   srcdest_rnode2str(rn, buf1, sizeof(buf1)));  	}  	/* Identify appropriate route entry. */ @@ -974,12 +974,131 @@ static void copy_state(struct rnh *rnh, const struct route_entry *re,  	state->vrf_id = re->vrf_id;  	state->status = re->status; -	state->nhe = zebra_nhg_alloc(); +	state->nhe = zebra_nhe_copy(re->nhe, 0); + +	/* Copy the 'fib' nexthops also, if present - we want to capture +	 * the true installed nexthops. +	 */ +	if (re->fib_ng.nexthop) +		nexthop_group_copy(&state->fib_ng, &re->fib_ng); +	if (re->fib_backup_ng.nexthop) +		nexthop_group_copy(&state->fib_backup_ng, &re->fib_backup_ng); -	nexthop_group_copy(&(state->nhe->nhg), &(re->nhe->nhg));  	rnh->state = state;  } +/* + * Compare two route_entries' nexthops. + */ +static bool compare_valid_nexthops(struct route_entry *r1, +				   struct route_entry *r2) +{ +	bool matched_p = false; +	struct nexthop_group *nhg1, *nhg2; +	struct nexthop *nh1, *nh2; + +	/* Account for backup nexthops and for the 'fib' nexthop lists, +	 * if present. +	 */ +	nhg1 = rib_get_fib_nhg(r1); +	nhg2 = rib_get_fib_nhg(r2); + +	nh1 = nhg1->nexthop; +	nh2 = nhg2->nexthop; + +	while (1) { +		/* Find each list's next valid nexthop */ +		while ((nh1 != NULL) && !rnh_nexthop_valid(r1, nh1)) +			nh1 = nexthop_next(nh1); + +		while ((nh2 != NULL) && !rnh_nexthop_valid(r2, nh2)) +			nh2 = nexthop_next(nh2); + +		if (nh1 && nh2) { +			/* Any difference is a no-match */ +			if (nexthop_cmp(nh1, nh2) != 0) { +				if (IS_ZEBRA_DEBUG_NHT_DETAILED) +					zlog_debug("%s: nh1, nh2 differ", +						   __func__); +				goto done; +			} + +			nh1 = nexthop_next(nh1); +			nh2 = nexthop_next(nh2); +		} else if (nh1 || nh2) { +			/* One list has more valid nexthops than the other */ +			if (IS_ZEBRA_DEBUG_NHT_DETAILED) +				zlog_debug("%s: nh1 %s, nh2 %s", __func__, +					   nh1 ? "non-NULL" : "NULL", +					   nh2 ? "non-NULL" : "NULL"); +			goto done; +		} else +			break; /* Done with both lists */ +	} + +	/* The test for the backups is slightly different: the only installed +	 * backups will be in the 'fib' list. +	 */ +	nhg1 = rib_get_fib_backup_nhg(r1); +	if (nhg1 == zebra_nhg_get_backup_nhg(r1->nhe)) +		nhg1 = NULL; + +	nhg2 = rib_get_fib_backup_nhg(r2); +	if (nhg2 == zebra_nhg_get_backup_nhg(r2->nhe)) +		nhg2 = NULL; + +	if (nhg1) +		nh1 = nhg1->nexthop; +	else +		nh1 = NULL; + +	if (nhg2) +		nh2 = nhg2->nexthop; +	else +		nh2 = NULL; + +	while (1) { +		/* Find each backup list's next valid nexthop */ +		while ((nh1 != NULL) && !rnh_nexthop_valid(r1, nh1)) +			nh1 = nexthop_next(nh1); + +		while ((nh2 != NULL) && !rnh_nexthop_valid(r2, nh2)) +			nh2 = nexthop_next(nh2); + +		if (nh1 && nh2) { +			/* Any difference is a no-match */ +			if (nexthop_cmp(nh1, nh2) != 0) { +				if (IS_ZEBRA_DEBUG_NHT_DETAILED) +					zlog_debug("%s: backup nh1, nh2 differ", +						   __func__); +				goto done; +			} + +			nh1 = nexthop_next(nh1); +			nh2 = nexthop_next(nh2); +		} else if (nh1 || nh2) { +			/* One list has more valid nexthops than the other */ +			if (IS_ZEBRA_DEBUG_NHT_DETAILED) +				zlog_debug("%s: backup nh1 %s, nh2 %s", +					   __func__, +					   nh1 ? "non-NULL" : "NULL", +					   nh2 ? "non-NULL" : "NULL"); +			goto done; +		} else +			break; /* Done with both lists */ +	} + +	/* Well, it's a match */ +	if (IS_ZEBRA_DEBUG_NHT_DETAILED) +		zlog_debug("%s: matched", __func__); + +	matched_p = true; + +done: + +	return matched_p; +} +  static int compare_state(struct route_entry *r1, struct route_entry *r2)  {  	if (!r1 && !r2) @@ -994,12 +1113,7 @@ static int compare_state(struct route_entry *r1, struct route_entry *r2)  	if (r1->metric != r2->metric)  		return 1; -	if (nexthop_group_nexthop_num(&(r1->nhe->nhg)) -	    != nexthop_group_nexthop_num(&(r2->nhe->nhg))) -		return 1; - -	if (nexthop_group_hash(&(r1->nhe->nhg)) != -	    nexthop_group_hash(&(r2->nhe->nhg))) +	if (!compare_valid_nexthops(r1, r2))  		return 1;  	return 0; @@ -1044,6 +1158,7 @@ static int send_client(struct rnh *rnh, struct zserv *client,  	}  	if (re) {  		struct zapi_nexthop znh; +		struct nexthop_group *nhg;  		stream_putc(s, re->type);  		stream_putw(s, re->instance); @@ -1052,7 +1167,9 @@ static int send_client(struct rnh *rnh, struct zserv *client,  		num = 0;  		nump = stream_get_endp(s);  		stream_putc(s, 0); -		for (ALL_NEXTHOPS(re->nhe->nhg, nh)) + +		nhg = rib_get_fib_nhg(re); +		for (ALL_NEXTHOPS_PTR(nhg, nh))  			if (rnh_nexthop_valid(re, nh)) {  				zapi_nexthop_from_nexthop(&znh, nh);  				ret = zapi_nexthop_encode(s, &znh, 0/*flags*/); @@ -1061,6 +1178,21 @@ static int send_client(struct rnh *rnh, struct zserv *client,  				num++;  			} + +		nhg = rib_get_fib_backup_nhg(re); +		if (nhg == zebra_nhg_get_backup_nhg(re->nhe)) +			nhg = NULL; + +		if (nhg) { +			for (ALL_NEXTHOPS_PTR(nhg, nh)) +				if (rnh_nexthop_valid(re, nh)) { +					zapi_nexthop_from_nexthop(&znh, nh); +					zapi_nexthop_encode(s, &znh, +							    0 /* flags */); +					num++; +				} +		} +  		stream_putc_at(s, nump, num);  	} else {  		stream_putc(s, 0); // type diff --git a/zebra/zebra_vty.c b/zebra/zebra_vty.c index 9718b40d9d..1da2660509 100644 --- a/zebra/zebra_vty.c +++ b/zebra/zebra_vty.c @@ -516,8 +516,189 @@ static void show_route_nexthop_helper(struct vty *vty,  				       sizeof(buf), 1));  	} -	if ((re == NULL) && nexthop->weight) +	if (nexthop->weight)  		vty_out(vty, ", weight %u", nexthop->weight); + +	if (CHECK_FLAG(nexthop->flags, NEXTHOP_FLAG_HAS_BACKUP)) +		vty_out(vty, ", backup %d", nexthop->backup_idx); +} + +/* + * Render a nexthop into a json object; the caller allocates and owns + * the json object memory. + */ +static void show_nexthop_json_helper(json_object *json_nexthop, +				     const struct nexthop *nexthop, +				     const struct route_entry *re) +{ +	char buf[SRCDEST2STR_BUFFER]; +	struct vrf *vrf = NULL; +	json_object *json_labels = NULL; + +	json_object_int_add(json_nexthop, "flags", +			    nexthop->flags); + +	if (CHECK_FLAG(nexthop->flags, NEXTHOP_FLAG_DUPLICATE)) +		json_object_boolean_true_add(json_nexthop, +					     "duplicate"); + +	if (CHECK_FLAG(nexthop->flags, NEXTHOP_FLAG_FIB)) +		json_object_boolean_true_add(json_nexthop, +					     "fib"); + +	switch (nexthop->type) { +	case NEXTHOP_TYPE_IPV4: +	case NEXTHOP_TYPE_IPV4_IFINDEX: +		json_object_string_add( +			json_nexthop, "ip", +			inet_ntoa(nexthop->gate.ipv4)); +		json_object_string_add(json_nexthop, "afi", +				       "ipv4"); + +		if (nexthop->ifindex) { +			json_object_int_add(json_nexthop, +					    "interfaceIndex", +					    nexthop->ifindex); +			json_object_string_add( +				json_nexthop, "interfaceName", +				ifindex2ifname( +					nexthop->ifindex, +					nexthop->vrf_id)); +		} +		break; +	case NEXTHOP_TYPE_IPV6: +	case NEXTHOP_TYPE_IPV6_IFINDEX: +		json_object_string_add( +			json_nexthop, "ip", +			inet_ntop(AF_INET6, &nexthop->gate.ipv6, +				  buf, sizeof(buf))); +		json_object_string_add(json_nexthop, "afi", +				       "ipv6"); + +		if (nexthop->ifindex) { +			json_object_int_add(json_nexthop, +					    "interfaceIndex", +					    nexthop->ifindex); +			json_object_string_add( +				json_nexthop, "interfaceName", +				ifindex2ifname( +					nexthop->ifindex, +					nexthop->vrf_id)); +		} +		break; + +	case NEXTHOP_TYPE_IFINDEX: +		json_object_boolean_true_add( +			json_nexthop, "directlyConnected"); +		json_object_int_add(json_nexthop, +				    "interfaceIndex", +				    nexthop->ifindex); +		json_object_string_add( +			json_nexthop, "interfaceName", +			ifindex2ifname(nexthop->ifindex, +				       nexthop->vrf_id)); +		break; +	case NEXTHOP_TYPE_BLACKHOLE: +		json_object_boolean_true_add(json_nexthop, +					     "unreachable"); +		switch (nexthop->bh_type) { +		case BLACKHOLE_REJECT: +			json_object_boolean_true_add( +				json_nexthop, "reject"); +			break; +		case BLACKHOLE_ADMINPROHIB: +			json_object_boolean_true_add( +				json_nexthop, +				"admin-prohibited"); +			break; +		case BLACKHOLE_NULL: +			json_object_boolean_true_add( +				json_nexthop, "blackhole"); +			break; +		case BLACKHOLE_UNSPEC: +			break; +		} +		break; +	default: +		break; +	} + +	if ((nexthop->vrf_id != re->vrf_id) +	    && (nexthop->type != NEXTHOP_TYPE_BLACKHOLE)) { +		vrf = vrf_lookup_by_id(nexthop->vrf_id); +		json_object_string_add(json_nexthop, "vrf", +				       vrf->name); +	} +	if (CHECK_FLAG(nexthop->flags, NEXTHOP_FLAG_DUPLICATE)) +		json_object_boolean_true_add(json_nexthop, +					     "duplicate"); + +	if (CHECK_FLAG(nexthop->flags, NEXTHOP_FLAG_ACTIVE)) +		json_object_boolean_true_add(json_nexthop, +					     "active"); + +	if (CHECK_FLAG(nexthop->flags, NEXTHOP_FLAG_ONLINK)) +		json_object_boolean_true_add(json_nexthop, +					     "onLink"); + +	if (CHECK_FLAG(nexthop->flags, NEXTHOP_FLAG_RECURSIVE)) +		json_object_boolean_true_add(json_nexthop, +					     "recursive"); + +	if (CHECK_FLAG(nexthop->flags, NEXTHOP_FLAG_HAS_BACKUP)) +		json_object_int_add(json_nexthop, "backupIndex", +				    nexthop->backup_idx); + +	switch (nexthop->type) { +	case NEXTHOP_TYPE_IPV4: +	case NEXTHOP_TYPE_IPV4_IFINDEX: +		if (nexthop->src.ipv4.s_addr) { +			if (inet_ntop(AF_INET, +				      &nexthop->src.ipv4, buf, +				      sizeof(buf))) +				json_object_string_add( +					json_nexthop, "source", +					buf); +		} +		break; +	case NEXTHOP_TYPE_IPV6: +	case NEXTHOP_TYPE_IPV6_IFINDEX: +		if (!IPV6_ADDR_SAME(&nexthop->src.ipv6, +				    &in6addr_any)) { +			if (inet_ntop(AF_INET6, +				      &nexthop->src.ipv6, buf, +				      sizeof(buf))) +				json_object_string_add( +					json_nexthop, "source", +					buf); +		} +		break; +	default: +		break; +	} + +	if (nexthop->nh_label +	    && nexthop->nh_label->num_labels) { +		json_labels = json_object_new_array(); + +		for (int label_index = 0; +		     label_index +			     < nexthop->nh_label->num_labels; +		     label_index++) +			json_object_array_add( +				json_labels, +				json_object_new_int( +					nexthop->nh_label->label +					[label_index])); + +		json_object_object_add(json_nexthop, "labels", +				       json_labels); +	} + +	if (nexthop->weight) +		json_object_int_add(json_nexthop, "weight", +				    nexthop->weight); +  }  static void vty_show_ip_route(struct vty *vty, struct route_node *rn, @@ -530,12 +711,12 @@ static void vty_show_ip_route(struct vty *vty, struct route_node *rn,  	json_object *json_nexthops = NULL;  	json_object *json_nexthop = NULL;  	json_object *json_route = NULL; -	json_object *json_labels = NULL;  	time_t uptime;  	struct vrf *vrf = NULL;  	rib_dest_t *dest = rib_dest_from_rnode(rn);  	struct nexthop_group *nhg;  	char up_str[MONOTIME_STRLEN]; +	bool first_p;  	uptime = monotime(NULL);  	uptime -= re->uptime; @@ -546,7 +727,7 @@ static void vty_show_ip_route(struct vty *vty, struct route_node *rn,  	 * nexthops.  	 */  	if (is_fib) -		nhg = rib_active_nhg(re); +		nhg = rib_get_fib_nhg(re);  	else  		nhg = &(re->nhe->nhg); @@ -611,177 +792,44 @@ static void vty_show_ip_route(struct vty *vty, struct route_node *rn,  		for (ALL_NEXTHOPS_PTR(nhg, nexthop)) {  			json_nexthop = json_object_new_object(); -			json_object_int_add(json_nexthop, "flags", -					    nexthop->flags); +			show_nexthop_json_helper(json_nexthop, nexthop, re); +			json_object_array_add(json_nexthops, json_nexthop); +		} -			if (CHECK_FLAG(nexthop->flags, NEXTHOP_FLAG_DUPLICATE)) -				json_object_boolean_true_add(json_nexthop, -							     "duplicate"); +		json_object_object_add(json_route, "nexthops", json_nexthops); -			if (CHECK_FLAG(nexthop->flags, NEXTHOP_FLAG_FIB)) -				json_object_boolean_true_add(json_nexthop, -							     "fib"); +		/* If there are backup nexthops, include them */ +		if (is_fib) +			nhg = rib_get_fib_backup_nhg(re); +		else +			nhg = zebra_nhg_get_backup_nhg(re->nhe); -			switch (nexthop->type) { -			case NEXTHOP_TYPE_IPV4: -			case NEXTHOP_TYPE_IPV4_IFINDEX: -				json_object_string_add( -					json_nexthop, "ip", -					inet_ntoa(nexthop->gate.ipv4)); -				json_object_string_add(json_nexthop, "afi", -						       "ipv4"); - -				if (nexthop->ifindex) { -					json_object_int_add(json_nexthop, -							    "interfaceIndex", -							    nexthop->ifindex); -					json_object_string_add( -						json_nexthop, "interfaceName", -						ifindex2ifname( -							nexthop->ifindex, -							nexthop->vrf_id)); -				} -				break; -			case NEXTHOP_TYPE_IPV6: -			case NEXTHOP_TYPE_IPV6_IFINDEX: -				json_object_string_add( -					json_nexthop, "ip", -					inet_ntop(AF_INET6, &nexthop->gate.ipv6, -						  buf, sizeof(buf))); -				json_object_string_add(json_nexthop, "afi", -						       "ipv6"); - -				if (nexthop->ifindex) { -					json_object_int_add(json_nexthop, -							    "interfaceIndex", -							    nexthop->ifindex); -					json_object_string_add( -						json_nexthop, "interfaceName", -						ifindex2ifname( -							nexthop->ifindex, -							nexthop->vrf_id)); -				} -				break; - -			case NEXTHOP_TYPE_IFINDEX: -				json_object_boolean_true_add( -					json_nexthop, "directlyConnected"); -				json_object_int_add(json_nexthop, -						    "interfaceIndex", -						    nexthop->ifindex); -				json_object_string_add( -					json_nexthop, "interfaceName", -					ifindex2ifname(nexthop->ifindex, -						       nexthop->vrf_id)); -				break; -			case NEXTHOP_TYPE_BLACKHOLE: -				json_object_boolean_true_add(json_nexthop, -							     "unreachable"); -				switch (nexthop->bh_type) { -				case BLACKHOLE_REJECT: -					json_object_boolean_true_add( -						json_nexthop, "reject"); -					break; -				case BLACKHOLE_ADMINPROHIB: -					json_object_boolean_true_add( -						json_nexthop, -						"admin-prohibited"); -					break; -				case BLACKHOLE_NULL: -					json_object_boolean_true_add( -						json_nexthop, "blackhole"); -					break; -				case BLACKHOLE_UNSPEC: -					break; -				} -				break; -			default: -				break; -			} +		if (nhg) { +			json_nexthops = json_object_new_array(); -			if ((nexthop->vrf_id != re->vrf_id) -			     && (nexthop->type != NEXTHOP_TYPE_BLACKHOLE)) { -				vrf = vrf_lookup_by_id(nexthop->vrf_id); -				json_object_string_add(json_nexthop, "vrf", -						       vrf->name); -			} -			if (CHECK_FLAG(nexthop->flags, NEXTHOP_FLAG_DUPLICATE)) -				json_object_boolean_true_add(json_nexthop, -							     "duplicate"); - -			if (CHECK_FLAG(nexthop->flags, NEXTHOP_FLAG_ACTIVE)) -				json_object_boolean_true_add(json_nexthop, -							     "active"); - -			if (CHECK_FLAG(nexthop->flags, NEXTHOP_FLAG_ONLINK)) -				json_object_boolean_true_add(json_nexthop, -							     "onLink"); - -			if (CHECK_FLAG(nexthop->flags, NEXTHOP_FLAG_RECURSIVE)) -				json_object_boolean_true_add(json_nexthop, -							     "recursive"); - -			switch (nexthop->type) { -			case NEXTHOP_TYPE_IPV4: -			case NEXTHOP_TYPE_IPV4_IFINDEX: -				if (nexthop->src.ipv4.s_addr) { -					if (inet_ntop(AF_INET, -						      &nexthop->src.ipv4, buf, -						      sizeof(buf))) -						json_object_string_add( -							json_nexthop, "source", -							buf); -				} -				break; -			case NEXTHOP_TYPE_IPV6: -			case NEXTHOP_TYPE_IPV6_IFINDEX: -				if (!IPV6_ADDR_SAME(&nexthop->src.ipv6, -						    &in6addr_any)) { -					if (inet_ntop(AF_INET6, -						      &nexthop->src.ipv6, buf, -						      sizeof(buf))) -						json_object_string_add( -							json_nexthop, "source", -							buf); -				} -				break; -			default: -				break; -			} +			for (ALL_NEXTHOPS_PTR(nhg, nexthop)) { +				json_nexthop = json_object_new_object(); -			if (nexthop->nh_label -			    && nexthop->nh_label->num_labels) { -				json_labels = json_object_new_array(); - -				for (int label_index = 0; -				     label_index -				     < nexthop->nh_label->num_labels; -				     label_index++) -					json_object_array_add( -						json_labels, -						json_object_new_int( -							nexthop->nh_label->label -								[label_index])); - -				json_object_object_add(json_nexthop, "labels", -						       json_labels); +				show_nexthop_json_helper(json_nexthop, +							 nexthop, re); +				json_object_array_add(json_nexthops, +						      json_nexthop);  			} -			if (nexthop->weight) -				json_object_int_add(json_nexthop, "weight", -						    nexthop->weight); - -			json_object_array_add(json_nexthops, json_nexthop); +			json_object_object_add(json_route, "backupNexthops", +					       json_nexthops);  		} -		json_object_object_add(json_route, "nexthops", json_nexthops);  		json_object_array_add(json, json_route);  		return;  	}  	/* Nexthop information. */ +	first_p = true;  	for (ALL_NEXTHOPS_PTR(nhg, nexthop)) { -		if (nexthop == nhg->nexthop) { +		if (first_p) { +			first_p = false; +  			/* Prefix information. */  			len = vty_out(vty, "%c", zebra_route_char(re->type));  			if (re->instance) @@ -808,40 +856,36 @@ static void vty_show_ip_route(struct vty *vty, struct route_node *rn,  		show_route_nexthop_helper(vty, re, nexthop); -		if (nexthop->weight) -			vty_out(vty, ", weight %u", nexthop->weight); -  		vty_out(vty, ", %s\n", up_str); +	} -		/* Check for backup info */ -		if (CHECK_FLAG(nexthop->flags, NEXTHOP_FLAG_HAS_BACKUP)) { -			struct nexthop *backup; -			int i; +	/* Check for backup info if present */ +	if (is_fib) +		nhg = rib_get_fib_backup_nhg(re); +	else +		nhg = zebra_nhg_get_backup_nhg(re->nhe); -			if (re->nhe->backup_info == NULL || -			    re->nhe->backup_info->nhe == NULL) -				continue; +	if (nhg == NULL) +		return; -			i = 0; -			for (ALL_NEXTHOPS(re->nhe->backup_info->nhe->nhg, -					  backup)) { -				if (i == nexthop->backup_idx) -					break; -				i++; -			} +	/* Print backup info */ +	for (ALL_NEXTHOPS_PTR(nhg, nexthop)) { +		bool star_p = false; -			/* Print useful backup info */ -			if (backup) { -				/* TODO -- install state is not accurate */ -				vty_out(vty, "   %*c [backup %d]", -					/*re_status_output_char(re, backup),*/ -					len - 3 + (2 * nexthop_level(nexthop)), -					' ', nexthop->backup_idx); -				show_route_nexthop_helper(vty, re, backup); -				vty_out(vty, "\n"); -			} -		} +		if (is_fib) +			star_p = CHECK_FLAG(nexthop->flags, NEXTHOP_FLAG_FIB); + +		/* TODO -- it'd be nice to be able to include +		 * the entire list of backups, *and* include the +		 * real installation state. +		 */ +		vty_out(vty, "  b%c %*c", +			(star_p ? '*' : ' '), +			len - 3 + (2 * nexthop_level(nexthop)),	' '); +		show_route_nexthop_helper(vty, re, nexthop); +		vty_out(vty, "\n");  	} +  }  static void vty_show_ip_route_detail_json(struct vty *vty,  | 
