xref: /openbsd-src/usr.sbin/bgpctl/output_json.c (revision f1dd7b858388b4a23f4f67a4957ec5ff656ebbe8)
1 /*	$OpenBSD: output_json.c,v 1.10 2021/05/03 14:01:56 claudio Exp $ */
2 
3 /*
4  * Copyright (c) 2020 Claudio Jeker <claudio@openbsd.org>
5  *
6  * Permission to use, copy, modify, and distribute this software for any
7  * purpose with or without fee is hereby granted, provided that the above
8  * copyright notice and this permission notice appear in all copies.
9  *
10  * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
11  * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
12  * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
13  * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
14  * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
15  * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
16  * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
17  */
18 
19 #include <endian.h>
20 #include <err.h>
21 #include <math.h>
22 #include <stdio.h>
23 #include <stdlib.h>
24 #include <string.h>
25 
26 #include "bgpd.h"
27 #include "session.h"
28 #include "rde.h"
29 
30 #include "bgpctl.h"
31 #include "parser.h"
32 #include "json.h"
33 
34 static void
35 json_head(struct parse_result *res)
36 {
37 	json_do_start();
38 }
39 
40 static void
41 json_neighbor_capabilities(struct capabilities *capa)
42 {
43 	int hascapamp;
44 	uint8_t i;
45 
46 	for (i = 0; i < AID_MAX; i++)
47 		if (capa->mp[i])
48 			hascapamp = 1;
49 	if (!hascapamp && !capa->refresh && !capa->grestart.restart &&
50 	    !capa->as4byte)
51 		return;
52 
53 	json_do_object("capabilities");
54 	json_do_bool("as4byte", capa->as4byte);
55 	json_do_bool("refresh", capa->refresh);
56 
57 	if (hascapamp) {
58 		json_do_array("multiprotocol");
59 		for (i = 0; i < AID_MAX; i++)
60 			if (capa->mp[i])
61 				json_do_printf("mp", "%s", aid2str(i));
62 		json_do_end();
63 	}
64 	if (capa->grestart.restart) {
65 		int restarted = 0, present = 0;
66 
67 		for (i = 0; i < AID_MAX; i++)
68 			if (capa->grestart.flags[i] & CAPA_GR_PRESENT) {
69 				present = 1;
70 				if (capa->grestart.flags[i] & CAPA_GR_RESTART)
71 					restarted = 1;
72 				break;
73 			}
74 		json_do_object("graceful_restart");
75 		json_do_bool("eor", 1);
76 		json_do_bool("restart", restarted);
77 
78 		if (capa->grestart.timeout)
79 			json_do_uint("timeout", capa->grestart.timeout);
80 
81 		if (present) {
82 			json_do_array("protocols");
83 			for (i = 0; i < AID_MAX; i++)
84 				if (capa->grestart.flags[i] & CAPA_GR_PRESENT) {
85 					json_do_object("family");
86 					json_do_printf("family", "%s",
87 					    aid2str(i));
88 					json_do_bool("preserved",
89 					    capa->grestart.flags[i] &
90 					    CAPA_GR_FORWARD);
91 					json_do_end();
92 				}
93 			json_do_end();
94 		}
95 
96 		json_do_end();
97 	}
98 
99 	json_do_end();
100 }
101 
102 static void
103 json_neighbor_stats(struct peer *p)
104 {
105 	json_do_object("stats");
106 	json_do_printf("last_read", "%s", fmt_monotime(p->stats.last_read));
107 	json_do_int("last_read_sec", get_monotime(p->stats.last_read));
108 	json_do_printf("last_write", "%s", fmt_monotime(p->stats.last_write));
109 	json_do_int("last_write_sec", get_monotime(p->stats.last_write));
110 
111 	json_do_object("prefixes");
112 	json_do_uint("sent", p->stats.prefix_out_cnt);
113 	json_do_uint("received", p->stats.prefix_cnt);
114 	json_do_end();
115 
116 	json_do_object("message");
117 
118 	json_do_object("sent");
119 	json_do_uint("open", p->stats.msg_sent_open);
120 	json_do_uint("notifications", p->stats.msg_sent_notification);
121 	json_do_uint("updates", p->stats.msg_sent_update);
122 	json_do_uint("keepalives", p->stats.msg_sent_keepalive);
123 	json_do_uint("route_refresh", p->stats.msg_sent_rrefresh);
124 	json_do_uint("total",
125 	    p->stats.msg_sent_open + p->stats.msg_sent_notification +
126 	    p->stats.msg_sent_update + p->stats.msg_sent_keepalive +
127 	    p->stats.msg_sent_rrefresh);
128 	json_do_end();
129 
130 	json_do_object("received");
131 	json_do_uint("open", p->stats.msg_rcvd_open);
132 	json_do_uint("notifications", p->stats.msg_rcvd_notification);
133 	json_do_uint("updates", p->stats.msg_rcvd_update);
134 	json_do_uint("keepalives", p->stats.msg_rcvd_keepalive);
135 	json_do_uint("route_refresh", p->stats.msg_rcvd_rrefresh);
136 	json_do_uint("total",
137 	    p->stats.msg_rcvd_open + p->stats.msg_rcvd_notification +
138 	    p->stats.msg_rcvd_update + p->stats.msg_rcvd_keepalive +
139 	    p->stats.msg_rcvd_rrefresh);
140 	json_do_end();
141 
142 	json_do_end();
143 
144 	json_do_object("update");
145 
146 	json_do_object("sent");
147 	json_do_uint("updates", p->stats.prefix_sent_update);
148 	json_do_uint("withdraws", p->stats.prefix_sent_withdraw);
149 	json_do_uint("eor", p->stats.prefix_sent_eor);
150 	json_do_end();
151 
152 	json_do_object("received");
153 	json_do_uint("updates", p->stats.prefix_rcvd_update);
154 	json_do_uint("withdraws", p->stats.prefix_rcvd_withdraw);
155 	json_do_uint("eor", p->stats.prefix_rcvd_eor);
156 	json_do_end();
157 
158 	json_do_end();
159 
160 	json_do_end();
161 }
162 
163 static void
164 json_neighbor_full(struct peer *p)
165 {
166 	const char *errstr;
167 
168 	/* config */
169 	json_do_object("config");
170 	json_do_bool("template", p->conf.template);
171 	json_do_bool("cloned", p->template != NULL);
172 	json_do_bool("passive", p->conf.passive);
173 	json_do_bool("down", p->conf.down);
174 	json_do_bool("multihop", p->conf.ebgp && p->conf.distance > 1);
175 	if (p->conf.ebgp && p->conf.distance > 1)
176 		json_do_uint("multihop_distance", p->conf.distance);
177 	if (p->conf.max_prefix) {
178 		json_do_uint("max_prefix", p->conf.max_prefix);
179 		if (p->conf.max_prefix_restart)
180 			json_do_uint("max_prefix_restart",
181 			    p->conf.max_prefix_restart);
182 	}
183 	if (p->conf.max_out_prefix) {
184 		json_do_uint("max_out_prefix", p->conf.max_out_prefix);
185 		if (p->conf.max_out_prefix_restart)
186 			json_do_uint("max_out_prefix_restart",
187 			    p->conf.max_out_prefix_restart);
188 	}
189 	if (p->auth.method != AUTH_NONE)
190 		json_do_printf("authentication", "%s",
191 		    fmt_auth_method(p->auth.method));
192 	json_do_bool("ttl_security", p->conf.ttlsec);
193 	json_do_uint("holdtime", p->conf.holdtime);
194 	json_do_uint("min_holdtime", p->conf.min_holdtime);
195 
196 	/* capabilities */
197 	json_do_bool("announce_capabilities", p->conf.announce_capa);
198 	json_neighbor_capabilities(&p->conf.capabilities);
199 
200 	json_do_end();
201 
202 
203 	/* stats */
204 	json_neighbor_stats(p);
205 
206 	/* errors */
207 	if (p->conf.reason[0])
208 		json_do_printf("my_shutdown_reason", "%s",
209 		    log_reason(p->conf.reason));
210 	if (p->stats.last_reason[0])
211 		json_do_printf("last_shutdown_reason", "%s",
212 		    log_reason(p->stats.last_reason));
213 	errstr = fmt_errstr(p->stats.last_sent_errcode,
214 	    p->stats.last_sent_suberr);
215 	if (errstr)
216 		json_do_printf("last_error_sent", "%s", errstr);
217 	errstr = fmt_errstr(p->stats.last_rcvd_errcode,
218 	    p->stats.last_rcvd_suberr);
219 	if (errstr)
220 		json_do_printf("last_error_received", "%s", errstr);
221 
222 	/* connection info */
223 	if (p->state >= STATE_OPENSENT) {
224 		json_do_object("session");
225 		json_do_uint("holdtime", p->holdtime);
226 		json_do_uint("keepalive", p->holdtime / 3);
227 
228 		json_do_object("local");
229 		json_do_printf("address", "%s", log_addr(&p->local));
230 		json_do_uint("port", p->local_port);
231 		json_neighbor_capabilities(&p->capa.ann);
232 		json_do_end();
233 
234 		json_do_object("remote");
235 		json_do_printf("address", "%s", log_addr(&p->remote));
236 		json_do_uint("port", p->remote_port);
237 		json_neighbor_capabilities(&p->capa.peer);
238 		json_do_end();
239 
240 		/* capabilities */
241 		json_neighbor_capabilities(&p->capa.neg);
242 
243 		json_do_end();
244 	}
245 }
246 
247 static void
248 json_neighbor(struct peer *p, struct parse_result *res)
249 {
250 	json_do_array("neighbors");
251 
252 	json_do_object("neighbor");
253 
254 	json_do_printf("remote_as", "%s", log_as(p->conf.remote_as));
255 	if (p->conf.descr[0])
256 		json_do_printf("description", "%s", p->conf.descr);
257 	if (p->conf.group[0])
258 		json_do_printf("group", "%s", p->conf.group);
259 	if (!p->conf.template)
260 		json_do_printf("remote_addr", "%s",
261 		    log_addr(&p->conf.remote_addr));
262 	else
263 		json_do_printf("remote_addr", "%s/%u",
264 		    log_addr(&p->conf.remote_addr), p->conf.remote_masklen);
265 	if (p->state == STATE_ESTABLISHED) {
266 		struct in_addr ina;
267 		ina.s_addr = p->remote_bgpid;
268 		json_do_printf("bgpid", "%s", inet_ntoa(ina));
269 	}
270 	json_do_printf("state", "%s", statenames[p->state]);
271 	json_do_printf("last_updown", "%s", fmt_monotime(p->stats.last_updown));
272 	json_do_int("last_updown_sec", get_monotime(p->stats.last_updown));
273 
274 	switch (res->action) {
275 	case SHOW:
276 	case SHOW_SUMMARY:
277 	case SHOW_SUMMARY_TERSE:
278 		/* only show basic data */
279 		break;
280 	case SHOW_NEIGHBOR:
281 	case SHOW_NEIGHBOR_TIMERS:
282 	case SHOW_NEIGHBOR_TERSE:
283 		json_neighbor_full(p);
284 		break;
285 	default:
286 		break;
287 	}
288 
289 	/* keep the object open in case there are timers */
290 }
291 
292 static void
293 json_timer(struct ctl_timer *t)
294 {
295 	json_do_array("timers");
296 
297 	json_do_object("timer");
298 	json_do_printf("name", "%s", timernames[t->type]);
299 	json_do_int("due", t->val);
300 	json_do_end();
301 }
302 
303 static void
304 json_fib(struct kroute_full *kf)
305 {
306 	const char *origin;
307 
308 	json_do_array("fib");
309 
310 	json_do_object("fib_entry");
311 
312 	json_do_printf("prefix", "%s/%u", log_addr(&kf->prefix), kf->prefixlen);
313 	json_do_uint("priority", kf->priority);
314 	json_do_bool("up", !(kf->flags & F_DOWN));
315 	if (kf->flags & F_BGPD_INSERTED)
316 		origin = "bgp";
317 	else if (kf->flags & F_CONNECTED)
318 		origin = "connected";
319 	else if (kf->flags & F_STATIC)
320 		origin = "static";
321 	else if (kf->flags & F_DYNAMIC)
322 		origin = "dynamic";
323 	else
324 		origin = "unknown";
325 	json_do_printf("origin", "%s", origin);
326 	json_do_bool("used_by_nexthop", kf->flags & F_NEXTHOP);
327 	json_do_bool("blackhole", kf->flags & F_BLACKHOLE);
328 	json_do_bool("reject", kf->flags & F_REJECT);
329 
330 	if (kf->flags & F_CONNECTED)
331 		json_do_printf("nexthop", "link#%u", kf->ifindex);
332 	else
333 		json_do_printf("nexthop", "%s", log_addr(&kf->nexthop));
334 
335 	json_do_end();
336 }
337 
338 static void
339 json_fib_table(struct ktable *kt)
340 {
341 	json_do_array("fibtables");
342 
343 	json_do_object("fibtable");
344 	json_do_uint("rtableid", kt->rtableid);
345 	json_do_printf("description", "%s", kt->descr);
346 	json_do_bool("coupled", kt->fib_sync);
347 	json_do_bool("admin_change", kt->fib_sync != kt->fib_conf);
348 	json_do_end();
349 }
350 
351 static void
352 json_do_interface(struct ctl_show_interface *iface)
353 {
354 	json_do_object("interface");
355 
356 	json_do_printf("name", "%s", iface->ifname);
357 	json_do_uint("rdomain", iface->rdomain);
358 	json_do_bool("is_up", iface->is_up);
359 	json_do_bool("nh_reachable", iface->nh_reachable);
360 
361 	if (iface->media[0])
362 		json_do_printf("media", "%s", iface->media);
363 
364 	json_do_printf("linkstate", "%s", iface->linkstate);
365 	if (iface->baudrate > 0)
366 		json_do_uint("baudrate", iface->baudrate);
367 
368 	json_do_end();
369 }
370 
371 static void
372 json_nexthop(struct ctl_show_nexthop *nh)
373 {
374 	struct kroute *k;
375 	struct kroute6 *k6;
376 
377 	json_do_array("nexthops");
378 
379 	json_do_object("nexthop");
380 
381 	json_do_printf("address", "%s", log_addr(&nh->addr));
382 	json_do_bool("valid", nh->valid);
383 
384 	if (!nh->krvalid)
385 		goto done;
386 
387 	switch (nh->addr.aid) {
388 	case AID_INET:
389 		k = &nh->kr.kr4;
390 		json_do_printf("prefix", "%s/%u", inet_ntoa(k->prefix),
391 		    k->prefixlen);
392 		json_do_uint("priority", k->priority);
393 		json_do_bool("connected", k->flags & F_CONNECTED);
394 		json_do_printf("nexthop", "%s", inet_ntoa(k->nexthop));
395 		break;
396 	case AID_INET6:
397 		k6 = &nh->kr.kr6;
398 		json_do_printf("prefix", "%s/%u", log_in6addr(&k6->prefix),
399 		    k6->prefixlen);
400 		json_do_uint("priority", k6->priority);
401 		json_do_bool("connected", k6->flags & F_CONNECTED);
402 		json_do_printf("nexthop", "%s", log_in6addr(&k6->nexthop));
403 		break;
404 	default:
405 		warnx("nexthop: unknown address family");
406 		goto done;
407 	}
408 	if (nh->iface.ifname[0])
409 		json_do_interface(&nh->iface);
410 done:
411 	json_do_end();
412 	/* keep array open */
413 }
414 
415 static void
416 json_interface(struct ctl_show_interface *iface)
417 {
418 	json_do_array("interfaces");
419 	json_do_interface(iface);
420 }
421 
422 static void
423 json_communities(u_char *data, size_t len, struct parse_result *res)
424 {
425 	struct community c;
426 	size_t  i;
427 	uint64_t ext;
428 
429 	if (len % sizeof(c)) {
430 		warnx("communities: bad size");
431 		return;
432 	}
433 
434 	for (i = 0; i < len; i += sizeof(c)) {
435 		memcpy(&c, data + i, sizeof(c));
436 
437 		switch (c.flags) {
438 		case COMMUNITY_TYPE_BASIC:
439 			json_do_array("communities");
440 			json_do_printf("community", "%s",
441 			    fmt_community(c.data1, c.data2));
442 			break;
443 		case COMMUNITY_TYPE_LARGE:
444 			json_do_array("large_communities");
445 			json_do_printf("community", "%s",
446 			    fmt_large_community(c.data1, c.data2, c.data3));
447 			break;
448 		case COMMUNITY_TYPE_EXT:
449 			ext = (uint64_t)c.data3 << 48;
450 			switch (c.data3 >> 8) {
451 			case EXT_COMMUNITY_TRANS_TWO_AS:
452 			case EXT_COMMUNITY_TRANS_OPAQUE:
453 			case EXT_COMMUNITY_TRANS_EVPN:
454 			case EXT_COMMUNITY_NON_TRANS_OPAQUE:
455 				ext |= ((uint64_t)c.data1 & 0xffff) << 32;
456 				ext |= (uint64_t)c.data2;
457 				break;
458 			case EXT_COMMUNITY_TRANS_FOUR_AS:
459 			case EXT_COMMUNITY_TRANS_IPV4:
460 				ext |= (uint64_t)c.data1 << 16;
461 				ext |= (uint64_t)c.data2 & 0xffff;
462 				break;
463 			}
464 			ext = htobe64(ext);
465 
466 			json_do_array("extended_communities");
467 			json_do_printf("community", "%s",
468 			    fmt_ext_community((void *)&ext));
469 			break;
470 		}
471 	}
472 }
473 
474 static void
475 json_do_community(u_char *data, uint16_t len)
476 {
477 	uint16_t a, v, i;
478 
479 	if (len & 0x3) {
480 		json_do_printf("error", "bad length");
481 		return;
482 	}
483 
484 	json_do_array("communities");
485 
486 	for (i = 0; i < len; i += 4) {
487 		memcpy(&a, data + i, sizeof(a));
488 		memcpy(&v, data + i + 2, sizeof(v));
489 		a = ntohs(a);
490 		v = ntohs(v);
491 		json_do_printf("community", "%s", fmt_community(a, v));
492 	}
493 
494 	json_do_end();
495 }
496 
497 static void
498 json_do_large_community(u_char *data, uint16_t len)
499 {
500 	uint32_t a, l1, l2;
501 	u_int16_t i;
502 
503 	if (len % 12) {
504 		json_do_printf("error", "bad length");
505 		return;
506 	}
507 
508 	json_do_array("large_communities");
509 
510 	for (i = 0; i < len; i += 12) {
511 		memcpy(&a, data + i, sizeof(a));
512 		memcpy(&l1, data + i + 4, sizeof(l1));
513 		memcpy(&l2, data + i + 8, sizeof(l2));
514 		a = ntohl(a);
515 		l1 = ntohl(l1);
516 		l2 = ntohl(l2);
517 
518 		json_do_printf("community", "%s",
519 		    fmt_large_community(a, l1, l2));
520 	}
521 
522 	json_do_end();
523 }
524 
525 static void
526 json_do_ext_community(u_char *data, uint16_t len)
527 {
528 	uint16_t i;
529 
530 	if (len & 0x7) {
531 		json_do_printf("error", "bad length");
532 		return;
533 	}
534 
535 	json_do_array("extended_communities");
536 
537 	for (i = 0; i < len; i += 8)
538 		json_do_printf("community", "%s", fmt_ext_community(data + i));
539 
540 	json_do_end();
541 }
542 
543 static void
544 json_attr(u_char *data, size_t len, int reqflags)
545 {
546 	struct bgpd_addr prefix;
547 	struct in_addr id;
548 	char *aspath;
549 	u_char *path;
550 	uint32_t as;
551 	uint16_t alen, afi, off, short_as;
552 	uint8_t flags, type, safi, aid, prefixlen;
553 	int e4, e2, pos;
554 
555 	if (len < 3) {
556 		warnx("Too short BGP attrbute");
557 		return;
558 	}
559 
560 	flags = data[0];
561 	type = data[1];
562 	if (flags & ATTR_EXTLEN) {
563 		if (len < 4) {
564 			warnx("Too short BGP attrbute");
565 			return;
566 		}
567 		memcpy(&alen, data+2, sizeof(uint16_t));
568 		alen = ntohs(alen);
569 		data += 4;
570 		len -= 4;
571 	} else {
572 		alen = data[2];
573 		data += 3;
574 		len -= 3;
575 	}
576 
577 	/* bad imsg len how can that happen!? */
578 	if (alen > len) {
579 		warnx("Bad BGP attrbute length");
580 		return;
581 	}
582 
583 	json_do_array("attributes");
584 
585 	json_do_object("attribute");
586 	json_do_printf("type", "%s", fmt_attr(type, -1));
587 	json_do_uint("length", alen);
588 	json_do_object("flags");
589 	json_do_bool("partial", flags & ATTR_PARTIAL);
590 	json_do_bool("transitive", flags & ATTR_TRANSITIVE);
591 	json_do_bool("optional", flags & ATTR_OPTIONAL);
592 	json_do_end();
593 
594 	switch (type) {
595 	case ATTR_ORIGIN:
596 		if (alen == 1)
597 			json_do_printf("origin", "%s", fmt_origin(*data, 0));
598 		else
599 			json_do_printf("error", "bad length");
600 		break;
601 	case ATTR_ASPATH:
602 	case ATTR_AS4_PATH:
603 		/* prefer 4-byte AS here */
604 		e4 = aspath_verify(data, alen, 1, 0);
605 		e2 = aspath_verify(data, alen, 0, 0);
606 		if (e4 == 0 || e4 == AS_ERR_SOFT) {
607 			path = data;
608 		} else if (e2 == 0 || e2 == AS_ERR_SOFT) {
609 			path = aspath_inflate(data, alen, &alen);
610 			if (path == NULL)
611 				errx(1, "aspath_inflate failed");
612 		} else {
613 			json_do_printf("error", "bad AS-Path");
614 			break;
615 		}
616 		if (aspath_asprint(&aspath, path, alen) == -1)
617 			err(1, NULL);
618 		json_do_printf("aspath", "%s", aspath);
619 		free(aspath);
620 		if (path != data)
621 			free(path);
622 		break;
623 	case ATTR_NEXTHOP:
624 		if (alen == 4) {
625 			memcpy(&id, data, sizeof(id));
626 			json_do_printf("nexthop", "%s", inet_ntoa(id));
627 		} else
628 			json_do_printf("error", "bad length");
629 		break;
630 	case ATTR_MED:
631 	case ATTR_LOCALPREF:
632 		if (alen == 4) {
633 			uint32_t val;
634 			memcpy(&val, data, sizeof(val));
635 			json_do_uint("metric", ntohl(val));
636 		} else
637 			json_do_printf("error", "bad length");
638 		break;
639 	case ATTR_AGGREGATOR:
640 	case ATTR_AS4_AGGREGATOR:
641 		if (alen == 8) {
642 			memcpy(&as, data, sizeof(as));
643 			memcpy(&id, data + sizeof(as), sizeof(id));
644 			as = ntohl(as);
645 		} else if (alen == 6) {
646 			memcpy(&short_as, data, sizeof(short_as));
647 			memcpy(&id, data + sizeof(short_as), sizeof(id));
648 			as = ntohs(short_as);
649 		} else {
650 			json_do_printf("error", "bad AS-Path");
651 			break;
652 		}
653 		json_do_uint("AS", as);
654 		json_do_printf("router_id", "%s", inet_ntoa(id));
655 		break;
656 	case ATTR_COMMUNITIES:
657 		json_do_community(data, alen);
658 		break;
659 	case ATTR_ORIGINATOR_ID:
660 		if (alen == 4) {
661 			memcpy(&id, data, sizeof(id));
662 			json_do_printf("originator", "%s", inet_ntoa(id));
663 		} else
664 			json_do_printf("error", "bad length");
665 		break;
666 	case ATTR_CLUSTER_LIST:
667 		json_do_array("cluster_list");
668 		for (off = 0; off + sizeof(id) <= alen;
669 		    off += sizeof(id)) {
670 			memcpy(&id, data + off, sizeof(id));
671 			json_do_printf("cluster_id", "%s", inet_ntoa(id));
672 		}
673 		json_do_end();
674 		break;
675 	case ATTR_MP_REACH_NLRI:
676 	case ATTR_MP_UNREACH_NLRI:
677 		if (alen < 3) {
678 bad_len:
679 			json_do_printf("error", "bad length");
680 			break;
681 		}
682 		memcpy(&afi, data, 2);
683 		data += 2;
684 		alen -= 2;
685 		afi = ntohs(afi);
686 		safi = *data++;
687 		alen--;
688 
689 		if (afi2aid(afi, safi, &aid) == -1) {
690 			json_do_printf("error", "bad AFI/SAFI pair: %d/%d",
691 			    afi, safi);
692 			break;
693 		}
694 		json_do_printf("family", "%s", aid2str(aid));
695 
696 		if (type == ATTR_MP_REACH_NLRI) {
697 			struct bgpd_addr nexthop;
698 			uint8_t nhlen;
699 			if (len == 0)
700 				goto bad_len;
701 			nhlen = *data++;
702 			alen--;
703 			if (nhlen > len)
704 				goto bad_len;
705 			memset(&nexthop, 0, sizeof(nexthop));
706 			switch (aid) {
707 			case AID_INET6:
708 				nexthop.aid = aid;
709 				if (nhlen != 16 && nhlen != 32)
710 					goto bad_len;
711 				memcpy(&nexthop.v6.s6_addr, data, 16);
712 				break;
713 			case AID_VPN_IPv4:
714 				if (nhlen != 12)
715 					goto bad_len;
716 				nexthop.aid = AID_INET;
717 				memcpy(&nexthop.v4, data + sizeof(uint64_t),
718 				    sizeof(nexthop.v4));
719 				break;
720 			case AID_VPN_IPv6:
721 				if (nhlen != 24)
722 					goto bad_len;
723 				nexthop.aid = AID_INET6;
724 				memcpy(&nexthop.v6, data + sizeof(uint64_t),
725 				    sizeof(nexthop.v6));
726 				break;
727 			default:
728 				json_do_printf("error", "unhandled AID: %d",
729 				    aid);
730 				return;
731 			}
732 			/* ignore reserved (old SNPA) field as per RFC4760 */
733 			data += nhlen + 1;
734 			alen -= nhlen + 1;
735 
736 			json_do_printf("nexthop", "%s", log_addr(&nexthop));
737 		}
738 
739 		json_do_array("NLRI");
740 		while (alen > 0) {
741 			switch (aid) {
742 			case AID_INET6:
743 				pos = nlri_get_prefix6(data, alen, &prefix,
744 				    &prefixlen);
745 				break;
746 			case AID_VPN_IPv4:
747 				 pos = nlri_get_vpn4(data, alen, &prefix,
748 				     &prefixlen, 1);
749 				 break;
750 			case AID_VPN_IPv6:
751 				 pos = nlri_get_vpn6(data, alen, &prefix,
752 				     &prefixlen, 1);
753 				 break;
754 			default:
755 				json_do_printf("error", "unhandled AID: %d",
756 				    aid);
757 				return;
758 			}
759 			if (pos == -1) {
760 				json_do_printf("error", "bad %s prefix",
761 				    aid2str(aid));
762 				break;
763 			}
764 			json_do_printf("prefix", "%s/%u", log_addr(&prefix),
765 			    prefixlen);
766 			data += pos;
767 			alen -= pos;
768 		}
769 		break;
770 	case ATTR_EXT_COMMUNITIES:
771 		json_do_ext_community(data, alen);
772 		break;
773 	case ATTR_LARGE_COMMUNITIES:
774 		json_do_large_community(data, alen);
775 		break;
776 	case ATTR_ATOMIC_AGGREGATE:
777 	default:
778 		if (alen)
779 			json_do_hexdump("data", data, alen);
780 		break;
781 	}
782 }
783 
784 static void
785 json_rib(struct ctl_show_rib *r, u_char *asdata, size_t aslen,
786     struct parse_result *res)
787 {
788 	struct in_addr id;
789 	char *aspath;
790 
791 	json_do_array("rib");
792 
793 	json_do_object("rib_entry");
794 
795 	json_do_printf("prefix", "%s/%u", log_addr(&r->prefix), r->prefixlen);
796 
797 	if (aspath_asprint(&aspath, asdata, aslen) == -1)
798 		err(1, NULL);
799 	json_do_printf("aspath", "%s", aspath);
800 	free(aspath);
801 
802 	json_do_printf("exit_nexthop", "%s", log_addr(&r->exit_nexthop));
803 	json_do_printf("true_nexthop", "%s", log_addr(&r->true_nexthop));
804 
805 	json_do_object("neighbor");
806 	if (r->descr[0])
807 		json_do_printf("description", "%s", r->descr);
808 	json_do_printf("remote_addr", "%s", log_addr(&r->remote_addr));
809 	id.s_addr = htonl(r->remote_id);
810 	json_do_printf("bgp_id", "%s", inet_ntoa(id));
811 	json_do_end();
812 
813 	/* flags */
814 	json_do_bool("valid", r->flags & F_PREF_ELIGIBLE);
815 	if (r->flags & F_PREF_ACTIVE)
816 		json_do_bool("best", 1);
817 	if (r->flags & F_PREF_INTERNAL)
818 		json_do_printf("source", "%s", "internal");
819 	else
820 		json_do_printf("source", "%s", "external");
821 	if (r->flags & F_PREF_STALE)
822 		json_do_bool("stale", 1);
823 	if (r->flags & F_PREF_ANNOUNCE)
824 		json_do_bool("announced", 1);
825 
826 	/* various attribibutes */
827 	json_do_printf("ovs", "%s", fmt_ovs(r->validation_state, 0));
828 	json_do_printf("origin", "%s", fmt_origin(r->origin, 0));
829 	json_do_uint("metric", r->med);
830 	json_do_uint("localpref", r->local_pref);
831 	json_do_uint("weight", r->weight);
832 	json_do_printf("last_update", "%s", fmt_timeframe(r->age));
833 	json_do_int("last_update_sec", r->age);
834 
835 	/* keep the object open for communities and attribuites */
836 }
837 
838 static void
839 json_rib_mem_element(const char *name, uint64_t count, uint64_t size,
840     uint64_t refs)
841 {
842 	json_do_object(name);
843 	if (count != UINT64_MAX)
844 		json_do_uint("count", count);
845 	if (size != UINT64_MAX)
846 		json_do_uint("size", size);
847 	if (refs != UINT64_MAX)
848 		json_do_uint("references", refs);
849 	json_do_end();
850 }
851 
852 static void
853 json_rib_mem(struct rde_memstats *stats)
854 {
855 	size_t pts = 0;
856 	int i;
857 
858 	json_do_object("memory");
859 	for (i = 0; i < AID_MAX; i++) {
860 		if (stats->pt_cnt[i] == 0)
861 			continue;
862 		pts += stats->pt_cnt[i] * pt_sizes[i];
863 		json_rib_mem_element(aid_vals[i].name, stats->pt_cnt[i],
864 		    stats->pt_cnt[i] * pt_sizes[i], UINT64_MAX);
865 	}
866 	json_rib_mem_element("rib", stats->rib_cnt,
867 	    stats->rib_cnt * sizeof(struct rib_entry), UINT64_MAX);
868 	json_rib_mem_element("prefix", stats->prefix_cnt,
869 	    stats->prefix_cnt * sizeof(struct prefix), UINT64_MAX);
870 	json_rib_mem_element("rde_aspath", stats->path_cnt,
871 	    stats->path_cnt * sizeof(struct rde_aspath),
872 	    stats->path_refs);
873 	json_rib_mem_element("aspath", stats->aspath_cnt,
874 	    stats->aspath_size, stats->aspath_refs);
875 	json_rib_mem_element("community_entries", stats->comm_cnt,
876 	    stats->comm_cnt * sizeof(struct rde_community), UINT64_MAX);
877 	json_rib_mem_element("community", stats->comm_nmemb,
878 	    stats->comm_size * sizeof(struct community), stats->comm_refs);
879 	json_rib_mem_element("attributes_entries", stats->attr_cnt,
880 	    stats->attr_cnt * sizeof(struct attr), stats->attr_refs);
881 	json_rib_mem_element("attributes", stats->attr_dcnt,
882 	    stats->attr_data, UINT64_MAX);
883 	json_rib_mem_element("total", UINT64_MAX,
884 	    pts + stats->prefix_cnt * sizeof(struct prefix) +
885 	    stats->rib_cnt * sizeof(struct rib_entry) +
886 	    stats->path_cnt * sizeof(struct rde_aspath) +
887 	    stats->aspath_size + stats->attr_cnt * sizeof(struct attr) +
888 	    stats->attr_data, UINT64_MAX);
889 	json_do_end();
890 
891 	json_do_object("sets");
892 	json_rib_mem_element("as_set", stats->aset_nmemb,
893 	    stats->aset_size, UINT64_MAX);
894 	json_rib_mem_element("as_set_tables", stats->aset_cnt, UINT64_MAX,
895 	    UINT64_MAX);
896 	json_rib_mem_element("prefix_set", stats->pset_cnt, stats->pset_size,
897 	    UINT64_MAX);
898 	json_rib_mem_element("total", UINT64_MAX,
899 	    stats->aset_size + stats->pset_size, UINT64_MAX);
900 	json_do_end();
901 }
902 
903 static void
904 json_rib_hash(struct rde_hashstats *hash)
905 {
906 	double avg, dev;
907 
908 	json_do_array("hashtables");
909 
910 	avg = (double)hash->sum / (double)hash->num;
911 	dev = sqrt(fmax(0, hash->sumq / hash->num - avg * avg));
912 
913 	json_do_object("hashtable");
914 
915 	json_do_printf("name", "%s", hash->name);
916 	json_do_uint("size", hash->num);
917 	json_do_uint("entries", hash->sum);
918 	json_do_uint("min", hash->min);
919 	json_do_uint("max", hash->max);
920 	json_do_double("avg", avg);
921 	json_do_double("std_dev", dev);
922 	json_do_end();
923 }
924 
925 static void
926 json_rib_set(struct ctl_show_set *set)
927 {
928 	json_do_array("sets");
929 
930 	json_do_object("set");
931 	json_do_printf("name", "%s", set->name);
932 	json_do_printf("type", "%s", fmt_set_type(set));
933 	json_do_printf("last_change", "%s", fmt_monotime(set->lastchange));
934 	json_do_int("last_change_sec", get_monotime(set->lastchange));
935 	if (set->type == ASNUM_SET) {
936 		json_do_uint("num_ASnum", set->as_cnt);
937 	} else {
938 		json_do_uint("num_IPv4", set->v4_cnt);
939 		json_do_uint("num_IPv6", set->v6_cnt);
940 	}
941 	json_do_end();
942 }
943 
944 static void
945 json_rtr(struct ctl_show_rtr *rtr)
946 {
947 	json_do_array("rtrs");
948 
949 	json_do_object("rtr");
950 	if (rtr->descr[0])
951 		json_do_printf("descr", "%s", rtr->descr);
952 	json_do_printf("remote_addr", "%s", log_addr(&rtr->remote_addr));
953 	json_do_uint("remote_port", rtr->remote_port);
954 	if (rtr->local_addr.aid != AID_UNSPEC)
955 		json_do_printf("local_addr", "%s", log_addr(&rtr->local_addr));
956 
957 	if (rtr->session_id != -1) {
958 		json_do_uint("session_id", rtr->session_id);
959 		json_do_uint("serial", rtr->serial);
960 	}
961 	json_do_uint("refresh", rtr->refresh);
962 	json_do_uint("retry", rtr->retry);
963 	json_do_uint("expire", rtr->expire);
964 
965 	if (rtr->last_sent_error != NO_ERROR) {
966 		json_do_printf("last_sent_error", "%s",
967 		    log_rtr_error(rtr->last_sent_error));
968 		if (rtr->last_sent_msg[0])
969 			json_do_printf("last_sent_msg", "%s",
970 			    log_reason(rtr->last_sent_msg));
971 	}
972 	if (rtr->last_recv_error != NO_ERROR) {
973 		json_do_printf("last_recv_error", "%s",
974 		    log_rtr_error(rtr->last_recv_error));
975 		if (rtr->last_recv_msg[0])
976 			json_do_printf("last_recv_msg", "%s",
977 			    log_reason(rtr->last_recv_msg));
978 	}
979 }
980 
981 static void
982 json_result(u_int rescode)
983 {
984 	if (rescode == 0)
985 		json_do_printf("status", "OK");
986 	else if (rescode >=
987 	    sizeof(ctl_res_strerror)/sizeof(ctl_res_strerror[0])) {
988 		json_do_printf("status", "FAILED");
989 		json_do_printf("error", "unknown error %d", rescode);
990 	} else {
991 		json_do_printf("status", "FAILED");
992 		json_do_printf("error", "%s", ctl_res_strerror[rescode]);
993 	}
994 }
995 
996 static void
997 json_tail(void)
998 {
999 	json_do_finish();
1000 }
1001 
1002 const struct output json_output = {
1003 	.head = json_head,
1004 	.neighbor = json_neighbor,
1005 	.timer = json_timer,
1006 	.fib = json_fib,
1007 	.fib_table = json_fib_table,
1008 	.nexthop = json_nexthop,
1009 	.interface = json_interface,
1010 	.communities = json_communities,
1011 	.attr = json_attr,
1012 	.rib = json_rib,
1013 	.rib_mem = json_rib_mem,
1014 	.rib_hash = json_rib_hash,
1015 	.set = json_rib_set,
1016 	.rtr = json_rtr,
1017 	.result = json_result,
1018 	.tail = json_tail
1019 };
1020