On Fri, May 01, 2020 at 01:18:03PM +0200, Claudio Jeker wrote:
> 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.
> 

Hi,

Maybe in the function do_name() and in functions where strings are printed the
text should be escaped just in case? For example the characters ", \ and
encoding of control-characters.

> -- 
> :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 <clau...@openbsd.org>
> + *
> + * 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 <clau...@openbsd.org>
> + *
> + * 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 <clau...@openbsd.org>
> + *
> + * 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
> +};
> 

-- 
Kind regards,
Hiltjo

Reply via email to