diff options
author | Russ White <russ@riw.us> | 2022-03-08 17:15:25 +0100 |
---|---|---|
committer | GitHub <noreply@github.com> | 2022-03-08 17:15:25 +0100 |
commit | 82934a6a326977b40dd2d31772e6e4c7f2953d0b (patch) | |
tree | 7b258b5eab8bd02690ddbe22b35bdb468a489784 | |
parent | Merge pull request #10722 from chiragshah6/evpn_dev3 (diff) | |
parent | topotest: Add test for isis json cmds. (diff) | |
download | frr-82934a6a326977b40dd2d31772e6e4c7f2953d0b.tar.xz frr-82934a6a326977b40dd2d31772e6e4c7f2953d0b.zip |
Merge pull request #10701 from rampxxxx/feat_isis_json_show_cmds
Feat isis json show cmds
-rw-r--r-- | doc/user/isisd.rst | 8 | ||||
-rw-r--r-- | isisd/isis_adjacency.c | 214 | ||||
-rw-r--r-- | isisd/isis_adjacency.h | 2 | ||||
-rw-r--r-- | isisd/isis_circuit.c | 145 | ||||
-rw-r--r-- | isisd/isis_circuit.h | 2 | ||||
-rw-r--r-- | isisd/isis_lsp.c | 76 | ||||
-rw-r--r-- | isisd/isis_lsp.h | 17 | ||||
-rw-r--r-- | isisd/isis_pdu.c | 4 | ||||
-rw-r--r-- | isisd/isis_spf.c | 12 | ||||
-rw-r--r-- | isisd/isis_spf.h | 2 | ||||
-rw-r--r-- | isisd/isis_tlvs.c | 1288 | ||||
-rw-r--r-- | isisd/isis_tlvs.h | 2 | ||||
-rw-r--r-- | isisd/isisd.c | 633 | ||||
-rw-r--r-- | isisd/isisd.h | 12 | ||||
-rw-r--r-- | tests/isisd/test_fuzz_isis_tlv.c | 8 | ||||
-rw-r--r-- | tests/isisd/test_isis_spf.c | 2 | ||||
-rw-r--r-- | tests/topotests/isis_topo1/test_isis_topo1.py | 88 |
17 files changed, 2094 insertions, 421 deletions
diff --git a/doc/user/isisd.rst b/doc/user/isisd.rst index f7d42d820..d2859670d 100644 --- a/doc/user/isisd.rst +++ b/doc/user/isisd.rst @@ -272,7 +272,7 @@ ISIS interface Showing ISIS information ======================== -.. clicmd:: show isis summary +.. clicmd:: show isis [vrf <NAME|all>] summary [json] Show summary information about ISIS. @@ -280,17 +280,17 @@ Showing ISIS information Show information about ISIS node. -.. clicmd:: show isis interface [detail] [IFNAME] +.. clicmd:: show isis [vrf <NAME|all>] interface [detail] [IFNAME] [json] Show state and configuration of ISIS specified interface, or all interfaces if no interface is given with or without details. -.. clicmd:: show isis neighbor [detail] [SYSTEMID] +.. clicmd:: show isis [vrf <NAME|all>] neighbor [detail] [SYSTEMID] [json] Show state and information of ISIS specified neighbor, or all neighbors if no system id is given with or without details. -.. clicmd:: show isis database [detail] [LSPID] +.. clicmd:: show isis [vrf <NAME|all>] database [detail] [LSPID] [json] Show the ISIS database globally, for a specific LSP id without or with details. diff --git a/isisd/isis_adjacency.c b/isisd/isis_adjacency.c index 06909c430..2729dce38 100644 --- a/isisd/isis_adjacency.c +++ b/isisd/isis_adjacency.c @@ -464,6 +464,220 @@ void isis_adj_expire(struct thread *thread) } /* + * show isis neighbor [detail] json + */ +void isis_adj_print_json(struct isis_adjacency *adj, struct json_object *json, + char detail) +{ + json_object *iface_json, *ipv4_addr_json, *ipv6_link_json, + *ipv6_non_link_json, *topo_json, *dis_flaps_json, + *area_addr_json, *adj_sid_json; + time_t now; + struct isis_dynhn *dyn; + int level; + char buf[256]; + + json_object_string_add(json, "adj", isis_adj_name(adj)); + + if (detail == ISIS_UI_LEVEL_BRIEF) { + if (adj->circuit) + json_object_string_add(json, "interface", + adj->circuit->interface->name); + else + json_object_string_add(json, "interface", + "NULL circuit!"); + json_object_int_add(json, "level", adj->level); + json_object_string_add(json, "state", + adj_state2string(adj->adj_state)); + now = time(NULL); + if (adj->last_upd) { + if (adj->last_upd + adj->hold_time < now) + json_object_string_add(json, "last-upd", + "expiring"); + else + json_object_string_add( + json, "expires-in", + time2string(adj->last_upd + + adj->hold_time - now)); + } + json_object_string_add(json, "snpa", snpa_print(adj->snpa)); + } + + if (detail == ISIS_UI_LEVEL_DETAIL) { + struct sr_adjacency *sra; + struct listnode *anode; + + level = adj->level; + iface_json = json_object_new_object(); + json_object_object_add(json, "interface", iface_json); + if (adj->circuit) + json_object_string_add(iface_json, "name", + adj->circuit->interface->name); + else + json_object_string_add(iface_json, "name", + "null-circuit"); + json_object_int_add(json, "level", adj->level); + json_object_string_add(iface_json, "state", + adj_state2string(adj->adj_state)); + now = time(NULL); + if (adj->last_upd) { + if (adj->last_upd + adj->hold_time < now) + json_object_string_add(iface_json, "last-upd", + "expiring"); + else + json_object_string_add( + json, "expires-in", + time2string(adj->last_upd + + adj->hold_time - now)); + } else + json_object_string_add(json, "expires-in", + time2string(adj->hold_time)); + json_object_int_add(iface_json, "adj-flaps", adj->flaps); + json_object_string_add(iface_json, "last-ago", + time2string(now - adj->last_flap)); + json_object_string_add(iface_json, "circuit-type", + circuit_t2string(adj->circuit_t)); + json_object_string_add(iface_json, "speaks", + nlpid2string(&adj->nlpids)); + if (adj->mt_count != 1 || + adj->mt_set[0] != ISIS_MT_IPV4_UNICAST) { + topo_json = json_object_new_object(); + json_object_object_add(iface_json, "topologies", + topo_json); + for (unsigned int i = 0; i < adj->mt_count; i++) { + snprintfrr(buf, sizeof(buf), "topo-%d", i); + json_object_string_add( + topo_json, buf, + isis_mtid2str(adj->mt_set[i])); + } + } + json_object_string_add(iface_json, "snpa", + snpa_print(adj->snpa)); + if (adj->circuit && + (adj->circuit->circ_type == CIRCUIT_T_BROADCAST)) { + dyn = dynhn_find_by_id(adj->circuit->isis, adj->lanid); + if (dyn) { + snprintfrr(buf, sizeof(buf), "%s-%02x", + dyn->hostname, + adj->lanid[ISIS_SYS_ID_LEN]); + json_object_string_add(iface_json, "lan-id", + buf); + } else { + snprintfrr(buf, sizeof(buf), "%s-%02x", + sysid_print(adj->lanid), + adj->lanid[ISIS_SYS_ID_LEN]); + json_object_string_add(iface_json, "lan-id", + buf); + } + + json_object_int_add(iface_json, "lan-prio", + adj->prio[adj->level - 1]); + + dis_flaps_json = json_object_new_object(); + json_object_object_add(iface_json, "dis-flaps", + dis_flaps_json); + json_object_string_add( + dis_flaps_json, "dis-record", + isis_disflag2string( + adj->dis_record[ISIS_LEVELS + level - 1] + .dis)); + json_object_int_add(dis_flaps_json, "last", + adj->dischanges[level - 1]); + json_object_string_add( + dis_flaps_json, "ago", + time2string(now - (adj->dis_record[ISIS_LEVELS + + level - 1] + .last_dis_change))); + } + + if (adj->area_address_count) { + area_addr_json = json_object_new_object(); + json_object_object_add(iface_json, "area-address", + area_addr_json); + for (unsigned int i = 0; i < adj->area_address_count; + i++) { + json_object_string_add( + area_addr_json, "isonet", + isonet_print(adj->area_addresses[i] + .area_addr, + adj->area_addresses[i] + .addr_len)); + } + } + if (adj->ipv4_address_count) { + ipv4_addr_json = json_object_new_object(); + json_object_object_add(iface_json, "ipv4-address", + ipv4_addr_json); + for (unsigned int i = 0; i < adj->ipv4_address_count; + i++){ + inet_ntop(AF_INET, &adj->ipv4_addresses[i], buf, + sizeof(buf)); + json_object_string_add(ipv4_addr_json, "ipv4", buf); + } + } + if (adj->ll_ipv6_count) { + ipv6_link_json = json_object_new_object(); + json_object_object_add(iface_json, "ipv6-link-local", + ipv6_link_json); + for (unsigned int i = 0; i < adj->ll_ipv6_count; i++) { + char buf[INET6_ADDRSTRLEN]; + inet_ntop(AF_INET6, &adj->ll_ipv6_addrs[i], buf, + sizeof(buf)); + json_object_string_add(ipv6_link_json, "ipv6", + buf); + } + } + if (adj->global_ipv6_count) { + ipv6_non_link_json = json_object_new_object(); + json_object_object_add(iface_json, "ipv6-global", + ipv6_non_link_json); + for (unsigned int i = 0; i < adj->global_ipv6_count; + i++) { + char buf[INET6_ADDRSTRLEN]; + inet_ntop(AF_INET6, &adj->global_ipv6_addrs[i], + buf, sizeof(buf)); + json_object_string_add(ipv6_non_link_json, + "ipv6", buf); + } + } + + adj_sid_json = json_object_new_object(); + json_object_object_add(iface_json, "adj-sid", adj_sid_json); + for (ALL_LIST_ELEMENTS_RO(adj->adj_sids, anode, sra)) { + const char *adj_type; + const char *backup; + uint32_t sid; + + switch (sra->adj->circuit->circ_type) { + case CIRCUIT_T_BROADCAST: + adj_type = "LAN Adjacency-SID"; + sid = sra->u.ladj_sid->sid; + break; + case CIRCUIT_T_P2P: + adj_type = "Adjacency-SID"; + sid = sra->u.adj_sid->sid; + break; + default: + continue; + } + backup = (sra->type == ISIS_SR_LAN_BACKUP) ? " (backup)" + : ""; + + json_object_string_add(adj_sid_json, "nexthop", + (sra->nexthop.family == AF_INET) + ? "IPv4" + : "IPv6"); + json_object_string_add(adj_sid_json, "adj-type", + adj_type); + json_object_string_add(adj_sid_json, "is-backup", + backup); + json_object_int_add(adj_sid_json, "sid", sid); + } + } + return; +} + +/* * show isis neighbor [detail] */ void isis_adj_print_vty(struct isis_adjacency *adj, struct vty *vty, diff --git a/isisd/isis_adjacency.h b/isisd/isis_adjacency.h index 4d84c5ca4..7467a619c 100644 --- a/isisd/isis_adjacency.h +++ b/isisd/isis_adjacency.h @@ -144,6 +144,8 @@ const char *isis_adj_yang_state(enum isis_adj_state state); void isis_adj_expire(struct thread *thread); void isis_adj_print_vty(struct isis_adjacency *adj, struct vty *vty, char detail); +void isis_adj_print_json(struct isis_adjacency *adj, struct json_object *json, + char detail); void isis_adj_build_neigh_list(struct list *adjdb, struct list *list); void isis_adj_build_up_list(struct list *adjdb, struct list *list); int isis_adj_usage2levels(enum isis_adj_usage usage); diff --git a/isisd/isis_circuit.c b/isisd/isis_circuit.c index 1b0447226..da75f196b 100644 --- a/isisd/isis_circuit.c +++ b/isisd/isis_circuit.c @@ -933,6 +933,151 @@ void circuit_update_nlpids(struct isis_circuit *circuit) return; } +void isis_circuit_print_json(struct isis_circuit *circuit, + struct json_object *json, char detail) +{ + int level; + json_object *iface_json, *ipv4_addr_json, *ipv6_link_json, + *ipv6_non_link_json, *hold_json, *lan_prio_json, *levels_json, + *level_json; + char buf_prx[INET6_BUFSIZ]; + char buf[255]; + + snprintfrr(buf, sizeof(buf), "0x%x", circuit->circuit_id); + if (detail == ISIS_UI_LEVEL_BRIEF) { + iface_json = json_object_new_object(); + json_object_object_add(json, "interface", iface_json); + json_object_string_add(iface_json, "name", + circuit->interface->name); + json_object_string_add(iface_json, "circuit-id", buf); + json_object_string_add(iface_json, "state", + circuit_state2string(circuit->state)); + json_object_string_add(iface_json, "type", + circuit_type2string(circuit->circ_type)); + json_object_string_add(iface_json, "level", + circuit_t2string(circuit->is_type)); + } + + if (detail == ISIS_UI_LEVEL_DETAIL) { + struct listnode *node; + struct prefix *ip_addr; + + iface_json = json_object_new_object(); + json_object_object_add(json, "interface", iface_json); + json_object_string_add(iface_json, "name", + circuit->interface->name); + json_object_string_add(iface_json, "state", + circuit_state2string(circuit->state)); + if (circuit->is_passive) + json_object_string_add(iface_json, "is-passive", + "passive"); + else + json_object_string_add(iface_json, "is-passive", + "active"); + json_object_string_add(iface_json, "circuit-id", buf); + json_object_string_add(iface_json, "type", + circuit_type2string(circuit->circ_type)); + json_object_string_add(iface_json, "level", + circuit_t2string(circuit->is_type)); + if (circuit->circ_type == CIRCUIT_T_BROADCAST) + json_object_string_add(iface_json, "snpa", + snpa_print(circuit->u.bc.snpa)); + + + levels_json = json_object_new_array(); + json_object_object_add(iface_json, "levels", levels_json); + for (level = ISIS_LEVEL1; level <= ISIS_LEVELS; level++) { + if ((circuit->is_type & level) == 0) + continue; + level_json = json_object_new_object(); + json_object_string_add(level_json, "level", + circuit_t2string(level)); + if (circuit->area->newmetric) + json_object_int_add(level_json, "metric", + circuit->te_metric[0]); + else + json_object_int_add(level_json, "metric", + circuit->metric[0]); + if (!circuit->is_passive) { + json_object_int_add(level_json, + "active-neighbors", + circuit->upadjcount[0]); + json_object_int_add(level_json, + "hello-interval", + circuit->hello_interval[0]); + hold_json = json_object_new_object(); + json_object_object_add(level_json, "holddown", + hold_json); + json_object_int_add( + hold_json, "count", + circuit->hello_multiplier[0]); + json_object_string_add( + hold_json, "pad", + (circuit->pad_hellos ? "yes" : "no")); + json_object_int_add(level_json, "cnsp-interval", + circuit->csnp_interval[0]); + json_object_int_add(level_json, "psnp-interval", + circuit->psnp_interval[0]); + if (circuit->circ_type == CIRCUIT_T_BROADCAST) { + lan_prio_json = + json_object_new_object(); + json_object_object_add(level_json, + "lan", + lan_prio_json); + json_object_int_add( + lan_prio_json, "priority", + circuit->priority[0]); + json_object_string_add( + lan_prio_json, "is-dis", + (circuit->u.bc.is_dr[0] + ? "yes" + : "no")); + } + } + json_object_array_add(levels_json, level_json); + } + + if (circuit->ip_addrs && listcount(circuit->ip_addrs) > 0) { + ipv4_addr_json = json_object_new_object(); + json_object_object_add(iface_json, "ip-prefix", + ipv4_addr_json); + for (ALL_LIST_ELEMENTS_RO(circuit->ip_addrs, node, + ip_addr)) { + snprintfrr(buf_prx, INET6_BUFSIZ, "%pFX", + ip_addr); + json_object_string_add(ipv4_addr_json, "ip", + buf_prx); + } + } + if (circuit->ipv6_link && listcount(circuit->ipv6_link) > 0) { + ipv6_link_json = json_object_new_object(); + json_object_object_add(iface_json, "ipv6-link-locals", + ipv6_link_json); + for (ALL_LIST_ELEMENTS_RO(circuit->ipv6_link, node, + ip_addr)) { + snprintfrr(buf_prx, INET6_BUFSIZ, "%pFX", + ip_addr); + json_object_string_add(ipv6_link_json, "ipv6", + buf_prx); + } + } + if (circuit->ipv6_non_link && + listcount(circuit->ipv6_non_link) > 0) { + ipv6_non_link_json = json_object_new_object(); + json_object_object_add(iface_json, "ipv6-prefixes", + ipv6_non_link_json); + for (ALL_LIST_ELEMENTS_RO(circuit->ipv6_non_link, node, + ip_addr)) { + snprintfrr(buf_prx, INET6_BUFSIZ, "%pFX", + ip_addr); + json_object_string_add(ipv6_non_link_json, + "ipv6", buf_prx); + } + } + } + return; +} + void isis_circuit_print_vty(struct isis_circuit *circuit, struct vty *vty, char detail) { diff --git a/isisd/isis_circuit.h b/isisd/isis_circuit.h index 746578084..5ff0390c2 100644 --- a/isisd/isis_circuit.h +++ b/isisd/isis_circuit.h @@ -206,6 +206,8 @@ void isis_circuit_down(struct isis_circuit *); void circuit_update_nlpids(struct isis_circuit *circuit); void isis_circuit_print_vty(struct isis_circuit *circuit, struct vty *vty, char detail); +void isis_circuit_print_json(struct isis_circuit *circuit, + struct json_object *json, char detail); size_t isis_circuit_pdu_size(struct isis_circuit *circuit); void isis_circuit_stream(struct isis_circuit *circuit, struct stream **stream); diff --git a/isisd/isis_lsp.c b/isisd/isis_lsp.c index 463d26f6c..eb7e9e725 100644 --- a/isisd/isis_lsp.c +++ b/isisd/isis_lsp.c @@ -733,8 +733,48 @@ static const char *lsp_bits2string(uint8_t lsp_bits, char *buf, size_t buf_size) } /* this function prints the lsp on show isis database */ -void lsp_print(struct isis_lsp *lsp, struct vty *vty, char dynhost, - struct isis *isis) +void lsp_print_common(struct isis_lsp *lsp, struct vty *vty, struct json_object *json, + char dynhost, struct isis *isis) +{ + if (json) { + return lsp_print_json(lsp, json, dynhost, isis); + } else { + return lsp_print_vty(lsp, vty, dynhost, isis); + } +} + +void lsp_print_json(struct isis_lsp *lsp, struct json_object *json, + char dynhost, struct isis *isis) +{ + char LSPid[255]; + char age_out[8]; + char b[200]; + json_object *own_json; + char buf[256]; + + lspid_print(lsp->hdr.lsp_id, LSPid, sizeof(LSPid), dynhost, 1, isis); + own_json = json_object_new_object(); + json_object_object_add(json, "lsp", own_json); + json_object_string_add(own_json, "id", LSPid); + json_object_string_add(own_json, "own", lsp->own_lsp ? "*" : " "); + json_object_int_add(json, "pdu-len", lsp->hdr.pdu_len); + snprintfrr(buf, sizeof(buf), "0x%08x", lsp->hdr.seqno); + json_object_string_add(json, "seq-number", buf); + snprintfrr(buf, sizeof(buf), "0x%04hx", lsp->hdr.checksum); + json_object_string_add(json, "chksum", buf); + if (lsp->hdr.rem_lifetime == 0) { + snprintf(age_out, sizeof(age_out), "(%d)", lsp->age_out); + age_out[7] = '\0'; + json_object_string_add(json, "holdtime", age_out); + } else { + json_object_int_add(json, "holdtime", lsp->hdr.rem_lifetime); + } + json_object_string_add( + json, "att-p-ol", lsp_bits2string(lsp->hdr.lsp_bits, b, sizeof(b))); +} + +void lsp_print_vty(struct isis_lsp *lsp, struct vty *vty, + char dynhost, struct isis *isis) { char LSPid[255]; char age_out[8]; @@ -754,30 +794,40 @@ void lsp_print(struct isis_lsp *lsp, struct vty *vty, char dynhost, vty_out(vty, "%s\n", lsp_bits2string(lsp->hdr.lsp_bits, b, sizeof(b))); } -void lsp_print_detail(struct isis_lsp *lsp, struct vty *vty, char dynhost, - struct isis *isis) +void lsp_print_detail(struct isis_lsp *lsp, struct vty *vty, + struct json_object *json, char dynhost, + struct isis *isis) { - lsp_print(lsp, vty, dynhost, isis); - if (lsp->tlvs) - vty_multiline(vty, " ", "%s", isis_format_tlvs(lsp->tlvs)); - vty_out(vty, "\n"); + if (json) { + lsp_print_json(lsp, json, dynhost, isis); + if (lsp->tlvs) { + isis_format_tlvs(lsp->tlvs, json); + } + } else { + lsp_print_vty(lsp, vty, dynhost, isis); + if (lsp->tlvs) + vty_multiline(vty, " ", "%s", + isis_format_tlvs(lsp->tlvs, NULL)); + vty_out(vty, "\n"); + } } /* print all the lsps info in the local lspdb */ -int lsp_print_all(struct vty *vty, struct lspdb_head *head, char detail, - char dynhost, struct isis *isis) +int lsp_print_all(struct vty *vty, struct json_object *json, + struct lspdb_head *head, char detail, char dynhost, + struct isis *isis) { struct isis_lsp *lsp; int lsp_count = 0; if (detail == ISIS_UI_LEVEL_BRIEF) { frr_each (lspdb, head, lsp) { - lsp_print(lsp, vty, dynhost, isis); + lsp_print_common(lsp, vty, json, dynhost, isis); lsp_count++; } } else if (detail == ISIS_UI_LEVEL_DETAIL) { frr_each (lspdb, head, lsp) { - lsp_print_detail(lsp, vty, dynhost, isis); + lsp_print_detail(lsp, vty, json, dynhost, isis); lsp_count++; } } @@ -1264,7 +1314,7 @@ static void lsp_build(struct isis_lsp *lsp, struct isis_area *area) if (!fragments) { zlog_warn("BUG: could not fragment own LSP:"); log_multiline(LOG_WARNING, " ", "%s", - isis_format_tlvs(tlvs)); + isis_format_tlvs(tlvs, NULL)); isis_free_tlvs(tlvs); return; } diff --git a/isisd/isis_lsp.h b/isisd/isis_lsp.h index f42d702b3..b13b2a35e 100644 --- a/isisd/isis_lsp.h +++ b/isisd/isis_lsp.h @@ -120,12 +120,19 @@ void lsp_update(struct isis_lsp *lsp, struct isis_lsp_hdr *hdr, void lsp_inc_seqno(struct isis_lsp *lsp, uint32_t seqno); void lspid_print(uint8_t *lsp_id, char *dest, size_t dest_len, char dynhost, char frag, struct isis *isis); -void lsp_print(struct isis_lsp *lsp, struct vty *vty, char dynhost, - struct isis *isis); -void lsp_print_detail(struct isis_lsp *lsp, struct vty *vty, char dynhost, +void lsp_print_common(struct isis_lsp *lsp, struct vty *vty, + struct json_object *json, char dynhost, struct isis *isis); -int lsp_print_all(struct vty *vty, struct lspdb_head *head, char detail, - char dynhost, struct isis *isis); +void lsp_print_vty(struct isis_lsp *lsp, struct vty *vty, char dynhost, + struct isis *isis); +void lsp_print_json(struct isis_lsp *lsp, struct json_object *json, + char dynhost, struct isis *isis); +void lsp_print_detail(struct isis_lsp *lsp, struct vty *vty, + struct json_object *json, char dynhost, + struct isis *isis); +int lsp_print_all(struct vty *vty, struct json_object *json, + struct lspdb_head *head, char detail, char dynhost, + struct isis *isis); /* sets SRMflags for all active circuits of an lsp */ void lsp_set_all_srmflags(struct isis_lsp *lsp, bool set); diff --git a/isisd/isis_pdu.c b/isisd/isis_pdu.c index 517c9ec5a..1a54d47f3 100644 --- a/isisd/isis_pdu.c +++ b/isisd/isis_pdu.c @@ -2209,7 +2209,7 @@ int send_csnp(struct isis_circuit *circuit, int level) circuit->interface->name, stream_get_endp(circuit->snd_stream)); log_multiline(LOG_DEBUG, " ", "%s", - isis_format_tlvs(tlvs)); + isis_format_tlvs(tlvs, NULL)); if (IS_DEBUG_PACKET_DUMP) zlog_dump_data( STREAM_DATA(circuit->snd_stream), @@ -2368,7 +2368,7 @@ static int send_psnp(int level, struct isis_circuit *circuit) circuit->interface->name, stream_get_endp(circuit->snd_stream)); log_multiline(LOG_DEBUG, " ", "%s", - isis_format_tlvs(tlvs)); + isis_format_tlvs(tlvs, NULL)); if (IS_DEBUG_PACKET_DUMP) zlog_dump_data( STREAM_DATA(circuit->snd_stream), diff --git a/isisd/isis_spf.c b/isisd/isis_spf.c index 04b5cf1a6..d5b02f388 100644 --- a/isisd/isis_spf.c +++ b/isisd/isis_spf.c @@ -2683,3 +2683,15 @@ void isis_spf_print(struct isis_spftree *spftree, struct vty *vty) vty_out(vty, " run count : %u\n", spftree->runcount); } +void isis_spf_print_json(struct isis_spftree *spftree, struct json_object *json) +{ + char uptime[MONOTIME_STRLEN]; + time_t cur; + cur = time(NULL); + cur -= spftree->last_run_timestamp; + frrtime_to_interval(cur, uptime, sizeof(uptime)); + json_object_string_add(json, "last-run-elapsed", uptime); + json_object_int_add(json, "last-run-duration-usec", + spftree->last_run_duration); + json_object_int_add(json, "last-run-count", spftree->runcount); +} diff --git a/isisd/isis_spf.h b/isisd/isis_spf.h index 5b3aa5937..815db7b22 100644 --- a/isisd/isis_spf.h +++ b/isisd/isis_spf.h @@ -75,6 +75,8 @@ void isis_print_routes(struct vty *vty, struct isis_spftree *spftree, bool prefix_sid, bool backup); void isis_spf_init(void); void isis_spf_print(struct isis_spftree *spftree, struct vty *vty); +void isis_spf_print_json(struct isis_spftree *spftree, + struct json_object *json); void isis_run_spf(struct isis_spftree *spftree); struct isis_spftree *isis_run_hopcount_spf(struct isis_area *area, uint8_t *sysid, diff --git a/isisd/isis_tlvs.c b/isisd/isis_tlvs.c index f1aae7caf..d3d59fb43 100644 --- a/isisd/isis_tlvs.c +++ b/isisd/isis_tlvs.c @@ -22,6 +22,7 @@ * Software Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA * 02111-1307, USA. */ +#include <json-c/json_object.h> #include <zebra.h> #ifdef CRYPTO_INTERNAL @@ -57,7 +58,8 @@ typedef void (*free_item_func)(struct isis_item *i); typedef int (*unpack_item_func)(uint16_t mtid, uint8_t len, struct stream *s, struct sbuf *log, void *dest, int indent); typedef void (*format_item_func)(uint16_t mtid, struct isis_item *i, - struct sbuf *buf, int indent); + struct sbuf *buf, struct json_object *json, + int indent); typedef struct isis_item *(*copy_item_func)(struct isis_item *i); struct tlv_ops { @@ -208,152 +210,430 @@ copy_item_ext_subtlvs(struct isis_ext_subtlvs *exts, uint16_t mtid) /* mtid parameter is used to manage multi-topology i.e. IPv4 / IPv6 */ static void format_item_ext_subtlvs(struct isis_ext_subtlvs *exts, - struct sbuf *buf, int indent, - uint16_t mtid) + struct sbuf *buf, struct json_object *json, + int indent, uint16_t mtid) { + char aux_buf[255]; + char cnt_buf[255]; /* Standard metrics */ - if (IS_SUBTLV(exts, EXT_ADM_GRP)) - sbuf_push(buf, indent, "Administrative Group: 0x%x\n", - exts->adm_group); + if (IS_SUBTLV(exts, EXT_ADM_GRP)) { + if (json) { + snprintfrr(aux_buf, sizeof(aux_buf), "0x%x", + exts->adm_group); + json_object_string_add(json, "adm-group", aux_buf); + } else + sbuf_push(buf, indent, "Administrative Group: 0x%x\n", + exts->adm_group); + } if (IS_SUBTLV(exts, EXT_LLRI)) { - sbuf_push(buf, indent, "Link Local ID: %u\n", - exts->local_llri); - sbuf_push(buf, indent, "Link Remote ID: %u\n", - exts->remote_llri); - } - if (IS_SUBTLV(exts, EXT_LOCAL_ADDR)) - sbuf_push(buf, indent, "Local Interface IP Address(es): %pI4\n", - &exts->local_addr); - if (IS_SUBTLV(exts, EXT_NEIGH_ADDR)) - sbuf_push(buf, indent, - "Remote Interface IP Address(es): %pI4\n", - &exts->neigh_addr); - if (IS_SUBTLV(exts, EXT_LOCAL_ADDR6)) - sbuf_push(buf, indent, - "Local Interface IPv6 Address(es): %pI6\n", - &exts->local_addr6); - if (IS_SUBTLV(exts, EXT_NEIGH_ADDR6)) - sbuf_push(buf, indent, - "Remote Interface IPv6 Address(es): %pI6\n", - &exts->neigh_addr6); - if (IS_SUBTLV(exts, EXT_MAX_BW)) - sbuf_push(buf, indent, "Maximum Bandwidth: %g (Bytes/sec)\n", - exts->max_bw); - if (IS_SUBTLV(exts, EXT_MAX_RSV_BW)) - sbuf_push(buf, indent, - "Maximum Reservable Bandwidth: %g (Bytes/sec)\n", - exts->max_rsv_bw); + if (json) { + json_object_int_add(json, "link-local-id", + exts->local_llri); + json_object_int_add(json, "link-remote-id", + exts->remote_llri); + } else { + sbuf_push(buf, indent, "Link Local ID: %u\n", + exts->local_llri); + sbuf_push(buf, indent, "Link Remote ID: %u\n", + exts->remote_llri); + } + } + if (IS_SUBTLV(exts, EXT_LOCAL_ADDR)) { + if (json) { + inet_ntop(AF_INET, &exts->local_addr, aux_buf, + sizeof(aux_buf)); + json_object_string_add(json, "local-iface-ip", aux_buf); + } else + sbuf_push(buf, indent, + "Local Interface IP Address(es): %pI4\n", + &exts->local_addr); + } + if (IS_SUBTLV(exts, EXT_NEIGH_ADDR)) { + if (json) { + inet_ntop(AF_INET, &exts->neigh_addr, aux_buf, + sizeof(aux_buf)); + json_object_string_add(json, "remote-iface-ip", + aux_buf); + } else + sbuf_push(buf, indent, + "Remote Interface IP Address(es): %pI4\n", + &exts->neigh_addr); + } + if (IS_SUBTLV(exts, EXT_LOCAL_ADDR6)) { + if (json) { + inet_ntop(AF_INET6, &exts->local_addr6, aux_buf, + sizeof(aux_buf)); + json_object_string_add(json, "local-iface-ipv6", + aux_buf); + } else + sbuf_push(buf, indent, + "Local Interface IPv6 Address(es): %pI6\n", + &exts->local_addr6); + } + if (IS_SUBTLV(exts, EXT_NEIGH_ADDR6)) { + if (json) { + inet_ntop(AF_INET6, &exts->neigh_addr6, aux_buf, + sizeof(aux_buf)); + json_object_string_add(json, "remote-iface-ipv6", + aux_buf); + } else + sbuf_push(buf, indent, + "Remote Interface IPv6 Address(es): %pI6\n", + &exts->neigh_addr6); + } + if (IS_SUBTLV(exts, EXT_MAX_BW)) { + if (json) { + snprintfrr(aux_buf, sizeof(aux_buf), "%g", + exts->max_bw); + json_object_string_add(json, "max-bandwith-bytes-sec", + aux_buf); + } else + sbuf_push(buf, indent, + "Maximum Bandwidth: %g (Bytes/sec)\n", + exts->max_bw); + } + if (IS_SUBTLV(exts, EXT_MAX_RSV_BW)) { + if (json) { + snprintfrr(aux_buf, sizeof(aux_buf), "%g", + exts->max_rsv_bw); + json_object_string_add( + json, "max-res-bandwith-bytes-sec", aux_buf); + } else + sbuf_push( + buf, indent, + "Maximum Reservable Bandwidth: %g (Bytes/sec)\n", + exts->max_rsv_bw); + } if (IS_SUBTLV(exts, EXT_UNRSV_BW)) { - sbuf_push(buf, indent, "Unreserved Bandwidth:\n"); - for (int j = 0; j < MAX_CLASS_TYPE; j += 2) { - sbuf_push(buf, indent + 2, - "[%d]: %g (Bytes/sec),\t[%d]: %g (Bytes/sec)\n", - j, exts->unrsv_bw[j], - j + 1, exts->unrsv_bw[j + 1]); + if (json) { + struct json_object *unrsv_json; + unrsv_json = json_object_new_object(); + json_object_object_add(json, "unrsv-bandwith-bytes-sec", + unrsv_json); + for (int j = 0; j < MAX_CLASS_TYPE; j += 1) { + snprintfrr(cnt_buf, sizeof(cnt_buf), "%d", j); + snprintfrr(aux_buf, sizeof(aux_buf), "%g", + exts->unrsv_bw[j]); + json_object_string_add(unrsv_json, cnt_buf, + aux_buf); + } + } else { + sbuf_push(buf, indent, "Unreserved Bandwidth:\n"); + for (int j = 0; j < MAX_CLASS_TYPE; j += 2) { + sbuf_push( + buf, indent + 2, + "[%d]: %g (Bytes/sec),\t[%d]: %g (Bytes/sec)\n", + j, exts->unrsv_bw[j], j + 1, + exts->unrsv_bw[j + 1]); + } } } - if (IS_SUBTLV(exts, EXT_TE_METRIC)) - sbuf_push(buf, indent, "Traffic Engineering Metric: %u\n", - exts->te_metric); - if (IS_SUBTLV(exts, EXT_RMT_AS)) - sbuf_push(buf, indent, - "Inter-AS TE Remote AS number: %u\n", - exts->remote_as); - if (IS_SUBTLV(exts, EXT_RMT_IP)) - sbuf_push(buf, indent, - "Inter-AS TE Remote ASBR IP address: %pI4\n", - &exts->remote_ip); + if (IS_SUBTLV(exts, EXT_TE_METRIC)) { + if (json) { + json_object_int_add(json, "te-metric", exts->te_metric); + } else + sbuf_push(buf, indent, + "Traffic Engineering Metric: %u\n", + exts->te_metric); + } + if (IS_SUBTLV(exts, EXT_RMT_AS)) { + if (json) { + json_object_int_add(json, "inter-as-te-remote-as", + exts->remote_as); + } else + sbuf_push(buf, indent, + "Inter-AS TE Remote AS number: %u\n", + exts->remote_as); + } + if (IS_SUBTLV(exts, EXT_RMT_IP)) { + if (json) { + inet_ntop(AF_INET6, &exts->remote_ip, aux_buf, + sizeof(aux_buf)); + json_object_string_add( + json, "inter-as-te-remote-asbr-ip", aux_buf); + } else + sbuf_push(buf, indent, + "Inter-AS TE Remote ASBR IP address: %pI4\n", + &exts->remote_ip); + } /* Extended metrics */ - if (IS_SUBTLV(exts, EXT_DELAY)) - sbuf_push(buf, indent, - "%s Average Link Delay: %u (micro-sec)\n", - IS_ANORMAL(exts->delay) ? "Anomalous" : "Normal", - exts->delay); + if (IS_SUBTLV(exts, EXT_DELAY)) { + if (json) { + struct json_object *avg_json; + avg_json = json_object_new_object(); + json_object_object_add(json, "avg-delay", avg_json); + json_object_string_add(avg_json, "delay", + IS_ANORMAL(exts->delay) + ? "Anomalous" + : "Normal"); + json_object_int_add(avg_json, "micro-sec", exts->delay); + } else + sbuf_push(buf, indent, + "%s Average Link Delay: %u (micro-sec)\n", + IS_ANORMAL(exts->delay) ? "Anomalous" + : "Normal", + exts->delay); + } if (IS_SUBTLV(exts, EXT_MM_DELAY)) { - sbuf_push(buf, indent, "%s Min/Max Link Delay: %u / %u (micro-sec)\n", - IS_ANORMAL(exts->min_delay) ? "Anomalous" : "Normal", - exts->min_delay & TE_EXT_MASK, - exts->max_delay & TE_EXT_MASK); + if (json) { + struct json_object *avg_json; + avg_json = json_object_new_object(); + json_object_object_add(json, "max-min-delay", avg_json); + json_object_string_add(avg_json, "delay", + IS_ANORMAL(exts->min_delay) + ? "Anomalous" + : "Normal"); + snprintfrr(aux_buf, sizeof(aux_buf), "%u / %u", + exts->min_delay & TE_EXT_MASK, + exts->max_delay & TE_EXT_MASK); + json_object_string_add(avg_json, "micro-sec", aux_buf); + + } else + sbuf_push( + buf, indent, + "%s Min/Max Link Delay: %u / %u (micro-sec)\n", + IS_ANORMAL(exts->min_delay) ? "Anomalous" + : "Normal", + exts->min_delay & TE_EXT_MASK, + exts->max_delay & TE_EXT_MASK); } if (IS_SUBTLV(exts, EXT_DELAY_VAR)) { - sbuf_push(buf, indent, - "Delay Variation: %u (micro-sec)\n", - exts->delay_var & TE_EXT_MASK); - } - if (IS_SUBTLV(exts, EXT_PKT_LOSS)) - sbuf_push(buf, indent, "%s Link Packet Loss: %g (%%)\n", - IS_ANORMAL(exts->pkt_loss) ? "Anomalous" : "Normal", - (float)((exts->pkt_loss & TE_EXT_MASK) - * LOSS_PRECISION)); - if (IS_SUBTLV(exts, EXT_RES_BW)) - sbuf_push(buf, indent, - "Unidir. Residual Bandwidth: %g (Bytes/sec)\n", - exts->res_bw); - if (IS_SUBTLV(exts, EXT_AVA_BW)) - sbuf_push(buf, indent, - "Unidir. Available Bandwidth: %g (Bytes/sec)\n", - exts->ava_bw); - if (IS_SUBTLV(exts, EXT_USE_BW)) - sbuf_push(buf, indent, - "Unidir. Utilized Bandwidth: %g (Bytes/sec)\n", - exts->use_bw); + if (json) { + json_object_int_add(json, "delay-variation-micro-sec", + exts->delay_var & TE_EXT_MASK); + } else + sbuf_push(buf, indent, + "Delay Variation: %u (micro-sec)\n", + exts->delay_var & TE_EXT_MASK); + } + if (IS_SUBTLV(exts, EXT_PKT_LOSS)) { + if (json) { + snprintfrr(aux_buf, sizeof(aux_buf), "%g", + (float)((exts->pkt_loss & TE_EXT_MASK) * + LOSS_PRECISION)); + struct json_object *link_json; + link_json = json_object_new_object(); + json_object_object_add(json, "link-packet-loss", + link_json); + json_object_string_add(link_json, "loss", + IS_ANORMAL(exts->pkt_loss) + ? "Anomalous" + : "Normal"); + json_object_string_add(link_json, "percentaje", + aux_buf); + } else + sbuf_push(buf, indent, "%s Link Packet Loss: %g (%%)\n", + IS_ANORMAL(exts->pkt_loss) ? "Anomalous" + : "Normal", + (float)((exts->pkt_loss & TE_EXT_MASK) * + LOSS_PRECISION)); + } + if (IS_SUBTLV(exts, EXT_RES_BW)) { + if (json) { + snprintfrr(aux_buf, sizeof(aux_buf), "%g", + (exts->res_bw)); + json_object_string_add(json, + "unidir-residual-band-bytes-sec", + aux_buf); + } else + sbuf_push( + buf, indent, + "Unidir. Residual Bandwidth: %g (Bytes/sec)\n", + exts->res_bw); + } + if (IS_SUBTLV(exts, EXT_AVA_BW)) { + if (json) { + snprintfrr(aux_buf, sizeof(aux_buf), "%g", + (exts->ava_bw)); + json_object_string_add( + json, "unidir-available-band-bytes-sec", + aux_buf); + } else + sbuf_push( + buf, indent, + "Unidir. Available Bandwidth: %g (Bytes/sec)\n", + exts->ava_bw); + } + if (IS_SUBTLV(exts, EXT_USE_BW)) { + if (json) { + snprintfrr(aux_buf, sizeof(aux_buf), "%g", + (exts->use_bw)); + json_object_string_add(json, + "unidir-utilized-band-bytes-sec", + aux_buf); + } else + sbuf_push( + buf, indent, + "Unidir. Utilized Bandwidth: %g (Bytes/sec)\n", + exts->use_bw); + } /* Segment Routing Adjacency as per RFC8667 section #2.2.1 */ if (IS_SUBTLV(exts, EXT_ADJ_SID)) { struct isis_adj_sid *adj; - for (adj = (struct isis_adj_sid *)exts->adj_sid.head; adj; - adj = adj->next) { - sbuf_push( - buf, indent, - "Adjacency-SID: %u, Weight: %hhu, Flags: F:%c B:%c, V:%c, L:%c, S:%c, P:%c\n", - adj->sid, adj->weight, - adj->flags & EXT_SUBTLV_LINK_ADJ_SID_FFLG ? '1' - : '0', - adj->flags & EXT_SUBTLV_LINK_ADJ_SID_BFLG ? '1' - : '0', - adj->flags & EXT_SUBTLV_LINK_ADJ_SID_VFLG ? '1' - : '0', - adj->flags & EXT_SUBTLV_LINK_ADJ_SID_LFLG ? '1' - : '0', - adj->flags & EXT_SUBTLV_LINK_ADJ_SID_SFLG ? '1' - : '0', - adj->flags & EXT_SUBTLV_LINK_ADJ_SID_PFLG - ? '1' - : '0'); - } + if (json) { + struct json_object *arr_adj_json, *flags_json; + arr_adj_json = json_object_new_array(); + json_object_object_add(json, "adj-sid", arr_adj_json); + for (adj = (struct isis_adj_sid *)exts->adj_sid.head; + adj; adj = adj->next) { + snprintfrr(cnt_buf, sizeof(cnt_buf), "%d", + adj->sid); + flags_json = json_object_new_object(); + json_object_int_add(flags_json, "sid", + adj->sid); + json_object_int_add(flags_json, "weight", + adj->weight); + json_object_string_add( + flags_json, "flag-f", + adj->flags & EXT_SUBTLV_LINK_ADJ_SID_FFLG + ? "1" + : "0"); + json_object_string_add( + flags_json, "flag-b", + adj->flags & EXT_SUBTLV_LINK_ADJ_SID_BFLG + ? "1" + : "0"); + json_object_string_add( + flags_json, "flag-v", + adj->flags & EXT_SUBTLV_LINK_ADJ_SID_VFLG + ? "1" + : "0"); + json_object_string_add( + flags_json, "flag-l", + adj->flags & EXT_SUBTLV_LINK_ADJ_SID_LFLG + ? "1" + : "0"); + json_object_string_add( + flags_json, "flag-s", + adj->flags & EXT_SUBTLV_LINK_ADJ_SID_SFLG + ? "1" + : "0"); + json_object_string_add( + flags_json, "flag-p", + adj->flags & EXT_SUBTLV_LINK_ADJ_SID_PFLG + ? "1" + : "0"); + json_object_array_add(arr_adj_json, flags_json); + } + } else + for (adj = (struct isis_adj_sid *)exts->adj_sid.head; + adj; adj = adj->next) { + sbuf_push( + buf, indent, + "Adjacency-SID: %u, Weight: %hhu, Flags: F:%c B:%c, V:%c, L:%c, S:%c, P:%c\n", + adj->sid, adj->weight, + adj->flags & EXT_SUBTLV_LINK_ADJ_SID_FFLG + ? '1' + : '0', + adj->flags & EXT_SUBTLV_LINK_ADJ_SID_BFLG + ? '1' + : '0', + adj->flags & EXT_SUBTLV_LINK_ADJ_SID_VFLG + ? '1' + : '0', + adj->flags & EXT_SUBTLV_LINK_ADJ_SID_LFLG + ? '1' + : '0', + adj->flags & EXT_SUBTLV_LINK_ADJ_SID_SFLG + ? '1' + : '0', + adj->flags & EXT_SUBTLV_LINK_ADJ_SID_PFLG + ? '1' + : '0'); + } } /* Segment Routing LAN-Adjacency as per RFC8667 section #2.2.2 */ if (IS_SUBTLV(exts, EXT_LAN_ADJ_SID)) { struct isis_lan_adj_sid *lan; - - for (lan = (struct isis_lan_adj_sid *)exts->lan_sid.head; - lan; lan = lan->next) { - continue; - sbuf_push(buf, indent, - "Lan-Adjacency-SID: %u, Weight: %hhu, Flags: F:%c B:%c, V:%c, L:%c, S:%c, P:%c\n" - " Neighbor-ID: %s\n", - lan->sid, lan->weight, - lan->flags & EXT_SUBTLV_LINK_ADJ_SID_FFLG - ? '1' - : '0', - lan->flags & EXT_SUBTLV_LINK_ADJ_SID_BFLG - ? '1' - : '0', - lan->flags & EXT_SUBTLV_LINK_ADJ_SID_VFLG - ? '1' - : '0', - lan->flags & EXT_SUBTLV_LINK_ADJ_SID_LFLG - ? '1' - : '0', - lan->flags & EXT_SUBTLV_LINK_ADJ_SID_SFLG - ? '1' - : '0', - lan->flags & EXT_SUBTLV_LINK_ADJ_SID_PFLG - ? '1' - : '0', - isis_format_id(lan->neighbor_id, 6)); - } + if (json) { + struct json_object *arr_adj_json, *flags_json; + arr_adj_json = json_object_new_array(); + json_object_object_add(json, "lan-adj-sid", + arr_adj_json); + for (lan = (struct isis_lan_adj_sid *) + exts->adj_sid.head; + lan; lan = lan->next) { + if (((mtid == ISIS_MT_IPV4_UNICAST) && + (lan->family != AF_INET)) || + ((mtid == ISIS_MT_IPV6_UNICAST) && + (lan->family != AF_INET6))) + continue; + snprintfrr(cnt_buf, sizeof(cnt_buf), "%d", + lan->sid); + flags_json = json_object_new_object(); + json_object_int_add(flags_json, "sid", + lan->sid); + json_object_int_add(flags_json, "weight", + lan->weight); + json_object_string_add( + flags_json, "flag-f", + lan->flags & EXT_SUBTLV_LINK_ADJ_SID_FFLG + ? "1" + : "0"); + json_object_string_add( + flags_json, "flag-b", + lan->flags & EXT_SUBTLV_LINK_ADJ_SID_BFLG + ? "1" + : "0"); + json_object_string_add( + flags_json, "flag-v", + lan->flags & EXT_SUBTLV_LINK_ADJ_SID_VFLG + ? "1" + : "0"); + json_object_string_add( + flags_json, "flag-l", + lan->flags & EXT_SUBTLV_LINK_ADJ_SID_LFLG + ? "1" + : "0"); + json_object_string_add( + flags_json, "flag-s", + lan->flags & EXT_SUBTLV_LINK_ADJ_SID_SFLG + ? "1" + : "0"); + json_object_string_add( + flags_json, "flag-p", + lan->flags & EXT_SUBTLV_LINK_ADJ_SID_PFLG + ? "1" + : "0"); + json_object_array_add(arr_adj_json, flags_json); + } + } else + + for (lan = (struct isis_lan_adj_sid *) + exts->lan_sid.head; + lan; lan = lan->next) { + if (((mtid == ISIS_MT_IPV4_UNICAST) && + (lan->family != AF_INET)) || + ((mtid == ISIS_MT_IPV6_UNICAST) && + (lan->family != AF_INET6))) + continue; + sbuf_push( + buf, indent, + "Lan-Adjacency-SID: %u, Weight: %hhu, Flags: F:%c B:%c, V:%c, L:%c, S:%c, P:%c\n" + " Neighbor-ID: %s\n", + lan->sid, lan->weight, + lan->flags & EXT_SUBTLV_LINK_ADJ_SID_FFLG + ? '1' + : '0', + lan->flags & EXT_SUBTLV_LINK_ADJ_SID_BFLG + ? '1' + : '0', + lan->flags & EXT_SUBTLV_LINK_ADJ_SID_VFLG + ? '1' + : '0', + lan->flags & EXT_SUBTLV_LINK_ADJ_SID_LFLG + ? '1' + : '0', + lan->flags & EXT_SUBTLV_LINK_ADJ_SID_SFLG + ? '1' + : '0', + lan->flags & EXT_SUBTLV_LINK_ADJ_SID_PFLG + ? '1' + : '0', + isis_format_id(lan->neighbor_id, 6)); + } } } @@ -880,26 +1160,64 @@ static struct isis_item *copy_item_prefix_sid(struct isis_item *i) } static void format_item_prefix_sid(uint16_t mtid, struct isis_item *i, - struct sbuf *buf, int indent) + struct sbuf *buf, struct json_object *json, + int indent) { struct isis_prefix_sid *sid = (struct isis_prefix_sid *)i; - sbuf_push(buf, indent, "SR Prefix-SID "); - if (sid->flags & ISIS_PREFIX_SID_VALUE) { - sbuf_push(buf, 0, "Label: %u, ", sid->value); + if (json) { + struct json_object *sr_json; + sr_json = json_object_new_object(); + json_object_object_add(json, "sr", sr_json); + if (sid->flags & ISIS_PREFIX_SID_VALUE) { + json_object_int_add(sr_json, "label", sid->value); + } else { + json_object_int_add(sr_json, "index", sid->value); + } + json_object_int_add(sr_json, "alg", sid->algorithm); + json_object_string_add( + sr_json, "readvertised", + ((sid->flags & ISIS_PREFIX_SID_READVERTISED) ? "yes" + : "")); + json_object_string_add( + sr_json, "node", + ((sid->flags & ISIS_PREFIX_SID_NODE) ? "yes" : "")); + json_object_string_add(sr_json, "php", + ((sid->flags & ISIS_PREFIX_SID_NO_PHP) + ? "no-php" + : "php")); + json_object_string_add( + sr_json, "explicit-null", + ((sid->flags & ISIS_PREFIX_SID_EXPLICIT_NULL) ? "yes" + : "")); + json_object_string_add( + sr_json, "value", + ((sid->flags & ISIS_PREFIX_SID_VALUE) ? "yes" : "")); + json_object_string_add( + sr_json, "local", + ((sid->flags & ISIS_PREFIX_SID_LOCAL) ? "yes" : "")); + } else { - sbuf_push(buf, 0, "Index: %u, ", sid->value); + sbuf_push(buf, indent, "SR Prefix-SID "); + if (sid->flags & ISIS_PREFIX_SID_VALUE) { + sbuf_push(buf, 0, "Label: %u, ", sid->value); + } else { + sbuf_push(buf, 0, "Index: %u, ", sid->value); + } + sbuf_push(buf, 0, "Algorithm: %hhu, ", sid->algorithm); + sbuf_push(buf, 0, "Flags:%s%s%s%s%s%s\n", + sid->flags & ISIS_PREFIX_SID_READVERTISED + ? " READVERTISED" + : "", + sid->flags & ISIS_PREFIX_SID_NODE ? " NODE" : "", + sid->flags & ISIS_PREFIX_SID_NO_PHP ? " NO-PHP" + : " PHP", + sid->flags & ISIS_PREFIX_SID_EXPLICIT_NULL + ? " EXPLICIT-NULL" + : "", + sid->flags & ISIS_PREFIX_SID_VALUE ? " VALUE" : "", + sid->flags & ISIS_PREFIX_SID_LOCAL ? " LOCAL" : ""); } - sbuf_push(buf, 0, "Algorithm: %hhu, ", sid->algorithm); - sbuf_push(buf, 0, "Flags:%s%s%s%s%s%s\n", - sid->flags & ISIS_PREFIX_SID_READVERTISED ? " READVERTISED" - : "", - sid->flags & ISIS_PREFIX_SID_NODE ? " NODE" : "", - sid->flags & ISIS_PREFIX_SID_NO_PHP ? " NO-PHP" : " PHP", - sid->flags & ISIS_PREFIX_SID_EXPLICIT_NULL ? " EXPLICIT-NULL" - : "", - sid->flags & ISIS_PREFIX_SID_VALUE ? " VALUE" : "", - sid->flags & ISIS_PREFIX_SID_LOCAL ? " LOCAL" : ""); } static void free_item_prefix_sid(struct isis_item *i) @@ -977,7 +1295,7 @@ static int unpack_item_prefix_sid(uint16_t mtid, uint8_t len, struct stream *s, sid.value = stream_getl(s); } - format_item_prefix_sid(mtid, (struct isis_item *)&sid, log, indent + 2); + format_item_prefix_sid(mtid, (struct isis_item *)&sid, log, NULL, indent + 2); append_item(&subtlvs->prefix_sids, copy_item_prefix_sid((struct isis_item *)&sid)); return 0; } @@ -997,14 +1315,21 @@ static struct prefix_ipv6 *copy_subtlv_ipv6_source_prefix(struct prefix_ipv6 *p) } static void format_subtlv_ipv6_source_prefix(struct prefix_ipv6 *p, - struct sbuf *buf, int indent) + struct sbuf *buf, + struct json_object *json, + int indent) { if (!p) return; char prefixbuf[PREFIX2STR_BUFFER]; - sbuf_push(buf, indent, "IPv6 Source Prefix: %s\n", - prefix2str(p, prefixbuf, sizeof(prefixbuf))); + if (json) { + prefix2str(p, prefixbuf, sizeof(prefixbuf)); + json_object_string_add(json, "ipv6-src-prefix", prefixbuf); + } else { + sbuf_push(buf, indent, "IPv6 Source Prefix: %s\n", + prefix2str(p, prefixbuf, sizeof(prefixbuf))); + } } static int pack_subtlv_ipv6_source_prefix(struct prefix_ipv6 *p, @@ -1080,7 +1405,8 @@ static void copy_items(enum isis_tlv_context context, enum isis_tlv_type type, struct isis_item_list *src, struct isis_item_list *dest); static void format_items_(uint16_t mtid, enum isis_tlv_context context, enum isis_tlv_type type, struct isis_item_list *items, - struct sbuf *buf, int indent); + struct sbuf *buf, struct json_object *json, + int indent); #define format_items(...) format_items_(ISIS_MT_IPV4_UNICAST, __VA_ARGS__) static void free_items(enum isis_tlv_context context, enum isis_tlv_type type, struct isis_item_list *items); @@ -1124,12 +1450,12 @@ static struct isis_subtlvs *copy_subtlvs(struct isis_subtlvs *subtlvs) } static void format_subtlvs(struct isis_subtlvs *subtlvs, struct sbuf *buf, - int indent) + struct json_object *json, int indent) { format_items(subtlvs->context, ISIS_SUBTLV_PREFIX_SID, - &subtlvs->prefix_sids, buf, indent); + &subtlvs->prefix_sids, buf, json, indent); - format_subtlv_ipv6_source_prefix(subtlvs->source_prefix, buf, indent); + format_subtlv_ipv6_source_prefix(subtlvs->source_prefix, buf, json, indent); } static void isis_free_subtlvs(struct isis_subtlvs *subtlvs) @@ -1189,12 +1515,18 @@ static struct isis_item *copy_item_area_address(struct isis_item *i) } static void format_item_area_address(uint16_t mtid, struct isis_item *i, - struct sbuf *buf, int indent) + struct sbuf *buf, struct json_object *json, + int indent) { struct isis_area_address *addr = (struct isis_area_address *)i; - sbuf_push(buf, indent, "Area Address: %s\n", - isonet_print(addr->addr, addr->len)); + if (json) { + json_object_string_add(json, "area-addr", + isonet_print(addr->addr, addr->len)); + } else { + sbuf_push(buf, indent, "Area Address: %s\n", + isonet_print(addr->addr, addr->len)); + } } static void free_item_area_address(struct isis_item *i) @@ -1251,7 +1583,7 @@ static int unpack_item_area_address(uint16_t mtid, uint8_t len, stream_get(rv->addr, s, rv->len); format_item_area_address(ISIS_MT_IPV4_UNICAST, (struct isis_item *)rv, - log, indent + 2); + log, NULL, indent + 2); append_item(&tlvs->area_addresses, (struct isis_item *)rv); return 0; out: @@ -1271,12 +1603,21 @@ static struct isis_item *copy_item_oldstyle_reach(struct isis_item *i) } static void format_item_oldstyle_reach(uint16_t mtid, struct isis_item *i, - struct sbuf *buf, int indent) + struct sbuf *buf, + struct json_object *json, int indent) { struct isis_oldstyle_reach *r = (struct isis_oldstyle_reach *)i; - sbuf_push(buf, indent, "IS Reachability: %s (Metric: %hhu)\n", - isis_format_id(r->id, 7), r->metric); + if (json) { + struct json_object *old_json; + old_json = json_object_new_object(); + json_object_object_add(json, "old-reach-style", old_json); + json_object_string_add(old_json, "is-reach", + isis_format_id(r->id, 7)); + json_object_int_add(old_json, "metric", r->metric); + } else + sbuf_push(buf, indent, "IS Reachability: %s (Metric: %hhu)\n", + isis_format_id(r->id, 7), r->metric); } static void free_item_oldstyle_reach(struct isis_item *i) @@ -1327,7 +1668,7 @@ static int unpack_item_oldstyle_reach(uint16_t mtid, uint8_t len, stream_forward_getp(s, 3); /* Skip other metrics */ stream_get(rv->id, s, 7); - format_item_oldstyle_reach(mtid, (struct isis_item *)rv, log, + format_item_oldstyle_reach(mtid, (struct isis_item *)rv, log, NULL, indent + 2); append_item(&tlvs->oldstyle_reach, (struct isis_item *)rv); return 0; @@ -1344,11 +1685,17 @@ static struct isis_item *copy_item_lan_neighbor(struct isis_item *i) } static void format_item_lan_neighbor(uint16_t mtid, struct isis_item *i, - struct sbuf *buf, int indent) + struct sbuf *buf, struct json_object *json, + int indent) { struct isis_lan_neighbor *n = (struct isis_lan_neighbor *)i; - sbuf_push(buf, indent, "LAN Neighbor: %s\n", isis_format_id(n->mac, 6)); + if (json) { + json_object_string_add(json, "lan-neighbor", + isis_format_id(n->mac, 6)); + } else + sbuf_push(buf, indent, "LAN Neighbor: %s\n", + isis_format_id(n->mac, 6)); } static void free_item_lan_neighbor(struct isis_item *i) @@ -1389,7 +1736,7 @@ static int unpack_item_lan_neighbor(uint16_t mtid, uint8_t len, struct isis_lan_neighbor *rv = XCALLOC(MTYPE_ISIS_TLV, sizeof(*rv)); stream_get(rv->mac, s, 6); - format_item_lan_neighbor(mtid, (struct isis_item *)rv, log, indent + 2); + format_item_lan_neighbor(mtid, (struct isis_item *)rv, log, NULL, indent + 2); append_item(&tlvs->lan_neighbor, (struct isis_item *)rv); return 0; } @@ -1409,10 +1756,23 @@ static struct isis_item *copy_item_lsp_entry(struct isis_item *i) } static void format_item_lsp_entry(uint16_t mtid, struct isis_item *i, - struct sbuf *buf, int indent) + struct sbuf *buf, struct json_object *json, + int indent) { struct isis_lsp_entry *e = (struct isis_lsp_entry *)i; + if (json) { + char buf[255]; + struct json_object *lsp_json; + lsp_json = json_object_new_object(); + json_object_object_add(json, "lsp-entry", lsp_json); + json_object_string_add(lsp_json, "id", isis_format_id(e->id, 8)); + snprintfrr(buf,sizeof(buf),"0x%08x",e->seqno); + json_object_string_add(lsp_json, "seq", buf); + snprintfrr(buf,sizeof(buf),"0x%04hx",e->checksum); + json_object_string_add(lsp_json, "chksum", buf); + json_object_int_add(lsp_json, "lifetime", e->checksum); + } else sbuf_push(buf, indent, "LSP Entry: %s, seq 0x%08x, cksum 0x%04hx, lifetime %hus\n", isis_format_id(e->id, 8), e->seqno, e->checksum, @@ -1462,7 +1822,7 @@ static int unpack_item_lsp_entry(uint16_t mtid, uint8_t len, struct stream *s, rv->seqno = stream_getl(s); rv->checksum = stream_getw(s); - format_item_lsp_entry(mtid, (struct isis_item *)rv, log, indent + 2); + format_item_lsp_entry(mtid, (struct isis_item *)rv, log, NULL, indent + 2); append_item(&tlvs->lsp_entries, (struct isis_item *)rv); return 0; } @@ -1484,19 +1844,40 @@ static struct isis_item *copy_item_extended_reach(struct isis_item *i) } static void format_item_extended_reach(uint16_t mtid, struct isis_item *i, - struct sbuf *buf, int indent) + struct sbuf *buf, + struct json_object *json, int indent) { struct isis_extended_reach *r = (struct isis_extended_reach *)i; - sbuf_push(buf, indent, "%s Reachability: %s (Metric: %u)", - (mtid == ISIS_MT_IPV4_UNICAST) ? "Extended" : "MT", - isis_format_id(r->id, 7), r->metric); - if (mtid != ISIS_MT_IPV4_UNICAST) - sbuf_push(buf, 0, " %s", isis_mtid2str(mtid)); - sbuf_push(buf, 0, "\n"); + if (json) { + struct json_object *reach_json; + reach_json = json_object_new_object(); + json_object_object_add(json, "ext-reach", reach_json); + json_object_string_add( + reach_json, "mt-id", + (mtid == ISIS_MT_IPV4_UNICAST) ? "Extended" : "MT"); + json_object_string_add(reach_json, "id", + isis_format_id(r->id, 7)); + json_object_int_add(reach_json, "metric", r->metric); + if (mtid != ISIS_MT_IPV4_UNICAST) + json_object_string_add(reach_json, "mt-name", + isis_mtid2str(mtid)); + + if (r->subtlvs) + format_item_ext_subtlvs(r->subtlvs, NULL, json, + indent + 2, mtid); + } else { + sbuf_push(buf, indent, "%s Reachability: %s (Metric: %u)", + (mtid == ISIS_MT_IPV4_UNICAST) ? "Extended" : "MT", + isis_format_id(r->id, 7), r->metric); + if (mtid != ISIS_MT_IPV4_UNICAST) + sbuf_push(buf, 0, " %s", isis_mtid2str(mtid)); + sbuf_push(buf, 0, "\n"); - if (r->subtlvs) - format_item_ext_subtlvs(r->subtlvs, buf, indent + 2, mtid); + if (r->subtlvs) + format_item_ext_subtlvs(r->subtlvs, buf, NULL, + indent + 2, mtid); + } } static void free_item_extended_reach(struct isis_item *i) @@ -1579,7 +1960,7 @@ static int unpack_item_extended_reach(uint16_t mtid, uint8_t len, } } - format_item_extended_reach(mtid, (struct isis_item *)rv, log, + format_item_extended_reach(mtid, (struct isis_item *)rv, log, NULL, indent + 2); append_item(items, (struct isis_item *)rv); return 0; @@ -1603,11 +1984,20 @@ static struct isis_item *copy_item_oldstyle_ip_reach(struct isis_item *i) } static void format_item_oldstyle_ip_reach(uint16_t mtid, struct isis_item *i, - struct sbuf *buf, int indent) + struct sbuf *buf, + struct json_object *json, int indent) { struct isis_oldstyle_ip_reach *r = (struct isis_oldstyle_ip_reach *)i; char prefixbuf[PREFIX2STR_BUFFER]; + if (json) { + struct json_object *old_json; + old_json = json_object_new_object(); + json_object_object_add(json, "old-ip-reach-style", old_json); + json_object_string_add(old_json, "prefix", + prefix2str(&r->prefix, prefixbuf, sizeof(prefixbuf))); + json_object_int_add(old_json, "metric", r->metric); + } else sbuf_push(buf, indent, "IP Reachability: %s (Metric: %hhu)\n", prefix2str(&r->prefix, prefixbuf, sizeof(prefixbuf)), r->metric); @@ -1669,7 +2059,7 @@ static int unpack_item_oldstyle_ip_reach(uint16_t mtid, uint8_t len, stream_get(&mask, s, 4); rv->prefix.prefixlen = ip_masklen(mask); - format_item_oldstyle_ip_reach(mtid, (struct isis_item *)rv, log, + format_item_oldstyle_ip_reach(mtid, (struct isis_item *)rv, log, NULL, indent + 2); append_item(dest, (struct isis_item *)rv); return 0; @@ -1689,17 +2079,32 @@ static void copy_tlv_protocols_supported(struct isis_protocols_supported *src, } static void format_tlv_protocols_supported(struct isis_protocols_supported *p, - struct sbuf *buf, int indent) + struct sbuf *buf, + struct json_object *json, int indent) { if (!p || !p->count || !p->protocols) return; - sbuf_push(buf, indent, "Protocols Supported: "); - for (uint8_t i = 0; i < p->count; i++) { - sbuf_push(buf, 0, "%s%s", nlpid2str(p->protocols[i]), - (i + 1 < p->count) ? ", " : ""); + if (json) { + struct json_object *protocol_json; + char buf[255]; + + protocol_json = json_object_new_object(); + json_object_object_add(json, "protocols-supported", + protocol_json); + for (uint8_t i = 0; i < p->count; i++) { + snprintfrr(buf, sizeof(buf), "%d", i); + json_object_string_add(protocol_json, buf, + nlpid2str(p->protocols[i])); + } + } else { + sbuf_push(buf, indent, "Protocols Supported: "); + for (uint8_t i = 0; i < p->count; i++) { + sbuf_push(buf, 0, "%s%s", nlpid2str(p->protocols[i]), + (i + 1 < p->count) ? ", " : ""); + } + sbuf_push(buf, 0, "\n"); } - sbuf_push(buf, 0, "\n"); } static void free_tlv_protocols_supported(struct isis_protocols_supported *p) @@ -1746,7 +2151,7 @@ static int unpack_tlv_protocols_supported(enum isis_tlv_context context, tlvs->protocols_supported.protocols = XCALLOC(MTYPE_ISIS_TLV, tlv_len); stream_get(tlvs->protocols_supported.protocols, s, tlv_len); - format_tlv_protocols_supported(&tlvs->protocols_supported, log, + format_tlv_protocols_supported(&tlvs->protocols_supported, log, NULL, indent + 2); return 0; } @@ -1762,13 +2167,18 @@ static struct isis_item *copy_item_ipv4_address(struct isis_item *i) } static void format_item_ipv4_address(uint16_t mtid, struct isis_item *i, - struct sbuf *buf, int indent) + struct sbuf *buf, struct json_object *json, + int indent) { struct isis_ipv4_address *a = (struct isis_ipv4_address *)i; char addrbuf[INET_ADDRSTRLEN]; inet_ntop(AF_INET, &a->addr, addrbuf, sizeof(addrbuf)); - sbuf_push(buf, indent, "IPv4 Interface Address: %s\n", addrbuf); + if (json) { + json_object_string_add(json, "ipv4", addrbuf); + } else { + sbuf_push(buf, indent, "IPv4 Interface Address: %s\n", addrbuf); + } } static void free_item_ipv4_address(struct isis_item *i) @@ -1809,7 +2219,7 @@ static int unpack_item_ipv4_address(uint16_t mtid, uint8_t len, struct isis_ipv4_address *rv = XCALLOC(MTYPE_ISIS_TLV, sizeof(*rv)); stream_get(&rv->addr, s, 4); - format_item_ipv4_address(mtid, (struct isis_item *)rv, log, indent + 2); + format_item_ipv4_address(mtid, (struct isis_item *)rv, log, NULL, indent + 2); append_item(&tlvs->ipv4_address, (struct isis_item *)rv); return 0; } @@ -1826,13 +2236,17 @@ static struct isis_item *copy_item_ipv6_address(struct isis_item *i) } static void format_item_ipv6_address(uint16_t mtid, struct isis_item *i, - struct sbuf *buf, int indent) + struct sbuf *buf, struct json_object *json, + int indent) { struct isis_ipv6_address *a = (struct isis_ipv6_address *)i; char addrbuf[INET6_ADDRSTRLEN]; inet_ntop(AF_INET6, &a->addr, addrbuf, sizeof(addrbuf)); - sbuf_push(buf, indent, "IPv6 Interface Address: %s\n", addrbuf); + if (json) + json_object_string_add(json, "ipv6", addrbuf); + else + sbuf_push(buf, indent, "IPv6 Interface Address: %s\n", addrbuf); } static void free_item_ipv6_address(struct isis_item *i) @@ -1873,7 +2287,7 @@ static int unpack_item_ipv6_address(uint16_t mtid, uint8_t len, struct isis_ipv6_address *rv = XCALLOC(MTYPE_ISIS_TLV, sizeof(*rv)); stream_get(&rv->addr, s, IPV6_MAX_BYTELEN); - format_item_ipv6_address(mtid, (struct isis_item *)rv, log, indent + 2); + format_item_ipv6_address(mtid, (struct isis_item *)rv, log, NULL, indent + 2); append_item(&tlvs->ipv6_address, (struct isis_item *)rv); return 0; } @@ -1890,13 +2304,19 @@ static struct isis_item *copy_item_global_ipv6_address(struct isis_item *i) } static void format_item_global_ipv6_address(uint16_t mtid, struct isis_item *i, - struct sbuf *buf, int indent) + struct sbuf *buf, + struct json_object *json, + int indent) { struct isis_ipv6_address *a = (struct isis_ipv6_address *)i; char addrbuf[INET6_ADDRSTRLEN]; inet_ntop(AF_INET6, &a->addr, addrbuf, sizeof(addrbuf)); - sbuf_push(buf, indent, "Global IPv6 Interface Address: %s\n", addrbuf); + if (json) + json_object_string_add(json, "global-ipv6", addrbuf); + else + sbuf_push(buf, indent, "Global IPv6 Interface Address: %s\n", + addrbuf); } static void free_item_global_ipv6_address(struct isis_item *i) @@ -1937,7 +2357,7 @@ static int unpack_item_global_ipv6_address(uint16_t mtid, uint8_t len, struct isis_ipv6_address *rv = XCALLOC(MTYPE_ISIS_TLV, sizeof(*rv)); stream_get(&rv->addr, s, IPV6_MAX_BYTELEN); - format_item_global_ipv6_address(mtid, (struct isis_item *)rv, log, + format_item_global_ipv6_address(mtid, (struct isis_item *)rv, log, NULL, indent + 2); append_item(&tlvs->global_ipv6_address, (struct isis_item *)rv); return 0; @@ -1956,14 +2376,23 @@ static struct isis_item *copy_item_mt_router_info(struct isis_item *i) } static void format_item_mt_router_info(uint16_t mtid, struct isis_item *i, - struct sbuf *buf, int indent) + struct sbuf *buf, + struct json_object *json, int indent) { struct isis_mt_router_info *info = (struct isis_mt_router_info *)i; - sbuf_push(buf, indent, "MT Router Info: %s%s%s\n", - isis_mtid2str(info->mtid), - info->overload ? " Overload" : "", - info->attached ? " Attached" : ""); + if (json) { + struct json_object *mt_json; + mt_json = json_object_new_object(); + json_object_object_add(json, "mt", mt_json); + json_object_int_add(mt_json, "mtid", info->mtid); + json_object_string_add(mt_json, "overload", info->overload?"true":"false"); + json_object_string_add(mt_json, "attached", info->attached?"true":"false"); + } else + sbuf_push(buf, indent, "MT Router Info: %s%s%s\n", + isis_mtid2str(info->mtid), + info->overload ? " Overload" : "", + info->attached ? " Attached" : ""); } static void free_item_mt_router_info(struct isis_item *i) @@ -2015,7 +2444,7 @@ static int unpack_item_mt_router_info(uint16_t mtid, uint8_t len, rv->attached = entry & ISIS_MT_AT_MASK; rv->mtid = entry & ISIS_MT_MASK; - format_item_mt_router_info(mtid, (struct isis_item *)rv, log, + format_item_mt_router_info(mtid, (struct isis_item *)rv, log, NULL, indent + 2); append_item(&tlvs->mt_router_info, (struct isis_item *)rv); return 0; @@ -2034,14 +2463,17 @@ static struct in_addr *copy_tlv_te_router_id(const struct in_addr *id) } static void format_tlv_te_router_id(const struct in_addr *id, struct sbuf *buf, - int indent) + struct json_object *json, int indent) { if (!id) return; char addrbuf[INET_ADDRSTRLEN]; inet_ntop(AF_INET, id, addrbuf, sizeof(addrbuf)); - sbuf_push(buf, indent, "TE Router ID: %s\n", addrbuf); + if (json) + json_object_string_add(json, "te-router-id", addrbuf); + else + sbuf_push(buf, indent, "TE Router ID: %s\n", addrbuf); } static void free_tlv_te_router_id(struct in_addr *id) @@ -2085,7 +2517,7 @@ static int unpack_tlv_te_router_id(enum isis_tlv_context context, tlvs->te_router_id = XCALLOC(MTYPE_ISIS_TLV, 4); stream_get(tlvs->te_router_id, s, 4); - format_tlv_te_router_id(tlvs->te_router_id, log, indent + 2); + format_tlv_te_router_id(tlvs->te_router_id, log, NULL, indent + 2); return 0; } @@ -2107,22 +2539,46 @@ static struct isis_item *copy_item_extended_ip_reach(struct isis_item *i) } static void format_item_extended_ip_reach(uint16_t mtid, struct isis_item *i, - struct sbuf *buf, int indent) + struct sbuf *buf, + struct json_object *json, int indent) { struct isis_extended_ip_reach *r = (struct isis_extended_ip_reach *)i; char prefixbuf[PREFIX2STR_BUFFER]; - sbuf_push(buf, indent, "%s IP Reachability: %s (Metric: %u)%s", - (mtid == ISIS_MT_IPV4_UNICAST) ? "Extended" : "MT", - prefix2str(&r->prefix, prefixbuf, sizeof(prefixbuf)), r->metric, - r->down ? " Down" : ""); - if (mtid != ISIS_MT_IPV4_UNICAST) - sbuf_push(buf, 0, " %s", isis_mtid2str(mtid)); - sbuf_push(buf, 0, "\n"); - - if (r->subtlvs) { - sbuf_push(buf, indent, " Subtlvs:\n"); - format_subtlvs(r->subtlvs, buf, indent + 4); + if (json) { + struct json_object *ext_json; + ext_json = json_object_new_object(); + json_object_object_add(json, "ext-ip-reach", ext_json); + json_object_string_add( + json, "mt-id", + (mtid == ISIS_MT_IPV4_UNICAST) ? "Extended" : "MT"); + json_object_string_add( + json, "ip-reach", + prefix2str(&r->prefix, prefixbuf, sizeof(prefixbuf))); + json_object_int_add(json, "ip-reach-metric", r->metric); + json_object_string_add(json, "down", r->down ? "yes" : ""); + if (mtid != ISIS_MT_IPV4_UNICAST) + json_object_string_add(json, "mt-name", + isis_mtid2str(mtid)); + if (r->subtlvs) { + struct json_object *subtlv_json; + subtlv_json = json_object_new_object(); + json_object_object_add(json, "subtlvs", subtlv_json); + format_subtlvs(r->subtlvs, NULL, subtlv_json, 0); + } + } else { + sbuf_push(buf, indent, "%s IP Reachability: %s (Metric: %u)%s", + (mtid == ISIS_MT_IPV4_UNICAST) ? "Extended" : "MT", + prefix2str(&r->prefix, prefixbuf, sizeof(prefixbuf)), + r->metric, r->down ? " Down" : ""); + if (mtid != ISIS_MT_IPV4_UNICAST) + sbuf_push(buf, 0, " %s", isis_mtid2str(mtid)); + sbuf_push(buf, 0, "\n"); + + if (r->subtlvs) { + sbuf_push(buf, indent, " Subtlvs:\n"); + format_subtlvs(r->subtlvs, buf, NULL, indent + 4); + } } } @@ -2216,7 +2672,7 @@ static int unpack_item_extended_ip_reach(uint16_t mtid, uint8_t len, if (orig_prefix != rv->prefix.prefix.s_addr) sbuf_push(log, indent + 2, "WARNING: Prefix had hostbits set.\n"); - format_item_extended_ip_reach(mtid, (struct isis_item *)rv, log, + format_item_extended_ip_reach(mtid, (struct isis_item *)rv, log, NULL, indent + 2); if (control & ISIS_EXTENDED_IP_REACH_SUBTLV) { @@ -2273,12 +2729,15 @@ static char *copy_tlv_dynamic_hostname(const char *hostname) } static void format_tlv_dynamic_hostname(const char *hostname, struct sbuf *buf, - int indent) + struct json_object *json, int indent) { if (!hostname) return; - sbuf_push(buf, indent, "Hostname: %s\n", hostname); + if (json) + json_object_string_add(json, "hostname", hostname); + else + sbuf_push(buf, indent, "Hostname: %s\n", hostname); } static void free_tlv_dynamic_hostname(char *hostname) @@ -2356,14 +2815,18 @@ static struct in6_addr *copy_tlv_te_router_id_ipv6(const struct in6_addr *id) } static void format_tlv_te_router_id_ipv6(const struct in6_addr *id, - struct sbuf *buf, int indent) + struct sbuf *buf, + struct json_object *json, int indent) { if (!id) return; char addrbuf[INET6_ADDRSTRLEN]; inet_ntop(AF_INET6, id, addrbuf, sizeof(addrbuf)); - sbuf_push(buf, indent, "IPv6 TE Router ID: %s\n", addrbuf); + if (json) + json_object_string_add(json, "ipv6-te-router-id", addrbuf); + else + sbuf_push(buf, indent, "IPv6 TE Router ID: %s\n", addrbuf); } static void free_tlv_te_router_id_ipv6(struct in6_addr *id) @@ -2409,7 +2872,7 @@ static int unpack_tlv_te_router_id_ipv6(enum isis_tlv_context context, tlvs->te_router_id_ipv6 = XCALLOC(MTYPE_ISIS_TLV, IPV6_MAX_BYTELEN); stream_get(tlvs->te_router_id_ipv6, s, IPV6_MAX_BYTELEN); - format_tlv_te_router_id_ipv6(tlvs->te_router_id_ipv6, log, indent + 2); + format_tlv_te_router_id_ipv6(tlvs->te_router_id_ipv6, log, NULL, indent + 2); return 0; } @@ -2429,26 +2892,50 @@ static struct isis_spine_leaf *copy_tlv_spine_leaf( } static void format_tlv_spine_leaf(const struct isis_spine_leaf *spine_leaf, - struct sbuf *buf, int indent) + struct sbuf *buf, struct json_object *json, + int indent) { if (!spine_leaf) return; - sbuf_push(buf, indent, "Spine-Leaf-Extension:\n"); - if (spine_leaf->has_tier) { - if (spine_leaf->tier == ISIS_TIER_UNDEFINED) { - sbuf_push(buf, indent, " Tier: undefined\n"); - } else { - sbuf_push(buf, indent, " Tier: %hhu\n", - spine_leaf->tier); + char aux_buf[255]; + + if (json) { + struct json_object *spine_json; + spine_json = json_object_new_object(); + json_object_object_add(json, "spine-leaf-extension", + spine_json); + if (spine_leaf->has_tier) { + snprintfrr(aux_buf, sizeof(aux_buf), "%hhu", + spine_leaf->tier); + json_object_string_add( + spine_json, "tier", + (spine_leaf->tier == ISIS_TIER_UNDEFINED) + ? "undefined" + : aux_buf); + } + json_object_string_add(spine_json, "flag-leaf", + spine_leaf->is_leaf ? "yes" : ""); + json_object_string_add(spine_json, "flag-spine", + spine_leaf->is_spine ? "yes" : ""); + json_object_string_add(spine_json, "flag-backup", + spine_leaf->is_backup ? "yes" : ""); + } else { + sbuf_push(buf, indent, "Spine-Leaf-Extension:\n"); + if (spine_leaf->has_tier) { + if (spine_leaf->tier == ISIS_TIER_UNDEFINED) { + sbuf_push(buf, indent, " Tier: undefined\n"); + } else { + sbuf_push(buf, indent, " Tier: %hhu\n", + spine_leaf->tier); + } } - } - - sbuf_push(buf, indent, " Flags:%s%s%s\n", - spine_leaf->is_leaf ? " LEAF" : "", - spine_leaf->is_spine ? " SPINE" : "", - spine_leaf->is_backup ? " BACKUP" : ""); + sbuf_push(buf, indent, " Flags:%s%s%s\n", + spine_leaf->is_leaf ? " LEAF" : "", + spine_leaf->is_spine ? " SPINE" : "", + spine_leaf->is_backup ? " BACKUP" : ""); + } } static void free_tlv_spine_leaf(struct isis_spine_leaf *spine_leaf) @@ -2562,25 +3049,45 @@ static struct isis_threeway_adj *copy_tlv_threeway_adj( return rv; } -static void format_tlv_threeway_adj(const struct isis_threeway_adj *threeway_adj, - struct sbuf *buf, int indent) +static void +format_tlv_threeway_adj(const struct isis_threeway_adj *threeway_adj, + struct sbuf *buf, struct json_object *json, int indent) { if (!threeway_adj) return; - sbuf_push(buf, indent, "P2P Three-Way Adjacency:\n"); - sbuf_push(buf, indent, " State: %s (%d)\n", - isis_threeway_state_name(threeway_adj->state), - threeway_adj->state); - sbuf_push(buf, indent, " Extended Local Circuit ID: %u\n", - threeway_adj->local_circuit_id); - if (!threeway_adj->neighbor_set) - return; + if (json) { + struct json_object *three_json; + three_json = json_object_new_object(); + json_object_object_add(json, "p2p-three-way-adj", three_json); + json_object_string_add( + three_json, "state-name", + isis_threeway_state_name(threeway_adj->state)); + json_object_int_add(three_json, "state", threeway_adj->state); + json_object_int_add(three_json, "ext-local-circuit-id", + threeway_adj->local_circuit_id); + if (!threeway_adj->neighbor_set) + return; + json_object_string_add( + three_json, "neigh-system-id", + isis_format_id(threeway_adj->neighbor_id, 6)); + json_object_int_add(three_json, "neigh-ext-circuit-id", + threeway_adj->neighbor_circuit_id); + } else { + sbuf_push(buf, indent, "P2P Three-Way Adjacency:\n"); + sbuf_push(buf, indent, " State: %s (%d)\n", + isis_threeway_state_name(threeway_adj->state), + threeway_adj->state); + sbuf_push(buf, indent, " Extended Local Circuit ID: %u\n", + threeway_adj->local_circuit_id); + if (!threeway_adj->neighbor_set) + return; - sbuf_push(buf, indent, " Neighbor System ID: %s\n", - isis_format_id(threeway_adj->neighbor_id, 6)); - sbuf_push(buf, indent, " Neighbor Extended Circuit ID: %u\n", - threeway_adj->neighbor_circuit_id); + sbuf_push(buf, indent, " Neighbor System ID: %s\n", + isis_format_id(threeway_adj->neighbor_id, 6)); + sbuf_push(buf, indent, " Neighbor Extended Circuit ID: %u\n", + threeway_adj->neighbor_circuit_id); + } } static void free_tlv_threeway_adj(struct isis_threeway_adj *threeway_adj) @@ -2663,24 +3170,51 @@ static struct isis_item *copy_item_ipv6_reach(struct isis_item *i) } static void format_item_ipv6_reach(uint16_t mtid, struct isis_item *i, - struct sbuf *buf, int indent) + struct sbuf *buf, struct json_object *json, + int indent) { struct isis_ipv6_reach *r = (struct isis_ipv6_reach *)i; char prefixbuf[PREFIX2STR_BUFFER]; - sbuf_push(buf, indent, "%sIPv6 Reachability: %s (Metric: %u)%s%s", - (mtid == ISIS_MT_IPV4_UNICAST) ? "" : "MT ", - prefix2str(&r->prefix, prefixbuf, sizeof(prefixbuf)), - r->metric, - r->down ? " Down" : "", - r->external ? " External" : ""); - if (mtid != ISIS_MT_IPV4_UNICAST) - sbuf_push(buf, 0, " %s", isis_mtid2str(mtid)); - sbuf_push(buf, 0, "\n"); - - if (r->subtlvs) { - sbuf_push(buf, indent, " Subtlvs:\n"); - format_subtlvs(r->subtlvs, buf, indent + 4); + if (json) { + struct json_object *reach_json; + reach_json = json_object_new_object(); + json_object_object_add(json, "ipv6-reach", reach_json); + json_object_string_add(reach_json, "mt-id", + (mtid == ISIS_MT_IPV4_UNICAST) ? "" + : "mt"); + json_object_string_add( + reach_json, "prefix", + prefix2str(&r->prefix, prefixbuf, sizeof(prefixbuf))); + json_object_int_add(reach_json, "metric", r->metric); + json_object_string_add(reach_json, "down", + r->down ? "yes" : ""); + json_object_string_add(reach_json, "external", + r->external ? "yes" : ""); + if (mtid != ISIS_MT_IPV4_UNICAST) + json_object_string_add(reach_json, "mt-name", + isis_mtid2str(mtid)); + if (r->subtlvs) { + struct json_object *subtlvs_json; + subtlvs_json = json_object_new_object(); + json_object_object_add(json, "subtlvs", subtlvs_json); + format_subtlvs(r->subtlvs, NULL, subtlvs_json, 0); + } + } else { + sbuf_push(buf, indent, + "%sIPv6 Reachability: %s (Metric: %u)%s%s", + (mtid == ISIS_MT_IPV4_UNICAST) ? "" : "MT ", + prefix2str(&r->prefix, prefixbuf, sizeof(prefixbuf)), + r->metric, r->down ? " Down" : "", + r->external ? " External" : ""); + if (mtid != ISIS_MT_IPV4_UNICAST) + sbuf_push(buf, 0, " %s", isis_mtid2str(mtid)); + sbuf_push(buf, 0, "\n"); + + if (r->subtlvs) { + sbuf_push(buf, indent, " Subtlvs:\n"); + format_subtlvs(r->subtlvs, buf, NULL, indent + 4); + } } } @@ -2773,7 +3307,7 @@ static int unpack_item_ipv6_reach(uint16_t mtid, uint8_t len, struct stream *s, if (memcmp(&orig_prefix, &rv->prefix.prefix, sizeof(orig_prefix))) sbuf_push(log, indent + 2, "WARNING: Prefix had hostbits set.\n"); - format_item_ipv6_reach(mtid, (struct isis_item *)rv, log, indent + 2); + format_item_ipv6_reach(mtid, (struct isis_item *)rv, log, NULL, indent + 2); if (control & ISIS_IPV6_REACH_SUBTLV) { consume += 1; @@ -2834,6 +3368,77 @@ static struct isis_router_cap *copy_tlv_router_cap( return rv; } +static void format_tlv_router_cap_json(const struct isis_router_cap *router_cap, + struct json_object *json) +{ + char addrbuf[INET_ADDRSTRLEN]; + + if (!router_cap) + return; + + /* Router ID and Flags */ + struct json_object *cap_json; + cap_json = json_object_new_object(); + json_object_object_add(json, "router-capability", cap_json); + inet_ntop(AF_INET, &router_cap->router_id, addrbuf, sizeof(addrbuf)); + json_object_string_add(cap_json, "id", addrbuf); + json_object_string_add( + cap_json, "flag-d", + router_cap->flags & ISIS_ROUTER_CAP_FLAG_D ? "1" : "0"); + json_object_string_add( + cap_json, "flag-s", + router_cap->flags & ISIS_ROUTER_CAP_FLAG_S ? "1" : "0"); + + /* Segment Routing Global Block as per RFC8667 section #3.1 */ + if (router_cap->srgb.range_size != 0) { + struct json_object *gb_json; + gb_json = json_object_new_object(); + json_object_object_add(json, "segment-routing-gb", gb_json); + json_object_string_add(gb_json, "ipv4", + IS_SR_IPV4(&router_cap->srgb) ? "1" + : "0"); + json_object_string_add(gb_json, "ipv6", + IS_SR_IPV6(&router_cap->srgb) ? "1" + : "0"); + json_object_int_add(gb_json, "global-block-base", + router_cap->srgb.lower_bound); + json_object_int_add(gb_json, "global-block-range", + router_cap->srgb.range_size); + } + + /* Segment Routing Local Block as per RFC8667 section #3.3 */ + if (router_cap->srlb.range_size != 0) { + struct json_object *lb_json; + lb_json = json_object_new_object(); + json_object_object_add(json, "segment-routing-lb", lb_json); + json_object_int_add(lb_json, "global-block-base", + router_cap->srlb.lower_bound); + json_object_int_add(lb_json, "global-block-range", + router_cap->srlb.range_size); + } + + /* Segment Routing Algorithms as per RFC8667 section #3.2 */ + if (router_cap->algo[0] != SR_ALGORITHM_UNSET) { + char buf[255]; + struct json_object *alg_json; + alg_json = json_object_new_object(); + json_object_object_add(json, "segment-routing-algorithm", + alg_json); + for (int i = 0; i < SR_ALGORITHM_COUNT; i++) + if (router_cap->algo[i] != SR_ALGORITHM_UNSET) { + snprintfrr(buf, sizeof(buf), "%d", i); + json_object_string_add(alg_json, buf, + router_cap->algo[i] == 0 + ? "SPF" + : "Strict SPF"); + } + } + + /* Segment Routing Node MSD as per RFC8491 section #2 */ + if (router_cap->msd != 0) + json_object_int_add(json, "msd", router_cap->msd); +} + static void format_tlv_router_cap(const struct isis_router_cap *router_cap, struct sbuf *buf, int indent) { @@ -3177,26 +3782,40 @@ static struct isis_item *copy_item_auth(struct isis_item *i) } static void format_item_auth(uint16_t mtid, struct isis_item *i, - struct sbuf *buf, int indent) + struct sbuf *buf, struct json_object *json, + int indent) { struct isis_auth *auth = (struct isis_auth *)i; char obuf[768]; - sbuf_push(buf, indent, "Authentication:\n"); + if (json) + json_object_string_add(json, "test-auth", "ok"); + else + sbuf_push(buf, indent, "Authentication:\n"); switch (auth->type) { case ISIS_PASSWD_TYPE_CLEARTXT: zlog_sanitize(obuf, sizeof(obuf), auth->value, auth->length); - sbuf_push(buf, indent, " Password: %s\n", obuf); + if (json) + json_object_string_add(json, "auth-pass", obuf); + else + sbuf_push(buf, indent, " Password: %s\n", obuf); break; case ISIS_PASSWD_TYPE_HMAC_MD5: for (unsigned int j = 0; j < 16; j++) { - snprintf(obuf + 2 * j, sizeof(obuf) - 2 * j, - "%02hhx", auth->value[j]); + snprintf(obuf + 2 * j, sizeof(obuf) - 2 * j, "%02hhx", + auth->value[j]); } - sbuf_push(buf, indent, " HMAC-MD5: %s\n", obuf); + if (json) + json_object_string_add(json, "auth-hmac-md5", obuf); + else + sbuf_push(buf, indent, " HMAC-MD5: %s\n", obuf); break; default: - sbuf_push(buf, indent, " Unknown (%hhu)\n", auth->type); + if (json) + json_object_int_add(json, "auth-unknown", auth->type); + else + sbuf_push(buf, indent, " Unknown (%hhu)\n", + auth->type); break; } } @@ -3270,7 +3889,7 @@ static int unpack_item_auth(uint16_t mtid, uint8_t len, struct stream *s, rv->offset = stream_get_getp(s); stream_get(rv->value, s, rv->length); - format_item_auth(mtid, (struct isis_item *)rv, log, indent + 2); + format_item_auth(mtid, (struct isis_item *)rv, log, NULL, indent + 2); append_item(&tlvs->isis_auth, (struct isis_item *)rv); return 0; } @@ -3294,17 +3913,36 @@ static struct isis_purge_originator *copy_tlv_purge_originator( } static void format_tlv_purge_originator(struct isis_purge_originator *poi, - struct sbuf *buf, int indent) + struct sbuf *buf, + struct json_object *json, int indent) { if (!poi) return; - sbuf_push(buf, indent, "Purge Originator Identification:\n"); - sbuf_push(buf, indent, " Generator: %s\n", - isis_format_id(poi->generator, sizeof(poi->generator))); - if (poi->sender_set) { - sbuf_push(buf, indent, " Received-From: %s\n", - isis_format_id(poi->sender, sizeof(poi->sender))); + if (json) { + struct json_object *purge_json; + purge_json = json_object_new_object(); + json_object_object_add(json, "purge_originator", purge_json); + + json_object_string_add( + purge_json, "id", + isis_format_id(poi->generator, sizeof(poi->generator))); + if (poi->sender_set) { + json_object_string_add( + purge_json, "rec-from", + isis_format_id(poi->sender, + sizeof(poi->sender))); + } + } else { + sbuf_push(buf, indent, "Purge Originator Identification:\n"); + sbuf_push( + buf, indent, " Generator: %s\n", + isis_format_id(poi->generator, sizeof(poi->generator))); + if (poi->sender_set) { + sbuf_push(buf, indent, " Received-From: %s\n", + isis_format_id(poi->sender, + sizeof(poi->sender))); + } } } @@ -3417,12 +4055,12 @@ static void copy_items(enum isis_tlv_context context, enum isis_tlv_type type, static void format_item(uint16_t mtid, enum isis_tlv_context context, enum isis_tlv_type type, struct isis_item *i, - struct sbuf *buf, int indent) + struct sbuf *buf, struct json_object *json, int indent) { const struct tlv_ops *ops = tlv_table[context][type]; if (ops && ops->format_item) { - ops->format_item(mtid, i, buf, indent); + ops->format_item(mtid, i, buf, json, indent); return; } @@ -3431,12 +4069,13 @@ static void format_item(uint16_t mtid, enum isis_tlv_context context, static void format_items_(uint16_t mtid, enum isis_tlv_context context, enum isis_tlv_type type, struct isis_item_list *items, - struct sbuf *buf, int indent) + struct sbuf *buf, struct json_object *json, + int indent) { struct isis_item *i; for (i = items->head; i; i = i->next) - format_item(mtid, context, type, i, buf, indent); + format_item(mtid, context, type, i, buf, json, indent); } static void free_item(enum isis_tlv_context tlv_context, @@ -3765,12 +4404,12 @@ static void free_mt_items(enum isis_tlv_context context, static void format_mt_items(enum isis_tlv_context context, enum isis_tlv_type type, struct isis_mt_item_list *m, struct sbuf *buf, - int indent) + struct json_object *json, int indent) { struct isis_item_list *n; RB_FOREACH (n, isis_mt_item_list, m) { - format_items_(n->mtid, context, type, n, buf, indent); + format_items_(n->mtid, context, type, n, buf, json, indent); } } @@ -3917,87 +4556,100 @@ struct isis_tlvs *isis_copy_tlvs(struct isis_tlvs *tlvs) return rv; } -static void format_tlvs(struct isis_tlvs *tlvs, struct sbuf *buf, int indent) +static void format_tlvs(struct isis_tlvs *tlvs, struct sbuf *buf, struct json_object *json, int indent) { - format_tlv_protocols_supported(&tlvs->protocols_supported, buf, indent); + format_tlv_protocols_supported(&tlvs->protocols_supported, buf, json, + indent); format_items(ISIS_CONTEXT_LSP, ISIS_TLV_AUTH, &tlvs->isis_auth, buf, - indent); + json, indent); - format_tlv_purge_originator(tlvs->purge_originator, buf, indent); + format_tlv_purge_originator(tlvs->purge_originator, buf, json, indent); format_items(ISIS_CONTEXT_LSP, ISIS_TLV_AREA_ADDRESSES, - &tlvs->area_addresses, buf, indent); + &tlvs->area_addresses, buf, json, indent); if (tlvs->mt_router_info_empty) { - sbuf_push(buf, indent, "MT Router Info: None\n"); + if (json) + json_object_string_add(json, "mt-router-info", "none"); + else + sbuf_push(buf, indent, "MT Router Info: None\n"); } else { format_items(ISIS_CONTEXT_LSP, ISIS_TLV_MT_ROUTER_INFO, - &tlvs->mt_router_info, buf, indent); + &tlvs->mt_router_info, buf, json, indent); } format_items(ISIS_CONTEXT_LSP, ISIS_TLV_OLDSTYLE_REACH, - &tlvs->oldstyle_reach, buf, indent); + &tlvs->oldstyle_reach, buf, json, indent); format_items(ISIS_CONTEXT_LSP, ISIS_TLV_LAN_NEIGHBORS, - &tlvs->lan_neighbor, buf, indent); + &tlvs->lan_neighbor, buf, json, indent); format_items(ISIS_CONTEXT_LSP, ISIS_TLV_LSP_ENTRY, &tlvs->lsp_entries, - buf, indent); - - format_tlv_dynamic_hostname(tlvs->hostname, buf, indent); - format_tlv_te_router_id(tlvs->te_router_id, buf, indent); - format_tlv_te_router_id_ipv6(tlvs->te_router_id_ipv6, buf, indent); - format_tlv_router_cap(tlvs->router_cap, buf, indent); + buf, json, indent); + + format_tlv_dynamic_hostname(tlvs->hostname, buf, json, indent); + format_tlv_te_router_id(tlvs->te_router_id, buf, json, indent); + format_tlv_te_router_id_ipv6(tlvs->te_router_id_ipv6, buf, json, + indent); + if (json) + format_tlv_router_cap_json(tlvs->router_cap, json); + else + format_tlv_router_cap(tlvs->router_cap, buf, indent); format_items(ISIS_CONTEXT_LSP, ISIS_TLV_EXTENDED_REACH, - &tlvs->extended_reach, buf, indent); + &tlvs->extended_reach, buf, json, indent); format_mt_items(ISIS_CONTEXT_LSP, ISIS_TLV_MT_REACH, &tlvs->mt_reach, - buf, indent); + buf, json, indent); format_items(ISIS_CONTEXT_LSP, ISIS_TLV_OLDSTYLE_IP_REACH, - &tlvs->oldstyle_ip_reach, buf, indent); + &tlvs->oldstyle_ip_reach, buf, json, indent); format_items(ISIS_CONTEXT_LSP, ISIS_TLV_OLDSTYLE_IP_REACH_EXT, - &tlvs->oldstyle_ip_reach_ext, buf, indent); + &tlvs->oldstyle_ip_reach_ext, buf, json, indent); format_items(ISIS_CONTEXT_LSP, ISIS_TLV_IPV4_ADDRESS, - &tlvs->ipv4_address, buf, indent); + &tlvs->ipv4_address, buf, json, indent); format_items(ISIS_CONTEXT_LSP, ISIS_TLV_IPV6_ADDRESS, - &tlvs->ipv6_address, buf, indent); + &tlvs->ipv6_address, buf, json, indent); format_items(ISIS_CONTEXT_LSP, ISIS_TLV_GLOBAL_IPV6_ADDRESS, - &tlvs->global_ipv6_address, buf, indent); + &tlvs->global_ipv6_address, buf, json, indent); format_items(ISIS_CONTEXT_LSP, ISIS_TLV_EXTENDED_IP_REACH, - &tlvs->extended_ip_reach, buf, indent); + &tlvs->extended_ip_reach, buf, json, indent); format_mt_items(ISIS_CONTEXT_LSP, ISIS_TLV_MT_IP_REACH, - &tlvs->mt_ip_reach, buf, indent); + &tlvs->mt_ip_reach, buf, json, indent); format_items(ISIS_CONTEXT_LSP, ISIS_TLV_IPV6_REACH, &tlvs->ipv6_reach, - buf, indent); + buf, json, indent); format_mt_items(ISIS_CONTEXT_LSP, ISIS_TLV_MT_IPV6_REACH, - &tlvs->mt_ipv6_reach, buf, indent); + &tlvs->mt_ipv6_reach, buf, json, indent); - format_tlv_threeway_adj(tlvs->threeway_adj, buf, indent); + format_tlv_threeway_adj(tlvs->threeway_adj, buf, json, indent); - format_tlv_spine_leaf(tlvs->spine_leaf, buf, indent); + format_tlv_spine_leaf(tlvs->spine_leaf, buf, json, indent); } -const char *isis_format_tlvs(struct isis_tlvs *tlvs) +const char *isis_format_tlvs(struct isis_tlvs *tlvs, struct json_object *json) { - static struct sbuf buf; + if (json) { + format_tlvs(tlvs, NULL, json, 0); + return NULL; + } else { + static struct sbuf buf; - if (!sbuf_buf(&buf)) - sbuf_init(&buf, NULL, 0); + if (!sbuf_buf(&buf)) + sbuf_init(&buf, NULL, 0); - sbuf_reset(&buf); - format_tlvs(tlvs, &buf, 0); - return sbuf_buf(&buf); + sbuf_reset(&buf); + format_tlvs(tlvs, &buf, NULL, 0); + return sbuf_buf(&buf); + } } void isis_free_tlvs(struct isis_tlvs *tlvs) diff --git a/isisd/isis_tlvs.h b/isisd/isis_tlvs.h index 0c6ed11cb..364e38aba 100644 --- a/isisd/isis_tlvs.h +++ b/isisd/isis_tlvs.h @@ -549,7 +549,7 @@ void isis_free_tlvs(struct isis_tlvs *tlvs); struct isis_tlvs *isis_alloc_tlvs(void); int isis_unpack_tlvs(size_t avail_len, struct stream *stream, struct isis_tlvs **dest, const char **error_log); -const char *isis_format_tlvs(struct isis_tlvs *tlvs); +const char *isis_format_tlvs(struct isis_tlvs *tlvs, struct json_object *json); struct isis_tlvs *isis_copy_tlvs(struct isis_tlvs *tlvs); struct list *isis_fragment_tlvs(struct isis_tlvs *tlvs, size_t size); diff --git a/isisd/isisd.c b/isisd/isisd.c index 3fa2b7cc2..369b83396 100644 --- a/isisd/isisd.c +++ b/isisd/isisd.c @@ -109,12 +109,19 @@ DEFINE_HOOK(isis_hook_db_overload, (const struct isis_area *area), (area)); int isis_area_get(struct vty *, const char *); int area_net_title(struct vty *, const char *); int area_clear_net_title(struct vty *, const char *); -int show_isis_interface_common(struct vty *, const char *ifname, char, - const char *vrf_name, bool all_vrf); -int show_isis_neighbor_common(struct vty *, const char *id, char, - const char *vrf_name, bool all_vrf); -int clear_isis_neighbor_common(struct vty *, const char *id, const char *vrf_name, +int show_isis_interface_common(struct vty *, struct json_object *json, + const char *ifname, char, const char *vrf_name, bool all_vrf); +int show_isis_interface_common_vty(struct vty *, const char *ifname, char, + const char *vrf_name, bool all_vrf); +int show_isis_interface_common_json(struct json_object *json, + const char *ifname, char, + const char *vrf_name, bool all_vrf); +int show_isis_neighbor_common(struct vty *, struct json_object *json, + const char *id, char, const char *vrf_name, + bool all_vrf); +int clear_isis_neighbor_common(struct vty *, const char *id, + const char *vrf_name, bool all_vrf); /* Link ISIS instance to VRF. */ void isis_vrf_link(struct isis *isis, struct vrf *vrf) @@ -202,7 +209,7 @@ struct isis *isis_new(const char *vrf_name) /* * Default values */ - isis->max_area_addrs = 3; + isis->max_area_addrs = ISIS_DEFAULT_MAX_AREA_ADDRESSES; isis->process_id = getpid(); isis->router_id = 0; isis->area_list = list_new(); @@ -933,10 +940,125 @@ int area_clear_net_title(struct vty *vty, const char *net_title) /* * 'show isis interface' command */ - -int show_isis_interface_common(struct vty *vty, const char *ifname, char detail, +int show_isis_interface_common(struct vty *vty, struct json_object *json, + const char *ifname, char detail, const char *vrf_name, bool all_vrf) { + if (json) { + return show_isis_interface_common_json(json, ifname, detail, + vrf_name, all_vrf); + } else { + return show_isis_interface_common_vty(vty, ifname, detail, + vrf_name, all_vrf); + } +} + +int show_isis_interface_common_json(struct json_object *json, + const char *ifname, char detail, + const char *vrf_name, bool all_vrf) +{ + struct listnode *anode, *cnode, *inode; + struct isis_area *area; + struct isis_circuit *circuit; + struct isis *isis; + struct json_object *areas_json, *area_json; + struct json_object *circuits_json, *circuit_json; + if (!im) { + // IS-IS Routing Process not enabled + json_object_string_add(json, "is-is-routing-process-enabled", + "no"); + return CMD_SUCCESS; + } + if (vrf_name) { + if (all_vrf) { + for (ALL_LIST_ELEMENTS_RO(im->isis, inode, isis)) { + areas_json = json_object_new_array(); + json_object_object_add(json, "areas", + areas_json); + for (ALL_LIST_ELEMENTS_RO(isis->area_list, + anode, area)) { + area_json = json_object_new_object(); + json_object_string_add( + area_json, "area", + area->area_tag ? area->area_tag + : "null"); + circuits_json = json_object_new_array(); + json_object_object_add(area_json, + "circuits", + circuits_json); + for (ALL_LIST_ELEMENTS_RO( + area->circuit_list, cnode, + circuit)) { + circuit_json = + json_object_new_object(); + json_object_int_add( + circuit_json, "circuit", + circuit->circuit_id); + if (!ifname) + isis_circuit_print_json( + circuit, + circuit_json, + detail); + else if (strcmp(circuit->interface->name, ifname) == 0) + isis_circuit_print_json( + circuit, + circuit_json, + detail); + json_object_array_add( + circuits_json, + circuit_json); + } + json_object_array_add(areas_json, + area_json); + } + } + return CMD_SUCCESS; + } + isis = isis_lookup_by_vrfname(vrf_name); + if (isis != NULL) { + areas_json = json_object_new_array(); + json_object_object_add(json, "areas", areas_json); + for (ALL_LIST_ELEMENTS_RO(isis->area_list, anode, + area)) { + area_json = json_object_new_object(); + json_object_string_add(area_json, "area", + area->area_tag + ? area->area_tag + : "null"); + + circuits_json = json_object_new_array(); + json_object_object_add(area_json, "circuits", + circuits_json); + for (ALL_LIST_ELEMENTS_RO(area->circuit_list, + cnode, circuit)) { + circuit_json = json_object_new_object(); + json_object_int_add( + circuit_json, "circuit", + circuit->circuit_id); + if (!ifname) + isis_circuit_print_json( + circuit, circuit_json, + detail); + else if ( + strcmp(circuit->interface->name, + ifname) == 0) + isis_circuit_print_json( + circuit, circuit_json, + detail); + json_object_array_add(circuits_json, + circuit_json); + } + json_object_array_add(areas_json, area_json); + } + } + } + return CMD_SUCCESS; +} + +int show_isis_interface_common_vty(struct vty *vty, const char *ifname, + char detail, const char *vrf_name, + bool all_vrf) +{ struct listnode *anode, *cnode, *inode; struct isis_area *area; struct isis_circuit *circuit; @@ -990,8 +1112,7 @@ int show_isis_interface_common(struct vty *vty, const char *ifname, char detail, circuit, vty, detail); else if ( strcmp(circuit->interface->name, - ifname) - == 0) + ifname) == 0) isis_circuit_print_vty( circuit, vty, detail); } @@ -1003,63 +1124,90 @@ int show_isis_interface_common(struct vty *vty, const char *ifname, char detail, DEFUN(show_isis_interface, show_isis_interface_cmd, - "show " PROTO_NAME " [vrf <NAME|all>] interface", + "show " PROTO_NAME " [vrf <NAME|all>] interface [json]", SHOW_STR PROTO_HELP VRF_CMD_HELP_STR "All VRFs\n" + "json output\n" "IS-IS interface\n") { + int res = CMD_SUCCESS; const char *vrf_name = VRF_DEFAULT_NAME; bool all_vrf = false; int idx_vrf = 0; + bool uj = use_json(argc, argv); + json_object *json = NULL; ISIS_FIND_VRF_ARGS(argv, argc, idx_vrf, vrf_name, all_vrf); - return show_isis_interface_common(vty, NULL, ISIS_UI_LEVEL_BRIEF, - vrf_name, all_vrf); + if (uj) + json = json_object_new_object(); + res = show_isis_interface_common(vty, json, NULL, ISIS_UI_LEVEL_BRIEF, + vrf_name, all_vrf); + if (uj) + vty_json(vty, json); + return res; } DEFUN(show_isis_interface_detail, show_isis_interface_detail_cmd, - "show " PROTO_NAME " [vrf <NAME|all>] interface detail", + "show " PROTO_NAME " [vrf <NAME|all>] interface detail [json]", SHOW_STR PROTO_HELP VRF_CMD_HELP_STR "All VRFs\n" "IS-IS interface\n" - "show detailed information\n") + "show detailed information\n" + "json output\n") { + int res = CMD_SUCCESS; const char *vrf_name = VRF_DEFAULT_NAME; bool all_vrf = false; int idx_vrf = 0; + bool uj = use_json(argc, argv); + json_object *json = NULL; ISIS_FIND_VRF_ARGS(argv, argc, idx_vrf, vrf_name, all_vrf); - return show_isis_interface_common(vty, NULL, ISIS_UI_LEVEL_DETAIL, - vrf_name, all_vrf); + if (uj) + json = json_object_new_object(); + res = show_isis_interface_common(vty, json, NULL, ISIS_UI_LEVEL_DETAIL, + vrf_name, all_vrf); + if (uj) + vty_json(vty, json); + return res; } DEFUN(show_isis_interface_arg, show_isis_interface_arg_cmd, - "show " PROTO_NAME " [vrf <NAME|all>] interface WORD", + "show " PROTO_NAME " [vrf <NAME|all>] interface WORD [json]", SHOW_STR PROTO_HELP VRF_CMD_HELP_STR "All VRFs\n" "IS-IS interface\n" - "IS-IS interface name\n") + "IS-IS interface name\n" + "json output\n") { + int res = CMD_SUCCESS; int idx_word = 0; const char *vrf_name = VRF_DEFAULT_NAME; bool all_vrf = false; int idx_vrf = 0; + bool uj = use_json(argc, argv); + json_object *json = NULL; ISIS_FIND_VRF_ARGS(argv, argc, idx_vrf, vrf_name, all_vrf); + if (uj) + json = json_object_new_object(); char *ifname = argv_find(argv, argc, "WORD", &idx_word) ? argv[idx_word]->arg : NULL; - return show_isis_interface_common(vty, ifname, ISIS_UI_LEVEL_DETAIL, - vrf_name, all_vrf); + res = show_isis_interface_common( + vty, json, ifname, ISIS_UI_LEVEL_DETAIL, vrf_name, all_vrf); + if (uj) + vty_json(vty, json); + return res; } static int id_to_sysid(struct isis *isis, const char *id, uint8_t *sysid) @@ -1079,8 +1227,65 @@ static int id_to_sysid(struct isis *isis, const char *id, uint8_t *sysid) return 0; } -static void isis_neighbor_common(struct vty *vty, const char *id, char detail, - struct isis *isis, uint8_t *sysid) +static void isis_neighbor_common_json(struct json_object *json, const char *id, + char detail, struct isis *isis, + uint8_t *sysid) +{ + struct listnode *anode, *cnode, *node; + struct isis_area *area; + struct isis_circuit *circuit; + struct list *adjdb; + struct isis_adjacency *adj; + struct json_object *areas_json, *area_json; + struct json_object *circuits_json, *circuit_json; + int i; + + areas_json = json_object_new_array(); + json_object_object_add(json, "areas", areas_json); + for (ALL_LIST_ELEMENTS_RO(isis->area_list, anode, area)) { + area_json = json_object_new_object(); + json_object_string_add(area_json, "area", + area->area_tag ? area->area_tag + : "null"); + circuits_json = json_object_new_array(); + json_object_object_add(area_json, "circuits", circuits_json); + for (ALL_LIST_ELEMENTS_RO(area->circuit_list, cnode, circuit)) { + circuit_json = json_object_new_object(); + json_object_int_add(circuit_json, "circuit", + circuit->circuit_id); + if (circuit->circ_type == CIRCUIT_T_BROADCAST) { + for (i = 0; i < 2; i++) { + adjdb = circuit->u.bc.adjdb[i]; + if (adjdb && adjdb->count) { + for (ALL_LIST_ELEMENTS_RO( + adjdb, node, adj)) + if (!id || + !memcmp(adj->sysid, + sysid, + ISIS_SYS_ID_LEN)) + isis_adj_print_json( + adj, + circuit_json, + detail); + } + } + } else if (circuit->circ_type == CIRCUIT_T_P2P && + circuit->u.p2p.neighbor) { + adj = circuit->u.p2p.neighbor; + if (!id || + !memcmp(adj->sysid, sysid, ISIS_SYS_ID_LEN)) + isis_adj_print_json(adj, circuit_json, + detail); + } + json_object_array_add(circuits_json, circuit_json); + } + json_object_array_add(areas_json, area_json); + } +} + +static void isis_neighbor_common_vty(struct vty *vty, const char *id, + char detail, struct isis *isis, + uint8_t *sysid) { struct listnode *anode, *cnode, *node; struct isis_area *area; @@ -1103,9 +1308,8 @@ static void isis_neighbor_common(struct vty *vty, const char *id, char detail, if (adjdb && adjdb->count) { for (ALL_LIST_ELEMENTS_RO( adjdb, node, adj)) - if (!id - || !memcmp( - adj->sysid, + if (!id || + !memcmp(adj->sysid, sysid, ISIS_SYS_ID_LEN)) isis_adj_print_vty( @@ -1114,24 +1318,35 @@ static void isis_neighbor_common(struct vty *vty, const char *id, char detail, detail); } } - } else if (circuit->circ_type == CIRCUIT_T_P2P - && circuit->u.p2p.neighbor) { + } else if (circuit->circ_type == CIRCUIT_T_P2P && + circuit->u.p2p.neighbor) { adj = circuit->u.p2p.neighbor; - if (!id - || !memcmp(adj->sysid, sysid, - ISIS_SYS_ID_LEN)) + if (!id || + !memcmp(adj->sysid, sysid, ISIS_SYS_ID_LEN)) isis_adj_print_vty(adj, vty, detail); } } } +} +static void isis_neighbor_common(struct vty *vty, struct json_object *json, + const char *id, char detail, struct isis *isis, + uint8_t *sysid) +{ + if (json) { + isis_neighbor_common_json(json, id, detail,isis,sysid); + } else { + isis_neighbor_common_vty(vty, id, detail,isis,sysid); + } } + /* * 'show isis neighbor' command */ -int show_isis_neighbor_common(struct vty *vty, const char *id, char detail, - const char *vrf_name, bool all_vrf) +int show_isis_neighbor_common(struct vty *vty, struct json_object *json, + const char *id, char detail, const char *vrf_name, + bool all_vrf) { struct listnode *node; uint8_t sysid[ISIS_SYS_ID_LEN]; @@ -1150,8 +1365,8 @@ int show_isis_neighbor_common(struct vty *vty, const char *id, char detail, id); return CMD_SUCCESS; } - isis_neighbor_common(vty, id, detail, isis, - sysid); + isis_neighbor_common(vty, json, id, detail, + isis, sysid); } return CMD_SUCCESS; } @@ -1161,7 +1376,8 @@ int show_isis_neighbor_common(struct vty *vty, const char *id, char detail, vty_out(vty, "Invalid system id %s\n", id); return CMD_SUCCESS; } - isis_neighbor_common(vty, id, detail, isis, sysid); + isis_neighbor_common(vty, json, id, detail, isis, + sysid); } } @@ -1254,64 +1470,91 @@ int clear_isis_neighbor_common(struct vty *vty, const char *id, const char *vrf_ DEFUN(show_isis_neighbor, show_isis_neighbor_cmd, - "show " PROTO_NAME " [vrf <NAME|all>] neighbor", + "show " PROTO_NAME " [vrf <NAME|all>] neighbor [json]", SHOW_STR PROTO_HELP VRF_CMD_HELP_STR "All vrfs\n" - "IS-IS neighbor adjacencies\n") + "IS-IS neighbor adjacencies\n" + "json output\n") { + int res = CMD_SUCCESS; const char *vrf_name = VRF_DEFAULT_NAME; bool all_vrf = false; int idx_vrf = 0; + bool uj = use_json(argc, argv); + json_object *json = NULL; ISIS_FIND_VRF_ARGS(argv, argc, idx_vrf, vrf_name, all_vrf); - return show_isis_neighbor_common(vty, NULL, ISIS_UI_LEVEL_BRIEF, - vrf_name, all_vrf); + if (uj) + json = json_object_new_object(); + res = show_isis_neighbor_common(vty, json, NULL, ISIS_UI_LEVEL_BRIEF, + vrf_name, all_vrf); + if (uj) + vty_json(vty, json); + return res; } DEFUN(show_isis_neighbor_detail, show_isis_neighbor_detail_cmd, - "show " PROTO_NAME " [vrf <NAME|all>] neighbor detail", + "show " PROTO_NAME " [vrf <NAME|all>] neighbor detail [json]", SHOW_STR PROTO_HELP VRF_CMD_HELP_STR "all vrfs\n" "IS-IS neighbor adjacencies\n" - "show detailed information\n") + "show detailed information\n" + "json output\n") { + int res = CMD_SUCCESS; const char *vrf_name = VRF_DEFAULT_NAME; bool all_vrf = false; int idx_vrf = 0; + bool uj = use_json(argc, argv); + json_object *json = NULL; ISIS_FIND_VRF_ARGS(argv, argc, idx_vrf, vrf_name, all_vrf); + if (uj) + json = json_object_new_object(); - return show_isis_neighbor_common(vty, NULL, ISIS_UI_LEVEL_DETAIL, - vrf_name, all_vrf); + res = show_isis_neighbor_common(vty, json, NULL, ISIS_UI_LEVEL_DETAIL, + vrf_name, all_vrf); + if (uj) + vty_json(vty, json); + return res; } DEFUN(show_isis_neighbor_arg, show_isis_neighbor_arg_cmd, - "show " PROTO_NAME " [vrf <NAME|all>] neighbor WORD", + "show " PROTO_NAME " [vrf <NAME|all>] neighbor WORD [json]", SHOW_STR PROTO_HELP VRF_CMD_HELP_STR "All vrfs\n" "IS-IS neighbor adjacencies\n" - "System id\n") + "System id\n" + "json output\n") { + int res = CMD_SUCCESS; int idx_word = 0; const char *vrf_name = VRF_DEFAULT_NAME; bool all_vrf = false; int idx_vrf = 0; + bool uj = use_json(argc, argv); + json_object *json = NULL; ISIS_FIND_VRF_ARGS(argv, argc, idx_vrf, vrf_name, all_vrf); + if (uj) + json = json_object_new_object(); char *id = argv_find(argv, argc, "WORD", &idx_word) ? argv[idx_word]->arg : NULL; - return show_isis_neighbor_common(vty, id, ISIS_UI_LEVEL_DETAIL, - vrf_name, all_vrf); + res = show_isis_neighbor_common(vty, json, id, ISIS_UI_LEVEL_DETAIL, + vrf_name, all_vrf); + if (uj) + vty_json(vty, json); + return res; } DEFUN(clear_isis_neighbor, @@ -2056,7 +2299,152 @@ DEFUN(show_isis_spf_ietf, show_isis_spf_ietf_cmd, return CMD_SUCCESS; } -static void common_isis_summary(struct vty *vty, struct isis *isis) + +static const char *pdu_counter_index_to_name_json(enum pdu_counter_index index) +{ + switch (index) { + case L1_LAN_HELLO_INDEX: + return "l1-iih"; + case L2_LAN_HELLO_INDEX: + return "l2-iih"; + case P2P_HELLO_INDEX: + return "p2p-iih"; + case L1_LINK_STATE_INDEX: + return "l1-lsp"; + case L2_LINK_STATE_INDEX: + return "l2-lsp"; + case FS_LINK_STATE_INDEX: + return "fs-lsp"; + case L1_COMPLETE_SEQ_NUM_INDEX: + return "l1-csnp"; + case L2_COMPLETE_SEQ_NUM_INDEX: + return "l2-csnp"; + case L1_PARTIAL_SEQ_NUM_INDEX: + return "l1-psnp"; + case L2_PARTIAL_SEQ_NUM_INDEX: + return "l2-psnp"; + default: + return "???????"; + } +} + +static void common_isis_summary_json(struct json_object *json, + struct isis *isis) +{ + int level; + json_object *areas_json, *area_json, *tx_pdu_json, *rx_pdu_json, + *levels_json, *level_json; + struct listnode *node, *node2; + struct isis_area *area; + time_t cur; + char uptime[MONOTIME_STRLEN]; + char stier[5]; + json_object_string_add(json, "vrf", isis->name); + json_object_int_add(json, "process-id", isis->process_id); + if (isis->sysid_set) + json_object_string_add(json, "system-id", + sysid_print(isis->sysid)); + + cur = time(NULL); + cur -= isis->uptime; + frrtime_to_interval(cur, uptime, sizeof(uptime)); + json_object_string_add(json, "up-time", uptime); + if (isis->area_list) + json_object_int_add(json, "number-areas", + isis->area_list->count); + areas_json = json_object_new_array(); + json_object_object_add(json, "areas", areas_json); + for (ALL_LIST_ELEMENTS_RO(isis->area_list, node, area)) { + area_json = json_object_new_object(); + json_object_string_add(area_json, "area", + area->area_tag ? area->area_tag + : "null"); + + + if (fabricd) { + uint8_t tier = fabricd_tier(area); + snprintfrr(stier, sizeof(stier), "%s", &tier); + json_object_string_add(area_json, "tier", + tier == ISIS_TIER_UNDEFINED + ? "undefined" + : stier); + } + + if (listcount(area->area_addrs) > 0) { + struct area_addr *area_addr; + for (ALL_LIST_ELEMENTS_RO(area->area_addrs, node2, + area_addr)) { + json_object_string_add( + area_json, "net", + isonet_print(area_addr->area_addr, + area_addr->addr_len + + ISIS_SYS_ID_LEN + + 1)); + } + } + + tx_pdu_json = json_object_new_object(); + json_object_object_add(area_json, "tx-pdu-type", tx_pdu_json); + for (int i = 0; i < PDU_COUNTER_SIZE; i++) { + if (!area->pdu_tx_counters[i]) + continue; + json_object_int_add(tx_pdu_json, + pdu_counter_index_to_name_json(i), + area->pdu_tx_counters[i]); + } + json_object_int_add(tx_pdu_json, "lsp-rxmt", + area->lsp_rxmt_count); + + rx_pdu_json = json_object_new_object(); + json_object_object_add(area_json, "rx-pdu-type", rx_pdu_json); + for (int i = 0; i < PDU_COUNTER_SIZE; i++) { + if (!area->pdu_rx_counters[i]) + continue; + json_object_int_add(rx_pdu_json, + pdu_counter_index_to_name_json(i), + area->pdu_rx_counters[i]); + } + + levels_json = json_object_new_array(); + json_object_object_add(area_json, "levels", levels_json); + for (level = ISIS_LEVEL1; level <= ISIS_LEVELS; level++) { + if ((area->is_type & level) == 0) + continue; + level_json = json_object_new_object(); + json_object_int_add(level_json, "id", level); + json_object_int_add(level_json, "lsp0-regenerated", + area->lsp_gen_count[level - 1]); + json_object_int_add(level_json, "lsp-purged", + area->lsp_purge_count[level - 1]); + if (area->spf_timer[level - 1]) + json_object_string_add(level_json, "spf", + "pending"); + else + json_object_string_add(level_json, "spf", + "no pending"); + json_object_int_add(level_json, "minimum-interval", + area->min_spf_interval[level - 1]); + if (area->spf_delay_ietf[level - 1]) + json_object_string_add( + level_json, "ietf-spf-delay-activated", + "not used"); + if (area->ip_circuits) { + isis_spf_print_json( + area->spftree[SPFTREE_IPV4][level - 1], + level_json); + } + if (area->ipv6_circuits) { + isis_spf_print_json( + area->spftree[SPFTREE_IPV6][level - 1], + level_json); + } + json_object_array_add(levels_json, level_json); + } + json_object_array_add(areas_json, area_json); + } +} + +static void common_isis_summary_vty(struct vty *vty, struct isis *isis) { struct listnode *node, *node2; struct isis_area *area; @@ -2156,10 +2544,21 @@ static void common_isis_summary(struct vty *vty, struct isis *isis) } } +static void common_isis_summary(struct vty *vty, struct json_object *json, + struct isis *isis) +{ + if (json) { + common_isis_summary_json(json, isis); + } else { + common_isis_summary_vty(vty, isis); + } +} + DEFUN(show_isis_summary, show_isis_summary_cmd, - "show " PROTO_NAME " [vrf <NAME|all>] summary", + "show " PROTO_NAME " [vrf <NAME|all>] summary [json]", SHOW_STR PROTO_HELP VRF_CMD_HELP_STR "All VRFs\n" + "json output\n" "summary\n") { struct listnode *node; @@ -2167,25 +2566,30 @@ DEFUN(show_isis_summary, show_isis_summary_cmd, struct isis *isis; const char *vrf_name = VRF_DEFAULT_NAME; bool all_vrf = false; + bool uj = use_json(argc, argv); + json_object *json = NULL; ISIS_FIND_VRF_ARGS(argv, argc, idx_vrf, vrf_name, all_vrf) if (!im) { vty_out(vty, PROTO_NAME " is not running\n"); return CMD_SUCCESS; } + if (uj) + json = json_object_new_object(); if (vrf_name) { if (all_vrf) { for (ALL_LIST_ELEMENTS_RO(im->isis, node, isis)) - common_isis_summary(vty, isis); + common_isis_summary(vty, json, isis); return CMD_SUCCESS; } isis = isis_lookup_by_vrfname(vrf_name); if (isis != NULL) - common_isis_summary(vty, isis); + common_isis_summary(vty, json, isis); } - vty_out(vty, "\n"); + if (uj) + vty_json(vty, json); return CMD_SUCCESS; } @@ -2250,9 +2654,40 @@ struct isis_lsp *lsp_for_sysid(struct lspdb_head *head, const char *sysid_str, return lsp; } -void show_isis_database_lspdb(struct vty *vty, struct isis_area *area, - int level, struct lspdb_head *lspdb, - const char *sysid_str, int ui_level) +void show_isis_database_lspdb_json(struct json_object *json, + struct isis_area *area, int level, + struct lspdb_head *lspdb, + const char *sysid_str, int ui_level) +{ + struct isis_lsp *lsp; + int lsp_count; + + if (lspdb_count(lspdb) > 0) { + lsp = lsp_for_sysid(lspdb, sysid_str, area->isis); + + if (lsp != NULL || sysid_str == NULL) { + json_object_int_add(json, "id", level + 1); + } + + if (lsp) { + if (ui_level == ISIS_UI_LEVEL_DETAIL) + lsp_print_detail(lsp, NULL, json, + area->dynhostname, area->isis); + else + lsp_print_json(lsp, json, area->dynhostname, + area->isis); + } else if (sysid_str == NULL) { + lsp_count = + lsp_print_all(NULL, json, lspdb, ui_level, + area->dynhostname, area->isis); + + json_object_int_add(json, "count", lsp_count); + } + } +} +void show_isis_database_lspdb_vty(struct vty *vty, struct isis_area *area, + int level, struct lspdb_head *lspdb, + const char *sysid_str, int ui_level) { struct isis_lsp *lsp; int lsp_count; @@ -2271,14 +2706,14 @@ void show_isis_database_lspdb(struct vty *vty, struct isis_area *area, if (lsp) { if (ui_level == ISIS_UI_LEVEL_DETAIL) - lsp_print_detail(lsp, vty, area->dynhostname, - area->isis); + lsp_print_detail(lsp, vty, NULL, + area->dynhostname, area->isis); else - lsp_print(lsp, vty, area->dynhostname, - area->isis); + lsp_print_vty(lsp, vty, area->dynhostname, + area->isis); } else if (sysid_str == NULL) { lsp_count = - lsp_print_all(vty, lspdb, ui_level, + lsp_print_all(vty, NULL, lspdb, ui_level, area->dynhostname, area->isis); vty_out(vty, " %u LSPs\n\n", lsp_count); @@ -2286,7 +2721,43 @@ void show_isis_database_lspdb(struct vty *vty, struct isis_area *area, } } -static void show_isis_database_common(struct vty *vty, const char *sysid_str, +static void show_isis_database_json(struct json_object *json, const char *sysid_str, + int ui_level, struct isis *isis) +{ + struct listnode *node; + struct isis_area *area; + int level; + struct json_object *tag_area_json,*area_json, *lsp_json, *area_arr_json, *arr_json; + uint8_t area_cnt = 0; + + if (isis->area_list->count == 0) + return; + + area_arr_json = json_object_new_array(); + json_object_object_add(json, "areas", area_arr_json); + for (ALL_LIST_ELEMENTS_RO(isis->area_list, node, area)) { + area_json = json_object_new_object(); + tag_area_json = json_object_new_object(); + json_object_string_add(tag_area_json, "name", + area->area_tag ? area->area_tag + : "null"); + + arr_json = json_object_new_array(); + json_object_object_add(area_json,"area",tag_area_json); + json_object_object_add(area_json,"levels",arr_json); + for (level = 0; level < ISIS_LEVELS; level++) { + lsp_json = json_object_new_object(); + show_isis_database_lspdb_json(lsp_json, area, level, + &area->lspdb[level], + sysid_str, ui_level); + json_object_array_add(arr_json, lsp_json); + } + json_object_array_add(area_arr_json, area_json); + area_cnt++; + } +} + +static void show_isis_database_vty(struct vty *vty, const char *sysid_str, int ui_level, struct isis *isis) { struct listnode *node; @@ -2301,11 +2772,22 @@ static void show_isis_database_common(struct vty *vty, const char *sysid_str, area->area_tag ? area->area_tag : "null"); for (level = 0; level < ISIS_LEVELS; level++) - show_isis_database_lspdb(vty, area, level, + show_isis_database_lspdb_vty(vty, area, level, &area->lspdb[level], sysid_str, ui_level); } } + +static void show_isis_database_common(struct vty *vty, struct json_object *json, const char *sysid_str, + int ui_level, struct isis *isis) +{ + if (json) { + show_isis_database_json(json, sysid_str, ui_level, isis); + } else { + show_isis_database_vty(vty, sysid_str, ui_level, isis); + } +} + /* * This function supports following display options: * [ show isis database [detail] ] @@ -2322,7 +2804,7 @@ static void show_isis_database_common(struct vty *vty, const char *sysid_str, * [ show isis database detail <sysid>.<pseudo-id>-<fragment-number> ] * [ show isis database detail <hostname>.<pseudo-id>-<fragment-number> ] */ -static int show_isis_database(struct vty *vty, const char *sysid_str, +static int show_isis_database(struct vty *vty, struct json_object *json, const char *sysid_str, int ui_level, const char *vrf_name, bool all_vrf) { struct listnode *node; @@ -2331,28 +2813,30 @@ static int show_isis_database(struct vty *vty, const char *sysid_str, if (vrf_name) { if (all_vrf) { for (ALL_LIST_ELEMENTS_RO(im->isis, node, isis)) - show_isis_database_common(vty, sysid_str, + show_isis_database_common(vty, json, sysid_str, ui_level, isis); return CMD_SUCCESS; } isis = isis_lookup_by_vrfname(vrf_name); if (isis) - show_isis_database_common(vty, sysid_str, ui_level, - isis); + show_isis_database_common(vty, json, sysid_str, + ui_level, isis); } return CMD_SUCCESS; } DEFUN(show_database, show_database_cmd, - "show " PROTO_NAME " [vrf <NAME|all>] database [detail] [WORD]", + "show " PROTO_NAME " [vrf <NAME|all>] database [detail] [WORD] [json]", SHOW_STR PROTO_HELP VRF_CMD_HELP_STR "All VRFs\n" "Link state database\n" "Detailed information\n" - "LSP ID\n") + "LSP ID\n" + "json output\n") { + int res = CMD_SUCCESS; int idx = 0; int idx_vrf = 0; const char *vrf_name = VRF_DEFAULT_NAME; @@ -2361,8 +2845,17 @@ DEFUN(show_database, show_database_cmd, ? ISIS_UI_LEVEL_DETAIL : ISIS_UI_LEVEL_BRIEF; char *id = argv_find(argv, argc, "WORD", &idx) ? argv[idx]->arg : NULL; + bool uj = use_json(argc, argv); + json_object *json = NULL; + ISIS_FIND_VRF_ARGS(argv, argc, idx_vrf, vrf_name, all_vrf); - return show_isis_database(vty, id, uilevel, vrf_name, all_vrf); + if (uj) + json = json_object_new_object(); + + res = show_isis_database(vty, json, id, uilevel, vrf_name, all_vrf); + if (uj) + vty_json(vty, json); + return res; } #ifdef FABRICD diff --git a/isisd/isisd.h b/isisd/isisd.h index 7f8474a5f..c313fd9ef 100644 --- a/isisd/isisd.h +++ b/isisd/isisd.h @@ -89,6 +89,8 @@ struct isis_master { }; #define F_ISIS_UNIT_TEST 0x01 +#define ISIS_DEFAULT_MAX_AREA_ADDRESSES 3 + struct isis { vrf_id_t vrf_id; char *name; @@ -305,9 +307,13 @@ int isis_area_passwd_cleartext_set(struct isis_area *area, int level, const char *passwd, uint8_t snp_auth); int isis_area_passwd_hmac_md5_set(struct isis_area *area, int level, const char *passwd, uint8_t snp_auth); -void show_isis_database_lspdb(struct vty *vty, struct isis_area *area, - int level, struct lspdb_head *lspdb, - const char *argv, int ui_level); +void show_isis_database_lspdb_json(struct json_object *json, + struct isis_area *area, int level, + struct lspdb_head *lspdb, const char *argv, + int ui_level); +void show_isis_database_lspdb_vty(struct vty *vty, struct isis_area *area, + int level, struct lspdb_head *lspdb, + const char *argv, int ui_level); /* YANG paths */ #define ISIS_INSTANCE "/frr-isisd:isis/instance" diff --git a/tests/isisd/test_fuzz_isis_tlv.c b/tests/isisd/test_fuzz_isis_tlv.c index 97aade657..8f0b92d0f 100644 --- a/tests/isisd/test_fuzz_isis_tlv.c +++ b/tests/isisd/test_fuzz_isis_tlv.c @@ -108,12 +108,12 @@ static int test(FILE *input, FILE *output) } fprintf(output, "Unpack log:\n%s", log); - const char *s_tlvs = isis_format_tlvs(tlvs); + const char *s_tlvs = isis_format_tlvs(tlvs, NULL); fprintf(output, "Unpacked TLVs:\n%s", s_tlvs); struct isis_item *orig_auth = tlvs->isis_auth.head; tlvs->isis_auth.head = NULL; - s_tlvs = isis_format_tlvs(tlvs); + s_tlvs = isis_format_tlvs(tlvs, NULL); struct isis_tlvs *tlv_copy = isis_copy_tlvs(tlvs); tlvs->isis_auth.head = orig_auth; isis_free_tlvs(tlvs); @@ -133,7 +133,7 @@ static int test(FILE *input, FILE *output) } char *orig_tlvs = XSTRDUP(MTYPE_TMP, s_tlvs); - s_tlvs = isis_format_tlvs(tlvs); + s_tlvs = isis_format_tlvs(tlvs, NULL); if (strcmp(orig_tlvs, s_tlvs)) { fprintf(output, @@ -166,7 +166,7 @@ static int test(FILE *input, FILE *output) fprintf(output, "Could not pack fragment, too large.\n"); assert(0); } - sbuf_push(&fragment_format, 0, "%s", isis_format_tlvs(tlvs)); + sbuf_push(&fragment_format, 0, "%s", isis_format_tlvs(tlvs, NULL)); isis_free_tlvs(tlvs); } list_delete(&fragments); diff --git a/tests/isisd/test_isis_spf.c b/tests/isisd/test_isis_spf.c index a30f33cca..971aba4c4 100644 --- a/tests/isisd/test_isis_spf.c +++ b/tests/isisd/test_isis_spf.c @@ -294,7 +294,7 @@ static int test_run(struct vty *vty, const struct isis_topology *topology, /* Print the LDPDB. */ if (CHECK_FLAG(flags, F_DISPLAY_LSPDB)) - show_isis_database_lspdb(vty, area, level - 1, + show_isis_database_lspdb_vty(vty, area, level - 1, &area->lspdb[level - 1], NULL, ISIS_UI_LEVEL_DETAIL); diff --git a/tests/topotests/isis_topo1/test_isis_topo1.py b/tests/topotests/isis_topo1/test_isis_topo1.py index 94c5faf2e..014722387 100644 --- a/tests/topotests/isis_topo1/test_isis_topo1.py +++ b/tests/topotests/isis_topo1/test_isis_topo1.py @@ -236,6 +236,94 @@ def test_isis_linux_route6_installation(): assert topotest.json_cmp(actual, expected) is None, assertmsg +def test_isis_summary_json(): + "Check json struct in show isis summary json" + + tgen = get_topogen() + # Don't run this test if we have any failure. + if tgen.routers_have_failure(): + pytest.skip(tgen.errors) + + logger.info("Checking 'show isis summary json'") + for rname, router in tgen.routers().items(): + logger.info("Checking router %s", rname) + json_output = tgen.gears[rname].vtysh_cmd("show isis summary json", isjson=True) + assertmsg = "Test isis summary json failed in '{}' data '{}'".format(rname, json_output) + assert json_output['vrf'] == "default", assertmsg + assert json_output['areas'][0]['area'] == "1", assertmsg + assert json_output['areas'][0]['levels'][0]['id'] != '3', assertmsg + + +def test_isis_interface_json(): + "Check json struct in show isis interface json" + + tgen = get_topogen() + # Don't run this test if we have any failure. + if tgen.routers_have_failure(): + pytest.skip(tgen.errors) + + logger.info("Checking 'show isis interface json'") + for rname, router in tgen.routers().items(): + logger.info("Checking router %s", rname) + json_output = tgen.gears[rname].vtysh_cmd("show isis interface json", isjson=True) + assertmsg = "Test isis interface json failed in '{}' data '{}'".format(rname, json_output) + assert json_output['areas'][0]['circuits'][0]['interface']['name'] == rname+"-eth0", assertmsg + + for rname, router in tgen.routers().items(): + logger.info("Checking router %s", rname) + json_output = tgen.gears[rname].vtysh_cmd("show isis interface detail json", isjson=True) + assertmsg = "Test isis interface json failed in '{}' data '{}'".format(rname, json_output) + assert json_output['areas'][0]['circuits'][0]['interface']['name'] == rname+"-eth0", assertmsg + + +def test_isis_neighbor_json(): + "Check json struct in show isis neighbor json" + + tgen = get_topogen() + # Don't run this test if we have any failure. + if tgen.routers_have_failure(): + pytest.skip(tgen.errors) + + #tgen.mininet_cli() + logger.info("Checking 'show isis neighbor json'") + for rname, router in tgen.routers().items(): + logger.info("Checking router %s", rname) + json_output = tgen.gears[rname].vtysh_cmd("show isis neighbor json", isjson=True) + assertmsg = "Test isis neighbor json failed in '{}' data '{}'".format(rname, json_output) + assert json_output['areas'][0]['circuits'][0]['interface'] == rname+"-eth0", assertmsg + + for rname, router in tgen.routers().items(): + logger.info("Checking router %s", rname) + json_output = tgen.gears[rname].vtysh_cmd("show isis neighbor detail json", isjson=True) + assertmsg = "Test isis neighbor json failed in '{}' data '{}'".format(rname, json_output) + assert json_output['areas'][0]['circuits'][0]['interface']['name'] == rname+"-eth0", assertmsg + + +def test_isis_database_json(): + "Check json struct in show isis database json" + + tgen = get_topogen() + # Don't run this test if we have any failure. + if tgen.routers_have_failure(): + pytest.skip(tgen.errors) + + #tgen.mininet_cli() + logger.info("Checking 'show isis database json'") + for rname, router in tgen.routers().items(): + logger.info("Checking router %s", rname) + json_output = tgen.gears[rname].vtysh_cmd("show isis database json", isjson=True) + assertmsg = "Test isis database json failed in '{}' data '{}'".format(rname, json_output) + assert json_output['areas'][0]['area']['name'] == "1", assertmsg + assert json_output['areas'][0]['levels'][0]['id'] != '3', assertmsg + + for rname, router in tgen.routers().items(): + logger.info("Checking router %s", rname) + json_output = tgen.gears[rname].vtysh_cmd("show isis database detail json", isjson=True) + assertmsg = "Test isis database json failed in '{}' data '{}'".format(rname, json_output) + assert json_output['areas'][0]['area']['name'] == "1", assertmsg + assert json_output['areas'][0]['levels'][0]['id'] != '3', assertmsg + + def test_memory_leak(): "Run the memory leak test and report results." tgen = get_topogen() |