Hi,

Great to hear about the json support for bgpctl.

Will bgpctl latest work with 6.6 bgpd, so we can compile a bgpctl_latest
with the json support for testing, without upgrading the rest of bgpd on
our boxes?

I am tempted to have a go at an ospfctl json implementation, following your
lead on this in bgpctl.

I guess the usr.sbin/bgpctl/json.c is reusable, needs some thought but
could be an interesting first adventure into C, to date my experience is
limited to a tiny addon for varnish cache...

We were going to add a wrapper to parse ospfctl, but this would be much
much cleaner and hopefully be useful for others.

Has anyone else done anything with ospfctl already in terms of json output,
or are we missing something and there is one already somewhere?

Cheers

Richard



On Fri, May 1, 2020 at 12:25 PM Claudio Jeker <cje...@diehard.n-r-g.com>
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.
>
> --
> :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
> +};
>
>

Reply via email to