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