This diff implements as-set. Similar to prefix-set this allows to reduce
large lists of AS numbers to a single set.

To define an as-set:
as-set "AS_SET_AS_PCH" {
        27 42 187 297 715 3856 10886 11893 13202 16327 16668 19281 20539 21312 
        21556 24999 25505 27678 32978 32979 35160 38052 42530 44876 45170 45494 
        48892 50843 51874 51972 52234 52304 52306 54145 54390 60313 197058 
}

To match against an as-set:
match from any source-as as-set "AS_SET_AS_PCH" set localpref 1000

OK?
-- 
:wq Claudio


Index: Makefile
===================================================================
RCS file: /cvs/src/usr.sbin/bgpd/Makefile,v
retrieving revision 1.32
diff -u -p -r1.32 Makefile
--- Makefile    21 Aug 2017 14:43:33 -0000      1.32
+++ Makefile    9 Aug 2018 20:35:53 -0000
@@ -4,7 +4,7 @@ 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: bgpd.c
===================================================================
RCS file: /cvs/src/usr.sbin/bgpd/bgpd.c,v
retrieving revision 1.194
diff -u -p -r1.194 bgpd.c
--- bgpd.c      14 Jul 2018 12:32:35 -0000      1.194
+++ bgpd.c      29 Aug 2018 16:25:43 -0000
@@ -519,6 +519,12 @@ reconfigure(char *conffile, struct bgpd_
                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: bgpd.conf.5
===================================================================
RCS file: /cvs/src/usr.sbin/bgpd/bgpd.conf.5,v
retrieving revision 1.171
diff -u -p -r1.171 bgpd.conf.5
--- bgpd.conf.5 11 Jul 2018 14:08:46 -0000      1.171
+++ bgpd.conf.5 5 Sep 2018 18:12:07 -0000
@@ -144,6 +144,20 @@ or as a large number (ASPLAIN format), f
 AS 196618
 .Ed
 .Pp
+.Pp
+.It Xo
+.Ic as-set Ar name
+.Ic { Ar as-number ... Ic }
+.Xc
+A
+.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 on
 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
@@ -1146,6 +1164,10 @@ It may be set to
 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
Index: bgpd.h
===================================================================
RCS file: /cvs/src/usr.sbin/bgpd/bgpd.h,v
retrieving revision 1.333
diff -u -p -r1.333 bgpd.h
--- bgpd.h      5 Sep 2018 09:49:57 -0000       1.333
+++ bgpd.h      6 Sep 2018 13:31:46 -0000
@@ -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, prefixse
 
 struct prefixset {
        int                              sflags;
-       char                             name[PREFIXSET_NAME_LEN];
+       char                             name[SET_NAME_LEN];
        struct prefixset_items_h         psitems;
        SIMPLEQ_ENTRY(prefixset)         entry;
 };
@@ -1144,6 +1155,21 @@ int               filterset_cmp(struct filter_set *,
 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 *);
Index: config.c
===================================================================
RCS file: /cvs/src/usr.sbin/bgpd/config.c,v
retrieving revision 1.71
diff -u -p -r1.71 config.c
--- config.c    4 Sep 2018 10:48:39 -0000       1.71
+++ config.c    4 Sep 2018 12:26:31 -0000
@@ -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 *p
                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 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, 
 
        /* 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, 
 
        /* 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: parse.y
===================================================================
RCS file: /cvs/src/usr.sbin/bgpd/parse.y,v
retrieving revision 1.333
diff -u -p -r1.333 parse.y
--- parse.y     5 Sep 2018 17:32:43 -0000       1.333
+++ parse.y     5 Sep 2018 17:33:09 -0000
@@ -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},
@@ -4098,4 +4138,43 @@ 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: printconf.c
===================================================================
RCS file: /cvs/src/usr.sbin/bgpd/printconf.c,v
retrieving revision 1.111
diff -u -p -r1.111 printconf.c
--- printconf.c 5 Sep 2018 17:32:43 -0000       1.111
+++ printconf.c 6 Sep 2018 13:36:46 -0000
@@ -606,6 +606,10 @@ print_announce(struct peer_config *p, co
 
 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, s
        }
        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: rde.c
===================================================================
RCS file: /cvs/src/usr.sbin/bgpd/rde.c,v
retrieving revision 1.418
diff -u -p -r1.418 rde.c
--- rde.c       5 Sep 2018 09:49:57 -0000       1.418
+++ rde.c       5 Sep 2018 10:43:26 -0000
@@ -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 
 {
        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 
        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 
                        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 
                                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 
                                        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 
                                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: rde.h
===================================================================
RCS file: /cvs/src/usr.sbin/bgpd/rde.h,v
retrieving revision 1.186
diff -u -p -r1.186 rde.h
--- rde.h       8 Aug 2018 13:08:54 -0000       1.186
+++ rde.h       29 Aug 2018 16:39:53 -0000
@@ -412,7 +412,7 @@ enum filter_actions rde_filter(struct fi
 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: rde_filter.c
===================================================================
RCS file: /cvs/src/usr.sbin/bgpd/rde_filter.c,v
retrieving revision 1.101
diff -u -p -r1.101 rde_filter.c
--- rde_filter.c        5 Sep 2018 17:32:43 -0000       1.101
+++ rde_filter.c        5 Sep 2018 17:33:09 -0000
@@ -571,10 +571,11 @@ rde_filter_skip_rule(struct rde_peer *pe
 
 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, 
                /* 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) &&
@@ -615,6 +621,13 @@ rde_filter_equal(struct filter_head *a, 
                    & PREFIXSET_FLAG_DIRTY) != 0)) {
                        log_debug("%s: prefixset %s has changed",
                            __func__, fa->match.prefixset.name);
+                       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);
                }
 
Index: rde_sets.c
===================================================================
RCS file: rde_sets.c
diff -N rde_sets.c
--- /dev/null   1 Jan 1970 00:00:00 -0000
+++ rde_sets.c  29 Aug 2018 19:15:36 -0000
@@ -0,0 +1,241 @@
+/*     $OpenBSD$ */
+
+/*
+ * Copyright (c) 2018 Claudio Jeker <[email protected]>
+ *
+ * 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) {
+                               printf("\n\t");
+                               len = 0;
+                       }
+                       len += printf("%s%u", len > 0 ? " " : "", 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: util.c
===================================================================
RCS file: /cvs/src/usr.sbin/bgpd/util.c,v
retrieving revision 1.33
diff -u -p -r1.33 util.c
--- util.c      5 Sep 2018 09:49:57 -0000       1.33
+++ util.c      5 Sep 2018 10:45:18 -0000
@@ -317,6 +317,11 @@ as_compare(struct filter_as *f, u_int32_
 {
        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 *u
 
        return (bbuf);
 }
-

Reply via email to