This implements a way to add a limit for bgpctl show rib output.
When a limit is set then the output will include a token (at the end)
that can be used to get the next batch of output. These two things allow
to build a frontend that puts the output onto multiple pages.
Both regular output and JSON output include the token.

A simple but dumb example of this:
    set -e
    TMP=$(mktemp)
    while true; do
        bgpctl show rib limit 100 $token | tee "$TMP"
        token=$(grep ^token "$TMP")
    done

This can be used for looking glass software to limit output and offer
people a way to page through the output.
-- 
:wq Claudio

Index: bgpctl/bgpctl.c
===================================================================
RCS file: /cvs/src/usr.sbin/bgpctl/bgpctl.c,v
retrieving revision 1.263
diff -u -p -r1.263 bgpctl.c
--- bgpctl/bgpctl.c     10 May 2020 13:38:46 -0000      1.263
+++ bgpctl/bgpctl.c     13 May 2020 14:41:24 -0000
@@ -240,6 +240,8 @@ main(int argc, char *argv[])
                strlcpy(ribreq.rib, res->rib, sizeof(ribreq.rib));
                ribreq.aid = res->aid;
                ribreq.flags = res->flags;
+               ribreq.limit = res->limit;
+               ribreq.token = res->token;
                imsg_compose(ibuf, type, 0, 0, -1, &ribreq, sizeof(ribreq));
                break;
        case SHOW_RIB_MEM:
@@ -391,11 +393,12 @@ show(struct imsg *imsg, struct parse_res
 {
        struct peer             *p;
        struct ctl_timer        *t;
-       struct ctl_show_interface       *iface;
        struct ctl_show_nexthop *nh;
        struct kroute_full      *kf;
        struct ktable           *kt;
        struct ctl_show_rib      rib;
+       struct ctl_show_rib_token        token;
+       struct ctl_show_interface       *iface;
        u_char                  *asdata;
        struct rde_memstats     stats;
        struct rde_hashstats    hash;
@@ -466,6 +469,14 @@ show(struct imsg *imsg, struct parse_res
                memcpy(&hash, imsg->data, sizeof(hash));
                output->rib_hash(&hash);
                break;
+       case IMSG_CTL_TOKEN:
+               if (imsg->hdr.len != IMSG_HEADER_SIZE + sizeof(token)) {
+                       warnx("got IMSG_CTL_TOKEN with wrong len");
+                       break;
+               }
+               memcpy(&token, imsg->data, sizeof(token));
+               output->token(&token);
+               break;
        case IMSG_CTL_RESULT:
                if (imsg->hdr.len != IMSG_HEADER_SIZE + sizeof(rescode)) {
                        warnx("got IMSG_CTL_RESULT with wrong len");
@@ -975,6 +986,20 @@ fmt_ext_community(u_int8_t *data)
                    (unsigned long long)be64toh(ext));
                return buf;
        }
+}
+
+const char *
+fmt_token(struct ctl_show_rib_token *t)
+{
+       static char buf[128];
+
+       snprintf(buf, sizeof(buf), "%d:%d:%u:%x:%x:%x:%x:%x:%x:%x:%x:%d",
+           t->peerid, t->prefix.aid, t->prefix.scope_id,
+           t->prefix.addr32[0], t->prefix.addr32[1], t->prefix.addr32[2],
+           t->prefix.addr32[3], t->prefix.addr32[4], t->prefix.addr32[5],
+           t->prefix.addr32[6], t->prefix.addr32[7], t->prefixlen);
+
+       return buf;
 }
 
 void
Index: bgpctl/bgpctl.h
===================================================================
RCS file: /cvs/src/usr.sbin/bgpctl/bgpctl.h,v
retrieving revision 1.7
diff -u -p -r1.7 bgpctl.h
--- bgpctl/bgpctl.h     2 May 2020 14:33:33 -0000       1.7
+++ bgpctl/bgpctl.h     13 May 2020 10:00:49 -0000
@@ -30,6 +30,7 @@ struct output {
                    struct parse_result *);
        void    (*rib_hash)(struct rde_hashstats *);
        void    (*rib_mem)(struct rde_memstats *);
+       void    (*token)(struct ctl_show_rib_token *);
        void    (*result)(u_int);
        void    (*tail)(void);
 };
@@ -53,3 +54,4 @@ const char    *fmt_attr(u_int8_t, int);
 const char     *fmt_community(u_int16_t, u_int16_t);
 const char     *fmt_large_community(u_int32_t, u_int32_t, u_int32_t);
 const char     *fmt_ext_community(u_int8_t *);
+const char     *fmt_token(struct ctl_show_rib_token *);
Index: bgpctl/json.c
===================================================================
RCS file: /cvs/src/usr.sbin/bgpctl/json.c,v
retrieving revision 1.2
diff -u -p -r1.2 json.c
--- bgpctl/json.c       4 May 2020 16:00:13 -0000       1.2
+++ bgpctl/json.c       14 May 2020 14:57:32 -0000
@@ -117,6 +117,20 @@ json_do_array(const char *name)
 }
 
 void
+json_do_end_array(const char *name)
+{
+       int l;
+
+       l = do_find(ARRAY, name);
+       if (l == -1)
+               errx(1, "json array %s not found", name);
+
+       /* array already in use, close element and move on */
+       while (level >= l)
+               json_do_end();
+}
+
+void
 json_do_object(const char *name)
 {
        int i, l;
Index: bgpctl/json.h
===================================================================
RCS file: /cvs/src/usr.sbin/bgpctl/json.h,v
retrieving revision 1.2
diff -u -p -r1.2 json.h
--- bgpctl/json.h       4 May 2020 16:00:13 -0000       1.2
+++ bgpctl/json.h       14 May 2020 14:54:04 -0000
@@ -20,6 +20,7 @@
 void json_do_start(void);
 void json_do_finish(void);
 void json_do_array(const char *);
+void json_do_end_array(const char *);
 void json_do_object(const char *);
 void json_do_end(void);
 void json_do_printf(const char *, const char *, ...)
Index: bgpctl/output.c
===================================================================
RCS file: /cvs/src/usr.sbin/bgpctl/output.c,v
retrieving revision 1.9
diff -u -p -r1.9 output.c
--- bgpctl/output.c     10 May 2020 13:38:46 -0000      1.9
+++ bgpctl/output.c     13 May 2020 14:45:18 -0000
@@ -957,6 +957,12 @@ show_rib_hash(struct rde_hashstats *hash
 }
 
 static void
+show_token(struct ctl_show_rib_token *t)
+{
+       printf("token %s\n", fmt_token(t));
+}
+
+static void
 show_result(u_int rescode)
 {
        if (rescode == 0)
@@ -987,6 +993,7 @@ const struct output show_output = {
        .rib = show_rib,
        .rib_mem = show_rib_mem,
        .rib_hash = show_rib_hash,
+       .token = show_token,
        .result = show_result,
        .tail = show_tail
 };
Index: bgpctl/output_json.c
===================================================================
RCS file: /cvs/src/usr.sbin/bgpctl/output_json.c,v
retrieving revision 1.3
diff -u -p -r1.3 output_json.c
--- bgpctl/output_json.c        10 May 2020 13:38:46 -0000      1.3
+++ bgpctl/output_json.c        14 May 2020 15:05:00 -0000
@@ -58,7 +58,7 @@ json_neighbor_capabilities(struct capabi
                for (i = 0; i < AID_MAX; i++)
                        if (capa->mp[i])
                                json_do_printf("mp", "%s", aid2str(i));
-               json_do_end();
+               json_do_end_array("multiprotocol");
        }
        if (capa->grestart.restart) {
                int restarted = 0, present = 0;
@@ -70,6 +70,7 @@ json_neighbor_capabilities(struct capabi
                                        restarted = 1;
                                break;
                        }
+
                json_do_object("graceful_restart");
                json_do_bool("eor", 1);
                json_do_bool("restart", restarted);
@@ -89,7 +90,7 @@ json_neighbor_capabilities(struct capabi
                                            CAPA_GR_FORWARD);
                                        json_do_end();
                                }
-                       json_do_end();
+                       json_do_end_array("protocols");
                }
 
                json_do_end();
@@ -487,7 +488,7 @@ json_do_community(u_char *data, uint16_t
                json_do_printf("community", "%s", fmt_community(a, v));
        }
 
-       json_do_end();
+       json_do_end_array("communities");
 }
 
 static void
@@ -515,7 +516,7 @@ json_do_large_community(u_char *data, ui
                    fmt_large_community(a, l1, l2));
        }
 
-       json_do_end();
+       json_do_end_array("large_communities");
 }
 
 static void
@@ -533,7 +534,7 @@ json_do_ext_community(u_char *data, uint
        for (i = 0; i < len; i += 8)
                json_do_printf("community", "%s", fmt_ext_community(data + i));
 
-       json_do_end();
+       json_do_end_array("extended_communities");
 }
 
 static void
@@ -762,6 +763,7 @@ bad_len:
                        data += pos;
                        alen -= pos;
                }
+               json_do_end_array("NLRI");
                break;
        case ATTR_EXT_COMMUNITIES:
                json_do_ext_community(data, alen);
@@ -918,6 +920,13 @@ json_rib_hash(struct rde_hashstats *hash
 }
 
 static void
+json_token(struct ctl_show_rib_token *t)
+{
+       json_do_end_array("rib");
+       json_do_printf("token", "%s", fmt_token(t));
+}
+
+static void
 json_result(u_int rescode)
 {
        if (rescode == 0)
@@ -951,6 +960,7 @@ const struct output json_output = {
        .rib = json_rib,
        .rib_mem = json_rib_mem,
        .rib_hash = json_rib_hash,
+       .token = json_token,
        .result = json_result,
        .tail = json_tail
 };
Index: bgpctl/parser.c
===================================================================
RCS file: /cvs/src/usr.sbin/bgpctl/parser.c,v
retrieving revision 1.104
diff -u -p -r1.104 parser.c
--- bgpctl/parser.c     12 May 2020 13:26:02 -0000      1.104
+++ bgpctl/parser.c     14 May 2020 11:03:57 -0000
@@ -61,7 +61,9 @@ enum token_type {
        RD,
        FAMILY,
        RTABLE,
-       FILENAME
+       FILENAME,
+       TOKEN,
+       LIMIT
 };
 
 struct token {
@@ -110,6 +112,8 @@ static const struct token t_pftable[];
 static const struct token t_prepnbr[];
 static const struct token t_prepself[];
 static const struct token t_weight[];
+static const struct token t_limit[];
+static const struct token t_token[];
 static const struct token t_log[];
 static const struct token t_fib_table[];
 static const struct token t_show_fib_table[];
@@ -168,6 +172,7 @@ static const struct token t_show_rib[] =
        { KEYWORD,      "community",    NONE,           t_show_community},
        { KEYWORD,      "ext-community", NONE,          t_show_extcommunity},
        { KEYWORD,      "large-community", NONE,        t_show_largecommunity},
+       { KEYWORD,      "ovs",          NONE,           t_show_ovs},
        { FLAG,         "best",         F_CTL_ACTIVE,   t_show_rib},
        { FLAG,         "selected",     F_CTL_ACTIVE,   t_show_rib},
        { FLAG,         "detail",       F_CTL_DETAIL,   t_show_rib},
@@ -179,7 +184,8 @@ static const struct token t_show_rib[] =
        { KEYWORD,      "table",        NONE,           t_show_rib_rib},
        { KEYWORD,      "summary",      SHOW_SUMMARY,   t_show_summary},
        { KEYWORD,      "memory",       SHOW_RIB_MEM,   NULL},
-       { KEYWORD,      "ovs",          NONE,           t_show_ovs},
+       { KEYWORD,      "limit",        NONE,           t_limit},
+       { KEYWORD,      "token",        NONE,           t_token},
        { FAMILY,       "",             NONE,           t_show_rib},
        { PREFIX,       "",             NONE,           t_show_prefix},
        { ENDTOKEN,     "",             NONE,           NULL}
@@ -461,6 +467,16 @@ static const struct token t_weight[] = {
        { ENDTOKEN,     "",                     NONE,   NULL}
 };
 
+static const struct token t_limit[] = {
+       { LIMIT,        "",                     NONE,   t_show_rib},
+       { ENDTOKEN,     "",                     NONE,   NULL}
+};
+
+static const struct token t_token[] = {
+       { TOKEN,        "",             NONE,           t_show_rib},
+       { ENDTOKEN,     "",             NONE,           NULL}
+};
+
 static const struct token t_log[] = {
        { KEYWORD,      "verbose",      LOG_VERBOSE,    NULL},
        { KEYWORD,      "brief",        LOG_BRIEF,      NULL},
@@ -489,6 +505,7 @@ int parse_number(const char *, struct pa
 void   parsecommunity(struct community *c, int type, char *s);
 void   parseextcommunity(struct community *c, const char *t, char *s);
 int    parse_nexthop(const char *, struct parse_result *);
+int    parse_token(const char *, struct ctl_show_rib_token *);
 
 struct parse_result *
 parse(int argc, char *argv[])
@@ -561,6 +578,14 @@ match_token(int *argc, char **argv[], co
                                res.flags |= t->value;
                        }
                        break;
+               case ASTYPE:
+                       if (word != NULL && strncmp(word, table[i].keyword,
+                           wordlen) == 0) {
+                               match++;
+                               t = &table[i];
+                               res.as.type = t->value;
+                       }
+                       break;
                case FAMILY:
                        if (word == NULL)
                                break;
@@ -600,19 +625,12 @@ match_token(int *argc, char **argv[], co
                        }
                        break;
                case PREFIX:
-                       if (parse_prefix(word, wordlen, &res.addr, 
&res.prefixlen)) {
+                       if (parse_prefix(word, wordlen, &res.addr,
+                           &res.prefixlen)) {
                                match++;
                                t = &table[i];
                        }
                        break;
-               case ASTYPE:
-                       if (word != NULL && strncmp(word, table[i].keyword,
-                           wordlen) == 0) {
-                               match++;
-                               t = &table[i];
-                               res.as.type = t->value;
-                       }
-                       break;
                case ASNUM:
                        if (parse_asnum(word, wordlen, &res.as.as_min)) {
                                res.as.as_max = res.as.as_min;
@@ -745,6 +763,7 @@ match_token(int *argc, char **argv[], co
                case PREPNBR:
                case PREPSELF:
                case WEIGHT:
+               case LIMIT:
                case RTABLE:
                        if (word != NULL && wordlen > 0 &&
                            parse_number(word, &res, table[i].type)) {
@@ -790,6 +809,12 @@ match_token(int *argc, char **argv[], co
                                t = &table[i];
                        }
                        break;
+               case TOKEN:
+                       if (parse_token(word, &res.token)) {
+                               match++;
+                               t = &table[i];
+                       }
+                       break;
                case ENDTOKEN:
                        break;
                }
@@ -861,6 +886,7 @@ show_valid_args(const struct token table
                case PREPNBR:
                case PREPSELF:
                case WEIGHT:
+               case LIMIT:
                        fprintf(stderr, "  <number>\n");
                        break;
                case RTABLE:
@@ -879,6 +905,9 @@ show_valid_args(const struct token table
                case FILENAME:
                        fprintf(stderr, "  <filename>\n");
                        break;
+               case TOKEN:
+                       fprintf(stderr, "  <token>\n");
+                       break;
                case ENDTOKEN:
                        break;
                }
@@ -1017,9 +1046,15 @@ parse_number(const char *word, struct pa
                errx(1, "number is %s: %s", errstr, word);
 
        /* number was parseable */
-       if (type == RTABLE) {
+       switch (type) {
+       case RTABLE:
                r->rtableid = uval;
                return (1);
+       case LIMIT:
+               r->limit = uval;
+               return (1);
+       default:
+               break;
        }
 
        if ((fs = calloc(1, sizeof(struct filter_set))) == NULL)
@@ -1383,4 +1418,21 @@ parse_nexthop(const char *word, struct p
 
        TAILQ_INSERT_TAIL(&r->set, fs, entry);
        return (1);
+}
+
+int
+parse_token(const char *word, struct ctl_show_rib_token *t)
+{
+       int n;
+
+       memset(t, 0, sizeof(*t));
+       n = sscanf(word, "%d:%hhd:%u:%x:%x:%x:%x:%x:%x:%x:%x:%hhd", 
+           &t->peerid, &t->prefix.aid, &t->prefix.scope_id,
+           &t->prefix.addr32[0], &t->prefix.addr32[1], &t->prefix.addr32[2],
+           &t->prefix.addr32[3], &t->prefix.addr32[4], &t->prefix.addr32[5],
+           &t->prefix.addr32[6], &t->prefix.addr32[7], &t->prefixlen);
+
+       if (n == 12)
+               return (1);
+       return (0);
 }
Index: bgpctl/parser.h
===================================================================
RCS file: /cvs/src/usr.sbin/bgpctl/parser.h,v
retrieving revision 1.38
diff -u -p -r1.38 parser.h
--- bgpctl/parser.h     10 May 2020 13:38:46 -0000      1.38
+++ bgpctl/parser.h     12 May 2020 15:56:15 -0000
@@ -59,6 +59,7 @@ enum actions {
 struct parse_result {
        struct bgpd_addr         addr;
        struct bgpd_addr         peeraddr;
+       struct ctl_show_rib_token token;
        struct filter_as         as;
        struct filter_set_head   set;
        struct community         community;
@@ -67,14 +68,16 @@ struct parse_result {
        char                     reason[REASON_LEN];
        const char              *ext_comm_subtype;
        u_int64_t                rd;
+       u_int32_t                limit;
+       u_int                    rtableid;
        int                      flags;
        int                      is_group;
-       u_int8_t                 validation_state;
-       u_int                    rtableid;
+       int                      mrtfd;
        enum actions             action;
+       u_int8_t                 validation_state;
        u_int8_t                 prefixlen;
+       u_int8_t                 afterlen;
        u_int8_t                 aid;
-       int                      mrtfd;
 };
 
 __dead void             usage(void);
Index: bgpd/bgpd.h
===================================================================
RCS file: /cvs/src/usr.sbin/bgpd/bgpd.h,v
retrieving revision 1.403
diff -u -p -r1.403 bgpd.h
--- bgpd/bgpd.h 10 May 2020 13:38:46 -0000      1.403
+++ bgpd/bgpd.h 14 May 2020 15:08:02 -0000
@@ -466,6 +466,7 @@ enum imsg_type {
        IMSG_CTL_LOG_VERBOSE,
        IMSG_CTL_SHOW_FIB_TABLES,
        IMSG_CTL_TERMINATE,
+       IMSG_CTL_TOKEN,
        IMSG_NETWORK_ADD,
        IMSG_NETWORK_ASPATH,
        IMSG_NETWORK_ATTR,
@@ -801,18 +802,26 @@ struct community {
        u_int32_t       data3;
 };
 
+struct ctl_show_rib_token {
+       u_int32_t                       peerid;
+       struct bgpd_addr                prefix;
+       u_int8_t                        prefixlen;
+};
+
 struct ctl_show_rib_request {
-       char                    rib[PEER_DESCR_LEN];
-       struct ctl_neighbor     neighbor;
-       struct bgpd_addr        prefix;
-       struct filter_as        as;
-       struct community        community;
-       u_int32_t               flags;
-       u_int8_t                validation_state;
-       pid_t                   pid;
-       enum imsg_type          type;
-       u_int8_t                prefixlen;
-       u_int8_t                aid;
+       char                            rib[PEER_DESCR_LEN];
+       struct ctl_neighbor             neighbor;
+       struct bgpd_addr                prefix;
+       struct ctl_show_rib_token       token;
+       struct filter_as                as;
+       struct community                community;
+       u_int32_t                       limit;
+       u_int32_t                       flags;
+       u_int8_t                        validation_state;
+       pid_t                           pid;
+       enum imsg_type                  type;
+       u_int8_t                        prefixlen;
+       u_int8_t                        aid;
 };
 
 enum filter_actions {
Index: bgpd/mrt.c
===================================================================
RCS file: /cvs/src/usr.sbin/bgpd/mrt.c,v
retrieving revision 1.103
diff -u -p -r1.103 mrt.c
--- bgpd/mrt.c  9 Jan 2020 11:55:25 -0000       1.103
+++ bgpd/mrt.c  13 May 2020 09:35:43 -0000
@@ -687,7 +687,7 @@ fail:
        return (-1);
 }
 
-void
+int
 mrt_dump_upcall(struct rib_entry *re, void *ptr)
 {
        struct mrt              *mrtbuf = ptr;
@@ -695,7 +695,7 @@ mrt_dump_upcall(struct rib_entry *re, vo
 
        if (mrtbuf->type == MRT_TABLE_DUMP_V2) {
                mrt_dump_entry_v2(mrtbuf, re, mrtbuf->seqnum++);
-               return;
+               return 0;
        }
 
        /*
@@ -711,6 +711,8 @@ mrt_dump_upcall(struct rib_entry *re, vo
                        mrt_dump_entry_mp(mrtbuf, p, mrtbuf->seqnum++,
                            prefix_peer(p));
        }
+
+       return 0;
 }
 
 void
Index: bgpd/rde.c
===================================================================
RCS file: /cvs/src/usr.sbin/bgpd/rde.c,v
retrieving revision 1.502
diff -u -p -r1.502 rde.c
--- bgpd/rde.c  2 May 2020 14:12:17 -0000       1.502
+++ bgpd/rde.c  14 May 2020 12:59:50 -0000
@@ -73,10 +73,10 @@ void                 rde_reload_done(void);
 static void     rde_softreconfig_in_done(void *, u_int8_t);
 static void     rde_softreconfig_out_done(void *, u_int8_t);
 static void     rde_softreconfig_done(void);
-static void     rde_softreconfig_out(struct rib_entry *, void *);
-static void     rde_softreconfig_in(struct rib_entry *, void *);
-static void     rde_softreconfig_sync_reeval(struct rib_entry *, void *);
-static void     rde_softreconfig_sync_fib(struct rib_entry *, void *);
+static int      rde_softreconfig_out(struct rib_entry *, void *);
+static int      rde_softreconfig_in(struct rib_entry *, void *);
+static int      rde_softreconfig_sync_reeval(struct rib_entry *, void *);
+static int      rde_softreconfig_sync_fib(struct rib_entry *, void *);
 static void     rde_softreconfig_sync_done(void *, u_int8_t);
 int             rde_update_queue_pending(void);
 void            rde_update_queue_runner(void);
@@ -92,8 +92,8 @@ static void    rde_peer_send_eor(struct rd
 
 void            network_add(struct network_config *, struct filterstate *);
 void            network_delete(struct network_config *);
-static void     network_dump_upcall(struct rib_entry *, void *);
-static void     network_flush_upcall(struct rib_entry *, void *);
+static int      network_dump_upcall(struct rib_entry *, void *);
+static int      network_flush_upcall(struct rib_entry *, void *);
 
 void            rde_shutdown(void);
 int             ovs_match(struct prefix *, u_int32_t);
@@ -114,6 +114,7 @@ struct rde_dump_ctx {
        LIST_ENTRY(rde_dump_ctx)        entry;
        struct ctl_show_rib_request     req;
        u_int32_t                       peerid;
+       u_int32_t                       count;
        u_int8_t                        throttled;
 };
 
@@ -2295,109 +2296,154 @@ rde_match_peer(struct rde_peer *p, struc
        return 1;
 }
 
-static void
+static int
 rde_dump_filter(struct prefix *p, struct ctl_show_rib_request *req)
 {
        struct rde_aspath       *asp;
 
        if (!rde_match_peer(prefix_peer(p), &req->neighbor))
-               return;
+               return 0;
 
        asp = prefix_aspath(p);
        if (asp == NULL)        /* skip pending withdraw in Adj-RIB-Out */
-               return;
+               return 0;
        if ((req->flags & F_CTL_ACTIVE) && p->re->active != p)
-               return;
+               return 0;
        if ((req->flags & F_CTL_INVALID) &&
            (asp->flags & F_ATTR_PARSE_ERR) == 0)
-               return;
+               return 0;
        if (req->as.type != AS_UNDEF &&
            !aspath_match(asp->aspath, &req->as, 0))
-               return;
+               return 0;
        if (req->community.flags != 0) {
                if (!community_match(prefix_communities(p), &req->community,
                    NULL))
-                       return;
+                       return 0;
        }
        if (!ovs_match(p, req->flags))
-               return;
+               return 0;
+
        rde_dump_rib_as(p, asp, req->pid, req->flags);
+       return 1;
 }
 
-static void
+static int
+rde_dump_limit(struct prefix *p, struct rde_dump_ctx *ctx)
+{
+       struct ctl_show_rib_token       token;
+
+       ctx->count++;
+       if (ctx->req.limit != 0 && ctx->req.limit <= ctx->count) {
+               token.peerid = ctx->peerid;
+               pt_getaddr(p->pt, &token.prefix);
+               token.prefixlen = p->pt->prefixlen;
+               imsg_compose(ibuf_se_ctl, IMSG_CTL_TOKEN, 0,
+                   ctx->req.pid, -1, &token, sizeof(token));
+               return 1;
+       }
+
+       return 0;
+}
+
+static int
 rde_dump_upcall(struct rib_entry *re, void *ptr)
 {
        struct rde_dump_ctx     *ctx = ptr;
        struct prefix           *p;
+       int                      r = 0;
 
        LIST_FOREACH(p, &re->prefix_h, entry.list.rib)
-               rde_dump_filter(p, &ctx->req);
+               r |= rde_dump_filter(p, &ctx->req);
+
+       if (r && rde_dump_limit(LIST_FIRST(&re->prefix_h), ctx))
+               return 1;
+
+       return 0;
 }
 
-static void
+static int
 rde_dump_prefix_upcall(struct rib_entry *re, void *ptr)
 {
        struct rde_dump_ctx     *ctx = ptr;
        struct prefix           *p;
        struct pt_entry         *pt;
        struct bgpd_addr         addr;
+       int                      r = 0;
 
        pt = re->prefix;
        pt_getaddr(pt, &addr);
        if (addr.aid != ctx->req.prefix.aid)
-               return;
+               return 0;
        if (ctx->req.flags & F_LONGER) {
                if (ctx->req.prefixlen > pt->prefixlen)
-                       return;
+                       return 0;
                if (!prefix_compare(&ctx->req.prefix, &addr,
                    ctx->req.prefixlen))
                        LIST_FOREACH(p, &re->prefix_h, entry.list.rib)
-                               rde_dump_filter(p, &ctx->req);
+                               r |= rde_dump_filter(p, &ctx->req);
        } else {
                if (ctx->req.prefixlen < pt->prefixlen)
-                       return;
+                       return 0;
                if (!prefix_compare(&addr, &ctx->req.prefix,
                    pt->prefixlen))
                        LIST_FOREACH(p, &re->prefix_h, entry.list.rib)
-                               rde_dump_filter(p, &ctx->req);
+                               r |= rde_dump_filter(p, &ctx->req);
        }
+
+       if (r && rde_dump_limit(LIST_FIRST(&re->prefix_h), ctx))
+               return 1;
+
+       return 0;
 }
 
-static void
+static int
 rde_dump_adjout_upcall(struct prefix *p, void *ptr)
 {
        struct rde_dump_ctx     *ctx = ptr;
+       int                      r = 0;
 
        if (p->flags & (PREFIX_FLAG_WITHDRAW | PREFIX_FLAG_DEAD))
-               return;
-       rde_dump_filter(p, &ctx->req);
+               return 0;
+
+       r |= rde_dump_filter(p, &ctx->req);
+
+       if (r && rde_dump_limit(p, ctx))
+               return 1;
+
+       return 0;
 }
 
-static void
+static int
 rde_dump_adjout_prefix_upcall(struct prefix *p, void *ptr)
 {
        struct rde_dump_ctx     *ctx = ptr;
        struct bgpd_addr         addr;
+       int                      r = 0;
 
        if (p->flags & (PREFIX_FLAG_WITHDRAW | PREFIX_FLAG_DEAD))
-               return;
+               return 0;
 
        pt_getaddr(p->pt, &addr);
        if (addr.aid != ctx->req.prefix.aid)
-               return;
+               return 0;
        if (ctx->req.flags & F_LONGER) {
                if (ctx->req.prefixlen > p->pt->prefixlen)
-                       return;
+                       return 0;
                if (!prefix_compare(&ctx->req.prefix, &addr,
                    ctx->req.prefixlen))
-                       rde_dump_filter(p, &ctx->req);
+                       r |= rde_dump_filter(p, &ctx->req);
        } else {
                if (ctx->req.prefixlen < p->pt->prefixlen)
-                       return;
+                       return 0;
                if (!prefix_compare(&addr, &ctx->req.prefix,
                    p->pt->prefixlen))
-                       rde_dump_filter(p, &ctx->req);
+                       r |= rde_dump_filter(p, &ctx->req);
        }
+
+       if (r && rde_dump_limit(p, ctx))
+               return 1;
+
+       return 0;
 }
 
 static int
@@ -2415,6 +2461,9 @@ rde_dump_done(void *arg, u_int8_t aid)
        struct rde_peer         *peer;
        u_int                    error;
 
+       if (ctx->req.limit != 0 && ctx->req.limit <= ctx->count)
+               goto done;
+
        if (ctx->req.flags & F_CTL_ADJ_OUT) {
                peer = peer_match(&ctx->req.neighbor, ctx->peerid);
                if (peer == NULL)
@@ -2465,7 +2514,7 @@ rde_dump_ctx_new(struct ctl_show_rib_req
        u_int16_t                rid;
 
        if ((ctx = calloc(1, sizeof(*ctx))) == NULL) {
- nomem:
+nomem:
                log_warn(__func__);
                error = CTL_RES_NOMEM;
                imsg_compose(ibuf_se_ctl, IMSG_CTL_RESULT, 0, pid, -1, &error,
@@ -2482,7 +2531,10 @@ rde_dump_ctx_new(struct ctl_show_rib_req
        } else if (req->flags & F_CTL_ADJ_OUT) {
                struct rde_peer *peer;
 
-               peer = peer_match(&req->neighbor, 0);
+               if (req->token.peerid)
+                       peer = peer_get(req->token.peerid);
+               else
+                       peer = peer_match(&req->neighbor, req->token.peerid);
                if (peer == NULL) {
                        log_warnx("%s: no peer found for adj-rib-out",
                            __func__);
@@ -2492,19 +2544,35 @@ rde_dump_ctx_new(struct ctl_show_rib_req
                        free(ctx);
                        return;
                }
+               /* extract start point from token */
+               p = NULL;
+               if (req->token.prefix.aid != AID_UNSPEC) {
+                       if (ctx->req.aid != AID_UNSPEC &&
+                           ctx->req.aid != req->token.prefix.aid)
+                               /* bad address family */
+                               goto done;
+                       p = prefix_adjout_getnext(peer, &req->token.prefix,
+                           req->token.prefixlen);
+                       if (p == NULL) {
+                               peer = peer_match(&req->neighbor,
+                                   peer->conf.id);
+                               if (peer == NULL)
+                                       /* all peers done */
+                                       goto done;
+                       }
+               }
                ctx->peerid = peer->conf.id;
                switch (ctx->req.type) {
                case IMSG_CTL_SHOW_RIB:
-                       if (prefix_dump_new(peer, ctx->req.aid,
-                           CTL_MSG_HIGH_MARK, ctx, rde_dump_adjout_upcall,
-                           rde_dump_done, rde_dump_throttled) == -1)
+                       if (prefix_dump_restart(peer, ctx->req.aid, p, ctx,
+                           rde_dump_adjout_upcall, rde_dump_done,
+                           rde_dump_throttled) == -1)
                                goto nomem;
                        break;
                case IMSG_CTL_SHOW_RIB_PREFIX:
                        if (req->flags & (F_LONGER|F_SHORTER)) {
-                               if (prefix_dump_new(peer, ctx->req.aid,
-                                   CTL_MSG_HIGH_MARK, ctx,
-                                   rde_dump_adjout_prefix_upcall,
+                               if (prefix_dump_restart(peer, ctx->req.aid, p,
+                                   ctx, rde_dump_adjout_prefix_upcall,
                                    rde_dump_done, rde_dump_throttled) == -1)
                                        goto nomem;
                                break;
@@ -2522,6 +2590,8 @@ rde_dump_ctx_new(struct ctl_show_rib_req
                                fatalx("%s: unknown af", __func__);
                        }
 
+                       /* turn off limit, this is considered a single entry. */
+                       ctx->req.limit = 0;
                        do {
                                if (req->prefixlen == hostplen)
                                        p = prefix_match(peer, &req->prefix);
@@ -2533,10 +2603,7 @@ rde_dump_ctx_new(struct ctl_show_rib_req
                        } while ((peer = peer_match(&req->neighbor,
                            peer->conf.id)));
 
-                       imsg_compose(ibuf_se_ctl, IMSG_CTL_END, 0, ctx->req.pid,
-                           -1, NULL, 0);
-                       free(ctx);
-                       return;
+                       goto done;
                default:
                        fatalx("%s: unsupported imsg type", __func__);
                }
@@ -2552,23 +2619,36 @@ rde_dump_ctx_new(struct ctl_show_rib_req
                return;
        }
 
+       /* extract start point from token */
+       re = NULL;
+       if (req->token.prefix.aid != AID_UNSPEC) {
+               if (ctx->req.aid != AID_UNSPEC &&
+                   ctx->req.aid != req->token.prefix.aid)
+                       /* bad address family */
+                       goto done;
+               re = rib_getnext(rib_byid(rid), &req->token.prefix,
+                   req->token.prefixlen);
+               if (re == NULL)
+                       /* no next prefix found */
+                       goto done;
+       }
        switch (ctx->req.type) {
        case IMSG_CTL_SHOW_NETWORK:
-               if (rib_dump_new(rid, ctx->req.aid, CTL_MSG_HIGH_MARK, ctx,
+               if (rib_dump_restart(rid, ctx->req.aid, re, ctx,
                    network_dump_upcall, rde_dump_done,
                    rde_dump_throttled) == -1)
                        goto nomem;
                break;
        case IMSG_CTL_SHOW_RIB:
-               if (rib_dump_new(rid, ctx->req.aid, CTL_MSG_HIGH_MARK, ctx,
+               if (rib_dump_restart(rid, ctx->req.aid, re, ctx,
                    rde_dump_upcall, rde_dump_done, rde_dump_throttled) == -1)
                        goto nomem;
                break;
        case IMSG_CTL_SHOW_RIB_PREFIX:
                if (req->flags & (F_LONGER|F_SHORTER)) {
-                       if (rib_dump_new(rid, ctx->req.aid,
-                           CTL_MSG_HIGH_MARK, ctx, rde_dump_prefix_upcall,
-                           rde_dump_done, rde_dump_throttled) == -1)
+                       if (rib_dump_restart(rid, ctx->req.aid, re, ctx,
+                           rde_dump_prefix_upcall, rde_dump_done,
+                           rde_dump_throttled) == -1)
                                goto nomem;
                        break;
                }
@@ -2591,6 +2671,7 @@ rde_dump_ctx_new(struct ctl_show_rib_req
                            req->prefixlen);
                if (re)
                        rde_dump_upcall(re, ctx);
+done:
                imsg_compose(ibuf_se_ctl, IMSG_CTL_END, 0, ctx->req.pid,
                    -1, NULL, 0);
                free(ctx);
@@ -2796,10 +2877,11 @@ rde_generate_updates(struct rib *rib, st
        }
 }
 
-static void
+static int
 rde_up_flush_upcall(struct prefix *p, void *ptr)
 {
        up_generate_updates(out_rules, prefix_peer(p), NULL, p);
+       return 0;
 }
 
 u_char queue_buf[4096];
@@ -3299,7 +3381,7 @@ rde_softreconfig_done(void)
            -1, NULL, 0);
 }
 
-static void
+static int
 rde_softreconfig_in(struct rib_entry *re, void *bula)
 {
        struct filterstate       state;
@@ -3361,9 +3443,11 @@ rde_softreconfig_in(struct rib_entry *re
                        rde_filterstate_clean(&state);
                }
        }
+
+       return 0;
 }
 
-static void
+static int
 rde_softreconfig_out(struct rib_entry *re, void *bula)
 {
        struct prefix           *p = re->active;
@@ -3371,16 +3455,18 @@ rde_softreconfig_out(struct rib_entry *r
 
        if (p == NULL)
                /* no valid path for prefix */
-               return;
+               return 0;
 
        LIST_FOREACH(peer, &peerlist, peer_l) {
                if (peer->loc_rib_id == re->rib_id && peer->reconf_out)
                        /* Regenerate all updates. */
                        up_generate_updates(out_rules, peer, p, p);
        }
+
+       return 0;
 }
 
-static void
+static int
 rde_softreconfig_sync_reeval(struct rib_entry *re, void *arg)
 {
        struct prefix_list      prefixes;
@@ -3401,7 +3487,7 @@ rde_softreconfig_sync_reeval(struct rib_
                        rde_generate_updates(rib, NULL, re->active);
                        re->active = NULL;
                }
-               return;
+               return 0;
        }
 
        /* evaluation process is turned on, so evaluate all prefixes again */
@@ -3416,13 +3502,16 @@ rde_softreconfig_sync_reeval(struct rib_
                LIST_REMOVE(p, entry.list.rib);
                prefix_evaluate(p, re);
        }
+
+       return 0;
 }
 
-static void
+static int
 rde_softreconfig_sync_fib(struct rib_entry *re, void *bula)
 {
        if (re->active)
                rde_send_kroute(re_rib(re), re->active, NULL);
+       return 0;
 }
 
 static void
@@ -3688,7 +3777,7 @@ network_delete(struct network_config *nc
        rde_send_pftable_commit();
 }
 
-static void
+static int
 network_dump_upcall(struct rib_entry *re, void *ptr)
 {
        struct prefix           *p;
@@ -3720,9 +3809,11 @@ network_dump_upcall(struct rib_entry *re
                        log_warnx("network_dump_upcall: "
                            "imsg_compose error");
        }
+
+       return 0;
 }
 
-static void
+static int
 network_flush_upcall(struct rib_entry *re, void *ptr)
 {
        struct rde_peer *peer = ptr;
@@ -3733,9 +3824,9 @@ network_flush_upcall(struct rib_entry *r
 
        p = prefix_bypeer(re, peer);
        if (p == NULL)
-               return;
+               return 0;
        if ((prefix_aspath(p)->flags & F_ANN_DYNAMIC) != F_ANN_DYNAMIC)
-               return;
+               return 0;
 
        pt_getaddr(re->prefix, &addr);
        prefixlen = re->prefix->prefixlen;
@@ -3752,6 +3843,8 @@ network_flush_upcall(struct rib_entry *r
        if (prefix_withdraw(rib_byid(RIB_ADJ_IN), peer, &addr,
            prefixlen) == 1)
                peer->prefix_cnt--;
+
+       return 0;
 }
 
 /* clean up */
Index: bgpd/rde.h
===================================================================
RCS file: /cvs/src/usr.sbin/bgpd/rde.h,v
retrieving revision 1.233
diff -u -p -r1.233 rde.h
--- bgpd/rde.h  24 Jan 2020 05:44:05 -0000      1.233
+++ bgpd/rde.h  14 May 2020 11:50:37 -0000
@@ -357,7 +357,7 @@ extern struct rde_memstats rdemem;
 /* mrt.c */
 int            mrt_dump_v2_hdr(struct mrt *, struct bgpd_config *,
                    struct rde_peer_head *);
-void           mrt_dump_upcall(struct rib_entry *, void *);
+int            mrt_dump_upcall(struct rib_entry *, void *);
 
 /* rde.c */
 void            rde_update_err(struct rde_peer *, u_int8_t , u_int8_t,
@@ -541,11 +541,16 @@ u_int16_t  rib_find(char *);
 void            rib_free(struct rib *);
 void            rib_shutdown(void);
 struct rib_entry *rib_get(struct rib *, struct bgpd_addr *, int);
+struct rib_entry *rib_getnext(struct rib *, struct bgpd_addr *, int);
 struct rib_entry *rib_match(struct rib *, struct bgpd_addr *);
 int             rib_dump_pending(void);
 void            rib_dump_runner(void);
 int             rib_dump_new(u_int16_t, u_int8_t, unsigned int, void *,
-                   void (*)(struct rib_entry *, void *),
+                   int (*)(struct rib_entry *, void *),
+                   void (*)(void *, u_int8_t),
+                   int (*)(void *));
+int             rib_dump_restart(u_int16_t, u_int8_t, struct rib_entry *,
+                   void *, int (*)(struct rib_entry *, void *),
                    void (*)(void *, u_int8_t),
                    int (*)(void *));
 void            rib_dump_terminate(void *);
@@ -582,11 +587,16 @@ int                prefix_adjout_update(struct rde_pe
                    struct bgpd_addr *, int, u_int8_t);
 int             prefix_adjout_withdraw(struct rde_peer *, struct bgpd_addr *,
                    int);
+struct prefix  *prefix_adjout_getnext(struct rde_peer *, struct bgpd_addr *,
+                   int);
 void            prefix_adjout_destroy(struct prefix *p);
 void            prefix_adjout_dump(struct rde_peer *, void *,
                    void (*)(struct prefix *, void *));
 int             prefix_dump_new(struct rde_peer *, u_int8_t, unsigned int,
-                   void *, void (*)(struct prefix *, void *),
+                   void *, int (*)(struct prefix *, void *),
+                   void (*)(void *, u_int8_t), int (*)(void *));
+int             prefix_dump_restart(struct rde_peer *, u_int8_t,
+                   struct prefix *, void *, int (*)(struct prefix *, void *),
                    void (*)(void *, u_int8_t), int (*)(void *));
 int             prefix_write(u_char *, int, struct bgpd_addr *, u_int8_t, int);
 int             prefix_writebuf(struct ibuf *, struct bgpd_addr *, u_int8_t);
Index: bgpd/rde_peer.c
===================================================================
RCS file: /cvs/src/usr.sbin/bgpd/rde_peer.c,v
retrieving revision 1.5
diff -u -p -r1.5 rde_peer.c
--- bgpd/rde_peer.c     12 Feb 2020 10:33:56 -0000      1.5
+++ bgpd/rde_peer.c     14 May 2020 11:42:12 -0000
@@ -183,26 +183,28 @@ peer_add(u_int32_t id, struct peer_confi
 /*
  * Various RIB walker callbacks.
  */
-static void
+static int
 peer_adjout_clear_upcall(struct prefix *p, void *arg)
 {
        prefix_adjout_destroy(p);
+       return 0;
 }
 
-static void
+static int
 peer_adjout_stale_upcall(struct prefix *p, void *arg)
 {
        if (p->flags & PREFIX_FLAG_DEAD) {
-               return;
+               return 0;
        } else if (p->flags & PREFIX_FLAG_WITHDRAW) {
                /* no need to keep stale withdraws, they miss all attributes */
                prefix_adjout_destroy(p);
-               return;
+               return 0;
        } 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;
+       return 0;
 }
 
 struct peer_flush {
@@ -210,7 +212,7 @@ struct peer_flush {
        time_t           staletime;
 };
 
-static void
+static int
 peer_flush_upcall(struct rib_entry *re, void *arg)
 {
        struct rde_peer *peer = ((struct peer_flush *)arg)->peer;
@@ -250,9 +252,11 @@ peer_flush_upcall(struct rib_entry *re, 
                peer->prefix_cnt--;
                break;  /* optimization, only one match per peer possible */
        }
+
+       return 0;
 }
 
-static void
+static int
 rde_up_adjout_force_upcall(struct prefix *p, void *ptr)
 {
        if (p->flags & PREFIX_FLAG_STALE) {
@@ -267,6 +271,7 @@ rde_up_adjout_force_upcall(struct prefix
                    p) != NULL)
                        fatalx("%s: RB tree invariant violated", __func__);
        }
+       return 0;
 }
 
 static void
@@ -280,7 +285,7 @@ rde_up_adjout_force_done(void *ptr, u_in
                prefix_add_eor(peer, aid);
 }
 
-static void
+static int
 rde_up_dump_upcall(struct rib_entry *re, void *ptr)
 {
        struct rde_peer         *peer = ptr;
@@ -289,8 +294,9 @@ rde_up_dump_upcall(struct rib_entry *re,
                fatalx("%s: Unexpected RIB %u != %u.", __func__, re->rib_id,
                    peer->loc_rib_id);
        if (re->active == NULL)
-               return;
+               return 0;
        up_generate_updates(out_rules, peer, re->active, NULL);
+       return 0;
 }
 
 static void
Index: bgpd/rde_rib.c
===================================================================
RCS file: /cvs/src/usr.sbin/bgpd/rde_rib.c,v
retrieving revision 1.215
diff -u -p -r1.215 rde_rib.c
--- bgpd/rde_rib.c      25 Jan 2020 23:54:21 -0000      1.215
+++ bgpd/rde_rib.c      14 May 2020 11:58:30 -0000
@@ -54,8 +54,8 @@ struct rib_context {
        struct rib_entry                *ctx_re;
        struct prefix                   *ctx_p;
        u_int32_t                        ctx_id;
-       void            (*ctx_rib_call)(struct rib_entry *, void *);
-       void            (*ctx_prefix_call)(struct prefix *, void *);
+       int             (*ctx_rib_call)(struct rib_entry *, void *);
+       int             (*ctx_prefix_call)(struct prefix *, void *);
        void            (*ctx_done)(void *, u_int8_t);
        int             (*ctx_throttle)(void *);
        void                            *ctx_arg;
@@ -64,7 +64,7 @@ struct rib_context {
 };
 LIST_HEAD(, rib_context) rib_dumps = LIST_HEAD_INITIALIZER(rib_dumps);
 
-static void    prefix_dump_r(struct rib_context *);
+static void    prefix_dump_r(struct rib_context *, struct prefix *);
 
 static inline struct rib_entry *
 re_lock(struct rib_entry *re)
@@ -299,6 +299,7 @@ rib_shutdown(void)
        free(ribs);
 }
 
+/* Find rib_entry matching prefix. */
 struct rib_entry *
 rib_get(struct rib *rib, struct bgpd_addr *prefix, int prefixlen)
 {
@@ -316,6 +317,25 @@ rib_get(struct rib *rib, struct bgpd_add
        return re;
 }
 
+/* Find the next rib_entry that is grater than prefix. */
+struct rib_entry *
+rib_getnext(struct rib *rib, struct bgpd_addr *prefix, int prefixlen)
+{
+       struct rib_entry xre, *re;
+       struct pt_entry *pte;
+
+       pte = pt_fill(prefix, prefixlen);
+       memset(&xre, 0, sizeof(xre));
+       xre.prefix = pte;
+
+       re = RB_NFIND(rib_tree, rib_tree(rib), &xre);
+       if (re && rib_compare(re, &xre) == 0)
+               re = RB_NEXT(rib_tree, unused, re);
+
+       return re;
+}
+
+/* Find longest prefix matching addr. */
 struct rib_entry *
 rib_match(struct rib *rib, struct bgpd_addr *addr)
 {
@@ -419,22 +439,25 @@ rib_restart(struct rib_context *ctx)
 }
 
 static void
-rib_dump_r(struct rib_context *ctx)
+rib_dump_r(struct rib_context *ctx, struct rib_entry *start)
 {
        struct rib_entry        *re, *next;
        struct rib              *rib;
-       unsigned int             i;
+       unsigned int             i = 0;
 
        rib = rib_byid(ctx->ctx_id);
        if (rib == NULL)
                fatalx("%s: rib id %u gone", __func__, ctx->ctx_id);
 
-       if (ctx->ctx_re == NULL)
+       if (start != NULL) {
+               re = start;
+               i = ctx->ctx_count;     /* park call as soon as possible */
+       } else if (ctx->ctx_re == NULL)
                re = RB_MIN(rib_tree, rib_tree(rib));
        else
                re = rib_restart(ctx);
 
-       for (i = 0; re != NULL; re = next) {
+       for (; re != NULL; re = next) {
                next = RB_NEXT(rib_tree, unused, re);
                if (re->rib_id != ctx->ctx_id)
                        fatalx("%s: Unexpected RIB %u != %u.", __func__,
@@ -448,7 +471,8 @@ rib_dump_r(struct rib_context *ctx)
                        ctx->ctx_re = re_lock(re);
                        return;
                }
-               ctx->ctx_rib_call(re, ctx->ctx_arg);
+               if (ctx->ctx_rib_call(re, ctx->ctx_arg))
+                       break;
        }
 
        if (ctx->ctx_done)
@@ -480,9 +504,9 @@ rib_dump_runner(void)
                if (ctx->ctx_throttle && ctx->ctx_throttle(ctx->ctx_arg))
                        continue;
                if (ctx->ctx_rib_call != NULL)
-                       rib_dump_r(ctx);
+                       rib_dump_r(ctx, NULL);
                else
-                       prefix_dump_r(ctx);
+                       prefix_dump_r(ctx, NULL);
        }
 }
 
@@ -526,7 +550,7 @@ rib_dump_terminate(void *arg)
 
 int
 rib_dump_new(u_int16_t id, u_int8_t aid, unsigned int count, void *arg,
-    void (*upcall)(struct rib_entry *, void *), void (*done)(void *, u_int8_t),
+    int (*upcall)(struct rib_entry *, void *), void (*done)(void *, u_int8_t),
     int (*throttle)(void *))
 {
        struct rib_context *ctx;
@@ -545,7 +569,32 @@ rib_dump_new(u_int16_t id, u_int8_t aid,
 
        /* requested a sync traversal */
        if (count == 0)
-               rib_dump_r(ctx);
+               rib_dump_r(ctx, NULL);
+
+       return 0;
+}
+
+int
+rib_dump_restart(u_int16_t id, u_int8_t aid, struct rib_entry *re, void *arg,
+    int (*upcall)(struct rib_entry *, void *), void (*done)(void *, u_int8_t),
+    int (*throttle)(void *))
+{
+       struct rib_context *ctx;
+
+       if ((ctx = calloc(1, sizeof(*ctx))) == NULL)
+               return -1;
+       ctx->ctx_id = id;
+       ctx->ctx_aid = aid;
+       ctx->ctx_count = CTL_MSG_HIGH_MARK;
+       ctx->ctx_arg = arg;
+       ctx->ctx_rib_call = upcall;
+       ctx->ctx_done = done;
+       ctx->ctx_throttle = throttle;
+
+       LIST_INSERT_HEAD(&rib_dumps, ctx, entry);
+
+       if (re)
+               rib_dump_r(ctx, re);
 
        return 0;
 }
@@ -895,7 +944,7 @@ RB_GENERATE(prefix_tree, prefix, entry.t
 RB_GENERATE_STATIC(prefix_index, prefix, entry.tree.index, prefix_index_cmp)
 
 /*
- * search for specified prefix of a peer. Returns NULL if not found.
+ * Search for specified prefix of a peer. Returns NULL if not found.
  */
 struct prefix *
 prefix_get(struct rib *rib, struct rde_peer *peer, struct bgpd_addr *prefix,
@@ -1275,6 +1324,29 @@ prefix_adjout_withdraw(struct rde_peer *
        return (1);
 }
 
+/*
+ * Search for the next prefix after the one specified.
+ * Returns NULL if not found.
+ */
+struct prefix *
+prefix_adjout_getnext(struct rde_peer *peer, struct bgpd_addr *prefix,
+    int prefixlen)
+{
+       struct prefix xp, *p;
+       struct pt_entry *pte;
+
+       memset(&xp, 0, sizeof(xp));
+       pte = pt_fill(prefix, prefixlen);
+       xp.pt = pte;
+
+       p = RB_FIND(prefix_index, &peer->adj_rib_out, &xp);
+       if (p && prefix_index_cmp(p, &xp) == 0)
+               p = RB_NEXT(prefix_index, unused, p);
+
+       return p;
+}
+
+
 static struct prefix *
 prefix_restart(struct rib_context *ctx)
 {
@@ -1337,21 +1409,24 @@ prefix_adjout_destroy(struct prefix *p)
 }
 
 static void
-prefix_dump_r(struct rib_context *ctx)
+prefix_dump_r(struct rib_context *ctx, struct prefix *start)
 {
        struct prefix *p, *next;
        struct rde_peer *peer;
-       unsigned int i;
+       unsigned int i = 0;
 
        if ((peer = peer_get(ctx->ctx_id)) == NULL)
                goto done;
 
-       if (ctx->ctx_p == NULL)
+       if (start != NULL) {
+               p = start;
+               i = ctx->ctx_count;     /* park call as soon as possible */
+       } else if (ctx->ctx_p == NULL)
                p = RB_MIN(prefix_index, &peer->adj_rib_out);
        else
                p = prefix_restart(ctx);
 
-       for (i = 0; p != NULL; p = next) {
+       for (; p != NULL; p = next) {
                next = RB_NEXT(prefix_index, unused, p);
                if (prefix_is_dead(p))
                        continue;
@@ -1364,7 +1439,8 @@ prefix_dump_r(struct rib_context *ctx)
                        ctx->ctx_p = prefix_lock(p);
                        return;
                }
-               ctx->ctx_prefix_call(p, ctx->ctx_arg);
+               if (ctx->ctx_prefix_call(p, ctx->ctx_arg))
+                       break;
        }
 
 done:
@@ -1376,7 +1452,7 @@ done:
 
 int
 prefix_dump_new(struct rde_peer *peer, u_int8_t aid, unsigned int count,
-    void *arg, void (*upcall)(struct prefix *, void *),
+    void *arg, int (*upcall)(struct prefix *, void *),
     void (*done)(void *, u_int8_t), int (*throttle)(void *))
 {
        struct rib_context *ctx;
@@ -1395,7 +1471,33 @@ prefix_dump_new(struct rde_peer *peer, u
 
        /* requested a sync traversal */
        if (count == 0)
-               prefix_dump_r(ctx);
+               prefix_dump_r(ctx, NULL);
+
+       return 0;
+}
+
+int
+prefix_dump_restart(struct rde_peer *peer, u_int8_t aid, struct prefix *p,
+    void *arg, int (*upcall)(struct prefix *, void *),
+    void (*done)(void *, u_int8_t), int (*throttle)(void *))
+{
+       struct rib_context *ctx;
+
+       if ((ctx = calloc(1, sizeof(*ctx))) == NULL)
+               return -1;
+       ctx->ctx_id = peer->conf.id;
+       ctx->ctx_aid = aid;
+       ctx->ctx_count = CTL_MSG_HIGH_MARK;
+       ctx->ctx_arg = arg;
+       ctx->ctx_prefix_call = upcall;
+       ctx->ctx_done = done;
+       ctx->ctx_throttle = throttle;
+
+       LIST_INSERT_HEAD(&rib_dumps, ctx, entry);
+
+       /* requested a sync traversal */
+       if (p)
+               prefix_dump_r(ctx, p);
 
        return 0;
 }
Index: bgpd/session.c
===================================================================
RCS file: /cvs/src/usr.sbin/bgpd/session.c,v
retrieving revision 1.401
diff -u -p -r1.401 session.c
--- bgpd/session.c      10 May 2020 13:38:46 -0000      1.401
+++ bgpd/session.c      12 May 2020 15:53:59 -0000
@@ -2829,6 +2829,7 @@ session_dispatch_imsg(struct imsgbuf *ib
                                fatalx("ctl rib request not from RDE");
                        control_imsg_relay(&imsg);
                        break;
+               case IMSG_CTL_TOKEN:
                case IMSG_CTL_END:
                case IMSG_CTL_RESULT:
                        control_imsg_relay(&imsg);

Reply via email to