From 63d02478b557011b8606668f1e3c2edbf263794d Mon Sep 17 00:00:00 2001 From: Hiroki Shirokura Date: Mon, 6 Jan 2020 09:52:39 +0900 Subject: [PATCH 1/3] include: add kernel header for seg6/seg6local The linux kernel is adding support for seg6/seg6local lwtunnel type to support SRv6 feature. This commit update our includes to reflect new types and structs we should be listening for from the kernel Signed-off-by: Hiroki Shirokura --- include/linux/seg6.h | 55 ++++++++++++++++++++++++ include/linux/seg6_genl.h | 33 +++++++++++++++ include/linux/seg6_hmac.h | 23 ++++++++++ include/linux/seg6_iptunnel.h | 41 ++++++++++++++++++ include/linux/seg6_local.h | 80 +++++++++++++++++++++++++++++++++++ include/subdir.am | 5 +++ 6 files changed, 237 insertions(+) create mode 100644 include/linux/seg6.h create mode 100644 include/linux/seg6_genl.h create mode 100644 include/linux/seg6_hmac.h create mode 100644 include/linux/seg6_iptunnel.h create mode 100644 include/linux/seg6_local.h diff --git a/include/linux/seg6.h b/include/linux/seg6.h new file mode 100644 index 000000000000..329163e4a08d --- /dev/null +++ b/include/linux/seg6.h @@ -0,0 +1,55 @@ +/* SPDX-License-Identifier: GPL-2.0+ WITH Linux-syscall-note */ +/* + * SR-IPv6 implementation + * + * Author: + * David Lebrun + * + * + * 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. + */ + +#ifndef _LINUX_SEG6_H +#define _LINUX_SEG6_H + +#include +#include /* For struct in6_addr. */ + +/* + * SRH + */ +struct ipv6_sr_hdr { + __u8 nexthdr; + __u8 hdrlen; + __u8 type; + __u8 segments_left; + __u8 first_segment; /* Represents the last_entry field of SRH */ + __u8 flags; + __u16 tag; + + struct in6_addr segments[0]; +}; + +#define SR6_FLAG1_PROTECTED (1 << 6) +#define SR6_FLAG1_OAM (1 << 5) +#define SR6_FLAG1_ALERT (1 << 4) +#define SR6_FLAG1_HMAC (1 << 3) + +#define SR6_TLV_INGRESS 1 +#define SR6_TLV_EGRESS 2 +#define SR6_TLV_OPAQUE 3 +#define SR6_TLV_PADDING 4 +#define SR6_TLV_HMAC 5 + +#define sr_has_hmac(srh) ((srh)->flags & SR6_FLAG1_HMAC) + +struct sr6_tlv { + __u8 type; + __u8 len; + __u8 data[0]; +}; + +#endif diff --git a/include/linux/seg6_genl.h b/include/linux/seg6_genl.h new file mode 100644 index 000000000000..0c230524e0a1 --- /dev/null +++ b/include/linux/seg6_genl.h @@ -0,0 +1,33 @@ +/* SPDX-License-Identifier: GPL-2.0 WITH Linux-syscall-note */ +#ifndef _LINUX_SEG6_GENL_H +#define _LINUX_SEG6_GENL_H + +#define SEG6_GENL_NAME "SEG6" +#define SEG6_GENL_VERSION 0x1 + +enum { + SEG6_ATTR_UNSPEC, + SEG6_ATTR_DST, + SEG6_ATTR_DSTLEN, + SEG6_ATTR_HMACKEYID, + SEG6_ATTR_SECRET, + SEG6_ATTR_SECRETLEN, + SEG6_ATTR_ALGID, + SEG6_ATTR_HMACINFO, + __SEG6_ATTR_MAX, +}; + +#define SEG6_ATTR_MAX (__SEG6_ATTR_MAX - 1) + +enum { + SEG6_CMD_UNSPEC, + SEG6_CMD_SETHMAC, + SEG6_CMD_DUMPHMAC, + SEG6_CMD_SET_TUNSRC, + SEG6_CMD_GET_TUNSRC, + __SEG6_CMD_MAX, +}; + +#define SEG6_CMD_MAX (__SEG6_CMD_MAX - 1) + +#endif diff --git a/include/linux/seg6_hmac.h b/include/linux/seg6_hmac.h new file mode 100644 index 000000000000..3fb3412e1eb2 --- /dev/null +++ b/include/linux/seg6_hmac.h @@ -0,0 +1,23 @@ +/* SPDX-License-Identifier: GPL-2.0 WITH Linux-syscall-note */ +#ifndef _LINUX_SEG6_HMAC_H +#define _LINUX_SEG6_HMAC_H + +#include +#include + +#define SEG6_HMAC_SECRET_LEN 64 +#define SEG6_HMAC_FIELD_LEN 32 + +struct sr6_tlv_hmac { + struct sr6_tlv tlvhdr; + __u16 reserved; + __be32 hmackeyid; + __u8 hmac[SEG6_HMAC_FIELD_LEN]; +}; + +enum { + SEG6_HMAC_ALGO_SHA1 = 1, + SEG6_HMAC_ALGO_SHA256 = 2, +}; + +#endif diff --git a/include/linux/seg6_iptunnel.h b/include/linux/seg6_iptunnel.h new file mode 100644 index 000000000000..3004e982c23d --- /dev/null +++ b/include/linux/seg6_iptunnel.h @@ -0,0 +1,41 @@ +/* SPDX-License-Identifier: GPL-2.0+ WITH Linux-syscall-note */ +/* + * SR-IPv6 implementation + * + * Author: + * David Lebrun + * + * + * 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. + */ + +#ifndef _LINUX_SEG6_IPTUNNEL_H +#define _LINUX_SEG6_IPTUNNEL_H + +#include /* For struct ipv6_sr_hdr. */ + +enum { + SEG6_IPTUNNEL_UNSPEC, + SEG6_IPTUNNEL_SRH, + __SEG6_IPTUNNEL_MAX, +}; +#define SEG6_IPTUNNEL_MAX (__SEG6_IPTUNNEL_MAX - 1) + +struct seg6_iptunnel_encap { + int mode; + struct ipv6_sr_hdr srh[0]; +}; + +#define SEG6_IPTUN_ENCAP_SIZE(x) ((sizeof(*x)) + (((x)->srh->hdrlen + 1) << 3)) + +enum { + SEG6_IPTUN_MODE_INLINE, + SEG6_IPTUN_MODE_ENCAP, + SEG6_IPTUN_MODE_L2ENCAP, +}; + + +#endif diff --git a/include/linux/seg6_local.h b/include/linux/seg6_local.h new file mode 100644 index 000000000000..5312de80bcfa --- /dev/null +++ b/include/linux/seg6_local.h @@ -0,0 +1,80 @@ +/* + * SR-IPv6 implementation + * + * Author: + * David Lebrun + * + * + * 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. + */ + +#ifndef _LINUX_SEG6_LOCAL_H +#define _LINUX_SEG6_LOCAL_H + +#include + +enum { + SEG6_LOCAL_UNSPEC, + SEG6_LOCAL_ACTION, + SEG6_LOCAL_SRH, + SEG6_LOCAL_TABLE, + SEG6_LOCAL_NH4, + SEG6_LOCAL_NH6, + SEG6_LOCAL_IIF, + SEG6_LOCAL_OIF, + SEG6_LOCAL_BPF, + __SEG6_LOCAL_MAX, +}; +#define SEG6_LOCAL_MAX (__SEG6_LOCAL_MAX - 1) + +enum { + SEG6_LOCAL_ACTION_UNSPEC = 0, + /* node segment */ + SEG6_LOCAL_ACTION_END = 1, + /* adjacency segment (IPv6 cross-connect) */ + SEG6_LOCAL_ACTION_END_X = 2, + /* lookup of next seg NH in table */ + SEG6_LOCAL_ACTION_END_T = 3, + /* decap and L2 cross-connect */ + SEG6_LOCAL_ACTION_END_DX2 = 4, + /* decap and IPv6 cross-connect */ + SEG6_LOCAL_ACTION_END_DX6 = 5, + /* decap and IPv4 cross-connect */ + SEG6_LOCAL_ACTION_END_DX4 = 6, + /* decap and lookup of DA in v6 table */ + SEG6_LOCAL_ACTION_END_DT6 = 7, + /* decap and lookup of DA in v4 table */ + SEG6_LOCAL_ACTION_END_DT4 = 8, + /* binding segment with insertion */ + SEG6_LOCAL_ACTION_END_B6 = 9, + /* binding segment with encapsulation */ + SEG6_LOCAL_ACTION_END_B6_ENCAP = 10, + /* binding segment with MPLS encap */ + SEG6_LOCAL_ACTION_END_BM = 11, + /* lookup last seg in table */ + SEG6_LOCAL_ACTION_END_S = 12, + /* forward to SR-unaware VNF with static proxy */ + SEG6_LOCAL_ACTION_END_AS = 13, + /* forward to SR-unaware VNF with masquerading */ + SEG6_LOCAL_ACTION_END_AM = 14, + /* custom BPF action */ + SEG6_LOCAL_ACTION_END_BPF = 15, + + __SEG6_LOCAL_ACTION_MAX, +}; + +#define SEG6_LOCAL_ACTION_MAX (__SEG6_LOCAL_ACTION_MAX - 1) + +enum { + SEG6_LOCAL_BPF_PROG_UNSPEC, + SEG6_LOCAL_BPF_PROG, + SEG6_LOCAL_BPF_PROG_NAME, + __SEG6_LOCAL_BPF_PROG_MAX, +}; + +#define SEG6_LOCAL_BPF_PROG_MAX (__SEG6_LOCAL_BPF_PROG_MAX - 1) + +#endif diff --git a/include/subdir.am b/include/subdir.am index b1ca1be54fdb..86129c4d68a5 100644 --- a/include/subdir.am +++ b/include/subdir.am @@ -11,4 +11,9 @@ noinst_HEADERS += \ include/linux/socket.h \ include/linux/net_namespace.h \ include/linux/fib_rules.h \ + include/linux/seg6.h \ + include/linux/seg6_genl.h \ + include/linux/seg6_hmac.h \ + include/linux/seg6_iptunnel.h \ + include/linux/seg6_local.h \ # end From 770df5fd80f3b00ec552deb9755c13075b77b3ce Mon Sep 17 00:00:00 2001 From: Hiroki Shirokura Date: Mon, 6 Jan 2020 17:34:11 +0900 Subject: [PATCH 2/3] bgpd: extended nexthop capability for vpnv4-unicast This commit makes bgpd to support VPNv4's extended nexthop capability for bgp-capability negotiation when BGP open messaging. Signed-off-by: Hiroki Shirokura --- bgpd/bgp_open.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/bgpd/bgp_open.c b/bgpd/bgp_open.c index f17bc7b8c06e..948d7575061d 100644 --- a/bgpd/bgp_open.c +++ b/bgpd/bgp_open.c @@ -656,7 +656,7 @@ static int bgp_capability_enhe(struct peer *peer, struct capability_header *hdr) nh_afi = afi_iana2int(pkt_nh_afi); if (afi != AFI_IP || nh_afi != AFI_IP6 - || !(safi == SAFI_UNICAST + || !(safi == SAFI_UNICAST || safi == SAFI_MPLS_VPN || safi == SAFI_LABELED_UNICAST)) { flog_warn( EC_BGP_CAPABILITY_INVALID_DATA, @@ -1341,7 +1341,7 @@ void bgp_open_capability(struct stream *s, struct peer *peer) if (CHECK_FLAG(peer->flags, PEER_FLAG_CAPABILITY_ENHE) && peer->su.sa.sa_family == AF_INET6 && afi == AFI_IP - && (safi == SAFI_UNICAST + && (safi == SAFI_UNICAST || safi == SAFI_MPLS_VPN || safi == SAFI_LABELED_UNICAST)) { /* RFC 5549 Extended Next Hop Encoding */ From e496b4203055c50806dc7193b9762304261c4bbd Mon Sep 17 00:00:00 2001 From: Hiroki Shirokura Date: Thu, 9 Jan 2020 12:00:43 +0900 Subject: [PATCH 3/3] bgpd: prefix-sid srv6 l3vpn service tlv bgpd already supports BGP Prefix-SID path attribute and there are some sub-types of Prefix-SID path attribute. This commits makes bgpd to support additional sub-types. sub-Type-4 and sub-Type-5 for construct the VPNv4 SRv6 backend with vpnv4-unicast address family. This path attributes is already supported by Ciscos IOS-XR and NX-OS. Prefix-SID sub-Type-4 and sub-Type-5 is defined on following IETF-drafts. Supports(A-part-of): - https://tools.ietf.org/html/draft-dawra-idr-srv6-vpn-04 - https://tools.ietf.org/html/draft-dawra-idr-srv6-vpn-05 Signed-off-by: Hiroki Shirokura --- bgpd/bgp_attr.c | 318 +++++++++++++++++++++++++++++++++++++++-- bgpd/bgp_attr.h | 32 +++++ bgpd/bgp_memory.c | 3 + bgpd/bgp_memory.h | 3 + bgpd/bgp_route.c | 38 +++++ bgpd/bgp_route.h | 7 + bgpd/rfapi/rfapi_vty.c | 8 ++ lib/srv6.c | 116 +++++++++++++++ lib/srv6.h | 133 +++++++++++++++++ lib/subdir.am | 2 + 10 files changed, 647 insertions(+), 13 deletions(-) create mode 100644 lib/srv6.c create mode 100644 lib/srv6.h diff --git a/bgpd/bgp_attr.c b/bgpd/bgp_attr.c index 16de59b72cac..53a096753eac 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 8bd527c0ff16..2e91f56df57a 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 3e4dfb11add1..41c4108c0abd 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 03715f562110..542802255143 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 5f4486b80042..e35d160197dd 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 b9f3f3f76201..91f8ef40b2dc 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 5ad822211bbb..77fcf909c87a 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 000000000000..be340f13f557 --- /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 000000000000..24c7ffc3a2eb --- /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 +#include +#include + +#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 cb6fa7a3b81c..d804d839db38 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 \