diff options
author | Claudio Jeker <claudio@cvs.openbsd.org> | 2019-02-26 10:49:16 +0000 |
---|---|---|
committer | Claudio Jeker <claudio@cvs.openbsd.org> | 2019-02-26 10:49:16 +0000 |
commit | d70cbcf1ecef9eafe8d77b451466b5edb728ba01 (patch) | |
tree | 7f0227f504be95673e1cb96813aa6ffdd3f20464 /usr.sbin | |
parent | 8b7c44fdfd62544312b38cf7248eb584f04c5271 (diff) |
Add support for '*', local-as and neighbor-as for ext-community matching
and setting. This allows rules like:
ext-community * * # delete any ext-community
ext-community ovs * # delete any ext-community of specified type
ext-community rt 1.2.3.4:*
and
ext-community rt 65001:local-as
ext-community rt local-as:11111
Note: 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 an expand of as-number like
ext-community rt local-as:11111
then bgpd will default to the 4-byte AS type to encode the community.
OK benno@
Diffstat (limited to 'usr.sbin')
-rw-r--r-- | usr.sbin/bgpd/bgpd.conf.5 | 53 | ||||
-rw-r--r-- | usr.sbin/bgpd/bgpd.h | 12 | ||||
-rw-r--r-- | usr.sbin/bgpd/parse.y | 77 | ||||
-rw-r--r-- | usr.sbin/bgpd/printconf.c | 60 | ||||
-rw-r--r-- | usr.sbin/bgpd/rde.h | 4 | ||||
-rw-r--r-- | usr.sbin/bgpd/rde_attr.c | 273 | ||||
-rw-r--r-- | usr.sbin/bgpd/util.c | 6 |
7 files changed, 348 insertions, 137 deletions
diff --git a/usr.sbin/bgpd/bgpd.conf.5 b/usr.sbin/bgpd/bgpd.conf.5 index 377efd1cdfd..a6f975e935d 100644 --- a/usr.sbin/bgpd/bgpd.conf.5 +++ b/usr.sbin/bgpd/bgpd.conf.5 @@ -1,4 +1,4 @@ -.\" $OpenBSD: bgpd.conf.5,v 1.185 2019/02/11 17:45:59 jmc Exp $ +.\" $OpenBSD: bgpd.conf.5,v 1.186 2019/02/26 10:49:15 claudio Exp $ .\" .\" Copyright (c) 2004 Claudio Jeker <claudio@openbsd.org> .\" Copyright (c) 2003, 2004 Henning Brauer <henning@openbsd.org> @@ -16,7 +16,7 @@ .\" ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF .\" OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. .\" -.Dd $Mdocdate: February 11 2019 $ +.Dd $Mdocdate: February 26 2019 $ .Dt BGPD.CONF 5 .Os .Sh NAME @@ -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 assigned AS number. .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 @@ -1737,6 +1752,22 @@ vrfri VRF Route Import 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 .Em LOCAL_PREF diff --git a/usr.sbin/bgpd/bgpd.h b/usr.sbin/bgpd/bgpd.h index 07a57e44f81..d3a868d0ccf 100644 --- a/usr.sbin/bgpd/bgpd.h +++ b/usr.sbin/bgpd/bgpd.h @@ -1,4 +1,4 @@ -/* $OpenBSD: bgpd.h,v 1.374 2019/02/21 11:17:22 claudio Exp $ */ +/* $OpenBSD: bgpd.h,v 1.375 2019/02/26 10:49:15 claudio Exp $ */ /* * Copyright (c) 2003, 2004 Henning Brauer <henning@openbsd.org> @@ -784,7 +784,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; @@ -877,8 +877,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 @@ -888,7 +890,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; }; @@ -917,7 +919,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" }, \ @@ -1269,7 +1271,7 @@ const char *log_in6addr(const struct in6_addr *); 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); diff --git a/usr.sbin/bgpd/parse.y b/usr.sbin/bgpd/parse.y index 3b89e5b48e9..1738f9e055f 100644 --- a/usr.sbin/bgpd/parse.y +++ b/usr.sbin/bgpd/parse.y @@ -1,4 +1,4 @@ -/* $OpenBSD: parse.y,v 1.379 2019/02/18 16:31:46 claudio Exp $ */ +/* $OpenBSD: parse.y,v 1.380 2019/02/26 10:49:15 claudio Exp $ */ /* * Copyright (c) 2002, 2003, 2004 Henning Brauer <henning@openbsd.org> @@ -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 *subtype) } 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_t *v) 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_community *c, char *t, char *s) 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_community *c, char *t, char *s) 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) { diff --git a/usr.sbin/bgpd/printconf.c b/usr.sbin/bgpd/printconf.c index bd042334974..80f733a5f2a 100644 --- a/usr.sbin/bgpd/printconf.c +++ b/usr.sbin/bgpd/printconf.c @@ -1,4 +1,4 @@ -/* $OpenBSD: printconf.c,v 1.131 2019/02/18 11:43:44 claudio Exp $ */ +/* $OpenBSD: printconf.c,v 1.132 2019/02/26 10:49:15 claudio Exp $ */ /* * Copyright (c) 2003, 2004 Henning Brauer <henning@openbsd.org> @@ -200,34 +200,66 @@ print_community(struct filter_community *c) } 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); diff --git a/usr.sbin/bgpd/rde.h b/usr.sbin/bgpd/rde.h index 1e4fb6fccbb..8eb453f617a 100644 --- a/usr.sbin/bgpd/rde.h +++ b/usr.sbin/bgpd/rde.h @@ -1,4 +1,4 @@ -/* $OpenBSD: rde.h,v 1.209 2019/02/04 18:53:10 claudio Exp $ */ +/* $OpenBSD: rde.h,v 1.210 2019/02/26 10:49:15 claudio Exp $ */ /* * Copyright (c) 2003, 2004 Claudio Jeker <claudio@openbsd.org> and @@ -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 */ diff --git a/usr.sbin/bgpd/rde_attr.c b/usr.sbin/bgpd/rde_attr.c index fa8086842f0..d1743197d24 100644 --- a/usr.sbin/bgpd/rde_attr.c +++ b/usr.sbin/bgpd/rde_attr.c @@ -1,4 +1,4 @@ -/* $OpenBSD: rde_attr.c,v 1.118 2019/02/15 09:55:21 claudio Exp $ */ +/* $OpenBSD: rde_attr.c,v 1.119 2019/02/26 10:49:15 claudio Exp $ */ /* * Copyright (c) 2004 Claudio Jeker <claudio@openbsd.org> @@ -1155,26 +1155,42 @@ aspath_lenmatch(struct aspath *a, enum aslen_spec type, u_int aslen) 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_community *fc, struct rde_peer *peer, 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_community *c, struct rde_peer *peer, 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_community *c, struct rde_peer *peer, 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, struct filter_community *fc, /* 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, struct filter_community *fc, 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, struct filter_community *fc, /* 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, struct filter_community *c, 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 *asp, struct filter_community *c, { 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 *asp, struct filter_community *c, 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 *asp, struct filter_community *c, 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 *asp, struct filter_community *c, 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_community *c, struct rde_peer *peer, 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 *asp, struct filter_community *fc, /* 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 *asp, struct filter_community *fc, 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 *asp, struct filter_community *fc, /* 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; @@ -1755,34 +1901,3 @@ community_large_delete(struct rde_aspath *asp, struct filter_community *fc, 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; -} diff --git a/usr.sbin/bgpd/util.c b/usr.sbin/bgpd/util.c index ed1e406662d..c97c9b7f216 100644 --- a/usr.sbin/bgpd/util.c +++ b/usr.sbin/bgpd/util.c @@ -1,4 +1,4 @@ -/* $OpenBSD: util.c,v 1.46 2019/02/21 11:17:22 claudio Exp $ */ +/* $OpenBSD: util.c,v 1.47 2019/02/26 10:49:15 claudio Exp $ */ /* * Copyright (c) 2006 Claudio Jeker <claudio@openbsd.org> @@ -146,13 +146,13 @@ const struct ext_comm_pairs iana_ext_comms[] = IANA_EXT_COMMUNITIES; /* 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); |