From a8e18e829f73b0bebf25f606781beae6d4a49407 Mon Sep 17 00:00:00 2001 From: claudio Date: Fri, 7 Sep 2018 05:43:33 +0000 Subject: [PATCH] Implement as-set a fast lookup table to be used instead of long list of AS numbers in source-as, AS and transit-as filterstatements. These table use bsearch to quickly verify if an AS is in the set or not. The filter syntax is not fully set in stone yet. OK denis@ benno@ and previously OK deraadt@ --- usr.sbin/bgpd/Makefile | 4 +- usr.sbin/bgpd/bgpd.c | 8 +- usr.sbin/bgpd/bgpd.conf.5 | 34 +++++- usr.sbin/bgpd/bgpd.h | 52 ++++++-- usr.sbin/bgpd/config.c | 26 ++-- usr.sbin/bgpd/parse.y | 89 +++++++++++++- usr.sbin/bgpd/printconf.c | 8 +- usr.sbin/bgpd/rde.c | 60 +++++++++- usr.sbin/bgpd/rde.h | 4 +- usr.sbin/bgpd/rde_filter.c | 17 ++- usr.sbin/bgpd/rde_sets.c | 239 +++++++++++++++++++++++++++++++++++++ usr.sbin/bgpd/util.c | 8 +- 12 files changed, 497 insertions(+), 52 deletions(-) create mode 100644 usr.sbin/bgpd/rde_sets.c diff --git a/usr.sbin/bgpd/Makefile b/usr.sbin/bgpd/Makefile index a2a5721d7fd..b57e53914f9 100644 --- a/usr.sbin/bgpd/Makefile +++ b/usr.sbin/bgpd/Makefile @@ -1,10 +1,10 @@ -# $OpenBSD: Makefile,v 1.32 2017/08/21 14:43:33 phessler Exp $ +# $OpenBSD: Makefile,v 1.33 2018/09/07 05:43:33 claudio Exp $ PROG= bgpd SRCS= bgpd.c session.c log.c logmsg.c parse.y config.c \ rde.c rde_rib.c rde_decide.c rde_prefix.c mrt.c kroute.c \ control.c pfkey.c rde_update.c rde_attr.c printconf.c \ - rde_filter.c pftable.c name2id.c util.c carp.c timer.c + rde_filter.c rde_sets.c pftable.c name2id.c util.c carp.c timer.c CFLAGS+= -Wall -I${.CURDIR} CFLAGS+= -Wstrict-prototypes -Wmissing-prototypes CFLAGS+= -Wmissing-declarations diff --git a/usr.sbin/bgpd/bgpd.c b/usr.sbin/bgpd/bgpd.c index 12a4e6a359c..894b3127052 100644 --- a/usr.sbin/bgpd/bgpd.c +++ b/usr.sbin/bgpd/bgpd.c @@ -1,4 +1,4 @@ -/* $OpenBSD: bgpd.c,v 1.194 2018/07/14 12:32:35 benno Exp $ */ +/* $OpenBSD: bgpd.c,v 1.195 2018/09/07 05:43:33 claudio Exp $ */ /* * Copyright (c) 2003, 2004 Henning Brauer @@ -519,6 +519,12 @@ reconfigure(char *conffile, struct bgpd_config *conf, struct peer **peer_l) free(ps); } + /* as-sets for filters in the RDE */ + if (as_sets_send(ibuf_rde, conf->as_sets) == -1) + return (-1); + as_sets_free(conf->as_sets); + conf->as_sets = NULL; + /* filters for the RDE */ while ((r = TAILQ_FIRST(conf->filters)) != NULL) { TAILQ_REMOVE(conf->filters, r, entry); diff --git a/usr.sbin/bgpd/bgpd.conf.5 b/usr.sbin/bgpd/bgpd.conf.5 index 9c0629004f1..106760ce568 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.171 2018/07/11 14:08:46 benno Exp $ +.\" $OpenBSD: bgpd.conf.5,v 1.172 2018/09/07 05:43: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: July 11 2018 $ +.Dd $Mdocdate: September 7 2018 $ .Dt BGPD.CONF 5 .Os .Sh NAME @@ -144,6 +144,20 @@ or as a large number (ASPLAIN format), for example: AS 196618 .Ed .Pp +.Pp +.It Xo +.Ic as-set Ar name +.Ic { Ar as-number ... Ic } +.Xc +An +.Ic as-set +holds a collection of AS numbers and can be used with the AS specific +parameter in +.Sx FILTER +rules. +Lookups against as-sets are more efficient than a large number of rules +which differ only in the AS number. +.Pp .It Ic connect-retry Ar seconds Set the number of seconds before retrying to open a connection. This timer should be sufficiently large in EBGP configurations. @@ -1111,21 +1125,25 @@ If a parameter is specified, the rule only applies to packets with matching attributes. .Pp .Bl -tag -width Ds -compact -.It Xo +.It Xo .Ar as-type Op Ar operator .Ar as-number .Xc +.It Ar as-type Ic as-set Ar name This rule applies only to .Em UPDATES where the .Em AS path matches. The -.Ar as-number -is matched against a part of the +part of the .Em AS path specified by the -.Ar as-type : +.Ar as-type +is matched against the +.Ar as-number +or the +.Ic as-set Ar name : .Pp .Bl -tag -width transmit-as -compact .It Ic AS @@ -1147,6 +1165,10 @@ which is expanded to the current neighbor remote AS number, or .Ic local-as , which is expanded to the locally assigned AS number. .Pp +When specifying an +.Ic as-set Ar name +the AS path will instead be matched against all the AS numbers in the set. +.Pp The .Ar operator can be unspecified (this case is identical to the equality operator), or one of the numerical operators diff --git a/usr.sbin/bgpd/bgpd.h b/usr.sbin/bgpd/bgpd.h index 56f9ffad1aa..91c9c8c2233 100644 --- a/usr.sbin/bgpd/bgpd.h +++ b/usr.sbin/bgpd/bgpd.h @@ -1,4 +1,4 @@ -/* $OpenBSD: bgpd.h,v 1.333 2018/09/05 09:49:57 claudio Exp $ */ +/* $OpenBSD: bgpd.h,v 1.334 2018/09/07 05:43:33 claudio Exp $ */ /* * Copyright (c) 2003, 2004 Henning Brauer @@ -43,7 +43,7 @@ #define TCP_MD5_KEY_LEN 80 #define IPSEC_ENC_KEY_LEN 32 #define IPSEC_AUTH_KEY_LEN 20 -#define PREFIXSET_NAME_LEN 32 +#define SET_NAME_LEN 64 #define MAX_PKTSIZE 4096 #define MIN_HOLDTIME 3 @@ -213,6 +213,9 @@ TAILQ_HEAD(network_head, network); struct prefixset; SIMPLEQ_HEAD(prefixset_head, prefixset); +struct as_set; +SIMPLEQ_HEAD(as_set_head, as_set); + struct filter_rule; TAILQ_HEAD(filter_head, filter_rule); @@ -223,6 +226,7 @@ struct bgpd_config { struct listen_addrs *listen_addrs; struct mrt_head *mrt; struct prefixset_head *prefixsets; + struct as_set_head *as_sets; char *csock; char *rcsock; int flags; @@ -404,8 +408,6 @@ enum imsg_type { IMSG_NETWORK_FLUSH, IMSG_NETWORK_DONE, IMSG_FILTER_SET, - IMSG_RECONF_PREFIXSET, - IMSG_RECONF_PREFIXSETITEM, IMSG_SOCKET_CONN, IMSG_SOCKET_CONN_CTL, IMSG_RECONF_CONF, @@ -418,6 +420,11 @@ enum imsg_type { IMSG_RECONF_RDOMAIN_EXPORT, IMSG_RECONF_RDOMAIN_IMPORT, IMSG_RECONF_RDOMAIN_DONE, + IMSG_RECONF_PREFIXSET, + IMSG_RECONF_PREFIXSETITEM, + IMSG_RECONF_AS_SET, + IMSG_RECONF_AS_SET_ITEMS, + IMSG_RECONF_AS_SET_DONE, IMSG_RECONF_DONE, IMSG_UPDATE, IMSG_UPDATE_ERR, @@ -648,12 +655,18 @@ enum aslen_spec { ASLEN_SEQ }; +#define AS_FLAG_NEIGHBORAS 0x01 +#define AS_FLAG_AS_SET_NAME 0x02 +#define AS_FLAG_AS_SET 0x04 + struct filter_as { - u_int16_t flags; - enum as_spec type; - u_int8_t op; - u_int32_t as_min; - u_int32_t as_max; + char name[SET_NAME_LEN]; + struct as_set *aset; + u_int32_t as_min; + u_int32_t as_max; + enum as_spec type; + u_int8_t flags; + u_int8_t op; }; struct filter_aslen { @@ -666,12 +679,10 @@ struct filter_aslen { struct filter_prefixset { int flags; - char name[PREFIXSET_NAME_LEN]; + char name[SET_NAME_LEN]; struct prefixset *ps; }; -#define AS_FLAG_NEIGHBORAS 0x01 - struct filter_community { int as; int type; @@ -942,7 +953,7 @@ SIMPLEQ_HEAD(prefixset_items_h, prefixset_item); struct prefixset { int sflags; - char name[PREFIXSET_NAME_LEN]; + char name[SET_NAME_LEN]; struct prefixset_items_h psitems; SIMPLEQ_ENTRY(prefixset) entry; }; @@ -1145,6 +1156,21 @@ void filterset_move(struct filter_set_head *, struct filter_set_head *); const char *filterset_name(enum action_types); +/* rde_sets.c */ +void as_sets_insert(struct as_set_head *, struct as_set *); +struct as_set *as_sets_lookup(struct as_set_head *, const char *); +void as_sets_free(struct as_set_head *); +void print_as_sets(struct as_set_head *); +int as_sets_send(struct imsgbuf *, struct as_set_head *); +void as_sets_mark_dirty(struct as_set_head *, struct as_set_head *); + +struct as_set *as_set_new(const char *, size_t); +int as_set_add(struct as_set *, u_int32_t *, size_t); +void as_set_prep(struct as_set *); +int as_set_match(const struct as_set *, u_int32_t); +int as_set_equal(const struct as_set *, const struct as_set *); +int as_set_dirty(const struct as_set *); + /* util.c */ const char *log_addr(const struct bgpd_addr *); const char *log_in6addr(const struct in6_addr *); diff --git a/usr.sbin/bgpd/config.c b/usr.sbin/bgpd/config.c index 73fa2b4b3bd..bf4752b7ba4 100644 --- a/usr.sbin/bgpd/config.c +++ b/usr.sbin/bgpd/config.c @@ -1,4 +1,4 @@ -/* $OpenBSD: config.c,v 1.71 2018/09/04 10:48:39 claudio Exp $ */ +/* $OpenBSD: config.c,v 1.72 2018/09/07 05:43:33 claudio Exp $ */ /* * Copyright (c) 2003, 2004, 2005 Henning Brauer @@ -67,6 +67,8 @@ new_config(void) if ((conf->prefixsets = calloc(1, sizeof(struct prefixset_head))) == NULL) fatal(NULL); + if ((conf->as_sets = calloc(1, sizeof(struct as_set_head))) == NULL) + fatal(NULL); if ((conf->filters = calloc(1, sizeof(struct filter_head))) == NULL) fatal(NULL); if ((conf->listen_addrs = calloc(1, sizeof(struct listen_addrs))) == @@ -79,6 +81,7 @@ new_config(void) TAILQ_INIT(&conf->networks); SIMPLEQ_INIT(&conf->rdomains); SIMPLEQ_INIT(conf->prefixsets); + SIMPLEQ_INIT(conf->as_sets); TAILQ_INIT(conf->filters); TAILQ_INIT(conf->listen_addrs); @@ -132,6 +135,7 @@ free_prefixsets(struct prefixset_head *psh) SIMPLEQ_REMOVE_HEAD(psh, entry); free(ps); } + free(psh); } void @@ -144,6 +148,7 @@ free_config(struct bgpd_config *conf) free_networks(&conf->networks); filterlist_free(conf->filters); free_prefixsets(conf->prefixsets); + as_sets_free(conf->as_sets); while ((la = TAILQ_FIRST(conf->listen_addrs)) != NULL) { TAILQ_REMOVE(conf->listen_addrs, la, entry); @@ -169,8 +174,6 @@ merge_config(struct bgpd_config *xconf, struct bgpd_config *conf, { struct listen_addr *nla, *ola, *next; struct network *n; - struct rdomain *rd; - struct prefixset *ps; /* * merge the freshly parsed conf into the running xconf @@ -220,10 +223,14 @@ merge_config(struct bgpd_config *xconf, struct bgpd_config *conf, /* switch the prefixsets, first remove the old ones */ free_prefixsets(xconf->prefixsets); - while ((ps = SIMPLEQ_FIRST(conf->prefixsets)) != NULL) { - SIMPLEQ_REMOVE_HEAD(conf->prefixsets, entry); - SIMPLEQ_INSERT_TAIL(xconf->prefixsets, ps, entry); - } + xconf->prefixsets = conf->prefixsets; + conf->prefixsets = NULL; + + /* switch the as_sets, first remove the old ones */ + as_sets_free(xconf->as_sets); + xconf->as_sets = conf->as_sets; + conf->as_sets = NULL; + /* switch the network statements, but first remove the old ones */ free_networks(&xconf->networks); while ((n = TAILQ_FIRST(&conf->networks)) != NULL) { @@ -233,10 +240,7 @@ merge_config(struct bgpd_config *xconf, struct bgpd_config *conf, /* switch the rdomain configs, first remove the old ones */ free_rdomains(&xconf->rdomains); - while ((rd = SIMPLEQ_FIRST(&conf->rdomains)) != NULL) { - SIMPLEQ_REMOVE_HEAD(&conf->rdomains, entry); - SIMPLEQ_INSERT_TAIL(&xconf->rdomains, rd, entry); - } + SIMPLEQ_CONCAT(&xconf->rdomains, &conf->rdomains); /* * merge new listeners: diff --git a/usr.sbin/bgpd/parse.y b/usr.sbin/bgpd/parse.y index 7194e3f16e2..3554d6cada5 100644 --- a/usr.sbin/bgpd/parse.y +++ b/usr.sbin/bgpd/parse.y @@ -1,4 +1,4 @@ -/* $OpenBSD: parse.y,v 1.333 2018/09/05 17:32:43 claudio Exp $ */ +/* $OpenBSD: parse.y,v 1.334 2018/09/07 05:43:33 claudio Exp $ */ /* * Copyright (c) 2002, 2003, 2004 Henning Brauer @@ -160,6 +160,9 @@ int parsesubtype(char *, int *, int *); int parseextvalue(char *, u_int32_t *); int parseextcommunity(struct filter_extcommunity *, char *, char *); +int asset_new(char *); +void asset_add(u_int32_t); +void asset_done(void); typedef struct { union { @@ -207,9 +210,10 @@ typedef struct { %token QUICK %token FROM TO ANY %token CONNECTED STATIC -%token COMMUNITY EXTCOMMUNITY LARGECOMMUNITY -%token PREFIX PREFIXLEN PREFIXSET SOURCEAS TRANSITAS PEERAS DELETE MAXASLEN -%token MAXASSEQ SET LOCALPREF MED METRIC NEXTHOP REJECT BLACKHOLE NOMODIFY SELF +%token COMMUNITY EXTCOMMUNITY LARGECOMMUNITY DELETE +%token PREFIX PREFIXLEN PREFIXSET +%token ASSET SOURCEAS TRANSITAS PEERAS MAXASLEN MAXASSEQ +%token SET LOCALPREF MED METRIC NEXTHOP REJECT BLACKHOLE NOMODIFY SELF %token PREPEND_SELF PREPEND_PEER PFTABLE WEIGHT RTLABEL ORIGIN PRIORITY %token ERROR INCLUDE %token IPSEC ESP AH SPI IKE @@ -242,9 +246,10 @@ typedef struct { grammar : /* empty */ | grammar '\n' + | grammar varset '\n' | grammar include '\n' + | grammar asset '\n' | grammar conf_main '\n' - | grammar varset '\n' | grammar rdomain '\n' | grammar neighbor '\n' | grammar group '\n' @@ -396,6 +401,17 @@ include : INCLUDE STRING { } ; +asset : ASSET STRING '{' optnl { + if (asset_new($2) != 0) + YYERROR; + free($2); + } asset_l optnl '}' { + asset_done(); + } + +asset_l : as4number { asset_add($1); } + | asset_l optnl as4number { asset_add($3); } + conf_main : AS as4number { conf->as = $2; if ($2 > USHRT_MAX) @@ -1874,6 +1890,27 @@ filter_as_t : filter_as_type filter_as { for (a = $$; a != NULL; a = a->next) a->a.type = $1; } + | filter_as_type ASSET STRING { + if (as_sets_lookup(conf->as_sets, $3) == NULL) { + yyerror("as-set \"%s\" not defined", $3); + free($3); + YYERROR; + } + if (($$ = calloc(1, sizeof(struct filter_as_l))) == + NULL) + fatal(NULL); + $$->a.type = $1; + $$->a.flags = AS_FLAG_AS_SET_NAME; + if (strlcpy($$->a.name, $3, sizeof($$->a.name)) >= + sizeof($$->a.name)) { + yyerror("as-set name \"%s\" too long: " + "max %zu", $3, sizeof($$->a.name) - 1); + free($3); + free($$); + YYERROR; + } + free($3); + } ; filter_as_l_h : filter_as_l @@ -1903,6 +1940,7 @@ filter_as : as4number_any { NULL) fatal(NULL); $$->a.as_min = $1; + $$->a.as_max = $1; $$->a.op = OP_EQ; } | NEIGHBORAS { @@ -1917,6 +1955,7 @@ filter_as : as4number_any { fatal(NULL); $$->a.op = $1; $$->a.as_min = $2; + $$->a.as_max = $2; } | as4number_any binaryop as4number_any { if (($$ = calloc(1, sizeof(struct filter_as_l))) == @@ -2559,6 +2598,7 @@ lookup(char *s) { "announce", ANNOUNCE}, { "any", ANY}, { "as-4byte", AS4BYTE }, + { "as-set", ASSET }, { "blackhole", BLACKHOLE}, { "capabilities", CAPABILITIES}, { "community", COMMUNITY}, @@ -4099,3 +4139,42 @@ get_rule(enum action_types type) } return (r); } + +struct as_set *curasset; +int +asset_new(char *name) +{ + struct as_set *aset; + + if (curasset) + fatalx("%s: bad mojo jojo", __func__); + + if (as_sets_lookup(conf->as_sets, name) != NULL) { + yyerror("as-set \"%s\" already exists", name); + return -1; + } + + aset = as_set_new(name, 0); + if (aset == NULL) + fatal(NULL); + as_sets_insert(conf->as_sets, aset); + + curasset = aset; + return 0; +} + +void +asset_add(u_int32_t as) +{ + if (curasset == NULL) + fatalx("%s: bad mojo jojo", __func__); + + if (as_set_add(curasset, &as, 1) != 0) + fatal(NULL); +} + +void +asset_done(void) +{ + curasset = NULL; +} diff --git a/usr.sbin/bgpd/printconf.c b/usr.sbin/bgpd/printconf.c index 0690679cac2..c116a3c3eea 100644 --- a/usr.sbin/bgpd/printconf.c +++ b/usr.sbin/bgpd/printconf.c @@ -1,4 +1,4 @@ -/* $OpenBSD: printconf.c,v 1.111 2018/09/05 17:32:43 claudio Exp $ */ +/* $OpenBSD: printconf.c,v 1.112 2018/09/07 05:43:33 claudio Exp $ */ /* * Copyright (c) 2003, 2004 Henning Brauer @@ -606,6 +606,10 @@ print_announce(struct peer_config *p, const char *c) void print_as(struct filter_rule *r) { + if (r->match.as.flags & AS_FLAG_AS_SET_NAME) { + printf("as-set \"%s\" ", r->match.as.name); + return; + } switch(r->match.as.op) { case OP_RANGE: printf("%s - ", log_as(r->match.as.as_min)); @@ -862,7 +866,7 @@ print_config(struct bgpd_config *conf, struct rib_names *rib_l, } printf("\n"); print_prefixsets(conf->prefixsets); - printf("\n"); + print_as_sets(conf->as_sets); print_mrt(conf, 0, 0, "", ""); printf("\n"); print_groups(conf, peer_l); diff --git a/usr.sbin/bgpd/rde.c b/usr.sbin/bgpd/rde.c index 6b140783b9f..f7a077fb5fa 100644 --- a/usr.sbin/bgpd/rde.c +++ b/usr.sbin/bgpd/rde.c @@ -1,4 +1,4 @@ -/* $OpenBSD: rde.c,v 1.418 2018/09/05 09:49:57 claudio Exp $ */ +/* $OpenBSD: rde.c,v 1.419 2018/09/07 05:43:33 claudio Exp $ */ /* * Copyright (c) 2003, 2004 Henning Brauer @@ -129,6 +129,7 @@ time_t reloadtime; struct rde_peer_head peerlist; struct rde_peer *peerself; struct prefixset_head *prefixsets_tmp, *prefixsets_old; +struct as_set_head *as_sets_tmp, *as_sets_old; struct filter_head *out_rules, *out_rules_tmp; struct rdomain_head *rdomains_l, *newdomains; struct imsgbuf *ibuf_se; @@ -687,6 +688,7 @@ rde_dispatch_imsg_parent(struct imsgbuf *ibuf) { static struct rdomain *rd; static struct prefixset *last_prefixset; + static struct as_set *last_as_set; struct imsg imsg; struct mrt xmrt; struct rde_rib rn; @@ -697,6 +699,8 @@ rde_dispatch_imsg_parent(struct imsgbuf *ibuf) struct rib *rib; struct prefixset *ps; struct prefixset_item *psi; + char *name; + size_t nmemb; int n, fd; u_int16_t rid; @@ -769,6 +773,11 @@ rde_dispatch_imsg_parent(struct imsgbuf *ibuf) if (prefixsets_tmp == NULL) fatal(NULL); SIMPLEQ_INIT(prefixsets_tmp); + as_sets_tmp = calloc(1, + sizeof(struct as_set_head)); + if (as_sets_tmp == NULL) + fatal(NULL); + SIMPLEQ_INIT(as_sets_tmp); out_rules_tmp = calloc(1, sizeof(struct filter_head)); if (out_rules_tmp == NULL) fatal(NULL); @@ -822,8 +831,6 @@ rde_dispatch_imsg_parent(struct imsgbuf *ibuf) fatal(NULL); memcpy(r, imsg.data, sizeof(struct filter_rule)); if (r->match.prefixset.flags != 0) { - log_debug("%s: retrieving prefixset %s for " - "rule", __func__, r->match.prefixset.name); r->match.prefixset.ps = find_prefixset(r->match.prefixset.name, prefixsets_tmp); @@ -831,6 +838,19 @@ rde_dispatch_imsg_parent(struct imsgbuf *ibuf) log_warnx("%s: no prefixset for %s", __func__, r->match.prefixset.name); } + if (r->match.as.flags & AS_FLAG_AS_SET_NAME) { + struct as_set * aset; + + aset = as_sets_lookup(as_sets_tmp, + r->match.as.name); + if (aset == NULL) { + log_warnx("%s: no as-set for %s", + __func__, r->match.as.name); + } else { + r->match.as.flags = AS_FLAG_AS_SET; + r->match.as.aset = aset; + } + } TAILQ_INIT(&r->set); if ((rib = rib_find(r->rib)) == NULL) { log_warnx("IMSG_RECONF_FILTER: filter rule " @@ -879,6 +899,27 @@ rde_dispatch_imsg_parent(struct imsgbuf *ibuf) fatalx("King Bula has no prefixset"); SIMPLEQ_INSERT_TAIL(&last_prefixset->psitems, psi, entry); break; + case IMSG_RECONF_AS_SET: + if (imsg.hdr.len - IMSG_HEADER_SIZE != + sizeof(nmemb) + SET_NAME_LEN) + fatalx("IMSG_RECONF_AS_SET bad len"); + memcpy(&nmemb, imsg.data, sizeof(nmemb)); + name = (char *)imsg.data + sizeof(nmemb); + if (as_sets_lookup(as_sets_tmp, name) != NULL) + fatalx("duplicate as-set %s", name); + last_as_set = as_set_new(name, nmemb); + break; + case IMSG_RECONF_AS_SET_ITEMS: + nmemb = imsg.hdr.len - IMSG_HEADER_SIZE; + nmemb /= sizeof(u_int32_t); + if (as_set_add(last_as_set, imsg.data, nmemb) != 0) + fatal(NULL); + break; + case IMSG_RECONF_AS_SET_DONE: + as_set_prep(last_as_set); + as_sets_insert(as_sets_tmp, last_as_set); + last_as_set = NULL; + break; case IMSG_RECONF_RDOMAIN: if (imsg.hdr.len - IMSG_HEADER_SIZE != sizeof(struct rdomain)) @@ -2753,6 +2794,7 @@ rde_reload_done(void) } prefixsets_old = conf->prefixsets; + as_sets_old = conf->as_sets; memcpy(conf, nconf, sizeof(struct bgpd_config)); conf->listen_addrs = NULL; @@ -2782,9 +2824,14 @@ rde_reload_done(void) /* XXX WHERE IS THE SYNC ??? */ rde_mark_prefixsets_dirty(prefixsets_old, prefixsets_tmp); + as_sets_mark_dirty(as_sets_old, as_sets_tmp); + /* swap the prefixsets */ conf->prefixsets = prefixsets_tmp; prefixsets_tmp = NULL; + /* and the as_sets */ + conf->as_sets = as_sets_tmp; + as_sets_tmp = NULL; /* * make the new filter rules the active one but keep the old for @@ -2812,8 +2859,7 @@ rde_reload_done(void) peer->reconf_rib = 1; continue; } - if (!rde_filter_equal(out_rules, out_rules_tmp, peer, - conf->prefixsets)) { + if (!rde_filter_equal(out_rules, out_rules_tmp, peer)) { char *p = log_fmt_peer(&peer->conf); log_debug("out filter change: reloading peer %s", p); free(p); @@ -2837,7 +2883,7 @@ rde_reload_done(void) break; case RECONF_KEEP: if (rde_filter_equal(ribs[rid].in_rules, - ribs[rid].in_rules_tmp, NULL, conf->prefixsets)) + ribs[rid].in_rules_tmp, NULL)) /* rib is in sync */ break; log_debug("in filter change: reloading RIB %s", @@ -2974,6 +3020,8 @@ rde_softreconfig_done(void) free_prefixsets(prefixsets_old); prefixsets_old = NULL; + as_sets_free(as_sets_old); + as_sets_old = NULL; log_info("RDE soft reconfiguration done"); imsg_compose(ibuf_main, IMSG_RECONF_DONE, 0, 0, diff --git a/usr.sbin/bgpd/rde.h b/usr.sbin/bgpd/rde.h index 6049bdaa60f..f32c8b034ed 100644 --- a/usr.sbin/bgpd/rde.h +++ b/usr.sbin/bgpd/rde.h @@ -1,4 +1,4 @@ -/* $OpenBSD: rde.h,v 1.186 2018/08/08 13:08:54 claudio Exp $ */ +/* $OpenBSD: rde.h,v 1.187 2018/09/07 05:43:33 claudio Exp $ */ /* * Copyright (c) 2003, 2004 Claudio Jeker and @@ -412,7 +412,7 @@ enum filter_actions rde_filter(struct filter_head *, struct rde_peer *, void rde_apply_set(struct filter_set_head *, struct filterstate *, u_int8_t, struct rde_peer *, struct rde_peer *); int rde_filter_equal(struct filter_head *, struct filter_head *, - struct rde_peer *, struct prefixset_head *); + struct rde_peer *); void rde_filter_calc_skip_steps(struct filter_head *); /* rde_prefix.c */ diff --git a/usr.sbin/bgpd/rde_filter.c b/usr.sbin/bgpd/rde_filter.c index a314181ff26..3633e94b3ac 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.101 2018/09/05 17:32:43 claudio Exp $ */ +/* $OpenBSD: rde_filter.c,v 1.102 2018/09/07 05:43:33 claudio Exp $ */ /* * Copyright (c) 2004 Claudio Jeker @@ -571,10 +571,11 @@ rde_filter_skip_rule(struct rde_peer *peer, struct filter_rule *f) int rde_filter_equal(struct filter_head *a, struct filter_head *b, - struct rde_peer *peer, struct prefixset_head *psh) + struct rde_peer *peer) { struct filter_rule *fa, *fb; struct prefixset *psa, *psb; + struct as_set *asa, *asb; fa = a ? TAILQ_FIRST(a) : NULL; fb = b ? TAILQ_FIRST(b) : NULL; @@ -603,11 +604,16 @@ rde_filter_equal(struct filter_head *a, struct filter_head *b, /* compare filter_rule.match without the prefixset pointer */ psa = fa->match.prefixset.ps; psb = fb->match.prefixset.ps; + asa = fa->match.as.aset; + asb = fb->match.as.aset; fa->match.prefixset.ps = fb->match.prefixset.ps = NULL; + fa->match.as.aset = fb->match.as.aset = NULL; if (memcmp(&fa->match, &fb->match, sizeof(fa->match))) return (0); fa->match.prefixset.ps = psa; fb->match.prefixset.ps = psb; + fa->match.as.aset = asa; + fb->match.as.aset = asb; if ((fa->match.prefixset.flags != 0) && (fa->match.prefixset.ps != NULL) && @@ -618,6 +624,13 @@ rde_filter_equal(struct filter_head *a, struct filter_head *b, return (0); } + if ((fa->match.as.flags & AS_FLAG_AS_SET) && + as_set_dirty(fa->match.as.aset)) { + log_debug("%s: as-set %s has changed", + __func__, fa->match.as.name); + return (0); + } + if (!filterset_equal(&fa->set, &fb->set)) return (0); diff --git a/usr.sbin/bgpd/rde_sets.c b/usr.sbin/bgpd/rde_sets.c new file mode 100644 index 00000000000..1d9e744ce50 --- /dev/null +++ b/usr.sbin/bgpd/rde_sets.c @@ -0,0 +1,239 @@ +/* $OpenBSD: rde_sets.c,v 1.1 2018/09/07 05:43:33 claudio Exp $ */ + +/* + * Copyright (c) 2018 Claudio Jeker + * + * Permission to use, copy, modify, and distribute this software for any + * purpose with or without fee is hereby granted, provided that the above + * copyright notice and this permission notice appear in all copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES + * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR + * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES + * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN + * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF + * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + */ +#include +#include + +#include +#include +#include +#include +#include +#include + +#include "rde.h" + +struct as_set { + char name[SET_NAME_LEN]; + u_int32_t *set; + SIMPLEQ_ENTRY(as_set) entry; + size_t nmemb; + size_t max; + int dirty; +}; + +void +as_sets_insert(struct as_set_head *as_sets, struct as_set *aset) +{ + SIMPLEQ_INSERT_TAIL(as_sets, aset, entry); +} + +struct as_set * +as_sets_lookup(struct as_set_head *as_sets, const char *name) +{ + struct as_set *aset; + + SIMPLEQ_FOREACH(aset, as_sets, entry) { + if (strcmp(aset->name, name) == 0) + return aset; + } + return NULL; +} + +static void +as_set_free(struct as_set *aset) +{ + free(aset->set); + free(aset); +} + +void +as_sets_free(struct as_set_head *as_sets) +{ + struct as_set *aset; + + if (as_sets == NULL) + return; + while (!SIMPLEQ_EMPTY(as_sets)) { + aset = SIMPLEQ_FIRST(as_sets); + SIMPLEQ_REMOVE_HEAD(as_sets, entry); + as_set_free(aset); + } + free(as_sets); +} + +void +print_as_sets(struct as_set_head *as_sets) +{ + struct as_set *aset; + size_t i; + int len = 0;; + + if (as_sets == NULL) + return; + SIMPLEQ_FOREACH(aset, as_sets, entry) { + printf("as-set \"%s\" {", aset->name); + for (i = 0; i < aset->nmemb; i++) { + if (len == 0 || len > 72) + len = printf("\n\t"); + len += printf("%u ", aset->set[i]); + } + printf("\n}\n\n"); + } +} + +int +as_sets_send(struct imsgbuf *ibuf, struct as_set_head *as_sets) +{ + struct as_set *aset; + struct ibuf *wbuf; + size_t i, l; + + if (as_sets == NULL) + return 0; + SIMPLEQ_FOREACH(aset, as_sets, entry) { + if ((wbuf = imsg_create(ibuf, IMSG_RECONF_AS_SET, 0, 0, + sizeof(aset->nmemb) + sizeof(aset->name))) == NULL) + return -1; + if (imsg_add(wbuf, &aset->nmemb, sizeof(aset->nmemb)) == -1 || + imsg_add(wbuf, aset->name, sizeof(aset->name)) == -1) + return -1; + imsg_close(ibuf, wbuf); + + for (i = 0; i < aset->nmemb; i += l) { + l = (aset->nmemb - i > 1024 ? 1024 : aset->nmemb - i); + + if (imsg_compose(ibuf, IMSG_RECONF_AS_SET_ITEMS, 0, 0, + -1, aset->set + i, l * sizeof(*aset->set)) == -1) + return -1; + } + + if (imsg_compose(ibuf, IMSG_RECONF_AS_SET_DONE, 0, 0, -1, + NULL, 0) == -1) + return -1; + } + return 0; +} + +void +as_sets_mark_dirty(struct as_set_head *old, struct as_set_head *new) +{ + struct as_set *n, *o; + + SIMPLEQ_FOREACH(n, new, entry) { + if (old == NULL || (o = as_sets_lookup(old, n->name)) == NULL || + !as_set_equal(n, o)) + n->dirty = 1; + } +} + +struct as_set * +as_set_new(const char *name, size_t nmemb) +{ + struct as_set *aset; + size_t len; + + aset = calloc(1, sizeof(*aset)); + if (aset == NULL) + return NULL; + + len = strlcpy(aset->name, name, sizeof(aset->name)); + assert(len < sizeof(aset->name)); + + if (nmemb == 0) + nmemb = 16; + + aset->max = nmemb; + aset->set = calloc(nmemb, sizeof(*aset->set)); + if (aset->set == NULL) { + free(aset); + return NULL; + } + + return aset; +} + +int +as_set_add(struct as_set *aset, u_int32_t *elms, size_t nelms) +{ + if (aset->max < nelms || aset->max - nelms < aset->nmemb) { + u_int32_t *s; + size_t new_size; + + if (aset->nmemb >= SIZE_T_MAX - 4096 - nelms) { + errno = ENOMEM; + return -1; + } + for (new_size = aset->max; new_size < aset->nmemb + nelms; ) + new_size += (new_size < 4096 ? new_size : 4096); + + s = reallocarray(aset->set, new_size, sizeof(*aset->set)); + if (s == NULL) + return -1; + aset->set = s; + aset->max = new_size; + } + + memcpy(aset->set + aset->nmemb, elms, nelms * sizeof(*elms)); + aset->nmemb += nelms; + + return 0; +} + +static int +as_set_cmp(const void *ap, const void *bp) +{ + const u_int32_t *a = ap; + const u_int32_t *b = bp; + + if (*a > *b) + return 1; + else if (*a < *b) + return -1; + return 0; +} + +void +as_set_prep(struct as_set *aset) +{ + qsort(aset->set, aset->nmemb, sizeof(*aset->set), as_set_cmp); +} + +int +as_set_match(const struct as_set *a, u_int32_t asnum) +{ + if (bsearch(&asnum, a->set, a->nmemb, sizeof(asnum), as_set_cmp)) + return 1; + else + return 0; +} + +int +as_set_equal(const struct as_set *a, const struct as_set *b) +{ + if (a->nmemb != b->nmemb) + return 0; + if (memcmp(a->set, b->set, a->nmemb * sizeof(*a->set)) != 0) + return 0; + return 1; +} + +int +as_set_dirty(const struct as_set *a) +{ + return (a->dirty); +} diff --git a/usr.sbin/bgpd/util.c b/usr.sbin/bgpd/util.c index 3fb491a2f70..e28e3c5d28a 100644 --- a/usr.sbin/bgpd/util.c +++ b/usr.sbin/bgpd/util.c @@ -1,4 +1,4 @@ -/* $OpenBSD: util.c,v 1.33 2018/09/05 09:49:57 claudio Exp $ */ +/* $OpenBSD: util.c,v 1.34 2018/09/07 05:43:33 claudio Exp $ */ /* * Copyright (c) 2006 Claudio Jeker @@ -317,6 +317,11 @@ as_compare(struct filter_as *f, u_int32_t as, u_int32_t neighas) { u_int32_t match; + if (f->flags & AS_FLAG_AS_SET_NAME) /* should not happen */ + return (0); + if (f->flags & AS_FLAG_AS_SET) + return (as_set_match(f->aset, as)); + if (f->flags & AS_FLAG_NEIGHBORAS) match = neighas; else @@ -955,4 +960,3 @@ get_baudrate(u_int64_t baudrate, char *unit) return (bbuf); } - -- 2.20.1