Move the struct peer into bgpd_config and switch it to a TAILQ instead of
the hand-rolled list. This changes the way peers are reloaded since now
both parent and session engine are now merging the lists.

If you are using neighbor templates you should really test this diff since
it may fix problems when switching from a templated peer to a fully
confiured one. All other users are also invited to test this :)

-- 
:wq Claudio

Index: bgpd.c
===================================================================
RCS file: /cvs/src/usr.sbin/bgpd/bgpd.c,v
retrieving revision 1.213
diff -u -p -r1.213 bgpd.c
--- bgpd.c      7 Mar 2019 07:42:36 -0000       1.213
+++ bgpd.c      14 Mar 2019 11:11:57 -0000
@@ -43,7 +43,7 @@ __dead void   usage(void);
 int            main(int, char *[]);
 pid_t          start_child(enum bgpd_process, char *, int, int, int);
 int            send_filterset(struct imsgbuf *, struct filter_set_head *);
-int            reconfigure(char *, struct bgpd_config *, struct peer **);
+int            reconfigure(char *, struct bgpd_config *);
 int            dispatch_imsg(struct imsgbuf *, int, struct bgpd_config *);
 int            control_setup(struct bgpd_config *);
 int            imsg_send_sockets(struct imsgbuf *, struct imsgbuf *);
@@ -100,7 +100,6 @@ int
 main(int argc, char *argv[])
 {
        struct bgpd_config      *conf;
-       struct peer             *peer_l, *p;
        struct rde_rib          *rr;
        struct pollfd            pfd[POLL_MAX];
        time_t                   timeout;
@@ -125,8 +124,6 @@ main(int argc, char *argv[])
        if (saved_argv0 == NULL)
                saved_argv0 = "bgpd";
 
-       peer_l = NULL;
-
        while ((ch = getopt(argc, argv, "cdD:f:nRSv")) != -1) {
                switch (ch) {
                case 'c':
@@ -169,20 +166,14 @@ main(int argc, char *argv[])
                usage();
 
        if (cmd_opts & BGPD_OPT_NOACTION) {
-               conf = new_config();
-               if (parse_config(conffile, conf, &peer_l))
+               if ((conf = parse_config(conffile, NULL)) == NULL)
                        exit(1);
 
                if (cmd_opts & BGPD_OPT_VERBOSE)
-                       print_config(conf, &ribnames, &conf->networks, peer_l,
-                           conf->filters, conf->mrt, &conf->l3vpns);
+                       print_config(conf, &ribnames);
                else
                        fprintf(stderr, "configuration OK\n");
 
-               while ((p = peer_l) != NULL) {
-                       peer_l = p->next;
-                       free(p);
-               }
                while ((rr = SIMPLEQ_FIRST(&ribnames)) != NULL) {
                        SIMPLEQ_REMOVE_HEAD(&ribnames, entry);
                        free(rr);
@@ -261,7 +252,7 @@ BROKEN      if (pledge("stdio rpath wpath cpa
        if (imsg_send_sockets(ibuf_se, ibuf_rde))
                fatal("could not establish imsg links");
        conf = new_config();
-       quit = reconfigure(conffile, conf, &peer_l);
+       quit = reconfigure(conffile, conf);
        if (pftable_clear_all() != 0)
                quit = 1;
 
@@ -317,7 +308,7 @@ BROKEN      if (pledge("stdio rpath wpath cpa
                        u_int   error;
 
                        reconfig = 0;
-                       switch (reconfigure(conffile, conf, &peer_l)) {
+                       switch (reconfigure(conffile, conf)) {
                        case -1:        /* fatal error */
                                quit = 1;
                                break;
@@ -358,10 +349,6 @@ BROKEN     if (pledge("stdio rpath wpath cpa
                ibuf_rde = NULL;
        }
 
-       while ((p = peer_l) != NULL) {
-               peer_l = p->next;
-               free(p);
-       }
        while ((rr = SIMPLEQ_FIRST(&ribnames)) != NULL) {
                SIMPLEQ_REMOVE_HEAD(&ribnames, entry);
                free(rr);
@@ -451,8 +438,9 @@ send_filterset(struct imsgbuf *i, struct
 }
 
 int
-reconfigure(char *conffile, struct bgpd_config *conf, struct peer **peer_l)
+reconfigure(char *conffile, struct bgpd_config *conf)
 {
+       struct bgpd_config      *new_conf;
        struct peer             *p;
        struct filter_rule      *r;
        struct listen_addr      *la;
@@ -469,12 +457,13 @@ reconfigure(char *conffile, struct bgpd_
        reconfpending = 2;      /* one per child */
 
        log_info("rereading config");
-       if (parse_config(conffile, conf, peer_l)) {
+       if ((new_conf = parse_config(conffile, &conf->peers)) == NULL) {
                log_warnx("config file %s has errors, not reloading",
                    conffile);
                reconfpending = 0;
                return (1);
        }
+       merge_config(conf, new_conf);
 
        if (prepare_listeners(conf) == -1) {
                reconfpending = 0;
@@ -524,7 +513,7 @@ reconfigure(char *conffile, struct bgpd_
        }
 
        /* send peer list to the SE */
-       for (p = *peer_l; p != NULL; p = p->next) {
+       TAILQ_FOREACH(p, &conf->peers, entry) {
                if (imsg_compose(ibuf_se, IMSG_RECONF_PEER, p->conf.id, 0, -1,
                    &p->conf, sizeof(struct peer_config)) == -1)
                        return (-1);
Index: bgpd.h
===================================================================
RCS file: /cvs/src/usr.sbin/bgpd/bgpd.h,v
retrieving revision 1.377
diff -u -p -r1.377 bgpd.h
--- bgpd.h      7 Mar 2019 07:42:36 -0000       1.377
+++ bgpd.h      14 Mar 2019 10:40:33 -0000
@@ -231,6 +231,9 @@ struct listen_addr {
 TAILQ_HEAD(listen_addrs, listen_addr);
 TAILQ_HEAD(filter_set_head, filter_set);
 
+struct peer;
+TAILQ_HEAD(peer_head, peer);
+
 struct l3vpn;
 SIMPLEQ_HEAD(l3vpn_head, l3vpn);
 
@@ -267,6 +270,7 @@ struct filter_rule;
 TAILQ_HEAD(filter_head, filter_rule);
 
 struct bgpd_config {
+       struct peer_head                         peers;
        struct l3vpn_head                        l3vpns;
        struct network_head                      networks;
        struct filter_head                      *filters;
@@ -376,7 +380,6 @@ struct peer_config {
        enum export_type         export_type;
        enum enforce_as          enforce_as;
        enum enforce_as          enforce_local_as;
-       enum reconf_action       reconf_action;
        u_int16_t                max_prefix_restart;
        u_int16_t                holdtime;
        u_int16_t                min_holdtime;
Index: config.c
===================================================================
RCS file: /cvs/src/usr.sbin/bgpd/config.c,v
retrieving revision 1.86
diff -u -p -r1.86 config.c
--- config.c    15 Mar 2019 09:54:54 -0000      1.86
+++ config.c    15 Mar 2019 10:47:02 -0000
@@ -54,6 +54,7 @@ new_config(void)
                fatal(NULL);
 
        /* init the various list for later */
+       TAILQ_INIT(&conf->peers);
        TAILQ_INIT(&conf->networks);
        SIMPLEQ_INIT(&conf->l3vpns);
        SIMPLEQ_INIT(&conf->prefixsets);
@@ -156,6 +157,7 @@ free_prefixtree(struct prefixset_tree *p
 void
 free_config(struct bgpd_config *conf)
 {
+       struct peer             *p;
        struct listen_addr      *la;
        struct mrt              *m;
 
@@ -181,6 +183,11 @@ free_config(struct bgpd_config *conf)
        }
        free(conf->mrt);
 
+       while ((p = TAILQ_FIRST(&conf->peers)) != NULL) {
+               TAILQ_REMOVE(&conf->peers, p, entry);
+               free(p);
+       }
+
        free(conf->csock);
        free(conf->rcsock);
 
@@ -188,11 +195,11 @@ free_config(struct bgpd_config *conf)
 }
 
 void
-merge_config(struct bgpd_config *xconf, struct bgpd_config *conf,
-    struct peer *peer_l)
+merge_config(struct bgpd_config *xconf, struct bgpd_config *conf)
 {
        struct listen_addr      *nla, *ola, *next;
        struct network          *n;
+       struct peer             *p, *np;
 
        /*
         * merge the freshly parsed conf into the running xconf
@@ -298,6 +305,26 @@ merge_config(struct bgpd_config *xconf, 
                        free(nla);
                }
        }
+
+       /*
+        * merge peers:
+        * - need to know which peers are new, replaced and removed
+        * - first mark all new peers as RECONF_REINIT
+        * - walk over old peers and check if there is a corresponding new
+        *   peer if so mark it RECONF_KEEP. Remove all old peers.
+        * - swap lists (old peer list is actually empty).
+        */
+       TAILQ_FOREACH(p, &conf->peers, entry)
+               p->reconf_action = RECONF_REINIT;
+       while ((p = TAILQ_FIRST(&xconf->peers)) != NULL) {
+               np = getpeerbyid(conf, p->conf.id);
+               if (np != NULL)
+                       np->reconf_action = RECONF_KEEP;
+
+               TAILQ_REMOVE(&xconf->peers, p, entry);
+               free(p);
+       }
+       TAILQ_CONCAT(&xconf->peers, &conf->peers, entry);
 
        /* conf is merged so free it */
        free_config(conf);
Index: control.c
===================================================================
RCS file: /cvs/src/usr.sbin/bgpd/control.c,v
retrieving revision 1.95
diff -u -p -r1.95 control.c
--- control.c   12 Feb 2019 13:30:39 -0000      1.95
+++ control.c   14 Mar 2019 10:42:22 -0000
@@ -220,7 +220,8 @@ control_close(int fd)
 }
 
 int
-control_dispatch_msg(struct pollfd *pfd, u_int *ctl_cnt)
+control_dispatch_msg(struct pollfd *pfd, u_int *ctl_cnt,
+    struct peer_head *peers)
 {
        struct imsg              imsg;
        struct ctl_conn         *c;
@@ -294,7 +295,7 @@ control_dispatch_msg(struct pollfd *pfd,
                            0, NULL, 0);
                        break;
                case IMSG_CTL_SHOW_TERSE:
-                       for (p = peers; p != NULL; p = p->next)
+                       TAILQ_FOREACH(p, peers, entry)
                                imsg_compose(&c->ibuf, IMSG_CTL_SHOW_NEIGHBOR,
                                    0, 0, -1, p, sizeof(struct peer));
                        imsg_compose(&c->ibuf, IMSG_CTL_END, 0, 0, -1, NULL, 0);
@@ -309,7 +310,8 @@ control_dispatch_msg(struct pollfd *pfd,
                        } else {
                                neighbor = NULL;
                        }
-                       for (matched = 0, p = peers; p != NULL; p = p->next) {
+                       matched = 0;
+                       TAILQ_FOREACH(p, peers, entry) {
                                if (!peer_matched(p, neighbor))
                                        continue;
 
@@ -337,7 +339,7 @@ control_dispatch_msg(struct pollfd *pfd,
                                        }
                                }
                        }
-                       if (!matched && peers != NULL) {
+                       if (!matched && TAILQ_EMPTY(peers)) {
                                control_result(c, CTL_RES_NOSUCHPEER);
                        } else if (!neighbor || !neighbor->show_timers) {
                                imsg_ctl_rde(IMSG_CTL_END, imsg.hdr.pid,
@@ -362,7 +364,8 @@ control_dispatch_msg(struct pollfd *pfd,
                        neighbor = imsg.data;
                        neighbor->descr[PEER_DESCR_LEN - 1] = 0;
 
-                       for (matched = 0, p = peers; p != NULL; p = p->next) {
+                       matched = 0;
+                       TAILQ_FOREACH(p, peers, entry) {
                                if (!peer_matched(p, neighbor))
                                        continue;
 
@@ -417,7 +420,7 @@ control_dispatch_msg(struct pollfd *pfd,
                                                 * Mark as deleted, will be
                                                 * collected on next poll loop.
                                                 */
-                                               p->conf.reconf_action =
+                                               p->reconf_action =
                                                    RECONF_DELETE;
                                                control_result(c, CTL_RES_OK);
                                        }
@@ -458,10 +461,10 @@ control_dispatch_msg(struct pollfd *pfd,
                        neighbor->descr[PEER_DESCR_LEN - 1] = 0;
 
                        /* check if at least one neighbor exists */
-                       for (p = peers; p != NULL; p = p->next)
+                       TAILQ_FOREACH(p, peers, entry)
                                if (peer_matched(p, neighbor))
                                        break;
-                       if (p == NULL && peers != NULL) {
+                       if (p == NULL && TAILQ_EMPTY(peers)) {
                                control_result(c, CTL_RES_NOSUCHPEER);
                                break;
                        }
Index: parse.y
===================================================================
RCS file: /cvs/src/usr.sbin/bgpd/parse.y,v
retrieving revision 1.384
diff -u -p -r1.384 parse.y
--- parse.y     15 Mar 2019 09:54:54 -0000      1.384
+++ parse.y     15 Mar 2019 10:47:45 -0000
@@ -88,7 +88,7 @@ char          *symget(const char *);
 
 static struct bgpd_config      *conf;
 static struct network_head     *netconf;
-static struct peer             *peer_l, *peer_l_old;
+static struct peer_head                *new_peers, *cur_peers;
 static struct peer             *curpeer;
 static struct peer             *curgroup;
 static struct l3vpn            *curvpn;
@@ -99,7 +99,6 @@ static struct filter_head     *peerfilter_l;
 static struct filter_head      *groupfilter_l;
 static struct filter_rule      *curpeer_filter[2];
 static struct filter_rule      *curgroup_filter[2];
-static u_int32_t                id;
 
 struct filter_rib_l {
        struct filter_rib_l     *next;
@@ -1203,8 +1202,7 @@ neighbor  : {     curpeer = new_peer(); }
 
                        if (neighbor_consistent(curpeer) == -1)
                                YYERROR;
-                       curpeer->next = peer_l;
-                       peer_l = curpeer;
+                       TAILQ_INSERT_TAIL(new_peers, curpeer, entry);
                        curpeer = curgroup;
                }
                ;
@@ -1812,7 +1810,7 @@ filter_peer       : ANY           {
                                fatal(NULL);
                        $$->p.remote_as = $$->p.groupid = $$->p.peerid = 0;
                        $$->next = NULL;
-                       for (p = peer_l; p != NULL; p = p->next)
+                       TAILQ_FOREACH(p, new_peers, entry)
                                if (!memcmp(&p->conf.remote_addr,
                                    &$1, sizeof(p->conf.remote_addr))) {
                                        $$->p.peerid = p->conf.id;
@@ -1839,7 +1837,7 @@ filter_peer       : ANY           {
                                fatal(NULL);
                        $$->p.remote_as = $$->p.peerid = 0;
                        $$->next = NULL;
-                       for (p = peer_l; p != NULL; p = p->next)
+                       TAILQ_FOREACH(p, new_peers, entry)
                                if (!strcmp(p->conf.group, $2)) {
                                        $$->p.groupid = p->conf.groupid;
                                        break;
@@ -3263,11 +3261,10 @@ init_config(struct bgpd_config *c)
                fatal(NULL);
 }
 
-int
-parse_config(char *filename, struct bgpd_config *xconf, struct peer **xpeers)
+struct bgpd_config *
+parse_config(char *filename, struct peer_head *ph)
 {
        struct sym              *sym, *next;
-       struct peer             *p, *pnext;
        struct rde_rib          *rr;
        struct network          *n;
        int                      errors = 0;
@@ -3285,12 +3282,11 @@ parse_config(char *filename, struct bgpd
        TAILQ_INIT(peerfilter_l);
        TAILQ_INIT(groupfilter_l);
 
-       peer_l = NULL;
-       peer_l_old = *xpeers;
        curpeer = NULL;
        curgroup = NULL;
-       id = 1;
 
+       cur_peers = ph;
+       new_peers = &conf->peers;
        netconf = &conf->networks;
 
        add_rib("Adj-RIB-In", conf->default_tableid,
@@ -3334,13 +3330,15 @@ parse_config(char *filename, struct bgpd
                errors++;
        }
 
+       /* clear the globals */
+       curpeer = NULL;
+       curgroup = NULL;
+       cur_peers = NULL;
+       new_peers = NULL;
+       netconf = NULL;
+
        if (errors) {
 errors:
-               for (p = peer_l; p != NULL; p = pnext) {
-                       pnext = p->next;
-                       free(p);
-               }
-
                while ((rr = SIMPLEQ_FIRST(&ribnames)) != NULL) {
                        SIMPLEQ_REMOVE_HEAD(&ribnames, entry);
                        free(rr);
@@ -3351,7 +3349,7 @@ errors:
                filterlist_free(groupfilter_l);
 
                free_config(conf);
-               return -1;
+               return (NULL);
        } else {
                /*
                 * Concatenate filter list and static group and peer filtersets
@@ -3364,18 +3362,11 @@ errors:
 
                optimize_filters(conf->filters);
 
-               merge_config(xconf, conf, peer_l);
-               *xpeers = peer_l;
-
-               for (p = peer_l_old; p != NULL; p = pnext) {
-                       pnext = p->next;
-                       free(p);
-               }
-
                free(filter_l);
                free(peerfilter_l);
                free(groupfilter_l);
-               return 0;
+
+               return (conf);
        }
 }
 
@@ -3784,7 +3775,6 @@ alloc_peer(void)
 
        /* some sane defaults */
        p->state = STATE_NONE;
-       p->next = NULL;
        p->conf.distance = 1;
        p->conf.export_type = EXPORT_UNSET;
        p->conf.announce_capa = 1;
@@ -3796,6 +3786,9 @@ alloc_peer(void)
        p->conf.local_as = conf->as;
        p->conf.local_short_as = conf->short_as;
 
+       if (conf->flags & BGPD_FLAG_DECISION_TRANS_AS)
+               p->conf.flags |= PEERFLAG_TRANS_AS;
+
        return (p);
 }
 
@@ -3818,9 +3811,6 @@ new_peer(void)
                p->conf.local_as = curgroup->conf.local_as;
                p->conf.local_short_as = curgroup->conf.local_short_as;
        }
-       p->next = NULL;
-       if (conf->flags & BGPD_FLAG_DECISION_TRANS_AS)
-               p->conf.flags |= PEERFLAG_TRANS_AS;
        return (p);
 }
 
@@ -3962,32 +3952,39 @@ find_prefixset(char *name, struct prefix
 int
 get_id(struct peer *newpeer)
 {
-       struct peer     *p;
+       static u_int32_t id = 1;
+       struct peer     *p = NULL;
 
-       for (p = peer_l_old; p != NULL; p = p->next)
-               if (newpeer->conf.remote_addr.aid) {
-                       if (!memcmp(&p->conf.remote_addr,
-                           &newpeer->conf.remote_addr,
-                           sizeof(p->conf.remote_addr))) {
-                               newpeer->conf.id = p->conf.id;
-                               return (0);
-                       }
-               } else {        /* newpeer is a group */
-                       if (strcmp(newpeer->conf.group, p->conf.group) == 0) {
-                               newpeer->conf.id = p->conf.groupid;
-                               return (0);
-                       }
+       /* check if the peer already existed before */
+       if (newpeer->conf.remote_addr.aid) {
+               /* neighbor */
+               if (cur_peers)
+                       TAILQ_FOREACH(p, cur_peers, entry)
+                               if (memcmp(&p->conf.remote_addr,
+                                   &newpeer->conf.remote_addr,
+                                   sizeof(p->conf.remote_addr)) == 0)
+                                       break;
+               if (p) {
+                       newpeer->conf.id = p->conf.id;
+                       return (0);
                }
-
-       /* new one */
-       for (; id < UINT_MAX / 2; id++) {
-               for (p = peer_l_old; p != NULL &&
-                   p->conf.id != id && p->conf.groupid != id; p = p->next)
-                       ;       /* nothing */
-               if (p == NULL) {        /* we found a free id */
-                       newpeer->conf.id = id++;
+       } else {
+               /* group */
+               if (cur_peers)
+                       TAILQ_FOREACH(p, cur_peers, entry)
+                               if (strcmp(p->conf.group,
+                                   newpeer->conf.group) == 0)
+                                       break;
+               if (p) {
+                       newpeer->conf.id = p->conf.groupid;
                        return (0);
                }
+       }
+
+       /* else new one */
+       if (id < UINT_MAX / 2) {
+               newpeer->conf.id = id++;
+               return (0);
        }
 
        return (-1);
Index: printconf.c
===================================================================
RCS file: /cvs/src/usr.sbin/bgpd/printconf.c,v
retrieving revision 1.133
diff -u -p -r1.133 printconf.c
--- printconf.c 15 Mar 2019 09:54:54 -0000      1.133
+++ printconf.c 15 Mar 2019 10:47:02 -0000
@@ -49,11 +49,11 @@ const char  *print_auth_alg(u_int8_t);
 const char     *print_enc_alg(u_int8_t);
 void            print_announce(struct peer_config *, const char *);
 void            print_as(struct filter_rule *);
-void            print_rule(struct peer *, struct filter_rule *);
-const char     *mrt_type(enum mrt_type);
+void            print_rule(struct bgpd_config *, struct filter_rule *);
+const char *    mrt_type(enum mrt_type);
 void            print_mrt(struct bgpd_config *, u_int32_t, u_int32_t,
                    const char *, const char *);
-void            print_groups(struct bgpd_config *, struct peer *);
+void            print_groups(struct bgpd_config *);
 int             peer_compare(const void *, const void *);
 
 void
@@ -759,7 +759,7 @@ print_as(struct filter_rule *r)
 }
 
 void
-print_rule(struct peer *peer_l, struct filter_rule *r)
+print_rule(struct bgpd_config *conf, struct filter_rule *r)
 {
        struct peer *p;
        int i;
@@ -784,17 +784,17 @@ print_rule(struct peer *peer_l, struct f
                printf("eeeeeeeps. ");
 
        if (r->peer.peerid) {
-               for (p = peer_l; p != NULL && p->conf.id != r->peer.peerid;
-                   p = p->next)
-                       ;       /* nothing */
+               TAILQ_FOREACH(p, &conf->peers, entry)
+                       if (p->conf.id == r->peer.peerid)
+                               break;
                if (p == NULL)
                        printf("? ");
                else
                        printf("%s ", log_addr(&p->conf.remote_addr));
        } else if (r->peer.groupid) {
-               for (p = peer_l; p != NULL &&
-                   p->conf.groupid != r->peer.groupid; p = p->next)
-                       ;       /* nothing */
+               TAILQ_FOREACH(p, &conf->peers, entry)
+                       if (p->conf.groupid == r->peer.groupid)
+                               break;
                if (p == NULL)
                        printf("group ? ");
                else
@@ -928,7 +928,7 @@ print_mrt(struct bgpd_config *conf, u_in
 }
 
 void
-print_groups(struct bgpd_config *conf, struct peer *peer_l)
+print_groups(struct bgpd_config *conf)
 {
        struct peer_config      **peerlist;
        struct peer              *p;
@@ -939,14 +939,14 @@ print_groups(struct bgpd_config *conf, s
        const char               *c;
 
        peer_cnt = 0;
-       for (p = peer_l; p != NULL; p = p->next)
+       TAILQ_FOREACH(p, &conf->peers, entry)
                peer_cnt++;
 
        if ((peerlist = calloc(peer_cnt, sizeof(struct peer_config *))) == NULL)
                fatal("print_groups calloc");
 
        i = 0;
-       for (p = peer_l; p != NULL; p = p->next)
+       TAILQ_FOREACH(p, &conf->peers, entry)
                peerlist[i++] = &p->conf;
 
        qsort(peerlist, peer_cnt, sizeof(struct peer_config *), peer_compare);
@@ -986,10 +986,7 @@ peer_compare(const void *aa, const void 
 }
 
 void
-print_config(struct bgpd_config *conf, struct rib_names *rib_l,
-    struct network_head *net_l, struct peer *peer_l,
-    struct filter_head *rules_l, struct mrt_head *mrt_l,
-    struct l3vpn_head *vpns_l)
+print_config(struct bgpd_config *conf, struct rib_names *rib_l)
 {
        struct filter_rule      *r;
        struct network          *n;
@@ -1001,11 +998,11 @@ print_config(struct bgpd_config *conf, s
        print_as_sets(conf->as_sets);
        print_prefixsets(&conf->prefixsets);
        print_originsets(&conf->originsets);
-       TAILQ_FOREACH(n, net_l, entry)
+       TAILQ_FOREACH(n, &conf->networks, entry)
                print_network(&n->net, "");
-       if (!SIMPLEQ_EMPTY(vpns_l))
+       if (!SIMPLEQ_EMPTY(&conf->l3vpns))
                printf("\n");
-       SIMPLEQ_FOREACH(vpn, vpns_l, entry)
+       SIMPLEQ_FOREACH(vpn, &conf->l3vpns, entry)
                print_l3vpn(vpn);
        printf("\n");
        SIMPLEQ_FOREACH(rr, rib_l, entry) {
@@ -1020,7 +1017,7 @@ print_config(struct bgpd_config *conf, s
        }
        printf("\n");
        print_mrt(conf, 0, 0, "", "");
-       print_groups(conf, peer_l);
-       TAILQ_FOREACH(r, rules_l, entry)
-               print_rule(peer_l, r);
+       print_groups(conf);
+       TAILQ_FOREACH(r, conf->filters, entry)
+               print_rule(conf, r);
 }
Index: session.c
===================================================================
RCS file: /cvs/src/usr.sbin/bgpd/session.c,v
retrieving revision 1.376
diff -u -p -r1.376 session.c
--- session.c   15 Mar 2019 09:54:54 -0000      1.376
+++ session.c   15 Mar 2019 17:45:22 -0000
@@ -94,17 +94,15 @@ void        session_up(struct peer *);
 void   session_down(struct peer *);
 int    imsg_rde(int, u_int32_t, void *, u_int16_t);
 void   session_demote(struct peer *, int);
+void   merge_peers(struct bgpd_config *, struct bgpd_config *);
 
 int             la_cmp(struct listen_addr *, struct listen_addr *);
-struct peer    *getpeerbyip(struct sockaddr *);
-struct peer    *getpeerbyid(u_int32_t);
 void            session_template_clone(struct peer *, struct sockaddr *,
                    u_int32_t, u_int32_t);
 int             session_match_mask(struct peer *, struct bgpd_addr *);
 
 struct bgpd_config     *conf, *nconf;
 struct bgpd_sysdep      sysdep;
-struct peer            *peers, *npeers;
 volatile sig_atomic_t   session_quit;
 int                     pending_reconf;
 int                     csock = -1, rcsock = -1;
@@ -196,7 +194,7 @@ session_main(int debug, int verbose)
        u_int                    listener_cnt, ctl_cnt, mrt_cnt;
        u_int                    new_cnt;
        struct passwd           *pw;
-       struct peer             *p, **peer_l = NULL, *last, *next;
+       struct peer             *p, **peer_l = NULL, *next;
        struct mrt              *m, *xm, **mrt_l = NULL;
        struct pollfd           *pfd = NULL;
        struct ctl_conn         *ctl_conn;
@@ -251,46 +249,43 @@ session_main(int debug, int verbose)
 
        while (session_quit == 0) {
                /* check for peers to be initialized or deleted */
-               last = NULL;
                if (!pending_reconf) {
-                       for (p = peers; p != NULL; p = next) {
-                               next = p->next;
+                       for (p = TAILQ_FIRST(&conf->peers); p != NULL;
+                          p = next) {
+                               next = TAILQ_NEXT(p, entry);
                                /* cloned peer that idled out? */
                                if (p->template && (p->state == STATE_IDLE ||
                                    p->state == STATE_ACTIVE) &&
                                    time(NULL) - p->stats.last_updown >=
                                    INTERVAL_HOLD_CLONED)
-                                       p->conf.reconf_action = RECONF_DELETE;
+                                       p->reconf_action = RECONF_DELETE;
 
                                /* new peer that needs init? */
                                if (p->state == STATE_NONE)
                                        init_peer(p);
 
                                /* reinit due? */
-                               if (p->conf.reconf_action == RECONF_REINIT) {
+                               if (p->reconf_action == RECONF_REINIT) {
                                        session_stop(p, ERR_CEASE_ADMIN_RESET);
                                        if (!p->conf.down)
                                                timer_set(p, Timer_IdleHold, 0);
                                }
 
                                /* deletion due? */
-                               if (p->conf.reconf_action == RECONF_DELETE) {
+                               if (p->reconf_action == RECONF_DELETE) {
                                        if (p->demoted)
                                                session_demote(p, -1);
                                        p->conf.demote_group[0] = 0;
                                        session_stop(p, ERR_CEASE_PEER_UNCONF);
                                        log_peer_warnx(&p->conf, "removed");
-                                       if (last != NULL)
-                                               last->next = next;
-                                       else
-                                               peers = next;
+                                       TAILQ_REMOVE(&conf->peers, p, entry);
                                        timer_remove_all(p);
+                                       pfkey_remove(p);
                                        free(p);
                                        peer_cnt--;
                                        continue;
                                }
-                               p->conf.reconf_action = RECONF_NONE;
-                               last = p;
+                               p->reconf_action = RECONF_NONE;
                        }
                }
 
@@ -375,7 +370,7 @@ session_main(int debug, int verbose)
                idx_listeners = i;
                timeout = 240;  /* loop every 240s at least */
 
-               for (p = peers; p != NULL; p = p->next) {
+               TAILQ_FOREACH(p, &conf->peers, entry) {
                        time_t  nextaction;
                        struct peer_timer *pt;
 
@@ -526,7 +521,7 @@ session_main(int debug, int verbose)
                        session_dispatch_msg(&pfd[j],
                            peer_l[j - idx_listeners]);
 
-               for (p = peers; p != NULL; p = p->next)
+               TAILQ_FOREACH(p, &conf->peers, entry)
                        if (p->rbuf && p->rbuf->wpos)
                                session_process_msg(p);
 
@@ -535,11 +530,11 @@ session_main(int debug, int verbose)
                                mrt_write(mrt_l[j - idx_peers]);
 
                for (; j < i; j++)
-                       control_dispatch_msg(&pfd[j], &ctl_cnt);
+                       control_dispatch_msg(&pfd[j], &ctl_cnt, &conf->peers);
        }
 
-       while ((p = peers) != NULL) {
-               peers = p->next;
+       while ((p = TAILQ_FIRST(&conf->peers)) != NULL) {
+               TAILQ_REMOVE(&conf->peers, p, entry);
                strlcpy(p->conf.shutcomm,
                    "bgpd shutting down",
                    sizeof(p->conf.shutcomm));
@@ -608,7 +603,7 @@ init_peer(struct peer *p)
         * do not handle new peers. they must reach ESTABLISHED beforehands.
         * peers added at runtime have reconf_action set to RECONF_REINIT.
         */
-       if (p->conf.reconf_action != RECONF_REINIT && p->conf.demote_group[0])
+       if (p->reconf_action != RECONF_REINIT && p->conf.demote_group[0])
                session_demote(p, +1);
 }
 
@@ -1008,7 +1003,7 @@ session_accept(int listenfd)
                return;
        }
 
-       p = getpeerbyip((struct sockaddr *)&cliaddr);
+       p = getpeerbyip(conf, (struct sockaddr *)&cliaddr);
 
        if (p != NULL && p->state == STATE_IDLE && p->errcnt < 2) {
                if (timer_running(p, Timer_IdleHold, NULL)) {
@@ -1523,7 +1518,7 @@ session_update(u_int32_t peerid, void *d
        struct peer             *p;
        struct bgp_msg          *buf;
 
-       if ((p = getpeerbyid(peerid)) == NULL) {
+       if ((p = getpeerbyid(conf, peerid)) == NULL) {
                log_warnx("no such peer: id=%u", peerid);
                return;
        }
@@ -2555,12 +2550,10 @@ session_dispatch_imsg(struct imsgbuf *ib
        struct mrt               xmrt;
        struct mrt              *mrt;
        struct imsgbuf          *i;
-       struct peer_config      *pconf;
-       struct peer             *p, *next;
+       struct peer             *p;
        struct listen_addr      *la, *nla;
        struct kif              *kif;
        u_char                  *data;
-       enum reconf_action       reconf;
        int                      n, fd, depend_ok, restricted;
        u_int8_t                 aid, errcode, subcode;
 
@@ -2606,7 +2599,6 @@ session_dispatch_imsg(struct imsgbuf *ib
                        if (idx != PFD_PIPE_MAIN)
                                fatalx("reconf request not from parent");
                        nconf = new_config();
-                       npeers = NULL;
 
                        copy_config(nconf, imsg.data); 
                        pending_reconf = 1;
@@ -2614,45 +2606,12 @@ session_dispatch_imsg(struct imsgbuf *ib
                case IMSG_RECONF_PEER:
                        if (idx != PFD_PIPE_MAIN)
                                fatalx("reconf request not from parent");
-                       pconf = imsg.data;
-                       p = getpeerbyaddr(&pconf->remote_addr);
-                       if (p == NULL) {
-                               if ((p = calloc(1, sizeof(struct peer))) ==
-                                   NULL)
-                                       fatal("new_peer");
-                               p->state = p->prev_state = STATE_NONE;
-                               p->next = npeers;
-                               npeers = p;
-                               reconf = RECONF_REINIT;
-                       } else
-                               reconf = RECONF_KEEP;
-
-                       memcpy(&p->conf, pconf, sizeof(struct peer_config));
-                       p->conf.reconf_action = reconf;
-
-                       /* sync the RDE in case we keep the peer */
-                       if (reconf == RECONF_KEEP) {
-                               if (imsg_rde(IMSG_SESSION_ADD, p->conf.id,
-                                   &p->conf, sizeof(struct peer_config)) == -1)
-                                       fatalx("imsg_compose error");
-                               if (p->conf.template) {
-                                       /* apply the conf to all clones */
-                                       struct peer *np;
-                                       for (np = peers; np; np = np->next) {
-                                               if (np->template != p)
-                                                       continue;
-                                               session_template_clone(np,
-                                                   NULL, np->conf.id,
-                                                   np->conf.remote_as);
-                                               if (imsg_rde(IMSG_SESSION_ADD,
-                                                   np->conf.id, &np->conf,
-                                                   sizeof(struct peer_config))
-                                                   == -1)
-                                                       fatalx("imsg_compose"
-                                                           " error");
-                                       }
-                               }
-                       }
+                       if ((p = calloc(1, sizeof(struct peer))) == NULL)
+                               fatal("new_peer");
+                       memcpy(&p->conf, imsg.data, sizeof(struct peer_config));
+                       p->state = p->prev_state = STATE_NONE;
+                       p->reconf_action = RECONF_REINIT;
+                       TAILQ_INSERT_TAIL(&nconf->peers, p, entry);
                        break;
                case IMSG_RECONF_LISTENER:
                        if (idx != PFD_PIPE_MAIN)
@@ -2723,23 +2682,7 @@ session_dispatch_imsg(struct imsgbuf *ib
                        if (nconf == NULL)
                                fatalx("got IMSG_RECONF_DONE but no config");
                        copy_config(conf, nconf);
-
-                       /* add new peers */
-                       for (p = npeers; p != NULL; p = next) {
-                               next = p->next;
-                               p->next = peers;
-                               peers = p;
-                       }
-                       /* find ones that need attention */
-                       for (p = peers; p != NULL; p = p->next) {
-                               /* needs to be deleted? */
-                               if (p->conf.reconf_action == RECONF_NONE &&
-                                   !p->template)
-                                       p->conf.reconf_action = RECONF_DELETE;
-                               /* had demotion, is demoted, demote removed? */
-                               if (p->demoted && !p->conf.demote_group[0])
-                                               session_demote(p, -1);
-                       }
+                       merge_peers(conf, nconf);
 
                        /* delete old listeners */
                        for (la = TAILQ_FIRST(conf->listen_addrs); la != NULL;
@@ -2781,7 +2724,7 @@ session_dispatch_imsg(struct imsgbuf *ib
                        kif = imsg.data;
                        depend_ok = kif->depend_state;
 
-                       for (p = peers; p != NULL; p = p->next)
+                       TAILQ_FOREACH(p, &conf->peers, entry)
                                if (!strcmp(p->conf.if_depend, kif->ifname)) {
                                        if (depend_ok && !p->depend_ok) {
                                                p->depend_ok = depend_ok;
@@ -2876,7 +2819,7 @@ session_dispatch_imsg(struct imsgbuf *ib
                                log_warnx("RDE sent invalid notification");
                                break;
                        }
-                       if ((p = getpeerbyid(imsg.hdr.peerid)) == NULL) {
+                       if ((p = getpeerbyid(conf, imsg.hdr.peerid)) == NULL) {
                                log_warnx("no such peer: id=%u",
                                    imsg.hdr.peerid);
                                break;
@@ -2916,7 +2859,7 @@ session_dispatch_imsg(struct imsgbuf *ib
                                log_warnx("RDE sent invalid restart msg");
                                break;
                        }
-                       if ((p = getpeerbyid(imsg.hdr.peerid)) == NULL) {
+                       if ((p = getpeerbyid(conf, imsg.hdr.peerid)) == NULL) {
                                log_warnx("no such peer: id=%u",
                                    imsg.hdr.peerid);
                                break;
@@ -2943,7 +2886,7 @@ session_dispatch_imsg(struct imsgbuf *ib
                case IMSG_SESSION_DOWN:
                        if (idx != PFD_PIPE_ROUTE)
                                fatalx("update request not from RDE");
-                       if ((p = getpeerbyid(imsg.hdr.peerid)) == NULL) {
+                       if ((p = getpeerbyid(conf, imsg.hdr.peerid)) == NULL) {
                                log_warnx("no such peer: id=%u",
                                    imsg.hdr.peerid);
                                break;
@@ -2993,26 +2936,12 @@ la_cmp(struct listen_addr *a, struct lis
 }
 
 struct peer *
-getpeerbyaddr(struct bgpd_addr *addr)
-{
-       struct peer *p;
-
-       /* we might want a more effective way to find peers by IP */
-       for (p = peers; p != NULL &&
-           memcmp(&p->conf.remote_addr, addr, sizeof(p->conf.remote_addr));
-           p = p->next)
-               ;       /* nothing */
-
-       return (p);
-}
-
-struct peer *
-getpeerbydesc(const char *descr)
+getpeerbydesc(struct bgpd_config *c, const char *descr)
 {
        struct peer     *p, *res = NULL;
        int              match = 0;
 
-       for (p = peers; p != NULL; p = p->next)
+       TAILQ_FOREACH(p, &c->peers, entry)
                if (!strcmp(p->conf.descr, descr)) {
                        res = p;
                        match++;
@@ -3029,7 +2958,7 @@ getpeerbydesc(const char *descr)
 }
 
 struct peer *
-getpeerbyip(struct sockaddr *ip)
+getpeerbyip(struct bgpd_config *c, struct sockaddr *ip)
 {
        struct bgpd_addr addr;
        struct peer     *p, *newpeer, *loose = NULL;
@@ -3038,13 +2967,13 @@ getpeerbyip(struct sockaddr *ip)
        sa2addr(ip, &addr, NULL);
 
        /* we might want a more effective way to find peers by IP */
-       for (p = peers; p != NULL; p = p->next)
+       TAILQ_FOREACH(p, &c->peers, entry)
                if (!p->conf.template &&
                    !memcmp(&addr, &p->conf.remote_addr, sizeof(addr)))
                        return (p);
 
        /* try template matching */
-       for (p = peers; p != NULL; p = p->next)
+       TAILQ_FOREACH(p, &c->peers, entry)
                if (p->conf.template &&
                    p->conf.remote_addr.aid == addr.aid &&
                    session_match_mask(p, &addr))
@@ -3058,22 +2987,20 @@ getpeerbyip(struct sockaddr *ip)
                        fatal(NULL);
                memcpy(newpeer, loose, sizeof(struct peer));
                for (id = UINT_MAX; id > UINT_MAX / 2; id--) {
-                       for (p = peers; p != NULL && p->conf.id != id;
-                           p = p->next)
-                               ;       /* nothing */
-                       if (p == NULL) {        /* we found a free id */
+                       TAILQ_FOREACH(p, &c->peers, entry)
+                               if (p->conf.id == id)
+                                       break;
+                       if (p == NULL)          /* we found a free id */
                                break;
-                       }
                }
                newpeer->template = loose;
                session_template_clone(newpeer, ip, id, 0);
                newpeer->state = newpeer->prev_state = STATE_NONE;
-               newpeer->conf.reconf_action = RECONF_KEEP;
+               newpeer->reconf_action = RECONF_KEEP;
                newpeer->rbuf = NULL;
                init_peer(newpeer);
                bgp_fsm(newpeer, EVNT_START);
-               newpeer->next = peers;
-               peers = newpeer;
+               TAILQ_INSERT_TAIL(&c->peers, newpeer, entry);
                return (newpeer);
        }
 
@@ -3081,16 +3008,15 @@ getpeerbyip(struct sockaddr *ip)
 }
 
 struct peer *
-getpeerbyid(u_int32_t peerid)
+getpeerbyid(struct bgpd_config *c, u_int32_t peerid)
 {
        struct peer *p;
 
-       /* we might want a more effective way to find peers by IP */
-       for (p = peers; p != NULL &&
-           p->conf.id != peerid; p = p->next)
-               ;       /* nothing */
-
-       return (p);
+       /* we might want a more effective way to find peers by id */
+       TAILQ_FOREACH(p, &c->peers, entry)
+               if (p->conf.id == peerid)
+                       return (p);
+       return (NULL);
 }
 
 int
@@ -3285,4 +3211,52 @@ session_stop(struct peer *peer, u_int8_t
                break;
        }
        bgp_fsm(peer, EVNT_STOP);
+}
+
+void
+merge_peers(struct bgpd_config *c, struct bgpd_config *nc)
+{
+       struct peer *p, *np;
+
+       TAILQ_FOREACH(p, &c->peers, entry) {
+               /* templates are handled specially */
+               if (p->template != NULL)
+                       continue;
+               np = getpeerbyid(nc, p->conf.id);
+               if (np == NULL) {
+                       p->reconf_action = RECONF_DELETE;
+                       continue;
+               }
+
+               memcpy(&p->conf, &np->conf, sizeof(p->conf));
+               TAILQ_REMOVE(&nc->peers, np, entry);
+               free(np);
+
+               p->reconf_action = RECONF_KEEP;
+
+               /* had demotion, is demoted, demote removed? */
+               if (p->demoted && !p->conf.demote_group[0])
+                       session_demote(p, -1);
+
+               /* sync the RDE in case we keep the peer */
+               if (imsg_rde(IMSG_SESSION_ADD, p->conf.id,
+                   &p->conf, sizeof(struct peer_config)) == -1)
+                       fatalx("imsg_compose error");
+
+               /* apply the config to all clones of a template */
+               if (p->conf.template) {
+                       struct peer *xp;
+                       TAILQ_FOREACH(xp, &conf->peers, entry) {
+                               if (xp->template != p)
+                                       continue;
+                               session_template_clone(xp, NULL, xp->conf.id,
+                                   xp->conf.remote_as);
+                               if (imsg_rde(IMSG_SESSION_ADD, xp->conf.id,
+                                   &xp->conf, sizeof(xp->conf)) == -1)
+                                       fatalx("imsg_compose error");
+                       }
+               }
+       }
+
+       TAILQ_CONCAT(&c->peers, &nc->peers, entry);
 }
Index: session.h
===================================================================
RCS file: /cvs/src/usr.sbin/bgpd/session.h,v
retrieving revision 1.134
diff -u -p -r1.134 session.h
--- session.h   7 Mar 2019 07:42:36 -0000       1.134
+++ session.h   15 Mar 2019 15:31:27 -0000
@@ -196,6 +196,7 @@ TAILQ_HEAD(peer_timer_head, peer_timer);
 struct peer {
        struct peer_config       conf;
        struct peer_stats        stats;
+       TAILQ_ENTRY(peer)        entry;
        struct {
                struct capabilities     ann;
                struct capabilities     peer;
@@ -207,13 +208,12 @@ struct peer {
                u_int32_t               spi_out;
                enum auth_method        method;
                u_int8_t                established;
-       } auth;
+       }                        auth;
        struct bgpd_addr         local;
        struct bgpd_addr         remote;
        struct peer_timer_head   timers;
        struct msgbuf            wbuf;
        struct ibuf_read        *rbuf;
-       struct peer             *next;
        struct peer             *template;
        int                      fd;
        int                      lasterr;
@@ -222,6 +222,7 @@ struct peer {
        u_int32_t                remote_bgpid;
        enum session_state       state;
        enum session_state       prev_state;
+       enum reconf_action       reconf_action;
        u_int16_t                short_as;
        u_int16_t                holdtime;
        u_int16_t                local_port;
@@ -232,7 +233,6 @@ struct peer {
        u_int8_t                 throttled;
 };
 
-extern struct peer     *peers;
 extern time_t           pauseaccept;
 
 struct ctl_timer {
@@ -247,8 +247,7 @@ int  carp_demote_get(char *);
 int     carp_demote_set(char *, int);
 
 /* config.c */
-void    merge_config(struct bgpd_config *, struct bgpd_config *,
-           struct peer *);
+void    merge_config(struct bgpd_config *, struct bgpd_config *);
 int     prepare_listeners(struct bgpd_config *);
 
 /* control.c */
@@ -256,7 +255,7 @@ int control_check(char *);
 int    control_init(int, char *);
 int    control_listen(int);
 void   control_shutdown(int);
-int    control_dispatch_msg(struct pollfd *, u_int *);
+int    control_dispatch_msg(struct pollfd *, u_int *, struct peer_head *);
 unsigned int   control_accept(int, int);
 
 /* log.c */
@@ -276,7 +275,7 @@ void                 mrt_dump_state(struct mrt *, u_in
 void            mrt_done(struct mrt *);
 
 /* parse.y */
-int     parse_config(char *, struct bgpd_config *, struct peer **);
+struct bgpd_config *parse_config(char *, struct peer_head *);
 
 /* pfkey.c */
 int    pfkey_read(int, struct sadb_msg *);
@@ -285,9 +284,7 @@ int pfkey_remove(struct peer *);
 int    pfkey_init(struct bgpd_sysdep *);
 
 /* printconf.c */
-void   print_config(struct bgpd_config *, struct rib_names *,
-           struct network_head *, struct peer *, struct filter_head *,
-           struct mrt_head *, struct l3vpn_head *);
+void   print_config(struct bgpd_config *, struct rib_names *);
 
 /* rde.c */
 void    rde_main(int, int);
@@ -296,8 +293,9 @@ void         rde_main(int, int);
 void            session_main(int, int);
 void            bgp_fsm(struct peer *, enum session_events);
 int             session_neighbor_rrefresh(struct peer *p);
-struct peer    *getpeerbyaddr(struct bgpd_addr *);
-struct peer    *getpeerbydesc(const char *);
+struct peer    *getpeerbydesc(struct bgpd_config *, const char *);
+struct peer    *getpeerbyip(struct bgpd_config *, struct sockaddr *);
+struct peer    *getpeerbyid(struct bgpd_config *, u_int32_t);
 int             peer_matched(struct peer *, struct ctl_neighbor *);
 int             imsg_ctl_parent(int, u_int32_t, pid_t, void *, u_int16_t);
 int             imsg_ctl_rde(int, pid_t, void *, u_int16_t);

Reply via email to