]> git.proxmox.com Git - mirror_frr.git/commitdiff
bgpd: prefix-sid srv6 l3vpn service tlv
authorHiroki Shirokura <slank.dev@gmail.com>
Thu, 9 Jan 2020 03:00:43 +0000 (12:00 +0900)
committerHiroki Shirokura <slank.dev@gmail.com>
Wed, 15 Jan 2020 09:20:35 +0000 (18:20 +0900)
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 <slank.dev@gmail.com>
bgpd/bgp_attr.c
bgpd/bgp_attr.h
bgpd/bgp_memory.c
bgpd/bgp_memory.h
bgpd/bgp_route.c
bgpd/bgp_route.h
bgpd/rfapi/rfapi_vty.c
lib/srv6.c [new file with mode: 0644]
lib/srv6.h [new file with mode: 0644]
lib/subdir.am

index 16de59b72caca0ee2320ff71164d22ba90bb58bb..53a096753eac32eb1a8bdf966bb7703efd745ba3 100644 (file)
@@ -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. */
index 8bd527c0ff169da64cd75b3139dd271fad8fac06..2e91f56df57aca47f8d47ba51e1219bd166e26b6 100644 (file)
 #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 */
 
index 3e4dfb11add16f35eaf5321cbe0e13fa6b5cf644..41c4108c0abd0b057f4859cbb96d1771e6086066 100644 (file)
@@ -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")
index 03715f5621104c46f881b3b09a629a856e7c083f..5428022551436a397ba3190689bd41aacb123dc2 100644 (file)
@@ -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 */
index 5f4486b8004252115eed66fa33771999d78f33ca..e35d160197dd2e6b6355e4ccd3736a9942d4967f 100644 (file)
@@ -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)
index b9f3f3f7620119988f3cc6eebb11673c287ba0c4..91f8ef40b2dcf6fd84d24de6d3b863f651ade841 100644 (file)
@@ -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 {
 
index 5ad822211bbb407618eb32165561c7a565cff7d1..77fcf909c87ad7ecf05ad2a3c4692cfff8f0a45a 100644 (file)
@@ -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 (file)
index 0000000..be340f1
--- /dev/null
@@ -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 (file)
index 0000000..24c7ffc
--- /dev/null
@@ -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
index cb6fa7a3b81c050098c8adb2d29e0deba7f1b265..d804d839db38f28bd8a5078f7bc0ae1d8b293b00 100644 (file)
@@ -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 \