bgpd, support wildcard matching on ext-community

classic Classic list List threaded Threaded
3 messages Options
Reply | Threaded
Open this post in threaded view
|

bgpd, support wildcard matching on ext-community

Claudio Jeker
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 | Threaded
Open this post in threaded view
|

Re: bgpd, support wildcard matching on ext-community

Claudio Jeker
On Tue, Feb 12, 2019 at 04:32:03PM +0100, Claudio Jeker wrote:

> 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 :)

Nobody excited about this?

> --
> :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 | Threaded
Open this post in threaded view
|

Re: bgpd, support wildcard matching on ext-community

Claudio Jeker
On Wed, Feb 20, 2019 at 09:26:35AM +0100, Claudio Jeker wrote:

> On Tue, Feb 12, 2019 at 04:32:03PM +0100, Claudio Jeker wrote:
> > 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 :)
>
> Nobody excited about this?
>

Actually here a new version that actually applies again after all the
changes done recently.

--
:wq Claudio

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.373
diff -u -p -r1.373 bgpd.h
--- bgpd.h 19 Feb 2019 09:13:23 -0000 1.373
+++ bgpd.h 20 Feb 2019 08:22:13 -0000
@@ -774,7 +774,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;
@@ -867,8 +867,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
@@ -878,7 +880,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;
 };
@@ -907,7 +909,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" }, \
@@ -1259,7 +1261,7 @@ const char *log_in6addr(const struct in6
 const char *log_sockaddr(struct sockaddr *, socklen_t);
 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.379
diff -u -p -r1.379 parse.y
--- parse.y 18 Feb 2019 16:31:46 -0000 1.379
+++ parse.y 20 Feb 2019 08:22:13 -0000
@@ -1100,6 +1100,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;
@@ -1109,7 +1110,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;
  }
@@ -3598,7 +3599,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;
@@ -3607,6 +3608,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);
@@ -3672,12 +3681,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 = 0, subtype = 0;
 
+ 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);
@@ -3688,33 +3701,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') {
@@ -3728,21 +3747,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.131
diff -u -p -r1.131 printconf.c
--- printconf.c 18 Feb 2019 11:43:44 -0000 1.131
+++ printconf.c 20 Feb 2019 09:48:53 -0000
@@ -200,34 +200,66 @@ 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),
-    (unsigned long long)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),
-    (unsigned long long)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 ",
+    (unsigned long long)c->c.e.data2);
  break;
  case EXT_COMMUNITY_TRANS_OPAQUE:
  case EXT_COMMUNITY_TRANS_EVPN:
  printf("0x%llx ", (unsigned long long)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 ", (unsigned long long)c->c.e.data2);
  break;
  default:
  printf("0x%llx ", (unsigned long long)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.118
diff -u -p -r1.118 rde_attr.c
--- rde_attr.c 15 Feb 2019 09:55:21 -0000 1.118
+++ rde_attr.c 20 Feb 2019 08:22:13 -0000
@@ -1155,26 +1155,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;
@@ -1194,7 +1210,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;
 }
@@ -1203,10 +1219,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 = be64toh(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))
@@ -1214,9 +1244,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))
@@ -1230,39 +1262,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);
 }
 
@@ -1280,8 +1321,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);
 
@@ -1311,8 +1352,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);
 
@@ -1370,8 +1411,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;
 
@@ -1460,7 +1501,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);
@@ -1504,12 +1545,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)
@@ -1518,7 +1562,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);
@@ -1535,9 +1591,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);
  }
@@ -1551,25 +1621,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:
@@ -1577,11 +1687,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;
@@ -1603,9 +1749,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);
 
@@ -1639,9 +1785,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);
 
@@ -1704,9 +1850,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;
 
@@ -1754,35 +1900,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.45
diff -u -p -r1.45 util.c
--- util.c 18 Feb 2019 12:35:08 -0000 1.45
+++ util.c 20 Feb 2019 08:22:13 -0000
@@ -149,13 +149,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);