xref: /openbsd-src/usr.sbin/bgpctl/output_json.c (revision fc405d53b73a2d73393cb97f684863d17b583e38)
1 /*	$OpenBSD: output_json.c,v 1.34 2023/05/05 07:42:40 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");
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");
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_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");
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");
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");
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");
150 
151 	json_do_object("sent");
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");
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");
178 
179 	json_do_object("sent");
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");
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");
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");
199 
200 	json_do_object("sent");
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");
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");
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_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_string("my_shutdown_reason",
265 		    log_reason(p->conf.reason));
266 	if (p->stats.last_reason[0])
267 		json_do_string("last_shutdown_reason",
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_string("last_error_sent", errstr);
273 	errstr = fmt_errstr(p->stats.last_rcvd_errcode,
274 	    p->stats.last_rcvd_suberr);
275 	if (errstr)
276 		json_do_string("last_error_received", 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_string("address", 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_string("address", 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 		if (p->conf.ebgp && p->conf.role != ROLE_NONE) {
300 			json_do_string("remote_role",
301 			    log_policy(p->remote_role));
302 			json_do_string("local_role",
303 			    log_policy(p->conf.role));
304 		}
305 		json_do_end();
306 	}
307 }
308 
309 static void
310 json_neighbor(struct peer *p, struct parse_result *res)
311 {
312 	json_do_array("neighbors");
313 
314 	json_do_object("neighbor");
315 
316 	json_do_string("remote_as", log_as(p->conf.remote_as));
317 	if (p->conf.descr[0])
318 		json_do_string("description", p->conf.descr);
319 	if (p->conf.group[0])
320 		json_do_string("group", p->conf.group);
321 	if (!p->conf.template)
322 		json_do_string("remote_addr", log_addr(&p->conf.remote_addr));
323 	else
324 		json_do_printf("remote_addr", "%s/%u",
325 		    log_addr(&p->conf.remote_addr), p->conf.remote_masklen);
326 	if (p->state == STATE_ESTABLISHED) {
327 		struct in_addr ina;
328 		ina.s_addr = p->remote_bgpid;
329 		json_do_string("bgpid", inet_ntoa(ina));
330 	}
331 	json_do_string("state", statenames[p->state]);
332 	json_do_string("last_updown", fmt_monotime(p->stats.last_updown));
333 	json_do_int("last_updown_sec", get_monotime(p->stats.last_updown));
334 
335 	switch (res->action) {
336 	case SHOW:
337 	case SHOW_SUMMARY:
338 	case SHOW_SUMMARY_TERSE:
339 		/* only show basic data */
340 		break;
341 	case SHOW_NEIGHBOR:
342 	case SHOW_NEIGHBOR_TIMERS:
343 	case SHOW_NEIGHBOR_TERSE:
344 		json_neighbor_full(p);
345 		break;
346 	default:
347 		break;
348 	}
349 
350 	/* keep the object open in case there are timers */
351 }
352 
353 static void
354 json_timer(struct ctl_timer *t)
355 {
356 	json_do_array("timers");
357 
358 	json_do_object("timer");
359 	json_do_string("name", timernames[t->type]);
360 	json_do_int("due", t->val);
361 	json_do_end();
362 }
363 
364 static void
365 json_fib(struct kroute_full *kf)
366 {
367 	const char *origin;
368 
369 	json_do_array("fib");
370 
371 	json_do_object("fib_entry");
372 
373 	json_do_printf("prefix", "%s/%u", log_addr(&kf->prefix), kf->prefixlen);
374 	json_do_uint("priority", kf->priority);
375 	if (kf->flags & F_BGPD)
376 		origin = "bgp";
377 	else if (kf->flags & F_CONNECTED)
378 		origin = "connected";
379 	else if (kf->flags & F_STATIC)
380 		origin = "static";
381 	else
382 		origin = "unknown";
383 	json_do_string("origin", origin);
384 	json_do_bool("used_by_nexthop", kf->flags & F_NEXTHOP);
385 	json_do_bool("blackhole", kf->flags & F_BLACKHOLE);
386 	json_do_bool("reject", kf->flags & F_REJECT);
387 
388 	if (kf->flags & F_CONNECTED)
389 		json_do_printf("nexthop", "link#%u", kf->ifindex);
390 	else
391 		json_do_string("nexthop", log_addr(&kf->nexthop));
392 
393 	if (kf->flags & F_MPLS) {
394 		json_do_array("mplslabel");
395 		json_do_uint("mplslabel",
396 		    ntohl(kf->mplslabel) >> MPLS_LABEL_OFFSET);
397 		json_do_end();
398 	}
399 	json_do_end();
400 }
401 
402 static void
403 json_fib_table(struct ktable *kt)
404 {
405 	json_do_array("fibtables");
406 
407 	json_do_object("fibtable");
408 	json_do_uint("rtableid", kt->rtableid);
409 	json_do_string("description", kt->descr);
410 	json_do_bool("coupled", kt->fib_sync);
411 	json_do_bool("admin_change", kt->fib_sync != kt->fib_conf);
412 	json_do_end();
413 }
414 
415 static void
416 json_do_interface(struct ctl_show_interface *iface)
417 {
418 	json_do_object("interface");
419 
420 	json_do_string("name", iface->ifname);
421 	json_do_uint("rdomain", iface->rdomain);
422 	json_do_bool("is_up", iface->is_up);
423 	json_do_bool("nh_reachable", iface->nh_reachable);
424 
425 	if (iface->media[0])
426 		json_do_string("media", iface->media);
427 
428 	json_do_string("linkstate", iface->linkstate);
429 	if (iface->baudrate > 0)
430 		json_do_uint("baudrate", iface->baudrate);
431 
432 	json_do_end();
433 }
434 
435 static void
436 json_nexthop(struct ctl_show_nexthop *nh)
437 {
438 	json_do_array("nexthops");
439 
440 	json_do_object("nexthop");
441 
442 	json_do_string("address", log_addr(&nh->addr));
443 	json_do_bool("valid", nh->valid);
444 
445 	if (!nh->krvalid)
446 		goto done;
447 
448 	json_do_printf("prefix", "%s/%u", log_addr(&nh->kr.prefix),
449 	    nh->kr.prefixlen);
450 	json_do_uint("priority", nh->kr.priority);
451 	json_do_bool("connected", nh->kr.flags & F_CONNECTED);
452 	json_do_string("nexthop", log_addr(&nh->kr.nexthop));
453 	if (nh->iface.ifname[0])
454 		json_do_interface(&nh->iface);
455 done:
456 	json_do_end();
457 	/* keep array open */
458 }
459 
460 static void
461 json_interface(struct ctl_show_interface *iface)
462 {
463 	json_do_array("interfaces");
464 	json_do_interface(iface);
465 }
466 
467 static void
468 json_communities(u_char *data, size_t len, struct parse_result *res)
469 {
470 	struct community c;
471 	size_t  i;
472 	uint64_t ext;
473 
474 	if (len % sizeof(c)) {
475 		warnx("communities: bad size");
476 		return;
477 	}
478 
479 	for (i = 0; i < len; i += sizeof(c)) {
480 		memcpy(&c, data + i, sizeof(c));
481 
482 		switch (c.flags) {
483 		case COMMUNITY_TYPE_BASIC:
484 			json_do_array("communities");
485 			json_do_string("community",
486 			    fmt_community(c.data1, c.data2));
487 			break;
488 		case COMMUNITY_TYPE_LARGE:
489 			json_do_array("large_communities");
490 			json_do_string("community",
491 			    fmt_large_community(c.data1, c.data2, c.data3));
492 			break;
493 		case COMMUNITY_TYPE_EXT:
494 			ext = (uint64_t)c.data3 << 48;
495 			switch ((c.data3 >> 8) & EXT_COMMUNITY_VALUE) {
496 			case EXT_COMMUNITY_TRANS_TWO_AS:
497 			case EXT_COMMUNITY_TRANS_OPAQUE:
498 			case EXT_COMMUNITY_TRANS_EVPN:
499 				ext |= ((uint64_t)c.data1 & 0xffff) << 32;
500 				ext |= (uint64_t)c.data2;
501 				break;
502 			case EXT_COMMUNITY_TRANS_FOUR_AS:
503 			case EXT_COMMUNITY_TRANS_IPV4:
504 				ext |= (uint64_t)c.data1 << 16;
505 				ext |= (uint64_t)c.data2 & 0xffff;
506 				break;
507 			}
508 			ext = htobe64(ext);
509 
510 			json_do_array("extended_communities");
511 			json_do_string("community",
512 			    fmt_ext_community((void *)&ext));
513 			break;
514 		}
515 	}
516 }
517 
518 static void
519 json_do_community(u_char *data, uint16_t len)
520 {
521 	uint16_t a, v, i;
522 
523 	if (len & 0x3) {
524 		json_do_string("error", "bad length");
525 		return;
526 	}
527 
528 	json_do_array("communities");
529 
530 	for (i = 0; i < len; i += 4) {
531 		memcpy(&a, data + i, sizeof(a));
532 		memcpy(&v, data + i + 2, sizeof(v));
533 		a = ntohs(a);
534 		v = ntohs(v);
535 		json_do_string("community", fmt_community(a, v));
536 	}
537 
538 	json_do_end();
539 }
540 
541 static void
542 json_do_large_community(u_char *data, uint16_t len)
543 {
544 	uint32_t a, l1, l2;
545 	uint16_t i;
546 
547 	if (len % 12) {
548 		json_do_string("error", "bad length");
549 		return;
550 	}
551 
552 	json_do_array("large_communities");
553 
554 	for (i = 0; i < len; i += 12) {
555 		memcpy(&a, data + i, sizeof(a));
556 		memcpy(&l1, data + i + 4, sizeof(l1));
557 		memcpy(&l2, data + i + 8, sizeof(l2));
558 		a = ntohl(a);
559 		l1 = ntohl(l1);
560 		l2 = ntohl(l2);
561 
562 		json_do_string("community",
563 		    fmt_large_community(a, l1, l2));
564 	}
565 
566 	json_do_end();
567 }
568 
569 static void
570 json_do_ext_community(u_char *data, uint16_t len)
571 {
572 	uint16_t i;
573 
574 	if (len & 0x7) {
575 		json_do_string("error", "bad length");
576 		return;
577 	}
578 
579 	json_do_array("extended_communities");
580 
581 	for (i = 0; i < len; i += 8)
582 		json_do_string("community", fmt_ext_community(data + i));
583 
584 	json_do_end();
585 }
586 
587 static void
588 json_attr(u_char *data, size_t len, int reqflags, int addpath)
589 {
590 	struct bgpd_addr prefix;
591 	struct in_addr id;
592 	char *aspath;
593 	u_char *path;
594 	uint32_t as, pathid;
595 	uint16_t alen, afi, off, short_as;
596 	uint8_t flags, type, safi, aid, prefixlen;
597 	int e4, e2, pos;
598 
599 	if (len < 3) {
600 		warnx("Too short BGP attribute");
601 		return;
602 	}
603 
604 	flags = data[0];
605 	type = data[1];
606 	if (flags & ATTR_EXTLEN) {
607 		if (len < 4) {
608 			warnx("Too short BGP attribute");
609 			return;
610 		}
611 		memcpy(&alen, data+2, sizeof(uint16_t));
612 		alen = ntohs(alen);
613 		data += 4;
614 		len -= 4;
615 	} else {
616 		alen = data[2];
617 		data += 3;
618 		len -= 3;
619 	}
620 
621 	/* bad imsg len how can that happen!? */
622 	if (alen > len) {
623 		warnx("Bad BGP attribute length");
624 		return;
625 	}
626 
627 	json_do_array("attributes");
628 
629 	json_do_object("attribute");
630 	json_do_string("type", fmt_attr(type, -1));
631 	json_do_uint("length", alen);
632 	json_do_object("flags");
633 	json_do_bool("partial", flags & ATTR_PARTIAL);
634 	json_do_bool("transitive", flags & ATTR_TRANSITIVE);
635 	json_do_bool("optional", flags & ATTR_OPTIONAL);
636 	json_do_end();
637 
638 	switch (type) {
639 	case ATTR_ORIGIN:
640 		if (alen == 1)
641 			json_do_string("origin", fmt_origin(*data, 0));
642 		else
643 			json_do_string("error", "bad length");
644 		break;
645 	case ATTR_ASPATH:
646 	case ATTR_AS4_PATH:
647 		/* prefer 4-byte AS here */
648 		e4 = aspath_verify(data, alen, 1, 0);
649 		e2 = aspath_verify(data, alen, 0, 0);
650 		if (e4 == 0 || e4 == AS_ERR_SOFT) {
651 			path = data;
652 		} else if (e2 == 0 || e2 == AS_ERR_SOFT) {
653 			path = aspath_inflate(data, alen, &alen);
654 			if (path == NULL)
655 				errx(1, "aspath_inflate failed");
656 		} else {
657 			json_do_string("error", "bad AS-Path");
658 			break;
659 		}
660 		if (aspath_asprint(&aspath, path, alen) == -1)
661 			err(1, NULL);
662 		json_do_string("aspath", aspath);
663 		free(aspath);
664 		if (path != data)
665 			free(path);
666 		break;
667 	case ATTR_NEXTHOP:
668 		if (alen == 4) {
669 			memcpy(&id, data, sizeof(id));
670 			json_do_string("nexthop", inet_ntoa(id));
671 		} else
672 			json_do_string("error", "bad length");
673 		break;
674 	case ATTR_MED:
675 	case ATTR_LOCALPREF:
676 		if (alen == 4) {
677 			uint32_t val;
678 			memcpy(&val, data, sizeof(val));
679 			json_do_uint("metric", ntohl(val));
680 		} else
681 			json_do_string("error", "bad length");
682 		break;
683 	case ATTR_AGGREGATOR:
684 	case ATTR_AS4_AGGREGATOR:
685 		if (alen == 8) {
686 			memcpy(&as, data, sizeof(as));
687 			memcpy(&id, data + sizeof(as), sizeof(id));
688 			as = ntohl(as);
689 		} else if (alen == 6) {
690 			memcpy(&short_as, data, sizeof(short_as));
691 			memcpy(&id, data + sizeof(short_as), sizeof(id));
692 			as = ntohs(short_as);
693 		} else {
694 			json_do_string("error", "bad AS-Path");
695 			break;
696 		}
697 		json_do_uint("AS", as);
698 		json_do_string("router_id", inet_ntoa(id));
699 		break;
700 	case ATTR_COMMUNITIES:
701 		json_do_community(data, alen);
702 		break;
703 	case ATTR_ORIGINATOR_ID:
704 		if (alen == 4) {
705 			memcpy(&id, data, sizeof(id));
706 			json_do_string("originator", inet_ntoa(id));
707 		} else
708 			json_do_string("error", "bad length");
709 		break;
710 	case ATTR_CLUSTER_LIST:
711 		json_do_array("cluster_list");
712 		for (off = 0; off + sizeof(id) <= alen;
713 		    off += sizeof(id)) {
714 			memcpy(&id, data + off, sizeof(id));
715 			json_do_string("cluster_id", inet_ntoa(id));
716 		}
717 		json_do_end();
718 		break;
719 	case ATTR_MP_REACH_NLRI:
720 	case ATTR_MP_UNREACH_NLRI:
721 		if (alen < 3) {
722 bad_len:
723 			json_do_string("error", "bad length");
724 			break;
725 		}
726 		memcpy(&afi, data, 2);
727 		data += 2;
728 		alen -= 2;
729 		afi = ntohs(afi);
730 		safi = *data++;
731 		alen--;
732 
733 		if (afi2aid(afi, safi, &aid) == -1) {
734 			json_do_printf("error", "bad AFI/SAFI pair: %d/%d",
735 			    afi, safi);
736 			break;
737 		}
738 		json_do_string("family", aid2str(aid));
739 
740 		if (type == ATTR_MP_REACH_NLRI) {
741 			struct bgpd_addr nexthop;
742 			uint8_t nhlen;
743 			if (len == 0)
744 				goto bad_len;
745 			nhlen = *data++;
746 			alen--;
747 			if (nhlen > len)
748 				goto bad_len;
749 			memset(&nexthop, 0, sizeof(nexthop));
750 			switch (aid) {
751 			case AID_INET6:
752 				nexthop.aid = aid;
753 				if (nhlen != 16 && nhlen != 32)
754 					goto bad_len;
755 				memcpy(&nexthop.v6.s6_addr, data, 16);
756 				break;
757 			case AID_VPN_IPv4:
758 				if (nhlen != 12)
759 					goto bad_len;
760 				nexthop.aid = AID_INET;
761 				memcpy(&nexthop.v4, data + sizeof(uint64_t),
762 				    sizeof(nexthop.v4));
763 				break;
764 			case AID_VPN_IPv6:
765 				if (nhlen != 24)
766 					goto bad_len;
767 				nexthop.aid = AID_INET6;
768 				memcpy(&nexthop.v6, data + sizeof(uint64_t),
769 				    sizeof(nexthop.v6));
770 				break;
771 			default:
772 				json_do_printf("error", "unhandled AID: %d",
773 				    aid);
774 				return;
775 			}
776 			/* ignore reserved (old SNPA) field as per RFC4760 */
777 			data += nhlen + 1;
778 			alen -= nhlen + 1;
779 
780 			json_do_string("nexthop", log_addr(&nexthop));
781 		}
782 
783 		json_do_array("NLRI");
784 		while (alen > 0) {
785 			json_do_object("prefix");
786 			if (addpath) {
787 				if (alen <= sizeof(pathid)) {
788 					json_do_string("error", "bad path-id");
789 					break;
790 				}
791 				memcpy(&pathid, data, sizeof(pathid));
792 				pathid = ntohl(pathid);
793 				data += sizeof(pathid);
794 				alen -= sizeof(pathid);
795 			}
796 			switch (aid) {
797 			case AID_INET6:
798 				pos = nlri_get_prefix6(data, alen, &prefix,
799 				    &prefixlen);
800 				break;
801 			case AID_VPN_IPv4:
802 				 pos = nlri_get_vpn4(data, alen, &prefix,
803 				     &prefixlen, 1);
804 				 break;
805 			case AID_VPN_IPv6:
806 				 pos = nlri_get_vpn6(data, alen, &prefix,
807 				     &prefixlen, 1);
808 				 break;
809 			default:
810 				json_do_printf("error", "unhandled AID: %d",
811 				    aid);
812 				return;
813 			}
814 			if (pos == -1) {
815 				json_do_printf("error", "bad %s prefix",
816 				    aid2str(aid));
817 				break;
818 			}
819 			json_do_printf("prefix", "%s/%u", log_addr(&prefix),
820 			    prefixlen);
821 			if (addpath)
822 				 json_do_uint("path_id", pathid);
823 			data += pos;
824 			alen -= pos;
825 			json_do_end();
826 		}
827 		json_do_end();
828 		break;
829 	case ATTR_EXT_COMMUNITIES:
830 		json_do_ext_community(data, alen);
831 		break;
832 	case ATTR_LARGE_COMMUNITIES:
833 		json_do_large_community(data, alen);
834 		break;
835 	case ATTR_OTC:
836 		if (alen == 4) {
837 			memcpy(&as, data, sizeof(as));
838 			as = ntohl(as);
839 			json_do_uint("as", as);
840 		} else
841 			json_do_string("error", "bad length");
842 		break;
843 	case ATTR_ATOMIC_AGGREGATE:
844 	default:
845 		if (alen)
846 			json_do_hexdump("data", data, alen);
847 		break;
848 	}
849 }
850 
851 static void
852 json_rib(struct ctl_show_rib *r, u_char *asdata, size_t aslen,
853     struct parse_result *res)
854 {
855 	struct in_addr id;
856 	char *aspath;
857 
858 	json_do_array("rib");
859 
860 	json_do_object("rib_entry");
861 
862 	json_do_printf("prefix", "%s/%u", log_addr(&r->prefix), r->prefixlen);
863 
864 	if (aspath_asprint(&aspath, asdata, aslen) == -1)
865 		err(1, NULL);
866 	json_do_string("aspath", aspath);
867 	free(aspath);
868 
869 	json_do_string("exit_nexthop", log_addr(&r->exit_nexthop));
870 	json_do_string("true_nexthop", log_addr(&r->true_nexthop));
871 
872 	json_do_object("neighbor");
873 	if (r->descr[0])
874 		json_do_string("description", r->descr);
875 	json_do_string("remote_addr", log_addr(&r->remote_addr));
876 	id.s_addr = htonl(r->remote_id);
877 	json_do_string("bgp_id", inet_ntoa(id));
878 	json_do_end();
879 
880 	if (r->flags & F_PREF_PATH_ID)
881 		json_do_uint("path_id", r->path_id);
882 
883 	/* flags */
884 	json_do_bool("valid", r->flags & F_PREF_ELIGIBLE);
885 	if (r->flags & F_PREF_BEST)
886 		json_do_bool("best", 1);
887 	if (r->flags & F_PREF_ECMP)
888 		json_do_bool("ecmp", 1);
889 	if (r->flags & F_PREF_AS_WIDE)
890 		json_do_bool("as-wide", 1);
891 	if (r->flags & F_PREF_INTERNAL)
892 		json_do_string("source", "internal");
893 	else
894 		json_do_string("source", "external");
895 	if (r->flags & F_PREF_STALE)
896 		json_do_bool("stale", 1);
897 	if (r->flags & F_PREF_ANNOUNCE)
898 		json_do_bool("announced", 1);
899 
900 	/* various attribibutes */
901 	json_do_string("ovs", fmt_ovs(r->roa_validation_state, 0));
902 	json_do_string("avs", fmt_avs(r->aspa_validation_state, 0));
903 	json_do_string("origin", fmt_origin(r->origin, 0));
904 	json_do_uint("metric", r->med);
905 	json_do_uint("localpref", r->local_pref);
906 	json_do_uint("weight", r->weight);
907 	json_do_int("dmetric", r->dmetric);
908 	json_do_string("last_update", fmt_timeframe(r->age));
909 	json_do_int("last_update_sec", r->age);
910 
911 	/* keep the object open for communities and attributes */
912 }
913 
914 static void
915 json_rib_mem_element(const char *name, uint64_t count, uint64_t size,
916     uint64_t refs)
917 {
918 	json_do_object(name);
919 	if (count != UINT64_MAX)
920 		json_do_uint("count", count);
921 	if (size != UINT64_MAX)
922 		json_do_uint("size", size);
923 	if (refs != UINT64_MAX)
924 		json_do_uint("references", refs);
925 	json_do_end();
926 }
927 
928 static void
929 json_rib_mem(struct rde_memstats *stats)
930 {
931 	size_t pts = 0;
932 	int i;
933 
934 	json_do_object("memory");
935 	for (i = 0; i < AID_MAX; i++) {
936 		if (stats->pt_cnt[i] == 0)
937 			continue;
938 		pts += stats->pt_size[i];
939 		json_rib_mem_element(aid_vals[i].name, stats->pt_cnt[i],
940 		    stats->pt_size[i], UINT64_MAX);
941 	}
942 	json_rib_mem_element("rib", stats->rib_cnt,
943 	    stats->rib_cnt * sizeof(struct rib_entry), UINT64_MAX);
944 	json_rib_mem_element("prefix", stats->prefix_cnt,
945 	    stats->prefix_cnt * sizeof(struct prefix), UINT64_MAX);
946 	json_rib_mem_element("rde_aspath", stats->path_cnt,
947 	    stats->path_cnt * sizeof(struct rde_aspath),
948 	    stats->path_refs);
949 	json_rib_mem_element("aspath", stats->aspath_cnt,
950 	    stats->aspath_size, UINT64_MAX);
951 	json_rib_mem_element("community_entries", stats->comm_cnt,
952 	    stats->comm_cnt * sizeof(struct rde_community), UINT64_MAX);
953 	json_rib_mem_element("community", stats->comm_nmemb,
954 	    stats->comm_size * sizeof(struct community), stats->comm_refs);
955 	json_rib_mem_element("attributes_entries", stats->attr_cnt,
956 	    stats->attr_cnt * sizeof(struct attr), stats->attr_refs);
957 	json_rib_mem_element("attributes", stats->attr_dcnt,
958 	    stats->attr_data, UINT64_MAX);
959 	json_rib_mem_element("total", UINT64_MAX,
960 	    pts + stats->prefix_cnt * sizeof(struct prefix) +
961 	    stats->rib_cnt * sizeof(struct rib_entry) +
962 	    stats->path_cnt * sizeof(struct rde_aspath) +
963 	    stats->aspath_size + stats->attr_cnt * sizeof(struct attr) +
964 	    stats->attr_data, UINT64_MAX);
965 	json_do_end();
966 
967 	json_do_object("sets");
968 	json_rib_mem_element("as_set", stats->aset_nmemb,
969 	    stats->aset_size, UINT64_MAX);
970 	json_rib_mem_element("as_set_tables", stats->aset_cnt, UINT64_MAX,
971 	    UINT64_MAX);
972 	json_rib_mem_element("prefix_set", stats->pset_cnt, stats->pset_size,
973 	    UINT64_MAX);
974 	json_rib_mem_element("total", UINT64_MAX,
975 	    stats->aset_size + stats->pset_size, UINT64_MAX);
976 	json_do_end();
977 }
978 
979 static void
980 json_rib_set(struct ctl_show_set *set)
981 {
982 	json_do_array("sets");
983 
984 	json_do_object("set");
985 	json_do_string("name", set->name);
986 	json_do_string("type", fmt_set_type(set));
987 	json_do_string("last_change", fmt_monotime(set->lastchange));
988 	json_do_int("last_change_sec", get_monotime(set->lastchange));
989 	if (set->type == ASNUM_SET || set->type == ASPA_SET) {
990 		json_do_uint("num_ASnum", set->as_cnt);
991 	} else {
992 		json_do_uint("num_IPv4", set->v4_cnt);
993 		json_do_uint("num_IPv6", set->v6_cnt);
994 	}
995 	json_do_end();
996 }
997 
998 static void
999 json_rtr(struct ctl_show_rtr *rtr)
1000 {
1001 	json_do_array("rtrs");
1002 
1003 	json_do_object("rtr");
1004 	if (rtr->descr[0])
1005 		json_do_string("descr", rtr->descr);
1006 	json_do_string("remote_addr", log_addr(&rtr->remote_addr));
1007 	json_do_uint("remote_port", rtr->remote_port);
1008 	if (rtr->local_addr.aid != AID_UNSPEC)
1009 		json_do_string("local_addr", log_addr(&rtr->local_addr));
1010 
1011 	if (rtr->session_id != -1) {
1012 		json_do_uint("session_id", rtr->session_id);
1013 		json_do_uint("serial", rtr->serial);
1014 	}
1015 	json_do_uint("refresh", rtr->refresh);
1016 	json_do_uint("retry", rtr->retry);
1017 	json_do_uint("expire", rtr->expire);
1018 
1019 	if (rtr->last_sent_error != NO_ERROR) {
1020 		json_do_string("last_sent_error",
1021 		    log_rtr_error(rtr->last_sent_error));
1022 		if (rtr->last_sent_msg[0])
1023 			json_do_string("last_sent_msg",
1024 			    log_reason(rtr->last_sent_msg));
1025 	}
1026 	if (rtr->last_recv_error != NO_ERROR) {
1027 		json_do_string("last_recv_error",
1028 		    log_rtr_error(rtr->last_recv_error));
1029 		if (rtr->last_recv_msg[0])
1030 			json_do_string("last_recv_msg",
1031 			    log_reason(rtr->last_recv_msg));
1032 	}
1033 }
1034 
1035 static void
1036 json_result(u_int rescode)
1037 {
1038 	if (rescode == 0)
1039 		json_do_string("status", "OK");
1040 	else if (rescode >=
1041 	    sizeof(ctl_res_strerror)/sizeof(ctl_res_strerror[0])) {
1042 		json_do_string("status", "FAILED");
1043 		json_do_printf("error", "unknown error %d", rescode);
1044 	} else {
1045 		json_do_string("status", "FAILED");
1046 		json_do_string("error", ctl_res_strerror[rescode]);
1047 	}
1048 }
1049 
1050 static void
1051 json_tail(void)
1052 {
1053 	json_do_finish();
1054 }
1055 
1056 const struct output json_output = {
1057 	.head = json_head,
1058 	.neighbor = json_neighbor,
1059 	.timer = json_timer,
1060 	.fib = json_fib,
1061 	.fib_table = json_fib_table,
1062 	.nexthop = json_nexthop,
1063 	.interface = json_interface,
1064 	.communities = json_communities,
1065 	.attr = json_attr,
1066 	.rib = json_rib,
1067 	.rib_mem = json_rib_mem,
1068 	.set = json_rib_set,
1069 	.rtr = json_rtr,
1070 	.result = json_result,
1071 	.tail = json_tail,
1072 };
1073