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