Implement as-set a fast lookup table to be used instead of long list of
authorclaudio <claudio@openbsd.org>
Fri, 7 Sep 2018 05:43:33 +0000 (05:43 +0000)
committerclaudio <claudio@openbsd.org>
Fri, 7 Sep 2018 05:43:33 +0000 (05:43 +0000)
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@

12 files changed:
usr.sbin/bgpd/Makefile
usr.sbin/bgpd/bgpd.c
usr.sbin/bgpd/bgpd.conf.5
usr.sbin/bgpd/bgpd.h
usr.sbin/bgpd/config.c
usr.sbin/bgpd/parse.y
usr.sbin/bgpd/printconf.c
usr.sbin/bgpd/rde.c
usr.sbin/bgpd/rde.h
usr.sbin/bgpd/rde_filter.c
usr.sbin/bgpd/rde_sets.c [new file with mode: 0644]
usr.sbin/bgpd/util.c

index a2a5721..b57e539 100644 (file)
@@ -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
index 12a4e6a..894b312 100644 (file)
@@ -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 <henning@openbsd.org>
@@ -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);
index 9c06290..106760c 100644 (file)
@@ -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 <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: 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
index 56f9ffa..91c9c8c 100644 (file)
@@ -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 <henning@openbsd.org>
@@ -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 *);
index 73fa2b4..bf4752b 100644 (file)
@@ -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 <henning@openbsd.org>
@@ -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:
index 7194e3f..3554d6c 100644 (file)
@@ -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 <henning@openbsd.org>
@@ -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;
+}
index 0690679..c116a3c 100644 (file)
@@ -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 <henning@openbsd.org>
@@ -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);
index 6b14078..f7a077f 100644 (file)
@@ -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 <henning@openbsd.org>
@@ -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,
index 6049bda..f32c8b0 100644 (file)
@@ -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 <claudio@openbsd.org> 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 */
index a314181..3633e94 100644 (file)
@@ -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 <claudio@openbsd.org>
@@ -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 (file)
index 0000000..1d9e744
--- /dev/null
@@ -0,0 +1,239 @@
+/*     $OpenBSD: rde_sets.c,v 1.1 2018/09/07 05:43:33 claudio Exp $ */
+
+/*
+ * Copyright (c) 2018 Claudio Jeker <claudio@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
+ * 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 <sys/types.h>
+#include <sys/queue.h>
+
+#include <assert.h>
+#include <errno.h>
+#include <limits.h>
+#include <stdlib.h>
+#include <stdio.h>
+#include <string.h>
+
+#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);
+}
index 3fb491a..e28e3c5 100644 (file)
@@ -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 <claudio@openbsd.org>
@@ -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);
 }
-