This diff just moves some of the code from rde.c to rde_peer.c where it
should be. Apart from moving the code peer_down() was modified so that it
can be used as callback of peer_foreach(). Also peer_foreach() was changed
to just walk the peer list instead of traversing the peer hash table.

OK?
-- 
:wq Claudio


Index: rde.c
===================================================================
RCS file: /cvs/src/usr.sbin/bgpd/rde.c,v
retrieving revision 1.496
diff -u -p -r1.496 rde.c
--- rde.c       8 Jan 2020 18:01:22 -0000       1.496
+++ rde.c       9 Jan 2020 07:51:33 -0000
@@ -20,12 +20,10 @@
  */
 
 #include <sys/types.h>
-#include <sys/socket.h>
 #include <sys/time.h>
 #include <sys/resource.h>
 
 #include <errno.h>
-#include <ifaddrs.h>
 #include <pwd.h>
 #include <poll.h>
 #include <signal.h>
@@ -63,9 +61,6 @@ int            rde_get_mp_nexthop(u_char *, u_int
                     struct filterstate *);
 void            rde_update_err(struct rde_peer *, u_int8_t , u_int8_t,
                     void *, u_int16_t);
-void            rde_update_log(const char *, u_int16_t,
-                    const struct rde_peer *, const struct bgpd_addr *,
-                    const struct bgpd_addr *, u_int8_t);
 void            rde_as4byte_fixup(struct rde_peer *, struct rde_aspath *);
 void            rde_reflector(struct rde_peer *, struct rde_aspath *);
 
@@ -94,20 +89,8 @@ void          rde_mark_prefixsets_dirty(struct 
 u_int8_t        rde_roa_validity(struct rde_prefixset *,
                     struct bgpd_addr *, u_int8_t, u_int32_t);
 
-void            peer_init(u_int32_t);
-void            peer_shutdown(void);
-void            peer_foreach(void (*)(struct rde_peer *, void *), void *);
-int             peer_imsg_pending(void);
-int             peer_localaddrs(struct rde_peer *, struct bgpd_addr *);
-struct rde_peer *peer_match(struct ctl_neighbor *, u_int32_t);
-struct rde_peer        *peer_add(u_int32_t, struct peer_config *);
-void            peer_up(struct rde_peer *, struct session_up *);
-void            peer_down(struct rde_peer *);
-void            peer_flush(struct rde_peer *, u_int8_t, time_t);
-void            peer_stale(struct rde_peer *, u_int8_t);
-void            peer_dump(struct rde_peer *, u_int8_t);
-static void     peer_recv_eor(struct rde_peer *, u_int8_t);
-static void     peer_send_eor(struct rde_peer *, u_int8_t);
+static void     rde_peer_recv_eor(struct rde_peer *, u_int8_t);
+static void     rde_peer_send_eor(struct rde_peer *, u_int8_t);
 
 void            network_add(struct network_config *, struct filterstate *);
 void            network_delete(struct network_config *);
@@ -115,13 +98,10 @@ static void         network_dump_upcall(struct 
 static void     network_flush_upcall(struct rib_entry *, void *);
 
 void            rde_shutdown(void);
-int             sa_cmp(struct bgpd_addr *, struct sockaddr *);
 int             ovs_match(struct prefix *, u_int32_t);
 
 volatile sig_atomic_t   rde_quit = 0;
 struct bgpd_config     *conf, *nconf;
-struct rde_peer_head    peerlist;
-struct rde_peer                *peerself;
 struct filter_head     *out_rules, *out_rules_tmp;
 struct imsgbuf         *ibuf_se;
 struct imsgbuf         *ibuf_se_ctl;
@@ -129,6 +109,9 @@ struct imsgbuf              *ibuf_main;
 struct rde_memstats     rdemem;
 int                     softreconfig;
 
+extern struct rde_peer_head     peerlist;
+extern struct rde_peer         *peerself;
+
 struct rde_dump_ctx {
        LIST_ENTRY(rde_dump_ctx)        entry;
        struct ctl_show_rib_request     req;
@@ -990,10 +973,14 @@ rde_dispatch_imsg_peer(struct rde_peer *
                if (imsg.hdr.len - IMSG_HEADER_SIZE != sizeof(sup))
                        fatalx("incorrect size of session request");
                memcpy(&sup, imsg.data, sizeof(sup));
-               peer_up(peer, &sup);
+               if (peer_up(peer, &sup) == -1) {
+                       peer->state = PEER_DOWN;
+                       imsg_compose(ibuf_se, IMSG_SESSION_DOWN, peer->conf.id,
+                           0, -1, NULL, 0);
+               }
                break;
        case IMSG_SESSION_DOWN:
-               peer_down(peer);
+               peer_down(peer, NULL);
                break;
        case IMSG_SESSION_STALE:
        case IMSG_SESSION_FLUSH:
@@ -1088,7 +1075,7 @@ rde_update_dispatch(struct rde_peer *pee
                }
                if (withdrawn_len == 0) {
                        /* EoR marker */
-                       peer_recv_eor(peer, AID_INET);
+                       rde_peer_recv_eor(peer, AID_INET);
                        return;
                }
        }
@@ -1200,7 +1187,7 @@ rde_update_dispatch(struct rde_peer *pee
                if ((state.aspath.flags & ~F_ATTR_MP_UNREACH) == 0 &&
                    mplen == 0) {
                        /* EoR marker */
-                       peer_recv_eor(peer, aid);
+                       rde_peer_recv_eor(peer, aid);
                }
 
                switch (aid) {
@@ -2290,7 +2277,7 @@ rde_dump_rib_as(struct prefix *p, struct
        }
 }
 
-static int
+int
 rde_match_peer(struct rde_peer *p, struct ctl_neighbor *n)
 {
        char *s;
@@ -2809,63 +2796,11 @@ rde_generate_updates(struct rib *rib, st
 }
 
 static void
-rde_up_dump_upcall(struct rib_entry *re, void *ptr)
-{
-       struct rde_peer         *peer = ptr;
-
-       if (re->rib_id != peer->loc_rib_id)
-               fatalx("%s: Unexpected RIB %u != %u.", __func__, re->rib_id,
-                   peer->loc_rib_id);
-       if (re->active == NULL)
-               return;
-       up_generate_updates(out_rules, peer, re->active, NULL);
-}
-
-static void
 rde_up_flush_upcall(struct prefix *p, void *ptr)
 {
        up_generate_updates(out_rules, prefix_peer(p), NULL, p);
 }
 
-static void
-rde_up_adjout_force_upcall(struct prefix *p, void *ptr)
-{
-       if (p->flags & PREFIX_FLAG_STALE) {
-               /* remove stale entries */
-               prefix_adjout_destroy(p);
-       } else if (p->flags & PREFIX_FLAG_DEAD) {
-               /* ignore dead prefixes, they will go away soon */
-       } else if ((p->flags & PREFIX_FLAG_MASK) == 0) {
-               /* put entries on the update queue if not allready on a queue */
-               p->flags |= PREFIX_FLAG_UPDATE;
-               if (RB_INSERT(prefix_tree, &prefix_peer(p)->updates[p->pt->aid],
-                   p) != NULL)
-                       fatalx("%s: RB tree invariant violated", __func__);
-       }
-}
-
-static void
-rde_up_adjout_force_done(void *ptr, u_int8_t aid)
-{
-       struct rde_peer         *peer = ptr;
-
-       /* Adj-RIB-Out ready, unthrottle peer and inject EOR */
-       peer->throttled = 0;
-       if (peer->capa.grestart.restart)
-               prefix_add_eor(peer, aid);
-}
-
-static void
-rde_up_dump_done(void *ptr, u_int8_t aid)
-{
-       struct rde_peer         *peer = ptr;
-
-       /* force out all updates of Adj-RIB-Out for this peer */
-       if (prefix_dump_new(peer, aid, 0, peer, rde_up_adjout_force_upcall,
-           rde_up_adjout_force_done, NULL) == -1)
-               fatal("%s: prefix_dump_new", __func__);
-}
-
 u_char queue_buf[4096];
 
 int
@@ -2939,7 +2874,7 @@ rde_update_queue_runner(void)
                                sent++;
                        }
                        if (eor)
-                               peer_send_eor(peer, AID_INET);
+                               rde_peer_send_eor(peer, AID_INET);
                }
                max -= sent;
        } while (sent != 0 && max > 0);
@@ -2989,7 +2924,7 @@ rde_update6_queue_runner(u_int8_t aid)
                                continue;
                        len = sizeof(queue_buf) - MSGSIZE_HEADER;
                        if (up_is_eor(peer, aid)) {
-                               peer_send_eor(peer, aid);
+                               rde_peer_send_eor(peer, aid);
                                continue;
                        }
                        r = up_dump_mp_reach(queue_buf, len, peer, aid);
@@ -3513,411 +3448,9 @@ rde_as4byte(struct rde_peer *peer)
        return (peer->capa.as4byte);
 }
 
-/*
- * peer functions
- */
-struct peer_table {
-       struct rde_peer_head    *peer_hashtbl;
-       u_int32_t                peer_hashmask;
-} peertable;
-
-#define PEER_HASH(x)           \
-       &peertable.peer_hashtbl[(x) & peertable.peer_hashmask]
-
-void
-peer_init(u_int32_t hashsize)
-{
-       struct peer_config pc;
-       u_int32_t        hs, i;
-
-       for (hs = 1; hs < hashsize; hs <<= 1)
-               ;
-       peertable.peer_hashtbl = calloc(hs, sizeof(struct rde_peer_head));
-       if (peertable.peer_hashtbl == NULL)
-               fatal("peer_init");
-
-       for (i = 0; i < hs; i++)
-               LIST_INIT(&peertable.peer_hashtbl[i]);
-       LIST_INIT(&peerlist);
-
-       peertable.peer_hashmask = hs - 1;
-
-       bzero(&pc, sizeof(pc));
-       snprintf(pc.descr, sizeof(pc.descr), "LOCAL");
-       pc.id = PEER_ID_SELF;
-
-       peerself = peer_add(PEER_ID_SELF, &pc);
-       if (peerself == NULL)
-               fatalx("peer_init add self");
-
-       peerself->state = PEER_UP;
-}
-
-void
-peer_shutdown(void)
-{
-       u_int32_t       i;
-
-       for (i = 0; i <= peertable.peer_hashmask; i++)
-               if (!LIST_EMPTY(&peertable.peer_hashtbl[i]))
-                       log_warnx("peer_free: free non-free table");
-
-       free(peertable.peer_hashtbl);
-}
-
-/*
- * Traverse all peers calling callback for each peer.
- */
-void
-peer_foreach(void (*callback)(struct rde_peer *, void *), void *arg)
-{
-       struct rde_peer *peer, *np;
-       u_int32_t i;
-
-       for (i = 0; i <= peertable.peer_hashmask; i++)
-               LIST_FOREACH_SAFE(peer,  &peertable.peer_hashtbl[i], hash_l, np)
-                       callback(peer, arg);
-}
-
-int
-peer_imsg_pending(void)
-{
-       int pending = 0;
-
-       peer_foreach(peer_imsg_queued, &pending);
-
-       return pending;
-}
-
-struct rde_peer *
-peer_get(u_int32_t id)
-{
-       struct rde_peer_head    *head;
-       struct rde_peer         *peer;
-
-       head = PEER_HASH(id);
-
-       LIST_FOREACH(peer, head, hash_l) {
-               if (peer->conf.id == id)
-                       return (peer);
-       }
-       return (NULL);
-}
-
-struct rde_peer *
-peer_match(struct ctl_neighbor *n, u_int32_t peerid)
-{
-       struct rde_peer_head    *head;
-       struct rde_peer         *peer;
-       u_int32_t               i = 0;
-
-       if (peerid != 0)
-               i = peerid & peertable.peer_hashmask;
-
-       while (i <= peertable.peer_hashmask) {
-               head = &peertable.peer_hashtbl[i];
-               LIST_FOREACH(peer, head, hash_l) {
-                       /* skip peers until peerid is found */
-                       if (peerid == peer->conf.id) {
-                               peerid = 0;
-                               continue;
-                       }
-                       if (peerid != 0)
-                               continue;
-
-                       if (rde_match_peer(peer, n))
-                               return (peer);
-               }
-               i++;
-       }
-       return (NULL);
-}
-
-struct rde_peer *
-peer_add(u_int32_t id, struct peer_config *p_conf)
-{
-       struct rde_peer_head    *head;
-       struct rde_peer         *peer;
-
-       if ((peer = peer_get(id))) {
-               memcpy(&peer->conf, p_conf, sizeof(struct peer_config));
-               return (NULL);
-       }
-
-       peer = calloc(1, sizeof(struct rde_peer));
-       if (peer == NULL)
-               fatal("peer_add");
-
-       memcpy(&peer->conf, p_conf, sizeof(struct peer_config));
-       peer->remote_bgpid = 0;
-       peer->loc_rib_id = rib_find(peer->conf.rib);
-       if (peer->loc_rib_id == RIB_NOTFOUND)
-               fatalx("King Bula's new peer met an unknown RIB");
-       peer->state = PEER_NONE;
-       SIMPLEQ_INIT(&peer->imsg_queue);
-
-       head = PEER_HASH(id);
-
-       LIST_INSERT_HEAD(head, peer, hash_l);
-       LIST_INSERT_HEAD(&peerlist, peer, peer_l);
-
-       return (peer);
-}
-
-int
-peer_localaddrs(struct rde_peer *peer, struct bgpd_addr *laddr)
-{
-       struct ifaddrs  *ifap, *ifa, *match;
-
-       if (getifaddrs(&ifap) == -1)
-               fatal("getifaddrs");
-
-       for (match = ifap; match != NULL; match = match->ifa_next)
-               if (sa_cmp(laddr, match->ifa_addr) == 0)
-                       break;
-
-       if (match == NULL) {
-               log_warnx("peer_localaddrs: local address not found");
-               return (-1);
-       }
-
-       for (ifa = ifap; ifa != NULL; ifa = ifa->ifa_next) {
-               if (ifa->ifa_addr->sa_family == AF_INET &&
-                   strcmp(ifa->ifa_name, match->ifa_name) == 0) {
-                       if (ifa->ifa_addr->sa_family ==
-                           match->ifa_addr->sa_family)
-                               ifa = match;
-                       sa2addr(ifa->ifa_addr, &peer->local_v4_addr, NULL);
-                       break;
-               }
-       }
-       for (ifa = ifap; ifa != NULL; ifa = ifa->ifa_next) {
-               if (ifa->ifa_addr->sa_family == AF_INET6 &&
-                   strcmp(ifa->ifa_name, match->ifa_name) == 0) {
-                       /*
-                        * only accept global scope addresses except explicitly
-                        * specified.
-                        */
-                       if (ifa->ifa_addr->sa_family ==
-                           match->ifa_addr->sa_family)
-                               ifa = match;
-                       else if (IN6_IS_ADDR_LINKLOCAL(
-                           &((struct sockaddr_in6 *)ifa->
-                           ifa_addr)->sin6_addr) ||
-                           IN6_IS_ADDR_SITELOCAL(
-                           &((struct sockaddr_in6 *)ifa->
-                           ifa_addr)->sin6_addr))
-                               continue;
-                       sa2addr(ifa->ifa_addr, &peer->local_v6_addr, NULL);
-                       break;
-               }
-       }
-
-       freeifaddrs(ifap);
-       return (0);
-}
-
-static void
-peer_adjout_clear_upcall(struct prefix *p, void *arg)
-{
-       prefix_adjout_destroy(p);
-}
-
-static void
-peer_adjout_stale_upcall(struct prefix *p, void *arg)
-{
-       if (p->flags & PREFIX_FLAG_DEAD) {
-               return;
-       } else if (p->flags & PREFIX_FLAG_WITHDRAW) {
-               /* no need to keep stale withdraws, they miss all attributes */
-               prefix_adjout_destroy(p);
-               return;
-       } else if (p->flags & PREFIX_FLAG_UPDATE) {
-               RB_REMOVE(prefix_tree, &prefix_peer(p)->updates[p->pt->aid], p);
-               p->flags &= ~PREFIX_FLAG_UPDATE;
-       }
-       p->flags |= PREFIX_FLAG_STALE;
-}
-
-void
-peer_up(struct rde_peer *peer, struct session_up *sup)
-{
-       u_int8_t         i;
-
-       if (peer->state == PEER_ERR) {
-               /*
-                * There is a race condition when doing PEER_ERR -> PEER_DOWN.
-                * So just do a full reset of the peer here.
-                */
-               if (prefix_dump_new(peer, AID_UNSPEC, 0, NULL,
-                   peer_adjout_clear_upcall, NULL, NULL) == -1)
-                       fatal("%s: prefix_dump_new", __func__);
-               peer_flush(peer, AID_UNSPEC, 0);
-               peer->prefix_cnt = 0;
-               peer->state = PEER_DOWN;
-       }
-       peer->remote_bgpid = ntohl(sup->remote_bgpid);
-       peer->short_as = sup->short_as;
-       memcpy(&peer->remote_addr, &sup->remote_addr,
-           sizeof(peer->remote_addr));
-       memcpy(&peer->capa, &sup->capa, sizeof(peer->capa));
-
-       if (peer_localaddrs(peer, &sup->local_addr)) {
-               peer->state = PEER_DOWN;
-               imsg_compose(ibuf_se, IMSG_SESSION_DOWN, peer->conf.id, 0, -1,
-                   NULL, 0);
-               return;
-       }
-
-       peer->state = PEER_UP;
-
-       for (i = 0; i < AID_MAX; i++) {
-               if (peer->capa.mp[i])
-                       peer_dump(peer, i);
-       }
-}
-
-void
-peer_down(struct rde_peer *peer)
-{
-       peer->remote_bgpid = 0;
-       peer->state = PEER_DOWN;
-       /* stop all pending dumps which may depend on this peer */
-       rib_dump_terminate(peer);
-
-       /* flush Adj-RIB-Out for this peer */
-       if (prefix_dump_new(peer, AID_UNSPEC, 0, NULL,
-           peer_adjout_clear_upcall, NULL, NULL) == -1)
-               fatal("%s: prefix_dump_new", __func__);
-
-       peer_flush(peer, AID_UNSPEC, 0);
-       peer->prefix_cnt = 0;
-
-       peer_imsg_flush(peer);
-
-       LIST_REMOVE(peer, hash_l);
-       LIST_REMOVE(peer, peer_l);
-       free(peer);
-}
-
-struct peer_flush {
-       struct rde_peer *peer;
-       time_t           staletime;
-};
-
-static void
-peer_flush_upcall(struct rib_entry *re, void *arg)
-{
-       struct rde_peer *peer = ((struct peer_flush *)arg)->peer;
-       struct rde_aspath *asp;
-       struct bgpd_addr addr;
-       struct prefix *p, *np, *rp;
-       time_t staletime = ((struct peer_flush *)arg)->staletime;
-       u_int32_t i;
-       u_int8_t prefixlen;
-
-       pt_getaddr(re->prefix, &addr);
-       prefixlen = re->prefix->prefixlen;
-       LIST_FOREACH_SAFE(p, &re->prefix_h, entry.list.rib, np) {
-               if (peer != prefix_peer(p))
-                       continue;
-               if (staletime && p->lastchange > staletime)
-                       continue;
-
-               for (i = RIB_LOC_START; i < rib_size; i++) {
-                       struct rib *rib = rib_byid(i);
-                       if (rib == NULL)
-                               continue;
-                       rp = prefix_get(rib, peer, &addr, prefixlen);
-                       if (rp) {
-                               asp = prefix_aspath(rp);
-                               if (asp->pftableid)
-                                       rde_send_pftable(asp->pftableid, &addr,
-                                           prefixlen, 1);
-
-                               prefix_destroy(rp);
-                               rde_update_log("flush", i, peer, NULL,
-                                   &addr, prefixlen);
-                       }
-               }
-
-               prefix_destroy(p);
-               peer->prefix_cnt--;
-               break;  /* optimization, only one match per peer possible */
-       }
-}
-
-/*
- * Flush all routes older then staletime. If staletime is 0 all routes will
- * be flushed.
- */
-void
-peer_flush(struct rde_peer *peer, u_int8_t aid, time_t staletime)
-{
-       struct peer_flush pf = { peer, staletime };
-
-       /* this dump must run synchronous, too much depends on that right now */
-       if (rib_dump_new(RIB_ADJ_IN, aid, 0, &pf, peer_flush_upcall,
-           NULL, NULL) == -1)
-               fatal("%s: rib_dump_new", __func__);
-
-       /* Deletions may have been performed in peer_flush_upcall */
-       rde_send_pftable_commit();
-
-       /* flushed no need to keep staletime */
-       if (aid == AID_UNSPEC) {
-               u_int8_t i;
-               for (i = 0; i < AID_MAX; i++)
-                       peer->staletime[i] = 0;
-       } else {
-               peer->staletime[aid] = 0;
-       }
-}
-
-void
-peer_stale(struct rde_peer *peer, u_int8_t aid)
-{
-       time_t                   now;
-
-       /* flush the now even staler routes out */
-       if (peer->staletime[aid])
-               peer_flush(peer, aid, peer->staletime[aid]);
-
-       peer->staletime[aid] = now = time(NULL);
-       peer->state = PEER_DOWN;
-
-       /* mark Adj-RIB-Out stale for this peer */
-       if (prefix_dump_new(peer, AID_UNSPEC, 0, NULL,
-           peer_adjout_stale_upcall, NULL, NULL) == -1)
-               fatal("%s: prefix_dump_new", __func__);
-
-       /* make sure new prefixes start on a higher timestamp */
-       while (now >= time(NULL))
-               sleep(1);
-}
-
-void
-peer_dump(struct rde_peer *peer, u_int8_t aid)
-{
-       if (peer->conf.export_type == EXPORT_NONE) {
-               /* nothing to send apart from the marker */
-               if (peer->capa.grestart.restart)
-                       prefix_add_eor(peer, aid);
-       } else if (peer->conf.export_type == EXPORT_DEFAULT_ROUTE) {
-               up_generate_default(out_rules, peer, aid);
-               rde_up_dump_done(peer, aid);
-       } else {
-               if (rib_dump_new(peer->loc_rib_id, aid, RDE_RUNNER_ROUNDS, peer,
-                   rde_up_dump_upcall, rde_up_dump_done, NULL) == -1)
-                       fatal("%s: rib_dump_new", __func__);
-               /* throttle peer until dump is done */
-               peer->throttled = 1;
-       }
-}
-
 /* End-of-RIB marker, RFC 4724 */
 static void
-peer_recv_eor(struct rde_peer *peer, u_int8_t aid)
+rde_peer_recv_eor(struct rde_peer *peer, u_int8_t aid)
 {
        peer->prefix_rcvd_eor++;
 
@@ -3931,14 +3464,14 @@ peer_recv_eor(struct rde_peer *peer, u_i
         */
        if (imsg_compose(ibuf_se, IMSG_SESSION_RESTARTED, peer->conf.id,
            0, -1, &aid, sizeof(aid)) == -1)
-               fatal("%s %d imsg_compose error", __func__, __LINE__);
+               fatal("imsg_compose error while receiving EoR");
 
        log_peer_info(&peer->conf, "received %s EOR marker",
            aid2str(aid));
 }
 
 static void
-peer_send_eor(struct rde_peer *peer, u_int8_t aid)
+rde_peer_send_eor(struct rde_peer *peer, u_int8_t aid)
 {
        u_int16_t       afi;
        u_int8_t        safi;
@@ -3951,8 +3484,7 @@ peer_send_eor(struct rde_peer *peer, u_i
                bzero(&null, 4);
                if (imsg_compose(ibuf_se, IMSG_UPDATE, peer->conf.id,
                    0, -1, &null, 4) == -1)
-                       fatal("%s %d imsg_compose error in peer_send_eor",
-                           __func__, __LINE__);
+                       fatal("imsg_compose error while sending EoR");
        } else {
                u_int16_t       i;
                u_char          buf[10];
@@ -4211,18 +3743,13 @@ network_flush_upcall(struct rib_entry *r
 void
 rde_shutdown(void)
 {
-       struct rde_peer         *p;
-       u_int32_t                i;
-
        /*
         * the decision process is turned off if rde_quit = 1 and
         * rde_shutdown depends on this.
         */
 
        /* First all peers go down */
-       for (i = 0; i <= peertable.peer_hashmask; i++)
-               while ((p = LIST_FIRST(&peertable.peer_hashtbl[i])) != NULL)
-                       peer_down(p);
+       peer_foreach(peer_down, NULL);
 
        /* free filters */
        filterlist_free(out_rules);
@@ -4239,44 +3766,6 @@ rde_shutdown(void)
        attr_shutdown();
        pt_shutdown();
        peer_shutdown();
-}
-
-int
-sa_cmp(struct bgpd_addr *a, struct sockaddr *b)
-{
-       struct sockaddr_in      *in_b;
-       struct sockaddr_in6     *in6_b;
-
-       if (aid2af(a->aid) != b->sa_family)
-               return (1);
-
-       switch (b->sa_family) {
-       case AF_INET:
-               in_b = (struct sockaddr_in *)b;
-               if (a->v4.s_addr != in_b->sin_addr.s_addr)
-                       return (1);
-               break;
-       case AF_INET6:
-               in6_b = (struct sockaddr_in6 *)b;
-#ifdef __KAME__
-               /* directly stolen from sbin/ifconfig/ifconfig.c */
-               if (IN6_IS_ADDR_LINKLOCAL(&in6_b->sin6_addr)) {
-                       in6_b->sin6_scope_id =
-                           ntohs(*(u_int16_t *)&in6_b->sin6_addr.s6_addr[2]);
-                       in6_b->sin6_addr.s6_addr[2] =
-                           in6_b->sin6_addr.s6_addr[3] = 0;
-               }
-#endif
-               if (bcmp(&a->v6, &in6_b->sin6_addr,
-                   sizeof(struct in6_addr)))
-                       return (1);
-               break;
-       default:
-               fatal("king bula sez: unknown address family");
-               /* NOTREACHED */
-       }
-
-       return (0);
 }
 
 struct rde_prefixset *
Index: rde.h
===================================================================
RCS file: /cvs/src/usr.sbin/bgpd/rde.h,v
retrieving revision 1.229
diff -u -p -r1.229 rde.h
--- rde.h       8 Jan 2020 18:01:22 -0000       1.229
+++ rde.h       9 Jan 2020 07:51:33 -0000
@@ -25,6 +25,7 @@
 #include <stdint.h>
 #include <stddef.h>
 
+
 #include "bgpd.h"
 #include "log.h"
 
@@ -357,6 +358,9 @@ int         mrt_dump_v2_hdr(struct mrt *, struc
 void           mrt_dump_upcall(struct rib_entry *, void *);
 
 /* rde.c */
+void            rde_update_log(const char *, u_int16_t,
+                    const struct rde_peer *, const struct bgpd_addr *,
+                    const struct bgpd_addr *, u_int8_t);
 void           rde_send_kroute_flush(struct rib *);
 void           rde_send_kroute(struct rib *, struct prefix *, struct prefix *);
 void           rde_send_nexthop(struct bgpd_addr *, int);
@@ -369,14 +373,28 @@ void              rde_generate_updates(struct rib *,
 u_int32_t      rde_local_as(void);
 int            rde_decisionflags(void);
 int            rde_as4byte(struct rde_peer *);
-struct rde_peer        *peer_get(u_int32_t);
+int            rde_match_peer(struct rde_peer *, struct ctl_neighbor *);
 
 /* rde_peer.c */
+void            peer_init(u_int32_t);
+void            peer_shutdown(void);
+void            peer_foreach(void (*)(struct rde_peer *, void *), void *);
+struct rde_peer        *peer_get(u_int32_t);
+struct rde_peer *peer_match(struct ctl_neighbor *, u_int32_t);
+struct rde_peer        *peer_add(u_int32_t, struct peer_config *);
+
 void            peer_imsg_push(struct rde_peer *, struct imsg *);
 int             peer_imsg_pop(struct rde_peer *, struct imsg *);
-void            peer_imsg_queued(struct rde_peer *, void *);
+int             peer_imsg_pending(void);
 void            peer_imsg_flush(struct rde_peer *);
 
+int             peer_up(struct rde_peer *, struct session_up *);
+void            peer_down(struct rde_peer *, void *);
+void            peer_flush(struct rde_peer *, u_int8_t, time_t);
+void            peer_stale(struct rde_peer *, u_int8_t);
+void            peer_dump(struct rde_peer *, u_int8_t);
+void            peer_recv_eor(struct rde_peer *, u_int8_t);
+void            peer_send_eor(struct rde_peer *, u_int8_t);
 
 /* rde_attr.c */
 int             attr_write(void *, u_int16_t, u_int8_t, u_int8_t, void *,
Index: rde_peer.c
===================================================================
RCS file: /cvs/src/usr.sbin/bgpd/rde_peer.c,v
retrieving revision 1.1
diff -u -p -r1.1 rde_peer.c
--- rde_peer.c  1 Jan 2020 07:25:04 -0000       1.1
+++ rde_peer.c  9 Jan 2020 08:26:11 -0000
@@ -17,21 +17,536 @@
  */
 #include <sys/types.h>
 #include <sys/queue.h>
+#include <sys/socket.h>
 
-#include <netinet/in.h>
-
+#include <ifaddrs.h>
 #include <stdlib.h>
 #include <stdio.h>
 #include <string.h>
+#include <unistd.h>
 
 #include "bgpd.h"
 #include "rde.h"
 
+struct peer_table {
+       struct rde_peer_head    *peer_hashtbl;
+       u_int32_t                peer_hashmask;
+} peertable;
+
+#define PEER_HASH(x)           \
+       &peertable.peer_hashtbl[(x) & peertable.peer_hashmask]
+
+struct rde_peer_head    peerlist;
+struct rde_peer                *peerself;
+
 struct iq {
        SIMPLEQ_ENTRY(iq)       entry;
        struct imsg             imsg;
 };
 
+extern struct filter_head      *out_rules;
+
+void
+peer_init(u_int32_t hashsize)
+{
+       struct peer_config pc;
+       u_int32_t        hs, i;
+
+       for (hs = 1; hs < hashsize; hs <<= 1)
+               ;
+       peertable.peer_hashtbl = calloc(hs, sizeof(struct rde_peer_head));
+       if (peertable.peer_hashtbl == NULL)
+               fatal("peer_init");
+
+       for (i = 0; i < hs; i++)
+               LIST_INIT(&peertable.peer_hashtbl[i]);
+       LIST_INIT(&peerlist);
+
+       peertable.peer_hashmask = hs - 1;
+
+       bzero(&pc, sizeof(pc));
+       snprintf(pc.descr, sizeof(pc.descr), "LOCAL");
+       pc.id = PEER_ID_SELF;
+
+       peerself = peer_add(PEER_ID_SELF, &pc);
+       if (peerself == NULL)
+               fatalx("peer_init add self");
+
+       peerself->state = PEER_UP;
+}
+
+void
+peer_shutdown(void)
+{
+       u_int32_t       i;
+
+       for (i = 0; i <= peertable.peer_hashmask; i++)
+               if (!LIST_EMPTY(&peertable.peer_hashtbl[i]))
+                       log_warnx("peer_free: free non-free table");
+
+       free(peertable.peer_hashtbl);
+}
+
+/*
+ * Traverse all peers calling callback for each peer.
+ */
+void
+peer_foreach(void (*callback)(struct rde_peer *, void *), void *arg)
+{
+       struct rde_peer *peer, *np;
+
+       LIST_FOREACH_SAFE(peer,  &peerlist, peer_l, np)
+               callback(peer, arg);
+}
+
+/*
+ * Lookup a peer by peer_id, return NULL if not found.
+ */
+struct rde_peer *
+peer_get(u_int32_t id)
+{
+       struct rde_peer_head    *head;
+       struct rde_peer         *peer;
+
+       head = PEER_HASH(id);
+
+       LIST_FOREACH(peer, head, hash_l) {
+               if (peer->conf.id == id)
+                       return (peer);
+       }
+       return (NULL);
+}
+
+/*
+ * Find next peer that matches neighbor options in *n.
+ * If peerid was set then pickup the lookup after that peer.
+ * Returns NULL if no more peers match.
+ */
+struct rde_peer *
+peer_match(struct ctl_neighbor *n, u_int32_t peerid)
+{
+       struct rde_peer_head    *head;
+       struct rde_peer         *peer;
+       u_int32_t               i = 0;
+
+       if (peerid != 0)
+               i = peerid & peertable.peer_hashmask;
+
+       while (i <= peertable.peer_hashmask) {
+               head = &peertable.peer_hashtbl[i];
+               LIST_FOREACH(peer, head, hash_l) {
+                       /* skip peers until peerid is found */
+                       if (peerid == peer->conf.id) {
+                               peerid = 0;
+                               continue;
+                       }
+                       if (peerid != 0)
+                               continue;
+
+                       if (rde_match_peer(peer, n))
+                               return (peer);
+               }
+               i++;
+       }
+       return (NULL);
+}
+
+struct rde_peer *
+peer_add(u_int32_t id, struct peer_config *p_conf)
+{
+       struct rde_peer_head    *head;
+       struct rde_peer         *peer;
+
+       if ((peer = peer_get(id))) {
+               memcpy(&peer->conf, p_conf, sizeof(struct peer_config));
+               return (NULL);
+       }
+
+       peer = calloc(1, sizeof(struct rde_peer));
+       if (peer == NULL)
+               fatal("peer_add");
+
+       memcpy(&peer->conf, p_conf, sizeof(struct peer_config));
+       peer->remote_bgpid = 0;
+       peer->loc_rib_id = rib_find(peer->conf.rib);
+       if (peer->loc_rib_id == RIB_NOTFOUND)
+               fatalx("King Bula's new peer met an unknown RIB");
+       peer->state = PEER_NONE;
+       SIMPLEQ_INIT(&peer->imsg_queue);
+
+       head = PEER_HASH(id);
+
+       LIST_INSERT_HEAD(head, peer, hash_l);
+       LIST_INSERT_HEAD(&peerlist, peer, peer_l);
+
+       return (peer);
+}
+
+/*
+ * Various RIB walker callbacks.
+ */
+static void
+peer_adjout_clear_upcall(struct prefix *p, void *arg)
+{
+       prefix_adjout_destroy(p);
+}
+
+static void
+peer_adjout_stale_upcall(struct prefix *p, void *arg)
+{
+       if (p->flags & PREFIX_FLAG_DEAD) {
+               return;
+       } else if (p->flags & PREFIX_FLAG_WITHDRAW) {
+               /* no need to keep stale withdraws, they miss all attributes */
+               prefix_adjout_destroy(p);
+               return;
+       } else if (p->flags & PREFIX_FLAG_UPDATE) {
+               RB_REMOVE(prefix_tree, &prefix_peer(p)->updates[p->pt->aid], p);
+               p->flags &= ~PREFIX_FLAG_UPDATE;
+       }
+       p->flags |= PREFIX_FLAG_STALE;
+}
+
+struct peer_flush {
+       struct rde_peer *peer;
+       time_t           staletime;
+};
+
+static void
+peer_flush_upcall(struct rib_entry *re, void *arg)
+{
+       struct rde_peer *peer = ((struct peer_flush *)arg)->peer;
+       struct rde_aspath *asp;
+       struct bgpd_addr addr;
+       struct prefix *p, *np, *rp;
+       time_t staletime = ((struct peer_flush *)arg)->staletime;
+       u_int32_t i;
+       u_int8_t prefixlen;
+
+       pt_getaddr(re->prefix, &addr);
+       prefixlen = re->prefix->prefixlen;
+       LIST_FOREACH_SAFE(p, &re->prefix_h, entry.list.rib, np) {
+               if (peer != prefix_peer(p))
+                       continue;
+               if (staletime && p->lastchange > staletime)
+                       continue;
+
+               for (i = RIB_LOC_START; i < rib_size; i++) {
+                       struct rib *rib = rib_byid(i);
+                       if (rib == NULL)
+                               continue;
+                       rp = prefix_get(rib, peer, &addr, prefixlen);
+                       if (rp) {
+                               asp = prefix_aspath(rp);
+                               if (asp->pftableid)
+                                       rde_send_pftable(asp->pftableid, &addr,
+                                           prefixlen, 1);
+
+                               prefix_destroy(rp);
+                               rde_update_log("flush", i, peer, NULL,
+                                   &addr, prefixlen);
+                       }
+               }
+
+               prefix_destroy(p);
+               peer->prefix_cnt--;
+               break;  /* optimization, only one match per peer possible */
+       }
+}
+
+static void
+rde_up_adjout_force_upcall(struct prefix *p, void *ptr)
+{
+       if (p->flags & PREFIX_FLAG_STALE) {
+               /* remove stale entries */
+               prefix_adjout_destroy(p);
+       } else if (p->flags & PREFIX_FLAG_DEAD) {
+               /* ignore dead prefixes, they will go away soon */
+       } else if ((p->flags & PREFIX_FLAG_MASK) == 0) {
+               /* put entries on the update queue if not allready on a queue */
+               p->flags |= PREFIX_FLAG_UPDATE;
+               if (RB_INSERT(prefix_tree, &prefix_peer(p)->updates[p->pt->aid],
+                   p) != NULL)
+                       fatalx("%s: RB tree invariant violated", __func__);
+       }
+}
+
+static void
+rde_up_adjout_force_done(void *ptr, u_int8_t aid)
+{
+       struct rde_peer         *peer = ptr;
+
+       /* Adj-RIB-Out ready, unthrottle peer and inject EOR */
+       peer->throttled = 0;
+       if (peer->capa.grestart.restart)
+               prefix_add_eor(peer, aid);
+}
+
+static void
+rde_up_dump_upcall(struct rib_entry *re, void *ptr)
+{
+       struct rde_peer         *peer = ptr;
+
+       if (re->rib_id != peer->loc_rib_id)
+               fatalx("%s: Unexpected RIB %u != %u.", __func__, re->rib_id,
+                   peer->loc_rib_id);
+       if (re->active == NULL)
+               return;
+       up_generate_updates(out_rules, peer, re->active, NULL);
+}
+
+static void
+rde_up_dump_done(void *ptr, u_int8_t aid)
+{
+       struct rde_peer         *peer = ptr;
+
+       /* force out all updates of Adj-RIB-Out for this peer */
+       if (prefix_dump_new(peer, aid, 0, peer, rde_up_adjout_force_upcall,
+           rde_up_adjout_force_done, NULL) == -1)
+               fatal("%s: prefix_dump_new", __func__);
+}
+
+static int
+sa_cmp(struct bgpd_addr *a, struct sockaddr *b)
+{
+       struct sockaddr_in      *in_b;
+       struct sockaddr_in6     *in6_b;
+
+       if (aid2af(a->aid) != b->sa_family)
+               return (1);
+
+       switch (b->sa_family) {
+       case AF_INET:
+               in_b = (struct sockaddr_in *)b;
+               if (a->v4.s_addr != in_b->sin_addr.s_addr)
+                       return (1);
+               break;
+       case AF_INET6:
+               in6_b = (struct sockaddr_in6 *)b;
+#ifdef __KAME__
+               /* directly stolen from sbin/ifconfig/ifconfig.c */
+               if (IN6_IS_ADDR_LINKLOCAL(&in6_b->sin6_addr)) {
+                       in6_b->sin6_scope_id =
+                           ntohs(*(u_int16_t *)&in6_b->sin6_addr.s6_addr[2]);
+                       in6_b->sin6_addr.s6_addr[2] =
+                           in6_b->sin6_addr.s6_addr[3] = 0;
+               }
+#endif
+               if (bcmp(&a->v6, &in6_b->sin6_addr,
+                   sizeof(struct in6_addr)))
+                       return (1);
+               break;
+       default:
+               fatal("king bula sez: unknown address family");
+               /* NOTREACHED */
+       }
+
+       return (0);
+}
+
+/*
+ * Figure out the local IP addresses most suitable for this session.
+ * This looks up the local address of other address family based on
+ * the address of the TCP session.
+ */
+static int
+peer_localaddrs(struct rde_peer *peer, struct bgpd_addr *laddr)
+{
+       struct ifaddrs  *ifap, *ifa, *match;
+
+       if (getifaddrs(&ifap) == -1)
+               fatal("getifaddrs");
+
+       for (match = ifap; match != NULL; match = match->ifa_next)
+               if (sa_cmp(laddr, match->ifa_addr) == 0)
+                       break;
+
+       if (match == NULL) {
+               log_warnx("peer_localaddrs: local address not found");
+               return (-1);
+       }
+
+       for (ifa = ifap; ifa != NULL; ifa = ifa->ifa_next) {
+               if (ifa->ifa_addr->sa_family == AF_INET &&
+                   strcmp(ifa->ifa_name, match->ifa_name) == 0) {
+                       if (ifa->ifa_addr->sa_family ==
+                           match->ifa_addr->sa_family)
+                               ifa = match;
+                       sa2addr(ifa->ifa_addr, &peer->local_v4_addr, NULL);
+                       break;
+               }
+       }
+       for (ifa = ifap; ifa != NULL; ifa = ifa->ifa_next) {
+               if (ifa->ifa_addr->sa_family == AF_INET6 &&
+                   strcmp(ifa->ifa_name, match->ifa_name) == 0) {
+                       /*
+                        * only accept global scope addresses except explicitly
+                        * specified.
+                        */
+                       if (ifa->ifa_addr->sa_family ==
+                           match->ifa_addr->sa_family)
+                               ifa = match;
+                       else if (IN6_IS_ADDR_LINKLOCAL(
+                           &((struct sockaddr_in6 *)ifa->
+                           ifa_addr)->sin6_addr) ||
+                           IN6_IS_ADDR_SITELOCAL(
+                           &((struct sockaddr_in6 *)ifa->
+                           ifa_addr)->sin6_addr))
+                               continue;
+                       sa2addr(ifa->ifa_addr, &peer->local_v6_addr, NULL);
+                       break;
+               }
+       }
+
+       freeifaddrs(ifap);
+       return (0);
+}
+
+/*
+ * Session got established, bring peer up, load RIBs do initial table dump.
+ */
+int
+peer_up(struct rde_peer *peer, struct session_up *sup)
+{
+       u_int8_t         i;
+
+       if (peer->state == PEER_ERR) {
+               /*
+                * There is a race condition when doing PEER_ERR -> PEER_DOWN.
+                * So just do a full reset of the peer here.
+                */
+               if (prefix_dump_new(peer, AID_UNSPEC, 0, NULL,
+                   peer_adjout_clear_upcall, NULL, NULL) == -1)
+                       fatal("%s: prefix_dump_new", __func__);
+               peer_flush(peer, AID_UNSPEC, 0);
+               peer->prefix_cnt = 0;
+               peer->state = PEER_DOWN;
+       }
+       peer->remote_bgpid = ntohl(sup->remote_bgpid);
+       peer->short_as = sup->short_as;
+       memcpy(&peer->remote_addr, &sup->remote_addr,
+           sizeof(peer->remote_addr));
+       memcpy(&peer->capa, &sup->capa, sizeof(peer->capa));
+
+       if (peer_localaddrs(peer, &sup->local_addr))
+               return (-1);
+
+       peer->state = PEER_UP;
+
+       for (i = 0; i < AID_MAX; i++) {
+               if (peer->capa.mp[i])
+                       peer_dump(peer, i);
+       }
+
+       return (0);
+}
+
+/*
+ * Session dropped and no graceful restart is done. Stop everything for
+ * this peer and clean up.
+ */
+void
+peer_down(struct rde_peer *peer, void *bula)
+{
+       peer->remote_bgpid = 0;
+       peer->state = PEER_DOWN;
+       /* stop all pending dumps which may depend on this peer */
+       rib_dump_terminate(peer);
+
+       /* flush Adj-RIB-Out */
+       if (prefix_dump_new(peer, AID_UNSPEC, 0, NULL,
+           peer_adjout_clear_upcall, NULL, NULL) == -1)
+               fatal("%s: prefix_dump_new", __func__);
+
+       /* flush Adj-RIB-In */
+       peer_flush(peer, AID_UNSPEC, 0);
+       peer->prefix_cnt = 0;
+
+       peer_imsg_flush(peer);
+
+       LIST_REMOVE(peer, hash_l);
+       LIST_REMOVE(peer, peer_l);
+       free(peer);
+}
+
+/*
+ * Flush all routes older then staletime. If staletime is 0 all routes will
+ * be flushed.
+ */
+void
+peer_flush(struct rde_peer *peer, u_int8_t aid, time_t staletime)
+{
+       struct peer_flush pf = { peer, staletime };
+
+       /* this dump must run synchronous, too much depends on that right now */
+       if (rib_dump_new(RIB_ADJ_IN, aid, 0, &pf, peer_flush_upcall,
+           NULL, NULL) == -1)
+               fatal("%s: rib_dump_new", __func__);
+
+       /* Deletions may have been performed in peer_flush_upcall */
+       rde_send_pftable_commit();
+
+       /* flushed no need to keep staletime */
+       if (aid == AID_UNSPEC) {
+               u_int8_t i;
+               for (i = 0; i < AID_MAX; i++)
+                       peer->staletime[i] = 0;
+       } else {
+               peer->staletime[aid] = 0;
+       }
+}
+
+/*
+ * During graceful restart mark a peer as stale if the session goes down.
+ * For the specified AID the Adj-RIB-Out as marked stale and the staletime
+ * is set to the current timestamp for identifying stale routes in Adj-RIB-In.
+ */
+void
+peer_stale(struct rde_peer *peer, u_int8_t aid)
+{
+       time_t                   now;
+
+       /* flush the now even staler routes out */
+       if (peer->staletime[aid])
+               peer_flush(peer, aid, peer->staletime[aid]);
+
+       peer->staletime[aid] = now = time(NULL);
+       peer->state = PEER_DOWN;
+
+       /* mark Adj-RIB-Out stale for this peer */
+       if (prefix_dump_new(peer, AID_UNSPEC, 0, NULL,
+           peer_adjout_stale_upcall, NULL, NULL) == -1)
+               fatal("%s: prefix_dump_new", __func__);
+
+       /* make sure new prefixes start on a higher timestamp */
+       while (now >= time(NULL))
+               sleep(1);
+}
+
+/*
+ * Load the Adj-RIB-Out of a peer normally called when a session is 
established.
+ * Once the Adj-RIB-Out is ready stale routes are removed from the Adj-RIB-Out
+ * and all routes are put on the update queue so they will be sent out.
+ */
+void
+peer_dump(struct rde_peer *peer, u_int8_t aid)
+{
+       if (peer->conf.export_type == EXPORT_NONE) {
+               /* nothing to send apart from the marker */
+               if (peer->capa.grestart.restart)
+                       prefix_add_eor(peer, aid);
+       } else if (peer->conf.export_type == EXPORT_DEFAULT_ROUTE) {
+               up_generate_default(out_rules, peer, aid);
+               rde_up_dump_done(peer, aid);
+       } else {
+               if (rib_dump_new(peer->loc_rib_id, aid, RDE_RUNNER_ROUNDS, peer,
+                   rde_up_dump_upcall, rde_up_dump_done, NULL) == -1)
+                       fatal("%s: rib_dump_new", __func__);
+               /* throttle peer until dump is done */
+               peer->throttled = 1;
+       }
+}
+
 /*
  * move an imsg from src to dst, disconnecting any dynamic memory from src.
  */
@@ -77,12 +592,25 @@ peer_imsg_pop(struct rde_peer *peer, str
        return 1;
 }
 
-void
+static void
 peer_imsg_queued(struct rde_peer *peer, void *arg)
 {
        int *p = arg;
 
        *p = *p || !SIMPLEQ_EMPTY(&peer->imsg_queue);
+}
+
+/*
+ * Check if any imsg are pending, return 0 if none are pending
+ */
+int
+peer_imsg_pending(void)
+{
+       int pending = 0;
+
+       peer_foreach(peer_imsg_queued, &pending);
+
+       return pending;
 }
 
 /*

Reply via email to