diff options
| -rw-r--r-- | bgpd/bgp_attr.c | 318 | ||||
| -rw-r--r-- | bgpd/bgp_attr.h | 32 | ||||
| -rw-r--r-- | bgpd/bgp_memory.c | 3 | ||||
| -rw-r--r-- | bgpd/bgp_memory.h | 3 | ||||
| -rw-r--r-- | bgpd/bgp_route.c | 38 | ||||
| -rw-r--r-- | bgpd/bgp_route.h | 7 | ||||
| -rw-r--r-- | bgpd/rfapi/rfapi_vty.c | 8 | ||||
| -rw-r--r-- | lib/srv6.c | 116 | ||||
| -rw-r--r-- | lib/srv6.h | 133 | ||||
| -rw-r--r-- | lib/subdir.am | 2 | 
10 files changed, 647 insertions, 13 deletions
diff --git a/bgpd/bgp_attr.c b/bgpd/bgp_attr.c index 16de59b72c..53a096753e 100644 --- a/bgpd/bgp_attr.c +++ b/bgpd/bgp_attr.c @@ -32,6 +32,7 @@  #include "table.h"  #include "filter.h"  #include "command.h" +#include "srv6.h"  #include "bgpd/bgpd.h"  #include "bgpd/bgp_attr.h" @@ -201,6 +202,8 @@ static struct hash *encap_hash = NULL;  #if ENABLE_BGP_VNC  static struct hash *vnc_hash = NULL;  #endif +static struct hash *srv6_l3vpn_hash; +static struct hash *srv6_vpn_hash;  struct bgp_attr_encap_subtlv *encap_tlv_dup(struct bgp_attr_encap_subtlv *orig)  { @@ -434,6 +437,158 @@ static void transit_unintern(struct transit **transit)  	}  } +static void *srv6_l3vpn_hash_alloc(void *p) +{ +	return p; +} + +static void srv6_l3vpn_free(struct bgp_attr_srv6_l3vpn *l3vpn) +{ +	XFREE(MTYPE_BGP_SRV6_L3VPN, l3vpn); +} + +static struct bgp_attr_srv6_l3vpn * +srv6_l3vpn_intern(struct bgp_attr_srv6_l3vpn *l3vpn) +{ +	struct bgp_attr_srv6_l3vpn *find; + +	find = hash_get(srv6_l3vpn_hash, l3vpn, srv6_l3vpn_hash_alloc); +	if (find != l3vpn) +		srv6_l3vpn_free(l3vpn); +	find->refcnt++; +	return find; +} + +static void srv6_l3vpn_unintern(struct bgp_attr_srv6_l3vpn **l3vpnp) +{ +	struct bgp_attr_srv6_l3vpn *l3vpn = *l3vpnp; + +	if (l3vpn->refcnt) +		l3vpn->refcnt--; + +	if (l3vpn->refcnt == 0) { +		hash_release(srv6_l3vpn_hash, l3vpn); +		srv6_l3vpn_free(l3vpn); +		*l3vpnp = NULL; +	} +} + +static void *srv6_vpn_hash_alloc(void *p) +{ +	return p; +} + +static void srv6_vpn_free(struct bgp_attr_srv6_vpn *vpn) +{ +	XFREE(MTYPE_BGP_SRV6_VPN, vpn); +} + +static struct bgp_attr_srv6_vpn *srv6_vpn_intern(struct bgp_attr_srv6_vpn *vpn) +{ +	struct bgp_attr_srv6_vpn *find; + +	find = hash_get(srv6_vpn_hash, vpn, srv6_vpn_hash_alloc); +	if (find != vpn) +		srv6_vpn_free(vpn); +	find->refcnt++; +	return find; +} + +static void srv6_vpn_unintern(struct bgp_attr_srv6_vpn **vpnp) +{ +	struct bgp_attr_srv6_vpn *vpn = *vpnp; + +	if (vpn->refcnt) +		vpn->refcnt--; + +	if (vpn->refcnt == 0) { +		hash_release(srv6_vpn_hash, vpn); +		srv6_vpn_free(vpn); +		*vpnp = NULL; +	} +} + +static uint32_t srv6_l3vpn_hash_key_make(const void *p) +{ +	const struct bgp_attr_srv6_l3vpn *l3vpn = p; +	uint32_t key = 0; + +	key = jhash(&l3vpn->sid, 16, key); +	key = jhash_1word(l3vpn->sid_flags, key); +	key = jhash_1word(l3vpn->endpoint_behavior, key); +	return key; +} + +static bool srv6_l3vpn_hash_cmp(const void *p1, const void *p2) +{ +	const struct bgp_attr_srv6_l3vpn *l3vpn1 = p1; +	const struct bgp_attr_srv6_l3vpn *l3vpn2 = p2; + +	return sid_same(&l3vpn1->sid, &l3vpn2->sid) +	       && l3vpn1->sid_flags == l3vpn2->sid_flags +	       && l3vpn1->endpoint_behavior == l3vpn2->endpoint_behavior; +} + +static bool srv6_l3vpn_same(const struct bgp_attr_srv6_l3vpn *h1, +			    const struct bgp_attr_srv6_l3vpn *h2) +{ +	if (h1 == h2) +		return true; +	else if (h1 == NULL || h2 == NULL) +		return false; +	else +		return srv6_l3vpn_hash_cmp((const void *)h1, (const void *)h2); +} + +static unsigned int srv6_vpn_hash_key_make(const void *p) +{ +	const struct bgp_attr_srv6_vpn *vpn = p; +	uint32_t key = 0; + +	key = jhash(&vpn->sid, 16, key); +	key = jhash_1word(vpn->sid_flags, key); +	return key; +} + +static bool srv6_vpn_hash_cmp(const void *p1, const void *p2) +{ +	const struct bgp_attr_srv6_vpn *vpn1 = p1; +	const struct bgp_attr_srv6_vpn *vpn2 = p2; + +	return sid_same(&vpn1->sid, &vpn2->sid) +	       && vpn1->sid_flags == vpn2->sid_flags; +} + +static bool srv6_vpn_same(const struct bgp_attr_srv6_vpn *h1, +			  const struct bgp_attr_srv6_vpn *h2) +{ +	if (h1 == h2) +		return true; +	else if (h1 == NULL || h2 == NULL) +		return false; +	else +		return srv6_vpn_hash_cmp((const void *)h1, (const void *)h2); +} + +static void srv6_init(void) +{ +	srv6_l3vpn_hash = +		hash_create(srv6_l3vpn_hash_key_make, srv6_l3vpn_hash_cmp, +			    "BGP Prefix-SID SRv6-L3VPN-Service-TLV"); +	srv6_vpn_hash = hash_create(srv6_vpn_hash_key_make, srv6_vpn_hash_cmp, +				    "BGP Prefix-SID SRv6-VPN-Service-TLV"); +} + +static void srv6_finish(void) +{ +	hash_clean(srv6_l3vpn_hash, (void (*)(void *))srv6_l3vpn_free); +	hash_free(srv6_l3vpn_hash); +	srv6_l3vpn_hash = NULL; +	hash_clean(srv6_vpn_hash, (void (*)(void *))srv6_vpn_free); +	hash_free(srv6_vpn_hash); +	srv6_vpn_hash = NULL; +} +  static unsigned int transit_hash_key_make(const void *p)  {  	const struct transit *transit = p; @@ -557,7 +712,9 @@ bool attrhash_cmp(const void *p1, const void *p2)  		    && overlay_index_same(attr1, attr2)  		    && attr1->nh_ifindex == attr2->nh_ifindex  		    && attr1->nh_lla_ifindex == attr2->nh_lla_ifindex -		    && attr1->distance == attr2->distance) +		    && attr1->distance == attr2->distance +		    && srv6_l3vpn_same(attr1->srv6_l3vpn, attr2->srv6_l3vpn) +		    && srv6_vpn_same(attr1->srv6_vpn, attr2->srv6_vpn))  			return true;  	} @@ -588,12 +745,22 @@ static void attrhash_finish(void)  static void attr_show_all_iterator(struct hash_bucket *bucket, struct vty *vty)  {  	struct attr *attr = bucket->data; +	char sid_str[BUFSIZ];  	vty_out(vty, "attr[%ld] nexthop %s\n", attr->refcnt,  		inet_ntoa(attr->nexthop)); -	vty_out(vty, "\tflags: %" PRIu64 " med: %u local_pref: %u origin: %u weight: %u label: %u\n", + +	sid_str[0] = '\0'; +	if (attr->srv6_l3vpn) +		inet_ntop(AF_INET6, &attr->srv6_l3vpn->sid, sid_str, BUFSIZ); +	else if (attr->srv6_vpn) +		inet_ntop(AF_INET6, &attr->srv6_vpn->sid, sid_str, BUFSIZ); + +	vty_out(vty, +		"\tflags: %" PRIu64 +		" med: %u local_pref: %u origin: %u weight: %u label: %u sid: %s\n",  		attr->flag, attr->med, attr->local_pref, attr->origin, -		attr->weight, attr->label); +		attr->weight, attr->label, sid_str);  }  void attr_show_all(struct vty *vty) @@ -618,6 +785,11 @@ static void *bgp_attr_hash_alloc(void *p)  		val->vnc_subtlvs = NULL;  	}  #endif +	if (val->srv6_l3vpn) +		val->srv6_l3vpn = NULL; +	if (val->srv6_vpn) +		val->srv6_vpn = NULL; +  	attr->refcnt = 0;  	return attr;  } @@ -672,6 +844,18 @@ struct attr *bgp_attr_intern(struct attr *attr)  		else  			attr->encap_subtlvs->refcnt++;  	} +	if (attr->srv6_l3vpn) { +		if (!attr->srv6_l3vpn->refcnt) +			attr->srv6_l3vpn = srv6_l3vpn_intern(attr->srv6_l3vpn); +		else +			attr->srv6_l3vpn->refcnt++; +	} +	if (attr->srv6_vpn) { +		if (!attr->srv6_vpn->refcnt) +			attr->srv6_vpn = srv6_vpn_intern(attr->srv6_vpn); +		else +			attr->srv6_vpn->refcnt++; +	}  #if ENABLE_BGP_VNC  	if (attr->vnc_subtlvs) {  		if (!attr->vnc_subtlvs->refcnt) @@ -862,6 +1046,12 @@ void bgp_attr_unintern_sub(struct attr *attr)  	if (attr->vnc_subtlvs)  		encap_unintern(&attr->vnc_subtlvs, VNC_SUBTLV_TYPE);  #endif + +	if (attr->srv6_l3vpn) +		srv6_l3vpn_unintern(&attr->srv6_l3vpn); + +	if (attr->srv6_vpn) +		srv6_vpn_unintern(&attr->srv6_vpn);  }  /* @@ -2147,6 +2337,9 @@ static bgp_attr_parse_ret_t bgp_attr_psid_sub(uint8_t type, uint16_t length,  	uint32_t srgb_base;  	uint32_t srgb_range;  	int srgb_count; +	uint8_t sid_type, sid_flags; +	uint16_t endpoint_behavior; +	char buf[BUFSIZ];  	if (type == BGP_PREFIX_SID_LABEL_INDEX) {  		if (STREAM_READABLE(peer->curr) < length @@ -2268,13 +2461,81 @@ static bgp_attr_parse_ret_t bgp_attr_psid_sub(uint8_t type, uint16_t length,  		}  	} -	/* -	 * Placeholder code for Unsupported TLV -	 *  - SRv6 L3 Service TLV (type5) -	 *  - SRv6 L2 Service TLV (type6) -	 */ -	else if (type == BGP_PREFIX_SID_SRV6_L3_SERVICE -	    || type == BGP_PREFIX_SID_SRV6_L2_SERVICE) { +	/* Placeholder code for the VPN-SID Service type */ +	else if (type == BGP_PREFIX_SID_VPN_SID) { +		if (STREAM_READABLE(peer->curr) < length +		    || length != BGP_PREFIX_SID_VPN_SID_LENGTH) { +			flog_err(EC_BGP_ATTR_LEN, +				 "Prefix SID VPN SID length is %" PRIu16 +				 " instead of %u", +				 length, BGP_PREFIX_SID_VPN_SID_LENGTH); +			return bgp_attr_malformed(args, +						  BGP_NOTIFY_UPDATE_ATTR_LENG_ERR, +						  args->total); +		} + +		/* Parse VPN-SID Sub-TLV */ +		stream_getc(peer->curr);               /* reserved  */ +		sid_type = stream_getc(peer->curr);    /* sid_type  */ +		sid_flags = stream_getc(peer->curr);   /* sid_flags */ +		stream_get(&ipv6_sid, peer->curr, +			   sizeof(ipv6_sid)); /* sid_value */ + +		/* Log VPN-SID Sub-TLV */ +		if (BGP_DEBUG(vpn, VPN_LEAK_LABEL)) { +			inet_ntop(AF_INET6, &ipv6_sid, buf, sizeof(buf)); +			zlog_debug( +				"%s: vpn-sid: sid %s, sid-type 0x%02x sid-flags 0x%02x", +				__func__, buf, sid_type, sid_flags); +		} + +		/* Configure from Info */ +		attr->srv6_vpn = XMALLOC(MTYPE_BGP_SRV6_VPN, +					 sizeof(struct bgp_attr_srv6_vpn)); +		attr->srv6_vpn->refcnt = 0; +		attr->srv6_vpn->sid_flags = sid_flags; +		sid_copy(&attr->srv6_vpn->sid, &ipv6_sid); +	} + +	/* Placeholder code for the SRv6 L3 Service type */ +	else if (type == BGP_PREFIX_SID_SRV6_L3_SERVICE) { +		if (STREAM_READABLE(peer->curr) < length +		    || length != BGP_PREFIX_SID_SRV6_L3_SERVICE_LENGTH) { +			flog_err(EC_BGP_ATTR_LEN, +				 "Prefix SID SRv6 L3-Service length is %" PRIu16 +				 " instead of %u", +				 length, BGP_PREFIX_SID_SRV6_L3_SERVICE_LENGTH); +			return bgp_attr_malformed(args, +				 BGP_NOTIFY_UPDATE_ATTR_LENG_ERR, +				 args->total); +		} + +		/* Parse L3-SERVICE Sub-TLV */ +		stream_getc(peer->curr);               /* reserved  */ +		stream_get(&ipv6_sid, peer->curr, +			   sizeof(ipv6_sid)); /* sid_value */ +		sid_flags = stream_getc(peer->curr);   /* sid_flags */ +		endpoint_behavior = stream_getw(peer->curr); /* endpoint */ +		stream_getc(peer->curr);               /* reserved  */ + +		/* Log L3-SERVICE Sub-TLV */ +		if (BGP_DEBUG(vpn, VPN_LEAK_LABEL)) { +			inet_ntop(AF_INET6, &ipv6_sid, buf, sizeof(buf)); +			zlog_debug( +				"%s: srv6-l3-srv sid %s, sid-flags 0x%02x, end-behaviour 0x%04x", +				__func__, buf, sid_flags, endpoint_behavior); +		} + +		/* Configure from Info */ +		attr->srv6_l3vpn = XMALLOC(MTYPE_BGP_SRV6_L3VPN, +					   sizeof(struct bgp_attr_srv6_l3vpn)); +		attr->srv6_l3vpn->sid_flags = sid_flags; +		attr->srv6_l3vpn->endpoint_behavior = endpoint_behavior; +		sid_copy(&attr->srv6_l3vpn->sid, &ipv6_sid); +	} + +	/* Placeholder code for Unsupported TLV */ +	else {  		if (STREAM_READABLE(peer->curr) < length) {  			flog_err( @@ -2966,9 +3227,8 @@ size_t bgp_packet_mpattr_start(struct stream *s, struct peer *peer, afi_t afi,  	/* Nexthop AFI */  	if (afi == AFI_IP -	    && (safi == SAFI_UNICAST || -		safi == SAFI_LABELED_UNICAST || -		safi == SAFI_MULTICAST)) +	    && (safi == SAFI_UNICAST || safi == SAFI_LABELED_UNICAST +		|| safi == SAFI_MPLS_VPN || safi == SAFI_MULTICAST))  		nh_afi = peer_cap_enhe(peer, afi, safi) ? AFI_IP6 : AFI_IP;  	else  		nh_afi = BGP_NEXTHOP_AFI_FROM_NHLEN(attr->mp_nexthop_len); @@ -3610,6 +3870,36 @@ bgp_size_t bgp_packet_attribute(struct bgp *bgp, struct peer *peer,  		}  	} +	/* SRv6 Service Information Attribute. */ +	if (afi == AFI_IP && safi == SAFI_MPLS_VPN) { +		if (attr->srv6_l3vpn) { +			stream_putc(s, BGP_ATTR_FLAG_OPTIONAL +					       | BGP_ATTR_FLAG_TRANS); +			stream_putc(s, BGP_ATTR_PREFIX_SID); +			stream_putc(s, 24);     /* tlv len */ +			stream_putc(s, BGP_PREFIX_SID_SRV6_L3_SERVICE); +			stream_putw(s, 21);     /* sub-tlv len */ +			stream_putc(s, 0);      /* reserved */ +			stream_put(s, &attr->srv6_l3vpn->sid, +				   sizeof(attr->srv6_l3vpn->sid)); /* sid */ +			stream_putc(s, 0);      /* sid_flags */ +			stream_putw(s, 0xffff); /* endpoint */ +			stream_putc(s, 0);      /* reserved */ +		} else if (attr->srv6_vpn) { +			stream_putc(s, BGP_ATTR_FLAG_OPTIONAL +					       | BGP_ATTR_FLAG_TRANS); +			stream_putc(s, BGP_ATTR_PREFIX_SID); +			stream_putc(s, 22);     /* tlv len */ +			stream_putc(s, BGP_PREFIX_SID_VPN_SID); +			stream_putw(s, 0x13);   /* tlv len */ +			stream_putc(s, 0x00);   /* reserved */ +			stream_putc(s, 0x01);   /* sid_type */ +			stream_putc(s, 0x00);   /* sif_flags */ +			stream_put(s, &attr->srv6_vpn->sid, +				   sizeof(attr->srv6_vpn->sid)); /* sid */ +		} +	} +  	if (send_as4_path) {  		/* If the peer is NOT As4 capable, AND */  		/* there are ASnums > 65535 in path  THEN @@ -3738,6 +4028,7 @@ void bgp_attr_init(void)  	cluster_init();  	transit_init();  	encap_init(); +	srv6_init();  }  void bgp_attr_finish(void) @@ -3750,6 +4041,7 @@ void bgp_attr_finish(void)  	cluster_finish();  	transit_finish();  	encap_finish(); +	srv6_finish();  }  /* Make attribute packet. */ diff --git a/bgpd/bgp_attr.h b/bgpd/bgp_attr.h index 8bd527c0ff..2e91f56df5 100644 --- a/bgpd/bgp_attr.h +++ b/bgpd/bgp_attr.h @@ -62,12 +62,15 @@  #define BGP_PREFIX_SID_LABEL_INDEX     1  #define BGP_PREFIX_SID_IPV6            2  #define BGP_PREFIX_SID_ORIGINATOR_SRGB 3 +#define BGP_PREFIX_SID_VPN_SID 4  #define BGP_PREFIX_SID_SRV6_L3_SERVICE 5  #define BGP_PREFIX_SID_SRV6_L2_SERVICE 6  #define BGP_PREFIX_SID_LABEL_INDEX_LENGTH      7  #define BGP_PREFIX_SID_IPV6_LENGTH            19  #define BGP_PREFIX_SID_ORIGINATOR_SRGB_LENGTH  6 +#define BGP_PREFIX_SID_VPN_SID_LENGTH         19 +#define BGP_PREFIX_SID_SRV6_L3_SERVICE_LENGTH 21  #define BGP_ATTR_NH_AFI(afi, attr) \  	((afi != AFI_L2VPN) ? afi : \ @@ -111,6 +114,29 @@ enum pta_type {  	PMSI_TNLTYPE_MAX = PMSI_TNLTYPE_MLDP_MP2MP  }; +/* + * Prefix-SID type-4 + * SRv6-VPN-SID-TLV + * draft-dawra-idr-srv6-vpn-04 + */ +struct bgp_attr_srv6_vpn { +	unsigned long refcnt; +	uint8_t sid_flags; +	struct in6_addr sid; +}; + +/* + * Prefix-SID type-5 + * SRv6-L3VPN-Service-TLV + * draft-dawra-idr-srv6-vpn-05 + */ +struct bgp_attr_srv6_l3vpn { +	unsigned long refcnt; +	uint8_t sid_flags; +	uint16_t endpoint_behavior; +	struct in6_addr sid; +}; +  /* BGP core attribute structure. */  struct attr {  	/* AS Path structure */ @@ -198,6 +224,12 @@ struct attr {  	/* MPLS label */  	mpls_label_t label; +	/* SRv6 VPN SID */ +	struct bgp_attr_srv6_vpn *srv6_vpn; + +	/* SRv6 L3VPN SID */ +	struct bgp_attr_srv6_l3vpn *srv6_l3vpn; +  	uint16_t encap_tunneltype;		     /* grr */  	struct bgp_attr_encap_subtlv *encap_subtlvs; /* rfc5512 */ diff --git a/bgpd/bgp_memory.c b/bgpd/bgp_memory.c index 3e4dfb11ad..41c4108c0a 100644 --- a/bgpd/bgp_memory.c +++ b/bgpd/bgp_memory.c @@ -128,3 +128,6 @@ DEFINE_MTYPE(BGPD, BGP_FLOWSPEC_RULE_STR, "BGP flowspec rule str")  DEFINE_MTYPE(BGPD, BGP_FLOWSPEC_COMPILED, "BGP flowspec compiled")  DEFINE_MTYPE(BGPD, BGP_FLOWSPEC_NAME, "BGP flowspec name")  DEFINE_MTYPE(BGPD, BGP_FLOWSPEC_INDEX, "BGP flowspec index") + +DEFINE_MTYPE(BGPD, BGP_SRV6_L3VPN, "BGP prefix-sid srv6 l3vpn servcie") +DEFINE_MTYPE(BGPD, BGP_SRV6_VPN, "BGP prefix-sid srv6 vpn service") diff --git a/bgpd/bgp_memory.h b/bgpd/bgp_memory.h index 03715f5621..5428022551 100644 --- a/bgpd/bgp_memory.h +++ b/bgpd/bgp_memory.h @@ -126,4 +126,7 @@ DECLARE_MTYPE(BGP_FLOWSPEC_COMPILED)  DECLARE_MTYPE(BGP_FLOWSPEC_NAME)  DECLARE_MTYPE(BGP_FLOWSPEC_INDEX) +DECLARE_MTYPE(BGP_SRV6_L3VPN) +DECLARE_MTYPE(BGP_SRV6_VPN) +  #endif /* _QUAGGA_BGP_MEMORY_H */ diff --git a/bgpd/bgp_route.c b/bgpd/bgp_route.c index 5f4486b800..e35d160197 100644 --- a/bgpd/bgp_route.c +++ b/bgpd/bgp_route.c @@ -38,6 +38,7 @@  #include "workqueue.h"  #include "queue.h"  #include "memory.h" +#include "srv6.h"  #include "lib/json.h"  #include "lib_errors.h" @@ -3431,6 +3432,22 @@ int bgp_update(struct peer *peer, struct prefix *p, uint32_t addpath_id,  				bgp_set_valid_label(&extra->label[0]);  		} +		/* Update SRv6 SID */ +		if (attr->srv6_l3vpn) { +			extra = bgp_path_info_extra_get(pi); +			if (sid_diff(&extra->sid[0], &attr->srv6_l3vpn->sid)) { +				sid_copy(&extra->sid[0], +					 &attr->srv6_l3vpn->sid); +				extra->num_sids = 1; +			} +		} else if (attr->srv6_vpn) { +			extra = bgp_path_info_extra_get(pi); +			if (sid_diff(&extra->sid[0], &attr->srv6_vpn->sid)) { +				sid_copy(&extra->sid[0], &attr->srv6_vpn->sid); +				extra->num_sids = 1; +			} +		} +  #if ENABLE_BGP_VNC  		if ((afi == AFI_IP || afi == AFI_IP6)  		    && (safi == SAFI_UNICAST)) { @@ -3610,6 +3627,18 @@ int bgp_update(struct peer *peer, struct prefix *p, uint32_t addpath_id,  			bgp_set_valid_label(&extra->label[0]);  	} +	/* Update SRv6 SID */ +	if (safi == SAFI_MPLS_VPN) { +		extra = bgp_path_info_extra_get(new); +		if (attr->srv6_l3vpn) { +			sid_copy(&extra->sid[0], &attr->srv6_l3vpn->sid); +			extra->num_sids = 1; +		} else if (attr->srv6_vpn) { +			sid_copy(&extra->sid[0], &attr->srv6_vpn->sid); +			extra->num_sids = 1; +		} +	} +  	/* Update Overlay Index */  	if (afi == AFI_L2VPN) {  		overlay_index_update(new->attr, @@ -8994,6 +9023,15 @@ void route_vty_out_detail(struct vty *vty, struct bgp *bgp,  			vty_out(vty, "      Remote label: %d\n", label);  	} +	/* Remote SID */ +	if (path->extra && path->extra->num_sids > 0 && safi != SAFI_EVPN) { +		inet_ntop(AF_INET6, &path->extra->sid, buf, sizeof(buf)); +		if (json_paths) +			json_object_string_add(json_path, "remoteSid", buf); +		else +			vty_out(vty, "      Remote SID: %s\n", buf); +	} +  	/* Label Index */  	if (attr->label_index != BGP_INVALID_LABEL_INDEX) {  		if (json_paths) diff --git a/bgpd/bgp_route.h b/bgpd/bgp_route.h index b9f3f3f762..91f8ef40b2 100644 --- a/bgpd/bgp_route.h +++ b/bgpd/bgp_route.h @@ -78,6 +78,9 @@ enum bgp_show_adj_route_type {   */  #define BGP_MAX_LABELS 2 +/* Maximum number of sids we can process or send with a prefix. */ +#define BGP_MAX_SIDS 6 +  /* Error codes for handling NLRI */  #define BGP_NLRI_PARSE_OK 0  #define BGP_NLRI_PARSE_ERROR_PREFIX_OVERFLOW -1 @@ -118,6 +121,10 @@ struct bgp_path_info_extra {  	uint16_t af_flags;  #define BGP_EVPN_MACIP_TYPE_SVI_IP (1 << 0) +	/* SRv6 SID(s) for SRv6-VPN */ +	struct in6_addr sid[BGP_MAX_SIDS]; +	uint32_t num_sids; +  #if ENABLE_BGP_VNC  	union { diff --git a/bgpd/rfapi/rfapi_vty.c b/bgpd/rfapi/rfapi_vty.c index 5ad822211b..77fcf909c8 100644 --- a/bgpd/rfapi/rfapi_vty.c +++ b/bgpd/rfapi/rfapi_vty.c @@ -431,6 +431,14 @@ void rfapi_vty_out_vncinfo(struct vty *vty, struct prefix *p,  		else  			vty_out(vty, " label=%u",  				decode_label(&bpi->extra->label[0])); + +		if (bpi->extra->num_sids) { +			char buf[BUFSIZ]; + +			vty_out(vty, " sid=%s", +				inet_ntop(AF_INET6, &bpi->extra->sid[0], buf, +					  sizeof(buf))); +		}  	}  	if (!rfapiGetVncLifetime(bpi->attr, &lifetime)) { diff --git a/lib/srv6.c b/lib/srv6.c new file mode 100644 index 0000000000..be340f13f5 --- /dev/null +++ b/lib/srv6.c @@ -0,0 +1,116 @@ +/* + * SRv6 definitions + * Copyright (C) 2020  Hiroki Shirokura, LINE Corporation + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License as published by the Free + * Software Foundation; either version 2 of the License, or (at your option) + * any later version. + * + * This program is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License for + * more details. + * + * You should have received a copy of the GNU General Public License along + * with this program; see the file COPYING; if not, write to the Free Software + * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA + */ + +#include "srv6.h" +#include "log.h" + +const char *seg6local_action2str(uint32_t action) +{ +	switch (action) { +	case ZEBRA_SEG6_LOCAL_ACTION_END: +		return "End"; +	case ZEBRA_SEG6_LOCAL_ACTION_END_X: +		return "End.X"; +	case ZEBRA_SEG6_LOCAL_ACTION_END_T: +		return "End.T"; +	case ZEBRA_SEG6_LOCAL_ACTION_END_DX2: +		return "End.DX2"; +	case ZEBRA_SEG6_LOCAL_ACTION_END_DX6: +		return "End.DX6"; +	case ZEBRA_SEG6_LOCAL_ACTION_END_DX4: +		return "End.DX4"; +	case ZEBRA_SEG6_LOCAL_ACTION_END_DT6: +		return "End.DT6"; +	case ZEBRA_SEG6_LOCAL_ACTION_END_DT4: +		return "End.DT4"; +	case ZEBRA_SEG6_LOCAL_ACTION_END_B6: +		return "End.B6"; +	case ZEBRA_SEG6_LOCAL_ACTION_END_B6_ENCAP: +		return "End.B6.Encap"; +	case ZEBRA_SEG6_LOCAL_ACTION_END_BM: +		return "End.BM"; +	case ZEBRA_SEG6_LOCAL_ACTION_END_S: +		return "End.S"; +	case ZEBRA_SEG6_LOCAL_ACTION_END_AS: +		return "End.AS"; +	case ZEBRA_SEG6_LOCAL_ACTION_END_AM: +		return "End.AM"; +	case ZEBRA_SEG6_LOCAL_ACTION_UNSPEC: +		return "unspec"; +	default: +		return "unknown"; +	} +} + +int snprintf_seg6_segs(char *str, +		size_t size, const struct seg6_segs *segs) +{ +	str[0] = '\0'; +	for (size_t i = 0; i < segs->num_segs; i++) { +		char addr[INET6_ADDRSTRLEN]; +		bool not_last = (i + 1) < segs->num_segs; + +		inet_ntop(AF_INET6, &segs->segs[i], addr, sizeof(addr)); +		strlcat(str, addr, size); +		strlcat(str, not_last ? "," : "", size); +	} +	return strlen(str); +} + +const char *seg6local_context2str(char *str, size_t size, +		struct seg6local_context *ctx, uint32_t action) +{ +	char b0[128]; + +	switch (action) { + +	case ZEBRA_SEG6_LOCAL_ACTION_END: +		snprintf(str, size, "USP"); +		return str; + +	case ZEBRA_SEG6_LOCAL_ACTION_END_X: +	case ZEBRA_SEG6_LOCAL_ACTION_END_DX6: +		inet_ntop(AF_INET6, &ctx->nh6, b0, 128); +		snprintf(str, size, "nh6 %s", b0); +		return str; + +	case ZEBRA_SEG6_LOCAL_ACTION_END_DX4: +		inet_ntop(AF_INET, &ctx->nh4, b0, 128); +		snprintf(str, size, "nh4 %s", b0); +		return str; + +	case ZEBRA_SEG6_LOCAL_ACTION_END_T: +	case ZEBRA_SEG6_LOCAL_ACTION_END_DT6: +	case ZEBRA_SEG6_LOCAL_ACTION_END_DT4: +		snprintf(str, size, "table %u", ctx->table); +		return str; + +	case ZEBRA_SEG6_LOCAL_ACTION_END_DX2: +	case ZEBRA_SEG6_LOCAL_ACTION_END_B6: +	case ZEBRA_SEG6_LOCAL_ACTION_END_B6_ENCAP: +	case ZEBRA_SEG6_LOCAL_ACTION_END_BM: +	case ZEBRA_SEG6_LOCAL_ACTION_END_S: +	case ZEBRA_SEG6_LOCAL_ACTION_END_AS: +	case ZEBRA_SEG6_LOCAL_ACTION_END_AM: +	case ZEBRA_SEG6_LOCAL_ACTION_UNSPEC: +	default: +		snprintf(str, size, "unknown(%s)", __func__); +		return str; +	} +} diff --git a/lib/srv6.h b/lib/srv6.h new file mode 100644 index 0000000000..24c7ffc3a2 --- /dev/null +++ b/lib/srv6.h @@ -0,0 +1,133 @@ +/* + * SRv6 definitions + * Copyright (C) 2020  Hiroki Shirokura, LINE Corporation + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License as published by the Free + * Software Foundation; either version 2 of the License, or (at your option) + * any later version. + * + * This program is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License for + * more details. + * + * You should have received a copy of the GNU General Public License along + * with this program; see the file COPYING; if not, write to the Free Software + * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA + */ + +#ifndef _FRR_SRV6_H +#define _FRR_SRV6_H + +#include <zebra.h> +#include <arpa/inet.h> +#include <netinet/in.h> + +#define SRV6_MAX_SIDS 16 + +#ifdef __cplusplus +extern "C" { +#endif + +#define sid2str(sid, str, size) \ +	inet_ntop(AF_INET6, sid, str, size) + +enum seg6_mode_t { +	INLINE, +	ENCAP, +	L2ENCAP, +}; + +enum seg6local_action_t { +	ZEBRA_SEG6_LOCAL_ACTION_UNSPEC       = 0, +	ZEBRA_SEG6_LOCAL_ACTION_END          = 1, +	ZEBRA_SEG6_LOCAL_ACTION_END_X        = 2, +	ZEBRA_SEG6_LOCAL_ACTION_END_T        = 3, +	ZEBRA_SEG6_LOCAL_ACTION_END_DX2      = 4, +	ZEBRA_SEG6_LOCAL_ACTION_END_DX6      = 5, +	ZEBRA_SEG6_LOCAL_ACTION_END_DX4      = 6, +	ZEBRA_SEG6_LOCAL_ACTION_END_DT6      = 7, +	ZEBRA_SEG6_LOCAL_ACTION_END_DT4      = 8, +	ZEBRA_SEG6_LOCAL_ACTION_END_B6       = 9, +	ZEBRA_SEG6_LOCAL_ACTION_END_B6_ENCAP = 10, +	ZEBRA_SEG6_LOCAL_ACTION_END_BM       = 11, +	ZEBRA_SEG6_LOCAL_ACTION_END_S        = 12, +	ZEBRA_SEG6_LOCAL_ACTION_END_AS       = 13, +	ZEBRA_SEG6_LOCAL_ACTION_END_AM       = 14, +	ZEBRA_SEG6_LOCAL_ACTION_END_BPF      = 15, +}; + +struct seg6_segs { +	size_t num_segs; +	struct in6_addr segs[256]; +}; + +struct seg6local_context { +	struct in_addr nh4; +	struct in6_addr nh6; +	uint32_t table; +}; + +static inline const char *seg6_mode2str(enum seg6_mode_t mode) +{ +	switch (mode) { +	case INLINE: +		return "INLINE"; +	case ENCAP: +		return "ENCAP"; +	case L2ENCAP: +		return "L2ENCAP"; +	default: +		return "unknown"; +	} +} + +static inline bool sid_same( +		const struct in6_addr *a, +		const struct in6_addr *b) +{ +	if (!a && !b) +		return true; +	else if (!(a && b)) +		return false; +	else +		return memcmp(a, b, sizeof(struct in6_addr)) == 0; +} + +static inline bool sid_diff( +		const struct in6_addr *a, +		const struct in6_addr *b) +{ +	return !sid_same(a, b); +} + +static inline bool sid_zero( +		const struct in6_addr *a) +{ +	struct in6_addr zero = {}; + +	return sid_same(a, &zero); +} + +static inline void *sid_copy(struct in6_addr *dst, +		const struct in6_addr *src) +{ +	return memcpy(dst, src, sizeof(struct in6_addr)); +} + +const char * +seg6local_action2str(uint32_t action); + +const char * +seg6local_context2str(char *str, size_t size, +		struct seg6local_context *ctx, uint32_t action); + +int snprintf_seg6_segs(char *str, +		size_t size, const struct seg6_segs *segs); + +#ifdef __cplusplus +} +#endif + +#endif diff --git a/lib/subdir.am b/lib/subdir.am index cb6fa7a3b8..d804d839db 100644 --- a/lib/subdir.am +++ b/lib/subdir.am @@ -51,6 +51,7 @@ lib_libfrr_la_SOURCES = \  	lib/mlag.c \  	lib/module.c \  	lib/mpls.c \ +	lib/srv6.c \  	lib/network.c \  	lib/nexthop.c \  	lib/netns_linux.c \ @@ -193,6 +194,7 @@ pkginclude_HEADERS += \  	lib/module.h \  	lib/monotime.h \  	lib/mpls.h \ +	lib/srv6.h \  	lib/network.h \  	lib/nexthop.h \  	lib/nexthop_group.h \  | 
