For the missing JSON debug print a generic JSON print interface is proposed. A specific nla_policy is describing (a) the key name of the entry and (b) the corresponding callback function for printing the value. If the netlink attribute is not at disposal no entry for that will be printed.
This patch reffering to following patches:
[1] https://lists.open-mesh.org/mailman3/hyperkitty/list/b.a.t.m.a.n@lists.open-... [2] https://lists.open-mesh.org/mailman3/hyperkitty/list/b.a.t.m.a.n@lists.open-... [3] https://lists.open-mesh.org/mailman3/hyperkitty/list/b.a.t.m.a.n@lists.open-... [4] https://lists.open-mesh.org/mailman3/hyperkitty/list/b.a.t.m.a.n@lists.open-... [5] https://lists.open-mesh.org/mailman3/hyperkitty/list/b.a.t.m.a.n@lists.open-... [6] https://lists.open-mesh.org/mailman3/hyperkitty/list/b.a.t.m.a.n@lists.open-...
Alexander Sarmanow (2): batctl: netlink: Make netlink_query_common non-static batctl: genl_json: Add generic JSON interface
Makefile | 1 + genl_json.c | 359 ++++++++++++++++++++++++++++++++++++++++++++++++++++ genl_json.h | 24 ++++ netlink.c | 12 +- netlink.h | 14 ++ 5 files changed, 402 insertions(+), 8 deletions(-) create mode 100644 genl_json.c create mode 100644 genl_json.h
With a view to the introduction of a JSON debug support the netlink_query_common can be used for simple query of batman-adv atttirubtes without any filters.
Signed-off-by: Alexander Sarmanow asarmanow@gmail.com --- netlink.c | 12 ++++-------- netlink.h | 8 ++++++++ 2 files changed, 12 insertions(+), 8 deletions(-)
diff --git a/netlink.c b/netlink.c index eb5888b..2c45645 100644 --- a/netlink.c +++ b/netlink.c @@ -29,10 +29,6 @@ #include "functions.h" #include "main.h"
-struct nlquery_opts { - int err; -}; - struct nla_policy batadv_netlink_policy[NUM_BATADV_ATTR] = { [BATADV_ATTR_VERSION] = { .type = NLA_STRING, @@ -623,10 +619,10 @@ static int nlquery_stop_cb(struct nl_msg *msg, void *arg) return NL_STOP; }
-static int netlink_query_common(struct state *state, - unsigned int mesh_ifindex, uint8_t nl_cmd, - nl_recvmsg_msg_cb_t callback, int flags, - struct nlquery_opts *query_opts) +int netlink_query_common(struct state *state, + unsigned int mesh_ifindex, uint8_t nl_cmd, + nl_recvmsg_msg_cb_t callback, int flags, + struct nlquery_opts *query_opts) { struct nl_msg *msg; struct nl_cb *cb; diff --git a/netlink.h b/netlink.h index 2cc5862..c93f500 100644 --- a/netlink.h +++ b/netlink.h @@ -25,6 +25,10 @@ struct print_opts { uint8_t nl_cmd; };
+struct nlquery_opts { + int err; +}; + struct ether_addr;
int netlink_create(struct state *state); @@ -54,6 +58,10 @@ int netlink_print_error(struct sockaddr_nl *nla, struct nlmsgerr *nlerr, void *arg); void netlink_print_remaining_header(struct print_opts *opts);
+int netlink_query_common(struct state *state, unsigned int mesh_ifindex, + uint8_t nl_cmd, nl_recvmsg_msg_cb_t callback, int flags, + struct nlquery_opts *query_opts); + extern char algo_name_buf[256]; extern int last_err; extern int64_t mcast_flags;
To print JSON strings in a generic way a second nla_policy is used, with a name (dict key) and a callback function for each entry corresponding to its formats. There are multiple callback functions created, depending on the NLA type.
Signed-off-by: Alexander Sarmanow asarmanow@gmail.com --- Makefile | 1 + genl_json.c | 359 ++++++++++++++++++++++++++++++++++++++++++++++++++++ genl_json.h | 24 ++++ netlink.h | 6 + 4 files changed, 390 insertions(+) create mode 100644 genl_json.c create mode 100644 genl_json.h
diff --git a/Makefile b/Makefile index 98bf695..cee4f0c 100755 --- a/Makefile +++ b/Makefile @@ -16,6 +16,7 @@ obj-y += bat-hosts.o obj-y += debug.o obj-y += functions.o obj-y += genl.o +obj-y += genl_json.o obj-y += hash.o obj-y += icmp_helper.o obj-y += main.o diff --git a/genl_json.c b/genl_json.c new file mode 100644 index 0000000..cff54d8 --- /dev/null +++ b/genl_json.c @@ -0,0 +1,359 @@ +/* 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 <stdbool.h> +#include <stdio.h> +#include <ctype.h> +#include <netlink/netlink.h> +#include <netlink/attr.h> + +#include "genl_json.h" +#include "batman_adv.h" +#include "netlink.h" +#include "main.h" + +static void nljson_print_str(struct nlattr *attrs[], int idx) +{ + const char *value; + + value = nla_get_string(attrs[idx]); + + putc('"', stdout); + sanitize_string(value); + putc('"', stdout); +} + +static void nljson_print_uint8_t(struct nlattr *attrs[], int idx) +{ + uint8_t value = nla_get_u8(attrs[idx]); + + printf("%u", value); +} + +static void nljson_print_uint16_t(struct nlattr *attrs[], int idx) +{ + uint16_t value = nla_get_u16(attrs[idx]); + + printf("%u", value); +} + +static void nljson_print_uint32_t(struct nlattr *attrs[], int idx) +{ + uint32_t value = nla_get_u32(attrs[idx]); + + printf("%u", value); +} + +static void nljson_print_uint64_t(struct nlattr *attrs[], int idx) +{ + uint64_t value = nla_get_u64(attrs[idx]); + + printf("%llu", value); +} + +static void nljson_print_mac(struct nlattr *attrs[], int idx) +{ + uint8_t *value = nla_data(attrs[idx]); + + printf(""%02x:%02x:%02x:%02x:%02x:%02x"", + value[0], value[1], value[2], value[3], value[4], value[5]); +} + +static void nljson_print_flag(struct nlattr *attrs[], int idx) +{ + if (nla_get_flag(attrs[idx])) + printf("true"); +} + +void sanitize_string(const char *str) +{ + while (*str) { + if (*str == '"' || *str == '\') { + putchar('\'); + putchar(*str); + } else if (*str == '\') { + printf("\\"); + } else if (!isprint(*str)) { + printf("\x%02x", *str); + } else { + printf("%c", *str); + } + str++; + } +} + +void netlink_print_json_entries(struct nlattr *attrs[], struct json_opts *json_opts) +{ + bool first_valid_attr = true; + int i; + + if (!json_opts->is_first) + printf(","); + + printf("{"); + for (i = 0; i < BATADV_ATTR_MAX + 1; i++) { + if (!attrs[i]) + continue; + + if (!batadv_netlink_policy_json[i].cb) + continue; + + if (!first_valid_attr) + printf(","); + + putc('"', stdout); + sanitize_string(batadv_netlink_policy_json[i].name); + printf("":"); + batadv_netlink_policy_json[i].cb(attrs, i); + + first_valid_attr = false; + } + printf("}"); +} + + +struct nla_policy_json batadv_netlink_policy_json[NUM_BATADV_ATTR] = { + [BATADV_ATTR_VERSION] = { + .name = "version", + .cb = nljson_print_str, + }, + [BATADV_ATTR_ALGO_NAME] = { + .name = "algo_name", + .cb = nljson_print_str, + }, + [BATADV_ATTR_MESH_IFINDEX] = { + .name = "mesh_ifindex", + .cb = nljson_print_uint32_t, + }, + [BATADV_ATTR_MESH_IFNAME] = { + .name = "mesh_ifname", + .cb = nljson_print_str, + }, + [BATADV_ATTR_MESH_ADDRESS] = { + .name = "mesh_address", + .cb = nljson_print_mac, + }, + [BATADV_ATTR_HARD_IFINDEX] = { + .name = "hard_ifindex", + .cb = nljson_print_uint32_t, + }, + [BATADV_ATTR_HARD_IFNAME] = { + .name = "hard_ifname", + .cb = nljson_print_str, + }, + [BATADV_ATTR_HARD_ADDRESS] = { + .name = "hard_address", + .cb = nljson_print_mac, + }, + [BATADV_ATTR_ORIG_ADDRESS] = { + .name = "orig_address", + .cb = nljson_print_mac, + }, + [BATADV_ATTR_TPMETER_RESULT] = { + .name = "tpmeter_result", + .cb = nljson_print_uint8_t, + }, + [BATADV_ATTR_TPMETER_TEST_TIME] = { + .name = "tpmeter_test_time", + .cb = nljson_print_uint32_t, + }, + [BATADV_ATTR_TPMETER_BYTES] = { + .name = "tpmeter_bytes", + .cb = nljson_print_uint64_t + }, + [BATADV_ATTR_TPMETER_COOKIE] = { + .name = "tpmeter_cookie", + .cb = nljson_print_uint32_t, + }, + [BATADV_ATTR_PAD] = { + .name = "pad", + }, + [BATADV_ATTR_ACTIVE] = { + .name = "active", + .cb = nljson_print_flag, + }, + [BATADV_ATTR_TT_ADDRESS] = { + .name = "tt_address", + .cb = nljson_print_mac, + }, + [BATADV_ATTR_TT_TTVN] = { + .name = "tt_ttvn", + .cb = nljson_print_uint8_t, + }, + [BATADV_ATTR_TT_LAST_TTVN] = { + .name = "last_ttvn", + .cb = nljson_print_uint8_t, + }, + [BATADV_ATTR_TT_CRC32] = { + .name = "crc32", + .cb = nljson_print_uint32_t, + }, + [BATADV_ATTR_TT_VID] = { + .name = "tt_vid", + .cb = nljson_print_uint16_t, + }, + [BATADV_ATTR_TT_FLAGS] = { + .name = "tt_flags", + .cb = nljson_print_uint32_t, + }, + [BATADV_ATTR_FLAG_BEST] = { + .name = "best", + .cb = nljson_print_flag, + }, + [BATADV_ATTR_LAST_SEEN_MSECS] = { + .name = "last_seen_msecs", + .cb = nljson_print_uint32_t, + }, + [BATADV_ATTR_NEIGH_ADDRESS] = { + .name = "neigh_address", + .cb = nljson_print_mac, + }, + [BATADV_ATTR_TQ] = { + .name = "tq", + .cb = nljson_print_uint8_t, + }, + [BATADV_ATTR_THROUGHPUT] = { + .name = "throughput", + .cb = nljson_print_uint32_t, + }, + [BATADV_ATTR_BANDWIDTH_UP] = { + .name = "bandwidth_up", + .cb = nljson_print_uint32_t, + }, + [BATADV_ATTR_BANDWIDTH_DOWN] = { + .name = "bandwidth_down", + .cb = nljson_print_uint32_t, + }, + [BATADV_ATTR_ROUTER] = { + .name = "router", + .cb = nljson_print_mac, + }, + [BATADV_ATTR_BLA_OWN] = { + .name = "bla_own", + .cb = nljson_print_flag, + }, + [BATADV_ATTR_BLA_ADDRESS] = { + .name = "bla_address", + .cb = nljson_print_mac, + }, + [BATADV_ATTR_BLA_VID] = { + .name = "bla_vid", + .cb = nljson_print_uint16_t, + }, + [BATADV_ATTR_BLA_BACKBONE] = { + .name = "bla_backbone", + .cb = nljson_print_mac, + }, + [BATADV_ATTR_BLA_CRC] = { + .name = "bla_crc", + .cb = nljson_print_uint16_t, + }, + [BATADV_ATTR_DAT_CACHE_IP4ADDRESS] = { + .name = "dat_cache_ip4_address", + }, + [BATADV_ATTR_DAT_CACHE_HWADDRESS] = { + .name = "dat_cache_hw_address", + .cb = nljson_print_mac, + }, + [BATADV_ATTR_DAT_CACHE_VID] = { + .name = "dat_cache_vid", + .cb = nljson_print_uint16_t, + }, + [BATADV_ATTR_MCAST_FLAGS] = { + .name = "mcast_flags", + .cb = nljson_print_uint32_t, + }, + [BATADV_ATTR_MCAST_FLAGS_PRIV] = { + .name = "mcast_flags_priv", + .cb = nljson_print_uint32_t, + }, + [BATADV_ATTR_VLANID] = { + .name = "vlan_id", + .cb = nljson_print_uint16_t, + }, + [BATADV_ATTR_AGGREGATED_OGMS_ENABLED] = { + .name = "aggregated_ogms_enabled", + .cb = nljson_print_uint8_t, + }, + [BATADV_ATTR_AP_ISOLATION_ENABLED] = { + .name = "ap_isolation_enabled", + .cb = nljson_print_uint8_t, + }, + [BATADV_ATTR_ISOLATION_MARK] = { + .name = "isolation_mark", + .cb = nljson_print_uint32_t, + }, + [BATADV_ATTR_ISOLATION_MASK] = { + .name = "isolation_mask", + .cb = nljson_print_uint32_t, + }, + [BATADV_ATTR_BONDING_ENABLED] = { + .name = "bonding_enabled", + .cb = nljson_print_uint8_t, + }, + [BATADV_ATTR_BRIDGE_LOOP_AVOIDANCE_ENABLED] = { + .name = "bridge_loop_avoidance_enabled", + .cb = nljson_print_uint8_t, + }, + [BATADV_ATTR_DISTRIBUTED_ARP_TABLE_ENABLED] = { + .name = "distributed_arp_table_enabled", + .cb = nljson_print_uint8_t, + }, + [BATADV_ATTR_FRAGMENTATION_ENABLED] = { + .name = "fragmented_enabled", + .cb = nljson_print_uint8_t, + }, + [BATADV_ATTR_GW_BANDWIDTH_DOWN] = { + .name = "bandwidth_down", + .cb = nljson_print_uint32_t, + }, + [BATADV_ATTR_GW_BANDWIDTH_UP] = { + .name = "bandwidth_up", + .cb = nljson_print_uint32_t, + }, + [BATADV_ATTR_GW_MODE] = { + .name = "gw_mode", + .cb = nljson_print_uint8_t, + }, + [BATADV_ATTR_GW_SEL_CLASS] = { + .name = "gw_sel_class", + .cb = nljson_print_uint32_t, + }, + [BATADV_ATTR_HOP_PENALTY] = { + .name = "hop_penalty", + .cb = nljson_print_uint8_t, + }, + [BATADV_ATTR_LOG_LEVEL] = { + .name = "log_level", + .cb = nljson_print_uint32_t, + }, + [BATADV_ATTR_MULTICAST_FORCEFLOOD_ENABLED] = { + .name = "multicast_forceflood_enabled", + .cb = nljson_print_uint8_t, + }, + [BATADV_ATTR_NETWORK_CODING_ENABLED] = { + .name = "network_coding_enabled", + .cb = nljson_print_uint8_t, + }, + [BATADV_ATTR_ORIG_INTERVAL] = { + .name = "orig_interval", + .cb = nljson_print_uint32_t, + }, + [BATADV_ATTR_ELP_INTERVAL] = { + .name = "elp_interval", + .cb = nljson_print_uint32_t, + }, + [BATADV_ATTR_THROUGHPUT_OVERRIDE] = { + .name = "throughput_override", + .cb = nljson_print_uint32_t, + }, + [BATADV_ATTR_MULTICAST_FANOUT] = { + .name = "multicast_fanout", + .cb = nljson_print_uint32_t, + }, +}; diff --git a/genl_json.h b/genl_json.h new file mode 100644 index 0000000..0336b40 --- /dev/null +++ b/genl_json.h @@ -0,0 +1,24 @@ +/* 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 + */ + +#ifndef _BATCTL_GENLJSON_H +#define _BATCTL_GENLJSON_H + +#include <stdint.h> + +#include "netlink.h" + +struct json_opts { + uint8_t is_first; + struct nlquery_opts query_opts; +}; + +void netlink_print_json_entries(struct nlattr *attrs[], struct json_opts *json_opts); +void sanitize_string(const char *str); + +#endif /* _BATCTL_GENLJSON_H */ diff --git a/netlink.h b/netlink.h index c93f500..d96935a 100644 --- a/netlink.h +++ b/netlink.h @@ -29,6 +29,11 @@ struct nlquery_opts { int err; };
+struct nla_policy_json { + const char *name; + void (*cb)(struct nlattr *attrs[], int idx); +}; + struct ether_addr;
int netlink_create(struct state *state); @@ -44,6 +49,7 @@ int get_algoname_netlink(struct state *state, unsigned int mesh_ifindex, char *algoname, size_t algoname_len);
extern struct nla_policy batadv_netlink_policy[]; +extern struct nla_policy_json batadv_netlink_policy_json[];
int missing_mandatory_attrs(struct nlattr *attrs[], const int mandatory[], int num);
On Thursday, 13 May 2021 16:02:34 CEST Alexander Sarmanow wrote:
To print JSON strings in a generic way a second nla_policy is used, with
This is not a nla_policy but a private json translation "policy".
[...]
new file mode 100644 index 0000000..cff54d8 --- /dev/null +++ b/genl_json.c @@ -0,0 +1,359 @@ +/* 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 <stdbool.h> +#include <stdio.h> +#include <ctype.h> +#include <netlink/netlink.h> +#include <netlink/attr.h>
+#include "genl_json.h" +#include "batman_adv.h" +#include "netlink.h" +#include "main.h"
#include "genl_json.h" #include "main.h"
#include <ctype.h> #include <inttypes.h> #include <netlink/netlink.h> #include <netlink/attr.h> #include <stdbool.h> #include <stdio.h>
#include "batman_adv.h" #include "netlink.h"
+static void nljson_print_uint8_t(struct nlattr *attrs[], int idx) +{
- uint8_t value = nla_get_u8(attrs[idx]);
- printf("%u", value);
+}
"%"PRIu8
+static void nljson_print_uint16_t(struct nlattr *attrs[], int idx) +{
- uint16_t value = nla_get_u16(attrs[idx]);
- printf("%u", value);
+}
"%"PRIu16
+static void nljson_print_uint32_t(struct nlattr *attrs[], int idx) +{
- uint32_t value = nla_get_u32(attrs[idx]);
- printf("%u", value);
+}
"%"PRIu32
+static void nljson_print_uint64_t(struct nlattr *attrs[], int idx) +{
- uint64_t value = nla_get_u64(attrs[idx]);
- printf("%llu", value);
+}
"%"PRIu64
+static void nljson_print_mac(struct nlattr *attrs[], int idx) +{
- uint8_t *value = nla_data(attrs[idx]);
- printf(""%02x:%02x:%02x:%02x:%02x:%02x"",
value[0], value[1], value[2], value[3], value[4], value[5]);
+}
Alignment
+static void nljson_print_flag(struct nlattr *attrs[], int idx) +{
- if (nla_get_flag(attrs[idx]))
printf("true");
+}
The check is not necessary because it check only if attrs[idx] is != NULL - which you've already checked. If this would not be the case, then this function would create broken json like '{"foobar":}', right?
+void sanitize_string(const char *str) +{
- while (*str) {
if (*str == '"' || *str == '\\') {
putchar('\\');
putchar(*str);
} else if (*str == '\\') {
printf("\\\\");
} else if (!isprint(*str)) {
printf("\\x%02x", *str);
} else {
printf("%c", *str);
}
if (*str == '"' || *str == '\') { putchar('\'); putchar(*str); } else if (!isprint(*str)) { printf("\x%02x", *str); } else { putchar(*str); }
str++;
- }
+}
This function should have been static and at the beginning of this file.
+void netlink_print_json_entries(struct nlattr *attrs[], struct json_opts *json_opts) +{
- bool first_valid_attr = true;
- int i;
- if (!json_opts->is_first)
printf(",");
Code to handle the json_opts->is_first = false was again not added to this function.
- printf("{");
- for (i = 0; i < BATADV_ATTR_MAX + 1; i++) {
if (!attrs[i])
continue;
if (!batadv_netlink_policy_json[i].cb)
continue;
if (!first_valid_attr)
printf(",");
putc('"', stdout);
sanitize_string(batadv_netlink_policy_json[i].name);
printf("\":");
batadv_netlink_policy_json[i].cb(attrs, i);
first_valid_attr = false;
- }
- printf("}");
+}
+struct nla_policy_json batadv_netlink_policy_json[NUM_BATADV_ATTR] = {
this should have been static const
- [BATADV_ATTR_PAD] = {
.name = "pad",
- },
I would guess we can ignore this one.
- [BATADV_ATTR_TT_FLAGS] = {
.name = "tt_flags",
.cb = nljson_print_uint32_t,
- },
Why was the actually decoding removed?
- [BATADV_ATTR_DAT_CACHE_IP4ADDRESS] = {
.name = "dat_cache_ip4_address",
- },
IPv4 print code missing
[...]
- [BATADV_ATTR_VLANID] = {
.name = "vlan_id",
.cb = nljson_print_uint16_t,
- },
wasn't this originally printed in a special way?
- [BATADV_ATTR_AGGREGATED_OGMS_ENABLED] = {
.name = "aggregated_ogms_enabled",
.cb = nljson_print_uint8_t,
- },
This and other _ENABLED are booleans.
[...]
There were also different other weird names used. Like fragmented_enabled for the fragmentation feature.
diff --git a/genl_json.h b/genl_json.h new file mode 100644 index 0000000..0336b40 --- /dev/null +++ b/genl_json.h @@ -0,0 +1,24 @@ +/* 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
- */
+#ifndef _BATCTL_GENLJSON_H +#define _BATCTL_GENLJSON_H
+#include <stdint.h>
+#include "netlink.h"
+struct json_opts {
- uint8_t is_first;
is_first:1
- struct nlquery_opts query_opts;
+};
+void netlink_print_json_entries(struct nlattr *attrs[], struct json_opts *json_opts); +void sanitize_string(const char *str);
+#endif /* _BATCTL_GENLJSON_H */ diff --git a/netlink.h b/netlink.h index c93f500..d96935a 100644 --- a/netlink.h +++ b/netlink.h @@ -29,6 +29,11 @@ struct nlquery_opts { int err; };
+struct nla_policy_json {
- const char *name;
- void (*cb)(struct nlattr *attrs[], int idx);
+};
This doesn't belong to netlink.h. If it would really be needed outside of genl_json.c then you should have added it to genl_json.h
struct ether_addr;
int netlink_create(struct state *state); @@ -44,6 +49,7 @@ int get_algoname_netlink(struct state *state, unsigned int mesh_ifindex, char *algoname, size_t algoname_len);
extern struct nla_policy batadv_netlink_policy[]; +extern struct nla_policy_json batadv_netlink_policy_json[];
This doesn't belong to netlink.h. If it would really be needed outside of genl_json.c then you should have added it to genl_json.h
Kind regards, Sven
b.a.t.m.a.n@lists.open-mesh.org