diff options
58 files changed, 2881 insertions, 738 deletions
diff --git a/bgpd/bgp_errors.c b/bgpd/bgp_errors.c index f11717b41f..193c96a169 100644 --- a/bgpd/bgp_errors.c +++ b/bgpd/bgp_errors.c @@ -475,6 +475,12 @@ static struct log_ref ferr_bgp_err[] = { .suggestion = "Get log files from router and open an issue", }, { + .code = EC_BGP_NO_LL_ADDRESS_AVAILABLE, + .title = "BGP v6 peer with no LL address on outgoing interface", + .description = "BGP when using a v6 peer requires a v6 LL address to be configured on the outgoing interface as per RFC 4291 section 2.1", + .suggestion = "Add a v6 LL address to the outgoing interfaces as per RFC", + }, + { .code = END_FERR, } }; diff --git a/bgpd/bgp_errors.h b/bgpd/bgp_errors.h index 20056d382a..0b71af3fc6 100644 --- a/bgpd/bgp_errors.h +++ b/bgpd/bgp_errors.h @@ -101,6 +101,7 @@ enum bgp_log_refs { EC_BGP_ROUTER_ID_SAME, EC_BGP_INVALID_BGP_INSTANCE, EC_BGP_INVALID_ROUTE, + EC_BGP_NO_LL_ADDRESS_AVAILABLE, }; extern void bgp_error_init(void); diff --git a/bgpd/bgp_zebra.c b/bgpd/bgp_zebra.c index c0a9a38773..78eaac7806 100644 --- a/bgpd/bgp_zebra.c +++ b/bgpd/bgp_zebra.c @@ -882,6 +882,12 @@ bool bgp_zebra_nexthop_set(union sockunion *local, union sockunion *remote, */ if (!v6_ll_avail && if_is_loopback(ifp)) v6_ll_avail = true; + else { + flog_warn( + EC_BGP_NO_LL_ADDRESS_AVAILABLE, + "Interface: %s does not have a v6 LL address associated with it, waiting until one is created for it", + ifp->name); + } } else /* Link-local address. */ { diff --git a/bgpd/bgpd.c b/bgpd/bgpd.c index 16488eb4a4..38a106359e 100644 --- a/bgpd/bgpd.c +++ b/bgpd/bgpd.c @@ -1223,7 +1223,7 @@ int bgp_global_gr_init(struct bgp *bgp) { /*Event -> */ /*GLOBAL_GR_cmd*/ /*no_Global_GR_cmd*/ - GLOBAL_INVALID, GLOBAL_HELPER, + GLOBAL_GR, GLOBAL_HELPER, /*GLOBAL_DISABLE_cmd*/ /*no_Global_Disable_cmd*/ GLOBAL_DISABLE, GLOBAL_INVALID }, diff --git a/doc/developer/frr-release-procedure.rst b/doc/developer/frr-release-procedure.rst index 6a7f9c4ca9..4ef0ca8416 100644 --- a/doc/developer/frr-release-procedure.rst +++ b/doc/developer/frr-release-procedure.rst @@ -204,7 +204,7 @@ Stage 3 - Publish .. code-block:: console - cp <old-version>.md <version>.md + cp content/release/<old-version>.md content/release/<new-version>.md Paste the GitHub release announcement text into this document, and **remove line breaks**. In other words, this:: @@ -220,10 +220,17 @@ Stage 3 - Publish This is very important otherwise the announcement will be unreadable on the website. - Make sure to add a link to the GitHub releases page at the top. + To get the number of commiters and commits, here is a couple of handy commands: + + .. code-block:: console - Once finished, manually add a new entry into ``index.html`` to link to this - new announcement. Look at past commits to see how to do this. + # The number of commits + % git log --oneline --no-merges base_8.2...base_8.1 | wc -l + + # The number of commiters + % git shortlog --summary --no-merges base_8.2...base_8.1 | wc -l + + Make sure to add a link to the GitHub releases page at the top. #. Deploy the updated ``frr-www`` on the frrouting.org web server and verify that the announcement text is visible. diff --git a/doc/developer/logging.rst b/doc/developer/logging.rst index 4e6fc04206..7046361204 100644 --- a/doc/developer/logging.rst +++ b/doc/developer/logging.rst @@ -163,6 +163,10 @@ Networking data types - :c:union:`prefixptr` (dereference to get :c:struct:`prefix`) - :c:union:`prefixconstptr` (dereference to get :c:struct:`prefix`) + Options: + + ``%pFXh``: (address only) :frrfmtout:`1.2.3.0` / :frrfmtout:`fe80::1234` + .. frrfmt:: %pPSG4 (struct prefix_sg *) :frrfmtout:`(*,1.2.3.4)` diff --git a/doc/user/index.rst b/doc/user/index.rst index cadf4cb9cf..5a018a5583 100644 --- a/doc/user/index.rst +++ b/doc/user/index.rst @@ -54,6 +54,7 @@ Protocols ospf6d pathd pim + pimv6 pbr ripd ripngd diff --git a/doc/user/isisd.rst b/doc/user/isisd.rst index f7d42d8200..d2859670dd 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/doc/user/pimv6.rst b/doc/user/pimv6.rst new file mode 100644 index 0000000000..e71cf4631c --- /dev/null +++ b/doc/user/pimv6.rst @@ -0,0 +1,288 @@ +.. _pimv6: + +***** +PIMv6 +***** + +PIMv6 -- Protocol Independent Multicast for IPv6 + +*pim6d* supports pim-sm as well as MLD v1 and v2. PIMv6 is +vrf aware and can work within the context of vrf's in order to +do S,G mrouting. + +.. _starting-and-stopping-pim6d: + +Starting and Stopping pim6d +=========================== + +The default configuration file name of *pim6d*'s is :file:`pim6d.conf`. When +invoked *pim6d* searches directory |INSTALL_PREFIX_ETC|. If +:file:`pim6d.conf` is not there then next search current directory. + +*pim6d* requires zebra for proper operation. Additionally *pim6d* depends on +routing properly setup and working in the network that it is working on. + +:: + + # zebra -d + # pim6d -d + + +Please note that *zebra* must be invoked before *pim6d*. + +To stop *pim6d* please use:: + + kill `cat /var/run/pim6d.pid` + +Certain signals have special meanings to *pim6d*. + ++---------+---------------------------------------------------------------------+ +| Signal | Meaning | ++=========+=====================================================================+ +| SIGUSR1 | Rotate the *pim6d* logfile | ++---------+---------------------------------------------------------------------+ +| SIGINT | *pim6d* sweeps all installed PIM mroutes then terminates gracefully.| +| SIGTERM | | ++---------+---------------------------------------------------------------------+ + +*pim6d* invocation options. Common options that can be specified +(:ref:`common-invocation-options`). + +.. clicmd:: ipv6 pim rp X:X::X:X Y:Y::Y:Y/M + + In order to use pimv6, it is necessary to configure a RP for join messages to + be sent to. Currently the only methodology to do this is via static rp + commands. All routers in the pimv6 network must agree on these values. The + first ipv6 address is the RP's address and the second value is the matching + prefix of group ranges covered. This command is vrf aware, to configure for + a vrf, enter the vrf submode. + +.. clicmd:: ipv6 pim rp X:X::X:X prefix-list WORD + + This CLI helps in configuring RP address for a range of groups specified + by the prefix-list. + +.. clicmd:: ipv6 pim rp keep-alive-timer (1-65535) + + Modify the time out value for a S,G flow from 1-65535 seconds at RP. + The normal keepalive period for the KAT(S,G) defaults to 210 seconds. + However, at the RP, the keepalive period must be at least the + Register_Suppression_Time, or the RP may time out the (S,G) state + before the next Null-Register arrives. Thus, the KAT(S,G) is set to + max(Keepalive_Period, RP_Keepalive_Period) when a Register-Stop is sent. + If choosing a value below 31 seconds be aware that some hardware platforms + cannot see data flowing in better than 30 second chunks. This command is + vrf aware, to configure for a vrf, enter the vrf submode. + +.. clicmd:: ipv6 pim spt-switchover infinity-and-beyond [prefix-list PLIST] + + On the last hop router if it is desired to not switch over to the SPT tree + configure this command. Optional parameter prefix-list can be use to control + which groups to switch or not switch. If a group is PERMIT as per the + PLIST, then the SPT switchover does not happen for it and if it is DENY, + then the SPT switchover happens. + This command is vrf aware, to configure for a vrf, + enter the vrf submode. + +.. clicmd:: ipv6 pim join-prune-interval (1-65535) + + Modify the join/prune interval that pim uses to the new value. Time is + specified in seconds. This command is vrf aware, to configure for a vrf, + enter the vrf submode. The default time is 60 seconds. If you enter + a value smaller than 60 seconds be aware that this can and will affect + convergence at scale. + +.. clicmd:: ipv6 pim keep-alive-timer (1-65535) + + Modify the time out value for a S,G flow from 1-65535 seconds. If choosing + a value below 31 seconds be aware that some hardware platforms cannot see data + flowing in better than 30 second chunks. This command is vrf aware, to + configure for a vrf, enter the vrf submode. + +.. clicmd:: ipv6 pim packets (1-255) + + When processing packets from a neighbor process the number of packets + incoming at one time before moving on to the next task. The default value is + 3 packets. This command is only useful at scale when you can possibly have + a large number of pim control packets flowing. This command is vrf aware, to + configure for a vrf, enter the vrf submode. + +.. clicmd:: ipv6 pim register-suppress-time (1-65535) + + Modify the time that pim will register suppress a FHR will send register + notifications to the kernel. This command is vrf aware, to configure for a + vrf, enter the vrf submode. + +.. _pimv6-interface-configuration: + +PIMv6 Interface Configuration +============================= + +PIMv6 interface commands allow you to configure an interface as either a Receiver +or a interface that you would like to form pimv6 neighbors on. If the interface +is in a vrf, enter the interface command with the vrf keyword at the end. + +.. clicmd:: ipv6 pim active-active + + Turn on pim active-active configuration for a Vxlan interface. This + command will not do anything if you do not have the underlying ability + of a mlag implementation. + +.. clicmd:: ipv6 pim drpriority (1-4294967295) + + Set the DR Priority for the interface. This command is useful to allow the + user to influence what node becomes the DR for a lan segment. + +.. clicmd:: ipv6 pim hello (1-65535) (1-65535) + + Set the pim hello and hold interval for a interface. + +.. clicmd:: ipv6 pim + + Tell pim that we would like to use this interface to form pim neighbors + over. Please note that this command does not enable the reception of MLD + reports on the interface. Refer to the next ``ipv6 mld`` command for MLD + management. + +.. clicmd:: ipv6 pim use-source X:X::X:X + + If you have multiple addresses configured on a particular interface + and would like pim to use a specific source address associated with + that interface. + +.. clicmd:: ipv6 mld + + Tell pim to receive MLD reports and Query on this interface. The default + version is v2. This command is useful on a LHR. + +.. clicmd:: ipv6 mld join X:X::X:X [Y:Y::Y:Y] + + Join multicast group or source-group on an interface. + +.. clicmd:: ipv6 mld query-interval (1-65535) + + Set the MLD query interval that PIM will use. + +.. clicmd:: ipv6 mld query-max-response-time (1-65535) + + Set the MLD query response timeout value. If an report is not returned in + the specified time we will assume the S,G or \*,G has timed out. + +.. clicmd:: ipv6 mld version (1-2) + + Set the MLD version used on this interface. The default value is 2. + +.. clicmd:: ipv6 multicast boundary oil WORD + + Set a PIMv6 multicast boundary, based upon the WORD prefix-list. If a PIMv6 + join or MLD report is received on this interface and the Group is denied by + the prefix-list, PIMv6 will ignore the join or report. + +.. clicmd:: ipv6 mld last-member-query-count (1-255) + + Set the MLD last member query count. The default value is 2. 'no' form of + this command is used to configure back to the default value. + +.. clicmd:: ipv6 MLD last-member-query-interval (1-65535) + + Set the MLD last member query interval in deciseconds. The default value is + 10 deciseconds. 'no' form of this command is used to to configure back to the + default value. + +.. clicmd:: ipv6 mroute INTERFACE X:X::X:X [Y:Y::Y:Y] + + Set a static multicast route for a traffic coming on the current interface to + be forwarded on the given interface if the traffic matches the group address + and optionally the source address. + +.. _show-pimv6-information: + +Show PIMv6 Information +====================== + +All PIMv6 show commands are vrf aware and typically allow you to insert a +specified vrf command if information is desired about a specific vrf. If no +vrf is specified then the default vrf is assumed. Finally the special keyword +'all' allows you to look at all vrfs for the command. Naming a vrf 'all' will +cause great confusion. + +.. clicmd:: show ipv6 pim [vrf NAME] group-type [json] + + Display SSM group ranges. + +.. clicmd:: show ipv6 pim interface + + Display information about interfaces PIM is using. + +.. clicmd:: show ipv6 pim [vrf NAME] join [X:X::X:X [X:X::X:X]] [json] +.. clicmd:: show ipv6 pim vrf all join [json] + + Display information about PIM joins received. If one address is specified + then we assume it is the Group we are interested in displaying data on. + If the second address is specified then it is Source Group. + +.. clicmd:: show ipv6 pim [vrf NAME] local-membership [json] + + Display information about PIM interface local-membership. + +.. clicmd:: show ipv6 pim [vrf NAME] neighbor [detail|WORD] [json] +.. clicmd:: show ipv6 pim vrf all neighbor [detail|WORD] [json] + + Display information about PIM neighbors. + +.. clicmd:: show ipv6 pim [vrf NAME] nexthop + + Display information about pim nexthops that are being used. + +.. clicmd:: show ipv6 pim [vrf NAME] nexthop-lookup X:X::X:X X:X::X:X + + Display information about a S,G pair and how the RPF would be chosen. This + is especially useful if there are ECMP's available from the RPF lookup. + +.. clicmd:: show ipv6 pim [vrf NAME] rp-info [json] +.. clicmd:: show ipv6 pim vrf all rp-info [json] + + Display information about RP's that are configured on this router. + +.. clicmd:: show ipv6 pim [vrf NAME] rpf [json] +.. clicmd:: show ipv6 pim vrf all rpf [json] + + Display information about currently being used S,G's and their RPF lookup + information. Additionally display some statistics about what has been + happening on the router. + +.. clicmd:: show ipv6 pim [vrf NAME] secondary + + Display information about an interface and all the secondary addresses + associated with it. + +.. clicmd:: show ipv6 pim [vrf NAME] state [X:X::X:X [X:X::X:X]] [json] +.. clicmd:: show ipv6 pim vrf all state [X:X::X:X [X:X::X:X]] [json] + + Display information about known S,G's and incoming interface as well as the + OIL and how they were chosen. + +.. clicmd:: show ipv6 pim [vrf NAME] upstream [X:X::X:X [Y:Y::Y:Y]] [json] +.. clicmd:: show ipv6 pim vrf all upstream [json] + + Display upstream information about a S,G mroute. Allow the user to + specify sub Source and Groups that we are interested in. + +.. clicmd:: show ipv6 pim [vrf NAME] upstream-join-desired [json] + + Display upstream information for S,G's and if we desire to + join the multicast tree + +.. clicmd:: show ipv6 pim [vrf NAME] upstream-rpf [json] + + Display upstream information for S,G's and the RPF data associated with them. + +PIMv6 Debug Commands +==================== + +The debugging subsystem for PIMv6 behaves in accordance with how FRR handles +debugging. You can specify debugging at the enable CLI mode as well as the +configure CLI mode. If you specify debug commands in the configuration cli +mode, the debug commands can be persistent across restarts of the FRR pim6d if +the config was written out. + diff --git a/doc/user/subdir.am b/doc/user/subdir.am index 31158cb5f7..14ace2c856 100644 --- a/doc/user/subdir.am +++ b/doc/user/subdir.am @@ -30,6 +30,7 @@ user_RSTFILES = \ doc/user/packet-dumps.rst \ doc/user/pathd.rst \ doc/user/pim.rst \ + doc/user/pimv6.rst \ doc/user/ripd.rst \ doc/user/pbr.rst \ doc/user/ripngd.rst \ diff --git a/isisd/isis_adjacency.c b/isisd/isis_adjacency.c index 06909c4306..2729dce382 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 4d84c5ca4d..7467a619cb 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 1b0447226d..da75f196ba 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 7465780848..5ff0390c26 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 463d26f6c7..eb7e9e725e 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 f42d702b37..b13b2a35e6 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 517c9ec5aa..1a54d47f3c 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 04b5cf1a67..d5b02f3881 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 5b3aa59379..815db7b226 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 f1aae7caf1..d3d59fb435 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 0c6ed11cb6..364e38aba1 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 3fa2b7cc20..369b83396a 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 7f8474a5f2..c313fd9ef7 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/lib/json.c b/lib/json.c index 854a3d59d1..d85a21215c 100644 --- a/lib/json.c +++ b/lib/json.c @@ -74,6 +74,19 @@ void json_object_string_addv(struct json_object *obj, const char *key, json_object_object_add(obj, key, json_object_new_stringv(fmt, args)); } +void json_object_object_addv(struct json_object *parent, + struct json_object *child, const char *keyfmt, + va_list args) +{ + char *text, buf[256]; + + text = vasnprintfrr(MTYPE_TMP, buf, sizeof(buf), keyfmt, args); + json_object_object_add(parent, text, child); + + if (text != buf) + XFREE(MTYPE_TMP, text); +} + void json_object_int_add(struct json_object *obj, const char *key, int64_t i) { json_object_object_add(obj, key, json_object_new_int64(i)); diff --git a/lib/json.h b/lib/json.h index fcaa84c816..78c3836515 100644 --- a/lib/json.h +++ b/lib/json.h @@ -116,6 +116,28 @@ static inline struct json_object *json_object_new_stringf(const char *fmt, ...) return ret; } +/* NOTE: argument order differs! (due to varargs) + * json_object_object_add(parent, key, child) + * json_object_object_addv(parent, child, key, va) + * json_object_object_addf(parent, child, key, ...) + * (would be weird to have the child inbetween the format string and args) + */ +PRINTFRR(3, 0) +extern void json_object_object_addv(struct json_object *parent, + struct json_object *child, + const char *keyfmt, va_list args); +PRINTFRR(3, 4) +static inline void json_object_object_addf(struct json_object *parent, + struct json_object *child, + const char *keyfmt, ...) +{ + va_list args; + + va_start(args, keyfmt); + json_object_object_addv(parent, child, keyfmt, args); + va_end(args); +} + #define JSON_STR "JavaScript Object Notation\n" /* NOTE: json-c lib has following commit 316da85 which diff --git a/lib/libfrr.c b/lib/libfrr.c index 10b3aad89e..042c9d3704 100644 --- a/lib/libfrr.c +++ b/lib/libfrr.c @@ -333,6 +333,8 @@ void frr_preinit(struct frr_daemon_info *daemon, int argc, char **argv) umask(0027); + log_args_init(daemon->early_logging); + opt_extend(&os_always); if (!(di->flags & FRR_NO_SPLIT_CONFIG)) opt_extend(&os_cfg); @@ -431,6 +433,8 @@ static int frr_opt(int opt) static int vty_port_set = 0; static int vty_addr_set = 0; struct option_chain *oc; + struct log_arg *log_arg; + size_t arg_len; char *err; switch (opt) { @@ -613,7 +617,10 @@ static int frr_opt(int opt) di->privs->group = optarg; break; case OPTION_LOG: - di->early_logging = optarg; + arg_len = strlen(optarg) + 1; + log_arg = XCALLOC(MTYPE_TMP, sizeof(*log_arg) + arg_len); + memcpy(log_arg->target, optarg, arg_len); + log_args_add_tail(di->early_logging, log_arg); break; case OPTION_LOGLEVEL: di->early_loglevel = optarg; @@ -706,10 +713,12 @@ static struct thread_master *master; struct thread_master *frr_init(void) { struct option_chain *oc; + struct log_arg *log_arg; struct frrmod_runtime *module; struct zprivs_ids_t ids; char p_instance[16] = "", p_pathspace[256] = ""; const char *dir; + dir = di->module_path ? di->module_path : frr_moduledir; srandom(time(NULL)); @@ -739,7 +748,11 @@ struct thread_master *frr_init(void) zlog_init(di->progname, di->logname, di->instance, ids.uid_normal, ids.gid_normal); - command_setup_early_logging(di->early_logging, di->early_loglevel); + while ((log_arg = log_args_pop(di->early_logging))) { + command_setup_early_logging(log_arg->target, + di->early_loglevel); + XFREE(MTYPE_TMP, log_arg); + } if (!frr_zclient_addr(&zclient_addr, &zclient_addr_len, frr_zclientpath)) { diff --git a/lib/libfrr.h b/lib/libfrr.h index 65c1df9675..69054e4264 100644 --- a/lib/libfrr.h +++ b/lib/libfrr.h @@ -21,6 +21,7 @@ #ifndef _ZEBRA_FRR_H #define _ZEBRA_FRR_H +#include "typesafe.h" #include "sigevent.h" #include "privs.h" #include "thread.h" @@ -52,6 +53,14 @@ extern "C" { */ #define FRR_DETACH_LATER (1 << 6) +PREDECL_DLIST(log_args); +struct log_arg { + struct log_args_item itm; + + char target[0]; +}; +DECLARE_DLIST(log_args, struct log_arg, itm); + enum frr_cli_mode { FRR_CLI_CLASSIC = 0, FRR_CLI_TRANSACTIONAL, @@ -88,7 +97,7 @@ struct frr_daemon_info { const char *pathspace; bool zpathspace; - const char *early_logging; + struct log_args_head early_logging[1]; const char *early_loglevel; const char *proghelp; diff --git a/lib/log_vty.c b/lib/log_vty.c index 682c9ea372..ef33a39d4a 100644 --- a/lib/log_vty.c +++ b/lib/log_vty.c @@ -427,6 +427,22 @@ void command_setup_early_logging(const char *dest, const char *level) set_log_file(&zt_file_cmdline, NULL, sep, nlevel); return; } + if (strcmp(type, "monitor") == 0 && sep) { + struct zlog_live_cfg cfg = {}; + unsigned long fd; + char *endp; + + sep++; + fd = strtoul(sep, &endp, 10); + if (!*sep || *endp) { + fprintf(stderr, "invalid monitor fd \"%s\"\n", sep); + exit(1); + } + + zlog_live_open_fd(&cfg, nlevel, fd); + zlog_live_disown(&cfg); + return; + } fprintf(stderr, "invalid log target \"%s\" (\"%s\")\n", type, dest); exit(1); diff --git a/lib/prefix.c b/lib/prefix.c index 90ab48a13b..89c5be8f38 100644 --- a/lib/prefix.c +++ b/lib/prefix.c @@ -1071,6 +1071,26 @@ const char *prefix2str(union prefixconstptr pu, char *str, int size) return str; } +static ssize_t prefixhost2str(struct fbuf *fbuf, union prefixconstptr pu) +{ + const struct prefix *p = pu.p; + char buf[PREFIX2STR_BUFFER]; + + switch (p->family) { + case AF_INET: + case AF_INET6: + inet_ntop(p->family, &p->u.prefix, buf, sizeof(buf)); + return bputs(fbuf, buf); + + case AF_ETHERNET: + prefix_mac2str(&p->u.prefix_eth, buf, sizeof(buf)); + return bputs(fbuf, buf); + + default: + return bprintfrr(fbuf, "{prefix.af=%dPF}", p->family); + } +} + void prefix_mcast_inet4_dump(const char *onfail, struct in_addr addr, char *buf, int buf_size) { @@ -1458,13 +1478,24 @@ printfrr_ext_autoreg_p("FX", printfrr_pfx); static ssize_t printfrr_pfx(struct fbuf *buf, struct printfrr_eargs *ea, const void *ptr) { - char cbuf[PREFIX_STRLEN]; + bool host_only = false; + + if (ea->fmt[0] == 'h') { + ea->fmt++; + host_only = true; + } if (!ptr) return bputs(buf, "(null)"); - prefix2str(ptr, cbuf, sizeof(cbuf)); - return bputs(buf, cbuf); + if (host_only) + return prefixhost2str(buf, (struct prefix *)ptr); + else { + char cbuf[PREFIX_STRLEN]; + + prefix2str(ptr, cbuf, sizeof(cbuf)); + return bputs(buf, cbuf); + } } printfrr_ext_autoreg_p("PSG4", printfrr_psg); diff --git a/lib/zlog.c b/lib/zlog.c index 85606d2624..e0bb34a258 100644 --- a/lib/zlog.c +++ b/lib/zlog.c @@ -401,7 +401,7 @@ void zlog_tls_buffer_flush(void) return; rcu_read_lock(); - frr_each (zlog_targets, &zlog_targets, zt) { + frr_each_safe (zlog_targets, &zlog_targets, zt) { if (!zt->logfn) continue; @@ -431,7 +431,7 @@ static void vzlog_notls(const struct xref_logmsg *xref, int prio, msg->stackbufsz = sizeof(stackbuf); rcu_read_lock(); - frr_each (zlog_targets, &zlog_targets, zt) { + frr_each_safe (zlog_targets, &zlog_targets, zt) { if (prio > zt->prio_min) continue; if (!zt->logfn) diff --git a/lib/zlog_live.c b/lib/zlog_live.c index fbe0e5ee49..931aa3461d 100644 --- a/lib/zlog_live.c +++ b/lib/zlog_live.c @@ -22,6 +22,7 @@ #include "frrcu.h" #include "zlog.h" #include "printfrr.h" +#include "network.h" DEFINE_MTYPE_STATIC(LOG, LOG_LIVE, "log vtysh live target"); @@ -39,6 +40,7 @@ struct zlt_live { struct rcu_head head_self; atomic_uint_fast32_t state; + atomic_uint_fast32_t lost_msgs; }; static void zlog_live(struct zlog_target *zt, struct zlog_msg *msgs[], @@ -63,14 +65,16 @@ static void zlog_live(struct zlog_target *zt, struct zlog_msg *msgs[], for (i = 0; i < nmsgs; i++) { const struct fmt_outpos *argpos; - size_t n_argpos, arghdrlen; + size_t n_argpos, texthdrlen; struct zlog_msg *msg = msgs[i]; int prio = zlog_msg_prio(msg); + const struct xref_logmsg *xref; + intmax_t pid, tid; if (prio > zt->prio_min) continue; - zlog_msg_args(msg, &arghdrlen, &n_argpos, &argpos); + zlog_msg_args(msg, &texthdrlen, &n_argpos, &argpos); mmh->msg_hdr.msg_iov = iov; @@ -89,14 +93,29 @@ static void zlog_live(struct zlog_target *zt, struct zlog_msg *msgs[], iov++; zlog_msg_tsraw(msg, &ts); + zlog_msg_pid(msg, &pid, &tid); + xref = zlog_msg_xref(msg); hdr->ts_sec = ts.tv_sec; hdr->ts_nsec = ts.tv_nsec; - hdr->prio = zlog_msg_prio(msg); + hdr->pid = pid; + hdr->tid = tid; + hdr->lost_msgs = atomic_load_explicit(&zte->lost_msgs, + memory_order_relaxed); + hdr->prio = prio; hdr->flags = 0; hdr->textlen = textlen; - hdr->arghdrlen = arghdrlen; + hdr->texthdrlen = texthdrlen; hdr->n_argpos = n_argpos; + if (xref) { + memcpy(hdr->uid, xref->xref.xrefdata->uid, + sizeof(hdr->uid)); + hdr->ec = xref->ec; + } else { + memset(hdr->uid, 0, sizeof(hdr->uid)); + hdr->ec = 0; + } + hdr->hdrlen = sizeof(*hdr) + sizeof(*argpos) * n_argpos; mmh->msg_hdr.msg_iovlen = iov - mmh->msg_hdr.msg_iov; mmh++; @@ -109,6 +128,12 @@ static void zlog_live(struct zlog_target *zt, struct zlog_msg *msgs[], for (size_t msgpos = 0; msgpos < msgtotal; msgpos += sent) { sent = sendmmsg(fd, mmhs + msgpos, msgtotal - msgpos, 0); + if (sent <= 0 && (errno == EAGAIN || errno == EWOULDBLOCK)) { + atomic_fetch_add_explicit(&zte->lost_msgs, + msgtotal - msgpos, + memory_order_relaxed); + break; + } if (sent <= 0) goto out_err; } @@ -134,7 +159,7 @@ static void zlog_live_sigsafe(struct zlog_target *zt, const char *text, size_t len) { struct zlt_live *zte = container_of(zt, struct zlt_live, zt); - struct zlog_live_hdr hdr[1]; + struct zlog_live_hdr hdr[1] = {}; struct iovec iovs[2], *iov = iovs; struct timespec ts; int fd; @@ -143,14 +168,12 @@ static void zlog_live_sigsafe(struct zlog_target *zt, const char *text, if (fd < 0) return; - clock_gettime(CLOCK_MONOTONIC, &ts); + clock_gettime(CLOCK_REALTIME, &ts); hdr->ts_sec = ts.tv_sec; hdr->ts_nsec = ts.tv_nsec; hdr->prio = LOG_CRIT; - hdr->flags = 0; hdr->textlen = len; - hdr->n_argpos = 0; iov->iov_base = (char *)hdr; iov->iov_len = sizeof(hdr); @@ -166,8 +189,6 @@ static void zlog_live_sigsafe(struct zlog_target *zt, const char *text, void zlog_live_open(struct zlog_live_cfg *cfg, int prio_min, int *other_fd) { int sockets[2]; - struct zlt_live *zte; - struct zlog_target *zt; if (cfg->target) zlog_live_close(cfg); @@ -192,12 +213,23 @@ void zlog_live_open(struct zlog_live_cfg *cfg, int prio_min, int *other_fd) shutdown(sockets[0], SHUT_RD); *other_fd = sockets[1]; + zlog_live_open_fd(cfg, prio_min, sockets[0]); +} + +void zlog_live_open_fd(struct zlog_live_cfg *cfg, int prio_min, int fd) +{ + struct zlt_live *zte; + struct zlog_target *zt; + + if (cfg->target) + zlog_live_close(cfg); zt = zlog_target_clone(MTYPE_LOG_LIVE, NULL, sizeof(*zte)); zte = container_of(zt, struct zlt_live, zt); cfg->target = zte; - zte->fd = sockets[0]; + set_nonblocking(fd); + zte->fd = fd; zte->zt.prio_min = prio_min; zte->zt.logfn = zlog_live; zte->zt.logfn_sigsafe = zlog_live_sigsafe; diff --git a/lib/zlog_live.h b/lib/zlog_live.h index c948baeab1..55e60ae674 100644 --- a/lib/zlog_live.h +++ b/lib/zlog_live.h @@ -20,13 +20,42 @@ #include "printfrr.h" struct zlog_live_hdr { + /* timestamp (CLOCK_REALTIME) */ uint64_t ts_sec; uint32_t ts_nsec; + + /* length of zlog_live_hdr, including variable length bits and + * possible future extensions - aka start of text + */ + uint32_t hdrlen; + + /* process & thread ID, meaning depends on OS */ + int64_t pid; + int64_t tid; + + /* number of lost messages due to best-effort non-blocking mode */ + uint32_t lost_msgs; + /* syslog priority value */ uint32_t prio; + /* flags: currently unused */ uint32_t flags; + /* length of message text - extra data (e.g. future key/value metadata) + * may follow after it + */ uint32_t textlen; + /* length of "[XXXXX-XXXXX][EC 0] " header; consumer may want to skip + * over it if using the raw values below. Note that this text may be + * absent depending on "log error-category" and "log unique-id" + * settings + */ + uint32_t texthdrlen; + + /* xref unique identifier, "XXXXX-XXXXX\0" = 12 bytes */ + char uid[12]; + /* EC value */ + uint32_t ec; - uint32_t arghdrlen; + /* recorded printf formatting argument positions (variable length) */ uint32_t n_argpos; struct fmt_outpos argpos[0]; }; @@ -41,6 +70,7 @@ struct zlog_live_cfg { extern void zlog_live_open(struct zlog_live_cfg *cfg, int prio_min, int *other_fd); +extern void zlog_live_open_fd(struct zlog_live_cfg *cfg, int prio_min, int fd); static inline bool zlog_live_is_null(struct zlog_live_cfg *cfg) { diff --git a/ospf6d/ospf6_gr.c b/ospf6d/ospf6_gr.c index d618ed86e0..87407245b3 100644 --- a/ospf6d/ospf6_gr.c +++ b/ospf6d/ospf6_gr.c @@ -689,7 +689,7 @@ DEFPY(ospf6_graceful_restart_prepare, ospf6_graceful_restart_prepare_cmd, "graceful-restart prepare ipv6 ospf", "Graceful Restart commands\n" "Prepare upcoming graceful restart\n" IPV6_STR - "Prepare to restart the OSPFv3 process") + "Prepare to restart the OSPFv3 process\n") { ospf6_gr_prepare(); diff --git a/ospfd/ospf_gr.c b/ospfd/ospf_gr.c index ee1ca256e3..2521f2fce0 100644 --- a/ospfd/ospf_gr.c +++ b/ospfd/ospf_gr.c @@ -730,7 +730,7 @@ DEFPY(graceful_restart_prepare, graceful_restart_prepare_cmd, "Graceful Restart commands\n" "Prepare upcoming graceful restart\n" IP_STR - "Prepare to restart the OSPF process") + "Prepare to restart the OSPF process\n") { struct ospf *ospf; struct listnode *node; diff --git a/pimd/pim6_stubs.c b/pimd/pim6_stubs.c index dab46b2892..e689c7aca4 100644 --- a/pimd/pim6_stubs.c +++ b/pimd/pim6_stubs.c @@ -29,40 +29,6 @@ /* * NH lookup / NHT */ -void pim_sendmsg_zebra_rnh(struct pim_instance *pim, struct zclient *zclient, - struct pim_nexthop_cache *pnc, int command) -{ -} - -int pim_ecmp_nexthop_lookup(struct pim_instance *pim, - struct pim_nexthop *nexthop, struct prefix *src, - struct prefix *grp, int neighbor_needed) -{ - return 0; -} - -int pim_find_or_track_nexthop(struct pim_instance *pim, struct prefix *addr, - struct pim_upstream *up, struct rp_info *rp, - struct pim_nexthop_cache *out_pnc) -{ - return 0; -} - -void pim_delete_tracked_nexthop(struct pim_instance *pim, struct prefix *addr, - struct pim_upstream *up, struct rp_info *rp) -{ -} - -struct pim_nexthop_cache *pim_nexthop_cache_find(struct pim_instance *pim, - struct pim_rpf *rpf) -{ - return NULL; -} - -void pim_rp_nexthop_del(struct rp_info *rp_info) -{ -} - void pim_nht_bsr_add(struct pim_instance *pim, struct in_addr addr) { } diff --git a/pimd/pim_br.c b/pimd/pim_br.c index 3e64296deb..6ec6b11e7b 100644 --- a/pimd/pim_br.c +++ b/pimd/pim_br.c @@ -30,14 +30,12 @@ struct pim_br { pim_sgaddr sg; - struct in_addr pmbr; + pim_addr pmbr; }; -struct in_addr pim_br_unknown = {.s_addr = 0}; - static struct list *pim_br_list = NULL; -struct in_addr pim_br_get_pmbr(pim_sgaddr *sg) +pim_addr pim_br_get_pmbr(pim_sgaddr *sg) { struct listnode *node; struct pim_br *pim_br; @@ -47,10 +45,10 @@ struct in_addr pim_br_get_pmbr(pim_sgaddr *sg) return pim_br->pmbr; } - return pim_br_unknown; + return PIMADDR_ANY; } -void pim_br_set_pmbr(pim_sgaddr *sg, struct in_addr br) +void pim_br_set_pmbr(pim_sgaddr *sg, pim_addr br) { struct listnode *node, *next; struct pim_br *pim_br; diff --git a/pimd/pim_br.h b/pimd/pim_br.h index ef24ef3c19..7b87c0f1fd 100644 --- a/pimd/pim_br.h +++ b/pimd/pim_br.h @@ -20,13 +20,11 @@ #ifndef PIM_BR_H #define PIM_BR_H -struct in_addr pim_br_get_pmbr(pim_sgaddr *sg); +pim_addr pim_br_get_pmbr(pim_sgaddr *sg); -void pim_br_set_pmbr(pim_sgaddr *sg, struct in_addr value); +void pim_br_set_pmbr(pim_sgaddr *sg, pim_addr value); void pim_br_clear_pmbr(pim_sgaddr *sg); void pim_br_init(void); -extern struct in_addr pim_br_unknown; - #endif diff --git a/pimd/pim_ifchannel.c b/pimd/pim_ifchannel.c index 956ab0d67c..f9fb8cf094 100644 --- a/pimd/pim_ifchannel.c +++ b/pimd/pim_ifchannel.c @@ -872,7 +872,7 @@ void pim_ifchannel_join_add(struct interface *ifp, pim_addr neigh_addr, address of the join message is our primary address. */ if (ch->ifassert_state == PIM_IFASSERT_I_AM_LOSER) { - zlog_warn("%s: Assert Loser recv Join%s from %pI4 on %s", + zlog_warn("%s: Assert Loser recv Join%s from %pPA on %s", __func__, ch->sg_str, &neigh_addr, ifp->name); assert_action_a5(ch); diff --git a/pimd/pim_msg.h b/pimd/pim_msg.h index 522e94504a..456c356d9f 100644 --- a/pimd/pim_msg.h +++ b/pimd/pim_msg.h @@ -21,6 +21,9 @@ #define PIM_MSG_H #include <netinet/in.h> +#if PIM_IPV == 6 +#include <netinet/ip6.h> +#endif #include "pim_jp_agg.h" @@ -181,6 +184,30 @@ struct pim_jp { struct pim_jp_groups groups[1]; } __attribute__((packed)); +#if PIM_IPV == 4 +static inline pim_sgaddr pim_sgaddr_from_iphdr(const void *iphdr) +{ + const struct ip *ipv4_hdr = iphdr; + pim_sgaddr sg; + + sg.src = ipv4_hdr->ip_src; + sg.grp = ipv4_hdr->ip_dst; + + return sg; +} +#else +static inline pim_sgaddr pim_sgaddr_from_iphdr(const void *iphdr) +{ + const struct ip6_hdr *ipv6_hdr = iphdr; + pim_sgaddr sg; + + sg.src = ipv6_hdr->ip6_src; + sg.grp = ipv6_hdr->ip6_dst; + + return sg; +} +#endif + void pim_msg_build_header(uint8_t *pim_msg, size_t pim_msg_size, uint8_t pim_msg_type, bool no_fwd); uint8_t *pim_msg_addr_encode_ipv4_ucast(uint8_t *buf, struct in_addr addr); diff --git a/pimd/pim_nht.c b/pimd/pim_nht.c index 48dd565b25..80d214b2f7 100644 --- a/pimd/pim_nht.c +++ b/pimd/pim_nht.c @@ -162,6 +162,7 @@ int pim_find_or_track_nexthop(struct pim_instance *pim, struct prefix *addr, return 0; } +#if PIM_IPV == 4 void pim_nht_bsr_add(struct pim_instance *pim, struct in_addr addr) { struct pim_nexthop_cache *pnc; @@ -175,6 +176,7 @@ void pim_nht_bsr_add(struct pim_instance *pim, struct in_addr addr) pnc->bsr_count++; } +#endif /* PIM_IPV == 4 */ static void pim_nht_drop_maybe(struct pim_instance *pim, struct pim_nexthop_cache *pnc) @@ -244,6 +246,7 @@ void pim_delete_tracked_nexthop(struct pim_instance *pim, struct prefix *addr, pim_nht_drop_maybe(pim, pnc); } +#if PIM_IPV == 4 void pim_nht_bsr_del(struct pim_instance *pim, struct in_addr addr) { struct pim_nexthop_cache *pnc = NULL; @@ -398,6 +401,7 @@ bool pim_nht_bsr_rpf_check(struct pim_instance *pim, struct in_addr bsr_addr, } return false; } +#endif /* PIM_IPV == 4 */ void pim_rp_nexthop_del(struct rp_info *rp_info) { @@ -482,23 +486,13 @@ static int pim_update_upstream_nh(struct pim_instance *pim, uint32_t pim_compute_ecmp_hash(struct prefix *src, struct prefix *grp) { uint32_t hash_val; - uint32_t s = 0, g = 0; - if ((!src)) + if (!src) return 0; - switch (src->family) { - case AF_INET: { - s = src->u.prefix4.s_addr; - s = s == 0 ? 1 : s; - if (grp) - g = grp->u.prefix4.s_addr; - } break; - default: - break; - } - - hash_val = jhash_2words(g, s, 101); + hash_val = prefix_hash_key(src); + if (grp) + hash_val ^= prefix_hash_key(grp); return hash_val; } @@ -549,9 +543,9 @@ static int pim_ecmp_nexthop_search(struct pim_instance *pim, break; } - if (curr_route_valid - && !pim_if_connected_to_source(nexthop->interface, - src->u.prefix4)) { + if (curr_route_valid && + !pim_if_connected_to_source(nexthop->interface, + src_addr)) { nbr = pim_neighbor_find_prefix( nexthop->interface, &nexthop->mrib_nexthop_addr); @@ -668,7 +662,7 @@ static int pim_ecmp_nexthop_search(struct pim_instance *pim, nh_node->gate.ipv4; #else nexthop->mrib_nexthop_addr.u.prefix6 = - nh_node->gate->ipv6; + nh_node->gate.ipv6; #endif nexthop->mrib_metric_preference = pnc->distance; nexthop->mrib_route_metric = pnc->metric; diff --git a/pimd/pim_register.c b/pimd/pim_register.c index 2cc80f957c..8313c8d4f6 100644 --- a/pimd/pim_register.c +++ b/pimd/pim_register.c @@ -311,30 +311,26 @@ void pim_null_register_send(struct pim_upstream *up) * } * } */ -int pim_register_recv(struct interface *ifp, struct in_addr dest_addr, - struct in_addr src_addr, uint8_t *tlv_buf, - int tlv_buf_size) +int pim_register_recv(struct interface *ifp, pim_addr dest_addr, + pim_addr src_addr, uint8_t *tlv_buf, int tlv_buf_size) { int sentRegisterStop = 0; - struct ip *ip_hdr; + const void *ip_hdr; pim_sgaddr sg; uint32_t *bits; int i_am_rp = 0; struct pim_interface *pim_ifp = ifp->info; struct pim_instance *pim = pim_ifp->pim; + pim_addr rp_addr; #define PIM_MSG_REGISTER_BIT_RESERVED_LEN 4 - ip_hdr = (struct ip *)(tlv_buf + PIM_MSG_REGISTER_BIT_RESERVED_LEN); + ip_hdr = (tlv_buf + PIM_MSG_REGISTER_BIT_RESERVED_LEN); - if (!if_address_is_local(&dest_addr, AF_INET, pim->vrf->vrf_id)) { - if (PIM_DEBUG_PIM_REG) { - char dest[INET_ADDRSTRLEN]; - - pim_inet4_dump("<dst?>", dest_addr, dest, sizeof(dest)); + if (!if_address_is_local(&dest_addr, PIM_AF, pim->vrf->vrf_id)) { + if (PIM_DEBUG_PIM_REG) zlog_debug( - "%s: Received Register message for destination address: %s that I do not own", - __func__, dest); - } + "%s: Received Register message for destination address: %pPA that I do not own", + __func__, &dest_addr); return 0; } @@ -367,18 +363,14 @@ int pim_register_recv(struct interface *ifp, struct in_addr dest_addr, * start of the actual Encapsulated data. */ memset(&sg, 0, sizeof(sg)); - sg.src = ip_hdr->ip_src; - sg.grp = ip_hdr->ip_dst; + sg = pim_sgaddr_from_iphdr(ip_hdr); i_am_rp = I_am_RP(pim, sg.grp); - if (PIM_DEBUG_PIM_REG) { - char src_str[INET_ADDRSTRLEN]; - - pim_inet4_dump("<src?>", src_addr, src_str, sizeof(src_str)); - zlog_debug("Received Register message%pSG from %s on %s, rp: %d", - &sg, src_str, ifp->name, i_am_rp); - } + if (PIM_DEBUG_PIM_REG) + zlog_debug( + "Received Register message%pSG from %pPA on %s, rp: %d", + &sg, &src_addr, ifp->name, i_am_rp); if (pim_is_grp_ssm(pim_ifp->pim, sg.grp)) { if (pim_addr_is_any(sg.src)) { @@ -390,9 +382,8 @@ int pim_register_recv(struct interface *ifp, struct in_addr dest_addr, } } - if (i_am_rp - && (dest_addr.s_addr - == ((RP(pim, sg.grp))->rpf_addr.u.prefix4.s_addr))) { + rp_addr = pim_addr_from_prefix(&(RP(pim, sg.grp))->rpf_addr); + if (i_am_rp && (!pim_addr_cmp(dest_addr, rp_addr))) { sentRegisterStop = 0; if (pim->register_plist) { @@ -407,31 +398,25 @@ int pim_register_recv(struct interface *ifp, struct in_addr dest_addr, if (prefix_list_apply(plist, &src) == PREFIX_DENY) { pim_register_stop_send(ifp, &sg, dest_addr, src_addr); - if (PIM_DEBUG_PIM_PACKETS) { - char src_str[INET_ADDRSTRLEN]; - - pim_inet4_dump("<src?>", src_addr, - src_str, - sizeof(src_str)); + if (PIM_DEBUG_PIM_PACKETS) zlog_debug( - "%s: Sending register-stop to %s for %pSG due to prefix-list denial, dropping packet", - __func__, src_str, &sg); - } + "%s: Sending register-stop to %pPA for %pSG due to prefix-list denial, dropping packet", + __func__, &src_addr, &sg); return 0; } } if (*bits & PIM_REGISTER_BORDER_BIT) { - struct in_addr pimbr = pim_br_get_pmbr(&sg); + pim_addr pimbr = pim_br_get_pmbr(&sg); if (PIM_DEBUG_PIM_PACKETS) zlog_debug( "%s: Received Register message with Border bit set", __func__); - if (pimbr.s_addr == pim_br_unknown.s_addr) + if (pim_addr_is_any(pimbr)) pim_br_set_pmbr(&sg, src_addr); - else if (src_addr.s_addr != pimbr.s_addr) { + else if (pim_addr_cmp(src_addr, pimbr)) { pim_register_stop_send(ifp, &sg, dest_addr, src_addr); if (PIM_DEBUG_PIM_PACKETS) diff --git a/pimd/pim_register.h b/pimd/pim_register.h index fd4284b802..0ebef40c58 100644 --- a/pimd/pim_register.h +++ b/pimd/pim_register.h @@ -32,9 +32,8 @@ int pim_register_stop_recv(struct interface *ifp, uint8_t *buf, int buf_size); -int pim_register_recv(struct interface *ifp, struct in_addr dest_addr, - struct in_addr src_addr, uint8_t *tlv_buf, - int tlv_buf_size); +int pim_register_recv(struct interface *ifp, pim_addr dest_addr, + pim_addr src_addr, uint8_t *tlv_buf, int tlv_buf_size); void pim_register_send(const uint8_t *buf, int buf_size, struct in_addr src, struct pim_rpf *rpg, int null_register, diff --git a/pimd/pim_rp.c b/pimd/pim_rp.c index 25eabe6743..00a1e1b58c 100644 --- a/pimd/pim_rp.c +++ b/pimd/pim_rp.c @@ -77,26 +77,21 @@ int pim_rp_list_cmp(void *v1, void *v2) { struct rp_info *rp1 = (struct rp_info *)v1; struct rp_info *rp2 = (struct rp_info *)v2; + int ret; /* * Sort by RP IP address */ - if (rp1->rp.rpf_addr.u.prefix4.s_addr - < rp2->rp.rpf_addr.u.prefix4.s_addr) - return -1; - - if (rp1->rp.rpf_addr.u.prefix4.s_addr - > rp2->rp.rpf_addr.u.prefix4.s_addr) - return 1; + ret = prefix_cmp(&rp1->rp.rpf_addr, &rp2->rp.rpf_addr); + if (ret) + return ret; /* * Sort by group IP address */ - if (rp1->group.u.prefix4.s_addr < rp2->group.u.prefix4.s_addr) - return -1; - - if (rp1->group.u.prefix4.s_addr > rp2->group.u.prefix4.s_addr) - return 1; + ret = prefix_cmp(&rp1->group, &rp2->group); + if (ret) + return ret; return 0; } @@ -116,13 +111,12 @@ void pim_rp_init(struct pim_instance *pim) if (!pim_get_all_mcast_group(&rp_info->group)) { flog_err(EC_LIB_DEVELOPMENT, - "Unable to convert 224.0.0.0/4 to prefix"); + "Unable to convert all-multicast prefix"); list_delete(&pim->rp_list); route_table_finish(pim->rp_table); XFREE(MTYPE_PIM_RP, rp_info); return; } - rp_info->group.family = AF_INET; pim_addr_to_prefix(&rp_info->rp.rpf_addr, PIMADDR_ANY); listnode_add(pim->rp_list, rp_info); @@ -130,9 +124,9 @@ void pim_rp_init(struct pim_instance *pim) rn = route_node_get(pim->rp_table, &rp_info->group); rn->info = rp_info; if (PIM_DEBUG_PIM_TRACE) - zlog_debug( - "Allocated: %p for rp_info: %p(224.0.0.0/4) Lock: %d", - rn, rp_info, route_node_get_lock_count(rn)); + zlog_debug("Allocated: %p for rp_info: %p(%pFX) Lock: %d", rn, + rp_info, &rp_info->group, + route_node_get_lock_count(rn)); } void pim_rp_free(struct pim_instance *pim) @@ -375,7 +369,7 @@ void pim_upstream_update(struct pim_instance *pim, struct pim_upstream *up) up->sg.grp); if (PIM_DEBUG_PIM_TRACE) - zlog_debug("%s: pim upstream update for old upstream %pI4", + zlog_debug("%s: pim upstream update for old upstream %pPA", __func__, &old_upstream_addr); if (!pim_addr_cmp(old_upstream_addr, new_upstream_addr)) @@ -932,9 +926,7 @@ void pim_rp_setup(struct pim_instance *pim) if (pim_rpf_addr_is_inaddr_any(&rp_info->rp)) continue; - nht_p.family = AF_INET; - nht_p.prefixlen = IPV4_MAX_BITLEN; - nht_p.u.prefix4 = rp_info->rp.rpf_addr.u.prefix4; + nht_p = rp_info->rp.rpf_addr; pim_find_or_track_nexthop(pim, &nht_p, NULL, rp_info, NULL); if (!pim_ecmp_nexthop_lookup(pim, &rp_info->rp.source_nexthop, @@ -1159,7 +1151,6 @@ void pim_rp_show_information(struct pim_instance *pim, struct vty *vty, bool uj) struct rp_info *prev_rp_info = NULL; struct listnode *node; char source[7]; - char buf[PREFIX_STRLEN]; json_object *json = NULL; json_object *json_rp_rows = NULL; @@ -1171,110 +1162,89 @@ void pim_rp_show_information(struct pim_instance *pim, struct vty *vty, bool uj) vty_out(vty, "RP address group/prefix-list OIF I am RP Source\n"); for (ALL_LIST_ELEMENTS_RO(pim->rp_list, node, rp_info)) { - if (!pim_rpf_addr_is_inaddr_any(&rp_info->rp)) { - char buf[48]; + if (pim_rpf_addr_is_inaddr_any(&rp_info->rp)) + continue; - if (rp_info->rp_src == RP_SRC_STATIC) - strlcpy(source, "Static", sizeof(source)); - else if (rp_info->rp_src == RP_SRC_BSR) - strlcpy(source, "BSR", sizeof(source)); + if (rp_info->rp_src == RP_SRC_STATIC) + strlcpy(source, "Static", sizeof(source)); + else if (rp_info->rp_src == RP_SRC_BSR) + strlcpy(source, "BSR", sizeof(source)); + else + strlcpy(source, "None", sizeof(source)); + if (uj) { + /* + * If we have moved on to a new RP then add the + * entry for the previous RP + */ + if (prev_rp_info && + prefix_cmp(&prev_rp_info->rp.rpf_addr, + &rp_info->rp.rpf_addr)) { + json_object_object_addf( + json, json_rp_rows, "%pFXh", + &prev_rp_info->rp.rpf_addr); + json_rp_rows = NULL; + } + + if (!json_rp_rows) + json_rp_rows = json_object_new_array(); + + json_row = json_object_new_object(); + json_object_string_addf(json_row, "rpAddress", "%pFXh", + &rp_info->rp.rpf_addr); + if (rp_info->rp.source_nexthop.interface) + json_object_string_add( + json_row, "outboundInterface", + rp_info->rp.source_nexthop + .interface->name); else - strlcpy(source, "None", sizeof(source)); - if (uj) { - /* - * If we have moved on to a new RP then add the - * entry for the previous RP - */ - if (prev_rp_info - && prev_rp_info->rp.rpf_addr.u.prefix4 - .s_addr - != rp_info->rp.rpf_addr.u.prefix4 - .s_addr) { - json_object_object_add( - json, - inet_ntop(AF_INET, - &prev_rp_info->rp - .rpf_addr.u - .prefix4, - buf, sizeof(buf)), - json_rp_rows); - json_rp_rows = NULL; - } + json_object_string_add(json_row, + "outboundInterface", + "Unknown"); + if (rp_info->i_am_rp) + json_object_boolean_true_add(json_row, "iAmRP"); + else + json_object_boolean_false_add(json_row, + "iAmRP"); - if (!json_rp_rows) - json_rp_rows = json_object_new_array(); - - json_row = json_object_new_object(); - json_object_string_addf( - json_row, "rpAddress", "%pI4", - &rp_info->rp.rpf_addr.u.prefix4); - if (rp_info->rp.source_nexthop.interface) - json_object_string_add( - json_row, "outboundInterface", - rp_info->rp.source_nexthop - .interface->name); - else - json_object_string_add( - json_row, "outboundInterface", - "Unknown"); - if (rp_info->i_am_rp) - json_object_boolean_true_add(json_row, - "iAmRP"); - else - json_object_boolean_false_add(json_row, - "iAmRP"); + if (rp_info->plist) + json_object_string_add(json_row, "prefixList", + rp_info->plist); + else + json_object_string_addf(json_row, "group", + "%pFX", + &rp_info->group); + json_object_string_add(json_row, "source", source); - if (rp_info->plist) - json_object_string_add(json_row, - "prefixList", - rp_info->plist); - else - json_object_string_addf( - json_row, "group", "%pFX", - &rp_info->group); - json_object_string_add(json_row, "source", - source); + json_object_array_add(json_rp_rows, json_row); + } else { + vty_out(vty, "%-15pFXh ", &rp_info->rp.rpf_addr); - json_object_array_add(json_rp_rows, json_row); - } else { - vty_out(vty, "%-15s ", - inet_ntop(AF_INET, - &rp_info->rp.rpf_addr.u - .prefix4, - buf, sizeof(buf))); - - if (rp_info->plist) - vty_out(vty, "%-18s ", rp_info->plist); - else - vty_out(vty, "%-18pFX ", - &rp_info->group); + if (rp_info->plist) + vty_out(vty, "%-18s ", rp_info->plist); + else + vty_out(vty, "%-18pFX ", &rp_info->group); - if (rp_info->rp.source_nexthop.interface) - vty_out(vty, "%-16s ", - rp_info->rp.source_nexthop - .interface->name); - else - vty_out(vty, "%-16s ", "(Unknown)"); + if (rp_info->rp.source_nexthop.interface) + vty_out(vty, "%-16s ", + rp_info->rp.source_nexthop + .interface->name); + else + vty_out(vty, "%-16s ", "(Unknown)"); - if (rp_info->i_am_rp) - vty_out(vty, "yes"); - else - vty_out(vty, "no"); + if (rp_info->i_am_rp) + vty_out(vty, "yes"); + else + vty_out(vty, "no"); - vty_out(vty, "%14s\n", source); - } - prev_rp_info = rp_info; + vty_out(vty, "%14s\n", source); } + prev_rp_info = rp_info; } if (uj) { if (prev_rp_info && json_rp_rows) - json_object_object_add( - json, - inet_ntop(AF_INET, - &prev_rp_info->rp.rpf_addr.u.prefix4, - buf, sizeof(buf)), - json_rp_rows); + json_object_object_addf(json, json_rp_rows, "%pFXh", + &prev_rp_info->rp.rpf_addr); vty_json(vty, json); } @@ -1292,17 +1262,20 @@ void pim_resolve_rp_nh(struct pim_instance *pim, struct pim_neighbor *nbr) if (pim_rpf_addr_is_inaddr_any(&rp_info->rp)) continue; - nht_p.family = AF_INET; - nht_p.prefixlen = IPV4_MAX_BITLEN; - nht_p.u.prefix4 = rp_info->rp.rpf_addr.u.prefix4; + nht_p = rp_info->rp.rpf_addr; memset(&pnc, 0, sizeof(struct pim_nexthop_cache)); if (!pim_find_or_track_nexthop(pim, &nht_p, NULL, rp_info, &pnc)) continue; for (nh_node = pnc.nexthop; nh_node; nh_node = nh_node->next) { - if (nh_node->gate.ipv4.s_addr != INADDR_ANY) +#if PIM_IPV == 4 + if (!pim_addr_is_any(nh_node->gate.ipv4)) + continue; +#else + if (!pim_addr_is_any(nh_node->gate.ipv6)) continue; +#endif struct interface *ifp1 = if_lookup_by_index( nh_node->ifindex, pim->vrf->vrf_id); @@ -1315,15 +1288,11 @@ void pim_resolve_rp_nh(struct pim_instance *pim, struct pim_neighbor *nbr) #else nh_node->gate.ipv6 = nbr->source_addr; #endif - if (PIM_DEBUG_PIM_NHT_RP) { - char str[PREFIX_STRLEN]; - pim_addr_dump("<nht_addr?>", &nht_p, str, - sizeof(str)); + if (PIM_DEBUG_PIM_NHT_RP) zlog_debug( - "%s: addr %s new nexthop addr %pPAs interface %s", - __func__, str, &nbr->source_addr, + "%s: addr %pFXh new nexthop addr %pPAs interface %s", + __func__, &nht_p, &nbr->source_addr, ifp1->name); - } } } } diff --git a/pimd/pim_zebra.c b/pimd/pim_zebra.c index 0acd3c0694..526fdcbc27 100644 --- a/pimd/pim_zebra.c +++ b/pimd/pim_zebra.c @@ -454,10 +454,11 @@ static void pim_zebra_capabilities(struct zclient_capabilities *cap) static zclient_handler *const pim_handlers[] = { [ZEBRA_INTERFACE_ADDRESS_ADD] = pim_zebra_if_address_add, [ZEBRA_INTERFACE_ADDRESS_DELETE] = pim_zebra_if_address_del, + + [ZEBRA_NEXTHOP_UPDATE] = pim_parse_nexthop_update, #if PIM_IPV == 4 [ZEBRA_ROUTER_ID_UPDATE] = pim_router_id_update_zebra, [ZEBRA_INTERFACE_VRF_UPDATE] = pim_zebra_interface_vrf_update, - [ZEBRA_NEXTHOP_UPDATE] = pim_parse_nexthop_update, [ZEBRA_VXLAN_SG_ADD] = pim_zebra_vxlan_sg_proc, [ZEBRA_VXLAN_SG_DEL] = pim_zebra_vxlan_sg_proc, @@ -820,7 +821,7 @@ void pim_forward_start(struct pim_ifchannel *ch) uint32_t mask = 0; if (PIM_DEBUG_PIM_TRACE) - zlog_debug("%s: (S,G)=%pSG oif=%s (%pI4)", __func__, &ch->sg, + zlog_debug("%s: (S,G)=%pSG oif=%s (%pPA)", __func__, &ch->sg, ch->interface->name, &up->upstream_addr); if (PIM_IF_FLAG_TEST_PROTO_IGMP(ch->flags)) diff --git a/pimd/subdir.am b/pimd/subdir.am index 0fb5de7e09..d617be3cb7 100644 --- a/pimd/subdir.am +++ b/pimd/subdir.am @@ -37,6 +37,7 @@ pim_common = \ pimd/pim_nb.c \ pimd/pim_nb_config.c \ pimd/pim_neighbor.c \ + pimd/pim_nht.c \ pimd/pim_oil.c \ pimd/pim_routemap.c \ pimd/pim_rp.c \ @@ -70,7 +71,6 @@ pimd_pimd_SOURCES = \ pimd/pim_msdp.c \ pimd/pim_msdp_packet.c \ pimd/pim_msdp_socket.c \ - pimd/pim_nht.c \ pimd/pim_pim.c \ pimd/pim_register.c \ pimd/pim_signals.c \ diff --git a/tests/isisd/test_fuzz_isis_tlv.c b/tests/isisd/test_fuzz_isis_tlv.c index 97aade6578..8f0b92d0fc 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 a30f33ccad..971aba4c46 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/lib/test_printfrr.c b/tests/lib/test_printfrr.c index 8f9d637afd..59d08ae82b 100644 --- a/tests/lib/test_printfrr.c +++ b/tests/lib/test_printfrr.c @@ -207,6 +207,24 @@ int main(int argc, char **argv) assert(strcmp(p, "test#5") == 0); XFREE(MTYPE_TMP, p); + struct prefix pfx; + + str2prefix("192.168.1.23/24", &pfx); + printchk("192.168.1.23/24", "%pFX", &pfx); + printchk("192.168.1.23", "%pFXh", &pfx); + + str2prefix("2001:db8::1234/64", &pfx); + printchk("2001:db8::1234/64", "%pFX", &pfx); + printchk("2001:db8::1234", "%pFXh", &pfx); + + pfx.family = AF_UNIX; + printchk("UNK prefix", "%pFX", &pfx); + printchk("{prefix.af=AF_UNIX}", "%pFXh", &pfx); + + str2prefix_eth("02:ca:fe:f0:0d:1e/48", (struct prefix_eth *)&pfx); + printchk("02:ca:fe:f0:0d:1e/48", "%pFX", &pfx); + printchk("02:ca:fe:f0:0d:1e", "%pFXh", &pfx); + struct prefix_sg sg; sg.src.s_addr = INADDR_ANY; sg.grp.s_addr = INADDR_ANY; diff --git a/tests/topotests/isis_topo1/test_isis_topo1.py b/tests/topotests/isis_topo1/test_isis_topo1.py index 94c5faf2e0..014722387f 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() diff --git a/vrrpd/Makefile b/vrrpd/Makefile index 027c6ee1f8..0abb1a6381 100644 --- a/vrrpd/Makefile +++ b/vrrpd/Makefile @@ -1,7 +1,7 @@ all: ALWAYS - @$(MAKE) -s -C .. vrrp/vrrp + @$(MAKE) -s -C .. vrrpd/vrrpd %: ALWAYS - @$(MAKE) -s -C .. vrrp/$@ + @$(MAKE) -s -C .. vrrpd/$@ Makefile: #nothing diff --git a/vtysh/vtysh.c b/vtysh/vtysh.c index f8c6a1fc1d..ed1f1fb5bb 100644 --- a/vtysh/vtysh.c +++ b/vtysh/vtysh.c @@ -73,6 +73,7 @@ struct vtysh_client { struct thread *log_reader; int log_fd; + uint32_t lost_msgs; }; static bool stderr_tty; @@ -3654,6 +3655,15 @@ static void vtysh_log_read(struct thread *thread) if (ret < 0 && ERRNO_IO_RETRY(errno)) return; + if (stderr_stdout_same) { +#ifdef HAVE_RL_CLEAR_VISIBLE_LINE + rl_clear_visible_line(); +#else + puts("\r"); +#endif + fflush(stdout); + } + if (ret <= 0) { struct timespec ts; @@ -3677,17 +3687,17 @@ static void vtysh_log_read(struct thread *thread) buf.hdr.ts_nsec = ts.tv_nsec; buf.hdr.prio = LOG_ERR; buf.hdr.flags = 0; - buf.hdr.arghdrlen = 0; + buf.hdr.texthdrlen = 0; buf.hdr.n_argpos = 0; - } + } else { + int32_t lost_msgs = buf.hdr.lost_msgs - vclient->lost_msgs; - if (stderr_stdout_same) { -#ifdef HAVE_RL_CLEAR_VISIBLE_LINE - rl_clear_visible_line(); -#else - puts("\r"); -#endif - fflush(stdout); + if (lost_msgs > 0) { + vclient->lost_msgs = buf.hdr.lost_msgs; + fprintf(stderr, + "%d log messages from %s lost (vtysh reading too slowly)\n", + lost_msgs, vclient->name); + } } text = buf.text + sizeof(buf.hdr.argpos[0]) * buf.hdr.n_argpos; diff --git a/zebra/if_netlink.c b/zebra/if_netlink.c index db8ee3236c..a75b165270 100644 --- a/zebra/if_netlink.c +++ b/zebra/if_netlink.c @@ -1876,6 +1876,7 @@ int netlink_link_change(struct nlmsghdr *h, ns_id_t ns_id, int startup) } else { bool was_bridge_slave, was_bond_slave; uint8_t chgflags = ZEBRA_BRIDGE_NO_ACTION; + zif = ifp->info; /* Interface update. */ if (IS_ZEBRA_DEBUG_KERNEL) @@ -1909,9 +1910,21 @@ int netlink_link_change(struct nlmsghdr *h, ns_id_t ns_id, int startup) netlink_to_zebra_link_type(ifi->ifi_type); netlink_interface_update_hw_addr(tb, ifp); + if (tb[IFLA_PROTO_DOWN]) { + uint8_t protodown; + + protodown = *(uint8_t *)RTA_DATA( + tb[IFLA_PROTO_DOWN]); + netlink_proc_dplane_if_protodown(zif, + !!protodown); + } + if (if_is_no_ptm_operative(ifp)) { + bool is_up = if_is_operative(ifp); ifp->flags = ifi->ifi_flags & 0x0000fffff; - if (!if_is_no_ptm_operative(ifp)) { + if (!if_is_no_ptm_operative(ifp) || + CHECK_FLAG(zif->flags, + ZIF_FLAG_PROTODOWN)) { if (IS_ZEBRA_DEBUG_KERNEL) zlog_debug( "Intf %s(%u) has gone DOWN", @@ -1927,7 +1940,7 @@ int netlink_link_change(struct nlmsghdr *h, ns_id_t ns_id, int startup) zlog_debug( "Intf %s(%u) PTM up, notifying clients", name, ifp->ifindex); - zebra_interface_up_update(ifp); + if_up(ifp, !is_up); /* Update EVPN VNI when SVI MAC change */ @@ -1956,12 +1969,14 @@ int netlink_link_change(struct nlmsghdr *h, ns_id_t ns_id, int startup) } } else { ifp->flags = ifi->ifi_flags & 0x0000fffff; - if (if_is_operative(ifp)) { + if (if_is_operative(ifp) && + !CHECK_FLAG(zif->flags, + ZIF_FLAG_PROTODOWN)) { if (IS_ZEBRA_DEBUG_KERNEL) zlog_debug( "Intf %s(%u) has come UP", name, ifp->ifindex); - if_up(ifp); + if_up(ifp, true); if (IS_ZEBRA_IF_BRIDGE(ifp)) chgflags = ZEBRA_BRIDGE_MASTER_UP; @@ -1990,15 +2005,6 @@ int netlink_link_change(struct nlmsghdr *h, ns_id_t ns_id, int startup) else if (IS_ZEBRA_IF_BOND_SLAVE(ifp) || was_bond_slave) zebra_l2if_update_bond_slave(ifp, bond_ifindex, !!bypass); - - if (tb[IFLA_PROTO_DOWN]) { - uint8_t protodown; - - protodown = *(uint8_t *)RTA_DATA( - tb[IFLA_PROTO_DOWN]); - netlink_proc_dplane_if_protodown(ifp->info, - !!protodown); - } } zif = ifp->info; diff --git a/zebra/interface.c b/zebra/interface.c index fbd2aac005..a76f8741e0 100644 --- a/zebra/interface.c +++ b/zebra/interface.c @@ -224,9 +224,13 @@ static int if_zebra_new_hook(struct interface *ifp) static void if_nhg_dependents_check_valid(struct nhg_hash_entry *nhe) { zebra_nhg_check_valid(nhe); - if (!CHECK_FLAG(nhe->flags, NEXTHOP_GROUP_VALID)) - /* Assuming uninstalled as well here */ - UNSET_FLAG(nhe->flags, NEXTHOP_GROUP_INSTALLED); + if (!CHECK_FLAG(nhe->flags, NEXTHOP_GROUP_VALID)) { + /* If we're in shutdown, this interface event needs to clean + * up installed NHGs, so don't clear that flag directly. + */ + if (!zrouter.in_shutdown) + UNSET_FLAG(nhe->flags, NEXTHOP_GROUP_INSTALLED); + } } static void if_down_nhg_dependents(const struct interface *ifp) @@ -517,7 +521,7 @@ void if_flags_update(struct interface *ifp, uint64_t newflags) /* inoperative -> operative? */ ifp->flags = newflags; if (if_is_operative(ifp)) - if_up(ifp); + if_up(ifp, true); } } @@ -1045,7 +1049,7 @@ bool if_nhg_dependents_is_empty(const struct interface *ifp) } /* Interface is up. */ -void if_up(struct interface *ifp) +void if_up(struct interface *ifp, bool install_connected) { struct zebra_if *zif; struct interface *link_if; @@ -1077,7 +1081,8 @@ void if_up(struct interface *ifp) #endif /* Install connected routes to the kernel. */ - if_install_connected(ifp); + if (install_connected) + if_install_connected(ifp); /* Handle interface up for specific types for EVPN. Non-VxLAN interfaces * are checked to see if (remote) neighbor entries need to be installed @@ -2778,7 +2783,7 @@ int if_linkdetect(struct interface *ifp, bool detect) /* Interface may come up after disabling link detection */ if (if_is_operative(ifp) && !if_was_operative) - if_up(ifp); + if_up(ifp, true); } /* FIXME: Will defer status change forwarding if interface does not come down! */ diff --git a/zebra/interface.h b/zebra/interface.h index c19e494860..315a3170d8 100644 --- a/zebra/interface.h +++ b/zebra/interface.h @@ -486,7 +486,7 @@ extern void if_nbr_ipv6ll_to_ipv4ll_neigh_update(struct interface *ifp, extern void if_nbr_ipv6ll_to_ipv4ll_neigh_del_all(struct interface *ifp); extern void if_delete_update(struct interface *ifp); extern void if_add_update(struct interface *ifp); -extern void if_up(struct interface *); +extern void if_up(struct interface *ifp, bool install_connected); extern void if_down(struct interface *); extern void if_refresh(struct interface *); extern void if_flags_update(struct interface *, uint64_t); diff --git a/zebra/zebra_evpn_mac.c b/zebra/zebra_evpn_mac.c index e38766bc6b..74043e521c 100644 --- a/zebra/zebra_evpn_mac.c +++ b/zebra/zebra_evpn_mac.c @@ -1141,14 +1141,6 @@ int zebra_evpn_mac_del(struct zebra_evpn *zevpn, struct zebra_mac *mac) sizeof(mac_buf))); } - /* If the MAC is freed before the neigh we will end up - * with a stale pointer against the neigh - */ - if (!list_isempty(mac->neigh_list)) - zlog_warn("%s: MAC %pEA flags 0x%x neigh list not empty %d", - __func__, &mac->macaddr, mac->flags, - listcount(mac->neigh_list)); - /* force de-ref any ES entry linked to the MAC */ zebra_evpn_es_mac_deref_entry(mac); @@ -1161,6 +1153,26 @@ int zebra_evpn_mac_del(struct zebra_evpn *zevpn, struct zebra_mac *mac) /* Cancel auto recovery */ THREAD_OFF(mac->dad_mac_auto_recovery_timer); + /* If the MAC is freed before the neigh we will end up + * with a stale pointer against the neigh. + * The situation can arise when a MAC is in remote state + * and its associated neigh is local state. + * zebra_evpn_cfg_cleanup() cleans up remote neighs and MACs. + * Instead of deleting remote MAC, if its neigh list is non-empty + * (associated to local neighs), mark the MAC as AUTO. + */ + if (!list_isempty(mac->neigh_list)) { + if (IS_ZEBRA_DEBUG_VXLAN) + zlog_debug( + "MAC %pEA (flags 0x%x vni %u) has non-empty neigh list " + "count %u, mark MAC as AUTO", + &mac->macaddr, mac->flags, zevpn->vni, + listcount(mac->neigh_list)); + + SET_FLAG(mac->flags, ZEBRA_MAC_AUTO); + return 0; + } + list_delete(&mac->neigh_list); /* Free the VNI hash entry and allocated memory. */ diff --git a/zebra/zebra_evpn_neigh.c b/zebra/zebra_evpn_neigh.c index f557e66384..01ac7c227f 100644 --- a/zebra/zebra_evpn_neigh.c +++ b/zebra/zebra_evpn_neigh.c @@ -2203,7 +2203,6 @@ int zebra_evpn_neigh_gw_macip_add(struct interface *ifp, /* Only advertise in BGP if the knob is enabled */ if (advertise_gw_macip_enabled(zevpn)) { - SET_FLAG(mac->flags, ZEBRA_MAC_DEF_GW); SET_FLAG(n->flags, ZEBRA_NEIGH_DEF_GW); /* Set Router flag (R-bit) */ if (ip->ipa_type == IPADDR_V6) diff --git a/zebra/zebra_nhg.c b/zebra/zebra_nhg.c index e1d28e1534..469a94a65b 100644 --- a/zebra/zebra_nhg.c +++ b/zebra/zebra_nhg.c @@ -1966,7 +1966,7 @@ static int resolve_backup_nexthops(const struct nexthop *nexthop, */ static int nexthop_active(struct nexthop *nexthop, struct nhg_hash_entry *nhe, const struct prefix *top, int type, uint32_t flags, - uint32_t *pmtu) + uint32_t *pmtu, vrf_id_t vrf_id) { struct prefix p; struct route_table *table; @@ -2061,13 +2061,13 @@ static int nexthop_active(struct nexthop *nexthop, struct nhg_hash_entry *nhe, return 1; } - if (top - && ((top->family == AF_INET && top->prefixlen == IPV4_MAX_BITLEN - && nexthop->gate.ipv4.s_addr == top->u.prefix4.s_addr) - || (top->family == AF_INET6 && top->prefixlen == IPV6_MAX_BITLEN - && memcmp(&nexthop->gate.ipv6, &top->u.prefix6, - IPV6_MAX_BYTELEN) - == 0))) { + if (top && + ((top->family == AF_INET && top->prefixlen == IPV4_MAX_BITLEN && + nexthop->gate.ipv4.s_addr == top->u.prefix4.s_addr) || + (top->family == AF_INET6 && top->prefixlen == IPV6_MAX_BITLEN && + memcmp(&nexthop->gate.ipv6, &top->u.prefix6, IPV6_MAX_BYTELEN) == + 0)) && + nexthop->vrf_id == vrf_id) { if (IS_ZEBRA_DEBUG_RIB_DETAILED) zlog_debug( " :%s: Attempting to install a max prefixlength route through itself", @@ -2361,6 +2361,7 @@ static unsigned nexthop_active_check(struct route_node *rn, const struct prefix *p, *src_p; struct zebra_vrf *zvrf; uint32_t mtu = 0; + vrf_id_t vrf_id; srcdest_rnode_prefixes(rn, &p, &src_p); @@ -2389,10 +2390,12 @@ static unsigned nexthop_active_check(struct route_node *rn, goto skip_check; } + + vrf_id = zvrf_id(rib_dest_vrf(rib_dest_from_rnode(rn))); switch (nexthop->type) { case NEXTHOP_TYPE_IFINDEX: - if (nexthop_active(nexthop, nhe, &rn->p, re->type, - re->flags, &mtu)) + if (nexthop_active(nexthop, nhe, &rn->p, re->type, re->flags, + &mtu, vrf_id)) SET_FLAG(nexthop->flags, NEXTHOP_FLAG_ACTIVE); else UNSET_FLAG(nexthop->flags, NEXTHOP_FLAG_ACTIVE); @@ -2400,16 +2403,16 @@ static unsigned nexthop_active_check(struct route_node *rn, case NEXTHOP_TYPE_IPV4: case NEXTHOP_TYPE_IPV4_IFINDEX: family = AFI_IP; - if (nexthop_active(nexthop, nhe, &rn->p, re->type, - re->flags, &mtu)) + if (nexthop_active(nexthop, nhe, &rn->p, re->type, re->flags, + &mtu, vrf_id)) SET_FLAG(nexthop->flags, NEXTHOP_FLAG_ACTIVE); else UNSET_FLAG(nexthop->flags, NEXTHOP_FLAG_ACTIVE); break; case NEXTHOP_TYPE_IPV6: family = AFI_IP6; - if (nexthop_active(nexthop, nhe, &rn->p, re->type, - re->flags, &mtu)) + if (nexthop_active(nexthop, nhe, &rn->p, re->type, re->flags, + &mtu, vrf_id)) SET_FLAG(nexthop->flags, NEXTHOP_FLAG_ACTIVE); else UNSET_FLAG(nexthop->flags, NEXTHOP_FLAG_ACTIVE); @@ -2419,8 +2422,8 @@ static unsigned nexthop_active_check(struct route_node *rn, if (rn->p.family != AF_INET) family = AFI_IP6; - if (nexthop_active(nexthop, nhe, &rn->p, re->type, - re->flags, &mtu)) + if (nexthop_active(nexthop, nhe, &rn->p, re->type, re->flags, + &mtu, vrf_id)) SET_FLAG(nexthop->flags, NEXTHOP_FLAG_ACTIVE); else UNSET_FLAG(nexthop->flags, NEXTHOP_FLAG_ACTIVE); diff --git a/zebra/zebra_ptm.c b/zebra/zebra_ptm.c index 68e5c391cf..c28e251e3a 100644 --- a/zebra/zebra_ptm.c +++ b/zebra/zebra_ptm.c @@ -350,7 +350,7 @@ DEFUN (no_zebra_ptm_enable_if, if (IS_ZEBRA_DEBUG_EVENT) zlog_debug("%s: Bringing up interface %s", __func__, ifp->name); - if_up(ifp); + if_up(ifp, true); } } @@ -553,7 +553,7 @@ static int zebra_ptm_handle_cbl_msg(void *arg, void *in_ctxt, ifp->ptm_status = ZEBRA_PTM_STATUS_UP; if (ifp->ptm_enable && if_is_no_ptm_operative(ifp) && send_linkup) - if_up(ifp); + if_up(ifp, true); } else if (!strcmp(cbl_str, ZEBRA_PTM_FAIL_STR) && (ifp->ptm_status != ZEBRA_PTM_STATUS_DOWN)) { ifp->ptm_status = ZEBRA_PTM_STATUS_DOWN; @@ -1163,7 +1163,7 @@ void zebra_ptm_reset_status(int ptm_disable) zlog_debug( "%s: Bringing up interface %s", __func__, ifp->name); - if_up(ifp); + if_up(ifp, true); } } } |
