mirror of
				https://git.proxmox.com/git/mirror_frr
				synced 2025-10-31 09:04:27 +00:00 
			
		
		
		
	Merge pull request #14089 from dmytroshytyi-6WIND/srv6_multiple_segs_sids
bgpd,doc,lib,sharpd,staticd,yang,zebra: SRv6 multiple segs SIDs
This commit is contained in:
		
						commit
						90d19d1489
					
				| @ -1557,17 +1557,17 @@ void bgp_zebra_announce(struct bgp_dest *dest, const struct prefix *p, | ||||
| 		api_nh->weight = nh_weight; | ||||
| 
 | ||||
| 		if (((mpinfo->attr->srv6_l3vpn && | ||||
| 		      !sid_zero(&mpinfo->attr->srv6_l3vpn->sid)) || | ||||
| 		      !sid_zero_ipv6(&mpinfo->attr->srv6_l3vpn->sid)) || | ||||
| 		     (mpinfo->attr->srv6_vpn && | ||||
| 		      !sid_zero(&mpinfo->attr->srv6_vpn->sid))) && | ||||
| 		      !sid_zero_ipv6(&mpinfo->attr->srv6_vpn->sid))) && | ||||
| 		    !is_evpn && bgp_is_valid_label(&labels[0])) { | ||||
| 			struct in6_addr *sid_tmp = | ||||
| 				mpinfo->attr->srv6_l3vpn | ||||
| 					? (&mpinfo->attr->srv6_l3vpn->sid) | ||||
| 					: (&mpinfo->attr->srv6_vpn->sid); | ||||
| 
 | ||||
| 			memcpy(&api_nh->seg6_segs, sid_tmp, | ||||
| 			       sizeof(api_nh->seg6_segs)); | ||||
| 			memcpy(&api_nh->seg6_segs[0], sid_tmp, | ||||
| 			       sizeof(api_nh->seg6_segs[0])); | ||||
| 
 | ||||
| 			if (mpinfo->attr->srv6_l3vpn && | ||||
| 			    mpinfo->attr->srv6_l3vpn->transposition_len != 0) { | ||||
| @ -1581,13 +1581,14 @@ void bgp_zebra_announce(struct bgp_dest *dest, const struct prefix *p, | ||||
| 					continue; | ||||
| 				} | ||||
| 
 | ||||
| 				transpose_sid(&api_nh->seg6_segs, nh_label, | ||||
| 				transpose_sid(&api_nh->seg6_segs[0], nh_label, | ||||
| 					      mpinfo->attr->srv6_l3vpn | ||||
| 						      ->transposition_offset, | ||||
| 					      mpinfo->attr->srv6_l3vpn | ||||
| 						      ->transposition_len); | ||||
| 			} | ||||
| 
 | ||||
| 			api_nh->seg_num = 1; | ||||
| 			SET_FLAG(api_nh->flags, ZAPI_NEXTHOP_FLAG_SEG6); | ||||
| 		} | ||||
| 
 | ||||
| @ -1704,7 +1705,7 @@ void bgp_zebra_announce(struct bgp_dest *dest, const struct prefix *p, | ||||
| 			if (CHECK_FLAG(api_nh->flags, ZAPI_NEXTHOP_FLAG_SEG6) && | ||||
| 			    !CHECK_FLAG(api_nh->flags, | ||||
| 					ZAPI_NEXTHOP_FLAG_EVPN)) { | ||||
| 				inet_ntop(AF_INET6, &api_nh->seg6_segs, | ||||
| 				inet_ntop(AF_INET6, &api_nh->seg6_segs[0], | ||||
| 					  sid_buf, sizeof(sid_buf)); | ||||
| 				snprintf(segs_buf, sizeof(segs_buf), "segs %s", | ||||
| 					 sid_buf); | ||||
|  | ||||
| @ -164,3 +164,23 @@ network 9.9.9.9/24: | ||||
| .. code-block:: frr | ||||
| 
 | ||||
|   ip route 9.9.9.9/24 6.6.6.6 color 123 | ||||
| 
 | ||||
| SRv6 Route Commands | ||||
| ==================== | ||||
| 
 | ||||
| It is possible to specify a static route for ipv6 prefixes using an SRv6 | ||||
| `segments` instruction. The `/` separator can be used to specify | ||||
| multiple segments instructions. | ||||
| 
 | ||||
| .. code-block:: frr | ||||
| 
 | ||||
|   ipv6 route X:X::X:X <X:X::X:X|nexthop> segments U:U::U:U/Y:Y::Y:Y/Z:Z::Z:Z | ||||
| 
 | ||||
| 
 | ||||
| :: | ||||
| 
 | ||||
|   router(config)# ipv6 route 2005::1/64 ens3 segments 2001:db8:aaaa::7/2002::4/2002::3/2002::2 | ||||
| 
 | ||||
|   router# show ipv6 route | ||||
|   [..] | ||||
|   S>* 2005::/64 [1/0] is directly connected, ens3, seg6 2001:db8:aaaa::7,2002::4,2002::3,2002::2, weight 1, 00:00:06 | ||||
|  | ||||
| @ -1,4 +1,4 @@ | ||||
| /* SPDX-License-Identifier: GPL-2.0+ WITH Linux-syscall-note */ | ||||
| // SPDX-License-Identifier: GPL-2.0+ WITH Linux-syscall-note
 | ||||
| /*
 | ||||
|  *  SR-IPv6 implementation | ||||
|  * | ||||
| @ -30,7 +30,7 @@ struct ipv6_sr_hdr { | ||||
| 	__u8	flags; | ||||
| 	__u16	tag; | ||||
| 
 | ||||
| 	struct in6_addr segments[0]; | ||||
| 	struct in6_addr segments[]; | ||||
| }; | ||||
| 
 | ||||
| #define SR6_FLAG1_PROTECTED	(1 << 6) | ||||
|  | ||||
							
								
								
									
										112
									
								
								lib/nexthop.c
									
									
									
									
									
								
							
							
						
						
									
										112
									
								
								lib/nexthop.c
									
									
									
									
									
								
							| @ -56,6 +56,7 @@ static int _nexthop_srv6_cmp(const struct nexthop *nh1, | ||||
| 			     const struct nexthop *nh2) | ||||
| { | ||||
| 	int ret = 0; | ||||
| 	int i = 0; | ||||
| 
 | ||||
| 	if (!nh1->nh_srv6 && !nh2->nh_srv6) | ||||
| 		return 0; | ||||
| @ -78,9 +79,26 @@ static int _nexthop_srv6_cmp(const struct nexthop *nh1, | ||||
| 	if (ret != 0) | ||||
| 		return ret; | ||||
| 
 | ||||
| 	ret = memcmp(&nh1->nh_srv6->seg6_segs, | ||||
| 		     &nh2->nh_srv6->seg6_segs, | ||||
| 		     sizeof(struct in6_addr)); | ||||
| 	if (!nh1->nh_srv6->seg6_segs && !nh2->nh_srv6->seg6_segs) | ||||
| 		return 0; | ||||
| 
 | ||||
| 	if (!nh1->nh_srv6->seg6_segs && nh2->nh_srv6->seg6_segs) | ||||
| 		return -1; | ||||
| 
 | ||||
| 	if (nh1->nh_srv6->seg6_segs && !nh2->nh_srv6->seg6_segs) | ||||
| 		return 1; | ||||
| 
 | ||||
| 	if (nh1->nh_srv6->seg6_segs->num_segs != | ||||
| 	    nh2->nh_srv6->seg6_segs->num_segs) | ||||
| 		return -1; | ||||
| 
 | ||||
| 	for (i = 0; i < nh1->nh_srv6->seg6_segs->num_segs; i++) { | ||||
| 		ret = memcmp(&nh1->nh_srv6->seg6_segs->seg[i], | ||||
| 			     &nh2->nh_srv6->seg6_segs->seg[i], | ||||
| 			     sizeof(struct in6_addr)); | ||||
| 		if (ret != 0) | ||||
| 			break; | ||||
| 	} | ||||
| 
 | ||||
| 	return ret; | ||||
| } | ||||
| @ -362,7 +380,7 @@ struct nexthop *nexthop_new(void) | ||||
| 	 * The linux kernel does some weird stuff with adding +1 to | ||||
| 	 * all nexthop weights it gets over netlink. | ||||
| 	 * To handle this, just default everything to 1 right from | ||||
| 	 * from the beginning so we don't have to special case | ||||
| 	 * the beginning so we don't have to special case | ||||
| 	 * default weights in the linux netlink code. | ||||
| 	 * | ||||
| 	 * 1 should be a valid on all platforms anyway. | ||||
| @ -568,15 +586,25 @@ void nexthop_del_srv6_seg6local(struct nexthop *nexthop) | ||||
| 	if (!nexthop->nh_srv6) | ||||
| 		return; | ||||
| 
 | ||||
| 	if (nexthop->nh_srv6->seg6local_action == ZEBRA_SEG6_LOCAL_ACTION_UNSPEC) | ||||
| 		return; | ||||
| 
 | ||||
| 	nexthop->nh_srv6->seg6local_action = ZEBRA_SEG6_LOCAL_ACTION_UNSPEC; | ||||
| 
 | ||||
| 	if (sid_zero(&nexthop->nh_srv6->seg6_segs)) | ||||
| 	if (nexthop->nh_srv6->seg6_segs && | ||||
| 	    (nexthop->nh_srv6->seg6_segs->num_segs == 0 || | ||||
| 	     sid_zero(nexthop->nh_srv6->seg6_segs))) | ||||
| 		XFREE(MTYPE_NH_SRV6, nexthop->nh_srv6->seg6_segs); | ||||
| 
 | ||||
| 	if (nexthop->nh_srv6->seg6_segs == NULL) | ||||
| 		XFREE(MTYPE_NH_SRV6, nexthop->nh_srv6); | ||||
| } | ||||
| 
 | ||||
| void nexthop_add_srv6_seg6(struct nexthop *nexthop, | ||||
| 			   const struct in6_addr *segs) | ||||
| void nexthop_add_srv6_seg6(struct nexthop *nexthop, const struct in6_addr *segs, | ||||
| 			   int num_segs) | ||||
| { | ||||
| 	int i; | ||||
| 
 | ||||
| 	if (!segs) | ||||
| 		return; | ||||
| 
 | ||||
| @ -584,7 +612,22 @@ void nexthop_add_srv6_seg6(struct nexthop *nexthop, | ||||
| 		nexthop->nh_srv6 = XCALLOC(MTYPE_NH_SRV6, | ||||
| 					   sizeof(struct nexthop_srv6)); | ||||
| 
 | ||||
| 	nexthop->nh_srv6->seg6_segs = *segs; | ||||
| 	/* Enforce limit on srv6 seg stack size */ | ||||
| 	if (num_segs > SRV6_MAX_SIDS) | ||||
| 		num_segs = SRV6_MAX_SIDS; | ||||
| 
 | ||||
| 	if (!nexthop->nh_srv6->seg6_segs) { | ||||
| 		nexthop->nh_srv6->seg6_segs = | ||||
| 			XCALLOC(MTYPE_NH_SRV6, | ||||
| 				sizeof(struct seg6_seg_stack) + | ||||
| 					num_segs * sizeof(struct in6_addr)); | ||||
| 	} | ||||
| 
 | ||||
| 	nexthop->nh_srv6->seg6_segs->num_segs = num_segs; | ||||
| 
 | ||||
| 	for (i = 0; i < num_segs; i++) | ||||
| 		memcpy(&nexthop->nh_srv6->seg6_segs->seg[i], &segs[i], | ||||
| 		       sizeof(struct in6_addr)); | ||||
| } | ||||
| 
 | ||||
| void nexthop_del_srv6_seg6(struct nexthop *nexthop) | ||||
| @ -592,12 +635,14 @@ void nexthop_del_srv6_seg6(struct nexthop *nexthop) | ||||
| 	if (!nexthop->nh_srv6) | ||||
| 		return; | ||||
| 
 | ||||
| 	memset(&nexthop->nh_srv6->seg6_segs, 0, | ||||
| 	       sizeof(nexthop->nh_srv6->seg6_segs)); | ||||
| 
 | ||||
| 	if (nexthop->nh_srv6->seg6local_action == | ||||
| 	    ZEBRA_SEG6_LOCAL_ACTION_UNSPEC) | ||||
| 		XFREE(MTYPE_NH_SRV6, nexthop->nh_srv6); | ||||
| 	if (nexthop->nh_srv6->seg6local_action == ZEBRA_SEG6_LOCAL_ACTION_UNSPEC && | ||||
| 			nexthop->nh_srv6->seg6_segs) { | ||||
| 		memset(nexthop->nh_srv6->seg6_segs->seg, 0, | ||||
| 		       sizeof(struct in6_addr) * | ||||
| 			       nexthop->nh_srv6->seg6_segs->num_segs); | ||||
| 		XFREE(MTYPE_NH_SRV6, nexthop->nh_srv6->seg6_segs); | ||||
| 	} | ||||
| 	XFREE(MTYPE_NH_SRV6, nexthop->nh_srv6); | ||||
| } | ||||
| 
 | ||||
| const char *nexthop2str(const struct nexthop *nexthop, char *str, int size) | ||||
| @ -743,11 +788,32 @@ uint32_t nexthop_hash_quick(const struct nexthop *nexthop) | ||||
| 	} | ||||
| 
 | ||||
| 	if (nexthop->nh_srv6) { | ||||
| 		key = jhash_1word(nexthop->nh_srv6->seg6local_action, key); | ||||
| 		key = jhash(&nexthop->nh_srv6->seg6local_ctx, | ||||
| 			    sizeof(nexthop->nh_srv6->seg6local_ctx), key); | ||||
| 		key = jhash(&nexthop->nh_srv6->seg6_segs, | ||||
| 			    sizeof(nexthop->nh_srv6->seg6_segs), key); | ||||
| 		int segs_num = 0; | ||||
| 		int i = 0; | ||||
| 
 | ||||
| 		if (nexthop->nh_srv6->seg6local_action != | ||||
| 		    ZEBRA_SEG6_LOCAL_ACTION_UNSPEC) { | ||||
| 			key = jhash_1word(nexthop->nh_srv6->seg6local_action, | ||||
| 					  key); | ||||
| 			key = jhash(&nexthop->nh_srv6->seg6local_ctx, | ||||
| 				    sizeof(nexthop->nh_srv6->seg6local_ctx), | ||||
| 				    key); | ||||
| 			if (nexthop->nh_srv6->seg6_segs) | ||||
| 				key = jhash(&nexthop->nh_srv6->seg6_segs->seg[0], | ||||
| 					    sizeof(struct in6_addr), key); | ||||
| 		} else { | ||||
| 			if (nexthop->nh_srv6->seg6_segs) { | ||||
| 				segs_num = nexthop->nh_srv6->seg6_segs->num_segs; | ||||
| 				while (segs_num >= 1) { | ||||
| 					key = jhash(&nexthop->nh_srv6->seg6_segs | ||||
| 							    ->seg[i], | ||||
| 						    sizeof(struct in6_addr), | ||||
| 						    key); | ||||
| 					segs_num -= 1; | ||||
| 					i += 1; | ||||
| 				} | ||||
| 			} | ||||
| 		} | ||||
| 	} | ||||
| 
 | ||||
| 	return key; | ||||
| @ -810,9 +876,13 @@ void nexthop_copy_no_recurse(struct nexthop *copy, | ||||
| 			nexthop_add_srv6_seg6local(copy, | ||||
| 				nexthop->nh_srv6->seg6local_action, | ||||
| 				&nexthop->nh_srv6->seg6local_ctx); | ||||
| 		if (!sid_zero(&nexthop->nh_srv6->seg6_segs)) | ||||
| 		if (nexthop->nh_srv6->seg6_segs && | ||||
| 		    nexthop->nh_srv6->seg6_segs->num_segs && | ||||
| 		    !sid_zero(nexthop->nh_srv6->seg6_segs)) | ||||
| 			nexthop_add_srv6_seg6(copy, | ||||
| 				&nexthop->nh_srv6->seg6_segs); | ||||
| 					      &nexthop->nh_srv6->seg6_segs->seg[0], | ||||
| 					      nexthop->nh_srv6->seg6_segs | ||||
| 						      ->num_segs); | ||||
| 	} | ||||
| } | ||||
| 
 | ||||
|  | ||||
| @ -157,8 +157,8 @@ void nexthop_del_labels(struct nexthop *); | ||||
| void nexthop_add_srv6_seg6local(struct nexthop *nexthop, uint32_t action, | ||||
| 				const struct seg6local_context *ctx); | ||||
| void nexthop_del_srv6_seg6local(struct nexthop *nexthop); | ||||
| void nexthop_add_srv6_seg6(struct nexthop *nexthop, | ||||
| 			   const struct in6_addr *segs); | ||||
| void nexthop_add_srv6_seg6(struct nexthop *nexthop, const struct in6_addr *seg, | ||||
| 			   int num_segs); | ||||
| void nexthop_del_srv6_seg6(struct nexthop *nexthop); | ||||
| 
 | ||||
| /*
 | ||||
|  | ||||
							
								
								
									
										29
									
								
								lib/srv6.h
									
									
									
									
									
								
							
							
						
						
									
										29
									
								
								lib/srv6.h
									
									
									
									
									
								
							| @ -14,8 +14,11 @@ | ||||
| #include <arpa/inet.h> | ||||
| #include <netinet/in.h> | ||||
| 
 | ||||
| #define SRV6_MAX_SIDS 16 | ||||
| #define SRV6_MAX_SIDS	  16 | ||||
| #define SRV6_MAX_SEGS	  8 | ||||
| #define SRV6_LOCNAME_SIZE 256 | ||||
| #define SRH_BASE_HEADER_LENGTH 8 | ||||
| #define SRH_SEGMENT_LENGTH     16 | ||||
| 
 | ||||
| #ifdef __cplusplus | ||||
| extern "C" { | ||||
| @ -74,6 +77,8 @@ enum seg6local_flavor_op { | ||||
| 	ZEBRA_SEG6_LOCAL_FLV_OP_NEXT_CSID    = 4, | ||||
| }; | ||||
| 
 | ||||
| #define SRV6_SEG_STRLEN 1024 | ||||
| 
 | ||||
| struct seg6_segs { | ||||
| 	size_t num_segs; | ||||
| 	struct in6_addr segs[256]; | ||||
| @ -89,6 +94,11 @@ struct seg6local_flavors_info { | ||||
| 	uint8_t lcnode_func_len; | ||||
| }; | ||||
| 
 | ||||
| struct seg6_seg_stack { | ||||
| 	uint8_t num_segs; | ||||
| 	struct in6_addr seg[0]; /* 1 or more segs */ | ||||
| }; | ||||
| 
 | ||||
| struct seg6local_context { | ||||
| 	struct in_addr nh4; | ||||
| 	struct in6_addr nh6; | ||||
| @ -170,7 +180,7 @@ struct nexthop_srv6 { | ||||
| 	struct seg6local_context seg6local_ctx; | ||||
| 
 | ||||
| 	/* SRv6 Headend-behaviour */ | ||||
| 	struct in6_addr seg6_segs; | ||||
| 	struct seg6_seg_stack *seg6_segs; | ||||
| }; | ||||
| 
 | ||||
| static inline const char *seg6_mode2str(enum seg6_mode_t mode) | ||||
| @ -206,12 +216,21 @@ static inline bool sid_diff( | ||||
| 	return !sid_same(a, b); | ||||
| } | ||||
| 
 | ||||
| static inline bool sid_zero( | ||||
| 		const struct in6_addr *a) | ||||
| 
 | ||||
| static inline bool sid_zero(const struct seg6_seg_stack *a) | ||||
| { | ||||
| 	struct in6_addr zero = {}; | ||||
| 
 | ||||
| 	return sid_same(a, &zero); | ||||
| 	assert(a); | ||||
| 
 | ||||
| 	return sid_same(&a->seg[0], &zero); | ||||
| } | ||||
| 
 | ||||
| static inline bool sid_zero_ipv6(const struct in6_addr *a) | ||||
| { | ||||
| 	struct in6_addr zero = {}; | ||||
| 
 | ||||
| 	return sid_same(&a[0], &zero); | ||||
| } | ||||
| 
 | ||||
| static inline void *sid_copy(struct in6_addr *dst, | ||||
|  | ||||
| @ -1061,10 +1061,11 @@ int zapi_nexthop_encode(struct stream *s, const struct zapi_nexthop *api_nh, | ||||
| 			     sizeof(struct seg6local_context)); | ||||
| 	} | ||||
| 
 | ||||
| 	if (CHECK_FLAG(nh_flags, ZAPI_NEXTHOP_FLAG_SEG6)) | ||||
| 		stream_write(s, &api_nh->seg6_segs, | ||||
| 			     sizeof(struct in6_addr)); | ||||
| 
 | ||||
| 	if (CHECK_FLAG(nh_flags, ZAPI_NEXTHOP_FLAG_SEG6)) { | ||||
| 		stream_putc(s, api_nh->seg_num); | ||||
| 		stream_put(s, &api_nh->seg6_segs[0], | ||||
| 			   api_nh->seg_num * sizeof(struct in6_addr)); | ||||
| 	} | ||||
| done: | ||||
| 	return ret; | ||||
| } | ||||
| @ -1430,9 +1431,18 @@ int zapi_nexthop_decode(struct stream *s, struct zapi_nexthop *api_nh, | ||||
| 			   sizeof(struct seg6local_context)); | ||||
| 	} | ||||
| 
 | ||||
| 	if (CHECK_FLAG(api_nh->flags, ZAPI_NEXTHOP_FLAG_SEG6)) | ||||
| 		STREAM_GET(&api_nh->seg6_segs, s, | ||||
| 			   sizeof(struct in6_addr)); | ||||
| 	if (CHECK_FLAG(api_nh->flags, ZAPI_NEXTHOP_FLAG_SEG6)) { | ||||
| 		STREAM_GETC(s, api_nh->seg_num); | ||||
| 		if (api_nh->seg_num > SRV6_MAX_SIDS) { | ||||
| 			flog_err(EC_LIB_ZAPI_ENCODE, | ||||
| 				 "%s: invalid number of SRv6 Segs (%u)", | ||||
| 				 __func__, api_nh->seg_num); | ||||
| 			return -1; | ||||
| 		} | ||||
| 
 | ||||
| 		STREAM_GET(&api_nh->seg6_segs[0], s, | ||||
| 			   api_nh->seg_num * sizeof(struct in6_addr)); | ||||
| 	} | ||||
| 
 | ||||
| 	/* Success */ | ||||
| 	ret = 0; | ||||
| @ -2132,8 +2142,8 @@ struct nexthop *nexthop_from_zapi_nexthop(const struct zapi_nexthop *znh) | ||||
| 		nexthop_add_srv6_seg6local(n, znh->seg6local_action, | ||||
| 					   &znh->seg6local_ctx); | ||||
| 
 | ||||
| 	if (!sid_zero(&znh->seg6_segs)) | ||||
| 		nexthop_add_srv6_seg6(n, &znh->seg6_segs); | ||||
| 	if (znh->seg_num && !sid_zero_ipv6(znh->seg6_segs)) | ||||
| 		nexthop_add_srv6_seg6(n, &znh->seg6_segs[0], znh->seg_num); | ||||
| 
 | ||||
| 	return n; | ||||
| } | ||||
| @ -2193,10 +2203,14 @@ int zapi_nexthop_from_nexthop(struct zapi_nexthop *znh, | ||||
| 			       sizeof(struct seg6local_context)); | ||||
| 		} | ||||
| 
 | ||||
| 		if (!sid_zero(&nh->nh_srv6->seg6_segs)) { | ||||
| 		if (nh->nh_srv6->seg6_segs && nh->nh_srv6->seg6_segs->num_segs && | ||||
| 		    !sid_zero(nh->nh_srv6->seg6_segs)) { | ||||
| 			SET_FLAG(znh->flags, ZAPI_NEXTHOP_FLAG_SEG6); | ||||
| 			memcpy(&znh->seg6_segs, &nh->nh_srv6->seg6_segs, | ||||
| 			       sizeof(struct in6_addr)); | ||||
| 			znh->seg_num = nh->nh_srv6->seg6_segs->num_segs; | ||||
| 			for (i = 0; i < nh->nh_srv6->seg6_segs->num_segs; i++) | ||||
| 				memcpy(&znh->seg6_segs[i], | ||||
| 				       &nh->nh_srv6->seg6_segs->seg[i], | ||||
| 				       sizeof(struct in6_addr)); | ||||
| 		} | ||||
| 	} | ||||
| 
 | ||||
|  | ||||
| @ -438,7 +438,8 @@ struct zapi_nexthop { | ||||
| 	struct seg6local_context seg6local_ctx; | ||||
| 
 | ||||
| 	/* SRv6 Headend-behaviour */ | ||||
| 	struct in6_addr seg6_segs; | ||||
| 	int seg_num; | ||||
| 	struct in6_addr seg6_segs[SRV6_MAX_SEGS]; | ||||
| }; | ||||
| 
 | ||||
| /*
 | ||||
|  | ||||
| @ -402,7 +402,7 @@ DEFPY (install_seg6_routes, | ||||
| 	sg.r.nhop.gate.ipv6 = seg6_nh6; | ||||
| 	sg.r.nhop.vrf_id = vrf->vrf_id; | ||||
| 	sg.r.nhop_group.nexthop = &sg.r.nhop; | ||||
| 	nexthop_add_srv6_seg6(&sg.r.nhop, &seg6_seg); | ||||
| 	nexthop_add_srv6_seg6(&sg.r.nhop, &seg6_seg, 1); | ||||
| 
 | ||||
| 	sg.r.vrf_id = vrf->vrf_id; | ||||
| 	sharp_install_routes_helper(&prefix, sg.r.vrf_id, sg.r.inst, 0, | ||||
|  | ||||
| @ -74,6 +74,21 @@ const struct frr_yang_module_info frr_staticd_info = { | ||||
| 				.destroy = routing_control_plane_protocols_control_plane_protocol_staticd_route_list_path_list_frr_nexthops_nexthop_color_destroy, | ||||
| 			} | ||||
| 		}, | ||||
| 		{ | ||||
| 			.xpath = "/frr-routing:routing/control-plane-protocols/control-plane-protocol/frr-staticd:staticd/route-list/path-list/frr-nexthops/nexthop/srv6-segs-stack/entry", | ||||
| 			.cbs = { | ||||
| 				.create = routing_control_plane_protocols_control_plane_protocol_staticd_route_list_path_list_frr_nexthops_nexthop_srv6_segs_stack_entry_create, | ||||
| 				.destroy = routing_control_plane_protocols_control_plane_protocol_staticd_route_list_path_list_frr_nexthops_nexthop_srv6_segs_stack_entry_destroy, | ||||
| 
 | ||||
| 			} | ||||
| 		}, | ||||
| 		{ | ||||
| 			.xpath = "/frr-routing:routing/control-plane-protocols/control-plane-protocol/frr-staticd:staticd/route-list/path-list/frr-nexthops/nexthop/srv6-segs-stack/entry/seg", | ||||
| 			.cbs = { | ||||
| 				.modify = routing_control_plane_protocols_control_plane_protocol_staticd_route_list_path_list_frr_nexthops_nexthop_srv6_segs_stack_entry_seg_modify, | ||||
| 				.destroy = routing_control_plane_protocols_control_plane_protocol_staticd_route_list_path_list_frr_nexthops_nexthop_srv6_segs_stack_entry_seg_destroy, | ||||
| 			} | ||||
| 		}, | ||||
| 		{ | ||||
| 			.xpath = "/frr-routing:routing/control-plane-protocols/control-plane-protocol/frr-staticd:staticd/route-list/path-list/frr-nexthops/nexthop/mpls-label-stack/entry", | ||||
| 			.cbs = { | ||||
| @ -182,6 +197,20 @@ const struct frr_yang_module_info frr_staticd_info = { | ||||
| 				.destroy = routing_control_plane_protocols_control_plane_protocol_staticd_route_list_src_list_path_list_frr_nexthops_nexthop_color_destroy, | ||||
| 			} | ||||
| 		}, | ||||
| 		{ | ||||
| 			.xpath = "/frr-routing:routing/control-plane-protocols/control-plane-protocol/frr-staticd:staticd/route-list/src-list/path-list/frr-nexthops/nexthop/srv6-segs-stack/entry", | ||||
| 			.cbs = { | ||||
| 				.create = routing_control_plane_protocols_control_plane_protocol_staticd_route_list_src_list_path_list_frr_nexthops_nexthop_srv6_segs_stack_entry_create, | ||||
| 				.destroy = routing_control_plane_protocols_control_plane_protocol_staticd_route_list_src_list_path_list_frr_nexthops_nexthop_srv6_segs_stack_entry_destroy, | ||||
| 			} | ||||
| 		}, | ||||
| 		{ | ||||
| 			.xpath = "/frr-routing:routing/control-plane-protocols/control-plane-protocol/frr-staticd:staticd/route-list/src-list/path-list/frr-nexthops/nexthop/srv6-segs-stack/entry/seg", | ||||
| 			.cbs = { | ||||
| 				.modify = routing_control_plane_protocols_control_plane_protocol_staticd_route_list_src_list_path_list_frr_nexthops_nexthop_srv6_segs_stack_entry_seg_modify, | ||||
| 				.destroy = routing_control_plane_protocols_control_plane_protocol_staticd_route_list_src_list_path_list_frr_nexthops_nexthop_srv6_segs_stack_entry_seg_destroy, | ||||
| 			} | ||||
| 		}, | ||||
| 		{ | ||||
| 			.xpath = "/frr-routing:routing/control-plane-protocols/control-plane-protocol/frr-staticd:staticd/route-list/src-list/path-list/frr-nexthops/nexthop/mpls-label-stack/entry", | ||||
| 			.cbs = { | ||||
|  | ||||
| @ -35,6 +35,14 @@ int routing_control_plane_protocols_control_plane_protocol_staticd_route_list_pa | ||||
| 	struct nb_cb_modify_args *args); | ||||
| int routing_control_plane_protocols_control_plane_protocol_staticd_route_list_path_list_frr_nexthops_nexthop_color_destroy( | ||||
| 	struct nb_cb_destroy_args *args); | ||||
| int routing_control_plane_protocols_control_plane_protocol_staticd_route_list_path_list_frr_nexthops_nexthop_srv6_segs_stack_entry_create( | ||||
| 	struct nb_cb_create_args *args); | ||||
| int routing_control_plane_protocols_control_plane_protocol_staticd_route_list_path_list_frr_nexthops_nexthop_srv6_segs_stack_entry_destroy( | ||||
| 	struct nb_cb_destroy_args *args); | ||||
| int routing_control_plane_protocols_control_plane_protocol_staticd_route_list_path_list_frr_nexthops_nexthop_srv6_segs_stack_entry_seg_modify( | ||||
| 	struct nb_cb_modify_args *args); | ||||
| int routing_control_plane_protocols_control_plane_protocol_staticd_route_list_path_list_frr_nexthops_nexthop_srv6_segs_stack_entry_seg_destroy( | ||||
| 	struct nb_cb_destroy_args *args); | ||||
| int routing_control_plane_protocols_control_plane_protocol_staticd_route_list_path_list_frr_nexthops_nexthop_mpls_label_stack_entry_create( | ||||
| 	struct nb_cb_create_args *args); | ||||
| int routing_control_plane_protocols_control_plane_protocol_staticd_route_list_path_list_frr_nexthops_nexthop_mpls_label_stack_entry_destroy( | ||||
| @ -80,6 +88,14 @@ int routing_control_plane_protocols_control_plane_protocol_staticd_route_list_sr | ||||
| 	struct nb_cb_modify_args *args); | ||||
| int routing_control_plane_protocols_control_plane_protocol_staticd_route_list_src_list_path_list_frr_nexthops_nexthop_color_destroy( | ||||
| 	struct nb_cb_destroy_args *args); | ||||
| int routing_control_plane_protocols_control_plane_protocol_staticd_route_list_src_list_path_list_frr_nexthops_nexthop_srv6_segs_stack_entry_create( | ||||
| 	struct nb_cb_create_args *args); | ||||
| int routing_control_plane_protocols_control_plane_protocol_staticd_route_list_src_list_path_list_frr_nexthops_nexthop_srv6_segs_stack_entry_destroy( | ||||
| 	struct nb_cb_destroy_args *args); | ||||
| int routing_control_plane_protocols_control_plane_protocol_staticd_route_list_src_list_path_list_frr_nexthops_nexthop_srv6_segs_stack_entry_seg_modify( | ||||
| 	struct nb_cb_modify_args *args); | ||||
| int routing_control_plane_protocols_control_plane_protocol_staticd_route_list_src_list_path_list_frr_nexthops_nexthop_srv6_segs_stack_entry_seg_destroy( | ||||
| 	struct nb_cb_destroy_args *args); | ||||
| int routing_control_plane_protocols_control_plane_protocol_staticd_route_list_src_list_path_list_frr_nexthops_nexthop_mpls_label_stack_entry_create( | ||||
| 	struct nb_cb_create_args *args); | ||||
| int routing_control_plane_protocols_control_plane_protocol_staticd_route_list_src_list_path_list_frr_nexthops_nexthop_mpls_label_stack_entry_destroy( | ||||
| @ -147,6 +163,10 @@ int routing_control_plane_protocols_name_validate( | ||||
| 
 | ||||
| #define FRR_STATIC_ROUTE_NHLB_KEY_XPATH "/entry[id='%u']/label" | ||||
| 
 | ||||
| #define FRR_STATIC_ROUTE_NH_SRV6_SEGS_XPATH "/srv6-segs-stack" | ||||
| 
 | ||||
| #define FRR_STATIC_ROUTE_NH_SRV6_KEY_SEG_XPATH "/entry[id='%u']/seg" | ||||
| 
 | ||||
| /* route-list/srclist */ | ||||
| #define FRR_S_ROUTE_SRC_INFO_KEY_XPATH                                         \ | ||||
| 	"/frr-routing:routing/control-plane-protocols/"                        \ | ||||
|  | ||||
| @ -209,6 +209,98 @@ static bool static_nexthop_destroy(struct nb_cb_destroy_args *args) | ||||
| 	return NB_OK; | ||||
| } | ||||
| 
 | ||||
| static int nexthop_srv6_segs_stack_entry_create(struct nb_cb_create_args *args) | ||||
| { | ||||
| 	struct static_nexthop *nh; | ||||
| 	uint32_t pos; | ||||
| 	uint8_t index; | ||||
| 
 | ||||
| 	switch (args->event) { | ||||
| 	case NB_EV_VALIDATE: | ||||
| 	case NB_EV_PREPARE: | ||||
| 	case NB_EV_ABORT: | ||||
| 		break; | ||||
| 	case NB_EV_APPLY: | ||||
| 		nh = nb_running_get_entry(args->dnode, NULL, true); | ||||
| 		pos = yang_get_list_pos(args->dnode); | ||||
| 		if (!pos) { | ||||
| 			flog_warn(EC_LIB_NB_CB_CONFIG_APPLY, | ||||
| 				  "libyang returns invalid seg position"); | ||||
| 			return NB_ERR; | ||||
| 		} | ||||
| 		/* Mapping to array = list-index -1 */ | ||||
| 		index = pos - 1; | ||||
| 		memset(&nh->snh_seg.seg[index], 0, sizeof(struct in6_addr)); | ||||
| 		nh->snh_seg.num_segs++; | ||||
| 		break; | ||||
| 	} | ||||
| 
 | ||||
| 	return NB_OK; | ||||
| } | ||||
| 
 | ||||
| static int nexthop_srv6_segs_stack_entry_destroy(struct nb_cb_destroy_args *args) | ||||
| { | ||||
| 	struct static_nexthop *nh; | ||||
| 	uint32_t pos; | ||||
| 	uint8_t index; | ||||
| 	int old_num_segs; | ||||
| 
 | ||||
| 	switch (args->event) { | ||||
| 	case NB_EV_VALIDATE: | ||||
| 	case NB_EV_PREPARE: | ||||
| 	case NB_EV_ABORT: | ||||
| 		break; | ||||
| 	case NB_EV_APPLY: | ||||
| 		nh = nb_running_get_entry(args->dnode, NULL, true); | ||||
| 		pos = yang_get_list_pos(args->dnode); | ||||
| 		if (!pos) { | ||||
| 			flog_warn(EC_LIB_NB_CB_CONFIG_APPLY, | ||||
| 				  "libyang returns invalid seg position"); | ||||
| 			return NB_ERR; | ||||
| 		} | ||||
| 		index = pos - 1; | ||||
| 		old_num_segs = nh->snh_seg.num_segs; | ||||
| 		memset(&nh->snh_seg.seg[index], 0, sizeof(struct in6_addr)); | ||||
| 		nh->snh_seg.num_segs--; | ||||
| 
 | ||||
| 		if (old_num_segs != nh->snh_seg.num_segs) | ||||
| 			nh->state = STATIC_START; | ||||
| 		break; | ||||
| 	} | ||||
| 
 | ||||
| 	return NB_OK; | ||||
| } | ||||
| 
 | ||||
| static int static_nexthop_srv6_segs_modify(struct nb_cb_modify_args *args) | ||||
| { | ||||
| 	struct static_nexthop *nh; | ||||
| 	uint32_t pos; | ||||
| 	uint8_t index; | ||||
| 	struct in6_addr old_seg; | ||||
| 	struct in6_addr cli_seg; | ||||
| 
 | ||||
| 	nh = nb_running_get_entry(args->dnode, NULL, true); | ||||
| 	pos = yang_get_list_pos(lyd_parent(args->dnode)); | ||||
| 	if (!pos) { | ||||
| 		flog_warn(EC_LIB_NB_CB_CONFIG_APPLY, | ||||
| 			  "libyang returns invalid seg position"); | ||||
| 		return NB_ERR; | ||||
| 	} | ||||
| 	/* Mapping to array = list-index -1 */ | ||||
| 	index = pos - 1; | ||||
| 
 | ||||
| 	old_seg = nh->snh_seg.seg[index]; | ||||
| 	yang_dnode_get_ipv6(&cli_seg, args->dnode, NULL); | ||||
| 
 | ||||
| 	memcpy(&nh->snh_seg.seg[index], &cli_seg, sizeof(struct in6_addr)); | ||||
| 
 | ||||
| 	if (memcmp(&old_seg, &nh->snh_seg.seg[index], | ||||
| 		   sizeof(struct in6_addr)) != 0) | ||||
| 		nh->state = STATIC_START; | ||||
| 
 | ||||
| 	return NB_OK; | ||||
| } | ||||
| 
 | ||||
| static int nexthop_mpls_label_stack_entry_create(struct nb_cb_create_args *args) | ||||
| { | ||||
| 	struct static_nexthop *nh; | ||||
| @ -641,6 +733,60 @@ int routing_control_plane_protocols_control_plane_protocol_staticd_route_list_pa | ||||
| 	return NB_OK; | ||||
| } | ||||
| 
 | ||||
| /*
 | ||||
|  * XPath: | ||||
|  * /frr-routing:routing/control-plane-protocols/control-plane-protocol/frr-staticd:staticd/route-list/path-list/frr-nexthops/nexthop/srv6-segs-stack/entry | ||||
|  */ | ||||
| int routing_control_plane_protocols_control_plane_protocol_staticd_route_list_path_list_frr_nexthops_nexthop_srv6_segs_stack_entry_create( | ||||
| 	struct nb_cb_create_args *args) | ||||
| { | ||||
| 	return nexthop_srv6_segs_stack_entry_create(args); | ||||
| } | ||||
| 
 | ||||
| int routing_control_plane_protocols_control_plane_protocol_staticd_route_list_path_list_frr_nexthops_nexthop_srv6_segs_stack_entry_destroy( | ||||
| 	struct nb_cb_destroy_args *args) | ||||
| { | ||||
| 	return nexthop_srv6_segs_stack_entry_destroy(args); | ||||
| } | ||||
| 
 | ||||
| /*
 | ||||
|  * XPath: | ||||
|  * /frr-routing:routing/control-plane-protocols/control-plane-protocol/frr-staticd:staticd/route-list/path-list/frr-nexthops/nexthop/srv6-segs-stack/entry/seg | ||||
|  */ | ||||
| int routing_control_plane_protocols_control_plane_protocol_staticd_route_list_path_list_frr_nexthops_nexthop_srv6_segs_stack_entry_seg_modify( | ||||
| 	struct nb_cb_modify_args *args) | ||||
| { | ||||
| 	switch (args->event) { | ||||
| 	case NB_EV_VALIDATE: | ||||
| 	case NB_EV_PREPARE: | ||||
| 	case NB_EV_ABORT: | ||||
| 		break; | ||||
| 	case NB_EV_APPLY: | ||||
| 		if (static_nexthop_srv6_segs_modify(args) != NB_OK) | ||||
| 			return NB_ERR; | ||||
| 		break; | ||||
| 	} | ||||
| 	return NB_OK; | ||||
| } | ||||
| 
 | ||||
| int routing_control_plane_protocols_control_plane_protocol_staticd_route_list_path_list_frr_nexthops_nexthop_srv6_segs_stack_entry_seg_destroy( | ||||
| 	struct nb_cb_destroy_args *args) | ||||
| { | ||||
| 	/*
 | ||||
| 	 * No operation is required in this call back. | ||||
| 	 * nexthop_srv6_segs_stack_entry_destroy() will take care | ||||
| 	 * to reset the seg vaue. | ||||
| 	 */ | ||||
| 	switch (args->event) { | ||||
| 	case NB_EV_VALIDATE: | ||||
| 	case NB_EV_PREPARE: | ||||
| 	case NB_EV_ABORT: | ||||
| 	case NB_EV_APPLY: | ||||
| 		break; | ||||
| 	} | ||||
| 	return NB_OK; | ||||
| } | ||||
| 
 | ||||
| /*
 | ||||
|  * XPath: | ||||
|  * /frr-routing:routing/control-plane-protocols/control-plane-protocol/frr-staticd:staticd/route-list/path-list/frr-nexthops/nexthop/mpls-label-stack/entry | ||||
| @ -1019,6 +1165,60 @@ int routing_control_plane_protocols_control_plane_protocol_staticd_route_list_sr | ||||
| 	return NB_OK; | ||||
| } | ||||
| 
 | ||||
| /*
 | ||||
|  * XPath: | ||||
|  * /frr-routing:routing/control-plane-protocols/control-plane-protocol/frr-staticd:staticd/route-list/src-list/path-list/frr-nexthops/nexthop/srv6-segs-stack/entry | ||||
|  */ | ||||
| int routing_control_plane_protocols_control_plane_protocol_staticd_route_list_src_list_path_list_frr_nexthops_nexthop_srv6_segs_stack_entry_create( | ||||
| 	struct nb_cb_create_args *args) | ||||
| { | ||||
| 	return nexthop_srv6_segs_stack_entry_create(args); | ||||
| } | ||||
| 
 | ||||
| int routing_control_plane_protocols_control_plane_protocol_staticd_route_list_src_list_path_list_frr_nexthops_nexthop_srv6_segs_stack_entry_destroy( | ||||
| 	struct nb_cb_destroy_args *args) | ||||
| { | ||||
| 	return nexthop_srv6_segs_stack_entry_destroy(args); | ||||
| } | ||||
| 
 | ||||
| /*
 | ||||
|  * XPath: | ||||
|  * /frr-routing:routing/control-plane-protocols/control-plane-protocol/frr-staticd:staticd/route-list/src-list/path-list/frr-nexthops/nexthop/srv6-segs-stack/entry/seg | ||||
|  */ | ||||
| int routing_control_plane_protocols_control_plane_protocol_staticd_route_list_src_list_path_list_frr_nexthops_nexthop_srv6_segs_stack_entry_seg_modify( | ||||
| 	struct nb_cb_modify_args *args) | ||||
| { | ||||
| 	switch (args->event) { | ||||
| 	case NB_EV_VALIDATE: | ||||
| 	case NB_EV_PREPARE: | ||||
| 	case NB_EV_ABORT: | ||||
| 		break; | ||||
| 	case NB_EV_APPLY: | ||||
| 		if (static_nexthop_srv6_segs_modify(args) != NB_OK) | ||||
| 			return NB_ERR; | ||||
| 		break; | ||||
| 	} | ||||
| 	return NB_OK; | ||||
| } | ||||
| 
 | ||||
| int routing_control_plane_protocols_control_plane_protocol_staticd_route_list_src_list_path_list_frr_nexthops_nexthop_srv6_segs_stack_entry_seg_destroy( | ||||
| 	struct nb_cb_destroy_args *args) | ||||
| { | ||||
| 	/*
 | ||||
| 	 * No operation is required in this call back. | ||||
| 	 * nexthop_mpls_seg_stack_entry_destroy() will take care | ||||
| 	 * to reset the seg vaue. | ||||
| 	 */ | ||||
| 	switch (args->event) { | ||||
| 	case NB_EV_VALIDATE: | ||||
| 	case NB_EV_PREPARE: | ||||
| 	case NB_EV_ABORT: | ||||
| 	case NB_EV_APPLY: | ||||
| 		break; | ||||
| 	} | ||||
| 	return NB_OK; | ||||
| } | ||||
| 
 | ||||
| /*
 | ||||
|  * XPath: | ||||
|  * /frr-routing:routing/control-plane-protocols/control-plane-protocol/frr-staticd:staticd/route-list/src-list/path-list/frr-nexthops/nexthop/mpls-label-stack/entry | ||||
|  | ||||
| @ -9,6 +9,7 @@ | ||||
| 
 | ||||
| #include "lib/bfd.h" | ||||
| #include "lib/mpls.h" | ||||
| #include "lib/srv6.h" | ||||
| #include "table.h" | ||||
| #include "memory.h" | ||||
| 
 | ||||
| @ -27,6 +28,12 @@ struct static_nh_label { | ||||
| 	mpls_label_t label[MPLS_MAX_LABELS]; | ||||
| }; | ||||
| 
 | ||||
| /* Static route seg information */ | ||||
| struct static_nh_seg { | ||||
| 	int num_segs; | ||||
| 	struct in6_addr seg[SRV6_MAX_SIDS]; | ||||
| }; | ||||
| 
 | ||||
| enum static_blackhole_type { | ||||
| 	STATIC_BLACKHOLE_DROP = 0, | ||||
| 	STATIC_BLACKHOLE_NULL, | ||||
| @ -129,6 +136,9 @@ struct static_nexthop { | ||||
| 	/* Label information */ | ||||
| 	struct static_nh_label snh_label; | ||||
| 
 | ||||
| 	/* SRv6 Seg information */ | ||||
| 	struct static_nh_seg snh_seg; | ||||
| 
 | ||||
| 	/*
 | ||||
| 	 * Whether to pretend the nexthop is directly attached to the specified | ||||
| 	 * link. Only meaningful when both a gateway address and interface name | ||||
|  | ||||
| @ -47,6 +47,7 @@ struct static_route_args { | ||||
| 	const char *source; | ||||
| 	const char *gateway; | ||||
| 	const char *interface_name; | ||||
| 	const char *segs; | ||||
| 	const char *flag; | ||||
| 	const char *tag; | ||||
| 	const char *distance; | ||||
| @ -73,12 +74,16 @@ static int static_route_nb_run(struct vty *vty, struct static_route_args *args) | ||||
| 	char xpath_nexthop[XPATH_MAXLEN]; | ||||
| 	char xpath_mpls[XPATH_MAXLEN]; | ||||
| 	char xpath_label[XPATH_MAXLEN]; | ||||
| 	char xpath_segs[XPATH_MAXLEN]; | ||||
| 	char xpath_seg[XPATH_MAXLEN]; | ||||
| 	char ab_xpath[XPATH_MAXLEN]; | ||||
| 	char buf_prefix[PREFIX_STRLEN]; | ||||
| 	char buf_src_prefix[PREFIX_STRLEN] = {}; | ||||
| 	char buf_nh_type[PREFIX_STRLEN] = {}; | ||||
| 	char buf_tag[PREFIX_STRLEN]; | ||||
| 	uint8_t label_stack_id = 0; | ||||
| 	uint8_t segs_stack_id = 0; | ||||
| 
 | ||||
| 	const char *buf_gate_str; | ||||
| 	uint8_t distance = ZEBRA_STATIC_DISTANCE_DEFAULT; | ||||
| 	route_tag_t tag = 0; | ||||
| @ -345,7 +350,39 @@ static int static_route_nb_run(struct vty *vty, struct static_route_args *args) | ||||
| 			nb_cli_enqueue_change(vty, xpath_mpls, NB_OP_DESTROY, | ||||
| 					      NULL); | ||||
| 		} | ||||
| 		if (args->segs) { | ||||
| 			/* copy of seg string (start) */ | ||||
| 			char *ostr; | ||||
| 			/* pointer to next segment */ | ||||
| 			char *nump; | ||||
| 
 | ||||
| 			strlcpy(xpath_segs, xpath_nexthop, sizeof(xpath_segs)); | ||||
| 			strlcat(xpath_segs, FRR_STATIC_ROUTE_NH_SRV6_SEGS_XPATH, | ||||
| 				sizeof(xpath_segs)); | ||||
| 
 | ||||
| 			nb_cli_enqueue_change(vty, xpath_segs, NB_OP_DESTROY, | ||||
| 					      NULL); | ||||
| 
 | ||||
| 			ostr = XSTRDUP(MTYPE_TMP, args->segs); | ||||
| 			while ((nump = strsep(&ostr, "/")) != NULL) { | ||||
| 				snprintf(ab_xpath, sizeof(ab_xpath), | ||||
| 					 FRR_STATIC_ROUTE_NH_SRV6_KEY_SEG_XPATH, | ||||
| 					 segs_stack_id); | ||||
| 				strlcpy(xpath_seg, xpath_segs, | ||||
| 					sizeof(xpath_seg)); | ||||
| 				strlcat(xpath_seg, ab_xpath, sizeof(xpath_seg)); | ||||
| 				nb_cli_enqueue_change(vty, xpath_seg, | ||||
| 						      NB_OP_MODIFY, nump); | ||||
| 				segs_stack_id++; | ||||
| 			} | ||||
| 			XFREE(MTYPE_TMP, ostr); | ||||
| 		} else { | ||||
| 			strlcpy(xpath_segs, xpath_nexthop, sizeof(xpath_segs)); | ||||
| 			strlcat(xpath_segs, FRR_STATIC_ROUTE_NH_SRV6_SEGS_XPATH, | ||||
| 				sizeof(xpath_segs)); | ||||
| 			nb_cli_enqueue_change(vty, xpath_segs, NB_OP_DESTROY, | ||||
| 					      NULL); | ||||
| 		} | ||||
| 		if (args->bfd) { | ||||
| 			char xpath_bfd[XPATH_MAXLEN]; | ||||
| 
 | ||||
| @ -951,9 +988,8 @@ DEFPY_YANG(ipv6_route_blackhole_vrf, | ||||
| 	return static_route_nb_run(vty, &args); | ||||
| } | ||||
| 
 | ||||
| DEFPY_YANG(ipv6_route_address_interface, | ||||
|       ipv6_route_address_interface_cmd, | ||||
|       "[no] ipv6 route X:X::X:X/M$prefix [from X:X::X:X/M] \
 | ||||
| DEFPY_YANG(ipv6_route_address_interface, ipv6_route_address_interface_cmd, | ||||
| 	   "[no] ipv6 route X:X::X:X/M$prefix [from X:X::X:X/M] \
 | ||||
|           X:X::X:X$gate                                    \ | ||||
|           <INTERFACE|Null0>$ifname                         \ | ||||
|           [{                                               \ | ||||
| @ -966,33 +1002,28 @@ DEFPY_YANG(ipv6_route_address_interface, | ||||
| 	    |onlink$onlink                                 \ | ||||
| 	    |color (1-4294967295)                          \ | ||||
| 	    |bfd$bfd [{multi-hop$bfd_multi_hop|source X:X::X:X$bfd_source|profile BFDPROF$bfd_profile}] \ | ||||
| 		|segments WORD 								   \ | ||||
|           }]", | ||||
|       NO_STR | ||||
|       IPV6_STR | ||||
|       "Establish static routes\n" | ||||
|       "IPv6 destination prefix (e.g. 3ffe:506::/32)\n" | ||||
|       "IPv6 source-dest route\n" | ||||
|       "IPv6 source prefix\n" | ||||
|       "IPv6 gateway address\n" | ||||
|       "IPv6 gateway interface name\n" | ||||
|       "Null interface\n" | ||||
|       "Set tag for this route\n" | ||||
|       "Tag value\n" | ||||
|       "Distance value for this prefix\n" | ||||
|       VRF_CMD_HELP_STR | ||||
|       MPLS_LABEL_HELPSTR | ||||
|       "Table to configure\n" | ||||
|       "The table number to configure\n" | ||||
|       VRF_CMD_HELP_STR | ||||
|       "Treat the nexthop as directly attached to the interface\n" | ||||
|       "SR-TE color\n" | ||||
|       "The SR-TE color to configure\n" | ||||
|       BFD_INTEGRATION_STR | ||||
|       BFD_INTEGRATION_MULTI_HOP_STR | ||||
|       BFD_INTEGRATION_SOURCE_STR | ||||
|       BFD_INTEGRATION_SOURCEV4_STR | ||||
|       BFD_PROFILE_STR | ||||
|       BFD_PROFILE_NAME_STR) | ||||
| 	   NO_STR IPV6_STR | ||||
| 	   "Establish static routes\n" | ||||
| 	   "IPv6 destination prefix (e.g. 3ffe:506::/32)\n" | ||||
| 	   "IPv6 source-dest route\n" | ||||
| 	   "IPv6 source prefix\n" | ||||
| 	   "IPv6 gateway address\n" | ||||
| 	   "IPv6 gateway interface name\n" | ||||
| 	   "Null interface\n" | ||||
| 	   "Set tag for this route\n" | ||||
| 	   "Tag value\n" | ||||
| 	   "Distance value for this prefix\n" VRF_CMD_HELP_STR MPLS_LABEL_HELPSTR | ||||
| 	   "Table to configure\n" | ||||
| 	   "The table number to configure\n" VRF_CMD_HELP_STR | ||||
| 	   "Treat the nexthop as directly attached to the interface\n" | ||||
| 	   "SR-TE color\n" | ||||
| 	   "The SR-TE color to configure\n" BFD_INTEGRATION_STR | ||||
| 		   BFD_INTEGRATION_MULTI_HOP_STR BFD_INTEGRATION_SOURCE_STR | ||||
| 			   BFD_INTEGRATION_SOURCEV4_STR BFD_PROFILE_STR | ||||
| 				   BFD_PROFILE_NAME_STR "Value of segs\n" | ||||
| 	   "Segs (SIDs)\n") | ||||
| { | ||||
| 	struct static_route_args args = { | ||||
| 		.delete = !!no, | ||||
| @ -1014,14 +1045,15 @@ DEFPY_YANG(ipv6_route_address_interface, | ||||
| 		.bfd_multi_hop = !!bfd_multi_hop, | ||||
| 		.bfd_source = bfd_source_str, | ||||
| 		.bfd_profile = bfd_profile, | ||||
| 		.segs = segments, | ||||
| 	}; | ||||
| 
 | ||||
| 	return static_route_nb_run(vty, &args); | ||||
| } | ||||
| 
 | ||||
| DEFPY_YANG(ipv6_route_address_interface_vrf, | ||||
|       ipv6_route_address_interface_vrf_cmd, | ||||
|       "[no] ipv6 route X:X::X:X/M$prefix [from X:X::X:X/M] \
 | ||||
| 	   ipv6_route_address_interface_vrf_cmd, | ||||
| 	   "[no] ipv6 route X:X::X:X/M$prefix [from X:X::X:X/M] \
 | ||||
|           X:X::X:X$gate                                    \ | ||||
|           <INTERFACE|Null0>$ifname                         \ | ||||
|           [{                                               \ | ||||
| @ -1033,32 +1065,28 @@ DEFPY_YANG(ipv6_route_address_interface_vrf, | ||||
| 	    |onlink$onlink                                 \ | ||||
| 	    |color (1-4294967295)                          \ | ||||
| 	    |bfd$bfd [{multi-hop$bfd_multi_hop|source X:X::X:X$bfd_source|profile BFDPROF$bfd_profile}] \ | ||||
| 		|segments WORD 								   \ | ||||
|           }]", | ||||
|       NO_STR | ||||
|       IPV6_STR | ||||
|       "Establish static routes\n" | ||||
|       "IPv6 destination prefix (e.g. 3ffe:506::/32)\n" | ||||
|       "IPv6 source-dest route\n" | ||||
|       "IPv6 source prefix\n" | ||||
|       "IPv6 gateway address\n" | ||||
|       "IPv6 gateway interface name\n" | ||||
|       "Null interface\n" | ||||
|       "Set tag for this route\n" | ||||
|       "Tag value\n" | ||||
|       "Distance value for this prefix\n" | ||||
|       MPLS_LABEL_HELPSTR | ||||
|       "Table to configure\n" | ||||
|       "The table number to configure\n" | ||||
|       VRF_CMD_HELP_STR | ||||
|       "Treat the nexthop as directly attached to the interface\n" | ||||
|       "SR-TE color\n" | ||||
|       "The SR-TE color to configure\n" | ||||
|       BFD_INTEGRATION_STR | ||||
|       BFD_INTEGRATION_MULTI_HOP_STR | ||||
|       BFD_INTEGRATION_SOURCE_STR | ||||
|       BFD_INTEGRATION_SOURCEV4_STR | ||||
|       BFD_PROFILE_STR | ||||
|       BFD_PROFILE_NAME_STR) | ||||
| 	   NO_STR IPV6_STR | ||||
| 	   "Establish static routes\n" | ||||
| 	   "IPv6 destination prefix (e.g. 3ffe:506::/32)\n" | ||||
| 	   "IPv6 source-dest route\n" | ||||
| 	   "IPv6 source prefix\n" | ||||
| 	   "IPv6 gateway address\n" | ||||
| 	   "IPv6 gateway interface name\n" | ||||
| 	   "Null interface\n" | ||||
| 	   "Set tag for this route\n" | ||||
| 	   "Tag value\n" | ||||
| 	   "Distance value for this prefix\n" MPLS_LABEL_HELPSTR | ||||
| 	   "Table to configure\n" | ||||
| 	   "The table number to configure\n" VRF_CMD_HELP_STR | ||||
| 	   "Treat the nexthop as directly attached to the interface\n" | ||||
| 	   "SR-TE color\n" | ||||
| 	   "The SR-TE color to configure\n" BFD_INTEGRATION_STR | ||||
| 		   BFD_INTEGRATION_MULTI_HOP_STR BFD_INTEGRATION_SOURCE_STR | ||||
| 			   BFD_INTEGRATION_SOURCEV4_STR BFD_PROFILE_STR | ||||
| 				   BFD_PROFILE_NAME_STR "Value of segs\n" | ||||
| 	   "Segs (SIDs)\n") | ||||
| { | ||||
| 	struct static_route_args args = { | ||||
| 		.delete = !!no, | ||||
| @ -1080,14 +1108,14 @@ DEFPY_YANG(ipv6_route_address_interface_vrf, | ||||
| 		.bfd_multi_hop = !!bfd_multi_hop, | ||||
| 		.bfd_source = bfd_source_str, | ||||
| 		.bfd_profile = bfd_profile, | ||||
| 		.segs = segments, | ||||
| 	}; | ||||
| 
 | ||||
| 	return static_route_nb_run(vty, &args); | ||||
| } | ||||
| 
 | ||||
| DEFPY_YANG(ipv6_route, | ||||
|       ipv6_route_cmd, | ||||
|       "[no] ipv6 route X:X::X:X/M$prefix [from X:X::X:X/M] \
 | ||||
| DEFPY_YANG(ipv6_route, ipv6_route_cmd, | ||||
| 	   "[no] ipv6 route X:X::X:X/M$prefix [from X:X::X:X/M] \
 | ||||
|           <X:X::X:X$gate|<INTERFACE|Null0>$ifname>         \ | ||||
|           [{                                               \ | ||||
|             tag (1-4294967295)                             \ | ||||
| @ -1098,32 +1126,26 @@ DEFPY_YANG(ipv6_route, | ||||
|             |nexthop-vrf NAME                              \ | ||||
|             |color (1-4294967295)                          \ | ||||
| 	    |bfd$bfd [{multi-hop$bfd_multi_hop|source X:X::X:X$bfd_source|profile BFDPROF$bfd_profile}] \ | ||||
| 			|segments WORD 								   \ | ||||
|           }]", | ||||
|       NO_STR | ||||
|       IPV6_STR | ||||
|       "Establish static routes\n" | ||||
|       "IPv6 destination prefix (e.g. 3ffe:506::/32)\n" | ||||
|       "IPv6 source-dest route\n" | ||||
|       "IPv6 source prefix\n" | ||||
|       "IPv6 gateway address\n" | ||||
|       "IPv6 gateway interface name\n" | ||||
|       "Null interface\n" | ||||
|       "Set tag for this route\n" | ||||
|       "Tag value\n" | ||||
|       "Distance value for this prefix\n" | ||||
|       VRF_CMD_HELP_STR | ||||
|       MPLS_LABEL_HELPSTR | ||||
|       "Table to configure\n" | ||||
|       "The table number to configure\n" | ||||
|       VRF_CMD_HELP_STR | ||||
|       "SR-TE color\n" | ||||
|       "The SR-TE color to configure\n" | ||||
|       BFD_INTEGRATION_STR | ||||
|       BFD_INTEGRATION_MULTI_HOP_STR | ||||
|       BFD_INTEGRATION_SOURCE_STR | ||||
|       BFD_INTEGRATION_SOURCEV4_STR | ||||
|       BFD_PROFILE_STR | ||||
|       BFD_PROFILE_NAME_STR) | ||||
| 	   NO_STR IPV6_STR | ||||
| 	   "Establish static routes\n" | ||||
| 	   "IPv6 destination prefix (e.g. 3ffe:506::/32)\n" | ||||
| 	   "IPv6 source-dest route\n" | ||||
| 	   "IPv6 source prefix\n" | ||||
| 	   "IPv6 gateway address\n" | ||||
| 	   "IPv6 gateway interface name\n" | ||||
| 	   "Null interface\n" | ||||
| 	   "Set tag for this route\n" | ||||
| 	   "Tag value\n" | ||||
| 	   "Distance value for this prefix\n" VRF_CMD_HELP_STR MPLS_LABEL_HELPSTR | ||||
| 	   "Table to configure\n" | ||||
| 	   "The table number to configure\n" VRF_CMD_HELP_STR "SR-TE color\n" | ||||
| 	   "The SR-TE color to configure\n" BFD_INTEGRATION_STR | ||||
| 		   BFD_INTEGRATION_MULTI_HOP_STR BFD_INTEGRATION_SOURCE_STR | ||||
| 			   BFD_INTEGRATION_SOURCEV4_STR BFD_PROFILE_STR | ||||
| 				   BFD_PROFILE_NAME_STR "Value of segs\n" | ||||
| 	   "Segs (SIDs)\n") | ||||
| { | ||||
| 	struct static_route_args args = { | ||||
| 		.delete = !!no, | ||||
| @ -1144,14 +1166,15 @@ DEFPY_YANG(ipv6_route, | ||||
| 		.bfd_multi_hop = !!bfd_multi_hop, | ||||
| 		.bfd_source = bfd_source_str, | ||||
| 		.bfd_profile = bfd_profile, | ||||
| 		.segs = segments, | ||||
| 
 | ||||
| 	}; | ||||
| 
 | ||||
| 	return static_route_nb_run(vty, &args); | ||||
| } | ||||
| 
 | ||||
| DEFPY_YANG(ipv6_route_vrf, | ||||
|       ipv6_route_vrf_cmd, | ||||
|       "[no] ipv6 route X:X::X:X/M$prefix [from X:X::X:X/M] \
 | ||||
| DEFPY_YANG(ipv6_route_vrf, ipv6_route_vrf_cmd, | ||||
| 	   "[no] ipv6 route X:X::X:X/M$prefix [from X:X::X:X/M] \
 | ||||
|           <X:X::X:X$gate|<INTERFACE|Null0>$ifname>                 \ | ||||
|           [{                                               \ | ||||
|             tag (1-4294967295)                             \ | ||||
| @ -1161,31 +1184,26 @@ DEFPY_YANG(ipv6_route_vrf, | ||||
|             |nexthop-vrf NAME                              \ | ||||
| 	    |color (1-4294967295)                          \ | ||||
| 	    |bfd$bfd [{multi-hop$bfd_multi_hop|source X:X::X:X$bfd_source|profile BFDPROF$bfd_profile}] \ | ||||
| 		|segments WORD 								   \ | ||||
|           }]", | ||||
|       NO_STR | ||||
|       IPV6_STR | ||||
|       "Establish static routes\n" | ||||
|       "IPv6 destination prefix (e.g. 3ffe:506::/32)\n" | ||||
|       "IPv6 source-dest route\n" | ||||
|       "IPv6 source prefix\n" | ||||
|       "IPv6 gateway address\n" | ||||
|       "IPv6 gateway interface name\n" | ||||
|       "Null interface\n" | ||||
|       "Set tag for this route\n" | ||||
|       "Tag value\n" | ||||
|       "Distance value for this prefix\n" | ||||
|       MPLS_LABEL_HELPSTR | ||||
|       "Table to configure\n" | ||||
|       "The table number to configure\n" | ||||
|       VRF_CMD_HELP_STR | ||||
|       "SR-TE color\n" | ||||
|       "The SR-TE color to configure\n" | ||||
|       BFD_INTEGRATION_STR | ||||
|       BFD_INTEGRATION_MULTI_HOP_STR | ||||
|       BFD_INTEGRATION_SOURCE_STR | ||||
|       BFD_INTEGRATION_SOURCEV4_STR | ||||
|       BFD_PROFILE_STR | ||||
|       BFD_PROFILE_NAME_STR) | ||||
| 	   NO_STR IPV6_STR | ||||
| 	   "Establish static routes\n" | ||||
| 	   "IPv6 destination prefix (e.g. 3ffe:506::/32)\n" | ||||
| 	   "IPv6 source-dest route\n" | ||||
| 	   "IPv6 source prefix\n" | ||||
| 	   "IPv6 gateway address\n" | ||||
| 	   "IPv6 gateway interface name\n" | ||||
| 	   "Null interface\n" | ||||
| 	   "Set tag for this route\n" | ||||
| 	   "Tag value\n" | ||||
| 	   "Distance value for this prefix\n" MPLS_LABEL_HELPSTR | ||||
| 	   "Table to configure\n" | ||||
| 	   "The table number to configure\n" VRF_CMD_HELP_STR "SR-TE color\n" | ||||
| 	   "The SR-TE color to configure\n" BFD_INTEGRATION_STR | ||||
| 		   BFD_INTEGRATION_MULTI_HOP_STR BFD_INTEGRATION_SOURCE_STR | ||||
| 			   BFD_INTEGRATION_SOURCEV4_STR BFD_PROFILE_STR | ||||
| 				   BFD_PROFILE_NAME_STR "Value of segs\n" | ||||
| 	   "Segs (SIDs)\n") | ||||
| { | ||||
| 	struct static_route_args args = { | ||||
| 		.delete = !!no, | ||||
| @ -1206,6 +1224,7 @@ DEFPY_YANG(ipv6_route_vrf, | ||||
| 		.bfd_multi_hop = !!bfd_multi_hop, | ||||
| 		.bfd_source = bfd_source_str, | ||||
| 		.bfd_profile = bfd_profile, | ||||
| 		.segs = segments, | ||||
| 	}; | ||||
| 
 | ||||
| 	return static_route_nb_run(vty, &args); | ||||
| @ -1252,6 +1271,39 @@ static int mpls_label_iter_cb(const struct lyd_node *dnode, void *arg) | ||||
| 	return YANG_ITER_CONTINUE; | ||||
| } | ||||
| 
 | ||||
| struct srv6_seg_iter { | ||||
| 	struct vty *vty; | ||||
| 	bool first; | ||||
| }; | ||||
| 
 | ||||
| static int srv6_seg_iter_cb(const struct lyd_node *dnode, void *arg) | ||||
| { | ||||
| 	struct srv6_seg_iter *iter = arg; | ||||
| 	char buffer[INET6_ADDRSTRLEN]; | ||||
| 	struct in6_addr cli_seg; | ||||
| 
 | ||||
| 	if (yang_dnode_exists(dnode, "./seg")) { | ||||
| 		if (iter->first) { | ||||
| 			yang_dnode_get_ipv6(&cli_seg, dnode, "./seg"); | ||||
| 			if (inet_ntop(AF_INET6, &cli_seg, buffer, | ||||
| 				      INET6_ADDRSTRLEN) == NULL) { | ||||
| 				return 1; | ||||
| 			} | ||||
| 			vty_out(iter->vty, " segments %s", buffer); | ||||
| 		} else { | ||||
| 			yang_dnode_get_ipv6(&cli_seg, dnode, "./seg"); | ||||
| 			if (inet_ntop(AF_INET6, &cli_seg, buffer, | ||||
| 				      INET6_ADDRSTRLEN) == NULL) { | ||||
| 				return 1; | ||||
| 			} | ||||
| 			vty_out(iter->vty, "/%s", buffer); | ||||
| 		} | ||||
| 		iter->first = false; | ||||
| 	} | ||||
| 
 | ||||
| 	return YANG_ITER_CONTINUE; | ||||
| } | ||||
| 
 | ||||
| static void nexthop_cli_show(struct vty *vty, const struct lyd_node *route, | ||||
| 			     const struct lyd_node *src, | ||||
| 			     const struct lyd_node *path, | ||||
| @ -1266,6 +1318,7 @@ static void nexthop_cli_show(struct vty *vty, const struct lyd_node *route, | ||||
| 	uint32_t tag; | ||||
| 	uint8_t distance; | ||||
| 	struct mpls_label_iter iter; | ||||
| 	struct srv6_seg_iter seg_iter; | ||||
| 	const char *nexthop_vrf; | ||||
| 	uint32_t table_id; | ||||
| 	bool onlink; | ||||
| @ -1342,6 +1395,11 @@ static void nexthop_cli_show(struct vty *vty, const struct lyd_node *route, | ||||
| 	yang_dnode_iterate(mpls_label_iter_cb, &iter, nexthop, | ||||
| 			   "./mpls-label-stack/entry"); | ||||
| 
 | ||||
| 	seg_iter.vty = vty; | ||||
| 	seg_iter.first = true; | ||||
| 	yang_dnode_iterate(srv6_seg_iter_cb, &seg_iter, nexthop, | ||||
| 			   "./srv6-segs-stack/entry"); | ||||
| 
 | ||||
| 	nexthop_vrf = yang_dnode_get_string(nexthop, "./vrf"); | ||||
| 	if (strcmp(vrf, nexthop_vrf)) | ||||
| 		vty_out(vty, " nexthop-vrf %s", nexthop_vrf); | ||||
|  | ||||
| @ -499,6 +499,21 @@ extern void static_zebra_route_add(struct static_path *pn, bool install) | ||||
| 			for (i = 0; i < api_nh->label_num; i++) | ||||
| 				api_nh->labels[i] = nh->snh_label.label[i]; | ||||
| 		} | ||||
| 		if (nh->snh_seg.num_segs) { | ||||
| 			int i; | ||||
| 
 | ||||
| 			api_nh->seg6local_action = | ||||
| 				ZEBRA_SEG6_LOCAL_ACTION_UNSPEC; | ||||
| 			SET_FLAG(api_nh->flags, ZAPI_NEXTHOP_FLAG_SEG6); | ||||
| 			SET_FLAG(api.flags, ZEBRA_FLAG_ALLOW_RECURSION); | ||||
| 			api.safi = SAFI_UNICAST; | ||||
| 
 | ||||
| 			api_nh->seg_num = nh->snh_seg.num_segs; | ||||
| 			for (i = 0; i < api_nh->seg_num; i++) | ||||
| 				memcpy(&api_nh->seg6_segs[i], | ||||
| 				       &nh->snh_seg.seg[i], | ||||
| 				       sizeof(struct in6_addr)); | ||||
| 		} | ||||
| 		nh_num++; | ||||
| 	} | ||||
| 
 | ||||
|  | ||||
							
								
								
									
										0
									
								
								tests/topotests/srv6_static_route/__init__.py
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										0
									
								
								tests/topotests/srv6_static_route/__init__.py
									
									
									
									
									
										Normal file
									
								
							
							
								
								
									
										49
									
								
								tests/topotests/srv6_static_route/expected_srv6_route.json
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										49
									
								
								tests/topotests/srv6_static_route/expected_srv6_route.json
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,49 @@ | ||||
| { | ||||
| 	"2001:db8:aaaa::/64": [ | ||||
| 		{ | ||||
| 			"prefix": "2001:db8:aaaa::/64", | ||||
| 			"prefixLen": 64, | ||||
| 			"protocol": "static", | ||||
| 			"selected": true, | ||||
| 			"destSelected": true, | ||||
| 			"distance": 1, | ||||
| 			"metric": 0, | ||||
| 			"installed": true, | ||||
| 			"nexthops": [ | ||||
| 				{ | ||||
| 					"directlyConnected": true, | ||||
| 					"active": true, | ||||
| 					"weight": 1 | ||||
| 				} | ||||
| 			] | ||||
| 		} | ||||
| 	], | ||||
| 	"2005::/64": [ | ||||
| 		{ | ||||
| 			"prefix": "2005::/64", | ||||
| 			"prefixLen": 64, | ||||
| 			"protocol": "static", | ||||
| 			"selected": true, | ||||
| 			"destSelected": true, | ||||
| 			"distance": 1, | ||||
| 			"metric": 0, | ||||
| 			"installed": true, | ||||
| 			"nexthops": [ | ||||
| 				{ | ||||
| 					"directlyConnected": true, | ||||
| 					"active": true, | ||||
| 					"weight": 1, | ||||
| 					"seg6local": { | ||||
| 						"action": "unspec" | ||||
| 					}, | ||||
| 					"seg6": [ | ||||
| 						"2001:db8:aaaa::7", | ||||
| 						"2002::2", | ||||
| 						"2003::3", | ||||
| 						"2004::4" | ||||
| 					] | ||||
| 				} | ||||
| 			] | ||||
| 		} | ||||
| 	] | ||||
| } | ||||
							
								
								
									
										0
									
								
								tests/topotests/srv6_static_route/r1/mgmtd.conf
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										0
									
								
								tests/topotests/srv6_static_route/r1/mgmtd.conf
									
									
									
									
									
										Normal file
									
								
							
							
								
								
									
										2
									
								
								tests/topotests/srv6_static_route/r1/setup.sh
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										2
									
								
								tests/topotests/srv6_static_route/r1/setup.sh
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,2 @@ | ||||
| ip link add dummy0 type dummy | ||||
| ip link set dummy0 up | ||||
							
								
								
									
										9
									
								
								tests/topotests/srv6_static_route/r1/staticd.conf
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										9
									
								
								tests/topotests/srv6_static_route/r1/staticd.conf
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,9 @@ | ||||
| hostname r1 | ||||
| ! | ||||
| log stdout notifications | ||||
| log monitor notifications | ||||
| log commands | ||||
| log file staticd.log debugging | ||||
| ! | ||||
| ipv6 route 2001:db8:aaaa::/64 dummy0 | ||||
| ipv6 route 2005::/64 dummy0 segments 2001:db8:aaaa::7/2002::2/2003::3/2004::4 | ||||
							
								
								
									
										10
									
								
								tests/topotests/srv6_static_route/r1/zebra.conf
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										10
									
								
								tests/topotests/srv6_static_route/r1/zebra.conf
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,10 @@ | ||||
| hostname r1 | ||||
| ! | ||||
| ! debug zebra events | ||||
| ! debug zebra rib detailed | ||||
| ! | ||||
| log stdout notifications | ||||
| log monitor notifications | ||||
| log commands | ||||
| log file zebra.log debugging | ||||
| ! | ||||
							
								
								
									
										90
									
								
								tests/topotests/srv6_static_route/test_srv6_route.py
									
									
									
									
									
										Executable file
									
								
							
							
						
						
									
										90
									
								
								tests/topotests/srv6_static_route/test_srv6_route.py
									
									
									
									
									
										Executable file
									
								
							| @ -0,0 +1,90 @@ | ||||
| #!/usr/bin/env python | ||||
| # SPDX-License-Identifier: ISC | ||||
| 
 | ||||
| # | ||||
| # test_srv6_route.py | ||||
| # | ||||
| # Copyright 2023 6WIND S.A. | ||||
| # Dmytro Shytyi <dmytro.shytyi@6wind.com> | ||||
| # | ||||
| 
 | ||||
| """ | ||||
| test_srv6_route.py: | ||||
| Test for SRv6 static route on zebra | ||||
| """ | ||||
| 
 | ||||
| import os | ||||
| import sys | ||||
| import json | ||||
| import pytest | ||||
| import functools | ||||
| 
 | ||||
| CWD = os.path.dirname(os.path.realpath(__file__)) | ||||
| sys.path.append(os.path.join(CWD, "../")) | ||||
| 
 | ||||
| # pylint: disable=C0413 | ||||
| from lib import topotest | ||||
| from lib.topogen import Topogen, TopoRouter, get_topogen | ||||
| from lib.topolog import logger | ||||
| 
 | ||||
| pytestmark = [pytest.mark.bgpd, pytest.mark.sharpd] | ||||
| 
 | ||||
| 
 | ||||
| def open_json_file(filename): | ||||
|     try: | ||||
|         with open(filename, "r") as f: | ||||
|             return json.load(f) | ||||
|     except IOError: | ||||
|         assert False, "Could not read file {}".format(filename) | ||||
| 
 | ||||
| 
 | ||||
| def setup_module(mod): | ||||
|     tgen = Topogen({None: "r1"}, mod.__name__) | ||||
|     tgen.start_topology() | ||||
|     for rname, router in tgen.routers().items(): | ||||
|         router.run("/bin/bash {}/{}/setup.sh".format(CWD, rname)) | ||||
|         router.load_config( | ||||
|             TopoRouter.RD_ZEBRA, os.path.join(CWD, "{}/zebra.conf".format(rname)) | ||||
|         ) | ||||
|         router.load_config( | ||||
|             TopoRouter.RD_MGMTD, os.path.join(CWD, "{}/mgmtd.conf".format(rname)) | ||||
|         ) | ||||
|         router.load_config( | ||||
|             TopoRouter.RD_STATIC, os.path.join(CWD, "{}/staticd.conf".format(rname)) | ||||
|         ) | ||||
|     tgen.start_router() | ||||
| 
 | ||||
| 
 | ||||
| def teardown_module(mod): | ||||
|     tgen = get_topogen() | ||||
|     tgen.stop_topology() | ||||
| 
 | ||||
| 
 | ||||
| def test_srv6_static_route(): | ||||
|     tgen = get_topogen() | ||||
|     if tgen.routers_have_failure(): | ||||
|         pytest.skip(tgen.errors) | ||||
|     router = tgen.gears["r1"] | ||||
| 
 | ||||
|     def _check_srv6_static_route(router, expected_route_file): | ||||
|         logger.info("checking zebra srv6 static route with multiple segs status") | ||||
|         output = json.loads(router.vtysh_cmd("show ipv6 route static json")) | ||||
|         expected = open_json_file("{}/{}".format(CWD, expected_route_file)) | ||||
|         return topotest.json_cmp(output, expected) | ||||
| 
 | ||||
|     def check_srv6_static_route(router, expected_file): | ||||
|         func = functools.partial(_check_srv6_static_route, router, expected_file) | ||||
|         success, result = topotest.run_and_expect(func, None, count=15, wait=1) | ||||
|         assert result is None, "Failed" | ||||
| 
 | ||||
|     # FOR DEVELOPER: | ||||
|     # If you want to stop some specific line and start interactive shell, | ||||
|     # please use tgen.mininet_cli() to start it. | ||||
| 
 | ||||
|     logger.info("Test for srv6 route configuration") | ||||
|     check_srv6_static_route(router, "expected_srv6_route.json") | ||||
| 
 | ||||
| 
 | ||||
| if __name__ == "__main__": | ||||
|     args = ["-s"] + sys.argv[1:] | ||||
|     sys.exit(pytest.main(args)) | ||||
| @ -201,6 +201,11 @@ module frr-nexthop { | ||||
|       description | ||||
|         "Nexthop's MPLS label stack."; | ||||
|     } | ||||
| 
 | ||||
|     uses srv6-segs-stack { | ||||
|       description | ||||
|         "Nexthop's SRv6 segs SIDs stack."; | ||||
|     } | ||||
|   } | ||||
| 
 | ||||
|   /* | ||||
| @ -298,6 +303,32 @@ module frr-nexthop { | ||||
|     } | ||||
|   } | ||||
| 
 | ||||
|   /* Contaner for SRv6 segs SIDs */ | ||||
|   grouping srv6-segs-stack { | ||||
|     description | ||||
|       "This grouping specifies an SRv6 segs SIDs stack. The segs | ||||
|       SIDs stack is encoded as a list of SID entries. The | ||||
|       list key is an identifier that indicates the relative | ||||
|       ordering of each entry."; | ||||
|       container srv6-segs-stack { | ||||
|         description | ||||
|           "Container for a list of SRv6 segs SIDs entries."; | ||||
|           list entry { | ||||
|             key "id"; | ||||
|             description | ||||
|               "List of SRv6 segs SIDs entries."; | ||||
| 	    leaf id { | ||||
|               type uint8; | ||||
|               description | ||||
|               "Identifies the entry in a sequence of SRv6 segs SIDs | ||||
|               entries."; | ||||
|             } | ||||
|             leaf seg { | ||||
|                 type inet:ipv6-address; | ||||
|             } | ||||
|           } | ||||
|       } | ||||
|   } | ||||
|   container frr-nexthop-group { | ||||
|     description | ||||
|       "A nexthop-group, represented as a list of nexthop objects."; | ||||
|  | ||||
| @ -66,6 +66,7 @@ | ||||
| #include "zebra/zebra_evpn_mh.h" | ||||
| #include "zebra/zebra_trace.h" | ||||
| #include "zebra/zebra_neigh.h" | ||||
| #include "lib/srv6.h" | ||||
| 
 | ||||
| #ifndef AF_MPLS | ||||
| #define AF_MPLS 28 | ||||
| @ -77,6 +78,8 @@ | ||||
| #define BR_SPH_LIST_SIZE 10 | ||||
| #endif | ||||
| 
 | ||||
| DEFINE_MTYPE_STATIC(LIB, NH_SRV6, "Nexthop srv6"); | ||||
| 
 | ||||
| static vlanid_t filter_vlan = 0; | ||||
| 
 | ||||
| /* We capture whether the current kernel supports nexthop ids; by
 | ||||
| @ -476,19 +479,19 @@ static int parse_encap_seg6(struct rtattr *tb, struct in6_addr *segs) | ||||
| { | ||||
| 	struct rtattr *tb_encap[SEG6_IPTUNNEL_MAX + 1] = {}; | ||||
| 	struct seg6_iptunnel_encap *ipt = NULL; | ||||
| 	struct in6_addr *segments = NULL; | ||||
| 	int i; | ||||
| 
 | ||||
| 	netlink_parse_rtattr_nested(tb_encap, SEG6_IPTUNNEL_MAX, tb); | ||||
| 
 | ||||
| 	/*
 | ||||
| 	 * TODO: It's not support multiple SID list. | ||||
| 	 */ | ||||
| 	if (tb_encap[SEG6_IPTUNNEL_SRH]) { | ||||
| 		ipt = (struct seg6_iptunnel_encap *) | ||||
| 			RTA_DATA(tb_encap[SEG6_IPTUNNEL_SRH]); | ||||
| 		segments = ipt->srh[0].segments; | ||||
| 		*segs = segments[0]; | ||||
| 		return 1; | ||||
| 
 | ||||
| 		for (i = ipt->srh[0].first_segment; i >= 0; i--) | ||||
| 			memcpy(&segs[i], &ipt->srh[0].segments[i], | ||||
| 			       sizeof(struct in6_addr)); | ||||
| 
 | ||||
| 		return ipt->srh[0].first_segment + 1; | ||||
| 	} | ||||
| 
 | ||||
| 	return 0; | ||||
| @ -506,7 +509,7 @@ parse_nexthop_unicast(ns_id_t ns_id, struct rtmsg *rtm, struct rtattr **tb, | ||||
| 	int num_labels = 0; | ||||
| 	enum seg6local_action_t seg6l_act = ZEBRA_SEG6_LOCAL_ACTION_UNSPEC; | ||||
| 	struct seg6local_context seg6l_ctx = {}; | ||||
| 	struct in6_addr seg6_segs = {}; | ||||
| 	struct in6_addr segs[SRV6_MAX_SIDS] = {}; | ||||
| 	int num_segs = 0; | ||||
| 
 | ||||
| 	vrf_id_t nh_vrf_id = vrf_id; | ||||
| @ -555,7 +558,7 @@ parse_nexthop_unicast(ns_id_t ns_id, struct rtmsg *rtm, struct rtattr **tb, | ||||
| 	if (tb[RTA_ENCAP] && tb[RTA_ENCAP_TYPE] | ||||
| 	    && *(uint16_t *)RTA_DATA(tb[RTA_ENCAP_TYPE]) | ||||
| 		       == LWTUNNEL_ENCAP_SEG6) { | ||||
| 		num_segs = parse_encap_seg6(tb[RTA_ENCAP], &seg6_segs); | ||||
| 		num_segs = parse_encap_seg6(tb[RTA_ENCAP], segs); | ||||
| 	} | ||||
| 
 | ||||
| 	if (rtm->rtm_flags & RTNH_F_ONLINK) | ||||
| @ -581,7 +584,7 @@ parse_nexthop_unicast(ns_id_t ns_id, struct rtmsg *rtm, struct rtattr **tb, | ||||
| 		nexthop_add_srv6_seg6local(&nh, seg6l_act, &seg6l_ctx); | ||||
| 
 | ||||
| 	if (num_segs) | ||||
| 		nexthop_add_srv6_seg6(&nh, &seg6_segs); | ||||
| 		nexthop_add_srv6_seg6(&nh, segs, num_segs); | ||||
| 
 | ||||
| 	return nh; | ||||
| } | ||||
| @ -601,7 +604,7 @@ static uint8_t parse_multipath_nexthops_unicast(ns_id_t ns_id, | ||||
| 	int num_labels = 0; | ||||
| 	enum seg6local_action_t seg6l_act = ZEBRA_SEG6_LOCAL_ACTION_UNSPEC; | ||||
| 	struct seg6local_context seg6l_ctx = {}; | ||||
| 	struct in6_addr seg6_segs = {}; | ||||
| 	struct in6_addr segs[SRV6_MAX_SIDS] = {}; | ||||
| 	int num_segs = 0; | ||||
| 	struct rtattr *rtnh_tb[RTA_MAX + 1] = {}; | ||||
| 
 | ||||
| @ -657,7 +660,7 @@ static uint8_t parse_multipath_nexthops_unicast(ns_id_t ns_id, | ||||
| 			    && *(uint16_t *)RTA_DATA(rtnh_tb[RTA_ENCAP_TYPE]) | ||||
| 				       == LWTUNNEL_ENCAP_SEG6) { | ||||
| 				num_segs = parse_encap_seg6(rtnh_tb[RTA_ENCAP], | ||||
| 							   &seg6_segs); | ||||
| 							    segs); | ||||
| 			} | ||||
| 		} | ||||
| 
 | ||||
| @ -700,7 +703,7 @@ static uint8_t parse_multipath_nexthops_unicast(ns_id_t ns_id, | ||||
| 							   &seg6l_ctx); | ||||
| 
 | ||||
| 			if (num_segs) | ||||
| 				nexthop_add_srv6_seg6(nh, &seg6_segs); | ||||
| 				nexthop_add_srv6_seg6(nh, segs, num_segs); | ||||
| 
 | ||||
| 			if (rtnh->rtnh_flags & RTNH_F_ONLINK) | ||||
| 				SET_FLAG(nh->flags, NEXTHOP_FLAG_ONLINK); | ||||
| @ -1514,37 +1517,40 @@ static bool _netlink_route_encode_nexthop_src(const struct nexthop *nexthop, | ||||
| } | ||||
| 
 | ||||
| static ssize_t fill_seg6ipt_encap(char *buffer, size_t buflen, | ||||
| 				  const struct in6_addr *seg) | ||||
| 				  struct seg6_seg_stack *segs) | ||||
| { | ||||
| 	struct seg6_iptunnel_encap *ipt; | ||||
| 	struct ipv6_sr_hdr *srh; | ||||
| 	const size_t srhlen = 24; | ||||
| 	size_t srhlen; | ||||
| 	int i; | ||||
| 
 | ||||
| 	/*
 | ||||
| 	 * Caution: Support only SINGLE-SID, not MULTI-SID | ||||
| 	 * This function only supports the case where segs represents | ||||
| 	 * a single SID. If you want to extend the SRv6 functionality, | ||||
| 	 * you should improve the Boundary Check. | ||||
| 	 * Ex. In case of set a SID-List include multiple-SIDs as an | ||||
| 	 * argument of the Transit Behavior, we must support variable | ||||
| 	 * boundary check for buflen. | ||||
| 	 */ | ||||
| 	if (buflen < (sizeof(struct seg6_iptunnel_encap) + | ||||
| 		      sizeof(struct ipv6_sr_hdr) + 16)) | ||||
| 	if (segs->num_segs > SRV6_MAX_SEGS) { | ||||
| 		/* Exceeding maximum supported SIDs */ | ||||
| 		return -1; | ||||
| 	} | ||||
| 
 | ||||
| 	srhlen = SRH_BASE_HEADER_LENGTH + SRH_SEGMENT_LENGTH * segs->num_segs; | ||||
| 
 | ||||
| 	if (buflen < (sizeof(struct seg6_iptunnel_encap) + srhlen)) | ||||
| 		return -1; | ||||
| 
 | ||||
| 	memset(buffer, 0, buflen); | ||||
| 
 | ||||
| 	ipt = (struct seg6_iptunnel_encap *)buffer; | ||||
| 	ipt->mode = SEG6_IPTUN_MODE_ENCAP; | ||||
| 	srh = ipt->srh; | ||||
| 
 | ||||
| 	srh = (struct ipv6_sr_hdr *)&ipt->srh; | ||||
| 	srh->hdrlen = (srhlen >> 3) - 1; | ||||
| 	srh->type = 4; | ||||
| 	srh->segments_left = 0; | ||||
| 	srh->first_segment = 0; | ||||
| 	memcpy(&srh->segments[0], seg, sizeof(struct in6_addr)); | ||||
| 	srh->segments_left = segs->num_segs - 1; | ||||
| 	srh->first_segment = segs->num_segs - 1; | ||||
| 
 | ||||
| 	return srhlen + 4; | ||||
| 	for (i = 0; i < segs->num_segs; i++) { | ||||
| 		memcpy(&srh->segments[i], &segs->seg[i], | ||||
| 		       sizeof(struct in6_addr)); | ||||
| 	} | ||||
| 
 | ||||
| 	return sizeof(struct seg6_iptunnel_encap) + srhlen; | ||||
| } | ||||
| 
 | ||||
| static bool | ||||
| @ -1726,7 +1732,9 @@ static bool _netlink_route_build_singlepath(const struct prefix *p, | ||||
| 			nl_attr_nest_end(nlmsg, nest); | ||||
| 		} | ||||
| 
 | ||||
| 		if (!sid_zero(&nexthop->nh_srv6->seg6_segs)) { | ||||
| 		if (nexthop->nh_srv6->seg6_segs && | ||||
| 		    nexthop->nh_srv6->seg6_segs->num_segs && | ||||
| 		    !sid_zero(nexthop->nh_srv6->seg6_segs)) { | ||||
| 			char tun_buf[4096]; | ||||
| 			ssize_t tun_len; | ||||
| 			struct rtattr *nest; | ||||
| @ -1737,8 +1745,9 @@ static bool _netlink_route_build_singlepath(const struct prefix *p, | ||||
| 			nest = nl_attr_nest(nlmsg, req_size, RTA_ENCAP); | ||||
| 			if (!nest) | ||||
| 				return false; | ||||
| 			tun_len = fill_seg6ipt_encap(tun_buf, sizeof(tun_buf), | ||||
| 					&nexthop->nh_srv6->seg6_segs); | ||||
| 			tun_len = | ||||
| 				fill_seg6ipt_encap(tun_buf, sizeof(tun_buf), | ||||
| 						   nexthop->nh_srv6->seg6_segs); | ||||
| 			if (tun_len < 0) | ||||
| 				return false; | ||||
| 			if (!nl_attr_put(nlmsg, req_size, SEG6_IPTUNNEL_SRH, | ||||
| @ -2971,7 +2980,9 @@ ssize_t netlink_nexthop_msg_encode(uint16_t cmd, | ||||
| 					nl_attr_nest_end(&req->n, nest); | ||||
| 				} | ||||
| 
 | ||||
| 				if (!sid_zero(&nh->nh_srv6->seg6_segs)) { | ||||
| 				if (nh->nh_srv6->seg6_segs && | ||||
| 				    nh->nh_srv6->seg6_segs->num_segs && | ||||
| 				    !sid_zero(nh->nh_srv6->seg6_segs)) { | ||||
| 					char tun_buf[4096]; | ||||
| 					ssize_t tun_len; | ||||
| 					struct rtattr *nest; | ||||
| @ -2984,9 +2995,9 @@ ssize_t netlink_nexthop_msg_encode(uint16_t cmd, | ||||
| 					    NHA_ENCAP | NLA_F_NESTED); | ||||
| 					if (!nest) | ||||
| 						return 0; | ||||
| 					tun_len = fill_seg6ipt_encap(tun_buf, | ||||
| 					    sizeof(tun_buf), | ||||
| 					    &nh->nh_srv6->seg6_segs); | ||||
| 					tun_len = fill_seg6ipt_encap( | ||||
| 						tun_buf, sizeof(tun_buf), | ||||
| 						nh->nh_srv6->seg6_segs); | ||||
| 					if (tun_len < 0) | ||||
| 						return 0; | ||||
| 					if (!nl_attr_put(&req->n, buflen, | ||||
|  | ||||
| @ -1794,7 +1794,8 @@ static bool zapi_read_nexthops(struct zserv *client, struct prefix *p, | ||||
| 			if (IS_ZEBRA_DEBUG_RECV) | ||||
| 				zlog_debug("%s: adding seg6", __func__); | ||||
| 
 | ||||
| 			nexthop_add_srv6_seg6(nexthop, &api_nh->seg6_segs); | ||||
| 			nexthop_add_srv6_seg6(nexthop, &api_nh->seg6_segs[0], | ||||
| 					      api_nh->seg_num); | ||||
| 		} | ||||
| 
 | ||||
| 		if (IS_ZEBRA_DEBUG_RECV) { | ||||
|  | ||||
| @ -598,6 +598,28 @@ const struct frr_yang_module_info frr_zebra_info = { | ||||
| 				.get_elem = lib_vrf_zebra_ribs_rib_route_route_entry_nexthop_group_nexthop_color_get_elem, | ||||
| 			} | ||||
| 		}, | ||||
| 
 | ||||
| 		{ | ||||
| 			.xpath = "/frr-vrf:lib/vrf/frr-zebra:zebra/ribs/rib/route/route-entry/nexthop-group/nexthop/srv6-segs-stack/entry", | ||||
| 			.cbs = { | ||||
| 				.get_next = lib_vrf_zebra_ribs_rib_route_route_entry_nexthop_group_nexthop_srv6_segs_stack_entry_get_next, | ||||
| 				.get_keys = lib_vrf_zebra_ribs_rib_route_route_entry_nexthop_group_nexthop_srv6_segs_stack_entry_get_keys, | ||||
| 				.lookup_entry = lib_vrf_zebra_ribs_rib_route_route_entry_nexthop_group_nexthop_srv6_segs_stack_entry_lookup_entry, | ||||
| 			} | ||||
| 		}, | ||||
| 		{ | ||||
| 			.xpath = "/frr-vrf:lib/vrf/frr-zebra:zebra/ribs/rib/route/route-entry/nexthop-group/nexthop/srv6-segs-stack/entry/id", | ||||
| 			.cbs = { | ||||
| 				.get_elem = lib_vrf_zebra_ribs_rib_route_route_entry_nexthop_group_nexthop_srv6_segs_stack_entry_id_get_elem, | ||||
| 			} | ||||
| 		}, | ||||
| 		{ | ||||
| 			.xpath = "/frr-vrf:lib/vrf/frr-zebra:zebra/ribs/rib/route/route-entry/nexthop-group/nexthop/srv6-segs-stack/entry/seg", | ||||
| 			.cbs = { | ||||
| 				.get_elem = lib_vrf_zebra_ribs_rib_route_route_entry_nexthop_group_nexthop_srv6_segs_stack_entry_seg_get_elem, | ||||
| 			} | ||||
| 		}, | ||||
| 
 | ||||
| 		{ | ||||
| 			.xpath = "/frr-vrf:lib/vrf/frr-zebra:zebra/ribs/rib/route/route-entry/nexthop-group/nexthop/mpls-label-stack/entry", | ||||
| 			.cbs = { | ||||
|  | ||||
| @ -240,6 +240,20 @@ struct yang_data * | ||||
| lib_vrf_zebra_ribs_rib_route_route_entry_nexthop_group_nexthop_color_get_elem( | ||||
| 	struct nb_cb_get_elem_args *args); | ||||
| const void * | ||||
| lib_vrf_zebra_ribs_rib_route_route_entry_nexthop_group_nexthop_srv6_segs_stack_entry_get_next( | ||||
| 	struct nb_cb_get_next_args *args); | ||||
| int lib_vrf_zebra_ribs_rib_route_route_entry_nexthop_group_nexthop_srv6_segs_stack_entry_get_keys( | ||||
| 	struct nb_cb_get_keys_args *args); | ||||
| const void * | ||||
| lib_vrf_zebra_ribs_rib_route_route_entry_nexthop_group_nexthop_srv6_segs_stack_entry_lookup_entry( | ||||
| 	struct nb_cb_lookup_entry_args *args); | ||||
| struct yang_data * | ||||
| lib_vrf_zebra_ribs_rib_route_route_entry_nexthop_group_nexthop_srv6_segs_stack_entry_id_get_elem( | ||||
| 	struct nb_cb_get_elem_args *args); | ||||
| struct yang_data * | ||||
| lib_vrf_zebra_ribs_rib_route_route_entry_nexthop_group_nexthop_srv6_segs_stack_entry_seg_get_elem( | ||||
| 	struct nb_cb_get_elem_args *args); | ||||
| const void * | ||||
| lib_vrf_zebra_ribs_rib_route_route_entry_nexthop_group_nexthop_mpls_label_stack_entry_get_next( | ||||
| 	struct nb_cb_get_next_args *args); | ||||
| int lib_vrf_zebra_ribs_rib_route_route_entry_nexthop_group_nexthop_mpls_label_stack_entry_get_keys( | ||||
|  | ||||
| @ -840,6 +840,58 @@ lib_vrf_zebra_ribs_rib_route_route_entry_nexthop_group_nexthop_color_get_elem( | ||||
| 	return NULL; | ||||
| } | ||||
| 
 | ||||
| /*
 | ||||
|  * XPath: | ||||
|  * /frr-vrf:lib/vrf/frr-zebra:zebra/ribs/rib/route/route-entry/nexthop-group/nexthop/srv6-segs-stack/entry | ||||
|  */ | ||||
| const void * | ||||
| lib_vrf_zebra_ribs_rib_route_route_entry_nexthop_group_nexthop_srv6_segs_stack_entry_get_next( | ||||
| 	struct nb_cb_get_next_args *args) | ||||
| { | ||||
| 	/* TODO: implement me. */ | ||||
| 	return NULL; | ||||
| } | ||||
| 
 | ||||
| int lib_vrf_zebra_ribs_rib_route_route_entry_nexthop_group_nexthop_srv6_segs_stack_entry_get_keys( | ||||
| 	struct nb_cb_get_keys_args *args) | ||||
| { | ||||
| 	/* TODO: implement me. */ | ||||
| 	return NB_OK; | ||||
| } | ||||
| 
 | ||||
| const void * | ||||
| lib_vrf_zebra_ribs_rib_route_route_entry_nexthop_group_nexthop_srv6_segs_stack_entry_lookup_entry( | ||||
| 	struct nb_cb_lookup_entry_args *args) | ||||
| { | ||||
| 	/* TODO: implement me. */ | ||||
| 	return NULL; | ||||
| } | ||||
| 
 | ||||
| /*
 | ||||
|  * XPath: | ||||
|  * /frr-vrf:lib/vrf/frr-zebra:zebra/ribs/rib/route/route-entry/nexthop-group/nexthop/srv6-segs-stack/entry/id | ||||
|  */ | ||||
| struct yang_data * | ||||
| lib_vrf_zebra_ribs_rib_route_route_entry_nexthop_group_nexthop_srv6_segs_stack_entry_id_get_elem( | ||||
| 	struct nb_cb_get_elem_args *args) | ||||
| { | ||||
| 	/* TODO: implement me. */ | ||||
| 	return NULL; | ||||
| } | ||||
| 
 | ||||
| /*
 | ||||
|  * XPath: | ||||
|  * /frr-vrf:lib/vrf/frr-zebra:zebra/ribs/rib/route/route-entry/nexthop-group/nexthop/srv6-segs-stack/entry/seg | ||||
|  */ | ||||
| struct yang_data * | ||||
| lib_vrf_zebra_ribs_rib_route_route_entry_nexthop_group_nexthop_srv6_segs_stack_entry_seg_get_elem( | ||||
| 	struct nb_cb_get_elem_args *args) | ||||
| { | ||||
| 	/* TODO: implement me. */ | ||||
| 	return NULL; | ||||
| } | ||||
| 
 | ||||
| 
 | ||||
| /*
 | ||||
|  * XPath: | ||||
|  * /frr-vrf:lib/vrf/frr-zebra:zebra/ribs/rib/route/route-entry/nexthop-group/nexthop/mpls-label-stack/entry | ||||
|  | ||||
| @ -1868,11 +1868,18 @@ static struct nexthop *nexthop_set_resolved(afi_t afi, | ||||
| 				   labels); | ||||
| 
 | ||||
| 	if (nexthop->nh_srv6) { | ||||
| 		nexthop_add_srv6_seg6local(resolved_hop, | ||||
| 					   nexthop->nh_srv6->seg6local_action, | ||||
| 					   &nexthop->nh_srv6->seg6local_ctx); | ||||
| 		nexthop_add_srv6_seg6(resolved_hop, | ||||
| 				      &nexthop->nh_srv6->seg6_segs); | ||||
| 		if (nexthop->nh_srv6->seg6local_action != | ||||
| 		    ZEBRA_SEG6_LOCAL_ACTION_UNSPEC) | ||||
| 			nexthop_add_srv6_seg6local(resolved_hop, | ||||
| 						   nexthop->nh_srv6 | ||||
| 							   ->seg6local_action, | ||||
| 						   &nexthop->nh_srv6 | ||||
| 							    ->seg6local_ctx); | ||||
| 		if (nexthop->nh_srv6->seg6_segs) | ||||
| 			nexthop_add_srv6_seg6(resolved_hop, | ||||
| 					      &nexthop->nh_srv6->seg6_segs->seg[0], | ||||
| 					      nexthop->nh_srv6->seg6_segs | ||||
| 						      ->num_segs); | ||||
| 	} | ||||
| 
 | ||||
| 	resolved_hop->rparent = nexthop; | ||||
|  | ||||
| @ -1268,6 +1268,7 @@ void show_nexthop_json_helper(json_object *json_nexthop, | ||||
| 	json_object *json_backups = NULL; | ||||
| 	json_object *json_seg6local = NULL; | ||||
| 	json_object *json_seg6 = NULL; | ||||
| 	json_object *json_segs = NULL; | ||||
| 	int i; | ||||
| 
 | ||||
| 	json_object_int_add(json_nexthop, "flags", nexthop->flags); | ||||
| @ -1425,11 +1426,31 @@ void show_nexthop_json_helper(json_object *json_nexthop, | ||||
| 				nexthop->nh_srv6->seg6local_action)); | ||||
| 		json_object_object_add(json_nexthop, "seg6local", | ||||
| 				       json_seg6local); | ||||
| 
 | ||||
| 		json_seg6 = json_object_new_object(); | ||||
| 		json_object_string_addf(json_seg6, "segs", "%pI6", | ||||
| 					&nexthop->nh_srv6->seg6_segs); | ||||
| 		json_object_object_add(json_nexthop, "seg6", json_seg6); | ||||
| 		if (nexthop->nh_srv6->seg6_segs && | ||||
| 		    nexthop->nh_srv6->seg6_segs->num_segs == 1) { | ||||
| 			json_seg6 = json_object_new_object(); | ||||
| 			json_object_string_addf(json_seg6, "segs", "%pI6", | ||||
| 						&nexthop->nh_srv6->seg6_segs | ||||
| 							 ->seg[0]); | ||||
| 			json_object_object_add(json_nexthop, "seg6", json_seg6); | ||||
| 		} else { | ||||
| 			json_segs = json_object_new_array(); | ||||
| 			if (nexthop->nh_srv6->seg6_segs) { | ||||
| 				for (int seg_idx = 0; | ||||
| 				     seg_idx < | ||||
| 				     nexthop->nh_srv6->seg6_segs->num_segs; | ||||
| 				     seg_idx++) | ||||
| 					json_object_array_add( | ||||
| 						json_segs, | ||||
| 						json_object_new_stringf( | ||||
| 							"%pI6", | ||||
| 							&nexthop->nh_srv6 | ||||
| 								 ->seg6_segs | ||||
| 								 ->seg[seg_idx])); | ||||
| 				json_object_object_add(json_nexthop, "seg6", | ||||
| 						       json_segs); | ||||
| 			} | ||||
| 		} | ||||
| 	} | ||||
| } | ||||
| 
 | ||||
| @ -1440,7 +1461,9 @@ void show_route_nexthop_helper(struct vty *vty, const struct route_entry *re, | ||||
| 			       const struct nexthop *nexthop) | ||||
| { | ||||
| 	char buf[MPLS_LABEL_STRLEN]; | ||||
| 	int i; | ||||
| 	char seg_buf[SRV6_SEG_STRLEN]; | ||||
| 	struct seg6_segs segs; | ||||
| 	uint8_t i; | ||||
| 
 | ||||
| 	switch (nexthop->type) { | ||||
| 	case NEXTHOP_TYPE_IPV4: | ||||
| @ -1538,9 +1561,17 @@ void show_route_nexthop_helper(struct vty *vty, const struct route_entry *re, | ||||
| 				seg6local_action2str( | ||||
| 					nexthop->nh_srv6->seg6local_action), | ||||
| 				buf); | ||||
| 		if (IPV6_ADDR_CMP(&nexthop->nh_srv6->seg6_segs, &in6addr_any)) | ||||
| 			vty_out(vty, ", seg6 %pI6", | ||||
| 				&nexthop->nh_srv6->seg6_segs); | ||||
| 		if (nexthop->nh_srv6->seg6_segs && | ||||
| 		    IPV6_ADDR_CMP(&nexthop->nh_srv6->seg6_segs->seg[0], | ||||
| 				  &in6addr_any)) { | ||||
| 			segs.num_segs = nexthop->nh_srv6->seg6_segs->num_segs; | ||||
| 			for (i = 0; i < segs.num_segs; i++) | ||||
| 				memcpy(&segs.segs[i], | ||||
| 				       &nexthop->nh_srv6->seg6_segs->seg[i], | ||||
| 				       sizeof(struct in6_addr)); | ||||
| 			snprintf_seg6_segs(seg_buf, SRV6_SEG_STRLEN, &segs); | ||||
| 			vty_out(vty, ", seg6 %s", seg_buf); | ||||
| 		} | ||||
| 	} | ||||
| 
 | ||||
| 	if (nexthop->weight) | ||||
|  | ||||
		Loading…
	
		Reference in New Issue
	
	Block a user
	 Russ White
						Russ White