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