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