xref: /openbsd-src/usr.sbin/bgpctl/output_json.c (revision 2364114a542ebf506c29f634babfb954692fde92)
1*2364114aSclaudio /*	$OpenBSD: output_json.c,v 1.50 2024/12/13 19:22:01 claudio Exp $ */
214178ff0Sclaudio 
314178ff0Sclaudio /*
414178ff0Sclaudio  * Copyright (c) 2020 Claudio Jeker <claudio@openbsd.org>
514178ff0Sclaudio  *
614178ff0Sclaudio  * Permission to use, copy, modify, and distribute this software for any
714178ff0Sclaudio  * purpose with or without fee is hereby granted, provided that the above
814178ff0Sclaudio  * copyright notice and this permission notice appear in all copies.
914178ff0Sclaudio  *
1014178ff0Sclaudio  * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
1114178ff0Sclaudio  * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
1214178ff0Sclaudio  * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
1314178ff0Sclaudio  * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
1414178ff0Sclaudio  * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
1514178ff0Sclaudio  * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
1614178ff0Sclaudio  * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
1714178ff0Sclaudio  */
1814178ff0Sclaudio 
1924e1c343Sclaudio #include <endian.h>
2014178ff0Sclaudio #include <err.h>
2114178ff0Sclaudio #include <math.h>
2214178ff0Sclaudio #include <stdio.h>
2314178ff0Sclaudio #include <stdlib.h>
2414178ff0Sclaudio #include <string.h>
2514178ff0Sclaudio 
2614178ff0Sclaudio #include "bgpd.h"
2714178ff0Sclaudio #include "session.h"
2814178ff0Sclaudio #include "rde.h"
2914178ff0Sclaudio 
3014178ff0Sclaudio #include "bgpctl.h"
3114178ff0Sclaudio #include "parser.h"
3214178ff0Sclaudio #include "json.h"
3314178ff0Sclaudio 
3414178ff0Sclaudio static void
3514178ff0Sclaudio json_head(struct parse_result *res)
3614178ff0Sclaudio {
37644f9c42Sclaudio 	json_do_start(stdout);
3814178ff0Sclaudio }
3914178ff0Sclaudio 
4014178ff0Sclaudio static void
4114178ff0Sclaudio json_neighbor_capabilities(struct capabilities *capa)
4214178ff0Sclaudio {
43b6faf4c9Sclaudio 	int hascapamp = 0, hascapaap = 0;
4414178ff0Sclaudio 	uint8_t i;
4514178ff0Sclaudio 
46ecfba8dbSclaudio 	for (i = AID_MIN; i < AID_MAX; i++) {
4714178ff0Sclaudio 		if (capa->mp[i])
4814178ff0Sclaudio 			hascapamp = 1;
49b6faf4c9Sclaudio 		if (capa->add_path[i])
50b6faf4c9Sclaudio 			hascapaap = 1;
51b6faf4c9Sclaudio 	}
52b6faf4c9Sclaudio 	if (!hascapamp && !hascapaap && !capa->grestart.restart &&
53b6faf4c9Sclaudio 	    !capa->refresh && !capa->enhanced_rr && !capa->as4byte)
5414178ff0Sclaudio 		return;
5514178ff0Sclaudio 
5656bc7cf3Sclaudio 	json_do_object("capabilities", 0);
5714178ff0Sclaudio 	json_do_bool("as4byte", capa->as4byte);
5814178ff0Sclaudio 	json_do_bool("refresh", capa->refresh);
59b6faf4c9Sclaudio 	json_do_bool("enhanced_refresh", capa->enhanced_rr);
6082293aebSclaudio 	json_do_bool("extended_message", capa->ext_msg);
6114178ff0Sclaudio 
6214178ff0Sclaudio 	if (hascapamp) {
6314178ff0Sclaudio 		json_do_array("multiprotocol");
64ecfba8dbSclaudio 		for (i = AID_MIN; i < AID_MAX; i++)
6514178ff0Sclaudio 			if (capa->mp[i])
66a32a1b0fSclaudio 				json_do_string("mp", aid2str(i));
6714178ff0Sclaudio 		json_do_end();
6814178ff0Sclaudio 	}
6914178ff0Sclaudio 	if (capa->grestart.restart) {
7014178ff0Sclaudio 		int restarted = 0, present = 0;
7114178ff0Sclaudio 
72ecfba8dbSclaudio 		for (i = AID_MIN; i < AID_MAX; i++)
7314178ff0Sclaudio 			if (capa->grestart.flags[i] & CAPA_GR_PRESENT) {
7414178ff0Sclaudio 				present = 1;
7514178ff0Sclaudio 				if (capa->grestart.flags[i] & CAPA_GR_RESTART)
7614178ff0Sclaudio 					restarted = 1;
7714178ff0Sclaudio 				break;
7814178ff0Sclaudio 			}
7956bc7cf3Sclaudio 		json_do_object("graceful_restart", 0);
8014178ff0Sclaudio 		json_do_bool("eor", 1);
8114178ff0Sclaudio 		json_do_bool("restart", restarted);
8214178ff0Sclaudio 
8314178ff0Sclaudio 		if (capa->grestart.timeout)
8414178ff0Sclaudio 			json_do_uint("timeout", capa->grestart.timeout);
85*2364114aSclaudio 		if (capa->grestart.grnotification)
86*2364114aSclaudio 			json_do_bool("graceful_notification", 1);
8714178ff0Sclaudio 
8814178ff0Sclaudio 		if (present) {
8914178ff0Sclaudio 			json_do_array("protocols");
90ecfba8dbSclaudio 			for (i = AID_MIN; i < AID_MAX; i++)
9114178ff0Sclaudio 				if (capa->grestart.flags[i] & CAPA_GR_PRESENT) {
9256bc7cf3Sclaudio 					json_do_object("family", 1);
93a32a1b0fSclaudio 					json_do_string("family", aid2str(i));
9414178ff0Sclaudio 					json_do_bool("preserved",
9514178ff0Sclaudio 					    capa->grestart.flags[i] &
9614178ff0Sclaudio 					    CAPA_GR_FORWARD);
9714178ff0Sclaudio 					json_do_end();
9814178ff0Sclaudio 				}
9914178ff0Sclaudio 			json_do_end();
10014178ff0Sclaudio 		}
10114178ff0Sclaudio 
10214178ff0Sclaudio 		json_do_end();
10314178ff0Sclaudio 	}
104b6faf4c9Sclaudio 	if (hascapaap) {
105b6faf4c9Sclaudio 		json_do_array("add-path");
106ecfba8dbSclaudio 		for (i = AID_MIN; i < AID_MAX; i++)
107b6faf4c9Sclaudio 			if (capa->add_path[i]) {
10856bc7cf3Sclaudio 				json_do_object("add-path-elm", 1);
109a32a1b0fSclaudio 				json_do_string("family", aid2str(i));
110b6faf4c9Sclaudio 				switch (capa->add_path[i]) {
111b6faf4c9Sclaudio 				case CAPA_AP_RECV:
112a32a1b0fSclaudio 					json_do_string("mode", "recv");
113b6faf4c9Sclaudio 					break;
114b6faf4c9Sclaudio 				case CAPA_AP_SEND:
115a32a1b0fSclaudio 					json_do_string("mode", "send");
116b6faf4c9Sclaudio 					break;
117b6faf4c9Sclaudio 				case CAPA_AP_BIDIR:
118a32a1b0fSclaudio 					json_do_string("mode", "bidir");
119b6faf4c9Sclaudio 					break;
120b6faf4c9Sclaudio 				default:
121b6faf4c9Sclaudio 					json_do_printf("mode", "unknown %d",
122b6faf4c9Sclaudio 					    capa->add_path[i]);
123b6faf4c9Sclaudio 					break;
124b6faf4c9Sclaudio 				}
125b6faf4c9Sclaudio 				json_do_end();
126b6faf4c9Sclaudio 			}
127b6faf4c9Sclaudio 		json_do_end();
128b6faf4c9Sclaudio 	}
12914178ff0Sclaudio 
1301d3c7c09Sclaudio 	if (capa->policy) {
131a32a1b0fSclaudio 		json_do_string("open_policy",
1321d3c7c09Sclaudio 		    capa->policy == 2 ? "enforce" : "present");
133135bf897Sclaudio 	}
134135bf897Sclaudio 
13514178ff0Sclaudio 	json_do_end();
13614178ff0Sclaudio }
13714178ff0Sclaudio 
13814178ff0Sclaudio static void
13914178ff0Sclaudio json_neighbor_stats(struct peer *p)
14014178ff0Sclaudio {
14156bc7cf3Sclaudio 	json_do_object("stats", 0);
142a32a1b0fSclaudio 	json_do_string("last_read", fmt_monotime(p->stats.last_read));
143fd18fec4Sclaudio 	json_do_int("last_read_sec", get_monotime(p->stats.last_read));
144a32a1b0fSclaudio 	json_do_string("last_write", fmt_monotime(p->stats.last_write));
145fd18fec4Sclaudio 	json_do_int("last_write_sec", get_monotime(p->stats.last_write));
14614178ff0Sclaudio 
14756bc7cf3Sclaudio 	json_do_object("prefixes", 1);
14814178ff0Sclaudio 	json_do_uint("sent", p->stats.prefix_out_cnt);
14914178ff0Sclaudio 	json_do_uint("received", p->stats.prefix_cnt);
15014178ff0Sclaudio 	json_do_end();
15114178ff0Sclaudio 
15256bc7cf3Sclaudio 	json_do_object("message", 0);
15314178ff0Sclaudio 
15456bc7cf3Sclaudio 	json_do_object("sent", 0);
15514178ff0Sclaudio 	json_do_uint("open", p->stats.msg_sent_open);
15614178ff0Sclaudio 	json_do_uint("notifications", p->stats.msg_sent_notification);
15714178ff0Sclaudio 	json_do_uint("updates", p->stats.msg_sent_update);
15814178ff0Sclaudio 	json_do_uint("keepalives", p->stats.msg_sent_keepalive);
15914178ff0Sclaudio 	json_do_uint("route_refresh", p->stats.msg_sent_rrefresh);
16014178ff0Sclaudio 	json_do_uint("total",
16114178ff0Sclaudio 	    p->stats.msg_sent_open + p->stats.msg_sent_notification +
16214178ff0Sclaudio 	    p->stats.msg_sent_update + p->stats.msg_sent_keepalive +
16314178ff0Sclaudio 	    p->stats.msg_sent_rrefresh);
16414178ff0Sclaudio 	json_do_end();
16514178ff0Sclaudio 
16656bc7cf3Sclaudio 	json_do_object("received", 0);
16714178ff0Sclaudio 	json_do_uint("open", p->stats.msg_rcvd_open);
16814178ff0Sclaudio 	json_do_uint("notifications", p->stats.msg_rcvd_notification);
16914178ff0Sclaudio 	json_do_uint("updates", p->stats.msg_rcvd_update);
17014178ff0Sclaudio 	json_do_uint("keepalives", p->stats.msg_rcvd_keepalive);
17114178ff0Sclaudio 	json_do_uint("route_refresh", p->stats.msg_rcvd_rrefresh);
17214178ff0Sclaudio 	json_do_uint("total",
17314178ff0Sclaudio 	    p->stats.msg_rcvd_open + p->stats.msg_rcvd_notification +
17414178ff0Sclaudio 	    p->stats.msg_rcvd_update + p->stats.msg_rcvd_keepalive +
17514178ff0Sclaudio 	    p->stats.msg_rcvd_rrefresh);
17614178ff0Sclaudio 	json_do_end();
17714178ff0Sclaudio 
17814178ff0Sclaudio 	json_do_end();
17914178ff0Sclaudio 
18056bc7cf3Sclaudio 	json_do_object("update", 0);
18114178ff0Sclaudio 
18256bc7cf3Sclaudio 	json_do_object("sent", 1);
18314178ff0Sclaudio 	json_do_uint("updates", p->stats.prefix_sent_update);
18414178ff0Sclaudio 	json_do_uint("withdraws", p->stats.prefix_sent_withdraw);
18514178ff0Sclaudio 	json_do_uint("eor", p->stats.prefix_sent_eor);
18614178ff0Sclaudio 	json_do_end();
18714178ff0Sclaudio 
18856bc7cf3Sclaudio 	json_do_object("received", 1);
18914178ff0Sclaudio 	json_do_uint("updates", p->stats.prefix_rcvd_update);
19014178ff0Sclaudio 	json_do_uint("withdraws", p->stats.prefix_rcvd_withdraw);
19114178ff0Sclaudio 	json_do_uint("eor", p->stats.prefix_rcvd_eor);
19214178ff0Sclaudio 	json_do_end();
19314178ff0Sclaudio 
19456bc7cf3Sclaudio 	json_do_object("pending", 1);
1956c560a4bSclaudio 	json_do_uint("updates", p->stats.pending_update);
1966c560a4bSclaudio 	json_do_uint("withdraws", p->stats.pending_withdraw);
1976c560a4bSclaudio 	json_do_end();
1986c560a4bSclaudio 
19914178ff0Sclaudio 	json_do_end();
20014178ff0Sclaudio 
20156bc7cf3Sclaudio 	json_do_object("route-refresh", 0);
202b6faf4c9Sclaudio 
20356bc7cf3Sclaudio 	json_do_object("sent", 1);
204b6faf4c9Sclaudio 	json_do_uint("request", p->stats.refresh_sent_req);
205b6faf4c9Sclaudio 	json_do_uint("borr", p->stats.refresh_sent_borr);
206b6faf4c9Sclaudio 	json_do_uint("eorr", p->stats.refresh_sent_eorr);
207b6faf4c9Sclaudio 	json_do_end();
208b6faf4c9Sclaudio 
20956bc7cf3Sclaudio 	json_do_object("received", 1);
210b6faf4c9Sclaudio 	json_do_uint("request", p->stats.refresh_rcvd_req);
211b6faf4c9Sclaudio 	json_do_uint("borr", p->stats.refresh_rcvd_borr);
212b6faf4c9Sclaudio 	json_do_uint("eorr", p->stats.refresh_rcvd_eorr);
213b6faf4c9Sclaudio 	json_do_end();
214b6faf4c9Sclaudio 
215b6faf4c9Sclaudio 	json_do_end();
216b6faf4c9Sclaudio 
21714178ff0Sclaudio 	json_do_end();
21814178ff0Sclaudio }
21914178ff0Sclaudio 
22014178ff0Sclaudio static void
22114178ff0Sclaudio json_neighbor_full(struct peer *p)
22214178ff0Sclaudio {
22314178ff0Sclaudio 	const char *errstr;
22414178ff0Sclaudio 
22514178ff0Sclaudio 	/* config */
22656bc7cf3Sclaudio 	json_do_object("config", 0);
22714178ff0Sclaudio 	json_do_bool("template", p->conf.template);
22814178ff0Sclaudio 	json_do_bool("cloned", p->template != NULL);
22914178ff0Sclaudio 	json_do_bool("passive", p->conf.passive);
23014178ff0Sclaudio 	json_do_bool("down", p->conf.down);
23114178ff0Sclaudio 	json_do_bool("multihop", p->conf.ebgp && p->conf.distance > 1);
23214178ff0Sclaudio 	if (p->conf.ebgp && p->conf.distance > 1)
23314178ff0Sclaudio 		json_do_uint("multihop_distance", p->conf.distance);
23414178ff0Sclaudio 	if (p->conf.max_prefix) {
23514178ff0Sclaudio 		json_do_uint("max_prefix", p->conf.max_prefix);
23614178ff0Sclaudio 		if (p->conf.max_prefix_restart)
23714178ff0Sclaudio 			json_do_uint("max_prefix_restart",
23814178ff0Sclaudio 			    p->conf.max_prefix_restart);
23914178ff0Sclaudio 	}
24014178ff0Sclaudio 	if (p->conf.max_out_prefix) {
2411d861d65Sclaudio 		json_do_uint("max_out_prefix", p->conf.max_out_prefix);
24214178ff0Sclaudio 		if (p->conf.max_out_prefix_restart)
24314178ff0Sclaudio 			json_do_uint("max_out_prefix_restart",
24414178ff0Sclaudio 			    p->conf.max_out_prefix_restart);
24514178ff0Sclaudio 	}
246d3e7ac16Sclaudio 	if (p->auth_conf.method != AUTH_NONE)
247a32a1b0fSclaudio 		json_do_string("authentication",
248d3e7ac16Sclaudio 		    fmt_auth_method(p->auth_conf.method));
24914178ff0Sclaudio 	json_do_bool("ttl_security", p->conf.ttlsec);
25014178ff0Sclaudio 	json_do_uint("holdtime", p->conf.holdtime);
25114178ff0Sclaudio 	json_do_uint("min_holdtime", p->conf.min_holdtime);
2521d3c7c09Sclaudio 	if (p->conf.ebgp && p->conf.role != ROLE_NONE)
253a32a1b0fSclaudio 		json_do_string("role", log_policy(p->conf.role));
25414178ff0Sclaudio 
25514178ff0Sclaudio 	/* capabilities */
25614178ff0Sclaudio 	json_neighbor_capabilities(&p->conf.capabilities);
25714178ff0Sclaudio 
25814178ff0Sclaudio 	json_do_end();
25914178ff0Sclaudio 
26014178ff0Sclaudio 
26114178ff0Sclaudio 	/* stats */
26214178ff0Sclaudio 	json_neighbor_stats(p);
26314178ff0Sclaudio 
26414178ff0Sclaudio 	/* errors */
26569d2b5abSclaudio 	if (p->conf.reason[0])
266a32a1b0fSclaudio 		json_do_string("my_shutdown_reason",
267a78f83ceSderaadt 		    log_reason(p->conf.reason));
26869d2b5abSclaudio 	if (p->stats.last_reason[0])
269a32a1b0fSclaudio 		json_do_string("last_shutdown_reason",
270a78f83ceSderaadt 		    log_reason(p->stats.last_reason));
27114178ff0Sclaudio 	errstr = fmt_errstr(p->stats.last_sent_errcode,
27214178ff0Sclaudio 	    p->stats.last_sent_suberr);
27314178ff0Sclaudio 	if (errstr)
274a32a1b0fSclaudio 		json_do_string("last_error_sent", errstr);
27514178ff0Sclaudio 	errstr = fmt_errstr(p->stats.last_rcvd_errcode,
27614178ff0Sclaudio 	    p->stats.last_rcvd_suberr);
27714178ff0Sclaudio 	if (errstr)
278a32a1b0fSclaudio 		json_do_string("last_error_received", errstr);
27914178ff0Sclaudio 
28014178ff0Sclaudio 	/* connection info */
28114178ff0Sclaudio 	if (p->state >= STATE_OPENSENT) {
28256bc7cf3Sclaudio 		json_do_object("session", 0);
28314178ff0Sclaudio 		json_do_uint("holdtime", p->holdtime);
28414178ff0Sclaudio 		json_do_uint("keepalive", p->holdtime / 3);
28514178ff0Sclaudio 
28656bc7cf3Sclaudio 		json_do_object("local", 0);
287a32a1b0fSclaudio 		json_do_string("address", log_addr(&p->local));
28814178ff0Sclaudio 		json_do_uint("port", p->local_port);
28914178ff0Sclaudio 		json_neighbor_capabilities(&p->capa.ann);
29014178ff0Sclaudio 		json_do_end();
29114178ff0Sclaudio 
29256bc7cf3Sclaudio 		json_do_object("remote", 0);
293a32a1b0fSclaudio 		json_do_string("address", log_addr(&p->remote));
29414178ff0Sclaudio 		json_do_uint("port", p->remote_port);
29514178ff0Sclaudio 		json_neighbor_capabilities(&p->capa.peer);
29614178ff0Sclaudio 		json_do_end();
29714178ff0Sclaudio 
29814178ff0Sclaudio 		/* capabilities */
29914178ff0Sclaudio 		json_neighbor_capabilities(&p->capa.neg);
30014178ff0Sclaudio 
3011d3c7c09Sclaudio 		if (p->conf.ebgp && p->conf.role != ROLE_NONE) {
302a32a1b0fSclaudio 			json_do_string("remote_role",
3031d3c7c09Sclaudio 			    log_policy(p->remote_role));
304a32a1b0fSclaudio 			json_do_string("local_role",
3051d3c7c09Sclaudio 			    log_policy(p->conf.role));
3061d3c7c09Sclaudio 		}
30714178ff0Sclaudio 		json_do_end();
30814178ff0Sclaudio 	}
30914178ff0Sclaudio }
31014178ff0Sclaudio 
31114178ff0Sclaudio static void
31214178ff0Sclaudio json_neighbor(struct peer *p, struct parse_result *res)
31314178ff0Sclaudio {
31414178ff0Sclaudio 	json_do_array("neighbors");
31514178ff0Sclaudio 
31656bc7cf3Sclaudio 	json_do_object("neighbor", 0);
31714178ff0Sclaudio 
318a32a1b0fSclaudio 	json_do_string("remote_as", log_as(p->conf.remote_as));
31914178ff0Sclaudio 	if (p->conf.descr[0])
320a32a1b0fSclaudio 		json_do_string("description", p->conf.descr);
3211d861d65Sclaudio 	if (p->conf.group[0])
322a32a1b0fSclaudio 		json_do_string("group", p->conf.group);
32314178ff0Sclaudio 	if (!p->conf.template)
324a32a1b0fSclaudio 		json_do_string("remote_addr", log_addr(&p->conf.remote_addr));
32514178ff0Sclaudio 	else
32614178ff0Sclaudio 		json_do_printf("remote_addr", "%s/%u",
32714178ff0Sclaudio 		    log_addr(&p->conf.remote_addr), p->conf.remote_masklen);
32814178ff0Sclaudio 	if (p->state == STATE_ESTABLISHED) {
32914178ff0Sclaudio 		struct in_addr ina;
330765bd20aSclaudio 		ina.s_addr = htonl(p->remote_bgpid);
331a32a1b0fSclaudio 		json_do_string("bgpid", inet_ntoa(ina));
33214178ff0Sclaudio 	}
333a32a1b0fSclaudio 	json_do_string("state", statenames[p->state]);
334a32a1b0fSclaudio 	json_do_string("last_updown", fmt_monotime(p->stats.last_updown));
335fd18fec4Sclaudio 	json_do_int("last_updown_sec", get_monotime(p->stats.last_updown));
33614178ff0Sclaudio 
33714178ff0Sclaudio 	switch (res->action) {
33814178ff0Sclaudio 	case SHOW:
33914178ff0Sclaudio 	case SHOW_SUMMARY:
34014178ff0Sclaudio 	case SHOW_SUMMARY_TERSE:
34114178ff0Sclaudio 		/* only show basic data */
34214178ff0Sclaudio 		break;
34314178ff0Sclaudio 	case SHOW_NEIGHBOR:
34414178ff0Sclaudio 	case SHOW_NEIGHBOR_TIMERS:
34514178ff0Sclaudio 	case SHOW_NEIGHBOR_TERSE:
34614178ff0Sclaudio 		json_neighbor_full(p);
34714178ff0Sclaudio 		break;
34814178ff0Sclaudio 	default:
34914178ff0Sclaudio 		break;
35014178ff0Sclaudio 	}
35114178ff0Sclaudio 
35214178ff0Sclaudio 	/* keep the object open in case there are timers */
35314178ff0Sclaudio }
35414178ff0Sclaudio 
35514178ff0Sclaudio static void
35614178ff0Sclaudio json_timer(struct ctl_timer *t)
35714178ff0Sclaudio {
35814178ff0Sclaudio 	json_do_array("timers");
35914178ff0Sclaudio 
36056bc7cf3Sclaudio 	json_do_object("timer", 1);
361a32a1b0fSclaudio 	json_do_string("name", timernames[t->type]);
36214178ff0Sclaudio 	json_do_int("due", t->val);
36314178ff0Sclaudio 	json_do_end();
36414178ff0Sclaudio }
36514178ff0Sclaudio 
36614178ff0Sclaudio static void
36714178ff0Sclaudio json_fib(struct kroute_full *kf)
36814178ff0Sclaudio {
36914178ff0Sclaudio 	const char *origin;
37014178ff0Sclaudio 
37114178ff0Sclaudio 	json_do_array("fib");
37214178ff0Sclaudio 
37356bc7cf3Sclaudio 	json_do_object("fib_entry", 0);
37414178ff0Sclaudio 
37514178ff0Sclaudio 	json_do_printf("prefix", "%s/%u", log_addr(&kf->prefix), kf->prefixlen);
37614178ff0Sclaudio 	json_do_uint("priority", kf->priority);
377ef0c8610Sclaudio 	if (kf->flags & F_BGPD)
37814178ff0Sclaudio 		origin = "bgp";
37914178ff0Sclaudio 	else if (kf->flags & F_CONNECTED)
38014178ff0Sclaudio 		origin = "connected";
38114178ff0Sclaudio 	else if (kf->flags & F_STATIC)
38214178ff0Sclaudio 		origin = "static";
38314178ff0Sclaudio 	else
38414178ff0Sclaudio 		origin = "unknown";
385a32a1b0fSclaudio 	json_do_string("origin", origin);
38614178ff0Sclaudio 	json_do_bool("used_by_nexthop", kf->flags & F_NEXTHOP);
38714178ff0Sclaudio 	json_do_bool("blackhole", kf->flags & F_BLACKHOLE);
38814178ff0Sclaudio 	json_do_bool("reject", kf->flags & F_REJECT);
38914178ff0Sclaudio 
39014178ff0Sclaudio 	if (kf->flags & F_CONNECTED)
39114178ff0Sclaudio 		json_do_printf("nexthop", "link#%u", kf->ifindex);
39214178ff0Sclaudio 	else
393a32a1b0fSclaudio 		json_do_string("nexthop", log_addr(&kf->nexthop));
39414178ff0Sclaudio 
3951edf3470Sclaudio 	if (kf->flags & F_MPLS) {
3961edf3470Sclaudio 		json_do_array("mplslabel");
3971edf3470Sclaudio 		json_do_uint("mplslabel",
3981edf3470Sclaudio 		    ntohl(kf->mplslabel) >> MPLS_LABEL_OFFSET);
3991edf3470Sclaudio 		json_do_end();
4001edf3470Sclaudio 	}
40114178ff0Sclaudio 	json_do_end();
40214178ff0Sclaudio }
40314178ff0Sclaudio 
40414178ff0Sclaudio static void
40514178ff0Sclaudio json_fib_table(struct ktable *kt)
40614178ff0Sclaudio {
40714178ff0Sclaudio 	json_do_array("fibtables");
40814178ff0Sclaudio 
40956bc7cf3Sclaudio 	json_do_object("fibtable", 0);
41014178ff0Sclaudio 	json_do_uint("rtableid", kt->rtableid);
411a32a1b0fSclaudio 	json_do_string("description", kt->descr);
41214178ff0Sclaudio 	json_do_bool("coupled", kt->fib_sync);
41314178ff0Sclaudio 	json_do_bool("admin_change", kt->fib_sync != kt->fib_conf);
41414178ff0Sclaudio 	json_do_end();
41514178ff0Sclaudio }
41614178ff0Sclaudio 
41714178ff0Sclaudio static void
41814178ff0Sclaudio json_do_interface(struct ctl_show_interface *iface)
41914178ff0Sclaudio {
42056bc7cf3Sclaudio 	json_do_object("interface", 0);
42114178ff0Sclaudio 
422a32a1b0fSclaudio 	json_do_string("name", iface->ifname);
42314178ff0Sclaudio 	json_do_uint("rdomain", iface->rdomain);
42414178ff0Sclaudio 	json_do_bool("is_up", iface->is_up);
42514178ff0Sclaudio 	json_do_bool("nh_reachable", iface->nh_reachable);
42614178ff0Sclaudio 
42714178ff0Sclaudio 	if (iface->media[0])
428a32a1b0fSclaudio 		json_do_string("media", iface->media);
42914178ff0Sclaudio 
430a32a1b0fSclaudio 	json_do_string("linkstate", iface->linkstate);
43114178ff0Sclaudio 	if (iface->baudrate > 0)
43214178ff0Sclaudio 		json_do_uint("baudrate", iface->baudrate);
43314178ff0Sclaudio 
43414178ff0Sclaudio 	json_do_end();
43514178ff0Sclaudio }
43614178ff0Sclaudio 
43714178ff0Sclaudio static void
43814178ff0Sclaudio json_nexthop(struct ctl_show_nexthop *nh)
43914178ff0Sclaudio {
44014178ff0Sclaudio 	json_do_array("nexthops");
44114178ff0Sclaudio 
44256bc7cf3Sclaudio 	json_do_object("nexthop", 0);
44314178ff0Sclaudio 
444a32a1b0fSclaudio 	json_do_string("address", log_addr(&nh->addr));
44514178ff0Sclaudio 	json_do_bool("valid", nh->valid);
44614178ff0Sclaudio 
44714178ff0Sclaudio 	if (!nh->krvalid)
44814178ff0Sclaudio 		goto done;
44914178ff0Sclaudio 
450fe65f56aSclaudio 	json_do_printf("prefix", "%s/%u", log_addr(&nh->kr.prefix),
451fe65f56aSclaudio 	    nh->kr.prefixlen);
452fe65f56aSclaudio 	json_do_uint("priority", nh->kr.priority);
453fe65f56aSclaudio 	json_do_bool("connected", nh->kr.flags & F_CONNECTED);
454a32a1b0fSclaudio 	json_do_string("nexthop", log_addr(&nh->kr.nexthop));
45514178ff0Sclaudio 	if (nh->iface.ifname[0])
45614178ff0Sclaudio 		json_do_interface(&nh->iface);
45714178ff0Sclaudio done:
45814178ff0Sclaudio 	json_do_end();
45914178ff0Sclaudio 	/* keep array open */
46014178ff0Sclaudio }
46114178ff0Sclaudio 
46214178ff0Sclaudio static void
46314178ff0Sclaudio json_interface(struct ctl_show_interface *iface)
46414178ff0Sclaudio {
46514178ff0Sclaudio 	json_do_array("interfaces");
46614178ff0Sclaudio 	json_do_interface(iface);
46714178ff0Sclaudio }
46814178ff0Sclaudio 
46914178ff0Sclaudio static void
4704b4c1963Sclaudio json_communities(struct ibuf *data, struct parse_result *res)
47114178ff0Sclaudio {
47214178ff0Sclaudio 	struct community c;
47314178ff0Sclaudio 	uint64_t ext;
47414178ff0Sclaudio 
4754b4c1963Sclaudio 
4764b4c1963Sclaudio 	while (ibuf_size(data) != 0) {
4774b4c1963Sclaudio 		if (ibuf_get(data, &c, sizeof(c)) == -1) {
4784b4c1963Sclaudio 			warn("communities");
47914178ff0Sclaudio 			return;
48014178ff0Sclaudio 		}
48114178ff0Sclaudio 
48214178ff0Sclaudio 		switch (c.flags) {
48314178ff0Sclaudio 		case COMMUNITY_TYPE_BASIC:
48414178ff0Sclaudio 			json_do_array("communities");
485a32a1b0fSclaudio 			json_do_string("community",
48614178ff0Sclaudio 			    fmt_community(c.data1, c.data2));
48714178ff0Sclaudio 			break;
48814178ff0Sclaudio 		case COMMUNITY_TYPE_LARGE:
48914178ff0Sclaudio 			json_do_array("large_communities");
490a32a1b0fSclaudio 			json_do_string("community",
49114178ff0Sclaudio 			    fmt_large_community(c.data1, c.data2, c.data3));
49214178ff0Sclaudio 			break;
49314178ff0Sclaudio 		case COMMUNITY_TYPE_EXT:
49414178ff0Sclaudio 			ext = (uint64_t)c.data3 << 48;
495c2371130Sclaudio 			switch ((c.data3 >> 8) & EXT_COMMUNITY_VALUE) {
49614178ff0Sclaudio 			case EXT_COMMUNITY_TRANS_TWO_AS:
49714178ff0Sclaudio 			case EXT_COMMUNITY_TRANS_OPAQUE:
49814178ff0Sclaudio 			case EXT_COMMUNITY_TRANS_EVPN:
49914178ff0Sclaudio 				ext |= ((uint64_t)c.data1 & 0xffff) << 32;
50014178ff0Sclaudio 				ext |= (uint64_t)c.data2;
50114178ff0Sclaudio 				break;
50214178ff0Sclaudio 			case EXT_COMMUNITY_TRANS_FOUR_AS:
50314178ff0Sclaudio 			case EXT_COMMUNITY_TRANS_IPV4:
50414178ff0Sclaudio 				ext |= (uint64_t)c.data1 << 16;
50514178ff0Sclaudio 				ext |= (uint64_t)c.data2 & 0xffff;
50614178ff0Sclaudio 				break;
50714178ff0Sclaudio 			}
50814178ff0Sclaudio 
50914178ff0Sclaudio 			json_do_array("extended_communities");
5104b4c1963Sclaudio 			json_do_string("community", fmt_ext_community(ext));
51114178ff0Sclaudio 			break;
51214178ff0Sclaudio 		}
51314178ff0Sclaudio 	}
51414178ff0Sclaudio }
51514178ff0Sclaudio 
51614178ff0Sclaudio static void
517dd147aaaSclaudio json_do_community(struct ibuf *buf)
51814178ff0Sclaudio {
519dd147aaaSclaudio 	uint16_t a, v;
52014178ff0Sclaudio 
52114178ff0Sclaudio 	json_do_array("communities");
52214178ff0Sclaudio 
523dd147aaaSclaudio 	while (ibuf_size(buf) > 0) {
524dd147aaaSclaudio 		if (ibuf_get_n16(buf, &a) == -1 ||
525dd147aaaSclaudio 		    ibuf_get_n16(buf, &v) == -1) {
526dd147aaaSclaudio 			json_do_string("error", "bad length");
527dd147aaaSclaudio 			return;
528dd147aaaSclaudio 		}
529a32a1b0fSclaudio 		json_do_string("community", fmt_community(a, v));
53014178ff0Sclaudio 	}
53114178ff0Sclaudio 
53214178ff0Sclaudio 	json_do_end();
53314178ff0Sclaudio }
53414178ff0Sclaudio 
53514178ff0Sclaudio static void
536dd147aaaSclaudio json_do_large_community(struct ibuf *buf)
53714178ff0Sclaudio {
53814178ff0Sclaudio 	uint32_t a, l1, l2;
53914178ff0Sclaudio 
54014178ff0Sclaudio 	json_do_array("large_communities");
54114178ff0Sclaudio 
542dd147aaaSclaudio 	while (ibuf_size(buf) > 0) {
543dd147aaaSclaudio 		if (ibuf_get_n32(buf, &a) == -1 ||
544dd147aaaSclaudio 		    ibuf_get_n32(buf, &l1) == -1 ||
545dd147aaaSclaudio 		    ibuf_get_n32(buf, &l2) == -1) {
546dd147aaaSclaudio 			json_do_string("error", "bad length");
547dd147aaaSclaudio 			return;
548dd147aaaSclaudio 		}
549dd147aaaSclaudio 		json_do_string("community", fmt_large_community(a, l1, l2));
55014178ff0Sclaudio 	}
55114178ff0Sclaudio 
55214178ff0Sclaudio 	json_do_end();
55314178ff0Sclaudio }
55414178ff0Sclaudio 
55514178ff0Sclaudio static void
556dd147aaaSclaudio json_do_ext_community(struct ibuf *buf)
55714178ff0Sclaudio {
5584b4c1963Sclaudio 	uint64_t ext;
559dd147aaaSclaudio 	json_do_array("extended_communities");
56014178ff0Sclaudio 
561dd147aaaSclaudio 	while (ibuf_size(buf) > 0) {
562dd147aaaSclaudio 		if (ibuf_get_n64(buf, &ext) == -1) {
563a32a1b0fSclaudio 			json_do_string("error", "bad length");
56414178ff0Sclaudio 			return;
56514178ff0Sclaudio 		}
5664b4c1963Sclaudio 		json_do_string("community", fmt_ext_community(ext));
5674b4c1963Sclaudio 	}
56814178ff0Sclaudio 
56914178ff0Sclaudio 	json_do_end();
57014178ff0Sclaudio }
57114178ff0Sclaudio 
57214178ff0Sclaudio static void
573dd147aaaSclaudio json_attr(struct ibuf *buf, int reqflags, int addpath)
57414178ff0Sclaudio {
57514178ff0Sclaudio 	struct bgpd_addr prefix;
57614178ff0Sclaudio 	struct in_addr id;
577dd147aaaSclaudio 	struct ibuf asbuf, *path = NULL;
57814178ff0Sclaudio 	char *aspath;
579dd147aaaSclaudio 	uint32_t as, pathid, val;
580dd147aaaSclaudio 	uint16_t alen, afi, short_as;
581dd147aaaSclaudio 	uint8_t flags, type, safi, aid, prefixlen, origin;
5825ad4fcbaSclaudio 	int e4, e2;
58314178ff0Sclaudio 
58414178ff0Sclaudio 	json_do_array("attributes");
58556bc7cf3Sclaudio 	json_do_object("attribute", 0);
586dd147aaaSclaudio 
587dd147aaaSclaudio 	if (ibuf_get_n8(buf, &flags) == -1 ||
588dd147aaaSclaudio 	    ibuf_get_n8(buf, &type) == -1)
589dd147aaaSclaudio 		goto bad_len;
590dd147aaaSclaudio 
591a32a1b0fSclaudio 	json_do_string("type", fmt_attr(type, -1));
59256bc7cf3Sclaudio 	json_do_object("flags", 1);
59314178ff0Sclaudio 	json_do_bool("partial", flags & ATTR_PARTIAL);
59414178ff0Sclaudio 	json_do_bool("transitive", flags & ATTR_TRANSITIVE);
59514178ff0Sclaudio 	json_do_bool("optional", flags & ATTR_OPTIONAL);
59614178ff0Sclaudio 	json_do_end();
59714178ff0Sclaudio 
598dd147aaaSclaudio 	if (flags & ATTR_EXTLEN) {
599dd147aaaSclaudio 		uint16_t attr_len;
600dd147aaaSclaudio 		if (ibuf_get_n16(buf, &attr_len) == -1)
601dd147aaaSclaudio 			goto bad_len;
602dd147aaaSclaudio 		alen = attr_len;
603dd147aaaSclaudio 	} else {
604dd147aaaSclaudio 		uint8_t attr_len;
605dd147aaaSclaudio 		if (ibuf_get_n8(buf, &attr_len) == -1)
606dd147aaaSclaudio 			goto bad_len;
607dd147aaaSclaudio 		alen = attr_len;
608dd147aaaSclaudio 	}
609dd147aaaSclaudio 
610dd147aaaSclaudio 	json_do_uint("length", alen);
611dd147aaaSclaudio 
612dd147aaaSclaudio 	/* bad imsg len how can that happen!? */
613dd147aaaSclaudio 	if (alen > ibuf_size(buf))
614dd147aaaSclaudio 		goto bad_len;
615dd147aaaSclaudio 
61614178ff0Sclaudio 	switch (type) {
61714178ff0Sclaudio 	case ATTR_ORIGIN:
618dd147aaaSclaudio 		if (alen != 1 || ibuf_get_n8(buf, &origin) == -1)
619dd147aaaSclaudio 			goto bad_len;
620dd147aaaSclaudio 		json_do_string("origin", fmt_origin(origin, 0));
62114178ff0Sclaudio 		break;
62214178ff0Sclaudio 	case ATTR_ASPATH:
62314178ff0Sclaudio 	case ATTR_AS4_PATH:
62414178ff0Sclaudio 		/* prefer 4-byte AS here */
62546d5331aSclaudio 		e4 = aspath_verify(buf, 1, 0);
62646d5331aSclaudio 		e2 = aspath_verify(buf, 0, 0);
62714178ff0Sclaudio 		if (e4 == 0 || e4 == AS_ERR_SOFT) {
62846d5331aSclaudio 			ibuf_from_ibuf(&asbuf, buf);
62914178ff0Sclaudio 		} else if (e2 == 0 || e2 == AS_ERR_SOFT) {
63046d5331aSclaudio 			if ((path = aspath_inflate(buf)) == NULL) {
63146d5331aSclaudio 				json_do_string("error",
63246d5331aSclaudio 				    "aspath_inflate failed");
63346d5331aSclaudio 				break;
63446d5331aSclaudio 			}
63546d5331aSclaudio 			ibuf_from_ibuf(&asbuf, path);
63614178ff0Sclaudio 		} else {
637a32a1b0fSclaudio 			json_do_string("error", "bad AS-Path");
63814178ff0Sclaudio 			break;
63914178ff0Sclaudio 		}
64046d5331aSclaudio 		if (aspath_asprint(&aspath, &asbuf) == -1)
64114178ff0Sclaudio 			err(1, NULL);
642a32a1b0fSclaudio 		json_do_string("aspath", aspath);
64314178ff0Sclaudio 		free(aspath);
64446d5331aSclaudio 		ibuf_free(path);
64514178ff0Sclaudio 		break;
64614178ff0Sclaudio 	case ATTR_NEXTHOP:
647dd147aaaSclaudio 		if (alen != 4 || ibuf_get(buf, &id, sizeof(id)) == -1)
648dd147aaaSclaudio 			goto bad_len;
649a32a1b0fSclaudio 		json_do_string("nexthop", inet_ntoa(id));
65014178ff0Sclaudio 		break;
65114178ff0Sclaudio 	case ATTR_MED:
65214178ff0Sclaudio 	case ATTR_LOCALPREF:
653dd147aaaSclaudio 		if (alen != 4 || ibuf_get_n32(buf, &val) == -1)
654dd147aaaSclaudio 			goto bad_len;
655dd147aaaSclaudio 		json_do_uint("metric", val);
65614178ff0Sclaudio 		break;
65714178ff0Sclaudio 	case ATTR_AGGREGATOR:
65814178ff0Sclaudio 	case ATTR_AS4_AGGREGATOR:
65914178ff0Sclaudio 		if (alen == 8) {
660dd147aaaSclaudio 			if (ibuf_get_n32(buf, &as) == -1 ||
661dd147aaaSclaudio 			    ibuf_get(buf, &id, sizeof(id)) == -1)
662dd147aaaSclaudio 				goto bad_len;
66314178ff0Sclaudio 		} else if (alen == 6) {
664dd147aaaSclaudio 			if (ibuf_get_n16(buf, &short_as) == -1 ||
665dd147aaaSclaudio 			    ibuf_get(buf, &id, sizeof(id)) == -1)
666dd147aaaSclaudio 				goto bad_len;
667dd147aaaSclaudio 			as = short_as;
66814178ff0Sclaudio 		} else {
669dd147aaaSclaudio 			goto bad_len;
67014178ff0Sclaudio 		}
67114178ff0Sclaudio 		json_do_uint("AS", as);
672a32a1b0fSclaudio 		json_do_string("router_id", inet_ntoa(id));
67314178ff0Sclaudio 		break;
67414178ff0Sclaudio 	case ATTR_COMMUNITIES:
675dd147aaaSclaudio 		json_do_community(buf);
67614178ff0Sclaudio 		break;
67714178ff0Sclaudio 	case ATTR_ORIGINATOR_ID:
678dd147aaaSclaudio 		if (alen != 4 || ibuf_get(buf, &id, sizeof(id)) == -1)
679dd147aaaSclaudio 			goto bad_len;
680a32a1b0fSclaudio 		json_do_string("originator", inet_ntoa(id));
68114178ff0Sclaudio 		break;
68214178ff0Sclaudio 	case ATTR_CLUSTER_LIST:
68314178ff0Sclaudio 		json_do_array("cluster_list");
684dd147aaaSclaudio 		while (ibuf_size(buf) > 0) {
685dd147aaaSclaudio 			if (ibuf_get(buf, &id, sizeof(id)) == -1)
686dd147aaaSclaudio 				goto bad_len;
687a32a1b0fSclaudio 			json_do_string("cluster_id", inet_ntoa(id));
68814178ff0Sclaudio 		}
68914178ff0Sclaudio 		json_do_end();
69014178ff0Sclaudio 		break;
69114178ff0Sclaudio 	case ATTR_MP_REACH_NLRI:
69214178ff0Sclaudio 	case ATTR_MP_UNREACH_NLRI:
693dd147aaaSclaudio 		if (ibuf_get_n16(buf, &afi) == -1 ||
694dd147aaaSclaudio 		    ibuf_get_n8(buf, &safi) == -1)
695dd147aaaSclaudio 			goto bad_len;
69614178ff0Sclaudio 
69714178ff0Sclaudio 		if (afi2aid(afi, safi, &aid) == -1) {
69814178ff0Sclaudio 			json_do_printf("error", "bad AFI/SAFI pair: %d/%d",
69914178ff0Sclaudio 			    afi, safi);
70014178ff0Sclaudio 			break;
70114178ff0Sclaudio 		}
702a32a1b0fSclaudio 		json_do_string("family", aid2str(aid));
70314178ff0Sclaudio 
70414178ff0Sclaudio 		if (type == ATTR_MP_REACH_NLRI) {
70514178ff0Sclaudio 			struct bgpd_addr nexthop;
70614178ff0Sclaudio 			uint8_t nhlen;
707dd147aaaSclaudio 			if (ibuf_get_n8(buf, &nhlen) == -1)
70814178ff0Sclaudio 				goto bad_len;
70914178ff0Sclaudio 			memset(&nexthop, 0, sizeof(nexthop));
71014178ff0Sclaudio 			switch (aid) {
71114178ff0Sclaudio 			case AID_INET6:
71214178ff0Sclaudio 				nexthop.aid = aid;
71314178ff0Sclaudio 				if (nhlen != 16 && nhlen != 32)
71414178ff0Sclaudio 					goto bad_len;
715dd147aaaSclaudio 				if (ibuf_get(buf, &nexthop.v6,
716dd147aaaSclaudio 				    sizeof(nexthop.v6)) == -1)
717dd147aaaSclaudio 					goto bad_len;
71814178ff0Sclaudio 				break;
71914178ff0Sclaudio 			case AID_VPN_IPv4:
72014178ff0Sclaudio 				if (nhlen != 12)
72114178ff0Sclaudio 					goto bad_len;
72214178ff0Sclaudio 				nexthop.aid = AID_INET;
723dd147aaaSclaudio 				if (ibuf_skip(buf, sizeof(uint64_t)) == -1 ||
724dd147aaaSclaudio 				    ibuf_get(buf, &nexthop.v4,
725dd147aaaSclaudio 				    sizeof(nexthop.v4)) == -1)
726dd147aaaSclaudio 					goto bad_len;
72714178ff0Sclaudio 				break;
72814178ff0Sclaudio 			case AID_VPN_IPv6:
72914178ff0Sclaudio 				if (nhlen != 24)
73014178ff0Sclaudio 					goto bad_len;
73114178ff0Sclaudio 				nexthop.aid = AID_INET6;
732dd147aaaSclaudio 				if (ibuf_skip(buf, sizeof(uint64_t)) == -1 ||
733dd147aaaSclaudio 				    ibuf_get(buf, &nexthop.v6,
734dd147aaaSclaudio 				    sizeof(nexthop.v6)) == -1)
735dd147aaaSclaudio 					goto bad_len;
73614178ff0Sclaudio 				break;
73714178ff0Sclaudio 			default:
73814178ff0Sclaudio 				json_do_printf("error", "unhandled AID: %d",
73914178ff0Sclaudio 				    aid);
74014178ff0Sclaudio 				return;
74114178ff0Sclaudio 			}
74214178ff0Sclaudio 			/* ignore reserved (old SNPA) field as per RFC4760 */
743dd147aaaSclaudio 			if (ibuf_skip(buf, 1) == -1)
744dd147aaaSclaudio 				goto bad_len;
74514178ff0Sclaudio 
746a32a1b0fSclaudio 			json_do_string("nexthop", log_addr(&nexthop));
74714178ff0Sclaudio 		}
74814178ff0Sclaudio 
74914178ff0Sclaudio 		json_do_array("NLRI");
7505ad4fcbaSclaudio 		while (ibuf_size(buf) > 0) {
75156bc7cf3Sclaudio 			json_do_object("prefix", 1);
7525ad4fcbaSclaudio 			if (addpath)
7535ad4fcbaSclaudio 				if (ibuf_get_n32(buf, &pathid) == -1)
7545ad4fcbaSclaudio 					goto bad_len;
75514178ff0Sclaudio 			switch (aid) {
75614178ff0Sclaudio 			case AID_INET6:
7575ad4fcbaSclaudio 				if (nlri_get_prefix6(buf, &prefix,
7585ad4fcbaSclaudio 				    &prefixlen) == -1)
7595ad4fcbaSclaudio 					goto bad_len;
76014178ff0Sclaudio 				break;
76114178ff0Sclaudio 			case AID_VPN_IPv4:
7625ad4fcbaSclaudio 				if (nlri_get_vpn4(buf, &prefix,
7635ad4fcbaSclaudio 				    &prefixlen, 1) == -1)
7645ad4fcbaSclaudio 					goto bad_len;
76514178ff0Sclaudio 				break;
76614178ff0Sclaudio 			case AID_VPN_IPv6:
7675ad4fcbaSclaudio 				if (nlri_get_vpn6(buf, &prefix,
7685ad4fcbaSclaudio 				    &prefixlen, 1) == -1)
7695ad4fcbaSclaudio 					goto bad_len;
77014178ff0Sclaudio 				break;
77114178ff0Sclaudio 			default:
77214178ff0Sclaudio 				json_do_printf("error", "unhandled AID: %d",
77314178ff0Sclaudio 				    aid);
77414178ff0Sclaudio 				return;
77514178ff0Sclaudio 			}
77614178ff0Sclaudio 			json_do_printf("prefix", "%s/%u", log_addr(&prefix),
77714178ff0Sclaudio 			    prefixlen);
7789e59dee7Sclaudio 			if (addpath)
7799e59dee7Sclaudio 				 json_do_uint("path_id", pathid);
7809e59dee7Sclaudio 			json_do_end();
78114178ff0Sclaudio 		}
7829e59dee7Sclaudio 		json_do_end();
78314178ff0Sclaudio 		break;
78414178ff0Sclaudio 	case ATTR_EXT_COMMUNITIES:
785dd147aaaSclaudio 		json_do_ext_community(buf);
78614178ff0Sclaudio 		break;
78714178ff0Sclaudio 	case ATTR_LARGE_COMMUNITIES:
788dd147aaaSclaudio 		json_do_large_community(buf);
78914178ff0Sclaudio 		break;
790135bf897Sclaudio 	case ATTR_OTC:
791dd147aaaSclaudio 		if (alen != 4 || ibuf_get_n32(buf, &as) == -1)
792dd147aaaSclaudio 			goto bad_len;
793135bf897Sclaudio 		json_do_uint("as", as);
794135bf897Sclaudio 		break;
79514178ff0Sclaudio 	case ATTR_ATOMIC_AGGREGATE:
79614178ff0Sclaudio 	default:
79714178ff0Sclaudio 		if (alen)
798dd147aaaSclaudio 			json_do_hexdump("data", ibuf_data(buf), ibuf_size(buf));
79914178ff0Sclaudio 		break;
80014178ff0Sclaudio 	}
801dd147aaaSclaudio 	return;
802dd147aaaSclaudio 
803dd147aaaSclaudio  bad_len:
804dd147aaaSclaudio 	json_do_string("error", "bad length");
80514178ff0Sclaudio }
80614178ff0Sclaudio 
80714178ff0Sclaudio static void
80846d5331aSclaudio json_rib(struct ctl_show_rib *r, struct ibuf *asbuf, struct parse_result *res)
80914178ff0Sclaudio {
81014178ff0Sclaudio 	struct in_addr id;
81114178ff0Sclaudio 	char *aspath;
81214178ff0Sclaudio 
81314178ff0Sclaudio 	json_do_array("rib");
81414178ff0Sclaudio 
81556bc7cf3Sclaudio 	json_do_object("rib_entry", 0);
81614178ff0Sclaudio 
81714178ff0Sclaudio 	json_do_printf("prefix", "%s/%u", log_addr(&r->prefix), r->prefixlen);
81814178ff0Sclaudio 
81946d5331aSclaudio 	if (aspath_asprint(&aspath, asbuf) == -1)
82014178ff0Sclaudio 		err(1, NULL);
821a32a1b0fSclaudio 	json_do_string("aspath", aspath);
82214178ff0Sclaudio 	free(aspath);
82314178ff0Sclaudio 
824a32a1b0fSclaudio 	json_do_string("exit_nexthop", log_addr(&r->exit_nexthop));
825a32a1b0fSclaudio 	json_do_string("true_nexthop", log_addr(&r->true_nexthop));
82614178ff0Sclaudio 
82756bc7cf3Sclaudio 	json_do_object("neighbor", 1);
82814178ff0Sclaudio 	if (r->descr[0])
829a32a1b0fSclaudio 		json_do_string("description", r->descr);
830a32a1b0fSclaudio 	json_do_string("remote_addr", log_addr(&r->remote_addr));
83114178ff0Sclaudio 	id.s_addr = htonl(r->remote_id);
832a32a1b0fSclaudio 	json_do_string("bgp_id", inet_ntoa(id));
83314178ff0Sclaudio 	json_do_end();
83414178ff0Sclaudio 
8359e59dee7Sclaudio 	if (r->flags & F_PREF_PATH_ID)
8369e59dee7Sclaudio 		json_do_uint("path_id", r->path_id);
8379e59dee7Sclaudio 
83814178ff0Sclaudio 	/* flags */
83914178ff0Sclaudio 	json_do_bool("valid", r->flags & F_PREF_ELIGIBLE);
84009b2f24cSclaudio 	if (r->flags & F_PREF_FILTERED)
84109b2f24cSclaudio 		json_do_bool("filtered", 1);
8424ff57d1dSclaudio 	if (r->flags & F_PREF_BEST)
84314178ff0Sclaudio 		json_do_bool("best", 1);
8442953a2c1Sclaudio 	if (r->flags & F_PREF_ECMP)
8452953a2c1Sclaudio 		json_do_bool("ecmp", 1);
8462953a2c1Sclaudio 	if (r->flags & F_PREF_AS_WIDE)
8472953a2c1Sclaudio 		json_do_bool("as-wide", 1);
84814178ff0Sclaudio 	if (r->flags & F_PREF_INTERNAL)
849a32a1b0fSclaudio 		json_do_string("source", "internal");
85014178ff0Sclaudio 	else
851a32a1b0fSclaudio 		json_do_string("source", "external");
85214178ff0Sclaudio 	if (r->flags & F_PREF_STALE)
85314178ff0Sclaudio 		json_do_bool("stale", 1);
85414178ff0Sclaudio 	if (r->flags & F_PREF_ANNOUNCE)
85514178ff0Sclaudio 		json_do_bool("announced", 1);
85614178ff0Sclaudio 
85714178ff0Sclaudio 	/* various attribibutes */
858a32a1b0fSclaudio 	json_do_string("ovs", fmt_ovs(r->roa_validation_state, 0));
859a32a1b0fSclaudio 	json_do_string("avs", fmt_avs(r->aspa_validation_state, 0));
860a32a1b0fSclaudio 	json_do_string("origin", fmt_origin(r->origin, 0));
86114178ff0Sclaudio 	json_do_uint("metric", r->med);
86214178ff0Sclaudio 	json_do_uint("localpref", r->local_pref);
86314178ff0Sclaudio 	json_do_uint("weight", r->weight);
8642953a2c1Sclaudio 	json_do_int("dmetric", r->dmetric);
865a32a1b0fSclaudio 	json_do_string("last_update", fmt_timeframe(r->age));
866fd18fec4Sclaudio 	json_do_int("last_update_sec", r->age);
86714178ff0Sclaudio 
8683a50f0a9Sjmc 	/* keep the object open for communities and attributes */
86914178ff0Sclaudio }
87014178ff0Sclaudio 
87114178ff0Sclaudio static void
87214178ff0Sclaudio json_rib_mem_element(const char *name, uint64_t count, uint64_t size,
87314178ff0Sclaudio     uint64_t refs)
87414178ff0Sclaudio {
87556bc7cf3Sclaudio 	json_do_object(name, 1);
87614178ff0Sclaudio 	if (count != UINT64_MAX)
87714178ff0Sclaudio 		json_do_uint("count", count);
87814178ff0Sclaudio 	if (size != UINT64_MAX)
87914178ff0Sclaudio 		json_do_uint("size", size);
88014178ff0Sclaudio 	if (refs != UINT64_MAX)
88114178ff0Sclaudio 		json_do_uint("references", refs);
88214178ff0Sclaudio 	json_do_end();
88314178ff0Sclaudio }
88414178ff0Sclaudio 
88514178ff0Sclaudio static void
88614178ff0Sclaudio json_rib_mem(struct rde_memstats *stats)
88714178ff0Sclaudio {
88814178ff0Sclaudio 	size_t pts = 0;
88914178ff0Sclaudio 	int i;
89014178ff0Sclaudio 
89156bc7cf3Sclaudio 	json_do_object("memory", 0);
89214178ff0Sclaudio 	for (i = 0; i < AID_MAX; i++) {
89314178ff0Sclaudio 		if (stats->pt_cnt[i] == 0)
89414178ff0Sclaudio 			continue;
895093d1e70Sclaudio 		pts += stats->pt_size[i];
89614178ff0Sclaudio 		json_rib_mem_element(aid_vals[i].name, stats->pt_cnt[i],
897093d1e70Sclaudio 		    stats->pt_size[i], UINT64_MAX);
89814178ff0Sclaudio 	}
89914178ff0Sclaudio 	json_rib_mem_element("rib", stats->rib_cnt,
90014178ff0Sclaudio 	    stats->rib_cnt * sizeof(struct rib_entry), UINT64_MAX);
90114178ff0Sclaudio 	json_rib_mem_element("prefix", stats->prefix_cnt,
90214178ff0Sclaudio 	    stats->prefix_cnt * sizeof(struct prefix), UINT64_MAX);
90314178ff0Sclaudio 	json_rib_mem_element("rde_aspath", stats->path_cnt,
90414178ff0Sclaudio 	    stats->path_cnt * sizeof(struct rde_aspath),
90514178ff0Sclaudio 	    stats->path_refs);
90614178ff0Sclaudio 	json_rib_mem_element("aspath", stats->aspath_cnt,
907a03ec1fdSclaudio 	    stats->aspath_size, UINT64_MAX);
90814178ff0Sclaudio 	json_rib_mem_element("community_entries", stats->comm_cnt,
90914178ff0Sclaudio 	    stats->comm_cnt * sizeof(struct rde_community), UINT64_MAX);
91014178ff0Sclaudio 	json_rib_mem_element("community", stats->comm_nmemb,
91114178ff0Sclaudio 	    stats->comm_size * sizeof(struct community), stats->comm_refs);
91214178ff0Sclaudio 	json_rib_mem_element("attributes_entries", stats->attr_cnt,
91314178ff0Sclaudio 	    stats->attr_cnt * sizeof(struct attr), stats->attr_refs);
91414178ff0Sclaudio 	json_rib_mem_element("attributes", stats->attr_dcnt,
91514178ff0Sclaudio 	    stats->attr_data, UINT64_MAX);
91614178ff0Sclaudio 	json_rib_mem_element("total", UINT64_MAX,
91714178ff0Sclaudio 	    pts + stats->prefix_cnt * sizeof(struct prefix) +
91814178ff0Sclaudio 	    stats->rib_cnt * sizeof(struct rib_entry) +
91914178ff0Sclaudio 	    stats->path_cnt * sizeof(struct rde_aspath) +
92014178ff0Sclaudio 	    stats->aspath_size + stats->attr_cnt * sizeof(struct attr) +
92114178ff0Sclaudio 	    stats->attr_data, UINT64_MAX);
92214178ff0Sclaudio 	json_do_end();
92314178ff0Sclaudio 
92456bc7cf3Sclaudio 	json_do_object("sets", 0);
92514178ff0Sclaudio 	json_rib_mem_element("as_set", stats->aset_nmemb,
92614178ff0Sclaudio 	    stats->aset_size, UINT64_MAX);
92714178ff0Sclaudio 	json_rib_mem_element("as_set_tables", stats->aset_cnt, UINT64_MAX,
92814178ff0Sclaudio 	    UINT64_MAX);
92914178ff0Sclaudio 	json_rib_mem_element("prefix_set", stats->pset_cnt, stats->pset_size,
93014178ff0Sclaudio 	    UINT64_MAX);
93114178ff0Sclaudio 	json_rib_mem_element("total", UINT64_MAX,
93214178ff0Sclaudio 	    stats->aset_size + stats->pset_size, UINT64_MAX);
93314178ff0Sclaudio 	json_do_end();
93414178ff0Sclaudio }
93514178ff0Sclaudio 
93614178ff0Sclaudio static void
937413f97b7Sclaudio json_rib_set(struct ctl_show_set *set)
938413f97b7Sclaudio {
939413f97b7Sclaudio 	json_do_array("sets");
940413f97b7Sclaudio 
94156bc7cf3Sclaudio 	json_do_object("set", 0);
942a32a1b0fSclaudio 	json_do_string("name", set->name);
943a32a1b0fSclaudio 	json_do_string("type", fmt_set_type(set));
944a32a1b0fSclaudio 	json_do_string("last_change", fmt_monotime(set->lastchange));
945fd18fec4Sclaudio 	json_do_int("last_change_sec", get_monotime(set->lastchange));
946c4f772fdSclaudio 	if (set->type == ASNUM_SET || set->type == ASPA_SET) {
947413f97b7Sclaudio 		json_do_uint("num_ASnum", set->as_cnt);
948413f97b7Sclaudio 	} else {
949413f97b7Sclaudio 		json_do_uint("num_IPv4", set->v4_cnt);
950413f97b7Sclaudio 		json_do_uint("num_IPv6", set->v6_cnt);
951413f97b7Sclaudio 	}
952413f97b7Sclaudio 	json_do_end();
953413f97b7Sclaudio }
954413f97b7Sclaudio 
955413f97b7Sclaudio static void
95669d2b5abSclaudio json_rtr(struct ctl_show_rtr *rtr)
95769d2b5abSclaudio {
95869d2b5abSclaudio 	json_do_array("rtrs");
95969d2b5abSclaudio 
96056bc7cf3Sclaudio 	json_do_object("rtr", 0);
96169d2b5abSclaudio 	if (rtr->descr[0])
962a32a1b0fSclaudio 		json_do_string("descr", rtr->descr);
963a32a1b0fSclaudio 	json_do_string("remote_addr", log_addr(&rtr->remote_addr));
96469d2b5abSclaudio 	json_do_uint("remote_port", rtr->remote_port);
96569d2b5abSclaudio 	if (rtr->local_addr.aid != AID_UNSPEC)
966a32a1b0fSclaudio 		json_do_string("local_addr", log_addr(&rtr->local_addr));
9678167eafeSclaudio 	json_do_string("state", rtr->state);
96869d2b5abSclaudio 
96969d2b5abSclaudio 	if (rtr->session_id != -1) {
970bf4f3ceaSclaudio 		json_do_uint("version", rtr->version);
971282ccd24Sclaudio 		json_do_uint("minimal_version", rtr->min_version);
97269d2b5abSclaudio 		json_do_uint("session_id", rtr->session_id);
97369d2b5abSclaudio 		json_do_uint("serial", rtr->serial);
97469d2b5abSclaudio 	}
97569d2b5abSclaudio 	json_do_uint("refresh", rtr->refresh);
97669d2b5abSclaudio 	json_do_uint("retry", rtr->retry);
97769d2b5abSclaudio 	json_do_uint("expire", rtr->expire);
97869d2b5abSclaudio 
97969d2b5abSclaudio 	if (rtr->last_sent_error != NO_ERROR) {
980a32a1b0fSclaudio 		json_do_string("last_sent_error",
98169d2b5abSclaudio 		    log_rtr_error(rtr->last_sent_error));
98269d2b5abSclaudio 		if (rtr->last_sent_msg[0])
983a32a1b0fSclaudio 			json_do_string("last_sent_msg",
98469d2b5abSclaudio 			    log_reason(rtr->last_sent_msg));
98569d2b5abSclaudio 	}
98669d2b5abSclaudio 	if (rtr->last_recv_error != NO_ERROR) {
987a32a1b0fSclaudio 		json_do_string("last_recv_error",
98869d2b5abSclaudio 		    log_rtr_error(rtr->last_recv_error));
98969d2b5abSclaudio 		if (rtr->last_recv_msg[0])
990a32a1b0fSclaudio 			json_do_string("last_recv_msg",
99169d2b5abSclaudio 			    log_reason(rtr->last_recv_msg));
99269d2b5abSclaudio 	}
99369d2b5abSclaudio }
99469d2b5abSclaudio 
99569d2b5abSclaudio static void
99614178ff0Sclaudio json_result(u_int rescode)
99714178ff0Sclaudio {
99814178ff0Sclaudio 	if (rescode == 0)
999a32a1b0fSclaudio 		json_do_string("status", "OK");
1000c2102d68Sjsg 	else if (rescode >=
100114178ff0Sclaudio 	    sizeof(ctl_res_strerror)/sizeof(ctl_res_strerror[0])) {
1002a32a1b0fSclaudio 		json_do_string("status", "FAILED");
100314178ff0Sclaudio 		json_do_printf("error", "unknown error %d", rescode);
100414178ff0Sclaudio 	} else {
1005a32a1b0fSclaudio 		json_do_string("status", "FAILED");
1006a32a1b0fSclaudio 		json_do_string("error", ctl_res_strerror[rescode]);
100714178ff0Sclaudio 	}
100814178ff0Sclaudio }
100914178ff0Sclaudio 
101014178ff0Sclaudio static void
101114178ff0Sclaudio json_tail(void)
101214178ff0Sclaudio {
101314178ff0Sclaudio 	json_do_finish();
101414178ff0Sclaudio }
101514178ff0Sclaudio 
101614178ff0Sclaudio const struct output json_output = {
101714178ff0Sclaudio 	.head = json_head,
101814178ff0Sclaudio 	.neighbor = json_neighbor,
101914178ff0Sclaudio 	.timer = json_timer,
102014178ff0Sclaudio 	.fib = json_fib,
102114178ff0Sclaudio 	.fib_table = json_fib_table,
102214178ff0Sclaudio 	.nexthop = json_nexthop,
102314178ff0Sclaudio 	.interface = json_interface,
102414178ff0Sclaudio 	.communities = json_communities,
102514178ff0Sclaudio 	.attr = json_attr,
102614178ff0Sclaudio 	.rib = json_rib,
102714178ff0Sclaudio 	.rib_mem = json_rib_mem,
1028413f97b7Sclaudio 	.set = json_rib_set,
102969d2b5abSclaudio 	.rtr = json_rtr,
103014178ff0Sclaudio 	.result = json_result,
103177a10e6fSclaudio 	.tail = json_tail,
103214178ff0Sclaudio };
1033