From bd0e176e4a5b96144396c0ba0f3d6f7de39139b8 Mon Sep 17 00:00:00 2001 From: claudio Date: Tue, 31 May 2022 09:45:33 +0000 Subject: [PATCH] Implement a max communities filter match When max-communities X is set on a filterrule the filter will match when more than X communities are present in the path. In other words max-communities 0 means no communities are allowed and max-communities 3 limits it up to 3 communities. There is max-communities, max-ext-communities and max-large-communities for each of the 3 community attributes. These three max checks can be used together. OK tb@ job@ --- usr.sbin/bgpd/bgpd.conf.5 | 17 +++++++++++-- usr.sbin/bgpd/bgpd.h | 5 +++- usr.sbin/bgpd/parse.y | 46 ++++++++++++++++++++++++++++++++++- usr.sbin/bgpd/printconf.c | 9 ++++++- usr.sbin/bgpd/rde.h | 3 ++- usr.sbin/bgpd/rde_community.c | 41 ++++++++++++++++++++++++++++++- usr.sbin/bgpd/rde_filter.c | 18 +++++++++++++- 7 files changed, 131 insertions(+), 8 deletions(-) diff --git a/usr.sbin/bgpd/bgpd.conf.5 b/usr.sbin/bgpd/bgpd.conf.5 index 530e2e9182a..a4073681694 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.219 2022/03/31 17:27:29 naddy Exp $ +.\" $OpenBSD: bgpd.conf.5,v 1.220 2022/05/31 09:45:33 claudio Exp $ .\" .\" Copyright (c) 2004 Claudio Jeker .\" Copyright (c) 2003, 2004 Henning Brauer @@ -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: March 31 2022 $ +.Dd $Mdocdate: May 31 2022 $ .Dt BGPD.CONF 5 .Os .Sh NAME @@ -1609,6 +1609,19 @@ is repeated more than .Ar len times. .Pp +.It Ic max-communities Ns | Ns Ic max-large-communities Ns | \ +Ns Ic max-ext-communities Ar num +This rule applies only to +.Em UPDATES +where the +.Em Basic, +.Em Large , +or +.Em Extended Community +attribute has more than +.Ar num +elements. +.Pp .It Ic nexthop Ar address This rule applies only to .Em UPDATES diff --git a/usr.sbin/bgpd/bgpd.h b/usr.sbin/bgpd/bgpd.h index faa97db8554..c52d1a47629 100644 --- a/usr.sbin/bgpd/bgpd.h +++ b/usr.sbin/bgpd/bgpd.h @@ -1,4 +1,4 @@ -/* $OpenBSD: bgpd.h,v 1.424 2022/05/25 16:03:34 claudio Exp $ */ +/* $OpenBSD: bgpd.h,v 1.425 2022/05/31 09:45:33 claudio Exp $ */ /* * Copyright (c) 2003, 2004 Henning Brauer @@ -1053,6 +1053,9 @@ struct filter_match { struct filter_prefixset prefixset; struct filter_originset originset; struct filter_ovs ovs; + int maxcomm; + int maxextcomm; + int maxlargecomm; }; struct filter_rule { diff --git a/usr.sbin/bgpd/parse.y b/usr.sbin/bgpd/parse.y index 5dd4eb55795..a0cd7b30cfa 100644 --- a/usr.sbin/bgpd/parse.y +++ b/usr.sbin/bgpd/parse.y @@ -1,4 +1,4 @@ -/* $OpenBSD: parse.y,v 1.424 2022/05/23 13:40:12 deraadt Exp $ */ +/* $OpenBSD: parse.y,v 1.425 2022/05/31 09:45:33 claudio Exp $ */ /* * Copyright (c) 2002, 2003, 2004 Henning Brauer @@ -220,6 +220,7 @@ typedef struct { %token FROM TO ANY %token CONNECTED STATIC %token COMMUNITY EXTCOMMUNITY LARGECOMMUNITY DELETE +%token MAXCOMMUNITIES MAXEXTCOMMUNITIES MAXLARGECOMMUNITIES %token PREFIX PREFIXLEN PREFIXSET %token ROASET ORIGINSET OVS EXPIRES %token ASSET SOURCEAS TRANSITAS PEERAS MAXASLEN MAXASSEQ @@ -2338,6 +2339,46 @@ filter_elm : filter_prefix_h { } free($3); } + | MAXCOMMUNITIES NUMBER { + if ($2 < 0 || $2 > INT16_MAX) { + yyerror("bad max-comunities %lld", $2); + YYERROR; + } + if (fmopts.m.maxcomm != 0) { + yyerror("%s already specified", + "max-communities"); + YYERROR; + } + /* + * Offset by 1 since 0 means not used. + * The match function then uses >= to compensate. + */ + fmopts.m.maxcomm = $2 + 1; + } + | MAXEXTCOMMUNITIES NUMBER { + if ($2 < 0 || $2 > INT16_MAX) { + yyerror("bad max-ext-communities %lld", $2); + YYERROR; + } + if (fmopts.m.maxextcomm != 0) { + yyerror("%s already specified", + "max-ext-communities"); + YYERROR; + } + fmopts.m.maxextcomm = $2 + 1; + } + | MAXLARGECOMMUNITIES NUMBER { + if ($2 < 0 || $2 > INT16_MAX) { + yyerror("bad max-large-communities %lld", $2); + YYERROR; + } + if (fmopts.m.maxlargecomm != 0) { + yyerror("%s already specified", + "max-large-communities"); + YYERROR; + } + fmopts.m.maxlargecomm = $2 + 1; + } | NEXTHOP address { if (fmopts.m.nexthop.flags) { yyerror("nexthop already specified"); @@ -2999,6 +3040,9 @@ lookup(char *s) { "match", MATCH}, { "max-as-len", MAXASLEN}, { "max-as-seq", MAXASSEQ}, + { "max-communities", MAXCOMMUNITIES}, + { "max-ext-communities", MAXEXTCOMMUNITIES}, + { "max-large-communities", MAXLARGECOMMUNITIES}, { "max-prefix", MAXPREFIX}, { "maxlen", MAXLEN}, { "md5sig", MD5SIG}, diff --git a/usr.sbin/bgpd/printconf.c b/usr.sbin/bgpd/printconf.c index 1ad98e4ea49..3a20405ef82 100644 --- a/usr.sbin/bgpd/printconf.c +++ b/usr.sbin/bgpd/printconf.c @@ -1,4 +1,4 @@ -/* $OpenBSD: printconf.c,v 1.151 2022/05/25 16:03:34 claudio Exp $ */ +/* $OpenBSD: printconf.c,v 1.152 2022/05/31 09:45:33 claudio Exp $ */ /* * Copyright (c) 2003, 2004 Henning Brauer @@ -925,6 +925,13 @@ print_rule(struct bgpd_config *conf, struct filter_rule *r) } } + if (r->match.maxcomm != 0) + printf("max-communities %d ", r->match.maxcomm - 1); + if (r->match.maxextcomm != 0) + printf("max-ext-communities %d ", r->match.maxextcomm - 1); + if (r->match.maxlargecomm != 0) + printf("max-large-communities %d ", r->match.maxlargecomm - 1); + print_set(&r->set); printf("\n"); diff --git a/usr.sbin/bgpd/rde.h b/usr.sbin/bgpd/rde.h index f77e9899bb2..d78711d6b99 100644 --- a/usr.sbin/bgpd/rde.h +++ b/usr.sbin/bgpd/rde.h @@ -1,4 +1,4 @@ -/* $OpenBSD: rde.h,v 1.252 2022/05/25 16:03:34 claudio Exp $ */ +/* $OpenBSD: rde.h,v 1.253 2022/05/31 09:45:33 claudio Exp $ */ /* * Copyright (c) 2003, 2004 Claudio Jeker and @@ -452,6 +452,7 @@ int aspath_lenmatch(struct aspath *, enum aslen_spec, u_int); /* rde_community.c */ int community_match(struct rde_community *, struct community *, struct rde_peer *); +int community_count(struct rde_community *, uint8_t type); int community_set(struct rde_community *, struct community *, struct rde_peer *); void community_delete(struct rde_community *, struct community *, diff --git a/usr.sbin/bgpd/rde_community.c b/usr.sbin/bgpd/rde_community.c index 815ac40cd20..bdf0ce462d6 100644 --- a/usr.sbin/bgpd/rde_community.c +++ b/usr.sbin/bgpd/rde_community.c @@ -1,4 +1,4 @@ -/* $OpenBSD: rde_community.c,v 1.5 2022/05/25 16:03:34 claudio Exp $ */ +/* $OpenBSD: rde_community.c,v 1.6 2022/05/31 09:45:33 claudio Exp $ */ /* * Copyright (c) 2019 Claudio Jeker @@ -282,6 +282,45 @@ struct rde_peer *peer) } } +/* + * Count the number of communities of type type. + */ +int +community_count(struct rde_community *comm, uint8_t type) +{ + size_t l; + int count = 0; + + /* use the fact that the array is ordered by type */ + switch (type) { + case COMMUNITY_TYPE_BASIC: + for (l = 0; l < comm->nentries; l++) { + if ((uint8_t)comm->communities[l].flags == type) + count++; + else + break; + } + break; + case COMMUNITY_TYPE_EXT: + for (l = 0; l < comm->nentries; l++) { + if ((uint8_t)comm->communities[l].flags == type) + count++; + else if ((uint8_t)comm->communities[l].flags > type) + break; + } + break; + case COMMUNITY_TYPE_LARGE: + for (l = comm->nentries; l > 0; l--) { + if ((uint8_t)comm->communities[l - 1].flags == type) + count++; + else + break; + } + break; + } + return count; +} + /* * Insert a community, expanding local-as and neighbor-as if needed. */ diff --git a/usr.sbin/bgpd/rde_filter.c b/usr.sbin/bgpd/rde_filter.c index 2b40cab34f8..c2c21ac062d 100644 --- a/usr.sbin/bgpd/rde_filter.c +++ b/usr.sbin/bgpd/rde_filter.c @@ -1,4 +1,4 @@ -/* $OpenBSD: rde_filter.c,v 1.127 2022/02/06 09:51:19 claudio Exp $ */ +/* $OpenBSD: rde_filter.c,v 1.128 2022/05/31 09:45:33 claudio Exp $ */ /* * Copyright (c) 2004 Claudio Jeker @@ -246,6 +246,22 @@ rde_filter_match(struct filter_rule *f, struct rde_peer *peer, return (0); } + if (f->match.maxcomm != 0) { + if (f->match.maxcomm > + community_count(&state->communities, COMMUNITY_TYPE_BASIC)) + return (0); + } + if (f->match.maxextcomm != 0) { + if (f->match.maxextcomm > + community_count(&state->communities, COMMUNITY_TYPE_EXT)) + return (0); + } + if (f->match.maxlargecomm != 0) { + if (f->match.maxlargecomm > + community_count(&state->communities, COMMUNITY_TYPE_LARGE)) + return (0); + } + if (f->match.nexthop.flags != 0) { struct bgpd_addr *nexthop, *cmpaddr; if (state->nexthop == NULL) -- 2.20.1