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