While it has been possible to do wildcard matching on communities and
large communities for a long time, ext-communities did not offer this.
The result is more complex rulesets when cleaning ext-communities.
This diff allows the same use of '*' on ext-communities and also adds
support for local-as and neighbor-as on some forms of ext-communities.

Since the encoding of ext-communities is a bit strange not all options are
possible. Here a list of possibilities:

ext-community * * # delete any ext-community
ext-community ovs * # delete any ext-community of specified type
ext-community rt 1.2.3.4:*
ext-community rt local-as:*
ext-community rt 65001:*

ext-community rt 65001:11111
ext-community rt 65001:local-as
ext-community rt local-as:11111 # not this defaults to the 4-byte AS encoding

There is a bit of a gotcha since sometimes the type of the ext-community
is underspecified when using wildchars or expands.  So 'ext-community rt *'
or 'ext-community soo *' will match for any of the 3 possible types
(2-byte AS, 4-byte AS and IP address). If local-as/neighbor-as is used as
expand of as-number (e.g. ext-community rt local-as:1) then bgpd will
default to the 4-byte AS type to encode the community.

ext-community encoding is a bit of a desaster and so the wildcard matching
may be a bit more limited then we probably would like.

As usual comments, test reports and OKs welcome :)
-- 
:wq Claudio

PS: In general people are advised to use large-communities whenever proper
expanding of neighbor-as is required since that is the only format
supporting 4-byte values in all cases.


Index: bgpd.conf.5
===================================================================
RCS file: /cvs/src/usr.sbin/bgpd/bgpd.conf.5,v
retrieving revision 1.185
diff -u -p -r1.185 bgpd.conf.5
--- bgpd.conf.5 11 Feb 2019 17:45:59 -0000      1.185
+++ bgpd.conf.5 12 Feb 2019 15:13:56 -0000
@@ -1354,14 +1354,9 @@ and
 .Ar local
 may be set to
 .Sq *
-to do wildcard matching.
-Both
-.Ar as-number
-and
-.Ar local
-may be set to
+to do wildcard matching,
 .Ic neighbor-as ,
-which is expanded to the current neighbor remote AS number,
+which is expanded to the current neighbor remote AS number, or
 .Ic local-as ,
 which is expanded to the locally assigned AS number.
 .Pp
@@ -1379,7 +1374,7 @@ which is expanded to the locally assigne
 .Xc
 .It Xo
 .Ic ext-community
-.Ar ovs
+.Ic ovs
 .Pq Ic valid | not-found | invalid
 .Xc
 This rule applies only to
@@ -1391,6 +1386,26 @@ Extended Communities are specified by a
 .Ar subtype
 and normally two values, a globally unique part (e.g. the AS number) and a
 local part.
+Both
+.Ar as-number
+and
+.Ar local
+may be set to
+.Ic neighbor-as ,
+which is expanded to the current neighbor remote AS number, or
+.Ic local-as ,
+which is expanded to the locally assigned AS number.
+Wildcard matching is supported for
+.Ar local ,
+.Ar numvalue
+and
+.Ar subtype .
+If wildcard matching is used on the 
+.Ar subtype
+then
+.Ar numvalue
+also needs to be set to
+.Sq * .
 See also the
 .Sx ATTRIBUTE SET
 section for further information about the encoding.
@@ -1689,7 +1704,7 @@ to do wildcard matching.
 .Xc
 .It Xo
 .Ic ext-community Op Ar delete
-.Ar ovs
+.Ic ovs
 .Pq Ic valid | not-found | invalid
 .Xc
 Set or delete the
@@ -1736,6 +1751,22 @@ vrfri    VRF Route Import
 .Pp
 Not all type and subtype value pairs are allowed by IANA and the parser
 will ensure that no invalid combination is created.
+.Pp
+For
+.Cm delete ,
+.Ar subtype ,
+.Ar numvalue ,
+or
+.Ar local ,
+may be set to
+.Sq *
+to do wildcard matching.
+If wildcard matching is used on the 
+.Ar subtype
+then
+.Ar numvalue
+also needs to be set to
+.Sq * .
 .Pp
 .It Ic localpref Ar number
 Set the
Index: bgpd.h
===================================================================
RCS file: /cvs/src/usr.sbin/bgpd/bgpd.h,v
retrieving revision 1.365
diff -u -p -r1.365 bgpd.h
--- bgpd.h      11 Feb 2019 15:44:25 -0000      1.365
+++ bgpd.h      11 Feb 2019 17:15:03 -0000
@@ -772,7 +772,7 @@ struct filter_community {
                struct ext {
                        u_int32_t       data1;
                        u_int64_t       data2;
-                       u_int8_t        type;
+                       short           type;
                        u_int8_t        subtype;        /* if extended type */
                } e;
        }               c;
@@ -865,8 +865,10 @@ struct filter_peers {
 #define EXT_COMMUNITY_NON_TRANS_IPV4   0x41    /* IPv4 specific */
 #define EXT_COMMUNITY_NON_TRANS_FOUR_AS        0x42    /* 4 octet AS specific 
*/
 #define EXT_COMMUNITY_NON_TRANS_OPAQUE 0x43    /* opaque ext community */
+#define EXT_COMMUNITY_UNKNOWN          -1
 
 /* BGP Origin Validation State Extended Community RFC8097 */
+#define EXT_COMMUNITY_SUBTYPE_OVS      0
 #define EXT_COMMUNITY_OVS_VALID                0
 #define EXT_COMMUNITY_OVS_NOTFOUND     1
 #define EXT_COMMUNITY_OVS_INVALID      2
@@ -876,7 +878,7 @@ struct filter_peers {
 #define EXT_COMMUNITY_FLAG_VALID       0x01
 
 struct ext_comm_pairs {
-       u_int8_t        type;
+       short           type;
        u_int8_t        subtype;
        const char      *subname;
 };
@@ -905,7 +907,7 @@ struct ext_comm_pairs {
        { EXT_COMMUNITY_TRANS_OPAQUE, 0x06, "ort" },            \
        { EXT_COMMUNITY_TRANS_OPAQUE, 0x0d, "defgw" },          \
                                                                \
-       { EXT_COMMUNITY_NON_TRANS_OPAQUE, 0x00, "ovs" },        \
+       { EXT_COMMUNITY_NON_TRANS_OPAQUE, EXT_COMMUNITY_SUBTYPE_OVS, "ovs" }, \
                                                                \
        { EXT_COMMUNITY_TRANS_EVPN, 0x00, "mac-mob" },          \
        { EXT_COMMUNITY_TRANS_EVPN, 0x01, "esi-lab" },          \
@@ -1257,7 +1259,7 @@ const char        *log_in6addr(const struct in6
 const char     *log_sockaddr(struct sockaddr *);
 const char     *log_as(u_int32_t);
 const char     *log_rd(u_int64_t);
-const char     *log_ext_subtype(u_int8_t, u_int8_t);
+const char     *log_ext_subtype(short, u_int8_t);
 const char     *log_shutcomm(const char *);
 int             aspath_snprint(char *, size_t, void *, u_int16_t);
 int             aspath_asprint(char **, void *, u_int16_t);
Index: parse.y
===================================================================
RCS file: /cvs/src/usr.sbin/bgpd/parse.y,v
retrieving revision 1.371
diff -u -p -r1.371 parse.y
--- parse.y     12 Feb 2019 09:02:41 -0000      1.371
+++ parse.y     12 Feb 2019 10:41:06 -0000
@@ -1098,6 +1098,7 @@ l3vpnopts : RD STRING {
                        struct filter_community ext;
                        u_int64_t               rd;
 
+                       memset(&ext, 0, sizeof(ext));
                        if (parseextcommunity(&ext, "rt", $2) == -1) {
                                free($2);
                                YYERROR;
@@ -1107,7 +1108,7 @@ l3vpnopts : RD STRING {
                         * RD is almost encode like an ext-community,
                         * but only almost so convert here.
                         */
-                       if (community_ext_conv(&ext, 0, &rd)) {
+                       if (community_ext_conv(&ext, NULL, &rd, NULL)) {
                                yyerror("bad encoding of rd");
                                YYERROR;
                        }
@@ -3596,7 +3597,7 @@ parsesubtype(char *name, int *type, int 
 }
 
 static int
-parseextvalue(int type, char *s, u_int32_t *v)
+parseextvalue(int type, char *s, u_int32_t *v, u_int8_t *flag)
 {
        const char      *errstr;
        char            *p;
@@ -3605,6 +3606,14 @@ parseextvalue(int type, char *s, u_int32
 
        if (type != -1) {
                /* nothing */
+       } else if (strcmp(s, "neighbor-as") == 0) {
+               *flag = COMMUNITY_NEIGHBOR_AS;
+               *v = 0;
+               return EXT_COMMUNITY_TRANS_FOUR_AS;
+       } else if (strcmp(s, "local-as") == 0) {
+               *flag = COMMUNITY_LOCAL_AS;
+               *v = 0;
+               return EXT_COMMUNITY_TRANS_FOUR_AS;
        } else if ((p = strchr(s, '.')) == NULL) {
                /* AS_PLAIN number (4 or 2 byte) */
                strtonum(s, 0, USHRT_MAX, &errstr);
@@ -3670,12 +3679,16 @@ int
 parseextcommunity(struct filter_community *c, char *t, char *s)
 {
        const struct ext_comm_pairs *cp;
-       const char      *errstr;
        u_int64_t        ullval;
-       u_int32_t        uval;
+       u_int32_t        uval, uval2;
        char            *p, *ep;
        int              type, subtype;
 
+       if (strcmp(t, "*") == 0 && strcmp(s, "*") == 0) {
+               c->type = COMMUNITY_TYPE_EXT;
+               c->dflag3 = COMMUNITY_ANY;
+               return (0);
+       }
        if (parsesubtype(t, &type, &subtype) == 0) {
                yyerror("Bad ext-community unknown type");
                return (-1);
@@ -3686,33 +3699,39 @@ parseextcommunity(struct filter_communit
        case EXT_COMMUNITY_TRANS_FOUR_AS:
        case EXT_COMMUNITY_TRANS_IPV4:
        case -1:
+               if (strcmp(s, "*") == 0) {
+                       c->dflag1 = COMMUNITY_ANY;
+                       break;
+               }
                if ((p = strchr(s, ':')) == NULL) {
                        yyerror("Bad ext-community %s", s);
                        return (-1);
                }
                *p++ = '\0';
-               if ((type = parseextvalue(type, s, &uval)) == -1)
+               if ((type = parseextvalue(type, s, &uval, &c->dflag1)) == -1)
                        return (-1);
                switch (type) {
                case EXT_COMMUNITY_TRANS_TWO_AS:
-                       ullval = strtonum(p, 0, UINT_MAX, &errstr);
+                       if (getcommunity(p, 1, &uval2, &c->dflag2) == -1)
+                               return (-1);
                        break;
                case EXT_COMMUNITY_TRANS_IPV4:
                case EXT_COMMUNITY_TRANS_FOUR_AS:
-                       ullval = strtonum(p, 0, USHRT_MAX, &errstr);
+                       if (getcommunity(p, 0, &uval2, &c->dflag2) == -1)
+                               return (-1);
                        break;
                default:
                        fatalx("parseextcommunity: unexpected result");
                }
-               if (errstr) {
-                       yyerror("Bad ext-community %s is %s", p, errstr);
-                       return (-1);
-               }
                c->c.e.data1 = uval;
-               c->c.e.data2 = ullval;
+               c->c.e.data2 = uval2;
                break;
        case EXT_COMMUNITY_TRANS_OPAQUE:
        case EXT_COMMUNITY_TRANS_EVPN:
+               if (strcmp(s, "*") == 0) {
+                       c->dflag1 = COMMUNITY_ANY;
+                       break;
+               }
                errno = 0;
                ullval = strtoull(s, &ep, 0);
                if (s[0] == '\0' || *ep != '\0') {
@@ -3726,21 +3745,33 @@ parseextcommunity(struct filter_communit
                c->c.e.data2 = ullval;
                break;
        case EXT_COMMUNITY_NON_TRANS_OPAQUE:
-               if (strcmp(s, "valid") == 0)
-                       c->c.e.data2 = EXT_COMMUNITY_OVS_VALID;
-               else if (strcmp(s, "invalid") == 0)
-                       c->c.e.data2 = EXT_COMMUNITY_OVS_INVALID;
-               else if (strcmp(s, "not-found") == 0)
-                       c->c.e.data2 = EXT_COMMUNITY_OVS_NOTFOUND;
-               else {
-                       yyerror("Bad ext-community %s", s);
-                       return (-1);
+               if (subtype == EXT_COMMUNITY_SUBTYPE_OVS) {
+                       if (strcmp(s, "valid") == 0) {
+                               c->c.e.data2 = EXT_COMMUNITY_OVS_VALID;
+                               break;
+                       } else if (strcmp(s, "invalid") == 0) {
+                               c->c.e.data2 = EXT_COMMUNITY_OVS_INVALID;
+                               break;
+                       } else if (strcmp(s, "not-found") == 0) {
+                               c->c.e.data2 = EXT_COMMUNITY_OVS_NOTFOUND;
+                               break;
+                       } else if (strcmp(s, "*") == 0) {
+                               c->dflag1 = COMMUNITY_ANY;
+                               break;
+                       }
                }
-               break;
+               yyerror("Bad ext-community %s", s);
+               return (-1);
        }
        c->c.e.type = type;
        c->c.e.subtype = subtype;
 
+       /* special handling of ext-community rt * since type is not known */
+       if (c->dflag1 == COMMUNITY_ANY && c->c.e.type == -1) {
+               c->type = COMMUNITY_TYPE_EXT;
+               return (0);
+       }
+               
        /* verify type/subtype combo */
        for (cp = iana_ext_comms; cp->subname != NULL; cp++) {
                if (cp->type == type && cp->subtype == subtype) {
Index: printconf.c
===================================================================
RCS file: /cvs/src/usr.sbin/bgpd/printconf.c,v
retrieving revision 1.129
diff -u -p -r1.129 printconf.c
--- printconf.c 12 Feb 2019 09:00:56 -0000      1.129
+++ printconf.c 12 Feb 2019 09:04:34 -0000
@@ -200,34 +200,65 @@ print_community(struct filter_community 
                }
                break;
        case COMMUNITY_TYPE_EXT:
+               if (c->dflag3 == COMMUNITY_ANY) {
+                       printf("* * ");
+                       break;
+               }
                printf("%s ", log_ext_subtype(c->c.e.type, c->c.e.subtype));
+               if (c->dflag1 == COMMUNITY_ANY) {
+                       printf("* ");
+                       break;
+               }
+
                switch (c->c.e.type) {
                case EXT_COMMUNITY_TRANS_TWO_AS:
                case EXT_COMMUNITY_TRANS_FOUR_AS:
-                       printf("%s:%llu ", log_as(c->c.e.data1),
-                           c->c.e.data2);
+                       if (c->dflag1 == COMMUNITY_NEIGHBOR_AS)
+                               printf("neighbor-as:");
+                       else if (c->dflag1 == COMMUNITY_LOCAL_AS)
+                               printf("local-as:");
+                       else
+                               printf("%s:", log_as(c->c.e.data1));
                        break;
                case EXT_COMMUNITY_TRANS_IPV4:
                        addr.s_addr = htonl(c->c.e.data1);
-                       printf("%s:%llu ", inet_ntoa(addr),
-                           c->c.e.data2);
+                       printf("%s:", inet_ntoa(addr));
+                       break;
+               }
+
+               switch (c->c.e.type) {
+               case EXT_COMMUNITY_TRANS_TWO_AS:
+               case EXT_COMMUNITY_TRANS_FOUR_AS:
+               case EXT_COMMUNITY_TRANS_IPV4:
+                       if (c->dflag2 == COMMUNITY_ANY)
+                               printf("* ");
+                       else if (c->dflag2 == COMMUNITY_NEIGHBOR_AS)
+                               printf("neighbor-as ");
+                       else if (c->dflag2 == COMMUNITY_LOCAL_AS)
+                               printf("local-as ");
+                       else
+                               printf("%llu ", c->c.e.data2);
                        break;
                case EXT_COMMUNITY_TRANS_OPAQUE:
                case EXT_COMMUNITY_TRANS_EVPN:
                        printf("0x%llx ", c->c.e.data2);
                        break;
                case EXT_COMMUNITY_NON_TRANS_OPAQUE:
-                       switch (c->c.e.data2) {
-                       case EXT_COMMUNITY_OVS_VALID:
-                               printf("valid ");
-                               break;
-                       case EXT_COMMUNITY_OVS_NOTFOUND:
-                               printf("not-found ");
-                               break;
-                       case EXT_COMMUNITY_OVS_INVALID:
-                               printf("invalid ");
+                       if (c->c.e.subtype == EXT_COMMUNITY_SUBTYPE_OVS) {
+                               switch (c->c.e.data2) {
+                               case EXT_COMMUNITY_OVS_VALID:
+                                       printf("valid ");
+                                       break;
+                               case EXT_COMMUNITY_OVS_NOTFOUND:
+                                       printf("not-found ");
+                                       break;
+                               case EXT_COMMUNITY_OVS_INVALID:
+                                       printf("invalid ");
+                                       break;
+                               }
                                break;
                        }
+                       printf("0x%llx ", c->c.e.data2);
                        break;
                default:
                        printf("0x%llx ", c->c.e.data2);
Index: rde.h
===================================================================
RCS file: /cvs/src/usr.sbin/bgpd/rde.h,v
retrieving revision 1.209
diff -u -p -r1.209 rde.h
--- rde.h       4 Feb 2019 18:53:10 -0000       1.209
+++ rde.h       8 Feb 2019 09:17:19 -0000
@@ -393,7 +393,7 @@ int  community_ext_set(struct rde_aspath
 void    community_ext_delete(struct rde_aspath *,
            struct filter_community *, struct rde_peer *);
 int     community_ext_conv(struct filter_community *, struct rde_peer *,
-           u_int64_t *);
+           u_int64_t *, u_int64_t *);
 u_char *community_ext_delete_non_trans(u_char *, u_int16_t, u_int16_t *);
 
 /* rde_decide.c */
Index: rde_attr.c
===================================================================
RCS file: /cvs/src/usr.sbin/bgpd/rde_attr.c,v
retrieving revision 1.116
diff -u -p -r1.116 rde_attr.c
--- rde_attr.c  4 Feb 2019 18:53:10 -0000       1.116
+++ rde_attr.c  12 Feb 2019 15:01:05 -0000
@@ -1154,26 +1154,42 @@ aspath_lenmatch(struct aspath *a, enum a
 
 static int
 community_extract(struct filter_community *fc, struct rde_peer *peer,
-     int field, int large, u_int32_t *value)
+     int field, u_int32_t *value)
 {
        u_int32_t data;
        u_int8_t flag;
        switch (field) {
        case 1:
                flag = fc->dflag1;
-               if (large)
-                       data = fc->c.l.data1;
-               else
+               switch (fc->type) {
+               case COMMUNITY_TYPE_BASIC:
                        data = fc->c.b.data1;
+                       break;
+               case COMMUNITY_TYPE_LARGE:
+                       data = fc->c.l.data1;
+                       break;
+               case COMMUNITY_TYPE_EXT:
+                       data = fc->c.e.data1;
+                       break;
+               }
                break;
        case 2:
                flag = fc->dflag2;
-               if (large)
-                       data = fc->c.l.data2;
-               else
+               switch (fc->type) {
+               case COMMUNITY_TYPE_BASIC:
                        data = fc->c.b.data2;
+                       break;
+               case COMMUNITY_TYPE_LARGE:
+                       data = fc->c.l.data2;
+                       break;
+               case COMMUNITY_TYPE_EXT:
+                       data = fc->c.e.data2;
+                       break;
+               }
                break;
        case 3:
+               if (fc->type != COMMUNITY_TYPE_LARGE)
+                       fatalx("%s: bad field %d", __func__, field);
                flag = fc->dflag3;
                data = fc->c.l.data3;
                break;
@@ -1193,7 +1209,7 @@ community_extract(struct filter_communit
        default:
                *value = data;
        }
-       if (!large && *value > USHRT_MAX)
+       if (fc->type == COMMUNITY_TYPE_BASIC && *value > USHRT_MAX)
                return -1;
        return 0;
 }
@@ -1202,10 +1218,24 @@ static int
 community_ext_matchone(struct filter_community *c, struct rde_peer *peer,
     u_int64_t community)
 {
+       u_int32_t       val;
        u_int64_t       com, mask;
 
        community = betoh64(community);
 
+       if (c->dflag3 == COMMUNITY_ANY)
+               /* handle 'ext-community *', etc */
+               return (1);
+
+       /* special handling of ext-community rt * since type is not known */
+       if (c->dflag1 == COMMUNITY_ANY && c->c.e.type == -1) {
+               u_int8_t type = community >> 56;
+               if (type == EXT_COMMUNITY_TRANS_TWO_AS ||
+                   type == EXT_COMMUNITY_TRANS_FOUR_AS ||
+                   type == EXT_COMMUNITY_TRANS_IPV4)
+                       goto subtype;
+       }
+
        com = (u_int64_t)c->c.e.type << 56;
        mask = 0xffULL << 56;
        if ((com & mask) != (community & mask))
@@ -1213,9 +1243,11 @@ community_ext_matchone(struct filter_com
 
        switch (c->c.e.type & EXT_COMMUNITY_VALUE) {
        case EXT_COMMUNITY_TRANS_TWO_AS:
-       case EXT_COMMUNITY_TRANS_IPV4:
        case EXT_COMMUNITY_TRANS_FOUR_AS:
+       case EXT_COMMUNITY_TRANS_IPV4:
        case EXT_COMMUNITY_TRANS_OPAQUE:
+       case EXT_COMMUNITY_NON_TRANS_OPAQUE:
+subtype:
                com = (u_int64_t)c->c.e.subtype << 48;
                mask = 0xffULL << 48;
                if ((com & mask) != (community & mask))
@@ -1229,39 +1261,48 @@ community_ext_matchone(struct filter_com
                return (0);
        }
 
+       if (c->dflag1 == COMMUNITY_ANY)
+               /* handle 'ext-community rt *', etc */
+               return (1);
 
        switch (c->c.e.type & EXT_COMMUNITY_VALUE) {
        case EXT_COMMUNITY_TRANS_TWO_AS:
-               com = (u_int64_t)c->c.e.data1 << 32;
+               if (community_extract(c, peer, 1, &val) == -1)
+                       return (0);
+               com = (u_int64_t)val << 32;
                mask = 0xffffULL << 32;
                if ((com & mask) != (community & mask))
                        return (0);
 
-               com = c->c.e.data2;
+               if (community_extract(c, peer, 2, &val) == -1)
+                       return (0);
+               com = val;
                mask = 0xffffffffULL;
-               if ((com & mask) == (community & mask))
-                       return (1);
                break;
        case EXT_COMMUNITY_TRANS_IPV4:
        case EXT_COMMUNITY_TRANS_FOUR_AS:
-               com = (u_int64_t)c->c.e.data1 << 16;
+               if (community_extract(c, peer, 1, &val) == -1)
+                       return (0);
+               com = (u_int64_t)val << 16;
                mask = 0xffffffffULL << 16;
                if ((com & mask) != (community & mask))
                        return (0);
 
-               com = c->c.e.data2;
+               if (community_extract(c, peer, 2, &val) == -1)
+                       return (0);
+               com = val;
                mask = 0xffff;
-               if ((com & mask) == (community & mask))
-                       return (1);
                break;
        case EXT_COMMUNITY_TRANS_OPAQUE:
+       case EXT_COMMUNITY_NON_TRANS_OPAQUE:
                com = c->c.e.data2;
                mask = EXT_COMMUNITY_OPAQUE_MAX;
-               if ((com & mask) == (community & mask))
-                       return (1);
                break;
        }
 
+       if (c->dflag2 == COMMUNITY_ANY ||
+           (com & mask) == (community & mask))
+               return (1);
        return (0);
 }
 
@@ -1279,8 +1320,8 @@ community_match(struct rde_aspath *asp, 
                /* no communities, no match */
                return (0);
 
-       if (community_extract(fc, peer, 1, 0, &as) == -1 ||
-           community_extract(fc, peer, 2, 0, &type) == -1)
+       if (community_extract(fc, peer, 1, &as) == -1 ||
+           community_extract(fc, peer, 2, &type) == -1)
                /* can't match community */
                return (0);
 
@@ -1310,8 +1351,8 @@ community_set(struct rde_aspath *asp, st
        u_int8_t         f = ATTR_OPTIONAL|ATTR_TRANSITIVE;
 
        if (fc->dflag1 == COMMUNITY_ANY || fc->dflag2 == COMMUNITY_ANY ||
-           community_extract(fc, peer, 1, 0, &as) == -1 ||
-           community_extract(fc, peer, 2, 0, &type) == -1)
+           community_extract(fc, peer, 1, &as) == -1 ||
+           community_extract(fc, peer, 2, &type) == -1)
                /* bad community */
                return (0);
 
@@ -1369,8 +1410,8 @@ community_delete(struct rde_aspath *asp,
                /* no attr nothing to do */
                return;
 
-       if (community_extract(fc, peer, 1, 0, &as) == -1 ||
-           community_extract(fc, peer, 2, 0, &type) == -1)
+       if (community_extract(fc, peer, 1, &as) == -1 ||
+           community_extract(fc, peer, 2, &type) == -1)
                /* bad community, nothing to do */
                return;
 
@@ -1459,7 +1500,7 @@ community_ext_set(struct rde_aspath *asp
        unsigned int     i, ncommunities = 0;
        u_int8_t         f = ATTR_OPTIONAL|ATTR_TRANSITIVE;
 
-       if (community_ext_conv(c, peer, &community))
+       if (community_ext_conv(c, peer, &community, NULL))
                return (0);
 
        attr = attr_optget(asp, ATTR_EXT_COMMUNITIES);
@@ -1503,12 +1544,15 @@ community_ext_delete(struct rde_aspath *
 {
        struct attr     *attr;
        u_int8_t        *p, *n;
-       u_int64_t        community;
+       u_int64_t        community, mask, test;
        u_int16_t        l, len = 0;
        u_int8_t         f;
+       int              check_type = 0;
 
-       if (community_ext_conv(c, peer, &community))
+       if (community_ext_conv(c, peer, &community, &mask))
                return;
+       if (mask != 0 && betoh64(mask) >> 56 == 0)
+               check_type = 1;
 
        attr = attr_optget(asp, ATTR_EXT_COMMUNITIES);
        if (attr == NULL)
@@ -1517,7 +1561,19 @@ community_ext_delete(struct rde_aspath *
 
        p = attr->data;
        for (l = 0; l < attr->len; l += sizeof(community)) {
-               if (memcmp(&community, p + l, sizeof(community)) == 0)
+               memcpy(&test, p + l, sizeof(community));
+               /* special handling of ext-community rt *, type is not known */
+               if (check_type) {
+                       u_int8_t type = betoh64(test) >> 56;
+                       if (type != EXT_COMMUNITY_TRANS_TWO_AS &&
+                           type != EXT_COMMUNITY_TRANS_FOUR_AS &&
+                           type != EXT_COMMUNITY_TRANS_IPV4) {
+                               /* no match */
+                               len += sizeof(community);
+                               continue;
+                       }
+               }
+               if ((test & mask) == (community & mask))
                        /* match */
                        continue;
                len += sizeof(community);
@@ -1534,9 +1590,23 @@ community_ext_delete(struct rde_aspath *
        p = attr->data;
        for (l = 0; l < len && p < attr->data + attr->len;
            p += sizeof(community)) {
-               if (memcmp(&community, p, sizeof(community)) == 0)
+               memcpy(&test, p, sizeof(community));
+               /* special handling of ext-community rt *, type is not known */
+               if (check_type) {
+                       u_int8_t type = betoh64(test) >> 56;
+                       if (type != EXT_COMMUNITY_TRANS_TWO_AS &&
+                           type != EXT_COMMUNITY_TRANS_FOUR_AS &&
+                           type != EXT_COMMUNITY_TRANS_IPV4) {
+                               /* no match */
+                               memcpy(n + l, p, sizeof(community));
+                               l += sizeof(community);
+                               continue;
+                       }
+               }
+               if ((test & mask) == (community & mask)) {
                        /* match */
                        continue;
+               }
                memcpy(n + l, p, sizeof(community));
                l += sizeof(community);
        }
@@ -1550,25 +1620,65 @@ community_ext_delete(struct rde_aspath *
 
 int
 community_ext_conv(struct filter_community *c, struct rde_peer *peer,
-    u_int64_t *community)
+    u_int64_t *community, u_int64_t *mask)
 {
-       u_int64_t       com;
+       u_int64_t       com = 0, m = 0;
+       u_int32_t       val;
 
+       if (c->dflag3 == COMMUNITY_ANY) {
+               m = ~m;
+               goto done;
+       }
+
+       /* special handling of ext-community rt * since type is not known */
+       if (c->dflag1 == COMMUNITY_ANY && c->c.e.type == -1) {
+               m |= 0xffULL << 56;
+               goto subtype;
+       }
        com = (u_int64_t)c->c.e.type << 56;
        switch (c->c.e.type & EXT_COMMUNITY_VALUE) {
        case EXT_COMMUNITY_TRANS_TWO_AS:
+       case EXT_COMMUNITY_TRANS_FOUR_AS:
+       case EXT_COMMUNITY_TRANS_IPV4:
+       case EXT_COMMUNITY_TRANS_OPAQUE:
+subtype:
                com |= (u_int64_t)c->c.e.subtype << 48;
-               com |= (u_int64_t)c->c.e.data1 << 32;
-               com |= c->c.e.data2 & 0xffffffff;
+               if (c->dflag1 == COMMUNITY_ANY) {
+                       m |= 0xffffffffffffULL;
+                       goto done;
+               }
+               break;
+       }
+
+       switch (c->c.e.type & EXT_COMMUNITY_VALUE) {
+       case EXT_COMMUNITY_TRANS_TWO_AS:
+               if (community_extract(c, peer, 1, &val) == -1)
+                       return (-1);
+               com |= (u_int64_t)val << 32;
+
+               if (c->dflag2 == COMMUNITY_ANY) {
+                       m |= 0xffffffffULL;
+                       goto done;
+               }
+               if (community_extract(c, peer, 2, &val) == -1)
+                       return (-1);
+               com |= (u_int64_t)val & 0xffffffffULL;
                break;
        case EXT_COMMUNITY_TRANS_IPV4:
        case EXT_COMMUNITY_TRANS_FOUR_AS:
-               com |= (u_int64_t)c->c.e.subtype << 48;
-               com |= (u_int64_t)c->c.e.data1 << 16;
-               com |= c->c.e.data2 & 0xffff;
+               if (community_extract(c, peer, 1, &val) == -1)
+                       return (-1);
+               com |= (u_int64_t)val << 16;
+
+               if (c->dflag2 == COMMUNITY_ANY) {
+                       m |= 0xffffULL;
+                       goto done;
+               }
+               if (community_extract(c, peer, 2, &val) == -1)
+                       return (-1);
+               com |= (u_int64_t)val & 0xffffULL;
                break;
        case EXT_COMMUNITY_TRANS_OPAQUE:
-               com |= (u_int64_t)c->c.e.subtype << 48;
                com |= c->c.e.data2 & EXT_COMMUNITY_OPAQUE_MAX;
                break;
        default:
@@ -1576,11 +1686,47 @@ community_ext_conv(struct filter_communi
                break;
        }
 
+done:
        *community = htobe64(com);
+       if (mask)
+               *mask = htobe64(~m);
+       else if (m != 0)
+               return (-1);
 
        return (0);
 }
 
+u_char *
+community_ext_delete_non_trans(u_char *data, u_int16_t len, u_int16_t *newlen)
+{
+       u_int8_t        *ext = data, *newdata;
+       u_int16_t       l, nlen = 0;
+
+       for (l = 0; l < len; l += sizeof(u_int64_t)) {
+               if (!(ext[l] & EXT_COMMUNITY_TRANSITIVE))
+                       nlen += sizeof(u_int64_t);
+       }
+
+       if (nlen == 0) {
+               *newlen = 0;
+               return NULL;
+       }
+
+       newdata = malloc(nlen);
+       if (newdata == NULL)
+               fatal("%s", __func__);
+
+       for (l = 0, nlen = 0; l < len; l += sizeof(u_int64_t)) {
+               if (!(ext[l] & EXT_COMMUNITY_TRANSITIVE)) {
+                       memcpy(newdata + nlen, ext + l, sizeof(u_int64_t));
+                       nlen += sizeof(u_int64_t);
+               }
+       }
+
+       *newlen = nlen;
+       return newdata;
+}
+
 struct wire_largecommunity {
        uint32_t        as;
        uint32_t        ld1;
@@ -1602,9 +1748,9 @@ community_large_match(struct rde_aspath 
                /* no communities, no match */
                return (0);
 
-       if (community_extract(fc, peer, 1, 1, &as) == -1 ||
-           community_extract(fc, peer, 2, 1, &ld1) == -1 ||
-           community_extract(fc, peer, 3, 1, &ld2) == -1)
+       if (community_extract(fc, peer, 1, &as) == -1 ||
+           community_extract(fc, peer, 2, &ld1) == -1 ||
+           community_extract(fc, peer, 3, &ld2) == -1)
                /* can't match community */
                return (0);
        
@@ -1638,9 +1784,9 @@ community_large_set(struct rde_aspath *a
 
        if (fc->dflag1 == COMMUNITY_ANY || fc->dflag2 == COMMUNITY_ANY ||
            fc->dflag3 == COMMUNITY_ANY ||
-           community_extract(fc, peer, 1, 1, &as) == -1 ||
-           community_extract(fc, peer, 2, 1, &ld1) == -1 ||
-           community_extract(fc, peer, 3, 1, &ld2) == -1)
+           community_extract(fc, peer, 1, &as) == -1 ||
+           community_extract(fc, peer, 2, &ld1) == -1 ||
+           community_extract(fc, peer, 3, &ld2) == -1)
                /* can't match community */
                return (0);
 
@@ -1703,9 +1849,9 @@ community_large_delete(struct rde_aspath
                /* no attr nothing to do */
                return;
 
-       if (community_extract(fc, peer, 1, 1, &as) == -1 ||
-           community_extract(fc, peer, 2, 1, &ld1) == -1 ||
-           community_extract(fc, peer, 3, 1, &ld2) == -1)
+       if (community_extract(fc, peer, 1, &as) == -1 ||
+           community_extract(fc, peer, 2, &ld1) == -1 ||
+           community_extract(fc, peer, 3, &ld2) == -1)
                /* can't match community */
                return;
 
@@ -1753,35 +1899,4 @@ community_large_delete(struct rde_aspath
        attr_free(asp, attr);
        attr_optadd(asp, f, ATTR_LARGE_COMMUNITIES, n, len);
        free(n);
-}
-
-u_char *
-community_ext_delete_non_trans(u_char *data, u_int16_t len, u_int16_t *newlen)
-{
-       u_int8_t        *ext = data, *newdata;
-       u_int16_t       l, nlen = 0;
-
-       for (l = 0; l < len; l += sizeof(u_int64_t)) {
-               if (!(ext[l] & EXT_COMMUNITY_TRANSITIVE))
-                       nlen += sizeof(u_int64_t);
-       }
-
-       if (nlen == 0) {
-               *newlen = 0;
-               return NULL;
-       }
-
-       newdata = malloc(nlen);
-       if (newdata == NULL)
-               fatal("%s", __func__);
-
-       for (l = 0, nlen = 0; l < len; l += sizeof(u_int64_t)) {
-               if (!(ext[l] & EXT_COMMUNITY_TRANSITIVE)) {
-                       memcpy(newdata + nlen, ext + l, sizeof(u_int64_t));
-                       nlen += sizeof(u_int64_t);
-               }
-       }
-
-       *newlen = nlen;
-       return newdata;
 }
Index: util.c
===================================================================
RCS file: /cvs/src/usr.sbin/bgpd/util.c,v
retrieving revision 1.42
diff -u -p -r1.42 util.c
--- util.c      30 Dec 2018 13:53:07 -0000      1.42
+++ util.c      8 Feb 2019 10:28:02 -0000
@@ -150,13 +150,13 @@ const struct ext_comm_pairs iana_ext_com
 /* NOTE: this function does not check if the type/subtype combo is
  * actually valid. */
 const char *
-log_ext_subtype(u_int8_t type, u_int8_t subtype)
+log_ext_subtype(short type, u_int8_t subtype)
 {
        static char etype[6];
        const struct ext_comm_pairs *cp;
 
        for (cp = iana_ext_comms; cp->subname != NULL; cp++) {
-               if (type == cp->type && subtype == cp->subtype)
+               if ((type == cp->type || type == -1) && subtype == cp->subtype)
                        return (cp->subname);
        }
        snprintf(etype, sizeof(etype), "[%u]", subtype);

Reply via email to