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