According to part 1 of this patch following commands will be added to the JSON debug: meshinfo_json, originators_json, neighbors_json, translocal_json, transglobal_json.
Signed-off-by: Alexander Sarmanow asarmanow@gmail.com --- Makefile | 5 ++ gateways.c | 2 +- main.c | 14 +++-- main.h | 1 + mcast_flags.c | 2 +- meshinfo_json.c | 139 +++++++++++++++++++++++++++++++++++++++++++++ neighbors_json.c | 89 +++++++++++++++++++++++++++++ netlink.c | 39 ++++++++----- netlink.h | 2 +- originators.c | 2 +- originators_json.c | 107 ++++++++++++++++++++++++++++++++++ transglobal_json.c | 109 +++++++++++++++++++++++++++++++++++ translocal_json.c | 102 +++++++++++++++++++++++++++++++++ 13 files changed, 592 insertions(+), 21 deletions(-) create mode 100644 meshinfo_json.c create mode 100644 neighbors_json.c create mode 100644 originators_json.c create mode 100644 transglobal_json.c create mode 100644 translocal_json.c
diff --git a/Makefile b/Makefile index 98bf695..0f85561 100755 --- a/Makefile +++ b/Makefile @@ -54,13 +54,16 @@ $(eval $(call add_command,interface,y)) $(eval $(call add_command,isolation_mark,y)) $(eval $(call add_command,loglevel,y)) $(eval $(call add_command,mcast_flags,y)) +$(eval $(call add_command,meshinfo_json,y)) $(eval $(call add_command,multicast_fanout,y)) $(eval $(call add_command,multicast_forceflood,y)) $(eval $(call add_command,multicast_mode,y)) $(eval $(call add_command,neighbors,y)) +$(eval $(call add_command,neighbors_json,y)) $(eval $(call add_command,network_coding,y)) $(eval $(call add_command,orig_interval,y)) $(eval $(call add_command,originators,y)) +$(eval $(call add_command,originators_json,y)) $(eval $(call add_command,ping,y)) $(eval $(call add_command,routing_algo,y)) $(eval $(call add_command,statistics,y)) @@ -69,8 +72,10 @@ $(eval $(call add_command,throughput_override,y)) $(eval $(call add_command,throughputmeter,y)) $(eval $(call add_command,traceroute,y)) $(eval $(call add_command,transglobal,y)) +$(eval $(call add_command,transglobal_json,y)) $(eval $(call add_command,translate,y)) $(eval $(call add_command,translocal,y)) +$(eval $(call add_command,translocal_json,y))
MANPAGE = man/batctl.8
diff --git a/gateways.c b/gateways.c index 867c882..3704c99 100644 --- a/gateways.c +++ b/gateways.c @@ -123,7 +123,7 @@ static int netlink_print_gateways(struct state *state, char *orig_iface, /* only parse routing algorithm name */ last_err = -EINVAL; info_header = netlink_get_info(state->mesh_ifindex, - BATADV_CMD_GET_ORIGINATORS, NULL); + BATADV_CMD_GET_ORIGINATORS, NULL, 0); free(info_header);
if (strlen(algo_name_buf) == 0) diff --git a/main.c b/main.c index d9b63f3..1371bc0 100644 --- a/main.c +++ b/main.c @@ -43,6 +43,10 @@ static void print_usage(void) .label = "debug tables: \tdisplay the corresponding debug table\n", .types = BIT(DEBUGTABLE), }, + { + .label = "debug JSONs: \tdisplay the corresponding debug JSON\n", + .types = BIT(DEBUGJSON), + }, }; const char *default_prefixes[] = { "", @@ -67,9 +71,9 @@ static void print_usage(void) char buf[64]; size_t i;
- fprintf(stderr, "Usage: batctl [options] command|debug table [parameters]\n"); + fprintf(stderr, "Usage: batctl [options] command|debug table|debug json [parameters]\n"); fprintf(stderr, "options:\n"); - fprintf(stderr, " \t-h print this help (or 'batctl <command|debug table> -h' for the parameter help)\n"); + fprintf(stderr, " \t-h print this help (or 'batctl <command|debug table|debug json> -h' for the parameter help)\n"); fprintf(stderr, " \t-v print version\n");
for (i = 0; i < sizeof(type) / sizeof(*type); i++) { @@ -87,6 +91,7 @@ static void print_usage(void) continue;
switch (cmd->type) { + case DEBUGJSON: case DEBUGTABLE: case SUBCOMMAND_MIF: prefixes = meshif_prefixes; @@ -167,7 +172,8 @@ static const struct command *find_command(struct state *state, const char *name) /* fall through */ case SP_MESHIF: types |= BIT(SUBCOMMAND_MIF) | - BIT(DEBUGTABLE); + BIT(DEBUGTABLE) | + BIT(DEBUGJSON); break; case SP_VLAN: types = BIT(SUBCOMMAND_VID); @@ -380,7 +386,7 @@ int main(int argc, char **argv) cmd = find_command(&state, argv[0]); if (!cmd) { fprintf(stderr, - "Error - no valid command or debug table specified: %s\n", + "Error - no valid command or debug table/JSON specified: %s\n", argv[0]); goto err; } diff --git a/main.h b/main.h index b1ff050..2efd136 100644 --- a/main.h +++ b/main.h @@ -71,6 +71,7 @@ enum command_type { SUBCOMMAND_VID, SUBCOMMAND_HIF, DEBUGTABLE, + DEBUGJSON, };
struct state { diff --git a/mcast_flags.c b/mcast_flags.c index cb6e89d..87fb077 100644 --- a/mcast_flags.c +++ b/mcast_flags.c @@ -105,7 +105,7 @@ static int netlink_print_mcast_flags(struct state *state, char *orig_iface,
/* only parse own multicast flags */ info_header = netlink_get_info(state->mesh_ifindex, - BATADV_CMD_GET_MCAST_FLAGS, NULL); + BATADV_CMD_GET_MCAST_FLAGS, NULL, 0); free(info_header);
if (mcast_flags == -EOPNOTSUPP || mcast_flags_priv == -EOPNOTSUPP) diff --git a/meshinfo_json.c b/meshinfo_json.c new file mode 100644 index 0000000..8c6f675 --- /dev/null +++ b/meshinfo_json.c @@ -0,0 +1,139 @@ +// SPDX-License-Identifier: GPL-2.0 +/* Copyright (C) B.A.T.M.A.N. contributors: + * + * Alexander Sarmanow asarmanow@gmail.com + * + * License-Filename: LICENSES/preferred/GPL-2.0 + */ + +#include <errno.h> +#include <netlink/netlink.h> +#include <netlink/genl/genl.h> +#include <netlink/genl/ctrl.h> +#include <stdio.h> +#include <stdlib.h> +#include <stdint.h> + +#include "batman_adv.h" +#include "debug.h" +#include "main.h" +#include "netlink.h" + +static const int info_mandatory[] = { + BATADV_ATTR_MESH_IFINDEX, + BATADV_ATTR_MESH_IFNAME, +}; + +static int meshinfo_callback(struct nl_msg *msg, void *arg) +{ + struct print_opts *opts = arg; + struct nlattr *attrs[BATADV_ATTR_MAX+1]; + struct nlmsghdr *nlh = nlmsg_hdr(msg); + struct genlmsghdr *ghdr; + int selected_attrs[10] = { BATADV_ATTR_MESH_IFNAME, + BATADV_ATTR_MESH_ADDRESS, + BATADV_ATTR_HARD_IFNAME, + BATADV_ATTR_VERSION, + BATADV_ATTR_ALGO_NAME, + BATADV_ATTR_HARD_ADDRESS, + BATADV_ATTR_TT_TTVN }; + + if (!genlmsg_valid_hdr(nlh, 0)) { + fputs("Received invalid data from kernel.\n", stderr); + exit(1); + } + + ghdr = nlmsg_data(nlh); + + if (ghdr->cmd != BATADV_CMD_GET_MESH_INFO) + return NL_OK; + if (nla_parse(attrs, BATADV_ATTR_MAX, genlmsg_attrdata(ghdr, 0), + genlmsg_len(ghdr), batadv_netlink_policy)) { + fputs("Received invalid data from kernel.\n", stderr); + exit(1); + } + + if (missing_mandatory_attrs(attrs, info_mandatory, + ARRAY_SIZE(info_mandatory))) { + fputs("Missing attributes from kernel\n", stderr); + exit(1); + } + + netlink_print_json_entries(attrs, selected_attrs, + ARRAY_SIZE(selected_attrs), opts); + opts->is_first = 0; + + return NL_OK; +} + +static int netlink_print_meshinfo_json(struct state *state, char *orig_iface, + int read_opts, float orig_timeout, + float watch_interval) +{ + (void) orig_iface; + (void) orig_timeout; + (void) watch_interval; + (void) read_opts; + struct nl_sock *sock; + struct nl_msg *msg; + struct nl_cb *cb; + struct print_opts opts = { + .is_json = 1, + .is_first = 1, + }; + int family; + + if (!state->sock) { + last_err = -EOPNOTSUPP; + return last_err; + } + + sock = nl_socket_alloc(); + if (!sock) + return -1; + + genl_connect(sock); + + family = genl_ctrl_resolve(sock, BATADV_NL_NAME); + if (family < 0) { + nl_socket_free(sock); + return -1; + } + + msg = nlmsg_alloc(); + if (!msg) { + nl_socket_free(sock); + return -1; + } + + genlmsg_put(msg, NL_AUTO_PID, NL_AUTO_SEQ, family, 0, 0, + BATADV_CMD_GET_MESH_INFO, 1); + + nla_put_u32(msg, BATADV_ATTR_MESH_IFINDEX, state->mesh_ifindex); + + nl_send_auto_complete(sock, msg); + + nlmsg_free(msg); + + cb = nl_cb_alloc(NL_CB_DEFAULT); + if (!cb) + goto err_free_sock; + + nl_cb_set(cb, NL_CB_VALID, NL_CB_CUSTOM, meshinfo_callback, &opts); + nl_cb_err(cb, NL_CB_CUSTOM, netlink_print_error, NULL); + + nl_recvmsgs(sock, cb); + +err_free_sock: + nl_socket_free(sock); + + return 0; +} + +static struct debug_table_data batctl_debug_json_meshinfo = { + .netlink_fn = netlink_print_meshinfo_json, +}; + +COMMAND_NAMED(DEBUGJSON, meshinfo_json, "mij", handle_debug_table, + COMMAND_FLAG_MESH_IFACE | COMMAND_FLAG_NETLINK, + &batctl_debug_json_meshinfo, ""); diff --git a/neighbors_json.c b/neighbors_json.c new file mode 100644 index 0000000..5182e47 --- /dev/null +++ b/neighbors_json.c @@ -0,0 +1,89 @@ +// SPDX-License-Identifier: GPL-2.0 +/* Copyright (C) B.A.T.M.A.N. contributors: + * + * Andrew Lunn andrew@lunn.ch + * Alexander Sarmanow asarmanow@gmail.com + * + * License-Filename: LICENSES/preferred/GPL-2.0 + */ + +#include <net/if.h> +#include <netinet/if_ether.h> +#include <netlink/netlink.h> +#include <netlink/genl/genl.h> +#include <netlink/genl/ctrl.h> +#include <stdio.h> +#include <stdlib.h> +#include <stdint.h> + +#include "batadv_packet.h" +#include "batman_adv.h" +#include "bat-hosts.h" +#include "debug.h" +#include "functions.h" +#include "main.h" +#include "netlink.h" + +static const int neighbors_mandatory[] = { + BATADV_ATTR_NEIGH_ADDRESS, + BATADV_ATTR_HARD_IFINDEX, + BATADV_ATTR_LAST_SEEN_MSECS, +}; + +static int neighbors_json_callback(struct nl_msg *msg, void *arg) +{ + struct nlattr *attrs[BATADV_ATTR_MAX+1]; + struct nlmsghdr *nlh = nlmsg_hdr(msg); + struct print_opts *opts = arg; + struct genlmsghdr *ghdr; + int selected_attrs[4] = { BATADV_ATTR_NEIGH_ADDRESS, + BATADV_ATTR_HARD_IFINDEX, + BATADV_ATTR_LAST_SEEN_MSECS, + BATADV_ATTR_THROUGHPUT }; + + if (!genlmsg_valid_hdr(nlh, 0)) { + fputs("Received invalid data from kernel.\n", stderr); + exit(1); + } + + ghdr = nlmsg_data(nlh); + + if (ghdr->cmd != BATADV_CMD_GET_NEIGHBORS) + return NL_OK; + + if (nla_parse(attrs, BATADV_ATTR_MAX, genlmsg_attrdata(ghdr, 0), + genlmsg_len(ghdr), batadv_netlink_policy)) { + fputs("Received invalid data from kernel.\n", stderr); + exit(1); + } + + if (missing_mandatory_attrs(attrs, neighbors_mandatory, + ARRAY_SIZE(neighbors_mandatory))) { + fputs("Missing attributes from kernel\n", stderr); + exit(1); + } + + netlink_print_json_entries(attrs, selected_attrs, + ARRAY_SIZE(selected_attrs), opts); + opts->is_first = 0; + + return NL_OK; +} + +static int netlink_print_neighbors_json(struct state *state, char *orig_iface, + int read_opts, float orig_timeout, + float watch_interval) +{ + return netlink_print_common(state, orig_iface, read_opts, + orig_timeout, watch_interval, NULL, + BATADV_CMD_GET_NEIGHBORS, + neighbors_json_callback, true); +} + +static struct debug_table_data batctl_debug_json_neighbors = { + .netlink_fn = netlink_print_neighbors_json, +}; + +COMMAND_NAMED(DEBUGJSON, neighbors_json, "nj", handle_debug_table, + COMMAND_FLAG_MESH_IFACE | COMMAND_FLAG_NETLINK, + &batctl_debug_json_neighbors, ""); diff --git a/netlink.c b/netlink.c index 26ae5ef..db44d82 100644 --- a/netlink.c +++ b/netlink.c @@ -363,17 +363,21 @@ static int info_callback(struct nl_msg *msg, void *arg) else extra_header = "";
- ret = asprintf(&opts->remaining_header, - "[B.A.T.M.A.N. adv %s, MainIF/MAC: %s/%02x:%02x:%02x:%02x:%02x:%02x (%s/%02x:%02x:%02x:%02x:%02x:%02x %s)%s]\n%s", - version, primary_if, - primary_mac[0], primary_mac[1], primary_mac[2], - primary_mac[3], primary_mac[4], primary_mac[5], - mesh_name, - mesh_mac[0], mesh_mac[1], mesh_mac[2], - mesh_mac[3], mesh_mac[4], mesh_mac[5], - algo_name, extra_info, extra_header); - if (ret < 0) - opts->remaining_header = NULL; + if (!opts->is_json) { + ret = asprintf(&opts->remaining_header, + "[B.A.T.M.A.N. adv %s, MainIF/MAC: %s/%02x:%02x:%02x:%02x:%02x:%02x (%s/%02x:%02x:%02x:%02x:%02x:%02x %s)%s]\n%s", + version, primary_if, + primary_mac[0], primary_mac[1], + primary_mac[2], primary_mac[3], + primary_mac[4], primary_mac[5], + mesh_name, + mesh_mac[0], mesh_mac[1], mesh_mac[2], + mesh_mac[3], mesh_mac[4], mesh_mac[5], + algo_name, extra_info, extra_header); + + if (ret < 0) + opts->remaining_header = NULL; + }
if (extra_info) free(extra_info); @@ -387,7 +391,8 @@ static int info_callback(struct nl_msg *msg, void *arg) return NL_STOP; }
-char *netlink_get_info(int ifindex, uint8_t nl_cmd, const char *header) +char *netlink_get_info(int ifindex, uint8_t nl_cmd, const char *header, + uint8_t is_json) { struct nl_sock *sock; struct nl_msg *msg; @@ -398,6 +403,7 @@ char *netlink_get_info(int ifindex, uint8_t nl_cmd, const char *header) .nl_cmd = nl_cmd, .remaining_header = NULL, .static_header = header, + .is_json = is_json, };
sock = nl_socket_alloc(); @@ -494,6 +500,9 @@ int netlink_print_common(struct state *state, char *orig_iface, int read_opt, } }
+ if (is_json) + printf("["); + bat_hosts_init(read_opt);
nl_cb_set(state->cb, NL_CB_VALID, NL_CB_CUSTOM, netlink_print_common_cb, &opts); @@ -508,7 +517,8 @@ int netlink_print_common(struct state *state, char *orig_iface, int read_opt, if (!(read_opt & SKIP_HEADER)) opts.remaining_header = netlink_get_info(state->mesh_ifindex, nl_cmd, - header); + header, + is_json);
msg = nlmsg_alloc(); if (!msg) @@ -529,6 +539,9 @@ int netlink_print_common(struct state *state, char *orig_iface, int read_opt, last_err = 0; nl_recvmsgs(state->sock, state->cb);
+ if (is_json) + printf("]"); + /* the header should still be printed when no entry was received */ if (!last_err) netlink_print_remaining_header(&opts); diff --git a/netlink.h b/netlink.h index c766741..47390fc 100644 --- a/netlink.h +++ b/netlink.h @@ -44,7 +44,7 @@ struct ether_addr; int netlink_create(struct state *state); void netlink_destroy(struct state *state);
-char *netlink_get_info(int ifindex, uint8_t nl_cmd, const char *header); +char *netlink_get_info(int ifindex, uint8_t nl_cmd, const char *header, uint8_t is_json); int translate_mac_netlink(const char *mesh_iface, const struct ether_addr *mac, struct ether_addr *mac_out); int get_nexthop_netlink(const char *mesh_iface, const struct ether_addr *mac, diff --git a/originators.c b/originators.c index 674656c..c3af740 100644 --- a/originators.c +++ b/originators.c @@ -175,7 +175,7 @@ static int netlink_print_originators(struct state *state, char *orig_iface, /* only parse routing algorithm name */ last_err = -EINVAL; info_header = netlink_get_info(state->mesh_ifindex, - BATADV_CMD_GET_ORIGINATORS, NULL); + BATADV_CMD_GET_ORIGINATORS, NULL, 0); free(info_header);
if (strlen(algo_name_buf) == 0) diff --git a/originators_json.c b/originators_json.c new file mode 100644 index 0000000..91dcb44 --- /dev/null +++ b/originators_json.c @@ -0,0 +1,107 @@ +// SPDX-License-Identifier: GPL-2.0 +/* Copyright (C) B.A.T.M.A.N. contributors: + * + * Andrew Lunn andrew@lunn.ch + * Sven Eckelmann sven@narfation.org + * Alexander Sarmanow asarmanow@gmail.com + * + * License-Filename: LICENSES/preferred/GPL-2.0 + */ + +#include <errno.h> +#include <net/if.h> +#include <netinet/if_ether.h> +#include <netlink/netlink.h> +#include <netlink/genl/genl.h> +#include <netlink/genl/ctrl.h> +#include <stdio.h> +#include <stdlib.h> +#include <stdint.h> +#include <string.h> + +#include "batadv_packet.h" +#include "batman_adv.h" +#include "bat-hosts.h" +#include "debug.h" +#include "functions.h" +#include "main.h" +#include "netlink.h" + +static const int originators_mandatory[] = { + BATADV_ATTR_ORIG_ADDRESS, + BATADV_ATTR_NEIGH_ADDRESS, + BATADV_ATTR_HARD_IFINDEX, + BATADV_ATTR_LAST_SEEN_MSECS, +}; + +static int originators_json_callback(struct nl_msg *msg, void *arg) +{ + struct nlattr *attrs[BATADV_ATTR_MAX+1]; + struct nlmsghdr *nlh = nlmsg_hdr(msg); + int last_seen_msecs; + struct print_opts *opts = arg; + struct genlmsghdr *ghdr; + float last_seen; + int selected_attrs[6] = { BATADV_ATTR_ORIG_ADDRESS, + BATADV_ATTR_NEIGH_ADDRESS, + BATADV_ATTR_HARD_IFINDEX, + BATADV_ATTR_LAST_SEEN_MSECS, + BATADV_ATTR_THROUGHPUT, + BATADV_ATTR_TQ }; + + if (!genlmsg_valid_hdr(nlh, 0)) { + fputs("Received invalid data from kernel.\n", stderr); + exit(1); + } + + ghdr = nlmsg_data(nlh); + + if (ghdr->cmd != BATADV_CMD_GET_ORIGINATORS) + return NL_OK; + + if (nla_parse(attrs, BATADV_ATTR_MAX, genlmsg_attrdata(ghdr, 0), + genlmsg_len(ghdr), batadv_netlink_policy)) { + fputs("Received invalid data from kernel.\n", stderr); + exit(1); + } + + if (missing_mandatory_attrs(attrs, originators_mandatory, + ARRAY_SIZE(originators_mandatory))) { + fputs("Missing attributes from kernel\n", stderr); + exit(1); + } + + last_seen_msecs = nla_get_u32(attrs[BATADV_ATTR_LAST_SEEN_MSECS]); + last_seen = (float)last_seen_msecs / 1000.0; + + /* skip timed out originators */ + if (opts->read_opt & NO_OLD_ORIGS) + if (last_seen > opts->orig_timeout) + return NL_OK; + + netlink_print_json_entries(attrs, selected_attrs, + ARRAY_SIZE(selected_attrs), opts); + opts->is_first = 0; + + return NL_OK; +} + +static int netlink_print_originators_json(struct state *state, char *orig_iface, + int read_opts, float orig_timeout, + float watch_interval) +{ + return netlink_print_common(state, orig_iface, read_opts, + orig_timeout, watch_interval, NULL, + BATADV_CMD_GET_ORIGINATORS, + originators_json_callback, 1); +} + +static struct debug_table_data batctl_debug_json_originators = { + .netlink_fn = netlink_print_originators_json, + .option_timeout_interval = 1, + .option_orig_iface = 1, +}; + +COMMAND_NAMED(DEBUGJSON, originators_json, "oj", handle_debug_table, + COMMAND_FLAG_MESH_IFACE | COMMAND_FLAG_NETLINK, + &batctl_debug_json_originators, ""); diff --git a/transglobal_json.c b/transglobal_json.c new file mode 100644 index 0000000..faff8ec --- /dev/null +++ b/transglobal_json.c @@ -0,0 +1,109 @@ +// SPDX-License-Identifier: GPL-2.0 +/* Copyright (C) B.A.T.M.A.N. contributors: + * + * Andrew Lunn <andrew(a)lunn.ch> + * Sven Eckelmann <sven(a)narfation.org> + * Alexander Sarmanow <asarmanow(a)gmail.com> + * + * License-Filename: LICENSES/preferred/GPL-2.0 + */ + +#include <netinet/if_ether.h> +#include <netlink/netlink.h> +#include <netlink/genl/genl.h> +#include <netlink/genl/ctrl.h> +#include <stdio.h> +#include <stdlib.h> +#include <stdint.h> + +#include "batadv_packet.h" +#include "batman_adv.h" +#include "bat-hosts.h" +#include "debug.h" +#include "functions.h" +#include "main.h" +#include "netlink.h" + +static const int transglobal_mandatory[] = { + BATADV_ATTR_TT_ADDRESS, + BATADV_ATTR_ORIG_ADDRESS, + BATADV_ATTR_TT_VID, + BATADV_ATTR_TT_TTVN, + BATADV_ATTR_TT_LAST_TTVN, + BATADV_ATTR_TT_CRC32, + BATADV_ATTR_TT_FLAGS, +}; + +static int transglobal_json_callback(struct nl_msg *msg, void *arg) +{ + struct nlattr *attrs[BATADV_ATTR_MAX+1]; + struct nlmsghdr *nlh = nlmsg_hdr(msg); + struct print_opts *opts = arg; + struct genlmsghdr *ghdr; + uint8_t *addr; + int selected_attrs[8] = { BATADV_ATTR_TT_ADDRESS, + BATADV_ATTR_ORIG_ADDRESS, + BATADV_ATTR_TT_VID, + BATADV_ATTR_TT_TTVN, + BATADV_ATTR_TT_LAST_TTVN, + BATADV_ATTR_TT_CRC32, + BATADV_ATTR_TT_FLAGS, + BATADV_ATTR_FLAG_BEST }; + + if (!genlmsg_valid_hdr(nlh, 0)) { + fputs("Received invalid data from kernel.\n", stderr); + exit(1); + } + + ghdr = nlmsg_data(nlh); + + if (ghdr->cmd != BATADV_CMD_GET_TRANSTABLE_GLOBAL) + return NL_OK; + + if (nla_parse(attrs, BATADV_ATTR_MAX, genlmsg_attrdata(ghdr, 0), + genlmsg_len(ghdr), batadv_netlink_policy)) { + fputs("Received invalid data from kernel.\n", stderr); + exit(1); + } + + if (missing_mandatory_attrs(attrs, transglobal_mandatory, + ARRAY_SIZE(transglobal_mandatory))) { + fputs("Missing attributes from kernel\n", stderr); + exit(1); + } + + addr = nla_data(attrs[BATADV_ATTR_TT_ADDRESS]); + + if (opts->read_opt & MULTICAST_ONLY && !(addr[0] & 0x01)) + return NL_OK; + + if (opts->read_opt & UNICAST_ONLY && (addr[0] & 0x01)) + return NL_OK; + + netlink_print_json_entries(attrs, selected_attrs, + ARRAY_SIZE(selected_attrs), opts); + opts->is_first = 0; + + + return NL_OK; +} + +static int netlink_print_transglobal_json(struct state *state, char *orig_iface, + int read_opts, float orig_timeout, + float watch_interval) +{ + return netlink_print_common(state, orig_iface, read_opts, + orig_timeout, watch_interval, NULL, + BATADV_CMD_GET_TRANSTABLE_GLOBAL, + transglobal_json_callback, true); +} + +static struct debug_table_data batctl_debug_json_transglobal = { + .netlink_fn = netlink_print_transglobal_json, + .option_unicast_only = 1, + .option_multicast_only = 1, +}; + +COMMAND_NAMED(DEBUGJSON, transglobal_json, "tgj", handle_debug_table, + COMMAND_FLAG_MESH_IFACE | COMMAND_FLAG_NETLINK, + &batctl_debug_json_transglobal, ""); diff --git a/translocal_json.c b/translocal_json.c new file mode 100644 index 0000000..e78150e --- /dev/null +++ b/translocal_json.c @@ -0,0 +1,102 @@ +// SPDX-License-Identifier: GPL-2.0 +/* Copyright (C) B.A.T.M.A.N. contributors: + * + * Andrew Lunn andrew@lunn.ch + * Sven Eckelmann sven@narfation.org + * Alexander Sarmanow asarmanow@gmail.com + * + * License-Filename: LICENSES/preferred/GPL-2.0 + */ + +#include <netinet/if_ether.h> +#include <netlink/netlink.h> +#include <netlink/genl/genl.h> +#include <netlink/genl/ctrl.h> +#include <stdio.h> +#include <stdlib.h> +#include <stdint.h> + +#include "batadv_packet.h" +#include "batman_adv.h" +#include "bat-hosts.h" +#include "debug.h" +#include "functions.h" +#include "main.h" +#include "netlink.h" + +static const int translocal_mandatory[] = { + BATADV_ATTR_TT_ADDRESS, + BATADV_ATTR_TT_VID, + BATADV_ATTR_TT_CRC32, + BATADV_ATTR_TT_FLAGS, +}; + +static int translocal_json_callback(struct nl_msg *msg, void *arg) +{ + struct nlattr *attrs[BATADV_ATTR_MAX+1]; + struct nlmsghdr *nlh = nlmsg_hdr(msg); + struct print_opts *opts = arg; + struct genlmsghdr *ghdr; + uint8_t *addr; + int selected_attrs[5] = { BATADV_ATTR_TT_ADDRESS, + BATADV_ATTR_TT_VID, + BATADV_ATTR_TT_CRC32, + BATADV_ATTR_TT_FLAGS, + BATADV_ATTR_LAST_SEEN_MSECS }; + + if (!genlmsg_valid_hdr(nlh, 0)) { + fputs("Received invalid data from kernel.\n", stderr); + exit(1); + } + + ghdr = nlmsg_data(nlh); + + if (ghdr->cmd != BATADV_CMD_GET_TRANSTABLE_LOCAL) + return NL_OK; + + if (nla_parse(attrs, BATADV_ATTR_MAX, genlmsg_attrdata(ghdr, 0), + genlmsg_len(ghdr), batadv_netlink_policy)) { + fputs("Received invalid data from kernel.\n", stderr); + exit(1); + } + + if (missing_mandatory_attrs(attrs, translocal_mandatory, + ARRAY_SIZE(translocal_mandatory))) { + fputs("Missing attributes from kernel\n", stderr); + exit(1); + } + + addr = nla_data(attrs[BATADV_ATTR_TT_ADDRESS]); + + if (opts->read_opt & MULTICAST_ONLY && !(addr[0] & 0x01)) + return NL_OK; + + if (opts->read_opt & UNICAST_ONLY && (addr[0] & 0x01)) + return NL_OK; + + netlink_print_json_entries(attrs, selected_attrs, + ARRAY_SIZE(selected_attrs), opts); + opts->is_first = 0; + + return NL_OK; +} + +static int netlink_print_translocal_json(struct state *state, char *orig_iface, + int read_opts, float orig_timeout, + float watch_interval) +{ + return netlink_print_common(state, orig_iface, read_opts, + orig_timeout, watch_interval, NULL, + BATADV_CMD_GET_TRANSTABLE_LOCAL, + translocal_json_callback, 1); +} + +static struct debug_table_data batctl_debug_json_translocal = { + .netlink_fn = netlink_print_translocal_json, + .option_unicast_only = 1, + .option_multicast_only = 1, +}; + +COMMAND_NAMED(DEBUGJSON, translocal_json, "tlj", handle_debug_table, + COMMAND_FLAG_MESH_IFACE | COMMAND_FLAG_NETLINK, + &batctl_debug_json_translocal, "");
On Friday, 7 May 2021 19:14:35 CEST Alexander Sarmanow wrote:
According to part 1 of this patch following commands will be added to the JSON debug: meshinfo_json, originators_json, neighbors_json, translocal_json, transglobal_json.
I've asked you to split this up into helper code and one commit per command. And also add the documentation to the manpage and README.rst in each commit which adds/changes a command.
Why are you only printing selected attributes and not all?
Kind regards, Sven
On Monday, 10 May 2021 09:58:26 CEST asarmanow@gmail.com wrote:
Sven Eckelmann wrote:
Why are you only printing selected attributes and not all?
I wanted to reduce the amount to query for the netlink_print_json_entries function. There are only a few of the attributes needed for each of the JSON commands.
You are doing currently two queries (one BATADV_CMD_GET_MESH_INFO and one for the actual data). I want to you to get rid of the BATADV_CMD_GET_MESH_INFO and print all data which was returned for the actual data query.
So I don't see a reason why you're approach would help here at all. Just go with netlink_print_json_entries through all entries in attrs till BATADV_ATTR_MAX and check if you have a function to print them. If you have, just print it.
You can then also get rid of missing_mandatory_attrs and also extra checks like MULTICAST_ONLY/UNICAST_ONLY/NO_OLD_ORIGS. The json is not for human consumption and thus we don't need special flags to prefilter the data. The consumer can take care of processing the data.
Btw. I am also more of a fan of following style of struct/array initialization.
struct nla_policy_json batadv_netlink_policy_json[NUM_BATADV_ATTR] = { [BATADV_ATTR_VERSION] = { .name = "version", .cb = nljson_print_str }, [.....]
I have to change this at some point in netlink.c
+void create_json_print_string(const char *str, const char **str_ret) +{
size_t needed = snprintf(NULL, 0, "\"%s\"", str);
char *str_buf = malloc(needed+1);
sprintf(str_buf, "\"%s\"", str);
*str_ret = str_buf;
+}
Another thing I saw: you allocate a lot of strings but never free them. Can we just avoid to allocate them?
Other than that - why? This function doesn't make a lot of sense in the first place.
[BATADV_ATTR_FLAG_BEST] = { .name = "best",
.cb = nljson_print_bool},
Also: BATADV_ATTR_FLAG_BEST is not a boolean - it is a flag. So it can be present and absent. So is comparable to an HTML attribute without value (for example "disabled"). I think we can encode it as "best": true when it exists.
+void nljson_print_ttflags(struct json_opts *json_opts, struct print_opts *opts,
struct nlattr *attrs[], int idx)
+{
const char *key_ls;
uint32_t flags;
char r, n, x, w, i, p, t;
flags = nla_get_u32(attrs[idx]);
r = '.', p = '.', n = '.', x = '.', w = '.', i = '.', t = '.';
if (opts->nl_cmd == BATADV_CMD_GET_TRANSTABLE_LOCAL) {
[...]
} else {
if (flags & BATADV_TT_CLIENT_ROAM)
r = 'R';
if (flags & BATADV_TT_CLIENT_WIFI)
w = 'W';
if (flags & BATADV_TT_CLIENT_ISOLA)
i = 'I';
if (flags & BATADV_TT_CLIENT_TEMP)
t = 'T';
goto print_global_table;
}
+print_local_table:
printf("\"%c%c%c%c%c%c\"", r, n, x, w, i, p);
printf(",");
create_json_print_string(batadv_netlink_policy_json[BATADV_ATTR_LAST_SEEN_MSECS].name,
&key_ls);
sanitize_string(key_ls, strlen(key_ls));
printf(":");
nljson_print_int(json_opts, opts, attrs, BATADV_ATTR_LAST_SEEN_MSECS);
return;
+print_global_table:
printf("\"%c%c%c%c\"", r, w, i, t);
+}
nljson_print_ttflags is also odd. If you decode the binary, why are you creating a compact representation again? Why aren't you just create a sub object with the parsed data? And there is no need to differentiate between BATADV_CMD_GET_TRANSTABLE_LOCAL and BATADV_CMD_GET_TRANSTABLE_GLOBAL.
And why is there a goto in the first place at the end of each scope?
And you interpretation of BATADV_ATTR_LAST_SEEN_MSECS seems to be really messed up. Just don't print BATADV_ATTR_LAST_SEEN_MSECS when it is not there - which can be evaluated automatically. And just as info: the kernel will not send it when the nopurge bit is set. So it is absolutely not what you've coded here.
+void nljson_print_crc32(struct json_opts *json_opts, struct print_opts *opts,
struct nlattr *attrs[], int idx)
+{
(void) json_opts;
(void) opts;
int32_t value = nla_get_u32(attrs[idx]);
printf("\"0x%.8x\"", value);
+}
Can we just print the raw 32 bit unsigned integer?
Also, please not the name but the actual number in places were you are now calling nljson_print_ifname_by_ifindex. Otherwise you would print the name twice and make unnecessary syscalls. And if you want to print the actual name for things like the originator/neighbor dump, maybe just add it to the generic netlink message in the kernel.
+void netlink_print_json_entries(struct nlattr *attrs[], int selected_attrs[],
int arr_len, struct print_opts *opts)
+{
const char *name;
uint8_t first_valid_attr = 1;
int idx, i;
struct json_opts json_opts = {
.use_alt_int_val = 0,
.alt_int_val = 0,
};
if (!opts->is_first)
printf(",");
for (i = 0; i < arr_len; i++) {
idx = selected_attrs[i];
if (attrs[idx] && batadv_netlink_policy_json[idx].cb) {
if (!first_valid_attr)
printf(",");
else
printf("{");
create_json_print_string(batadv_netlink_policy_json[idx].name,
&name);
sanitize_string(name, strlen(name));
printf(":");
batadv_netlink_policy_json[idx].cb(&json_opts, opts,
attrs, idx);
first_valid_attr = 0;
}
}
if (!first_valid_attr)
printf("}");
+}
Why aren't you handling the is_first = 0 in this function? And why aren't you printing empty objects?
And please try to handle rejections and not acceptance. So instead of:
for (i = 0; i < arr_len; i++) { idx = selected_attrs[i];
if (attrs[idx] && batadv_netlink_policy_json[idx].cb) {
Maybe something like:
for (i = 0; i < arr_len; i++) { [...] if (!attrs[idx]) continue;
if (!batadv_netlink_policy_json[idx].cb) continue;
+void sanitize_string(const char *str, int str_len) +{
int i;
for (i = 0; i < str_len; i++) {
if (str[i] == '"')
printf("\"");
else if (str[i] == '\\')
printf("\\\\");
else if (!isprint(str[i]))
printf("\\x%02x", str[i]);
else
printf("%c", str[i]);
}
+}
I know, it is the similar in alfred but maybe you can change it to something like:
void sanitize_string(const char *str) { while (*str) { if (*str == '"') puts("""); else if (*str == '\') puts("\\"); else if (!isprint(*str)) printf("\x%02x", *str); else putc(*str);
str++; } }
+static int meshinfo_callback(struct nl_msg *msg, void *arg) {
[...]
int selected_attrs[10] = { BATADV_ATTR_MESH_IFNAME,
BATADV_ATTR_MESH_ADDRESS,
BATADV_ATTR_HARD_IFNAME,
BATADV_ATTR_VERSION,
BATADV_ATTR_ALGO_NAME,
BATADV_ATTR_HARD_ADDRESS,
BATADV_ATTR_TT_TTVN };
I hope to get rid of this anyway but this should have been:
static const enum batadv_nl_attrs selected_attrs[] = { BATADV_ATTR_MESH_IFNAME, BATADV_ATTR_MESH_ADDRESS, BATADV_ATTR_HARD_IFNAME, BATADV_ATTR_VERSION, BATADV_ATTR_ALGO_NAME, BATADV_ATTR_HARD_ADDRESS, BATADV_ATTR_TT_TTVN, }
So no hardcoded (and wrong) length, correct type, constant, not allocated on the stack, different identation.
+static int netlink_print_meshinfo_json(struct state *state, char *orig_iface,
int read_opts, float orig_timeout,
float watch_interval)
+{
(void) orig_iface;
(void) orig_timeout;
(void) watch_interval;
(void) read_opts;
Please annotate unused parameters correctly with __maybe_unused
Also use the already existing netlink socket and don't create a new one. Maybe even with sys_simple_nlquery. Most functionality which creates its own batadv generic netlink socket only does it because it needed to support the old sysfs functionality in parallel in the past. And no on found the time to clean this up after the sysfs stuff was dropped.
If we don't need the "fancy" features of the debug tables then we can also use the functionality sys_simple_nlquery for the rest. At least we don't have the bat-host handling anymore, header is dropped and the filter might be dropped. And the only reason why I was against the special code to prevent the "watch" functionality was that we need to introduce a new hack for it.
Just make sure that you allow the caller to change the 6th parameter of genlmsg_put. Maybe by introducing a new function which allows to set this parameter. And the sys_simple_nlquery is changed to a wrapper which omits the new "flags" parameter.
And then you can also add the putc for '[' and ']' directly to the caller of sys_simple_nlquery (or whatever the new function will be called).
+#define BOOL_STRING(x) (x ? "true" : "false")
I don't see a lot of benefit in this macro.
- Andrew Lunn <andrew(a)lunn.ch>
- Sven Eckelmann <sven(a)narfation.org>
- Alexander Sarmanow <asarmanow(a)gmail.com>
No fancy html tags in headers please.
Overall: please run you patches through Linux's scripts/checkpatch.pl --strict and check what of the things you see is only relevant for the kernel and what might be actual things you should clean up.
[BATADV_ATTR_MESH_IFNAME] = { .name = "mesh_if",
.cb = nljson_print_str },
[BATADV_ATTR_MESH_ADDRESS] = { .name = "mesh_address",
.cb = nljson_print_mac},
[BATADV_ATTR_ORIG_ADDRESS] = { .name = "originator",
.cb = nljson_print_mac },
Please keep the same order as the attributes in batman_adv.h
Missing print functions:
* BATADV_ATTR_TPMETER_RESULT * BATADV_ATTR_TPMETER_TEST_TIME * BATADV_ATTR_TPMETER_BYTES * BATADV_ATTR_TPMETER_COOKIE * BATADV_ATTR_ACTIVE * BATADV_ATTR_BANDWIDTH_UP * BATADV_ATTR_BANDWIDTH_DOWN * BATADV_ATTR_ROUTER * BATADV_ATTR_BLA_OWN * BATADV_ATTR_BLA_ADDRESS * BATADV_ATTR_BLA_VID * BATADV_ATTR_BLA_BACKBONE * BATADV_ATTR_DAT_CACHE_IP4ADDRESS * BATADV_ATTR_DAT_CACHE_HWADDRESS * BATADV_ATTR_DAT_CACHE_VID * BATADV_ATTR_MCAST_FLAGS * BATADV_ATTR_MCAST_FLAGS_PRIV * BATADV_ATTR_VLANID * BATADV_ATTR_AGGREGATED_OGMS_ENABLED * BATADV_ATTR_AP_ISOLATION_ENABLED * BATADV_ATTR_ISOLATION_MARK * BATADV_ATTR_ISOLATION_MASK * BATADV_ATTR_BONDING_ENABLED * BATADV_ATTR_BRIDGE_LOOP_AVOIDANCE_ENABLED * BATADV_ATTR_DISTRIBUTED_ARP_TABLE_ENABLED * BATADV_ATTR_FRAGMENTATION_ENABLED * BATADV_ATTR_GW_BANDWIDTH_DOWN * BATADV_ATTR_GW_BANDWIDTH_UP * BATADV_ATTR_GW_MODE * BATADV_ATTR_GW_SEL_CLASS * BATADV_ATTR_HOP_PENALTY * BATADV_ATTR_LOG_LEVEL * BATADV_ATTR_MULTICAST_FORCEFLOOD_ENABLED * BATADV_ATTR_NETWORK_CODING_ENABLED * BATADV_ATTR_ORIG_INTERVAL * BATADV_ATTR_ELP_INTERVAL * BATADV_ATTR_THROUGHPUT_OVERRIDE * BATADV_ATTR_MULTICAST_FANOUT
[BATADV_ATTR_MESH_IFINDEX] = { .name = "mesh_if_idx",
meshif_idx or mesh_ifindex
[BATADV_ATTR_MESH_IFNAME] = { .name = "mesh_if",
meshif or mesh_ifname
[BATADV_ATTR_HARD_IFINDEX] = { .name = "hard_if_idx",
hardif_idx or hard_ifindex
[BATADV_ATTR_HARD_IFNAME] = { .name = "hard_if",
hardif or hard_ifname
[BATADV_ATTR_TT_ADDRESS] = { .name = "client",
why client? It is not really a client.
tt_address
[BATADV_ATTR_TT_VID] = { .name = "vid",
tt_vid
[BATADV_ATTR_TT_FLAGS] = { .name = "flags",
tt_flags
[BATADV_ATTR_LAST_SEEN_MSECS] = { .name = "last_seen",
last_seen_msecs
[BATADV_ATTR_NEIGH_ADDRESS] = { .name = "neighbor",
neigh_address
@@ -21,8 +21,22 @@ struct print_opts { float watch_interval; nl_recvmsg_msg_cb_t callback; char *remaining_header;
char *remaining_entry; const char *static_header; uint8_t nl_cmd;
uint8_t is_json;
uint8_t is_first;
+};
uint8_t is_json:1 uint8_t is_first:1
And what is remaining_entry?
Kind regards, Sven
On Monday, 10 May 2021 12:14:10 CEST Sven Eckelmann wrote:
void sanitize_string(const char *str) { while (*str) { if (*str == '"') puts("\""); else if (*str == '\\') puts("\\\\"); else if (!isprint(*str)) printf("\\x%02x", *str); else putc(*str); str++; }
}
Should have tested this. It should be more like:
void sanitize_string(const char *str) { while (*str) { if (*str == '"' || *str == '\') { putchar('\'); putchar(*str); } else if (!isprint(*str)) { printf("\x%02x", *str); } else { putchar(*str); } } }
Kind regards, Sven
On Friday, 7 May 2021 19:14:35 CEST Alexander Sarmanow wrote:
--- a/netlink.c +++ b/netlink.c @@ -363,17 +363,21 @@ static int info_callback(struct nl_msg *msg, void *arg) else extra_header = "";
ret = asprintf(&opts->remaining_header,
"[B.A.T.M.A.N. adv %s, MainIF/MAC: %s/%02x:%02x:%02x:%02x:%02x:%02x (%s/%02x:%02x:%02x:%02x:%02x:%02x %s)%s]\n%s",
version, primary_if,
primary_mac[0], primary_mac[1], primary_mac[2],
primary_mac[3], primary_mac[4], primary_mac[5],
mesh_name,
mesh_mac[0], mesh_mac[1], mesh_mac[2],
mesh_mac[3], mesh_mac[4], mesh_mac[5],
algo_name, extra_info, extra_header);
if (ret < 0)
opts->remaining_header = NULL;
if (!opts->is_json) {
ret = asprintf(&opts->remaining_header,
"[B.A.T.M.A.N. adv %s, MainIF/MAC: %s/%02x:%02x:%02x:%02x:%02x:%02x (%s/%02x:%02x:%02x:%02x:%02x:%02x %s)%s]\n%s",
version, primary_if,
primary_mac[0], primary_mac[1],
primary_mac[2], primary_mac[3],
primary_mac[4], primary_mac[5],
mesh_name,
mesh_mac[0], mesh_mac[1], mesh_mac[2],
mesh_mac[3], mesh_mac[4], mesh_mac[5],
algo_name, extra_info, extra_header);
if (ret < 0)
opts->remaining_header = NULL;
}
This is rather bad. You've already queried stuff before. Just make sure that you set the SKIP_HEADER flag and get rid of this is_json.
Kind regards, Sven
b.a.t.m.a.n@lists.open-mesh.org