]> git.puffer.fish Git - mirror/frr.git/commitdiff
bgpd: support for bgp ipv6 ext community, and flowspec redirect ipv6
authorPhilippe Guibert <philippe.guibert@6wind.com>
Thu, 17 Oct 2019 14:08:16 +0000 (16:08 +0200)
committerPhilippe Guibert <philippe.guibert@6wind.com>
Fri, 21 Aug 2020 11:37:08 +0000 (13:37 +0200)
rfc 5701 is supported. it is possible to configure in bgp vpn, a list of
route target with ipv6 external communities to import. it is to be noted
that this ipv6 external community has been developed only for matching a
bgp flowspec update with same ipv6 ext commmunity.
adding to this, draft-ietf-idr-flow-spec-v6-09 is implemented regarding
the redirect ipv6 option.

Practically, under bgp vpn, under ipv6 unicast, it is possible to
configure : [no] rt6 redirect import <IPV6>:<AS> values.

An incoming bgp update with fs ipv6 and that option matching a bgp vrf,
will be imported in that bgp vrf.

Signed-off-by: Philippe Guibert <philippe.guibert@6wind.com>
bgpd/bgp_attr.c
bgpd/bgp_attr.h
bgpd/bgp_ecommunity.c
bgpd/bgp_ecommunity.h
bgpd/bgp_flowspec_vty.c
bgpd/bgp_mplsvpn.c
bgpd/bgp_pbr.c
bgpd/bgp_vty.c
bgpd/bgpd.h

index 2c7091f2e44009600dac7ddcccf92f4877b0bf4f..a77d9467cbbbdaad63d7aa1c296fb8f33204d68f 100644 (file)
@@ -84,6 +84,7 @@ static const struct message attr_str[] = {
 #endif
        {BGP_ATTR_LARGE_COMMUNITIES, "LARGE_COMMUNITY"},
        {BGP_ATTR_PREFIX_SID, "PREFIX_SID"},
+       {BGP_ATTR_IPV6_EXT_COMMUNITIES, "IPV6_EXT_COMMUNITIES"},
        {0}};
 
 static const struct message attr_flag_str[] = {
@@ -662,6 +663,8 @@ unsigned int attrhash_key_make(const void *p)
                MIX(lcommunity_hash_make(attr->lcommunity));
        if (attr->ecommunity)
                MIX(ecommunity_hash_make(attr->ecommunity));
+       if (attr->ipv6_ecommunity)
+               MIX(ecommunity_hash_make(attr->ipv6_ecommunity));
        if (attr->cluster)
                MIX(cluster_hash_key_make(attr->cluster));
        if (attr->transit)
@@ -700,6 +703,7 @@ bool attrhash_cmp(const void *p1, const void *p2)
                    && attr1->label_index == attr2->label_index
                    && attr1->mp_nexthop_len == attr2->mp_nexthop_len
                    && attr1->ecommunity == attr2->ecommunity
+                   && attr1->ipv6_ecommunity == attr2->ipv6_ecommunity
                    && attr1->lcommunity == attr2->lcommunity
                    && attr1->cluster == attr2->cluster
                    && attr1->transit == attr2->transit
@@ -829,6 +833,15 @@ struct attr *bgp_attr_intern(struct attr *attr)
                else
                        attr->ecommunity->refcnt++;
        }
+
+       if (attr->ipv6_ecommunity) {
+               if (!attr->ipv6_ecommunity->refcnt)
+                       attr->ipv6_ecommunity =
+                               ecommunity_intern(attr->ipv6_ecommunity);
+               else
+                       attr->ipv6_ecommunity->refcnt++;
+       }
+
        if (attr->lcommunity) {
                if (!attr->lcommunity->refcnt)
                        attr->lcommunity = lcommunity_intern(attr->lcommunity);
@@ -1034,6 +1047,10 @@ void bgp_attr_unintern_sub(struct attr *attr)
                ecommunity_unintern(&attr->ecommunity);
        UNSET_FLAG(attr->flag, ATTR_FLAG_BIT(BGP_ATTR_EXT_COMMUNITIES));
 
+       if (attr->ipv6_ecommunity)
+               ecommunity_unintern(&attr->ipv6_ecommunity);
+       UNSET_FLAG(attr->flag, ATTR_FLAG_BIT(BGP_ATTR_IPV6_EXT_COMMUNITIES));
+
        if (attr->lcommunity)
                lcommunity_unintern(&attr->lcommunity);
        UNSET_FLAG(attr->flag, ATTR_FLAG_BIT(BGP_ATTR_LARGE_COMMUNITIES));
@@ -1118,6 +1135,8 @@ void bgp_attr_flush(struct attr *attr)
                community_free(&attr->community);
        if (attr->ecommunity && !attr->ecommunity->refcnt)
                ecommunity_free(&attr->ecommunity);
+       if (attr->ipv6_ecommunity && !attr->ipv6_ecommunity->refcnt)
+               ecommunity_free(&attr->ipv6_ecommunity);
        if (attr->lcommunity && !attr->lcommunity->refcnt)
                lcommunity_free(&attr->lcommunity);
        if (attr->cluster && !attr->cluster->refcnt) {
@@ -1201,6 +1220,7 @@ bgp_attr_malformed(struct bgp_attr_parser_args *args, uint8_t subcode,
        case BGP_ATTR_LOCAL_PREF:
        case BGP_ATTR_COMMUNITIES:
        case BGP_ATTR_EXT_COMMUNITIES:
+       case BGP_ATTR_IPV6_EXT_COMMUNITIES:
        case BGP_ATTR_LARGE_COMMUNITIES:
        case BGP_ATTR_ORIGINATOR_ID:
        case BGP_ATTR_CLUSTER_LIST:
@@ -1286,6 +1306,8 @@ const uint8_t attr_flags_values[] = {
        [BGP_ATTR_LARGE_COMMUNITIES] =
                BGP_ATTR_FLAG_OPTIONAL | BGP_ATTR_FLAG_TRANS,
        [BGP_ATTR_PREFIX_SID] = BGP_ATTR_FLAG_OPTIONAL | BGP_ATTR_FLAG_TRANS,
+       [BGP_ATTR_IPV6_EXT_COMMUNITIES] =
+               BGP_ATTR_FLAG_OPTIONAL | BGP_ATTR_FLAG_TRANS,
 };
 static const size_t attr_flags_values_max = array_size(attr_flags_values) - 1;
 
@@ -2264,6 +2286,35 @@ bgp_attr_ext_communities(struct bgp_attr_parser_args *args)
        return BGP_ATTR_PARSE_PROCEED;
 }
 
+/* IPv6 Extended Community attribute. */
+static bgp_attr_parse_ret_t
+bgp_attr_ipv6_ext_communities(struct bgp_attr_parser_args *args)
+{
+       struct peer *const peer = args->peer;
+       struct attr *const attr = args->attr;
+       const bgp_size_t length = args->length;
+
+       if (length == 0) {
+               attr->ipv6_ecommunity = NULL;
+               /* Empty extcomm doesn't seem to be invalid per se */
+               return BGP_ATTR_PARSE_PROCEED;
+       }
+
+       attr->ipv6_ecommunity =
+               ecommunity_parse_ipv6((uint8_t *)stream_pnt(peer->curr),
+                                     length);
+       /* XXX: fix ecommunity_parse to use stream API */
+       stream_forward_getp(peer->curr, length);
+
+       if (!attr->ipv6_ecommunity)
+               return bgp_attr_malformed(args, BGP_NOTIFY_UPDATE_OPT_ATTR_ERR,
+                                         args->total);
+
+       attr->flag |= ATTR_FLAG_BIT(BGP_ATTR_IPV6_EXT_COMMUNITIES);
+
+       return BGP_ATTR_PARSE_PROCEED;
+}
+
 /* Parse Tunnel Encap attribute in an UPDATE */
 static int bgp_attr_encap(uint8_t type, struct peer *peer, /* IN */
                          bgp_size_t length, /* IN: attr's length field */
@@ -3082,6 +3133,9 @@ bgp_attr_parse_ret_t bgp_attr_parse(struct peer *peer, struct attr *attr,
                case BGP_ATTR_PMSI_TUNNEL:
                        ret = bgp_attr_pmsi_tunnel(&attr_args);
                        break;
+               case BGP_ATTR_IPV6_EXT_COMMUNITIES:
+                       ret = bgp_attr_ipv6_ext_communities(&attr_args);
+                       break;
                default:
                        ret = bgp_attr_unknown(&attr_args);
                        break;
index 1b2c75fbef64d99cd2882076caea47da4d018bc4..c57cf8100773c62acb41008e3b1a6fc64b397ff3 100644 (file)
@@ -177,6 +177,9 @@ struct attr {
        /* Extended Communities attribute. */
        struct ecommunity *ecommunity;
 
+       /* Extended Communities attribute. */
+       struct ecommunity *ipv6_ecommunity;
+
        /* Large Communities attribute. */
        struct lcommunity *lcommunity;
 
index c7fabb396a3e3b3db9eba40778e4ceb2f19b4a9e..9a987ad1e7a65e0fb577ce277f92dbb079d042c7 100644 (file)
@@ -48,7 +48,12 @@ static struct hash *ecomhash;
 /* Allocate a new ecommunities.  */
 struct ecommunity *ecommunity_new(void)
 {
-       return XCALLOC(MTYPE_ECOMMUNITY, sizeof(struct ecommunity));
+       struct ecommunity *ecom;
+
+       ecom = (struct ecommunity *)XCALLOC(MTYPE_ECOMMUNITY,
+                                           sizeof(struct ecommunity));
+       ecom->unit_size = ECOMMUNITY_SIZE;
+       return ecom;
 }
 
 void ecommunity_strfree(char **s)
@@ -83,36 +88,51 @@ static void ecommunity_hash_free(struct ecommunity *ecom)
    once and whether the new value should replace what is existing or
    not.
 */
-bool ecommunity_add_val(struct ecommunity *ecom, struct ecommunity_val *eval,
-                       bool unique, bool overwrite)
+static bool ecommunity_add_val_internal(struct ecommunity *ecom, const void *eval,
+                                       bool unique, bool overwrite, uint8_t ecom_size)
 {
        int c, ins_idx;
+       const struct ecommunity_val *eval4 = (struct ecommunity_val *)eval;
+       const struct ecommunity_val_ipv6 *eval6 = (struct ecommunity_val_ipv6 *)eval;
 
        /* When this is fist value, just add it. */
        if (ecom->val == NULL) {
                ecom->size = 1;
-               ecom->val = XCALLOC(MTYPE_ECOMMUNITY_VAL, ECOMMUNITY_SIZE);
-               memcpy(ecom->val, eval->val, ECOMMUNITY_SIZE);
+               ecom->val = XMALLOC(MTYPE_ECOMMUNITY_VAL,
+                                   ecom_length_size(ecom, ecom_size));
+               memcpy(ecom->val, eval, ecom_size);
                return true;
        }
 
        /* If the value already exists in the structure return 0.  */
        /* check also if the extended community itself exists. */
        c = 0;
+
        ins_idx = -1;
        for (uint8_t *p = ecom->val; c < ecom->size;
-            p += ECOMMUNITY_SIZE, c++) {
+            p += ecom_size, c++) {
                if (unique) {
-                       if (p[0] == eval->val[0] &&
-                           p[1] == eval->val[1]) {
-                               if (overwrite) {
-                                       memcpy(p, eval->val, ECOMMUNITY_SIZE);
-                                       return true;
+                       if (ecom_size == ECOMMUNITY_SIZE) {
+                               if (p[0] == eval4->val[0] &&
+                                   p[1] == eval4->val[1]) {
+                                       if (overwrite) {
+                                               memcpy(p, eval4->val, ecom_size);
+                                               return true;
+                                       }
+                                       return false;
+                               }
+                       } else {
+                               if (p[0] == eval6->val[0] &&
+                                   p[1] == eval6->val[1]) {
+                                       if (overwrite) {
+                                               memcpy(p, eval6->val, ecom_size);
+                                               return true;
+                                       }
+                                       return false;
                                }
-                               return false;
                        }
                }
-               int ret = memcmp(p, eval->val, ECOMMUNITY_SIZE);
+               int ret = memcmp(p, eval, ecom_size);
                if (ret == 0)
                        return false;
                if (ret > 0) {
@@ -129,61 +149,104 @@ bool ecommunity_add_val(struct ecommunity *ecom, struct ecommunity_val *eval,
        /* Add the value to the structure with numerical sorting.  */
        ecom->size++;
        ecom->val = XREALLOC(MTYPE_ECOMMUNITY_VAL, ecom->val,
-                            ecom->size * ECOMMUNITY_SIZE);
+                        ecom_length_size(ecom, ecom_size));
+
 
-       memmove(ecom->val + ((ins_idx + 1) * ECOMMUNITY_SIZE),
-               ecom->val + (ins_idx * ECOMMUNITY_SIZE),
-               (ecom->size - 1 - ins_idx) * ECOMMUNITY_SIZE);
-       memcpy(ecom->val + (ins_idx * ECOMMUNITY_SIZE),
-              eval->val, ECOMMUNITY_SIZE);
+       memmove(ecom->val + ((ins_idx + 1) * ecom_size),
+               ecom->val + (ins_idx * ecom_size),
+               (ecom->size - 1 - ins_idx) * ecom_size);
+       memcpy(ecom->val + (ins_idx * ecom_size),
+              eval, ecom_size);
 
        return true;
 }
 
-/* This function takes pointer to Extended Communites strucutre then
-   create a new Extended Communities structure by uniq and sort each
-   Extended Communities value.  */
-struct ecommunity *ecommunity_uniq_sort(struct ecommunity *ecom)
+/* Add a new Extended Communities value to Extended Communities
+   Attribute structure.  When the value is already exists in the
+   structure, we don't add the value.  Newly added value is sorted by
+   numerical order.  When the value is added to the structure return 1
+   else return 0.  */
+bool ecommunity_add_val(struct ecommunity *ecom, struct ecommunity_val *eval,
+                      bool unique, bool overwrite)
+{
+       return ecommunity_add_val_internal(ecom, (const void *)eval, unique,
+                                          overwrite, ECOMMUNITY_SIZE);
+}
+
+bool ecommunity_add_val_ipv6(struct ecommunity *ecom, struct ecommunity_val_ipv6 *eval,
+                           bool unique, bool overwrite)
+{
+       return ecommunity_add_val_internal(ecom, (const void *)eval, unique,
+                                          overwrite, IPV6_ECOMMUNITY_SIZE);
+}
+
+static struct ecommunity *
+ecommunity_uniq_sort_internal(struct ecommunity *ecom,
+                             unsigned short ecom_size)
 {
        int i;
        struct ecommunity *new;
-       struct ecommunity_val *eval;
+       const void *eval;
 
        if (!ecom)
                return NULL;
 
        new = ecommunity_new();
+       new->unit_size = ecom_size;
 
        for (i = 0; i < ecom->size; i++) {
-               eval = (struct ecommunity_val *)(ecom->val
-                                                + (i * ECOMMUNITY_SIZE));
-               ecommunity_add_val(new, eval, false, false);
+               eval = (void *)(ecom->val
+                                                + (i * ecom_size));
+               ecommunity_add_val_internal(new, eval, false, false, ecom_size);
        }
        return new;
 }
 
+/* This function takes pointer to Extended Communites strucutre then
+   create a new Extended Communities structure by uniq and sort each
+   Extended Communities value.  */
+struct ecommunity *ecommunity_uniq_sort(struct ecommunity *ecom)
+{
+       return ecommunity_uniq_sort_internal(ecom, ECOMMUNITY_SIZE);
+}
+
 /* Parse Extended Communites Attribute in BGP packet.  */
-struct ecommunity *ecommunity_parse(uint8_t *pnt, unsigned short length)
+static struct ecommunity *ecommunity_parse_internal(uint8_t *pnt,
+                                       unsigned short length,
+                                       unsigned short size_ecom)
 {
        struct ecommunity tmp;
        struct ecommunity *new;
 
        /* Length check.  */
-       if (length % ECOMMUNITY_SIZE)
+       if (length % size_ecom)
                return NULL;
 
        /* Prepare tmporary structure for making a new Extended Communities
           Attribute.  */
-       tmp.size = length / ECOMMUNITY_SIZE;
+       tmp.size = length / size_ecom;
        tmp.val = pnt;
 
        /* Create a new Extended Communities Attribute by uniq and sort each
           Extended Communities value  */
-       new = ecommunity_uniq_sort(&tmp);
+       new = ecommunity_uniq_sort_internal(&tmp, size_ecom);
 
        return ecommunity_intern(new);
 }
 
+struct ecommunity *ecommunity_parse(uint8_t *pnt,
+                                   unsigned short length)
+{
+       return ecommunity_parse_internal(pnt, length, ECOMMUNITY_SIZE);
+}
+
+struct ecommunity *ecommunity_parse_ipv6(uint8_t *pnt,
+                                        unsigned short length)
+{
+       return ecommunity_parse_internal(pnt, length,
+                                        IPV6_ECOMMUNITY_SIZE);
+}
+
 /* Duplicate the Extended Communities Attribute structure.  */
 struct ecommunity *ecommunity_dup(struct ecommunity *ecom)
 {
@@ -191,10 +254,11 @@ struct ecommunity *ecommunity_dup(struct ecommunity *ecom)
 
        new = XCALLOC(MTYPE_ECOMMUNITY, sizeof(struct ecommunity));
        new->size = ecom->size;
+       new->unit_size = ecom->unit_size;
        if (new->size) {
                new->val = XMALLOC(MTYPE_ECOMMUNITY_VAL,
-                                  ecom->size * ECOMMUNITY_SIZE);
-               memcpy(new->val, ecom->val, ecom->size * ECOMMUNITY_SIZE);
+                                  ecom->size * ecom->unit_size);
+               memcpy(new->val, ecom->val, ecom->size * ecom->unit_size);
        } else
                new->val = NULL;
        return new;
@@ -216,14 +280,16 @@ struct ecommunity *ecommunity_merge(struct ecommunity *ecom1,
        if (ecom1->val)
                ecom1->val =
                        XREALLOC(MTYPE_ECOMMUNITY_VAL, ecom1->val,
-                                (ecom1->size + ecom2->size) * ECOMMUNITY_SIZE);
+                                (ecom1->size + ecom2->size) *
+                                ecom1->unit_size);
        else
                ecom1->val =
                        XMALLOC(MTYPE_ECOMMUNITY_VAL,
-                               (ecom1->size + ecom2->size) * ECOMMUNITY_SIZE);
+                               (ecom1->size + ecom2->size) *
+                               ecom1->unit_size);
 
-       memcpy(ecom1->val + (ecom1->size * ECOMMUNITY_SIZE), ecom2->val,
-              ecom2->size * ECOMMUNITY_SIZE);
+       memcpy(ecom1->val + (ecom1->size * ecom1->unit_size), ecom2->val,
+              ecom2->size * ecom1->unit_size);
        ecom1->size += ecom2->size;
 
        return ecom1;
@@ -235,9 +301,7 @@ struct ecommunity *ecommunity_intern(struct ecommunity *ecom)
        struct ecommunity *find;
 
        assert(ecom->refcnt == 0);
-
        find = (struct ecommunity *)hash_get(ecomhash, ecom, hash_alloc_intern);
-
        if (find != ecom)
                ecommunity_free(&ecom);
 
@@ -272,7 +336,7 @@ void ecommunity_unintern(struct ecommunity **ecom)
 unsigned int ecommunity_hash_make(const void *arg)
 {
        const struct ecommunity *ecom = arg;
-       int size = ecom->size * ECOMMUNITY_SIZE;
+       int size = ecom->size * ecom->unit_size;
 
        return jhash(ecom->val, size, 0x564321ab);
 }
@@ -289,9 +353,12 @@ bool ecommunity_cmp(const void *arg1, const void *arg2)
        if (ecom1 == NULL || ecom2 == NULL)
                return false;
 
+       if (ecom1->unit_size != ecom2->unit_size)
+               return false;
+
        return (ecom1->size == ecom2->size
-               && memcmp(ecom1->val, ecom2->val, ecom1->size * ECOMMUNITY_SIZE)
-                          == 0);
+               && memcmp(ecom1->val, ecom2->val, ecom1->size *
+                         ecom1->unit_size) == 0);
 }
 
 /* Initialize Extended Comminities related hash. */
@@ -314,16 +381,21 @@ enum ecommunity_token {
        ecommunity_token_rt,
        ecommunity_token_soo,
        ecommunity_token_val,
+       ecommunity_token_rt6,
+       ecommunity_token_val6,
 };
 
-/*
- * Encode BGP extended community from passed values. Supports types
- * defined in RFC 4360 and well-known sub-types.
- */
-static int ecommunity_encode(uint8_t type, uint8_t sub_type, int trans, as_t as,
-                            struct in_addr ip, uint32_t val,
-                            struct ecommunity_val *eval)
+static int ecommunity_encode_internal(uint8_t type, uint8_t sub_type,
+                                     int trans, as_t as,
+                                     struct in_addr *ip,
+                                     struct in6_addr *ip6,
+                                     uint32_t val,
+                                     void *eval_ptr)
 {
+       struct ecommunity_val *eval = (struct ecommunity_val *)eval_ptr;
+       struct ecommunity_val_ipv6 *eval6 =
+               (struct ecommunity_val_ipv6 *)eval_ptr;
+
        assert(eval);
        if (type == ECOMMUNITY_ENCODE_AS) {
                if (as > BGP_AS_MAX)
@@ -332,6 +404,10 @@ static int ecommunity_encode(uint8_t type, uint8_t sub_type, int trans, as_t as,
                   || type == ECOMMUNITY_ENCODE_AS4) {
                if (val > UINT16_MAX)
                        return -1;
+       } else if (type == ECOMMUNITY_ENCODE_TRANS_EXP &&
+                  sub_type == ECOMMUNITY_FLOWSPEC_REDIRECT_IPV6 &&
+                  (!ip6 || val > UINT16_MAX)) {
+               return -1;
        }
 
        /* Fill in the values. */
@@ -347,9 +423,14 @@ static int ecommunity_encode(uint8_t type, uint8_t sub_type, int trans, as_t as,
                eval->val[6] = (val >> 8) & 0xff;
                eval->val[7] = val & 0xff;
        } else if (type == ECOMMUNITY_ENCODE_IP) {
-               memcpy(&eval->val[2], &ip, sizeof(struct in_addr));
+               memcpy(&eval->val[2], ip, sizeof(struct in_addr));
                eval->val[6] = (val >> 8) & 0xff;
                eval->val[7] = val & 0xff;
+       } else if (type == ECOMMUNITY_ENCODE_TRANS_EXP &&
+                  sub_type == ECOMMUNITY_FLOWSPEC_REDIRECT_IPV6) {
+               memcpy(&eval6->val[2], ip6, sizeof(struct in6_addr));
+               eval6->val[18] = (val >> 8) & 0xff;
+               eval6->val[19] = val & 0xff;
        } else {
                eval->val[2] = (as >> 24) & 0xff;
                eval->val[3] = (as >> 16) & 0xff;
@@ -362,9 +443,21 @@ static int ecommunity_encode(uint8_t type, uint8_t sub_type, int trans, as_t as,
        return 0;
 }
 
+/*
+ * Encode BGP extended community from passed values. Supports types
+ * defined in RFC 4360 and well-known sub-types.
+ */
+static int ecommunity_encode(uint8_t type, uint8_t sub_type, int trans, as_t as,
+                            struct in_addr ip, uint32_t val,
+                            struct ecommunity_val *eval)
+{
+       return ecommunity_encode_internal(type, sub_type, trans, as,
+                                         &ip, NULL, val, (void *)eval);
+}
+
 /* Get next Extended Communities token from the string. */
 static const char *ecommunity_gettoken(const char *str,
-                                      struct ecommunity_val *eval,
+                                      void *eval_ptr,
                                       enum ecommunity_token *token)
 {
        int ret;
@@ -374,11 +467,12 @@ static const char *ecommunity_gettoken(const char *str,
        const char *p = str;
        char *endptr;
        struct in_addr ip;
+       struct in6_addr ip6;
        as_t as = 0;
        uint32_t val = 0;
        uint8_t ecomm_type;
        char buf[INET_ADDRSTRLEN + 1];
-
+       struct ecommunity_val *eval = (struct ecommunity_val *)eval_ptr;
        /* Skip white space. */
        while (isspace((unsigned char)*p)) {
                p++;
@@ -396,7 +490,10 @@ static const char *ecommunity_gettoken(const char *str,
                        p++;
                        if (tolower((unsigned char)*p) == 't') {
                                p++;
-                               *token = ecommunity_token_rt;
+                               if (*p != '\0' && tolower((int)*p) == '6')
+                                       *token = ecommunity_token_rt6;
+                               else
+                                       *token = ecommunity_token_rt;
                                return p;
                        }
                        if (isspace((unsigned char)*p) || *p == '\0') {
@@ -435,6 +532,7 @@ static const char *ecommunity_gettoken(const char *str,
         * a) A.B.C.D:MN
         * b) EF:OPQR
         * c) GHJK:MN
+        * d) <IPV6>:MN (only with rt6)
         *
         * A.B.C.D: Four Byte IP
         * EF:      Two byte ASN
@@ -443,6 +541,34 @@ static const char *ecommunity_gettoken(const char *str,
         * OPQR:    Four byte value
         *
         */
+       /* IPv6 case : look for last ':' */
+       if (*token == ecommunity_token_rt6 ||
+           *token == ecommunity_token_val6) {
+               char *limit;
+
+               limit = endptr = strrchr(p, ':');
+               if (!endptr)
+                       goto error;
+               endptr++;
+               as = strtoul(endptr, &endptr, 10);
+               if (*endptr != '\0' || as == BGP_AS4_MAX)
+                       goto error;
+               memcpy(buf, p, (limit - p));
+               buf[limit - p] = '\0';
+               ret = inet_pton(AF_INET6, buf, &ip6);
+               if (ret == 0)
+                       goto error;
+               ecomm_type = ECOMMUNITY_ENCODE_TRANS_EXP;
+               if (ecommunity_encode_internal(ecomm_type,
+                                      ECOMMUNITY_FLOWSPEC_REDIRECT_IPV6,
+                                      1, 0, NULL, &ip6, as, eval_ptr))
+                       goto error;
+               *token = ecommunity_token_val6;
+               while (isdigit((int)*p) || *p == ':' || *p == '.') {
+                       p++;
+               }
+               return p;
+       }
        while (isdigit((unsigned char)*p) || *p == ':' || *p == '.') {
                if (*p == ':') {
                        if (separator)
@@ -508,41 +634,18 @@ error:
        return p;
 }
 
-/* Convert string to extended community attribute.
-
-   When type is already known, please specify both str and type.  str
-   should not include keyword such as "rt" and "soo".  Type is
-   ECOMMUNITY_ROUTE_TARGET or ECOMMUNITY_SITE_ORIGIN.
-   keyword_included should be zero.
-
-   For example route-map's "set extcommunity" command case:
-
-   "rt 100:1 100:2 100:3"        -> str = "100:1 100:2 100:3"
-                                   type = ECOMMUNITY_ROUTE_TARGET
-                                   keyword_included = 0
-
-   "soo 100:1"                   -> str = "100:1"
-                                   type = ECOMMUNITY_SITE_ORIGIN
-                                   keyword_included = 0
-
-   When string includes keyword for each extended community value.
-   Please specify keyword_included as non-zero value.
-
-   For example standard extcommunity-list case:
-
-   "rt 100:1 rt 100:2 soo 100:1" -> str = "rt 100:1 rt 100:2 soo 100:1"
-                                   type = 0
-                                   keyword_include = 1
-*/
-struct ecommunity *ecommunity_str2com(const char *str, int type,
-                                     int keyword_included)
+static struct ecommunity *ecommunity_str2com_internal(const char *str, int type,
+                                                     int keyword_included,
+                                                     bool is_ipv6_extcomm)
 {
        struct ecommunity *ecom = NULL;
        enum ecommunity_token token = ecommunity_token_unknown;
-       struct ecommunity_val eval;
+       struct ecommunity_val_ipv6 eval;
        int keyword = 0;
 
-       while ((str = ecommunity_gettoken(str, &eval, &token))) {
+       if (is_ipv6_extcomm)
+               token = ecommunity_token_rt6;
+       while ((str = ecommunity_gettoken(str, (void *)&eval, &token))) {
                switch (token) {
                case ecommunity_token_rt:
                case ecommunity_token_soo:
@@ -553,7 +656,8 @@ struct ecommunity *ecommunity_str2com(const char *str, int type,
                        }
                        keyword = 1;
 
-                       if (token == ecommunity_token_rt) {
+                       if (token == ecommunity_token_rt ||
+                           token == ecommunity_token_rt6) {
                                type = ECOMMUNITY_ROUTE_TARGET;
                        }
                        if (token == ecommunity_token_soo) {
@@ -572,7 +676,24 @@ struct ecommunity *ecommunity_str2com(const char *str, int type,
                        if (ecom == NULL)
                                ecom = ecommunity_new();
                        eval.val[1] = type;
-                       ecommunity_add_val(ecom, &eval, false, false);
+                       ecommunity_add_val_internal(ecom, (void *)&eval, false, false,
+                                                   ecom->unit_size);
+                       break;
+               case ecommunity_token_val6:
+                       if (keyword_included) {
+                               if (!keyword) {
+                                       if (ecom)
+                                               ecommunity_free(&ecom);
+                                       return NULL;
+                               }
+                               keyword = 0;
+                       }
+                       if (ecom == NULL)
+                               ecom = ecommunity_new();
+                       ecom->unit_size = IPV6_ECOMMUNITY_SIZE;
+                       eval.val[1] = type;
+                       ecommunity_add_val_internal(ecom, (void *)&eval, false, false,
+                                                   ecom->unit_size);
                        break;
                case ecommunity_token_unknown:
                default:
@@ -584,16 +705,59 @@ struct ecommunity *ecommunity_str2com(const char *str, int type,
        return ecom;
 }
 
-static int ecommunity_rt_soo_str(char *buf, size_t bufsz, const uint8_t *pnt,
-                                int type, int sub_type, int format)
+/* Convert string to extended community attribute.
+
+   When type is already known, please specify both str and type.  str
+   should not include keyword such as "rt" and "soo".  Type is
+   ECOMMUNITY_ROUTE_TARGET or ECOMMUNITY_SITE_ORIGIN.
+   keyword_included should be zero.
+
+   For example route-map's "set extcommunity" command case:
+
+   "rt 100:1 100:2 100:3"        -> str = "100:1 100:2 100:3"
+                                   type = ECOMMUNITY_ROUTE_TARGET
+                                   keyword_included = 0
+
+   "soo 100:1"                   -> str = "100:1"
+                                   type = ECOMMUNITY_SITE_ORIGIN
+                                   keyword_included = 0
+
+   When string includes keyword for each extended community value.
+   Please specify keyword_included as non-zero value.
+
+   For example standard extcommunity-list case:
+
+   "rt 100:1 rt 100:2 soo 100:1" -> str = "rt 100:1 rt 100:2 soo 100:1"
+                                   type = 0
+                                   keyword_include = 1
+*/
+struct ecommunity *ecommunity_str2com(const char *str, int type,
+                                     int keyword_included)
+{
+       return ecommunity_str2com_internal(str, type,
+                                          keyword_included, false);
+}
+
+struct ecommunity *ecommunity_str2com_ipv6(const char *str, int type,
+                                          int keyword_included)
+{
+       return ecommunity_str2com_internal(str, type,
+                                          keyword_included, true);
+}
+
+static int ecommunity_rt_soo_str_internal(char *buf, size_t bufsz,
+                                         const uint8_t *pnt, int type,
+                                         int sub_type, int format,
+                                         unsigned short ecom_size)
 {
        int len = 0;
        const char *prefix;
+       char buf_local[INET6_ADDRSTRLEN];
 
        /* For parse Extended Community attribute tupple. */
        struct ecommunity_as eas;
        struct ecommunity_ip eip;
-
+       struct ecommunity_ip6 eip6;
 
        /* Determine prefix for string, if any. */
        switch (format) {
@@ -619,11 +783,24 @@ static int ecommunity_rt_soo_str(char *buf, size_t bufsz, const uint8_t *pnt,
 
                len = snprintf(buf, bufsz, "%s%u:%u", prefix, eas.as, eas.val);
        } else if (type == ECOMMUNITY_ENCODE_AS) {
-               eas.as = (*pnt++ << 8);
-               eas.as |= (*pnt++);
-               pnt = ptr_get_be32(pnt, &eas.val);
+               if (ecom_size == ECOMMUNITY_SIZE) {
+                       eas.as = (*pnt++ << 8);
+                       eas.as |= (*pnt++);
+                       pnt = ptr_get_be32(pnt, &eas.val);
 
-               len = snprintf(buf, bufsz, "%s%u:%u", prefix, eas.as, eas.val);
+                       len = snprintf(buf, bufsz, "%s%u:%u", prefix, eas.as, eas.val);
+               } else {
+                       /* this is an IPv6 ext community
+                        */
+                       memcpy(&eip6.ip, pnt, 16);
+                       pnt += 16;
+                       eip6.val = (*pnt++ << 8);
+                       eip6.val |= (*pnt++);
+
+                       inet_ntop(AF_INET6, &eip6.ip, buf_local, sizeof(buf_local));
+                       len = snprintf(buf, bufsz, "%s%s:%u", prefix,
+                                      buf_local, eip6.val);
+               }
        } else if (type == ECOMMUNITY_ENCODE_IP) {
                memcpy(&eip.ip, pnt, 4);
                pnt += 4;
@@ -640,6 +817,14 @@ static int ecommunity_rt_soo_str(char *buf, size_t bufsz, const uint8_t *pnt,
        return len;
 }
 
+static int ecommunity_rt_soo_str(char *buf, size_t bufsz, const uint8_t *pnt,
+                                int type, int sub_type, int format)
+{
+       return ecommunity_rt_soo_str_internal(buf, bufsz, pnt, type,
+                                             sub_type, format,
+                                             ECOMMUNITY_SIZE);
+}
+
 static int ecommunity_lb_str(char *buf, size_t bufsz, const uint8_t *pnt)
 {
        int len = 0;
@@ -852,15 +1037,43 @@ char *ecommunity_ecom2str(struct ecommunity *ecom, int format, int filter)
                           type == ECOMMUNITY_EXTENDED_COMMUNITY_PART_2 ||
                           type == ECOMMUNITY_EXTENDED_COMMUNITY_PART_3) {
                        sub_type = *pnt++;
-                       if (sub_type == ECOMMUNITY_REDIRECT_VRF) {
-                               char buf[16] = {};
-                               ecommunity_rt_soo_str(
-                                       buf, sizeof(buf), pnt,
-                                       type & ~ECOMMUNITY_ENCODE_TRANS_EXP,
-                                       ECOMMUNITY_ROUTE_TARGET,
-                                       ECOMMUNITY_FORMAT_DISPLAY);
+
+                       if (sub_type == ECOMMUNITY_ROUTE_TARGET) {
+                               char buf[64];
+
+                               memset(buf, 0, sizeof(buf));
+                               ecommunity_rt_soo_str_internal(buf, sizeof(buf),
+                                               (const uint8_t *)pnt,
+                                               type &
+                                               ~ECOMMUNITY_ENCODE_TRANS_EXP,
+                                               ECOMMUNITY_ROUTE_TARGET,
+                                               format,
+                                               ecom->unit_size);
+                               snprintf(encbuf, sizeof(encbuf), "%s", buf);
+                       } else if (sub_type == ECOMMUNITY_FLOWSPEC_REDIRECT_IPV6) {
+                               char buf[64];
+
+                               memset(buf, 0, sizeof(buf));
+                               ecommunity_rt_soo_str_internal(buf, sizeof(buf),
+                                               (const uint8_t *)pnt,
+                                               type &
+                                               ~ECOMMUNITY_ENCODE_TRANS_EXP,
+                                               ECOMMUNITY_ROUTE_TARGET,
+                                               ECOMMUNITY_FORMAT_DISPLAY,
+                                               ecom->unit_size);
+                               snprintf(encbuf, sizeof(encbuf), "FS:redirect VRF %s", buf);
+                       } else if (sub_type == ECOMMUNITY_REDIRECT_VRF) {
+                               char buf[16];
+
+                               memset(buf, 0, sizeof(buf));
+                               ecommunity_rt_soo_str(buf, sizeof(buf), (const uint8_t *)pnt,
+                                                     type &
+                                                     ~ECOMMUNITY_ENCODE_TRANS_EXP,
+                                                     ECOMMUNITY_ROUTE_TARGET,
+                                                     ECOMMUNITY_FORMAT_DISPLAY);
                                snprintf(encbuf, sizeof(encbuf),
                                         "FS:redirect VRF %s", buf);
+                               snprintf(encbuf, sizeof(encbuf), "FS:redirect VRF %s", buf);
                        } else if (type != ECOMMUNITY_ENCODE_TRANS_EXP)
                                unk_ecom = 1;
                        else if (sub_type == ECOMMUNITY_TRAFFIC_ACTION) {
@@ -935,8 +1148,9 @@ bool ecommunity_match(const struct ecommunity *ecom1,
 
        /* Every community on com2 needs to be on com1 for this to match */
        while (i < ecom1->size && j < ecom2->size) {
-               if (memcmp(ecom1->val + i * ECOMMUNITY_SIZE,
-                          ecom2->val + j * ECOMMUNITY_SIZE, ECOMMUNITY_SIZE)
+               if (memcmp(ecom1->val + i * ecom1->unit_size,
+                          ecom2->val + j * ecom2->unit_size,
+                          ecom2->unit_size)
                    == 0)
                        j++;
                i++;
@@ -957,7 +1171,7 @@ extern struct ecommunity_val *ecommunity_lookup(const struct ecommunity *ecom,
 
        /* If the value already exists in the structure return 0.  */
        c = 0;
-       for (p = ecom->val; c < ecom->size; p += ECOMMUNITY_SIZE, c++) {
+       for (p = ecom->val; c < ecom->size; p += ecom->unit_size, c++) {
                if (p == NULL) {
                        continue;
                }
@@ -984,7 +1198,7 @@ bool ecommunity_strip(struct ecommunity *ecom, uint8_t type,
         * multiple times, handle that.
         */
        c = 0;
-       for (p = ecom->val; c < ecom->size; p += ECOMMUNITY_SIZE, c++) {
+       for (p = ecom->val; c < ecom->size; p += ecom->unit_size, c++) {
                if (p[0] == type && p[1] == subtype)
                        found++;
        }
@@ -1001,12 +1215,12 @@ bool ecommunity_strip(struct ecommunity *ecom, uint8_t type,
 
        /* Strip matching ext community(ies). */
        new = XMALLOC(MTYPE_ECOMMUNITY_VAL,
-                     (ecom->size - found) * ECOMMUNITY_SIZE);
+                     (ecom->size - found) * ecom->unit_size);
        q = new;
-       for (c = 0, p = ecom->val; c < ecom->size; c++, p += ECOMMUNITY_SIZE) {
+       for (c = 0, p = ecom->val; c < ecom->size; c++, p += ecom->unit_size) {
                if (!(p[0] == type && p[1] == subtype)) {
-                       memcpy(q, p, ECOMMUNITY_SIZE);
-                       q += ECOMMUNITY_SIZE;
+                       memcpy(q, p, ecom->unit_size);
+                       q += ecom->unit_size;
                }
        }
        XFREE(MTYPE_ECOMMUNITY_VAL, ecom->val);
@@ -1028,8 +1242,8 @@ bool ecommunity_del_val(struct ecommunity *ecom, struct ecommunity_val *eval)
        if (ecom == NULL || ecom->val == NULL)
                return false;
        c = 0;
-       for (p = ecom->val; c < ecom->size; p += ECOMMUNITY_SIZE, c++) {
-               if (!memcmp(p, eval->val, ECOMMUNITY_SIZE)) {
+       for (p = ecom->val; c < ecom->size; p += ecom->unit_size, c++) {
+               if (!memcmp(p, eval->val, ecom->unit_size)) {
                        found = 1;
                        break;
                }
@@ -1039,13 +1253,13 @@ bool ecommunity_del_val(struct ecommunity *ecom, struct ecommunity_val *eval)
 
        /* Delete the selected value */
        ecom->size--;
-       p = XMALLOC(MTYPE_ECOMMUNITY_VAL, ecom->size * ECOMMUNITY_SIZE);
+       p = XMALLOC(MTYPE_ECOMMUNITY_VAL, ecom->size * ecom->unit_size);
        if (c != 0)
-               memcpy(p, ecom->val, c * ECOMMUNITY_SIZE);
+               memcpy(p, ecom->val, c * ecom->unit_size);
        if ((ecom->size - c) != 0)
-               memcpy(p + (c)*ECOMMUNITY_SIZE,
-                      ecom->val + (c + 1) * ECOMMUNITY_SIZE,
-                      (ecom->size - c) * ECOMMUNITY_SIZE);
+               memcpy(p + (c)*ecom->unit_size,
+                      ecom->val + (c + 1) * ecom->unit_size,
+                      (ecom->size - c) * ecom->unit_size);
        XFREE(MTYPE_ECOMMUNITY_VAL, ecom->val);
        ecom->val = p;
        return true;
index 8ae3b44babb1002f05b3d3e38cb1b8bb340ecd28..4a1af0d203b943243ecfb27b9185419c58d8c23a 100644 (file)
  * 0x0c Flow-spec Redirect to IPv4 - draft-ietf-idr-flowspec-redirect
  */
 #define ECOMMUNITY_FLOWSPEC_REDIRECT_IPV4   0x0c
+/* from draft-ietf-idr-flow-spec-v6-09
+ * 0x0b Flow-spec Redirect to IPv6
+ */
+#define ECOMMUNITY_FLOWSPEC_REDIRECT_IPV6   0x0b
 
 /* Low-order octet of the Extended Communities type field for EVPN types */
 #define ECOMMUNITY_EVPN_SUBTYPE_MACMOBILITY  0x00
@@ -90,6 +94,7 @@
 
 /* Extended Communities value is eight octet long.  */
 #define ECOMMUNITY_SIZE                        8
+#define IPV6_ECOMMUNITY_SIZE                   20
 
 /* Extended Communities type flag.  */
 #define ECOMMUNITY_FLAG_NON_TRANSITIVE      0x40
@@ -99,6 +104,11 @@ struct ecommunity {
        /* Reference counter.  */
        unsigned long refcnt;
 
+       /* Size of Each Unit of Extended Communities attribute.
+        * to differentiate between IPv6 ext comm and ext comm
+        */
+       int unit_size;
+
        /* Size of Extended Communities attribute.  */
        int size;
 
@@ -119,11 +129,23 @@ struct ecommunity_ip {
        uint16_t val;
 };
 
+struct ecommunity_ip6 {
+       struct in6_addr ip;
+       uint16_t val;
+};
+
 /* Extended community value is eight octet.  */
 struct ecommunity_val {
        char val[ECOMMUNITY_SIZE];
 };
 
+/* IPv6 Extended community value is eight octet.  */
+struct ecommunity_val_ipv6 {
+       char val[IPV6_ECOMMUNITY_SIZE];
+};
+
+#define ecom_length_size(X, Y)    ((X)->size * (Y))
+
 /*
  * Encode BGP Route Target AS:nn.
  */
@@ -193,6 +215,8 @@ extern void ecommunity_init(void);
 extern void ecommunity_finish(void);
 extern void ecommunity_free(struct ecommunity **);
 extern struct ecommunity *ecommunity_parse(uint8_t *, unsigned short);
+extern struct ecommunity *ecommunity_parse_ipv6(uint8_t *pnt,
+                                               unsigned short length);
 extern struct ecommunity *ecommunity_dup(struct ecommunity *);
 extern struct ecommunity *ecommunity_merge(struct ecommunity *,
                                           struct ecommunity *);
@@ -202,6 +226,8 @@ extern bool ecommunity_cmp(const void *arg1, const void *arg2);
 extern void ecommunity_unintern(struct ecommunity **);
 extern unsigned int ecommunity_hash_make(const void *);
 extern struct ecommunity *ecommunity_str2com(const char *, int, int);
+extern struct ecommunity *ecommunity_str2com_ipv6(const char *str, int type,
+                                                 int keyword_included);
 extern char *ecommunity_ecom2str(struct ecommunity *, int, int);
 extern void ecommunity_strfree(char **s);
 extern bool ecommunity_match(const struct ecommunity *,
@@ -209,9 +235,13 @@ extern bool ecommunity_match(const struct ecommunity *,
 extern char *ecommunity_str(struct ecommunity *);
 extern struct ecommunity_val *ecommunity_lookup(const struct ecommunity *,
                                                uint8_t, uint8_t);
+
 extern bool ecommunity_add_val(struct ecommunity *ecom,
                               struct ecommunity_val *eval,
                               bool unique, bool overwrite);
+extern bool ecommunity_add_val_ipv6(struct ecommunity *ecom,
+                                   struct ecommunity_val_ipv6 *eval,
+                                   bool unique, bool overwrite);
 
 /* for vpn */
 extern struct ecommunity *ecommunity_new(void);
index 3e3366bf8b2d8055b0db26f95bf51946511e0468..b4bec75f0a68f54c31b97a71973220d76444edef 100644 (file)
@@ -263,7 +263,7 @@ void route_vty_out_flowspec(struct vty *vty, const struct prefix *p,
 {
        struct attr *attr;
        char return_string[BGP_FLOWSPEC_STRING_DISPLAY_MAX];
-       char *s;
+       char *s = NULL, *s2 = NULL;
        json_object *json_nlri_path = NULL;
        json_object *json_ecom_path = NULL;
        json_object *json_time_path = NULL;
@@ -306,21 +306,31 @@ void route_vty_out_flowspec(struct vty *vty, const struct prefix *p,
        }
        if (!path)
                return;
-       if (path->attr->ecommunity) {
+       if (path->attr &&
+           (path->attr->ecommunity || path->attr->ipv6_ecommunity)) {
                /* Print attribute */
                attr = path->attr;
-               s = ecommunity_ecom2str(attr->ecommunity,
-                                       ECOMMUNITY_FORMAT_ROUTE_MAP, 0);
-               if (!s)
+               if (attr->ecommunity)
+                       s = ecommunity_ecom2str(attr->ecommunity,
+                                               ECOMMUNITY_FORMAT_ROUTE_MAP, 0);
+               if (attr->ipv6_ecommunity)
+                       s2 = ecommunity_ecom2str(attr->ipv6_ecommunity,
+                                               ECOMMUNITY_FORMAT_ROUTE_MAP, 0);
+               if (!s && !s2)
                        return;
                if (display == NLRI_STRING_FORMAT_LARGE)
-                       vty_out(vty, "\t%s\n", s);
+                       vty_out(vty, "\t%s%s%s\n", s ? s : "",
+                               s2 && s ? " " : "", s2 ? s2 : "");
                else if (display == NLRI_STRING_FORMAT_MIN)
-                       vty_out(vty, "%s", s);
+                       vty_out(vty, "%s%s", s ? s : "", s2 ? s2 : "");
                else if (json_paths) {
                        json_ecom_path = json_object_new_object();
-                       json_object_string_add(json_ecom_path,
-                                      "ecomlist", s);
+                       if (s)
+                               json_object_string_add(json_ecom_path,
+                                                      "ecomlist", s);
+                       if (s2)
+                               json_object_string_add(json_ecom_path,
+                                                      "ecom6list", s2);
                        if (display == NLRI_STRING_FORMAT_JSON)
                                json_object_array_add(json_paths,
                                                      json_ecom_path);
index 314337d19455e87a2194bcdf56373c92b8d1928a..86fc4bc0a3b602743ae69c795b7e7dcb070d0d64 100644 (file)
@@ -391,12 +391,11 @@ static bool ecom_intersect(struct ecommunity *e1, struct ecommunity *e2)
 
        if (!e1 || !e2)
                return false;
-
        for (i = 0; i < e1->size; ++i) {
                for (j = 0; j < e2->size; ++j) {
-                       if (!memcmp(e1->val + (i * ECOMMUNITY_SIZE),
-                                   e2->val + (j * ECOMMUNITY_SIZE),
-                                   ECOMMUNITY_SIZE)) {
+                       if (!memcmp(e1->val + (i * e1->unit_size),
+                                   e2->val + (j * e2->unit_size),
+                                   e1->unit_size)) {
 
                                return true;
                        }
@@ -2456,6 +2455,10 @@ vrf_id_t get_first_vrf_for_redirect_with_rt(struct ecommunity *eckey)
 {
        struct listnode *mnode, *mnnode;
        struct bgp *bgp;
+       afi_t afi = AFI_IP;
+
+       if (eckey->unit_size == IPV6_ECOMMUNITY_SIZE)
+               afi = AFI_IP6;
 
        for (ALL_LIST_ELEMENTS(bm->bgp, mnode, mnnode, bgp)) {
                struct ecommunity *ec;
@@ -2463,7 +2466,10 @@ vrf_id_t get_first_vrf_for_redirect_with_rt(struct ecommunity *eckey)
                if (bgp->inst_type != BGP_INSTANCE_TYPE_VRF)
                        continue;
 
-               ec = bgp->vpn_policy[AFI_IP].import_redirect_rtlist;
+               ec = bgp->vpn_policy[afi].import_redirect_rtlist;
+
+               if (ec && eckey->unit_size != ec->unit_size)
+                       continue;
 
                if (ecom_intersect(ec, eckey))
                        return bgp->vrf_id;
index 69c19fa7d8daa2b57510b9e0e0f59080a3123002..dd01ba3e35d70dd07fccda9aad94cc64ae3b5850 100644 (file)
@@ -42,6 +42,9 @@ DEFINE_MTYPE_STATIC(BGPD, PBR_RULE, "PBR rule")
 DEFINE_MTYPE_STATIC(BGPD, PBR, "BGP PBR Context")
 DEFINE_MTYPE_STATIC(BGPD, PBR_VALMASK, "BGP PBR Val Mask Value")
 
+/* chain strings too long to fit in one line */
+#define FSPEC_ACTION_EXCEED_LIMIT "flowspec actions exceeds limit"
+
 RB_GENERATE(bgp_pbr_interface_head, bgp_pbr_interface,
            id_entry, bgp_pbr_interface_compare);
 struct bgp_pbr_interface_head ifaces_by_name_ipv4 =
@@ -756,8 +759,10 @@ int bgp_pbr_build_and_validate_entry(const struct prefix *p,
                                if (BGP_DEBUG(pbr, PBR_ERROR))
                                        flog_err(
                                                EC_BGP_FLOWSPEC_PACKET,
-                                               "%s: flowspec actions exceeds limit (max %u)",
-                                               __func__, action_count);
+                                               "%s: %s (max %u)",
+                                               __func__,
+                                               FSPEC_ACTION_EXCEED_LIMIT,
+                                               action_count);
                                break;
                        }
                        api_action = &api->actions[action_count - 1];
@@ -887,6 +892,45 @@ int bgp_pbr_build_and_validate_entry(const struct prefix *p,
                        api->action_num++;
                }
        }
+       if (path && path->attr && path->attr->ipv6_ecommunity) {
+               struct ecommunity_val_ipv6 *ipv6_ecom_eval;
+
+               ecom = path->attr->ipv6_ecommunity;
+               for (i = 0; i < ecom->size; i++) {
+                       ipv6_ecom_eval = (struct ecommunity_val_ipv6 *)
+                               (ecom->val + (i * ecom->unit_size));
+                       action_count++;
+                       if (action_count > ACTIONS_MAX_NUM) {
+                               if (BGP_DEBUG(pbr, PBR_ERROR))
+                                       flog_err(
+                                               EC_BGP_FLOWSPEC_PACKET,
+                                               "%s: flowspec actions exceeds limit (max %u)",
+                                               __func__, action_count);
+                               break;
+                       }
+                       api_action = &api->actions[action_count - 1];
+                       if ((ipv6_ecom_eval->val[1] ==
+                            (char)ECOMMUNITY_FLOWSPEC_REDIRECT_IPV6) &&
+                           (ipv6_ecom_eval->val[0] ==
+                            (char)ECOMMUNITY_ENCODE_TRANS_EXP)) {
+                               struct ecommunity *eckey = ecommunity_new();
+                               struct ecommunity_val_ipv6 ecom_copy;
+
+                               eckey->unit_size = IPV6_ECOMMUNITY_SIZE;
+                               memcpy(&ecom_copy, ipv6_ecom_eval,
+                                      sizeof(struct ecommunity_val_ipv6));
+                               ecom_copy.val[1] = ECOMMUNITY_ROUTE_TARGET;
+                               ecommunity_add_val_ipv6(eckey, &ecom_copy,
+                                                       false, false);
+                               api_action->action = ACTION_REDIRECT;
+                               api_action->u.redirect_vrf =
+                                       get_first_vrf_for_redirect_with_rt(
+                                                               eckey);
+                               ecommunity_free(&eckey);
+                               api->action_num++;
+                       }
+               }
+       }
        /* if ECOMMUNITY_TRAFFIC_RATE = 0 as action
         * then reduce the API action list to that action
         */
@@ -2812,8 +2856,11 @@ static void bgp_pbr_handle_entry(struct bgp *bgp, struct bgp_path_info *path,
                        continue_loop = 0;
                        break;
                case ACTION_REDIRECT:
+                       if (api->afi == AFI_IP)
+                               nh.type = NEXTHOP_TYPE_IPV4;
+                       else
+                               nh.type = NEXTHOP_TYPE_IPV6;
                        nh.vrf_id = api->actions[i].u.redirect_vrf;
-                       nh.type = NEXTHOP_TYPE_IPV4;
                        bgp_pbr_policyroute_add_to_zebra(bgp, path, &bpf, &bpof,
                                                         &nh, &rate);
                        continue_loop = 0;
index af02e13340ae8c218255ff4033341178ab90ac94..191c1a561ccf7711a104921d869010ec5fa8ed7d 100644 (file)
@@ -7332,15 +7332,20 @@ DEFPY(
 }
 
 static int set_ecom_list(struct vty *vty, int argc, struct cmd_token **argv,
-                        struct ecommunity **list)
+                        struct ecommunity **list, int is_rt6)
 {
        struct ecommunity *ecom = NULL;
        struct ecommunity *ecomadd;
 
        for (; argc; --argc, ++argv) {
-
-               ecomadd = ecommunity_str2com(argv[0]->arg,
-                                            ECOMMUNITY_ROUTE_TARGET, 0);
+               if (is_rt6)
+                       ecomadd = ecommunity_str2com_ipv6(argv[0]->arg,
+                                               ECOMMUNITY_ROUTE_TARGET,
+                                               0);
+               else
+                       ecomadd = ecommunity_str2com(argv[0]->arg,
+                                               ECOMMUNITY_ROUTE_TARGET,
+                                               0);
                if (!ecomadd) {
                        vty_out(vty, "Malformed community-list value\n");
                        if (ecom)
@@ -7660,7 +7665,7 @@ DEFPY (af_rt_vpn_imexport,
                        vty_out(vty, "%% Missing RTLIST\n");
                        return CMD_WARNING_CONFIG_FAILED;
                }
-               ret = set_ecom_list(vty, argc - idx, argv + idx, &ecom);
+               ret = set_ecom_list(vty, argc - idx, argv + idx, &ecom, 0);
                if (ret != CMD_SUCCESS) {
                        return ret;
                }
@@ -8017,34 +8022,44 @@ DEFPY (bgp_imexport_vpn,
 
 DEFPY (af_routetarget_import,
        af_routetarget_import_cmd,
-       "[no] <rt|route-target> redirect import RTLIST...",
+       "[no] <rt|route-target|route-target6|rt6> redirect import RTLIST...",
        NO_STR
        "Specify route target list\n"
        "Specify route target list\n"
+       "Specify route target list\n"
+       "Specify route target list\n"
        "Flow-spec redirect type route target\n"
        "Import routes to this address-family\n"
-       "Space separated route target list (A.B.C.D:MN|EF:OPQR|GHJK:MN)\n")
+       "Space separated route target list (A.B.C.D:MN|EF:OPQR|GHJK:MN|IPV6:MN)\n")
 {
        VTY_DECLVAR_CONTEXT(bgp, bgp);
        int ret;
        struct ecommunity *ecom = NULL;
        afi_t afi;
-       int idx = 0;
+       int idx = 0, idx_unused = 0;
        int yes = 1;
+       int rt6 = 0;
 
        if (argv_find(argv, argc, "no", &idx))
                yes = 0;
 
+       if (argv_find(argv, argc, "rt6", &idx_unused) ||
+           argv_find(argv, argc, "route-target6", &idx_unused))
+               rt6 = 1;
+
        afi = vpn_policy_getafi(vty, bgp, false);
        if (afi == AFI_MAX)
                return CMD_WARNING_CONFIG_FAILED;
 
+       if (rt6 && afi != AFI_IP6)
+               return CMD_WARNING_CONFIG_FAILED;
+
        if (yes) {
                if (!argv_find(argv, argc, "RTLIST", &idx)) {
                        vty_out(vty, "%% Missing RTLIST\n");
                        return CMD_WARNING_CONFIG_FAILED;
                }
-               ret = set_ecom_list(vty, argc - idx, argv + idx, &ecom);
+               ret = set_ecom_list(vty, argc - idx, argv + idx, &ecom, rt6);
                if (ret != CMD_SUCCESS)
                        return ret;
        }
@@ -14577,7 +14592,13 @@ static void bgp_vpn_policy_config_write_afi(struct vty *vty, struct bgp *bgp,
                                        ECOMMUNITY_FORMAT_ROUTE_MAP,
                                        ECOMMUNITY_ROUTE_TARGET);
 
-               vty_out(vty, "%*srt redirect import %s\n", indent, "", b);
+               if (bgp->vpn_policy[afi].import_redirect_rtlist->unit_size
+                   != ECOMMUNITY_SIZE)
+                       vty_out(vty, "%*srt redirect import %s\n",
+                               indent, "", b);
+               else
+                       vty_out(vty, "%*srt redirect import %s\n",
+                               indent, "", b);
                XFREE(MTYPE_ECOMMUNITY_STR, b);
        }
 }
index e84b0e43b9f142967c599c2a442a8289867145e9..c5af6c2161676f687a920d7a528050a4b4187662 100644 (file)
@@ -1530,6 +1530,7 @@ struct bgp_nlri {
 #define BGP_ATTR_AS_PATHLIMIT                   21
 #define BGP_ATTR_PMSI_TUNNEL                    22
 #define BGP_ATTR_ENCAP                          23
+#define BGP_ATTR_IPV6_EXT_COMMUNITIES           25
 #define BGP_ATTR_LARGE_COMMUNITIES              32
 #define BGP_ATTR_PREFIX_SID                     40
 #ifdef ENABLE_BGP_VNC_ATTR