This diff add JSON output support for bgpctl.
Most commands should produce now a resonable JSON object.
The individual objects can probably be improved and extended.
I'm at a point where I'm happy with the result and looking for feedback.
It is possible to commit the non-JSON bits first and add output-json.c
once the output is enough stable.

-- 
:wq Claudio

Index: Makefile
===================================================================
RCS file: /cvs/src/usr.sbin/bgpctl/Makefile,v
retrieving revision 1.16
diff -u -p -r1.16 Makefile
--- Makefile    20 Dec 2019 09:16:05 -0000      1.16
+++ Makefile    30 Apr 2020 12:24:42 -0000
@@ -3,7 +3,7 @@
 .PATH:         ${.CURDIR}/../bgpd
 
 PROG=  bgpctl
-SRCS=  bgpctl.c output.c parser.c mrtparser.c util.c
+SRCS=  bgpctl.c output.c output_json.c parser.c mrtparser.c util.c json.c
 CFLAGS+= -Wall
 CFLAGS+= -Wstrict-prototypes -Wmissing-prototypes
 CFLAGS+= -Wmissing-declarations
Index: bgpctl.c
===================================================================
RCS file: /cvs/src/usr.sbin/bgpctl/bgpctl.c,v
retrieving revision 1.259
diff -u -p -r1.259 bgpctl.c
--- bgpctl.c    20 Mar 2020 07:56:34 -0000      1.259
+++ bgpctl.c    30 Apr 2020 15:45:05 -0000
@@ -60,6 +60,7 @@ int            match_aspath(void *, u_int16_t, st
 struct imsgbuf *ibuf;
 struct mrt_parser show_mrt = { show_mrt_dump, show_mrt_state, show_mrt_msg };
 struct mrt_parser net_mrt = { network_mrt_dump, NULL, NULL };
+const struct output    *output = &show_output;
 int tableid;
 int nodescr;
 
@@ -68,7 +69,7 @@ usage(void)
 {
        extern char     *__progname;
 
-       fprintf(stderr, "usage: %s [-n] [-s socket] command [argument ...]\n",
+       fprintf(stderr, "usage: %s [-jn] [-s socket] command [argument ...]\n",
            __progname);
        exit(1);
 }
@@ -93,12 +94,15 @@ main(int argc, char *argv[])
        if (asprintf(&sockname, "%s.%d", SOCKET_NAME, tableid) == -1)
                err(1, "asprintf");
 
-       while ((ch = getopt(argc, argv, "ns:")) != -1) {
+       while ((ch = getopt(argc, argv, "jns:")) != -1) {
                switch (ch) {
                case 'n':
                        if (++nodescr > 1)
                                usage();
                        break;
+               case 'j':
+                       output = &json_output;
+                       break;
                case 's':
                        sockname = optarg;
                        break;
@@ -139,7 +143,7 @@ main(int argc, char *argv[])
                if (res->flags & F_CTL_NEIGHBORS)
                        show_mrt.dump = show_mrt_dump_neighbors;
                else
-                       show_head(res);
+                       output->head(res);
                mrt_parse(res->mrtfd, &show_mrt, 1);
                exit(0);
        default:
@@ -351,7 +355,7 @@ main(int argc, char *argv[])
                if (msgbuf_write(&ibuf->w) <= 0 && errno != EAGAIN)
                        err(1, "write error");
 
-       show_head(res);
+       output->head(res);
 
        while (!done) {
                if ((n = imsg_read(ibuf)) == -1 && errno != EAGAIN)
@@ -369,6 +373,9 @@ main(int argc, char *argv[])
                        imsg_free(&imsg);
                }
        }
+
+       output->tail();
+
        close(fd);
        free(ibuf);
 
@@ -394,33 +401,33 @@ show(struct imsg *imsg, struct parse_res
        switch (imsg->hdr.type) {
        case IMSG_CTL_SHOW_NEIGHBOR:
                p = imsg->data;
-               show_neighbor(p, res);
+               output->neighbor(p, res);
                break;
        case IMSG_CTL_SHOW_TIMER:
                t = imsg->data;
                if (t->type > 0 && t->type < Timer_Max)
-                       show_timer(t);
+                       output->timer(t);
                break;
        case IMSG_CTL_SHOW_INTERFACE:
                iface = imsg->data;
-               show_interface(iface);
+               output->interface(iface);
                break;
        case IMSG_CTL_SHOW_NEXTHOP:
                nh = imsg->data;
-               show_nexthop(nh);
+               output->nexthop(nh);
                break;
        case IMSG_CTL_KROUTE:
        case IMSG_CTL_SHOW_NETWORK:
                if (imsg->hdr.len < IMSG_HEADER_SIZE + sizeof(*kf))
                        errx(1, "wrong imsg len");
                kf = imsg->data;
-               show_fib(kf);
+               output->fib(kf);
                break;
        case IMSG_CTL_SHOW_FIB_TABLES:
                if (imsg->hdr.len < IMSG_HEADER_SIZE + sizeof(*kt))
                        errx(1, "wrong imsg len");
                kt = imsg->data;
-               show_fib_table(kt);
+               output->fib_table(kt);
                break;
        case IMSG_CTL_SHOW_RIB:
                if (imsg->hdr.len < IMSG_HEADER_SIZE + sizeof(rib))
@@ -429,7 +436,7 @@ show(struct imsg *imsg, struct parse_res
                aslen = imsg->hdr.len - IMSG_HEADER_SIZE - sizeof(rib);
                asdata = imsg->data;
                asdata += sizeof(rib);
-               show_rib(&rib, asdata, aslen, res);
+               output->rib(&rib, asdata, aslen, res);
                break;
        case IMSG_CTL_SHOW_RIB_COMMUNITIES:
                ilen = imsg->hdr.len - IMSG_HEADER_SIZE;
@@ -437,7 +444,7 @@ show(struct imsg *imsg, struct parse_res
                        warnx("bad IMSG_CTL_SHOW_RIB_COMMUNITIES received");
                        break;
                }
-               show_communities(imsg->data, ilen, res);
+               output->communities(imsg->data, ilen, res);
                break;
        case IMSG_CTL_SHOW_RIB_ATTR:
                ilen = imsg->hdr.len - IMSG_HEADER_SIZE;
@@ -445,15 +452,15 @@ show(struct imsg *imsg, struct parse_res
                        warnx("bad IMSG_CTL_SHOW_RIB_ATTR received");
                        break;
                }
-               show_attr(imsg->data, ilen, res);
+               output->attr(imsg->data, ilen, res);
                break;
        case IMSG_CTL_SHOW_RIB_MEM:
                memcpy(&stats, imsg->data, sizeof(stats));
-               show_rib_mem(&stats);
+               output->rib_mem(&stats);
                break;
        case IMSG_CTL_SHOW_RIB_HASH:
                memcpy(&hash, imsg->data, sizeof(hash));
-               show_rib_hash(&hash);
+               output->rib_hash(&hash);
                break;
        case IMSG_CTL_RESULT:
                if (imsg->hdr.len != IMSG_HEADER_SIZE + sizeof(rescode)) {
@@ -461,7 +468,7 @@ show(struct imsg *imsg, struct parse_res
                        break;
                }
                memcpy(&rescode, imsg->data, sizeof(rescode));
-               show_result(rescode);
+               output->result(rescode);
                return (1);
        case IMSG_CTL_END:
                return (1);
@@ -762,7 +769,7 @@ fmt_errstr(u_int8_t errcode, u_int8_t su
 }
 
 const char *
-fmt_attr(u_int8_t type, u_int8_t flags)
+fmt_attr(u_int8_t type, int flags)
 {
 #define CHECK_FLAGS(s, t, m)   \
        if (((s) & ~(ATTR_DEFMASK | (m))) != (t)) pflags = 1
@@ -841,7 +848,7 @@ fmt_attr(u_int8_t type, u_int8_t flags)
                pflags = 1;
                break;
        }
-       if (pflags) {
+       if (flags != -1 && pflags) {
                strlcat(cstr, " flags [", sizeof(cstr));
                if (flags & ATTR_OPTIONAL)
                        strlcat(cstr, "O", sizeof(cstr));
@@ -1123,10 +1130,10 @@ show_mrt_dump(struct mrt_rib *mr, struct
                    !match_aspath(mre->aspath, mre->aspath_len, &req->as))
                        continue;
 
-               show_rib(&ctl, mre->aspath, mre->aspath_len, &res);
+               output->rib(&ctl, mre->aspath, mre->aspath_len, &res);
                if (req->flags & F_CTL_DETAIL) {
                        for (j = 0; j < mre->nattrs; j++)
-                               show_attr(mre->attrs[j].attr,
+                               output->attr(mre->attrs[j].attr,
                                    mre->attrs[j].attr_len, &res);
                }
        }
@@ -1509,6 +1516,7 @@ show_mrt_notification(u_char *p, u_int16
        }
 }
 
+/* XXX this function does not handle JSON output */
 static void
 show_mrt_update(u_char *p, u_int16_t len)
 {
@@ -1579,7 +1587,7 @@ show_mrt_update(u_char *p, u_int16_t len
                        attrlen += 1 + 2;
                }
 
-               show_attr(p, attrlen, 0);
+               output->attr(p, attrlen, 0);
                p += attrlen;
                alen -= attrlen;
                len -= attrlen;
Index: bgpctl.h
===================================================================
RCS file: /cvs/src/usr.sbin/bgpctl/bgpctl.h,v
retrieving revision 1.4
diff -u -p -r1.4 bgpctl.h
--- bgpctl.h    20 Mar 2020 07:56:34 -0000      1.4
+++ bgpctl.h    30 Apr 2020 16:06:20 -0000
@@ -15,21 +15,27 @@
  */
 
 struct parse_result;
-void   show_head(struct parse_result *);
-void   show_neighbor(struct peer *, struct parse_result *);
-void   show_timer(struct ctl_timer *);
-void   show_fib(struct kroute_full *);
-void   show_fib_table(struct ktable *);
-void   show_nexthop(struct ctl_show_nexthop *);
-void   show_interface(struct ctl_show_interface *);
-void   show_attr(u_char *, size_t, struct parse_result *);
-void   show_communities(u_char *, size_t, struct parse_result *);
-void   show_rib(struct ctl_show_rib *, u_char *, size_t,
-           struct parse_result *);
-void   show_rib_hash(struct rde_hashstats *);
-void   show_rib_mem(struct rde_memstats *);
-void   show_result(u_int);
 
+struct output {
+       void    (*head)(struct parse_result *);
+       void    (*neighbor)(struct peer *, struct parse_result *);
+       void    (*timer)(struct ctl_timer *);
+       void    (*fib)(struct kroute_full *);
+       void    (*fib_table)(struct ktable *);
+       void    (*nexthop)(struct ctl_show_nexthop *);
+       void    (*interface)(struct ctl_show_interface *);
+       void    (*attr)(u_char *, size_t, struct parse_result *);
+       void    (*communities)(u_char *, size_t, struct parse_result *);
+       void    (*rib)(struct ctl_show_rib *, u_char *, size_t,
+                   struct parse_result *);
+       void    (*rib_hash)(struct rde_hashstats *);
+       void    (*rib_mem)(struct rde_memstats *);
+       void    (*result)(u_int);
+       void    (*tail)(void);
+};
+
+extern const struct output show_output, json_output;
+extern const size_t pt_sizes[];
 
 #define EOL0(flag)     ((flag & F_CTL_SSV) ? ';' : '\n')
 
@@ -43,8 +49,7 @@ const char    *fmt_ovs(u_int8_t, int);
 const char     *fmt_auth_method(enum auth_method);
 const char     *fmt_mem(long long);
 const char     *fmt_errstr(u_int8_t, u_int8_t);
-const char     *fmt_attr(u_int8_t, u_int8_t);
+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 *);
-
Index: json.c
===================================================================
RCS file: json.c
diff -N json.c
--- /dev/null   1 Jan 1970 00:00:00 -0000
+++ json.c      30 Apr 2020 16:43:41 -0000
@@ -0,0 +1,224 @@
+/*
+ * Copyright (c) 2020 Claudio Jeker <[email protected]>
+ *
+ * Permission to use, copy, modify, and distribute this software for any
+ * purpose with or without fee is hereby granted, provided that the above
+ * copyright notice and this permission notice appear in all copies.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
+ * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
+ * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
+ * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
+ * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
+ * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+ */
+
+#include <err.h>
+#include <stdarg.h>
+#include <stdint.h>
+#include <stdio.h>
+#include <string.h>
+
+#include "json.h"
+
+#define JSON_MAX_STACK 16
+
+enum json_type {
+       NONE,
+       START,
+       ARRAY,
+       OBJECT
+};
+
+struct json_stack {
+       const char      *name;
+       unsigned int    count;
+       enum json_type  type;
+} stack[JSON_MAX_STACK];
+
+char indent[JSON_MAX_STACK + 1];
+int level;
+
+static void
+do_coma_indent(void)
+{
+       if (stack[level].count++ > 0)
+               printf(",\n");
+       printf("\t%.*s", level, indent);
+}
+
+static void
+do_name(const char *name)
+{
+       if (stack[level].type == ARRAY)
+               return;
+       printf("\"%s\": ", name);
+}
+
+static int
+do_find(enum json_type type, const char *name)
+{
+       int i;
+
+       for (i = level; i > 0; i--)
+               if (type == stack[i].type &&
+                   strcmp(name, stack[i].name) == 0)
+                       return i;
+
+       /* not found */
+       return -1;
+}
+
+void
+json_do_start(void)
+{
+       memset(indent, '\t', JSON_MAX_STACK);
+       memset(stack, 0, sizeof(stack));
+       level = 0;
+       stack[level].type = START;
+
+       printf("{\n");
+}
+
+void
+json_do_finish(void)
+{
+       while (level > 0)
+               json_do_end();
+       printf("\n}\n");
+}
+
+void
+json_do_array(const char *name)
+{
+       int i, l;
+
+       if ((l = do_find(ARRAY, name)) > 0) {
+               /* array already in use, close element and move on */
+               for (i = level - l; i > 0; i--)
+                       json_do_end();
+               return;
+       }
+       /* Do not stack arrays, while allowed this is not needed */
+       if (stack[level].type == ARRAY)
+               json_do_end();
+
+       do_coma_indent();
+       do_name(name);
+       printf("[\n");
+
+       if (++level >= JSON_MAX_STACK)
+               errx(1, "json stack too deep");
+       
+       stack[level].name = name;
+       stack[level].type = ARRAY;
+       stack[level].count = 0;
+}
+
+void
+json_do_object(const char *name)
+{
+       int i, l;
+
+       if ((l = do_find(OBJECT, name)) > 0) {
+               /* roll back to that object and close it */
+               for (i = level - l; i >= 0; i--)
+                       json_do_end();
+       }
+
+       do_coma_indent();
+       do_name(name);
+       printf("{\n");
+
+       if (++level >= JSON_MAX_STACK)
+               errx(1, "json stack too deep");
+       
+       stack[level].name = name;
+       stack[level].type = OBJECT;
+       stack[level].count = 0;
+}
+
+void
+json_do_end(void)
+{
+       if (stack[level].type == ARRAY)
+               printf("\n%.*s]", level, indent);
+       else if (stack[level].type == OBJECT)
+               printf("\n%.*s}", level, indent);
+       else
+               errx(1, "json bad stack state");
+
+       stack[level].name = NULL;
+       stack[level].type = NONE;
+       stack[level].count = 0;
+
+       if (level-- <= 0)
+               errx(1, "json stack underflow");
+
+       stack[level].count++;
+}
+
+void
+json_do_printf(const char *name, const char *fmt, ...)
+{
+       va_list ap;
+
+       do_coma_indent();
+
+       do_name(name);
+       printf("\"");
+       va_start(ap, fmt);
+       vprintf(fmt, ap);
+       va_end(ap);
+       printf("\"");
+}
+
+void
+json_do_hexdump(const char *name, void *buf, size_t len)
+{
+       uint8_t *data = buf;
+       size_t i;
+
+       do_coma_indent();
+       do_name(name);
+       printf("\"");
+       for (i=0; i < len; i++)
+               printf("%02x", *(data+i));
+       printf("\"");
+}
+
+void
+json_do_bool(const char *name, int v)
+{
+       do_coma_indent();
+       do_name(name);
+       if (v)
+               printf("true");
+       else
+               printf("false");
+}
+
+void
+json_do_uint(const char *name, uint64_t v)
+{
+       do_coma_indent();
+       do_name(name);
+       printf("%llu", v);
+}
+
+void
+json_do_int(const char *name, int64_t v)
+{
+       do_coma_indent();
+       do_name(name);
+       printf("%lld", name, v);
+}
+
+void
+json_do_double(const char *name, double v)
+{
+       do_coma_indent();
+       do_name(name);
+       printf("%f", v);
+}
Index: json.h
===================================================================
RCS file: json.h
diff -N json.h
--- /dev/null   1 Jan 1970 00:00:00 -0000
+++ json.h      30 Apr 2020 16:03:33 -0000
@@ -0,0 +1,31 @@
+/*
+ * Copyright (c) 2020 Claudio Jeker <[email protected]>
+ *
+ * Permission to use, copy, modify, and distribute this software for any
+ * purpose with or without fee is hereby granted, provided that the above
+ * copyright notice and this permission notice appear in all copies.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
+ * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
+ * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
+ * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
+ * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
+ * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+ */
+
+#include <stdarg.h>
+#include <sys/cdefs.h>
+
+void json_do_start(void);
+void json_do_finish(void);
+void json_do_array(const char *);
+void json_do_object(const char *);
+void json_do_end(void);
+void json_do_printf(const char *, const char *, ...)
+       __attribute__((__format__ (printf, 2, 3)));
+void json_do_hexdump(const char *, void *, size_t);
+void json_do_bool(const char *, int);
+void json_do_uint(const char *, uint64_t);
+void json_do_int(const char *, int64_t);
+void json_do_double(const char *, double);
Index: output.c
===================================================================
RCS file: /cvs/src/usr.sbin/bgpctl/output.c,v
retrieving revision 1.6
diff -u -p -r1.6 output.c
--- output.c    20 Mar 2020 07:56:34 -0000      1.6
+++ output.c    1 May 2020 11:04:45 -0000
@@ -32,7 +32,9 @@
 #include "bgpctl.h"
 #include "parser.h"
 
-void
+const size_t  pt_sizes[AID_MAX] = AID_PTSIZE;
+
+static void
 show_head(struct parse_result *res)
 {
        switch (res->action) {
@@ -200,6 +202,7 @@ show_neighbor_msgstats(struct peer *p)
 static void
 show_neighbor_full(struct peer *p, struct parse_result *res)
 {
+       const char              *errstr;
        struct in_addr           ina;
        char                    *s;
        int                      hascapamp = 0;
@@ -216,7 +219,6 @@ show_neighbor_full(struct peer *p, struc
        } else if ((s = strdup(log_addr(&p->conf.remote_addr))) == NULL)
                        err(1, "strdup");
 
-       ina.s_addr = p->remote_bgpid;
        printf("BGP neighbor is %s, ", s);
        free(s);
        if (p->conf.remote_as == 0 && p->conf.template)
@@ -249,16 +251,19 @@ show_neighbor_full(struct peer *p, struc
        if (p->conf.max_prefix || p->conf.max_out_prefix)
                printf("\n");
 
-       printf("  BGP version 4, remote router-id %s",
-           inet_ntoa(ina));
-       printf("%s\n", fmt_auth_method(p->auth.method));
+       if (p->state == STATE_ESTABLISHED) {
+               ina.s_addr = p->remote_bgpid;
+               printf("  BGP version 4, remote router-id %s",
+                   inet_ntoa(ina));
+               printf("%s\n", fmt_auth_method(p->auth.method));
+       }
        printf("  BGP state = %s", statenames[p->state]);
        if (p->conf.down) {
                printf(", marked down");
-               if (*(p->conf.shutcomm)) {
-                       printf(" with shutdown reason \"%s\"",
-                           log_shutcomm(p->conf.shutcomm));
-               }
+       }
+       if (*(p->conf.shutcomm)) {
+               printf(" with shutdown reason \"%s\"",
+                   log_shutcomm(p->conf.shutcomm));
        }
        if (p->stats.last_updown != 0)
                printf(", %s for %s",
@@ -295,18 +300,17 @@ show_neighbor_full(struct peer *p, struc
                printf("  Last received shutdown reason: \"%s\"\n",
                    log_shutcomm(p->stats.last_shutcomm));
        }
-       if (p->state == STATE_IDLE) {
-               const char *errstr;
 
-               errstr = fmt_errstr(p->stats.last_sent_errcode,
-                   p->stats.last_sent_suberr);
-               if (errstr)
-                       printf("  Last error sent: %s\n\n", errstr);
-               errstr = fmt_errstr(p->stats.last_rcvd_errcode,
-                   p->stats.last_rcvd_suberr);
-               if (errstr)
-                       printf("  Last error received: %s\n\n", errstr);
-       } else {
+       errstr = fmt_errstr(p->stats.last_sent_errcode,
+           p->stats.last_sent_suberr);
+       if (errstr)
+               printf("  Last error sent: %s\n", errstr);
+       errstr = fmt_errstr(p->stats.last_rcvd_errcode,
+           p->stats.last_rcvd_suberr);
+       if (errstr)
+               printf("  Last error received: %s\n", errstr);
+
+       if (p->state >= STATE_OPENSENT) {
                printf("  Local host:  %20s, Local port:  %5u\n",
                    log_addr(&p->local), p->local_port);
 
@@ -316,7 +320,7 @@ show_neighbor_full(struct peer *p, struc
        }
 }
 
-void
+static void
 show_neighbor(struct peer *p, struct parse_result *res)
 {
        char *s;
@@ -360,7 +364,7 @@ show_neighbor(struct peer *p, struct par
        }
 }
 
-void
+static void
 show_timer(struct ctl_timer *t)
 {
        printf("  %-20s ", timernames[t->type]);
@@ -371,7 +375,7 @@ show_timer(struct ctl_timer *t)
                printf("due in %-13s\n", fmt_timeframe(t->val));
 }
 
-void
+static void
 show_fib(struct kroute_full *kf)
 {
        char                    *p;
@@ -388,7 +392,7 @@ show_fib(struct kroute_full *kf)
        printf("\n");
 }
 
-void
+static void
 show_fib_table(struct ktable *kt)
 {
        printf("%5i %-20s %-8s%s\n", kt->rtableid, kt->descr,
@@ -396,7 +400,7 @@ show_fib_table(struct ktable *kt)
            kt->fib_sync != kt->fib_conf ? "*" : "");
 }
 
-void
+static void
 show_nexthop(struct ctl_show_nexthop *nh)
 {
        struct kroute           *k;
@@ -445,7 +449,7 @@ show_nexthop(struct ctl_show_nexthop *nh
        printf("\n");
 }
 
-void
+static void
 show_interface(struct ctl_show_interface *iface)
 {
        printf("%-15s", iface->ifname);
@@ -462,13 +466,13 @@ show_interface(struct ctl_show_interface
        printf("\n");
 }
 
-void
+static void
 show_communities(u_char *data, size_t len, struct parse_result *res)
 {
        struct community c;
        size_t  i;
        u_int64_t ext;
-       u_int8_t type = 0, nt;
+       u_int8_t type = 0;
 
        if (len % sizeof(c))
                return;
@@ -476,16 +480,15 @@ show_communities(u_char *data, size_t le
        for (i = 0; i < len; i += sizeof(c)) {
                memcpy(&c, data + i, sizeof(c));
 
-               nt = c.flags;
-               if (type != nt) {
+               if (type != c.flags) {
                        if (type != 0)
                                printf("%c", EOL0(res->flags));
-                       printf("    %s:", fmt_attr(nt,
+                       printf("    %s:", fmt_attr(c.flags,
                            ATTR_OPTIONAL | ATTR_TRANSITIVE));
-                       type = nt;
+                       type = c.flags;
                }
 
-               switch (nt) {
+               switch (c.flags) {
                case COMMUNITY_TYPE_BASIC:
                        printf(" %s", fmt_community(c.data1, c.data2));
                        break;
@@ -525,8 +528,10 @@ show_community(u_char *data, u_int16_t l
        u_int16_t       a, v;
        u_int16_t       i;
 
-       if (len & 0x3)
+       if (len & 0x3) {
+               printf("bad length");
                return;
+       }
 
        for (i = 0; i < len; i += 4) {
                memcpy(&a, data + i, sizeof(a));
@@ -546,8 +551,10 @@ show_large_community(u_char *data, u_int
        u_int32_t       a, l1, l2;
        u_int16_t       i;
 
-       if (len % 12)
+       if (len % 12) {
+               printf("bad length");
                return;
+       }
 
        for (i = 0; i < len; i += 12) {
                memcpy(&a, data + i, sizeof(a));
@@ -556,7 +563,7 @@ show_large_community(u_char *data, u_int
                a = ntohl(a);
                l1 = ntohl(l1);
                l2 = ntohl(l2);
-                       printf("%s", fmt_large_community(a, l1, l2));
+               printf("%s", fmt_large_community(a, l1, l2));
 
                if (i + 12 < len)
                        printf(" ");
@@ -568,8 +575,10 @@ show_ext_community(u_char *data, u_int16
 {
        u_int16_t       i;
 
-       if (len & 0x7)
+       if (len & 0x7) {
+               printf("bad length");
                return;
+       }
 
        for (i = 0; i < len; i += 8) {
                printf("%s", fmt_ext_community(data + i));
@@ -579,7 +588,7 @@ show_ext_community(u_char *data, u_int16
        }
 }
 
-void
+static void
 show_attr(u_char *data, size_t len, struct parse_result *res)
 {
        u_char          *path;
@@ -591,16 +600,20 @@ show_attr(u_char *data, size_t len, stru
        u_int8_t         flags, type, safi, aid, prefixlen;
        int              i, pos, e2, e4;
 
-       if (len < 3)
-               errx(1, "show_attr: too short bgp attr");
+       if (len < 3) {
+               warnx("Too short BGP attrbute");
+               return;
+       }
 
        flags = data[0];
        type = data[1];
 
        /* get the attribute length */
        if (flags & ATTR_EXTLEN) {
-               if (len < 4)
-                       errx(1, "show_attr: too short bgp attr");
+               if (len < 4) {
+                       warnx("Too short BGP attrbute");
+                       return;
+               }
                memcpy(&alen, data+2, sizeof(u_int16_t));
                alen = ntohs(alen);
                data += 4;
@@ -612,15 +625,17 @@ show_attr(u_char *data, size_t len, stru
        }
 
        /* bad imsg len how can that happen!? */
-       if (alen > len)
-               errx(1, "show_attr: bad length");
+       if (alen > len) {
+               warnx("Bad BGP attrbute length");
+               return;
+       }
 
        printf("    %s: ", fmt_attr(type, flags));
 
        switch (type) {
        case ATTR_ORIGIN:
                if (alen == 1)
-                       printf("%u", *data);
+                       printf("%s", fmt_origin(*data, 0));
                else
                        printf("bad length");
                break;
@@ -683,8 +698,11 @@ show_attr(u_char *data, size_t len, stru
                show_community(data, alen);
                break;
        case ATTR_ORIGINATOR_ID:
-               memcpy(&id, data, sizeof(id));
-               printf("%s", inet_ntoa(id));
+               if (alen == 4) {
+                       memcpy(&id, data, sizeof(id));
+                       printf("%s", inet_ntoa(id));
+               } else
+                       printf("bad length");
                break;
        case ATTR_CLUSTER_LIST:
                for (ioff = 0; ioff + sizeof(id) <= alen;
@@ -722,7 +740,7 @@ show_attr(u_char *data, size_t len, stru
                        alen--;
                        if (nhlen > len)
                                goto bad_len;
-                       bzero(&nexthop, sizeof(nexthop));
+                       memset(&nexthop, 0, sizeof(nexthop));
                        switch (aid) {
                        case AID_INET6:
                                nexthop.aid = aid;
@@ -737,6 +755,13 @@ show_attr(u_char *data, size_t len, stru
                                memcpy(&nexthop.v4, data + sizeof(u_int64_t),
                                    sizeof(nexthop.v4));
                                break;
+                       case AID_VPN_IPv6:
+                               if (nhlen != 24)
+                                       goto bad_len;
+                               nexthop.aid = AID_INET6;
+                               memcpy(&nexthop.v6, data + sizeof(u_int64_t),
+                                   sizeof(nexthop.v6));
+                               break;
                        default:
                                printf("unhandled AID #%u", aid);
                                goto done;
@@ -758,6 +783,10 @@ show_attr(u_char *data, size_t len, stru
                                pos = nlri_get_vpn4(data, alen, &prefix,
                                    &prefixlen, 1);
                                break;
+                       case AID_VPN_IPv6:
+                               pos = nlri_get_vpn6(data, alen, &prefix,
+                                   &prefixlen, 1);
+                               break;
                        default:
                                printf("unhandled AID #%u", aid);
                                goto done;
@@ -845,7 +874,7 @@ show_rib_detail(struct ctl_show_rib *r, 
            fmt_timeframe(r->age), EOL0(flag0));
 }
 
-void
+static void
 show_rib(struct ctl_show_rib *r, u_char *asdata, size_t aslen,
     struct parse_result *res)
 {
@@ -855,10 +884,9 @@ show_rib(struct ctl_show_rib *r, u_char 
                show_rib_brief(r, asdata, aslen);
 }
 
-void
+static void
 show_rib_mem(struct rde_memstats *stats)
 {
-       static size_t  pt_sizes[AID_MAX] = AID_PTSIZE;
        size_t                  pts = 0;
        int                     i;
 
@@ -915,7 +943,7 @@ show_rib_mem(struct rde_memstats *stats)
        printf("\nRDE hash statistics\n");
 }
 
-void
+static void
 show_rib_hash(struct rde_hashstats *hash)
 {
        double avg, dev;
@@ -928,16 +956,37 @@ show_rib_hash(struct rde_hashstats *hash
            hash->min, hash->max, avg, dev);
 }
 
-void
+static void
 show_result(u_int rescode)
 {
        if (rescode == 0)
                printf("request processed\n");
-       else {
-               if (rescode >
-                   sizeof(ctl_res_strerror)/sizeof(ctl_res_strerror[0]))
-                       printf("unknown result error code %u\n", rescode);
-               else
-                       printf("%s\n", ctl_res_strerror[rescode]);
-       }
+       else if (rescode >
+           sizeof(ctl_res_strerror)/sizeof(ctl_res_strerror[0]))
+               printf("unknown result error code %u\n", rescode);
+       else
+               printf("%s\n", ctl_res_strerror[rescode]);
 }
+
+static void
+show_tail(void)
+{
+       /* nothing */
+}
+
+const struct output show_output = {
+       .head = show_head,
+       .neighbor = show_neighbor,
+       .timer = show_timer,
+       .fib = show_fib,
+       .fib_table = show_fib_table,
+       .nexthop = show_nexthop,
+       .interface = show_interface,
+       .communities = show_communities,
+       .attr = show_attr,
+       .rib = show_rib,
+       .rib_mem = show_rib_mem,
+       .rib_hash = show_rib_hash,
+       .result = show_result,
+       .tail = show_tail
+};
Index: output_json.c
===================================================================
RCS file: output_json.c
diff -N output_json.c
--- /dev/null   1 Jan 1970 00:00:00 -0000
+++ output_json.c       1 May 2020 11:04:25 -0000
@@ -0,0 +1,954 @@
+/*     $OpenBSD: output.c,v 1.6 2020/03/20 07:56:34 claudio Exp $ */
+
+/*
+ * Copyright (c) 2020 Claudio Jeker <[email protected]>
+ *
+ * Permission to use, copy, modify, and distribute this software for any
+ * purpose with or without fee is hereby granted, provided that the above
+ * copyright notice and this permission notice appear in all copies.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
+ * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
+ * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
+ * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
+ * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
+ * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+ */
+
+#include <err.h>
+#include <math.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+
+#include "bgpd.h"
+#include "session.h"
+#include "rde.h"
+
+#include "bgpctl.h"
+#include "parser.h"
+#include "json.h"
+
+static void
+json_head(struct parse_result *res)
+{
+       json_do_start();
+}
+
+static void
+json_neighbor_capabilities(struct capabilities *capa)
+{
+       int hascapamp;
+       uint8_t i;
+
+       for (i = 0; i < AID_MAX; i++)
+               if (capa->mp[i])
+                       hascapamp = 1;
+       if (!hascapamp && !capa->refresh && !capa->grestart.restart &&
+           !capa->as4byte)
+               return;
+
+       json_do_object("capabilities");
+       json_do_bool("as4byte", capa->as4byte);
+       json_do_bool("refresh", capa->refresh);
+
+       if (hascapamp) {
+               json_do_array("multiprotocol");
+               for (i = 0; i < AID_MAX; i++)
+                       if (capa->mp[i])
+                               json_do_printf("mp", "%s", aid2str(i));
+               json_do_end();
+       }
+       if (capa->grestart.restart) {
+               int restarted = 0, present = 0;
+
+               for (i = 0; i < AID_MAX; i++)
+                       if (capa->grestart.flags[i] & CAPA_GR_PRESENT) {
+                               present = 1;
+                               if (capa->grestart.flags[i] & CAPA_GR_RESTART)
+                                       restarted = 1;
+                               break;
+                       }
+               json_do_object("graceful_restart");
+               json_do_bool("eor", 1);
+               json_do_bool("restart", restarted);
+
+               if (capa->grestart.timeout)
+                       json_do_uint("timeout", capa->grestart.timeout);
+
+               if (present) {
+                       json_do_array("protocols");
+                       for (i = 0; i < AID_MAX; i++)
+                               if (capa->grestart.flags[i] & CAPA_GR_PRESENT) {
+                                       json_do_object("family");
+                                       json_do_printf("family", "%s",
+                                           aid2str(i));
+                                       json_do_bool("preserved",
+                                           capa->grestart.flags[i] &
+                                           CAPA_GR_FORWARD);
+                                       json_do_end();
+                               }
+                       json_do_end();
+               }
+
+               json_do_end();
+       }
+
+       json_do_end();
+}
+
+static void
+json_neighbor_stats(struct peer *p)
+{
+       json_do_object("stats");
+       json_do_printf("last_read", "%s", fmt_monotime(p->stats.last_read));
+       json_do_printf("last_write", "%s", fmt_monotime(p->stats.last_write));
+
+       json_do_object("prefixes");
+       json_do_uint("sent", p->stats.prefix_out_cnt);
+       json_do_uint("recieved", p->stats.prefix_cnt);
+       json_do_end();
+
+       json_do_object("message");
+
+       json_do_object("sent");
+       json_do_uint("open", p->stats.msg_sent_open);
+       json_do_uint("notifications", p->stats.msg_sent_notification);
+       json_do_uint("updates", p->stats.msg_sent_update);
+       json_do_uint("keepalives", p->stats.msg_sent_keepalive);
+       json_do_uint("route_refresh", p->stats.msg_sent_rrefresh);
+       json_do_uint("total",
+           p->stats.msg_sent_open + p->stats.msg_sent_notification +
+           p->stats.msg_sent_update + p->stats.msg_sent_keepalive +
+           p->stats.msg_sent_rrefresh);
+       json_do_end();
+
+       json_do_object("received");
+       json_do_uint("open", p->stats.msg_rcvd_open);
+       json_do_uint("notifications", p->stats.msg_rcvd_notification);
+       json_do_uint("updates", p->stats.msg_rcvd_update);
+       json_do_uint("keepalives", p->stats.msg_rcvd_keepalive);
+       json_do_uint("route_refresh", p->stats.msg_rcvd_rrefresh);
+       json_do_uint("total",
+           p->stats.msg_rcvd_open + p->stats.msg_rcvd_notification +
+           p->stats.msg_rcvd_update + p->stats.msg_rcvd_keepalive +
+           p->stats.msg_rcvd_rrefresh);
+       json_do_end();
+
+       json_do_end();
+
+       json_do_object("update");
+
+       json_do_object("sent");
+       json_do_uint("updates", p->stats.prefix_sent_update);
+       json_do_uint("withdraws", p->stats.prefix_sent_withdraw);
+       json_do_uint("eor", p->stats.prefix_sent_eor);
+       json_do_end();
+
+       json_do_object("received");
+       json_do_uint("updates", p->stats.prefix_rcvd_update);
+       json_do_uint("withdraws", p->stats.prefix_rcvd_withdraw);
+       json_do_uint("eor", p->stats.prefix_rcvd_eor);
+       json_do_end();
+
+       json_do_end();
+
+       json_do_end();
+}
+
+static void
+json_neighbor_full(struct peer *p)
+{
+       const char *errstr;
+
+       /* config */
+       json_do_object("config");
+       json_do_bool("template", p->conf.template);
+       json_do_bool("cloned", p->template != NULL);
+       json_do_bool("passive", p->conf.passive);
+       json_do_bool("down", p->conf.down);
+       json_do_bool("multihop", p->conf.ebgp && p->conf.distance > 1);
+       if (p->conf.ebgp && p->conf.distance > 1)
+               json_do_uint("multihop_distance", p->conf.distance);
+       if (p->conf.max_prefix) {
+               json_do_uint("max_prefix", p->conf.max_prefix);
+               if (p->conf.max_prefix_restart)
+                       json_do_uint("max_prefix_restart",
+                           p->conf.max_prefix_restart);
+       }
+       if (p->conf.max_out_prefix) {
+               json_do_uint("max_prefix", p->conf.max_out_prefix);
+               if (p->conf.max_out_prefix_restart)
+                       json_do_uint("max_out_prefix_restart",
+                           p->conf.max_out_prefix_restart);
+       }
+       if (p->auth.method != AUTH_NONE)
+               json_do_printf("authentication", "%s",
+                   fmt_auth_method(p->auth.method));
+       json_do_bool("ttl_security", p->conf.ttlsec);
+       json_do_uint("holdtime", p->conf.holdtime);
+       json_do_uint("min_holdtime", p->conf.min_holdtime);
+
+       /* capabilities */
+       json_do_bool("announce_capabilities", p->conf.announce_capa);
+       json_neighbor_capabilities(&p->conf.capabilities);
+
+       json_do_end();
+
+
+       /* stats */
+       json_neighbor_stats(p);
+
+       /* errors */
+       if (*(p->conf.shutcomm))
+               json_do_printf("my_shutdown_reason", "%s",
+                   log_shutcomm(p->conf.shutcomm));
+       if (*(p->stats.last_shutcomm))
+               json_do_printf("last_shutdown_reason", "%s",
+                   log_shutcomm(p->stats.last_shutcomm));
+       errstr = fmt_errstr(p->stats.last_sent_errcode,
+           p->stats.last_sent_suberr);
+       if (errstr)
+               json_do_printf("last_error_sent", "%s", errstr);
+       errstr = fmt_errstr(p->stats.last_rcvd_errcode,
+           p->stats.last_rcvd_suberr);
+       if (errstr)
+               json_do_printf("last_error_received", "%s", errstr);
+
+       /* connection info */
+       if (p->state >= STATE_OPENSENT) {
+               json_do_object("session");
+               json_do_uint("holdtime", p->holdtime);
+               json_do_uint("keepalive", p->holdtime / 3);
+
+               json_do_object("local");
+               json_do_printf("address", "%s", log_addr(&p->local));
+               json_do_uint("port", p->local_port);
+               json_neighbor_capabilities(&p->capa.ann);
+               json_do_end();
+
+               json_do_object("remote");
+               json_do_printf("address", "%s", log_addr(&p->remote));
+               json_do_uint("port", p->remote_port);
+               json_neighbor_capabilities(&p->capa.peer);
+               json_do_end();
+
+               /* capabilities */
+               json_neighbor_capabilities(&p->capa.neg);
+
+               json_do_end();
+       }
+}
+
+static void
+json_neighbor(struct peer *p, struct parse_result *res)
+{
+       json_do_array("neighbors");
+
+       json_do_object("neighbor");
+
+       json_do_printf("remote_as", "%s", log_as(p->conf.remote_as));
+       if (p->conf.descr[0])
+               json_do_printf("description", "%s", p->conf.descr);
+       if (!p->conf.template)
+               json_do_printf("remote_addr", "%s",
+                   log_addr(&p->conf.remote_addr));
+       else
+               json_do_printf("remote_addr", "%s/%u",
+                   log_addr(&p->conf.remote_addr), p->conf.remote_masklen);
+       if (p->state == STATE_ESTABLISHED) {
+               struct in_addr ina;
+               ina.s_addr = p->remote_bgpid;
+               json_do_printf("bgpid", "%s", inet_ntoa(ina));
+       }
+       json_do_printf("state", "%s", statenames[p->state]);
+       json_do_printf("last_updown", "%s", fmt_monotime(p->stats.last_updown));
+
+       switch (res->action) {
+       case SHOW:
+       case SHOW_SUMMARY:
+       case SHOW_SUMMARY_TERSE:
+               /* only show basic data */
+               break;
+       case SHOW_NEIGHBOR:
+       case SHOW_NEIGHBOR_TIMERS:
+       case SHOW_NEIGHBOR_TERSE:
+               json_neighbor_full(p);
+               break;
+       default:
+               break;
+       }
+
+       /* keep the object open in case there are timers */
+}
+
+static void
+json_timer(struct ctl_timer *t)
+{
+       json_do_array("timers");
+
+       json_do_object("timer");
+       json_do_printf("name", "%s", timernames[t->type]);
+       json_do_int("due", t->val);
+       json_do_end();
+}
+
+static void
+json_fib(struct kroute_full *kf)
+{
+       const char *origin;
+
+       json_do_array("fib");
+
+       json_do_object("fib_entry");
+
+       json_do_printf("prefix", "%s/%u", log_addr(&kf->prefix), kf->prefixlen);
+       json_do_uint("priority", kf->priority);
+       json_do_bool("up", !(kf->flags & F_DOWN));
+       if (kf->flags & F_BGPD_INSERTED)
+               origin = "bgp";
+       else if (kf->flags & F_CONNECTED)
+               origin = "connected";
+       else if (kf->flags & F_STATIC)
+               origin = "static";
+       else if (kf->flags & F_DYNAMIC)
+               origin = "dynamic";
+       else
+               origin = "unknown";
+       json_do_printf("origin", "%s", origin);
+       json_do_bool("used_by_nexthop", kf->flags & F_NEXTHOP);
+       json_do_bool("blackhole", kf->flags & F_BLACKHOLE);
+       json_do_bool("reject", kf->flags & F_REJECT);
+
+       if (kf->flags & F_CONNECTED)
+               json_do_printf("nexthop", "link#%u", kf->ifindex);
+       else
+               json_do_printf("nexthop", "%s", log_addr(&kf->nexthop));
+
+       json_do_end();
+}
+
+static void
+json_fib_table(struct ktable *kt)
+{
+       json_do_array("fibtables");
+
+       json_do_object("fibtable");
+       json_do_uint("rtableid", kt->rtableid);
+       json_do_printf("description", "%s", kt->descr);
+       json_do_bool("coupled", kt->fib_sync);
+       json_do_bool("admin_change", kt->fib_sync != kt->fib_conf);
+       json_do_end();
+}
+
+static void
+json_do_interface(struct ctl_show_interface *iface)
+{
+       json_do_object("interface");
+
+       json_do_printf("name", "%s", iface->ifname);
+       json_do_uint("rdomain", iface->rdomain);
+       json_do_bool("is_up", iface->is_up);
+       json_do_bool("nh_reachable", iface->nh_reachable);
+
+       if (iface->media[0])
+               json_do_printf("media", "%s", iface->media);
+
+       json_do_printf("linkstate", "%s", iface->linkstate);
+       if (iface->baudrate > 0)
+               json_do_uint("baudrate", iface->baudrate);
+
+       json_do_end();
+}
+
+static void
+json_nexthop(struct ctl_show_nexthop *nh)
+{
+       struct kroute *k;
+       struct kroute6 *k6;
+
+       json_do_array("nexthops");
+
+       json_do_object("nexthop");
+
+       json_do_printf("address", "%s", log_addr(&nh->addr));
+       json_do_bool("valid", nh->valid);
+
+       if (!nh->krvalid)
+               goto done;
+
+       switch (nh->addr.aid) {
+       case AID_INET:
+               k = &nh->kr.kr4;
+               json_do_printf("prefix", "%s/%u", inet_ntoa(k->prefix),
+                   k->prefixlen);
+               json_do_uint("priority", k->priority);
+               json_do_bool("connected", k->flags & F_CONNECTED);
+               json_do_printf("nexthop", "%s", inet_ntoa(k->nexthop));
+               break;
+       case AID_INET6:
+               k6 = &nh->kr.kr6;
+               json_do_printf("prefix", "%s/%u", log_in6addr(&k6->prefix),
+                   k6->prefixlen);
+               json_do_uint("priority", k6->priority);
+               json_do_bool("connected", k6->flags & F_CONNECTED);
+               json_do_printf("nexthop", "%s", log_in6addr(&k6->nexthop));
+               break;
+       default:
+               warnx("nexthop: unknown address family");
+               goto done;
+       }
+       if (nh->iface.ifname[0])
+               json_do_interface(&nh->iface);
+done:
+       json_do_end();
+       /* keep array open */
+}
+
+static void
+json_interface(struct ctl_show_interface *iface)
+{
+       json_do_array("interfaces");
+       json_do_interface(iface);
+}
+
+static void
+json_communities(u_char *data, size_t len, struct parse_result *res)
+{
+       struct community c;
+       size_t  i;
+       uint64_t ext;
+
+       if (len % sizeof(c)) {
+               warnx("communities: bad size");
+               return;
+       }
+
+       for (i = 0; i < len; i += sizeof(c)) {
+               memcpy(&c, data + i, sizeof(c));
+
+               switch (c.flags) {
+               case COMMUNITY_TYPE_BASIC:
+                       json_do_array("communities");
+                       json_do_printf("community", "%s",
+                           fmt_community(c.data1, c.data2));
+                       break;
+               case COMMUNITY_TYPE_LARGE:
+                       json_do_array("large_communities");
+                       json_do_printf("community", "%s",
+                           fmt_large_community(c.data1, c.data2, c.data3));
+                       break;
+               case COMMUNITY_TYPE_EXT:
+                       ext = (uint64_t)c.data3 << 48;
+                       switch (c.data3 >> 8) {
+                       case EXT_COMMUNITY_TRANS_TWO_AS:
+                       case EXT_COMMUNITY_TRANS_OPAQUE:
+                       case EXT_COMMUNITY_TRANS_EVPN:
+                       case EXT_COMMUNITY_NON_TRANS_OPAQUE:
+                               ext |= ((uint64_t)c.data1 & 0xffff) << 32;
+                               ext |= (uint64_t)c.data2;
+                               break;
+                       case EXT_COMMUNITY_TRANS_FOUR_AS:
+                       case EXT_COMMUNITY_TRANS_IPV4:
+                               ext |= (uint64_t)c.data1 << 16;
+                               ext |= (uint64_t)c.data2 & 0xffff;
+                               break;
+                       }
+                       ext = htobe64(ext);
+
+                       json_do_array("extended_communities");
+                       json_do_printf("community", "%s",
+                           fmt_ext_community((void *)&ext));
+                       break;
+               }
+       }
+}
+
+static void
+json_do_community(u_char *data, uint16_t len)
+{
+       uint16_t a, v, i;
+
+       if (len & 0x3) {
+               json_do_printf("error", "bad length");
+               return;
+       }
+
+       json_do_array("communities");
+
+       for (i = 0; i < len; i += 4) {
+               memcpy(&a, data + i, sizeof(a));
+               memcpy(&v, data + i + 2, sizeof(v));
+               a = ntohs(a);
+               v = ntohs(v);
+               json_do_printf("community", "%s", fmt_community(a, v));
+       }
+
+       json_do_end();
+}
+
+static void
+json_do_large_community(u_char *data, uint16_t len)
+{
+       uint32_t a, l1, l2;
+       u_int16_t i;
+
+       if (len % 12) {
+               json_do_printf("error", "bad length");
+               return;
+       }
+
+       json_do_array("large_communities");
+
+       for (i = 0; i < len; i += 12) {
+               memcpy(&a, data + i, sizeof(a));
+               memcpy(&l1, data + i + 4, sizeof(l1));
+               memcpy(&l2, data + i + 8, sizeof(l2));
+               a = ntohl(a);
+               l1 = ntohl(l1);
+               l2 = ntohl(l2);
+
+               json_do_printf("community", "%s",
+                   fmt_large_community(a, l1, l2));
+       }
+
+       json_do_end();
+}
+
+static void
+json_do_ext_community(u_char *data, uint16_t len)
+{
+       uint16_t i;
+
+       if (len & 0x7) {
+               json_do_printf("error", "bad length");
+               return;
+       }
+
+       json_do_array("extended_communities");
+
+       for (i = 0; i < len; i += 8)
+               json_do_printf("community", "%s", fmt_ext_community(data + i));
+
+       json_do_end();
+}
+
+static void
+json_attr(u_char *data, size_t len, struct parse_result *res)
+{
+       struct bgpd_addr prefix;
+       struct in_addr id;
+       char *aspath;
+       u_char *path;
+       uint32_t as;
+       uint16_t alen, afi, off, short_as;
+       uint8_t flags, type, safi, aid, prefixlen;
+       int e4, e2, pos;
+
+       if (len < 3) {
+               warnx("Too short BGP attrbute");
+               return;
+       }
+
+       flags = data[0];
+       type = data[1];
+       if (flags & ATTR_EXTLEN) {
+               if (len < 4) {
+                       warnx("Too short BGP attrbute");
+                       return;
+               }
+               memcpy(&alen, data+2, sizeof(uint16_t));
+               alen = ntohs(alen);
+               data += 4;
+               len -= 4;
+       } else {
+               alen = data[2];
+               data += 3;
+               len -= 3;
+       }
+
+       /* bad imsg len how can that happen!? */
+       if (alen > len) {
+               warnx("Bad BGP attrbute length");
+               return;
+       }
+
+       json_do_array("attributes");
+
+       json_do_object("attribute");
+       json_do_printf("type", "%s", fmt_attr(type, -1));
+       json_do_uint("length", alen);
+       json_do_object("flags");
+       json_do_bool("partial", flags & ATTR_PARTIAL);
+       json_do_bool("transitive", flags & ATTR_TRANSITIVE);
+       json_do_bool("optional", flags & ATTR_OPTIONAL);
+       json_do_end();
+
+       switch (type) {
+       case ATTR_ORIGIN:
+               if (alen == 1)
+                       json_do_printf("origin", "%s", fmt_origin(*data, 0));
+               else
+                       json_do_printf("error", "bad length");
+               break;
+       case ATTR_ASPATH:
+       case ATTR_AS4_PATH:
+               /* prefer 4-byte AS here */
+               e4 = aspath_verify(data, alen, 1);
+               e2 = aspath_verify(data, alen, 0);
+               if (e4 == 0 || e4 == AS_ERR_SOFT) {
+                       path = data;
+               } else if (e2 == 0 || e2 == AS_ERR_SOFT) {
+                       path = aspath_inflate(data, alen, &alen);
+                       if (path == NULL)
+                               errx(1, "aspath_inflate failed");
+               } else {
+                       json_do_printf("error", "bad AS-Path");
+                       break;
+               }
+               if (aspath_asprint(&aspath, path, alen) == -1)
+                       err(1, NULL);
+               json_do_printf("aspath", "%s", aspath);
+               free(aspath);
+               if (path != data)
+                       free(path);
+               break;
+       case ATTR_NEXTHOP:
+               if (alen == 4) {
+                       memcpy(&id, data, sizeof(id));
+                       json_do_printf("nexthop", "%s", inet_ntoa(id));
+               } else
+                       json_do_printf("error", "bad length");
+               break;
+       case ATTR_MED:
+       case ATTR_LOCALPREF:
+               if (alen == 4) {
+                       uint32_t val;
+                       memcpy(&val, data, sizeof(val));
+                       json_do_uint("metric", ntohl(val));
+               } else
+                       json_do_printf("error", "bad length");
+               break;
+       case ATTR_AGGREGATOR:
+       case ATTR_AS4_AGGREGATOR:
+               if (alen == 8) {
+                       memcpy(&as, data, sizeof(as));
+                       memcpy(&id, data + sizeof(as), sizeof(id));
+                       as = ntohl(as);
+               } else if (alen == 6) {
+                       memcpy(&short_as, data, sizeof(short_as));
+                       memcpy(&id, data + sizeof(short_as), sizeof(id));
+                       as = ntohs(short_as);
+               } else {
+                       json_do_printf("error", "bad AS-Path");
+                       break;
+               }
+               json_do_uint("AS", as);
+               json_do_printf("router_id", "%s", inet_ntoa(id));
+               break;
+       case ATTR_COMMUNITIES:
+               json_do_community(data, alen);
+               break;
+       case ATTR_ORIGINATOR_ID:
+               if (alen == 4) {
+                       memcpy(&id, data, sizeof(id));
+                       json_do_printf("originator", "%s", inet_ntoa(id));
+               } else
+                       json_do_printf("error", "bad length");
+               break;
+       case ATTR_CLUSTER_LIST:
+               json_do_array("cluster_list");
+               for (off = 0; off + sizeof(id) <= alen;
+                   off += sizeof(id)) {
+                       memcpy(&id, data + off, sizeof(id));
+                       json_do_printf("cluster_id", "%s", inet_ntoa(id));
+               }
+               json_do_end();
+               break;
+       case ATTR_MP_REACH_NLRI:
+       case ATTR_MP_UNREACH_NLRI:
+               if (alen < 3) {
+bad_len:
+                       json_do_printf("error", "bad length");
+                       break;
+               }
+               memcpy(&afi, data, 2);
+               data += 2;
+               alen -= 2;
+               afi = ntohs(afi);
+               safi = *data++;
+               alen--;
+
+               if (afi2aid(afi, safi, &aid) == -1) {
+                       json_do_printf("error", "bad AFI/SAFI pair: %d/%d",
+                           afi, safi);
+                       break;
+               }
+               json_do_printf("family", "%s", aid2str(aid));
+
+               if (type == ATTR_MP_REACH_NLRI) {
+                       struct bgpd_addr nexthop;
+                       uint8_t nhlen;
+                       if (len == 0)
+                               goto bad_len;
+                       nhlen = *data++;
+                       alen--;
+                       if (nhlen > len)
+                               goto bad_len;
+                       memset(&nexthop, 0, sizeof(nexthop));
+                       switch (aid) {
+                       case AID_INET6:
+                               nexthop.aid = aid;
+                               if (nhlen != 16 && nhlen != 32)
+                                       goto bad_len;
+                               memcpy(&nexthop.v6.s6_addr, data, 16);
+                               break;
+                       case AID_VPN_IPv4:
+                               if (nhlen != 12)
+                                       goto bad_len;
+                               nexthop.aid = AID_INET;
+                               memcpy(&nexthop.v4, data + sizeof(uint64_t),
+                                   sizeof(nexthop.v4));
+                               break;
+                       case AID_VPN_IPv6:
+                               if (nhlen != 24)
+                                       goto bad_len;
+                               nexthop.aid = AID_INET6;
+                               memcpy(&nexthop.v6, data + sizeof(uint64_t),
+                                   sizeof(nexthop.v6));
+                               break;
+                       default:
+                               json_do_printf("error", "unhandled AID: %d",
+                                   aid);
+                               return;
+                       }
+                       /* ignore reserved (old SNPA) field as per RFC4760 */
+                       data += nhlen + 1;
+                       alen -= nhlen + 1;
+
+                       json_do_printf("nexthop", "%s", log_addr(&nexthop));
+               }
+
+               json_do_array("NLRI");
+               while (alen > 0) {
+                       switch (aid) {
+                       case AID_INET6:
+                               pos = nlri_get_prefix6(data, alen, &prefix,
+                                   &prefixlen);
+                               break;
+                       case AID_VPN_IPv4:
+                                pos = nlri_get_vpn4(data, alen, &prefix,
+                                    &prefixlen, 1);
+                                break;
+                       case AID_VPN_IPv6:
+                                pos = nlri_get_vpn6(data, alen, &prefix,
+                                    &prefixlen, 1);
+                                break;
+                       default:
+                               json_do_printf("error", "unhandled AID: %d",
+                                   aid);
+                               return;
+                       }
+                       if (pos == -1) {
+                               json_do_printf("error", "bad %s prefix",
+                                   aid2str(aid));
+                               break;
+                       }
+                       json_do_printf("prefix", "%s/%u", log_addr(&prefix),
+                           prefixlen);
+                       data += pos;
+                       alen -= pos;
+               }
+               break;
+       case ATTR_EXT_COMMUNITIES:
+               json_do_ext_community(data, alen);
+               break;
+       case ATTR_LARGE_COMMUNITIES:
+               json_do_large_community(data, alen);
+               break;
+       case ATTR_ATOMIC_AGGREGATE:
+       default:
+               if (alen)
+                       json_do_hexdump("data", data, alen);
+               break;
+       }
+}
+
+static void
+json_rib(struct ctl_show_rib *r, u_char *asdata, size_t aslen,
+    struct parse_result *res)
+{
+       struct in_addr id;
+       char *aspath;
+
+       json_do_array("rib");
+
+       json_do_object("rib_entry");
+
+       json_do_printf("prefix", "%s/%u", log_addr(&r->prefix), r->prefixlen);
+
+       if (aspath_asprint(&aspath, asdata, aslen) == -1)
+               err(1, NULL);
+       json_do_printf("aspath", "%s", aspath);
+       free(aspath);
+
+       json_do_printf("exit_nexthop", "%s", log_addr(&r->exit_nexthop));
+       json_do_printf("true_nexthop", "%s", log_addr(&r->true_nexthop));
+
+       json_do_object("neighbor");
+       if (r->descr[0])
+               json_do_printf("description", "%s", r->descr);
+       json_do_printf("remote_addr", "%s", log_addr(&r->remote_addr));
+       id.s_addr = htonl(r->remote_id);
+       json_do_printf("bgp_id", "%s", inet_ntoa(id));
+       json_do_end();
+
+       /* flags */
+       json_do_bool("valid", r->flags & F_PREF_ELIGIBLE);
+       if (r->flags & F_PREF_ACTIVE)
+               json_do_bool("best", 1);
+       if (r->flags & F_PREF_INTERNAL)
+               json_do_printf("source", "%s", "internal");
+       else
+               json_do_printf("source", "%s", "external");
+       if (r->flags & F_PREF_STALE)
+               json_do_bool("stale", 1);
+       if (r->flags & F_PREF_ANNOUNCE)
+               json_do_bool("announced", 1);
+
+       /* various attribibutes */
+       json_do_printf("ovs", "%s", fmt_ovs(r->validation_state, 0));
+       json_do_printf("origin", "%s", fmt_origin(r->origin, 0));
+       json_do_uint("metric", r->med);
+       json_do_uint("localpref", r->local_pref);
+       json_do_uint("weight", r->weight);
+       json_do_printf("last_update", "%s", fmt_timeframe(r->age));
+
+       /* keep the object open for communities and attribuites */
+}
+
+static void
+json_rib_mem_element(const char *name, uint64_t count, uint64_t size,
+    uint64_t refs)
+{
+       json_do_object(name);
+       if (count != UINT64_MAX)
+               json_do_uint("count", count);
+       if (size != UINT64_MAX)
+               json_do_uint("size", size);
+       if (refs != UINT64_MAX)
+               json_do_uint("references", refs);
+       json_do_end();
+}
+
+static void
+json_rib_mem(struct rde_memstats *stats)
+{
+       size_t pts = 0;
+       int i;
+
+       json_do_object("memory");
+       for (i = 0; i < AID_MAX; i++) {
+               if (stats->pt_cnt[i] == 0)
+                       continue;
+               pts += stats->pt_cnt[i] * pt_sizes[i];
+               json_rib_mem_element(aid_vals[i].name, stats->pt_cnt[i],
+                   stats->pt_cnt[i] * pt_sizes[i], UINT64_MAX);
+       }
+       json_rib_mem_element("rib", stats->rib_cnt,
+           stats->rib_cnt * sizeof(struct rib_entry), UINT64_MAX);
+       json_rib_mem_element("prefix", stats->prefix_cnt,
+           stats->prefix_cnt * sizeof(struct prefix), UINT64_MAX);
+       json_rib_mem_element("rde_aspath", stats->path_cnt,
+           stats->path_cnt * sizeof(struct rde_aspath),
+           stats->path_refs);
+       json_rib_mem_element("aspath", stats->aspath_cnt,
+           stats->aspath_size, stats->aspath_refs);
+       json_rib_mem_element("community_entries", stats->comm_cnt,
+           stats->comm_cnt * sizeof(struct rde_community), UINT64_MAX);
+       json_rib_mem_element("community", stats->comm_nmemb,
+           stats->comm_size * sizeof(struct community), stats->comm_refs);
+       json_rib_mem_element("attributes_entries", stats->attr_cnt,
+           stats->attr_cnt * sizeof(struct attr), stats->attr_refs);
+       json_rib_mem_element("attributes", stats->attr_dcnt,
+           stats->attr_data, UINT64_MAX);
+       json_rib_mem_element("total", UINT64_MAX, 
+           pts + stats->prefix_cnt * sizeof(struct prefix) +
+           stats->rib_cnt * sizeof(struct rib_entry) +
+           stats->path_cnt * sizeof(struct rde_aspath) +
+           stats->aspath_size + stats->attr_cnt * sizeof(struct attr) +
+           stats->attr_data, UINT64_MAX);
+       json_do_end();
+
+       json_do_object("sets");
+       json_rib_mem_element("as_set", stats->aset_nmemb,
+           stats->aset_size, UINT64_MAX);
+       json_rib_mem_element("as_set_tables", stats->aset_cnt, UINT64_MAX,
+           UINT64_MAX);
+       json_rib_mem_element("prefix_set", stats->pset_cnt, stats->pset_size,
+           UINT64_MAX);
+       json_rib_mem_element("total", UINT64_MAX, 
+           stats->aset_size + stats->pset_size, UINT64_MAX);
+       json_do_end();
+}
+
+static void
+json_rib_hash(struct rde_hashstats *hash)
+{
+       double avg, dev;
+
+       json_do_array("hashtables");
+
+       avg = (double)hash->sum / (double)hash->num;
+       dev = sqrt(fmax(0, hash->sumq / hash->num - avg * avg));
+
+       json_do_object("hashtable");
+
+       json_do_printf("name", "%s", hash->name);
+       json_do_uint("size", hash->num);
+       json_do_uint("entries", hash->sum);
+       json_do_uint("min", hash->min);
+       json_do_uint("max", hash->max);
+       json_do_double("avg", avg);
+       json_do_double("std_dev", dev);
+       json_do_end();
+}
+
+static void
+json_result(u_int rescode)
+{
+       if (rescode == 0)
+               json_do_printf("status", "OK");
+       else if (rescode >
+           sizeof(ctl_res_strerror)/sizeof(ctl_res_strerror[0])) {
+               json_do_printf("status", "FAILED");
+               json_do_printf("error", "unknown error %d", rescode);
+       } else {
+               json_do_printf("status", "FAILED");
+               json_do_printf("error", "%s", ctl_res_strerror[rescode]);
+       }
+}
+
+static void
+json_tail(void)
+{
+       json_do_finish();
+}
+
+const struct output json_output = {
+       .head = json_head,
+       .neighbor = json_neighbor,
+       .timer = json_timer,
+       .fib = json_fib,
+       .fib_table = json_fib_table,
+       .nexthop = json_nexthop,
+       .interface = json_interface,
+       .communities = json_communities,
+       .attr = json_attr,
+       .rib = json_rib,
+       .rib_mem = json_rib_mem,
+       .rib_hash = json_rib_hash,
+       .result = json_result,
+       .tail = json_tail
+};

Reply via email to