diff options
author | Peter Hessler <phessler@cvs.openbsd.org> | 2016-10-14 16:05:37 +0000 |
---|---|---|
committer | Peter Hessler <phessler@cvs.openbsd.org> | 2016-10-14 16:05:37 +0000 |
commit | 01b441ff7f82f081c867c730843a20ced322c69a (patch) | |
tree | ca0453d28e3be1dd47c8ca80ee1921b62665f03a /usr.sbin/bgpd | |
parent | ab11fdd7396c99faa03965917bfae604b99150fb (diff) |
Add support for draft-ietf-idr-large-community
Joint work with Job Snijders, many thanks!
OK benno@ deraadt@
Diffstat (limited to 'usr.sbin/bgpd')
-rw-r--r-- | usr.sbin/bgpd/bgpd.8 | 15 | ||||
-rw-r--r-- | usr.sbin/bgpd/bgpd.conf.5 | 64 | ||||
-rw-r--r-- | usr.sbin/bgpd/bgpd.h | 20 | ||||
-rw-r--r-- | usr.sbin/bgpd/control.c | 4 | ||||
-rw-r--r-- | usr.sbin/bgpd/parse.y | 118 | ||||
-rw-r--r-- | usr.sbin/bgpd/printconf.c | 52 | ||||
-rw-r--r-- | usr.sbin/bgpd/rde.c | 27 | ||||
-rw-r--r-- | usr.sbin/bgpd/rde.h | 11 | ||||
-rw-r--r-- | usr.sbin/bgpd/rde_attr.c | 148 | ||||
-rw-r--r-- | usr.sbin/bgpd/rde_filter.c | 134 |
10 files changed, 579 insertions, 14 deletions
diff --git a/usr.sbin/bgpd/bgpd.8 b/usr.sbin/bgpd/bgpd.8 index aab3d517dd9..89f34fc0d81 100644 --- a/usr.sbin/bgpd/bgpd.8 +++ b/usr.sbin/bgpd/bgpd.8 @@ -1,4 +1,4 @@ -.\" $OpenBSD: bgpd.8,v 1.48 2013/08/14 06:32:36 jmc Exp $ +.\" $OpenBSD: bgpd.8,v 1.49 2016/10/14 16:05:35 phessler Exp $ .\" .\" Copyright (c) 2003, 2004 Henning Brauer <henning@openbsd.org> .\" @@ -14,7 +14,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: August 14 2013 $ +.Dd $Mdocdate: October 14 2016 $ .Dt BGPD 8 .Os .Sh NAME @@ -238,6 +238,17 @@ control socket .Re .Pp .Rs +.%A J. Snijders +.%A J. Heitz +.%A K. Patel +.%A I. Bagdonas +.%A A. Simpson +.%D September 2016 +.%R draft-ietf-idr-large-community +.%T Large BGP Communities Attribute +.Re +.Pp +.Rs .%A A. Heffernan .%D August 1998 .%R RFC 2385 diff --git a/usr.sbin/bgpd/bgpd.conf.5 b/usr.sbin/bgpd/bgpd.conf.5 index a4b9613951c..987c2b3a712 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.147 2016/10/05 07:38:06 phessler Exp $ +.\" $OpenBSD: bgpd.conf.5,v 1.148 2016/10/14 16:05:35 phessler 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: October 5 2016 $ +.Dd $Mdocdate: October 14 2016 $ .Dt BGPD.CONF 5 .Os .Sh NAME @@ -1143,6 +1143,37 @@ may be set to which is expanded to the current neighbor remote AS number. .Pp .It Xo +.Ic large-community +.Ar as-number : Ns Ar local : Ns Ar local +This rule applies only to +.Em UPDATES +where the +.Ic Large community +path attribute is present and matches. +Communities are specified as +.Ar as-number : Ns Ar local : Ns Ar local , +where +.Ar as-number +is an AS number and +.Ar local +is a locally significant number between zero and +.Li 4294967295. +Both +.Ar as-number +and +.Ar local +may be set to +.Sq * +to do wildcard matching. +Both +.Ar as-number +and +.Ar local +may be set to +.Ic neighbor-as , +which is expanded to the current neighbor remote AS number. +.Pp +.It Xo .Ic ext-community .Ar subtype Ar as-number : Ns Ar local .Xc @@ -1375,6 +1406,35 @@ may be set to to do wildcard matching. .Pp .It Xo +.Ic large-community Op Ar delete +.Ar as-number : Ns Ar local : Ns Ar local +.Xc +.It Xo +.Ic large-community Op Ar delete +.Ar name +.Xc +Set or delete the +.Em Large Communities +path attribute. +Communities are specified as +.Ar as-number : Ns Ar local : Ns Ar local , +where +.Ar as-number +is an AS number and +.Ar local +is a locally-significant number between zero and +.Li 4294967295 . +For +.Cm delete , +both +.Ar as-number +and +.Ar local +may be set to +.Sq * +to do wildcard matching. +.Pp +.It Xo .Ic ext-community Op Ar delete .Ar subtype Ar as-number : Ns Ar local .Xc diff --git a/usr.sbin/bgpd/bgpd.h b/usr.sbin/bgpd/bgpd.h index 1531dac3bd9..87dd2d891b2 100644 --- a/usr.sbin/bgpd/bgpd.h +++ b/usr.sbin/bgpd/bgpd.h @@ -1,4 +1,4 @@ -/* $OpenBSD: bgpd.h,v 1.296 2016/10/05 07:38:06 phessler Exp $ */ +/* $OpenBSD: bgpd.h,v 1.297 2016/10/14 16:05:35 phessler Exp $ */ /* * Copyright (c) 2003, 2004 Henning Brauer <henning@openbsd.org> @@ -377,6 +377,7 @@ enum imsg_type { IMSG_CTL_SHOW_RIB_PREFIX, IMSG_CTL_SHOW_RIB_ATTR, IMSG_CTL_SHOW_RIB_COMMUNITY, + IMSG_CTL_SHOW_RIB_LARGECOMMUNITY, IMSG_CTL_SHOW_NETWORK, IMSG_CTL_SHOW_RIB_MEM, IMSG_CTL_SHOW_TERSE, @@ -648,6 +649,18 @@ struct filter_community { int type; }; +struct filter_largecommunity { + int64_t as; + int64_t ld1; + int64_t ld2; +}; + +struct wire_largecommunity { + uint32_t as; + uint32_t ld1; + uint32_t ld2; +}; + struct filter_extcommunity { u_int16_t flags; u_int8_t type; @@ -675,6 +688,7 @@ struct ctl_show_rib_request { struct bgpd_addr prefix; struct filter_as as; struct filter_community community; + struct filter_largecommunity large_community; u_int32_t peerid; pid_t pid; u_int16_t flags; @@ -793,6 +807,7 @@ struct filter_match { struct filter_as as; struct filter_aslen aslen; struct filter_community community; + struct filter_largecommunity large_community; struct filter_extcommunity ext_community; }; @@ -834,6 +849,8 @@ enum action_types { ACTION_SET_NEXTHOP_SELF, ACTION_SET_COMMUNITY, ACTION_DEL_COMMUNITY, + ACTION_DEL_LARGE_COMMUNITY, + ACTION_SET_LARGE_COMMUNITY, ACTION_SET_EXT_COMMUNITY, ACTION_DEL_EXT_COMMUNITY, ACTION_PFTABLE, @@ -852,6 +869,7 @@ struct filter_set { int32_t relative; struct bgpd_addr nexthop; struct filter_community community; + struct filter_largecommunity large_community; struct filter_extcommunity ext_community; char pftable[PFTABLE_LEN]; char rtlabel[RTLABEL_LEN]; diff --git a/usr.sbin/bgpd/control.c b/usr.sbin/bgpd/control.c index 38fde685290..b65fed6406f 100644 --- a/usr.sbin/bgpd/control.c +++ b/usr.sbin/bgpd/control.c @@ -1,4 +1,4 @@ -/* $OpenBSD: control.c,v 1.82 2015/12/05 18:28:04 benno Exp $ */ +/* $OpenBSD: control.c,v 1.83 2016/10/14 16:05:35 phessler Exp $ */ /* * Copyright (c) 2003, 2004 Henning Brauer <henning@openbsd.org> @@ -244,6 +244,7 @@ control_dispatch_msg(struct pollfd *pfd, u_int *ctl_cnt) case IMSG_CTL_SHOW_RIB_PREFIX: case IMSG_CTL_SHOW_RIB_MEM: case IMSG_CTL_SHOW_RIB_COMMUNITY: + case IMSG_CTL_SHOW_RIB_LARGECOMMUNITY: case IMSG_CTL_SHOW_NETWORK: case IMSG_CTL_SHOW_TERSE: case IMSG_CTL_SHOW_TIMER: @@ -462,6 +463,7 @@ control_dispatch_msg(struct pollfd *pfd, u_int *ctl_cnt) break; case IMSG_CTL_SHOW_RIB_MEM: case IMSG_CTL_SHOW_RIB_COMMUNITY: + case IMSG_CTL_SHOW_RIB_LARGECOMMUNITY: case IMSG_CTL_SHOW_NETWORK: c->ibuf.pid = imsg.hdr.pid; imsg_ctl_rde(imsg.hdr.type, imsg.hdr.pid, diff --git a/usr.sbin/bgpd/parse.y b/usr.sbin/bgpd/parse.y index 524ec1fa07a..f3f17d3ed8d 100644 --- a/usr.sbin/bgpd/parse.y +++ b/usr.sbin/bgpd/parse.y @@ -1,10 +1,12 @@ -/* $OpenBSD: parse.y,v 1.289 2016/10/05 07:38:06 phessler Exp $ */ +/* $OpenBSD: parse.y,v 1.290 2016/10/14 16:05:36 phessler Exp $ */ /* * Copyright (c) 2002, 2003, 2004 Henning Brauer <henning@openbsd.org> * Copyright (c) 2001 Markus Friedl. All rights reserved. * Copyright (c) 2001 Daniel Hartmeier. All rights reserved. * Copyright (c) 2001 Theo de Raadt. All rights reserved. + * Copyright (c) 2016 Job Snijders <job@instituut.net> + * Copyright (c) 2016 Peter Hessler <phessler@openbsd.org> * * Permission to use, copy, modify, and distribute this software for any * purpose with or without fee is hereby granted, provided that the above @@ -138,6 +140,8 @@ struct filter_rule *get_rule(enum action_types); int getcommunity(char *); int parsecommunity(struct filter_community *, char *); +u_int getlargecommunity(char *); +int parselargecommunity(struct filter_largecommunity *, char *); int parsesubtype(char *); int parseextvalue(char *, u_int32_t *); int parseextcommunity(struct filter_extcommunity *, char *, @@ -185,7 +189,7 @@ typedef struct { %token QUICK %token FROM TO ANY %token CONNECTED STATIC -%token COMMUNITY EXTCOMMUNITY +%token COMMUNITY EXTCOMMUNITY LARGECOMMUNITY %token PREFIX PREFIXLEN SOURCEAS TRANSITAS PEERAS DELETE MAXASLEN MAXASSEQ %token SET LOCALPREF MED METRIC NEXTHOP REJECT BLACKHOLE NOMODIFY SELF %token PREPEND_SELF PREPEND_PEER PFTABLE WEIGHT RTLABEL ORIGIN @@ -1712,10 +1716,12 @@ filter_as : as4number_any { filter_match_h : /* empty */ { bzero(&$$, sizeof($$)); $$.m.community.as = COMMUNITY_UNSET; + $$.m.large_community.as = COMMUNITY_UNSET; } | { bzero(&fmopts, sizeof(fmopts)); fmopts.m.community.as = COMMUNITY_UNSET; + fmopts.m.large_community.as = COMMUNITY_UNSET; } filter_match { memcpy(&$$, &fmopts, sizeof($$)); @@ -1776,6 +1782,18 @@ filter_elm : filter_prefix_h { } free($2); } + | LARGECOMMUNITY STRING { + if (fmopts.m.large_community.as != COMMUNITY_UNSET) { + yyerror("\"large-community\" already specified"); + free($2); + YYERROR; + } + if (parselargecommunity(&fmopts.m.large_community, $2) == -1) { + free($2); + YYERROR; + } + free($2); + } | EXTCOMMUNITY STRING STRING { if (fmopts.m.ext_community.flags & EXT_COMMUNITY_FLAG_VALID) { @@ -2131,6 +2149,31 @@ filter_set_opt : LOCALPREF NUMBER { YYERROR; } } + | LARGECOMMUNITY delete STRING { + if (($$ = calloc(1, sizeof(struct filter_set))) == NULL) + fatal(NULL); + if ($2) + $$->type = ACTION_DEL_LARGE_COMMUNITY; + else + $$->type = ACTION_SET_LARGE_COMMUNITY; + + if (parselargecommunity(&$$->action.large_community, + $3) == -1) { + free($3); + free($$); + YYERROR; + } + free($3); + /* Don't allow setting of any match */ + if (!$2 && + ($$->action.large_community.as == COMMUNITY_ANY || + $$->action.large_community.ld1 == COMMUNITY_ANY || + $$->action.large_community.ld2 == COMMUNITY_ANY)) { + yyerror("'*' is not allowed in set community"); + free($$); + YYERROR; + } + } | EXTCOMMUNITY delete STRING STRING { if (($$ = calloc(1, sizeof(struct filter_set))) == NULL) fatal(NULL); @@ -2266,6 +2309,7 @@ lookup(char *s) { "inet6", IPV6}, { "ipsec", IPSEC}, { "key", KEY}, + { "large-community", LARGECOMMUNITY}, { "listen", LISTEN}, { "local-address", LOCALADDR}, { "localpref", LOCALPREF}, @@ -2910,6 +2954,59 @@ parsecommunity(struct filter_community *c, char *s) return (0); } +u_int +getlargecommunity(char *s) +{ + u_int val; + const char *errstr; + + if (strcmp(s, "*") == 0) + return (COMMUNITY_ANY); + if (strcmp(s, "neighbor-as") == 0) + return (COMMUNITY_NEIGHBOR_AS); + val = strtonum(s, 0, UINT_MAX, &errstr); + if (errstr) { + yyerror("Large Community %s is %s (max: %u)", + s, errstr, UINT_MAX); + return (COMMUNITY_ERROR); + } + return (val); +} + +int +parselargecommunity(struct filter_largecommunity *c, char *s) +{ + char *p, *q; + int64_t as, ld1, ld2; + + if ((p = strchr(s, ':')) == NULL) { + yyerror("Bad community syntax"); + return (-1); + } + *p++ = 0; + + if ((q = strchr(p, ':')) == NULL) { + yyerror("Bad community syntax"); + return (-1); + } + *q++ = 0; + + if ((as = getlargecommunity(s)) == COMMUNITY_ERROR) + return (-1); + + if ((ld1 = getlargecommunity(p)) == COMMUNITY_ERROR) + return (-1); + + if ((ld2 = getlargecommunity(q)) == COMMUNITY_ERROR) + return (-1); + + c->as = as; + c->ld1 = ld1; + c->ld2 = ld2; + + return (0); +} + int parsesubtype(char *type) { @@ -3527,6 +3624,10 @@ merge_filterset(struct filter_set_head *sh, struct filter_set *s) yyerror("community is already set"); else if (s->type == ACTION_DEL_COMMUNITY) yyerror("community will already be deleted"); + else if (s->type == ACTION_SET_LARGE_COMMUNITY) + yyerror("large-community is already set"); + else if (s->type == ACTION_DEL_LARGE_COMMUNITY) + yyerror("large-community will already be deleted"); else if (s->type == ACTION_SET_EXT_COMMUNITY) yyerror("ext-community is already set"); else if (s->type == ACTION_DEL_EXT_COMMUNITY) @@ -3558,6 +3659,18 @@ merge_filterset(struct filter_set_head *sh, struct filter_set *s) return (0); } break; + case ACTION_SET_LARGE_COMMUNITY: + case ACTION_DEL_LARGE_COMMUNITY: + if (s->action.large_community.as < + t->action.large_community.as || + (s->action.large_community.as == + t->action.large_community.as && + s->action.large_community.ld1 < + t->action.large_community.ld2 )) { + TAILQ_INSERT_BEFORE(t, s, entry); + return (0); + } + break; case ACTION_SET_EXT_COMMUNITY: case ACTION_DEL_EXT_COMMUNITY: if (memcmp(&s->action.ext_community, @@ -3634,6 +3747,7 @@ get_rule(enum action_types type) r->dir = out ? DIR_OUT : DIR_IN; r->action = ACTION_NONE; r->match.community.as = COMMUNITY_UNSET; + r->match.large_community.as = COMMUNITY_UNSET; TAILQ_INIT(&r->set); if (curpeer == curgroup) { /* group */ diff --git a/usr.sbin/bgpd/printconf.c b/usr.sbin/bgpd/printconf.c index a5f9d15443f..06a5e234d5e 100644 --- a/usr.sbin/bgpd/printconf.c +++ b/usr.sbin/bgpd/printconf.c @@ -1,7 +1,9 @@ -/* $OpenBSD: printconf.c,v 1.98 2016/10/05 07:38:06 phessler Exp $ */ +/* $OpenBSD: printconf.c,v 1.99 2016/10/14 16:05:36 phessler Exp $ */ /* * Copyright (c) 2003, 2004 Henning Brauer <henning@openbsd.org> + * Copyright (c) 2016 Job Snijders <job@instituut.net> + * Copyright (c) 2016 Peter Hessler <phessler@openbsd.org> * * Permission to use, copy, modify, and distribute this software for any * purpose with or without fee is hereby granted, provided that the above @@ -28,6 +30,7 @@ void print_op(enum comp_ops); void print_community(int, int); +void print_largecommunity(int64_t, int64_t, int64_t); void print_extcommunity(struct filter_extcommunity *); void print_origin(u_int8_t); void print_set(struct filter_set_head *); @@ -102,6 +105,33 @@ print_community(int as, int type) } void +print_largecommunity(int64_t as, int64_t ld1, int64_t ld2) +{ + if (as == COMMUNITY_ANY) + printf("*:"); + else if (as == COMMUNITY_NEIGHBOR_AS) + printf("neighbor-as:"); + else + printf("%lld:", as); + + if (ld1 == COMMUNITY_ANY) + printf("*:"); + else if (ld1 == COMMUNITY_NEIGHBOR_AS) + printf("neighbor-as:"); + else + printf("%lld:", ld1); + + if (ld2 == COMMUNITY_ANY) + printf("* "); + else if (ld2 == COMMUNITY_NEIGHBOR_AS) + printf("neighbor-as "); + else + printf("%lld ", ld2); + +} + + +void print_extcommunity(struct filter_extcommunity *c) { switch (c->type & EXT_COMMUNITY_VALUE) { @@ -202,6 +232,20 @@ print_set(struct filter_set_head *set) s->action.community.type); printf(" "); break; + case ACTION_DEL_LARGE_COMMUNITY: + printf("large-community delete "); + print_largecommunity(s->action.large_community.as, + s->action.large_community.ld1, + s->action.large_community.ld2); + printf(" "); + break; + case ACTION_SET_LARGE_COMMUNITY: + printf("large-community "); + print_largecommunity(s->action.large_community.as, + s->action.large_community.ld1, + s->action.large_community.ld2); + printf(" "); + break; case ACTION_PFTABLE: printf("pftable %s ", s->action.pftable); break; @@ -628,6 +672,12 @@ print_rule(struct peer *peer_l, struct filter_rule *r) printf("ext-community "); print_extcommunity(&r->match.ext_community); } + if (r->match.large_community.as != COMMUNITY_UNSET) { + printf("large-community "); + print_largecommunity(r->match.large_community.as, + r->match.large_community.ld1, + r->match.large_community.ld2); + } print_set(&r->set); diff --git a/usr.sbin/bgpd/rde.c b/usr.sbin/bgpd/rde.c index 53fd307816b..6b468104f41 100644 --- a/usr.sbin/bgpd/rde.c +++ b/usr.sbin/bgpd/rde.c @@ -1,7 +1,9 @@ -/* $OpenBSD: rde.c,v 1.350 2016/09/03 16:22:17 renato Exp $ */ +/* $OpenBSD: rde.c,v 1.351 2016/10/14 16:05:36 phessler Exp $ */ /* * Copyright (c) 2003, 2004 Henning Brauer <henning@openbsd.org> + * Copyright (c) 2016 Job Snijders <job@instituut.net> + * Copyright (c) 2016 Peter Hessler <phessler@openbsd.org> * * Permission to use, copy, modify, and distribute this software for any * purpose with or without fee is hereby granted, provided that the above @@ -559,6 +561,7 @@ badnet: case IMSG_CTL_SHOW_RIB: case IMSG_CTL_SHOW_RIB_AS: case IMSG_CTL_SHOW_RIB_COMMUNITY: + case IMSG_CTL_SHOW_RIB_LARGECOMMUNITY: case IMSG_CTL_SHOW_RIB_PREFIX: if (imsg.hdr.len != IMSG_HEADER_SIZE + sizeof(req)) { log_warnx("rde_dispatch: wrong imsg len"); @@ -1577,6 +1580,23 @@ bad_flags: ATTR_PARTIAL)) goto bad_flags; goto optattr; + case ATTR_LARGE_COMMUNITIES: + if (attr_len % 12 != 0) { + /* + * mark update as bad and withdraw all routes as per + * draft-ietf-idr-optional-transitive-00.txt + * but only if partial bit is set + */ + if ((flags & ATTR_PARTIAL) == 0) + goto bad_len; + a->flags |= F_ATTR_PARSE_ERR; + log_peer_warnx(&peer->conf, "bad LARGE COMMUNITIES, " + "path invalidated and prefix withdrawn"); + } + if (!CHECK_FLAGS(flags, ATTR_OPTIONAL|ATTR_TRANSITIVE, + ATTR_PARTIAL)) + goto bad_flags; + goto optattr; case ATTR_EXT_COMMUNITIES: if (attr_len % 8 != 0) { /* @@ -2266,6 +2286,10 @@ rde_dump_filter(struct prefix *p, struct ctl_show_rib_request *req) !community_match(p->aspath, req->community.as, req->community.type)) return; + if (req->type == IMSG_CTL_SHOW_RIB_LARGECOMMUNITY && + !community_large_match(p->aspath, req->large_community.as, + req->large_community.ld1, req->large_community.ld2)) + return; if ((req->flags & F_CTL_ACTIVE) && p->rib->active != p) return; rde_dump_rib_as(p, p->aspath, req->pid, req->flags); @@ -2348,6 +2372,7 @@ rde_dump_ctx_new(struct ctl_show_rib_request *req, pid_t pid, case IMSG_CTL_SHOW_RIB: case IMSG_CTL_SHOW_RIB_AS: case IMSG_CTL_SHOW_RIB_COMMUNITY: + case IMSG_CTL_SHOW_RIB_LARGECOMMUNITY: ctx->ribctx.ctx_upcall = rde_dump_upcall; break; case IMSG_CTL_SHOW_RIB_PREFIX: diff --git a/usr.sbin/bgpd/rde.h b/usr.sbin/bgpd/rde.h index 1ff890a1a0c..edb49c81eca 100644 --- a/usr.sbin/bgpd/rde.h +++ b/usr.sbin/bgpd/rde.h @@ -1,4 +1,4 @@ -/* $OpenBSD: rde.h,v 1.149 2015/11/06 16:23:26 phessler Exp $ */ +/* $OpenBSD: rde.h,v 1.150 2016/10/14 16:05:36 phessler Exp $ */ /* * Copyright (c) 2003, 2004 Claudio Jeker <claudio@openbsd.org> and @@ -112,7 +112,8 @@ enum attrtypes { ATTR_MP_UNREACH_NLRI=15, ATTR_EXT_COMMUNITIES=16, ATTR_AS4_PATH=17, - ATTR_AS4_AGGREGATOR=18 + ATTR_AS4_AGGREGATOR=18, + ATTR_LARGE_COMMUNITIES=30, }; /* attribute flags. 4 low order bits reserved */ @@ -367,6 +368,12 @@ int aspath_lenmatch(struct aspath *, enum aslen_spec, u_int); int community_match(struct rde_aspath *, int, int); int community_set(struct rde_aspath *, int, int); void community_delete(struct rde_aspath *, int, int); +int community_large_match(struct rde_aspath *, int64_t, int64_t, + int64_t); +int community_large_set(struct rde_aspath *, int64_t, int64_t, + int64_t); +void community_large_delete(struct rde_aspath *, int64_t, + int64_t, int64_t); int community_ext_match(struct rde_aspath *, struct filter_extcommunity *, u_int16_t); int community_ext_set(struct rde_aspath *, diff --git a/usr.sbin/bgpd/rde_attr.c b/usr.sbin/bgpd/rde_attr.c index 5aa4f02bb45..c629f9d34df 100644 --- a/usr.sbin/bgpd/rde_attr.c +++ b/usr.sbin/bgpd/rde_attr.c @@ -1,7 +1,9 @@ -/* $OpenBSD: rde_attr.c,v 1.95 2015/10/24 08:00:42 claudio Exp $ */ +/* $OpenBSD: rde_attr.c,v 1.96 2016/10/14 16:05:36 phessler Exp $ */ /* * Copyright (c) 2004 Claudio Jeker <claudio@openbsd.org> + * Copyright (c) 2016 Job Snijders <job@instituut.net> + * Copyright (c) 2016 Peter Hessler <phessler@openbsd.org> * * Permission to use, copy, modify, and distribute this software for any * purpose with or without fee is hereby granted, provided that the above @@ -1352,3 +1354,147 @@ community_ext_matchone(struct filter_extcommunity *c, u_int16_t neighas, return (0); } + +int +community_large_match(struct rde_aspath *asp, int64_t as, int64_t ld1, + int64_t ld2) +{ + struct wire_largecommunity *bar; + struct attr *a; + u_int8_t *p; + u_int16_t len; + u_int32_t eas, eld1, eld2; + + a = attr_optget(asp, ATTR_LARGE_COMMUNITIES); + if (a == NULL) + /* no communities, no match */ + return (0); + + p = a->data; + for (len = a->len / 12; len > 0; len--) { + bar = (struct wire_largecommunity *)p; + p += 12; + eas = betoh32(bar->as); + eld1 = betoh32(bar->ld1); + eld2 = betoh32(bar->ld2); + + if ((as == COMMUNITY_ANY || as == eas) && + (ld1 == COMMUNITY_ANY || ld1 == eld1) && + (ld2 == COMMUNITY_ANY || ld2 == eld2)) + return (1); + } + return (0); +} + +int +community_large_set(struct rde_aspath *asp, int64_t as, int64_t ld1, + int64_t ld2) +{ + struct wire_largecommunity *bar; + struct attr *attr; + u_int8_t *p = NULL; + unsigned int i, ncommunities = 0; + u_int8_t f = ATTR_OPTIONAL|ATTR_TRANSITIVE; + + attr = attr_optget(asp, ATTR_LARGE_COMMUNITIES); + if (attr != NULL) { + p = attr->data; + ncommunities = attr->len / 12; + } + + /* first check if the community is not already set */ + for (i = 0; i < ncommunities; i++) { + bar = (struct wire_largecommunity *)p; + if (bar->as == as && bar->ld1 == ld1 && bar->ld2 == ld2) + /* already present, nothing todo */ + return (1); + p += 12; + } + + if (ncommunities++ >= USHRT_MAX / 12) + /* overflow */ + return (0); + + if ((p = reallocarray(NULL, ncommunities, 12)) == NULL) + fatal("community_set"); + + bar = (struct wire_largecommunity *)p; + bar->as = htobe32(as); + bar->ld1 = htobe32(ld1); + bar->ld2 = htobe32(ld2); + + if (attr != NULL) { + memcpy(p + 12, attr->data, attr->len); + f = attr->flags; + attr_free(asp, attr); + } + + attr_optadd(asp, f, ATTR_LARGE_COMMUNITIES, p, ncommunities * 12); + + free(p); + return (1); +} + +void +community_large_delete(struct rde_aspath *asp, int64_t as, int64_t ld1, + int64_t ld2) +{ + struct wire_largecommunity *bar; + struct attr *attr; + u_int8_t *p, *n; + u_int16_t l = 0, len = 0; + u_int32_t eas, eld1, eld2; + u_int8_t f; + + attr = attr_optget(asp, ATTR_LARGE_COMMUNITIES); + if (attr == NULL) + /* no attr nothing to do */ + return; + + p = attr->data; + for (len = 0; l < attr->len; l += 12) { + bar = (struct wire_largecommunity *)p; + p += 12; + eas = betoh32(bar->as); + eld1 = betoh32(bar->ld1); + eld2 = betoh32(bar->ld2); + + if ((as == COMMUNITY_ANY || as == eas) && + (ld1 == COMMUNITY_ANY || ld1 == eld1) && + (ld2 == COMMUNITY_ANY || ld2 == eld2)) + /* match */ + continue; + len += 12; + } + + if (len == 0) { + attr_free(asp, attr); + return; + } + + if ((n = malloc(len)) == NULL) + fatal("community_delete"); + + p = attr->data; + for (l = 0; l < len && p < attr->data + attr->len; ) { + bar = (struct wire_largecommunity *)p; + p += 12; + eas = betoh32(bar->as); + eld1 = betoh32(bar->ld1); + eld2 = betoh32(bar->ld2); + + if ((as == COMMUNITY_ANY || as == eas) && + (ld1 == COMMUNITY_ANY || ld1 == eld1) && + (ld2 == COMMUNITY_ANY || ld2 == eld2)) + /* match */ + continue; + memcpy(n + l, bar, sizeof(*bar)); + l += 12; + } + + f = attr->flags; + + attr_free(asp, attr); + attr_optadd(asp, f, ATTR_LARGE_COMMUNITIES, n, len); + free(n); +} diff --git a/usr.sbin/bgpd/rde_filter.c b/usr.sbin/bgpd/rde_filter.c index 83a45d5a591..bd6be859d3c 100644 --- a/usr.sbin/bgpd/rde_filter.c +++ b/usr.sbin/bgpd/rde_filter.c @@ -1,7 +1,9 @@ -/* $OpenBSD: rde_filter.c,v 1.77 2016/06/03 17:36:37 benno Exp $ */ +/* $OpenBSD: rde_filter.c,v 1.78 2016/10/14 16:05:36 phessler Exp $ */ /* * Copyright (c) 2004 Claudio Jeker <claudio@openbsd.org> + * Copyright (c) 2016 Job Snijders <job@instituut.net> + * Copyright (c) 2016 Peter Hessler <phessler@openbsd.org> * * Permission to use, copy, modify, and distribute this software for any * purpose with or without fee is hereby granted, provided that the above @@ -36,6 +38,7 @@ rde_apply_set(struct rde_aspath *asp, struct filter_set_head *sh, struct filter_set *set; u_char *np; int as, type; + int64_t las, ld1, ld2; u_int32_t prep_as; u_int16_t nl; u_int8_t prepend; @@ -181,6 +184,84 @@ rde_apply_set(struct rde_aspath *asp, struct filter_set_head *sh, community_delete(asp, as, type); break; + case ACTION_SET_LARGE_COMMUNITY: + switch (set->action.large_community.as) { + case COMMUNITY_ERROR: + fatalx("rde_apply_set bad large community string"); + case COMMUNITY_NEIGHBOR_AS: + las = peer->conf.remote_as; + break; + case COMMUNITY_ANY: + default: + las = set->action.large_community.as; + break; + } + + switch (set->action.large_community.ld1) { + case COMMUNITY_ERROR: + fatalx("rde_apply_set bad large community string"); + case COMMUNITY_NEIGHBOR_AS: + ld1 = peer->conf.remote_as; + break; + case COMMUNITY_ANY: + default: + ld1 = set->action.large_community.ld1; + break; + } + + switch (set->action.large_community.ld2) { + case COMMUNITY_ERROR: + fatalx("rde_apply_set bad large community string"); + case COMMUNITY_NEIGHBOR_AS: + ld2 = peer->conf.remote_as; + break; + case COMMUNITY_ANY: + default: + ld2 = set->action.large_community.ld2; + break; + } + + community_large_set(asp, las, ld1, ld2); + break; + case ACTION_DEL_LARGE_COMMUNITY: + switch (set->action.large_community.as) { + case COMMUNITY_ERROR: + fatalx("rde_apply_set bad large community string"); + case COMMUNITY_NEIGHBOR_AS: + las = peer->conf.remote_as; + break; + case COMMUNITY_ANY: + default: + las = set->action.large_community.as; + break; + } + + switch (set->action.large_community.ld1) { + case COMMUNITY_ERROR: + fatalx("rde_apply_set bad large community string"); + case COMMUNITY_NEIGHBOR_AS: + ld1 = peer->conf.remote_as; + break; + case COMMUNITY_ANY: + default: + ld1 = set->action.large_community.ld1; + break; + } + + switch (set->action.large_community.ld2) { + case COMMUNITY_ERROR: + fatalx("rde_apply_set bad large community string"); + case COMMUNITY_NEIGHBOR_AS: + ld2 = peer->conf.remote_as; + break; + case COMMUNITY_ANY: + default: + ld2 = set->action.large_community.ld2; + break; + } + + community_large_delete(asp, las, ld1, ld2); + break; case ACTION_PFTABLE: /* convert pftable name to an id */ set->action.id = pftable_name2id(set->action.pftable); @@ -223,6 +304,7 @@ rde_filter_match(struct filter_rule *f, struct rde_aspath *asp, { u_int32_t pas; int cas, type; + int64_t las, ld1, ld2; if (asp != NULL && f->match.as.type != AS_NONE) { if (f->match.as.flags & AS_FLAG_NEIGHBORAS) @@ -270,6 +352,44 @@ rde_filter_match(struct filter_rule *f, struct rde_aspath *asp, if (community_ext_match(asp, &f->match.ext_community, peer->conf.remote_as) == 0) return (0); + if (asp != NULL && f->match.large_community.as != + COMMUNITY_UNSET) { + switch (f->match.large_community.as) { + case COMMUNITY_ERROR: + fatalx("rde_apply_set bad community string"); + case COMMUNITY_NEIGHBOR_AS: + las = peer->conf.remote_as; + break; + default: + las = f->match.large_community.as; + break; + } + + switch (f->match.large_community.ld1) { + case COMMUNITY_ERROR: + fatalx("rde_apply_set bad community string"); + case COMMUNITY_NEIGHBOR_AS: + ld1 = peer->conf.remote_as; + break; + default: + ld1 = f->match.large_community.ld1; + break; + } + + switch (f->match.large_community.ld2) { + case COMMUNITY_ERROR: + fatalx("rde_apply_set bad community string"); + case COMMUNITY_NEIGHBOR_AS: + ld2 = peer->conf.remote_as; + break; + default: + ld2 = f->match.large_community.ld2; + break; + } + + if (community_large_match(asp, las, ld1, ld2) == 0) + return (0); + } if (f->match.prefix.addr.aid != 0) { if (f->match.prefix.addr.aid != prefix->aid) @@ -547,6 +667,14 @@ filterset_equal(struct filter_set_head *ah, struct filter_set_head *bh) sizeof(a->action.community)) == 0) continue; break; + case ACTION_DEL_LARGE_COMMUNITY: + case ACTION_SET_LARGE_COMMUNITY: + if (a->type == b->type && + memcmp(&a->action.large_community, + &b->action.large_community, + sizeof(a->action.large_community)) == 0) + continue; + break; case ACTION_PFTABLE: case ACTION_PFTABLE_ID: if (b->type == ACTION_PFTABLE) @@ -630,6 +758,10 @@ filterset_name(enum action_types type) return ("community"); case ACTION_DEL_COMMUNITY: return ("community delete"); + case ACTION_SET_LARGE_COMMUNITY: + return ("large-community"); + case ACTION_DEL_LARGE_COMMUNITY: + return ("large-community delete"); case ACTION_PFTABLE: case ACTION_PFTABLE_ID: return ("pftable"); |