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
@@ -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
@@ -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)
@@ -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;
}
@@ -71,6 +71,7 @@ enum command_type {
SUBCOMMAND_VID,
SUBCOMMAND_HIF,
DEBUGTABLE,
+ DEBUGJSON,
};
struct state {
@@ -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)
new file mode 100644
@@ -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, "");
new file mode 100644
@@ -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, "");
@@ -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);
@@ -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,
@@ -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)
new file mode 100644
@@ -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, "");
new file mode 100644
@@ -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, "");
new file mode 100644
@@ -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, "");