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